summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBBS Administrator <bbs@sony.tfcis.org>2013-02-08 16:53:05 +0800
committerBBS Administrator <bbs@sony.tfcis.org>2013-02-08 16:53:05 +0800
commit838b5338b88e0af60a8808036a0a2e17411db917 (patch)
tree875117556a8aef47d53a052eac76592de1ac1bca
downloadsonybbs-838b5338b88e0af60a8808036a0a2e17411db917.tar.gz
sonybbs-838b5338b88e0af60a8808036a0a2e17411db917.tar.zst
sonybbs-838b5338b88e0af60a8808036a0a2e17411db917.zip
SonyBBS Initial Commit - Version 20121021
-rw-r--r--.gitignore93
-rw-r--r--Makefile147
-rw-r--r--daemon/Makefile65
-rw-r--r--daemon/bguard.c1229
-rw-r--r--daemon/bhttpd.c4122
-rw-r--r--daemon/bmtad.c4004
-rw-r--r--daemon/bnntpd.c1500
-rw-r--r--daemon/bpop3d.c1871
-rw-r--r--daemon/gemd.c1066
-rw-r--r--daemon/xchatd.c4027
-rw-r--r--game/Makefile64
-rw-r--r--game/bar.c387
-rw-r--r--game/bingo.c186
-rw-r--r--game/bj.c389
-rw-r--r--game/bwboard.c2408
-rw-r--r--game/chessmj.c1023
-rw-r--r--game/dice.c178
-rw-r--r--game/dict.c338
-rw-r--r--game/dragon.c315
-rw-r--r--game/fantan.c413
-rw-r--r--game/gp.c643
-rw-r--r--game/gray.c785
-rw-r--r--game/guessnum.c293
-rw-r--r--game/km.c532
-rw-r--r--game/liteon.c216
-rw-r--r--game/marie.c189
-rw-r--r--game/mine.c474
-rw-r--r--game/nine.c601
-rw-r--r--game/pushbox.c321
-rw-r--r--game/race.c357
-rw-r--r--game/recall.c231
-rw-r--r--game/seven.c967
-rw-r--r--game/tetris.c446
-rw-r--r--include/attr.h30
-rw-r--r--include/battr.h104
-rw-r--r--include/bbs.h65
-rw-r--r--include/config.h433
-rw-r--r--include/dao.h39
-rw-r--r--include/dns.h88
-rw-r--r--include/global.h503
-rw-r--r--include/hdr.h114
-rw-r--r--include/modes.h253
-rw-r--r--include/perm.h160
-rw-r--r--include/proto.h52
-rw-r--r--include/splay.h16
-rw-r--r--include/struct.h710
-rw-r--r--include/theme.h257
-rw-r--r--include/theme/theme_blue.h246
-rw-r--r--include/theme/theme_cyan.h246
-rw-r--r--include/theme/theme_green.h246
-rw-r--r--include/theme/theme_neck.h246
-rw-r--r--include/theme/theme_pack.h246
-rw-r--r--include/theme/theme_purple.h246
-rw-r--r--include/theme/theme_red.h246
-rw-r--r--include/theme/theme_wd.h246
-rw-r--r--include/theme/theme_yellow.h246
-rw-r--r--include/ufo.h173
-rw-r--r--include/xchat.h136
-rw-r--r--innbbsd/Makefile71
-rw-r--r--innbbsd/bbslib.c283
-rw-r--r--innbbsd/bbslib.h33
-rw-r--r--innbbsd/bbslink.c1053
-rw-r--r--innbbsd/channel.c884
-rw-r--r--innbbsd/convcode.c169
-rw-r--r--innbbsd/history.c135
-rw-r--r--innbbsd/innbbsconf.h70
-rw-r--r--innbbsd/inntobbs.c242
-rw-r--r--innbbsd/inntobbs.h17
-rw-r--r--innbbsd/nntp.h145
-rw-r--r--innbbsd/nocem.c508
-rw-r--r--innbbsd/nocem.h41
-rw-r--r--innbbsd/nocem/CHANGES.nocem94
-rw-r--r--innbbsd/nocem/NOCEM140
-rw-r--r--innbbsd/nocem/README.nocem39
-rw-r--r--innbbsd/rec_article.c522
-rw-r--r--lib/Makefile92
-rw-r--r--lib/acl_has.c102
-rw-r--r--lib/archiv32.c41
-rw-r--r--lib/chrono32.c18
-rw-r--r--lib/dao.p151
-rw-r--r--lib/dl_lib.c114
-rw-r--r--lib/dns.c45
-rw-r--r--lib/dns.p14
-rw-r--r--lib/dns_aton.c149
-rw-r--r--lib/dns_ident.c178
-rw-r--r--lib/dns_name.c78
-rw-r--r--lib/dns_open.c98
-rw-r--r--lib/dns_smtp.c129
-rw-r--r--lib/f_cat.c16
-rw-r--r--lib/f_cp.c35
-rw-r--r--lib/f_img.c32
-rw-r--r--lib/f_ln.c22
-rw-r--r--lib/f_lock.c36
-rw-r--r--lib/f_map.c35
-rw-r--r--lib/f_mode.c14
-rw-r--r--lib/f_mv.c17
-rw-r--r--lib/f_new.c50
-rw-r--r--lib/f_path.c86
-rw-r--r--lib/f_rm.c59
-rw-r--r--lib/f_suck.c26
-rw-r--r--lib/hash32.c13
-rw-r--r--lib/hdr_fpath.c37
-rw-r--r--lib/hdr_stamp.c131
-rw-r--r--lib/is_alnum.c7
-rw-r--r--lib/is_alpha.c6
-rw-r--r--lib/mak_dirs.c66
-rw-r--r--lib/not_addr.c32
-rw-r--r--lib/radix32.c13
-rw-r--r--lib/rec_add.c21
-rw-r--r--lib/rec_bot.c87
-rw-r--r--lib/rec_del.c84
-rw-r--r--lib/rec_get.c26
-rw-r--r--lib/rec_ins.c58
-rw-r--r--lib/rec_mov.c75
-rw-r--r--lib/rec_num.c14
-rw-r--r--lib/rec_put.c92
-rw-r--r--lib/rec_ref.c88
-rw-r--r--lib/rec_sync.c72
-rw-r--r--lib/rfc2047.c193
-rw-r--r--lib/shm.c34
-rw-r--r--lib/splay.c174
-rw-r--r--lib/str_ansi.c37
-rw-r--r--lib/str_cat.c15
-rw-r--r--lib/str_cmp.c19
-rw-r--r--lib/str_decode.c374
-rw-r--r--lib/str_folder.c19
-rw-r--r--lib/str_fpath.c109
-rw-r--r--lib/str_from.c92
-rw-r--r--lib/str_has.c30
-rw-r--r--lib/str_hash.c13
-rw-r--r--lib/str_lower.c14
-rw-r--r--lib/str_lowest.c17
-rw-r--r--lib/str_ncmp.c26
-rw-r--r--lib/str_ncpy.c22
-rw-r--r--lib/str_passwd.c52
-rw-r--r--lib/str_stamp.c23
-rw-r--r--lib/str_str.c41
-rw-r--r--lib/str_str_kmp.c119
-rw-r--r--lib/str_sub.c55
-rw-r--r--lib/str_tail.c10
-rw-r--r--lib/str_time.c60
-rw-r--r--lib/str_trim.c16
-rw-r--r--lib/str_ttl.c14
-rw-r--r--lib/xsort.c198
-rw-r--r--lib/xwrite.c18
-rw-r--r--maple/CHANGE1766
-rw-r--r--maple/Makefile69
-rw-r--r--maple/acct.c1203
-rw-r--r--maple/bbsd.c1663
-rw-r--r--maple/bmw.c1195
-rw-r--r--maple/board.c2259
-rw-r--r--maple/cache.c589
-rw-r--r--maple/edit.c2380
-rw-r--r--maple/favor.c848
-rw-r--r--maple/gem.c1472
-rw-r--r--maple/mail.c1943
-rw-r--r--maple/maple.p276
-rw-r--r--maple/menu.c1287
-rw-r--r--maple/more.c835
-rw-r--r--maple/pal.c907
-rw-r--r--maple/post.c2730
-rw-r--r--maple/talk.c1424
-rw-r--r--maple/ulist.c1243
-rw-r--r--maple/user.c834
-rw-r--r--maple/visio.c2071
-rw-r--r--maple/window.c439
-rw-r--r--maple/xover.c1788
-rw-r--r--maple/xpost.c1203
-rw-r--r--pip/Makefile64
-rw-r--r--pip/pip.c241
-rw-r--r--pip/pip.h63
-rw-r--r--pip/pip_basic.c350
-rw-r--r--pip/pip_ending.c1040
-rw-r--r--pip/pip_fight.c1888
-rw-r--r--pip/pip_item.c256
-rw-r--r--pip/pip_job.c910
-rw-r--r--pip/pip_menu.c809
-rw-r--r--pip/pip_pk.c801
-rw-r--r--pip/pip_play.c352
-rw-r--r--pip/pip_prac.c522
-rw-r--r--pip/pip_quest.c418
-rw-r--r--pip/pip_race.c287
-rw-r--r--pip/pip_royal.c200
-rw-r--r--pip/pip_stuff.c670
-rw-r--r--pip/pip_visio.c299
-rw-r--r--pip/pip_weapon.c241
-rw-r--r--pip/pipglobal.h59
-rw-r--r--pip/pipstruct.h430
-rwxr-xr-xsh/bmta.sh8
-rwxr-xr-xsh/killbbs.sh13
-rwxr-xr-xsh/killhigh.sh3
-rwxr-xr-xsh/killtop.sh2
-rwxr-xr-xsh/routin.sh45
-rw-r--r--so/Makefile64
-rw-r--r--so/admutil.c883
-rw-r--r--so/aloha.c445
-rw-r--r--so/bank.c521
-rw-r--r--so/calendar.c359
-rw-r--r--so/chat.c827
-rw-r--r--so/classtable.c311
-rw-r--r--so/credit.c274
-rw-r--r--so/help.c207
-rw-r--r--so/innbbs.c701
-rw-r--r--so/manage.c762
-rw-r--r--so/newbrd.c906
-rw-r--r--so/song.c752
-rw-r--r--so/todo.c365
-rw-r--r--so/vote.c1336
-rw-r--r--so/xyz.c213
-rw-r--r--util/Makefile59
-rw-r--r--util/account.c1181
-rw-r--r--util/acl-sort.c130
-rw-r--r--util/backup/Makefile57
-rw-r--r--util/backup/backupacct.c58
-rw-r--r--util/backup/backupbrd.c51
-rw-r--r--util/backup/backupgem.c77
-rw-r--r--util/backup/backupoth.c40
-rw-r--r--util/backup/backupusr.c58
-rw-r--r--util/backup/restoreacct.c58
-rw-r--r--util/backup/restorebrd.c56
-rw-r--r--util/backup/restoregem.c80
-rw-r--r--util/backup/restoreusr.c64
-rw-r--r--util/bbsmail.c330
-rw-r--r--util/bquota.c385
-rw-r--r--util/brdmail.c334
-rw-r--r--util/camera.c395
-rw-r--r--util/changeperm.c58
-rw-r--r--util/counter.c112
-rw-r--r--util/expire.c490
-rw-r--r--util/gem-index.c245
-rw-r--r--util/give_paycheck.c91
-rw-r--r--util/hdr-dump.c43
-rw-r--r--util/mailpost.c202
-rw-r--r--util/outgo.c70
-rw-r--r--util/poststat.c471
-rw-r--r--util/reaper.c647
-rw-r--r--util/redir.c358
-rw-r--r--util/rmbadmail.c318
-rw-r--r--util/setperm.c88
-rw-r--r--util/setusr.c166
-rw-r--r--util/showACCT.c116
-rw-r--r--util/showBRD.c79
-rw-r--r--util/showDIR.c81
-rw-r--r--util/showUSR.c57
-rw-r--r--util/showperm.c53
-rw-r--r--util/topgem.c161
-rw-r--r--util/topsong.c91
-rw-r--r--util/topusr.c751
-rw-r--r--util/tran/Makefile66
-rw-r--r--util/tran/ats.h274
-rw-r--r--util/tran/ats2bmw.c143
-rw-r--r--util/tran/ats2brd.c233
-rw-r--r--util/tran/ats2gem.c250
-rw-r--r--util/tran/ats2mf.c173
-rw-r--r--util/tran/ats2pal.c185
-rw-r--r--util/tran/ats2usr.c592
-rw-r--r--util/tran/brd2gem.c104
-rw-r--r--util/tran/cola.h74
-rw-r--r--util/tran/cola2brd.c212
-rw-r--r--util/tran/cola2gem.c211
-rw-r--r--util/tran/cola2post.c223
-rw-r--r--util/tran/cola2usr.c291
-rw-r--r--util/tran/fb.h154
-rw-r--r--util/tran/fb2brd.c234
-rw-r--r--util/tran/fb2gem.c381
-rw-r--r--util/tran/fb2pal.c177
-rw-r--r--util/tran/fb2usr.c328
-rw-r--r--util/tran/mag.h111
-rw-r--r--util/tran/mag2brd.c239
-rw-r--r--util/tran/mag2gem.c180
-rw-r--r--util/tran/mag2usr.c259
-rw-r--r--util/tran/snap.h110
-rw-r--r--util/tran/snap2brd.c59
-rw-r--r--util/tran/snap2usr.c174
-rw-r--r--util/tran/sob.h96
-rw-r--r--util/tran/sob2brd.c228
-rw-r--r--util/tran/sob2gem.c233
-rw-r--r--util/tran/sob2pal.c182
-rw-r--r--util/tran/sob2usr.c564
-rw-r--r--util/tran/transacct.c212
-rw-r--r--util/tran/transbrd.c125
-rw-r--r--util/tran/wd.h141
-rw-r--r--util/tran/wd2bmw.c131
-rw-r--r--util/tran/wd2brd.c289
-rw-r--r--util/tran/wd2gem.c232
-rw-r--r--util/tran/wd2list.c172
-rw-r--r--util/tran/wd2mf.c171
-rw-r--r--util/tran/wd2pal.c213
-rw-r--r--util/tran/wd2pip.c498
-rw-r--r--util/tran/wd2usr.c597
-rw-r--r--util/tran/windtop.h131
-rw-r--r--util/tran/windtop2brd.c88
-rw-r--r--util/tran/windtop2pip.c490
-rw-r--r--util/tran/windtop2usr.c192
-rw-r--r--util/umodestat.c69
-rw-r--r--util/uno/0readme22
-rw-r--r--util/uno/Makefile59
-rw-r--r--util/uno/collect_uno.c77
-rw-r--r--util/uno/conflict_uno.c65
-rw-r--r--util/uno/fix_uno.c520
-rw-r--r--util/webc.c137
301 files changed, 112879 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..c37954d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,93 @@
+*.o
+*.a
+*.so
+*.bak
+*.duck
+*.old*
+*.swp
+*.swo
+/daemon/bguard
+/daemon/bhttpd
+/daemon/bmtad
+/daemon/bnntpd
+/daemon/bpop3d
+/daemon/gemd
+/daemon/xchatd
+/innbbsd/bbslink
+/innbbsd/innbbsd
+/maple/bbsd
+/util/account
+/util/acl-sort
+/util/backup/backupacct
+/util/backup/backupbrd
+/util/backup/backupgem
+/util/backup/backupoth
+/util/backup/backupusr
+/util/backup/restoreacct
+/util/backup/restorebrd
+/util/backup/restoregem
+/util/backup/restoreusr
+/util/bbsmail
+/util/bquota
+/util/brdmail
+/util/camera
+/util/changeperm
+/util/counter
+/util/gem-index
+/util/give_paycheck
+/util/hdr-dump
+/util/mailpost
+/util/outgo
+/util/poststat
+/util/reaper
+/util/redir
+/util/setperm
+/util/setusr
+/util/showACCT
+/util/showBRD
+/util/showDIR
+/util/showUSR
+/util/showperm
+/util/topgem
+/util/topsong
+/util/topusr
+/util/tran/ats2bmw
+/util/tran/ats2brd
+/util/tran/ats2gem
+/util/tran/ats2mf
+/util/tran/ats2pal
+/util/tran/ats2usr
+/util/tran/brd2gem
+/util/tran/cola2brd
+/util/tran/cola2gem
+/util/tran/cola2post
+/util/tran/cola2usr
+/util/tran/fb2brd
+/util/tran/fb2gem
+/util/tran/fb2pal
+/util/tran/fb2usr
+/util/tran/mag2brd
+/util/tran/mag2gem
+/util/tran/mag2usr
+/util/tran/snap2brd
+/util/tran/snap2usr
+/util/tran/sob2brd
+/util/tran/sob2gem
+/util/tran/sob2pal
+/util/tran/sob2usr
+/util/tran/transacct
+/util/tran/transbrd
+/util/tran/wd2bmw
+/util/tran/wd2brd
+/util/tran/wd2gem
+/util/tran/wd2list
+/util/tran/wd2mf
+/util/tran/wd2pal
+/util/tran/wd2pip
+/util/tran/wd2usr
+/util/tran/windtop2brd
+/util/tran/windtop2pip
+/util/tran/windtop2usr
+/util/uno/collect_uno
+/util/uno/conflict_uno
+/util/uno/fix_uno
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..d3f5358
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,147 @@
+# ------------------------------------------------------- #
+# src/Makefile ( NTHU CS MapleBBS Ver 3.10 ) #
+# ------------------------------------------------------- #
+# target : Makefile for ALL #
+# create : 00/02/12 #
+# update : / / #
+# ------------------------------------------------------- #
+
+
+# ¤ä´©ªº OS-type
+# sun linux solaris sol-x86 freebsd bsd cygwin
+
+# »Ý­n compile ªº¥Ø¿ý
+# lib daemon innbbsd maple so game pip util util/backup util/tran util/uno
+
+
+all:
+ @echo "Please enter 'make sys-type', "
+ @echo " make sun : for Sun-OS 4.x and maybe some BSD systems, cc or gcc"
+ @echo " make linux : for Linux"
+ @echo " make solaris : for Sun-OS 5.x gcc"
+ @echo " make sol-x86 : for Solaris 7 x86"
+ @echo " make freebsd : for BSD 4.4 systems"
+ @echo " make bsd : for BSD systems, cc or gcc, if not in the above lists"
+ @echo " make cygwin : for Microsoft Windows and Cygwin gcc"
+
+
+sun:
+ @cd lib; make
+ @cd daemon; make sun
+ @cd innbbsd; make sun
+ @cd maple; make sun
+ @cd so; make sun
+ @cd game; make sun
+ @cd pip; make sun
+ @cd util; make sun
+ @cd util/backup; make sun
+ @cd util/tran; make sun
+ @cd util/uno; make sun
+
+linux:
+ @cd lib; make
+ @cd daemon; make linux
+ @cd innbbsd; make linux
+ @cd maple; make linux
+ @cd so; make linux
+ @cd game; make linux
+ @cd pip; make linux
+ @cd util; make linux
+ @cd util/backup; make linux
+ @cd util/tran; make linux
+ @cd util/uno; make linux
+
+solaris:
+ @cd lib; make
+ @cd daemon; make solaris
+ @cd innbbsd; make solaris
+ @cd maple; make solaris
+ @cd so; make solaris
+ @cd game; make solaris
+ @cd pip; make solaris
+ @cd util; make solaris
+ @cd util/backup; make solaris
+ @cd util/tran; make solaris
+ @cd util/uno; make solaris
+
+sol-x86:
+ @cd lib; make
+ @cd daemon; make sol-x86
+ @cd innbbsd; make sol-x86
+ @cd maple; make sol-x86
+ @cd so; make sol-x86
+ @cd game; make sol-x86
+ @cd pip; make sol-x86
+ @cd util; make sol-x86
+ @cd util/backup; make sol-x86
+ @cd util/tran; make sol-x86
+ @cd util/uno; make sol-x86
+
+freebsd:
+ @cd lib; make
+ @cd daemon; make freebsd
+ @cd innbbsd; make freebsd
+ @cd maple; make freebsd
+ @cd so; make freebsd
+ @cd game; make freebsd
+ @cd pip; make freebsd
+ @cd util; make freebsd
+ @cd util/backup; make freebsd
+ @cd util/tran; make freebsd
+ @cd util/uno; make freebsd
+
+bsd:
+ @cd lib; make
+ @cd daemon; make bsd
+ @cd innbbsd; make bsd
+ @cd maple; make bsd
+ @cd so; make bsd
+ @cd game; make bsd
+ @cd pip; make bsd
+ @cd util; make bsd
+ @cd util/backup; make bsd
+ @cd util/tran; make bsd
+ @cd util/uno; make bsd
+
+cygwin:
+ @cd lib; make
+ @cd daemon; make cygwin
+ @cd innbbsd; make cygwin
+ @cd maple; make cygwin
+ @cd so; make cygwin
+ @cd game; make cygwin
+ @cd pip; make cygwin
+ @cd util; make cygwin
+ @cd util/backup; make cygwin
+ @cd util/tran; make cygwin
+ @cd util/uno; make cygwin
+
+install:
+ @cd daemon; make install
+ @cd innbbsd; make install
+ @cd maple; make install
+ @cd so; make install
+ @cd game; make install
+ @cd pip; make install
+ @cd util; make install
+ @cd util/backup; make install
+ @cd util/tran; make install
+ @cd util/uno; make install
+
+update:
+ @cd daemon; make update
+ @cd innbbsd; make update
+ @cd maple; make update
+
+clean:
+ @cd lib; make clean
+ @cd daemon; make clean
+ @cd innbbsd; make clean
+ @cd maple; make clean
+ @cd so; make clean
+ @cd game; make clean
+ @cd pip; make clean
+ @cd util; make clean
+ @cd util/backup; make clean
+ @cd util/tran; make clean
+ @cd util/uno; make clean
diff --git a/daemon/Makefile b/daemon/Makefile
new file mode 100644
index 0000000..b65a717
--- /dev/null
+++ b/daemon/Makefile
@@ -0,0 +1,65 @@
+# ------------------------------------------------------- #
+# util/Makefile ( NTHU CS MapleBBS Ver 3.10 ) #
+# ------------------------------------------------------- #
+# target : Makefile #
+# create : 95/03/29 #
+# update : 95/12/15 #
+# ------------------------------------------------------- #
+
+
+# ------------------------------------------------------ #
+# ¤U¦Cªº make rules ¤£»Ý­×§ï #
+# ------------------------------------------------------ #
+
+#EXE = bguard bhttpd bmtad bnntpd bpop3d gemd xchatd
+EXE = bguard bmtad bnntpd bpop3d gemd xchatd
+
+all:
+ @echo "Please enter 'make sys-type', "
+ @echo " make sun : for Sun-OS 4.x and maybe some BSD systems, cc or gcc"
+ @echo " make linux : for Linux"
+ @echo " make solaris : for Sun-OS 5.x gcc"
+ @echo " make sol-x86 : for Solaris 7 x86"
+ @echo " make freebsd : for BSD 4.4 systems"
+ @echo " make bsd : for BSD systems, cc or gcc, if not in the above lists"
+ @echo " make cygwin : for Microsoft Windows and Cygwin gcc"
+
+sun:
+ @$(MAKE) CC=gcc CFLAGS="-O2 -pipe -fomit-frame-pointer -Wunused -I../include" LDFLAGS="-s -L../lib -ldao" $(EXE)
+
+linux:
+ @$(MAKE) CC=gcc CFLAGS="-DLINUX -O2 -pipe -I../include -fomit-frame-pointer -Wunused" LDFLAGS="-s -L../lib -ldao -lcrypt -lresolv" $(EXE)
+
+solaris:
+ @$(MAKE) CC=gcc CFLAGS="-DSOLARIS -O2 -pipe -I../include -fomit-frame-pointer -Wunused" LDFLAGS="-s -L../lib -ldao -lsocket -lresolv -lnsl -L/usr/ucblib -lucb -R/usr/ucblib" $(EXE)
+
+sol-x86:
+ @$(MAKE) CC=gcc CFLAGS="-DSOLARIS -O2 -I../include -fomit-frame-pointer -Wunused" LDFLAGS="-s -L../lib -ldao -lsocket -lresolv -lnsl" $(EXE)
+
+freebsd:
+ @$(MAKE) CC=gcc CFLAGS="-DBSD44 -O2 -pipe -I../include -fomit-frame-pointer -Wunused" LDFLAGS="-s -L../lib -ldao -lcrypt" $(EXE)
+
+bsd:
+ @$(MAKE) CC=gcc CFLAGS="-DBSD44 -O2 -pipe -I../include -fomit-frame-pointer -Wunused" LDFLAGS="-s -L../lib -ldao" $(EXE)
+
+cygwin:
+ @$(MAKE) CC=gcc CFLAGS="-DLINUX -DCYGWIN -O2 -pipe -I../include -fomit-frame-pointer -Wunused" LDFLAGS="-s -L../lib -ldao -lcrypt -lresolv -lcygipc" $(EXE)
+
+
+.c: ; $(CC) -o $@ $@.c $(CFLAGS) $(LDFLAGS)
+
+
+install: $(EXE)
+ install -m 0700 $? $(HOME)/bin
+
+update:
+ -csh -c "kill `tail -1 $(HOME)/run/bguard.pid | awk '{print $$1}'`";exit 0
+ -csh -c "kill `tail -1 $(HOME)/run/bhttp.pid | awk '{print $$1}'`";exit 0
+ -csh -c "kill `tail -1 $(HOME)/run/bmta.pid | awk '{print $$1}'`";exit 0
+ -csh -c "kill `tail -1 $(HOME)/run/bnntp.pid | awk '{print $$1}'`";exit 0
+ -csh -c "kill `tail -1 $(HOME)/run/pop3.pid | awk '{print $$1}'`";exit 0
+ -csh -c "kill `tail -1 $(HOME)/run/gemd.pid | awk '{print $$1}'`";exit 0
+ -csh -c "kill `tail -1 $(HOME)/run/chat.pid | awk '{print $$1}'`";exit 0
+
+clean:
+ rm -f $(EXE) *.exe *.o *~
diff --git a/daemon/bguard.c b/daemon/bguard.c
new file mode 100644
index 0000000..6a05755
--- /dev/null
+++ b/daemon/bguard.c
@@ -0,0 +1,1229 @@
+/*-------------------------------------------------------*/
+/* util/bguard.c ( NTHU CS MapleBBS Ver 3.00 ) */
+/*-------------------------------------------------------*/
+/* target : BBS finger daemon ¦C¥X¯¸¤º¨Ï¥ÎªÌ¸ê®Æ */
+/* create : 96/11/20 */
+/* update : 96/12/15 */
+/*-------------------------------------------------------*/
+/* syntax : bguard */
+/*-------------------------------------------------------*/
+/* notice : ushm (utmp shared memory) synchronize */
+/*-------------------------------------------------------*/
+
+
+#define _MODES_C_
+#define VERBOSE
+#define WATCH_DOG
+
+
+#include "bbs.h"
+
+
+#include <sys/ipc.h>
+
+#ifdef HAVE_SEM
+#include <sys/sem.h>
+#endif
+
+#include <sys/shm.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+#include <netinet/tcp.h>
+
+
+static int gline;
+
+
+#ifdef WATCH_DOG
+# define MYDOG gline = __LINE__
+#else
+# define MYDOG /* NOOP */
+#endif
+
+
+#define GUARD_LOGFILE "run/bguard.log"
+#define GUARD_PIDFILE "run/bguard.pid"
+
+
+#define LOAD_INTERVAL 90 /* check system load */
+#define GUARD_INTERVAL 60 /* check user status */
+#define FINGER_INTERVAL (30 * 60)
+
+
+#define FINGER_TIMEOUT (60 * 3)
+
+
+#define TCP_QLEN 3
+#define TCP_BUFSIZ (512 * 7)
+#define TCP_LINSIZ 256
+#define TCP_RCVSIZ 128
+
+
+/* ----------------------------------------------------- */
+/* client connection structure */
+/* ----------------------------------------------------- */
+
+
+typedef struct Agent Agent;
+
+
+struct Agent
+{
+ Agent *next;
+ int sock;
+ int state;
+ int locus;
+ time_t uptime; /* «Ø¥ß connection ªº®É¶¡ */
+
+ int count; /* ½u¤W¦@¦³¦h¤Ö¤H¡H */
+ UTMP *uentp;
+
+ char pool[TCP_BUFSIZ]; /* buffered I/O pool */
+
+ /* Thor:980726: ending zero for string */
+ char ZERO;
+};
+
+
+/* ----------------------------------------------------- */
+/* connection state */
+/* ----------------------------------------------------- */
+
+
+#define CS_FREE 0x00
+#define CS_READING 0x01
+#define CS_WRITING 0x02
+
+
+/* ----------------------------------------------------- */
+/* operation log and debug information */
+/* ----------------------------------------------------- */
+
+
+static FILE *flog; /* log file descriptor */
+
+
+extern int errno;
+
+
+static void
+logit(key, msg)
+ char *key;
+ char *msg;
+{
+ time_t now;
+ struct tm *p;
+
+ time(&now);
+ p = localtime(&now);
+ fprintf(flog, "%02d/%02d %02d:%02d:%02d %-8s%s\n",
+ p->tm_mon + 1, p->tm_mday,
+ p->tm_hour, p->tm_min, p->tm_sec, key, msg);
+}
+
+
+static void
+log_open()
+{
+ FILE *fp;
+
+ umask(077);
+
+ if (fp = fopen(GUARD_PIDFILE, "w"))
+ {
+ fprintf(fp, "%d\n", getpid());
+ fclose(fp);
+ }
+
+ flog = fopen(GUARD_LOGFILE, "a");
+ logit("START", "guard (finger) daemon");
+}
+
+
+/*-------------------------------------------------------*/
+/* .UTMP cache */
+/*-------------------------------------------------------*/
+
+
+static UCACHE *ushm;
+static UTMP *ushm_head, *ushm_tail;
+
+
+static void
+attach_err(shmkey, name)
+ int shmkey;
+ char *name;
+{
+ char buf[80];
+
+ sprintf(buf, "error, key = %x", shmkey);
+ logit(name, buf);
+ exit(1);
+}
+
+
+#ifdef HAVE_SEM
+/* ----------------------------------------------------- */
+/* semaphore : for critical section */
+/* ----------------------------------------------------- */
+
+
+static int mysemid;
+
+
+static void
+resolve_sem()
+{
+ int semid;
+
+ union semun
+ {
+ int val;
+ struct semid_ds *buf;
+ ushort *array;
+ } arg =
+ {
+ 1
+ };
+
+ semid = semget(BSEM_KEY, 1, 0);
+ if (semid == -1)
+ {
+ semid = semget(BSEM_KEY, 1, IPC_CREAT | BSEM_FLG);
+ if (semid == -1)
+ attach_err(BSEM_KEY, "semget");
+ semctl(semid, 0, SETVAL, arg);
+ }
+ mysemid = semid;
+}
+
+
+static void
+sem_lock(op)
+ int op; /* op is BSEM_ENTER or BSEM_LEAVE */
+{
+ struct sembuf sops;
+
+ sops.sem_num = 0;
+ sops.sem_flg = SEM_UNDO;
+ sops.sem_op = op;
+ semop(mysemid, &sops, 1);
+}
+#endif /* HAVE_SEM */
+
+
+static void
+init_ushm()
+{
+ UCACHE *xshm;
+ int shmsize;
+ int shmid;
+
+ shmsize = sizeof(UCACHE);
+ shmid = shmget(UTMPSHM_KEY, shmsize, 0);
+ if (shmid < 0)
+ {
+ shmid = shmget(UTMPSHM_KEY, shmsize, IPC_CREAT | 0600);
+ if (shmid < 0)
+ attach_err(UTMPSHM_KEY, "shmget");
+ }
+ else
+ {
+ shmsize = 0;
+ }
+
+ xshm = (UCACHE *) shmat(shmid, NULL, 0);
+ if (xshm == (UCACHE *) - 1)
+ attach_err(UTMPSHM_KEY, "shmat");
+
+ if (shmsize)
+ {
+ memset(xshm, 0, shmsize);
+ if (xshm->mbase < xshm->mpool)
+ xshm->mbase = xshm->mpool;
+ }
+
+ ushm = xshm;
+ ushm_head = xshm->uslot;
+ ushm_tail = ushm_head + MAXACTIVE;
+}
+
+
+static void
+ushm_guard()
+{
+ static int flip;
+ int flop;
+ usint count;
+ UTMP *uentp, *uceil, *utail;
+ int idle;
+ pid_t pid;
+ UCACHE *xshm;
+ char buf[128];
+#ifdef DETAIL_IDLETIME
+ time_t now;
+#endif
+
+ flop = ++flip;
+ count = 0;
+ uentp = ushm_head;
+ utail = ushm_tail;
+ uceil = uentp;
+
+#ifdef HAVE_SEM
+ sem_lock(BSEM_ENTER);
+#endif
+
+#ifdef DETAIL_IDLETIME
+ time(&now);
+#endif
+
+ do
+ {
+ flop++;
+ if (pid = uentp->pid)
+ {
+#ifdef DETAIL_IDLETIME
+ idle = (now - uentp->idle_time) / 60;
+#else
+ idle = uentp->idle_time;
+#endif
+ if (flop & 15)
+ {
+#ifdef TIME_KICKER
+ if (idle >= IDLE_TIMEOUT)
+ {
+ errno = 0;
+ if ((kill(pid, SIGTERM) < 0) && (errno == ESRCH))
+ {
+ uentp->pid = uentp->userno = 0;
+ ushm->count--;
+ }
+ }
+ else
+#endif
+ {
+ if (uceil < uentp)
+ uceil = uentp;
+ count++;
+ }
+ }
+ else
+ {
+ int sig;
+
+ errno = sig = 0;
+
+#ifdef TIME_KICKER
+ if (idle >= IDLE_TIMEOUT)
+ sig = SIGTERM;
+#endif
+
+ if ((kill(pid, sig) < 0) && (errno == ESRCH))
+ {
+ uentp->pid = uentp->userno = 0;
+ ushm->count--;
+ }
+ else
+ {
+ if (!sig)
+ {
+ if (uceil < uentp)
+ uceil = uentp;
+ count++;
+ }
+ }
+ }
+ }
+ } while (++uentp < utail);
+
+ xshm = ushm;
+ xshm->count = count;
+ xshm->offset = (void *) uceil - (void *) ushm_head;
+
+#ifdef HAVE_SEM
+ sem_lock(BSEM_LEAVE);
+#endif
+
+#ifdef VERBOSE
+ sprintf(buf, "%d %p (%p - %p)", count, uceil, ushm_head, utail);
+ logit("count", buf);
+#endif
+}
+
+
+/*-------------------------------------------------------*/
+/* check system / memory / CPU loading */
+/*-------------------------------------------------------*/
+
+
+static int fkmem;
+
+
+#ifdef SOLARIS
+static void
+chkload_init()
+{
+#include <nlist.h>
+#define VMUNIX "/dev/ksyms"
+#define KMEM "/dev/kmem"
+ /* Thor.981207: °O±ocheck permission ­nbbs readable */
+
+ static struct nlist nlst[] =
+ {
+ {"avenrun"},
+ {0}
+ };
+
+ int kmem;
+ long offset;
+
+ nlist(VMUNIX, nlst);
+ if (nlst[0].n_type == 0)
+ exit(1);
+ offset = (long) nlst[0].n_value;
+
+ if ((kmem = open(KMEM, O_RDONLY)) == -1)
+ exit(1);
+
+ if (lseek(kmem, offset, L_SET) == -1)
+ exit(1);
+
+ fkmem = kmem;
+}
+#endif
+
+
+static void
+chkload()
+{
+
+ struct
+ {
+ int avgload;
+ double sysload[3];
+ } myload;
+#define cpu_load myload.sysload
+
+
+#if defined(LINUX)
+ FILE *fp;
+
+ MYDOG;
+ fp = fopen("/proc/loadavg", "r");
+ if (!fp)
+ cpu_load[0] = cpu_load[1] = cpu_load[2] = 0;
+ else
+ {
+ float av[3];
+
+ fscanf(fp, "%g %g %g", av, av + 1, av + 2);
+ fclose(fp);
+ cpu_load[0] = av[0];
+ cpu_load[1] = av[1];
+ cpu_load[2] = av[2];
+ }
+
+#elif defined(BSD44)
+ getloadavg(cpu_load, 3);
+
+#else
+
+ long avenrun[3];
+ int i;
+
+ i = fkmem;
+
+ if (read(i, (char *) avenrun, sizeof(avenrun)) == -1)
+ exit(1);
+
+ lseek(i, -(off_t) sizeof(avenrun), SEEK_CUR);
+
+#define loaddouble(la) ((double)(la) / (1 << 8))
+
+ for (i = 0; i < 3; i++)
+ cpu_load[i] = loaddouble(avenrun[i]);
+
+ /* Thor.980728: lkchu patch: linux³¡¤À¤Î bsd³¡ ¤À¤w¦³©w cpu_load */
+
+#endif
+
+ myload.avgload = cpu_load[0] + cpu_load[1] * 4;
+ /* memcpy(&ushm->avgload, &myload, sizeof(myload)); */
+
+ ushm->avgload = myload.avgload;
+ ushm->sysload[0] = myload.sysload[0];
+ ushm->sysload[1] = myload.sysload[1];
+ ushm->sysload[2] = myload.sysload[2];
+}
+
+
+/* ----------------------------------------------------- */
+/* server side stuff */
+/* ----------------------------------------------------- */
+
+
+static char *
+mail_string(fpath)
+ char *fpath;
+{
+ char *answer;
+ int fd, size;
+ struct stat st;
+
+ answer = "³£¬Ý¹L¤F";
+ if ((fd = open(fpath, O_RDONLY)) >= 0)
+ {
+ if (!fstat(fd, &st) && (size = st.st_size) > 0)
+ {
+ char *fimage;
+ HDR *fhdr;
+
+ fimage = (char *) malloc(size);
+ read(fd, fimage, size);
+ fhdr = (HDR *) (fimage + size);
+ while (--fhdr >= (HDR *) fimage)
+ {
+ if (!(fhdr->xmode & MAIL_READ))
+ {
+ answer = "¦³·s«H¥ó";
+ break;
+ }
+ }
+ free(fimage);
+ }
+ close(fd);
+ }
+
+ return answer;
+}
+
+
+static void
+serve_finger(ap)
+ Agent *ap;
+{
+ char *base, *head, fpath[128];
+ int fd;
+
+ base = head = ap->pool;
+
+ /* Thor.980726: pool¤º®e¤£·|¶W¹L idlen */
+ head[IDLEN] = '\0';
+
+
+ sprintf(fpath, "usr/%c/%s/.ACCT", *head, head);
+
+ MYDOG;
+ if ((fd = open(fpath, O_RDONLY)) >= 0)
+ {
+ char *str, *mailstr, *modestr;
+ UTMP *uentp, *utail;
+ int len;
+ ACCT acct;
+
+ read(fd, &acct, sizeof(ACCT));
+ close(fd);
+
+ /* ¬O§_¦³·s«H¥óÁÙ¨S¬Ý¡H */
+
+ str = (char *) strchr(fpath, '.');
+ strcpy(str, FN_DIR);
+ MYDOG;
+ mailstr = mail_string(fpath);
+
+ /* ¬O§_¦b½u¤W¡H */
+
+ MYDOG;
+
+ uentp = ushm_head;
+ utail = ushm_tail;
+ fd = acct.userno;
+ modestr = "¤£¦b¯¸¤W";
+
+ do
+ {
+ if (fd == uentp->userno && !(uentp->ufo & (UFO_CLOAK | UFO_SUPERCLOAK)))
+ {
+ modestr = ModeTypeTable[uentp->mode];
+ break;
+ }
+ } while (++uentp < utail);
+
+ sprintf(head, "%s(%s) ¦@¤W¯¸ %d ¦¸¡Aµoªí¤å³¹ %d ½g¡C\n"
+ "³Ìªñ(%s)¨Ó¦Û(%s)\n%s³q¹L¨­¤À»{ÃÒ [°ÊºA] %s [«H½c] %s\n",
+ acct.userid, acct.username, acct.numlogins, acct.numposts,
+ Btime(&(acct.lastlogin)), acct.lasthost,
+ acct.userlevel & PERM_VALID ? "¤w¸g" : "©|¥¼",
+ modestr, mailstr);
+
+ head += strlen(head);
+
+ /* Åã¥Ü [¦W¤ù/­pµeÀÉ] */
+
+ MYDOG;
+ strcpy(str, FN_PLANS);
+ if ((fd = open(fpath, O_RDONLY)) >= 0)
+ {
+ strcpy(head, "[­pµe]\n");
+ head += strlen(head);
+
+ /* °²³] buffer ºïºï¦³¾l¡AÅý¨Æ±¡Â²¤Æ */
+
+ len = read(fd, head, TCP_BUFSIZ - (head - base) - 10);
+ close(fd);
+
+ if (len > 0)
+ {
+ head += len;
+ strcpy(head, "\033[m\n");
+ head += strlen(head);
+ }
+ }
+ }
+ else
+ {
+ strcat(head, " ==> not exist here.\n");
+ head += strlen(head);
+ }
+
+ ap->locus = head - base;
+ ap->count = -1; /* ¥Nªí end of transmission */
+}
+
+
+static void
+serve_userlist(ap)
+ Agent *ap;
+{
+ int count;
+ UTMP *uentp, *utail;
+ char *base, *head, *tail;
+
+ count = ap->count;
+ uentp = ap->uentp;
+ utail = ushm_tail;
+
+ base = ap->pool;
+ head = base + ap->locus;
+ tail = base + TCP_BUFSIZ - TCP_LINSIZ;
+
+ for (;;)
+ {
+ if (uentp->pid && uentp->userno &&
+#ifdef HAVE_SUPERCLOAK
+ !(uentp->ufo & UFO_SUPERCLOAK) &&
+#endif
+ !(uentp->ufo & UFO_CLOAK)) /* lkchu.990118: Áô¨­¤£Åã¥Ü */
+ {
+ sprintf(head, "%-13s%-25s%-30.29s%s\n",
+ uentp->userid, uentp->username, uentp->from,
+ ModeTypeTable[uentp->mode]);
+ count++;
+ head += strlen(head);
+ if (head > tail)
+ {
+ uentp++;
+ break;
+ }
+ }
+
+ if (++uentp >= utail)
+ {
+ sprintf(head, "============ ======================== ============================= ==========\n"
+ "¡i" BBSNAME "¡j Total users = %d\n", count);
+ head += strlen(head);
+ count = -1;
+ break;
+ }
+ }
+
+ ap->uentp = uentp;
+ ap->count = count;
+ ap->locus = head - base;
+}
+
+
+/* ----------------------------------------------------- */
+/* client's service dispatcher */
+/* ----------------------------------------------------- */
+
+
+static void
+agent_serve(ap)
+ Agent *ap;
+{
+ char *cmd, *str;
+ int ch;
+
+ cmd = str = ap->pool;
+
+ while (ch = *str)
+ {
+ str++;
+ if (ch != ' ' && ch != '\t')
+ {
+ if (ch >= 'A' && ch <= 'Z')
+ ch |= 0x20;
+ else if (ch == '.' || ch == '@')
+ {
+ *cmd = '\0';
+ break;
+ }
+ *cmd++ = ch;
+ }
+ }
+
+ str = ap->pool;
+ ap->state = CS_WRITING;
+ if (str == cmd)
+ {
+ strcpy(str, "ID Nick From Mode\n"
+ "============ ======================== ============================= ==========\n");
+ ap->locus = strlen(str);
+ ap->count = 0;
+ ap->uentp = ushm_head;
+ serve_userlist(ap);
+ }
+ else
+ {
+ serve_finger(ap);
+ }
+}
+
+
+/* ----------------------------------------------------- */
+/* send output to client */
+/* ----------------------------------------------------- */
+
+
+static int
+agent_write(ap)
+ Agent *ap;
+{
+ int len, bytes;
+ char *str;
+
+ len = ap->locus;
+ str = ap->pool;
+ bytes = send(ap->sock, str, len, 0);
+ if (bytes <= 0)
+ {
+ len = errno;
+ if (len != EWOULDBLOCK)
+ {
+ logit("write", strerror(len));
+ return 0;
+ }
+
+ return -1;
+ }
+
+ len -= bytes;
+ ap->locus = len;
+ if (len)
+ {
+ memcpy(str, str + bytes, len);
+ return bytes;
+ }
+
+ if (ap->count >= 0)
+ {
+ serve_userlist(ap);
+ return bytes;
+ }
+
+ return 0;
+}
+
+
+/* ----------------------------------------------------- */
+/* receive request from client */
+/* ----------------------------------------------------- */
+
+
+static int
+agent_read(ap)
+ Agent *ap;
+{
+ int pos, len, cc;
+ char *str;
+
+ pos = ap->locus;
+ str = &ap->pool[pos];
+ len = recv(ap->sock, str, BMIN(TCP_RCVSIZ, sizeof(ap->pool)-pos), 0);
+ /* Thor.980726: Á×§K pool overflow */
+
+ if (len <= 0)
+ {
+ len = errno;
+ if (len != EWOULDBLOCK)
+ {
+ logit("read", strerror(len));
+ return 0;
+ }
+
+ return -1; /* would block, so leave it to do later */
+ }
+
+ str[len] = '\0';
+
+ while (cc = *str)
+ {
+ if (cc == '\r' || cc == '\n')
+ {
+ *str = '\0';
+ agent_serve(ap);
+ return 1;
+ }
+ str++;
+ }
+
+ ap->locus = pos + len;
+ return 1;
+}
+
+
+/* ---------------------------------------------------- */
+/* server core routines */
+/* ---------------------------------------------------- */
+
+
+static void
+servo_daemon(inetd)
+ int inetd;
+{
+ int fd, value;
+ char buf[80];
+ struct sockaddr_in sin;
+ struct linger ld;
+
+ /*
+ * 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;
+ struct tm *dummy_time;
+
+#ifdef HAVE_RLIMIT
+ struct rlimit limit;
+#endif
+
+ time(&dummy);
+ dummy_time = gmtime(&dummy);
+ dummy_time = localtime(&dummy);
+ strftime(buf, 80, "%d/%b/%Y:%H:%M:%S", dummy_time);
+
+#ifdef HAVE_RLIMIT
+ /* --------------------------------------------------- */
+ /* adjust the resource limit */
+ /* --------------------------------------------------- */
+
+ getrlimit(RLIMIT_NOFILE, &limit);
+ limit.rlim_cur = limit.rlim_max;
+ setrlimit(RLIMIT_NOFILE, &limit);
+
+ limit.rlim_cur = limit.rlim_max = 16 * 1024 * 1024;
+ setrlimit(RLIMIT_FSIZE, &limit);
+
+ limit.rlim_cur = limit.rlim_max = 4 * 1024 * 1024;
+ setrlimit(RLIMIT_DATA, &limit);
+
+#ifdef SOLARIS
+#define RLIMIT_RSS RLIMIT_AS /* Thor.981206: port for solaris 2.6 */
+#endif
+
+ setrlimit(RLIMIT_RSS, &limit);
+
+ limit.rlim_cur = limit.rlim_max = 0;
+ setrlimit(RLIMIT_CORE, &limit);
+#endif
+
+ /* --------------------------------------------------- */
+ /* detatch & daemonize */
+ /* --------------------------------------------------- */
+
+ close(1);
+ close(2);
+
+ if (inetd)
+ return;
+
+ close(0);
+
+ if (fork())
+ exit(0);
+
+ setsid();
+
+ if (fork())
+ exit(0);
+
+ /* --------------------------------------------------- */
+ /* setup socket */
+ /* --------------------------------------------------- */
+
+
+ fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+
+ value = 1;
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &value, sizeof(value));
+
+ ld.l_onoff = ld.l_linger = 0;
+ setsockopt(fd, SOL_SOCKET, SO_LINGER, (char *) &ld, sizeof(ld));
+
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(FINGER_PORT);
+ sin.sin_addr.s_addr = htonl(INADDR_ANY);
+ memset(sin.sin_zero, 0, sizeof(sin.sin_zero));
+
+ if (bind(fd, (struct sockaddr *) & sin, sizeof(sin)) ||
+ listen(fd, TCP_QLEN))
+ exit(1);
+}
+
+
+static void
+server_usage()
+{
+ struct rusage ru;
+
+ if (getrusage(RUSAGE_SELF, &ru))
+ return;
+
+ fprintf(flog, "\n[Server Usage]\n\n"
+ "user time: %.6f\n"
+ "system time: %.6f\n"
+ "maximum resident set size: %lu P\n"
+ "integral resident set size: %lu\n"
+ "page faults not requiring physical I/O: %d\n"
+ "page faults requiring physical I/O: %d\n"
+ "swaps: %d\n"
+ "block input operations: %d\n"
+ "block output operations: %d\n"
+ "messages sent: %d\n"
+ "messages received: %d\n"
+ "signals received: %d\n"
+ "voluntary context switches: %d\n"
+ "involuntary context switches: %d\n"
+ "gline: %d\n\n",
+
+ (double) ru.ru_utime.tv_sec + (double) ru.ru_utime.tv_usec / 1000000.0,
+ (double) ru.ru_stime.tv_sec + (double) ru.ru_stime.tv_usec / 1000000.0,
+ ru.ru_maxrss,
+ ru.ru_idrss,
+ ru.ru_minflt,
+ ru.ru_majflt,
+ ru.ru_nswap,
+ ru.ru_inblock,
+ ru.ru_oublock,
+ ru.ru_msgsnd,
+ ru.ru_msgrcv,
+ ru.ru_nsignals,
+ ru.ru_nvcsw,
+ ru.ru_nivcsw,
+ gline);
+}
+
+
+static void
+sig_trap(sig)
+ int sig;
+{
+ char buf[80];
+
+ close(fkmem);
+ sprintf(buf, "signal %d", sig);
+ logit("EXIT", buf);
+ server_usage();
+ fclose(flog);
+ exit(1);
+}
+
+
+static void
+reaper()
+{
+ while (waitpid(-1, NULL, WNOHANG | WUNTRACED) > 0)
+ ;
+}
+
+
+static void
+main_signals()
+{
+ struct sigaction act;
+
+ /* sigblock(sigmask(SIGPIPE)); */
+ /* Thor.981206: ²Î¤@ POSIX ¼Ð·Ç¥Îªk */
+
+ /* act.sa_mask = 0; */ /* Thor.981105: ¼Ð·Ç¥Îªk */
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+
+ act.sa_handler = reaper;
+ sigaction(SIGCHLD, &act, NULL);
+
+ act.sa_handler = sig_trap;
+ sigaction(SIGTERM, &act, NULL);
+ sigaction(SIGBUS, &act, NULL);
+ sigaction(SIGSEGV, &act, NULL);
+
+ act.sa_handler = server_usage;
+ sigaction(SIGPROF, &act, NULL);
+
+ /* Thor.981206: lkchu patch: ²Î¤@ POSIX ¼Ð·Ç¥Îªk */
+ /* ¦b¦¹­É¥Î sigset_t act.sa_mask */
+ sigaddset(&act.sa_mask, SIGPIPE);
+ sigprocmask(SIG_BLOCK, &act.sa_mask, NULL);
+
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int csock, nfds, state;
+ Agent **FBI, *Scully, *Mulder, *agent;
+ fd_set rset, wset, xset;
+ struct timeval tv;
+ time_t uptime, tcheck, tguard, tagent;
+
+ state = 0;
+
+ while ((nfds = getopt(argc, argv, "hid")) != -1)
+ {
+ switch (nfds)
+ {
+ case 'i':
+ state = 1;
+ break;
+
+ case 'd':
+ break;
+
+ case 'h':
+ default:
+
+ fprintf(stderr, "Usage: %s [options]\n"
+ "\t-i start from inetd with wait option\n"
+ "\t-d debug mode\n"
+ "\t-h help\n",
+ argv[0]);
+ exit(0);
+ }
+ }
+
+ servo_daemon(state);
+ /* start_daemon(); */
+ setgid(BBSGID);
+ setuid(BBSUID);
+ chdir(BBSHOME);
+ main_signals();
+ log_open();
+
+#ifdef HAVE_SEM
+ resolve_sem();
+#endif
+
+ init_ushm();
+#ifdef SOLARIS
+ chkload_init();
+#endif
+
+ /* Thor.981221: for future reservation bug */
+ tcheck = tguard = tagent = 0;
+
+ Scully = Mulder = NULL;
+
+ for (;;) /* Thor.981221: µù¸Ñ: Main loop begin */
+ {
+ uptime = time(0);
+
+ /* system guard / resource and garbage collection */
+
+ if (uptime > tcheck)
+ {
+ chkload();
+ tcheck = uptime + LOAD_INTERVAL;
+
+ if (uptime > tguard)
+ {
+ ushm_guard();
+ tguard = uptime + GUARD_INTERVAL;
+ }
+
+ if (uptime > tagent)
+ {
+ tagent = uptime - FINGER_TIMEOUT;
+
+ for (FBI = &Scully; agent = *FBI;)
+ {
+ if (agent->uptime < tagent)
+ {
+ csock = agent->sock;
+ if (csock > 0)
+ {
+ shutdown(csock, 2);
+ close(csock);
+ }
+
+ *FBI = agent->next;
+
+ agent->next = Mulder;
+ Mulder = agent;
+ }
+ else
+ {
+ FBI = &(agent->next);
+ }
+ }
+
+ tagent = uptime + FINGER_INTERVAL;
+ fflush(flog);
+ }
+ }
+
+ /* Set up the fdsets. */
+
+ FD_ZERO(&rset);
+ FD_ZERO(&wset);
+ FD_ZERO(&xset);
+
+ FD_SET(0, &rset);
+ nfds = 0;
+
+ for (agent = Scully; agent; agent = agent->next)
+ {
+ csock = agent->sock;
+ state = agent->state;
+
+ if (nfds < csock)
+ nfds = csock;
+
+ FD_SET(csock, state == CS_READING ? &rset : &wset);
+
+ FD_SET(csock, &xset);
+ }
+
+ /* Thor.981221: for future reservation bug */
+ tv.tv_sec = LOAD_INTERVAL;
+ tv.tv_usec = 0;
+
+ nfds = select(nfds + 1, &rset, &wset, &xset, &tv);
+
+ if (nfds == 0)
+ continue;
+
+ if (nfds < 0)
+ {
+ csock = errno;
+ if (csock != EINTR)
+ {
+ logit("select", strerror(csock));
+ }
+ continue;
+ }
+
+ /* ------------------------------------------------- */
+ /* serve active agents */
+ /* ------------------------------------------------- */
+
+ uptime = time(0);
+
+ for (FBI = &Scully; agent = *FBI;)
+ {
+ csock = agent->sock;
+
+ if (FD_ISSET(csock, &wset))
+ {
+ state = agent_write(agent);
+ }
+ else if (FD_ISSET(csock, &rset))
+ {
+ state = agent_read(agent);
+ }
+ else if (FD_ISSET(csock, &xset))
+ {
+ state = 0;
+ }
+ else
+ {
+ state = -1;
+ }
+
+ if (state == 0) /* fire this agent */
+ {
+ shutdown(csock, 2);
+ close(csock);
+ *FBI = agent->next;
+
+ agent->next = Mulder;
+ Mulder = agent;
+ continue;
+ }
+
+ if (state > 0)
+ {
+ agent->uptime = uptime;
+ }
+
+ FBI = &(agent->next);
+ }
+
+ /* ------------------------------------------------- */
+ /* serve new connection */
+ /* ------------------------------------------------- */
+
+ if (FD_ISSET(0, &rset))
+ {
+ for (;;)
+ {
+ csock = accept(0, NULL, NULL);
+
+ if (csock > 0)
+ {
+ if (agent = Mulder)
+ {
+ Mulder = agent->next;
+ }
+ else
+ {
+ agent = (Agent *) malloc(sizeof(Agent));
+ }
+
+ *FBI = agent;
+
+ /* variable initialization */
+
+ agent->next = NULL;
+ agent->sock = csock;
+ agent->state = CS_READING;
+ agent->locus = 0;
+ agent->uptime = uptime;
+ agent->count = 0;
+ agent->uentp = NULL;
+
+ break;
+ }
+
+ state = errno;
+ if (state != EINTR)
+ {
+ logit("accept", strerror(state));
+ break;
+ }
+ while (waitpid(-1, NULL, WNOHANG | WUNTRACED) > 0);
+ }
+ }
+
+ /* ------------------------------------------------- */
+ /* tail of main loop */
+ /* ------------------------------------------------- */
+ }
+}
diff --git a/daemon/bhttpd.c b/daemon/bhttpd.c
new file mode 100644
index 0000000..a813004
--- /dev/null
+++ b/daemon/bhttpd.c
@@ -0,0 +1,4122 @@
+/*-------------------------------------------------------*/
+/* bhttpd.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : BBS's HTTP daemon */
+/* create : 05/07/11 */
+/* update : 05/08/04 */
+/* author : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/* author : yiting.bbs@bbscs.tku.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#if 0 /* ³sµ²¤@Äýªí */
+
+ http://my.domain/ ­º­¶
+ http://my.domain/brdlist ¬ÝªO¦Cªí
+ http://my.domain/fvrlist §Úªº³Ì·R
+ http://my.domain/usrlist ¨Ï¥ÎªÌ¦W³æ
+ http://my.domain/brd?brdname&## ¤å³¹¦Cªí¡A¦C¥X¬ÝªO [brdname] ½s¸¹ ## ¶}©lªº 50 ½g¤å³¹
+ http://my.domain/gem?brdname&folder ºëµØ°Ï¦Cªí¡A¦C¥X¬ÝªO [brdname] ºëµØ°Ï¤¤ folder ³o­Ó¨÷©v¤Uªº©Ò¦³ªF¦è
+ http://my.domain/mbox?## «H½c¦Cªí¡A¦C¥X«H½c¤¤½s¸¹ ## ¶}©lªº 50 ½g¤å³¹
+ http://my.domain/bmore?brdname&## ¾\Ū¬ÝªO¤å³¹¡A¾\Ū¬ÝªO [brdname] ªº²Ä ## ½g¤å³¹
+ http://my.domain/bmost?brdname&## ¾\Ū¬ÝªO¤å³¹¡A¾\Ū¬ÝªO [brdname] ¤¤©Ò¦³¦W²Ä ## ½g¦P¼ÐÃDªº¤å³¹
+ http://my.domain/gmore?brdname&folder&## ¾\ŪºëµØ°Ï¤å³¹¡A¾\Ū¬ÝªO [brdname] ºëµØ°Ï¤¤ folder ³o­Ó¨÷©v¤Uªº²Ä ## ½g¤å³¹
+ http://my.domain/mmore?## ¾\Ū«H½c¤å³¹¡A¾\Ū«H½c¤¤²Ä ## ½g¤å³¹
+ http://my.domain/dopost?brdname µoªí¤å³¹©ó¬ÝªO [brdname]
+ http://my.domain/domail?userid µo°e«H¥óµ¹ [userid]
+ http://my.domain/dpost?brdname&##&### ¸ß°Ý½T©w§R°£¬ÝªO [brdname] ¤¤²Ä ## ½g¤å³¹ (¨ä chrono ¬O ###)
+ http://my.domain/delpost?brdname&##&### §R°£¬ÝªO [brdname] ¤¤²Ä ## ½g¤å³¹ (¨ä chrono ¬O ###)
+ http://my.domain/mpost?brdname&##&### ¼Ð°O¬ÝªO [brdname] ¤¤²Ä ## ½g¤å³¹ (¨ä chrono ¬O ###)
+ http://my.domain/dmail?##&### ¸ß°Ý½T©w§R°£«H½c¤¤²Ä ## ½g¤å³¹ (¨ä chrono ¬O ###)
+ http://my.domain/delmail?##&### §R°£«H½c¤¤²Ä ## ½g¤å³¹ (¨ä chrono ¬O ###)
+ http://my.domain/mmail?##&### ¼Ð°O«H½c¤¤²Ä ## ½g¤å³¹ (¨ä chrono ¬O ###)
+ http://my.domain/query?userid ¬d¸ß userid
+ http://my.domain/img?filename Åã¥Ü¹ÏÀÉ
+ http://my.domain/rss?brdname ¦U¬ÝªOªºRSS Feed
+ http://my.domain/class?folder ¦C¥X¤ÀÃþ¤¤ [folder] ³o­Ó¨÷©v¤Uªº©Ò¦³¬ÝªO
+ http://my.domain/robot.txt Robot Exclusion
+
+#endif
+
+
+#define _MODES_C_
+
+#include "bbs.h"
+
+
+#include <sys/wait.h>
+#include <netinet/tcp.h>
+#include <sys/resource.h>
+
+#undef ROBOT_EXCLUSION /* robot exclusion */
+
+
+#define SERVER_USAGE
+#undef LOG_VERBOSE /* ¸Ô²Ó¬ö¿ý */
+
+
+#define BHTTP_PIDFILE "run/bhttp.pid"
+#define BHTTP_LOGFILE "run/bhttp.log"
+
+
+#define BHTTP_PERIOD (60 * 5) /* ¨C 5 ¤ÀÄÁ check ¤@¦¸ */
+#define BHTTP_TIMEOUT (60 * 3) /* ¶W¹L 3 ¤ÀÄÁªº³s½u´Nµø¬°¿ù»~ */
+#define BHTTP_FRESH 86400 /* ¨C 1 ¤Ñ¾ã²z¤@¦¸ log ÀÉ */
+
+
+#define TCP_BACKLOG 3
+#define TCP_RCVSIZ 2048
+
+
+#define MIN_DATA_SIZE (TCP_RCVSIZ + 3)
+#define MAX_DATA_SIZE 262143 /* POST ªº¤j¤p­­¨î(byte) */
+
+
+/* Thor.000425: POSIX ¥Î O_NONBLOCK */
+
+#ifndef O_NONBLOCK
+#define M_NONBLOCK FNDELAY
+#else
+#define M_NONBLOCK O_NONBLOCK
+#endif
+
+#define HTML_TALL 50 /* ¦Cªí¤@­¶ 50 ½g */
+
+
+/* ----------------------------------------------------- */
+/* ¿ï³æªºÃC¦â */
+/* ----------------------------------------------------- */
+
+#define HCOLOR_BG "#000000" /* ­I´ºªºÃC¦â */
+#define HCOLOR_TEXT "#ffffff" /* ¤å¦rªºÃC¦â */
+#define HCOLOR_LINK "#00ffff" /* ¥¼ÂsÄý¹L³sµ²ªºÃC¦â */
+#define HCOLOR_VLINK "#c0c0c0" /* ¤wÂsÄý¹L³sµ²ªºÃC¦â */
+#define HCOLOR_ALINK "#ff0000" /* ³sµ²³QÀ£¤U®ÉªºÃC¦â */
+
+#define HCOLOR_NECK "#000070" /* ²ä¤lªºÃC¦â */
+#define HCOLOR_TIE "#a000a0" /* »â±aªºÃC¦â */
+
+#define HCOLOR_BAR "#808080" /* ¥ú´ÎÃC¦â */
+
+
+/* ----------------------------------------------------- */
+/* HTTP commands */
+/* ----------------------------------------------------- */
+
+typedef struct
+{
+ int (*func) ();
+ char *cmd;
+ int len; /* strlen(Command.cmd) */
+} Command;
+
+
+/* ----------------------------------------------------- */
+/* client connection structure */
+/* ----------------------------------------------------- */
+
+#define LEN_COOKIE (IDLEN + PASSLEN + 3 + 1) /* userid&p=passwd */
+
+typedef struct Agent
+{
+ struct Agent *anext;
+ int sock;
+
+ unsigned int ip_addr;
+
+ time_t tbegin; /* ³s½u¶}©l®É¶¡ */
+ time_t uptime; /* ¤W¦¸¤U«ü¥Oªº®É¶¡ */
+
+ char url[48]; /* ±ýÂsÄýªººô­¶ */
+ char *urlp;
+
+ char cookie[32];
+ int setcookie;
+
+ char modified[30];
+
+ /* ¨Ï¥ÎªÌ¸ê®Æ­n¥ý acct_fetch() ¤~¯à¨Ï¥Î */
+ int userno;
+ char userid[IDLEN + 1];
+ char username[UNLEN + 1];
+ usint userlevel;
+
+ /* ©Ò¯à¬Ý¨ìªº¬ÝªO¦Cªí©Î¨Ï¥ÎªÌ¦W³æ */
+
+#if MAXBOARD > MAXACTIVE
+ void *myitem[MAXBOARD];
+#else
+ void *myitem[MAXACTIVE];
+#endif
+ int total_item;
+
+ /* input ¥Î */
+
+ char *data;
+ int size; /* ¥Ø«e data ©Ò malloc ªºªÅ¶¡¤j¤p */
+ int used;
+
+ /* output ¥Î */
+
+ FILE *fpw;
+} Agent;
+
+
+/* ----------------------------------------------------- */
+/* http state code */
+/* ----------------------------------------------------- */
+
+enum
+{
+ HS_END,
+
+ HS_ERROR, /* »yªk¿ù»~ */
+ HS_ERR_LOGIN, /* ©|¥¼µn¤J */
+ HS_ERR_USER, /* ±b¸¹Åª¨ú¿ù»~ */
+ HS_ERR_MORE, /* ¤å³¹Åª¨ú¿ù»~ */
+ HS_ERR_BOARD, /* ¬ÝªOŪ¨ú¿ù»~ */
+ HS_ERR_MAIL, /* «H¥óŪ¨ú¿ù»~ */
+ HS_ERR_CLASS, /* ¤ÀÃþŪ¨ú¿ù»~ */
+ HS_ERR_PERM, /* Åv­­¤£¨¬ */
+
+ HS_OK,
+
+ HS_REDIRECT, /* ­«·s¾É¦V */
+ HS_NOTMOIDIFY, /* ÀɮרS¦³Åܧó */
+ HS_BADREQUEST, /* ¿ù»~ªº­n¨D */
+ HS_FORBIDDEN, /* ¥¼±ÂÅvªº­¶­± */
+ HS_NOTFOUND, /* §ä¤£¨ìÀÉ®× */
+
+ LAST_HS
+};
+
+
+static char *http_msg[LAST_HS] =
+{
+ NULL,
+
+ "»yªk¿ù»~",
+ "±z©|¥¼µn¤J",
+ "¨S¦³³o­Ó±b¸¹",
+ "¾Þ§@¿ù»~¡G±z©Ò¿ï¨úªº¤å³¹¤£¦s¦b©Î¤w§R°£",
+ "¾Þ§@¿ù»~¡GµL¦¹¬ÝªO©Î±zªºÅv­­¤£¨¬",
+ "¾Þ§@¿ù»~¡GµL¦¹«H¥ó©Î±z©|¥¼µn¤J",
+ "¾Þ§@¿ù»~¡G±z©Ò¿ï¨úªº¤ÀÃþ¤£¦s¦b©Î¤w§R°£",
+ "¾Þ§@¿ù»~¡G±z©|¥¼µn¤J©ÎÅv­­¤£¨¬¡AµLªk¶i¦æ³o¶µ¾Þ§@",
+
+ "200 OK",
+
+ "302 Found",
+ "304 Not Modified",
+ "400 Bad Request",
+ "403 Forbidden",
+ "404 Not Found",
+};
+
+
+#define HS_REFRESH 0x0100 /* ¦Û°Ê¸õ­¶(¹w³]¬O3¬í) */
+
+
+/* ----------------------------------------------------- */
+/* AM : Agent Mode */
+/* ----------------------------------------------------- */
+
+#define AM_GET 0x010
+#define AM_POST 0x020
+
+
+/* ----------------------------------------------------- */
+/* operation log and debug information */
+/* ----------------------------------------------------- */
+/* @START | ... | time */
+/* ----------------------------------------------------- */
+
+static FILE *flog;
+
+extern int errno;
+extern char *crypt();
+
+static void
+log_fresh()
+{
+ int count;
+ char fsrc[64], fdst[64];
+ char *fpath = BHTTP_LOGFILE;
+
+ if (flog)
+ fclose(flog);
+
+ count = 9;
+ do
+ {
+ sprintf(fdst, "%s.%d", fpath, count);
+ sprintf(fsrc, "%s.%d", fpath, --count);
+ rename(fsrc, fdst);
+ } while (count);
+
+ rename(fpath, fsrc);
+ flog = fopen(fpath, "a");
+}
+
+
+static void
+logit(key, msg)
+ char *key;
+ char *msg;
+{
+ time_t now;
+ struct tm *p;
+
+ time(&now);
+ p = localtime(&now);
+ /* Thor.990329: y2k */
+ fprintf(flog, "%s\t%s\t%02d/%02d/%02d %02d:%02d:%02d\n",
+ key, msg, p->tm_year % 100, p->tm_mon + 1, p->tm_mday,
+ p->tm_hour, p->tm_min, p->tm_sec);
+}
+
+
+static void
+log_open()
+{
+ FILE *fp;
+
+ umask(077);
+
+ if (fp = fopen(BHTTP_PIDFILE, "w"))
+ {
+ fprintf(fp, "%d\n", getpid());
+ fclose(fp);
+ }
+
+ flog = fopen(BHTTP_LOGFILE, "a");
+ logit("START", "MTA daemon");
+}
+
+
+/* ----------------------------------------------------- */
+/* target : ANSI text to HTML tag */
+/* author : yiting.bbs@bbs.cs.tku.edu.tw */
+/* ----------------------------------------------------- */
+
+#define ANSI_TAG 27
+#define is_ansi(ch) ((ch >= '0' && ch <= '9') || ch == ';' || ch == '[')
+
+#define HAVE_HYPERLINK /* ³B²z¶W³sµ² */
+#undef HAVE_ANSIATTR /* «Ü¤Ö¥Î¨ì¦Ó¥BIE¤£¤ä´©°{Ã{¡A°®¯Ü¤£³B²z :( */
+#define HAVE_SAKURA /* Äåªá¤é¤å¦Û°ÊÂàUnicode */
+
+#ifdef HAVE_ANSIATTR
+#define ATTR_UNDER 0x1 /* ©³½u */
+#define ATTR_BLINK 0x2 /* °{°Ê */
+#define ATTR_ITALIC 0x4 /* ±×Åé */
+
+static int old_attr, now_attr;
+#endif
+
+static int old_color, now_color;
+static char ansi_buf[1024]; /* ANSILINELEN * 4 */
+
+
+#ifdef HAVE_HYPERLINK
+static uschar *linkEnd = NULL;
+
+static void
+ansi_hyperlink(fpw, src)
+ FILE *fpw;
+ uschar *src;
+{
+ int ch;
+
+ linkEnd = src;
+ fputs("<a class=PRE target=_blank href=", fpw);
+ while (ch = *linkEnd)
+ {
+ if (ch < '#' || ch == '<' || ch == '>' || ch > '~')
+ break;
+ fputc(ch, fpw);
+ linkEnd++;
+ }
+ fputc('>', fpw);
+}
+#endif
+
+
+#ifdef HAVE_SAKURA
+static int
+sakura2unicode(code)
+ int code;
+{
+ if (code > 0xC6DD && code < 0xC7F3)
+ {
+ if (code > 0xC7A0)
+ code -= 38665;
+ else if (code > 0xC700)
+ code -= 38631;
+ else if (code > 0xC6E6)
+ code -= 38566;
+ else if (code == 0xC6E3)
+ return 0x30FC;
+ else
+ code -= 38619;
+ if (code > 0x3093)
+ code += 13;
+ return code;
+ }
+ return 0;
+}
+#endif
+
+
+static int
+ansi_remove(psrc)
+ uschar **psrc;
+{
+ uschar *src = *psrc;
+ int ch = *src;
+
+ while (is_ansi(ch))
+ ch = *(++src);
+
+ if (ch && ch != '\n')
+ ch = *(++src);
+
+ *psrc = src;
+ return ch;
+}
+
+
+static int
+ansi_color(psrc)
+ uschar **psrc;
+{
+ uschar *src, *ptr;
+ int ch, value;
+ int color = old_color;
+#ifdef HAVE_ANSIATTR
+ int attr = old_attr;
+#endif
+ uschar *cptr = (uschar *) & color;
+
+ src = ptr = (*psrc) + 1;
+
+ ch = *src;
+ while (ch)
+ {
+ if (ch == ';' || ch == 'm')
+ {
+ *src = '\0';
+ value = atoi(ptr);
+ ptr = src + 1;
+ if (value == 0)
+ {
+ color = 0x00003740;
+
+#ifdef HAVE_ANSIATTR
+ attr = 0;
+#endif
+ }
+ else if (value >= 30 && value <= 37)
+ cptr[1] = value + 18;
+ else if (value >= 40 && value <= 47)
+ cptr[0] = value + 24;
+ else if (value == 1)
+ cptr[2] = 1;
+
+#ifdef HAVE_ANSIATTR
+ else if (value == 4)
+ attr |= ATTR_UNDER;
+ else if (value == 5)
+ attr |= ATTR_BLINK;
+ else if (value == 7) /* ¤Ï¥Õªº®ÄªG¥Î±×Åé¨Ó¥N´À */
+ attr |= ATTR_ITALIC;
+#endif
+
+ if (ch == 'm')
+ {
+ now_color = color;
+
+#ifdef HAVE_ANSIATTR
+ now_attr = attr;
+#endif
+
+ ch = *(++src);
+ break;
+ }
+ }
+ else if (ch < '0' || ch > '9')
+ {
+ ch = *(++src);
+ break;
+ }
+ ch = *(++src);
+ }
+
+ *psrc = src;
+ return ch;
+}
+
+
+static void
+ansi_tag(fpw)
+ FILE *fpw;
+{
+#ifdef HAVE_ANSIATTR
+ /* Äݩʤ£¦P¤~»Ý­n¦L¥X */
+ if (!(now_attr & ATTR_ITALIC) && (old_attr & ATTR_ITALIC))
+ {
+ fputs("</I>", fpw);
+ }
+ if (!(now_attr & ATTR_UNDER) && (old_attr & ATTR_UNDER))
+ {
+ fputs("</U>", fpw);
+ }
+ if (!(now_attr & ATTR_BLINK) && (old_attr & ATTR_BLINK))
+ {
+ fputs("</BLINK>", fpw);
+ }
+#endif
+
+ /* ÃC¦â¤£¦P¤~»Ý­n¦L¥X */
+ if (old_color != now_color)
+ {
+ fprintf(fpw, "</font><font class=A%05X>", now_color);
+ old_color = now_color;
+ }
+
+#ifdef HAVE_ANSIATTR
+ /* Äݩʤ£¦P¤~»Ý­n¦L¥X */
+ if (oldattr != attr)
+ {
+ if ((now_attr & ATTR_ITALIC) && !(old_attr & ATTR_ITALIC))
+ {
+ fputs("<I>", fpw);
+ }
+ if ((now_attr & ATTR_UNDER) && !(old_attr & ATTR_UNDER))
+ {
+ fputs("<U>", fpw);
+ }
+ if ((now_attr & ATTR_BLINK) && !(old_attr & ATTR_BLINK))
+ {
+ fputs("<BLINK>", fpw);
+ }
+ old_attr = now_attr;
+ }
+#endif
+}
+
+
+static void
+ansi_html(fpw, src)
+ FILE *fpw;
+ uschar *src;
+{
+ int ch1, ch2;
+ int has_ansi = 0;
+
+#ifdef HAVE_SAKURA
+ int scode;
+#endif
+
+ ch2 = *src;
+ while (ch2)
+ {
+ ch1 = ch2;
+ ch2 = *(++src);
+ if (IS_ZHC_HI(ch1))
+ {
+ while (ch2 == ANSI_TAG)
+ {
+ if (*(++src) == '[') /* ÃC¦â */
+ {
+ ch2 = ansi_color(&src);
+ has_ansi = 1;
+ }
+ else /* ¨ä¥Lª½±µ§R°£ */
+ ch2 = ansi_remove(&src);
+ }
+ if (ch2)
+ {
+ if (ch2 < ' ') /* ©È¥X²{\n */
+ fputc(ch2, fpw);
+#ifdef HAVE_SAKURA
+ else if (scode = sakura2unicode((ch1 << 8) | ch2))
+ fprintf(fpw, "&#%d;", scode);
+#endif
+ else
+ {
+ fputc(ch1, fpw);
+ fputc(ch2, fpw);
+ }
+ ch2 = *(++src);
+ }
+ if (has_ansi)
+ {
+ has_ansi = 0;
+ if (ch2 != ANSI_TAG)
+ ansi_tag(fpw);
+ }
+ continue;
+ }
+ else if (ch1 == ANSI_TAG)
+ {
+ do
+ {
+ if (ch2 == '[') /* ÃC¦â */
+ ch2 = ansi_color(&src);
+ else if (ch2 == '*') /* ±±¨î½X */
+ fputc('*', fpw);
+ else /* ¨ä¥Lª½±µ§R°£ */
+ ch2 = ansi_remove(&src);
+ } while (ch2 == ANSI_TAG && (ch2 = *(++src)));
+ ansi_tag(fpw);
+ continue;
+ }
+ /* ³Ñ¤Uªº¦r¤¸°µhtmlÂà´« */
+ if (ch1 == '<')
+ {
+ fputs("&lt;", fpw);
+ }
+ else if (ch1 == '>')
+ {
+ fputs("&gt;", fpw);
+ }
+ else if (ch1 == '&')
+ {
+ fputc(ch1, fpw);
+ if (ch2 == '#') /* Unicode¦r¤¸¤£Âà´« */
+ {
+ fputc(ch2, fpw);
+ ch2 = *(++src);
+ }
+ else if (ch2 >= 'A' && ch2 <= 'z')
+ {
+ fputs("amp;", fpw);
+ fputc(ch2, fpw);
+ ch2 = *(++src);
+ }
+ }
+#ifdef HAVE_HYPERLINK
+ else if (linkEnd) /* ³B²z¶W³sµ² */
+ {
+ fputc(ch1, fpw);
+ if (linkEnd <= src)
+ {
+ fputs("</a>", fpw);
+ linkEnd = NULL;
+ }
+ }
+#endif
+ else
+ {
+#ifdef HAVE_HYPERLINK
+ /* ¨ä¥Lªº¦Û¤v¥[§a :) */
+ if (!str_ncmp(src - 1, "http://", 7))
+ ansi_hyperlink(fpw, src - 1);
+ else if (!str_ncmp(src - 1, "telnet://", 9))
+ ansi_hyperlink(fpw, src - 1);
+#endif
+
+ fputc(ch1, fpw);
+ }
+ }
+}
+
+
+static char *
+str_html(src, len)
+ uschar *src;
+ int len;
+{
+ int in_chi, ch;
+ uschar *dst = ansi_buf, *end = src + len;
+
+ ch = *src;
+ while (ch && src < end)
+ {
+ if (IS_ZHC_HI(ch))
+ {
+ in_chi = *(++src);
+ while (in_chi == ANSI_TAG)
+ {
+ src++;
+ in_chi = ansi_remove(&src);
+ }
+
+ if (in_chi)
+ {
+ if (in_chi < ' ') /* ¥i¯à¥u¦³¥b­Ó¦r¡A«e¥b³¡´N¤£­n¤F */
+ *dst++ = in_chi;
+#ifdef HAVE_SAKURA
+ else if (len = sakura2unicode((ch << 8) + in_chi))
+ {
+ sprintf(dst, "&#%d;", len); /* 12291~12540 */
+ dst += 8;
+ }
+#endif
+ else
+ {
+ *dst++ = ch;
+ *dst++ = in_chi;
+ }
+ }
+ else
+ break;
+ }
+ else if (ch == ANSI_TAG)
+ {
+ src++;
+ ch = ansi_remove(&src);
+ continue;
+ }
+ else if (ch == '<')
+ {
+ strcpy(dst, "&lt;");
+ dst += 4;
+ }
+ else if (ch == '>')
+ {
+ strcpy(dst, "&gt;");
+ dst += 4;
+ }
+ else if (ch == '&')
+ {
+ ch = *(++src);
+ if (ch == '#')
+ {
+ if ((uschar *) strchr(src + 1, ';') >= end) /* ¥i¯à·|¤£¬O©Îªø«×¨S¶W¹L */
+ break;
+ *dst++ = '&';
+ *dst++ = '#';
+ }
+ else
+ {
+ strcpy(dst, "&amp;");
+ dst += 5;
+ continue;
+ }
+ }
+ else
+ *dst++ = ch;
+ ch = *(++src);
+ }
+
+ *dst = '\0';
+ return ansi_buf;
+}
+
+
+static int
+ansi_quote(fpw, src) /* ¦pªG¬O¤Þ¨¥¡A´N²¤¹L©Ò¦³ªº ANSI ½X */
+ FILE *fpw;
+ uschar *src;
+{
+ int ch1, ch2;
+
+ ch1 = src[0];
+ ch2 = src[1];
+ if (ch2 == ' ' && (ch1 == QUOTE_CHAR1 || ch1 == QUOTE_CHAR2)) /* ¤Þ¨¥ */
+ {
+ ch2 = src[2];
+ if (ch2 == QUOTE_CHAR1 || ch2 == QUOTE_CHAR2) /* ¤Þ¥Î¤@¼h/¤G¼h¤£¦PÃC¦â */
+ now_color = 0x00003340;
+ else
+ now_color = 0x00003640;
+ }
+ else if (ch1 == '\241' && ch2 == '\260') /* ¡° ¤Þ¨¥ªÌ */
+ {
+ now_color = 0x00013640;
+ }
+ else
+ {
+ ansi_tag(fpw);
+ return 0; /* ¤£¬O¤Þ¨¥ */
+ }
+
+ ansi_tag(fpw);
+ fputs(str_html(src, ANSILINELEN), fpw);
+ now_color = 0x00003740;
+ return 1;
+}
+
+
+static void
+txt2htm(fpw, fp)
+ FILE *fpw;
+ FILE *fp;
+{
+ static const char header1[LINE_HEADER][LEN_AUTHOR1] = {"§@ªÌ", "¼ÐÃD", "®É¶¡"};
+ static const char header2[LINE_HEADER][LEN_AUTHOR2] = {"µo«H¤H", "¼Ð ÃD", "µo«H¯¸"};
+ int i;
+ char *headvalue, *pbrd, *board;
+ char buf[ANSILINELEN];
+
+ fputs("<table width=760 cellspacing=0 cellpadding=0 border=0>\n", fpw);
+ /* ³B²zÀÉÀY */
+ for (i = 0; i < LINE_HEADER; i++)
+ {
+ if (!fgets(buf, ANSILINELEN, fp)) /* ÁöµM³sÀÉÀY³£ÁÙ¨S¦L§¹¡A¦ý¬OÀɮפw¸gµ²§ô¡Aª½±µÂ÷¶} */
+ {
+ fputs("</table>\n", fpw);
+ return;
+ }
+
+ if (memcmp(buf, header1[i], LEN_AUTHOR1 - 1) && memcmp(buf, header2[i], LEN_AUTHOR2 - 1)) /* ¤£¬OÀÉÀY */
+ break;
+
+ /* §@ªÌ/¬ÝªO ÀÉÀY¦³¤GÄæ¡A¯S§O³B²z */
+ if (i == 0 && ((pbrd = strstr(buf, "¬ÝªO:")) || (pbrd = strstr(buf, "¯¸¤º:"))))
+ {
+ if (board = strchr(pbrd, '\n'))
+ *board = '\0';
+ board = pbrd + 6;
+ pbrd[-1] = '\0';
+ pbrd[4] = '\0';
+ }
+
+ if (!(headvalue = strchr(buf, ':')))
+ break;
+
+ fprintf(fpw, "<tr>\n"
+ " <td align=center width=10%% class=A03447>%s</td>\n", header1[i]);
+
+ str_html(headvalue + 2, TTLEN);
+ if (i == 0 && pbrd)
+ {
+ fprintf(fpw, " <td width=60%% class=A03744>&nbsp;%s</td>\n"
+ " <td align=center width=10%% class=A03447>%s</td>\n"
+ " <td width=20%% class=A03744>&nbsp;%s</td>\n</tr>\n",
+ ansi_buf, pbrd, board);
+ }
+ else
+ {
+ fputs(" <td width=90% colspan=3 class=A03744>&nbsp;", fpw);
+ fputs(ansi_buf, fpw);
+ fputs("</td>\n</tr>\n", fpw);
+ }
+ }
+
+ fputs("<tr>\n"
+ "<td colspan=4><pre><font class=A03740>", fpw);
+
+ old_color = now_color = 0x00003740;
+
+#ifdef HAVE_ANSIATTR
+ old_attr = now_attr = 0;
+#endif
+
+ if (i >= LINE_HEADER) /* ³Ì«á¤@¦æ¬OÀÉÀY */
+ fgets(buf, ANSILINELEN, fp);
+
+ /* ³B²z¤º¤å */
+ do
+ {
+ if (!ansi_quote(fpw, buf))
+ ansi_html(fpw, buf);
+ } while (fgets(buf, ANSILINELEN, fp));
+
+ fputs("</font></pre></td>\n</table>\n", fpw);
+}
+
+
+/* ----------------------------------------------------- */
+/* HTML output basic function */
+/* ----------------------------------------------------- */
+
+static char *
+Gtime(now)
+ time_t *now;
+{
+ static char datemsg[32];
+
+ strftime(datemsg, sizeof(datemsg), "%a, %d %b %Y %T GMT", gmtime(now));
+ return datemsg;
+}
+
+
+static FILE *
+out_http(ap, code, type)
+ Agent *ap;
+ int code;
+ char *type;
+{
+ time_t now;
+ FILE *fpw;
+ int state;
+
+ fpw = ap->fpw;
+ state = code & ~HS_REFRESH;
+
+ /* HTTP 1.0 ÀÉÀY */
+ time(&now);
+
+ fprintf(fpw, "HTTP/1.0 %s\r\n"
+ "Date: %s\r\n"
+ "Server: MapleBBS 3.10\r\n"
+ "Connection: close\r\n", http_msg[state], Gtime(&now));
+
+ if (state == HS_NOTMOIDIFY)
+ {
+ fputs("\r\n", fpw);
+ }
+ else if (state == HS_REDIRECT)/* Location¤§«á¤£»Ý­n¤º®e */
+ {
+#if BHTTP_PORT == 80
+ fprintf(fpw, "Location: http://" MYHOSTNAME "/\r\n\r\n");
+#else
+ fprintf(fpw, "Location: http://" MYHOSTNAME ":%d/\r\n\r\n", BHTTP_PORT);
+#endif
+ }
+ else
+ {
+ if (code & HS_REFRESH)
+ {
+ if (!type)
+ type = "/";
+ fprintf(fpw, "Refresh: 3; url=%s\r\n", type);
+ }
+ if ((code & HS_REFRESH) || !type)
+ {
+ fputs("Pragma: no-cache\r\n" /* ºô­¶¤@«ß¤£Åýproxy°µcache */
+ "Content-Type: text/html; charset=" MYCHARSET "\r\n", fpw);
+ }
+ else
+ fprintf(fpw, "Content-Type: %s\r\n", type);
+
+ if (ap->setcookie) /* cmd_login() §¹¥H«á¤~»Ý­n Set-Cookie */
+ fprintf(fpw, "Set-Cookie: user=%s; path=/\r\n", ap->cookie);
+ }
+
+ return fpw;
+}
+
+
+static void
+out_error(ap, code) /* code¤£¥i¥H¬OHS_OK */
+ Agent *ap;
+ int code;
+{
+ char *msg;
+
+ if (code < HS_OK)
+ {
+ fprintf(ap->fpw, "<BR>%s<BR><BR>\n", http_msg[code]);
+ return;
+ }
+
+ out_http(ap, code, NULL);
+ switch (code)
+ {
+ case HS_BADREQUEST:
+ msg = "Your browser sent a request that this server could not understand.";
+ break;
+ case HS_FORBIDDEN:
+ msg = "You don't have permission to access the URL on this server.";
+ break;
+ case HS_NOTFOUND:
+ msg = "The requested URL was not found on this server.";
+ break;
+ default: /* HS_REDIRECT, HS_NOTMOIDIFY */
+ return;
+ }
+ /* html ÀÉ®×¶}©l */
+ fprintf(ap->fpw, "\r\n<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n"
+ "<HTML><HEAD>\n"
+ "<TITLE>%s</TITLE>\n"
+ "</HEAD><BODY>\n"
+ "<H1>%s</H1>\n%s\n<HR>\n"
+ "<ADDRESS>MapleBBS/3.10 Server at " MYHOSTNAME "</ADDRESS>\n"
+ "</BODY></HTML>\n", http_msg[code], http_msg[code] + 4, msg);
+}
+
+
+/* out_head() ¤¤ªº <HTML> <BODY> <CENTER> ¤T­Ó¤j¼g¼ÐÅÒ³e¬ï¾ã­Ó html ÀÉ
+ ª½¨ì out_tail() ¤~¥Ñ </HTML> </BODY> </CENTER> ÁÙ­ì */
+
+
+static void
+out_title(fpw, title)
+ FILE *fpw;
+ char *title;
+{
+ /* html ÀÉ®×¶}©l */
+ fprintf(fpw, "\r\n<HTML><HEAD>\n"
+ "<meta http-equiv=Content-Type content=\"text/html; charset=" MYCHARSET "\">\n"
+ "<title>-=" BBSNAME "=- %s</title>\n", title);
+
+ fputs("<script language=javascript>\n"
+ " function mOver(obj) {obj.bgColor='" HCOLOR_BAR "';}\n"
+ " function mOut(obj) {obj.bgColor='" HCOLOR_BG "';}\n"
+ "</script>\n<style type=text/css>\n"
+ " PRE {font-size: 15pt; line-height: 15pt; font-weight: lighter; background-color: #000000; color: #C0C0C0;}\n"
+ " TD {font-size: 15pt; line-height: 15pt; font-weight: lighter;}\n"
+ "</style>\n"
+ "<link rel=stylesheet href=/img?ansi.css type=text/css>\n"
+ "</head>\n"
+ "<BODY bgcolor=" HCOLOR_BG " text=" HCOLOR_TEXT " link=" HCOLOR_LINK " vlink=" HCOLOR_VLINK " alink=" HCOLOR_ALINK "><CENTER>\n"
+ "<a href=/><img src=/img?site.gif border=0></a><br>\n"
+ "<input type=image src=/img?back.gif onclick=\"javascript:history.go(-1);\"> / "
+ "<a href=/class><img src=/img?class.gif border=0></a> / "
+ "<a href=/brdlist><img src=/img?board.gif border=0></a> / "
+ "<a href=/fvrlist><img src=/img?favor.gif border=0></a> / "
+ "<a href=/mbox><img src=/img?mbox.gif border=0></a> / "
+ "<a href=/usrlist><img src=/img?user.gif border=0></a> / "
+ "<a href=telnet://" MYHOSTNAME "><img src=/img?telnet.gif border=0></a><br>\n", fpw);
+}
+
+
+static FILE *
+out_head(ap, title)
+ Agent *ap;
+ char *title;
+{
+ FILE *fpw = out_http(ap, HS_OK, NULL);
+ out_title(fpw, title);
+ return fpw;
+}
+
+
+static void
+out_mesg(fpw, msg)
+ FILE *fpw;
+ char *msg;
+{
+ fprintf(fpw, "<BR>%s<BR><BR>\n", msg);
+}
+
+
+static void
+out_style(fpw)
+ FILE *fpw;
+{
+#ifdef HAVE_HYPERLINK
+ fputs("<style type=text/css>\n"
+ " a:link.PRE {COLOR: #FFFFFF}\n"
+ " a:visited.PRE {COLOR: #FFFFFF}\n"
+ " a:active.PRE {COLOR: " HCOLOR_ALINK "; TEXT-DECORATION: none}\n"
+ " a:hover.PRE {COLOR: " HCOLOR_ALINK "; TEXT-DECORATION: none}\n"
+ "</style>\n", fpw);
+#endif
+}
+
+
+static void
+out_article(fpw, fpath)
+ FILE *fpw;
+ char *fpath;
+{
+ FILE *fp;
+
+ if (fp = fopen(fpath, "r"))
+ {
+ out_style(fpw);
+ txt2htm(fpw, fp);
+ fclose(fp);
+ }
+}
+
+
+static void
+out_tail(fpw)
+ FILE *fpw;
+{
+ fputs("</CENTER></BODY></HTML>\n", fpw);
+}
+
+
+static void
+out_reload(fpw, msg) /* God.050327: ±N¥Dµøµ¡ reload ¨ÃÃö±¼·s¶}µøµ¡ */
+ FILE *fpw;
+ char *msg;
+{
+ fprintf(fpw, "</CENTER></BODY></HTML>\n"
+ "<script>alert(\'%s\');\n"
+ "opener.location.reload();\n"
+ "parent.close();</script>", msg);
+}
+
+
+/* ----------------------------------------------------- */
+/* ¸Ñ½X¤ÀªR°Ñ¼Æ */
+/* ----------------------------------------------------- */
+
+#define hex2int(x) ((x >= 'A') ? (x - 'A' + 10) : (x - '0'))
+
+static int /* 1:¦¨¥\ */
+arg_analyze(argc, mark, str, arg1, arg2, arg3, arg4)
+ int argc; /* ¦³´X­Ó°Ñ¼Æ */
+ int mark; /* !=0: str ­n¬O mark ¶}ÀYªº¦r¦ê */
+ char *str; /* ¤Þ¼Æ */
+ char **arg1; /* °Ñ¼Æ¤@ */
+ char **arg2; /* °Ñ¼Æ¤G */
+ char **arg3; /* °Ñ¼Æ¤T */
+ char **arg4; /* °Ñ¼Æ¥| */
+{
+ int i, ch;
+ char *dst;
+
+ if ((mark && *str++ != mark) || !(ch = *str))
+ {
+ *arg1 = NULL;
+ return 0;
+ }
+
+ *arg1 = dst = str;
+ i = 2;
+
+ while (ch)
+ {
+ if (ch == '&' || ch == '\r')
+ {
+ if (i > argc)
+ break;
+
+ *dst++ = '\0';
+ if (i == 2)
+ *arg2 = dst;
+ else if (i == 3)
+ *arg3 = dst;
+ else /* if (i == 4) */
+ *arg4 = dst;
+ i++;
+ }
+ else if (ch == '+')
+ {
+ *dst++ = ' ';
+ }
+ else if (ch == '%')
+ {
+ ch = *(++str);
+ if (isxdigit(ch) && isxdigit(str[1]))
+ {
+ ch = (hex2int(ch) << 4) + hex2int(str[1]);
+ str++;
+ if (ch != '\r') /* '\r' ´N¤£­n¤F */
+ *dst++ = ch;
+ }
+ else
+ {
+ *dst++ = '%';
+ continue;
+ }
+ }
+ else
+ *dst++ = ch;
+
+ ch = *(++str);
+ }
+ *dst = '\0';
+
+ return i > argc;
+}
+
+
+/* ----------------------------------------------------- */
+/* ¥Ñ Cookie ¬Ý¨Ï¥ÎªÌ¬O§_µn¤J */
+/* ----------------------------------------------------- */
+
+static int guestuno = 0;
+
+static void
+guest_userno()
+{
+ char fpath[64];
+ ACCT acct;
+
+ usr_fpath(fpath, STR_GUEST, FN_ACCT);
+ if (!rec_get(fpath, &acct, sizeof(ACCT), 0))
+ guestuno = acct.userno;
+}
+
+static int /* 1:µn¤J¦¨¥\ 0:µn¤J¥¢±Ñ */
+acct_fetch(ap)
+ Agent *ap;
+{
+ char *userid, *passwd;
+ char fpath[64];
+ ACCT acct;
+
+ if (ap->cookie[0])
+ {
+ /* u=userid&p=passwd */
+ if (!arg_analyze(2, 0, ap->cookie, &userid, &passwd, NULL, NULL))
+ return 0;
+
+ passwd += 2; /* skip "p=" */
+
+ if (*userid && *passwd && strlen(userid) <= IDLEN && strlen(passwd) == PASSLEN)
+ {
+ usr_fpath(fpath, userid, FN_ACCT);
+ if (!rec_get(fpath, &acct, sizeof(ACCT), 0) &&
+ !(acct.userlevel & (PERM_DENYLOGIN | PERM_PURGE)) &&
+ !strncmp(acct.passwd, passwd, PASSLEN)) /* µn¤J¦¨¥\ */
+ {
+ ap->userno = acct.userno;
+ strcpy(ap->userid, acct.userid);
+ strcpy(ap->username, acct.username);
+ ap->userlevel = acct.userlevel;
+ return 1;
+ }
+ }
+ }
+
+ /* ¨S¦³µn¤J¡Bµn¤J¥¢±Ñ */
+ ap->userno = guestuno; /* ¶ñ¤J¹ê»Úguestªºuserno¡A¥H«K°µpalÀˬd */
+ ap->userlevel = 0;
+ strcpy(ap->userid, STR_GUEST);
+ strcpy(ap->username, STR_GUEST);
+ return 0;
+}
+
+
+/* ----------------------------------------------------- */
+/* UTMP shm ³¡¤À¶·»P cache.c ¬Û®e */
+/* ----------------------------------------------------- */
+
+static UCACHE *ushm;
+
+static void
+init_ushm()
+{
+ ushm = shm_new(UTMPSHM_KEY, sizeof(UCACHE));
+}
+
+
+static int /* 1: userno ¦b pool ¦W³æ¤W */
+pertain_pal(pool, max, userno) /* °Ñ¦Ò pal.c:belong_pal() */
+ int *pool;
+ int max;
+ int userno;
+{
+ int datum, mid;
+
+ while (max > 0)
+ {
+ datum = pool[mid = max >> 1];
+ if (userno == datum)
+ {
+ return 1;
+ }
+ if (userno > datum)
+ {
+ pool += (++mid);
+ max -= mid;
+ }
+ else
+ {
+ max = mid;
+ }
+ }
+ return 0;
+}
+
+
+static int /* 1: ¹ï¤è³]§Ú¬°Ãa¤H */
+is_hisbad(up, userno) /* °Ñ¦Ò pal.c:is_obad() */
+ UTMP *up;
+ int userno;
+{
+#ifdef HAVE_BADPAL
+ return pertain_pal(up->pal_spool, up->pal_max, -userno);
+#else
+ return 0;
+#endif
+}
+
+
+static int /* 1:¥i¬Ý¨£ 0:¤£¥i¬Ý¨£ */
+can_seen(up, userno, ulevel) /* °Ñ¦Ò bmw.c:can_see() */
+ UTMP *up;
+ int userno;
+ usint ulevel;
+{
+ usint urufo;
+
+ urufo = up->ufo;
+
+ if (!(ulevel & PERM_SEECLOAK) && ((urufo & UFO_CLOAK) || is_hisbad(up, userno)))
+ return 0;
+
+#ifdef HAVE_SUPERCLOAK
+ if (urufo & UFO_SUPERCLOAK)
+ return 0;
+#endif
+
+ return 1;
+}
+
+
+/* itoc.030711: ¥[¤WÀˬd¨Ï¥ÎªÌ±b¸¹ªº³¡¤À¡A¥H§K¦³¤H¶Ã¿å */
+static int
+allow_userid(ap, userid)
+ Agent *ap;
+ char *userid;
+{
+ int ch, rc;
+ char *str;
+
+ rc = 0;
+ ch = strlen(userid);
+ if (ch >= 2 && ch <= IDLEN && is_alpha(*userid))
+ {
+ rc = 1;
+ str = userid;
+ while (ch = *(++str))
+ {
+ if (!is_alnum(ch))
+ {
+ rc = 0;
+ break;
+ }
+ }
+ }
+
+ return rc;
+}
+
+
+/* ----------------------------------------------------- */
+/* board¡Gshm ³¡¥÷¶·»P cache.c ¬Û®e */
+/* ----------------------------------------------------- */
+
+static BCACHE *bshm;
+
+static void
+init_bshm()
+{
+ /* itoc.030727: ¦b¶}±Ò bbsd ¤§«e¡AÀ³¸Ó´N­n°õ¦æ¹L account¡A
+ ©Ò¥H bshm À³¸Ó¤w³]©w¦n */
+
+ if (bshm)
+ return;
+
+ bshm = shm_new(BRDSHM_KEY, sizeof(BCACHE));
+
+ if (bshm->uptime <= 0) /* bshm ¥¼³]©w§¹¦¨ */
+ exit(0);
+}
+
+
+#ifdef HAVE_MODERATED_BOARD
+static int /* !=0:¬OªO¦n 0:¤£¦b¦W³æ¤¤ */
+is_brdgood(userno, bpal) /* °Ñ¦Ò pal.c:is_bgood() */
+ int userno;
+ BPAL *bpal;
+{
+ return pertain_pal(bpal->pal_spool, bpal->pal_max, userno);
+}
+
+
+static int /* !=0:¬OªOÃa 0:¤£¦b¦W³æ¤¤ */
+is_brdbad(userno, bpal) /* °Ñ¦Ò pal.c:is_bbad() */
+ int userno;
+ BPAL *bpal;
+{
+#ifdef HAVE_BADPAL
+ return pertain_pal(bpal->pal_spool, bpal->pal_max, -userno);
+#else
+ return 0;
+#endif
+}
+#endif
+
+
+static int
+Ben_Perm(brd, uno, uid, ulevel) /* °Ñ¦Ò board.c:Ben_Perm() */
+ BRD *brd;
+ int uno;
+ char *uid;
+ usint ulevel;
+{
+ usint readlevel, postlevel, bits;
+ char *blist, *bname;
+
+#ifdef HAVE_MODERATED_BOARD
+ BPAL *bpal;
+ int ftype; /* 0:¤@¯ëID 1:ªO¦n 2:ªOÃa */
+
+ /* itoc.040103: ¬ÝªO¾\Ūµ¥¯Å»¡©úªí
+
+ ¢z¢w¢w¢w¢w¢s¢w¢w¢w¢w¢s¢w¢w¢w¢w¢s¢w¢w¢w¢w¢{
+ ¢x ¢x¤@¯ë¥Î¤á¢x¬ÝªO¦n¤Í¢x¬ÝªOÃa¤H¢x
+ ¢u¢w¢w¢w¢w¢q¢w¢w¢w¢w¢q¢w¢w¢w¢w¢q¢w¢w¢w¢w¢t
+ ¢x¤@¯ë¬ÝªO¢xÅv­­¨M©w¢x ¤ô±í ¢x ¬Ý¤£¨£ ¢x ¬Ý¤£¨£¡G¦b¬ÝªO¦Cªí¤¤µLªk¬Ý¨ì³o­ÓªO¡A¤]¶i¤£¥h
+ ¢u¢w¢w¢w¢w¢q¢w¢w¢w¢w¢q¢w¢w¢w¢w¢q¢w¢w¢w¢w¢t ¶i¤£¥h¡G¦b¬ÝªO¦Cªí¤¤¥i¥H¬Ý¨ì³o­ÓªO¡A¦ý¬O¶i¤£¥h
+ ¢x¦n¤Í¬ÝªO¢x ¶i¤£¥h ¢x §¹¾ã ¢x ¤ô±í ¢x ¤ô ±í¡G¦b¬ÝªO¦Cªí¤¤¥i¥H¬Ý¨ì³o­ÓªO¡A¤]¶i±o¥h¡A¦ý¬O¤£¯àµo¤å
+ ¢u¢w¢w¢w¢w¢q¢w¢w¢w¢w¢q¢w¢w¢w¢w¢q¢w¢w¢w¢w¢t §¹ ¾ã¡G¦b¬ÝªO¦Cªí¤¤¥i¥H¬Ý¨ì³o­ÓªO¡A¤]¶i±o¥h¤Îµo¤å
+ ¢x¯µ±K¬ÝªO¢x ¬Ý¤£¨£ ¢x §¹¾ã ¢x ¤ô±í ¢x
+ ¢|¢w¢w¢w¢w¢r¢w¢w¢w¢w¢r¢w¢w¢w¢w¢r¢w¢w¢w¢w¢}
+ */
+
+ static int bit_data[9] =
+ { /* ¤@¯ë¥Î¤á ¬ÝªO¦n¤Í ¬ÝªOÃa¤H */
+ /* ¤½¶}¬ÝªO */ 0, BRD_L_BIT | BRD_R_BIT, 0,
+ /* ¦n¤Í¬ÝªO */ BRD_L_BIT, BRD_L_BIT | BRD_R_BIT | BRD_W_BIT, BRD_L_BIT | BRD_R_BIT,
+ /* ¯µ±K¬ÝªO */ 0, BRD_L_BIT | BRD_R_BIT | BRD_W_BIT, BRD_L_BIT | BRD_R_BIT,
+ };
+#endif
+
+ if (!brd)
+ return 0;
+ bname = brd->brdname;
+ if (!*bname)
+ return 0;
+
+ readlevel = brd->readlevel;
+
+#ifdef HAVE_MODERATED_BOARD
+ bpal = bshm->pcache + (brd - bshm->bcache);
+ ftype = is_brdgood(uno, bpal) ? 1 : is_brdbad(uno, bpal) ? 2 : 0;
+
+ if (readlevel == PERM_SYSOP) /* ¯µ±K¬ÝªO */
+ bits = bit_data[6 + ftype];
+ else if (readlevel == PERM_BOARD) /* ¦n¤Í¬ÝªO */
+ bits = bit_data[3 + ftype];
+ else if (ftype) /* ¤½¶}¬ÝªO¡A­Y¦bªO¦n/ªOÃa¦W³æ¤¤ */
+ bits = bit_data[ftype];
+ else /* ¤½¶}¬ÝªO¡A¨ä¥L¨ÌÅv­­§P©w */
+#endif
+
+ if (!readlevel || (readlevel & ulevel))
+ {
+ bits = BRD_L_BIT | BRD_R_BIT;
+
+ postlevel = brd->postlevel;
+ if (!postlevel || (postlevel & ulevel))
+ bits |= BRD_W_BIT;
+ }
+ else
+ {
+ bits = 0;
+ }
+
+ /* Thor.980813.µù¸Ñ: ¯S§O¬° BM ¦Ò¶q¡AªO¥D¦³¸ÓªOªº©Ò¦³Åv­­ */
+ blist = brd->BM;
+ if ((ulevel & PERM_BM) && blist[0] > ' ' && str_has(blist, uid, strlen(uid)))
+ bits = BRD_L_BIT | BRD_R_BIT | BRD_W_BIT | BRD_X_BIT | BRD_M_BIT;
+
+ /* itoc.030515: ¬ÝªOÁ`ºÞ­«·s§PÂ_ */
+ else if (ulevel & PERM_ALLBOARD)
+ bits = BRD_L_BIT | BRD_R_BIT | BRD_W_BIT | BRD_X_BIT;
+
+ return bits;
+}
+
+
+static BRD *
+brd_get(bname)
+ char *bname;
+{
+ BRD *bhdr, *tail;
+
+ bhdr = bshm->bcache;
+ tail = bhdr + bshm->number;
+ do
+ {
+ if (!strcmp(bname, bhdr->brdname))
+ return bhdr;
+ } while (++bhdr < tail);
+ return NULL;
+}
+
+
+static int /* ¦^¶ÇPermBits¤è«K°µ¦h¦¸§PÂ_ */
+ben_perm(ap, brdname)
+ Agent *ap;
+ char *brdname;
+{
+ BRD *brd;
+
+ if (acct_fetch(ap) && (brd = brd_get(brdname)))
+ return Ben_Perm(brd, ap->userno, ap->userid, ap->userlevel);
+ return 0;
+}
+
+
+static BRD *
+allow_brdname(ap, brdname)
+ Agent *ap;
+ char *brdname;
+{
+ BRD *bhdr;
+
+ if (bhdr = brd_get(brdname))
+ {
+ /* ­Y readlevel == 0¡Aªí¥Ü guest ¥iŪ¡AµL»Ý acct_fetch() */
+ if (!bhdr->readlevel)
+ return bhdr;
+
+ if (acct_fetch(ap) && (Ben_Perm(bhdr, ap->userno, ap->userid, ap->userlevel) & BRD_R_BIT))
+ return bhdr;
+ }
+ return NULL;
+}
+
+
+/* ----------------------------------------------------- */
+/* movie¡Gshm ³¡¥÷¶·»P cache.c ¬Û®e */
+/* ----------------------------------------------------- */
+
+static FCACHE *fshm;
+
+
+static void
+init_fshm()
+{
+ fshm = shm_new(FILMSHM_KEY, sizeof(FCACHE));
+}
+
+
+static void
+out_film(fpw, tag)
+ FILE *fpw;
+ int tag;
+{
+ int i, *shot, len;
+ char *film, buf[FILM_SIZ];
+
+ shot = fshm->shot;
+ for (i = 0; !(*shot) && i < 5; ++i)
+ sleep(1);
+
+ if (!(*shot))
+ return; /* ­Y 5 ¬í¥H«áÁÙ¨S´«¦n¤ù¡A¥i¯à¬O¨S¶] camera¡Aª½±µÂ÷¶} */
+
+ film = fshm->film;
+ if (tag)
+ {
+ len = shot[tag];
+ film += len;
+ len = shot[tag + 1] - len;
+ }
+ else
+ len = shot[1];
+
+ memcpy(buf, film, len);
+ buf[len] = '\0';
+
+ out_style(fpw);
+ fputs("<table width=760 cellspacing=0 cellpadding=0 border=0>\n"
+ "<tr>\n"
+ "<td colspan=4><pre><font class=A03740>", fpw);
+ ansi_html(fpw, buf);
+ fputs("</font></pre></td>\n</table>\n", fpw);
+}
+
+
+/* ----------------------------------------------------- */
+/* command dispatch (GET) */
+/* ----------------------------------------------------- */
+
+ /* --------------------------------------------------- */
+ /* ³q¥Î²M³æ */
+ /* --------------------------------------------------- */
+
+static void
+list_neck(fpw, start, total, title)
+ FILE *fpw;
+ int start, total;
+ char *title;
+{
+ fputs("<br>\n"
+ "<table cellspacing=0 cellpadding=1 border=0 width=760>\n"
+ "<tr bgcolor=" HCOLOR_NECK ">\n <td width=15%", fpw);
+ if (start != 1)
+ {
+ fprintf(fpw, " align=center><a href=?%d>¤W%d­Ó</a",
+ (start > HTML_TALL ? start - HTML_TALL : 1), HTML_TALL);
+ }
+ fputs("></td>\n <td width=15%", fpw);
+
+ start += HTML_TALL;
+ if (start <= total)
+ {
+ fprintf(fpw, " align=center><a href=?%d>¤U%d­Ó</a",
+ start, HTML_TALL);
+ }
+ fputs("></td>\n <td width=40% align=center>", fpw);
+ fprintf(fpw, title, total);
+ fprintf(fpw, "</td>\n"
+ " <td width=15%% align=center><a href=?1>«e%d­Ó</a></td>\n"
+ " <td width=15%% align=center><a href=?0>¥½%d­Ó</a></td>\n"
+ "</tr></table><br>\n", HTML_TALL, HTML_TALL);
+}
+
+
+static void
+cmdlist_list(ap, title, list_tie, list_item)
+ Agent *ap;
+ char *title;
+ void (*list_tie) (FILE *);
+ void (*list_item) (FILE *, void *, int);
+{
+ int i, start, end, total;
+ char *number;
+ FILE *fpw;
+
+ if (!arg_analyze(1, '?', ap->urlp, &number, NULL, NULL, NULL))
+ start = 1;
+ else
+ start = atoi(number);
+ total = ap->total_item;
+ if (start <= 0 || start > total) /* ¶W¹L½d³òªº¸Ü¡Aª½±µ¨ì³Ì«á¤@­¶ */
+ start = total > HTML_TALL ? total - HTML_TALL + 1 : 1;
+
+ fpw = ap->fpw;
+ list_neck(fpw, start, total, title);
+ fputs("<table cellspacing=0 cellpadding=4 border=0>\n<tr bgcolor=" HCOLOR_TIE ">\n", fpw);
+ list_tie(fpw);
+
+ end = start + HTML_TALL - 1;
+ if (end > total)
+ end = total;
+ for (i = start - 1; i < end; i++)
+ {
+ fputs("<tr onmouseover=mOver(this); onmouseout=mOut(this);>\n", fpw);
+ list_item(fpw, ap->myitem[i], i + 1);
+ }
+
+ fputs("</table>\n", fpw);
+ list_neck(fpw, start, total, title);
+}
+
+
+ /* --------------------------------------------------- */
+ /* ¨Ï¥ÎªÌ¦W³æ */
+ /* --------------------------------------------------- */
+
+static int
+userid_cmp(a, b)
+ UTMP **a, **b;
+{
+ return str_cmp((*a)->userid, (*b)->userid);
+}
+
+
+static void
+init_myusr(ap)
+ Agent *ap;
+{
+ int num, userno;
+ UTMP *uentp, *uceil;
+ usint ulevel;
+
+ acct_fetch(ap);
+ uentp = ushm->uslot;
+ uceil = (void *)uentp + ushm->offset;
+ userno = ap->userno;
+ ulevel = ap->userlevel;
+ num = 0;
+
+ do
+ {
+ if (!uentp->pid || !uentp->userno || !can_seen(uentp, userno, ulevel))
+ continue;
+
+ ap->myitem[num] = uentp;
+ num++;
+ } while (++uentp <= uceil);
+
+ if (num > 1)
+ qsort(ap->myitem, num, sizeof(UTMP *), userid_cmp);
+
+ ap->total_item = num;
+}
+
+
+static void
+userlist_tie(fpw)
+ FILE *fpw;
+{
+ fputs(" <td width=40>½s¸¹</td>\n"
+ " <td width=100>ºô¤Í¥N¸¹</td>\n"
+ " <td width=210>ºô¤Í¼ÊºÙ</td>\n"
+ " <td width=230>«È³~¬G¶m</td>\n"
+ " <td width=100>ºô¤Í°ÊºA</td>\n"
+ "</tr>\n", fpw);
+}
+
+
+static void
+userlist_item(fpw, up, n)
+ FILE *fpw;
+ UTMP *up;
+ int n;
+{
+ fprintf(fpw, " <td>%d</td>\n"
+ " <td><a href=/query?%s>%s</a></td>\n"
+ " <td>%s</td>\n"
+ " <td>%s</td>\n"
+ " <td>%s</td>\n"
+ "</tr>\n",
+ n,
+ up->userid, up->userid,
+ str_html(up->username, UNLEN),
+ up->from,
+ ModeTypeTable[up->mode]);
+}
+
+
+static int
+cmd_userlist(ap)
+ Agent *ap;
+{
+ init_myusr(ap);
+ out_head(ap, "¨Ï¥ÎªÌ¦W³æ");
+
+ cmdlist_list(ap, "¥Ø«e¯¸¤W¦³ %d ­Ó¤H", userlist_tie, userlist_item);
+
+ return HS_END;
+}
+
+
+ /* --------------------------------------------------- */
+ /* ¬ÝªO¦Cªí */
+ /* --------------------------------------------------- */
+
+static int
+brdtitle_cmp(a, b)
+ BRD **a, **b;
+{
+ /* itoc.010413: ¤ÀÃþ/ªO¦W¥æ¤e¤ñ¹ï */
+ int k = strcmp((*a)->class, (*b)->class);
+ return k ? k : str_cmp((*a)->brdname, (*b)->brdname);
+}
+
+
+static void
+init_mybrd(ap)
+ Agent *ap;
+{
+ int num, uno;
+ char *uid;
+ usint ulevel;
+ BRD *bhdr, *tail;
+
+ acct_fetch(ap);
+ uno = ap->userno;
+ uid = ap->userid;
+ ulevel = ap->userlevel;
+ bhdr = bshm->bcache;
+ tail = bhdr + bshm->number;
+ num = 0;
+
+ do
+ {
+ if (Ben_Perm(bhdr, uno, uid, ulevel) & BRD_R_BIT)
+ {
+ ap->myitem[num] = bhdr;
+ num++;
+ }
+ } while (++bhdr < tail);
+
+ if (num > 1)
+ qsort(ap->myitem, num, sizeof(BRD *), brdtitle_cmp);
+
+ ap->total_item = num;
+}
+
+
+static void
+boardlist_tie(fpw)
+ FILE *fpw;
+{
+ fputs(" <td width=40>½s¸¹</td>\n"
+ " <td width=80>¬ÝªO</td>\n"
+ " <td width=40>Ãþ§O</td>\n"
+ " <td width=25>Âà</td>\n"
+ " <td width=350>¤¤¤å±Ô­z</td>\n"
+ " <td width=75>ªO¥D</td>\n"
+ "</tr>\n", fpw);
+}
+
+
+static void
+boardlist_item(fpw, brd, n)
+ FILE *fpw;
+ BRD *brd;
+ int n;
+{
+ fprintf(fpw, " <td>%d</td>\n"
+ " <td><a href=/brd?%s>%s</a></td>\n"
+ " <td>%s</td>\n"
+ " <td>%s</td>\n"
+ " <td>%s</td>\n"
+ " <td>%.13s</td>\n"
+ "</tr>\n",
+ n,
+ brd->brdname, brd->brdname,
+ brd->class,
+ (brd->battr & BRD_NOTRAN) ? ICON_NOTRAN_BRD : ICON_TRAN_BRD,
+ str_html(brd->title, 33),
+ brd->BM);
+}
+
+
+static int
+cmd_boardlist(ap)
+ Agent *ap;
+{
+ init_mybrd(ap);
+ out_head(ap, "¬ÝªO¦Cªí");
+
+ cmdlist_list(ap, "¥Ø«e¯¸¤W¦³ %d ­ÓªO", boardlist_tie, boardlist_item);
+
+ return HS_END;
+}
+
+
+ /* --------------------------------------------------- */
+ /* §Úªº³Ì·R */
+ /* --------------------------------------------------- */
+
+static void
+init_myfavor(ap)
+ Agent *ap;
+{
+ int num, uno;
+ usint ulevel;
+ char *uid, fpath[64];
+ BRD *bhdr;
+ FILE *fp;
+ MF mf;
+
+ uno = ap->userno;
+ uid = ap->userid;
+ ulevel = ap->userlevel;
+ num = 0;
+ usr_fpath(fpath, ap->userid, "MF/" FN_MF);
+
+ if (fp = fopen(fpath, "r"))
+ {
+ while (fread(&mf, sizeof(MF), 1, fp) == 1)
+ {
+ /* ¥u¤ä´©²Ä¤@¼hªº¬ÝªO */
+ if ((mf.mftype & MF_BOARD) &&
+ (bhdr = brd_get(mf.xname)) &&
+ (Ben_Perm(bhdr, uno, uid, ulevel) & BRD_R_BIT))
+ {
+ ap->myitem[num] = bhdr;
+ num++;
+ }
+ }
+ fclose(fp);
+ }
+ ap->total_item = num;
+}
+
+
+static int
+cmd_favorlist(ap)
+ Agent *ap;
+{
+ out_head(ap, "§Úªº³Ì·R");
+ if (!acct_fetch(ap))
+ return HS_ERR_LOGIN;
+
+ init_myfavor(ap);
+
+ cmdlist_list(ap, "§Úªº³Ì·R", boardlist_tie, boardlist_item);
+
+ return HS_END;
+}
+
+
+ /* --------------------------------------------------- */
+ /* ¤ÀÃþ¬ÝªO¦Cªí */
+ /* --------------------------------------------------- */
+
+static void
+class_neck(fpw)
+ FILE *fpw;
+{
+ fputs("<br>\n"
+ "<table cellspacing=0 cellpadding=1 border=0 width=760>\n"
+ "<tr bgcolor=" HCOLOR_NECK ">\n"
+ " <td width=50%></td>\n"
+ " <td width=50% align=center><a href=/class>¦^³Ì¤W¼h</a></td>\n"
+ "</tr></table><br>\n", fpw);
+}
+
+
+static int
+cmd_class(ap)
+ Agent *ap;
+{
+ int fd, i, userno;
+ usint ulevel;
+ char folder[64], *xname, *userid;
+ BRD *brd;
+ HDR hdr;
+ FILE *fpw = out_head(ap, "¤ÀÃþ¬ÝªO");
+
+ if (!arg_analyze(1, '?', ap->urlp, &xname, NULL, NULL, NULL))
+ xname = CLASS_INIFILE;
+
+ if (strlen(xname) > 12)
+ return HS_ERROR;
+
+ sprintf(folder, "gem/@/@%s", xname);
+ if ((fd = open(folder, O_RDONLY)) < 0)
+ return HS_ERR_CLASS;
+
+ acct_fetch(ap);
+ userno = ap->userno;
+ userid = ap->userid;
+ ulevel = ap->userlevel;
+
+ class_neck(fpw);
+ fputs("<table cellspacing=0 cellpadding=4 border=0>\n<tr bgcolor=" HCOLOR_TIE ">\n", fpw);
+ boardlist_tie(fpw);
+ i = 1;
+ while (read(fd, &hdr, sizeof(HDR)) == sizeof(HDR))
+ {
+ fputs("<tr onmouseover=mOver(this); onmouseout=mOut(this);>\n", fpw);
+ if (hdr.xmode & GEM_BOARD) /* ¬ÝªO */
+ {
+ if ((brd = brd_get(hdr.xname)) &&
+ Ben_Perm(brd, userno, userid, ulevel) & BRD_R_BIT)
+ {
+ boardlist_item(fpw, brd, i);
+ }
+ else
+ continue;
+ }
+ else if ((hdr.xmode & GEM_FOLDER) && *hdr.xname == '@') /* ¤ÀÃþ */
+ {
+ fprintf(fpw, " <td>%d</td>\n"
+ " <td><a href=/class?%s>%s/</a></td>\n"
+ " <td>¤ÀÃþ</td>\n"
+ " <td>¡¼</td>\n"
+ " <td colspan=2>%s</td>\n</tr>\n",
+ i,
+ hdr.xname + 1, hdr.xname + 1,
+ str_html(hdr.title + 21, 52));
+ }
+ else /* ¨ä¥LÃþ§O´N¤£¨q¤F */
+ {
+ continue;
+ }
+
+ i++;
+ }
+ close(fd);
+ fputs("</table>\n", fpw);
+ class_neck(fpw);
+ return HS_END;
+}
+
+
+ /* --------------------------------------------------- */
+ /* ¤å³¹¦Cªí */
+ /* --------------------------------------------------- */
+
+static void
+postlist_list(fpw, folder, brdname, start, total)
+ FILE *fpw;
+ char *folder, *brdname;
+ int start, total;
+{
+ HDR hdr;
+ char owner[80], *ptr1, *ptr2;
+ int fd;
+
+ fputs("<table cellspacing=0 cellpadding=4 border=0>\n<tr bgcolor=" HCOLOR_TIE ">\n"
+ " <td width=15>¼Ð</td>\n"
+ " <td width=15>§R</td>\n"
+ " <td width=50>½s¸¹</td>\n"
+ " <td width=10>m</td>\n"
+
+#ifdef HAVE_SCORE
+ " <td width=10>&nbsp;</td>\n"
+#endif
+
+ " <td width=50>¤é´Á</td>\n"
+ " <td width=100>§@ªÌ</td>\n"
+ " <td width=400>¼ÐÃD</td>\n"
+ "</tr>\n", fpw);
+
+ if ((fd = open(folder, O_RDONLY)) >= 0)
+ {
+ int i, end;
+
+ /* ¨q¥X¬ÝªOªº²Ä start ½g¶}©lªº HTML_TALL ½g */
+ i = start;
+ end = i + HTML_TALL;
+
+ lseek(fd, (off_t) (sizeof(HDR) * (i - 1)), SEEK_SET);
+
+ while (i < end && read(fd, &hdr, sizeof(HDR)) == sizeof(HDR))
+ {
+ strcpy(owner, hdr.owner);
+ if (ptr1 = strchr(owner, '.')) /* ¯¸¥~§@ªÌ */
+ *(ptr1 + 1) = '\0';
+ if (ptr2 = strchr(owner, '@')) /* ¯¸¥~§@ªÌ */
+ *ptr2 = '\0';
+
+ fputs("<tr onmouseover=mOver(this); onmouseout=mOut(this);>\n", fpw);
+ if (brdname)
+ {
+ fprintf(fpw, " <td><a href=/mpost?%s&%d&%d><img src=/img?mark.gif border=0></a></td>\n"
+ " <td><a href=/dpost?%s&%d&%d><img src=/img?del.gif border=0></a></td>\n",
+ brdname, i, hdr.chrono, brdname, i, hdr.chrono);
+ }
+ else
+ {
+ fprintf(fpw, " <td><a href=/mmail?%d&%d><img src=/img?mark.gif border=0></a></td>\n"
+ " <td><a href=/dmail?%d&%d><img src=/img?del.gif border=0></a></td>\n",
+ i, hdr.chrono, i, hdr.chrono);
+ }
+
+ if (hdr.xmode & POST_BOTTOM)
+ fputs(" <td>­«­n</td>\n", fpw);
+ else
+ fprintf(fpw, " <td>%d</td>\n", i);
+ fprintf(fpw, " <td>%s</td>\n <td>", hdr.xmode & POST_MARKED ? "m" : "");
+
+#ifdef HAVE_SCORE
+ if (hdr.xmode & POST_SCORE)
+ fprintf(fpw, "<font color='%s'>%d</font>", hdr.score >= 0 ? "red" : "green", abs(hdr.score));
+#endif
+
+ fprintf(fpw, "</td>\n <td>%s</td>\n <td><a href=%s%s>%s</a></td>\n",
+ hdr.date + 3, (ptr1 || ptr2) ? "mailto:" : "/query?", hdr.owner, owner);
+
+ if (brdname)
+ fprintf(fpw, " <td><a href=/bmore?%s&%d>", brdname, i);
+ else
+ fprintf(fpw, " <td><a href=/mmore?%d&>", i);
+
+ fprintf(fpw, "%s</td>\n</tr>\n", str_html(hdr.title, 50));
+
+ i++;
+ }
+ close(fd);
+ }
+
+ fputs("</table>\n", fpw);
+}
+
+
+static void
+postlist_neck(fpw, start, total, brdname)
+ FILE *fpw;
+ int start, total;
+ char *brdname;
+{
+ fputs("<br>\n"
+ "<table cellspacing=0 cellpadding=1 border=0 width=760>\n"
+ "<tr bgcolor=" HCOLOR_NECK ">\n <td width=20%", fpw);
+
+ if (start > HTML_TALL)
+ {
+ fprintf(fpw, " align=center><a href=?%s&%d>¤W%d½g</a",
+ brdname, start - HTML_TALL, HTML_TALL);
+ }
+ fputs("></td>\n <td width=20%", fpw);
+
+ start += HTML_TALL;
+ if (start <= total)
+ {
+ fprintf(fpw, " align=center><a href=?%s&%d>¤U%d½g</a",
+ brdname, start, HTML_TALL);
+ }
+
+ fprintf(fpw, "></td>\n <td width=20%% align=center><a href=/dopost?%s target=_blank>µoªí¤å³¹</a></td>\n"
+ " <td width=20%% align=center><a href=/gem?%s>ºëµØ°Ï</a></td>\n"
+ " <td width=20%% align=center><a href=/brdlist>¬ÝªO¦Cªí</a>&nbsp;"
+ "<a href=/rss?%s><img border=0 src=/img?xml.gif alt=\"RSS ­q¾\\³o­Ó¬ÝªO\"></a></td>\n"
+ "</tr></table><br>\n",
+ brdname, brdname, brdname, brdname);
+}
+
+
+static int
+cmd_postlist(ap)
+ Agent *ap;
+{
+ int start, total;
+ char folder[64], *brdname, *number;
+ FILE *fpw = out_head(ap, "¤å³¹¦Cªí");
+
+ if (!arg_analyze(2, '?', ap->urlp, &brdname, &number, NULL, NULL))
+ {
+ if (brdname)
+ number = "0";
+ else
+ return HS_ERROR;
+ }
+
+ if (!allow_brdname(ap, brdname))
+ return HS_ERR_BOARD;
+
+ brd_fpath(folder, brdname, FN_DIR);
+
+ start = atoi(number);
+ total = rec_num(folder, sizeof(HDR));
+ if (start <= 0 || start > total) /* ¶W¹L½d³òªº¸Ü¡Aª½±µ¨ì³Ì«á¤@­¶ */
+ start = (total - 1) / HTML_TALL * HTML_TALL + 1;
+
+ postlist_neck(fpw, start, total, brdname);
+
+ postlist_list(fpw, folder, brdname, start, total);
+
+ postlist_neck(fpw, start, total, brdname);
+ return HS_END;
+}
+
+
+ /* --------------------------------------------------- */
+ /* «H½c¦Cªí */
+ /* --------------------------------------------------- */
+
+static void
+mboxlist_neck(fpw, start, total)
+ FILE *fpw;
+ int start, total;
+{
+ fputs("<br>\n"
+ "<table cellspacing=0 cellpadding=1 border=0 width=760>\n"
+ "<tr bgcolor=" HCOLOR_NECK ">\n <td width=33% ", fpw);
+
+ if (start > HTML_TALL)
+ {
+ fprintf(fpw, "align=center><a href=?%d>¤W%d½g</a",
+ start - HTML_TALL, HTML_TALL);
+ }
+ fputs("></td>\n <td width=33%", fpw);
+
+ start += HTML_TALL;
+ if (start <= total)
+ {
+ fprintf(fpw, " align=center><a href=?%d>¤U%d½g</a",
+ start, HTML_TALL);
+ }
+
+ fputs("></td>\n <td width=34% align=center><a href=/domail target=_blank>µo°e«H¥ó</a></td>\n"
+ "</tr></table><br>\n", fpw);
+}
+
+
+static int
+cmd_mboxlist(ap)
+ Agent *ap;
+{
+ int start, total;
+ char folder[64], *number;
+ FILE *fpw = out_head(ap, "«H½c¦Cªí");
+
+ if (!acct_fetch(ap))
+ return HS_ERR_LOGIN;
+
+ if (!arg_analyze(1, '?', ap->urlp, &number, NULL, NULL, NULL))
+ number = "0";
+
+ usr_fpath(folder, ap->userid, FN_DIR);
+
+ start = atoi(number);
+ total = rec_num(folder, sizeof(HDR));
+ if (start <= 0 || start > total) /* ¶W¹L½d³òªº¸Ü¡Aª½±µ¨ì³Ì«á¤@­¶ */
+ start = (total - 1) / HTML_TALL * HTML_TALL + 1;
+
+ mboxlist_neck(fpw, start, total);
+
+ postlist_list(fpw, folder, NULL, start, total);
+
+ mboxlist_neck(fpw, start, total);
+ return HS_END;
+}
+
+
+ /* --------------------------------------------------- */
+ /* ºëµØ°Ï¦Cªí */
+ /* --------------------------------------------------- */
+
+static void
+gemlist_neck(fpw, brdname)
+ FILE *fpw;
+ char *brdname;
+{
+ fprintf(fpw, "<br>\n"
+ "<table cellspacing=0 cellpadding=1 border=0 width=760>\n"
+ "<tr bgcolor=" HCOLOR_NECK ">\n"
+ " <td width=50%% align=center><a href=/brd?%s>¦^¨ì¬ÝªO</a></td>\n"
+ " <td width=50%% align=center><a href=/brdlist>¬ÝªO¦Cªí</a></td>\n"
+ "</tr></table><br>\n",
+ brdname);
+}
+
+
+static int
+cmd_gemlist(ap)
+ Agent *ap;
+{
+ int fd, i;
+ char folder[64], *brdname, *xname;
+ HDR hdr;
+ FILE *fpw = out_head(ap, "ºëµØ°Ï");
+
+ if (!arg_analyze(2, '?', ap->urlp, &brdname, &xname, NULL, NULL))
+ {
+ if (brdname)
+ xname = FN_DIR;
+ else
+ return HS_ERROR;
+ }
+
+ if ((*xname != 'F' || strlen(xname) != 8) && strcmp(xname, FN_DIR))
+ return HS_ERROR;
+
+ if (!allow_brdname(ap, brdname))
+ return HS_ERR_BOARD;
+
+ gemlist_neck(fpw, brdname);
+
+ fputs("<table cellspacing=0 cellpadding=4 border=0>\n"
+ "<tr bgcolor=" HCOLOR_TIE ">\n"
+ " <td width=50>½s¸¹</td>\n"
+ " <td width=400>¼ÐÃD</td>\n"
+ "</tr>\n", fpw);
+
+ if (*xname == '.')
+ sprintf(folder, "gem/brd/%s/%s", brdname, FN_DIR);
+ else /* if (*xname == 'F') */
+ sprintf(folder, "gem/brd/%s/%c/%s", brdname, xname[7], xname);
+
+ if ((fd = open(folder, O_RDONLY)) >= 0)
+ {
+ i = 1;
+ while (read(fd, &hdr, sizeof(HDR)) == sizeof(HDR))
+ {
+ fprintf(fpw, "<tr onmouseover=mOver(this); onmouseout=mOut(this);>\n"
+ " <td>%d</td>\n", i);
+ if (hdr.xmode & GEM_RESTRICT)
+ {
+ fputs(" <td>[°ßŪ] ¸ê®Æ«O±K</td>\n</tr>\n", fpw);
+ }
+ else if (hdr.xname[0] == 'A') /* ¤å³¹ */
+ {
+ fprintf(fpw, " <td><a href=/gmore?%s&%s&%d>[¤å³¹] %s</a></td>\n</tr>\n",
+ brdname, xname, i, str_html(hdr.title, TTLEN));
+ }
+ else if (hdr.xname[0] == 'F') /* ¨÷©v */
+ {
+ fprintf(fpw, " <td><a href=/gem?%s&%s>[¨÷©v] %s</a></td>\n</tr>\n",
+ brdname, hdr.xname, str_html(hdr.title, TTLEN));
+ }
+ else /* ¨ä¥LÃþ§O´N¤£¨q¤F */
+ {
+ fputs(" <td>[°ßŪ] ¨ä¥L¸ê®Æ</td>\n</tr>\n", fpw);
+ }
+
+ i++;
+ }
+ close(fd);
+ }
+
+ fputs("</table>\n", fpw);
+
+ gemlist_neck(fpw, brdname);
+ return HS_END;
+}
+
+
+ /* --------------------------------------------------- */
+ /* ¾\Ū¬ÝªO¤å³¹ */
+ /* --------------------------------------------------- */
+
+static void
+more_neck(fpw, pos, total, brdname, xname)
+ FILE *fpw;
+ int pos, total;
+ char *brdname, *xname;
+{
+ fputs("<br>\n"
+ "<table cellspacing=0 cellpadding=1 border=0 width=760>\n"
+ "<tr bgcolor=" HCOLOR_NECK ">\n <td width=20%", fpw);
+
+ if (pos > 1)
+ {
+ fputs(" align=center><a href=?", fpw);
+ if (brdname)
+ fprintf(fpw, "%s&", brdname);
+ if (xname)
+ fprintf(fpw, "%s&", xname);
+ fprintf(fpw, "%d>¤W¤@½g</a", pos - 1);
+ }
+ fputs("></td>\n <td width=20%", fpw);
+
+ if (pos < total)
+ {
+ fputs(" align=center><a href=?", fpw);
+ if (brdname)
+ fprintf(fpw, "%s&", brdname);
+ if (xname)
+ fprintf(fpw, "%s&", xname);
+ fprintf(fpw, "%d>¤U¤@½g</a", pos + 1);
+ }
+
+ if (xname)
+ fprintf(fpw, "></td>\n <td width=60%% align=center><a href=/gem?%s&%s>¦^¨ì¨÷©v</a", brdname, xname);
+ else if (brdname)
+ {
+ fprintf(fpw, "></td>\n <td width=20%% align=center><a href=/bmost?%s&%d target=_blank>¦P¼ÐÃD</a></td>\n"
+ " <td width=20%% align=center><a href=/dopost?%s target=_blank>µoªí¤å³¹</a></td>\n"
+ " <td width=20%% align=center><a href=/brd?%s>¤å³¹¦Cªí</a",
+ brdname, pos,
+ brdname, brdname);
+ }
+ else
+ fputs("></td>\n <td width=60% align=center><a href=/mbox>«H½c¦Cªí</a", fpw);
+
+ fputs("></td>\n</tr></table><br>\n", fpw);
+}
+
+
+static int
+more_item(fpw, folder, pos, brdname)
+ FILE *fpw;
+ char *folder;
+ int pos;
+ char *brdname;
+{
+ int fd, total;
+ HDR hdr;
+ char fpath[64];
+
+ total = rec_num(folder, sizeof(HDR));
+ if ((fd = open(folder, O_RDONLY)) >= 0)
+ {
+ int find;
+
+ lseek(fd, (off_t) (sizeof(HDR) * (pos - 1)), SEEK_SET);
+ find = read(fd, &hdr, sizeof(HDR)) == sizeof(HDR);
+ close(fd);
+
+ if (find)
+ {
+ more_neck(fpw, pos, total, brdname, NULL);
+
+#ifdef HAVE_REFUSEMARK
+ if (!(hdr.xmode & POST_RESTRICT))
+ {
+#endif
+ hdr_fpath(fpath, folder, &hdr);
+ out_article(fpw, fpath);
+#ifdef HAVE_REFUSEMARK
+ }
+ else
+ out_mesg(fpw, "³o¬O¥[±Kªº¤å³¹¡A±zµLªk¾\\Ū");
+#endif
+
+ more_neck(fpw, pos, total, brdname, NULL);
+ return HS_END;
+ }
+ }
+
+ return HS_ERR_MORE;
+}
+
+
+static int
+cmd_brdmore(ap)
+ Agent *ap;
+{
+ int pos;
+ char folder[64], *brdname, *number;
+ FILE *fpw = out_head(ap, "¾\\Ū¬ÝªO¤å³¹");
+
+ if (!arg_analyze(2, '?', ap->urlp, &brdname, &number, NULL, NULL))
+ return HS_ERROR;
+
+ if (!allow_brdname(ap, brdname))
+ return HS_ERR_BOARD;
+
+ brd_fpath(folder, brdname, FN_DIR);
+
+ if ((pos = atoi(number)) <= 0)
+ pos = 1;
+
+ return more_item(fpw, folder, pos, brdname);
+}
+
+
+ /* --------------------------------------------------- */
+ /* ¾\Ū«H½c¤å³¹ */
+ /* --------------------------------------------------- */
+
+static int
+cmd_mboxmore(ap)
+ Agent *ap;
+{
+ int pos;
+ char folder[64], *number;
+ FILE *fpw = out_head(ap, "¾\\Ū«H½c¤å³¹");
+
+ if (!arg_analyze(1, '?', ap->urlp, &number, NULL, NULL, NULL))
+ return HS_ERROR;
+
+ if (!acct_fetch(ap))
+ return HS_ERR_LOGIN;
+
+ usr_fpath(folder, ap->userid, FN_DIR);
+
+ if ((pos = atoi(number)) <= 0)
+ pos = 1;
+
+ return more_item(fpw, folder, pos, NULL);
+}
+
+
+ /* --------------------------------------------------- */
+ /* ¾\Ū¬ÝªO¦P¼ÐÃD¤å³¹ */
+ /* --------------------------------------------------- */
+
+static void
+do_brdmost(fpw, folder, title)
+ FILE *fpw;
+ char *folder, *title;
+{
+ int fd;
+ char fpath[64];
+ FILE *fp;
+ HDR hdr;
+
+ if ((fd = open(folder, O_RDONLY)) >= 0)
+ {
+ while (read(fd, &hdr, sizeof(HDR)) == sizeof(HDR))
+ {
+#ifdef HAVE_REFUSEMARK
+ if (hdr.xmode & POST_RESTRICT)
+ continue;
+#endif
+
+ if (!strcmp(str_ttl(hdr.title), title))
+ {
+ hdr_fpath(fpath, folder, &hdr);
+ if (fp = fopen(fpath, "r"))
+ {
+ txt2htm(fpw, fp);
+ fclose(fp);
+ }
+ }
+ }
+ close(fd);
+ }
+}
+
+
+static void
+brdmost_neck(fpw)
+ FILE *fpw;
+{
+ fputs("<br>\n"
+ "<table cellspacing=0 cellpadding=1 border=0 width=760>\n"
+ "<tr bgcolor=" HCOLOR_NECK ">\n"
+ " <td align=center>¦P¼ÐÃD¾\\Ū</td>\n"
+ "</tr></table><br>\n", fpw);
+}
+
+
+static int
+cmd_brdmost(ap)
+ Agent *ap;
+{
+ int fd, pos;
+ char folder[64], *brdname, *number;
+ HDR hdr;
+ FILE *fpw = out_head(ap, "¾\\Ū¬ÝªO¦P¼ÐÃD¤å³¹");
+
+ if (!arg_analyze(2, '?', ap->urlp, &brdname, &number, NULL, NULL))
+ return HS_ERROR;
+
+ if (!allow_brdname(ap, brdname))
+ return HS_ERR_BOARD;
+
+ brd_fpath(folder, brdname, FN_DIR);
+
+ if ((pos = atoi(number)) <= 0)
+ pos = 1;
+
+ if ((fd = open(folder, O_RDONLY)) >= 0)
+ {
+ int find;
+
+ lseek(fd, (off_t) (sizeof(HDR) * (pos - 1)), SEEK_SET);
+ find = read(fd, &hdr, sizeof(HDR)) == sizeof(HDR);
+ close(fd);
+
+ if (find)
+ {
+ brdmost_neck(fpw);
+ out_style(fpw);
+ do_brdmost(fpw, folder, str_ttl(hdr.title));
+ brdmost_neck(fpw);
+ return HS_END;
+ }
+ }
+ return HS_ERR_MORE;
+}
+
+
+ /* --------------------------------------------------- */
+ /* ¾\ŪºëµØ°Ï¤å³¹ */
+ /* --------------------------------------------------- */
+
+static int
+cmd_gemmore(ap)
+ Agent *ap;
+{
+ int fd, pos, total;
+ char *brdname, *xname, *number, folder[64];
+ HDR hdr;
+ FILE *fpw = out_head(ap, "¾\\ŪºëµØ°Ï¤å³¹");
+
+ if (!arg_analyze(3, '?', ap->urlp, &brdname, &xname, &number, NULL))
+ return HS_ERROR;
+
+ if (*xname != 'F' && strlen(xname) != 8 && strcmp(xname, FN_DIR))
+ return HS_ERROR;
+
+ if (!allow_brdname(ap, brdname))
+ return HS_ERR_BOARD;
+
+ if (*xname == '.')
+ gem_fpath(folder, brdname, FN_DIR);
+ else /* if (*xname == 'F') */
+ sprintf(folder, "gem/brd/%s/%c/%s", brdname, xname[7], xname);
+
+ if ((pos = atoi(number)) <= 0)
+ pos = 1;
+ total = rec_num(folder, sizeof(HDR));
+
+ if ((fd = open(folder, O_RDONLY)) >= 0)
+ {
+ int find;
+
+ lseek(fd, (off_t) (sizeof(HDR) * (pos - 1)), SEEK_SET);
+ find = read(fd, &hdr, sizeof(HDR)) == sizeof(HDR);
+ close(fd);
+
+ if (find)
+ {
+ more_neck(fpw, pos, total, brdname, xname);
+ if (hdr.xname[0] == 'A')
+ {
+ if (!(hdr.xmode & GEM_RESTRICT))
+ {
+ sprintf(folder, "gem/brd/%s/%c/%s", brdname, hdr.xname[7], hdr.xname);
+ out_article(fpw, folder);
+ }
+ else
+ out_mesg(fpw, "¦¹¬°«O±KºëµØ°Ï¡A±zµLªk¾\\Ū");
+ }
+ else
+ out_mesg(fpw, "³o¬O¨÷©v©Î°ßŪ¸ê®Æ¡A±z¥²¶·¥ÑºëµØ°Ï¦Cªí¨ÓŪ¨ú");
+ more_neck(fpw, pos, total, brdname, xname);
+ return HS_END;
+ }
+ }
+
+ return HS_ERR_MORE;
+}
+
+
+ /* --------------------------------------------------- */
+ /* µoªí¤å³¹ */
+ /* --------------------------------------------------- */
+
+static int
+cmd_dopost(ap)
+ Agent *ap;
+{
+ char *brdname;
+ FILE *fpw = out_head(ap, "µoªí¤å³¹");
+
+ if (!arg_analyze(1, '?', ap->urlp, &brdname, NULL, NULL, NULL))
+ return HS_ERROR;
+
+ if (!(ben_perm(ap, brdname) & BRD_W_BIT))
+ return HS_ERR_BOARD;
+
+ fputs("<form method=post onsubmit=\"if(t.value.length==0 || c.value.length==0) {alert('¼ÐÃD¡B¤º®e§¡¤£¥i¬°ªÅ¥Õ'); return false;} return true;\">\n"
+ " <input type=hidden name=dopost>\n"
+ " ½Ð¿é¤J¼ÐÃD¡G<br>\n", fpw);
+ fprintf(fpw,
+ " <input type=hidden name=b value=%s>\n"
+ " <input type=text name=t size=%d maxlength=%d><br><br>\n"
+ " ½Ð¿é¤J¤º®e¡G<br>\n"
+ " <textarea name=c rows=10 cols=%d></textarea><br><br>\n"
+ " <input type=hidden name=end>\r\n"
+ " <input type=submit value=°e¥X¤å³¹> "
+ " <input type=reset value=­«·s¶ñ¼g>"
+ "</form>\n",
+ brdname,
+ TTLEN, TTLEN,
+ SCR_WIDTH);
+
+ return HS_END;
+}
+
+
+ /* --------------------------------------------------- */
+ /* µo°e«H¥ó */
+ /* --------------------------------------------------- */
+
+static int
+cmd_domail(ap)
+ Agent *ap;
+{
+ char *userid;
+ FILE *fpw = out_head(ap, "µo°e«H¥ó");
+
+ if (!acct_fetch(ap))
+ return HS_ERR_LOGIN;
+
+ if (!arg_analyze(1, '?', ap->urlp, &userid, NULL, NULL, NULL))
+ userid = "";
+
+ fputs("<form method=post onsubmit=\"if(u.value.length==0 || t.value.length==0 || c.value.length==0) {alert('¦¬«H¤H¡B¼ÐÃD¡B¤º®e§¡¤£¥i¬°ªÅ¥Õ'); return false;} return true;\">\n"
+ " <input type=hidden name=domail>\n"
+ " ½Ð¿é¤J¦¬«H¤H¢×¢Ò¡G<br>\n", fpw);
+ fprintf(fpw,
+ " <input type=text name=u size=%d maxlength=%d value=%s><br><br>\n"
+ " ½Ð¿é¤J¼ÐÃD¡G<br>\n"
+ " <input type=text name=t size=%d maxlength=%d><br><br>\n"
+ " ½Ð¿é¤J¤º®e¡G<br>\n"
+ " <textarea name=c rows=10 cols=%d></textarea><br><br>\n"
+ " <input type=hidden name=end>\r\n"
+ " <input type=submit value=°e¥X«H¥ó> "
+ " <input type=reset value=­«·s¶ñ¼g>"
+ "</form>\n",
+ IDLEN, IDLEN, userid,
+ TTLEN, TTLEN,
+ SCR_WIDTH);
+
+ return HS_END;
+}
+
+
+ /* --------------------------------------------------- */
+ /* ¼Ð°O/§R°£ ¤å³¹ */
+ /* --------------------------------------------------- */
+
+static void outgo_post();
+
+static void
+move_post(userid, hdr, folder, by_bm) /* ±N hdr ±q folder ·h¨ì§OªºªO */
+ char *userid;
+ HDR *hdr;
+ char *folder;
+ int by_bm;
+{
+ HDR post;
+ int xmode;
+ char fpath[64], fnew[64], *board;
+
+ xmode = hdr->xmode;
+ hdr_fpath(fpath, folder, hdr);
+
+ if (!(xmode & POST_BOTTOM)) /* ¸m©³¤å³Q¬å¤£¥Î move_post */
+ {
+#ifdef HAVE_REFUSEMARK
+ board = by_bm && !(xmode & POST_RESTRICT) ? BN_DELETED : BN_JUNK; /* ¥[±K¤å³¹¥á¥h junk */
+#else
+ board = by_bm ? BN_DELETED : BN_JUNK;
+#endif
+
+ brd_fpath(fnew, board, FN_DIR);
+ hdr_stamp(fnew, HDR_LINK | 'A', &post, fpath);
+
+ /* ª½±µ½Æ»s trailing data¡Gowner(§t)¥H¤U©Ò¦³Äæ¦ì */
+ memcpy(post.owner, hdr->owner, sizeof(HDR) -
+ (sizeof(post.chrono) + sizeof(post.xmode) + sizeof(post.xid) + sizeof(post.xname)));
+
+ if (by_bm)
+ sprintf(post.title, "%-13s%.59s", userid, hdr->title);
+
+ rec_bot(fnew, &post, sizeof(HDR));
+ brd_get(board)->btime = -1;
+ }
+ unlink(fpath);
+}
+
+
+static int
+op_shell(op_func, ap, title, msg)
+ int (*op_func) (Agent *, char *, char *);
+ Agent *ap;
+ char *title, *msg;
+{
+ int ret = op_func(ap, title, msg);
+ if (ret != HS_END)
+ out_head(ap, title + 1);
+ return ret;
+}
+
+
+static int
+post_op(ap, title, msg)
+ Agent *ap;
+ char *title, *msg;
+{
+ int pos;
+ time_t chrono;
+ HDR hdr;
+ usint bits;
+ char *brdname, *number, *stamp, folder[64];
+ FILE *fpw;
+
+ if (!arg_analyze(3, '?', ap->urlp, &brdname, &number, &stamp, NULL) ||
+ (pos = atoi(number) - 1) < 0 ||
+ (chrono = atoi(stamp)) < 0)
+ return HS_ERROR;
+
+ if (bits = ben_perm(ap, brdname))
+ {
+ brd_fpath(folder, brdname, FN_DIR);
+ if (rec_get(folder, &hdr, sizeof(HDR), pos) ||
+ (hdr.chrono != chrono))
+ return HS_ERR_MORE;
+
+ if (*title == 'm')
+ {
+ if (!(bits & BRD_X_BIT))
+ return HS_ERR_PERM;
+
+ hdr.xmode ^= POST_MARKED;
+ rec_put(folder, &hdr, sizeof(HDR), pos, NULL);
+ }
+ else /* if (*title == 'd') */
+ {
+ if (!(hdr.xmode & POST_MARKED))
+ {
+ if (!(bits & BRD_X_BIT) &&
+ (!(bits & BRD_W_BIT) || strcmp(ap->userid, hdr.owner)))
+ return HS_ERR_PERM;
+
+ rec_del(folder, sizeof(HDR), pos, NULL);
+ move_post(ap->userid, &hdr, folder, bits & BRD_X_BIT);
+ brd_get(brdname)->btime = -1;
+ /* ³s½u¬å«H */
+ if ((hdr.xmode & POST_OUTGO) && /* ¥~Âà«H¥ó */
+ hdr.chrono > (time(0) - 7 * 86400)) /* 7 ¤Ñ¤§¤º¦³®Ä */
+ {
+ hdr.chrono = -1;
+ outgo_post(&hdr, brdname);
+ }
+ }
+ }
+
+ sprintf(folder, "/brd?%s&%d", brdname, (pos - 1) / HTML_TALL * HTML_TALL + 1);
+ fpw = out_http(ap, HS_OK | HS_REFRESH, folder);
+ out_title(fpw, title + 1);
+ out_mesg(fpw, msg);
+ fprintf(fpw, "<a href=%s>¦^¤å³¹¦Cªí</a>\n", folder);
+
+ return HS_END;
+ }
+ return HS_ERR_BOARD;
+}
+
+
+static int
+cmd_markpost(ap)
+ Agent *ap;
+{
+ return op_shell(post_op, ap, "m¼Ð°O¤å³¹", "¤w°õ¦æ(¨ú®ø)¼Ð°O«ü¥O");
+}
+
+
+static int
+cmd_delpost(ap)
+ Agent *ap;
+{
+ return op_shell(post_op, ap, "d§R°£¤å³¹", "¤w°õ¦æ§R°£«ü¥O¡A­Y¥¼§R°£ªí¥Ü¦¹¤å³¹³Q¼Ð°O¤F");
+}
+
+
+static int
+cmd_predelpost(ap)
+ Agent *ap;
+{
+ char *brdname, *number, *stamp;
+ FILE *fpw = out_head(ap, "½T»{§R°£¤å³¹");
+
+ if (!arg_analyze(3, '?', ap->urlp, &brdname, &number, &stamp, NULL))
+ return HS_ERROR;
+
+ out_mesg(fpw, "­Y½T©w­n§R°£¦¹½g¤å³¹¡A½Ð¦A¦¸ÂI¿ï¥H¤U³sµ²¡F­Y­n¨ú®ø§R°£¡A½Ð«ö [¤W¤@­¶]");
+ fprintf(fpw, "<a href=/delpost?%s&%s&%s>§R°£ [%s] ªO²Ä %s ½g¤å³¹</a><br>\n",
+ brdname, number, stamp, brdname, number);
+
+ return HS_END;
+}
+
+
+ /* --------------------------------------------------- */
+ /* ¼Ð°O/§R°£ «H¥ó */
+ /* --------------------------------------------------- */
+
+static int
+mail_op(ap, title, msg)
+ Agent *ap;
+ char *title, *msg;
+{
+ int pos;
+ time_t chrono;
+ HDR hdr;
+ char *number, *stamp, folder[64], fpath[64];
+
+ if (!arg_analyze(2, '?', ap->urlp, &number, &stamp, NULL, NULL) ||
+ (pos = atoi(number) - 1) < 0 ||
+ (chrono = atoi(stamp)) < 0)
+ return HS_ERROR;
+
+ if (acct_fetch(ap))
+ {
+ usr_fpath(folder, ap->userid, FN_DIR);
+ if (rec_get(folder, &hdr, sizeof(HDR), pos) ||
+ (hdr.chrono != chrono))
+ return HS_ERR_MAIL;
+
+ if (*title == 'm')
+ {
+ hdr.xmode ^= POST_MARKED;
+ rec_put(folder, &hdr, sizeof(HDR), pos, NULL);
+ }
+ else /* if (*title == 'd') */
+ {
+ if (!(hdr.xmode & POST_MARKED))
+ {
+ rec_del(folder, sizeof(HDR), pos, NULL);
+ hdr_fpath(fpath, folder, &hdr);
+ unlink(fpath);
+ }
+ }
+
+ sprintf(folder, "/mbox?%d", (pos - 1) / HTML_TALL * HTML_TALL + 1);
+ out_title(out_http(ap, HS_OK | HS_REFRESH, folder), title + 1);
+ out_mesg(ap->fpw, msg);
+ fprintf(ap->fpw, "<a href=%s>¦^«H½c¦Cªí</a>\n", folder);
+ return HS_END;
+ }
+ return HS_ERR_LOGIN;
+}
+
+
+static int
+cmd_markmail(ap)
+ Agent *ap;
+{
+ return op_shell(mail_op, ap, "m¼Ð°O«H¥ó", "¤w°õ¦æ(¨ú®ø)¼Ð°O«ü¥O");
+}
+
+
+static int
+cmd_delmail(ap)
+ Agent *ap;
+{
+ return op_shell(mail_op, ap, "d§R°£«H¥ó", "¤w°õ¦æ§R°£«ü¥O¡A­Y¥¼§R°£ªí¥Ü¦¹«H¥ó³Q¼Ð°O¤F");
+}
+
+
+static int
+cmd_predelmail(ap)
+ Agent *ap;
+{
+ char *number, *stamp;
+ FILE *fpw = out_head(ap, "½T»{§R°£«H¥ó");
+
+ if (!arg_analyze(2, '?', ap->urlp, &number, &stamp, NULL, NULL))
+ return HS_ERROR;
+
+ out_mesg(fpw, "­Y½T©w­n§R°£¦¹½g«H¥ó¡A½Ð¦A¦¸ÂI¿ï¥H¤U³sµ²¡F­Y­n¨ú®ø§R°£¡A½Ð«ö [¤W¤@­¶]");
+ fprintf(fpw, "<a href=/delmail?%s&%s>§R°£«H½c²Ä %s ½g«H¥ó</a><br>\n",
+ number, stamp, number);
+
+ return HS_END;
+}
+
+
+ /* --------------------------------------------------- */
+ /* ¬d¸ß¨Ï¥ÎªÌ */
+ /* --------------------------------------------------- */
+
+static int
+cmd_query(ap)
+ Agent *ap;
+{
+ int fd;
+ ACCT acct;
+ char fpath[64], *userid;
+ FILE *fpw = out_head(ap, "¬d¸ß¨Ï¥ÎªÌ");
+
+ if (!arg_analyze(1, '?', ap->urlp, &userid, NULL, NULL, NULL))
+ return HS_ERROR;
+
+ if (!allow_userid(ap, userid))
+ return HS_ERR_USER;
+
+ usr_fpath(fpath, userid, FN_ACCT);
+ if ((fd = open(fpath, O_RDONLY)) >= 0)
+ {
+ read(fd, &acct, sizeof(ACCT));
+ close(fd);
+
+ fprintf(fpw, "<pre>\n"
+ "<a target=_blank href=domail?%s>%s (%s)</a><br>\n"
+ "%s³q¹L»{ÃÒ¡A¦@¤W¯¸ %d ¦¸¡Aµoªí¹L %d ½g¤å³¹<br>\n"
+ "³Ìªñ(%s)±q[%s]¤W¯¸<br>\n"
+ "</pre>\n",
+ acct.userid, acct.userid,
+ str_html(acct.username, UNLEN),
+ (acct.userlevel & PERM_VALID) ? "¤w" : "¥¼", acct.numlogins, acct.numposts,
+ Btime(&(acct.lastlogin)), acct.lasthost);
+
+ usr_fpath(fpath, acct.userid, FN_PLANS);
+ out_article(fpw, fpath);
+ return HS_END;
+ }
+
+ return HS_ERR_USER;
+}
+
+
+ /* --------------------------------------------------- */
+ /* Åã¥Ü¹Ï¤ù */
+ /* --------------------------------------------------- */
+
+static int
+valid_path(str)
+ char *str;
+{
+ int ch;
+
+ if (!*str)
+ return;
+
+ while (ch = *str++)
+ {
+ if (!is_alnum(ch) && ch != '.' && ch != '-' && ch != '_')
+ return 0;
+ }
+ return 1;
+}
+
+
+static int
+cmd_image(ap)
+ Agent *ap;
+{
+ FILE *fpw;
+ struct stat st;
+ char *fname, *ptr, fpath[64];
+
+ if (!arg_analyze(1, '?', ap->urlp, &fname, NULL, NULL, NULL))
+ return HS_NOTFOUND;
+
+ if (!valid_path(fname) || !(ptr = strchr(fname, '.')))
+ return HS_NOTFOUND;
+
+ /* ¤ä´©®æ¦¡ */
+ if (!str_cmp(ptr, ".html"))
+ ptr = "text/html";
+ else if (!str_cmp(ptr, ".gif"))
+ ptr = "image/gif";
+ else if (!str_cmp(ptr, ".jpg"))
+ ptr = "image/jpeg";
+ else if (!str_cmp(ptr, ".css"))
+ ptr = "text/css";
+ else if (!str_cmp(ptr, ".png"))
+ ptr = "image/png";
+ else
+ return HS_NOTFOUND;
+
+ sprintf(fpath, "run/html/%.40s", fname);
+ if (stat(fpath, &st))
+ return HS_NOTFOUND;
+
+ if (ap->modified[0] && !strcmp(Gtime(&st.st_mtime), ap->modified)) /* ¨S¦³Åܧ󤣻ݭn¶Ç¿é */
+ return HS_NOTMOIDIFY;
+
+ fpw = out_http(ap, HS_OK, ptr);
+ fprintf(fpw, "Content-Length: %d\r\n", st.st_size);
+ fprintf(fpw, "Last-Modified: %s\r\n\r\n", Gtime(&st.st_mtime));
+ f_suck(fpw, fpath);
+
+ return HS_OK;
+}
+
+
+ /* --------------------------------------------------- */
+ /* RSS */
+ /* --------------------------------------------------- */
+
+static int
+cmd_rss(ap)
+ Agent *ap;
+{
+ time_t blast;
+ char folder[64], *brdname, *ptr;
+ BRD *brd;
+ HDR hdr;
+ FILE *fpw;
+ int fd;
+
+ if (!arg_analyze(1, '?', ap->urlp, &brdname, NULL, NULL, NULL))
+ return HS_BADREQUEST;
+
+ if (!(brd = brd_get(brdname)))
+ return HS_NOTFOUND;
+
+ if (brd->readlevel) /* ¥u¦³¤½¶}ªO¤~´£¨Ñ rss */
+ return HS_FORBIDDEN;
+
+ blast = brd->blast;
+
+ if (ap->modified[0] && !strcmp(Gtime(&blast), ap->modified)) /* ¨S¦³Åܧ󤣻ݭn¶Ç¿é */
+ return HS_NOTMOIDIFY;
+
+ fpw = out_http(ap, HS_OK, "application/xml");
+ fprintf(fpw, "Last-Modified: %s\r\n\r\n", Gtime(&blast));
+
+ /* xml header */
+ fputs("<?xml version=\"1.0\" encoding=\"" MYCHARSET "\" ?>\n"
+ "<rss version=\"2.0\">\n"
+ "<channel>\n", fpw);
+ ptr = Gtime(&blast);
+ ptr[4] = '\0';
+ fprintf(fpw, "<title>" BBSNAME "-%sªO</title>\n"
+#if BHTTP_PORT == 80
+ "<link>http://" MYHOSTNAME "/brd?%s</link>\n"
+#else
+ "<link>http://" MYHOSTNAME ":%d/brd?%s</link>\n"
+#endif
+ "<description>%s</description>\n"
+ "<language>zh-tw</language>\n"
+ "<lastBuildDate>%s ",
+ brdname,
+#if BHTTP_PORT != 80
+ BHTTP_PORT,
+#endif
+ brdname,
+ str_html(brd->title, TTLEN), ptr);
+ ptr += 5;
+ if (*ptr == '0')
+ ptr++;
+ fprintf(fpw, "%s</lastBuildDate>\n<image>\n"
+ "<title>" BBSNAME "</title>"
+#if BHTTP_PORT == 80
+ "<link>http://" MYHOSTNAME "</link>\n"
+ "<url>http://" MYHOSTNAME "/img?rss.gif</url>\n"
+#else
+ "<link>http://" MYHOSTNAME ":%d</link>\n"
+ "<url>http://" MYHOSTNAME ":%d/img?rss.gif</url>\n"
+#endif
+ "</image>\n",
+#if BHTTP_PORT == 80
+ ptr);
+#else
+ ptr, BHTTP_PORT, BHTTP_PORT);
+#endif
+
+ /* rss item */
+ brd_fpath(folder, brdname, FN_DIR);
+ if ((fd = open(folder, O_RDONLY)) >= 0)
+ {
+ int fsize;
+ struct stat st;
+
+ if (!fstat(fd, &st) && (fsize = st.st_size) >= sizeof(HDR))
+ {
+ int i, end;
+
+ /* ¥u¦C¥X³Ì«á¤G¤Q½g */
+ if (fsize > 20 * sizeof(HDR))
+ end = fsize - 20 * sizeof(HDR);
+ else
+ end = 0;
+ i = fsize / sizeof(HDR);
+
+ while ((fsize -= sizeof(HDR)) >= end)
+ {
+ lseek(fd, fsize, SEEK_SET);
+ read(fd, &hdr, sizeof(HDR));
+
+ ptr = Gtime(&hdr.chrono);
+ ptr[4] = '\0';
+ fprintf(fpw, "<!-- %d --><item><title>%s</title>"
+#if BHTTP_PORT == 80
+ "<link>http://" MYHOSTNAME "/bmore?%s&amp;%d</link>"
+#else
+ "<link>http://" MYHOSTNAME ":%d/bmore?%s&amp;%d</link>"
+#endif
+ "<author>%s</author>"
+ "<pubDate>%s ",
+ hdr.chrono, str_html(hdr.title, TTLEN),
+#if BHTTP_PORT != 80
+ BHTTP_PORT,
+#endif
+ brdname, i,
+ hdr.owner,
+ ptr);
+ ptr += 5;
+ if (*ptr == '0')
+ ptr++;
+ fprintf(fpw, "%s</pubDate></item>\n", ptr);
+
+ i--;
+ }
+ }
+ close(fd);
+ }
+ fputs("</channel>\n</rss>\n", fpw);
+
+ return HS_OK;
+}
+
+
+ /* --------------------------------------------------- */
+ /* Robot Exclusion */
+ /* --------------------------------------------------- */
+
+#ifdef ROBOT_EXCLUSION
+static int
+cmd_robot(ap)
+ Agent *ap;
+{
+ FILE *fpw = out_http(ap, HS_OK, NULL);
+
+ fprintf(fpw, "Content-Length: 28\r\n"); /* robot.txt ªºªø«× */
+ fprintf(fpw, "Last-Modified: Sat, 01 Jan 2000 00:02:21 GMT\r\n\r\n"); /* ÀH«Kµ¹­Ó®É¶¡ */
+
+ fprintf(fpw, "User-agent: *\r\nDisallow: /\r\n");
+
+ return HS_OK;
+}
+#endif
+
+
+ /* --------------------------------------------------- */
+ /* ­º­¶ */
+ /* --------------------------------------------------- */
+
+static void
+mainpage_neck(fpw, userid, logined)
+ FILE *fpw;
+ char *userid;
+ int logined;
+{
+ fprintf(fpw, "<br>\n"
+ "<table cellspacing=0 cellpadding=1 border=0 width=760>\n"
+ "<tr bgcolor=" HCOLOR_NECK ">\n"
+ " <td width=100%% align=center>%s%sÅwªï¥úÁ{</td>\n"
+ "</tr></table><br>\n",
+ logined ? userid : "",
+ logined ? "¡A" : "");
+}
+
+
+static int
+cmd_mainpage(ap)
+ Agent *ap;
+{
+ int logined;
+ FILE *fpw = out_head(ap, "");
+
+ logined = acct_fetch(ap);
+ mainpage_neck(fpw, ap->userid, logined);
+
+ out_film(fpw, (ap->uptime % 3) + FILM_OPENING0);
+ /* µn¤J */
+ if (!logined)
+ {
+ /* ¶}ÀYµe­± */
+ fputs("<form method=post>\n"
+ " <input type=hidden name=login>\n"
+ " ±b¸¹ <input type=text name=u size=12 maxlength=12> "
+ " ±K½X <input type=password name=p size=12 maxlength=8> "
+ " <input type=submit value=µn¤J> "
+ " <input type=reset value=²M°£>"
+ "</form>\n", fpw);
+ }
+
+ mainpage_neck(fpw, ap->userid, logined);
+ return HS_END;
+}
+
+
+ /* --------------------------------------------------- */
+ /* «ü¥O¶° */
+ /* --------------------------------------------------- */
+
+static Command cmd_table_get[] =
+{
+ cmd_userlist, "usrlist", 7,
+ cmd_boardlist, "brdlist", 7,
+ cmd_favorlist, "fvrlist", 7,
+ cmd_class, "class", 5,
+
+ cmd_postlist, "brd", 3,
+ cmd_gemlist, "gem", 3,
+ cmd_mboxlist, "mbox", 4,
+
+ cmd_brdmore, "bmore", 5,
+ cmd_brdmost, "bmost", 5,
+ cmd_gemmore, "gmore", 5,
+ cmd_mboxmore, "mmore", 5,
+
+ cmd_dopost, "dopost", 6,
+ cmd_domail, "domail", 6,
+
+ cmd_delpost, "delpost", 7,
+ cmd_predelpost, "dpost", 5,
+ cmd_delmail, "delmail", 7,
+ cmd_predelmail, "dmail", 5,
+ cmd_markpost, "mpost", 5,
+ cmd_markmail, "mmail", 5,
+
+ cmd_query, "query", 5,
+
+ cmd_image, "img", 3,
+ cmd_rss, "rss", 3,
+
+#ifdef ROBOT_EXCLUSION
+ cmd_robot, "robot.txt", 9,
+#endif
+
+ cmd_mainpage, "", 0,
+
+ NULL, NULL, 0
+};
+
+
+/* ----------------------------------------------------- */
+/* command dispatch (POST) */
+/* ----------------------------------------------------- */
+
+static char *
+getfromhost(pip)
+ void *pip;
+{
+ struct hostent *hp;
+
+ if (hp = gethostbyaddr((char *)pip, 4, AF_INET))
+ return hp->h_name;
+ return inet_ntoa(*(struct in_addr *) pip);
+}
+
+
+ /* --------------------------------------------------- */
+ /* ¨Ï¥ÎªÌµn¤J */
+ /* --------------------------------------------------- */
+
+static int
+cmd_login(ap)
+ Agent *ap;
+{
+ char *userid, *passwd;
+ char fpath[64];
+ ACCT acct;
+
+ /* u=userid&p=passwd */
+ if (!arg_analyze(2, 0, ap->urlp, &userid, &passwd, NULL, NULL))
+ return HS_ERROR;
+
+ userid += 2; /* skip "u=" */
+ passwd += 2; /* skip "p=" */
+
+ if (*userid && *passwd && strlen(userid) <= IDLEN && strlen(passwd) <= PSWDLEN)
+ {
+ usr_fpath(fpath, userid, FN_ACCT);
+ if (!rec_get(fpath, &acct, sizeof(ACCT), 0) &&
+ !(acct.userlevel & (PERM_DENYLOGIN | PERM_PURGE)) &&
+ !chkpasswd(acct.passwd, passwd)) /* µn¤J¦¨¥\ */
+ {
+ /* itoc.040308: ²£¥Í Cookie */
+ sprintf(ap->cookie, "%s&p=%s", userid, acct.passwd);
+ ap->setcookie = 1;
+ }
+ }
+
+ return cmd_mainpage(ap);
+}
+
+
+ /* --------------------------------------------------- */
+ /* µoªí·s¤å³¹ */
+ /* --------------------------------------------------- */
+
+static void
+outgo_post(hdr, board)
+ HDR *hdr;
+ char *board;
+{
+ bntp_t bntp;
+
+ memset(&bntp, 0, sizeof(bntp_t));
+ bntp.chrono = hdr->chrono;
+ strcpy(bntp.board, board);
+ strcpy(bntp.xname, hdr->xname);
+ strcpy(bntp.owner, hdr->owner);
+ strcpy(bntp.nick, hdr->nick);
+ strcpy(bntp.title, hdr->title);
+ rec_add("innd/out.bntp", &bntp, sizeof(bntp_t));
+}
+
+
+static int
+cmd_addpost(ap)
+ Agent *ap;
+{
+ char *brdname, *title, *content, *end;
+ char folder[64], fpath[64];
+ HDR hdr;
+ BRD *brd;
+ FILE *fp;
+ FILE *fpw = out_head(ap, "¤å³¹µoªí");
+
+ if (!acct_fetch(ap))
+ return HS_ERR_LOGIN;
+
+ /* b=brdname&t=title&c=content&end= */
+ if (arg_analyze(4, 0, ap->urlp, &brdname, &title, &content, &end))
+ {
+ brdname += 2; /* skip "b=" */
+ title += 2; /* skip "t=" */
+ content += 2; /* skip "c=" */
+
+ if (*brdname && *title && *content)
+ {
+ if ((brd = brd_get(brdname)) &&
+ (Ben_Perm(brd, ap->userno, ap->userid, ap->userlevel) & BRD_W_BIT))
+ {
+ brd_fpath(folder, brdname, FN_DIR);
+
+ fp = fdopen(hdr_stamp(folder, 'A', &hdr, fpath), "w");
+ fprintf(fp, "%s %s (%s) %s %s\n",
+ STR_AUTHOR1, ap->userid, ap->username,
+ STR_POST2, brdname);
+ str_ncpy(hdr.title, title, sizeof(hdr.title));
+ fprintf(fp, "¼ÐÃD: %s\n®É¶¡: %s\n\n", hdr.title, Now());
+ fprintf(fp, "%s\n", content);
+ fprintf(fp, EDIT_BANNER, ap->userid, getfromhost(&(ap->ip_addr)));
+ fclose(fp);
+
+ hdr.xmode = (brd->battr & BRD_NOTRAN) ? 0 : POST_OUTGO;
+ strcpy(hdr.owner, ap->userid);
+ strcpy(hdr.nick, ap->username);
+ rec_bot(folder, &hdr, sizeof(HDR));
+
+ brd->btime = -1;
+ if (hdr.xmode & POST_OUTGO)
+ outgo_post(&hdr, brdname);
+
+ out_reload(fpw, "±zªº¤å³¹µoªí¦¨¥\\");
+ return HS_OK;
+ }
+ return HS_ERR_BOARD;
+ }
+ }
+
+ out_reload(fpw, "±zªº¤å³¹µoªí¥¢±Ñ");
+ return HS_OK;
+}
+
+
+ /* --------------------------------------------------- */
+ /* µo°e·s«H¥ó */
+ /* --------------------------------------------------- */
+
+static int
+cmd_addmail(ap)
+ Agent *ap;
+{
+ char *userid, *title, *content, *end;
+ char folder[64], fpath[64];
+ HDR hdr;
+ FILE *fp;
+ FILE *fpw = out_head(ap, "«H¥óµo°e");
+
+ /* u=userid&t=title&c=content&end= */
+ if (arg_analyze(4, 0, ap->urlp, &userid, &title, &content, &end))
+ {
+ userid += 2; /* skip "u=" */
+ title += 2; /* skip "t=" */
+ content += 2; /* skip "c=" */
+
+ if (*userid && *title && *content && allow_userid(ap, userid))
+ {
+ usr_fpath(fpath, userid, FN_ACCT);
+ if (dashf(fpath) && acct_fetch(ap))
+ {
+ usr_fpath(folder, userid, FN_DIR);
+
+ fp = fdopen(hdr_stamp(folder, 0, &hdr, fpath), "w");
+ fprintf(fp, "%s %s (%s)\n",
+ STR_AUTHOR1, ap->userid, ap->username);
+ str_ncpy(hdr.title, title, sizeof(hdr.title));
+ fprintf(fp, "¼ÐÃD: %s\n®É¶¡: %s\n\n", hdr.title, Now());
+ fprintf(fp, "%s\n", content);
+ fprintf(fp, EDIT_BANNER, ap->userid, getfromhost(&(ap->ip_addr)));
+ fclose(fp);
+
+ strcpy(hdr.owner, ap->userid);
+ strcpy(hdr.nick, ap->username);
+ rec_add(folder, &hdr, sizeof(HDR));
+
+ out_reload(fpw, "±zªº«H¥óµo°e¦¨¥\\");
+ return HS_OK;
+ }
+ }
+ }
+
+ out_reload(fpw, "±zªº«H¥óµo°e¥¢±Ñ¡A¤]³\\¬O¦]¬°±z©|¥¼µn¤J©Î¬O¬dµL¦¹¨Ï¥ÎªÌ");
+ return HS_OK;
+}
+
+
+ /* --------------------------------------------------- */
+ /* «ü¥O¶° */
+ /* --------------------------------------------------- */
+
+static Command cmd_table_post[] =
+{
+ cmd_login, "login=&", 7,
+ cmd_addpost, "dopost=&", 8,
+ cmd_addmail, "domail=&", 8,
+
+ NULL, NULL, 0
+};
+
+
+/* ----------------------------------------------------- */
+/* close a connection & release its resource */
+/* ----------------------------------------------------- */
+
+static void
+agent_fire(ap)
+ Agent *ap;
+{
+ int csock;
+
+ csock = ap->sock;
+ if (csock > 0)
+ {
+ fcntl(csock, F_SETFL, M_NONBLOCK);
+ shutdown(csock, 2);
+
+ /* fclose(ap->fpw); */
+ close(csock);
+ }
+
+ if (ap->data)
+ free(ap->data);
+}
+
+
+/* ----------------------------------------------------- */
+/* receive request from client */
+/* ----------------------------------------------------- */
+
+static int /* >=0:mode -1:µ²§ô */
+do_cmd(ap, str, end, mode)
+ Agent *ap;
+ uschar *str, *end; /* command line ªº¶}ÀY©Mµ²§À */
+ int mode;
+{
+ int code;
+ char *ptr;
+
+ if (!(mode & (AM_GET | AM_POST)))
+ {
+ if (!str_ncmp(str, "GET ", 4)) /* str ®æ¦¡¬° GET /index.htm HTTP/1.0 */
+ {
+ mode ^= AM_GET;
+ str += 4;
+
+ if (*str != '/')
+ {
+ out_error(ap, HS_BADREQUEST);
+ return -1;
+ }
+
+ if (ptr = strchr(str, ' '))
+ {
+ *ptr = '\0';
+ str_ncpy(ap->url, str + 1, sizeof(ap->url));
+ }
+ else
+ {
+ *ap->url = '\0';
+ }
+ }
+ else if (!str_ncmp(str, "POST ", 5)) /* str ®æ¦¡¬° POST /dopost?sysop HTTP/1.0 */
+ {
+ mode ^= AM_POST;
+ }
+ }
+ else
+ {
+ if (*str) /* ¤£¬OªÅ¦æ¡GÀÉÀY */
+ {
+ /* ¤ÀªR Cookie */
+ if (!str_ncmp(str, "Cookie: user=", 13))
+ str_ncpy(ap->cookie, str + 13, LEN_COOKIE);
+
+ /* ¤ÀªR If-Modified-Since */
+ if ((mode & AM_GET) && !str_ncmp(str, "If-Modified-Since: ", 19)) /* str ®æ¦¡¬° If-Modified-Since: Sat, 29 Oct 1994 19:43:31 GMT */
+ str_ncpy(ap->modified, str + 19, sizeof(ap->modified));
+ }
+ else /* ªÅ¦æ */
+ {
+ Command *cmd;
+ char *url;
+
+ if (mode & AM_GET)
+ {
+ cmd = cmd_table_get;
+ url = ap->url;
+ }
+ else /* if (mode & AM_POST) */
+ {
+ cmd = cmd_table_post;
+ /* ¦b AM_POST ®É¡AªÅ¦æªº¤U¤@¦æ¬O POST ªº¤º®e */
+ for (url = end + 1; *url == '\r' || *url == '\n'; url++) /* §ä¤U¤@¦æ */
+ ;
+ }
+
+ for (; ptr = cmd->cmd; cmd++)
+ {
+ if (!str_ncmp(url, ptr, cmd->len))
+ break;
+ }
+
+ /* ¦pªG¦b command_table ¸Ì­±§ä¤£¨ì¡A¨º»ò¦Û°Ê­«·s¾É¦V */
+ if (!ptr)
+ {
+ out_http(ap, HS_REDIRECT, NULL);
+ return -1;
+ }
+
+ ap->urlp = url + cmd->len;
+
+ code = (*cmd->func) (ap);
+ if (code != HS_OK)
+ {
+ if (code != HS_END)
+ out_error(ap, code);
+ if (code < HS_OK)
+ out_tail(ap->fpw);
+ }
+ return -1;
+ }
+ }
+
+ return mode;
+}
+
+
+static int
+agent_recv(ap)
+ Agent *ap;
+{
+ int cc, mode, size, used;
+ uschar *data, *head;
+
+ used = ap->used;
+ data = ap->data;
+
+ if (used > 0)
+ {
+ /* check the available space */
+
+ size = ap->size;
+ cc = size - used;
+
+ if (cc < TCP_RCVSIZ + 3)
+ {
+ if (size < MAX_DATA_SIZE)
+ {
+ size += TCP_RCVSIZ + (size >> 2);
+
+ if (data = (uschar *) realloc(data, size))
+ {
+ ap->data = data;
+ ap->size = size;
+ }
+ else
+ {
+#ifdef LOG_VERBOSE
+ fprintf(flog, "ERROR\trealloc: %d\n", size);
+#endif
+ return 0;
+ }
+ }
+ else
+ {
+#ifdef LOG_VERBOSE
+ fprintf(flog, "WARN\tdata too long\n");
+#endif
+ return 0;
+ }
+ }
+ }
+
+ head = data + used;
+ cc = recv(ap->sock, head, TCP_RCVSIZ, 0);
+
+ if (cc <= 0)
+ {
+ cc = errno;
+ if (cc != EWOULDBLOCK)
+ {
+#ifdef LOG_VERBOSE
+ fprintf(flog, "RECV\t%s\n", strerror(cc));
+#endif
+ return 0;
+ }
+
+ /* would block, so leave it to do later */
+
+ return -1;
+ }
+
+ head[cc] = '\0';
+ ap->used = (used += cc);
+
+ /* itoc.050807: recv() ¤@¦¸ÁÙŪ¤£§¹ªº¡A¤@©w¬O cmd_dopost ©Î cmd_domail¡A³o¤GªÌªºµ²§ô³£¦³ &end= */
+ if (used >= TCP_RCVSIZ)
+ {
+ /* ¦h -2 ¬O¦]¬°¦³¨ÇÂsÄý¾¹·|¦Û°Ê¸É¤W \r\n */
+ if (!strstr(head + cc - strlen("&end=") - 2, "&end=")) /* ÁÙ¨SŪ§¹¡AÄ~ÄòŪ */
+ return 1;
+ }
+
+ mode = 0;
+ head = data;
+
+ while (cc = *head)
+ {
+ if (cc == '\n')
+ {
+ data++;
+ }
+ else if (cc == '\r')
+ {
+ *head = '\0';
+
+ if ((mode = do_cmd(ap, data, head, mode)) < 0)
+ {
+ fflush(ap->fpw); /* do_cmd() ¦^¶Ç -1 ªí¥Üµ²§ô¡A´N fflush ©Ò¦³µ²ªG */
+ return 0;
+ }
+
+ data = head + 1;
+ }
+ head++;
+ }
+
+ return 0;
+}
+
+
+/* ----------------------------------------------------- */
+/* accept a new connection */
+/* ----------------------------------------------------- */
+
+static int
+agent_accept(ipaddr)
+ unsigned int *ipaddr;
+{
+ int csock;
+ int value;
+ struct sockaddr_in csin;
+
+ for (;;)
+ {
+ value = sizeof(csin);
+ csock = accept(0, (struct sockaddr *) & csin, &value);
+ /* if (csock > 0) */
+ if (csock >= 0) /* Thor.000126: more proper */
+ break;
+
+ csock = errno;
+ if (csock != EINTR)
+ {
+#ifdef LOG_VERBOSE
+ fprintf(flog, "ACCEPT\t%s\n", strerror(csock));
+#endif
+ return -1;
+ }
+
+ while (waitpid(-1, NULL, WNOHANG | WUNTRACED) > 0);
+ }
+
+ value = 1;
+ /* Thor.000511: µù¸Ñ: don't delay send to coalesce(Áp¦X) packets */
+ setsockopt(csock, IPPROTO_TCP, TCP_NODELAY, (char *)&value, sizeof(value));
+
+ *ipaddr = csin.sin_addr.s_addr;
+
+ return csock;
+}
+
+
+/* ----------------------------------------------------- */
+/* signal routines */
+/* ----------------------------------------------------- */
+
+#ifdef SERVER_USAGE
+static void
+servo_usage()
+{
+ struct rusage ru;
+
+ if (getrusage(RUSAGE_SELF, &ru))
+ return;
+
+ fprintf(flog, "\n[Server Usage]\n\n"
+ " user time: %.6f\n"
+ " system time: %.6f\n"
+ " maximum resident set size: %lu P\n"
+ " integral resident set size: %lu\n"
+ " page faults not requiring physical I/O: %d\n"
+ " page faults requiring physical I/O: %d\n"
+ " swaps: %d\n"
+ " block input operations: %d\n"
+ " block output operations: %d\n"
+ " messages sent: %d\n"
+ " messages received: %d\n"
+ " signals received: %d\n"
+ " voluntary context switches: %d\n"
+ " involuntary context switches: %d\n",
+
+ (double)ru.ru_utime.tv_sec + (double)ru.ru_utime.tv_usec / 1000000.0,
+ (double)ru.ru_stime.tv_sec + (double)ru.ru_stime.tv_usec / 1000000.0,
+ ru.ru_maxrss,
+ ru.ru_idrss,
+ ru.ru_minflt,
+ ru.ru_majflt,
+ ru.ru_nswap,
+ ru.ru_inblock,
+ ru.ru_oublock,
+ ru.ru_msgsnd,
+ ru.ru_msgrcv,
+ ru.ru_nsignals,
+ ru.ru_nvcsw,
+ ru.ru_nivcsw);
+
+ fflush(flog);
+}
+#endif
+
+
+static void
+sig_term(sig)
+ int sig;
+{
+ char buf[80];
+
+ sprintf(buf, "sig: %d, errno: %d => %s", sig, errno, strerror(errno));
+ logit("EXIT", buf);
+ fclose(flog);
+ exit(0);
+}
+
+
+static void
+reaper()
+{
+ while (waitpid(-1, NULL, WNOHANG | WUNTRACED) > 0);
+}
+
+
+static void
+servo_signal()
+{
+ struct sigaction act;
+
+ /* sigblock(sigmask(SIGPIPE)); *//* Thor.981206: ²Î¤@ POSIX ¼Ð·Ç¥Îªk */
+
+ /* act.sa_mask = 0; *//* Thor.981105: ¼Ð·Ç¥Îªk */
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+
+ act.sa_handler = sig_term;
+ sigaction(SIGTERM, &act, NULL); /* forced termination */
+ sigaction(SIGSEGV, &act, NULL); /* if rlimit violate */
+ sigaction(SIGBUS, &act, NULL);
+
+#if 1 /* Thor.990203: §ì signal */
+ sigaction(SIGURG, &act, NULL);
+ sigaction(SIGXCPU, &act, NULL);
+ sigaction(SIGXFSZ, &act, NULL);
+
+#ifdef SOLARIS
+ sigaction(SIGLOST, &act, NULL);
+ sigaction(SIGPOLL, &act, NULL);
+ sigaction(SIGPWR, &act, NULL);
+#endif
+
+#ifdef LINUX
+ sigaction(SIGSYS, &act, NULL);
+ /* sigaction(SIGEMT, &act, NULL); */
+ /* itoc.010317: §Úªº linux ¨S¦³³o­Ó»¡ :p */
+#endif
+
+ sigaction(SIGFPE, &act, NULL);
+ sigaction(SIGWINCH, &act, NULL);
+ sigaction(SIGINT, &act, NULL);
+ sigaction(SIGQUIT, &act, NULL);
+ sigaction(SIGILL, &act, NULL);
+ sigaction(SIGTRAP, &act, NULL);
+ sigaction(SIGABRT, &act, NULL);
+ sigaction(SIGTSTP, &act, NULL);
+ sigaction(SIGTTIN, &act, NULL);
+ sigaction(SIGTTOU, &act, NULL);
+ sigaction(SIGVTALRM, &act, NULL);
+#endif
+
+ sigaction(SIGHUP, &act, NULL);
+
+ act.sa_handler = reaper;
+ sigaction(SIGCHLD, &act, NULL);
+
+#ifdef SERVER_USAGE
+ act.sa_handler = servo_usage;
+ sigaction(SIGPROF, &act, NULL);
+#endif
+
+ /* Thor.981206: lkchu patch: ²Î¤@ POSIX ¼Ð·Ç¥Îªk */
+ /* ¦b¦¹­É¥Î sigset_t act.sa_mask */
+ sigaddset(&act.sa_mask, SIGPIPE);
+ sigprocmask(SIG_BLOCK, &act.sa_mask, NULL);
+}
+
+
+/* ----------------------------------------------------- */
+/* server core routines */
+/* ----------------------------------------------------- */
+
+static void
+servo_daemon(inetd)
+ int inetd;
+{
+ int fd, value;
+ char buf[80];
+ struct linger ld;
+ struct sockaddr_in sin;
+
+#ifdef HAVE_RLIMIT
+ struct rlimit limit;
+#endif
+
+ /* 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((time_t *) & value);
+ gmtime((time_t *) & value);
+ strftime(buf, 80, "%d/%b/%Y:%H:%M:%S", localtime((time_t *) & value));
+
+#ifdef HAVE_RLIMIT
+ /* --------------------------------------------------- */
+ /* adjust the resource limit */
+ /* --------------------------------------------------- */
+
+ getrlimit(RLIMIT_NOFILE, &limit);
+ limit.rlim_cur = limit.rlim_max;
+ setrlimit(RLIMIT_NOFILE, &limit);
+
+ limit.rlim_cur = limit.rlim_max = 16 * 1024 * 1024;
+ setrlimit(RLIMIT_FSIZE, &limit);
+
+ limit.rlim_cur = limit.rlim_max = 16 * 1024 * 1024;
+ setrlimit(RLIMIT_DATA, &limit);
+
+#ifdef SOLARIS
+#define RLIMIT_RSS RLIMIT_AS /* Thor.981206: port for solaris 2.6 */
+#endif
+
+ setrlimit(RLIMIT_RSS, &limit);
+
+ limit.rlim_cur = limit.rlim_max = 0;
+ setrlimit(RLIMIT_CORE, &limit);
+#endif
+
+ /* --------------------------------------------------- */
+ /* detach daemon process */
+ /* --------------------------------------------------- */
+
+ close(1);
+ close(2);
+
+ if (inetd)
+ return;
+
+ close(0);
+
+ if (fork())
+ exit(0);
+
+ setsid();
+
+ if (fork())
+ exit(0);
+
+ /* --------------------------------------------------- */
+ /* setup socket */
+ /* --------------------------------------------------- */
+
+ fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+
+ value = 1;
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&value, sizeof(value));
+
+ ld.l_onoff = ld.l_linger = 0;
+ setsockopt(fd, SOL_SOCKET, SO_LINGER, (char *)&ld, sizeof(ld));
+
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(BHTTP_PORT);
+ sin.sin_addr.s_addr = htonl(INADDR_ANY);
+ memset((char *)&sin.sin_zero, 0, sizeof(sin.sin_zero));
+
+ if (bind(fd, (struct sockaddr *) & sin, sizeof(sin)) ||
+ listen(fd, TCP_BACKLOG))
+ exit(1);
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int n, sock, cc;
+ time_t uptime, tcheck, tfresh;
+ Agent **FBI, *Scully, *Mulder, *agent;
+ fd_set rset;
+ struct timeval tv;
+
+ cc = 0;
+
+ while ((n = getopt(argc, argv, "i")) != -1)
+ {
+ switch (n)
+ {
+ case 'i':
+ cc = 1;
+ break;
+
+ default:
+ fprintf(stderr, "Usage: %s [options]\n"
+ "\t-i start from inetd with wait option\n",
+ argv[0]);
+ exit(0);
+ }
+ }
+
+ servo_daemon(cc);
+
+ setgid(BBSGID);
+ setuid(BBSUID);
+ chdir(BBSHOME);
+
+ init_bshm();
+ init_ushm();
+ init_fshm();
+
+ guest_userno();
+
+ servo_signal();
+
+ log_open();
+ dns_init();
+
+ uptime = time(0);
+ tcheck = uptime + BHTTP_PERIOD;
+ tfresh = uptime + BHTTP_FRESH;
+
+ Scully = Mulder = NULL;
+
+ for (;;)
+ {
+ /* maintain : resource and garbage collection */
+
+ uptime = time(0);
+ if (tcheck < uptime)
+ {
+ /* ----------------------------------------------- */
+ /* ±N¹L¤[¨S¦³°Ê§@ªº agent ½ð°£ */
+ /* ----------------------------------------------- */
+
+ tcheck = uptime - BHTTP_TIMEOUT;
+
+ for (FBI = &Scully; agent = *FBI;)
+ {
+ if (agent->uptime < tcheck)
+ {
+ agent_fire(agent);
+
+ *FBI = agent->anext;
+
+ agent->anext = Mulder;
+ Mulder = agent;
+ }
+ else
+ {
+ FBI = &(agent->anext);
+ }
+ }
+
+ /* ----------------------------------------------- */
+ /* maintain server log */
+ /* ----------------------------------------------- */
+
+ if (tfresh < uptime)
+ {
+ tfresh = uptime + BHTTP_FRESH;
+#ifdef SERVER_USAGE
+ servo_usage();
+#endif
+ log_fresh();
+ }
+ else
+ {
+ fflush(flog);
+ }
+
+ tcheck = uptime + BHTTP_PERIOD;
+ }
+
+ /* ------------------------------------------------- */
+ /* Set up the fdsets */
+ /* ------------------------------------------------- */
+
+ FD_ZERO(&rset);
+ FD_SET(0, &rset);
+
+ n = 0;
+ for (agent = Scully; agent; agent = agent->anext)
+ {
+ sock = agent->sock;
+
+ if (n < sock)
+ n = sock;
+
+ FD_SET(sock, &rset);
+ }
+
+ /* in order to maintain history, timeout every BHTTP_PERIOD seconds in case no connections */
+ tv.tv_sec = BHTTP_PERIOD;
+ tv.tv_usec = 0;
+ if (select(n + 1, &rset, NULL, NULL, &tv) <= 0)
+ continue;
+
+ /* ------------------------------------------------- */
+ /* serve active agents */
+ /* ------------------------------------------------- */
+
+ uptime = time(0);
+
+ for (FBI = &Scully; agent = *FBI;)
+ {
+ sock = agent->sock;
+
+ if (FD_ISSET(sock, &rset))
+ cc = agent_recv(agent);
+ else
+ cc = -1;
+
+ if (cc == 0)
+ {
+ agent_fire(agent);
+
+ *FBI = agent->anext;
+
+ agent->anext = Mulder;
+ Mulder = agent;
+
+ continue;
+ }
+
+ if (cc > 0) /* ÁÙ¦³¸ê®Æ­n recv */
+ agent->uptime = uptime;
+
+ FBI = &(agent->anext);
+ }
+
+ /* ------------------------------------------------- */
+ /* serve new connection */
+ /* ------------------------------------------------- */
+
+ /* Thor.000209: ¦Ò¼{²¾«e¦¹³¡¤À, §K±o¥d¦b accept() */
+ if (FD_ISSET(0, &rset))
+ {
+ unsigned int ip_addr;
+
+ sock = agent_accept(&ip_addr);
+ if (sock > 0)
+ {
+ Agent *anext;
+
+ if (agent = Mulder)
+ {
+ anext = agent->anext;
+ }
+ else
+ {
+ if (!(agent = (Agent *) malloc(sizeof(Agent))))
+ {
+ fcntl(sock, F_SETFL, M_NONBLOCK);
+ shutdown(sock, 2);
+ close(sock);
+
+#ifdef LOG_VERBOSE
+ fprintf(flog, "ERROR\tNot enough space in main()\n");
+#endif
+ continue;
+ }
+ anext = NULL;
+ }
+
+ /* variable initialization */
+
+ memset(agent, 0, sizeof(Agent));
+
+ agent->sock = sock;
+ agent->tbegin = agent->uptime = uptime;
+
+ agent->ip_addr = ip_addr;
+
+ if (!(agent->data = (char *) malloc(MIN_DATA_SIZE)))
+ {
+ agent_fire(agent);
+#ifdef LOG_VERBOSE
+ fprintf(flog, "ERROR\tNot enough space in agent->data\n");
+#endif
+ continue;
+ }
+ agent->size = MIN_DATA_SIZE;
+ agent->used = 0;
+
+ agent->fpw = fdopen(sock, "w");
+
+ Mulder = anext;
+ *FBI = agent;
+ }
+ }
+
+ /* ------------------------------------------------- */
+ /* tail of main loop */
+ /* ------------------------------------------------- */
+ }
+
+ logit("EXIT", "shutdown");
+ fclose(flog);
+
+ exit(0);
+}
diff --git a/daemon/bmtad.c b/daemon/bmtad.c
new file mode 100644
index 0000000..f51affe
--- /dev/null
+++ b/daemon/bmtad.c
@@ -0,0 +1,4004 @@
+/*-------------------------------------------------------*/
+/* util/bmtad.c ( NTHU CS MapleBBS Ver 3.00 ) */
+/*-------------------------------------------------------*/
+/* target : Mail Transport Agent for BBS */
+/* create : 96/11/20 */
+/* update : 96/12/15 */
+/*-------------------------------------------------------*/
+/* syntax : bmtad */
+/*-------------------------------------------------------*/
+/* notice : brdshm (board shared memory) synchronize */
+/*-------------------------------------------------------*/
+
+
+#define FORGE_CHECK
+#undef SMTP_CONN_CHECK
+#define HELO_CHECK
+
+#define ANTI_HTMLMAIL /* itoc.021014: ¾× html_mail */
+#define ANTI_NOTMYCHARSETMAIL /* itoc.030513: ¾× not-mycharset mail */
+
+
+#include "bbs.h"
+
+
+/* Thor.990221: ¾¨¶q fflush ¥X log */
+#undef DEBUG
+
+#define ADM_ALIASES {"root", "mailer-daemon", NULL}
+
+
+#include <sys/wait.h>
+#include <netinet/tcp.h>
+#include <sys/resource.h>
+
+
+#define SERVER_USAGE
+#define WATCH_DOG
+
+
+#define BMTA_PIDFILE "run/bmta.pid"
+/* #define BMTA_LOGFILE "run/bmta.log" */ /* ·h¥h global.h */
+#define BMTA_DEBUGFILE "run/bmta.debug"
+
+
+#define BMTA_PERIOD (60 * 15) /* ¨C 15 ¤ÀÄÁ check ¤@¦¸ */
+#define BMTA_TIMEOUT (60 * 30) /* ¶W¹L 30 ¤ÀÄÁªº³s½u´Nµø¬°¿ù»~ */
+#define BMTA_FRESH 86400 /* ¨C 1 ¤Ñ¾ã²z¤@¦¸ */
+#define BMTA_FAULT 100
+
+
+#define TCP_BACKLOG 3
+#define TCP_BUFSIZ 4096
+#define TCP_LINSIZ 256
+#define TCP_RCVSIZ 2048
+
+
+#define MIN_DATA_SIZE 8000
+#define MAX_DATA_SIZE 262143 /* ¨C¤@«Ê«Hªº¤j¤p­­¨î(byte) */
+#define MAX_CMD_LEN 1024
+#define MAX_RCPT 7 /* ¦P¤@«H¦³¶W¹L 7 ­Ó¦¬«HªÌ´N¾×±¼ */
+#define MAX_HOST_CONN 2
+
+
+#define SPAM_MHOST_LIMIT 1000 /* ¦P¤@­Ó @host ±H¶i¨Ó¶W¹L 1000 «Ê«H¡A´N±N¦¹ @host µø¬°¼s§i°Ó */
+#define SPAM_MFROM_LIMIT 100 /* ¦P¤@­Ó from ±H¶i¨Ó¶W¹L 100 «Ê«H¡A´N±N¦¹ from µø¬°¼s§i°Ó */
+
+#define SPAM_TITLE_LIMIT 50 /* ¦P¤@­Ó¼ÐÃD±H¶i¨Ó¶W¹L 50 ¦¸´N¯S§O°O¿ý */
+#define SPAM_FORGE_LIMIT 10 /* ¦P¤@­Ó @domain ¿ù 10 ¦¸¥H¤W¡A´N»{©w¤£¬Oµ§»~¡A¦Ó¬O¬G·Nªº */
+
+
+/* Thor.000425: POSIX ¥Î O_NONBLOCK */
+
+#ifndef O_NONBLOCK
+#define M_NONBLOCK FNDELAY
+#else
+#define M_NONBLOCK O_NONBLOCK
+#endif
+
+/* ----------------------------------------------------- */
+/* SMTP commands */
+/* ----------------------------------------------------- */
+
+
+typedef struct
+{
+ void (*func) ();
+ char *cmd;
+ char *help;
+} Command;
+
+
+/* ----------------------------------------------------- */
+/* client connection structure */
+/* ----------------------------------------------------- */
+
+
+typedef struct RCPT
+{
+ struct RCPT *rnext;
+ char userid[0];
+} RCPT;
+
+
+typedef struct Agent
+{
+ struct Agent *anext;
+ int sock;
+ int sno;
+ int state;
+ int mode;
+ int letter; /* 1:±Hµ¹ *.bbs@ 0:±Hµ¹ *.brd@ */
+ u_long ip_addr;
+
+ time_t uptime;
+ time_t tbegin;
+
+ int xsize;
+ int xrcpt;
+ int xdata;
+ int xerro;
+ int xspam;
+
+ char ident[80];
+ char memo[80];
+ char fpath[80];
+
+ char from[80];
+ char title[80];
+
+ char addr[80];
+ char nick[256]; /* Thor.000131: ¦³ªº¤HµoºÆµ¹¤Óªø */
+
+ int nrcpt; /* number of rcpt */
+ RCPT *rcpt;
+
+ char *data;
+ int used;
+ int size;
+} Agent;
+
+
+static int servo_sno;
+
+
+/* ----------------------------------------------------- */
+/* connection state */
+/* ----------------------------------------------------- */
+
+
+#define CS_FREE 0x00
+#define CS_RECV 0x01
+#define CS_REPLY 0x02
+#define CS_SEND 0x03
+#define CS_FLUSH 0x04 /* flush data and quit */
+
+
+/* ----------------------------------------------------- */
+/* AM : Agent Mode */
+/* ----------------------------------------------------- */
+
+
+#define AM_VALID 0x001 /* ¦¬¥ó¤H¬O bbsreg@MYHOSTNAME */
+#define AM_BBSADM 0x002 /* ¦¬¥ó¤H¬O ADM_ALIASES@MYHOSTNAME */
+
+#define AM_DROP 0x010 /* swallow command */
+#define AM_SWALLOW 0x020 /* swallow data */
+#define AM_DATA 0x040 /* data mode */
+
+#define AM_SPAM 0x100
+
+#define AM_HELO 0x200 /* HELOed */
+
+#ifdef DEBUG
+#define AM_DEBUG 0x400 /* for tracing malicious connection */
+#endif
+
+/* ----------------------------------------------------- */
+/* operation log and debug information */
+/* ----------------------------------------------------- */
+/* @START | ... | time */
+/* @CONN | [sno] ident | time */
+/* ----------------------------------------------------- */
+
+
+static FILE *flog;
+static int gline;
+static char gtext[100];
+
+
+#ifdef WATCH_DOG
+#define MYDOG gline = __LINE__
+#else
+#define MYDOG /* NOOP */
+#endif
+
+
+extern int errno;
+extern char *crypt();
+
+
+static void
+log_fresh()
+{
+ int count;
+ char fsrc[64], fdst[64];
+ char *fpath = BMTA_LOGFILE;
+
+ if (flog)
+ fclose(flog);
+
+ count = 9;
+ do
+ {
+ sprintf(fdst, "%s.%d", fpath, count);
+ sprintf(fsrc, "%s.%d", fpath, --count);
+ rename(fsrc, fdst);
+ } while (count);
+
+ rename(fpath, fsrc);
+ flog = fopen(fpath, "a");
+}
+
+
+static void
+logit(key, msg)
+ char *key;
+ char *msg;
+{
+ time_t now;
+ struct tm *p;
+
+ time(&now);
+ p = localtime(&now);
+ /* Thor.990329: y2k */
+ fprintf(flog, "%s\t%s\t%02d/%02d/%02d %02d:%02d:%02d\n",
+ key, msg, p->tm_year % 100, p->tm_mon + 1, p->tm_mday,
+ p->tm_hour, p->tm_min, p->tm_sec);
+
+#ifdef DEBUG
+ fflush(flog);
+#endif
+}
+
+
+static void
+log_open()
+{
+ FILE *fp;
+
+ umask(077);
+
+ if (fp = fopen(BMTA_PIDFILE, "w"))
+ {
+ fprintf(fp, "%d\n", getpid());
+ fclose(fp);
+ }
+
+ flog = fopen(BMTA_LOGFILE, "a");
+ logit("START", "MTA daemon");
+}
+
+
+static inline void
+agent_log(ap, key, msg)
+ Agent *ap;
+ char *key;
+ char *msg;
+{
+ fprintf(flog, "%s\t[%d] %s\n", key, ap->sno, msg);
+
+#ifdef DEBUG
+ fflush(flog);
+#endif
+}
+
+
+static void
+agent_reply(ap, msg)
+ Agent *ap;
+ char *msg;
+{
+ int cc;
+ char *base, *head;
+
+ head = base = ap->data;
+ while (cc = *msg++)
+ {
+ *head++ = cc;
+ }
+ *head++ = '\r';
+ *head++ = '\n';
+ ap->used = head - base;
+ ap->state = CS_SEND;
+}
+
+
+/* ----------------------------------------------------- */
+/* server side routines */
+/* ----------------------------------------------------- */
+
+
+#ifdef EMAIL_JUSTIFY
+static int
+is_badid(userid)
+ char *userid;
+{
+ int ch;
+ char *str;
+
+ if (strlen(userid) < 2)
+ return 1;
+
+ if (!is_alpha(*userid))
+ return 1;
+
+ str = userid;
+ while (ch = *(++str))
+ {
+ if (!is_alnum(ch))
+ return 1;
+ }
+ return 0;
+}
+
+
+static int
+acct_fetch(userid, acct)
+ char *userid;
+ ACCT *acct;
+{
+ int fd;
+ char fpath[64];
+
+ if (is_badid(userid))
+ return -1;
+
+ usr_fpath(fpath, userid, FN_ACCT);
+ fd = open(fpath, O_RDWR, 0600);
+ if (fd >= 0)
+ {
+ if (read(fd, acct, sizeof(ACCT)) != sizeof(ACCT))
+ {
+ close(fd);
+ fd = -1;
+ }
+ }
+ return fd;
+}
+#endif
+
+
+/* ----------------------------------------------------- */
+/* board¡Gshm ³¡¥÷¶·»P cache.c ¬Û®e */
+/* ----------------------------------------------------- */
+
+
+static BCACHE *bshm;
+
+
+static void
+init_bshm()
+{
+ /* itoc.030727: ¦b¶}±Ò bbsd ¤§«e¡AÀ³¸Ó´N­n°õ¦æ¹L account¡A
+ ©Ò¥H bshm À³¸Ó¤w³]©w¦n */
+
+ bshm = shm_new(BRDSHM_KEY, sizeof(BCACHE));
+
+ if (bshm->uptime <= 0) /* bshm ¥¼³]©w§¹¦¨ */
+ exit(0);
+}
+
+
+static BRD *
+brd_get(bname)
+ char *bname;
+{
+ BRD *bhdr, *tail;
+
+ bhdr = bshm->bcache;
+ tail = bhdr + bshm->number;
+ do
+ {
+ if (!str_cmp(bname, bhdr->brdname))
+ return bhdr;
+ } while (++bhdr < tail);
+ return NULL;
+}
+
+
+static int /* 1: §ä¨ì¤F³o­ÓªO¡A¥BªO¦W¦b brdname */
+getbrdname(brdname)
+ char *brdname;
+{
+ BRD *brd;
+
+ if (brd = brd_get(brdname))
+ {
+ strcpy(brdname, brd->brdname); /* ´«¦¨¥¿½Tªº¤j¤p¼g */
+ return 1;
+ }
+ return 0;
+}
+
+
+static void
+update_btime(brdname)
+ char *brdname;
+{
+ BRD *brd;
+
+ if (brd = brd_get(brdname))
+ brd->btime = -1;
+}
+
+
+/* ----------------------------------------------------- */
+/* user¡Gshm ³¡¥÷¶·»P cache.c ¬Û®e */
+/* ----------------------------------------------------- */
+
+
+static UCACHE *ushm;
+
+
+static inline void
+init_ushm()
+{
+ ushm = shm_new(UTMPSHM_KEY, sizeof(UCACHE));
+}
+
+
+static inline void
+bbs_biff(userno)
+ int userno;
+{
+ UTMP *utmp, *uceil;
+ usint offset;
+
+ offset = ushm->offset;
+ if (offset > (MAXACTIVE - 1) * sizeof(UTMP)) /* Thor.980805: ¤£µMcall¤£¨ì */
+ offset = (MAXACTIVE - 1) * sizeof(UTMP);
+
+ utmp = ushm->uslot;
+ uceil = (void *) utmp + offset;
+
+ do
+ {
+ if (utmp->userno == userno)
+ utmp->status |= STATUS_BIFF;
+ } while (++utmp <= uceil);
+}
+
+
+/* ----------------------------------------------------- */
+/* Hash Table */
+/* ----------------------------------------------------- */
+
+
+#define HASH_TABLE_SIZE 256
+#define HASH_TABLE_SEED 101
+
+
+typedef struct HashEntry
+{
+ struct HashEntry *next;
+ usint hv; /* hashing value */
+ time_t uptime;
+ int visit; /* reference counts */
+ int score;
+ int fsize; /* file size (¥u¦³¦b title_ht ¤~¦³¥Î) */
+ struct HashEntry *ttl; /* title (¥u¦³¦b mfrom_ht ¤~¦³¥Î) */
+ char key[0];
+} HashEntry;
+
+
+typedef struct
+{
+ int mask;
+ int keylen; /* 0 : string */
+ int tale;
+ int leak;
+ int (*comp) (const void *k1, const void *k2, int len);
+ int (*hash) (const void *key, int len);
+ HashEntry *bucket[0];
+} HashTable;
+
+
+static int
+he_hash(key, len)
+ const unsigned char *key;
+ int len; /* 0 : string */
+{
+ usint seed, shft;
+
+ seed = HASH_TABLE_SEED;
+ shft = 0;
+
+ if (len > 0)
+ {
+ while (len-- > 0)
+ {
+ seed += (*key++) << shft;
+ shft = (shft + 1) & 7;
+ }
+ }
+ else
+ {
+ while (len = *key)
+ {
+ key++;
+ seed += len << shft;
+ shft = (shft + 1) & 7;
+ }
+ }
+
+ return seed;
+}
+
+
+static HashTable *
+ht_new(size, keylen)
+ int size; /* 2's power */
+ int keylen; /* 0 : key is string */
+{
+ HashTable *ht;
+ int he_len;
+
+ if (size <= 0)
+ size = HASH_TABLE_SIZE;
+
+ he_len = size * sizeof(HashEntry *);
+ if (ht = (HashTable *) malloc(sizeof(HashTable) + he_len))
+ {
+ ht->mask = size - 1;
+ ht->keylen = keylen;
+ ht->tale = 0;
+ ht->leak = 0;
+ if (keylen)
+ {
+ ht->hash = he_hash;
+ ht->comp = (void *) memcmp;
+
+ }
+ else
+ {
+ ht->hash = (void *) hash32;
+ ht->comp = (void *) str_cmp;
+ }
+
+ memset(ht->bucket, 0, he_len);
+ }
+ return ht;
+}
+
+
+#if 0
+static void
+ht_free(ht)
+ HashTable *ht;
+{
+ int i, len;
+ HashEntry *node, *next;
+
+ len = ht->keylen;
+ for (i = ht->mask; i >= 0; i--)
+ {
+ node = ht->bucket[i];
+ while (node)
+ {
+ next = node->next;
+ if (len > 0)
+ free(node->ttl);
+ free(node);
+ node = next;
+ }
+ }
+
+ free(ht);
+}
+
+
+static void
+ht_apply(ht, func)
+ HashTable *ht;
+ int (*func) (const HashEntry * he);
+{
+ int i, len;
+ HashEntry *he, **hp;
+
+ len = ht->len;
+ for (i = ht->mask; i >= 0; i--)
+ {
+ hp = &(ht->bucket[i]);
+ while (he = *hp)
+ {
+ if (func(he) < 0) /* unlink this entry */
+ {
+ *hp = he->next;
+ if (len > 0)
+ free(he->ttl);
+ free(he);
+ ht->tale--;
+ }
+ else
+ {
+ hp = &(he->next);
+ }
+ }
+ }
+}
+
+
+static HashEntry *
+ht_look(ht, key)
+ HashTable *ht;
+ const void *key;
+{
+ int len;
+ usint hv;
+ HashEntry *he;
+ int (*comp) ();
+
+ len = ht->keylen;
+ comp = ht->comp;
+ hv = ht->hash(key, len);
+ he = ht->bucket[hv & (ht->mask)];
+ while (he)
+ {
+ if (hv == he->hv && !comp(key, he->key, len))
+ break;
+ he = he->next;
+ }
+ return he;
+}
+#endif
+
+
+static HashEntry *
+ht_add(ht, key)
+ HashTable *ht;
+ const void *key;
+{
+ HashEntry *he, **hp;
+ int len;
+ usint hv;
+ int (*comp) ();
+
+ len = ht->keylen;
+ comp = ht->comp;
+ hv = ht->hash(key, len);
+ hp = &(ht->bucket[hv & (ht->mask)]);
+
+ for (;;)
+ {
+ he = *hp;
+ if (he == NULL)
+ {
+ if (len == 0)
+ len = strlen(key) + 1;
+ if (he = (HashEntry *) malloc(sizeof(HashEntry) + len))
+ {
+ *hp = he;
+ he->hv = hv;
+ he->next = NULL;
+ he->visit = 0;
+ he->score = 0;
+ he->fsize = 0;
+ he->ttl = NULL;
+ memcpy(he->key, key, len);
+ ht->tale++;
+ ht->leak++;
+ }
+ break;
+ }
+
+ if (hv == he->hv && !comp(key, he->key, len))
+ break;
+
+ hp = &(he->next);
+ }
+
+ he->visit++;
+ return he;
+}
+
+
+static void
+ht_expire(ht, expire)
+ HashTable *ht;
+ time_t expire;
+{
+ int i, delta, tale, score;
+ HashEntry *he, **hp;
+
+ tale = ht->tale;
+ delta = 2 + (ht->leak >> 4) + (tale >> 6);
+ ht->leak = 0;
+
+ for (i = ht->mask; i >= 0; i--)
+ {
+ hp = &(ht->bucket[i]);
+ while (he = *hp)
+ {
+ if (he->uptime < expire)
+ {
+ score = he->score - delta;
+ if (score <= 0 && he->visit <= 0) /* unlink this entry */
+ {
+ *hp = he->next;
+ free(he);
+ tale--;
+ continue;
+ }
+
+ if (score < 0)
+ score = 0;
+ he->score -= score;
+ }
+
+ hp = &(he->next);
+ }
+ }
+
+ ht->tale = tale;
+}
+
+
+/* ----------------------------------------------------- */
+/* Host Hash Table */
+/* ----------------------------------------------------- */
+
+
+#define HOST_HASH_TABLE_SIZE 256
+#define HOST_HASH_ENTRY_LIFE (30 * 60)
+#define HOST_HASH_ENTRY_DELTA 8
+
+
+typedef union
+{
+ unsigned long addr;
+ unsigned char ipv4[4];
+} HostAddr;
+
+
+typedef struct HostHashEntry
+{
+ unsigned long hostaddr;
+ struct HostHashEntry *next;
+ time_t uptime;
+ int ref;
+ int hflag;
+ char hostname[0];
+} HostHashEntry;
+
+
+static HostHashEntry *HostHashTable[HOST_HASH_TABLE_SIZE];
+
+
+static int
+hht_look(addr, host)
+ HostAddr *addr;
+ char *host;
+{
+ int hv;
+ HostHashEntry *he;
+
+ /* hash value of 140.114.87.5 : 114 ^ 87 ^ 5 */
+
+ hv = (addr->ipv4[1] ^ addr->ipv4[2] ^ addr->ipv4[3]) &
+ (HOST_HASH_TABLE_SIZE - 1);
+
+ he = HostHashTable[hv];
+
+ for (;;)
+ {
+ if (!he)
+ {
+ int hflag, len;
+
+ hflag = dns_name((char *)addr, host);
+ len = strlen(host) + 1;
+ he = (HostHashEntry *) malloc(sizeof(HostHashEntry) + len);
+ he->hostaddr = addr->addr;
+ he->next = HostHashTable[hv];
+ he->ref = 0;
+ he->hflag = hflag;
+ memcpy(he->hostname, host, len);
+ HostHashTable[hv] = he;
+ break;
+ }
+
+ if (he->hostaddr == addr->addr)
+ {
+ strcpy(host, he->hostname);
+ break;
+ }
+
+ he = he->next;
+ }
+
+ he->ref++;
+ time(&he->uptime);
+
+ return he->hflag;
+}
+
+
+static void
+hht_expire(now)
+ time_t now;
+{
+ int i;
+ HostHashEntry **hp, *he;
+
+ now -= HOST_HASH_ENTRY_LIFE;
+
+ for (i = 0; i < HOST_HASH_TABLE_SIZE; i++)
+ {
+ hp = &HostHashTable[i];
+
+ while (he = *hp)
+ {
+ if (he->uptime < now)
+ {
+ if (he->ref <= HOST_HASH_ENTRY_DELTA)
+ {
+ *hp = he->next;
+ free(he);
+ continue;
+ }
+
+ he->ref -= HOST_HASH_ENTRY_DELTA;
+ }
+
+ hp = &(he->next);
+ }
+ }
+}
+
+
+/* ----------------------------------------------------- */
+/* Anti Spam */
+/* ----------------------------------------------------- */
+
+
+static HashTable *mrcpt_ht;
+static HashTable *mhost_ht;
+static HashTable *mfrom_ht;
+static HashTable *title_ht;
+
+
+#ifdef FORGE_CHECK
+static HashTable *forge_ht;
+
+static int /* 1: host/domain ¬O°²³yªº */
+is_forge(host)
+ char *host;
+{
+ HashEntry *he;
+ int score;
+ unsigned long addr;
+
+ he = ht_add(forge_ht, host);
+ if ((score = he->score) == 0)
+ {
+ /* he->uptime = addr = dns_addr(host); */
+
+ /* Thor.990811: check forge by dns mx & a record */
+ char mxlist[MAX_MXLIST];
+ /* if (dns_aton(domain) != INADDR_NONE) return; */
+ dns_mx(host, mxlist);
+ if (!*mxlist && dns_a(host) == INADDR_NONE)
+ he->uptime = addr = INADDR_NONE; /* Thor.990811: ¬O­tªº, ·|³Q expire */
+ else
+ he->uptime = addr = time(0); /* Thor.990811: ¤Ï¥¿¥u­n¤£¬OINADDR_NONE´N¥i¥H¤F */
+ }
+ else
+ addr = he->uptime;
+
+ /* if (addr != INADDR_NONE) */ /* Thor.990811: ¦s¦bªºhost¤~¥[¤À¡A¯dµÛ */
+ he->score = ++score;
+
+ if (score == SPAM_FORGE_LIMIT)
+ {
+ fprintf(flog, "FORGE_L\t%s\n", host);
+ he->score = 0; /* Thor.000623: release this hashing entry for expire aging */
+ }
+
+ return (addr == INADDR_NONE);
+}
+
+#else
+
+static int /* 1: host/domain ¬O°²³yªº */
+is_forge(host)
+ char *host;
+{
+ int cc;
+ char *str;
+
+ str = NULL;
+
+ while (cc = *host)
+ {
+ host++;
+ if (cc == '.')
+ str = host;
+ }
+
+ return ((str == NULL) || (host - str <= 1));
+}
+#endif /* FORGE_CHECK */
+
+
+static void
+spam_add(he)
+ HashEntry *he;
+{
+ FILE *fp;
+
+ if (fp = fopen(UNMAIL_ACLFILE, "a"))
+ {
+ struct tm *p;
+
+ p = localtime(&he->uptime);
+ str_lower(he->key, he->key);
+ /* Thor.990329: y2k */
+ fprintf(fp, "%s # %d %02d/%02d/%02d %02d:%02d:%02d\n",
+ he->key, he->score,
+ p->tm_year % 100, p->tm_mon + 1, p->tm_mday,
+ p->tm_hour, p->tm_min, p->tm_sec);
+ fclose(fp);
+ }
+
+ he->score = 0; /* release this hashing entry */
+}
+
+
+/* ----------------------------------------------------- */
+/* statistics of visitor */
+/* ----------------------------------------------------- */
+
+
+#include "splay.h"
+
+
+static int
+vx_cmp(x, y)
+ HashEntry *x;
+ HashEntry *y;
+{
+ int dif;
+
+ dif = y->visit - x->visit;
+ if (dif)
+ return dif;
+ return str_cmp(x->key, y->key);
+}
+
+
+static void
+vx_out(fp, top)
+ FILE *fp;
+ SplayNode *top;
+{
+ HashEntry *he;
+
+ if (top == NULL)
+ return;
+
+ vx_out(fp, top->left);
+
+ he = (HashEntry *) top->data;
+
+ fprintf(fp, "%6d %s\n", he->visit, he->key);
+
+ vx_out(fp, top->right);
+}
+
+
+static void
+splay_free(top)
+ SplayNode *top;
+{
+ SplayNode *node;
+
+ if (top == NULL)
+ return;
+
+ if (node = top->left)
+ splay_free(node);
+
+ if (node = top->right)
+ splay_free(node);
+
+ MYDOG;
+ free(top);
+ MYDOG;
+}
+
+
+static void
+vx_log(fp, tag, ht)
+ FILE *fp;
+ char *tag;
+ HashTable *ht;
+{
+ int i, nentry, nvisit;
+ SplayNode *top;
+ HashEntry *he;
+
+ fprintf(fp, "[%s]\n\n", tag);
+
+ top = NULL;
+ nentry = 0;
+ nvisit = 0;
+
+ /* splay sort */
+
+ for (i = ht->mask; i >= 0; i--)
+ {
+ for (he = ht->bucket[i]; he; he = he->next)
+ {
+ nentry++;
+ nvisit += he->visit;
+ top = splay_in(top, he, vx_cmp);
+ }
+ }
+
+ /* report */
+
+ fprintf(fp, "%d entry, %d visit:\n\n", nentry, nvisit);
+ vx_out(fp, top);
+
+ /* free memory */
+
+ splay_free(top);
+
+ for (i = ht->mask; i >= 0; i--)
+ {
+ for (he = ht->bucket[i]; he; he = he->next)
+ {
+ he->visit = 0;
+ }
+ }
+}
+
+
+static void
+visit_fresh()
+{
+ char folder[64], fpath[64];
+ FILE *fp;
+ HDR hdr;
+
+ brd_fpath(folder, BN_JUNK, FN_DIR);
+
+ if (!(fp = fdopen(hdr_stamp(folder, 'A', &hdr, fpath), "w")))
+ return;
+
+ vx_log(fp, "Host", mhost_ht);
+ vx_log(fp, "From", mfrom_ht);
+ vx_log(fp, "Rcpt", mrcpt_ht);
+ vx_log(fp, "¼ÐÃD", title_ht);
+ fclose(fp);
+
+ hdr.xmode = POST_MARKED;
+ strcpy(hdr.owner, "<BMTA>");
+ strcpy(hdr.title, "²Î­p¸ê®Æ");
+ rec_bot(folder, &hdr, sizeof(HDR));
+
+ update_btime(BN_JUNK);
+}
+
+
+/* ----------------------------------------------------- */
+/* memo of mail header / body */
+/* ----------------------------------------------------- */
+
+
+static void
+mta_memo(ap, mark)
+ Agent *ap;
+ int mark;
+{
+ /* char folder[64], fpath[64], nick[80], *memo; */
+ char folder[64], fpath[64], nick[256], *memo; /* Thor.000131: ¥H¨¾¸U¤@ */
+ FILE *fp;
+ HDR hdr;
+
+ memo = ap->nick;
+ if (*memo)
+ sprintf(nick, " (%s)", memo);
+ else
+ nick[0] = '\0';
+
+ memo = ap->memo;
+ if (!*memo)
+ {
+ memo = ap->fpath;
+ if (!*memo)
+ {
+ memo = ap->title;
+ }
+ }
+
+ brd_fpath(folder, BN_JUNK, FN_DIR);
+
+ if (!(fp = fdopen(hdr_stamp(folder, 'A', &hdr, fpath), "w")))
+ return;
+
+ /* Thor.990915: Åã¥Ü mail from ¥H«K°lÂÜ */
+ fprintf(fp, "MAIL FROM: <%s>\nFrom: %s%s\nSubj: %s\nDate: %s\n"
+ "Host: %s\nMemo: %s\nFile: %s\nSize: %d\n%s",
+ ap->from, ap->addr, nick, ap->title, Btime(&hdr.chrono),
+ ap->ident, ap->memo, ap->fpath, ap->used, ap->data);
+ fclose(fp);
+
+ if (mark)
+ hdr.xmode = POST_MARKED;
+
+ strcpy(hdr.owner, "<BMTA>");
+ strcpy(hdr.title, memo);
+ rec_bot(folder, &hdr, sizeof(HDR));
+
+ update_btime(BN_JUNK);
+}
+
+
+/* ----------------------------------------------------- */
+/* mailers */
+/* ----------------------------------------------------- */
+
+
+static int
+bbs_mail(ap, data, userid)
+ Agent *ap;
+ char *data;
+ char *userid;
+{
+ HDR hdr;
+ int fd, method, sno;
+ FILE *fp;
+ char folder[80], buf[256], from[256], *author, *fpath, *title;
+ struct stat st;
+
+ fp = flog;
+ sno = ap->sno;
+
+ usr_fpath(folder, userid, FN_DIR);
+
+ /* Thor.990617: get file stat */
+ if (!stat(folder, &st) && st.st_size > MAX_BBSMAIL * sizeof(HDR))
+ {
+ fprintf(fp, "MAIL-\t[%d] <%s> over-spammed\n", sno, userid);
+ return -1;
+ }
+
+ /* allocate a file for the new mail */
+
+ fpath = ap->fpath;
+ method = *fpath ? HDR_LINK : 0;
+ if ((fd = hdr_stamp(folder, method, &hdr, fpath)) < 0)
+ {
+ fprintf(fp, "MAIL-\t[%d] <%s> stamp error\n", sno, userid);
+ return -2;
+ }
+
+ author = ap->addr;
+ title = ap->nick;
+ sprintf(ap->memo, "%s -> %s", author, userid);
+
+ if (*title)
+ sprintf(from, "%s (%s)", author, title);
+ else
+ strcpy(from, author);
+
+ title = ap->title;
+ if (!method)
+ {
+ if (!*title)
+ sprintf(title, "¨Ó¦Û %.64s", author);
+
+ sprintf(buf, "§@ªÌ: %.72s\n¼ÐÃD: %.72s\n®É¶¡: %s\n\n",
+ from, title, Btime(&hdr.chrono));
+
+ write(fd, buf, strlen(buf));
+ write(fd, data, ap->data + ap->used - data);
+ close(fd);
+ }
+
+ hdr.xmode = MAIL_INCOME;
+ str_ncpy(hdr.owner, author, sizeof(hdr.owner));
+ str_ncpy(hdr.title, title, sizeof(hdr.title));
+ rec_add(folder, &hdr, sizeof(HDR));
+
+ fprintf(fp, "%d\t%s\t%s\t%s/%s\n", sno, author, title, userid, hdr.xname);
+ ap->xrcpt++;
+ ap->xdata += ap->used;
+
+ /* --------------------------------------------------- */
+ /* ³qª¾ user ¦³·s«H¥ó */
+ /* --------------------------------------------------- */
+
+ sprintf(folder, "usr/%c/%s/.ACCT", *userid, userid);
+ fd = open(folder, O_RDONLY);
+ if (fd >= 0)
+ {
+ if ((read(fd, &sno, sizeof(sno)) == sizeof(sno)) && (sno > 0))
+ bbs_biff(sno);
+ close(fd);
+ }
+
+ mta_memo(ap, 0);
+ return 0;
+}
+
+
+static int
+bbs_brd(ap, data, brdname) /* itoc.030323: ±H«Hµ¹¬ÝªO */
+ Agent *ap;
+ char *data;
+ char *brdname;
+{
+ HDR hdr;
+ int fd, method, sno;
+ FILE *fp;
+ char folder[80], buf[256], from[256], *author, *fpath, *title;
+
+ fp = flog;
+ sno = ap->sno;
+
+ brd_fpath(folder, brdname, FN_DIR);
+
+ BRD* brd=brd_get(brdname);
+ if(brd->battr & BRD_LOCAL){
+ return -2;
+ }
+
+ /* allocate a file for the new post */
+ fpath = ap->fpath;
+ method = *fpath ? HDR_LINK | 'A' : 'A';
+ if ((fd = hdr_stamp(folder, method, &hdr, fpath)) < 0)
+ {
+ fprintf(fp, "MAIL-\t[%d] <%s> stamp error\n", sno, brdname);
+ return -2;
+ }
+
+ author = ap->addr;
+ title = ap->nick;
+
+ sprintf(ap->memo, "%s -> %s", author, brdname);
+
+ if (*title)
+ sprintf(from, "%s (%s)", author, title);
+ else
+ strcpy(from, author);
+
+ title = ap->title;
+ if (method == 'A')
+ {
+ if (!*title)
+ sprintf(title, "¨Ó¦Û %.64s", author);
+
+ sprintf(buf, "µo«H¤H: %.50s ¬ÝªO: %s\n¼Ð ÃD: %.72s\nµo«H¯¸: %s\n\n",
+ from, brdname, title, Btime(&hdr.chrono));
+
+ write(fd, buf, strlen(buf));
+ write(fd, data, ap->data + ap->used - data);
+ close(fd);
+ }
+
+ hdr.xmode = POST_INCOME;
+ str_ncpy(hdr.owner, author, sizeof(hdr.owner));
+ str_ncpy(hdr.title, title, sizeof(hdr.title));
+ rec_bot(folder, &hdr, sizeof(HDR));
+
+ update_btime(brdname);
+
+ fprintf(fp, "%d\t%s\t%s\t%s/%s\n", sno, author, title, brdname, hdr.xname);
+ ap->xrcpt++;
+ ap->xdata += ap->used;
+
+ mta_memo(ap, 0);
+ return 0;
+}
+
+
+#ifdef EMAIL_JUSTIFY
+static int
+bbs_valid(ap)
+ Agent *ap;
+{
+ int fd, sno;
+ FILE *fp;
+ char pool[256], folder[64], justify[128], *str, *ptr, *userid;
+ ACCT acct;
+ HDR hdr;
+
+ fp = flog;
+ sno = ap->sno;
+ ptr = ap->title;
+
+ /* itoc.µù¸Ñ: mail.c: TAG_VALID " userid(regkey) [VALID]" */
+ if (!(str = strstr(ptr, TAG_VALID)))
+ {
+ sprintf(ap->memo, "REG : %.64s", ptr);
+ fprintf(fp, "REG-\t[%d] %s\n", sno, ptr);
+ return -2;
+ }
+
+ userid = pool;
+ strcpy(userid, str + sizeof(TAG_VALID));
+ if (!(str = strchr(userid, '(')))
+ return -1;
+
+ *str = '\0';
+
+ if (!(ptr = (char *) strchr(str + 1, ')')) || !strstr(ptr, "[VALID]"))
+ {
+ sprintf(ap->memo, "REG - %s (format)", userid);
+ fprintf(fp, "REG-\t[%d] <%s> format\n", sno, userid);
+ return -1;
+ }
+
+ *ptr++ = 0;
+
+ if ((fd = acct_fetch(userid, &acct)) < 0)
+ {
+ sprintf(ap->memo, "REG - %s (not exist)", userid);
+ fprintf(fp, "REG-\t[%d] <%s> not exist\n", sno, userid);
+ return -1;
+ }
+
+ if (str_hash(acct.email, acct.tvalid) != chrono32(str))
+ {
+ close(fd);
+ sprintf(ap->memo, "REG - %s (checksum)", userid);
+ fprintf(fp, "REG-\t[%d] <%s> check sum: %s\n", sno, acct.userid, str + 1);
+ return -1;
+ }
+
+ /* ´£¤ÉÅv­­ */
+ acct.userlevel |= PERM_VALID;
+ time(&acct.tvalid);
+ lseek(fd, 0, SEEK_SET);
+ write(fd, &acct, sizeof(ACCT));
+ close(fd);
+
+ usr_fpath(folder, userid, FN_DIR);
+ if (!hdr_stamp(folder, HDR_LINK, &hdr, FN_ETC_JUSTIFIED))
+ {
+ strcpy(hdr.title, MSG_REG_VALID);
+ strcpy(hdr.owner, STR_SYSOP);
+ hdr.xmode = MAIL_NOREPLY;
+ rec_add(folder, &hdr, sizeof(HDR));
+ }
+
+ ptr = ap->nick;
+ if (*ptr)
+ sprintf(justify, "RPY: %s (%s)", ap->addr, ptr);
+ else
+ sprintf(justify, "RPY: %s", ap->addr);
+
+ usr_fpath(folder, userid, FN_JUSTIFY);
+ if (fp = fopen(folder, "a"))
+ {
+ fprintf(fp, "%s\n", justify);
+ fclose(fp);
+ }
+
+ /* ¦b usr ¥Ø¿ý¯d¤U§¹¾ã¦^«H°O¿ý */
+ usr_fpath(folder, userid, FN_EMAIL);
+ if (fp = fopen(folder, "w"))
+ {
+ fprintf(fp, "ID: %s\nVALID: %s\nHost: %s\nFrom: %s\n%s\n",
+ userid, justify, ap->ident, ap->addr, ap->data);
+ fclose(fp);
+ }
+
+ /* Thor.990414: ¥[¦L Timestamp, ¤è«K°l¬d */
+ fprintf(fp, "REG\t[%d] <%s> %s %s\n", sno, acct.userid, justify, str + 1);
+ sprintf(ap->memo, "REG + %s", userid);
+ ap->xrcpt++;
+
+ return 0;
+}
+#endif
+
+
+/* ----------------------------------------------------- */
+/* Access Control List routines */
+/* ----------------------------------------------------- */
+
+/* ----------------------------------------------------- */
+/* ACL config file format */
+/* ----------------------------------------------------- */
+/* user: majordomo@ bad@cs.nthu.edu.tw */
+/* host: cs.nthu.edu.tw 140.114.77.1 */
+/* subnet: .nthu.edu.tw 140.114.77. */
+/* ----------------------------------------------------- */
+
+
+
+typedef struct ACL_t
+{
+ struct ACL_t *nacl; /* next acl */
+ int locus;
+ unsigned char filter[0];
+} ACL_t;
+
+
+static int
+str_cpy(dst, src, n)
+ unsigned char *dst;
+ unsigned char *src;
+ int n;
+{
+ int cc, len;
+
+ len = 0;
+ for (;;)
+ {
+ cc = *src;
+ if (cc >= 'A' && cc <= 'Z')
+ cc |= 0x20;
+ *dst = cc;
+ if ((len > n) || (!cc)) /* lkchu.990511: Á×§K overflow */
+ break;
+ src++;
+ dst++;
+ len++;
+ }
+ return len;
+}
+
+
+static ACL_t *
+acl_add(root, filter)
+ ACL_t *root;
+ unsigned char *filter;
+{
+ int at, cc, len;
+ char *str;
+ ACL_t *ax;
+
+ str = filter;
+ at = len = 0;
+ for (;;)
+ {
+ cc = *str;
+ if (cc == '\n' || cc == ' ' || cc == '#' || cc == '\t' || cc == '\r')
+ {
+ *str = '\0';
+ break;
+ }
+ if (!cc)
+ break;
+
+ str++;
+ len++;
+ if (cc == '@')
+ at = -len;
+ }
+
+ if (len <= 0)
+ return root;
+
+ ax = (ACL_t *) malloc(sizeof(ACL_t) + len + 1);
+
+ ax->nacl = root;
+ ax->locus = at ? at : len;
+
+ str = ax->filter;
+ do
+ {
+ cc = *filter++;
+ if (cc >= 'A' && cc <= 'Z')
+ cc |= 0x20;
+ } while (*str++ = cc);
+
+ return ax;
+}
+
+
+static ACL_t *
+acl_load(fpath, root)
+ char *fpath;
+ ACL_t *root;
+{
+ FILE *fp;
+ char buf[256];
+ ACL_t *ax, *nacl;
+
+ /* ¥ý²MªÅ */
+ if (ax = root)
+ {
+ do
+ {
+ nacl = ax->nacl;
+ free(ax);
+ } while (ax = nacl);
+
+ ax = NULL;
+ }
+
+ if (fp = fopen(fpath, "r"))
+ {
+ fpath = buf;
+ while (fgets(fpath, sizeof(buf), fp))
+ {
+ if (!*fpath)
+ break;
+
+ if (*fpath == '#')
+ continue;
+
+ ax = acl_add(ax, fpath);
+ }
+ fclose(fp);
+ }
+
+ return ax;
+}
+
+
+static int
+acl_match(root, ruser, rhost)
+ ACL_t *root;
+ unsigned char *ruser;
+ unsigned char *rhost;
+{
+ ACL_t *ax;
+ unsigned char *filter, xuser[80], xhost[128];
+ int lhost, luser, locus, len;
+
+ if (!(ax = root))
+ return 0;
+
+ /* lkchu.990511: rhost ©M ruser ³£¨S¦³¥ýÀˬdªø«×´N copy,
+ «Ü¥i¯àµo¥Í segmentation fault */
+ luser = str_cpy(xuser, ruser, sizeof(xuser));
+ lhost = str_cpy(xhost, rhost, sizeof(xhost));
+ ruser = xuser;
+ rhost = xhost;
+
+ do
+ {
+ filter = ax->filter;
+ locus = ax->locus;
+
+ /* match remote user name */
+
+ if (locus < 0)
+ {
+ len = -1 - locus;
+ if ((len != luser) || memcmp(ruser, filter, luser))
+ continue;
+
+ filter -= locus;
+ if (*filter == '\0') /* majordomo@ */
+ return 1;
+ locus = strlen(filter);
+ }
+
+ /* match remote host name */
+
+#if 0
+ if (locus == lhost)
+ {
+ if (!strcmp(filter, rhost))
+ return 1;
+ }
+ else if (locus < lhost)
+ {
+ if (*filter == '.')
+ {
+ if (!strcmp(filter, rhost + lhost - locus))
+ return 1;
+ }
+ else if (filter[locus - 1] == '.')
+ {
+ if (!memcmp(filter, rhost, locus))
+ return 1;
+ }
+ }
+#endif
+
+ /* subnet ¬Û¦P¤]ºâ match */
+ if (locus <= lhost)
+ {
+ if (!strcmp(rhost + lhost - locus, filter))
+ return 1;
+ }
+ } while (ax = ax->nacl);
+
+ return 0;
+}
+
+
+static ACL_t *mail_root = NULL; /* MAIL_ACLFILE ªº acl_root */
+static ACL_t *unmail_root = NULL; /* UNMAIL_ACLFILE ªº acl_root */
+
+
+static int /* 1: spam */
+acl_spam(ruser, rhost)
+ unsigned char *ruser;
+ unsigned char *rhost;
+{
+ /* ¤£¦b¥Õ¦W³æ¤W©Î¦b¶Â¦W³æ¤W */
+ return (!acl_match(mail_root, ruser, rhost) || acl_match(unmail_root, ruser, rhost));
+}
+
+
+/* ----------------------------------------------------- */
+/* mail header routines */
+/* ----------------------------------------------------- */
+
+/* ----------------------------------------------------- */
+/* From xyz Wed Dec 24 17:05:37 1997 */
+/* From: xyz (nick) */
+/* From user@domain Wed Dec 24 18:00:26 1997 */
+/* From: user@domain (nick) */
+/* ----------------------------------------------------- */
+
+
+static char *
+mta_from(ap, str)
+ Agent *ap;
+ unsigned char *str; /* Thor.990629: ¤¤¤å? */
+{
+ int cc;
+ char pool[512], *head, *tail;
+
+ head = pool;
+ tail = head + sizeof(pool) - 1;
+
+ for (;;)
+ {
+ /* skip leading space */
+ while (*str == ' ' || *str == '\t')
+ str++;
+
+ /* copy the <From> to buffer pool */
+
+ for (;;)
+ {
+ cc = *str;
+
+ if (cc == '\0')
+ {
+ *head = '\0';
+ sprintf(head, "%s %s", pool, ap->addr);
+ agent_log(ap, "From:", head);
+ return str;
+ }
+
+ str++;
+ if (cc == '\n')
+ break;
+
+ *head++ = cc;
+
+ if (head >= tail)
+ return str;
+ }
+
+ /* if (*str != ' ') */
+ if (*str > ' ' || *str == '\n') /* Thor.990617: for merge multiline */
+ break;
+
+ /* go on to merge multi-line <From> */
+ }
+
+ *head = '\0';
+
+ str_from(pool, head = ap->addr, ap->nick);
+
+ /* Thor.000909: ¨S¦³head (from)ªº´N¦Û¤v¸É¤W¤F */
+ if (!*head)
+ strcpy(head, ap->from);
+
+ if (str_cmp(head, ap->from))
+ { /* Thor.000911.µù¸Ñ: ¦pªG¤£¤@¼Ëªº¸Ü, ­ncheck; ¤@¼Ëªº¤§«echeck¹L¤F */
+ if (tail = strchr(head, '@')) /* Thor.000911.µù¸Ñ: ¥¿±`ªºaddrªº¸Ü */
+ {
+ *tail++ = '\0';
+
+ if (is_forge(tail)) /* Thor.990811: °²³yªº, ·Q³£§O·Q */
+ return NULL;
+
+ /* Àˬd From: ¬O§_¦b¶Â¥Õ¦W³æ¤W */
+ if (!(ap->mode & (AM_VALID | AM_BBSADM)) && acl_spam(head, tail))
+ {
+ tail[-1] = '@';
+ agent_log(ap, "SPAM-M", head);
+ return NULL;
+ }
+
+ tail[-1] = '@';
+ }
+ else /* Thor.000911: ¤£¦¬¤£¥¿±`ªº addr */
+ {
+ return NULL;
+ }
+ }
+
+ return str;
+}
+
+
+static char *
+mta_subject(ap, str)
+ Agent *ap;
+ unsigned char *str;
+{
+ int cc;
+ char pool[640], *head, *tail; /* *line */
+
+ head = pool;
+ tail = head + sizeof(pool) - 128;
+
+ /* Thor.980831: modified for multi-line header */
+
+ /* skip leading space */
+
+ /* while (*str == ' ') str++; */
+ while (*str == ' ' || *str == '\t')
+ str++;
+
+ /* copy the <Subject> to buffer pool */
+
+ for (;;)
+ {
+ cc = *str;
+
+ if (cc == '\0')
+ {
+ sprintf(head, "%s %s", ap->from, ap->addr);
+ agent_log(ap, "Subj:", pool /* head */ ); /* Thor.980904:·Q¬Ýsubj¥þ»ª */
+ return str;
+ }
+
+ str++;
+ /* Thor.980906: |no body |header |body seperator */
+ if (cc == '\n')
+ {
+ if (!*str || *str > ' ' || *str == '\n')
+ break;
+ /* Thor.991014: skip next line leading space */
+ while (*str == ' ' || *str == '\t')
+ str++;
+ continue;
+ }
+
+ *head++ = cc;
+
+ if (head >= tail) /* line too long */
+ {
+ agent_log(ap, "Subj:", pool); /* Xshadow.980906: really line too long? */
+ return str;
+ }
+ }
+
+ *head = 0;
+
+ str_decode(pool);
+ str_ansi(ap->title, pool, sizeof(ap->title));
+ return str;
+}
+
+
+static inline int
+is_host_alias(addr)
+ char *addr;
+{
+ int i;
+ char *str;
+ static char *alias[] = HOST_ALIASES;
+
+ /* check the aliases */
+
+ for (i = 0; str = alias[i]; i++)
+ {
+ if (!str_cmp(addr, str))
+ return 1;
+ }
+ return 0;
+}
+
+
+/* Thor.980901: mail decode, only for ONE part, not for "This is a multi-part message in MIME format." */
+
+static char *
+mta_decode(ap, str, code)
+ Agent *ap;
+ unsigned char *str;
+ char *code;
+{
+ str = mm_getencode(str, code);
+
+ /* skip whole line */
+ while (*str && *str++ != '\n')
+ ;
+
+ return str;
+}
+
+
+static char *
+mta_boundary(ap, str, boundary)
+ Agent *ap;
+ unsigned char *str;
+ char *boundary;
+{
+ int cc;
+ char *base = boundary;
+
+ *boundary = 0;
+ /* skip leading space */
+ while (*str == ' ')
+ str++;
+
+ if (!str_ncmp(str, "multipart", 9))
+ {
+ char *tmp;
+
+ if (tmp = str_str(str, "boundary="))
+ {
+ /* Thor.990221: ©~µM¦³ªº¤H¤£¥Î " */
+ tmp += 9;
+ if (*tmp == '"')
+ tmp++;
+
+ while (*tmp && *tmp != '"' && *tmp != '\n')
+ *boundary++ = *tmp++;
+ /* *boundary++ = '\n'; */ /* Thor.980907: ¤§«á·|³QµL·N¶¡¸õ¹L */
+ *boundary = 0;
+ }
+ logit("MULTI", base);
+ }
+
+ while (cc = *str)
+ {
+ str++;
+ if (cc == '\n' && (!*str || *str > ' ' || *str == '\n'))
+ break;
+ }
+
+ return str;
+}
+
+
+/* Thor.980907: support multipart mime */
+/* ----------------------------------------------------- */
+/* multipart decoder */
+/* ----------------------------------------------------- */
+
+
+static int
+multipart(src, dst, boundary)
+ unsigned char *src;
+ unsigned char *dst; /* Thor: no ending 0 */
+ unsigned char *boundary; /* Thor: should include "--" */
+{
+ unsigned char *base = dst;
+ char *bound;
+ unsigned char *tmp;
+ char decode;
+ char buf[512] = "--"; /* Thor: sub mime boundary */
+ int cc;
+ int boundlen;
+
+ boundlen = strlen(boundary);
+ if (boundlen < 6)
+ return 0; /* Thor: boundary too small, "\n> <\n" */
+
+ while (*src)
+ {
+ bound = strstr(src, boundary);
+
+ if (bound)
+ *bound = 0;
+
+ if (buf[2]) /* Thor: multipart & encoded can't be happened simutaneously */
+ {
+ cc = multipart(src, dst, buf);
+ if (cc > 0)
+ dst += cc;
+ else
+ goto bypass;
+ }
+ else
+ {
+ bypass:
+ while (*src)
+ *dst++ = *src++;
+ }
+
+ /* reset */
+ buf[2] = 0;
+ decode = 0;
+
+ if (!bound)
+ break;
+
+ src = bound + boundlen; /* Thor: src over boundary */
+
+ tmp = dst + boundlen - 3 /* " <\n" */ ;
+
+ *dst++ = '\n';
+ *dst++ = '>';
+ *dst++ = ' ';
+
+ while (dst < tmp)
+ *dst++ = '-';
+
+ *dst++ = ' ';
+ *dst++ = '<';
+ *dst++ = '\n';
+
+ for (;;) /* Thor: processing sub-header */
+ {
+ if (!str_ncmp(src, "Content-Transfer-Encoding:", 26))
+ { /* Thor.980901: ¸Ñ rfc1522 body code */
+ src = mta_decode(NULL, src + 26, &decode);
+ }
+ else if (!str_ncmp(src, "Content-Type:", 13))
+ {
+ src = mta_boundary(NULL, src + 13, buf + 2);
+ }
+ else
+ {
+ while (cc = *src)
+ /* Thor.980907: ­è¦n¸õ¤Fboundary«áªº \n,¤£¬O¬G·Nªº :p */
+ {
+ src++;
+ if (cc == '\n')
+ break;
+ }
+ }
+ if (!*src || *src == '\n')
+ break; /* null body or end of mail header */
+ }
+
+ }
+ return dst - base;
+}
+
+
+/* ----------------------------------------------------- */
+/* mailer */
+/* ----------------------------------------------------- */
+
+
+static int
+mta_mailer(ap)
+ Agent *ap;
+{
+ char *data, *str, *addr, *delimiter, decode = 0;
+ char boundary[512] = "--";
+ int cc, mode;
+ RCPT *rcpt;
+
+ mode = ap->mode;
+ data = ap->data + 1; /* skip leading stuff */
+
+ /* --------------------------------------------------- */
+ /* parse the mail header and filter spam mails */
+ /* --------------------------------------------------- */
+
+ for (;;)
+ {
+ if (!str_ncmp(data, "From:", 5))
+ {
+ data += 5;
+ data = mta_from(ap, data);
+ if (!data)
+ {
+ agent_reply(ap, "550 you are not in my access list");
+ return -1;
+ }
+ }
+ else if (!str_ncmp(data, "Subject:", 8))
+ {
+ data = mta_subject(ap, data + 8);
+ }
+ else if (!str_ncmp(data, "Content-Transfer-Encoding:", 26))
+ { /* Thor.980901: ¸Ñ rfc1522 body code */
+ data = mta_decode(ap, data + 26, &decode);
+ }
+ else if (!str_ncmp(data, "Content-Type:", 13))
+ { /* Thor.980907: ¸Ñ multi-part */
+#ifdef ANTI_HTMLMAIL
+ /* ¤@¯ë BBS ¨Ï¥ÎªÌ³q±`¥u±H¤å¦r¶l¥ó©Î¬O±q¨ä¥L BBS ¯¸±H¤å³¹¨ì¦Û¤vªº«H½c
+ ¦Ó¼s§i«H¥ó³q±`¬O html ®æ¦¡©Î¬O¸Ì­±¦³§¨±a¨ä¥LÀÉ®×
+ §Q¥Î¶l¥óªºÀÉÀY¦³ Content-Type: ªºÄݩʧⰣ¤F text/plain (¤å¦r¶l¥ó) ªº«H¥ó³£¾×¤U¨Ó */
+ char *content = data + 14;
+ if (*content != '\0' && str_ncmp(content, "text/plain", 10))
+ {
+ agent_reply(ap, "550 we only accept plain text");
+ return -1;
+ }
+#endif
+
+#ifdef ANTI_NOTMYCHARSETMAIL
+ {
+ char charset[32];
+ mm_getcharset(data + 13, charset, sizeof(charset));
+ if (str_cmp(charset, MYCHARSET) && str_cmp(charset, "us-ascii"))
+ {
+ agent_reply(ap, "550 non-supported charset");
+ return -1;
+ }
+ }
+#endif
+
+ data = mta_boundary(ap, data + 13, boundary + 2);
+ }
+ else
+ {
+ /* skip this line */
+ for (;;)
+ {
+ cc = *data;
+
+ if (cc == '\0') /* null body */
+ {
+ data--;
+ goto mta_mail_body;
+ /* return 0; */
+ }
+
+ data++;
+
+ if (cc == '\n')
+ break;
+ }
+ }
+
+ if (*data == '\n')
+ break; /* end of mail header */
+ }
+
+ /* --------------------------------------------------- */
+ /* process the mail body */
+ /* --------------------------------------------------- */
+
+mta_mail_body:
+
+ if (mode & AM_BBSADM)
+ {
+ sprintf(ap->memo, "ADM: %.64s", ap->from);
+ mta_memo(ap, 1); /* lkchu: mark ADM's letter */
+
+ return 0;
+ }
+
+ *data = '\0';
+
+ /* --------------------------------------------------- */
+ /* validate user's character */
+ /* --------------------------------------------------- */
+
+ rcpt = ap->rcpt;
+
+#ifdef EMAIL_JUSTIFY
+ if (mode & AM_VALID)
+ {
+ /* Thor.000328: µù¸Ñ: rcpt to bbsreg@MYHOSTNAME */
+ if (bbs_valid(ap) == -2)
+ *data = '\n';
+
+ mta_memo(ap, 0);
+
+ if (rcpt == NULL)
+ return 0;
+ }
+#endif
+
+ /* --------------------------------------------------- */
+ /* check mail body */
+ /* --------------------------------------------------- */
+
+ delimiter = data;
+
+ for (;;)
+ {
+ cc = *++data;
+ if (cc == '\0') /* null mail body */
+ {
+ /* Thor.000327: °O¿ýªÅ«H */
+ fprintf(flog, "NULL BODY\t[%d] from:%s nrcpt:%d\n", ap->sno, ap->from, ap->nrcpt);
+ return 0;
+ }
+
+ if (cc != '\n') /* skip empty lines in mail body */
+ break;
+ }
+
+ /* --------------------------------------------------- */
+ /* decode mail body */
+ /* --------------------------------------------------- */
+
+ /* Thor.980901: decode multipart body */
+ if (boundary[2])
+ {
+ /* logit("MULTIDATA",data); */
+ cc = multipart(data, data, boundary);
+ if (cc > 0)
+ ap->used = (data - ap->data) + cc; /* (data - ap->data) ¬O header ªø«×¡Acc ¬O«H¤º®eªºªø«× */
+ }
+ /* Thor.980901: decode mail body */
+ else if (decode)
+ {
+ /* data[mmdecode(data,decode,data)]=0; */
+ /* Thor.980901: ¦] decode¥²¬° b or q, ¬G mmdecode¤£¬°-1, *
+ * ¥²©w¦¨¥\, ¤S¦] mmdecode¤£¦Û°Ê¥[ 0, ¬G¤â°Ê¥[¤W */
+
+ /* Thor.980901: ¤£¥Î 0 §@µ²§ô, ¦Ó¥Îªø«×, ­pºâ¤è¦¡¦p¤U *
+ * write(fd, data, ap->data + ap->used - data); *
+ * ¬G­×§ï ap->used, ¥HºI±¼¹Lªø data */
+
+ cc = mmdecode(data, decode, data);
+ if (cc > 0)
+ ap->used = (data - ap->data) + cc; /* (data - ap->data) ¬O header ªø«×¡Acc ¬O«H¤º®eªºªø«× */
+
+ /* logit("DECODEDATA", data); */
+ }
+
+ /* --------------------------------------------------- */
+ /* check E-mail address for anti-spam first */
+ /* --------------------------------------------------- */
+
+ addr = ap->addr;
+ if ((str = strchr(addr, '@')) && str_ncmp(addr, "mailer-daemon@", 14))
+ {
+ HashEntry *he, *hx;
+ int nrcpt, score, delta;
+ time_t uptime;
+
+ uptime = ap->uptime;
+ nrcpt = ap->nrcpt;
+
+ /* -------------------------------------------------- */
+ /* Àˬd³o­Ó @host ±H¶i¨Óªº«H¦³µL¶W¹L SPAM_MHOST_LIMIT */
+ /* -------------------------------------------------- */
+
+ he = ht_add(mhost_ht, ++str);
+ he->uptime = uptime;
+ he->score += (nrcpt > 0) ? nrcpt : 1; /* ¤@­Ó¦¬¥ó¤H³£¨S¦³¤]ºâ¤@¦¸³X°Ý */
+
+ if (he->score >= SPAM_MHOST_LIMIT)
+ {
+ unmail_root = acl_add(unmail_root, str);
+ spam_add(he);
+ fprintf(flog, "SPAM-H\t[%d] %s\n", ap->sno, str);
+
+ sprintf(ap->memo, "SPAM : %s", str);
+ *delimiter = '\n';
+ }
+
+ /* -------------------------------------------- */
+ /* Àˬd³o­Ó title ªº«H¦³µL¶W¹L SPAM_TITLE_LIMIT */
+ /* -------------------------------------------- */
+
+ if (nrcpt > 0)
+ {
+ hx = ht_add(title_ht, str_ttl(ap->title));
+ hx->uptime = uptime;
+ hx->visit += nrcpt - 1; /* title_ht ªº visit ¬O°O¿ý³o¼ÐÃDªº«H¦³´X¤H¦¬¹L */
+ hx->score += nrcpt;
+ if (hx->score >= SPAM_TITLE_LIMIT)
+ fprintf(flog, "TITLE\t[%d] %s\n", ap->sno, ap->title);
+
+ /* ¦pªG³o¦¸¨Ó«H©M¤W¦¸¦P¼ÐÃDªº¨Ó«HÀÉ®×®t¤£¦h¤j¡A¨º»ò³o¦¸¨Ó«H«Ü¥i¯à¬O¼s§i«H */
+ score = nrcpt;
+ delta = hx->fsize - ap->used;
+ if (delta >= -16 && delta <= 16)
+ score += SPAM_MFROM_LIMIT >> 5;
+ hx->fsize = ap->used; /* °O¿ý¥Î³o¼ÐÃDªº³Ì«á¤@«Ê«H¤§Àɮפj¤p */
+ }
+ else
+ {
+ score = 1;
+ }
+
+ /* ------------------------------------------------- */
+ /* Àˬd³o­Ó from ±H¶i¨Óªº«H¦³µL¶W¹L SPAM_MFROM_LIMIT */
+ /* ------------------------------------------------- */
+
+ he = ht_add(mfrom_ht, addr);
+ he->uptime = uptime;
+
+ if (nrcpt > 0) /* ¦³ title_ht ªº HashEntry hx-> */
+ {
+ /* itoc.060420.µù¸Ñ: ¦³¨Ç¨Ï¥ÎªÌ·|±q§Oªº BBS ¯¸¤@¦¸Âà±H¾ã­Ó°Q½×¦êªº¤å³¹(¦P¼ÐÃD)
+ ¨Ó¥»¯¸¡A´N·|¦]¬°¤U­±³o±ø rule ¦Ó³Qµø¬°¼s§i°Ó */
+
+ /* ¦pªG³o­Ó from ¦b³o¦¸¨Ó«H©M¥L¦Û¤v¤W¦¸¨Ó«Hªº¼ÐÃD¬Û¦P¡A¨º»ò³o­Ó from «Ü¥i¯à¬O¼s§i°Ó */
+ if (he->ttl == hx)
+ score += SPAM_MFROM_LIMIT >> 4;
+ else
+ he->ttl = hx; /* °O¿ý³o­Ó from ¦b³o¦¸¨Ó«Hªº¼ÐÃD */
+ }
+
+ he->score += score;
+
+ if (he->score >= SPAM_MFROM_LIMIT)
+ {
+ unmail_root = acl_add(unmail_root, addr);
+ spam_add(he);
+ fprintf(flog, "SPAM-F\t[%d] %s\n", ap->sno, addr);
+
+ sprintf(ap->memo, "SPAM : %s", addr);
+ *delimiter = '\n';
+ }
+
+ /* ------------------------------------------------- */
+
+ if (*delimiter) /* ­Y *delimiter == '\n'¡Aªí¥Ü¶W¹L SPAM_*_LIMIT */
+ {
+ MYDOG;
+ mta_memo(ap, 0);
+ MYDOG;
+ return 0;
+ }
+ }
+
+ /* --------------------------------------------------- */
+ /* mail user */
+ /* --------------------------------------------------- */
+
+ if (rcpt)
+ {
+ char *dot;
+
+ addr = ap->addr;
+ if (dot = strchr(addr, '.'))
+ {
+ if (!str_cmp(dot, ".bbs@" MYHOSTNAME)) /* itoc.020125.µù¸Ñ: ­Y±H«HªÌ¬°¥»¯¸µo«H¡A¥u¯d ID */
+ *dot = '\0';
+ else
+ dot = NULL;
+ }
+
+ do
+ {
+ str = rcpt->userid;
+ if (ap->letter)
+ bbs_mail(ap, data, str);
+ else
+ bbs_brd(ap, data, str);
+ } while (rcpt = rcpt->rnext);
+
+ if (dot)
+ *dot = '.';
+ }
+
+ return 0;
+}
+
+
+/* ----------------------------------------------------- */
+/* command dispatch */
+/* ----------------------------------------------------- */
+
+
+static void
+agent_free_rcpt(ap)
+ Agent *ap;
+{
+ RCPT *rcpt, *next;
+
+ ap->nrcpt = 0;
+
+ if (rcpt = ap->rcpt)
+ {
+ ap->rcpt = NULL;
+ do
+ {
+ next = rcpt->rnext;
+ free(rcpt);
+ } while (rcpt = next);
+ }
+}
+
+
+static void
+agent_spam(ap)
+ Agent *ap;
+{
+ int cc;
+ char *from;
+ FILE *fp;
+
+ from = ap->from;
+ cc = *from;
+ if (cc == ' ' || cc == '\0')
+ return;
+
+ unmail_root = acl_add(unmail_root, from);
+
+ if (fp = fopen(UNMAIL_ACLFILE, "a"))
+ {
+ struct tm *p;
+
+ p = localtime(&ap->uptime);
+ /* Thor.990329: y2k */
+ fprintf(fp, "%s # 100 %02d/%02d/%02d %02d:%02d:%02d\n",
+ from,
+ p->tm_year % 100, p->tm_mon + 1, p->tm_mday,
+ p->tm_hour, p->tm_min, p->tm_sec);
+ fclose(fp);
+ }
+}
+
+
+/* ----------------------------------------------------- */
+/* command dispatch */
+/* ----------------------------------------------------- */
+
+
+static void
+cmd_what(ap)
+ Agent *ap;
+{
+ ap->xerro++;
+ agent_reply(ap, "500 Command unrecognized");
+}
+
+
+static void
+cmd_help(ap)
+ Agent *ap;
+{
+ agent_reply(ap, "214-Commands:\r\n"
+ "214- HELO MAIL RCPT DATA\r\n"
+ "214- NOOP QUIT RSET HELP\r\n"
+ "214-See RFC-821 for more info.\r\n"
+ "214 End of HELP info");
+}
+
+
+static void
+cmd_noop(ap)
+ Agent *ap;
+{
+ agent_reply(ap, "250 OK");
+}
+
+
+static void
+agent_reset(ap)
+ Agent *ap;
+{
+ MYDOG;
+
+#ifdef HELO_CHECK
+ ap->mode &= AM_HELO;
+#else
+ ap->mode = 0;
+#endif
+
+ ap->memo[0] = '\0';
+ ap->fpath[0] = '\0';
+ ap->from[0] = '\0';
+ ap->title[0] = '\0';
+ ap->addr[0] = '\0';
+ ap->nick[0] = '\0';
+ MYDOG;
+ agent_free_rcpt(ap);
+ MYDOG;
+}
+
+
+static void
+cmd_rset(ap)
+ Agent *ap;
+{
+ agent_reset(ap);
+ agent_reply(ap, "250 Reset state");
+}
+
+
+/* ----------------------------------------------------- */
+/* 0 : OK , -1 : error, -2 : <>, 1 : relayed */
+/* ----------------------------------------------------- */
+
+
+static int
+parse_addr(addr, user, domain)
+ char *addr, **user, **domain;
+{
+ int ch, relay;
+ char *ptr, *str;
+
+ /* <[@domain_list:]user@doamin> */
+
+ addr = strchr(addr, '<');
+ if (!addr)
+ return -1;
+
+ ptr = strrchr(++addr, '>');
+ if (!ptr)
+ return -1;
+
+ if (ptr == addr)
+ return -2; /* <> null domain */
+
+ *ptr = '\0';
+ if (ptr = strrchr(addr, ':'))
+ {
+ relay = 1;
+ addr = ptr + 1;
+ }
+ else
+ {
+ relay = 0;
+ }
+
+ /* check the E-mail address format */
+
+ *user = str = addr;
+
+ /* Thor.000607: dequote */
+ if (**user == '"')
+ (*user)++;
+
+ for (ptr = NULL; ch = *addr; addr++)
+ {
+ if (ch == '@')
+ {
+ if (ptr)
+ return -1;
+
+ ptr = addr;
+ continue;
+ }
+
+ /* Thor.000607: dequote */
+ if (ch == '"')
+ {
+ *addr = 0;
+ continue;
+ }
+
+ if (ch <= 32 || ch >= 127)
+ return -1;
+ if (strchr("<>()[]\\,;:", ch))
+ return -1;
+ }
+
+ if (!ptr)
+ return -1;
+
+ *ptr++ = '\0';
+ *domain = ptr;
+
+ if (!*user) /* more ... */
+ {
+ return -1;
+ }
+
+ return relay;
+}
+
+
+static void
+cmd_mail(ap)
+ Agent *ap;
+{
+ char *data, *from, *user, *domain, *ptr;
+ int cc;
+
+#ifdef HELO_CHECK
+ if (!(ap->mode & AM_HELO))
+ {
+ ap->xerro++;
+ agent_reply(ap, "503 Polite people say HELO first");
+ return;
+ }
+#endif
+
+ from = ap->from;
+ if (*from)
+ {
+ ap->xerro++;
+ agent_reply(ap, "503 Sender already specified");
+ return;
+ }
+
+ /* mail from:<[@domain_list:]user@doamin> */
+
+ data = ap->data;
+ MYDOG;
+ cc = parse_addr(data, &user, &domain);
+ MYDOG;
+
+ if (cc)
+ {
+ if (cc == -2) /* null domain */
+ {
+ from[0] = ' ';
+ from[1] = '\0';
+
+ strcpy(data, "250 Sender ok\r\n");
+ ap->used = strlen(data);
+ ap->state = CS_SEND;
+ return;
+ }
+
+ ap->xerro++;
+ agent_reply(ap, cc < 0 ? "501 Syntax error" :
+ "551 we dont accept relayed mail");
+ return;
+ }
+
+ /* Thor.990811: §ì°²³yªºdomain */
+ MYDOG;
+ if (is_forge(domain))
+ {
+ ap->xspam++;
+ ap->mode |= AM_SPAM;
+ agent_log(ap, "FORGE", domain);
+ agent_reply(ap, "501 Sender host must exist");
+ return;
+ }
+
+ /* Thor.990817: ¦pªGhost°²³y¡A³s¥[³£¤£·Q¥[¤Jmfrom_ht */
+ MYDOG;
+ ptr = data + MAX_CMD_LEN;
+ sprintf(ptr, "%s@%s", user, domain);
+ ht_add(mfrom_ht, ptr);
+
+ MYDOG;
+ /* Àˬd MAIL FROM: ¬O§_¦b¶Â¥Õ¦W³æ¤W */
+ if (acl_spam(user, domain))
+ {
+ ap->xspam++;
+ ap->mode |= AM_SPAM;
+ agent_log(ap, "SPAM-M", ptr);
+ agent_reply(ap, "550 you are not in my access list");
+ return;
+ }
+
+ str_ncpy(from, ptr, sizeof(ap->from));
+ sprintf(data, "250 <%s> Sender ok\r\n", ptr);
+ ap->used = strlen(data);
+ ap->state = CS_SEND;
+ MYDOG;
+}
+
+
+static int /* AM_VALID:»{ÃÒ«H AM_BBSADM:bbsadm 0:¤@¯ë«H -1:©Ú¦¬ */
+is_rcpt(rcpt, letter)
+ char *rcpt;
+ int *letter; /* ¦^¶Ç 1:±Hµ¹ *.bbs@ 0:±Hµ¹ *.brd@ */
+{
+ int len;
+ char *str, fpath[64];
+ char *alias[] = ADM_ALIASES;
+
+#ifdef EMAIL_JUSTIFY
+ if (!str_cmp(rcpt, "bbsreg"))
+ return AM_VALID;
+#endif
+
+ /* check the aliases */
+
+ for (len = 0; str = alias[len]; len++)
+ {
+ if (!str_cmp(rcpt, str))
+ return AM_BBSADM;
+ }
+
+ /* check the users */
+
+ len = strlen(rcpt);
+ if (len > 4) /* ".bbs" ©Î ".brd" => 4 */
+ {
+ str = rcpt + len - 4;
+
+ if (!str_cmp(str, ".bbs"))
+ {
+ if (len <= IDLEN + 4)
+ {
+ *str = '\0';
+ str_lower(rcpt, rcpt);
+ sprintf(fpath, "usr/%c/%s/@", *rcpt, rcpt);
+ if (dashd(fpath))
+ {
+ *letter = 1;
+ return 0;
+ }
+ }
+ }
+ else if (!str_cmp(str, ".brd"))
+ {
+ if (len <= BNLEN + 4)
+ {
+ *str = '\0';
+ if (getbrdname(rcpt))
+ {
+ *letter = 0;
+ return 0;
+ }
+ }
+ }
+ }
+
+ return -1;
+}
+
+
+static void
+cmd_rcpt(ap)
+ Agent *ap;
+{
+ char *data, *user, *domain;
+ int cc, letter;
+ RCPT *rcpt;
+
+ if (!ap->from[0])
+ {
+ ap->xerro++;
+ agent_reply(ap, "503 MAIL first");
+ return;
+ }
+
+ if (ap->nrcpt > MAX_RCPT)
+ {
+ /* maybe spammer */
+
+ /* Thor.000131: ³o¼Ë´N¤£¥Î¤@ª½¥h§Runmail.acl­«ÂЪºentry :) */
+ if (!(ap->mode & AM_SPAM))
+ agent_spam(ap); /* ¦P¤@«H±Hµ¹¤Ó¦h¦¬«HªÌª½±µ¾×±¼ */
+
+ ap->mode |= AM_SPAM;
+ ap->xspam += ap->nrcpt;
+ agent_log(ap, "SPAM-R", "too many recipients");
+ agent_reply(ap, "552 Too many recipients");
+ return;
+ }
+
+ /* rcpt to:<[@domain_list:]user@doamin> */
+
+ data = ap->data;
+ cc = parse_addr(data, &user, &domain);
+ if (cc)
+ {
+ ap->xerro++;
+ agent_reply(ap, cc < 0 ? "501 Syntax error" : "551 we dont accept relayed mail");
+
+ return;
+ }
+
+ if (domain == NULL)
+ {
+ agent_reply(ap, "550 null domain");
+ return;
+ }
+
+ /* if (str_cmp(domain, MYHOSTNAME)) */
+ if (!is_host_alias(domain)) /* HOST_ALIAS ¸Ìªº³£¥i¥H */
+ {
+ ap->xerro++;
+ agent_reply(ap, "550 we dont relay mail");
+ return;
+ }
+
+ ht_add(mrcpt_ht, user);
+
+ cc = is_rcpt(user, &letter);
+
+ if (cc < 0) /* µL¦¹¨Ï¥ÎªÌ©Î¬ÝªO */
+ {
+ ap->xerro++;
+ agent_reply(ap, "550 no such user or board");
+ return;
+ }
+
+ if (cc) /* AM_VALID¡BAM_BBSADM */
+ {
+ ap->mode |= cc;
+ }
+ else /* Thor.991130.µù¸Ñ: ¤@¯ë *.bbs@ ©Î *.brd@ ±¡ªp */
+ {
+#if 1 /* Thor.981227: ½T©w¤£¬O bbsreg ¤~¾×¡Adelay ¾×³s½u */
+ /* format: sprintf(servo_ident, "[%d] %s ip:%08x", ++servo_sno, rhost, csin.sin_addr.s_addr); */
+ char rhost[256], *s;
+
+ if (s = strchr(ap->ident, ' '))
+ {
+ strcpy(rhost, s + 1);
+ if (s = strchr(rhost, ' '))
+ {
+ *s = '\0';
+
+ /* Àˬd µo«Hªº¾÷¾¹ ¬O§_¦b¶Â¥Õ¦W³æ¤W */
+ if (acl_spam("*", rhost))
+ {
+ ap->mode |= AM_SPAM;
+ agent_log(ap, "SPAM-M", rhost);
+ agent_reply(ap, "550 deny connection");
+ return;
+ }
+ }
+ }
+#endif
+
+#if 1 /* Thor.981223: ½T©w¤£¬O bbsreg ´N¾× */
+ if (ap->mode & AM_SPAM)
+ {
+ agent_reply(ap, "550 spam mail");
+ return;
+ }
+#endif
+
+ ap->letter = letter;
+ ap->nrcpt++;
+ cc = strlen(user) + 1;
+ MYDOG;
+ rcpt = (RCPT *) malloc(sizeof(RCPT) + cc);
+ MYDOG;
+ if (!rcpt) /* Thor.990205: °O¿ýªÅ¶¡¤£°÷ */
+ logit("ERROR", "Not enough space in cmd_rcpt()");
+
+ rcpt->rnext = ap->rcpt;
+ memcpy(rcpt->userid, user, cc);
+ ap->rcpt = rcpt;
+ }
+
+ agent_reply(ap, "250 Recipient ok");
+}
+
+
+static void
+cmd_helo(ap)
+ Agent *ap;
+{
+ char *data;
+
+#ifdef SMTP_CONN_CHECK
+ /* Thor.990806: check if the peer is a normal smtp server, otherwise possibly a dialup spammer */
+
+ struct sockaddr_in sin;
+ int sock = sizeof(sin);
+
+ if (getpeername(ap->sock, (struct sockaddr *) & sin, &sock) >= 0)
+ {
+ sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (sock >= 0)
+ {
+ int t;
+
+ sin.sin_port = htons(BMTA_PORT);
+ t = connect(sock, (struct sockaddr *) & sin, sizeof sin);
+ close(sock);
+
+ if (t < 0)
+ {
+ data = ap->data;
+ strcpy(data, "550 reject non-smtp server\r\n");
+ ap->used = strlen(data);
+ ap->state = CS_FLUSH;
+ return;
+ }
+ }
+ }
+#endif
+
+ data = ap->data;
+ sprintf(data, "250 Hello %s\r\n", ap->ident);
+ ap->used = strlen(data);
+ ap->state = CS_SEND;
+
+#ifdef HELO_CHECK
+ ap->mode |= AM_HELO;
+#endif
+}
+
+
+static void
+cmd_data(ap)
+ Agent *ap;
+{
+ int mode;
+
+ mode = ap->mode;
+ if (!ap->rcpt && !(mode & (AM_VALID | AM_BBSADM)))
+ {
+ ap->xerro++;
+ agent_reply(ap, "503 RCPT first");
+ return;
+ }
+
+ ap->mode = mode | AM_DATA;
+ agent_reply(ap, "354 go ahead");
+}
+
+
+static void
+cmd_quit(ap)
+ Agent *ap;
+{
+ char *data;
+
+ data = ap->data;
+ strcpy(data, "221 bye\r\n");
+ ap->used = strlen(data);
+ ap->state = CS_FLUSH;
+}
+
+
+static void
+cmd_nogo(ap)
+ Agent *ap;
+{
+ agent_reply(ap, "502 operation not allowed");
+ return;
+}
+
+
+static Command cmd_table[] =
+{
+ cmd_helo, "helo", "HELO <hostname> - Introduce yourself",
+ cmd_nogo, "ehlo", "", /* Thor.980929: ¤£¤ä´©enhanced smtp */
+ cmd_mail, "mail", "MAIL FROM: <sender> - Specifies the sender",
+ cmd_rcpt, "rcpt", "RCPT TO: <recipient> - Specifies the recipient",
+ cmd_data, "data", "",
+ cmd_quit, "quit", "QUIT - Exit sendmail (SMTP)",
+ cmd_noop, "noop", "NOOP - Do nothing",
+ cmd_rset, "rset", "RSET - Resets the system",
+ cmd_help, "help", "HELP [ <topic> ] - gives help info",
+ cmd_nogo, "expn", "",
+ cmd_nogo, "vrfy", "",
+ cmd_what, NULL, NULL
+};
+
+
+/* ----------------------------------------------------- */
+/* send output to client */
+/* ----------------------------------------------------- */
+/* return value : */
+/* > 0 : bytes sent */
+/* = 0 : close this agent */
+/* < 0 : there are some error, but keep trying */
+/* ----------------------------------------------------- */
+
+
+static int
+agent_send(ap)
+ Agent *ap;
+{
+ int csock, len, cc;
+ char *data;
+
+ csock = ap->sock;
+ data = ap->data;
+ len = ap->used;
+ cc = send(csock, data, len, 0);
+
+#ifdef AM_DEBUG
+ if (ap->mode & AM_DEBUG)
+ {
+ char buf[1024];
+
+ sprintf(buf, "%s\t[%d]\tbmtad>>>\n", Now(), ap->sno);
+ f_cat(BMTA_DEBUGFILE, buf);
+ str_ncpy(buf, data, len + 1);
+ f_cat(BMTA_DEBUGFILE, buf);
+ sprintf(buf, "\t[%d]\tbmtad<<<\n", ap->sno);
+ f_cat(BMTA_DEBUGFILE, buf);
+ }
+#endif
+
+ if (cc < 0)
+ {
+ cc = errno;
+ if (cc != EWOULDBLOCK)
+ {
+ agent_log(ap, "SEND", strerror(cc));
+ return 0;
+ }
+
+ /* would block, so leave it to do later */
+ return -1;
+ }
+
+ if (cc == 0)
+ return -1;
+
+ len -= cc;
+ ap->used = len;
+ if (len)
+ {
+ memcpy(data, data + cc, len);
+ return cc;
+ }
+
+ if (ap->state == CS_FLUSH)
+ {
+ shutdown(csock, 2);
+ close(csock);
+ ap->sock = -1;
+ return 0;
+ }
+
+ ap->state = CS_RECV;
+ return cc;
+}
+
+
+/* ----------------------------------------------------- */
+/* receive request from client */
+/* ----------------------------------------------------- */
+
+
+static int
+agent_recv(ap)
+ Agent *ap;
+{
+ int cc, mode, size, used;
+ char *data, *head, *dest;
+
+ mode = ap->mode;
+ used = ap->used;
+ data = ap->data;
+
+ if (mode & AM_DATA)
+ {
+ if (used <= 0)
+ {
+ /* pre-set data */
+
+ *data = '\n';
+ used = 1;
+ }
+ else
+ {
+ /* check the available space */
+
+ size = ap->size;
+ cc = size - used;
+
+ if (cc < TCP_RCVSIZ + 3)
+ {
+ if (size < MAX_DATA_SIZE)
+ {
+ size += TCP_RCVSIZ + (size >> 2);
+
+ if (data = (char *) realloc(data, size))
+ {
+ ap->data = data;
+ ap->size = size;
+ }
+ else
+ {
+ fprintf(flog, "ERROR\t[%d] malloc: %d\n", ap->sno, size);
+
+#ifdef DEBUG
+ fflush(flog);
+#endif
+
+ return 0;
+ }
+ }
+ else
+ {
+ /* DATA ¤Óªø¤F¡A³q³q¦Y¤U¨Ó¡A¨Ã°²³] mail header ¤£·|¶W¹L HEADER_SIZE */
+
+#define HEADER_SIZE 8192
+ data[HEADER_SIZE - 2] = data[used - 2];
+ data[HEADER_SIZE - 1] = data[used - 1];
+ used = HEADER_SIZE;
+#undef HEADER_SIZE
+ ap->mode = (mode |= AM_SWALLOW);
+ ap->xerro++;
+ fprintf(flog, "ERROR\t[%d] data too long\n", ap->sno);
+
+#ifdef DEBUG
+ fflush(flog);
+#endif
+ }
+ }
+ }
+ }
+
+ head = data + used;
+ MYDOG;
+ cc = recv(ap->sock, head, TCP_RCVSIZ, 0);
+ MYDOG;
+
+ if (cc <= 0)
+ {
+ cc = errno;
+ MYDOG;
+ if (cc != EWOULDBLOCK)
+ {
+ agent_log(ap, "RECV", strerror(cc));
+ return 0;
+ }
+
+ /* would block, so leave it to do later */
+
+ return -1;
+ }
+ MYDOG;
+
+ head[cc] = '\0';
+ ap->xsize += cc;
+
+#ifdef AM_DEBUG
+ if (mode & AM_DEBUG)
+ {
+ char buf[80];
+ sprintf(buf, "%s\t[%d]\tpeer>>>\n", Now(), ap->sno);
+ f_cat(BMTA_DEBUGFILE, buf);
+ f_cat(BMTA_DEBUGFILE, head);
+ sprintf(buf, "\t[%d]\tpeer<<<\n", ap->sno);
+ f_cat(BMTA_DEBUGFILE, buf);
+ }
+#endif
+
+ /* --------------------------------------------------- */
+ /* DATA mode */
+ /* --------------------------------------------------- */
+
+ if (mode & AM_DATA)
+ {
+ dest = head - 1;
+
+ for (;;)
+ {
+ cc = *head;
+
+ if (!cc)
+ {
+ ap->used = dest - data + 1;
+ return 1;
+ }
+
+ head++;
+
+ if (cc == '\r')
+ continue;
+
+ if (cc == '\n')
+ {
+ /* Thor.990604: \n«á°¨¤Wcheck, ¥H¥ß¨è±Æ°£ \r\n.\r\n ªºª¬ªp */
+ used = dest - data + 1;
+ if (used >= 2 && *dest == '.' && dest[-1] == '\n')
+ break; /* end of mail body */
+
+ for (;;)
+ {
+ used = *dest;
+
+ if (used == ' ' || used == '\t')
+ {
+ dest--; /* strip the trailing space */
+ continue;
+ }
+ break;
+ }
+
+ /* Thor.990604: strip leading ".." to "." */
+ {
+ char *first = dest;
+
+ while (first >= data && *first != '\n')
+ first--;
+ first++;
+
+ if (first <= dest && *first == '.')
+ {
+ while (first < dest)
+ {
+ *first = first[1];
+ first++;
+ }
+ dest--;
+ }
+ }
+ }
+
+ *++dest = cc;
+ }
+ MYDOG;
+
+#if 1 /* Thor.990906: null from ´N±µ¦a:p */
+ cc = *ap->from;
+ if (cc == ' ' || cc == '\0')
+ {
+ agent_reply(ap, "250 Message dropped");
+ agent_log(ap, "SPAM-NULL", ap->ident);
+ MYDOG;
+ return -1;
+ }
+#endif
+
+ if (mode & AM_SWALLOW)
+ {
+ agent_reset(ap);
+ agent_reply(ap, "552 Too much mail data");
+ MYDOG;
+ return -1;
+ }
+
+ /* strip the trailing empty lines */
+
+ dest -= 2; /* 3; */
+
+ while (*dest == '\n' && dest > data)
+ dest--;
+ dest += 2;
+ *dest = '\0';
+
+ ap->used = dest - data;
+
+ MYDOG;
+ /* Thor.981223: ½T©w¤£¬O bbsreg ¤~¾× */
+ if (!(mode & (AM_VALID | AM_BBSADM)) && (mode & AM_SPAM))
+ {
+ sprintf(ap->memo, "SPAM : %s", ap->from);
+ mta_memo(ap, 0);
+
+ agent_reply(ap, "250 Message dropped");
+ agent_log(ap, "SPAM", "mail");
+ }
+ else
+ {
+ MYDOG;
+ cc = mta_mailer(ap);
+ MYDOG;
+ if (!cc)
+ {
+ agent_reply(ap, "250 Message accepted");
+ MYDOG;
+ }
+ else if (cc < 0)
+ {
+ ap->xspam++;
+ MYDOG;
+ agent_log(ap, "SPAM", "mail");
+ MYDOG;
+ }
+ }
+
+ MYDOG;
+ agent_reset(ap);
+ MYDOG;
+ return 1;
+ }
+
+ /* --------------------------------------------------- */
+ /* command mode */
+ /* --------------------------------------------------- */
+
+ used += cc;
+
+ if (used >= MAX_CMD_LEN)
+ {
+ fprintf(flog, "CMD\t[%d] too long (%d) %.32s\n",
+ ap->sno, used, data);
+
+#ifdef DEBUG
+ fflush(flog);
+#endif
+
+ ap->mode = (mode |= AM_DROP);
+ ap->xerro += 10; /* are you hacker ? */
+ used = 32;
+ }
+
+ while (cc = *head)
+ {
+ if (cc == '\r' || cc == '\n')
+ {
+ Command *cmd;
+
+ *head = '\0';
+
+ if (mode & AM_DROP)
+ {
+ agent_reset(ap);
+ agent_reply(ap, "552 command too long");
+ MYDOG;
+ return -1;
+ }
+
+ for (cmd = cmd_table; head = cmd->cmd; cmd++)
+ {
+ if (!str_ncmp(data, head, 4))
+ break;
+ }
+
+ MYDOG;
+ ap->used = 0;
+
+ sprintf(gtext, "ip:%08x ", ap->ip_addr);
+ str_ncpy(gtext + 3 + 8 + 1, data, 50);
+
+ (*cmd->func) (ap);
+
+ *gtext = 0;
+ MYDOG;
+ return 1;
+ }
+
+ if (cc == '\t')
+ *head = ' ';
+
+ head++;
+ }
+
+ MYDOG;
+ ap->used = used;
+ return 1;
+}
+
+
+/* ----------------------------------------------------- */
+/* close a connection & release its resource */
+/* ----------------------------------------------------- */
+
+
+static void
+agent_fire(ap)
+ Agent *ap;
+{
+ int num;
+ char *data, *key, xerro[32], xspam[32];
+
+ num = ap->sock;
+ if (num > 0)
+ {
+ MYDOG;
+ fcntl(num, F_SETFL, M_NONBLOCK);
+ MYDOG;
+
+#define MSG_ABORT "\r\n450 buggy, closing ...\r\n"
+ send(num, MSG_ABORT, sizeof(MSG_ABORT) - 1, 0);
+#undef MSG_ABORT
+ MYDOG;
+ shutdown(num, 2);
+ MYDOG;
+ close(num);
+ MYDOG;
+
+ key = "END";
+ }
+ else
+ {
+ key = "BYE";
+ }
+
+ MYDOG;
+ agent_free_rcpt(ap);
+ MYDOG;
+
+ /* log */
+
+ data = ap->data;
+
+ *xerro = *xspam = '\0';
+ if ((num = ap->xerro) > 0)
+ sprintf(xerro, " X%d", num);
+ if ((num = ap->xspam) > 0)
+ sprintf(xspam, " Z%d", num);
+
+ sprintf(data, "[%d] T%d R%d D%d S%d%s%s", ap->sno, time(0) - ap->tbegin,
+ ap->xrcpt, ap->xdata, ap->xsize, xerro, xspam);
+ logit(key, data);
+
+ MYDOG;
+ free(data);
+ MYDOG;
+}
+
+
+/* ----------------------------------------------------- */
+/* accept a new connection */
+/* ----------------------------------------------------- */
+
+
+static char servo_ident[128];
+
+
+static int
+agent_accept()
+{
+ int csock;
+ int value;
+ struct sockaddr_in csin;
+ char rhost[160], *ident;
+
+ for (;;)
+ {
+ value = sizeof(csin);
+ MYDOG;
+ csock = accept(0, (struct sockaddr *) & csin, &value);
+ MYDOG;
+ /* if (csock > 0) */
+ if (csock >= 0) /* Thor.000126: more proper */
+ break;
+
+ csock = errno;
+ if (csock != EINTR)
+ {
+ logit("ACCEPT", strerror(csock));
+ return -1;
+ }
+
+ MYDOG;
+ while (waitpid(-1, NULL, WNOHANG | WUNTRACED) > 0);
+ MYDOG;
+ }
+ MYDOG;
+
+ value = 1;
+
+ /* Thor.000511: µù¸Ñ: don't delay send to coalesce(Áp¦X) packets */
+ setsockopt(csock, IPPROTO_TCP, TCP_NODELAY, (char *) &value, sizeof(value));
+
+ /* --------------------------------------------------- */
+ /* check remote host / user name */
+ /* --------------------------------------------------- */
+
+ MYDOG;
+
+ /* Thor.001026: °lÂÜ¥d¦b­þ */
+ sprintf(gtext, "ip:%08x", csin.sin_addr.s_addr);
+
+ hht_look((HostAddr *) &csin.sin_addr, rhost);
+
+ *gtext = 0;
+ MYDOG;
+ /* Thor.981207: °lÂÜ ip address */
+ sprintf(ident = servo_ident, "[%d] %s ip:%08x", ++servo_sno, rhost, csin.sin_addr.s_addr);
+
+ MYDOG;
+ ht_add(mhost_ht, rhost);
+ logit("CONN", ident);
+ MYDOG;
+ return csock;
+}
+
+
+/* ----------------------------------------------------- */
+/* signal routines */
+/* ----------------------------------------------------- */
+
+
+#ifdef SERVER_USAGE
+static void
+servo_usage()
+{
+ struct rusage ru;
+
+ if (getrusage(RUSAGE_SELF, &ru))
+ return;
+
+ fprintf(flog, "\n[Server Usage]\n\n"
+ " user time: %.6f\n"
+ " system time: %.6f\n"
+ " maximum resident set size: %lu P\n"
+ " integral resident set size: %lu\n"
+ " page faults not requiring physical I/O: %d\n"
+ " page faults requiring physical I/O: %d\n"
+ " swaps: %d\n"
+ " block input operations: %d\n"
+ " block output operations: %d\n"
+ " messages sent: %d\n"
+ " messages received: %d\n"
+ " signals received: %d\n"
+ " voluntary context switches: %d\n"
+ " involuntary context switches: %d\ngline: %d\ngtext: %s\n",
+
+ (double) ru.ru_utime.tv_sec + (double) ru.ru_utime.tv_usec / 1000000.0,
+ (double) ru.ru_stime.tv_sec + (double) ru.ru_stime.tv_usec / 1000000.0,
+ ru.ru_maxrss,
+ ru.ru_idrss,
+ ru.ru_minflt,
+ ru.ru_majflt,
+ ru.ru_nswap,
+ ru.ru_inblock,
+ ru.ru_oublock,
+ ru.ru_msgsnd,
+ ru.ru_msgrcv,
+ ru.ru_nsignals,
+ ru.ru_nvcsw,
+ ru.ru_nivcsw, gline, gtext);
+
+ fflush(flog);
+}
+#endif
+
+
+#define SS_CONFIG 1
+#define SS_SHUTDOWN 2
+
+
+static int servo_state;
+
+
+static void
+sig_hup()
+{
+ servo_state |= SS_CONFIG;
+}
+
+
+static void
+sig_term() /* graceful termination */
+{
+ servo_state |= SS_SHUTDOWN;
+}
+
+
+static void
+sig_abort(sig)
+ int sig;
+{
+ char buf[80];
+
+ sprintf(buf, "abort: %d, errno: %d, gline: %d", sig, errno, gline);
+ logit("EXIT", buf);
+ fclose(flog);
+ exit(0);
+}
+
+
+static void
+reaper()
+{
+ while (waitpid(-1, NULL, WNOHANG | WUNTRACED) > 0);
+}
+
+
+static void
+servo_signal()
+{
+ struct sigaction act;
+
+ /* sigblock(sigmask(SIGPIPE)); *//* Thor.981206: ²Î¤@ POSIX ¼Ð·Ç¥Îªk */
+
+ /* act.sa_mask = 0; *//* Thor.981105: ¼Ð·Ç¥Îªk */
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+
+ act.sa_handler = sig_term; /* forced termination */
+ sigaction(SIGTERM, &act, NULL);
+
+ act.sa_handler = sig_abort; /* forced termination */
+ sigaction(SIGSEGV, &act, NULL); /* if rlimit violate */
+ sigaction(SIGBUS, &act, NULL);
+
+#if 1 /* Thor.990203: §ì signal */
+ sigaction(SIGURG, &act, NULL);
+ sigaction(SIGXCPU, &act, NULL);
+ sigaction(SIGXFSZ, &act, NULL);
+
+#ifdef SOLARIS
+ sigaction(SIGLOST, &act, NULL);
+ sigaction(SIGPOLL, &act, NULL);
+ sigaction(SIGPWR, &act, NULL);
+#endif
+
+#ifdef LINUX
+ sigaction(SIGSYS, &act, NULL);
+ /* sigaction(SIGEMT, &act, NULL); */
+ /* itoc.010317: §Úªº linux ¨S¦³³o­Ó»¡ :p */
+#endif
+
+ sigaction(SIGFPE, &act, NULL);
+ sigaction(SIGWINCH, &act, NULL);
+ sigaction(SIGINT, &act, NULL);
+ sigaction(SIGQUIT, &act, NULL);
+ sigaction(SIGILL, &act, NULL);
+ sigaction(SIGTRAP, &act, NULL);
+ sigaction(SIGABRT, &act, NULL);
+ sigaction(SIGTSTP, &act, NULL);
+ sigaction(SIGTTIN, &act, NULL);
+ sigaction(SIGTTOU, &act, NULL);
+ sigaction(SIGVTALRM, &act, NULL);
+#endif
+
+ act.sa_handler = sig_hup; /* restart config */
+ sigaction(SIGHUP, &act, NULL);
+
+ act.sa_handler = reaper;
+ sigaction(SIGCHLD, &act, NULL);
+
+#ifdef SERVER_USAGE
+ act.sa_handler = servo_usage;
+ sigaction(SIGPROF, &act, NULL);
+#endif
+
+ /* Thor.981206: lkchu patch: ²Î¤@ POSIX ¼Ð·Ç¥Îªk */
+ /* ¦b¦¹­É¥Î sigset_t act.sa_mask */
+ sigaddset(&act.sa_mask, SIGPIPE);
+ sigprocmask(SIG_BLOCK, &act.sa_mask, NULL);
+}
+
+
+/* ----------------------------------------------------- */
+/* server core routines */
+/* ----------------------------------------------------- */
+
+
+static void
+servo_daemon(inetd)
+ int inetd;
+{
+ int fd, value;
+ char buf[80];
+ struct linger ld;
+ struct sockaddr_in sin;
+#ifdef HAVE_RLIMIT
+ struct rlimit limit;
+#endif
+
+ /* 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((time_t *) & value);
+ gmtime((time_t *) & value);
+ strftime(buf, 80, "%d/%b/%Y:%H:%M:%S", localtime((time_t *) & value));
+
+#ifdef HAVE_RLIMIT
+ /* --------------------------------------------------- */
+ /* adjust the resource limit */
+ /* --------------------------------------------------- */
+
+ getrlimit(RLIMIT_NOFILE, &limit);
+ limit.rlim_cur = limit.rlim_max;
+ setrlimit(RLIMIT_NOFILE, &limit);
+
+ limit.rlim_cur = limit.rlim_max = 16 * 1024 * 1024;
+ setrlimit(RLIMIT_FSIZE, &limit);
+
+ limit.rlim_cur = limit.rlim_max = 16 * 1024 * 1024;
+ setrlimit(RLIMIT_DATA, &limit);
+
+#ifdef SOLARIS
+#define RLIMIT_RSS RLIMIT_AS /* Thor.981206: port for solaris 2.6 */
+#endif
+
+ setrlimit(RLIMIT_RSS, &limit);
+
+ limit.rlim_cur = limit.rlim_max = 0;
+ setrlimit(RLIMIT_CORE, &limit);
+#endif
+
+ /* --------------------------------------------------- */
+ /* detach daemon process */
+ /* --------------------------------------------------- */
+
+ close(1);
+ close(2);
+
+ if (inetd)
+ return;
+
+ close(0);
+
+ if (fork())
+ exit(0);
+
+ setsid();
+
+ if (fork())
+ exit(0);
+
+ /* --------------------------------------------------- */
+ /* setup socket */
+ /* --------------------------------------------------- */
+
+ fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+
+ value = 1;
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &value, sizeof(value));
+
+ ld.l_onoff = ld.l_linger = 0;
+ setsockopt(fd, SOL_SOCKET, SO_LINGER, (char *) &ld, sizeof(ld));
+
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(BMTA_PORT);
+ sin.sin_addr.s_addr = htonl(INADDR_ANY);
+ memset((char *) &sin.sin_zero, 0, sizeof(sin.sin_zero));
+
+ if (bind(fd, (struct sockaddr *) & sin, sizeof(sin)) ||
+ listen(fd, TCP_BACKLOG))
+ exit(1);
+}
+
+
+/* Thor.990204: µù¸Ñ: ¨C¤Ñ¦­¤W 5:30 ¾ã²z¤@¦¸ */
+#define SERVO_HOUR 5
+#define SERVO_MIN 30
+
+
+static time_t
+fresh_time(uptime)
+ time_t uptime;
+{
+ struct tm *local;
+ int i;
+
+ local = localtime(&uptime);
+ i = (SERVO_HOUR - local->tm_hour) * 3600 + (SERVO_MIN - local->tm_min) * 60;
+ if (i < 120) /* «O¯d®É¶¡®t 120 ¬í */
+ i += BMTA_FRESH;
+ return (uptime + i);
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int n, sock, state;
+ time_t uptime, tcheck, tfresh, tscore;
+ Agent **FBI, *Scully, *Mulder, *agent;
+ fd_set rset, wset, xset;
+ static struct timeval tv = {BMTA_PERIOD, 0};
+
+ state = 0;
+
+ while ((n = getopt(argc, argv, "i")) != -1)
+ {
+ switch (n)
+ {
+ case 'i':
+ state = 1;
+ break;
+
+ default:
+ fprintf(stderr, "Usage: %s [options]\n"
+ "\t-i start from inetd with wait option\n",
+ argv[0]);
+ exit(0);
+ }
+ }
+
+ servo_daemon(state);
+
+ setgid(BBSGID);
+ setuid(BBSUID);
+ chdir(BBSHOME);
+ servo_signal();
+
+ mail_root = acl_load(MAIL_ACLFILE, mail_root);
+ unmail_root = acl_load(UNMAIL_ACLFILE, unmail_root);
+
+ log_open();
+ init_bshm();
+ init_ushm();
+ dns_init();
+
+ mrcpt_ht = ht_new(128, 0);
+ mhost_ht = ht_new(256, 0);
+ mfrom_ht = ht_new(256, 0);
+ title_ht = ht_new(512, 0);
+
+#ifdef FORGE_CHECK
+ forge_ht = ht_new(256, 0);
+#endif
+
+ uptime = time(0);
+ tcheck = uptime + BMTA_PERIOD;
+ tfresh = fresh_time(uptime);
+ tscore = uptime + 2 * 60 * 60;
+
+ Scully = Mulder = NULL;
+
+ for (;;)
+ {
+ /* maintain : resource and garbage collection */
+
+ uptime = time(0);
+ if (tcheck < uptime)
+ {
+ /* ----------------------------------------------- */
+ /* agent_audit (uptime - BMTA_TIMEOUT) */
+ /* ----------------------------------------------- */
+
+ tcheck = uptime - BMTA_TIMEOUT;
+
+ for (FBI = &Scully; agent = *FBI;)
+ {
+ if ((agent->uptime < tcheck) || (agent->xerro > BMTA_FAULT))
+ {
+ agent_fire(agent);
+
+ *FBI = agent->anext;
+
+ agent->anext = Mulder;
+ Mulder = agent;
+ }
+ else
+ {
+ FBI = &(agent->anext);
+ }
+ }
+
+ /* ----------------------------------------------- */
+ /* expire SPAM HashTable */
+ /* ----------------------------------------------- */
+
+ if (uptime > tscore)
+ {
+ tscore = uptime + 120 * 60;
+
+ /* ¶W¹L 120 ¤ÀÄÁ¨S·s¶i°O¿ý¡A´N¶}©l expire */
+
+ tcheck = uptime - 120 * 60;
+ ht_expire(mfrom_ht, tcheck);
+ ht_expire(mhost_ht, tcheck);
+ ht_expire(mrcpt_ht, tcheck);
+ ht_expire(title_ht, tcheck);
+
+ /* --------------------------------------------- */
+ /* expire DNS HostHashTable cache */
+ /* --------------------------------------------- */
+
+ hht_expire(uptime - 3 * 60 * 60);
+
+ /* ht_expire(forge_ht, tcheck); /* never expire */
+
+#ifdef FORGE_CHECK
+ /* Thor.990811: expire±¼¨S¥Îªºforge host */
+ ht_expire(forge_ht, tcheck);
+#endif
+ }
+
+ /* ----------------------------------------------- */
+ /* maintain SPAM & server log */
+ /* ----------------------------------------------- */
+
+ if (tfresh < uptime)
+ {
+ tfresh = uptime + BMTA_FRESH;
+ visit_fresh();
+#ifdef SERVER_USAGE
+ servo_usage();
+#endif
+ log_fresh();
+ }
+ else
+ {
+ fflush(flog);
+ }
+
+ tcheck = uptime + BMTA_PERIOD;
+ }
+
+ /* ------------------------------------------------- */
+ /* check servo operation state */
+ /* ------------------------------------------------- */
+
+ n = 0;
+
+ if (state = servo_state)
+ {
+ if (state & SS_CONFIG)
+ {
+ state ^= SS_CONFIG;
+
+ mail_root = acl_load(MAIL_ACLFILE, mail_root);
+ unmail_root = acl_load(UNMAIL_ACLFILE, unmail_root);
+ }
+
+ if (state & SS_SHUTDOWN) /* graceful shutdown */
+ {
+ n = -1;
+ close(0);
+ }
+
+ servo_state = state;
+ }
+
+ /* ------------------------------------------------- */
+ /* Set up the fdsets */
+ /* ------------------------------------------------- */
+
+ FD_ZERO(&rset);
+ FD_ZERO(&wset);
+ FD_ZERO(&xset);
+
+ if (n == 0)
+ FD_SET(0, &rset);
+
+ for (agent = Scully; agent; agent = agent->anext)
+ {
+ sock = agent->sock;
+ state = agent->state;
+
+ if (n < sock)
+ n = sock;
+
+ if (state == CS_RECV)
+ {
+ FD_SET(sock, &rset);
+ }
+ else
+ {
+ FD_SET(sock, &wset);
+ }
+
+ FD_SET(sock, &xset);
+ }
+
+ /* no active agent and ready to die */
+
+ if (n < 0)
+ {
+ break;
+ }
+
+ {
+ struct timeval tv_tmp = tv;
+ /* Thor.981221: for future reservation bug */
+ n = select(n + 1, &rset, &wset, &xset, &tv_tmp);
+ }
+
+ if (n == 0)
+ {
+ continue;
+ }
+
+ if (n < 0)
+ {
+ n = errno;
+ if (n != EINTR)
+ {
+ logit("SELECT", strerror(n));
+ }
+ continue;
+ }
+
+ /* ------------------------------------------------- */
+ /* serve active agents */
+ /* ------------------------------------------------- */
+
+ uptime = time(0);
+
+ MYDOG;
+ for (FBI = &Scully; agent = *FBI;)
+ {
+ sock = agent->sock;
+
+ if (FD_ISSET(sock, &wset))
+ {
+ MYDOG;
+ state = agent_send(agent);
+ MYDOG;
+ }
+ else if (FD_ISSET(sock, &rset))
+ {
+ MYDOG;
+ state = agent_recv(agent);
+ MYDOG;
+ }
+ else if (FD_ISSET(sock, &xset))
+ {
+ MYDOG;
+ state = 0;
+ MYDOG;
+ }
+ else
+ {
+ state = -1;
+ }
+
+ if (state == 0) /* fire this agent */
+ {
+ MYDOG;
+ agent_fire(agent);
+ MYDOG;
+
+ *FBI = agent->anext;
+
+ agent->anext = Mulder;
+ Mulder = agent;
+
+ continue;
+ }
+
+ if (state > 0)
+ {
+ agent->uptime = uptime;
+ }
+
+ FBI = &(agent->anext);
+ }
+ MYDOG;
+
+ /* ------------------------------------------------- */
+ /* serve new connection */
+ /* ------------------------------------------------- */
+
+ /* Thor.000209: ¦Ò¼{²¾«e¦¹³¡¤À, §K±o¥d¦b accept() */
+ if (FD_ISSET(0, &rset))
+ {
+ /* Thor.990319: check maximum connection number */
+ u_long ip_addr;
+ MYDOG;
+ sock = agent_accept();
+ MYDOG;
+
+ if (sock > 0)
+ {
+#if 1
+ /* Thor.990319: check maximum connection number */
+ int num = 0;
+
+ sscanf(strstr(servo_ident, "ip:") + 3, "%x", &ip_addr);
+ for (agent = Scully; agent; agent = agent->anext)
+ {
+ if (agent->ip_addr == ip_addr)
+ num++;
+ }
+ if (num >= MAX_HOST_CONN)
+ {
+ char buf[256];
+
+ sprintf(buf, "421 %s over max connection\r\n", MYHOSTNAME);
+
+ MYDOG;
+ fcntl(sock, F_SETFL, M_NONBLOCK); /* Thor.000511: ©È block */
+ MYDOG;
+ send(sock, buf, strlen(buf), 0); /* Thor.981206: ¸É­Ó0¤W¨Ó */
+ MYDOG;
+ shutdown(sock, 2);
+ MYDOG;
+ close(sock);
+ MYDOG;
+ logit("OVER", servo_ident);
+ MYDOG;
+ continue;
+ }
+#endif
+
+ if (agent = Mulder)
+ {
+ Mulder = agent->anext;
+ }
+ else
+ {
+ MYDOG;
+ agent = (Agent *) malloc(sizeof(Agent));
+ MYDOG;
+ if (!agent) /* Thor.990205: °O¿ýªÅ¶¡¤£°÷ */
+ logit("ERROR", "Not enough space in main()");
+ }
+
+ *FBI = agent;
+
+ /* variable initialization */
+
+ memset(agent, 0, sizeof(Agent));
+
+ agent->sock = sock;
+ agent->sno = servo_sno;
+ agent->state = CS_SEND;
+ agent->tbegin = agent->uptime = uptime;
+ strcpy(agent->ident, servo_ident);
+
+ /* Thor.990319: check maximum connection number */
+ agent->ip_addr = ip_addr;
+
+#ifdef AM_DEBUG
+ if (strstr(agent->ident, "8c7a4133"))
+ { /* Thor.990221: that's mail.cc.ntnu.edu.tw */
+ agent->mode |= AM_DEBUG;
+ }
+#endif
+
+ MYDOG;
+ agent->data = (char *) malloc(MIN_DATA_SIZE);
+ MYDOG;
+ if (!agent->data) /* Thor.990205: °O¿ýªÅ¶¡¤£°÷ */
+ logit("ERROR", "Not enough space in agent->data");
+ sprintf(agent->data, "220 " MYHOSTNAME " SMTP ready %s\r\n", servo_ident); /* Thor.981001: ¤£¥Î enhanced SMTP */
+ agent->used = strlen(agent->data);
+ agent->size = MIN_DATA_SIZE;
+ }
+ }
+
+ /* ------------------------------------------------- */
+ /* tail of main loop */
+ /* ------------------------------------------------- */
+ }
+
+ logit("EXIT", "shutdown");
+ fclose(flog);
+
+ exit(0);
+}
diff --git a/daemon/bnntpd.c b/daemon/bnntpd.c
new file mode 100644
index 0000000..42b2bdf
--- /dev/null
+++ b/daemon/bnntpd.c
@@ -0,0 +1,1500 @@
+/*-------------------------------------------------------*/
+/* bnntpd.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : BBS's NNTP daemon */
+/* create : 03/12/14 */
+/* update : / / */
+/* author : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+
+#include <sys/wait.h>
+#include <netinet/tcp.h>
+#include <sys/resource.h>
+
+
+#define SERVER_USAGE
+
+
+#define BNNTP_PIDFILE "run/bnntp.pid"
+#define BNNTP_LOGFILE "run/bnntp.log"
+
+
+#define BNNTP_PERIOD (60 * 15) /* ¨C 15 ¤ÀÄÁ check ¤@¦¸ */
+#define BNNTP_TIMEOUT (60 * 30) /* ¶W¹L 30 ¤ÀÄÁªº³s½u´Nµø¬°¿ù»~ */
+#define BNNTP_FRESH 86400 /* ¨C 1 ¤Ñ¾ã²z¤@¦¸ */
+
+
+#define TCP_BACKLOG 3
+#define TCP_BUFSIZ 4096
+#define TCP_LINSIZ 256
+#define TCP_RCVSIZ 2048
+
+
+#define MIN_DATA_SIZE 2048
+#define MAX_CMD_LEN 1024
+
+
+/* Thor.000425: POSIX ¥Î O_NONBLOCK */
+
+#ifndef O_NONBLOCK
+#define M_NONBLOCK FNDELAY
+#else
+#define M_NONBLOCK O_NONBLOCK
+#endif
+
+/* ----------------------------------------------------- */
+/* SMTP commands */
+/* ----------------------------------------------------- */
+
+
+typedef struct
+{
+ void (*func) ();
+ char *cmd;
+ int len; /* strlen(Command.cmd) */
+} Command;
+
+
+/* ----------------------------------------------------- */
+/* client connection structure */
+/* ----------------------------------------------------- */
+
+
+typedef struct Agent
+{
+ struct Agent *anext;
+ int sock;
+ int sno;
+ int state;
+ int mode;
+ unsigned int ip_addr;
+
+ time_t tbegin; /* ³s½u¶}©l®É¶¡ */
+ time_t uptime; /* ¤W¦¸¤U«ü¥Oªº®É¶¡ */
+
+ char newsgroup[BNLEN + 1]; /* ¥Ø«e©Ò¦bªº¬ÝªO */
+
+ char *data;
+ int used;
+ int size; /* ¥Ø«e data ©Ò malloc ªºªÅ¶¡¤j¤p */
+} Agent;
+
+
+static int servo_sno = 0;
+
+
+/* ----------------------------------------------------- */
+/* connection state */
+/* ----------------------------------------------------- */
+
+
+#define CS_FREE 0x00
+#define CS_RECV 0x01
+#define CS_SEND 0x02
+#define CS_FLUSH 0x03 /* flush data and quit */
+
+
+/* ----------------------------------------------------- */
+/* AM : Agent Mode */
+/* ----------------------------------------------------- */
+
+
+#define AM_DROP 0x010 /* swallow command */
+
+
+/* ----------------------------------------------------- */
+/* operation log and debug information */
+/* ----------------------------------------------------- */
+/* @START | ... | time */
+/* ----------------------------------------------------- */
+
+
+static FILE *flog;
+
+
+extern int errno;
+extern char *crypt();
+
+
+static void
+log_fresh()
+{
+ int count;
+ char fsrc[64], fdst[64];
+ char *fpath = BNNTP_LOGFILE;
+
+ if (flog)
+ fclose(flog);
+
+ count = 9;
+ do
+ {
+ sprintf(fdst, "%s.%d", fpath, count);
+ sprintf(fsrc, "%s.%d", fpath, --count);
+ rename(fsrc, fdst);
+ } while (count);
+
+ rename(fpath, fsrc);
+ flog = fopen(fpath, "a");
+}
+
+
+static void
+logit(key, msg)
+ char *key;
+ char *msg;
+{
+ time_t now;
+ struct tm *p;
+
+ time(&now);
+ p = localtime(&now);
+ /* Thor.990329: y2k */
+ fprintf(flog, "%s\t%s\t%02d/%02d/%02d %02d:%02d:%02d\n",
+ key, msg, p->tm_year % 100, p->tm_mon + 1, p->tm_mday,
+ p->tm_hour, p->tm_min, p->tm_sec);
+}
+
+
+static void
+log_open()
+{
+ FILE *fp;
+
+ umask(077);
+
+ if (fp = fopen(BNNTP_PIDFILE, "w"))
+ {
+ fprintf(fp, "%d\n", getpid());
+ fclose(fp);
+ }
+
+ flog = fopen(BNNTP_LOGFILE, "a");
+ logit("START", "MTA daemon");
+}
+
+
+static inline void
+agent_log(ap, key, msg)
+ Agent *ap;
+ char *key;
+ char *msg;
+{
+ fprintf(flog, "%s\t[%d] %s\n", key, ap->sno, msg);
+}
+
+
+static void
+agent_reply(ap, msg) /* ±N msg °e¥X¥h */
+ Agent *ap;
+ char *msg;
+{
+ int cc;
+ char *base, *head;
+
+ head = base = ap->data;
+ while (cc = *msg++)
+ {
+ *head++ = cc;
+ }
+ *head++ = '\r';
+ *head++ = '\n';
+ ap->used = head - base;
+ ap->state = CS_SEND;
+}
+
+
+static void
+agent_write(ap, ftemp) /* ±N ftemp ¸Ì­±ªº¤º®e°e¥X¥h */
+ Agent *ap;
+ char *ftemp;
+{
+ int fd, fsize;
+ char *data;
+ struct stat st;
+
+ if ((fd = open(ftemp, O_RDONLY)) >= 0)
+ {
+ fstat(fd, &st);
+ if ((fsize = st.st_size) > 0)
+ {
+ data = ap->data;
+ if (fsize > ap->size)
+ {
+ ap->data = data = realloc(data, fsize);
+ ap->size = fsize;
+ }
+ read(fd, data, fsize);
+ }
+
+ close(fd);
+ unlink(ftemp);
+
+ ap->used = fsize;
+ ap->state = CS_SEND;
+ }
+}
+
+
+/* ----------------------------------------------------- */
+/* board¡Gshm ³¡¥÷¶·»P cache.c ¬Û®e */
+/* ----------------------------------------------------- */
+
+
+static BCACHE *bshm;
+
+
+static void
+init_bshm()
+{
+ /* itoc.030727: ¦b¶}±Ò bbsd ¤§«e¡AÀ³¸Ó´N­n°õ¦æ¹L account¡A
+ ©Ò¥H bshm À³¸Ó¤w³]©w¦n */
+
+ if (bshm)
+ return;
+
+ bshm = shm_new(BRDSHM_KEY, sizeof(BCACHE));
+
+ if (bshm->uptime <= 0) /* bshm ¥¼³]©w§¹¦¨ */
+ exit(0);
+}
+
+
+/* ----------------------------------------------------- */
+/* command dispatch */
+/* ----------------------------------------------------- */
+
+
+ /* --------------------------------------------------- */
+ /* ¯S®í«ü¥O */
+ /* --------------------------------------------------- */
+
+
+static void
+cmd_what(ap)
+ Agent *ap;
+{
+ agent_reply(ap, "500 Bad command use");
+}
+
+
+static void
+cmd_help(ap)
+ Agent *ap;
+{
+ agent_reply(ap, "100 Legal commands\r\n"
+ " article [Number]\r\n"
+ " group newsgroup\r\n"
+ " head [Number]\r\n"
+ " help\r\n"
+ " list\r\n"
+ " mode reader\r\n"
+ " quit\r\n"
+ " xhdr header [range]\r\n"
+ " xover [range]\r\n"
+ "Report problems to the "STR_SYSOP".bbs@"MYHOSTNAME);
+}
+
+
+static void
+cmd_quit(ap)
+ Agent *ap;
+{
+ char *data;
+
+ data = ap->data;
+ strcpy(data, "205 closing connection - goodbye!\r\n");
+ ap->used = strlen(data);
+ ap->state = CS_FLUSH;
+}
+
+
+ /* --------------------------------------------------- */
+ /* ¼Ò¦¡ */
+ /* --------------------------------------------------- */
+
+
+static void
+cmd_mode(ap)
+ Agent *ap;
+{
+ if (ap->data[4] == '\0') /* mode */
+ agent_reply(ap, "reader");
+ else /* mode reader */
+ agent_reply(ap, "200 MapleBBS NNTP server ready (read only)");
+}
+
+
+ /* --------------------------------------------------- */
+ /* ¦C¥X©Ò¦³¬ÝªO */
+ /* --------------------------------------------------- */
+
+
+static void
+cmd_list(ap)
+ Agent *ap;
+{
+ char ftemp[64];
+ BRD *brdp, *bend;
+ FILE *fp;
+
+ sprintf(ftemp, "tmp/bnntp.%d", ap->sno);
+ fp = fopen(ftemp, "w");
+
+ fputs("215 list of newsgroups follows\r\n", fp);
+
+ brdp = bshm->bcache;
+ bend = brdp + bshm->number;
+
+ do
+ {
+ if (brdp->brdname[0] && !brdp->readlevel) /* guest ¥iŪ */
+ fprintf(fp, "%s %d 1 y\r\n", brdp->brdname, brdp->bpost);
+ } while (++brdp < bend);
+
+ fputs(".\r\n", fp);
+ fclose(fp);
+
+ agent_write(ap, ftemp);
+}
+
+
+ /* --------------------------------------------------- */
+ /* ¤Á´«¸s²Õ */
+ /* --------------------------------------------------- */
+
+
+static BRD *
+brd_get(brdname)
+ char *brdname;
+{
+ BRD *bhdr, *tail;
+
+ bhdr = bshm->bcache;
+ tail = bhdr + bshm->number;
+
+ do
+ {
+ if (!str_cmp(brdname, bhdr->brdname))
+ {
+ if (!bhdr->readlevel) /* guest ¥iŪ */
+ return bhdr;
+ return NULL;
+ }
+ } while (++bhdr < tail);
+
+ return NULL;
+}
+
+
+static void
+cmd_group(ap)
+ Agent *ap;
+{
+ char *brdname, msg[80];
+ BRD *brd;
+
+ brdname = ap->data + 6;
+
+ if (*brdname && (brd = brd_get(brdname)))
+ {
+ strcpy(ap->newsgroup, brd->brdname);
+ sprintf(msg, "211 %d 1 %d %s selected", brd->bpost, brd->bpost, brd->brdname);
+ }
+ else
+ {
+ sprintf(msg, "411 No such group %s", brdname);
+ }
+ agent_reply(ap, msg);
+}
+
+
+ /* --------------------------------------------------- */
+ /* ´M°ÝÀÉÀY */
+ /* --------------------------------------------------- */
+
+
+static char *
+postowner(owner)
+ char *owner;
+{
+ static char ownmsg[80];
+
+ if (strchr(owner, '@'))
+ return owner;
+ sprintf(ownmsg, "%s@"MYHOSTNAME, owner);
+ return ownmsg;
+}
+
+
+static char *
+Gtime(now)
+ time_t now;
+{
+ static char datemsg[40];
+
+ strftime(datemsg, sizeof(datemsg), "%d %b %Y %X GMT", gmtime(&now));
+ return datemsg;
+}
+
+
+static void
+cmd_xhdr(ap)
+ Agent *ap;
+{
+ char *str, *newsgroup, *ptr1, *ptr2;
+ char folder[64], ftemp[64];
+ int low, high, max, fd, patern;
+ HDR hdr;
+ FILE *fp;
+
+ newsgroup = ap->newsgroup;
+
+ if (*newsgroup == '\0')
+ {
+ agent_reply(ap, "412 no newsgroup has been selected");
+ return;
+ }
+
+ str = ap->data + 5;
+
+ /* ¥u¤ä´© subject from date message-id ³o¥|ºØÀÉÀY */
+ if (!str_ncmp(str, "subject", 7))
+ patern = 1;
+ else if (!str_ncmp(str, "from", 4))
+ patern = 2;
+ else if (!str_ncmp(str, "date", 4))
+ patern = 3;
+ else if (!strncmp(str, "message-id", 10))
+ patern = 4;
+ else
+ patern = 0;
+
+ if ((ptr1 = strchr(str, ' ')) && *(++ptr1) && (ptr2 = strchr(ptr1, '-')) && *(++ptr2))
+ {
+ low = atoi(ptr1) - 1;
+ if (low < 0)
+ low = 0;
+ high = atoi(ptr2) - 1;
+ }
+ else
+ {
+ low = 0;
+ high = 0;
+ }
+
+ brd_fpath(folder, newsgroup, FN_DIR);
+ max = rec_num(folder, sizeof(HDR)) - 1;
+ if (high > max)
+ high = max;
+
+ sprintf(ftemp, "tmp/bnntp.%d", ap->sno);
+ fp = fopen(ftemp, "w");
+
+ fputs("221 header fields follow\r\n", fp);
+ if (!patern)
+ {
+ while (low <= high)
+ fprintf(fp, "%d (none)\r\n", ++low);
+ }
+ else
+ {
+ if ((fd = open(folder, O_RDONLY)) >= 0)
+ {
+ lseek(fd, (off_t) (sizeof(HDR) * low), SEEK_SET);
+ while (low <= high && read(fd, &hdr, sizeof(HDR)) == sizeof(HDR))
+ {
+ low++;
+
+ if (patern == 1)
+ {
+ fprintf(fp, "%d ", low);
+ output_rfc2047_qp(fp, "", hdr.title, MYCHARSET, "\r\n");
+ }
+ else if (patern == 2)
+ {
+ fprintf(fp, "%d %s (%s)\r\n", low, postowner(hdr.owner), hdr.nick);
+ }
+ else if (patern == 3)
+ {
+ fprintf(fp, "%d %s\r\n", low, Gtime(hdr.chrono));
+ }
+ else /* if (patern == 4) */
+ {
+ fprintf(fp, "%d %s$%s@"MYHOSTNAME"\r\n", low, hdr.xname, newsgroup);
+ }
+ }
+ close(fd);
+ }
+ }
+ fputs(".\r\n", fp);
+
+ fclose(fp);
+
+ agent_write(ap, ftemp);
+}
+
+
+static void
+cmd_xover(ap)
+ Agent *ap;
+{
+ char *str, *newsgroup, *ptr;
+ char folder[64], ftemp[64];
+ int low, high, max, fd;
+ HDR hdr;
+ FILE *fp;
+
+ newsgroup = ap->newsgroup;
+
+ if (*newsgroup == '\0')
+ {
+ agent_reply(ap, "412 no newsgroup has been selected");
+ return;
+ }
+
+ str = ap->data + 6;
+
+ if (*str && (ptr = strchr(str, '-')) && *(++ptr))
+ {
+ low = atoi(str) - 1;
+ if (low < 0)
+ low = 0;
+ high = atoi(ptr) - 1;
+ }
+ else
+ {
+ low = 0;
+ high = 0;
+ }
+
+ brd_fpath(folder, newsgroup, FN_DIR);
+ max = rec_num(folder, sizeof(HDR)) - 1;
+ if (high > max)
+ high = max;
+
+ sprintf(ftemp, "tmp/bnntp.%d", ap->sno);
+ fp = fopen(ftemp, "w");
+
+ fputs("224 data follows\r\n", fp);
+ if ((fd = open(folder, O_RDONLY)) >= 0)
+ {
+ lseek(fd, (off_t) (sizeof(HDR) * low), SEEK_SET);
+ while (low <= high && read(fd, &hdr, sizeof(HDR)) == sizeof(HDR))
+ {
+ /* subject from date message-id references lines xref */
+ low++;
+ fprintf(fp, "%d\t", low);
+ output_rfc2047_qp(fp, "", hdr.title, MYCHARSET, "\t");
+ fprintf(fp, "%s (%s)\t", postowner(hdr.owner), hdr.nick);
+ fprintf(fp, "%s\t", Gtime(hdr.chrono));
+ fprintf(fp, "<%s$%s@"MYHOSTNAME">\t", hdr.xname, newsgroup);
+ fprintf(fp, "%d\t", low);
+ fprintf(fp, "6\t"); /* lines ÀH«Kµ¹ */
+ fprintf(fp, "%s.%s\r\n", newsgroup, hdr.xname);
+ }
+ close(fd);
+ }
+ fputs(".\r\n", fp);
+
+ fclose(fp);
+
+ agent_write(ap, ftemp);
+}
+
+
+ /* --------------------------------------------------- */
+ /* §ì¨ú¤å³¹ */
+ /* --------------------------------------------------- */
+
+
+static void
+fetch_article(ap, str, mode)
+ Agent *ap;
+ char *str;
+ int mode;
+{
+ char *newsgroup;
+ char folder[64], fpath[64], ftemp[64], buf[ANSILINELEN];
+ int pos, max;
+ HDR hdr;
+ FILE *fp, *fpr;
+
+ newsgroup = ap->newsgroup;
+
+ if (*newsgroup == '\0')
+ {
+ agent_reply(ap, "412 no newsgroup has been selected");
+ return;
+ }
+
+ brd_fpath(folder, newsgroup, FN_DIR);
+ if (*str)
+ {
+ pos = atoi(str) - 1;
+ max = rec_num(folder, sizeof(HDR)) - 1;
+ if (pos > max)
+ pos = max;
+ if (pos < 0)
+ pos = 0;
+ }
+ else
+ {
+ pos = 0;
+ }
+
+ sprintf(ftemp, "tmp/bnntp.%d", ap->sno);
+ fp = fopen(ftemp, "w");
+
+ fprintf(fp, "22%d %d article retrieved - %s follows\r\n", mode, pos + 1, mode ? "head" : "article");
+ if (!rec_get(folder, &hdr, sizeof(HDR), pos))
+ {
+ /* ¤å³¹ÀÉÀY */
+ fprintf(fp, "From: %s (%s)\r\n", postowner(hdr.owner), hdr.nick);
+ fprintf(fp, "Newsgroups: %s\r\n", newsgroup);
+ output_rfc2047_qp(fp, "Subject: ", hdr.title, MYCHARSET, "\r\n");
+ fprintf(fp, "Date: %s\r\n", Gtime(hdr.chrono));
+ fprintf(fp, "Message-ID: <%s$%s@"MYHOSTNAME">\r\n", hdr.xname, newsgroup);
+ fprintf(fp, "Mime-Version: 1.0\r\n");
+ fprintf(fp, "Content-Type: text/plain; charset=\""MYCHARSET"\"\r\n");
+ fprintf(fp, "Content-Transfer-Encoding: 8bit\r\n");
+
+ /* ¤å³¹¤º®e */
+ if (!mode)
+ {
+ fputs("\r\n", fp); /* ÀÉÀY©M¤º¤åªÅ¤@¦æ */
+
+#ifdef HAVE_REFUSEMARK
+ if (!(hdr.xmode & POST_RESTRICT))
+ {
+#endif
+ hdr_fpath(fpath, folder, &hdr);
+ if (fpr = fopen(fpath, "r"))
+ {
+ while (fgets(buf, ANSILINELEN, fpr))
+ {
+ str_ansi(buf, buf, sizeof(buf)); /* ¥h°£ '\n' ¤Î±±¨î½X */
+ fprintf(fp, "%s\r\n", buf);
+ }
+ fclose(fpr);
+ }
+#ifdef HAVE_REFUSEMARK
+ }
+#endif
+ }
+ }
+ fputs(".\r\n", fp);
+
+ fclose(fp);
+
+ agent_write(ap, ftemp);
+}
+
+
+static void
+cmd_head(ap)
+ Agent *ap;
+{
+ fetch_article(ap, ap->data + 5, 1);
+}
+
+
+static void
+cmd_article(ap)
+ Agent *ap;
+{
+ fetch_article(ap, ap->data + 8, 0);
+}
+
+
+ /* --------------------------------------------------- */
+ /* «ü¥O¶° */
+ /* --------------------------------------------------- */
+
+
+static Command cmd_table[] =
+{
+ cmd_help, "help", 4,
+ cmd_quit, "quit", 4,
+
+ cmd_mode, "mode", 4,
+ cmd_list, "list", 4,
+
+ cmd_group, "group ", 6,
+
+ cmd_xhdr, "xhdr ", 5,
+ cmd_xover, "xover ", 6,
+
+ cmd_head, "head ", 5,
+ cmd_article, "article ", 8,
+
+ cmd_what, NULL, 0
+};
+
+
+/* ----------------------------------------------------- */
+/* send output to client */
+/* ----------------------------------------------------- */
+/* return value : */
+/* > 0 : bytes sent */
+/* = 0 : close this agent */
+/* < 0 : there are some error, but keep trying */
+/* ----------------------------------------------------- */
+
+
+static int
+agent_send(ap)
+ Agent *ap;
+{
+ int csock, len, cc;
+ char *data;
+
+ csock = ap->sock;
+ data = ap->data;
+ len = ap->used;
+ cc = send(csock, data, len, 0);
+
+ if (cc < 0)
+ {
+ cc = errno;
+ if (cc != EWOULDBLOCK)
+ {
+ agent_log(ap, "SEND", strerror(cc));
+ return 0;
+ }
+
+ /* would block, so leave it to do later */
+ return -1;
+ }
+
+ if (cc == 0)
+ return -1;
+
+ len -= cc;
+ ap->used = len;
+ if (len)
+ {
+ memcpy(data, data + cc, len);
+ return cc;
+ }
+
+ if (ap->state == CS_FLUSH)
+ {
+ shutdown(csock, 2);
+ close(csock);
+ ap->sock = -1;
+ return 0;
+ }
+
+ ap->state = CS_RECV;
+ return cc;
+}
+
+
+/* ----------------------------------------------------- */
+/* receive request from client */
+/* ----------------------------------------------------- */
+
+
+static int
+agent_recv(ap)
+ Agent *ap;
+{
+ int cc, mode, used;
+ char *data, *head;
+
+ mode = ap->mode;
+ used = ap->used;
+ data = ap->data;
+
+ head = data + used;
+ cc = recv(ap->sock, head, TCP_RCVSIZ, 0);
+
+ if (cc <= 0)
+ {
+ cc = errno;
+ if (cc != EWOULDBLOCK)
+ {
+ agent_log(ap, "RECV", strerror(cc));
+ return 0;
+ }
+
+ /* would block, so leave it to do later */
+
+ return -1;
+ }
+
+ head[cc] = '\0';
+ used += cc;
+
+ if (used >= MAX_CMD_LEN)
+ {
+ fprintf(flog, "CMD\t[%d] too long (%d) %.32s\n",
+ ap->sno, used, data);
+
+ ap->mode = (mode |= AM_DROP);
+ used = 32;
+ }
+
+ while (cc = *head)
+ {
+ if (cc == '\r' || cc == '\n')
+ {
+ Command *cmd;
+
+ *head = '\0';
+
+ if (mode & AM_DROP)
+ {
+ ap->mode = mode ^ AM_DROP;
+ agent_reply(ap, "552 command too long");
+ return -1;
+ }
+
+ for (cmd = cmd_table; head = cmd->cmd; cmd++)
+ {
+ if (!str_ncmp(data, head, cmd->len))
+ break;
+ }
+
+ ap->used = 0;
+
+ (*cmd->func) (ap);
+
+ return 1;
+ }
+
+ if (cc == '\t')
+ *head = ' ';
+
+ head++;
+ }
+
+ ap->used = used;
+ return 1;
+}
+
+
+/* ----------------------------------------------------- */
+/* close a connection & release its resource */
+/* ----------------------------------------------------- */
+
+
+static void
+agent_fire(ap)
+ Agent *ap;
+{
+ int num;
+ char *data, *key;
+
+ num = ap->sock;
+ if (num > 0)
+ {
+ fcntl(num, F_SETFL, M_NONBLOCK);
+
+#define MSG_ABORT "\r\n450 buggy, closing ...\r\n"
+ send(num, MSG_ABORT, sizeof(MSG_ABORT) - 1, 0);
+#undef MSG_ABORT
+ shutdown(num, 2);
+ close(num);
+
+ key = "END";
+ }
+ else
+ {
+ key = "BYE";
+ }
+
+ /* log */
+
+ data = ap->data;
+
+ sprintf(data, "[%d] T%d", ap->sno, time(0) - ap->tbegin);
+ logit(key, data);
+
+ free(data);
+}
+
+
+/* ----------------------------------------------------- */
+/* accept a new connection */
+/* ----------------------------------------------------- */
+
+
+static int
+agent_accept()
+{
+ int csock;
+ int value;
+ struct sockaddr_in csin;
+
+ for (;;)
+ {
+ value = sizeof(csin);
+ csock = accept(0, (struct sockaddr *) & csin, &value);
+ /* if (csock > 0) */
+ if (csock >= 0) /* Thor.000126: more proper */
+ break;
+
+ csock = errno;
+ if (csock != EINTR)
+ {
+ logit("ACCEPT", strerror(csock));
+ return -1;
+ }
+
+ while (waitpid(-1, NULL, WNOHANG | WUNTRACED) > 0);
+ }
+
+ value = 1;
+
+ /* Thor.000511: µù¸Ñ: don't delay send to coalesce(Áp¦X) packets */
+ setsockopt(csock, IPPROTO_TCP, TCP_NODELAY, (char *) &value, sizeof(value));
+
+ /* --------------------------------------------------- */
+ /* check remote host / user name */
+ /* --------------------------------------------------- */
+
+ logit("CONN", "");
+ return csock;
+}
+
+
+/* ----------------------------------------------------- */
+/* signal routines */
+/* ----------------------------------------------------- */
+
+
+#ifdef SERVER_USAGE
+static void
+servo_usage()
+{
+ struct rusage ru;
+
+ if (getrusage(RUSAGE_SELF, &ru))
+ return;
+
+ fprintf(flog, "\n[Server Usage]\n\n"
+ " user time: %.6f\n"
+ " system time: %.6f\n"
+ " maximum resident set size: %lu P\n"
+ " integral resident set size: %lu\n"
+ " page faults not requiring physical I/O: %d\n"
+ " page faults requiring physical I/O: %d\n"
+ " swaps: %d\n"
+ " block input operations: %d\n"
+ " block output operations: %d\n"
+ " messages sent: %d\n"
+ " messages received: %d\n"
+ " signals received: %d\n"
+ " voluntary context switches: %d\n"
+ " involuntary context switches: %d\n",
+
+ (double) ru.ru_utime.tv_sec + (double) ru.ru_utime.tv_usec / 1000000.0,
+ (double) ru.ru_stime.tv_sec + (double) ru.ru_stime.tv_usec / 1000000.0,
+ ru.ru_maxrss,
+ ru.ru_idrss,
+ ru.ru_minflt,
+ ru.ru_majflt,
+ ru.ru_nswap,
+ ru.ru_inblock,
+ ru.ru_oublock,
+ ru.ru_msgsnd,
+ ru.ru_msgrcv,
+ ru.ru_nsignals,
+ ru.ru_nvcsw,
+ ru.ru_nivcsw);
+
+ fflush(flog);
+}
+#endif
+
+
+#define SS_CONFIG 1
+#define SS_SHUTDOWN 2
+
+
+static int servo_state;
+
+
+static void
+sig_hup()
+{
+ servo_state |= SS_CONFIG;
+}
+
+
+static void
+sig_term() /* graceful termination */
+{
+ servo_state |= SS_SHUTDOWN;
+}
+
+
+static void
+sig_abort(sig)
+ int sig;
+{
+ char buf[80];
+
+ sprintf(buf, "abort: %d, errno: %d", sig, errno);
+ logit("EXIT", buf);
+ fclose(flog);
+ exit(0);
+}
+
+
+static void
+reaper()
+{
+ while (waitpid(-1, NULL, WNOHANG | WUNTRACED) > 0);
+}
+
+
+static void
+servo_signal()
+{
+ struct sigaction act;
+
+ /* sigblock(sigmask(SIGPIPE)); *//* Thor.981206: ²Î¤@ POSIX ¼Ð·Ç¥Îªk */
+
+ /* act.sa_mask = 0; *//* Thor.981105: ¼Ð·Ç¥Îªk */
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+
+ act.sa_handler = sig_term; /* forced termination */
+ sigaction(SIGTERM, &act, NULL);
+
+ act.sa_handler = sig_abort; /* forced termination */
+ sigaction(SIGSEGV, &act, NULL); /* if rlimit violate */
+ sigaction(SIGBUS, &act, NULL);
+
+#if 1 /* Thor.990203: §ì signal */
+ sigaction(SIGURG, &act, NULL);
+ sigaction(SIGXCPU, &act, NULL);
+ sigaction(SIGXFSZ, &act, NULL);
+
+#ifdef SOLARIS
+ sigaction(SIGLOST, &act, NULL);
+ sigaction(SIGPOLL, &act, NULL);
+ sigaction(SIGPWR, &act, NULL);
+#endif
+
+#ifdef LINUX
+ sigaction(SIGSYS, &act, NULL);
+ /* sigaction(SIGEMT, &act, NULL); */
+ /* itoc.010317: §Úªº linux ¨S¦³³o­Ó»¡ :p */
+#endif
+
+ sigaction(SIGFPE, &act, NULL);
+ sigaction(SIGWINCH, &act, NULL);
+ sigaction(SIGINT, &act, NULL);
+ sigaction(SIGQUIT, &act, NULL);
+ sigaction(SIGILL, &act, NULL);
+ sigaction(SIGTRAP, &act, NULL);
+ sigaction(SIGABRT, &act, NULL);
+ sigaction(SIGTSTP, &act, NULL);
+ sigaction(SIGTTIN, &act, NULL);
+ sigaction(SIGTTOU, &act, NULL);
+ sigaction(SIGVTALRM, &act, NULL);
+#endif
+
+ act.sa_handler = sig_hup; /* restart config */
+ sigaction(SIGHUP, &act, NULL);
+
+ act.sa_handler = reaper;
+ sigaction(SIGCHLD, &act, NULL);
+
+#ifdef SERVER_USAGE
+ act.sa_handler = servo_usage;
+ sigaction(SIGPROF, &act, NULL);
+#endif
+
+ /* Thor.981206: lkchu patch: ²Î¤@ POSIX ¼Ð·Ç¥Îªk */
+ /* ¦b¦¹­É¥Î sigset_t act.sa_mask */
+ sigaddset(&act.sa_mask, SIGPIPE);
+ sigprocmask(SIG_BLOCK, &act.sa_mask, NULL);
+}
+
+
+/* ----------------------------------------------------- */
+/* server core routines */
+/* ----------------------------------------------------- */
+
+
+static void
+servo_daemon(inetd)
+ int inetd;
+{
+ int fd, value;
+ char buf[80];
+ struct linger ld;
+ struct sockaddr_in sin;
+#ifdef HAVE_RLIMIT
+ struct rlimit limit;
+#endif
+
+ /* 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((time_t *) & value);
+ gmtime((time_t *) & value);
+ strftime(buf, 80, "%d/%b/%Y:%H:%M:%S", localtime((time_t *) & value));
+
+#ifdef HAVE_RLIMIT
+ /* --------------------------------------------------- */
+ /* adjust the resource limit */
+ /* --------------------------------------------------- */
+
+ getrlimit(RLIMIT_NOFILE, &limit);
+ limit.rlim_cur = limit.rlim_max;
+ setrlimit(RLIMIT_NOFILE, &limit);
+
+ limit.rlim_cur = limit.rlim_max = 16 * 1024 * 1024;
+ setrlimit(RLIMIT_FSIZE, &limit);
+
+ limit.rlim_cur = limit.rlim_max = 16 * 1024 * 1024;
+ setrlimit(RLIMIT_DATA, &limit);
+
+#ifdef SOLARIS
+#define RLIMIT_RSS RLIMIT_AS /* Thor.981206: port for solaris 2.6 */
+#endif
+
+ setrlimit(RLIMIT_RSS, &limit);
+
+ limit.rlim_cur = limit.rlim_max = 0;
+ setrlimit(RLIMIT_CORE, &limit);
+#endif
+
+ /* --------------------------------------------------- */
+ /* detach daemon process */
+ /* --------------------------------------------------- */
+
+ close(1);
+ close(2);
+
+ if (inetd)
+ return;
+
+ close(0);
+
+ if (fork())
+ exit(0);
+
+ setsid();
+
+ if (fork())
+ exit(0);
+
+ /* --------------------------------------------------- */
+ /* setup socket */
+ /* --------------------------------------------------- */
+
+ fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+
+ value = 1;
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &value, sizeof(value));
+
+ ld.l_onoff = ld.l_linger = 0;
+ setsockopt(fd, SOL_SOCKET, SO_LINGER, (char *) &ld, sizeof(ld));
+
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(BNNTP_PORT);
+ sin.sin_addr.s_addr = htonl(INADDR_ANY);
+ memset((char *) &sin.sin_zero, 0, sizeof(sin.sin_zero));
+
+ if (bind(fd, (struct sockaddr *) & sin, sizeof(sin)) ||
+ listen(fd, TCP_BACKLOG))
+ exit(1);
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int n, sock, state;
+ time_t uptime, tcheck, tfresh;
+ Agent **FBI, *Scully, *Mulder, *agent;
+ fd_set rset, wset, xset;
+ static struct timeval tv = {BNNTP_PERIOD, 0};
+
+ state = 0;
+
+ while ((n = getopt(argc, argv, "i")) != -1)
+ {
+ switch (n)
+ {
+ case 'i':
+ state = 1;
+ break;
+
+ default:
+ fprintf(stderr, "Usage: %s [options]\n"
+ "\t-i start from inetd with wait option\n",
+ argv[0]);
+ exit(0);
+ }
+ }
+
+ servo_daemon(state);
+
+ setgid(BBSGID);
+ setuid(BBSUID);
+ chdir(BBSHOME);
+
+ init_bshm();
+
+ servo_signal();
+
+ log_open();
+ dns_init();
+
+ uptime = time(0);
+ tcheck = uptime + BNNTP_PERIOD;
+ tfresh = uptime + BNNTP_FRESH;
+
+ Scully = Mulder = NULL;
+
+ for (;;)
+ {
+ /* maintain : resource and garbage collection */
+
+ uptime = time(0);
+ if (tcheck < uptime)
+ {
+ /* ----------------------------------------------- */
+ /* agent_audit (uptime - BNNTP_TIMEOUT) */
+ /* ----------------------------------------------- */
+
+ tcheck = uptime - BNNTP_TIMEOUT;
+
+ for (FBI = &Scully; agent = *FBI;)
+ {
+ if (agent->uptime < tcheck)
+ {
+ agent_fire(agent);
+
+ *FBI = agent->anext;
+
+ agent->anext = Mulder;
+ Mulder = agent;
+ }
+ else
+ {
+ FBI = &(agent->anext);
+ }
+ }
+
+ /* ----------------------------------------------- */
+ /* maintain SPAM & server log */
+ /* ----------------------------------------------- */
+
+ if (tfresh < uptime)
+ {
+ tfresh = uptime + BNNTP_FRESH;
+#ifdef SERVER_USAGE
+ servo_usage();
+#endif
+ log_fresh();
+ }
+ else
+ {
+ fflush(flog);
+ }
+
+ tcheck = uptime + BNNTP_PERIOD;
+ }
+
+ /* ------------------------------------------------- */
+ /* check servo operation state */
+ /* ------------------------------------------------- */
+
+ n = 0;
+
+ if (state = servo_state)
+ {
+ if (state & SS_CONFIG)
+ {
+ state ^= SS_CONFIG;
+ }
+
+ if (state & SS_SHUTDOWN) /* graceful shutdown */
+ {
+ n = -1;
+ close(0);
+ }
+
+ servo_state = state;
+ }
+
+ /* ------------------------------------------------- */
+ /* Set up the fdsets */
+ /* ------------------------------------------------- */
+
+ FD_ZERO(&rset);
+ FD_ZERO(&wset);
+ FD_ZERO(&xset);
+
+ if (n == 0)
+ FD_SET(0, &rset);
+
+ for (agent = Scully; agent; agent = agent->anext)
+ {
+ sock = agent->sock;
+ state = agent->state;
+
+ if (n < sock)
+ n = sock;
+
+ if (state == CS_RECV)
+ {
+ FD_SET(sock, &rset);
+ }
+ else
+ {
+ FD_SET(sock, &wset);
+ }
+
+ FD_SET(sock, &xset);
+ }
+
+ /* no active agent and ready to die */
+
+ if (n < 0)
+ {
+ break;
+ }
+
+ {
+ struct timeval tv_tmp = tv;
+ /* Thor.981221: for future reservation bug */
+ n = select(n + 1, &rset, &wset, &xset, &tv_tmp);
+ }
+
+ if (n == 0)
+ {
+ continue;
+ }
+
+ if (n < 0)
+ {
+ n = errno;
+ if (n != EINTR)
+ {
+ logit("SELECT", strerror(n));
+ }
+ continue;
+ }
+
+ /* ------------------------------------------------- */
+ /* serve active agents */
+ /* ------------------------------------------------- */
+
+ uptime = time(0);
+
+ for (FBI = &Scully; agent = *FBI;)
+ {
+ sock = agent->sock;
+
+ if (FD_ISSET(sock, &wset))
+ {
+ state = agent_send(agent);
+ }
+ else if (FD_ISSET(sock, &rset))
+ {
+ state = agent_recv(agent);
+ }
+ else if (FD_ISSET(sock, &xset))
+ {
+ state = 0;
+ }
+ else
+ {
+ state = -1;
+ }
+
+ if (state == 0) /* fire this agent */
+ {
+ agent_fire(agent);
+
+ *FBI = agent->anext;
+
+ agent->anext = Mulder;
+ Mulder = agent;
+
+ continue;
+ }
+
+ if (state > 0)
+ {
+ agent->uptime = uptime;
+ }
+
+ FBI = &(agent->anext);
+ }
+
+ /* ------------------------------------------------- */
+ /* serve new connection */
+ /* ------------------------------------------------- */
+
+ /* Thor.000209: ¦Ò¼{²¾«e¦¹³¡¤À, §K±o¥d¦b accept() */
+ if (FD_ISSET(0, &rset))
+ {
+ /* Thor.990319: check maximum connection number */
+ unsigned int ip_addr;
+ sock = agent_accept();
+
+ if (sock > 0)
+ {
+ if (agent = Mulder)
+ {
+ Mulder = agent->anext;
+ }
+ else
+ {
+ agent = (Agent *) malloc(sizeof(Agent));
+ if (!agent) /* Thor.990205: °O¿ýªÅ¶¡¤£°÷ */
+ logit("ERROR", "Not enough space in main()");
+ }
+
+ *FBI = agent;
+
+ /* variable initialization */
+
+ memset(agent, 0, sizeof(Agent));
+
+ agent->sock = sock;
+ agent->sno = ++servo_sno;
+ agent->state = CS_SEND;
+ agent->tbegin = agent->uptime = uptime;
+
+ /* Thor.990319: check maximum connection number */
+ agent->ip_addr = ip_addr;
+
+ agent->data = (char *) malloc(MIN_DATA_SIZE);
+ if (!agent->data) /* Thor.990205: °O¿ýªÅ¶¡¤£°÷ */
+ logit("ERROR", "Not enough space in agent->data");
+ sprintf(agent->data, "200 MapleBBS NNTP server ready (read only)\r\n");
+ agent->used = strlen(agent->data);
+ agent->size = MIN_DATA_SIZE;
+ }
+ }
+
+ /* ------------------------------------------------- */
+ /* tail of main loop */
+ /* ------------------------------------------------- */
+ }
+
+ logit("EXIT", "shutdown");
+ fclose(flog);
+
+ exit(0);
+}
diff --git a/daemon/bpop3d.c b/daemon/bpop3d.c
new file mode 100644
index 0000000..3b76e8c
--- /dev/null
+++ b/daemon/bpop3d.c
@@ -0,0 +1,1871 @@
+/*-------------------------------------------------------*/
+/* util/bpop3d.c ( NTHU CS MapleBBS Ver 3.00 ) */
+/*-------------------------------------------------------*/
+/* target : Simple POP3 server for BBS user */
+/* create : 96/05/10 */
+/* update : 96/11/15 */
+/*-------------------------------------------------------*/
+/* notice : single process concurrent server */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+
+#undef DEBUG
+#define WATCH_DOG
+#define SERVER_USAGE
+
+
+#include <netinet/tcp.h>
+#include <sys/wait.h>
+#include <sys/resource.h>
+
+
+#ifdef HAVE_DNS_LOOKUP
+#include "dns.ic"
+#endif
+
+
+#define POP3_HOME (BBSHOME "/usr")
+#define POP3_TIMEOUT (60 * 30)
+
+#define POP3_LOGFILE (BBSHOME "/run/pop3.log")
+#define POP3_PIDFILE (BBSHOME "/run/pop3.pid")
+#define POP3_DEBUGFILE (BBSHOME "/run/pop3.debug")
+
+#define POP3_FQDN (".bbs@" MYHOSTNAME)
+
+#define SOCK_BACKLOG 3
+
+
+#define SNDBUFSIZ (256 * 14)
+#define SNDLINSIZ 256 /* Thor.990522: µù¸Ñ: °e¥X¨C¦æ³Ìªø */
+#define RCVBUFSIZ 128 /* Thor.990522: µù¸Ñ: ¦¬¨ì¨C¦æ³Ìªø */
+
+
+/* ----------------------------------------------------- */
+/* MapleBBS pop3d message strings */
+/* ----------------------------------------------------- */
+
+
+#define POP3_BYE_MSG "+OK POP3 server sign off\r\n"
+#define POP3_BYE_LEN (sizeof(POP3_BYE_MSG) - 1)
+
+
+#define POP3_ERRCMD_MSG "-ERR invalid command\r\n"
+#define POP3_ERRARG_MSG "-ERR invalid argument\r\n"
+#define POP3_ERRNUM_MSG "-ERR message number out of range\r\n"
+#define POP3_DELETE_MSG "-ERR message has been deleted\r\n"
+
+
+/* ----------------------------------------------------- */
+/* POPH : pop3header extended from HDR */
+/* ----------------------------------------------------- */
+
+
+typedef struct
+{
+ time_t chrono; /* timestamp */
+ int xmode;
+
+ int xid; /* reserved */
+
+ char xname[24]; /* ÀɮצWºÙ */
+ int pmode; /* Thor.990522: ¬O§_§R«H µ¥ */
+ int psize; /* Thor.990522: ¥»«H¶Ç°eÁ`¤j¤p */
+
+ char owner[80]; /* §@ªÌ (E-mail address) */
+ char nick[50]; /* ¼ÊºÙ */
+
+ char date[9]; /* [96/12/01] */
+ char title[TTLEN + 1]; /* ¥DÃD */
+} POPH;
+
+
+/* ----------------------------------------------------- */
+/* client connection structure */
+/* ----------------------------------------------------- */
+
+
+typedef struct Client
+{
+ struct Client *next;
+ int sno; /* serial number */
+
+ int sock;
+ int state;
+ time_t tbirth;
+ time_t uptime;
+
+ int mode;
+#ifdef DEBUG
+ int debug;
+#endif
+ int pcount; /* Thor.990522: «H¥ó¼Æ */
+ int pbytes; /* Thor.990522: Á`¤j¤p */
+ POPH *cache; /* Thor.990522: ¦s .DIR */
+
+ char userid[IDLEN + 1];
+ char passwd[PASSLEN + 1];
+ char home[IDLEN + 4]; /* Thor.990122: 4­Ó¦r¤¸¤¤, 2­Ó¬O'/', 1­Ó¬O­^¤å¦r 1­Ó¬O'\0' */
+
+ char pool[SNDBUFSIZ]; /* output pool */
+ int locus; /* Thor.990522: output pool ¤º¥i°edata¤§¶q */
+
+ char *body; /* mail body */
+ char *bptr; /* Thor.990522: body pointer, ¤w°e¦h¤Ö */
+
+ int lcur; /* Thor.990522: ¥Ø«e°e¦Ü­þ¦æ */
+ int lmax; /* Thor.990522: ³Ì¤j¦æ¼Æ */
+
+ int xbytes; /* Thor.990522: ¶Ç°eÁ`¶q */
+} Client;
+
+
+/* ----------------------------------------------------- */
+/* connection state */
+/* ----------------------------------------------------- */
+
+
+#define CS_FREE 0x00 /* Thor.990522: ¥Ø«e¥¼¥Î */
+#define CS_READ 0x01 /* Thor.990522: µ¥«Ýuser¿é¤J */
+#define CS_INDEX 0x02 /* Thor.990522: «H½c±ø¦C */
+#define CS_UIDL 0x03 /* Thor.990522: uniq id±ø¦C */
+#define CS_FILE 0x04 /* Thor.990522: «H¥ó¶Ç°e¤¤ */
+#define CS_WRITE 0x05 /* Thor.990522: ¶Ç°e¤¤ */
+#define CS_FLUSH 0x06 /* Thor.990522: ³Ì²×¤@¦¸¶Ç°e */
+
+
+#define CM_LOGIN 1
+#define CM_DIRTY 2 /* ¦³§R°£«H¥ó */
+
+
+/* ----------------------------------------------------- */
+/* operation log and debug information */
+/* ----------------------------------------------------- */
+
+
+static FILE *flog; /* log file descriptor */
+static int gline;
+
+
+#ifdef WATCH_DOG
+# define MYDOG gline = __LINE__
+#else
+# define MYDOG /* NOOP */
+#endif
+
+
+static void
+logit(key, msg)
+ char *key;
+ char *msg;
+{
+ time_t now;
+ struct tm *p;
+
+ time(&now);
+ p = localtime(&now);
+ fprintf(flog, "%02d/%02d %02d:%02d:%02d %-8s%s\n",
+ p->tm_mon + 1, p->tm_mday,
+ p->tm_hour, p->tm_min, p->tm_sec, key, msg);
+}
+
+
+static void
+log_init()
+{
+ FILE *fp;
+
+ if (fp = fopen(POP3_PIDFILE, "w"))
+ {
+ fprintf(fp, "%d\n", getpid());
+ fclose(fp);
+ }
+
+ flog = fopen(POP3_LOGFILE, "a");
+ logit("START", "pop3 daemon");
+}
+
+
+/* ----------------------------------------------------- */
+/* parse string token */
+/* ----------------------------------------------------- */
+
+
+#define LOWER 1
+
+
+static char *trail_token;
+
+
+static char *
+parse_token(str, lower)
+ char *str;
+ int lower;
+{
+ char *token;
+ int ch;
+
+ if (str == NULL)
+ {
+ str = trail_token;
+ if (str == NULL)
+ return NULL;
+ }
+
+ token = NULL;
+ while (ch = *str)
+ {
+ if (ch == ' ' /* || ch == '\t' || ch == '\r' || ch == '\n' */ )
+ {
+ if (token)
+ {
+ *str++ = '\0';
+ break;
+ }
+ }
+ else
+ {
+ if (token == NULL)
+ token = str;
+
+ if (lower && ch >= 'A' && ch <= 'Z')
+ *str = ch | 0x20;
+ }
+ str++;
+ }
+
+ trail_token = str;
+ return token;
+}
+
+
+/* ----------------------------------------------------- */
+/* server side stuff */
+/* ----------------------------------------------------- */
+
+
+static void
+mbox_open(cn)
+ Client *cn;
+{
+ int fd, fsize, pbytes, pad;
+ struct stat st;
+ char fpath[80], *fname;
+ POPH *hdr;
+
+ cn->pcount = cn->pbytes = 0;
+ /* Thor.990522: cache, body µ¥, ¤w¦b accept®Éinitial */
+
+ sprintf(fpath, "%s"FN_DIR, cn->home);
+ fd = open(fpath, O_RDONLY);
+
+ if (fd < 0)
+ return;
+
+ if (fstat(fd, &st) || (fsize = st.st_size) < sizeof(HDR))
+ {
+ close(fd);
+ return;
+ }
+
+ hdr = (POPH *) malloc(fsize);
+ fsize = read(fd, hdr, fsize) / sizeof(HDR);
+ close(fd);
+
+ if (fsize <= 0)
+ {
+ free(hdr);
+ return;
+ }
+
+ cn->cache = hdr;
+ cn->pcount = fsize;
+ pbytes = 0;
+ fname = strchr(fpath, '.');
+ *fname++ = '@';
+ *fname++ = '/';
+
+ pad = 10 + strlen(cn->userid) + strlen(POP3_FQDN) + 9 + 44;
+ for (;;)
+ {
+ char *author;
+ int psize;
+
+ strcpy(fname, hdr->xname);
+ psize = 0;
+ if (!stat(fpath, &st))
+ psize = st.st_size;
+
+ author = hdr->owner;
+ if (!strchr(author, '@'))
+ strcat(author, POP3_FQDN);
+
+ psize += pad + strlen(author) + strlen(hdr->title) + 10 + 40 + 80;
+
+ hdr->pmode = 0;
+ hdr->psize = psize;
+ pbytes += psize;
+
+ if (--fsize == 0)
+ break;
+
+ hdr++;
+ }
+ cn->pbytes = pbytes;
+}
+
+
+static void
+mbox_read(cn, phdr, lmax)
+ Client *cn;
+ POPH *phdr;
+ int lmax; /* Thor.990522: ¤¹³\¤§Á`¦æ¼Æ */
+{
+ char *pool, *head, *body, buf[80];
+ /* struct tm *mytm; */
+ int fd;
+
+ /* mytm = localtime(&phdr->chrono);
+ strftime(buf, 46, "%a, %e %h %Y %T +0800 (%Z)", mytm); */
+
+
+ pool = cn->pool;
+ sprintf(pool, "+OK %d octets\r\nFrom: %s\r\nTo: %s%s\r\n"
+ "Subject: %s\r\nDate: %s\r\n%s\r\n",
+ phdr->psize, phdr->owner, cn->userid, POP3_FQDN,
+ phdr->title, Atime(&phdr->chrono), (phdr->xmode & MAIL_READ ? "Status: RO\r\n" : ""));
+
+ head = pool + strlen(pool);
+ if (!lmax)
+ {
+ *head++ = '.';
+ *head++ = '\r';
+ *head++ = '\n';
+ cn->locus = head - pool;
+ cn->state = CS_WRITE;
+ return;
+ }
+
+ cn->locus = head - pool;
+ cn->state = CS_FILE;
+
+ if (body = cn->body)
+ {
+ /* Thor.990522: ¦³°õ¦æªº¾÷·|¶Ü? */
+ free(body);
+ }
+ body = NULL;
+
+ sprintf(buf, "%s@/%s", cn->home, phdr->xname);
+ fd = open(buf, O_RDONLY);
+ if (fd >= 0)
+ {
+ int size;
+ struct stat st;
+
+ if (!fstat(fd, &st) && ((size = st.st_size) > 0))
+ {
+ if (body = (char *) malloc(size + 1))
+ {
+ size = read(fd, body, size);
+ if (size > 0)
+ {
+ body[size] = '\0';
+ }
+ else
+ {
+ free(body);
+ body = NULL;
+ }
+ }
+ }
+#if 0
+ else
+ {
+ phdr->pmode = MAIL_DELETE;
+ *head++ = '.';
+ *head++ = '\r';
+ *head++ = '\n';
+ cn->locus = head - pool;
+ cn->state = CS_WRITE;
+ lmax = 0;
+ }
+#endif
+ close(fd);
+ }
+
+ if (!body) /* Thor.991220: for open fail, size==0, and read fail */
+ {
+ phdr->pmode = MAIL_DELETE;
+ cn->mode = CM_DIRTY; /* Thor.991221: for incorrect mail delete */
+ *head++ = '.';
+ *head++ = '\r';
+ *head++ = '\n';
+ cn->locus = head - pool;
+ cn->state = CS_WRITE;
+ lmax = 0;
+ }
+
+ cn->bptr = cn->body = body;
+ cn->lcur = 0;
+ cn->lmax = lmax;
+}
+
+
+static void
+mbox_close(cn)
+ Client *cn;
+{
+ int n, max, saved;
+ FILE *fpr, *fpw;
+ char fpath[80], fold[80], fnew[80], *fname;
+ HDR mhdr;
+ POPH *phdr;
+
+ sprintf(fold, "%s"FN_DIR, cn->home);
+ if ((fpr = fopen(fold, "r")) == NULL)
+ return;
+
+ fpw = f_new(fold, fnew);
+ if (fpw == NULL)
+ {
+ fclose(fpr);
+ return;
+ }
+
+ strcpy(fpath, fold);
+ fname = strchr(fpath, '.');
+ *fname++ = '@';
+ *fname++ = '/';
+
+ n = saved = 0;
+ max = cn->pcount;
+ phdr = cn->cache;
+
+ while (fread(&mhdr, sizeof(mhdr), 1, fpr) == 1)
+ {
+ if (n >= max || !phdr->pmode)
+ {
+ saved = fwrite(&mhdr, sizeof(mhdr), 1, fpw);
+ if (saved <= 0)
+ {
+ saved = -1;
+ break;
+ }
+ }
+ else
+ {
+ strcpy(fname, mhdr.xname);
+ unlink(fpath);
+ }
+ n++;
+ phdr++;
+ }
+ fclose(fpr);
+ fclose(fpw);
+
+ if (saved < 0)
+ {
+ unlink(fnew);
+ }
+ else if (saved)
+ {
+ rename(fnew, fold);
+ }
+ else
+ {
+ unlink(fnew);
+ unlink(fold);
+ }
+}
+
+
+static void
+mbox_index(cn, state)
+ Client *cn;
+ int state;
+{
+ char *pool, *head, *tail;
+ int cur, max;
+ POPH *hdr;
+
+ pool = cn->pool;
+ tail = pool + SNDBUFSIZ - 32;
+
+ cur = cn->lcur;
+ if (cur)
+ {
+ head = pool + cn->locus;
+ }
+ else
+ {
+ memcpy(pool, "+OK\r\n", 5);
+ head = pool + 5;
+ }
+
+ hdr = &cn->cache[cur];
+ max = cn->pcount;
+ do
+ {
+ cur++;
+
+ MYDOG;
+
+ if (cur > max)
+ {
+ *head++ = '.';
+ *head++ = '\r';
+ *head++ = '\n';
+ cn->state = CS_WRITE;
+ break;
+ }
+
+ if (!hdr->pmode)
+ {
+ if (state == CS_UIDL)
+ sprintf(head, "%d %s\r\n", cur, hdr->xname + 1);
+ else
+ sprintf(head, "%d %d\r\n", cur, hdr->psize);
+ head += strlen(head);
+ }
+ hdr++;
+
+ } while (head < tail);
+
+ MYDOG;
+
+ cn->locus = head - pool;
+ cn->lcur = cur;
+}
+
+
+static void
+mbox_file(cn)
+ Client *cn;
+{
+ int lcur, lmax, ch, cx, cc; /* Thor.990522: ±±¨î³æ¦æ³Ì¤j¶Ç°e¶q, Char Count */
+ char *bptr, *pool, *head, *tail;
+
+ /* send out a text file in MIME mode */
+
+ bptr = cn->bptr;
+ if (!bptr)
+ return;
+
+ MYDOG;
+
+ pool = cn->pool;
+ head = pool + cn->locus;
+ tail = pool + SNDBUFSIZ - SNDLINSIZ - 4 - 3; /* Thor.991129: µ²§Àªº.\r\n 3¦r */
+
+ lcur = cn->lcur;
+ lmax = cn->lmax;
+ cx = '\n'; cc = 0;
+
+ while (ch = *bptr)
+ {
+ /* Thor.990522: ±±¨î³æ¦æ³Ì¤j¶Ç°e¶q, Char Count. ¼g±oÁà¤FÂI, ·|¶]´N¦n:p */
+ if (cc++ >= SNDLINSIZ)
+ ch = '\n';
+ else
+ bptr++;
+
+ if (ch == '\r')
+ continue;
+
+ if (ch == '\n')
+ {
+ cc = 0; /* Thor.990522: ±±¨î³æ¦æ³Ì¤j¶Ç°e¶q, Char Count */
+
+ *head++ = '\r';
+ *head++ = '\n';
+ if (++lcur >= lmax)
+ break;
+
+ if (head > tail)
+ {
+ cn->state = CS_FILE;
+ cn->bptr = bptr;
+ cn->lcur = lcur;
+ cn->locus = head - pool;
+ return;
+ }
+ cx = ch;
+ continue;
+ }
+
+ if (ch == '.' && cx == '\n')
+ *head++ = ch;
+
+ *head++ = ch;
+ cx = ch;
+ }
+
+ /* end of mail body transmission */
+
+#if 1
+ /* Thor.981206: Odysseus patch:
+ §@ªÌ Odysseus.bbs@bbs.cs.nccu.edu.tw (¥Ñ°ø¤J»üÃø~~), ¬ÝªO plan
+ ¼ÐÃD Re: bpop3d?
+ ®É¶¡ ¬F¤j¿ßªÅ¦æÀ] (Thu Oct 15 01:19:56 1998)
+¢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
+¡° ¤Þ­z¡mdreamer.bbs@Rouge.Dorm10.NCTU.edu.tw (¸H¤ß«÷¹Ï)¡n¤§»Ê¨¥¡G
+: ¦ý¬O¹ê»Ú¥Î Netscape Messager ¥h¸Õ¸Õ¬Ý,
+: ±o¨ìÁ`¦@«H¥ó¼Æ¥Ø¤§«á,
+: ·í­n¥h retreive ²Ä¤@«Ê«H´N·í¦í¤F....-_-
+: ps. Solaris 2.5.1, Maple 3.02
+ */
+ *head++ = '\r';
+ *head++ = '\n';
+#endif
+
+ *head++ = '.';
+ *head++ = '\r';
+ *head++ = '\n';
+ cn->locus = head - pool;
+ cn->state = CS_WRITE;
+
+ MYDOG;
+ free(cn->body);
+ MYDOG;
+ cn->bptr = cn->body = NULL;
+}
+
+
+/* ----------------------------------------------------- */
+/* supporting commands */
+/* ----------------------------------------------------- */
+
+
+static void cmd_xxxx();
+static void client_flush(); /* Thor.991221: µù¸Ñ: ¤£¥Î¦bmsg¥[ \r\n, µ{¦¡·|¥[ */
+
+/* Thor.991221: µù¸Ñ: ­n¦bmsg¥[ \r\n, µ{¦¡¤£¥[ */
+#define CMD_MSG(cn, msg) \
+ cn->state = CS_WRITE; memcpy(cn->pool, msg, cn->locus = sizeof(msg) - 1);
+
+
+static void
+do_argument(cn)
+ Client *cn;
+{
+ CMD_MSG(cn, POP3_ERRARG_MSG);
+}
+
+
+/* return value : -1(all), 0, n */
+
+
+static int
+do_number(cn, n)
+ Client *cn;
+ int n; /* 0 ==> exact 1 arg -1 ==> 1 or 0 arg */
+{
+ char *cmd;
+
+ if (cn->mode < CM_LOGIN)
+ {
+ cmd_xxxx(cn);
+ return 0;
+ }
+
+ cmd = parse_token(NULL, 0);
+
+ if (!cmd || !*cmd)
+ {
+ if (n == 0)
+ do_argument(cn);
+ return n;
+ }
+
+ n = atoi(cmd);
+ if (n <= 0 || n > cn->pcount)
+ {
+ CMD_MSG(cn, POP3_ERRNUM_MSG);
+ return 0;
+ }
+
+ return n;
+}
+
+
+/* ----------------------------------------------------- */
+/* main commands */
+/* ----------------------------------------------------- */
+
+
+static void
+cmd_xxxx(cn)
+ Client *cn;
+{
+ CMD_MSG(cn, POP3_ERRCMD_MSG);
+}
+
+
+static void
+cmd_noop(cn)
+ Client *cn;
+{
+ CMD_MSG(cn, "+OK\r\n");
+}
+
+
+static void
+cmd_user(cn)
+ Client *cn;
+{
+ int fd;
+ ACCT acct;
+ char *userid, *ptr, fpath[80], msg[128];
+ char *msg_no_user = "-ERR no such user in our server";
+
+ MYDOG;
+
+ if (cn->mode >= CM_LOGIN)
+ {
+ cmd_xxxx(cn);
+ return;
+ }
+
+ userid = parse_token(NULL, LOWER);
+
+ if (!userid || !*userid)
+ {
+ do_argument(cn);
+ return;
+ }
+
+ /* userid is "userid.bbs" or "userid" */
+
+ if (ptr = strchr(userid, '.'))
+ {
+ if (strcmp(ptr, ".bbs"))
+ {
+ client_flush(cn, msg_no_user);
+ return;
+ }
+ *ptr = '\0';
+ }
+
+ /* Thor.981122: chc@gaisnews.iis.sinica.edu.tw patch:
+ ¥i¯àµo¥Íªº°ÝÃD:
+ ·í connect ¨ì bpop3d ®É, ¦pªG¿é¤J userid ¤Óªø, ´N·|³y¦¨ overflow,
+ ª¬ªp»´®É·| disconnect, ÄY­«®É·|¨Ï bpop3d segmentation fault.
+ ¦³¤ß¤H¤h¥i¯à·|¥H¦¹¯}Ãa¨t²Îªº¦w¥þ©Ê. */
+ /* Thor.990122: check §¹ *.bbs ¦A¬Ý idlen */
+ if (strlen(userid) > IDLEN)
+ {
+ client_flush(cn, msg_no_user);
+ return;
+ }
+
+#ifdef DEBUG
+ if (!strcmp(userid, STR_SYSOP)) /* ¥u¦³ sysop ¨ú«H¤~ debug */
+ {
+ cn->debug = 1;
+ }
+#endif
+
+ ptr = cn->home;
+ sprintf(ptr, "%c/%s/", *userid, userid);
+ sprintf(fpath, "%s.ACCT", ptr);
+ if ((fd = open(fpath, O_RDONLY)) >= 0)
+ {
+ read(fd, &acct, sizeof(ACCT));
+ close(fd);
+
+ if (acct.userlevel & PERM_DENYLOGIN)
+ {
+ sprintf(msg, "-ERR %s denied", acct.userid);
+ }
+ else
+ {
+ strcpy(cn->userid, acct.userid);
+ memcpy(cn->passwd, acct.passwd, PASSLEN + 1);
+ /* SoC: follow the behavior of qpopper */
+ /* sprintf(msg, "+OK Password required for %s%s", acct.userid, POP3_FQDN); */
+ sprintf(msg, "+OK Password required for %s.", acct.userid);
+ }
+ }
+ else
+ {
+ strcpy(msg, msg_no_user);
+ }
+
+ client_flush(cn, msg);
+}
+
+
+static void
+cmd_password(cn)
+ Client *cn;
+{
+ char *cmd, *passwd, msg[128];
+
+ MYDOG;
+
+ if (cn->mode >= CM_LOGIN)
+ {
+ cmd_xxxx(cn);
+ return;
+ }
+
+ passwd = cn->passwd;
+ MYDOG;
+ if (!*passwd)
+ {
+ CMD_MSG(cn, "-ERR need USER command\r\n"); /* Thor.991221: ¥[¤W \r\n */
+ return;
+ }
+
+ cmd = trail_token; /* to support password with [space] */
+
+ /* cmd = parse_token(NULL, 0); */
+
+ if (!cmd || !*cmd)
+ {
+ do_argument(cn);
+ return;
+ }
+
+ MYDOG;
+ /* Thor.990214: ²Î¤@¥Î dao library */
+ /* if (!checkpasswd(passwd, cmd)) */
+ if (chkpasswd(passwd, cmd))
+ {
+ fprintf(flog, "PASS\t[%d]\t%s\n", cn->sno, cn->userid);
+
+ MYDOG;
+ *passwd = '\0';
+ CMD_MSG(cn, "-ERR password incorrect\r\n"); /* Thor.991221: ¥[¤W \r\n */
+ return;
+ }
+
+ MYDOG;
+ mbox_open(cn);
+
+ MYDOG;
+ sprintf(msg, "+OK %s has %d messages (%d octets)",
+ cn->userid, cn->pcount, cn->pbytes);
+
+ MYDOG;
+ client_flush(cn, msg);
+
+ MYDOG;
+ fprintf(flog, "ENTER\t[%d]%s\n", cn->sno, msg + 3);
+
+ MYDOG;
+ cn->mode = CM_LOGIN;
+}
+
+
+static void
+cmd_stat(cn)
+ Client *cn;
+{
+ char *ptr;
+
+ if (cn->mode < CM_LOGIN)
+ {
+ cmd_xxxx(cn);
+ return;
+ }
+
+ ptr = cn->pool;
+ sprintf(ptr, "+OK %d %d\r\n", cn->pcount, cn->pbytes);
+ cn->locus = strlen(ptr);
+ cn->state = CS_WRITE;
+}
+
+
+static void
+cmd_last(cn)
+ Client *cn;
+{
+ char *ptr;
+
+ if (cn->mode < CM_LOGIN)
+ {
+ cmd_xxxx(cn);
+ return;
+ }
+
+ ptr = cn->pool;
+ sprintf(ptr, "+OK %d\r\n", cn->pcount);
+ cn->locus = strlen(ptr);
+ cn->state = CS_WRITE;
+}
+
+
+static void
+cmd_reset(cn)
+ Client *cn;
+{
+ int n, max;
+ POPH *phdr;
+ char *ptr;
+
+ n = cn->mode;
+ if (n < CM_LOGIN)
+ {
+ cmd_xxxx(cn);
+ return;
+ }
+
+ max = cn->pcount;
+ if (n == CM_DIRTY)
+ {
+ for (n = 0, phdr = cn->cache; n < max; phdr++, n++)
+ phdr->pmode = 0;
+ }
+
+ ptr = cn->pool;
+ sprintf(ptr, "+OK mail reset %d messages %d octets\r\n", max, cn->pbytes);
+ cn->locus = strlen(ptr);
+ cn->mode = CM_LOGIN;
+ cn->state = CS_WRITE;
+}
+
+
+static void
+cmd_retrive(cn)
+ Client *cn;
+{
+ int n;
+ POPH *phdr;
+
+ n = do_number(cn, 0);
+ if (!n)
+ return;
+
+ phdr = &cn->cache[n - 1];
+ if (phdr->pmode)
+ {
+ CMD_MSG(cn, POP3_DELETE_MSG);
+ return;
+ }
+
+ mbox_read(cn, phdr, 0x400000);
+}
+
+
+static void
+cmd_top(cn)
+ Client *cn;
+{
+ int n;
+ POPH *phdr;
+ char *cmd;
+
+ MYDOG;
+
+ n = do_number(cn, 0);
+ if (!n)
+ return;
+
+ phdr = &cn->cache[n - 1];
+ if (phdr->pmode)
+ {
+ CMD_MSG(cn, POP3_DELETE_MSG);
+ return;
+ }
+
+ cmd = parse_token(NULL, 0);
+
+ if (!cmd || !*cmd)
+ {
+ do_argument(cn);
+ return;
+ }
+
+ n = atoi(cmd);
+ if (n < 0)
+ {
+ CMD_MSG(cn, POP3_ERRNUM_MSG);
+ return;
+ }
+
+ mbox_read(cn, phdr, n);
+}
+
+
+static void
+cmd_delete(cn)
+ Client *cn;
+{
+ int n;
+ POPH *phdr;
+
+ MYDOG;
+
+ if (!(n = do_number(cn, 0)))
+ return;
+
+ phdr = &cn->cache[n - 1];
+ if (phdr->pmode)
+ {
+ CMD_MSG(cn, POP3_DELETE_MSG);
+ return;
+ }
+
+ phdr->pmode = MAIL_DELETE;
+ cn->mode = CM_DIRTY;
+ CMD_MSG(cn, "+OK message deleted\r\n");
+}
+
+
+static void
+cmd_list(cn)
+ Client *cn;
+{
+ int n;
+ POPH *phdr;
+ char buf[80];
+
+ MYDOG;
+
+ n = do_number(cn, -1);
+
+ MYDOG;
+
+ if (n < 0)
+ {
+ cn->lcur = 0;
+ cn->state = CS_INDEX;
+ }
+ else if (n > 0)
+ {
+ phdr = &cn->cache[n - 1];
+ if (phdr->pmode)
+ {
+ CMD_MSG(cn, POP3_DELETE_MSG);
+ }
+ else
+ {
+ sprintf(buf, "+OK %d %d", n, phdr->psize);
+ client_flush(cn, buf);
+ }
+ }
+}
+
+
+static void
+cmd_uidl(cn)
+ Client *cn;
+{
+ int n;
+ POPH *phdr;
+ char buf[80];
+
+ n = do_number(cn, -1);
+
+ if (n < 0)
+ {
+ cn->lcur = 0;
+ cn->state = CS_UIDL;
+ }
+ else if (n > 0)
+ {
+ phdr = &cn->cache[n - 1];
+ if (phdr->pmode)
+ {
+ CMD_MSG(cn, POP3_DELETE_MSG);
+ }
+ else
+ {
+ sprintf(buf, "+OK %d %s", n, phdr->xname + 1);
+ client_flush(cn, buf);
+ }
+ }
+}
+
+
+static void
+cmd_quit(cn)
+ Client *cn;
+{
+ if (cn->mode >= CM_DIRTY)
+ {
+ mbox_close(cn);
+ }
+
+ fprintf(flog, "QUIT\t[%d] %s S%d T%d\n",
+ cn->sno, cn->userid, cn->xbytes, time(0) - cn->tbirth);
+
+ cn->state = CS_FLUSH;
+ strcpy(cn->pool, POP3_BYE_MSG);
+ cn->locus = POP3_BYE_LEN;
+}
+
+
+typedef struct
+{
+ char *cmd;
+ void (*fun) ();
+} PopCmd;
+
+
+static PopCmd cmdlist[] =
+{
+ "list", cmd_list,
+ "uidl", cmd_uidl,
+ "retr", cmd_retrive,
+ "dele", cmd_delete,
+
+ "user", cmd_user,
+ "pass", cmd_password,
+ "stat", cmd_stat,
+ "quit", cmd_quit,
+
+ "last", cmd_last,
+ "top", cmd_top,
+
+ "rset", cmd_reset,
+ "noop", cmd_noop,
+ NULL, cmd_xxxx
+};
+
+
+/* ----------------------------------------------------- */
+/* put a line into client's buffer, padding with "\r\n" */
+/* ----------------------------------------------------- */
+
+
+static void
+client_flush(cn, msg)
+ Client *cn;
+ char *msg;
+{
+ char *pool, *head;
+
+ head = pool = cn->pool;
+
+
+#if 0 /* itoc.010606: ¦pªG client ¬O¥H´«¦æ¦r¤¸¨Ó§P§O°T®§µ²§ôªº¸Ü¡A´N·|¥d¦º¤F */
+ while (*head++ = *msg++)
+ ;
+#endif
+ while (*head = *msg++)
+ head++;
+
+
+ /* SoC: Previous trick causes the output msg ending with "00 0D 0A" */
+ head--;
+
+ *head++ = '\r';
+ *head++ = '\n';
+ cn->locus = head - pool;
+ cn->state = CS_WRITE;
+}
+
+
+/* ----------------------------------------------------- */
+/* send data to client */
+/* ----------------------------------------------------- */
+
+
+static int
+client_write(cn)
+ Client *cn;
+{
+ int len, cc;
+ char *data;
+
+ len = cn->locus;
+ data = cn->pool;
+ MYDOG;
+ cc = send(cn->sock, data, len, 0);
+ MYDOG;
+
+ if (cc <= 0)
+ {
+ MYDOG;
+ cc = errno;
+ if (cc != EWOULDBLOCK)
+ {
+ fprintf(flog, "SEND\t[%d] %s\n", cn->sno, strerror(cc));
+ return 0;
+ }
+
+ return -1; /* would block, so leave it to do later */
+ }
+
+#ifdef DEBUG
+ if (cn->debug)
+ {
+ char msg[SNDBUFSIZ + 10];
+ sprintf(msg, "%s\t[%d]bpop3>>>\n", Now(), cn->sno);
+ f_cat(POP3_DEBUGFILE, msg);
+ str_ncpy(msg, data, len+1);
+ f_cat(POP3_DEBUGFILE, msg);
+ sprintf(msg, "\t[%d]bpop3<<<\n", cn->sno);
+ f_cat(POP3_DEBUGFILE, msg);
+ MYDOG;
+ }
+#endif
+
+ MYDOG;
+ cn->xbytes += cc;
+ MYDOG;
+ len -= cc;
+ MYDOG;
+ cn->locus = len;
+ MYDOG;
+ if (len)
+ {
+ MYDOG;
+ memcpy(data, data + cc, len);
+ MYDOG;
+ return cc;
+ }
+
+ MYDOG;
+ len = cn->state;
+ MYDOG;
+
+ if (len == CS_FLUSH)
+ return 0;
+
+ if (len == CS_WRITE)
+ cn->state = CS_READ;
+
+ MYDOG;
+ return cc;
+}
+
+
+/* ----------------------------------------------------- */
+/* client's service dispatcher */
+/* ----------------------------------------------------- */
+
+
+static void
+client_serve(cn)
+ Client *cn;
+{
+ char *cmd, *str;
+ PopCmd *pc;
+
+ cn->locus = 0; /* reset buffer pool position */
+
+ cmd = parse_token(cn->pool, LOWER);
+
+ if (!cmd || !*cmd)
+ return;
+
+ for (pc = cmdlist; str = pc->cmd; pc++)
+ {
+ if (!strcmp(cmd, str))
+ break;
+ }
+
+#ifdef DEBUG
+ logit(cmd, cn->userid);
+#endif
+
+ MYDOG;
+ (*pc->fun) (cn);
+ MYDOG;
+
+#ifdef DEBUG
+ {
+ char buf[80];
+
+ /* Thor.990222: ©È str§ä¤£¨ìcmd®É ¬° NULL */
+ /* sprintf(buf, "%s-", str); */
+ sprintf(buf, "%s-", cmd);
+ logit(buf, cn->userid);
+ }
+#endif
+}
+
+
+/* ----------------------------------------------------- */
+/* receive command from client */
+/* ----------------------------------------------------- */
+
+
+static int
+client_read(cn)
+ Client *cn;
+{
+ int cc, pos, len;
+ char *str;
+
+ MYDOG;
+
+ pos = cn->locus;
+ str = &cn->pool[pos];
+ len = recv(cn->sock, str, RCVBUFSIZ, 0);
+
+ if (len <= 0)
+ {
+ cc = errno;
+ if (cc != EWOULDBLOCK)
+ {
+ fprintf(flog, "RECV\t[%d] %s\n", cn->sno, strerror(cc));
+ return 0;
+ }
+
+ return -1;
+ }
+
+ str[len] = '\0';
+
+#ifdef DEBUG
+ if (cn->debug)
+ {
+ char msg[80];
+ sprintf(msg, "%s\t[%d]peer>>>\n", Now(), cn->sno);
+ f_cat(POP3_DEBUGFILE, msg);
+ f_cat(POP3_DEBUGFILE, str);
+ sprintf(msg, "\t[%d]peer<<<\n", cn->sno);
+ f_cat(POP3_DEBUGFILE, msg);
+ }
+#endif
+
+ while (cc = *str)
+ {
+ switch (cc)
+ {
+ case '\r':
+ case '\n':
+ *str = '\0';
+ client_serve(cn);
+ return 1;
+
+ case '\t':
+ *str = ' ';
+ }
+ str++;
+ }
+
+ pos += len;
+ if (pos >= RCVBUFSIZ)
+ {
+ sprintf(str, "[%d] buffer overflow", cn->sno);
+ logit("HACK", str);
+ return 0;
+ }
+
+ cn->locus = pos;
+ return 1;
+}
+
+
+/* ----------------------------------------------------- */
+/* release idle/timeout connections */
+/* ----------------------------------------------------- */
+
+
+static void
+client_close(cn)
+ Client *cn;
+{
+ int sock;
+ char *ptr;
+
+ if (cn->mode >= CM_LOGIN)
+ {
+ if (ptr = (char *) cn->cache)
+ free(ptr);
+
+ if (ptr = cn->body)
+ free(ptr);
+ }
+
+ sock = cn->sock;
+ shutdown(sock, 2);
+ close(sock);
+}
+
+
+/* ----------------------------------------------------- */
+/* server core routines */
+/* ----------------------------------------------------- */
+
+
+static void
+/* start_daemon() */
+servo_daemon(inetd)
+ int inetd;
+{
+ int fd, value;
+ char buf[80];
+ struct sockaddr_in sin;
+ struct linger ld;
+#ifdef HAVE_RLIMIT
+ struct rlimit limit;
+#endif
+
+ /*
+ * 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((time_t *)&value);
+ gmtime((time_t *)&value);
+ strftime(buf, 80, "%d/%b/%Y:%H:%M:%S", localtime((time_t *)&value));
+
+#ifdef HAVE_RLIMIT
+ /* --------------------------------------------------- */
+ /* adjust the resource limit */
+ /* --------------------------------------------------- */
+
+ limit.rlim_cur = limit.rlim_max = 8 * 1024 * 1024;
+ setrlimit(RLIMIT_DATA, &limit);
+
+#ifdef SOLARIS
+#define RLIMIT_RSS RLIMIT_AS /* Thor.981206: port for solaris 2.6 */
+#endif
+
+ setrlimit(RLIMIT_RSS, &limit);
+
+ limit.rlim_cur = limit.rlim_max = 0;
+ setrlimit(RLIMIT_CORE, &limit);
+#endif
+
+ /* --------------------------------------------------- */
+ /* detach daemon process */
+ /* --------------------------------------------------- */
+
+ close(2);
+ close(1);
+
+ if (inetd)
+ return;
+
+ close(0);
+
+ if (fork())
+ exit(0);
+
+ setsid();
+
+ if (fork())
+ exit(0);
+
+ /* --------------------------------------------------- */
+ /* setup socket */
+ /* --------------------------------------------------- */
+
+ fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+
+ value = 1;
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &value, sizeof(value));
+
+ ld.l_onoff = ld.l_linger = 0;
+ setsockopt(fd, SOL_SOCKET, SO_LINGER, (char *) &ld, sizeof(ld));
+
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(POP3_PORT);
+ sin.sin_addr.s_addr = INADDR_ANY;
+ memset(sin.sin_zero, 0, sizeof(sin.sin_zero));
+
+ if (bind(fd, (struct sockaddr *) & sin, sizeof(sin)) ||
+ listen(fd, SOCK_BACKLOG))
+ exit(1);
+}
+
+
+static void
+abort_server()
+{
+ fclose(flog);
+ exit(0);
+}
+
+
+static void
+reaper()
+{
+ while (waitpid(-1, NULL, WNOHANG | WUNTRACED) > 0);
+}
+
+
+static void
+sig_trap(sig)
+ int sig;
+{
+ char buf[32];
+
+ sprintf(buf, "[%d] at %d (err: %d)", sig, gline, errno);
+ logit("TRAP", buf);
+ abort_server();
+}
+
+
+#ifdef SERVER_USAGE
+static void
+server_usage()
+{
+ struct rusage ru;
+
+ if (getrusage(RUSAGE_SELF, &ru))
+ return;
+
+ fprintf(flog, "\n[Server Usage]\n\n"
+ "user time: %.6f\n"
+ "system time: %.6f\n"
+ "maximum resident set size: %lu P\n"
+ "integral resident set size: %lu\n"
+ "page faults not requiring physical I/O: %d\n"
+ "page faults requiring physical I/O: %d\n"
+ "swaps: %d\n"
+ "block input operations: %d\n"
+ "block output operations: %d\n"
+ "messages sent: %d\n"
+ "messages received: %d\n"
+ "signals received: %d\n"
+ "voluntary context switches: %d\n"
+ "involuntary context switches: %d\ngline: %d\n\n",
+
+ (double) ru.ru_utime.tv_sec + (double) ru.ru_utime.tv_usec / 1000000.0,
+ (double) ru.ru_stime.tv_sec + (double) ru.ru_stime.tv_usec / 1000000.0,
+ ru.ru_maxrss,
+ ru.ru_idrss,
+ ru.ru_minflt,
+ ru.ru_majflt,
+ ru.ru_nswap,
+ ru.ru_inblock,
+ ru.ru_oublock,
+ ru.ru_msgsnd,
+ ru.ru_msgrcv,
+ ru.ru_nsignals,
+ ru.ru_nvcsw,
+ ru.ru_nivcsw, gline);
+
+ fflush(flog);
+}
+#endif
+
+
+/* ----------------------------------------------------- */
+/* signal routines */
+/* ----------------------------------------------------- */
+
+
+static void
+main_signals()
+{
+ struct sigaction act;
+
+ /* sigblock(sigmask(SIGPIPE)); */ /* Thor.981206: ²Î¤@ POSIX ¼Ð·Ç¥Îªk */
+
+ /* act.sa_mask = 0; */ /* Thor.981105: ¼Ð·Ç¥Îªk */
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+
+ act.sa_handler = sig_trap;
+ sigaction(SIGBUS, &act, NULL);
+ sigaction(SIGSEGV, &act, NULL);
+
+ act.sa_handler = reaper;
+ sigaction(SIGCHLD, &act, NULL);
+
+ act.sa_handler = abort_server;
+ sigaction(SIGTERM, &act, NULL);
+
+#ifdef SERVER_USAGE
+ act.sa_handler = server_usage;
+ sigaction(SIGPROF, &act, NULL);
+#endif
+
+ /* Thor.981206: lkchu patch: ²Î¤@ POSIX ¼Ð·Ç¥Îªk */
+ /* ¦b¦¹­É¥Î sigset_t act.sa_mask */
+ sigaddset(&act.sa_mask, SIGPIPE);
+ sigprocmask(SIG_BLOCK, &act.sa_mask, NULL);
+
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int main_sno, csock, nfds, state;
+ Client **FBI, *Scully, *Mulder, *cn;
+ fd_set rset, wset, xset;
+ struct timeval tv;
+ time_t uptime, tcheck;
+
+ state = 0;
+
+ while ((nfds = getopt(argc, argv, "hid")) != -1)
+ {
+ switch (nfds)
+ {
+ case 'i':
+ state = 1;
+ break;
+
+ case 'd':
+ break;
+
+ case 'h':
+ default:
+
+ fprintf(stderr, "Usage: %s [options]\n"
+ "\t-i start from inetd with wait option\n"
+ "\t-d debug mode\n"
+ "\t-h help\n",
+ argv[0]);
+ exit(0);
+ }
+ }
+
+ servo_daemon(state);
+ /* start_daemon(); */
+
+ setgid(BBSGID);
+ setuid(BBSUID);
+ chdir(POP3_HOME);
+ main_signals();
+
+ umask(077);
+ log_init();
+
+#if 0
+ dns_init();
+#endif
+
+ tv.tv_sec = POP3_TIMEOUT;
+ tv.tv_usec = 0;
+
+ tcheck = time(0) + POP3_TIMEOUT;
+ Scully = Mulder = NULL;
+ main_sno = 0;
+
+ for (;;)
+ {
+ /* resource and garbage collection */
+
+ uptime = time(0);
+ if (tcheck < uptime)
+ {
+ tcheck = uptime - POP3_TIMEOUT;
+
+ for (FBI = &Scully; cn = *FBI;)
+ {
+ if (cn->uptime < tcheck)
+ {
+ client_close(cn);
+
+ *FBI = cn->next;
+
+ cn->next = Mulder;
+ Mulder = cn;
+ }
+ else
+ {
+ FBI = &(cn->next);
+ }
+ }
+
+ fflush(flog);
+ tcheck = uptime + POP3_TIMEOUT;
+ }
+
+ /* ------------------------------------------------- */
+ /* Set up the fdsets */
+ /* ------------------------------------------------- */
+
+ FD_ZERO(&rset);
+ FD_ZERO(&wset);
+ FD_ZERO(&xset);
+
+ FD_SET(0, &rset);
+ nfds = 0;
+
+ for (cn = Scully; cn; cn = cn->next)
+ {
+ csock = cn->sock;
+ state = cn->state;
+
+ if (nfds < csock)
+ nfds = csock;
+
+ if (state == CS_READ)
+ {
+ FD_SET(csock, &rset);
+ }
+ else
+ {
+ switch (state)
+ {
+ case CS_FILE:
+ mbox_file(cn);
+ break;
+
+ case CS_INDEX:
+ case CS_UIDL:
+ mbox_index(cn, state);
+ break;
+ }
+
+ FD_SET(csock, &wset);
+ }
+
+ FD_SET(csock, &xset);
+ }
+
+ {
+ struct timeval tv_tmp = tv;
+ /* Thor.981221: for future reservation bug */
+ nfds = select(nfds + 1, &rset, &wset, &xset, &tv_tmp);
+ }
+
+ if (nfds == 0)
+ {
+ continue;
+ }
+
+ if (nfds < 0)
+ {
+ csock = errno;
+ if (csock != EINTR)
+ {
+ logit("select", strerror(csock));
+ }
+ continue;
+ }
+
+ /* ------------------------------------------------- */
+ /* serve active agents */
+ /* ------------------------------------------------- */
+
+ uptime = time(0);
+
+ for (FBI = &Scully; cn = *FBI;)
+ {
+ csock = cn->sock;
+
+ if (FD_ISSET(csock, &wset))
+ {
+ MYDOG;
+ state = client_write(cn);
+ MYDOG;
+ }
+ else if (FD_ISSET(csock, &rset))
+ {
+ MYDOG;
+ state = client_read(cn);
+ MYDOG;
+ }
+ else if (FD_ISSET(csock, &xset))
+ {
+ MYDOG;
+ state = 0;
+ MYDOG;
+ }
+ else
+ {
+ state = -1;
+ }
+
+ if (state == 0) /* fire this agent */
+ {
+ client_close(cn);
+
+ *FBI = cn->next;
+
+ cn->next = Mulder;
+ Mulder = cn;
+
+ continue;
+ }
+
+ if (state > 0)
+ {
+ MYDOG;
+ cn->uptime = uptime;
+ MYDOG;
+ }
+
+ MYDOG;
+ FBI = &(cn->next);
+ MYDOG;
+ }
+
+ /* ------------------------------------------------- */
+ /* serve new connection */
+ /* ------------------------------------------------- */
+
+ if (FD_ISSET(0, &rset))
+ {
+ /* Thor.990222: ¬d¥X¹ï¤è ip */
+ int len;
+ struct sockaddr_in csin;
+ len = sizeof csin;
+
+ for (;;)
+ {
+ /* csock = accept(0, NULL, NULL); */
+ /* Thor.990222: ¬d¥X¹ï¤è ip */
+ csock = accept(0, (struct sockaddr *)&csin, &len);
+
+ if (csock > 0)
+ break;
+
+ state = errno;
+ if (state != EINTR)
+ {
+ logit("accept", strerror(state));
+ break;
+ }
+
+ while (waitpid(-1, NULL, WNOHANG | WUNTRACED) > 0);
+ }
+
+ if (csock <= 0)
+ continue;
+
+ if (cn = Mulder)
+ {
+ Mulder = cn->next;
+ }
+ else
+ {
+ cn = (Client *) malloc(sizeof(Client));
+ }
+
+ *FBI = cn;
+
+ /* variable initialization */
+
+ /* sprintf(cn->pool, "[%d]", ++main_sno); */
+ /* Thor.990222: ¬d¥X¹ï¤è ip */
+ sprintf(cn->pool, "[%d] ip:%08x", ++main_sno, csin.sin_addr.s_addr);
+ logit("CONN", cn->pool);
+
+ cn->next = NULL;
+ cn->sock = csock;
+ cn->sno = main_sno;
+
+#ifdef DEBUG
+ cn->debug = 0;
+#endif
+ cn->mode = 0;
+ cn->passwd[0] = '\0';
+ cn->cache = NULL;
+ cn->body = NULL;
+ cn->bptr = NULL;
+
+ cn->state = CS_WRITE;
+ sprintf(cn->pool, "+OK MapleBBS POP3 server ready\r\n");
+ cn->locus = strlen(cn->pool);
+ cn->tbirth = cn->uptime = uptime;
+ cn->xbytes = 0;
+ }
+
+ /* ------------------------------------------------- */
+ /* tail of main loop */
+ /* ------------------------------------------------- */
+ }
+}
diff --git a/daemon/gemd.c b/daemon/gemd.c
new file mode 100644
index 0000000..379145a
--- /dev/null
+++ b/daemon/gemd.c
@@ -0,0 +1,1066 @@
+/*-------------------------------------------------------*/
+/* util/gemd.c ( NTHU CS MapleBBS Ver 3.00 ) */
+/*-------------------------------------------------------*/
+/* target : BBS gopher daemon */
+/* create : 96/11/20 */
+/* update : 97/03/29 */
+/*-------------------------------------------------------*/
+/* syntax : gemd */
+/*-------------------------------------------------------*/
+/* notice : single process concurrent server */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+
+#include <netinet/tcp.h>
+#include <sys/wait.h>
+#include <sys/resource.h>
+
+
+#define WATCH_DOG
+#define SERVER_USAGE
+
+
+#define GEMD_LOGFILE "run/gemd.log"
+#define GEMD_PIDFILE "run/gemd.pid"
+
+
+#define GEMD_PERIOD (60 * 30)
+#define GEMD_TIMEOUT (60 * 10)
+
+
+#define TCP_QLEN 3
+#define TCP_BUFSIZ (256 * 14)
+#define TCP_LINSIZ 256
+#define TCP_RCVSIZ 512
+
+
+/* ----------------------------------------------------- */
+/* client connection structure */
+/* ----------------------------------------------------- */
+
+
+typedef struct Agent
+{
+ struct Agent *anext;
+ int state;
+ int sock;
+ int sno;
+ time_t tbegin; /* «Ø¥ß connection ªº®É¶¡ */
+ time_t uptime;
+
+ FILE *stream;
+ char zone[64];
+
+ char pool[TCP_BUFSIZ]; /* buffered I/O pool */
+ int locus;
+ int xdata;
+} Agent;
+
+
+#define ap_log(key, sno, msg) fprintf(flog, "%s\t[%d] %s\n", key, sno, msg);
+
+
+/* ----------------------------------------------------- */
+/* connection state */
+/* ----------------------------------------------------- */
+
+
+#define CS_FREE 0x00
+#define CS_READ 0x01 /* reading command */
+#define CS_FILE 0x02 /* writing file */
+#define CS_INDEX 0x03 /* writing index */
+#define CS_GPLUS 0x04 /* writing index (gopher 2.0+) */
+#define CS_FLUSH 0x05 /* flush data */
+
+
+/* ----------------------------------------------------- */
+/* operation log and debug information */
+/* ----------------------------------------------------- */
+
+
+extern int errno;
+
+
+static FILE *flog; /* log file descriptor */
+static int gline;
+
+
+#ifdef WATCH_DOG
+# define MYDOG gline = __LINE__
+#else
+# define MYDOG /* NOOP */
+#endif
+
+
+static void
+logit(key, msg)
+ char *key;
+ char *msg;
+{
+ time_t now;
+ struct tm *p;
+
+ time(&now);
+ p = localtime(&now);
+ fprintf(flog, "%02d/%02d %02d:%02d:%02d %-7s%s\n",
+ p->tm_mon + 1, p->tm_mday,
+ p->tm_hour, p->tm_min, p->tm_sec, key, msg);
+}
+
+
+static inline void
+log_open()
+{
+ FILE *fp;
+
+ umask(077);
+
+ if (fp = fopen(GEMD_PIDFILE, "w"))
+ {
+ fprintf(fp, "%d\n", getpid());
+ fclose(fp);
+ }
+
+ flog = fopen(GEMD_LOGFILE, "w");
+ logit("START", "gemd (gopher) daemon");
+}
+
+
+/*-------------------------------------------------------*/
+/* BRD shm ³¡¤À¶·»P cache.c ¬Û®e */
+/*-------------------------------------------------------*/
+
+
+static BCACHE *bshm;
+
+
+static void
+init_bshm()
+{
+ /* itoc.030727: ¦b¶}±Ò bbsd ¤§«e¡AÀ³¸Ó´N­n°õ¦æ¹L account¡A
+ ©Ò¥H bshm À³¸Ó¤w³]©w¦n */
+
+ bshm = shm_new(BRDSHM_KEY, sizeof(BCACHE));
+
+ if (bshm->uptime <= 0) /* bshm ¥¼³]©w§¹¦¨ */
+ exit(0);
+}
+
+
+/* itoc.030708: ¥[¤WÀˬd¬ÝªOÅv­­ªº³¡¤À¡A¥H§K¦³¤H¶Ã¿å */
+
+static int
+allow_brdname(brdname)
+ char *brdname;
+{
+ BRD *bhdr, *tail;
+
+ bhdr = bshm->bcache;
+ tail = bhdr + bshm->number;
+
+ do
+ {
+ if (!strcmp(bhdr->brdname, brdname))
+ {
+ if (!bhdr->readlevel)
+ return 1;
+ break;
+ }
+ } while (++bhdr < tail);
+
+ return 0;
+}
+
+
+static int
+allow_path(fpath)
+ char *fpath;
+{
+ char *brdname, *str;
+ int ret;
+
+ if (!strncmp(fpath, "brd/", 4))
+ {
+ brdname = fpath + 4;
+ if (str = strchr(brdname, '/'))
+ {
+ *str = '\0';
+ ret = allow_brdname(brdname);
+ *str = '/';
+ return ret;
+ }
+ }
+
+ return 1;
+}
+
+
+/* ----------------------------------------------------- */
+/* server side stuff */
+/* ----------------------------------------------------- */
+
+
+static char *
+str_copy(dst, src)
+ char *dst;
+ char *src;
+{
+ int ch;
+
+ while (ch = *src++)
+ *dst++ = ch;
+ return dst;
+}
+
+
+static FILE *
+gem_open(fpath)
+ char *fpath;
+{
+ FILE *fp;
+ int fd;
+ struct stat st;
+
+ if (!allow_path(fpath))
+ return NULL;
+
+ fp = NULL;
+ if ((fd = open(fpath, O_RDONLY)) >= 0)
+ {
+ if (fstat(fd, &st) || !S_ISREG(st.st_mode) ||
+ (st.st_size <= 0) || !(fp = fdopen(fd, "r")))
+ {
+ close(fd);
+ }
+ }
+ return fp;
+}
+
+
+/* ----------------------------------------------------- */
+/* transform ASCII file to text-mail format */
+/* ----------------------------------------------------- */
+
+
+static void
+gem_file(ap)
+ Agent *ap;
+{
+ char *pool, *head, *tail;
+ FILE *fp;
+ int cc;
+
+ fp = ap->stream;
+ pool = ap->pool;
+ head = pool + ap->locus;
+ tail = pool + TCP_BUFSIZ - TCP_LINSIZ;
+
+ while (head < tail)
+ {
+ if (!fgets(head, TCP_LINSIZ, fp))
+ {
+ fclose(fp);
+ ap->stream = NULL;
+ ap->state = CS_FLUSH;
+
+ *head++ = '.';
+ *head++ = '\r';
+ *head++ = '\n';
+ break;
+ }
+
+ while (cc = *head)
+ {
+ if (cc == '\n')
+ {
+ *head++ = '\r';
+ *head++ = '\n';
+ break;
+ }
+ head++;
+ }
+ }
+ ap->locus = head - pool;
+}
+
+
+/* ----------------------------------------------------- */
+/* transform FOLDER to gopher index format */
+/* ----------------------------------------------------- */
+
+
+static void
+gem_index(ap)
+ Agent *ap;
+{
+ char *pool, *head, *tail, *xname, *zone;
+ int cc, state, xmode;
+ FILE *fp;
+ HDR hdr;
+
+ fp = ap->stream;
+ zone = ap->zone;
+ pool = ap->pool;
+ head = pool + ap->locus;
+ tail = pool + TCP_BUFSIZ - TCP_LINSIZ;
+ state = ap->state;
+
+ while (head < tail)
+ {
+ if (fread(&hdr, sizeof(hdr), 1, fp) != 1)
+ {
+ fclose(fp);
+ ap->stream = NULL;
+ ap->state = CS_FLUSH;
+
+ *head++ = '.';
+ *head++ = '\r';
+ *head++ = '\n';
+ break;
+ }
+
+ xmode = hdr.xmode;
+ if ((xmode & GEM_RESTRICT) || (hdr.title[0] == '#'))
+ continue;
+
+ if (state == CS_GPLUS)
+ head = str_copy(head, "+INFO: ");
+
+ *head++ = cc = (xmode & GEM_FOLDER) ? '1' : '0';
+
+ head = str_copy(head, hdr.title);
+ *head++ = '\t';
+
+ xname = hdr.xname;
+
+ *head++ = cc;
+ *head++ = '/';
+
+ if (xmode & GEM_BOARD)
+ {
+ brd_fpath(head, xname, FN_DIR);
+ head += strlen(head);
+ }
+ else
+ {
+ head = str_copy(head, zone);
+
+ cc = *xname;
+ if (cc != '@')
+ cc = xname[7];
+ *head++ = cc;
+ *head++ = '/';
+
+ head = str_copy(head, xname);
+ }
+
+ head = str_copy(head, "\t" MYHOSTNAME "\t70\r\n");
+ }
+ ap->locus = head - pool;
+}
+
+
+/* ----------------------------------------------------- */
+/* parse the command, check security & serve it */
+/* ----------------------------------------------------- */
+
+
+static int
+agent_serve(ap)
+ Agent *ap;
+{
+ char *data, *str, *zone;
+ int cc;
+ FILE *fp;
+
+ data = ap->pool;
+ if (strstr(data, "..") || strstr(data, "//"))
+ return CS_FREE;
+
+ switch (*data)
+ {
+ case '0':
+ data += 2;
+ if (*data == '/')
+ return CS_FREE;
+ cc = CS_FILE;
+ break;
+
+ case '1':
+ if (data[1])
+ {
+ data += 2;
+ if (*data == '/')
+ return CS_FREE;
+ cc = CS_INDEX;
+ break;
+ }
+
+ case 0:
+ data = ".DIR";
+ cc = CS_INDEX;
+ break;
+
+ case '\t':
+ data = ".DIR";
+ cc = CS_GPLUS;
+ break;
+
+ default:
+ return CS_FREE;
+ }
+
+ fp = gem_open(data);
+
+ zone = ap->zone;
+ *zone = '\0';
+
+ if (!fp && cc == CS_INDEX && *data != '.' && strlen(data) <= BNLEN)
+ {
+ /* map "xyz" to "brd/xyz/.DIR" and try it once again */
+
+ strcpy(zone, data);
+ brd_fpath(data, zone, FN_DIR);
+ fprintf(flog, "MAP\t[%d] %s -> %s\n", ap->sno, zone, data);
+ fp = gem_open(data);
+ }
+
+ if (!fp)
+ return CS_FREE;
+
+ ap->stream = fp;
+ if (cc != CS_FILE)
+ {
+ ap_log("DIR", ap->sno, data);
+ if (str = strrchr(data, '/'))
+ {
+ if (str[1] != '.')
+ str--;
+ else
+ str++;
+
+ while (data < str)
+ {
+ *zone++ = *data++;
+ }
+ }
+ *zone = '\0';
+ ap_log("ZONE", ap->sno, ap->zone);
+ }
+ else
+ {
+ ap_log("FILE", ap->sno, data);
+ }
+ return cc;
+}
+
+
+/* ----------------------------------------------------- */
+/* send output to client */
+/* ----------------------------------------------------- */
+
+
+static int
+agent_send(ap)
+ Agent *ap;
+{
+ int sock, len, cc;
+ char *data;
+
+ sock = ap->sock;
+ len = ap->locus;
+ data = ap->pool;
+ cc = send(sock, data, len, 0);
+
+ if (cc < 0)
+ {
+ cc = errno;
+ if (cc != EWOULDBLOCK)
+ {
+ ap_log("send", ap->sno, strerror(cc));
+ return 0;
+ }
+
+ return -1; /* would block, so leave it to do later */
+ }
+
+ if (cc == 0)
+ return -1;
+
+ len -= cc;
+ ap->xdata += cc;
+ ap->locus = len;
+
+ if (len)
+ {
+ memcpy(data, data + cc, len);
+ return cc;
+ }
+
+ if (ap->state == CS_FLUSH)
+ {
+ ap->state = CS_FREE;
+ return 0;
+ }
+
+ return cc;
+}
+
+
+/* ----------------------------------------------------- */
+/* receive request from client */
+/* ----------------------------------------------------- */
+
+
+static int
+agent_recv(ap)
+ Agent *ap;
+{
+ int cc;
+ char *pool, *data;
+
+ pool = ap->pool;
+ data = pool + ap->locus;
+ cc = recv(ap->sock, data, TCP_RCVSIZ, 0);
+
+ if (cc <= 0)
+ {
+ cc = errno;
+ if (cc != EWOULDBLOCK)
+ {
+ ap_log("RECV", ap->sno, strerror(cc));
+ return 0;
+ }
+
+ return -1; /* would block, so leave it to do later */
+ }
+
+ data[cc] = '\0';
+
+ while (cc = *data)
+ {
+ if (cc == '\r' || cc == '\n')
+ {
+ /* remove tailing '/' from path */
+
+ if ((data > pool + 1) && data[-1] == '/' && data[-2] != '/')
+ data--;
+
+ *data = '\0';
+
+ ap_log("CMD", ap->sno, pool);
+
+ ap->state = cc = agent_serve(ap);
+
+ if (cc == CS_FREE)
+ return 0;
+
+ if (cc == CS_GPLUS)
+ {
+ strcpy(pool, "+-1\r\n");
+ ap->locus = 5;
+ }
+ else
+ {
+ ap->locus = 0;
+ }
+
+ return 1;
+ }
+ data++;
+ }
+
+ if ((ap->locus = data - pool) > TCP_RCVSIZ)
+ {
+ ap_log("HACK", ap->sno, "buffer overflow");
+ return 0;
+ }
+
+ return 1;
+}
+
+
+/* ----------------------------------------------------- */
+/* close a connection & release its resource */
+/* ----------------------------------------------------- */
+
+
+static void
+agent_fire(ap)
+ Agent *ap;
+{
+ int sock;
+ FILE *fp;
+
+ sock = ap->sock;
+ shutdown(sock, 2);
+ close(sock);
+
+ if (fp = ap->stream)
+ fclose(fp);
+
+ fprintf(flog, "%s\t[%d] T%d D%d\n",
+ ap->state == CS_FREE ? "BYE" : "END",
+ ap->sno, time(0) - ap->tbegin, ap->xdata);
+}
+
+
+/* ----------------------------------------------------- */
+/* accept a new connection */
+/* ----------------------------------------------------- */
+
+
+static inline int
+agent_accept()
+{
+ int sock;
+ int value;
+
+ /* gopher do not care remote host / user name */
+
+ for (;;)
+ {
+ sock = accept(0, NULL, NULL);
+ if (sock > 0)
+ break;
+
+ sock = errno;
+ if (sock != EINTR)
+ {
+ logit("ACCEPT", strerror(sock));
+ return -1;
+ }
+
+ while (waitpid(-1, NULL, WNOHANG | WUNTRACED) > 0);
+ }
+
+ value = 1;
+ setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *) &value, sizeof(value));
+ return sock;
+}
+
+
+/* ----------------------------------------------------- */
+/* server core routines */
+/* ----------------------------------------------------- */
+
+
+static void
+servo_daemon(inetd)
+ int inetd;
+{
+ int fd, value;
+ char buf[80];
+ struct sockaddr_in fsin;
+ struct linger ld;
+#ifdef HAVE_RLIMIT
+ struct rlimit limit;
+#endif
+
+ /*
+ * 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((time_t *) &value);
+ gmtime((time_t *) &value);
+ strftime(buf, 80, "%d/%b/%Y:%H:%M:%S", localtime((time_t *) &value));
+
+#ifdef HAVE_RLIMIT
+ /* --------------------------------------------------- */
+ /* adjust the resource limit */
+ /* --------------------------------------------------- */
+
+ limit.rlim_cur = limit.rlim_max = 4 * 1024 * 1024;
+ setrlimit(RLIMIT_FSIZE, &limit);
+ setrlimit(RLIMIT_DATA, &limit);
+
+#ifdef SOLARIS
+#define RLIMIT_RSS RLIMIT_AS /* Thor.981206: port for solaris 2.6 */
+#endif
+
+ setrlimit(RLIMIT_RSS, &limit);
+
+ limit.rlim_cur = limit.rlim_max = 0;
+ setrlimit(RLIMIT_CORE, &limit);
+#endif
+
+ /* --------------------------------------------------- */
+ /* detach process */
+ /* --------------------------------------------------- */
+
+ close(1);
+ close(2);
+
+ if (inetd)
+ return;
+
+ close(0);
+
+ if (fork())
+ exit(0);
+
+ setsid();
+
+ if (fork())
+ exit(0);
+
+ /* --------------------------------------------------- */
+ /* setup socket */
+ /* --------------------------------------------------- */
+
+ fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+
+ value = 1;
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &value, sizeof(value));
+
+#if 0
+ setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *) &value, sizeof(value));
+#endif
+
+ ld.l_onoff = ld.l_linger = 0;
+ setsockopt(fd, SOL_SOCKET, SO_LINGER, (char *) &ld, sizeof(ld));
+
+ memset((char *) &fsin, 0, sizeof(fsin));
+ fsin.sin_family = AF_INET;
+ fsin.sin_port = htons(GEMD_PORT);
+ fsin.sin_addr.s_addr = htonl(INADDR_ANY);
+
+ if (bind(fd, (struct sockaddr *) & fsin, sizeof(fsin)) ||
+ listen(fd, TCP_QLEN))
+ exit(1);
+}
+
+
+#ifdef SERVER_USAGE
+static void
+servo_usage()
+{
+ struct rusage ru;
+
+ if (getrusage(RUSAGE_SELF, &ru))
+ return;
+
+ fprintf(flog, "\n[Server Usage]\n\n"
+ "user time: %.6f\n"
+ "system time: %.6f\n"
+ "maximum resident set size: %lu P\n"
+ "integral resident set size: %lu\n"
+ "page faults not requiring physical I/O: %d\n"
+ "page faults requiring physical I/O: %d\n"
+ "swaps: %d\n"
+ "block input operations: %d\n"
+ "block output operations: %d\n"
+ "messages sent: %d\n"
+ "messages received: %d\n"
+ "signals received: %d\n"
+ "voluntary context switches: %d\n"
+ "involuntary context switches: %d\n"
+ "gline: %d\n\n",
+
+ (double) ru.ru_utime.tv_sec + (double) ru.ru_utime.tv_usec / 1000000.0,
+ (double) ru.ru_stime.tv_sec + (double) ru.ru_stime.tv_usec / 1000000.0,
+ ru.ru_maxrss,
+ ru.ru_idrss,
+ ru.ru_minflt,
+ ru.ru_majflt,
+ ru.ru_nswap,
+ ru.ru_inblock,
+ ru.ru_oublock,
+ ru.ru_msgsnd,
+ ru.ru_msgrcv,
+ ru.ru_nsignals,
+ ru.ru_nvcsw,
+ ru.ru_nivcsw,
+ gline);
+
+ fflush(flog);
+}
+#endif
+
+
+static void
+reaper()
+{
+ while (waitpid(-1, NULL, WNOHANG | WUNTRACED) > 0);
+}
+
+
+static void
+sig_trap(sig)
+ int sig;
+{
+ char buf[80];
+
+ sprintf(buf, "[%d] at %d", sig, gline);
+ logit("EXIT", buf);
+ fclose(flog);
+ exit(0);
+}
+
+
+/* ----------------------------------------------------- */
+/* signal routines */
+/* ----------------------------------------------------- */
+
+
+static void
+servo_signal()
+{
+ struct sigaction act;
+
+ /* sigblock(sigmask(SIGPIPE)); */ /* Thor.981206: ²Î¤@ POSIX ¼Ð·Ç¥Îªk */
+
+ /* act.sa_mask = 0; */ /* Thor.981105: ¼Ð·Ç¥Îªk */
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+
+ act.sa_handler = sig_trap;
+ sigaction(SIGBUS, &act, NULL);
+ sigaction(SIGSEGV, &act, NULL);
+ sigaction(SIGTERM, &act, NULL);
+
+ act.sa_handler = reaper;
+ sigaction(SIGCHLD, &act, NULL);
+
+#ifdef SERVER_USAGE
+ act.sa_handler = servo_usage;
+ sigaction(SIGPROF, &act, NULL);
+#endif
+
+ /* Thor.981206: lkchu patch: ²Î¤@ POSIX ¼Ð·Ç¥Îªk */
+ /* ¦b¦¹­É¥Î sigset_t act.sa_mask */
+ sigaddset(&act.sa_mask, SIGPIPE);
+ sigprocmask(SIG_BLOCK, &act.sa_mask, NULL);
+
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int sock, nfds, state, servo_sno;
+ time_t uptime, tcheck;
+ Agent **FBI, *Scully, *Mulder, *agent;
+ fd_set rset, wset, xset;
+ struct timeval tv;
+
+ state = 0;
+
+ while ((nfds = getopt(argc, argv, "hid")) != -1)
+ {
+ switch (nfds)
+ {
+ case 'i':
+ state = 1;
+ break;
+
+ case 'd':
+ break;
+
+ case 'h':
+ default:
+
+ fprintf(stderr, "Usage: %s [options]\n"
+ "\t-i start from inetd with wait option\n"
+ "\t-d debug mode\n"
+ "\t-h help\n",
+ argv[0]);
+ exit(0);
+ }
+ }
+
+ servo_daemon(state);
+
+ setgid(BBSGID);
+ setuid(BBSUID);
+ chdir(BBSHOME);
+
+ log_open();
+ init_bshm();
+
+ chdir("gem");
+ servo_signal();
+
+ tcheck = time(0) + GEMD_PERIOD;
+ Scully = Mulder = NULL;
+ servo_sno = 0;
+
+ for (;;)
+ {
+ uptime = time(0);
+ if (tcheck < uptime)
+ {
+ tcheck = uptime - GEMD_TIMEOUT;
+
+ for (FBI = &Scully; agent = *FBI;)
+ {
+ if (agent->uptime < tcheck)
+ {
+ agent_fire(agent);
+
+ *FBI = agent->anext;
+
+ agent->anext = Mulder;
+ Mulder = agent;
+ }
+ else
+ {
+ FBI = &(agent->anext);
+ }
+ }
+
+ fflush(flog);
+ tcheck = uptime + GEMD_PERIOD;
+ }
+
+ /* ------------------------------------------------- */
+ /* Set up the fdsets */
+ /* ------------------------------------------------- */
+
+ FD_ZERO(&rset);
+ FD_ZERO(&wset);
+ FD_ZERO(&xset);
+
+ FD_SET(0, &rset);
+ nfds = 0;
+
+ for (agent = Scully; agent; agent = agent->anext)
+ {
+ sock = agent->sock;
+ state = agent->state;
+
+ if (nfds < sock)
+ nfds = sock;
+
+ if (state == CS_READ)
+ {
+ FD_SET(sock, &rset);
+ }
+ else
+ {
+ FD_SET(sock, &wset);
+
+ if (state == CS_FILE)
+ gem_file(agent);
+ else if (state == CS_INDEX || state == CS_GPLUS)
+ gem_index(agent);
+ }
+
+ FD_SET(sock, &xset);
+ }
+
+ /* Thor.981206: for reservation future bug */
+ tv.tv_sec = GEMD_PERIOD;
+ tv.tv_usec = 0;
+
+ nfds = select(nfds + 1, &rset, &wset, &xset, &tv);
+
+ if (nfds == 0)
+ {
+ continue;
+ }
+
+ if (nfds < 0)
+ {
+ sock = errno;
+ if (sock != EINTR)
+ logit("select", strerror(sock));
+ continue;
+ }
+
+ /* ------------------------------------------------- */
+ /* serve active agents */
+ /* ------------------------------------------------- */
+
+ uptime = time(0);
+
+ for (FBI = &Scully; agent = *FBI;)
+ {
+ sock = agent->sock;
+
+ if (FD_ISSET(sock, &wset))
+ {
+ state = agent_send(agent);
+ }
+ else if (FD_ISSET(sock, &rset))
+ {
+ state = agent_recv(agent);
+ }
+ else if (FD_ISSET(sock, &xset))
+ {
+ state = 0;
+ }
+ else
+ {
+ state = -1;
+ }
+
+ if (state == 0) /* fire this agent */
+ {
+ agent_fire(agent);
+
+ *FBI = agent->anext;
+
+ agent->anext = Mulder;
+ Mulder = agent;
+
+ continue;
+ }
+
+ if (state > 0)
+ {
+ agent->uptime = uptime;
+ }
+
+ FBI = &(agent->anext);
+ }
+
+ /* ------------------------------------------------- */
+ /* serve new connection */
+ /* ------------------------------------------------- */
+
+ if (FD_ISSET(0, &rset))
+ {
+ sock = agent_accept();
+ if (sock <= 0)
+ continue;
+
+ if (agent = Mulder)
+ {
+ Mulder = agent->anext;
+ }
+ else
+ {
+ agent = (Agent *) malloc(sizeof(Agent));
+ }
+
+ *FBI = agent;
+
+ /* variable initialization */
+
+ agent->anext = NULL;
+ agent->state = CS_READ;
+ agent->sock = sock;
+ agent->sno = ++servo_sno;
+ agent->tbegin = uptime;
+ agent->uptime = uptime;
+ agent->stream = NULL;
+ agent->locus = 0;
+ agent->xdata = 0;
+
+ fprintf(flog, "CONN\t[%d] %s\n", servo_sno, Btime(&agent->tbegin));
+ }
+
+ /* ------------------------------------------------- */
+ /* tail of main loop */
+ /* ------------------------------------------------- */
+ }
+}
diff --git a/daemon/xchatd.c b/daemon/xchatd.c
new file mode 100644
index 0000000..f9a40ea
--- /dev/null
+++ b/daemon/xchatd.c
@@ -0,0 +1,4027 @@
+/*-------------------------------------------------------*/
+/* xchatd.c ( NTHU CS MapleBBS Ver 3.00 ) */
+/*-------------------------------------------------------*/
+/* target : super KTV daemon for chat server */
+/* create : 95/03/29 */
+/* update : 97/10/20 */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+#include "xchat.h"
+
+
+#include <sys/wait.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <netdb.h>
+#include <sys/resource.h>
+
+
+#define SERVER_USAGE
+#define WATCH_DOG
+#undef DEBUG /* µ{¦¡°£¿ù¤§¥Î */
+#undef MONITOR /* ºÊ·þ chatroom ¬¡°Ê¥H¸Ñ¨MªÈ¯É */
+#undef STAND_ALONE /* ¤£·f°t BBS ¿W¥ß°õ¦æ */
+
+
+#ifdef DEBUG
+#define MONITOR
+#endif
+
+static int gline;
+
+#ifdef WATCH_DOG
+#define MYDOG gline = __LINE__
+#else
+#define MYDOG /* NOOP */
+#endif
+
+
+#define CHAT_PIDFILE "run/chat.pid"
+#define CHAT_LOGFILE "run/chat.log"
+#define CHAT_INTERVAL (60 * 30)
+#define SOCK_QLEN 3
+
+
+/* name of the main room (always exists) */
+
+
+#define MAIN_NAME "main"
+#define MAIN_TOPIC "¦³½t¤d¨½¨Ó¬Û·|"
+
+
+#define ROOM_LOCKED 1
+#define ROOM_SECRET 2
+#define ROOM_OPENTOPIC 4
+#define ROOM_ALL (NULL)
+
+
+#define LOCKED(room) (room->rflag & ROOM_LOCKED)
+#define SECRET(room) (room->rflag & ROOM_SECRET)
+#define OPENTOPIC(room) (room->rflag & ROOM_OPENTOPIC)
+
+
+#define RESTRICTED(usr) (usr->uflag == 0) /* guest */
+#define CHATSYSOP(usr) (usr->uflag & PERM_ALLCHAT)
+#define PERM_ROOMOP PERM_CHAT /* Thor: ­É PERM_CHAT ¬° PERM_ROOMOP */
+#define PERM_CHATOP PERM_DENYCHAT /* Thor: ­É PERM_DENYCHAT ¬° PERM_CHATOP */
+/* #define ROOMOP(usr) (usr->uflag & (PERM_ROOMOP | PERM_ALLCHAT)) */
+/* Thor.980603: PERM_ALLCHAT §ï¬° default ¨S¦³ roomop, ¦ý¥i¥H¦Û¤v¨ú±o chatop */
+#define ROOMOP(usr) (usr->uflag & (PERM_ROOMOP | PERM_CHATOP))
+#define CLOAK(usr) (usr->uflag & PERM_CLOAK)
+
+
+/* ----------------------------------------------------- */
+/* ChatRoom data structure */
+/* ----------------------------------------------------- */
+
+
+typedef struct ChatRoom ChatRoom;
+typedef struct ChatUser ChatUser;
+typedef struct UserList UserList;
+typedef struct ChatCmd ChatCmd;
+typedef struct ChatAction ChatAction;
+
+
+struct ChatUser
+{
+ ChatUser *unext;
+ ChatRoom *room;
+ UserList *ignore;
+ int sock; /* user socket */
+ int userno;
+ int uflag;
+ int clitype; /* Xshadow: client type. 1 for common client,
+ * 0 for bbs only client */
+ time_t tbegin;
+ time_t uptime;
+ int sno;
+ int xdata;
+ int retry;
+
+ int isize; /* current size of ibuf */
+ char ibuf[80]; /* buffer for non-blocking receiving */
+ char userid[IDLEN + 1]; /* real userid */
+ char chatid[9]; /* chat id */
+ char rhost[30]; /* host address */
+};
+
+
+struct ChatRoom
+{
+ ChatRoom *next, *prev;
+ UserList *invite;
+ char name[IDLEN + 1];
+ char topic[48]; /* Let the room op to define room topic */
+ int rflag; /* ROOM_LOCKED, ROOM_SECRET, ROOM_OPENTOPIC */
+ int occupants; /* number of users in room */
+};
+
+
+struct UserList
+{
+ UserList *next;
+ int userno;
+ char userid[0];
+};
+
+
+struct ChatCmd
+{
+ char *cmdstr;
+ void (*cmdfunc) ();
+ int exact;
+};
+
+
+static ChatRoom mainroom, *roompool;
+static ChatUser *mainuser, *userpool;
+static fd_set mainfset;
+static int totaluser; /* current number of connections */
+static struct timeval zerotv; /* timeval for selecting */
+static int common_client_command;
+
+
+#ifdef STAND_ALONE
+static int userno_inc = 0; /* userno auto-incrementer */
+#endif
+
+
+static char msg_not_op[] = "¡» ±z¤£¬O³o¶¡²á¤Ñ«Çªº Op";
+static char msg_no_such_id[] = "¡» ¥Ø«e¨S¦³¤H¨Ï¥Î [%s] ³o­Ó²á¤Ñ¥N¸¹";
+static char msg_not_here[] = "¡» [%s] ¤£¦b³o¶¡²á¤Ñ«Ç";
+
+
+#define FUZZY_USER ((ChatUser *) -1)
+
+
+/* ----------------------------------------------------- */
+/* operation log and debug information */
+/* ----------------------------------------------------- */
+
+
+static FILE *flog;
+
+
+static void
+logit(key, msg)
+ char *key;
+ char *msg;
+{
+ time_t now;
+ struct tm *p;
+
+ time(&now);
+ p = localtime(&now);
+ fprintf(flog, "%02d/%02d %02d:%02d:%02d %-13s%s\n",
+ p->tm_mon + 1, p->tm_mday,
+ p->tm_hour, p->tm_min, p->tm_sec, key, msg);
+}
+
+
+static inline void
+log_init()
+{
+ FILE *fp;
+
+ /* --------------------------------------------------- */
+ /* log daemon's PID */
+ /* --------------------------------------------------- */
+
+ if (fp = fopen(CHAT_PIDFILE, "w"))
+ {
+ fprintf(fp, "%d\n", getpid());
+ fclose(fp);
+ }
+
+ flog = fopen(CHAT_LOGFILE, "a");
+ logit("START", "chat daemon");
+}
+
+
+#ifdef DEBUG
+static char chatbuf[256]; /* general purpose buffer */
+
+
+static void
+debug_list(list)
+ UserList *list;
+{
+ char buf[80];
+ int i = 0;
+
+ if (!list)
+ {
+ logit("DEBUG_L", "NULL");
+ return;
+ }
+ while (list)
+ {
+ sprintf(buf, "%d) list: %p userno: %d next: %p", i++, list, list->userno, list->next);
+ logit("DEBUG_L", buf);
+
+ list = list->next;
+ }
+ logit("DEBUG_L", "end");
+}
+
+
+static void
+debug_user()
+{
+ ChatUser *user;
+ int i;
+ char buf[80];
+
+ sprintf(buf, "mainuser: %p userpool: %p", mainuser, userpool);
+ logit("DEBUG_U", buf);
+ for (i = 0, user = mainuser; user; user = user->unext)
+ {
+ /* MYDOG; */
+ sprintf(buf, "%d) %p %-6d %s %s", ++i, user, user->userno, user->userid, user->chatid);
+ logit("DEBUG_U", buf);
+ }
+}
+
+
+static void
+debug_room()
+{
+ ChatRoom *room;
+ int i;
+ char buf[80];
+
+ i = 0;
+ room = &mainroom;
+
+ sprintf(buf, "mainroom: %p roompool: %p", mainroom, roompool);
+ logit("DEBUG_R", buf);
+ do
+ {
+ MYDOG;
+ sprintf(buf, "%d) %p %s %d", ++i, room, room->name, room->occupants);
+ logit("DEBUG_R", buf);
+ } while (room = room->next);
+}
+
+
+static void
+log_user(cu)
+ ChatUser *cu;
+{
+ static int log_num;
+
+ if (cu)
+ {
+ if (log_num > 100 && log_num < 150)
+ {
+ sprintf(chatbuf, "%d: %p <%d>", log_num, cu, gline);
+ logit("travese user ", chatbuf);
+ }
+ else if (log_num == 100)
+ {
+ sprintf(chatbuf, "BOOM !! at line %d", gline);
+ logit("travese user ", chatbuf);
+ }
+ log_num++;
+ }
+ else
+ log_num = 0;
+}
+#endif /* DEBUG */
+
+
+/* ----------------------------------------------------- */
+/* string routines */
+/* ----------------------------------------------------- */
+
+
+static int
+valid_chatid(id)
+ char *id;
+{
+ int ch, len;
+
+ for (len = 0; ch = *id; id++)
+ { /* Thor.980921: ªÅ¥Õ¬°¤£¦X²zchatid, ©Ègetnext§PÂ_¿ù»~µ¥µ¥ */
+ if (ch == '/' || ch == '*' || ch == ':' || ch ==' ')
+ return 0;
+ if (++len > 8)
+ return 0;
+ }
+ return len;
+}
+
+
+/* itoc.µù¸Ñ: ¥Ñ©ó§ï±Ä MUD-like ªº³¡¤À match §Y¥i */
+/* ©Ò¥H MUD-like ªº action ºÉ¶q¤£­n¥Î­^¤åÁY¼g¡A¨Ã¤£­n¦³­«ÂЪº */
+
+static int /* 0: fit */
+str_belong(s1, s2) /* itoc.010321: Åý mud-like «ü¥O³¡¤À match §Y¥i¡A©M mud ¤@¼Ë */
+ uschar *s1; /* ChatAction ¸Ìªº¤p¼g verb */
+ uschar *s2; /* user input command ¤j¤p¼g§¡¥i */
+{
+ int c1, c2;
+ int num = 0;
+
+ for (;;)
+ {
+ c1 = *s1;
+ c2 = *s2;
+
+ if (c2 >= 'A' && c2 <= 'Z')
+ c2 |= 0x20; /* ´«¤p¼g */
+
+ if (num >= 2) /* ¦Ü¤Ö­n¦³¤G¦r¤¸¬Û¦P */
+ {
+ if (!c1 || !c2) /* §¹¥þ match ©Î³¡¤À match ¬Ò¥i (s1¥]§ts2 ©Î s2¥]§ts1§¡ºâ) */
+ return 0;
+ }
+
+ if (c1 > c2) /* itoc.010927: ¤£¦Pªº¦^¶Ç­È */
+ return 1;
+ else if (c1 < c2)
+ return -1;
+
+ s1++;
+ s2++;
+ num++;
+ }
+}
+
+
+/* ----------------------------------------------------- */
+/* match strings' similarity case-insensitively */
+/* ----------------------------------------------------- */
+/* str_match(keyword, string) */
+/* ----------------------------------------------------- */
+/* 0 : equal ("foo", "foo") */
+/* -1 : mismatch ("abc", "xyz") */
+/* ow : similar ("goo", "good") */
+/* ----------------------------------------------------- */
+
+
+static int
+str_match(s1, s2)
+ uschar *s1; /* lower-case (sub)string */
+ uschar *s2;
+{
+ int c1, c2;
+
+ for (;;)
+ {
+ c1 = *s1;
+ c2 = *s2;
+
+ if (!c1)
+ return c2;
+
+ if (c2 >= 'A' && c2 <= 'Z')
+ c2 |= 0x20; /* ´«¤p¼g */
+
+ if (c1 != c2)
+ return -1;
+
+ s1++;
+ s2++;
+ }
+}
+
+
+/* ----------------------------------------------------- */
+/* search user/room by its ID */
+/* ----------------------------------------------------- */
+
+
+static ChatUser *
+cuser_by_userid(userid)
+ char *userid;
+{
+ ChatUser *cu;
+ char buf[80]; /* Thor.980727: ¤@¦¸³Ìªø¤~80 */
+
+ str_lower(buf, userid);
+ for (cu = mainuser; cu; cu = cu->unext)
+ {
+ if (!cu->userno)
+ continue;
+ if (!str_cmp(buf, cu->userid))
+ break;
+ }
+ return cu;
+}
+
+
+static ChatUser *
+cuser_by_chatid(chatid)
+ char *chatid;
+{
+ ChatUser *cu;
+ char buf[80]; /* Thor.980727: ¤@¦¸³Ìªø¤~80 */
+
+ str_lower(buf, chatid);
+
+ for (cu = mainuser; cu; cu = cu->unext)
+ {
+ if (!cu->userno)
+ continue;
+ if (!str_cmp(buf, cu->chatid))
+ break;
+ }
+ return cu;
+}
+
+
+static ChatUser *
+fuzzy_cuser_by_chatid(chatid)
+ char *chatid;
+{
+ ChatUser *cu, *xuser;
+ int mode;
+ char buf[80]; /* Thor.980727: ¤@¦¸³Ìªø¤~80 */
+
+ str_lower(buf, chatid);
+ xuser = NULL;
+
+ for (cu = mainuser; cu; cu = cu->unext)
+ {
+ if (!cu->userno)
+ continue;
+
+ mode = str_match(buf, cu->chatid);
+ if (mode == 0)
+ return cu;
+
+ if (mode > 0)
+ {
+ if (xuser)
+ return FUZZY_USER; /* ²Å¦XªÌ¤j©ó 2 ¤H */
+
+ xuser = cu;
+ }
+ }
+ return xuser;
+}
+
+
+static ChatRoom *
+croom_by_roomid(roomid)
+ char *roomid;
+{
+ ChatRoom *room;
+ char buf[80]; /* Thor.980727: ¤@¦¸³Ìªø¤~80 */
+
+ str_lower(buf, roomid);
+ room = &mainroom;
+ do
+ {
+ if (!str_cmp(buf, room->name))
+ break;
+ } while (room = room->next);
+ return room;
+}
+
+
+/* ----------------------------------------------------- */
+/* UserList routines */
+/* ----------------------------------------------------- */
+
+
+static void
+list_free(list)
+ UserList **list;
+{
+ UserList *user, *next;
+
+ for (user = *list, *list = NULL; user; user = next)
+ {
+ next = user->next;
+ free(user);
+ }
+}
+
+
+static void
+list_add(list, user)
+ UserList **list;
+ ChatUser *user;
+{
+ UserList *node;
+ char *userid;
+ int len;
+
+ len = strlen(userid = user->userid) + 1;
+ if (node = (UserList *) malloc(sizeof(UserList) + len))
+ {
+ node->next = *list;
+ node->userno = user->userno;
+ memcpy(node->userid, userid, len);
+ *list = node;
+ }
+}
+
+
+static int
+list_delete(list, userid)
+ UserList **list;
+ char *userid;
+{
+ UserList *node;
+ char buf[80]; /* Thor.980727: ¿é¤J¤@¦¸³Ìªø¤~ 80 */
+
+ str_lower(buf, userid);
+
+ while (node = *list)
+ {
+ if (!str_cmp(buf, node->userid))
+ {
+ *list = node->next;
+ free(node);
+ return 1;
+ }
+ list = &node->next;
+ }
+
+ return 0;
+}
+
+
+static int
+list_belong(list, userno)
+ UserList *list;
+ int userno;
+{
+ while (list)
+ {
+ if (userno == list->userno)
+ return 1;
+ list = list->next;
+ }
+ return 0;
+}
+
+
+/* ------------------------------------------------------ */
+/* non-blocking socket routines : send message to users */
+/* ------------------------------------------------------ */
+
+
+static void
+do_send(nfds, wset, msg)
+ int nfds;
+ fd_set *wset;
+ char *msg;
+{
+ int len, sr;
+
+#if 1
+ /* Thor: for future reservation bug */
+ zerotv.tv_sec = 0;
+ zerotv.tv_usec = 0;
+#endif
+
+ sr = select(nfds + 1, NULL, wset, NULL, &zerotv);
+
+ if (sr > 0)
+ {
+ len = strlen(msg) + 1;
+ do
+ {
+ if (FD_ISSET(nfds, wset))
+ {
+ send(nfds, msg, len, 0);
+ if (--sr <= 0)
+ return;
+ }
+ } while (--nfds > 0);
+ }
+}
+
+
+static void
+send_to_room(room, msg, userno, number)
+ ChatRoom *room;
+ char *msg;
+ int userno;
+ int number;
+{
+ ChatUser *cu;
+ fd_set wset;
+ int sock, max;
+ int clitype; /* ¤À¬° bbs client ¤Î common client ¨â¦¸³B²z */
+ char *str, buf[256];
+
+ for (clitype = (number == MSG_MESSAGE || !number) ? 0 : 1;
+ clitype < 2; clitype++)
+ {
+ FD_ZERO(&wset);
+ max = -1;
+
+ for (cu = mainuser; cu; cu = cu->unext)
+ {
+ if (cu->userno && (cu->clitype == clitype) &&
+ (room == ROOM_ALL || room == cu->room) &&
+ (!userno || !list_belong(cu->ignore, userno)))
+ {
+ sock = cu->sock;
+
+ FD_SET(sock, &wset);
+
+ if (max < sock)
+ max = sock;
+ }
+ }
+
+ if (max <= 0)
+ continue;
+
+ if (clitype)
+ {
+ str = buf;
+
+ if (*msg)
+ sprintf(str, "%3d %s", number, msg);
+ else
+ sprintf(str, "%3d", number);
+ }
+ else
+ {
+ str = msg;
+ }
+
+ do_send(max, &wset, str);
+ }
+}
+
+
+static void
+send_to_user(user, msg, userno, number)
+ ChatUser *user;
+ char *msg;
+ int userno;
+ int number;
+{
+ int sock;
+
+#if 0
+ if (!user->userno || (!user->clitype && number && number != MSG_MESSAGE))
+#endif
+ /* Thor.980911: ¦pªG¬duser->userno«h¦blogin_userªºerror message·|µLªk°e¦^ */
+ if (!user->clitype && number != MSG_MESSAGE)
+ return;
+
+ if ((sock = user->sock) <= 0)
+ return;
+
+ if (!userno || !list_belong(user->ignore, userno))
+ {
+ fd_set wset;
+ char buf[256];
+
+ FD_ZERO(&wset);
+ FD_SET(sock, &wset);
+
+ if (user->clitype)
+ {
+ if (*msg)
+ sprintf(buf, "%3d %s", number, msg);
+ else
+ sprintf(buf, "%3d", number);
+ msg = buf;
+ }
+
+ do_send(sock, &wset, msg);
+ }
+}
+
+
+/* ----------------------------------------------------- */
+
+
+static void
+room_changed(room)
+ ChatRoom *room;
+{
+ if (room)
+ {
+ char buf[256];
+
+ sprintf(buf, "= %s %d %d %s",
+ room->name, room->occupants, room->rflag, room->topic);
+ send_to_room(ROOM_ALL, buf, 0, MSG_ROOMNOTIFY);
+ }
+}
+
+
+static void
+user_changed(cu)
+ ChatUser *cu;
+{
+ if (cu)
+ {
+ ChatRoom *room;
+ char buf[256];
+
+ room = cu->room;
+ sprintf(buf, "= %s %s %s %s%s",
+ cu->userid, cu->chatid, room->name, cu->rhost,
+ ROOMOP(cu) ? " Op" : "");
+ send_to_room(room, buf, 0, MSG_USERNOTIFY);
+ }
+}
+
+
+static void
+exit_room(user, mode, msg)
+ ChatUser *user;
+ int mode;
+ char *msg;
+{
+ ChatRoom *room;
+ char buf[128];
+
+ if (!(room = user->room))
+ return;
+
+ user->room = NULL;
+ /* user->uflag &= ~(PERM_ROOMOP | PERM_ALLCHAT); */
+ user->uflag &= ~PERM_ROOMOP;
+ /* Thor.980601: Â÷¶}©Ð¶¡®É¥u²M room op, ¤£²M sysop, chatroom ¦]¤Ñ¥Í¨ã¦³ */
+
+ if (--room->occupants > 0)
+ {
+ char *chatid;
+
+ chatid = user->chatid;
+ switch (mode)
+ {
+ case EXIT_LOGOUT:
+
+ sprintf(buf, "¡» %s Â÷¶}¤F ... %.50s", chatid, (msg && *msg) ? msg : "");
+ break;
+
+ case EXIT_LOSTCONN:
+
+ sprintf(buf, "¡» %s ¦¨¤FÂ_½uªº­·ºåÅo", chatid);
+ break;
+
+ case EXIT_KICK:
+
+ sprintf(buf, "¡» «¢«¢¡I%s ³Q½ð¥X¥h¤F", chatid);
+ break;
+ }
+
+ if (!CLOAK(user))
+ send_to_room(room, buf, 0, MSG_MESSAGE);
+
+ sprintf(buf, "- %s", user->userid);
+ send_to_room(room, buf, 0, MSG_USERNOTIFY);
+ room_changed(room);
+ }
+ else if (room != &mainroom)
+ {
+ ChatRoom *next;
+
+ fprintf(flog, "room-\t[%d] %s\n", user->sno, room->name);
+ sprintf(buf, "- %s", room->name);
+
+ room->prev->next = next = room->next;
+ if (next)
+ next->prev = room->prev;
+
+ list_free(&room->invite);
+
+ /* free(room); */
+
+ /* ¦^¦¬ */
+ room->next = roompool;
+ roompool = room;
+
+ send_to_room(ROOM_ALL, buf, 0, MSG_ROOMNOTIFY);
+ }
+}
+
+
+/* ----------------------------------------------------- */
+/* chat commands */
+/* ----------------------------------------------------- */
+
+
+#ifndef STAND_ALONE
+/* ----------------------------------------------------- */
+/* BBS server side routines */
+/* ----------------------------------------------------- */
+
+
+/* static */
+int
+acct_load(acct, userid)
+ ACCT *acct;
+ char *userid;
+{
+ int fd;
+
+ usr_fpath((char *) acct, userid, FN_ACCT);
+ fd = open((char *) acct, O_RDONLY);
+ if (fd >= 0)
+ {
+ read(fd, acct, sizeof(ACCT));
+ close(fd);
+ }
+ return fd;
+}
+
+
+static void
+chat_query(cu, msg)
+ ChatUser *cu;
+ char *msg;
+{
+ FILE *fp;
+ ACCT acct;
+ char buf[256];
+
+ /* Thor.980617: ¥i¥ý¬d¬O§_¬°ªÅ¦r¦ê */
+ if (*msg && acct_load(&acct, msg) >= 0)
+ {
+ sprintf(buf, "%s(%s) ¦@¤W¯¸ %d ¦¸¡A¤å³¹ %d ½g",
+ acct.userid, acct.username, acct.numlogins, acct.numposts);
+ send_to_user(cu, buf, 0, MSG_MESSAGE);
+
+ sprintf(buf, "³Ìªñ(%s)±q(%s)¤W¯¸", Btime(&acct.lastlogin),
+ (acct.lasthost[0] ? acct.lasthost : "¥~¤ÓªÅ"));
+ send_to_user(cu, buf, 0, MSG_MESSAGE);
+
+ usr_fpath(buf, acct.userid, FN_PLANS);
+ if (fp = fopen(buf, "r"))
+ {
+ int i;
+
+ i = 0;
+ while (fgets(buf, sizeof(buf), fp))
+ {
+ buf[strlen(buf) - 1] = 0;
+ send_to_user(cu, buf, 0, MSG_MESSAGE);
+ if (++i >= MAXQUERYLINES)
+ break;
+ }
+ fclose(fp);
+ }
+ }
+ else
+ {
+ sprintf(buf, msg_no_such_id, msg);
+ send_to_user(cu, buf, 0, MSG_MESSAGE);
+ }
+}
+#endif
+
+
+static void
+chat_clear(cu, msg)
+ ChatUser *cu;
+ char *msg;
+{
+ if (cu->clitype)
+ send_to_user(cu, "", 0, MSG_CLRSCR);
+ else
+ send_to_user(cu, "/c", 0, MSG_MESSAGE);
+}
+
+
+static void
+chat_date(cu, msg)
+ ChatUser *cu;
+ char *msg;
+{
+ char buf[128];
+
+ sprintf(buf, "¡» ¼Ð·Ç®É¶¡: %s", Now());
+ send_to_user(cu, buf, 0, MSG_MESSAGE);
+}
+
+
+static void
+chat_topic(cu, msg)
+ ChatUser *cu;
+ char *msg;
+{
+ ChatRoom *room;
+ char *topic, buf[128];
+
+ room = cu->room;
+
+ if (!ROOMOP(cu) && !OPENTOPIC(room))
+ {
+ send_to_user(cu, msg_not_op, 0, MSG_MESSAGE);
+ return;
+ }
+
+ if (*msg == '\0')
+ {
+ send_to_user(cu, "¡° ½Ð«ü©w¸ÜÃD", 0, MSG_MESSAGE);
+ return;
+ }
+
+ topic = room->topic;
+ str_ncpy(topic, msg, sizeof(room->topic));
+
+ if (cu->clitype)
+ {
+ send_to_room(room, topic, 0, MSG_TOPIC);
+ }
+ else
+ {
+ sprintf(buf, "/t%s", topic);
+ send_to_room(room, buf, 0, MSG_MESSAGE);
+ }
+
+ room_changed(room);
+
+ if (!CLOAK(cu))
+ {
+ sprintf(buf, "¡» %s ±N¸ÜÃD§ï¬° \033[1;32m%s\033[m", cu->chatid, topic);
+ send_to_room(room, buf, 0, MSG_MESSAGE);
+ }
+}
+
+
+static void
+chat_version(cu, msg)
+ ChatUser *cu;
+ char *msg;
+{
+ char buf[80];
+
+ sprintf(buf, "[Version] MapleBBS-3.10-20040726-PACK.itoc + Xchat-%d.%d",
+ XCHAT_VERSION_MAJOR, XCHAT_VERSION_MINOR);
+ send_to_user(cu, buf, 0, MSG_MESSAGE);
+}
+
+
+static void
+chat_nick(cu, msg)
+ ChatUser *cu;
+ char *msg;
+{
+ char *chatid, *str, buf[128];
+ ChatUser *xuser;
+
+ chatid = nextword(&msg);
+ chatid[8] = '\0';
+ if (!valid_chatid(chatid))
+ {
+ send_to_user(cu, "¡° ³o­Ó²á¤Ñ¥N¸¹¬O¤£¥¿½Tªº", 0, MSG_MESSAGE);
+ return;
+ }
+
+ xuser = cuser_by_chatid(chatid);
+ if (xuser != NULL && xuser != cu)
+ {
+ send_to_user(cu, "¡° ¤w¸g¦³¤H±¶¨¬¥ýµnÅo", 0, MSG_MESSAGE);
+ return;
+ }
+
+ /* itoc.010528: ¤£¥i¥H¥Î§O¤Hªº id °µ¬°²á¤Ñ¥N¸¹ */
+ usr_fpath(buf, chatid, NULL);
+ if (dashd(buf) && str_cmp(chatid, cu->userid))
+ {
+ send_to_user(cu, "¡° ©êºp³o­Ó¥N¸¹¦³¤Hµù¥U¬° id¡A©Ò¥H±z¤£¯à·í¦¨²á¤Ñ¥N¸¹", 0, MSG_MESSAGE);
+ return;
+ }
+
+ str = cu->chatid;
+
+ if (!CLOAK(cu))
+ {
+ sprintf(buf, "¡° %s ±N²á¤Ñ¥N¸¹§ï¬° \033[1;33m%s\033[m", str, chatid);
+ send_to_room(cu->room, buf, cu->userno, MSG_MESSAGE);
+ }
+
+ strcpy(str, chatid);
+
+ user_changed(cu);
+
+ if (cu->clitype)
+ {
+ send_to_user(cu, chatid, 0, MSG_NICK);
+ }
+ else
+ {
+ sprintf(buf, "/n%s", chatid);
+ send_to_user(cu, buf, 0, MSG_MESSAGE);
+ }
+}
+
+
+static void
+chat_list_rooms(cuser, msg)
+ ChatUser *cuser;
+ char *msg;
+{
+ ChatRoom *cr, *room;
+ char buf[128];
+ int mode;
+
+ if (RESTRICTED(cuser))
+ {
+ send_to_user(cuser, "¡° ±z¨S¦³Åv­­¦C¥X²{¦³ªº²á¤Ñ«Ç", 0, MSG_MESSAGE);
+ return;
+ }
+
+ mode = common_client_command;
+
+ if (mode)
+ send_to_user(cuser, "", 0, MSG_ROOMLISTSTART);
+ else
+ send_to_user(cuser, "\033[7m ½Í¤Ñ«Ç¦WºÙ ¢x¤H¼Æ¢x¸ÜÃD [/h]»¡©ú [/quit]Â÷¶}\033[m", 0, MSG_MESSAGE);
+
+ room = cuser->room;
+ cr = &mainroom;
+
+ do
+ {
+ if ((cr == room) || !SECRET(cr) || CHATSYSOP(cuser))
+ {
+ if (mode)
+ {
+ sprintf(buf, "%s %d %d %s",
+ cr->name, cr->occupants, cr->rflag, cr->topic);
+ send_to_user(cuser, buf, 0, MSG_ROOMLIST);
+ }
+ else
+ {
+ sprintf(buf, " %-12s¢x%4d¢x%s", cr->name, cr->occupants, cr->topic);
+ if (LOCKED(cr))
+ strcat(buf, " [Âê¦í]");
+ if (SECRET(cr))
+ strcat(buf, " [¯µ±K]");
+ if (OPENTOPIC(cr))
+ strcat(buf, " [¸ÜÃD]");
+ send_to_user(cuser, buf, 0, MSG_MESSAGE);
+ }
+ }
+ } while (cr = cr->next);
+
+ if (mode)
+ send_to_user(cuser, "", 0, MSG_ROOMLISTEND);
+}
+
+
+static void
+chat_do_user_list(cu, msg, theroom)
+ ChatUser *cu;
+ char *msg;
+ ChatRoom *theroom;
+{
+ ChatRoom *myroom, *room;
+ ChatUser *user;
+ int start, stop, curr, mode; /* , uflag; */
+ char buf[128];
+
+ curr = 0; /* Thor.980619: initialize curr */
+ start = atoi(nextword(&msg));
+ stop = atoi(nextword(&msg));
+
+ mode = common_client_command;
+
+ if (mode)
+ send_to_user(cu, "", 0, MSG_USERLISTSTART);
+ else
+ send_to_user(cu, "\033[7m ²á¤Ñ¥N¸¹¢x¨Ï¥ÎªÌ¥N¸¹ ¢x²á¤Ñ«Ç \033[m", 0,
+ MSG_MESSAGE);
+
+ myroom = cu->room;
+
+ /* Thor.980717: »Ý­n¥ý±Æ°£ cu->userno == 0 ªºª¬ªp¶Ü? */
+ for (user = mainuser; user; user = user->unext)
+ {
+#if 0 /* Thor.980717: ¬JµM cu ³£ªÅ¤F¨ºÁÙ¶i¨Ó·F»ò? */
+ if (!cu->userno)
+ continue;
+#endif
+
+ if (!user->userno)
+ continue;
+
+ room = user->room;
+ if ((theroom != ROOM_ALL) && (theroom != room))
+ continue;
+
+ /* Thor.980717: viewer check */
+ if ((myroom != room) && (RESTRICTED(cu) || (room && SECRET(room) && !CHATSYSOP(cu))))
+ continue;
+
+ /* Thor.980717: viewee check */
+ if (CLOAK(user) && (user != cu) && !CHATSYSOP(cu))
+ continue;
+
+ curr++;
+ if (start && curr < start)
+ continue;
+ else if (stop && (curr > stop))
+ break;
+
+ if (mode)
+ {
+ if (!room)
+ continue; /* Xshadow: ÁÙ¨S¶i¤J¥ô¦ó©Ð¶¡ªº´N¤£¦C¥X */
+
+ sprintf(buf, "%s %s %s %s",
+ user->chatid, user->userid, room->name, user->rhost);
+
+ /* Thor.980603: PERM_ALLCHAT §ï¬° default ¨S¦³ roomop, ¦ý¥i¥H¦Û¤v¨ú±o */
+ /* if (uflag & (PERM_ROOMOP | PERM_ALLCHAT)) */
+ if (ROOMOP(user))
+ strcat(buf, " Op");
+ }
+ else
+ {
+ sprintf(buf, " %-8s¢x%-12s¢x%s",
+ user->chatid, user->userid, room ? room->name : "[¦bªù¤f±r«Þ]");
+ /* Thor.980603: PERM_ALLCHAT §ï¬° default ¨S¦³ roomop, ¦ý¥i¥H¦Û¤v¨ú±o */
+ /* if (uflag & (PERM_ROOMOP | PERM_ALLCHAT)) */
+ /* if (uflag & (PERM_ROOMOP | PERM_CHATOP)) */
+ if (ROOMOP(user)) /* Thor.980602: ²Î¤@¥Îªk */
+ strcat(buf, " [Op]");
+ }
+
+ send_to_user(cu, buf, 0, mode ? MSG_USERLIST : MSG_MESSAGE);
+ }
+
+ if (mode)
+ send_to_user(cu, "", 0, MSG_USERLISTEND);
+}
+
+
+static void
+chat_list_by_room(cu, msg)
+ ChatUser *cu;
+ char *msg;
+{
+ ChatRoom *whichroom;
+ char *roomstr, buf[128];
+
+ roomstr = nextword(&msg);
+ if (!*roomstr)
+ {
+ whichroom = cu->room;
+ }
+ else
+ {
+ if (!(whichroom = croom_by_roomid(roomstr)))
+ {
+ sprintf(buf, "¡° ¨S¦³ [%s] ³o­Ó²á¤Ñ«Ç", roomstr);
+ send_to_user(cu, buf, 0, MSG_MESSAGE);
+ return;
+ }
+
+ if (whichroom != cu->room && SECRET(whichroom) && !CHATSYSOP(cu))
+ {
+ send_to_user(cu, "¡° µLªk¦C¥X¦b¯µ±K²á¤Ñ«Çªº¨Ï¥ÎªÌ", 0, MSG_MESSAGE);
+ return;
+ }
+ }
+ chat_do_user_list(cu, msg, whichroom);
+}
+
+
+static void
+chat_list_users(cu, msg)
+ ChatUser *cu;
+ char *msg;
+{
+ chat_do_user_list(cu, msg, ROOM_ALL);
+}
+
+
+static void
+chat_chatroom(cu, msg)
+ ChatUser *cu;
+ char *msg;
+{
+ if (common_client_command)
+ send_to_user(cu, "²á¤Ñ«Ç", 0, MSG_CHATROOM);
+}
+
+
+static void
+chat_map_chatids(cu, whichroom)
+ ChatUser *cu; /* Thor: ÁÙ¨S¦³§@¤£¦P¶¡ªº */
+ ChatRoom *whichroom;
+{
+ int c;
+ ChatRoom *myroom, *room;
+ ChatUser *user;
+ char buf[128];
+
+ myroom = cu->room;
+
+ send_to_user(cu,
+ "\033[7m ²á¤Ñ¥N¸¹ ¨Ï¥ÎªÌ¥N¸¹ ¢x ²á¤Ñ¥N¸¹ ¨Ï¥ÎªÌ¥N¸¹ ¢x ²á¤Ñ¥N¸¹ ¨Ï¥ÎªÌ¥N¸¹ \033[m", 0, MSG_MESSAGE);
+
+ for (c = 0, user = mainuser; user; user = user->unext)
+ {
+ if (!cu->userno)
+ continue;
+
+ room = user->room;
+ if (whichroom != ROOM_ALL && whichroom != room)
+ continue;
+
+ if (myroom != room)
+ {
+ if (RESTRICTED(cu) || /* Thor: ­n¥ýcheck room ¬O¤£¬OªÅªº */
+ (room && SECRET(room) && !CHATSYSOP(cu)))
+ continue;
+ }
+
+ if (CLOAK(user) && (user != cu) && !CHATSYSOP(cu)) /* Thor:Áô¨­³N */
+ continue;
+
+ sprintf(buf + (c * 24), " %-8s%c%-12s%s",
+ user->chatid, ROOMOP(user) ? '*' : ' ',
+ user->userid, (c < 2 ? "¢x" : " "));
+
+ if (++c == 3)
+ {
+ send_to_user(cu, buf, 0, MSG_MESSAGE);
+ c = 0;
+ }
+ }
+
+ if (c > 0)
+ send_to_user(cu, buf, 0, MSG_MESSAGE);
+}
+
+
+static void
+chat_map_chatids_thisroom(cu, msg)
+ ChatUser *cu;
+ char *msg;
+{
+ chat_map_chatids(cu, cu->room);
+}
+
+
+static void
+chat_setroom(cu, msg)
+ ChatUser *cu;
+ char *msg;
+{
+ char *modestr;
+ ChatRoom *room;
+ char *chatid;
+ int sign, flag;
+ char *fstr, buf[128];
+
+ if (!ROOMOP(cu))
+ {
+ send_to_user(cu, msg_not_op, 0, MSG_MESSAGE);
+ return;
+ }
+
+ modestr = nextword(&msg);
+ sign = 1;
+ if (*modestr == '+')
+ {
+ modestr++;
+ }
+ else if (*modestr == '-')
+ {
+ modestr++;
+ sign = 0;
+ }
+
+ if (*modestr == '\0')
+ {
+ send_to_user(cu,
+ "¡° ½Ð«ü©wª¬ºA: {[+(³]©w)][-(¨ú®ø)]}{[L(Âê¦í)][s(¯µ±K)][t(¶}©ñ¸ÜÃD)}", 0, MSG_MESSAGE);
+ return;
+ }
+
+ room = cu->room;
+ chatid = cu->chatid;
+
+ while (*modestr)
+ {
+ flag = 0;
+ switch (*modestr)
+ {
+ case 'l':
+ case 'L':
+ flag = ROOM_LOCKED;
+ fstr = "Âê¦í";
+ break;
+
+ case 's':
+ case 'S':
+ flag = ROOM_SECRET;
+ fstr = "¯µ±K";
+ break;
+
+ case 't':
+ case 'T':
+ flag = ROOM_OPENTOPIC;
+ fstr = "¶}©ñ¸ÜÃD";
+ break;
+
+ default:
+ sprintf(buf, "¡° ª¬ºA¿ù»~¡G[%c]", *modestr);
+ send_to_user(cu, buf, 0, MSG_MESSAGE);
+ }
+
+ /* Thor: check room ¬O¤£¬OªÅªº, À³¸Ó¤£¬OªÅªº */
+
+ if (flag && (room->rflag & flag) != sign * flag)
+ {
+ room->rflag ^= flag;
+
+ if (!CLOAK(cu))
+ {
+ sprintf(buf, "¡° ¥»²á¤Ñ«Ç³Q %s %s [%s] ª¬ºA",
+ chatid, sign ? "³]©w¬°" : "¨ú®ø", fstr);
+ send_to_room(room, buf, 0, MSG_MESSAGE);
+ }
+ }
+ modestr++;
+ }
+
+ /* Thor.980602: ¤£­ã Main room Âê°_ or ¯µ±K¡A§_«hÂ÷¶}ªº´N¶i¤£¨Ó¡A­n¬Ý¤]¬Ý¤£¨ì¡C
+ ·Q­n½ð¤H¤]½ð¤£¶i main room¡A¤£·|«Ü©_©Ç¶Ü¡H */
+
+ if (!str_cmp(MAIN_NAME, room->name))
+ {
+ if (room->rflag & (ROOM_LOCKED | ROOM_SECRET))
+ {
+ send_to_room(room, "¡° ¦ý¤Ñ¨Ï¬I¤F¡y´_­ì¡zªºÅ]ªk", 0, MSG_MESSAGE);
+ room->rflag &= ~(ROOM_LOCKED | ROOM_SECRET);
+ }
+ }
+
+ room_changed(room);
+}
+
+
+static char *chat_msg[] =
+{
+ "[//]help", "MUD-like ªÀ¥æ°Êµü",
+ "[/h]elp op", "½Í¤Ñ«ÇºÞ²z­û±M¥Î«ü¥O",
+ "[/a]ct <msg>", "°µ¤@­Ó°Ê§@",
+ "[/b]ye [msg]", "¹D§O",
+ "[/c]lear [/d]ate", "²M°£¿Ã¹õ ¥Ø«e®É¶¡",
+ "[/i]gnore [user]", "©¿²¤¨Ï¥ÎªÌ",
+ "[/j]oin <room>", "«Ø¥ß©Î¥[¤J½Í¤Ñ«Ç",
+ "[/l]ist [start [stop]]", "¦C¥X½Í¤Ñ«Ç¨Ï¥ÎªÌ",
+ "[/m]sg <id|user> <msg>", "¸ò <id> »¡®¨®¨¸Ü",
+ "[/n]ick <id>", "±N½Í¤Ñ¥N¸¹´«¦¨ <id>",
+ "[/p]ager", "¤Á´«©I¥s¾¹",
+ "[/q]uery <user>", "¬d¸ßºô¤Í",
+ "[/qui]t [msg]", "¹D§O",
+ "[/r]oom", "¦C¥X¤@¯ë½Í¤Ñ«Ç",
+ "[/t]ape", "¶}Ãö¿ý­µ¾÷",
+ "[/u]nignore <user>", "¨ú®ø©¿²¤",
+ "[/w]ho", "¦C¥X¥»½Í¤Ñ«Ç¨Ï¥ÎªÌ",
+ "[/w]hoin <room>", "¦C¥X½Í¤Ñ«Ç<room> ªº¨Ï¥ÎªÌ",
+ NULL
+};
+
+
+static char *room_msg[] =
+{
+ "[/f]lag [+-][lst]", "³]©wÂê©w¡B¯µ±K¡B¶}©ñ¸ÜÃD",
+ "[/i]nvite <id>", "ÁܽР<id> ¥[¤J½Í¤Ñ«Ç",
+ "[/kick] <id>", "±N <id> ½ð¥X½Í¤Ñ«Ç",
+ "[/o]p [<id>]", "±N Op ªºÅv¤OÂಾµ¹ <id>",
+ "[/topic] <text>", "´«­Ó¸ÜÃD",
+ "[/w]all", "¼s¼½ (¯¸ªø±M¥Î)",
+ NULL
+};
+
+
+static void
+chat_help(cu, msg)
+ ChatUser *cu;
+ char *msg;
+{
+ char **table, *str, buf[128];
+
+ if (!str_cmp("op", nextword(&msg)))
+ {
+ send_to_user(cu, "½Í¤Ñ«ÇºÞ²z­û±M¥Î«ü¥O", 0, MSG_MESSAGE);
+ table = room_msg;
+ }
+ else
+ {
+ table = chat_msg;
+ }
+
+ while (str = *table++)
+ {
+ sprintf(buf, " %-20s- %s", str, *table++);
+ send_to_user(cu, buf, 0, MSG_MESSAGE);
+ }
+}
+
+
+static void
+chat_private(cu, msg)
+ ChatUser *cu;
+ char *msg;
+{
+ ChatUser *xuser;
+ char *recipient, buf[128];
+
+ recipient = nextword(&msg);
+ xuser = (ChatUser *) fuzzy_cuser_by_chatid(recipient);
+ if (xuser == NULL) /* Thor.980724: ¥Î userid¤]¥i¶Ç®¨®¨¸Ü */
+ {
+ xuser = cuser_by_userid(recipient);
+ }
+
+ if (xuser == NULL)
+ {
+ sprintf(buf, msg_no_such_id, recipient);
+ }
+ else if (xuser == FUZZY_USER)
+ { /* ambiguous */
+ strcpy(buf, "¡° ½Ð«ü©ú²á¤Ñ¥N¸¹");
+ }
+ else if (*msg)
+ {
+ int userno;
+
+ userno = cu->userno;
+
+ sprintf(buf, "\033[1m*%s*\033[m %.50s", cu->chatid, msg);
+ send_to_user(xuser, buf, userno, MSG_MESSAGE);
+
+ if (xuser->clitype) /* Xshadow: ¦pªG¹ï¤è¬O¥Î client ¤W¨Óªº */
+ {
+ sprintf(buf, "%s %s %.50s", cu->userid, cu->chatid, msg);
+ send_to_user(xuser, buf, userno, MSG_PRIVMSG);
+ }
+
+ if (cu->clitype)
+ {
+ sprintf(buf, "%s %s %.50s", xuser->userid, xuser->chatid, msg);
+ send_to_user(cu, buf, 0, MSG_MYPRIVMSG);
+ }
+
+ sprintf(buf, "%s> %.50s", xuser->chatid, msg);
+ }
+ else
+ {
+ sprintf(buf, "¡° ±z·Q¹ï %s »¡¤°»ò¸Ü©O¡H", xuser->chatid);
+ }
+
+ send_to_user(cu, buf, 0, MSG_MESSAGE);
+}
+
+
+static void
+chat_cloak(cu, msg)
+ ChatUser *cu;
+ char *msg;
+{
+ if (CHATSYSOP(cu))
+ {
+ char buf[128];
+
+ cu->uflag ^= PERM_CLOAK;
+ sprintf(buf, "¡» %s", CLOAK(cu) ? MSG_CLOAKED : MSG_UNCLOAK);
+ send_to_user(cu, buf, 0, MSG_MESSAGE);
+ }
+}
+
+
+/* ----------------------------------------------------- */
+
+
+static void
+arrive_room(cuser, room)
+ ChatUser *cuser;
+ ChatRoom *room;
+{
+ char *rname, buf[256];
+
+ /* Xshadow: ¤£¥²°eµ¹¦Û¤v, ¤Ï¥¿´«©Ð¶¡´N·|­«·s build user list */
+
+ sprintf(buf, "+ %s %s %s %s",
+ cuser->userid, cuser->chatid, room->name, cuser->rhost);
+ if (ROOMOP(cuser))
+ strcat(buf, " Op");
+ send_to_room(room, buf, 0, MSG_USERNOTIFY);
+
+ room->occupants++;
+ room_changed(room);
+
+ cuser->room = room;
+ rname = room->name;
+
+ if (cuser->clitype)
+ {
+ send_to_user(cuser, rname, 0, MSG_ROOM);
+ send_to_user(cuser, room->topic, 0, MSG_TOPIC);
+ }
+ else
+ {
+ sprintf(buf, "/r%s", rname);
+ send_to_user(cuser, buf, 0, MSG_MESSAGE);
+ sprintf(buf, "/t%s", room->topic);
+ send_to_user(cuser, buf, 0, MSG_MESSAGE);
+ }
+
+ sprintf(buf, "¡° \033[32;1m%s\033[m ¶i¤J \033[33;1m[%s]\033[m ¥]´[ \033[1;32m[/h]»¡©ú\033[m",
+ cuser->chatid, rname);
+
+ if (!CLOAK(cuser))
+ send_to_room(room, buf, cuser->userno, MSG_MESSAGE);
+ else
+ send_to_user(cuser, buf, 0, MSG_MESSAGE);
+}
+
+
+static int
+enter_room(cuser, rname, msg)
+ ChatUser *cuser;
+ char *rname;
+ char *msg;
+{
+ ChatRoom *room;
+ int create;
+ char buf[256];
+
+ create = 0;
+ room = croom_by_roomid(rname);
+
+ if (room == NULL)
+ {
+ /* new room */
+
+#ifdef DEBUG
+ logit(cuser->userid, "create new room");
+ debug_room();
+#endif
+
+ if (room = roompool)
+ {
+ roompool = room->next;
+ }
+ else
+ {
+ room = (ChatRoom *) malloc(sizeof(ChatRoom));
+ }
+
+ if (room == NULL)
+ {
+ send_to_user(cuser, "¡° µLªk¦A·sÅP¥]´[¤F", 0, MSG_MESSAGE);
+ return 0;
+ }
+
+ memset(room, 0, sizeof(ChatRoom));
+ str_ncpy(room->name, rname, sizeof(room->name));
+ strcpy(room->topic, "³o¬O¤@­Ó·s¤Ñ¦a");
+
+ sprintf(buf, "+ %s 1 0 %s", room->name, room->topic);
+ send_to_room(ROOM_ALL, buf, 0, MSG_ROOMNOTIFY);
+
+ if (mainroom.next)
+ mainroom.next->prev = room;
+ room->next = mainroom.next;
+
+ mainroom.next = room;
+ room->prev = &mainroom;
+
+#ifdef DEBUG
+ logit(cuser->userid, "create room succeed");
+ debug_room();
+#endif
+
+ create = 1;
+ fprintf(flog, "room+\t[%d] %s\n", cuser->sno, rname);
+ }
+ else
+ {
+ if (cuser->room == room)
+ {
+ sprintf(buf, "¡° ±z¥»¨Ó´N¦b [%s] ²á¤Ñ«ÇÅo :)", rname);
+ send_to_user(cuser, buf, 0, MSG_MESSAGE);
+ return 0;
+ }
+
+ if (!CHATSYSOP(cuser) && LOCKED(room) &&
+ !list_belong(room->invite, cuser->userno))
+ {
+ send_to_user(cuser, "¡° ¤º¦³´c¤ü¡A«D½Ð²ö¤J", 0, MSG_MESSAGE);
+ return 0;
+ }
+ }
+
+ exit_room(cuser, EXIT_LOGOUT, msg);
+ arrive_room(cuser, room);
+
+ if (create)
+ cuser->uflag |= PERM_ROOMOP;
+
+ return 0;
+}
+
+
+static void
+cuser_free(cuser)
+ ChatUser *cuser;
+{
+ int sock;
+
+ sock = cuser->sock;
+ shutdown(sock, 2);
+ close(sock);
+
+ FD_CLR(sock, &mainfset);
+
+ list_free(&cuser->ignore);
+ totaluser--;
+
+ if (cuser->room)
+ {
+ exit_room(cuser, EXIT_LOSTCONN, NULL);
+ }
+
+ fprintf(flog, "BYE\t[%d] T%d X%d\n",
+ cuser->sno, time(0) - cuser->tbegin, cuser->xdata);
+}
+
+
+static void
+print_user_counts(cuser)
+ ChatUser *cuser;
+{
+ ChatRoom *room;
+ int num, userc, suserc, roomc, number;
+ char buf[256];
+
+ userc = suserc = roomc = 0;
+
+ room = &mainroom;
+ do
+ {
+ num = room->occupants;
+ if (SECRET(room))
+ {
+ suserc += num;
+ if (CHATSYSOP(cuser))
+ roomc++;
+ }
+ else
+ {
+ userc += num;
+ roomc++;
+ }
+ } while (room = room->next);
+
+ number = (cuser->clitype) ? MSG_MOTD : MSG_MESSAGE;
+
+ sprintf(buf,
+ "¡ó Åwªï¥úÁ{¡i²á¤Ñ«Ç¡j¡A¥Ø«e¶}¤F \033[1;31m%d\033[m ¶¡¥]´[", roomc);
+ send_to_user(cuser, buf, 0, number);
+
+ sprintf(buf, "¡ó ¦@¦³ \033[1;36m%d\033[m ¤H¨ÓÂ\\Àsªù°}", userc);
+ if (suserc)
+ sprintf(buf + strlen(buf), " [%d ¤H¦b¯µ±K²á¤Ñ«Ç]", suserc);
+
+ send_to_user(cuser, buf, 0, number);
+}
+
+
+static int
+login_user(cu, msg)
+ ChatUser *cu;
+ char *msg;
+{
+ int utent;
+
+ char *userid;
+ char *chatid, *passwd;
+ ChatUser *xuser;
+ int level;
+ /* struct hostent *hp; */
+
+#ifndef STAND_ALONE
+ ACCT acct;
+#endif
+
+ /* Xshadow.0915: common client support : /-! userid chatid password */
+ /* client/server ª©¥»¨Ì¾Ú userid §ì .PASSWDS §PÂ_ userlevel */
+
+ userid = nextword(&msg);
+ chatid = nextword(&msg);
+
+#ifdef DEBUG
+ logit("ENTER", userid);
+#endif
+
+#ifndef STAND_ALONE
+ /* Thor.980730: parse space before passwd */
+
+ passwd = msg;
+
+ /* Thor.980813: ¸õ¹L¤@ªÅ®æ§Y¥i, ¦]¬°¤Ï¥¿¦pªGchatid¦³ªÅ®æ, ±K½X¤]¤£¹ï */
+ /* ´Nºâ±K½X¹ï, ¤]¤£·|«ç»ò¼Ë:p */
+ /* ¥i¬O¦pªG±K½X²Ä¤@­Ó¦r¬OªÅ®æ, ¨º¸õ¤Ó¦hªÅ®æ·|¶i¤£¨Ó... */
+ /* Thor.980910: ¥Ñ©ó nextword­×§ï¬°«á±µªÅ®æ¶ñ0, ¶Ç¤J­È«hª½±µ«á²¾¦Ü0«á,
+ ©Ò¥H¤£»Ý§@¦¹°Ê§@ */
+#if 0
+ if (*passwd == ' ')
+ passwd++;
+#endif
+
+ /* Thor.980729: load acct */
+
+ if (!*userid || (acct_load(&acct, userid) < 0))
+ {
+
+#ifdef DEBUG
+ logit("noexist", userid);
+#endif
+
+ if (cu->clitype)
+ send_to_user(cu, "¿ù»~ªº¨Ï¥ÎªÌ¥N¸¹", 0, ERR_LOGIN_NOSUCHUSER);
+ else
+ send_to_user(cu, CHAT_LOGIN_INVALID, 0, MSG_MESSAGE);
+
+ return -1;
+ }
+
+ /* Thor.980813: §ï¥Î¯u¹ê password check, for C/S bbs */
+
+ /* Thor.990214: ª`·N daolib ¤¤ «D 0 ¥Nªí¥¢±Ñ */
+ /* if (!chkpasswd(acct.passwd, passwd)) */
+ if (chkpasswd(acct.passwd, passwd))
+ {
+
+#ifdef DEBUG
+ logit("fake", userid);
+#endif
+
+ if (cu->clitype)
+ send_to_user(cu, "±K½X¿ù»~", 0, ERR_LOGIN_PASSERROR);
+ else
+ send_to_user(cu, CHAT_LOGIN_INVALID, 0, MSG_MESSAGE);
+
+ return -1;
+ }
+
+ level = acct.userlevel;
+ utent = acct.userno;
+
+#else /* STAND_ALONE */
+ level = 1;
+ utent = ++userno_inc;
+#endif /* STAND_ALONE */
+
+ /* Thor.980819: for client/server bbs */
+
+#ifdef DEBUG
+ log_user(NULL);
+#endif
+
+ for (xuser = mainuser; xuser; xuser = xuser->unext)
+ {
+
+#ifdef DEBUG
+ log_user(xuser);
+#endif
+
+ if (xuser->userno == utent)
+ {
+
+#ifdef DEBUG
+ logit("enter", "bogus");
+#endif
+
+ if (cu->clitype)
+ send_to_user(cu, "½Ð¤Å¬£»º¤À¨­¶i¤J²á¤Ñ«Ç¡I", 0,
+ ERR_LOGIN_USERONLINE);
+ else
+ send_to_user(cu, CHAT_LOGIN_BOGUS, 0, MSG_MESSAGE);
+ return -1; /* Thor: ©Î¬O0µ¥¥¦¦Û¤v¤FÂ_? */
+ }
+ }
+
+
+#ifndef STAND_ALONE
+ /* Thor.980629: ¼È®É­É¥Î invalid_chatid Âo°£ ¨S¦³PERM_CHATªº¤H */
+
+ if (!valid_chatid(chatid) || !(level & PERM_CHAT) || (level & PERM_DENYCHAT))
+ { /* Thor.981012: ¹ý©³¤@¨Ç, ³s denychat¤]BAN±¼, §K±o client§@©Ç */
+
+#ifdef DEBUG
+ logit("enter", chatid);
+#endif
+
+ if (cu->clitype)
+ send_to_user(cu, "¤£¦Xªkªº²á¤Ñ«Ç¥N¸¹¡I", 0, ERR_LOGIN_NICKERROR);
+ else
+ send_to_user(cu, CHAT_LOGIN_INVALID, 0, MSG_MESSAGE);
+ return 0;
+ }
+#endif
+
+#ifdef DEBUG
+ debug_user();
+#endif
+
+ if (cuser_by_chatid(chatid) != NULL)
+ {
+ /* chatid in use */
+
+#ifdef DEBUG
+ logit("enter", "duplicate");
+#endif
+
+ if (cu->clitype)
+ send_to_user(cu, "³o­Ó¥N¸¹¤w¸g¦³¤H¨Ï¥Î", 0, ERR_LOGIN_NICKINUSE);
+ else
+ send_to_user(cu, CHAT_LOGIN_EXISTS, 0, MSG_MESSAGE);
+ return 0;
+ }
+
+#ifdef DEBUG /* CHATSYSOP ¤@¶i¨Ó´NÁô¨­ */
+ cu->uflag = level & ~(PERM_ROOMOP | PERM_CHATOP | (CHATSYSOP(cu) ? 0 : PERM_CLOAK));
+#else
+ cu->uflag = level & ~(PERM_ROOMOP | PERM_CHATOP | PERM_CLOAK);
+#endif
+
+ /* Thor: ¶i¨Ó¥ý²MªÅ ROOMOP (¦PPERM_CHAT) */
+
+ strcpy(cu->userid, userid);
+ str_ncpy(cu->chatid, chatid, sizeof(cu->chatid));
+ /* Thor.980921: str_ncpy»P¤@¯ë strncpy¦³©Ò¤£¦P, ¯S§Oª`·N */
+
+ fprintf(flog, "ENTER\t[%d] %s\n", cu->sno, userid);
+
+ /* Xshadow: ¨ú±o client ªº¨Ó·½ */
+
+ dns_name(cu->rhost, cu->ibuf);
+ str_ncpy(cu->rhost, cu->ibuf, sizeof(cu->rhost));
+#if 0
+ hp = gethostbyaddr(cu->rhost, sizeof(struct in_addr), AF_INET);
+ str_ncpy(cu->rhost, hp ? hp->h_name : inet_ntoa((struct in_addr *) cu->rhost), sizeof(cu->rhost));
+#endif
+
+ cu->userno = utent;
+
+ if (cu->clitype)
+ send_to_user(cu, "¶¶§Q", 0, MSG_LOGINOK);
+ else
+ send_to_user(cu, CHAT_LOGIN_OK, 0, MSG_MESSAGE);
+
+ arrive_room(cu, &mainroom);
+
+ send_to_user(cu, "", 0, MSG_MOTDSTART);
+ print_user_counts(cu);
+ send_to_user(cu, "", 0, MSG_MOTDEND);
+
+#ifdef DEBUG
+ logit("enter", "OK");
+#endif
+
+ return 0;
+}
+
+
+static void
+chat_act(cu, msg)
+ ChatUser *cu;
+ char *msg;
+{
+ if (*msg)
+ {
+ char buf[256];
+
+ sprintf(buf, "%s \033[36m%s\033[m", cu->chatid, msg);
+ send_to_room(cu->room, buf, cu->userno, MSG_MESSAGE);
+ }
+}
+
+
+static void
+chat_ignore(cu, msg)
+ ChatUser *cu;
+ char *msg;
+{
+ char *str, buf[256];
+
+ if (RESTRICTED(cu))
+ {
+ str = "¡° ±z¨S¦³ ignore §O¤HªºÅv§Q";
+ }
+ else
+ {
+ char *ignoree;
+
+ str = buf;
+ ignoree = nextword(&msg);
+ if (*ignoree)
+ {
+ ChatUser *xuser;
+
+ xuser = cuser_by_userid(ignoree);
+
+ if (xuser == NULL)
+ {
+ sprintf(str, msg_no_such_id, ignoree);
+ }
+ else if (xuser == cu || CHATSYSOP(xuser) ||
+ (ROOMOP(xuser) && (xuser->room == cu->room)))
+ {
+ sprintf(str, "¡» ¤£¥i¥H ignore [%s]", ignoree);
+ }
+ else
+ {
+ if (list_belong(cu->ignore, xuser->userno))
+ {
+ sprintf(str, "¡° %s ¤w¸g³Q­áµ²¤F", xuser->chatid);
+ }
+ else
+ {
+ list_add(&(cu->ignore), xuser);
+ sprintf(str, "¡» ±N [%s] ¥´¤J§N®c¤F :p", xuser->chatid);
+ }
+ }
+ }
+ else
+ {
+ UserList *list;
+
+ if (list = cu->ignore)
+ {
+ int len;
+ char userid[16];
+
+ send_to_user(cu, "¡» ³o¨Ç¤H³Q¥´¤J§N®c¤F¡G", 0, MSG_MESSAGE);
+ len = 0;
+ do
+ {
+ sprintf(userid, "%-13s", list->userid);
+ strcpy(str + len, userid);
+ len += 13;
+ if (len >= 78)
+ {
+ send_to_user(cu, str, 0, MSG_MESSAGE);
+ len = 0;
+ }
+ } while (list = list->next);
+
+ if (len == 0)
+ return;
+ }
+ else
+ {
+ str = "¡» ±z¥Ø«e¨Ã¨S¦³ ignore ¥ô¦ó¤H";
+ }
+ }
+ }
+
+ send_to_user(cu, str, 0, MSG_MESSAGE);
+}
+
+
+static void
+chat_unignore(cu, msg)
+ ChatUser *cu;
+ char *msg;
+{
+ char *ignoree, *str, buf[80];
+
+ ignoree = nextword(&msg);
+
+ if (*ignoree)
+ {
+ sprintf(str = buf, (list_delete(&(cu->ignore), ignoree)) ?
+ "¡» [%s] ¤£¦A³Q±z§N¸¨¤F" :
+ "¡» ±z¨Ã¥¼ ignore [%s] ³o¸¹¤Hª«", ignoree);
+ }
+ else
+ {
+ str = "¡» ½Ð«ü©ú user ID";
+ }
+ send_to_user(cu, str, 0, MSG_MESSAGE);
+}
+
+
+static void
+chat_join(cu, msg)
+ ChatUser *cu;
+ char *msg;
+{
+ if (RESTRICTED(cu))
+ {
+ send_to_user(cu, "¡° ±z¨S¦³¥[¤J¨ä¥L²á¤Ñ«ÇªºÅv­­", 0, MSG_MESSAGE);
+ }
+ else
+ {
+ char *roomid = nextword(&msg);
+
+ if (*roomid)
+ enter_room(cu, roomid, msg);
+ else
+ send_to_user(cu, "¡° ½Ð«ü©w²á¤Ñ«Ç", 0, MSG_MESSAGE);
+ }
+}
+
+
+static void
+chat_kick(cu, msg)
+ ChatUser *cu;
+ char *msg;
+{
+ char *twit, buf[80];
+ ChatUser *xuser;
+ ChatRoom *room;
+
+ if (!ROOMOP(cu))
+ {
+ send_to_user(cu, msg_not_op, 0, MSG_MESSAGE);
+ return;
+ }
+
+ twit = nextword(&msg);
+ xuser = cuser_by_chatid(twit);
+
+ if (xuser == NULL)
+ { /* Thor.980604: ¥Î userid¤]¹À³q */
+ xuser = cuser_by_userid(twit);
+ }
+
+ if (xuser == NULL)
+ {
+ sprintf(buf, msg_no_such_id, twit);
+ send_to_user(cu, buf, 0, MSG_MESSAGE);
+ return;
+ }
+
+ room = cu->room;
+ if (room != xuser->room || CLOAK(xuser))
+ {
+ sprintf(buf, msg_not_here, twit);
+ send_to_user(cu, buf, 0, MSG_MESSAGE);
+ return;
+ }
+
+ if (CHATSYSOP(xuser))
+ {
+ sprintf(buf, "¡» ¤£¥i¥H kick [%s]", twit);
+ send_to_user(cu, buf, 0, MSG_MESSAGE);
+ return;
+ }
+
+ exit_room(xuser, EXIT_KICK, (char *) NULL);
+
+ if (room == &mainroom)
+ xuser->uptime = 0; /* logout_user(xuser); */
+ else
+ enter_room(xuser, MAIN_NAME, (char *) NULL);
+ /* Thor.980602: ¨ä¹ê½ð´N½ð,¤£­nshow¥XxxxÂ÷¶}¤Fªº°T®§¤ñ¸û¦n */
+}
+
+
+static void
+chat_makeop(cu, msg)
+ ChatUser *cu;
+ char *msg;
+{
+ char *newop, buf[80];
+ ChatUser *xuser;
+ ChatRoom *room;
+
+ /* Thor.980603: PERM_ALLCHAT §ï¬° default ¨S¦³ roomop, ¦ý¥i¥H¦Û¤v¨ú±o */
+
+ newop = nextword(&msg);
+
+ room = cu->room;
+
+ if (!*newop && CHATSYSOP(cu))
+ {
+ /* Thor.980603: PERM_ALLCHAT §ï¬° default ¨S¦³ roomop, ¦ý¥i¥H¦Û¤v¨ú±o */
+ cu->uflag ^= PERM_CHATOP;
+
+ user_changed(cu);
+ if (!CLOAK(cu))
+ {
+ sprintf(buf,ROOMOP(cu) ? "¡° ¤Ñ¨Ï ±N Op Åv¤O±Â¤© %s"
+ : "¡° ¤Ñ¨Ï ±N %s ªº Op Åv¤O¦¬¦^", cu->chatid);
+ send_to_room(room, buf, 0, MSG_MESSAGE);
+ }
+
+ return;
+ }
+
+ /* if (!ROOMOP(cu)) */
+ if (!(cu->uflag & PERM_ROOMOP)) /* Thor.980603: chat roomÁ`ºÞ¤£¯àÂಾ Op Åv¤O */
+ {
+ send_to_user(cu, "¡» ±z¤£¯àÂಾ Op ªºÅv¤O" /* msg_not_op */, 0, MSG_MESSAGE);
+ return;
+ }
+
+ xuser = cuser_by_chatid(newop);
+
+#if 0
+ if (xuser == NULL)
+ { /* Thor.980604: ¥Î userid ¹À¤]³q */
+ xuser = cuser_by_userid(newop);
+ }
+#endif
+
+ if (xuser == NULL)
+ {
+ sprintf(buf, msg_no_such_id, newop);
+ send_to_user(cu, buf, 0, MSG_MESSAGE);
+ return;
+ }
+
+ if (cu == xuser)
+ {
+ send_to_user(cu, "¡° ±z¦­´N¤w¸g¬O Op ¤F°Ú", 0, MSG_MESSAGE);
+ return;
+ }
+
+ /* room = cu->room; */
+
+ if (room != xuser->room || CLOAK(xuser))
+ {
+ sprintf(buf, msg_not_here, xuser->chatid);
+ send_to_user(cu, buf, 0, MSG_MESSAGE);
+ return;
+ }
+
+ cu->uflag &= ~PERM_ROOMOP;
+ xuser->uflag |= PERM_ROOMOP;
+
+ user_changed(cu);
+ user_changed(xuser);
+
+ if (!CLOAK(cu))
+ {
+ sprintf(buf, "¡° %s ±N Op Åv¤OÂಾµ¹ %s",
+ cu->chatid, xuser->chatid);
+ send_to_room(room, buf, 0, MSG_MESSAGE);
+ }
+}
+
+
+static void
+chat_invite(cu, msg)
+ ChatUser *cu;
+ char *msg;
+{
+ char *invitee, buf[80];
+ ChatUser *xuser;
+ ChatRoom *room;
+ UserList **list;
+
+ if (!ROOMOP(cu))
+ {
+ send_to_user(cu, msg_not_op, 0, MSG_MESSAGE);
+ return;
+ }
+
+ invitee = nextword(&msg);
+ xuser = cuser_by_chatid(invitee);
+
+#if 0
+ if (xuser == NULL)
+ { /* Thor.980604: ¥Î userid ¹À¤]³q */
+ xuser = cuser_by_userid(invitee);
+ }
+#endif
+
+ if (xuser == NULL)
+ {
+ sprintf(buf, msg_no_such_id, invitee);
+ send_to_user(cu, buf, 0, MSG_MESSAGE);
+ return;
+ }
+
+ room = cu->room; /* Thor: ¬O§_­n check room ¬O§_ NULL ? */
+ list = &(room->invite);
+
+ if (list_belong(*list, xuser->userno))
+ {
+ sprintf(buf, "¡° %s ¤w¸g±µ¨ü¹LÁܽФF", xuser->chatid);
+ send_to_user(cu, buf, 0, MSG_MESSAGE);
+ return;
+ }
+ list_add(list, xuser);
+
+ sprintf(buf, "¡° %s Áܽбz¨ì [%s] ²á¤Ñ«Ç",
+ cu->chatid, room->name);
+ send_to_user(xuser, buf, 0, MSG_MESSAGE);
+ sprintf(buf, "¡° %s ¦¬¨ì±zªºÁܽФF", xuser->chatid);
+ send_to_user(cu, buf, 0, MSG_MESSAGE);
+}
+
+
+static void
+chat_broadcast(cu, msg)
+ ChatUser *cu;
+ char *msg;
+{
+ char buf[80];
+
+ if (!CHATSYSOP(cu))
+ {
+ send_to_user(cu, "¡° ±z¨S¦³¦b²á¤Ñ«Ç¼s¼½ªºÅv¤O!", 0, MSG_MESSAGE);
+ return;
+ }
+
+ if (*msg == '\0')
+ {
+ send_to_user(cu, "¡° ½Ð«ü©w¼s¼½¤º®e", 0, MSG_MESSAGE);
+ return;
+ }
+
+ sprintf(buf, "\033[1m¡° " BBSNAME "½Í¤Ñ«Ç¼s¼½¤¤ [%s].....\033[m",
+ cu->chatid);
+ send_to_room(ROOM_ALL, buf, 0, MSG_MESSAGE);
+ sprintf(buf, "¡» %s", msg);
+ send_to_room(ROOM_ALL, buf, 0, MSG_MESSAGE);
+}
+
+
+static void
+chat_bye(cu, msg)
+ ChatUser *cu;
+ char *msg;
+{
+ exit_room(cu, EXIT_LOGOUT, msg);
+ cu->uptime = 0;
+ /* logout_user(cu); */
+}
+
+
+/* --------------------------------------------- */
+/* MUD-like social commands : action */
+/* --------------------------------------------- */
+
+
+#if 0 /* itoc.010816: ­«·s½­×¤@¨Ç¤£¤Ó¾A·íªº action ±Ô­z */
+ 1. ª`·N«ö¦r¥À±Æ¦C¡C
+ 2. ½Ð·R¥Î¥þ§Î¼ÐÂI²Å¸¹¡C
+ 3. ¤TÃþ action ¤£¯à¦³­«ÂСC
+ 4. ¥Ñ©ó action ±Ä¥Î¡u³¡¤À¤ñ¹ï¡v¡A¬G³Ì¦n¤£­n¦³«ü¥O¥]§t¥t¤@«ü¥O©Ò¦³ÃöÁä¦rªºª¬ªp¡C
+ ¡]¨Ò¦p fire/fireball¡Akiss/kissbye¡Ano/nod¡Atea/tear/tease¡Adrive/drivel¡Alove/lover¡^
+ ¡]¦³³o¼Ëªº±¡§Î¤]¤£·|«ç»ò¼Ë¡A¥u¬O¨Ï¥ÎªÌ®e©ö·d²V¡^
+ 5. ¥Ñ©ó action ³¡¤À¤ñ¹ï¦Ü¤Ö 2 bytes¡A¬G¤£­n¥Î //1 //2 ³oÃþ¥u¦³¤@­Ó¦rªº action¡C
+ 6. ¥Ñ©ó action ±Ä¥Î³¡¤À¤ñ¹ï¡A¬G«ü¥O¤£¥²¥ÎÁY¼g¡C
+ 7. ²Î¤@ action message ³Ì«á¤£­n¥[¥yÂI¡C
+ 8. ­×¥¿¿ù¦r¡C¡]¬O adore¡A¤£¬O aodre °Ú :p¡^
+ 9. ´î¤Ö­«ÂЪº¦r²´¡C¡]¤£­n¦Ñ¬O¡u¦º¥h¬¡¨Ó¡v°Ú :p¡^
+#endif
+
+
+struct ChatAction
+{
+ char *verb; /* °Êµü */
+ char *chinese; /* ¤¤¤å½Ķ */
+ char *part1_msg; /* ¤¶µü */
+ char *part2_msg; /* °Ê§@ */
+};
+
+
+static ChatAction *
+action_fit(action, actnum, cmd) /* §ä¬Ý¬Ý¬O­þ­Ó ChatAction */
+ ChatAction *action;
+ int actnum;
+ char *cmd;
+{
+ ChatAction *pos, *locus, *mid; /* locus:¥ª«ü¼Ð mid:¤¤«ü¼Ð pos:¥k«ü¼Ð */
+ int cmp;
+
+ /* itoc.010927: ¥Ñ©ó ChatAction ³£¬O«ö¦r¥À±Æ§Çªº¡A©Ò¥H¥i¥H¥Î binary search */
+ /* itoc.010928.µù¸Ñ:¥Ñ©ó¬O binary search ©Ò¥HÁöµM recline ±Æ¦b recycle «e­±
+ ¦ý¬O¥´ //rec ®É«o¥i¯à¥X²{ //recycle ªº®ÄªG¡A§P©wÀu¥ý¦¸§ÇºÝ¿à binary ªº¶¶§Ç */
+
+ locus = action;
+ pos = action + actnum - 1; /* ³Ì«á¤@­Ó¬O NULL¡A¦ý¤£¥i¯à³QÀˬd¨ì */
+
+ while (1)
+ {
+ if (pos <= locus + 1)
+ break;
+
+ mid = locus + ((pos - locus) >> 1);
+
+ if (!(cmp = str_belong(mid->verb, cmd))) /* itoc.010321: MUD-like match */
+ return mid;
+ else if (cmp < 0)
+ locus = mid;
+ else
+ pos = mid;
+ }
+
+ /* ¯S¨Ò: ¦pªG¥k«ü¼Ð°±¯d¦b 1¡A­nÀˬd²Ä 0 ­Ó */
+ if (pos == action + 1)
+ {
+ if (!str_belong(action->verb, cmd)) /* itoc.010321: MUD-like match */
+ return action;
+ }
+
+ return NULL;
+}
+
+
+/* itoc.010805.µù¸Ñ: //adore sysop itoc ¹ï sysop ªº´º¥õ¦³¦p·Ê·Ê¦¿¤ô¡A³sºø¤£µ´¡K */
+
+#define ACTNUM_PARTY 110
+
+static ChatAction party_data[ACTNUM_PARTY] =
+{
+ {
+ "adore", "´º¥õ", "¹ï", "ªº´º¥õ¦³¦p·Ê·Ê¦¿¤ô¡A³sºø¤£µ´¡K"
+ },
+ {
+ "aluba", "ªü¾|¤Ú", "§â", "¬[¤W¬W¤lªü¨ì¦º¡I"
+ },
+ {
+ "aruba", "ªü¾|¤Ú", "§â", "¬[¤W¬W¤lªü¨ì¦º¡I"
+ },
+ {
+ "bark", "§p¥s", "¨L¨L¡I¹ï", "¤jÁn§p¥s"
+ },
+ {
+ "bite", "°Ù«r", "§â", "«r±o¦º¥h¬¡¨Ó"
+ },
+ {
+ "blade", "¤@¤M", "¤@¤M§â", "°e¤W¦è¤Ñ"
+ },
+ {
+ "bless", "¯¬ºÖ", "¯¬ºÖ", "¤ß·Q¨Æ¦¨"
+ },
+ {
+ "blink", "¯w²´", "¹ïµÛ", "¯w¯w²´¡A¤£ª¾·t¥ÜµÛ¤°»ò"
+ },
+ {
+ "board", "¥D¾÷ªO", "§â", "§ì¥h¸÷¥D¾÷ªO"
+ },
+ {
+ "bokan", "®ð¥\\", "Âù´x·L¦X¡A»W¶Õ«Ýµo¡K¡K¬ðµM¶¡¡A¹q¥ú¥E²{¡A¹ï", "¨Ï¥X¤F¢Ð¢÷--¢Ù¢é¢ö"
+ },
+ {
+ "bow", "Áù°`", "²¦°`²¦·qªº¦V", "Áù°`"
+ },
+ {
+ "box", "¹õ¤§¤º", "¶}©l½üÂ\\¦¡²¾¦ì¡A¹ï", "§@¨xŦ§ðÀ»"
+ },
+ {
+ "bye", "ÙTÙT", "¦V", "»¡ÙTÙT"
+ },
+ {
+ "cake", "¥á³J¿|", "®³¥X¤@­Ó³J¿|¡A©¹", "ªºÁy¤W¯{¥h"
+ },
+ {
+ "call", "©I³ê", "¤jÁnªº©I³ê¡A°Ú¡ã", "°Ú¡ã¤H¦b­þ¸Ì°Ú°Ú¡ã°Ú"
+ },
+ {
+ "caress", "»´¼¾", "»´»´ªº¼¾ºNµÛ", ""
+ },
+ {
+ "clap", "¹ª´x", "¦V", "¼ö¯P¹ª´x"
+ },
+ {
+ "claw", "§ì§ì", "±q¿ß«}¼Ö¶é­É¤F°¦¿ß¤ö¡A§â", "§ì±o©ü¤Ñ·t¦a"
+ },
+ {
+ "clock", "¤Á¾xÄÁ", "¤Á±¼", "ªº¾xÄÁ¡A§Ö°_§É°Õ"
+ },
+ {
+ "cola", "Äé¥i¼Ö", "¹ï", "Äé¤F¤@¥[¨Úªº¥i¼Ö"
+ },
+ {
+ "comfort", "¦w¼¢", "·Å¨¥¦w¼¢", ""
+ },
+ {
+ "congratulate", "®¥³ß", "±q­I«á®³¥X¤F©Ô¬¶¡AËé¡IËé¡I®¥³ß", ""
+ },
+ {
+ "cowhide", "Ã@¥´","®³Ã@¤l¹ï", "¬½¬½¦a©â¥´"
+ },
+ {
+ "cpr", "¤f¹ï¤f", "¹ïµÛ", "°µ¤f¹ï¤f¤H¤u©I§l"
+ },
+ {
+ "crime", "¹D¼w", "»¡¡G", "ªº¹D¼w«ü¼Æ¤£°÷¡Aº¡Áy©Ñ®ð"
+ },
+ {
+ "cringe", "¤^¼¦", "¦V", "¨õ°`©}½¥¡A·n§À¤^¼¦"
+ },
+ {
+ "cry", "¤j­ú", "¦V", "Àz°Þ¤j­ú"
+ },
+ {
+ "curtsy", "¤¤¥j§", "Àu¶®¦a¹ïµÛ", "¦æ¤¤¥j¥@¬öªº©}½¥Â§¡C"
+ },
+ {
+ "dance", "¸õ»R", "©Ô¤F", "ªº¤â½¡½¡°_»R"
+ },
+ {
+ "destroy", "·´·À", "²½°_¤F¡y·¥¤j·´·À©G¤å¡z¡AÅF¦V", ""
+ },
+ {
+ "dogleg", "ª¯»L", "¹ï", "ªü½Û©^©Ó¡A¤j¤jª¯»L¤F¤@µf"
+ },
+ {
+ "drivel", "¬y¤f¤ô", "¹ïµÛ", "¬y¤f¤ô"
+ },
+ {
+ "envy", "¸r¼}", "¦V", "¬yÅS¥X¸r¼}ªº²´¥ú"
+ },
+ {
+ "evening", "±ß¦w", "¹ï", "»¡¡y±ß¦w¡z"
+ },
+ {
+ "eye", "°e¬îªi", "¹ï", "ÀW°e¬îªi"
+ },
+ {
+ "fire", "¾R°Ý", "®³µÛ¤õ¬õªºÅK´Î¨«¦V", ""
+ },
+ {
+ "forgive", "­ì½Ì", "±µ¨ü", "ªº¹Dºp¡A­ì½Ì¤F¥L"
+ },
+ {
+ "french", "ªk¦¡§k", "§â¦ÞÀY¦ù¨ì", "³ïÄV¸Ì¡ã¡ã¡ã«z¡I¤@­Ó®öº©ªºªk°ê¦¡²`§k"
+ },
+ {
+ "fuzzy", "­¸³¾", "¬£¥X­¸³¾¤@¸¹¦V", "½Ä¹L¥h"
+ },
+ {
+ "gag", "Á_¼L¤Ú", "§â", " ªº¼L¤Ú¥Î°wÁ_°_¨Ó"
+ },
+ {
+ "giggle", "¶Ì¯º", "¹ïµÛ", "¶Ì¶Ìªº§b¯º"
+ },
+ {
+ "glare", "Àü¤H", "§N§N¦aÀüµÛ", ""
+ },
+ {
+ "glue", "¸É¤ß", "¥Î§Ö°®§â", "ªº¤ßÂH¤F°_¨Ó"
+ },
+ {
+ "goodbye", "§i§O", "²\\²´¨L¨Lªº¦V", "§i§O"
+ },
+ {
+ "grin", "¦l¯º", "¹ï", "ÅS¥X¨¸´cªº¯º®e"
+ },
+ {
+ "growl", "©H­ý", "¹ï", "©H­ý¤£¤w"
+ },
+ {
+ "hand", "´¤¤â", "¸ò", "´¤¤â"
+ },
+ {
+ "hide", "¸ú", "¸ú¦b", "­I«á"
+ },
+ {
+ "hospital", "°eÂå°|", "§â", "°e¶iÂå°|"
+ },
+ {
+ "hrk", "ª@Às®±", "¨Ií¤F¨­§Î¡A¶×»E¤F¤º«l¡A¹ï", "¨Ï¥X¤F¤@°O¢Ö¢÷--¢à£B¢ý--¢Ù¢é¢ö"
+ },
+ {
+ "hug", "¼ö¾Ö", "¼ö±¡ªº¾Ö©ê", ""
+ },
+ {
+ "hypnoze", "¶Ê¯v", "®³µÛ±¾¿ö®Ì§r®Ìªº¡A¹ï", "®i¶}¶Ê¯v"
+ },
+ {
+ "jab", "Ѷ¤H", "¥Î¤O¦aѶµÛ", "¡A¦ü¥G¹ï¥L«Ü¬O¤£º¡"
+ },
+ {
+ "judo", "¬X¹D", "§ì¦í¤F", "ªº¦çÃÌ¡AÂਭ¡K¡K°Ú¡A¬O¤@°O¹LªÓºL"
+ },
+ {
+ "kick", "½ð¤H", "§â", "½ð±oµh­ú¬y®÷"
+ },
+ {
+ "kill", "¬å¤H", "§â", "¶Ã¤M¬å¦º¡ã¡ã"
+ },
+ {
+ "kiss", "»´§k", "»´§k", "ªºÁyÀU"
+ },
+ {
+ "laugh", "¼J¯º", "¤jÁn¼J¯º", ""
+ },
+ {
+ "levis", "µ¹§Ú", "»¡¡Gµ¹§Ú", "¡I¨ä¾l§K½Í¡I"
+ },
+ {
+ "lick", "»Q", "¨g»Q", ""
+ },
+ {
+ "listen", "Å¥", "¥s", "³¬¼L¥J²ÓÅ¥"
+ },
+ {
+ "lobster", "À£¨î", "¬I®i°f½¼§Î©T©w¡A§â", "À£¨î¦b¦aªO¤W"
+ },
+ {
+ "love", "ªí¥Õ", "¹ï", "²`±¡ªºªí¥Õ"
+ },
+ {
+ "mail", "¥´¥]", "§â", "¥´¥]»¼°e¨ì¤j³°"
+ },
+ {
+ "marry", "¨D±B", "±·µÛ¤E¦Ê¤E¤Q¤E¦·ª´ºÀ¦V", "¨D±B"
+ },
+ {
+ "morning", "¦­¦w", "¹ï", "»¡¡y¦­¦w¡z"
+ },
+ {
+ "noon", "¤È¦w", "¹ï", "»¡¡y¤È¦w¡z"
+ },
+ {
+ "nod", "ÂIÀY", "¦V", "ÂIÀYºÙ¬O"
+ },
+ {
+ "nudge", "³»¨{¤l", "¥Î¤â¨y³»", "ªºªÎ¨{¤l"
+ },
+ {
+ "pad", "©çªÓ»H", "»´©ç", "ªºªÓ»H"
+ },
+ {
+ "pan", "¥­©³Áç", "±q­I«á®³¥X¤F¥­©³Áç¡A§â", "ºV©ü¤F"
+ },
+ {
+ "pat", "©çÀY", "©ç©ç", "ªºÀY"
+ },
+ {
+ "pettish", "¼»¼b", "¸ò", "ÜÝÁnÜÝ®ð¦a¼»¼b"
+ },
+ {
+ "pili", "ÅRÆE", "¨Ï¥X §g¤l­· ¤Ñ¦a®Ú ¯ë­YÄb ¤T¦¡¦X¤@¥´¦V", "¡ã¡ã"
+ },
+ {
+ "pinch", "À¾¤H", "¥Î¤Oªº§â", "À¾±o¶Â«C"
+ },
+ {
+ "poke", "ÂW§Ë", "ÂW¤FÂW", "·Q­n¤Þ°_¥Lªºª`·N"
+ },
+ {
+ "puding", "Ä饬¤B", "¹ï", "Äé¤F¤@¥d¨®¥¬¤B"
+ },
+ {
+ "roll", "¥´ºu", "©ñ¥X¦hº¸³Oªº­µ¼Ö¡A", "¦b¦a¤Wºu¨Óºu¥h"
+ },
+ {
+ "protect", "«OÅ@", "»}¦º«OÅ@µÛ", ""
+ },
+ {
+ "pull", "©Ô", "¦º©R¦a©Ô¦í", "¤£©ñ"
+ },
+ {
+ "punch", "´~¤H", "¬½¬½´~¤F", "¤@¹y"
+ },
+ {
+ "rascal", "­A¿à", "¸ò", "­A¿à"
+ },
+ {
+ "recline", "¤JÃh", "Æp¨ì", "ªºÃh¸ÌºÎµÛ¤F¡K¡K"
+ },
+ {
+ "recycle", "¦^¦¬±í", "§â", "¥á¨ì¸ê·½¦^¦¬±í"
+ },
+ {
+ "respond", "­t³d", "¦w¼¢", "»¡¡G¡y¤£­n­ú¡A§Ú·|­t³dªº¡z"
+ },
+ {
+ "scratch", "¿i¤ö", "¾ß°_", "¨­Ã䪺¥Û¤l¿i¿i¦Û¤vªº§Q¤ö"
+ },
+ {
+ "sex", "©ÊÄÌÂZ", "¹ï", "©ÊÄÌÂZ"
+ },
+ {
+ "shit", "³·¯S", "¹ï", "½|¤F¤@Án¡y³·¯S¡z"
+ },
+ {
+ "shrug", "ÁqªÓ", "µL©`¦a¦V", "Áq¤FÁqªÓ»H"
+ },
+ {
+ "sigh", "¼Û®ð", "¹ï", "¼Û¤F¤@¤f®ð"
+ },
+ {
+ "slap", "¥´¦Õ¥ú", "°Ô°Ôªº¤Ú¤F", "¤@¹y¦Õ¥ú"
+ },
+ {
+ "smooch", "¾Ö§k", "¾Ö§kµÛ", ""
+ },
+ {
+ "snicker", "Åѯº", "¼K¼K¼K¦a¹ï", "Åѯº"
+ },
+ {
+ "sniff", "¤£®h", "¹ï", "¶á¤§¥H»ó"
+ },
+ {
+ "sorry", "¹ï¤£°_", "¦V", "»¡¹ï¤£°_¡I§Ú¹ï¤£°_¤j®a¡A§Ú¹ï¤£°_°ê®aªÀ·|"
+ },
+ {
+ "spank", "¥´§¾§¾", "¥Î¤Ú´x¥´", "ªºÁv³¡"
+ },
+ {
+ "squeeze", "ºò¾Ö", "ºòºò¦a¾Ö©êµÛ", ""
+ },
+ {
+ "thank", "·PÁÂ", "¦V", "·PÁ±o¤­Åé§ë¦a"
+ },
+ {
+ "throw", "¥áÂY", "®³¤F¸}¤U¤@¶ô¤j¥ÛÀY´Â", "¨º¥á¤F¹L¥h"
+ },
+ {
+ "tickle", "·kÄo", "©B¼T©B¼T¡A·k", "ªºÄo"
+ },
+ {
+ "wait", "µ¥¤@¤U", "¥s", "µ¥¤@¤U®@¡I"
+ },
+ {
+ "wake", "·n¿ô", "»´»´¦a§â", "·n¿ô"
+ },
+ {
+ "wave", "´§¤â", "¹ïµÛ", "´§´§¤â¡Aªí¥Ü§i§O¤§·N"
+ },
+ {
+ "welcome", "Åwªï", "Åwªï", "¶i¨Ó¤K¨ö¤@¤U"
+ },
+ {
+ "what", "¤°»ò", "»¡¡G¡y", "­ù¤½½M±K«zÃ÷Å¥¬Y?¡H?¡S?¡z"
+ },
+ {
+ "whip", "Ã@²Ç", "¤â¤W®³µÛÄúÀë¡A¥ÎÃ@¤lµh¥´", ""
+ },
+ {
+ "wiggle", "§á§¾ªÑ", "¹ïµÛ", "§á§¾ªÑ"
+ },
+ {
+ "wink", "¯w²´", "¹ï", "¯«¯µªº¯w¯w²´·ú"
+ },
+ {
+ "zap", "²r§ð", "¹ï", "ºÆ¨gªº§ðÀ»"
+ },
+ {
+ NULL, NULL, NULL, NULL
+ }
+};
+
+
+static int
+party_action(cu, cmd, party)
+ ChatUser *cu;
+ char *cmd;
+ char *party;
+{
+ ChatAction *cap;
+ char buf[256];
+
+ if ((cap = action_fit(party_data, ACTNUM_PARTY, cmd)))
+ {
+ if (*party == '\0')
+ {
+ party = "¤j®a";
+ }
+ else
+ {
+ ChatUser *xuser;
+
+ xuser = fuzzy_cuser_by_chatid(party);
+ if (xuser == NULL)
+ { /* Thor.980724: ¥Î userid¤]¹À³q */
+ xuser = cuser_by_userid(party);
+ }
+
+ if (xuser == NULL)
+ {
+ sprintf(buf, msg_no_such_id, party);
+ send_to_user(cu, buf, 0, MSG_MESSAGE);
+ return 0;
+ }
+ else if (xuser == FUZZY_USER)
+ {
+ send_to_user(cu, "¡° ½Ð«ü©ú²á¤Ñ¥N¸¹", 0, MSG_MESSAGE);
+ return 0;
+ }
+ else if (cu->room != xuser->room || CLOAK(xuser))
+ {
+ sprintf(buf, msg_not_here, party);
+ send_to_user(cu, buf, 0, MSG_MESSAGE);
+ return 0;
+ }
+ else
+ {
+ party = xuser->chatid;
+ }
+ }
+ sprintf(buf, "\033[1;32m%s \033[31m%s\033[33m %s \033[31m%s\033[m",
+ cu->chatid, cap->part1_msg, party, cap->part2_msg);
+ send_to_room(cu->room, buf, cu->userno, MSG_MESSAGE);
+ return 0; /* Thor: cu->room ¬O§_¬° NULL? */
+ }
+ return 1;
+}
+
+
+/* --------------------------------------------- */
+/* MUD-like social commands : speak */
+/* --------------------------------------------- */
+
+
+/* itoc.010805.µù¸Ñ: //ask ¤j®a¤µ¤Ñ¹L±o¦n¶Ü¡H itoc °Ý¤j®a¤µ¤Ñ¹L±o¦n¶Ü¡H*/
+
+#define ACTNUM_SPEAK 29
+
+static ChatAction speak_data[ACTNUM_SPEAK] =
+{
+ {
+ "ask", "¸ß°Ý", "°Ý", NULL
+ },
+ {
+ "broadcast", "¼s¼½", "¼s¼½", NULL
+ },
+ {
+ "chant", "ºq¹|", "°ªÁnºq¹|", NULL
+ },
+ {
+ "cheer", "³Üªö", "³Üªö", NULL
+ },
+ {
+ "chuckle", "»´¯º", "»´¯º", NULL
+ },
+ {
+ "curse", "¶A©G", "·t·F", NULL
+ },
+ {
+ "demand", "­n¨D", "­n¨D", NULL
+ },
+ {
+ "fuck", "¤½·F", "¤½·F", NULL
+ },
+ {
+ "groan", "©D§u", "©D§u", NULL
+ },
+ {
+ "grumble", "µo¨cÄÌ", "µo¨cÄÌ", NULL
+ },
+ {
+ "guitar", "¼u°Û", "Ãä¼uµÛ¦N¥L¡AÃä°ÛµÛ", NULL
+ },
+ {
+ "hum", "³ä³ä", "³ä³ä¦Û»y", NULL
+ },
+ {
+ "moan", "«è¹Ä", "«è¹Ä", NULL
+ },
+ {
+ "notice", "±j½Õ", "±j½Õ", NULL
+ },
+ {
+ "order", "©R¥O", "©R¥O", NULL
+ },
+ {
+ "ponder", "¨H«ä", "¨H«ä", NULL
+ },
+ {
+ "pout", "äþ¼L", "äþµÛ¼L»¡", NULL
+ },
+ {
+ "pray", "¬èë", "¬èë", NULL
+ },
+ {
+ "request", "Àµ¨D", "Àµ¨D", NULL
+ },
+ {
+ "shout", "¤j½|", "¤j½|", NULL
+ },
+ {
+ "sing", "°Ûºq", "°Ûºq", NULL
+ },
+ {
+ "smile", "·L¯º", "·L¯º", NULL
+ },
+ {
+ "smirk", "°²¯º", "°²¯º", NULL
+ },
+ {
+ "swear", "µo»}", "µo»}", NULL
+ },
+ {
+ "tease", "¼J¯º", "¼J¯º", NULL
+ },
+ {
+ "whimper", "¶ã«|", "¶ã«|ªº»¡", NULL
+ },
+ {
+ "yawn", "«¢¤í", "Ã䥴«¢¤íÃ仡", NULL
+ },
+ {
+ "yell", "¤j³Û", "¤j³Û", NULL
+ },
+ {
+ NULL, NULL, NULL, NULL
+ }
+};
+
+
+static int
+speak_action(cu, cmd, msg)
+ ChatUser *cu;
+ char *cmd;
+ char *msg;
+{
+ ChatAction *cap;
+ char buf[256];
+
+ if ((cap = action_fit(speak_data, ACTNUM_SPEAK, cmd)))
+ {
+ sprintf(buf, "\033[1;32m%s \033[31m%s¡G\033[33m %s\033[m",
+ cu->chatid, cap->part1_msg, msg);
+ send_to_room(cu->room, buf, cu->userno, MSG_MESSAGE);
+ return 0;
+ }
+ return 1;
+}
+
+
+/* ----------------------------------------------------- */
+/* MUD-like social commands : condition */
+/* ----------------------------------------------------- */
+
+
+/* itoc.010805.µù¸Ñ: //agree itoc ²`ªí¦P·N */
+
+#define ACTNUM_CONDITION 73
+
+static ChatAction condition_data[ACTNUM_CONDITION] =
+{
+ {
+ "agree", "¦P·N", "²`ªí¦P·N", NULL
+ },
+ {
+ "aha", "ÆF¥ú", "­W«ä¨}¤[¡A©¿µMÆF¥ú¤@²{¡A¤£¸T§r«¢ªº¤@Án", NULL
+ },
+ {
+ "akimbo", "´¡¸y", "¤S®ð¤SµL©`ªº¨â¤â´¡¸y", NULL
+ },
+ {
+ "alas", "«u§r", "«u§r§r¡ã", NULL
+ },
+ {
+ "applaud", "©ç¤â", "°Ô°Ô°Ô°Ô°Ô¡K¡K°Ô°Ô", NULL
+ },
+ {
+ "avert", "®`²Û", "®`²Û¦aÂà¶}µø½u", NULL
+ },
+ {
+ "ayo", "­üËç³Þ", "­üËç³Þ¡ã", NULL
+ },
+ {
+ "back", "§¤¦^¨Ó", "¦^¨Ó§¤¥¿Ä~Äò¾Ä¾Ô", NULL
+ },
+ {
+ "blood", "¦b¦å¤¤", "­Ë¦b¦åªy¤§¤¤", NULL
+ },
+ {
+ "blush", "Áy¬õ", "Áy³£¬õ¤F", NULL
+ },
+ {
+ "broke", "¤ß¸H", "ªº¤ß¯}¸H¦¨¤@¤ù¤@¤ùªº", NULL
+ },
+ {
+ "bug", "¯äÂÎ", "µo²{³o¨t²Î¦³¢Ð¢ý¢ï¡ã", NULL
+ },
+ {
+ "careles", "¨S¤H²z", "¶ã¡ã¡ã³£¨S¦³¤H²z§Ú ¡G¡ã", NULL
+ },
+ {
+ "chew", "¶ß¥Ê¤l", "«Ü±y¶¢ªº¶ß°_¥Ê¤l¨Ó¤F", NULL
+ },
+ {
+ "climb", "ª¦¤s", "¦Û¤vºCºCª¦¤W¤s¨Ó¡K¡K", NULL
+ },
+ {
+ "cold", "·P«_", "·P«_¤F¡A¶ý¶ý¤£Åý§Ú¥X¥hª± ¡G¡]", NULL
+ },
+ {
+ "cough", "«y¹Â", "«y¤F´XÁn", NULL
+ },
+ {
+ "crash", "·í¾÷", "¶ã¡K" BBSNAME "·í¾÷¤F", NULL
+ },
+ {
+ "die", "¼ÉÀÅ", "·í³õ¼ÉÀÅ", NULL
+ },
+ {
+ "dive", "¼ç¤ô", "¸õ¨ì¤ô¸Ì¸ú°_¨Ó", NULL
+ },
+ {
+ "faint", "©ü­Ë", "·í³õ©ü­Ë", NULL
+ },
+ {
+ "fart", "©ñ§¾", "¥þ¬O¦b©ñ§¾¡A­J§è¤@³q¡I", NULL
+ },
+ {
+ "flop", "­»¿¼¥Ö", "½ò¨ì­»¿¼¥Ö¡K·Æ­Ë¡I", NULL
+ },
+ {
+ "fly", "ÄÆÄÆµM", "ÄÆÄÆµM¦a¡A¦n¦ü­¸¤F°_¨Ó", NULL
+ },
+ {
+ "frown", "ÂÙ¬Ü", "Â٬ܡA¤£ª¾¬°¤F¤°»ò", NULL
+ },
+ {
+ "gold", "®³ª÷µP", "°ÛµÛ¡G¡yª÷£|£±£½ª÷£|£±£½¥X°ê¤ñÁÉ¡A±o«a­x¡A®³ª÷µP¡A¥úºa­Ë¾H¨Ó¡I¡z", NULL
+ },
+ {
+ "gulu", "¨{¤l¾j", "ªº¨{¤lµo¥X©BÂP©BÂP¡ãªºÁn­µ", NULL
+ },
+ {
+ "haha", "«¢«¢", "«z«¢«¢«¢¡K¤j¯º¤F°_¨Ó", NULL
+ },
+ {
+ "happy", "°ª¿³", "°ª¿³±o¦b¦a¤W¥´ºu", NULL
+ },
+ {
+ "hiccup", "¥´ÜÐ", "¥´ÜЭӤ£°±", NULL
+ },
+ {
+ "hoho", "¨þ¨þ", "¨þ¨þ¨þ¯º­Ó¤£°±", NULL
+ },
+ {
+ "hypnzed", "³Q¶Ê¯v", "²´¯«§bº¢¡A³Q¶Ê¯v¤F¡K¡K£C¢è£Czzz", NULL
+ },
+ {
+ "idle", "§b¦í", "Àþ¶¡§b¦í¤F", NULL
+ },
+ {
+ "jacky", "µl¤l", "µl¤l¯ëªº®Ì¨Ó®Ì¥h", NULL
+ },
+ {
+ "jealous", "¦Y¾L", "®ð¹ª¹ª¦a³Ü¤F¤@¬û¾L", NULL
+ },
+ {
+ "jump", "¸õ¼Ó", "¸õ¼Ó¦Û±þ", NULL
+ },
+ {
+ "luck", "©¯¹B", "«z¡IºÖ®ð°Õ¡I", NULL
+ },
+ {
+ "macarn", "¤@ºØ»R", "¶}©l¸õ°_¤F¢Û¢é¢Ñ¢é¢à¢í¢Ü¢é¡ã¡ã¡ã¡ã", NULL
+ },
+ {
+ "miou", "ØpØp", "ØpØp¤f­]¤f­]¡ã¡ã¡ã¡ã¡ã", NULL
+ },
+ {
+ "money", "ÁÈ¿ú", "®I­º¬ã¨s«ç¼ËÁȤj¿ú", NULL
+ },
+ {
+ "mouth", "«ó¼L", "«ó¼L¤¤¡I", NULL
+ },
+ {
+ "mutter", "§C©B", "§CÁn©B¾ºµÛ¬Y¨Ç¨Æ¡C", NULL
+ },
+ {
+ "nani", "«ç»ò·|", "¡G©`£®°Ú®º??", NULL
+ },
+ {
+ "nose", "¬y»ó¦å", "¬y»ó¦å", NULL
+ },
+ {
+ "puke", "¹Ã¦R", "¹Ã¦R¤¤", NULL
+ },
+ {
+ "rest", "¥ð®§", "¥ð®§¤¤¡A½Ð¤Å¥´ÂZ", NULL
+ },
+ {
+ "reverse", "½¨{", "½¨{", NULL
+ },
+ {
+ "room", "¶}©Ð¶¡", "r-o-O-m-r-O-¢Ý-Mmm-rR¢à........", NULL
+ },
+ {
+ "scream", "¦y¥s", "¤jÁn¦y¥s¡I °Ú~~~~~~~", NULL
+ },
+ {
+ "shake", "·nÀY", "·n¤F·nÀY", NULL
+ },
+ {
+ "sleep", "ºÎµÛ", "­w¦bÁä½L¤WºÎµÛ¤F¡A¤f¤ô¬y¶iÁä½L¡A³y¦¨·í¾÷¡I", NULL
+ },
+ {
+ "snore", "¥´ÂM¤¤", "¥´ÂM¤¤¡K", NULL
+ },
+ {
+ "sob", "½â­F", "¢á¢÷¢ö ¢Ý¢î ¢Ð¢ñ¢ü¢ë¢ð¡I¡I", NULL
+ },
+ {
+ "stare", "¾®µø", "ÀRÀR¦a¾®µøµÛ¤ÑªÅ", NULL
+ },
+ {
+ "stretch", "¯h­Â", "¦ù¦ùÃi¸y¤S¥´¤F­Ó¨þ¤í«Ü¯h­Â¦üªº¡C", NULL
+ },
+ {
+ "story", "Á¿¥j", "¶}©lÁ¿¥j¤F", NULL
+ },
+ {
+ "strut", "·nÂ\\¨«", "¤j·n¤jÂ\\¦a¨«", NULL
+ },
+ {
+ "suicide", "¦Û±þ", "¦Û±þ", NULL
+ },
+ {
+ "sweat", "¬y¦½", "´§¦½¦p«B¡I", NULL
+ },
+ {
+ "tear", "¬y²\\", "µh­ú¬y®÷¤¤.....", NULL
+ },
+ {
+ "think", "«ä¦Ò", "¬nµÛÀY·Q¤F¤@¤U", NULL
+ },
+ {
+ "tongue", "¦R¦Þ", "¦R¤F¦R¦ÞÀY", NULL
+ },
+ {
+ "wall", "¼²Àð", "¶]¥h¼²Àð", NULL
+ },
+ {
+ "wawa", "«z«z", "«z«z«z~~~~~!!!!! ~~~>_<~~~", NULL
+ },
+ {
+ "wc", "¬~¤â¶¡", "¥ø¬~¤â¶¡¤@¤U :>", NULL
+ },
+ {
+ "whine", "¨{¤l¾j", "¨{¤l¾j! :(", NULL
+ },
+ {
+ "whistle", "§j¤f­ï", "§j¤f­ï", NULL
+ },
+ {
+ "wolf", "¯TÀz", "£±£¹£±£¹¡K£±£¹£±£¹¡K", NULL
+ },
+ {
+ "www", "¨L¨L", "¨L¨L¨L¡I", NULL
+ },
+ {
+ "ya", "£¬­C", "¾¾¡ã¢ç¢Ï¡I *^_^*", NULL
+ },
+ {
+ "zzz", "¥´©I", "©IÂP¡ãZZzZz£C¢èZZzzZzzzZZ", NULL
+ },
+ {
+ NULL, NULL, NULL, NULL
+ }
+};
+
+
+static int
+condition_action(cu, cmd)
+ ChatUser *cu;
+ char *cmd;
+{
+ ChatAction *cap;
+ char buf[256];
+
+ if ((cap = action_fit(condition_data, ACTNUM_CONDITION, cmd)))
+ {
+ sprintf(buf, "\033[1;32m%s \033[31m%s\033[m",
+ cu->chatid, cap->part1_msg);
+ send_to_room(cu->room, buf, cu->userno, MSG_MESSAGE);
+ return 1;
+ }
+ return 0;
+}
+
+
+/* --------------------------------------------- */
+/* MUD-like social commands : help */
+/* --------------------------------------------- */
+
+
+static char *dscrb[] =
+{
+ "\033[1;37m¡i Verb + Nick¡G °Êµü + ¹ï¤è¦W¦r ¡j\033[36m ¨Ò¡G//kick piggy\033[m",
+ "\033[1;37m¡i Verb + Message¡G°Êµü + ­n»¡ªº¸Ü ¡j\033[36m ¨Ò¡G//sing ¤Ñ¤Ñ¤ÑÂÅ\033[m",
+ "\033[1;37m¡i Verb¡G°Êµü ¡j ¡ô¡õ¡G¸ܭ«´£\033[m", NULL
+};
+
+
+
+static ChatAction *catbl[] =
+{
+ party_data, speak_data, condition_data, NULL
+};
+
+
+static void
+chat_partyinfo(cu, msg)
+ ChatUser *cu;
+ char *msg;
+{
+ if (common_client_command)
+ {
+ send_to_user(cu, "3 °Ê§@ ¥æ½Í ª¬ºA", 0, MSG_PARTYINFO);
+ }
+}
+
+
+static void
+chat_party(cu, msg)
+ ChatUser *cu;
+ char *msg;
+{
+ int kind, i;
+ ChatAction *cap;
+ char buf[80];
+
+ if (!common_client_command)
+ return;
+
+ kind = atoi(nextword(&msg));
+ if (kind < 0 || kind > 2)
+ return;
+
+ sprintf(buf, "%d\t%s", kind, kind == 2 ? "I" : "");
+
+ /* Xshadow: ¥u¦³ condition ¤~¬O immediate mode */
+ send_to_user(cu, buf, 0, MSG_PARTYLISTSTART);
+
+ cap = catbl[kind];
+ for (i = 0; cap[i].verb; i++)
+ {
+ sprintf(buf, "%-10s %-20s", cap[i].verb, cap[i].chinese);
+ send_to_user(cu, buf, 0, MSG_PARTYLIST);
+ }
+
+ sprintf(buf, "%d", kind);
+ send_to_user(cu, buf, 0, MSG_PARTYLISTEND);
+}
+
+
+#define MAX_VERB_LEN 8
+#define VERB_NO 10
+
+
+static void
+view_action_verb(cu, cmd) /* Thor.980726: ·s¥[°Êµü¤ÀÃþÅã¥Ü */
+ ChatUser *cu;
+ int cmd;
+{
+ int i;
+ char *p, *q, *data, *expn, buf[256];
+ ChatAction *cap;
+
+ send_to_user(cu, "/c", 0, MSG_CLRSCR);
+
+ data = buf;
+
+ if (cmd < '1' || cmd > '3')
+ { /* Thor.980726: ¼g±o¤£¦n, ·Q¿ìªk§ï¶i... */
+ for (i = 0; p = dscrb[i]; i++)
+ {
+ sprintf(data, " [//]help %d - MUD-like ªÀ¥æ°Êµü ²Ä %d Ãþ", i + 1, i + 1);
+ send_to_user(cu, data, 0, MSG_MESSAGE);
+ send_to_user(cu, p, 0, MSG_MESSAGE);
+ send_to_user(cu, " ", 0, MSG_MESSAGE); /* Thor.980726: ´«¦æ */
+ }
+ }
+ else
+ {
+ i = cmd - '1';
+
+ send_to_user(cu, dscrb[i], 0, MSG_MESSAGE);
+
+ expn = buf + 100; /* Thor.980726: À³¸Ó¤£·|overlap§a? */
+
+ *data = '\0';
+ *expn = '\0';
+
+ cap = catbl[i];
+
+ for (i = 0; p = cap[i].verb; i++)
+ {
+ q = cap[i].chinese;
+
+ strcat(data, p);
+ strcat(expn, q);
+
+ if (((i + 1) % VERB_NO) == 0)
+ {
+ send_to_user(cu, data, 0, MSG_MESSAGE);
+ send_to_user(cu, expn, 0, MSG_MESSAGE); /* Thor.980726: Åã¥Ü¤¤¤åµù¸Ñ */
+ *data = '\0';
+ *expn = '\0';
+ }
+ else
+ {
+ strncat(data, " ", MAX_VERB_LEN - strlen(p));
+ strncat(expn, " ", MAX_VERB_LEN - strlen(q));
+ }
+ }
+
+ if (i % VERB_NO)
+ {
+ send_to_user(cu, data, 0, MSG_MESSAGE);
+ send_to_user(cu, expn, 0, MSG_MESSAGE); /* Thor.980726: Åã¥Ü¤¤¤åµù¸Ñ */
+ }
+ }
+ /* send_to_user(cu, " ", 0); *//* Thor.980726: ´«¦æ, »Ý­n " " ¶Ü? */
+}
+
+
+/* ----------------------------------------------------- */
+/* chat user service routines */
+/* ----------------------------------------------------- */
+
+
+static ChatCmd chatcmdlist[] =
+{
+ "act", chat_act, 0,
+ "bye", chat_bye, 0,
+ "chatroom", chat_chatroom, 1, /* Xshadow: for common client */
+ "clear", chat_clear, 0,
+ "cloak", chat_cloak, 2,
+ "date", chat_date, 0,
+ "flags", chat_setroom, 0,
+ "help", chat_help, 0,
+ "ignore", chat_ignore, 1,
+ "invite", chat_invite, 0,
+ "join", chat_join, 0,
+ "kick", chat_kick, 1,
+ "msg", chat_private, 0,
+ "nick", chat_nick, 0,
+ "operator", chat_makeop, 0,
+ "party", chat_party, 1, /* Xshadow: party data for common client */
+ "partyinfo", chat_partyinfo, 1, /* Xshadow: party info for common client */
+
+#ifndef STAND_ALONE
+ "query", chat_query, 0,
+#endif
+
+ "quit", chat_bye, 0,
+
+ "room", chat_list_rooms, 0,
+ "unignore", chat_unignore, 1,
+ "whoin", chat_list_by_room, 1,
+ "wall", chat_broadcast, 2,
+
+ "who", chat_map_chatids_thisroom, 0,
+ "list", chat_list_users, 0,
+ "topic", chat_topic, 1,
+ "version", chat_version, 1,
+
+ NULL, NULL, 0
+};
+
+
+/* Thor: 0 ¤£¥Î exact, 1 ­n exactly equal, 2 ¯µ±K«ü¥O */
+
+
+static int
+command_execute(cu)
+ ChatUser *cu;
+{
+ char *cmd, *msg, buf[128];
+ /* Thor.981108: lkchu patch: chatid + msg ¥u¥Î 80 bytes ¤£°÷, §ï¬° 128 */
+ ChatCmd *cmdrec;
+ int match, ch;
+
+ msg = cu->ibuf;
+ match = *msg;
+
+ /* Validation routine */
+
+ if (cu->room == NULL)
+ {
+ /* MUST give special /! or /-! command if not in the room yet */
+
+ if (match == '/' && ((ch = msg[1]) == '!' || (ch == '-' && msg[2] == '!')))
+ {
+ if (ch == '-')
+ fprintf(flog, "cli\t[%d] S%d\n", cu->sno, cu->sock);
+
+ cu->clitype = (ch == '-') ? 1 : 0;
+ return (login_user(cu, msg + 2 + cu->clitype));
+ }
+ else
+ {
+ return -1;
+ }
+ }
+
+ /* If not a /-command, it goes to the room. */
+
+ if (match != '/')
+ {
+ if (match)
+ {
+ if (cu->room && !CLOAK(cu)) /* Áô¨­ªº¤H¤]¤£¯à»¡¸Ü®@ */
+ {
+ char chatid[16];
+
+ sprintf(chatid, "%s:", cu->chatid);
+ sprintf(buf, "%-10s%s", chatid, msg);
+ send_to_room(cu->room, buf, cu->userno, MSG_MESSAGE);
+ }
+ }
+ return 0;
+ }
+
+ msg++;
+ cmd = nextword(&msg);
+ match = 0;
+
+ if (*cmd == '/')
+ {
+ cmd++;
+ /* if (!*cmd || !str_cmp("help", cmd)) */
+ if (!*cmd || str_match(cmd, "help") >= 0) /* itoc.010321: ³¡¤À match ´Nºâ */
+ {
+ cmd = nextword(&msg); /* Thor.980726: °Êµü¤ÀÃþ */
+ view_action_verb(cu, *cmd);
+ match = 1;
+ }
+ else if (!party_action(cu, cmd, msg))
+ match = 1;
+ else if (!speak_action(cu, cmd, msg))
+ match = 1;
+ else
+ match = condition_action(cu, cmd);
+ }
+ else
+ {
+ char *str;
+
+ common_client_command = 0;
+ if (*cmd == '-')
+ {
+ if (cu->clitype)
+ {
+ cmd++; /* Xshadow: «ü¥O±q¤U¤@­Ó¦r¤¸¤~¶}©l */
+ common_client_command = 1;
+ }
+ else
+ {
+ /* ¤£¬O common client ¦ý°e¥X common client «ü¥O -> °²¸Ë¨S¬Ý¨ì */
+ }
+ }
+
+ str_lower(buf, cmd);
+
+ for (cmdrec = chatcmdlist; str = cmdrec->cmdstr; cmdrec++)
+ {
+ switch (cmdrec->exact)
+ {
+ case 1: /* exactly equal */
+ match = !str_cmp(str, buf);
+ break;
+
+ case 2: /* Thor: secret command */
+ if (CHATSYSOP(cu))
+ match = !str_cmp(str, buf);
+ break;
+
+ default: /* not necessary equal */
+ match = str_match(buf, str) >= 0;
+ break;
+ }
+
+ if (match)
+ {
+ cmdrec->cmdfunc(cu, msg);
+ break;
+ }
+ }
+ }
+
+ if (!match)
+ {
+ sprintf(buf, "¡» «ü¥O¿ù»~¡G/%s", cmd);
+ send_to_user(cu, buf, 0, MSG_MESSAGE);
+ }
+
+ return 0;
+}
+
+
+/* ----------------------------------------------------- */
+/* serve chat_user's connection */
+/* ----------------------------------------------------- */
+
+
+static int
+cuser_serve(cu)
+ ChatUser *cu;
+{
+ int ch, len, isize;
+ char *str, *cmd, buf[256];
+
+ str = buf;
+ len = recv(cu->sock, str, sizeof(buf) - 1, 0);
+ if (len < 0)
+ {
+ ch = errno;
+
+ exit_room(cu, EXIT_LOSTCONN, NULL);
+ logit("recv", strerror(ch));
+ return -1;
+ }
+
+ if (len == 0)
+ {
+ if (++cu->retry > 100)
+ return -1;
+ return 0;
+ }
+
+#if 0
+ /* Xshadow: ±N°e¹Fªº¸ê®Æ©¾¹ê¬ö¿ý¤U¨Ó */
+ memcpy(logbuf, buf, sizeof(buf));
+ for (ch = 0; ch < sizeof(buf); ch++)
+ {
+ if (!logbuf[ch])
+ logbuf[ch] = '$';
+ }
+
+ logbuf[len + 1] = '\0';
+ logit("recv: ", logbuf);
+#endif
+
+#if 0
+ logit(cu->userid, str);
+#endif
+
+ cu->xdata += len;
+
+ isize = cu->isize;
+ cmd = cu->ibuf + isize;
+ while (len--)
+ {
+ ch = *str++;
+
+ if (ch == '\r' || !ch)
+ continue;
+
+ if (ch == '\n')
+ {
+ *cmd = '\0';
+
+ if (command_execute(cu) < 0)
+ return -1;
+
+ isize = 0;
+ cmd = cu->ibuf;
+
+ continue;
+ }
+
+ if (isize < SCR_WIDTH)
+ {
+ *cmd++ = ch;
+ isize++;
+ }
+ }
+ cu->isize = isize;
+ return 1;
+}
+
+
+/* ----------------------------------------------------- */
+/* chatroom server core routines */
+/* ----------------------------------------------------- */
+
+
+static int
+/* start_daemon(mode)
+ int mode; */
+servo_daemon(inetd)
+ int inetd;
+{
+ int fd, value;
+ char buf[80];
+ struct sockaddr_in sin;
+ struct linger ld;
+#ifdef HAVE_RLIMIT
+ struct rlimit limit;
+#endif
+
+ /*
+ * 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((time_t *) &value);
+ gmtime((time_t *) &value);
+ strftime(buf, 80, "%d/%b/%Y:%H:%M:%S", localtime((time_t *) &value));
+
+ /* --------------------------------------------------- */
+ /* speed-hacking DNS resolve */
+ /* --------------------------------------------------- */
+
+ dns_init();
+#if 0
+ gethostname(buf, sizeof(buf));
+ gethostbyname(buf);
+#endif
+
+#ifdef HAVE_RLIMIT
+ /* --------------------------------------------------- */
+ /* adjust the resource limit */
+ /* --------------------------------------------------- */
+
+ getrlimit(RLIMIT_NOFILE, &limit);
+ limit.rlim_cur = limit.rlim_max;
+ setrlimit(RLIMIT_NOFILE, &limit);
+
+ limit.rlim_cur = limit.rlim_max = 4 * 1024 * 1024;
+ setrlimit(RLIMIT_DATA, &limit);
+
+#ifdef SOLARIS
+#define RLIMIT_RSS RLIMIT_AS /* Thor.981206: port for solaris 2.6 */
+#endif
+
+ setrlimit(RLIMIT_RSS, &limit);
+
+ limit.rlim_cur = limit.rlim_max = 0;
+ setrlimit(RLIMIT_CORE, &limit);
+
+#if 0
+ limit.rlim_cur = limit.rlim_max = 60 * 20;
+ setrlimit(RLIMIT_CPU, &limit);
+#endif
+#endif
+
+ /* --------------------------------------------------- */
+ /* detach daemon process */
+ /* --------------------------------------------------- */
+
+ close(2);
+ close(1);
+
+ /* if (mode > 1) */
+ if (inetd)
+ return 0;
+
+ close(0);
+
+ if (fork())
+ exit(0);
+
+ setsid();
+
+ if (fork())
+ exit(0);
+
+ /* --------------------------------------------------- */
+ /* bind the service port */
+ /* --------------------------------------------------- */
+
+ fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+
+ /*
+ * timeout ¤è­±, ±N socket §ï¦¨ O_NDELAY (no delay, non-blocking),
+ * ¦pªG¯à¶¶§Q°e¥X¸ê®Æ´N°e¥X, ¤£¯à°e¥X´Nºâ¤F, ¤£¦Aµ¥«Ý TCP_TIMEOUT ®É¶¡¡C
+ * (default ¬O 120 ¬í, ¨Ã¥B¦³ 3-way handshaking ¾÷¨î, ¦³¥i¯à¤@µ¥¦Aµ¥)¡C
+ */
+
+#if 1
+ fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NDELAY);
+#endif
+
+ value = 1;
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &value, sizeof(value));
+
+ value = 1;
+ setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *) &value, sizeof(value));
+
+ ld.l_onoff = ld.l_linger = 0;
+ setsockopt(fd, SOL_SOCKET, SO_LINGER, (char *) &ld, sizeof(ld));
+
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(CHAT_PORT);
+ sin.sin_addr.s_addr = htonl(INADDR_ANY);
+ memset(sin.sin_zero, 0, sizeof(sin.sin_zero));
+
+ if ((bind(fd, (struct sockaddr *) & sin, sizeof(sin)) < 0) ||
+ (listen(fd, SOCK_QLEN) < 0))
+ exit(1);
+
+ return fd;
+}
+
+
+#ifdef SERVER_USAGE
+static void
+server_usage()
+{
+ struct rusage ru;
+
+ if (getrusage(RUSAGE_SELF, &ru))
+ return;
+
+ fprintf(flog, "\n[Server Usage]\n\n"
+ "user time: %.6f\n"
+ "system time: %.6f\n"
+ "maximum resident set size: %lu P\n"
+ "integral resident set size: %lu\n"
+ "page faults not requiring physical I/O: %d\n"
+ "page faults requiring physical I/O: %d\n"
+ "swaps: %d\n"
+ "block input operations: %d\n"
+ "block output operations: %d\n"
+ "messages sent: %d\n"
+ "messages received: %d\n"
+ "signals received: %d\n"
+ "voluntary context switches: %d\n"
+ "involuntary context switches: %d\n"
+ "gline: %d\n\n",
+
+ (double) ru.ru_utime.tv_sec + (double) ru.ru_utime.tv_usec / 1000000.0,
+ (double) ru.ru_stime.tv_sec + (double) ru.ru_stime.tv_usec / 1000000.0,
+ ru.ru_maxrss,
+ ru.ru_idrss,
+ ru.ru_minflt,
+ ru.ru_majflt,
+ ru.ru_nswap,
+ ru.ru_inblock,
+ ru.ru_oublock,
+ ru.ru_msgsnd,
+ ru.ru_msgrcv,
+ ru.ru_nsignals,
+ ru.ru_nvcsw,
+ ru.ru_nivcsw,
+ gline);
+
+ fflush(flog);
+}
+#endif
+
+
+static void
+reaper()
+{
+ while (waitpid(-1, NULL, WNOHANG | WUNTRACED) > 0)
+ ;
+}
+
+
+static void
+sig_trap(sig)
+ int sig;
+{
+ char buf[80];
+
+ sprintf(buf, "signal [%d] at line %d (errno: %d)", sig, gline, errno);
+ logit("EXIT", buf);
+ fclose(flog);
+ exit(1);
+}
+
+
+static void
+sig_over()
+{
+ int fd;
+
+ server_usage();
+ logit("OVER", "");
+ fclose(flog);
+ for (fd = 0; fd < 64; fd++)
+ close(fd);
+ execl("bin/xchatd", NULL);
+}
+
+
+static void
+main_signals()
+{
+ struct sigaction act;
+
+ /* sigblock(sigmask(SIGPIPE)); */
+ /* Thor.981206: ²Î¤@ POSIX ¼Ð·Ç¥Îªk */
+
+ /* act.sa_mask = 0; */ /* Thor.981105: ¼Ð·Ç¥Îªk */
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+
+ act.sa_handler = sig_trap;
+ sigaction(SIGBUS, &act, NULL);
+ sigaction(SIGSEGV, &act, NULL);
+ sigaction(SIGTERM, &act, NULL);
+
+ act.sa_handler = sig_over;
+ sigaction(SIGXCPU, &act, NULL);
+
+ act.sa_handler = reaper;
+ sigaction(SIGCHLD, &act, NULL);
+
+#ifdef SERVER_USAGE
+ act.sa_handler = server_usage;
+ sigaction(SIGPROF, &act, NULL);
+#endif
+
+ /* Thor.981206: lkchu patch: ²Î¤@ POSIX ¼Ð·Ç¥Îªk */
+ /* ¦b¦¹­É¥Î sigset_t act.sa_mask */
+ sigaddset(&act.sa_mask, SIGPIPE);
+ sigprocmask(SIG_BLOCK, &act.sa_mask, NULL);
+
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int sock, nfds, maxfds, servo_sno;
+ ChatUser *cu,/* *userpool,*/ **FBI;
+ time_t uptime, tcheck;
+ fd_set rset, xset;
+ static struct timeval tv = {CHAT_INTERVAL, 0};
+ struct timeval tv_tmp; /* Thor.981206: for future reservation bug */
+
+ sock = 0;
+
+ while ((nfds = getopt(argc, argv, "hid")) != -1)
+ {
+ switch (nfds)
+ {
+ case 'i':
+ sock = 1;
+ break;
+
+ case 'd':
+ break;
+
+ case 'h':
+ default:
+
+ fprintf(stderr, "Usage: %s [options]\n"
+ "\t-i start from inetd with wait option\n"
+ "\t-d debug mode\n"
+ "\t-h help\n",
+ argv[0]);
+ exit(0);
+ }
+ }
+
+ servo_daemon(sock);
+ /* start_daemon(argc); */
+
+ setgid(BBSGID);
+ setuid(BBSUID);
+ chdir(BBSHOME);
+ umask(077);
+
+ log_init();
+
+ main_signals();
+
+ /* --------------------------------------------------- */
+ /* init variable : rooms & users */
+ /* --------------------------------------------------- */
+
+ userpool = NULL;
+ strcpy(mainroom.name, MAIN_NAME);
+ strcpy(mainroom.topic, MAIN_TOPIC);
+
+ /* --------------------------------------------------- */
+ /* main loop */
+ /* --------------------------------------------------- */
+
+ tcheck = 0;
+ servo_sno = 0;
+
+ for (;;)
+ {
+ uptime = time(0);
+ if (tcheck < uptime)
+ {
+ nfds = maxfds = 0;
+ FD_ZERO(&mainfset);
+ FD_SET(0, &mainfset);
+
+ tcheck = uptime - CHAT_INTERVAL;
+
+ for (FBI = &mainuser; cu = *FBI;)
+ {
+ if (cu->uptime < tcheck)
+ {
+ cuser_free(cu);
+
+ *FBI = cu->unext;
+
+ cu->unext = userpool;
+ userpool = cu;
+ }
+ else
+ {
+ nfds++;
+ sock = cu->sock;
+ FD_SET(sock, &mainfset);
+ if (maxfds < sock)
+ maxfds = sock;
+
+ FBI = &(cu->unext);
+ }
+ }
+
+ totaluser = nfds;
+ fprintf(flog, "MAINTAIN %d user (%d)\n", nfds, maxfds++);
+ fflush(flog);
+
+ tcheck = uptime + CHAT_INTERVAL;
+ }
+
+ /* ------------------------------------------------- */
+ /* Set up the fdsets */
+ /* ------------------------------------------------- */
+
+ rset = mainfset;
+ xset = mainfset;
+
+ /* Thor.981206: for future reservation bug */
+ tv_tmp = tv;
+ nfds = select(maxfds, &rset, NULL, &xset, &tv_tmp);
+
+#if 0
+ {
+ char buf[32];
+ static int xxx;
+
+ if ((++xxx & 8191) == 0)
+ {
+ sprintf(buf, "%d/%d", nfds, maxfds);
+ logit("MAIN", buf);
+ }
+ }
+#endif
+
+ if (nfds == 0)
+ {
+ continue;
+ }
+
+ if (nfds < 0)
+ {
+ sock = errno;
+ if (sock != EINTR)
+ {
+ logit("select", strerror(sock));
+ }
+ continue;
+ }
+
+ /* ------------------------------------------------- */
+ /* serve active agents */
+ /* ------------------------------------------------- */
+
+ uptime = time(0);
+
+ for (FBI = &mainuser; cu = *FBI;)
+ {
+ sock = cu->sock;
+
+ if (FD_ISSET(sock, &rset))
+ {
+ static int xxx, xno;
+
+ nfds = cuser_serve(cu);
+
+ if ((++xxx & 511) == 0)
+ {
+ int sno;
+
+ sno = cu->sno;
+ fprintf(flog, "rset\t[%d] S%d R%d %d\n", sno, sock, nfds, xxx);
+ if (sno == xno)
+ nfds = -1;
+ else
+ xno = sno;
+ }
+ }
+ else if (FD_ISSET(sock, &xset))
+ {
+ nfds = -1;
+ }
+ else
+ {
+ nfds = 0;
+ }
+
+ if (nfds < 0 || cu->uptime <= 0) /* free this client */
+ {
+ cuser_free(cu);
+
+ *FBI = cu->unext;
+
+ cu->unext = userpool;
+ userpool = cu;
+
+ continue;
+ }
+
+ if (nfds > 0)
+ {
+ cu->uptime = uptime;
+ }
+
+ FBI = &(cu->unext);
+ }
+
+ /* ------------------------------------------------- */
+ /* accept new connection */
+ /* ------------------------------------------------- */
+
+ if (FD_ISSET(0, &rset))
+ {
+
+ {
+ static int yyy;
+
+ if ((++yyy & 2047) == 0)
+ fprintf(flog, "conn\t%d\n", yyy);
+ }
+
+ for (;;)
+ {
+ int value;
+ struct sockaddr_in sin;
+
+ value = sizeof(sin);
+ sock = accept(0, (struct sockaddr *) &sin, &value);
+ if (sock > 0)
+ {
+ if (cu = userpool)
+ {
+ userpool = cu->unext;
+ }
+ else
+ {
+ cu = (ChatUser *) malloc(sizeof(ChatUser));
+ }
+
+ *FBI = cu;
+
+ /* variable initialization */
+
+ memset(cu, 0, sizeof(ChatUser));
+ cu->sock = sock;
+ cu->tbegin = uptime;
+ cu->uptime = uptime;
+ cu->sno = ++servo_sno;
+ cu->xdata = 0;
+ cu->retry = 0;
+ memcpy(cu->rhost, &sin.sin_addr, sizeof(struct in_addr));
+
+ totaluser++;
+
+ FD_SET(sock, &mainfset);
+ if (sock >= maxfds)
+ maxfds = sock + 1;
+
+ {
+ int value;
+
+ value = 1;
+ setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
+ (char *) &value, sizeof(value));
+ }
+
+#if 1
+ fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, 0) | O_NDELAY);
+#endif
+
+ fprintf(flog, "CONN\t[%d] %d %s\n",
+ servo_sno, sock, Btime(&cu->tbegin));
+ break;
+ }
+
+ nfds = errno;
+ if (nfds != EINTR)
+ {
+ logit("accept", strerror(nfds));
+ break;
+ }
+
+#if 0
+ while (waitpid(-1, NULL, WNOHANG | WUNTRACED) > 0);
+#endif
+ }
+ }
+
+ /* ------------------------------------------------- */
+ /* tail of main loop */
+ /* ------------------------------------------------- */
+
+ }
+}
diff --git a/game/Makefile b/game/Makefile
new file mode 100644
index 0000000..a2c5848
--- /dev/null
+++ b/game/Makefile
@@ -0,0 +1,64 @@
+# ------------------------------------------------------ #
+# Makefile ( NTHU CS MapleBBS Ver 2.36 ) #
+# ------------------------------------------------------ #
+# author : opus.bbs@bbs.cs.nthu.edu.tw #
+# target : Makefile for MapleBBS main programs #
+# create : 95/03/29 #
+# update : 95/12/15 #
+# ------------------------------------------------------ #
+
+
+# ------------------------------------------------------ #
+# ¤U¦Cªº make rules ¤£»Ý­×§ï #
+# ------------------------------------------------------ #
+
+
+SO = bar.so bingo.so bj.so bwboard.so chessmj.so dice.so dragon.so dict.so fantan.so \
+ gp.so gray.so guessnum.so km.so liteon.so marie.so mine.so nine.so \
+ pushbox.so race.so recall.so seven.so tetris.so
+
+
+.SUFFIXES:
+.SUFFIXES: .c .o .so
+
+.c.o: ; $(CC) $(CFLAGS) -c $*.c
+.o.so: ; ld -s -G $*.o -o $*.so -L../lib -ldao
+
+
+all:
+ @echo "Please enter 'make sys-type', "
+ @echo " make sun : for Sun-OS 4.x and maybe some BSD systems, cc or gcc"
+ @echo " make linux : for Linux"
+ @echo " make solaris : for Sun-OS 5.x gcc"
+ @echo " make sol-x86 : for Solaris 7 x86"
+ @echo " make freebsd : for BSD 4.4 systems"
+ @echo " make bsd : for BSD systems, cc or gcc, if not in the above lists"
+ @echo " make cygwin : for Microsoft Windows and Cygwin gcc"
+
+sun:
+ @$(MAKE) CC=gcc CFLAGS="-O2 -pipe -fomit-frame-pointer -Wunused -I../include" $(SO)
+
+linux:
+ @$(MAKE) CC=gcc CFLAGS="-DLINUX -O2 -pipe -fomit-frame-pointer -Wunused -I../include" $(SO)
+
+solaris:
+ @$(MAKE) CC=gcc CFLAGS="-DSOLARIS -DSYSV -O2 -pipe -fomit-frame-pointer -Wunused -I../include" $(SO)
+
+sol-x86:
+ @$(MAKE) CC=gcc CFLAGS="-DSOLARIS -DSYSV -O2 -fomit-frame-pointer -Wunused -I../include" $(SO)
+
+freebsd:
+ @$(MAKE) CC=gcc CFLAGS="-DBSD44 -O2 -pipe -fomit-frame-pointer -Wunused -I../include" $(SO)
+
+bsd:
+ @$(MAKE) CC=gcc CFLAGS="-DBSD44 -O2 -pipe -fomit-frame-pointer -Wunused -I../include" $(SO)
+
+cygwin:
+ @cd ../maple; make cygwin
+
+
+install: $(SO)
+ install -m 0700 $? $(HOME)/bin
+
+clean:
+ rm -f *.so *.o *~
diff --git a/game/bar.c b/game/bar.c
new file mode 100644
index 0000000..aeb4a26
--- /dev/null
+++ b/game/bar.c
@@ -0,0 +1,387 @@
+/*-------------------------------------------------------*/
+/* bar.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : BAR ¥x¹CÀ¸ */
+/* create : / / */
+/* update : 01/04/27 */
+/* author : unknown */
+/* recast : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+#ifdef HAVE_GAME
+
+
+#if 0
+ ¢~¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢¡
+ ¢x¡ñ¢x£Y¢x¡º¢x¡À¢x¡¸¢x77¢x¡ð¢x¡ñ¢x¡µ¢x¡º¢x£Y¢x£[¢x¡º¢x77¢x¡¸¢x¡ñ¢x¡ð¢x¡º¢x
+ ¢u¢w¢q¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢q¢w¢t
+ ¢x£Y¢x ¢x¡À¢x
+ ¢u¢w¢t ¢u¢w¢t
+ ¢x¡µ¢x ¢x¡µ¢x
+ ¢u¢w¢t ¢u¢w¢t
+ ¢x£[¢x ¢x£[¢x
+ ¢u¢w¢t ¢u¢w¢t
+ ¢x£Y¢x ¢x¡ð¢x
+ ¢u¢w¢t ¢u¢w¢t
+ ¢x77¢x ¢x¡¸¢x
+ ¢u¢w¢t ùþ¤j ¡¼¤p ¢u¢w¢t
+ ¢x¡º¢x ¢x£Y¢x
+ ¢u¢w¢q¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢q¢w¢t
+ ¢x¡À¢x¡ð¢x77¢x£Y¢x¡º¢x¡µ¢x¡ñ¢x¡¸¢x¡ð¢x£Y¢x¡À¢x£[¢x¡º¢x77¢x£Y¢x¡ñ¢x¡µ¢x77¢x
+ ¢¢¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢£
+#endif
+
+static char *itemlist[10] = {" ", "£[", "¡ð", "¡ñ", "¡À", "¡µ", "¡¸", "77", "¡º", "£Y"}; /* ¶µ¥Ø¦W */
+
+static int bar[49] = /* ¤W¹Ï 48 ®æ¤¤(¥Ñ¥ª¤W"¡ñ"¶}©l¶¶®É°w¶)©Ò¹ïÀ³ªº itemlist[] */
+{
+ 0, 3, 9, 8, 4, 6, 7, 2, 3, 5,
+ 8, 9, 1, 8, 7, 6, 3, 2, 8, 4,
+ 5, 1, 2, 6, 9, 7, 5, 3, 9, 7,
+ 8, 1, 4, 9, 2, 6, 3, 5, 8, 9,
+ 7, 2, 4, 8, 7, 9, 1, 5, 9
+}; /* ªO­±¦ì§} */
+
+static int money[10]; /* ©ãª÷ */
+
+
+static int
+total_money() /* ¬O§_¦³¤Uª` */
+{
+ if (money[1] || money[2] || money[3] || money[4] || money[5] ||
+ money[6] || money[7] || money[8] || money[9])
+ {
+ return 1;
+ }
+ return 0;
+}
+
+
+static int
+run(step, last, freq)
+ int step; /* ¦ì¸m */
+ int last; /* 1: ³Ì«á¤@¨B 0: ¤¤¶¡¨B */
+ int freq; /* frequency: Hz */
+{
+ int x1, y1, x2, y2;
+
+ if (step == 1)
+ {
+ x1 = 6;
+ y1 = 4;
+ x2 = 4;
+ y2 = 4;
+ }
+
+ else if (step > 1 && step < 19)
+ {
+ x1 = 4;
+ y1 = 4 * step - 4;
+ x2 = 4;
+ y2 = 4 * step;
+ }
+
+ else if (step > 18 && step < 26)
+ {
+ x1 = 2 * step - 34;
+ y1 = 72;
+ x2 = 2 * step - 32;
+ y2 = 72;
+ }
+
+ else if (step > 25 && step < 43)
+ {
+ x1 = 18;
+ y1 = 176 - 4 * step;
+ x2 = 18;
+ y2 = 172 - 4 * step;
+ }
+
+ else if (step > 42 && step < 49)
+ {
+ x1 = 104 - 2 * step;
+ y1 = 4;
+ x2 = 102 - 2 * step;
+ y2 = 4;
+ }
+
+ move(x1, y1);
+ if (step == 1)
+ outs(itemlist[9]);
+ else
+ outs(itemlist[bar[step - 1]]);
+
+ move(x2, y2);
+ if (last)
+ outs(itemlist[bar[step]]);
+ else
+ outs("ùþ");
+
+ refresh();
+ usleep(1000000 / freq); /* µ¥«Ý */
+}
+
+
+static inline int
+get_item() /* ¶Ã¼Æ¿ï¨ú­n¤¤ªº¶µ¥Ø */
+{
+
+#if 0
+½s¸¹¡G 1 2 3 4 5 6 7 8 9
+¶µ¥Ø¡G "£[", "¡ð", "¡ñ", "¡À", "¡µ", "¡¸", "77", "¡º", "£Y"
+¾÷²v¡G 13 16 21 26 32 43 64 127 308 /650
+½ß²v¡G 50 40 30 25 20 15 10 5 2
+#endif
+
+ int randnum = rnd(650); /* ½ß²v * ¾÷²v = ´Á±æ­È (¨C©ã 1 ¤¸©Ò¦^¦¬ªºª÷ÃB) */
+
+ if (randnum < 308) /* 2 * 308 / 650 = 0.948 */
+ return 9;
+ if (randnum < 435) /* 5 * 127 / 650 = 0.978 */
+ return 8;
+ if (randnum < 499) /* 10 * 64 / 650 = 0.985 */
+ return 7;
+ if (randnum < 542) /* 15 * 43 / 650 = 0.992 */
+ return 6;
+ if (randnum < 574) /* 20 * 32 / 650 = 0.985 */
+ return 5;
+ if (randnum < 600) /* 25 * 26 / 650 = 1.000 */
+ return 4;
+ if (randnum < 621) /* 30 * 21 / 650 = 0.969 */
+ return 3;
+ if (randnum < 637) /* 40 * 16 / 650 = 0.985 */
+ return 2;
+
+ return 1; /* 50 * 13 / 650 = 1.000 */
+}
+
+
+static inline int /* ¶Ç¦^ map ¤Wªº 1~48 ¨ä¤¤¤@­Ó¼Æ¦r */
+get_dst(item) /* ¶Ç¤J¤¤ªº¶µ¥Ø¿ï¨ú³Ì«á°±¯dªº¦ì¸m */
+ int item;
+{
+ int dst, randnum;
+
+ randnum = rnd(48) + 1; /* ±q¤¤¶¡©¹«e«á¿ï¨ú¡AÁ×§K¨C¦¸³£±q 1 ¿ï·|³£¤¤¥k¤W¨¤ªº³¡¤À */
+
+ for (dst = randnum; dst <= 48; dst++)
+ {
+ if (bar[dst] == item)
+ return dst;
+ }
+
+ for (dst = randnum; dst >= 1; dst--)
+ {
+ if (bar[dst] == item)
+ return dst;
+ }
+}
+
+
+static inline void
+print_total()
+{
+ outs("\n\n");
+ outs(" ¢~¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢¡\n");
+ outs(" ¢x¡ñ¢x£Y¢x¡º¢x¡À¢x¡¸¢x77¢x¡ð¢x¡ñ¢x¡µ¢x¡º¢x£Y¢x£[¢x£Y¢x77¢x¡¸¢x¡ñ¢x¡ð¢x¡º¢x\n");
+ outs(" ¢u¢w¢q¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢q¢w¢t\n");
+ outs(" ¢x£Y¢x ¢x¡À¢x\n");
+ outs(" ¢u¢w¢t ¢u¢w¢t\n");
+ outs(" ¢x¡µ¢x ¢x¡µ¢x\n");
+ outs(" ¢u¢w¢t ¢u¢w¢t\n");
+ outs(" ¢x£[¢x ¢x£[¢x\n");
+ outs(" ¢u¢w¢t ¢u¢w¢t\n");
+ outs(" ¢x£Y¢x ¢x¡ð¢x\n");
+ outs(" ¢u¢w¢t ¢u¢w¢t\n");
+ outs(" ¢x77¢x ¢x¡¸¢x\n");
+ outs(" ¢u¢w¢t ùþ¤j ¡¼¤p ¢u¢w¢t\n");
+ outs(" ¢x¡º¢x ¢x£Y¢x\n");
+ outs(" ¢u¢w¢q¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢q¢w¢t\n");
+ outs(" ¢x¡À¢x¡ð¢x77¢x£Y¢x¡º¢x¡µ¢x¡ñ¢x¡¸¢x¡ð¢x£Y¢x¡À¢x£[¢x¡º¢x77¢x£Y¢x¡ñ¢x¡µ¢x77¢x\n");
+ outs(" ¢¢¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢£\n");
+ outs(" \033[1;31m¢x£[¢x \033[32m¢x¡ð¢x \033[33m¢x¡ñ¢x \033[34m¢x¡À¢x \033[35m¢x¡µ¢x \033[36m¢x¡¸¢x \033[37m¢x77¢x \033[0;36m¢x¡º¢x \033[33m¢x£Y¢x \033[m\n");
+ outs(" \033[1;31m¢x50¢x \033[32m¢x40¢x \033[33m¢x30¢x \033[34m¢x25¢x \033[35m¢x20¢x \033[36m¢x15¢x \033[37m¢x10¢x \033[0;36m¢x 5¢x \033[33m¢x 2¢x \033[m");
+}
+
+
+int
+main_bar()
+{
+ int price[10] = {0, 50, 40, 30, 25, 20, 15, 10, 5, 2}; /* ­¿²v */
+
+ int item; /* ¶µ¥Ø½s¸¹ */
+ int ogn, dst; /* OriGiN: °_ÂI DeStinaTion: ²×ÂI */
+ int ch, i, j;
+ char buf[80];
+
+ if (HAS_STATUS(STATUS_COINLOCK))
+ {
+ vmsg(msg_coinlock);
+ return XEASY;
+ }
+
+ vs_bar("BAR ¥x");
+ print_total(); /* ¦LªO­± */
+ ogn = 1; /* °_ÂI¦b²Ä¤@®æ */
+
+ while (1)
+ {
+ for (i = 1; i < 10; i++) /* money Âk¹s */
+ money[i] = 0;
+
+ for (;;)
+ {
+ /* ¨M©w¦U¶µ½äª` */
+
+ ch = vget(2, 0, "±z­n©ã­þ¶µ(1-9)¡H[S]¶}©l [Q]Â÷¶}¡G", buf, 3, DOECHO);
+ if (!ch || ch == 's')
+ {
+ if (total_money())
+ break;
+ addmoney(money[1] + money[2] + money[3] + money[4] + money[5] + money[6] + money[7] + money[8] + money[9]); /* ÁÙ¿ú */
+ goto abort_game;
+ }
+ else if (ch < '1' || ch > '9')
+ {
+ addmoney(money[1] + money[2] + money[3] + money[4] + money[5] + money[6] + money[7] + money[8] + money[9]); /* ÁÙ¿ú */
+ goto abort_game;
+ }
+
+ if (!(vget(2, 0, "­n©ã¦h¤Ö½äª÷¡H", buf, 6, DOECHO)))
+ {
+ if (total_money())
+ break;
+ addmoney(money[1] + money[2] + money[3] + money[4] + money[5] + money[6] + money[7] + money[8] + money[9]); /* ÁÙ¿ú */
+ goto abort_game;
+ }
+
+ j = atoi(buf);
+ if (j < 1 || j > cuser.money)
+ {
+ addmoney(money[1] + money[2] + money[3] + money[4] + money[5] + money[6] + money[7] + money[8] + money[9]); /* ÁÙ¿ú */
+ goto abort_game;
+ }
+
+ cuser.money -= j;
+ money[ch - '0'] += j;
+
+ move(b_lines - 1, 0);
+ clrtoeol();
+ outs("\033[1m ");
+ for (i = 1; i < 10; i++)
+ prints("\033[3%dm%6d ", i, money[i]);
+ prints("\n\033[m Äw½XÁÙ¦³ %d ¤¸ ", cuser.money);
+ }
+
+ /* ¶}©l¶]¤F */
+
+ item = get_item(); /* ¶Ã¼Æ¿ï¨ú¤¤ªº¶µ¥Ø */
+ dst = get_dst(item); /* ¥Ñ©Ò¤¤ªº¶µ¥Ø¨Ó¨M©w³Ì«á°±¯dªº¦ì¸m */
+
+ for (i = ogn; i <= 48; i++) /* ²Ä¤@°é */
+ {
+ run(i, 0, 10 + i / 10); /* °_©l¥[³t¡A¨C¬í 10 - 14 ¦¸¡A¶V¶]¶V§Ö */
+ }
+
+ for (j = 0; j < 2; j++) /* ¤¤¶¡¶]¤G°é´N¦n¤F */
+ {
+ for (i = 1; i < 49; i++) /* ¤¤¶¡ªº°é */
+ {
+ run(i, 0, 15); /* ¤¤¬q°ª³t¡Aºû«ù¨C¬í 15 ¦¸ */
+ }
+ }
+
+ for (i = 1; i <= dst; i++) /* ³Ì«á¤@°é */
+ {
+ run(i, 0, 14 - i / 10); /* ³Ì«á´î³t¡A¨C¬í 14 - 10 ¦¸ */
+ }
+
+ for (j = 0; j < 2; j++) /* ¦b³Ì«á°±¯dªº¦ì¸m°{¤G¦¸ */
+ {
+ run(dst, 0, 2);
+ run(dst, 1, 2);
+ }
+
+ move(2, 0);
+ clrtoeol();
+ prints("¤¤¼úªº¬O \033[37m%s\033[m¡A", itemlist[item]);
+
+ if (!money[item])
+ {
+ outs("Ý¢Àt +_+");
+ }
+ else
+ {
+ money[0] = money[item] * price[item];
+ prints("®¥³ß±z©ã¤¤¤F¡AÀò±o¼úª÷ \033[32m%d\033[m", money[0]);
+
+ for (;;) /* ¥i¥H¤@ª½¤ñ¤j¤p¤ñ¨ì²n */
+ {
+ sprintf(buf, "¥Ø«e¼úª÷: %d ±zÁÙ­n¤ñ¤j¤p¶Ü(Y/N)¡H[N] ", money[0]);
+
+ if (vans(buf) != 'y') /* ¤£¤ñ¤j¤p */
+ {
+ move(2, 0);
+ clrtoeol();
+ prints("±o¼úª÷ %d", money[0]);
+ break;
+ }
+ else /* ¤ñ¤j¤p */
+ {
+ sprintf(buf, "±z­n©ã¤°»ò¡H [1]¤j (2)¤p ");
+ ch = vans(buf) - '1'; /* ch = 0:¤j 1:¤p */
+
+ for (i = 16; i <= 20; i++)
+ {
+ move(15, 30);
+ outs("ùþ¤j ¡¼¤p");
+ refresh();
+ usleep(6000 * (i ^ 2));
+
+ move(15, 30);
+ outs("¡¼¤j ùþ¤p");
+ usleep(6000 * (i ^ 2));
+ refresh();
+ }
+
+ price[0] = rnd(2);
+ move(15, 30);
+ if (price[0])
+ outs("ùþ¤j ¡¼¤p");
+ else
+ outs("¡¼¤j ùþ¤p");
+
+ if (price[0] == ch)
+ {
+ money[0] *= 2;
+ move(2, 0);
+ clrtoeol();
+ outs("°Ú¡I©ã¤¤¤F¡I¼úª÷Åܦ¨¤G­¿¡I");
+ }
+ else
+ {
+ money[0] = 0;
+ move(2, 0);
+ clrtoeol();
+ outs("µª¿ù¤F¡I¹s¤À¡I");
+ break; /* ¤ñ¤j¤pµ²§ô */
+ }
+ }
+ }
+ addmoney(money[0]);
+ } /* ¤ñ¤j¤pµ²§ô */
+
+ vmsg("Ä~Äò¤U¤@½L¤jÁÉ");
+ move(b_lines, 0);
+ clrtoeol(); /* ²M°£½Ð«ö¥ô·NÁäÄ~Äò */
+
+ ogn = dst; /* ¤U¦¸°_ÂI¬O¤W¦¸ªº²×ÂI */
+ }
+
+abort_game:
+ return 0;
+}
+#endif /* HAVE_GAME */
diff --git a/game/bingo.c b/game/bingo.c
new file mode 100644
index 0000000..e22ec8e
--- /dev/null
+++ b/game/bingo.c
@@ -0,0 +1,186 @@
+/*-------------------------------------------------------*/
+/* bingo.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : »«ªG¹CÀ¸ */
+/* create : / / */
+/* update : 01/04/21 */
+/* author : unknown */
+/* recast : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+#ifdef HAVE_GAME
+
+
+static void
+out_song()
+{
+ static int count = 0;
+
+ /* ±i¾Ç¤Í & °ª¼z§g£»§A³Ì¬Ã¶Q */
+ uschar *msg[9] =
+ {
+ "©ú¦~³o­Ó®É¶¡ ¬ù¦b³o­Ó¦aÂI",
+ "°O±o±aµÛª´ºÀ ¥´¤W»â±aô¤W«ä©À",
+ "°Ê±¡®É¨è³Ì¬ü ¯u±¡ªºµ¹¤£²Ö",
+ "¤Ó¦hªº·R©È¾K ¨S¤H¯k·R¦A¬üªº¤H ¤]·|¼¬±|",
+ "§Ú·|°e§A¬õ¦âª´ºÀ §A§O®³¤@¥Í²´²\\¬Û¹ï",
+ "¥¼¨Óªº¤é¤l¦³§A¤~¬ü ¹Ú¤~·|¯u¤@ÂI",
+ "§Ú¾ÇµÛ ¦b§A·R¸Ì¨I¾K §A¦uÅ@µÛ§Ú¬ï¹L¶Â©]",
+ "§Ú¤£ºM°h §ÚÄ@·N",
+ "³o±ø±¡¸ô ¬Û¦u¬ÛÀH §A³Ì¬Ã¶Q"
+ };
+ move(b_lines - 2, 0);
+ prints("\033[1;3%dm%s\033[m Äw½XÁÙ¦³ %d ¤¸", time(0) % 7, msg[count], cuser.money);
+ clrtoeol();
+ if (++count == 9)
+ count = 0;
+}
+
+
+int
+main_bingo()
+{
+ /* 25­Ó {x§¤¼Ð, y§¤¼Ð, ­È} */
+ int place[5][5][3] =
+ {
+ {{3, 2, 0}, {3, 6, 0}, {3, 10, 0}, {3, 14, 0}, {3, 18, 0}},
+ {{5, 2, 0}, {5, 6, 0}, {5, 10, 0}, {5, 14, 0}, {5, 18, 0}},
+ {{7, 2, 0}, {7, 6, 0}, {7, 10, 0}, {7, 14, 0}, {7, 18, 0}},
+ {{9, 2, 0}, {9, 6, 0}, {9, 10, 0}, {9, 14, 0}, {9, 18, 0}},
+ {{11, 2, 0}, {11, 6, 0}, {11, 10, 0}, {11, 14, 0}, {11, 18, 0}}
+ };
+
+ /* ½ß²v : ²q 5 ¦¸´N¦¨¥\½ß 20 ­¿¡A²q 6 ¦¸´N¦¨¥\½ß 15 ­¿ ... ¦Ü¤Ö­n²q 5 ¦¸¤~¥i¯à¦¨¥\ */
+ int rate[13 + 1] = {0, 1, 2, 3, 5, 7, 10, 15, 20, 0, 0, 0, 0, 0};
+
+ char used[25]; /* 25 ­Ó¼Æ¦r¬O§_¥Î¹L */
+ int money; /* ©ãª÷ */
+ int account; /* ÁÙ¦³´X¦¸¥i²q */
+ int success; /* ³s¦¨¤@ª½½u¤F¶Ü */
+
+ int row, col, i;
+ char buf[60];
+
+ if (HAS_STATUS(STATUS_COINLOCK))
+ {
+ vmsg(msg_coinlock);
+ return XEASY;
+ }
+
+ while (1)
+ {
+ vs_bar("»«ªG¤j¾Ô");
+ out_song();
+
+ vget(2, 0, "½Ð°Ý­n¤Uª`¦h¤Ö©O¡H(1 ~ 50000) ", buf, 6, DOECHO);
+ money = atoi(buf);
+ if (money < 1 || money > 50000 || money > cuser.money)
+ break; /* Â÷¶}½ä³õ */
+ cuser.money -= money;
+
+ /* initialize */
+
+ for (i = 0; i < 25; i++) /* 25 ­Ó¼Æ¦r³£ÁÙ¨S³Q¿ï */
+ used[i] = 0;
+
+ for (row = 0; row < 5; row++) /* ´Ñ½Lªº 25 ®æ¥ýÂk¹s */
+ {
+ for (col = 0; col < 5; col++)
+ place[row][col][2] = 0;
+ }
+
+ /* µe´Ñ½L */
+
+ move(4, 0);
+ for (i = 0; i < 5; i++)
+ {
+ outs(" \033[1;44m \033[m\n"
+ " \033[44m \033[42m \033[44m \033[42m \033[44m \033[42m "
+ "\033[44m \033[42m \033[44m \033[42m \033[44m \033[m\n");
+ }
+ outs(" \033[44m \033[m\n");
+
+ /* game start */
+
+ for (account = 13, success = 0; account && !success; account--)
+ {
+ move(b_lines - 7, 0);
+ outs("\033[1;37;44m©|¥¼¶}¥Xªº¸¹½X\033[m\n");
+ for (i = 1; i <= 25; i++)
+ {
+ if (!used[i - 1])
+ prints(" %2d", i);
+ }
+
+ prints("\n©|¦³\033[1;33;41m %2d \033[m¦¸¾÷·|¥i²q ¤U¦¸²q¤¤¥i±o\033[1;37;44m %d \033[m­¿\n",
+ account, rate[account]);
+
+ do
+ {
+ vget(b_lines - 4, 0, "½Ð¿é¤J±zªº¸¹½X¡G", buf, 3, DOECHO);
+ i = atoi(buf);
+ } while (i <= 0 || i > 25 || used[i - 1]);
+
+ /* ¨Ã¤£¬O¤@¶}©l´N¨M©w´Ñ½L¤W 25 ­Ó¦ì¸mªº­È¡A¦Ó¬O user ¨C²q¤@­Ó¼Æ¦r
+ ¦A¥h¨M©w­n§â³o­Ó¼Æ¦r©ñ¦b´Ñ½L¤Wªº­þ­Ó¦ì¸m */
+ do
+ {
+ row = rnd(5);
+ col = rnd(5);
+ } while (place[row][col][2]);
+
+ place[row][col][2] = i;
+ used[i - 1] = 1;
+
+ /* §â·sªºÂI¨º¾ã¦Cµe¤W´Ñ½L */
+
+ move(5 + row * 2, 0);
+ clrtoeol();
+ outs(" \033[1m");
+ for (i = 0; i < 5; i++)
+ {
+ outs("\033[44m ");
+ if (place[row][i][2])
+ prints("\033[40m%2d", place[row][i][2]);
+ else
+ outs("\033[42m ");
+ }
+ outs("\033[44m \033[m\n");
+
+ if (account >= 13 - 5) /* ¦Ü¤Ö­n²q 5 ¦¸«á¤~»Ý­nÀˬd¬O§_³s¦¨¤@ª½½u */
+ continue;
+
+ /* Àˬd¬O§_³s¦¨¤@ª½½u */
+ /* ­Y¦b¤G±ø¹ï¨¤½u¤W¡A­n¯S§OÀˬd¹ï¨¤½u¡A§_«h¥u­nÀˬd¸Ó¦æ¦C§Y¥i */
+
+ if ((row == col && place[0][0][2] && place[1][1][2] && place[2][2][2] && place[3][3][2] && place[4][4][2]) ||
+ (row + col == 4 && place[0][4][2] && place[1][3][2] && place[2][2][2] && place[3][1][2] && place[4][0][2]) ||
+ (place[row][0][2] && place[row][1][2] && place[row][2][2] && place[row][3][2] && place[row][4][2]) ||
+ (place[0][col][2] && place[1][col][2] && place[2][col][2] && place[3][col][2] && place[4][col][2]))
+ {
+ success = 1;
+ break;
+ }
+
+ } /* for °j°éµ²§ô¡A¥i¯à¬O²q§¹ 13 ¦¸©Î¬O¦¨¥\¤F */
+
+ if (success)
+ {
+ money *= rate[account];
+ sprintf(buf, "®¥³ß±z...ŤF %d ¤¸ ", money);
+ addmoney(money);
+ }
+ else
+ {
+ strcpy(buf, "¹B®ð¤£¨Î...¦A¨Ó¤@½L§a¡I");
+ }
+ vmsg(buf);
+
+ } /* while °j°éµ²§ô¡AÂ÷¶}¹CÀ¸ */
+
+ return 0;
+}
+#endif /* HAVE_GAME */
diff --git a/game/bj.c b/game/bj.c
new file mode 100644
index 0000000..9fe2eb1
--- /dev/null
+++ b/game/bj.c
@@ -0,0 +1,389 @@
+/*-------------------------------------------------------*/
+/* bj.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : ¶Â³Ç§J¤G¤Q¤@ÂI¹CÀ¸ */
+/* create : / / */
+/* update : 01/04/23 */
+/* author : unknown */
+/* recast : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+
+#ifdef HAVE_GAME
+
+
+enum
+{ /* ­¿²v */
+
+ SEVEN = 5, /* 777 */
+ SUPERAJ = 5, /* spade A+J */
+ AJ = 4, /* A+J */
+ FIVE = 3, /* ¹L¤­Ãö */
+ JACK = 3, /* ¬°«e¨â±i´N 21 ÂI */
+ WIN = 2 /* Ĺ */
+};
+
+
+static char *flower[4] = {"¢á", "¢Ö", "¢Ò", "¢Ñ"};
+static char *poker[53] =
+{
+ "¢Ï", "¢Ï", "¢Ï", "¢Ï", "¢±", "¢±", "¢±", "¢±", "¢²", "¢²", "¢²", "¢²",
+ "¢³", "¢³", "¢³", "¢³", "¢´", "¢´", "¢´", "¢´", "¢µ", "¢µ", "¢µ", "¢µ",
+ "¢¶", "¢¶", "¢¶", "¢¶", "¢·", "¢·", "¢·", "¢·", "¢¸", "¢¸", "¢¸", "¢¸",
+ "¢â", "¢â", "¢â", "¢â", "¢Ø", "¢Ø", "¢Ø", "¢Ø", "¢ß", "¢ß", "¢ß", "¢ß",
+ "¢Ù", "¢Ù", "¢Ù", "¢Ù", " "
+};
+
+
+static void
+out_song()
+{
+ static int count = 0;
+
+ /* ³\¯øªå£»·R¥u³Ñ¤@¬í */
+ uschar *msg[9] =
+ {
+ "½t¥÷§Ö·À¤F ²´«e¬O®ü¨¤ ÁÙ©¹«e¸õ ¤âÀ¹¤W¤â¾R",
+ "¤£·QÅý¾Ö©ê¡@¦A©ñ±¼ §A¤@±Ã¤ã §Ú´Nµh¨ì ©I§l¤£¤F",
+ "·R´¿¨º»ò¦n ¤£¯à¨ì¦Ñ §A¦ó­W¡@³s°²À¸¯u§@§A³£°µ¤£¨ì",
+ "²´¬Ý ©¯ºÖ¥u³Ñ¤@¬í §AÁÙ¸¨²\\¨DÄÇ",
+ "­n¨D§Ú©ñ±¼¡@¤£ª¾¸Ó»¡¤°»ò¤~¦n",
+ "¦Û¤v²`·Rªº¤H ³º¹³¤p«Ä¡@µL²z¨ú¾x",
+ "¥Í©R¡@¦pªG¥u³Ñ¤@¬í §Ú¥u·Q­n§ë¾a",
+ "¦º¦b§AÃh©ê ¹Ú¸Ì¦Û´M¤Ñ¯î¦a¦Ñ¡@¹j¥@§â¤ß¸®±¼",
+ "§Aªººp·N¡@¦A¶Ë¤£¤F¡@§Úªº¦n"
+ };
+ move(b_lines - 2, 0);
+ prints("\033[1;3%dm%s\033[m Äw½XÁÙ¦³ %d ¤¸", time(0) % 7, msg[count], cuser.money);
+ clrtoeol();
+ if (++count == 9)
+ count = 0;
+}
+
+
+static void
+print_card(int card, int x, int y)
+{
+
+ move(x, y);
+ outs("¢~¢w¢w¢w¢¡");
+ move(x + 1, y);
+ prints("¢x%s ¢x", poker[card]);
+ move(x + 2, y);
+ prints("¢x%s ¢x", card != 52 ? flower[card % 4] : " ");
+ move(x + 3, y);
+ outs("¢x ¢x");
+ move(x + 4, y);
+ outs("¢x ¢x");
+ move(x + 5, y);
+ outs("¢x ¢x");
+ move(x + 6, y);
+ outs("¢¢¢w¢w¢w¢£");
+}
+
+
+static char
+get_newcard(mode)
+ int mode; /* 0:­«·s¬~µP 1:µoµP */
+{
+ static char card[10]; /* ³Ì¦h¥u·|¥Î¨ì 10 ±iµP */
+ static int now; /* µo¥X²Ä now ±iµP */
+ char num;
+ int i;
+
+ if (!mode) /* ­«·s¬~µP */
+ {
+ now = 0;
+ return -1;
+ }
+
+rand_num: /* random ¥X¤@±i©M¤§«e³£¤£¦PªºµP */
+ num = rnd(52);
+ for (i = 0; i < now; i++)
+ {
+ if (num == card[i]) /* ³o±iµP¥H«e random ¹L¤F */
+ goto rand_num;
+ }
+
+ card[now] = num;
+ now++;
+
+ return num;
+}
+
+
+int
+main_bj()
+{
+ int money; /* ©ãª÷ */
+
+ /* ¹q¸£¬O²ø®a */
+ char host_card[12]; /* ¹q¸£ªºµP±i */
+ char guest_card[12]; /* ª±®aªºµP±i */
+
+ int host_count; /* ¹q¸£®³¤F´X±iµP */
+ int guest_count; /* ª±®a®³¤F´X±iµP */
+
+ int host_A; /* ¹q¸£®³ A ªº±i¼Æ */
+ int guest_A; /* ª±®a®³ A ªº±i¼Æ */
+ int host_point; /* ¹q¸£®³ªºÁ`ÂI¼Æ */
+ int guest_point; /* ª±®a®³ªºÁ`ÂI¼Æ */
+
+ int doub; /* ¬O§_¤w¸g¥[­¿©ãª÷ */
+ int ch; /* «öÁä */
+
+ int card;
+ char buf[60];
+
+ int num[52] = /* ¦U±iµPªºÂI¼Æ */
+ {
+ 11, 11, 11, 11, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6,
+ 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10
+ };
+
+ if (HAS_STATUS(STATUS_COINLOCK))
+ {
+ vmsg(msg_coinlock);
+ return XEASY;
+ }
+
+ while (1)
+ {
+ vs_bar("¶Â³Ç§J¤j¾Ô");
+ out_song();
+
+ vget(2, 0, "½Ð°Ý­n¤Uª`¦h¤Ö©O¡H(1 ~ 50000) ", buf, 6, DOECHO);
+ money = atoi(buf);
+ if (money < 1 || money > 50000 || money > cuser.money)
+ break; /* Â÷¶}½ä³õ */
+
+ cuser.money -= money;
+ doub = 0;
+
+ move(3, 0);
+ outs("(«ö y ÄòµP, n ¤£ÄòµP, d double)");
+
+ out_song();
+
+ get_newcard(0); /* ¬~µP */
+
+ guest_card[0] = get_newcard(1);
+ guest_card[1] = get_newcard(1);
+ host_card[0] = get_newcard(1);
+ host_card[1] = get_newcard(1);
+
+ host_count = 2; /* ¦¹®É¡AÂù¤è¦U¦³¤G±iµP */
+ guest_count = 2;
+ host_A = 0; /* ¦¹®É¡AÂù¤è³£¨S¦³ Ace */
+ guest_A = 0;
+
+ /* Àˬd A ªº±i¼Æ¡A¨Ó¨M©w¬O§_¦³ Black Jack ©Î¬O§â A ·í 11 ÂI */
+
+ if (host_card[0] < 4)
+ host_A++;
+ if (host_card[1] < 4)
+ host_A++;
+ if (guest_card[0] < 4)
+ guest_A++;
+ if (guest_card[1] < 4)
+ guest_A++;
+
+ print_card(52, 5, 0); /* ¦L¥X¹q¸£ªº²Ä¤@±iµP(¦ýÅã¥ÜªÅ¥Õ) */
+ print_card(host_card[1], 5, 4); /* ¦L¥X¹q¸£ªº²Ä¤G±iµP */
+ print_card(guest_card[0], 14, 0); /* ¦L¥Xª±®aªº²Ä¤@±iµP */
+ print_card(guest_card[1], 14, 4); /* ¦L¥Xª±®aªº²Ä¤G±iµP */
+
+ host_point = num[host_card[1]]; /* ¹q¸£ªº²Ä¤@±iµP¼È®É¤£ºâÂI(¥H§K¼ÉÅSµª®×) */
+ guest_point = num[guest_card[0]] + num[guest_card[1]];
+
+ move(12, 0);
+ prints("\033[1;32mÂI¼Æ¡G\033[33m%2d\033[m", host_point);
+
+ for (;;)
+ {
+ /* ÀˬdµP«¬ */
+check_condition:
+
+ /* itoc.011025: ª±®a¨C¨ú¤@±iµP´N­n­«Ã¸¤@¦¸ÂI¼Æ */
+ move(13, 0);
+ prints("\033[1;35mÂI¼Æ¡G\033[36m%2d\033[m", guest_point);
+
+ if (guest_count == 3 && /* ­nÀˬd guest_count ¥H§K²Ä¤T±iµP¬O¤W¦¸µP */
+ (guest_card[0] >= 24 && guest_card[0] <= 27) &&
+ (guest_card[1] >= 24 && guest_card[1] <= 27) &&
+ (guest_card[2] >= 24 && guest_card[2] <= 27))
+ {
+ money *= SEVEN;
+ sprintf(buf, "\033[1;41;33m ¢¶¢¶¢¶ ±o¼úª÷ %d »È¨â \033[m", money);
+ goto next_game;
+ }
+
+ else if ((guest_card[0] == 40 && guest_card[1] == 0) ||
+ (guest_card[0] == 0 && guest_card[1] == 40))
+ {
+ money *= SUPERAJ;
+ sprintf(buf, "\033[1;41;33m¥¿²Î BLACK JACK ±o¼úª÷ %d »È¨â \033[m", money);
+ goto next_game;
+ }
+
+ else if (((guest_card[0] <= 3 && guest_card[0] >= 0) &&
+ (guest_card[1] <= 43 && guest_card[1] >= 40)) ||
+ ((guest_card[1] <= 3 && guest_card[1] >= 0) &&
+ (guest_card[0] <= 43 && guest_card[0] >= 40)))
+ {
+ money *= AJ;
+ sprintf(buf, "\033[1;41;33m BLACK JACK ±o¼úª÷ %d »È¨â \033[m", money);
+ goto next_game;
+ }
+
+ else if (guest_point == 21 && guest_count == 2) /* «e¨â±i´N 21 ÂI */
+ {
+ money *= JACK;
+ sprintf(buf, "\033[1;41;33m JACK ±o¼úª÷ %d »È¨â \033[m", money);
+ goto next_game;
+ }
+
+ else if (guest_count == 5 && guest_point <= 21)
+ {
+ money *= FIVE;
+ sprintf(buf, "\033[1;41;33m ¹L¤­Ãö ±o¼úª÷ %d »È¨â \033[m", money);
+ goto next_game;
+ }
+
+ else if (guest_point > 21 && guest_A)
+ {
+ guest_point -= 10;
+ guest_A--;
+ move(13, 0);
+ prints("\033[1;35mÂI¼Æ¡G\033[36m%2d\033[m", guest_point);
+ goto check_condition;
+ }
+
+ /* ¤@¯ëµP«¬ */
+
+ else
+ {
+ /* Ãz¤F */
+
+ if (guest_point > 21)
+ {
+ money = 0;
+ strcpy(buf, "\033[1;41;33m Ãz¤F ¿ú³Q¦Y¥ú¤F \033[m");
+ goto next_game;
+ }
+
+ /* ¨SÃz */
+
+ do
+ {
+ ch = igetch();
+
+ if (ch == 'd' && !doub && guest_count == 2) /* ¥u¯à¦b²Ä¤G±iµP¯à®É double */
+ {
+ doub = 1; /* ¤w double¡A¤£¯à¦A double */
+
+ if (cuser.money >= money)
+ {
+ cuser.money -= money;
+ money *= 2;
+ }
+ else
+ {
+ money += cuser.money; /* ¿ú¤£°÷¤G­¿¡A´N¥þ±ô¤F */
+ cuser.money = 0;
+ }
+ out_song();
+ }
+ } while (ch != 'y' && ch != 'n');
+
+ if (ch == 'y' && guest_point != 21) /* ª±®aÄòµP */
+ {
+ card = get_newcard(1);
+ guest_card[guest_count] = card;
+ if (card < 4)
+ guest_A++;
+
+ guest_point += num[card];
+ print_card(card, 14, 4 * guest_count);
+ guest_count++;
+ move(13, 0);
+ prints("\033[1;35mÂI¼Æ¡G\033[36m%2d\033[m", guest_point);
+ goto check_condition;
+ }
+ else /* ª±®a¤£ÄòµP */
+ {
+ /* ¥ýÅã¥Ü²ø®aªº²Ä¤@±iµP */
+ move(6, 2);
+ outs(poker[host_card[0]]);
+ move(7, 2);
+ outs(flower[host_card[0] % 4]);
+ host_point += num[host_card[0]]; /* §â­è¶}©l¨S¥[ªº²Ä¤@±iµPÂI¥[¦^¨Ó */
+
+ /* ½ü¨ì¹q¸£(²ø®a)¨úµP */
+
+ if (host_A == 2) /* ¯S¨Ò: ¹q¸£¤G±i©³µP«ê¦n³£¬O A ®É·|§PÂ_¿ù»~ */
+ {
+ /* §â¤@±i A ±q 11 ÂI´«¦¨ 1 ÂI */
+ host_point -= 10;
+ host_A--;
+ }
+
+ while (host_point < guest_point) /* ²ø®a¦pªGÂI¤ñ¸û¤Ö¡A´N±j­¢¨úµP */
+ {
+ card = get_newcard(1);
+ host_card[host_count] = card;
+ if (card < 4)
+ host_A++;
+
+ host_point += num[card];
+ print_card(card, 5, 4 * host_count);
+ host_count++;
+
+ if (host_point > 21)
+ {
+ if (host_A)
+ {
+ host_point -= 10;
+ host_A--;
+ continue; /* Ä~Äò¨ú¤U¤@±iµP */
+ }
+
+ move(12, 0);
+ prints("\033[1;32mÂI¼Æ¡G\033[33m%2d\033[m", host_point);
+
+ /* ¹q¸£(²ø®a)Ãz¤F */
+
+ money *= WIN;
+ sprintf(buf, "\033[1;41;33m WINNER ±o¼úª÷ %d »È¨â \033[m", money);
+ goto next_game;
+ }
+ }
+
+ /* ¹q¸£(²ø®a)¨úµPµ²§ô¡A¥B¨S¦³Ãz±¼¡A¹q¸£Àò³Ó */
+
+ move(12, 0);
+ prints("\033[1;32mÂI¼Æ¡G\033[33m%2d\033[m", host_point);
+
+ money = 0;
+ strcpy(buf, "\033[1;41;33m ¿é¤F ¿ú³Q¦Y¥ú¤F \033[m");
+ goto next_game;
+ }
+
+ } /* if ÀˬdµP«¬µ²§ô */
+
+ } /* for °j°éµ²§ô¡Aµ²§ô³o¦^¦X */
+
+next_game:
+ move(18, 3);
+ outs(buf);
+ addmoney(money);
+ vmsg(NULL);
+ } /* while °j°éµ²§ô¡AÂ÷¶}¹CÀ¸ */
+
+ return 0;
+}
+#endif /* HAVE_GAME */
diff --git a/game/bwboard.c b/game/bwboard.c
new file mode 100644
index 0000000..bb18d0e
--- /dev/null
+++ b/game/bwboard.c
@@ -0,0 +1,2408 @@
+/*-------------------------------------------------------*/
+/* bwboard.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : B/W & Chinese Chess Board */
+/* create : 02/08/05 */
+/* update : / / */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+#ifdef HAVE_GAME
+
+
+/*-------------------------------------------------------*/
+/* ©Ò¦³´Ñ½L¤½¥Îªº°Ñ¼Æ */
+/*-------------------------------------------------------*/
+
+/* lantw44: ±q ch_printmsg() ·h¥X¨Óªº¨ç¦¡«Å§i */
+
+static char *ch_brdline(int);
+
+extern char lastcmd[MAXLASTCMD][80];
+
+
+enum
+{
+ DISCONNECT = -2, LEAVE = -1, NOTHING = 0
+};
+
+
+enum
+{
+ Binit = 0, Bupdate = 1, Ballow = 2
+};
+
+
+enum
+{
+ Empty = 0, Black = 1, White = 2, Red = 2
+};
+
+
+static int msgline; /* Where to display message now */
+static int cfd; /* socket number */
+static int myColor; /* my chess color */
+static int Choose; /* -3:¶Â¥Õ´Ñ -2:¤­¤l´Ñ -1:³ò´Ñ 0:­x´Ñ 1:·t´Ñ */
+static int dark_choose; /* ·t´Ñ¬O§_¤w½¶}²Ä¤@¤l */
+
+static int (**rule) ();
+
+static int bwRow, bwCol;
+static char Board[19][19];
+static char forspace[61];
+
+
+static char *ruleStrSet[] = {"¶Â¥Õ´Ñ", "¤­¤l´Ñ", "³ò´Ñ", "­x´Ñ", "·t´Ñ"};
+static char *ruleStr;
+
+static KeyFunc *mapTalk, *mapTurn;
+
+static int cmdCol, cmdPos;
+static char talkBuf[42] = "T";
+static char *cmdBuf = &talkBuf[1];
+static KeyFunc Talk[];
+
+
+static void bw_printmsg();
+static void ch_printmsg();
+static void do_init();
+
+
+static int
+do_send(buf)
+ char *buf;
+{
+ int len;
+
+ len = strlen(buf) + 1; /* with trail 0 */
+ return (send(cfd, buf, len, 0) == len);
+}
+
+
+#ifdef EVERY_BIFF
+static void
+check_biff()
+{
+ /* Thor.980805: ¦³¤H¦b®ÇÃä«öenter¤~»Ý­ncheck biff */
+ static int old_biff;
+ int biff;
+ char *msg = "¡» ¾´! ¶l®t¨Ó«ö¹a¤F!";
+
+ biff = cutmp->status & STATUS_BIFF;
+ if (biff && !old_biff)
+ (Choose < 0) ? bw_printmsg(msg) : ch_printmsg(1, msg);
+ old_biff = biff;
+}
+#endif
+
+
+static int
+fTAB()
+{
+ mapTalk = mapTalk ? NULL : Talk;
+ return NOTHING;
+}
+
+
+static int
+fNoOp()
+{
+ return NOTHING;
+}
+
+
+static int
+fCtrlD()
+{
+ /* send Q.....\0 cmd */
+ if (!do_send("Q"))
+ return DISCONNECT;
+ return LEAVE;
+}
+
+
+static int
+ftkCtrlC()
+{
+ *cmdBuf = '\0';
+ cmdCol = 0;
+ move(b_lines - 2, 35);
+ clrtoeol();
+ return NOTHING;
+}
+
+
+static int
+ftkCtrlH()
+{
+ if (cmdCol)
+ {
+ int ch = cmdCol--;
+ memcpy(&cmdBuf[cmdCol], &cmdBuf[ch], sizeof(talkBuf) - ch - 1);
+ move(b_lines - 2, cmdCol + 35);
+ outs(&cmdBuf[cmdCol]);
+ clrtoeol();
+ }
+ return NOTHING;
+}
+
+
+static int
+ftkEnter()
+{
+ char msg[80];
+
+#ifdef EVERY_BIFF
+ check_biff();
+#endif
+
+ if (*cmdBuf)
+ {
+ for (cmdPos = MAXLASTCMD - 1; cmdPos; cmdPos--)
+ strcpy(lastcmd[cmdPos], lastcmd[cmdPos - 1]);
+ strcpy(lastcmd[0], cmdBuf);
+
+ if (!do_send(talkBuf))
+ return DISCONNECT;
+
+ sprintf(msg, "\033[1;36m¡¸%s\033[m", cmdBuf);
+ (Choose < 0) ? bw_printmsg(msg) : ch_printmsg(1, msg);
+
+ *cmdBuf = '\0';
+ cmdCol = 0;
+ cmdPos = -1;
+ move(b_lines - 2, 35);
+ clrtoeol();
+ }
+ return NOTHING;
+}
+
+
+static int
+ftkLEFT()
+{
+ if (cmdCol)
+ --cmdCol;
+ return NOTHING;
+}
+
+
+static int
+ftkRIGHT()
+{
+ if (cmdBuf[cmdCol])
+ ++cmdCol;
+ return NOTHING;
+}
+
+
+static int
+ftkUP()
+{
+ cmdPos++;
+ cmdPos %= MAXLASTCMD;
+ str_ncpy(cmdBuf, lastcmd[cmdPos], 41);
+ move(b_lines - 2, 35);
+ outs(cmdBuf);
+ clrtoeol();
+ cmdCol = strlen(cmdBuf);
+ return NOTHING;
+}
+
+
+static int
+ftkDOWN()
+{
+ cmdPos += MAXLASTCMD - 2;
+ return ftkUP();
+}
+
+
+static int
+ftkDefault(ch)
+ int ch;
+{
+ if (isprint2(ch))
+ {
+ if (cmdCol < 40)
+ {
+ if (cmdBuf[cmdCol])
+ { /* insert */
+ int i;
+ for (i = cmdCol; cmdBuf[i] && i < 39; i++);
+ cmdBuf[i + 1] = '\0';
+ for (; i > cmdCol; i--)
+ cmdBuf[i] = cmdBuf[i - 1];
+ }
+ else
+ { /* append */
+ cmdBuf[cmdCol + 1] = '\0';
+ }
+ cmdBuf[cmdCol] = ch;
+ move(b_lines - 2, cmdCol + 35);
+ outs(&cmdBuf[cmdCol++]);
+ }
+ return NOTHING;
+ }
+}
+
+
+static KeyFunc Talk[] =
+{
+ Ctrl('C'), ftkCtrlC,
+ Ctrl('D'), fCtrlD,
+ Ctrl('H'), ftkCtrlH,
+ '\n', ftkEnter,
+ KEY_LEFT, ftkLEFT,
+ KEY_RIGHT, ftkRIGHT,
+ KEY_UP, ftkUP,
+ KEY_DOWN, ftkDOWN,
+ KEY_TAB, fTAB,
+ 0, ftkDefault
+};
+
+
+/*-------------------------------------------------------*/
+/* target : ¾ÔÁZ°O¿ýµ{¦¡ */
+/* author : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+static void
+play_count(userid, total, win) /* ¦^¶Ç userid ªºÁ`³õ¼Æ¡B³Ó³õ¼Æ */
+ char *userid;
+ int *total, *win;
+{
+ char fpath[64];
+ int fd;
+ int grade[32]; /* «O¯d 32 ­Ó int */
+
+ *total = 0;
+ *win = 0;
+
+ usr_fpath(fpath, userid, "bwboard");
+ if ((fd = open(fpath, O_RDONLY)) >= 0)
+ {
+ read(fd, grade, sizeof(grade));
+ close(fd);
+
+ /* ¨Ì§ÇÀx¦s ¶Â¥ÕÁ`,¶Â¥Õ³Ó; ¤­¤lÁ`,¤­¤l³Ó; ³òÁ`,³ò³Ó; ­xÁ`,­x³Ó; ·tÁ`,·t³Ó */
+ fd = (Choose + 3) << 1;
+ *total = grade[fd];
+ *win = grade[fd + 1];
+ }
+}
+
+
+static void
+play_add(win) /* §ÚªºÁ`/³Ó³õ¼Æ +1 */
+ int win;
+{
+ char fpath[64];
+ int fd, kind;
+ int grade[32]; /* «O¯d 32 ­Ó int */
+
+ usr_fpath(fpath, cuser.userid, "bwboard");
+ if ((fd = open(fpath, O_RDWR | O_CREAT, 0600)) >= 0)
+ {
+ if (read(fd, grade, sizeof(grade)) != sizeof(grade))
+ memset(grade, 0, sizeof(grade));
+
+ /* ¨Ì§ÇÀx¦s ¶Â¥ÕÁ`,¶Â¥Õ³Ó; ¤­¤lÁ`,¤­¤l³Ó; ³òÁ`,³ò³Ó; ­xÁ`,­x³Ó; ·tÁ`,·t³Ó */
+ kind = (Choose + 3) << 1;
+ grade[kind]++;
+ if (win)
+ grade[kind + 1]++;
+
+ lseek(fd, (off_t) 0, SEEK_SET);
+ write(fd, grade, sizeof(grade));
+ close(fd);
+ }
+}
+
+
+/*-------------------------------------------------------*/
+/* target : Black & White Chess Board ¶Â¥Õ´Ñ/¤­¤l´Ñ/³ò´Ñ */
+/* create : 99/02/20 */
+/* update : 02/08/05 */
+/* author : thor.bbs@bbs.cs.nthu.edu.tw */
+/* modify : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+enum
+{
+ Deny = 3
+};
+
+
+static KeyFunc myRound[], yourRound[];
+
+static char *bw_icon[] = {"¢q", "¡´", "¡³", " "}; /* Empty, Black, White, Deny */
+
+
+/* 19 * 19, standard chess board */
+static char Allow[19][19];
+static int numWhite, numBlack;
+static int rulerRow, rulerCol;
+
+static int yourPass, myPass;
+static int yourStep, myStep;
+
+static int GameOver; /* end game */
+
+
+#if 0
+
+ % rules util include
+ 0. Board initialize
+ (happen before start, and clear board action)
+ 1. Board update by "color" down a "chess pos"(without update screen)
+ (screen by a main redraw routine, after call this)
+ return value, represent win side to end(for both turn)
+ (happen when state change)
+ 2. Board allow step checking by "color"(for my turn)
+ return value, represent
+
+ % possible step
+ (happen when state change)
+ if Game is over, won''t allow any step
+
+#endif
+
+
+/* numWhite, numBlack maintained by rule, 0&1 */
+/* Board[19][19] Allow[19][19] maintained by rule */
+
+
+static inline void
+countBWnum()
+{
+ int i, j;
+
+ numWhite = numBlack = 0;
+ for (i = 0; i < 19; i++)
+ {
+ for (j = 0; j < 19; j++)
+ {
+ if (Board[i][j] == White)
+ numWhite++;
+ else if (Board[i][j] == Black)
+ numBlack++;
+ }
+ }
+}
+
+
+ /*-----------------------------------------------------*/
+ /* ¶Â¥Õ´Ñ 8 x 8 */
+ /*-----------------------------------------------------*/
+
+static int
+othInit()
+{
+ int i, j;
+ for (i = 0; i < 19; i++)
+ for (j = 0; j < 19; j++)
+ Board[i][j] = i < 8 && j < 8 ? Empty : Deny;
+ Board[3][3] = Board[4][4] = Black;
+ Board[3][4] = Board[4][3] = White;
+ numWhite = numBlack = 2;
+ rulerRow = rulerCol = 8;
+
+ return 0;
+}
+
+
+static inline int
+othEatable(Color, row, col, rowstep, colstep)
+ int Color, row, col, rowstep, colstep;
+{
+ int eat = 0;
+ do
+ {
+ row += rowstep;
+ col += colstep;
+ /* check range */
+ if (row < 0 || row >= 8 || col < 0 || col >= 8)
+ return 0;
+ if (Board[row][col] == Color)
+ return eat;
+ eat = 1;
+ } while (Board[row][col] == Deny - Color);
+ return 0;
+}
+
+
+static int othAllow();
+
+
+static int
+othUpdate(Color, row, col)
+ int Color, row, col;
+{
+ int i, j, p, q;
+ int winside = Empty;
+
+ Board[row][col] = Color;
+ for (i = -1; i <= 1; i++)
+ {
+ for (j = -1; j <= 1; j++)
+ {
+ if (i != 0 || j != 0)
+ {
+ if (othEatable(Color, row, col, i, j))
+ {
+ p = row + i;
+ q = col + j;
+ for (;;)
+ {
+ if (Board[p][q] == Color)
+ break;
+ Board[p][q] = Color;
+ p += i;
+ q += j;
+ }
+ }
+ }
+ }
+ }
+
+ /* count numWhite & numBlack */
+ countBWnum();
+
+ /* Thor.990329.µù¸Ñ: ¤Uº¡®É */
+ {
+ int my = myColor; /* Thor.990331: ¼È¦smyColor */
+ int allowBlack, allowWhite;
+ myColor = Black;
+ allowBlack = othAllow();
+ myColor = White;
+ allowWhite = othAllow();
+ myColor = my;
+ if (allowBlack == 0 && allowWhite == 0)
+ {
+ if (numWhite > numBlack)
+ winside = White;
+ else if (numWhite < numBlack)
+ winside = Black;
+ else
+ winside = Deny;
+ }
+ }
+
+ return winside;
+}
+
+
+static void
+do_othAllow(i, j, num)
+ int i, j;
+ int *num;
+{
+ int p, q;
+
+ Allow[i][j] = 0;
+ if (Board[i][j] == Empty)
+ {
+ for (p = -1; p <= 1; p++)
+ {
+ for (q = -1; q <= 1; q++)
+ {
+ if (p != 0 || q != 0)
+ {
+ if (othEatable(myColor, i, j, p, q))
+ {
+ Allow[i][j] = 1;
+ (*num)++;
+ return;
+ }
+ }
+ }
+ }
+ }
+}
+
+
+static int
+othAllow()
+{
+ int i, j, num;
+
+ num = 0;
+ for (i = 0; i < 8; i++)
+ {
+ for (j = 0; j < 8; j++)
+ {
+ do_othAllow(i, j, &num);
+ }
+ }
+
+ return num;
+}
+
+
+static int (*othRule[]) () =
+{
+ othInit, othUpdate, othAllow
+};
+
+
+ /*-----------------------------------------------------*/
+ /* ¤­¤l´Ñ 15 x 15 */
+ /*-----------------------------------------------------*/
+
+static int
+fivInit()
+{
+ int i, j;
+ for (i = 0; i < 19; i++)
+ {
+ for (j = 0; j < 19; j++)
+ Board[i][j] = i < 15 && j < 15 ? Empty : Deny;
+ }
+ numWhite = numBlack = 0;
+ rulerRow = rulerCol = 15;
+ return 0;
+}
+
+
+static int
+fivCount(Color, row, col, rowstep, colstep)
+ int Color, row, col, rowstep, colstep;
+{
+ int count = 0;
+ for (;;)
+ {
+ row += rowstep;
+ col += colstep;
+ /* check range */
+ if (row < 0 || row >= 15 || col < 0 || col >= 15)
+ return count;
+
+ if (Board[row][col] != Color)
+ return count;
+ count++;
+ }
+}
+
+
+static int
+fivUpdate(Color, row, col)
+ int Color, row, col;
+{
+#if 0
+ int cnt[4], n3, n4, n5, nL, i;
+#endif
+
+ int winside = Empty;
+ Board[row][col] = Color;
+ if (Color == Black)
+ numBlack++;
+ else if (Color == White)
+ numWhite++;
+
+
+ /* ¶Â´Ñ¡]¥ýµÛªÌ¡^¦³¤U¦C¤TµÛ¸TµÛ(¤SºÙ¸T¤â)ÂI¡G¤T¤T(Âù¬¡¤T)¡B¥|¥|¡Bªø³s¡C */
+ /* ¦b³s¤­¤§«e§Î¦¨¸TµÛªÌ¡Aµô©w¬°¸TµÛ­t¡C */
+ /* ¥Õ´Ñ¨S¦³¸TµÛÂI¡Aªø³s©ÎªÌ¤T¤T¤]³£¥i¥H³Ó¡C */
+
+#if 0
+ cnt[0] = fivCount(Color, row, col, -1, -1) + fivCount(Color, row, col, +1, +1) + 1;
+ cnt[1] = fivCount(Color, row, col, -1, 0) + fivCount(Color, row, col, +1, 0) + 1;
+ cnt[2] = fivCount(Color, row, col, 0, -1) + fivCount(Color, row, col, 0, +1) + 1;
+ cnt[3] = fivCount(Color, row, col, -1, +1) + fivCount(Color, row, col, +1, -1) + 1;
+
+ n3 = 0; /* Âù¬¡¤T */
+ n4 = 0; /* Âù¥| */
+ n5 = 0; /* ¤­ */
+ nL = 0; /* ªø³s */
+
+ for (i = 0; i < 4; i++)
+ {
+ if (cnt[i] == (3 | (LIVE_SIDE + LIVE_SIDE)))
+ n3++;
+ if ((cnt[i] % LIVE_SIDE) == 4)
+ n4++;
+ if ((cnt[i] % LIVE_SIDE) == 5)
+ n5++;
+ if ((cnt[i] % LIVE_SIDE) > 5)
+ nL++;
+ }
+
+ if (n5 > 0)
+ winside = Color;
+ else
+ {
+ if (Color == Black)
+ {
+ if (n3 >= 2)
+ {
+ bw_printmsg("¡» ¶Â¤èÂù¤T¸TµÛ");
+ winside = White;
+ }
+ if (n4 >= 2)
+ {
+ bw_printmsg("¡» ¶Â¤èÂù¥|¸TµÛ");
+ winside = White;
+ }
+ if (nL > 0)
+ {
+ bw_printmsg("¡» ¶Â¤èªø³s¸TµÛ");
+ winside = White;
+ }
+ }
+ else
+ {
+ if (nL > 0)
+ winside = Color;
+ }
+ }
+#endif
+
+#if 0 /* Thor.990415: ¤W­±¨º¬q¤S¼g¿ù¤F, ¯d«Ý¦³¤ß¤H¤h¦AºCºC¼g§a :p */
+
+ (¤@)¡´ ¡´ ¡´ ¡´
+ ¡ô
+ ¦A©ñ¶i¥h´Nºâ¥|¥|
+ ¡õ
+ (¤G)¡´¡´¡´ ¡´¡´¡´(¤¤¶¡ªÅ¤T®æ)
+
+ (¤T)¡´ ¡´¡´¡³
+ ¡ô©ñ³o¸Ì¤]ºâ
+ ¡´
+ ¡´
+ ¡´
+
+ (¥|)¡´¡´ ¡´ ¡´¡´
+ ¡ô¦A©ñ´N¬O¥|¥|
+
+
+ ªø³sªº¸Ü,¥u­n¤­¤l¥H¤W´Nºâ,¤£ºÞ¦º¬¡
+
+#endif
+
+#if 1
+ if (fivCount(Color, row, col, -1, -1) + fivCount(Color, row, col, +1, +1) >= 4 ||
+ fivCount(Color, row, col, -1, 0) + fivCount(Color, row, col, +1, 0) >= 4 ||
+ fivCount(Color, row, col, 0, -1) + fivCount(Color, row, col, 0, +1) >= 4 ||
+ fivCount(Color, row, col, -1, +1) + fivCount(Color, row, col, +1, -1) >= 4)
+ winside = Color;
+#endif
+
+ return winside;
+}
+
+
+static int
+fivAllow()
+{
+ int i, j, num = 0;
+ for (i = 0; i < 19; i++)
+ {
+ for (j = 0; j < 19; j++)
+ num += Allow[i][j] = (Board[i][j] == Empty);
+ }
+ return num;
+}
+
+
+static int (*fivRule[]) () =
+{
+ fivInit, fivUpdate, fivAllow
+};
+
+
+ /*-----------------------------------------------------*/
+ /* ³ò´Ñ 19 x 19 */
+ /*-----------------------------------------------------*/
+
+static int
+blkInit()
+{
+ memset(Board, 0, sizeof(Board));
+ numWhite = numBlack = 0;
+ rulerRow = rulerCol = 18;
+ return 0;
+}
+
+
+/* borrow Allow for traversal, and return region */
+/* a recursive procedure, clear Allow before call it */
+/* with row,col range check, return false if out */
+static int
+blkLive(Color, row, col)
+ int Color, row, col;
+{
+ if (row < 0 || row >= 19 || col < 0 || col >= 19)
+ return 0;
+ if (Board[row][col] == Empty)
+ return 1;
+ if (Board[row][col] != Color)
+ return 0;
+ if (Allow[row][col])
+ return 0;
+ Allow[row][col] = 1;
+ return blkLive(Color, row - 1, col) |
+ blkLive(Color, row + 1, col) |
+ blkLive(Color, row, col - 1) |
+ blkLive(Color, row, col + 1);
+}
+
+
+static inline void
+blkClear()
+{
+ int i, j;
+
+ for (i = 0; i < 19; i++)
+ {
+ for (j = 0; j < 19; j++)
+ {
+ if (Allow[i][j])
+ Board[i][j] = Empty;
+ }
+ }
+}
+
+
+static int
+blkUpdate(Color, row, col)
+ int Color, row, col;
+{
+ Board[row][col] = Color;
+
+ memset(Allow, 0, sizeof(Allow));
+ if (!blkLive(Deny - Color, row - 1, col))
+ blkClear();
+
+ memset(Allow, 0, sizeof(Allow));
+ if (!blkLive(Deny - Color, row + 1, col))
+ blkClear();
+
+ memset(Allow, 0, sizeof(Allow));
+ if (!blkLive(Deny - Color, row, col - 1))
+ blkClear();
+
+ memset(Allow, 0, sizeof(Allow));
+ if (!blkLive(Deny - Color, row, col + 1))
+ blkClear();
+
+ /* check for suiside */
+ memset(Allow, 0, sizeof(Allow));
+ if (!blkLive(Color, row, col))
+ blkClear();
+
+ /* count numWhite & numBlack */
+ countBWnum();
+
+ return Empty; /* Please check win side by your own */
+}
+
+
+static int (*blkRule[]) () =
+{
+ blkInit, blkUpdate, fivAllow /* borrow fivAllow as blkAllow */
+};
+
+
+ /*-----------------------------------------------------*/
+ /* board util */
+ /*-----------------------------------------------------*/
+
+#if 0 /* screen */
+
+ [maple BWboard]
+ xxxx vs yyyy
+ ++++++++ talkline(you color, yellow) (40 chars)
+ ++++++++ talkline(my color, cryn)
+ ++++++++
+ ++++++++
+ ++++++++
+
+ one line for simple help, press key to......
+ one line for nth turn, myColor, num, pass < -youcolor, num, pass(35), input talk
+ two line for write msg
+
+#endif
+
+
+static char *
+bw_brdline(row)
+ int row;
+{
+ static char buf[80] = "\033[30;43m";
+ static char rTxtY[] = " A B C D E F G H I J K L M N O P Q R S";
+ static char rTxtX[] = " 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9";
+ char txt[3];
+ char *ptr = buf + 8, *t;
+ int i;
+
+ for (i = 0; i < 19; i++)
+ {
+ t = bw_icon[Board[row][i]];
+ if (t == bw_icon[Empty])
+ {
+ if (row == 0)
+ {
+ if (i == 0)
+ t = "ùÝ";
+ else if (i >= 18 || Board[row][i + 1] == Deny)
+ t = "ùß";
+ else
+ t = "ùç";
+ }
+ else if (row >= 18 || Board[row + 1][i] == Deny)
+ {
+ if (i == 0)
+ t = "ùã";
+ else if (i >= 18 || Board[row][i + 1] == Deny)
+ t = "ùå";
+ else
+ t = "ùí";
+ }
+ else
+ {
+ if (i == 0)
+ t = "ùò";
+ else if (i >= 18 || Board[row][i + 1] == Deny)
+ t = "ùô";
+ }
+ }
+ if (t != bw_icon[Black] && t != bw_icon[White])
+ {
+ if (row == rulerRow && i < rulerCol)
+ {
+ str_ncpy(txt, rTxtX + 2 * i, 3);
+ t = txt;
+ }
+ else if (i == rulerCol && row < rulerRow)
+ {
+ str_ncpy(txt, rTxtY + 2 * row, 3);
+ t = txt;
+ }
+ }
+ strcpy(ptr, t);
+ ptr += 2;
+ }
+ strcpy(ptr, "\033[m");
+ return buf;
+}
+
+
+static char *
+bw_coorstr(row, col)
+ int row;
+ int col;
+{
+ static char coor[10];
+ sprintf(coor, "(%c,%d)", row + 'A', col + 1);
+ return coor;
+}
+
+
+static void
+bw_printmsg(msg)
+ char *msg;
+{
+ int line;
+
+ line = msgline;
+ move(line, 0);
+ outs(bw_brdline(line - 1));
+ outs(msg);
+ clrtoeol();
+ if (++line == b_lines - 3) /* stop line */
+ line = 1;
+ move(line, 0);
+ outs(bw_brdline(line - 1));
+ outs("¡÷");
+ clrtoeol();
+ msgline = line;
+}
+
+
+static void
+bw_draw()
+{
+ int i, myNum, yourNum;
+ for (i = 0; i < 19; i++)
+ {
+ move(1 + i, 0);
+ outs(bw_brdline(i));
+ }
+ myNum = (myColor == Black) ? numBlack : numWhite;
+ yourNum = (myColor == Black) ? numWhite : numBlack;
+
+ move(b_lines - 2, 0);
+ prints("²Ä%d¦^ %s%d¤l%dÅý (%s) %s%d¤l%dÅý",
+ BMIN(myStep, yourStep) + 1, bw_icon[Deny - myColor], myNum, myPass,
+ (mapTurn == myRound ? "¡ö" : "¡÷"), bw_icon[myColor], yourNum, yourPass);
+ /* Thor.990219: ¯S§Oª`·N, ¦b¦¹¦]ÃC¦âÃö«Y, ¬G¥Î¤lªºicon, ­è¦n»P­ì¥»¬Û¤Ï */
+ /* nth turn,myColor,num,pass <- youcolor, num,pass */
+}
+
+
+static void
+bw_init()
+{
+ yourPass = myPass = yourStep = myStep = 0;
+
+ /* ¶Â¥Õ´Ñ/¤­¤l´Ñ/³ò´Ñ¬°¶Â¤l¥ý¦æ */
+ if (myColor == Black)
+ {
+ (*rule[Ballow]) ();
+ mapTurn = myRound;
+ }
+ else
+ {
+ mapTurn = yourRound;
+ }
+
+ move(b_lines - 1, 0);
+ outs(COLOR1 " ¹ï«³¼Ò¦¡ " COLOR2 " (Enter)¸¨¤l (TAB)¤Á´«´Ñ½L/¥æ½Í (^P)Åý¤â (^C)­«ª± (^D)Â÷¶} \033[m");
+
+ bw_draw();
+}
+
+
+static inline void
+bw_overgame()
+{
+ if (GameOver == Black)
+ bw_printmsg("\033[1;32m¡» ¶Â¤èÀò³Ó\033[m");
+ else if (GameOver == White)
+ bw_printmsg("\033[1;32m¡» ¥Õ¤èÀò³Ó\033[m");
+ else if (GameOver == Deny)
+ bw_printmsg("\033[1;32m¡» Âù¤è¥­¤â\033[m");
+
+ play_add(myColor == GameOver);
+}
+
+
+#if 0 /* communication protocol */
+
+ Ctrl('O'):
+
+ enter BWboard mode, (pass to another)
+ first hand specify rule set(pass Rule later)
+ then start
+
+ clear chess board, C.....\ 0
+ talk line by line, T.....\ 0
+ specify down pos, Dxxx \ 0, y = xxx / 19, x = xxx % 19
+ pass one turn, P.....\ 0
+ leave BWboard mode, Q.....\ 0
+
+#endif
+
+
+static inline int
+bw_recv()
+{
+ static char buf[512];
+ static int bufstart = 0;
+ int cc, len;
+ char *bptr, *str;
+ char msg[80];
+ int i;
+
+ bptr = buf;
+ cc = bufstart;
+ len = sizeof(buf) - cc - 1;
+
+ if ((len = recv(cfd, bptr + cc, len, 0)) <= 0)
+ return DISCONNECT;
+
+ cc += len;
+
+ for (;;)
+ {
+ len = strlen(bptr);
+
+ if (len >= cc)
+ { /* wait for trailing data */
+ memcpy(buf, bptr, len);
+ bufstart = len;
+ break;
+ }
+ str = bptr + 1;
+ switch (*bptr)
+ {
+ /* clear chess board, C.....\0 */
+ case 'C':
+ do_init();
+ break;
+
+ /* talk line by line, T.....\0 */
+ case 'T':
+ sprintf(msg, "\033[1;33m¡¹%s\033[m", str);
+ bw_printmsg(msg);
+ break;
+
+ /* specify down pos, Dxxx\0 , y = xxx / 19, x = xxx % 19 */
+ case 'D':
+ yourStep++;
+ /* get pos */
+ i = atoi(str);
+ sprintf(msg, "¡» ¹ï¤è¸¨¤l %s", bw_coorstr(i / 19, i % 19));
+ /* update board */
+ GameOver = (*rule[Bupdate]) (Deny - myColor, i / 19, i % 19);
+
+ mapTurn = myRound;
+
+ bw_draw();
+
+ bw_printmsg(msg);
+
+ if (GameOver)
+ {
+ bw_overgame();
+ memset(Allow, 0, sizeof(Allow));
+ }
+ else
+ {
+ if ((*rule[Ballow]) () <= 0)
+ bw_printmsg("¡» ±z¨«§ëµL¸ô¤F");
+ }
+ break;
+
+ /* pass one turn, P.....\0 */
+ case 'P':
+ yourPass++;
+ yourStep++;
+
+ mapTurn = myRound;
+ bw_draw();
+ bw_printmsg("¡» ¹ï¤èÅý¤â");
+ if (GameOver)
+ {
+ memset(Allow, 0, sizeof(Allow));
+ }
+ else
+ {
+ if ((*rule[Ballow]) () <= 0)
+ bw_printmsg("¡» ±z¨«§ëµL¸ô¤F"); /* Thor.990329: ending game? */
+ }
+ break;
+
+ /* leave BWboard mode, Q.....\0 */
+ case 'Q':
+ return LEAVE;
+ }
+
+ cc -= ++len;
+ if (cc <= 0)
+ {
+ bufstart = 0;
+ break;
+ }
+ bptr += len;
+ }
+
+ return NOTHING;
+}
+
+
+static int
+ftnCtrlC()
+{
+ if (!do_send("C"))
+ return DISCONNECT;
+ do_init();
+ return NOTHING;
+}
+
+
+static int
+ftnUP()
+{
+ if (bwRow)
+ bwRow--;
+ return NOTHING;
+}
+
+
+static int
+ftnDOWN()
+{
+ if (bwRow < 18)
+ if (Board[bwRow + 1][bwCol] != Deny)
+ bwRow++;
+ return NOTHING;
+}
+
+
+static int
+ftnLEFT()
+{
+ if (bwCol)
+ bwCol--;
+ return NOTHING;
+}
+
+
+static int
+ftnRIGHT()
+{
+ if (bwCol < 18)
+ if (Board[bwRow][bwCol + 1] != Deny)
+ bwCol++;
+ return NOTHING;
+}
+
+
+static int
+ftnPass()
+{
+ /* Thor.990220: for chat mode to enter ^P pass */
+ if (mapTurn == myRound && !GameOver)
+ {
+ myPass++;
+ myStep++;
+ if (!do_send("P"))
+ return DISCONNECT;
+ mapTurn = yourRound;
+ bw_draw();
+ bw_printmsg("¡» §Ú¤èÅý¤â");
+ }
+ return NOTHING;
+}
+
+
+static int
+ftnEnter()
+{
+ char msg[80];
+ char buf[20];
+
+ if (!Allow[bwRow][bwCol])
+ return NOTHING;
+
+ sprintf(msg, "¡» §Ú¤è¸¨¤l %s", bw_coorstr(bwRow, bwCol));
+
+ myStep++;
+ sprintf(buf, "D%d", bwRow * 19 + bwCol);
+
+ if (!do_send(buf))
+ return DISCONNECT;
+
+ /* update board */
+ GameOver = (*rule[Bupdate]) (myColor, bwRow, bwCol);
+
+ mapTurn = yourRound;
+
+ bw_draw();
+
+ bw_printmsg(msg);
+
+ if (GameOver)
+ bw_overgame();
+
+ return NOTHING;
+}
+
+
+static KeyFunc yourRound[] =
+{
+ Ctrl('C'), ftnCtrlC,
+ Ctrl('D'), fCtrlD,
+ KEY_LEFT, ftnLEFT,
+ KEY_RIGHT, ftnRIGHT,
+ KEY_UP, ftnUP,
+ KEY_DOWN, ftnDOWN,
+ KEY_TAB, fTAB,
+ 0, fNoOp
+};
+
+
+static KeyFunc myRound[] =
+{
+ Ctrl('C'), ftnCtrlC,
+ ' ', ftnEnter,
+ '\n', ftnEnter,
+ Ctrl('P'), ftnPass,
+ Ctrl('D'), fCtrlD,
+ KEY_LEFT, ftnLEFT,
+ KEY_RIGHT, ftnRIGHT,
+ KEY_UP, ftnUP,
+ KEY_DOWN, ftnDOWN,
+ KEY_TAB, fTAB,
+ 0, fNoOp
+};
+
+
+/*-------------------------------------------------------*/
+/* target : Chinese Chess Board ­x´Ñ/·t´Ñ */
+/* create : 99/12/14 */
+/* update : 02/08/05 */
+/* author : weichung.bbs@bbs.ntit.edu.tw */
+/* modify : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+enum
+{
+ Cover = 1
+};
+
+
+static KeyFunc myTurn[], yourTurn[];
+
+static int sideline;
+static int Totalch; /* ¥[³t¥Î :p */
+static int Focus;
+static int youreat_index;
+static int myeat_index;
+
+static char MyEat[16], YourEat[16];
+static char Appear[14];
+static char dstr[19][200];
+
+static char *ch_icon[] =
+{
+ " ", "¡´", /* Empty, Cover */
+ "\033[1;31m«Ó\033[m", "\033[1;31m¥K\033[m", "\033[1;31m¬Û\033[m",
+ "\033[1;31mÚÏ\033[m", "\033[1;31mØX\033[m", "\033[1;31m¬¶\033[m", "\033[1;31m§L\033[m",
+ "\033[1;32m±N\033[m", "\033[1;32m¤h\033[m", "\033[1;32m¶H\033[m",
+ "\033[1;32m¨®\033[m", "\033[1;32m°¨\033[m", "\033[1;32m¥]\033[m", "\033[1;32m¨ò\033[m"
+};
+
+
+ /*-----------------------------------------------------*/
+ /* ­x´Ñ 10 x 9 */
+ /*-----------------------------------------------------*/
+
+static int
+armyInit()
+{
+ int i, j;
+
+ for (i = 0; i < 10; i++)
+ {
+ for (j = 0; j < 9; j++)
+ Board[i][j] = Empty;
+ }
+
+ Board[0][4] = 2; /* «Ó */
+ Board[0][3] = Board[0][5] = 3; /* ¥K */
+ Board[0][2] = Board[0][6] = 4; /* ¬Û */
+ Board[0][1] = Board[0][7] = 6; /* ØX */
+ Board[0][0] = Board[0][8] = 5; /* ÚÏ */
+ Board[2][1] = Board[2][7] = 7; /* ¦ */
+ Board[3][0] = Board[3][2] = Board[3][4] = Board[3][6] = Board[3][8] = 8; /* §L */
+
+ Board[9][4] = 9; /* ±N */
+ Board[9][3] = Board[9][5] = 10; /* ¤h */
+ Board[9][2] = Board[9][6] = 11; /* ¶H */
+ Board[9][1] = Board[9][7] = 13; /* °¨ */
+ Board[9][0] = Board[9][8] = 12; /* ¨® */
+ Board[7][1] = Board[7][7] = 14; /* ¥] */
+ Board[6][0] = Board[6][2] = Board[6][4] = Board[6][6] = Board[6][8] = 15; /* ¨ò */
+
+ memset(MyEat, Empty, sizeof(MyEat));
+ memset(YourEat, Empty, sizeof(YourEat));
+
+ sideline = 19;
+ return 0;
+}
+
+
+static int (*armyRule[]) () =
+{
+ armyInit
+};
+
+
+ /*-----------------------------------------------------*/
+ /* ·t´Ñ 4 x 8 */
+ /*-----------------------------------------------------*/
+
+static int
+darkInit()
+{
+ int i, j;
+
+ for (i = 0; i < 4; i++)
+ for (j = 0; j < 8; j++)
+ Board[i][j] = Cover;
+
+ memset(Appear, Empty, sizeof(Appear));
+ memset(MyEat, Empty, sizeof(MyEat));
+ memset(YourEat, Empty, sizeof(YourEat));
+
+ sideline = 9;
+ return 0;
+}
+
+
+static int (*darkRule[]) () =
+{
+ darkInit
+};
+
+
+ /*-----------------------------------------------------*/
+ /* board util */
+ /*-----------------------------------------------------*/
+
+
+static void
+ch_printmsg(type, msg)
+ int type;
+ char *msg;
+{
+/* char *ch_brdline();
+ *
+ * lantw44: ³o­Ó¥ýµù¸Ñ±¼¡A§ï«Å§i¦bÀÉ®×¶}ÀY
+ * ¦]¬°³o·|³y¦¨ gcc 4 ½sĶ¥¢±Ñ
+ */
+ char buf[100];
+ int tmp;
+
+ switch (type)
+ {
+ case 1: /* for move uncover eat */
+ /*move(msgline + 9, 37);
+ outs(msg);*/
+ tmp=msgline + 8;
+ strcpy(dstr[tmp], msg);
+ outs(ch_brdline(tmp));
+ clrtoeol();
+ if (++msgline >= 11)
+ msgline = 1;
+ /*move(msgline + 9, 37);
+ outs("¡÷");*/
+ tmp=msgline + 8;
+ strcpy(dstr[tmp], "¡÷");
+ outs(ch_brdline(tmp));
+ clrtoeol();
+ break;
+
+ case 2: /* for select */
+ sprintf(buf, "\033[1;33m¡»±z¿ï¨ú¤F %s\033[1;33m(%d, %c)\033[m",
+ ch_icon[Focus / 256], bwCol, bwRow + 'A');
+ /*move(1, 37);
+ outs(buf);*/
+ strcpy(dstr[0], buf);
+ outs(ch_brdline(0));
+ clrtoeol();
+ break;
+
+ case 3: /* for disable select */
+ /*move(1, 37);*/
+ strcpy(dstr[0], "");
+ outs(ch_brdline(0));
+ clrtoeol();
+ break;
+ }
+}
+
+
+static void
+ch_printeat()
+{
+ int i;
+ char buf[200];
+ buf[0]=0;
+ /* my:4, your:7 */
+
+ if (myeat_index)
+ {
+ /* sprintf(buf, "%d ", myeat_index); */
+ for (i = 0; i < myeat_index; i++)
+ {
+ /*move(4, 37 + i * 2);
+ outs(ch_icon[MyEat[i]]);*/
+ strcat(buf,ch_icon[MyEat[i]]);
+ }
+ strcpy(dstr[3], buf);
+ outs(ch_brdline(3));
+ }
+
+ buf[0]=0;
+
+ if (youreat_index)
+ {
+ for (i = 0; i < youreat_index; i++)
+ {
+ /*move(7, 37 + i * 2);
+ outs(ch_icon[YourEat[i]]);*/
+ strcat(buf,ch_icon[YourEat[i]]);
+ }
+ strcpy(dstr[6], buf);
+ outs(ch_brdline(6));
+ }
+}
+
+
+static void
+ch_overgame(win)
+ int win;
+{
+ char buf[80];
+
+ GameOver = 1;
+ sprintf(buf, "%s¤èÀò³Ó¡I½Ð«ö Ctrl-C ­«ª±", win == myColor ? "§Ú" : "¹ï");
+ ch_printmsg(1, buf);
+
+ play_add(win == myColor);
+}
+
+
+static char *
+ch_brdline(row)
+ int row;
+{
+ char *t, *str, ch;
+ static char buf[350];
+ static char river[] = "¢x·¡ ªe º~ ¬É¢x";
+ static char side[] = " A B C D E F G H I J";
+ int i,col;
+
+ move(row + 1, 0);
+ if (row == 9 && Choose) {
+ strcpy(buf, " ¢Ý ¢° ¢± ¢² ¢³ ¢´ ¢µ ¢¶ ");
+ strcat(buf, dstr[row]);
+ return buf;
+ }
+ else if (row > 8 && Choose) { /* ·t´Ñªº row ³Ì¦h¨ì 8 */
+ strncpy(buf, forspace, 37);
+ buf[37] = 0;
+ strcat(buf, dstr[row]);
+ return buf;
+ }
+
+ if (row == 9) { /* ­x´Ñªº row 9 ¬O ·¡ªeº~¬É */
+ strcpy(buf, river);
+ strcat(buf, " ");
+ strcat(buf, dstr[9]);
+ return buf;
+ }
+
+ str = buf;
+
+ /* µe®æ¤l¤Î®æ¤l¤Wªº´Ñ¤l */
+
+ for (i = 0; i < 17; i++)
+ {
+ col = 2;
+ if (!Choose && (row % 2 || i % 2))
+ ch = Empty;
+ else
+ ch = Board[row / 2][i / 2];
+
+ if (Choose || (ch == Empty))
+ {
+ if (row == 0) /* ¢z¢w¢s¢w¢{ */
+ {
+ if (i == 0)
+ t = "¢z";
+ else if (i == 16)
+ t = "¢{";
+ else if (i % 2)
+ t = "¢w";
+ else
+ t = "¢s";
+ }
+ else if (row == 18 || (row == 8 && Choose)) /* ¢|¢w¢r¢w¢} */
+ {
+ if (i == 0)
+ t = "¢|";
+ else if (i == 16)
+ t = "¢}";
+ else if (i % 2)
+ t = "¢w";
+ else
+ t = "¢r";
+ }
+ else if (row % 2) /* ¢x ¢x ¢x */
+ {
+ if (i % 2) {
+ t = ch_icon[ch];
+ if(ch > 1) col = 12;
+ }
+ else
+ t = "¢x";
+ }
+ else /* ¢u¢w¢q¢w¢t */
+ {
+ if (i == 0)
+ t = "¢u";
+ else if (i == 16)
+ t = "¢t";
+ else if (i % 2)
+ t = "¢w";
+ else
+ t = "¢q";
+ }
+ }
+ else
+ {
+ t = ch_icon[ch];
+ if(ch > 1) col = 12;
+ }
+ strcpy(str, t); /* color */
+ str += col;
+ } /* end for loop */
+
+ if ((Choose && (row % 2 == 1)) || (!Choose && (row % 2 == 0)))
+ {
+ i = row / 2;
+ strncpy(str, side + i * 2, 2);
+ str += 2;
+ *str = 0;
+ }
+ else
+ {
+ strcpy(str, " ");
+ str += 2;
+ *str = 0;
+ }
+ strcpy(str, " ");
+ str++;
+ strcpy(str, dstr[row]);
+ return buf;
+}
+
+
+static void
+ch_draw()
+{
+ int i;
+
+ for (i = 0; i < sideline; i++)
+ {
+ /*move(1 + i, 0);*/
+ outs(ch_brdline(i));
+ }
+ /*move(3, 37);
+ outs("¡¸§Ú¤è©Ò¦Y¤§¤l");
+ move(6, 37);
+ outs("¡¹¹ï¤è©Ò¦Y¤§¤l");
+ move(9, 37);
+ outs("====================================");*/
+
+ move(sideline + 1, 0);
+ if (Choose) {
+ outs(ch_brdline(sideline));
+ }
+ else
+ outs("¢Ý ¢° ¢± ¢² ¢³ ¢´ ¢µ ¢¶ ¢·");
+
+ move(b_lines - 2, 0);
+ prints("§Ú¬O \033[47;%s¤l\033[m [½ü¨ì%s]",
+ dark_choose ? (myColor == Black ? "30m¶Â" : "31m¬õ") : "30m¥Õ",
+ mapTurn == myTurn ? "§Ú¤F" : "¹ï¤è");
+}
+
+
+static void
+ch_init()
+{
+ int i;
+ Totalch = youreat_index = myeat_index = Focus = 0;
+
+ /* ·t´Ñªº«ù¤lÃC¦â¥¼©w */
+ dark_choose = (Choose == 1) ? 0 : 1;
+
+ /* ­x´Ñ/·t´Ñ¬°¶Â¤l¥ý¦æ (·t´Ñ: ¯u¥¿ªº«ù¤lÃC¦â·|¦b²Ä¤@¦¸Â½¤l®É¨M©w) */
+ mapTurn = (myColor == Black) ? myTurn : yourTurn;
+
+ move(b_lines - 1, 0);
+ outs(COLOR1 " ¹ï«³¼Ò¦¡ " COLOR2 " (Enter)½¤l/²¾°Ê (TAB)¤Á´«´Ñ½L/¥æ½Í (^S)»{¿é (^C)­«ª± (^D)Â÷¶} \033[m");
+
+ for(i = 0; i < 19; i++) strcpy(dstr[i], forspace);
+ strcpy(dstr[2],"¡¸§Ú¤è©Ò¦Y¤§¤l");
+ strcpy(dstr[5],"¡¹¹ï¤è©Ò¦Y¤§¤l");
+ strcpy(dstr[8],"====================================");
+
+ ch_draw();
+}
+
+
+static inline int
+ch_recv()
+{
+ static char buf[256];
+ char msg[80];
+ int len, ch, row, col, ch2, row2, col2;
+
+ len = sizeof(buf) + 1;
+ if ((len = recv(cfd, buf, len, 0)) <= 0)
+ return DISCONNECT;
+
+ switch (*buf)
+ {
+ /* clear chess board, C.....\0 */
+ case 'C':
+ do_init();
+ break;
+
+ case 'D':
+ ch = atoi(buf + 1);
+ row = (ch / 16) % 16;
+ col = ch % 16;
+ ch = ch / 256;
+
+ Board[row][col] = ch;
+ Appear[ch - 2]++;
+ Totalch++;
+ mapTurn = myTurn;
+ sprintf(msg, "\033[1;32m¡µ¹ï¤è½¶} %s\033[1;32m(%d, %c)\033[m", ch_icon[ch], col, row + 'A');
+ ch_draw();
+ ch_printmsg(1, msg);
+ break;
+
+ case 'T':
+ sprintf(msg, "\033[1;33m¡¹%s\033[m", buf + 1);
+ ch_printmsg(1, msg);
+ break;
+
+ case 'E':
+ ch = atoi(strtok(buf + 1, ":"));
+ row = (ch / 16) % 16;
+ col = ch % 16;
+ ch = ch / 256;
+ ch2 = atoi(strtok(NULL, ":"));
+ row2 = (ch2 / 16) % 16;
+ col2 = ch2 % 16;
+ ch2 = ch2 / 256;
+
+ Board[row][col] = Empty;
+ Board[row2][col2] = ch;
+ YourEat[youreat_index++] = ch2;
+ mapTurn = myTurn;
+ sprintf(msg, "\033[1;32m¡¾¹ï¤è²¾°Ê %s\033[1;32m(%d, %c) ¦Y %s\033[1;32m(%d, %c)\033[m",
+ ch_icon[ch], col, row + 'A',
+ ch_icon[ch2], col2, row2 + 'A');
+ ch_draw();
+ ch_printmsg(1, msg);
+ ch_printeat();
+
+ if (Choose)
+ {
+ if (youreat_index == 16)
+ ch_overgame((myColor == Black) ? Red : Black);
+ }
+ else
+ {
+ if (ch2 == 2 || ch2 == 9)
+ ch_overgame((myColor == Black) ? Red : Black);
+ }
+ break;
+
+ case 'M':
+ ch = atoi(strtok(buf + 1, ":"));
+ row = (ch / 16) % 16;
+ col = ch % 16;
+ ch = ch / 256;
+ ch2 = atoi(strtok(NULL, ":"));
+ row2 = (ch2 / 16) % 16;
+ col2 = ch2 % 16;
+ ch2 = ch2 / 256;
+
+ Board[row][col] = Empty;
+ Board[row2][col2] = ch;
+ mapTurn = myTurn;
+ sprintf(msg, "\033[1;37m¡¾¹ï¤è²¾°Ê %s\033[1;37m(%d, %c) ¦Ü (%d, %c)\033[m",
+ ch_icon[ch], col, row + 'A', col2, row2 + 'A');
+ ch_draw();
+ ch_printmsg(1, msg);
+ break;
+
+ case 'F':
+ dark_choose = 1;
+ myColor = (atoi(buf + 1) == Black) ? Red : Black;
+ ch_draw();
+ break;
+
+ case 'S':
+ ch_overgame(myColor);
+ break;
+
+ case 'Q':
+ return LEAVE;
+ }
+
+ return NOTHING;
+}
+
+
+static int
+ch_rand()
+{
+ int rd, i;
+ char *index[] = {"1", "2", "2", "2", "2", "2", "5", "1", "2", "2", "2", "2", "2", "5"};
+
+ if (Totalch == 31) /* Á×§K³Ñ³Ì«á¤@­Ó®ÉÁÙ­n random */
+ {
+ for (i = 0; i < 14; i++)
+ {
+ if (Appear[i] < atoi(index[i]))
+ {
+ i += 2;
+ return i;
+ }
+ }
+ }
+
+ for (;;)
+ {
+ rd = rnd(16);
+
+ if (rd < 2)
+ continue;
+ if (Appear[rd - 2] < atoi(index[rd - 2]))
+ {
+ Appear[rd - 2] += 1;
+ Totalch++;
+ break;
+ }
+ else
+ continue;
+ }
+ return rd;
+}
+
+
+static int
+ch_count(row, col) /* ¦^¶Ç¥]/¬¶»P«Ý¦Yª«¤¤¶¡¦³´X¤@¤l */
+ int row, col;
+{
+ int count, start, end;
+
+ if (bwRow != row && bwCol != col) /* ¥²¶·¦b¦P¤@¦æ©Î¦P¤@¦C¸õ */
+ return -1;
+
+ count = 0;
+ if (bwRow != row)
+ {
+ if (bwRow > row)
+ {
+ start = row + 1;
+ end = bwRow;
+ }
+ else
+ {
+ start = bwRow + 1;
+ end = row;
+ }
+ for (; start < end; start++)
+ {
+ if (Board[start][bwCol] != Empty)
+ count++;
+ }
+ }
+ else
+ {
+ if (bwCol > col)
+ {
+ start = col + 1;
+ end = bwCol;
+ }
+ else
+ {
+ start = bwCol + 1;
+ end = col;
+ }
+ for (; start < end; start++)
+ {
+ if (Board[bwRow][start] != Empty)
+ count++;
+ }
+ }
+
+ return count;
+}
+
+
+static int /* 0:¬Û¦PÃC¦â 1:¤£¦PÃC¦â */
+ch_check(ch)
+ char ch;
+{
+ if ((myColor == Red && ch < 9) ||
+ (myColor == Black && ch > 8))
+ return 0;
+
+ return 1;
+}
+
+
+static int
+ch_Mv0() /* for ­x´Ñ */
+{
+ int mych, yourch, way; /* way: 0:NOTHING 1:move 2:eat */
+ int row, col, Rdis, Cdis;
+ char buf[120];
+
+ if(GameOver) return NOTHING;
+
+ row = (Focus / 16) % 16;
+ col = Focus % 16;
+ mych = Focus / 256;
+ yourch = Board[bwRow][bwCol];
+
+ Rdis = abs(row - bwRow); /* displayment */
+ Cdis = abs(col - bwCol);
+ way = NOTHING;
+
+ switch (mych)
+ {
+ case 2: /* «Ó±N */
+ case 9:
+ if (((bwCol >= 3 && bwCol <= 5) && (bwRow <= 2 || bwRow >= 7) && (Rdis + Cdis == 1)) || /* ­­¨î¦b¤E®c®æ¤¤¡A¨«¤@®æ */
+ (bwCol == col && abs(mych - yourch) == 7 && !ch_count(row, col))) /* ¤ý¨£¤ý */
+ {
+ if (yourch == Empty)
+ way = 1;
+ else if (ch_check(yourch))
+ way = 2;
+ }
+ break;
+
+ case 3: /* ¥K¤h */
+ case 10:
+ if ((bwCol >= 3 && bwCol <= 5) && (bwRow <= 2 || bwRow >= 7) && /* ­­¨î¦b¤E®c®æ¤¤ */
+ (Rdis == 1 && Cdis == 1)) /* ¨«¤@±× */
+ {
+ if (yourch == Empty)
+ way = 1;
+ else if (ch_check(yourch))
+ way = 2;
+ }
+ break;
+
+ case 4: /* ¬Û¶H */
+ case 11:
+ if (((bwRow <= 4 && myColor == Red) || (bwRow >= 5 && myColor == Black)) && /* ­­¨î¤£¯à¹Lªe */
+ (Rdis == 2 && Cdis == 2) && /* ¨«¤@¥Ð */
+ (Board[(bwRow + row) / 2][(bwCol + col) / 2] == Empty)) /* ¤£¯à©ä¶H¸} */
+ {
+ if (yourch == Empty)
+ way = 1;
+ else if (ch_check(yourch))
+ way = 2;
+ }
+ break;
+
+ case 5: /* ÚϨ® */
+ case 12:
+ if (!ch_count(row, col) && (row == bwRow || col == bwCol)) /* ¨«¤@½u */
+ {
+ if (yourch == Empty)
+ way = 1;
+ else if (ch_check(yourch))
+ way = 2;
+ }
+ break;
+
+ case 6: /* ØX°¨ */
+ case 13:
+ if ((Rdis == 2 && Cdis == 1 && Board[(bwRow + row) / 2][col] == Empty) || /* ¨«¤@©ä */
+ (Rdis == 1 && Cdis == 2 && Board[row][(bwCol + col) / 2] == Empty)) /* ¤£¯à©ä°¨¸} */
+ {
+ if (yourch == Empty)
+ way = 1;
+ else if (ch_check(yourch))
+ way = 2;
+ }
+ break;
+
+ case 7: /* ¬¶¥] */
+ case 14:
+ if (row == bwRow || col == bwCol) /* ¨«¤@½u */
+ {
+ Rdis = ch_count(row, col); /* ­É¥Î Rdis */
+ if (Rdis == 0 && yourch == Empty)
+ way = 1;
+ else if (Rdis == 1 && yourch != Empty && ch_check(yourch))
+ way = 2;
+ }
+ break;
+
+ case 8: /* §L¨ò */
+ case 15:
+ if (Rdis + Cdis != 1) /* ¨«¤@®æ */
+ break;
+
+ if (myColor == Red)
+ {
+ if ((bwRow < row) || /* ¤£¯à¨«¦^ÀY¨B */
+ (row <= 4 && col != bwCol)) /* ¦b°ê¤º¥u¯à¨«ª½ªº */
+ break;
+ }
+ else
+ {
+ if ((bwRow > row) || /* ¤£¯à¨«¦^ÀY¨B */
+ (row >= 5 && col != bwCol)) /* ¦b°ê¤º¥u¯à¨«ª½ªº */
+ break;
+ }
+
+ if (yourch == Empty)
+ way = 1;
+ else if (ch_check(yourch))
+ way = 2;
+ break;
+ } /* end switch */
+ if (way == 1)
+ {
+ sprintf(buf, "M%d:%d", Focus, bwRow * 16 + bwCol);
+ if (!do_send(buf))
+ return DISCONNECT;
+ sprintf(buf, "\033[1;36m¡¿§Ú¤è²¾°Ê %s\033[1;36m(%d, %c) ¦Ü (%d, %c)\033[m",
+ ch_icon[mych], col, row + 'A', bwCol, bwRow + 'A');
+ strcpy(dstr[0], forspace);
+ outs(ch_brdline(0));
+ }
+ else if (way == 2)
+ {
+ sprintf(buf, "E%d:%d", Focus, yourch * 256 + bwRow * 16 + bwCol);
+ if (!do_send(buf))
+ return DISCONNECT;
+ sprintf(buf, "\033[1;32m¡¿§Ú¤è²¾°Ê %s\033[1;32m(%d, %c) ¦Y %s\033[1;32m(%d, %c)\033[m",
+ ch_icon[mych], col, row + 'A', ch_icon[yourch], bwCol, bwRow + 'A');
+ MyEat[myeat_index++] = yourch;
+ strcpy(dstr[0], forspace);
+ outs(ch_brdline(0));
+ ch_printeat();
+ }
+
+ if (way)
+ {
+ Board[bwRow][bwCol] = mych;
+ Board[row][col] = Empty;
+ mapTurn = yourTurn;
+ Focus = Empty;
+ ch_draw();
+ ch_printmsg(1, buf);
+
+ if (yourch == 2 || yourch == 9)
+ ch_overgame(myColor);
+ }
+
+ return NOTHING;
+}
+
+
+static int
+ch_Mv1() /* for ·t´Ñ */
+{
+ int row, col;
+ char mych, yourch;
+ char buf[120];
+
+ row = (Focus / 16) % 16;
+ col = Focus % 16;
+ mych = Focus / 256;
+ yourch = Board[bwRow][bwCol];
+
+ if (yourch == Empty) /* ²¾¶iªÅ¦a */
+ {
+ if (abs(bwRow - row) + abs(bwCol - col) != 1) /* ­n¦b¹j¾À¤~¯à²¾¹L¥h */
+ return NOTHING;
+
+ sprintf(buf, "M%d:%d", Focus, bwRow * 16 + bwCol);
+ if (!do_send(buf))
+ return DISCONNECT;
+ sprintf(buf, "\033[1;36m¡¿§Ú¤è²¾°Ê %s\033[1;36m(%d, %c) ¦Ü (%d, %c)\033[m",
+ ch_icon[mych], col, row + 'A', bwCol, bwRow + 'A');
+ strcpy(dstr[0], forspace);
+ outs(ch_brdline(0));
+ }
+ else /* ²¾¶i¦³¤lªº¦a */
+ {
+ if (!ch_check(yourch)) /* ¤£¦P¦â¤~¥i¥H¦Y */
+ return NOTHING;
+
+ if (mych == 7 || mych == 14) /* ¥]/¬¶¥Î¸õªº¤~¯à¦Y */
+ {
+ if (ch_count(row, col) != 1)
+ return NOTHING;
+ }
+ else
+ {
+ if (abs(bwRow - row) + abs(bwCol - col) != 1) /* ¤@¯ë¤l­n¦b¹j¾À¤~¯à¦Y¹L¥h */
+ return NOTHING;
+
+ if (myColor == Black)
+ {
+ if (mych != 15 || yourch != 2) /* ¨ò¥i¥H¦Y«Ó */
+ {
+ if (mych - 7 > yourch) /* ¤p¤£¯à¦Y¤j */
+ return NOTHING;
+ if (mych == 9 && yourch == 8) /* ±N¤£¯à¦Y§L */
+ return NOTHING;
+ }
+ }
+ else
+ {
+ if (mych != 8 || yourch != 9) /* §L¥i¥H¦Y±N */
+ {
+ if (mych + 7 > yourch) /* ¤p¤£¯à¦Y¤j */
+ return NOTHING;
+ if (mych == 2 && yourch == 15) /* «Ó¤£¯à¦Y§L */
+ return NOTHING;
+ }
+ }
+ }
+
+ sprintf(buf, "E%d:%d", Focus, yourch * 256 + bwRow * 16 + bwCol);
+ if (!do_send(buf))
+ return DISCONNECT;
+ MyEat[myeat_index++] = yourch;
+ sprintf(buf, "\033[1;32m¡¿§Ú¤è²¾°Ê %s\033[1;32m(%d, %c) ¦Y %s\033[1;32m(%d, %c)\033[m",
+ ch_icon[mych], col, row + 'A', ch_icon[yourch], bwCol, bwRow + 'A');
+ strcpy(dstr[0], forspace);
+ outs(ch_brdline(0));
+ ch_printeat();
+ }
+
+ Board[bwRow][bwCol] = mych;
+ Board[row][col] = Empty;
+ mapTurn = yourTurn;
+ Focus = Empty;
+
+ ch_draw();
+ ch_printmsg(1, buf);
+
+ if (myeat_index == 16)
+ ch_overgame(myColor);
+
+ return NOTHING;
+}
+
+
+static int
+chCtrlS()
+{
+ if(!GameOver) {
+ if (!do_send("S"))
+ return DISCONNECT;
+ ch_overgame((myColor == Black) ? Red : Black);
+ }
+ return NOTHING;
+}
+
+
+static int
+chLEFT()
+{
+ if (bwCol > 0)
+ bwCol--;
+ return NOTHING;
+}
+
+
+static int
+chRIGHT()
+{
+ if (bwCol < 8 - Choose)
+ bwCol++;
+ return NOTHING;
+}
+
+
+static int
+chUP()
+{
+ if (bwRow > 0)
+ bwRow--;
+ return NOTHING;
+}
+
+
+static int
+chDOWN()
+{
+ if (bwRow < 9 - Choose * 6)
+ bwRow++;
+ return NOTHING;
+}
+
+
+static int
+chEnter()
+{
+ char buf[40], ch;
+
+ if(GameOver) return NOTHING;
+
+ ch = Board[bwRow][bwCol];
+ if (ch == Cover) /* ·t´Ñ½¤l */
+ {
+ ch = ch_rand();
+ Board[bwRow][bwCol] = ch;
+ sprintf(buf, "D%d", ch * 256 + bwRow * 16 + bwCol);
+ if (!do_send(buf))
+ return DISCONNECT;
+
+ if (!dark_choose) /* ·t´Ñ²Ä¤@¦¸Â½¤l¨M©wÃC¦â */
+ {
+ dark_choose = 1;
+ myColor = (ch < 9) ? Red : Black;
+ sprintf(buf, "F%d", myColor);
+ if (!do_send(buf))
+ return DISCONNECT;
+ }
+
+ mapTurn = yourTurn;
+ Focus = Empty;
+ sprintf(buf, "\033[1;32m¡¶§Ú¤è½¶} %s\033[1;32m(%d, %c)\033[m",
+ ch_icon[ch], bwCol, bwRow + 'A');
+ strcpy(dstr[0], forspace);
+ outs(ch_brdline(0));
+ ch_draw();
+ ch_printmsg(1, buf);
+ }
+ else
+ {
+ if (Focus) /* ­x/·t´Ñ²¾°Ê */
+ {
+ if (Focus == ch * 256 + bwRow * 16 + bwCol) /* ¦A¿ï¤@¹Mªí¥Ü¨ú®ø¿ï¨ú */
+ {
+ Focus = 0;
+ ch_printmsg(3, NULL);
+ }
+ else
+ return Choose ? ch_Mv1() : ch_Mv0();
+ }
+ else /* ­x/·t´Ñ¿ï¨ú */
+ {
+ if (ch != Empty && !ch_check(ch))
+ {
+ Focus = ch * 256 + bwRow * 16 + bwCol;
+ ch_printmsg(2, NULL);
+ }
+ }
+ }
+ return NOTHING;
+}
+
+
+static KeyFunc yourTurn[] =
+{
+ Ctrl('C'), ftnCtrlC,
+ Ctrl('D'), fCtrlD,
+ KEY_LEFT, chLEFT,
+ KEY_RIGHT, chRIGHT,
+ KEY_UP, chUP,
+ KEY_DOWN, chDOWN,
+ KEY_TAB, fTAB,
+ 0, fNoOp
+};
+
+
+static KeyFunc myTurn[] =
+{
+ Ctrl('C'), ftnCtrlC,
+ Ctrl('D'), fCtrlD,
+ Ctrl('S'), chCtrlS,
+ ' ', chEnter,
+ '\n', chEnter,
+ KEY_LEFT, chLEFT,
+ KEY_RIGHT, chRIGHT,
+ KEY_UP, chUP,
+ KEY_DOWN, chDOWN,
+ KEY_TAB, fTAB,
+ 0, fNoOp
+};
+
+
+/*-------------------------------------------------------*/
+/* ©Ò¦³´Ñ½L¤½¥Î¥Dµ{¦¡ */
+/*-------------------------------------------------------*/
+
+
+/* rule set */
+static int (**ruleSet[]) () =
+{
+ othRule, fivRule, blkRule, armyRule, darkRule
+};
+
+
+static void
+do_init()
+{
+ char *t, *mateid, msg[160], buf[80];
+ int i, myTotal, yourTotal, myWin, yourWin;
+
+ /* Initialize forspace */
+ memset(forspace, 32, 60);
+ forspace[60] = 0;
+
+ /* Initialize state */
+ (*rule[Binit]) ();
+ mapTalk = NULL; /* ¤@¶}©l¶i¤J¬O¦b´Ñ½L¤W */
+ GameOver = 0;
+
+ bwRow = bwCol = 0;
+ msgline = 1;
+
+ cmdCol = 0;
+ *cmdBuf = 0;
+ cmdPos = -1;
+
+ /* Initialize screen */
+ clear();
+
+ t = cuser.userid;
+ mateid = cutmp->mateid;
+
+ /* ¨ú±o¾ÔÁZ */
+ play_count(t, &myTotal, &myWin);
+ play_count(mateid, &yourTotal, &yourWin);
+
+ sprintf(buf, "¡¸%s(%d¾Ô%d³Ó) vs ¡¹%s(%d¾Ô%d³Ó) \033[m",
+ t, myTotal, myWin, mateid, yourTotal, yourWin);
+
+ sprintf(msg, "\033[1;33;44m¡i ¹ï«³%s ¡j", ruleStr);
+ i = 80 - strlen(buf) + 3 - strlen(msg) + 10;
+ t = str_tail(msg);
+ for (; i; i--)
+ *t++ = ' ';
+ strcpy(t, buf);
+ outs(msg);
+
+ (Choose < 0) ? bw_init() : ch_init();
+}
+
+
+static int
+main_board(sock, later)
+ int sock, later;
+{
+ screenline sl[T_LINES];
+ char c;
+ int ch;
+ KeyFunc *k;
+
+ vs_save(sl);
+ cfd = sock;
+
+ if (!later)
+ {
+ /* ask for which rule set */
+ /* assume: peer won't send char until setup */
+ c = vans("·Q¤U­þºØ´Ñ (1)¶Â¥Õ´Ñ (2)¤­¤l´Ñ (3)³ò´Ñ (4)­x´Ñ (5)·t´Ñ (Q)¨ú®ø¡H[Q] ");
+ if (c >= '1' && c <= '5')
+ {
+ c -= '1';
+ }
+ else
+ {
+ c = -1;
+ vs_restore(sl); /* lkchu.990428: §â foot restore ¦^¨Ó */
+ }
+
+ /* transmit rule set */
+ if (send(cfd, &c, 1, 0) != 1)
+ return DISCONNECT;
+
+ /* ±Ò°Ê¹CÀ¸ªÌ¬°¶Â¤l */
+ myColor = Black;
+ }
+ else
+ {
+ /* prompt for waiting rule set */
+ outz("¡¹ ¹ï¤è­n¨D¶i¤J¹ï«³¼Ò¦¡¿ï¾Ü¤¤¡A½Ðµy­Ô \033[5m...\033[m");
+ refresh();
+ /* receive rule set */
+ if (recv(cfd, &c, 1, 0) != 1)
+ return DISCONNECT;
+
+ vs_restore(sl); /* lkchu.990428: §â foot restore ¦^¨Ó */
+
+ /* ³Q±Ò°Ê¹CÀ¸ªÌ¬°¬õ(¥Õ)¤l */
+ myColor = White; /* White == Red */
+ }
+
+ if (c < 0)
+ return LEAVE;
+ rule = ruleSet[c];
+ ruleStr = ruleStrSet[c];
+
+ Choose = c - 3; /* -3:¶Â¥Õ´Ñ -2:¤­¤l´Ñ -1:³ò´Ñ 0:­x´Ñ 1:·t´Ñ */
+
+ /* initialize all */
+ do_init();
+
+ for (;;)
+ {
+ if (mapTalk)
+ {
+ move(b_lines - 2, cmdCol + 35);
+ k = mapTalk;
+ }
+ else
+ {
+ if (Choose < 0)
+ move(bwRow + 1, bwCol * 2 + 1);
+ else if (Choose == 0)
+ move(bwRow * 2 + 1, bwCol * 4 + 1);
+ else
+ move(bwRow * 2 + 2, bwCol * 4 + 3);
+
+ k = mapTurn;
+ }
+
+ ch = vkey();
+ if (ch == I_OTHERDATA)
+ { /* incoming */
+ ch = (Choose < 0) ? bw_recv() : ch_recv();
+ if (ch >= NOTHING) /* -1 for exit bwboard, -2 for exit talk */
+ continue;
+ vs_restore(sl);
+ return ch;
+ }
+
+#ifdef EVERY_Z
+ /* Thor: Chat ¤¤«ö ctrl-z */
+ else if (ch == Ctrl('Z'))
+ {
+ char buf[IDLEN + 1];
+ screenline slt[T_LINES];
+
+ /* Thor.980731: ¼È¦s mateid, ¦]¬°¥X¥h®É¥i¯à·|¥Î±¼ mateid */
+ strcpy(buf, cutmp->mateid);
+
+ vio_save(); /* Thor.980727: ¼È¦s vio_fd */
+ vs_save(slt);
+ every_Z(0);
+ vs_restore(slt);
+ vio_restore(); /* Thor.980727: ÁÙ­ì vio_fd */
+
+ /* Thor.980731: ÁÙ­ì mateid, ¦]¬°¥X¥h®É¥i¯à·|¥Î±¼ mateid */
+ strcpy(cutmp->mateid, buf);
+ continue;
+ }
+#endif
+
+ for (;; k++)
+ {
+ if (!k->key || ch == k->key)
+ break;
+ }
+
+ /* -1 for exit bwboard, -2 for exit talk */
+ if ((ch = k->key ? (*k->func) () : (*k->func) (ch)) >= NOTHING)
+ continue;
+ vs_restore(sl);
+ return ch;
+ }
+}
+
+#include <stdarg.h>
+
+int
+vaBWboard(pvar)
+ va_list pvar;
+{
+ int sock, later;
+ sock = va_arg(pvar, int);
+ later = va_arg(pvar, int);
+ return main_board(sock, later);
+}
+#endif /* HAVE_GAME */
diff --git a/game/chessmj.c b/game/chessmj.c
new file mode 100644
index 0000000..afca8a7
--- /dev/null
+++ b/game/chessmj.c
@@ -0,0 +1,1023 @@
+/*-------------------------------------------------------*/
+/* chessmj.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : ¶H´Ñ³Â±N¹CÀ¸ */
+/* create : 98/07/29 */
+/* update : 01/04/25 */
+/* author : weiren@mail.eki.com.tw */
+/* recast : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+
+#ifdef HAVE_GAME
+
+
+static int host_card[5]; /* ¹q¸£(²ø®a) ªºµP */
+static int guest_card[5]; /* ª±®aªºµP */
+static int throw[32]; /* ³Q¥á±óªºµP */
+
+static int flag;
+static int tflag;
+static int tflagA;
+static int tflagB;
+static int selftouch; /* ¦ÛºN */
+
+static int cnum[32] =
+{
+ 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
+ 7, 7, 7, 7, 7, 8, 9, 9, 10, 10,
+ 11, 11, 12, 12, 13, 13,
+ 14, 14, 14, 14, 14
+};
+
+static int group[32] =
+{
+ 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2,
+ 3, 3, 3, 3, 3, 4, 4, 4, 4, 4,
+ 5, 5, 5, 5, 5, 5,
+ 6, 6, 6, 6, 6
+};
+
+
+static void
+out_song()
+{
+ static int count = 0;
+
+ /* Ĭ¼z­Û£»¤£­n¦A»¡·R§Ú */
+ uschar *msg[8] =
+ {
+ "§Ñ±¼§a§Úªº¦Ñ±¡¤H §O¦b·N¤]§O»¡¥X¨Ó",
+ " ®É¶¡¤w¨R²H¤@¤Á ¤ß±¡¤]¨S¦³©u¸` §â¹L¥h©ñ¦^§A¤ß¤¤",
+ "¤£­n°Ý§Ú½Ö¹ï½Ö¿ù §Aªº·R¦b§Ú¦^¾Ð¤¤ ³o¨Ç¦~²ßºD¦Û¥Ñ",
+ "¤]¨S¦³¤Ó¦h·Ð¼~ ¨º¹L¥h´N¹³¤@°}­·",
+ "¤£­n¦A»¡·R§Ú ¤£­n¦A»¡·R§Ú",
+ "²{¦b§Ú­Ì¤§¶¡ ¥u¯à·í·íªB¤Í",
+ "¸¨¤U¤Ó¦h²´²\\ µ¥¹L¤Ó¦h¶Â©]",
+ "²{¦b§Ú¤@­Ó¤H ·R±¡§Ú¤£·Q°Ý"
+ };
+ move(b_lines - 2, 0);
+ prints("\033[1;3%dm%s\033[m Äw½XÁÙ¦³ %d ¤¸", time(0) % 7, msg[count], cuser.money);
+ clrtoeol();
+ if (++count == 8)
+ count = 0;
+}
+
+
+static void
+print_Sign(x, y)
+ int x, y;
+{
+ move(x, y);
+ outs("¢~¡´¢¡");
+}
+
+
+static void
+print_Schess(card, x, y)
+ int card, x, y;
+{
+ char *chess[33] =
+ {
+ "«Ó", "¥K", "¥K", "¬Û", "¬Û", "ÚÏ", "ÚÏ", "ØX", "ØX", "¬¶", "¬¶",
+ "§L", "§L", "§L", "§L", "§L",
+ "±N", "¤h", "¤h", "¶H", "¶H", "¨®", "¨®", "°¨", "°¨", "¥]", "¥]",
+ "¨ò", "¨ò", "¨ò", "¨ò", "¨ò", "¢æ"
+ };
+
+ move(x, y);
+ outs("¢~¢w¢¡");
+ move(x + 1, y);
+ prints("¢x%2s¢x", chess[card]);
+ move(x + 2, y);
+ outs("¢¢¢w¢£");
+}
+
+
+static inline void
+clear_5card()
+{
+ move(15, 24);
+ outs(" ");
+ move(16, 24);
+ outs(" ");
+ move(17, 24);
+ outs(" ");
+}
+
+
+static inline void
+Phost5()
+{
+ move(4, 24);
+ outs("¢~¢w¢¡");
+ move(5, 24);
+ outs("¢x ¢x");
+ move(6, 24);
+ outs("¢¢¢w¢£");
+}
+
+
+static inline void
+Chost5()
+{
+ move(4, 24);
+ outs(" ");
+ move(5, 24);
+ outs(" ");
+ move(6, 24);
+ outs(" ");
+}
+
+
+static inline void
+P_allchess()
+{
+ char *chess[33] = {
+ "«Ó", "¥K", "¥K", "¬Û", "¬Û", "ÚÏ", "ÚÏ", "ØX", "ØX", "¬¶", "¬¶",
+ "§L", "§L", "§L", "§L", "§L",
+ "±N", "¤h", "¤h", "¶H", "¶H", "¨®", "¨®", "°¨", "°¨", "¥]", "¥]",
+ "¨ò", "¨ò", "¨ò", "¨ò", "¨ò", ""
+ };
+
+ int i;
+
+ for (i = 0; i < 4; i++)
+ {
+ move(16, 2 + 6 * i);
+ outs(chess[guest_card[i]]);
+ }
+}
+
+
+static inline void
+sortchess()
+{
+ int i, j, x;
+ for (i = 0; i < 4; i++)
+ {
+ for (j = 0; j < (3 - i); j++)
+ {
+ if (guest_card[j] > guest_card[j + 1])
+ {
+ x = guest_card[j];
+ guest_card[j] = guest_card[j + 1];
+ guest_card[j + 1] = x;
+ }
+ if (host_card[j] > host_card[j + 1])
+ {
+ x = host_card[j];
+ host_card[j] = host_card[j + 1];
+ host_card[j + 1] = x;
+ }
+ }
+ }
+}
+
+
+static int
+testpair(a, b)
+ int a, b;
+{
+ if (cnum[a] == cnum[b])
+ return 1;
+ if (cnum[a] == 1 && cnum[b] == 8)
+ return 1;
+ if (cnum[a] == 8 && cnum[b] == 1)
+ return 1;
+ return 0;
+}
+
+
+static int
+testthree(a, b, c)
+ int a, b, c;
+{
+ int tmp;
+ if (a > b)
+ {
+ tmp = a;
+ a = b;
+ b = tmp;
+ }
+ if (b > c)
+ {
+ tmp = b;
+ b = c;
+ c = tmp;
+ }
+ if (a > b)
+ {
+ tmp = a;
+ a = b;
+ b = tmp;
+ }
+ if (cnum[a] == 1 && cnum[b] == 2 && cnum[c] == 3)
+ return 1; /* «Ó¥K¬Û */
+ if (cnum[a] == 4 && cnum[b] == 5 && cnum[c] == 6)
+ return 1; /* ÚÏØX¬¶ */
+ if (cnum[a] == 8 && cnum[b] == 9 && cnum[c] == 10)
+ return 1; /* ±N¤h¶H */
+ if (cnum[a] == 11 && cnum[b] == 12 && cnum[c] == 13)
+ return 1; /* ¨®°¨¥] */
+ if (cnum[a] == 7 && cnum[b] == 7 && cnum[c] == 7)
+ return 1; /* §L§L§L */
+ if (cnum[a] == 14 && cnum[b] == 14 && cnum[c] == 14)
+ return 1; /* ¨ò¨ò¨ò */
+ return 0;
+}
+
+
+static int
+testall(set)
+ int set[5];
+{
+ int i, j, k, m, p[3];
+ for (i = 0; i < 4; i++)
+ {
+ for (j = i + 1; j < 5; j++)
+ {
+ m = 0;
+ for (k = 0; k < 5; k++)
+ {
+ if (k != i && k != j)
+ {
+ p[m] = set[k];
+ m++;
+ }
+ }
+ if (testpair(set[i], set[j]) != 0 && testthree(p[0], p[1], p[2]) != 0)
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+static void
+printhostall()
+{
+ int i;
+ for (i = 0; i < 5; i++)
+ print_Schess(host_card[i], 4, 6 * i);
+}
+
+
+static void
+printhostfour()
+{
+ int i;
+ for (i = 0; i < 4; i++)
+ print_Schess(host_card[i], 4, 6 * i);
+}
+
+
+static int
+testlisten(set)
+ int set[4];
+{
+ int i, j, k, p[2] = {0}, m = 0, mm = 0;
+
+ j = 0;
+ for (i = 0; i < 4; i++)
+ {
+ if (group[set[i]] != 3)
+ j++;
+ } if (j == 0)
+ return 1; /* ¥|¤ä§L */
+ j = 0;
+ for (i = 0; i < 4; i++)
+ {
+ if (group[set[i]] != 6)
+ j++;
+ } if (j == 0)
+ return 1; /* ¥|¤ä¨ò */
+
+ if (testthree(set[1], set[2], set[3]) != 0)
+ return 1;
+ if (testthree(set[0], set[2], set[3]) != 0)
+ return 1;
+ if (testthree(set[0], set[1], set[3]) != 0)
+ return 1;
+ if (testthree(set[0], set[1], set[2]) != 0)
+ return 1; /* ¤T¤ä¦¨§Î«hÅ¥ */
+ for (i = 0; i < 3; i++)
+ {
+ for (j = i + 1; j < 4; j++)
+ {
+ if (testpair(set[i], set[j]))
+ { /* ¨â¤ä¦³­F¬Ý¥t¨â¤ä¦³¨S¦³Å¥ */
+ m = 0;
+ for (k = 0; k < 4; k++)
+ {
+ if (k != i && k != j)
+ {
+ p[m] = set[k];
+ m++;
+ }
+ if (group[set[i]] == 3 || group[set[i]] == 6)
+ mm = 1; /* ¦³­Fªº¬O§L©Î¨ò */
+ }
+ }
+ }
+ }
+ if (m != 0)
+ {
+ if ((group[p[0]] == group[p[1]]) && (cnum[p[0]] != cnum[p[1]]))
+ return 1; /* ¨â¤ä¬O pair ¥t¨â¤ä¦³Å¥ */
+ if ((group[p[0]] == group[p[1]] == 3) || (group[p[0]] == group[p[1]] == 6))
+ return 1;
+ if (testpair(p[0], p[1]) && mm == 1)
+ return 1;
+ }
+ return 0;
+}
+
+
+static inline void
+host_hula()
+{
+ print_Sign(11, (tflagB - 1) * 4); /* ¦L¾ßµP²Å¸¹ */
+ printhostall();
+}
+
+
+static inline void
+host_self()
+{
+ printhostall();
+}
+
+
+static int
+diecard(a) /* ¶Ç¶i¤@±iµP, ¬Ý¬O§_µ´±i */
+ int a;
+{
+ int i, k = 0;
+ for (i = 0; i < tflag; i++)
+ {
+ if (cnum[throw[i]] == cnum[a])
+ k++;
+ if (cnum[throw[i]] == 1 && cnum[a] == 8)
+ return 1;
+ if (cnum[throw[i]] == 8 && cnum[a] == 1)
+ return 1;
+ }
+ if ((cnum[a] == 7 || cnum[a] == 14) && k == 4)
+ return 1; /* §L¨òµ´±i */
+ if (k == 1 && (cnum[a] != 7 && cnum[a] != 14))
+ return 1;
+ return 0;
+}
+
+
+static inline int
+any_throw()
+{
+ int i, j, k, set[5] = {0}, tmp[4] = {0};
+ int point[5] = {0}; /* point[5] ¬°µû¤À¨t²Î, ¬Ý¥á­þ±iµP¤ñ¸û¦n, ¤À¼Æ°ªªºÀu¥ý¥á */
+
+ /* ´ú¸Õ±N¤â¤W¤­¤ä®³±¼¤@¤ä */
+ for (i = 0; i < 5; i++)
+ {
+ k = 0;
+ for (j = 0; j < 5; j++)
+ {
+ if (i != j)
+ {
+ tmp[k] = host_card[j];
+ k++;
+ }
+ }
+ if (testlisten(tmp)) /* ­Y³Ñ¾lªº¥|¤ä¤wÅ¥µP, ¥á¦hªº¨º±i */
+ {
+ point[i] += 10; /* ¦³Å¥´N¥[ 10 ¤À */
+ if (diecard(host_card[i]))
+ point[i] += 5; /* µ´±i§ó¸Ó¥á */
+ for (k = 0; k < 4; k++)
+ {
+ if (((cnum[host_card[i]] == cnum[tmp[k]]) ||
+ (cnum[tmp[k]] == 1 && cnum[host_card[i]] == 8) ||
+ (cnum[tmp[k]] == 8 && cnum[host_card[i]] == 1)) &&
+ cnum[host_card[i]] != 7 && cnum[host_card[i]] != 14)
+ point[i] += 10;
+ }
+ /* ¨®°¨¥]¥], ¥]¸Ó¥á */
+ }
+ }
+
+ k = 0;
+ for (i = 0; i < 5; i++)
+ {
+ if (cnum[host_card[i]] == 7)
+ k++; /* ºâ¦³´X¤ä§L */
+ }
+ if (k == 3) /* ¦³¤T¤ä§L: ³Ñ¤U¤G¤ä¤£¬O§Lªº¦U¥[ 5 ¤À */
+ {
+ for (i = 0; i < 5; i++)
+ {
+ if (cnum[host_card[i]] != 7)
+ point[i] += 5;
+ }
+ }
+ else if (k == 4) /* ¦³¥|¤ä§L */
+ {
+ if (diecard(12)) /* ¦ý³Ì«á¤@¤ä§L¤wµ´±i: ¥á§L */
+ {
+ for (i = 0; i < 5; i++)
+ {
+ if (cnum[host_card[i]] == 7)
+ point[i] += 9999;
+ }
+ }
+ else /* ³Ì«á¤@¤ä§L©|¥¼µ´±i: ¥á¤£¬O§Lªº¨º¤ä */
+ {
+ for (i = 0; i < 5; i++)
+ {
+ if (cnum[host_card[i]] != 7)
+ point[i] += 9999;
+ }
+ }
+ }
+
+ k = 0;
+ for (i = 0; i < 5; i++)
+ {
+ if (cnum[host_card[i]] == 14)
+ k++; /* ºâ¦³´X¤ä¨ò */
+ }
+ if (k == 3) /* ¦³¤T¤ä¨ò: ³Ñ¤U¤G¤ä¤£¬O¨òªº¦U¥[ 5 ¤À */
+ {
+ for (i = 0; i < 5; i++)
+ {
+ if (cnum[host_card[i]] != 14)
+ point[i] += 5;
+ }
+ }
+ else if (k == 4) /* ¦³¥|¤ä¨ò */
+ {
+ if (diecard(28)) /* ¦ý³Ì«á¤@¤ä¨ò¤wµ´±i: ¥á¨ò */
+ {
+ for (i = 0; i < 5; i++)
+ {
+ if (cnum[host_card[i]] == 14)
+ point[i] += 9999;
+ }
+ }
+ else /* ³Ì«á¤@¤ä¨ò©|¥¼µ´±i: ¥á¤£¬O¨òªº¨º¤ä */
+ {
+ for (i = 0; i < 5; i++)
+ {
+ if (cnum[host_card[i]] != 14)
+ point[i] += 9999;
+ }
+ }
+ }
+
+ for (i = 0; i < 4; i++)
+ {
+ for (j = i + 1; j < 5; j++)
+ {
+ if (group[host_card[i]] == group[host_card[j]])
+ {
+ point[i] -= 2;
+ point[j] -= 2; /* ®t¤@¤ä¦¨¤Tªº¤£¥á */
+ }
+ if (testpair(host_card[i], host_card[j]))
+ {
+ point[i] -= 2;
+ point[j] -= 2; /* ¦³­Fªº¤£¥á */
+ }
+ }
+ }
+
+#if 1 /* ­A½â, ¦pªG¥á¤F·|³Q­J´N¦º³£¤£¥á, ­A½â¾÷²v 5/6 */
+ for (i = 0; i < 4; i++)
+ set[i] = guest_card[i];
+ for (i = 0; i < 5; i++)
+ {
+ set[4] = host_card[i];
+ if (testall(set) && rnd(6))
+ point[i] = -9999;
+ }
+#endif
+
+ /* §ä¥X¤À¼Æ³Ì°ªªº */
+ j = 0;
+ k = point[0];
+ for (i = 1; i < 5; i++)
+ {
+ if (point[i] >= k)
+ {
+ k = point[i];
+ j = i;
+ }
+ }
+ return j;
+}
+
+
+static int
+count_tai(set)
+ int set[5]; /* Ĺªº¤­±iµP */
+{
+ char *name[10] =
+ {
+ "±N«Ó¹ï", "±N¤h¶H", "«Ó¥K¬Û",
+ "¤­§L¦XÁa", "¤­¨ò³s¾î", "¤T§L¤J¦C", "¤T¨ò¤J¦C",
+ "®ü©³", "¤Ñ­J", "¦ÛºN"
+ };
+
+ int tai[10] = /* ¥x¼Æ¹ïÀ³¤W­±ªº±Ô­z */
+ {
+ 2, 1, 1,
+ 5, 5, 2, 2,
+ 3, 5, 1
+ };
+
+ int yes[10] = {0};
+ int i, j, k, sum;
+
+ if (selftouch)
+ yes[9] = 1; /* ¦ÛºN */
+
+ if (flag == 32)
+ yes[7] = 1; /* ®ü©³ */
+ else if (tflag <= 1)
+ yes[8] = 1; /* ¤Ñ­J */
+
+ for (i = 0, j = 0, k = 0; i < 5; i++)
+ {
+ /* ºâ «Ó/±N ªº¤ä¼Æ */
+ if (cnum[set[i]] == 1)
+ j++;
+ if (cnum[set[i]] == 8)
+ k++;
+ }
+ if (j)
+ {
+ if (k)
+ yes[0] = 1; /* ¦³«Ó¤S¦³±N´N¬O ±N«Ó¹ï */
+ else
+ yes[2] = 1; /* ¦³«Ó¨S¦³±N´N¬O «Ó¥K¬Û */
+ }
+ else if (k)
+ {
+ yes[1] = 1; /* ¦³±N¨S¦³«Ó´N¬O ±N¤h¶H */
+ }
+
+ for (i = 0, j = 0; i < 5; i++)
+ {
+ /* ºâ §L ªº¤ä¼Æ */
+ if (cnum[set[i]] == 7)
+ j++;
+ }
+ if (j == 5)
+ yes[3] = 1; /* ¤­§L¦XÁa */
+ else if (j == 3)
+ yes[5] = 1; /* ¤T§L¤J¦C */
+
+ for (i = 0, j = 0; i < 5; i++)
+ {
+ /* ºâ ¨ò ªº¤ä¼Æ */
+ if (cnum[set[i]] == 14)
+ j++;
+ }
+ if (j == 5)
+ yes[4] = 1; /* ¤­¨ò³s¾î */
+ else if (j == 3)
+ yes[6] = 1; /* ¤T¨ò¤J¦C */
+
+ /* ºâ¥x¼Æ */
+ sum = 0;
+ for (i = 0; i < 10; i++)
+ {
+ if (yes[i])
+ sum += tai[i];
+ }
+
+ /* ¦C¦L¥X¼ú¶µ */
+ move(b_lines - 5, 0);
+ outs("¢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¢{\n");
+ for (i = 0; i < 5; i++)
+ {
+ if (yes[i])
+ prints(" %8s [%d ¥x]", name[i], tai[i]);
+ }
+ move(b_lines - 3, 0);
+ for (i = 5; i < 10; i++)
+ {
+ if (yes[i])
+ prints(" %8s [%d ¥x]", name[i], tai[i]);
+ }
+ move(b_lines - 2, 0);
+ clrtoeol(); /* ²M°£ out_song() */
+ prints(" ©³ [2 ¥x] ¦X­p [%d ¥x]\n", sum += 2);
+ 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¢}");
+
+ return sum;
+}
+
+
+int
+main_chessmj()
+{
+ int money; /* ©ãª÷ */
+ int mo; /* 1:¤wºNµP 0:¥¼ºNµP */
+ int picky; /* 1:¾ß§O¤HªºµP 0:¦Û¤vºNªº */
+ int pickup;
+ int listen; /* 1:Å¥µP 0:¨S¦³Å¥µP */
+ int chesslist[32]; /* 32 ±iµP²Õ */
+
+ int i, j, k, m;
+ int jp, x, xx, ch, z;
+
+ char ans[10], *msg;
+ int tmp[4];
+
+ if (HAS_STATUS(STATUS_COINLOCK))
+ {
+ vmsg(msg_coinlock);
+ return XEASY;
+ }
+
+ while (1)
+ {
+ vs_bar("¶H´Ñ³Â±N");
+
+ out_song();
+
+ vget(2, 0, "½Ð°Ý­n¤Uª`¦h¤Ö©O¡H(1 ~ 50000) ", ans, 6, DOECHO);
+ money = atoi(ans);
+ if (money < 1 || money > 50000 || money * 10 > cuser.money)
+ {
+ vmsg("­n¦³¤Q­¿ªº½äª÷¤~¥i¥H¶i¨Ó³á...¡I"); /* ½ß¿ú³Ì¦h½ß¤Q­¿ */
+ break; /* Â÷¶}½ä³õ */
+ }
+ cuser.money -= money; /* ¦©¤@¥÷½äª÷¡Aª±®a¦pªG¤¤³~Â÷¶}±N®³¤£¦^½äª÷ */
+
+ move(2, 0);
+ clrtoeol(); /* ²M±¼¡u½Ð°Ý­n¤Uª`¦h¤Ö¡v */
+ outs("(«ö ¡ö¡÷¿ïµP, ¡ô¥áµP, «ö ENTER ­JµP)");
+
+ for (i = 0; i < 32; i++) /* µP¥ý¤@±i¤@±i±Æ¦n¡A·Ç³Æ¬~µP */
+ chesslist[i] = i;
+
+ for (i = 0; i < 31; i++)
+ {
+ j = rnd(32 - i) + i;
+
+ /* chesslist[j] ©M chesslist[i] ¥æ´« */
+ m = chesslist[i];
+ chesslist[i] = chesslist[j];
+ chesslist[j] = m;
+ }
+
+ for (i = 0; i < 32; i++) /* ÁÙ¨S¦³µP³Q¥á±ó */
+ throw[i] = 32;
+
+ selftouch = 0; /* Âk¹s */
+ mo = 0;
+ pickup = 0;
+ picky = 0;
+ listen = 0;
+ flag = 0;
+ tflag = 0;
+ tflagA = 0;
+ tflagB = 0;
+
+ for (i = 0; i < 4; i++) /* µo«e¥|±iµP */
+ {
+ host_card[i] = chesslist[flag];
+ flag++;
+ guest_card[i] = chesslist[flag];
+ flag++;
+ }
+
+ for (i = 0; i < 4; i++) /* ±Æ§Ç */
+ {
+ for (j = 0; j < (3 - i); j++)
+ {
+ if (guest_card[j] > guest_card[j + 1])
+ {
+ m = guest_card[j];
+ guest_card[j] = guest_card[j + 1];
+ guest_card[j + 1] = m;
+ }
+ if (host_card[j] > host_card[j + 1])
+ {
+ m = host_card[j];
+ host_card[j] = host_card[j + 1];
+ host_card[j + 1] = m;
+ }
+ }
+ }
+
+ move(4, 0);
+ outs("¢~¢w¢¡¢~¢w¢¡¢~¢w¢¡¢~¢w¢¡");
+ move(5, 0);
+ outs("¢x ¢x¢x ¢x¢x ¢x¢x ¢x");
+ move(6, 0);
+ outs("¢¢¢w¢£¢¢¢w¢£¢¢¢w¢£¢¢¢w¢£");
+
+ for (i = 0; i < 4; i++)
+ print_Schess(guest_card[i], 15, 6 * i); /* ¦L¥X«e¥|±iµP */
+
+ for (;;)
+ {
+ jp = 5;
+ x = 0;
+ z = 1;
+ move(18, 26);
+ do
+ {
+ if (!mo)
+ {
+ move(14, 24);
+ outs("«ö¥ô¤@ÁäºNµP(©Î ¡õ ¾ßµP)");
+ }
+ else
+ {
+ move(14, 0);
+ clrtoeol();
+ }
+ move(18, 2 + (jp - 1) * 6);
+ outs("¡¶");
+ move(18, 3 + (jp - 1) * 6); /* Á×§K¥þ§Î°»´ú */
+
+ ch = vkey();
+
+ if (ch != '\n' && flag == 32)
+ {
+ msg = "¬y§½";
+ goto next_game;
+ }
+ if (!mo && ch != KEY_DOWN && ch != '\n')
+ {
+ ch = 'p'; /* ¥|±iµP«h±j¨îºNµP */
+ }
+
+ switch (ch)
+ {
+ case KEY_RIGHT:
+ move(18, 2 + (jp - 1) * 6);
+ outs(" ");
+ jp += 1;
+ if (jp > 5)
+ jp = 5;
+ move(18, 2 + (jp - 1) * 6);
+ outs("¡¶");
+ move(18, 3 + (jp - 1) * 6); /* Á×§K¥þ§Î°»´ú */
+ break;
+
+ case KEY_LEFT:
+ move(18, 2 + (jp - 1) * 6);
+ outs(" ");
+ jp -= 1;
+ if (jp < 1)
+ jp = 1;
+ move(18, 2 + (jp - 1) * 6);
+ outs("¡¶");
+ move(18, 3 + (jp - 1) * 6); /* Á×§K¥þ§Î°»´ú */
+ break;
+
+ case KEY_UP: /* ¥XµP */
+ move(18, 2 + (jp - 1) * 6);
+ outs(" ");
+ throw[tflag] = guest_card[jp - 1];
+ tflag++;
+ tflagB++;
+ z = 0;
+ mo = 0;
+ guest_card[jp - 1] = guest_card[4];
+ guest_card[4] = 0;
+ sortchess();
+ clear_5card();
+ P_allchess();
+ print_Schess(throw[tflag - 1], 11, (tflagB - 1) * 4);
+ picky = 0;
+ break;
+
+ case 'p': /* ºNµP */
+ if (!mo)
+ {
+ move(18, 2 + (jp - 1) * 6);
+ outs(" ");
+ guest_card[4] = chesslist[flag];
+ flag++;
+ print_Schess(guest_card[4], 15, 24);
+ mo = 1;
+ }
+ break;
+
+ case KEY_DOWN:
+ if (tflag > 0 && !mo)
+ {
+ guest_card[4] = throw[tflag - 1];
+ print_Sign(8, (tflagA - 1) * 4);
+ print_Schess(guest_card[4], 15, 24);
+ mo = 1;
+ picky = 1;
+ }
+ break;
+
+ case 'q':
+ return 0;
+ goto abort_game;
+ break;
+
+ case '\n':
+ if (testall(guest_card) && mo && !picky)
+ {
+ printhostfour();
+ msg = "«z«¨¦ÛºN°Õ¡I";
+ addmoney(money * count_tai(guest_card));
+ goto next_game;
+ }
+ else if (picky && testall(guest_card))
+ {
+ printhostfour();
+ msg = "¬Ý§Úªº¼F®`¡A­J°Õ¡I";
+ addmoney(money * count_tai(guest_card));
+ goto next_game;
+ }
+
+ if (tflag > 0 && !mo)
+ {
+ i = guest_card[4];
+ guest_card[4] = throw[tflag - 1];
+ if (testall(guest_card) == 1)
+ {
+ print_Sign(8, (tflagA - 1) * 4);
+ print_Schess(guest_card[4], 15, 24);
+ printhostfour();
+ msg = "­J¡I";
+ addmoney(money * count_tai(guest_card));
+ goto next_game;
+ }
+ guest_card[4] = i;
+ }
+ break;
+
+ default:
+ break;
+ }
+ } while (z == 1);
+
+ host_card[4] = throw[tflag - 1];
+ if (testall(host_card))
+ {
+ host_hula();
+ msg = "¹q¸£­J°Õ¡I";
+ cuser.money -= money * count_tai(host_card); /* ¹q¸£¥x¼Æ¶V¦h´N½ß¶V¦h¡A¥B©ãª÷¨S¦¬ */
+ if (cuser.money < 0)
+ cuser.money = 0;
+ goto next_game;
+ }
+
+ if (tflag == 32)
+ {
+ msg = "¬y§½";
+ goto next_game;
+ }
+
+ host_card[4] = chesslist[flag];
+ if (testall(host_card))
+ {
+ host_self();
+ msg = "¹q¸£¦ÛºN¡I";
+ cuser.money -= money * count_tai(host_card); /* ¹q¸£¥x¼Æ¶V¦h´N½ß¶V¦h¡A¥B©ãª÷¨S¦¬ */
+ if (cuser.money < 0)
+ cuser.money = 0;
+ goto next_game;
+ }
+
+ for (i = 0; i < 4; i++)
+ tmp[i] = host_card[i];
+
+ if (!testlisten(tmp))
+ { /* ¨SÅ¥ªº¸Ü */
+ for (i = 0; i < 4; i++)
+ {
+ k = 0;
+ for (j = 0; j < 4; j++)
+ {
+ if (i != j)
+ {
+ tmp[k] = host_card[j];
+ k++;
+ }
+ }
+ tmp[3] = throw[tflag - 1]; /* §â¾ß°_¨º±i¸ò¤â¤WªºµP¤ñ¹ï */
+ if (testlisten(tmp))
+ { /* ¾ßµP¦³Å¥ªº¸Ü */
+ listen = 1;
+ host_card[4] = throw[tflag - 1];
+ tflag--;
+ print_Sign(11, (tflagB - 1) * 4); /* ¦L¾ßµP²Å¸¹ */
+ xx = i; /* ¬ö¿ý¤U­n¥áªº¨º±iµP */
+ pickup = 1;
+ break; /* ¸õ¥X i loop */
+ }
+ }
+ }
+
+ for (i = 0; i < 4; i++)
+ tmp[i] = host_card[i];
+ if (testlisten(tmp) && !pickup)
+ { /* ¦³Å¥¥B­è­è¨S¾ß */
+ m = 0;
+ for (i = 0; i < 4; i++)
+ if (cnum[tmp[i]] == 7)
+ m++;
+ if (m == 2 && cnum[throw[tflag - 1]] == 7)
+ pickup = 1;
+ if (m == 3 && cnum[throw[tflag - 1]] == 7)
+ {
+ pickup = 1;
+ for (i = 0; i < tflag - 1; i++)
+ if (cnum[throw[i]] == 7)
+ pickup = 0;
+ }
+ m = 0;
+ for (i = 0; i < 4; i++)
+ if (cnum[tmp[i]] == 14)
+ m++;
+ if (m == 2 && cnum[throw[tflag - 1]] == 14)
+ pickup = 1;
+ if (m == 3 && cnum[throw[tflag - 1]] == 14)
+ {
+ pickup = 1;
+ for (i = 0; i < tflag - 1; i++)
+ if (cnum[throw[i]] == 14)
+ pickup = 0;
+ }
+ if (pickup)
+ {
+ host_card[4] = throw[tflag - 1];
+ tflag--;
+ print_Sign(11, (tflagB - 1) * 4); /* ¦L¾ßµP²Å¸¹ */
+ }
+ }
+
+
+ if (!pickup)
+ {
+ host_card[4] = chesslist[flag];
+ flag++;
+ }
+ /* ­è­è¨S¾ßµP²{¦b´NºNµP */
+ Phost5();
+ Chost5();
+
+ if (!pickup)
+ {
+ for (i = 0; i < 4; i++)
+ {
+ k = 0;
+ for (j = 0; j < 4; j++)
+ {
+ if (i != j)
+ {
+ tmp[k] = host_card[j];
+ k++;
+ }
+ }
+ tmp[3] = host_card[4];
+ if (testlisten(tmp))
+ { /* ºNµP¦³Å¥ªº¸Ü */
+ listen = 1;
+ xx = i; /* ¬ö¿ý¤U­n¥áªº¨º±iµP */
+ break; /* ¸õ¥X i loop */
+ }
+ }
+ }
+
+ for (i = 0; i < 4; i++)
+ tmp[i] = host_card[i];
+
+ xx = any_throw();
+
+ throw[tflag] = host_card[xx];
+ tflag++;
+ tflagA++;
+ host_card[xx] = host_card[4]; /* ¥á¥X¨SÅ¥¨º±i */
+ print_Schess(throw[tflag - 1], 8, (tflagA - 1) * 4);
+
+ pickup = 0;
+ listen = 0;
+
+ } /* for °j°éµ²§ô */
+
+next_game:
+ vmsg(msg);
+
+ } /* while °j°éµ²§ô */
+
+abort_game:
+ return 0;
+}
+#endif /* HAVE_GAME */
diff --git a/game/dice.c b/game/dice.c
new file mode 100644
index 0000000..3bd949a
--- /dev/null
+++ b/game/dice.c
@@ -0,0 +1,178 @@
+/*-------------------------------------------------------*/
+/* dice.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : ÂY»ë¤l¹CÀ¸ */
+/* create : 01/02/15 */
+/* update : 01/04/20 */
+/* author : wsyfish */
+/* recast : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+#ifdef HAVE_GAME
+
+
+static char *pic[6][3] =
+{
+ " ",
+ " ¡´ ", /* 1 */
+ " ",
+
+ " ¡´ ",
+ " ", /* 2 */
+ " ¡´ ",
+
+ "¡´ ",
+ " ¡´ ", /* 3 */
+ " ¡´",
+
+ "¡´ ¡´",
+ " ", /* 4 */
+ "¡´ ¡´",
+
+ "¡´ ¡´",
+ " ¡´ ", /* 5 */
+ "¡´ ¡´",
+
+ "¡´ ¡´",
+ "¡´ ¡´", /* 6 */
+ "¡´ ¡´"
+};
+
+
+static void
+out_song()
+{
+ static int count = 0;
+
+ /* ¶O¥É²M£»¶}¤@®°¤ßµ¡ */
+ uschar *msg[11] =
+ {
+ "¶}¤@®°¤ßµ¡ ­¼µÛ¹Úªº¯Í»H­¸µ¾",
+ "ÂàÀþ¶¡´N¯à¨ì¹F",
+ "¶}¤@®°¤ßµ¡ ¤£­n¦b¶Â·t¤¤·K´a",
+ "´§§O¤Fµh­W¤ß»Ä",
+ "Åý¥Í¬¡±q¦¹¹L±o²³æ ³¬¤W²´·ú´N¥i¥H·Q¹³",
+ "¾C¹CµLÃäµL»Ú¯EÃv®ü¬v ¬Ý¨£ÂŤѤ£¦A°g±¦",
+ "­n¦h«º¦h±m¦Û¥Ñ©b©ñ",
+ "¶§¥ú »´»´¦a¶}¤@®°¤ßµ¡",
+ "¥^¥^¦a±a§Ú­Ì¨«¥X¶Â·t ¾ã­Ó¥@¬É³£ÀéÄê½÷·×",
+ "¶§¥ú »´»´¦a¶}¤@®°¤ßµ¡",
+ "¬X¬X¦a°{Ä£µÛ¬üÄR¹Ú·Q ©Ò¦³Åw¼ÖÄ@»P§A¤À¨É"
+ };
+ move(b_lines - 2, 0);
+ prints("\033[1;3%dm%s\033[m Äw½XÁÙ¦³ %d ¤¸", time(0) % 7, msg[count], cuser.money);
+ clrtoeol();
+ if (++count == 11)
+ count = 0;
+}
+
+
+int
+main_dice()
+{
+ int money; /* ©ãª÷ */
+ int i; /* ¶Ã¼Æ */
+ char choice; /* °O¿ý¿ï¶µ */
+ char dice[3]; /* ¤T­Ó»ë¤lªº­È */
+ char total; /* ¤T­Ó»ë¤lªº©M */
+ char buf[60];
+
+ if (HAS_STATUS(STATUS_COINLOCK))
+ {
+ vmsg(msg_coinlock);
+ return XEASY;
+ }
+
+ vs_bar("£££¸ £t£« £{£« ¤Uª`");
+ outs("\n\n\n"
+ "¢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¢w¢w¢w¢{\n"
+ "¢x 2­¿ 1. ¤j 2. ¤p ¢x\n"
+ "¢x 14­¿ 3. ¤TÂI 4. ¥|ÂI 5. ¤­ÂI 6. ¤»ÂI 7. ¤CÂI ¢x\n"
+ "¢x 8­¿ 8. ¤KÂI 9. ¤EÂI 10. ¤QÂI 11. ¤Q¤@ÂI 12. ¤Q¤GÂI 13. ¤Q¤TÂI¢x\n"
+ "¢x 14­¿ 14. ¤Q¥|ÂI 15. ¤Q¤­ÂI 16. ¤Q¤»ÂI 17. ¤Q¤CÂI 18. ¤Q¤KÂI ¢x\n"
+ "¢x216­¿ 19. ¤@¤@¤@ 20. ¤G¤G¤G 21. ¤T¤T¤T 22. ¥|¥|¥| 23. ¤­¤­¤­ 24. ¤»¤»¤»¢x\n"
+ "¢|¢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¢}\n");
+
+#if 0 /* ÂY»ë¤l¨C 216 ¦¸¦UÁ`¼Æ¥X²{ªº¦¸¼Æ¾÷²v */
+¢z¢w¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢{
+¢xÁ`¼Æ¢x3 ¢x4 ¢x5 ¢x6 ¢x7 ¢x8 ¢x9 ¢x10¢x11¢x12¢x13¢x14¢x15¢x16¢x17¢x18¢x
+¢u¢w¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢t
+¢x¦¸¼Æ¢x1 ¢x3 ¢x6 ¢x10¢x15¢x21¢x25¢x27¢x27¢x25¢x21¢x15¢x10¢x6 ¢x3 ¢x1 ¢x / 216 ¦¸
+¢|¢w¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢}
+#endif
+
+ out_song(0);
+
+ while (1)
+ {
+ vget(2, 0, "½Ð°Ý­n¤Uª`¦h¤Ö©O¡H(1 ~ 50000) ", buf, 6, DOECHO);
+ money = atoi(buf);
+ if (money < 1 || money > 50000 || money > cuser.money)
+ break; /* Â÷¶}½ä³õ */
+
+ vget(12, 0, "­n©ã­þ¤@¶µ©O¡H(½Ð¿é¤J¸¹½X) ", buf, 3, DOECHO);
+ choice = atoi(buf);
+ if (choice < 1 || choice > 24)
+ break; /* Â÷¶}½ä³õ */
+
+ outs("\n«ö¥ô¤@ÁäÂY¥X»ë¤l \033[5m....\033[m\n");
+ igetch();
+
+ /* ¨M©w¤T­Ó»ë¤lÂI¼Æ */
+ total = 0;
+ for (i = 0; i < 3; i++)
+ {
+ dice[i] = rnd(6) + 1;
+ total += dice[i];
+ }
+
+ /* ³B²zµ²ªG */
+ if ((choice == 1 && total > 10) || (choice == 2 && total <= 10)) /* ³B²z¤j¤p */
+ {
+ sprintf(buf, "¤¤¤F¡I±o¨ì¢±­¿¼úª÷ %d ¤¸", money * 2);
+ addmoney(money);
+ }
+ else if (choice <= 18 && total == choice) /* ³B²zÁ`©M */
+ {
+ if (choice >= 8 && choice <= 13)
+ {
+ sprintf(buf, "¤¤¤F¡I±o¨ì¢·­¿¼úª÷ %d ¤¸", money * 8);
+ addmoney(money * 7);
+ }
+ else
+ {
+ sprintf(buf, "¤¤¤F¡I±o¨ì¢°¢³­¿¼úª÷ %d ¤¸", money * 14);
+ addmoney(money * 13);
+ }
+ }
+ else if ((choice - 18) == dice[0] && (dice[0] == dice[1]) && (dice[1] == dice[2]))/* ³B²z¤T­Ó¤@¼Ë */
+ {
+ sprintf(buf, "¤¤¤F¡I±o¨ì¢±¢°¢µ­¿¼úª÷ %d ¤¸", money * 216);
+ addmoney(money * 215);
+ }
+ else /* ³B²z¨S¤¤ */
+ {
+ strcpy(buf, "«Ü¥i±¤¨S¦³©ã¤¤¡I");
+ cuser.money -= money;
+ }
+
+ /* ¦L¥X»ë¤lµ²ªG */
+ outs("¢~¢w¢w¢w¢w¢¡¢~¢w¢w¢w¢w¢¡¢~¢w¢w¢w¢w¢¡\n");
+ for (i = 0; i < 3; i++)
+ {
+ prints("¢x%s¢x¢x%s¢x¢x%s¢x\n", pic[dice[0] - 1][i],
+ pic[dice[1] - 1][i], pic[dice[2] - 1][i]);
+ }
+ outs("¢¢¢w¢w¢w¢w¢£¢¢¢w¢w¢w¢w¢£¢¢¢w¢w¢w¢w¢£\n\n");
+
+ out_song();
+ vmsg(buf);
+ }
+
+ return 0;
+}
+
+#endif /* HAVE_GAME */
diff --git a/game/dict.c b/game/dict.c
new file mode 100644
index 0000000..1732c80
--- /dev/null
+++ b/game/dict.c
@@ -0,0 +1,338 @@
+/*-------------------------------------------------------*/
+/* target : Yahoo ½u¤W¦r¨å */
+/* create : 01/07/09 */
+/* update : 06/01/16 */
+/* author : statue.bbs@bbs.yzu.edu.tw */
+/* change : kyo@cszone.tw */
+/*-------------------------------------------------------*/
+
+#if 0
+
+ ´¶³q
+ http://tw.dictionary.yahoo.com/search?ei=big5&p=hello
+
+ ¦P¸q¦r/¤Ï¸q¦r
+ http://tw.dictionary.yahoo.com/search?ei=big5&p=hello&type=t
+
+ ÅܤƧÎ
+ http://tw.dictionary.yahoo.com/search?ei=big5&p=hello&type=v
+
+#endif
+
+
+#include "bbs.h"
+
+#ifdef HAVE_NETTOOL
+
+#define mouts(y,x,s) { move(y, x); outs(s); }
+
+#define HTTP_PORT 80
+#define SERVER_yahoo "tw.dictionary.yahoo.com"
+#define CGI_yahoo "/search?ei=big5&p="
+#define CGI_yahoo2 "&type="
+#define REF "http://tw.dictionary.yahoo.com/"
+#define Dict_Name "Yahoo! ½u¤W¦r¨å"
+
+static void
+url_encode(dst, src) /* URL encoding */
+ uschar *dst; /* Thor.990331: ­n src ªº¤T­¿ªÅ¶¡ */
+ uschar *src;
+{
+ for (; *src; src++)
+ {
+ if (*src == ' ')
+ *dst++ = '+';
+ else if (is_alnum(*src))
+ *dst++ = *src;
+ else
+ {
+ register cc = *src;
+ *dst++ = '%';
+ *dst++ = radix32[cc >> 4];
+ *dst++ = radix32[cc & 0xf];
+ }
+ }
+ *dst = '\0';
+}
+
+
+static int
+write_file(type, sockfd, fp, ftmp)
+ char type;
+ int sockfd;
+ FILE *fp;
+ char *ftmp;
+{
+ static char pool[2048];
+ int cc, i, j, fsize;
+ char *xhead, *fimage;
+ int show, start_show;
+ int space; /* ¦b html ¤¤¡A³sÄòªº space ¥u·|ºâ¤@¦¸ */
+
+ char *start_str[] =
+ {
+ "d", "<div class=pcixin>",
+ "d", "<div class=chinese>",
+ "t", "<h4>",
+ "v", "<h4>",
+ NULL
+ };
+
+ char *stop_str[] =
+ {
+ "d", "</h2>",
+ "A", "<h4>",
+ "A", "</blockquote>",
+ NULL
+ };
+
+ char *newline_str[] = /* ¨ú¥N´«¦æ¦r¤¸ªº²Å¸¹ */
+ {
+ "<br>",
+ "</div>",
+ "</li>",
+ NULL
+ };
+
+ char *other_str[] = /* ¨ä¥L¦r¤¸²Å¸¹ */
+ {
+ "A", "&nbsp;", " ",
+ "d", "<li><div class=pexplain>", "\033[m\n \033[1;33m£»\033[32m",
+ "d", "<li>\n<div class=pexplain>", "\033[m\n \033[1;33m£»\033[32m",
+ "d", "<li>", "\033[m\n \033[1;33m£»\033[32m",
+ "d", "<div class=pexplain>", "\033[m\n ",
+ "d", "<div class=pcixin>", "\033[1;31m",
+ "d", "<span id=dropdownid>", "\033[m \033[1;37m",
+ "d", "<div class=ptitle>", "\033[1;36m",
+ "t", "<div class=ptitle>", "\033[m\n\n\033[1;36m",
+ "t", "<div class=pexplain>", "\033[m\n",
+ "v", "<div class=ptitle>", "\033[m\n\n\033[1;36m",
+ "v", "<div class=pexplain>", "\033[m\n",
+ NULL
+ };
+
+ FILE *fpw;
+
+ fpw = fopen(ftmp, "w");
+
+ while ((cc = read(sockfd, &pool, sizeof(pool))) > 0)
+ fwrite(&pool, cc, 1, fpw);
+
+ fclose(fpw);
+ close(sockfd);
+
+ sprintf(pool, "/usr/local/bin/autob5 -i utf8 -o big5 < %s > %s.big5",
+ ftmp, ftmp);
+ system(pool);
+ unlink(ftmp);
+ strcat(ftmp, ".big5");
+
+ fimage = f_map(ftmp, &fsize);
+ if (fimage == (char *) -1)
+ {
+ fclose(fp);
+ vmsg("¥Ø«eµLªk¶}±Ò Yahoo ½u¤W¦r¨å¼È¦sÀÉ");
+ return -1;
+ }
+
+ /* parser return message from web server */
+ show = 1;
+ start_show = 0;
+ space = 0;
+
+ for (xhead = fimage; *xhead; xhead++)
+ {
+ if (!start_show)
+ {
+ for (i = 0; start_str[i] != NULL; i += 2)
+ {
+ j = strlen(start_str[i + 1]);
+ if ((start_str[i][0] == type || start_str[i][0] == 'A') &&
+ str_ncmp(xhead, start_str[i + 1], j) == 0)
+ {
+ fputs("\033[m\n\n\033[1;31m", fp);
+ start_show = 1;
+ xhead += j;
+ break;
+ }
+ }
+ }
+ else if (start_show)
+ {
+ for (i = 0; stop_str[i] != NULL; i += 2)
+ {
+ j = strlen(stop_str[i + 1]);
+ if ((stop_str[i][0] == type || stop_str[i][0] == 'A') &&
+ str_ncmp(xhead, stop_str[i + 1], j) == 0)
+ {
+ start_show = 0;
+ xhead += j;
+ break;
+ }
+ }
+ }
+
+ if (!start_show)
+ continue;
+
+ for (i = 0; newline_str[i] != NULL; i++)
+ {
+ j = strlen(newline_str[i]);
+ if (str_ncmp(xhead, newline_str[i], j) == 0)
+ {
+ fputs("\033[m\n", fp);
+ xhead += j;
+ space = 0;
+ break;
+ }
+ }
+
+
+ for (i = 0; other_str[i] != NULL; i += 3)
+ {
+ j = strlen(other_str[i + 1]);
+ if ((other_str[i][0] == type || other_str[i][0] == 'A') &&
+ str_ncmp(xhead, other_str[i + 1], j) == 0)
+ {
+ fputs(other_str[i + 2], fp);
+ xhead += j;
+ space = 0;
+ break;
+ }
+ }
+
+ /* ¼ÐÅÒ²¤¹L */
+
+ cc = *xhead;
+ switch(cc)
+ {
+ case '<':
+ show = 0;
+ continue;
+ case '>':
+ show = 1;
+ continue;
+ case '\n':
+ case '\r':
+ continue;
+ case ' ':
+ if (space)
+ continue;
+ space = 1;
+ }
+
+ if (show)
+ fputc(cc, fp);
+
+ if (cc != ' ')
+ space = 0;
+ }
+ fputc('\n', fp);
+
+ munmap(fimage, fsize);
+ unlink(ftmp);
+
+ return 1;
+}
+
+
+static int
+http_conn(server, word, type, s)
+ char *server;
+ char *word;
+ char type;
+ char *s;
+{
+ int sockfd;
+ FILE *fp;
+ char fname[64], ftmp[64], *str;
+
+ if ((sockfd = dns_open(server, HTTP_PORT)) < 0)
+ {
+ vmsg("µLªk»P¦øªA¾¹¨ú±o³sµ²¡A¬d¸ß¥¢±Ñ");
+ return 0;
+ }
+ else
+ {
+ mouts(22, 0, "¥¿¦b³s±µ¦øªA¾¹¡A½Ðµy«á(«ö¥ô·NÁäÂ÷¶}).............");
+ refresh();
+ }
+ write(sockfd, s, strlen(s));
+ shutdown(sockfd, 1);
+
+ sprintf(fname, "tmp/%s.yahoo_dict", cuser.userid);
+
+ fp = fopen(fname, "w");
+ sprintf(ftmp, "tmp/%s.yahoo_dict.tmp", cuser.userid);
+ str = strchr(s + 4, ' ');
+
+ if (str)
+ *str = '\0';
+
+ fprintf(fp, "%-24s\033[1;37;44m " Dict_Name " \033[m\n%s\n",
+ "", msg_seperator);
+ fprintf(fp, "¸Ó­¶³sµ²¡Ghttp://%s%s\n\n", server, s + 4);
+ fprintf(fp, "%s", word);
+
+ outz("Â^¨ú¸ê®Æ¤¤¡A½Ðµy«á...");
+ refresh();
+
+ if (write_file(type, sockfd, fp, ftmp) >= 0)
+ fprintf(fp, "\n\n%s\n%-26s\033[1;36;40mYahoo! \033[37m½u¤W¦r¨å\033[m\n",
+ msg_seperator, "");
+
+ fclose(fp);
+
+ more(fname, (char *) -1);
+ unlink(fname);
+ return 0;
+}
+
+
+static void
+yahoo_dict(word, type)
+ char *word;
+ char type;
+{
+ char sendform[512];
+ char ue_word[90];
+
+ url_encode(ue_word, word);
+
+ if (type == 'd')
+ sprintf(sendform, "GET " CGI_yahoo "%s HTTP/1.0\n\n", ue_word);
+ else
+ sprintf(sendform, "GET " CGI_yahoo "%s" CGI_yahoo2 "%c HTTP/1.0\n\n",
+ ue_word, type);
+
+ http_conn(SERVER_yahoo, word, type, sendform);
+}
+
+
+int
+main_yahoo()
+{
+ char word[30];
+ char *menu[] = {"DD",
+ "D ÄÀ¸q",
+ "T ¦P¸q¦r/¤Ï¸q¦r",
+ "V ÅܤƧÎ", NULL};
+
+ while (1)
+ {
+ clear();
+ move(0, 25);
+ outs("\033[1;37;44m¡· " Dict_Name " ¡·\033[m");
+ move(3, 0);
+ outs("¦¹¦r¨å¨Ó·½¬° " Dict_Name "¡C\n\n");
+ outs("ºô§}¡G" REF);
+
+ if (!vget(8, 0, "¬d¸ß¦r·J¡G", word, 30, DOECHO))
+ break;
+
+ yahoo_dict(word, pans(-1, -1, "¬d¸ß¿ï¶µ", menu));
+ }
+
+ return 0;
+}
+#endif /* HAVE_NETTOOL */ \ No newline at end of file
diff --git a/game/dragon.c b/game/dragon.c
new file mode 100644
index 0000000..f029520
--- /dev/null
+++ b/game/dragon.c
@@ -0,0 +1,315 @@
+/*-------------------------------------------------------*/
+/* dragon.c ( YZU WindTopBBS Ver 3.00 ) */
+/*-------------------------------------------------------*/
+/* target : ±µÀs¹CÀ¸ */
+/* create : 01/01/12 */
+/* update : 03/07/23 */
+/* author : verit.bbs@bbs.yzu.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+#ifdef HAVE_GAME
+
+static int cards[52];
+static int seven[7]; /* ¨C°ïµP¼[¦UÁÙ¦³´X±iµP */
+static int points[7];
+
+
+/*-------------------------------------------------------*/
+/* µe¥XµP */
+/*-------------------------------------------------------*/
+
+
+static void
+draw_card(x, y, card)
+ int x, y;
+ int card; /* >=0:¦L¥X¾ã±iµP¤Î¦¹±iµPªº¸¹½X -1:¥u¦LµP¥~´ß */
+{
+ char flower[4][3] = {"¢Ñ", "¢Ò", "¢Ö", "¢á"};
+ char number[13][3] = {"¢Ï", "¢±", "¢²", "¢³", "¢´", "¢µ", "¢¶", "¢·", "¢¸", "¢â", "¢Ø", "¢ß", "¢Ù"};
+
+ move(x, y);
+ outs("¢~¢w¢w¢w¢¡");
+
+ if (card < 0)
+ return;
+
+ move(x + 1, y);
+ prints("¢x%s¡@¡@¢x", number[card % 13]);
+ move(x + 2, y);
+ prints("¢x%s¡@¡@¢x", flower[card % 4]);
+ move(x + 3, y);
+ outs("¢x¡@¡@¡@¢x");
+ move(x + 4, y);
+ outs("¢x¡@¡@¡@¢x");
+ move(x + 5, y);
+ outs("¢¢¢w¢w¢w¢£");
+}
+
+
+/*-------------------------------------------------------*/
+/* ¹CÀ¸»¡©ú */
+/*-------------------------------------------------------*/
+
+
+static int
+draw_explain()
+{
+ vs_bar("±µÀs¹CÀ¸");
+
+ move(4, 10);
+ outs("¡i¹CÀ¸»¡©ú¡j");
+ move(6, 15);
+ outs("(1) ¦b¿Ã¹õ¤W¤è¬°µP¼[¡A¥i¥H§Q¥Î ¡ö¡B¡÷ ¤Á´«¡C");
+ move(8, 15);
+ outs("(2) ¦b¿Ã¹õ¥ª¤U¤è¬°«ùµP , ¥i¥H§Q¥Î c ¤Á´«¡C");
+ move(10, 15);
+ outs("(3) ·íµP¼[ªºµP¬O«ùµPªº¤U¤@±i©Î¤W¤@±i¡A§Y¥i§Q¥Î Enter ¦YµP¡C");
+ move(11, 15);
+ outs(" (¥u¬ÝÂI¼Æ¡A¤£¬Ýªá¦â)");
+ move(13, 15);
+ outs("(4) ·íµP¼[ªºµP³£¦Y§¹¡A¤Î¹CÀ¸Àò³Ó¡C");
+ move(15, 15);
+ outs("(5) ·í«ùµP¤Á´«§¹¥B©|¥¼¦Y§¹µP¼[ªºµP¡A§Y¹CÀ¸¥¢±Ñ¡C");
+ vmsg(NULL);
+}
+
+
+/*-------------------------------------------------------*/
+/* µe¥X¹CÀ¸µPªº°t¸m */
+/*-------------------------------------------------------*/
+
+
+static void
+draw_screen()
+{
+ int i, j;
+ vs_bar("±µÀs¹CÀ¸");
+
+ for (i = 0; i < 7; i++)
+ {
+ for (j = 0; j <= i; j++)
+ draw_card(4 + j, 5 + i * 10, (i == j) ? cards[i] : -1);
+ }
+}
+
+
+/*-------------------------------------------------------*/
+/* µe¥X´å¼Ð */
+/*-------------------------------------------------------*/
+
+
+static void
+draw_cursor(location, mode)
+ int location;
+ int mode; /* 1:¤W¦â 0:²M°£ */
+{
+ int x, y;
+
+ x = 8 + seven[location];
+ y = 9 + location * 10;
+ move(x, y);
+ outs(mode ? "¡´" : "¡@");
+ if (mode)
+ move(x, y + 1); /* Á×§K¦Û°Ê°»´ú¥þ§Î */
+}
+
+
+/*-------------------------------------------------------*/
+/* ²M°£¿Ã¹õ¤WªºµP */
+/*-------------------------------------------------------*/
+
+
+static void
+clear_card(location)
+ int location;
+{
+ move(9 + seven[location], 5 + location * 10);
+ outs(" ");
+}
+
+
+/*-------------------------------------------------------*/
+/* ¹CÀ¸°Ñ¼Æªì©l¤Æ */
+/*-------------------------------------------------------*/
+
+
+static int
+init_dragon()
+{
+ int i, j, num;
+
+ for (i = 0; i < 52; i++) /* µP¥ý¤@±i¤@±i±Æ¦n¡A·Ç³Æ¬~µP */
+ cards[i] = i;
+
+ for (i = 0; i < 51; i++)
+ {
+ j = rnd(52 - i) + i;
+
+ /* cards[j] ©M cards[i] ¥æ´« */
+ num = cards[i];
+ cards[i] = cards[j];
+ cards[j] = num;
+ }
+
+ for (i = 0; i < 7; i++)
+ {
+ seven[i] = i;
+ points[i] = cards[i];
+ }
+
+ return 0;
+}
+
+
+/*-------------------------------------------------------*/
+/* §PÂ_¹CÀ¸¬O§_µ²§ô */
+/*-------------------------------------------------------*/
+
+
+static int /* 1:¦¨¥\ */
+gameover()
+{
+ int i;
+
+ for (i = 0; i < 7; i++)
+ {
+ if (seven[i] != -1)
+ return 0;
+ }
+ return 1;
+}
+
+
+/*-------------------------------------------------------*/
+/* ¹CÀ¸¥Dµ{¦¡ */
+/*-------------------------------------------------------*/
+
+
+int /* >=0:¦¨¥\ -1:¥¢±Ñ -2:Â÷¶} */
+play_dragon()
+{
+ int i;
+ int location = 0; /* ¥Ø«e´å¼Ðªº¦ì¸m */
+ int now = 7; /* ¥Ø«e¥Î¨ì cards[] ²Ä´X±iµP */
+ int have_card = 22; /* 22 ¦¸´«µP¾÷·| */
+ int point; /* ¥Ø«e¤â¤Wªº³o±iµP */
+
+ clear();
+
+ draw_screen();
+ draw_cursor(location, 1);
+ point = cards[now];
+ draw_card(14, 5, cards[now++]);
+ move(19, 40);
+ prints("±zÁÙ¦³ %2d ¦¸¾÷·|¥i¥H´«µP", have_card);
+ move(b_lines, 0);
+ outs("¡¹ ¾Þ§@»¡©ú¡G(¡ö)¥ª²¾ (¡÷)¥k²¾ (Enter)¦YµP (c)´«µP (q)Â÷¶}");
+
+ for (;;)
+ {
+ switch (vkey())
+ {
+ case 'c':
+ if (have_card <= 0)
+ return -1;
+ have_card--;
+ move(19, 47);
+ prints("%2d", have_card);
+ point = cards[now];
+ draw_card(14, 5, cards[now++]);
+ break;
+
+ case KEY_RIGHT:
+ draw_cursor(location, 0);
+ do
+ {
+ location = (location + 1) % 7;
+ } while (seven[location] == -1);
+ draw_cursor(location, 1);
+ break;
+
+ case KEY_LEFT:
+ draw_cursor(location, 0);
+ do
+ {
+ location = (location == 0) ? 6 : location - 1;
+ } while (seven[location] == -1);
+ draw_cursor(location, 1);
+ break;
+
+ case '\n':
+ case ' ':
+ if (points[location] % 13 - point % 13 == 1 ||
+ points[location] % 13 - point % 13 == -1 ||
+ points[location] % 13 - point % 13 == 12 ||
+ points[location] % 13 - point % 13 == -12)
+ {
+ point = points[location];
+ draw_card(14, 5, point);
+ clear_card(location);
+ draw_cursor(location, 0);
+ seven[location]--;
+ if (seven[location] >= 0)
+ {
+ points[location] = cards[now];
+ draw_card(4 + seven[location], 5 + location * 10, cards[now++]);
+ draw_cursor(location, 1);
+ }
+ else
+ {
+ for (i = 0; i < 5; i++)
+ {
+ move(4 + i, 5 + location * 10);
+ outs(" ");
+ }
+ if (gameover() == 1)
+ return have_card;
+ do
+ {
+ location = (location + 1) % 7;
+ } while (seven[location] == -1);
+ draw_cursor(location, 1);
+ }
+ }
+ break;
+
+ case 'q':
+ return -2;
+ }
+ }
+}
+
+
+int
+main_dragon()
+{
+ draw_explain();
+
+ while (1)
+ {
+ init_dragon();
+
+ switch (play_dragon())
+ {
+ case -1:
+ vmsg("¬D¾Ô¥¢±Ñ¡I");
+ break;
+
+ case -2:
+ vmsg(MSG_QUITGAME);
+ return 0;
+
+ default:
+ vmsg("®¥³ß±z¹LÃö°Õ¡I");
+ }
+
+ if (vans("¬O§_­nÄ~Äòª±(Y/N)¡H[N] ") != 'y')
+ break;
+ }
+
+ return 0;
+}
+#endif /* HAVE_GAME */
diff --git a/game/fantan.c b/game/fantan.c
new file mode 100644
index 0000000..7571ab7
--- /dev/null
+++ b/game/fantan.c
@@ -0,0 +1,413 @@
+/*-------------------------------------------------------*/
+/* fantan.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : ±µÀs¹CÀ¸ */
+/* create : 98/08/04 */
+/* update : 01/03/01 */
+/* author : dsyan.bbs@Forever.twbbs.org */
+/* recast : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+#ifdef HAVE_GAME
+
+enum
+{
+ SIDE_UP = 1, /* ¦b¤W­± */
+ SIDE_DOWN = 0 /* ¦b¤U­± */
+};
+
+
+static inline int
+cal_kind(card) /* ºâªá¦â */
+ int card;
+{
+ /* card: 1 - 13 ²Ä¡³ºØªá¦â ¡¸
+ 14 - 26 ²Ä¤@ºØªá¦â ¡¹
+ 27 - 39 ²Ä¤GºØªá¦â ¡³
+ 40 - 52 ²Ä¤TºØªá¦â ¡´ */
+
+ return (card - 1) / 13;
+}
+
+
+static inline int
+cal_num(card) /* ºâÂI¼Æ */
+ int card;
+{
+ card %= 13;
+ return card ? card : 13;
+}
+
+
+static void
+move_cur(a, b, c, d) /* ²¾°Ê½b¸¹ */
+ int a, b; /* ­ì¦ì¸m */
+ int c, d; /* ·s¦ì¸m */
+{
+ move(a, b);
+ outs(" ");
+ move(c, d);
+ outs("¡÷");
+ move(c, d + 1); /* Á×§K¦Û°Ê°»´ú¥þ§Î */
+}
+
+
+static inline int
+ycol(y) /* ºâ®y¼Ð */
+ int y;
+{
+ return y * 11 - 8;
+}
+
+
+static void
+draw(x, y, card) /* µeµP */
+ int x, y;
+ int card;
+{
+ char kind[4][3] = {"¡¸", "¡¹", "¡³", "¡´"}; /* ªá¦â */
+ char num[13][3] = {"¢Ï", "¢±", "¢²", "¢³", "¢´", "¢µ", "¢¶", /* ÂI¼Æ */
+ "¢·", "¢¸", "¢â", "¢Ø", "¢ß", "¢Ù"};
+
+ move(x, y);
+
+ if (card > 0)
+ prints("¢u%s%s¢t", kind[cal_kind(card)], num[cal_num(card) - 1]);
+ else if (!card)
+ outs("¢u¢w¢w¢t");
+ else
+ outs(" ");
+}
+
+
+static inline void
+out_prompt() /* ´£¥Ü¦r²´ */
+{
+ move(b_lines - 1, 0);
+ outs(COLOR2 " (q)Â÷¶} (r)­«ª± (¡ö¡÷¡ô¡õ)²¾°Ê (¡ô)½Âà (Enter)°ïÅ| (Space)«ü©w/²¾°Ê/½µP \033[m");
+}
+
+
+static char
+get_newcard(mode)
+ int mode; /* 0:­«·s¬~µP 1:µoµP */
+{
+ static char card[52]; /* ³Ì¦h¥u·|¥Î¨ì 52 ±iµP */
+ static int now; /* µo¥X²Ä now ±iµP */
+ int i, num;
+ char tmp;
+
+ if (!mode) /* ­«·s¬~µP */
+ {
+ for (i = 0; i < 52; i++)
+ card[i] = i + 1;
+
+ for (i = 0; i < 51; i++)
+ {
+ num = rnd(52 - i) + i;
+
+ /* card[num] ©M card[i] ¥æ´« */
+ tmp = card[i];
+ card[i] = card[num];
+ card[num] = tmp;
+ }
+
+ now = 0;
+ return -1;
+ }
+
+ tmp = card[now];
+ now++;
+ return tmp;
+}
+
+
+int
+main_fantan()
+{
+ char max[9]; /* ¨C°ïªºµP¼Æ¡A²Ä¤K°ï¬O¥ª¤W¨¤ªºµP */
+ char rmax[8]; /* ¨C°ï¥¼©|³Q½¶}ªºµP¼Æ */
+
+ char left_stack[25]; /* ¥ª¤W¨¤ªº 24 ±iµP */
+ char up_stack[5]; /* ¥k¤W¨¤ªº 4 ±iµP (¥u­n°O¿ý³Ì¤jµP§Y¥i) */
+ char down_stack[8][21]; /* ¤U­±¤C­Ó°ïÅ|ªº©Ò¦³µP */
+
+ int level; /* ¤@¦¸Â½´X±iµP */
+ int side; /* ´å¼Ð¦b¤W­±ÁÙ¬O¤U­± */
+
+ int cx, cy; /* ¥Ø«e©Ò¦b (x, y) ®y¼Ð */
+ int xx, yy; /* ¹L¥h©Ò¦b (x, y) ®y¼Ð */
+ int star_c, star_x, star_y; /* ¥´ '*' ³Bªº µP¡B®y¼Ð */
+ int left; /* ¥ª¤W¨¤°ïÅ|½¨ì²Ä´X±i */
+
+ int i, j;
+
+ time_t init_time; /* ¹CÀ¸¶}©lªº®É¶¡ */
+
+ level = vans("½Ð¿ï¾Ü¤@¦¸Â½ [1~3] ¤@¡ã¤T ±iµP¡A©Î«ö [Q] Â÷¶}¡G");
+ if (level > '0' && level < '4')
+ level -= '0';
+ else
+ return XEASY;
+
+game_start:
+ vs_bar("±µÀs");
+ out_prompt();
+
+ side = SIDE_DOWN;
+ star_c = 0;
+ star_x = 2;
+ star_y = 79;
+
+ for (i = 0; i <= 4; i++) /* ¤W­±ªº¥|­Ó°ïÅ|Âk¹s */
+ up_stack[i] = 0;
+
+ get_newcard(0); /* ¬~µP */
+
+ for (i = 1; i <= 7; i++)
+ {
+ max[i] = i; /* ²Ä i °ï­è¶}©l¦³ i ±iµP */
+ rmax[i] = i - 1; /* ²Ä i °ï­è¶}©l¦³ i-1 ±i¥¼¥´¶} */
+ for (j = 1; j <= i; j++)
+ {
+ down_stack[i][j] = get_newcard(1); /* °t¸m¤U­±ªºµP */
+ draw(j + 2, ycol(i), i != j ? 0 : down_stack[i][j]); /* ¨C°ï¥´¶}³Ì«á¤@±iµP */
+ }
+ }
+
+ max[8] = 24; /* ¥ª¤W¨¤­è¶}©l¦³ 24 ±iµP */
+ for (i = 1; i <= 24; i++)
+ left_stack[i] = get_newcard(1); /* °t¸m¥ª¤W¨¤ªºµP */
+ draw(1, 1, 0);
+
+ left = 0;
+ cx = 1;
+ cy = 1;
+ xx = 1;
+ yy = 1;
+
+ init_time = time(0); /* ¶}©l°O¿ý®É¶¡ */
+
+ for (;;)
+ {
+ if (side == SIDE_DOWN)
+ {
+ move_cur(xx + 2, ycol(yy) - 2, cx + 2, ycol(cy) - 2);
+ xx = cx;
+ yy = cy;
+
+ switch (vkey())
+ {
+ case 'q':
+ vmsg(MSG_QUITGAME);
+ return;
+
+ case 'r':
+ goto game_start;
+
+ case KEY_LEFT:
+ cy--;
+ if (!cy)
+ cy = 7;
+ if (cx > max[cy] + 1)
+ cx = max[cy] + 1;
+ break;
+
+ case KEY_RIGHT:
+ cy++;
+ if (cy == 8)
+ cy = 1;
+ if (cx > max[cy] + 1)
+ cx = max[cy] + 1;
+ break;
+
+ case KEY_DOWN:
+ cx++;
+ if (cx == max[cy] + 2)
+ cx--;
+ break;
+
+ case KEY_UP:
+ cx--;
+ if (!cx) /* ¶]¨ì¤W­±¥h¤F */
+ {
+ side = SIDE_UP;
+ move_cur(xx + 2, ycol(yy) - 2, 1, 9);
+ }
+ break;
+
+ case '\n': /* ®³µP¨ì¥k¤W¨¤ */
+ j = down_stack[cy][cx];
+ if ((cal_num(j) == up_stack[cal_kind(j)] + 1) && cx == max[cy] && cx > rmax[cy])
+ {
+ up_stack[cal_kind(j)]++;
+ max[cy]--;
+ draw(1, cal_kind(j) * 10 + 40, j);
+ draw(cx + 2, ycol(cy), -1);
+ if (star_c == j) /* ¦pªG¦³°O¸¹´N®ø±¼ */
+ {
+ move(star_x, star_y);
+ outc(' ');
+ }
+ }
+ /* ¯}Ãö±ø¥ó: ¥k¤W¨¤¥|­Ó³£¬O 13 */
+ if (up_stack[0] & up_stack[1] & up_stack[2] & up_stack[3] == 13)
+ {
+ char buf[80];
+ sprintf(buf, "±zªá¤F %.0lf ¬í ¯}²Ä %d Ãö ¦n±R«ô ^O^",
+ difftime(time(0), init_time), level);
+ vmsg(buf);
+ addmoney(level * 100);
+ return;
+ }
+ break;
+
+ case ' ':
+ if (cx == max[cy] && cx == rmax[cy]) /* ½·sµP */
+ {
+ rmax[cy]--;
+ draw(cx + 2, ycol(cy), down_stack[cy][cx]);
+ break;
+ }
+ else if (cx > rmax[cy] && cx <= max[cy]) /* °Å¤U */
+ {
+ move(star_x, star_y);
+ outc(' ');
+ star_c = down_stack[cy][cx];
+ star_x = cx + 2;
+ star_y = cy * 11;
+ move(star_x, star_y);
+ outc('*');
+ break;
+ }
+ else if (cx != max[cy] + 1)
+ break; /* ¶K¤W */
+
+ if ((max[cy] && (cal_num(down_stack[cy][max[cy]]) == cal_num(star_c) + 1) &&
+ (cal_kind(down_stack[cy][max[cy]]) + cal_kind(star_c)) % 2) ||
+ (max[cy] == 0 && cal_num(star_c) == 13))
+ {
+ if (star_x == 1) /* ±q¤W­±¶K¤U¨Óªº */
+ {
+ max[cy]++;
+ max[8]--;
+ star_x = 2;
+ left--;
+ for (i = left + 1; i <= max[8]; i++)
+ left_stack[i] = left_stack[i + 1];
+ down_stack[cy][max[cy]] = star_c;
+ draw(max[cy] + 2, ycol(cy), star_c);
+ move(1, 19);
+ outc(' ');
+ draw(1, 11, left ? left_stack[left] : -1);
+ }
+ else if (star_x > 2) /* ¦b¤U­±¶K¨Ó¶K¥hªº */
+ {
+ int tmp;;
+ j = star_y / 11;
+ tmp = max[j];
+ for (i = star_x - 2; i <= tmp; i++)
+ {
+ max[cy]++;
+ max[j]--;
+ down_stack[cy][max[cy]] = down_stack[j][i];
+ draw(max[cy] + 2, ycol(cy), down_stack[cy][max[cy]]);
+ draw(i + 2, ycol(j), -1);
+ }
+ move(star_x, star_y);
+ outc(' ');
+ star_x = 2;
+ }
+ }
+ break;
+ }
+
+ }
+ else /* side == SIDE_UP */ /* ¦b¤W­± */
+ {
+ draw(1, 11, left ? left_stack[left] : -1);
+
+ switch (vkey())
+ {
+ case 'q':
+ vmsg(MSG_QUITGAME);
+ return;
+
+ case 'r':
+ goto game_start;
+
+ case '\n': /* ®³µP¨ì¥k¤W¨¤ */
+ j = left_stack[left];
+ if (cal_num(j) == up_stack[cal_kind(j)] + 1)
+ {
+ up_stack[cal_kind(j)]++;
+ max[8]--;
+ left--;
+ draw(1, cal_kind(j) * 10 + 40, j);
+
+ for (i = left + 1; i <= max[8]; i++)
+ left_stack[i] = left_stack[i + 1];
+
+ draw(1, 11, left ? left_stack[left] : -1);
+
+ if (star_x == 1) /* ¦pªG¦³°O¸¹´N²M±¼ */
+ {
+ star_x = 2;
+ move(1, 19);
+ outc(' ');
+ }
+ /* ¯}Ãö±ø¥ó: ¥k¤W¨¤¥|­Ó³£¬O 13 */
+ if (up_stack[0] & up_stack[1] & up_stack[2] & up_stack[3] == 13)
+ {
+ char buf[80];
+ sprintf(buf, "±zªá¤F %.0lf ¬í ¯}²Ä %d Ãö ¦n±R«ô ^O^",
+ difftime(time(0), init_time), level);
+ vmsg(buf);
+ }
+ }
+ break;
+
+ case KEY_DOWN:
+ side = SIDE_DOWN;
+ cx = 1;
+ move_cur(1, 9, cx + 2, ycol(cy) - 2);
+ break;
+
+ case KEY_UP:
+ if (left == max[8])
+ left = 0;
+ else
+ left += level; /* ¤@¦¸µo level ±iµP */
+ if (left > max[8])
+ left = max[8];
+
+ if (star_x == 1)
+ {
+ star_x = 2;
+ move(1, 19);
+ outc(' ');
+ }
+
+ draw(1, 1, left == max[8] ? -1 : 0);
+ break;
+
+ case ' ':
+ if (left > 0)
+ {
+ move(star_x, star_y);
+ outc(' ');
+ star_c = left_stack[left];
+ star_x = 1;
+ star_y = 19;
+ move(1, 19);
+ outc('*');
+ }
+ break;
+ }
+ }
+ }
+}
+#endif /* HAVE_GAME */
diff --git a/game/gp.c b/game/gp.c
new file mode 100644
index 0000000..fbda59c
--- /dev/null
+++ b/game/gp.c
@@ -0,0 +1,643 @@
+/*-------------------------------------------------------*/
+/* gp.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : ª÷¼³§J±ô«¢¹CÀ¸ */
+/* create : 98/10/24 */
+/* update : 01/04/21 */
+/* author : dsyan.bbs@forever.twbbs.org */
+/* recast : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#if 0
+ -=== ª÷¼³§J±ô«¢¹CÀ¸ ===-
+
+ 1. ª±ªkÃþ¦ü±ô«¢¡A¸ò¹q¸£¤ñ¤j¡A¥i¥[­¿¡I
+ 2. ¥i¥H±N¼úª÷·í¤U¤@¦¸ªº½äª`¡C
+
+ ¤j¤p¡G
+ ¦Pªá¶¶¡ÖÅKªK¡Ö¸¬Äª¡Ö¦Pªá¡Ö¶¶¤l¡Ö¤T±ø¡Ö¨ß­F¡Ö³æ­F¡Ö³æ±i
+
+ ¯S®í¥[¤À¡G
+ ¦Pªá¶¶ ¢°¢´­¿
+ ¥| ±i ¢°¢¯­¿
+ ¸¬¡@Ī¡@¡@¢´­¿
+
+#endif
+
+
+#include "bbs.h"
+
+
+#ifdef HAVE_GAME
+
+#define MAX_CHEAT 2 /* ¹q¸£§@¹ú¦h´«µP¦¸¼Æ (0:¤£§@¹ú¡A³Ì¦h¥i§@¹ú 6 ¦¸) */
+
+static char mycard[5]; /* §Úªº 5 ±iµP */
+static char cpucard[5]; /* ¹q¸£ 5 ±iµP */
+
+
+static void
+out_song()
+{
+ static int count = 0;
+
+ /* ©PµØ°·£»ªB¤Í */
+ uschar *msg[7] =
+ {
+ "³o¨Ç¦~ ¤@­Ó¤H ­·¤]¹L «B¤]¨«",
+ "¦³¹L²\\ ¦³¹L¿ù ÁÙ°O±o°í«ù¤°»ò",
+ "¯u·R¹L ¤~·|À´ ·|±I¹æ ·|¦^­º",
+ "²×¦³¹Ú ²×¦³§A ¦b¤ß¤¤",
+ "ªB¤Í¤@¥Í¤@°_¨« ¨º¨Ç¤é¤l¤£¦A¦³",
+ "¤@¥y¸Ü ¤@½ú¤l ¤@¥Í±¡ ¤@ªM°s",
+ "ªB¤Í¤£´¿©t³æ¹L ¤@ÁnªB¤Í§A·|À´"
+ };
+ move(b_lines - 2, 0);
+ prints("\033[1;3%dm%s\033[m Äw½XÁÙ¦³ %d ¤¸", time(0) % 7, msg[count], cuser.money);
+ clrtoeol();
+ if (++count == 7)
+ count = 0;
+}
+
+
+static void
+show_card(isDealer, c, x)
+ int isDealer; /* 1:¹q¸£ 2:ª±®a */
+ char c; /* µP±i */
+ int x; /* ²Ä´X±iµP */
+{
+ int beginL;
+ char *suit[4] = {"¢Ñ", "¢Ò", "¢Ö", "¢á"};
+ char *num[13] = {"¢Ù", "¢Ï", "¢±", "¢²", "¢³", "¢´", "¢µ", "¢¶", "¢·", "¢¸", "¢â", "¢Ø", "¢ß"};
+
+ beginL = (isDealer) ? 2 : 12;
+ move(beginL, x * 4);
+ outs("¢~¢w¢w¢w¢¡");
+ move(beginL + 1, x * 4);
+ prints("¢x%2s ¢x", num[c % 13]);
+ move(beginL + 2, x * 4);
+ prints("¢x%2s ¢x", suit[c / 13]);
+ move(beginL + 3, x * 4);
+ outs("¢x ¢x");
+ move(beginL + 4, x * 4);
+ outs("¢x ¢x");
+ move(beginL + 5, x * 4);
+ outs("¢x ¢x");
+ move(beginL + 6, x * 4);
+ outs("¢¢¢w¢w¢w¢£");
+}
+
+
+/* ¦Pªá¶¶¡BÅKªK¡B¸¬¡B¦Pªá¡B¶¶¡B¤T±ø¡B¨ß­F¡B­F¡B¤@°¦ */
+static void
+show_style(my, cpu)
+ int my, cpu;
+{
+ char *style[9] = {"¦Pªá¶¶", "¥|±i", "¸¬Äª", "¦Pªá", "¶¶¤l", "¤T±ø", "¨ß­F", "³æ­F", "¤@±i"};
+
+ move(5, 26);
+ prints("\033[41;37;1m%s\033[m", style[cpu - 1]);
+ move(15, 26);
+ prints("\033[41;37;1m%s\033[m", style[my - 1]);
+}
+
+
+static int
+card_cmp(a, b)
+ char *a, *b;
+{
+ /* 00~12: C, KA23456789TJQ
+ 13~25: D, KA23456789TJQ
+ 26~38: H, KA23456789TJQ
+ 39~51: S, KA23456789TJQ */
+
+ char c = (*a) % 13;
+ char d = (*b) % 13;
+
+ if (c == 0)
+ c = 13;
+ else if (c == 1)
+ c = 14;
+ if (d == 0)
+ d = 13;
+ else if (d == 1)
+ d = 14;
+
+ /* ¥ý¤ñÂI¼Æ¡A¦A¤ñªá¦â */
+ if (c == d)
+ return *a - *b;
+ return c - d;
+}
+
+
+/* a ¬OÂI¼Æ .. b ¬Oªá¦â */
+static void
+tran(a, b, c)
+ char *a, *b, *c;
+{
+ int i;
+ for (i = 0; i < 5; i++)
+ {
+ a[i] = c[i] % 13;
+ if (!a[i])
+ a[i] = 13;
+ }
+
+ for (i = 0; i < 5; i++)
+ b[i] = c[i] / 13;
+}
+
+
+static void
+check(p, q, r, cc)
+ char *p, *q, *r, *cc;
+{
+ char i;
+
+ for (i = 0; i < 13; i++)
+ p[i] = 0;
+ for (i = 0; i < 5; i++)
+ q[i] = 0;
+ for (i = 0; i < 4; i++)
+ r[i] = 0;
+
+ for (i = 0; i < 5; i++)
+ p[cc[i] % 13]++;
+
+ for (i = 0; i < 13; i++)
+ q[p[i]]++;
+
+ for (i = 0; i < 5; i++)
+ r[cc[i] / 13]++;
+}
+
+
+/* ¦Pªá¶¶¡BÅKªK¡B¸¬¡B¦Pªá¡B¶¶¡B¤T±ø¡B¨ß­F¡B­F¡B¤@°¦ */
+static int
+complex(cc, x, y)
+ char *cc, *x, *y;
+{
+ char p[13], q[5], r[4];
+ char a[5], b[5], c[5], d[5];
+ int i, j, k;
+
+ tran(a, b, cc);
+ check(p, q, r, cc);
+
+ /* ¦Pªá¶¶ */
+ if ((a[0] == a[1] - 1 && a[1] == a[2] - 1 && a[2] == a[3] - 1 && a[3] == a[4] - 1) &&
+ (b[0] == b[1] && b[1] == b[2] && b[2] == b[3] && b[3] == b[4]))
+ {
+ *x = a[4];
+ *y = b[4];
+ return 1;
+ }
+
+ if (a[4] == 1 && a[0] == 2 && a[1] == 3 && a[2] == 4 && a[3] == 5 &&
+ (b[0] == b[1] && b[1] == b[2] && b[2] == b[3] && b[3] == b[4]))
+ {
+ *x = a[3];
+ *y = b[4];
+ return 1;
+ }
+
+ if (a[4] == 1 && a[0] == 10 && a[1] == 11 && a[2] == 12 && a[3] == 13 &&
+ (b[0] == b[1] && b[1] == b[2] && b[2] == b[3] && b[3] == b[4]))
+ {
+ *x = 1;
+ *y = b[4];
+ return 1;
+ }
+
+ /* ÅKªK */
+ if (q[4] == 1)
+ {
+ for (i = 0; i < 13; i++)
+ {
+ if (p[i] == 4)
+ *x = i ? i : 13;
+ }
+ return 2;
+ }
+
+ /* ¸¬Äª */
+ if (q[3] == 1 && q[2] == 1)
+ {
+ for (i = 0; i < 13; i++)
+ {
+ if (p[i] == 3)
+ *x = i ? i : 13;
+ }
+ return 3;
+ }
+
+ /* ¦Pªá */
+ for (i = 0; i < 4; i++)
+ {
+ if (r[i] == 5)
+ {
+ *x = i;
+ return 4;
+ }
+ }
+
+ /* ¶¶¤l */
+ memcpy(c, a, 5);
+ memcpy(d, b, 5);
+ for (i = 0; i < 4; i++)
+ {
+ for (j = i; j < 5; j++)
+ {
+ if (c[i] > c[j])
+ {
+ k = c[i];
+ c[i] = c[j];
+ c[j] = k;
+ k = d[i];
+ d[i] = d[j];
+ d[j] = k;
+ }
+ }
+ }
+
+ if (10 == c[1] && c[1] == c[2] - 1 && c[2] == c[3] - 1 && c[3] == c[4] - 1 && c[0] == 1)
+ {
+ *x = 1;
+ *y = d[0];
+ return 5;
+ }
+
+ if (c[0] == c[1] - 1 && c[1] == c[2] - 1 && c[2] == c[3] - 1 && c[3] == c[4] - 1)
+ {
+ *x = c[4];
+ *y = d[4];
+ return 5;
+ }
+
+ /* ¤T±ø */
+ if (q[3] == 1)
+ {
+ for (i = 0; i < 13; i++)
+ {
+ if (p[i] == 3)
+ {
+ *x = i ? i : 13;
+ return 6;
+ }
+ }
+ }
+
+ /* ¨ß­F */
+ if (q[2] == 2)
+ {
+ for (*x = 0, i = 0; i < 13; i++)
+ {
+ if (p[i] == 2)
+ {
+ if ((i > 1 ? i : i + 13) > (*x == 1 ? 14 : *x))
+ {
+ *x = i ? i : 13;
+ *y = 0;
+ for (j = 0; j < 5; j++)
+ {
+ if (a[j] == i && b[j] > *y)
+ *y = b[j];
+ }
+ }
+ }
+ }
+ return 7;
+ }
+
+ /* ³æ­F */
+ if (q[2] == 1)
+ {
+ for (i = 0; i < 13; i++)
+ {
+ if (p[i] == 2)
+ {
+ *x = i ? i : 13;
+ *y = 0;
+ for (j = 0; j < 5; j++)
+ if (a[j] == i && b[j] > *y)
+ *y = b[j];
+ return 8;
+ }
+ }
+ }
+
+ /* ¤@±i */
+ *x = 0;
+ *y = 0;
+ for (i = 0; i < 5; i++)
+ {
+ if ((a[i] = a[i] ? a[i] : 13 > *x || a[i] == 1) && *x != 1)
+ {
+ *x = a[i];
+ *y = b[i];
+ }
+ }
+ return 9;
+}
+
+
+static int /* <0:ª±®aŵP <-1000:ª±®a¯S®íŵP >0:¹q¸£Ä¹µP */
+gp_win(my, cpu)
+ int *my, *cpu; /* ¶Ç¦^ª±®a©M¹q¸£ªºµP²Õ */
+{
+ int ret;
+ char myX, myY, cpuX, cpuY;
+
+ *my = complex(mycard, &myX, &myY);
+ *cpu = complex(cpucard, &cpuX, &cpuY);
+
+ if (*my != *cpu) /* ¦pªGµP«¬¤£¦P¡Aª½±µ¤ñ¸ûµP«¬¤j¤p */
+ ret = *my - *cpu;
+ else if (myX == 1 && cpuX != 1)
+ ret = -1;
+ else if (myX != 1 && cpuX == 1)
+ ret = 1;
+ else if (myX != cpuX)
+ ret = cpuX - myX;
+ else if (myY != cpuY)
+ ret = cpuY - myY;
+
+ if (ret < 0) /* ¦pªGª±®aŵP */
+ {
+ switch (*my)
+ {
+ case 1: /* ¦Pªá¶¶ */
+ ret = -1001;
+ break;
+ case 2: /* ÅKªK */
+ ret = -1002;
+ break;
+ case 3: /* ¸¬Äª */
+ ret = -1003;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+
+static char
+get_newcard(mode)
+ int mode; /* 0:­«·s¬~µP 1:µoµP */
+{
+ static char card[20 + 5 * MAX_CHEAT]; /* ³Ì¦h¥u·|¥Î¨ì 20+5*MAX_CHEAT ±iµP */
+ static int now; /* µo¥X²Ä now ±iµP */
+ char num;
+ int i;
+
+ if (!mode) /* ­«·s¬~µP */
+ {
+ now = 0;
+ return -1;
+ }
+
+rand_num: /* random ¥X¤@±i©M¤§«e³£¤£¦PªºµP */
+ num = rnd(52);
+ for (i = 0; i < now; i++)
+ {
+ if (num == card[i]) /* ³o±iµP¥H«e random ¹L¤F */
+ goto rand_num;
+ }
+
+ card[now] = num;
+ now++;
+
+ return num;
+}
+
+
+static int
+cpu_doing()
+{
+ int my, cpu;
+ int i, j, k;
+ char hold[5];
+ char p[13], q[5], r[4];
+ char a[5], b[5];
+
+ for (i = 0; i < 5; i++)
+ {
+ cpucard[i] = get_newcard(1);
+ hold[i] = 0;
+ }
+ qsort(cpucard, 5, sizeof(char), card_cmp);
+ for (i = 0; i < 5; i++)
+ show_card(1, cpucard[i], i);
+
+ tran(a, b, cpucard);
+ check(p, q, r, cpucard);
+
+ /* ­Y¦³¯S®íµP«¬¡A«h«O¯d */
+ k = 0; /* 1:¦³¯S®íµP«¬ */
+ for (j = 0; j < 13; j++)
+ {
+ if (p[j] > 1)
+ {
+ for (i = 0; i < 5; i++)
+ {
+ if (j == cpucard[i] % 13)
+ {
+ hold[i] = 1;
+ k = 1;
+ }
+ }
+ }
+ }
+
+ for (i = 0; i < 5; i++)
+ {
+ /* ¦pªG¨S¦³¯S®íµP«¬¡A¨º»ò«O¯d A¡BK¡A§_«h¥þ³¡¤£«O¯d */
+ if (!k && (a[i] == 13 || a[i] == 1))
+ hold[i] = 1;
+
+ move(6, i * 4 + 2);
+ outs(hold[i] ? "«O" : " ");
+ move(7, i * 4 + 2);
+ outs(hold[i] ? "¯d" : " ");
+ }
+
+ vmsg("¹q¸£´«µP«e..");
+
+ for (j = 0; j < 1 + MAX_CHEAT; j++) /* ´«µP¤@¦¸¡B§@¹ú MAX_CHEAT ¦¸ */
+ {
+ /* ¹q¸£´«µP */
+ for (i = 0; i < 5; i++)
+ {
+ if (!hold[i])
+ cpucard[i] = get_newcard(1);
+ }
+ qsort(cpucard, 5, sizeof(char), card_cmp);
+
+ if ((k = gp_win(&my, &cpu)) > 0) /* ­Y¹q¸£Ä¹¡AÂ÷¶}§@¹ú°j°é */
+ break;
+ }
+
+ for (i = 0; i < 5; i++)
+ show_card(1, cpucard[i], i);
+
+ show_style(my, cpu);
+
+ return k;
+}
+
+
+int
+main_gp()
+{
+ int money; /* À£ª`ª÷ÃB */
+ int cont; /* Ä~ÄòÀ£ª`ªº¦¸¼Æ */
+ int doub; /* ¬O§_½ä­¿ */
+ char hold[5]; /* ±ý«O¯dªºµP */
+
+ char buf[60];
+ int i, x, xx;
+
+ if (HAS_STATUS(STATUS_COINLOCK))
+ {
+ vmsg(msg_coinlock);
+ return XEASY;
+ }
+
+ cont = 0; /* À£ª`¦¸¼ÆÂk¹s */
+
+ while (1)
+ {
+ vs_bar("ª÷¼³§J±ô«¢");
+ out_song();
+
+ if (!cont) /* ²Ä¤@¦¸À£ª` */
+ {
+ vget(b_lines - 3, 0, "½Ð°Ý­n¤Uª`¦h¤Ö©O¡H(1 ~ 50000) ", buf, 6, DOECHO);
+ money = atoi(buf);
+ if (money < 1 || money > 50000 || money > cuser.money)
+ break; /* Â÷¶}½ä³õ */
+ cuser.money -= money;
+ move(b_lines - 4, 0);
+ prints(COLOR1 " (¡ö)(¡÷)§ïÅÜ¿ïµP (d)Double (SPCAE)§ïÅÜ´«µP (Enter)½T©w \033[m");
+ }
+ else /* Ä~Äò¤W¤@½LĹªº©ãª÷¡A´N¤£¥i¥H¦A double ¤F */
+ {
+ move(b_lines - 4, 0);
+ prints(COLOR1 " (¡ö)(¡÷)§ïÅÜ¿ïµP (SPCAE)§ïÅÜ´«µP (Enter)½T©w \033[m");
+ }
+
+ out_song();
+
+ get_newcard(0); /* ¬~µP */
+
+ doub = 0;
+ for (i = 0; i < 5; i++)
+ {
+ mycard[i] = get_newcard(1);
+ hold[i] = 1;
+ }
+ qsort(mycard, 5, sizeof(char), card_cmp);
+
+ for (i = 0; i < 5; i++)
+ show_card(0, mycard[i], i);
+
+ x = xx = 0;
+ do
+ {
+ for (i = 0; i < 5; i++)
+ {
+ move(16, i * 4 + 2);
+ outs(hold[i] < 0 ? "«O" : " ");
+ move(17, i * 4 + 2);
+ outs(hold[i] < 0 ? "¯d" : " ");
+ }
+ move(11, xx * 4 + 2);
+ outs(" ");
+ move(11, x * 4 + 2);
+ outs("¡õ");
+ move(11, x * 4 + 3); /* Á×§K¥þ§Î°»´ú */
+ xx = x;
+
+ switch (i = vkey())
+ {
+ case KEY_LEFT:
+ x = x ? x - 1 : 4;
+ break;
+
+ case KEY_RIGHT:
+ x = (x == 4) ? 0 : x + 1;
+ break;
+
+ case ' ':
+ hold[x] *= -1;
+ break;
+
+ case 'd':
+ if (!cont && !doub && cuser.money >= money)
+ {
+ doub = 1;
+ cuser.money -= money;
+ money *= 2;
+ move(b_lines - 4, 0);
+ prints(COLOR1 " (¡ö)(¡÷)§ïÅÜ¿ïµP (SPCAE)§ïÅÜ´«µP (Enter)½T©w \033[m");
+ out_song();
+ }
+ break;
+ }
+ } while (i != '\n');
+
+ for (i = 0; i < 5; i++)
+ {
+ if (hold[i] == 1)
+ mycard[i] = get_newcard(1);
+ }
+ qsort(mycard, 5, sizeof(char), card_cmp);
+ for (i = 0; i < 5; i++)
+ show_card(0, mycard[i], i);
+ move(11, x * 4 + 2);
+ outs(" ");
+
+ i = cpu_doing();
+
+ if (i < 0) /* ª±®aŵP */
+ {
+ switch (i)
+ {
+ /* ¯S®íµP«¬¦³¯S§Oªº½ß²v */
+ case -1001:
+ money *= 16;
+ break;
+ case -1002:
+ money *= 11;
+ break;
+ case -1003:
+ money *= 6;
+ break;
+ default:
+ money <<= 1;
+ break;
+ }
+ sprintf(buf, "«z¡I¦n´Î³á¡I±o¨ì %d ¤¸«¨ :)", money);
+ vmsg(buf);
+
+ if (vans("±z­n§â¼úª÷Ä~ÄòÀ£ª`¶Ü(Y/N)¡H[N] ") == 'y')
+ {
+ cont++;
+ }
+ else
+ {
+ cont = 0;
+ addmoney(money); /* ¤@¯ëµP«¬¦hŤ@­¿¡A¯S®íµP«¬¦h 15/10/5 ­¿ */
+ }
+ }
+ else /* ¿éµP */
+ {
+ vmsg("¿é¤F..:~~~");
+ cont = 0;
+ }
+ }
+ return 0;
+}
+#endif /* HAVE_GAME */
diff --git a/game/gray.c b/game/gray.c
new file mode 100644
index 0000000..21ea4e9
--- /dev/null
+++ b/game/gray.c
@@ -0,0 +1,785 @@
+/*-------------------------------------------------------*/
+/* gray.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : ¶Â¥Õ´Ñ¹CÀ¸ */
+/* create : 01/07/24 */
+/* update : / / */
+/* author : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+#ifdef HAVE_GAME
+
+enum
+{
+ /* GRAY_XPOS + MAP_X ­n¤p©ó b_lines - 2 = 21 *
+ * GRAY_YPOS + MAP_Y * 2 ­n¤p©ó STRLEN - 1 = 79 *
+ * GRAY_YPOS ­n¨¬°÷¨Ï out_prompt() ©ñ¤J */
+
+ GRAY_XPOS = 2, /* ¡õ x ¤è¦V */
+ GRAY_YPOS = 17, /* ¡÷ y ¤è¦V */
+
+ MAP_X = 8, /* ­n¬O°¸¼Æ */
+ MAP_Y = 8, /* ­n¬O°¸¼Æ */
+
+ /* These are flags for "map, tile" bitwise operators */
+ TILE_BLANK= 0, /* ¨S¦³¼Ð°O */
+ TILE_CPU = 1, /* ¹q¸£¾Ö¦³ */
+ TILE_USR = 2, /* ª±®a¾Ö¦³ */
+
+ /* These are flags for "level" bitwise operators */
+ LEVEL_USR_FIRST = 1, /* ª±®a¥ý¤U */
+ LEVEL_1 = 2, /* ¤@¯Å */
+ LEVEL_2 = 4, /* ¤G¯Å */
+ LEVEL_3 = 8 /* ¤T¯Å */
+};
+
+
+static char piece[3][3] = {"¡¼", "¡´", "¡³"};
+static char map[MAP_X][MAP_Y]; /* ¦a¹Ï¤W¨C®æªº¾Ö¦³ªÌ */
+static int cx, cy; /* current (x, y) */
+static int EndGame; /* -1: Â÷¶}¹CÀ¸ 1: ¹CÀ¸µ²§ô 0: ÁÙ¦bª± */
+
+#define mouts(x,y,t) { move(GRAY_XPOS + x, GRAY_YPOS + (y) * 2); outs(piece[t]); }
+
+
+/* int count##(x, y, tile) */
+/* ## : ´ú¸Õªº¤è¦V N E W S */
+/* x, y : ©ñ¸mªº (x,y) ®y¼Ð */
+/* tile : ½Ö¤Uªº¤l */
+/* return : ¯à¦Y´X¤l */
+
+
+static int
+countN(x, y, tile)
+ int x, y;
+ int tile;
+{
+ int i;
+
+ for (i = x - 1; i > 0;)
+ {
+ if (map[i][y] == TILE_BLANK || map[i][y] & tile)
+ break;
+ i--;
+ }
+
+ if (i != x - 1 && map[i][y] & tile)
+ return x - i - 1;
+ return 0;
+}
+
+
+static int
+countS(x, y, tile)
+ int x, y;
+ int tile;
+{
+ int i;
+
+ for (i = x + 1; i < MAP_X - 1;)
+ {
+ if (map[i][y] == TILE_BLANK || map[i][y] & tile)
+ break;
+ i++;
+ }
+
+ if (i != x + 1 && map[i][y] & tile)
+ return i - x - 1;
+ return 0;
+}
+
+
+static int
+countE(x, y, tile)
+ int x, y;
+ int tile;
+{
+ int j;
+
+ for (j = y + 1; j < MAP_Y - 1;)
+ {
+ if (map[x][j] == TILE_BLANK || map[x][j] & tile)
+ break;
+ j++;
+ }
+
+ if (j != y + 1 && map[x][j] & tile)
+ return j - y - 1;
+ return 0;
+}
+
+
+static int
+countW(x, y, tile)
+ int x, y;
+ int tile;
+{
+ int j;
+
+ for (j = y - 1; j > 0;)
+ {
+ if (map[x][j] == TILE_BLANK || map[x][j] & tile)
+ break;
+ j--;
+ }
+
+ if (j != y - 1 && map[x][j] & tile)
+ return y - j - 1;
+ return 0;
+}
+
+
+static int
+countNE(x, y, tile)
+ int x, y;
+ int tile;
+{
+ int i, j;
+
+ for (i = x - 1, j = y + 1; i > 0 && j < MAP_Y - 1;)
+ {
+ if (map[i][j] == TILE_BLANK || map[i][j] & tile)
+ break;
+ i--;
+ j++;
+ }
+
+ if (i != x - 1 && map[i][j] & tile)
+ return x - i - 1;
+ return 0;
+}
+
+
+static int
+countNW(x, y, tile)
+ int x, y;
+ int tile;
+{
+ int i, j;
+
+ for (i = x - 1, j = y - 1; i > 0 && j > 0;)
+ {
+ if (map[i][j] == TILE_BLANK || map[i][j] & tile)
+ break;
+ i--;
+ j--;
+ }
+
+ if (i != x - 1 && map[i][j] & tile)
+ return x - i - 1;
+ return 0;
+}
+
+
+static int
+countSE(x, y, tile)
+ int x, y;
+ int tile;
+{
+ int i, j;
+
+ for (i = x + 1, j = y + 1; i < MAP_X - 1 && j < MAP_Y - 1;)
+ {
+ if (map[i][j] == TILE_BLANK || map[i][j] & tile)
+ break;
+ i++;
+ j++;
+ }
+
+ if (i != x + 1 && map[i][j] & tile)
+ return i - x - 1;
+ return 0;
+}
+
+
+static int
+countSW(x, y, tile)
+ int x, y;
+ int tile;
+{
+ int i, j;
+
+ for (i = x + 1, j = y - 1; i < MAP_X - 1 && j > 0;)
+ {
+ if (map[i][j] == TILE_BLANK || map[i][j] & tile)
+ break;
+ i++;
+ j--;
+ }
+
+ if (i != x + 1 && map[i][j] & tile)
+ return i - x - 1;
+ return 0;
+}
+
+
+static int /* Á`¦@¥i¥H¦Y´X¤l 0: ¤£¯à¦Y */
+do_count(x, y, tile)
+{
+ if (map[x][y] != TILE_BLANK)
+ return 0;
+
+ return countN(x, y, tile) + countS(x, y, tile) + countE(x, y, tile) + countW(x, y, tile) +
+ countNE(x, y, tile) + countNW(x, y, tile) + countSE(x, y, tile) + countSW(x, y, tile);
+}
+
+
+/* void eat##(x, y, tile, num) */
+/* ## : ±ý¦Yªº¤è¦V N E W S */
+/* x, y : ©ñ¸mªº (x,y) ®y¼Ð */
+/* tile : ½Ö¤Uªº¤l */
+/* num : ¦Y´X¤l */
+
+
+static inline void
+eatN(x, y, tile, num)
+ int x, y;
+ int tile;
+ int num;
+{
+ int i;
+
+ for (i = x - 1; i >= x - num; i--)
+ {
+ map[i][y] = tile;
+ mouts(i, y, tile);
+ }
+}
+
+
+static inline void
+eatS(x, y, tile, num)
+ int x, y;
+ int tile;
+ int num;
+{
+ int i;
+
+ for (i = x + 1; i <= x + num; i++)
+ {
+ map[i][y] = tile;
+ mouts(i, y, tile);
+ }
+}
+
+
+static inline void
+eatE(x, y, tile, num)
+ int x, y;
+ int tile;
+ int num;
+{
+ int j;
+
+ for (j = y + 1; j <= y + num; j++)
+ {
+ map[x][j] = tile;
+ mouts(x, j, tile);
+ }
+}
+
+
+static inline void
+eatW(x, y, tile, num)
+ int x, y;
+ int tile;
+ int num;
+{
+ int j;
+
+ for (j = y - 1; j >= y - num; j--)
+ {
+ map[x][j] = tile;
+ mouts(x, j, tile);
+ }
+}
+
+
+static inline void
+eatNE(x, y, tile, num)
+ int x, y;
+ int tile;
+ int num;
+{
+ int i, j;
+
+ for (i = x - 1, j = y + 1; i >= x - num; i--, j++)
+ {
+ map[i][j] = tile;
+ mouts(i, j, tile);
+ }
+}
+
+
+static inline void
+eatNW(x, y, tile, num)
+ int x, y;
+ int tile;
+ int num;
+{
+ int i, j;
+
+ for (i = x - 1, j = y - 1; i >= x - num; i--, j--)
+ {
+ map[i][j] = tile;
+ mouts(i, j, tile);
+ }
+}
+
+
+static inline void
+eatSE(x, y, tile, num)
+ int x, y;
+ int tile;
+ int num;
+{
+ int i, j;
+
+ for (i = x + 1, j = y + 1; i <= x + num; i++, j++)
+ {
+ map[i][j] = tile;
+ mouts(i, j, tile);
+ }
+}
+
+
+static inline void
+eatSW(x, y, tile, num)
+ int x, y;
+ int tile;
+ int num;
+{
+ int i, j;
+
+ for (i = x + 1, j = y - 1; i <= x + num; i++, j--)
+ {
+ map[i][j] = tile;
+ mouts(i, j, tile);
+ }
+}
+
+
+static void
+do_eat(x, y, tile)
+{
+ /* ¦Y¦U¤è¦V¯à¦Yªº */
+ eatN(x, y, tile, countN(x, y, tile));
+ eatS(x, y, tile, countS(x, y, tile));
+ eatE(x, y, tile, countE(x, y, tile));
+ eatW(x, y, tile, countW(x, y, tile));
+ eatNE(x, y, tile, countNE(x, y, tile));
+ eatNW(x, y, tile, countNW(x, y, tile));
+ eatSE(x, y, tile, countSE(x, y, tile));
+ eatSW(x, y, tile, countSW(x, y, tile));
+
+ /* ¦Y©Ò¤Uªº³o®æ */
+ map[x][y] = tile;
+ mouts(x, y, tile);
+}
+
+
+/* µû¤À¨î«×¡A«Ü²Ê²Lªº¤H¤u´¼¼z¡A«Ý§ïµ½ */
+
+/* ºâ¯à¦Y¨ì´X­ÓÃä */
+static int
+count_edge(x, y, tile)
+ int x, y;
+ int tile;
+{
+ /* ¥»¨­¬OÃä¡A¤~¤]¥i¯à¦Y¨ì¤W¤U©Î¥ª¥kªºÃä */
+ if (x == 0 || x == MAP_X - 1)
+ {
+ return 1 + countE(x, y, tile) + countW(x, y, tile); /* ¥]¬A¦Û¤v¤@­ÓÃä */
+ }
+ if (y == 0 || y == MAP_Y - 1)
+ {
+ return 1 + countN(x, y, tile) + countS(x, y, tile); /* ¥]¬A¦Û¤v¤@­ÓÃä */
+ }
+ return 0;
+}
+
+
+static inline int
+find_best(x, y, level) /* ¶Ç¦^ (x, y) ¹q¸£©Ò©ñ¸m³Ì¦nªº¦ì¸m */
+ int *x, *y;
+ int level;
+{
+ int i, j, bestx, besty, tmp;
+ int score = 0;
+
+ if (level & LEVEL_1) /* ¤@¯Å: ¦Y¶V¦h¶V¦n */
+ {
+ for (i = 0; i < MAP_X; i++)
+ {
+ for (j = 0; j < MAP_Y; j++)
+ {
+ if (tmp = do_count(i, j, TILE_CPU))
+ {
+ if (tmp > score)
+ {
+ score = tmp;
+ bestx = i;
+ besty = j;
+ }
+ }
+ }
+ }
+ }
+ else if (level & LEVEL_2) /* ¤G¯Å: ²¤Æªºª÷¨¤»ÈÃä */
+ {
+ for (i = 0; i < MAP_X; i++)
+ {
+ for (j = 0; j < MAP_Y; j++)
+ {
+ if (tmp = do_count(i, j, TILE_CPU))
+ {
+ /* ¨¤ +100 Ãä +50 ¤@¯ë +1*/
+ if (i == 0 || i == MAP_X - 1)
+ {
+ if (j == 0 || j == MAP_Y - 1)
+ tmp += 100;
+ else
+ tmp += 50;
+ }
+ else if (j == 0 || j == MAP_Y - 1)
+ {
+ tmp += 50;
+ }
+
+ if (tmp > score)
+ {
+ score = tmp;
+ bestx = i;
+ besty = j;
+ }
+ }
+ }
+ }
+ }
+ else /* if (level & LEVEL_3) */ /* ¤T¯Å: ª÷¨¤»ÈÃä */
+ {
+ for (i = 0; i < MAP_X; i++)
+ {
+ for (j = 0; j < MAP_Y; j++)
+ {
+ if (tmp = do_count(i, j, TILE_CPU))
+ {
+ /* ¨¤ +100 ¤@­ÓÃä +10 ¤@¯ë +1 */
+ if (i == 0 || i == MAP_X - 1)
+ {
+ if (j == 0 || j == MAP_Y - 1)
+ tmp += 100;
+ else
+ tmp += 10 * count_edge(i, j, TILE_CPU);
+ }
+ else if (j == 0 || j == MAP_Y - 1)
+ {
+ tmp += 10 * count_edge(i, j, TILE_CPU);
+ }
+
+ if (tmp > score)
+ {
+ score = tmp;
+ bestx = i;
+ besty = j;
+ }
+ }
+ }
+ }
+ }
+
+ *x = bestx;
+ *y = besty;
+ return score;
+}
+
+
+/* ³]©w´Ñ½L */
+
+static inline void
+init_map()
+{
+ int i, j;
+
+ for (i = 0; i < MAP_X; i++)
+ {
+ for (j = 0; j < MAP_Y; j++)
+ {
+ map[i][j] = TILE_BLANK;
+ }
+ }
+
+ map[MAP_X / 2 - 1][MAP_Y / 2 - 1] = TILE_CPU;
+ map[MAP_X / 2][MAP_Y / 2] = TILE_CPU;
+ map[MAP_X / 2 - 1][MAP_Y / 2] = TILE_USR;
+ map[MAP_X / 2][MAP_Y / 2 - 1] = TILE_USR;
+}
+
+
+
+/* ¿Ã¹õ±±¨î */
+
+static inline void
+out_prompt()
+{
+ /* ¤£±o¶W¹L GRAY_YPOS¡A§_«h·|¿ù¶Ã */
+ move(3, 0);
+ outs("«öÁ仡©ú¡G");
+ move(5, 0);
+ outs("²¾°Ê ¤è¦VÁä");
+ move(6, 0);
+ outs("¦û»â ªÅ¥ÕÁä");
+ move(7, 0);
+ outs("¦û»â Enter");
+ move(8, 0);
+ outs("Â÷¶} Esc / q");
+ move(10, 0);
+ outs("ª±®a ");
+ outs(piece[TILE_USR]);
+ move(11, 0);
+ outs("¹q¸£ ");
+ outs(piece[TILE_CPU]);
+}
+
+
+static inline void
+out_song()
+{
+ uschar *msg[5] =
+ {
+ "¤G°¦¦Ñªê ¤G°¦¦Ñªê",
+ "¶]±o§Ö ¶]±o§Ö",
+ "¤@°¦¨S¦³²´·ú",
+ "¤@°¦¨S¦³§À¤Ú",
+ "¯u©_©Ç ¯u©_©Ç"
+ };
+ move(b_lines - 2, 0);
+ prints("\033[1;3%dm%s\033[m", time(0) % 7, msg[time(0) % 5]);
+ clrtoeol();
+}
+
+
+static inline void
+out_map()
+{
+ int i, j;
+
+ vs_bar("¶Â¥Õ´Ñ");
+
+ out_prompt();
+ out_song();
+
+ for (i = 0; i < MAP_X; i++)
+ {
+ move(GRAY_XPOS + i, GRAY_YPOS);
+ for (j = 0; j < MAP_Y; j++)
+ outs(piece[TILE_BLANK]);
+ }
+
+ mouts(MAP_X / 2 - 1, MAP_Y / 2 - 1, TILE_CPU);
+ mouts(MAP_X / 2, MAP_Y / 2, TILE_CPU);
+ mouts(MAP_X / 2 - 1, MAP_Y / 2, TILE_USR);
+ mouts(MAP_X / 2, MAP_Y / 2 - 1, TILE_USR);
+
+ move(GRAY_XPOS + cx, GRAY_YPOS + cy * 2 + 1); /* move to (0, 0) */
+}
+
+
+/* ¹CÀ¸¥Dµ{¦¡ */
+
+static inline void
+result(msg)
+ char *msg;
+{
+ int i, j;
+ int sumCPU, sumUSR;
+
+ sumCPU = sumUSR = 0;
+ for (i = 0; i < MAP_X; i++)
+ {
+ for (j = 0; j < MAP_Y; j++)
+ {
+ if (map[i][j] & TILE_CPU)
+ sumCPU++;
+ else if(map[i][j] & TILE_USR)
+ sumUSR++;
+ }
+ }
+
+ sprintf(msg, "[%s] ª±®a¡G¹q¸£ = %d¡G%d",
+ (sumUSR > sumCPU) ? "³Ó§Q" : (sumUSR < sumCPU ? "¸¨±Ñ" : "¥­¤â"),
+ sumUSR, sumCPU);
+}
+
+
+static inline void
+play_gray(level)
+ int level;
+{
+ int i, j;
+ int ch;
+ int usr_turn; /* 1: ¸Óª±®a 0: ¸Ó¹q¸£ */
+ int pass; /* 0: ¨S¦³¤Hpass 1: ¤@­Ó¤Hpass 2:³sÄò¤G­Ó¤Hpass */
+ int bestx, besty; /* ¹q¸£³Ì¨Î¤U¤l³B */
+
+ pass = 0;
+ if (!(level & LEVEL_USR_FIRST))
+ goto cpu_first;
+
+ while (!EndGame)
+ {
+ /* ¥ýºâª±®aÁÙ¦³¨S¦³¤l¥i¥H¤U */
+ for (i = 0; i < MAP_X; i++)
+ {
+ for (j = 0; j < MAP_Y; j++)
+ {
+ if (do_count(i, j, TILE_USR))
+ {
+ usr_turn = 1;
+ i = MAP_X; /* Â÷¶} for °j°é */
+ j = MAP_Y;
+ }
+ }
+ }
+
+ if (!usr_turn)
+ {
+ pass++;
+
+ /* Àˬd¬O§_¦³¤G¤H passout */
+ if (pass == 2)
+ {
+ EndGame = 1; /* ¹CÀ¸µ²§ô */
+ return;
+ }
+ }
+ else if (pass)
+ {
+ vmsg("¹q¸£µL¤l¥i¤U¡A½ü¨ì±z¤F");
+ move(b_lines, 0);
+ clrtoeol(); /* ®ø±¼ vmsg() */
+ }
+
+ while (usr_turn && (ch = vkey())) /* ¸Óª±®a¤U */
+ {
+ switch (ch)
+ {
+ case KEY_ESC:
+ case 'q':
+ case 'Q':
+ EndGame = -1;
+ return;
+
+ case KEY_UP:
+ if (cx)
+ {
+ cx--;
+ move(GRAY_XPOS + cx, GRAY_YPOS + cy * 2 + 1);
+ }
+ break;
+
+ case KEY_DOWN:
+ if (cx < MAP_X - 1)
+ {
+ cx++;
+ move(GRAY_XPOS + cx, GRAY_YPOS + cy * 2 + 1);
+ }
+ break;
+
+ case KEY_LEFT:
+ if (cy)
+ {
+ cy--;
+ move(GRAY_XPOS + cx, GRAY_YPOS + cy * 2 + 1);
+ }
+ break;
+
+ case KEY_RIGHT:
+ if (cy < MAP_Y - 1)
+ {
+ cy++;
+ move(GRAY_XPOS + cx, GRAY_YPOS + cy * 2 + 1);
+ }
+ break;
+
+ case '\n':
+ case ' ':
+ if (do_count(cx, cy, TILE_USR))
+ {
+ do_eat(cx, cy, TILE_USR);
+ usr_turn = 0;
+ pass = 0;
+ }
+ break;
+
+ default:
+ break;
+ }
+ } /* ª±®a¤U while °j°éµ²§ô */
+
+cpu_first:
+
+ /* ¸Ó CPU ¤U */
+ if (!find_best(&bestx, &besty, level))
+ {
+ pass++;
+ }
+ else
+ {
+ do_eat(bestx, besty, TILE_CPU);
+ pass = 0;
+ cx = bestx; /* ²¾¨ì CPU ©Ò¤Uªº¦ì¸m */
+ cy = besty;
+ }
+ move(GRAY_XPOS + cx, GRAY_YPOS + cy * 2 + 1);
+
+ /* Àˬd¬O§_¦³¤G¤H passout */
+ if (pass == 2)
+ EndGame = 1; /* ¹CÀ¸µ²§ô */
+
+ } /* while (!EndGame) °j°éµ²§ô */
+}
+
+
+int
+main_gray()
+{
+ int level;
+
+ level = vans("½Ð¿ï¾Ü [1] ©ö¦p¤Ï´x [2] «D±`²³æ [3] ´¶³qÃø«×¡A©Î«ö [Q] Â÷¶}¡G") - '1';
+ if (level >= 0 && level <= 2)
+ {
+ level = LEVEL_1 << level; /* ³]©wÃø«× */
+ if (vans("ª±®a¥ý¤U¶Ü(Y/N)¡H[Y] ") != 'n')
+ level |= LEVEL_USR_FIRST;
+ }
+ else
+ {
+ /* vmsg(MSG_QUITGAME); */ /* itoc.010312: ¤£­n¤F */
+ return XEASY;
+ }
+
+ cx = MAP_X / 2 - 1;
+ cy = MAP_Y / 2 - 1;
+ EndGame = 0;
+
+ init_map();
+ out_map();
+ play_gray(level);
+
+ if (EndGame < 0)
+ {
+ vmsg(MSG_QUITGAME);
+ }
+ else
+ {
+ char buf[60];
+ result(buf);
+ vmsg(buf);
+ }
+ return 0;
+}
+#endif /* HAVE_GAME */
diff --git a/game/guessnum.c b/game/guessnum.c
new file mode 100644
index 0000000..9b42ed1
--- /dev/null
+++ b/game/guessnum.c
@@ -0,0 +1,293 @@
+/*-------------------------------------------------------*/
+/* guessnum.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* author : thor.bbs@bbs.cs.nthu.edu.tw */
+/* target : Guess Number tool dynamic link module */
+/* create : 99/02/16 */
+/* update : / / */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+
+#ifdef HAVE_GAME
+
+
+typedef char Num[4];
+
+typedef struct
+{
+ Num n;
+ int A, B;
+} His;
+
+static int hisNum;
+static His *hisList;
+
+static int numNum;
+static char *numSet;
+
+
+static void
+AB(p, q, A, B)
+ Num p, q;
+ int *A, *B;
+{ /* compare p and q, return ?A?B */
+ int i, j;
+
+ *A = *B = 0;
+ for (i = 0; i < 4; i++)
+ {
+ for (j = 0; j < 4; j++)
+ {
+ if (p[i] == q[j])
+ {
+ if (i == j)
+ ++*A;
+ else
+ ++*B;
+ }
+ }
+ }
+}
+
+
+static int
+getth(o, a)
+ int o;
+ char *a;
+{ /* return "o"th element index in a[], base 0 */
+ int i = -1;
+
+ o++;
+ while (o)
+ {
+ if (a[++i])
+ continue;
+ else
+ o--;
+ }
+ return i;
+}
+
+
+static void
+ord2Num(o, p)
+ int o;
+ Num p;
+{ /* return "o"th filtered number */
+ char digit[10];
+ int i, j, k;
+
+ memset(digit, 0, sizeof digit);
+
+ for (j = 0, k = 10; j < 4; j++, k--)
+ {
+ i = o % k;
+ o /= k;
+ i = getth(i, digit);
+ p[j] = i;
+ digit[i] = 1;
+ }
+}
+
+
+static int
+matchHis(n)
+ Num n ;
+{
+ int i, A, B;
+
+ for (i = 0; i < hisNum; i++)
+ {
+ AB(n, hisList[i].n, &A, &B);
+ if (A != hisList[i].A || B != hisList[i].B)
+ return 0;
+ }
+ return 1;
+}
+
+
+static void
+finish(msg)
+ char *msg;
+{
+ free(hisList);
+ free(numSet);
+
+ vmsg(msg);
+}
+
+
+static int
+valid_guess(num)
+ char *num;
+{
+ const char n0 = num[0];
+ const char n1 = num[1];
+ const char n2 = num[2];
+ const char n3 = num[3];
+
+ if (n0 >= '0' && n0 <= '9' && n1 >= '0' && n1 <= '9' &&
+ n2 >= '0' && n2 <= '9' && n3 >= '0' && n3 <= '9' &&
+ n0 != n1 && n0 != n2 && n0 != n3 && n1 != n2 && n1 != n3 && n2 != n3)
+ {
+ return 1;
+ }
+ return 0;
+}
+
+
+static int
+mainNum(fighting)
+ int fighting; /* Thor.990317: ¹ï¾Ô¼Ò¦¡ */
+{
+ Num myNumber;
+
+ if (vans("·Q¦n±zªº¼Æ¦r¤F¶Ü(Y/N)¡H[N] ") != 'y')
+ {
+ /* vmsg(MSG_QUITGAME); */ /* itoc.010312: ¤£­n¤F */
+ return XEASY;
+ }
+
+ /* initialize variables */
+
+ hisList = (His *) malloc(sizeof(His)); /* pseudo */
+ numSet = (char *)malloc(10 * 9 * 8 * 7 * sizeof(char));
+
+ hisNum = 0;
+ numNum = 10 * 9 * 8 * 7;
+ memset(numSet, 0, 10 * 9 * 8 * 7 * sizeof(char));
+
+ /* Thor.990317:¹ï¾Ô¼Ò¦¡ */
+ vs_bar(fighting ? "²q¼Æ¦r¤j¾Ô" : "¶Ì¥Ê²q¼Æ¦r");
+
+ if (fighting)
+ ord2Num(rnd(numNum), myNumber); /* Thor.990317:¹ï¾Ô¼Ò¦¡ */
+
+ /* while there is possibility */
+ for (;;)
+ {
+ Num myGuess, yourGuess;
+ int youA, youB, myA, myB;
+
+ if (fighting) /* Thor.990317:¹ï¾Ô¼Ò¦¡ */
+ {
+ int i;
+ char tmp[50];
+ vget(b_lines - 3, 0, "±z²q§Úªº¼Æ¦r¬O[????]¡G", tmp, 5, DOECHO);
+ if (!valid_guess(tmp))
+ goto abort_game;
+
+ for (i = 0; i < 4; i++)
+ yourGuess[i] = tmp[i] - '0';
+ AB(myNumber, yourGuess, &myA, &myB);
+ move(b_lines - 2, 0);
+ prints("§Ú»¡ \033[1m%dA%dB \033[m", myA, myB);
+
+ if (myA == 4)
+ {
+ /* you win */
+ finish("±zŤF! ¦n±R«ô ^O^");
+ return 0;
+ }
+ }
+
+ /* pickup a candidate number */
+ for (;;)
+ {
+ int i;
+ /* pickup by random */
+ if (numNum <= 0)
+ goto foolme;
+ i = rnd(numNum);
+ i = getth(i, numSet); /* i-th ordering num */
+ numSet[i] = 1;
+ numNum--; /* filtered out */
+ ord2Num(i, myGuess); /* convert ordering num to Num */
+
+ /* check history */
+ if (matchHis(myGuess))
+ break;
+ }
+
+ /* show the picked number */
+ move(b_lines - 1, 0);
+ prints("§Ú²q±zªº¼Æ¦r¬O \033[1;37m%d%d%d%d\033[m", myGuess[0], myGuess[1], myGuess[2], myGuess[3]);
+
+ /* get ?A?B */
+ for (;;)
+ {
+ char buf[5];
+ /* get response */
+ vget(b_lines, 0, "±zªº¦^µª[?A?B]¡G", buf, 5, DOECHO);
+
+ if (!buf[0])
+ {
+ abort_game:
+ finish(MSG_QUITGAME);
+ return 0;
+ }
+ if (isdigit(buf[0]) && (buf[1] | 0x20) == 'a'
+ && isdigit(buf[2]) && (buf[3] | 0x20) == 'b')
+ {
+ youA = buf[0] - '0';
+ youB = buf[2] - '0';
+ /* check legimate */
+ if (youA >= 0 && youA <= 4
+ && youB >= 0 && youB <= 4
+ && youA + youB <= 4)
+ {
+ /* if 4A, end the game */
+ if (youA == 4)
+ {
+ /* I win */
+ finish("§ÚŤF! ¼F®`§a ^O^");
+ return 0;
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+ /* err A B */
+ zmsg("¿é¤J®æ¦¡¦³»~");
+ }
+ /* put in history */
+ hisNum++;
+ hisList = (His *) realloc(hisList, hisNum * sizeof(His)); /* assume must succeeded */
+ memcpy(hisList[hisNum - 1].n, myGuess, sizeof(Num));
+ hisList[hisNum - 1].A = youA;
+ hisList[hisNum - 1].B = youB;
+
+ move(hisNum + 2, 0);
+ if (fighting) /* Thor.990317: ¹ï¾Ô¼Ò¦¡ */
+ prints("²Ä \033[1;37m%d\033[m ¦¸, ±z²q \033[1;36m%d%d%d%d\033[m, §Ú»¡ \033[1;33m%dA%dB\033[m; §Ú²q \033[1;33m%d%d%d%d\033[m, ±z»¡ \033[1;36m%dA%dB\033[m", hisNum, yourGuess[0], yourGuess[1], yourGuess[2], yourGuess[3], myA, myB, myGuess[0], myGuess[1], myGuess[2], myGuess[3], youA, youB);
+ else
+ prints("²Ä \033[1;37m%d\033[m ¦¸, §Ú²q \033[1;33m%d%d%d%d\033[m, ±z»¡ \033[1;36m%dA%dB\033[m", hisNum, myGuess[0], myGuess[1], myGuess[2], myGuess[3], youA, youB);
+ }
+
+foolme:
+ /* there is no posibility, show "you fool me" */
+ finish("±zÄF§Ú¡I¤£¸ò±zª±¤F ~~~>_<~~~");
+
+ return 0;
+}
+
+
+int
+guessNum()
+{
+ mainNum(0);
+}
+
+
+int
+fightNum()
+{
+ mainNum(1);
+}
+
+#endif /* HAVE_GAME */
diff --git a/game/km.c b/game/km.c
new file mode 100644
index 0000000..0014c99
--- /dev/null
+++ b/game/km.c
@@ -0,0 +1,532 @@
+/*-------------------------------------------------------*/
+/* km.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : KongMing Chess routines */
+/* create : 01/02/08 */
+/* update : 01/05/09 */
+/* author : einstein@bbs.tnfsh.tn.edu.tw */
+/* recast : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+
+#ifdef HAVE_GAME
+
+#define LOG_KM /* ¬O§_´£¨Ñ°O¿ý´ÑÃЪº¥\¯à */
+#define RETRACT_CHESS /* ¬O§_´£¨Ñ®¬´Ñ¥\¯à */
+
+
+#if 0
+
+´Ñ½L¦b etc/game/km ®æ¦¡¦p¤U¡G
+
+²Ä¤@¦æ©ñÁ`¦@¦³´X½L´Ñ½L(¤T¦ì¼Æ)¡A±q²Ä¤T¦æ¶}©l«h¬O¤@½L¤@½Lªº´ÑÃСC
+
+TILE_NOUSE 0 ªí¥Ü¤£¯à²¾°Êªº®æ¤l
+TILE_BLANK 1 ªí¥ÜªÅ®æ
+TILE_CHESS 2 ªí¥Ü´Ñ¤l
+
+#123²V¤@¦t¤º
+0 0 2 2 2 0 0
+0 0 2 2 2 0 0
+2 2 2 2 2 2 2
+2 2 2 1 2 2 2
+2 2 2 2 2 2 2
+0 0 2 2 2 0 0
+0 0 2 2 2 0 0
+
+#endif
+
+
+enum
+{
+ KM_XPOS = 5,
+ KM_YPOS = 5,
+ MAX_X = 7, /* ­n¬O©_¼Æ */
+ MAX_Y = 7, /* ­n¬O©_¼Æ */
+
+ /* ¥Î bitwise operators & ¨Ó¨ú¥N == */
+ TILE_NOUSE = 0, /* ¤£¯à²¾°Êªº®æ¤l */
+ TILE_BLANK = 1, /* ªÅ®æ */
+ TILE_CHESS = 2 /* ´Ñ¤l */
+};
+
+
+static int board[MAX_X][MAX_Y];
+static int cx, cy;
+static int stage, NUM_TABLE;
+static char piece[4][3] = {"¡@", "¡³", "¡´", "¡¸"};
+static char title[20]; /* ´ÑÃЦWºÙ */
+
+#ifdef RETRACT_CHESS
+static int route[MAX_X * MAX_Y][4]; /* °O¿ý (fx, fy) -> (tx, ty)¡A®¬´Ñ¨B¼Æ¤£¥i¯à¶W¹L´Ñ½L¤j¤p */
+static int step;
+#endif
+
+
+static void
+out_song()
+{
+ /* itoc.µù¸Ñ: ¨C¥y¸Ü³£§Ë¦¨¤@¼Ëªø«×¡A´N¤£¥Î clrtoeol() :p */
+ uschar *msg[8] =
+ {
+ "±z¤Ó±j¤F¡A´N¬O³o¼Ë¡I",
+ "±z«ç»ò¥i¯à·Q¨ì³o¤@¨B",
+ "³o¯u¬O¤Ó¯«©_¤F¡A³Ç§J",
+ "§Ú¤£ª¾¹D¸Ó»¡¨Ç¤°»ò¤F",
+ "³o¤@µÛ¯u¬O¤Ñ¤H¤âµ§§r",
+ "¤Ó¨ØªA±z¤F¡A³o¼Ë¤]¦æ",
+ "§Ö§¹¦¨¤F¡I¥[ªo¥[ªo¡I",
+ "¦nªº´ÑÃЭn§i¶D¯¸ªø³á"
+ };
+ move(21, 0);
+ prints("\033[1;3%dm%s\033[m", time(0) % 7, msg[time(0) % 8]);
+}
+
+
+static void
+show_board()
+{
+ int i, j;
+
+ vs_bar("¤Õ©ú´Ñ");
+ move(2, KM_YPOS + MAX_Y - 6); /* ¸m¤¤Åã¥Ü´ÑÃЦWºÙ */
+ outs(title);
+
+ for (i = 0; i < MAX_X; i++)
+ {
+ for (j = 0; j < MAX_Y; j++)
+ {
+ move(KM_XPOS + i, KM_YPOS + j * 2);
+ outs(piece[board[i][j]]);
+ }
+ }
+
+ move(3, 40);
+ outs("¡ô¡õ¡ö¡÷ ¤è¦VÁä");
+ move(5, 40);
+ outs("[Enter] ¿ï¨ú/¤Ï¿ï¨ú");
+ move(7, 40);
+ outs("Q/q Â÷¶}");
+ move(9, 40);
+ outs("h Ū¨ú´ÑÃнd¨Ò");
+
+#ifdef RETRACT_CHESS
+ move(11, 40);
+ outs("r ®¬´Ñ");
+#endif
+
+ move(13, 40);
+ outs("¡³ ªÅ¦ì");
+ move(14, 40);
+ outs("¡´ ´Ñ¤l");
+ move(15, 40);
+ outs("¡¸ ¿ï¨ú");
+
+ out_song();
+ move(KM_XPOS + MAX_X / 2, KM_YPOS + MAX_Y / 2 * 2 + 1); /* ¤@¶}©l±N´å¼Ð¸m¤¤ */
+}
+
+
+static int
+read_board()
+{
+ int i, j, count;
+ FILE *fp;
+ char buf[40], ans[4];
+
+ if (!(fp = fopen("etc/game/km", "r")))
+ return 0;
+
+ if (stage < 0) /* ²Ä¤@¦¸¶i¤J¹CÀ¸ */
+ {
+ fgets(buf, 4, fp);
+ NUM_TABLE = atoi(buf); /* etc/game/km ²Ä¤@¦æ°O¿ý´ÑÃÐ¼Æ */
+ sprintf(buf, "½Ð¿ï¾Ü½s¸¹ [1-%d]¡A[0] ÀH¾÷¥XÃD¡A©Î«ö [Q] Â÷¶}¡G", NUM_TABLE);
+ if (vget(b_lines, 0, buf, ans, 4, DOECHO) == 'q')
+ {
+ fclose(fp);
+ return 0;
+ }
+
+ stage = atoi(ans) - 1;
+ if (stage < 0 || stage >= NUM_TABLE) /* ÀH¾÷¥XÃD */
+ stage = time(0) % NUM_TABLE;
+ }
+
+ fseek(fp, 4 + stage * (2 * MAX_X * MAX_Y + 14), SEEK_SET);
+ /* 4: ²Ä¤@¦æªº¤T¦ì¼Æ´ÑÃмƥØ\n 14: \n#999´ÑÃЦWºÙ\n */
+
+ fscanf(fp, "%s", &title); /* ´ÑÃЦWºÙ */
+
+ count = 0;
+ for (i = 0; i < MAX_X; i++)
+ {
+ for (j = 0; j < MAX_Y; j++)
+ {
+ fscanf(fp, "%d", &board[i][j]);
+ if (board[i][j] & TILE_CHESS)
+ {
+ count++;
+ }
+ }
+ }
+ fclose(fp);
+ return count;
+}
+
+
+static inline int
+valid_pos(x, y)
+ int x, y;
+{
+ if (x < 0 || x >= MAX_X || y < 0 || y >= MAX_Y ||
+ board[x][y] == TILE_NOUSE) /* TILE_NOUSE = 0 ¤£¯à¥Î & operation */
+ {
+ return 0;
+ }
+ return 1;
+}
+
+
+static void
+get_pos(x, y)
+ int *x, *y;
+{
+ int ch;
+ while (1)
+ {
+ ch = vkey();
+ if (ch == KEY_UP && valid_pos(cx - 1, cy))
+ {
+ cx--;
+ move(KM_XPOS + cx, KM_YPOS + cy * 2 + 1);
+ }
+ else if (ch == KEY_DOWN && valid_pos(cx + 1, cy))
+ {
+ cx++;
+ move(KM_XPOS + cx, KM_YPOS + cy * 2 + 1);
+ }
+ else if (ch == KEY_LEFT && valid_pos(cx, cy - 1))
+ {
+ cy--;
+ move(KM_XPOS + cx, KM_YPOS + cy * 2 + 1);
+ }
+ else if (ch == KEY_RIGHT && valid_pos(cx, cy + 1))
+ {
+ cy++;
+ move(KM_XPOS + cx, KM_YPOS + cy * 2 + 1);
+ }
+ else if (ch == 'h')
+ {
+ more("etc/game/km.hlp", NULL);
+ show_board();
+ move(KM_XPOS + cx, KM_YPOS + cy * 2 + 1);
+ }
+ else if (ch == 'q' || ch == 'Q')
+ {
+ vmsg(MSG_QUITGAME);
+ *x = -1;
+ break;
+ }
+ else if (ch == '\n')
+ {
+ *x = cx;
+ *y = cy;
+ break;
+ }
+#ifdef RETRACT_CHESS
+ else if (ch == 'r')
+ {
+ *x = -2;
+ break;
+ }
+#endif
+ }
+}
+
+
+static inline void
+jump(fx, fy, tx, ty)
+ int fx, fy, tx, ty; /* From (fx, fy) To (tx, ty) */
+{
+ out_song();
+
+ board[fx][fy] = TILE_BLANK;
+ move(KM_XPOS + fx, KM_YPOS + fy * 2);
+ outs(piece[1]);
+
+ board[(fx + tx) / 2][(fy + ty) / 2] = TILE_BLANK;
+ move(KM_XPOS + (fx + tx) / 2, KM_YPOS + (fy + ty));
+ outs(piece[1]);
+
+ board[tx][ty] = TILE_CHESS;
+ move(KM_XPOS + tx, KM_YPOS + ty * 2);
+ outs(piece[2]);
+ move(KM_XPOS + tx, KM_YPOS + ty * 2 + 1);
+
+#ifdef RETRACT_CHESS
+ route[step][0] = fx;
+ route[step][1] = fy;
+ route[step][2] = tx;
+ route[step][3] = ty;
+ step++;
+#endif
+}
+
+
+#ifdef RETRACT_CHESS
+static inline void
+retract()
+{
+ int fx, fy, tx, ty;
+
+ out_song();
+
+ step--;
+ ty = route[step][3];
+ tx = route[step][2];
+ fy = route[step][1];
+ fx = route[step][0];
+
+ board[tx][ty] = TILE_BLANK;
+ move(KM_XPOS + tx, KM_YPOS + ty * 2);
+ outs(piece[1]);
+
+ board[(fx + tx) / 2][(fy + ty) / 2] = TILE_CHESS;
+ move(KM_XPOS + (fx + tx) / 2, KM_YPOS + (fy + ty));
+ outs(piece[2]);
+
+ board[fx][fy] = TILE_CHESS;
+ move(KM_XPOS + fx, KM_YPOS + fy * 2);
+ outs(piece[2]);
+ move(KM_XPOS + fx, KM_YPOS + fy * 2);
+ cx = fx;
+ cy = fy;
+}
+#endif
+
+
+static inline int
+check(fx, fy, tx, ty)
+ int fx, fy, tx, ty;
+{
+ if ((board[(fx + tx) / 2][(fy + ty) / 2] & TILE_CHESS) &&
+ ((abs(fx - tx) == 2 && fy == ty) || (fx == tx && abs(fy - ty) == 2)))
+ {
+ return 1;
+ }
+ return 0;
+}
+
+
+static inline int
+live()
+{
+ int dir[4][2] = {1, 0, -1, 0, 0, 1, 0, -1};
+ int i, j, k, nx, ny, nx2, ny2;
+ for (i = 0; i < MAX_X; i++)
+ {
+ for (j = 0; j < MAX_Y; j++)
+ {
+ for (k = 0; k < 4; k++)
+ {
+ nx = i + dir[k][0];
+ ny = j + dir[k][1];
+ nx2 = nx + dir[k][0];
+ ny2 = ny + dir[k][1];
+ if (valid_pos(nx2, ny2) && (board[i][j] & TILE_CHESS) &&
+ (board[nx][ny] & TILE_CHESS) && (board[nx2][ny2] & TILE_BLANK))
+ {
+ return 1;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+
+#ifdef LOG_KM
+static void
+log_km(fp)
+ FILE *fp;
+{
+ int i, j;
+
+ for (i = 0; i < MAX_X; i++)
+ {
+ for (j = 0; j < MAX_Y; j++)
+ {
+ fprintf(fp, "%s", piece[board[i][j]]);
+ }
+ fprintf(fp, "\n");
+ }
+ fprintf(fp, "\n");
+}
+#endif
+
+
+int
+main_km()
+{
+ int fx, fy, tx, ty, count;
+
+#ifdef LOG_KM
+ char fpath[64];
+ FILE *fp;
+#endif
+
+ stage = -1;
+
+start_game:
+
+ if (!(count = read_board()))
+ return 0;
+
+#ifdef LOG_KM
+ usr_fpath(fpath, cuser.userid, "km.log");
+ fp = fopen(fpath, "w");
+ fprintf(fp, "%s %s (%s)\n", str_author1, cuser.userid, cuser.username);
+ fprintf(fp, "¼ÐÃD: ¤Õ©ú´ÑÃÐ %s ¯}¸Ñ¹Lµ{\n®É¶¡: %s\n\n", title, Now());
+ fprintf(fp, "%s\n\n", title);
+ log_km(fp);
+#endif
+
+#ifdef RETRACT_CHESS
+ step = 0;
+#endif
+
+ show_board();
+ cx = MAX_X / 2;
+ cy = MAX_Y / 2;
+
+ while (1)
+ {
+ if (count == 1 && board[MAX_X / 2][MAX_Y / 2] & TILE_CHESS)
+ { /* ³Ì«á¤@¤l­n¦b¥¿¤¤¶¡ */
+ vmsg("®¥³ß±z¦¨¥\\¤F");
+
+#ifdef LOG_KM
+ ve_banner(fp, 0);
+ fclose(fp);
+
+ if (vans("±z¬O§_­n§â§¹¦¨ªº´ÑÃЫO¦s¦b«H½c¤¤(Y/N)¡H[Y] ") != 'n')
+ {
+ char buf[60];
+
+ sprintf(buf, "¤Õ©ú´ÑÃÐ %s ¯}¸Ñ¹Lµ{", title);
+ mail_self(fpath, cuser.userid, buf, MAIL_READ);
+ }
+ unlink(fpath);
+#endif
+
+ switch (vans("½Ð¿ï¾Ü (1)Ä~Äò¤U¤@Ãö (2)­«·s¬D¾Ô¦¹Ãö (Q)Â÷¶} ¡H[1] "))
+ {
+ case 'q':
+ goto abort_game;
+ case '2':
+ stage--;
+ default:
+ if (++stage >= NUM_TABLE)
+ stage = 0;
+ goto start_game;
+ }
+ }
+ if (!live())
+ {
+#ifdef LOG_KM
+ unlink(fpath);
+#endif
+
+ vmsg("ÁV¿|...¨S´Ñ¤F...@@");
+
+ switch (vans("½Ð¿ï¾Ü (1)Ä~Äò¤U¤@Ãö (2)­«·s¬D¾Ô¦¹Ãö (Q)Â÷¶} ¡H[2] "))
+ {
+ case 'q':
+ goto abort_game;
+ case '1':
+ if (++stage >= NUM_TABLE)
+ stage = 0;
+ default:
+ goto start_game;
+ }
+ }
+
+ while (1) /* ²Ä¤@¦¸ */
+ {
+ get_pos(&fx, &fy);
+ if (fx < 0)
+ {
+#ifdef RETRACT_CHESS
+ if (fx == -2)
+ {
+ if (step) /* ¤@¨B³£ÁÙ¨S¨«¡A¤£¯à®¬´Ñ */
+ {
+ retract();
+ count++;
+#ifdef LOG_KM
+ fprintf(fp, "®¬´Ñ¡A¦^¨ì¤W¤@¨B\n");
+ log_km(fp);
+#endif
+ }
+ continue;
+ }
+#endif
+ goto abort_game;
+ }
+ if (!(board[fx][fy] & TILE_CHESS))
+ {
+ continue;
+ }
+ else /* ¿ï¤l */
+ {
+ move(KM_XPOS + fx, KM_YPOS + fy * 2);
+ outs(piece[3]);
+ move(KM_XPOS + fx, KM_YPOS + fy * 2 + 1);
+ break;
+ }
+ }
+
+ while (1) /* ²Ä¤G¦¸ */
+ {
+ get_pos(&tx, &ty);
+ if (tx < 0)
+ {
+#ifdef RETRACT_CHESS
+ if (tx == -2)
+ {
+ continue; /* ­n¨ú®ø¿ï¤l¤~¯à®¬´Ñ */
+ }
+#endif
+ goto abort_game;
+ }
+ if (fx == tx && fy == ty) /* ©ñ±ó¿ï¤l */
+ {
+ move(KM_XPOS + tx, KM_YPOS + ty * 2);
+ outs(piece[2]);
+ move(KM_XPOS + tx, KM_YPOS + ty * 2 + 1);
+ break;
+ }
+ else if (!(board[tx][ty] & TILE_BLANK) || !check(fx, fy, tx, ty)) /* ¿ï¸õªº¦a¤è¤£¯à¸õ */
+ {
+ continue;
+ }
+ else /* ¸õ¨ì¸Ó¦a¤è */
+ {
+ jump(fx, fy, tx, ty);
+ count--;
+#ifdef LOG_KM
+ log_km(fp);
+#endif
+ break;
+ }
+ }
+ }
+abort_game:
+ return 0;
+}
+
+#endif /* HAVE_GAME */
diff --git a/game/liteon.c b/game/liteon.c
new file mode 100644
index 0000000..c81b269
--- /dev/null
+++ b/game/liteon.c
@@ -0,0 +1,216 @@
+/*-------------------------------------------------------*/
+/* liteon.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : ¶}¿O¹CÀ¸ */
+/* create : 02/05/23 */
+/* update : / / */
+/* author : Gein.bbs@csdc.twbbs.org */
+/* recast : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+
+#ifdef HAVE_GAME
+
+#define MAX_LEVEL (b_lines - 3)
+
+enum
+{
+ TL_XPOS = 2,
+ TL_YPOS = 5,
+
+ /* ¥Î bitwise operators */
+ TILE_BLANK = 0, /* ·t°Ï */
+ TILE_LIGHT = 1 /* «G°Ï */
+};
+
+
+static int cx, cy; /* ¥Ø«e©Ò¦b´å¼Ð */
+static int level; /* µ¥¯Å¡A¦P®É¤]¬O board ªºÃäªø */
+static int onturn; /* ¦³´X­Ó¿O¥´¶}¤F */
+static int candle; /* ÂI¤F´X¦¸ÄúÀë */
+static int tl_board[T_LINES - 4][T_LINES - 4];
+
+
+static void
+tl_setb() /* set board all 0 */
+{
+ int i, j;
+
+ move(1, 0);
+ clrtobot();
+
+ for (i = 0; i < level; i++)
+ {
+ move(i + TL_XPOS, TL_YPOS);
+ for (j = 0; j < level; j++)
+ {
+ tl_board[i][j] = TILE_BLANK;
+ outs("¡³");
+ }
+ }
+
+ cx = cy = onturn = candle = 0;
+ move(TL_XPOS, TL_YPOS + 1); /* move back to (0, 0) */
+}
+
+
+static void
+tl_draw(x, y) /* set/reset and draw a tile */
+ int x, y;
+{
+ tl_board[x][y] ^= TILE_LIGHT;
+ move(x + TL_XPOS, y * 2 + TL_YPOS);
+ if (tl_board[x][y] == TILE_BLANK) /* on-turn -> off-turn */
+ {
+ onturn--;
+ outs("¡³");
+ }
+ else /* off-turn -> on-turn */
+ {
+ onturn++;
+ outs("¡´");
+ }
+}
+
+
+static void
+tl_turn() /* turn light and light arround it */
+{
+ tl_draw(cx, cy);
+
+ if (cx > 0)
+ tl_draw(cx - 1, cy);
+
+ if (cx < level - 1)
+ tl_draw(cx + 1, cy);
+
+ if (cy > 0)
+ tl_draw(cx, cy - 1);
+
+ if (cy < level - 1)
+ tl_draw(cx, cy + 1);
+}
+
+
+static void
+tl_candle() /* cheat: use candle */
+{
+ /* itoc.µù¸Ñ: ¦]¬°¤j®a³£¯}¤£¤F³o¹CÀ¸¡A©Ò¥H´£¨Ñ¤@¤U§@¹ú¥ÎªºÂIÄúÀë */
+ tl_draw(cx, cy);
+ candle++;
+}
+
+static int /* 1:win 0:lose */
+tl_play() /* play turn_light */
+{
+ tl_setb();
+
+ while (onturn != level * level)
+ {
+ switch (vkey())
+ {
+ case KEY_LEFT:
+ cy--;
+ if (cy < 0)
+ cy = level - 1;
+ break;
+
+ case KEY_RIGHT:
+ cy++;
+ if (cy == level)
+ cy = 0;
+ break;
+
+ case KEY_UP:
+ cx--;
+ if (cx < 0)
+ cx = level - 1;
+ break;
+
+ case KEY_DOWN:
+ cx++;
+ if (cx == level)
+ cx = 0;
+ break;
+
+ case 'c':
+ tl_candle();
+ break;
+
+ case ' ':
+ case '\n':
+ tl_turn();
+ break;
+
+ case 'r':
+ tl_setb();
+ break;
+
+ case 'q':
+ return 0;
+ }
+ move(cx + TL_XPOS, cy * 2 + TL_YPOS + 1); /* move back to current (x, y) */
+ }
+ return 1;
+}
+
+
+int
+main_liteon()
+{
+ char ans[5], buf[80];
+
+ sprintf(buf, "½Ð¿ï¾Üµ¥¯Å(1¡ã%d)¡A©Î«ö [Q] Â÷¶}¡G", MAX_LEVEL);
+ level = vget(b_lines, 0, buf, ans, 3, DOECHO);
+ if (level == 'q' || level == 'Q')
+ {
+ return XEASY;
+ }
+ else
+ {
+ level = atoi(ans);
+ if (level < 1 || level > MAX_LEVEL)
+ return XEASY;
+ }
+
+ vs_bar("¶}¿O¹CÀ¸");
+ move(4, 13);
+ outs("«e±¡´£­n¡G");
+ move(5, 15);
+ outs("¦³¤@¤Ñ¡A¤p«Ø¦^¨ì®aµo²{¿O³£³QÃö¤F¡C");
+ move(6, 15);
+ outs("¥i¬O¥L®aªº¿O¦³¤@­Ó¯S©Ê¡A¨º´N¬O¡G");
+ move(7, 15);
+ outs("·í¤@·ø¿O³Q«ö¤U¶}Ãö¥H«á¡A¥L©P³òªº¿O");
+ move(8, 15);
+ outs("­ì¥»«Gªº¡A´N·|ÅÜ·t¡A­ì¥»·tªº¡A´N·|ÅÜ«G¡C -____-#");
+ move(9, 15);
+ outs("²{¦b´N½ÐÁo©úªº±zÀ°¥L§â©Ò¦³¿O¥´¶}§a¡I");
+
+ move(11, 13);
+ outs("«öÁ仡©ú¡G");
+ move(12, 15);
+ outs("¡ô¡õ¡ö¡÷ ²¾°Ê¤è¦V");
+ move(13, 15);
+ outs("Enter/Space ¤Á´«¶}Ãö");
+ move(14, 15);
+ outs("c ÂI¿UÄúÀë [±K§Þ]");
+ move(15, 15);
+ outs("r ­«·s¨Ó¹L");
+ move(16, 15);
+ outs("q Â÷¶}¹CÀ¸");
+
+ vmsg(NULL);
+
+ if (tl_play()) /* if win */
+ {
+ sprintf(buf, "®¥³ß±z¦¨¥\\¤F (¥Î¤F %d ®ÚÄúÀë)", candle);
+ vmsg(buf);
+ }
+
+ return 0;
+}
+#endif /* HAVE_GAME */
diff --git a/game/marie.c b/game/marie.c
new file mode 100644
index 0000000..8ffbb4b
--- /dev/null
+++ b/game/marie.c
@@ -0,0 +1,189 @@
+/*-------------------------------------------------------*/
+/* marie.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : ¤pº¿²ú¼Ö¶é¹CÀ¸ */
+/* create : / / */
+/* update : 01/04/26 */
+/* author : unknown */
+/* recast : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+#ifdef HAVE_GAME
+
+#define MAX_MARIEBET 50000 /* ³Ì¦h©ã¨ì 50000 ¤¸ */
+
+
+static inline int
+get_item() /* ¶Ã¼Æ¿ï¨ú­n¤¤ªº¶µ¥Ø */
+{
+
+#if 0
+½s¸¹¡G 0 1 2 3 4 5 6 7 8 9
+¾÷²v¡G 200 2 5 10 1 20 40 100 500 122 /1000
+½ß²v¡G 5 500 200 100 1000 50 25 10 2 0
+#endif
+
+ int randnum = rnd(1000); /* ½ß²v * ¾÷²v = ´Á±æ­È (¨C©ã 1 ¤¸©Ò¦^¦¬ªºª÷ÃB) */
+
+ if (randnum < 500) /* 2 * 0.500 = 1 */
+ return 8;
+ if (randnum < 700) /* 5 * 0.200 = 1 */
+ return 0;
+ if (randnum < 800) /* 10 * 0.100 = 1 */
+ return 7;
+ if (randnum < 840) /* 25 * 0.040 = 1 */
+ return 6;
+ if (randnum < 860) /* 50 * 0.020 = 1 */
+ return 5;
+ if (randnum < 870) /* 100 * 0.010 = 1 */
+ return 3;
+ if (randnum < 875) /* 200 * 0.005 = 1 */
+ return 2;
+ if (randnum < 877) /* 500 * 0.002 = 1 */
+ return 1;
+ if (randnum < 878) /* 1000 * 0.001 = 1 */
+ return 4;
+
+ return 9; /* »ÊÁ´fÅU¾÷²v¬O 0.122 */
+}
+
+
+int
+main_marie()
+{
+ int c_flag[7] = {1, 5, 10, 50, 100, 500, 1000}; /* ­¿²v */
+ int price[10] = {5, 500, 200, 100, 1000, 50, 25, 10, 2, 0}; /* ½ä²v */
+ int x[9] = {0}; /* ¦U¶µ©ãª÷ */
+
+ int w; /* ­¿²vªººØÃþ */
+ int flag; /* flag = c_flag[w] */
+ int item; /* ¤¤¼úªº¶µ¥Ø */
+ int xtotal; /* Á`©ãª÷ */
+ int i, ch;
+ FILE *fp;
+ char buf[STRLEN];
+
+ if (HAS_STATUS(STATUS_COINLOCK))
+ {
+ vmsg(msg_coinlock);
+ return XEASY;
+ }
+
+ if (!(fp = fopen("etc/game/marie", "r")))
+ return XEASY;
+
+ vs_bar("¤pº¿²ú¼Ö¶é");
+ move(1, 0);
+ while (fgets(buf, STRLEN, fp)) /* ¦L¥X½ä³õ¤º³¡Â\³] */
+ outs(buf);
+ fclose(fp);
+
+ w = 0; /* ²Ä¤@¦¸¶i¤J¿ï¾Ü¤@­¿¡A²Ä¤G¦¸¥H«á«h¸ò¤W¦¸ª±¤@¼Ë */
+ flag = c_flag[w];
+ item = 0;
+ xtotal = 0;
+
+ while (1)
+ {
+ move(9, 44);
+ prints("\033[1m±z¨­¤WÁÙ¦³Äw½X %8d ¤¸\033[m", cuser.money);
+ move(10, 44);
+ prints("\033[1m¥Ø«e©ãª`ªº­¿²v¬O \033[46m%6d ­¿\033[m", flag);
+
+ move(b_lines - 3, 0);
+ for (i = 0; i < 9; i++)
+ prints(" %5d", x[i]);
+
+ ch = igetch(); /* ¤£»Ý¥Î¨ì vkey() */
+ switch (ch)
+ {
+ case 'w': /* ¤Á´«­¿²v */
+ w = (w + 1) % 7;
+ flag = c_flag[w]; /* ¤Á´«­¿²v®É¤~»Ý­n­«³] flag */
+ break;
+
+ case 'a': /* ¥þÀ£ */
+ i = 9 * flag;
+ if ((xtotal + i <= MAX_MARIEBET) && (cuser.money >= i))
+ {
+ cuser.money -= i;
+ xtotal += i;
+ for (i = 0; i <= 8; i++)
+ x[i] += flag;
+ }
+ break;
+
+ case '1': /* ¤À§O¤Uª` */
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if ((xtotal + flag <= MAX_MARIEBET) && (cuser.money >= flag))
+ {
+ cuser.money -= flag;
+ xtotal += flag;
+ x[ch - '1'] += flag;
+ }
+ break;
+
+ case 's': /* ¶}©l¹CÀ¸ */
+ case '\n':
+ if (x[0] || x[1] || x[2] || x[3] || x[4] || x[5] || x[6] || x[7] || x[8])
+ { /* ¦³¤Uª`¤~¯à¶}©lª± */
+ move(15, 5 + 7 * item);
+ outs(" "); /* ²M°£¤W¦¸¤¤ªº¶µ¥Ø */
+ item = get_item();
+ if (item != 9) /* item=9 ¬O»ÊÁ´fÅU */
+ {
+ move(15, 5 + 7 * item);
+ outs("¡´"); /* ø¤W³o¦¸¤¤ªº¶µ¥Ø */
+
+ if (x[item])
+ {
+ i = x[item] * price[item];
+ addmoney(i);
+ sprintf(buf, "±z¥i±o %d ¤¸", i);
+ vmsg(buf);
+ }
+ else
+ {
+ vmsg("«Ü©êºp¡A±z¨S¦³©ã¤¤");
+ }
+ }
+ else
+ {
+ vmsg("«Ü©êºp¡A»ÊÁ´fÅU");
+ }
+
+ move(b_lines, 0);
+ clrtoeol(); /* ²M°£ vmsg() */
+
+ /* ¦U¶µ½äª÷Âk¹s */
+ xtotal = 0;
+ for (i = 0; i < 9; i++)
+ x[i] = 0;
+ }
+ else
+ {
+ goto abort_game;
+ }
+ break;
+
+ case 'q': /* Â÷¶} */
+ goto abort_game;
+
+ } /* switch µ²§ô */
+
+ } /* while °j°éµ²§ô */
+
+abort_game:
+ return 0;
+}
+#endif /* HAVE_GAME */
diff --git a/game/mine.c b/game/mine.c
new file mode 100644
index 0000000..550f85e
--- /dev/null
+++ b/game/mine.c
@@ -0,0 +1,474 @@
+/*-------------------------------------------------------*/
+/* mine.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : ½ò¦a¹p¹CÀ¸ */
+/* create : 01/02/15 */
+/* update : 01/03/01 */
+/* author : piaip.bbs@sob.twbbs.org */
+/* recast : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+#ifdef HAVE_GAME
+
+#define _CHINESE_ /* ¤¤¤å¦r symbol */
+
+enum
+{
+ /* MINE_XPOS + MAP_MAX_X ­n¤p©ó b_lines - 2 = 21 *
+ * MINE_YPOS + MAP_MAX_Y * 2 ­n¤p©ó STRLEN - 1 = 79 *
+ * MINE_YPOS ­n¨¬°÷¨Ï out_prompt() ©ñ¤J */
+
+ MINE_XPOS = 0,
+ MINE_YPOS = 17,
+ MAP_MAX_X = 20, /* ¡õ x ¤è¦V */
+ MAP_MAX_Y = 30, /* ¡÷ y ¤è¦V */
+
+ /* These are flags for bitwise operators */
+ TILE_BLANK = 0, /* ¨S¦³¦a¹p */
+ TILE_MINE = 1, /* ¦³¦a¹p */
+ TILE_TAGGED = 0x10, /* ³Q¼Ð°O */
+ TILE_EXPAND = 0x20 /* ¤w³Q®i¶} */
+};
+
+
+static char MineMap[MAP_MAX_X + 2][MAP_MAX_Y + 2]; /* ¦a¹Ï¤W¨C®æªºÄÝ©Ê */
+static char MineNei[MAP_MAX_X + 2][MAP_MAX_Y + 2]; /* ¦a¹Ï¤W¨C®æ¾F©~¦³¦h¤Ö¦a¹p */
+
+static int MAP_X, MAP_Y; /* ´Ñ½L¤j¤p */
+static int cx, cy; /* current (x, y) */
+static int TotalMines; /* ¤w¼Ð°Oªº¦a¹p¼Æ */
+static int TaggedMines; /* ¤w¼Ð°Oªº¦a¹p¼Æ */
+static time_t InitTime; /* ¶}©lª±ªº®É¶¡ */
+static int LoseGame; /* 1: ¿é¤F 0: ÁÙ¦bª± */
+static int EndGame; /* 1: Â÷¶}¹CÀ¸ 0: ÁÙ¦bª± */
+
+
+#ifdef _CHINESE_
+static char symTag[3] = "¡°"; /* ¼Ð°O¦a¹p/¦¹³B¦³¦a¹p¥B¦³³Q¼Ð¥Ü */
+static char symMine[3] = "¡ó"; /* /¦¹³B¦³¦a¹p¦ý¨S³Q¼Ð¥Ü */
+static char symWrong[3] = "¢æ"; /* /¦¹³B¨S¦a¹p¦ý¦³³Q¼Ð¥Ü */
+static char symBlank[3] = "¡½"; /* ¥¼³Q®i¶}/¦¹³B¨S¦a¹p¥B¨S³Q®i¶} */
+static char *strMines[9] = {"¡@", "¢°", "¢±", "¢²", "¢³", "¢´", "¢µ", "¢¶", "¢·"}; /* ®ÇÃ䦳´XÁû¦a¹p */
+#else
+static char symTag[3] = " M";
+static char symMine[3] = " m";
+static char symWrong[3] = " X";
+static char symBlank[3] = " o";
+static char *strMines[9] = {" _", " 1", " 2", " 3", " 4", " 5", " 6", " 7", " 8"};
+#endif
+
+
+static inline int
+count_neighbor(x, y, bitmask)
+ int x, y, bitmask;
+{
+ return (((MineMap[x - 1][y - 1] & bitmask) + (MineMap[x - 1][y] & bitmask) +
+ (MineMap[x - 1][y + 1] & bitmask) + (MineMap[x][y - 1] & bitmask) +
+ (MineMap[x][y] & bitmask) + (MineMap[x][y + 1] & bitmask) +
+ (MineMap[x + 1][y - 1] & bitmask) + (MineMap[x + 1][y] & bitmask) +
+ (MineMap[x + 1][y + 1] & bitmask)) / bitmask);
+}
+
+
+static void
+init_map()
+{
+ int x, y, i;
+
+ /* ³]©w¦³®Ä´Ñ½L */
+
+ for (x = 0; x < MAP_X + 2; x++)
+ {
+ for (y = 0; y < MAP_Y + 2; y++)
+ {
+ MineMap[x][y] = TILE_BLANK;
+ if (x == 0 || y == 0 || x == MAP_X + 1 || y == MAP_Y + 1)
+ MineMap[x][y] |= TILE_EXPAND;
+ }
+ }
+
+ /* ³]©w¦a¹p©Ò¦b */
+
+ for (i = 0; i < TotalMines;)
+ {
+ x = rnd(MAP_X) + 1;
+ y = rnd(MAP_Y) + 1;
+
+ if (MineMap[x][y] == TILE_BLANK)
+ {
+ MineMap[x][y] = TILE_MINE;
+ i++;
+ }
+ }
+
+ /* ºâ¥X©Ò¦³®æªº¾F©~¦³¦h¤Ö¦a¹p */
+
+ for (x = 1; x <= MAP_X; x++)
+ {
+ for (y = 1; y <= MAP_Y; y++)
+ {
+ MineNei[x][y] = count_neighbor(x, y, TILE_MINE);
+ }
+ }
+
+ /* ²{¦b¶}©l­p®É¡A¦]¬° out_map() ªº out_info() ­n¥Î¨ì */
+ InitTime = time(0);
+}
+
+
+static void
+out_prompt()
+{
+ /* outs() ¸Ì­±ªº±Ô­z¤£±o¶W¹L MINE_YPOS¡A§_«h·|¿ù¶Ã */
+ move(3, 0);
+ outs("«öÁ仡©ú¡G");
+ move(5, 0);
+ outs("²¾°Ê ¤è¦VÁä");
+ move(7, 0);
+ outs("½¶} ªÅ¥ÕÁä");
+ move(9, 0);
+ outs("¼Ð°O¦a¹p ¢î");
+ move(11, 0);
+ outs("±½¹p ¢ì");
+ move(13, 0);
+ outs("Â÷¶} ¢ù");
+}
+
+
+static inline void
+out_song()
+{
+ uschar *msg[8] =
+ {
+ "«D¿FªyµL¥H©ú§Ó¡A«D¹çÀRµL¥H­P»·", /* ½Ñ¸¯«G»|¤l®Ñ */
+ "¤@µ°¤@¶º¡A·í«ä±o¨Ó¤£©ö¡F¥bµ·¥bÁ\\¡A«í©Àª«¤OºûÁ}", /* ¦¶¤l­P®a®æ¨¥ */
+ "©y¥¼«B¦Óº÷Á[¡A¤ðÁ{´÷¦Ó±¸¤«", /* ¦¶¤l­P®a®æ¨¥ */
+ "µL©Àº¸¯ª¡A¦Ö²ç³Ö¼w", /* ¤j¶® */
+ "¤j¾Ç¤§¹D¡A¦b©ú©ú¼w¡A¦b¿Ë¥Á¡A¦b¤î©ó¦Üµ½", /* ¤j¾Ç */
+ "¤Ñ©R¤§¿×©Ê¡A²v©Ê¤§»w¹D¡A²ç¹D¤§¿×±Ð", /* ¤¤±e */
+ "¤£±w¤H¤§¤£ª¾¤v¡A±w¤£ª¾¤H¤]", /* ½×»y£»¾Ç¦Ó */
+ "´Â»D¹D¡A¤i¦º¥i¨o" /* ½×»y£»¨½¤¯ */
+ };
+ move(b_lines - 2, 0);
+ prints("\033[1;3%dm%s\033[m", time(0) % 7, msg[time(0) % 8]);
+ clrtoeol();
+}
+
+
+static void
+out_info()
+{
+ move(b_lines - 1, 0);
+ prints("©Òªá®É¶¡: %.0lf ¬í¡A³Ñ¤U %d ­Ó¦a¹p¥¼¼Ð°O¡C",
+ difftime(time(0), InitTime), TotalMines - TaggedMines);
+ clrtoeol();
+
+ out_song();
+}
+
+
+static inline void
+out_map()
+{
+ int x, y;
+
+ vs_bar("«lÃz½ò¦a¹p");
+
+ for (x = 1; x <= MAP_X; x++)
+ {
+ move(MINE_XPOS + x, MINE_YPOS + 2);
+ for (y = 1; y <= MAP_Y; y++)
+ outs(symBlank);
+ }
+
+ out_prompt();
+ out_info();
+ move(MINE_XPOS + cx, MINE_YPOS + cy * 2 + 1); /* move to (0, 0) */
+}
+
+
+static void
+draw_map() /* µe¥X§¹¾ãµª®× */
+{
+ int x, y;
+
+ vs_bar("«lÃz½ò¦a¹p");
+
+ for (x = 1; x <= MAP_X; x++)
+ {
+ move(MINE_XPOS + x, MINE_YPOS + 2);
+ for (y = 1; y <= MAP_Y; y++)
+ {
+ if (MineMap[x][y] & TILE_TAGGED)
+ {
+ if (!(MineMap[x][y] & TILE_MINE))
+ outs(symWrong);
+ else
+ outs(symTag);
+ }
+ else if (MineMap[x][y] & TILE_EXPAND)
+ outs(strMines[MineNei[x][y]]);
+ else if (MineMap[x][y] & TILE_MINE)
+ outs(symMine);
+ else
+ outs(symBlank);
+ }
+ }
+}
+
+
+static void
+expand_map(x, y)
+ int x, y;
+{
+ if (MineMap[x][y] & TILE_TAGGED || MineMap[x][y] & TILE_EXPAND)
+ return;
+
+ if (MineMap[x][y] & TILE_MINE && !(MineMap[x][y] & TILE_TAGGED))
+ {
+ LoseGame = 1;
+ return;
+ }
+
+ MineMap[x][y] |= TILE_EXPAND;
+ move(MINE_XPOS + x, MINE_YPOS + y * 2);
+ outs(strMines[MineNei[x][y]]);
+
+ if (MineNei[x][y] == 0)
+ {
+ if ((MineMap[x - 1][y] & TILE_EXPAND) == 0)
+ expand_map(x - 1, y);
+ if ((MineMap[x][y - 1] & TILE_EXPAND) == 0)
+ expand_map(x, y - 1);
+ if ((MineMap[x + 1][y] & TILE_EXPAND) == 0)
+ expand_map(x + 1, y);
+ if ((MineMap[x][y + 1] & TILE_EXPAND) == 0)
+ expand_map(x, y + 1);
+ if ((MineMap[x - 1][y - 1] & TILE_EXPAND) == 0)
+ expand_map(x - 1, y - 1);
+ if ((MineMap[x + 1][y - 1] & TILE_EXPAND) == 0)
+ expand_map(x + 1, y - 1);
+ if ((MineMap[x - 1][y + 1] & TILE_EXPAND) == 0)
+ expand_map(x - 1, y + 1);
+ if ((MineMap[x + 1][y + 1] & TILE_EXPAND) == 0)
+ expand_map(x + 1, y + 1);
+ }
+}
+
+
+static inline void
+trace_map(x, y)
+ int x, y;
+{
+ if (MineMap[x][y] & TILE_EXPAND &&
+ MineNei[x][y] == count_neighbor(x, y, TILE_TAGGED))
+ {
+ expand_map(x - 1, y);
+ expand_map(x, y - 1);
+ expand_map(x + 1, y);
+ expand_map(x, y + 1);
+ expand_map(x - 1, y - 1);
+ expand_map(x + 1, y - 1);
+ expand_map(x - 1, y + 1);
+ expand_map(x + 1, y + 1);
+ }
+}
+
+
+static inline void
+play_mine()
+{
+ int ch;
+
+ LoseGame = 0;
+ EndGame = 0;
+
+ while (!LoseGame && (ch = vkey()))
+ {
+ switch (ch)
+ {
+ case KEY_ESC:
+ case 'q':
+ EndGame = 1;
+ return;
+
+ /* °µ¥ô¦ó°Ê§@³£­n§â´å¼Ð¦^´_¨ì­ì¨Óªº¦ì¸m */
+
+ case KEY_UP:
+ if (cx > 1)
+ {
+ cx--;
+ move(MINE_XPOS + cx, MINE_YPOS + cy * 2 + 1);
+ }
+ break;
+
+ case KEY_DOWN:
+ if (cx < MAP_X)
+ {
+ cx++;
+ move(MINE_XPOS + cx, MINE_YPOS + cy * 2 + 1);
+ }
+ break;
+
+ case KEY_LEFT:
+ if (cy > 1)
+ {
+ cy--;
+ move(MINE_XPOS + cx, MINE_YPOS + cy * 2 + 1);
+ }
+ break;
+
+ case KEY_RIGHT:
+ if (cy < MAP_Y)
+ {
+ cy++;
+ move(MINE_XPOS + cx, MINE_YPOS + cy * 2 + 1);
+ }
+ break;
+
+ case 'd':
+ case '\n':
+ trace_map(cx, cy);
+ out_info();
+ move(MINE_XPOS + cx, MINE_YPOS + cy * 2 + 1);
+ break;
+
+ case ' ':
+ expand_map(cx, cy);
+ out_info();
+ move(MINE_XPOS + cx, MINE_YPOS + cy * 2 + 1);
+ break;
+
+ case 'f':
+ if (MineMap[cx][cy] & TILE_EXPAND)
+ {
+ if (MineMap[cx][cy] & TILE_TAGGED) /* ¥»¨Ó³Q¼Ð°O, ¨ú®ø */
+ {
+ TaggedMines--;
+ MineMap[cx][cy] ^= TILE_EXPAND;
+ MineMap[cx][cy] ^= TILE_TAGGED;
+ move(MINE_XPOS + cx, MINE_YPOS + cy * 2);
+ outs(symBlank);
+ }
+ }
+ else /* ¥»¨Ó¨S¼Ð°O, ¤W¼Ð°O */
+ {
+ TaggedMines++;
+ MineMap[cx][cy] ^= TILE_EXPAND;
+ MineMap[cx][cy] ^= TILE_TAGGED;
+ move(MINE_XPOS + cx, MINE_YPOS + cy * 2);
+ outs(symTag);
+ if (TaggedMines == TotalMines)
+ return;
+ }
+ out_info();
+ move(MINE_XPOS + cx, MINE_YPOS + cy * 2 + 1);
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+
+static inline int
+win()
+{
+ int x, y;
+
+ for (x = 1; x <= MAP_X; x++)
+ {
+ for (y = 1; y <= MAP_Y; y++)
+ {
+ if (((MineMap[x][y] & TILE_TAGGED) && !(MineMap[x][y] & TILE_MINE)) ||
+ (!(MineMap[x][y] & TILE_TAGGED) && (MineMap[x][y] & TILE_MINE)))
+ {
+ return 0; /* ¼Ð¿ù¦a¤è */
+ }
+ }
+ }
+ return 1;
+}
+
+
+int
+main_mine()
+{
+ int level;
+ char ans[4];
+
+ level = vans("½Ð¿ï¾Ü [1-5] µ¥¯Å¡A[0] ¦Û©w¡A©Î«ö [Q] Â÷¶}¡G");
+ if (level == 'q')
+ {
+ return XEASY;
+ }
+ else if (level < '1' || level > '5') /* ¦Û©w´Ñ½L¤£±o¤j©ó 60 * 20 */
+ {
+ vget(b_lines, 0, "½Ð¿é¤J¦a¹Ïªºªø¡G", ans, 3, DOECHO);
+ MAP_Y = atoi(ans) > MAP_MAX_Y ? MAP_MAX_Y : atoi(ans);
+
+ vget(b_lines, 0, "½Ð¿é¤J¦a¹Ïªº¼e¡G", ans, 3, DOECHO);
+ MAP_X = atoi(ans) > MAP_MAX_X ? MAP_MAX_X : atoi(ans);
+
+ vget(b_lines, 0, "½Ð¿é¤J¦a¹p¼Æ¡G", ans, 3, DOECHO);
+ level = atoi(ans);
+ TotalMines = MAP_Y * MAP_X / 3;
+ if (TotalMines > level)
+ TotalMines = level;
+ /* ­­¨î¦a¹p¼Æ¤£±o¶W¹L MAP_Y * MAP_X / 3¡A¥H§K init_map() ¶Ã¼Æ¨ú¤Ó¤[ */
+
+ if (MAP_Y < 1 || MAP_X < 1 || TotalMines < 1)
+ return 0;
+ level = 0;
+ }
+ else
+ {
+ level -= '0';
+ MAP_Y = 5 * level; /* ¤£±o¶W¹L MAP_MAX_Y */
+ MAP_X = (level < 4) ? 5 * level : MAP_MAX_X; /* ¤£±o¶W¹L MAP_MAX_X */
+ TotalMines = MAP_Y * MAP_X / 10;
+ }
+
+ TaggedMines = 0;
+ cx = MAP_X / 2 + 1;
+ cy = MAP_Y / 2 + 1;
+
+ init_map();
+ out_map();
+ play_mine();
+
+ if (!EndGame)
+ {
+ if (LoseGame)
+ {
+ draw_map();
+ vmsg("¸I¡I½ò¨ì¦a¹p¤F¡I");
+ }
+ else /* ¼Ð°O¼Æ == ¦a¹p¼Æ */
+ {
+ if (win()) /* itoc.010711: ­nÀˬd¬O§_¯}Ãö¡A¥H§KÀH«K¶Ã¼Ð°O¡A·í¼Ð°O¼Æ=¦a¹p¼Æ´N»¡¹LÃö¤F */
+ {
+ char buf[STRLEN];
+ sprintf(buf, "±zªá¤F %.0lf ¬í ¯}²Ä %d Ãö ¦n±R«ô ^O^", difftime(time(0), InitTime), level);
+ vmsg(buf);
+ addmoney(level * 75);
+ }
+ else
+ {
+ draw_map();
+ vmsg("±z¼Ð¿ù¦a¹p¤F³á =.=");
+ }
+ }
+ }
+ else
+ {
+ vmsg(MSG_QUITGAME);
+ }
+
+ return 0;
+}
+#endif /* HAVE_GAME */
diff --git a/game/nine.c b/game/nine.c
new file mode 100644
index 0000000..2eb584a
--- /dev/null
+++ b/game/nine.c
@@ -0,0 +1,601 @@
+/*-------------------------------------------------------*/
+/* nine.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : ¤Ñ¦a¤E¤E¹CÀ¸ */
+/* create : 98/11/26 */
+/* update : 01/04/24 */
+/* author : dsyan.bbs@Forever.twbbs.org */
+/* recast : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+
+#ifdef HAVE_GAME
+
+
+#undef NINE_DEBUG
+
+
+static char AI_score[13] = {7, 6, 5, 4,10, 9, 3, 2, 1, 0,11, 8,12};
+/* ¹q¸£ AI ©Ò¦b : K A 2 3 4 5 6 7 8 9 T J Q ¨CÂIµP©Ò¹ïÀ³ªº¤À¼Æ */
+/* AI_score 6 ¥H¤Uªº¬O¼Æ¦rµP¡A7 ¥H¤Wªº¬O¯S®íµP */
+/* ¹q¸£¶É¦V§â AI_score ¤pªºµP¥á¥X¥h */
+
+static char str_dir[4][3] = {"¡õ", "¡÷", "¡ô", "¡ö"};
+
+/* hand[] ¸Ì­±ªº­È©Ò¥NªíªºµP±i 0~12:CK~CQ 13~25:DK~DQ 26~38:HK~KQ 39~51:SK~SQ */
+static char str_suit[4][3] = {"¢Ñ", "¢Ò", "¢Ö", "¢á"};
+static char str_num[13][3] = {"¢Ù", "¢Ï", "¢±", "¢²", "¢³", "¢´", "¢µ", "¢¶", "¢·", "¢¸", "¢â", "¢Ø", "¢ß"};
+
+static char hand[4][5]; /* ªF¦è«n¥_¥|®aªºµP±i */
+static char now; /* ²{¦b®à¤WÂI¼Æ */
+static char dir; /* ¥Ø«e±ÛÂ઺¤è¦V 1:°f®É°w -1:¶¶®É°w */
+static char turn; /* ¥Ø«e½ü¨ì­þ¤@®a 0:¦Û¤v 1-3:¹q¸£ */
+static char live; /* ¹q¸£ÁÙ¦³´X®a¬¡µÛ 0~3 0:ª±®a³Ó§Q */
+static int sum; /* ®à¤W¤w¸g¦³´X±iµP¤F */
+
+
+static void
+out_song()
+{
+ static int count = 0;
+
+ /* ¼B­Y­^£»«Ü·R«Ü·R§A */
+ uschar *msg[12] =
+ {
+ "·Q¬°§A°µ¥ó¨Æ Åý§A§ó§Ö¼Öªº¨Æ",
+ "¦n¦b§Aªº¤ß¤¤ ®I¤U§Úªº¦W¦r",
+ "¨D®É¶¡ ¶XµÛ§A ¤£ª`·Nªº®É­Ô",
+ "®¨®¨¦a §â³oºØ¤l ÆC¦¨ªG¹ê",
+ "§Ú·Q¦oªº½T¬O §ó¾A¦X§Aªº¤k¤l",
+ "§Ú¤Ó¤£°÷·Å¬XÀu¶®¦¨¼ôÀ´¨Æ",
+ "¦pªG§Ú °h¦^¨ì ¦nªB¤Íªº¦ì¸m",
+ "§A¤]´N ¤£¦A»Ý­n ¬°Ãø¦¨³o¼Ë¤l",
+ "«Ü·R«Ü·R§A ©Ò¥HÄ@·N ±Ë±oÅý§A",
+ "©¹§ó¦h©¯ºÖªº¦a¤è­¸¥h",
+ "«Ü·R«Ü·R§A ¥u¦³Åý§A ¾Ö¦³·R±¡",
+ "§Ú¤~¦w¤ß"
+ };
+ move(b_lines - 2, 0);
+ prints("\033[1;3%dm%s\033[m Äw½XÁÙ¦³ %d ¤¸", time(0) % 7, msg[count], cuser.money);
+ clrtoeol();
+ if (++count == 12)
+ count = 0;
+}
+
+
+static int
+score_cmp(a, b)
+ char *a, *b;
+{
+ return AI_score[(*a) % 13] - AI_score[(*b) % 13];
+}
+
+
+static void
+show_mycard() /* ¨q¥X§Ú¤â¤Wªº¤­±iµP */
+{
+ int i;
+ char t;
+
+ for (i = 0; i < 5; i++)
+ {
+ t = hand[0][i];
+ move(16, 30 + i * 4);
+ outs(str_num[t % 13]);
+ move(17, 30 + i * 4);
+ outs(str_suit[t / 13]);
+ }
+}
+
+
+static void
+show_seacard(t) /* ¨q¥X®ü©³ªºµP */
+ char t; /* ¥X¤F­þ±iµP */
+{
+ char x, y;
+
+ /* ªF«n¦è¥_¥|®aªº¥XµP¸m©ñ (x, y) §¤¼Ð */
+ /* «n ªF ¥_ ¦è */
+ char coorx[4] = { 8, 6, 4, 6};
+ char coory[4] = {30, 38, 30, 22};
+
+#ifdef NINE_DEBUG
+ /* ¨q¥X¹q¸£ªºµP */
+ move(b_lines - 1, 5);
+ for (x = 3; x > 0; x--)
+ {
+ if (hand[x][0] == -1) /* ³o®a¤w¸gºG¾D²^¨O¡A¤£¥²¦L¥X */
+ continue;
+ qsort(hand[x], 5, sizeof(char), score_cmp);
+ for (y = 0; y < 5; y++)
+ outs(str_num[hand[x][y] % 13]);
+ outs(" ");
+ }
+#endif
+
+ x = coorx[turn];
+ y = coory[turn];
+ move(x, y);
+ outs("¢~¢w¢w¢w¢¡");
+ move(x + 1, y);
+ prints("¢x%s ¢x", str_num[t % 13]);
+ move(x + 2, y);
+ prints("¢x%s ¢x", str_suit[t / 13]);
+ move(x + 3, y);
+ outs("¢x ¢x");
+ move(x + 4, y);
+ outs("¢x ¢x");
+ move(x + 5, y);
+ outs("¢x ¢x");
+ move(x + 6, y);
+ outs("¢¢¢w¢w¢w¢£");
+
+ move(8, 50);
+ prints("%s %s", dir == 1 ? "¡ú" : "¡ù", dir == 1 ? "¡ø" : "¡û");
+ move(10, 50);
+ prints("%s %s", dir == 1 ? "¡û" : "¡ø", dir == 1 ? "¡ù" : "¡ú");
+
+ move(13, 46);
+ prints("ÂI¼Æ¡G%-2d", now);
+ /* prints("ÂI¼Æ¡G%c%c%c%c", (now / 10) ? 162 : 32, (now / 10) ? (now / 10 + 175) : 32, 162, now % 10 + 175); */ /* µL»Ý´«¦¨¥þ§Î */
+
+ move(14, 46);
+ prints("±i¼Æ¡G%d", sum);
+
+ refresh();
+
+ sleep(1); /* Åýª±®a¬Ý²M·¡¥XµPª¬ªp */
+}
+
+
+static void
+ten_or_twenty(t) /* ¥[©Î´î 10/20 */
+ char t;
+{
+ if (now < t) /* ª½±µ¥[ */
+ {
+ now += t;
+ }
+ else if (now > 99 - t) /* ª½±µ´î */
+ {
+ now -= t;
+ }
+ else /* ¸ß°Ý­n¥[ÁÙ¬O­n´î */
+ {
+ int ch;
+
+ move(b_lines - 4, 0);
+ clrtoeol();
+ prints(" (¡ö)(+)¥[%d (¡÷)(-)´î%d ", t, t);
+
+ while (1)
+ {
+ if (turn) /* ¹q¸£ */
+ ch = rnd(2) + KEY_LEFT; /* KEY_RIGHT == KEY_LEFT + 1 */
+ else /* ª±®a */
+ ch = vkey();
+
+ switch (ch)
+ {
+ case KEY_LEFT:
+ case '+':
+ now += t;
+ prints("\033[32;1m¥[ %d\033[m", t);
+ return;
+
+ case KEY_RIGHT:
+ case '-':
+ now -= t;
+ prints("\033[32;1m´î %d\033[m", t);
+ return;
+ }
+ }
+ }
+}
+
+
+static void
+next_turn()
+{
+ while (1)
+ {
+ turn = (turn + 4 + dir) % 4;
+ if (hand[turn][0] >= 0) /* ¦pªG¤U¤@®a¤w¾D²^¨O¡A¸õ¦A¤U¤@®a */
+ break;
+ }
+}
+
+
+static char
+get_newcard()
+{
+ /* 0~12:C)KA23456789TJQ 13~25:D))KA23456789TJQ 26~38:H))KA23456789TJQ 39~51:S)KA23456789TJQ */
+
+ /* itoc.020929: ¬°¨Dµ{¦¡Â²³æ¡AµP§Y¨Ï­«ÂÐ¥X²{¤]µL§«¡A¤Ï¥¿¤Ñ¦a¤E¤E³o¹CÀ¸
+ À³¸Ó¬O®³«Ü¦h°Æ¾ë§JµP²V°_¨Óª± */
+
+ return rnd(52);
+
+ /* itoc.µù¸Ñ: ¦pªGı±o¤Ñ¦a¤[¤[Ãø«×¤Ó°ª¡A¨º»ò¥i¥H¦b¦¹¶Ã¼Æ°µ½Õ¾ã¡A
+ ¹³¬O now ±µªñ 99 ®É¡Aª±®aªºµP·|Åܦn¡F©Î¬O¹q¸£¨ú¨ì¯S®íµPªº¾÷²vÅܤp */
+}
+
+
+static int /* -1:Ãz±¼ */
+lead_card(t) /* ¥XµP */
+ char *t; /* ¶Ç¤J¥X¤F­þ±iµP¡A¤]­n¶Ç¥X¤@±i·sªºµP´À¥N³o±iµP */
+{
+ int ch;
+ char m;
+
+ m = *t % 13;
+ switch (m)
+ {
+ case 4: /* °jÂà */
+ dir = -dir;
+ break;
+
+ case 5: /* «ü©w */
+ move(b_lines - 4, 0);
+ clrtoeol();
+ outs(" «ü©w¨º¤@®a¡H");
+ for (ch = 3; ch >= 0; ch--)
+ {
+ if (turn != ch && hand[ch][0] >= 0)
+ prints("(%s) ", str_dir[ch]);
+ }
+
+ /* «ü©w¤@®a©|¥¼²^¨Oªº */
+ while (1)
+ {
+
+#if 0 /* from global.h */
+#define KEY_UP -1
+#define KEY_DOWN -2
+#define KEY_RIGHT -3
+#define KEY_LEFT -4
+#endif
+
+ if (turn || live == 1) /* ¦pªG¬O¹q¸£¥XªºµP©Î¬O¥u³Ñ¤U¤@­Ó¹q¸£¹ï¤â¡A´N¦Û°Ê«ü©w */
+ ch = rnd(4) + KEY_LEFT;
+ else
+ ch = vkey();
+
+ /* ³Q«ü©wªº¨º®a¤£¯à¤w¸g¾D²^¨O */
+ if (turn != 3 && hand[3][0] >= 0 && ch == KEY_LEFT)
+ ch = 3;
+ else if (turn != 2 && hand[2][0] >= 0 && (ch == KEY_UP || ch == KEY_DOWN))
+ ch = 2;
+ else if (turn != 1 && hand[1][0] >= 0 && ch == KEY_RIGHT)
+ ch = 1;
+ else if (turn != 0 && hand[0][0] >= 0)
+ ch = 0;
+ else
+ continue;
+
+ break;
+ }
+
+ prints("\033[32;1m(%s)\033[m", str_dir[ch]);
+ break;
+
+ case 10: /* ¥[©Î´î10 */
+ ten_or_twenty(10);
+ break;
+
+ case 11: /* Pass */
+ break;
+
+ case 12: /* ¥[©Î´î20 */
+ ten_or_twenty(20);
+ break;
+
+ case 0: /* °¨¤WÅÜ99 */
+ now = 99;
+ break;
+
+ default: /* ¤@¯ë¼Æ¦rµP */
+ if (now + m > 99)
+ return -1;
+ else
+ now += m;
+ break;
+ }
+
+ show_seacard(*t);
+
+ /* ®³¤@±i·sªºµP */
+ *t = get_newcard();
+
+ /* ½ü¨ì¤U¤@®a */
+ if (m == 5)
+ turn = ch;
+ else
+ next_turn();
+
+ return 0;
+}
+
+
+static void
+cpu_die()
+{
+ char buf[20];
+
+ switch (turn)
+ {
+ case 1:
+ move(9, 55);
+ break;
+ case 2:
+ move(7, 52);
+ break;
+ case 3:
+ move(9, 49);
+ break;
+ }
+ outs(" ");
+ live--;
+
+ sprintf(buf, "¹q¸£ %d Ãz¤F", turn);
+ vmsg(buf);
+ sleep(1); /* Åýª±®a¬Ý²M·¡¥XµPª¬ªp */
+
+ hand[turn][0] = -1; /* Ãz±¼ªº¨º®aªº hand[turn][0] ³]¬° -1 */
+ next_turn();
+}
+
+
+static void
+online_help(t)
+ char t;
+{
+ char m;
+
+ move(b_lines - 4, 0);
+ clrtoeol();
+
+ m = t % 13;
+ switch (m)
+ {
+ case 0:
+ outs(" ¤E¤E¡GÂI¼Æ°¨¤WÅܦ¨¢¸¢¸");
+ break;
+
+ case 4:
+ outs(" °jÂà¡G¹CÀ¸¶i¦æ¤è¦V¬Û¤Ï");
+ break;
+
+ case 5:
+ outs(" «ü©w¡G¦Û¥Ñ«ü©w¤U¤@­Óª±®a");
+ break;
+
+ case 11:
+ outs(" PASS¡G¥i pass ¤@¦¸");
+ break;
+
+ case 10:
+ outs(" ÂI¼Æ¥[©Î´î 10");
+ break;
+
+ case 12:
+ outs(" ÂI¼Æ¥[©Î´î 20");
+ break;
+
+ default:
+ prints(" ÂI¼Æ¥[ %d", m);
+ break;
+ }
+}
+
+
+int
+main_nine()
+{
+ int money; /* ©ãª÷ */
+
+ int i, j;
+ char m;
+ char buf[STRLEN];
+ FILE *fp;
+
+ if (HAS_STATUS(STATUS_COINLOCK))
+ {
+ vmsg(msg_coinlock);
+ return XEASY;
+ }
+
+ while (1)
+ {
+ vs_bar("¤Ñ¦a¤E¤E");
+ out_song();
+
+ vget(2, 0, "½Ð°Ý­n¤Uª`¦h¤Ö©O¡H(1 ~ 50000) ", buf, 6, DOECHO);
+ money = atoi(buf);
+ if (money < 1 || money > 50000 || money > cuser.money)
+ break; /* Â÷¶}½ä³õ */
+
+ /* ¦L¥X½ä³õ¤º³¡Â\³] */
+
+ if (!(fp = fopen("etc/game/99", "r")))
+ break; /* Â÷¶}½ä³õ */
+
+ move(2, 0);
+ clrtoeol(); /* ²M±¼¡u½Ð°Ý­n¤Uª`¦h¤Ö©O¡H¡vªº´Ý¾l */
+
+ move(1, 0);
+ while (fgets(buf, STRLEN, fp))
+ outs(buf);
+
+ fclose(fp);
+
+ cuser.money -= money; /* ¥ý½T©w¯à¶}ÀɦA¦©¿ú */
+
+ for (i = 0; i < 4; i++)
+ {
+ for (j = 0; j < 5; j++)
+ hand[i][j] = get_newcard();
+ }
+
+ sum = 0; /* ¥Ø«eµP®à¤¤¶¡¨S¦³µP */
+ now = 0; /* ¥Ø«eµP®à¤¤¶¡ªºÂI¼Æ¬O 0 */
+ turn = 0; /* ª±®a¶}©l²Ä¤@¨B */
+ dir = 1; /* ¤@¶}©l¹w³] °f®É°w */
+ live = 3; /* ©|¦³¤T®a¹q¸£¬¡µÛ */
+
+ /* ª±®aªºµP­n±Æ§Ç¡A¹q¸£ªºµPµ¥½ü¨ì¹q¸£®É¦A±Æ§Ç */
+ qsort(hand[0], 5, sizeof(char), score_cmp);
+ show_mycard();
+
+ /* ¹CÀ¸¶}©l */
+ for (;;)
+ {
+ move(9, 52);
+ outs(str_dir[turn]);
+
+ /* ½ü¨ì¹q¸£¥XµP */
+
+ if (turn)
+ {
+ qsort(hand[turn], 5, sizeof(char), score_cmp);
+
+ for (i = 0; i < 5; i++)
+ {
+ m = hand[turn][i] % 13;
+ if (AI_score[m] >= 7) /* ¥Î AI_score[] ¨Ó§PÂ_¬O§_¬°¯S®íµP */
+ break;
+ if (now + m <= 99)
+ break;
+ }
+
+ if (i == 5) /* ¨S¦³¥ô¦ó¤@±iµP¯à¥X */
+ {
+ cpu_die();
+ }
+ else
+ {
+ /* ¥Ñ©ó qsort ·|§â AI_score ªºµP©ñ¦b³Ì¥ªÃä¡A´Nª½±µ©ß¥X³Ì¥ªÃä³o±iµP§Y¥i */
+ sum++;
+ lead_card(&(hand[turn][i]));
+ }
+
+ if (rnd(5) == 0)
+ out_song();
+
+ continue;
+ }
+
+ /* ½ü¨ìª±®a¥XµP */
+
+ if (!live) /* ¤T®a¹q¸£³£¦º¥ú¤F */
+ {
+ if (sum < 25)
+ money *= 15;
+ else if (sum < 50)
+ money *= 10;
+ else if (sum < 100)
+ money *= 5;
+ else if (sum < 150)
+ money *= 3;
+ else if (sum < 200)
+ money *= 2;
+ /* ¶W¹L 200 ¦¸¤~Ĺ¡A¥uÁÙ­ì©ãª÷ */
+
+ addmoney(money);
+ sprintf(buf, "¦b¸g¹L %d ¦^¦Xªº¼r±þ¡A±z²æ¿o¦Ó¥X¡AÀò±o¼úª÷ %d", sum, money);
+ vmsg(buf);
+ break;
+ }
+
+ /* ¸û¤pªºµP/¯S®íµP©ñ¦b¥kÃä¡A­Y³Ì«á¤@±i¤£¬O¯S®íµP¥B¥[¤W¥h·|¶W¹L 99¡A´N¬OÃz¤F */
+ m = hand[0][4] % 13;
+ if (AI_score[m] < 7 && now + m > 99)
+ {
+ sprintf(buf, "¶ã¶ã¶ã..¦b %d ±iµP³Q¹q¸£¹qÃz±¼¤F.. :~", sum);
+ vmsg(buf);
+ break;
+ }
+
+ i = 0; /* ¦b¥H¤U while(j) °j°é¤¤¡A®³ i ¨Ó·í²Ä´X±iµP */
+ while (1) /* ª½¨ì vkey() == '\n' ©Î ' ' ®É¤~Â÷¶}°j°é */
+ {
+ m = hand[0][i] % 13;
+ move(18, i * 4 + 30);
+
+ if (AI_score[m] < 7) /* ¥Î AI_score[] ¨Ó§PÂ_¬O§_¬°¯S®íµP */
+ {
+ if (now + m > 99)
+ outs("¡I"); /* ĵ§i·|Ãz±¼ */
+ else
+ outs("¡³"); /* ¤@¯ëµP */
+ }
+ else
+ {
+ outs("¡¹"); /* ¯S®íµP */
+ }
+
+ move(18, i * 4 + 31); /* Á×§K°»´ú¥ª¥kÁä¥þ§Î */
+
+ j = vkey();
+
+ if (j == KEY_LEFT)
+ {
+ move(18, i * 4 + 30);
+ outs(" ");
+ i = i ? i - 1 : 4;
+ }
+ else if (j == KEY_RIGHT)
+ {
+ move(18, i * 4 + 30);
+ outs(" ");
+ i = (i == 4) ? 0 : i + 1;
+ }
+#ifdef NINE_DEBUG
+ else if (j == KEY_UP)
+ {
+ if (vget(b_lines - 4, 5, "§âµP´«¦¨¡G", buf, 3, DOECHO))
+ {
+ m = atoi(buf);
+ if (m >= 0 && m < 52)
+ {
+ hand[0][i] = m;
+ qsort(hand[0], 5, sizeof(char), score_cmp);
+ show_mycard();
+ }
+ }
+ }
+#endif
+ else if (j == KEY_DOWN)
+ {
+ online_help(hand[0][i]);
+ }
+ else if (j == '\n' || j == ' ')
+ {
+ move(18, i * 4 + 30);
+ outs(" ");
+ sum++;
+ break;
+ }
+ else if (j == 'q')
+ {
+ return 0;
+ }
+ }
+
+ if (lead_card(&(hand[0][i])) < 0)
+ {
+ vmsg("¶ã¶ã¶ã..¥ÕÄêÃz¤F!!.. :~");
+ break;
+ }
+
+ qsort(hand[0], 5, sizeof(char), score_cmp);
+ show_mycard();
+ }
+ }
+ return 0;
+}
+
+#endif /* HAVE_GAME */
diff --git a/game/pushbox.c b/game/pushbox.c
new file mode 100644
index 0000000..10414db
--- /dev/null
+++ b/game/pushbox.c
@@ -0,0 +1,321 @@
+/*-------------------------------------------------------*/
+/* pushbox.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : ­Ü®wµf */
+/* create : 98/11/11 */
+/* update : 02/09/05 */
+/* author : period.bbs@smth.org */
+/* cityhunter.bbs@smth.org */
+/* modify : zhch.bbs@bbs.nju.edu.cn */
+/* hightman.bbs@bbs.hightman.net */
+/* recast : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+#ifdef HAVE_GAME
+
+#define MAX_MAP_WIDTH 20
+#define MAX_MAP_HEIGHT 16
+#define MAX_MOVE_TIMES 1000 /* ³Ì¦h²¾°Ê´X¨B */
+
+#define move2(x, y) move(x+6, y*2)
+#define move3(x, y) move(x+6, y*2+1) /* Á×§K¥þ§Î°»´ú */
+
+static int cx, cy; /* ¥Ø«e©Ò¦b¦ì¸m */
+static int stage; /* ²Ä´XÃö */
+static int NUM_TABLE; /* Á`¦@¦³´XÃö */
+static int total_line; /* ¥»Ãö¦³´X¦C */
+static usint map[MAX_MAP_HEIGHT][MAX_MAP_WIDTH];
+
+
+static char *
+map_char(n)
+ usint n;
+{
+ if (n & 8)
+ return "¡½";
+ if (n & 4)
+ return "¡¼";
+ if (n & 2)
+ return "¡ó";
+ if (n & 1)
+ return "¡O";
+ return " ";
+}
+
+
+static usint
+map_item(item)
+ char *item;
+{
+ if (!str_ncmp(item, map_char(1), 2))
+ return 1;
+ if (!str_ncmp(item, map_char(2), 2))
+ return 2;
+ if (!str_ncmp(item, map_char(4), 2))
+ return 4;
+ if (!str_ncmp(item, map_char(8), 2))
+ return 8;
+ return 0;
+}
+
+
+static int /* 0:¥¢±Ñ !=0:¦@¦³´X¦C */
+map_init(fp)
+ FILE *fp;
+{
+ int i, j;
+ char buf[80], level[8];
+
+ sprintf(level, "--%03d", stage);
+ while (fgets(buf, 10, fp))
+ {
+ if (!str_ncmp(buf, level, 5)) /* §ä¨ì¥¿½TªºÃö¥d */
+ break;
+ }
+
+ memset(map, 0, sizeof(map));
+
+ i = 0;
+ while (fgets(buf, 80, fp))
+ {
+ /* ¸ü¤J¦a¹Ï */
+ if (buf[0] == '-')
+ break;
+
+ for (j = 0; j < MAX_MAP_WIDTH; j++)
+ map[i][j] = map_item(buf + j * 2);
+
+ if (++i >= MAX_MAP_HEIGHT)
+ break;
+
+ memset(buf, 0, sizeof(buf));
+ }
+
+ return i;
+}
+
+
+static int
+map_line(x) /* ¦L¥X²Ä x ¦C */
+ int x;
+{
+ int j;
+ usint n;
+
+ move2(x, 0);
+ clrtoeol();
+ for (j = 0; j < MAX_MAP_WIDTH; j++)
+ {
+ n = map[x][j];
+ if (n & 5) /* ¥[±jÃC¦â¨Ï²M·¡ */
+ prints("\033[1;32m%s\033[m", map_char(n));
+ else
+ outs(map_char(n));
+ }
+}
+
+
+static void
+map_show()
+{
+ int i;
+
+ for (i = 0; i < total_line; i++)
+ map_line(i);
+
+ move3(cx, cy);
+}
+
+
+static void
+map_move(x0, y0, x1, y1) /* (x0, y0) -> (x1, y1) */
+ int x0, y0, x1, y1;
+{
+ usint m;
+
+ m = map[x0][y0];
+
+ map[x1][y1] = (m & 6) | (map[x1][y1] & 1); /* ¥Øªº®æ¥[¤J '¡ó' ¤Î '¡¼' */
+ map[x0][y0] = m & 1; /* ©Ò¦b®æ²MªÅ '¡ó' ¤Î '¡¼' */
+
+ map_line(x0);
+ if (x1 != x0)
+ map_line(x1);
+}
+
+
+static int /* 1:¦¨¥\ */
+check_win()
+{
+ int i, j;
+ for (i = 0; i < total_line; i++)
+ {
+ for (j = 0; j < MAX_MAP_WIDTH; j++)
+ {
+ if ((map[i][j] & 1) && !(map[i][j] & 4)) /* ÁÙ¦³ '¡O' ¤W¨S¦³ '¡¼' */
+ return 0;
+ }
+ }
+ return 1;
+}
+
+
+static int
+find_cxy() /* §äªì©l¦ì¸m */
+{
+ int i, j;
+ for (i = 0; i < total_line; i++)
+ {
+ for (j = 0; j < MAX_MAP_WIDTH; j++)
+ {
+ if (map[i][j] & 2)
+ {
+ cx = i;
+ cy = j;
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+
+static int
+select_stage()
+{
+ int count;
+ char buf[80], ans[4];
+ FILE *fp;
+
+ if (!(fp = fopen("etc/game/pushbox.map", "r")))
+ return 0;
+
+ if (stage < 0) /* ²Ä¤@¦¸¶i¤J¹CÀ¸ */
+ {
+ fgets(buf, 4, fp);
+ NUM_TABLE = atoi(buf); /* etc/game/pushbox.map ²Ä¤@¦æ°O¿ýÃö¥d¼Æ */
+ sprintf(buf, "½Ð¿ï¾Ü½s¸¹ [1-%d]¡A[0] ÀH¾÷¥XÃD¡A©Î«ö [Q] Â÷¶}¡G", NUM_TABLE);
+ if (vget(b_lines, 0, buf, ans, 4, DOECHO) == 'q')
+ {
+ fclose(fp);
+ return 0;
+ }
+ stage = atoi(ans);
+ if (stage <= 0 || stage > NUM_TABLE) /* ÀH¾÷¥XÃD */
+ stage = 1 + time(0) % NUM_TABLE;
+ }
+
+ count = map_init(fp);
+ fclose(fp);
+
+ return count;
+}
+
+
+int
+main_pushbox()
+{
+ int dx, dy; /* ¤U¤@¨B©Ò¦æ¦ì¬í */
+ int valid;
+ usint n;
+
+ stage = -1;
+
+start_game:
+
+ if (!(total_line = select_stage()))
+ return XEASY;
+
+ vs_bar("­Ü®wµf");
+ move(2, 0);
+ prints("²Ä \033[1;32m%03d\033[m Ãö¡G§â©Ò¦³ªº '¡¼' ³£±À¨ì '¡O' ¤W­±¥h(·|Åܦ¨\033[1;32mºñ¦â\033[m)´N¹LÃö¤F\n", stage);
+ outs("«öÁ仡©ú¡G(¡ô¡õ¡ö¡÷)²¾°Ê (s)­«ª± (q)Â÷¶} (^L)¿Ã¹õ­«Ã¸");
+
+ if (!find_cxy())
+ {
+ vmsg("³o±i¦a¹Ï¦ü¥G¤£¹ï«l¡I");
+ return 0;
+ }
+
+ map_show();
+
+ while (1)
+ {
+ dx = dy = 0;
+
+ switch (vkey())
+ {
+ case KEY_UP:
+ dx = -1;
+ break;
+
+ case KEY_DOWN:
+ dx = 1;
+ break;
+
+ case KEY_LEFT:
+ dy = -1;
+ break;
+
+ case KEY_RIGHT:
+ dy = 1;
+ break;
+
+ case Ctrl('L'):
+ map_show();
+ break;
+
+ case 'q':
+ goto game_over;
+
+ case 's':
+ goto start_game;
+ }
+
+ if (!dx && !dy)
+ continue;
+
+ /* ¶}©l²¾°Ê */
+ valid = 0;
+ n = map[cx + dx][cy + dy]; /* ¥Øªº®æ */
+
+ if (n <= 1) /* ¥Øªº®æ¬OªÅªº¡Aª½±µ²¾¤J§Y¥i */
+ {
+ map_move(cx, cy, cx + dx, cy + dy);
+ valid = 1;
+ }
+ else if (n & 4) /* ¥Øªº®æ¦³ '¡¼'¡A±À¥h¤U¤U®æ */
+ {
+ if (map[cx + dx * 2][cy + dy * 2] <= 1) /* ¤U¤U®æ¬OªÅªº */
+ {
+ map_move(cx + dx, cy + dy, cx + dx * 2, cy + dy * 2);
+ map_move(cx, cy, cx + dx, cy + dy);
+ valid = 1;
+ }
+ }
+
+ if (valid) /* ¦³®Ä²¾°Ê */
+ {
+ if (check_win())
+ break;
+
+ cx += dx;
+ cy += dy;
+ move3(cx, cy);
+ }
+ }
+
+ vmsg("¯¬¶P±z¡I¦¨¥\\¹LÃö");
+
+ if (++stage > NUM_TABLE)
+ stage = 1;
+
+ goto start_game;
+
+game_over:
+ return 0;
+}
+#endif /* HAVE_GAME */
diff --git a/game/race.c b/game/race.c
new file mode 100644
index 0000000..b4d18d4
--- /dev/null
+++ b/game/race.c
@@ -0,0 +1,357 @@
+/*-------------------------------------------------------*/
+/* race.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : Áɰ¨³õ¹CÀ¸ */
+/* create : 98/12/17 */
+/* update : 01/04/21 */
+/* author : SugarII (u861838@Oz.nthu.edu.tw) */
+/* recast : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+
+#ifdef HAVE_GAME
+
+
+static int pace[5]; /* ¤­¤Ç°¨¶]¤F¦h»· */
+
+
+static void
+out_song()
+{
+ static int count = 0;
+
+ /* ±i«B£»¤@¨¥ÃøºÉ */
+ uschar *msg[13] =
+ {
+ "©pµ¹§Ú¤@³õÀ¸ ©p¬ÝµÛ§Ú¤J°g",
+ "³Q©p±q¤ß¸Ì­é¸¨ªº·P±¡ µhªº¤£ª¾«ç»ò±Ë¥h",
+ "¿ð¿ð¤£¯à¬Û«H³o·Pı ¹³¦Û¤v©M¦Û¤v¤ÀÂ÷",
+ "¦Ó«H»}¥¹¥¹ªº·R±¡ ¦b¨º¸Ì",
+ "§Ú¤@¨¥ÃøºÉ §Ô¤£¦í¶Ë¤ß",
+ "¿Å¶q¤£¥X·R©Î¤£·R¤§¶¡ªº¶ZÂ÷",
+ "©p»¡©pªº¤ß ¤£¦A·Å¼ö¦p©õ",
+ "±q¨º¸Ì¶}©l ±q¨º¸Ì¥¢¥h",
+ "§Ú¤@¨¥ÃøºÉ §Ô¤£¦í¶Ë¤ß",
+ "¿Å¶q¤£¥X·R©Î¤£·R¤§¶¡ªº¶ZÂ÷",
+ "ÁôÁô¬ù¬ù¤¤ ©ú¥Õ©pªº¨M©w",
+ "¤£´±«j±j©p ¥u¦n¬°Ãø¦Û¤v",
+ "§Ú¬°Ãø§Ú¦Û¤v §Ú¬°Ãø§Ú¦Û¤v"
+ };
+ move(b_lines - 2, 0);
+ prints("\033[1;3%dm%s\033[m Äw½XÁÙ¦³ %d ¤¸", time(0) % 7, msg[count], cuser.money);
+ clrtoeol();
+ if (++count == 13)
+ count = 0;
+}
+
+
+static int /* -1: ÁÙ¨S¤À¥X³Ó­t 0~4:Ĺªº¨º¤Ç°¨ */
+race_path(run, j, step)
+ int run, j, step;
+{
+ int i;
+
+ if (!step)
+ {
+ return -1;
+ }
+ else if (step < 0)
+ {
+ if (j + step < 0)
+ j = -step;
+ move(run + 9, (j + step) * 2 + 8);
+ clrtoeol();
+ pace[run] += step * 100;
+ if (pace[run] < 1)
+ pace[run] = 1;
+ return -1;
+ }
+
+ /* step > 0 */
+ move(run + 9, j * 2 + 8);
+ for (i = 0; i < step; i++)
+ outs("¡½");
+
+ if (pace[run] + step * 100 > 3000)
+ return run;
+ return -1;
+}
+
+
+int
+main_race()
+{
+ int money[5]; /* ¤­¤Ç°¨ªº©ãª÷ */
+ int speed[5]; /* ¤­¤Ç°¨ªº³t«× */
+ int stop[5]; /* ¤­¤Ç°¨ªº¼È°± */
+ int bomb; /* ¬O§_¨Ï¥Î¬µ¼u */
+ int run; /* ¥Ø«e¦b­pºâªº¨º¤Ç */
+ int win; /* ­þ¤Ç°¨Ä¹¤F */
+ int flag; /* ¨Æ¥óµo¥Í¦¸¼Æ */
+ int i, j, ch;
+ char buf[60];
+ char *racename[5] = {"¨ª¨ß", "ªº¿c", "¤ö¶À", "­¸¹q", "¦½¦å"};
+
+ if (HAS_STATUS(STATUS_COINLOCK))
+ {
+ vmsg(msg_coinlock);
+ return XEASY;
+ }
+
+ while (1)
+ {
+ vs_bar("Áɰ¨³õ");
+ out_song();
+ bomb = 0;
+ win = -1;
+ flag = 0;
+ for (i = 0; i < 5; i++)
+ {
+ pace[i] = 1;
+ stop[i] = money[i] = 0;
+ speed[i] = 100;
+ }
+ move(5, 0);
+ outs(" \033[1m°¨¦W¡G\033[m");
+ for (i = 0; i < 5; i++)
+ prints(" %d. \033[1;3%dm%s\033[m", i + 1, i + 1, racename[i]);
+ outs("\n \033[1m³t«×¡G\033[m\n \033[1m½äª÷¡G\033[m\n\n");
+
+ for (i = 0; i < 5; i++)
+ prints("%d.\033[1;3%dm%s\033[mùø\n", i + 1, i + 1, racename[i]);
+ outs("¢w¢w¢wùö¢w¢w¢r¢w¢w¢r¢w¢w¢r¢w¢w¢r¢w¢w¢r¢w¢w¢r¢w¢w¢r¢w¢w¢r¢w¢w¢r¢w¢wù÷");
+
+ while (1)
+ {
+ /* ¨M©w¦U¤Ç°¨½äª` */
+ ch = vget(2, 0, "±z­n©ã­þ¤Ç°¨(1-5)¡H[S]¶}©l [Q]Â÷¶}¡G", buf, 3, DOECHO);
+ i = buf[0];
+ if (!ch || i == 's')
+ {
+ if (money[0] || money[1] || money[2] || money[3] || money[4])
+ break;
+ addmoney(money[0] + money[1] + money[2] + money[3] + money[4]); /* ÁÙ¿ú */
+ goto abort_game;
+ }
+ else if (i < '1' || i > '5')
+ {
+ addmoney(money[0] + money[1] + money[2] + money[3] + money[4]); /* ÁÙ¿ú */
+ goto abort_game;
+ }
+
+ ch = vget(3, 0, "­n©ã¦h¤Ö½äª÷¡H", buf, 6, DOECHO);
+ j = atoi(buf);
+ if (!ch)
+ {
+ if (money[0] || money[1] || money[2] || money[3] || money[4])
+ break;
+ addmoney(money[0] + money[1] + money[2] + money[3] + money[4]); /* ÁÙ¿ú */
+ goto abort_game;
+ }
+ if (j < 1 || j > cuser.money)
+ {
+ addmoney(money[0] + money[1] + money[2] + money[3] + money[4]); /* ÁÙ¿ú */
+ goto abort_game;
+ }
+
+ money[i - '1'] += j;
+ cuser.money -= j;
+
+ move(7, 15);
+ clrtoeol();
+ outs("\033[1m");
+ for (i = 0; i < 5; i++)
+ prints(" \033[3%dm%7d", i + 1, money[i]);
+ outs("\033[m");
+ out_song();
+ }
+
+ /* ¶}©l¹CÀ¸ */
+ move(3, 0);
+ clrtoeol();
+ move(2, 0);
+ clrtoeol();
+ outs("-== ½Ð«ö \033[1;36mk\033[m ¬°±z¿ïªº«l¾s¥[ªo¡A«ö \033[1;36mz\033[m ¥i¥á¥X¬µ¼u(¥u¦³¤@¦¸¾÷·|) ==-");
+
+ while (win < 0)
+ {
+ move(6, 15);
+ clrtoeol();
+ outs("\033[1m");
+ for (i = 0; i < 5; i++)
+ {
+ if (stop[i] < 1)
+ speed[i] += rnd(20) - (speed[i] + 170) / 30;
+ if (speed[i] < 0)
+ speed[i] = 0;
+ prints(" \033[3%dm%7d", i + 1, speed[i]);
+ }
+ outs("\033[m");
+
+ do
+ {
+ ch = igetch();
+ } while (ch != 'k' && (ch != 'z' || bomb));
+
+ run = rnd(5); /* ¿ï¾Ü¨Æ¥óµo¥Í¹ï¶H */
+ flag %= 5; /* ¦C¦L¨Æ¥ó©ó¿Ã¹õ¤W */
+ move(15 + flag, 0);
+ clrtoeol();
+
+ if (ch == 'z') /* ¥á¬µ¼u */
+ {
+ stop[run] = 3;
+ prints("\033[1m«OÄÖ²y¯{¨ì\033[3%dm%s\033[37m°±¤î«e¶i¤T¦¸¡A³t«× = 0\033[m",
+ run + 1, racename[run]);
+ speed[run] = 0;
+ flag++;
+ bomb = 1;
+ }
+ else if (rnd(12) == 0) /* ¯S®í¨Æ¥ó */
+ {
+ prints("\033[1;3%dm%s\033[36m", run + 1, racename[run]);
+
+ switch (rnd(14))
+ {
+ case 0:
+ outs("ªA¤U«Â¦Ó­è¡A³t«× x1.5\033[m");
+ speed[run] *= 1.5;
+ break;
+ case 1:
+ outs("¨Ï¥XºµªºÃzµo¤O¡A«e¶i¤­®æ\033[m");
+ win = race_path(run, pace[run] / 100, 5);
+ pace[run] += 500;
+ break;
+ case 2:
+ outs("½ò¨ì¦a¹p¡A³t«×´î¥b\033[m");
+ speed[run] /= 2;
+ break;
+ case 3:
+ outs("½ò¨ì­»¿¼¥Ö·Æ­Ë¡A¼È°±¤G¦¸\033[m");
+ stop[run] += 2;
+ break;
+ case 4:
+ outs("½Ð¯«¤W¨­¡A¼È°±¥|¦¸¡A³t«×¥[­¿\033[m");
+ stop[run] += 4;
+ speed[run] *= 2;
+ break;
+ case 5:
+ outs("°Û¥X¤jÅ]ªk©G¡A¨Ï¨ä¥L¤H¼È°±¤T¦¸\033[m");
+ for (i = 0; i < 5 && i != run; i++)
+ stop[i] += 3;
+ break;
+ case 6:
+ outs("Å¥¨£ badboy ªº¥[ªoÁn¡A³t«× +100\033[m");
+ speed[run] += 100;
+ break;
+ case 7:
+ outs("¨Ï¥X¿ûÅKÅܨ­¡A«e¶i¤T®æ¡A³t«× +30\033[m");
+ win = race_path(run, pace[run] / 100, 3);
+ speed[run] += 30;
+ break;
+ case 8:
+ outs("°I¯«¤W¨­³t«×´î¥b¡A®ÇÃä¼È°±¤G¦¸\033[m");
+ speed[run] /= 2;
+ if (run > 0)
+ stop[run - 1] += 2;
+ if (run < 4)
+ stop[run + 1] += 2;
+ break;
+ case 9:
+ outs("³Q¶A©G¡A¦^¨ì°_ÂI\033[m");
+ win = race_path(run, pace[run] / 100, -30);
+ break;
+ case 10:
+ if (pace[0] + pace[1] + pace[2] + pace[3] + pace[4] > 6000)
+ {
+ outs("\033[5m¨Ï¥X³Í¤ÆÂȳ³¡A©Ò¦³¤H¦^¨ì°_ÂI\033[m");
+ for (i = 0; i < 5; i++)
+ win = race_path(i, pace[i] / 100, -30);
+ }
+ else
+ {
+ outs("¨Ï¥X¥øÃZ¼u¸õ¡A³t«× x1.3¡A¨ä¥L¤H´î¥b\033[m");
+ for (i = 0; i < 5 && i != run; i++)
+ speed[i] /= 2;
+ speed[run] *= 1.3;
+ }
+ break;
+ case 11:
+ if (money[run])
+ {
+ outs("¾ß¨ì«Ü¦h¿ú¡A¼È°±¤@¦¸\033");
+ addmoney(money[run]);
+ out_song();
+ stop[run]++;
+ }
+ else
+ {
+ outs("¾ã¤Ç°¨²n°_¨Ó¤F¡A³t«× +50\033[m");
+ speed[run] += 50;
+ }
+ break;
+ case 12:
+ j = rnd(5);
+ prints("·R¤W¤F[3%dm%s¡A³t«×¸ò¨e¤@¼Ë", j + 1, racename[j]);
+ speed[run] = speed[j];
+ break;
+ case 13:
+ if (money[run] > 0)
+ {
+ outs("ªº½äª÷ x1.5¡AÁȰաI\033[m");
+ money[run] *= 1.5;
+ move(7, 15);
+ clrtoeol();
+ outs("\033[1m");
+ for (i = 0; i < 5; i++)
+ prints(" \033[3%dm%7d ", i + 1, money[i]);
+ outs("\033[m");
+ }
+ else
+ {
+ outs("¾c¤l±¼¤F¡A°h«á¤T®æ\033[m");
+ race_path(run, pace[run] / 100, -3);
+ }
+ break;
+ }
+ }
+ else /* ©¹«e¶] */
+ {
+ if (stop[run])
+ {
+ prints("\033[1;3%dm%s\033[37m ª¦¤£°_¨Ó\033[m", run + 1, racename[run]);
+ stop[run]--;
+ }
+ else
+ {
+ prints("\033[1;3%dm%s\033[37m ©é©R©b¶]\033[m", run + 1, racename[run]);
+ i = pace[run] / 100;
+ win = race_path(run, i, (pace[run] + speed[run]) / 100 - i);
+ pace[run] += speed[run];
+ }
+ }
+ flag++;
+ }
+ move(b_lines - 1, 0);
+ prints("\033[1;35m¡¹ \033[37m¹CÀ¸µ²§ô \033[35m¡¹ \033[37mÀò³Óªº¬O\033[3%dm %s \033[m",
+ win + 1, racename[win]);
+ if (money[win])
+ {
+ money[win] += money[win] * (pace[win] - (pace[0] + pace[1] + pace[2] + pace[3] + pace[4]) / 5) / 500;
+ sprintf(buf, "®¥³ß±z©ã¤¤¤F¡AÀò±o¼úª÷ %d ¤¸", money[win]);
+ addmoney(money[win]);
+ }
+ else
+ {
+ strcpy(buf, "©êºp...±z¨S©ã¤¤£¬~~~");
+ }
+ vmsg(buf);
+ }
+
+abort_game:
+ return 0;
+}
+#endif /* HAVE_GAME */
diff --git a/game/recall.c b/game/recall.c
new file mode 100644
index 0000000..db7b5c7
--- /dev/null
+++ b/game/recall.c
@@ -0,0 +1,231 @@
+/*-------------------------------------------------------*/
+/* recall.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : Memory Game routines */
+/* create : 01/07/19 */
+/* update : / / */
+/* author : einstein@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+#include "bbs.h"
+
+#ifdef HAVE_GAME
+
+enum
+{
+ MG_XPOS = 4,
+ MG_YPOS = 4,
+
+ /* MAX_X * MAX_Y ¥²¶·¬O°¸¼Æ */
+ MAX_X = 10,
+ MAX_Y = 10,
+};
+
+
+static int cx, cy;
+static int board[MAX_X][MAX_Y], isopen[MAX_X][MAX_Y];
+static char card[52][3] = {"¢Ï", "¢Ð", "¢Ñ", "¢Ò", "¢Ó", "¢Ô", "¢Õ", "¢Ö", "¢×", "¢Ø",
+ "¢Ù", "¢Ú", "¢Û", "¢Ü", "¢Ý", "¢Þ", "¢ß", "¢à", "¢á", "¢â",
+ "¢ã", "¢ä", "¢å", "¢æ", "¢ç", "¢è", "¢é", "¢ê", "¢ë", "¢ì",
+ "¢í", "¢î", "¢ï", "¢ð", "¢ñ", "¢ò", "¢ó", "¢ô", "¢õ", "¢ö",
+ "¢÷", "¢ø", "¢ù", "¢ú", "¢û", "¢ü", "¢ý", "¢þ", "£@", "£A",
+ "£B", "£C"};
+
+
+
+static inline void
+init_board()
+{
+ int i, j, rx, ry, temp, count = 0;
+
+ for (i = 0; i < MAX_X; i++)
+ {
+ for (j = 0; j < MAX_Y; j++)
+ {
+ board[i][j] = (count++) / 2;
+ isopen[i][j] = 0;
+ }
+ }
+
+ for (i = 0; i < MAX_X; i++)
+ {
+ for (j = 0; j < MAX_Y; j++)
+ {
+ rx = rnd(MAX_X);
+ ry = rnd(MAX_Y);
+ temp = board[i][j];
+ board[i][j] = board[rx][ry];
+ board[rx][ry] = temp;
+ }
+ }
+
+ cx = 0;
+ cy = 0;
+}
+
+
+static void
+show_board()
+{
+ int i, j;
+
+ vs_bar("°O¾Ð¹CÀ¸");
+
+ for (i = 0; i < MAX_X; i++)
+ {
+ for (j = 0; j < MAX_Y; j++)
+ {
+ move(MG_XPOS + i, MG_YPOS + j * 2);
+ if (isopen[i][j])
+ {
+ outs(card[board[i][j]]);
+ }
+ else
+ {
+ outs("¡½");
+ }
+ }
+ }
+
+ move(3, 40);
+ outs("¡ô¡õ¡ö¡÷ ¤è¦VÁä");
+ move(5, 40);
+ outs("[Space][Enter] ½¶}");
+ move(7, 40);
+ outs("Q/q Â÷¶}");
+
+ move(MG_XPOS + cx, MG_YPOS + cy * 2 + 1);
+}
+
+
+static inline int
+valid_pos(x, y)
+ int x, y;
+{
+ if (x < 0 || x >= MAX_X || y < 0 || y >= MAX_Y)
+ {
+ return 0;
+ }
+ return 1;
+}
+
+
+static void
+get_pos(x, y)
+ int *x, *y;
+{
+ char ch;
+ while (1)
+ {
+ ch = vkey();
+ if (ch == KEY_UP && valid_pos(cx - 1, cy))
+ {
+ cx -= 1;
+ move(MG_XPOS + cx, MG_YPOS + cy * 2 + 1);
+ }
+ else if (ch == KEY_DOWN && valid_pos(cx + 1, cy))
+ {
+ cx += 1;
+ move(MG_XPOS + cx, MG_YPOS + cy * 2 + 1);
+ }
+ else if (ch == KEY_LEFT && valid_pos(cx, cy - 1))
+ {
+ cy -= 1;
+ move(MG_XPOS + cx, MG_YPOS + cy * 2 + 1);
+ }
+ else if (ch == KEY_RIGHT && valid_pos(cx, cy + 1))
+ {
+ cy += 1;
+ move(MG_XPOS + cx, MG_YPOS + cy * 2 + 1);
+ }
+ else if (ch == 'q' || ch == 'Q')
+ {
+ vmsg(MSG_QUITGAME);
+ *x = -1;
+ break;
+ }
+ else if (ch == '\n' || ch == ' ')
+ {
+ *x = cx;
+ *y = cy;
+ break;
+ }
+ }
+}
+
+
+int
+main_recall()
+{
+ int fx, fy, sx, sy, count = 0;
+
+ init_board();
+ show_board();
+
+ while (1)
+ {
+
+ while (1) /* ²Ä¤@¦¸ */
+ {
+ get_pos(&fx, &fy);
+ if (fx < 0)
+ {
+ goto abort_game;
+ }
+ if (isopen[fx][fy])
+ {
+ continue;
+ }
+ move(MG_XPOS + fx, MG_YPOS + 2 * fy);
+ outs(card[board[fx][fy]]);
+ move(MG_XPOS + fx, MG_YPOS + 2 * fy + 1);
+ isopen[fx][fy] = 1;
+ break;
+ }
+
+ while (1) /* ²Ä¤G¦¸ */
+ {
+ get_pos(&sx, &sy);
+ if (sx < 0)
+ {
+ goto abort_game;
+ }
+ if (isopen[sx][sy])
+ {
+ continue;
+ }
+ move(MG_XPOS + sx, MG_YPOS + 2 * sy);
+ outs(card[board[sx][sy]]);
+ move(MG_XPOS + sx, MG_YPOS + 2 * sy + 1);
+ isopen[sx][sy] = 1;
+ if (board[fx][fy] == board[sx][sy])
+ {
+ count += 2;
+ }
+ else
+ {
+ vmsg("¬Ý²M·¡¤F¨S¡H");
+ move(b_lines, 0);
+ clrtoeol();
+ move(MG_XPOS + fx, MG_YPOS + 2 * fy);
+ outs("¡½");
+ isopen[fx][fy] = 0;
+ move(MG_XPOS + sx, MG_YPOS + 2 * sy);
+ outs("¡½");
+ move(MG_XPOS + sx, MG_YPOS + 2 * sy + 1);
+ isopen[sx][sy] = 0;
+ }
+ break;
+ }
+
+ if (count == MAX_X * MAX_Y)
+ {
+ vmsg("®¥³ß±z¦¨¥\\¤F");
+ break;
+ }
+
+ }
+abort_game:
+ return 0;
+}
+#endif /* HAVE_GAME */
diff --git a/game/seven.c b/game/seven.c
new file mode 100644
index 0000000..b876a65
--- /dev/null
+++ b/game/seven.c
@@ -0,0 +1,967 @@
+/*-------------------------------------------------------*/
+/* seven.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : ½ä«°¤C±i¹CÀ¸ */
+/* create : 98/07/29 */
+/* update : 01/04/26 */
+/* author : weiren@mail.eki.com.tw */
+/* recast : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+
+#ifdef HAVE_GAME
+
+
+static char *kind[9] = {"¯QÀs", "³æ­F", "¨ß­F", "¤T±ø", "¶¶¤l", "¦Pªá", "¸¬Äª","ÅK¤ä", "¬h¤B"};
+static char *poker[52] =
+{
+ "¢±", "¢±", "¢±", "¢±", "¢²", "¢²", "¢²", "¢²", "¢³", "¢³", "¢³", "¢³",
+ "¢´", "¢´", "¢´", "¢´", "¢µ", "¢µ", "¢µ", "¢µ", "¢¶", "¢¶", "¢¶", "¢¶",
+ "¢·", "¢·", "¢·", "¢·", "¢¸", "¢¸", "¢¸", "¢¸", "¢â", "¢â", "¢â", "¢â",
+ "¢Ø", "¢Ø", "¢Ø", "¢Ø", "¢ß", "¢ß", "¢ß", "¢ß", "¢Ù", "¢Ù", "¢Ù", "¢Ù",
+ "¢Ï", "¢Ï", "¢Ï", "¢Ï"
+};
+
+
+static void
+out_song()
+{
+ static int count = 0;
+
+ /* ³¯Ä¶½å£»¤ß¸õ */
+ uschar *msg[8] =
+ {
+ "§A¤Sªñ¤S»· ¸ÜÁôÁô¬ù¬ù §Ú²q¤£¥X ¤]±´¤£¨ì§Aªº¤@¤Á",
+ "©]¥b¿ô¥bºÎ ¤ß­W­W²¢²¢ §Ú¨¾¤£¨ì ¤w¨«¶i¬Y¤@ºØ¦MÀI",
+ "¦å²GºCºCµo¼öÁyµo¿S ©I§l§Ï©»­n°±¤F",
+ "©ú©ú¥Õ¥Õ¤£¬O³æ¯Â³ßÅw ¨º¬O·R ¨º¬O·R¶Ü",
+ "Å¥¨ì¦Û¤v¤ß¸õ ¬°½Ö¤ß¸õ ¹ªÁn¯ëªº¾_Àú ºòÄñµÛ§Ú¤£¯à©ñ",
+ "¦h·Q §â¤ß¥æ´« §A´N¯à¼Æºâ §A´N·|©úÁA ¤ß¸õ ¤À¤À¬í",
+ "¦å²GºCºCµo¼öÁyµo¿S ©I§l§Ï©»­n°±¤F",
+ "©ú©ú¥Õ¥Õ¤£¬O³æ¯Â³ßÅw ²ö«D´N¬O·R ²ö«D¬O·R"
+ };
+ move(b_lines - 2, 0);
+ prints("\033[1;3%dm%s\033[m Äw½XÁÙ¦³ %d ¤¸", time(0) % 7, msg[count], cuser.money);
+ clrtoeol();
+ if (++count == 8)
+ count = 0;
+}
+
+
+static inline int
+find_pair(set) /* ¦³ Pair ´N¶Ç¦^ 1 */
+ int set[6];
+{
+ int i, j;
+
+ for (i = 0; i < set[5] - 1; i++)
+ {
+ for (j = i + 1; j < set[5]; j++)
+ {
+ if (set[j] / 4 == set[i] / 4)
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+static inline int
+find_tpair(set) /* Two Pair ¶Ç¦^ 1 */
+ int set[6];
+{
+ int i, j, k;
+ int z[13] = {0};
+
+ for (i = 0; i < 13; i++)
+ {
+ for (j = 0; j < 5; j++)
+ {
+ if (set[j] / 4 == i)
+ z[i]++;
+ }
+ }
+ k = 0;
+ for (i = 0; i < 13; i++)
+ {
+ if (z[i] >= 2)
+ k++;
+ }
+ if (k == 2)
+ return 1;
+ return 0;
+}
+
+
+static inline int
+find_triple(set) /* ¤T±ø¶Ç¦^ 3, ÅK¤ä¶Ç¦^ 4 */
+ int set[6];
+{
+ int i, j, k;
+
+ for (i = 0; i < 13; i++)
+ {
+ k = 0;
+ for (j = 0; j < 5; j++)
+ {
+ if (set[j] / 4 == i)
+ k++;
+ }
+ if (k == 4)
+ return 4;
+ if (k == 3)
+ return 3;
+ }
+ return 0;
+}
+
+
+static inline int
+find_dragon(set) /* ¶¶¶Ç¦^ 1, §_«h¶Ç¦^ 0 */
+ int set[6];
+{
+ int i;
+ int test[6];
+
+ for (i = 0; i < 5; i++)
+ test[i] = set[i] / 4;
+
+ for (i = 0; i < 3; i++)
+ {
+ if (test[i] + 1 != test[i + 1])
+ return 0;
+ }
+
+ if (test[4] == 12 && test[0] == 0)
+ return 1; /* A2345 ¶¶ */
+
+ if (test[3] + 1 == test[4])
+ return 1; /* ¤@¯ë¶¶ */
+ return 0;
+}
+
+
+static inline int
+find_flush(set) /* ¦Pªá¶Ç¦^ 1, §_«h¶Ç¦^ 0 */
+ int set[6];
+{
+ int i;
+ int test[6];
+
+ for (i = 0; i < 5; i++)
+ test[i] = set[i] % 4;
+
+ for (i = 1; i < 5; i++)
+ {
+ if (test[0] != test[i])
+ return 0;
+ }
+
+ return 1;
+}
+
+
+static int
+find_all(set)
+ int set[6];
+{
+ int i;
+ int a[9]; /* ¯QÀs, ­F , ¨ß­F, ¤T±ø, ¶¶, ¦Pªá, ­J¿c, ÅK¤ä, ¦Pªá¶¶ */
+
+ a[0] = 1; /* a[0] 1 2 3 4 5 6 7 a[8] */
+
+ for (i = 1; i < 9; i++)
+ a[i] = 0;
+
+ a[1] = find_pair(set);
+ a[2] = find_tpair(set);
+
+ switch (find_triple(set))
+ {
+ case 3:
+ a[3] = 1;
+ break;
+
+ case 4:
+ a[7] = 1;
+ break;
+ }
+
+ a[4] = find_dragon(set);
+ a[5] = find_flush(set);
+
+ if (a[2] && a[3])
+ a[6] = 1; /* ¨ß­F + ¤T±ø = ­J¿c */
+ if (a[4] && a[5])
+ a[8] = 1; /* ¦Pªá + ¶¶ = ¦Pªá¶¶ */
+
+ for (i = 8; i >= 0; i--)
+ if (a[i])
+ return i;
+}
+
+
+static inline int
+diedragon(set, a, b)
+ int set[6];
+ int a, b;
+{
+ int card[13] = {0};
+ int first[2];
+ int i, z;
+
+ first[0] = a;
+ first[1] = b;
+ z = find_all(set);
+
+ if (!z)
+ { /* ²Ä¤G°ô¯QÀs */
+ if (first[0] / 4 == first[1] / 4)
+ return 1;
+ if (first[1] / 4 > set[4] / 4)
+ return 1;
+ if (first[1] / 4 == set[4] / 4)
+ {
+ if (first[0] / 4 > set[3] / 4)
+ return 1;
+ } /* ­ËÀs */
+ }
+
+ else if (z == 1) /* ­F */
+ {
+ for (i = 0; i < 5; i++)
+ card[set[i] / 4]++;
+
+ for (i = 0; i < 13; i++)
+ {
+ if (card[i] == 2 && first[0] / 4 == first[1] / 4 && first[0] / 4 > i)
+ return 1; /* ¨â°ô³£³æ­F¥B­ËÀs */
+ }
+ }
+ return 0;
+}
+
+
+static inline int
+bigsmall(h, g, key, gm)
+ int h[7], g[7], key, gm[2];
+{
+ int hm[2];
+ int i, j, k = 0, tmp = 0, tmp2 = 0, x, a, b;
+ int duA = 0, duB = 0; /* duA duB ¬O¨â°ô§P©w¿éĹ°Ñ¼Æ, 1 ¬O¹q¸£Ä¹ */
+ int hset[6], gset[6]; /* host, guest */
+ int gc[13] = {0}, hc[13] = {0};
+
+ for (i = 0; i < 6; i++)
+ {
+ for (j = i + 1; j < 7; j++)
+ {
+ if (key == k)
+ {
+ hm[0] = i;
+ hm[1] = j;
+ };
+ k++;
+ }
+ }
+
+ if (hm[1] < hm[0])
+ {
+ k = hm[1];
+ hm[1] = hm[0];
+ hm[0] = k;
+ }
+
+ if (gm[1] < gm[0])
+ {
+ k = gm[1];
+ gm[1] = gm[0];
+ gm[0] = k;
+ }
+
+ if (h[hm[0]] / 4 == h[hm[1]] / 4)
+ tmp = 1;
+ if (g[gm[0]] / 4 == g[gm[1]] / 4)
+ tmp2 = 1;
+ if (tmp == tmp2)
+ {
+ if (h[hm[1]] / 4 > g[gm[1]] / 4)
+ duA = 1; /* duA=1 ªí¥Ü²Ä¤@°ô²ø®aĹ */
+ if (h[hm[1]] / 4 == g[gm[1]] / 4 && tmp == 1)
+ duA = 1; /* ²Ä¤@°ô³£­F¥B¥­¤â, ²ø®aĹ */
+ if (h[hm[1]] / 4 == g[gm[1]] / 4 && tmp == 0 && h[hm[0]] / 4 >= g[gm[0]] / 4)
+ duA = 1;
+ }
+ if (tmp > tmp2)
+ duA = 1;
+ if (tmp < tmp2)
+ duA = 0;
+ k = 0;
+ j = 0;
+
+ for (i = 0; i < 7; i++)
+ {
+ if (i != hm[0] && i != hm[1])
+ {
+ hset[j] = h[i];
+ j++;
+ }
+ if (i != gm[0] && i != gm[1])
+ {
+ gset[k] = g[i];
+ k++;
+ }
+ }
+
+ hset[5] = 5;
+ gset[5] = 5;
+ tmp = find_all(hset);
+ tmp2 = find_all(gset);
+
+ if (tmp > tmp2)
+ {
+ duB = 1;
+ }
+ else if (tmp == tmp2)
+ {
+ for (i = 0; i < 5; i++)
+ {
+ gc[gset[i] / 4]++;
+ hc[hset[i] / 4]++;
+ }
+ switch (tmp)
+ {
+ case 0:
+ i = 12;
+ x = 0;
+ duB = 1; /* ¨â¤è³£¬O¯QÀs */
+ do
+ {
+ if (hc[i] > gc[i])
+ {
+ duB = 1;
+ x = 1;
+ }
+ if (hc[i] < gc[i])
+ {
+ duB = 0;
+ x = 1;
+ }
+ i--;
+ if (i < 0)
+ x = 1;
+ } while (x == 0);
+ break;
+
+ case 1:
+ for (i = 0; i < 12; i++)
+ {
+ if (hc[i] == 2)
+ a = i;
+ if (gc[i] == 2)
+ b = i;
+ }
+ if (a > b)
+ {
+ duB = 1; /* ¨â¤è³£¬O­F */
+ }
+ else if (a == b)
+ {
+ i = 12;
+ j = 12;
+ x = 0;
+ duB = 1;
+ do
+ {
+ if (hc[i] == 2)
+ i--;
+ if (hc[j] == 2)
+ j--;
+ if (hc[i] > gc[j])
+ {
+ duB = 1;
+ x = 1;
+ }
+ if (hc[i] < gc[j])
+ {
+ duB = 0;
+ x = 1;
+ }
+ i--;
+ j--;
+ if (i < 0 || j < 0)
+ x = 1;
+ } while (x == 0);
+ }
+ break;
+
+ case 2:
+ i = 12;
+ x = 0;
+ duB = 2; /* ¨â¤è³£¬O¨ß­F */
+ do
+ {
+ if (hc[i] > gc[i] && hc[i] != 1)
+ {
+ duB = 1;
+ x = 1;
+ };
+ if (hc[i] < gc[i] && gc[i] != 1)
+ {
+ duB = 0;
+ x = 1;
+ };
+ i--;
+ if (i < 0)
+ x = 1;
+ } while (x == 0);
+ if (duB == 2)
+ {
+ for (i = 0; i < 12; i++)
+ {
+ if (hc[i] == 1)
+ a = i;
+ if (gc[i] == 1)
+ b = i;
+ }
+ duB = 1;
+ if (a < b)
+ duB = 0;
+ }
+ break;
+
+ case 3:
+ case 6:
+ for (i = 0; i < 12; i++)
+ {
+ if (hc[i] == 3)
+ a = i;
+ if (gc[i] == 3)
+ b = i;
+ }
+ if (a > b)
+ duB = 1; /* ¨â¤è³£¬O¤T±ø(­J¿c) */
+ else if (a < b)
+ duB = 0;
+ break;
+
+ case 4:
+ i = 12;
+ x = 0;
+ a = 0;
+ b = 0; /* ¨â¤è³£¬O¶¶¤l */
+ do
+ {
+ if (hc[i] > gc[i])
+ {
+ duB = 1;
+ x = 1;
+ }
+ if (hc[i] < gc[i])
+ {
+ duB = 0;
+ x = 1;
+ }
+ i--;
+ if (i < 0)
+ {
+ duB = 1;
+ x = 1;
+ }
+ } while (x == 0);
+
+ if (hc[12] == hc[0] && hc[0] == 1)
+ a = 1;
+ if (gc[12] == gc[0] && gc[0] == 1)
+ b = 1;
+ if (a > b)
+ duB = 0;
+ if (a < b)
+ duB = 1;
+ if (a == b && b == 1)
+ duB = 1;
+ break;
+
+ case 5:
+ if (hset[0] % 4 > gset[0] % 4)
+ duB = 1; /* ¨â¤è³£¬O¦Pªá */
+ if (hset[0] % 4 < gset[0] % 4)
+ duB = 0;
+ if (hset[0] % 4 == gset[0] % 4)
+ {
+ if (hset[4] > gset[4])
+ duB = 1;
+ if (hset[4] < gset[4])
+ duB = 0;
+ }
+ break;
+
+ case 7:
+ for (i = 0; i < 12; i++)
+ {
+ if (hc[i] == 4)
+ a = i;
+ if (gc[i] == 4)
+ b = i;
+ }
+ if (a > b)
+ duB = 1; /* ¨â¤è³£¬OÅK¤ä */
+ if (a < b)
+ duB = 0;
+ break;
+
+ case 8:
+ if (hset[0] % 4 > gset[0] % 4)
+ duB = 1; /* ¨â¤è³£¬O¦Pªá¶¶ */
+ if (hset[0] % 4 < gset[0] % 4)
+ duB = 0;
+ if (hset[0] % 4 == gset[0] % 4)
+ {
+ i = 12;
+ x = 0;
+ do
+ {
+ if (hc[i] > gc[i])
+ {
+ duB = 1;
+ x = 1;
+ }
+ if (hc[i] < gc[i])
+ {
+ duB = 0;
+ x = 1;
+ }
+ i--;
+ if (i < 0)
+ {
+ duB = 1;
+ x = 1;
+ }
+ } while (x == 0);
+ }
+ break;
+ }
+ }
+ return 2 * duA + duB;
+}
+
+
+static void
+print_Scard(card, x, y)
+ int card, x, y;
+{
+ char *flower[4] = {"¢Ñ", "¢Ò", "¢Ö", "¢á"};
+
+ move(x, y);
+ outs("¢~¢w¢w¢w¢¡");
+ move(x + 1, y);
+ prints("¢x%s ¢x", poker[card]);
+ move(x + 2, y);
+ prints("¢x%s ¢x", flower[card % 4]);
+ move(x + 3, y);
+ outs("¢x ¢x");
+ move(x + 4, y);
+ outs("¢x ¢x");
+ move(x + 5, y);
+ outs("¢x ¢x");
+ move(x + 6, y);
+ outs("¢¢¢w¢w¢w¢£");
+}
+
+
+static inline void
+print_hostcard(card, x) /* x ¬°¨â±iªº²Õ¦X key */
+ int card[7];
+ int x;
+{
+ int i, j, k = 0;
+ int tmp, tmp2;
+ int set[6];
+
+ for (i = 1; i < 6; i++)
+ {
+ move(5 + i, 0);
+ clrtoeol();
+ }
+
+ for (i = 0; i < 6; i++)
+ {
+ for (j = i + 1; j < 7; j++)
+ {
+ if (x == k)
+ {
+ tmp = i;
+ tmp2 = j;
+ };
+ k++;
+ }
+ }
+
+ print_Scard(card[tmp], 3, 0);
+ print_Scard(card[tmp2], 3, 4);
+
+ j = 0;
+ for (i = 0; i < 7; i++)
+ {
+ if (i != tmp && i != tmp2)
+ {
+ print_Scard(card[i], 3, 16 + j * 4);
+ set[j] = card[i];
+ j++;
+ }
+ }
+ set[5] = 5;
+ move(7, 4);
+ if (card[tmp] / 4 == card[tmp2] / 4)
+ x = 1;
+ prints("\033[1;44;33m %s%s \033[m ¢x ¢x \033[1;44;33m %s \033[m",
+ poker[card[tmp]], x == 1 ? "­F" : poker[card[tmp2]] ,kind[find_all(set)]);
+}
+
+
+static inline int
+score(first, set) /* ¦^¶Ç¤À¨â°ô«áªºµû¤À(AI), ¹q¸£·|§â 21 ºØµP«¬³£©î¥X¨Ó, ¨úµû¤À°ªªÌ */
+ int first[2], set[6];
+{
+ int i, z;
+ int points = 0;
+ int card[13] = {0};
+
+ z = find_all(set);
+ if (z == 0)
+ {
+ if (first[0] / 4 == first[1] / 4)
+ return 0;
+ if (first[1] / 4 >= set[4] / 4)
+ return 0; /* ­ËÀs */
+ }
+ else if (z == 1)
+ {
+ for (i = 0; i < 5; i++)
+ card[set[i] / 4]++;
+ for (i = 0; i < 13; i++)
+ {
+ if (card[i] == 2 && first[0] / 4 == first[1] / 4 && first[0] / 4 >= i)
+ return 0; /* ¨â°ô³£³æ­F¥B­ËÀs */
+ }
+ }
+
+ points = z + 2; /* ²Ä¤G°ô¯QÀs´Nºâ¨â¤À, ¥H¤W»¼¼W */
+ if (points >= 5)
+ points ++; /* ²Ä¤G°ô­Y¦³¶¶¥H¤W¦A¥[¤@¤À */
+ if (first[0] / 4 == first[1] / 4)
+ points += 3; /* ²Ä¤@°ô¦³­F¤À¼Æ¥[¤T */
+ if (first[0] / 4 != first[1] / 4 && first[1] / 4 >= 10)
+ points++;
+ /* ²Ä¤@°ôµL­F¦³ Q ¥H¤W¥[¤@¤À */
+ if (first[0] / 4 == 12 || first[1] / 4 == 12)
+ points += 1; /* ²Ä¤@°ô¦³ A ¤À¼Æ¦A¥[¤@ */
+ return points;
+}
+
+
+static inline int
+find_host(h) /* ¶Ç¦^¨â±iªº²Õ¦X key */
+ int h[7];
+{
+ int i, j, k, x = 0, z = 0;
+ int tmp = 0, tmp2 = 0;
+ int result[21] = {0}, set[6] = {0}, first[2];
+
+ for (i = 0; i < 6; i++)
+ {
+ for (j = i + 1; j < 7; j++)
+ {
+ first[0] = h[i];
+ first[1] = h[j];
+ x = 0;
+ for (k = 0; k < 7; k++)
+ {
+ if (i != k && j != k)
+ {
+ set[x] = h[k];
+ x++;
+ }
+ }
+ set[5] = 5;
+ result[z] = score(first, set);
+ z++;
+ }
+ }
+ for (i = 0; i < 21; i++)
+ {
+ if (result[i] >= tmp)
+ {
+ tmp = result[i];
+ tmp2 = i;
+ }
+ }
+ return tmp2;
+}
+
+
+static int
+get_newcard(mode)
+ int mode; /* 0:­«·s¬~µP 1:µoµP */
+{
+ static int card[14]; /* ³Ì¦h¥u·|¥Î¨ì 14 ±iµP */
+ static int now; /* µo¥X²Ä now ±iµP */
+ char num;
+ int i;
+
+ if (!mode) /* ­«·s¬~µP */
+ {
+ now = 0;
+ return -1;
+ }
+
+rand_num: /* random ¥X¤@±i©M¤§«e³£¤£¦PªºµP */
+ num = rnd(52);
+ for (i = 0; i < now; i++)
+ {
+ if (num == card[i]) /* ³o±iµP¥H«e random ¹L¤F */
+ goto rand_num;
+ }
+
+ card[now] = num;
+ now++;
+
+ return num;
+}
+
+
+int
+main_seven()
+{
+ int money; /* ©ãª÷ */
+ int host_card[7]; /* ¹q¸£ªº 7 ±iµP±i */
+ int guest_card[7]; /* ª±®aªº 7 ±iµP±i */
+ int mark[2]; /* ª±®a¼Ð°O¥Î */
+ int set[6];
+
+ int i, j, win;
+ char buf[10];
+
+ if (HAS_STATUS(STATUS_COINLOCK))
+ {
+ vmsg(msg_coinlock);
+ return XEASY;
+ }
+
+ while (1)
+ {
+ vs_bar("½ä«°¤C±i");
+ out_song();
+
+ vget(2, 0, "½Ð°Ý­n¤Uª`¦h¤Ö©O¡H(1 ~ 50000) ", buf, 6, DOECHO);
+ money = atoi(buf);
+ if (money < 1 || money > 50000 || money > cuser.money)
+ break; /* Â÷¶}½ä³õ */
+
+ cuser.money -= money;
+
+ out_song();
+
+ move(2, 0);
+ clrtoeol(); /* ²M±¼¡u½Ð°Ý­n¤Uª`¦h¤Ö¡v */
+ outs("(«ö ¡ö¡÷ ²¾°Ê¡A«ö ¡ô¡õ ¿ï¨ú¤G±iµP¡A¿ï¦n«ö enter ÅuµP)");
+
+ get_newcard(0); /* ¬~µP */
+
+ mark[0] = mark[1] = 123; /* mark[?] = 123 ªí¥Ü¨S¦³ mark */
+
+ /* µo¤Q¥|±iµP */
+ for (i = 0; i < 7; i++)
+ {
+ host_card[i] = get_newcard(1);
+ guest_card[i] = get_newcard(1);
+ }
+
+ /* ±Æ§Ç */
+ for (i = 0; i < 7; i++)
+ {
+ for (j = 0; j < (6 - i); j++)
+ {
+ /* ­É¥Î win */
+ if (guest_card[j] > guest_card[j + 1])
+ {
+ win = guest_card[j];
+ guest_card[j] = guest_card[j + 1];
+ guest_card[j + 1] = win;
+ }
+ if (host_card[j] > host_card[j + 1])
+ {
+ win = host_card[j];
+ host_card[j] = host_card[j + 1];
+ host_card[j + 1] = win;
+ }
+ }
+ }
+
+ /* ¦L¥X¤â¤WªºµP */
+ move(3, 0);
+ outs("¢~¢w¢~¢w¢~¢w¢~¢w¢~¢w¢~¢w¢~¢w¢w¢w¢¡\n");
+ outs("¢x ¢x ¢x ¢x ¢x ¢x ¢x ¢x\n");
+ outs("¢x ¢x ¢x ¢x ¢x ¢x ¢x ¢x\n");
+ outs("¢x ¢x ¢x ¢x ¢x ¢x ¢x ¢x\n");
+ outs("¢x ¢x ¢x ¢x ¢x ¢x ¢x ¢x\n");
+ outs("¢x ¢x ¢x ¢x ¢x ¢x ¢x ¢x\n");
+ outs("¢¢¢w¢¢¢w¢¢¢w¢¢¢w¢¢¢w¢¢¢w¢¢¢w¢w¢w¢£");
+ for (i = 0; i < 7; i++)
+ print_Scard(guest_card[i], 11, 0 + 4 * i);
+
+ i = j = 0;
+ for (;;) /* ¿ï¥X¤G±iµP */
+ {
+ /* ¦b¦¹°j°é¤º¡Ai ¬O²Ä´X±iµP¡Aj ¬O¤w¿ï¨ú¤F´X±iµP */
+ move(15, 1 + i * 4);
+ switch (vkey())
+ {
+ case KEY_RIGHT:
+ if (i < 6)
+ i++;
+ break;
+
+ case KEY_LEFT:
+ if (i > 0)
+ i--;
+ break;
+
+ case KEY_UP: /* ¿ï¨ú³o±iµP */
+ if (j < 2 && mark[0] != i && mark[1] != i) /* ¤£¯à­«ÂÐ mark ¥B³Ì¦h mark ¤G±i */
+ {
+ if (mark[0] == 123)
+ mark[0] = i;
+ else
+ mark[1] = i;
+ j++;
+ move(15, 2 + i * 4);
+ outs("¡´");
+ }
+ break;
+
+ case KEY_DOWN: /* ¨ú®ø¿ï¨ú³o±iµP */
+ if (mark[0] == i)
+ {
+ mark[0] = 123;
+ j--;
+ move(15, 2 + i * 4);
+ outs(" ");
+ }
+ else if (mark[1] == i)
+ {
+ mark[1] = 123;
+ j--;
+ move(15, 2 + i * 4);
+ outs(" ");
+ }
+ break;
+
+ case '\n': /* ¿ï¥X¨â±i«á«ö enter */
+ if (j == 2)
+ goto end_choose;
+ break;
+ }
+ }
+ end_choose:
+
+ if (mark[0] > mark[1])
+ {
+ i = mark[0];
+ mark[0] = mark[1];
+ mark[1] = i;
+ }
+
+ /* ¦L¥Xª±®a¤À¦n¨â°ô«áªºµP */
+ for (i = 1; i < 18; i++)
+ {
+ move(i, 0);
+ clrtoeol();
+ }
+ print_Scard(guest_card[mark[0]], 11, 0);
+ print_Scard(guest_card[mark[1]], 11, 4);
+
+ j = 0;
+ for (i = 0; i < 7; i++)
+ {
+ if (i != mark[0] && i != mark[1])
+ {
+ print_Scard(guest_card[i], 11, 16 + j * 4);
+ set[j] = guest_card[i];
+ j++;
+ }
+ }
+
+ /* §PÂ_¬O§_­ËÀs¡A­Y­ËÀs«hµ²§ô */
+ set[5] = 5;
+ if (diedragon(set, guest_card[mark[0]], guest_card[mark[1]]))
+ {
+ vmsg("­ËÀs");
+ continue;
+ }
+
+ /* §PÂ_³Ó­t */
+ i = find_host(host_card);
+ print_hostcard(host_card, i);
+ win = bigsmall(host_card, guest_card, i, mark);
+
+ /* ¨q¥Xµ²ªG */
+ switch (win)
+ {
+ /* ­É¥Î i ¨Ó·í°µ color1; ­É¥Î j ¨Ó·í°µ color2 */
+
+ case 0: /* ª±®a duA duB ¬ÒĹ */
+ win = 2;
+ i = 41;
+ j = 41;
+ break;
+
+ case 1: /* ª±®a duA Ĺ duB ¿é */
+ win = 1;
+ i = 41;
+ j = 47;
+ break;
+
+ case 2: /* ª±®a duA ¿é duB Ĺ */
+ win = 1;
+ i = 47;
+ j = 41;
+ break;
+
+ case 3: /* ª±®a duA duB ¬Ò¿é */
+ win = 0;
+ i = 47;
+ j = 47;
+ break;
+ }
+
+ move(15, 4);
+ prints("\033[1;%d;%dm %s%s \033[m ¢x ¢x \033[1;%d;%dm %s \033[m",
+ i, i == 41 ? 33 : 30, poker[guest_card[mark[0]]],
+ (guest_card[mark[0]] / 4 == guest_card[mark[1]] / 4) ? "­F" : poker[guest_card[mark[1]]],
+ j, j == 41 ? 33 : 30, kind[find_all(set)]);
+
+ switch (win)
+ {
+ case 2:
+ vmsg("±zŤF");
+ money *= 2;
+ break;
+
+ case 1:
+ vmsg("¥­¤â");
+ break;
+
+ case 0:
+ vmsg("±z¿é¤F");
+ money = 0;
+ break;
+ }
+ addmoney(money);
+ }
+ return 0;
+}
+#endif /* HAVE_GAME */
diff --git a/game/tetris.c b/game/tetris.c
new file mode 100644
index 0000000..817b028
--- /dev/null
+++ b/game/tetris.c
@@ -0,0 +1,446 @@
+/*-------------------------------------------------------*/
+/* tetris.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : «Xù´µ¤è¶ô */
+/* create : / / */
+/* update : 02/10/15 */
+/* author : zhch.bbs@bbs.nju.edu.cn */
+/* modify : hightman.bbs@bbs.hightman.net */
+/* recast : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+#ifdef HAVE_GAME
+
+#define MAX_MAP_WIDTH 10 /* ´Ñ½Lªº¼e«×¦@ 10 ¦æ */
+#define MAX_MAP_HEIGHT 20 /* ´Ñ½Lªº°ª«×¦@ 20 ¦C */
+#define MAX_STYLE 7 /* Á`¦@¦³ 7 ºØÃþ«¬ªº¤è¶ô */
+
+
+/* ¨CºØ¤è¶ô³£¬O 4*4 ªº¡AµM«á¥i¥HÂà 4 ­Ó¤è¦V */
+
+static int style_x[MAX_STYLE][4][4] =
+{
+ 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+ 0, 1, 2, 3, 1, 1, 1, 1, 0, 1, 2, 3, 1, 1, 1, 1,
+ 0, 1, 1, 2, 1, 0, 1, 0, 0, 1, 1, 2, 1, 0, 1, 0,
+ 1, 2, 0, 1, 0, 0, 1, 1, 1, 2, 0, 1, 0, 0, 1, 1,
+ 0, 1, 2, 0, 0, 1, 1, 1, 2, 0, 1, 2, 0, 0, 0, 1,
+ 0, 1, 2, 2, 1, 1, 0, 1, 0, 0, 1, 2, 0, 1, 0, 0,
+ 0, 1, 2, 1, 1, 0, 1, 1, 1, 0, 1, 2, 0, 0, 1, 0
+};
+
+static int style_y[MAX_STYLE][4][4] =
+{
+ 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1,
+ 1, 1, 1, 1, 0, 1, 2, 3, 1, 1, 1, 1, 0, 1, 2, 3,
+ 0, 0, 1, 1, 0, 1, 1, 2, 0, 0, 1, 1, 0, 1, 1, 2,
+ 0, 0, 1, 1, 0, 1, 1, 2, 0, 0, 1, 1, 0, 1, 1, 2,
+ 0, 0, 0, 1, 0, 0, 1, 2, 0, 1, 1, 1, 0, 1, 2, 2,
+ 0, 0, 0, 1, 0, 1, 2, 2, 0, 1, 1, 1, 0, 0, 1, 2,
+ 0, 0, 0, 1, 0, 1, 1, 2, 0, 1, 1, 1, 0, 1, 1, 2
+};
+
+
+static int map[MAX_MAP_HEIGHT + 1][MAX_MAP_WIDTH + 2];
+
+static int my_lines; /* Á`®ø¥h±ø¼Æ */
+static int my_scores; /* Á`±o¤À */
+static int level; /* Ãö¥d */
+static int delay; /* ¤è¶ô¦Û°Ê¤U¶^ªº®É¶¡¶¡¶Z (³æ¦ì:usec) */
+static int cx, cy; /* ¥Ø«e©Ò¦b (x, y) */
+static int style; /* ¥Ø«e¤è¶ôªºÃþ«¬ */
+static int dir; /* ¥Ø«e¤è¶ôªº¤è¦V */
+static int last_dir; /* ¤W¦¸¤è¦Vªº¤è¦V */
+
+
+static void
+move_map(x, y)
+ int x, y;
+{
+ move(x + 2, 2 * y);
+}
+
+
+static void
+tetris_welcome()
+{
+ move(4, 33);
+ outs("«öÁ仡©ú¡G");
+ move(5, 35);
+ outs("¡õ¡ö¡÷ ²¾°Ê¤è¦V");
+ move(6, 35);
+ outs("Space §Ö³t­°¤U");
+ move(7, 35);
+ outs("¡ô 180 «×±ÛÂà");
+ move(8, 35);
+ outs("k ¶¶®É°w±ÛÂà");
+ move(9, 35);
+ outs("j °f®É°w±ÛÂà");
+ move(10, 35);
+ outs("^S ¼È°±¹CÀ¸");
+ move(11, 35);
+ outs("q Â÷¶}¹CÀ¸");
+
+ move(13, 35);
+ outs("­Y´å¼Ð¦³²¾°Ê¿ù»~ªº²{¶H");
+ move(14, 35);
+ outs("¼È®É±N°»´ú¤è¦VÁä¥þ§ÎÃö³¬§Y¥i");
+
+ move(16, 33);
+ outs("¨C®ø¢²¢¯±ø¤É¤@¯Å");
+ move(17, 33);
+ outs("®ø¤@¦æ±o¢°¤À¡B¤G¦æ¢²¤À¡B¤T¦æ¢¶¤À¡B¥|¦æ¢°¢´¤À");
+
+ vmsg(NULL);
+}
+
+
+static void
+tetris_init() /* initialize map[][] */
+{
+ int i, j, line;
+
+ line = MAX_MAP_HEIGHT - level; /* ¨Ìµ¥¯Å¨Ó¨M©w¦³´X¦æ¦³ªF¦è */
+
+ /* map[][] ¸Ì­±ªº­È 0:ªÅ¥Õ( ) 1:¦ûº¡(¢i) 2:¥ª¥k¬É(¢x) 3:©³¬É(¢w) 4:¥ª¨¤¸¨¬É(¢|) 5:¥k¨¤¸¨¬É(¢}) */
+
+ for (i = 0; i < MAX_MAP_HEIGHT; i++)
+ {
+ for (j = 1; j <= MAX_MAP_WIDTH; j++)
+ {
+ if (i >= line)
+ map[i][j] = rnd(2); /* ¦ûº¡ */
+ else
+ map[i][j] = 0; /* ªÅ¥Õ */
+ }
+ map[i][0] = map[i][MAX_MAP_WIDTH + 1] = 2; /* ¥ª¥k¬É */
+ }
+
+ for (j = 1; j <= MAX_MAP_WIDTH; j++)
+ map[MAX_MAP_HEIGHT][j] = 3; /* ©³¬É */
+
+ map[MAX_MAP_HEIGHT][0] = 4; /* ¥ª¨¤¸¨¬É */
+ map[MAX_MAP_HEIGHT][MAX_MAP_WIDTH + 1] = 5; /* ¥ª¨¤¸¨¬É */
+}
+
+
+static void
+tetris_mapshow()
+{
+ int i, j;
+ char piece[6][3] = {" ", "¢i", "¢x", "¢w", "¢|", "¢}"};
+
+ /* map[][] ¸Ì­±ªº­È 0:ªÅ¥Õ( ) 1:¦ûº¡(¢i) 2:¥ª¥k¬É(¢x) 3:©³¬É(¢w) 4:¥ª¨¤¸¨¬É(¢|) 5:¥k¨¤¸¨¬É(¢}) */
+
+ for (i = 0; i <= MAX_MAP_HEIGHT; i++)
+ {
+ move_map(i, 0);
+ for (j = 0; j <= MAX_MAP_WIDTH + 1; j++)
+ outs(piece[map[i][j]]);
+ }
+}
+
+
+static void
+tetris_blineshow()
+{
+ move(b_lines, 0);
+ clrtoeol();
+ prints("µ¥¯Å¡G\033[1;32m%d\033[m Á`®ø¥h±ø¼Æ¡G\033[1;32m%d\033[m Á`±o¤À¡G\033[1;32m%d\033[m",
+ level, my_lines, my_scores);
+}
+
+
+static void
+block_show(x, y, s, d, f)
+ int x, y; /* (x, y) */
+ int s; /* style */
+ int d; /* dir */
+ int f; /* 1:¥[¤W¤è¶ô 0:²¾°£¤è¶ô */
+{
+ int n;
+
+ if (d == -1)
+ return;
+
+ for (n = 0; n <= 3; n++)
+ {
+ move_map(x + style_x[s][d][n], y + style_y[s][d][n]);
+ if (f)
+ outs("¢i"); /* piece[1] */
+ else
+ outs(" "); /* piece[0] */
+ }
+ move(1, 0);
+}
+
+
+static void
+block_move()
+{
+ static int last_x = -1;
+ static int last_y = -1;
+ static int last_style = -1;
+
+ if (last_x == cx && last_y == cy && last_style == style && last_dir == dir)
+ return;
+
+ block_show(last_x, last_y, last_style, last_dir, 0);
+ last_x = cx;
+ last_y = cy;
+ last_style = style;
+ last_dir = dir;
+ block_show(cx, cy, style, dir, 1);
+}
+
+
+static void
+tune_delay()
+{
+ /* delay ­n¦b 1 ~ 999999 ¤§¶¡ */
+ delay = 999999 / (level + 1);
+}
+
+
+static void
+check_lines() /* Àˬd¬Ý¬O§_¯à®ø¥h¤@±ø */
+{
+ int i, j, s;
+
+ s = 1;
+ for (i = 0; i < MAX_MAP_HEIGHT; i++)
+ {
+ for (j = 1; j <= MAX_MAP_WIDTH; j++)
+ {
+ if (map[i][j] == 0) /* ªÅ¥Õ */
+ break;
+ }
+
+ if (j == MAX_MAP_WIDTH + 1)
+ {
+ int n;
+
+ s *= 2;
+ /* ®ø¥h±ø¤W³¡¥þ³¡¤U²¾ */
+ for (n = i; n > 0; n--)
+ {
+ for (j = 1; j <= MAX_MAP_WIDTH; j++)
+ map[n][j] = map[n - 1][j];
+ }
+ for (j = 1; j <= MAX_MAP_WIDTH; j++)
+ map[0][j] = 0;
+
+ if (++my_lines % 30 == 0) /* ¨C®ø 30 ±øÃö¥d¥[¤@ */
+ {
+ level++;
+ tune_delay();
+ }
+ }
+ }
+
+ if (--s > 0) /* ¦³®ø¥h¦Ü¤Ö¤@±ø */
+ {
+ s = s * (10 + level) / 10;
+ my_scores += s;
+ tetris_mapshow();
+ tetris_blineshow();
+ }
+}
+
+
+static int /* 1:¸I¨ì»Ùꪫ 0:³q¦æµLªý */
+crash(x, y, s, d)
+ int x, y; /* (x, y) */
+ int s; /* style */
+ int d; /* dir */
+{
+ int n;
+
+ for (n = 0; n <= 3; n++)
+ {
+ if (map[x + style_x[s][d][n]][y + style_y[s][d][n]]) /* ¤w¦ûº¡ */
+ return 1;
+ }
+ return 0;
+}
+
+
+static void
+arrived()
+{
+ int n;
+
+ for (n = 0; n <= 3; n++)
+ map[cx + style_x[style][dir][n]][cy + style_y[style][dir][n]] = 1; /* ¦ûº¡ */
+
+ check_lines();
+}
+
+
+static int
+getkey()
+{
+ int fd;
+ struct timeval tv;
+
+ fd = 1;
+ tv.tv_sec = 0;
+ tv.tv_usec = delay;
+
+ /* ­Y¦³«öÁä¡A¦^¶Ç©Ò«öªºÁä¡F­Y delay ªº®É¶¡¨ì¤F¤´¨S¦³«öÁä¡A¦^¶Ç 0 */
+
+ if (select(1, (fd_set *) &fd, NULL, NULL, &tv) > 0)
+ return vkey();
+
+ return 0;
+}
+
+
+int
+main_tetris()
+{
+ int ch;
+ int next_style;
+
+ vs_bar("«Xù´µ¤è¶ô");
+ tetris_welcome();
+
+start_game:
+
+ level = vans("±q²Ä´X¯Å¶}©lª±(0-9)¡H[0] ") - '0';
+ if (level < 0 || level > 9)
+ level = 0;
+
+ tetris_init();
+ tetris_mapshow();
+
+ vmsg("¹CÀ¸¶}©l");
+ tetris_blineshow();
+
+ style = 0;
+ next_style = rnd(MAX_STYLE);
+ my_lines = my_scores = 0;
+ tune_delay();
+
+ while (1)
+ {
+ style = next_style;
+ next_style = rnd(MAX_STYLE);/* ¶Ã¼Æ¨M©w¤U¤@­Ó¥X¨Óªº¤è¶ôÃþ«¬ */
+ last_dir = -1; /* ¨C¦¸·s¤è¶ô¥X¨Ó³£­n­«³]¬° -1 */
+ dir = 0; /* ¤è¶ô¤@¥X¨Ó¬O´Â¤Wªº */
+ cx = 0; /* ¤è¶ô¤@¥X¨Óªº¦ì¸m */
+ cy = MAX_MAP_WIDTH / 2;
+
+ /* §â¤W¤@­Ó¤w¥X¨Óªº²M°£¡A§â¤U¤@­Ó­n¥X¨Óªºµe¦b¥k¤W¨¤ */
+ block_show(0, MAX_MAP_WIDTH + 2, style, dir, 0);
+ block_show(0, MAX_MAP_WIDTH + 2, next_style, dir, 1);
+
+ block_move();
+
+ if (crash(cx, cy, style, dir)) /* ·s¤è¶ô¤@¥X¨Ó´N crash¡Agame over */
+ break;
+
+ for (;;)
+ {
+ switch (ch = getkey())
+ {
+ case Ctrl('S'):
+ vmsg("¹CÀ¸¼È°±¡A«ö¥ô·NÁäÄ~Äòª±");
+ tetris_blineshow();
+ break;
+
+ case KEY_LEFT:
+ if (!crash(cx, cy - 1, style, dir))
+ {
+ cy--;
+ block_move();
+ }
+ break;
+
+ case KEY_RIGHT:
+ if (!crash(cx, cy + 1, style, dir))
+ {
+ cy++;
+ block_move();
+ }
+ break;
+
+ case KEY_DOWN:
+ if (!crash(cx + 1, cy, style, dir))
+ {
+ cx++;
+ block_move();
+ }
+ else
+ {
+ arrived();
+ ch = '#';
+ }
+ break;
+
+ case KEY_UP:
+ if (!crash(cx, cy, style, (dir + 2) % 4))
+ {
+ dir = (dir + 2) % 4;
+ block_move();
+ }
+ break;
+
+ case 'k':
+ if (!crash(cx, cy, style, (dir + 1) % 4))
+ {
+ dir = (dir + 1) % 4;
+ block_move();
+ }
+ break;
+
+ case 'j':
+ if (!crash(cx, cy, style, (dir + 3) % 4))
+ {
+ dir = (dir + 3) % 4;
+ block_move();
+ }
+ break;
+
+ case ' ':
+ while (!crash(cx + 1, cy, style, dir))
+ cx++;
+ block_move();
+ arrived();
+ ch = '#';
+ break;
+
+ case 0: /* ÀH delay ®É¶¡¤@¨ì·|¦Û°Ê©¹¤U¸õ */
+ if (!crash(cx + 1, cy, style, dir))
+ {
+ cx++;
+ block_move();
+ }
+ else
+ {
+ arrived();
+ ch = '#';
+ break;
+ }
+ break;
+ }
+
+ if (ch == 'q' || ch == '#')
+ break;
+
+ refresh();
+ } /* end of for (;;) */
+
+ if (ch == 'q')
+ break;
+ } /* end of while (1) */
+
+ if (vans("¥»§½µ²§ô¡I±zÁÙ­nÄ~Äòª±¶Ü(Y/N)¡H[N] ") == 'y')
+ goto start_game;
+
+ return 0;
+}
+#endif /* HAVE_GAME */
diff --git a/include/attr.h b/include/attr.h
new file mode 100644
index 0000000..8905a48
--- /dev/null
+++ b/include/attr.h
@@ -0,0 +1,30 @@
+/*-------------------------------------------------------*/
+/* attr.h ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : dynamic attribute database */
+/* create : 99/03/11 */
+/* update : / / */
+/*-------------------------------------------------------*/
+
+
+#ifndef _ATTR_H_
+#define _ATTR_H_
+
+#if 0
+ int key;
+ key < 0 is reserved.
+ key & 0xff == 0 is reserved.
+ 0x000000?? < key < 0x0000ff?? is reserved by maple.org
+ sizeof(attr): key & 0xff
+
+ file: $userhome/.ATTR
+#endif
+
+#define ATTR_OTHELLO_TOTAL 0x00001004
+#define ATTR_FIVE_TOTAL 0x00001104
+#define ATTR_BLOCK_TOTAL 0x00001204
+#define ATTR_OTHELLO_WIN 0x00001404
+#define ATTR_FIVE_WIN 0x00001504
+#define ATTR_BLOCK_WIN 0x00001604
+
+#endif
diff --git a/include/battr.h b/include/battr.h
new file mode 100644
index 0000000..1f4aff4
--- /dev/null
+++ b/include/battr.h
@@ -0,0 +1,104 @@
+/*-------------------------------------------------------*/
+/* battr.h ( NTHU CS MapleBBS Ver 2.36 ) */
+/*-------------------------------------------------------*/
+/* target : Board Attribution */
+/* create : 95/03/29 */
+/* update : 95/12/15 */
+/*-------------------------------------------------------*/
+
+
+#ifndef _BATTR_H_
+#define _BATTR_H_
+
+
+/* ----------------------------------------------------- */
+/* Board Attribution : flags in BRD.battr */
+/* ----------------------------------------------------- */
+
+
+#define BRD_NOZAP 0x01 /* ¤£¥i zap */
+#define BRD_NOTRAN 0x02 /* ¤£Âà«H */
+#define BRD_NOCOUNT 0x04 /* ¤£­p¤å³¹µoªí½g¼Æ */
+#define BRD_NOSTAT 0x08 /* ¤£¯Ç¤J¼öªù¸ÜÃD²Î­p */
+#define BRD_NOVOTE 0x10 /* ¤£¤½§G§ë²¼µ²ªG©ó [record] ªO */
+#define BRD_ANONYMOUS 0x20 /* °Î¦W¬ÝªO */
+#define BRD_NOSCORE 0x40 /* ¤£µû¤À¬ÝªO */
+#define BRD_NOPHONETIC 0x80 /* ¤Ïª`­µ¤å¬ÝªO */
+
+#define BRD_LOCAL 0x00000100 /* ¤£¦¬¯¸¥~«H */
+#define BRD_A10 0x00000200
+#define BRD_A11 0x00000400
+#define BRD_A12 0x00000800
+#define BRD_A13 0x00001000
+#define BRD_A14 0x00002000
+#define BRD_A15 0x00004000
+#define BRD_A16 0x00008000
+
+#define BRD_A17 0x00010000
+#define BRD_A18 0x00020000
+#define BRD_A19 0x00040000
+#define BRD_A20 0x00080000
+#define BRD_A21 0x00100000
+#define BRD_A22 0x00200000
+#define BRD_A23 0x00400000
+#define BRD_A24 0x00800000
+
+#define BRD_A25 0x01000000
+#define BRD_A26 0x02000000
+#define BRD_A27 0x04000000
+#define BRD_A28 0x08000000
+#define BRD_A29 0x10000000
+#define BRD_A30 0x20000000
+#define BRD_A31 0x40000000
+#define BRD_A32 0x80000000
+/* ----------------------------------------------------- */
+/* ¦UºØºX¼Ðªº¤¤¤å·N¸q */
+/* ----------------------------------------------------- */
+
+
+#define NUMBATTRS 32
+
+#define STR_BATTR "zTcsvA%PL-----------------------"
+/* itoc: ·s¼WºX¼Ðªº®É­Ô§O§Ñ¤F§ï³o¸Ì°Ú */
+
+
+#ifdef _ADMIN_C_
+static char *battr_tbl[NUMBATTRS] =
+{
+ "¤£¥i Zap", /* BRD_NOZAP */
+ "¤£Âà«H¥X¥h", /* BRD_NOTRAN */
+ "¤£°O¿ý½g¼Æ", /* BRD_NOCOUNT */
+ "¤£°µ¼öªù¸ÜÃD²Î­p", /* BRD_NOSTAT */
+ "¤£¤½¶}§ë²¼µ²ªG", /* BRD_NOVOTE */
+ "°Î¦W¬ÝªO", /* BRD_ANONYMOUS */
+ "¤£µû¤À¬ÝªO", /* BRD_NOSCORE */
+ "¤£¨Ï¥Îª`­µ¤å", /* BRD_NOPHONETIC */
+ "¤£¦¬¯¸¥~«H", /* BRD_LOCAL */
+ "«O¯d", /* BRD_A10 */
+ "«O¯d", /* BRD_A11 */
+ "«O¯d", /* BRD_A12 */
+ "«O¯d", /* BRD_A13 */
+ "«O¯d", /* BRD_A14 */
+ "«O¯d", /* BRD_A15 */
+ "«O¯d", /* BRD_A16 */
+ "«O¯d", /* BRD_A17 */
+ "«O¯d", /* BRD_A18 */
+ "«O¯d", /* BRD_A19 */
+ "«O¯d", /* BRD_A20 */
+ "«O¯d", /* BRD_A21 */
+ "«O¯d", /* BRD_A22 */
+ "«O¯d", /* BRD_A23 */
+ "«O¯d", /* BRD_A24 */
+ "«O¯d", /* BRD_A25 */
+ "«O¯d", /* BRD_A26 */
+ "«O¯d", /* BRD_A27 */
+ "«O¯d", /* BRD_A28 */
+ "«O¯d", /* BRD_A29 */
+ "«O¯d", /* BRD_A30 */
+ "«O¯d", /* BRD_A31 */
+ "«O¯d" /* BRD_A32 */
+};
+
+#endif
+
+#endif /* _BATTR_H_ */
diff --git a/include/bbs.h b/include/bbs.h
new file mode 100644
index 0000000..f690d51
--- /dev/null
+++ b/include/bbs.h
@@ -0,0 +1,65 @@
+/*-------------------------------------------------------*/
+/* bbs.h ( NTHU CS MapleBBS Ver 2.36 ) */
+/*-------------------------------------------------------*/
+/* target : all header files */
+/* create : 95/03/29 */
+/* update : 95/12/15 */
+/*-------------------------------------------------------*/
+
+
+#ifndef _BBS_H_
+#define _BBS_H_
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <errno.h>
+#include <string.h>
+#include <dirent.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/time.h>
+
+
+#ifdef SYSV
+
+#ifndef LOCK_EX
+#define LOCK_EX F_LOCK
+#define LOCK_UN F_ULOCK
+#endif
+
+#define getdtablesize() (64)
+
+#define usleep(usec) { \
+ struct timeval t; \
+ t.tv_sec = usec / 1000000; \
+ t.tv_usec = usec % 1000000; \
+ select( 0, NULL, NULL, NULL, &t); \
+}
+
+#endif /* SYSV */
+
+
+#define BMIN(a,b) ((a<b)?a:b)
+#define BMAX(a,b) ((a>b)?a:b)
+
+
+#include "config.h" /* User-configurable stuff */
+#include "dao.h"
+#include "perm.h" /* user/board permission */
+#include "ufo.h" /* user flag option */
+#include "battr.h" /* board attribution */
+#include "modes.h" /* The list of valid user modes */
+#include "struct.h" /* data structure */
+#include "global.h" /* global variable & definition */
+#include "theme.h" /* custom theme */
+#include "proto.h" /* prototype of functions */
+
+#endif /* _BBS_H_ */
diff --git a/include/config.h b/include/config.h
new file mode 100644
index 0000000..376e71f
--- /dev/null
+++ b/include/config.h
@@ -0,0 +1,433 @@
+/*-------------------------------------------------------*/
+/* config.h ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : site-configurable settings */
+/* create : 95/03/29 */
+/* update : 95/12/15 */
+/*-------------------------------------------------------*/
+
+
+/*--------------------------------------------------------------------*/
+/* MapleBridge Bulletin Board System Version: 2.36 */
+/* Copyright (C) 1994-1995 Jeng-Hermes Lin, Hung-Pin Chen, */
+/* SoC, Xshadow */
+/*--------------------------------------------------------------------*/
+/* MapleBridge Bulletin Board System Version: 3.10 */
+/* Copyright (C) 1997-1998 Jeng-Hermes Lin */
+/*--------------------------------------------------------------------*/
+
+
+#ifndef _CONFIG_H_
+#define _CONFIG_H_
+
+
+/* ----------------------------------------------------- */
+/* ©w¸q BBS ¯¸¦W¦ì§} */
+/* ------------------------------------------------------*/
+
+#define SCHOOLNAME "¥x«n¤@¤¤" /* ²Õ´¦WºÙ */
+#define BBSNAME "¯Á¥§¤p¯¸" /* ¤¤¤å¯¸¦W */
+#define BBSNAME2 "SonyBBS" /* ­^¤å¯¸¦W */
+#define SYSOPNICK "¤u¤Í¥_¥_" /* sysop ªº¼ÊºÙ */
+#define TAG_VALID "["BBSNAME2"]To" /* ¨­¤À»{ÃÒ¨ç token */
+
+#define MYIPADDR "210.70.137.212" /* IP address */
+#define MYHOSTNAME "sony.tfcis.org" /* ºô¸ô¦a§} FQDN */
+
+#define HOST_ALIASES {MYHOSTNAME, MYIPADDR, \
+ "sony.twbbs.org", "sony.twbbs.org.tw", \
+ "bbs.tfcis.org", \
+ NULL}
+
+#define MYCHARSET "big5" /* BBS ©Ò¨Ï¥Îªº¦r¶° */
+
+#define BBSHOME "/home/bbs" /* BBS ªº®a */
+#define BAKPATH "/home/bbsbak" /* ³Æ¥÷Àɪº¸ô®| */
+
+#define BBSUID 9999
+#define BBSGID 99 /* Linux ½Ð³]¬° 999 */
+
+
+/* ----------------------------------------------------- */
+/* ²ÕºA³W¹º */
+/* ----------------------------------------------------- */
+
+ /* ------------------------------------------------- */
+ /* ²ÕºA³W¹º£»¨t²Î°lÂÜ */
+ /* ------------------------------------------------- */
+
+#define HAVE_SEM /* ¨Ï¥Î semaphore */
+
+#define HAVE_MMAP /* ±Ä¥Î mmap(): memory mapped I/O */
+
+#ifndef CYGWIN
+#define HAVE_RLIMIT /* ±Ä¥Î resource limit¡ACygwin ¤£¯à¥Î */
+#endif
+
+#define MODE_STAT /* Æ[¹î¤Î²Î­p user ªº¥ÍºA¡A¥H°µ¬°¸gÀç¤è°w */
+
+#undef SYSOP_CHECK_MAIL /* itoc.001029: ¯¸ªø¥i¥HŪ¨ú¨Ï¥ÎªÌ«H½c */
+
+#undef SYSOP_SU /* itoc.001102: ¥H sysop µn¤J¥i¥HÅܧó¨Ï¥ÎªÌ¨­¤À */
+
+#define HAVE_MULTI_BYTE /* hightman.060504: ¤ä´©Âù¦r¸`º~¦r³B²z */
+
+ /* ------------------------------------------------- */
+ /* ²ÕºA³W¹º£»µù¥U»{ÃÒ */
+ /* ------------------------------------------------- */
+
+#define LOGINASNEW /* ±Ä¥Î¤W¯¸¥Ó½Ð±b¸¹¨î«× */
+
+#ifdef LOGINASNEW
+#undef HAVE_GUARANTOR /* itoc.000319: ±Ä¥Î«OÃÒ¤H¨î«× */
+#endif
+
+#undef HAVE_LOGIN_DENIED /* itoc.000319: ¾×±¼¬Y¨Ç¨Ó·½ªº³sµ²¡A°Ñ·Ó etc/bbs.acl */
+
+#undef NEWUSER_LIMIT /* ·s¤â¤W¸ôªº¤T¤Ñ­­¨î */
+
+#define JUSTIFY_PERIODICAL /* ©w´Á¨­¤À»{ÃÒ */
+
+#undef EMAIL_JUSTIFY /* µo¥X Internet Email ¨­¤À»{ÃÒ«H¨ç */
+
+#ifdef EMAIL_JUSTIFY
+#define HAVE_POP3_CHECK /* itoc.000315: POP3 ¨t²Î»{ÃÒ */
+#define HAVE_REGKEY_CHECK /* itoc.010112: »{ÃÒ½XÅçÃÒ */
+#endif
+
+#define HAVE_REGISTER_FORM /* µù¥U³æ»{ÃÒ */
+
+ /* ------------------------------------------------- */
+ /* ²ÕºA³W¹º£»µ²¥æªB¤Í */
+ /* ------------------------------------------------- */
+
+#define HAVE_MODERATED_BOARD /* ´£¨Ñ¦n¤Í¯µ±KªO */ /* ¯µ±K¬ÝªOªº¾\ŪÅv­­­n¬O PERM_SYSOP ¤~·|³Q·í¦¨¯µ±K¬ÝªO */
+ /* ¦n¤Í¬ÝªOªº¾\ŪÅv­­­n¬O PERM_BOARD ¤~·|³Q·í¦¨¦n¤Í¬ÝªO */
+#define CHECK_ONLINE /* itoc.010306: ¤å³¹¦Cªí¤¤¥i¥HÅã¥Ü¨Ï¥ÎªÌ¬O§_¦b¯¸¤W */
+
+#define HAVE_BADPAL /* itoc.010302: ´£¨ÑÃa¤Hªº¥\¯à */
+
+#define HAVE_LIST /* itoc.010923: ¸s²Õ¦W³æ */
+
+#define HAVE_ALOHA /* itoc.001202: ¤W¯¸³qª¾ */
+
+#undef LOGIN_NOTIFY /* ¨t²Î¨ó´Mºô¤Í */
+
+#if (defined(HAVE_ALOHA) || defined(LOGIN_NOTIFY))
+#define HAVE_NOALOHA /* itoc.010716: ¤W¯¸¤£³qª¾/¨ó´M */
+#endif
+
+#define LOG_BMW /* lkchu.981201: ¤ô²y°O¿ý³B²z */
+
+#ifdef LOG_BMW
+#define RETAIN_BMW /* itoc.021102: ¤ô²y¦sÃÒ */
+#endif
+
+#define LOG_TALK /* lkchu.981201: ²á¤Ñ°O¿ý³B²z */
+
+#define HAVE_NOBROAD /* itoc.010716: ©Ú¦¬¼s¼½ */
+
+#define BMW_COUNT /* itoc.010312: ­pºâ¤¤¤F´X­Ó¤ô²y */
+
+#define BMW_DISPLAY /* itoc.010313: Åã¥Ü¤§«eªº¤ô²y */
+
+#define HAVE_CHANGE_NICK /* ¨Ï¥ÎªÌ¦W³æ ^N ¥Ã¤[§ó§ï¼ÊºÙ */
+
+#define HAVE_CHANGE_FROM /* ¨Ï¥ÎªÌ¦W³æ ^F ¼È®É§ó§ï¬G¶m */
+
+#define HAVE_CHANGE_ID /* ¨Ï¥ÎªÌ¦W³æ ^D ¼È®É§ó§ï ID */
+
+#define HAVE_WHERE /* itoc.001102: ¨Ï¥ÎªÌ¦W³æ¬G¶m¿ëÃÑ¡A°Ñ·Ó etc/host fqdn */
+
+#ifdef HAVE_WHERE
+#define GUEST_WHERE /* itoc.010208: guest ¶Ã¼Æ¨ú¬G¶m */
+#endif
+
+#define GUEST_NICK /* itoc.000319: guest ¶Ã¼Æ¨ú¼ÊºÙ */
+
+#define DETAIL_IDLETIME /* itoc.020316: ®É±`§ó·s¶¢¸m®É¶¡ */
+
+#define TIME_KICKER /* itoc.030514: ¬O§_¦Û°Êñ°h idle ¹L¤[ªº¨Ï¥ÎªÌ */
+
+#define HAVE_BRDMATE /* itoc.020602: ªO¦ñ (¾\Ū¦P¤@¬ÝªO) */
+
+#define HAVE_SUPERCLOAK /* itoc.020602: µµÁô¡A¯¸ªø¶W¯ÅÁô§Î */
+
+ /* ------------------------------------------------- */
+ /* ²ÕºA³W¹º£»¬ÝªO«H½c */
+ /* ------------------------------------------------- */
+
+#define HAVE_ANONYMOUS /* ´£¨Ñ anonymous ªO */
+
+#ifdef HAVE_ANONYMOUS
+#undef HAVE_UNANONYMOUS_BOARD /* itoc.020602: ¤Ï°Î¦WªO¡A¥²¶·¦³¶} BN_UNANONYMOUS */
+#endif
+
+#define SHOW_USER_IN_TEXT /* ¦b¤å¥ó¤¤ Ctrl+Q ¥iÅã¥Ü User ªº¦W¦r */
+
+#define ANTI_PHONETIC /* itoc.030503: ¸T¥Îª`­µ¤å */
+
+#define ENHANCED_VISIT /* itoc.010407: ¤wŪ/¥¼ÅªÀˬd¬O¸Ó¬ÝªOªº³Ì«á¤@½g¨M©w */
+
+#define SLIDE_SHOW /* itoc.030411: ¦Û°Ê¼½©ñ¤å³¹ */
+
+#undef COLOR_HEADER /* lkchu.981201: ÅÜ´«±m¦â¼ÐÀY */
+
+#define CURSOR_BAR /* itoc.010113: ¿ï³æ¥ú´Î¡A­Y¶}±Ò¿ï³æ¥ú´Î¡A¿ï³æ´N¤£¯à¦³ÃC¦â±±¨î½X */
+
+#define HAVE_DECLARE /* ¨Ï title ¤¤¦³ [] §ó©úÅã¡A¥B¤é´Á¤W¦â */
+
+#define HAVE_POPUPMENU /* ÂÛ¥X¦¡¿ï³æ */
+
+#ifdef HAVE_POPUPMENU
+#define POPUP_ANSWER /* ÂÛ¥X¦¡¿ï³æ -- ¸ß°Ý¿ï¶µ */
+#define POPUP_MESSAGE /* ÂÛ¥X¦¡¿ï³æ -- °T®§µøµ¡ */
+#endif
+
+#define AUTHOR_EXTRACTION /* ´M§ä¦P¤@§@ªÌ¤å³¹ */
+
+#undef HAVE_TERMINATOR /* ²×µ²¤å³¹¤jªk --- ©Ø·¬¸¨¸­±Ù */
+
+#define HAVE_XYNEWS /* itoc.010822: ·s»D¾\Ū¼Ò¦¡ */
+
+#define HAVE_SCORE /* itoc.020114: ¤å³¹µû¤À¥\¯à */
+
+#define HAVE_DETECT_CROSSPOST /* cross-post ¦Û°Ê°»´ú */
+
+#define AUTO_JUMPBRD /* itoc.010910: ¬ÝªO¦Cªí¦Û°Ê¸õ¥h¤U¤@­Ó¥¼Åª¬ÝªO */
+
+#undef AUTO_JUMPPOST /* itoc.010910: ¤å³¹¦Cªí¦Û°Ê¸õ¥h³Ì«á¤@½g¥¼Åª */
+
+#define ENHANCED_BSHM_UPDATE /* itoc.021101: ¬ÝªO¦Cªí§R°£/¼Ð°O¤å³¹¤£¦C¤J¥¼Åªªº¿O */
+
+#define EVERY_Z /* ctrl-z Everywhere (°£¤F¼g¤å³¹) */
+
+#define EVERY_BIFF /* Thor.980805: ¶l®t¨ì³B¨Ó«ö¹a */
+
+#define HAVE_SIGNED_MAIL /* Thor.990409: ¥~°e«H¥ó¥[ñ¦W */
+
+#define INPUT_TOOLS /* itoc.000319: ²Å¸¹¿é¤J¤u¨ã */
+
+#define HAVE_FORCE_BOARD /* itoc.000319: ±j­¢ user login ®É­ÔŪ¨ú¬Y¤½§i¬ÝªO */
+ /* itoc.010726: ¸Ó¤½§i¬ÝªO­n¤£¥i zap¡A§_«h zap «á¤£·|°O¿ý brh */
+
+#define MY_FAVORITE /* itoc.001202: ´£¨Ñ§Úªº³Ì·R¬ÝªO */
+
+#define HAVE_COSIGN /* itoc.010108: ´£¨Ñ¬ÝªO³s¸p */
+
+#ifdef HAVE_COSIGN
+#undef SYSOP_START_COSIGN /* itoc.030613: ·sªO³s¸p­n¥ý¸g¯¸ªø¼f®Ö¤~¯à¶}©l */
+#endif
+
+#define HAVE_REFUSEMARK /* itoc.010602: ´£¨Ñ¬ÝªO¤å³¹¥[±K */
+
+#define HAVE_LABELMARK /* itoc.020307: ´£¨Ñ¬ÝªO¤å³¹¥[«Ý¬å¼Ð°O */
+
+#define POST_PREFIX /* itoc.020113: µoªí¤å³¹®É¼ÐÃD¥i¿ï¾ÜºØÃþ */
+
+#define MULTI_MAIL /* ¸s²Õ±H«H¥\¯à */
+
+#define HAVE_MAIL_ZIP /* itoc.010228: ´£¨Ñ§â«H¥ó/ºëµØ°ÏÀ£ÁYÂà±Hªº¥\¯à */
+
+#undef OVERDUE_MAILDEL /* itoc.020217: ²M°£¹L´Á«H¥ó */
+
+ /* ------------------------------------------------- */
+ /* ²ÕºA³W¹º£»¥~±¾µ{¦¡ */
+ /* ------------------------------------------------- */
+
+#define HAVE_EXTERNAL /* Xyz ¿ï³æ */
+
+#ifdef HAVE_EXTERNAL
+# define HAVE_SONG /* itoc.010205: ´£¨ÑÂIºq¥\¯à */
+# define HAVE_NETTOOL /* itoc.010209: ´£¨Ñºô¸ôªA°È¤u¨ã */
+# define HAVE_GAME /* itoc.010208: ´£¨Ñ¹CÀ¸ */
+# define HAVE_BUY /* itoc.010716: ´£¨ÑÁʶRÅv­­ */
+# define HAVE_TIP /* itoc.010301: ´£¨Ñ¨C¤é¤p¯µ³Z */
+# define HAVE_CLASSTABLE /* itoc.010907: ´£¨Ñ¥\½Òªí */
+# define HAVE_CREDIT /* itoc.020125: ´£¨Ñ°O±b¥» */
+# define HAVE_LOVELETTER /* itoc.020602: ´£¨Ñ±¡®Ñ²£¥Í¾¹ */
+# define HAVE_CALENDAR /* itoc.020831: ´£¨Ñ¸U¦~¾ä */
+#endif
+
+#ifdef HAVE_SONG
+# define HAVE_SONG_CAMERA /* itoc.010207: ´£¨ÑÂIºq¨ì°ÊºA¬ÝªO¥\¯à */
+# define LOG_SONG_USIES /* itoc.010928: ÂIºq°O¿ý */
+#endif
+
+
+/* ----------------------------------------------------- */
+/* ²ÕºA³]©w */
+/* ------------------------------------------------------*/
+
+#ifdef HAVE_MMAP
+#include <sys/mman.h>
+#endif
+
+#ifdef HAVE_SIGNED_MAIL
+#define PRIVATE_KEY_PERIOD 0 /* ¥­§¡´X¤Ñ´«¤@¦¸key¡A0 ªí¥Ü¤£´«key¡A¦Û°Ê²£¥Í */
+#endif
+
+#ifndef SHOW_USER_IN_TEXT
+#define outx outs
+#endif
+
+
+/* ----------------------------------------------------- */
+/* ¨t²Î°Ñ¼Æ£»ÀH BBS ¯¸³W¼Ò¦¨ªø¦ÓÂX¼W */
+/* ----------------------------------------------------- */
+
+#define MAXBOARD 1024 /* ³Ì¤j¶}ªO­Ó¼Æ */
+
+#define MAXACTIVE 512 /* ³Ì¦h¦P®É¤W¯¸¤H¼Æ */
+
+/* ----------------------------------------------------- */
+/* ¨t²Î°Ñ¼Æ£»¨ä¥L±`¥Î°Ñ¼Æ */
+/* ----------------------------------------------------- */
+
+/* bbsd.c ¤W¯¸µn¤J */
+
+#define LOGINATTEMPTS 3 /* ³Ì¤j¶i¯¸±K½X¥´¿ù¦¸¼Æ */
+#define MULTI_MAX 2 /* ¤@¯ë¨Ï¥ÎªÌ³Ì¤j multi-login ­Ó¼Æ */
+
+/* bbsd.c user.c admutil.c ©w´Á¨­¤À»{ÃÒ¡A°O±o¦P¨B­×§ï gem/@/@re-reg */
+
+#ifdef JUSTIFY_PERIODICAL
+#define VALID_PERIOD (86400 * 365) /* ¨­¤À»{ÃÒ¦³®Ä´Á(¬í) */
+#define INVALID_NOTICE_PERIOD (86400 * 10) /* ¨­¤À»{ÃÒ¥¢®Ä«e¦h¤[®É¶¡´£¿ô¨Ï¥ÎªÌ(¬í) */
+#endif
+
+/* talk.c ªB¤Í¦W³æ/¤ô²y¦Cªí */
+
+/* itoc.010825: ª`·N BMW_LOCAL_MAX >= BMW_PER_USER */
+
+#define PAL_MAX 400 /* ªB¤Í¦W³æ¤W­­(¤H) */
+#define BMW_EXPIRE 60 /* ¤ô²y³B²z®É¶¡(¬í) */
+#define BMW_PER_USER 5 /* ¤ô²y³B²z®É¶¡¤º¡A¤¹³\¥á´X­Ó¤ô²y */
+#define BMW_MAX 128 /* UCACHE ¤¤ pool ¤W­­ */
+#define BMW_LOCAL_MAX 8 /* Ctrl+R ¤W¤U©Ò¯àÂsÄý¤§«e¤ô²y­Ó¼Æ */
+
+/* aloha.c ¤W¯¸³qª¾ */
+
+#ifdef HAVE_ALOHA
+#define ALOHA_MAX 64 /* ¤W¯¸³qª¾¤W­­(¤H) */
+#endif
+
+/* menu.c ¯d¨¥ªO */
+
+#define NOTE_MAX 100 /* ¯d¨¥ªO«O¯d½g¼Æ */
+#define NOTE_DUE 48 /* ¯d¨¥ªO«O¯d®É¶¡(¤Ñ) */
+
+/* mail.c ¥Î¨ÓÀˬd«H½c¤j¤p¡A¶W¹L«h´£¥Ü¡A°O±o¦P¨B­×§ï etc/justified gem/@/@mailover */
+
+#define MAX_BBSMAIL 1000 /* PERM_MBOX ¦¬«H¤W­­(«Ê) */
+#define MAX_VALIDMAIL 300 /* »{ÃÒ user ¦¬«H¤W­­(«Ê) */
+#define MAX_NOVALIDMAIL 100 /* ¥¼»{ÃÒ user ¦¬«H¤W­­(«Ê) */
+
+/* bquota.c mail.c ¥Î¨Ó²M¹L´ÁÀÉ®×/«H¥óªº®É¶¡¡A°O±o¦P¨B­×§ï etc/justified */
+
+#ifdef OVERDUE_MAILDEL
+#define MARK_DUE 180 /* ¼Ð°O«O¦s¤§«H¥ó«O¯d®É¶¡(¤Ñ) */
+#define MAIL_DUE 60 /* ¤@¯ë«H¥ó«O¯d®É¶¡(¤Ñ) */
+#define FILE_DUE 30 /* ¨ä¥LÀɮ׫O¯d®É¶¡(¤Ñ) */
+#endif
+
+/* newbrd.c ¬ÝªO³s¸p */
+
+#ifdef HAVE_COSIGN
+#define NBRD_NUM_BRD 10 /* ¶}ªO»Ý­n³s¸p¤H¼Æ */
+#define NBRD_DAY_BRD 3 /* ¶}ªO¥i³s¸p¤Ñ¼Æ */
+#endif
+
+/* post.c °»´ú cross-post */
+
+#ifdef HAVE_DETECT_CROSSPOST
+#define MAX_CROSS_POST 3 /* cross post ³Ì¤j¼Æ¶q(½g) */
+#define CROSSPOST_DENY_DAY 1 /* cross post °±Åv®É¶¡(¤Ñ) */
+#endif
+
+/* visio.c bguard.c ¦Û°Ê½ð¤H */
+
+#ifdef TIME_KICKER
+#define IDLE_TIMEOUT 30 /* visio.c bguard.c µo§b¹L¤[¦Û°Êñ°h(¤À) */
+#define IDLE_WARNOUT 3 /* visio.c µo§b¹L¤[´£¿ô(¤À) -- ¦Û°Êñ°h«e¤T¤ÀÄÁ«e */
+#endif
+
+/* more.c edit.c ½­¶ */
+
+#define PAGE_SCROLL (b_lines - 1) /* «ö PgUp/PgDn ­n±²°Ê´X¦C */
+
+#define MAXLASTCMD 8 /* line input buffer */
+#define BLK_SIZ 4096 /* disk I/O block size */
+#define MAXSIGLINES 6 /* edit.c ñ¦WÀɤޤJ³Ì¤j¦æ¼Æ */
+#define MAXQUERYLINES 17 /* talk.c xchatd.c Åã¥Ü Query/Plan °T®§³Ì¤j¦æ¼Æ */
+#define MAX_CHOICES 32 /* vote.c §ë²¼®É³Ì¦h¦³ 32 ºØ¿ï¾Ü */
+#define TAG_MAX 256 /* xover.c TagList ¼ÐÅҼƥؤ§¤W­­ */
+#define LINE_HEADER 3 /* more.c bhttpd.c ÀÉÀY¦³¤T¦C */
+
+/* bbsd.c mail.c ¾ã²z¶g´Á */
+
+#define CHECK_PERIOD (86400 * 20) /* ¾ã²z«H½c/ªB¤Í¦W³æªº¶g´Á(¬í) */
+
+/* camera.c °ÊºA¬ÝªO */
+
+#define MOVIE_MAX 180 /* °Êµe±i¼Æ */
+#define MOVIE_SIZE (108 * 1024) /* °Êµe cache size */
+
+
+/* ----------------------------------------------------- */
+/* chat.c & xchatd.c ¤¤±Ä¥Îªº protocol */
+/* ------------------------------------------------------*/
+
+#define CHAT_SECURE /* ¦w¥þªº²á¤Ñ«Ç */
+
+#define EXIT_LOGOUT 0
+#define EXIT_LOSTCONN -1
+#define EXIT_CLIERROR -2
+#define EXIT_TIMEDOUT -3
+#define EXIT_KICK -4
+
+#define CHAT_LOGIN_OK "OK"
+#define CHAT_LOGIN_EXISTS "EX"
+#define CHAT_LOGIN_INVALID "IN"
+#define CHAT_LOGIN_BOGUS "BG"
+
+
+/* ----------------------------------------------------- */
+/* BBS ªA°È©Ò¥Îªº port */
+/* ------------------------------------------------------*/
+
+/* itoc.030512: ¦pªG­n¦b¦P¤@¥x¾÷¾¹¤W¬[¤G­Ó bbs ¯¸¡A¨º»ò­n¡G
+
+ 1) ¦b OS ¶}¤@­Ó·sªº user (¨Ò¦p¥s bbs2)¡A¨ä uid ­n©M¥t¤@­Ó bbs ¤£¦P
+ 2) §ï BBSHOME¡BBAKPATH¡BBBSUID¡BBBSGID ¬° bbs2 ªº¸ô®|¤ÎUID¡BGID
+ 3) §ï¥H¤Uªº *_PORT ¤Î *_KEY ©M¥t¤@­Ó bbs ¤£¦P (¨Ò¦p³£¥[¤W 10000)
+*/
+
+#define MAX_BBSDPORT 1 /* bbsd ­n¶}´X­Ó port¡AÀH BBSD_PORT ¦ÓÅÜ */
+#define BBSD_PORT {23} /* bbsd ©Ò¥Îªº port (bbsd.c) */
+#define BMTA_PORT 25 /* SMTP ©Ò¥Îªº port (bmtad.c) */
+#define GEMD_PORT 70 /* Gopher ©Ò¥Îªº port (gemd.c) */
+#define FINGER_PORT 79 /* Finger ©Ò¥Îªº port (bguard.c) */
+#define BHTTP_PORT 80 /* HTTP ©Ò¥Îªº port (bhttpd.c) */
+#define POP3_PORT 110 /* POP3 ©Ò¥Îªº port (bpop3d.c) */
+#define BNNTP_PORT 119 /* NNTP ©Ò¥Îªº port (bnntp.c) */
+#define CHAT_PORT 3838 /* ²á¤Ñ«Ç ©Ò¥Îªº port (chat.c xchatd.c) */
+#define INNBBS_PORT 7777 /* Âà«H ©Ò¥Îªº port (channel.c) */
+
+
+/* ----------------------------------------------------- */
+/* SHM ¤Î SEM ©Ò¥Îªº key */
+/* ----------------------------------------------------- */
+
+#define BRDSHM_KEY 2997 /* ¬ÝªO */
+#define UTMPSHM_KEY 1998 /* ¨Ï¥ÎªÌ */
+#define FILMSHM_KEY 2999 /* °ÊºA¬ÝªO */
+#define PIPSHM_KEY 4998 /* ¹q¤lÂû¹ï¾Ô */
+
+#define BSEM_KEY 2000 /* semaphore key */
+#define BSEM_FLG 0600 /* semaphore mode */
+#define BSEM_ENTER -1 /* enter semaphore */
+#define BSEM_LEAVE 1 /* leave semaphore */
+#define BSEM_RESET 0 /* reset semaphore */
+
+#endif /* _CONFIG_H_ */
diff --git a/include/dao.h b/include/dao.h
new file mode 100644
index 0000000..b460ab5
--- /dev/null
+++ b/include/dao.h
@@ -0,0 +1,39 @@
+/*-------------------------------------------------------*/
+/* lib/dao.h ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : data abstract object */
+/* create : 96/11/20 */
+/* update : 96/12/15 */
+/*-------------------------------------------------------*/
+
+
+#ifndef _DAO_H_
+#define _DAO_H_
+
+
+#ifndef NULL
+#define NULL 0 /* ((char *) 0) */
+#endif
+
+
+#ifndef BLK_SIZ
+#define BLK_SIZ 4096 /* disk I/O block size */
+#endif
+
+
+#ifndef REC_SIZ
+#define REC_SIZ 512 /* disk I/O record size */
+#endif
+
+
+/* Thor.981206: lkchu patch */
+extern char radix32[];
+
+
+#include "hdr.h" /* prototype */
+#include "dns.h" /* dns type */
+#include "splay.h" /* splay type */
+#include "../lib/dao.p" /* prototype */
+
+
+#endif /* _DAO_H_ */
diff --git a/include/dns.h b/include/dns.h
new file mode 100644
index 0000000..df571f7
--- /dev/null
+++ b/include/dns.h
@@ -0,0 +1,88 @@
+/*-------------------------------------------------------*/
+/* lib/dns.h ( NTHU CS MapleBBS Ver 3.00 ) */
+/*-------------------------------------------------------*/
+/* target : header file for DNS routines */
+/* create : 96/11/20 */
+/* update : 96/12/15 */
+/*-------------------------------------------------------*/
+
+
+#ifndef _DNS_H_
+#define _DNS_H_
+
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <ctype.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+
+
+//#undef HAVE_RELAY_SERVER /* ±Ä¥Î relay server ¨Ó¥~±H«H¥ó */
+
+#define HAVE_RELAY_SERVER /* ±Ä¥Î relay server ¨Ó¥~±H«H¥ó */
+
+#ifdef HAVE_RELAY_SERVER
+//#define RELAY_SERVER "mail.tnfsh.tn.edu.tw" /* outbound mail server */
+#define RELAY_SERVER "localhost" /* outbound mail server */
+#endif
+
+
+#ifndef INADDR_NONE
+#define INADDR_NONE 0xffffffff
+#endif
+
+#define INADDR_FMT "%u.%u.%u.%u"
+
+
+typedef union
+{
+ unsigned char d[4];
+ unsigned long addr;
+} ip_addr;
+
+
+#if 0
+ The standard udp packet size PACKETSZ (512) is not sufficient for some
+ nameserver answers containing very many resource records. The resolver may
+ switch to tcp and retry if it detects udp packet overflow. Also note that
+ the resolver routines res_query and res_search return the size of the
+ un*truncated answer in case the supplied answer buffer it not big enough
+ to accommodate the entire answer.
+#endif
+
+
+#if PACKETSZ > 1024
+#define MAXPACKET PACKETSZ
+#else
+#define MAXPACKET 1024 /* max packet size used internally by BIND */
+#endif
+
+
+/* MAX_MXLIST ­n¤ñ MAX_DNAME ¤j±o¦h */
+#define MAX_DNAME 128 /* maximum domain name */
+#define MAX_MXLIST 1024 /* maximum dx list */
+
+
+typedef union
+{
+ HEADER hdr;
+ u_char buf[MAXPACKET];
+} querybuf; /* response of DNS query */
+
+
+static inline unsigned short
+getshort(c)
+ unsigned char *c;
+{
+ unsigned short u;
+
+ u = c[0];
+ return (u << 8) + c[1];
+}
+
+
+#endif /* _DNS_H_ */
diff --git a/include/global.h b/include/global.h
new file mode 100644
index 0000000..c1b4431
--- /dev/null
+++ b/include/global.h
@@ -0,0 +1,503 @@
+/*-------------------------------------------------------*/
+/* global.h ( NTHU CS MapleBBS Ver 2.36 ) */
+/*-------------------------------------------------------*/
+/* target : global definitions & variables */
+/* create : 95/03/29 */
+/* update : 95/12/15 */
+/*-------------------------------------------------------*/
+
+
+#ifndef _GLOBAL_H_
+#define _GLOBAL_H_
+
+
+#ifdef _MAIN_C_
+# define VAR
+# define INI(x) = x
+#else
+# define VAR extern
+# define INI(x)
+#endif
+
+
+/* ----------------------------------------------------- */
+/* GLOBAL DEFINITION */
+/* ----------------------------------------------------- */
+
+
+/* itoc.010821: ©R¦W­ì«h: ­Ó¤H¾Ö¦³ªÌ(¦p­Ó¤H/¬ÝªO/ºëµØ°Ï©Ò¿W¦Û¾Ö¦³ªº)¿×¤§ FN_XXXX
+ ¨t²Î¾Ö¦³ªÌ¡A­Y¬O¤£Åܰʪº¤å¥ó¡A¿×¤§ FN_ETC_XXXX¡F­Y¬OÅܰÊÀɮסA¿×¤§ FN_RUN_XXXX */
+
+
+/* ----------------------------------------------------- */
+/* ­Ó¤H¥Ø¿ýÀɦW³]©w */
+/* ----------------------------------------------------- */
+
+
+#define FN_ACCT ".ACCT" /* User account */
+#define FN_BRH ".BRH" /* board reading history */
+#define FN_CZH ".CZH" /* class zap history */
+
+#define FN_PLANS "plans" /* ­pµeÀÉ */
+#define FN_SIGN "sign" /* ñ¦WÀÉ.? */
+
+#define FN_LOG "log" /* ¤W¯¸¨Ó·½°O¿ý */
+#define FN_JUSTIFY "justify" /* ¾ú¦¸»{ÃÒ¸ê®Æ */
+#define FN_EMAIL "email" /* »{ÃÒ§¹¾ã¦^«H°O¿ý */
+#define FN_ACL "acl" /* ¤W¯¸¦aÂI³]©w */
+
+#define FN_BMW "bmw" /* ¤ô²y°O¿ý Binary Message Write */
+#define FN_AMW "amw" /* ¤ô²y°O¿ý ASCII Message Write */
+#define FN_PAL "friend" /* ªB¤Í¦W³æ (Àx¦s¦b¦Û¤v¥Ø¿ý¡A°O¿ý¦³­þ¨Ç¤H) */
+
+#ifdef HAVE_LIST
+#define FN_LIST "list" /* ¯S®í¦W³æ.? */
+#endif
+
+#ifdef LOGIN_NOTIFY
+#define FN_BENZ "benz" /* ¨t²Î¨ó´M¤W¯¸³qª¾ */
+#endif
+
+#ifdef HAVE_ALOHA
+#define FN_ALOHA "aloha" /* ¤W¯¸³qª¾ (Àx¦s¦b¦Û¤v¥Ø¿ý¡A°O¿ý¦³­þ¨Ç¤H) */
+#define FN_FRIENZ "frienz" /* ¤W¯¸³qª¾ (Àx¦s¦b¹ï¤è¥Ø¿ý¡A¹ï¤è¤W¯¸®ÉIJµo) */
+#endif
+
+#define FN_PAYCHECK "paycheck" /* ¤ä²¼ */
+
+#ifdef LOG_TALK
+#define FN_TALK_LOG "talk.log" /* ²á¤Ñ°O¿ýÀÉ */
+#endif
+
+#ifdef MY_FAVORITE
+#define FN_MF "@MyFavorite" /* §Úªº³Ì·R */
+#endif
+
+#ifdef HAVE_CLASSTABLE
+#define FN_CLASSTBL "classtable" /* ¥\½Òªí */
+#define FN_CLASSTBL_LOG "classtable.log"/* ¥\½Òªí */
+#endif
+
+#ifdef HAVE_CREDIT
+#define FN_CREDIT "credit" /* °O±b¥» */
+#endif
+
+#ifdef HAVE_CALENDAR
+#define FN_TODO "todo" /* ¦æ¨Æ¾ä */
+#endif
+
+
+/* ----------------------------------------------------- */
+/* ¬ÝªO/ºëµØ°Ï/«H½cÀɦW³]©w */
+/* ----------------------------------------------------- */
+
+
+#define FN_DIR ".DIR" /* index */
+#define FN_VCH ".VCH" /* vote control header */
+#define FN_LOCK "lock" /* ¬ÝªOÂê©w */
+#define FN_NOTE "note" /* ¶iªOµe­± */
+#define FN_POSTLAW "postlaw" /* µo¤åºõ»â */
+
+/* ----------------------------------------------------- */
+/* ¨t²ÎÀɦW³]©w */
+/* ----------------------------------------------------- */
+
+ /* --------------------------------------------------- */
+ /* ®Ú¥Ø¿ý¤U¨t²ÎÀÉ®× */
+ /* --------------------------------------------------- */
+
+
+#define FN_BRD ".BRD" /* board list */
+#define FN_SCHEMA ".USR" /* userid schema */
+
+
+ /* --------------------------------------------------- */
+ /* run/ ¥Ø¿ý¤U¨t²ÎÀÉ®× */
+ /* --------------------------------------------------- */
+
+
+#define FN_RUN_USIES "run/usies" /* BBS log */
+#define FN_RUN_NOTE_ALL "run/note.all" /* ¯d¨¥ªO */
+#define FN_RUN_PAL "run/pal.log" /* ªB¤Í¶W¹L¤W­­°O¿ý */
+
+#define FN_RUN_ADMIN "run/admin.log" /* ¯¸ªø¦æ¬°°O¿ý */
+
+#ifdef LOG_SONG_USIES
+#define FN_RUN_SONGUSIES "run/song_usies" /* ÂIºq°O¿ý */
+#endif
+
+#ifdef HAVE_REGISTER_FORM
+#define FN_RUN_RFORM "run/rform" /* µù¥Uªí³æ */
+#define FN_RUN_RFORM_LOG "run/rform.log" /* µù¥Uªí³æ¼f®Ö°O¿ýÀÉ */
+#endif
+
+#ifdef MODE_STAT
+#define FN_RUN_MODE_LOG "run/mode.log" /* ¨Ï¥ÎªÌ°ÊºA²Î­p - record per hour */
+#define FN_RUN_MODE_CUR "run/mode.cur"
+#define FN_RUN_MODE_TMP "run/mode.tmp"
+#endif
+
+#ifdef HAVE_ANONYMOUS
+#define FN_RUN_ANONYMOUS "run/anonymous" /* °Î¦Wµoªí¤å³¹°O¿ý */
+#endif
+
+#ifdef HAVE_BUY
+#define FN_RUN_BANK_LOG "run/bank.log" /* ¶×¿ú°O¿ý */
+#endif
+
+#ifdef HAVE_SIGNED_MAIL
+#define FN_RUN_PRIVATE "run/prikey" /* ¹q¤lñ³¹ */
+#endif
+
+#define FN_RUN_EMAILREG "run/emailreg" /* °O¿ý¥Î¨Ó»{ÃÒªº«H½c */
+#define FN_RUN_MAIL_LOG "run/mail.log" /* ±H«Hªº°O¿ý */
+
+#define FN_RUN_POST "run/post" /* ¤å³¹½g¼Æ²Î­p */
+#define FN_RUN_POST_LOG "run/post.log" /* ¤å³¹½g¼Æ²Î­p */
+
+/* reaper ©Ò²£¥Íªº log */
+#define FN_RUN_LAZYBM "run/lazybm" /* °½ÃiªO¥D²Î­p */
+#define FN_RUN_MANAGER "run/manager" /* ¯S®íÅv­­¨Ï¥ÎªÌ¦Cªí */
+#define FN_RUN_REAPER "run/reaper" /* ªø´Á¥¼¤W¯¸³Q²M°£ªº¨Ï¥ÎªÌ¦Cªí */
+#define FN_RUN_EMAILADDR "run/emailaddr" /* ¦P¤@ email »{ÃÒ¦h¦¸¦Cªí */
+
+#define BMTA_LOGFILE "run/bmta.log" /* ¦¬¥~³¡«Hªº°O¿ý */
+
+
+ /* --------------------------------------------------- */
+ /* etc/ ¥Ø¿ý¤U¨t²ÎÀÉ®× */
+ /* --------------------------------------------------- */
+
+
+#define FN_ETC_EXPIRE "etc/expire.conf" /* ¬ÝªO¤å³¹½g¼Æ¤W­­³]©w */
+
+#define FN_ETC_VALID "etc/valid" /* ¨­¤À»{ÃÒ«H¨ç (Email »{ÃҮɡA±H¥hµ¹¯¸¥~«H½c) */
+#define FN_ETC_JUSTIFIED "etc/justified" /* »{ÃÒ³q¹L³qª¾ (»{ÃÒ³q¹L®É¡A±H¨ì¯¸¤º«H½c) */
+#define FN_ETC_REREG "etc/re-reg" /* ­«·s»{ÃÒ³qª¾ (»{ÃÒ¹L´Á®É¡A±H¨ì¯¸¤º«H½c) */
+
+#define FN_ETC_CROSSPOST "etc/cross-post" /* ¸ó¶K°±Åv³qª¾ (Cross-Post ®É¡A±H¨ì¯¸¤º«H½c) */
+
+#define FN_ETC_BADID "etc/badid" /* ¤£¶®¦W³æ (©Úµ´µù¥U ID) */
+#define FN_ETC_SYSOP "etc/sysop" /* ¯¸°È¦W³æ */
+
+#define FN_ETC_FEAST "etc/feast" /* ¸`¤é */
+
+#define FN_ETC_IDHOME "etc/idhome" /* ¬G¶m ID */
+#define FN_ETC_HOST "etc/host" /* ¬G¶m IP */
+#define FN_ETC_FQDN "etc/fqdn" /* ¬G¶m FQDN */
+
+#define FN_ETC_TIP "etc/tip" /* ¨C¤é¤p¯µ³Z */
+
+#define FN_ETC_LOVELETTER "etc/loveletter" /* ±¡®Ñ²£¥Í¾¹¤å®w */
+
+
+ /* --------------------------------------------------- */
+ /* etc/ ¥Ø¿ý¤U access crontrol list (ACL) */
+ /* --------------------------------------------------- */
+
+#define TRUST_ACLFILE "etc/trust.acl" /* »{ÃÒ¥Õ¦W³æ */
+#define UNTRUST_ACLFILE "etc/untrust.acl" /* »{ÃҶ¦W³æ */
+
+#define MAIL_ACLFILE "etc/mail.acl" /* ¦¬«H¥Õ¦W³æ */
+#define UNMAIL_ACLFILE "etc/unmail.acl" /* ¦¬«H¶Â¦W³æ */
+
+#define BBS_ACLFILE "etc/bbs.acl" /* ©Úµ´ telnet ³s½u¦W³æ */
+
+
+/* ----------------------------------------------------- */
+/* ¦U­ÓªOªºÀɦW³]©w */
+/* ----------------------------------------------------- */
+
+
+#if 0 /* itoc.000512: ¨t²Î¬ÝªO¡A¹w³]¤p¼g¡A¤j¼gµL§« */
+¬ÝªO ¤¤ ¤å ±Ô ­z ¾\ŪÅv­­ µoªíÅv­­
+sysop ¯¸°È ³ø§i¯¸ªø¡Eµ¹§Ú³ø³ø 0 0
+0announce ¯¸°È ©^¤Ñ©Ó¹B¡E¯¸ªø¶@¤ê 0 PERM_ALLADMIN
+test ¯¸°È ´ú¸Õ±M°Ï¡E·s¤â¸ÕÃz 0 PERM_BASIC
+note ¯¸°È °ÊºA¬ÝªO¡E¯]¾÷¸Ü»y 0 PERM_POST
+newboard ¯¸°È ¶}ÅP±MÄæ¡E³s¸p­«¦a 0 PERM_POST
+ktv ¯¸°È ÂIºq°O¿ý¡E¯u±¡¹ï¸Ü 0 PERM_SYSOP
+record ¯¸°È »Ä²¢­W»¶¡E¨t²Î°O¿ý 0 PERM_SYSOP
+deleted ¯¸°È ¤å³¹¬@±Ï¡E¸ê·½¦^¦¬ PERM_BM PERM_SYSOP
+bm ¯¸°È ±M·~°Q½×¡EªO¥D¥æ½Ë PERM_BM 0
+admin ¯¸°È ´c·d¤Ñ¦a¡E¯¸ªø¦ÛºN PERM_ALLADMIN 0
+log ¯¸°È ¨t²Î«OÀI¡E¦w¥þ°O¿ý PERM_ALLADMIN PERM_SYSOP
+junk ¯¸°È ¤å³¹²M²z¡E©U§£±»®I PERM_ALLBOARD PERM_SYSOP
+UnAnonymous ¯¸°È ¶Â¨çº¡¤Ñ¡E°Î¦W²{¨­ PERM_ALLBOARD PERM_SYSOP
+
+¨ä¤¤­­¨îŪ¨úªº¬ÝªO¦b Class ¤ÀÃþ¤¤­n³]©w ¸ê®Æ«O±K
+#endif
+
+/* ¥H¤U¬O¤@©w­n¦³ªº¨t²Î¬ÝªO¡A¦ý¬O¤@­ÓªO¥i¥H­«ÂШϥΦh¦¸¡A¨Ò¦p§ë½Zºq¥»ªº©M°ÊºA¬ÝªO¦@¥Î note ªO */
+
+#define BN_CAMERA "note" /* °ÊºA¬ÝªO©ñ¦b³oªOªººëµØ°Ï */
+#define BN_ANNOUNCE "0announce" /* ¤½§i¬ÝªO¡A±j­¢¾\Ū */
+#define BN_JUNK "junk" /* ¦Û¤v§R°£ªº¤å³¹©ñ¦b¦¹ */
+#define BN_DELETED "deleted" /* ªO¥D§R°£ªº¤å³¹©ñ¦b¦¹ */
+#define BN_SECURITY "log" /* ¨t²Î¦w¥þ°O¿ý */
+#define BN_RECORD "record" /* ¨t²Îªº¤@¯ë°O¿ý */
+#define BN_UNANONYMOUS "UnAnonymous" /* °Î¦WªOªº¤å³¹·|½Æ»s¤@¥÷¦b³o¸Ì */
+#define BN_KTV "ktv" /* ÂIºq°O¿ý©ñ¦b³oªO¡Aºq¥»©ñ¦b³oªOªººëµØ°Ï */
+#define BN_REQUEST BN_CAMERA /* ºq¥»§ë½Z³B */
+
+#define BN_NULL "©|¥¼¿ï©w" /* ¶i¯¸«áÁÙ¨S¶i¤J¥ô¦ó¬ÝªO¡A©ÒÅã¥Üªº¬ÝªO¦WºÙ */
+
+
+/* ----------------------------------------------------- */
+/* Áä½L³]©w */
+/* ----------------------------------------------------- */
+
+
+#define KEY_BKSP 8 /* ©M Ctrl('H') ¬Û¦P */
+#define KEY_TAB 9 /* ©M Ctrl('I') ¬Û¦P */
+#define KEY_ENTER 10 /* ©M Ctrl('J') ¬Û¦P */
+#define KEY_ESC 27
+#define KEY_UP -1
+#define KEY_DOWN -2
+#define KEY_RIGHT -3
+#define KEY_LEFT -4
+#define KEY_HOME -21
+#define KEY_INS -22
+#define KEY_DEL -23
+#define KEY_END -24
+#define KEY_PGUP -25
+#define KEY_PGDN -26
+
+
+#define I_TIMEOUT -31
+#define I_OTHERDATA -32
+
+
+#define Ctrl(c) (c & '\037')
+#define Esc(c) (c) /* itoc.030824: ¤£ TRAP_ESC */
+#define isprint2(c) (c >= ' ')
+
+
+#if 0 /* itoc.020108: «öÁä¹ïÀ³ªí */
+
+ int HEX = vkey('KEY');
+ ³oªí¬O¥Ñ vkey() ¿é¤JÁä½L¡A¶Ç¥Xªº¾ã¼Æ­È¹ïÀ³ªí¡C
+
+
+/* ­È¬O­tªº */
+
+KEY_INS ffffffea KEY_DEL ffffffe9
+KEY_HOME ffffffeb KEY_END ffffffe8
+KEY_PGUP ffffffe7 KEY_PGDN ffffffe6
+
+KEY_UP ffffffff KEY_DOWN fffffffe
+KEY_RIGHT fffffffd KEY_LEFT fffffffc
+
+/* ­È¬O¥¿ªº */
+
+¢z¢w¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢s¢w¢{
+¢x HEX¢x00¢x01¢x02¢x03¢x04¢x05¢x06¢x07¢x08¢x09¢x0a¢x0b¢x0c¢x0d¢x0e¢x0f¢x
+¢u¢w¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢t
+¢x KEY¢x ¢x^A¢x^B¢x^C¢x^D¢x^E¢x^F¢x^G¢x^H¢x^I¢x^J¢x^K¢x^L¢x^M¢x^N¢x^O¢x
+¢u¢w¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢t
+¢x HEX¢x10¢x11¢x12¢x13¢x14¢x15¢x16¢x17¢x18¢x19¢x1a¢x1b¢x1c¢x1d¢x1e¢x1f¢x
+¢u¢w¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢t
+¢x KEY¢x^P¢x^Q¢x^R¢x^S¢x^T¢x^U¢x^V¢x^W¢x^X¢x^Y¢x^Z¢xEs¢x ¢x ¢x ¢x ¢x
+¢u¢w¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢t
+¢x HEX¢x20¢x21¢x22¢x23¢x24¢x25¢x26¢x27¢x28¢x29¢x2a¢x2b¢x2c¢x2d¢x2e¢x2f¢x /* 0x22 ¬OÂù¤Þ¸¹¡AÁ×§K compile ¿ù»~ */
+¢u¢w¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢t
+¢x KEY¢x ¢x !¢xXX¢x #¢x $¢x %¢x &¢xXX¢x (¢x )¢x *¢x ¢x ,¢x -¢x .¢x /¢x /* 0x27 ¬O³æ¤Þ¸¹¡AÁ×§K compile ¿ù»~ */
+¢u¢w¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢t
+¢x HEX¢x30¢x31¢x32¢x33¢x34¢x35¢x36¢x37¢x38¢x39¢x3a¢x3b¢x3c¢x3d¢x3e¢x3f¢x
+¢u¢w¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢t
+¢x KEY¢x 0¢x 1¢x 2¢x 3¢x 4¢x 5¢x 6¢x 7¢x 8¢x 9¢x :¢x ;¢x <¢x ¢x >¢x ?¢x
+¢u¢w¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢t
+¢x HEX¢x40¢x41¢x42¢x43¢x44¢x45¢x46¢x47¢x48¢x49¢x4a¢x4b¢x4c¢x4d¢x4e¢x4f¢x
+¢u¢w¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢t
+¢x KEY¢x @¢x A¢x B¢x C¢x D¢x E¢x F¢x G¢x H¢x I¢x J¢x K¢x L¢x M¢x N¢x O¢x
+¢u¢w¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢t
+¢x HEX¢x50¢x51¢x52¢x53¢x54¢x55¢x56¢x57¢x58¢x59¢x5a¢x5b¢x5c¢x5d¢x5e¢x5f¢x
+¢u¢w¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢t
+¢x KEY¢x P¢x Q¢x R¢x S¢x T¢x U¢x V¢x W¢x X¢x Y¢x Z¢x [¢x \¢x ]¢x ^¢x _¢x
+¢u¢w¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢t
+¢x HEX¢x60¢x61¢x62¢x63¢x64¢x65¢x66¢x67¢x68¢x69¢x6a¢x6b¢x6c¢x6d¢x6e¢x6f¢x
+¢u¢w¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢t
+¢x KEY¢x `¢x a¢x b¢x c¢x d¢x e¢x f¢x g¢x h¢x i¢x j¢x k¢x l¢x m¢x n¢x o¢x
+¢u¢w¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢t
+¢x HEX¢x70¢x71¢x72¢x73¢x74¢x75¢x76¢x77¢x78¢x79¢x7a¢x7b¢x7c¢x7d¢x7e¢x7f¢x
+¢u¢w¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢q¢w¢t
+¢x KEY¢x p¢x q¢x r¢x s¢x t¢x u¢x v¢x w¢x x¢x y¢x z¢x {¢x |¢x }¢x ¢x ¢x
+¢|¢w¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢r¢w¢}
+
+/* ­È­«ÂÐ */
+
+KEY_BKSP == Ctrl('H') == 0x08
+'\t' == Ctrl('I') == 0x09
+'\n' == Ctrl('J') == 0x0a
+'\r' == Ctrl('M') == 0x0d
+
+#endif
+
+
+/* ----------------------------------------------------- */
+/* °T®§¦r¦ê¡G¿W¥ß¥X¨Ó¡A¥H§Q¤ä´©¦UºØ»y¨¥ */
+/* ----------------------------------------------------- */
+
+
+#define QUOTE_CHAR1 '>'
+#define QUOTE_CHAR2 ':'
+
+#define STR_SPACE " \t\n\r"
+
+#define STR_AUTHOR1 "§@ªÌ:"
+#define STR_AUTHOR2 "µo«H¤H:"
+#define STR_POST1 "¬ÝªO:"
+#define STR_POST2 "¯¸¤º:"
+#define STR_REPLY "Re: "
+#define STR_FORWARD "Fw: "
+
+#define STR_LINE "\n\
+> -------------------------------------------------------------------------- <\n\n"
+
+#define LEN_AUTHOR1 (sizeof(STR_AUTHOR1) - 1)
+#define LEN_AUTHOR2 (sizeof(STR_AUTHOR2) - 1)
+
+#define STR_SYSOP "sysop"
+#define STR_GUEST "guest"
+#define STR_NEW "new"
+
+#define STR_ANONYMOUS "«Ó®ðªº¸ô¤H¥Ì" /* ­nµu©ó IDLEN ­Ó¦r */
+
+#define MSG_SEPERATOR "\
+¢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"
+
+#define MSG_CANCEL "¨ú®ø"
+#define MSG_USR_LEFT "¹ï¤è¤w¸gÂ÷¥h"
+#define MSG_XY_NONE "ªÅµL¤@ª«"
+
+#define MSG_USERPERM "Åv­­µ¥¯Å¡G"
+#define MSG_READPERM "¾\\ŪÅv­­¡G"
+#define MSG_POSTPERM "µoªíÅv­­¡G"
+#define MSG_BRDATTR "¬ÝªOÄݩʡG"
+#define MSG_USERUFO "²ßºDºX¼Ð¡G £¾ / ¡@ £¾ / ¡@"
+
+#define MSG_XYPOST1 "¼ÐÃDÃöÁä¦r¡G"
+#define MSG_XYPOST2 "§@ªÌÃöÁä¦r¡G"
+
+#define MSG_DEL_OK "§R°£§¹²¦"
+#define MSG_DEL_CANCEL "¨ú®ø§R°£"
+#define MSG_DEL_ERROR "§R°£¿ù»~"
+#define MSG_DEL_NY "½Ð½T©w§R°£(Y/N)¡H[N] "
+
+#define MSG_SURE_NY "½Ð±z½T©w(Y/N)¡H[N] "
+#define MSG_SURE_YN "½Ð±z½T©w(Y/N)¡H[Y] "
+
+#define MSG_MULTIREPLY "±z½T©w­n¸s²Õ¦^«H(Y/N)¡H[N] "
+
+#define MSG_BID "½Ð¿é¤J¬ÝªO¦WºÙ¡G"
+#define MSG_UID "½Ð¿é¤J¥N¸¹¡G"
+#define MSG_PASSWD "½Ð¿é¤J±K½X¡G"
+
+#define ERR_BID "¿ù»~ªº¬ÝªO¦WºÙ"
+#define ERR_UID "¿ù»~ªº¨Ï¥ÎªÌ¥N¸¹"
+#define ERR_PASSWD "±K½X¿é¤J¿ù»~"
+#define ERR_EMAIL "¤£¦X®æªº E-mail address"
+
+#define MSG_SENT_OK "«H¤w±H¥X"
+
+#define MSG_LIST_OVER "±zªº¦W³æ¤Ó¦h¡A½Ðµ½¥[¾ã²z"
+
+#define MSG_COINLOCK "±z¤£¯à¥H¤À¨­¶i¦æ³o¼ËªºªA°È"
+#define MSG_REG_VALID "±z¤w¸g³q¹L¨­¤À»{ÃÒ¡A½Ð­«·s¤W¯¸"
+
+#define MSG_LL "\033[32m[¸s²Õ¦W³æ]\033[m\n"
+#define MSG_DATA_CLOAK "<¸ê®Æ«O±K>\n"
+
+#define MSG_CHKDATA "¡¹ ¸ê®Æ¾ã²z½]®Ö¤¤¡A½Ðµy­Ô \033[5m...\033[m"
+#define MSG_QUITGAME "¤£ª±¤F°Ú¡H¤U¦¸¦A¨Ó®@¡I ^_^"
+
+#define MSG_CHAT_ULIST \
+"\033[7m ¨Ï¥ÎªÌ¥N¸¹ ¥Ø«eª¬ºA ¢x ¨Ï¥ÎªÌ¥N¸¹ ¥Ø«eª¬ºA ¢x ¨Ï¥ÎªÌ¥N¸¹ ¥Ø«eª¬ºA \033[m"
+
+
+/* ----------------------------------------------------- */
+/* GLOBAL VARIABLE */
+/* ----------------------------------------------------- */
+
+
+VAR int bbsmode; /* bbs operating mode, see modes.h */
+VAR usint bbstate; /* bbs operatine state */
+
+VAR ACCT cuser; /* current user structure */
+VAR UTMP *cutmp; /* current user temporary */
+
+VAR time_t ap_start; /* ¶i¯¸®É¨è */
+VAR int total_user; /* ¨Ï¥ÎªÌ¦Û¥H¬°ªº¯¸¤W¤H¼Æ */
+
+VAR int b_lines; /* bottom line */
+VAR int b_cols; /* bottom columns */
+VAR int d_cols; /* difference columns from standard */
+
+VAR char fromhost[48]; /* from FQDN */
+
+VAR char ve_title[80]; /* edited title */
+VAR char quote_file[80];
+VAR char quote_user[80];
+VAR char quote_nick[80];
+
+VAR char hunt[32]; /* hunt keyword */
+
+VAR int curredit; /* current edit mode */
+VAR time_t currchrono; /* current file timestamp */
+VAR char currtitle[80]; /* currently selected article title */
+
+VAR int currbno; /* currently selected board bno */
+VAR usint currbattr; /* currently selected board battr */
+VAR char currboard[BNLEN + 1]; /* currently selected board brdname */
+VAR char currBM[BMLEN + 7]; /* currently selected board BM */ /* BMLEN + 1 + strlen("ªO¥D¡G") */
+
+/* filename */
+
+VAR char *fn_acct INI(FN_ACCT);
+VAR char *fn_dir INI(FN_DIR);
+VAR char *fn_bmw INI(FN_BMW);
+VAR char *fn_amw INI(FN_AMW);
+VAR char *fn_pal INI(FN_PAL);
+VAR char *fn_plans INI(FN_PLANS);
+VAR char *fn_note INI(FN_NOTE);
+VAR char *fn_lock INI(FN_LOCK);
+
+
+/* message */
+
+VAR char *msg_seperator INI(MSG_SEPERATOR);
+
+VAR char *msg_cancel INI(MSG_CANCEL);
+
+VAR char *msg_sure_ny INI(MSG_SURE_NY);
+
+VAR char *msg_uid INI(MSG_UID);
+
+VAR char *msg_del_ny INI(MSG_DEL_NY);
+
+VAR char *err_bid INI(ERR_BID);
+VAR char *err_uid INI(ERR_UID);
+VAR char *err_email INI(ERR_EMAIL);
+
+VAR char *msg_sent_ok INI(MSG_SENT_OK);
+
+VAR char *msg_list_over INI(MSG_LIST_OVER);
+
+VAR char *msg_reg_valid INI(MSG_REG_VALID);
+VAR char *msg_coinlock INI(MSG_COINLOCK);
+
+VAR char *str_sysop INI(STR_SYSOP);
+VAR char *str_author1 INI(STR_AUTHOR1);
+VAR char *str_author2 INI(STR_AUTHOR2);
+VAR char *str_post1 INI(STR_POST1);
+VAR char *str_post2 INI(STR_POST2);
+VAR char *str_host INI(MYHOSTNAME);
+VAR char *str_site INI(BBSNAME);
+VAR char *str_line INI(STR_LINE);
+
+VAR char *str_ransi INI("\033[m");
+
+#undef VAR
+#undef INI
+
+#endif /* _GLOBAL_H_ */
diff --git a/include/hdr.h b/include/hdr.h
new file mode 100644
index 0000000..f02e965
--- /dev/null
+++ b/include/hdr.h
@@ -0,0 +1,114 @@
+/*-------------------------------------------------------*/
+/* lib/hdr.h ( NTHU CS MapleBBS Ver 3.00 ) */
+/*-------------------------------------------------------*/
+/* target : header file for HDR routines */
+/* create : 96/11/20 */
+/* update : 96/12/15 */
+/*-------------------------------------------------------*/
+
+
+#ifndef _HDR_H_
+#define _HDR_H_
+
+
+#include <sys/types.h>
+
+
+/* ----------------------------------------------------- */
+/* DIR of post / mail struct : 256 bytes */
+/* ----------------------------------------------------- */
+
+
+typedef struct
+{
+ time_t chrono; /* timestamp */
+ int xmode;
+
+ int xid; /* reserved */
+
+ char xname[32]; /* ÀɮצWºÙ */
+ char owner[80]; /* §@ªÌ (E-mail address) */
+ char nick[49]; /* ¼ÊºÙ */
+ char score; /* ¤å³¹µû¤ñ¤À¼Æ */
+
+ char date[9]; /* 96/12/31 */
+ /* Thor.990329:¯S§Oª`·N, date¥u¨ÑÅã¥Ü¥Î, ¤£§@¤ñ¸û, ¥HÁ×§K y2k °ÝÃD,
+ ©w¸q 2000¬° 00, 2001¬°01 */
+
+ char title[73]; /* ¥DÃD TTLEN + 1 */
+} HDR;
+
+
+/* gopher url ¦r¦ê¡Gxname + owner + nick + date */
+
+#define GEM_URLEN (32 + 80 + 50 + 9 - 1)
+
+
+/* ----------------------------------------------------- */
+/* post.xmode ªº©w¸q */
+/* ----------------------------------------------------- */
+
+
+#define POST_READ 0x00000001 /* already read */
+#define POST_MARKED 0x00000002 /* marked */
+#define POST_RECORDED 0x00000004 /* ¦³³Q°O¿ý (¦³µ¹¿ú) */
+#define POST_4 0x00000008
+#define POST_5 0x00000010
+#define POST_6 0x00000020
+#define POST_BOTTOM 0x00000040 /* ¸m©³ */
+#define POST_DELETE 0x00000080 /* ¼Ð°O«Ý¬åªº */
+#define POST_INCOME 0x00000100 /* Âà«H¶i¨Óªº */
+#define POST_10 0x00000200
+#define POST_OUTGO 0x00000400 /* ¶·Âà«H¥X¥h */
+#define POST_RESTRICT 0x00000800 /* ­­¨î¯Å¤å³¹¡A¶· manager/owner ¤~¯à¬Ý */
+#define POST_RESERVED 0x00001000 /* ­­¨î¯Å¤å³¹¡A¶· sysop ¤~¯à§ó§ï */
+#define POST_14 0x00002000
+#define POST_SCORE 0x00004000 /* ¼Ð°Oµû¤À¹Lªº */
+#define POST_16 0x00008000
+
+
+/* ----------------------------------------------------- */
+/* mail.xmode ªº©w¸q */
+/* ----------------------------------------------------- */
+
+
+#define MAIL_READ 0x00000001 /* already read */
+#define MAIL_MARKED 0x00000002 /* marked */
+#define MAIL_REPLIED 0x00000004 /* ¤w¸g¦^¹L«H¤F */
+#define MAIL_MULTI 0x00000008 /* mail list */
+#define MAIL_5 0x00000010
+#define MAIL_NOREPLY 0x00000020 /* ¤£¥i¦^«H */
+#define MAIL_BOTTOM 0x00000040 /* ¸m©³ */
+#define MAIL_DELETE 0x00000080 /* ±N¾D§R°£ */
+#define MAIL_INCOME 0x00000100 /* bbsmail ¶i¨Óªº */
+#define MAIL_10 0x00000200
+#define MAIL_11 0x00000400
+#define MAIL_RESTRICT 0x00000800 /* ­­¨î¯Å«H¥ó¡A¶· manager/owner ¤~¯à¬Ý */
+#define MAIL_RESERVED 0x00001000 /* ­­¨î¯Å«H¥ó¡A¶· sysop ¤~¯à§ó§ï */
+#define MAIL_14 0x00002000
+#define MAIL_15 0x00004000
+#define MAIL_16 0x00008000
+
+
+/* ----------------------------------------------------- */
+/* gem(gopher).xmode ªº©w¸q */
+/* ----------------------------------------------------- */
+
+
+/* itoc.010602.µù¸Ñ: GEM_RESTRICT/RESERVED ©M POST_RESTRICT/RESERVED ­n¤Ç°t */
+#define GEM_RESTRICT 0x00000800 /* ­­¨î¯ÅºëµØ°Ï¡A¶· manager/owner ¤~¯à¬Ý */
+#define GEM_RESERVED 0x00001000 /* ­­¨î¯ÅºëµØ°Ï¡A¶· sysop ¤~¯à§ó§ï */
+
+#define GEM_FOLDER 0x00010000 /* folder / article */
+#define GEM_BOARD 0x00020000 /* ¬ÝªOºëµØ°Ï */
+#define GEM_LINE 0x00080000 /* ¤À¹j½u */
+
+/* ----------------------------------------------------- */
+/* hdr_stamp() ªº©w¸q */
+/* ----------------------------------------------------- */
+
+
+#define HDR_LINK 0x400 /* link() */
+#define HDR_COPY 0x800 /* copy */
+
+#endif /* _HDR_H_ */
diff --git a/include/modes.h b/include/modes.h
new file mode 100644
index 0000000..92af120
--- /dev/null
+++ b/include/modes.h
@@ -0,0 +1,253 @@
+/*-------------------------------------------------------*/
+/* modes.h ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : user operating mode & status */
+/* create : 95/03/29 */
+/* update : 95/12/15 */
+/*-------------------------------------------------------*/
+
+
+#ifndef _MODES_H_
+#define _MODES_H_
+
+
+/* ----------------------------------------------------- */
+/* user ¾Þ§@ª¬ºA»P¼Ò¦¡ */
+/* ----------------------------------------------------- */
+
+/* itoc.010329.µù¸Ñ: §â 0 «O¯d¡A®³¨Ó°µ¯S®í¥Î³~ */
+
+#define M_0MENU 1 /* main MENU */
+#define M_AMENU 2 /* admin MENU */
+#define M_MMENU 3 /* mail MENU */
+#define M_TMENU 4 /* talk MENU */
+#define M_UMENU 5 /* user MENU */
+#define M_XMENU 6 /* tool MENU */
+ /* M_XMENU ¬O menu ªº³Ì«á¤@­Ó */
+ /* M_XMENU ¤§«e¦³°ÊºA¬ÝªO */
+
+#define M_LOGIN 7 /* login */
+
+#define M_GEM 8 /* announce */
+#define M_BOARD 9 /* board list */
+#define M_MF 10 /* my favorite */
+#define M_READA 11 /* read article */
+#define M_RMAIL 12 /* read mail */
+
+#define M_PAL 13 /* set pal/aloha list */
+#define M_LUSERS 14 /* user list */
+#define M_VOTE 15
+#define M_BMW 16
+#define M_SONG 17
+#define M_COSIGN 18
+
+#define M_SYSTEM 19 /* ¡÷ M_SYSTEM(§t) »P M_CHAT(§t) ¶¡¤£±µ¨ü talk request */
+
+#define M_XFILES 20 /* admin set system files */
+#define M_UFILES 21 /* user set user files */
+
+#define M_BMW_REPLY 22
+#define M_GAME 23
+#define M_POST 24
+#define M_SMAIL 25
+#define M_TRQST 26
+
+#define M_TALK 27 /* ¡ö M_TALK(§t) »P M_IDLE(§t) ¶¡±µ mateid */
+#define M_CHAT 28 /* ¡÷ M_BBTP(§t) »P M_CHAT(§t) ¶¡¤£±µ¨ü talk request */
+#define M_PAGE 29
+#define M_QUERY 30
+#define M_IDLE 31 /* ¡ö M_TALK(§t) »P M_IDLE(§t) ¶¡±µ mateid */
+
+#define M_XMODE 32
+#define M_MAX M_XMODE
+
+
+#ifdef _MODES_C_
+static char *ModeTypeTable[] =
+{
+ "«O¯d",
+
+ "¥D¿ï³æ", /* M_0MENU */
+ "¨t²ÎºûÅ@", /* M_AMENU */
+ "¶l¥ó¿ï³æ", /* M_MMENU */
+ "¥æ½Í¿ï³æ", /* M_TMENU */
+ "¨Ï¥ÎªÌ¿ï³æ", /* M_UMENU */
+ "Xyz ¿ï³æ", /* M_XMENU */
+
+ "¤W¯¸³~¤¤", /* M_LOGIN */
+
+ "ºëµØ°Ï", /* M_GEM */
+ "¬ÝªO¦Cªí", /* M_BOARD */
+ "§Úªº³Ì·R", /* M_MF */
+ "¾\\ۤ峹", /* M_READA */
+ "Ū«H", /* M_RMAIL */
+
+ "µ²¥æªB¤Í", /* M_PAL */
+ "¨Ï¥ÎªÌ¦W³æ", /* M_LUSERS */
+ "§ë²¼¤¤", /* M_VOTE */
+ "¹î¬Ý¤ô²y", /* M_BMW */
+ "ÂIºq", /* M_SONG */
+ "¬ÝªO³s¸p", /* M_COSIGN */
+
+ "¨t²ÎºÞ²z", /* M_SYSTEM */
+
+ "½s¨t²ÎÀÉ®×", /* M_XFILES */
+ "½s­Ó¤HÀÉ®×", /* M_UFILES */
+
+ "¤ô²y·Ç³Æ¤¤", /* M_BMW_REPLY */
+ "ª±¹CÀ¸", /* M_GAME */
+ "µoªí¤å³¹", /* M_POST */
+ "¼g«H", /* M_SMAIL */
+ "«Ý¾÷", /* M_TRQST */
+
+ "¥æ½Í", /* M_TALK */ /* ±µ mateid ªº°ÊºA¤¤¤å¦r¤£¥i¤Óªø */
+ "²á¤Ñ", /* M_CHAT */
+ "©I¥s", /* M_PAGE */
+ "¬d¸ß", /* M_QUERY */
+ "µo§b", /* M_IDLE */
+
+ "¨ä¥L" /* M_XMODE */
+};
+#endif /* _MODES_C_ */
+
+
+/* ----------------------------------------------------- */
+/* menu.c ¤¤ªº¼Ò¦¡ */
+/* ----------------------------------------------------- */
+
+
+#define XEASY 0x333 /* Return value to un-redraw screen */
+
+
+/* ----------------------------------------------------- */
+/* pal.c ¤¤ªº¼Ò¦¡ */
+/* ----------------------------------------------------- */
+
+
+#define PALTYPE_PAL 0 /* ªB¤Í¦W³æ */
+#define PALTYPE_LIST 1 /* ¸s²Õ¦W³æ */
+#define PALTYPE_BPAL 2 /* ªO¤Í¦W³æ */
+#define PALTYPE_VOTE 3 /* ­­¨î§ë²¼¦W³æ */
+
+
+/* ----------------------------------------------------- */
+/* visio.c ¤¤ªº¼Ò¦¡ */
+/* ----------------------------------------------------- */
+
+
+/* Flags to getdata input function */
+
+#define NOECHO 0x0000 /* ¤£Åã¥Ü¡A¥Î©ó±K½X¨ú±o */
+#define DOECHO 0x0100 /* ¤@¯ëÅã¥Ü */
+#define LCECHO 0x0200 /* low case echo¡A´«¦¨¤p¼g */
+#define GCARRY 0x0400 /* ·|Åã¥Ü¤W¤@¦¸/¥Ø«eªº­È */
+
+#define GET_LIST 0x1000 /* ¨ú±o Link List */
+#define GET_USER 0x2000 /* ¨ú±o user id */
+#define GET_BRD 0x4000 /* ¨ú±o board id */
+
+
+/* ----------------------------------------------------- */
+/* read.c ¤¤ªº¼Ò¦¡ */
+/* ----------------------------------------------------- */
+
+/* for tag */
+
+#define TAG_NIN 0 /* ¤£ÄÝ©ó TagList */
+#define TAG_TOGGLE 1 /* ¤Á´« Taglist */
+#define TAG_INSERT 2 /* ¥[¤J TagList */
+
+
+/* for bbstate : bbs operating state */
+
+#define STAT_POST 0x0010000 /* ¬O§_¥i¥H¦b currboard µoªí¤å³¹ */
+#define STAT_BOARD 0x0020000 /* ¬O§_¥i¥H¦b currboard §R°£¡Bmark¤å³¹ (ªO¥D¡B¬ÝªOÁ`ºÞ) */
+#define STAT_BM 0x0040000 /* ¬O§_¬° currboard ªºªO¥D */
+#define STAT_LOCK 0x0100000 /* ¬O§_¬°Âê©w¿Ã¹õ */
+#define STAT_STARTED 0x8000000 /* ¬O§_¤w¸g¶i¤J¨t²Î */
+
+
+/* for user's board permission level & state record */
+
+#define BRD_L_BIT 0x0001 /* ¥i¦C¡Alist */
+#define BRD_R_BIT 0x0002 /* ¥iŪ¡Aread */
+#define BRD_W_BIT 0x0004 /* ¥i¼g¡Awrite */
+#define BRD_X_BIT 0x0008 /* ¥iºÞ¡Aexecute¡AªO¥D¡B¬ÝªOÁ`ºÞ */
+#define BRD_M_BIT 0x0010 /* ¥i²z¡Amanage¡AªO¥D */
+
+#define BRD_V_BIT 0x0020 /* ¤w¸g³}¹L¤F¡Avisit ==> ¬Ý¹L¡u¶iªOµe­±¡v */
+#define BRD_H_BIT 0x0040 /* .BRH ¤¤¦³¾\Ū°O¿ý (history) */
+#define BRD_Z_BIT 0x0080 /* .BRH zap ±¼¤F */
+
+
+/* for user's gem permission level record */
+
+#define GEM_W_BIT 0x0001 /* ¥i¼g¡Awrite¡AªO¥D¡A¬ÝªOÁ`ºÞ */
+#define GEM_X_BIT 0x0002 /* ¥iºÞ¡Aexecute¡A¯¸ªø */
+#define GEM_M_BIT 0x0004 /* ¥i²z¡Amanage¡AªO¥D */
+
+
+/* for curredit */
+
+#define EDIT_MAIL 0x0001 /* ¥Ø«e¬O mail/board ? */
+#define EDIT_LIST 0x0002 /* ¬O§_¬° mail list ? */
+#define EDIT_BOTH 0x0004 /* both reply to author/board ? */
+#define EDIT_OUTGO 0x0008 /* «ÝÂà«H¥X¥h */
+#define EDIT_ANONYMOUS 0x0010 /* °Î¦W¼Ò¦¡ */
+#define EDIT_RESTRICT 0x0020 /* ¥[±K¦sÀÉ */
+#define EDIT_BOTTOM 0x0040 /* ¸m©³¦sÀÉ */
+
+/* ----------------------------------------------------- */
+/* xover.c ¤¤ªº¼Ò¦¡ */
+/* ----------------------------------------------------- */
+
+
+#define XO_DL 0x80000000
+
+#define XO_MODE 0x10000000
+
+
+#define XO_NONE (XO_MODE + 0)
+#define XO_INIT (XO_MODE + 1)
+#define XO_LOAD (XO_MODE + 2)
+#define XO_HEAD (XO_MODE + 3)
+#define XO_NECK (XO_MODE + 4)
+#define XO_BODY (XO_MODE + 5)
+#define XO_FOOT (XO_MODE + 6) /* itoc.µù¸Ñ: ²M°£ b_lines */
+#define XO_LAST (XO_MODE + 7)
+#define XO_QUIT (XO_MODE + 8)
+
+
+#define XO_RSIZ 256 /* max record length */
+#define XO_TALL (b_lines - 3) /* page size = b_lines - 3 (¦©¥h head/neck/foot ¦@¤T¦æ) */
+
+
+#define XO_MOVE 0x20000000 /* cursor movement */
+#define XO_WRAP 0x08000000 /* cursor wrap in movement */
+#define XO_TAIL (XO_MOVE - 999) /* init cursor to tail */
+
+
+#define XO_ZONE 0x40000000 /* ¶i¤J¬Y¤@­Ó zone */
+#define XZ_BACK 0x100
+
+
+#define XZ_CLASS (XO_ZONE + 0) /* ¬ÝªO¦Cªí */
+#define XZ_ULIST (XO_ZONE + 1) /* ½u¤W¨Ï¥ÎªÌ¦W³æ */
+#define XZ_PAL (XO_ZONE + 2) /* ªB¤Í¦W³æ */
+#define XZ_ALOHA (XO_ZONE + 3) /* ¤W¯¸³qª¾¦W³æ */
+#define XZ_VOTE (XO_ZONE + 4) /* §ë²¼ */
+#define XZ_BMW (XO_ZONE + 5) /* ¤ô²y */
+#define XZ_MF (XO_ZONE + 6) /* §Úªº³Ì·R */
+#define XZ_COSIGN (XO_ZONE + 7) /* ³s¸p */
+#define XZ_SONG (XO_ZONE + 8) /* ÂIºq */
+#define XZ_NEWS (XO_ZONE + 9) /* ·s»D¾\Ū¼Ò¦¡ */
+
+/* ¥H¤Uªº¦³ thread ¥DÃD¦¡¾\Ūªº¥\¯à */
+/* ¥H¤Uªº¦³ tag ¥\¯à */
+
+#define XZ_XPOST (XO_ZONE + 10) /* ·j´M¤å³¹¼Ò¦¡ */
+#define XZ_MBOX (XO_ZONE + 11) /* «H½c */
+#define XZ_POST (XO_ZONE + 12) /* ¬ÝªO */
+#define XZ_GEM (XO_ZONE + 13) /* ºëµØ°Ï */
+
+#endif /* _MODES_H_ */
diff --git a/include/perm.h b/include/perm.h
new file mode 100644
index 0000000..6c73e31
--- /dev/null
+++ b/include/perm.h
@@ -0,0 +1,160 @@
+/*-------------------------------------------------------*/
+/* perm.h ( NTHU CS MapleBBS Ver 2.36 ) */
+/*-------------------------------------------------------*/
+/* target : permission levels of user & board */
+/* create : 95/03/29 */
+/* update : 95/12/15 */
+/*-------------------------------------------------------*/
+
+
+#ifndef _PERM_H_
+#define _PERM_H_
+
+
+/* ----------------------------------------------------- */
+/* These are the 32 basic permission bits. */
+/* ----------------------------------------------------- */
+
+#define PERM_BASIC 0x00000001 /* 1-8 : °ò¥»Åv­­ */
+#define PERM_CHAT 0x00000002
+#define PERM_PAGE 0x00000004
+#define PERM_POST 0x00000008
+#define PERM_VALID 0x00000010 /* LOGINOK */
+#define PERM_MBOX 0x00000020
+#define PERM_CLOAK 0x00000040
+#define PERM_XEMPT 0x00000080
+
+#define PERM_CHANGEID 0x00000100
+#define PERM_ELDER 0x00000200
+#define PERM_ROBOT 0x00000400
+#define PERM_MSCORE 0x00000800
+#define PERM_13 0x00001000
+#define PERM_14 0x00002000
+#define PERM_15 0x00004000
+#define PERM_16 0x00008000
+
+#define PERM_DENYPOST 0x00010000 /* 17-24 : ¸T¨îÅv­­ */
+#define PERM_DENYTALK 0x00020000
+#define PERM_DENYCHAT 0x00040000
+#define PERM_DENYMAIL 0x00080000
+#define PERM_DENY5 0x00100000
+#define PERM_DENY6 0x00200000
+#define PERM_DENYLOGIN 0x00400000
+#define PERM_PURGE 0x00800000
+
+#define PERM_BM 0x01000000 /* 25-32 : ºÞ²zÅv­­ */
+#define PERM_SEECLOAK 0x02000000
+#define PERM_CAST 0x04000000
+#define PERM_REGISTRAR 0x08000000
+#define PERM_ACCOUNTS 0x10000000
+#define PERM_CHATROOM 0x20000000
+#define PERM_BOARD 0x40000000
+#define PERM_SYSOP 0x80000000
+
+
+/* ----------------------------------------------------- */
+/* These permissions are bitwise ORs of the basic bits. */
+/* ----------------------------------------------------- */
+
+
+/* This is the default permission granted to all new accounts. */
+#define PERM_DEFAULT PERM_BASIC
+
+/* ¦³ PERM_VALID ¤~¥i¥H«O¤H¶i¨Ó¥»¯¸µù¥U */
+#ifdef HAVE_GUARANTOR
+#define PERM_GUARANTOR PERM_VALID
+#endif
+
+#if 0 /* itoc.»¡©ú: Ãö©ó¯¸°ÈÅv­­ */
+
+ ©Ò¦³µ{¦¡ªºÃö©ó¯¸°ÈªºÅv­­³£§ï¦¨ PERM_ALLXXXX
+ ¦b¦¹µe¤@­Ó¹Ï¨Ó´y­z¯¸°ÈÅv­­ªº³]©w¡C
+
+ ¢z µù¥UÁ`ºÞ PERM_REGISTRAR : ¥i¥H¼fµù¥U³æ¡C
+ ¢x
+ ¢u ±b¸¹Á`ºÞ PERM_ACCOUNTS : ¥i¥H­×§ïÅv­­¡B½ð½u¤W¨Ï¥ÎªÌ¡B¼fµù¥U³æ¡C
+ ¢x
+ ¯¸ªø PERM_SYSOP ¢q ²á¤Ñ«ÇÁ`ºÞ PERM_CHATROOM : ¦b²á¤Ñ«Ç¬O roomop¡C
+ ¢x
+ ¢u ¬ÝªOÁ`ºÞ PERM_BOARD : ¥i¥H­×§ï¬ÝªO³]©w¡B¶i¤J¯µ±K¤Î¦n¤Í¬ÝªO¡C
+ ¢x
+ ¢| ¥þÅ鯸°È PERM_ALLADMIN : ¥H¤W¥|­ÓÁ`ºÞ¡A³£¦³¥H¤U¥\¯à¡G
+ ¤W¯¸¨Ó·½³]©w¡BÁô¨­³N¡BµµÁô¡B¬Ý¨£«O±KªººëµØ°Ï¡B
+ ¤£¥²©w´Á»{ÃÒ¡BµL¶··s¤â¨£²ß¤T¤Ñ¡Bmulti-login¡B
+ ­×§ï¯¸¤W¤å¥ó¡B§ó·s¨t²Î¡B¤Þ¨¥¥i¥H¹L¦h¡B
+ ±H«Hµ¹¥þ¯¸¨Ï¥ÎªÌ¡B«H½cµL¤W­­¡C
+
+ ¯¸ªø PERM_SYSOP °£¤F¥H¤W©Ò¦³¥\¯à¡AÁÙ¾Ö¦³¥H¤U¥\¯à¡G
+ ºëµØ°Ï«Ø¸mµ·¸ô¤Î¸ê®Æ¡B¾\Ū©Ò¦³¤Hªº«H¥ó¡B±oª¾©Ò¦³¤H¦b¬Ý­þ­ÓªO¡B¶}±Ò¯¸°ÈÅv­­¡C
+
+#endif
+
+#define PERM_ALLADMIN (PERM_REGISTRAR | PERM_BOARD | PERM_ACCOUNTS | PERM_SYSOP) /* ¯¸°È */
+#define PERM_ALLREG (PERM_SYSOP | PERM_ACCOUNTS | PERM_REGISTRAR) /* ¼fµù¥U³æ */
+#define PERM_ALLACCT (PERM_SYSOP | PERM_ACCOUNTS) /* ±b¸¹ºÞ²z */
+#define PERM_ALLCHAT (PERM_SYSOP | PERM_CHATROOM) /* ²á¤ÑºÞ²z */
+#define PERM_ALLBOARD (PERM_SYSOP | PERM_BOARD) /* ¬ÝªOºÞ²z */
+
+#define PERM_ALLVALID (PERM_VALID | PERM_POST | PERM_PAGE | PERM_CHAT) /* »{ÃÒ³q¹L«áÀ³¦³ªº§¹¾ãÅv­­ */
+#define PERM_ALLDENY (PERM_DENYPOST | PERM_DENYTALK | PERM_DENYCHAT | PERM_DENYMAIL) /* ©Ò¦³°±Åv */
+
+#define PERM_LOCAL PERM_BASIC /* ¤£¬O guest ´N¯à±H«H¨ì¯¸¤º¨ä¥L¨Ï¥ÎªÌ */
+#define PERM_INTERNET PERM_VALID /* ¨­¤À»{ÃÒ¹LÃöªº¤~¯à±H«H¨ì Internet */
+
+/* #define HAS_PERM(x) ((x)?cuser.userlevel&(x):1) */
+/* #define HAVE_PERM(x) (cuser.userlevel&(x)) */
+/* itoc.001217: µ{¦¡¤¤¤£·|¦³ HAS_PERM(0) ªº¼gªk¡AHAVE_PERM ¤£·|¥Î¨ì */
+#define HAS_PERM(x) (cuser.userlevel&(x))
+
+
+/* ----------------------------------------------------- */
+/* ¦UºØÅv­­ªº¤¤¤å·N¸q */
+/* ----------------------------------------------------- */
+
+#define NUMPERMS 32
+
+#define STR_PERM "bctpjm#xecrs---@PTCM--L*B#ERACBS" /* itoc: ·s¼WÅv­­ªº®É­Ô§O§Ñ¤F§ï³o¸Ì°Ú */
+
+#ifdef _ADMIN_C_
+
+static char *perm_tbl[NUMPERMS] =
+{
+ "°ò¥»Åv¤O", /* PERM_BASIC */
+ "¶i¤J²á¤Ñ«Ç", /* PERM_CHAT */
+ "§ä¤H²á¤Ñ", /* PERM_PAGE */
+ "µoªí¤å³¹", /* PERM_POST */
+ "¨­¤À»{ÃÒ", /* PERM_VALID */
+ "«H¥óµL¤W­­", /* PERM_MBOX */
+ "Áô¨­³N", /* PERM_CLOAK */
+ "¥Ã¤[«O¯d±b¸¹", /* PERM_XEMPT */
+
+ "¥i¥H§ïID", /* PERM_CHANGEID */
+ "¦Ñ¤H", /* PERM_ELDER */
+ "¾÷¾¹", /* PERM_ROBOT */
+ "ªO¥D§ï±À¤å¼Æ", /* PERM_MSCORE */
+ "«O¯d", /* PERM_13 */
+ "«O¯d", /* PERM_14 */
+ "«O¯d", /* PERM_15 */
+ "«O¯d", /* PERM_16 */
+
+ "¸T¤îµoªí¤å³¹", /* PERM_DENYPOST */
+ "¸T¤î talk", /* PERM_DENYTALK */
+ "¸T¤î chat", /* PERM_DENYCHAT */
+ "¸T¤î mail", /* PERM_DENYMAIL */
+ "«O¯d", /* PERM_DENY5 */
+ "«O¯d", /* PERM_DENY6 */
+ "¸T¤î login", /* PERM_DENYLOGIN */
+ "²M°£±b¸¹", /* PERM_PURGE */
+
+ "ªO¥D", /* PERM_BM */
+ "¬Ý¨£§ÔªÌ", /* PERM_SEECLOAK */
+ "¥þ¯¸¼s¼½", /* PERM_CAST */
+ "µù¥UÁ`ºÞ", /* PERM_REGISTRAR */
+ "±b¸¹Á`ºÞ", /* PERM_ACCOUNTS */
+ "²á¤Ñ«ÇÁ`ºÞ", /* PERM_CHATCLOAK */
+ "¬ÝªOÁ`ºÞ", /* PERM_BOARD */
+ "¯¸ªø" /* PERM_SYSOP */
+};
+
+#endif
+#endif /* _PERM_H_ */
diff --git a/include/proto.h b/include/proto.h
new file mode 100644
index 0000000..d5c36da
--- /dev/null
+++ b/include/proto.h
@@ -0,0 +1,52 @@
+/*-------------------------------------------------------*/
+/* proto.h ( NTHU CS MapleBBS Ver 2.36 ) */
+/*-------------------------------------------------------*/
+/* target : prototype and macros */
+/* create : 95/03/29 */
+/* update : 95/12/15 */
+/*-------------------------------------------------------*/
+
+
+#ifndef _PROTO_H_
+#define _PROTO_H_
+
+
+/* ----------------------------------------------------- */
+/* External function declarations */
+/* ----------------------------------------------------- */
+
+/* OS */
+
+char *genpasswd();
+
+
+/* ----------------------------------------------------- */
+/* prototypes */
+/* ----------------------------------------------------- */
+
+
+#include "../maple/maple.p"
+
+
+/* ----------------------------------------------------- */
+/* macros */
+/* ----------------------------------------------------- */
+
+
+#define dashd(fpath) S_ISDIR(f_mode(fpath))
+#define dashf(fpath) S_ISREG(f_mode(fpath))
+
+
+#define STR4(x) ((x[0] << 24) + (x[1] << 16) + (x[2] << 8) + x[3])
+ /* Thor.980913: «OÃÒprecedence */
+
+#define IS_ZHC_LO is_zhc_low
+#define IS_ZHC_HI(x) (x & 0x80)
+
+#if 0
+#define rnd(x) (rand() % (x)) /* using lower-order bits */
+#endif
+
+#define rnd(x) ((int) (((x) + 0.0) * rand() / (RAND_MAX + 1.0))) /* using high-order bits */
+
+#endif /* _PROTO_H_ */
diff --git a/include/splay.h b/include/splay.h
new file mode 100644
index 0000000..f8c552b
--- /dev/null
+++ b/include/splay.h
@@ -0,0 +1,16 @@
+#ifndef _SPLAY_H_
+#define _SPLAY_H_
+
+
+typedef struct SplayNode
+{
+ void *data;
+ struct SplayNode *left;
+ struct SplayNode *right;
+} SplayNode;
+
+
+SplayNode *splay_in(SplayNode *top, void *data, int (*compare)());
+
+
+#endif /* _SPLAY_H_ */
diff --git a/include/struct.h b/include/struct.h
new file mode 100644
index 0000000..ea9cccb
--- /dev/null
+++ b/include/struct.h
@@ -0,0 +1,710 @@
+/*-------------------------------------------------------*/
+/* struct.h ( NTHU CS MapleBBS Ver 2.36 ) */
+/*-------------------------------------------------------*/
+/* target : all definitions about data structure */
+/* create : 95/03/29 */
+/* update : 95/12/15 */
+/*-------------------------------------------------------*/
+
+
+#ifndef _STRUCT_H_
+#define _STRUCT_H_
+
+
+/* screen control */
+
+#define STRLEN 80 /* Length of most string data */
+#define ANSILINELEN 250 /* Maximum Screen width in chars */
+
+/* itoc.031123: ¿Ã¹õªº¼e«×³]¬° 80 ¤]µL§«¡A¥u¬O¦³¨Ç telnet term ¦b¶K¤W¤å¦r¤Óªø®É¡A
+ ·|¦Û°ÊÂ_¦æ¦b 79¡A©Ò¥H¦b¦¹´N±q¨ä³]©w */
+#define SCR_WIDTH 79 /* edit/more/talk/visio screen width */
+
+#define TAB_STOP 4 /* «ö TAB ´«¦¨´X®æªÅ¥Õ (­n¬O 2 ªº¦¸¤è) */
+#define TAB_WIDTH (TAB_STOP - 1)
+
+#define T_LINES 50 /* maximum total lines */
+#define T_COLS 120 /* maximum total columns¡A­n¤ñ ANSILINELEN ¤p */
+
+/* board structure */
+
+#define BNLEN 12 /* Length of board name */
+#define BCLEN 4 /* Length of board class */
+#define BTLEN 43 /* Length of board title */
+#define BMLEN 36 /* Length of board managers */
+
+/* file structure */
+
+#define TTLEN 72 /* Length of title */
+#define FNLEN 28 /* Length of filename */
+
+/* user structure */
+/* DES ½s½Xªk¡A±µ¨ü 8 ­Ó¦r¤¸ªø«×ªº¦r¦ê¡A²£¥ÍªºÁä­È¬° 13 ¦ì¤¸²Õ */
+
+#define IDLEN 12 /* Length of user id */
+#define PASSLEN 13 /* Length of encrypted passwd field */
+#define PSWDLEN 8 /* Length of passwd (¥¼¥[±Kªºªø«×) */
+#define RNLEN 19 /* Length of real name */
+#define UNLEN 23 /* Length of user name */
+
+
+typedef char const *STRING;
+typedef unsigned char uschar; /* length = 1 */
+typedef unsigned int usint; /* length = 4 */
+typedef struct UTMP UTMP;
+
+
+/* ----------------------------------------------------- */
+/* ¨Ï¥ÎªÌ±b¸¹ .ACCT struct : 512 bytes */
+/* ----------------------------------------------------- */
+
+
+typedef struct
+{
+ int userno; /* unique positive code */
+
+ char userid[IDLEN + 1]; /* ID */
+ char passwd[PASSLEN + 1]; /* ±K½X */
+ char realname[RNLEN + 1]; /* ¯u¹ê©m¦W */
+ char username[UNLEN + 1]; /* ¼ÊºÙ */
+
+ usint userlevel; /* Åv­­ */
+ usint ufo; /* habit */
+ uschar signature; /* ¹w³]ñ¦WÀÉ */
+
+ unsigned char year; /* ¥Í¤é(¥Á°ê¦~) */
+ unsigned char month; /* ¥Í¤é(¤ë) */
+ unsigned char day; /* ¥Í¤é(¤é) */
+ char sex; /* ©Ê§O 0:¤¤©Ê ©_¼Æ:¨k©Ê °¸¼Æ:¤k©Ê */
+ int money; /* »È¹ô */
+ int gold; /* ª÷¹ô */
+
+ int numlogins; /* ¤W¯¸¦¸¼Æ */
+ int numposts; /* µoªí¦¸¼Æ */
+ int numemails; /* ±Hµo Inetrnet E-mail ¦¸¼Æ */
+
+ time_t firstlogin; /* ²Ä¤@¦¸¤W¯¸®É¶¡ */
+ time_t lastlogin; /* ¤W¤@¦¸¤W¯¸®É¶¡ */
+ time_t tcheck; /* ¤W¦¸ check «H½c/ªB¤Í¦W³æªº®É¶¡ */
+ time_t tvalid; /* ­Y°±Åv¡A°±Åv´Áº¡ªº®É¶¡¡F
+ ­Y¥¼°±Åv¥B³q¹L»{ÃÒ¡A³q¹L»{ÃÒªº®É¶¡¡F
+ ­Y¥¼°±Åv¥B¥¼³q¹L»{ÃÒ¡A»{ÃҨ窺 time-seed */
+
+ char lasthost[30]; /* ¤W¦¸µn¤J¨Ó·½ */
+ char email[60]; /* ¥Ø«eµn°Oªº¹q¤l«H½c */
+} ACCT;
+
+
+typedef struct /* 16 bytes */
+{
+ time_t uptime;
+ char userid[IDLEN];
+} SCHEMA;
+
+
+#ifdef HAVE_REGISTER_FORM
+
+/* itoc.041025: RFROM ¤£À³¥X²{©M ACCT ¤@¼Ë¦ý­È¥i¯à¤£¦PªºÄæ¦ì
+ RFORM ©M ACCT °ß¤G¬Û¦PªºÄæ¦ì¬O userno¡Buserid */
+
+typedef struct /* µù¥Uªí³æ (Register From) 256 bytes */
+{
+ int userno;
+ time_t rtime;
+ char userid[IDLEN + 1];
+ char agent[IDLEN + 1];
+ char nouse[20];
+ char career[50];
+ char address[60];
+ char phone[20];
+ char reply[72];
+} RFORM;
+
+#endif
+
+
+#include "hdr.h"
+
+
+/* ----------------------------------------------------- */
+/* control of board vote : 256 bytes */
+/* ----------------------------------------------------- */
+
+
+/* itoc.041101.µù¸Ñ: VCH ©M HDR ªº xname ¦ì¸m¡Bªø«×­n¤Ç°t */
+
+typedef struct VoteControlHeader
+{
+ time_t chrono; /* §ë²¼¶}¿ì®É¶¡ */ /* Thor: ¬° key ¦Ó¥B match HDR chrono */
+ time_t bstamp; /* ¬ÝªO¿ëÃÑ¥N½X */ /* Thor: ¬° key */
+ time_t vclose; /* §ë²¼µ²§ô®É¶¡ */
+
+ char xname[32]; /* ¥DÀɦW */ /* Thor: match HDR ªº xname */
+ char date[9]; /* ¶}©l¤é´Á */ /* Thor: match HDR ªº date */
+ char cdate[9]; /* µ²§ô¤é´Á */ /* Thor: ¥u¨ÑÅã¥Ü¡A¤£°µ¤ñ¸û */
+ char owner[IDLEN + 1]; /* Á|¿ì¤H */
+ char title[TTLEN + 1]; /* §ë²¼¥DÃD */
+
+ char vgamble; /* ¬O§_¬°½ä½L '$':½ä½L ' ':¤@¯ë§ë²¼ */
+ char vsort; /* ¶}²¼µ²ªG¬O§_±Æ§Ç 's':±Æ§Ç ' ':¤£±Æ§Ç */
+ char vpercent; /* ¬O§_Åã¥Ü¦Ê¤À¤ñ¨Ò '%':¦Ê¤À ' ':¤@¯ë */
+ char vprivate; /* ¬O§_¬°¨p¤H§ë²¼ ')':¨p¤H ' ':¤½¶} */
+
+ int maxblt; /* ¨C¤H¥i§ë´X²¼ */
+ int price; /* ¨C±i½ä²¼ªº°â»ù */
+
+ char nouse[96];
+} VCH;
+
+
+typedef char vitem_t[32]; /* §ë²¼¿ï¶µ */
+
+
+typedef struct
+{
+ char userid[IDLEN + 1];
+ char numvotes; /* §ë´X±i */
+ usint choice;
+} VLOG;
+
+
+/* filepath : brd/<board>/.VCH, brd/<board>/@/... */
+
+
+/* ----------------------------------------------------- */
+/* Mail-Queue struct : 256 bytes */
+/* ----------------------------------------------------- */
+
+
+typedef struct
+{
+ time_t mailtime; /* ±H«H®É¶¡ */
+ char method;
+ char sender[IDLEN + 1];
+ char username[UNLEN + 1];
+ char subject[TTLEN + 1];
+ char rcpt[60];
+ char filepath[77];
+ char *niamod; /* reverse domain */
+} MailQueue;
+
+
+#define MQ_JUSTIFY 0x01 /* ¨­¤À»{ÃÒ«H¨ç */
+#define MQ_ATTACH 0x02 /* ¦³ attachment ªº«H¨ç */
+
+
+/* ----------------------------------------------------- */
+/* PAL : friend struct : 64 bytes */
+/* ----------------------------------------------------- */
+
+
+typedef struct
+{
+ char userid[IDLEN + 1];
+ char ftype;
+ char ship[46];
+ int userno;
+} PAL;
+
+#define PAL_BAD 0x02 /* ¦n¤Í vs Ãa¤H */
+
+
+/* ----------------------------------------------------- */
+/* structure for call-in message : 100 bytes */
+/* ----------------------------------------------------- */
+
+
+typedef struct
+{
+ time_t btime;
+ UTMP *caller; /* who call-in me ? */
+ int sender; /* calling userno */
+ int recver; /* called userno */
+ char userid[IDLEN + 1 + 2]; /* itoc.010529: «O¯d 2 byte µ¹¼s¼½²Å¸¹ > */
+ char msg[69]; /* ¤ô²y */
+} BMW; /* bbs message write */
+
+
+#ifdef LOGIN_NOTIFY
+
+/* ----------------------------------------------------- */
+/* BENZ : ¨t²Î¨ó´M¦W³æ : 20 bytes */
+/* ----------------------------------------------------- */
+
+
+typedef struct
+{
+ int userno;
+ char userid[IDLEN + 1];
+} BENZ;
+
+#endif /* LOGIN_NOTIFY */
+
+
+#ifdef HAVE_ALOHA
+
+/* ----------------------------------------------------- */
+/* ALOHA : ¤W¯¸³qª¾¦W³æ : 20 bytes */
+/* ----------------------------------------------------- */
+
+
+typedef struct
+{
+ char userid[IDLEN + 1];
+ int userno;
+} ALOHA;
+
+
+/* ----------------------------------------------------- */
+/* FRIENZ : ¤W¯¸³qª¾¦W³æ : 100 bytes */
+/* ----------------------------------------------------- */
+
+
+/* itoc.041011.µù¸Ñ: ®Ú¥»¨S¥²­n¥Î³o»ò¤j */
+
+typedef struct
+{
+ int nouse1;
+ int nouse2;
+ int nouse3;
+ int userno;
+ char userid[IDLEN + 1];
+ char nouse4[71];
+} FRIENZ;
+
+#endif /* HAVE_ALOHA */
+
+
+/* ----------------------------------------------------- */
+/* PAYCHECK : ¤ä²¼ : 32 bytes */
+/* ----------------------------------------------------- */
+
+
+typedef struct
+{
+ time_t tissue; /* µo¤ä²¼®É¶¡ */
+ int money;
+ int gold;
+ char reason[20];
+} PAYCHECK;
+
+
+/* ----------------------------------------------------- */
+/* Structure used in UTMP file : 128 bytes */
+/* lantw44: ¥[¤J realid ©Ò¥H¼W¬° 141 bytes */
+/* ----------------------------------------------------- */
+
+
+struct UTMP
+{
+ pid_t pid; /* process ID */
+ int userno; /* user number */
+
+ int mode; /* bbsmode */
+ usint userlevel; /* the save as ACCT.userlevel */
+ usint ufo; /* the same as ACCT.ufo */
+ usint status; /* status */
+
+ time_t idle_time; /* active time for last event */
+ u_long in_addr; /* Internet address */
+ int sockport; /* socket port for talk */
+ UTMP *talker; /* who talk-to me ? */
+
+ BMW *mslot[BMW_PER_USER];
+
+ char userid[IDLEN + 1]; /* user's ID */
+ char realid[IDLEN + 1]; /* user's real ID */
+ char mateid[IDLEN + 1]; /* partner's ID */
+ char username[UNLEN + 1]; /* user's nickname */
+ char from[34]; /* remote host */
+#ifdef HAVE_BRDMATE
+ char reading[BNLEN + 1]; /* reading board */
+#endif
+
+ int pal_max; /* ¦³´X­ÓªB¤Í */
+ int pal_spool[PAL_MAX]; /* ©Ò¦³ªB¤Íªº userno */
+
+#ifdef BMW_COUNT
+ int bmw_count; /* °O¿ý¤¤¤F´X­Ó¤ô²y */
+#endif
+};
+
+
+/* ----------------------------------------------------- */
+/* BOARDS struct : 128 bytes */
+/* ----------------------------------------------------- */
+
+
+typedef struct BoardHeader
+{
+ char brdname[BNLEN + 1]; /* board name */
+ char class[BCLEN + 1];
+ char title[BTLEN + 1];
+ char BM[BMLEN + 1]; /* BMs' uid, token '/' */
+
+ char bvote; /* 0:µL§ë²¼ -1:¦³½ä½L(¥i¯à¦³§ë²¼) 1:¦³§ë²¼ */
+
+ time_t bstamp; /* «Ø¥ß¬ÝªOªº®É¶¡, unique */
+ usint readlevel; /* ¾\ۤ峹ªºÅv­­ */
+ usint postlevel; /* µoªí¤å³¹ªºÅv­­ */
+ usint battr; /* ¬ÝªOÄÝ©Ê */
+ time_t btime; /* -1:bpost/blast »Ý­n§ó·s */
+ int bpost; /* ¦@¦³´X½g post */
+ time_t blast; /* ³Ì«á¤@½g post ªº®É¶¡ */
+} BRD;
+
+
+typedef struct
+{
+ int pal_max; /* ¦³´X­ÓªO¤Í */
+ int pal_spool[PAL_MAX]; /* ©Ò¦³ªO¤Íªº userno */
+} BPAL;
+
+
+/* ----------------------------------------------------- */
+/* Class image */
+/* ----------------------------------------------------- */
+
+
+#define CLASS_INIFILE "Class"
+
+/* itoc.010413: §â class.img ¤À¦¨¤G­Ó */
+#define CLASS_IMGFILE_NAME "run/classname.img"
+#define CLASS_IMGFILE_TITLE "run/classtitle.img"
+
+
+#define CH_MAX 100 /* ³Ì¤j¤ÀÃþ¼Æ¥Ø */
+#define CH_END -1
+#define CH_TTLEN 64
+
+
+typedef struct
+{
+ int count;
+ char title[CH_TTLEN];
+ short chno[0];
+} ClassHeader;
+
+
+#ifdef MY_FAVORITE
+
+/* ----------------------------------------------------- */
+/* favor.c ¤¤¹B¥Îªº¸ê®Æµ²ºc */
+/* ----------------------------------------------------- */
+
+
+typedef struct MF
+{
+ time_t chrono; /* «Ø¥ß®É¶¡ */
+ int mftype; /* type */
+ char xname[BNLEN + 1]; /* ªO¦W©ÎÀɦW */
+ char class[BCLEN + 1]; /* ¤ÀÃþ */
+ char title[BTLEN + 1]; /* ¥DÃD */
+} MF;
+
+#define MF_MARK 0x01 /* ³Q mark °_¨Óªº */
+#define MF_BOARD 0x02 /* ¬ÝªO±¶®| */
+#define MF_FOLDER 0x04 /* ¨÷©v */
+#define MF_GEM 0x08 /* ºëµØ°Ï±¶®| */
+#define MF_LINE 0x10 /* ¤À¹j½u */
+
+#endif /* MY_FAVORITE */
+
+
+#ifdef HAVE_COSIGN
+
+/* ----------------------------------------------------- */
+/* newbrd.c ¤¤¹B¥Îªº¸ê®Æµ²ºc */
+/* ----------------------------------------------------- */
+
+
+typedef struct NewBoardHeader
+{
+ char brdname[BNLEN + 1];
+ char class[BCLEN + 1];
+ char title[BTLEN + 1];
+ time_t btime;
+ time_t etime;
+ char xname[32];
+ char owner[IDLEN + 1];
+ char date[9];
+ usint mode;
+ int total;
+} NBRD;
+
+
+#define NBRD_FINISH 0x00001 /* ¤wµ²®× */
+#define NBRD_END 0x00002 /* ³s¸pµ²§ô */
+#define NBRD_START 0x00004 /* ³s¸p¶}©l */
+#define NBRD_ANONYMOUS 0x00100 /* °Î¦W */
+#define NBRD_NEWBOARD 0x10000 /* ·sªO³s¸p */
+#define NBRD_OTHER 0x20000 /* ¨ä¥L³s¸p */
+
+#endif /* HAVE_COSIGN */
+
+
+#ifdef LOG_SONG_USIES
+
+/* ----------------------------------------------------- */
+/* SONG log ¤¤¹B¥Îªº¸ê®Æµ²ºc */
+/* ----------------------------------------------------- */
+
+typedef struct SONGDATA
+{
+ time_t chrono;
+ int count; /* ³QÂI¦¸¼Æ */
+ char title[80];
+} SONGDATA;
+
+#endif /* LOG_SONG_USIES */
+
+
+/* ----------------------------------------------------- */
+/* cache.c ¤¤¹B¥Îªº¸ê®Æµ²ºc */
+/* ----------------------------------------------------- */
+
+
+typedef struct
+{
+ int shot[MOVIE_MAX]; /* Thor.980805: ¦X²z½d³ò¬° 0..MOVIE_MAX - 1 */
+ char film[MOVIE_SIZE];
+ char today[16];
+} FCACHE;
+
+
+#define FILM_SIZ 4000 /* max size for each film */
+
+
+#define FILM_OPENING0 0 /* ¶}ÀYµe­±(¢¯) */
+#define FILM_OPENING1 1 /* ¶}ÀYµe­±(¢°) */
+#define FILM_OPENING2 2 /* ¶}ÀYµe­±(¢±) */
+#define FILM_GOODBYE 3 /* ¦A¨£µe­± */
+#define FILM_NOTIFY 4 /* ©|¥¼³q¹L»{ÃÒ³qª¾ */
+#define FILM_MQUOTA 5 /* «H¥ó¶W¹L«O¦s´Á­­³qª¾ */
+#define FILM_MAILOVER 6 /* «H¥ó«Ê¼Æ¹L¦h³qª¾ */
+#define FILM_MGEMOVER 7 /* ­Ó¤HºëµØ°Ï¹L¦h³qª¾ */
+#define FILM_BIRTHDAY 8 /* ¥Í¤é·í¤Ñªº¤W¯¸µe­± */
+#define FILM_APPLY 9 /* µù¥U´£¥Üµe­± */
+#define FILM_JUSTIFY 10 /* ¨­¥÷»{ÃÒªº¤èªk */
+#define FILM_REREG 11 /* ­«·s»{ÃÒ»¡©ú */
+#define FILM_EMAIL 12 /* ¶l¥ó«H½c»{ÃÒ»¡©ú */
+#define FILM_NEWUSER 13 /* ·s¤â¤W¸ô¶·ª¾ */
+#define FILM_TRYOUT 14 /* ±K½X¿ù»~ */
+#define FILM_POST 15 /* ¤å³¹µoªíºõ»â */
+#define FILM_MOVIE 16 /* °ÊºA¬ÝªO FILM_MOVIE ­n©ñ¦b³Ì«á­± */
+
+
+typedef struct
+{
+ UTMP uslot[MAXACTIVE]; /* UTMP slots */
+ usint count; /* number of active session */
+ usint offset; /* offset for last active UTMP */
+
+ double sysload[3];
+ int avgload;
+
+ BMW *mbase; /* sequential pointer for BMW */
+ BMW mpool[BMW_MAX];
+} UCACHE;
+
+
+typedef struct
+{
+ BRD bcache[MAXBOARD];
+ BPAL pcache[MAXBOARD];
+ int mantime[MAXBOARD]; /* ¦UªO¥Ø«e¥¿¦³¦h¤Ö¤H¦b¾\Ū */
+ int number; /* ¥þ³¡¬ÝªOªº¼Æ¥Ø */
+ int numberOld; /* ­è¶}¯¸®É¬ÝªOªº¼Æ¥Ø */
+ int min_chn; /* °O¿ýÁ`¦@¦³´X­Ó¤ÀÃþ */
+ time_t uptime;
+} BCACHE;
+
+
+/* ----------------------------------------------------- */
+/* visio.c ¤¤¹B¥Îªº¸ê®Æµ²ºc */
+/* ----------------------------------------------------- */
+
+
+/* Screen Line buffer modes */
+
+
+#define SL_MODIFIED (1) /* if line has been modifed, screen output */
+#define SL_STANDOUT (2) /* if this line contains standout code */
+#define SL_ANSICODE (4) /* if this line contains ANSI code */
+
+
+typedef struct screenline
+{
+ uschar oldlen; /* previous line length */
+ uschar len; /* current length of line */
+ uschar width; /* padding length of ANSI codes */
+ uschar smod; /* start of modified data */
+ uschar emod; /* end of modified data */
+ uschar sso; /* start of standout data */
+ uschar eso; /* end of standout data */
+ uschar mode; /* status of line, as far as update */
+ uschar data[ANSILINELEN];
+} screenline;
+
+
+typedef struct LinkList
+{
+ struct LinkList *next;
+ char data[0];
+} LinkList;
+
+
+/* ----------------------------------------------------- */
+/* xover.c ¤¤¹B¥Îªº¸ê®Æµ²ºc */
+/* ----------------------------------------------------- */
+
+
+typedef struct OverView
+{
+ int pos; /* current position */
+ int top; /* top */
+ int max; /* max */
+ int key; /* key */
+ char *xyz; /* staff */
+ struct OverView *nxt; /* next */
+ char dir[0]; /* data path */
+} XO;
+
+
+typedef struct
+{
+ int key;
+ int (*func) ();
+} KeyFunc;
+
+
+typedef struct
+{
+ XO *xo;
+ KeyFunc *cb;
+ int mode;
+ char *feeter;
+} XZ;
+
+
+typedef struct
+{
+ time_t chrono;
+ int recno;
+} TagItem;
+
+
+/* ----------------------------------------------------- */
+/* poststat.c ¤¤¹B¥Îªº¸ê®Æµ²ºc */
+/* ----------------------------------------------------- */
+
+
+typedef struct
+{
+ char author[IDLEN + 1];
+ char board[BNLEN + 1];
+ char title[66];
+ time_t date; /* last post's date */
+ int number; /* post number */
+} POSTLOG;
+
+
+#ifdef MODE_STAT
+
+/* ----------------------------------------------------- */
+/* modestat.c ¤¤¹B¥Îªº¸ê®Æµ²ºc */
+/* ----------------------------------------------------- */
+
+typedef struct
+{
+ time_t logtime;
+ time_t used_time[M_MAX + 1]; /* itoc.010901: depend on mode.h */
+} UMODELOG;
+
+
+typedef struct
+{
+ time_t logtime;
+ time_t used_time[M_MAX + 1]; /* itoc.010901: depend on mode.h */
+ int count[30];
+ int usercount;
+} MODELOG;
+
+#endif /* MODE_STAT */
+
+
+/* ----------------------------------------------------- */
+/* innbbsd ¤¤¹B¥Îªº¸ê®Æµ²ºc */
+/* ----------------------------------------------------- */
+
+
+typedef struct
+{
+ time_t chrono; /* >=0:stamp -1:cancel */
+ char board[BNLEN + 1];
+
+ /* ¥H¤UÄæ¦ìªº¤j¤p»P HDR ¬Û¦P */
+ char xname[32];
+ char owner[80];
+ char nick[49];
+ char title[TTLEN + 1];
+} bntp_t;
+
+
+#define INN_USEIHAVE 0x0001
+#define INN_USEPOST 0x0002
+#define INN_FEEDED 0x0010
+
+typedef struct
+{
+ char name[13]; /* ¸Ó¯¸ªº short-name */
+ char host[83]; /* ¸Ó¯¸ªº host */
+ int port; /* ¸Ó¯¸ªº port */
+ usint xmode; /* ¸Ó¯¸ªº xmode */
+ char blank[20]; /* «O¯d */
+ int feedfd; /* bbslink.c ¨Ï¥Î */
+} nodelist_t;
+
+
+#define INN_NOINCOME 0x0001
+#define INN_ERROR 0x0004
+
+typedef struct
+{
+ char path[13]; /* ¸Ó¸s²Õ©Ò¹ïÂ઺¯¸¥x */
+ char newsgroup[83]; /* ¸Ó¸s²Õªº¦WºÙ */
+ char board[BNLEN + 1];/* ¸Ó¸s²Õ©Ò¹ïÀ³ªº¬ÝªO */
+ char charset[11]; /* ¸Ó¸s²Õªº¦r¶° */
+ usint xmode; /* ¸Ó¸s²Õªº xmode */
+ int high; /* ¥Ø«e§ì¨ì¸Ó¸s²Õªº­þ¤@½g */
+} newsfeeds_t;
+
+
+typedef struct
+{
+ char issuer[128]; /* NCM message ªºµo«H¤H */
+ char type[64]; /* NCM message ªººØÃþ¦WºÙ */
+ int perm; /* ¤¹³\¦¹ NCM message §R«H (1:¶} 0:Ãö) */
+ char blank[60]; /* «O¯d */
+} ncmperm_t;
+
+
+#define INN_SPAMADDR 0x0001
+#define INN_SPAMNICK 0x0002
+#define INN_SPAMSUBJECT 0x0010
+#define INN_SPAMPATH 0x0020
+#define INN_SPAMMSGID 0x0040
+#define INN_SPAMBODY 0x0100
+#define INN_SPAMSITE 0x0200
+#define INN_SPAMPOSTHOST 0x0400
+
+typedef struct
+{
+ char detail[80]; /* ¸Ó³W«hªº ¤º®e */
+ usint xmode; /* ¸Ó³W«hªº xmode */
+ char board[BNLEN + 1];/* ¸Ó³W«h¾A¥Îªº¬ÝªO */
+ char path[13]; /* ¸Ó³W«h¾A¥Îªº¯¸¥x */
+ char blank[18]; /* «O¯d */
+} spamrule_t;
+
+#endif /* _STRUCT_H_ */
diff --git a/include/theme.h b/include/theme.h
new file mode 100644
index 0000000..241597c
--- /dev/null
+++ b/include/theme.h
@@ -0,0 +1,257 @@
+/*-------------------------------------------------------*/
+/* theme.h ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : custom theme */
+/* create : 02/08/17 */
+/* update : / / */
+/*-------------------------------------------------------*/
+
+
+#ifndef _THEME_H_
+#define _THEME_H_
+
+
+/* ----------------------------------------------------- */
+/* °ò¥»ÃC¦â©w¸q¡A¥H§Q¤¶­±­×§ï */
+/* ----------------------------------------------------- */
+
+#define COLOR1 "\033[1;37;43m" /* footer/feeter ªº«e¬qÃC¦â */
+#define COLOR2 "\033[33;42m" /* footer/feeter ªº«á¬qÃC¦â */
+#define COLOR3 "\033[1;37;42m" /* neck ªºÃC¦â */
+#define COLOR4 "\033[1;33;42m" /* ¥ú´Î ªºÃC¦â */
+#define COLOR5 "\033[32;47m" /* more ÀÉÀYªº¼ÐÃDÃC¦â */
+#define COLOR6 "\033[37;42m" /* more ÀÉÀYªº¤º®eÃC¦â */
+#define COLOR7 "\033[32m" /* §@ªÌ¦b½u¤WªºÃC¦â */
+
+/* ----------------------------------------------------- */
+/* ¨Ï¥ÎªÌ¦W³æÃC¦â */
+/* ----------------------------------------------------- */
+
+#define COLOR_NORMAL "" /* ¤@¯ë¨Ï¥ÎªÌ */
+#define COLOR_MYBAD "\033[1;31m" /* Ãa¤H */
+#define COLOR_MYGOOD "\033[1;32m" /* §Úªº¦n¤Í */
+#define COLOR_OGOOD "\033[1;33m" /* »P§Ú¬°¤Í */
+#define COLOR_CLOAK "\033[1;35m" /* Áô§Î */ /* itoc.µù¸Ñ: ¨S¥Î¨ì¡A­nªº¤H½Ð¦Û¦æ¥[¤J ulist_body() */
+#define COLOR_SELF "\033[1;36m" /* ¦Û¤v */
+#define COLOR_BOTHGOOD "\033[1;37m" /* ¤¬³]¦n¤Í */
+#define COLOR_BRDMATE "\033[36m" /* ªO¦ñ */
+
+
+/* ----------------------------------------------------- */
+/* ¿ï³æ¦ì¸m */
+/* ----------------------------------------------------- */
+
+/* itoc.µù¸Ñ: ª`·N MENU_XPOS ­n >= MENU_XNOTE + MOVIE_LINES */
+
+#define MENU_XNOTE 1 /* °ÊºA¬ÝªO¥Ñ (2, 0) ¶}©l */
+#define MOVIE_LINES 12 /* °Êµe³Ì¦h¦³ 10 ¦C */
+
+#define MENU_XPOS 13 /* ¿ï³æ¶}©lªº (x, y) ®y¼Ð */
+#define MENU_YPOS ((d_cols >> 1) + 18)
+
+
+/* ----------------------------------------------------- */
+/* °T®§¦r¦ê¡G*_neck() ®Éªº necker ³£§ì¥X¨Ó©w¸q¦b³o */
+/* ----------------------------------------------------- */
+
+/* necker ªº¦æ¼Æ³£¬O¤G¦æ¡A±q (1, 0) ¨ì (2, 80) */
+
+/* ©Ò¦³ªº XZ_* ³£¦³ necker¡A¥u¬O¦³¨Ç¦b *_neck()¡A¦³¨ÇÂæb *_head() */
+
+/* ulist_neck() ¤Î xpost_head() ªº²Ä¤@¦æ¤ñ¸û¯S§O¡A¤£¦b¦¹©w¸q */
+
+#define NECKER_CLASS "[¡ö]¥D¿ï³æ [¡÷]¾\\Ū [¡ô¡õ]¿ï¾Ü [W]¬d¸ß [y]¸ü¤J [/?]·j´M [s]¬ÝªO [h]»¡©ú\n" \
+ COLOR3 " %s ¬Ý ªO Ãþ§OÂà«H ¤¤ ¤å ±Ô ­z%*s ¤H ªO ¥D%*s \033[m"
+
+#define NECKER_ULIST "\n" \
+ COLOR3 " ½s¸¹ ¥N¸¹ ¼ÊºÙ%*s %-*s °ÊºA ¶¢¸m \033[m"
+
+#define NECKER_PAL "[¡ö]Â÷¶} [a]·s¼W [c]­×§ï [d]§R°£ [m]±H«H [w]¤ô²y [s]¾ã²z [¡÷]¬d¸ß [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¥N ¸¹ ¤Í ½Ë%*s \033[m"
+
+#define NECKER_ALOHA "[¡ö]Â÷¶} [a]·s¼W [d]§R°£ [D]°Ï¬q§R°£ [m]±H«H [w]¤ô²y [s]­«¾ã [f]¤Þ¤J [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¤W ¯¸ ³q ª¾ ¦W ³æ%*s \033[m"
+
+#define NECKER_VOTE "[¡ö]Â÷¶} [R]µ²ªG [^P]Á|¦æ [E]­×§ï [V]¹wÄý [^Q]§ï´Á [o]¦W³æ [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¶}²¼¤é ¥D¿ì¤H §ë ²¼ ©v ¦®%*s \033[m"
+
+#define NECKER_BMW "[¡ö]Â÷¶} [d]§R°£ [D]°Ï¬q§R°£ [m]±H«H [M]Àx¦s [w]¤ô²y [s]§ó·s [¡÷]¬d¸ß [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¥N ¸¹ ¤º ®e%*s ®É¶¡ \033[m"
+
+#define NECKER_MF "[¡ö]Â÷¶} [¡÷]¶i¤J [W]¬d¸ß [m]²¾°Ê [h]»¡©ú\n" \
+ COLOR3 " %s ¬Ý ªO Ãþ§OÂà«H ¤¤ ¤å ±Ô ­z%*s ¤H ªO ¥D%*s \033[m"
+
+#define NECKER_COSIGN "[¡ö]Â÷¶} [¡÷]¾\\Ū [^P]¥Ó½Ð [d]§R°£ [o]¶}ªO [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á Á|¿ì¤H ¬Ý ªO ¼Ð ÃD%*s \033[m"
+
+#define NECKER_SONG "[¡ö]Â÷¶} [¡÷]ÂsÄý [o]ÂIºq¨ì¬ÝªO [m]ÂIºq¨ì«H½c [Enter]ÂsÄý [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¥D ÃD%*s [½s ¿ï] [¤é ´Á]\033[m"
+
+#define NECKER_NEWS "[¡ö]Â÷¶} [¡÷]¾\\Ū [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á §@ ªÌ ·s »D ¼Ð ÃD%*s \033[m"
+
+#define NECKER_XPOST "\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á §@ ªÌ ¤å ³¹ ¼Ð ÃD%*s £t:%s µû:%s \033[m"
+
+#define NECKER_MBOX "[¡ö]Â÷¶} [¡÷,r]Ū«H [d]§R°£ [R,y](¸s²Õ)¦^«H [s]±H«H [x]Âà¿ý [X]Âà¹F [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á §@ ªÌ «H ¥ó ¼Ð ÃD%*s \033[m"
+
+#define NECKER_POST "[¡ö]Â÷¶} [¡÷]¾\\Ū [^P]µoªí [b]¶iªOµe­± [d]§R°£ [I,W]¬d¸ß [TAB]ºëµØ°Ï [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á §@ ªÌ ¤å ³¹ ¼Ð ÃD%*s £t:%s µû:%s ¤H®ð:%-4d \033[m"
+
+#define NECKER_GEM "[¡ö]Â÷¶} [¡÷]ÂsÄý [B]¼Ò¦¡ [C]¼È¦s [F]Âà±H [d]§R°£ [h]»¡©ú %s\n" \
+ COLOR3 " ½s¸¹ ¥D ÃD%*s [½s ¿ï] [¤é ´Á]\033[m"
+
+/* ¥H¤U³o¨Ç«h¬O¤@¨ÇÃþ XZ_* µ²ºcªº necker */
+
+#define NECKER_VOTEALL "[¡ô/¡õ]¤W¤U [PgUp/PgDn]¤W¤U­¶ [Home/End]­º§À [¡÷]§ë²¼ [¡ö][q]Â÷¶}\n" \
+ COLOR3 " ½s¸¹ ¬Ý ªO Ãþ§OÂà«H ¤¤ ¤å ±Ô ­z%*s ªO ¥D%*s \033[m"
+
+#define NECKER_CREDIT "[¡ö]Â÷¶} [C]´«­¶ [1]·s¼W [2]§R°£ [3]¥þ§R [4]Á`­p\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á ¦¬¤ä ª÷ ÃB ¤ÀÃþ »¡ ©ú%*s \033[m"
+
+#define NECKER_HELP "[¡ö]Â÷¶} [¡÷]¾\\Ū [^P]·s¼W [d]§R°£ [T]¼ÐÃD [E]½s¿è [m]²¾°Ê\n" \
+ COLOR3 " ½s¸¹ ÀÉ ®× ¼Ð ÃD%*s \033[m"
+
+#define NECKER_INNBBS "[¡ö]Â÷¶} [^P]·s¼W [d]§R°£ [E]½s¿è [/]·j´M [Enter]¸Ô²Ó\n" \
+ COLOR3 " ½s¸¹ ¤º ®e%*s \033[m"
+
+
+/* ----------------------------------------------------- */
+/* °T®§¦r¦ê¡Gmore() ®Éªº footer ³£§ì¥X¨Ó©w¸q¦b³o */
+/* ----------------------------------------------------- */
+
+/* itoc.010914.µù¸Ñ: ³æ¤@½g¡A©Ò¥H¥s FOOTER¡A³£¬O 78 char */
+
+/* itoc.010821: ª`·N \\ ¬O \¡A³Ì«á§Oº|¤F¤@­ÓªÅ¥ÕÁä :p */
+
+#define FOOTER_POST \
+COLOR1 " ¤å³¹¿ïŪ " COLOR2 " (ry)¦^À³ (=\\[]<>-+;'`)¥DÃD (|?QA)·j´M¼ÐÃD§@ªÌ (kj)¤W¤U½g (C)¼È¦s "
+
+#define FOOTER_MAILER \
+COLOR1 " ³½¶­©¹ªð " COLOR2 " (ry)¦^«H/¸s²Õ (X)Âà¹F (d)§R°£ (m)¼Ð°O (C)¼È¦s (=\\[]<>-+;'`|?QAkj) "
+
+#define FOOTER_GEM \
+COLOR1 " ºëµØ¿ïŪ " COLOR2 " (=\\[]<>-+;'`)¥DÃD (|?QA)·j´M¼ÐÃD§@ªÌ (kj)¤W¤U½g (¡ô¡õ¡ö)¤W¤UÂ÷¶} "
+
+#ifdef HAVE_GAME
+#define FOOTER_TALK \
+COLOR1 " ¥æ½Í¼Ò¦¡ " COLOR2 " (^O)¹ï«³¼Ò¦¡ (^C,^D)µ²§ô¥æ½Í (^T)¤Á´«©I¥s¾¹ (^Z)§Ö±¶¦Cªí (^G)¹Í¹Í "
+#else
+#define FOOTER_TALK \
+COLOR1 " ¥æ½Í¼Ò¦¡ " COLOR2 " (^C,^D)µ²§ô¥æ½Í (^T)¤Á´«©I¥s¾¹ (^Z)§Ö±¶¦Cªí (^G)¹Í¹Í (^Y)²M°£ "
+#endif
+
+#define FOOTER_COSIGN \
+COLOR1 " ³s¸p¾÷¨î " COLOR2 " (ry)¥[¤J³s¸p (kj)¤W¤U½g (¡ô¡õ¡ö)¤W¤UÂ÷¶} (h)»¡©ú "
+
+#define FOOTER_MORE \
+COLOR1 " ÂsÄý P.%d (%d%%) " COLOR2 " (h)»¡©ú [PgUp][PgDn][0][$]²¾°Ê (/n)·j´M (C)¼È¦s (¡öq)µ²§ô "
+
+#define FOOTER_VEDIT \
+COLOR1 " %s " COLOR2 " (^Z)»¡©ú(^W)²Å¸¹(^L)­«Ã¸(^X)ÀÉ®×(^G)¥þ«¬ùø%s¢x%sùø%5d:%3d \033[m"
+
+
+/* ----------------------------------------------------- */
+/* °T®§¦r¦ê¡Gxo_foot() ®Éªº feeter ³£§ì¥X¨Ó©w¸q¦b³o */
+/* ----------------------------------------------------- */
+
+
+/* itoc.010914.µù¸Ñ: ¦Cªí¦h½g¡A©Ò¥H¥s FEETER¡A³£¬O 78 char */
+
+#define FEETER_CLASS \
+COLOR1 " ¬ÝªO¿ï¾Ü " COLOR2 " (c)·s¤å³¹ (vV)¼Ð°O¤wŪ¥¼Åª (y)¥þ³¡¦C¥X (z)¿ï­q (A)¥þ°ì·j´M (S)±Æ§Ç "
+
+#define FEETER_ULIST \
+COLOR1 " ºô¤Í¦Cªí " COLOR2 " (f)¦n¤Í (T)²á¤Ñ (q)¬d¸ß (ad)¥æ¤Í (m)±H«H (w)¤ô²y (s)§ó·s (TAB)¤Á´« "
+
+#define FEETER_PAL \
+COLOR1 " ©IªB¤Þ¦ñ " COLOR2 " (a)·s¼W (d)§R°£ (c)¤Í½Ë (m)±H«H (f)¤Þ¤J¦n¤Í (r^Q)¬d¸ß (s)§ó·s "
+
+#define FEETER_ALOHA \
+COLOR1 " ¤W¯¸³qª¾ " COLOR2 " (a)·s¼W (d)§R°£ (D)°Ï¬q§R°£ (f)¤Þ¤J¦n¤Í (r^Q)¬d¸ß (s)§ó·s "
+
+#define FEETER_VOTE \
+COLOR1 " ¬ÝªO§ë²¼ " COLOR2 " (¡÷/r/v)§ë²¼ (R)µ²ªG (^P)·s¼W§ë²¼ (E)­×§ï (V)¹wÄý (b)¶}²¼ (o)¦W³æ "
+
+#define FEETER_BMW \
+COLOR1 " ¤ô²y¦^ÅU " COLOR2 " (d)§R°£ (D)°Ï¬q§R°£ (m)±H«H (w)¤ô²y (^R)¦^°T (^Q)¬d¸ß (s)§ó·s "
+
+#define FEETER_MF \
+COLOR1 " ³Ì·R¬ÝªO " COLOR2 " (^P)·s¼W (Cg)½Æ»s (p^V)¶K¤W (d)§R°£ (c)·s¤å³¹ (vV)¼Ð°O¤wŪ/¥¼Åª "
+
+#define FEETER_COSIGN \
+COLOR1 " ³s¸p¤p¯¸ " COLOR2 " (r)Ū¨ú (y)¦^À³ (^P)µoªí (d)§R°£ (o)¶}ªO (c)Ãö³¬ (E)½s¿è (B)³]©w "
+
+#define FEETER_SONG \
+COLOR1 " ÂIºq¨t²Î " COLOR2 " (r)Ū¨ú (o)ÂIºq¨ì¬ÝªO (m)ÂIºq¨ì«H½c (E)½s¿èÀÉ®× (T)½s¿è¼ÐÃD "
+
+#define FEETER_NEWS \
+COLOR1 " ·s»DÂI¿ï " COLOR2 " (¡ô/¡õ)¤W¤U (PgUp/PgDn)¤W¤U­¶ (Home/End)­º§À (¡÷r)¿ï¨ú (¡ö)(q)Â÷¶} "
+
+#define FEETER_XPOST \
+COLOR1 " ¦ê¦C·j´M " COLOR2 " (y)¦^À³ (x)Âà¿ý (m)¼Ð°O (d)§R°£ (^P)µoªí (^Q)¬d¸ß§@ªÌ (t)¼ÐÅÒ "
+
+#define FEETER_MBOX \
+COLOR1 " «H«H¬Û±¤ " COLOR2 " (y)¦^«H (F/X/x)Âà±H/Âà¹F/Âà¿ý (d)§R°£ (D)°Ï¬q§R°£ (m)¼Ð°O (E)½s¿è "
+
+#define FEETER_POST \
+COLOR1 " ¤å³¹¦Cªí " COLOR2 " (ry)¦^«H (S/a)·j´M/¼ÐÃD/§@ªÌ (~G)¦ê¦C·j´M (x)Âà¿ý (V)§ë²¼ (u)·s»D "
+
+#define FEETER_GEM \
+COLOR1 " ¬ÝªOºëµØ " COLOR2 " (^P/a/f)·s¼W/¤å³¹/¥Ø¿ý (E)½s¿è (T)¼ÐÃD (m)²¾°Ê (c)½Æ»s (p^V)¶K¤W "
+
+#define FEETER_VOTEALL \
+COLOR1 " §ë²¼¤¤¤ß " COLOR2 " (¡ô/¡õ)¤W¤U (PgUp/PgDn)¤W¤U­¶ (Home/End)­º§À (¡÷)§ë²¼ (¡ö)(q)Â÷¶} "
+
+#define FEETER_HELP \
+COLOR1 " »¡©ú¤å¥ó " COLOR2 " (¡ô/¡õ)¤W¤U (PgUp/PgDn)¤W¤U­¶ (Home/End)­º§À (¡÷r)ÂsÄý (¡ö)(q)Â÷¶} "
+
+#define FEETER_INNBBS \
+COLOR1 " Âà«H³]©w " COLOR2 " (¡ô/¡õ)¤W¤U (PgUp/PgDn)¤W¤U­¶ (Home/End)­º§À (¡ö)(q)Â÷¶} "
+
+
+/* ----------------------------------------------------- */
+/* ¯¸¥x¨Ó·½Ã±¦W */
+/* ----------------------------------------------------- */
+
+/* itoc: «ØÄ³ banner ¤£­n¶W¹L¤T¦æ¡A¹Lªøªº¯¸Ã±¥i¯à·|³y¦¨¬Y¨Ç¨Ï¥ÎªÌªº¤Ï·P */
+
+
+#if 0
+#define EDIT_BANNER "\n--\n" \
+ " \033[1;41m¡÷\033[44m¡õ\033[m O\033[1mri\033[30mgi\033[mn: \033[1;43m "SCHOOLNAME"£»"BBSNAME" \033[47m "MYHOSTNAME" \033[m\n" \
+ " \033[1;45m¡ô\033[42m¡ö\033[m Au\033[1mt\033[30mho\033[mr: \033[1;33m%s\033[m ±q \033[1;34m%s\033[m µoªí\n"
+
+#define MODIFY_BANNER " \033[1;43m¡ù\033[46m¡ø\033[m \033[1mMo\033[30mdi\033[mfy: %s ©ó \033[1;33m%s\033[m ­×§ï\n"
+#endif
+
+/* ·s¯¸Ã± by hppy.bbs@sony.tfcis.org */
+#define EDIT_BANNER "\n--\n" \
+"\033[1m \033[42m¢q\033[40m¢w¢¡ ¢~¢w¢w¢w¢w¢w¢w¢w¢¡\033[m\n" \
+"\033[1m ¢x ¢x\033[1m¥\033[mx«\033[1mn¤@¤\033[m¤¡D¯\033[1mÁ¥§¤\033[mp¯\033[1m¸\033[m¡ý\033[1msony.TFcis.org¢x ¢x\033[m\n" \
+"\033[1m ¢¢¢w¢w¢w¢w¢w¢w¢w¢w¢w¢£ ¢¢¢w\033[42m¢q\033[m\n" \
+"\033[1m by %s from \033[m \033[1m%s (%s)\033[m\n\n"
+
+#define MODIFY_BANNER "¡¶ \033[1;30m%s \033[;33m§ï\033[m@\033[m%s\033[m\n"
+
+/* ----------------------------------------------------- */
+/* ¨ä¥L°T®§¦r¦ê */
+/* ----------------------------------------------------- */
+
+#define VMSG_NULL " \033[1;32;45m ¡´ ½Ð«ö¥ô·NÁäÄ~Äò ¡´ \033[m"
+
+#define ICON_UNREAD_BRD "\033[0;32m£¾" /* ¥¼Åª¬ÝªO */
+#define ICON_READ_BRD " " /* ¤wŪ¬ÝªO */
+
+#define ICON_GAMBLED_BRD "\033[1;31m½ä\033[m" /* Á|¦æ½ä½L¤¤ªº¬ÝªO */
+#define ICON_VOTED_BRD "\033[1;33m§ë\033[m" /* Á|¦æ§ë²¼¤¤ªº¬ÝªO */
+#define ICON_NOTRAN_BRD "¡·" /* ¤£Âà«HªO */
+#define ICON_TRAN_BRD "¡´" /* Âà«HªO */
+
+#define TOKEN_ZAP_BRD '-' /* zap ªO */
+#define TOKEN_FRIEND_BRD '.' /* ¦n¤ÍªO */
+#define TOKEN_SECRET_BRD ')' /* ¯µ±KªO */
+
+#endif /* _THEME_H_ */
+
diff --git a/include/theme/theme_blue.h b/include/theme/theme_blue.h
new file mode 100644
index 0000000..73ff28a
--- /dev/null
+++ b/include/theme/theme_blue.h
@@ -0,0 +1,246 @@
+/*-------------------------------------------------------*/
+/* theme.h ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : custom theme */
+/* create : 02/08/17 */
+/* update : / / */
+/*-------------------------------------------------------*/
+
+
+#ifndef _THEME_H_
+#define _THEME_H_
+
+
+/* ----------------------------------------------------- */
+/* °ò¥»ÃC¦â©w¸q¡A¥H§Q¤¶­±­×§ï */
+/* ----------------------------------------------------- */
+
+#define COLOR1 "\033[34;46m" /* footer/feeter ªº«e¬qÃC¦â */
+#define COLOR2 "\033[37;44m" /* footer/feeter ªº«á¬qÃC¦â */
+#define COLOR3 "\033[37;44m" /* neck ªºÃC¦â */
+#define COLOR4 "\033[1;44m" /* ¥ú´Î ªºÃC¦â */
+#define COLOR5 "\033[34;47m" /* more ÀÉÀYªº¼ÐÃDÃC¦â */
+#define COLOR6 "\033[37;44m" /* more ÀÉÀYªº¤º®eÃC¦â */
+#define COLOR7 "\033[1;34m" /* §@ªÌ¦b½u¤WªºÃC¦â */
+
+
+/* ----------------------------------------------------- */
+/* ¨Ï¥ÎªÌ¦W³æÃC¦â */
+/* ----------------------------------------------------- */
+
+#define COLOR_NORMAL "" /* ¤@¯ë¨Ï¥ÎªÌ */
+#define COLOR_MYBAD "\033[1;31m" /* Ãa¤H */
+#define COLOR_MYGOOD "\033[1;32m" /* §Úªº¦n¤Í */
+#define COLOR_OGOOD "\033[1;33m" /* »P§Ú¬°¤Í */
+#define COLOR_CLOAK "\033[1;35m" /* Áô§Î */ /* itoc.µù¸Ñ: ¨S¥Î¨ì¡A­nªº¤H½Ð¦Û¦æ¥[¤J ulist_body() */
+#define COLOR_SELF "\033[1;36m" /* ¦Û¤v */
+#define COLOR_BOTHGOOD "\033[1;37m" /* ¤¬³]¦n¤Í */
+#define COLOR_BRDMATE "\033[36m" /* ªO¦ñ */
+
+
+/* ----------------------------------------------------- */
+/* ¿ï³æ¦ì¸m */
+/* ----------------------------------------------------- */
+
+/* itoc.µù¸Ñ: ª`·N MENU_XPOS ­n >= MENU_XNOTE + MOVIE_LINES */
+
+#define MENU_XNOTE 2 /* °ÊºA¬ÝªO¥Ñ (2, 0) ¶}©l */
+#define MOVIE_LINES 10 /* °Êµe³Ì¦h¦³ 10 ¦C */
+
+#define MENU_XPOS 13 /* ¿ï³æ¶}©lªº (x, y) ®y¼Ð */
+#define MENU_YPOS ((d_cols >> 1) + 18)
+
+
+/* ----------------------------------------------------- */
+/* °T®§¦r¦ê¡G*_neck() ®Éªº necker ³£§ì¥X¨Ó©w¸q¦b³o */
+/* ----------------------------------------------------- */
+
+/* necker ªº¦æ¼Æ³£¬O¤G¦æ¡A±q (1, 0) ¨ì (2, 80) */
+
+/* ©Ò¦³ªº XZ_* ³£¦³ necker¡A¥u¬O¦³¨Ç¦b *_neck()¡A¦³¨ÇÂæb *_head() */
+
+/* ulist_neck() ¤Î xpost_head() ªº²Ä¤@¦æ¤ñ¸û¯S§O¡A¤£¦b¦¹©w¸q */
+
+#define NECKER_CLASS "[¡ö]¥D¿ï³æ [¡÷]¾\\Ū [¡ô¡õ]¿ï¾Ü [c]½g¼Æ [y]¸ü¤J [/?]·j´M [s]¬ÝªO [h]»¡©ú\n" \
+ COLOR3 " %s ¬Ý ªO Ãþ§OÂà«H ¤¤ ¤å ±Ô ­z%*s ¤H®ð ªO ¥D%*s \033[m"
+
+#define NECKER_ULIST "\n" \
+ COLOR3 " ½s¸¹ ¥N¸¹ ¼ÊºÙ%*s %-*s °ÊºA ¶¢¸m \033[m"
+
+#define NECKER_PAL "[¡ö]Â÷¶} [a]·s¼W [c]­×§ï [d]§R°£ [m]±H«H [w]¤ô²y [s]¾ã²z [¡÷]¬d¸ß [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¥N ¸¹ ¤Í ½Ë%*s \033[m"
+
+#define NECKER_ALOHA "[¡ö]Â÷¶} [a]·s¼W [d]§R°£ [D]°Ï¬q§R°£ [m]±H«H [w]¤ô²y [s]­«¾ã [f]¤Þ¤J [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¤W ¯¸ ³q ª¾ ¦W ³æ%*s \033[m"
+
+#define NECKER_VOTE "[¡ö]Â÷¶} [R]µ²ªG [^P]Á|¦æ [E]­×§ï [V]¹wÄý [^Q]§ï´Á [o]¦W³æ [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¶}²¼¤é ¥D¿ì¤H §ë ²¼ ©v ¦®%*s \033[m"
+
+#define NECKER_BMW "[¡ö]Â÷¶} [d]§R°£ [D]°Ï¬q§R°£ [m]±H«H [M]Àx¦s [w]¤ô²y [s]§ó·s [¡÷]¬d¸ß [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¥N ¸¹ ¤º ®e%*s ®É¶¡ \033[m"
+
+#define NECKER_MF "[¡ö]Â÷¶} [¡÷]¶i¤J [^P]·s¼W [d]§R°£ [c]¤Á´« [C]½Æ»s [^V]¶K¤W [m]²¾°Ê [h]»¡©ú\n" \
+ COLOR3 " %s ¬Ý ªO Ãþ§OÂà«H ¤¤ ¤å ±Ô ­z%*s ¤H®ð ªO ¥D%*s \033[m"
+
+#define NECKER_COSIGN "[¡ö]Â÷¶} [¡÷]¾\\Ū [^P]¥Ó½Ð [d]§R°£ [o]¶}ªO [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á Á|¿ì¤H ¬Ý ªO ¼Ð ÃD%*s \033[m"
+
+#define NECKER_SONG "[¡ö]Â÷¶} [¡÷]ÂsÄý [o]ÂIºq¨ì¬ÝªO [m]ÂIºq¨ì«H½c [Enter]ÂsÄý [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¥D ÃD%*s [½s ¿ï] [¤é ´Á]\033[m"
+
+#define NECKER_NEWS "[¡ö]Â÷¶} [¡÷]¾\\Ū [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á §@ ªÌ ·s »D ¼Ð ÃD%*s \033[m"
+
+#define NECKER_XPOST "\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á §@ ªÌ ¤å ³¹ ¼Ð ÃD%*s µû:%s \033[m"
+
+#define NECKER_MBOX "[¡ö]Â÷¶} [¡÷,r]Ū«H [d]§R°£ [R,y](¸s²Õ)¦^«H [s]±H«H [x]Âà¿ý [X]Âà¹F [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á §@ ªÌ «H ¥ó ¼Ð ÃD%*s \033[m"
+
+#define NECKER_POST "[¡ö]Â÷¶} [¡÷]¾\\Ū [^P]µoªí [b]¶iªOµe­± [d]§R°£ [V]§ë²¼ [TAB]ºëµØ°Ï [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á §@ ªÌ ¤å ³¹ ¼Ð ÃD%*s µû:%s ¤H®ð:%-4d \033[m"
+
+#define NECKER_GEM "[¡ö]Â÷¶} [¡÷]ÂsÄý [B]¼Ò¦¡ [C]¼È¦s [F]Âà±H [d]§R°£ [h]»¡©ú %s\n" \
+ COLOR3 " ½s¸¹ ¥D ÃD%*s [½s ¿ï] [¤é ´Á]\033[m"
+
+/* ¥H¤U³o¨Ç«h¬O¤@¨ÇÃþ XZ_* µ²ºcªº necker */
+
+#define NECKER_VOTEALL "[¡ô/¡õ]¤W¤U [PgUp/PgDn]¤W¤U­¶ [Home/End]­º§À [¡÷]§ë²¼ [¡ö][q]Â÷¶}\n" \
+ COLOR3 " ½s¸¹ ¬Ý ªO Ãþ§OÂà«H ¤¤ ¤å ±Ô ­z%*s ªO ¥D%*s \033[m"
+
+#define NECKER_CREDIT "[¡ö]Â÷¶} [C]´«­¶ [1]·s¼W [2]§R°£ [3]¥þ§R [4]Á`­p\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á ¦¬¤ä ª÷ ÃB ¤ÀÃþ »¡ ©ú%*s \033[m"
+
+#define NECKER_HELP "[¡ö]Â÷¶} [¡÷]¾\\Ū [^P]·s¼W [d]§R°£ [T]¼ÐÃD [E]½s¿è [m]²¾°Ê\n" \
+ COLOR3 " ½s¸¹ ÀÉ ®× ¼Ð ÃD%*s \033[m"
+
+#define NECKER_INNBBS "[¡ö]Â÷¶} [^P]·s¼W [d]§R°£ [E]½s¿è [/]·j´M [Enter]¸Ô²Ó\n" \
+ COLOR3 " ½s¸¹ ¤º ®e%*s \033[m"
+
+
+/* ----------------------------------------------------- */
+/* °T®§¦r¦ê¡Gmore() ®Éªº footer ³£§ì¥X¨Ó©w¸q¦b³o */
+/* ----------------------------------------------------- */
+
+/* itoc.010914.µù¸Ñ: ³æ¤@½g¡A©Ò¥H¥s FOOTER¡A³£¬O 78 char */
+
+/* itoc.010821: ª`·N \\ ¬O \¡A³Ì«á§Oº|¤F¤@­ÓªÅ¥ÕÁä :p */
+
+#define FOOTER_POST \
+COLOR1 " ¤å³¹¿ïŪ " COLOR2 " (ry)¦^À³ (=\\[]<>-+;'`)¥DÃD (|?QA)·j´M¼ÐÃD§@ªÌ (kj)¤W¤U½g (C)¼È¦s "
+
+#define FOOTER_MAILER \
+COLOR1 " ³½¶­©¹ªð " COLOR2 " (ry)¦^«H/¸s²Õ (X)Âà¹F (d)§R°£ (m)¼Ð°O (C)¼È¦s (=\\[]<>-+;'`|?QAkj) "
+
+#define FOOTER_GEM \
+COLOR1 " ºëµØ¿ïŪ " COLOR2 " (=\\[]<>-+;'`)¥DÃD (|?QA)·j´M¼ÐÃD§@ªÌ (kj)¤W¤U½g (¡ô¡õ¡ö)¤W¤UÂ÷¶} "
+
+#ifdef HAVE_GAME
+#define FOOTER_TALK \
+COLOR1 " ¥æ½Í¼Ò¦¡ " COLOR2 " (^O)¹ï«³¼Ò¦¡ (^C,^D)µ²§ô¥æ½Í (^T)¤Á´«©I¥s¾¹ (^Z)§Ö±¶¦Cªí (^G)¹Í¹Í "
+#else
+#define FOOTER_TALK \
+COLOR1 " ¥æ½Í¼Ò¦¡ " COLOR2 " (^C,^D)µ²§ô¥æ½Í (^T)¤Á´«©I¥s¾¹ (^Z)§Ö±¶¦Cªí (^G)¹Í¹Í (^Y)²M°£ "
+#endif
+
+#define FOOTER_COSIGN \
+COLOR1 " ³s¸p¾÷¨î " COLOR2 " (ry)¥[¤J³s¸p (kj)¤W¤U½g (¡ô¡õ¡ö)¤W¤UÂ÷¶} (h)»¡©ú "
+
+#define FOOTER_MORE \
+COLOR1 " ÂsÄý P.%d (%d%%) " COLOR2 " (h)»¡©ú [PgUp][PgDn][0][$]²¾°Ê (/n)·j´M (C)¼È¦s (¡öq)µ²§ô "
+
+#define FOOTER_VEDIT \
+COLOR1 " %s " COLOR2 " (^Z)»¡©ú (^W)²Å¸¹ (^L)­«Ã¸ (^X)Àɮ׳B²z ùø%s¢x%sùø%5d:%3d \033[m"
+
+
+/* ----------------------------------------------------- */
+/* °T®§¦r¦ê¡Gxo_foot() ®Éªº feeter ³£§ì¥X¨Ó©w¸q¦b³o */
+/* ----------------------------------------------------- */
+
+
+/* itoc.010914.µù¸Ñ: ¦Cªí¦h½g¡A©Ò¥H¥s FEETER¡A³£¬O 78 char */
+
+#define FEETER_CLASS \
+COLOR1 " ¬ÝªO¿ï¾Ü " COLOR2 " (c)·s¤å³¹ (vV)¼Ð°O¤wŪ¥¼Åª (y)¥þ³¡¦C¥X (z)¿ï­q (A)¥þ°ì·j´M (S)±Æ§Ç "
+
+#define FEETER_ULIST \
+COLOR1 " ºô¤Í¦Cªí " COLOR2 " (f)¦n¤Í (t)²á¤Ñ (q)¬d¸ß (ad)¥æ¤Í (m)±H«H (w)¤ô²y (s)§ó·s (TAB)¤Á´« "
+
+#define FEETER_PAL \
+COLOR1 " ©IªB¤Þ¦ñ " COLOR2 " (a)·s¼W (d)§R°£ (c)¤Í½Ë (m)±H«H (f)¤Þ¤J¦n¤Í (r^Q)¬d¸ß (s)§ó·s "
+
+#define FEETER_ALOHA \
+COLOR1 " ¤W¯¸³qª¾ " COLOR2 " (a)·s¼W (d)§R°£ (D)°Ï¬q§R°£ (f)¤Þ¤J¦n¤Í (r^Q)¬d¸ß (s)§ó·s "
+
+#define FEETER_VOTE \
+COLOR1 " ¬ÝªO§ë²¼ " COLOR2 " (¡÷/r/v)§ë²¼ (R)µ²ªG (^P)·s¼W§ë²¼ (E)­×§ï (V)¹wÄý (b)¶}²¼ (o)¦W³æ "
+
+#define FEETER_BMW \
+COLOR1 " ¤ô²y¦^ÅU " COLOR2 " (d)§R°£ (D)°Ï¬q§R°£ (m)±H«H (w)¤ô²y (^R)¦^°T (^Q)¬d¸ß (s)§ó·s "
+
+#define FEETER_MF \
+COLOR1 " ³Ì·R¬ÝªO " COLOR2 " (^P)·s¼W (Cg)½Æ»s (p^V)¶K¤W (d)§R°£ (c)·s¤å³¹ (vV)¼Ð°O¤wŪ/¥¼Åª "
+
+#define FEETER_COSIGN \
+COLOR1 " ³s¸p¤p¯¸ " COLOR2 " (r)Ū¨ú (y)¦^À³ (^P)µoªí (d)§R°£ (o)¶}ªO (c)Ãö³¬ (E)½s¿è (B)³]©w "
+
+#define FEETER_SONG \
+COLOR1 " ÂIºq¨t²Î " COLOR2 " (r)Ū¨ú (o)ÂIºq¨ì¬ÝªO (m)ÂIºq¨ì«H½c (E)½s¿èÀÉ®× (T)½s¿è¼ÐÃD "
+
+#define FEETER_NEWS \
+COLOR1 " ·s»DÂI¿ï " COLOR2 " (¡ô/¡õ)¤W¤U (PgUp/PgDn)¤W¤U­¶ (Home/End)­º§À (¡÷r)¿ï¨ú (¡ö)(q)Â÷¶} "
+
+#define FEETER_XPOST \
+COLOR1 " ¦ê¦C·j´M " COLOR2 " (y)¦^À³ (x)Âà¿ý (m)¼Ð°O (d)§R°£ (^P)µoªí (^Q)¬d¸ß§@ªÌ (t)¼ÐÅÒ "
+
+#define FEETER_MBOX \
+COLOR1 " «H«H¬Û±¤ " COLOR2 " (y)¦^«H (F/X/x)Âà±H/Âà¹F/Âà¿ý (d)§R°£ (D)°Ï¬q§R°£ (m)¼Ð°O (E)½s¿è "
+
+#define FEETER_POST \
+COLOR1 " ¤å³¹¦Cªí " COLOR2 " (ry)¦^«H (S/a)·j´M/¼ÐÃD/§@ªÌ (~G)¦ê¦C·j´M (x)Âà¿ý (V)§ë²¼ (u)·s»D "
+
+#define FEETER_GEM \
+COLOR1 " ¬ÝªOºëµØ " COLOR2 " (^P/a/f)·s¼W/¤å³¹/¥Ø¿ý (E)½s¿è (T)¼ÐÃD (m)²¾°Ê (c)½Æ»s (p^V)¶K¤W "
+
+#define FEETER_VOTEALL \
+COLOR1 " §ë²¼¤¤¤ß " COLOR2 " (¡ô/¡õ)¤W¤U (PgUp/PgDn)¤W¤U­¶ (Home/End)­º§À (¡÷)§ë²¼ (¡ö)(q)Â÷¶} "
+
+#define FEETER_HELP \
+COLOR1 " »¡©ú¤å¥ó " COLOR2 " (¡ô/¡õ)¤W¤U (PgUp/PgDn)¤W¤U­¶ (Home/End)­º§À (¡÷r)ÂsÄý (¡ö)(q)Â÷¶} "
+
+#define FEETER_INNBBS \
+COLOR1 " Âà«H³]©w " COLOR2 " (¡ô/¡õ)¤W¤U (PgUp/PgDn)¤W¤U­¶ (Home/End)­º§À (¡ö)(q)Â÷¶} "
+
+
+/* ----------------------------------------------------- */
+/* ¯¸¥x¨Ó·½Ã±¦W */
+/* ----------------------------------------------------- */
+
+/* itoc: «ØÄ³ banner ¤£­n¶W¹L¤T¦æ¡A¹Lªøªº¯¸Ã±¥i¯à·|³y¦¨¬Y¨Ç¨Ï¥ÎªÌªº¤Ï·P */
+
+#define EDIT_BANNER "\n--\n" \
+ " \033[1;43m¢«\033[46m¢ª\033[m Or\033[1mig\033[30min\033[m: \033[1;44m "SCHOOLNAME"£»"BBSNAME" \033[42m "MYHOSTNAME" \033[m\n" \
+ " \033[1;44m¢©\033[41m¢¨\033[m A\033[1mut\033[30mho\033[mr: \033[1;34m%s\033[m ±q \033[1;31m%s\033[m µoªí\n"
+
+#define MODIFY_BANNER " \033[1;45m¢c\033[42m¢i\033[m \033[1mMo\033[30mdi\033[mfy: %s ©ó \033[1;34m%s\033[m ­×§ï\n"
+
+
+/* ----------------------------------------------------- */
+/* ¨ä¥L°T®§¦r¦ê */
+/* ----------------------------------------------------- */
+
+#define VMSG_NULL " \033[1;33;46m ¡´ ½Ð«ö¥ô·NÁäÄ~Äò ¡´ \033[m"
+
+#define ICON_UNREAD_BRD "\033[1;33m£»\033[m" /* ¥¼Åª¬ÝªO */
+#define ICON_READ_BRD " " /* ¤wŪ¬ÝªO */
+
+#define ICON_GAMBLED_BRD "\033[1;31m½ä\033[m" /* Á|¦æ½ä½L¤¤ªº¬ÝªO */
+#define ICON_VOTED_BRD "\033[1;33m§ë\033[m" /* Á|¦æ§ë²¼¤¤ªº¬ÝªO */
+#define ICON_NOTRAN_BRD " " /* ¤£Âà«HªO */
+#define ICON_TRAN_BRD "Âà" /* Âà«HªO */
+
+#define TOKEN_ZAP_BRD '-' /* zap ªO */
+#define TOKEN_FRIEND_BRD '.' /* ¦n¤ÍªO */
+#define TOKEN_SECRET_BRD ')' /* ¯µ±KªO */
+
+#endif /* _THEME_H_ */
diff --git a/include/theme/theme_cyan.h b/include/theme/theme_cyan.h
new file mode 100644
index 0000000..8766d14
--- /dev/null
+++ b/include/theme/theme_cyan.h
@@ -0,0 +1,246 @@
+/*-------------------------------------------------------*/
+/* theme.h ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : custom theme */
+/* create : 02/08/17 */
+/* update : / / */
+/*-------------------------------------------------------*/
+
+
+#ifndef _THEME_H_
+#define _THEME_H_
+
+
+/* ----------------------------------------------------- */
+/* °ò¥»ÃC¦â©w¸q¡A¥H§Q¤¶­±­×§ï */
+/* ----------------------------------------------------- */
+
+#define COLOR1 "\033[1;33;45m" /* footer/feeter ªº«e¬qÃC¦â */
+#define COLOR2 "\033[37;46m" /* footer/feeter ªº«á¬qÃC¦â */
+#define COLOR3 "\033[1;33;46m" /* neck ªºÃC¦â */
+#define COLOR4 "\033[1;46m" /* ¥ú´Î ªºÃC¦â */
+#define COLOR5 "\033[1;33;46m" /* more ÀÉÀYªº¼ÐÃDÃC¦â */
+#define COLOR6 "\033[m" /* more ÀÉÀYªº¤º®eÃC¦â */
+#define COLOR7 "\033[36m" /* §@ªÌ¦b½u¤WªºÃC¦â */
+
+
+/* ----------------------------------------------------- */
+/* ¨Ï¥ÎªÌ¦W³æÃC¦â */
+/* ----------------------------------------------------- */
+
+#define COLOR_NORMAL "" /* ¤@¯ë¨Ï¥ÎªÌ */
+#define COLOR_MYBAD "\033[1;31m" /* Ãa¤H */
+#define COLOR_MYGOOD "\033[1;32m" /* §Úªº¦n¤Í */
+#define COLOR_OGOOD "\033[1;33m" /* »P§Ú¬°¤Í */
+#define COLOR_CLOAK "\033[1;35m" /* Áô§Î */ /* itoc.µù¸Ñ: ¨S¥Î¨ì¡A­nªº¤H½Ð¦Û¦æ¥[¤J ulist_body() */
+#define COLOR_SELF "\033[1;36m" /* ¦Û¤v */
+#define COLOR_BOTHGOOD "\033[1;37m" /* ¤¬³]¦n¤Í */
+#define COLOR_BRDMATE "\033[36m" /* ªO¦ñ */
+
+
+/* ----------------------------------------------------- */
+/* ¿ï³æ¦ì¸m */
+/* ----------------------------------------------------- */
+
+/* itoc.µù¸Ñ: ª`·N MENU_XPOS ­n >= MENU_XNOTE + MOVIE_LINES */
+
+#define MENU_XNOTE 2 /* °ÊºA¬ÝªO¥Ñ (2, 0) ¶}©l */
+#define MOVIE_LINES 10 /* °Êµe³Ì¦h¦³ 10 ¦C */
+
+#define MENU_XPOS 13 /* ¿ï³æ¶}©lªº (x, y) ®y¼Ð */
+#define MENU_YPOS ((d_cols >> 1) + 18)
+
+
+/* ----------------------------------------------------- */
+/* °T®§¦r¦ê¡G*_neck() ®Éªº necker ³£§ì¥X¨Ó©w¸q¦b³o */
+/* ----------------------------------------------------- */
+
+/* necker ªº¦æ¼Æ³£¬O¤G¦æ¡A±q (1, 0) ¨ì (2, 80) */
+
+/* ©Ò¦³ªº XZ_* ³£¦³ necker¡A¥u¬O¦³¨Ç¦b *_neck()¡A¦³¨ÇÂæb *_head() */
+
+/* ulist_neck() ¤Î xpost_head() ªº²Ä¤@¦æ¤ñ¸û¯S§O¡A¤£¦b¦¹©w¸q */
+
+#define NECKER_CLASS "[¡ö]¥D¿ï³æ [¡÷]¾\\Ū [¡ô¡õ]¿ï¾Ü [c]½g¼Æ [y]¸ü¤J [/?]·j´M [s]¬ÝªO [h]»¡©ú\n" \
+ COLOR3 " %s ¬Ý ªO Ãþ§OÂà«H ¤¤ ¤å ±Ô ­z%*s ¤H®ð ªO ¥D%*s \033[m"
+
+#define NECKER_ULIST "\n" \
+ COLOR3 " ½s¸¹ ¥N¸¹ ¼ÊºÙ%*s %-*s °ÊºA ¶¢¸m \033[m"
+
+#define NECKER_PAL "[¡ö]Â÷¶} [a]·s¼W [c]­×§ï [d]§R°£ [m]±H«H [w]¤ô²y [s]¾ã²z [¡÷]¬d¸ß [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¥N ¸¹ ¤Í ½Ë%*s \033[m"
+
+#define NECKER_ALOHA "[¡ö]Â÷¶} [a]·s¼W [d]§R°£ [D]°Ï¬q§R°£ [m]±H«H [w]¤ô²y [s]­«¾ã [f]¤Þ¤J [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¤W ¯¸ ³q ª¾ ¦W ³æ%*s \033[m"
+
+#define NECKER_VOTE "[¡ö]Â÷¶} [R]µ²ªG [^P]Á|¦æ [E]­×§ï [V]¹wÄý [^Q]§ï´Á [o]¦W³æ [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¶}²¼¤é ¥D¿ì¤H §ë ²¼ ©v ¦®%*s \033[m"
+
+#define NECKER_BMW "[¡ö]Â÷¶} [d]§R°£ [D]°Ï¬q§R°£ [m]±H«H [M]Àx¦s [w]¤ô²y [s]§ó·s [¡÷]¬d¸ß [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¥N ¸¹ ¤º ®e%*s ®É¶¡ \033[m"
+
+#define NECKER_MF "[¡ö]Â÷¶} [¡÷]¶i¤J [^P]·s¼W [d]§R°£ [c]¤Á´« [C]½Æ»s [^V]¶K¤W [m]²¾°Ê [h]»¡©ú\n" \
+ COLOR3 " %s ¬Ý ªO Ãþ§OÂà«H ¤¤ ¤å ±Ô ­z%*s ¤H®ð ªO ¥D%*s \033[m"
+
+#define NECKER_COSIGN "[¡ö]Â÷¶} [¡÷]¾\\Ū [^P]¥Ó½Ð [d]§R°£ [o]¶}ªO [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á Á|¿ì¤H ¬Ý ªO ¼Ð ÃD%*s \033[m"
+
+#define NECKER_SONG "[¡ö]Â÷¶} [¡÷]ÂsÄý [o]ÂIºq¨ì¬ÝªO [m]ÂIºq¨ì«H½c [Enter]ÂsÄý [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¥D ÃD%*s [½s ¿ï] [¤é ´Á]\033[m"
+
+#define NECKER_NEWS "[¡ö]Â÷¶} [¡÷]¾\\Ū [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á §@ ªÌ ·s »D ¼Ð ÃD%*s \033[m"
+
+#define NECKER_XPOST "\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á §@ ªÌ ¤å ³¹ ¼Ð ÃD%*s µû:%s \033[m"
+
+#define NECKER_MBOX "[¡ö]Â÷¶} [¡÷,r]Ū«H [d]§R°£ [R,y](¸s²Õ)¦^«H [s]±H«H [x]Âà¿ý [X]Âà¹F [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á §@ ªÌ «H ¥ó ¼Ð ÃD%*s \033[m"
+
+#define NECKER_POST "[¡ö]Â÷¶} [¡÷]¾\\Ū [^P]µoªí [b]¶iªOµe­± [d]§R°£ [V]§ë²¼ [TAB]ºëµØ°Ï [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á §@ ªÌ ¤å ³¹ ¼Ð ÃD%*s µû:%s ¤H®ð:%-4d \033[m"
+
+#define NECKER_GEM "[¡ö]Â÷¶} [¡÷]ÂsÄý [B]¼Ò¦¡ [C]¼È¦s [F]Âà±H [d]§R°£ [h]»¡©ú %s\n" \
+ COLOR3 " ½s¸¹ ¥D ÃD%*s [½s ¿ï] [¤é ´Á]\033[m"
+
+/* ¥H¤U³o¨Ç«h¬O¤@¨ÇÃþ XZ_* µ²ºcªº necker */
+
+#define NECKER_VOTEALL "[¡ô/¡õ]¤W¤U [PgUp/PgDn]¤W¤U­¶ [Home/End]­º§À [¡÷]§ë²¼ [¡ö][q]Â÷¶}\n" \
+ COLOR3 " ½s¸¹ ¬Ý ªO Ãþ§OÂà«H ¤¤ ¤å ±Ô ­z%*s ªO ¥D%*s \033[m"
+
+#define NECKER_CREDIT "[¡ö]Â÷¶} [C]´«­¶ [1]·s¼W [2]§R°£ [3]¥þ§R [4]Á`­p\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á ¦¬¤ä ª÷ ÃB ¤ÀÃþ »¡ ©ú%*s \033[m"
+
+#define NECKER_HELP "[¡ö]Â÷¶} [¡÷]¾\\Ū [^P]·s¼W [d]§R°£ [T]¼ÐÃD [E]½s¿è [m]²¾°Ê\n" \
+ COLOR3 " ½s¸¹ ÀÉ ®× ¼Ð ÃD%*s \033[m"
+
+#define NECKER_INNBBS "[¡ö]Â÷¶} [^P]·s¼W [d]§R°£ [E]½s¿è [/]·j´M [Enter]¸Ô²Ó\n" \
+ COLOR3 " ½s¸¹ ¤º ®e%*s \033[m"
+
+
+/* ----------------------------------------------------- */
+/* °T®§¦r¦ê¡Gmore() ®Éªº footer ³£§ì¥X¨Ó©w¸q¦b³o */
+/* ----------------------------------------------------- */
+
+/* itoc.010914.µù¸Ñ: ³æ¤@½g¡A©Ò¥H¥s FOOTER¡A³£¬O 78 char */
+
+/* itoc.010821: ª`·N \\ ¬O \¡A³Ì«á§Oº|¤F¤@­ÓªÅ¥ÕÁä :p */
+
+#define FOOTER_POST \
+COLOR1 " ¤å³¹¿ïŪ " COLOR2 " (ry)¦^À³ (=\\[]<>-+;'`)¥DÃD (|?QA)·j´M¼ÐÃD§@ªÌ (kj)¤W¤U½g (C)¼È¦s "
+
+#define FOOTER_MAILER \
+COLOR1 " ³½¶­©¹ªð " COLOR2 " (ry)¦^«H/¸s²Õ (X)Âà¹F (d)§R°£ (m)¼Ð°O (C)¼È¦s (=\\[]<>-+;'`|?QAkj) "
+
+#define FOOTER_GEM \
+COLOR1 " ºëµØ¿ïŪ " COLOR2 " (=\\[]<>-+;'`)¥DÃD (|?QA)·j´M¼ÐÃD§@ªÌ (kj)¤W¤U½g (¡ô¡õ¡ö)¤W¤UÂ÷¶} "
+
+#ifdef HAVE_GAME
+#define FOOTER_TALK \
+COLOR1 " ¥æ½Í¼Ò¦¡ " COLOR2 " (^O)¹ï«³¼Ò¦¡ (^C,^D)µ²§ô¥æ½Í (^T)¤Á´«©I¥s¾¹ (^Z)§Ö±¶¦Cªí (^G)¹Í¹Í "
+#else
+#define FOOTER_TALK \
+COLOR1 " ¥æ½Í¼Ò¦¡ " COLOR2 " (^C,^D)µ²§ô¥æ½Í (^T)¤Á´«©I¥s¾¹ (^Z)§Ö±¶¦Cªí (^G)¹Í¹Í (^Y)²M°£ "
+#endif
+
+#define FOOTER_COSIGN \
+COLOR1 " ³s¸p¾÷¨î " COLOR2 " (ry)¥[¤J³s¸p (kj)¤W¤U½g (¡ô¡õ¡ö)¤W¤UÂ÷¶} (h)»¡©ú "
+
+#define FOOTER_MORE \
+COLOR1 " ÂsÄý P.%d (%d%%) " COLOR2 " (h)»¡©ú [PgUp][PgDn][0][$]²¾°Ê (/n)·j´M (C)¼È¦s (¡öq)µ²§ô "
+
+#define FOOTER_VEDIT \
+COLOR1 " %s " COLOR2 " (^Z)»¡©ú (^W)²Å¸¹ (^L)­«Ã¸ (^X)Àɮ׳B²z ùø%s¢x%sùø%5d:%3d \033[m"
+
+
+/* ----------------------------------------------------- */
+/* °T®§¦r¦ê¡Gxo_foot() ®Éªº feeter ³£§ì¥X¨Ó©w¸q¦b³o */
+/* ----------------------------------------------------- */
+
+
+/* itoc.010914.µù¸Ñ: ¦Cªí¦h½g¡A©Ò¥H¥s FEETER¡A³£¬O 78 char */
+
+#define FEETER_CLASS \
+COLOR1 " ¬ÝªO¿ï¾Ü " COLOR2 " (c)·s¤å³¹ (vV)¼Ð°O¤wŪ¥¼Åª (y)¥þ³¡¦C¥X (z)¿ï­q (A)¥þ°ì·j´M (S)±Æ§Ç "
+
+#define FEETER_ULIST \
+COLOR1 " ºô¤Í¦Cªí " COLOR2 " (f)¦n¤Í (t)²á¤Ñ (q)¬d¸ß (ad)¥æ¤Í (m)±H«H (w)¤ô²y (s)§ó·s (TAB)¤Á´« "
+
+#define FEETER_PAL \
+COLOR1 " ©IªB¤Þ¦ñ " COLOR2 " (a)·s¼W (d)§R°£ (c)¤Í½Ë (m)±H«H (f)¤Þ¤J¦n¤Í (r^Q)¬d¸ß (s)§ó·s "
+
+#define FEETER_ALOHA \
+COLOR1 " ¤W¯¸³qª¾ " COLOR2 " (a)·s¼W (d)§R°£ (D)°Ï¬q§R°£ (f)¤Þ¤J¦n¤Í (r^Q)¬d¸ß (s)§ó·s "
+
+#define FEETER_VOTE \
+COLOR1 " ¬ÝªO§ë²¼ " COLOR2 " (¡÷/r/v)§ë²¼ (R)µ²ªG (^P)·s¼W§ë²¼ (E)­×§ï (V)¹wÄý (b)¶}²¼ (o)¦W³æ "
+
+#define FEETER_BMW \
+COLOR1 " ¤ô²y¦^ÅU " COLOR2 " (d)§R°£ (D)°Ï¬q§R°£ (m)±H«H (w)¤ô²y (^R)¦^°T (^Q)¬d¸ß (s)§ó·s "
+
+#define FEETER_MF \
+COLOR1 " ³Ì·R¬ÝªO " COLOR2 " (^P)·s¼W (Cg)½Æ»s (p^V)¶K¤W (d)§R°£ (c)·s¤å³¹ (vV)¼Ð°O¤wŪ/¥¼Åª "
+
+#define FEETER_COSIGN \
+COLOR1 " ³s¸p¤p¯¸ " COLOR2 " (r)Ū¨ú (y)¦^À³ (^P)µoªí (d)§R°£ (o)¶}ªO (c)Ãö³¬ (E)½s¿è (B)³]©w "
+
+#define FEETER_SONG \
+COLOR1 " ÂIºq¨t²Î " COLOR2 " (r)Ū¨ú (o)ÂIºq¨ì¬ÝªO (m)ÂIºq¨ì«H½c (E)½s¿èÀÉ®× (T)½s¿è¼ÐÃD "
+
+#define FEETER_NEWS \
+COLOR1 " ·s»DÂI¿ï " COLOR2 " (¡ô/¡õ)¤W¤U (PgUp/PgDn)¤W¤U­¶ (Home/End)­º§À (¡÷r)¿ï¨ú (¡ö)(q)Â÷¶} "
+
+#define FEETER_XPOST \
+COLOR1 " ¦ê¦C·j´M " COLOR2 " (y)¦^À³ (x)Âà¿ý (m)¼Ð°O (d)§R°£ (^P)µoªí (^Q)¬d¸ß§@ªÌ (t)¼ÐÅÒ "
+
+#define FEETER_MBOX \
+COLOR1 " «H«H¬Û±¤ " COLOR2 " (y)¦^«H (F/X/x)Âà±H/Âà¹F/Âà¿ý (d)§R°£ (D)°Ï¬q§R°£ (m)¼Ð°O (E)½s¿è "
+
+#define FEETER_POST \
+COLOR1 " ¤å³¹¦Cªí " COLOR2 " (ry)¦^«H (S/a)·j´M/¼ÐÃD/§@ªÌ (~G)¦ê¦C·j´M (x)Âà¿ý (V)§ë²¼ (u)·s»D "
+
+#define FEETER_GEM \
+COLOR1 " ¬ÝªOºëµØ " COLOR2 " (^P/a/f)·s¼W/¤å³¹/¥Ø¿ý (E)½s¿è (T)¼ÐÃD (m)²¾°Ê (c)½Æ»s (p^V)¶K¤W "
+
+#define FEETER_VOTEALL \
+COLOR1 " §ë²¼¤¤¤ß " COLOR2 " (¡ô/¡õ)¤W¤U (PgUp/PgDn)¤W¤U­¶ (Home/End)­º§À (¡÷)§ë²¼ (¡ö)(q)Â÷¶} "
+
+#define FEETER_HELP \
+COLOR1 " »¡©ú¤å¥ó " COLOR2 " (¡ô/¡õ)¤W¤U (PgUp/PgDn)¤W¤U­¶ (Home/End)­º§À (¡÷r)ÂsÄý (¡ö)(q)Â÷¶} "
+
+#define FEETER_INNBBS \
+COLOR1 " Âà«H³]©w " COLOR2 " (¡ô/¡õ)¤W¤U (PgUp/PgDn)¤W¤U­¶ (Home/End)­º§À (¡ö)(q)Â÷¶} "
+
+
+/* ----------------------------------------------------- */
+/* ¯¸¥x¨Ó·½Ã±¦W */
+/* ----------------------------------------------------- */
+
+/* itoc: «ØÄ³ banner ¤£­n¶W¹L¤T¦æ¡A¹Lªøªº¯¸Ã±¥i¯à·|³y¦¨¬Y¨Ç¨Ï¥ÎªÌªº¤Ï·P */
+
+#define EDIT_BANNER "\n--\n" \
+ " \033[1;41m¢~\033[44m¢q\033[m Or\033[1mig\033[30min\033[m: \033[1;46m "SCHOOLNAME"£»"BBSNAME" \033[44m "MYHOSTNAME" \033[m\n" \
+ " \033[1;42m¢q\033[45m¢}\033[m A\033[1mut\033[30mho\033[mr: \033[1;36m%s\033[m ±q \033[1;33m%s\033[m µoªí\n"
+
+#define MODIFY_BANNER " \033[1;43m¢|\033[46m¢w\033[m \033[1mMo\033[30mdi\033[mfy: %s ©ó \033[1;36m%s\033[m ­×§ï\n"
+
+
+/* ----------------------------------------------------- */
+/* ¨ä¥L°T®§¦r¦ê */
+/* ----------------------------------------------------- */
+
+#define VMSG_NULL "\033[1;46m ¡¹ ½Ð«ö (Space/Return) Ä~Äò ¡¹ \033[m"
+
+#define ICON_UNREAD_BRD "\033[36m¡á" /* ¥¼Åª¬ÝªO */
+#define ICON_READ_BRD " " /* ¤wŪ¬ÝªO */
+
+#define ICON_GAMBLED_BRD "\033[1;31m½ä\033[m" /* Á|¦æ½ä½L¤¤ªº¬ÝªO */
+#define ICON_VOTED_BRD "\033[1;33m§ë\033[m" /* Á|¦æ§ë²¼¤¤ªº¬ÝªO */
+#define ICON_NOTRAN_BRD "¡³" /* ¤£Âà«HªO */
+#define ICON_TRAN_BRD "¡´" /* Âà«HªO */
+
+#define TOKEN_ZAP_BRD '-' /* zap ªO */
+#define TOKEN_FRIEND_BRD '.' /* ¦n¤ÍªO */
+#define TOKEN_SECRET_BRD ')' /* ¯µ±KªO */
+
+#endif /* _THEME_H_ */
diff --git a/include/theme/theme_green.h b/include/theme/theme_green.h
new file mode 100644
index 0000000..2370a66
--- /dev/null
+++ b/include/theme/theme_green.h
@@ -0,0 +1,246 @@
+/*-------------------------------------------------------*/
+/* theme.h ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : custom theme */
+/* create : 02/08/17 */
+/* update : / / */
+/*-------------------------------------------------------*/
+
+
+#ifndef _THEME_H_
+#define _THEME_H_
+
+
+/* ----------------------------------------------------- */
+/* °ò¥»ÃC¦â©w¸q¡A¥H§Q¤¶­±­×§ï */
+/* ----------------------------------------------------- */
+
+#define COLOR1 "\033[1;37;43m" /* footer/feeter ªº«e¬qÃC¦â */
+#define COLOR2 "\033[33;42m" /* footer/feeter ªº«á¬qÃC¦â */
+#define COLOR3 "\033[1;37;42m" /* neck ªºÃC¦â */
+#define COLOR4 "\033[1;33;42m" /* ¥ú´Î ªºÃC¦â */
+#define COLOR5 "\033[32;47m" /* more ÀÉÀYªº¼ÐÃDÃC¦â */
+#define COLOR6 "\033[37;42m" /* more ÀÉÀYªº¤º®eÃC¦â */
+#define COLOR7 "\033[32m" /* §@ªÌ¦b½u¤WªºÃC¦â */
+
+
+/* ----------------------------------------------------- */
+/* ¨Ï¥ÎªÌ¦W³æÃC¦â */
+/* ----------------------------------------------------- */
+
+#define COLOR_NORMAL "" /* ¤@¯ë¨Ï¥ÎªÌ */
+#define COLOR_MYBAD "\033[1;31m" /* Ãa¤H */
+#define COLOR_MYGOOD "\033[1;32m" /* §Úªº¦n¤Í */
+#define COLOR_OGOOD "\033[1;33m" /* »P§Ú¬°¤Í */
+#define COLOR_CLOAK "\033[1;35m" /* Áô§Î */ /* itoc.µù¸Ñ: ¨S¥Î¨ì¡A­nªº¤H½Ð¦Û¦æ¥[¤J ulist_body() */
+#define COLOR_SELF "\033[1;36m" /* ¦Û¤v */
+#define COLOR_BOTHGOOD "\033[1;37m" /* ¤¬³]¦n¤Í */
+#define COLOR_BRDMATE "\033[36m" /* ªO¦ñ */
+
+
+/* ----------------------------------------------------- */
+/* ¿ï³æ¦ì¸m */
+/* ----------------------------------------------------- */
+
+/* itoc.µù¸Ñ: ª`·N MENU_XPOS ­n >= MENU_XNOTE + MOVIE_LINES */
+
+#define MENU_XNOTE 2 /* °ÊºA¬ÝªO¥Ñ (2, 0) ¶}©l */
+#define MOVIE_LINES 10 /* °Êµe³Ì¦h¦³ 10 ¦C */
+
+#define MENU_XPOS 13 /* ¿ï³æ¶}©lªº (x, y) ®y¼Ð */
+#define MENU_YPOS ((d_cols >> 1) + 18)
+
+
+/* ----------------------------------------------------- */
+/* °T®§¦r¦ê¡G*_neck() ®Éªº necker ³£§ì¥X¨Ó©w¸q¦b³o */
+/* ----------------------------------------------------- */
+
+/* necker ªº¦æ¼Æ³£¬O¤G¦æ¡A±q (1, 0) ¨ì (2, 80) */
+
+/* ©Ò¦³ªº XZ_* ³£¦³ necker¡A¥u¬O¦³¨Ç¦b *_neck()¡A¦³¨ÇÂæb *_head() */
+
+/* ulist_neck() ¤Î xpost_head() ªº²Ä¤@¦æ¤ñ¸û¯S§O¡A¤£¦b¦¹©w¸q */
+
+#define NECKER_CLASS "[¡ö]¥D¿ï³æ [¡÷]¾\\Ū [¡ô¡õ]¿ï¾Ü [c]½g¼Æ [y]¸ü¤J [/?]·j´M [s]¬ÝªO [h]»¡©ú\n" \
+ COLOR3 " %s ¬Ý ªO Ãþ§OÂà«H ¤¤ ¤å ±Ô ­z%*s ¤H®ð ªO ¥D%*s \033[m"
+
+#define NECKER_ULIST "\n" \
+ COLOR3 " ½s¸¹ ¥N¸¹ ¼ÊºÙ%*s %-*s °ÊºA ¶¢¸m \033[m"
+
+#define NECKER_PAL "[¡ö]Â÷¶} [a]·s¼W [c]­×§ï [d]§R°£ [m]±H«H [w]¤ô²y [s]¾ã²z [¡÷]¬d¸ß [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¥N ¸¹ ¤Í ½Ë%*s \033[m"
+
+#define NECKER_ALOHA "[¡ö]Â÷¶} [a]·s¼W [d]§R°£ [D]°Ï¬q§R°£ [m]±H«H [w]¤ô²y [s]­«¾ã [f]¤Þ¤J [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¤W ¯¸ ³q ª¾ ¦W ³æ%*s \033[m"
+
+#define NECKER_VOTE "[¡ö]Â÷¶} [R]µ²ªG [^P]Á|¦æ [E]­×§ï [V]¹wÄý [^Q]§ï´Á [o]¦W³æ [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¶}²¼¤é ¥D¿ì¤H §ë ²¼ ©v ¦®%*s \033[m"
+
+#define NECKER_BMW "[¡ö]Â÷¶} [d]§R°£ [D]°Ï¬q§R°£ [m]±H«H [M]Àx¦s [w]¤ô²y [s]§ó·s [¡÷]¬d¸ß [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¥N ¸¹ ¤º ®e%*s ®É¶¡ \033[m"
+
+#define NECKER_MF "[¡ö]Â÷¶} [¡÷]¶i¤J [^P]·s¼W [d]§R°£ [c]¤Á´« [C]½Æ»s [^V]¶K¤W [m]²¾°Ê [h]»¡©ú\n" \
+ COLOR3 " %s ¬Ý ªO Ãþ§OÂà«H ¤¤ ¤å ±Ô ­z%*s ¤H®ð ªO ¥D%*s \033[m"
+
+#define NECKER_COSIGN "[¡ö]Â÷¶} [¡÷]¾\\Ū [^P]¥Ó½Ð [d]§R°£ [o]¶}ªO [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á Á|¿ì¤H ¬Ý ªO ¼Ð ÃD%*s \033[m"
+
+#define NECKER_SONG "[¡ö]Â÷¶} [¡÷]ÂsÄý [o]ÂIºq¨ì¬ÝªO [m]ÂIºq¨ì«H½c [Enter]ÂsÄý [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¥D ÃD%*s [½s ¿ï] [¤é ´Á]\033[m"
+
+#define NECKER_NEWS "[¡ö]Â÷¶} [¡÷]¾\\Ū [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á §@ ªÌ ·s »D ¼Ð ÃD%*s \033[m"
+
+#define NECKER_XPOST "\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á §@ ªÌ ¤å ³¹ ¼Ð ÃD%*s µû:%s \033[m"
+
+#define NECKER_MBOX "[¡ö]Â÷¶} [¡÷,r]Ū«H [d]§R°£ [R,y](¸s²Õ)¦^«H [s]±H«H [x]Âà¿ý [X]Âà¹F [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á §@ ªÌ «H ¥ó ¼Ð ÃD%*s \033[m"
+
+#define NECKER_POST "[¡ö]Â÷¶} [¡÷]¾\\Ū [^P]µoªí [b]¶iªOµe­± [d]§R°£ [V]§ë²¼ [TAB]ºëµØ°Ï [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á §@ ªÌ ¤å ³¹ ¼Ð ÃD%*s µû:%s ¤H®ð:%-4d \033[m"
+
+#define NECKER_GEM "[¡ö]Â÷¶} [¡÷]ÂsÄý [B]¼Ò¦¡ [C]¼È¦s [F]Âà±H [d]§R°£ [h]»¡©ú %s\n" \
+ COLOR3 " ½s¸¹ ¥D ÃD%*s [½s ¿ï] [¤é ´Á]\033[m"
+
+/* ¥H¤U³o¨Ç«h¬O¤@¨ÇÃþ XZ_* µ²ºcªº necker */
+
+#define NECKER_VOTEALL "[¡ô/¡õ]¤W¤U [PgUp/PgDn]¤W¤U­¶ [Home/End]­º§À [¡÷]§ë²¼ [¡ö][q]Â÷¶}\n" \
+ COLOR3 " ½s¸¹ ¬Ý ªO Ãþ§OÂà«H ¤¤ ¤å ±Ô ­z%*s ªO ¥D%*s \033[m"
+
+#define NECKER_CREDIT "[¡ö]Â÷¶} [C]´«­¶ [1]·s¼W [2]§R°£ [3]¥þ§R [4]Á`­p\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á ¦¬¤ä ª÷ ÃB ¤ÀÃþ »¡ ©ú%*s \033[m"
+
+#define NECKER_HELP "[¡ö]Â÷¶} [¡÷]¾\\Ū [^P]·s¼W [d]§R°£ [T]¼ÐÃD [E]½s¿è [m]²¾°Ê\n" \
+ COLOR3 " ½s¸¹ ÀÉ ®× ¼Ð ÃD%*s \033[m"
+
+#define NECKER_INNBBS "[¡ö]Â÷¶} [^P]·s¼W [d]§R°£ [E]½s¿è [/]·j´M [Enter]¸Ô²Ó\n" \
+ COLOR3 " ½s¸¹ ¤º ®e%*s \033[m"
+
+
+/* ----------------------------------------------------- */
+/* °T®§¦r¦ê¡Gmore() ®Éªº footer ³£§ì¥X¨Ó©w¸q¦b³o */
+/* ----------------------------------------------------- */
+
+/* itoc.010914.µù¸Ñ: ³æ¤@½g¡A©Ò¥H¥s FOOTER¡A³£¬O 78 char */
+
+/* itoc.010821: ª`·N \\ ¬O \¡A³Ì«á§Oº|¤F¤@­ÓªÅ¥ÕÁä :p */
+
+#define FOOTER_POST \
+COLOR1 " ¤å³¹¿ïŪ " COLOR2 " (ry)¦^À³ (=\\[]<>-+;'`)¥DÃD (|?QA)·j´M¼ÐÃD§@ªÌ (kj)¤W¤U½g (C)¼È¦s "
+
+#define FOOTER_MAILER \
+COLOR1 " ³½¶­©¹ªð " COLOR2 " (ry)¦^«H/¸s²Õ (X)Âà¹F (d)§R°£ (m)¼Ð°O (C)¼È¦s (=\\[]<>-+;'`|?QAkj) "
+
+#define FOOTER_GEM \
+COLOR1 " ºëµØ¿ïŪ " COLOR2 " (=\\[]<>-+;'`)¥DÃD (|?QA)·j´M¼ÐÃD§@ªÌ (kj)¤W¤U½g (¡ô¡õ¡ö)¤W¤UÂ÷¶} "
+
+#ifdef HAVE_GAME
+#define FOOTER_TALK \
+COLOR1 " ¥æ½Í¼Ò¦¡ " COLOR2 " (^O)¹ï«³¼Ò¦¡ (^C,^D)µ²§ô¥æ½Í (^T)¤Á´«©I¥s¾¹ (^Z)§Ö±¶¦Cªí (^G)¹Í¹Í "
+#else
+#define FOOTER_TALK \
+COLOR1 " ¥æ½Í¼Ò¦¡ " COLOR2 " (^C,^D)µ²§ô¥æ½Í (^T)¤Á´«©I¥s¾¹ (^Z)§Ö±¶¦Cªí (^G)¹Í¹Í (^Y)²M°£ "
+#endif
+
+#define FOOTER_COSIGN \
+COLOR1 " ³s¸p¾÷¨î " COLOR2 " (ry)¥[¤J³s¸p (kj)¤W¤U½g (¡ô¡õ¡ö)¤W¤UÂ÷¶} (h)»¡©ú "
+
+#define FOOTER_MORE \
+COLOR1 " ÂsÄý P.%d (%d%%) " COLOR2 " (h)»¡©ú [PgUp][PgDn][0][$]²¾°Ê (/n)·j´M (C)¼È¦s (¡öq)µ²§ô "
+
+#define FOOTER_VEDIT \
+COLOR1 " %s " COLOR2 " (^Z)»¡©ú (^W)²Å¸¹ (^L)­«Ã¸ (^X)Àɮ׳B²z ùø%s¢x%sùø%5d:%3d \033[m"
+
+
+/* ----------------------------------------------------- */
+/* °T®§¦r¦ê¡Gxo_foot() ®Éªº feeter ³£§ì¥X¨Ó©w¸q¦b³o */
+/* ----------------------------------------------------- */
+
+
+/* itoc.010914.µù¸Ñ: ¦Cªí¦h½g¡A©Ò¥H¥s FEETER¡A³£¬O 78 char */
+
+#define FEETER_CLASS \
+COLOR1 " ¬ÝªO¿ï¾Ü " COLOR2 " (c)·s¤å³¹ (vV)¼Ð°O¤wŪ¥¼Åª (y)¥þ³¡¦C¥X (z)¿ï­q (A)¥þ°ì·j´M (S)±Æ§Ç "
+
+#define FEETER_ULIST \
+COLOR1 " ºô¤Í¦Cªí " COLOR2 " (f)¦n¤Í (t)²á¤Ñ (q)¬d¸ß (ad)¥æ¤Í (m)±H«H (w)¤ô²y (s)§ó·s (TAB)¤Á´« "
+
+#define FEETER_PAL \
+COLOR1 " ©IªB¤Þ¦ñ " COLOR2 " (a)·s¼W (d)§R°£ (c)¤Í½Ë (m)±H«H (f)¤Þ¤J¦n¤Í (r^Q)¬d¸ß (s)§ó·s "
+
+#define FEETER_ALOHA \
+COLOR1 " ¤W¯¸³qª¾ " COLOR2 " (a)·s¼W (d)§R°£ (D)°Ï¬q§R°£ (f)¤Þ¤J¦n¤Í (r^Q)¬d¸ß (s)§ó·s "
+
+#define FEETER_VOTE \
+COLOR1 " ¬ÝªO§ë²¼ " COLOR2 " (¡÷/r/v)§ë²¼ (R)µ²ªG (^P)·s¼W§ë²¼ (E)­×§ï (V)¹wÄý (b)¶}²¼ (o)¦W³æ "
+
+#define FEETER_BMW \
+COLOR1 " ¤ô²y¦^ÅU " COLOR2 " (d)§R°£ (D)°Ï¬q§R°£ (m)±H«H (w)¤ô²y (^R)¦^°T (^Q)¬d¸ß (s)§ó·s "
+
+#define FEETER_MF \
+COLOR1 " ³Ì·R¬ÝªO " COLOR2 " (^P)·s¼W (Cg)½Æ»s (p^V)¶K¤W (d)§R°£ (c)·s¤å³¹ (vV)¼Ð°O¤wŪ/¥¼Åª "
+
+#define FEETER_COSIGN \
+COLOR1 " ³s¸p¤p¯¸ " COLOR2 " (r)Ū¨ú (y)¦^À³ (^P)µoªí (d)§R°£ (o)¶}ªO (c)Ãö³¬ (E)½s¿è (B)³]©w "
+
+#define FEETER_SONG \
+COLOR1 " ÂIºq¨t²Î " COLOR2 " (r)Ū¨ú (o)ÂIºq¨ì¬ÝªO (m)ÂIºq¨ì«H½c (E)½s¿èÀÉ®× (T)½s¿è¼ÐÃD "
+
+#define FEETER_NEWS \
+COLOR1 " ·s»DÂI¿ï " COLOR2 " (¡ô/¡õ)¤W¤U (PgUp/PgDn)¤W¤U­¶ (Home/End)­º§À (¡÷r)¿ï¨ú (¡ö)(q)Â÷¶} "
+
+#define FEETER_XPOST \
+COLOR1 " ¦ê¦C·j´M " COLOR2 " (y)¦^À³ (x)Âà¿ý (m)¼Ð°O (d)§R°£ (^P)µoªí (^Q)¬d¸ß§@ªÌ (t)¼ÐÅÒ "
+
+#define FEETER_MBOX \
+COLOR1 " «H«H¬Û±¤ " COLOR2 " (y)¦^«H (F/X/x)Âà±H/Âà¹F/Âà¿ý (d)§R°£ (D)°Ï¬q§R°£ (m)¼Ð°O (E)½s¿è "
+
+#define FEETER_POST \
+COLOR1 " ¤å³¹¦Cªí " COLOR2 " (ry)¦^«H (S/a)·j´M/¼ÐÃD/§@ªÌ (~G)¦ê¦C·j´M (x)Âà¿ý (V)§ë²¼ (u)·s»D "
+
+#define FEETER_GEM \
+COLOR1 " ¬ÝªOºëµØ " COLOR2 " (^P/a/f)·s¼W/¤å³¹/¥Ø¿ý (E)½s¿è (T)¼ÐÃD (m)²¾°Ê (c)½Æ»s (p^V)¶K¤W "
+
+#define FEETER_VOTEALL \
+COLOR1 " §ë²¼¤¤¤ß " COLOR2 " (¡ô/¡õ)¤W¤U (PgUp/PgDn)¤W¤U­¶ (Home/End)­º§À (¡÷)§ë²¼ (¡ö)(q)Â÷¶} "
+
+#define FEETER_HELP \
+COLOR1 " »¡©ú¤å¥ó " COLOR2 " (¡ô/¡õ)¤W¤U (PgUp/PgDn)¤W¤U­¶ (Home/End)­º§À (¡÷r)ÂsÄý (¡ö)(q)Â÷¶} "
+
+#define FEETER_INNBBS \
+COLOR1 " Âà«H³]©w " COLOR2 " (¡ô/¡õ)¤W¤U (PgUp/PgDn)¤W¤U­¶ (Home/End)­º§À (¡ö)(q)Â÷¶} "
+
+
+/* ----------------------------------------------------- */
+/* ¯¸¥x¨Ó·½Ã±¦W */
+/* ----------------------------------------------------- */
+
+/* itoc: «ØÄ³ banner ¤£­n¶W¹L¤T¦æ¡A¹Lªøªº¯¸Ã±¥i¯à·|³y¦¨¬Y¨Ç¨Ï¥ÎªÌªº¤Ï·P */
+
+#define EDIT_BANNER "\n--\n" \
+ " \033[1;41mùÝ\033[44mùß\033[m O\033[1mri\033[30mgi\033[mn: \033[1;42m "SCHOOLNAME"£»"BBSNAME" \033[44m "MYHOSTNAME" \033[m\n" \
+ " \033[1;42mùã\033[45mùå\033[m Au\033[1mt\033[30mho\033[mr: \033[1;32m%s\033[m ±q \033[1;35m%s\033[m µoªí\n"
+
+#define MODIFY_BANNER " \033[1;43m¢¬\033[46m¢­\033[m \033[1mMo\033[30mdi\033[mfy: %s ©ó \033[1;32m%s\033[m ­×§ï\n"
+
+
+/* ----------------------------------------------------- */
+/* ¨ä¥L°T®§¦r¦ê */
+/* ----------------------------------------------------- */
+
+#define VMSG_NULL " \033[1;32;45m¡´ ½Ð«ö¥ô·NÁäÄ~Äò ¡´\033[m"
+
+#define ICON_UNREAD_BRD "\033[32m£¾" /* ¥¼Åª¬ÝªO */
+#define ICON_READ_BRD " " /* ¤wŪ¬ÝªO */
+
+#define ICON_GAMBLED_BRD "\033[1;31m½ä\033[m" /* Á|¦æ½ä½L¤¤ªº¬ÝªO */
+#define ICON_VOTED_BRD "\033[1;33m§ë\033[m" /* Á|¦æ§ë²¼¤¤ªº¬ÝªO */
+#define ICON_NOTRAN_BRD "¡·" /* ¤£Âà«HªO */
+#define ICON_TRAN_BRD "¡´" /* Âà«HªO */
+
+#define TOKEN_ZAP_BRD '-' /* zap ªO */
+#define TOKEN_FRIEND_BRD '.' /* ¦n¤ÍªO */
+#define TOKEN_SECRET_BRD ')' /* ¯µ±KªO */
+
+#endif /* _THEME_H_ */
diff --git a/include/theme/theme_neck.h b/include/theme/theme_neck.h
new file mode 100644
index 0000000..7ca15d3
--- /dev/null
+++ b/include/theme/theme_neck.h
@@ -0,0 +1,246 @@
+/*-------------------------------------------------------*/
+/* theme.h ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : custom theme */
+/* create : 02/08/17 */
+/* update : / / */
+/*-------------------------------------------------------*/
+
+
+#ifndef _THEME_H_
+#define _THEME_H_
+
+
+/* ----------------------------------------------------- */
+/* °ò¥»ÃC¦â©w¸q¡A¥H§Q¤¶­±­×§ï */
+/* ----------------------------------------------------- */
+
+#define COLOR1 "\033[1;37;44m" /* footer/feeter ªº«e¬qÃC¦â */
+#define COLOR2 "\033[0;30;47m" /* footer/feeter ªº«á¬qÃC¦â */
+#define COLOR3 "\033[30;47m" /* neck ªºÃC¦â */
+#define COLOR4 "\033[1;44m" /* ¥ú´Î ªºÃC¦â */
+#define COLOR5 "\033[34;47m" /* more ÀÉÀYªº¼ÐÃDÃC¦â */
+#define COLOR6 "\033[37;44m" /* more ÀÉÀYªº¤º®eÃC¦â */
+#define COLOR7 "\033[1m" /* §@ªÌ¦b½u¤WªºÃC¦â */
+
+
+/* ----------------------------------------------------- */
+/* ¨Ï¥ÎªÌ¦W³æÃC¦â */
+/* ----------------------------------------------------- */
+
+#define COLOR_NORMAL "" /* ¤@¯ë¨Ï¥ÎªÌ */
+#define COLOR_MYBAD "\033[1;31m" /* Ãa¤H */
+#define COLOR_MYGOOD "\033[1;32m" /* §Úªº¦n¤Í */
+#define COLOR_OGOOD "\033[1;33m" /* »P§Ú¬°¤Í */
+#define COLOR_CLOAK "\033[1;35m" /* Áô§Î */ /* itoc.µù¸Ñ: ¨S¥Î¨ì¡A­nªº¤H½Ð¦Û¦æ¥[¤J ulist_body() */
+#define COLOR_SELF "\033[1;36m" /* ¦Û¤v */
+#define COLOR_BOTHGOOD "\033[1;37m" /* ¤¬³]¦n¤Í */
+#define COLOR_BRDMATE "\033[36m" /* ªO¦ñ */
+
+
+/* ----------------------------------------------------- */
+/* ¿ï³æ¦ì¸m */
+/* ----------------------------------------------------- */
+
+/* itoc.µù¸Ñ: ª`·N MENU_XPOS ­n >= MENU_XNOTE + MOVIE_LINES */
+
+#define MENU_XNOTE 2 /* °ÊºA¬ÝªO¥Ñ (2, 0) ¶}©l */
+#define MOVIE_LINES 10 /* °Êµe³Ì¦h¦³ 10 ¦C */
+
+#define MENU_XPOS 13 /* ¿ï³æ¶}©lªº (x, y) ®y¼Ð */
+#define MENU_YPOS ((d_cols >> 1) + 18)
+
+
+/* ----------------------------------------------------- */
+/* °T®§¦r¦ê¡G*_neck() ®Éªº necker ³£§ì¥X¨Ó©w¸q¦b³o */
+/* ----------------------------------------------------- */
+
+/* necker ªº¦æ¼Æ³£¬O¤G¦æ¡A±q (1, 0) ¨ì (2, 80) */
+
+/* ©Ò¦³ªº XZ_* ³£¦³ necker¡A¥u¬O¦³¨Ç¦b *_neck()¡A¦³¨ÇÂæb *_head() */
+
+/* ulist_neck() ¤Î xpost_head() ªº²Ä¤@¦æ¤ñ¸û¯S§O¡A¤£¦b¦¹©w¸q */
+
+#define NECKER_CLASS "\033[40m¢¨\033[30;47m¬ÝªO\033[37;46m¢©¤å³¹¢@ºëµØ\033[36;40m¢©\033[37m S)±Æ§Ç c)·s¤å³¹¼Ò¦¡ v|V)¼Ð°O¤wŪ|¥¼Åª h)»¡©ú\033[m\n" \
+ COLOR3 " %s ¬Ý ªO Ãþ§OÂà«H ¤¤ ¤å ±Ô ­z%*s ¤H®ð ªO ¥D%*s \033[m"
+
+#define NECKER_ULIST "\n" \
+ COLOR3 " ½s¸¹ ¥N¸¹ ¼ÊºÙ%*s %-*s °ÊºA ¶¢¸m \033[m"
+
+#define NECKER_PAL "\033[30;46m¢«\033[37m¤ô²y¢¨\033[30;47m¦n¤Í\033[37;46m¢©¤W¯¸\033[36;40m¢©\033[37m a)·s¼W c)­×§ï d)§R°£ s)¾ã²z m)±H«H w)¤ô²y h)»¡©ú\033[m\n" \
+ COLOR3 " ½s¸¹ ¥N ¸¹ ¤Í ½Ë%*s \033[m"
+
+#define NECKER_ALOHA "\033[30;46m¢«\033[37m¤ô²y¢@¦n¤Í¢¨\033[30;47m¤W¯¸\033[37;40m¢© a)·s¼W c)­×§ï d|D)§R°£ f)¤Þ¤J m)±H«H w)¤ô²y h)»¡©ú\033[m\n" \
+ COLOR3 " ½s¸¹ ¤W ¯¸ ³q ª¾ ¦W ³æ%*s \033[m"
+
+#define NECKER_VOTE "\033[40m¢¨\033[30;47m§ë²¼\033[37;46m¢©¤¤¤ß¢@³s¸p\033[36;40m¢©\033[37m R)µ²ªG ^P|^Q)Á|¿ì|§ï´Á V)¹wÄý E)½s¿è h)»¡©ú\033[m\n" \
+ COLOR3 " ½s¸¹ ¶}²¼¤é ¥D¿ì¤H §ë ²¼ ©v ¦®%*s \033[m"
+
+#define NECKER_BMW "\033[40m¢¨\033[30;47m¤ô²y\033[37;46m¢©¦n¤Í¢@¤W¯¸\033[36;40m¢©\033[37m s)§ó·s w)¤ô²y m)±H«H d|D)§R°£ ¡÷)¬d¸ß h)»¡©ú\033[m\n" \
+ COLOR3 " ½s¸¹ ¥N ¸¹ ¤º ®e%*s ®É¶¡ \033[m"
+
+#define NECKER_MF "\033[40m¢¨\033[30;47m³Ì·R\033[37;46m¢©·s»D¢@ÂIºq\033[36;40m¢©\033[37m ^P)·s¼W d)§R°£ c)¤Á´« C|^V)½Æ»s|¶K¤W m)²¾°Ê h)»¡©ú\033[m\n" \
+ COLOR3 " %s ¬Ý ªO Ãþ§OÂà«H ¤¤ ¤å ±Ô ­z%*s ¤H®ð ªO ¥D%*s \033[m"
+
+#define NECKER_COSIGN "\033[30;46m¢«\033[37m§ë²¼¢@¤¤¤ß¢¨\033[30;47m³s¸p\033[37;40m¢© ^P)µoªí d)§R°£ y)¥[¤J h)»¡©ú\033[m\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á Á|¿ì¤H ¬Ý ªO ¼Ð ÃD%*s \033[m"
+
+#define NECKER_SONG "\033[30;46m¢«\033[37m³Ì·R¢@·s»D¢¨\033[30;47mÂIºq\033[37;40m¢© o|m)ÂIºq|¬ÝªO|«H½c ¡÷)ÂsÄý h)»¡©ú\033[m\n" \
+ COLOR3 " ½s¸¹ ¥D ÃD%*s [½s ¿ï] [¤é ´Á]\033[m"
+
+#define NECKER_NEWS "\033[30;46m¢«\033[37m³Ì·R¢¨\033[30;47m·s»D\033[37;46m¢©ÂIºq\033[36;40m¢©\033[37m ¡÷)ÂsÄý h)»¡©ú\033[m\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á §@ ªÌ ·s »D ¼Ð ÃD%*s \033[m"
+
+#define NECKER_XPOST "\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á §@ ªÌ ¤å ³¹ ¼Ð ÃD%*s µû:%s \033[m"
+
+#define NECKER_MBOX "\033[40m¢¨\033[30;47m¶l§½\033[37;46m¢©»È¦æ¢@±b¤á\033[36;40m¢©\033[37m d)§R°£ R|y)¸s²Õ|¦^«H s)±H«H x|X)Âà¿ý|Âà¹F h)»¡©ú\033[m\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á §@ ªÌ «H ¥ó ¼Ð ÃD%*s \033[m"
+
+#define NECKER_POST "\033[30;46m¢«\033[37m¬ÝªO¢¨\033[30;47m¤å³¹\033[37;46m¢©ºëµØ\033[36;40m¢©\033[37m S|a|/)·j´M|§@ªÌ|¼ÐÃD ^P)µoªí z)ºëµØ°Ï h)»¡©ú\033[m\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á §@ ªÌ ¤å ³¹ ¼Ð ÃD%*s µû:%s ¤H®ð:%-4d \033[m"
+
+#define NECKER_GEM "\033[30;46m¢«\033[37m¬ÝªO¢@¤å³¹¢¨\033[30;47mºëµØ\033[37;40m¢© %21.21s B)¼Ò¦¡ C)¼È¦s F)Âà±H h)»¡©ú\033[m\n" \
+ COLOR3 " ½s¸¹ ¥D ÃD%*s [½s ¿ï] [¤é ´Á]\033[m"
+
+/* ¥H¤U³o¨Ç«h¬O¤@¨ÇÃþ XZ_* µ²ºcªº necker */
+
+#define NECKER_VOTEALL "\033[30;46m¢«\033[37m§ë²¼¢¨\033[30;47m¤¤¤ß\033[37;46m¢©³s¸p\033[36;40m¢©\033[37m ¡÷)§ë²¼ h)»¡©ú\033[m\n" \
+ COLOR3 " ½s¸¹ ¬Ý ªO Ãþ§OÂà«H ¤¤ ¤å ±Ô ­z%*s ªO ¥D%*s \033[m"
+
+#define NECKER_CREDIT "\033[30;46m¢«\033[37m¶l§½¢@»È¦æ¢¨\033[30;47m±b¤á\033[37;40m¢©\033[m\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á ¦¬¤ä ª÷ ÃB ¤ÀÃþ »¡ ©ú%*s \033[m"
+
+#define NECKER_HELP "\033[30;46m¢«\033[37m»¡©ú¢¨\033[30;47mªA°È\033[37;46m¢©´©§U\033[36;40m¢©\033[37m T)¼ÐÃD E)½s¿è m)²¾°Ê ^P)·s¼W d)§R°£\033[m\n" \
+ COLOR3 " ½s¸¹ ÀÉ ®× ¼Ð ÃD%*s \033[m"
+
+#define NECKER_INNBBS "\033[30;46m¢«\033[37mÂà«H¢¨\033[30;47mÂà«H\033[37;46m¢©Âà«H\033[36;40m¢©\033[37m ^P)·s¼W d)§R°£ E½s¿è /)·j´M Enter)¸Ô²Ó\033[m\n" \
+ COLOR3 " ½s¸¹ ¤º ®e%*s \033[m"
+
+
+/* ----------------------------------------------------- */
+/* °T®§¦r¦ê¡Gmore() ®Éªº footer ³£§ì¥X¨Ó©w¸q¦b³o */
+/* ----------------------------------------------------- */
+
+/* itoc.010914.µù¸Ñ: ³æ¤@½g¡A©Ò¥H¥s FOOTER¡A³£¬O 78 char */
+
+/* itoc.010821: ª`·N \\ ¬O \¡A³Ì«á§Oº|¤F¤@­ÓªÅ¥ÕÁä :p */
+
+#define FOOTER_POST \
+COLOR1 " ¤å³¹¿ïŪ " COLOR2 " (ry)¦^À³ (=\\[]<>-+;'`)¥DÃD (|?QA)·j´M¼ÐÃD§@ªÌ (kj)¤W¤U½g (C)¼È¦s "
+
+#define FOOTER_MAILER \
+COLOR1 " ³½¶­©¹ªð " COLOR2 " (ry)¸s²Õ/¦^«H (X)Âà¹F (d)§R°£ (m)¼Ð°O (C)¼È¦s (=\\[]<>-+;'`|?QAkj)"
+
+#define FOOTER_GEM \
+COLOR1 " ºëµØ¿ïŪ " COLOR2 " (=\\[]<>-+;'`)¥DÃD (|?QA)·j´M¼ÐÃD§@ªÌ (kj)¤W¤U½g (¡ô¡õ¡ö)¤W¤UÂ÷¶} "
+
+#ifdef HAVE_GAME
+#define FOOTER_TALK \
+COLOR1 " ¥æ½Í¼Ò¦¡ " COLOR2 " (^O)¹ï«³ (^C,^D)µ²§ô¥æ½Í (^T)¤Á´«©I¥s¾¹ (^Z)§Ö±¶¦Cªí (^G)¹Í¹Í "
+#else
+#define FOOTER_TALK \
+COLOR1 " ¥æ½Í¼Ò¦¡ " COLOR2 " (^C,^D)µ²§ô¥æ½Í (^T)¤Á´«©I¥s¾¹ (^Z)§Ö±¶¦Cªí (^G)¹Í¹Í (^Y)²M°£ "
+#endif
+
+#define FOOTER_COSIGN \
+COLOR1 " ³s¸p¾÷¨î " COLOR2 " (ry)¥[¤J³s¸p (kj)¤W¤U½g (¡ô¡õ¡ö)¤W¤UÂ÷¶} (h)»¡©ú "
+
+#define FOOTER_MORE \
+COLOR1 " ÂsÄý P.%d (%d%%) " COLOR2 " (h)»¡©ú [PgUp][PgDn][0][$]²¾°Ê (/n)·j´M (C)¼È¦s (¡öq)µ²§ô "
+
+#define FOOTER_VEDIT \
+COLOR1 " %s " COLOR2 " (^Z)»¡©ú (^W)²Å¸¹ (^L)­«Ã¸ (^X)Àɮ׳B²z ùø%s¢x%sùø%5d:%3d \033[m"
+
+
+/* ----------------------------------------------------- */
+/* °T®§¦r¦ê¡Gxo_foot() ®Éªº feeter ³£§ì¥X¨Ó©w¸q¦b³o */
+/* ----------------------------------------------------- */
+
+
+/* itoc.010914.µù¸Ñ: ¦Cªí¦h½g¡A©Ò¥H¥s FEETER¡A³£¬O 78 char */
+
+#define FEETER_CLASS \
+COLOR1 " ¬ÝªO¿ï¾Ü " COLOR2 " (c)·s¤å³¹ (vV)¤wŪ¥¼Åª (y)¥þ³¡¦C¥X (z)¿ï­q (A)¥þ°ì·j´M (S)±Æ§Ç "
+
+#define FEETER_ULIST \
+COLOR1 " ºô¤Í¦Cªí " COLOR2 " (f)¦n¤Í (t)²á¤Ñ (q)¬d¸ß (ad)¥æ¤Í (w)¤ô²y (s)§ó·s (TAB)¤Á´« "
+
+#define FEETER_PAL \
+COLOR1 " ©IªB¤Þ¦ñ " COLOR2 " (a)·s¼W (d)§R°£ (c)¤Í½Ë (m)±H«H (f)¤Þ¤J¦n¤Í (r^Q)¬d¸ß (s)§ó·s "
+
+#define FEETER_ALOHA \
+COLOR1 " ¤W¯¸³qª¾ " COLOR2 " (a)·s¼W (d)§R°£ (D)°Ï¬q§R°£ (f)¤Þ¤J¦n¤Í (r^Q)¬d¸ß (s)§ó·s "
+
+#define FEETER_VOTE \
+COLOR1 " ¬ÝªO§ë²¼ " COLOR2 " (¡÷/r/v)§ë²¼ (R)µ²ªG (^P)·s¼W§ë²¼ (E)­×§ï (V)¹wÄý (b)¶}²¼ (o)¦W³æ "
+
+#define FEETER_BMW \
+COLOR1 " ¤ô²y¦^ÅU " COLOR2 " (d)§R°£ (D)°Ï¬q§R°£ (m)±H«H (w)¤ô²y (^R)¦^°T (^Q)¬d¸ß (s)§ó·s "
+
+#define FEETER_MF \
+COLOR1 " ³Ì·R¬ÝªO " COLOR2 " (^P)·s¼W (Cg)½Æ»s (p^V)¶K¤W (d)§R°£ (c)·s¤å³¹ (vV)¼Ð°O¤wŪ/¥¼Åª "
+
+#define FEETER_COSIGN \
+COLOR1 " ³s¸p¤p¯¸ " COLOR2 " (r)Ū¨ú (y)¦^À³ (^P)µoªí (d)§R°£ (o)¶}ªO (c)Ãö³¬ (E)½s¿è (B)³]©w "
+
+#define FEETER_SONG \
+COLOR1 " ÂIºq¨t²Î " COLOR2 " (r)Ū¨ú (o)ÂIºq¨ì¬ÝªO (m)ÂIºq¨ì«H½c (E)½s¿èÀÉ®× (T)½s¿è¼ÐÃD "
+
+#define FEETER_NEWS \
+COLOR1 " ·s»DÂI¿ï " COLOR2 " (¡ô/¡õ)¤W¤U (PgUp/PgDn)¤W¤U­¶ (Home/End)­º§À (¡÷r)¿ï¨ú (¡ö)Â÷¶} "
+
+#define FEETER_XPOST \
+COLOR1 " ¦ê¦C·j´M " COLOR2 " (y)¦^À³ (x)Âà¿ý (m)¼Ð°O (d)§R°£ (^P)µoªí (^Q)¬d¸ß§@ªÌ (t)¼ÐÅÒ "
+
+#define FEETER_MBOX \
+COLOR1 " «H«H¬Û±¤ " COLOR2 " (y)¦^«H (F/X/x)Âà±H/Âà¹F/Âà¿ý (d)§R°£ (D)°Ï¬q§R°£ (m)¼Ð°O "
+
+#define FEETER_POST \
+COLOR1 " ¤å³¹¦Cªí " COLOR2 " (ry)¦^«H (S/a)·j´M/¼ÐÃD/§@ªÌ (~G)¦ê¦C (x)Âà¿ý (V)§ë²¼ (u)·s»D "
+
+#define FEETER_GEM \
+COLOR1 " ¬ÝªOºëµØ " COLOR2 " (^P/a/f)·s¼W/¤å³¹/¥Ø¿ý (E)½s¿è (T)¼ÐÃD (m)²¾°Ê (c)½Æ»s (p^V)¶K¤W "
+
+#define FEETER_VOTEALL \
+COLOR1 " §ë²¼¤¤¤ß " COLOR2 " (¡ô/¡õ)¤W¤U (PgUp/PgDn)¤W¤U­¶ (Home/End)­º§À (¡÷)§ë²¼ (¡ö)Â÷¶} "
+
+#define FEETER_HELP \
+COLOR1 " »¡©ú¤å¥ó " COLOR2 " (¡ô/¡õ)¤W¤U (PgUp/PgDn)¤W¤U­¶ (Home/End)­º§À (¡÷r)ÂsÄý (¡ö)Â÷¶} "
+
+#define FEETER_INNBBS \
+COLOR1 " Âà«H³]©w " COLOR2 " (¡ô/¡õ)¤W¤U (PgUp/PgDn)¤W¤U­¶ (Home/End)­º§À (¡ö)(q)Â÷¶} "
+
+
+/* ----------------------------------------------------- */
+/* ¯¸¥x¨Ó·½Ã±¦W */
+/* ----------------------------------------------------- */
+
+/* itoc: «ØÄ³ banner ¤£­n¶W¹L¤T¦æ¡A¹Lªøªº¯¸Ã±¥i¯à·|³y¦¨¬Y¨Ç¨Ï¥ÎªÌªº¤Ï·P */
+
+#define EDIT_BANNER "\n--\n" \
+ " \033[1;41m ¯¸ ¥x \033[40;33m "SCHOOLNAME"£»"BBSNAME" \033[35m¡m"MYHOSTNAME"¡n\033[m\n" \
+ " \033[1;42m ¨Ó ·½ \033[40;36m %.0s%s\033[m\n"
+
+#define MODIFY_BANNER " \033[1;44m ½s ¿è \033[40;37m %.0s%s\033[m\n"
+
+
+/* ----------------------------------------------------- */
+/* ¨ä¥L°T®§¦r¦ê */
+/* ----------------------------------------------------- */
+
+#define VMSG_NULL " \033[1;36m ¢j¢k¢l¢m¢n¢o¢p ½Ð«ö¥ô·NÁäÄ~Äò ¢p\033[m"
+
+#define ICON_UNREAD_BRD "\033[33m¡÷" /* ¥¼Åª¬ÝªO */
+#define ICON_READ_BRD " " /* ¤wŪ¬ÝªO */
+
+#define ICON_GAMBLED_BRD "\033[1;31m½ä\033[m" /* Á|¦æ½ä½L¤¤ªº¬ÝªO */
+#define ICON_VOTED_BRD "\033[1;33m§ë\033[m" /* Á|¦æ§ë²¼¤¤ªº¬ÝªO */
+#define ICON_NOTRAN_BRD "¡¸" /* ¤£Âà«HªO */
+#define ICON_TRAN_BRD "¡¹" /* Âà«HªO */
+
+#define TOKEN_ZAP_BRD '-' /* zap ªO */
+#define TOKEN_FRIEND_BRD '.' /* ¦n¤ÍªO */
+#define TOKEN_SECRET_BRD ')' /* ¯µ±KªO */
+
+#endif /* _THEME_H_ */
diff --git a/include/theme/theme_pack.h b/include/theme/theme_pack.h
new file mode 100644
index 0000000..82722a7
--- /dev/null
+++ b/include/theme/theme_pack.h
@@ -0,0 +1,246 @@
+/*-------------------------------------------------------*/
+/* theme.h ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : custom theme */
+/* create : 02/08/17 */
+/* update : / / */
+/*-------------------------------------------------------*/
+
+
+#ifndef _THEME_H_
+#define _THEME_H_
+
+
+/* ----------------------------------------------------- */
+/* °ò¥»ÃC¦â©w¸q¡A¥H§Q¤¶­±­×§ï */
+/* ----------------------------------------------------- */
+
+#define COLOR1 "\033[34;46m" /* footer/feeter ªº«e¬qÃC¦â */
+#define COLOR2 "\033[0;30;47m" /* footer/feeter ªº«á¬qÃC¦â */
+#define COLOR3 "\033[30;47m" /* neck ªºÃC¦â */
+#define COLOR4 "\033[1;44m" /* ¥ú´Î ªºÃC¦â */
+#define COLOR5 "\033[34;47m" /* more ÀÉÀYªº¼ÐÃDÃC¦â */
+#define COLOR6 "\033[37;44m" /* more ÀÉÀYªº¤º®eÃC¦â */
+#define COLOR7 "\033[36m" /* §@ªÌ¦b½u¤WªºÃC¦â */
+
+
+/* ----------------------------------------------------- */
+/* ¨Ï¥ÎªÌ¦W³æÃC¦â */
+/* ----------------------------------------------------- */
+
+#define COLOR_NORMAL "" /* ¤@¯ë¨Ï¥ÎªÌ */
+#define COLOR_MYBAD "\033[1;31m" /* Ãa¤H */
+#define COLOR_MYGOOD "\033[1;32m" /* §Úªº¦n¤Í */
+#define COLOR_OGOOD "\033[1;33m" /* »P§Ú¬°¤Í */
+#define COLOR_CLOAK "\033[1;35m" /* Áô§Î */ /* itoc.µù¸Ñ: ¨S¥Î¨ì¡A­nªº¤H½Ð¦Û¦æ¥[¤J ulist_body() */
+#define COLOR_SELF "\033[1;36m" /* ¦Û¤v */
+#define COLOR_BOTHGOOD "\033[1;37m" /* ¤¬³]¦n¤Í */
+#define COLOR_BRDMATE "\033[36m" /* ªO¦ñ */
+
+
+/* ----------------------------------------------------- */
+/* ¿ï³æ¦ì¸m */
+/* ----------------------------------------------------- */
+
+/* itoc.µù¸Ñ: ª`·N MENU_XPOS ­n >= MENU_XNOTE + MOVIE_LINES */
+
+#define MENU_XNOTE 2 /* °ÊºA¬ÝªO¥Ñ (2, 0) ¶}©l */
+#define MOVIE_LINES 10 /* °Êµe³Ì¦h¦³ 10 ¦C */
+
+#define MENU_XPOS 13 /* ¿ï³æ¶}©lªº (x, y) ®y¼Ð */
+#define MENU_YPOS ((d_cols >> 1) + 18)
+
+
+/* ----------------------------------------------------- */
+/* °T®§¦r¦ê¡G*_neck() ®Éªº necker ³£§ì¥X¨Ó©w¸q¦b³o */
+/* ----------------------------------------------------- */
+
+/* necker ªº¦æ¼Æ³£¬O¤G¦æ¡A±q (1, 0) ¨ì (2, 80) */
+
+/* ©Ò¦³ªº XZ_* ³£¦³ necker¡A¥u¬O¦³¨Ç¦b *_neck()¡A¦³¨ÇÂæb *_head() */
+
+/* ulist_neck() ¤Î xpost_head() ªº²Ä¤@¦æ¤ñ¸û¯S§O¡A¤£¦b¦¹©w¸q */
+
+#define NECKER_CLASS "[¡ö]¥D¿ï³æ [¡÷]¾\\Ū [¡ô¡õ]¿ï¾Ü [c]½g¼Æ [y]¸ü¤J [/?]·j´M [s]¬ÝªO [h]»¡©ú\n" \
+ COLOR3 " %s ¬Ý ªO Ãþ§OÂà«H ¤¤ ¤å ±Ô ­z%*s ¤H®ð ªO ¥D%*s \033[m"
+
+#define NECKER_ULIST "\n" \
+ COLOR3 " ½s¸¹ ¥N¸¹ ¼ÊºÙ%*s %-*s °ÊºA ¶¢¸m \033[m"
+
+#define NECKER_PAL "[¡ö]Â÷¶} [a]·s¼W [c]­×§ï [d]§R°£ [m]±H«H [w]¤ô²y [s]¾ã²z [¡÷]¬d¸ß [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¥N ¸¹ ¤Í ½Ë%*s \033[m"
+
+#define NECKER_ALOHA "[¡ö]Â÷¶} [a]·s¼W [d]§R°£ [D]°Ï¬q§R°£ [m]±H«H [w]¤ô²y [s]­«¾ã [f]¤Þ¤J [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¤W ¯¸ ³q ª¾ ¦W ³æ%*s \033[m"
+
+#define NECKER_VOTE "[¡ö]Â÷¶} [R]µ²ªG [^P]Á|¦æ [E]­×§ï [V]¹wÄý [^Q]§ï´Á [o]¦W³æ [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¶}²¼¤é ¥D¿ì¤H §ë ²¼ ©v ¦®%*s \033[m"
+
+#define NECKER_BMW "[¡ö]Â÷¶} [d]§R°£ [D]°Ï¬q§R°£ [m]±H«H [M]Àx¦s [w]¤ô²y [s]§ó·s [¡÷]¬d¸ß [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¥N ¸¹ ¤º ®e%*s ®É¶¡ \033[m"
+
+#define NECKER_MF "[¡ö]Â÷¶} [¡÷]¶i¤J [^P]·s¼W [d]§R°£ [c]¤Á´« [C]½Æ»s [^V]¶K¤W [m]²¾°Ê [h]»¡©ú\n" \
+ COLOR3 " %s ¬Ý ªO Ãþ§OÂà«H ¤¤ ¤å ±Ô ­z%*s ¤H®ð ªO ¥D%*s \033[m"
+
+#define NECKER_COSIGN "[¡ö]Â÷¶} [¡÷]¾\\Ū [^P]¥Ó½Ð [d]§R°£ [o]¶}ªO [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á Á|¿ì¤H ¬Ý ªO ¼Ð ÃD%*s \033[m"
+
+#define NECKER_SONG "[¡ö]Â÷¶} [¡÷]ÂsÄý [o]ÂIºq¨ì¬ÝªO [m]ÂIºq¨ì«H½c [Enter]ÂsÄý [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¥D ÃD%*s [½s ¿ï] [¤é ´Á]\033[m"
+
+#define NECKER_NEWS "[¡ö]Â÷¶} [¡÷]¾\\Ū [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á §@ ªÌ ·s »D ¼Ð ÃD%*s \033[m"
+
+#define NECKER_XPOST "\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á §@ ªÌ ¤å ³¹ ¼Ð ÃD%*s µû:%s \033[m"
+
+#define NECKER_MBOX "[¡ö]Â÷¶} [¡÷,r]Ū«H [d]§R°£ [R,y](¸s²Õ)¦^«H [s]±H«H [x]Âà¿ý [X]Âà¹F [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á §@ ªÌ «H ¥ó ¼Ð ÃD%*s \033[m"
+
+#define NECKER_POST "[¡ö]Â÷¶} [¡÷]¾\\Ū [^P]µoªí [b]¶iªOµe­± [d]§R°£ [V]§ë²¼ [TAB]ºëµØ°Ï [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á §@ ªÌ ¤å ³¹ ¼Ð ÃD%*s µû:%s ¤H®ð:%-4d \033[m"
+
+#define NECKER_GEM "[¡ö]Â÷¶} [¡÷]ÂsÄý [B]¼Ò¦¡ [C]¼È¦s [F]Âà±H [d]§R°£ [h]»¡©ú %s\n" \
+ COLOR3 " ½s¸¹ ¥D ÃD%*s [½s ¿ï] [¤é ´Á]\033[m"
+
+/* ¥H¤U³o¨Ç«h¬O¤@¨ÇÃþ XZ_* µ²ºcªº necker */
+
+#define NECKER_VOTEALL "[¡ô/¡õ]¤W¤U [PgUp/PgDn]¤W¤U­¶ [Home/End]­º§À [¡÷]§ë²¼ [¡ö][q]Â÷¶}\n" \
+ COLOR3 " ½s¸¹ ¬Ý ªO Ãþ§OÂà«H ¤¤ ¤å ±Ô ­z%*s ªO ¥D%*s \033[m"
+
+#define NECKER_CREDIT "[¡ö]Â÷¶} [C]´«­¶ [1]·s¼W [2]§R°£ [3]¥þ§R [4]Á`­p\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á ¦¬¤ä ª÷ ÃB ¤ÀÃþ »¡ ©ú%*s \033[m"
+
+#define NECKER_HELP "[¡ö]Â÷¶} [¡÷]¾\\Ū [^P]·s¼W [d]§R°£ [T]¼ÐÃD [E]½s¿è [m]²¾°Ê\n" \
+ COLOR3 " ½s¸¹ ÀÉ ®× ¼Ð ÃD%*s \033[m"
+
+#define NECKER_INNBBS "[¡ö]Â÷¶} [^P]·s¼W [d]§R°£ [E]½s¿è [/]·j´M [Enter]¸Ô²Ó\n" \
+ COLOR3 " ½s¸¹ ¤º ®e%*s \033[m"
+
+
+/* ----------------------------------------------------- */
+/* °T®§¦r¦ê¡Gmore() ®Éªº footer ³£§ì¥X¨Ó©w¸q¦b³o */
+/* ----------------------------------------------------- */
+
+/* itoc.010914.µù¸Ñ: ³æ¤@½g¡A©Ò¥H¥s FOOTER¡A³£¬O 78 char */
+
+/* itoc.010821: ª`·N \\ ¬O \¡A³Ì«á§Oº|¤F¤@­ÓªÅ¥ÕÁä :p */
+
+#define FOOTER_POST \
+COLOR1 " ¤å³¹¿ïŪ " COLOR2 " (ry)¦^À³ (=\\[]<>-+;'`)¥DÃD (|?QA)·j´M¼ÐÃD§@ªÌ (kj)¤W¤U½g (C)¼È¦s "
+
+#define FOOTER_MAILER \
+COLOR1 " ³½¶­©¹ªð " COLOR2 " (ry)¦^«H/¸s²Õ (X)Âà¹F (d)§R°£ (m)¼Ð°O (C)¼È¦s (=\\[]<>-+;'`|?QAkj) "
+
+#define FOOTER_GEM \
+COLOR1 " ºëµØ¿ïŪ " COLOR2 " (=\\[]<>-+;'`)¥DÃD (|?QA)·j´M¼ÐÃD§@ªÌ (kj)¤W¤U½g (¡ô¡õ¡ö)¤W¤UÂ÷¶} "
+
+#ifdef HAVE_GAME
+#define FOOTER_TALK \
+COLOR1 " ¥æ½Í¼Ò¦¡ " COLOR2 " (^O)¹ï«³¼Ò¦¡ (^C,^D)µ²§ô¥æ½Í (^T)¤Á´«©I¥s¾¹ (^Z)§Ö±¶¦Cªí (^G)¹Í¹Í "
+#else
+#define FOOTER_TALK \
+COLOR1 " ¥æ½Í¼Ò¦¡ " COLOR2 " (^C,^D)µ²§ô¥æ½Í (^T)¤Á´«©I¥s¾¹ (^Z)§Ö±¶¦Cªí (^G)¹Í¹Í (^Y)²M°£ "
+#endif
+
+#define FOOTER_COSIGN \
+COLOR1 " ³s¸p¾÷¨î " COLOR2 " (ry)¥[¤J³s¸p (kj)¤W¤U½g (¡ô¡õ¡ö)¤W¤UÂ÷¶} (h)»¡©ú "
+
+#define FOOTER_MORE \
+COLOR1 " ÂsÄý P.%d (%d%%) " COLOR2 " (h)»¡©ú [PgUp][PgDn][0][$]²¾°Ê (/n)·j´M (C)¼È¦s (¡öq)µ²§ô "
+
+#define FOOTER_VEDIT \
+COLOR1 " %s " COLOR2 " (^Z)»¡©ú (^W)²Å¸¹ (^L)­«Ã¸ (^X)Àɮ׳B²z ùø%s¢x%sùø%5d:%3d \033[m"
+
+
+/* ----------------------------------------------------- */
+/* °T®§¦r¦ê¡Gxo_foot() ®Éªº feeter ³£§ì¥X¨Ó©w¸q¦b³o */
+/* ----------------------------------------------------- */
+
+
+/* itoc.010914.µù¸Ñ: ¦Cªí¦h½g¡A©Ò¥H¥s FEETER¡A³£¬O 78 char */
+
+#define FEETER_CLASS \
+COLOR1 " ¬ÝªO¿ï¾Ü " COLOR2 " (c)·s¤å³¹ (vV)¼Ð°O¤wŪ¥¼Åª (y)¥þ³¡¦C¥X (z)¿ï­q (A)¥þ°ì·j´M (S)±Æ§Ç "
+
+#define FEETER_ULIST \
+COLOR1 " ºô¤Í¦Cªí " COLOR2 " (f)¦n¤Í (t)²á¤Ñ (q)¬d¸ß (ad)¥æ¤Í (m)±H«H (w)¤ô²y (s)§ó·s (TAB)¤Á´« "
+
+#define FEETER_PAL \
+COLOR1 " ©IªB¤Þ¦ñ " COLOR2 " (a)·s¼W (d)§R°£ (c)¤Í½Ë (m)±H«H (f)¤Þ¤J¦n¤Í (r^Q)¬d¸ß (s)§ó·s "
+
+#define FEETER_ALOHA \
+COLOR1 " ¤W¯¸³qª¾ " COLOR2 " (a)·s¼W (d)§R°£ (D)°Ï¬q§R°£ (f)¤Þ¤J¦n¤Í (r^Q)¬d¸ß (s)§ó·s "
+
+#define FEETER_VOTE \
+COLOR1 " ¬ÝªO§ë²¼ " COLOR2 " (¡÷/r/v)§ë²¼ (R)µ²ªG (^P)·s¼W§ë²¼ (E)­×§ï (V)¹wÄý (b)¶}²¼ (o)¦W³æ "
+
+#define FEETER_BMW \
+COLOR1 " ¤ô²y¦^ÅU " COLOR2 " (d)§R°£ (D)°Ï¬q§R°£ (m)±H«H (w)¤ô²y (^R)¦^°T (^Q)¬d¸ß (s)§ó·s "
+
+#define FEETER_MF \
+COLOR1 " ³Ì·R¬ÝªO " COLOR2 " (^P)·s¼W (Cg)½Æ»s (p^V)¶K¤W (d)§R°£ (c)·s¤å³¹ (vV)¼Ð°O¤wŪ/¥¼Åª "
+
+#define FEETER_COSIGN \
+COLOR1 " ³s¸p¤p¯¸ " COLOR2 " (r)Ū¨ú (y)¦^À³ (^P)µoªí (d)§R°£ (o)¶}ªO (c)Ãö³¬ (E)½s¿è (B)³]©w "
+
+#define FEETER_SONG \
+COLOR1 " ÂIºq¨t²Î " COLOR2 " (r)Ū¨ú (o)ÂIºq¨ì¬ÝªO (m)ÂIºq¨ì«H½c (E)½s¿èÀÉ®× (T)½s¿è¼ÐÃD "
+
+#define FEETER_NEWS \
+COLOR1 " ·s»DÂI¿ï " COLOR2 " (¡ô/¡õ)¤W¤U (PgUp/PgDn)¤W¤U­¶ (Home/End)­º§À (¡÷r)¿ï¨ú (¡ö)(q)Â÷¶} "
+
+#define FEETER_XPOST \
+COLOR1 " ¦ê¦C·j´M " COLOR2 " (y)¦^À³ (x)Âà¿ý (m)¼Ð°O (d)§R°£ (^P)µoªí (^Q)¬d¸ß§@ªÌ (t)¼ÐÅÒ "
+
+#define FEETER_MBOX \
+COLOR1 " «H«H¬Û±¤ " COLOR2 " (y)¦^«H (F/X/x)Âà±H/Âà¹F/Âà¿ý (d)§R°£ (D)°Ï¬q§R°£ (m)¼Ð°O (E)½s¿è "
+
+#define FEETER_POST \
+COLOR1 " ¤å³¹¦Cªí " COLOR2 " (ry)¦^«H (S/a)·j´M/¼ÐÃD/§@ªÌ (~G)¦ê¦C·j´M (x)Âà¿ý (V)§ë²¼ (u)·s»D "
+
+#define FEETER_GEM \
+COLOR1 " ¬ÝªOºëµØ " COLOR2 " (^P/a/f)·s¼W/¤å³¹/¥Ø¿ý (E)½s¿è (T)¼ÐÃD (m)²¾°Ê (c)½Æ»s (p^V)¶K¤W "
+
+#define FEETER_VOTEALL \
+COLOR1 " §ë²¼¤¤¤ß " COLOR2 " (¡ô/¡õ)¤W¤U (PgUp/PgDn)¤W¤U­¶ (Home/End)­º§À (¡÷)§ë²¼ (¡ö)(q)Â÷¶} "
+
+#define FEETER_HELP \
+COLOR1 " »¡©ú¤å¥ó " COLOR2 " (¡ô/¡õ)¤W¤U (PgUp/PgDn)¤W¤U­¶ (Home/End)­º§À (¡÷r)ÂsÄý (¡ö)(q)Â÷¶} "
+
+#define FEETER_INNBBS \
+COLOR1 " Âà«H³]©w " COLOR2 " (¡ô/¡õ)¤W¤U (PgUp/PgDn)¤W¤U­¶ (Home/End)­º§À (¡ö)(q)Â÷¶} "
+
+
+/* ----------------------------------------------------- */
+/* ¯¸¥x¨Ó·½Ã±¦W */
+/* ----------------------------------------------------- */
+
+/* itoc: «ØÄ³ banner ¤£­n¶W¹L¤T¦æ¡A¹Lªøªº¯¸Ã±¥i¯à·|³y¦¨¬Y¨Ç¨Ï¥ÎªÌªº¤Ï·P */
+
+#define EDIT_BANNER "\n--\n" \
+ " \033[1;42m Site \033[40;31m "SCHOOLNAME"£»"BBSNAME" \033[37m¡i"MYHOSTNAME"¡j\033[m\n" \
+ " \033[1;44m From \033[40;33m %.0s%s\033[m\n"
+
+#define MODIFY_BANNER " \033[1;46m Edit \033[40;35m %.0s%s\033[m\n"
+
+
+/* ----------------------------------------------------- */
+/* ¨ä¥L°T®§¦r¦ê */
+/* ----------------------------------------------------- */
+
+#define VMSG_NULL " \033[1;34;44m¢e¢e¢e¢e¢e¢e¢e¢e¢e¢e¢e¢e¢e¢e¢e\033[37;40m ½Ð«ö \033[33m¥ô·NÁä \033[37mÄ~Äò \033[34;44m¢e¢e¢e¢e¢e¢e¢e¢e¢e¢e¢e¢e¢e¢e¢e\033[m"
+
+#define ICON_UNREAD_BRD "\033[33m£¾" /* ¥¼Åª¬ÝªO */
+#define ICON_READ_BRD " " /* ¤wŪ¬ÝªO */
+
+#define ICON_GAMBLED_BRD "\033[1;31m½ä\033[m" /* Á|¦æ½ä½L¤¤ªº¬ÝªO */
+#define ICON_VOTED_BRD "\033[1;33m§ë\033[m" /* Á|¦æ§ë²¼¤¤ªº¬ÝªO */
+#define ICON_NOTRAN_BRD "¡¼" /* ¤£Âà«HªO */
+#define ICON_TRAN_BRD "¡½" /* Âà«HªO */
+
+#define TOKEN_ZAP_BRD '-' /* zap ªO */
+#define TOKEN_FRIEND_BRD '.' /* ¦n¤ÍªO */
+#define TOKEN_SECRET_BRD ')' /* ¯µ±KªO */
+
+#endif /* _THEME_H_ */
diff --git a/include/theme/theme_purple.h b/include/theme/theme_purple.h
new file mode 100644
index 0000000..dc496f8
--- /dev/null
+++ b/include/theme/theme_purple.h
@@ -0,0 +1,246 @@
+/*-------------------------------------------------------*/
+/* theme.h ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : custom theme */
+/* create : 02/08/17 */
+/* update : / / */
+/*-------------------------------------------------------*/
+
+
+#ifndef _THEME_H_
+#define _THEME_H_
+
+
+/* ----------------------------------------------------- */
+/* °ò¥»ÃC¦â©w¸q¡A¥H§Q¤¶­±­×§ï */
+/* ----------------------------------------------------- */
+
+#define COLOR1 "\033[1;32;45m" /* footer/feeter ªº«e¬qÃC¦â */
+#define COLOR2 "\033[0;35;47m" /* footer/feeter ªº«á¬qÃC¦â */
+#define COLOR3 "\033[37;45m" /* neck ªºÃC¦â */
+#define COLOR4 "\033[1;45m" /* ¥ú´Î ªºÃC¦â */
+#define COLOR5 "\033[1;36;43m" /* more ÀÉÀYªº¼ÐÃDÃC¦â */
+#define COLOR6 "\033[33;45m" /* more ÀÉÀYªº¤º®eÃC¦â */
+#define COLOR7 "\033[1;35m" /* §@ªÌ¦b½u¤WªºÃC¦â */
+
+
+/* ----------------------------------------------------- */
+/* ¨Ï¥ÎªÌ¦W³æÃC¦â */
+/* ----------------------------------------------------- */
+
+#define COLOR_NORMAL "" /* ¤@¯ë¨Ï¥ÎªÌ */
+#define COLOR_MYBAD "\033[1;31m" /* Ãa¤H */
+#define COLOR_MYGOOD "\033[1;32m" /* §Úªº¦n¤Í */
+#define COLOR_OGOOD "\033[1;33m" /* »P§Ú¬°¤Í */
+#define COLOR_CLOAK "\033[1;35m" /* Áô§Î */ /* itoc.µù¸Ñ: ¨S¥Î¨ì¡A­nªº¤H½Ð¦Û¦æ¥[¤J ulist_body() */
+#define COLOR_SELF "\033[1;36m" /* ¦Û¤v */
+#define COLOR_BOTHGOOD "\033[1;37m" /* ¤¬³]¦n¤Í */
+#define COLOR_BRDMATE "\033[36m" /* ªO¦ñ */
+
+
+/* ----------------------------------------------------- */
+/* ¿ï³æ¦ì¸m */
+/* ----------------------------------------------------- */
+
+/* itoc.µù¸Ñ: ª`·N MENU_XPOS ­n >= MENU_XNOTE + MOVIE_LINES */
+
+#define MENU_XNOTE 2 /* °ÊºA¬ÝªO¥Ñ (2, 0) ¶}©l */
+#define MOVIE_LINES 10 /* °Êµe³Ì¦h¦³ 10 ¦C */
+
+#define MENU_XPOS 13 /* ¿ï³æ¶}©lªº (x, y) ®y¼Ð */
+#define MENU_YPOS ((d_cols >> 1) + 18)
+
+
+/* ----------------------------------------------------- */
+/* °T®§¦r¦ê¡G*_neck() ®Éªº necker ³£§ì¥X¨Ó©w¸q¦b³o */
+/* ----------------------------------------------------- */
+
+/* necker ªº¦æ¼Æ³£¬O¤G¦æ¡A±q (1, 0) ¨ì (2, 80) */
+
+/* ©Ò¦³ªº XZ_* ³£¦³ necker¡A¥u¬O¦³¨Ç¦b *_neck()¡A¦³¨ÇÂæb *_head() */
+
+/* ulist_neck() ¤Î xpost_head() ªº²Ä¤@¦æ¤ñ¸û¯S§O¡A¤£¦b¦¹©w¸q */
+
+#define NECKER_CLASS "[¡ö]¥D¿ï³æ [¡÷]¾\\Ū [¡ô¡õ]¿ï¾Ü [c]½g¼Æ [y]¸ü¤J [/?]·j´M [s]¬ÝªO [h]»¡©ú\n" \
+ COLOR3 " %s ¬Ý ªO Ãþ§OÂà«H ¤¤ ¤å ±Ô ­z%*s ¤H®ð ªO ¥D%*s \033[m"
+
+#define NECKER_ULIST "\n" \
+ COLOR3 " ½s¸¹ ¥N¸¹ ¼ÊºÙ%*s %-*s °ÊºA ¶¢¸m \033[m"
+
+#define NECKER_PAL "[¡ö]Â÷¶} [a]·s¼W [c]­×§ï [d]§R°£ [m]±H«H [w]¤ô²y [s]¾ã²z [¡÷]¬d¸ß [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¥N ¸¹ ¤Í ½Ë%*s \033[m"
+
+#define NECKER_ALOHA "[¡ö]Â÷¶} [a]·s¼W [d]§R°£ [D]°Ï¬q§R°£ [m]±H«H [w]¤ô²y [s]­«¾ã [f]¤Þ¤J [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¤W ¯¸ ³q ª¾ ¦W ³æ%*s \033[m"
+
+#define NECKER_VOTE "[¡ö]Â÷¶} [R]µ²ªG [^P]Á|¦æ [E]­×§ï [V]¹wÄý [^Q]§ï´Á [o]¦W³æ [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¶}²¼¤é ¥D¿ì¤H §ë ²¼ ©v ¦®%*s \033[m"
+
+#define NECKER_BMW "[¡ö]Â÷¶} [d]§R°£ [D]°Ï¬q§R°£ [m]±H«H [M]Àx¦s [w]¤ô²y [s]§ó·s [¡÷]¬d¸ß [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¥N ¸¹ ¤º ®e%*s ®É¶¡ \033[m"
+
+#define NECKER_MF "[¡ö]Â÷¶} [¡÷]¶i¤J [^P]·s¼W [d]§R°£ [c]¤Á´« [C]½Æ»s [^V]¶K¤W [m]²¾°Ê [h]»¡©ú\n" \
+ COLOR3 " %s ¬Ý ªO Ãþ§OÂà«H ¤¤ ¤å ±Ô ­z%*s ¤H®ð ªO ¥D%*s \033[m"
+
+#define NECKER_COSIGN "[¡ö]Â÷¶} [¡÷]¾\\Ū [^P]¥Ó½Ð [d]§R°£ [o]¶}ªO [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á Á|¿ì¤H ¬Ý ªO ¼Ð ÃD%*s \033[m"
+
+#define NECKER_SONG "[¡ö]Â÷¶} [¡÷]ÂsÄý [o]ÂIºq¨ì¬ÝªO [m]ÂIºq¨ì«H½c [Enter]ÂsÄý [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¥D ÃD%*s [½s ¿ï] [¤é ´Á]\033[m"
+
+#define NECKER_NEWS "[¡ö]Â÷¶} [¡÷]¾\\Ū [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á §@ ªÌ ·s »D ¼Ð ÃD%*s \033[m"
+
+#define NECKER_XPOST "\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á §@ ªÌ ¤å ³¹ ¼Ð ÃD%*s µû:%s \033[m"
+
+#define NECKER_MBOX "[¡ö]Â÷¶} [¡÷,r]Ū«H [d]§R°£ [R,y](¸s²Õ)¦^«H [s]±H«H [x]Âà¿ý [X]Âà¹F [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á §@ ªÌ «H ¥ó ¼Ð ÃD%*s \033[m"
+
+#define NECKER_POST "[¡ö]Â÷¶} [¡÷]¾\\Ū [^P]µoªí [b]¶iªOµe­± [d]§R°£ [V]§ë²¼ [TAB]ºëµØ°Ï [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á §@ ªÌ ¤å ³¹ ¼Ð ÃD%*s µû:%s ¤H®ð:%-4d \033[m"
+
+#define NECKER_GEM "[¡ö]Â÷¶} [¡÷]ÂsÄý [B]¼Ò¦¡ [C]¼È¦s [F]Âà±H [d]§R°£ [h]»¡©ú %s\n" \
+ COLOR3 " ½s¸¹ ¥D ÃD%*s [½s ¿ï] [¤é ´Á]\033[m"
+
+/* ¥H¤U³o¨Ç«h¬O¤@¨ÇÃþ XZ_* µ²ºcªº necker */
+
+#define NECKER_VOTEALL "[¡ô/¡õ]¤W¤U [PgUp/PgDn]¤W¤U­¶ [Home/End]­º§À [¡÷]§ë²¼ [¡ö][q]Â÷¶}\n" \
+ COLOR3 " ½s¸¹ ¬Ý ªO Ãþ§OÂà«H ¤¤ ¤å ±Ô ­z%*s ªO ¥D%*s \033[m"
+
+#define NECKER_CREDIT "[¡ö]Â÷¶} [C]´«­¶ [1]·s¼W [2]§R°£ [3]¥þ§R [4]Á`­p\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á ¦¬¤ä ª÷ ÃB ¤ÀÃþ »¡ ©ú%*s \033[m"
+
+#define NECKER_HELP "[¡ö]Â÷¶} [¡÷]¾\\Ū [^P]·s¼W [d]§R°£ [T]¼ÐÃD [E]½s¿è [m]²¾°Ê\n" \
+ COLOR3 " ½s¸¹ ÀÉ ®× ¼Ð ÃD%*s \033[m"
+
+#define NECKER_INNBBS "[¡ö]Â÷¶} [^P]·s¼W [d]§R°£ [E]½s¿è [/]·j´M [Enter]¸Ô²Ó\n" \
+ COLOR3 " ½s¸¹ ¤º ®e%*s \033[m"
+
+
+/* ----------------------------------------------------- */
+/* °T®§¦r¦ê¡Gmore() ®Éªº footer ³£§ì¥X¨Ó©w¸q¦b³o */
+/* ----------------------------------------------------- */
+
+/* itoc.010914.µù¸Ñ: ³æ¤@½g¡A©Ò¥H¥s FOOTER¡A³£¬O 78 char */
+
+/* itoc.010821: ª`·N \\ ¬O \¡A³Ì«á§Oº|¤F¤@­ÓªÅ¥ÕÁä :p */
+
+#define FOOTER_POST \
+COLOR1 " ¤å³¹¿ïŪ " COLOR2 " (ry)¦^À³ (=\\[]<>-+;'`)¥DÃD (|?QA)·j´M¼ÐÃD§@ªÌ (kj)¤W¤U½g (C)¼È¦s "
+
+#define FOOTER_MAILER \
+COLOR1 " ³½¶­©¹ªð " COLOR2 " (ry)¦^«H/¸s²Õ (X)Âà¹F (d)§R°£ (m)¼Ð°O (C)¼È¦s (=\\[]<>-+;'`|?QAkj) "
+
+#define FOOTER_GEM \
+COLOR1 " ºëµØ¿ïŪ " COLOR2 " (=\\[]<>-+;'`)¥DÃD (|?QA)·j´M¼ÐÃD§@ªÌ (kj)¤W¤U½g (¡ô¡õ¡ö)¤W¤UÂ÷¶} "
+
+#ifdef HAVE_GAME
+#define FOOTER_TALK \
+COLOR1 " ¥æ½Í¼Ò¦¡ " COLOR2 " (^O)¹ï«³¼Ò¦¡ (^C,^D)µ²§ô¥æ½Í (^T)¤Á´«©I¥s¾¹ (^Z)§Ö±¶¦Cªí (^G)¹Í¹Í "
+#else
+#define FOOTER_TALK \
+COLOR1 " ¥æ½Í¼Ò¦¡ " COLOR2 " (^C,^D)µ²§ô¥æ½Í (^T)¤Á´«©I¥s¾¹ (^Z)§Ö±¶¦Cªí (^G)¹Í¹Í (^Y)²M°£ "
+#endif
+
+#define FOOTER_COSIGN \
+COLOR1 " ³s¸p¾÷¨î " COLOR2 " (ry)¥[¤J³s¸p (kj)¤W¤U½g (¡ô¡õ¡ö)¤W¤UÂ÷¶} (h)»¡©ú "
+
+#define FOOTER_MORE \
+COLOR1 " ÂsÄý P.%d (%d%%) " COLOR2 " (h)»¡©ú [PgUp][PgDn][0][$]²¾°Ê (/n)·j´M (C)¼È¦s (¡öq)µ²§ô "
+
+#define FOOTER_VEDIT \
+COLOR1 " %s " COLOR2 " (^Z)»¡©ú (^W)²Å¸¹ (^L)­«Ã¸ (^X)Àɮ׳B²z ùø%s¢x%sùø%5d:%3d \033[m"
+
+
+/* ----------------------------------------------------- */
+/* °T®§¦r¦ê¡Gxo_foot() ®Éªº feeter ³£§ì¥X¨Ó©w¸q¦b³o */
+/* ----------------------------------------------------- */
+
+
+/* itoc.010914.µù¸Ñ: ¦Cªí¦h½g¡A©Ò¥H¥s FEETER¡A³£¬O 78 char */
+
+#define FEETER_CLASS \
+COLOR1 " ¬ÝªO¿ï¾Ü " COLOR2 " (c)·s¤å³¹ (vV)¼Ð°O¤wŪ¥¼Åª (y)¥þ³¡¦C¥X (z)¿ï­q (A)¥þ°ì·j´M (S)±Æ§Ç "
+
+#define FEETER_ULIST \
+COLOR1 " ºô¤Í¦Cªí " COLOR2 " (f)¦n¤Í (t)²á¤Ñ (q)¬d¸ß (ad)¥æ¤Í (m)±H«H (w)¤ô²y (s)§ó·s (TAB)¤Á´« "
+
+#define FEETER_PAL \
+COLOR1 " ©IªB¤Þ¦ñ " COLOR2 " (a)·s¼W (d)§R°£ (c)¤Í½Ë (m)±H«H (f)¤Þ¤J¦n¤Í (r^Q)¬d¸ß (s)§ó·s "
+
+#define FEETER_ALOHA \
+COLOR1 " ¤W¯¸³qª¾ " COLOR2 " (a)·s¼W (d)§R°£ (D)°Ï¬q§R°£ (f)¤Þ¤J¦n¤Í (r^Q)¬d¸ß (s)§ó·s "
+
+#define FEETER_VOTE \
+COLOR1 " ¬ÝªO§ë²¼ " COLOR2 " (¡÷/r/v)§ë²¼ (R)µ²ªG (^P)·s¼W§ë²¼ (E)­×§ï (V)¹wÄý (b)¶}²¼ (o)¦W³æ "
+
+#define FEETER_BMW \
+COLOR1 " ¤ô²y¦^ÅU " COLOR2 " (d)§R°£ (D)°Ï¬q§R°£ (m)±H«H (w)¤ô²y (^R)¦^°T (^Q)¬d¸ß (s)§ó·s "
+
+#define FEETER_MF \
+COLOR1 " ³Ì·R¬ÝªO " COLOR2 " (^P)·s¼W (Cg)½Æ»s (p^V)¶K¤W (d)§R°£ (c)·s¤å³¹ (vV)¼Ð°O¤wŪ/¥¼Åª "
+
+#define FEETER_COSIGN \
+COLOR1 " ³s¸p¤p¯¸ " COLOR2 " (r)Ū¨ú (y)¦^À³ (^P)µoªí (d)§R°£ (o)¶}ªO (c)Ãö³¬ (E)½s¿è (B)³]©w "
+
+#define FEETER_SONG \
+COLOR1 " ÂIºq¨t²Î " COLOR2 " (r)Ū¨ú (o)ÂIºq¨ì¬ÝªO (m)ÂIºq¨ì«H½c (E)½s¿èÀÉ®× (T)½s¿è¼ÐÃD "
+
+#define FEETER_NEWS \
+COLOR1 " ·s»DÂI¿ï " COLOR2 " (¡ô/¡õ)¤W¤U (PgUp/PgDn)¤W¤U­¶ (Home/End)­º§À (¡÷r)¿ï¨ú (¡ö)(q)Â÷¶} "
+
+#define FEETER_XPOST \
+COLOR1 " ¦ê¦C·j´M " COLOR2 " (y)¦^À³ (x)Âà¿ý (m)¼Ð°O (d)§R°£ (^P)µoªí (^Q)¬d¸ß§@ªÌ (t)¼ÐÅÒ "
+
+#define FEETER_MBOX \
+COLOR1 " «H«H¬Û±¤ " COLOR2 " (y)¦^«H (F/X/x)Âà±H/Âà¹F/Âà¿ý (d)§R°£ (D)°Ï¬q§R°£ (m)¼Ð°O (E)½s¿è "
+
+#define FEETER_POST \
+COLOR1 " ¤å³¹¦Cªí " COLOR2 " (ry)¦^«H (S/a)·j´M/¼ÐÃD/§@ªÌ (~G)¦ê¦C·j´M (x)Âà¿ý (V)§ë²¼ (u)·s»D "
+
+#define FEETER_GEM \
+COLOR1 " ¬ÝªOºëµØ " COLOR2 " (^P/a/f)·s¼W/¤å³¹/¥Ø¿ý (E)½s¿è (T)¼ÐÃD (m)²¾°Ê (c)½Æ»s (p^V)¶K¤W "
+
+#define FEETER_VOTEALL \
+COLOR1 " §ë²¼¤¤¤ß " COLOR2 " (¡ô/¡õ)¤W¤U (PgUp/PgDn)¤W¤U­¶ (Home/End)­º§À (¡÷)§ë²¼ (¡ö)(q)Â÷¶} "
+
+#define FEETER_HELP \
+COLOR1 " »¡©ú¤å¥ó " COLOR2 " (¡ô/¡õ)¤W¤U (PgUp/PgDn)¤W¤U­¶ (Home/End)­º§À (¡÷r)ÂsÄý (¡ö)(q)Â÷¶} "
+
+#define FEETER_INNBBS \
+COLOR1 " Âà«H³]©w " COLOR2 " (¡ô/¡õ)¤W¤U (PgUp/PgDn)¤W¤U­¶ (Home/End)­º§À (¡ö)(q)Â÷¶} "
+
+
+/* ----------------------------------------------------- */
+/* ¯¸¥x¨Ó·½Ã±¦W */
+/* ----------------------------------------------------- */
+
+/* itoc: «ØÄ³ banner ¤£­n¶W¹L¤T¦æ¡A¹Lªøªº¯¸Ã±¥i¯à·|³y¦¨¬Y¨Ç¨Ï¥ÎªÌªº¤Ï·P */
+
+#define EDIT_BANNER "\n--\n" \
+ " \033[1;45mùæ\033[42mùè\033[m O\033[1mri\033[30mgi\033[mn: \033[1;45m "SCHOOLNAME"£»"BBSNAME" \033[43m "MYHOSTNAME" \033[m\n" \
+ " \033[1;46mùî\033[43m¢|\033[m Au\033[1mt\033[30mho\033[mr: \033[1;35m%s\033[m ±q \033[1;32m%s\033[m µoªí\n"
+
+#define MODIFY_BANNER " \033[1;41m¢~\033[44m¢¡\033[m \033[1mMo\033[30mdi\033[mfy: %s ©ó \033[1;35m%s\033[m ­×§ï\n"
+
+
+/* ----------------------------------------------------- */
+/* ¨ä¥L°T®§¦r¦ê */
+/* ----------------------------------------------------- */
+
+#define VMSG_NULL " \033[1;35m ¢j¢k¢l¢m¢n¢o¢p ½Ð«ö¥ô·NÁäÄ~Äò ¢p\033[m"
+
+#define ICON_UNREAD_BRD "\033[1;35m¡é" /* ¥¼Åª¬ÝªO */
+#define ICON_READ_BRD " " /* ¤wŪ¬ÝªO */
+
+#define ICON_GAMBLED_BRD "\033[1;31m½ä\033[m" /* Á|¦æ½ä½L¤¤ªº¬ÝªO */
+#define ICON_VOTED_BRD "\033[1;33m§ë\033[m" /* Á|¦æ§ë²¼¤¤ªº¬ÝªO */
+#define ICON_NOTRAN_BRD "¡Ñ" /* ¤£Âà«HªO */
+#define ICON_TRAN_BRD "¡ò" /* Âà«HªO */
+
+#define TOKEN_ZAP_BRD '-' /* zap ªO */
+#define TOKEN_FRIEND_BRD ']' /* ¦n¤ÍªO */
+#define TOKEN_SECRET_BRD '}' /* ¯µ±KªO */
+
+#endif /* _THEME_H_ */
diff --git a/include/theme/theme_red.h b/include/theme/theme_red.h
new file mode 100644
index 0000000..8a91b40
--- /dev/null
+++ b/include/theme/theme_red.h
@@ -0,0 +1,246 @@
+/*-------------------------------------------------------*/
+/* theme.h ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : custom theme */
+/* create : 02/08/17 */
+/* update : / / */
+/*-------------------------------------------------------*/
+
+
+#ifndef _THEME_H_
+#define _THEME_H_
+
+
+/* ----------------------------------------------------- */
+/* °ò¥»ÃC¦â©w¸q¡A¥H§Q¤¶­±­×§ï */
+/* ----------------------------------------------------- */
+
+#define COLOR1 "\033[1;37;41m" /* footer/feeter ªº«e¬qÃC¦â */
+#define COLOR2 "\033[34;47m" /* footer/feeter ªº«á¬qÃC¦â */
+#define COLOR3 "\033[31;47m" /* neck ªºÃC¦â */
+#define COLOR4 "\033[1;41m" /* ¥ú´Î ªºÃC¦â */
+#define COLOR5 "\033[31;47m" /* more ÀÉÀYªº¼ÐÃDÃC¦â */
+#define COLOR6 "\033[37;41m" /* more ÀÉÀYªº¤º®eÃC¦â */
+#define COLOR7 "\033[1;34m" /* §@ªÌ¦b½u¤WªºÃC¦â */
+
+
+/* ----------------------------------------------------- */
+/* ¨Ï¥ÎªÌ¦W³æÃC¦â */
+/* ----------------------------------------------------- */
+
+#define COLOR_NORMAL "" /* ¤@¯ë¨Ï¥ÎªÌ */
+#define COLOR_MYBAD "\033[1;31m" /* Ãa¤H */
+#define COLOR_MYGOOD "\033[1;32m" /* §Úªº¦n¤Í */
+#define COLOR_OGOOD "\033[1;33m" /* »P§Ú¬°¤Í */
+#define COLOR_CLOAK "\033[1;35m" /* Áô§Î */ /* itoc.µù¸Ñ: ¨S¥Î¨ì¡A­nªº¤H½Ð¦Û¦æ¥[¤J ulist_body() */
+#define COLOR_SELF "\033[1;36m" /* ¦Û¤v */
+#define COLOR_BOTHGOOD "\033[1;37m" /* ¤¬³]¦n¤Í */
+#define COLOR_BRDMATE "\033[36m" /* ªO¦ñ */
+
+
+/* ----------------------------------------------------- */
+/* ¿ï³æ¦ì¸m */
+/* ----------------------------------------------------- */
+
+/* itoc.µù¸Ñ: ª`·N MENU_XPOS ­n >= MENU_XNOTE + MOVIE_LINES */
+
+#define MENU_XNOTE 2 /* °ÊºA¬ÝªO¥Ñ (2, 0) ¶}©l */
+#define MOVIE_LINES 10 /* °Êµe³Ì¦h¦³ 10 ¦C */
+
+#define MENU_XPOS 13 /* ¿ï³æ¶}©lªº (x, y) ®y¼Ð */
+#define MENU_YPOS ((d_cols >> 1) + 18)
+
+
+/* ----------------------------------------------------- */
+/* °T®§¦r¦ê¡G*_neck() ®Éªº necker ³£§ì¥X¨Ó©w¸q¦b³o */
+/* ----------------------------------------------------- */
+
+/* necker ªº¦æ¼Æ³£¬O¤G¦æ¡A±q (1, 0) ¨ì (2, 80) */
+
+/* ©Ò¦³ªº XZ_* ³£¦³ necker¡A¥u¬O¦³¨Ç¦b *_neck()¡A¦³¨ÇÂæb *_head() */
+
+/* ulist_neck() ¤Î xpost_head() ªº²Ä¤@¦æ¤ñ¸û¯S§O¡A¤£¦b¦¹©w¸q */
+
+#define NECKER_CLASS "[¡ö]¥D¿ï³æ [¡÷]¾\\Ū [¡ô¡õ]¿ï¾Ü [c]½g¼Æ [y]¸ü¤J [/?]·j´M [s]¬ÝªO [h]»¡©ú\n" \
+ COLOR3 " %s ¬Ý ªO Ãþ§OÂà«H ¤¤ ¤å ±Ô ­z%*s ¤H®ð ªO ¥D%*s \033[m"
+
+#define NECKER_ULIST "\n" \
+ COLOR3 " ½s¸¹ ¥N¸¹ ¼ÊºÙ%*s %-*s °ÊºA ¶¢¸m \033[m"
+
+#define NECKER_PAL "[¡ö]Â÷¶} [a]·s¼W [c]­×§ï [d]§R°£ [m]±H«H [w]¤ô²y [s]¾ã²z [¡÷]¬d¸ß [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¥N ¸¹ ¤Í ½Ë%*s \033[m"
+
+#define NECKER_ALOHA "[¡ö]Â÷¶} [a]·s¼W [d]§R°£ [D]°Ï¬q§R°£ [m]±H«H [w]¤ô²y [s]­«¾ã [f]¤Þ¤J [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¤W ¯¸ ³q ª¾ ¦W ³æ%*s \033[m"
+
+#define NECKER_VOTE "[¡ö]Â÷¶} [R]µ²ªG [^P]Á|¦æ [E]­×§ï [V]¹wÄý [^Q]§ï´Á [o]¦W³æ [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¶}²¼¤é ¥D¿ì¤H §ë ²¼ ©v ¦®%*s \033[m"
+
+#define NECKER_BMW "[¡ö]Â÷¶} [d]§R°£ [D]°Ï¬q§R°£ [m]±H«H [M]Àx¦s [w]¤ô²y [s]§ó·s [¡÷]¬d¸ß [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¥N ¸¹ ¤º ®e%*s ®É¶¡ \033[m"
+
+#define NECKER_MF "[¡ö]Â÷¶} [¡÷]¶i¤J [^P]·s¼W [d]§R°£ [c]¤Á´« [C]½Æ»s [^V]¶K¤W [m]²¾°Ê [h]»¡©ú\n" \
+ COLOR3 " %s ¬Ý ªO Ãþ§OÂà«H ¤¤ ¤å ±Ô ­z%*s ¤H®ð ªO ¥D%*s \033[m"
+
+#define NECKER_COSIGN "[¡ö]Â÷¶} [¡÷]¾\\Ū [^P]¥Ó½Ð [d]§R°£ [o]¶}ªO [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á Á|¿ì¤H ¬Ý ªO ¼Ð ÃD%*s \033[m"
+
+#define NECKER_SONG "[¡ö]Â÷¶} [¡÷]ÂsÄý [o]ÂIºq¨ì¬ÝªO [m]ÂIºq¨ì«H½c [Enter]ÂsÄý [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¥D ÃD%*s [½s ¿ï] [¤é ´Á]\033[m"
+
+#define NECKER_NEWS "[¡ö]Â÷¶} [¡÷]¾\\Ū [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á §@ ªÌ ·s »D ¼Ð ÃD%*s \033[m"
+
+#define NECKER_XPOST "\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á §@ ªÌ ¤å ³¹ ¼Ð ÃD%*s µû:%s \033[m"
+
+#define NECKER_MBOX "[¡ö]Â÷¶} [¡÷,r]Ū«H [d]§R°£ [R,y](¸s²Õ)¦^«H [s]±H«H [x]Âà¿ý [X]Âà¹F [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á §@ ªÌ «H ¥ó ¼Ð ÃD%*s \033[m"
+
+#define NECKER_POST "[¡ö]Â÷¶} [¡÷]¾\\Ū [^P]µoªí [b]¶iªOµe­± [d]§R°£ [V]§ë²¼ [TAB]ºëµØ°Ï [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á §@ ªÌ ¤å ³¹ ¼Ð ÃD%*s µû:%s ¤H®ð:%-4d \033[m"
+
+#define NECKER_GEM "[¡ö]Â÷¶} [¡÷]ÂsÄý [B]¼Ò¦¡ [C]¼È¦s [F]Âà±H [d]§R°£ [h]»¡©ú %s\n" \
+ COLOR3 " ½s¸¹ ¥D ÃD%*s [½s ¿ï] [¤é ´Á]\033[m"
+
+/* ¥H¤U³o¨Ç«h¬O¤@¨ÇÃþ XZ_* µ²ºcªº necker */
+
+#define NECKER_VOTEALL "[¡ô/¡õ]¤W¤U [PgUp/PgDn]¤W¤U­¶ [Home/End]­º§À [¡÷]§ë²¼ [¡ö][q]Â÷¶}\n" \
+ COLOR3 " ½s¸¹ ¬Ý ªO Ãþ§OÂà«H ¤¤ ¤å ±Ô ­z%*s ªO ¥D%*s \033[m"
+
+#define NECKER_CREDIT "[¡ö]Â÷¶} [C]´«­¶ [1]·s¼W [2]§R°£ [3]¥þ§R [4]Á`­p\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á ¦¬¤ä ª÷ ÃB ¤ÀÃþ »¡ ©ú%*s \033[m"
+
+#define NECKER_HELP "[¡ö]Â÷¶} [¡÷]¾\\Ū [^P]·s¼W [d]§R°£ [T]¼ÐÃD [E]½s¿è [m]²¾°Ê\n" \
+ COLOR3 " ½s¸¹ ÀÉ ®× ¼Ð ÃD%*s \033[m"
+
+#define NECKER_INNBBS "[¡ö]Â÷¶} [^P]·s¼W [d]§R°£ [E]½s¿è [/]·j´M [Enter]¸Ô²Ó\n" \
+ COLOR3 " ½s¸¹ ¤º ®e%*s \033[m"
+
+
+/* ----------------------------------------------------- */
+/* °T®§¦r¦ê¡Gmore() ®Éªº footer ³£§ì¥X¨Ó©w¸q¦b³o */
+/* ----------------------------------------------------- */
+
+/* itoc.010914.µù¸Ñ: ³æ¤@½g¡A©Ò¥H¥s FOOTER¡A³£¬O 78 char */
+
+/* itoc.010821: ª`·N \\ ¬O \¡A³Ì«á§Oº|¤F¤@­ÓªÅ¥ÕÁä :p */
+
+#define FOOTER_POST \
+COLOR1 " ¤å³¹¿ïŪ " COLOR2 " (ry)¦^À³ (=\\[]<>-+;'`)¥DÃD (|?QA)·j´M¼ÐÃD§@ªÌ (kj)¤W¤U½g (C)¼È¦s "
+
+#define FOOTER_MAILER \
+COLOR1 " ³½¶­©¹ªð " COLOR2 " (ry)¦^«H/¸s²Õ (X)Âà¹F (d)§R°£ (m)¼Ð°O (C)¼È¦s (=\\[]<>-+;'`|?QAkj) "
+
+#define FOOTER_GEM \
+COLOR1 " ºëµØ¿ïŪ " COLOR2 " (=\\[]<>-+;'`)¥DÃD (|?QA)·j´M¼ÐÃD§@ªÌ (kj)¤W¤U½g (¡ô¡õ¡ö)¤W¤UÂ÷¶} "
+
+#ifdef HAVE_GAME
+#define FOOTER_TALK \
+COLOR1 " ¥æ½Í¼Ò¦¡ " COLOR2 " (^O)¹ï«³¼Ò¦¡ (^C,^D)µ²§ô¥æ½Í (^T)¤Á´«©I¥s¾¹ (^Z)§Ö±¶¦Cªí (^G)¹Í¹Í "
+#else
+#define FOOTER_TALK \
+COLOR1 " ¥æ½Í¼Ò¦¡ " COLOR2 " (^C,^D)µ²§ô¥æ½Í (^T)¤Á´«©I¥s¾¹ (^Z)§Ö±¶¦Cªí (^G)¹Í¹Í (^Y)²M°£ "
+#endif
+
+#define FOOTER_COSIGN \
+COLOR1 " ³s¸p¾÷¨î " COLOR2 " (ry)¥[¤J³s¸p (kj)¤W¤U½g (¡ô¡õ¡ö)¤W¤UÂ÷¶} (h)»¡©ú "
+
+#define FOOTER_MORE \
+COLOR1 " ÂsÄý P.%d (%d%%) " COLOR2 " (h)»¡©ú [PgUp][PgDn][0][$]²¾°Ê (/n)·j´M (C)¼È¦s (¡öq)µ²§ô "
+
+#define FOOTER_VEDIT \
+COLOR1 " %s " COLOR2 " (^Z)»¡©ú (^W)²Å¸¹ (^L)­«Ã¸ (^X)Àɮ׳B²z ùø%s¢x%sùø%5d:%3d \033[m"
+
+
+/* ----------------------------------------------------- */
+/* °T®§¦r¦ê¡Gxo_foot() ®Éªº feeter ³£§ì¥X¨Ó©w¸q¦b³o */
+/* ----------------------------------------------------- */
+
+
+/* itoc.010914.µù¸Ñ: ¦Cªí¦h½g¡A©Ò¥H¥s FEETER¡A³£¬O 78 char */
+
+#define FEETER_CLASS \
+COLOR1 " ¬ÝªO¿ï¾Ü " COLOR2 " (c)·s¤å³¹ (vV)¼Ð°O¤wŪ¥¼Åª (y)¥þ³¡¦C¥X (z)¿ï­q (A)¥þ°ì·j´M (S)±Æ§Ç "
+
+#define FEETER_ULIST \
+COLOR1 " ºô¤Í¦Cªí " COLOR2 " (f)¦n¤Í (t)²á¤Ñ (q)¬d¸ß (ad)¥æ¤Í (m)±H«H (w)¤ô²y (s)§ó·s (TAB)¤Á´« "
+
+#define FEETER_PAL \
+COLOR1 " ©IªB¤Þ¦ñ " COLOR2 " (a)·s¼W (d)§R°£ (c)¤Í½Ë (m)±H«H (f)¤Þ¤J¦n¤Í (r^Q)¬d¸ß (s)§ó·s "
+
+#define FEETER_ALOHA \
+COLOR1 " ¤W¯¸³qª¾ " COLOR2 " (a)·s¼W (d)§R°£ (D)°Ï¬q§R°£ (f)¤Þ¤J¦n¤Í (r^Q)¬d¸ß (s)§ó·s "
+
+#define FEETER_VOTE \
+COLOR1 " ¬ÝªO§ë²¼ " COLOR2 " (¡÷/r/v)§ë²¼ (R)µ²ªG (^P)·s¼W§ë²¼ (E)­×§ï (V)¹wÄý (b)¶}²¼ (o)¦W³æ "
+
+#define FEETER_BMW \
+COLOR1 " ¤ô²y¦^ÅU " COLOR2 " (d)§R°£ (D)°Ï¬q§R°£ (m)±H«H (w)¤ô²y (^R)¦^°T (^Q)¬d¸ß (s)§ó·s "
+
+#define FEETER_MF \
+COLOR1 " ³Ì·R¬ÝªO " COLOR2 " (^P)·s¼W (Cg)½Æ»s (p^V)¶K¤W (d)§R°£ (c)·s¤å³¹ (vV)¼Ð°O¤wŪ/¥¼Åª "
+
+#define FEETER_COSIGN \
+COLOR1 " ³s¸p¤p¯¸ " COLOR2 " (r)Ū¨ú (y)¦^À³ (^P)µoªí (d)§R°£ (o)¶}ªO (c)Ãö³¬ (E)½s¿è (B)³]©w "
+
+#define FEETER_SONG \
+COLOR1 " ÂIºq¨t²Î " COLOR2 " (r)Ū¨ú (o)ÂIºq¨ì¬ÝªO (m)ÂIºq¨ì«H½c (E)½s¿èÀÉ®× (T)½s¿è¼ÐÃD "
+
+#define FEETER_NEWS \
+COLOR1 " ·s»DÂI¿ï " COLOR2 " (¡ô/¡õ)¤W¤U (PgUp/PgDn)¤W¤U­¶ (Home/End)­º§À (¡÷r)¿ï¨ú (¡ö)(q)Â÷¶} "
+
+#define FEETER_XPOST \
+COLOR1 " ¦ê¦C·j´M " COLOR2 " (y)¦^À³ (x)Âà¿ý (m)¼Ð°O (d)§R°£ (^P)µoªí (^Q)¬d¸ß§@ªÌ (t)¼ÐÅÒ "
+
+#define FEETER_MBOX \
+COLOR1 " «H«H¬Û±¤ " COLOR2 " (y)¦^«H (F/X/x)Âà±H/Âà¹F/Âà¿ý (d)§R°£ (D)°Ï¬q§R°£ (m)¼Ð°O (E)½s¿è "
+
+#define FEETER_POST \
+COLOR1 " ¤å³¹¦Cªí " COLOR2 " (ry)¦^«H (S/a)·j´M/¼ÐÃD/§@ªÌ (~G)¦ê¦C·j´M (x)Âà¿ý (V)§ë²¼ (u)·s»D "
+
+#define FEETER_GEM \
+COLOR1 " ¬ÝªOºëµØ " COLOR2 " (^P/a/f)·s¼W/¤å³¹/¥Ø¿ý (E)½s¿è (T)¼ÐÃD (m)²¾°Ê (c)½Æ»s (p^V)¶K¤W "
+
+#define FEETER_VOTEALL \
+COLOR1 " §ë²¼¤¤¤ß " COLOR2 " (¡ô/¡õ)¤W¤U (PgUp/PgDn)¤W¤U­¶ (Home/End)­º§À (¡÷)§ë²¼ (¡ö)(q)Â÷¶} "
+
+#define FEETER_HELP \
+COLOR1 " »¡©ú¤å¥ó " COLOR2 " (¡ô/¡õ)¤W¤U (PgUp/PgDn)¤W¤U­¶ (Home/End)­º§À (¡÷r)ÂsÄý (¡ö)(q)Â÷¶} "
+
+#define FEETER_INNBBS \
+COLOR1 " Âà«H³]©w " COLOR2 " (¡ô/¡õ)¤W¤U (PgUp/PgDn)¤W¤U­¶ (Home/End)­º§À (¡ö)(q)Â÷¶} "
+
+
+/* ----------------------------------------------------- */
+/* ¯¸¥x¨Ó·½Ã±¦W */
+/* ----------------------------------------------------- */
+
+/* itoc: «ØÄ³ banner ¤£­n¶W¹L¤T¦æ¡A¹Lªøªº¯¸Ã±¥i¯à·|³y¦¨¬Y¨Ç¨Ï¥ÎªÌªº¤Ï·P */
+
+#define EDIT_BANNER "\n--\n" \
+ " \033[1;42mùá\033[41mùå\033[m O\033[1mri\033[30mgi\033[mn: \033[1;41m "SCHOOLNAME"£»"BBSNAME" \033[42m "MYHOSTNAME" \033[m\n" \
+ " \033[1;44mùö\033[43mùð\033[m Au\033[1mt\033[30mho\033[mr: \033[1;31m%s\033[m ±q \033[1;36m%s\033[m µoªí\n"
+
+#define MODIFY_BANNER " \033[1;46m¢s\033[45mùö\033[m \033[1mMo\033[30mdi\033[mfy: %s ©ó \033[1;31m%s\033[m ­×§ï\n"
+
+
+/* ----------------------------------------------------- */
+/* ¨ä¥L°T®§¦r¦ê */
+/* ----------------------------------------------------- */
+
+#define VMSG_NULL " \033[1;31;44m ¡¹ ½Ð«ö¥ô·NÁäÄ~Äò ¡¹ \033[m"
+
+#define ICON_UNREAD_BRD "\033[1;31m¡Ô\033[m" /* ¥¼Åª¬ÝªO */
+#define ICON_READ_BRD " " /* ¤wŪ¬ÝªO */
+
+#define ICON_GAMBLED_BRD "\033[1;31m½ä\033[m" /* Á|¦æ½ä½L¤¤ªº¬ÝªO */
+#define ICON_VOTED_BRD "\033[1;33m§ë\033[m" /* Á|¦æ§ë²¼¤¤ªº¬ÝªO */
+#define ICON_NOTRAN_BRD "¡ã" /* ¤£Âà«HªO */
+#define ICON_TRAN_BRD "¡Û" /* Âà«HªO */
+
+#define TOKEN_ZAP_BRD '-' /* zap ªO */
+#define TOKEN_FRIEND_BRD 'P' /* ¦n¤ÍªO */
+#define TOKEN_SECRET_BRD 'X' /* ¯µ±KªO */
+
+#endif /* _THEME_H_ */
diff --git a/include/theme/theme_wd.h b/include/theme/theme_wd.h
new file mode 100644
index 0000000..b2d7db2
--- /dev/null
+++ b/include/theme/theme_wd.h
@@ -0,0 +1,246 @@
+/*-------------------------------------------------------*/
+/* theme.h ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : custom theme */
+/* create : 02/08/17 */
+/* update : / / */
+/*-------------------------------------------------------*/
+
+
+#ifndef _THEME_H_
+#define _THEME_H_
+
+
+/* ----------------------------------------------------- */
+/* °ò¥»ÃC¦â©w¸q¡A¥H§Q¤¶­±­×§ï */
+/* ----------------------------------------------------- */
+
+#define COLOR1 "\033[1;33;44m" /* footer/feeter ªº«e¬qÃC¦â */
+#define COLOR2 "\033[37;46m" /* footer/feeter ªº«á¬qÃC¦â */
+#define COLOR3 "\033[1;46m" /* neck ªºÃC¦â */
+#define COLOR4 "\033[1;46m" /* ¥ú´Î ªºÃC¦â */
+#define COLOR5 "\033[1;33;46m" /* more ÀÉÀYªº¼ÐÃDÃC¦â */
+#define COLOR6 "\033[m" /* more ÀÉÀYªº¤º®eÃC¦â */
+#define COLOR7 "\033[36m" /* §@ªÌ¦b½u¤WªºÃC¦â */
+
+
+/* ----------------------------------------------------- */
+/* ¨Ï¥ÎªÌ¦W³æÃC¦â */
+/* ----------------------------------------------------- */
+
+#define COLOR_NORMAL "" /* ¤@¯ë¨Ï¥ÎªÌ */
+#define COLOR_MYBAD "\033[1;31m" /* Ãa¤H */
+#define COLOR_MYGOOD "\033[1;32m" /* §Úªº¦n¤Í */
+#define COLOR_OGOOD "\033[1;33m" /* »P§Ú¬°¤Í */
+#define COLOR_CLOAK "\033[1;35m" /* Áô§Î */ /* itoc.µù¸Ñ: ¨S¥Î¨ì¡A­nªº¤H½Ð¦Û¦æ¥[¤J ulist_body() */
+#define COLOR_SELF "\033[1;36m" /* ¦Û¤v */
+#define COLOR_BOTHGOOD "\033[1;37m" /* ¤¬³]¦n¤Í */
+#define COLOR_BRDMATE "\033[36m" /* ªO¦ñ */
+
+
+/* ----------------------------------------------------- */
+/* ¿ï³æ¦ì¸m */
+/* ----------------------------------------------------- */
+
+/* itoc.µù¸Ñ: ª`·N MENU_XPOS ­n >= MENU_XNOTE + MOVIE_LINES */
+
+#define MENU_XNOTE 12 /* °ÊºA¬ÝªO¥Ñ (12, 0) ¶}©l */
+#define MOVIE_LINES 11 /* °Êµe³Ì¦h¦³ 11 ¦C */
+
+#define MENU_XPOS 1 /* ¿ï³æ¶}©lªº (x, y) ®y¼Ð */
+#define MENU_YPOS ((d_cols >> 1) + 4)
+
+
+/* ----------------------------------------------------- */
+/* °T®§¦r¦ê¡G*_neck() ®Éªº necker ³£§ì¥X¨Ó©w¸q¦b³o */
+/* ----------------------------------------------------- */
+
+/* necker ªº¦æ¼Æ³£¬O¤G¦æ¡A±q (1, 0) ¨ì (2, 80) */
+
+/* ©Ò¦³ªº XZ_* ³£¦³ necker¡A¥u¬O¦³¨Ç¦b *_neck()¡A¦³¨ÇÂæb *_head() */
+
+/* ulist_neck() ¤Î xpost_head() ªº²Ä¤@¦æ¤ñ¸û¯S§O¡A¤£¦b¦¹©w¸q */
+
+#define NECKER_CLASS "[¡ö]¥D¿ï³æ [¡÷]¾\\Ū [¡ô¡õ]¿ï¾Ü [c]½g¼Æ [y]¸ü¤J [/?]·j´M [s]¬ÝªO [h]»¡©ú\n" \
+ COLOR3 " %s ¬Ý ªO Ãþ§OÂà«H ¤¤ ¤å ±Ô ­z%*s ¤H®ð ªO ¥D%*s \033[m"
+
+#define NECKER_ULIST "\n" \
+ COLOR3 " ½s¸¹ ¥N¸¹ ¼ÊºÙ%*s %-*s °ÊºA ¶¢¸m \033[m"
+
+#define NECKER_PAL "[¡ö]Â÷¶} [a]·s¼W [c]­×§ï [d]§R°£ [m]±H«H [w]¤ô²y [s]¾ã²z [¡÷]¬d¸ß [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¥N ¸¹ ¤Í ½Ë%*s \033[m"
+
+#define NECKER_ALOHA "[¡ö]Â÷¶} [a]·s¼W [d]§R°£ [D]°Ï¬q§R°£ [m]±H«H [w]¤ô²y [s]­«¾ã [f]¤Þ¤J [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¤W ¯¸ ³q ª¾ ¦W ³æ%*s \033[m"
+
+#define NECKER_VOTE "[¡ö]Â÷¶} [R]µ²ªG [^P]Á|¦æ [E]­×§ï [V]¹wÄý [^Q]§ï´Á [o]¦W³æ [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¶}²¼¤é ¥D¿ì¤H §ë ²¼ ©v ¦®%*s \033[m"
+
+#define NECKER_BMW "[¡ö]Â÷¶} [d]§R°£ [D]°Ï¬q§R°£ [m]±H«H [M]Àx¦s [w]¤ô²y [s]§ó·s [¡÷]¬d¸ß [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¥N ¸¹ ¤º ®e%*s ®É¶¡ \033[m"
+
+#define NECKER_MF "[¡ö]Â÷¶} [¡÷]¶i¤J [^P]·s¼W [d]§R°£ [c]¤Á´« [C]½Æ»s [^V]¶K¤W [m]²¾°Ê [h]»¡©ú\n" \
+ COLOR3 " %s ¬Ý ªO Ãþ§OÂà«H ¤¤ ¤å ±Ô ­z%*s ¤H®ð ªO ¥D%*s \033[m"
+
+#define NECKER_COSIGN "[¡ö]Â÷¶} [¡÷]¾\\Ū [^P]¥Ó½Ð [d]§R°£ [o]¶}ªO [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á Á|¿ì¤H ¬Ý ªO ¼Ð ÃD%*s \033[m"
+
+#define NECKER_SONG "[¡ö]Â÷¶} [¡÷]ÂsÄý [o]ÂIºq¨ì¬ÝªO [m]ÂIºq¨ì«H½c [Enter]ÂsÄý [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¥D ÃD%*s [½s ¿ï] [¤é ´Á]\033[m"
+
+#define NECKER_NEWS "[¡ö]Â÷¶} [¡÷]¾\\Ū [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á §@ ªÌ ·s »D ¼Ð ÃD%*s \033[m"
+
+#define NECKER_XPOST "\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á §@ ªÌ ¤å ³¹ ¼Ð ÃD%*s µû:%s \033[m"
+
+#define NECKER_MBOX "[¡ö]Â÷¶} [¡÷,r]Ū«H [d]§R°£ [R,y](¸s²Õ)¦^«H [s]±H«H [x]Âà¿ý [X]Âà¹F [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á §@ ªÌ «H ¥ó ¼Ð ÃD%*s \033[m"
+
+#define NECKER_POST "[¡ö]Â÷¶} [¡÷]¾\\Ū [^P]µoªí [b]¶iªOµe­± [d]§R°£ [V]§ë²¼ [TAB]ºëµØ°Ï [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á §@ ªÌ ¤å ³¹ ¼Ð ÃD%*s µû:%s ¤H®ð:%-4d \033[m"
+
+#define NECKER_GEM "[¡ö]Â÷¶} [¡÷]ÂsÄý [B]¼Ò¦¡ [C]¼È¦s [F]Âà±H [d]§R°£ [h]»¡©ú %s\n" \
+ COLOR3 " ½s¸¹ ¥D ÃD%*s [½s ¿ï] [¤é ´Á]\033[m"
+
+/* ¥H¤U³o¨Ç«h¬O¤@¨ÇÃþ XZ_* µ²ºcªº necker */
+
+#define NECKER_VOTEALL "[¡ô/¡õ]¤W¤U [PgUp/PgDn]¤W¤U­¶ [Home/End]­º§À [¡÷]§ë²¼ [¡ö][q]Â÷¶}\n" \
+ COLOR3 " ½s¸¹ ¬Ý ªO Ãþ§OÂà«H ¤¤ ¤å ±Ô ­z%*s ªO ¥D%*s \033[m"
+
+#define NECKER_CREDIT "[¡ö]Â÷¶} [C]´«­¶ [1]·s¼W [2]§R°£ [3]¥þ§R [4]Á`­p\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á ¦¬¤ä ª÷ ÃB ¤ÀÃþ »¡ ©ú%*s \033[m"
+
+#define NECKER_HELP "[¡ö]Â÷¶} [¡÷]¾\\Ū [^P]·s¼W [d]§R°£ [T]¼ÐÃD [E]½s¿è [m]²¾°Ê\n" \
+ COLOR3 " ½s¸¹ ÀÉ ®× ¼Ð ÃD%*s \033[m"
+
+#define NECKER_INNBBS "[¡ö]Â÷¶} [^P]·s¼W [d]§R°£ [E]½s¿è [/]·j´M [Enter]¸Ô²Ó\n" \
+ COLOR3 " ½s¸¹ ¤º ®e%*s \033[m"
+
+
+/* ----------------------------------------------------- */
+/* °T®§¦r¦ê¡Gmore() ®Éªº footer ³£§ì¥X¨Ó©w¸q¦b³o */
+/* ----------------------------------------------------- */
+
+/* itoc.010914.µù¸Ñ: ³æ¤@½g¡A©Ò¥H¥s FOOTER¡A³£¬O 78 char */
+
+/* itoc.010821: ª`·N \\ ¬O \¡A³Ì«á§Oº|¤F¤@­ÓªÅ¥ÕÁä :p */
+
+#define FOOTER_POST \
+COLOR1 " ¤å³¹¿ïŪ " COLOR2 " (ry)¦^À³ (=\\[]<>-+;'`)¥DÃD (|?QA)·j´M¼ÐÃD§@ªÌ (kj)¤W¤U½g (C)¼È¦s "
+
+#define FOOTER_MAILER \
+COLOR1 " ³½¶­©¹ªð " COLOR2 " (ry)¦^«H/¸s²Õ (X)Âà¹F (d)§R°£ (m)¼Ð°O (C)¼È¦s (=\\[]<>-+;'`|?QAkj) "
+
+#define FOOTER_GEM \
+COLOR1 " ºëµØ¿ïŪ " COLOR2 " (=\\[]<>-+;'`)¥DÃD (|?QA)·j´M¼ÐÃD§@ªÌ (kj)¤W¤U½g (¡ô¡õ¡ö)¤W¤UÂ÷¶} "
+
+#ifdef HAVE_GAME
+#define FOOTER_TALK \
+COLOR1 " ¥æ½Í¼Ò¦¡ " COLOR2 " (^O)¹ï«³¼Ò¦¡ (^C,^D)µ²§ô¥æ½Í (^T)¤Á´«©I¥s¾¹ (^Z)§Ö±¶¦Cªí (^G)¹Í¹Í "
+#else
+#define FOOTER_TALK \
+COLOR1 " ¥æ½Í¼Ò¦¡ " COLOR2 " (^C,^D)µ²§ô¥æ½Í (^T)¤Á´«©I¥s¾¹ (^Z)§Ö±¶¦Cªí (^G)¹Í¹Í (^Y)²M°£ "
+#endif
+
+#define FOOTER_COSIGN \
+COLOR1 " ³s¸p¾÷¨î " COLOR2 " (ry)¥[¤J³s¸p (kj)¤W¤U½g (¡ô¡õ¡ö)¤W¤UÂ÷¶} (h)»¡©ú "
+
+#define FOOTER_MORE \
+COLOR1 " ÂsÄý P.%d (%d%%) " COLOR2 " (h)»¡©ú [PgUp][PgDn][0][$]²¾°Ê (/n)·j´M (C)¼È¦s (¡öq)µ²§ô "
+
+#define FOOTER_VEDIT \
+COLOR1 " %s " COLOR2 " (^Z)»¡©ú (^W)²Å¸¹ (^L)­«Ã¸ (^X)Àɮ׳B²z ùø%s¢x%sùø%5d:%3d \033[m"
+
+
+/* ----------------------------------------------------- */
+/* °T®§¦r¦ê¡Gxo_foot() ®Éªº feeter ³£§ì¥X¨Ó©w¸q¦b³o */
+/* ----------------------------------------------------- */
+
+
+/* itoc.010914.µù¸Ñ: ¦Cªí¦h½g¡A©Ò¥H¥s FEETER¡A³£¬O 78 char */
+
+#define FEETER_CLASS \
+COLOR1 " ¬ÝªO¿ï¾Ü " COLOR2 " (c)·s¤å³¹ (vV)¼Ð°O¤wŪ¥¼Åª (y)¥þ³¡¦C¥X (z)¿ï­q (A)¥þ°ì·j´M (S)±Æ§Ç "
+
+#define FEETER_ULIST \
+COLOR1 " ºô¤Í¦Cªí " COLOR2 " (f)¦n¤Í (t)²á¤Ñ (q)¬d¸ß (ad)¥æ¤Í (m)±H«H (w)¤ô²y (s)§ó·s (TAB)¤Á´« "
+
+#define FEETER_PAL \
+COLOR1 " ©IªB¤Þ¦ñ " COLOR2 " (a)·s¼W (d)§R°£ (c)¤Í½Ë (m)±H«H (f)¤Þ¤J¦n¤Í (r^Q)¬d¸ß (s)§ó·s "
+
+#define FEETER_ALOHA \
+COLOR1 " ¤W¯¸³qª¾ " COLOR2 " (a)·s¼W (d)§R°£ (D)°Ï¬q§R°£ (f)¤Þ¤J¦n¤Í (r^Q)¬d¸ß (s)§ó·s "
+
+#define FEETER_VOTE \
+COLOR1 " ¬ÝªO§ë²¼ " COLOR2 " (¡÷/r/v)§ë²¼ (R)µ²ªG (^P)·s¼W§ë²¼ (E)­×§ï (V)¹wÄý (b)¶}²¼ (o)¦W³æ "
+
+#define FEETER_BMW \
+COLOR1 " ¤ô²y¦^ÅU " COLOR2 " (d)§R°£ (D)°Ï¬q§R°£ (m)±H«H (w)¤ô²y (^R)¦^°T (^Q)¬d¸ß (s)§ó·s "
+
+#define FEETER_MF \
+COLOR1 " ³Ì·R¬ÝªO " COLOR2 " (^P)·s¼W (Cg)½Æ»s (p^V)¶K¤W (d)§R°£ (c)·s¤å³¹ (vV)¼Ð°O¤wŪ/¥¼Åª "
+
+#define FEETER_COSIGN \
+COLOR1 " ³s¸p¤p¯¸ " COLOR2 " (r)Ū¨ú (y)¦^À³ (^P)µoªí (d)§R°£ (o)¶}ªO (c)Ãö³¬ (E)½s¿è (B)³]©w "
+
+#define FEETER_SONG \
+COLOR1 " ÂIºq¨t²Î " COLOR2 " (r)Ū¨ú (o)ÂIºq¨ì¬ÝªO (m)ÂIºq¨ì«H½c (E)½s¿èÀÉ®× (T)½s¿è¼ÐÃD "
+
+#define FEETER_NEWS \
+COLOR1 " ·s»DÂI¿ï " COLOR2 " (¡ô/¡õ)¤W¤U (PgUp/PgDn)¤W¤U­¶ (Home/End)­º§À (¡÷r)¿ï¨ú (¡ö)(q)Â÷¶} "
+
+#define FEETER_XPOST \
+COLOR1 " ¦ê¦C·j´M " COLOR2 " (y)¦^À³ (x)Âà¿ý (m)¼Ð°O (d)§R°£ (^P)µoªí (^Q)¬d¸ß§@ªÌ (t)¼ÐÅÒ "
+
+#define FEETER_MBOX \
+COLOR1 " «H«H¬Û±¤ " COLOR2 " (y)¦^«H (F/X/x)Âà±H/Âà¹F/Âà¿ý (d)§R°£ (D)°Ï¬q§R°£ (m)¼Ð°O (E)½s¿è "
+
+#define FEETER_POST \
+COLOR1 " ¤å³¹¦Cªí " COLOR2 " (ry)¦^«H (S/a)·j´M/¼ÐÃD/§@ªÌ (~G)¦ê¦C·j´M (x)Âà¿ý (V)§ë²¼ (u)·s»D "
+
+#define FEETER_GEM \
+COLOR1 " ¬ÝªOºëµØ " COLOR2 " (^P/a/f)·s¼W/¤å³¹/¥Ø¿ý (E)½s¿è (T)¼ÐÃD (m)²¾°Ê (c)½Æ»s (p^V)¶K¤W "
+
+#define FEETER_VOTEALL \
+COLOR1 " §ë²¼¤¤¤ß " COLOR2 " (¡ô/¡õ)¤W¤U (PgUp/PgDn)¤W¤U­¶ (Home/End)­º§À (¡÷)§ë²¼ (¡ö)(q)Â÷¶} "
+
+#define FEETER_HELP \
+COLOR1 " »¡©ú¤å¥ó " COLOR2 " (¡ô/¡õ)¤W¤U (PgUp/PgDn)¤W¤U­¶ (Home/End)­º§À (¡÷r)ÂsÄý (¡ö)(q)Â÷¶} "
+
+#define FEETER_INNBBS \
+COLOR1 " Âà«H³]©w " COLOR2 " (¡ô/¡õ)¤W¤U (PgUp/PgDn)¤W¤U­¶ (Home/End)­º§À (¡ö)(q)Â÷¶} "
+
+
+/* ----------------------------------------------------- */
+/* ¯¸¥x¨Ó·½Ã±¦W */
+/* ----------------------------------------------------- */
+
+/* itoc: «ØÄ³ banner ¤£­n¶W¹L¤T¦æ¡A¹Lªøªº¯¸Ã±¥i¯à·|³y¦¨¬Y¨Ç¨Ï¥ÎªÌªº¤Ï·P */
+
+#define EDIT_BANNER "\n--\n" \
+ "\033[36m¡° ¨Ó¦Û¡G"BBSNAME" ("MYHOSTNAME")\033[m\n" \
+ "\033[1;31m¡À \033[36mPost by \033[37m%-12s \033[36mfrom \033[33m%s\033[m\n"
+
+#define MODIFY_BANNER "\033[1;31m¡À \033[36mEdit by \033[37m%-12s \033[36mat \033[33m%s\033[m\n"
+
+
+/* ----------------------------------------------------- */
+/* ¨ä¥L°T®§¦r¦ê */
+/* ----------------------------------------------------- */
+
+#define VMSG_NULL "\033[1;46m ¡¹ ½Ð«ö (Space/Return) Ä~Äò ¡¹ \033[m"
+
+#define ICON_UNREAD_BRD "\033[36m¡´" /* ¥¼Åª¬ÝªO */
+#define ICON_READ_BRD " " /* ¤wŪ¬ÝªO */
+
+#define ICON_GAMBLED_BRD "\033[1;31m½ä\033[m" /* Á|¦æ½ä½L¤¤ªº¬ÝªO */
+#define ICON_VOTED_BRD "\033[1;33m§ë\033[m" /* Á|¦æ§ë²¼¤¤ªº¬ÝªO */
+#define ICON_NOTRAN_BRD "¡¸" /* ¤£Âà«HªO */
+#define ICON_TRAN_BRD "¡¹" /* Âà«HªO */
+
+#define TOKEN_ZAP_BRD '-' /* zap ªO */
+#define TOKEN_FRIEND_BRD '-' /* ¦n¤ÍªO */
+#define TOKEN_SECRET_BRD ')' /* ¯µ±KªO */
+
+#endif /* _THEME_H_ */
diff --git a/include/theme/theme_yellow.h b/include/theme/theme_yellow.h
new file mode 100644
index 0000000..f0742cd
--- /dev/null
+++ b/include/theme/theme_yellow.h
@@ -0,0 +1,246 @@
+/*-------------------------------------------------------*/
+/* theme.h ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : custom theme */
+/* create : 02/08/17 */
+/* update : / / */
+/*-------------------------------------------------------*/
+
+
+#ifndef _THEME_H_
+#define _THEME_H_
+
+
+/* ----------------------------------------------------- */
+/* °ò¥»ÃC¦â©w¸q¡A¥H§Q¤¶­±­×§ï */
+/* ----------------------------------------------------- */
+
+#define COLOR1 "\033[1;33;45m" /* footer/feeter ªº«e¬qÃC¦â */
+#define COLOR2 "\033[0;30;43m" /* footer/feeter ªº«á¬qÃC¦â */
+#define COLOR3 "\033[1;33;41m" /* neck ªºÃC¦â */
+#define COLOR4 "\033[1;43m" /* ¥ú´Î ªºÃC¦â */
+#define COLOR5 "\033[1;33;46m" /* more ÀÉÀYªº¼ÐÃDÃC¦â */
+#define COLOR6 "\033[37;43m" /* more ÀÉÀYªº¤º®eÃC¦â */
+#define COLOR7 "\033[1;33m" /* §@ªÌ¦b½u¤WªºÃC¦â */
+
+
+/* ----------------------------------------------------- */
+/* ¨Ï¥ÎªÌ¦W³æÃC¦â */
+/* ----------------------------------------------------- */
+
+#define COLOR_NORMAL "" /* ¤@¯ë¨Ï¥ÎªÌ */
+#define COLOR_MYBAD "\033[1;31m" /* Ãa¤H */
+#define COLOR_MYGOOD "\033[1;32m" /* §Úªº¦n¤Í */
+#define COLOR_OGOOD "\033[1;33m" /* »P§Ú¬°¤Í */
+#define COLOR_CLOAK "\033[1;35m" /* Áô§Î */ /* itoc.µù¸Ñ: ¨S¥Î¨ì¡A­nªº¤H½Ð¦Û¦æ¥[¤J ulist_body() */
+#define COLOR_SELF "\033[1;36m" /* ¦Û¤v */
+#define COLOR_BOTHGOOD "\033[1;37m" /* ¤¬³]¦n¤Í */
+#define COLOR_BRDMATE "\033[36m" /* ªO¦ñ */
+
+
+/* ----------------------------------------------------- */
+/* ¿ï³æ¦ì¸m */
+/* ----------------------------------------------------- */
+
+/* itoc.µù¸Ñ: ª`·N MENU_XPOS ­n >= MENU_XNOTE + MOVIE_LINES */
+
+#define MENU_XNOTE 2 /* °ÊºA¬ÝªO¥Ñ (2, 0) ¶}©l */
+#define MOVIE_LINES 10 /* °Êµe³Ì¦h¦³ 10 ¦C */
+
+#define MENU_XPOS 13 /* ¿ï³æ¶}©lªº (x, y) ®y¼Ð */
+#define MENU_YPOS ((d_cols >> 1) + 18)
+
+
+/* ----------------------------------------------------- */
+/* °T®§¦r¦ê¡G*_neck() ®Éªº necker ³£§ì¥X¨Ó©w¸q¦b³o */
+/* ----------------------------------------------------- */
+
+/* necker ªº¦æ¼Æ³£¬O¤G¦æ¡A±q (1, 0) ¨ì (2, 80) */
+
+/* ©Ò¦³ªº XZ_* ³£¦³ necker¡A¥u¬O¦³¨Ç¦b *_neck()¡A¦³¨ÇÂæb *_head() */
+
+/* ulist_neck() ¤Î xpost_head() ªº²Ä¤@¦æ¤ñ¸û¯S§O¡A¤£¦b¦¹©w¸q */
+
+#define NECKER_CLASS "[¡ö]¥D¿ï³æ [¡÷]¾\\Ū [¡ô¡õ]¿ï¾Ü [c]½g¼Æ [y]¸ü¤J [/?]·j´M [s]¬ÝªO [h]»¡©ú\n" \
+ COLOR3 " %s ¬Ý ªO Ãþ§OÂà«H ¤¤ ¤å ±Ô ­z%*s ¤H®ð ªO ¥D%*s \033[m"
+
+#define NECKER_ULIST "\n" \
+ COLOR3 " ½s¸¹ ¥N¸¹ ¼ÊºÙ%*s %-*s °ÊºA ¶¢¸m \033[m"
+
+#define NECKER_PAL "[¡ö]Â÷¶} [a]·s¼W [c]­×§ï [d]§R°£ [m]±H«H [w]¤ô²y [s]¾ã²z [¡÷]¬d¸ß [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¥N ¸¹ ¤Í ½Ë%*s \033[m"
+
+#define NECKER_ALOHA "[¡ö]Â÷¶} [a]·s¼W [d]§R°£ [D]°Ï¬q§R°£ [m]±H«H [w]¤ô²y [s]­«¾ã [f]¤Þ¤J [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¤W ¯¸ ³q ª¾ ¦W ³æ%*s \033[m"
+
+#define NECKER_VOTE "[¡ö]Â÷¶} [R]µ²ªG [^P]Á|¦æ [E]­×§ï [V]¹wÄý [^Q]§ï´Á [o]¦W³æ [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¶}²¼¤é ¥D¿ì¤H §ë ²¼ ©v ¦®%*s \033[m"
+
+#define NECKER_BMW "[¡ö]Â÷¶} [d]§R°£ [D]°Ï¬q§R°£ [m]±H«H [M]Àx¦s [w]¤ô²y [s]§ó·s [¡÷]¬d¸ß [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¥N ¸¹ ¤º ®e%*s ®É¶¡ \033[m"
+
+#define NECKER_MF "[¡ö]Â÷¶} [¡÷]¶i¤J [^P]·s¼W [d]§R°£ [c]¤Á´« [C]½Æ»s [^V]¶K¤W [m]²¾°Ê [h]»¡©ú\n" \
+ COLOR3 " %s ¬Ý ªO Ãþ§OÂà«H ¤¤ ¤å ±Ô ­z%*s ¤H®ð ªO ¥D%*s \033[m"
+
+#define NECKER_COSIGN "[¡ö]Â÷¶} [¡÷]¾\\Ū [^P]¥Ó½Ð [d]§R°£ [o]¶}ªO [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á Á|¿ì¤H ¬Ý ªO ¼Ð ÃD%*s \033[m"
+
+#define NECKER_SONG "[¡ö]Â÷¶} [¡÷]ÂsÄý [o]ÂIºq¨ì¬ÝªO [m]ÂIºq¨ì«H½c [Enter]ÂsÄý [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¥D ÃD%*s [½s ¿ï] [¤é ´Á]\033[m"
+
+#define NECKER_NEWS "[¡ö]Â÷¶} [¡÷]¾\\Ū [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á §@ ªÌ ·s »D ¼Ð ÃD%*s \033[m"
+
+#define NECKER_XPOST "\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á §@ ªÌ ¤å ³¹ ¼Ð ÃD%*s µû:%s \033[m"
+
+#define NECKER_MBOX "[¡ö]Â÷¶} [¡÷,r]Ū«H [d]§R°£ [R,y](¸s²Õ)¦^«H [s]±H«H [x]Âà¿ý [X]Âà¹F [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á §@ ªÌ «H ¥ó ¼Ð ÃD%*s \033[m"
+
+#define NECKER_POST "[¡ö]Â÷¶} [¡÷]¾\\Ū [^P]µoªí [b]¶iªOµe­± [d]§R°£ [V]§ë²¼ [TAB]ºëµØ°Ï [h]»¡©ú\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á §@ ªÌ ¤å ³¹ ¼Ð ÃD%*s µû:%s ¤H®ð:%-4d \033[m"
+
+#define NECKER_GEM "[¡ö]Â÷¶} [¡÷]ÂsÄý [B]¼Ò¦¡ [C]¼È¦s [F]Âà±H [d]§R°£ [h]»¡©ú %s\n" \
+ COLOR3 " ½s¸¹ ¥D ÃD%*s [½s ¿ï] [¤é ´Á]\033[m"
+
+/* ¥H¤U³o¨Ç«h¬O¤@¨ÇÃþ XZ_* µ²ºcªº necker */
+
+#define NECKER_VOTEALL "[¡ô/¡õ]¤W¤U [PgUp/PgDn]¤W¤U­¶ [Home/End]­º§À [¡÷]§ë²¼ [¡ö][q]Â÷¶}\n" \
+ COLOR3 " ½s¸¹ ¬Ý ªO Ãþ§OÂà«H ¤¤ ¤å ±Ô ­z%*s ªO ¥D%*s \033[m"
+
+#define NECKER_CREDIT "[¡ö]Â÷¶} [C]´«­¶ [1]·s¼W [2]§R°£ [3]¥þ§R [4]Á`­p\n" \
+ COLOR3 " ½s¸¹ ¤é ´Á ¦¬¤ä ª÷ ÃB ¤ÀÃþ »¡ ©ú%*s \033[m"
+
+#define NECKER_HELP "[¡ö]Â÷¶} [¡÷]¾\\Ū [^P]·s¼W [d]§R°£ [T]¼ÐÃD [E]½s¿è [m]²¾°Ê\n" \
+ COLOR3 " ½s¸¹ ÀÉ ®× ¼Ð ÃD%*s \033[m"
+
+#define NECKER_INNBBS "[¡ö]Â÷¶} [^P]·s¼W [d]§R°£ [E]½s¿è [/]·j´M [Enter]¸Ô²Ó\n" \
+ COLOR3 " ½s¸¹ ¤º ®e%*s \033[m"
+
+
+/* ----------------------------------------------------- */
+/* °T®§¦r¦ê¡Gmore() ®Éªº footer ³£§ì¥X¨Ó©w¸q¦b³o */
+/* ----------------------------------------------------- */
+
+/* itoc.010914.µù¸Ñ: ³æ¤@½g¡A©Ò¥H¥s FOOTER¡A³£¬O 78 char */
+
+/* itoc.010821: ª`·N \\ ¬O \¡A³Ì«á§Oº|¤F¤@­ÓªÅ¥ÕÁä :p */
+
+#define FOOTER_POST \
+COLOR1 " ¤å³¹¿ïŪ " COLOR2 " (ry)¦^À³ (=\\[]<>-+;'`)¥DÃD (|?QA)·j´M¼ÐÃD§@ªÌ (kj)¤W¤U½g (C)¼È¦s "
+
+#define FOOTER_MAILER \
+COLOR1 " ³½¶­©¹ªð " COLOR2 " (ry)¦^«H/¸s²Õ (X)Âà¹F (d)§R°£ (m)¼Ð°O (C)¼È¦s (=\\[]<>-+;'`|?QAkj) "
+
+#define FOOTER_GEM \
+COLOR1 " ºëµØ¿ïŪ " COLOR2 " (=\\[]<>-+;'`)¥DÃD (|?QA)·j´M¼ÐÃD§@ªÌ (kj)¤W¤U½g (¡ô¡õ¡ö)¤W¤UÂ÷¶} "
+
+#ifdef HAVE_GAME
+#define FOOTER_TALK \
+COLOR1 " ¥æ½Í¼Ò¦¡ " COLOR2 " (^O)¹ï«³¼Ò¦¡ (^C,^D)µ²§ô¥æ½Í (^T)¤Á´«©I¥s¾¹ (^Z)§Ö±¶¦Cªí (^G)¹Í¹Í "
+#else
+#define FOOTER_TALK \
+COLOR1 " ¥æ½Í¼Ò¦¡ " COLOR2 " (^C,^D)µ²§ô¥æ½Í (^T)¤Á´«©I¥s¾¹ (^Z)§Ö±¶¦Cªí (^G)¹Í¹Í (^Y)²M°£ "
+#endif
+
+#define FOOTER_COSIGN \
+COLOR1 " ³s¸p¾÷¨î " COLOR2 " (ry)¥[¤J³s¸p (kj)¤W¤U½g (¡ô¡õ¡ö)¤W¤UÂ÷¶} (h)»¡©ú "
+
+#define FOOTER_MORE \
+COLOR1 " ÂsÄý P.%d (%d%%) " COLOR2 " (h)»¡©ú [PgUp][PgDn][0][$]²¾°Ê (/n)·j´M (C)¼È¦s (¡öq)µ²§ô "
+
+#define FOOTER_VEDIT \
+COLOR1 " %s " COLOR2 " (^Z)»¡©ú (^W)²Å¸¹ (^L)­«Ã¸ (^X)Àɮ׳B²z ùø%s¢x%sùø%5d:%3d \033[m"
+
+
+/* ----------------------------------------------------- */
+/* °T®§¦r¦ê¡Gxo_foot() ®Éªº feeter ³£§ì¥X¨Ó©w¸q¦b³o */
+/* ----------------------------------------------------- */
+
+
+/* itoc.010914.µù¸Ñ: ¦Cªí¦h½g¡A©Ò¥H¥s FEETER¡A³£¬O 78 char */
+
+#define FEETER_CLASS \
+COLOR1 " ¬ÝªO¿ï¾Ü " COLOR2 " (c)·s¤å³¹ (vV)¼Ð°O¤wŪ¥¼Åª (y)¥þ³¡¦C¥X (z)¿ï­q (A)¥þ°ì·j´M (S)±Æ§Ç "
+
+#define FEETER_ULIST \
+COLOR1 " ºô¤Í¦Cªí " COLOR2 " (f)¦n¤Í (t)²á¤Ñ (q)¬d¸ß (ad)¥æ¤Í (m)±H«H (w)¤ô²y (s)§ó·s (TAB)¤Á´« "
+
+#define FEETER_PAL \
+COLOR1 " ©IªB¤Þ¦ñ " COLOR2 " (a)·s¼W (d)§R°£ (c)¤Í½Ë (m)±H«H (f)¤Þ¤J¦n¤Í (r^Q)¬d¸ß (s)§ó·s "
+
+#define FEETER_ALOHA \
+COLOR1 " ¤W¯¸³qª¾ " COLOR2 " (a)·s¼W (d)§R°£ (D)°Ï¬q§R°£ (f)¤Þ¤J¦n¤Í (r^Q)¬d¸ß (s)§ó·s "
+
+#define FEETER_VOTE \
+COLOR1 " ¬ÝªO§ë²¼ " COLOR2 " (¡÷/r/v)§ë²¼ (R)µ²ªG (^P)·s¼W§ë²¼ (E)­×§ï (V)¹wÄý (b)¶}²¼ (o)¦W³æ "
+
+#define FEETER_BMW \
+COLOR1 " ¤ô²y¦^ÅU " COLOR2 " (d)§R°£ (D)°Ï¬q§R°£ (m)±H«H (w)¤ô²y (^R)¦^°T (^Q)¬d¸ß (s)§ó·s "
+
+#define FEETER_MF \
+COLOR1 " ³Ì·R¬ÝªO " COLOR2 " (^P)·s¼W (Cg)½Æ»s (p^V)¶K¤W (d)§R°£ (c)·s¤å³¹ (vV)¼Ð°O¤wŪ/¥¼Åª "
+
+#define FEETER_COSIGN \
+COLOR1 " ³s¸p¤p¯¸ " COLOR2 " (r)Ū¨ú (y)¦^À³ (^P)µoªí (d)§R°£ (o)¶}ªO (c)Ãö³¬ (E)½s¿è (B)³]©w "
+
+#define FEETER_SONG \
+COLOR1 " ÂIºq¨t²Î " COLOR2 " (r)Ū¨ú (o)ÂIºq¨ì¬ÝªO (m)ÂIºq¨ì«H½c (E)½s¿èÀÉ®× (T)½s¿è¼ÐÃD "
+
+#define FEETER_NEWS \
+COLOR1 " ·s»DÂI¿ï " COLOR2 " (¡ô/¡õ)¤W¤U (PgUp/PgDn)¤W¤U­¶ (Home/End)­º§À (¡÷r)¿ï¨ú (¡ö)(q)Â÷¶} "
+
+#define FEETER_XPOST \
+COLOR1 " ¦ê¦C·j´M " COLOR2 " (y)¦^À³ (x)Âà¿ý (m)¼Ð°O (d)§R°£ (^P)µoªí (^Q)¬d¸ß§@ªÌ (t)¼ÐÅÒ "
+
+#define FEETER_MBOX \
+COLOR1 " «H«H¬Û±¤ " COLOR2 " (y)¦^«H (F/X/x)Âà±H/Âà¹F/Âà¿ý (d)§R°£ (D)°Ï¬q§R°£ (m)¼Ð°O (E)½s¿è "
+
+#define FEETER_POST \
+COLOR1 " ¤å³¹¦Cªí " COLOR2 " (ry)¦^«H (S/a)·j´M/¼ÐÃD/§@ªÌ (~G)¦ê¦C·j´M (x)Âà¿ý (V)§ë²¼ (u)·s»D "
+
+#define FEETER_GEM \
+COLOR1 " ¬ÝªOºëµØ " COLOR2 " (^P/a/f)·s¼W/¤å³¹/¥Ø¿ý (E)½s¿è (T)¼ÐÃD (m)²¾°Ê (c)½Æ»s (p^V)¶K¤W "
+
+#define FEETER_VOTEALL \
+COLOR1 " §ë²¼¤¤¤ß " COLOR2 " (¡ô/¡õ)¤W¤U (PgUp/PgDn)¤W¤U­¶ (Home/End)­º§À (¡÷)§ë²¼ (¡ö)(q)Â÷¶} "
+
+#define FEETER_HELP \
+COLOR1 " »¡©ú¤å¥ó " COLOR2 " (¡ô/¡õ)¤W¤U (PgUp/PgDn)¤W¤U­¶ (Home/End)­º§À (¡÷r)ÂsÄý (¡ö)(q)Â÷¶} "
+
+#define FEETER_INNBBS \
+COLOR1 " Âà«H³]©w " COLOR2 " (¡ô/¡õ)¤W¤U (PgUp/PgDn)¤W¤U­¶ (Home/End)­º§À (¡ö)(q)Â÷¶} "
+
+
+/* ----------------------------------------------------- */
+/* ¯¸¥x¨Ó·½Ã±¦W */
+/* ----------------------------------------------------- */
+
+/* itoc: «ØÄ³ banner ¤£­n¶W¹L¤T¦æ¡A¹Lªøªº¯¸Ã±¥i¯à·|³y¦¨¬Y¨Ç¨Ï¥ÎªÌªº¤Ï·P */
+
+#define EDIT_BANNER "\n--\n" \
+ " \033[1;41m¡÷\033[44m¡õ\033[m O\033[1mri\033[30mgi\033[mn: \033[1;43m "SCHOOLNAME"£»"BBSNAME" \033[47m "MYHOSTNAME" \033[m\n" \
+ " \033[1;45m¡ô\033[42m¡ö\033[m Au\033[1mt\033[30mho\033[mr: \033[1;33m%s\033[m ±q \033[1;34m%s\033[m µoªí\n"
+
+#define MODIFY_BANNER " \033[1;43m¡ù\033[46m¡ø\033[m \033[1mMo\033[30mdi\033[mfy: %s ©ó \033[1;33m%s\033[m ­×§ï\n"
+
+
+/* ----------------------------------------------------- */
+/* ¨ä¥L°T®§¦r¦ê */
+/* ----------------------------------------------------- */
+
+#define VMSG_NULL " \033[1;43m ¡ò ½Ð«ö¥ô·NÁäÄ~Äò ¡ò \033[m"
+
+#define ICON_UNREAD_BRD "\033[1;33m£k" /* ¥¼Åª¬ÝªO */
+#define ICON_READ_BRD " " /* ¤wŪ¬ÝªO */
+
+#define ICON_GAMBLED_BRD "\033[1;31m½ä\033[m" /* Á|¦æ½ä½L¤¤ªº¬ÝªO */
+#define ICON_VOTED_BRD "\033[1;33m§ë\033[m" /* Á|¦æ§ë²¼¤¤ªº¬ÝªO */
+#define ICON_NOTRAN_BRD "¡º" /* ¤£Âà«HªO */
+#define ICON_TRAN_BRD "¡»" /* Âà«HªO */
+
+#define TOKEN_ZAP_BRD '-' /* zap ªO */
+#define TOKEN_FRIEND_BRD '#' /* ¦n¤ÍªO */
+#define TOKEN_SECRET_BRD ']' /* ¯µ±KªO */
+
+#endif /* _THEME_H_ */
diff --git a/include/ufo.h b/include/ufo.h
new file mode 100644
index 0000000..4ed7d4b
--- /dev/null
+++ b/include/ufo.h
@@ -0,0 +1,173 @@
+/*-------------------------------------------------------*/
+/* ufo.h ( NTHU CS MapleBBS Ver 2.36 ) */
+/*-------------------------------------------------------*/
+/* target : User Flag Option */
+/* create : 95/03/29 */
+/* update : 95/12/15 */
+/*-------------------------------------------------------*/
+
+
+#ifndef _UFO_H_
+#define _UFO_H_
+
+
+/* ----------------------------------------------------- */
+/* User Flag Option : flags in ACCT.ufo */
+/* ----------------------------------------------------- */
+
+
+#define BFLAG(n) (1 << n) /* 32 bit-wise flag */
+
+
+#define UFO_NOUSE00 BFLAG(0) /* ¨S¥Î¨ì */
+#define UFO_MOVIE BFLAG(1) /* °ÊºA¬ÝªOÅã¥Ü */
+#define UFO_BRDPOST BFLAG(2) /* 1: ¬ÝªO¦CªíÅã¥Ü½g¼Æ 0: ¬ÝªO¦CªíÅã¥Ü¸¹½X itoc.010912: «í¬°·s¤å³¹¼Ò¦¡ */
+#define UFO_BRDNAME BFLAG(3) /* itoc.010413: ¬ÝªO¦Cªí¨Ì 1:brdname 0:class+title ±Æ§Ç */
+#define UFO_BRDNOTE BFLAG(4) /* Åã¥Ü¶iªOµe­± */
+#define UFO_VEDIT BFLAG(5) /* ²¤Æ½s¿è¾¹ */
+#define UFO_MOTD BFLAG(6) /* ²¤Æ¶i/Â÷¯¸µe­± */
+
+#define UFO_PAGER BFLAG(7) /* Ãö³¬©I¥s¾¹ */
+#define UFO_RCVER BFLAG(8) /* itoc.010716: ©Ú¦¬¼s¼½ */
+#define UFO_QUIET BFLAG(9) /* µ²Ãf¦b¤H¹Ò¡A¦ÓµL¨®°¨³Ù */
+#define UFO_PAL BFLAG(10) /* ¨Ï¥ÎªÌ¦W³æ¥uÅã¥Ü¦n¤Í */
+#define UFO_ALOHA BFLAG(11) /* ±µ¨ü¤W¯¸³qª¾ */
+#define UFO_NOALOHA BFLAG(12) /* itoc.010716: ¤W¯¸¤£³qª¾/¨ó´M */
+
+#define UFO_BMWDISPLAY BFLAG(13) /* itoc.010315: ¤ô²y¦^ÅU¤¶­± */
+#define UFO_NWLOG BFLAG(14) /* lkchu.990510: ¤£¦s¹ï¸Ü¬ö¿ý */
+#define UFO_NTLOG BFLAG(15) /* lkchu.990510: ¤£¦s²á¤Ñ¬ö¿ý */
+
+#define UFO_NOSIGN BFLAG(16) /* itoc.000320: ¤£¨Ï¥Îñ¦WÀÉ */
+#define UFO_SHOWSIGN BFLAG(17) /* itoc.000319: ¦sÀÉ«eÅã¥Üñ¦WÀÉ */
+
+#define UFO_ZHC BFLAG(18) /* hightman.060504: ¥þ«¬¦r°»´ú */
+#define UFO_JUMPBRD BFLAG(19) /* itoc.020122: ¦Û°Ê¸õ¥h¤U¤@­Ó¥¼Åª¬ÝªO */
+#define UFO_NOUSE20 BFLAG(20)
+#define UFO_NOUSE21 BFLAG(21)
+#define UFO_NOUSE22 BFLAG(22)
+#define UFO_NOUSE23 BFLAG(23)
+
+#define UFO_CLOAK BFLAG(24) /* 1: ¶i¤JÁô§Î */
+#define UFO_SUPERCLOAK BFLAG(25) /* 1: ¶W¯ÅÁô¨­ */
+#define UFO_ACL BFLAG(26) /* 1: ¨Ï¥Î ACL */
+#define UFO_NOUSE27 BFLAG(27)
+#define UFO_NOUSE28 BFLAG(28)
+#define UFO_NOUSE29 BFLAG(29)
+#define UFO_NOUSE30 BFLAG(30)
+#define UFO_NOUSE31 BFLAG(31)
+
+/* ·sµù¥U±b¸¹¡Bguest ªº¹w³] ufo */
+
+#define UFO_DEFAULT_NEW (UFO_MOVIE | UFO_BRDNOTE | UFO_MOTD | UFO_BMWDISPLAY | UFO_NOSIGN)
+#define UFO_DEFAULT_GUEST (UFO_MOVIE | UFO_BRDNOTE | UFO_QUIET | UFO_NOALOHA | UFO_NWLOG | UFO_NTLOG | UFO_NOSIGN)
+
+
+/* ----------------------------------------------------- */
+/* Status : flags in UTMP.status */
+/* ----------------------------------------------------- */
+
+
+#define STATUS_BIFF BFLAG(0) /* ¦³·s«H¥ó */
+#define STATUS_REJECT BFLAG(1) /* true if reject any body */
+#define STATUS_BIRTHDAY BFLAG(2) /* ¤µ¤Ñ¥Í¤é */
+#define STATUS_COINLOCK BFLAG(3) /* ¿ú¹ôÂê©w */
+#define STATUS_DATALOCK BFLAG(4) /* ¸ê®ÆÂê©w */
+#define STATUS_MQUOTA BFLAG(5) /* «H½c¤¤¦³¹L´Á¤§«H¥ó */
+#define STATUS_MAILOVER BFLAG(6) /* «H½c¹L¦h«H¥ó */
+#define STATUS_MGEMOVER BFLAG(7) /* ­Ó¤HºëµØ°Ï¹L¦h */
+#define STATUS_EDITHELP BFLAG(8) /* ¦b edit ®É¶i¤J help */
+#define STATUS_PALDIRTY BFLAG(9) /* ¦³¤H¦b¥LªºªB¤Í¦W³æ·s¼W©Î²¾°£¤F§Ú */
+#define STATUS_FOOLDAY BFLAG(10)
+
+#define HAS_STATUS(x) (cutmp->status&(x))
+
+
+/* ----------------------------------------------------- */
+/* ¦UºØ²ßºDªº¤¤¤å·N¸q */
+/* ----------------------------------------------------- */
+
+
+/* itoc.000320: ¼W´î¶µ¥Ø­n§ó§ï NUMUFOS_* ¤j¤p, ¤]§O§Ñ¤F§ï STR_UFO */
+
+#define NUMUFOS 27
+#define NUMUFOS_GUEST 5 /* guest ¥i¥H¥Î«e 5 ­Ó ufo */
+#define NUMUFOS_USER 20 /* ¤@¯ë¨Ï¥ÎªÌ ¥i¥H¥Î«e 20 ­Ó ufo */
+
+#define STR_UFO "-mpsnemPBQFANbwtSHZJ----CHA" /* itoc: ·s¼W²ßºDªº®É­Ô§O§Ñ¤F§ï³o¸Ì°Ú */
+
+
+#ifdef _ADMIN_C_
+
+char *ufo_tbl[NUMUFOS] =
+{
+ "«O¯d", /* UFO_NOUSE */
+ "Åã¥Ü°ÊºA¬ÝªO (¶}±Ò/Ãö³¬)", /* UFO_MOVIE */
+
+ "¬ÝªO¦CªíÅã¥Ü (¤å³¹¼Æ/½s¸¹)", /* UFO_BRDPOST */
+ "¬ÝªO¦Cªí±Æ§Ç¨Ì (¦r¥À/¤ÀÃþ)", /* UFO_BRDNAME */ /* itoc.010413: ¬ÝªO¨Ì·Ó¦r¥À/¤ÀÃþ±Æ§Ç */
+ "¶iªOµe­± (Åã¥Ü/¸õ¹L)", /* UFO_BRDNOTE */
+ "¤å³¹½s¿è¾¹ (²¤Æ/§¹¾ã)", /* UFO_VEDIT */
+ "¶i/Â÷¯¸µe­± (²¤Æ/§¹¾ã)", /* UFO_MOTD */
+
+ "½Ö¯à¥á§A¤ô²y(¥u¦³¦n¤Í/¥þ³¡)", /* UFO_PAGER */
+#ifdef HAVE_NOBROAD
+ "¤£±µ¨ü§O¤H¼s¼½ (©Ú¦¬/±µ¦¬)", /* UFO_RCVER */
+#else
+ "«O¯d",
+#endif
+ "¤£±µ¨ü¥ô¦ó¤ô²y (©Ú¦¬/±µ¦¬)", /* UFO_QUITE */
+
+ "¨Ï¥ÎªÌ¦W³æÅã¥Ü (¦n¤Í/¥þ³¡)", /* UFO_PAL */
+
+#ifdef HAVE_ALOHA
+ "§O¤H¤W¯¸®É³qª¾§Ú(³qª¾/¨ú®ø)", /* UFO_ALOHA */
+#else
+ "«O¯d",
+#endif
+#ifdef HAVE_NOALOHA
+ "¤W¯¸¤£³qª¾§O¤H(¤£³qª¾/³qª¾)", /* UFO_NOALOHA */
+#else
+ "«O¯d",
+#endif
+
+#ifdef BMW_DISPLAY
+ "¤ô²y¦^ÅU¤¶­± (§¹¾ã/¤W¦¸)", /* UFO_BMWDISPLAY */
+#else
+ "«O¯d",
+#endif
+ "Â÷¯¸®É§R°£¤ô²y¡H(§R±¼/°Ý§Ú)", /* UFO_NWLOG */
+ "Â÷¯¸§R°£²á¤Ñ¬ö¿ý(§R±¼/°Ý§Ú)", /* UFO_NTLOG */
+
+ "¤£¨Ï¥Îñ¦WÀÉ (¤£¥Î/¿ï¾Ü)", /* UFO_NOSIGN */
+ "Åã¥Üñ¦WÀÉ (Åã¥Ü/¤£¬Ý)", /* UFO_SHOWSIGN */
+
+#ifdef HAVE_MULTI_BYTE
+ "¥þ«¬¦r°»´ú (°»´ú/¤£¥Î)", /* UFO_ZHC */
+#else
+ "«O¯d",
+#endif
+
+#ifdef AUTO_JUMPBRD
+ "¦Û°Ê¸õ¨ì¥¼Åª¬ÝªO(¸õ¥h/¤£¸õ)", /* UFO_JUMPBRD */
+#else
+ "«O¯d",
+#endif
+
+ "«O¯d",
+ "«O¯d",
+ "«O¯d",
+ "«O¯d",
+
+ "Áô¨­³N (Áô¨­/²{¨­)", /* UFO_CLOAK */
+#ifdef HAVE_SUPERCLOAK
+ "¶W¯ÅÁô¨­³N (µµÁô/²{¨­)", /* UFO_SUPERCLOAK */
+#else
+ "«O¯d",
+#endif
+
+ "¯¸ªø¤W¯¸¨Ó·½ (­­¨î/¥ô·N)" /* UFO_ACL */
+};
+#endif
+
+#endif /* _UFO_H_ */
diff --git a/include/xchat.h b/include/xchat.h
new file mode 100644
index 0000000..b4ec436
--- /dev/null
+++ b/include/xchat.h
@@ -0,0 +1,136 @@
+/*-------------------------------------------------------*/
+/* xchat.h ( NTHU CS MapleBBS Ver 3.00 ) */
+/*-------------------------------------------------------*/
+/* target : definitions that xchat client/server used */
+/* create : 97/09/19 */
+/* update : 97/09/19 */
+/*-------------------------------------------------------*/
+
+
+#ifndef _XCHAT_H_
+#define _XCHAT_H_
+
+
+#define XCHAT_VERSION_MAJOR 3
+#define XCHAT_VERSION_MINOR 0
+
+
+/* ----------------------------------------------------- */
+/* XCHAT response code : RFI 3-digit */
+/* ----------------------------------------------------- */
+/* Response : */
+/* 1xx Informative message */
+/* 2xx Command ok */
+/* 3xx Command ok so far, send the rest of it */
+/* 4xx Command correct, but NG for some reason */
+/* 5xx Command unimplemented, incorrect, or serious */
+/* program error occurred */
+/* Function : */
+/* x0x Connection, setup, and miscellaneous messages */
+/* x1x Newsgroup selection */
+/* x2x Article selection */
+/* x3x Distribution functions */
+/* x4x Posting */
+/* x8x Nonstandard extensions (AUTHINFO, XGTITLE) */
+/* x9x Debugging output */
+/* Information : */
+/* No defined semantics */
+/* ----------------------------------------------------- */
+
+
+/* ¨Ñ·sª© client ¨Ï¥Î */
+
+
+#define MSG_LOGINOK 100
+#define MSG_VERSION 103
+#define MSG_MESSAGE 106
+
+#define MSG_CHATROOM 110
+#define MSG_TOPIC 113
+#define MSG_ROOM 116
+#define MSG_NICK 118
+#define MSG_CLRSCR 120
+
+#define MSG_MOTDSTART 130
+#define MSG_MOTD 330
+#define MSG_MOTDEND 230
+
+#define MSG_ROOMLISTSTART 133
+#define MSG_ROOMLIST 333
+#define MSG_ROOMLISTEND 233
+#define MSG_ROOMNOTIFY 134
+
+#define MSG_USERLISTSTART 136
+#define MSG_USERLIST 336
+#define MSG_USERLISTEND 236
+#define MSG_USERNOTIFY 137
+
+#define MSG_PARTYINFO 140
+#define MSG_PARTYLISTSTART 340
+#define MSG_PARTYLIST 240
+#define MSG_PARTYLISTEND 141
+
+#define MSG_PRIVMSG 145
+#define MSG_MYPRIVMSG 146
+
+#define ERR_LOGIN_NICKINUSE 501
+#define ERR_LOGIN_NICKERROR 502
+#define ERR_LOGIN_USERONLINE 503
+#define ERR_LOGIN_NOSUCHUSER 504
+#define ERR_LOGIN_PASSERROR 505
+
+
+static int
+Isspace(ch)
+ int ch;
+{
+ return (ch == ' ' || ch == '\t' || ch == 10 || ch == 13);
+}
+
+
+static char *
+nextword(str)
+ char **str;
+{
+ char *head, *tail;
+ int ch;
+
+ head = *str;
+ for (;;)
+ {
+
+ ch = *head;
+ if (!ch)
+ {
+ *str = head;
+ return head;
+ }
+ if (!Isspace(ch))
+ break;
+ head++;
+ }
+
+ tail = head + 1;
+ while (ch = *tail)
+ {
+
+ if (Isspace(ch))
+ {
+ *tail++ = '\0';
+ break;
+ }
+ tail++;
+ }
+ *str = tail;
+
+ return head;
+}
+
+
+/* ¨Ñ·sª© server ¨Ï¥Î */
+
+
+#define MSG_CLOAKED "«¢«¢¡I§ÚÁô§Î°_¨Ó¤F"
+#define MSG_UNCLOAK "§Ú­n­«²{¦¿´ò¤F...."
+
+#endif /* _XCHAT_H_ */
diff --git a/innbbsd/Makefile b/innbbsd/Makefile
new file mode 100644
index 0000000..63ca63d
--- /dev/null
+++ b/innbbsd/Makefile
@@ -0,0 +1,71 @@
+# ------------------------------------------------------ #
+# innbbsd/Makefile ( NTHU CS MapleBBS Ver 3.10 ) #
+# ------------------------------------------------------ #
+# target : Makefile for Âà«Hµ{¦¡ innbbsd #
+# create : 95/03/29 #
+# update : 01/08/26 #
+# author : skhuang #
+# ------------------------------------------------------ #
+
+
+# ------------------------------------------------------ #
+# ¤U¦Cªº make rules ¤£»Ý­×§ï #
+# ------------------------------------------------------ #
+
+INNOBJS = channel.o bbslib.o convcode.o inntobbs.o rec_article.o nocem.o history.o
+
+LNKOBJS = bbslink.o bbslib.o convcode.o inntobbs.o rec_article.o nocem.o history.o
+
+INNBBSD = innbbsd bbslink
+
+EXE = $(INNBBSD)
+
+
+all:
+ @echo "Please enter 'make sys-type', "
+ @echo " make sun : for Sun-OS 4.x and maybe some BSD systems, cc or gcc"
+ @echo " make linux : for Linux"
+ @echo " make solaris : for Sun-OS 5.x gcc"
+ @echo " make sol-x86 : for Solaris 7 x86"
+ @echo " make freebsd : for BSD 4.4 systems"
+ @echo " make bsd : for BSD systems, cc or gcc, if not in the above lists"
+ @echo " make cygwin : for Microsoft Windows and Cygwin gcc"
+
+sun:
+ @$(MAKE) CC=gcc CFLAGS="-O2 -pipe -fomit-frame-pointer -Wunused -I../include" LDFLAGS="-s -L../lib -ldao" $(EXE)
+
+linux:
+ @$(MAKE) CC=gcc CFLAGS="-O2 -pipe -I../include -fomit-frame-pointer -Wunused" LDFLAGS="-s -L../lib -ldao" $(EXE)
+
+solaris:
+ @$(MAKE) CC=gcc CFLAGS="-DSOLARIS -O2 -pipe -I../include -fomit-frame-pointer -Wunused" LDFLAGS="-s -L../lib -ldao -lsocket -lnsl -L/usr/ucblib -lucb" $(EXE)
+
+sol-x86:
+ @$(MAKE) CC=gcc CFLAGS="-DSOLARIS -O2 -I../include -fomit-frame-pointer -Wunused" LDFLAGS="-s -L../lib -ldao -lsocket -lnsl" $(EXE)
+
+freebsd:
+ @$(MAKE) CC=gcc CFLAGS="-O2 -pipe -I../include -fomit-frame-pointer -Wunused" LDFLAGS="-s -L../lib -ldao" $(EXE)
+
+bsd:
+ @$(MAKE) CC=gcc CFLAGS="-O2 -pipe -I../include -fomit-frame-pointer -Wunused" LDFLAGS="-s -L../lib -ldao" $(EXE)
+
+cygwin:
+ @$(MAKE) CC=gcc CFLAGS="-DCYGWIN -O2 -pipe -I../include -fomit-frame-pointer -Wunused" LDFLAGS="-s -L../lib -ldao -lcygipc" $(EXE)
+
+
+
+bbslink: $(LNKOBJS)
+ $(CC) -o bbslink $(LNKOBJS) $(OPT) $(LDFLAGS)
+
+innbbsd: $(INNOBJS)
+ $(CC) -o innbbsd $(INNOBJS) $(OPT) $(LDFLAGS)
+
+
+install: $(INNBBSD)
+ install -m 0700 $? $(HOME)/innd
+
+update:
+ -csh -c "kill `tail -1 $(HOME)/run/innbbsd.pid | awk '{print $$1}'`";exit 0
+
+clean:
+ rm -f $(EXE) *.exe *.o *~
diff --git a/innbbsd/bbslib.c b/innbbsd/bbslib.c
new file mode 100644
index 0000000..16260f2
--- /dev/null
+++ b/innbbsd/bbslib.c
@@ -0,0 +1,283 @@
+/*-------------------------------------------------------*/
+/* bbslib.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : innbbsd library */
+/* create : 95/04/27 */
+/* update : / / */
+/* author : skhuang@csie.nctu.edu.tw */
+/* modify : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "innbbsconf.h"
+#include "bbslib.h"
+
+#ifdef _NoCeM_
+#include "nocem.h"
+#endif
+
+#include <stdarg.h>
+
+
+/* ----------------------------------------------------- */
+/* read nodelist.bbs */
+/* ----------------------------------------------------- */
+
+
+int NLCOUNT;
+nodelist_t *NODELIST = NULL;
+
+
+int
+nl_bynamecmp(a, b)
+ nodelist_t *a, *b;
+{
+ return str_cmp(a->name, b->name);
+}
+
+
+static int /* 0:success -1:fail */
+read_nodelist()
+{
+ int fd, size;
+ struct stat st;
+
+ if ((fd = open("innd/nodelist.bbs", O_RDONLY)) < 0)
+ return -1;
+
+ fstat(fd, &st);
+ if ((size = st.st_size) <= 0)
+ {
+ close(fd);
+ return -1;
+ }
+ NODELIST = !NODELIST ? (nodelist_t *) malloc(size) : (nodelist_t *) realloc(NODELIST, size);
+ read(fd, NODELIST, size);
+ close(fd);
+
+ NLCOUNT = size / sizeof(nodelist_t);
+ if (NLCOUNT > 1)
+ {
+ /* ±N NODELIST[] ¨Ì name ±Æ§Ç¡A¨º»ò¦b search_nodelist_byname() ¥i¥H§ä§Ö¤@ÂI */
+ qsort(NODELIST, NLCOUNT, sizeof(nodelist_t), nl_bynamecmp);
+ }
+
+ return 0;
+}
+
+
+/* ----------------------------------------------------- */
+/* read newsfeeds.bbs */
+/* ----------------------------------------------------- */
+
+
+int NFCOUNT;
+newsfeeds_t *NEWSFEEDS = NULL;
+newsfeeds_t *NEWSFEEDS_B = NULL;
+newsfeeds_t *NEWSFEEDS_G = NULL;
+
+
+int
+nf_byboardcmp(a, b)
+ newsfeeds_t *a, *b;
+{
+ return str_cmp(a->board, b->board);
+}
+
+
+int
+nf_bygroupcmp(a, b)
+ newsfeeds_t *a, *b;
+{
+ return str_cmp(a->newsgroup, b->newsgroup);
+}
+
+
+static int /* 0:success -1:fail */
+read_newsfeeds()
+{
+ int fd, size;
+ struct stat st;
+
+ if ((fd = open("innd/newsfeeds.bbs", O_RDONLY)) < 0)
+ return -1;
+
+ fstat(fd, &st);
+ if ((size = st.st_size) <= 0)
+ {
+ close(fd);
+ return -1;
+ }
+ NEWSFEEDS = !NEWSFEEDS ? (newsfeeds_t *) malloc(size) : (newsfeeds_t *) realloc(NEWSFEEDS, size);
+ read(fd, NEWSFEEDS, size);
+ close(fd);
+
+ /* ¥t¥~·Ç³Æ¤G¥÷¬Û¦Pªº¸ê°T¡A¦ý¬O±Æ§Ç¤èªk¤£¦P */
+ NEWSFEEDS_B = !NEWSFEEDS_B ? (newsfeeds_t *) malloc(size) : (newsfeeds_t *) realloc(NEWSFEEDS_B, size);
+ memcpy(NEWSFEEDS_B, NEWSFEEDS, size);
+ NEWSFEEDS_G = !NEWSFEEDS_G ? (newsfeeds_t *) malloc(size) : (newsfeeds_t *) realloc(NEWSFEEDS_G, size);
+ memcpy(NEWSFEEDS_G, NEWSFEEDS, size);
+
+ NFCOUNT = size / sizeof(newsfeeds_t);
+ if (NFCOUNT > 1)
+ {
+ /* NEWSFEEDS[] ¤£ÅܰʡA¹w³]¨Ì¯¸¥x¦WºÙ±Æ§Ç */
+
+ /* ±N NEWSFEEDS_B[] ¨Ì board ±Æ§Ç¡A¨º»ò¦b search_newsfeeds_byboard() ¥i¥H§ä§Ö¤@ÂI */
+ qsort(NEWSFEEDS_B, NFCOUNT, sizeof(newsfeeds_t), nf_byboardcmp);
+
+ /* ±N NEWSFEEDS_G[] ¨Ì group ±Æ§Ç¡A¨º»ò¦b search_newsfeeds_bygroup() ¥i¥H§ä§Ö¤@ÂI */
+ qsort(NEWSFEEDS_G, NFCOUNT, sizeof(newsfeeds_t), nf_bygroupcmp);
+ }
+
+ return 0;
+}
+
+
+#ifdef _NoCeM_
+/* ----------------------------------------------------- */
+/* read ncmperm.bbs */
+/* ----------------------------------------------------- */
+
+
+ncmperm_t *NCMPERM = NULL;
+int NCMCOUNT = 0;
+
+
+int /* 0:success -1:fail */
+read_ncmperm()
+{
+ int fd, size;
+ struct stat st;
+
+ if ((fd = open("innd/ncmperm.bbs", O_RDONLY)) < 0)
+ return -1;
+
+ fstat(fd, &st);
+ if ((size = st.st_size) <= 0)
+ {
+ close(fd);
+ return -1;
+ }
+ NCMPERM = !NCMPERM ? (ncmperm_t *) malloc(size) : (ncmperm_t *) realloc(NCMPERM, size);
+ read(fd, NCMPERM, size);
+ close(fd);
+
+ NCMCOUNT = size / sizeof(ncmperm_t);
+
+ return 0;
+}
+#endif /* _NoCeM_ */
+
+
+/* ----------------------------------------------------- */
+/* read spamrule.bbs */
+/* ----------------------------------------------------- */
+
+
+spamrule_t *SPAMRULE = NULL;
+int SPAMCOUNT = 0;
+
+
+static int /* 0:success -1:fail */
+read_spamrule()
+{
+ int fd, size;
+ struct stat st;
+ spamrule_t *spam;
+ char *detail;
+
+ if ((fd = open("innd/spamrule.bbs", O_RDONLY)) < 0)
+ return -1;
+
+ fstat(fd, &st);
+ if ((size = st.st_size) <= 0)
+ {
+ close(fd);
+ return -1;
+ }
+ SPAMRULE = !SPAMRULE ? (spamrule_t *) malloc(size) : (spamrule_t *) realloc(SPAMRULE, size);
+ read(fd, SPAMRULE, size);
+ close(fd);
+
+ SPAMCOUNT = size / sizeof(spamrule_t);
+ for (fd = 0; fd < SPAMCOUNT; fd++)
+ {
+ /* ±N SPAMRULE[] ³£Åܦ¨¤p¼g¡A³o¼Ë¤ñ¹ï®É´N¥i¥H¤j¤p¼g³q¦Y */
+ spam = SPAMRULE + fd;
+ detail = spam->detail;
+ str_lowest(detail, detail);
+ }
+
+ return 0;
+}
+
+
+/* ----------------------------------------------------- */
+/* initail INNBBSD */
+/* ----------------------------------------------------- */
+
+
+int /* 1:success 0:failure */
+initial_bbs()
+{
+ chdir(BBSHOME); /* chdir to bbs_home first */
+
+ /* ¨Ì§Ç¸ü¤J nodelist.bbs¡Bnewsfeeds.bbs¡Bncmperm.bbs¡Bspamrule.bbs */
+
+ if (read_nodelist() < 0)
+ {
+ printf("½ÐÀˬd nodelist.bbs¡AµLªkŪÀÉ\n");
+ return 0;
+ }
+
+ if (read_newsfeeds() < 0)
+ {
+ printf("½ÐÀˬd newsfeeds.bbs¡AµLªkŪÀÉ\n");
+ return 0;
+ }
+
+#ifdef _NoCeM_
+ if (read_ncmperm() < 0)
+ {
+ printf("½ÐÀˬd ncmperm.bbs¡AµLªkŪÀÉ¡F¦pªG±z¤£·Q³]©w NoCeM¡A¨º»ò½Ð©¿²¤¦¹°T®§\n");
+ /* return 0; */ /* ncmperm.bbs ¥i¥H¬OªÅªº */
+ }
+#endif
+
+ if (read_spamrule() < 0)
+ {
+ printf("½ÐÀˬd spamrule.bbs¡AµLªkŪÀÉ¡F¦pªG±z¤£·Q³]©w¾×«H³W«h¡A¨º»ò½Ð©¿²¤¦¹°T®§\n");
+ /* return 0; */ /* spamrule.bbs ¥i¥H¬OªÅªº */
+ }
+
+ return 1;
+}
+
+
+/* ----------------------------------------------------- */
+/* log function */
+/* ----------------------------------------------------- */
+
+
+void
+bbslog(char *fmt, ...)
+{
+ va_list args;
+ char datebuf[40];
+ time_t now;
+ FILE *fp;
+
+ if (fp = fopen(LOGFILE, "a"))
+ {
+ time(&now);
+ strftime(datebuf, sizeof(datebuf), "%b %d %X ", localtime(&now));
+ fprintf(fp, "%s ", datebuf);
+
+ va_start(args, fmt);
+ vfprintf(fp, fmt, args);
+ va_end(args);
+
+ fclose(fp);
+ }
+}
diff --git a/innbbsd/bbslib.h b/innbbsd/bbslib.h
new file mode 100644
index 0000000..d92aef8
--- /dev/null
+++ b/innbbsd/bbslib.h
@@ -0,0 +1,33 @@
+#ifndef _BBSLIB_H_
+#define _BBSLIB_H_
+
+/* bbslib.c */
+extern int NLCOUNT;
+extern nodelist_t *NODELIST;
+extern int nl_bynamecmp();
+
+/* bbslib.c */
+extern int NFCOUNT;
+extern newsfeeds_t *NEWSFEEDS;
+extern newsfeeds_t *NEWSFEEDS_B;
+extern newsfeeds_t *NEWSFEEDS_G;
+extern int nf_byboardcmp();
+extern int nf_bygroupcmp();
+
+/* bbslib.c */
+extern int SPAMCOUNT;
+extern spamrule_t *SPAMRULE;
+
+/* bbslib.c */
+extern int initial_bbs();
+extern void bbslog(char *fmt, ...);
+
+/* rec_article.c */
+extern void init_bshm();
+extern int cancel_article();
+extern int receive_article();
+#ifdef _NoCeM_
+extern int receive_nocem();
+#endif
+
+#endif /* _BBSLIB_H_ */
diff --git a/innbbsd/bbslink.c b/innbbsd/bbslink.c
new file mode 100644
index 0000000..b9b181b
--- /dev/null
+++ b/innbbsd/bbslink.c
@@ -0,0 +1,1053 @@
+/*-------------------------------------------------------*/
+/* bbslink.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : innbbsd NNTP and NNRP */
+/* create : 95/04/27 */
+/* update : 04/10/23 */
+/* author : skhuang@csie.nctu.edu.tw */
+/* modify : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "innbbsconf.h"
+#include "bbslib.h"
+#include "inntobbs.h"
+#include "nntp.h"
+#include <stdarg.h>
+
+
+#if 0 /* itoc.030122.µù¸Ñ: µ{¦¡¬yµ{ */
+
+ 0. bbsd ·|§â·s¤å³¹ªºÀÉÀY°O¿ý¦b out.bntp
+
+ 1. °õ¦æ¥»µ{¦¡¥H«á¡A¦b main() ³B²z¤@¤U°Ñ¼Æ
+
+ 2. ¦b main():initial_bbs() Ū¥X³]©wÀÉ¡AµM«á¶i¤J bbslink()
+
+ 3. ¦b bbslink():deal_bntp() ¤¤­º¥ý³B²z out.bntp
+ ¥Ñ©ó out.bntp ¬O§â©Ò¦³ªOªº·s¤å³¹³£©ñ¦b¤@°_¡A©Ò¥H¦b³o¸Ì§â³o out.bntp Àɨ̯¸¥x¤À¥h *.link
+
+ 4. ¦b bbslink():visit_site() ¤¤¨Ì¥H¤U¨BÆJ¡A¤@¤@«ô³X¦U¯¸
+
+ 4.1. open_connect() ¶}±Ò³s½u
+ 4.2. send_outgoing() §â¥»¯¸¥x¹ïÀ³ªº link Àɤ@µ§¤@µ§Åª¥X¨Ó¡A§â«H°e¥h¹ï¤è¯¸
+ 4.3. readnews() ¨Ì§ÇŪ¨ú¨C­Ó·Q­nªº newsgroup¡A¨Ã¨ú¹ï¤è¯¸ªº«H
+ 4.4. close_connect() Ãö³¬³s½u
+
+ [µù] §Y¨Ï¨S¦³±Ò°Ê innbbsd¡A¤]¥i¥H¨Ï¥Î bbslink
+
+#endif
+
+
+static int SERVERfd = -1;
+static FILE *SERVERrfp = NULL;
+static FILE *SERVERwfp = NULL;
+static char SERVERbuffer[1024];
+
+
+/* itoc.030122.µù¸Ñ: ¥H¤U³o´X­Ó¦b«ü©w°Ñ¼Æ®É¤~¦³¥Î */
+static int Verbose = 0; /* 1: Åã¥Ü¸Ô²Ó°T®§ */
+static int KillFormerProc = 0; /* 1: §R°£¤W¦¸°õ¦æ¥¢±Ñªº bbslink */
+static int ResetActive = 0; /* 1: ±N high-number §ó·s¨ì»P news server ¤W¬Û¦P */
+static int MaxArts = MAX_ARTS; /* ¹ï news server ¨C­Ó¸s²Õ³Ì¦h¥u§ì´X«Ê¤å³¹ */
+static char *DefaultProcSite = NULL; /* !=NULL: ¥u³B²z¬Y¯S©w¯¸¥x */
+
+
+#define DEBUG(arg) if (Verbose) printf arg
+
+
+/*-------------------------------------------------------*/
+/* ³B²z bntp ÀÉ */
+/*-------------------------------------------------------*/
+
+
+static nodelist_t *
+search_nodelist_bynode(name)
+ char *name;
+{
+ nodelist_t nl;
+
+ str_ncpy(nl.name, name, sizeof(nl.name));
+ return bsearch(&nl, NODELIST, NLCOUNT, sizeof(nodelist_t), nl_bynamecmp);
+}
+
+
+static newsfeeds_t *
+search_newsfeeds_byboard(board)
+ char *board;
+{
+ newsfeeds_t nf;
+
+ str_ncpy(nf.board, board, sizeof(nf.board));
+ return bsearch(&nf, NEWSFEEDS_B, NFCOUNT, sizeof(newsfeeds_t), nf_byboardcmp);
+}
+
+
+typedef struct
+{
+ char board[BNLEN + 1];
+ char filename[9];
+ char group[80];
+ char from[80];
+ char title[80];
+ char date[40];
+ char msgid[80];
+ char control[80];
+ char charset[20];
+} soverview_t;
+
+
+static void
+queuefeed(node, sover)
+ nodelist_t *node;
+ soverview_t *sover;
+{
+ int fd;
+
+ /* itoc.030122.µù¸Ñ: *.link ÀɬO¨Ì¯¸¥x¤À¦n «Ý°e(©Î°e¤£¦¨)ªº batch */
+
+ if (node->feedfd < 0)
+ {
+ char linkfile[64];
+
+ sprintf(linkfile, "innd/%s.link", node->name);
+ if ((fd = open(linkfile, O_WRONLY | O_CREAT | O_APPEND, 0600)) < 0)
+ return;
+ node->feedfd = fd;
+ }
+ else
+ {
+ fd = node->feedfd;
+ }
+
+ /* flock(fd, LOCK_EX); */
+ /* Thor.981205: ¥Î fcntl ¨ú¥Nflock, POSIX¼Ð·Ç¥Îªk */
+ f_exlock(fd);
+
+ write(fd, sover, sizeof(soverview_t));
+
+ /* flock(fd, LOCK_UN); */
+ /* Thor.981205: ¥Î fcntl ¨ú¥Nflock, POSIX¼Ð·Ç¥Îªk */
+ f_unlock(fd);
+}
+
+
+static char *
+Gtime(now)
+ time_t now;
+{
+ static char datemsg[40];
+
+ strftime(datemsg, sizeof(datemsg), "%d %b %Y %X GMT", gmtime(&now));
+ return datemsg;
+}
+
+
+static void
+deal_sover(bntp)
+ bntp_t *bntp;
+{
+ newsfeeds_t *nf;
+ nodelist_t *nl;
+ soverview_t sover;
+ time_t mtime;
+ char buf[80];
+ char *board, *filename;
+
+ board = bntp->board;
+
+ if (!(nf = search_newsfeeds_byboard(board)))
+ {
+ bbslog("<bbslink> :Warn: %s ¦¹ªO¤£¦b newsfeeds.bbs ¤¤\n", board);
+ DEBUG(("¢w¡÷:Warn: %s ¦¹ªO¤£¦b newsfeeds.bbs ¤¤\n", board));
+ return;
+ }
+
+ if (!(nl = search_nodelist_bynode(nf->path)))
+ return;
+
+ filename = bntp->xname;
+
+ memset(&sover, 0, sizeof(soverview_t));
+
+ if (bntp->chrono > 0) /* ·s«H */
+ {
+ int fd;
+ char folder[64];
+ HDR hdr;
+
+ mtime = bntp->chrono;
+ brd_fpath(folder, board, FN_DIR);
+ if ((fd = open(folder, O_RDONLY)) >=0)
+ {
+ while (read(fd, &hdr, sizeof(HDR)) == sizeof(HDR))
+ {
+ if (mtime == hdr.chrono)
+ {
+ if (hdr.xmode & POST_RESTRICT)
+ {
+ close(fd);
+ return;
+ }
+ break;
+ }
+ }
+ close(fd);
+ }
+
+ str_ncpy(sover.title, bntp->title, sizeof(sover.title));
+ sprintf(sover.msgid, "%s$%s@" MYHOSTNAME, filename, board);
+ }
+ else /* cancel */
+ {
+ time(&mtime);
+ sprintf(buf, "%s$%s@" MYHOSTNAME, filename, board); /* ±ý¬å¤å³¹ªº Message-ID */
+ sprintf(sover.title, "cmsg cancel <%s>", buf);
+ sprintf(sover.msgid, "C%s$%s@" MYHOSTNAME, filename, board);/* LHD.030628: ¦b­ì msgid ¥[¥ô·N¦r¦ê·í§@ cmsg ªº Message-ID */
+ sprintf(sover.control, "cancel <%s>", buf);
+ }
+
+ str_ncpy(sover.board, board, sizeof(sover.board));
+ str_ncpy(sover.filename, filename, sizeof(sover.filename));
+ sprintf(sover.from, "%s.bbs@" MYHOSTNAME " (%s)", bntp->owner, bntp->nick);
+ str_ncpy(sover.date, Gtime(mtime), sizeof(sover.date));
+ str_ncpy(sover.group, nf->newsgroup, sizeof(sover.group));
+ str_ncpy(sover.charset, nf->charset, sizeof(sover.charset));
+
+ queuefeed(nl, &sover);
+}
+
+
+static void
+deal_bntp()
+{
+ char *OUTING = "innd/.outing"; /* ³B²z®É¼È¦sªºÀÉ */
+ int fd, i;
+ nodelist_t *node;
+ bntp_t bntp;
+
+ if (rename("innd/out.bntp", OUTING)) /* ¨S¦³·s¤å³¹ */
+ return;
+
+ /* initail ¦U node ªº feedfd */
+ for (i = 0; i < NLCOUNT; i++)
+ {
+ node = NODELIST + i;
+ node->feedfd = -1;
+ }
+
+ /* ¶K¨ì¦U­Ó¯¸¥x©ÒÄݪº *.link */
+ if ((fd = open(OUTING, O_RDONLY)) >= 0)
+ {
+ while (read(fd, &bntp, sizeof(bntp_t)) == sizeof(bntp_t))
+ deal_sover(&bntp);
+ close(fd);
+ }
+
+ /* close ¦U node ªº feedfd */
+ for (i = 0; i < NLCOUNT; i++)
+ {
+ node = NODELIST + i;
+ if (node->feedfd >= 0)
+ close(node->feedfd);
+ }
+
+ unlink(OUTING);
+}
+
+
+/*-------------------------------------------------------*/
+/* ³s¥h¬Y­Ó¯¸ */
+/*-------------------------------------------------------*/
+
+
+static int
+inetclient(server, port)
+ char *server;
+ int port;
+{
+ struct hostent *host; /* host information entry */
+ struct sockaddr_in sin; /* Internet endpoint address */
+ int fd;
+
+ if (!*server || !port)
+ return -1;
+
+ memset(&sin, 0, sizeof(sin));
+
+ if (!(host = gethostbyname(server)))
+ sin.sin_addr.s_addr = inet_addr(server);
+ else
+ memcpy(&sin.sin_addr.s_addr, host->h_addr, host->h_length);
+
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(port);
+
+ /* Allocate a socket */
+ fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (fd < 0)
+ return -1;
+
+ /* Connect the socket to the server */
+ if (connect(fd, (struct sockaddr *) & sin, sizeof(sin)) < 0)
+ {
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+
+static int
+tcpcommand(char *fmt, ...)
+{
+ va_list args;
+ char *ptr;
+
+ va_start(args, fmt);
+ vfprintf(SERVERwfp, fmt, args);
+ va_end(args);
+ fprintf(SERVERwfp, "\r\n");
+ fflush(SERVERwfp);
+
+ if (!fgets(SERVERbuffer, sizeof(SERVERbuffer), SERVERrfp))
+ return 0;
+
+ if (ptr = strchr(SERVERbuffer, '\r'))
+ *ptr = '\0';
+ if (ptr = strchr(SERVERbuffer, '\n'))
+ *ptr = '\0';
+
+ return atoi(SERVERbuffer);
+}
+
+
+static int /* 200~202:¦¨¥\ 0:¥¢±Ñ */
+open_connect(node) /* ³s¥h³o­Ó¯¸ */
+ nodelist_t *node;
+{
+ char *host = node->host;
+ int port = node->port;
+
+ DEBUG(("¢~<open_connect> ¥¿¦b¶}±Ò³s½u\n"));
+
+ if ((SERVERfd = inetclient(host, port)) < 0)
+ {
+ bbslog("<bbslink> :Err: ¦øªA¾¹³s½u¥¢±Ñ¡G%s %d\n", host, port);
+ DEBUG(("¢¢<open_connect> ¦øªA¾¹³s½u¥¢±Ñ\n"));
+ return 0;
+ }
+
+ if (!(SERVERrfp = fdopen(SERVERfd, "r")) || !(SERVERwfp = fdopen(SERVERfd, "w")))
+ {
+ bbslog("<bbslink> :Err: fdopen µo¥Í¿ù»~\n");
+ DEBUG(("¢¢<open_connect> fdopen µo¥Í¿ù»~\n"));
+ return 0;
+ }
+
+ if (!fgets(SERVERbuffer, sizeof(SERVERbuffer), SERVERrfp) || SERVERbuffer[0] != '2') /* 200 201 202 ³£¯à¨ú«H */
+ {
+ bbslog("<bbslink> :Err: ¦øªA¾¹©Úµ´³s½u¡G%s %d\n", host, port);
+ DEBUG(("¢¢<open_connect> ¦øªA¾¹©Úµ´³s½u\n"));
+ return 0;
+ }
+
+ /* itoc.040512: MODE READER ¥u­nÁ¿¤@¦¸´N°÷¤F */
+ if (node->xmode & INN_USEPOST)
+ {
+ tcpcommand("MODE READER");
+ if (SERVERbuffer[0] != '2') /* 200 201 202 ³£¯à¨ú«H */
+ {
+ bbslog("<bbslink> :Err: ¦øªA¾¹©Úµ´³s½u¡G%s %d\n", host, port);
+ DEBUG(("¢¢<open_connect> ¦øªA¾¹©Úµ´³s½u\n"));
+ return 0;
+ }
+ }
+
+ DEBUG(("¢x<open_connect> ¦øªA¾¹³s½u¦¨¥\\\n"));
+ return atoi(SERVERbuffer);
+}
+
+
+static void
+close_connect() /* µ²§ô³s¥h³o­Ó¯¸ */
+{
+ int status;
+
+ status = tcpcommand("QUIT");
+ if (status != NNTP_GOODBYE_ACK_VAL && status != 221)
+ {
+ bbslog("<bbslink> :Warn: µLªk¥¿±`Â_½u\n");
+ DEBUG(("¢x<close_connect> µLªk¥¿±`Â_½u\n"));
+ }
+
+ DEBUG(("¢¢<close_connect> ¤wÃö³¬³s½u\n"));
+
+ if (SERVERrfp)
+ fclose(SERVERrfp);
+ if (SERVERwfp)
+ fclose(SERVERwfp);
+ if (SERVERfd >= 0)
+ close(SERVERfd);
+}
+
+
+/*-------------------------------------------------------*/
+/* °e¥X¤å³¹ */
+/*-------------------------------------------------------*/
+
+
+static int /* -1:¥¢±Ñ */
+sover_post(sover)
+ soverview_t *sover;
+{
+ if (sover->control[0]) /* °e¥X cancel message */
+ {
+ static char BODY_BUF[128];
+
+ sprintf(BODY_BUF, "%s\r\n", sover->title);
+ BODY = BODY_BUF; /* cancel message ®É¡ABODY «ü¦V BODY_BUF */
+ }
+ else /* °e¥X·s¤å³¹ */
+ {
+ static char *BODY_BUF;
+ char *ptr, *str, fpath[64];
+ int fd, size;
+ struct stat st;
+
+ /* Àˬd¤å³¹ÁÙ¦b¤£¦b */
+ sprintf(fpath, "brd/%s/%c/%s", sover->board, sover->filename[7], sover->filename);
+ if ((fd = open(fpath, O_RDONLY)) < 0)
+ return -1;
+ fstat(fd, &st);
+ size = st.st_size;
+ if (size <= 0)
+ {
+ close(fd);
+ return -1;
+ }
+
+ /* ¤@¯ë¤å³¹®É¡ABODY «ü¦V malloc ¥Í¥X¨Óªº°Ï¶ô */
+
+ BODY_BUF = !BODY_BUF ? (char *) malloc(size + 1) : (char *) realloc(BODY_BUF, size + 1);
+ read(fd, BODY_BUF, size);
+ close(fd);
+ ptr = BODY_BUF + size;
+ *ptr = '\0';
+
+ /* ¸õ¹L¤å³¹ªº«e´X¦æÀÉÀY¤£­n */
+ for (str = BODY_BUF;;str = ptr + 1)
+ {
+ ptr = strchr(str, '\n');
+ if (!ptr) /* §ä¨ì¤å³¹³Ì«á¤FÁ٧䤣¨ìªÅ¦æ¡A¨º»ò¾ã­ÓÀɮ׳£·í°µ¤º¤å */
+ {
+ BODY = BODY_BUF;
+ break;
+ }
+
+ if (ptr == str) /* §ä¨ì¤@¦æªÅ¦æ¡A¨º»ò¥H¤U´N³£¬O¤º¤å¤F */
+ {
+ BODY = str + 1;
+ break;
+ }
+ }
+ }
+
+ if (sover->charset[0] == 'g')
+ {
+ b52gb(BODY);
+ b52gb(sover->from);
+ b52gb(sover->title);
+ }
+
+ return 0;
+}
+
+
+static void
+fail_post(msgid)
+ char *msgid;
+{
+ bbslog("<bbslink> :Warn: %s <%s>\n", SERVERbuffer, msgid);
+ DEBUG(("¢x¡÷:Warn: %s <%s>\n", SERVERbuffer, msgid));
+}
+
+
+static void
+send_outgoing(node, sover)
+ nodelist_t *node;
+ soverview_t *sover;
+{
+ int cc, status;
+ char *msgid, *str;
+
+ msgid = sover->msgid;
+
+ DEBUG(("¢x¢z MSGID: %s\n", msgid));
+ DEBUG(("¢x¢x GROUP: %s\n", sover->group));
+ DEBUG(("¢x¢x FROM : %s\n", sover->from));
+ DEBUG(("¢x¢| SUBJ : %s\n", sover->title));
+
+ /* ¥ý§â¤å³¹·Ç³Æ¦n */
+ if (sover_post(sover) < 0)
+ {
+ DEBUG(("¢x¡÷ ¥»½g¤å³¹¤w¾D§R°£©ÎÀɮ׿ò¥¢¡A¨ú®ø°e¥X\n"));
+ return;
+ }
+
+ /* ¦V server °e¥X IHAVE/POST ­n¨D */
+ if (node->xmode & INN_USEIHAVE)
+ {
+ status = tcpcommand("IHAVE <%s>", msgid);
+ if (status != NNTP_SENDIT_VAL)
+ {
+ fail_post(msgid);
+ return;
+ }
+ }
+ else /* if (node->xmode & INN_USEPOST) */
+ {
+ status = tcpcommand("POST");
+ if (status != NNTP_START_POST_VAL)
+ {
+ fail_post(msgid);
+ return;
+ }
+ }
+
+ /* ¼g¤J¤å³¹ªºÀÉÀY */
+ fprintf(SERVERwfp, "Path: %s\r\n", MYBBSID);
+ fprintf(SERVERwfp, "From: %s\r\n", sover->from);
+ fprintf(SERVERwfp, "Newsgroups: %s\r\n", sover->group);
+ /* fprintf(SERVERwfp, "Subject: %s\r\n", sover->title); */
+ output_rfc2047_qp(SERVERwfp, "Subject: ", sover->title, sover->charset, "\r\n");
+ fprintf(SERVERwfp, "Date: %s\r\n", sover->date);
+ fprintf(SERVERwfp, "Organization: %s\r\n", *sover->charset == 'b' ? BBSNAME : BBSNAME2); /* itoc.040425: ­Y¤£¬O big5 ´N¥Î­^¤å¯¸¦W */
+ fprintf(SERVERwfp, "Message-ID: <%s>\r\n", msgid);
+ fprintf(SERVERwfp, "Mime-Version: 1.0\r\n");
+ fprintf(SERVERwfp, "Content-Type: text/plain; charset=\"%s\"\r\n", sover->charset);
+ fprintf(SERVERwfp, "Content-Transfer-Encoding: 8bit\r\n");
+ if (sover->control[0])
+ fprintf(SERVERwfp, "Control: %s\r\n", sover->control);
+ fputs("\r\n", SERVERwfp); /* ÀÉÀY©M¤º¤åªÅ¤@¦æ */
+
+ /* ¼g¤J¤å³¹ªº¤º®e */
+ for (str = BODY; cc = *str; str++)
+ {
+ if (cc == '\n')
+ {
+ /* itoc.030127.µù¸Ñ: §â "\n" ´«¦¨ "\r\n" */
+ fputc('\r', SERVERwfp);
+ }
+ else if (cc == '.')
+ {
+ /* If the text contained a period as the first character of the text
+ line in the original, that first period is doubled. */
+ if (str == BODY || str[-1] == '\n')
+ fputc('.', SERVERwfp);
+ }
+
+ fputc(cc, SERVERwfp);
+ }
+
+ /* IHAVE/POST µ²§ô */
+ status = tcpcommand(".");
+ if (node->xmode & INN_USEIHAVE)
+ {
+ if (status != NNTP_TOOKIT_VAL)
+ fail_post(msgid);
+ }
+ else /* if (node->xmode & INN_USEPOST) */
+ {
+ if (status != NNTP_POSTEDOK_VAL)
+ fail_post(msgid);
+ }
+}
+
+
+/*-------------------------------------------------------*/
+/* ¹ï news server ¤U«ü¥O */
+/*-------------------------------------------------------*/
+
+
+static int
+NNRPgroup(newsgroup) /* ¤Á´« group¡A¨Ã¶Ç¦^ high number */
+ char *newsgroup;
+{
+ int high;
+ char *ptr;
+
+ if (tcpcommand("GROUP %s", newsgroup) != NNTP_GROUPOK_VAL)
+ return -1;
+
+ ptr = SERVERbuffer;
+ for (high = 0; high < 3; high++) /* §ä²Ä¤T­Ó ' ' */
+ {
+ ptr++;
+ if (!*ptr || !(ptr = strchr(ptr, ' ')))
+ return -1;
+ }
+
+ if ((high = atoi(ptr + 1)) >= 0)
+ return high;
+ return -1;
+}
+
+
+static char *tempfile = "innd/bbslinktmp";
+
+static int /* 1:¦¨¥\ 0:¥¢±Ñ */
+NNRParticle(artno) /* ¨ú¦^²Ä artno ½gªº¥þ¤å */
+ int artno;
+{
+ FILE *fp;
+ char *ptr;
+
+ if (tcpcommand("ARTICLE %d", artno) != NNTP_ARTICLE_FOLLOWS_VAL)
+ return 0;
+
+ if (!(fp = fopen(tempfile, "w")))
+ return 0;
+
+ while (fgets(SERVERbuffer, sizeof(SERVERbuffer), SERVERrfp))
+ {
+ if (ptr = strchr(SERVERbuffer, '\r'))
+ *ptr = '\0';
+ if (ptr = strchr(SERVERbuffer, '\n'))
+ *ptr = '\0';
+
+ if (!strcmp(SERVERbuffer, ".")) /* ¤å³¹µ²§ô */
+ break;
+
+ fprintf(fp, "%s\n", SERVERbuffer);
+ }
+
+ fclose(fp);
+ return 1;
+}
+
+
+
+#if 0 /* itoc.030109.µù¸Ñ: my_post ªº¬yµ{ */
+ ¢z¡÷ receive_article() ¡÷ bbspost_add()
+ my_post() ¢u¡÷ receive_nocem() ¡÷ °e¥h nocem.c ³B²z
+ ¢|¡÷ cancel_article() ¡÷ bbspost_cancel()
+#endif
+
+
+static void
+my_post()
+{
+ int rel, size;
+ char *ptr, *data;
+ struct stat st;
+
+ if ((rel = open(tempfile, O_RDONLY)) >= 0)
+ {
+ fstat(rel, &st);
+ size = st.st_size;
+ data = (char *) malloc(size + 1); /* «O¯d 1 byte µ¹ '\0' */
+ size = read(rel, data, size);
+ close(rel);
+
+ if (size >= 2)
+ {
+ if (data[size - 2] == '\n') /* §â³Ì«á­«ÂЪº '\n' ´«¦¨ '\0' */
+ size--;
+ }
+ data[size] = '\0'; /* ¸É¤W '\0' */
+
+ rel = readlines(data - 1);
+
+ if (rel > 0)
+ {
+ if (ptr = CONTROL)
+ {
+ if (!str_ncmp(ptr, "cancel ", 7))
+ rel = cancel_article(ptr + 7);
+ }
+ else
+ {
+#ifdef _NoCeM_
+ if (strstr(SUBJECT, "@@") && strstr(BODY, "NCM") && strstr(BODY, "PGP"))
+ rel = receive_nocem();
+ else
+#endif
+ rel = receive_article();
+ }
+
+ if (rel < 0)
+ {
+ DEBUG(("¢x¡÷<my_post> ±µ¦¬¤å³¹¥¢±Ñ\n"));
+ }
+ }
+ else if (rel == 0) /* PATH¥]¬A¦Û¤v */
+ {
+ DEBUG(("¢x¡÷<my_post> PATH ¥]¬A¦Û¤v\n"));
+ }
+ else /* if (rel < 0) */ /* ÀÉÀYÄæ¦ì¤£§¹¾ã */
+ {
+ DEBUG(("¢x¡÷<my_post> ÀÉÀYÄæ¦ì¤£§¹¾ã\n"));
+ }
+
+ free(data);
+ }
+
+ unlink(tempfile);
+}
+
+
+/*-------------------------------------------------------*/
+/* §ó·s high number */
+/*-------------------------------------------------------*/
+
+
+static int
+nf_samegroup(nf)
+ newsfeeds_t *nf;
+{
+ return !strcmp(nf->newsgroup, GROUP) && !strcmp(nf->path, NODENAME);
+}
+
+
+static void
+changehigh(hdd, ram)
+ newsfeeds_t *hdd, *ram;
+{
+ if (ram->high >= 0)
+ {
+ hdd->high = ram->high;
+ hdd->xmode &= ~INN_ERROR;
+ }
+ else
+ {
+ hdd->xmode |= INN_ERROR;
+ }
+}
+
+
+static void
+updaterc(nf, pos, high)
+ newsfeeds_t *nf;
+ int pos; /* ©ó newsfeeds.bbs ¸Ì­±ªº¦ì¸m */
+ int high; /* >=0:¥Ø«e§ì¨ì­þ¤@½g <0:error */
+{
+ nf->high = high;
+ GROUP = nf->newsgroup;
+ rec_ref("innd/newsfeeds.bbs", nf, sizeof(newsfeeds_t), pos, nf_samegroup, changehigh);
+}
+
+
+/*-------------------------------------------------------*/
+/* §ì¨ú¤å³¹ */
+/*-------------------------------------------------------*/
+
+
+static void
+readnews(node)
+ nodelist_t *node;
+{
+ int i, high, artcount, artno;
+ char *name, *newsgroup;
+ newsfeeds_t *nf;
+
+ name = node->name;
+
+ for (i = 0; i < NFCOUNT; i++) /* ¨Ì§ÇŪ¨ú¨C­Ó newsgroup */
+ {
+ nf = NEWSFEEDS + i;
+
+ if (strcmp(name, nf->path)) /* ¦pªG¤£¬O³o­Ó¯¸¥x´N¸õ¹L */
+ continue;
+
+ newsgroup = nf->newsgroup;
+
+ DEBUG(("¢x¢z<readnews> ¶i¤J %s\n", newsgroup));
+
+ /* ¨ú±o news server ¤Wªº high */
+ if ((high = NNRPgroup(newsgroup)) < 0)
+ {
+ updaterc(nf, i, -1);
+ DEBUG(("¢x¢|<readnews> µLªk¨ú±o¦¹¸s²Õªº high-number ©Î¦¹¸s²Õ¤£¦s¦b\n"));
+ continue;
+ }
+
+ if (ResetActive)
+ {
+ if (nf->high != high)
+ updaterc(nf, i, high);
+ DEBUG(("¢x¢|<readnews> µ²§ô %s¡A¦¹¸s²Õ¤§ high-number ¤w§ó·s\n", newsgroup));
+ continue; /* ­Y ResetActive «h¤£¨ú«H¡A½ü¤U¤@­Ó¸s²Õ */
+ }
+
+ if (nf->high >= high)
+ {
+ if (nf->high > high) /* server re-number */
+ updaterc(nf, i, high);
+
+ DEBUG(("¢x¢|<readnews> µ²§ô %s¡A¦¹¸s²Õ¤w¨S¦³·s¤å³¹\n", newsgroup));
+ continue; /* ³o¸s²Õ¤w¨S¦³·s¤å³¹¡A½ü¤U¤@­Ó¸s²Õ */
+ }
+
+ /* ¨ú¦^¸s²Õ¤W²Ä nf->high + 1 ¶}©lªº MaxArts ½gªº¤å³¹ */
+
+ artcount = 0;
+ for (artno = nf->high + 1;; artno++)
+ {
+ if (NNRParticle(artno))
+ {
+ DEBUG(("¢x¢x<readnews> [%d] ¥¿¨ú¦^¸s²Õ¤W²Ä %d ½g¤å³¹\n", artcount, artno));
+ my_post();
+ if (++artcount >= MaxArts)
+ break;
+ }
+ if (artno >= high)
+ break;
+ }
+
+ updaterc(nf, i, artno);
+
+ DEBUG(("¢x¢|<readnews> µ²§ô %s¡A¤@¦@¨ú¦^ %d ½g·s¤å³¹\n", newsgroup, artcount));
+ } /* end for() */
+}
+
+
+/*-------------------------------------------------------*/
+/* lock/unlock µ{¦¡¡A¦P®É¥u¯à¦³¤@­Ó bbslink ¦b¶] */
+/*-------------------------------------------------------*/
+
+
+static char *lockfile = "innd/bbslinking";
+
+static void
+bbslink_un_lock()
+{
+ unlink(lockfile);
+}
+
+
+static int
+bbslink_get_lock()
+{
+ int fd;
+ char buf[10];
+
+ if ((fd = open(lockfile, O_RDONLY)) >= 0)
+ {
+ int pid;
+ struct stat st;
+
+ /* lockfile ¤w¦s¦b¡A¥Nªí¦³ bbslink ¥¿¦b¶] */
+
+ if (read(fd, buf, sizeof(buf)) > 0 && (pid = atoi(buf)) > 0 && kill(pid, 0) == 0)
+ {
+ /* ¦pªG¥d¤Ó¤[¡A´N¦Û°Ê kill ±¼ */
+ if (KillFormerProc || (!fstat(fd, &st) && st.st_mtime > time(NULL) + BBSLINK_EXPIRE))
+ {
+ kill(pid, SIGTERM);
+ }
+ else
+ {
+ DEBUG(("¦³¥t¥~¤@­Ó bbslink ªº process [%d] ¥¿¦b¹B§@¤¤\n", pid));
+ return 0;
+ }
+ }
+
+ close(fd);
+
+ bbslink_un_lock();
+ }
+
+ sprintf(buf, "%d\n", getpid());
+ f_cat(lockfile, buf);
+
+ return 1;
+}
+
+
+/*-------------------------------------------------------*/
+/* ¥Dµ{¦¡ */
+/*-------------------------------------------------------*/
+
+
+static void
+visit_site(node)
+ nodelist_t *node;
+{
+ int status, response, fd, num;
+ char linkfile[64];
+ soverview_t sover;
+
+ NODENAME = node->name;
+
+ /* ­Y¦³«ü©w¥u³B²z¬Y¯S©w¯¸¡A¨º»ò´N¥u³B²z¸Ó¯¸¥x */
+ if (DefaultProcSite && strcmp(NODENAME, DefaultProcSite))
+ {
+ DEBUG(("¡÷ ³o¨Ã«D©Ò«ü©w­n³B²zªº¯¸¥x¡Aª½±µ¸õ¹L\n"));
+ return;
+ }
+
+ status = 0;
+ sprintf(linkfile, "innd/%s.link", NODENAME);
+ if (dashf(linkfile))
+ status ^= 0x01;
+ if (!(node->xmode & INN_FEEDED))
+ status ^= 0x02;
+
+ if (!status) /* ¤£»Ý­n¥h«ô³X¹ï¤è */
+ {
+ DEBUG(("¡÷ ¦¹¯¸¥x¨S¦³·s«H«Ý°e¥B³QÁý«H¡A¤£»Ý­n¥h«ô³X\n"));
+ return;
+ }
+
+ if (!(response = open_connect(node))) /* ³s½u¥¢±Ñ */
+ return;
+
+ if (status & 0x01) /* ¦³·s«H«Ý°e */
+ {
+ if (response == NNTP_POSTOK_VAL)
+ {
+ /* §â linkfile ¸Ì­±©Ò°O¿ý­n°eªº«H¤@¤@°e¥X */
+ num = 0;
+ if ((fd = open(linkfile, O_RDONLY)) >= 0)
+ {
+ while (read(fd, &sover, sizeof(soverview_t)) == sizeof(soverview_t))
+ {
+ send_outgoing(node, &sover);
+ num++;
+ }
+ close(fd);
+ unlink(linkfile);
+ }
+ DEBUG(("¢x¡÷ Á`¦@°e¥X %d ½g¤å³¹\n", num));
+ }
+ else
+ {
+ DEBUG(("¢x¡÷ ¨S¦³¦b¦¹¯¸¥xµoªí¤å³¹ªºÅv­­\n"));
+ }
+ }
+ else
+ {
+ DEBUG(("¢x¡÷ ¨S¦³·s«H«Ý°e\n"));
+ }
+
+ if (status & 0x02) /* »Ý­n³s¥h¨ú«H */
+ {
+ readnews(node);
+ }
+ else
+ {
+ DEBUG(("¢x¡÷ ¦¹¯¸¥x³]©w³QÁý«H¡A¤£»Ý­n¥h¨ú«H\n"));
+ }
+
+ close_connect();
+}
+
+
+static void
+bbslink()
+{
+ int i;
+ nodelist_t *node;
+
+ /* °T®§Åã¥Ü */
+ DEBUG(("¢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\n"));
+ DEBUG(("¡° nodelist.bbs ¸Ì­±¤@¦@¦³ %d ­Ó¯¸¥x¡A±µ¤U¨Ó±N¤@¤@¥h«ô³X\n", NLCOUNT));
+ DEBUG(("¡° °Ñ¼Æ³]©w¡G\n"));
+ DEBUG((" (1) §R°£¤W¦¸°õ¦æ¥¢±Ñªº bbslink¡G%s\n", KillFormerProc ? "¬O" : "§_"));
+ DEBUG((" (2) ±N high-number §ó·s¨ì»P news server ¤W¬Û¦P¡G%s\n", ResetActive ? "¬O" : "§_"));
+ DEBUG((" (3) ¹ï news server ¨C­Ó¸s²Õ³Ì¦h¥u§ì %d «Ê¤å³¹\n", MaxArts));
+ DEBUG((" (4) ¥u³B²z¬Y¯S©w¯¸¥x©Î¬O³B²z©Ò¦³¯¸¥x¡G%s\n", DefaultProcSite ? DefaultProcSite : "³B²z©Ò¦³¯¸¥x"));
+
+ DEBUG(("¢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\n"));
+ DEBUG(("¡· ¶}©l³B²z out.bntp¡A¾ã²z­n°e¥X¥hªº¤å³¹\n"));
+ deal_bntp();
+ DEBUG(("¡· out.bntp ¾ã²z§¹¦¨\n"));
+ DEBUG(("¢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\n"));
+
+ /* §â nodelist.bbs ¤¤ªº©Ò¦³¯¸¥x³£¥h«ô³X¤@¹M */
+ for (i = 0; i < NLCOUNT; i++)
+ {
+ node = NODELIST + i;
+ DEBUG(("¡· [%d] ¶}©l«ô³X <%s> %s (%d)\n", i + 1, node->name, node->host, node->port));
+ visit_site(node);
+ DEBUG(("¢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\n"));
+ }
+}
+
+
+static void
+usage(argv)
+ char *argv;
+{
+ printf("Usage: %s [options]\n", argv);
+ printf(" -c ±N high-number »P¦øªA¾¹¤W¦P¨B(¤£¨ú«H)\n");
+ printf(" -k ¬å±¼¥Ø«e¥¿¦b¶]ªº bbslink¡A¨Ã­«·s±Ò°Ê bbslink\n");
+ printf(" -v Åã¥Ü¸Ô²Óªº³s½u¹Lµ{\n");
+ printf(" -a ###### «ü©w¨C­Ó¸s²Õ³Ì¦h¨ú´X«Ê«H(¹w³] %d «Ê)\n", MAX_ARTS);
+ printf(" -s site ¥u¨ú³o­Ó¯¸¥xªº¤å³¹\n");
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int c, errflag = 0;
+
+ chdir(BBSHOME);
+ umask(077);
+
+ while ((c = getopt(argc, argv, "a:s:ckv")) != -1)
+ {
+ switch (c)
+ {
+ case 'a':
+ if ((c = atoi(optarg)) > 0)
+ MaxArts = c;
+ break;
+
+ case 's':
+ DefaultProcSite = optarg;
+ break;
+
+ case 'c':
+ ResetActive = 1;
+ break;
+
+ case 'k':
+ KillFormerProc = 1;
+ break;
+
+ case 'v':
+ Verbose = 1;
+ break;
+
+ default:
+ errflag++;
+ break;
+ }
+ }
+
+ if (errflag > 0)
+ {
+ usage(argv[0]);
+ return -1;
+ }
+
+ /* ¶}©l bbslink¡A±N bbslink Âê¦í */
+ if (!bbslink_get_lock())
+ return -1;
+
+ init_bshm();
+
+ if (initial_bbs())
+ bbslink();
+
+ /* µ²§ô bbslink¡A±N bbslink ¸Ñ¶} */
+ bbslink_un_lock();
+
+ return 0;
+}
diff --git a/innbbsd/channel.c b/innbbsd/channel.c
new file mode 100644
index 0000000..191a5ea
--- /dev/null
+++ b/innbbsd/channel.c
@@ -0,0 +1,884 @@
+/*-------------------------------------------------------*/
+/* channel.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : innbbsd main program */
+/* create : 95/04/27 */
+/* update : / / */
+/* author : skhuang@csie.nctu.edu.tw */
+/* modify : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "innbbsconf.h"
+#include "bbslib.h"
+#include "inntobbs.h"
+#include "nntp.h"
+
+#ifdef _NoCeM_
+#include "nocem.h"
+#endif
+
+
+#define INNBBSD_PIDFILE "run/innbbsd.pid"
+
+
+/* ----------------------------------------------------- */
+/* my recv */
+/* ----------------------------------------------------- */
+
+
+typedef struct
+{
+ char *name;
+ char *usage;
+ int minargc; /* argc ³Ì¤Ö´X­Ó */
+ int maxargc; /* argc ³Ì¦h´X­Ó */
+ int mode; /* 0:­ncommand-mode¤~¯à¶] 1:­ndata-mode¤~¯à¶] 2:¨S¦³­­¨î */
+ int errorcode;
+ int normalcode;
+ void (*main) ();
+} daemoncmd_t;
+
+
+typedef struct
+{
+ FILE *in, *out;
+ int argc;
+ char **argv;
+ daemoncmd_t *dc;
+} argv_t;
+
+
+typedef struct
+{
+ char *data;
+ int used;
+ int left;
+} buffer_t;
+
+
+typedef struct
+{
+ char nodename[13];
+ char hostname[128]; /* client hostname */
+ char buffer[4096];
+
+ int mode; /* 1:data mode 0:command mode */
+ argv_t Argv;
+
+ int fd;
+ buffer_t in;
+ buffer_t out;
+} ClientType;
+
+
+#if 0 /* itoc.030109.µù¸Ñ: my_recv ªº¬yµ{ */
+ ¢z¡÷ receive_article() ¡÷ bbspost_add()
+ my_recv() ¢u¡÷ receive_nocem() ¡÷ °e¥h nocem.c ³B²z
+ ¢|¡÷ cancel_article() ¡÷ bbspost_cancel()
+#endif
+
+
+static void
+my_recv(client)
+ ClientType *client;
+{
+ FILE *fout;
+ int rel;
+ char *ptr;
+
+ fout = client->Argv.out;
+
+ rel = readlines(client->in.data + 2);
+
+ if (rel > 0)
+ {
+ rel = 0;
+ if (ptr = CONTROL)
+ {
+ if (!str_ncmp(ptr, "cancel ", 7))
+ {
+ /* itoc.030127: cancel ¥¢±ÑÁÙ¬O­nÄ~Äò¦¬¨ä¥L«Ê«H */
+ /* rel = cancel_article(ptr + 7); */
+ cancel_article(ptr + 7);
+ }
+ }
+ else
+ {
+#ifdef _NoCeM_
+ if (strstr(SUBJECT, "@@") && strstr(BODY, "NCM") && strstr(BODY, "PGP"))
+ rel = receive_nocem();
+ else
+#endif
+ rel = receive_article();
+ }
+
+ if (rel == -1)
+ fprintf(fout, "400 server side failed\r\n");
+ else
+ fprintf(fout, "235\r\n");
+ }
+ else if (rel == 0) /* PATH¥]¬A¦Û¤v */
+ {
+ fprintf(fout, "235\r\n");
+ }
+ else /* if (rel < 0) */ /* ÀÉÀYÄæ¦ì¤£§¹¾ã */
+ {
+ fputs("437\r\n", fout);
+ }
+
+ fflush(fout);
+}
+
+
+/* ----------------------------------------------------- */
+/* command sets */
+/* ----------------------------------------------------- */
+
+
+static daemoncmd_t cmds[];
+
+
+static daemoncmd_t *
+searchcmd(cmd)
+ char *cmd;
+{
+ daemoncmd_t *p;
+ char *name;
+
+ for (p = cmds; name = p->name; p++)
+ {
+ if (!str_cmp(name, cmd))
+ return p;
+ }
+ return NULL;
+}
+
+
+#define MAX_ARG 16
+#define MAX_ARG_SIZE 1024
+
+
+static int
+argify(line, argvp)
+ char *line, ***argvp;
+{
+ static char *argvbuffer[MAX_ARG + 2];
+ char **argv = argvbuffer;
+ int i;
+ static char argifybuffer[MAX_ARG_SIZE];
+ char *p;
+
+ while (strchr("\t\n\r ", *line))
+ line++;
+ p = argifybuffer;
+ strncpy(p, line, sizeof(argifybuffer));
+ for (*argvp = argv, i = 0; *p && i < MAX_ARG;)
+ {
+ for (*argv++ = p; *p && !strchr("\t\r\n ", *p); p++);
+ if (*p == '\0')
+ break;
+ for (*p++ = '\0'; strchr("\t\r\n ", *p) && *p; p++);
+ }
+ *argv = NULL;
+ return argv - *argvp;
+}
+
+
+/* ----------------------------------------------------- */
+/* innd channel */
+/* ----------------------------------------------------- */
+
+
+static fd_set rfd; /* read fd_set */
+
+
+static void
+reapchild(s)
+ int s;
+{
+ int state;
+ while (waitpid(-1, &state, WNOHANG | WUNTRACED) > 0)
+ {
+ /* printf("reaping child\n"); */
+ }
+}
+
+
+static void
+dokill(s)
+ int s;
+{
+ kill(0, SIGKILL);
+}
+
+
+static int /* -1:¥¢±Ñ */
+initinetserver()
+{
+ struct sockaddr_in sin; /* Internet endpoint address */
+ int fd, value;
+ struct linger foobar;
+
+ /* Allocate a socket */
+ fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (fd < 0)
+ {
+ printf("inet socket ¶}±Ò¥¢±Ñ\n");
+ return -1;
+ }
+
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(INNBBS_PORT);
+
+ if (bind(fd, (struct sockaddr *) &sin, sizeof(sin)) < 0)
+ {
+ printf("innbbsd ¤w¥Ñ inetd ±Ò°Ê¤F¡AµL»Ý¦A¤â°Ê°õ¦æ\n");
+ return -1;
+ }
+ listen(fd, 10);
+
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &value, sizeof(value)) < 0)
+ bbslog("setsockopt (SO_REUSEADDR)");
+ foobar.l_onoff = 0;
+ if (setsockopt(fd, SOL_SOCKET, SO_LINGER, (char *) &foobar, sizeof(foobar)) < 0)
+ bbslog("setsockopt (SO_LINGER)");
+
+ signal(SIGHUP, SIG_IGN);
+ signal(SIGUSR1, SIG_IGN);
+ signal(SIGCHLD, reapchild);
+ signal(SIGINT, dokill);
+ signal(SIGPIPE, dokill);
+ signal(SIGTERM, dokill);
+
+ return fd;
+}
+
+
+static int
+tryaccept(s)
+ int s;
+{
+ int ns;
+ int fromlen = sizeof(struct sockaddr_in);
+ struct sockaddr sockaddr; /* Internet endpoint address */
+ extern int errno;
+
+ do
+ {
+ ns = accept(s, &sockaddr, &fromlen);
+ errno = 0;
+ } while (ns < 0 && errno == EINTR);
+ return ns;
+}
+
+
+static void
+channelcreate(client, sock, nodename, hostname)
+ ClientType *client;
+ int sock;
+ char *nodename, *hostname;
+{
+ buffer_t *in, *out;
+
+ str_ncpy(client->nodename, nodename, 13);
+ str_ncpy(client->hostname, hostname, 128);
+
+ client->fd = sock;
+ FD_SET(sock, &rfd);
+ client->Argv.in = fdopen(sock, "r");
+ client->Argv.out = fdopen(sock, "w");
+
+ client->buffer[0] = '\0';
+ client->mode = 0;
+
+ in = &client->in;
+ if (in->data != NULL)
+ free(in->data);
+ in->data = (char *) malloc(ChannelSize * 4);
+ in->left = ChannelSize * 4;
+ in->used = 0;
+
+ out = &client->out;
+ if (out->data != NULL)
+ free(out->data);
+ out->data = (char *) malloc(ChannelSize);
+ out->left = ChannelSize;
+ out->used = 0;
+}
+
+
+static void
+channeldestroy(client)
+ ClientType *client;
+{
+ FD_CLR(client->fd, &rfd);
+ fclose(client->Argv.in);
+ fclose(client->Argv.out);
+ close(client->fd);
+ client->fd = -1;
+
+ if (client->in.data != NULL)
+ {
+ free(client->in.data);
+ client->in.data = NULL;
+ }
+ if (client->out.data != NULL)
+ {
+ free(client->out.data);
+ client->out.data = NULL;
+ }
+}
+
+
+static int
+channelreader(client)
+ ClientType *client;
+{
+ int len, used;
+ char *data, *head;
+ buffer_t *in;
+
+ NODENAME = client->nodename;
+
+ in = &client->in;
+ len = in->left;
+ used = in->used;
+ data = in->data;
+ if (len < ReadSize + 3)
+ {
+ len += (used + ReadSize);
+ len += (len >> 3);
+
+ in->data = data = (char *) realloc(data, len);
+ len -= used;
+ in->left = len;
+ }
+
+ head = data + used;
+ len = recv(client->fd, head, ReadSize, 0);
+ if (len <= 0)
+ return len;
+
+ head[len] = '\0';
+ head[len + 1] = '\0';
+
+ if (client->mode) /* data mode */
+ {
+ char *dest;
+ int cc;
+
+ dest = head - 1;
+
+ for (;;)
+ {
+ cc = *head;
+
+ if (!cc)
+ {
+ used = dest - data + 1;
+ in->left -= (used - in->used);
+ in->used = used;
+ return len;
+ }
+
+ head++;
+
+ if (cc == '\r')
+ continue;
+
+ if (cc == '\n')
+ {
+ used = *dest;
+
+ if (used == '.')
+ {
+ if (dest[-1] == '\n')
+ break; /* end of article body */
+ }
+ else
+ {
+ /* strip the trailing space */
+ /* Thor.990110: ¤£¬å ªÅ¦æ\n ªº space, for multi-line merge */
+ /* while (used == ' ' || used == '\t') */
+ while (dest[-1] != '\n' && (used == ' ' || used == '\t'))
+ used = *--dest;
+ }
+ }
+
+ *++dest = cc;
+ }
+
+ /* strip the trailing empty lines */
+ *data = '\0';
+ while (*--dest == '\n')
+ ;
+ dest += 2;
+ *dest = '\0';
+
+ my_recv(client);
+ client->mode = 0;
+ }
+ else /* command mode */
+ {
+ argv_t *argv;
+ daemoncmd_t *dp;
+ FILE *out;
+
+ head = (char *) strchr(head, '\n');
+
+ if (head == NULL)
+ {
+ in->used += len;
+ in->left -= len;
+ return len;
+ }
+
+ *head++ = '\0';
+
+ argv = &client->Argv;
+ argv->argc = argify(data, &argv->argv);
+ argv->dc = dp = searchcmd(argv->argv[0]);
+ out = argv->out;
+
+ if (dp)
+ {
+ if ((argv->argc < dp->minargc) || (argv->argc > dp->maxargc)) /* Àˬd argc ¬O§_º¡¨¬­n¨D */
+ {
+ fprintf(out, "%d Usage: %s\r\n", dp->errorcode, dp->usage);
+ fflush(out);
+ }
+ else if ((dp->mode == 0 || dp->mode == 1) && (client->mode != dp->mode)) /* Àˬd data/command mode ¬O§_º¡¨¬­n¨D */
+ {
+ fprintf(out, "%d %s error\r\n", dp->name, dp->errorcode);
+ fflush(out);
+ }
+ else /* ³q¹L¥H¤W¤T¹DÀˬd */
+ {
+ void (*Main) ();
+
+ if (Main = dp->main)
+ (*Main) (client);
+ }
+ }
+ else
+ {
+ fprintf(out, "500 Syntax error or bad command\r\n");
+ fflush(out);
+ }
+ }
+
+ /* Thor.980825: gc patch: ¦pªG¤w¸g°õ¦æ¹L CMDquit, ¤U­±ªº°Ê§@³£¤£¥Î°µ¤F(client destroied) :) */
+ /* if (client->mode == 0) */
+ if (client->mode == 0 && client->in.data != NULL)
+ {
+ int left;
+
+ left = in->left + in->used;
+
+ if (used = *head)
+ {
+ char *str;
+
+ str = data;
+ while (*str++ = *head++)
+ ;
+
+ used = str - data;
+ }
+
+ in->left = left - used;
+ in->used = used;
+ }
+
+ return len;
+}
+
+
+/* ----------------------------------------------------- */
+/* command set */
+/* ----------------------------------------------------- */
+
+
+static int inetdstart = 0;
+
+
+static void
+CMDhelp(client)
+ ClientType *client;
+{
+ argv_t *argv = &client->Argv;
+ daemoncmd_t *p = argv->dc;
+ FILE *out = argv->out;
+
+ client->mode = 0;
+ fprintf(out, "%d Available Commands\r\n", p->normalcode);
+ for (p = cmds; p->name; p++)
+ fprintf(out, " %s\r\n", p->usage);
+ fprintf(out, "Report problems to "STR_SYSOP".bbs@"MYHOSTNAME"\r\n");
+ fputs(".\r\n", out);
+ fflush(out);
+}
+
+
+static void
+CMDihave(client)
+ ClientType *client;
+{
+ argv_t *argv = &client->Argv;
+ daemoncmd_t *p = argv->dc;
+ FILE *out = argv->out;
+ char *data = argv->argv[1];
+
+ if (data[0] != '<')
+ {
+ fprintf(out, "%d Bad Message-ID\r\n", p->errorcode);
+ }
+ else
+ {
+ fprintf(out, "%d\r\n", p->normalcode);
+ client->mode = 1;
+
+ strcpy(client->in.data, "\n\n\n");
+ client->in.left += client->in.used - 3;
+ client->in.used = 3;
+ }
+ fflush(out);
+}
+
+
+static void
+CMDstat(client)
+ ClientType *client;
+{
+ argv_t *argv = &client->Argv;
+ daemoncmd_t *p = argv->dc;
+ FILE *out = argv->out;
+ char *data = argv->argv[1];
+
+ if (data[0] != '<') /* ¥u¤ä´© <msgid> ¬d¸ß */
+ {
+ fprintf(out, "%d We does NOT support article number stating\r\n", p->errorcode);
+ }
+ else /* ¬d¸ß¬O§_¤w¸g¦¬¨ì <msgid> */
+ {
+ if (HISfetch(data, NULL, NULL))
+ fprintf(out, "%d 0 %s article received\r\n", p->normalcode, data);
+ else
+ fprintf(out, "%d No such article\r\n", p->errorcode);
+ }
+ fflush(out);
+}
+
+
+static void
+CMDquit(client)
+ ClientType *client;
+{
+ argv_t *argv = &client->Argv;
+ daemoncmd_t *p = argv->dc;
+ FILE *out = argv->out;
+
+ client->mode = 0;
+ fprintf(out, "%d quit\r\n", p->normalcode);
+ fflush(out);
+
+ channeldestroy(client);
+}
+
+
+static daemoncmd_t cmds[] =
+{
+ /* cmd-name, cmd-usage, min-argc, max-argc, mode, errorcode, normalcode, cmd-func */
+ {"help", "help [cmd]", 1, 2, 2, 0, NNTP_HELPOK_VAL, CMDhelp},
+ {"quit", "quit", 1, 1, 2, 0, NNTP_GOODBYE_ACK_VAL, CMDquit},
+ {"ihave", "ihave mid", 2, 2, 0, NNTP_HAVEIT_VAL, NNTP_SENDIT_VAL, CMDihave},
+ {"stat", "stat <mid>", 2, 2, 0, NNTP_NOTHING_FOLLOWS_VAL, NNTP_DONTHAVEIT_VAL, CMDstat},
+ {NULL, NULL, 0, 0, 2, 0, 0, NULL}
+};
+
+
+/* ----------------------------------------------------- */
+/* ¥Dµ{¦¡ */
+/* ----------------------------------------------------- */
+
+
+static char * /* ¶Ç¦^¬O¥Ñ­þ¤@­Ó¯¸¥xÁý«H¶i¨Óªº */
+search_nodelist_byhost(hostname)
+ char *hostname;
+{
+ nodelist_t *find;
+ struct hostent *he;
+ char client[128];
+ int i;
+
+ /* itoc.021216: §â NODELIST ³£´«¦¨ host¡A³o¼Ëªº¸Ü¡A
+ ¦pªG nodelist.bbs ¸Ì­±¶ñªº¤£¬O¥¿¸Ñ¡A¹ï¤èÁÙ¬O¥i¥H access */
+
+ for (i = 0; i < NLCOUNT; i++)
+ {
+ find = NODELIST + i;
+ if (he = gethostbyname(find->host))
+ {
+ str_ncpy(client, inet_ntoa(*(struct in_addr *) he->h_addr_list[0]), sizeof(client));
+ if (!strcmp(hostname, client))
+ return find->name;
+ }
+ }
+
+ return NULL;
+}
+
+
+static time_t
+filetime(fpath) /* ¶Ç¦^ fpath ªºÀɮ׮ɶ¡ */
+ char *fpath;
+{
+ struct stat st;
+
+ if (!stat(fpath, &st))
+ return st.st_mtime;
+ return 0;
+}
+
+
+static void
+inndchannel()
+{
+ int i, fd, sock;
+ char *nodename, hostname[128];
+ time_t uptime1; /* time to maintain history */
+ time_t uptime2; /* time in initail_bbs */
+ time_t now;
+ struct tm *ptime;
+ struct timeval to;
+ fd_set orfd; /* temp read fd_set */
+ struct sockaddr_in sin;
+ ClientType *clientp;
+ ClientType Channel[MAXCLIENT];
+
+ /* --------------------------------------------------- */
+ /* initial server */
+ /* --------------------------------------------------- */
+
+ if (inetdstart) /* inetd ±Ò°Ê */
+ {
+ sock = 0;
+ }
+ else /* standalone */
+ {
+ if ((sock = initinetserver()) < 0)
+ return;
+ }
+
+ FD_ZERO(&rfd);
+ FD_SET(sock, &rfd);
+
+ /* --------------------------------------------------- */
+ /* initail history maintain time */
+ /* --------------------------------------------------- */
+
+ time(&uptime1);
+ ptime = localtime(&uptime1);
+ i = (HIS_MAINT_HOUR - ptime->tm_hour) * 3600 + (HIS_MAINT_MIN - ptime->tm_min) * 60;
+ uptime1 += i;
+
+ uptime2 = 0; /* force to initail_bbs in the first time */
+
+ /* --------------------------------------------------- */
+ /* initial channel */
+ /* --------------------------------------------------- */
+
+ memset(Channel, 0, sizeof(Channel));
+
+ for (i = 0; i < MAXCLIENT; i++)
+ {
+ clientp = Channel + i;
+ clientp->fd = -1;
+ }
+
+ /* --------------------------------------------------- */
+ /* main loop */
+ /* --------------------------------------------------- */
+
+ for (;;)
+ {
+ time(&now);
+
+ /* When to maintain history files. */
+ if (now > uptime1)
+ {
+ HISmaint();
+ uptime1 += 86400;
+ }
+
+ /* ­YÀɮפñ uptime2 ÁÙ·sªº¸Ü¡A¨º»ò­«·s¸ü¤J */
+ if (filetime("innd/nodelist.bbs") > uptime2 ||
+ filetime("innd/newsfeeds.bbs") > uptime2 ||
+#ifdef _NoCeM_
+ filetime("innd/ncmperm.bbs") > uptime2 ||
+#endif
+ filetime("innd/spamrule.bbs") > uptime2)
+ {
+ if (!initial_bbs())
+ return;
+ uptime2 = now;
+ }
+
+ /* in order to maintain history, timeout every 20 minutes in case no connections */
+ to.tv_sec = 60 * 20;
+ to.tv_usec = 0;
+ orfd = rfd;
+ if (select(FD_SETSIZE, &orfd, NULL, NULL, &to) <= 0)
+ continue;
+
+ /* ¦³¤H¨Ó³X°Ý¤F */
+
+ if (FD_ISSET(sock, &orfd)) /* ­è¤W¯¸ */
+ {
+ if ((fd = tryaccept(sock)) < 0)
+ continue;
+
+ /* Àˬd¦³¨S¦³¦b nodelist.bbs ¸Ì­± */
+ i = sizeof(sin); /* ­É¥Î i */
+ if (getpeername(fd, (struct sockaddr *) &sin, &i) < 0)
+ {
+ close(fd);
+ continue;
+ }
+ str_ncpy(hostname, inet_ntoa(sin.sin_addr), sizeof(hostname));
+ if (!(nodename = search_nodelist_byhost(hostname)))
+ {
+ char buf[256];
+
+ bbslog("<channel> :Warn: %s ¸Õ¹Ï³s±µ¥»¯¸¡A¦ý¨ä¤£¦b nodelist.bbs ¦W³æ¤¤\n", hostname);
+ sprintf(buf, "502 ±z¤£¦b¥»¯¸ªº nodelist.bbs ¦W³æ¤¤ (%s).\r\n", hostname);
+ write(fd, buf, strlen(buf));
+ close(fd);
+ continue;
+ }
+
+ /* §ä¤@­ÓªÅªº ClientType */
+ for (i = 0; i < MAXCLIENT; i++)
+ {
+ clientp = Channel + i;
+ if (clientp->fd == -1)
+ break;
+ }
+ if (i == MAXCLIENT)
+ {
+ static char *msg_no_desc = "502 ¥Ø«e³s½u¤H¼Æ¹L¦h¡A½Ðµy«á¦A¸Õ\r\n";
+
+ write(fd, msg_no_desc, sizeof(msg_no_desc));
+ close(fd);
+ continue;
+ }
+
+ channelcreate(clientp, fd, nodename, hostname);
+
+ fprintf(clientp->Argv.out, "200 INNBBSD %s (%s)\r\n", VERSION, hostname);
+ fflush(clientp->Argv.out);
+ }
+
+ /* °õ¦æ©Ò¦³ ClientType ªº½Ð¨D¡A¨Ã²M±¼¨S¦b¥Îªº ClientType */
+ for (i = 0; i < MAXCLIENT; i++)
+ {
+ clientp = Channel + i;
+ fd = clientp->fd;
+
+ if ((fd >= 0) && FD_ISSET(fd, &orfd) && (channelreader(clientp) <= 0))
+ channeldestroy(clientp);
+ }
+ }
+}
+
+
+#ifdef SOLARIS
+
+#include <sys/resource.h>
+
+static int
+getdtablesize()
+{
+ struct rlimit limit;
+
+ if (getrlimit(RLIMIT_NOFILE, &limit) >= 0)
+ return limit.rlim_cur;
+ return -1;
+}
+#endif
+
+
+static void
+standaloneinit()
+{
+ int ndescriptors, s;
+ FILE *fp;
+
+ ndescriptors = getdtablesize();
+ if (!inetdstart)
+ {
+ if (fork())
+ exit(0);
+ }
+
+ for (s = 3; s < ndescriptors; s++)
+ close(s);
+
+ if (fp = fopen(INNBBSD_PIDFILE, "w"))
+ {
+ fprintf(fp, "%d\n", getpid());
+ fclose(fp);
+ }
+}
+
+
+static void
+usage(argv)
+ char *argv;
+{
+ printf("Usage: %s [options]\n", argv);
+ printf(" -i ¥H inetd wait option ±Ò°Ê\n");
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int c;
+ struct sockaddr_in sin;
+
+ setgid(BBSGID);
+ setuid(BBSUID);
+ chdir(BBSHOME);
+ umask(077);
+
+ while ((c = getopt(argc, argv, "i")) != -1)
+ {
+ switch (c)
+ {
+ case 'i':
+ c = sizeof(sin);
+ if (getsockname(0, (struct sockaddr *) &sin, &c) < 0)
+ {
+ printf("±z¤£¬O±q inetd ±Ò°Ê¡AµL»Ý¨Ï¥Î -i\n");
+ exit(0);
+ }
+ inetdstart = 1;
+ break;
+
+ default:
+ usage(argv[0]);
+ exit(-1);
+ }
+ }
+
+ init_bshm();
+ standaloneinit();
+ inndchannel();
+
+ exit(0);
+}
diff --git a/innbbsd/convcode.c b/innbbsd/convcode.c
new file mode 100644
index 0000000..55ac2c4
--- /dev/null
+++ b/innbbsd/convcode.c
@@ -0,0 +1,169 @@
+/* ----------------------------------------------------- */
+/* ²ÁcÅéº~¦rÂà´« */
+/* ----------------------------------------------------- */
+/* create : / / */
+/* update : 03/05/16 */
+/* author : kcn@cic.tsinghua.edu.cn */
+/* modify : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/* ----------------------------------------------------- */
+
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+
+#define BtoG_count 13973
+#define GtoB_count 7614
+
+#define BtoG_bad1 0xa1
+#define BtoG_bad2 0xf5
+#define GtoB_bad1 0xa1
+#define GtoB_bad2 0xbc
+
+
+static unsigned char *BtoG = NULL;
+static unsigned char *GtoB = NULL;
+
+
+static void
+conv_init()
+{
+ int fd, size, BGsize, GBsize;
+ struct stat st;
+
+ if (BtoG != NULL)
+ return;
+
+ BGsize = BtoG_count << 1; /* ¨C­Óº~¦r 2-byte */
+ GBsize = GtoB_count << 1;
+ BtoG = (unsigned char *) malloc(BGsize + GBsize);
+ GtoB = BtoG + BGsize;
+
+ if ((fd = open("etc/b2g_table", O_RDONLY)) >= 0)
+ {
+ fstat(fd, &st);
+ size = BGsize <= st.st_size ? BGsize : st.st_size;
+ read(fd, BtoG, size);
+ close(fd);
+ }
+ if ((fd = open("etc/g2b_table", O_RDONLY)) >= 0)
+ {
+ fstat(fd, &st);
+ size = GBsize <= st.st_size ? GBsize : st.st_size;
+ read(fd, GtoB, size);
+ close(fd);
+ }
+}
+
+
+#define c1 (unsigned char)(src[0])
+#define c2 (unsigned char)(src[1])
+
+
+static void
+b2g(src, dst)
+ unsigned char *src, *dst;
+{
+ int i;
+
+ if ((c1 >= 0xa1) && (c1 <= 0xf9))
+ {
+ if ((c2 >= 0x40) && (c2 <= 0x7e))
+ {
+ i = ((c1 - 0xa1) * 157 + (c2 - 0x40)) * 2;
+ dst[0] = BtoG[i++];
+ dst[1] = BtoG[i];
+ return;
+ }
+ else if ((c2 >= 0xa1) && (c2 <= 0xfe))
+ {
+ i = ((c1 - 0xa1) * 157 + (c2 - 0xa1) + 63) * 2;
+ dst[0] = BtoG[i++];
+ dst[1] = BtoG[i];
+ return;
+ }
+ }
+ dst[0] = BtoG_bad1;
+ dst[1] = BtoG_bad2;
+}
+
+
+static void
+g2b(src, dst)
+ unsigned char *src, *dst;
+{
+ int i;
+
+ if ((c2 >= 0xa1) && (c2 <= 0xfe))
+ {
+ if ((c1 >= 0xa1) && (c1 <= 0xa9))
+ {
+ i = ((c1 - 0xa1) * 94 + (c2 - 0xa1)) * 2;
+ dst[0] = GtoB[i++];
+ dst[1] = GtoB[i];
+ return;
+ }
+ else if ((c1 >= 0xb0) && (c1 <= 0xf7))
+ {
+ i = ((c1 - 0xb0 + 9) * 94 + (c2 - 0xa1)) * 2;
+ dst[0] = GtoB[i++];
+ dst[1] = GtoB[i];
+ return;
+ }
+ }
+ dst[0] = GtoB_bad1;
+ dst[1] = GtoB_bad2;
+}
+
+
+static char *
+hzconvert(src, dst, dbcvrt)
+ char *src; /* source char buffer pointer */
+ char *dst; /* destination char buffer pointer */
+ void (*dbcvrt) (); /* º~¦r 2-byte conversion funcntion */
+{
+ int len;
+ char *end, *p;
+
+ conv_init();
+
+ p = dst;
+ len = strlen(src);
+ end = src + len;
+ while (src < end)
+ {
+ if (*src & 0x80) /* hi-bit on ªí¥Ü¬Oº~¦r */
+ {
+ dbcvrt(src, p);
+ src += 2; /* ¤@¦¸Âà¤G½X */
+ p += 2;
+ }
+ else
+ {
+ /* *p = *src; */ /* ¤£»Ý­n¡A¦]¬°¦b b52gb()¡Bgb2b5() ªºÀ³¥Î¸Ì src == dst */
+ src++;
+ p++;
+ }
+ }
+ /* dst[len] = '\0'; */ /* ¤£»Ý­n¡A¦]¬°¦b b52gb()¡Bgb2b5() ªºÀ³¥Î¸Ì src == dst */
+
+ return dst;
+}
+
+
+void
+b52gb(str)
+ char *str;
+{
+ hzconvert(str, str, b2g);
+}
+
+
+void
+gb2b5(str)
+ char *str;
+{
+ hzconvert(str, str, g2b);
+}
diff --git a/innbbsd/history.c b/innbbsd/history.c
new file mode 100644
index 0000000..b4893da
--- /dev/null
+++ b/innbbsd/history.c
@@ -0,0 +1,135 @@
+/*-------------------------------------------------------*/
+/* history.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : innbbsd history */
+/* create : 04/04/01 */
+/* update : / / */
+/* author : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "innbbsconf.h"
+
+
+typedef struct
+{
+ time_t htime; /* ¥[¤J history Àɪº®É¶¡ */
+ int hash; /* ¬°¤F§Ö³t·j´M */
+ char msgid[256]; /* message id (°²³] 256 ¤w°÷ªø) */
+ char board[BNLEN + 1];
+ char xname[9];
+} HIS;
+
+
+void
+HISmaint() /* ºûÅ@ history ÀÉ¡A±N¹L¦­ªº history §R°£ */
+{
+ int i, fd, total;
+ char fpath[64];
+ time_t now;
+ struct stat st;
+ HIS *data, *hhead, *htail, *his;
+
+ /* ¥u«O¯d³Ìªñ EXPIREDAYS ¤Ñªº history */
+ time(&now);
+ now = time(NULL) - EXPIREDAYS * 86400;
+
+ for (i = 0; i < 32; i++)
+ {
+ sprintf(fpath, "innd/history/%02d", i);
+
+ if ((fd = open(fpath, O_RDONLY)) < 0)
+ continue;
+
+ fstat(fd, &st);
+ data = (HIS *) malloc(total = st.st_size);
+ total = read(fd, data, total);
+ close(fd);
+
+ hhead = data;
+ htail = data + total / sizeof(HIS);
+ total = 0;
+
+ for (his = hhead; his < htail; his++)
+ {
+ if (his->htime > now) /* ³oµ§ history ¤£³Q¬å */
+ {
+ memcpy(hhead, his, sizeof(HIS));
+ hhead++;
+ total += sizeof(HIS);
+ }
+ }
+
+ if ((fd = open(fpath, O_WRONLY | O_CREAT | O_TRUNC, 0600)) >= 0)
+ {
+ write(fd, data, total);
+ close(fd);
+ }
+
+ free(data);
+ }
+}
+
+
+void
+HISadd(msgid, board, xname) /* ±N (msgid, path, xname) ¦¹°t¹ï°O¿ý¦b history ¤¤ */
+ char *msgid;
+ char *board;
+ char *xname;
+{
+ HIS his;
+ char fpath[64];
+
+ memset(&his, 0, sizeof(HIS));
+
+ time(&(his.htime));
+ his.hash = str_hash(msgid, 1);
+ str_ncpy(his.msgid, msgid, sizeof(his.msgid));
+ str_ncpy(his.board, board, sizeof(his.board));
+ str_ncpy(his.xname, xname, sizeof(his.xname));
+
+ /* ¨Ì msgid ±N history ¥´´²¦Ü 32 ­ÓÀÉ®× */
+ sprintf(fpath, "innd/history/%02d", his.hash & 31);
+ rec_add(fpath, &his, sizeof(HIS));
+}
+
+
+int /* 1:¦bhistory¤¤ 0:¤£¦bhistory¤¤ */
+HISfetch(msgid, board, xname) /* ¬d¸ß history ¤¤¡Amsgid µoªí¥h¤F­þ¸Ì */
+ char *msgid;
+ char *board; /* ¶Ç¥X¦b history ¤¤ªº°O¿ýªº¬ÝªO¤ÎÀɦW */
+ char *xname;
+{
+ HIS his;
+ char fpath[64];
+ int fd, hash;
+ int rc = 0;
+
+ /* ¦pªG¦P¤@ msgid µoªí¥h«Ü¦h­Ó¬ÝªO¡A¨º»ò¥Ø«e¥u·|¦^¶Ç²Ä¤@­Ó¬ÝªO¤ÎÀɦW */
+
+ /* ¨Ì msgid §ä¥X¦b­þ¤@¥÷ history Àɮפ¤ */
+ hash = str_hash(msgid, 1);
+ sprintf(fpath, "innd/history/%02d", hash & 31);
+
+ /* ¥h¸Ó¥÷ history Àɮפ¤§ä¬Ý¬Ý¦³¨S¦³ */
+ if ((fd = open(fpath, O_RDONLY)) >= 0)
+ {
+ lseek(fd, 0, SEEK_SET);
+ while (read(fd, &his, sizeof(HIS)) == sizeof(HIS))
+ {
+ /* ¥Î hash ¥ý²Ê²¤¤ñ¹ï¡A­Y¬Û¦P¦A¥Î msgid §¹¾ã¤ñ¹ï */
+ if ((hash == his.hash) && !strcmp(msgid, his.msgid))
+ {
+ if (board)
+ strcpy(board, his.board);
+ if (xname)
+ strcpy(xname, his.xname);
+ rc = 1;
+ break;
+ }
+ }
+ close(fd);
+ }
+
+ return rc;
+}
diff --git a/innbbsd/innbbsconf.h b/innbbsd/innbbsconf.h
new file mode 100644
index 0000000..02c860d
--- /dev/null
+++ b/innbbsd/innbbsconf.h
@@ -0,0 +1,70 @@
+/*-------------------------------------------------------*/
+/* innbbsconf.h ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : innbbsd configurable settings */
+/* create : 95/04/27 */
+/* modify : / / */
+/* author : skhuang@csie.nctu.edu.tw */
+/* modify : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#ifndef _INNBBSCONF_H_
+#define _INNBBSCONF_H_
+
+#include "bbs.h"
+
+#include <sys/wait.h>
+
+
+/* ----------------------------------------------------- */
+/* ¤@¯ë²ÕºA */
+/* ----------------------------------------------------- */
+
+#define VERSION "0.8-MapleBBS" /* ª©¥»«Å§i */
+
+#define MYBBSID BBSNAME2 /* Path: ¥Î­^¤å¯¸¦W */
+
+#define LOGFILE "innd/innbbs.log" /* °O¿ýÀɸô®| */
+
+
+/* ----------------------------------------------------- */
+/* innbbsd ªº³]©w */
+/* ----------------------------------------------------- */
+
+ /* --------------------------------------------------- */
+ /* channel ªº³]©w */
+ /* --------------------------------------------------- */
+
+#define MAXCLIENT 20 /* Maximum number of connections accepted by innbbsd */
+
+#define ChannelSize 4096
+#define ReadSize 4096
+
+ /* --------------------------------------------------- */
+ /* rec_article ªº³]©w */
+ /* --------------------------------------------------- */
+
+#define _NoCeM_ /* No See Them ¾×«H¾÷¨î */
+
+#undef _KEEP_CANCEL_ /* «O¯d cancel ªº¤å³¹©ó deleted ªO */
+
+
+/* ----------------------------------------------------- */
+/* bbslink ªº³]©w */
+/* ----------------------------------------------------- */
+
+#define MAX_ARTS 100 /* ¨C­Ó newsgroup ¤@¦¸³Ì¦h¦¬´X«Ê«H */
+
+#define BBSLINK_EXPIRE 3600 /* bbslink ­Y°õ¦æ¹L¤[¡A´Nª½±µ kill ±¼(¬í) */
+
+
+/* ----------------------------------------------------- */
+/* History Âà«H°O¿ýºûÅ@ */
+/* ----------------------------------------------------- */
+
+#define EXPIREDAYS 5 /* Âà«H°O¿ý«O¯d¤Ñ¼Æ */
+#define HIS_MAINT_HOUR 4 /* time to maintain history database */
+#define HIS_MAINT_MIN 30 /* time to maintain history database */
+
+#endif /* _INNBBSCONF_H_ */
diff --git a/innbbsd/inntobbs.c b/innbbsd/inntobbs.c
new file mode 100644
index 0000000..0e868cf
--- /dev/null
+++ b/innbbsd/inntobbs.c
@@ -0,0 +1,242 @@
+/*-------------------------------------------------------*/
+/* inntobbs.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : innbbsd INN to BBS */
+/* create : 95/04/27 */
+/* update : / / */
+/* author : skhuang@csie.nctu.edu.tw */
+/* modify : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "innbbsconf.h"
+#include "bbslib.h"
+#include "inntobbs.h"
+
+
+typedef struct Header
+{
+ char *name;
+ int id;
+} header_t;
+
+
+enum HeaderValue /* ©Ò¦³¦³¥Î¨ìªº header */
+{
+ SUBJECT_H,
+ FROM_H,
+ DATE_H,
+ PATH_H,
+ GROUP_H,
+ MSGID_H,
+
+ SITE_H,
+ POSTHOST_H,
+ CONTROL_H,
+
+ LASTHEADER
+};
+
+
+/* ¥u¹ï³o¨ÇÀÉÀY¦³¿³½ì */
+static header_t headertable[LASTHEADER] =
+{
+ "Subject", SUBJECT_H,
+ "From", FROM_H,
+ "Date", DATE_H,
+ "Path", PATH_H,
+ "Newsgroups", GROUP_H,
+ "Message-ID", MSGID_H,
+
+ /* SITE_H (§t) ¥H¤U¬°«D¥²³ÆÀÉÀY */
+ "Organization", SITE_H,
+ "NNTP-Posting-Host", POSTHOST_H,
+ "Control", CONTROL_H,
+};
+
+
+char *NODENAME;
+char *BODY;
+char *SUBJECT, *FROM, *DATE, *PATH, *GROUP, *MSGID, *POSTHOST, *SITE, *CONTROL;
+
+
+static int
+header_cmp(a, b)
+ header_t *a, *b;
+{
+ return str_cmp(a->name, b->name);
+}
+
+
+static int
+header_value(inputheader)
+ char *inputheader;
+{
+ header_t key, *findkey;
+ static int already_init = 0;
+
+ if (!already_init)
+ {
+ qsort(headertable, sizeof(headertable) / sizeof(header_t), sizeof(header_t), header_cmp);
+ already_init = 1;
+ }
+
+ key.name = inputheader;
+ findkey = bsearch(&key, (char *) headertable, sizeof(headertable) / sizeof(header_t), sizeof(key), header_cmp);
+ if (findkey != NULL)
+ return findkey->id;
+
+ return -1;
+}
+
+
+static int
+is_loopback(path, token, len)
+ char *path, *token;
+ int len;
+{
+ int cc;
+
+ if (!path) /* ­Y¨S¦³ PATH «h¤£Àˬd */
+ return 0;
+
+ for (;;)
+ {
+ cc = path[len];
+ if ((!cc || cc == '!') && !str_ncmp(path, token, len))
+ return 1;
+
+ for (;;)
+ {
+ cc = *path;
+ if (!cc)
+ return 0;
+ path++;
+ if (cc == '!')
+ break;
+ }
+ }
+
+ return 0;
+}
+
+
+int /* 1:¦¨¥\ 0:PATH¥]¬A¦Û¤v -1:ÀÉÀY¤£§¹¾ã */
+readlines(data) /* Ū¤JÀÉÀY©M¤º¤å */
+ char *data;
+{
+ int i;
+ char *front, *ptr, *hptr;
+ static char *HEADER[LASTHEADER];
+
+ for (i = 0; i < LASTHEADER; i++)
+ HEADER[i] = NULL;
+ BODY = NULL;
+
+ ptr = data;
+
+ for (;;)
+ {
+ front = ptr + 1;
+ if (*front == '\n')
+ {
+ /* skip leading empty lines */
+ do
+ {
+ front++;
+ } while (*front == '\n');
+
+ BODY = front;
+ break;
+ }
+
+ ptr = (char *) strchr(front, '\n');
+ if (!ptr)
+ break;
+ *ptr = '\0';
+
+ hptr = (char *) strchr(front, ':');
+ if (hptr && hptr[1] == ' ')
+ {
+ *hptr = '\0';
+
+ i = header_value(front);
+ if (i >= 0) /* ¬O¦³¿³½ìªºÀÉÀY */
+ {
+ HEADER[i] = hptr + 2;
+
+ /* merge multi-line header */
+
+ hptr = ptr;
+
+ /* while (ptr[1] == ' ') */
+ /* Thor.990110: ¦³ªº¬O¥Î \t ¥NªíÁÙ¦³ */
+ while (ptr[1] == ' ' || ptr[1] == '\t')
+ {
+ /* while (*++ptr == ' ') ; */
+ do
+ {
+ ++ptr;
+ } while (*ptr == ' ' || *ptr == '\t');
+
+ for (;;)
+ {
+ i = *ptr;
+ if (i == '\n')
+ break;
+ if (i == '\0')
+ {
+ ptr--;
+ break;
+ }
+ ptr++;
+ *hptr++ = i;
+ }
+
+ *hptr = '\0';
+ }
+
+ /* well, ptr point to end of line */
+ }
+ }
+
+ front = ptr;
+ }
+
+ /* ÀˬdÀÉÀYÄæ¦ì¬O§_§¹¾ã */
+ for (i = 0; i < POSTHOST_H; i++) /* POSTHOST_H (§t) ¥H¤U¬°«D¥²³ÆÀÉÀY */
+ {
+ if (!HEADER[i] || !*HEADER[i])
+ return -1;
+ }
+ if (!BODY || !*BODY)
+ return -1;
+
+ SUBJECT = HEADER[SUBJECT_H];
+ FROM = HEADER[FROM_H];
+ DATE = HEADER[DATE_H];
+ PATH = HEADER[PATH_H];
+ GROUP = HEADER[GROUP_H];
+ MSGID = HEADER[MSGID_H];
+ SITE = HEADER[SITE_H];
+ POSTHOST = HEADER[POSTHOST_H];
+ CONTROL = HEADER[CONTROL_H];
+
+ /* SITE_H (§t) ¥H¤U¬°«D¥²³ÆÀÉÀY¡A¦ý¤´­nÀˬd¬O§_¬°ªÅ¦r¦ê */
+ if (SITE && !*SITE)
+ SITE = NULL;
+ if (POSTHOST && !*POSTHOST)
+ POSTHOST = NULL;
+ if (CONTROL && !*CONTROL)
+ return -1;
+
+ if (!CONTROL) /* ¤@¯ë«H¥ó */
+ {
+ /* itoc.030223.µù¸Ñ: ¬Ý¨ì path ¸Ì­±¦³¦Û¤v¯¸ªº¦WºÙ¥H«á¡A«H´N¤£·|¶i¨Ó¡A
+ Á×§K¯¸¤Wªº«H³Q bbslink °e¥h news server ¥H«á¡A¤S³Q¦Û¤v¥Î bbsnnrp ¨ú¦^ */
+ if (is_loopback(PATH, MYBBSID, strlen(MYBBSID)))
+ return 0;
+ }
+
+ return 1;
+}
diff --git a/innbbsd/inntobbs.h b/innbbsd/inntobbs.h
new file mode 100644
index 0000000..c593abf
--- /dev/null
+++ b/innbbsd/inntobbs.h
@@ -0,0 +1,17 @@
+#ifndef _INNTOBBS_H_
+#define _INNTOBBS_H_
+
+/* inntobbs.c */
+extern char *NODENAME;
+extern char *BODY;
+extern char *SUBJECT, *FROM, *DATE, *PATH, *GROUP, *MSGID, *SITE, *POSTHOST, *CONTROL;
+
+/* inntobbs.c */
+extern int readlines(char *data);
+
+/* history.c */
+extern void HISmaint();
+extern void HISadd(char *msgid, char *board, char *xname);
+extern int *HISfetch(char *msgid, char *board, char *xname);
+
+#endif /* _INNTOBBS_H_ */
diff --git a/innbbsd/nntp.h b/innbbsd/nntp.h
new file mode 100644
index 0000000..72dc5fa
--- /dev/null
+++ b/innbbsd/nntp.h
@@ -0,0 +1,145 @@
+/* $Revision: 1.1.1.1 $
+**
+** Here be a set of NNTP response codes as defined in RFC977 and elsewhere.
+** The reponse codes are three digits, RFI, defined like this:
+** R, Response:
+** 1xx Informative message
+** 2xx Command ok
+** 3xx Command ok so far, send the rest of it.
+** 4xx Command was correct, but couldn't be performed for
+** some reason.
+** 5xx Command unimplemented, or incorrect, or a serious
+** program error occurred.
+** F, Function:
+** x0x Connection, setup, and miscellaneous messages
+** x1x Newsgroup selection
+** x2x Article selection
+** x3x Distribution functions
+** x4x Posting
+** x8x Nonstandard extensions (AUTHINFO, XGTITLE)
+** x9x Debugging output
+** I, Information:
+** No defined semantics
+*/
+#define NNTP_HELPOK_VAL 100
+#define NNTP_BAD_COMMAND_VAL 500
+#define NNTP_BAD_COMMAND "500 Syntax error or bad command"
+#define NNTP_TEMPERR_VAL 503
+#define NNTP_ACCESS "502 Permission denied"
+#define NNTP_ACCESS_VAL 502
+#define NNTP_GOODBYE_ACK "205"
+#define NNTP_GOODBYE_ACK_VAL 205
+#define NNTP_GOODBYE "400"
+#define NNTP_GOODBYE_VAL 400
+#define NNTP_HAVEIT "435 Duplicate"
+#define NNTP_HAVEIT_BADID "435 Bad Message-ID"
+#define NNTP_HAVEIT_VAL 435
+#define NNTP_LIST_FOLLOWS "215"
+#define NNTP_LIST_FOLLOWS_VAL 215
+#define NNTP_HELP_FOLLOWS "100 Legal commands"
+#define NNTP_HELP_FOLLOWS_VAL 100
+#define NNTP_NOTHING_FOLLOWS_VAL 223
+#define NNTP_ARTICLE_FOLLOWS "220"
+#define NNTP_ARTICLE_FOLLOWS_VAL 220
+#define NNTP_NEWGROUPS_FOLLOWS_VAL 231
+#define NNTP_HEAD_FOLLOWS "221"
+#define NNTP_HEAD_FOLLOWS_VAL 221
+#define NNTP_BODY_FOLLOWS_VAL 222
+#define NNTP_OVERVIEW_FOLLOWS_VAL 224
+#define NNTP_DATE_FOLLOWS_VAL 111
+#define NNTP_POSTOK "200"
+#define NNTP_POSTOK_VAL 200
+#define NNTP_START_POST_VAL 340
+#define NNTP_NOPOSTOK_VAL 201
+#define NNTP_SLAVEOK_VAL 202
+#define NNTP_REJECTIT_VAL 437
+#define NNTP_REJECTIT_EMPTY "437 Empty article"
+#define NNTP_DONTHAVEIT "430"
+#define NNTP_DONTHAVEIT_VAL 430
+#define NNTP_RESENDIT_NOHIST "436 Can't write history"
+#define NNTP_RESENDIT_NOSPACE "436 No space"
+#define NNTP_RESENDIT_VAL 436
+#define NNTP_POSTEDOK "240 Article posted"
+#define NNTP_POSTEDOK_VAL 240
+#define NNTP_POSTFAIL_VAL 441
+#define NNTP_GROUPOK_VAL 211
+#define NNTP_SENDIT "335"
+#define NNTP_SENDIT_VAL 335
+#define NNTP_SYNTAX_USE "501 Bad command use"
+#define NNTP_SYNTAX_VAL 501
+#define NNTP_TOOKIT "235"
+#define NNTP_TOOKIT_VAL 235
+#define NNTP_NOTINGROUP "412 Not in a newsgroup"
+#define NNTP_NOTINGROUP_VAL 412
+#define NNTP_NOSUCHGROUP "411 No such group"
+#define NNTP_NOSUCHGROUP_VAL 411
+#define NNTP_NEWNEWSOK "230 New news follows"
+#define NNTP_NOARTINGRP "423 Bad article number"
+#define NNTP_NOARTINGRP_VAL 423
+#define NNTP_NOCURRART "420 No current article"
+#define NNTP_NOCURRART_VAL 420
+#define NNTP_NONEXT_VAL 421
+#define NNTP_NOPREV_VAL 422
+#define NNTP_CANTPOST "440 Posting not allowed"
+#define NNTP_CANTPOST_VAL 440
+
+
+/*
+** The first character of an NNTP reply can be used as a category class.
+*/
+#define NNTP_CLASS_OK '2'
+#define NNTP_CLASS_ERROR '4'
+#define NNTP_CLASS_FATAL '5'
+
+
+/*
+** The NNTP protocol currently has no way to say "offer me this article
+** later, but don't close the connection." That will be fixed in NNTP2.
+#define NNTP_RESENDIT_LATER "?"
+#define NNTP_RESENDIT_LATER_VAL ?
+*/
+
+
+/*
+** Authentication commands from the RFC update (not official).
+*/
+#define NNTP_AUTH_NEEDED "480"
+#define NNTP_AUTH_NEEDED_VAL 480
+#define NNTP_AUTH_BAD "481"
+#define NNTP_AUTH_NEXT "381"
+#define NNTP_AUTH_NEXT_VAL 381
+#define NNTP_AUTH_OK "281"
+#define NNTP_AUTH_OK_VAL 281
+#define NNTP_AUTH_REJECT_VAL 482
+
+/*
+** XGTITLE, from ANU news.
+*/
+#define NNTP_XGTITLE_BAD 481 /* Yes, 481. */
+#define NNTP_XGTITLE_OK 282
+
+#define NNTP_STRLEN 512
+
+/*
+** For tin newsreader
+*/
+#define OK_XINDEX 218 /* Tin style group index file follows */
+#define OK_XMOTD 217 /* Motd (message of the day) file follows */
+#define ERR_XINDEX 418 /* No tin style index file for newsgroup */
+#define ERR_XMOTD 417 /* No motd (message of the day) file */
+
+/* For DBZ server */
+#define NNTP_ADDHIST_OK 283 /* addhist OK */
+#define NNTP_GREPHIST_OK 284 /* grephist OK */
+#define NNTP_MIDCHECK_OK 285 /* grephist OK */
+#define NNTP_SHUTDOWN_OK 286 /* grephist OK */
+#define NNTP_RELOAD_OK 287 /* grephist OK */
+#define NNTP_MODE_OK 101 /* grephist OK */
+#define NNTP_VERBOSELOG_OK 289 /* grephist OK */
+#define NNTP_ADDHIST_BAD 483 /* addhist fail */
+#define NNTP_GREPHIST_BAD 484 /* grephist fail */
+#define NNTP_MIDCHECK_BAD 485 /* grephist fail */
+#define NNTP_SHUTDOWN_BAD 486 /* grephist fail */
+#define NNTP_RELOAD_BAD 487 /* grephist fail */
+#define NNTP_MODE_BAD 488 /* grephist fail */
+#define NNTP_VERBOSELOG_BAD 489 /* grephist fail */
diff --git a/innbbsd/nocem.c b/innbbsd/nocem.c
new file mode 100644
index 0000000..a67ef00
--- /dev/null
+++ b/innbbsd/nocem.c
@@ -0,0 +1,508 @@
+/*-------------------------------------------------------*/
+/* nocem.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : NoCeM-INNBBSD */
+/* create : 99/02/25 */
+/* update : / / */
+/* author : leeym@cae.ce.ntu.edu.tw */
+/* modify : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#if 0 /* itoc.030109.µù¸Ñ: nocem.c ªº¬yµ{ */
+ ±q rec_article.c ¦¬¨ì receive_nocem() ¥H«á
+ receive_nocem() ¡÷ NCMparse() §â notice parse ¥X¨Ó ¡÷ NCMverify() ÅçÃÒ¬O¤£¬O¯uªº
+ ¡÷ NCMcancel() ¦A°e¦^ rec_article.c ªº cancel_article() ³B²z
+#endif
+
+
+#include "innbbsconf.h"
+
+#ifdef _NoCeM_
+
+#include "bbslib.h"
+#include "inntobbs.h"
+#include "nocem.h"
+
+
+/* ÅçÃÒñ¦W¡G¥H¤U¤GªÌ¦Ü¦h¥u¯à¿ï¤@ªÌ #define (¥i¥H¤GªÌ³£ #undef) */
+#undef PGP /* ¥²¶·¸Ë¦³ pgp5 ¤~¥i define¡A¨Ã½ÐÀˬd pgpv ªº¸ô®| */
+#undef GPG /* ¥²¶·¸Ë¦³ gpg ¤~¥i define¡A¨Ã½ÐÀˬd gpg ªº¸ô®| */
+
+static int num_spammid = 0;
+static char NCMVER[20];
+static char ISSUER[80];
+static char TYPE[40];
+static char ACTION[20];
+static char SPAMMID_NOW[80];
+static char SPAMMID[MAXSPAMMID][80];
+static char errmsg[512] = "nothing";
+
+
+/* ----------------------------------------------------- */
+/* NCM maintain */
+/* ----------------------------------------------------- */
+
+
+ncmperm_t *
+search_issuer(issuer, type)
+ char *issuer;
+ char *type; /* ­Y type == NULL ªí¥Ü¥u¤ñ¹ï issuer */
+{
+ ncmperm_t *find;
+ int i;
+
+ for (i = 0; i < NCMCOUNT; i++)
+ {
+ find = NCMPERM + i;
+ if (strstr(issuer, find->issuer) &&
+ (!type || !strcmp(find->type, "*") || !str_cmp(find->type, type)))
+ return find;
+ }
+ return NULL;
+}
+
+
+static void
+NCMupdate(issuer, type)
+ char *issuer, *type;
+{
+ ncmperm_t ncm;
+
+ memset(&ncm, 0, sizeof(ncmperm_t));
+ str_ncpy(ncm.issuer, issuer, sizeof(ncm.issuer));
+ str_ncpy(ncm.type, type, sizeof(ncm.type));
+ ncm.perm = 0;
+ rec_add("innd/ncmperm.bbs", &ncm, sizeof(ncmperm_t));
+ read_ncmperm();
+}
+
+
+/* ----------------------------------------------------- */
+/* PGP verify */
+/* ----------------------------------------------------- */
+
+
+#ifdef PGP
+static int
+run_pgp(cmd, in, out)
+ char *cmd;
+ FILE **in, **out;
+{
+ int pin[2], pout[2], child_pid;
+ char fpath[64];
+
+ strcpy(fpath, BBSHOME "/.pgp");
+ setenv("PGPPATH", fpath, 1);
+
+ *in = *out = NULL;
+
+ pipe(pin);
+ pipe(pout);
+
+ if (!(child_pid = fork()))
+ {
+ /* We're the child. */
+ close(pin[1]);
+ dup2(pin[0], 0);
+ close(pin[0]);
+
+ close(pout[0]);
+ dup2(pout[1], 1);
+ close(pout[1]);
+
+ execl("/bin/sh", "sh", "-c", cmd, NULL);
+ _exit(127);
+ }
+ /* Only get here if we're the parent. */
+ close(pout[1]);
+ *out = fdopen(pout[0], "r");
+
+ close(pin[0]);
+ *in = fdopen(pin[1], "w");
+
+ return child_pid;
+}
+
+
+static int
+verify_buffer(buf, passphrase)
+ char *buf, *passphrase;
+{
+ FILE *pgpin, *pgpout;
+ char tmpbuf[1024] = " ";
+ int ans = NOPGP;
+
+ setenv("PGPPASSFD", "0", 1);
+ run_pgp("/usr/local/bin/pgpv -f +batchmode=1 +OutputInformationFD=1", &pgpin, &pgpout);
+ if (pgpin && pgpout)
+ {
+ fprintf(pgpin, "%s\n", passphrase); /* Send the passphrase in, first */
+ memset(passphrase, 0, strlen(passphrase)); /* Burn the passphrase */
+ fprintf(pgpin, "%s", buf);
+ fclose(pgpin);
+
+ *buf = '\0';
+ fgets(tmpbuf, sizeof(tmpbuf), pgpout);
+ while (!feof(pgpout))
+ {
+ strcat(buf, tmpbuf);
+ fgets(tmpbuf, sizeof(tmpbuf), pgpout);
+ }
+
+ wait(NULL);
+
+ fclose(pgpout);
+ }
+
+ if (strstr(buf, "BAD signature made"))
+ {
+ strcpy(errmsg, "BAD signature");
+ ans = PGPBAD;
+ }
+ else if (strstr(buf, "Good signature made"))
+ {
+ strcpy(errmsg, "Good signature");
+ ans = PGPGOOD;
+ }
+ else if (strcpy(tmpbuf, strstr(buf, "Signature by unknown keyid:")))
+ {
+ sprintf(errmsg, "%s ", strtok(tmpbuf, "\r\n"));
+ ans = PGPUN;
+ }
+
+ unsetenv("PGPPASSFD");
+ return ans;
+}
+
+
+static int /* return 0 success, otherwise fail */
+NCMverify()
+{
+ char passphrase[80] = "Haha, I am Leeym..";
+ return verify_buffer(BODY, passphrase);
+}
+#endif /* PGP */
+
+
+/* ----------------------------------------------------- */
+/* GPG verify */
+/* ----------------------------------------------------- */
+
+
+#ifdef GPG
+static int
+run_gpg(cmd, in, out)
+ char *cmd;
+ FILE **in, **out;
+{
+ int pin[2], pout[2], child_pid;
+ char fpath[64];
+
+ strcpy(fpath, BBSHOME "/.gnupg");
+ setenv("GPGPATH", fpath, 1);
+
+ *in = *out = NULL;
+
+ pipe(pin);
+ pipe(pout);
+
+ if (!(child_pid = fork()))
+ {
+ /* We're the child. */
+ close(pin[1]);
+ dup2(pin[0], 0);
+ close(pin[0]);
+
+ close(pout[0]);
+ dup2(pout[1], 1);
+ close(pout[1]);
+
+ execl("/bin/sh", "sh", "-c", cmd, NULL);
+ _exit(127);
+ }
+ /* Only get here if we're the parent. */
+ close(pout[1]);
+ *out = fdopen(pout[0], "r");
+
+ close(pin[0]);
+ *in = fdopen(pin[1], "w");
+
+ return child_pid;
+}
+
+
+static int
+verify_buffer(buf)
+ char *buf;
+{
+ FILE *pgpin, *pgpout;
+ char tmpbuf[1024] = " ";
+ int ans = NOPGP;
+
+ setenv("PGPPASSFD", "0", 1);
+ run_gpg("/usr/local/bin/gpg --no-secmem-warning --verify", &pgpin, &pgpout);
+ if (pgpin && pgpout)
+ {
+ fprintf(pgpin, "%s", buf);
+ fclose(pgpin);
+
+ *buf = '\0';
+ fgets(tmpbuf, sizeof(tmpbuf), pgpout);
+ while (!feof(pgpout))
+ {
+ strcat(buf, tmpbuf);
+ fgets(tmpbuf, sizeof(tmpbuf), pgpout);
+ }
+
+ wait(NULL);
+
+ fclose(pgpout);
+ }
+
+ if (strstr(buf, "BAD signature made"))
+ {
+ strcpy(errmsg, "BAD signature");
+ ans = PGPBAD;
+ }
+ else if (strstr(buf, "Good signature made"))
+ {
+ strcpy(errmsg, "Good signature");
+ ans = PGPGOOD;
+ }
+ else if (strcpy(tmpbuf, strstr(buf, "Signature by unknown keyid:")))
+ {
+ sprintf(errmsg, "%s ", strtok(tmpbuf, "\r\n"));
+ strcpy(KEYID, strrchr(tmpbuf, ' ') + 1);
+ ans = PGPUN;
+ }
+
+ unsetenv("PGPPASSFD");
+ return ans;
+}
+
+
+static int
+NCMverify()
+{
+ return verify_buffer(BODY);
+}
+#endif /* GPG */
+
+
+/* ----------------------------------------------------- */
+/* parse NoCeM Notice Headers/Body */
+/* ----------------------------------------------------- */
+
+
+static int
+readNCMheader(line)
+ char *line;
+{
+ if (!str_ncmp(line, "Version", strlen("Version")))
+ {
+ str_ncpy(NCMVER, line + strlen("Version") + 2, sizeof(NCMVER));
+ if (strcmp(NCMVER, "0.9"))
+ {
+ sprintf(errmsg, "unknown version: %s", NCMVER);
+ return P_FAIL;
+ }
+ }
+ else if (!str_ncmp(line, "Issuer", strlen("Issuer")))
+ {
+ str_ncpy(ISSUER, line + strlen("Issuer") + 2, sizeof(ISSUER));
+ FROM = ISSUER;
+ }
+ else if (!str_ncmp(line, "Type", strlen("Type")))
+ {
+ str_ncpy(TYPE, line + strlen("Type") + 2, sizeof(TYPE));
+ }
+ else if (!str_ncmp(line, "Action", strlen("Action")))
+ {
+ str_ncpy(ACTION, line + strlen("Action") + 2, sizeof(ACTION));
+ if (strcmp(ACTION, "hide"))
+ {
+ sprintf(errmsg, "unsupported action: %s", ACTION);
+ return P_FAIL;
+ }
+ }
+
+ return P_OKAY;
+}
+
+
+static int
+readNCMbody(line)
+ char *line;
+{
+ char buf[LINELEN], *group;
+
+ strcpy(buf, line);
+
+ if (!strstr(buf, "\t"))
+ return P_FAIL;
+
+ group = strrchr(line, '\t') + 1;
+
+ if (buf[0] == '<' && strstr(buf, ">"))
+ {
+ strtok(buf, "\t");
+ strcpy(SPAMMID_NOW, buf);
+ }
+
+ if (num_spammid && !strcmp(SPAMMID[num_spammid - 1], SPAMMID_NOW))
+ return 0;
+
+ if (search_newsfeeds_bygroup(group))
+ strcpy(SPAMMID[num_spammid++], SPAMMID_NOW);
+}
+
+
+static int /* return 0 success, otherwise fail */
+NCMparse()
+{
+ char *fptr, *ptr;
+ int type = TEXT;
+
+ if (!(fptr = strstr(BODY, "-----BEGIN PGP SIGNED MESSAGE-----")))
+ {
+ strcpy(errmsg, "notice isn't signed");
+ return P_FAIL;
+ }
+
+ for (ptr = strchr(fptr, '\n'); ptr != NULL && *ptr != '\0'; fptr = ptr + 1, ptr = strchr(fptr, '\n'))
+ {
+ int ch = *ptr;
+ int ch2 = *(ptr - 1);
+
+ *ptr = '\0';
+ if (*(ptr - 1) == '\r')
+ *(ptr - 1) = '\0';
+
+ if (num_spammid > MAXSPAMMID)
+ return P_OKAY;
+
+ if (!strncmp(fptr, "@@", 2))
+ {
+ if (strstr(fptr, "BEGIN NCM HEADERS"))
+ {
+ type = NCMHDR;
+ }
+ else if (strstr(fptr, "BEGIN NCM BODY"))
+ {
+ if (NCMVER && ISSUER && TYPE && ACTION)
+ {
+ ncmperm_t *ncmt;
+ ncmt = (ncmperm_t *) search_issuer(ISSUER, TYPE);
+ if (ncmt == NULL)
+ {
+ NCMupdate(ISSUER, TYPE);
+ sprintf(errmsg, "unknown issuer: %s, %s", ISSUER, MSGID);
+ return P_UNKNOWN;
+ }
+ if (!ncmt->perm)
+ {
+ sprintf(errmsg, "disallow issuer: %s, %s", ISSUER, MSGID);
+ return P_DISALLOW;
+ }
+ }
+ else
+ {
+ strcpy(errmsg, "HEADERS syntax not correct");
+ return P_FAIL;
+ }
+ type = NCMBDY;
+ }
+ else if (strstr(fptr, "END NCM BODY"))
+ {
+ *ptr = ch;
+ *(ptr - 1) = ch2;
+ break;
+ }
+ else
+ {
+ strcpy(errmsg, "NCM Notice syntax not correct");
+ return P_FAIL;
+ }
+ *ptr = ch;
+ *(ptr - 1) = ch2;
+ continue;
+ }
+
+ if (type == NCMHDR && readNCMheader(fptr) == P_FAIL)
+ return P_FAIL;
+ if (type == NCMBDY)
+ readNCMbody(fptr);
+ *ptr = ch;
+ *(ptr - 1) = ch2;
+ }
+
+ if (NCMVER && ISSUER && TYPE && ACTION)
+ return P_OKAY;
+
+ strcpy(errmsg, "HEADERS syntax not correct");
+ return P_FAIL;
+}
+
+
+extern int cancel_article();
+
+
+static void
+NCMcancel()
+{
+ int i;
+
+ for (i = 0; i < num_spammid; i++)
+ cancel_article(SPAMMID[i]);
+}
+
+
+/* ----------------------------------------------------- */
+/* NoCeM-innbbsd */
+/* ----------------------------------------------------- */
+
+
+static void
+initial_nocem()
+{
+ memset(SPAMMID[0], 0, strlen(SPAMMID[0]) * num_spammid);
+ num_spammid = 0;
+ memset(SPAMMID_NOW, 0, strlen(SPAMMID_NOW));
+}
+
+
+int /* 0:success -1:fail */
+receive_nocem()
+{
+ int cc;
+
+ initial_nocem();
+
+ cc = NCMparse();
+
+ if (cc != P_OKAY)
+ {
+ if (cc != P_DISALLOW)
+ bbslog("<nocem> :Warn: %s\n", errmsg);
+ return 0;
+ }
+
+ if (!num_spammid) /* nothing to cancel */
+ return 0;
+
+#if (defined(PGP) || defined(GPG))
+ cc = NCMverify();
+
+ if (cc != PGPGOOD)
+ {
+ bbslog("<nocem> :Warn: %s, %s, %s\n", errmsg, MSGID, ISSUER);
+ return -1;
+ }
+#endif
+
+ NCMcancel();
+
+ return 0;
+}
+#endif /* _NoCeM_ */
diff --git a/innbbsd/nocem.h b/innbbsd/nocem.h
new file mode 100644
index 0000000..92033b5
--- /dev/null
+++ b/innbbsd/nocem.h
@@ -0,0 +1,41 @@
+/*
+ NoCeM-INNBBSD
+ Yen-Ming Lee <leeym@cae.ce.ntu.edu.tw>
+*/
+
+#ifndef _NOCEM_H_
+#define _NOCEM_H_
+
+#define NCMINNBBSVER "NoCeM-INNBBSD-0.8" /* ª©¥»«Å§i */
+
+
+#define TEXT 0
+#define NCMHDR 1
+#define NCMBDY 2
+
+#define NOPGP -1
+#define PGPGOOD 0
+#define PGPBAD 1
+#define PGPUN 2
+
+#define P_OKAY 0
+#define P_FAIL -1
+#define P_UNKNOWN -2
+#define P_DISALLOW -3
+
+#define MAXSPAMMID 10000
+#define LINELEN 512
+
+
+/* bbslib.c */
+extern ncmperm_t *NCMPERM;
+extern int NCMCOUNT;
+
+/* receive_article.c */
+extern newsfeeds_t *search_newsfeeds_bygroup();
+
+/* nocem.c */
+extern ncmperm_t *search_issuer();
+extern int receive_nocem();
+
+#endif /* _NOCEM_H_ */
diff --git a/innbbsd/nocem/CHANGES.nocem b/innbbsd/nocem/CHANGES.nocem
new file mode 100644
index 0000000..3832c25
--- /dev/null
+++ b/innbbsd/nocem/CHANGES.nocem
@@ -0,0 +1,94 @@
+NoCeM-innbbsd-patch (¥H¤U²ºÙ ncm-innbbsd)
+CHANGES
+
+ver 0.80
+1-9-04 ¤ä´© GPG
+ (·PÁÂ AlanSung)
+
+ver 0.71
+5-23-01 ¸É¤W¤@¨Ç¿ù»~°T®§, ­×¥¿ NCMparse()
+
+ver 0.70
+5-11-01 ­×¥¿ NCMparse ªº "END NCM BODY" ³¡¤À
+ (·PÁÂ lcr.bbs@server.old-castle.org)
+
+ver 0.69
+11-5-00 ­×¥¿ NCMparse() ¦w¥þ©Ê°ÝÃD
+ (·PÁÂ DarkKiller.bbs@abpe.org)
+
+ver 0.68
+11-4-00 ­×¥¿ NCMparse() ¦w¥þ©Ê°ÝÃD
+ (·PÁÂ DarkKiller.bbs@abpe.org)
+
+ver 0.67
+11-3-00 ­×¥¿ NCMparse() ¦w¥þ©Ê°ÝÃD
+ (·PÁÂ DarkKiller.bbs@abpe.org)
+ ¹w³]§ï¦^¨Ï¥Î PGP ÅçÃÒ, ¤£¥Î PGP ªÌ½Ð¦Û¦æ #undef PGP5
+ (·PÁÂ Amis.bbs@bbs.ee.nthu.edu.tw)
+
+ver 0.66
+5-19-00 ¥Î stdarg.h ¨ú¥N varargs.h, ­×¥¿ SOLARIS ªº va_start() °ÝÃD
+ (·PÁÂ edwardc@concorde.upma.net)
+
+ver 0.65
+4-26-00 ¥[¤J search_issuer_type() , ¤À§O±µ¦¬¦P¤@ issuer ¤£¦P type ªº notice
+ ¼W¥[ MAXSPAMMID ¨ì 10000
+ ·s¼W LINELEN, ³]©w¬° 512
+ ¹w³]§ï¬°¤£¨Ï¥Î PGP ÅçÃÒ.
+ NCMregister() §¹¦¨¶¥¬q©Ê²Î­p¥ô°È, ¤£·Q¥[¤J²Î­p, ©ÎªÌ³s¦Ü TANet ³t«×
+ ¤£§Öªº¯¸¥x, ¥i¦b nocem.h ³]©w #define DONT_REGISTER ¤£¦C¤J²Î­p
+
+ver 0.63
+2-13-00 ¥[¤J¹ï M3-innbbsd ªº patch
+ (·PÁÂ mat.bbs@fall.twbbs.org)
+ M3 ªº BODY ¤w±N \r ³B²z±¼, ©Ò¥H­ì¥ý§ì¦æ§Àªº \r ±N·|§ì¤£¨ì
+ §ó¥¿ v0.6 ¤å¥ó INSTALL.nocem 4.b ¿ù»~ªº¦a¤è
+ * ¹ï¨Ï¥Î v0.6 ¦ÓµLªk¹B§@ªº M3 ¯¸ªø­Ì­Pºp *
+
+ver 0.62
+1-24-00 ¥[¤J tcpcommand() ªº Solaris patch
+ (·PÁÂ edwardc.bbs@drogon.seed.net.tw)
+ ¥[¤J PGP5 ªº #define ¥H¤Î #if .. #endif, ¥i¿ï¾ÜÃö³¬ PGP5 ÅçÃÒ
+
+ver 0.6
+1-17-00 ­«·s¾ã²z¤@¤U NCMregister(), §ï¥Î gethostname()
+ §K±o¦³ bbsadm ³s Makefile ªº ADMINUSER ³£Ãi±o§ï
+ ¥[¤J¹ï Maple3 ªº¦w¸Ë»¡©ú
+
+ver 0.51
+5-21-99 ·s¼W NCMregister(), ¬ö¿ý NoCeM-innbbsd ¨Ï¥Î±¡§Î.
+
+ver 0.5
+3-6-99 §ó·s¤å¥ó
+3-5-99 ±N strcasestr() ´«¦¨ strstr().
+3-4-99 de ±¼¤@­Ó«ÜÄY­«ªº bug: SPAMMID ¤£­­¨î°}¦C¤j¤pªº¸Ü±N·|¶W¥Î,
+ »\±¼¨ä¥LÅܼÆ.. ³y¦¨ innbbsd ±¾±¼.
+ (¯S§O¹³¬O¦Y¨ì nocem@newsgate.nctu.edu.tw ¤@½g notice ªº Count ¤C¦Ê¦h)
+ ¾ã²zµ{¦¡½X, Beta release..
+
+ver 0.43
+3-3-99 ­×§ï receive_nocem(), µo²{¤£¬O notice §Y§ï receive_article()
+
+ver 0.42
+3-2-99 ­×§ï cancel_article_front(), ¤¹³\ Issuer ¬å«H, ¨Ã°t¦X­×¥¿»¡©ú¤å¥ó
+
+ver 0.41
+3-2-99 ­×§ï NCMverify();
+
+ver 0.4
+3-1-99 ²Ó³¡¾ã¦X, ÅܼƳB²z, §ï¥Î NCMfunction() ²Î¤@®æ¦¡´£°ªµ{¦¡¥iŪ©Ê
+
+ver 0.3
+2-28-99 ·s¼W PGP verify ¤§Ã±¦WÀˬd
+ ·s¼W NCMupdate ¦Û°Ê§ó·s³]©wÀÉ
+ §ï¥Ñ search_group ¨Ó¤ñ¹ï¤å³¹, ´î¤Ö DBfetch ªº¦¸¼Æ, ¤j´T´£°ª®Ä¯à.
+
+ver 0.2
+2-27-99 ·s¼W ncmperm.bbs ¤§ Issuer Åv­­±±¨î
+
+ver 0.1
+2-26-99 ½s¼g ncm-innbbsd ¥Dµ{¦¡
+ ¤ÀªR NCM 0.9 ¤§ NCM HEADERS
+ ¤ÀªR NCM ¤§ NCM BODY
+ ¤ÀªR NCM 0.93 ¤§ NCM HEADERS
+2-25-99 °Ñ¦Ò ncmspool-0.92.3b ¤Î c-nocem-3.2 ¨M©w ncm-innbbsd ¤§¹B§@¤è¦¡
diff --git a/innbbsd/nocem/NOCEM b/innbbsd/nocem/NOCEM
new file mode 100644
index 0000000..20d42dc
--- /dev/null
+++ b/innbbsd/nocem/NOCEM
@@ -0,0 +1,140 @@
+§@ªÌ: benjamin («¢~) ¬ÝªO: benjamin
+¼ÐÃD: NoCeM ²¤¶
+®É¶¡: Fri Mar 5 05:03:38 1999
+
+
+ NoCeM ²¤¶
+
+1.NoCeM²¤¶
+
+ NoCeMªºµo­µ¬°(No See 'Em, ¤]´N¬ONo See Them), ¨ä¥D­nªº¥Øªº¬O³B²z²{¦b
+ ±`¨£ªº SPAM, ¾A¥Îªº¹ï¹³¬Û·í¼sªx, ±q news server ªººÞ²z­û¨ì¤@¯ë¾\Ū
+ NEWS ªº¨Ï¥ÎªÌ³£¥i¥[¤J.
+
+ ¨C¦ì¨Ï¥ÎªÌ¥i±N¥L¦bºô¸ô¤W»{¬°¤£­È±o¾\Ū(©Î­È±o¾\Ūªº¤å³¹), ¾ã²z¥X¨Ó,
+ «ö·Ó¤@©wªº®æ¦¡ (NoCeM¦³¯S§O³W©w®æ¦¡), ±N¬ÛÃöªº¸ê®Æ(message-id)»s¦¨¤@
+ ½g¤å³¹ (¦bNoCeM¤¤ºÙ¬°notice), §Q¥Î¦UºØºÞ¹D±N³o¨Ç¸ê®Æ´²¼½µ¹¨ä¥Lªº¨Ï¥Î
+ ªÌ, ´²¼½ªººÞ¹D¨Ã¨S¦³­­¨î, mailing list, web, news³£¬O±`¨£ªº¤è¦¡.
+
+ ¨ä¥Lªº¨Ï¥ÎªÌ«h¥i±µ¨ü³o¨Çnotice, ±µ¨üµo¤åªÌªº«ØÄ³, ¾\Ū©ÎªÌ©Ú¬Ý¤å³¹,
+ ¨Ï¥ÎªÌ¦³µ´¹ïªºÅv§Q¥i¥H¿ï¾Ü¬O§_±µ¨ü³o¨Çnotice, ¦pªG¨Ï¥ÎªÌ«H¥ôµo¥X
+ noticeªº¨Ï¥ÎªÌ, ¨º»ò¥i¥H¿ï¾Ü±µ¨ü¥Lªº«ØÄ³, ©Ú¬Ý¬Y¨Ç¤å³¹, ¤Ï¤§, ¨Ï¥ÎªÌ
+ ¤]¦³¥R¥÷ªº¦Û¥Ñ¥i¥H¿ï¾Ü¤£¥[²z·|.
+
+ ¯S§O­«­nªº¤@ÂI, ©Ò¦³ªºnotice¥²¶·¸g¹Lµo¤åªÌªº PGP ñ¦W, ¦p¦¹¥i¥H½T©w
+ notice¤£·|³Q¥ô¦ó¤H°°³y©Î«§ï.
+
+2.¹ê»Úªº°µªk¤ÎÀ³¥Î
+
+ ¤W­±´£¨ì¨Ï¥ÎªÌ±µ¨ì notice «á¥i¥H±µ¨ü«ØÄ³©Ú¬Ý¤å³¹, ¹ê»ÚªºÀ³¥Î¥i¤À:
+ ¤@¯ë¨Ï¥ÎªÌ»P news server ºÞ²zªÌ¨Ó»¡©ú
+
+ ¤@¯ë¨Ï¥ÎªÌ:
+
+ ¨C¦ì¨Ï¥ÎªÌ¥i¥H¦Û¦æ³]©w±µ¨ü¨º¨Ç¤H°e¥Xªº notice, ·í¨Ï¥ÎªÌ¦b¾\ۤ峹¤§«e
+ ¥ý¨ú±o nocem notice, µM«á°w¹ï¨Ï¥ÎªÌªº³]©w°µ PGP ñ¦WªºÀˬd, Àˬd³q¹L
+ «á¤~Ä~Äò¶i¦æ¤U¤@­Ó¨BÆJ.
+
+ ­è¤~´£¨ì notice ¥i¤À¬°«ØÄ³¾\Ū(show), ©Î©Ú¬Ý(hide), ¨Ó§ó°Ê¨Ï¥ÎªÌªº
+ .newsrcÀÉ, ¤Z¬O³QÂk¬°hideªº¤å³¹, ·|¦b .newsrc¤W°µ¤W¼Ð°O, ¨Ï±o¨Ï¥ÎªÌ±µ¤U
+ ¨Ó¾\ۤ峹®É, ¸Ó¤å³¹·|¦Û°Ê³Q¹LÂo±¼©Î³Q³]¬°¤wŪ, ¦Ü©óÂkÃþ¬°showªº¤å³¹
+ ¤]°µ¯S§O¼Ð°O±N¨äÅã¥Ü¥X¨Ó
+
+ ¥H¥Ø«e¨ÓÁ¿, hide¬O¸û±`³Q¨Ï¥Îªº, ©Ò¦³ªºspam³£·|³QÂkÃþ¬°hide.
+
+ ¦]¦¹, ¦b¨Ï¥ÎªÌ¾\Ū®É, ´N¤w¸g¬Ý¤£¨ì³o½g¤å³¹, ¤£¹L, ³o½g¤å³¹¨ÌµM¦s¦b news
+ server¤§¤¤, ¨Ã¤£·|³Q§R°£
+
+ ²{¦b¤w¦³µo®i¬ÛÃöªºµ{¦¡, ¨Ñ¨Ï¥ÎªÌ¿ï¥Î, ¤£¹L¥u­­©w¤ä´©¦³.newsrcÀɪº news
+ reader µ{¦¡, ¦p:tin......
+ ¤@¯ë¨Ï¥ÎªÌ°£¤F¥i¥H«ü©w±µ¦¬¨º¨Ç¤H°e¥Xªºnotice¥~, ¤]¥i³]©w³o¨Çnotice©Ò
+ ¯à¼vÅT¤Î±±¨îªºnewsgroup.
+
+ news serverºÞ²zªÌ:
+
+ ¥D­nªº­ì²z©M¤W­±©Ò¼gªº¤j­P¬Û¦P, ¤£¹L­ì¨Ó­×§ï.newsrcÀɪº°Ê§@, ²{¦bÅܦ¨
+ ª½±µ±N¤å³¹±q news server ¤¤§R°£, ¦P¼Ë¦a, ºÞ²zªÌ¦³µ´¹ïªºÅv§Q¿ï¾Ü¬O§_¥[¤J
+ NoCeM ©Î¬O¥[¤J«á¥u±µ¨ü¬Y¨Ç¤Hªºnotice.
+
+3.NoCeMªºÀu¯ÊÂI
+
+ A.¸`¬Ùºô¸ôÀW¼e
+
+ »Pcancel message¤ñ¸û, NoCeM¦û¤F¸û¤ÖªºÀW¼e¤ÎªÅ¶¡; cancel message
+ ªº¯S©Ê¬O¤@¹ï¤@, ¤@½gspam´N­n¹ïÀ³¤@½gcancel message, ¹ïµwºÐ¨Ó»¡
+ spam³Q§R°£¤F, ¦ý«o¦h¤F¤@½gcancel message; ¹ïºô¸ôÀW¼e¨Ó»¡, spam
+ »Pcancel message³£³Q¶Ç»¼, ¨âªÌ³£®ö¶O¤FÀW¼e, ¦pªG±q news ¬y¶q¨Ó¤ÀªR,
+ spam»Pcancel messageªº¶q¬O±µªñ 1:1 ªº
+
+ ¤£¹LNoCeM«h¤£¬Û¦P, ¤@½gnotice¤¤¥i¥H¥]§t¬Û·í¦hªºmessage-id, »P
+ cancel¬Û¤ñ, ¬Ù¥h¤F¤j¶q¶Ç»¼©Ò¦ûªºÀW¼e
+
+ B.¸û¤Öª§Ä³, §ó¦h¼u©Ê
+
+ cancel message³Ì±`¤Þ¤Hª§Ä³ªº¬O¨ä¬y¶Ç½d³ò¼s(·íµM, ¸g¹L¾A·íªº³]©w
+ «á, ¥i¥H§ïµ½), ¬Æ¦Ü¦³¤Ö¼Æ¨Ï¥ÎªÌ±N¤§µø¬°ºô¸ô¤Wªº¥Õ¦â®£©Æ, «a¤W¤@³»
+ ­­¨î¨¥½×¦Û¥Ñªº¤j´U¤l, ¬°¤F§K°£³o¨ÇÅU¼{, NoCeM¥iºâ¬O¤@ºØ¤£¿ùªº¸Ñªk
+ ºÞ²zªÌ¥i¦Û¥Ñ¿ï¾Ü¬O§_¥[¤JNoCeM, ©Î¬O¿ï¾Ü±µ¦¬¦ó¤Hªº¤å³¹, ¨S¦³¤H±j­¢¥þ
+ ¥@¬ÉªººÞ²zªÌ³£­n°Ñ¥[, ¦]¬°³o¬O¨C­Ó¤Hªº¿ï¾Ü
+
+ ·íµM, ©Î³\¦³¨Ï¥ÎªÌ­n°Ý, ¦pªG§Úªºnews serverºÞ²zªÌ¥[¤JNoCeM, ¨º»ò
+ ¥L¤£´N¶¡±µªº¼vÅT¨ì§Ú©ÒŪªº¤å³¹?? ªº½T, ·íºÞ²zªÌ±µ¨ünotice§R°£ news
+ server ¤¤ªº¤å³¹«á, ¨Ï¥ÎªÌªº½TµLªk¦b³o­Óserver¤W¬Ý¨ì³o½g¤å³¹, ¤£¹L
+ ¨Ï¥ÎªÌÁÙ¬O¦³¥R¥÷ªº¿ï¾ÜÅv, ¥i¥H¿ï¾Ü§Oªºnews server¾\Ū, ¬Æ¦Ü,
+ ´«¤@­ÓISP!!
+
+4.NoCeMªº©µ¦ù
+
+ NoCeM notice¥i¥H¥Ñ¥ô¦ó¤Hµo¥X, ¤]¥i¥H³Q¥ô¦ó¤H±µ¦¬, ®Ú¾Ú³o¼Ëªº·§©À, ´Nµo
+ ®i¥Xpseudo moderator; ·í¬Y­Ó¨Ï¥ÎªÌ¹ï¬Y¨Ç¯S©wªº°Q½×°Ï¦³µÛ¸û¦hªº¸gÅç®É,
+ ¥i¥H¦Û§i¾Ä«iªº°e¥Xnotice, °w¹ï¯S©wªº°Q½×°Ï¤ºªº¤å³¹§@¤@¿z¿ï, «ØÄ³¨ä¥Lªº
+ ¨º´X½g¥i¥H¸Ô¥[¾\Ū, ¨º´X½g¤£­È±o¤@¬Ý, ¦Ó¨ä¥LªºÅªªÌ, ¦pªG¬Û«H¥Lªº¸gÅç©M
+ §PÂ_, ´N¥i¥H³]©w±µ¦¬¥Lªºnotice, ³o´N¬Opseudo moderator.
+
+ ¤@¯ëªºnewsgroup moderator²£¥Íªº¤èªk¸û¬°ÄYÂÔ, ©Ò¦³ªº¤å³¹¥²»Ý¸g¹Lmoderator
+ »{¦P«á, ¤~¯à°e¤Wnewsgroup, ¦]¦¹¨Ã¤£¬O©Ò¦³ªºnewsgroup³£¦³moderator¨Ó¬°¤å
+ ³¹«~½è§âÃö.
+
+ ¨Æ¹ê¤Wpseudo moderatorªºÆ[©À, ´N¹³¤@¯ëBBS¤¤¨C­ÓªOªºªO¥D¤@¼Ë, ¥u¤£¹L
+ pseudo moderator¨Ã¨S¦³Åv§Q§R°£¥ô¦ó¤å³¹, °£«DŪªÌ±µ¨ü¥L°e¥Xªºnotice, ¦]
+ ¦¹, ¸û¤£©öµo¥Í "ªO¥DºÞ²z¤£¤½" "ªO¥DÀݬå¤å³¹", ½Ñ¦p¦¹Ãþªº±¡§Î, ¥u­nŪ
+ ªÌÃhºÃ©Î¤£¯à±µ¨üpseudo moderatorªº§PÂ_, ÀH®É¥i¥H°±¤î±µ¨üpseudo moderator
+ °e¥Xªºnotice, ŪªÌ¥i¥H¦Û¥Ñªº°µ¥X¨M©w.
+
+5.NoCeMªº±À¼s»PÀ³¥Î
+
+ ¥Ø«eNoCeMªº¾÷¨î¤w¦³¤@©wªº³W¼Ò, ¦Ó¥B¦bUSENET¤W¥H±À¦æ¦³¤@¬q®É¶¡,
+ ¦ý¬OÀ³¥Îµ{¦¡ªº´¶¤Î¤~¯à¨Ï§ó¦h¨Ï¥ÎªÌ¨ü´f, À³¥Îµ{¦¡ªº³¡¥÷¥i¥H¤Àµ{¨â³¡¥÷
+
+¤@¯ë¨Ï¥ÎªÌ:
+
+ ¥Ø«e¥D­nÁÙ¬O´£¨Ñµ¹¨Ï¥Î.newsrcÀɪºnews readerµ{¦¡¨Ï¥Î, ¦ptin....
+ ¨ä¥L¹³¬Onetscape outlookµ¥, ¥Ø«e¦ü¥G¨S¦³¬Ý¨ì¬ÛÃöªº¤ä´©
+
+ °£¦¹¤§¥~, tw.* ¦³¤£¤Öªº¤å³¹¬O¥Ñ BBS °e¥X, ¤£¥i§_»{ªº BBS ¤]À³³Qµø¬°
+ ¨Ï¥ÎªÌµoªí¤å³¹ªººÞ¹D»P¤u¨ã, ¥¦©Ò±a¨Óªº§ïÅܤ]¼vÅT¤F¤£¤Ö¨Ï¥ÎªÌ°Ñ»P NEWS
+ °Q½×ªº²ßºD, ¥Ø«e NoCeM »P BBS(innbbsd) ªº¾ã¦X¤w¸g¦³¤Fªì¨Bªºµ²ªG,
+ Leeym ¤w¸g¨Ï±o innbbsd ¥i¥H¤ä´© NoCeM, ¨Ã¥B¥¿¦b´ú¸Õ, ¬° NoCeM »P BBS
+ ªºµ²¦X»P¶Ê¥Í½ñ¥X¤@¤j¨B
+
+news serverºÞ²zªÌ:
+
+ ³o¤è­±¬ÛÃöªºµ{¦¡¤w¸g»á¦h, ¤]¤ä´©¼ÆºØ¤£¦Pªºnews serverµ{¦¡, ¥Hinn¬°¨Ò
+ http://sites.inka.de/~bigred/sw/c-nocem.html
+ c-nocem¥i¤ä´©inn2.x¥H¤UªºªO¥»
+
+6.¨ä¥L
+
+ A.¥Ø«e NoCeM notice¤j³¡¥÷³£¬O³z¹L news ¶Ç°e, alt.nocem.misc¬O¥D­n¶Ç°e
+ ³o¨Çnoticeªºnewsgroup
+
+7.°Ñ¦Ò
+
+ http://www.cm.org
+ http://sites.inka.de/~bigred/sw/c-nocem.html
+ http://cae.ce.ntu.edu.tw/~leeym/nocem/ncm-innbbsd-latest.tgz
+ news:news.admin.nocem
+ news:alt.nocem.misc
+
+--
+¡° Origin: ºô»Ú·s¥@¬É ¡» From: 163.28.64.246
diff --git a/innbbsd/nocem/README.nocem b/innbbsd/nocem/README.nocem
new file mode 100644
index 0000000..a693acd
--- /dev/null
+++ b/innbbsd/nocem/README.nocem
@@ -0,0 +1,39 @@
+# Author: Yen-Ming Lee <leeym@cae.ce.ntu.edu.tw>
+# Start Date: Thu Feb 25 1999 +0800
+# Project: INNBBSD - NoCeM
+# File: nocem.c nocem.h
+#
+# Copyright: Copyright (c) 2000 by Yen-Ming Lee
+#
+# Permission to use, copy, modify, and distribute this
+# software for any purpose with or without fee is hereby
+# granted, provided that the above copyright notice and this
+# permission notice appear in all copies.
+
+
+ innbbsd ¥Ñ skhuang@csie.nctu.edu.tw ½s¼g
+ ¥»µ{¦¡ NoCeM-innbbsd ¥u¬O patch
+
+ ¥»µ{¦¡·PÁ clcheng@CCCA.NCTU.edu.tw ¤Î stevel@bbs.ee.ntu.edu.tw
+ ´£¨ÑÄ_¶Q·N¨£.
+
+ NoCeM ¤§³]©wÀɦb ~bbs/innd/ncmperm.bbs, ®æ¦¡¬° "Issuer Type Perm"
+ ¦UÄæ¦ì *¥²¶·* ¨Ï¥Î tab ¹j¶}. ²Ä¤@¦¸¸ü¤J®Éµ{¦¡·|¦Û°Ê²£¥Í³]©wÀÉ.
+
+ ¦pÄ@±µ¦¬¬Y¤@¦W Issuer ªº ncm-notice, ½Ð±N³Ì«á¤@Äæ no ±q§ï¬° yes
+ µM«á ctlinnbbsd reload ­«·s¸ü¤J¸ê®Æ§Y¥i.
+ *½Ð¤Å* ¨Ï¥Î ve ½s¿è ncmperm.bbs , ¦]¬° ve ·|±N tab ¥þ³¡Âର space
+
+ ¥»µ{¦¡»Ý°t¦X pgp5 ¨Ï¥Î, ¦w¸Ë NoCeM-innbbsd-patch «e, ½Ð¥ý¦w¸Ë pgp5
+ §_«h½Ð­×§ï nocem.c , #undef PGP5
+
+ pgp5 ªº¤¤¤å¤å¥ó¥i¦b©³¤U´X³B§ä¨ì.
+ http://bbs.ee.ntu.edu.tw/boards/BSD/4/62.html
+ http://www.sinica.edu.tw/cc/netsrv/dawei/pgp5.0/
+
+ NoCeM ¸ê®Æ½Ð°Ñ¦Ò http://www.cm.org
+ ¤Î benjamin.bbs@com.neto.net (clcheng) ©Ò¼gªº»¡©ú¤å¥ó.
+
+--
+ Yen-Ming Lee [§õ«Û©ú] | http://cae.ce.ntu.edu.tw/~leeym/
+ CAE Group, Civil Engineering, NTU, Taipei, Taiwan
diff --git a/innbbsd/rec_article.c b/innbbsd/rec_article.c
new file mode 100644
index 0000000..9853929
--- /dev/null
+++ b/innbbsd/rec_article.c
@@ -0,0 +1,522 @@
+/*-------------------------------------------------------*/
+/* rec_article.c( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : innbbsd receive article */
+/* create : 95/04/27 */
+/* update : / / */
+/* author : skhuang@csie.nctu.edu.tw */
+/* modify : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#if 0
+ ¦¬¨ì¤§¤å³¹¤º®e©MÀÉÀY¤À§O¦b
+ ¤º¤å (body) ¦b char *BODY
+ ÀÉÀY (header) ¦b char *SUBJECT, *FROM, *SITE, *DATE, *PATH, *GROUP, *MSGID, *POSTHOST, *CONTROL;
+#endif
+
+
+#include "innbbsconf.h"
+#include "bbslib.h"
+#include "inntobbs.h"
+
+
+/* ----------------------------------------------------- */
+/* board¡Gshm ³¡¥÷¶·»P cache.c ¬Û®e */
+/* ----------------------------------------------------- */
+
+
+static BCACHE *bshm;
+
+
+void
+init_bshm()
+{
+ /* itoc.030727: ¦b¶}±Ò bbsd ¤§«e¡AÀ³¸Ó´N­n°õ¦æ¹L account¡A
+ ©Ò¥H bshm À³¸Ó¤w³]©w¦n */
+
+ bshm = shm_new(BRDSHM_KEY, sizeof(BCACHE));
+
+ if (bshm->uptime <= 0) /* bshm ¥¼³]©w§¹¦¨ */
+ exit(0);
+}
+
+
+/* ----------------------------------------------------- */
+/* ³B²z DATE */
+/* ----------------------------------------------------- */
+
+
+#if 0 /* itoc.030303.µù¸Ñ: RFC 822 ªº DATE Äæ¦ì¡FRFC 1123 ±N year §ï¦¨ 4-DIGIT */
+
+date-time := [ wday "," ] date time ; dd mm yy, hh:mm:ss zzz
+wday := "Mon" / "Tue" / "Wed" / "Thu" / "Fri" / "Sat" / "Sun"
+date := 1*2DIGIT month 4DIGIT ; mday month year
+month := "Jan" / "Feb" / "Mar" / "Apr" / "May" / "Jun" / "Jul" / "Aug" / "Sep" / "Oct" / "Nov" / "Dec"
+time := hour zone ; ANSI and Military
+hour := 2DIGIT ":" 2DIGIT [":" 2DIGIT] ; 00:00:00 - 23:59:59
+zone := "UT" / "GMT" / "EST" / "EDT" / "CST" / "CDT" / "MST" / "MDT" / "PST" / "PDT" / 1ALPHA / ( ("+" / "-") 4DIGIT )
+
+#endif
+
+static time_t datevalue;
+
+static void
+parse_date() /* §â²Å¦X "dd mmm yyyy hh:mm:ss" ªº®æ¦¡¡AÂন time_t */
+{
+ static char months[12][4] = {"jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec"};
+ int i;
+ char *ptr, *str, buf[80];
+ struct tm ptime;
+
+ str_ncpy(buf, DATE, sizeof(buf));
+ str_lower(buf, buf); /* ³q³q´«¤p¼g¡A¦]¬° Dec DEC dec ¦UºØ³£¦³¤H¥Î */
+
+ str = buf + 2;
+ for (i = 0; i < 12; i++)
+ {
+ if (ptr = strstr(str, months[i]))
+ break;
+ }
+
+ if (ptr)
+ {
+ ptr[-1] = ptr[3] = ptr[8] = ptr[11] = ptr[14] = ptr[17] = '\0';
+
+ ptime.tm_sec = atoi(ptr + 15);
+ ptime.tm_min = atoi(ptr + 12);
+ ptime.tm_hour = atoi(ptr + 9);
+ ptime.tm_mday = (ptr == buf + 2 || ptr == buf + 7) ? atoi(ptr - 2) : atoi(ptr - 3); /* RFC 822 ¤¹³\ mday ¬O 1- ©Î 2- DIGIT */
+ ptime.tm_mon = i;
+ ptime.tm_year = atoi(ptr + 4) - 1900;
+ ptime.tm_isdst = 0;
+#ifndef CYGWIN
+ ptime.tm_zone = "GMT";
+ ptime.tm_gmtoff = 0;
+#endif
+
+ datevalue = mktime(&ptime);
+ str = ptr + 18;
+ if (ptr = strchr(str, '+'))
+ {
+ /* ¦pªG¦³ +0100 (MET) µ¥µù©ú®É°Ï¡A¥ý½Õ¦^ GMT ®É°Ï */
+ datevalue -= ((ptr[1] - '0') * 10 + (ptr[2] - '0')) * 3600 + ((ptr[3] - '0') * 10 + (ptr[4] - '0')) * 60;
+ }
+ else if (ptr = strchr(str, '-'))
+ {
+ /* ¦pªG¦³ -1000 (HST) µ¥µù©ú®É°Ï¡A¥ý½Õ¦^ GMT ®É°Ï */
+ datevalue += ((ptr[1] - '0') * 10 + (ptr[2] - '0')) * 3600 + ((ptr[3] - '0') * 10 + (ptr[4] - '0')) * 60;
+ }
+ datevalue += 28800; /* ¥xÆW©Ò¦bªº CST ®É°Ï¤ñ GMT §Ö¤K¤p®É */
+ }
+ else
+ {
+ /* ¦pªG¤ÀªR¥¢±Ñ¡A¨º»ò®³²{¦b®É¶¡¨Ó·íµo¤å®É¶¡ */
+ time(&datevalue);
+ /* bbslog("<rec_article> :Warn: parse_date ¿ù»~¡G%s\n", DATE); */
+ }
+}
+
+
+/* ----------------------------------------------------- */
+/* process post write */
+/* ----------------------------------------------------- */
+
+
+static void
+update_btime(brdname)
+ char *brdname;
+{
+ BRD *brdp, *bend;
+
+ brdp = bshm->bcache;
+ bend = brdp + bshm->number;
+ do
+ {
+ if (!strcmp(brdname, brdp->brdname))
+ {
+ brdp->btime = -1;
+ break;
+ }
+ } while (++brdp < bend);
+}
+
+
+static void
+bbspost_add(board, addr, nick)
+ char *board, *addr, *nick;
+{
+ int cc;
+ char *str;
+ char folder[64], fpath[64];
+ HDR hdr;
+ FILE *fp;
+
+ /* ¼g¤J¤å³¹¤º®e */
+
+ brd_fpath(folder, board, FN_DIR);
+
+ if (fp = fdopen(hdr_stamp(folder, 'A', &hdr, fpath), "w"))
+ {
+ fprintf(fp, "µo«H¤H: %.50s ¬ÝªO: %s\n", FROM, board);
+ fprintf(fp, "¼Ð ÃD: %.70s\n", SUBJECT);
+ if (SITE)
+ fprintf(fp, "µo«H¯¸: %.27s (%.40s)\n\n", SITE, DATE);
+ else
+ fprintf(fp, "µo«H¯¸: %.40s\n\n", DATE);
+
+ /* chuan: header ¸ò body ­nªÅ¦æ¹j¶} */
+
+ /* fprintf(fp, "%s", BODY); */
+
+ for (str = BODY; cc = *str; str++)
+ {
+ if (cc == '.')
+ {
+ /* for line beginning with a period, collapse the doubled period to a single one. */
+ if (str >= BODY + 2 && str[-1] == '.' && str[-2] == '\n')
+ continue;
+ }
+
+ fputc(cc, fp);
+ }
+
+ fclose(fp);
+ }
+
+ /* ³y HDR */
+
+ hdr.xmode = POST_INCOME;
+
+ /* Thor.980825: ¨¾¤î¦r¦ê¤Óªø»\¹LÀY */
+ str_ncpy(hdr.owner, addr, sizeof(hdr.owner));
+ str_ncpy(hdr.nick, nick, sizeof(hdr.nick));
+ str_stamp(hdr.date, &datevalue); /* ¨Ì DATE: Äæ¦ìªº¤é´Á¡A»P hdr.chrono ¤£¦P¨B */
+ str_ncpy(hdr.title, SUBJECT, sizeof(hdr.title));
+
+ rec_bot(folder, &hdr, sizeof(HDR));
+
+ update_btime(board);
+
+ HISadd(MSGID, board, hdr.xname);
+}
+
+
+/* ----------------------------------------------------- */
+/* process cancel write */
+/* ----------------------------------------------------- */
+
+
+#ifdef _KEEP_CANCEL_
+static inline void
+move_post(hdr, board, filename)
+ HDR *hdr;
+ char *board, *filename;
+{
+ HDR post;
+ char folder[64];
+
+ brd_fpath(folder, board, FN_DIR);
+ hdr_stamp(folder, HDR_LINK | 'A', &post, filename);
+ unlink(filename);
+
+ /* ª½±µ½Æ»s trailing data */
+
+ memcpy(post.owner, hdr->owner, TTLEN + 140);
+
+ sprintf(post.title, "[cancel] %-60.60s", FROM);
+
+ rec_bot(folder, &post, sizeof(HDR));
+}
+#endif
+
+
+static void
+bbspost_cancel(board, chrono, fpath)
+ char *board;
+ time_t chrono;
+ char *fpath;
+{
+ HDR hdr;
+ struct stat st;
+ long size;
+ int fd, ent;
+ char folder[64];
+ off_t off, len;
+
+ /* XLOG("cancel [%s] %d\n", board, time); */
+
+ brd_fpath(folder, board, FN_DIR);
+ if ((fd = open(folder, O_RDWR)) == -1)
+ return;
+
+ /* flock(fd, LOCK_EX); */
+ /* Thor.981205: ¥Î fcntl ¨ú¥Nflock, POSIX¼Ð·Ç¥Îªk */
+ f_exlock(fd);
+
+ fstat(fd, &st);
+ size = sizeof(HDR);
+ ent = ((long) st.st_size) / size;
+
+ /* itoc.030307.µù¸Ñ: ¥h .DIR ¤¤ÂǥѤñ¹ï chrono §ä¥X¬O­þ¤@½g */
+
+ while (1)
+ {
+ /* itoc.030307.µù¸Ñ: ¨C 16 ½g¬°¤@­Ó block */
+ ent -= 16;
+ if (ent <= 0)
+ break;
+
+ lseek(fd, size * ent, SEEK_SET);
+ if (read(fd, &hdr, size) != size)
+ break;
+
+ if (hdr.chrono <= chrono) /* ¸¨¦b³o­Ó block ¸Ì */
+ {
+ do
+ {
+ if (hdr.chrono == chrono)
+ {
+ /* Thor.981014: mark ªº¤å³¹¤£³Q cancel */
+ if (hdr.xmode & POST_MARKED)
+ break;
+
+#ifdef _KEEP_CANCEL_
+ /* itoc.030613: «O¯d³Q cancel ªº¤å³¹©ó [deleted] */
+ move_post(&hdr, BN_DELETED, fpath);
+#else
+ unlink(fpath);
+#endif
+
+ update_btime(board);
+
+ /* itoc.030307: ³Q cancel ªº¤å³¹¤£«O¯d header */
+
+ off = lseek(fd, 0, SEEK_CUR);
+ len = st.st_size - off;
+
+ board = (char *) malloc(len);
+ read(fd, board, len);
+
+ lseek(fd, off - size, SEEK_SET);
+ write(fd, board, len);
+ ftruncate(fd, st.st_size - size);
+
+ free(board);
+ break;
+ }
+
+ if (hdr.chrono > chrono)
+ break;
+ } while (read(fd, &hdr, size) == size);
+
+ break;
+ }
+ }
+
+ /* flock(fd, LOCK_UN); */
+ /* Thor.981205: ¥Î fcntl ¨ú¥Nflock, POSIX¼Ð·Ç¥Îªk */
+ f_unlock(fd);
+
+ close(fd);
+ return;
+}
+
+
+int /* 0:cancel success -1:cancel fail */
+cancel_article(msgid)
+ char *msgid;
+{
+ int fd;
+ char fpath[64], cancelfrom[128], buffer[128];
+ char board[BNLEN + 1], xname[9];
+
+ /* XLOG("cancel %s <%s>\n", FROM, msgid); */
+
+ if (!HISfetch(msgid, board, xname))
+ return -1;
+
+ str_from(FROM, cancelfrom, buffer);
+
+ /* XLOG("cancel %s (%s)\n", cancelfrom, buffer); */
+
+ sprintf(fpath, "brd/%s/%c/%s", board, xname[7], xname); /* ¥h§ä¥X¨º½g¤å³¹ */
+
+ /* XLOG("cancel fpath (%s)\n", fpath); */
+
+ if ((fd = open(fpath, O_RDONLY)) >= 0)
+ {
+ int len;
+
+ len = read(fd, buffer, sizeof(buffer));
+ close(fd);
+
+ /* Thor.981221.µù¸Ñ: ¥~¨Ó¤å³¹¤~¯à³Q cancel */
+ if ((len > 10) && !memcmp(buffer, "µo«H¤H: ", 8))
+ {
+ char *xfrom, *str;
+
+ xfrom = buffer + 8;
+ if (str = strchr(xfrom, ' '))
+ {
+ *str = '\0';
+
+#ifdef _NoCeM_
+ /* gslin.000607: ncm_issuer ¥i¥H¬å§O¯¸µoªº«H */
+ if (strcmp(xfrom, cancelfrom) && !search_issuer(FROM, NULL))
+#else
+ if (strcmp(xfrom, cancelfrom))
+#endif
+ {
+ /* itoc.030107.µù¸Ñ: ­Y cancelfrom ©M¥»¦a¤å³¹ header °O¿ýªº xfrom ¤£¦P¡A´N¬O fake cancel */
+ bbslog("<rec_article> :Warn: µL®Äªº cancel¡G%s, sender: %s, path: %s\n", xfrom, FROM, PATH);
+ return -1;
+ }
+
+ bbspost_cancel(board, chrono32(xname), fpath);
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+/* ----------------------------------------------------- */
+/* check spam rule */
+/* ----------------------------------------------------- */
+
+
+static int /* 1: ²Å¦X¾×«H³W«h */
+is_spam(board, addr, nick)
+ char *board, *addr, *nick;
+{
+ spamrule_t *spam;
+ int i, xmode;
+ char *compare, *detail;
+
+ for (i = 0; i < SPAMCOUNT; i++)
+ {
+ spam = SPAMRULE + i;
+
+ compare = spam->path;
+ if (*compare && strcmp(compare, NODENAME))
+ continue;
+
+ compare = spam->board;
+ if (*compare && strcmp(compare, board))
+ continue;
+
+ xmode = spam->xmode;
+ detail = spam->detail;
+
+ if (xmode & INN_SPAMADDR)
+ compare = addr;
+ else if (xmode & INN_SPAMNICK)
+ compare = nick;
+ else if (xmode & INN_SPAMSUBJECT)
+ compare = SUBJECT;
+ else if (xmode & INN_SPAMPATH)
+ compare = PATH;
+ else if (xmode & INN_SPAMMSGID)
+ compare = MSGID;
+ else if (xmode & INN_SPAMBODY)
+ compare = BODY;
+ else if (xmode & INN_SPAMSITE && SITE) /* SITE ¥i¥H¬O NULL */
+ compare = SITE;
+ else if (xmode & INN_SPAMPOSTHOST && POSTHOST) /* POSTHOST ¥i¥H¬O NULL */
+ compare = POSTHOST;
+ else
+ continue;
+
+ if (str_sub(compare, detail))
+ return 1;
+ }
+ return 0;
+}
+
+
+/* ----------------------------------------------------- */
+/* process receive article */
+/* ----------------------------------------------------- */
+
+
+#ifndef _NoCeM_
+static
+#endif
+newsfeeds_t *
+search_newsfeeds_bygroup(newsgroup)
+ char *newsgroup;
+{
+ newsfeeds_t nf, *find;
+
+ str_ncpy(nf.newsgroup, newsgroup, sizeof(nf.newsgroup));
+ find = bsearch(&nf, NEWSFEEDS_G, NFCOUNT, sizeof(newsfeeds_t), nf_bygroupcmp);
+ if (find && !(find->xmode & INN_NOINCOME))
+ return find;
+ return NULL;
+}
+
+
+int /* 0:success -1:fail */
+receive_article()
+{
+ newsfeeds_t *nf;
+ char myaddr[128], mynick[128], mysubject[128], myfrom[128], mydate[80];
+ char poolx[256];
+ char *group;
+ int firstboard = 1;
+
+ /* try to split newsgroups into separate group */
+ for (group = strtok(GROUP, ","); group; group = strtok(NULL, ","))
+ {
+ if (!(nf = search_newsfeeds_bygroup(group)))
+ continue;
+
+ if (firstboard) /* opus: ²Ä¤@­ÓªO¤~»Ý­n³B²z */
+ {
+ /* Thor.980825: gc patch: lib/str_decode ¥u¯à±µ¨ü decode §¹ strlen < 256 */
+
+ str_ncpy(poolx, SUBJECT, 255);
+ str_decode(poolx);
+ str_ansi(mysubject, poolx, 70); /* 70 ¬O bbspost_add() ¼ÐÃD©Ò»Ýªºªø«× */
+ SUBJECT = mysubject;
+
+ str_ncpy(poolx, FROM, 255);
+ str_decode(poolx);
+ str_ansi(myfrom, poolx, 128); /* ÁöµM bbspost_add() µo«H¤H©Ò»Ýªºªø«×¥u»Ý­n 50¡A¦ý¬O str_from() »Ý­nªø¤@¨Ç */
+ FROM = myfrom;
+
+ /* itoc.030218.µù¸Ñ: ³B²z¡uµo«H¯¸¡v¤¤ªº®É¶¡ */
+ parse_date();
+ strcpy(mydate, (char *) Btime(&datevalue));
+ DATE = mydate;
+
+ if (*nf->charset == 'g')
+ {
+ gb2b5(BODY);
+ gb2b5(FROM);
+ gb2b5(SUBJECT);
+ if (SITE)
+ gb2b5(SITE);
+ }
+
+ strcpy(poolx, FROM);
+ str_from(poolx, myaddr, mynick);
+
+ if (is_spam(nf->board, myaddr, mynick))
+ {
+#ifdef _KEEP_CANCEL_
+ bbspost_add(BN_DELETED, myaddr, mynick);
+#endif
+ break;
+ }
+
+ firstboard = 0;
+ }
+
+ bbspost_add(nf->board, myaddr, mynick);
+ } /* for board1,board2,... */
+
+ return 0;
+}
diff --git a/lib/Makefile b/lib/Makefile
new file mode 100644
index 0000000..e5a0340
--- /dev/null
+++ b/lib/Makefile
@@ -0,0 +1,92 @@
+# ------------------------------------------------------ #
+# lib/Makefile ( NTHU CS MapleBBS Ver 3.10 ) #
+# ------------------------------------------------------ #
+# author : opus.bbs@bbs.cs.nthu.edu.tw #
+# target : Makefile for MapleBBS library routines #
+# create : 95/03/29 #
+# update : 95/12/15 #
+# ------------------------------------------------------ #
+
+
+CC = gcc
+RANLIB = ranlib
+CPROTO = cproto -E"gcc -pipe -E" -I../include # -s -v
+CFLAGS = -O2 -s -pipe -fomit-frame-pointer -Wunused -I../include
+
+
+# ------------------------------------------------------ #
+# ¤U¦Cªº make rules ¤£»Ý­×§ï #
+# ------------------------------------------------------ #
+
+
+SRC = \
+ is_alnum.c is_alpha.c not_addr.c \
+ \
+ dns.c dns_aton.c dns_name.c dns_open.c dns_smtp.c \
+ \
+ str_ansi.c str_cat.c str_cmp.c str_decode.c str_folder.c str_fpath.c \
+ str_from.c str_has.c str_hash.c str_lower.c str_lowest.c str_ncmp.c \
+ str_ncpy.c str_passwd.c str_stamp.c str_str.c str_sub.c str_tail.c \
+ str_time.c str_trim.c str_ttl.c \
+ \
+ archiv32.c chrono32.c hash32.c radix32.c \
+ \
+ f_cat.c f_cp.c f_img.c f_ln.c f_lock.c f_map.c f_mode.c f_mv.c f_new.c \
+ f_path.c f_rm.c f_suck.c mak_dirs.c \
+ \
+ rec_add.c rec_bot.c rec_num.c rec_del.c rec_get.c rec_ins.c rec_put.c \
+ rec_ref.c rec_sync.c \
+ \
+ hdr_fpath.c hdr_stamp.c \
+ \
+ shm.c \
+ \
+ splay.c \
+ \
+ acl_has.c rfc2047.c xwrite.c \
+ \
+ dl_lib.c
+
+OBJ = \
+ is_alnum.o is_alpha.o not_addr.o \
+ \
+ dns.o dns_aton.o dns_name.o dns_open.o dns_smtp.o \
+ \
+ str_ansi.o str_cat.o str_cmp.o str_decode.o str_folder.o str_fpath.o \
+ str_from.o str_has.o str_hash.o str_lower.o str_lowest.o str_ncmp.o \
+ str_ncpy.o str_passwd.o str_stamp.o str_str.o str_sub.o str_tail.o \
+ str_time.o str_trim.o str_ttl.o \
+ \
+ archiv32.o chrono32.o hash32.o radix32.o \
+ \
+ f_cat.o f_cp.o f_img.o f_ln.o f_lock.o f_map.o f_mode.o f_mv.o f_new.o \
+ f_path.o f_rm.o f_suck.o mak_dirs.o \
+ \
+ rec_add.o rec_bot.o rec_num.o rec_del.o rec_get.o rec_ins.o rec_put.o \
+ rec_ref.o rec_sync.o \
+ \
+ hdr_fpath.o hdr_stamp.o \
+ \
+ shm.o \
+ \
+ splay.o \
+ \
+ acl_has.o rfc2047.o xwrite.o \
+ \
+ dl_lib.o
+
+
+.c.o: ; $(CC) $(CFLAGS) -c $*.c
+
+
+all: libdao.a
+
+dao.p: $(SRC)
+ $(CPROTO) -o $@ $?
+
+libdao.a: $(OBJ)
+ ar rv $@ $?
+ $(RANLIB) $@
+
+clean:
+ rm -f libdao.a *.o *~
diff --git a/lib/acl_has.c b/lib/acl_has.c
new file mode 100644
index 0000000..c9e2c3e
--- /dev/null
+++ b/lib/acl_has.c
@@ -0,0 +1,102 @@
+/*-------------------------------------------------------*/
+/* lib/acl_has.c ( NTHU CS MapleBBS Ver 3.00 ) */
+/*-------------------------------------------------------*/
+/* target : Access Control List */
+/* create : 98/03/20 */
+/* update : 98/03/29 */
+/*-------------------------------------------------------*/
+
+
+#include <stdio.h>
+
+
+/* ----------------------------------------------------- */
+/* ACL config file format */
+/* ----------------------------------------------------- */
+/* user: majordomo@* bad@cs.nthu.edu.tw */
+/* ^ Thor.980825: À³¬°ªÅ¥Õ */
+/* host: cs.nthu.edu.tw 140.114.77.1 */
+/* subnet: .nthu.edu.tw 140.114.77. */
+/* ----------------------------------------------------- */
+
+
+/* return -1 : ACL file ¤£¦s¦b */
+/* return 0 : ACL ¤£¥]§t¸Ó pattern */
+/* return 1 : ACL ²Å¦X¸Ó pattern */
+
+
+int
+acl_has(acl, user, host)
+ char *acl; /* file name of access control list */
+ char *user; /* lower-case string */
+ char *host; /* lower-case string */
+{
+ int i, cc, luser, lhost;
+ FILE *fp;
+ char filter[256], *addr, *str;
+
+ if (!(fp = fopen(acl, "r")))
+ return -1;
+
+ i = 0;
+ luser = strlen(user); /* length of user name */
+ lhost = strlen(host); /* length of host name */
+
+ while (fgets(filter, sizeof(filter), fp))
+ {
+ addr = NULL;
+
+ for (str = filter; (cc = *str) > ' '; str++)
+ { /* Thor.980825: µù¸Ñ: ¹J¨ì ªÅ¥Õ ´Nºâ¦¹¦æµ²§ô */
+ if (cc == '@')
+ addr = str;
+ }
+
+ if (str == filter) /* empty line */
+ continue;
+
+ *str = '\0'; /* Thor.980825: µù¸Ñ: ±Nµ²§ô³B¶ñ0, §K¥ÍªK¸` */
+ str_lower(filter, filter); /* lkchu.981201: lower-case string */
+
+ if (addr) /* match user name */
+ {
+ if ((luser != addr - filter) || memcmp(user, filter, luser))
+ continue;
+
+ if (!*++addr)
+ {
+ i = 1;
+ break;
+ }
+ }
+ else
+ {
+ addr = filter;
+ }
+
+ /* match host name */
+
+ cc = str - addr;
+
+ if (cc > lhost)
+ continue;
+
+ if (cc == lhost)
+ {
+ if (memcmp(addr, host, lhost))
+ continue;
+ }
+ else
+ {
+ if (((*addr != '.') || memcmp(addr, host + lhost - cc, cc)) &&
+ ((addr[cc - 1] != '.') || memcmp(addr, host, cc)))
+ continue;
+ }
+
+ i = 1;
+ break;
+ }
+
+ fclose(fp);
+ return i;
+}
diff --git a/lib/archiv32.c b/lib/archiv32.c
new file mode 100644
index 0000000..7f56ff4
--- /dev/null
+++ b/lib/archiv32.c
@@ -0,0 +1,41 @@
+#include "dao.h"
+
+
+void
+archiv32(chrono, fname)
+ time_t chrono; /* 32 bits */
+ char *fname; /* 7 chars */
+{
+ char *str;
+
+ str = fname + 7;
+ *str = '\0';
+ for (;;)
+ {
+ *(--str) = radix32[chrono & 31];
+ if (str == fname)
+ return;
+ chrono >>= 5;
+ }
+}
+
+
+#if 0
+void
+archiv32m(chrono, fname)
+ time_t chrono; /* 32 bits */
+ char *fname; /* 7 chars */
+{
+ char *str;
+
+ str = fname + 8;
+ *str = '\0';
+ for (;;)
+ {
+ *(--str) = radix32[chrono & 31];
+ if (str == fname)
+ return;
+ chrono >>= 5;
+ }
+}
+#endif
diff --git a/lib/chrono32.c b/lib/chrono32.c
new file mode 100644
index 0000000..fe7fd34
--- /dev/null
+++ b/lib/chrono32.c
@@ -0,0 +1,18 @@
+#include "dao.h"
+
+
+time_t
+chrono32(str)
+ char *str; /* M0123456 */
+{
+ time_t chrono;
+ int ch;
+
+ chrono = 0;
+ while (ch = *++str)
+ {
+ ch -= (ch > '9') ? 'A' - 10 : '0';
+ chrono = (chrono << 5) + ch;
+ }
+ return chrono;
+}
diff --git a/lib/dao.p b/lib/dao.p
new file mode 100644
index 0000000..f9237de
--- /dev/null
+++ b/lib/dao.p
@@ -0,0 +1,151 @@
+/* is_alnum.c */
+int is_alnum(int ch);
+/* is_alpha.c */
+int is_alpha(int ch);
+/* not_addr.c */
+int not_addr(char *addr);
+
+#ifdef _RESOLV_H
+/* dns.c */
+void dns_init(void);
+/* dns_aton.c */
+unsigned long dns_aton(char *name);
+int dns_query(char *name, int qtype, querybuf *ans);
+/* dns_name.c */
+int dns_name(unsigned char *addr, char *name);
+/* dns_open.c */
+int dns_open(char *host, int port);
+/* dns_smtp.c */
+int dns_mx(char *domain, char *mxlist);
+int dns_smtp(char *host);
+#endif
+
+/* str_ansi.c */
+void str_ansi(char *dst, char *str, int max);
+/* str_cat.c */
+void str_cat(char *dst, char *s1, char *s2);
+/* str_cmp.c */
+int str_cmp(char *s1, char *s2);
+/* str_decode.c */
+char *mm_getencode(unsigned char *str, char *code);
+void mm_getcharset(const char *str, char *charset, int size);
+int mmdecode(unsigned char *src, int encode, unsigned char *dst);
+void str_decode(unsigned char *str);
+/* str_folder.c */
+void str_folder(char *fpath, char *folder, char *fname);
+/* str_fpath.c */
+void setdirpath(char *fpath, char *direct, char *fname);
+/* str_from.c */
+int str_from(char *from, char *addr, char *nick);
+/* str_has.c */
+int str_has(char *list, char *tag, int len);
+/* str_hash.c */
+int str_hash(char *str, int seed);
+/* str_len.c */
+int str_len(char *str);
+/* str_lower.c */
+void str_lower(char *dst, char *src);
+/* str_lowest.c */
+void str_lowest(char *dst, char *src);
+/* str_ncmp.c */
+int str_ncmp(char *s1, char *s2, int n);
+/* str_ncpy.c */
+void str_ncpy(char *dst, char *src, int n);
+/* str_passwd.c */
+char *genpasswd(char *pw);
+int chkpasswd(char *passwd, char *test);
+/* str_stamp.c */
+void str_stamp(char *str, time_t *chrono);
+/* str_str.c */
+char *str_str(char *str, char *tag);
+/* str_sub.c */
+char *str_sub(char *str, char *tag);
+/* str_tail.c */
+char *str_tail(char *str);
+/* str_time.c */
+char *Atime(time_t *clock);
+char *Btime(time_t *clock);
+char *Now(void);
+/* str_trim.c */
+void str_trim(char *buf);
+/* str_ttl.c */
+char *str_ttl(char *title);
+
+/* archiv32.c */
+void archiv32(time_t chrono, char *fname);
+/* chrono32.c */
+time_t chrono32(char *str);
+/* hash32.c */
+int hash32(unsigned char *str);
+/* radix32.c */
+
+/* f_cat.c */
+void f_cat(char *fpath, char *msg);
+/* f_cp.c */
+int f_cp(char *src, char *dst, int mode);
+/* f_img.c */
+char *f_img(char *fpath, int *fsize);
+/* f_ln.c */
+int f_ln(char *src, char *dst);
+/* f_lock.c */
+int f_exlock(int fd);
+int f_unlock(int fd);
+/* f_map.c */
+char *f_map(char *fpath, int *fsize);
+/* f_mode.c */
+int f_mode(char *fpath);
+/* f_mv.c */
+int f_mv(char *src, char *dst);
+/* f_new.c */
+FILE *f_new(char *fold, char *fnew);
+/* f_path.c */
+void brd_fpath(char *fpath, char *board, char *fname);
+void gem_fpath(char *fpath, char *board, char *fname);
+void usr_fpath(char *fpath, char *user, char *fname);
+/* f_rm.c */
+int f_rm(char *fpath);
+/* f_suck.c */
+void f_suck(FILE *fp, char *fpath);
+/* mak_dirs.c */
+void mak_dirs(char *fpath);
+void mak_links(char *fpath);
+
+/* rec_add.c */
+int rec_add(char *fpath, void *data, int size);
+/* rec_bot.c */
+int rec_bot(char *fpath, void *data, int size);
+/* rec_num.c */
+int rec_num(char *fpath, int size);
+/* rec_del.c */
+int rec_del(char *fpath, int size, int pos, int (*fchk)());
+/* rec_get.c */
+int rec_get(char *fpath, void *data, int size, int pos);
+/* rec_ins.c */
+int rec_ins(char *fpath, void *data, int size, int pos, int num);
+/* rec_put.c */
+int rec_put(char *fpath, void *data, int size, int pos, int (*fchk)());
+/* rec_ref.c */
+int rec_ref(char *fpath, void *data, int size, int pos, int (*fchk)(), void (*fref)());
+/* rec_sync.c */
+int rec_sync(char *fpath, int size, int (*fsync)(), int (*fchk)());
+
+/* hdr_fpath.c */
+void hdr_fpath(char *fpath, char *folder, HDR *hdr);
+/* hdr_stamp.c */
+int hdr_stamp(char *folder, int token, HDR *hdr, char *fpath);
+
+/* shm.c */
+void *shm_new(int shmkey, int shmsize);
+
+/* splay.c */
+SplayNode *splay_in(SplayNode *top, void *data, int (*compare)());
+
+/* acl_has.c */
+int acl_has(char *acl, char *user, char *host);
+/* rfc2047.c */
+void output_rfc2047_qp(FILE *fp, char *prefix, char *str, char *charset, char *suffix);
+/* xwrite.c */
+int xwrite(int fd, char *data, int size);
+
+/* dl_lib.c */
+void *DL_get(char *name);
diff --git a/lib/dl_lib.c b/lib/dl_lib.c
new file mode 100644
index 0000000..2a203b9
--- /dev/null
+++ b/lib/dl_lib.c
@@ -0,0 +1,114 @@
+/* ----------------------------------------------------- */
+/* author : thor.bbs@bbs.cs.nthu.edu.tw */
+/* target : dynamic link modules library for maple bbs */
+/* create : 99/02/14 */
+/* update : / / */
+/* ----------------------------------------------------- */
+
+
+#include <dlfcn.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+
+typedef struct
+{
+ char *path;
+ void *handle;
+} DL_list;
+
+
+static DL_list *dl_pool;
+static int dl_size, dl_head;
+
+#define DL_ALLOC_MIN 5
+
+
+void *
+DL_get(name)
+ char *name; /* format: "Xmodule_path:Xname" */
+{
+ char buf[512], *t;
+ DL_list *p, *tail;
+
+ strcpy(buf, name);
+ if (!(t = (char *)strchr(buf, ':')))
+ return NULL;
+
+ *t++ = 0;
+
+ if (!dl_pool)
+ {
+ /* Initialize DL entries */
+ dl_size = DL_ALLOC_MIN;
+ dl_head = 0;
+ dl_pool = (DL_list *) malloc(dl_size * sizeof(DL_list));
+ if (!dl_pool)
+ exit(1); /* Thor.991121: out of space */
+ }
+
+ p = dl_pool;
+ tail = p + dl_head;
+ while (p < tail)
+ {
+ if (!strcmp(buf, p->path))
+ break;
+ p++;
+ }
+
+ if (p >= tail)
+ { /* not found */
+ if (dl_head >= dl_size)
+ { /* not enough space */
+ dl_size += DL_ALLOC_MIN;
+ dl_pool = (DL_list *) realloc(dl_pool, dl_size * sizeof(DL_list));
+ if (!dl_pool)
+ exit(1); /* Thor.991121: out of space */
+ p = dl_pool + dl_head; /* Thor.991121: to a new place */
+ }
+
+#if defined(__OpenBSD__)
+ p->handle = dlopen(p->path = (char *)strdup(buf), RTLD_LAZY);
+#else
+ p->handle = dlopen(p->path = (char *)strdup(buf), RTLD_NOW);
+#endif
+
+ dl_head++;
+ }
+
+ if (!p->handle)
+ return NULL;
+
+#if defined(DLSYM_NEEDS_UNDERSCORE)
+ char *symbol = (char *) malloc(sizeof(char) * (strlen(t) + 2));
+ void *retval;
+ sprintf(symbol, "_%s", t);
+ retval = dlsym(p->handle, symbol);
+ free(symbol);
+ return retval;
+#else
+ return dlsym(p->handle, t);
+#endif
+}
+
+
+int
+DL_func(char *name, ...)
+{
+ va_list args;
+ int (*f) (), ret;
+
+ va_start(args, name);
+
+ if (!(f = DL_get(name)))
+ { /* not get func */
+ return -1;
+ }
+
+ ret = (*f) (args);
+ va_end(args);
+
+ return ret;
+}
diff --git a/lib/dns.c b/lib/dns.c
new file mode 100644
index 0000000..eed724f
--- /dev/null
+++ b/lib/dns.c
@@ -0,0 +1,45 @@
+/*-------------------------------------------------------*/
+/* lib/dns.c ( NTHU CS MapleBBS Ver 3.00 ) */
+/*-------------------------------------------------------*/
+/* target : included C file for DNS routines */
+/* create : 96/11/20 */
+/* update : 96/12/15 */
+/*-------------------------------------------------------*/
+
+
+#include "dns.h"
+
+
+void
+dns_init()
+{
+ res_init();
+ /* _res.retrans = 5; */ /* DNS query timeout */
+ _res.retry = 2;
+ /* _res.options |= RES_USEVC; */ /* Thor.001228: ¥ý¤£¥Î TCP, ©È¥d¦í */
+}
+
+
+int
+dns_query(name, qtype, ans)
+ char *name; /* domain name */
+ int qtype; /* type of query */
+ querybuf *ans; /* buffer to put answer */
+{
+ querybuf buf;
+
+ qtype = res_mkquery(QUERY, name, C_IN, qtype, (char *) NULL, 0, NULL,
+ (char *) &buf, sizeof(buf));
+
+ if (qtype >= 0)
+ {
+ qtype = res_send((char *) &buf, qtype, (char *) ans, sizeof(querybuf));
+
+ /* avoid problems after truncation in tcp packets */
+
+ if (qtype > sizeof(querybuf))
+ qtype = sizeof(querybuf);
+ }
+
+ return qtype;
+}
diff --git a/lib/dns.p b/lib/dns.p
new file mode 100644
index 0000000..824ee6b
--- /dev/null
+++ b/lib/dns.p
@@ -0,0 +1,14 @@
+/* dns.c */
+void dns_init(void);
+/* dns_aton.c */
+unsigned long dns_aton(char *name);
+int dns_query(char *name, int qtype, querybuf *ans);
+/* dns_ident.c */
+void dns_ident(int sock, struct sockaddr_in *from, char *rhost, char *ruser);
+/* dns_name.c */
+int dns_name(unsigned char *addr, char *name);
+/* dns_open.c */
+int dns_open(char *host, int port);
+/* dns_smtp.c */
+int dns_mx(char *domain, char *mxlist);
+int dns_smtp(char *host);
diff --git a/lib/dns_aton.c b/lib/dns_aton.c
new file mode 100644
index 0000000..b4355bf
--- /dev/null
+++ b/lib/dns_aton.c
@@ -0,0 +1,149 @@
+/*-------------------------------------------------------*/
+/* lib/dns_aton.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : included C file for DNS routines */
+/* create : 96/11/20 */
+/* update : 99/08/11 */
+/*-------------------------------------------------------*/
+
+
+#include "dns.h"
+
+
+/* ----------------------------------------------------- */
+/* get IP address by address string */
+/* ----------------------------------------------------- */
+
+/* Thor.990811: §ì¥Xip addr */
+
+unsigned long
+dns_aton(name)
+ char *name;
+{
+ ip_addr addr;
+ u_char *cp;
+ int cc, cd;
+
+ /* disallow names consisting only of digits/dots, unless they end in a dot. */
+
+ cc = name[0];
+ if (cc >= '0' && cc <= '9')
+ {
+ addr.addr = 0; cd = 0;
+
+ for (cp = name;; ++cp)
+ {
+ cc = *cp;
+ if (!cc)
+ {
+ /*
+ * All-numeric, no dot at the end. Fake up a hostent as if we'd
+ * actually done a lookup. What if someone types 255.255.255.255?
+ * The test below will succeed spuriously... ???
+ */
+
+ if (*--cp == '.' || cd != 3)
+ return INADDR_NONE;
+
+ return addr.addr;
+ }
+ else if(cc >= '0' && cc <= '9')
+ {
+ addr.d[cd] *= 10;
+ addr.d[cd] += cc - '0';
+ }
+ else if(cc == '.')
+ {
+ if( ++cd >= 4)
+ break;
+ }
+ else
+ break;
+ }
+ }
+ return INADDR_NONE;
+}
+
+#if 0
+/* ----------------------------------------------------- */
+/* get IP address by host name */
+/* ----------------------------------------------------- */
+
+
+unsigned long
+dns_addr(name)
+ char *name;
+{
+ ip_addr addr;
+ u_char *cp, *eom;
+ int cc, n, type, ancount, qdcount;
+ querybuf ans;
+ char hostbuf[MAXDNAME];
+
+ /* disallow names consisting only of digits/dots, unless they end in a dot. */
+
+ cc = name[0];
+ if (cc >= '0' && cc <= '9')
+ {
+ for (cp = name;; ++cp)
+ {
+ cc = *cp;
+ if (!cc)
+ {
+ /*
+ * All-numeric, no dot at the end. Fake up a hostent as if we'd
+ * actually done a lookup. What if someone types 255.255.255.255?
+ * The test below will succeed spuriously... ???
+ */
+
+ if (*--cp == '.')
+ break;
+
+ return inet_addr(name);
+ }
+ if ((cc < '0' || cc > '9') && cc != '.')
+ break;
+ }
+ }
+
+ n = dns_query(name, T_A, &ans);
+ if (n < 0)
+ return INADDR_NONE;
+
+ /* find first satisfactory answer */
+
+ cp = (u_char *) & ans + sizeof(HEADER);
+ eom = (u_char *) & ans + n;
+
+ for (qdcount = ntohs(ans.hdr.qdcount); qdcount--; cp += n + QFIXEDSZ)
+ {
+ if ((n = dn_skipname(cp, eom)) < 0)
+ return INADDR_NONE;
+ }
+
+ ancount = ntohs(ans.hdr.ancount);
+ while (--ancount >= 0 && cp < eom)
+ {
+ if ((n = dn_expand((u_char *) &ans, eom, cp, hostbuf, MAXDNAME)) < 0)
+ return INADDR_NONE;
+
+ cp += n;
+ type = getshort(cp);
+ n = getshort(cp + 8);
+ cp += 10;
+
+ if (type == T_A)
+ {
+
+ addr.d[0] = cp[0];
+ addr.d[1] = cp[1];
+ addr.d[2] = cp[2];
+ addr.d[3] = cp[3];
+ return addr.addr;
+ }
+
+ cp += n;
+ }
+ return INADDR_NONE;
+}
+#endif
diff --git a/lib/dns_ident.c b/lib/dns_ident.c
new file mode 100644
index 0000000..f42b294
--- /dev/null
+++ b/lib/dns_ident.c
@@ -0,0 +1,178 @@
+/*-------------------------------------------------------*/
+/* lib/dns_ident.c ( NTHU CS MapleBBS Ver 3.00 ) */
+/*-------------------------------------------------------*/
+/* target : included C file for DNS routines */
+/* create : 96/11/20 */
+/* update : 96/12/15 */
+/*-------------------------------------------------------*/
+
+
+#include "dns.h"
+
+#include <unistd.h> /* Thor.991215: for timeout */
+#include <signal.h>
+
+/* ----------------------------------------------------- */
+/* get remote host / user name */
+/* ----------------------------------------------------- */
+
+
+/*
+ * dns_ident() 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.
+ */
+
+
+#define RFC931_PORT 113 /* Semi-well-known port */
+#define ANY_PORT 0 /* Any old port will do */
+
+
+#define RFC931_TIMEOUT /* Thor.991215: ¬O§_´£¨Ñ timeout */
+
+#ifdef RFC931_TIMEOUT
+static const int timeout = 6; /* ­Y 6 ¬í«á³s½u¥¼§¹¦¨¡A«h©ñ±ó */
+
+static void
+pseudo_handler() /* Thor.991215: for timeout */
+{
+ /* connect time out */
+}
+#endif
+
+
+/* Thor.990325: ¬°¤FÅý¤Ï¬d®É¯à½T©w¬d¥X¡A¨Ó¦Û­þ­Óinterface´N±q¨º³s¦^¡A¥H¨¾¤Ï¬d¤£¨ì */
+
+void
+dns_ident(sock, from, rhost, ruser)
+ int sock; /* Thor.990330: ­t¼Æ«O¯dµ¹, ¥ÎgetsockµLªk§ì¥X¥¿½Tportªº®É­Ô.
+ ¥Nªí Port, ¤£¹L¤£¤Ó¥i¯à¥Î¨ì */
+ struct sockaddr_in *from;
+ char *rhost;
+ char *ruser;
+{
+ struct sockaddr_in rmt_sin;
+ struct sockaddr_in our_sin;
+ unsigned rmt_port, rmt_pt;
+ unsigned our_port, our_pt;
+ int s, cc;
+ char buf[512];
+
+#ifdef RFC931_TIMEOUT
+ unsigned old_alarm; /* Thor.991215: for timeout */
+ struct sigaction act, oact;
+#endif
+
+ *ruser = '\0';
+
+ /* get remote host name */
+
+ if (dns_name((char *) &from->sin_addr, rhost))
+ return; /* °²³]¨S¦³ FQDN ´N¨S¦³¶] identd */
+
+ /*
+ * 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.
+ */
+
+ /* Thor.990325: ¬°¤FÅý¤Ï¬d®É¯à½T©w¬d¥X¡A¨Ó¦Û­þ­Óinterface´N±q¨º³s¦^¡A¥H¨¾¤Ï¬d¤£¨ì */
+ /* Thor.990330: ­t¼Æ«O¯dµ¹¡A¥ÎgetsockµLªk§ì¥X¥¿½Tportªº®É­Ô¡A¤£¤Ó¥i¯à */
+ if (sock >= 0)
+ {
+ s = sizeof(our_sin);
+
+ if (getsockname(sock, (struct sockaddr *) &our_sin, &s) < 0)
+ return;
+
+ /* Thor.990325: ¬°¤FÅý¤Ï¬d®É¯à½T©w¬d¥X¡A¨Ó¦Û­þ­Óinterface´N±q¨º³s¦^ */
+ our_pt = ntohs(our_sin.sin_port);
+ our_sin.sin_port = htons(ANY_PORT);
+ }
+ else
+ {
+ our_pt = -sock;
+ }
+
+ if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0)
+ return;
+
+ /*
+ * 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.
+ */
+
+#ifdef RFC931_TIMEOUT
+ /* Thor.991215: set for timeout */
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+ act.sa_handler = pseudo_handler; /* SIG_IGN; */
+ sigaction(SIGALRM, &act, &oact);
+
+ old_alarm = alarm(timeout);
+#endif
+
+ rmt_sin = *from;
+ rmt_pt = ntohs(rmt_sin.sin_port);
+ rmt_sin.sin_port = htons(RFC931_PORT);
+
+ /* Thor.990325: ¬°¤FÅý¤Ï¬d®É¯à½T©w¬d¥X¡A¨Ó¦Û­þ­Óinterface´N±q¨º³s¦^ */
+ if ((sock < 0 || !bind(s, (struct sockaddr *) & our_sin, sizeof(our_sin)))
+ && !connect(s, (struct sockaddr *) & rmt_sin, sizeof(rmt_sin)))
+ {
+ /*
+ * 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.
+ */
+
+ sprintf(buf, "%u,%u\r\n", rmt_pt, our_pt);
+ write(s, buf, strlen(buf));
+
+ cc = read(s, buf, sizeof(buf));
+ if (cc > 0)
+ {
+ buf[cc] = '\0';
+
+ if (sscanf(buf, "%u , %u : USERID :%*[^:]:%79s", &rmt_port, &our_port, ruser) == 3 &&
+ rmt_pt == rmt_port && our_pt == our_port)
+ {
+ /*
+ * Strip trailing carriage return. It is part of the protocol, not
+ * part of the data.
+ */
+
+ for (;;)
+ {
+ cc = *ruser;
+
+ if (cc == '\r')
+ {
+ *ruser = '\0';
+ break;
+ }
+
+ if (cc == '\0')
+ break;
+
+ ruser++;
+ }
+ }
+ }
+ }
+
+#ifdef RFC931_TIMEOUT
+ /* Thor.991215: recover for timeout */
+ alarm(old_alarm);
+ sigaction(SIGALRM, &oact, NULL);
+#endif
+
+ close(s);
+}
diff --git a/lib/dns_name.c b/lib/dns_name.c
new file mode 100644
index 0000000..ca70e80
--- /dev/null
+++ b/lib/dns_name.c
@@ -0,0 +1,78 @@
+/*-------------------------------------------------------*/
+/* lib/dns_name.c ( NTHU CS MapleBBS Ver 3.00 ) */
+/*-------------------------------------------------------*/
+/* target : included C file for DNS routines */
+/* create : 96/11/20 */
+/* update : 96/12/15 */
+/*-------------------------------------------------------*/
+
+
+#include "dns.h"
+
+
+/* ----------------------------------------------------- */
+/* get host name by IP address */
+/* ----------------------------------------------------- */
+
+
+int
+dns_name(addr, name)
+ unsigned char *addr;
+ char *name;
+{
+ querybuf ans;
+ char qbuf[MAXDNAME];
+ char hostbuf[MAXDNAME];
+ int n, type, ancount, qdcount;
+ u_char *cp, *eom;
+
+ sprintf(name, INADDR_FMT, addr[0], addr[1], addr[2], addr[3]);
+
+ sprintf(qbuf, INADDR_FMT ".in-addr.arpa",
+ addr[3], addr[2], addr[1], addr[0]);
+
+ n = dns_query(qbuf, T_PTR, &ans);
+ if (n < 0)
+ return n;
+
+ /* find first satisfactory answer */
+
+ cp = (u_char *) & ans + sizeof(HEADER);
+ eom = (u_char *) & ans + n;
+
+ for (qdcount = ntohs(ans.hdr.qdcount); qdcount--; cp += n + QFIXEDSZ)
+ {
+ if ((n = dn_skipname(cp, eom)) < 0)
+ return n;
+ }
+
+ for (ancount = ntohs(ans.hdr.ancount); --ancount >= 0 && cp < eom; cp += n)
+ {
+ if ((n = dn_expand((u_char *) &ans, eom, cp, hostbuf, MAXDNAME)) < 0)
+ return n;
+
+ cp += n;
+ type = getshort(cp);
+ n = getshort(cp + 8);
+ cp += 10;
+
+ if (type == T_PTR)
+ {
+ if ((n = dn_expand((u_char *) &ans, eom, cp, hostbuf, MAXDNAME)) >= 0)
+ {
+ strcpy(name, hostbuf);
+ return 0;
+ }
+ }
+
+#if 0
+ if (type == T_CNAME)
+ {
+ strcpy(name, hostbuf);
+ return 0;
+ }
+#endif
+ }
+
+ return -1;
+}
diff --git a/lib/dns_open.c b/lib/dns_open.c
new file mode 100644
index 0000000..6f588c5
--- /dev/null
+++ b/lib/dns_open.c
@@ -0,0 +1,98 @@
+/*-------------------------------------------------------*/
+/* lib/dns_open.c ( NTHU CS MapleBBS Ver 3.00 ) */
+/*-------------------------------------------------------*/
+/* target : included C file for DNS routines */
+/* create : 96/11/20 */
+/* update : 96/12/15 */
+/*-------------------------------------------------------*/
+
+
+#include "dns.h"
+
+/* Thor.990811: find out A record */
+
+unsigned long
+dns_a(host)
+ char *host;
+{
+ querybuf ans;
+ int n, ancount, qdcount;
+ unsigned char *cp, *eom;
+ char buf[MAXDNAME];
+ int type;
+ ip_addr addr;
+
+ n = dns_query(host, T_A, &ans);
+ if (n < 0)
+ return INADDR_NONE;
+
+ /* find first satisfactory answer */
+
+ cp = (u_char *) & ans + sizeof(HEADER);
+ eom = (u_char *) & ans + n;
+
+ for (qdcount = ntohs(ans.hdr.qdcount); qdcount--; cp += n + QFIXEDSZ)
+ {
+ if ((n = dn_skipname(cp, eom)) < 0)
+ return INADDR_NONE;
+ }
+
+ ancount = ntohs(ans.hdr.ancount);
+
+ while (--ancount >= 0 && cp < eom)
+ {
+ if ((n = dn_expand((void *) &ans, eom, cp, buf, MAXDNAME)) < 0)
+ return INADDR_NONE;
+
+ cp += n;
+
+ type = getshort(cp);
+ n = getshort(cp + 8);
+ cp += 10;
+
+ if (type == T_A)
+ {
+ addr.d[0] = cp[0];
+ addr.d[1] = cp[1];
+ addr.d[2] = cp[2];
+ addr.d[3] = cp[3];
+ return addr.addr;
+ }
+
+ cp += n;
+ }
+
+ return INADDR_NONE;
+}
+
+int
+dns_open(host, port)
+ char *host;
+ int port;
+{
+ struct sockaddr_in sin;
+ ip_addr addr;
+
+ /* Thor.990811: check ¬O§_¤w¬°ip */
+ if ((addr.addr = dns_aton(host)) != INADDR_NONE
+ || (addr.addr = dns_a(host)) != INADDR_NONE)
+ {
+ int sock;
+
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(port);
+ memset(sin.sin_zero, 0, sizeof(sin.sin_zero));
+
+ sin.sin_addr.s_addr = addr.addr;
+
+ sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (sock < 0)
+ return sock;
+
+ if (!connect(sock, (struct sockaddr *) & sin, sizeof(sin)))
+ return sock;
+
+ close(sock);
+ }
+ return -1;
+}
diff --git a/lib/dns_smtp.c b/lib/dns_smtp.c
new file mode 100644
index 0000000..9124c55
--- /dev/null
+++ b/lib/dns_smtp.c
@@ -0,0 +1,129 @@
+/*-------------------------------------------------------*/
+/* lib/dns_smtp.c ( NTHU CS MapleBBS Ver 3.00 ) */
+/*-------------------------------------------------------*/
+/* target : included C file for DNS routines */
+/* create : 96/11/20 */
+/* update : 96/12/15 */
+/*-------------------------------------------------------*/
+
+
+#include "dns.h"
+
+
+/* static inline */ void /* Thor. 990811: for bmtad use to check from */
+dns_mx(domain, mxlist)
+ char *domain;
+ char *mxlist;
+{
+ querybuf ans;
+ int n, ancount, qdcount;
+ unsigned char *cp, *eom;
+ int type;
+
+ *mxlist = 0;
+
+ n = dns_query(domain, T_MX, &ans);
+
+ if (n < 0)
+ return;
+
+ /* find first satisfactory answer */
+
+ cp = (u_char *) & ans + sizeof(HEADER);
+ eom = (u_char *) & ans + n;
+
+ for (qdcount = ntohs(ans.hdr.qdcount); qdcount--; cp += n + QFIXEDSZ)
+ {
+ if ((n = dn_skipname(cp, eom)) < 0)
+ return;
+ }
+
+ ancount = ntohs(ans.hdr.ancount);
+ domain = mxlist + MAX_MXLIST - MAX_DNAME - 2;
+
+ while (--ancount >= 0 && cp < eom)
+ {
+ if ((n = dn_expand((void *) &ans, eom, cp, mxlist, MAX_DNAME)) < 0)
+ break;
+
+ cp += n;
+
+ type = getshort(cp);
+ n = getshort(cp + 8);
+ cp += 10;
+
+ if (type == T_MX)
+ {
+ /* pref = getshort(cp); */
+ *mxlist = '\0';
+ if ((dn_expand((void *) &ans, eom, cp + 2, mxlist, MAX_DNAME)) < 0)
+ break;
+
+ if (!*mxlist)
+ return;
+
+ /* Thor.980820:µù¸Ñ: ±N¼Æ­Ó MX entry ¥Î : ¦ê°_¨Ó¥H«K¤@­Ó¤@­Ó¸Õ */
+ while (*mxlist)
+ mxlist++;
+ *mxlist++ = ':';
+
+ if (mxlist >= domain)
+ break;
+ }
+
+ cp += n;
+ }
+
+ *mxlist = '\0';
+}
+
+
+int
+dns_smtp(host)
+ char *host;
+{
+ int sock;
+ char *str, *ptr, mxlist[MAX_MXLIST];
+
+#ifdef HAVE_RELAY_SERVER
+ /* ¦pªG¦³¦Û©wªº relay server¡A¥ý try ¥¦¸Õ¸Õ */
+ if ((sock = dns_open(RELAY_SERVER, 25)) >= 0)
+ return sock;
+#endif
+
+ dns_mx(host, str = mxlist);
+ if (!*str)
+ {
+ /* Thor.990716: ¦]©I¥s®É¥i¯à±Nhost¥Îip©ñ¤J¡A¬G§@¯S§O³B²z¡A
+ ¨Ï¤£³z¹Ldns_open()±H«H */
+ /* if(*host>='0' && *host<='9') return -1; */
+ /* Thor.990811: ¥Îdns_aton()¸û§¹¾ã */
+ if (dns_aton(host) != INADDR_NONE)
+ return -1;
+
+ str = host;
+ }
+
+ for (;;)
+ { /* Thor.980820: µù¸Ñ: ¸U¤@host®æ¦¡¬° xxx:yyy:zzz, «h¥ý¸Õ xxx,¤£¦æ¦A¸Õ yyy */
+ ptr = str;
+ while (sock = *ptr)
+ {
+ if (sock == ':')
+ {
+ *ptr++ = '\0';
+ break;
+ }
+ ptr++;
+ }
+
+ if (!*str)
+ return -1;
+
+ sock = dns_open(str, 25);
+ if (sock >= 0)
+ return sock;
+
+ str = ptr;
+ }
+}
diff --git a/lib/f_cat.c b/lib/f_cat.c
new file mode 100644
index 0000000..4aaf60b
--- /dev/null
+++ b/lib/f_cat.c
@@ -0,0 +1,16 @@
+#include <fcntl.h>
+
+
+void
+f_cat(fpath, msg)
+ char *fpath;
+ char *msg;
+{
+ int fd;
+
+ if ((fd = open(fpath, O_WRONLY | O_CREAT | O_APPEND, 0600)) >= 0)
+ {
+ write(fd, msg, strlen(msg));
+ close(fd);
+ }
+}
diff --git a/lib/f_cp.c b/lib/f_cp.c
new file mode 100644
index 0000000..bf741c0
--- /dev/null
+++ b/lib/f_cp.c
@@ -0,0 +1,35 @@
+#include "dao.h"
+#include <sys/types.h>
+#include <fcntl.h>
+
+
+int
+f_cp(src, dst, mode)
+ char *src, *dst;
+ int mode; /* O_EXCL / O_APPEND / O_TRUNC */
+{
+ int fsrc, fdst, ret;
+
+ ret = 0;
+
+ if ((fsrc = open(src, O_RDONLY)) >= 0)
+ {
+ ret = -1;
+
+ if ((fdst = open(dst, O_WRONLY | O_CREAT | mode, 0600)) >= 0)
+ {
+ char pool[BLK_SIZ];
+
+ src = pool;
+ do
+ {
+ ret = read(fsrc, src, BLK_SIZ);
+ if (ret <= 0)
+ break;
+ } while (write(fdst, src, ret) > 0);
+ close(fdst);
+ }
+ close(fsrc);
+ }
+ return ret;
+}
diff --git a/lib/f_img.c b/lib/f_img.c
new file mode 100644
index 0000000..e34c4d6
--- /dev/null
+++ b/lib/f_img.c
@@ -0,0 +1,32 @@
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+
+char *
+f_img(fpath, fsize)
+ char *fpath;
+ int *fsize;
+{
+ int fd, size;
+ struct stat st;
+
+ if ((fd = open(fpath, O_RDONLY)) < 0)
+ return NULL;
+
+ fpath = NULL;
+
+ if (!fstat(fd, &st) && S_ISREG(st.st_mode) && (size = st.st_size) > 0 &&
+ (fpath = (char *) malloc(size)))
+ {
+ *fsize = size;
+ if (read(fd, fpath, size) != size)
+ {
+ free(fpath);
+ fpath = NULL;
+ }
+ }
+
+ close(fd);
+ return fpath;
+}
diff --git a/lib/f_ln.c b/lib/f_ln.c
new file mode 100644
index 0000000..8726e2f
--- /dev/null
+++ b/lib/f_ln.c
@@ -0,0 +1,22 @@
+/* ----------------------------------------------------- */
+/* f_ln() : link() cross partition / disk */
+/* ----------------------------------------------------- */
+
+
+#include <fcntl.h>
+#include <errno.h>
+
+
+int
+f_ln(src, dst)
+ char *src, *dst;
+{
+ int ret;
+
+ if (ret = link(src, dst))
+ {
+ if (errno != EEXIST)
+ ret = f_cp(src, dst, O_EXCL);
+ }
+ return ret;
+}
diff --git a/lib/f_lock.c b/lib/f_lock.c
new file mode 100644
index 0000000..e47bfe9
--- /dev/null
+++ b/lib/f_lock.c
@@ -0,0 +1,36 @@
+#include <unistd.h>
+#include <fcntl.h>
+
+static struct flock fl =
+{
+ l_whence: SEEK_SET,
+ l_start: 0,
+ l_len: 0,
+};
+
+
+int
+f_exlock(fd)
+ int fd;
+{
+#if 0
+ return flock(fd, LOCK_EX);
+#endif
+ /* Thor.981205: ¥Î fcntl ¨ú¥N flock¡APOSIX ¼Ð·Ç¥Îªk */
+ fl.l_type = F_WRLCK;
+ /* Thor.990309: with blocking */
+ return fcntl(fd, F_SETLKW /* F_SETLK */, &fl);
+}
+
+
+int
+f_unlock(fd)
+ int fd;
+{
+#if 0
+ return flock(fd, LOCK_UN);
+#endif
+ /* Thor.981205: ¥Î fcntl ¨ú¥N flock¡APOSIX ¼Ð·Ç¥Îªk */
+ fl.l_type = F_UNLCK;
+ return fcntl(fd, F_SETLKW /* F_SETLK */, &fl);
+}
diff --git a/lib/f_map.c b/lib/f_map.c
new file mode 100644
index 0000000..e60c2f4
--- /dev/null
+++ b/lib/f_map.c
@@ -0,0 +1,35 @@
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+
+#ifdef MAP_FILE /* 44BSD defines this & requires it to mmap files */
+# define DAO_MAP (MAP_SHARED | MAP_FILE)
+#else
+# define DAO_MAP (MAP_SHARED)
+#endif
+
+
+char *
+f_map(fpath, fsize)
+ 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, DAO_MAP, fd, 0);
+ close(fd);
+ *fsize = size;
+ return fpath;
+}
diff --git a/lib/f_mode.c b/lib/f_mode.c
new file mode 100644
index 0000000..c077077
--- /dev/null
+++ b/lib/f_mode.c
@@ -0,0 +1,14 @@
+#include <sys/stat.h>
+
+
+int
+f_mode(fpath)
+ char *fpath;
+{
+ struct stat st;
+
+ if (stat(fpath, &st))
+ return 0;
+
+ return st.st_mode;
+}
diff --git a/lib/f_mv.c b/lib/f_mv.c
new file mode 100644
index 0000000..8ec96f9
--- /dev/null
+++ b/lib/f_mv.c
@@ -0,0 +1,17 @@
+#include <fcntl.h>
+
+
+int
+f_mv(src, dst)
+ char *src, *dst;
+{
+ int ret;
+
+ if (ret = rename(src, dst))
+ {
+ ret = f_cp(src, dst, O_TRUNC);
+ if (!ret)
+ unlink(src);
+ }
+ return ret;
+}
diff --git a/lib/f_new.c b/lib/f_new.c
new file mode 100644
index 0000000..2c21e75
--- /dev/null
+++ b/lib/f_new.c
@@ -0,0 +1,50 @@
+/* ----------------------------------------------------- */
+/* exclusively create file [*.n] */
+/* ----------------------------------------------------- */
+
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <errno.h>
+
+
+FILE *
+f_new(fold, fnew)
+ char *fold;
+ char *fnew;
+{
+ int fd, try;
+ extern int errno;
+
+ try = 0;
+ str_cat(fnew, fold, ".n");
+
+ for (;;)
+ {
+ fd = open(fnew, O_WRONLY | O_CREAT | O_EXCL, 0600);
+
+ if (fd >= 0)
+ return fdopen(fd, "w");
+
+ if (errno != EEXIST)
+ break;
+
+ if (!try++)
+ {
+ struct stat st;
+
+ if (stat(fnew, &st))
+ break;
+ if (st.st_mtime < time(NULL) - 20 * 60) /* °²³] 20 ¤ÀÄÁ¤ºÀ³¸Ó³B²z§¹ */
+ unlink(fnew);
+ }
+ else
+ {
+ if (try > 24) /* µ¥«Ý 120 ¬íÄÁ */
+ break;
+ sleep(5);
+ }
+ }
+ return NULL;
+}
diff --git a/lib/f_path.c b/lib/f_path.c
new file mode 100644
index 0000000..340e726
--- /dev/null
+++ b/lib/f_path.c
@@ -0,0 +1,86 @@
+/* ----------------------------------------------------- */
+/* file structure : set file path for boards/user home */
+/* ----------------------------------------------------- */
+
+
+static void
+mak_fpath(str, key, name)
+ char *str;
+ char *key;
+ char *name;
+{
+ int cc;
+
+ cc = '/';
+ for (;;)
+ {
+ *str = cc;
+ if (!cc)
+ break;
+ str++;
+ cc = *key++;
+ }
+
+ if (name)
+ {
+ *str++ = '/';
+ strcpy(str, name);
+ }
+}
+
+
+void
+brd_fpath(fpath, board, fname)
+ char *fpath;
+ char *board;
+ char *fname;
+{
+ *fpath++ = 'b';
+ *fpath++ = 'r';
+ *fpath++ = 'd';
+ mak_fpath(fpath, board, fname);
+}
+
+
+void
+gem_fpath(fpath, board, fname)
+ char *fpath;
+ char *board;
+ char *fname;
+{
+ *fpath++ = 'g';
+ *fpath++ = 'e';
+ *fpath++ = 'm';
+ *fpath++ = '/';
+ *fpath++ = 'b';
+ *fpath++ = 'r';
+ *fpath++ = 'd';
+ mak_fpath(fpath, board, fname);
+}
+
+
+void
+usr_fpath(fpath, user, fname)
+ char *fpath;
+ char *user;
+ char *fname;
+{
+#define IDLEN 12 /* Length of user id, copy from struct.h */
+
+ char buf[IDLEN + 1];
+
+ *fpath++ = 'u';
+ *fpath++ = 's';
+ *fpath++ = 'r';
+ *fpath++ = '/';
+
+#if 0
+ str_lower(buf, user); /* lower case */
+#endif
+ /* Thor.981027: ¨¾¤î buffer overflow, ÁöµM SunOS 4.1.x¤WµL¦¹±¡ªp, ¥H«á¦A·Q¦nªº§ïªk */
+ str_ncpy(buf, user, sizeof(buf));
+ str_lower(buf, buf);
+
+ *fpath++ = *buf;
+ mak_fpath(fpath, buf, fname);
+}
diff --git a/lib/f_rm.c b/lib/f_rm.c
new file mode 100644
index 0000000..5158306
--- /dev/null
+++ b/lib/f_rm.c
@@ -0,0 +1,59 @@
+#include <sys/stat.h>
+#include <dirent.h>
+
+
+static int
+rm_dir(fpath)
+ char *fpath;
+{
+ struct stat st;
+ DIR *dirp;
+ struct dirent *de;
+ char buf[256], *fname;
+
+ if (!(dirp = opendir(fpath)))
+ return -1;
+
+ for (fname = buf; *fname = *fpath; fname++, fpath++)
+ ;
+
+ *fname++ = '/';
+
+ while (de = readdir(dirp))
+ {
+ fpath = de->d_name;
+
+ /* skip ./ ¤Î ../ */
+ if (!*fpath || (*fpath == '.' && (fpath[1] == '\0' || (fpath[1] == '.' && fpath[2] == '\0'))))
+ continue;
+
+ strcpy(fname, fpath);
+ if (!stat(buf, &st))
+ {
+ if (S_ISDIR(st.st_mode))
+ rm_dir(buf);
+ else
+ unlink(buf);
+ }
+ }
+ closedir(dirp);
+
+ *--fname = '\0';
+ return rmdir(buf);
+}
+
+
+int
+f_rm(fpath)
+ char *fpath;
+{
+ struct stat st;
+
+ if (stat(fpath, &st))
+ return -1;
+
+ if (!S_ISDIR(st.st_mode))
+ return unlink(fpath);
+
+ return rm_dir(fpath);
+}
diff --git a/lib/f_suck.c b/lib/f_suck.c
new file mode 100644
index 0000000..098f96d
--- /dev/null
+++ b/lib/f_suck.c
@@ -0,0 +1,26 @@
+#include "dao.h"
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/types.h>
+
+
+void
+f_suck(fp, fpath)
+ FILE *fp;
+ char *fpath;
+{
+ int fd;
+
+ if ((fd = open(fpath, O_RDONLY)) >= 0)
+ {
+ char pool[BLK_SIZ];
+ int size;
+
+ fpath = pool;
+ while ((size = read(fd, fpath, BLK_SIZ)) > 0)
+ {
+ fwrite(fpath, size, 1, fp);
+ }
+ close(fd);
+ }
+}
diff --git a/lib/hash32.c b/lib/hash32.c
new file mode 100644
index 0000000..431cde4
--- /dev/null
+++ b/lib/hash32.c
@@ -0,0 +1,13 @@
+int
+hash32(str)
+ unsigned char *str;
+{
+ int xo, cc;
+
+ xo = 1048583; /* a big prime number */
+ while (cc = *str++)
+ {
+ xo = (xo << 5) - xo + cc; /* 31 * xo + cc */
+ }
+ return (xo & 0x7fffffff);
+}
diff --git a/lib/hdr_fpath.c b/lib/hdr_fpath.c
new file mode 100644
index 0000000..064a828
--- /dev/null
+++ b/lib/hdr_fpath.c
@@ -0,0 +1,37 @@
+#include "dao.h"
+
+
+void
+hdr_fpath(fpath, folder, hdr)
+ char *fpath;
+ char *folder;
+ HDR *hdr;
+{
+ char *str;
+ int cc, chrono;
+
+ while (cc = *folder++)
+ {
+ *fpath++ = cc;
+ if (cc == '/')
+ str = fpath;
+ }
+
+ chrono = hdr->chrono;
+ folder = hdr->xname;
+ cc = *folder;
+ if (cc != '@')
+ cc = radix32[chrono & 31];
+
+ if (*str == '.')
+ {
+ *str++ = cc;
+ *str++ = '/';
+ }
+ else
+ {
+ str[-2] = cc;
+ }
+
+ strcpy(str, hdr->xname);
+}
diff --git a/lib/hdr_stamp.c b/lib/hdr_stamp.c
new file mode 100644
index 0000000..bcdcae9
--- /dev/null
+++ b/lib/hdr_stamp.c
@@ -0,0 +1,131 @@
+/* ----------------------------------------------------- */
+/* hdr_stamp - create unique HDR based on timestamp */
+/* ----------------------------------------------------- */
+/* fpath - directory */
+/* token - (A / F / 0) | [HDR_LINK / HDR_COPY] */
+/* ----------------------------------------------------- */
+/* return : open() fd (not close yet) or link() result */
+/* ----------------------------------------------------- */
+
+
+#include "dao.h"
+#include <fcntl.h>
+#include <errno.h>
+
+#if 0 /* itoc.030303.µù¸Ñ: ²©ö»¡©ú */
+
+ hdr_stamp() ·|°µ¥X¤@­Ó·sªº HDR¡A¨Ì¶Ç¤Jªº token ¤£¦P¦Ó¦³®t²§¡G
+
+ 0 : ·s¼W¤@½g«H¥ó(family ¬O @)¡A¦^¶Çªº fpath ¬O hdr ©Ò«ü¦V
+ ©M hdr_fpath(fpath, folder, hdr); ©Ò²£¥Íªº fpath ¬Û¦P
+
+ 'A': ·s¼W¤@½g¤å³¹(family ¬O A)¡A¦^¶Çªº fpath ¬O hdr ©Ò«ü¦V
+ ©M hdr_fpath(fpath, folder, hdr); ©Ò²£¥Íªº fpath ¬Û¦P
+
+ 'F': ·s¼W¤@­Ó¨÷©v(family ¬O F)¡A¦^¶Çªº fpath ¬O hdr ©Ò«ü¦V
+ ©M hdr_fpath(fpath, folder, hdr); ©Ò²£¥Íªº fpath ¬Û¦P
+
+ HDR_LINK : fpath ¤w¦³ÂÂÀɮ׮ɡA­n½Æ»sÂÂÀɮרì·s«H¥ó(family ¬O @) ¥h
+ ¨Ã±N hdr «ü¦V³o½g·s«H¥ó¡A¦^¶Çªº fpath ¬O­ì¨ÓÂÂÀÉ®×
+ ÂÂÀɮשM·s«H¥ó¬O hard link¡A§ï¤F¨ä¤¤¤@½g¡A¥t¤@½g¤]·|¤@°_³Q§ï
+ §R°£ÂÂÀɮסA·s«H¥ó¨Ã¤£·|³Q§R°£
+
+ HDR_LINK | 'A': fpath ¤w¦³ÂÂÀɮ׮ɡA­n½Æ»sÂÂÀɮרì·s¤å³¹(family ¬O A) ¥h
+ ¨Ã±N hdr «ü¦V³o½g·s¤å³¹¡A¦^¶Çªº fpath ¬O­ì¨ÓÂÂÀÉ®×
+ ÂÂÀɮשM·s¤å³¹¬O hard link¡A§ï¤F¨ä¤¤¤@½g¡A¥t¤@½g¤]·|¤@°_³Q§ï
+ §R°£ÂÂÀɮסA·s¤å³¹¨Ã¤£·|³Q§R°£
+
+ HDR_COPY : fpath ¤w¦³ÂÂÀɮ׮ɡA­n½Æ»sÂÂÀɮרì·s«H¥ó(family ¬O @) ¥h
+ ¨Ã±N hdr «ü¦V³o½g·s«H¥ó¡A¦^¶Çªº fpath ¬O­ì¨ÓÂÂÀÉ®×
+ ÂÂÀɮשM·s«H¥ó¬O copy¡A§ï¤F¨ä¤¤¤@½g¡A¥t¤@½g¨Ã¤£·|³Q§ï
+ ÂÂÀÉ®×»P·s«H¥ó¬O§¹¥þ¿W¥ß¤£¬ÛÃöªº¤G­ÓÀÉ®×
+
+ HDR_COPY | 'A': fpath ¤w¦³ÂÂÀɮ׮ɡA­n½Æ»sÂÂÀɮרì·s¤å³¹(family ¬O A) ¥h
+ ¨Ã±N hdr «ü¦V³o½g·s¤å³¹¡A¦^¶Çªº fpath ¬O­ì¨ÓÂÂÀÉ®×
+ ÂÂÀɮשM·s¤å³¹¬O copy¡A§ï¤F¨ä¤¤¤@½g¡A¥t¤@½g¨Ã¤£·|³Q§ï
+ ÂÂÀÉ®×»P·s¤å³¹¬O§¹¥þ¿W¥ß¤£¬ÛÃöªº¤G­ÓÀÉ®×
+
+#endif
+
+int
+hdr_stamp(folder, token, hdr, fpath)
+ char *folder;
+ int token;
+ HDR *hdr;
+ char *fpath;
+{
+ char *fname, *family;
+ int rc, chrono;
+ char *flink, buf[128];
+
+ flink = NULL;
+ if (token & (HDR_LINK | HDR_COPY))
+ {
+ flink = fpath;
+ fpath = buf;
+ }
+
+ fname = fpath;
+ while (rc = *folder++)
+ {
+ *fname++ = rc;
+ if (rc == '/')
+ family = fname;
+ }
+ if (*family != '.')
+ {
+ fname = family;
+ family -= 2;
+ }
+ else
+ {
+ fname = family + 1;
+ *fname++ = '/';
+ }
+
+ if (rc = token & 0xdf) /* Åܤj¼g */
+ {
+ *fname++ = rc;
+ }
+ else
+ {
+ *fname = *family = '@';
+ family = ++fname;
+ }
+
+ chrono = time(0);
+
+ for (;;)
+ {
+ *family = radix32[chrono & 31];
+ archiv32(chrono, fname);
+
+ if (flink)
+ {
+ if (token & HDR_LINK)
+ rc = f_ln(flink, fpath);
+ else
+ rc = f_cp(flink, fpath, O_EXCL);
+ }
+ else
+ {
+ rc = open(fpath, O_WRONLY | O_CREAT | O_EXCL, 0600);
+ }
+
+ if (rc >= 0)
+ {
+ memset(hdr, 0, sizeof(HDR));
+ hdr->chrono = chrono;
+ str_stamp(hdr->date, &hdr->chrono);
+ strcpy(hdr->xname, --fname);
+ break;
+ }
+
+ if (errno != EEXIST)
+ break;
+
+ chrono++;
+ }
+
+ return rc;
+}
diff --git a/lib/is_alnum.c b/lib/is_alnum.c
new file mode 100644
index 0000000..cc65945
--- /dev/null
+++ b/lib/is_alnum.c
@@ -0,0 +1,7 @@
+int
+is_alnum(ch)
+ int ch;
+{
+ return ((ch >= '0' && ch <= '9') ||
+ (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'));
+}
diff --git a/lib/is_alpha.c b/lib/is_alpha.c
new file mode 100644
index 0000000..dad6cef
--- /dev/null
+++ b/lib/is_alpha.c
@@ -0,0 +1,6 @@
+int
+is_alpha(ch)
+ int ch;
+{
+ return ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'));
+}
diff --git a/lib/mak_dirs.c b/lib/mak_dirs.c
new file mode 100644
index 0000000..1857d14
--- /dev/null
+++ b/lib/mak_dirs.c
@@ -0,0 +1,66 @@
+/* ----------------------------------------------------- */
+/* make directory hierarchy [0-9A-V] : 32-way interleave */
+/* ----------------------------------------------------- */
+
+
+#include <sys/stat.h>
+
+
+void
+mak_dirs(fpath)
+ char *fpath;
+{
+ char *fname;
+ int ch;
+
+ if (mkdir(fpath, 0700))
+ return;
+
+ fname = fpath;
+ while (*++fname);
+ *fname++ = '/';
+ fname[1] = '\0';
+
+ ch = '0';
+ for (;;)
+ {
+ *fname = ch++;
+ mkdir(fpath, 0700);
+ if (ch == 'W')
+ break;
+ if (ch == '9' + 1)
+ ch = '@'; /* @ : for special purpose */
+ }
+
+ fname[-1] = '\0';
+}
+
+
+void
+mak_links(fpath) /* itoc.010924: ´î¤Ö­Ó¤HºëµØ°Ï¥Ø¿ý¡A¥Î link ¨Ó¥N´À¥Ø¿ý */
+ char *fpath;
+{
+ char *fname;
+ int ch;
+
+ if (mkdir(fpath, 0700))
+ return;
+
+ fname = fpath;
+ while (*++fname);
+ *fname++ = '/';
+ fname[1] = '\0';
+
+ ch = '0';
+ for (;;)
+ {
+ *fname = ch++;
+ symlink(".", fpath);
+ if (ch == 'W')
+ break;
+ if (ch == '9' + 1)
+ ch = '@'; /* @ : for special purpose */
+ }
+
+ fname[-1] = '\0';
+}
diff --git a/lib/not_addr.c b/lib/not_addr.c
new file mode 100644
index 0000000..5566213
--- /dev/null
+++ b/lib/not_addr.c
@@ -0,0 +1,32 @@
+#define STRICT_FQDN_EMAIL
+
+
+int
+not_addr(addr)
+ char *addr;
+{
+ int ch, mode;
+
+ mode = -1;
+
+ while (ch = *addr)
+ {
+ if (ch == '@')
+ {
+ if (++mode)
+ break;
+ }
+
+#ifdef STRICT_FQDN_EMAIL
+ else if ((ch != '.') && (ch != '-') && (ch != '_') && !is_alnum(ch))
+#else
+ else if (!is_alnum(ch) && !strchr(".-_[]%!:", ch))
+#endif
+
+ return 1;
+
+ addr++;
+ }
+
+ return mode;
+}
diff --git a/lib/radix32.c b/lib/radix32.c
new file mode 100644
index 0000000..2a6cbb0
--- /dev/null
+++ b/lib/radix32.c
@@ -0,0 +1,13 @@
+/* ----------------------------------------------------- */
+/* chrono ==> file name (32-based) */
+/* 0123456789ABCDEFGHIJKLMNOPQRSTUV */
+/* ----------------------------------------------------- */
+
+
+char radix32[32] =
+{
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
+ 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
+ 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
+};
diff --git a/lib/rec_add.c b/lib/rec_add.c
new file mode 100644
index 0000000..c7d0b1b
--- /dev/null
+++ b/lib/rec_add.c
@@ -0,0 +1,21 @@
+#include <fcntl.h>
+
+
+int
+rec_add(fpath, data, size)
+ char *fpath;
+ void *data;
+ int size;
+{
+ int fd;
+
+ if ((fd = open(fpath, O_WRONLY | O_CREAT | O_APPEND, 0600)) < 0)
+ return -1;
+
+ /* flock(fd, LOCK_EX); */
+ write(fd, data, size);
+ /* flock(fd, LOCK_UN); */
+ close(fd);
+
+ return 0;
+}
diff --git a/lib/rec_bot.c b/lib/rec_bot.c
new file mode 100644
index 0000000..3b15f1e
--- /dev/null
+++ b/lib/rec_bot.c
@@ -0,0 +1,87 @@
+#include "dao.h"
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+
+
+static int
+is_bottompost(hdr)
+ HDR *hdr;
+{
+ return (hdr->xmode & POST_BOTTOM);
+}
+
+
+int
+rec_bot(fpath, data, size) /* amaki.040715: ´O¤J¦¡¼gÀÉ */
+ char *fpath;
+ void *data;
+ int size;
+{
+ int fd, fsize, count;
+ void *pool, *set;
+ char set_pool[REC_SIZ];
+ struct stat st;
+
+ if ((fd = open(fpath, O_RDWR | O_CREAT, 0600)) < 0)
+ return -1;
+
+ /* flock(fd, LOCK_EX); */
+ /* Thor.981205: ¥Î fcntl ¨ú¥Nflock, POSIX¼Ð·Ç¥Îªk */
+ f_exlock(fd);
+
+ fstat(fd, &st);
+
+ count = 0;
+ set = (void *) set_pool;
+
+ if (fsize = st.st_size)
+ {
+ while ((fsize -= size) >= 0)
+ {
+ lseek(fd, fsize, SEEK_SET);
+ read(fd, set, size);
+
+ if (!is_bottompost(set))
+ {
+ if (count)
+ {
+ pool = (void *) malloc(count * size);
+
+ read(fd, pool, count * size);
+ lseek(fd, -size * count, SEEK_CUR);
+ }
+ break;
+ }
+ else if (fsize <= 0) /* amaki.040715: ¥þ³¡³£¬O¸m©³ªºªF¦è */
+ {
+ count++;
+ pool = (void *) malloc(count * size);
+
+ lseek(fd, -size, SEEK_CUR);
+ read(fd, pool, count * size);
+ lseek(fd, -size * count, SEEK_CUR);
+ break;
+ }
+ else
+ count++;
+ }
+ }
+
+ write(fd, data, size);
+
+ if (count)
+ {
+ write(fd, pool, count * size);
+ free(pool);
+ }
+
+ /* flock(fd, LOCK_EX); */
+ /* Thor.981205: ¥Î fcntl ¨ú¥Nflock, POSIX¼Ð·Ç¥Îªk */
+ f_unlock(fd);
+
+ close(fd);
+
+ return 0;
+}
diff --git a/lib/rec_del.c b/lib/rec_del.c
new file mode 100644
index 0000000..dd1a167
--- /dev/null
+++ b/lib/rec_del.c
@@ -0,0 +1,84 @@
+#include "dao.h"
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+
+
+int
+rec_del(fpath, size, pos, fchk)
+ char *fpath;
+ int size;
+ int pos;
+ int (*fchk) ();
+{
+ int fd;
+ off_t off, len;
+ char pool[REC_SIZ];
+ struct stat st;
+
+ if ((fd = open(fpath, O_RDWR)) < 0)
+ return -1;
+
+ /* flock(fd, LOCK_EX); */
+ /* Thor.981205: ¥Î fcntl ¨ú¥Nflock, POSIX¼Ð·Ç¥Îªk */
+ f_exlock(fd);
+
+ fstat(fd, &st);
+ len = st.st_size;
+
+ fpath = pool;
+ off = size * pos;
+
+ /* ÅçÃÒ pos ¦ì¸m¸ê®Æªº¥¿½T©Ê */
+
+ if (len > off)
+ {
+ lseek(fd, off, SEEK_SET);
+ read(fd, fpath, size);
+
+ pos = fchk ? (*fchk) (fpath) : 1;
+ }
+ else
+ {
+ pos = 0;
+ }
+
+ /* ¤£¹ïªº¸Ü¡A­«ÀY§ä°_ */
+
+ if (!pos)
+ {
+ off = 0;
+ lseek(fd, off, SEEK_SET);
+ while (read(fd, fpath, size) == size)
+ {
+ if (pos = (*fchk) (fpath))
+ break;
+
+ off += size;
+ }
+ }
+
+ /* §ä¨ì¤§«á¡A§R°£¸ê®Æ */
+
+ if (pos)
+ {
+ len -= (off + size);
+ fpath = (char *) malloc(len);
+ read(fd, fpath, len);
+
+ lseek(fd, off, SEEK_SET);
+ write(fd, fpath, len);
+
+ ftruncate(fd, off + len);
+ free(fpath);
+ }
+
+ /* flock(fd, LOCK_UN); */
+ /* Thor.981205: ¥Î fcntl ¨ú¥Nflock, POSIX¼Ð·Ç¥Îªk */
+ f_unlock(fd);
+
+ close(fd);
+
+ return !pos;
+}
diff --git a/lib/rec_get.c b/lib/rec_get.c
new file mode 100644
index 0000000..05c3f3a
--- /dev/null
+++ b/lib/rec_get.c
@@ -0,0 +1,26 @@
+#include <fcntl.h>
+#include <unistd.h>
+
+
+int
+rec_get(fpath, data, size, pos)
+ char *fpath;
+ void *data;
+ int size, pos;
+{
+ int fd;
+ int ret;
+
+ ret = -1;
+
+ if ((fd = open(fpath, O_RDONLY)) >= 0)
+ {
+ if (lseek(fd, (off_t) (size * pos), SEEK_SET) >= 0)
+ {
+ if (read(fd, data, size) == size)
+ ret = 0;
+ }
+ close(fd);
+ }
+ return ret;
+}
diff --git a/lib/rec_ins.c b/lib/rec_ins.c
new file mode 100644
index 0000000..fbac05f
--- /dev/null
+++ b/lib/rec_ins.c
@@ -0,0 +1,58 @@
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+
+
+int
+rec_ins(fpath, data, size, pos, num)
+ char *fpath;
+ void *data;
+ int size;
+ int pos;
+ int num;
+{
+ int fd;
+ off_t off, len;
+ struct stat st;
+
+ if ((fd = open(fpath, O_RDWR | O_CREAT, 0600)) < 0)
+ return -1;
+
+ /* flock(fd, LOCK_EX); */
+ /* Thor.981205: ¥Î fcntl ¨ú¥Nflock, POSIX¼Ð·Ç¥Îªk */
+ f_exlock(fd);
+
+ fstat(fd, &st);
+ len = st.st_size;
+
+ /* lkchu.990428: ernie patch ¦pªG len=0 & pos>0
+ (¦b­è¶}ºëµØ°Ï¥Ø¿ý¶i¥h¶K¤W¡A¿ï¤U¤@­Ó) ®É·|¼g¤J©U§£ */
+ off = len ? size * pos : 0;
+ lseek(fd, off, SEEK_SET);
+
+ size *= num;
+ len -= off;
+ if (len > 0)
+ {
+ fpath = (char *) malloc(pos = len + size);
+ memcpy(fpath, data, size);
+ read(fd, fpath + size, len);
+ lseek(fd, off, SEEK_SET);
+ data = fpath;
+ size = pos;
+ }
+
+ write(fd, data, size);
+
+ /* flock(fd, LOCK_UN); */
+ /* Thor.981205: ¥Î fcntl ¨ú¥Nflock, POSIX¼Ð·Ç¥Îªk */
+ f_unlock(fd);
+
+ close(fd);
+
+ if (len > 0)
+ free(data);
+
+ return 0;
+}
diff --git a/lib/rec_mov.c b/lib/rec_mov.c
new file mode 100644
index 0000000..d9eef86
--- /dev/null
+++ b/lib/rec_mov.c
@@ -0,0 +1,75 @@
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+
+
+int
+rec_mov(fpath, size, from, to)
+ char *fpath;
+ int size;
+ int from;
+ int to;
+{
+ int fd, backward;
+ off_t off, len;
+ char *pool;
+ struct stat st;
+
+ if ((fd = open(fpath, O_RDWR)) < 0)
+ return -1;
+
+ /* flock(fd, LOCK_EX); */
+ /* Thor.981205: ¥Î fcntl ¨ú¥Nflock, POSIX¼Ð·Ç¥Îªk */
+ f_exlock(fd);
+
+ fstat(fd, &st);
+ len = st.st_size / size - 1;
+
+ if (from > to)
+ {
+ backward = from;
+ from = to;
+ to = backward;
+ backward = 1;
+ }
+ else
+ {
+ backward = 0;
+ }
+
+ if (to >= len)
+ to = len;
+
+ off = size * from;
+ lseek(fd, off, SEEK_SET);
+
+ len = (to - from + 1) * size;
+ pool = fpath = (char *) malloc(len + size);
+
+ if (backward)
+ fpath += size;
+ read(fd, fpath, len);
+
+ fpath = pool + len;
+ if (backward)
+ memcpy(pool, fpath, size);
+ else
+ memcpy(fpath, pool, size);
+
+ fpath = pool;
+ if (!backward)
+ fpath += size;
+
+ lseek(fd, off, SEEK_SET);
+ write(fd, fpath, len);
+
+ /* flock(fd, LOCK_UN); */
+ /* Thor.981205: ¥Î fcntl ¨ú¥Nflock, POSIX¼Ð·Ç¥Îªk */
+ f_unlock(fd);
+
+ close(fd);
+ free(pool);
+
+ return 0;
+}
diff --git a/lib/rec_num.c b/lib/rec_num.c
new file mode 100644
index 0000000..b0cc375
--- /dev/null
+++ b/lib/rec_num.c
@@ -0,0 +1,14 @@
+#include <sys/stat.h>
+
+
+int
+rec_num(fpath, size)
+ char *fpath;
+ int size;
+{
+ struct stat st;
+
+ if (stat(fpath, &st))
+ return 0;
+ return (st.st_size / size);
+}
diff --git a/lib/rec_put.c b/lib/rec_put.c
new file mode 100644
index 0000000..c61f081
--- /dev/null
+++ b/lib/rec_put.c
@@ -0,0 +1,92 @@
+#include "dao.h"
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+
+
+int
+rec_put(fpath, data, size, pos, fchk)
+ char *fpath;
+ void *data;
+ int size, pos;
+ int (*fchk)();
+{
+ int fd;
+ off_t off, len;
+ char pool[REC_SIZ];
+ struct stat st;
+
+ if ((fd = open(fpath, O_RDWR | O_CREAT, 0600)) < 0)
+ return -1;
+
+ /* flock(fd, LOCK_EX); */
+ /* Thor.981205: ¥Î fcntl ¨ú¥Nflock, POSIX¼Ð·Ç¥Îªk */
+ f_exlock(fd);
+
+ fstat(fd, &st);
+ len = st.st_size;
+
+ fpath = pool;
+ off = size * pos;
+
+ /* ÅçÃÒ pos ¦ì¸m¸ê®Æªº¥¿½T©Ê */
+
+ if (len > off)
+ {
+ if (fchk)
+ {
+ lseek(fd, off, SEEK_SET);
+ read(fd, fpath, size);
+ pos = (*fchk) (fpath);
+ }
+ else
+ {
+ pos = 1;
+ }
+ }
+ else
+ {
+ if (len)
+ {
+ pos = 0; /* ±qÀY§ä°_ */
+ }
+ else
+ {
+ /* ­Y­ì¥»¬OªÅÀɮסA¨º»ò rec_put ·í¦¨ rec_add */
+ pos = 1;
+ off = 0;
+ }
+ }
+
+ /* ¤£¹ïªº¸Ü¡A­«ÀY§ä°_ */
+
+ if (!pos)
+ {
+ off = 0;
+ lseek(fd, off, SEEK_SET);
+ while (read(fd, fpath, size) == size)
+ {
+ if (pos = (*fchk) (fpath))
+ break;
+
+ off += size;
+ }
+ }
+
+ /* §ä¨ì¤§«á¡A§ó·s¸ê®Æ */
+
+ if (pos)
+ {
+ lseek(fd, off, SEEK_SET);
+ write(fd, data, size);
+ }
+
+ /* flock(fd, LOCK_UN); */
+ /* Thor.981205: ¥Î fcntl ¨ú¥Nflock, POSIX¼Ð·Ç¥Îªk */
+ f_unlock(fd);
+
+ close(fd);
+
+ return 0;
+}
diff --git a/lib/rec_ref.c b/lib/rec_ref.c
new file mode 100644
index 0000000..ca5afdb
--- /dev/null
+++ b/lib/rec_ref.c
@@ -0,0 +1,88 @@
+#include "dao.h"
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+
+
+int
+rec_ref(fpath, data, size, pos, fchk, fref)
+ char *fpath;
+ void *data;
+ int size, pos;
+ int (*fchk)();
+ void (*fref)();
+{
+ int fd;
+ off_t off, len;
+ char pool[REC_SIZ];
+ struct stat st;
+
+ if ((fd = open(fpath, O_RDWR, 0600)) < 0)
+ return -1;
+
+ /* flock(fd, LOCK_EX); */
+ /* Thor.981205: ¥Î fcntl ¨ú¥Nflock, POSIX¼Ð·Ç¥Îªk */
+ f_exlock(fd);
+
+ fstat(fd, &st);
+ len = st.st_size;
+
+ fpath = pool;
+ off = size * pos;
+
+ /* ÅçÃÒ pos ¦ì¸m¸ê®Æªº¥¿½T©Ê */
+
+ if (len > off)
+ {
+ lseek(fd, off, SEEK_SET);
+ read(fd, fpath, size);
+ pos = fchk ? (*fchk) (fpath) : 1;
+ }
+ else
+ {
+ if (len)
+ {
+ pos = 0; /* ±qÀY§ä°_ */
+ }
+ else
+ {
+ /* ­Y­ì¥»¬OªÅÀɮסA¨º»ò©ñ±ó */
+ f_unlock(fd);
+ close(fd);
+ return -1;
+ }
+ }
+
+ /* ¤£¹ïªº¸Ü¡A­«ÀY§ä°_ */
+
+ if (!pos)
+ {
+ off = 0;
+ lseek(fd, off, SEEK_SET);
+ while (read(fd, fpath, size) == size)
+ {
+ if (pos = (*fchk) (fpath))
+ break;
+
+ off += size;
+ }
+ }
+
+ /* §ä¨ì¤§«á¡A§ó·s¸ê®Æ */
+
+ if (pos)
+ {
+ (*fref) (fpath, data);
+ lseek(fd, off, SEEK_SET);
+ write(fd, fpath, size);
+ }
+
+ /* flock(fd, LOCK_UN); */
+ /* Thor.981205: ¥Î fcntl ¨ú¥Nflock, POSIX¼Ð·Ç¥Îªk */
+ f_unlock(fd);
+
+ close(fd);
+
+ return 0;
+}
diff --git a/lib/rec_sync.c b/lib/rec_sync.c
new file mode 100644
index 0000000..f3c372f
--- /dev/null
+++ b/lib/rec_sync.c
@@ -0,0 +1,72 @@
+#include "dao.h"
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+
+int
+rec_sync(fpath, size, fsync, fchk)
+ char *fpath;
+ int size;
+ int (*fsync) ();
+ int (*fchk) ();
+{
+ int fd, fsize;
+ struct stat st;
+
+ fsize = 0;
+
+ if ((fd = open(fpath, O_RDWR, 0600)) < 0)
+ return fsize;
+
+ if (!fstat(fd, &st) && (fsize = st.st_size) > 0)
+ {
+ char *base;
+
+ base = (char *) malloc(fsize);
+ fsize = read(fd, base, fsize);
+
+ if (fsize >= size)
+ {
+ if (fchk) /* Àˬd¬O§_¦³¤£¥¿½Tªº¸ê®Æ */
+ {
+ char *head, *tail;
+
+ head = base;
+ tail = base + fsize;
+ while (head < tail)
+ {
+ if (fchk(head)) /* ¦¹µ§¸ê®Æ¥¿½T */
+ {
+ head += size;
+ continue;
+ }
+
+ /* ¦³°ÝÃDªº¸ê®Æ­n§R°£ */
+ tail -= size;
+ if (head >= tail)
+ break;
+ memcpy(head, tail, size);
+ }
+ fsize = tail - base;
+ }
+
+ if (fsize > 0)
+ {
+ if (fsize > size)
+ qsort(base, fsize / size, size, fsync);
+
+ lseek(fd, 0, SEEK_SET);
+ write(fd, base, fsize);
+ ftruncate(fd, fsize);
+ }
+ }
+ free(base);
+ }
+ close(fd);
+
+ if (fsize <= 0)
+ unlink(fpath);
+
+ return fsize;
+}
diff --git a/lib/rfc2047.c b/lib/rfc2047.c
new file mode 100644
index 0000000..b521660
--- /dev/null
+++ b/lib/rfc2047.c
@@ -0,0 +1,193 @@
+/*-------------------------------------------------------*/
+/* rfc2047.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : RFC 2047 QP/base64 encode */
+/* create : 03/04/11 */
+/* update : 03/05/19 */
+/* author : PaulLiu.bbs@bbs.cis.nctu.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include <stdio.h>
+
+
+#if 0 /* itoc.030411: ¨S¦³¸g RFC 2047 encode */
+void
+output_str(fp, prefix, str, charset, suffix)
+ FILE *fp;
+ char *prefix;
+ char *str;
+ char *charset;
+ char *suffix;
+{
+ fprintf(fp, "%s%s%s", prefix, str, suffix);
+}
+#endif
+
+
+/*-------------------------------------------------------*/
+/* RFC2047 QP encode */
+/*-------------------------------------------------------*/
+
+
+void
+output_rfc2047_qp(fp, prefix, str, charset, suffix)
+ FILE *fp;
+ char *prefix;
+ char *str;
+ char *charset;
+ char *suffix;
+{
+ int i, ch;
+ int blank = 1; /* 1:¥þ¥ÑªÅ¥Õ²Õ¦¨ */
+ static char tbl[16] = {'0','1','2','3','4','5','6','7','8','9', 'A','B','C','D','E','F'};
+
+ fputs(prefix, fp);
+
+ /* ¦pªG¦r¦ê¶}ÀY¦³ US_ASCII printable characters¡A¥i¥ý¦æ¿é¥X¡A³o¼Ë¤ñ¸û¦n¬Ý¡A¤]¤ñ¸û¬Û®e */
+ for (i = 0; ch = str[i]; i++)
+ {
+ if (ch != '=' && ch != '?' && ch != '_' && ch > '\x1f' && ch < '\x7f')
+ {
+ if (blank)
+ {
+ if (ch != ' ')
+ blank = 0;
+ else if (str[i + 1] == '\0') /* ­Y¥þ¬OªÅ¥Õ¡A³Ì«á¤@­Ó­nÂà½X */
+ break;
+ }
+ fprintf(fp, "%c", ch);
+ }
+ else
+ break;
+ }
+
+ if (ch != '\0') /* ¦pªG³£¨S¦³¯S®í¦r¤¸´Nµ²§ô */
+ {
+ /* ¶}©l encode */
+ fprintf(fp, "=?%s?Q?", charset); /* «ü©w¦r¶° */
+ for (; ch = str[i]; i++)
+ {
+ /* ¦pªG¬O non-printable ¦r¤¸´N­nÂà½X */
+ /* ½d³ò: '\x20' ~ '\x7e' ¬° printable, ¨ä¤¤ =, ?, _, ªÅ¥Õ, ¬°¯S®í²Å¸¹¤]­nÂà½X */
+
+ if (ch == '=' || ch == '?' || ch == '_' || ch <= '\x1f' || ch >= '\x7f')
+ fprintf(fp, "=%c%c", tbl[(ch >> 4) & '\x0f'], tbl[ch & '\x0f']);
+ else if (ch == ' ') /* ªÅ¥Õ¤ñ¸û¯S®í, Âন '_' ©Î "=20" */
+ fprintf(fp, "=20");
+ else
+ fprintf(fp, "%c", ch);
+ }
+ fputs("?=", fp);
+ }
+
+ fputs(suffix, fp);
+}
+
+
+#if 0
+
+/* output_rfc2047_qp() ¥i¥H¥þ³¡´«¦¨ output_rfc2047_base64()¡A¦pªG·Q´« encode ªº¸Ü */
+
+/*-------------------------------------------------------*/
+/* RFC2047 base64 encode */
+/*-------------------------------------------------------*/
+
+
+static int
+output_rfc2047_prefix(fp, str)
+ FILE *fp;
+ const unsigned char *str;
+{
+ int i, lastspace;
+
+ /* output prefix US_ASCII printable characters */
+ lastspace = -1;
+
+ /* step 1: find the last space */
+ for (i = 0; str[i] != '\0' && str[i] != '=' && str[i] != '?'
+ && str[i] != '_' && str[i] > '\x1f' && str[i] < '\x7f'; i++)
+ {
+ if (str[i] == ' ')
+ lastspace = i;
+ }
+ if (str[i] == '\0') /* if non special char then outout directly */
+ {
+ fprintf(fp, "%s", str);
+ return i;
+ }
+
+ /* step 2: output the prefix with last space */
+ for (i = 0; i <= lastspace; i++)
+ fprintf(fp, "%c", str[i]);
+ return i;
+}
+
+
+static void
+output_rfc2047_base64_3to4(a, b, c, oa, ob, oc, od)
+ unsigned char a, b, c;
+ char *oa, *ob, *oc, *od;
+{
+ static char tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ int i;
+
+ *oa = '=';
+ *ob = '=';
+ *oc = '=';
+ *od = '=';
+
+ i = (int)((a >> 2) & '\x3f');
+ *oa = tbl[i];
+ i = (int)((a << 4) & '\x30') | (int)((b >> 4) & '\x0f');
+ *ob = tbl[i];
+ if (b == '\0')
+ return;
+ i = (int)((b << 2) & '\x3c') | (int)((c >> 6) & '\x03');
+ *oc = tbl[i];
+ if (c == '\0')
+ return;
+ i = (int)(c & '\x3f');
+ *od = tbl[i];
+}
+
+
+void
+output_rfc2047_base64(fp, prefix, str, charset, suffix)
+ FILE *fp;
+ char *prefix;
+ const unsigned char *str;
+ const unsigned char *charset;
+ char *suffix;
+{
+ int i, j;
+ char a[3], oa[5];
+
+ fputs(prefix, fp);
+
+ /* output prefix US_ASCII printable characters */
+ i = output_rfc2047_prefix(fp, str);
+ if (str[i] == '\0')
+ return;
+
+ /* start encoding */
+ fprintf(fp, "=?%s?B?", charset);
+ for (; str[i] != '\0';)
+ {
+ memset(a, 0, sizeof(a));
+ for (j = 0; j < 3; j++)
+ {
+ a[j] = str[i];
+ if (str[i] != '\0')
+ i++;
+ }
+ output_rfc2047_base64_3to4(a[0], a[1], a[2],
+ &(oa[0]), &(oa[1]), &(oa[2]), &(oa[3]));
+ oa[4] = '\0';
+ fprintf(fp, "%s", oa);
+ }
+ fprintf(fp, "?=");
+
+ fputs(suffix, fp);
+}
+#endif
diff --git a/lib/shm.c b/lib/shm.c
new file mode 100644
index 0000000..8fecea6
--- /dev/null
+++ b/lib/shm.c
@@ -0,0 +1,34 @@
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+
+
+void *
+shm_new(shmkey, shmsize)
+ int shmkey, shmsize;
+{
+ void *shmptr;
+ int shmid;
+
+ shmid = shmget(shmkey, shmsize, 0);
+ if (shmid < 0)
+ {
+ shmid = shmget(shmkey, shmsize, IPC_CREAT | 0600);
+ if (shmid < 0)
+ exit(-1);
+ }
+ else
+ {
+ shmsize = 0;
+ }
+
+ shmptr = (void *) shmat(shmid, NULL, 0);
+ if (shmptr == (void *) -1)
+ exit(-2);
+
+ if (shmsize)
+ memset(shmptr, 0, shmsize);
+
+ return shmptr;
+}
diff --git a/lib/splay.c b/lib/splay.c
new file mode 100644
index 0000000..990abf8
--- /dev/null
+++ b/lib/splay.c
@@ -0,0 +1,174 @@
+/*-------------------------------------------------------*/
+/* lib/splay.c ( NTHU CS MapleBBS Ver 3.00 ) */
+/*-------------------------------------------------------*/
+/* author : opus.bbs@bbs.cs.nthu.edu.tw */
+/* target : splay-tree sort routines */
+/* create : 97/03/29 */
+/* update : 97/03/29 */
+/*-------------------------------------------------------*/
+
+
+#include <stdio.h>
+#include "splay.h"
+
+
+SplayNode *
+splay_in(top, data, compare)
+ SplayNode *top;
+ void *data;
+ int (*compare)();
+{
+ int splay_cmp;
+ SplayNode *node, *l, *r, *x, N;
+
+ node = (SplayNode *) malloc(sizeof(SplayNode));
+ node->data = data;
+
+ if (top == NULL)
+ {
+ node->left = node->right = NULL;
+ return node;
+ }
+
+ /* --------------------------------------------------- */
+ /* splay this splay tree */
+ /* --------------------------------------------------- */
+
+ N.left = N.right = NULL;
+ l = r = &N;
+
+ for (;;)
+ {
+ splay_cmp = compare(data, top->data);
+ if (splay_cmp < 0)
+ {
+ if (!(x = top->left))
+ break;
+ if ((splay_cmp = compare(data, x->data)) < 0)
+ {
+ /* rotate right */
+
+ top->left = x->right;
+ x->right = top;
+ top = x;
+ if (top->left == NULL)
+ break;
+ }
+ r->left = top; /* link right */
+ r = top;
+ top = top->left;
+ }
+ else if (splay_cmp > 0)
+ {
+ if (!(x = top->right))
+ break;
+ if ((splay_cmp = compare(data, x->data)) > 0)
+ {
+ /* rotate left */
+
+ top->right = x->left;
+ x->left = top;
+ top = x;
+ if (top->right == NULL)
+ break;
+ }
+ l->right = top; /* link left */
+ l = top;
+ top = top->right;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ l->right = top->left; /* assemble */
+ r->left = top->right;
+ top->left = N.right;
+ top->right = N.left;
+
+ /* --------------------------------------------------- */
+ /* construct this splay tree */
+ /* --------------------------------------------------- */
+
+ if (splay_cmp < 0)
+ {
+ node->left = top->left;
+ node->right = top;
+ top->left = NULL;
+ return node;
+ }
+
+ if (splay_cmp > 0)
+ {
+ node->right = top->right;
+ node->left = top;
+ top->right = NULL;
+ return node;
+ }
+
+ /* duplicate entry */
+
+ free(node);
+ return top;
+}
+
+
+#ifdef TEST
+
+typedef struct
+{
+ int i;
+} intnode;
+
+
+static void
+printint(a)
+ intnode *a;
+{
+ printf("%d\n", a->i);
+}
+
+
+static int
+compareint(a, b)
+ intnode *a, *b;
+{
+ return a->i - b->i;
+}
+
+
+static void
+splay_out(top)
+ SplayNode *top;
+{
+ if (top == NULL)
+ return;
+
+ splay_out(top->left);
+ printint(top->data);
+ splay_out(top->right);
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int i;
+ intnode *I;
+ SplayNode *top = NULL;
+
+ srand(time(NULL));
+ for (i = 0; i < 100; i++)
+ {
+ I = (intnode *) malloc(sizeof(intnode));
+ I->i = rand() % 1000;
+ top = splay_in(top, I, compareint);
+ }
+ splay_out(top);
+ return 0;
+}
+
+#endif /* TEST */
diff --git a/lib/str_ansi.c b/lib/str_ansi.c
new file mode 100644
index 0000000..81d411e
--- /dev/null
+++ b/lib/str_ansi.c
@@ -0,0 +1,37 @@
+/* ----------------------------------------------------- */
+/* ¥h°£ ANSI ±±¨î½X */
+/* ----------------------------------------------------- */
+
+
+void
+str_ansi(dst, str, max) /* strip ANSI code */
+ char *dst, *str;
+ int max;
+{
+ int ch, ansi;
+ char *tail;
+
+ for (ansi = 0, tail = dst + max - 1; ch = *str; str++)
+ {
+ if (ch == '\n')
+ {
+ break;
+ }
+ else if (ch == '\033')
+ {
+ ansi = 1;
+ }
+ else if (ansi)
+ {
+ if ((ch < '0' || ch > '9') && ch != ';' && ch != '[')
+ ansi = 0;
+ }
+ else
+ {
+ *dst++ = ch;
+ if (dst >= tail)
+ break;
+ }
+ }
+ *dst = '\0';
+}
diff --git a/lib/str_cat.c b/lib/str_cat.c
new file mode 100644
index 0000000..42723cd
--- /dev/null
+++ b/lib/str_cat.c
@@ -0,0 +1,15 @@
+void
+str_cat(dst, s1, s2)
+ char *dst;
+ char *s1;
+ char *s2;
+{
+ while (*dst = *s1)
+ {
+ s1++;
+ dst++;
+ }
+
+ while (*dst++ = *s2++)
+ ;
+}
diff --git a/lib/str_cmp.c b/lib/str_cmp.c
new file mode 100644
index 0000000..1fa82de
--- /dev/null
+++ b/lib/str_cmp.c
@@ -0,0 +1,19 @@
+int
+str_cmp(s1, s2)
+ char *s1, *s2;
+{
+ int c1, c2, diff;
+
+ do
+ {
+ c1 = *s1++;
+ c2 = *s2++;
+ if (c1 >= 'A' && c1 <= 'Z')
+ c1 |= 0x20;
+ if (c2 >= 'A' && c2 <= 'Z')
+ c2 |= 0x20;
+ if (diff = c1 - c2)
+ return (diff);
+ } while (c1);
+ return 0;
+}
diff --git a/lib/str_decode.c b/lib/str_decode.c
new file mode 100644
index 0000000..d2c2ab4
--- /dev/null
+++ b/lib/str_decode.c
@@ -0,0 +1,374 @@
+/*-------------------------------------------------------*/
+/* lib/str_decode.c ( NTHU CS MapleBBS Ver 3.00 ) */
+/*-------------------------------------------------------*/
+/* target : included C for QP/BASE64 decoding */
+/* create : 95/03/29 */
+/* update : 97/03/29 */
+/*-------------------------------------------------------*/
+
+
+/* ----------------------------------------------------- */
+/* QP code : "0123456789ABCDEF" */
+/* ----------------------------------------------------- */
+
+
+static int
+qp_code(x)
+ register int x;
+{
+ if (x >= '0' && x <= '9')
+ return x - '0';
+ if (x >= 'a' && x <= 'f')
+ return x - 'a' + 10;
+ if (x >= 'A' && x <= 'F')
+ return x - 'A' + 10;
+ return -1;
+}
+
+
+/* ------------------------------------------------------------------ */
+/* BASE64 : */
+/* "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" */
+/* ------------------------------------------------------------------ */
+
+
+static int
+base64_code(x)
+ register int x;
+{
+ if (x >= 'A' && x <= 'Z')
+ return x - 'A';
+ if (x >= 'a' && x <= 'z')
+ return x - 'a' + 26;
+ if (x >= '0' && x <= '9')
+ return x - '0' + 52;
+ if (x == '+')
+ return 62;
+ if (x == '/')
+ return 63;
+ return -1;
+}
+
+
+/* ----------------------------------------------------- */
+/* ¨ú encode / charset */
+/* ----------------------------------------------------- */
+
+
+static inline int
+isreturn(c)
+ unsigned char c;
+{
+ return c == '\r' || c == '\n';
+}
+
+
+static inline int
+isspace(c)
+ unsigned char c;
+{
+ return c == ' ' || c == '\t' || isreturn(c);
+}
+
+
+/* ¨úContent-Transfer-Encode ªº²Ä¤@­Ó¦r¤¸, ¨Ì·Ó¼Ð·Ç¥u¥i¯à¬O q,b,7,8 ³o¥|­Ó */
+char *
+mm_getencode(str, code)
+ unsigned char *str;
+ char *code;
+{
+ if (str)
+ {
+ /* skip leading space */
+ while (isspace(*str))
+ str++;
+
+ if (!str_ncmp(str, "quoted-printable", 16))
+ {
+ *code = 'q';
+ return str + 16;
+ }
+ if (!str_ncmp(str, "base64", 6))
+ {
+ *code = 'b';
+ return str + 6;
+ }
+ }
+
+ *code = 0;
+ return str;
+}
+
+
+/* ¨ú charset */
+void
+mm_getcharset(str, charset, size)
+ const char *str;
+ char *charset;
+ int size; /* charset size */
+{
+ char *ptr, delim;
+ int i;
+
+ *charset = '\0';
+
+ if (!str)
+ return;
+
+ if (!(ptr = (char *) strstr(str, "charset=")))
+ return;
+
+ ptr += 8;
+ delim = '\0';
+ i = 0;
+ size--; /* «O¯dªÅ¶¡µ¹ '\0' */
+
+ for (; *ptr && *ptr != delim && !isspace(*ptr); ptr++)
+ {
+ if (*ptr == '\"')
+ {
+ delim = *ptr;
+ continue;
+ }
+
+ charset[i] = *ptr;
+ if (++i >= size)
+ break;
+ }
+ charset[i] = '\0';
+
+ if (!str_cmp(charset, "iso-8859-1")) /* ¾ú¥v¥]¥ñ¤£¥i¥á */
+ *charset = '\0';
+}
+
+
+/* ----------------------------------------------------- */
+/* judge & decode QP / BASE64 */
+/* ----------------------------------------------------- */
+
+
+/* PaulLiu.030410:
+ RFC 2047 (Header) QP ³¡¤À¡A¸Ì­±³W©w '_' ªí¥Ü ' ' (US_ASCIIªºªÅ¥Õ)
+ ¦Ó RFC 2045 (Body) QP ³¡¤À¡A'_' ÁÙ¬O '_'¡A¨S¦³¯S®í¥Î³~
+ ©Ò¥H¦b¦¹ mmdecode ¤À¤G°¦¼g
+*/
+
+static int
+mmdecode_header(src, encode, dst) /* ¸Ñ Header ªº mmdecode */
+ unsigned char *src; /* Thor.980901: src©Mdst¥i¬Û¦P, ¦ýsrc¤@©w¦³?©Î\0µ²§ô */
+ unsigned char encode; /* Thor.980901: ª`·N, decode¥Xªºµ²ªG¤£·|¦Û¤v¥[¤W \0 */
+ unsigned char *dst;
+{
+ unsigned char *t;
+ int pattern, bits;
+ int ch;
+
+ t = dst;
+ encode |= 0x20; /* Thor: to lower */
+
+ switch (encode)
+ {
+ case 'q': /* Thor: quoted-printable */
+
+ while ((ch = *src) && ch != '?') /* Thor: Header ¸Ì­± 0 ©M '?' ³£¬O delimiter */
+ {
+ if (ch == '=')
+ {
+ int x, y;
+
+ x = *++src;
+ y = x ? *++src : 0;
+ if (isreturn(x))
+ continue;
+
+ if ((x = qp_code(x)) < 0 || (y = qp_code(y)) < 0)
+ return -1;
+
+ *t++ = (x << 4) + y;
+ }
+ else if (ch == '_') /* Header ­n§â '_' ´«¦¨ ' ' */
+ {
+ *t++ = ' ';
+ }
+ else
+ {
+ *t++ = ch;
+ }
+ src++;
+ }
+ return t - dst;
+
+ case 'b': /* Thor: base 64 */
+
+ /* Thor: pattern & bits are cleared outside while() */
+ pattern = 0;
+ bits = 0;
+
+ while ((ch = *src) && ch != '?') /* Thor: Header ¸Ì­± 0 ©M '?' ³£¬O delimiter */
+ {
+ int x;
+
+ x = base64_code(*src++);
+ if (x < 0) /* Thor: ignore everything not in the base64,=,.. */
+ continue;
+
+ pattern = (pattern << 6) | x;
+ bits += 6; /* Thor: 1 code gains 6 bits */
+ if (bits >= 8) /* Thor: enough to form a byte */
+ {
+ bits -= 8;
+ *t++ = (pattern >> bits) & 0xff;
+ }
+ }
+ return t - dst;
+ }
+
+ return -1;
+}
+
+
+int
+mmdecode(src, encode, dst) /* ¸Ñ Header ªº mmdecode */
+ unsigned char *src; /* Thor.980901: src©Mdst¥i¬Û¦P, ¦ýsrc¤@©w¦³?©Î\0µ²§ô */
+ unsigned char encode; /* Thor.980901: ª`·N, decode¥Xªºµ²ªG¤£·|¦Û¤v¥[¤W \0 */
+ unsigned char *dst;
+{
+ unsigned char *t;
+ int pattern, bits;
+ int ch;
+
+ t = dst;
+ encode |= 0x20; /* Thor: to lower */
+
+ switch (encode)
+ {
+ case 'q': /* Thor: quoted-printable */
+
+ while (ch = *src) /* Thor: 0 ¬O delimiter */
+ {
+ if (ch == '=')
+ {
+ int x, y;
+
+ x = *++src;
+ y = x ? *++src : 0;
+ if (isreturn(x))
+ continue;
+
+ if ((x = qp_code(x)) < 0 || (y = qp_code(y)) < 0)
+ return -1;
+
+ *t++ = (x << 4) + y;
+ }
+ else
+ {
+ *t++ = ch;
+ }
+ src++;
+ }
+ return t - dst;
+
+ case 'b': /* Thor: base 64 */
+
+ /* Thor: pattern & bits are cleared outside while() */
+ pattern = 0;
+ bits = 0;
+
+ while (ch = *src) /* Thor: 0 ¬O delimiter */
+ {
+ int x;
+
+ x = base64_code(*src++);
+ if (x < 0) /* Thor: ignore everything not in the base64,=,.. */
+ continue;
+
+ pattern = (pattern << 6) | x;
+ bits += 6; /* Thor: 1 code gains 6 bits */
+ if (bits >= 8) /* Thor: enough to form a byte */
+ {
+ bits -= 8;
+ *t++ = (pattern >> bits) & 0xff;
+ }
+ }
+ return t - dst;
+ }
+
+ return -1;
+}
+
+
+void
+str_decode(str)
+ unsigned char *str;
+{
+ int adj;
+ unsigned char *src, *dst;
+ unsigned char buf[512];
+
+ src = str;
+ dst = buf;
+ adj = 0;
+
+ while (*src && (dst - buf) < sizeof(buf) - 1)
+ {
+ if (*src != '=')
+ { /* Thor: not coded */
+ unsigned char *tmp = src;
+ while (adj && *tmp && isspace(*tmp))
+ tmp++;
+ if (adj && *tmp == '=')
+ { /* Thor: jump over space */
+ adj = 0;
+ src = tmp;
+ }
+ else
+ *dst++ = *src++;
+ }
+ else /* Thor: *src == '=' */
+ {
+ unsigned char *tmp = src + 1;
+ if (*tmp == '?') /* Thor: =? coded */
+ {
+ /* "=?%s?Q?" for QP, "=?%s?B?" for BASE64 */
+ tmp++;
+ while (*tmp && *tmp != '?')
+ tmp++;
+ if (*tmp && tmp[1] && tmp[2] == '?') /* Thor: *tmp == '?' */
+ {
+ int i = mmdecode_header(tmp + 3, tmp[1], dst);
+ if (i >= 0)
+ {
+ tmp += 3; /* Thor: decode's src */
+ while (*tmp && *tmp++ != '?'); /* Thor: no ? end, mmdecode_header -1 */
+ /* Thor.980901: 0 ¤]ºâ decode µ²§ô */
+ if (*tmp == '=')
+ tmp++;
+ src = tmp; /* Thor: decode over */
+ dst += i;
+ adj = 1; /* Thor: adjcent */
+ }
+ }
+ }
+
+ while (src != tmp) /* Thor: not coded */
+ *dst++ = *src++;
+ }
+ }
+ *dst = 0;
+ strcpy(str, buf);
+}
+
+
+#if 0
+int
+main()
+{
+ char buf[1024] = "=?Big5?B?pl7C0CA6IFtNYXBsZUJCU11UbyB5dW5sdW5nKDE4SzRGTE0pIFtWQUxJ?=\n\t=?Big5?B?RF0=?=";
+
+ str_decode(buf);
+ puts(buf);
+
+ buf[mmdecode("=A7=DA=A4@=AA=BD=B8I=A4=A3=A8=EC=A7=DA=BE=C7=AA=F8", 'q', buf)] = '\0';
+ puts(buf);
+}
+#endif
diff --git a/lib/str_folder.c b/lib/str_folder.c
new file mode 100644
index 0000000..aaa2a55
--- /dev/null
+++ b/lib/str_folder.c
@@ -0,0 +1,19 @@
+void
+str_folder(fpath, folder, fname)
+ char *fpath;
+ char *folder;
+ char *fname;
+{
+ int ch;
+ char *token;
+
+ while (ch = *folder++)
+ {
+ *fpath++ = ch;
+ if (ch == '/')
+ token = fpath;
+ }
+ if (*token != '.')
+ token -= 2;
+ strcpy(token, fname);
+}
diff --git a/lib/str_fpath.c b/lib/str_fpath.c
new file mode 100644
index 0000000..415ee6f
--- /dev/null
+++ b/lib/str_fpath.c
@@ -0,0 +1,109 @@
+void
+setdirpath(fpath, direct, fname)
+ char *fpath, *direct, *fname;
+{
+ int ch;
+ char *target;
+
+ while (ch = *direct)
+ {
+ *fpath++ = ch;
+ if (ch == '/')
+ target = fpath;
+ direct++;
+ }
+
+ strcpy(target, fname);
+}
+
+#if 0
+int
+is_fname(str)
+ char *str;
+{
+ int ch;
+
+ ch = *str;
+ if (ch == '/')
+ return 0;
+
+ do
+ {
+ if (!is_alnum(ch) && !strchr("-._/+@", ch))
+ return 0;
+ } while (ch = *++str);
+ return 1;
+}
+
+
+/* ----------------------------------------------------- */
+/* transform to real path & security check */
+/* ----------------------------------------------------- */
+
+
+int
+is_fpath(path)
+ char *path;
+{
+ int ch, level;
+ char *source, *target;
+
+ level = 0;
+ source = target = path;
+
+
+ for (;;)
+ {
+ ch = *source;
+
+ if (ch == '/')
+ {
+ int next;
+
+ next = source[1];
+
+ if (next == '/')
+ {
+ return 0; /* [//] */
+ }
+ else if (next == '.')
+ {
+ next = source[2];
+
+ if (next == '/')
+ return 0; /* [/./] */
+
+ if (next == '.' && source[3] == '/')
+ {
+ /* -------------------------- */
+ /* abc/xyz/../def ==> abc/def */
+ /* -------------------------- */
+
+ for (;;)
+ {
+ if (target <= path)
+ return 0;
+
+ target--;
+ if (*target == '/')
+ break;
+ }
+
+ source += 3;
+ continue;
+ }
+ }
+
+ level++;
+ }
+
+ *target = ch;
+
+ if (ch == 0)
+ return level;
+
+ target++;
+ source++;
+ }
+}
+#endif
diff --git a/lib/str_from.c b/lib/str_from.c
new file mode 100644
index 0000000..069a156
--- /dev/null
+++ b/lib/str_from.c
@@ -0,0 +1,92 @@
+/* ---------------------------------------------------- */
+/* E-mail address format */
+/* ---------------------------------------------------- */
+/* 1. user@domain */
+/* 2. <user@domain> */
+/* 3. user@domain (nick) */
+/* 4. user@domain ("nick") */
+/* 5. nick <user@domain> */
+/* 6. "nick" <user@domain> */
+/* ---------------------------------------------------- */
+
+
+#include "dao.h"
+
+
+int
+str_from(from, addr, nick)
+ char *from, *addr, *nick;
+{
+ char *str, *ptr, *langle;
+ int cc;
+
+ *nick = 0;
+
+ langle = ptr = NULL;
+
+ for (str = from; cc = *str; str++)
+ {
+ if (cc == '<')
+ langle = str;
+ else if (cc == '@')
+ ptr = str;
+ }
+
+ if (ptr == NULL)
+ {
+ strcpy(addr, from);
+ return -1;
+ }
+
+ if (langle && langle < ptr && str[-1] == '>')
+ {
+ /* case 2/5/6 : name <mail_addr> */
+
+ str[-1] = 0;
+ if (langle > from)
+ {
+ ptr = langle - 2;
+ if (*from == '"')
+ {
+ from++;
+ if (*ptr == '"')
+ ptr--;
+ }
+ if (*from == '(')
+ {
+ from++;
+ if (*ptr == ')')
+ ptr--;
+ }
+ ptr[1] = '\0';
+ strcpy(nick, from);
+ str_decode(nick);
+ }
+
+ from = langle + 1;
+ }
+ else
+ {
+ /* case 1/3/4 */
+
+ if (*--str == ')')
+ {
+ if (str[-1] == '"')
+ str--;
+ *str = 0;
+
+ if (ptr = (char *) strchr(from, '('))
+ {
+ ptr[-1] = 0;
+ if (*++ptr == '"')
+ ptr++;
+
+ strcpy(nick, ptr);
+ str_decode(nick);
+ }
+ }
+ }
+
+ strcpy(addr, from);
+ return 0;
+}
diff --git a/lib/str_has.c b/lib/str_has.c
new file mode 100644
index 0000000..e07b917
--- /dev/null
+++ b/lib/str_has.c
@@ -0,0 +1,30 @@
+int /* >=1:¦b¦W³æªº­þ¤@­Ó 0:¤£¦b¦W³æ¤º */
+str_has(list, tag, len)
+ char *list;
+ char *tag;
+ int len; /* strlen(tag) */
+{
+ int cc, priority;
+ char *str;
+
+ priority = 1;
+ str = tag;
+ do
+ {
+ cc = list[len]; /* itoc.030730.µù¸Ñ: ¥i¯à·|«ü¨ì¶W¹L list ªºªø«×¥H¥~¥h¤F¡A¤£¹L¨S®t */
+ if ((!cc || cc == '/') && !str_ncmp(list, str, len))
+ {
+ return priority;
+ }
+ while (cc = *list++)
+ {
+ if (cc == '/')
+ {
+ priority++;
+ break;
+ }
+ }
+ } while (cc);
+
+ return 0;
+}
diff --git a/lib/str_hash.c b/lib/str_hash.c
new file mode 100644
index 0000000..f513233
--- /dev/null
+++ b/lib/str_hash.c
@@ -0,0 +1,13 @@
+int
+str_hash(str, seed)
+ char *str;
+ int seed;
+{
+ int cc;
+
+ while (cc = *str++)
+ {
+ seed = (seed << 5) - seed + cc; /* 31 * seed + cc */
+ }
+ return (seed & 0x7fffffff);
+}
diff --git a/lib/str_lower.c b/lib/str_lower.c
new file mode 100644
index 0000000..e179c3e
--- /dev/null
+++ b/lib/str_lower.c
@@ -0,0 +1,14 @@
+void
+str_lower(dst, src)
+ char *dst, *src;
+{
+ int ch;
+
+ do
+ {
+ ch = *src++;
+ if (ch >= 'A' && ch <= 'Z')
+ ch |= 0x20;
+ *dst++ = ch;
+ } while (ch);
+}
diff --git a/lib/str_lowest.c b/lib/str_lowest.c
new file mode 100644
index 0000000..e36d310
--- /dev/null
+++ b/lib/str_lowest.c
@@ -0,0 +1,17 @@
+void
+str_lowest(dst, src)
+ char *dst, *src;
+{
+ int ch;
+ int in_chi = 0; /* 1: «e¤@½X¬O¤¤¤å¦r */
+
+ do
+ {
+ ch = *src++;
+ if (in_chi || ch & 0x80)
+ in_chi ^= 1;
+ else if (ch >= 'A' && ch <= 'Z')
+ ch |= 0x20;
+ *dst++ = ch;
+ } while (ch);
+}
diff --git a/lib/str_ncmp.c b/lib/str_ncmp.c
new file mode 100644
index 0000000..56995fc
--- /dev/null
+++ b/lib/str_ncmp.c
@@ -0,0 +1,26 @@
+int
+str_ncmp(s1, s2, n)
+ char *s1, *s2;
+ int n;
+{
+ int c1, c2;
+
+ while (n--)
+ {
+ c1 = *s1++;
+ if (c1 >= 'A' && c1 <= 'Z')
+ c1 |= 0x20;
+
+ c2 = *s2++;
+ if (c2 >= 'A' && c2 <= 'Z')
+ c2 |= 0x20;
+
+ if (c1 -= c2)
+ return (c1);
+
+ if (!c2)
+ break;
+ }
+
+ return 0;
+}
diff --git a/lib/str_ncpy.c b/lib/str_ncpy.c
new file mode 100644
index 0000000..b7a4d08
--- /dev/null
+++ b/lib/str_ncpy.c
@@ -0,0 +1,22 @@
+/*
+ * str_ncpy() - similar to strncpy(3) but terminates string always with '\0'
+ * if n != 0, and doesn't do padding
+ */
+
+
+void
+str_ncpy(dst, src, n)
+ char *dst;
+ char *src;
+ int n;
+{
+ char *end;
+
+ end = dst + n - 1;
+
+ do
+ {
+ n = (dst >= end) ? 0 : *src++;
+ *dst++ = n;
+ } while (n);
+}
diff --git a/lib/str_passwd.c b/lib/str_passwd.c
new file mode 100644
index 0000000..19603b2
--- /dev/null
+++ b/lib/str_passwd.c
@@ -0,0 +1,52 @@
+#define PASSLEN 13
+
+
+/* ----------------------------------------------------- */
+/* password encryption */
+/* ----------------------------------------------------- */
+
+
+char *crypt();
+static char pwbuf[PASSLEN + 1];
+
+
+char *
+genpasswd(pw)
+ char *pw;
+{
+ char saltc[2];
+ int i, c;
+
+ if (!*pw)
+ return pw;
+
+ 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);
+}
+
+
+/* Thor.990214: µù¸Ñ: ¦X±K½X®É, ¶Ç¦^0 */
+int
+chkpasswd(passwd, test)
+ char *passwd, *test;
+{
+ char *pw;
+
+ /* if(!*passwd) return -1 */ /* Thor.990416: ©È¦³®Épasswd¬OªÅªº */
+ str_ncpy(pwbuf, test, sizeof(pwbuf));
+ pw = crypt(pwbuf, passwd);
+ return (strncmp(pw, passwd, PASSLEN));
+}
diff --git a/lib/str_stamp.c b/lib/str_stamp.c
new file mode 100644
index 0000000..82236fd
--- /dev/null
+++ b/lib/str_stamp.c
@@ -0,0 +1,23 @@
+/* ------------------------------------------ */
+/* mail / post ®É¡A¨Ì¾Ú®É¶¡«Ø¥ßÀɮסA¥[¤W¶lÂW */
+/* ------------------------------------------ */
+/* Input: fpath = directory; */
+/* Output: fpath = full path; */
+/* ------------------------------------------ */
+
+
+#include <time.h>
+
+
+void
+str_stamp(str, chrono)
+ char *str;
+ time_t *chrono;
+{
+ struct tm *ptime;
+
+ ptime = localtime(chrono);
+ /* Thor.990329: y2k */
+ sprintf(str, "%02d/%02d/%02d",
+ ptime->tm_year % 100, ptime->tm_mon + 1, ptime->tm_mday);
+}
diff --git a/lib/str_str.c b/lib/str_str.c
new file mode 100644
index 0000000..71f9a8d
--- /dev/null
+++ b/lib/str_str.c
@@ -0,0 +1,41 @@
+#include "dao.h"
+
+
+char *
+str_str(str, tag)
+ char *str;
+ char *tag; /* non-empty lower case pattern */
+{
+ int cc, c1, c2;
+ char *p1, *p2;
+
+ cc = *tag++;
+
+ while (c1 = *str)
+ {
+ if (c1 >= 'A' && c1 <= 'Z')
+ c1 |= 0x20;
+
+ if (c1 == cc)
+ {
+ p1 = str;
+ p2 = tag;
+
+ do
+ {
+ c2 = *p2;
+ if (!c2)
+ return str;
+
+ p2++;
+ c1 = *++p1;
+ if (c1 >= 'A' && c1 <= 'Z')
+ c1 |= 0x20;
+ } while (c1 == c2);
+ }
+
+ str++;
+ }
+
+ return NULL;
+}
diff --git a/lib/str_str_kmp.c b/lib/str_str_kmp.c
new file mode 100644
index 0000000..7695865
--- /dev/null
+++ b/lib/str_str_kmp.c
@@ -0,0 +1,119 @@
+/* a alternative str_str() using KMP algorithm. */
+/* English case independent and BIG5 Chinese supported */
+
+#include <stdlib.h>
+
+
+void
+str_expand(dst, src) /* ±N char Âର short¡A¨Ã±N­^¤åÅܤp¼g */
+ char *dst, *src;
+{
+ int ch;
+ int in_chi = 0; /* 1: «e¤@½X¬O¤¤¤å¦r */
+
+ do
+ {
+ ch = *src++;
+
+ if (in_chi || ch & 0x80)
+ {
+ in_chi ^= 1;
+ }
+ else
+ {
+ if (ch >= 'A' && ch <= 'Z')
+ ch |= 0x20;
+ *dst++ = 0;
+ }
+ *dst++ = ch;
+ } while (ch);
+}
+
+
+void
+str_str_kmp_tbl(pat, tbl)
+ const short *pat;
+ int *tbl;
+{
+ register short c;
+ register int i, j;
+
+ tbl[0] = -1;
+ for (j = 1; c = pat[j]; j++)
+ {
+ i = tbl[j - 1];
+ while (i >= 0 && c != pat[i + 1])
+ i = tbl[i];
+ tbl[j] = (c == pat[i + 1]) ? i + 1 : -1;
+ }
+}
+
+
+const int
+str_str_kmp(str, pat, tbl)
+ const short *str;
+ const short *pat;
+ const int *tbl;
+{
+ register const short *i;
+ register int j;
+
+ for (i = str, j = 0; *i && pat[j];)
+ {
+ if (*i == pat[j])
+ {
+ j++;
+ }
+ else if (j)
+ {
+ j = tbl[j - 1] + 1;
+ continue; /* ¤£»Ý­n i++ */
+ }
+ i++;
+ }
+
+ /* match */
+ if (!pat[j])
+ return 1;
+
+ return 0;
+}
+
+
+#undef TEST
+
+#ifdef TEST
+static void
+try_match(str, key)
+ char *str, *key;
+{
+ short a[256], b[256]; /* °²³] 256 ¤w¨¬°÷ */
+ int tbl[256];
+
+ str_expand(a, str);
+ str_expand(b, key);
+
+ str_str_kmp_tbl(key, tbl);
+ printf("¡u%s¡v %s¥]¬A ¡u%s¡v\n",
+ str, str_str_kmp(a, b, tbl) ? "" : "¤£", key);
+}
+
+
+int
+main()
+{
+ try_match("¦nªº¹q¼v", "º¹");
+ try_match("¦nªº¹q¼v", "N");
+ try_match("¦nªº¹q¼v", "n");
+ try_match("¦nªº¹q¼v", "¦nªº");
+
+ try_match("x¦nªºx¹q¼v", "ºx¹q");
+ try_match("x¦nªºx¹q¼v", "ªºx");
+ try_match("x¦nªºx¹q¼v", "ªºX");
+ try_match("x¦nªºX¹q¼v", "ªºx");
+
+ try_match("abx¦nªºx¹q¼v", "x¹q");
+
+ return 0;
+}
+#endif
diff --git a/lib/str_sub.c b/lib/str_sub.c
new file mode 100644
index 0000000..225ef2b
--- /dev/null
+++ b/lib/str_sub.c
@@ -0,0 +1,55 @@
+#include "dao.h"
+
+
+char *
+str_sub(str, tag)
+ char *str;
+ char *tag; /* non-empty lowest case pattern */
+{
+ int cc, c1, c2;
+ char *p1, *p2;
+ int in_chi = 0; /* 1: «e¤@½X¬O¤¤¤å¦r */
+ int in_chii; /* 1: «e¤@½X¬O¤¤¤å¦r */
+
+ cc = *tag++;
+
+ while (c1 = *str)
+ {
+ if (in_chi)
+ {
+ in_chi ^= 1;
+ }
+ else
+ {
+ if (c1 & 0x80)
+ in_chi ^= 1;
+ else if (c1 >= 'A' && c1 <= 'Z')
+ c1 |= 0x20;
+
+ if (c1 == cc)
+ {
+ p1 = str;
+ p2 = tag;
+ in_chii = in_chi;
+
+ do
+ {
+ c2 = *p2;
+ if (!c2)
+ return str;
+
+ p2++;
+ c1 = *++p1;
+ if (in_chii || c1 & 0x80)
+ in_chii ^= 1;
+ else if (c1 >= 'A' && c1 <= 'Z')
+ c1 |= 0x20;
+ } while (c1 == c2);
+ }
+ }
+
+ str++;
+ }
+
+ return NULL;
+}
diff --git a/lib/str_tail.c b/lib/str_tail.c
new file mode 100644
index 0000000..77e4bc9
--- /dev/null
+++ b/lib/str_tail.c
@@ -0,0 +1,10 @@
+char *
+str_tail(str)
+ char *str;
+{
+ while (*str)
+ {
+ str++;
+ }
+ return str;
+}
diff --git a/lib/str_time.c b/lib/str_time.c
new file mode 100644
index 0000000..59bf986
--- /dev/null
+++ b/lib/str_time.c
@@ -0,0 +1,60 @@
+#include <time.h>
+
+
+static char datemsg[40];
+
+
+char *
+Atime(clock) /* Thor.990125: °²¸Ë ARPANET ®É¶¡®æ¦¡ */
+ time_t *clock;
+{
+ /* ARPANET format: Thu, 11 Feb 1999 06:00:37 +0800 (CST) */
+ /* strftime(datemsg, 40, "%a, %d %b %Y %T %Z", localtime(clock)); */
+ /* Thor.990125: time zoneªº¶Ç¦^­È¤£ª¾©MARPANET®æ¦¡¬O§_¤@¼Ë,¥ýµwµ¹,¦Psendmail*/
+ strftime(datemsg, 40, "%a, %d %b %Y %T +0800 (CST)", localtime(clock));
+ return (datemsg);
+}
+
+
+char *
+Btime(clock) /* BBS ®É¶¡®æ¦¡ */
+ time_t *clock;
+{
+ struct tm *t = localtime(clock);
+
+ sprintf(datemsg, "%d/%02d/%02d %.3s %02d:%02d:%02d",
+ t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
+ "SunMonTueWedThuFriSat" + (t->tm_wday * 3),
+ t->tm_hour, t->tm_min, t->tm_sec);
+ return (datemsg);
+}
+
+char *
+Bdifftime(sec)
+ int sec;
+{
+ char *buf;
+
+ int days, hours, minutes;
+
+ days = sec / (60 * 60 * 24);
+ sec %= (60 * 60 * 24);
+
+ hours = sec / (60 * 60);
+ sec %= (60 * 60);
+
+ minutes = sec / 60;
+ sec %= 60;
+
+ sprintf (buf, "%d ¤é %d ®É %d ¤À %d ¬í", days, hours, minutes, sec);
+ return buf;
+}
+
+char *
+Now()
+{
+ time_t now;
+
+ time(&now);
+ return Btime(&now);
+}
diff --git a/lib/str_trim.c b/lib/str_trim.c
new file mode 100644
index 0000000..63f4170
--- /dev/null
+++ b/lib/str_trim.c
@@ -0,0 +1,16 @@
+void
+str_trim(buf) /* remove trailing space */
+ char *buf;
+{
+ char *p = buf;
+
+ while (*p)
+ p++;
+ while (--p >= buf)
+ {
+ if (*p == ' ')
+ *p = '\0';
+ else
+ break;
+ }
+}
diff --git a/lib/str_ttl.c b/lib/str_ttl.c
new file mode 100644
index 0000000..14b7f7f
--- /dev/null
+++ b/lib/str_ttl.c
@@ -0,0 +1,14 @@
+char *
+str_ttl(title)
+ char *title;
+{
+ if ((title[2] == ':') &&
+ ((title[0] == 'R' && title[1] == 'e') || (title[0] == 'F' && title[1] == 'w')))
+ {
+ title += 3;
+ if (*title == ' ')
+ title++;
+ }
+
+ return title;
+}
diff --git a/lib/xsort.c b/lib/xsort.c
new file mode 100644
index 0000000..fa84b3e
--- /dev/null
+++ b/lib/xsort.c
@@ -0,0 +1,198 @@
+#include <sys/types.h>
+#include <stdlib.h>
+
+
+#define min(a, b) (a) < (b) ? a : b
+#undef TEST
+
+
+/* Qsort routine from Bentley & McIlroy's "Engineering a Sort Function". */
+
+
+#define swapcode(TYPE, parmi, parmj, n) { \
+ long i = (n) / sizeof (TYPE); \
+ register TYPE *pi = (TYPE *) (parmi); \
+ register TYPE *pj = (TYPE *) (parmj); \
+ do { \
+ register TYPE t = *pi; \
+ *pi++ = *pj; \
+ *pj++ = t; \
+ } while (--i > 0); \
+}
+
+
+#define SWAPINIT(a, es) \
+ swaptype = (((char *)a - (char *)0) % sizeof(long) || \
+ es % sizeof(long)) ? 2 : (es == sizeof(long)? 0 : 1);
+
+
+static inline void
+swapfunc(a, b, n, swaptype)
+ char *a, *b;
+ int n, swaptype;
+{
+ if (swaptype <= 1)
+ swapcode(long, a, b, n)
+ else
+ swapcode(char, a, b, n)
+}
+
+
+#define swap(a, b) \
+ if (swaptype == 0) { \
+ long t = *(long *)(a); \
+ *(long *)(a) = *(long *)(b); \
+ *(long *)(b) = t; \
+ } else \
+ swapfunc(a, b, es, swaptype)
+
+
+#define vecswap(a, b, n) if ((n) > 0) swapfunc(a, b, n, swaptype)
+
+
+static inline char *
+med3(a, b, c, cmp)
+ char *a, *b, *c;
+ int (*cmp) ();
+{
+ return cmp(a, b) < 0 ?
+ (cmp(b, c) < 0 ? b : (cmp(a, c) < 0 ? c : a))
+ : (cmp(b, c) > 0 ? b : (cmp(a, c) < 0 ? a : c));
+}
+
+
+void
+xsort(a, n, es, cmp)
+ void *a;
+ size_t n, es;
+ int (*cmp) ();
+{
+ char *pa, *pb, *pc, *pd, *pl, *pm, *pn;
+ int d, r, swaptype, swap_cnt;
+
+ SWAPINIT(a, es);
+
+loop:
+
+ swap_cnt = 0;
+ if (n < 7)
+ {
+ for (pm = a + es; pm < (char *) a + n * es; pm += es)
+ for (pl = pm; pl > (char *) a && cmp(pl - es, pl) > 0;
+ pl -= es)
+ swap(pl, pl - es);
+ return;
+ }
+
+ pm = a + (n / 2) * es;
+
+ if (n > 7)
+ {
+ pl = a;
+ pn = a + (n - 1) * es;
+ if (n > 40)
+ {
+ d = (n >> 3) * es;
+ pl = med3(pl, pl + d, pl + d + d, cmp);
+ pm = med3(pm - d, pm, pm + d, cmp);
+ pn = med3(pn - 2 * d, pn - d, pn, cmp);
+ }
+ pm = med3(pl, pm, pn, cmp);
+ }
+ swap(a, pm);
+ pa = pb = a + es;
+
+ pc = pd = a + (n - 1) * es;
+ for (;;)
+ {
+ while (pb <= pc && (r = cmp(pb, a)) <= 0)
+ {
+ if (r == 0)
+ {
+ swap_cnt = 1;
+ swap(pa, pb);
+ pa += es;
+ }
+ pb += es;
+ }
+ while (pb <= pc && (r = cmp(pc, a)) >= 0)
+ {
+ if (r == 0)
+ {
+ swap_cnt = 1;
+ swap(pc, pd);
+ pd -= es;
+ }
+ pc -= es;
+ }
+ if (pb > pc)
+ break;
+ swap(pb, pc);
+ swap_cnt = 1;
+ pb += es;
+ pc -= es;
+ }
+
+ if (swap_cnt == 0)
+ { /* Switch to insertion sort */
+ for (pm = a + es; pm < (char *) a + n * es; pm += es)
+ for (pl = pm; pl > (char *) a && cmp(pl - es, pl) > 0; pl -= es)
+ swap(pl, pl - es);
+ return;
+ }
+
+ pn = a + n * es;
+ r = min(pa - (char *) a, pb - pa);
+ vecswap(a, pb - r, r);
+
+ r = min(pd - pc, pn - pd - es);
+ vecswap(pb, pn - r, r);
+
+ if ((r = pb - pa) > es)
+ xsort(a, r / es, es, cmp);
+
+ if ((r = pd - pc) > es)
+ {
+ /* Iterate rather than recurse to save stack space */
+ a = pn - r;
+ n = r / es;
+ goto loop;
+ }
+ /* xsort(pn - r, r / es, es, cmp); */
+}
+
+
+#ifdef TEST
+
+#define MMM (0x40000)
+
+static int
+int_cmp(a, b)
+ int *a;
+ int *b;
+{
+ return *a - *b;
+}
+
+
+main()
+{
+ int *x, *y, *z, n;
+
+ x = malloc(MMM * sizeof(int));
+ if (!x)
+ return;
+
+ y = x;
+ z = x + MMM;
+
+ n = time(0) & (0x40000 -1) /* 16387 */;
+
+ do
+ {
+ *x = n = (n * 10001) & (0x100000 - 1);
+ } while (++x < z);
+
+ xsort(y, MMM, sizeof(int), int_cmp);
+}
+#endif
diff --git a/lib/xwrite.c b/lib/xwrite.c
new file mode 100644
index 0000000..23d2df1
--- /dev/null
+++ b/lib/xwrite.c
@@ -0,0 +1,18 @@
+int
+xwrite(fd, data, size)
+ int fd;
+ char *data;
+ int size;
+{
+ int cc;
+
+ while (size > 0)
+ {
+ cc = write(fd, data, size);
+ if (cc < 0)
+ return cc;
+ data += cc;
+ size -= cc;
+ }
+ return 0;
+}
diff --git a/maple/CHANGE b/maple/CHANGE
new file mode 100644
index 0000000..fed4f25
--- /dev/null
+++ b/maple/CHANGE
@@ -0,0 +1,1766 @@
+/*-------------------------------------------------------*/
+/* CHANGE ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : difference from MapleBBS Ver 3.10 */
+/* create : 00/02/01 */
+/* update : / / */
+/* author : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+ ¥» BBS ª©¥»¬O¥Ñ NTHU CS MapleBBS Ver 3.10 §ïª©¦Ó¨Ó¡C
+
+ ¨ä¤¤°Ñ¦Ò bbs.cs.nthu.edu.tw [plan] ªO ½Ñ¦ì¤j¤jªº´¼¼zµ²´¹¡A
+
+ ¥H¤Î WindTop WD Ptt Firebird µ¥ BBS µ{¦¡¡C
+
+ ©Ò¦³´¼¼z°]²£§¡ÄÝ©ó­ì§@ªÌ¡C
+
+
+
+[2000/02/ ] bbsd.c §ï¦¨¤W¯¸¤@¤ÀÄÁ¥H«á¤~¼W¥[¤W¯¸¦¸¼Æ¡C
+ acct.c perm.h ·s¼W¡yµù¥U²Õªø¡zÅv­­ PERM_REGISTRAR¡Aµ¹¼f®Öµù¥U³æ¥Î¡C
+ post.c ¤å³¹§@ªÌ¥i¥H edit ¦Û¤vªº¤å³¹¡C
+
+[2000/02/13] post.c ­ì§@ªÌ¥i¥H«ö T ­×§ï¦Û¤vªº¤å³¹¼ÐÃD¡C
+ talk.c ¦n¤Í¼s¼½¥X²{¼s¼½ªÌªº id¡C¬°¥H¥Ü°Ï§O¡A¤ô²y¥X²{ id¡A¼s¼½¥X²{ id >¡C
+
+[2000/02/19] acct.c menu.c maple.p ¥[/´î PERM_BM µ{¦¡¡C
+
+[2000/02/20] gem.c ·s¼W¤pªO¥D¥\¯à¡C
+
+[2000/02/25] talk.c ·s¼W¨Ï¥ÎªÌ¦W³æ¦n¤Í¸m«e¨ÌID±Æ§Ç¡A¥B³]¬°¹w³]­È¡C
+
+[2000/02/26] gem.c ºëµØ°Ï­­¨î¯Å¤å³¹¦WºÙ«O±K¡C
+
+[2000/02/27] post.c ¦ê±µ·j´M¦pªG¨S¦³¿é¤J¼ÐÃD¡B§@ªÌ¡A«h·j´M´å¼Ð©Ò¦b¤å³¹¼ÐÃD¡C(¤w¨ú®ø)
+ post.c ·s¼W¦ê¦C·j´M¤U [ ªº¥\¯à¡C
+
+[2000/02/28] gem.c ºëµØ°Ï¦¬¿ý¤è¦¡±q link ÅÜ copy¡AÁ×§K­×§ï¨ìªO¤Wªº¤å³¹¡C
+
+[2000/03/03] post.c ­×¥¿¦ê±µ·j´M¼Ò¦¡«á h Â÷¶}·|¦³°ÝÃD¡C
+
+[2000/03/04] xover.c ¾\ۤ峹®É¤£´`Àô¾\Ū¡C
+
+[2000/03/05] account.c ¤W¯¸¤H¦¸°O¿ý ¦~¥÷ % 100¡C
+
+[2000/03/12] acct.c board.c ªO¥D¥i¥H­×§ï¤¤¤åªO¦W±Ô­z¡C
+ talk.c ²á¤Ñ«Ç·s¼W«ü¥O HOME/END/Ctrl+A/Ctrl+E ²¾¨ì¦æÀY/¦æ§À¡C
+
+[2000/03/15] config.h acct.c pop3_check.c ·s¼W POP3 ¦Û°Ê»{ÃÒ¡C
+
+[2000/03/17] post.c ­×¥¿ more §¹²¦®É¤£·|¥X²{ help¡C
+
+[2000/03/19] xover.c every_Z ³Ì¦h¤G¼h¡C
+ edit.c ·s¼W ¤å³¹½s¿è®É Ctrl+W ²Å¸¹¿é¤J¤u¨ã¡C
+ xover.c ­×¥¿­­¨î¯Å¤å³¹¤£±oÂà±H¡B¶×¤J¼È¦sÀÉ¡B Z-modem ¤U¸ü¡C
+ post.c ¦Û¤v­èµoªí«áªº¤å³¹¤£­n¥X²{¥¼¾\Ūªº¡Ï¸¹¡C
+ bbsd.c guest ¶Ã¼Æ¨ú¼ÊºÙ¡C
+
+[2000/03/20] acct.c struch.h User -> Setup ·s¼W¤£¨Ï¥Îñ¦WÀÉ UFO_NOSIGN¡C
+ acct.c struct.h edit.c User -> Setup ·s¼WÅã¥Üñ¦WÀÉ UFO_SHOWSIGN¡C
+ bbsd.c Åܰʷs¨Ï¥ÎªÌªº¹w³]ºX¼Ð¡C
+
+[2000/03/25] global.h mail.c ­×§ï¸s²Õ±H«H»P¦^¤ô²yµ{¦¡½Ä¬ðªº°ÝÃD¡C
+
+[2000/03/28] modes.h ½s¨t²ÎÀÉ®× & ½s­Ó¤HÀÉ®× ®É­Ô¨ú®ø¥i¥H³Q talk request¡C
+
+[2000/04/07] bbsd.c board.c 0Announce ¦³·s¤å³¹®É±j­¢¾\Ū¡C
+ acct.c ³]©w ²¤Æ¶i/Â÷¯¸µe­± ªÌ¡AÂ÷¯¸¤£Åã¥Ü­Ó¤H¸ê®Æ¡C
+ post.c µoªí¤å³¹¶W¹L 100 ½g¥H«á¤£´£¥Ü¤å³¹µoªíºõ»â¡C
+ xover.c ­×¥¿ M_VOTE M_BMW °ÊºA¿ù»~¡C
+
+[2000/04/26] acct.c ¶}ªO³Ì«á·|³]©w expire.conf¡C(¤w¨ú®ø)
+
+[2000/05/07] sh/bakbbs/bak* ·s¼W³Æ¥÷ script¡C
+
+[2000/05/12] acct.c ¸Ñ¨M¦P®É¬å°£¦P¤@­Ó¬ÝªO·|³y¦¨ºëµØ°Ï¡B¬ÝªO¥þ·´ªº°ÝÃD¡C
+ visio.c ¶¢¸m¹L¤[µ¹¤©Äµ§i¡C
+ mail.c menu.c admutil.c ±H«Hµ¹¥þ³¡ªO¥D/¨Ï¥ÎªÌ¡C
+
+[2000/05/13] mail.c «HŪ¨ì¤@¥b¥i¥H reply mark delete forward¡C
+ more.c ¤å³¹/«H¾\Ū¨ì¤@¥bªº®É­Ô«ö q ¥i¥Hª½±µÂ÷¶}¡C
+ more.c §ïÅܤ@¨Ç«öÁä©w¸q¡C
+
+[2000/05/15] post.c mail.c post_browse mbox_browse ®É¥i¦s¤J¼È¦sÀÉ¡B¬Ý help¡C
+
+[2000/05/16] acct.c Â_½u®Éµù¥U³æ­×´_¡C
+
+[2000/05/27] bbsd.c Á×§K¦³¯¸ªø¥á¤ô²yµ¹ guest¡C
+
+[2000/09/30] xover.c ­×¥¿ every_Z «áªO¥DÅv­­¿ù»~¡C
+
+[2000/10/29] mail.c ¯¸ªø¥i¥HŪ¨ú¨Ï¥ÎªÌ«H½c¡C
+
+[2000/11/01] perm.h *.c §â¯¸°ÈªºÅv­­µe²M¡C
+
+[2000/11/02] bbsd.c ¥H sysop µn¤J¥i¥HÅܧó¨Ï¥ÎªÌ¨­¤À¡C
+
+[2000/11/09] menu.c post.c §ïÅÜ PERM_ALLBOARD ¦bºëµØ°ÏªºÅv­­¡C
+ gem.c §âºëµØ°Ï¦¬¿ý´£¥Ü¦r¼Ë§ï¦¨¤¤¤å¡C
+
+[2000/11/18] post.c ·s¼W XoXsearch() ·j´M¬Û¦P¼ÐÃD¤å³¹¡C
+ bbsd.c config.h HAVE_WHERE ­×¥¿¬G¶m§P©w¡C
+ acct.c menu.c ¦pªG¨S¦³ define HAVE_ALOHA¡A´N¤£¥Î UFO_ALOHA¡C
+
+[2000/11/19] acct.c ²¾°£ etc/register¡C
+
+[2000/11/25] mail.c §ï¼g¸s²Õ¦^«HªºÀˬd¡C
+
+[2000/11/26] maple.p Makefile acct.c talk.c bbsd.c xover.c aloha.c camera.c rec_loc.c
+ global.h modes.h struct.h @aloha.hlp ·sªº HAVE_ALOHA¡C
+ talk.c ·s¼W¤ô²y°O¿ý°Ï¬q§R°£¡C
+
+[2000/12/02] maple.p Makefile favorite.c bbsd.c board.c menu.c xover.c camera.c
+ config.h global.h modes.h struct.h @mf.hlp ·s¼W MY_FAVORITE¡C
+ aloha.c ·s¼W¦n¤Í¦W³æ±Æ§Ç¡B¬d¸ß¥\¯à¡C
+
+[2000/12/03] outgo.c ¦Û°Ê°e«Hµ{¦¡¡C
+ camera.c ²¾°£ FILM_GOODBYE¡C
+
+[2000/12/17] perm.h ­×§ï HAS_PERM(x) HAVE_PERM(x)¡C
+ *.c HAVE_PERM(x) ¤Î cuser.level & x ´«¦¨ HAS_PERM()¡C
+
+[2000/12/20] config.h §â SYSOPNICK ¿W¥ß¥X¨Ó¡C
+ post.c ·s¼W post_cross_terminator() §R°£¬Û¦P§@ªÌ¡C
+ xover.c §ïÅܤ@¨Ç«öÁä keymap¡C
+ post.c ·s¼W XoX ¨t¦Cªº·j´M¡A¨Ï»P ¨ä¥Lª©¥» BBS ¬Û¦P¡C
+
+[2000/12/21] talk.c Talk ®É¥i¥H¤è¦VÁä²¾°Ê¡BCtrl+Y ²M°£¤§Ãþªº¥\¯àÁä¡C
+ config.h talk.c ²¾°£ TALK_USER_LIST¡C
+
+[2000/12/22] talk.c ¨Ï¥ÎªÌ¦W³æ¥[¦n¤Í®ÉÀˬd¦n¤Í­Ó¼Æ¬O§_¶W¹L¤W­­¡C
+ global.h §â³\¦h message define ¦b³o¡A¨Ã±N COLOR ¿W¥ß¥X¨Ó¡C
+
+[2000/12/23] talk.c cache.c bbsd.c §âÃa¤H¤]¸ü¤J CACHE¡C
+
+[2000/12/24] config.h global.h talk.c ADV_ULIST ¨Ï¥ÎªÌ¦W³æ¤¤¡A¤£¦Pªº¦n¤Í·|°Ï¤ÀÃC¦â¡C
+ aloha.c ¤Þ¤J¦n¤Í¦Ü¦h¥u¤Þ¨ì ALOHA_MAX ªº­Ó¼Æ
+
+[2001/01/01] acct.c ­×¥¿ POP3 »{ÃҮɤ£·|Àx¦s¨Ï¥ÎªÌ«H½c¡C
+ talk.c ¨Ï¥ÎªÌ¦W³æ¤¤ UFO_PAL ¤£¥X²{»P§Ú¬°¤Í¡A¥H§K¦n¤Í¼s¼½¿ù»~¡C
+
+[2001/01/02] acct.c ¨t²Î­«¸m¡C
+
+[2001/01/06] dns_aton.c dns_open.c dns_smtp.c bmtad.c ¤ä´© mail-abuse¡C
+ config.h global.h acct.c untrust.acl trust.acl HAVE_TRUST »{ÃÒ¶Â¥Õ¦W³æ¡C
+ acct.c camera.c struct.h §â ¤@¨Ç±`¥Îªº¤å¥ó§ì¥h FILM_XX¡C
+ talk.c ¥[ªø¤ô²yªø«×¡C
+ talk.c @ulist.hlp ¨Ï¥ÎªÌ¦W³æ¤¤«ö d §R°£¦n¤Í/Ãa¤H¡C
+
+[2001/01/08] config.h global.h struct.h modes.h camera.c menu.c newbrd.c
+ ´£¨Ñ¬ÝªO³s¸p¥\¯à¡C
+ talk.c °O¿ý²á¤Ñ¶}©l®É¶¡¡C
+
+[2001/01/10] §â src/maple ¤¤ªº so ·h¥h src/so¡C
+ admutil.c menu.c acct.c maple.p
+ §â¯¸ªø«ü¥O m_xfile m_resetsys m_reg_merge ¥~±¾¡C
+ favorite.c @mf.hlp §Úªº³Ì·R¤¤¥i¥H­×§ï¬ÝªO¡C
+
+[2001/01/11] *.c §â xxx_head ¤ºªº´£¥Ü¦r¼Ë®æ¦¡§ï¦¨¬Û¦P¡C
+ config.h mail.c acct.c ¨Ï undef EMAIL_JUSTIFY ¦³®Ä¡C
+
+[2001/01/12] config.h global.h acct.c mail.c menu.c maple.p bmtad.c
+ @justify @e-mail valid HAVE_REGKEY_CHECK »{ÃÒ½XÅçÃÒ¡C
+ bbsd.c ¨Ï¬G¶m±Æ§Ç¥¿±`¡C
+
+[2001/01/13] config.h menu.c ´£¨Ñ¿ï³æ¥ú´Î¡C
+
+[2001/01/15] xover.c menu.c Ctrl('U') ¸õ¨ì¨Ï¥ÎªÌ¦W³æ¡C
+
+[2001/01/16] §â COLOR3 ¿W¥ß¥X¨Ó¡C
+
+[2001/01/17] mail.c ¤å³¹¡B¶l¥ó¤é´Á¤W¦â¡C
+
+[2001/01/27] newbrd.c struct.h °Î¦W³s¸p¾÷¨î¡C
+ visio.c menu.c ­×¥¿ vmsg(NULL) pad_view()
+ ¦b°»´ú¥ª¥kÁä¥þ§Î¤U¡A«ö¥ªÁä·|¸õÂ÷¤G¼h¿ï³æªº°ÝÃD¡C
+ talk.c ¹ï¤è§Y¨Ï¨S¶}¡u¦n¤Í¤W¯¸³qª¾¡v¤]³qª¾¡C
+ talk.c ¦Û¤v¬O¹ï¤èªº¦n¤Í¥B¹ï¤è¨S¦³¡u»·Â÷¹ÐÄÛ¡v¤~¥i¥H¦^¦©¤W¯¸³qª¾¡C
+ §ó·s bpop3d.c¡C
+
+[2001/01/28] bbsd.c admutil.c ¬G¶m¤ñ¹ï¥i¥H¤ñ¹ï FQDN¡C
+ board.c @class.hlp ¬ÝªO¦Cªí«ö v ³]©w¬ÝªO¤wŪ¡C
+
+[2001/01/29] bmtad.c ¥H indent ±Æª©¡C
+ board.c @class.hlp ¬ÝªO¦Cªí«ö V ³]©w¬ÝªO¥¼Åª¡C
+
+[2001/02/05] edit.c post.c bmtad.c config.h ÅÜ´« Origin ¼Ë¦¡¡C
+ post.c ­­¨î¤å³¹Âà¿ý½g¼Æ¡C
+ post.c µoªí«áªº¤å³¹´N°O¿ý¡A¨Ï¤£¥X²{¥¼¾\Ūªº¡Ï¸¹¡C
+ config.h struct.h global.h menu.c xover.c song.c camera.c
+ @song.hlp ´£¨ÑÂIºq¥\¯à¡C
+
+[2001/02/07] camera.c ´£¨ÑÂIºq¨ì°ÊºA¬ÝªO¥\¯à¡C
+ global.h *.c §â¨t²ÎªO¦W¿W¥ß¥X¨Ó define BN_XXXX¡C
+ bwboard.c guessnum.c ¥H indent ±Æª©¡C
+ post.c board.c ¤å³¹¦Cªí³¡¤À visit¡C
+ bmtad.c mailpost ®ÉÀˬd¨Ï¥ÎªÌÅv­­¬O§_º¡¨¬¬ÝªO¾\Ū/µoªíÅv­­¡C
+
+[2001/02/08] bbsd.c talk.c guest ¶Ã¼Æ¨ú¬G¶m¡C
+ modes.h menu.c °ÊºA­«±Æ¡C
+ km.c menu.c ´£¨Ñ¤Õ©ú´Ñ¹CÀ¸¡C
+
+[2001/02/09] menu.c ­«·s¾ã²z¿ï³æ¡C
+ menu.c ¥D¿ï³æ«ö s ª½±µ¶i¤J Select()¡A´î¤Ö¿ï³æªø«×¡C
+
+[2001/02/10] dns_smtp.c ¥Î relay server¡C
+
+[2001/02/11] acct.c bbsd.c ¦Û°Ê¨ú¤UÂ÷¾ªO¥DÅv­­¡C
+ acct.c ·s¬ÝªO¹w³]µoªíÅv­­¬°µoªí¤å³¹¡A¹w³]Äݩʬ°¤£Âà«H¡C
+
+[2001/02/12] src/Makefile §Ö³t¦w¸Ë¥Îªº Makefile¡C
+ acct.c ¶}·sªO/­×§ï¬ÝªO¦Û°Ê¥[¤WªO¥DÅv­­¡C
+
+[2001/02/15] mine.c menu.c ´£¨Ñ½ò¦a¹p¹CÀ¸¡C
+
+[2001/02/17] mail.c ¤å³¹¡B«H¥ó¦Cªí¤é´Á§ï¥Î¬P´Á´X¤W¦â¡C
+ talk.c Talk ®É¦³ insert ¥\¯à¡C
+
+[2001/02/18] gem.c ºëµØ°Ï²§°Ê°O¿ý®æ¦¡ÅܰʡC
+
+[2001/02/20] fantan.c menu.c ´£¨Ñ±µÀs¹CÀ¸¡C
+
+[2001/02/21] favorite.c ­­¨î§Úªº³Ì·R¥u¯à§R°£ªÅ¥Ø¿ý¡A¥H§K¯d¤UÀÉ®×´ÝÀe¡C
+ global.h edit.c §â origin banner ªº logo ¿W¥ß¥X¨Ó¡C
+
+[2001/02/22] gem-check.c Á×§KºëµØ°Ï Class¤U©ñ Folder ³Q expire ±¼¡C
+ transacct.c .ACCT Âà´«µ{¦¡¡C
+
+[2001/02/23] maple/Makefile ¤£¥Î make clean ª½±µ¹ï©Ò¦³ªº .o °µ³sµ²¡C
+ bbsd.c ¯¸°È¤£°µ©w´Á»{ÃÒ¡C
+ perm.h *.c §â¯¸°ÈÅv­­°µ§ó²Ó¨Bªº­«¾ã¡C
+ *.hlp ¥[¤J xover.c ¤¤ªº«ü¥O¡A¨Ã­«·s¾ã²z±Æª©¦¨¬Û¦P®æ¦¡¡C
+ post.c gem.c ·s¼W¨Ï¥ÎªÌ²ßºDªº c/p ¦¬¿ýºëµØ°Ï¡C
+
+[2001/02/24] gem-check.c µL±ø¥ó§R°£¨S¥ÎªºÀɮסC
+ account.c §â¤@¨Ç¤å¥ó©ñ¦b [BN_SECURITY]¡C
+ usies-sort.c account.c ²Î­p§C¨Ï¥Î²vªº¬ÝªO¨ì [BN_SECURITY]¡C
+ acct.c account.c °O¿ý¯¸ªø­×§ïÅv­­¨ì [BN_SECURITY]¡C
+
+[2001/02/25] mine.c ­×¥¿¤p¿ù»~¡C
+ talk.c ·s¼W²Ä¤Tª© talk_char()¡C
+
+[2001/02/26] board.c favorite.c ­×¥¿¤¤¤åªO¦W¤Óªø®É¡A¬Y¨Ç term ¬Ý°_¨Ó·|¸õ¦æªº°ÝÃD¡C
+
+[2001/02/28] config.h menu.c mail.c maple.p HAVE_MAIL_ZIP
+ ´£¨Ñ§â«H¥ó/ºëµØ°ÏÀ£ÁYÂà±Hªº¥\¯à¡C
+
+[2001/03/01] xyz.c menu.c etc/tip ¨C¤é¤p¯µ³Z¡C
+ post.c edit.c ´£¨Ñ½s¿è(¦ý¤£¯àÀx¦s)¨ä¥L¤Hµoªí¤å³¹ªº¥\¯à¡C
+ mail.c ´£¨Ñ½s¿è(¦ý¤£¯àÀx¦s)«H½c¤¤«H¥óªº¥\¯à¡C
+ fantan.c ­×¥¿¯}Ãö®É¤£·|µ²§ôªº°ÝÃD¡C
+
+[2001/03/02] inst.sh ¦Û°Ê¦w¸Ë script¡C
+ util/uno/Makefile util/tran/Makefile ¤è«K make¡C
+ config.h talk.c HAVE_BADPAL ¥i¥H®³±¼¾ã­ÓÃa¤H¥\¯à¡C
+
+[2001/03/03] bbsd.c currtitle ÁÙ­ì¡C
+ more.c @more.hlp ·s¼W¤W±²¤@¦æ¡C
+
+[2001/03/04] newbrd.c @cosign.hlp §â³s¸p¬É­±°µ±o§ó¹³¾\ۤ峹¤@¼Ë¡C
+ global.h *.c ¾ã²z©Ò¦³ªº footer¡C
+ talk.c ¼Wªø¤ô²y°O¿ý¡C
+ menu.c ¶i¥X¤l¿ï³æ³£­«¼·°ÊºA¬ÝªO¡C
+ menu.c ·s¼W§Úªº³Ì·R§Ö³tÁä¡C
+ talk.c ¦b¤ô²y¦^ÅU¤¤¡AÅý¶Ç°T/¦¬°T/¼s¼½ªº¤ô²y³£¥i¥H¦^¡C
+
+[2001/03/06] config.h mail.c CHECK_ONLINE ¤å³¹¦Cªí¤¤¥i¥HÅã¥Ü¨Ï¥ÎªÌ¬O§_¦b¯¸¤W¡C
+
+[2001/03/07] xyz.c menu.c ´£¨Ñ BBSNET ªA°È¡C
+
+[2001/03/09] menu.c ¤£¥²Â÷¯¸¥i¥H¼g¯d¨¥ªO¡C
+ menu.c ¯d¨¥ªO´£¨Ñ¤£¦PªºÃC¦â¤Î­¶¼Æ¡C
+ acct.c §â User/Setup ªº»¡©ú¼g©ú¥Õ¤@ÂI¡C
+ acct.c ¿é¤J id ®É´£¥Ü¥i«ö space ¦Û°Ê·j´M¡C
+ menu.c ¿ï³æ±Ô­z½s±Æ¡C
+ struct.h bbsd.c §â userlevel ¤]©ñ¤J cache¡C
+ talk.c §Q¥Î cache ªº userlevel §ïµ½Àˬd³t«×¡C
+
+[2001/03/10] mail.c ­×¥¿¸s²Õ±H«Hµ²§ô®É½Ð«ö¥ô·NÁäÄ~Äò·|©Ç©Çªº¡C
+
+[2001/03/11] global.h menu.c acct.c visio.c §ì¥X MENU_XYPOS ©w¸q¡A¥H«K­×§ï¤¶­±¡C
+ global.h acct.c menu.c §ì¥X MENU_YNOTE ©w¸q¡A¥H«K­×§ï¤¶­±¡C
+ cache.c camera.c §ì¥X MOVIE_LINES ©w¸q¡A¥H«K­×§ï¤¶­±¡C
+ post.c mbox.c @board.hlp @mbox.hlp ²Î¤@«öÁä¡C
+ 'x' Â൹ user 'X' Âà¨ì board ^X ©Ø·¬¸¨¸­
+
+[2001/03/12] config.h modes.h talk.c visio.c BMW_COUNT ­pºâ¤¤¤ô²yªº­Ó¼Æ¡C
+ visio.c acct.c ­×¥¿ vget() ´å¼Ð²¾°ÊÅã¥Üªº°ÝÃD¡C
+ fantan.c guessnum.c mine.c ¨ú®ø¤@¨Ç¨S¥Îªº°T®§¡C
+ config.h *.c §â¤@¨Ç°Ñ¼Æ¾ã²z¤@¤U¡A·h¥h¥L­ÌÂkÄݪºµ{¦¡¡C
+
+[2001/03/13] config.h talk.c BMW_DISPLAY Åã¥Ü«e´X­Ó¤ô²y¡C
+ acct.c ¥[±jª©ªº¿Ã¹õÂê©w¥\¯à¡AÅýÂê©w®É¤£¯à±µ/¶Ç¤ô²y¡A¥B²M°£¿Ã¹õ¡C
+
+[2001/03/15] struct.h acct.c talk.c ¥[¤J¦Û©w Ctrl+R ¬O§_­n¦^ÅU¤ô²yªº¿ï¶µ¡C
+
+[2001/03/16] *.c ²Î¤@°ÝÃD®æ¦¡¬° "§A­n¶Ü(Y/N)¡H[N] " ¥þ§Î°Ý¸¹ ¤j¼g Y/N ¹w³]¥Î [] ¡C
+ ^ µLªÅ¥Õ ^ªÅ¥Õ
+
+[2001/03/17] acct.c bbsd.c ¤£Åý user ­×§ï©m¦WÄæ¦ì¡C
+ util/backup/*.c ¦Û°Ê³Æ¥÷µ{¦¡¡C
+
+[2001/03/19] *.c *.h §â©Ò¦³ªº define ¤ÀÃþ¾ã²z¨ì config.h¡C
+ config.h modes.h bbsd.c mail.c xover.c mboxgem.c reaper.c
+ HAVE_MBOX_GEM ´£¨Ñ­Ó¤H«H½cºëµØ°Ï¡C
+
+[2001/03/21] xchatd.c MUD-like ªº«ü¥O³¡¤À match ´Nºâ¡C
+ xchatd.c MUD-like «ü¥O«ö¦r¥À±Æ§Ç¡C
+
+[2001/03/22] chat.c ¶i¤J²á¤Ñ«Ç®É¹w³]§ï¬°¤£²á¤Ñ¡C
+ xover.c §â¤£±`¥ÎªºÁ䮳±¼¡AÄÀ¥Xµ¹¨ä¥Lµ{¦¡¨Ï¥Î¡C
+ *.c @*.hlp «öÁä­«·s²Î¤@¡C
+ edit.c ANSI ½s¿è®É«ö«á°hÁä¦^¨ì«D ANSI ¼Ò¦¡¡C
+
+[2001/03/24] post.c ¦ê¦C·j´Mµ²ªG´£¥Ü§@ªÌ/¥DÃD¡A¨Ã¸Ñ¨M´£¥Ü¤¤¤å¦r·|©Ç©Çªº°ÝÃD¡C
+ mail.c ¸Ñ¨M«H¥óÂsÄý¥u§â³Ì«á¤@½g³]¤wŪªº°ÝÃD¡C
+ more.c KEY_UP KEY_PGUP ¹ï¤G­¶¥H¤Wªº¤å³¹¥iÂ÷¶}¨Ã½¤W¤@½g¡C
+ post.c ¦ê¦C·j´M¼Ò¦¡¤¤®É«ö¤W¤UÁä¤]¥i¥H´`§Ç¾\Ū¡C
+
+[2001/03/25] bmtad.c mail post Àˬd¬O§_¦³µoªíªºÅv­­¡C
+ post.c @board.hlp ·j´M©Ò¦³ mark ªº¤å³¹¡C
+ talk.c ½Õ¾ã¨Ï¥ÎªÌ¦W³æªº°ÊºAªø«×¡C
+
+[2001/03/27] post.c ´£¨ÑªO¥D¦bªO¤º­×§ï¤¤¤åªO¦W¡C
+ xover.c ­×¥¿¦bºëµØ°Ï¤£¥iÂà±H¡C
+
+[2001/03/28] post.c cache.c @board.hlp ¦b¤å³¹¦Cªí¥i¥H¥á½u¤W§@ªÌ¤ô²y¡C
+ config.h acct.c more.c post.c xover.c §ì¥X HAVE_NOFORWARD¡C
+ *.c §â¬Ýª©¡B¼Æ¥D³£§ï¦¨¡uªO¡v¡A²Î¤@¥Î¦r¡C :p
+ *.c Åý xxx_cb ¤¤ªº dynamic load ¤£·|¦³ warning¡C
+
+[2001/03/29] mode.h ­«·s¾ã²z¡C
+
+[2001/03/30] bmw.c board.c pal.c post.c talk.c maple.p
+ §â talk.c ¸Ì­±ªºµ{¦¡¤À´²¨ì¦U¾A·íªº¦a¤è¡C
+ util/tran/*.c ´£¨Ñ sob Ptt WD FireBird Âà maple 3 ªºµ{¦¡¡C
+
+[2001/04/02] favor.c §Úªº³Ì·R¤¤¥i¥H³]©w¬ÝªO¤wŪ/¥¼Åª¡C
+ board.c favor.c mf.hlp ¬ÝªO¦Cªí¤¤³]©w¬ÝªO¥¼Åª«á¡A¥¼Åªªº¿O·|«G°_¨Ó¡C
+
+[2001/04/03] bmw.c ­×´_¤ô²y°Ï¶ô§R°£ªº¥\¯à¡C
+ xover.c *.c global.h ¦b³Ì«á¤@¦æ´£¥Ü«ü¥O¡C
+ song.c ºq¥»¤¤Åý¤@¯ë¨Ï¥ÎªÌ¤]¥i¥H edit ¬Ý±±¨î½X¡C
+
+[2001/04/07] config.h board.c favor.c ENHANCED_VISIT ¤wŪ/¥¼Åª§ï¬°§PÂ_³Ì«á¤@½g¤å³¹¡C
+ menu.c ­×¥¿ guest ¥i¥H¶i¤J§Úªº³Ì·Rªº bug¡C
+ cache.c edit.c more.c visio.c ¼W¥[ SHOW_USER_IN_TEXT ªººØÃþ¡C
+
+[2001/04/08] acct.c talk.c ·s¼Wª÷¿ú¡B¥Í¤é¡B©Ê§OÄæ¦ì¡C
+ post.c edit.c µoªí¤å³¹¥[¿ú¡C
+ mail.c ¦b«H¥ó¦Cªí¥i¥H¥á½u¤W§@ªÌ¤ô²y¡C
+
+[2001/04/13] mboxgem.c ¤p patch¡C
+ @board.hlp @mbox.hlp ¥[±j¡C
+ struch.h @class.hlp board.c acct.c account.c ¬ÝªO¦Cªí¥i¿ï¾Ü¨Ì¦r¥À/¤ÀÃþ±Æ§Ç¡C
+
+[2001/04/14] vote.c menu.c global.h ·s¼W§ë²¼¤¤¤ßªº¥\¯à¡C
+
+[2001/04/15] account.c ·s¼W¦P¤@ email »{ÃÒ¹L¦hªº°O¿ý¡C
+ bbsd.c ¥Í¤é·í¤Ñ¤W¯¸¦³¯S§Oªº¶i¯¸µe­±¡C
+
+[2001/04/19] @gem.hlp gem.c mboxgem.c ¥[¤JºëµØ°Ï·s¼W¤å³¹/¨÷©vªº§Ö³tÁä¡C
+
+[2001/04/20] *.c §â¤@¨Ç vget() ¤£»Ý­n GCARRY ªº´«¦¨ DOECHO¡C
+ dice.c menu.c ·s¼WÂY»ë¤l¹CÀ¸¡C
+
+[2001/04/21] gp.c menu.c ·s¼Wª÷¼³§J±ô«¢¹CÀ¸¡C
+ race.c menu.c ·s¼WÁɰ¨³õ¹CÀ¸¡C
+ bingo.c menu.c ·s¼W»«ªG¹CÀ¸¡C
+ bmw.c cache.c ¸Ñ¨M¦pªG post ®É¤¤¤ô²y¡A¤£·|Åã¥Ü¨Ó·½ªº°ÝÃD¡C
+
+[2001/04/23] bj.c menu.c ·s¼W¶Â³Ç§J¤G¤Q¤@ÂI¹CÀ¸¡C
+ admutil.c acct.c menu.c maplep.p §âµù¥U³æ»{ÃÒªºµ{¦¡·h¥h dynamic load¡C
+
+[2001/04/24] nine.c menu.c etc/game/99 ·s¼W¤Ñ¦a¤E¤E¹CÀ¸¡C
+
+[2001/04/25] chessmj.c menu.c ·s¼W¶H´Ñ³Â±N¹CÀ¸¡C
+
+[2001/04/26] util/tran/wd2brd.c ´£¨ÑÂà´«¬ÝªOÄݩʡC
+ seven.c menu.c ·s¼W½ä«°¤C±i¹CÀ¸¡C
+ marie.c etc/game/marie menu.c ·s¼W¤pº¿ÄR¼Ö¶é¹CÀ¸¡C
+
+[2001/04/27] bar.c menu.c ·s¼W§a¥xº¿ÄR¹CÀ¸¡C
+
+[2001/05/02] km.c ¤Õ©ú´Ñ§ï¶i¡A¨Ï·|¥X²{´ÑÃЦWºÙ¡C
+
+[2001/05/08] stock.c menu.c etc/game/stock.name etc/game/stock.now etc/game/stock.sh
+ ·s¼WªÑ¥«¤j¦ë¹CÀ¸¡C
+
+[2001/05/09] km.c ¤Õ©ú´Ñ§ï¶i¡A¨Ï¯à°O¿ý§¹¦¨ªº´ÑÃФή¬´Ñ¡C
+ etc/game/km ¿é¤J¤j¶q¤Õ©ú´ÑÃСC
+
+[2001/05/25] visio.c ¡u½Ð«ö¥ô·NÁäÄ~Äò¡v¶]°¨¿O¡C
+
+[2001/05/28] chat.c ¤£¥i¥H¥Î§O¤Hªº id ·í°µ²á¤Ñ«Ç¥N¸¹¡C
+
+[2001/05/29] struct.h ¼W¥[ BMW.userid ªºªø«×¡Aµ¹¼s¼½²Å¸¹¥Î¡C
+
+[2001/06/02] menu.c §ï±¼¤@¨Ç­«ÂÐÃöÁä¦rªº¿ï¶µ¡C
+ config.h post.c xover.c @board.hlp HAVE_REFUSEMARK ´£¨Ñ¬ÝªO¤å³¹¥[±K¥\¯à¡C
+ xover.c ­×¥¿¤w§R°£¤å³¹¤£±oÂà±H¡B¶×¤J¼È¦sÀÉ¡B Z-modem ¤U¸ü¡C
+ bmtad.c mailpost.c ¸Ñ¨M¤£¯à Email Justufy »{ÃÒ¡C
+
+[2001/06/06] bpop3d.c ­×¥¿¬Y¨Ç client ¨ú«H·|¥d¦ºªº°ÝÃD¡C
+ dl_lib.c ¸Ñ¨M¥~±¾¤Ó¦h.so Ãz±¼¤Fªº°ÝÃD¡C
+
+[2001/06/09] railway.c ¿é¤J¨®¯¸¦W®É§ï¥Î¿ï¸¹½Xªº¡C
+
+[2001/06/16] menu.c ¸Ñ¨M°ÊºA¬ÝªO 12 ¦æ¡A¤S¨Ï¥Î¿ï³æ¥ú´Î®É¡Aµe­±·|¦³´ÝÀeªº°ÝÃD¡C
+
+[2001/06/19] wd2usr.c ¸Ñ¨M .PASSWD ±`¦³ªÅ¥ÕÄæ¦ìªº°ÝÃD¡C
+
+[2001/06/29] more.c ±N¤Þ¨¥¤ÀÃC¦â¡C
+
+[2001/07/09] acct.c POP3 REGKEY »{ÃÒ¥u¯à¦³ PERM_VALID¡A¦b¤U¦¸ login ®É¦Û°Ê¥[¤W¨ä¥LÅv­­¡A
+ ¥H§K³Q°±ÅvªÌÂǦ¹¤èªk´_Åv¡A¤]¤£·|»P NEWUSER_LIMIT ½Ä¬ð¡C
+
+[2001/07/09] post.c ­×§ï¤å³¹¼ÐÃD®É¶¶«K­×§ï¤º¤åªº¼ÐÃD¡C
+
+[2001/07/10] chip.c ª±¹CÀ¸¤£¯à multi_login¡C
+ backupacct.c ³Æ¥÷¥þ¯¸¨Ï¥ÎªÌ .ACCT¡C
+ menu.c ­×¥¿ system load ¬d¸ß¡C
+
+[2001/07/11] board.c ¬ÝªO¨Ì¦WºÙ/¤¤¤åªO¦W¤Á´«®ÉÀ³Àx¦s¾\Ū°O¿ý¡C
+ topusr.c ¥i²Î­p¤W¯¸¦¸¼Æ/Äé¤ô¦¸¼Æ/»È¹ô/ª÷¹ô/¦~ÄÖ/¬P®y¡C
+ mine.c ­×¥¿ª±½ò¦a¹pÀH«K¶Ã¼Ð°O¡A·í¼Ð°O¼Æ=¦a¹p¼Æ´N»¡§A¹LÃö¤F¡C
+ post.c §R°£¤å³¹­n¦©¿ú¡C
+
+[2001/07/12] chip.c marie.c etc/game/marie ­×§ï¦X²zªº½ß²v¡C
+ post.c §ï¥Î¦r¼Æ¤Îªá¶O®É¶¡¨Ó­pºâ½Z¶O¡C
+
+[2001/07/14] dice.c ­×§ï¦X²zªº½ß²v¡C
+ gp.c ­×§ï¨Ï¯à§@¹ú¨Ó­°§C³Ó²v¡C
+ .BRD brd/* gem/brd/* run/class*.img §â¹w³]¬ÝªO«Ø¥ß°_¨Ó¡C
+ gem/@ §â (A)nnounce ¿ï³æ«Ø¥ß°_¨Ó¡C
+
+[2001/07/16] showUSR.c showACCT.c Åã¥Ü .USR .ACCT ¥Îµ{¦¡¡C
+ setusr.c setperm.c ¥~³¡³]©w¨Ï¥ÎªÌ¸ê®Æ¡C
+ menu.c bank.c ¥[¤J»È¦æ¡A´£¨ÑÂà±b¡B¶×§I¥\¯à¡C
+ config.h struct.h acct.c bbsd.c HAVE_NOALOHA ´£¨Ñ¤W¯¸¤£³qª¾¦n¤Í¡C
+ config.h struct.h acct.c talk.c HAVE_NOBROAD ´£¨Ñ©Ú¦¬¼s¼½¡C
+ bbsd.c «OÃÒ¤H¨î«×°O¿ý«O¤H¡C
+ stock.c ¹w¨¾ªÑ»ù = 0 (¤½¥q­Ë³¬)¡C
+
+[2001/07/17] post.c ´£¨Ñ XPOST ¤¤¥i¥H½s¿è¼ÐÃD¡B¤å³¹¡A¥[±K¡C
+ config.h menu.c bank.c ´£¨ÑÁʶRÅv­­¡C
+ menu.c ½Õ¾ã feeter ªºªø«×¡C
+ config.h talk.c @ulist.hlp HAVE_CHANGE_ID ¨Ï¥ÎªÌ¦W³æ¯¸ªø¼È®É­×§ïID¡C
+
+[2001/07/19] menu.c recall.c ´£¨Ñ°O¾Ð¹CÀ¸¡C
+
+[2001/07/20] vote.c ­×¥¿ vote_all «ö End ©Î $ À³Àˬd¬O§_¤w¨ì³Ì«á¤@¶µ¡C
+
+[2001/07/23] xover.c post.c gem.c §ï¼g gem_gather()¡C
+ menu.c gray.c ´£¨Ñ²L¦Ç¤j¾Ô¹CÀ¸¡C
+ gem.c mboxgem.c ­×¥¿¬Y¨Ç±¡ªp¤U¤£¯à Ctrl+P gem_add ªº°ÝÃD¡C
+
+[2001/07/25] menu.c pip/* etc/game/pipgame etc/game/pipdata ´£¨Ñ¹q¤l¤pÂû¹CÀ¸¡C
+ post.c §Y¨Ï¬O­ì¤åÂà¸ü¡A¤]¤£­n¥Î HDR_LINK ªº¤èªk¡A´Nµø¬°¬O¤£¦Pªº¤å³¹¡C
+ gem.c ©Ò¦³¦¬¿ý¤å³¹ªº°Ê§@³£§ï¦¨¨S¦³ HDR_LINK¡A¤@½g¤å³¹´N¬O¤@­ÓÀɮסC
+
+[2001/07/26] gem.c ¥Ñ©ó¤@½g¤å³¹´N¬O¤@­ÓÀɮסA©Ò¥H§R°£®É§ï¦¨ª½±µ§R°£ÀɮסC
+ gem.c ´£¨Ñ°Ï¬q§R°£¥\¯à¡C
+ gem.c ¤ä´©¸ó°Ï«þ¨©¡C
+ gem-check.c ¤£¦A¨Ï¥Î¡C
+ favor.c ¨t²Î¦Û°Ê²¾°£¾D¬åªOªº¬ÝªO¡B¬ÝªO±¶®|¡C
+
+[2001/07/27] acct.c ­×§ï¬ÝªO®É¡A´£¨Ñ²M°£©Ò¦³ªO¥D(ªO¥DÄæ¯d¥Õ)¡C
+ mboxgem.c config.h global.h modes.h ¨ú®ø¤£¨Ï¥Î¡C
+ gem.c ¥Ñ©ó¤ä´©¸ó°Ï«þ¨©¡A¤¹³\©Ò¦³¤H¦¬¿ý¬ÝªO¡B½Æ»sºëµØ°Ï¤å³¹¨ì¥L¾á¥ô(¤p)ªO¥D/­Ó¤H«H½cªº¦a½L¡C
+ gem.c ºëµØ°Ï¶K¤W®ÉÀˬdÁ×§Kµo¥Í°j°é¡C
+ bbsd.c gem.c ±N­Ó¤H«H½cºëµØ°Ï©M¬ÝªOºëµØ°Ï¾ã¦X¡C
+ fb2usr.c sob2usr.c wd2usr.c Âà´«µ{¦¡¼W¥[­Ó¤HºëµØ°Ï¡C
+
+[2001/07/29] dreye.c ´£¨Ñ͍å³q½u¤W¦r¨å¥\¯à¡C
+
+[2001/08/02] etc/pipgame/* pip/* ¹q¤lÂû¹CÀ¸¤j§ïª©¡C
+
+[2001/08/03] @goodbye struct.h camera.c menu.c Â÷¯¸®É¨q¤@±i¹Ï¡C
+
+[2001/08/04] acct.c ´_Åv¥\¯à¡C
+
+[2001/08/05] acct.c ­×¥¿­«·s»{ÃÒ®ÉÅv­­¤£¹ïªº°ÝÃD¡C
+ visio.c «ö¥ô¤@Áä²M°£¤ô²y¼Æ¡C
+ newbrd.c acct.c ·sªO³s¸p§¹¡A¯¸ªø¥i¥Hª½±µ¶}ªO¡C
+
+[2001/08/11] acct.c so/*.c pip/pip_basic.c post.c §â¥[´î¿ú¦^¦sµwºÐªº¨ç¦¡¿W¥ß¦¨ acct_savemoney()¡C
+ cache.c acct.c perm.h bbsd.c admutil.c maple.p °ÊºA³]©w¨Ï¥ÎªÌ¸ê®Æ¡C
+
+[2001/08/12] menu.c so/*.c §â¹CÀ¸°ÝÄw½Xªº¨ç¦¡¨Ö¤J menu.c¡C
+
+[2001/08/14] pip_stuff.c §ïÅܦsÀɮ榡¡C
+ pipstruct.h pip_fight.c pip_prac.c ·s¼WÅ@¨­¡B»´¥\¡B¤ßªk¡B¤Mªkµ¥¾Ô°«§Þ¯à¡C
+ pip_fight.c Â×´I©ÇÃ~²£¥Í¾¹¥[¤J©Çª«ÄݩʡC
+
+[2001/08/15] mail.c ´£¨Ñ§â­Ó¤HºëµØ°ÏÀ£ÁY±H¦^¥hªº¥\¯à¡C
+
+[2001/08/16] xchatd.c ½Í¤Ñ«Ç¤¤ mud-like «ü¥O±Ô­z§ï¼g¡C
+ pip/* etc/game/pip/* ­«·s¾ã²z¡C
+
+[2001/08/20] pip.c pip_fight.c pipstruct.h ·s¼W¯S®í§Þ¯à¡C
+ gem.c post.c ¦¬¿ýºëµØ°Ï¤£¥[¤W POST_GEM¡C
+ *.c etc/* ²Î¤@¡u¨­¤À¡v¥Î¦r¡C
+ acct.c bmtad.c mailpost.c §ï¼g»{ÃÒ³¡¤Àµ{¦¡¡C
+
+[2001/08/21] chessmj.c ­×¥¿¥[¿ú¿ù»~¡C
+ global.h *.c ¾ã²z¤ÀÃþ FN_XXXX¡C
+ so/*.c ¨ú®øª±¹CÀ¸¤£»Ý­n­«Ã¸¿ï³æ¡C
+ jcee.c etc/jcee/* ¤j¾ÇÁp¦Ò¬dº]¡C
+ favor.c ­×¥¿¬ÝªO­^¤å¦WºÙÅܰʫá¡A§Úªº³Ì·R·|¥X¿ù¡F­×¥¿Åv­­Åܰʫá¡A¬Ý¨ì·sÅv­­¤£¯à¬Ý¨ìªº¬ÝªO¡C
+ newbrd.c acct.c ­×¥¿³s¸p¶}ªO®É¤£·|§â§@ªÌ¦Û°Ê¥[¤WªO¥DÅv­­ªº°ÝÃD¡C
+ global.h edit.c more.c §ï¤@¨Ç FOOTER ¨Ïµ¥ªø¹ï»ô¡C
+
+[2001/08/22] config.h mode.h global.h xover.c post.c HAVE_XYNEWS ·s»D¾\Ū¼Ò¦¡¡C
+ post.c ·j´M¥»¦a¤å³¹¡C
+
+[2001/08/24] pipstruct.h pip_fight.c ·s¼W§Þ¯à·t¾¹¡C
+ xyz.c §ïµ½ BBSNET ªº¬É­±¡C
+
+[2001/08/25] xyz.c etc/game/qkmj/* ·s¼W QKMJ¡C
+ global.h bmtad.c mailpost.c mail.c acct.c »{ÃÒ½X»{ÃÒ¤£»Ý­n¶}ÀÉ¡C
+
+[2001/08/27] innbbsd/Makefile §ï¼g¡C
+ gem-expire.c §ï¼g¡C
+ visio.c ·s¼W½Ð«ö¯S®íÁäÄ~Äò¡C
+
+[2001/08/29] talk.c §ïµ½¨Ï¥ÎªÌ¦W³æ®Ä²v¡C
+ pal.c ­×¥¿ÁÙ¨S¨ì¦n¤Í¤W­­´Nĵ§i¦n¤Í¹L¦hªº°ÝÃD¡C
+
+[2001/08/30] gp.c ­×¥¿¯S®íµP«¬¿é¿úªº°ÝÃD¡C
+
+[2001/08/31] acct.c bbsd.c menu.c post.c so/*.c pip_basic.c perm.h
+ §Q¥Î disable ²Ä¤G°¦¥H«áªº multi-login ªº¿ú¹ô¨Ó¹w¨¾ multi ÁÈ¿ú¡A
+ ¦p¦¹«K¥i¥H¨ú®øÄw½X¨î«×¡A¨Ã´î¤Ö¤j¶qªº¦^¦s .ACCT¡C
+
+[2001/09/01] struct.h ­×¥¿ MODE_STAT ·|¿ù¶Ãªº°ÝÃD¡C
+ vote.c global.h §ë²¼°Ï¥kÁä§Y¥i§ë²¼¡C
+
+[2001/09/02] admutil.c menu.c ·j´M¨Ï¥ÎªÌ¡C
+
+[2001/09/07] visio.c ­×¥¿¥Î Ctrl-R ¦^¤ô²y¤£·|­«­p¤ô²y¼Æ¡C
+ classtable.c menu.c ´£¨Ñ¥\½Òªí¥\¯à¡C
+ gray.c Â÷¶}²L¦Ç¹CÀ¸­n­«Ã¸ menu¡C
+
+[2001/09/08] wd2pal.c sob2pal.c Âà´«¦n¤Í¦W³æµ{¦¡¡C
+
+[2001/09/09] talk.c §Ö³tÁô¨­¡C
+ brd2gem.c Âà´«¬ÝªO¤ÀÃþ¨ìºëµØ°Ï¡C
+ board.c favor.c vote.c ¬ÝªO¦Cªí¬ÝªO¤ÀÃþ¥[ÃC¦â¡C
+ talk.c bmw.c pal.c struct.h ­×¥¿Âê©w¦^¤ô²y§PÂ_¤£¥¿½Tªº°ÝÃD¡C
+
+[2001/09/10] config.h board.c xover.c maple.p ²Ä¤@¦¸¶i¤J¤å³¹¦Cªí¡A§â´å¼Ð©ñ¦b²Ä¤@½g¥¼Åª¡C
+ config.h board.c ¬ÝªO¦Cªí¦Û°Ê¸õ¥h¤U¤@­Ó¥¼Åª¬ÝªO¡C
+ menu.c Select Favorite everywhere¡C
+
+[2001/09/11] edit.c @edit.hlp etc/model ½s¿è¤å³¹®É´¡¤J½d¥»ºëÆF¡C
+
+[2001/09/12] struct.h board.c favor.c acct.c «í¬°·s¤å³¹¼Ò¦¡¡C
+ talk.c menu.c maple.p Áô¨­±Kªk¡C
+
+[2001/09/13] board.c talk.c xover.c ­×¥¿·j´M®É¤£·|²M°£³Ì«á¤@¦æªº°ÝÃD¡C
+
+[2001/09/15] struct.h acct.c bbsd.c talk.c §â realname ©M username ªºªø«×§ì¥X¨Ó define¡C
+ wd2mf.c §Úªº³Ì·RÂà´«µ{¦¡¡C
+
+[2001/09/17] gem.c ­×¥¿ºëµØ°Ï¬å¥Ø¿ý»~¬åªº°ÝÃD¡C
+
+[2001/09/22] talk.c maple.p menu.c cache.c ¥[¤J¿ï¾Ü Talk ¥\¯à¡C
+ acct.c User/Info ´«ª©­±¡C
+ admutil.c ¯S®í·j´M«á¥i¥H­×§ï¸Ó¨Ï¥ÎªÌ¡C
+
+[2001/09/23] talk.c ¿ï³æ¤Á´«¦©¾÷¡C
+ edit.c ­×¥¿ SHOW_USER_IN_TEXT ¦b­«·s½s¿è¤å³¹®Éªº bug¡C
+ wd2usr.c Âà´« habit¡C
+ pal.c ­×¥¿¦b¬ÝªO¦n¤Í¦W³æ¤¤¥i¥H­«ÂÐ¥[¤J¡C
+ config.h pal.c menu.c maple.p ·s¼W¯S§O¦W³æªº¥\¯à¡C
+ pal.c aloha.c @pal.hlp @aloha.hlp ¦b¦n¤Í/¤W¯¸¦W³æ¤¤¥i¥H±H«H/¤ô²y¡C
+ maple/*.c xyz.c modes.h ²Î¤@¦r¦ê¬°¡u¤ô²y¡v¡C
+
+[2001/09/24] bbsd.c fb2usr.c sob2usr.c wd2usr.c §ïÅÜ­Ó¤HºëµØ°Ï¬[ºc¨Ó´î¤Ö¥Ø¿ý­Ó¼Æ¡C
+ modeh.h ­×¥¿ mode ©M type ¹ïÀ³¿ù»~¡C
+ bbsd.c etc/mboxgem.over Àˬd­Ó¤HºëµØ°Ï¬O§_¹L¤j¡C
+
+[2001/09/24] gem.c post.c @gem.hlp @board.hlp §ï¼gºëµØ°Ï¡G
+ ­×¥¿¤@¯ë¨Ï¥ÎªÌ¦b«DªO¥D¬ÝªOºëµØ°Ï¤£¯à gem_copy()¡C
+ Åý©Ò¦³¨Ï¥ÎªÌ¦bºëµØ°Ï¥i¥H½s¿è¤å³¹(¤£¯àÀx¦s)¡C
+ ¤pªO¥D¥i¥H¬Ý¨£¦Û¤vºÞÁҰϤ¤ªº­­¨î¯Å¥Ø¿ý¦WºÙ¡C
+ ­×¥¿¸ó°Ï«þ¨©¿ïªþ¥[ÀÉ®×·|¥¢±Ñ¡C
+ ­­¨î gem_gather() ¤@©w­n¥ý©wÁã¡A¥H§K¼È¦s­Ó¤HºëµØ°Ï¦^¦¬µ©¤Ó¦h¡C
+ ·s¼W post_copy() ¦¬¿ý¬ÝªO¤¤¤å³¹¨ìºëµØ°Ï¡C
+
+[2001/09/26] vote.c ´£¨Ñ¥ß§Y¶}²¼¿ï¶µ¡C
+ jcee.c ­×¥¿¥u¿é¤J©m¦W¬d¸ß®É¿ù»~ªº°ÝÃD¡C
+
+[2001/09/27] util/tran/*.c ¸É¤Wº|±¼ªº closedir()¡C
+ src/Makefile bin/install.sh §ï¼g¡C
+ xchatd.c §ïµ½ Mud-like «ü¥O·j´M¡C
+
+[2001/09/28] config.h global.h struct.h song.c topsong.c @-topsong
+ LOG_SONG_USIES ºq¥»¨Ï¥Î²Î­p¡C
+ global.h bbsd.c talk.c menu.c ­×¥¿¯¸¤WÁ`¨Ï¥ÎªÌÅã¥Ü¤£¥¿½Tªº°ÝÃD¡C
+
+[2001/09/29] admutil.c ¸É¤Wº|±¼ªº closedir()¡C
+ board.c menu.c maple.p ¼W¥[­×§ï¬ÝªO¿ï¶µ¡C
+
+[2001/10/01] topusr.c @-birthday ²Î­p¥»¤é¹Ø¬P¡C
+
+[2001/10/02] reaper.c bquota.c crontab (reaper-vac.c bquota-vac.c)
+ §â¥­®É/´»°²¥Îªº±b¸¹/«H¥ó²M°£µ{¦¡¾ã¦X¦¨¤@¤ä¡A¦Û°Ê§PÂ_¤é´Á¡C
+ talk.c ­×¥¿ guest ¥i¥HÂǥѨϥΪ̦W³æ¶i¤J XZ_BMW¡C
+ talk.c ­×¥¿¨Ï¥ÎªÌ¦W³æ±H«H­«ÂÐÀˬdÅv­­¡C
+
+[2001/10/03] np2gem.c Napoleon Âà´«ºëµØ°Ïµ{¦¡¡C
+
+[2001/10/04] nine.c ­×¥¿¤Ñ¦a¤E¤E·|¦Û¤vÀ°ª±®a¥XµPªº°ÝÃD¡C
+ account.c ­«·s¾ã²z½s±Æ¤@¤U¡C
+
+[2001/10/05] showACCT.c showBRD.c Åã¥Ü .ACCT .BRD¡C
+ acct.c ­×§ï acct_show() ºX¼ÐÅã¥Ü¡C
+ gem.c ­­¨î©Ê¤å³¹¤£¯à³Q½Æ»s¶K¤W¡C
+
+[2001/10/11] dice.c ­×¥¿Åã¥Ü­¿²v©M¥[¿ú¤£¦X¡C
+ bbsd.c ¤j¤p¼g¨Ó·½³£¯à³Q¦¨¥\§P©w¬G¶m¡C
+ chessmj.c ­×¥¿¥[¿ú¤£¹ïªº°ÝÃD¡C
+ bj.c ­×¥¿¥i¥HµL­­¦¸ double¡B¹q¸£®³¨ì AA µP®Éªº§PÂ_¿ù»~¡C
+
+[2001/10/12] etc/tip §ï¤@¨Ç¤£¹ïªº¡C
+ edit.c ¥Î -- ¤À³Îñ¦WÀÉ¡A¤£¥²­n¤»¦æ¡C
+
+[2001/10/13] song.c ÂIºq³Ì«á¤@¦æ¥[¤W <~Src~> ·Q¹ï <~Des~> »¡ <~Say~> (¬°WDªººq¥»³]­p)¡C
+
+[2001/10/18] gem.c ºëµØ°Ï«þ¨©/§R°£³¡¤Àµ{¦¡§ïµ½¡A­×¥¿«Ý§R°£¨÷©v¤U¦³¸ê®Æ¡B
+ ¬ÝªO¡B­­¨î¯Å¤å³¹®É·|µo¥ÍµL½a°j°éªº°ÝÃD¡C
+
+[2001/10/19] backup*.c ³Æ¥÷µ{¦¡¡C
+
+[2001/10/22] bar.c ­×¥¿¿ù»~¡C
+ struct.h bbsd.c talk.c ¨Ï¥ÎªÌ¦W³æÅã¥Ü¹Ø¬P¡C
+
+[2001/10/23] pip_stuff.c ­×¥¿¦¬Ã¬©u¤ñÁÉ¥[¿ú¤£¹ï¡C
+ gp.c ­YÄ~ÄòÀ£ª`¡Aµ¹¿ú·|¤£¹ï¡C
+
+[2001/10/24] bj.c ²ø®aÅã¥ÜÂI¼Æ¿ù»~¡C
+ etc/game/pip/badman/pic000 pip_fight.c ¾Ô°«­×¦æ³õ´º¦a¹Ï¡C
+
+[2001/10/25] board.c @class.hlp ¨ÌªO¥D·j´M¬ÝªO¡C
+ mail.c «H½c¤å³¹«þ¨©¡C
+
+[2001/10/26] struct.h talk.c post.c transacct.c §â ACCT ¤¤ money/gold Äæ¦ì§ï¦¨ int¡C
+ post.c ­×¥¿©Ø·¬¸¨¸­±Ù¥\¯à¡C
+ src/util/backup/* ³Æ¥÷/³Æ¥÷«ì´_µ{¦¡¡C
+
+[2001/10/27] global.h bbsd.c menu.c talk.c ¯¸¤WÁ`¤H¼ÆÅã¥Ü­×¥¿¡C
+
+[2001/10/30] pip_prac.c ½m²ß®É¿ï¨ú®ø¤@¼Ë·|¥[ÄݩʡC
+
+[2001/11/04] modes.h vote.c board.c maple.p ´£¨Ñ¦n¤Í¬ÝªOÄݩʡC
+ vote.c ­×¥¿§ë²¼¤¤¤ß«ö PGDN PGUP ·|½¹LÀYªº°ÝÃD¡C
+
+[2001/11/05] camera.c ¤¹³\°ÊºA¬ÝªO¤ÀÃþ¤¤ÁÙ¦³¨÷©v¤ÀÃþ¡C
+
+[2001/11/10] aloha.c ­×¥¿§R°£¤W¯¸³qª¾¦W³æ®É¡A­Y³Q§R°£ªº¨Ï¥ÎªÌ¤w¾D reaper ·|¶i¤JµL½a°j°éªº°ÝÃD¡C
+
+[2001/11/15] mail.c maple.p acct.c km.c §â±HÀɮתº¨ç¦¡µ¹¿W¥ß¥X¨Ó¡C
+ song.c Åý¨S¦³ <~Src~> ªººq¥»¤]¯à¥D°Ê¥[¤J <~Src~>¡C
+
+[2001/11/16] etc/re-reg global.h bbsd.c ­«·s»{ÃÒ³qª¾«H¡C
+
+[2001/11/20] config.h global.h mail.c bmw.c menu.c talk.c xover.c chat.c
+ topsong.c wd2mf.c ­×¥¿¤@¨Ç #undef ®Éªº¤p¿ù»~¡C
+ acct.c ¯¸ªø¥i¥H©ñ¤ô¥[¨Ï¥ÎªÌ»{ÃÒÅv­­¡C
+ showACCT.c §ï¥¿¿ù»~ªºµ{¦¡¡C
+
+[2001/11/23] bmw.c ´«¤ô²y°O¿ý®æ¦¡¡C
+ @bmw.hlp bmw.c ¤ô²y¦Cªí¤¤¥i¿ï¾ÜÀx¦s¡A¤£¥²µ¥¦AÂ÷¯¸¡C
+ admutil.c ¯¸ªø¥þ¯¸¨Ï¥ÎªÌ/ªO¥D±H«H®É¡A­Y¿ï¾Ü¨ú®ø¡AÀɮפ£·|²M°£¡C
+
+[2001/11/24] talk.c ¨Ï¥ÎªÌ¦W³æ¤¤¤£¯à¥á¤ô²yµ¹Áô§Îªº¨Ï¥ÎªÌ¡C
+ talk.c ­×¥¿¨Ï¥ÎªÌ¦W³æ¤¤¬Ý¨ìÁô§Îªº¨Ï¥ÎªÌ¤Î¤£¬O¦n¤Íªº¦n¤Í¡C
+
+[2001/11/25] post.c ¬ÝªO¤¤­×§ï¤¤¤åªO¦W«á¡A­n§ó·sª©­±¡C
+ vote.c ªO¥D¦b¨S¦³§ë²¼ªº¬ÝªOÁ|¦æ§ë²¼¡A¦ý¤£¿é¤J¼ÐÃD¡A·|µo¥Í¿ù»~¡C
+
+[2001/11/26] talk.c ­×¥¿¼s¼½»~´Óªº°ÝÃD¡C
+
+[2001/12/08] acct.c newbrd.c §ï¥Î¸û«K§Qªº¬ÝªOÅv­­³]©w¡C
+ bank.c Àˬd¿ú¬O§_¬°­tªº¡C
+
+[2001/12/09] pip_stuff.c ­×¥¿¤pÂû³Æ¥÷Àx¦s/Ū¨ú·|¥¢±Ñ¡C
+ modes.h ­×¥¿°ÊºA¬°¬d¸ß/½Í¤Ñ®É¤£·|¥X²{¹ï¶H id¡C
+ xover.c guest ¤£±oÂÇ every_Z ¶i¤J¤ô²y¦^ÅU¡C
+ xover.c menu.c talk.c bwboard.c chat.c ­×¥¿
+ ¶i§Úªº³Ì·R/¤ô²y¦^ÅU«á¥Î every_Z ¸õ¥h¨ä¥L¿ï³æ¦A¥X¨Ó·|µo¥Í¿ù»~¡C
+
+[2001/12/10] pip_basic.c ¬Ý playboy ®É¸oÄ^¬O¼W¥[¤£¬O´î¤Ö¡C
+
+[2001/12/17] acct.c board.c maple.p is_bm() ¨ç¦¡¾ã¦X¦b¤@°_¡C
+ gem.c ÅýºëµØ°Ï¤¤ [userA/userB ªº¦h¦ì¤pªO¥D¼Ò¦¡¤]¾A¥Î¡C
+
+[2001/12/21] bmtad.c ID ªø«×«ê¬° IDLEN ªº¨Ï¥ÎªÌµLªk¦¬¨Ó¦ÛInternetªº«H¥ó¡C
+
+[2001/12/22] pipstruct.h pip_menu.c pip_visio.c pip_quest.c etc/game/pip/quest/ ¹q¤lÂû¥ô°È¡C
+
+[2001/12/26] modes.h talk.c acct.c µo§b¥i¿é¤J²z¥Ñ¡C
+
+[2001/12/30] pip.h pip_fight.c pip_quest.c pip_item.c ­×¥¿ bug ¤Î¼W¥[¥ô°È¡C
+ post.c @board.hlp mail.c maple.p ·s¼WÂà¿ý¬ÝªO¤å³¹µ¹¨Ï¥ÎªÌ¡A¨Ã»P mbox ¾ã¦X¦b¤@°_¡C
+
+[2002/01/04] bmw.c global.h ¦^¤ô²y®É¥i¥Î ^R ^T ±²°Ê¡C
+
+[2002/01/05] bmw.c Åý¦^¤ô²y®É¡A¦b¤W¤èªº¦^ÅU¦³½bÀYÀH¤ô²y²¾°Ê¦Ó°Ê¡C
+ wd2brd.c wd2gem.c wd2usr.c WD->M3 Âà´«µ{¦¡¥i¤@¦¸Âà´«³æ¤H©Î¥þ¯¸¡C
+
+[2002/01/07] util/uno/*.c ¥[¤W chdir(BBSHOME); ¥H§K¨S¦b BBSHOME ¤U°õ¦æ¡C
+
+[2002/01/08] global.h ¥[¤J«öÁä¹ïÀ³µù¸Ñ¡C
+
+[2002/01/13] xover.c ­×¥¿­Y¨ú®ø every_Z ¥H«á¤£¯à¦A¶i every_Z¡A
+ ¥H¤Î¥Î every_Z ±q XZ_ ¦^ menu ®É³Ì«á¤@¦æ¥¼¦^´_¡C
+ vote.c ­×§ï§ë²¼¿ï¶µªº®É­Ô·|¹w³]¥X²{­ì¥ýªº¿ï¶µ¡C
+ maple.p mail.c post.c Åý¦Û¤v¥i¥H½s¿è¦Û¤v«H½cªº«Hªº¼ÐÃD¤Î¤º®e¡C
+ config.h post.c POST_PREFIX µoªí¤å³¹®É¿ï¾Ü¤å³¹¼ÐÃD¤ÀÃþ¡C
+
+[2002/01/14] post.c guest ¤£¯à¹ï¨ä¥L guest ªº¤å³¹¥[±K¡C
+
+[2002/01/17] struct.h vote.c pal.c maple.p ´£¨Ñ­­¨î¦W³æ§ë²¼¡C
+ acct.c §R°£¬ÝªO®É¶¶«K§R°£ gem/@/@Class ¤Uªº¬ÝªOºëµØ°Ï±¶®|¡C
+
+[2002/01/18] board.c Ben_Perm() ­×§ï¡C
+
+[2002/01/20] acct.c POP3 »{ÃҮɡA¥[¤J¥D¾÷¦WºÙ pop3.xxx ªº³s½u¡C
+
+[2002/01/22] newbrd.c ¦b¨S¦³³s¸p®ÉÁ|¦æ³s¸p¡A¦ý¨ú®ø¡A·|µo¥Í¿ù»~¡C
+ wd2bmw.c WD->M3 Âà´«¤ô²y°O¿ý¡C
+ menu.c ­×¥¿¾\Ū¯d¨¥ªO®É¡A­¶½XÅã¥Ü¿ù»~¤Î­Y«ê¦³ 5n ½g®É·|¦h¨q¤@­¶ªÅ¥Õ¡C
+
+[2002/01/23] board.c favor.c ¤£ºÞ¦³µL UFO_BRDPOST¡A¤@·§§ó·s¬ÝªOª¬ºA¡A¥H§K¥¼Åª¿O¤£·|«G¡C
+ acct.c XFile ¤À¤GÄæ¡A¨Ï¥i¥H½s¿è¤ñ¸û¦hÀɮסC
+ acct.c edit.c ¤T­Óñ¦WÀÉÀɮפÀ¶}¡C
+
+[2002/01/24] xover.c ¦b³Ì«á¤@­¶«ö KEY_PGDN ¥i½¨ì³Ì«e­±¡C
+
+[2002/01/25] config.h bmtad.c HOST_ALIAS §ì¥X¨Ó define¡C
+ mail.c ¯¸¤º internet mail ®É·|ÄdºI±H«H¨ì¥»¯¸ªº¡C
+
+[2002/01/26] config.h global.h credit.c menu.c HAVE_CREDIT ´£¨Ñ°O±b¥»¥\¯à¡C
+ mail.c ½Õ¾ã«H½cª©­±¨Ï©M¨ä¥L XZ_ ª©­±¬Û¦P¡C
+ admutil.c ¥þ¯¸/ªO¥D³q§iµ{¦¡­×¥¿¡C
+ wd2pip.c Âà´«¹q¤lÂûµ{¦¡¡C
+ wd2list.c Âà´«¯S®í¦W³æµ{¦¡¡C
+ global.h bbsd.c maple.p menu.c bmw.c ¤ô²yÂsÄý¬É­±¡C
+
+[2002/01/27] topgem.c ¤£Àˬd¯µ±K¬ÝªO¡C
+ topusr.c ª©­±­×¥¿¡C
+
+[2002/01/29] stock-open.c crontab ¨úªÑ¥«¸ê®Æ¡C
+ enews.c menu.c global.h struch.h enews-open.c run/kimo/* crontab ´£¨Ñ¹q¤l³ø¥\¯à¡C
+
+[2002/01/30] mail.c post.c global.h @mbox.hlp @board.hlp ²Î¤@¦b post/mbox ¤¤
+ ³£¬O¤p¼g x Âà¬ÝªO¡A¤j¼g X Âà¨Ï¥ÎªÌ¡C
+ xover.c ­YµL¿ï Tag ®É¡AAskTag ¤£°Ý°ÝÃD¡C
+
+[2002/02/17] config.h bbsd.c mail.c OVERDUE_MAILDEL Àˬd¹L´Á«H¥ó¡C
+ railway.c ¸¹½X§ï¬°ÅK¸ô¯¸ªº¨®¯¸¥N½X¡C
+ enews.c ­×¥¿¶W¹L¤@¦æªº¼ÐÃD·|¸õ¦æ¡C
+ config.h pipstruct.h pip_menu.c pip_pk.c cache.c maple.p ¹q¤lÂû PK ¹ï¾Ô¡C
+
+[2002/02/27] mail.c ¥´¥]¬ÝªO¤å³¹¡C
+
+[2002/03/01] visio.c Åý ESC + * + s µ¥±±¨î½X¦b¤å³¹½s¿è/¾\Ū¤]¾A¥Î¡C
+ config.h post.c board.c HAVE_BUCKET ´£¨Ñ¬ÝªO¤ô±í¥\¯à¡C
+ bank.c Âà±b¤]¥i¥HÂàª÷¹ô¡C
+ menu.c ¥D¿ï³æ¾\Ū¬ÝªO¡C
+
+[2002/03/02] board.c ­×¥¿¬Y¨ÇÁôÂêO·|³Q¬Ý¨£ªº°ÝÃD¡C
+
+[2002/03/05] post.c ­×¥¿Âà¹F¤å³¹¦^«H½c¡A¤å³¹¸Ì­±¼ÊºÙ¿ù»~¡C
+ xover.c Á×§K every_U ¶i¤J¤Ó¦h¼h¥H¤Î­×¥¿¬Y¨ÇÂ_½u°ÝÃD¡C
+ menu.c xover.c ¿ï³æ¯S®íÁäµ{¦¡§ó·s¡C
+
+[2002/03/07] config.h hdr.h @board.hlp post.c HAVE_LABELMARK ¬ÝªO¤å³¹¥[«Ý¬å¼Ð°O¥\¯à¡C
+
+[2002/03/08] post.c ­×¥¿¥Ñ every_Z ­«Âжi¤J¦ê±µ¼Ò¦¡/·s»D¾\Ū¡AÂ÷¶}·|µo¥Í¿ù»~¡C
+
+[2002/03/10] talk.c Áô§Îªº¤H¦b talk ®É¤£³Qª¾¹D©M½Ö talk¡C
+
+[2002/03/14] talk.c bwboard.c chat.c ½Í¤Ñ/²á¤Ñ/¤U´Ñ®É«ö Ctrl+Z ¦^¨Ó«á¿Ã¹õ¤£·|«ì´_¡C
+
+[2002/03/16] config.h talk.c visio.c bbsd.c bguard.c DETAIL_IDLETIME ®É±`§ó·s¶¢¸m®É¶¡¡C
+ bbsd.c §ó§ï¬G¶m§P©wµ{¦¡
+
+[2002/03/21] talk.c bmw.c maple.p post.c pal.c aloha.c §ó·s can_override()¡C
+
+[2002/03/25] shm.c cache.c maple.p account.c mailtoall.c bmtad.c bguard.c
+ pip_pk.c stock.c §â attach_shm() ¥Î lib/shm.c ¤¤ªº shm_new() ¨ú¥N¡C
+
+[2002/03/26] restoreacct.c ÁÙ­ì³Æ¥÷ªº .ACCT¡C
+
+[2002/04/01] poststat.c ­×¥¿¥»¶g/¥»¤ë/¥»¦~ªº¤Q¤j¸ÜÃDµLªkÅã¥Ü¡C
+
+[2002/04/02] mail.c ­×¥¿¸s²Õ±H«H¦pªG¦¬«H¤H¤Ó¦hªº¸Ü·|±¾±¼¡C
+
+[2002/04/05] song.c ¦bÂIºq®É¡A¶i¤J¬Ý§¹ºq³æ¦A¥X¨Ó«á¡A©³³¡ªº´£¥Ü°T®§·|®ø¥¢¡C
+ board.c ¬ÝªO«e«ö v/V ©ó .BRD ²Ä¤@­ÓªOµL®Ä¡C
+
+[2002/04/10] bmw.c maple.p post.c pal.c talk.c aloha.c ©Ò¦³ xo_write() ³£¾ã¦X°_¨Ó¡C
+ xyz.c BBSNET µ{¦¡­×¥¿¡C
+
+[2002/04/15] menu.c mail.c §ï vs_head()¡C
+
+[2002/04/27] bbsmail.c Á×§K SIGSEGV¡C
+
+[2002/04/31] newbrd.c §â³s¸p°O¿ý²¾¨ì run/newbrd/ ¤U¡C
+ vote.c §â§ë²¼°O¿ý²¾¨ì brd/_/@/G*¡C
+
+[2002/05/02] newbrd.c @cosign.hlp ­×§ï³s¸p¤º®e¡C
+
+[2002/05/04] board.c guest ¤£¯à±q¬ÝªO¦Cªí¥[¤J§Úªº³Ì·R¡C
+
+[2002/05/10] acct.c ¥i³]©w¨Ï¥ÎªÌ±b¸¹¡C
+
+[2002/05/14] setusr.c ¥i³]©w¨Ï¥ÎªÌ²ßºD¡C
+ ufo.h struct.h acct.c §â UFO ¿W¥ß¥X¨Ó¦¨¬° ufo.h¡C
+ acct.c ¥i³]©w¨Ï¥ÎªÌ²ßºD¡C
+
+[2002/05/23] liteon.c menu.c ·s¼W¶}¿O¹CÀ¸¡C
+
+[2002/05/29] mail.c @mbox.hlp «H¥ó³]©w¾\Ū°O¿ý¡C
+
+[2002/05/30] railway.c ·sª©µ{¦¡¡C
+
+[2002/05/31] talk.c ·sªº Talk µ{¦¡¡C
+ bbsnet.c ¿W¥ß©ó xyz.c¡C
+ visio.c save_foot() restore_foot() ­×¥¿¡C
+
+[2002/06/01] innbbsd/Makefile bbslib.c bbslink.c receive_article.c ncmbbs.h ncmbbs.c innd/ncmperm.bbs
+ ´£¨Ñ NoCeM ¥\¯à¡C
+
+[2002/06/02] menu.c xyz.c etc/loveletter ´£¨Ñ±¡®Ñ²£¥Í¾¹¡C
+ config.h global.h board.c talk.c HAVE_BRDPAL ´£¨Ñ¬ÝªO¦n¤Í¥\¯à¡C
+ config.h ufo.h talk.c bmw.c @ulist.hlp HAVE_SUPERCLOAK ´£¨Ñ¶W¯ÅÁô§Î(µµÁô)¥\¯à¡C
+ config.h global.h post.c HAVE_UNANONYMOUS_BOARD ¤Ï°Î¦WªO¡C
+
+[2002/06/08] account.c ªÅÀɮפ£¦bªO¤W keeplog¡C
+ usies-sort.c ­×¥¿¦pªG¨S¦³¤H¤W¯¸·|µo²{¿ù»~¡C
+
+[2002/06/11] post.c «Ý¬åªº¤å³¹¤£¯à mark¡C
+ config.h maple.p menu.c visio.c acct.c talk.c pal.c mine.c ²Î¤@®y¼Ð¬° (x, y)¡C
+
+[2002/06/12] post.c ­×¥¿¦b¦ê±µ¼Ò¦¡¤¤ªO¥D copy ¤å³¹¨ìºëµØ°Ï¡A¦^¦ê±µ·|¿ù»~¡C
+ post.c ­×¥¿¦b¦ê±µ¼Ò¦¡¤¤¾\ۤ峹 more() ®É¡AªO¥D«ö m ·|¥XÂñ¡C
+ etc/feast global.h menu.c bbsd.c ¿ï³æ feeter Åã¥Ü¸`¤é¡C
+ config.h mail.c post.c HAVE_DECORATE ªº³¡¤Àµ{¦¡§R°£¡C
+ mail.c ¥uÅýªO¥D§â¬ÝªO/ºëµØ°Ï¥´¥]¦^®a¡C
+
+[2002/06/13] check_newsfeeds.c Àˬd newsfeeds.bbs ¬O§_¦³¤£¦s¦bªº¬ÝªOÁÙÂà«Hªº¡C
+
+[2002/06/15] board.c favor.c ¤ÀÃþ¬ÝªO¤Î§Úªº³Ì·R³£¥i¥H¨Ï¥Î¦Û°Ê¸õ¥h¤U¤@­Ó¥¼Åª¬ÝªO¥\¯à¡C
+
+[2002/06/15] talk.c t_pager() ©Ú¦¬¼s¼½ªºµ{¦¡­×¥¿¡C
+ newbrd.c nbrd_browse() ÅýÂsÄý§ó¬yºZ¡C
+
+[2002/06/17] topusr.c ²Î­p¥»¤ë¹Ø¬P¡C
+
+[2002/06/18] xover.c xo_forward() µ{¦¡­×¥¿¡C
+
+[2002/06/19] bbsd.c ­×¥¿ multi-login ½ð¨ì¦Û¤v¥i¥H­«½Æ¬~¿ú¡C
+
+[2002/06/21] fb2usr.c sob2usr.c wd2usr.c Âà´«µ{¦¡­n«Ø usr/_/_/MF ³o¥Ø¿ý¡C
+ mail.c ¾\Ū«H¥ó¨ì¤@¥b«ö d §R°£¶l¥ó¥H«á­n­«·s¸ü¤J .DIR¡C
+
+[2002/06/23] stock.c µ{¦¡´«·s¡C
+
+[2002/07/02] camera.c µ{¦¡­×¥¿¡C
+
+[2002/07/03] mail.c m_internet() ª©­±§ó°Ê¡C
+ visio.c bmw.c §â¦^¤ô²y·h¥h³Ì¤W­±¤@¦æ¡C
+
+[2002/07/04] gem.c ºëµØ°Ï§R°£®É­n½T»{¡C
+ gem-index.c µ{¦¡­×¥¿¡C
+ src/Makefile ­×¥¿¦³¨Ç OS ·|¥X²{»yªk¿ù»~ªº°ÝÃD¡C
+ *.h *.c LINUX ¤U sys/time.h ´«¦¨ time.h¡C
+ wd2gem.c wd2usr.c ­×¥¿Âà´«¹LªººëµØ°Ï©MºëµØ°Ï¯Á¤Þ/²§°Ê½Ä¬ðªº°ÝÃD¡C
+
+[2002/07/08] menu.c pad_draw() ­×¥¿·|Â_½uªº°ÝÃD¡C
+
+[2002/07/12] bbsnet.c BBSNET ¤ä´© port ¤£¬O 23 ªº telnet¡C
+ classtable.c ¥\½Òªí¥\¯à¥[±j¡C
+
+[2002/07/13] menu.c vs_head() µ{¦¡­×¥¿¡C
+ railway.c ­×¥¿§ì¤£¨ìºô­¶·|¶i¤JµL½a°j°éªº°ÝÃD¡C
+
+[2002/07/16] bank.c ¶R§¹Åv­­·|´£¥Ü­«·s¤W¯¸¦r²´¡AÁ×§K¨Ï¥ÎªÌ¤@ª½­«ÂжRÅv­­¡C
+ cache.c talk.c µµÁô©ó§@ªÌ½u¤W¤Î¬d¸ß®É¤]¬Ý¤£¨£¡C
+
+[2002/07/18] bbsnet.c BBSNET ¥[¤J°O¿ý¡C
+ board.c gem/@/@class.hlp ·s¼W Zap ±¼¥þ³¡¬ÝªOªº¥\¯à¡C
+ talk.c ¨Ï¥ÎªÌ¦W³æ¬Ý¤£¨ìªO¤Íªº¬G¶m¡C
+ talk.c ©MÁô§Î¤H Talk ¤£¦b°ÊºA¤¤Åã¥Ü¡C
+
+[2002/07/20] bank.c ­×¥¿¶×¿ú¦³ bug ªº°ÝÃD¡C
+ enews.c enews-open.c run/kimo/L ©_¼¯·s»D·s¼W¥ð¶¢Ãþ¡C
+
+[2002/07/21] post.c Åý¤@¯ë¨Ï¥ÎªÌ¤]¯à¬Ý¨ì¶iªOµe­±ªº ANSI ½X
+
+[2002/07/25] etc/webx.conf.cwb ®ð¶H³ø¾É¡C
+ newbrd.c struct.h @cosign.hlp ·s¼W§@ªÌ¬d¸ß/³]©w¤Î³s¸p³]©wªº¥\¯à¡C
+
+[2002/07/26] railway.c ­×¥¿·|¯d¤U´Ý¾lÀɮתº°ÝÃD¡C
+ railway.c ­×¥¿¿ï³Ì«á¤@¦æªº¯¸®É·|Â_½u¡C
+ bbcall.c dreye.c enew.c fortune.c ¿ù¦r­q¥¿¡C
+ mail.c ¯¸ªø¥i¬Ý guest ªº«H½c¡C
+ acct.c newbrd.c ¶}·sªO³s¸p®É­nÀˬd¬ÝªOªO¦W¬O§_¦Xªk¡C
+
+[2002/07/28] km.c ¹CÀ¸¦¨¥\©Î¥¢±Ñ³£¥i±µÃö¡C
+
+[2002/07/30] check_newsfeeds.c Àˬd newsfeeds.bbs ¤¤¬O§_¦³¥¼Âà«Hªº¬ÝªO¡C
+
+[2002/08/05] bwboard.c ·s¼W­x´Ñ/·t´Ñ¹CÀ¸¡C
+
+[2002/08/06] show_id_in_USR.c ¥i¥H·j´M¬Y id ¦b .USR ¤¤ªº userno¡C
+
+[2002/08/07] acct.c ­×¥¿ 0Admin/QSetBoard ®É­Y¬O edit currboard ·|¥¢±Ñ¡C
+ board.c ­×¥¿¦b Boards ¬Ý§¹¬YªO¤å³¹«á¡A¦A±q¿ï³æ«ö s ¶i¥h¸ÓªO¡A­è¬Ý§¹ªº¤å³¹ÁÙ¬O¨S¬Ý§¹¡C
+ acct.c pop3.c POP3 »{ÃÒµ{¦¡­×¥¿¡C
+ bbsd.c Åý ps ®É¯à¥X²{ bbsd ¬O­þ­Ó¨Ï¥ÎªÌ¤Î¨Ó·½¡C
+
+[2002/08/08] post.c ¯¸ªø§ï¤å³¹¼ÐÃD®É¡A¤]¥i¶¶«K§ï¼ÊºÙ¡C
+
+[2002/08/09] etc/jcee/90.txt etc/jcee/91.txt jcee.c ´£¨Ñ 91 ¦~¤j¾ÇÁp©Û¬dº]¡C
+
+[2002/08/11] talk.c @ulist.hlp ¨Ï¥ÎªÌ¦W³æ¤Á´«¬G¶m/¤Í½Ë¡C
+
+[2002/08/12] src/innbbsd/Makefile innbbsd.h innbbsd.c ®³±¼ ADMINUSER¡C
+ menu.c ¯d¨¥ªO§ïÅܪ©­±¡C
+ stock-open.c ­×¥¿¬P´Á¤@¤£·|§ìªÑ¥«¦¬½L»ùªº°ÝÃD¡C
+
+[2002/08/13] config.h global.h maple.p edit.c post.c km.c bmtad.c mailpost.c ²¾°£ RANDOM_BANNER¡C
+ edit.c post.c km.c ¼W¥[¨ç¦¡ ve_banner()¡C
+
+[2002/08/14] modes.h struct.h bguard.c bmw.c cache.c talk.c visio.c BMW_COUNT µ{¦¡§ï¼g¡C
+
+[2002/08/16] talk.c @ulist.hlp ¨Ï¥ÎªÌ¦W³æ«ö L ÂsÄý¤ô²y¡C
+ bbsd.c ­×¥¿ multi-login ®É¿ù»~ªº acct_save()¡C
+
+[2002/08/17] newbrd.c credit.c aloha.c ²Î¤@ vs_head() ªº¥Îªk¡C
+ bbs.h config.h global.h theme.h *.c §G´º¥DÃD¡C
+
+[2002/08/21] visio.c ­×¥¿¦b¤W¯¸³~¤¤/ºëµØ°Ï idle ¹L¤[¡A·|¥X²{ status_foot¡C
+
+[2002/08/22] mailpost.c §â¤@¨Ç¨ç¦¡¥Î lib/ ¤¤ªº¥N±¼¡C
+ cache.c menu.c §â¶Ã¼Æ¿ï°ÊºA¬ÝªOªºµ{¦¡±q film_out() ²¾¨ì movie()¡C
+
+[2002/08/23] bmw.c theme.h ¦^¤ô²y®É«ö¤W¤UÁä§ï¦¨lastcmd¦Ó¤£¬O¦^«e«á¤@­Ó¨Ï¥ÎªÌ¡C
+
+[2002/08/28] bbsd.c ­×¥¿¦b¶}ÀYµe­±¹L¤[¥¼¿é¤J®É igetch ·|¥X²{ movie¡C
+ so/*.c pip.c ­×¥¿¦P¤@®É¶¡¤£¦P±b¸¹¶Ã¼Æ¤]¬Û¦Pªº°ÝÃD¡C
+ chessmj.c ¹CÀ¸¤@¶}©l®É´N¥ý¦©¤@¥÷¥»ª÷¡A¥H§Kª±®aª½±µ¸õ¥X®É¨S¦©¨ì¿ú¡C
+ bbs.h proto.h so/*.c ­×¥¿¶Ã¼Æµ{¦¡¡C
+ mail.c multi-send ®É¨ú®ø¦Ó·|³Q·í¦¨ MAIL_REPLIED¡C
+
+[2002/08/29] talk.c ¤W¯¸³qª¾/¨ó´M¡A§Y¨Ï§ÚÁô§Î¡A¹ï¤è¦pªG¦³¬Ý¨£Áô§Î¤]³qª¾¡C
+ bmw.c µµÁôªº¤H¥i¥H¥áµµÁôªº¤H¤ô²y¡C
+ menu.c ¿ï³æ¤p´T½Õ¾ã¡C
+ expire.c µ{¦¡­×§ï¡C
+ talk.c bwboard.c cache.c ²á¤Ñ¹ï¶HÁô§Î®É¦b¨Ï¥ÎªÌ¦W³æ°ÊºA¤£³Q¬Ý¨ì¡C
+ bbsd.c ufo.h maple.p §â¤W¯¸¤£°e¨ó´M¨Ö¤J UFO_NOALOHA¡C
+
+[2002/08/30] cola2brd.c cola2gem.c Cola Âà´«µ{¦¡¡C
+
+[2002/08/31] src/maple/Makefile src/util/Makefile solaris ªº Makefile¡C
+ menu.c SunOS ¤U¨S¦³ getloadavg()¡C
+ global.h bank.c account.c Âà±b°O¿ý¡C
+ calendar.c menu.c ¸U¦~¾ä¡C
+
+[2002/09/01] src/maple/Makefile src/so/Makefile src/pip/Makefile §ï¼g¡C
+ doc/b_postfix FreeBSD ¤U postfix + bbsmail + mailpost ªº¨Ï¥Î»¡©ú¡C
+ talk.c pal_ship() µ{¦¡­×¥¿¡C
+
+[2002/09/02] receive_article.c ­×¥¿¨Ï NoCeM notice ¦³®Ä¡C
+ gem/?/* ·s¼W¤@¨Ç»¡©ú¤å¥ó¡C
+
+[2002/09/04] edit.c ¤º½X¿é¤J¤u¨ãµ{¦¡­×¥¿¡C
+ rec_sync.c ·s¼W record ­«¾ã library¡C
+
+[2002/09/05] menu.c pushbox.c etc/game/pushbox.map ´£¨Ñ­Ü®wµf¹CÀ¸¡C
+ wd2*.c sob2*.c src/util/tran/Makefile ¤p§ï¼g¡C
+ talk.c ¤W¯¸³qª¾/¨ó´M¡AµµÁô¤£³qª¾¡C
+
+[2002/09/10] mag2*.c Magic Âà´«µ{¦¡¡C
+ make_new_USR.c µ{¦¡­×¥¿¡C
+ etc/game/qkmj/* QKMJ µ{¦¡ª©¥»§ó·s¡C
+
+[2002/09/11] receive_article.c µ{¦¡­×¥¿¡C
+
+[2002/09/14] pip.h pip_play.c pip_weapon.c gem.c railway.c µ{¦¡­×¥¿¡C
+
+[2002/09/15] bar.c ­×§ï§a¥xº¿²úªº¤¤¼ú¾÷²v¡C
+
+[2002/09/16] gem.c favor.c song.c vote.c µ{¦¡­×¥¿¡C
+
+[2002/09/17] acct.c ­×¥¿¥Î multi-login + User/Info ªº¤èªk¨Ó¨Ï¿ú­¿¼W¡C
+
+[2002/09/29] nine.c ¤Ñ¦a¤[¤[µ{¦¡§ï¼g¡C
+
+[2002/10/09] marie.c etc/game/marie ¤pº¿²úµ{¦¡§ï¼g¡C
+ more.c ­×¥¿¦b¤G­¶¥H¤Wªº¤å³¹¤¤«ö KEY_DEL µo²{¿ù»~¡C
+ song.c µ{¦¡­×¥¿¡C
+
+[2002/10/11] post.c ©Ø·¬¸¨¸­±Ù¥i¦Û©w§R°£ªº§@ªÌ¼ÐÃD¡C
+
+[2002/10/14] marie.c ¤pº¿²ú­­¨îÁ`©ãª÷¡C
+ bmtad.c bbsmail.c ¾× html ®æ¦¡ªº«H¥ó¡C
+
+[2002/10/15] marie.c ­×¥¿»ÊÁ´fÅU®É¤¤¼ú¶µ¥Ø·|¥X²{¿ù»~¡C
+ menu.c tetris.c ·s¼W«Xù´µ¤è¶ô¹CÀ¸¡C
+
+[2002/10/18] fortune.c ¹B¶Õ¹w´úºô­¶¸ô®|­×¥¿¡C
+ gem.c ¶i¤JªÅºëµØ°Ï¥i¥H°¨¤W¶K½Æ¡C
+
+[2002/10/21] acct.c ¼W¥[§R°£±b¸¹ªº¿ï¶µ¡C
+
+[2002/10/26] wd2usr.c wd2gem.c µ{¦¡­×¥¿¡C
+ sob.h sob2brd.c sob2gem.c sob2usr.c SOB Âà´«µ{¦¡§ï¼g¡C
+
+[2002/10/27] post.c ­×¥¿¥[±K¤å³¹¥i¥ô·NÂà¹F¨Ï¥ÎªÌ¡C
+
+[2002/10/28] post.c ­×¥¿ guest ¥iÂǦ^§@ªÌ«H½c¨Ó±H«H¡C
+ mail.c ¸s²Õ¦^«Hµ{¦¡§ï¼g¡C
+
+[2002/10/29] gem.c ­×¥¿ºëµØ°Ï§R°£¤å³¹§¹¥H«á¡A¥Ø¿ý¤º³Ñ 0 ½g¡A¿Ã¹õ·|¤£°®²b¡C
+
+[2002/10/31] post.c ­×¥¿ guest ¥iÂà¹F¤å³¹¡C
+ src/pip/* ¹q¤lÂû¹CÀ¸·s¼W¤É¯Å¥ô°È¡BªZ¾¹¨t²Î¡C
+ enews.c ­×¥¿©_¼¯·s»DÂà¿ý¨ú®ø¿Ã¹õ¿ù¶Ãªº°ÝÃD¡C
+
+[2002/11/01] board.c favor.c config.h ENHANCED_BSHM_UPDATE
+ ¬ÝªO¦Cªí§R°£/¼Ð°O¤å³¹¤£¦C¤J¥¼Åªªº¿O¡C
+
+[2002/11/02] bmw.c config.h ¤ô²y¦sÃÒ¡C
+ song.c ­­¨î¨C¤ÑÂIºq¦¸¼Æ¡C
+
+[2002/11/05] bguard.c bmw.c talk.c §âÀˬd¬O§_¯à¬Ý¨£¹ï¤èªºµ{¦¡¾ã²z¦b¤@°_¡C
+
+[2002/11/07] src/web/ html/ WEB-BBS ¥\¯à¡C
+
+[2002/11/11] bj.c chessmj.c fantan.c gp.c seven.c §ïÅܨúµPªº¶Ã¼Æµ{¦¡¡C
+
+[2002/11/13] showUSR.c µ{¦¡§ï¼g¡C
+
+[2002/11/14] Makefile ´£¨Ñ make update ªº«ü¥O¡C
+
+[2002/11/17] make_new_USR.c ­×¥¿¿ù»~¡C
+
+[2002/11/22] xover.c vote.c §âÃþ XZ_ µ²ºcªº´å¼Ð²¾°Êµ{¦¡¾ã²z¦b¤@°_¡C
+ xover.c maple.p camera.c struct.h *.c §â help ¿W¥ß¥X film_out Åܦ¨¿ï³æ¡C
+
+[2002/11/26] favor.c ­×¥¿ºëµØ°Ï±¶®|ªO¥DÅv­­¿ù»~ªº°ÝÃD¡C
+
+[2002/12/04] favor.c ·s¼W§Úªº³Ì·R¿ï¨ú¬ÝªO¥\¯à¡C
+
+[2002/12/05] menu.c ­×¥¿ #define COLOR_HEADER ®É¡A¶l®t¨Ó«ö¹aªºÃC¦â·|¤£¹ï¡C
+ talk.c ­Y¦b Talk ®É«ö¤F¤@°ï¦L¤£¥X¨Óªº¦r¡A´N¤£¼g¶i talk-log¡C
+ *.c mail_self() ¦h¤@­Ó°Ñ¼Æ¡A§ó¤è«K¨Ï¥Î¡C
+ talk.c §â mail_self() ¾ã¦X¶i talk_save()¡C
+ talk.c ­×¥¿ Talk ®É³Ì«á¤@¥y¸Ü¤£·|³Q°O¦b½Í¤Ñ°O¿ý¤¤¡C
+ talk.c ­×¥¿ Talk ®É·í´å¼Ð¦A¹ï¤è°Ï°ì®É«ö¤UCtrl+k·|µo¥Í¿ù»~¡C
+
+[2002/12/06] board.c favor.c theme.h maple.p Åý¬ÝªO¦Cªí¤¤ªº¯µ±KªO¡B¦n¤ÍªO¡BZAP ªO¼Ð¥Ü²M·¡¡C
+
+[2002/12/09] enews-open.c §ì©_¼¯·s»Dªºµ{¦¡§ó·s¡C
+
+[2002/12/10] pal.c ½s¿èªO¤Í¦W³æ®É¡A¤Þ¤J¦n¤Í¦W³æ¡C
+
+[2002/12/13] global.h struct.h account.c board.c ¥i¥H zap ±¼¤ÀÃþ¡C
+
+[2002/12/15] account.c µ{¦¡§ï¼g¡C
+
+[2002/12/16] bbslib.c ¦pªG nodelist.bbs ¸Ì­±¶ñªº¤£¬O¥¿¸Ñ¡A¹ï¤èÁÙ¬O¥i¥H access¡C
+ mail.c ­×§ï¸s²Õ±H«H®É¡A­Y¦W³æ¹L¦h·|Â_½u¡C
+
+[2002/12/18] song.c µ{¦¡­×¥¿¡C
+ bbslink.c news ¿é¥X RFC 2045¡C
+
+[2002/12/21] dns_ident.c dl_lib.c µ{¦¡§ó·s¡C
+
+[2002/12/22] *.c Query µ{¦¡§ó·s¡C
+
+[2002/12/24] struct.h acct.c bbsd.c §â±K½Xªø«×²Î¤@­­©w¬° 8¡C
+ edit.c ¦]Â_½u¥¼§¹¦¨ªº¤å³¹¥i¿ï¾Ü¥á¥h¼È¦sÀɩΫH½c¡C
+
+[2002/12/26] post.c ¦b¦ê±µ¼Ò¦¡¤U edit ¤å³¹§¹¡AÀ³¸Ó¥X²{ xpost_head() ¦Ó¤£¬O post_head()¡C
+
+[2002/12/27] xover.c post.hlp gem.hlp ²¾°£ Z-modem ¤U¸ü¥\¯à¡C
+ fortune.c menu.c ²¾°£¹B¶Õ¹w´ú¥\¯à¡C
+ bbcall.c menu.c ²¾°£ BBCall ¥\¯à¡C
+ emailpage.c mail.c bmtad.c ²¾°£ emailpage ¥\¯à¡C
+ config.h edit.c §R°£ HAVE_ORIGIN¡C
+ config.h km.c edit.c talk.c §R°£ REALINFO¡C
+ cache.c µ{¦¡§ó·s¡C
+ bingo.c µ{¦¡§ó·s¡C
+
+[2002/12/28] camera.c menu.c config.h µ{¦¡§ó·s¡C
+
+[2002/12/30] camera.c cache.c str_rle.c fshm ¤£À£ÁY¡C
+
+[2003/01/01] talk.c ­×¥¿¯¸ªø°µ¦n¤Í¼s¼½®É¥i¯à·|»~´Óªº°ÝÃD¡C
+ ufo.h acct.c §â²ßºDªºªø«×§ì¥X¨Ó define ¦¨ NUMUFOS¡C
+ bmta.sh ¤ÀªR bmta.log ªº³s½u¦¸¼Æ¡C
+
+[2003/01/02] pip_fight.c ­×¥¿¦³¨Ç§Þ¯à¾Ç¨ì¥H«áµLªk¨Ï¥Îªº°ÝÃD¡C
+
+[2003/01/03] src/innbbsd/*.c µ{¦¡ºë²¡C
+ *.c ²¾°£ HAVE_NOFORWARD¡A¨Ã§â NUMATTRS ©M STR_BATTR §ì¥X¨Ó©w¸q¡C
+ *.c ½Õ¾ã¬Y¨Ç¯¸ªøÅv­­¡C
+
+[2003/01/10] topgem.c µ{¦¡§ó·s¡C
+
+[2003/01/11] more.c µ{¦¡´«·s¡A­×¥¿¤W±²¤@¦æ¿ù»~¡B·j´M¦r¦ê·|§ä¤£¨ìªº°ÝÃD¡C
+ innbbsd/* Âà«Hµ{¦¡§ïª©¡C
+ aloha.c newbrd.c µ{¦¡§ó·s¡C
+ xover.c ­×¥¿¦b help ®É«ö KEY_HOME ·|Â_½uªº°ÝÃD¡C
+
+[2003/01/12] bmw.c ­×¥¿¤ô²yÀx¦sÅܦ¨¬Û¤Ï¹ï¸Üªº°ÝÃD¡C
+
+[2003/01/13] song.c °Î¦WÂIºq¤]ºâ¤J¨C¤é¥u¯àÂI¤T­º¡C
+
+[2003/01/15] innbbsd/* µ{¦¡§ó·s¡C
+ xover.c ­×¥¿ every_Z ¶i¤J¬ÝªO®É¥i¯à³y¦¨¤å³¹¤wŪ/¥¼Åª¿ù»~¡C
+
+[2003/01/19] admutil.c ¯¸ªø­«¸m¨t²Î®ÉÀx¦s¬ÝªO¾\Ū°O¿ý¡C
+ modes.h menu.c ®³±¼ M_CLASS¡C
+
+[2003/01/23] railway.c ÀH¥xÅKªº®æ¦¡§ó·s¡C
+
+[2003/02/07] enews.c ©_¼¯·s»DÂà±H¨ì¯¸¥~¡C
+
+[2003/02/08] edit.c ­­¨î¼È¦sÀɤj¤p¡C
+ marie.c ­°§C¤j¤pº¿²ú¹CÀ¸ªº´Á±æ­È¡C
+ dice.c ­°§C¨gÂY»ë¤l¹CÀ¸ªº´Á±æ­È¡C
+ xover.c enews.c mail.c ¾ã²z DENYMAIL ªºµ{¦¡¡C
+
+[2003/02/11] cola.h cola2brd.c cola2gem.c cola2usr.c ColaBBS Âà´«µ{¦¡¡C
+
+[2003/02/19] innbbsd/* innd/newsfeeds.bbs etc/b2g_table etc/g2b_table ²ÁcÅéÂà«H¡C
+
+[2003/02/21] cola2post.c Cola ¬ÝªO¤å³¹®æ¦¡Âà´«¡C
+
+[2003/02/23] post.c ­×¥¿Âà¿ý¤å³¹¨ì¤£°µ¼öªù¸ÜÃD²Î­pªº¬ÝªO¡A³o½g³QÂà¿ýªº¤å³¹¤´µM·|³Q¦C¤J²Î­p¡C
+ acct.c ¥u¦³ PERM_SYSOP ¯àÅܧó¨ä¥L¯¸°Èªº±K½X¡C
+
+[2003/02/25] cola2gem.c sob2gem.c wd2gem.c ­×¥¿¤l¥Ø¿ýÂà´«¥¢±Ñªº°ÝÃD¡C
+
+[2003/03/02] brdmail.c ¥Ñ Internet ±H«Hµ¹ BBS ¯¸¤º¬ÝªO¡C
+
+[2003/03/03] counter.c ¾ú¥v­y¸ñ¡C
+
+[2003/03/04] enews-open.c ©_¼¯·s»D¼ÐÃD¤Óªø®É·|¦³¿ù»~¡C
+ song.c ºq¥»¥[±K®É¤£¯àÂI¨º­ººq¡C
+
+[2003/03/05] edit.c ñ¦WÀɤ]ºâ¤Þ¨¥ªø«×¡C
+
+[2003/03/06] song.c global.h ºq¥»§ï©ñ¦b ktv ªOºëµØ°Ï¡C
+ gem-index.c ºëµØ°Ï¯Á¤Þ/²§°Ê ¥iÁôÂáC
+
+[2003/03/07] gem.c ­×¥¿ºëµØ°Ï½Æ»sªº³¡¤Àµ{¦¡¡C
+ hdr.h *.c ¤£¥Î lazy_delete¡Arec_del() ®³±¼¤@­ÓÄæ¦ì¡APOST_DELETE ²¾°£¡C
+ hdr.h post.c POST_LABEL ´«½s¸¹¡C
+ *.c §ó·s record ®É­nÀˬd¡C
+ rec_article.c ¦¬¨ì cancel ¤å³¹ª½±µ§R°£¡A¤£ lazy_delete¡C
+ hdr.h *.c POST_CANCEL ²¾°£¡C
+
+[2003/03/10] vote.c ¤À¨­¤£¯à§ë²¼¡C
+ rec_loc.c bmw.c aloha.c °Ï¬q§R°£µ{¦¡­×¥¿¡C
+
+[2003/03/15] edit.c Show ñ¦WÀɮɡA«e¨â­Ó¥u¯à¨q¥X MAXSIGLINES-1 ¦æ¡C
+
+[2003/03/23] bmtad.c ±H«Hµ¹¬ÝªO¡C
+ mail.c mail ¿é¥X RFC 2045¡C
+
+[2003/03/25] hdr_stamp.c post.c enews.c gem.c hdr_stamp() ·s¼W HDR_COPY ³o­Ó token¡C
+ bank.c Âà±bµ{¦¡­×¥¿¡C
+ post.c outgo.c Á×§K¨S¦³ nick ®É·|³y¦¨Âà«H¥¢±Ñ¡C
+ expire.c µ{¦¡¤p§ïª©¡C
+
+[2003/03/26] admutil.c ­×¥¿¥þ¯¸/ªO¥D³q§i³¡¤Àµ{¦¡¡A¨Ã¥[¤W¶l®t¨Ó«ö¹a¡C
+
+[4~[2003/03/27] post.c mail.c §ï¼ÐÃD®É­Y¨S¦³ÅܰʡA«h¤£°Ý Y/N¡C
+ more.c µ{¦¡­×¥¿¡C
+
+[2003/03/30] rec_article.c ­×¥¿¿é¤J RFC 2047 »P ANSI ±±¨î½Xªº°ÝÃD¡C
+
+[2003/03/31] pip_race.c ¦¬Ã¬©u²i¶¹¤ñÁÉ·s¼Wµæ¦â¿ï¾Ü¡C
+
+[2003/04/01] gem.c ºëµØ°Ïªº²Å¸¹¤À²M·¡¤@ÂI¡C
+
+[2003/04/07] post.c mail.c hdr_out() ±q mail.c ·h¥h post.c¡C
+ post.c §R°£ getsubject()¡C
+ rec_put.c µ{¦¡­×§ï¡C
+
+[2003/04/08] xover.c Ãþ XZ_* µ²ºcªº´å¼Ð²¾°Êµ{¦¡­×§ï¡C
+
+[2003/04/11] rfc2047.c dao.p bbslink.c mail.c news/mail ¿é¥X RFC 2047¡C
+
+[2003/04/15] inntobbs.c inntobbs.h rec_article.c ¨S¥Î¨ìªº HEADER ´N¤£¨ú¤F¡C
+
+[2003/04/16] board.c ²Ä¤G¦¸¶i¤J board_main ®É¡A­n free ±¼ class_img¡C
+
+[2003/04/22] talk.c config.h theme.h ±N #ndef ADV_ULIST ªºµ{¦¡§R°£¡C
+ aloha.c bmw.c pal.c xxxx_body() ²Î¤@©M¨ä¥L®æ¦¡¤@¼Ë¡C
+ talk.c ulist_body() ²Î¤@©M¨ä¥L®æ¦¡¤@¼Ë¡A¨Ã¿W¥ß¥X ulist_item¡C
+ railway.c ¥xÅKºô­¶®æ¦¡ÅܰʡC
+
+[2003/04/23] post.c bmtad.c topgem.c bwboard.c ­×§ï¤@¤U¨Ï²Å¦X gcc 3.2.2¡C
+
+[2003/04/26] talk.c ulist_cb[] µ{¦¡­×¥¿¡C
+ xover.c ­×¥¿©ó help ·s¼W»¡©ú®É¡A­Y¶W¹L¤@­¶·|µo¥Í¿ù»~¡C
+
+[2003/05/03] edit.c ª`­µ¤å¹LÂo¡C
+
+[2003/05/04] Makefile ¥þ³¡²Î¤@¼gªk¡C
+
+[2003/05/10] str_ansi.c bmtad.c rec_article.c bbsmail.c brdmail.c mailpost.c cola2post.c
+ §â strip_ansi ªºµ{¦¡²Î¤@¦b lib/¡C
+ cola2brd.c fb2brd.c mag2brd.c sob2brd.c wd2brd.c
+ Âà´«µ{¦¡³B²z¤å³¹¼ÐÃD®É¥[¤J strip_ansi¡C
+ mak_dirs.c bbsd.c cola2brd.c fb2brd.c mag2brd.c sob2brd.c wd2brd.c transacct.c
+ §â mak_links ªºµ{¦¡²Î¤@¦b lib/¡C
+ help.c xover.c xo_help() ·h¥h so/¡C
+
+[2003/05/11] innbbsd ³¡¤Àµ{¦¡§ï¼g¡C
+
+[2003/05/13] bmtad.c bbsmail.c ¾×±¼«D big5 ªº«H¥ó¡C
+
+[2003/05/14] config.h visio.c bguard.c TIME_KICKER ¬O§_¦Û°Êñ°h idle ¹L¤[ªº¨Ï¥ÎªÌ¡C
+ bbs.h Makefile ®³±¼ REDHAT ªº³¡¤À¡C
+
+[2003/05/15] modes.h board.c post.c xover.c ±N STAT_BOARD »P STAT_BM ¤ÀÂ÷¥X¨Ó¡C
+ post.c xover.c ¯¸ªøµLªk¬Ý¨ì¥[±K¤å³¹¡C
+
+[2003/05/24] showDIR.c ¨q¥X .DIR ªº¸ê°T¡C
+
+[2003/05/25] board.c ­×¥¿¿ù»~ªº¬ÝªO¾\Ū°O¿ý·|·í¦b¤W¯¸µe­±¡C
+ mail.c ±H¥Xªº«HªºÀÉÀY From Äæ¦ì¥[¤W real name Äæ¦ì¡C
+
+[2003/05/26] lottery.c lottery-open.c etc/game/lottery.main etc/game/lottery.rule ¼Ö³z¹CÀ¸¡C
+
+[2003/05/27] *.c *.h ¤ä´©¶W¹L 24 ¦Cªø¿Ã¹õªºµe­±¡C
+ util/Makefile §ï¼g¡C
+
+[2003/05/28] post.c ¦ê¦C/·s»Dµ{¦¡§ï¼g¡C
+
+[2003/06/04] config.h bmtad.c bbsmail.c mail.c §â MYCHARSET §ì¥X¨Ó©w¸q¡C
+
+[2003/06/05] cache.c post.c utmp_get() µ{¦¡­×¥¿¡C
+ hdr.h gem.c xover.c song.c camera.c gem-index.c gem-check.c §R°£ GEM_HTTP ¤Î GEM_URL¡C
+
+[2003/06/06] innbbsd/* Âà«Hµ{¦¡­×§ï¡C
+
+[2003/06/07] post.c mail.c maple.p §â tag ¼Ð * ªº§¤¼Ð±q (x, 8) ²¾¨ì (x, 6)¡C
+ config.h hdr.h post.c ¤å³¹µû¤À¥\¯à¡C
+
+[2003/06/11] global.h *.c ¨Ï¥Î¥þ°ìÅÜ¼Æ currbno ¨Ó¥N´À brd_bno(currboard)¡C
+
+[2003/06/13] rec_article.c «O¯d³Q cancel ªº¤å³¹©ó deleted ªO¡C
+
+[2003/06/18] global.h post.c edit.c ¨Ï¥Î¥þ°ìÅÜ¼Æ currbattr ±N battr ¿W¥ß¥X bbstate¡C
+
+[2003/06/19] bmw.c mail.c §R°£ bmw_choose() ªº¥\¯à¡C
+ bmw.c theme.h visio.c talk.c §ïÅܤô²y®æ¦¡¡C
+
+[2003/06/20] bmw.c visio.c ¦b reply ¤ô²y®É·|¨q¥X¤W¦¸¶Çµ¹³o¤Hªº¸Ü¡C
+
+[2003/06/27] global.h ufo.h visio.c more.c acct.c ²¾°£ UFO_COLOR¡C
+
+[2003/06/28] battr.h struct.h bbs.h acct.c ±N battr ¾ã²z¨ì battr.h¡C
+ bbslink.c ­×¥¿ cmsg »P­n¬åªº¤å³¹ Message-ID ¬Û¦P·|³Q news server ©Úµ´ªº°ÝÃD¡C
+ rec_article.c brdmail.c bmtad.c ®³±¼ Âà«H¯¸: ¤Î Origin: ªº¤å³¹ÀÉÀY¡C
+ more.c ¤£°»´ú Âà«H¯¸: ¤Î Origin: ªº¤å³¹ÀÉÀY¡C
+
+[2003/06/30] struct.h acct.c board.c post.c gem.c vote.c newbrd.c tran/*brd.c account.c
+ ¼W¥[ BRD.class ¬ÝªO¤ÀÃþÄæ¦ì¡C
+ theme.h board.c §ì¥X ICON_(NO)TRAN_BRD ¨Ó©w¸q¡C
+ windtop.h windtop2usr.c windtop2brd.c WindTop Âà´«µ{¦¡¡C
+
+[2003/07/02] bbsd.c ¤¹³\ belong_list() ¤¤ªº desc ¥i¥H¦³ªÅ¥Õ¡C
+
+[2003/07/03] weather.sh ¤Ñ®ð¹w³ø¥[¤W more ÀÉÀY¡C
+ topusr.c ²Î­p©Ê§O¡C
+
+[2003/07/04] bbslink.c ±q¤W¦¸¤¤Â_³BÄ~Äò°e«H¡C
+ admutil.c ¼f§¹µù¥U³æ³q¹L¡A·|¨Ï·í®É¦b½u¤Wªº¨Ï¥ÎªÌ«G°_¶l®t¨Ó«ö¹a¡C
+ acct.c showACCT.c topusr.c §ïÅÜ sex Äæ¦ì¹ï©Ê§Oªº©w¸q¡A¼W¥[¤¤©Ê¡C
+
+[2003/07/05] struct.h ufo.h perm.h ±N PERM_DATALOCK PERM_COINLOCK UFO_BIFF
+ UFO_REJECT UFO_BIRTHDAY UFO_MQUOTA ¿W¥ß¥X¨Ó¦¨¬° UTMP.status¡C
+ ±N cuser.ufo ¤Î cutmp->ufo ¦P¨B¡C
+
+[2003/07/06] bbslink.c bbsnnrp.c ¤ä´©»Ý­n±b¸¹/±K½Xªº news server¡C
+ talk.c bmw.c ¼s¼½µ{¦¡­×¥¿¡C
+
+[2003/07/07] bhttpd.c ²©öªº Web-BBS¡C
+
+[2003/07/09] transbrd.c .BRD Âà´«µ{¦¡¡C
+
+[2003/07/13] ±N doc ´«¦¨ html ÀÉ¡C
+
+[2003/07/14] decode.ic §R°£¡C
+ mailpost.c µ{¦¡§ï¼g¡C
+
+[2003/07/15] tetris.c µ{¦¡§ï¼g¡C
+
+[2003/07/18] bmw.c ­×¥¿­Y¹ï¤è¤U¯¸¤F¡A¤ô²y¥i¯à·|¥á¿ù¤H¡C
+ board.c ¥þ°ì·j´M¥i¦P®É·j´M¼ÐÃD¡C
+ pal.c ¦n¤Í/¸s²Õ¦W³æ¼s¼½¡C
+ bbsnnrp.c ¤j´T§ï¼g¡C
+
+[2003/07/19] gem.c post.c mail.c edit.c vote.c ¼ÐÃD vget() ªø«×ªº­×§ï¡C
+ admutil.c ¥þ¯¸¨Ï¥ÎªÌ/ªO¥D±H«Hµ{¦¡­×§ï¡C
+
+[2003/07/20] hdr.h hdr_fpath.c gem.c §â GEM_EXTEND ªº¥\¯à²¾°£¡C
+ gem.c ­×¥¿ªO¥D¥i¥H¦b¨ä¥LªO¨Ï¥Î gem_state¡C
+
+[2003/07/22] bmw.c »P¯S©w¹ï¶Hªº¤ô²y¦s¦Ü«H½c¡C
+
+[2003/07/23] dragon.c ±µÀs¹CÀ¸¡C
+ config.h window.c edit.c visio.c ÂÛ¥X¦¡µøµ¡¡C
+ etc/game/pip ¸É»ô¹q¤lÂûªº´¡¹Ï¡C
+ account.c Àˬd CH_MAX ¬O§_¤p©ó Class ¼Æ¶q¡C
+
+[2003/07/24] camera.c ¨t²Î¤å¥ó¦³¯Ê®É¡A·|¦Û°Ê¸É¤W³qª¾¡C
+
+[2003/07/25] account.c ¾ã²z .BRD ¤¤ªÅ¥ÕªºÄæ¦ì¡C
+ bmw.c visio.c Ctrl+T ¥i¥H¦^§OÁû¤ô²y¡C
+
+[2003/07/27] post.c manage.c ±NªO¥D¿ï³æ¾ã¦X¦b¤@°_¡C
+ bhttpd.c bshm µ{¦¡§ï¼g¡C
+ gemd.c ­×¥¿¥i¥H¥Ñ gopher ¬Ý¯µ±KªOºëµØ°Ïªº°ÝÃD¡C
+
+[2003/07/28] bbsd.c mail.c global.h ±N etc/* ªºÀɮ׸ô®|¾ã²z¥X¨Ó FN_ETC_*¡C
+
+[2003/07/31] xover.c every_Z ­×¥¿ÂÇµÛ ^Z ¸õ¥h§OªO¦A¦^³oªO®Éªº¿ù»~¡C
+
+[2003/08/01] bbsd.c ªø¿Ã¹õµ{¦¡­×¥¿¡C
+
+[2003/08/03] Makefile make update ­Y¥X²{ error «h©¿²¤¡C
+
+[2003/08/07] pal.c ­×¥¿ªO¤Í¦W³æ¤Þ¤J¦n¤Í¿ù»~¡C
+
+[2003/08/09] jcee.c etc/jcee/92.txt 92 ¦~¬dº]ªA°È¡C
+
+[2003/08/13] struct.h enews.c xover.c ²¾°£ MQ_UUENCODE¡C
+ struct.h mail.c ­×¥¿«H¥ó¥´¥]®É Content-Type ¿ù»~¡C
+
+[2003/08/15] board.c ·j´M¤U¤@­Ó¥¼Åª¬ÝªO¡C
+ rec_del.c µ{¦¡­×¥¿¡C
+
+[2003/08/16] aloha.c ¥Î rec_del ¨Ó¨ú¥N rec_loc¡C
+ src/lib ­«·s¾ã²z¡A±N¨S¥Î¨ìªº§R°£¡C
+ bbsmail.c brdmail.c ¸Ñ½X quoted-printable/base64 ªº«H¥ó¡C
+ str_decode.c ­×¥¿·í«H¥ó¤º¤å¬° QP ½s½X®É·|µo¥Í¿ù»~¡C
+ str_decode.c bbsmail.c brdmail.c bmtad.c ±N¨ú¥X charset ¤Î encode ªºµ{¦¡¶°¤¤¨ì lib¡C
+
+[2003/08/17] bmw.c µ{¦¡­×¥¿¡C
+
+[2003/08/23] src/Makefile src/game/ ±N so/ ¤Uªº¹CÀ¸¤Îºô¸ôªA°È·h¨ì game/¡C
+ str_has.c acct.c board.c bmtad.c ªO¥D¦W³æµ{¦¡³¡¤À¾ã¨Ö¡C
+ edit.c ­×¥¿Â²¤Æ½s¿è¾¹¡C
+
+[2003/08/24] todo.c etc/todo.welcome global.h menu.c ¦æ¨Æ¾ä¡C
+
+[2003/08/27] bbsnnrp.c ­×¥¿·í host/active ¤£¦s¦b®É¡AµLªk unlock ªº°ÝÃD¡C
+ favor.c §Úªº³Ì·R·j´M¤U¤@­Ó¥¼Åª¬ÝªO¡C
+
+[2003/08/29] stock.c µ{¦¡­×¥¿¡C
+
+[2003/09/06] pal.c ¦n¤Í/ªO¤Í/¯S§O¦W³æ¥æ¤e¤Þ¥Î¡C
+ vote.c §ë²¼¨ì¤@¥b¡AªO¥D¥i¥H°½¬Ý¥Ø«e§ë²¼ª¬ªp¡C
+
+[2003/09/07] pal.c ¦n¤Í¦W³æ°Ï¬q§R°£¡C
+ config.h menu.c acct.c §R°£ HAVE_REPORT °O¿ý°£¿ùªºµ{¦¡¡C
+ visio.c ANSILINELEN µ{¦¡­×¥¿¡C
+
+[2003/09/11] account.c admutil.c ­«¸m¨t²Î®É¤£°µ¼öªù¸ÜÃD²Î­p¡C
+
+[2003/09/12] favor.c §Úªº³Ì·R°Ï¬q§R°£¡C
+
+[2003/09/13] bhttpd.c ´£¨Ñ email µoªí¤å³¹¡C
+ bguard.c talk.c ¯¸ªø kick ¨Ï¥ÎªÌ®ÉÀ³±N ushm->count ´î¤@¡C
+
+[2003/09/14] mail.c ¯¸°È«H½c¿ï ID ®É¥i¥H«öªÅ¥ÕÁä¦Û°Ê·j´M¡C
+ post.c ¥þ¤å·j´M¡C
+
+[2003/09/23] xpost.c post.c mbox.c ±N¦ê±µ·j´M¼Ò¦¡¾ã¦X¤J«H½c¡C
+
+[2003/09/26] bbsd.c mail.c ±N m_count() Àˬd«H½c¤w¸g¶W¹L¤W­­ªº³¡¤À²¾¨ì bbsd.c¡C
+ mail.c ±N¦^«Hªºµ{¦¡¾ã¨Ö¨ì do_mreply() ¸Ì­±¡C
+ mail.c post.c §ï¼g mbox_browse() ¤Î post_browse()¡C
+ mail.c xpost.c ¦ê±µ¶l¥ó¤¤¥i¦^«H¡C
+ menu.c mail.c admutil.c ±N¿ï³æ¤¤·|¥Î¨ìªº¶l¥ó¿ï¶µ³£§ï¦W¬° m_xxxx()¡C
+ menu.c acct.c admutil.c ±N¿ï³æ¤¤·|¥Î¨ìªº¯¸°È¿ï¶µ³£§ï¦W¬° a_xxxx()¡C
+
+[2003/09/27] post.c gem.c ºëµØ°ÏÂà¿ý¨ì¬ÝªO¡BÂà¹F¨Ï¥ÎªÌ¡C
+
+[2003/09/28] bbsd.c §ï¼g tn_login()¡C
+
+[2003/09/29] ufo.h *.c ±N HAS_STATUS ¥¨¶°¾ã²z¥X¨Ó¡C
+
+[2003/10/01] xover.c bmw.c pal.c favor.c aloha.c post.c mail.c gem.c ±N°Ï¬q§R°£ªºµ{¦¡¾ã¦X¦b¤@°_¡C
+
+[2003/10/03] gem.c global.h backupgem.c restoregem.c ¼o°£ºëµØ°Ï¸ê·½¦^¦¬µ©ªº¥\¯à¡C
+ gem.c §ï¼gºëµØ°Ï§R°£ªºµ{¦¡¡C
+ xover.c post.c mail.c gem.c ±N¼ÐÅÒ§R°£ªºµ{¦¡¾ã¦X¦b¤@°_¡C
+
+[2003/10/04] edit.c post.c modes.h µo¤å¥[±K¦sÀÉ¡C
+ enews.c µ{¦¡­×¥¿¡C
+
+[2003/10/08] gem.c ­×¥¿¥[±K¤å³¹¥iÂÇ¥ÑºëµØ°Ïªþ¥[ÀɮרӰ½¬Ý¡C
+
+[2003/10/20] bhttpd.c ¤¹³\¨Ï¥ÎªÌµn¤J¡C
+
+[2003/11/09] edit.c ¶Ã¼Æ¿ï¾Üñ¦WÀÉ¡C
+
+[2003/11/10] stock.c stock-open.c ²¾°£ªÑ¥«¹CÀ¸¡C
+
+[2003/11/18] visio.c more.c edit.c etc/* °±¥Î ### %%% Åã¥Ü¨Ï¥Î¦WºÙ±±¨î½X¡A§ï¥Î **s ³oÃþ«¬ªº¡C
+
+[2003/11/22] dl_lib.c visio.c bbslink.c fileglue.c verbose.c ±N <varargs.h> §ï¥Î <stdarg.h>¡C
+
+[2003/11/23] more.c ­×¥¿ more_line() ¦b­è¦n 80 ¦r·|¦h¤@ªÅ¦æ¡C
+ struct.h edit.c more.c talk.c visio.c ±N¿Ã¹õ¼e«×³£²Î¤@¬° SCR_WIDTH¡C
+
+[2003/11/26] account.c ¶}²¼¥H«á­n²M°£¥i§ë²¼¦W³æ¡B¤w»â²¼°O¿ý¡C
+
+[2003/11/28] board.c class_namemode() ¥[¤J®É¶¡­­¨î¡C
+
+[2003/11/30] visio.c vget() ¤ä´© KEY_DEL¡C
+
+[2003/12/01] str_time.c *.c ±N©Ò¦³®É¶¡ªº®æ¦¡³£²Î¤@¡C
+
+[2003/12/02] acct.c admutil.c manage.c newbrd.c ­×¥¿³B²zÅv­­Åܰʪºµ{¦¡¡C
+ post.c ¦Û°Ê°»´ú cross-post¡C
+
+[2003/12/03] game/*.c ­×¥¿ª±¹CÀ¸®É­Y¶}±Ò¤è¦VÁä¥þ«¬°»´ú·|³y¦¨¿ù»~¡C
+
+[2003/12/04] more.c xover.c SLIDE_SHOW ¤å³¹/°Êµe¦Û°Ê¼½©ñ¡C
+
+[2003/12/11] rec_sync.c pal.c aloha.c ±N¯Á¤Þ±Æ§Çªºµ{¦¡¿W¥ß¥X¨Ó¡C
+
+[2003/12/14] bbslink.c ²¤Æ Message-ID ªº²£¥Í¤½¦¡¡Aª½±µ®³ÀɦW¡BªO¦W¨Óªí¥Ü¡C
+ bnntpd.c BBS-NNTP server¡C
+
+[2004/01/02] struct.h account.c board.c manage.c bhttpd.c bmtad.c ±NªO¤Í¦W³æ©ñ¶i shm¡C
+
+[2004/01/03] attr_lib.c attr.h §R°£ attr library¡C
+ bwboard.c §ï¼g¾ÔÁZ°O¿ýµ{¦¡¡C
+
+[2004/01/04] pal.c manage.c board.c ªO¤Í/¦n¤Í§Ö¨úµ{¦¡¾ã²z¦b¤@°_¡C
+
+[2004/01/06] acct.c bbsd.c bank.c game/*.c ª÷»È¹ô·¸¦ìÀˬd¡C
+
+[2004/01/10] bbsd.c µn¤J®É¦b¿é¤J§¹ ID/±K½X ¥H«á¤~¸ü¤J .ACCT¡AÁ×§K¦³¤HÂÇ vget ²£¥Íªº®É®t¨Ó¬~¿ú¡C
+ global.h bbsd.c ®³±¼ dns_ident ªº¨BÆJ¡C
+
+[2004/01/12] global.h cache.c bbsd.c §â¤@¨Ç etc/ ¤å¥ó·h¶i cache¡C
+ admutil.c §â etc/ ¤å¥ó³£¥[¤J¨t²ÎÀɮײM³æ¤¤¡C
+ expire.c rec_article.c ­×¥¿ chrono32 ¿ù»~¡C
+
+[2004/01/13] str_hash2.c str_xor.c mail.c ifsigned.c §ïÅܹq¤lñ³¹ªº²£¥Í¤½¦¡¡C
+
+[2004/01/30] ufo.h edit.c help.c ­×¥¿¦b vedit ®É¶i¤J help ¦A¶i¤J vedit ³y¦¨¤å³¹¬y¥¢¡C
+
+[2004/02/11] enews-open.c ¶®ªê¡I©_¼¯·s»Dºô­¶§ïª©¡C
+
+[2004/02/16] dreye.c menu.c ĶÂI³q½u¤W¦r¨åªA°È°±¤î¡C
+
+[2004/02/17] more.c post.c mail.c gem.c ·j´M¦r¦ê¤Ï¥Õ¡C
+
+[2004/02/21] bhttpd.c µn¤J¡Bµoªí¤å³¹¡B§Úªº³Ì·Rµ{¦¡­×¥¿¡C
+
+[2004/02/28] post.c Âà¿ý®É¥i¿ï¾Ü¥[±K¦sÀÉ¡C
+
+[2004/03/03] xpost.c ¦ê¦C¼Ò¦¡¾\Ū¤@­¶¤å³¹§¹¥i·j´M¦r¦ê¡B¼È¦s¡Bµû¤À¡C
+
+[2004/03/05] pal.c aloha.c bmw.c ¦n¤Í/¤W¯¸³qª¾/¤ô²y¨t²Î¤ä´© tag ¼ÐÅÒ¡C
+ xover.c post.c mbox.c gem.c pal.c aloha.c bmw.c ¼ÐÅÒ§R°£¾ã¦X¦b¤@°_¡C
+
+[2004/03/06] vote.c ¥[¤J°Ï¬q§R°£¤Î¼ÐÅÒ§R°£¡C
+ more.c xpost.c ¦ê¦C/¦r¦ê·j´M®É¡A³B²z¤¤¤å¦rÅܤp¼gªº°ÝÃD¡C
+
+[2003/03/11] board.c post.c account.c rec_article.c bmtad.c brdmail.c ­×¥¿¬ÝªO btime §ó·sªºµ{¦¡¡C
+
+[2003/03/13] bbsmail.c brdmail.c §ï¼g¡AºÉ¶q§ï¥Î libdao ªº¨ç¦¡¡C
+
+[2003/03/14] mailpost.c bmtad.c ®³±¼ mailpost ªº³¡¤À¡C
+
+[2004/03/15] struct.h *.c ±N PASSLEN ´À´«¦¨ PASSLEN + 1¡A¨Ã±N PASSLEN ±q 14 §ï¬° 13¡C
+
+[2004/03/18] bquota.c ¤£§R°£¾×«H¦W³æ¤W¨Ó·½ªº«H¡C
+ config.h acct.c admutil.c HAVE_TRUST Åܦ¨©l²× #define¡C
+ global.h admutil.c bmtad.c acl.ic etc/mail.acl etc/unmail.acl ¦¬«H¶Â¥Õ¦W³æ¡C
+
+[2004/03/22] topgem.c µ{¦¡§ï¼g¡C
+
+[2004/03/30] favor.c §Úªº³Ì·R¥i¥H¨ì¤U¤@­¶·j´M¥¼Åª¬ÝªO¡C
+
+[2004/04/01] innbbsd/* InnNNSD §ïª©¡C
+
+[2004/04/05] str_len.c talk.c acct.c ®³±¼ str_len¡C
+ board.c acct.c ¦b Class ¤Uª½±µ·s¼W¬ÝªO¨ì¸Ó¤ÀÃþ¡C
+
+[2004/04/13] ±N bguard.c bmtad.c bpop3d.c gemd.c xchatd.c bhttpd.c bnntpd.c ·h¨ì daemon/ ¥Ø¿ý¡C
+
+[2004/04/24] song.c ÂIºq¨ì¯¸¥~«H½c¡C
+
+[2004/04/25] innbbsd/*.c admutil.c ±NÂà«H³]©w©M BBS µ²¦X¡C
+
+[2004/04/30] struct.h bbslink.c post.c outgo.c ±N out.bntp/*.link ªº§ï¬° binary ®æ¦¡¡C
+
+[2004/05/11] mail.c post.c ²Î¤@±H«H§¹ªº°T®§¡C
+
+[2004/05/19] game/*.c so/*.c ¹CÀ¸°T®§¤¤ªº¡u§A¡v§ï¦¨¡u±z¡v¡C
+ mailpost.c »{ÃÒ«H³¡¤Àµ{¦¡­×¥¿¡C
+
+[2004/05/21] qkmj.c etc/game/qkmj ²¾°£ QKMJ ³s½u³Â±N¡C
+ jcee.c etc/jcee ²¾°£Áp¦Òº]³æ¬d¸ß¡C
+ webx.c ²¾°£®ð¶H³ø¾É§ì¨ú¡C
+ edit.c ²¾°Ê´¡¤J½d¥»¡C
+
+[2004/06/13] newbrd.c ²¤Æ³s¸p¨t²Î¡C
+
+[2004/06/16] board.c ­×¥¿¥i¶i¤J¤w§R°£ªº¬ÝªO¡C
+
+[2004/06/21] *.c *.h ¤ä´©¶W¹L 80 ¦æ¼e¿Ã¹õªºµe­±¡C
+
+[2004/07/21] bhttpd.c µoªí¤å³¹¨ú®ø¦r¼Æ­­¨î¡A¼W¥[µo¤å¨Ó·½¡A¨Ã¯àÂà«H¡C
+
+[2004/07/22] favor.c §Úªº³Ì·R´£¨Ñ¤À¹j½u¡C
+
+[2004/08/02] more.c ¤¤¤å¦rÂ_¦æ¡C
+
+[2004/08/04] bbsmail.c brdmail.c ¥[¤J¾×«H¶Â¥Õ¦W³æ¾÷¨î¡C
+
+[2004/08/06] str_lowest.c str_sub.c §ì¨ì library¡C
+ struct.h admutil.c bbslib.c rec_article.c ¥[¤J¾×«H³W«h¡C
+
+[2004/08/24] xpost.c ¥þ¤å·j´M¥[¤J½d³ò­­¨î¡C
+
+[2004/09/01] menu.c cache.c ¥D¿ï³æ/°ÊºA¬ÝªO¨Ì¼e¿Ã¹õ¸m¤¤¡C
+
+[2004/09/07] config.h xover.c post.c mail.c xpost.c HAVE_XYPOST Åܦ¨©l²× #define¡C
+
+[2004/09/11] bbsd.c struct.c camera.c §R°£ FILM_WELCOME ²¤Æ¶i¯¸µe­±¡C
+
+[2004/09/16] xover.c xo_thread() §ï¼g¡C
+
+[2004/09/17] more.c ¶W¹L 256 ­¶ªºÀɮפ]¯à°÷¥¿½Tªº³Q­pºâ¡C
+
+[2004/09/22] bbslink.c bbsnnrp.c ­×¥¿¿ù»~¨Ã¼W¥[ -v ªº°T®§¡C
+
+[2004/09/30] windtop2pip.c WindTop Âà´«¹q¤lÂû¡C
+
+[2004/10/01] talk.c ¨Ï¥ÎªÌ¦W³æµ{¦¡Â½·s¡C
+
+[2004/10/02] *.c *.h etc/help/pal ¡u¦n¤Í¦W³æ¡v§ï¬°¤¤©Êªº¡uªB¤Í¦W³æ¡v¡A¥H°Ï¤À¡u¦n¤Í¡v¡B¡uÃa¤H¡v¡B¡uªO¤Í¡v¡C
+ theme.h talk.c board.c ¡uªO¤Í¡v§ï¬°¡uªO¦ñ¡v¡C
+
+[2004/10/06] more.c ¾\Ū¨ì¤@¥b®É­Y¨Ï¥ÎªÌ¤¤Â_¡A«h¤£¨q footer¡C
+ bmtad.c ¶Â/¥Õ¦W³æÀˬdµ{¦¡­×¥¿¡C
+ talk.c ¨Ï¥ÎªÌ¦W³æ¨ÌªO¦ñ±Æ§Ç¡C
+
+[2004/10/07] ulist.c ±N¨Ï¥ÎªÌ¦W³æ³¡¤Àªºµ{¦¡±q talk.c ¿W¥ß¥X¨Ó¡C
+ ulist.c ²¾°£¥ô·N±Æ¦C¡C
+
+[2004/10/10] mail.c song.c ¯¸¥~±H«H¥¢±Ñ´N¦s©³½Z¡C
+ talk.c bbsd.c FN_FRIEND_BENZ ¦P¨Bµ{¦¡¡C
+
+[2004/10/11] struct.h talk.c ±N¨t²Î¨ó´Mªº struct BENZ ¿W¥ß¥X¨Ó¡C
+ struct.h talk.c aloha.c ±N¤W¯¸³qª¾ªº struct FRIENZ ¿W¥ß¥X¨Ó¡C
+
+[2004/10/16] util/uno/* ­«¼g¾ã²z userno ªºµ{¦¡¡C
+
+[2004/10/19] ulist.c ¾\Ū°Î¦WªO¤£¬Ý¨ìªO¦ñ¡C
+
+[2004/10/23] gem.c mail.c post.c xpost.c ¾\Ū¨ì¨ì¤@¥b®É¥i¥H«ö E ½s¿è¤å³¹¡C
+ more.c ¦Û°Ê¼½©ñ§ï¬°¥u­­©w¦b³æ¤@½g¤å³¹¡C
+ bbslink.c bbsnnrp.c ±N°e«H¡B§ì«H¦X¨Ö¬°³æ¤@µ{¦¡¡C
+
+[2004/10/25] struct.h admutil.c µù¥U³æ§R°£¯u¹ê©m¦WÄæ¦ì¡C
+
+[2004/10/27] lottery.c lottery-open.c etc/game/lottery.main etc/game/lottery.rule ®³±¼¼Ö³z¹CÀ¸¡C
+
+[2004/10/28] struct.h admutil.c rec_article.c ¾×«H³W«h¼W¥[ SITE¡C
+ struct.h global.h bank.c ¤ä²¼¶×´Ú¡C
+
+[2004/11/01] vote.c ¥ß§Y¶}²¼¡C
+ vote.c account.c struct.h ´£¨Ñ½ä½L¥\¯à¡C
+
+[2004/11/10] struct.h cache.c account.c bno ¨ÌªO¦W±Æ§Ç¡A¥[³t¬ÝªO·j´M¡C
+
+[2004/11/13] cache.c menu.c °ÊºA¬ÝªO§ï¬°§¹¥þÀH¾÷¼½©ñ¡A¦Ó¤£¬O´`§ÇÀH¾÷¼½©ñ¡C
+
+[2004/11/14] more.c §ó·s¾\ۤ峹®É¡A«ö Up/PgUp ¤W±²ªºµ{¦¡¡C
+
+[2004/11/16] mail.c account.c °O¿ý¯¸¤º±H«H¡A¨Ã±N¯¸¤º/¯¸¥~±H«Hªº°O¿ý©ñ¦b log ªO¡C
+
+[2004/11/19] struct.h admutil.c rec_article.c ¾×«H³W«h¼W¥[ POSTHOST¡C
+
+[2004/11/25] admutil.c rec_article.c ¾×«H³W«h¥i¦UªO¯S§O­q©w¡C
+
+[2004/11/29] redir.c ºëµØ°Ï .DIR ­««Øµ{¦¡¡C
+
+[2004/12/10] rec_bot.c *.c ­«­n¤å³¹¸m©³¡C
+
+[2004/12/11] cache.c ­×¥¿±qªB¤Í¦W³æ¥[¤J/²¾°£½u¤W¨Ï¥ÎªÌ®É¡A¦Û¤vªº¨Ï¥ÎªÌ¦W³æ¤£·|Åܦâ¡C
+
+[2004/12/23] config.h board.c HAVE_MMAP Åܦ¨©l²× #define¡C
+
+[2004/12/26] more.c ­×¥¿ Home ·|µo¥Í¿ù»~¡C
+
+[2004/12/30] admutil.c Âà«H³]©w¼W¥[·j´Mªº¥\¯à¡C
+
+[2004/12/31] more.c §ïµ½¦b¾\Ūªø½g¤å³¹®É¤W±²³t«×«ÜºCªº°ÝÃD¡C
+
+[2005/01/01] bbsd.c §R°£³Ì«á¤@­Ó­«½Æ login «áÅܦ¨¥»´L¡C
+
+[2005/01/08] global.h acct.c mail.c xover.c enews.c ±N¿ù»~ E-mail address ªº°T®§²Î¤@¦¨ ERR_EMAIL¡C
+ mail.c ±q¯¸¤W±H«Hµ¹ xyz.brd@mydoamil ¤Ï¦Ó·|±Hµ¹ xyz ³o ID¡C
+
+[2005/01/11] manage.c post.c ·s¼W¬ÝªOÄݩʡG¤£¯àµû¤À¡C
+ admutil.c »{ÃÒ¹L´Á«e¤Q¤Ñ¥i¶ñµù¥U³æ¡C
+ acct.c ­Ó¤H¤W¯¸¬ö¿ýÀ˯Á¡C
+ xyz.c §Ñ°O±K½XªA°È¡C
+ etc/cross-post global.h post.c Cross-Post °±Åv±H«H³qª¾¡C
+
+[2005/02/28] enews.c enews-open.c ²¾°£©_¼¯·s»D¡C
+
+[2005/03/08] global.h edit.c post.c more.c §â QUOTE_CHAR ²Î¤@¦b¤@°_¡C
+
+[2005/03/09] bhttpd.c ±N«H½c¦Cªí§ï¬°¤@¦¸¥u¦L¥X¤­¤Q«Ê«H¡C
+
+[2005/03/13] vote.c §R°£½ä½L®É·|°h´«½äª÷¡C
+
+[2005/03/16] config.h global.h board.c account.c usies-sort.c ²¾°£ LOG_BRD_USIES¡C
+
+[2005/03/23] board.c bhttpd.c pal.c ¬ÝªOÃa¤H¡C
+
+[2005/03/28] board.c pal.c ulist.c xover.c ¨Ï·j´M¤¤¤åÃöÁä¦r®É¤£·|¥X¿ù¡C
+
+[2005/03/29] acct.c ­×§ï¬ÝªOªO¦W®É¡A@Class ·|¤@¨ÖÅܰʡC
+
+[2005/04/05] innbbs.c admutil.c ±NÂà«H³]©w¿W¥ß¦¨ innbbs.c¡C
+ acct.c admutil.c ±N¯¸°È«ü¥O¶°¤¤¨ì admutil.c¡C
+ acct.c user.c ±N¨Ï¥ÎªÌ«ü¥O¶°¤¤¨ì user.c¡C
+ struct.h user.c admutil.c ¨Ï¥ÎªÌ¦Û¦æ´_Åv¡C
+
+[2005/04/13] innbbs.c channel.c bbslink.c inntobbs.c rec_article.c ¾×«H³W«h¥i«]­­¾×¬Y¯S©w·s»D¦øªA¾¹ªº«H¡C
+
+[2005/04/14] gem.c ­×¥¿ºëµØ°Ï½Æ»s·|Â_½uªº°ÝÃD¡C
+
+[2005/04/20] bwboard.c ­×¥¿­x´Ñ¬¶/¥]¦æ¨«¤è¦¡¡C
+ bwboard.c ·t´Ñ«ö·Ó²Ä¤@­Ó½¤l¨M©wÃC¦â¡C
+
+[2005/04/23] manage.c ©Ø·¬¸¨¸­±Ù¥i«ü©w¥i¬å(¤£)Âà«HªO¡C
+ post.c ªO¥D¦bºÞ²zªº¬ÝªO¤¤Âà¿ý¤£ºâ¸ó¶K¡C
+ vote.c ½ä½Lªº²¼»ù¥ÑªO¥D¨Ó¨M©w¡C
+
+[2005/04/24] innbbs.c innbbsd/* active Àɪº³]©w·h¤J newsfeeds.bbs ¤º¡C
+
+[2005/05/17] struct.h cache.c menu.c camera.c ±N¸`¤é·h¨ì fshm¡C
+ camera.c etc/feast ¤ä´©¬Y¤ë²Ä´X­Ó¬P´Á´X¬°¸`¤éªº®æ¦¡¡C
+
+[2005/05/18] xpost.c ­×¥¿¦P¼ÐÃD·j´M­n¼ÐÃD§¹¥þ¬Û¦P¤~ match¡C
+
+[2005/05/19] *.h *.c ±N¬ÝªOªø«×±q IDLEN ¿W¥ß¦¨ BNLEN¡C
+
+[2005/05/28] camera.c etc/feast ¤ä´©¹A¾ä¸`¼y¡C
+
+[2005/06/04] perm.h *.c §â¯¸¤º±H«HªºÅv­­¿W¥ß¦¨ PERM_LOCAL¡C
+
+[2005/06/06] config.h bbsd.c user.c admutil.c ±N INVALID_NOTICE_PERIOD ¾ã²z¥X¨Ó¡C
+
+[2005/06/09] theme.h edit.c bhttpd.c §â BANNER ¾ã²z¥X¨Ó¡C
+ struct.h board.c vote.c account.c ¬ÝªO¦CªíÅã¥Ü¸Ó¬ÝªO¦³½ä½L¶i¦æ¤¤¡C
+
+[2005/06/11] topusr.c ±Æ¦æº]¤¹³\¦P¦W¦¸¡C
+
+[2005/06/13] struct.h board.c post.c bbsd.c ¬ÝªO¤H®ð¡C
+
+[2005/06/17] bbsd.c game/*.c ¹CÀ¸¶Ã¼Æ¿ï¨ú¡C
+
+[2005/06/25] modes.h perm.h gem.c ºëµØ°ÏÅv­­»P¬ÝªOÅv­­¦P¨B¡C
+
+[2005/06/26] *.c ºëµØ°Ï¤£´£¨Ñ Gopher¡C
+
+[2005/06/30] *.c ±N xsort §ï¥Î qsort¡C
+
+[2005/07/01] bnntpd.c BNNTPD §ï¬° daemon¡C
+
+[2005/07/09] bhttpd.c BHTTPD §ï¬° daemon¡C
+
+[2005/07/12] xover.c bmw.c ±N bmw_cb ª½±µ¼g¨ì xz[]¡C
+ xover.c pal.c manage.c vote.c ±N pal_cb ª½±µ¼g¨ì xz[]¡C
+ modes.h pal.c manage.c vote.c °Ï¤ÀªB¤Í/¸s²Õ/ªO¤Í¦W³æªº´£¥Ü¦r¼Ë¡C
+
+[2005/07/16] post.c cache.c ´Nºâ¨S¦³ #define CHECK_ONLINE¡A¤]¥i¥H¥á¤å³¹/«H¥ó§@ªÌ¤ô²y¡C
+ pal.c aloha.c ªB¤Í¦W³æ/¤W¯¸³qª¾¦W³æ¤¤¡A­Y¸Ó¨Ï¥ÎªÌ¦b¯¸¤W·|«G¦â¡C
+ pal.c ­×¥¿§ï¸s²Õ¦W³æ/ªO¤Í¦W³æ/­­¨î§ë²¼¦W³æ®É·|¿ù»~¥[¤W STATUS_PALDIRTY¡C
+
+[2005/08/10] bhttpd.c ¤j´T§ïª©¡C
+
+[2005/08/14] cache.c ­×¥¿¨Ï¥ÎªÌ¤£¦b½u¤W¡A¦ý¦b¦Cªí¤¤«o«G¦â¡C
+
+[2005/08/26] visio.c §ï¼g¿Ã¹õ±±¨î³¡¤À¡C
+
+[2005/08/31] *.c ±N©Ò¦³´«¤j¤p¼gªºµ{¦¡²Î¤@¡C
+
+[2005/09/01] inntobbs.h inntobbs.c rec_article.c Organization ¤£¦C¬°¥²­nÀÉÀY¡C
+
+[2005/10/07] *.c ¯¸ªø¦æ¬°°O¿ý¡C
+
+[2005/10/08] admutil.c ÁÙ­ì³Æ¥÷¡C
+
+[2005/10/15] config.h innbbsd/* §â BBSNAME2¡BTAG_VALID¡Bbbsname.bbs ¾ã¦X¦b¤@°_¡C
+
+[2005/10/19] visio.c talk.c chat.c bwboard.c ­×¥¿ vio_fd »P mysql ©R¦W½Ä¬ð¡C
+
+[2005/10/20] post.c ±q¯µ±K¬ÝªOÂà¿ý¥X¥hªº¤å³¹¤£Åã¥Ü¨Ó·½¬ÝªO¡C
+
+[2005/11/17] xover.c ­×¥¿¦b¤å³¹¦Cªí«ö ^Z ¿ï¬ÝªO®É¡A·|¦h°µ¤@¦¸ XoPost()¡C
+
+[2005/11/18] more.c ±Ë±ó buffered file read ªº¤è¦¡¡A¦Ó±Ä full file read¡C
+
+[2005/11/23] bmw.c ´£¨Ñ§R°£©Ò¦³¤ô²yªº¥\¯à¡C
+
+[2005/12/09] bbsnet.c connlist ²¾°£ BBSNET¡C
+
+[2005/12/25] acct.c manage.c ªO¥D¦W³æ¿é¤J¦P¼Ë ID ®É¥i²¾°£¸Ó ID¡C
+
+[2006/01/23] camera.c etc/feasts ¯S®í¸`¤éªº¶}ÀYµe­±¡C
+
+[2006/02/06] xpost.c post.c mail.c xover.c ¦ê¦C·j´M¤¹³\¼W¥[±ø¥ó¦A«×·j´M¡C
+
+[2006/02/08] struct.h innbbs.c bbslink.c ¦b newsfeeds ¸Ì­±Åã¥Ü§ä¤£¨ì¸s²Õ¡C
+
+[2006/03/24] board.c vote.c ­×¥¿¬ÝªO¤H®ð­pºâ¿ù»~¡C
+
+[2006/03/30] bhttpd.c ´£¨Ñ robot exclusion¡C
+ ats2*.c ´£¨Ñ ATS Âà´«µ{¦¡¡C
+
+[2006/04/10] board.c ¦b¬ÝªO¦Cªí±N¬ÝªO¥[¤J§Úªº³Ì·R¤£·|­«ÂÐ¥[¤J¡C
+ visio.c vget() ®É«öªÅ¥ÕÁ侨¶q¸É§¹ªO¦W¡C
+
+[2006/04/17] camera.c ­­¨î°ÊºA¬ÝªO¨C¦Cªø«×¡C
+
+[2006/04/20] more.c ­×¥¿¤å³¹¾\Ū¨ì¤@¥b®É¡A¶i¥X help ·|¿ù»~¡C
+ str_ttl.c post.c ±NÂà¿ý¦r¼Ë§ï¦¨ Fw:¡C
+
+[2006/05/10] visio.c edit.c ¤ä´©º~¦r¾ã¦r§R°£¡B²¾°Ê¡BÅã¥Ü¡C
+
+[2006/05/12] chat.c ´£¨Ñ KEY_DEL §R¦r¥\¯à¡C
+ talk.c chat.c ¤ä´©º~¦r¾ã¦r§R°£¡B²¾°Ê¡BÅã¥Ü¡C
+
+[2006/05/21] railway.c ²¾°£ÅK¸ô®É¨è¬d¸ß¡C
+
diff --git a/maple/Makefile b/maple/Makefile
new file mode 100644
index 0000000..0276fcd
--- /dev/null
+++ b/maple/Makefile
@@ -0,0 +1,69 @@
+# ------------------------------------------------------ #
+# maple/Makefile ( NTHU CS MapleBBS Ver 3.10 ) #
+# ------------------------------------------------------ #
+# author : opus.bbs@bbs.cs.nthu.edu.tw #
+# target : Makefile for MapleBBS main programs #
+# create : 95/03/29 #
+# update : 95/12/15 #
+# ------------------------------------------------------ #
+
+# ------------------------------------------------------ #
+# ¤U¦Cªº make rules ¤£»Ý­×§ï #
+# ------------------------------------------------------ #
+
+
+SRC = acct.c bbsd.c bmw.c board.c cache.c edit.c favor.c \
+ gem.c mail.c menu.c more.c pal.c post.c talk.c ulist.c \
+ user.c visio.c window.c xover.c xpost.c
+
+OBJ = acct.o bbsd.o bmw.o board.o cache.o edit.o favor.o \
+ gem.o mail.o menu.o more.o pal.o post.o talk.o ulist.o \
+ user.o visio.o window.o xover.o xpost.o
+
+EXE = bbsd
+
+
+all:
+ @echo "Please enter 'make sys-type', "
+ @echo " make sun : for Sun-OS 4.x and maybe some BSD systems, cc or gcc"
+ @echo " make linux : for Linux"
+ @echo " make solaris : for Sun-OS 5.x gcc"
+ @echo " make sol-x86 : for Solaris 7 x86"
+ @echo " make freebsd : for BSD 4.4 systems"
+ @echo " make bsd : for BSD systems, cc or gcc, if not in the above lists"
+ @echo " make cygwin : for Microsoft Windows and Cygwin gcc"
+
+sun:
+ @$(MAKE) CC=gcc CFLAGS="-O2 -pipe -fomit-frame-pointer -Wunused -I../include" LDFLAGS="-s -L../lib -ldao" $(EXE)
+
+linux:
+ @$(MAKE) CC=gcc CFLAGS="-DLINUX -O2 -pipe -fomit-frame-pointer -Wunused -I../include" LDFLAGS="-s -L../lib -ldao -lcrypt -lresolv -ldl -rdynamic" $(EXE)
+
+solaris:
+ @$(MAKE) CC=gcc CFLAGS="-DSOLARIS -DSYSV -O2 -pipe -fomit-frame-pointer -Wunused -I../include" LDFLAGS="-s -L../lib -ldao -ltermcap -lsocket -lnsl -lresolv -lelf -ldl" $(EXE)
+
+sol-x86:
+ @$(MAKE) CC=gcc CFLAGS="-DSOLARIS -DSYSV -O2 -fomit-frame-pointer -Wunused -I../include" LDFLAGS="-s -L../lib -ldao -ltermcap -lsocket -lnsl -lresolv -lelf -ldl" $(EXE)
+
+freebsd:
+ @$(MAKE) CC=gcc CFLAGS="-DBSD44 -O2 -pipe -fomit-frame-pointer -Wunused -I../include" LDFLAGS="-s -L../lib -ldao -lcrypt -export-dynamic" $(EXE)
+
+bsd:
+ @$(MAKE) CC=gcc CFLAGS="-DBSD44 -O2 -pipe -fomit-frame-pointer -Wunused -I../include" LDFLAGS="-s -L../lib -ldao" $(EXE)
+
+cygwin:
+ @$(MAKE) CC=gcc CFLAGS="-DLINUX -DCYGWIN -O2 -pipe -fomit-frame-pointer -Wunused -I../include" LDFLAGS="-s -L../lib -ldao -lcrypt -lresolv -lcygipc" $(EXE)
+
+
+bbsd: $(OBJ)
+ $(CC) -o $@ $(OBJ) $(LDFLAGS)
+
+
+install: $(EXE)
+ install -m 0700 $? $(HOME)/bin
+
+update:
+ -csh -c "kill `tail -1 $(HOME)/run/bbs.pid | awk '{print $$1}'`";exit 0
+
+clean:
+ rm -f $(EXE) *.exe *.o *~
diff --git a/maple/acct.c b/maple/acct.c
new file mode 100644
index 0000000..44b319e
--- /dev/null
+++ b/maple/acct.c
@@ -0,0 +1,1203 @@
+/*-------------------------------------------------------*/
+/* acct.c ( NTHU CS MapleBBS Ver 3.00 ) */
+/*-------------------------------------------------------*/
+/* target : account / administration routines */
+/* create : 95/03/29 */
+/* update : 96/04/05 */
+/*-------------------------------------------------------*/
+
+
+#define _ADMIN_C_
+
+
+#include "bbs.h"
+
+
+extern BCACHE *bshm;
+
+
+/* ----------------------------------------------------- */
+/* (.ACCT) ¨Ï¥ÎªÌ±b¸¹ (account) subroutines */
+/* ----------------------------------------------------- */
+
+
+int
+acct_load(acct, userid)
+ ACCT *acct;
+ char *userid;
+{
+ int fd;
+
+ usr_fpath((char *) acct, userid, fn_acct);
+ fd = open((char *) acct, O_RDONLY);
+ if (fd >= 0)
+ {
+ /* Thor.990416: ¯S§Oª`·N, ¦³®É .ACCTªºªø«×·|¬O0 */
+ read(fd, acct, sizeof(ACCT));
+ close(fd);
+ }
+ return fd;
+}
+
+
+/* static */ /* itoc.010408: µ¹¨ä¥Lµ{¦¡¥Î */
+void
+acct_save(acct)
+ ACCT *acct;
+{
+ int fd;
+ char fpath[64];
+
+ /* itoc.010811: ­Y³Q¯¸ªøÂê©w¡A´N¤£¯à¼g¦^¦Û¤vªºÀÉ®× */
+ if ((acct->userno == cuser.userno) && HAS_STATUS(STATUS_DATALOCK) && !HAS_PERM(PERM_ALLACCT))
+ return;
+
+ usr_fpath(fpath, acct->userid, fn_acct);
+ fd = open(fpath, O_WRONLY, 0600); /* fpath ¥²¶·¤w¸g¦s¦b */
+ if (fd >= 0)
+ {
+ write(fd, acct, sizeof(ACCT));
+ close(fd);
+ }
+}
+
+
+int
+acct_userno(userid)
+ char *userid;
+{
+ int fd;
+ int userno;
+ char fpath[64];
+
+ usr_fpath(fpath, userid, fn_acct);
+ fd = open(fpath, O_RDONLY);
+ if (fd >= 0)
+ {
+ read(fd, &userno, sizeof(userno));
+ close(fd);
+ return userno;
+ }
+ return 0;
+}
+
+
+/* ----------------------------------------------------- */
+/* name complete for user ID */
+/* ----------------------------------------------------- */
+/* return value : */
+/* 0 : ¨Ï¥Îª½±µ«ö enter ==> cancel */
+/* -1 : bad user id */
+/* ow.: ¶Ç¦^¸Ó userid ¤§ userno */
+/* ----------------------------------------------------- */
+
+
+int
+acct_get(msg, acct)
+ char *msg;
+ ACCT *acct;
+{
+ outz("¡¹ ¿é¤J­º¦r¥À«á¡A¥i¥H«öªÅ¥ÕÁä¦Û°Ê·j´M");
+
+ if (!vget(1, 0, msg, acct->userid, IDLEN + 1, GET_USER))
+ return 0;
+
+ if (acct_load(acct, acct->userid) >= 0)
+ return acct->userno;
+
+ vmsg(err_uid);
+ return -1;
+}
+
+
+/* ----------------------------------------------------- */
+/* bit-wise display and setup */
+/* ----------------------------------------------------- */
+
+
+#define BIT_ON "£¾"
+#define BIT_OFF "¡@"
+
+
+void
+bitmsg(msg, str, level)
+ char *msg, *str;
+ int level;
+{
+ int cc;
+
+ outs(msg);
+ while (cc = *str)
+ {
+ outc((level & 1) ? cc : '-');
+ level >>= 1;
+ str++;
+ }
+
+ outc('\n');
+}
+
+
+usint
+bitset(pbits, count, maxon, msg, perms)
+ usint pbits;
+ int count; /* ¦@¦³´X­Ó¿ï¶µ */
+ int maxon; /* ³Ì¦h¥i¥H enable ´X¶µ */
+ char *msg;
+ char *perms[];
+{
+ int i, j, on;
+
+ move(1, 0);
+ clrtobot();
+ move(3, 0);
+ outs(msg);
+
+ for (i = on = 0, j = 1; i < count; i++)
+ {
+ msg = BIT_OFF;
+ if (pbits & j)
+ {
+ on++;
+ msg = BIT_ON;
+ }
+ move(5 + (i & 15), (i < 16 ? 0 : 40));
+ prints("%c %s %s", radix32[i], msg, perms[i]);
+ j <<= 1;
+ }
+
+ while (i = vans("½Ð«öÁä¤Á´«³]©w¡A©Î«ö [Return] µ²§ô¡G"))
+ {
+ i -= '0';
+ if (i >= 10)
+ i -= 'a' - '0' - 10;
+
+ if (i >= 0 && i < count)
+ {
+ j = 1 << i;
+ if (pbits & j)
+ {
+ on--;
+ msg = BIT_OFF;
+ }
+ else
+ {
+ if (on >= maxon)
+ continue;
+ on++;
+ msg = BIT_ON;
+ }
+
+ pbits ^= j;
+ move(5 + (i & 15), (i < 16 ? 2 : 42));
+ outs(msg);
+ }
+ }
+ return (pbits);
+}
+
+
+static usint
+setperm(level)
+ usint level;
+{
+ if (HAS_PERM(PERM_SYSOP))
+ return bitset(level, NUMPERMS, NUMPERMS, MSG_USERPERM, perm_tbl);
+
+ /* [±b¸¹ºÞ²z­û] ¤£¯àºÞ PERM_SYSOP */
+ if (level & PERM_SYSOP)
+ return level;
+
+ /* [±b¸¹ºÞ²z­û] ¤£¯à§ó§ïÅv­­ PERM_ACCOUNTS CHATROOM BOARD SYSOP */
+ return bitset(level, NUMPERMS - 4, NUMPERMS - 4, MSG_USERPERM, perm_tbl);
+}
+
+
+/* ----------------------------------------------------- */
+/* ±b¸¹ºÞ²z */
+/* ----------------------------------------------------- */
+
+
+static void
+bm_list(userid) /* Åã¥Ü userid ¬O­þ¨ÇªOªºªO¥D */
+ char *userid;
+{
+ int len;
+ char *list;
+ BRD *bhead, *btail;
+
+ len = strlen(userid);
+ outs(" \033[32m¾á¥ôªO¥D¡G\033[37m"); /* itoc.010922: ´« user info ª©­± */
+
+ bhead = bshm->bcache;
+ btail = bhead + bshm->number;
+
+ do
+ {
+ list = bhead->BM;
+ if (str_has(list, userid, len))
+ {
+ outs(bhead->brdname);
+ outc(' ');
+ }
+ } while (++bhead < btail);
+
+ outc('\n');
+}
+
+
+static void
+adm_log(old, new)
+ ACCT *old, *new;
+{
+ int i;
+ usint bit, oldl, newl;
+ char *userid, buf[80];
+
+ userid = new->userid;
+ alog("²§°Ê¸ê®Æ", userid);
+
+ if (strcmp(old->passwd, new->passwd))
+ alog("²§°Ê±K½X", userid);
+
+ if ((old->money != new->money) || (old->gold != new->gold))
+ {
+ sprintf(buf, "%-13s»È%d¡÷%d ª÷%d¡÷%d", userid, old->money, new->money, old->gold, new->gold);
+ alog("²§°Ê¿ú¹ô", buf);
+ }
+
+ /* Thor.990405: log permission modify */
+ oldl = old->userlevel;
+ newl = new->userlevel;
+ for (i = 0, bit = 1; i < NUMPERMS; i++, bit <<= 1)
+ {
+ if ((newl & bit) != (oldl & bit))
+ {
+ sprintf(buf, "%-13s%s %s", userid, (newl & bit) ? BIT_ON : BIT_OFF, perm_tbl[i]);
+ alog("²§°ÊÅv­­", buf);
+ }
+ }
+}
+
+static void
+brd_log(old, new)
+ BRD *old, *new;
+{
+ char buf[256];
+ alog("³]©w¬ÝªO",old->brdname);
+ if(strcmp(old->brdname,new->brdname)){
+ sprintf(buf,"%-13s%s",old->brdname,new->brdname);
+ alog("¬ÝªO§ó¦W",buf);
+ }
+ if(strcmp(old->class,new->class)){
+ sprintf(buf,"%-13s%s ¡÷ %s",new->brdname,old->class,new->class);
+ alog("¬ÝªO¤ÀÃþ",buf);
+ }
+ if(strcmp(old->title,new->title)){
+ sprintf(buf,"%-13s%s ¡÷ %s",new->brdname,old->title,new->title);
+ alog("¬ÝªO¥DÃD",buf);
+ }
+ if(strcmp(old->BM,new->BM)){
+ sprintf(buf,"%-13s%s ¡÷ %s",new->brdname,(strlen(old->BM)) ? old->BM : "(µL)",
+ (strlen(new->BM)) ? new->BM : "(µL)");
+ alog("­×§ïªO¥D",buf);
+ }
+ int oldl = old->readlevel;
+ int newl = new->readlevel;
+ int i,bit;
+ for (i = 0, bit = 1; i < NUMPERMS; i++, bit <<= 1)
+ {
+ if ((newl & bit) != (oldl & bit))
+ {
+ sprintf(buf, "%-13s%s %s", new->brdname, (newl & bit) ? BIT_ON : BIT_OFF, perm_tbl[i]);
+ alog("¾\\ŪÅv­­", buf);
+ }
+ }
+ oldl = old->postlevel;
+ newl = new->postlevel;
+ for (i = 0, bit = 1; i < NUMPERMS; i++, bit <<= 1)
+ {
+ if ((newl & bit) != (oldl & bit))
+ {
+ sprintf(buf, "%-13s%s %s", new->brdname, (newl & bit) ? BIT_ON : BIT_OFF, perm_tbl[i]);
+ alog("µoªíÅv­­", buf);
+ }
+ }
+ oldl = old->battr;
+ newl = new->battr;
+ for (i = 0, bit = 1; i < NUMBATTRS; i++, bit <<= 1)
+ {
+ if ((newl & bit) != (oldl & bit))
+ {
+ sprintf(buf, "%-13s%s %s", new->brdname, (newl & bit) ? BIT_ON : BIT_OFF, battr_tbl[i]);
+ alog("¬ÝªOÄÝ©Ê", buf);
+ }
+ }
+
+}
+
+static int /* 1:§ä¨ì 0:¨S§ä¨ì */
+find_rform(userno, userid, address, phone, career)
+ int userno; /* ¶Ç¤J userno/userid */
+ char *userid;
+ char *address, *phone, *career; /* ¦^¶Ç address/phone/career */
+{
+ RFORM *rform;
+ int fd, rc;
+
+ rc = 0;
+ if ((fd = open(FN_RUN_RFORM_LOG, O_RDONLY)) >= 0)
+ {
+ mgets(-1);
+
+ while (rform = mread(fd, sizeof(RFORM)))
+ {
+// if (userno == rform->userno && !strcmp(userid, rform->userid))
+ if (!strcmp(userid, rform->userid))
+ {
+ strcpy(address, rform->address);
+ strcpy(phone, rform->phone);
+ strcpy(career, rform->career);
+ rc = 1;
+ //break; /* ­Y§â³o¦æ break ®³±¼¡A¨º»ò´N¬O§ä³Ì«á¤@µ§µù¥U³æ */
+ } /* ­Y§â³o¦æ break ¯dµÛ¡A¨º»ò´N¬O§ä²Ä¤@µ§µù¥U³æ */
+ }
+
+ close(fd);
+ }
+
+ return rc;
+}
+
+
+void
+acct_show(u, adm)
+ ACCT *u;
+ int adm; /* 0: user info 1: admin 2: reg-form */
+{
+ int diff;
+ usint ulevel;
+ char *uid, buf[80];
+
+ clrtobot();
+
+ /* itoc.010922: ´« user info ª©­± */
+ if (adm == 0)
+ {
+ outs("\n \033[30;41m¢s¢r¢s¢r¢s¢r\033[m \033[45mùüùÞùÞùûùúùÞùùùû"
+ "ùÝùÞùùùûùúùÞùùùû\033[m \033[30;41m¢s¢r¢s¢r¢s¢r\033[m\n"
+ " \033[30;41m¢r¢s¢r¢s¢r¢s\033[m \033[1;37;45m ùàùâ ùàùâ"
+ " ùøùàùáùâ ùàùâ ùø\033[m \033[30;41m¢r¢s¢r¢s¢r¢s\033[m\n"
+ " \033[30;41m¢s¢r¢s¢r¢s¢r\033[m \033[45m ùàùâ ùàùâ ùø"
+ "ùàùâùý ùàùâ ùø\033[m \033[30;41m¢s¢r¢s¢r¢s¢r\033[m\n"
+ " \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");
+ }
+
+ uid = u->userid;
+
+ outs("\n\033[1m");
+
+ /* itoc.010408: ·s¼Wª÷¿ú/¥Í¤é/©Ê§OÄæ¦ì */
+
+ if (adm != 2)
+ prints(" \033[32m­^¤å¥N¸¹¡G\033[37m%-35s\033[32m¥Î¤á½s¸¹¡G\033[37m%d\n", uid, u->userno);
+
+ prints(" \033[32m§Úªº¼ÊºÙ¡G\033[37m%-35s\033[32m¾Ö¦³»È¹ô¡G\033[37m%d\n", u->username, u->money);
+
+ prints(" \033[32m¯u¹ê©m¦W¡G\033[37m%-35s\033[32m¾Ö¦³ª÷¹ô¡G\033[37m%d\n", u->realname, u->gold);
+
+ prints(" \033[32m¥X¥Í¤é´Á¡G\033[37m¥Á°ê %03d ¦~ %02d ¤ë %02d ¤é \033[32m§Úªº©Ê§O¡G\033[37m%.2s\n", u->year, u->month, u->day, "¡H¡ñ¡ð" + (u->sex << 1));
+
+ prints(" \033[32m¤W¯¸¦¸¼Æ¡G\033[37m%-35d\033[32m¤å³¹½g¼Æ¡G\033[37m%d\n", u->numlogins, u->numposts);
+
+ prints(" \033[32m¶l¥ó«H½c¡G\033[37m%s\n", u->email);
+
+ prints(" \033[32mµù¥U¤é´Á¡G\033[37m%s\n", Btime(&u->firstlogin));
+
+ prints(" \033[32m¥úÁ{¤é´Á¡G\033[37m%s\n", Btime(&u->lastlogin));
+
+ ulevel = u->userlevel;
+
+ if (ulevel & PERM_ALLDENY)
+ {
+ /* yiting: Åã¥Ü°±Åv¤Ñ¼Æ */
+ outs(" \033[32m°±Åv¤Ñ¼Æ¡G\033[37m");
+ if ((diff = u->tvalid - time(0)) < 0)
+ {
+ outs("°±Åv´Á­­¤w¨ì¡A¥i¦Û¦æ¥Ó½Ð´_Åv\n");
+ }
+ else
+ {
+ /* ¤£º¡¤@¤p®Éªº³¡¥÷¥[¤@¤p®É­pºâ¡A³o¼ËÅã¥Ü0¤p®É´Nªí¥Ü¥i¥H¥h´_Åv¤F */
+ diff += 3600;
+ prints("ÁÙ¦³ %d ¤Ñ %d ¤p®É\n", diff / 86400, (diff % 86400) / 3600);
+ }
+ }
+ else
+ {
+ prints(" \033[32m¨­¤À»{ÃÒ¡G\033[37m%s\n", (ulevel & PERM_VALID) ? Btime(&u->tvalid) : "½Ð°Ñ¦Ò¥»¯¸¤½§GÄæ¶i¦æ½T»{¡A¥H´£ª@Åv­­");
+ }
+
+ usr_fpath(buf, uid, fn_dir);
+ prints(" \033[32m­Ó¤H«H¥ó¡G\033[37m%d «Ê\n", rec_num(buf, sizeof(HDR)));
+
+ if (adm)
+ {
+ char address[60], phone[20], career[50];
+ prints(" \033[32m¤W¯¸¦aÂI¡G\033[37m%-35s\033[32mµo«H¦¸¼Æ¡G\033[37m%d\n", u->lasthost, u->numemails);
+ bitmsg(" \033[32mÅv­­µ¥¯Å¡G\033[37m", STR_PERM, ulevel);
+ bitmsg(" \033[32m²ßºDºX¼Ð¡G\033[37m", STR_UFO, u->ufo);
+ if (find_rform(u->userno, u->userid, address, phone, career))
+ {
+ prints (" \033[32mµù¥U¦í§}¡G\033[37m%s\n", address);
+ prints (" \033[32mªA°È³æ¦ì¡G\033[37m%-35s" \
+ "\033[32mµù¥U¹q¸Ü¡G\033[37m%s\n",
+ career, phone);
+ }
+ }
+ else
+ {
+ diff = (time(0) - ap_start) / 60;
+ prints(" \033[32m°±¯d´Á¶¡¡G\033[37m%d ¤p®É %d ¤À\n", diff / 60, diff % 60);
+ }
+
+ if (adm == 2)
+ goto end_show;
+
+ /* Thor: ·Q¬Ý¬Ý³o­Ó user ¬O¨º¨ÇªOªºªO¥D */
+
+ if (ulevel & PERM_BM)
+ bm_list(uid);
+
+#ifdef NEWUSER_LIMIT
+ if (u->lastlogin - u->firstlogin < 3 * 86400)
+ outs("\n \033[36m·s¤â¤W¸ô¡G¤T¤Ñ«á¶}©ñÅv­­\n");
+#endif
+
+end_show:
+ outs("\033[m");
+}
+
+
+void
+acct_setup(u, adm)
+ ACCT *u;
+ int adm;
+{
+ ACCT x;
+ int i, num;
+ char *str, buf[80], pass[PSWDLEN + 1];
+ int mmday[12] = {31, 28, 31, 30, 31, 30,
+ 31, 31, 30, 31, 30, 31};
+
+ acct_show(u, adm);
+ memcpy(&x, u, sizeof(ACCT));
+
+ char fpath_reg[256];
+
+ if (adm)
+ {
+ adm = vans("³]©w 1)¸ê®Æ 2)Åv­­ 3)À˵øµù¥U³æ Q)¨ú®ø [Q] ");
+ if (adm == '2')
+ goto set_perm;
+
+ if (adm == '3'){
+ usr_fpath(fpath_reg,u->userid,"justify");
+ if(more(fpath_reg,(char*) 0 ) == -1){
+ vmsg("³o­Ó¨Ï¥ÎªÌ¨S¦³¶ñ¹Lµù¥U³æ");
+ }else{
+ move(0,0),clrtoeol(),refresh();
+ }
+ return;
+ }
+
+ if (adm != '1')
+ return;
+ }
+ else
+ {
+ if (vans("­×§ï¸ê®Æ(Y/N)¡H[N] ") != 'y')
+ return;
+ }
+
+ move(i = 3, 0);
+ clrtobot();
+
+ if (adm)
+ {
+ str = x.userid;
+ for (;;)
+ {
+ /* itoc.010804.µù¸Ñ: §ï¨Ï¥ÎªÌ¥N¸¹®É½Ð½T©w¸Ó user ¤£¦b¯¸¤W */
+ vget(i, 0, "¨Ï¥ÎªÌ¥N¸¹(¤£§ï½Ð«ö Enter)¡G", str, IDLEN + 1, GCARRY);
+ if (!str_cmp(str, u->userid) || !acct_userno(str))
+ break;
+ vmsg("¿ù»~¡I¤w¦³¬Û¦P ID ªº¨Ï¥ÎªÌ");
+ }
+ }
+ else
+ {
+ vget(i, 0, "½Ð½T»{±K½X¡G", buf, PSWDLEN + 1, NOECHO);
+ if (chkpasswd(u->passwd, buf))
+ {
+ vmsg("±K½X¿ù»~");
+ return;
+ }
+ }
+
+ /* itoc.030223: ¥u¦³ PERM_SYSOP ¯àÅܧó¨ä¥L¯¸°Èªº±K½X */
+ if (!adm || !(u->userlevel & PERM_ALLADMIN) || HAS_PERM(PERM_SYSOP))
+ {
+ i++;
+ for (;;)
+ {
+ if (!vget(i, 0, "³]©w·s±K½X(¤£§ï½Ð«ö Enter)¡G", buf, PSWDLEN + 1, NOECHO))
+ break;
+
+ strcpy(pass, buf);
+ vget(i + 1, 0, "Àˬd·s±K½X¡G", buf, PSWDLEN + 1, NOECHO);
+ if (!strcmp(buf, pass))
+ {
+ str_ncpy(x.passwd, genpasswd(buf), sizeof(x.passwd));
+ break;
+ }
+ }
+ }
+
+ i++;
+ str = x.username;
+ while (1)
+ {
+ if (vget(i, 0, "¼Ê ºÙ¡G", str, UNLEN + 1, GCARRY))
+ break;
+ };
+
+ /* itoc.010408: ·s¼W¥Í¤é/©Ê§OÄæ¦ì¡A¤£±j­¢¨Ï¥ÎªÌ¶ñ (¤¹³\¶ñ 0) */
+ i++;
+ int year;
+ do
+ {
+ sprintf(buf, "¥Í¤é¡Ð¥Á°ê %02d ¦~¡G", u->year);
+ if (!vget(i, 0, buf, buf, 4, DOECHO))
+ break;
+ year = atoi(buf);
+ } while (year <= 0 || year >= 256 /*|| x.year > 99*/);
+ x.year=year;
+
+ if (((x.year + 1991) % 400 == 0) ||
+ ((x.year + 1991) % 100 != 0 && (x.year + 1991) % 4 == 0))
+ mmday[1]=29;
+
+ do
+ {
+ sprintf(buf, "¥Í¤é¡Ð %02d ¤ë¡G", u->month);
+ if (!vget(i, 0, buf, buf, 3, DOECHO))
+ break;
+ x.month = atoi(buf);
+ } while (x.month <= 0 || x.month > 12);
+ do
+ {
+ sprintf(buf, "¥Í¤é¡Ð %02d ¤é¡G", u->day);
+ if (!vget(i, 0, buf, buf, 3, DOECHO))
+ break;
+ x.day = atoi(buf);
+ } while (x.day <= 0 || x.day > mmday[x.month-1] );
+
+ i++;
+ sprintf(buf, "©Ê§O (0)¤¤©Ê (1)¨k©Ê (2)¤k©Ê¡G[%d] ", u->sex);
+ if (vget(i, 0, buf, buf, 3, DOECHO))
+ x.sex = (*buf - '0') & 3;
+
+ i++;
+ str = x.realname;
+ do
+ {
+ vget(i, 0, "¯u¹ê©m¦W¡G", str, RNLEN + 1, GCARRY);
+ } while (strlen(str) < 4);
+
+ if (adm)
+ {
+// /* itoc.010317: ¤£Åý user §ï©m¦W */
+// i++;
+// str = x.realname;
+// do
+// {
+// vget(i, 0, "¯u¹ê©m¦W¡G", str, RNLEN + 1, GCARRY);
+// } while (strlen(str) < 4);
+
+ sprintf(buf, "%d", u->userno);
+ vget(++i, 0, "¥Î¤á½s¸¹¡G", buf, 10, GCARRY);
+ if ((num = atoi(buf)) > 0)
+ x.userno = num;
+
+ sprintf(buf, "%d", u->numlogins);
+ vget(++i, 0, "¤W½u¦¸¼Æ¡G", buf, 10, GCARRY);
+ if ((num = atoi(buf)) >= 0)
+ x.numlogins = num;
+
+ sprintf(buf, "%d", u->numposts);
+ vget(++i, 0, "¤å³¹½g¼Æ¡G", buf, 10, GCARRY);
+ if ((num = atoi(buf)) >= 0)
+ x.numposts = num;
+
+ /* itoc.010408: ·s¼Wª÷¿úÄæ¦ì */
+ sprintf(buf, "%d", u->money);
+ vget(++i, 0, "»È ¹ô¡G", buf, 10, GCARRY);
+ if ((num = atoi(buf)) >= 0)
+ x.money = num;
+
+ sprintf(buf, "%d", u->gold);
+ vget(++i, 0, "ª÷ ¹ô¡G", buf, 10, GCARRY);
+ if ((num = atoi(buf)) >= 0)
+ x.gold = num;
+
+ sprintf(buf, "%d", u->numemails);
+ vget(++i, 0, "µo«H¦¸¼Æ¡G", buf, 10, GCARRY);
+ if ((num = atoi(buf)) >= 0)
+ x.numemails = num;
+
+ vget(++i, 0, "¤W¯¸¦aÂI¡G", x.lasthost, sizeof(x.lasthost), GCARRY);
+ vget(++i, 0, "¶l¥ó«H½c¡G", x.email, sizeof(x.email), GCARRY);
+
+ if (vans("³]©w²ßºD(Y/N)¡H[N] ") == 'y')
+ x.ufo = bitset(x.ufo, NUMUFOS, NUMUFOS, MSG_USERUFO, ufo_tbl);
+
+ if (vans("³]©wÅv­­(Y/N)¡H[N] ") == 'y')
+ {
+set_perm:
+
+ i = setperm(num = x.userlevel);
+
+ if (i == num)
+ {
+ vmsg("¨ú®ø­×§ï");
+ if (adm == '2')
+ return;
+ }
+ else
+ {
+ x.userlevel = i;
+
+ /* itoc.011120: ¯¸ªø©ñ¤ô¥[¤W»{ÃÒ³q¹LÅv­­¡A­nªþ¥[§ï»{ÃҮɶ¡ */
+ if ((i & PERM_VALID) && !(num & PERM_VALID))
+ time(&x.tvalid);
+
+ /* itoc.050413: ¦pªG¯¸ªø¤â°Ê°±Åv¡A´N­n¥Ñ¯¸ªø¤~¯à¨Ó´_Åv */
+ if ((i & PERM_ALLDENY) && (i & PERM_ALLDENY) != (num & PERM_ALLDENY))
+ x.tvalid = INT_MAX;
+ }
+ }
+ }
+
+ if (!memcmp(&x, u, sizeof(ACCT)) || vans(msg_sure_ny) != 'y')
+ return;
+
+ if (adm)
+ {
+ if (str_cmp(u->userid, x.userid))
+ { /* Thor: 980806: ¯S§Oª`·N¦pªG usr¨C­Ó¦r¥À¤£¦b¦P¤@partitionªº¸Ü·|¦³°ÝÃD */
+ char dst[80];
+
+ usr_fpath(buf, u->userid, NULL);
+ usr_fpath(dst, x.userid, NULL);
+ rename(buf, dst);
+ /* Thor.990416: ¯S§Oª`·N! .USR¨Ã¥¼¤@¨Ö§ó·s, ¥i¯à¦³³¡¤À°ÝÃD */
+ }
+
+ /* itoc.010811: °ÊºA³]©w½u¤W¨Ï¥ÎªÌ */
+ /* ³Q¯¸ªø§ï¹L¸ê®Æªº½u¤W¨Ï¥ÎªÌ(¥]¬A¯¸ªø¦Û¤v)¡A¨ä cutmp->status ·|³Q¥[¤W STATUS_DATALOCK
+ ³o­ÓºX¼Ð¡A´NµLªk acct_save()¡A©ó¬O¯¸ªø«K¥i¥H­×§ï½u¤W¨Ï¥ÎªÌ¸ê®Æ */
+ /* ¦b¯¸ªø­×§ï¹L¤~¤W½uªº ID ¦]¬°¨ä cutmp->status ¨S¦³ STATUS_DATALOCK ªººX¼Ð¡A
+ ©Ò¥H±N¥i¥HÄ~Äò¦s¨ú¡A©Ò¥H½u¤W¦pªG¦P®É¦³­×§ï«e¡B­×§ï«áªº¦P¤@°¦ ID multi-login¡A¤]¬OµL§«¡C */
+ utmp_admset(x.userno, STATUS_DATALOCK | STATUS_COINLOCK);
+
+ /* lkchu.981201: security log */
+ adm_log(u, &x);
+ }
+ else
+ {
+ /* itoc.010804.µù¸Ñ: ½u¤Wªº userlevel/tvalid ¬Oªº¡A.ACCT ¸Ì¤~¬O·sªº */
+ if (acct_load(u, x.userid) >= 0)
+ {
+ x.userlevel = u->userlevel;
+ x.tvalid = u->tvalid;
+ }
+ }
+
+ memcpy(u, &x, sizeof(ACCT));
+ acct_save(u);
+}
+
+
+#if 0 /* itoc.010805.µù¸Ñ */
+
+ »{ÃÒ¦¨¥\¥u¥[¤W PERM_VALID¡AÅý user ¦b¤U¦¸¶i¯¸¤~¦Û°Ê±o¨ì PERM_POST | PERM_PAGE | PERM_CHAT
+ ¥H§K·s¤â¤W¸ô¡B°±Åvªº¥\¯à¥¢®Ä
+
+ ¦ý­«¶ñ email ®³±¼»{ÃҪ̻ݮ³±¼ PERM_VALID | PERM_POST | PERM_PAGE | PERM_CHAT
+ §_«h user ¥i¥H¦b¤U¦¸¶i¯¸«e¥ô·N¨Ï¥Î bbs_post
+
+#endif
+
+#if 0 /* itoc.010831.µù¸Ñ */
+
+ ¦]¬°½u¤W cuser.userlevel ¨Ã¤£¬O³Ì·sªº¡A¨Ï¥ÎªÌ¦pªG¦b½u¤W»{ÃҩάO³Q°±Åv¡A
+ µwºÐ¤¤ªº .ACCT ¼gªº¤~¬O¥¿½Tªº userlevel¡A
+ ©Ò¥H­n¥ýŪ¥X .ACCT¡A¥[¤J level «á¦A»\¦^¥h¡C
+
+ ¨Ï¥Î acct_seperm(&acct, adm) ¤§«e­n¥ý acct_load(&acct, userid)¡A
+ ¨ä¤¤ &acct ¤£¯à¬O &cuser¡C
+ ¨Ï¥ÎªÌ­n­«·s¤W¯¸¤~·|´«¦¨·sªºÅv­­¡C
+
+#endif
+
+void
+acct_setperm(u, levelup, leveldown) /* itoc.000219: ¥[/´îÅv­­µ{¦¡ */
+ ACCT *u;
+ usint levelup; /* ¥[Åv­­ */
+ usint leveldown; /* ´îÅv­­ */
+{
+ u->userlevel |= levelup;
+ u->userlevel &= ~leveldown;
+
+ acct_save(u);
+}
+
+
+/* ----------------------------------------------------- */
+/* ¼W¥[ª÷»È¹ô */
+/* ----------------------------------------------------- */
+
+
+void
+addmoney(addend)
+ int addend;
+{
+ if (addend < (INT_MAX - cuser.money)) /* Á×§K·¸¦ì */
+ cuser.money += addend;
+ else
+ cuser.money = INT_MAX;
+}
+
+
+void
+addgold(addend)
+ int addend;
+{
+ if (addend < (INT_MAX - cuser.gold)) /* Á×§K·¸¦ì */
+ cuser.gold += addend;
+ else
+ cuser.gold = INT_MAX;
+}
+
+
+/* ----------------------------------------------------- */
+/* ¬ÝªOºÞ²z */
+/* ----------------------------------------------------- */
+
+
+#ifndef HAVE_COSIGN
+static
+#endif
+int /* 1:¦XªkªºªO¦W */
+valid_brdname(brd)
+ char *brd;
+{
+ int ch;
+
+ if (!is_alnum(*brd))
+ return 0;
+
+ while (ch = *++brd)
+ {
+ if (!is_alnum(ch) && ch != '.' && ch != '-' && ch != '_')
+ return 0;
+ }
+ return 1;
+}
+
+
+static int
+brd_set(brd, row)
+ BRD *brd;
+ int row;
+{
+ int i, BMlen, len;
+ char *brdname, buf[80], userid[IDLEN + 2];
+ ACCT acct;
+
+ i = row;
+ brdname = brd->brdname;
+ strcpy(buf, brdname);
+
+ for (;;)
+ {
+ if (!vget(i, 0, MSG_BID, brdname, BNLEN + 1, GCARRY))
+ {
+ if (i == 1) /* ¶}·sªO­YµL¿é¤JªO¦Wªí¥ÜÂ÷¶} */
+ return -1;
+
+ strcpy(brdname, buf); /* Thor:­Y¬O²MªÅ«h³]¬°­ì¦WºÙ */
+ continue;
+ }
+
+ if (!strcmp(buf, brdname) && valid_brdname(brdname)) /* Thor: »P­ì¦W¦P«h¸õ¹L */
+ break;
+
+ if (brd_bno(brdname) >= 0)
+ outs("\n¿ù»~¡IªO¦W¹p¦P");
+ else if (valid_brdname(brdname))
+ break;
+ }
+
+ vget(++i, 0, "¬ÝªO¤ÀÃþ¡G", brd->class, BCLEN + 1, GCARRY);
+ vget(++i, 0, "¬ÝªO¥DÃD¡G", brd->title, BTLEN + 1, GCARRY);
+
+ /* vget(++i, 0, "ªO¥D¦W³æ¡G", brd->BM, BMLEN + 1, GCARRY); */
+
+ /* itoc.010212: ¶}·sªO/­×§ï¬ÝªO¦Û°Ê¥[¤WªO¥DÅv­­. */
+ /* ¥Ø«eªº§@ªk¬O¤@¿é¤J§¹ id ´N¥[¤JªO¥DÅv­­¡A§Y¨Ï³Ì«á¿ï¾Ü¤£ÅܰʡA
+ ¦pªG¦]¦¹¦h¥[¤FªO¥DÅv­­¡A¦b reaper.c ¤¤®³¤U */
+ vget(++i, 0, "ªO¥D³]©w R)¤@¯ë³]©w¤è¦¡ F)¦Û¥Ñ­×§ï¼Ò¦¡¡H[R] ", buf, 3, LCECHO);
+
+ if(buf[0]=='f'){
+ ++i;
+ outs("\033[1;33mĵ§i¡G¨Ï¥Î¦¹¼Ò¦¡­×§ïª©¥D¦W³æ·|¾É­P¨ü¼vÅTªº¨Ï¥ÎªÌªO¥DÅv­­¤£¥¿½T\033[m\n"
+ "\033[1;33m«ØÄ³¥u¦bªO¥D¦W³æªÅ¥Õ®É¨Ï¥Î¡A§_«h­×§ï«á½Ð¦Û¦æ³B²zÅv­­°ÝÃD\033[m");
+ i += 2;
+ vget(i, 0, "ªO¥D¦W³æ¡G", brd->BM , BMLEN + 1, GCARRY);
+
+ }else{
+ i += 4;
+ move(i - 2, 0);
+ prints("¥Ø«eªO¥D¬° %s\n½Ð¿é¤J·sªºªO¥D¦W³æ¡A©Î«ö [Return] ¤£§ï", brd->BM);
+
+ strcpy(buf, brd->BM);
+ BMlen = strlen(buf);
+
+ while (vget(i, 0, "½Ð¿é¤JªO¥D¡Aµ²§ô½Ð«ö Enter¡A²M±¼©Ò¦³ªO¥D½Ð¥´¡uµL¡v¡G", userid, IDLEN + 1, DOECHO))
+ {
+ if (!strcmp(userid, "µL"))
+ {
+ buf[0] = '\0';
+ BMlen = 0;
+ }
+ else if (is_bm(buf, userid)) /* §R°£Â¦³ªºªO¥D */
+ {
+ len = strlen(userid);
+ if (BMlen == len)
+ {
+ buf[0] = '\0';
+ }
+ else if (!str_cmp(buf + BMlen - len, userid)) /* ¦W³æ¤W³Ì«á¤@¦ì¡AID «á­±¤£±µ '/' */
+ {
+ buf[BMlen - len - 1] = '\0'; /* §R°£ ID ¤Î«e­±ªº '/' */
+ len++;
+ }
+ else /* ID «á­±·|±µ '/' */
+ {
+ str_lower(userid, userid);
+ strcat(userid, "/");
+ len++;
+ brdname = str_str(buf, userid);
+ strcpy(brdname, brdname + len);
+ }
+ BMlen -= len;
+ }
+ else if (acct_load(&acct, userid) >= 0 && !is_bm(buf, userid)) /* ¿é¤J·sªO¥D */
+ {
+ len = strlen(userid);
+ if (BMlen)
+ {
+ len++; /* '/' + userid */
+ if (BMlen + len > BMLEN)
+ {
+ vmsg("ªO¥D¦W³æ¹Lªø¡AµLªk±N³o ID ³]¬°ªO¥D");
+ continue;
+ }
+ sprintf(buf + BMlen, "/%s", acct.userid);
+ BMlen += len;
+ }
+ else
+ {
+ strcpy(buf, acct.userid);
+ BMlen = len;
+ }
+
+ acct_setperm(&acct, PERM_BM, 0);
+ }
+ move(i - 2, 0);
+ prints("¥Ø«eªO¥D¬° %s", buf);
+ clrtoeol();
+ }
+ strcpy(brd->BM, buf);
+ }
+
+#ifdef HAVE_MODERATED_BOARD
+ /* itoc.011208: §ï¥Î¸û«K§Qªº¬ÝªOÅv­­³]©w */
+ switch (vget(++i, 0, "¬ÝªOÅv­­ A)¤@¯ë B)¦Û©w C)¯µ±K D)¦n¤Í E)¯¸°ÈÁôª©¡H[Q] ", buf, 3, LCECHO))
+ {
+ case 'c':
+ brd->readlevel = PERM_SYSOP; /* ¯µ±K¬ÝªO */
+ brd->postlevel = 0;
+ brd->battr |= (BRD_NOSTAT | BRD_NOVOTE);
+ break;
+
+ case 'd':
+ brd->readlevel = PERM_BOARD; /* ¦n¤Í¬ÝªO */
+ brd->postlevel = 0;
+ brd->battr |= (BRD_NOSTAT | BRD_NOVOTE);
+ break;
+
+#else
+
+
+ switch (vget(++i, 0, "¬ÝªOÅv­­ A)¤@¯ë B)¦Û©w E)¯¸°ÈÁôªO¡H[Q] ", buf, 3, LCECHO))
+ {
+#endif
+
+ case 'a':
+ brd->readlevel = 0;
+ brd->postlevel = PERM_POST; /* ¤@¯ë¬ÝªOµoªíÅv­­¬° PERM_POST */
+ brd->battr &= ~(BRD_NOSTAT | BRD_NOVOTE); /* ®³±¼¦n¤Í¡®¯µ±KªOÄÝ©Ê */
+ break;
+
+ case 'e': /* ¯¸°ÈÁôªO */
+ brd->readlevel = (PERM_REGISTRAR | PERM_ACCOUNTS | PERM_CHATROOM | PERM_BOARD | PERM_SYSOP);
+ brd->postlevel = PERM_SYSOP;
+ brd->battr |= (BRD_NOSTAT | BRD_NOVOTE);
+ break;
+
+ case 'b':
+ if (vget(++i, 0, "¾\\ŪÅv­­(Y/N)¡H[N] ", buf, 3, LCECHO) == 'y')
+ {
+ brd->readlevel = bitset(brd->readlevel, NUMPERMS, NUMPERMS, MSG_READPERM, perm_tbl);
+ move(2, 0);
+ clrtobot();
+ i = 1;
+ }
+
+ if (vget(++i, 0, "µoªíÅv­­(Y/N)¡H[N] ", buf, 3, LCECHO) == 'y')
+ {
+ brd->postlevel = bitset(brd->postlevel, NUMPERMS, NUMPERMS, MSG_POSTPERM, perm_tbl);
+ move(2, 0);
+ clrtobot();
+ i = 1;
+ }
+ break;
+
+ default: /* ¹w³]¤£ÅÜ°Ê */
+ break;
+ }
+
+ if (vget(++i, 0, "³]©wÄÝ©Ê(Y/N)¡H[N] ", buf, 3, LCECHO) == 'y')
+ brd->battr = bitset(brd->battr, NUMBATTRS, NUMBATTRS, MSG_BRDATTR, battr_tbl);
+
+ return 0;
+}
+
+
+int /* 0:¶}ªO¦¨¥\ -1:¶}ªO¥¢±Ñ */
+brd_new(brd)
+ BRD *brd;
+{
+ int bno;
+ char fpath[64];
+
+ vs_bar("«Ø¥ß·sªO");
+
+ if (brd_set(brd, 1))
+ return -1;
+
+ if (vans(msg_sure_ny) != 'y')
+ return -1;
+
+ time(&brd->bstamp);
+ if ((bno = brd_bno("")) >= 0)
+ {
+ rec_put(FN_BRD, brd, sizeof(BRD), bno, NULL);
+ }
+ /* Thor.981102: ¨¾¤î¶W¹Lshm¬ÝªO­Ó¼Æ */
+ else if (bshm->number >= MAXBOARD)
+ {
+ vmsg("¶W¹L¨t²Î©Ò¯à®e¯Ç¬ÝªO­Ó¼Æ¡A½Ð½Õ¾ã¨t²Î°Ñ¼Æ");
+ return -1;
+ }
+ else if (rec_add(FN_BRD, brd, sizeof(BRD)) < 0)
+ {
+ vmsg("µLªk«Ø¥ß·sªO");
+ return -1;
+ }
+
+ gem_fpath(fpath, brd->brdname, NULL);
+ mak_dirs(fpath);
+ mak_dirs(fpath + 4);
+
+ bshm_reload(); /* force reload of bcache */
+
+ brh_save();
+ board_main(); /* reload brd_bits[] */
+
+ alog("·s¼W¬ÝªO",brd->brdname);
+
+ return 0;
+}
+
+
+static void
+brd_classchange(folder, oldname, newbrd) /* itoc.020117: ²§°Ê @Class ¤¤ªº¬ÝªO */
+ char *folder;
+ char *oldname;
+ BRD *newbrd; /* ­Y¬° NULL¡Aªí¥Ü­n§R°£¬ÝªO */
+{
+ int pos, xmode;
+ char fpath[64];
+ HDR hdr;
+
+ pos = 0;
+ while (!rec_get(folder, &hdr, sizeof(HDR), pos))
+ {
+ xmode = hdr.xmode & (GEM_BOARD | GEM_FOLDER);
+
+ if (xmode == (GEM_BOARD | GEM_FOLDER)) /* ¬ÝªOºëµØ°Ï±¶®| */
+ {
+ if (!strcmp(hdr.xname, oldname))
+ {
+ if (newbrd) /* ¬ÝªO§ó¦W */
+ {
+ brd2gem(newbrd, &hdr);
+ rec_put(folder, &hdr, sizeof(HDR), pos, NULL);
+ }
+ else /* ¬ÝªO§R°£ */
+ {
+ rec_del(folder, sizeof(HDR), pos, NULL);
+ continue; /* rec_del ¥H«á¤£»Ý­n pos++ */
+ }
+ }
+ }
+ else if (xmode == GEM_FOLDER) /* ¤ÀÃþ recursive ¶i¥h¬å */
+ {
+ hdr_fpath(fpath, folder, &hdr);
+ brd_classchange(fpath, oldname, newbrd);
+ }
+ pos++;
+ }
+}
+
+
+void
+brd_edit(bno)
+ int bno;
+{
+ BRD *bhdr, newbh;
+ char *bname, src[64], dst[64];;
+ char fpath[64];
+
+ vs_bar("¬ÝªO³]©w");
+ bhdr = bshm->bcache + bno;
+ memcpy(&newbh, bhdr, sizeof(BRD));
+ prints("¬ÝªO¦WºÙ¡G%s\n¬ÝªO»¡©ú¡G[%s] %s\nªO¥D¦W³æ¡G%s\n",
+ newbh.brdname, newbh.class, newbh.title, newbh.BM);
+
+ bitmsg(MSG_READPERM, STR_PERM, newbh.readlevel);
+ bitmsg(MSG_POSTPERM, STR_PERM, newbh.postlevel);
+ bitmsg(MSG_BRDATTR, STR_BATTR, newbh.battr);
+
+ switch (vget(8, 0, "(D)§R°£ (E)³]©w (L)Âê©w (Q)¨ú®ø¡H[Q] ", src, 3, LCECHO))
+ {
+ case 'd':
+
+ if (vget(9, 0, msg_sure_ny, src, 3, LCECHO) != 'y')
+ {
+ vmsg(MSG_DEL_CANCEL);
+ }
+ else
+ {
+ bname = bhdr->brdname;
+ if (*bname) /* itoc.000512: ¦P®É¬å°£¦P¤@­Ó¬ÝªO·|³y¦¨ºëµØ°Ï¡B¬ÝªO¥þ·´ */
+ {
+ alog("§R°£¬ÝªO", bname);
+
+ gem_fpath(src, bname, NULL);
+ f_rm(src);
+ f_rm(src + 4);
+ brd_classchange("gem/@/@"CLASS_INIFILE, bname, NULL); /* itoc.020117: §R°£ @Class ¤¤ªº¬ÝªOºëµØ°Ï±¶®| */
+ memset(&newbh, 0, sizeof(BRD));
+ sprintf(newbh.title, "[%s] deleted by %s", bname, cuser.userid);
+ memcpy(bhdr, &newbh, sizeof(BRD));
+ rec_put(FN_BRD, &newbh, sizeof(BRD), bno, NULL);
+
+ /* itoc.050531: ¬åªO·|³y¦¨¬ÝªO¤£¬O«ö¦r¥À±Æ§Ç¡A©Ò¥H­n­×¥¿ numberOld */
+ if (bshm->numberOld > bno)
+ bshm->numberOld = bno;
+
+ vmsg("§RªO§¹²¦");
+ }
+ }
+ break;
+
+ case 'l':
+ brd_fpath(fpath, bhdr->brdname, fn_lock);
+ brd_editlock(fpath);
+ break;
+
+ case 'e':
+
+ move(9, 0);
+ outs("ª½±µ«ö [Return] ¤£­×§ï¸Ó¶µ³]©w");
+
+ BRD saveold;
+
+ if (!brd_set(&newbh, 11))
+ {
+ if (memcmp(&newbh, bhdr, sizeof(BRD)) && vans(msg_sure_ny) == 'y')
+ {
+ saveold = *bhdr;
+ bname = bhdr->brdname;
+ if (strcmp(bname, newbh.brdname)) /* ¬ÝªO§ó¦W­n²¾¥Ø¿ý */
+ {
+ /* Thor.980806: ¯S§Oª`·N¦pªG¬ÝªO¤£¦b¦P¤@partition¸Ìªº¸Ü·|¦³°ÝÃD */
+ gem_fpath(src, bname, NULL);
+ gem_fpath(dst, newbh.brdname, NULL);
+ rename(src, dst);
+ rename(src + 4, dst + 4);
+ brd_classchange("gem/@/@"CLASS_INIFILE, bname, &newbh);/* itoc.050329: ²§°Ê @Class ¤¤ªº¬ÝªOºëµØ°Ï±¶®| */
+
+ /* itoc.050520: §ï¤FªO¦W·|³y¦¨¬ÝªO¤£¬O«ö¦r¥À±Æ§Ç¡A©Ò¥H­n­×¥¿ numberOld */
+ if (bshm->numberOld > bno)
+ bshm->numberOld = bno;
+ }
+ memcpy(bhdr, &newbh, sizeof(BRD));
+ rec_put(FN_BRD, &newbh, sizeof(BRD), bno, NULL);
+ brd_log(&saveold,&newbh);
+ }
+ }
+ vmsg("³]©w§¹²¦");
+ break;
+ }
+}
+
+
+void
+brd_title(bno) /* itoc.000312: ªO¥D­×§ï¤¤¤å±Ô­z */
+ int bno;
+{
+ BRD *bhdr, newbh;
+ char *blist;
+
+ bhdr = bshm->bcache + bno;
+ memcpy(&newbh, bhdr, sizeof(BRD));
+
+ blist = bhdr->BM;
+
+ if (blist[0] > ' ' && is_bm(blist, cuser.userid))
+ {
+ if (vans("¬O§_­×§ï¤¤¤åªO¦W±Ô­z(Y/N)¡H[N] ") == 'y')
+ {
+ vget(b_lines, 0, "¬ÝªO¥DÃD¡G", newbh.title, BTLEN + 1, GCARRY);
+ memcpy(bhdr, &newbh, sizeof(BRD));
+ rec_put(FN_BRD, &newbh, sizeof(BRD), bno, NULL);
+ }
+ }
+}
diff --git a/maple/bbsd.c b/maple/bbsd.c
new file mode 100644
index 0000000..986ea4b
--- /dev/null
+++ b/maple/bbsd.c
@@ -0,0 +1,1663 @@
+/*-------------------------------------------------------*/
+/* bbsd.c ( NTHU CS MapleBBS Ver 3.00 ) */
+/*-------------------------------------------------------*/
+/* author : opus.bbs@bbs.cs.nthu.edu.tw */
+/* target : BBS daemon/main/login/top-menu routines */
+/* create : 95/03/29 */
+/* update : 96/10/10 */
+/*-------------------------------------------------------*/
+
+
+#define _MAIN_C_
+
+
+#include "bbs.h"
+#include "dns.h"
+
+
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/telnet.h>
+#include <sys/resource.h>
+
+
+#define QLEN 3
+#define PID_FILE "run/bbs.pid"
+#define LOG_FILE "run/bbs.log"
+#undef SERVER_USAGE
+
+static int myports[MAX_BBSDPORT] = BBSD_PORT;
+
+static pid_t currpid;
+
+extern BCACHE *bshm;
+extern UCACHE *ushm;
+
+/* static int mport; */ /* Thor.990325: ¤£»Ý­n¤F:P */
+static u_long tn_addr;
+
+
+#ifdef CHAT_SECURE
+char passbuf[PSWDLEN + 1];
+#endif
+
+
+#ifdef MODE_STAT
+extern UMODELOG modelog;
+extern time_t mode_lastchange;
+#endif
+
+
+/* ----------------------------------------------------- */
+/* Â÷¶} BBS µ{¦¡ */
+/* ----------------------------------------------------- */
+
+
+ void
+alog(mode, msg) /* Admin ¦æ¬°°O¿ý */
+ char *mode, *msg;
+{
+ char buf[512];
+
+ sprintf(buf, "%s %s %-13s%s\n", Now(), mode, cuser.userid, msg);
+ f_cat(FN_RUN_ADMIN, buf);
+}
+
+
+ void
+blog(mode, msg) /* BBS ¤@¯ë°O¿ý */
+ char *mode, *msg;
+{
+ char buf[512];
+
+ sprintf(buf, "%s %s %-13s%s\n", Now(), mode, cuser.userid, msg);
+ f_cat(FN_RUN_USIES, buf);
+}
+
+ void
+cclog(mode, msg) /* Åv­­ÁʶR°O¿ý */
+ char *mode, *msg;
+{
+ char buf[512];
+
+ sprintf(buf, "%s %s %-13s%s\n", Now(), mode, cuser.userid, msg);
+ f_cat("run/buyperm.log", buf);
+}
+
+
+#ifdef MODE_STAT
+ void
+log_modes()
+{
+ time(&modelog.logtime);
+ rec_add(FN_RUN_MODE_CUR, &modelog, sizeof(UMODELOG));
+}
+#endif
+
+
+ void
+u_exit(mode)
+ char *mode;
+{
+ int fd, diff;
+ char fpath[80];
+ ACCT tuser;
+
+ if (currbno >= 0 && bshm->mantime[currbno] > 0)
+ bshm->mantime[currbno]--; /* °h¥X³Ì«á¬Ýªº¨º­ÓªO */
+
+ utmp_free(cutmp); /* ÄÀ©ñ UTMP shm */
+
+ diff = (time(&cuser.lastlogin) - ap_start) / 60;
+ sprintf(fpath, "Stay: %d (%d)", diff, currpid);
+ blog(mode, fpath);
+
+ if (cuser.userlevel)
+ {
+ ve_backup(); /* ½s¿è¾¹¦Û°Ê³Æ¥÷ */
+ brh_save(); /* Àx¦s¾\Ū°O¿ýÀÉ */
+ }
+
+#ifndef LOG_BMW /* Â÷¯¸§R°£¤ô²y */
+ usr_fpath(fpath, cuser.userid, fn_amw);
+ unlink(fpath);
+ usr_fpath(fpath, cuser.userid, fn_bmw);
+ unlink(fpath);
+#endif
+
+#ifdef MODE_STAT
+ log_modes();
+#endif
+
+
+ /* ¼g¦^ .ACCT */
+
+ if (!HAS_STATUS(STATUS_DATALOCK)) /* itoc.010811: ¨S¦³³Q¯¸ªøÂê©w¡A¤~¥i¥H¦^¦s .ACCT */
+ {
+ usr_fpath(fpath, cuser.userid, fn_acct);
+ fd = open(fpath, O_RDWR);
+ if (fd >= 0)
+ {
+ if (read(fd, &tuser, sizeof(ACCT)) == sizeof(ACCT))
+ {
+ if (diff >= 1)
+ {
+ cuser.numlogins++; /* Thor.980727.µù¸Ñ: ¦b¯¸¤W¥¼¶W¹L¤@¤ÀÄÁ¤£¤©­pºâ¦¸¼Æ */
+ addmoney(diff); /* itoc.010805: ¤W¯¸¤@¤ÀÄÁ¥[¤@¤¸ */
+ }
+
+ if (HAS_STATUS(STATUS_COINLOCK)) /* itoc.010831: ­Y¬O multi-login ªº²Ä¤G°¦¥H«á¡A¤£Àx¦s¿ú¹ô */
+ {
+ cuser.money = tuser.money;
+ cuser.gold = tuser.gold;
+ }
+
+ /* itoc.010811.µù¸Ñ: ¦pªG¨Ï¥ÎªÌ¦b½u¤W¨S¦³»{ÃÒªº¸Ü¡A
+ ¨º»ò cuser ¤Î tuser ªº userlevel/tvalid ¬O¦P¨Bªº¡F
+ ¦ý­Y¨Ï¥ÎªÌ¦b½u¤W¦^»{ÃÒ«H/¶ñ»{ÃÒ½X/³Q¯¸ªø¼f®Öµù¥U³æ..µ¥»{ÃÒ³q¹Lªº¸Ü¡A
+ ¨º»ò tuser ªº userlevel/tvalid ¤~¬O¤ñ¸û·sªº */
+ cuser.userlevel = tuser.userlevel;
+ cuser.tvalid = tuser.tvalid;
+
+ lseek(fd, (off_t) 0, SEEK_SET);
+ write(fd, &cuser, sizeof(ACCT));
+ }
+ close(fd);
+ }
+ }
+}
+
+
+ void
+abort_bbs()
+{
+ if (bbstate)
+ u_exit("AXXED");
+ exit(0);
+}
+
+
+ static void
+login_abort(msg)
+ char *msg;
+{
+ outs(msg);
+ refresh();
+ exit(0);
+}
+
+
+/* Thor.980903: lkchu patch: ¤£¨Ï¥Î¤W¯¸¥Ó½Ð±b¸¹®É, «h¤U¦C function§¡¤£¥Î */
+
+#ifdef LOGINASNEW
+
+/* ----------------------------------------------------- */
+/* Àˬd user µù¥U±¡ªp */
+/* ----------------------------------------------------- */
+
+
+ static int
+belong(flist, key)
+ char *flist;
+ char *key;
+{
+ int fd, rc;
+
+ rc = 0;
+ if ((fd = open(flist, O_RDONLY)) >= 0)
+ {
+ mgets(-1);
+
+ while (flist = mgets(fd))
+ {
+ str_lower(flist, flist);
+ if (str_str(key, flist))
+ {
+ rc = 1;
+ break;
+ }
+ }
+
+ close(fd);
+ }
+ return rc;
+}
+
+
+ static int
+is_badid(userid)
+ char *userid;
+{
+ int ch;
+ char *str;
+
+ if (strlen(userid) < 2)
+ return 1;
+
+ if (!is_alpha(*userid))
+ return 1;
+
+ if (!str_cmp(userid, STR_NEW))
+ return 1;
+
+ str = userid;
+ while (ch = *(++str))
+ {
+ if (!is_alnum(ch))
+ return 1;
+ }
+ return (belong(FN_ETC_BADID, userid));
+}
+
+
+ static int
+uniq_userno(fd)
+ int fd;
+{
+ char buf[4096];
+ int userno, size;
+ SCHEMA *sp; /* record length 16 ¥i¾ã°£ 4096 */
+
+ userno = 1;
+
+ while ((size = read(fd, buf, sizeof(buf))) > 0)
+ {
+ sp = (SCHEMA *) buf;
+ do
+ {
+ if (sp->userid[0] == '\0')
+ {
+ lseek(fd, -size, SEEK_CUR);
+ return userno;
+ }
+ userno++;
+ size -= sizeof(SCHEMA);
+ sp++;
+ } while (size);
+ }
+
+ return userno;
+}
+
+
+ static void
+acct_apply()
+{
+ SCHEMA slot;
+ char buf[80];
+ char *userid;
+ int try, fd;
+
+ film_out(FILM_APPLY, 0);
+
+ memset(&cuser, 0, sizeof(ACCT));
+ userid = cuser.userid;
+ try = 0;
+ for (;;)
+ {
+ if (!vget(18, 0, msg_uid, userid, IDLEN + 1, DOECHO))
+ login_abort("\n¦A¨£ ...");
+
+ if (is_badid(userid))
+ {
+ vmsg("µLªk±µ¨ü³o­Ó¥N¸¹¡A½Ð¨Ï¥Î­^¤å¦r¥À¡A¨Ã¥B¤£­n¥]§tªÅ®æ");
+ }
+ else
+ {
+ usr_fpath(buf, userid, NULL);
+ if (dashd(buf))
+ vmsg("¦¹¥N¸¹¤w¸g¦³¤H¨Ï¥Î");
+ else
+ break;
+ }
+
+ if (++try >= 10)
+ login_abort("\n±z¹Á¸Õ¿ù»~ªº¿é¤J¤Ó¦h¡A½Ð¤U¦¸¦A¨Ó§a");
+ }
+
+ for (;;)
+ {
+ vget(19, 0, "½Ð³]©w±K½X¡G", buf, PSWDLEN + 1, NOECHO);
+ if ((strlen(buf) < 4) || !strcmp(buf, userid))
+ {
+ vmsg("±K½X¤Ó²³æ¡A©ö¾D¤J«I¡A¦Ü¤Ö­n 4 ­Ó¦r¡A½Ð­«·s¿é¤J");
+ continue;
+ }
+
+ vget(20, 0, "½ÐÀˬd±K½X¡G", buf + PSWDLEN + 2, PSWDLEN + 1, NOECHO);
+ if (!strcmp(buf, buf + PSWDLEN + 2))
+ break;
+
+ vmsg("±K½X¿é¤J¿ù»~, ½Ð­«·s¿é¤J±K½X");
+ }
+
+ str_ncpy(cuser.passwd, genpasswd(buf), sizeof(cuser.passwd));
+
+ do
+ {
+ vget(20, 0, "¼Ê ºÙ¡G", cuser.username, UNLEN + 1, DOECHO);
+ } while (strlen(cuser.username) < 2);
+
+ // /* itoc.010317: ´£¥Ü user ¥H«á±N¤£¯à§ï©m¦W */
+ // vmsg("ª`·N¡G½Ð¿é¤J¯u¹ê©m¦W¡A¥»¯¸¤£´£¨Ñ­×§ï©m¦Wªº¥\\¯à");
+
+ do
+ {
+ vget(21, 0, "¯u¹ê©m¦W¡G", cuser.realname, RNLEN + 1, DOECHO);
+ } while (strlen(cuser.realname) < 4);
+
+#if 0
+ char year; /* ¥Í¤é(¥Á°ê¦~) */
+ char month; /* ¥Í¤é(¤ë) */
+ char day; /* ¥Í¤é(¤é) */
+
+#endif
+
+ int year;
+ do
+ {
+ vget(22, 0, "¥X¥Í¦~(¥Á°ê)¡G", buf, 4, DOECHO);
+ year = atoi(buf);
+ } while (year < 1 || year > 255);
+
+ cuser.year = year;
+
+ do
+ {
+ vget (22, 18, "¥X¥Í¤ë¡G", buf, 3, DOECHO);
+ cuser.month = atoi(buf);
+ } while (cuser.month < 1 || cuser.month > 12);
+
+ do
+ {
+ vget (22, 30, "¥X¥Í¤é¡G", buf, 3, DOECHO);
+ cuser.day = atoi(buf);
+ } while (cuser.day < 1 || cuser.day > 31);
+
+ do
+ {
+ vget (23, 0, "©Ê§O (0)¤¤©Ê (1)¨k©Ê (2)¤k©Ê¡G", buf, 2, DOECHO);
+ cuser.sex = atoi(buf);
+ } while (cuser.sex < 0 || cuser.sex > 2);
+
+ cuser.userlevel = PERM_DEFAULT;
+ cuser.ufo = UFO_DEFAULT_NEW;
+ cuser.numlogins = 1;
+ cuser.tvalid = ap_start; /* itoc.030724: ®³¤W¯¸®É¶¡·í²Ä¤@¦¸»{ÃÒ½Xªº seed */
+ sprintf(cuser.email, "%s.bbs@%s", cuser.userid, str_host); /* itoc.010902: ¹w³] email */
+
+ /* Ragnarok.050528: ¥i¯à¤G¤H¦P®É¥Ó½Ð¦P¤@­Ó ID¡A¦b¦¹¥²¶·¦AÀˬd¤@¦¸ */
+ usr_fpath(buf, userid, NULL);
+ if (dashd(buf))
+ {
+ vmsg("¦¹¥N¸¹­è³Qµù¥U¨«¡A½Ð­«·s¥Ó½Ð");
+ abort_bbs();
+ }
+
+ /* dispatch unique userno */
+
+ cuser.firstlogin = cuser.lastlogin = cuser.tcheck = slot.uptime = ap_start;
+ memcpy(slot.userid, userid, IDLEN);
+
+ fd = open(FN_SCHEMA, O_RDWR | O_CREAT, 0600);
+ {
+ /* flock(fd, LOCK_EX); */
+ /* Thor.981205: ¥Î fcntl ¨ú¥Nflock, POSIX¼Ð·Ç¥Îªk */
+ f_exlock(fd);
+
+ cuser.userno = try = uniq_userno(fd);
+ write(fd, &slot, sizeof(slot));
+ /* flock(fd, LOCK_UN); */
+ /* Thor.981205: ¥Î fcntl ¨ú¥Nflock, POSIX¼Ð·Ç¥Îªk */
+ f_unlock(fd);
+ }
+ close(fd);
+
+ /* create directory */
+
+ /* usr_fpath(buf, userid, NULL); */ /* ­è°µ¹L */
+ mkdir(buf, 0700);
+ strcat(buf, "/@");
+ mkdir(buf, 0700);
+ usr_fpath(buf, userid, "gem"); /* itoc.010727: ­Ó¤HºëµØ°Ï */
+ /* mak_dirs(buf); */
+ mak_links(buf); /* itoc.010924: ´î¤Ö­Ó¤HºëµØ°Ï¥Ø¿ý */
+#ifdef MY_FAVORITE
+ usr_fpath(buf, userid, "MF");
+ mkdir(buf, 0700);
+#endif
+
+ usr_fpath(buf, userid, fn_acct);
+ fd = open(buf, O_WRONLY | O_CREAT, 0600);
+ write(fd, &cuser, sizeof(ACCT));
+ close(fd);
+ /* Thor.990416: ª`·N: «ç»ò·|¦³ .ACCTªø«×¬O0ªº, ¦Ó¥B¥u¦³ @¥Ø¿ý, «ùÄòÆ[¹î¤¤ */
+
+ sprintf(buf, "%d", try);
+ blog("APPLY", buf);
+}
+
+#endif /* LOGINASNEW */
+
+
+/* ----------------------------------------------------- */
+/* bad login */
+/* ----------------------------------------------------- */
+
+
+#define FN_BADLOGIN "logins.bad"
+
+ static void
+logattempt(type, content)
+ int type; /* '-' login failure ' ' success */
+ char *content;
+{
+ char buf[128], fpath[64];
+
+ sprintf(buf, "%s %c %s\n", Btime(&ap_start), type, content);
+
+ usr_fpath(fpath, cuser.userid, FN_LOG);
+ f_cat(fpath, buf);
+
+ if (type != ' ')
+ {
+ usr_fpath(fpath, cuser.userid, FN_BADLOGIN);
+ sprintf(buf, "[%s] %s\n", Btime(&ap_start), fromhost);
+ f_cat(fpath, buf);
+ }
+}
+
+
+/* ----------------------------------------------------- */
+/* µn¿ý BBS µ{¦¡ */
+/* ----------------------------------------------------- */
+
+
+extern void talk_rqst();
+extern void bmw_rqst();
+
+
+#ifdef HAVE_WHERE
+//static
+ int /* 1:¦blist¤¤ 0:¤£¦blist¤¤ */
+belong_list(filelist, key, desc)
+ char *filelist, *key, *desc;
+{
+ FILE *fp;
+ char buf[80], *str;
+ int rc;
+
+ rc = 0;
+ if (fp = fopen(filelist, "r"))
+ {
+ while (fgets(buf, sizeof(buf), fp))
+ {
+ if (buf[0] == '#')
+ continue;
+
+ if (str = (char *) strchr(buf, ' '))
+ {
+ *str = '\0';
+ if (strstr(key, buf))
+ {
+ /* ¸õ¹LªÅ¥Õ¤À¹j */
+ for (str++; *str && isspace(*str); str++)
+ ;
+
+ strcpy(desc, str);
+ if (str = (char *) strchr(desc, '\n')) /* ³Ì«áªº '\n' ¤£­n */
+ *str = '\0';
+ rc = 1;
+ break;
+ }
+ }
+ }
+ fclose(fp);
+ }
+ return rc;
+}
+#endif
+
+
+ static void
+utmp_setup(mode)
+ int mode;
+{
+ UTMP utmp;
+ uschar *addr;
+
+ memset(&utmp, 0, sizeof(utmp));
+
+ utmp.pid = currpid;
+ utmp.userno = cuser.userno;
+ utmp.mode = bbsmode = mode;
+ /* utmp.in_addr = tn_addr; */ /* itoc.010112: §ïÅÜumtp.in_addr¥H¨Ïulist_cmp_host¥¿±` */
+ addr = (uschar *) &tn_addr;
+ utmp.in_addr = (addr[0] << 24) + (addr[1] << 16) + (addr[2] << 8) + addr[3];
+ utmp.userlevel = cuser.userlevel; /* itoc.010309: §â userlevel ¤]©ñ¤J cache */
+ utmp.ufo = cuser.ufo;
+ utmp.status = 0;
+
+ strcpy(utmp.userid, cuser.userid);
+ strcpy(utmp.realid, cuser.userid);
+#ifdef DETAIL_IDLETIME
+ utmp.idle_time = ap_start;
+#endif
+
+#ifdef GUEST_NICK
+ if (!cuser.userlevel) /* guest */
+ {
+ char nick[9][5] = {"¹C¤l", "¤ôºw", "³X«È", "¸É©«", "½ÞÀY", "¼v¤l", "¯f¬r", "µ£¦~", "¥Û¹³"};
+ sprintf(cuser.username, "¤Ó¶§¤Uªº%s", nick[ap_start % 9]);
+ }
+#endif /* GUEST_NICK */
+
+ strcpy(utmp.username, cuser.username);
+
+#ifdef HAVE_WHERE
+
+# ifdef GUEST_WHERE
+ if (!cuser.userlevel) /* guest */
+ {
+ /* itoc.010910: GUEST_NICK ©M GUEST_WHERE ªº¶Ã¼Æ¼Ò¼ÆÁ×§K¤@¼Ë */
+ char from[16][9] = {"­·«F¤E«ä", "«C®H´Â¶§", "²v·N³qÅü", "«n¥x»·²·", "±d²øªïÄf", "ºÑ¯ó¦p¯ô", "½t¼z¼í¥Í", "¦è®x¯º»y",
+ "¥É¾ð¦Vºa", "ºñ±»­«¼Ó", "ªQªL¥ß»A", "¦Ë´ò±á­·", "¦Ë¶é¬M«F", "¦±¹D§¨½®", "²ü¶í¤ë¦â", "«ä¶é¬K¾å"};
+ strcpy(utmp.from, from[ap_start % 16]);
+ }
+ else
+# endif /* GUEST_WHERE */
+ {
+
+ /* ¹³ hinet ³oºØ ip «Ü¦h¡A DN «Ü¤Öªº¡A´N¼g¤J etc/fqdn *
+ * ¹³ 140.112. ³oºØ´N¼g¦b etc/host *
+ * §Y¨Ï DNS Äê±¼¡A¦b etc/host ¸Ì­±ªºÁÙ¬O¥i¥H·Ó¼Ë§PÂ_¦¨¥\ *
+ * ¦pªG§â 140.112. ¼g¤J etc/host ¤¤¡A´N¤£¥Î§â ntu.edu.tw *
+ * ­«Âмg¤J etc/fqdn ¸Ì¤F */
+
+ char name[48];
+
+ /* ¥ý¤ñ¹ï ID */
+ str_lower(name, cuser.userid);
+ if (!belong_list(FN_ETC_IDHOME, name, utmp.from))
+ {
+ /* ¦A¤ñ¹ï FQDN */
+ str_lower(name, fromhost); /* itoc.011011: ¤j¤p¼g§¡¥i¡Aetc/fqdn ¸Ì­±³£­n¼g¤p¼g */
+ if (!belong_list(FN_ETC_FQDN, name, utmp.from))
+ {
+ /* ¦A¤ñ¹ï ip */
+ sprintf(name, "%d.%d.%d.%d", addr[0], addr[1], addr[2], addr[3]);
+ if (!belong_list(FN_ETC_HOST, name, utmp.from))
+ str_ncpy(utmp.from, fromhost, sizeof(utmp.from)); /* ¦pªG³£¨S§ä¨ì¹ïÀ³¬G¶m¡A´N¬O¥Î fromhost */
+ }
+ }
+ }
+
+#else
+ str_ncpy(utmp.from, fromhost, sizeof(utmp.from));
+#endif /* HAVE_WHERE */
+
+ /* Thor: §i¶DUser¤w¸gº¡¤F©ñ¤£¤U... */
+ if (!utmp_new(&utmp))
+ login_abort("\n±z­è­è¿ïªº¦ì¤l¤w¸g³Q¤H±¶¨¬¥ýµn¤F¡A½Ð¤U¦¸¦A¨Ó§a");
+
+ /* itoc.001223: utmp_new §¹¦A pal_cache¡A¦pªG login_abort ´N¤£°µ¤F */
+ pal_cache();
+}
+
+
+/* ----------------------------------------------------- */
+/* user login */
+/* ----------------------------------------------------- */
+
+
+ static int /* ¦^¶Ç multi */
+login_user(content)
+ char *content;
+{
+ int attempts; /* ¹Á¸Õ´X¦¸¿ù»~ */
+ int multi;
+ char fpath[64], uid[IDLEN + 1];
+#ifndef CHAT_SECURE
+ char passbuf[PSWDLEN + 1];
+#endif
+
+ move(b_lines, 0);
+ time_t now = time(NULL);
+ if (now >= 1301587200 && now <= 1301673600) /* ·M¤H¸`­pµe */
+ {
+ outs("\033[1;44m ¡° ¥»¯¸¤£¶}©ñ \033[1;32m" STR_GUEST "\033[1;37m °ÑÆ["
+ " ¥Ó½Ð·s±b¸¹¡G\033[1;31m" STR_NEW "\033[1;44m \033[m");
+ }
+ else
+ {
+ outs(" ¡° ¥»¯¸¤£¶}©ñ \033[1;32m" STR_GUEST "\033[m °ÑÆ["
+ " ¥Ó½Ð·s±b¸¹¡G\033[1;31m" STR_NEW "\033[m");
+ }
+ attempts = 0;
+ multi = 0;
+ for (;;)
+ {
+ if (++attempts > LOGINATTEMPTS)
+ {
+ film_out(FILM_TRYOUT, 0);
+ login_abort("\n¦A¨£ ...");
+ }
+
+ time_t now = time(NULL);
+ if (now >= 1301587200 && now <= 1301673600) /* ·M¤H¸` */
+ {
+ vget(b_lines - 2, 0, " [Login ID] ", uid, IDLEN + 1, DOECHO);
+ }
+ else
+ {
+ vget(b_lines - 2, 0, " [±zªº±b¸¹] ", uid, IDLEN + 1, DOECHO);
+ }
+
+ if (!str_cmp(uid, STR_NEW))
+ {
+#ifdef LOGINASNEW
+# ifdef HAVE_GUARANTOR /* itoc.000319: «OÃÒ¤H¨î«× */
+ vget(b_lines - 2, 0, " [±zªº«O¤H] ", uid, IDLEN + 1, DOECHO);
+ if (!*uid || (acct_load(&cuser, uid) < 0))
+ {
+ vmsg("©êºp¡A¨S¦³¤¶²Ð¤H¤£±o¥[¤J¥»¯¸");
+ }
+ else if (!HAS_PERM(PERM_GUARANTOR))
+ {
+ vmsg("©êºp¡A±z¤£°÷¸ê®æ¾á¥ô§O¤Hªº¤¶²Ð¤H");
+ }
+ else if (!vget(b_lines - 2, 40, "[«O¤H±K½X] ", passbuf, PSWDLEN + 1, NOECHO))
+ {
+ continue;
+ }
+ else
+ {
+ if (chkpasswd(cuser.passwd, passbuf))
+ {
+ logattempt('-', content);
+ vmsg(ERR_PASSWD);
+ }
+ else
+ {
+ FILE *fp;
+ char parentid[IDLEN + 1], buf[80];
+ time_t now;
+
+ /* itoc.010820: °O¿ý«O¤H©ó«OÃÒ¤H¤Î³Q«O¤H */
+ strcpy(parentid, cuser.userid);
+ acct_apply();
+ time(&now);
+
+ /* itoc.010820.µù¸Ñ: §â¹ï¤è log ¦b¦æ­º¡A¦b reaper ®É¥i¥H¤è«K¬å tree */
+ sprintf(buf, "%s ©ó %s ¤¶²Ð¦¹¤H(%s)¥[¤J¥»¯¸\n", parentid, Btime(&now), cuser.userid);
+ usr_fpath(fpath, cuser.userid, "guarantor");
+ if (fp = fopen(fpath, "a"))
+ {
+ fputs(buf, fp);
+ fclose(fp);
+ }
+ sprintf(buf, "%s ©ó %s ³Q¦¹¤H(%s)¤¶²Ð¥[¤J¥»¯¸\n", cuser.userid, Btime(&now), parentid);
+ usr_fpath(fpath, parentid, "guarantor");
+ if (fp = fopen(fpath, "a"))
+ {
+ fputs(buf, fp);
+ fclose(fp);
+ }
+
+ break;
+ }
+ }
+# else
+ acct_apply(); /* Thor.980917: µù¸Ñ: setup cuser ok */
+ break;
+# endif
+#else
+ outs("\n¥»¨t²Î¥Ø«e¼È°±½u¤Wµù¥U, ½Ð¥Î " STR_GUEST " ¶i¤J");
+ continue;
+#endif
+ }
+ else if (!*uid)
+ {
+ /* ­Y¨S¿é¤J ID¡A¨º»ò continue */
+ }
+ else if (str_cmp(uid, STR_GUEST)) /* ¤@¯ë¨Ï¥ÎªÌ */
+ {
+ if (now >= 1301587200 && now <= 1301673600) /* ·M¤H¸` */
+ {
+ if (!vget(b_lines - 2, 40, "[ÅçÃÒª÷Æ_] ", passbuf, PSWDLEN + 1, NOECHO))
+ continue; /* ¤£¥´±K½X«h¨ú®øµn¤J */
+ }
+ else
+ {
+ if (!vget(b_lines - 2, 40, "[±zªº±K½X] ", passbuf, PSWDLEN + 1, NOECHO))
+ continue; /* ¤£¥´±K½X«h¨ú®øµn¤J */
+ }
+ /* itoc.040110: ¦b¿é¤J§¹ ID ¤Î±K½X¡A¤~¸ü¤J .ACCT */
+ if (acct_load(&cuser, uid) < 0)
+ {
+ vmsg(err_uid);
+ continue;
+ }
+
+ if (chkpasswd(cuser.passwd, passbuf))
+ {
+ logattempt('-', content);
+ vmsg(ERR_PASSWD);
+ }
+ else
+ {
+
+ if (!str_cmp(cuser.userid, str_sysop))
+ {
+#ifdef SYSOP_SU
+ /* ²³æªº SU ¥\¯à */
+ if (vans("Åܧó¨Ï¥ÎªÌ¨­¤À(Y/N)¡H[N] ") == 'y')
+ {
+ for (;;)
+ {
+ if (vget(b_lines - 2, 0, " [Åܧó±b¸¹] ", uid, IDLEN + 1, DOECHO) &&
+ acct_load(&cuser, uid) >= 0)
+ break;
+ vmsg(err_uid);
+ }
+ }
+ else
+#endif
+ {
+ /* SYSOP gets all permission bits */
+ /* itoc.010902: DENY perm ±Æ¥~ */
+ // cuser.userlevel = ~0 ^ (PERM_DENYMAIL | PERM_DENYTALK | PERM_DENYCHAT | PERM_DENYPOST | PERM_DENYLOGIN | PERM_PURGE);
+ }
+ }
+
+ if (cuser.ufo & UFO_ACL)
+ {
+ usr_fpath(fpath, cuser.userid, FN_ACL);
+ str_lower(fromhost, fromhost); /* lkchu.981201: ´«¤p¼g */
+ if (!acl_has(fpath, "", fromhost))
+ { /* Thor.980728: ª`·N acl Àɤ¤­n¥þ³¡¤p¼g */
+ logattempt('-', content);
+ login_abort("\n±zªº¤W¯¸¦aÂI¤£¤Ó¹ï«l¡A½Ð®Ö¹ï [¤W¯¸¦aÂI³]©wÀÉ]");
+ }
+ }
+
+ logattempt(' ', content);
+
+ /* check for multi-session */
+
+ if (!HAS_PERM(PERM_ALLADMIN))
+ {
+ UTMP *ui;
+ pid_t pid;
+
+ if (HAS_PERM(PERM_DENYLOGIN | PERM_PURGE))
+ login_abort("\n³o­Ó±b¸¹¼È°±ªA°È¡A¸Ô±¡½Ð¦V¯¸ªø¬¢¸ß¡C");
+
+
+ if (!(ui = (UTMP *) utmp_find(cuser.userno)))
+ break; /* user isn't logged in */
+
+ pid = ui->pid;
+ if (pid && vans("±z·Q½ð±¼¨ä¥L­«½Æªº login (Y/N)¶Ü¡H[Y] ") != 'n' && pid == ui->pid)
+ {
+ if ((kill(pid, SIGTERM) == -1) && (errno == ESRCH))
+ utmp_free(ui);
+ else
+ sleep(3); /* ³Q½ðªº¤H³o®É­Ô¥¿¦b¦Û§Ú¤FÂ_ */
+ blog("MULTI", cuser.userid);
+ }
+
+ if ((multi = utmp_count(cuser.userno, 0)) >= MULTI_MAX || /* ½u¤W¤w¦³ MULTI_MAX °¦¦Û¤v¡A¸T¤îµn¤J */
+ (!multi && acct_load(&cuser, uid) < 0)) /* yiting.050101: ­Y­è¤w½ð±¼©Ò¦³ multi-login¡A¨º»ò­«·sŪ¨ú¥H®M¥ÎÅܧó */
+ login_abort("\n¦A¨£ ...");
+ }
+ break;
+ }
+ }
+ else
+ { /* guest */
+ vmsg("©êºp¡A¥»¯¸¤£¶}©ñ " STR_GUEST " °ÑÆ[");
+ continue;
+ }
+ }
+
+ return multi;
+}
+
+
+ static void
+login_level()
+{
+ int fd;
+ usint level;
+ ACCT tuser;
+ char fpath[64];
+
+ /* itoc.010804.µù¸Ñ: ¦³ PERM_VALID ªÌ¦Û°Êµoµ¹ PERM_POST PERM_PAGE PERM_CHAT */
+ level = cuser.userlevel | (PERM_ALLVALID ^ PERM_VALID);
+
+ if (!(level & PERM_ALLADMIN))
+ {
+#ifdef JUSTIFY_PERIODICAL
+ if ((level & PERM_VALID) && (cuser.tvalid + VALID_PERIOD < ap_start))
+ {
+ level ^= PERM_VALID;
+ /* itoc.011116: ¥D°Êµo«H³qª¾¨Ï¥ÎªÌ¡A¤@ª½°e«H¤£ª¾¹D·|¤£·|¤Ó¯ÓªÅ¶¡ !? */
+ mail_self(FN_ETC_REREG, str_sysop, "±zªº»{ÃÒ¤w¸g¹L´Á¡A½Ð­«·s»{ÃÒ", 0);
+ }
+#endif
+
+#ifdef NEWUSER_LIMIT
+ /* §Y¨Ï¤w¸g³q¹L»{ÃÒ¡AÁÙ¬O­n¨£²ß¤T¤Ñ */
+ if (ap_start - cuser.firstlogin < 3 * 86400)
+ level &= ~PERM_POST;
+#endif
+
+ /* itoc.000520: ¥¼¸g¨­¤À»{ÃÒ, ¸T¤î post/chat/talk/write */
+ if (!(level & PERM_VALID))
+ level &= ~(PERM_POST | PERM_CHAT | PERM_PAGE);
+
+ if (level & PERM_DENYPOST)
+ level &= ~PERM_POST;
+
+ if (level & PERM_DENYTALK)
+ level &= ~PERM_PAGE;
+
+ if (level & PERM_DENYCHAT)
+ level &= ~PERM_CHAT;
+
+ if ((cuser.numemails >> 4) > (cuser.numlogins + cuser.numposts))
+ level |= PERM_DENYMAIL;
+ }
+
+ cuser.userlevel = level;
+
+ usr_fpath(fpath, cuser.userid, fn_acct);
+ if ((fd = open(fpath, O_RDWR)) >= 0)
+ {
+ if (read(fd, &tuser, sizeof(ACCT)) == sizeof(ACCT))
+ {
+ /* itoc.010805.µù¸Ñ: ³o¦¸ªº¼g¦^ .ACCT ¬O¬°¤FÅý§O¤H Query ½u¤W¨Ï¥ÎªÌ®É
+ ¥X²{ªº¤W¯¸®É¶¡/¨Ó·½¥¿½T¡A¥H¤Î¦^¦s¥¿½Tªº userlvel */
+tuser.userlevel = level;
+ tuser.lastlogin = ap_start;
+ strcpy(tuser.lasthost, cuser.lasthost);
+
+ lseek(fd, (off_t) 0, SEEK_SET);
+ write(fd, &tuser, sizeof(ACCT));
+ }
+ close(fd);
+ }
+}
+
+
+ static void
+login_status(multi)
+ int multi;
+{
+ usint status;
+ char fpath[64];
+ struct tm *ptime;
+
+ status = 0;
+
+ /* itoc.010831: multi-login ªº²Ä¤G°¦¥[¤W¤£¥iÅܰʿú¹ôªººX¼Ð */
+ if (multi)
+ status |= STATUS_COINLOCK;
+
+ /* itoc.011022: ¥[¤J¥Í¤éºX¼Ð */
+ ptime = localtime(&ap_start);
+ if (cuser.day == ptime->tm_mday && cuser.month == ptime->tm_mon + 1)
+ status |= STATUS_BIRTHDAY;
+
+ /* ªB¤Í¦W³æ¦P¨B¡B²M²z¹L´Á«H¥ó */
+ if (ap_start > cuser.tcheck + CHECK_PERIOD)
+ {
+ outz(MSG_CHKDATA);
+ refresh();
+
+ cuser.tcheck = ap_start;
+ usr_fpath(fpath, cuser.userid, fn_pal);
+ pal_sync(fpath);
+#ifdef HAVE_ALOHA
+ usr_fpath(fpath, cuser.userid, FN_FRIENZ);
+ frienz_sync(fpath);
+#endif
+#ifdef OVERDUE_MAILDEL
+ status |= m_quota(); /* Thor.µù¸Ñ: ¸ê®Æ¾ã²z½]®Ö¦³¥]§t BIFF check */
+#endif
+ }
+#ifdef OVERDUE_MAILDEL
+ else
+#endif
+ status |= m_query(cuser.userid);
+
+ /* itoc.010924: Àˬd­Ó¤HºëµØ°Ï¬O§_¹L¦h */
+#ifndef LINUX /* ¦b Linux ¤U³oÀˬd©Ç©Çªº */
+ {
+ struct stat st;
+ usr_fpath(fpath, cuser.userid, "gem");
+ if (!stat(fpath, &st) && (st.st_size >= 512 * 7))
+ status |= STATUS_MGEMOVER;
+ }
+#endif
+
+ cutmp->status |= status;
+}
+
+
+ static void
+login_other()
+{
+ usint status;
+ char fpath[64];
+
+ /* §R°£¿ù»~µn¤J°O¿ý */
+ usr_fpath(fpath, cuser.userid, FN_BADLOGIN);
+ if (more(fpath, (char *) -1) >= 0 && vans("¥H¤W¬°¿é¤J±K½X¿ù»~®Éªº¤W¯¸¦aÂI°O¿ý¡A­n§R°£¶Ü(Y/N)¡H[Y] ") != 'n')
+ unlink(fpath);
+
+ if (!HAS_PERM(PERM_VALID))
+ film_out(FILM_NOTIFY, -1); /* ©|¥¼»{ÃÒ³qª¾ */
+#ifdef JUSTIFY_PERIODICAL
+ else if (!HAS_PERM(PERM_ALLADMIN) && (cuser.tvalid + VALID_PERIOD - INVALID_NOTICE_PERIOD < ap_start))
+ film_out(FILM_REREG, -1); /* ¦³®Ä®É¶¡¹O´Á 10 ¤Ñ«e´£¥Xĵ§i */
+#endif
+
+#ifdef NEWUSER_LIMIT
+ if (ap_start - cuser.firstlogin < 3 * 86400)
+ film_out(FILM_NEWUSER, -1); /* §Y¨Ï¤w¸g³q¹L»{ÃÒ¡AÁÙ¬O­n¨£²ß¤T¤Ñ */
+#endif
+
+ status = cutmp->status;
+
+#ifdef OVERDUE_MAILDEL
+ if (status & STATUS_MQUOTA)
+ film_out(FILM_MQUOTA, -1); /* ¹L´Á«H¥ó§Y±N²M°£Äµ§i */
+#endif
+
+ if (status & STATUS_MAILOVER)
+ film_out(FILM_MAILOVER, -1); /* «H¥ó¹L¦h©Î±H«H¹L¦h */
+
+ //if (status & STATUS_MGEMOVER)
+ //film_out(FILM_MGEMOVER, -1); /* itoc.010924: ­Ó¤HºëµØ°Ï¹L¦hĵ§i */
+
+ if (status & STATUS_BIRTHDAY)
+ film_out(FILM_BIRTHDAY, -1); /* itoc.010415: ¥Í¤é·í¤Ñ¤W¯¸¦³ special Åwªïµe­± */
+
+ ve_recover(); /* ¤W¦¸Â_½u¡A½s¿è¾¹¦^¦s */
+}
+
+
+ static void
+tn_login()
+{
+ int multi;
+ char buf[128];
+
+ bbsmode = M_LOGIN; /* itoc.020828: ¥H§K¹L¤[¥¼¿é¤J®É igetch ·|¥X²{ movie */
+
+ /* --------------------------------------------------- */
+ /* µn¿ý¨t²Î */
+ /* --------------------------------------------------- */
+
+ /* Thor.990415: °O¿ýip, ©È¥¿¬d¤£¨ì */
+ sprintf(buf, "%s ip:%08x (%d)", fromhost, tn_addr, currpid);
+
+ multi = login_user(buf);
+
+ blog("ENTER", buf);
+
+ /* --------------------------------------------------- */
+ /* ªì©l¤Æ utmp¡Bflag¡Bmode¡B«H½c */
+ /* --------------------------------------------------- */
+
+ bbstate = STAT_STARTED; /* ¶i¤J¨t²Î¥H«á¤~¥i¥H¦^¤ô²y */
+ utmp_setup(M_LOGIN); /* Thor.980917: µù¸Ñ: cutmp, cutmp-> setup ok */
+ total_user = ushm->count; /* itoc.011027: ¥¼¶i¨Ï¥ÎªÌ¦W³æ«e¡A±Ò©l¤Æ total_user */
+
+ mbox_main();
+
+#ifdef MODE_STAT
+ memset(&modelog, 0, sizeof(UMODELOG));
+ mode_lastchange = ap_start;
+#endif
+
+ if (cuser.userlevel) /* not guest */
+ {
+ /* ------------------------------------------------- */
+ /* ®Ö¹ï user level ¨Ã±N .ACCT ¼g¦^ */
+ /* ------------------------------------------------- */
+
+ /* itoc.030929: ¦b .ACCT ¼g¦^¥H«e¡A¤£¥i¥H¦³¥ô¦ó vmsg(NULL) ©Î more(xxxx, NULL)
+ µ¥ªºªF¦è¡A³o¼Ë¦pªG user ¦b vmsg(NULL) ®É¦^»{ÃÒ«H¡A¤~¤£·|³Q¼g¦^ªº cuser »\¹L */
+
+ cuser.lastlogin = ap_start;
+ str_ncpy(cuser.lasthost, fromhost, sizeof(cuser.lasthost));
+
+ login_level();
+
+ /* ------------------------------------------------- */
+ /* ³]©w status */
+ /* ------------------------------------------------- */
+
+ login_status(multi);
+
+ /* ------------------------------------------------- */
+ /* ¨q¨Ç¸ê°T */
+ /* ------------------------------------------------- */
+
+ login_other();
+ }
+
+ srand(ap_start * cuser.userno * currpid);
+}
+
+
+ static void
+tn_motd()
+{
+ usint ufo;
+
+ ufo = cuser.ufo;
+
+ if (!(ufo & UFO_MOTD))
+ {
+ more("gem/@/@-day", NULL); /* ¤µ¤é¼öªù¸ÜÃD */
+ pad_view();
+ }
+ time_t now = time(NULL);
+ if (now >= 1301587200 && now <= 1301673600)
+ more("gem/@/@FOOLDAYPOST", NULL); /* ·M¤H¸` */
+
+#ifdef HAVE_NOALOHA
+ if (!(ufo & UFO_NOALOHA))
+#endif
+ {
+#ifdef LOGIN_NOTIFY
+ loginNotify();
+#endif
+#ifdef HAVE_ALOHA
+ aloha();
+#endif
+ }
+
+#ifdef HAVE_FORCE_BOARD
+ brd_force(); /* itoc.000319: ±j¨î¾\Ū¤½§iªO */
+#endif
+}
+
+
+/* ----------------------------------------------------- */
+/* trap signals */
+/* ----------------------------------------------------- */
+
+
+ static void
+tn_signals()
+{
+ struct sigaction act;
+
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+
+ act.sa_handler = (void *) abort_bbs;
+ sigaction(SIGBUS, &act, NULL);
+ sigaction(SIGSEGV, &act, NULL);
+ sigaction(SIGTERM, &act, NULL);
+ sigaction(SIGXCPU, &act, NULL);
+#ifdef SIGSYS
+ /* Thor.981221: easy for porting */
+ sigaction(SIGSYS, &act, NULL);/* bad argument to system call */
+#endif
+
+ act.sa_handler = (void *) talk_rqst;
+ sigaction(SIGUSR1, &act, NULL);
+
+ act.sa_handler = (void *) bmw_rqst;
+ sigaction(SIGUSR2, &act, NULL);
+
+ /* ¦b¦¹­É¥Î sigset_t act.sa_mask */
+ sigaddset(&act.sa_mask, SIGPIPE);
+ sigprocmask(SIG_BLOCK, &act.sa_mask, NULL);
+
+}
+
+ static int
+show_file(fpath, ln, lines)
+ char *fpath;
+ int ln, lines;
+{
+ FILE *fp;
+ char buf[ANSILINELEN];
+ int i;
+
+ if ((fp = fopen(fpath, "r")))
+ {
+ i = lines;
+ while (fgets(buf, ANSILINELEN, fp) && i--)
+ outs(buf);
+ fclose(fp);
+ return 1;
+ }
+
+ sprintf(buf, "%s ¿ò¥¢¡A½Ð§iª¾¯¸ªø", fpath);
+ zmsg(buf);
+ return 0;
+}
+
+ static inline void
+tn_main()
+{
+ clear();
+
+#ifdef HAVE_LOGIN_DENIED
+ if (acl_has(BBS_ACLFILE, "", fromhost))
+ login_abort("\n¶Q¾÷¾¹©ó¤£³Q±Í¯¸±µ¨ü");
+#endif
+
+
+ /*
+ outs("Ãö¯¸¡A¹w­p¤Q¤ÀÄÁ«á«ì´_\n³y¦¨±zªº§xÂZ²`·P©êºp");
+ if (vkey() != 'm' || vkey() != 'p' || vkey() != '6' || vkey() != '0' || vkey() != '7')
+ login_abort("\nÃö¯¸ºû­×¡A¦A¨£");
+ move(0, 0);
+ */
+
+ time(&ap_start);
+
+ time_t now = time(NULL);
+ if (now >= 1301587200 && now <= 1301673600)
+ {
+ prints("\033[1;36;44msony.tfcis.org\033[0;44m \033[1;32m¡÷¡÷¡÷\033[0;44m \033[1;33m«n¤@¸ê°T\033[0;44m \033[1;32m¡ö¡ö¡ö\033[0;44 \033[1;36m210.70.137.212\033[m\n");
+ prints("\033[1;44m Åwªï¥úÁ{¡i\033[33;46m ¯Á¥§¤p¯¸ \033[37;44m¡j¥Ø«e½u¤W¤H¼Æ [\033[33m%d\033[37m] ¤H\033[0;44m \033[m", ushm->count);
+ }
+ else
+ {
+ prints("%s ¡ó " SCHOOLNAME " ¡ó " MYIPADDR "\n"
+ "Åwªï¥úÁ{¡i\033[1;33;46m %s \033[m¡j¥Ø«e½u¤W¤H¼Æ [%d] ¤H",
+ str_host, str_site, ushm->count);
+ }
+ //film_out((ap_start % 3) + FILM_OPENING0, 3); /* ¶Ã¼ÆÅã¥Ü¶}ÀYµe­± */
+ char fpath[64];
+ time(&ap_start);
+ sprintf(fpath, "gem/@/@opening.%d", time(0) % 3);
+ prints("\n\n"); /* Chensc.070322: ­×¥¿¶i¯¸µe­±««ª½¦ì¸m */
+ show_file(fpath, 3, 20);
+
+ currpid = getpid();
+
+ tn_signals(); /* Thor.980806: ©ñ©ó tn_login«e, ¥H«K call in¤£·|³Q½ð */
+ tn_login();
+
+ board_main();
+ gem_main();
+#ifdef MY_FAVORITE
+ mf_main();
+#endif
+ talk_main();
+
+ tn_motd();
+
+ menu();
+ abort_bbs(); /* to make sure it will terminate */
+}
+
+
+/* ----------------------------------------------------- */
+/* 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
+ };
+
+ int n, len;
+ char *cmd;
+ int rset;
+ struct timeval to;
+ char buf[64];
+
+ /* --------------------------------------------------- */
+ /* init telnet protocol */
+ /* --------------------------------------------------- */
+
+ cmd = svr;
+
+ for (n = 0; n < 4; n++)
+ {
+ len = (n == 1 ? 6 : 3);
+ send(0, cmd, len, 0);
+ cmd += len;
+
+ rset = 1;
+ /* Thor.981221: for future reservation bug */
+ to.tv_sec = 1;
+ to.tv_usec = 1;
+ if (select(1, (fd_set *) & rset, NULL, NULL, &to) > 0)
+ recv(0, buf, sizeof(buf), 0);
+ }
+}
+
+
+/* ----------------------------------------------------- */
+/* ¤ä´©¶W¹L 24 ¦Cªºµe­± */
+/* ----------------------------------------------------- */
+
+
+ static void
+term_init()
+{
+#if 0 /* fuse.030518: µù¸Ñ */
+ server°Ý¡G§A·|§ïÅܦæ¦C¼Æ¶Ü¡H(TN_NAWS, Negotiate About Window Size)
+ client榭GYes, I do. (TNCH_DO)
+
+ ¨º»ò¦b³s½u®É¡A·íTERMÅܤƦæ¦C¼Æ®É´N·|µo¥X¡G
+ TNCH_IAC + TNCH_SB + TN_NAWS + ¦æ¼Æ¦C¼Æ + TNCH_IAC + TNCH_SE;
+#endif
+
+ /* ask client to report it's term size */
+ static char svr[] = /* server */
+ {
+ IAC, DO, TELOPT_NAWS
+ };
+
+ int rset;
+ char buf[64], *rcv;
+struct timeval to;
+
+ /* °Ý¹ï¤è (telnet client) ¦³¨S¦³¤ä´©¤£¦Pªº¿Ã¹õ¼e°ª */
+ send(0, svr, 3, 0);
+
+ rset = 1;
+ to.tv_sec = 1;
+ to.tv_usec = 1;
+ if (select(1, (fd_set *) & rset, NULL, NULL, &to) > 0)
+ recv(0, buf, sizeof(buf), 0);
+
+ rcv = NULL;
+ if ((uschar) buf[0] == IAC && buf[2] == TELOPT_NAWS)
+ {
+ /* gslin: Unix ªº telnet ¹ï¦³µL¥[ port °Ñ¼Æªº¦æ¬°¤£¤Ó¤@¼Ë */
+ if ((uschar) buf[1] == SB)
+ {
+ rcv = buf + 3;
+ }
+ else if ((uschar) buf[1] == WILL)
+ {
+ if ((uschar) buf[3] != IAC)
+ {
+ rset = 1;
+ to.tv_sec = 1;
+ to.tv_usec = 1;
+ if (select(1, (fd_set *) & rset, NULL, NULL, &to) > 0)
+ recv(0, buf + 3, sizeof(buf) - 3, 0);
+ }
+ if ((uschar) buf[3] == IAC && (uschar) buf[4] == SB && buf[5] == TELOPT_NAWS)
+ rcv = buf + 6;
+ }
+ }
+
+ if (rcv)
+ {
+ b_lines = ntohs(* (short *) (rcv + 2)) - 1;
+ b_cols = ntohs(* (short *) rcv) - 1;
+
+ /* b_lines ¦Ü¤Ö­n 23¡A³Ì¦h¤£¯à¶W¹L T_LINES - 1 */
+ if (b_lines >= T_LINES)
+ b_lines = T_LINES - 1;
+ else if (b_lines < 23)
+ b_lines = 23;
+ /* b_cols ¦Ü¤Ö­n 79¡A³Ì¦h¤£¯à¶W¹L T_COLS - 1 */
+ if (b_cols >= T_COLS)
+ b_cols = T_COLS - 1;
+ else if (b_cols < 79)
+ b_cols = 79;
+ }
+ else
+ {
+ b_lines = 23;
+ b_cols = 79;
+ }
+
+ d_cols = b_cols - 79;
+}
+
+
+/* ----------------------------------------------------- */
+/* stand-alone daemon */
+/* ----------------------------------------------------- */
+
+
+ static void
+start_daemon(port)
+ int port; /* Thor.981206: ¨ú 0 ¥Nªí *¨S¦³°Ñ¼Æ* , -1 ¥Nªí -i (inetd) */
+{
+ int n;
+ struct linger ld;
+ struct sockaddr_in sin;
+#ifdef HAVE_RLIMIT
+ struct rlimit limit;
+#endif
+ char buf[80], data[80];
+ time_t val;
+
+ /*
+ * 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(&val);
+ strftime(buf, 80, "%d/%b/%Y %H:%M:%S", localtime(&val));
+
+#ifdef HAVE_RLIMIT
+ /* --------------------------------------------------- */
+ /* adjust resource : 16 mega is enough */
+ /* --------------------------------------------------- */
+
+ limit.rlim_cur = limit.rlim_max = 16 * 1024 * 1024;
+ /* setrlimit(RLIMIT_FSIZE, &limit); */
+ setrlimit(RLIMIT_DATA, &limit);
+
+#ifdef SOLARIS
+#define RLIMIT_RSS RLIMIT_AS /* Thor.981206: port for solaris 2.6 */
+#endif
+
+ setrlimit(RLIMIT_RSS, &limit);
+
+ limit.rlim_cur = limit.rlim_max = 0;
+ setrlimit(RLIMIT_CORE, &limit);
+
+ limit.rlim_cur = limit.rlim_max = 60 * 20;
+ setrlimit(RLIMIT_CPU, &limit);
+#endif
+
+ /* --------------------------------------------------- */
+ /* speed-hacking DNS resolve */
+ /* --------------------------------------------------- */
+
+ dns_init();
+
+ /* --------------------------------------------------- */
+ /* change directory to bbshome */
+ /* --------------------------------------------------- */
+
+ chdir(BBSHOME);
+ umask(077);
+
+ /* --------------------------------------------------- */
+ /* detach daemon process */
+ /* --------------------------------------------------- */
+
+ /* The integer file descriptors associated with the streams
+ stdin, stdout, and stderr are 0,1, and 2, respectively. */
+
+ close(1);
+ close(2);
+
+ if (port == -1) /* Thor.981206: inetd -i */
+ {
+ /* Give up root privileges: no way back from here */
+ setgid(BBSGID);
+ setuid(BBSUID);
+#if 1
+ n = sizeof(sin);
+ if (getsockname(0, (struct sockaddr *) &sin, &n) >= 0)
+ port = ntohs(sin.sin_port);
+#endif
+ /* mport = port; */ /* Thor.990325: ¤£»Ý­n¤F:P */
+
+ sprintf(data, "%d\t%s\t%d\tinetd -i\n", getpid(), buf, port);
+ f_cat(PID_FILE, data);
+ return;
+ }
+
+ close(0);
+
+ if (fork())
+ exit(0);
+
+ setsid();
+
+ if (fork())
+ exit(0);
+
+ /* --------------------------------------------------- */
+ /* fork daemon process */
+ /* --------------------------------------------------- */
+
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = INADDR_ANY;
+
+ if (port == 0) /* Thor.981206: port 0 ¥Nªí¨S¦³°Ñ¼Æ */
+ {
+ n = MAX_BBSDPORT - 1;
+ while (n)
+ {
+ if (fork() == 0)
+ break;
+
+ sleep(1);
+ n--;
+ }
+ port = myports[n];
+ }
+
+ n = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+
+ val = 1;
+ setsockopt(n, SOL_SOCKET, SO_REUSEADDR, (char *) &val, sizeof(val));
+
+ ld.l_onoff = ld.l_linger = 0;
+ setsockopt(n, SOL_SOCKET, SO_LINGER, (char *) &ld, sizeof(ld));
+
+ /* mport = port; */ /* Thor.990325: ¤£»Ý­n¤F:P */
+ sin.sin_port = htons(port);
+ if ((bind(n, (struct sockaddr *) &sin, sizeof(sin)) < 0) || (listen(n, QLEN) < 0))
+ exit(1);
+
+ /* --------------------------------------------------- */
+ /* Give up root privileges: no way back from here */
+ /* --------------------------------------------------- */
+
+ setgid(BBSGID);
+ setuid(BBSUID);
+
+ /* standalone */
+ sprintf(data, "%d\t%s\t%d\n", getpid(), buf, port);
+ f_cat(PID_FILE, data);
+}
+
+
+/* ----------------------------------------------------- */
+/* reaper - clean up zombie children */
+/* ----------------------------------------------------- */
+
+
+ static inline void
+reaper()
+{
+ while (waitpid(-1, NULL, WNOHANG | WUNTRACED) > 0);
+}
+
+
+#ifdef SERVER_USAGE
+ static void
+servo_usage()
+{
+ struct rusage ru;
+ FILE *fp;
+
+ fp = fopen("run/bbs.usage", "a");
+
+ if (!getrusage(RUSAGE_CHILDREN, &ru))
+ {
+ fprintf(fp, "\n[Server Usage] %d: %d\n\n"
+ "user time: %.6f\n"
+ "system time: %.6f\n"
+ "maximum resident set size: %lu P\n"
+ "integral resident set size: %lu\n"
+ "page faults not requiring physical I/O: %d\n"
+ "page faults requiring physical I/O: %d\n"
+ "swaps: %d\n"
+ "block input operations: %d\n"
+ "block output operations: %d\n"
+ "messages sent: %d\n"
+ "messages received: %d\n"
+ "signals received: %d\n"
+ "voluntary context switches: %d\n"
+ "involuntary context switches: %d\n\n",
+
+ getpid(), ap_start,
+ (double) ru.ru_utime.tv_sec + (double) ru.ru_utime.tv_usec / 1000000.0,
+ (double) ru.ru_stime.tv_sec + (double) ru.ru_stime.tv_usec / 1000000.0,
+ ru.ru_maxrss,
+ ru.ru_idrss,
+ ru.ru_minflt,
+ ru.ru_majflt,
+ ru.ru_nswap,
+ ru.ru_inblock,
+ ru.ru_oublock,
+ ru.ru_msgsnd,
+ ru.ru_msgrcv,
+ ru.ru_nsignals,
+ ru.ru_nvcsw,
+ ru.ru_nivcsw);
+ }
+
+ fclose(fp);
+}
+#endif
+
+
+ static void
+main_term()
+{
+#ifdef SERVER_USAGE
+ servo_usage();
+#endif
+ exit(0);
+}
+
+
+ static inline void
+main_signals()
+{
+ struct sigaction act;
+
+ /* act.sa_mask = 0; */ /* Thor.981105: ¼Ð·Ç¥Îªk */
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+
+ act.sa_handler = reaper;
+ sigaction(SIGCHLD, &act, NULL);
+
+ act.sa_handler = main_term;
+ sigaction(SIGTERM, &act, NULL);
+
+#ifdef SERVER_USAGE
+ act.sa_handler = servo_usage;
+ sigaction(SIGPROF, &act, NULL);
+#endif
+
+ /* sigblock(sigmask(SIGPIPE)); */
+}
+
+
+ int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int csock; /* socket for Master and Child */
+ int value;
+ int *totaluser;
+ struct sockaddr_in sin;
+
+ /* --------------------------------------------------- */
+ /* setup standalone daemon */
+ /* --------------------------------------------------- */
+
+ /* Thor.990325: usage, bbsd, or bbsd -i, or bbsd 1234 */
+ /* Thor.981206: ¨ú 0 ¥Nªí *¨S¦³°Ñ¼Æ*, -1 ¥Nªí -i */
+ start_daemon(argc > 1 ? strcmp("-i", argv[1]) ? atoi(argv[1]) : -1 : 0);
+
+ main_signals();
+
+ /* --------------------------------------------------- */
+ /* attach shared memory & semaphore */
+ /* --------------------------------------------------- */
+
+#ifdef HAVE_SEM
+ sem_init();
+#endif
+ ushm_init();
+ bshm_init();
+ fshm_init();
+
+ /* --------------------------------------------------- */
+ /* main loop */
+ /* --------------------------------------------------- */
+
+ totaluser = &ushm->count;
+ /* avgload = &ushm->avgload; */
+
+ for (;;)
+ {
+ value = 1;
+ if (select(1, (fd_set *) & value, NULL, NULL, NULL) < 0)
+ continue;
+
+ value = sizeof(sin);
+ csock = accept(0, (struct sockaddr *) &sin, &value);
+ if (csock < 0)
+ {
+ reaper();
+ continue;
+ }
+
+ ap_start++;
+ argc = *totaluser;
+ if (argc >= MAXACTIVE - 5 /* || *avgload > THRESHOLD */ )
+ {
+ /* ­É¥Î currtitle */
+ sprintf(currtitle, "¥Ø«e½u¤W¤H¼Æ [%d] ¤H¡A¨t²Î¹¡©M¡A½Ðµy«á¦A¨Ó\n", argc);
+ send(csock, currtitle, strlen(currtitle), 0);
+ close(csock);
+ continue;
+ }
+
+ if (fork())
+ {
+ close(csock);
+ continue;
+ }
+
+ dup2(csock, 0);
+ close(csock);
+
+ /* ------------------------------------------------- */
+ /* ident remote host / user name via RFC931 */
+ /* ------------------------------------------------- */
+
+ tn_addr = sin.sin_addr.s_addr;
+ dns_name((char *) &sin.sin_addr, fromhost);
+
+ telnet_init();
+ term_init();
+ tn_main();
+ }
+}
diff --git a/maple/bmw.c b/maple/bmw.c
new file mode 100644
index 0000000..04a2e9b
--- /dev/null
+++ b/maple/bmw.c
@@ -0,0 +1,1195 @@
+/*-------------------------------------------------------*/
+/* bmw.c ( NTHU CS MapleBBS Ver 3.00 ) */
+/*-------------------------------------------------------*/
+/* target : bmw routines */
+/* create : 95/03/29 */
+/* update : 97/03/29 */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+
+extern UCACHE *ushm;
+extern XZ xz[];
+extern char xo_pool[];
+
+
+/* ----------------------------------------------------- */
+/* BMW : bbs message write routines */
+/* ----------------------------------------------------- */
+
+
+#define BMW_FORMAT "\033[1;33;46m¡¹%s \033[37;45m %s \033[m" /* ¦¬¨ìªº¤ô²y */
+#define BMW_FORMAT2 "\033[1;33;41m¡¸%s \033[34;47m %s \033[m" /* °e¥Xªº¤ô²y */
+#define BMW_FORMAT3 "\033[1;33;45m¡¹%s \033[34;47m %s \033[m" /* ¦¬¨ìªº¼s¼½ */
+#define BMW_FORMAT4 "\033[1;33;44m¡¹%s \033[34;47m %s \033[m" /* Robot ¤ô²y */
+
+static int bmw_locus = 0; /* Á`¦@«O¦s´X­Ó¤ô²y («O¯d³Ìªñ¦¬¨ì BMW_LOCAL_MAX ­Ó) */
+static BMW bmw_lslot[BMW_LOCAL_MAX]; /* «O¯d¦¬¨ìªº¤ô²y */
+
+static int bmw_locat = 0; /* Á`¦@«O¦s´X­Ó¤ô²y («O¯d³Ìªñ°e¥X BMW_LOCAL_MAX ­Ó) */
+static BMW bmw_lword[BMW_LOCAL_MAX]; /* «O¯d°e¥Xªº¤ô²y */
+
+
+int /* 1:¥i¥H¶Ç¤ô²yµ¹¹ï¤è/»P¹ï¤èTalk 0:¤£¯à¶Ç¤ô²yµ¹¹ï¤è/»P¹ï¤èTalk */
+can_override(up)
+ UTMP *up;
+{
+ int ufo;
+
+ if (up->userno == cuser.userno) /* ¤£¯à¶Ç¤ô²yµ¹¦Û¤v(§Y¨Ï¬O¤À¨­) */
+ return 0;
+
+ ufo = up->ufo;
+
+#ifdef HAVE_SUPERCLOAK
+ if ((ufo & UFO_SUPERCLOAK) && !(cuser.ufo & UFO_SUPERCLOAK)) /* µµÁô¥u¦³µµÁôªº¤~¬Ýªº¨£ */
+ return 0;
+#endif
+
+ /* itoc.010909.µù¸Ñ: ¯¸ªø¥i¥H¶Ç¤ô²yµ¹ Âê©w/BBSNET... ªº¤H¡A³o¼Ë¦n¶Ü¡HÆ[¹î¤¤ */
+
+ if (HAS_PERM(PERM_ALLACCT)) /* ¯¸ªø¡B±b¸¹ºÞ²z­û¥i¥H¶Çµ¹¥ô¦ó¤H */
+ return 1;
+
+ /* itoc.010909: Âê©w®É¤£¯à³Q¶Ç¤ô²y */
+ if ((ufo & UFO_QUIET) || (up->status & STATUS_REJECT)) /* »·Â÷¹ÐÄÛ/Âê©w®É ¤£¯à³Q¶Ç */
+ return 0;
+
+ if (!(up->ufo & UFO_CLOAK) || HAS_PERM(PERM_SEECLOAK))
+ {
+ /* itoc.001223: ¥Î is_ogood/is_obad ¨Ó°µ§PÂ_ */
+ if (ufo & UFO_PAGER)
+ return is_ogood(up); /* pager Ãö³¬®É¥u¦³³Q³]¦n¤Í¯à¶Ç¤ô²y */
+ else
+ return !is_obad(up); /* pager ¥´¶}®É¥u­n¨S¦³³Q³]Ãa¤H§Y¥i¶Ç¤ô²y */
+ }
+ else
+ {
+ /* itoc.020321: ¹ï¤è­YÁô§Î¶Ç§Ú¤ô²y¡A§Ú¤]¥i¥H³Q°Ê¦^ */
+ BMW *bmw;
+
+ for (ufo = bmw_locus - 1; ufo >= 0; ufo--)
+ {
+ bmw = &bmw_lslot[ufo];
+
+ /* itoc.030718: ¦pªG§Ú­«·s¤W¯¸¤F¡A¨º»ò§Y¨Ï§Ú¤W¤@¦¸¤W¯¸¦³¥á¹ï¤è¤ô²y¡A¹ï¤è¤]¤£¥i¥H¦^§Ú
+ ¤£¹L³oÀˬdÁÙ¬O¦³­Óº|¬}¡A´N¬O¦pªG­«·s¤W¯¸¥H«á¤S­è¦n§¤¦P¤@­Ó ushm ªº¦ì¸m¡A¨º»ò¹ï¤èÁÙ¬O¥i¥H¦^§Ú */
+ if (bmw->caller == up && bmw->sender == up->userno)
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+
+int /* 1:¥i¬Ý¨£ 0:¤£¥i¬Ý¨£ */
+can_see(my, up)
+ UTMP *my;
+ UTMP *up;
+{
+ usint mylevel, myufo, urufo;
+
+ if (my->userno == up->userno)
+ return 1;
+ if (my == cutmp) /* ¥Î cuser. ¨Ó¥N´À cutmp-> */
+ {
+ mylevel = cuser.userlevel;
+ myufo = cuser.ufo;
+ }
+ else
+ {
+ mylevel = my->userlevel;
+ myufo = my->ufo;
+ }
+ urufo = up->ufo;
+
+ if ((urufo & UFO_CLOAK) && !(mylevel & PERM_SEECLOAK))
+ return 0;
+
+#ifdef HAVE_SUPERCLOAK
+ if ((urufo & UFO_SUPERCLOAK) && !(myufo & UFO_SUPERCLOAK))
+ return 0;
+#endif
+
+#ifdef HAVE_BADPAL
+ if (my == cutmp) /* Àˬd§Ú¥i¤£¥i¥H¬Ý¨ì¹ï¤è */
+ {
+ if (!(mylevel & PERM_SEECLOAK) && is_obad(up))
+ return 0;
+ }
+ else /* Àˬd¹ï¤è¥i¤£¥i¥H¬Ý¨ì§Ú */
+ {
+ if (!(mylevel & PERM_SEECLOAK) && is_mybad(my->userno))
+ return 0;
+ }
+#endif
+
+ return 1;
+}
+
+
+int
+bmw_send(callee, bmw)
+ UTMP *callee;
+ BMW *bmw;
+{
+ BMW *mpool, *mhead, *mtail, **mslot;
+ int i;
+ pid_t pid;
+ time_t texpire;
+
+ if ((callee->userno != bmw->recver) || (pid = callee->pid) <= 0)
+ return 1;
+
+ /* sem_lock(BSEM_ENTER); */
+
+ /* find callee's available slot */
+
+ mslot = callee->mslot;
+ i = 0;
+
+ for (;;)
+ {
+ if (mslot[i] == NULL)
+ break;
+
+ if (++i >= BMW_PER_USER)
+ {
+ /* sem_lock(BSEM_LEAVE); */
+ return 1;
+ }
+ }
+
+ /* find available BMW slot in pool */
+
+ texpire = time(&bmw->btime) - BMW_EXPIRE;
+
+ mpool = ushm->mpool;
+ mhead = ushm->mbase;
+ if (mhead < mpool)
+ mhead = mpool;
+ mtail = mpool + BMW_MAX;
+
+ do
+ {
+ if (++mhead >= mtail)
+ mhead = mpool;
+ } while (mhead->btime > texpire);
+
+ *mhead = *bmw;
+ ushm->mbase = mslot[i] = mhead;
+ /* Thor.981206: »Ýª`·N, ­Yushm mapping¤£¦P,
+ «h¤£¦P°¦ bbsd ¤¬call·|core dump,
+ °£«D³o¤]¥Îoffset, ¤£¹L°£¤F -i, À³¸Ó¬O«D¥²­n */
+
+
+ /* sem_lock(BSEM_LEAVE); */
+ return kill(pid, SIGUSR2);
+}
+
+
+#ifdef BMW_DISPLAY
+static void
+bmw_display(max) /* itoc.010313: display ¥H«eªº¤ô²y */
+ int max;
+{
+ int i;
+ BMW *bmw;
+
+ move(1, 0);
+ clrtoeol();
+ outs("\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\033[37;44m [Ctrl-T]©¹¤W¤Á´« \033[36;40m¢w¢w¢w¢w¢w¢w¢¡\033[m");
+
+ i = 2;
+ for (; max >= 0; max--)
+ { /* ±q¸û·sªº¤ô²y©¹¤U¦L */
+ bmw = &bmw_lslot[max];
+ move(i, 0);
+ clrtoeol();
+ prints(" " BMW_FORMAT, bmw->userid, bmw->msg);
+ i++;
+ }
+
+ move(i, 0);
+ clrtoeol();
+ outs("\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\033[37;44m [Ctrl-R]©¹¤U¤Á´« \033[36;40m¢w¢w¢w¢w¢w¢w¢£\033[m");
+}
+#endif
+
+
+static int bmw_pos; /* ¥Ø«e«ü¦V bmw_lslot ªº­þ¤@Äæ */
+static UTMP *bmw_up; /* ¥Ø«e¦^­þ­Ó utmp */
+static int bmw_request; /* 1: ¦³·sªº¤ô²y¶i¨Ó */
+
+
+void
+bmw_edit(up, hint, bmw)
+ UTMP *up; /* °eªº¹ï¶H¡A­Y¬O NULL ªí¥Ü¼s¼½ */
+ char *hint;
+ BMW *bmw;
+{
+ int recver;
+ screenline slp[3];
+ char *userid, fpath[64];
+ FILE *fp;
+
+ if (bbsmode != M_BMW_REPLY) /* ­Y¬O reply ªº¸Ü¡A¦b bmw_reply() ·|¦Û¦æ³B²zµe­±­«Ã¸ */
+ save_foot(slp);
+
+ recver = up ? up->userno : 0;
+ bmw->msg[0] = '\0';
+
+ for (;;)
+ {
+ int ch;
+ BMW *benz;
+
+ ch = vget(0, 0, hint, bmw->msg, 62, GCARRY);
+
+ if (!ch) /* ¨S¿é¤JªF¦è */
+ {
+ if (bbsmode != M_BMW_REPLY)
+ restore_foot(slp, 1);
+ return;
+ }
+
+ if (ch != Ctrl('R') && ch != Ctrl('T')) /* §¹¦¨¤ô²y¿é¤J */
+ break;
+
+ /* ¦³·sªº¤ô²y¶i¨Ó¡A­«Ã¸¤ô²y¦^ÅU¡A¨Ã±N bmw_pos «ü¦V­ì¨Ó¨º­Ó¤ô²y */
+ if (bmw_request)
+ {
+ bmw_request = 0;
+ bmw_pos = bmw_locus - 1;
+ if (cuser.ufo & UFO_BMWDISPLAY)
+ bmw_display(bmw_pos);
+ bmw_reply_CtrlRT(ch);
+ continue;
+ }
+
+ /* ¦b vget ¤¤«ö ^R ´« reply §Oªº¤ô²y */
+ benz = &bmw_lslot[bmw_pos];
+ if (benz->sender != up->userno) /* reply ¤£¦P¤H */
+ {
+ up = bmw_up;
+ recver = up->userno;
+ sprintf(hint, "¡¹[%s]", up->userid);
+ }
+ }
+
+ sprintf(fpath, "½T©w­n°e¥X¡m¤ô²y¡nµ¹ %s ¶Ü(Y/N)¡H[Y] ", up ? up->userid : " ¼s¼½ ");
+ if (vans(fpath) != 'n')
+ {
+ int i;
+
+ bmw->caller = cutmp;
+ bmw->sender = cuser.userno;
+ userid = cuser.userid;
+
+ if (up) /* ¤£¬O¼s¼½ */
+ {
+ /* °e¥X¤ô²y */
+ bmw->recver = recver;
+ strcpy(bmw->userid, userid);
+ if (bmw_send(up, bmw)) /* ¤ô²y°e¤£¥X¥h¡A¤£¼g¤J¤ô²y¬ö¿ýÀÉ */
+ {
+ vmsg(MSG_USR_LEFT);
+ if (bbsmode != M_BMW_REPLY)
+ restore_foot(slp, 2);
+ return;
+ }
+
+ /* lkchu.990103: ­Y¬O¦Û¤v°e¥Xªº¤ô²y¡A¦s¹ï¤èªº userid */
+ strcpy(bmw->userid, up->userid);
+ }
+ else /* ¼s¼½ */
+ {
+ /* °e¥X¼s¼½ªºµ{¦¡¡A¦b ulist_broadcast() ³B²z */
+
+ bmw->recver = 0; /* ¦s 0 ¨Ï¤£¯à write ¦^¼s¼½ */
+
+ /* itoc.000213: ¥[ "> " ¬°¤F»P¤@¯ë¤ô²y°Ï¤À */
+ sprintf(bmw->userid, "%s> ", cuser.userid);
+ }
+
+ time(&bmw->btime);
+ usr_fpath(fpath, userid, fn_bmw);
+ rec_add(fpath, bmw, sizeof(BMW));
+
+ /* itoc.020126: ¥[¤J FN_AMW */
+ usr_fpath(fpath, userid, fn_amw);
+ if (fp = fopen(fpath, "a"))
+ {
+ fprintf(fp, BMW_FORMAT2 " %s\n", bmw->userid, bmw->msg, Btime(&bmw->btime));
+ fclose(fp);
+ }
+
+ /* itoc.030621: «O¯d°e¥Xªº¤ô²y */
+ if (bmw_locat >= BMW_LOCAL_MAX)
+ {
+ /* ªº©¹«e®¿ */
+ i = BMW_LOCAL_MAX - 1;
+ memcpy(bmw_lword, bmw_lword + 1, i * sizeof(BMW));
+ }
+ else
+ {
+ i = bmw_locat;
+ bmw_locat++;
+ }
+ bmw_lword[i].recver = recver;
+ strcpy(bmw_lword[i].msg, bmw->msg);
+ }
+
+ if (bbsmode != M_BMW_REPLY)
+ restore_foot(slp, 2);
+}
+
+static void
+bmw_npcAI(msg)
+ char *msg;
+{
+ /* input msg ¬O¨Ï¥ÎªÌ©Ò¿é¤J­nµ¹ guest ªº¤ô²y */
+ /* output ¤]¥Î msg¡A¬O NPC ¦^µ¹ cuser.userid ªº¤ô²y */
+ int find;
+ FILE *fp;
+ char rule[80], ans[80], *str;
+
+ find = 0;
+ if (fp = fopen("etc/npc", "r"))
+ {
+ while (fgets(rule, 80, fp) && fgets(ans, 80, fp)) /* ¤@¦¸ fget ¤G¦æ */
+ {
+ if (str = strchr(rule, '\n'))
+ *str = '\0';
+ if (strstr(msg, rule))
+ {
+ if (str = strchr(ans, '\n'))
+ *str = '\0';
+ str_ncpy(msg, ans, 60);
+ find = 1;
+ break;
+ }
+ }
+
+ fclose(fp);
+ }
+
+ if (!find) /* ¦pªG rule ¸Ì­±¨S¦³¡A´NÀH«K¦^µª */
+ strcpy(msg, "¤°»ò¤°»ò¡H¦Aµ¹§Ú»¡¤@¦¸¨Óťť = =+");
+}
+
+
+
+
+static void
+bmw_outz()
+{
+ int i;
+ BMW *bmw, *benz;
+
+ /* ¦C¦Lªº¦ì¸m­n©M save/restore_foot ©Ò­«Ã¸ªº³¡¤À¬O¬Û¦Pªº */
+
+ bmw = &bmw_lslot[bmw_pos];
+ move(b_lines, 0);
+ clrtoeol();
+ prints(BMW_FORMAT, bmw->userid, bmw->msg);
+
+ /* itoc.030621: ¥Ñ«O¯dªº°e¥X¤ô²y¤¤¡A§ä¥X¤W¦¸¦^³o¤Hªº¤ô²y¬O¤°»ò */
+ for (i = bmw_locat; i >= 0; i--)
+ {
+ benz = &bmw_lword[i];
+ if (benz->recver == bmw->sender)
+ break;
+ }
+ move(b_lines - 1, 0);
+ clrtoeol();
+ prints(BMW_FORMAT2, bmw->userid, i >= 0 ? benz->msg : "¡i±z³Ìªñ¨S¦³¶Ç¤ô²yµ¹³o¦ì¨Ï¥ÎªÌ¡j");
+}
+
+
+static UTMP *
+can_reply(uhead, pos)
+ UTMP *uhead;
+ int pos;
+{
+ int userno;
+ BMW *bmw;
+ UTMP *up;
+
+ bmw = &bmw_lslot[pos];
+
+ userno = bmw->sender;
+ if (!userno) /* Thor.980805: ¨¾¤î¨t²Î¨ó´M¦^¦© */
+ return NULL;
+
+ up = bmw->caller;
+ if ((up < uhead) || (up > uhead + ushm->offset) || (up->userno != userno))
+ {
+ /* ¦pªG up-> ¤£¦b ushm ¤º¡A©Î¬O up-> ¤£¬O call-in §Úªº¤H¡Aªí¥Ü³o¤H¤U¯¸¤F¡A
+ ¦ý¬O¥L¥i¯à¤S¤W¯¸©Î¦³ multi¡A©Ò¥H­«§ä¤@¦¸ */
+ if (!(up = utmp_find(userno))) /* ¦pªG¦A§ä¤@¦¸ÁÙ¬O¨S¦³ */
+ return NULL;
+ }
+
+ /* itoc.010909: ¥i¥H³Q°Ê¦^µ¹ Áô§Î/»·Â÷¹ÐÄÛ/Ãö³¬pager ªº¤H¡A¦ý¬O¤£¯à¦^µ¹Âê©wªº¤H */
+ if (bmw->caller != up || up->status & STATUS_REJECT)
+ return NULL;
+
+ return up;
+}
+
+
+static UTMP *
+bmw_lastslot(pos) /* §ä¥X³Ìªñ¤@­Ó¥i¥H¦^¤ô²yªº¹ï¶H */
+ int pos;
+{
+ int max, times;
+ UTMP *up, *uhead;
+
+ uhead = ushm->uslot;
+ max = bmw_locus - 1;
+
+ for (times = max; times >= 0; times--)
+ {
+ if (up = can_reply(uhead, pos))
+ {
+ bmw_pos = pos;
+ return up;
+ }
+
+ /* ©¹¤U´`Àô§ä¤@°é */
+ pos = (pos == 0) ? max : pos - 1;
+ }
+
+ return NULL;
+}
+
+
+static UTMP *
+bmw_firstslot(pos) /* §ä¥X³Ì»·¤@­Ó¥i¥H¦^¤ô²yªº¹ï¶H */
+ int pos;
+{
+ int max, times;
+ UTMP *up, *uhead;
+
+ uhead = ushm->uslot;
+ max = bmw_locus - 1;
+
+ for (times = max; times >= 0; times--)
+ {
+ if (up = can_reply(uhead, pos))
+ {
+ bmw_pos = pos;
+ return up;
+ }
+
+ /* ©¹¤W´`Àô§ä¤@°é */
+ pos = (pos == max) ? 0 : pos + 1;
+ }
+
+ return NULL;
+}
+
+
+int
+bmw_reply_CtrlRT(key)
+ int key;
+{
+ int max, pos;
+
+ max = bmw_locus - 1;
+ if (max == 0) /* ¨S¨ä¥Lªº¤ô²y¥i¥H¿ï */
+ return 0;
+
+ pos = bmw_pos; /* 廼 bmw_pos */
+
+ if (key == Ctrl('R'))
+ bmw_up = bmw_lastslot(pos == 0 ? max : pos - 1); /* ¥Ñ¥Ø«e©Ò¦b pos ©¹¤U§ä¤@­Ó¥i¥H¦^¤ô²yªº¹ï¶H */
+ else /* if (key == Ctrl('T')) */
+ bmw_up = bmw_firstslot(pos == max ? 0 : pos + 1); /* ¥Ñ¥Ø«e©Ò¦b pos ©¹¤W§ä¤@­Ó¥i¥H¦^¤ô²yªº¹ï¶H */
+
+ if (!bmw_up) /* §ä¤£¨ì§Oªº¤ô²y */
+ {
+ bmw_pos = pos;
+ return 0;
+ }
+
+#ifdef BMW_DISPLAY
+ if (cuser.ufo & UFO_BMWDISPLAY)
+ {
+ move(2 + max - pos, 0);
+ outc(' ');
+ move(2 + max - bmw_pos, 0);
+ outc('>');
+ }
+#endif
+
+ bmw_outz();
+ return 1;
+}
+
+
+void
+bmw_reply()
+{
+ int max, display, tmpmode;
+ char buf[128];
+ UTMP *up;
+ BMW bmw;
+#ifdef BMW_DISPLAY
+ screenline slt[T_LINES];
+#else
+ screenline slt[3];
+#endif
+
+ cursor_save();
+
+ max = bmw_locus - 1;
+ if (!(up = bmw_lastslot(max)))
+ {
+ save_foot(slt);
+ vmsg("¥ý«e¨ÃµL¤ô²y©I¥s¡A©Î¹ï¤è¬Ò¤w¤U¯¸");
+ restore_foot(slt, 2);
+ cursor_restore();
+ refresh();
+ return;
+ }
+
+ tmpmode = bbsmode; /* lkchu.981201: Àx¦s bbsmode */
+ utmp_mode(M_BMW_REPLY);
+
+#ifdef BMW_DISPLAY
+ display = cuser.ufo & UFO_BMWDISPLAY;
+ if (display)
+ {
+ vs_save(slt); /* itoc.010313: °O¿ý bmd_display ¤§«eªº screen */
+ bmw_display(max); /* itoc.010313: display ¥H«eªº¤ô²y */
+ move(2 + max - bmw_pos, 0);
+ outc('>');
+ bmw_request = 0;
+ }
+ else
+#endif
+ save_foot(slt);
+
+ bmw_outz();
+
+ sprintf(buf, "¡¹[%s]", up->userid);
+ bmw_edit(up, buf, &bmw);
+
+#ifdef BMW_DISPLAY
+ if (display)
+ {
+ cursor_restore();
+ vs_restore(slt); /* itoc.010313: ÁÙ­ì bmw_display ¤§«eªº screen */
+ }
+ else
+#endif
+ {
+ restore_foot(slt, 3); /* ¤w bmw_outz¡A­nÁÙ­ì¤T¦C */
+ cursor_restore();
+ refresh();
+ }
+
+ utmp_mode(tmpmode); /* lkchu.981201: ¦^´_ bbsmode */
+}
+
+
+void
+bmw_rqst()
+{
+ int i, j, userno, locus;
+ BMW bmw[BMW_PER_USER], *mptr, **mslot;
+ char fpath[64];
+
+ /* download BMW slot first */
+
+ i = j = 0;
+ userno = cuser.userno;
+ mslot = cutmp->mslot;
+
+ while (mptr = mslot[i])
+ {
+ mslot[i] = NULL;
+ if (mptr->recver == userno)
+ {
+ bmw[j++] = *mptr;
+ }
+ mptr->btime = 0;
+
+ if (++i >= BMW_PER_USER)
+ break;
+ }
+
+ /* process the request */
+
+ if (j)
+ {
+ char buf[128];
+ FILE *fp;
+
+ locus = bmw_locus;
+ i = locus + j - BMW_LOCAL_MAX;
+ if (i >= 0)
+ {
+ locus -= i;
+ memcpy(bmw_lslot, bmw_lslot + i, locus * sizeof(BMW));
+ }
+
+ /* itoc.020126: ¥[¤J FN_AMW */
+ usr_fpath(buf, cuser.userid, fn_amw);
+ fp = fopen(buf, "a");
+
+ i = 0;
+ do
+ {
+ mptr = &bmw[i];
+
+ /* lkchu.981230: §Q¥Î xover ¾ã¦X bmw */
+ usr_fpath(buf, cuser.userid, fn_bmw);
+ rec_add(buf, mptr, sizeof(BMW));
+
+ /* itoc.020126: ¥[¤J FN_AMW */
+ // fprintf(fp, BMW_FORMAT " %s\n", mptr->userid, mptr->msg, Btime(&mptr->btime));
+ fprintf(fp, mptr->userid[strlen(mptr->userid) - 2] != '>' ?
+ BMW_FORMAT " %s\n" : BMW_FORMAT3 " %s\n",
+ mptr->userid, mptr->msg, Btime(&(mptr->btime)));
+
+ bmw_lslot[locus++] = *mptr; /* structure copy */
+
+ /* ·í NPC ¦¬¨ì§O¤H¥á¤ô²y«á·|¦Û°Ê¦^¸Ü */
+ if (!strcmp(cuser.userid, "Robot")) /* Robot ªº ID ¬O Robot */
+ {
+ if (mptr->caller)
+ {
+ BMW new;
+
+ time(&new.btime);
+ new.caller = cutmp;
+ new.sender = cuser.userno;
+ new.recver = mptr->sender;
+ strcpy(new.userid, cuser.userid);
+ strcpy(new.msg, mptr->msg);
+ bmw_npcAI(new.msg);
+ bmw_send(mptr->caller, &new);
+
+ usr_fpath(fpath, cuser.userid, fn_bmw);
+ rec_add(fpath, &new, sizeof(BMW));
+
+ usr_fpath(fpath, cuser.userid, fn_amw);
+ if (fp = fopen(fpath, "a"))
+ {
+ fprintf(fp, BMW_FORMAT4 " %s\n",
+ new.userid, new.msg, Btime(&new.btime));
+ fclose(fp);
+ }
+ }
+ }
+
+ } while (++i < j);
+
+ fclose(fp);
+
+ bmw_locus = locus;
+ if (bbsmode == M_BMW_REPLY)
+ bmw_request = 1; /* ­n¨D§ó·s */
+
+ /* Thor.980827: ¬°¤F¨¾¤î¦C¦L¤@¥b(more)®É¤ô²y¦Ó«á¦C¦L¶W¹L½d³ò½ð¤H, ¬G¦s¤U´å¼Ð¦ì¸m */
+ cursor_save();
+
+ // sprintf(buf, BMW_FORMAT, mptr->userid, mptr->msg);
+ sprintf(buf, mptr->userid[strlen(mptr->userid) - 2] != '>' ?
+ BMW_FORMAT : BMW_FORMAT3, mptr->userid, mptr->msg);
+
+ outz(buf);
+
+ /* Thor.980827: ¬°¤F¨¾¤î¦C¦L¤@¥b(more)®É¤ô²y¦Ó«á¦C¦L¶W¹L½d³ò½ð¤H, ¬GÁÙ­ì´å¼Ð¦ì¸m */
+ cursor_restore();
+
+ refresh();
+ bell();
+
+#ifdef BMW_COUNT
+ /* itoc.010312: ¦h¤¤¤@­Ó¤ô²y */
+ cutmp->bmw_count++;
+#endif
+ }
+}
+
+
+void
+do_write(up)
+ UTMP *up;
+{
+ if (can_override(up))
+ {
+ BMW bmw;
+ char buf[20];
+
+ sprintf(buf, "¡¹[%s]", up->userid);
+ bmw_edit(up, buf, &bmw);
+ }
+}
+
+
+/* ----------------------------------------------------- */
+/* ¤ô²y¦Cªí: ¿ï³æ¦¡¾Þ§@¬É­±´y­z by lkchu */
+/* ----------------------------------------------------- */
+
+
+static void
+bmw_item(num, bmw)
+ int num;
+ BMW *bmw;
+{
+ struct tm *ptime = localtime(&bmw->btime);
+
+ if (bmw->sender == cuser.userno) /* °e¥Xªº¤ô²y */
+ {
+ prints("%6d%c\033[33m%-13s\033[36m%-*.*s\033[33m%02d:%02d\033[m\n",
+ num, tag_char(bmw->btime), bmw->userid, d_cols + 53, d_cols + 53, bmw->msg, ptime->tm_hour, ptime->tm_min);
+ }
+ else /* ¦¬¨ìªº¤ô²y */
+ {
+ prints("%6d%c%-13s\033[32m%-*.*s\033[m%02d:%02d\n",
+ num, tag_char(bmw->btime), bmw->userid, d_cols + 53, d_cols + 53, bmw->msg, ptime->tm_hour, ptime->tm_min);
+ }
+}
+
+
+static int
+bmw_body(xo)
+ XO *xo;
+{
+ BMW *bmw;
+ int num, max, tail;
+
+ max = xo->max;
+ if (max <= 0)
+ {
+ vmsg("¥ý«e¨ÃµL¤ô²y©I¥s");
+ return XO_QUIT;
+ }
+
+ bmw = (BMW *) xo_pool;
+ num = xo->top;
+ tail = num + XO_TALL;
+ if (max > tail)
+ max = tail;
+
+ move(3, 0);
+ do
+ {
+ bmw_item(++num, bmw++);
+ } while (num < max);
+ clrtobot();
+
+ /* return XO_NONE; */
+ return XO_FOOT; /* itoc.010403: §â b_lines ¶ñ¤W feeter */
+}
+
+
+static int
+bmw_head(xo)
+ XO *xo;
+{
+ vs_head("¹î¬Ý¤ô²y", str_site);
+ prints(NECKER_BMW, d_cols, "");
+ return bmw_body(xo);
+}
+
+
+static int
+bmw_load(xo)
+ XO *xo;
+{
+ xo_load(xo, sizeof(BMW));
+ return bmw_body(xo);
+}
+
+
+static int
+bmw_init(xo)
+ XO *xo;
+{
+ xo_load(xo, sizeof(BMW));
+ return bmw_head(xo);
+}
+
+
+static int
+bmw_delete(xo)
+ XO *xo;
+{
+ if (vans(msg_del_ny) == 'y')
+ {
+ if (!rec_del(xo->dir, sizeof(BMW), xo->pos, NULL))
+ return bmw_load(xo);
+ }
+
+ return XO_FOOT;
+}
+
+
+static int
+bmw_rangedel(xo) /* itoc.001126: ·s¼W¤ô²y°Ï¬q§R°£ */
+ XO *xo;
+{
+ return xo_rangedel(xo, sizeof(BMW), NULL, NULL);
+}
+
+
+static int
+vfybmw(bmw, pos)
+ BMW *bmw;
+ int pos;
+{
+ return Tagger(bmw->btime, pos, TAG_NIN);
+}
+
+
+static int
+bmw_prune(xo)
+ XO *xo;
+{
+ return xo_prune(xo, sizeof(BMW), vfybmw, NULL);
+}
+
+
+static int
+bmw_mail(xo)
+ XO *xo;
+{
+ BMW *bmw;
+ char *str, userid[IDLEN + 1];
+
+ bmw = (BMW *) xo_pool + (xo->pos - xo->top);
+ strcpy(userid, bmw->userid);
+ if (str = strchr(userid, '>')) /* ¼s¼½ */
+ *str = '\0';
+ return my_send(userid);
+}
+
+
+static int
+bmw_query(xo)
+ XO *xo;
+{
+ BMW *bmw;
+ char *str, userid[IDLEN + 1];
+
+ bmw = (BMW *) xo_pool + (xo->pos - xo->top);
+ move(1, 0);
+ clrtobot();
+ strcpy(userid, bmw->userid);
+ if (str = strchr(userid, '>')) /* ¼s¼½ */
+ *str = '\0';
+ my_query(userid);
+ return bmw_head(xo);
+}
+
+
+static int
+bmw_write(xo)
+ XO *xo;
+{
+ if (HAS_PERM(PERM_PAGE))
+ {
+ int userno;
+ UTMP *up;
+ BMW *bmw;
+
+ bmw = (BMW *) xo_pool + (xo->pos - xo->top);
+
+ /* itoc.010304: Åý¶Ç°Tªº bmw ¤]¥i¥H¦^ */
+ /* §Ú°e¤ô²yµ¹§O¤H¡A¦^µ¹¦¬°TªÌ¡F§O¤H°e¤ô²yµ¹§Ú¡A¦^µ¹°e°TªÌ */
+ userno = (bmw->sender == cuser.userno) ? bmw->recver : bmw->sender;
+ if (!userno)
+ return XO_NONE;
+
+ if (up = utmp_find(userno))
+ do_write(up);
+ }
+ return XO_NONE;
+}
+
+
+static void
+bmw_store(fpath)
+ char *fpath;
+{
+ int fd;
+ FILE *fp;
+ char buf[64], folder[64];
+ HDR fhdr;
+
+ /* itoc.020126.µù¸Ñ: ¥i¥Hª½±µ®³ FN_AMW ¨ÓÀx¦s§Y¥i¡A
+ ¥i¬O¦pªG¥Î FN_BMW ­«°µ¤@¦¸ªº¸Ü¡A¥i¥HÅý¨Ï¥ÎªÌ¦b t_bmw() ¤¤¦Û¥Ñ d ±¼¤£­nªº¤ô²y */
+
+ if ((fd = open(fpath, O_RDONLY)) < 0)
+ return;
+
+ usr_fpath(folder, cuser.userid, fn_dir);
+ if (fp = fdopen(hdr_stamp(folder, 0, &fhdr, buf), "w"))
+ {
+ BMW bmw;
+
+ fprintf(fp, " == ¤ô²y°O¿ý %s ==\n\n", Now());
+
+ while (read(fd, &bmw, sizeof(BMW)) == sizeof(BMW))
+ {
+ fprintf(fp, bmw.sender == cuser.userno ? BMW_FORMAT2 " %s\n" : BMW_FORMAT " %s\n",
+ bmw.userid, bmw.msg, Btime(&bmw.btime));
+ }
+ fclose(fp);
+ }
+
+ close(fd);
+
+ fhdr.xmode = MAIL_READ | MAIL_NOREPLY;
+ strcpy(fhdr.title, "[³Æ §Ñ ¿ý] ¤ô²y¬ö¿ý");
+ strcpy(fhdr.owner, cuser.userid);
+ rec_add(folder, &fhdr, sizeof(HDR));
+}
+
+
+static int
+bmw_save(xo)
+ XO *xo;
+{
+ if (vans("±z½T©w­n§â¤ô²y¦s¨ì«H½c¸Ì¶Ü(Y/N)¡H[N] ") == 'y')
+ {
+ char fpath[64];
+
+ usr_fpath(fpath, cuser.userid, fn_bmw);
+ bmw_store(fpath);
+ unlink(fpath);
+ usr_fpath(fpath, cuser.userid, fn_amw);
+ unlink(fpath);
+ return bmw_init(xo);
+ }
+ return XO_FOOT;
+}
+
+
+static int
+bmw_save_user(xo)
+ XO *xo;
+{
+ int fd;
+ FILE *fp;
+ char buf[64], folder[64];
+ HDR fhdr;
+ ACCT acct;
+
+ if (acct_get(msg_uid, &acct) > 0 && acct.userno != cuser.userno)
+ {
+ usr_fpath(buf, cuser.userid, fn_bmw);
+ if ((fd = open(buf, O_RDONLY)) >= 0)
+ {
+ usr_fpath(folder, cuser.userid, fn_dir);
+ if (fp = fdopen(hdr_stamp(folder, 0, &fhdr, buf), "w"))
+ {
+ BMW bmw;
+
+ fprintf(fp, " == »P %s ¥áªº¤ô²y¬ö¿ý %s ==\n\n", acct.userid, Now());
+
+ while (read(fd, &bmw, sizeof(BMW)) == sizeof(BMW))
+ {
+ if (bmw.sender == acct.userno || bmw.recver == acct.userno)
+ {
+ fprintf(fp, bmw.sender == cuser.userno ? BMW_FORMAT2 " %s\n" : BMW_FORMAT " %s\n",
+ bmw.userid, bmw.msg, Btime(&bmw.btime));
+ }
+ }
+ fclose(fp);
+ }
+ close(fd);
+
+ fhdr.xmode = MAIL_READ | MAIL_NOREPLY;
+ strcpy(fhdr.title, "[³Æ §Ñ ¿ý] ¤ô²y¬ö¿ý");
+ strcpy(fhdr.owner, cuser.userid);
+ rec_add(folder, &fhdr, sizeof(HDR));
+ vmsg("¤ô²y¬ö¿ý¤w±H¨ì«H½c");
+ }
+ }
+
+ return bmw_head(xo);
+}
+
+
+static int
+bmw_clear(xo)
+ XO *xo;
+{
+ if (vans("¬O§_§R°£©Ò¦³¤ô²y¬ö¿ý(Y/N)¡H[N] ") == 'y')
+ {
+ char fpath[64];
+
+ usr_fpath(fpath, cuser.userid, fn_bmw);
+ unlink(fpath);
+ usr_fpath(fpath, cuser.userid, fn_amw);
+ unlink(fpath);
+ return XO_QUIT;
+ }
+
+ return XO_FOOT;
+}
+
+
+static int
+bmw_tag(xo)
+ XO *xo;
+{
+ BMW *bmw;
+ int tag, pos, cur;
+
+ pos = xo->pos;
+ cur = pos - xo->top;
+ bmw = (BMW *) xo_pool + cur;
+
+ if (tag = Tagger(bmw->btime, pos, TAG_TOGGLE))
+ {
+ move(3 + cur, 6);
+ outc(tag > 0 ? '*' : ' ');
+ }
+
+ /* return XO_NONE; */
+ return xo->pos + 1 + XO_MOVE; /* lkchu.981201: ¸õ¦Ü¤U¤@¶µ */
+}
+
+
+static int
+bmw_help(xo)
+ XO *xo;
+{
+ xo_help("bmw");
+ return bmw_head(xo);
+}
+
+
+KeyFunc bmw_cb[] =
+{
+ XO_INIT, bmw_init,
+ XO_LOAD, bmw_load,
+ XO_HEAD, bmw_head,
+ XO_BODY, bmw_body,
+
+ 'd', bmw_delete,
+ 'D', bmw_rangedel,
+ 'm', bmw_mail,
+ 'w', bmw_write,
+ 'r', bmw_query,
+ Ctrl('Q'), bmw_query,
+ 's', bmw_init,
+ 'M', bmw_save,
+ 'u', bmw_save_user,
+ 't', bmw_tag,
+ Ctrl('D'), bmw_prune,
+ 'C', bmw_clear,
+
+ 'h', bmw_help
+};
+
+
+int
+t_bmw()
+{
+#if 0 /* itoc.010715: ¥Ñ©ó every_Z ­n¥Î¡A·h¥h talk_main ±`¾n */
+ XO *xo;
+ char fpath[64];
+
+ usr_fpath(fpath, cuser.userid, fn_bmw);
+ xz[XZ_BMW - XO_ZONE].xo = xo = xo_new(fpath);
+ xover(XZ_BMW);
+ free(xo);
+#endif
+
+ xover(XZ_BMW);
+ return 0;
+}
+
+
+int
+t_display() /* itoc.020126: display FN_AMW */
+{
+ char fpath[64];
+
+ usr_fpath(fpath, cuser.userid, fn_amw);
+ return more(fpath, NULL); /* Thor.990204: ¥u­n¤£¬O XEASY ´N¥i¥H reload menu ¤F */
+}
+
+
+#ifdef RETAIN_BMW
+static void
+bmw_retain(fpath)
+ char *fpath;
+{
+ char folder[64];
+ HDR fhdr;
+
+ usr_fpath(folder, str_sysop, fn_dir);
+ hdr_stamp(folder, HDR_COPY, &fhdr, fpath);
+ strcpy(fhdr.owner, cuser.userid);
+ strcpy(fhdr.title, "¤ô²y¦sÃÒ");
+ fhdr.xmode = 0;
+ rec_add(folder, &fhdr, sizeof(HDR));
+}
+#endif
+
+
+#ifdef LOG_BMW
+void
+bmw_log()
+{
+ int op;
+ char fpath[64], buf[64];
+ struct stat st;
+
+ /* lkchu.981201: ©ñ¶i¨p¤H«H½c¤º/²M°£/«O¯d */
+ usr_fpath(fpath, cuser.userid, fn_bmw);
+
+ if (!stat(fpath, &st) && S_ISREG(st.st_mode))
+ {
+ usr_fpath(buf, cuser.userid, fn_amw);
+
+ if ((cuser.ufo & UFO_NWLOG) || !st.st_size) /* itoc.000512: ¤£Àx¦s¤ô²y°O¿ý */
+ { /* itoc.020711: ¦pªG bmw size ¬O 0 ´N²M°£ */
+ op = 'c';
+ }
+ else
+ {
+ more(buf, (char *) -1);
+#ifdef RETAIN_BMW
+ op = vans("¥»¦¸¤W¯¸¤ô²y³B²z (M)²¾¦Ü³Æ§Ñ¿ý (R)«O¯d (C)²M°£ (S)¦sÃÒ¡H[R] ");
+#else
+ op = vans("¥»¦¸¤W¯¸¤ô²y³B²z (M)²¾¦Ü³Æ§Ñ¿ý (R)«O¯d (C)²M°£¡H[R] ");
+#endif
+ }
+
+ switch (op)
+ {
+ case 'm':
+ bmw_store(fpath);
+
+ case 'c':
+ unlink(fpath);
+ unlink(buf);
+ break;
+
+#ifdef RETAIN_BMW
+ case 's':
+ if (vans("¦sÃÒ¬O§â¤ô²yÂà±Hµ¹¯¸ªø¥HÀËÁ|¨ä¥L¨Ï¥ÎªÌ¡A±z½T©w­n¦sÃÒ¶Ü(Y/N)¡H[N] ") == 'y')
+ bmw_retain(buf); /* ¥Î¤£¯à§ïªº amw ¨Ó¦sÃÒ */
+ break;
+#endif
+
+ default:
+ break;
+ }
+ }
+}
+#endif
diff --git a/maple/board.c b/maple/board.c
new file mode 100644
index 0000000..817ae8f
--- /dev/null
+++ b/maple/board.c
@@ -0,0 +1,2259 @@
+/*-------------------------------------------------------*/
+/* board.c ( NTHU CS MapleBBS Ver 2.36 ) */
+/*-------------------------------------------------------*/
+/* target : ¬ÝªO¡B¸s²Õ¥\¯à */
+/* create : 95/03/29 */
+/* update : 95/12/15 */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+
+extern BCACHE *bshm;
+extern XZ xz[];
+extern char xo_pool[];
+
+
+char brd_bits[MAXBOARD];
+
+#ifndef ENHANCED_VISIT
+time_t brd_visit[MAXBOARD]; /* ³ÌªñÂsÄý®É¶¡ */
+#endif
+
+
+static char *class_img = NULL;
+static XO board_xo;
+
+/* ´M§ä¬ÝªO¨ç¦¡; °½¦Û daemon/bmtad.c -> brd_get () */
+/* ¥u¥Î©ó talk.c -> do_query () */
+BRD *
+has_personalbrd(bname)
+ char *bname;
+{
+ BRD *bhdr, *tail;
+
+ bhdr = bshm->bcache;
+ tail = bhdr + bshm->number;
+ do
+ {
+ if (!str_cmp(bname, bhdr->brdname))
+ return bhdr;
+ } while (++bhdr < tail);
+ return NULL;
+}
+
+
+/* ----------------------------------------------------- */
+/* ¬ÝªO¾\Ū°O¿ý .BRH (Board Reading History) */
+/* ----------------------------------------------------- */
+
+
+typedef struct BoardReadingHistory
+{
+ time_t bstamp; /* «Ø¥ß¬ÝªOªº®É¶¡, unique */ /* Thor.brh_tail */
+ time_t bvisit; /* ¤W¦¸¾\ۮɶ¡ */ /* Thor.980904: ¨S¦bۮɩñ¤W¦¸Åªªº®É¶¡, ¥¿¦bۮɩñ bhno */
+ int bcount; /* Thor.980902: ¨S¥Î¨ì */
+
+ /* --------------------------------------------------- */
+ /* time_t {final, begin} / {final | BRH_SIGN} */
+ /* --------------------------------------------------- */
+ /* Thor.980904.µù¸Ñ: BRH_SIGN¥Nªífinal begin ¬Û¦P */
+ /* Thor.980904.µù¸Ñ: ¥Ñ¤j¨ì¤p±Æ¦C,¦s©ñ¤wŪinterval */
+} BRH;
+
+
+#define BRH_EXPIRE 180 /* Thor.980902.µù¸Ñ: «O¯d¦h¤Ö¤Ñ */
+#define BRH_MAX 200 /* Thor.980902.µù¸Ñ: ¨CªO³Ì¦h¦³´X­Ó¼ÐÅÒ */
+#define BRH_PAGE 2048 /* Thor.980902.µù¸Ñ: ¨C¦¸¦h°t¶q, ¥Î¤£¨ì¤F */
+#define BRH_MASK 0x7fffffff /* Thor.980902.µù¸Ñ: ³Ì¤j¶q¬°2038¦~1¤ë¤¤*/
+#define BRH_SIGN 0x80000000 /* Thor.980902.µù¸Ñ: zap¤ÎÀ£final±M¥Î */
+#define BRH_WINDOW (sizeof(BRH) + sizeof(time_t) * BRH_MAX * 2)
+
+
+static int *brh_base; /* allocated memory */
+static int *brh_tail; /* allocated memory */
+static int brh_size; /* allocated memory size */
+static time_t brh_expire;
+
+
+static int *
+brh_alloc(tail, size)
+ int *tail;
+ int size;
+{
+ int *base, n;
+
+ base = brh_base;
+ n = (char *) tail - (char *) base;
+ size += n;
+ if (size > brh_size)
+ {
+ /* size = (size & -BRH_PAGE) + BRH_PAGE; */
+ size += n >> 4; /* ¦h¹w¬ù¤@¨Ç°O¾ÐÅé */
+ base = (int *) realloc((char *) base, size);
+
+ if (base == NULL)
+ abort_bbs();
+
+ brh_base = base;
+ brh_size = size;
+ tail = (int *) ((char *) base + n);
+ }
+
+ return tail;
+}
+
+
+static void
+brh_put()
+{
+ int *list;
+
+ /* compact the history list */
+
+ list = brh_tail;
+
+ if (*list)
+ {
+ int *head, *tail, n, item, chrono;
+
+ n = *++list; /* Thor.980904: ¥¿Åª®É¬Obhno */
+ brd_bits[n] |= BRD_H_BIT;
+ time((time_t *) list); /* Thor.980904.µù¸Ñ: bvisit time */
+
+ item = *++list;
+ head = ++list;
+ tail = head + item;
+
+ while (head < tail)
+ {
+ chrono = *head++;
+ n = *head++;
+ if (n == chrono) /* Thor.980904.µù¸Ñ: ¬Û¦Pªº®É­ÔÀ£°_¨Ó */
+ {
+ n |= BRH_SIGN;
+ item--;
+ }
+ else
+ {
+ *list++ = chrono;
+ }
+ *list++ = n;
+ }
+
+ list[-item - 1] = item;
+ *list = 0;
+ brh_tail = list; /* Thor.980904:·sªºªÅbrh */
+ }
+}
+
+
+void
+brh_get(bstamp, bhno)
+ time_t bstamp; /* board stamp */
+ int bhno;
+{
+ int *head, *tail;
+ int size, bcnt, item;
+ char buf[BRH_WINDOW];
+
+ if (bstamp == *brh_tail) /* Thor.980904.µù¸Ñ: ¸ÓªO¤w¦b brh_tail¤W */
+ return;
+
+ brh_put();
+
+ bcnt = 0;
+ tail = brh_tail;
+
+ if (brd_bits[bhno] & BRD_H_BIT)
+ {
+ head = brh_base;
+ while (head < tail)
+ {
+ item = head[2];
+ size = item * sizeof(time_t) + sizeof(BRH);
+
+ if (bstamp == *head)
+ {
+ bcnt = item;
+ memcpy(buf, head + 3, size);
+ tail = (int *) ((char *) tail - size);
+ if (item = (char *) tail - (char *) head)
+ memcpy(head, (char *) head + size, item);
+ break;
+ }
+ head = (int *) ((char *) head + size);
+ }
+ }
+
+ brh_tail = tail = brh_alloc(tail, BRH_WINDOW);
+
+ *tail++ = bstamp;
+ *tail++ = bhno;
+
+ if (bcnt) /* expand history list */
+ {
+ int *list;
+
+ size = bcnt;
+ list = tail;
+ head = (int *) buf;
+
+ do
+ {
+ item = *head++;
+ if (item & BRH_SIGN)
+ {
+ item ^= BRH_SIGN;
+ *++list = item;
+ bcnt++;
+ }
+ *++list = item;
+ } while (--size);
+ }
+
+ *tail = bcnt;
+}
+
+
+int
+brh_unread(chrono)
+ time_t chrono;
+{
+ int *head, *tail, item;
+
+ /* itoc.010407.µù¸Ñ: BRH_EXPIRE (180) ¤Ñ«eªº¤å³¹³£³]¬°¤wŪ */
+ if (chrono <= brh_expire)
+ return 0;
+
+ head = brh_tail + 2;
+ if ((item = *head) > 0)
+ {
+ /* check {final, begin} history list */
+
+ head++;
+ tail = head + item;
+ do
+ {
+ if (chrono > *head)
+ return 1;
+
+ head++;
+ if (chrono >= *head)
+ return 0;
+
+ } while (++head < tail);
+ }
+ return 1;
+}
+
+
+void
+brh_visit(mode)
+ int mode; /* 0 : visit, 1: un-visit */
+{ /* itoc.010207: ©Î¬O¶Ç¤Jchrono, ¥Nªíۦܭþ */
+ int *list;
+
+ list = (int *) brh_tail + 2;
+ *list++ = 2;
+ if (mode)
+ {
+ *list = mode;
+ }
+ else
+ {
+ time((time_t *)list);
+ }
+ /* *++list = mode; */
+ *++list = 0; /* itoc.010207: ±j©w¬° 0, for ³¡¤À visit */
+}
+
+
+int
+brh_add(prev, chrono, next)
+ time_t prev, chrono, next;
+{
+ int *base, *head, *tail, item, final, begin;
+
+ head = base = brh_tail + 2;
+ item = *head++;
+ tail = head + item;
+
+ begin = BRH_MASK;
+
+ while (head < tail)
+ {
+ final = *head;
+ if (chrono > final)
+ {
+ if (prev <= final)
+ {
+ if (next < begin) /* increase */
+ *head = chrono;
+ else
+ { /* merge */
+ *base = item - 2;
+ base = head - 1;
+ do
+ {
+ *base++ = *++head;
+ } while (head < tail);
+ }
+ return;
+ }
+
+ if (next >= begin)
+ {
+ head[-1] = chrono;
+ return;
+ }
+
+ break;
+ }
+
+ begin = *++head;
+ head++;
+ }
+
+ /* insert or append */
+
+ /* [21, 22, 23] ==> [32, 30] [15, 10] */
+
+ if (item < BRH_MAX)
+ {
+ /* [32, 30] [22, 22] [15, 10] */
+
+ *base = item + 2;
+ tail += 2;
+ }
+ else
+ {
+ /* [32, 30] [22, 10] */ /* Thor.980923: how about [6, 7, 8] ? [15, 7] ? */
+
+ tail--;
+ }
+
+ prev = chrono;
+ for (;;)
+ {
+ final = *head;
+ *head++ = chrono;
+
+ if (head >= tail)
+ return;
+
+ begin = *head;
+ *head++ = prev;
+
+ if (head >= tail)
+ return;
+
+ chrono = final;
+ prev = begin;
+ }
+}
+
+
+/* ----------------------------------------------------- */
+/* board permission check */
+/* ----------------------------------------------------- */
+
+
+int /* >=1:²Ä´X­ÓªO¥D 0:¤£¬OªO¥D */
+is_bm(list, userid)
+ char *list; /* ªO¥D¡GBM list */
+ char *userid;
+{
+ return str_has(list, userid, strlen(userid));
+}
+
+
+static inline int
+Ben_Perm(bno, ulevel)
+ int bno;
+ usint ulevel;
+{
+ usint readlevel, postlevel, bits;
+ char *blist, *bname;
+ BRD *brd;
+#ifdef HAVE_MODERATED_BOARD
+ BPAL *bpal;
+ int ftype; /* 0:¤@¯ëID 1:ªO¦n 2:ªOÃa */
+
+ /* itoc.040103: ¬ÝªO¾\Ūµ¥¯Å»¡©úªí
+
+ ¢z¢w¢w¢w¢w¢s¢w¢w¢w¢w¢s¢w¢w¢w¢w¢s¢w¢w¢w¢w¢{
+ ¢x ¢x¤@¯ë¥Î¤á¢x¬ÝªO¦n¤Í¢x¬ÝªOÃa¤H¢x
+ ¢u¢w¢w¢w¢w¢q¢w¢w¢w¢w¢q¢w¢w¢w¢w¢q¢w¢w¢w¢w¢t
+ ¢x¤@¯ë¬ÝªO¢xÅv­­¨M©w¢x §¹¾ã ¢x ¤ô±í ¢x ¬Ý¤£¨£¡G¦b¬ÝªO¦Cªí¤¤µLªk¬Ý¨ì³o­ÓªO¡A¤]¶i¤£¥h
+ ¢u¢w¢w¢w¢w¢q¢w¢w¢w¢w¢q¢w¢w¢w¢w¢q¢w¢w¢w¢w¢t ¶i¤£¥h¡G¦b¬ÝªO¦Cªí¤¤¥i¥H¬Ý¨ì³o­ÓªO¡A¦ý¬O¶i¤£¥h
+ ¢x¦n¤Í¬ÝªO¢x ¶i¤£¥h ¢x §¹¾ã ¢x ¤ô±í ¢x ¤ô ±í¡G¦b¬ÝªO¦Cªí¤¤¥i¥H¬Ý¨ì³o­ÓªO¡A¤]¶i±o¥h¡A¦ý¬O¤£¯àµo¤å
+ ¢u¢w¢w¢w¢w¢q¢w¢w¢w¢w¢q¢w¢w¢w¢w¢q¢w¢w¢w¢w¢t §¹ ¾ã¡G¦b¬ÝªO¦Cªí¤¤¥i¥H¬Ý¨ì³o­ÓªO¡A¤]¶i±o¥h¤Îµo¤å
+ ¢x¯µ±K¬ÝªO¢x ¬Ý¤£¨£ ¢x §¹¾ã ¢x ¤ô±í ¢x
+ ¢|¢w¢w¢w¢w¢r¢w¢w¢w¢w¢r¢w¢w¢w¢w¢r¢w¢w¢w¢w¢}
+ */
+
+ static int bit_data[9] =
+ { /* ¤@¯ë¥Î¤á ¬ÝªO¦n¤Í ¬ÝªOÃa¤H */
+ /* ¤½¶}¬ÝªO */ 0, BRD_L_BIT | BRD_R_BIT | BRD_W_BIT, BRD_L_BIT | BRD_R_BIT,
+ /* ¦n¤Í¬ÝªO */ BRD_L_BIT, BRD_L_BIT | BRD_R_BIT | BRD_W_BIT, BRD_L_BIT | BRD_R_BIT,
+ /* ¯µ±K¬ÝªO */ 0, BRD_L_BIT | BRD_R_BIT | BRD_W_BIT, BRD_L_BIT | BRD_R_BIT,
+ };
+#endif
+
+ brd = bshm->bcache + bno;
+ bname = brd->brdname;
+ if (!*bname)
+ return 0;
+
+ readlevel = brd->readlevel;
+
+#ifdef HAVE_MODERATED_BOARD
+ bpal = bshm->pcache + bno;
+ ftype = is_bgood(bpal) ? 1 : is_bbad(bpal) ? 2 : 0;
+
+ if (readlevel == PERM_SYSOP) /* ¯µ±K¬ÝªO */
+ bits = bit_data[6 + ftype];
+ else if (readlevel == PERM_BOARD) /* ¦n¤Í¬ÝªO */
+ bits = bit_data[3 + ftype];
+ else if (ftype) /* ¤½¶}¬ÝªO¡A­Y¦bªO¦n/ªOÃa¦W³æ¤¤ */
+ bits = bit_data[ftype];
+ else /* ¤½¶}¬ÝªO¡A¨ä¥L¨ÌÅv­­§P©w */
+#endif
+
+ if (!readlevel || (readlevel & ulevel))
+ {
+ bits = BRD_L_BIT | BRD_R_BIT;
+
+ postlevel = brd->postlevel;
+ if (!postlevel || (postlevel & ulevel))
+ /* ¬ÝªO¨S¦³³]©wthat½Ö¥i¥Hµo¤å || ¬ÝªO¦³³]©wthat½Ö¥i¥Hµo¤å¦Ó¥B·í«e¥ÎªÌ²Å¦X¦¹Åv­­ */
+ bits |= BRD_W_BIT;
+ }
+ else
+ {
+ bits = 0;
+ }
+
+ /* Thor.980813.µù¸Ñ: ¯S§O¬° BM ¦Ò¶q¡AªO¥D¦³¸ÓªOªº©Ò¦³Åv­­ */
+ blist = brd->BM;
+ if ((ulevel & PERM_BM) && blist[0] > ' ' && is_bm(blist, cuser.userid))
+ bits = BRD_L_BIT | BRD_R_BIT | BRD_W_BIT | BRD_X_BIT | BRD_M_BIT;
+
+ /* itoc.030515: ¬ÝªOÁ`ºÞ­«·s§PÂ_ */
+ if (ulevel & PERM_ALLBOARD)
+ {
+ /* chitsaou:060612: ¯¸°È¤£¯à¶i¤J¥¼¸g±ÂÅvªºªO */
+ bits |= BRD_L_BIT;
+
+ if (!strcmp(brd->class, "¯¸°È"))
+ bits = BRD_L_BIT | BRD_R_BIT | BRD_W_BIT | BRD_X_BIT | BRD_M_BIT;
+
+ }
+// bits = BRD_L_BIT | BRD_R_BIT | BRD_W_BIT | BRD_X_BIT;
+
+ return bits;
+}
+
+
+/* ----------------------------------------------------- */
+/* ¸ü¤J currboard ¶i¦æ­Y¤z³]©w */
+/* ----------------------------------------------------- */
+
+
+int
+bstamp2bno(stamp)
+ time_t stamp;
+{
+ BRD *brd;
+ int bno, max;
+
+ bno = 0;
+ brd = bshm->bcache;
+ max = bshm->number;
+ for (;;)
+ {
+ if (stamp == brd->bstamp)
+ return bno;
+ if (++bno >= max)
+ return -1;
+ brd++;
+ }
+}
+
+
+static inline void
+brh_load()
+{
+ BRD *brdp;
+ usint ulevel;
+ int n, cbno;
+ char *bits;
+
+ int size, *base;
+ time_t expire;
+ char fpath[64];
+
+#ifndef ENHANCED_VISIT
+ time_t *bstp;
+#endif
+
+ memset(bits = brd_bits, 0, sizeof(brd_bits));
+#ifndef ENHANCED_VISIT
+ memset(bstp = brd_visit, 0, sizeof(brd_visit));
+#endif
+
+ ulevel = cuser.userlevel;
+ n = 0;
+ cbno = bshm->number;
+
+ do
+ {
+ *bits++ = Ben_Perm(n, ulevel);
+ } while (++n < cbno);
+
+ /* --------------------------------------------------- */
+ /* ±N .BRH ¸ü¤J memory */
+ /* --------------------------------------------------- */
+
+ size = 0;
+ cbno = -1;
+ brh_expire = expire = time(0) - BRH_EXPIRE * 86400;
+
+ if (ulevel)
+ {
+ struct stat st;
+
+ usr_fpath(fpath, cuser.userid, FN_BRH);
+ if (!stat(fpath, &st))
+ size = st.st_size;
+ }
+
+ /* --------------------------------------------------- */
+ /* ¦h«O¯d BRH_WINDOW ªº¹B§@ªÅ¶¡ */
+ /* --------------------------------------------------- */
+
+ /* brh_size = n = ((size + BRH_WINDOW) & -BRH_PAGE) + BRH_PAGE; */
+ brh_size = n = size + BRH_WINDOW;
+ brh_base = base = (int *) malloc(n);
+
+ if (size && ((n = open(fpath, O_RDONLY)) >= 0))
+ {
+ int *head, *tail, *list, bstamp, bhno;
+
+ size = read(n, base, size);
+ close(n);
+
+ /* compact reading history : remove dummy/expired record */
+
+ head = base;
+ tail = (int *) ((char *) base + size);
+ bits = brd_bits;
+
+ while (head < tail && head >= brh_base)
+ {
+ bstamp = *head;
+
+ if (bstamp & BRH_SIGN) /* zap */
+ {
+ bstamp ^= BRH_SIGN;
+ bhno = bstamp2bno(bstamp);
+ if (bhno >= 0)
+ {
+ /* itoc.001029: NOZAP®É, ¤´·|¥X²{ */
+ brdp = bshm->bcache + bhno;
+ if (!(brdp->battr & BRD_NOZAP))
+ bits[bhno] |= BRD_Z_BIT;
+ }
+ head++;
+ continue;
+ }
+
+ bhno = bstamp2bno(bstamp);
+ list = head + 2;
+
+ if (list > tail)
+ break;
+
+ n = *list;
+ size = n + 3;
+
+ /* ³o­Ó¬ÝªO¦s¦b¡B¨S¦³³Q zap ±¼¡B¥i¥H list */
+
+ if (bhno >= 0 && (bits[bhno] & BRD_L_BIT))
+ {
+ bits[bhno] |= BRD_H_BIT;/* ¤w¦³¾\Ū°O¿ý */
+
+#ifndef ENHANCED_VISIT
+ bstp[bhno] = head[1]; /* ¤W¦¸¾\ۮɶ¡ */
+#endif
+
+ cbno = bhno;
+
+ if (n > 0)
+ {
+ list += n; /* Thor.980904.µù¸Ñ: ³Ì«á¤@­Ó tag */
+
+ if (list > tail)
+ break;
+
+ do
+ {
+ bhno = *list;
+ if ((bhno & BRH_MASK) > expire)
+ break;
+
+ if (!(bhno & BRH_SIGN))
+ {
+ if (*--list > expire)
+ break;
+ n--;
+ }
+
+ list--;
+ n--;
+ } while (n > 0);
+
+ head[2] = n;
+ }
+
+ n = n * sizeof(time_t) + sizeof(BRH);
+ if (base != head)
+ memcpy(base, head, n);
+ base = (int *) ((char *) base + n);
+ }
+ head += size;
+ }
+ }
+
+ *base = 0;
+ brh_tail = base;
+}
+
+
+void
+brh_save()
+{
+ int *base, *head, *tail, bhno, size;
+ BRD *bhdr, *bend;
+ char *bits;
+
+ /* Thor.980830: lkchu patch: ÁÙ¨S load ´N¤£¥Î save */
+ if (!(base = brh_base))
+ return;
+
+ brh_put();
+
+ /* save history of un-zapped boards */
+
+ bits = brd_bits;
+ head = base;
+ tail = brh_tail;
+ while (head < tail)
+ {
+ bhno = bstamp2bno(*head);
+ size = head[2] * sizeof(time_t) + sizeof(BRH);
+ if (bhno >= 0 && !(bits[bhno] & BRD_Z_BIT))
+ {
+ if (base != head)
+ memcpy(base, head, size);
+ base = (int *) ((char *) base + size);
+ }
+ head = (int *) ((char *) head + size);
+ }
+
+ /* save zap record */
+
+ tail = brh_alloc(base, sizeof(time_t) * MAXBOARD);
+
+ bhdr = bshm->bcache;
+ bend = bhdr + bshm->number;
+ do
+ {
+ if (*bits++ & BRD_Z_BIT)
+ {
+ *tail++ = bhdr->bstamp | BRH_SIGN;
+ }
+ } while (++bhdr < bend);
+
+ /* OK, save it */
+
+ base = brh_base;
+ if ((size = (char *) tail - (char *) base) > 0)
+ {
+ char fpath[64];
+ int fd;
+
+ usr_fpath(fpath, cuser.userid, FN_BRH);
+ if ((fd = open(fpath, O_WRONLY | O_CREAT | O_TRUNC, 0600)) >= 0)
+ {
+ write(fd, base, size);
+ close(fd);
+ }
+ }
+}
+
+
+/* ----------------------------------------------------- */
+/* ¤ÀÃþ zap °O¿ý .CZH (Class Zap History) */
+/* ----------------------------------------------------- */
+
+
+typedef struct ClassZapHistory
+{
+ char brdname[BNLEN + 1]; /* ¤ÀÃþªº brdname */
+} CZH;
+
+
+static char class_bits[CH_MAX + 3];
+
+
+static int /* >=0:pos -1:¤£¦b .CZH */
+czh_find(fpath, brdname) /* Àˬd¬O§_¤w¸g¦b .CZH ¤º */
+ char *fpath;
+ char *brdname;
+{
+ CZH czh;
+ int fd, pos;
+ int rc = -1;
+
+ if ((fd = open(fpath, O_RDONLY)) >= 0)
+ {
+ pos = 0;
+ while (read(fd, &czh, sizeof(CZH)) == sizeof(CZH))
+ {
+ if (!strcmp(czh.brdname, brdname))
+ {
+ rc = pos;
+ break;
+ }
+ pos++;
+ }
+ close(fd);
+ }
+ return rc;
+}
+
+
+static int /* >=0:¦b .CZH -1:¤£¦b .CZH */
+czh_put(brdname)
+ char *brdname;
+{
+ char fpath[64];
+ int pos;
+
+ usr_fpath(fpath, cuser.userid, FN_CZH);
+
+ /* ­Y¤w¸g¦b .CZH ¤ºªº¸Ü¡A«h§R°£¦¹µ§¸ê®Æ¡F­Y¤£¦b .CZH ¤ºªº¸Ü¡A«h¥[¤J¦¹µ§¸ê®Æ */
+ if ((pos = czh_find(fpath, brdname)) >= 0)
+ rec_del(fpath, sizeof(CZH), pos, NULL);
+ else
+ rec_add(fpath, brdname, sizeof(CZH));
+
+ return pos;
+}
+
+
+static void
+czh_load()
+{
+ int fsize, chn, min_chn;
+ short *chx;
+ char *img, fpath[64];
+ CZH *chead, *ctail, *czh;
+
+ memset(class_bits, 0, sizeof(class_bits));
+
+ usr_fpath(fpath, cuser.userid, FN_CZH);
+ if (chead = (CZH *) f_img(fpath, &fsize))
+ {
+ min_chn = bshm->min_chn;
+
+ czh = chead;
+ ctail = chead + fsize / sizeof(CZH);
+ img = class_img;
+ do
+ {
+ for (chn = CH_END - 2;; chn--) /* ¦b©Ò¦³ªº¤ÀÃþ¤¤§ä¤@­ÓªO¦W¬Û¦Pªº */
+ {
+ chx = (short *) img + (CH_END - chn);
+ if (!strncmp(czh->brdname, img + *chx, BNLEN))
+ {
+ class_bits[-chn] |= BRD_Z_BIT;
+ break;
+ }
+ if (chn <= min_chn) /* ¦pªG§ä¤£¨ì¡Aªí¥Ü¸Ó¤ÀÃþ¤w®ø¥¢¡A±q .CZH §R°£ */
+ {
+ czh_put(czh->brdname);
+ break;
+ }
+ }
+ } while (++czh < ctail);
+ free(chead);
+ }
+}
+
+static void
+brd_usies()
+{
+ char fpath[64], buf[256];
+
+ brd_fpath(fpath, currboard, "usies");
+ sprintf(buf, "%s %s\n", Now(), cuser.userid);
+ f_cat(fpath, buf);
+}
+
+
+
+/*-------------------------------------------------------*/
+
+
+int
+XoPost(bno)
+ int bno;
+{
+ XO *xo;
+ BRD *brd;
+ int bits;
+ char *str, fpath[64];
+
+ brd = bshm->bcache + bno;
+ if (!brd->brdname[0]) /* ¤w§R°£ªº¬ÝªO */
+ return -1;
+
+ bits = brd_bits[bno];
+
+ if (currbno != bno) /* ¬ÝªO¨S´«³q±`¬O¦]¬° every_Z() ¦^­ì¬ÝªO */
+ {
+#ifdef HAVE_MODERATED_BOARD
+ if (!(bits & BRD_R_BIT))
+ {
+ vmsg("¹ï¤£°_¡A¦¹ªO¥u­ãªO¤Í¶i¤J¡A½Ð¦VªO¥D¥Ó½Ð¤J¹Ò³\\¥i");
+ return -1;
+ }
+#endif
+
+ /* lantw44: ³B²z¬ÝªOÂê©w */
+ brd_fpath(fpath, brd->brdname, fn_lock);
+ struct stat trash;
+ char lock_perm = 0;
+ char can_enter = 0;
+ char is_admin = 0;
+ char lock_type,lock_id[IDLEN + 1];
+ if(stat(fpath, &trash) >= 0){
+ /* lock_type = '0' ªí¥Ü¤j®a³£¶i¤£¥h
+ * lock_type = '1' ªí¥ÜÂê©wªÌ¥i¶i¤J */
+ brd_getlockinfo(fpath, &lock_type, lock_id, NULL);
+ if(lock_type == '1' && !strcmp(lock_id, cuser.userid)){
+ can_enter = 1;
+ }
+ if(bits & BRD_X_BIT){
+ if(HAS_PERM(PERM_ALLBOARD)){
+ lock_perm = 1;
+ is_admin = 1;
+ }else{
+ switch(is_bm(brd->BM, cuser.userid)){
+ case 0:
+ break;
+ case 1:
+ lock_perm = 1;
+ break;
+ default:
+ if(!strcmp(lock_id,cuser.userid)){
+ lock_perm = 1;
+ }
+ }
+ }
+ }
+ brd_viewlock(fpath);
+ if(lock_perm && !is_admin){
+ if(can_enter){
+ if(vans("½Ð°Ý±z­n [E]¶i¤J¬ÝªO (M)­×§ïÂê©wª¬ºA ")=='m'){
+ brd_editlock(fpath);
+ }
+ }else{
+ if(vans("½Ð°Ý±z­n­×§ïÂê©wª¬ºA¶Ü¡H[N]")=='y'){
+ brd_editlock(fpath);
+ }
+ return -1;
+ }
+ }else{
+ if(!can_enter){
+ vmsg("¥Ø«e¦¹¬ÝªO¤w¸gÂê©w¡A±zµLªk¶i¤J");
+ return -1;
+ }
+ }
+ }
+
+ /* ³B²zÅv­­ */
+ bbstate = STAT_STARTED;
+ if (bits & BRD_M_BIT)
+ bbstate |= (STAT_BM | STAT_BOARD | STAT_POST);
+ else if (bits & BRD_X_BIT)
+ bbstate |= (STAT_BOARD | STAT_POST);
+ else if (bits & BRD_W_BIT)
+ bbstate |= STAT_POST;
+
+ /* itoc.050613.µù¸Ñ: ¤H®ðªº´î¤Ö¤£¬O¦bÂ÷¶}¬ÝªO®É¡A¦Ó¬O¦b¶i¤J·sªº¬ÝªO©Î¬OÂ÷¯¸®É¡A
+ ³o¬O¬°¤FÁ×§K switch ¸õ¬ÝªO·|ºâ¿ù¤H®ð */
+ if (currbno >= 0)
+ bshm->mantime[currbno]--; /* °h¥X¤W¤@­ÓªO */
+ bshm->mantime[bno]++; /* ¶i¤J·sªºªO */
+
+ currbno = bno;
+ currbattr = brd->battr;
+ strcpy(currboard, brd->brdname);
+ str = brd->BM;
+ sprintf(currBM, "ªO¥D¡G%s", *str <= ' ' ? "¼x¨D¤¤" : str);
+#ifdef HAVE_BRDMATE
+ strcpy(cutmp->reading, currboard);
+#endif
+
+
+ brd_fpath(fpath, currboard, fn_dir);
+
+#ifdef AUTO_JUMPPOST
+ xz[XZ_POST - XO_ZONE].xo = xo = xo_get_post(fpath, brd); /* itoc.010910: ¬° XoPost ¶q¨­¥´³y¤@¤ä xo_get() */
+#else
+ xz[XZ_POST - XO_ZONE].xo = xo = xo_get(fpath);
+#endif
+ xo->key = XZ_POST;
+ xo->xyz = brd->title;
+ }
+
+ /* itoc.011113: ²Ä¤@¦¸¶iªO¤@©w­n¬Ý¶iªOµe­±¡A²Ä¤G¦¸¥H«á«h¨ú¨M ufo ³]©w */
+ if (!(bits & BRD_V_BIT) || (cuser.ufo & UFO_BRDNOTE))
+ {
+ brd_bits[bno] = bits | BRD_V_BIT;
+ brd_fpath(fpath, currboard, fn_note);
+ more(fpath, NULL);
+ }
+
+ brh_get(brd->bstamp, bno);
+ if (!(brd->battr & BRD_ANONYMOUS))
+ brd_usies();
+// xo->pos = (brd->btime < 0 ? rec_num(fpath, sizeof(HDR)) : brd->bpost) - 1;
+ return 0;
+}
+
+void brd_editlock(const char* fpath){
+ struct stat trash;
+ char type,userid[IDLEN + 1],reason[49];
+ switch(vans("L)Âê©w M)­×§ï²z¥Ñ U)¸ÑÂê¡H[Q]")){
+ case 'l':
+ if(stat(fpath,&trash)>=0){
+ vmsg("³o­Ó¬ÝªO¥Ø«e¤w¸g³B©óÂê©wª¬ºA");
+ break;
+ }
+ if(vans("Âê©wÃþ«¬ (1)¤¹³\\¦Û¤v¥i¥H¶i¤J¬ÝªO [2]©Ò¦³¤H¬ÒµLªk¶i¤J¬ÝªO¡H")=='1'){
+ type = '1';
+ }else{
+ type = '0';
+ }
+ vget(b_lines, 0, "²z¥Ñ¡G", reason, 48, DOECHO);
+ strcpy(userid, cuser.userid);
+ if(vans(msg_sure_ny)=='y'){
+ brd_setlockinfo(fpath,type,userid,reason);
+ }
+ break;
+ break;
+ case 'm':
+ if(stat(fpath,&trash)<0){
+ vmsg("³o­Ó¬ÝªO¨Ã¥¼Âê©w");
+ break;
+ }
+ brd_getlockinfo(fpath,&type,userid,reason);
+ if(HAS_PERM(PERM_ALLBOARD)){
+ vget(b_lines, 0, "¥Ñ½ÖÂê©w¡G", userid, IDLEN + 1, GCARRY);
+ }
+ vget(b_lines, 0, "²z¥Ñ¡G", reason, 48, GCARRY);
+ if(vans(msg_sure_ny)=='y'){
+ brd_setlockinfo(fpath,type,userid,reason);
+ }
+ break;
+ case 'u':
+ if(stat(fpath,&trash)<0){
+ vmsg("³o­Ó¬ÝªO¨Ã¥¼Âê©w");
+ break;
+ }
+ unlink(fpath);
+ }
+}
+
+void brd_viewlock(const char* fpath){
+ /* move(19,0),clrtobot(),refresh(); */
+ char userid[IDLEN + 1],reason[49];
+ brd_getlockinfo(fpath, NULL, userid, reason);
+ char buf[80];
+ sprintf(buf, "Âê©wªÌ¡G%s", userid);
+ vmsg(buf);
+ sprintf(buf, "²z¥Ñ¡G%s", reason);
+ vmsg(buf);
+}
+/*
+ prints("\033[1;33m[¥Ñ½ÖÂê©w]\033[m %s\n", userid);
+ prints("\033[1;33m[Âê©w²z¥Ñ]\033[m %s\n", reason);
+*/
+void brd_getlockinfo(fpath, type, userid, reason)
+ const char* fpath;
+ char* type;
+ char* userid;
+ char* reason;
+{
+ FILE* fp = fopen(fpath, "r");
+ if(fp == NULL){
+ strcpy(userid, "Ū¨ú¥¢±Ñ");
+ strcpy(reason, "Ū¨ú¥¢±Ñ");
+ return;
+ }
+ if(type != NULL){
+ *type = getc(fp);
+ }else{
+ fseek(fp, 1, SEEK_CUR);
+ }
+ if(userid != NULL){
+ fread(userid, IDLEN, 1, fp);
+ userid[IDLEN] = '\0';
+ int i;
+ for(i=0;i<IDLEN;i++){
+ if(userid[i]=='\033'){
+ userid[i]='\0';
+ break;
+ }
+ }
+ }else{
+ fseek(fp, IDLEN, SEEK_CUR);
+ }
+ if(reason != NULL){
+ fread(reason, 48, 1, fp);
+ reason[48]='\0';
+ int i;
+ for(i=0;i<48;i++){
+ if(reason[i]=='\033'){
+ reason[i]='\0';
+ break;
+ }
+ }
+ }else{
+ fseek(fp, 48, SEEK_CUR);
+ }
+ fclose(fp);
+}
+
+void brd_setlockinfo(fpath, type, userid, reason)
+ const char* fpath;
+ char type;
+ const char* userid;
+ const char* reason;
+{
+ FILE* fp = fopen(fpath, "w");
+ if(fp == NULL){
+ return;
+ }
+ putc(type, fp);
+ char tmpid[IDLEN], tmprs[48];
+ int i;
+ char complete_flag;
+ for(i=0,complete_flag=0;i<IDLEN;i++){
+ if(complete_flag){
+ tmpid[i]='\033';
+ }else{
+ if(userid[i]=='\0'){
+ tmpid[i]='\033';
+ complete_flag = 1;
+ }else{
+ tmpid[i]=userid[i];
+ }
+ }
+ }
+ fwrite(tmpid, IDLEN, 1, fp);
+ for(i=0,complete_flag=0;i<48;i++){
+ if(complete_flag){
+ tmprs[i]='\033';
+ }else{
+ if(reason[i]=='\0'){
+ tmprs[i]='\033';
+ complete_flag = 1;
+ }else{
+ tmprs[i]=reason[i];
+ }
+ }
+ }
+ fwrite(tmprs, 48, 1, fp);
+ fclose(fp);
+}
+
+#ifdef HAVE_FORCE_BOARD
+void
+brd_force() /* itoc.010407: ±j¨î¾\Ū¤½§iªO¡A¥B±j­¢Åª³Ì«á¤@½g */
+{
+ if (cuser.userlevel) /* guest ¸õ¹L */
+ {
+ int bno;
+ BRD *brd;
+
+ if ((bno = brd_bno(BN_ANNOUNCE)) < 0)
+ return;
+ brd = bshm->bcache + bno;
+ if (brd->btime < 0) /* ©|¥¼§ó·s brd->blast ´N¤£±j¨î¾\Ū¤½§iªO */
+ return;
+
+#ifdef ENHANCED_VISIT
+ brh_get(brd->bstamp, bno);
+ while (brh_unread(brd->blast))
+#else
+ if (brd->blast > brd_visit[bno])
+#endif
+ {
+ vmsg("¦³·s¤½§i¡I½Ð¥ý¾\\Ū§¹·s¤½§i«á¦AÂ÷¶}");
+ XoPost(bno);
+ xover(XZ_POST);
+
+#ifndef ENHANCED_VISIT
+ time(&brd_visit[bno]);
+#endif
+ }
+ }
+}
+#endif /* HAVE_FORCE_BOARD */
+
+
+/* ----------------------------------------------------- */
+/* Class [¤ÀÃþ¸s²Õ] */
+/* ----------------------------------------------------- */
+
+
+#ifdef MY_FAVORITE
+int class_flag = 0; /* favorite.c ­n¥Î */
+#else
+static int class_flag = 0;
+#endif
+
+
+#ifdef AUTO_JUMPBRD
+static int class_jumpnext = 0; /* itoc.010910: ¬O§_¸õ¥h¤U¤@­Ó¥¼ÅªªO 1:­n 0:¤£­n */
+#endif
+
+
+#define BFO_YANK 0x01
+
+
+static int
+class_load(xo)
+ XO *xo;
+{
+ short *cbase, *chead, *ctail;
+ int chn; /* ClassHeader number */
+ int pos, max, val, zap;
+ BRD *brd;
+ char *bits;
+
+ /* lkchu.990106: ¨¾¤î ¥¼¥Î account ³y¥X class.img ©Î¨S¦³ class ªº±¡ªp */
+ if (!class_img)
+ return 0;
+
+ chn = CH_END - xo->key;
+
+ cbase = (short *) class_img;
+ chead = cbase + chn;
+
+ pos = chead[0] + CH_TTLEN;
+ max = chead[1];
+
+ chead = (short *) ((char *) cbase + pos);
+ ctail = (short *) ((char *) cbase + max);
+
+ max -= pos;
+
+ if (cbase = (short *) xo->xyz)
+ cbase = (short *) realloc(cbase, max);
+ else
+ cbase = (short *) malloc(max);
+
+ xo->xyz = (char *) cbase;
+
+ max = 0;
+ brd = bshm->bcache;
+ bits = brd_bits;
+ zap = (class_flag & BFO_YANK) ? 0 : BRD_Z_BIT;
+
+ do
+ {
+ chn = *chead++;
+ if (chn >= 0) /* ¤@¯ë¬ÝªO */
+ {
+ val = bits[chn];
+ if (!(val & BRD_L_BIT) || (val & zap) || !(brd[chn].brdname[0]))
+ continue;
+ }
+ else /* ¤ÀÃþ¸s²Õ */
+ {
+ if (class_bits[-chn] & zap)
+ continue;
+ }
+
+ max++;
+ *cbase++ = chn;
+ } while (chead < ctail);
+
+ xo->max = max;
+ if (xo->pos >= max)
+ xo->pos = xo->top = 0;
+
+ return max;
+}
+
+
+static int
+XoClass(chn)
+ int chn;
+{
+ XO xo, *xt;
+
+ /* Thor.980727: ¸Ñ¨M XO xoªº¤£½T©w©Ê,
+ class_load¤º³¡·| initial xo.max, ¨ä¥L¤£½T©w */
+ xo.pos = xo.top = 0;
+
+ xo.key = chn;
+ xo.xyz = NULL;
+ if (!class_load(&xo))
+ {
+ if (xo.xyz)
+ free(xo.xyz);
+ return 0;
+ }
+
+ xt = xz[XZ_CLASS - XO_ZONE].xo;
+ xz[XZ_CLASS - XO_ZONE].xo = &xo;
+
+#ifdef AUTO_JUMPBRD
+ if (cuser.ufo & UFO_JUMPBRD)
+ class_jumpnext = 1; /* itoc.010910: ¥D°Ê¸õ¥h¤U¤@­Ó¥¼Åª¬ÝªO */
+#endif
+ xover(XZ_CLASS);
+
+ free(xo.xyz);
+ xz[XZ_CLASS - XO_ZONE].xo = xt;
+
+ return 1;
+}
+
+
+static inline void
+btime_refresh(brd)
+ BRD *brd;
+{
+ /* itoc.020123: ¤£ºÞ¦³µL UFO_BRDPOST¡A¤@·§§ó·s¡A¥H§K¥¼Åª¿O¤£·|«G */
+ if (brd->btime < 0)
+ {
+ int fd, fsize;
+ char folder[64];
+ struct stat st;
+
+ brd->btime = 1;
+ brd_fpath(folder, brd->brdname, fn_dir);
+ if ((fd = open(folder, O_RDONLY)) >= 0)
+ {
+ if (!fstat(fd, &st) && (fsize = st.st_size) >= sizeof(HDR))
+ {
+#ifdef ENHANCED_BSHM_UPDATE
+ HDR hdr;
+
+ brd->bpost = fsize / sizeof(HDR);
+ /* itoc.020829: §ä³Ì«á¤@½g¥¼³Q¥[±K¡B¤£¬O¸m©³ªº HDR */
+ while ((fsize -= sizeof(HDR)) >= 0)
+ {
+ lseek(fd, fsize, SEEK_SET);
+ read(fd, &hdr, sizeof(HDR));
+ if (!(hdr.xmode & (POST_RESTRICT | POST_BOTTOM)))
+ break;
+ }
+ brd->blast = hdr.chrono;
+#else
+ brd->bpost = fsize / sizeof(HDR);
+ lseek(fd, fsize - sizeof(HDR), SEEK_SET);
+ read(fd, &brd->blast, sizeof(time_t));
+#endif
+ }
+ else
+ {
+ brd->blast = brd->bpost = 0;
+ }
+ close(fd);
+ }
+ }
+}
+
+
+void
+class_item(num, bno, brdpost)
+ int num, bno, brdpost;
+{
+ BRD *brd;
+ char *str1, *str2, *str3, token, buf[16];
+
+ brd = bshm->bcache + bno;
+
+ btime_refresh(brd);
+
+ /* ³B²z ½s¸¹/½g¼Æ */
+ if (brdpost)
+ num = brd->bpost;
+
+ /* ³B²z zap/friend/secret ªOªº²Å¸¹ */
+ if (brd_bits[bno] & BRD_Z_BIT)
+ token = TOKEN_ZAP_BRD;
+ else if (brd->readlevel == PERM_SYSOP)
+ token = TOKEN_SECRET_BRD;
+ else if (brd->readlevel == PERM_BOARD)
+ token = TOKEN_FRIEND_BRD;
+ else if ((brd->readlevel & 0xFF000000) && !((brd->readlevel) & (PERM_BASIC | PERM_CHAT | PERM_PAGE | PERM_POST | PERM_VALID))){
+ token = '#';
+ }
+ else
+ token = ' ';
+
+ /* ³B²z ¤wŪ/¥¼Åª */
+#ifdef ENHANCED_VISIT
+ /* itoc.010407: §ï¥Î³Ì«á¤@½g¤wŪ/¥¼Åª¨Ó§PÂ_ */
+ brh_get(brd->bstamp, bno);
+ str1 = brh_unread(brd->blast) ? ICON_UNREAD_BRD : ICON_READ_BRD;
+#else
+ str1 = brd->blast > brd_visit[bno] ? ICON_UNREAD_BRD : ICON_READ_BRD;
+#endif
+
+ /* ³B²z §ë²¼/Âà«H */
+ if (brd->bvote)
+ str2 = (brd->bvote > 0) ? ICON_VOTED_BRD : ICON_GAMBLED_BRD;
+ else
+ str2 = (brd->battr & BRD_NOTRAN) ? ICON_NOTRAN_BRD : ICON_TRAN_BRD;
+
+ /* ³B²z ¤H®ð */
+ bno = bshm->mantime[bno];
+// if (bno > 99)
+// str3 = "\033[1;31mÃz\033[m";
+ if (bno > 30)
+ str3 = "\033[1;33;41mÃz\033[m";
+ else if (bno >= 20)
+ str3 = "\033[1;33mµo\033[m";
+ else if (bno >= 10)
+ str3 = "\033[1;31mµo\033[m";
+ else if (bno >= 5)
+ sprintf(str3 = buf, "\033[1m%2d\033[m", bno);
+ else if (bno >= 1)
+ sprintf(str3 = buf, "\033[m%2d\033[m", bno);
+// else if (bno > 0)
+// sprintf(str3 = buf, "%2d", bno);
+ else
+ str3 = " ";
+
+ /* itoc.010909: ªO¦W¤Óªøªº§R±¼¡B¥[¤ÀÃþÃC¦â¡C°²³] BCLEN = 4 */
+ prints("%6d%c%s%-13s\033[1;3%dm%-5s\033[m%s %-*.*s %s %.*s\n",
+ num, token, str1, brd->brdname,
+ brd->class[3] & 7, brd->class, str2,
+ (d_cols >> 1) + 32, (d_cols >> 1) + 33, brd->title, str3,
+ d_cols - (d_cols >> 1) + 12, brd->BM);
+}
+
+
+static int
+class_body(xo)
+ XO *xo;
+{
+ short *chp;
+ BRD *bcache;
+ int n, cnt, max, chn, brdpost;
+#ifdef AUTO_JUMPBRD
+ int nextpos;
+#endif
+
+ bcache = bshm->bcache;
+ max = xo->max;
+ cnt = xo->top;
+
+#ifdef AUTO_JUMPBRD
+ nextpos = 0;
+
+ /* itoc.010910: ·j´M¤U¤@­Ó¥¼Åª¬ÝªO */
+ if (class_jumpnext)
+ {
+ class_jumpnext = 0;
+ n = xo->pos;
+ chp = (short *) xo->xyz + n;
+
+ while (n < max)
+ {
+ chn = *chp++;
+ if (chn >= 0)
+ {
+ BRD *brd;
+
+ brd = bcache + chn;
+
+#ifdef ENHANCED_VISIT
+ /* itoc.010407: §ï¥Î³Ì«á¤@½g¤wŪ/¥¼Åª¨Ó§PÂ_ */
+ brh_get(brd->bstamp, chn);
+ if (brh_unread(brd->blast))
+#else
+ if (brd->blast > brd_visit[chn])
+#endif
+ {
+ nextpos = n;
+ break;
+ }
+ }
+ n++;
+ }
+
+ /* ¤U¤@­Ó¥¼ÅªªO¦b§O­¶¡A­n½¹L¥h */
+ if (nextpos >= cnt + XO_TALL)
+ return nextpos + XO_MOVE;
+ }
+#endif
+
+ brdpost = class_flag & UFO_BRDPOST;
+ chp = (short *) xo->xyz + cnt;
+
+ n = 3;
+ move(3, 0);
+ do
+ {
+ chn = *chp;
+ if (cnt < max)
+ {
+ clrtoeol();
+ cnt++;
+ if (chn >= 0) /* ¤@¯ë¬ÝªO */
+ {
+ class_item(cnt, chn, brdpost);
+ }
+ else /* ¤ÀÃþ¸s²Õ */
+ {
+ short *chx;
+ char *img, *str;
+
+ img = class_img;
+ chx = (short *) img + (CH_END - chn);
+ str = img + *chx;
+ prints("%6d%c %-13.13s\033[1;3%dm%-5.5s\033[m%s\n",
+ cnt, class_bits[-chn] & BRD_Z_BIT ? TOKEN_ZAP_BRD : ' ',
+ str, str[BNLEN + 4] & 7, str + BNLEN + 1, str + BNLEN + 1 + BCLEN + 1);
+ }
+ chp++;
+ }
+ else
+ {
+ clrtobot();
+ break;
+ }
+ } while (++n < b_lines);
+
+#ifdef AUTO_JUMPBRD
+ /* itoc.010910: ¤U¤@­Ó¥¼ÅªªO¦b¥»­¶¡A­n§â´å¼Ð²¾¹L¥h */
+ outf(FEETER_CLASS);
+ return nextpos ? nextpos + XO_MOVE : XO_NONE;
+#else
+ /* return XO_NONE; */
+ return XO_FOOT; /* itoc.010403: §â b_lines ¶ñ¤W feeter */
+#endif
+}
+
+
+static int
+class_neck(xo)
+ XO *xo;
+{
+ move(1, 0);
+ prints(NECKER_CLASS,
+ class_flag & UFO_BRDPOST ? "Á`¼Æ" : "½s¸¹",
+ d_cols >> 1, "", d_cols - (d_cols >> 1), "");
+ return class_body(xo);
+}
+
+
+static int
+class_head(xo)
+ XO *xo;
+{
+ vs_head("¬ÝªO¦Cªí", str_site);
+ return class_neck(xo);
+}
+
+
+static int
+class_init(xo) /* re-init */
+ XO *xo;
+{
+ class_load(xo);
+ return class_head(xo);
+}
+
+
+static int
+class_postmode(xo)
+ XO *xo;
+{
+ cuser.ufo ^= UFO_BRDPOST;
+ cutmp->ufo = cuser.ufo;
+ class_flag ^= UFO_BRDPOST;
+ return class_neck(xo);
+}
+
+
+static int
+class_namemode(xo) /* itoc.010413: ¬ÝªO¨Ì·Ó¦r¥À/¤ÀÃþ±Æ¦C */
+ XO *xo;
+{
+ static time_t last = 0;
+ time_t now;
+
+ if (time(&now) - last < 10)
+ {
+ vmsg("¨C¤Q¬íÄÁ¥u¯à¤Á´«¤@¦¸");
+ return XO_FOOT;
+ }
+ last = now;
+
+ if (cuser.userlevel)
+ brh_save(); /* itoc.010711: Àx¦s¾\Ū°O¿ýÀÉ */
+ cuser.ufo ^= UFO_BRDNAME;
+ cutmp->ufo = cuser.ufo;
+ board_main(); /* ­«·s¸ü¤J class_img */
+ return class_neck(xo);
+}
+
+
+static int
+class_help(xo)
+ XO *xo;
+{
+ xo_help("class");
+ return class_head(xo);
+}
+
+
+static int
+class_search(xo)
+ XO *xo;
+{
+ int num, pos, max;
+ char buf[BNLEN + 1];
+
+ if (vget(b_lines, 0, MSG_BID, buf, BNLEN + 1, DOECHO))
+ {
+ short *chp, chn;
+ BRD *bcache, *brd;
+
+ str_lowest(buf, buf);
+
+ bcache = bshm->bcache;
+ pos = num = xo->pos;
+ max = xo->max;
+ chp = (short *) xo->xyz;
+
+ do
+ {
+ if (++pos >= max)
+ pos = 0;
+ chn = chp[pos];
+ if (chn >= 0)
+ {
+ brd = bcache + chn;
+ if (str_str(brd->brdname, buf) || str_sub(brd->title, buf))
+ {
+ outf(FEETER_CLASS); /* itoc.010913: §â b_lines ¶ñ¤W feeter */
+ return pos + XO_MOVE;
+ }
+ }
+ } while (pos != num);
+ }
+
+ return XO_FOOT;
+}
+
+
+static int
+class_searchBM(xo)
+ XO *xo;
+{
+ int num, pos, max;
+ char buf[IDLEN + 1];
+
+ if (vget(b_lines, 0, "½Ð¿é¤JªO¥D¡G", buf, IDLEN + 1, DOECHO))
+ {
+ short *chp, chn;
+ BRD *bcache, *brd;
+
+ str_lower(buf, buf);
+
+ bcache = bshm->bcache;
+ pos = num = xo->pos;
+ max = xo->max;
+ chp = (short *) xo->xyz;
+
+ do
+ {
+ if (++pos >= max)
+ pos = 0;
+ chn = chp[pos];
+ if (chn >= 0)
+ {
+ brd = bcache + chn;
+ if (str_str(brd->BM, buf))
+ {
+ outf(FEETER_CLASS); /* itoc.010913: §â b_lines ¶ñ¤W feeter */
+ return pos + XO_MOVE;
+ }
+ }
+ } while (pos != num);
+ }
+
+ return XO_FOOT;
+}
+
+
+static int
+class_yank(xo)
+ XO *xo;
+{
+ /* itoc.001029: ©Ò¦³ªºclass,board¦Cªí¤U, key < 0, 1 «h¬°§ä´M§@ªÌ¼Ò¦¡
+ ¨Ï¨ä¤£¯à¶] XO_INIT(ùØ­±ªºclass_load), ¦p class_yank,
+ °£¤F¨¾¤î§ä¥Xªº§@ªÌ¬ÝªO¦Cªí®ø¥¢, ¤]¨¾½ð¤H */
+ if (xo->key >= 0)
+ return XO_NONE;
+
+ class_flag ^= BFO_YANK;
+ return class_init(xo);
+}
+
+
+static int
+class_zap(xo)
+ XO *xo;
+{
+ BRD *brd;
+ short *chp;
+ int pos, num, chn;
+ char token;
+
+ pos = xo->pos;
+ chp = (short *) xo->xyz + pos;
+ chn = *chp;
+ if (chn >= 0) /* ¤@¯ë¬ÝªO */
+ {
+ brd = bshm->bcache + chn;
+ if (!(brd->battr & BRD_NOZAP))
+ {
+ /* itoc.010909: ­nÀH class_item() ª©­±ÅÜ */
+ move(3 + pos - xo->top, 6);
+ num = brd_bits[chn] ^= BRD_Z_BIT;
+
+ /* ³B²z zap/friend/secret ªOªº²Å¸¹ */
+ if (num & BRD_Z_BIT)
+ token = TOKEN_ZAP_BRD;
+ else if (brd->readlevel == PERM_SYSOP)
+ token = TOKEN_SECRET_BRD;
+ else if (brd->readlevel == PERM_BOARD)
+ token = TOKEN_FRIEND_BRD;
+ else
+ token = ' ';
+
+ outc(token);
+ }
+ }
+ else /* ¤ÀÃþ¸s²Õ */
+ {
+ short *chx;
+ char *img, brdname[BNLEN + 1];
+
+ /* itoc.010909: ­nÀH class_body() ª©­±ÅÜ */
+ move(3 + pos - xo->top, 6);
+ num = class_bits[-chn] ^= BRD_Z_BIT;
+ outc(num & BRD_Z_BIT ? TOKEN_ZAP_BRD : ' ');
+
+ img = class_img;
+ chx = (short *) img + (CH_END - chn);
+ str_ncpy(brdname, img + *chx, BNLEN + 1);
+ czh_put(brdname);
+ }
+
+ /* return XO_NONE; */
+ return XO_MOVE + pos + 1; /* itoc.020219: ¸õ¦Ü¤U¤@¶µ */
+}
+
+
+static int
+class_zapall(xo)
+ XO *xo;
+{
+ BRD *brdp, *bend;
+ int ans, bno;
+
+ ans = vans("³]©w©Ò¦³¬ÝªO (U)­q¾\\ (Z)¤£­q¾\\ (Q)¨ú®ø¡H [Q] ");
+ if (ans != 'z' && ans != 'u')
+ return XO_FOOT;
+
+ brdp = bshm->bcache;
+ bend = brdp + bshm->number;
+ bno = 0;
+ do
+ {
+ if (ans == 'z')
+ {
+ if (!(brdp->battr & BRD_NOZAP))
+ brd_bits[bno] |= BRD_Z_BIT;
+ }
+ else
+ {
+ brd_bits[bno] &= ~BRD_Z_BIT;
+ }
+
+ bno++;
+ } while (++brdp < bend);
+
+ class_flag |= BFO_YANK; /* ±j­¢ yank °_¨Ó¬Ýµ²ªG */
+ return class_init(xo);
+}
+
+
+static int
+class_visit(xo) /* itoc.010128: ¬ÝªO¦Cªí³]©w¬ÝªO¤wŪ */
+ XO *xo;
+{
+ short *chp;
+ int chn;
+
+ chp = (short *) xo->xyz + xo->pos;
+ chn = *chp;
+ if (chn >= 0)
+ {
+ BRD *brd;
+ brd = bshm->bcache + chn;
+ brh_get(brd->bstamp, chn);
+ brh_visit(0);
+#ifndef ENHANCED_VISIT
+ time(&brd_visit[chn]);
+#endif
+ }
+ return class_body(xo);
+}
+
+
+static int
+class_unvisit(xo) /* itoc.010129: ¬ÝªO¦Cªí³]©w¬ÝªO¥¼Åª */
+ XO *xo;
+{
+ short *chp;
+ int chn;
+
+ chp = (short *) xo->xyz + xo->pos;
+ chn = *chp;
+ if (chn >= 0)
+ {
+ BRD *brd;
+ brd = bshm->bcache + chn;
+ brh_get(brd->bstamp, chn);
+ brh_visit(1);
+#ifndef ENHANCED_VISIT
+ brd_visit[chn] = 0; /* itoc.010402: ³ÌªñÂsÄý®É¶¡Âk¹s¡A¨Ï¬ÝªO¦Cªí¤¤Åã¥Ü¥¼Åª */
+#endif
+ }
+ return class_body(xo);
+}
+
+
+static int
+class_nextunread(xo)
+ XO *xo;
+{
+ int max, pos, chn;
+ short *chp;
+ BRD *bcache, *brd;
+
+ bcache = bshm->bcache;
+ max = xo->max;
+ pos = xo->pos;
+ chp = (short *) xo->xyz + pos;
+
+ while (++pos < max)
+ {
+ chn = *(++chp);
+ if (chn >= 0 && !(brd_bits[chn] & BRD_Z_BIT)) /* ¸õ¹L¤ÀÃþ¤Î zap ±¼ªº¬ÝªO */
+ {
+ brd = bcache + chn;
+
+#ifdef ENHANCED_VISIT
+ /* itoc.010407: §ï¥Î³Ì«á¤@½g¤wŪ/¥¼Åª¨Ó§PÂ_ */
+ brh_get(brd->bstamp, chn);
+ if (brh_unread(brd->blast))
+#else
+ if (brd->blast > brd_visit[chn])
+#endif
+ return pos + XO_MOVE;
+ }
+ }
+
+ return XO_NONE;
+}
+
+
+static int
+class_edit(xo)
+ XO *xo;
+{
+ if (HAS_PERM(PERM_ALLBOARD | PERM_BM))
+ {
+ short *chp;
+ int chn;
+
+ chp = (short *) xo->xyz + xo->pos;
+ chn = *chp;
+ if (chn >= 0)
+ {
+ if (!HAS_PERM(PERM_ALLBOARD))
+ brd_title(chn); /* itoc.000312: ªO¥D­×§ï¤¤¤å±Ô­z */
+ else
+ brd_edit(chn);
+ return class_init(xo);
+ }
+ }
+ return XO_NONE;
+}
+
+
+static int
+hdr_cmp(a, b)
+ HDR *a;
+ HDR *b;
+{
+ /* ¥ý¤ñ¹ï¤ÀÃþ¡A¦A¤ñ¹ïªO¦W */
+ int k = strncmp(a->title + BNLEN + 1, b->title + BNLEN + 1, BCLEN);
+ return k ? k : str_cmp(a->xname, b->xname);
+}
+
+
+static int
+class_newbrd(xo)
+ XO *xo;
+{
+ BRD newboard;
+
+ if (!HAS_PERM(PERM_ALLBOARD))
+ return XO_NONE;
+
+ memset(&newboard, 0, sizeof(BRD));
+
+ /* itoc.010211: ·s¬ÝªO¹w³] postlevel = PERM_POST; battr = ¤£Âà«H */
+ newboard.postlevel = PERM_POST;
+ newboard.battr = BRD_NOTRAN;
+
+ if (brd_new(&newboard) < 0)
+ return class_head(xo);
+
+ if (xo->key < CH_END) /* ¦b¤ÀÃþ¸s²Õ¸Ì­± */
+ {
+ short *chx;
+ char *img, *str;
+ char xname[BNLEN + 1], fpath[64];
+ HDR hdr;
+
+ img = class_img;
+ chx = (short *) img + (CH_END - xo->key);
+ str = img + *chx;
+
+ str_ncpy(xname, str, sizeof(xname));
+ if (str = strchr(xname, '/'))
+ *str = '\0';
+
+ /* ¥[¤J¤ÀÃþ¸s²Õ */
+ sprintf(fpath, "gem/@/@%s", xname);
+ brd2gem(&newboard, &hdr);
+ rec_add(fpath, &hdr, sizeof(HDR));
+ rec_sync(fpath, sizeof(HDR), hdr_cmp, NULL);
+
+ vmsg("·sªO¦¨¥ß");
+ }
+ else /* ¦b¬ÝªO¦Cªí¸Ì­± */
+ {
+ vmsg("·sªO¦¨¥ß¡A°OµÛ¥[¤J¤ÀÃþ¸s²Õ");
+ }
+
+ return class_init(xo);
+}
+
+
+static int
+class_browse(xo)
+ XO *xo;
+{
+ short *chp;
+ int chn;
+
+ chp = (short *) xo->xyz + xo->pos;
+ chn = *chp;
+ if (chn < 0) /* ¶i¤J¤ÀÃþ */
+ {
+ if (!XoClass(chn))
+ return XO_NONE;
+ }
+ else /* ¶i¤J¬ÝªO */
+ {
+ if (XoPost(chn)) /* µLªk¾\Ū¸ÓªO */
+ return XO_FOOT;
+ xover(XZ_POST);
+#ifndef ENHANCED_VISIT
+ time(&brd_visit[chn]);
+#endif
+ }
+
+#ifdef AUTO_JUMPBRD
+ if (cuser.ufo & UFO_JUMPBRD)
+ class_jumpnext = 1; /* itoc.010910: ¥u¦³¦bÂ÷¶}¬ÝªO¦^¨ì¬ÝªO¦Cªí®É¤~»Ý­n¸õ¥h¤U¤@­Ó¥¼Åª¬ÝªO */
+#endif
+
+ return class_head(xo); /* Thor.980701: µLªk²M¤Ö¤@ÂI, ¦]¬° XoPost */
+}
+
+
+int
+Select()
+{
+ int bno;
+ BRD *brd;
+ char bname[BNLEN + 1];
+
+ if (brd = ask_board(bname, BRD_R_BIT, NULL))
+ {
+ bno = brd - bshm->bcache;
+ if(XoPost(bno)){
+ return 0;
+ }
+ xover(XZ_POST);
+#ifndef ENHANCED_VISIT
+ time(&brd_visit[bno]);
+#endif
+ }
+ else
+ {
+ vmsg(err_bid);
+ }
+
+ return 0;
+}
+
+
+static int
+class_switch(xo)
+ XO *xo;
+{
+ Select();
+ return class_head(xo);
+}
+
+
+#ifdef MY_FAVORITE
+
+/* ----------------------------------------------------- */
+/* MyFavorite [§Úªº³Ì·R] */
+/* ----------------------------------------------------- */
+
+
+static inline int
+in_favor(brdname)
+ char *brdname;
+{
+ MF mf;
+ int fd;
+ int rc = 0;
+ char fpath[64];
+
+ if (brdname[0])
+ {
+ mf_fpath(fpath, cuser.userid, FN_MF);
+
+ if ((fd = open(fpath, O_RDONLY)) >= 0)
+ {
+ while (read(fd, &mf, sizeof(MF)) == sizeof(MF))
+ {
+ if (!strcmp(brdname, mf.xname))
+ {
+ rc = 1;
+ break;
+ }
+ }
+ }
+ close(fd);
+ }
+ return rc;
+}
+
+
+static int
+class_addMF(xo)
+ XO *xo;
+{
+ short *chp;
+ int chn;
+
+ if (!cuser.userlevel)
+ return XO_NONE;
+
+ chp = (short *) xo->xyz + xo->pos;
+ chn = *chp;
+
+ if (chn >= 0)
+ {
+ BRD *bhdr;
+
+ bhdr = bshm->bcache + chn;
+
+ if (!in_favor(bhdr->brdname))
+ {
+ MF mf;
+ char fpath[64];
+
+ memset(&mf, 0, sizeof(MF));
+ time(&mf.chrono);
+ mf.mftype = MF_BOARD;
+ strcpy(mf.xname, bhdr->brdname);
+
+ mf_fpath(fpath, cuser.userid, FN_MF);
+ rec_add(fpath, &mf, sizeof(MF));
+ vmsg("¤w¥[¤J§Úªº³Ì·R");
+ }
+ else
+ {
+ vmsg("¦¹¬ÝªO¤w¦b³Ì·R¤¤¡C­Y­n­«ÂÐ¥[¤J¡A½Ð¶i§Úªº³Ì·R¸Ì·s¼W");
+ }
+ }
+
+ return XO_FOOT;
+}
+
+#endif /* MY_FAVORITE */
+
+
+#ifdef AUTHOR_EXTRACTION
+/* Thor.980818: ·Q§ï¦¨¥H¥Ø«eªº¬ÝªO¦Cªí©Î¤ÀÃþ¨Ó§ä, ¤£­n§ä¥þ³¡ */
+
+
+/* opus.1127 : ­pµe­«¼g, ¥i extract author/title */
+
+
+static int
+XoAuthor(xo)
+ XO *xo;
+{
+ int chn, len, max, tag;
+ short *chp, *chead, *ctail;
+ BRD *brd;
+ char key[30], author[IDLEN + 1];
+ XO xo_a, *xoTmp;
+
+ vget(b_lines, 0, MSG_XYPOST1, key, 30, DOECHO);
+ vget(b_lines, 0, MSG_XYPOST2, author, IDLEN + 1, DOECHO);
+
+ if (!*key && !*author)
+ return XO_FOOT;
+
+ str_lowest(key, key);
+ str_lower(author, author);
+ len = strlen(author);
+
+ chead = (short *) xo->xyz;
+ max = xo->max;
+ ctail = chead + max;
+
+ tag = 0;
+ chp = (short *) malloc(max * sizeof(short));
+ brd = bshm->bcache;
+
+ do
+ {
+ if ((chn = *chead++) >= 0) /* Thor.980818: ¤£¬° group */
+ {
+ /* Thor.980701: ´M§ä«ü©w§@ªÌ¤å³¹, ¦³«h²¾¦ì¸m, ¨Ã©ñ¤J */
+
+ int fsize;
+ char *fimage;
+
+ char folder[80];
+ HDR *head, *tail;
+
+ sprintf(folder, "¡m´M§ä«ü©w¼ÐÃD§@ªÌ¡n¬ÝªO¡G%s \033[5m...\033[m", brd[chn].brdname);
+ outz(folder);
+ refresh();
+ brd_fpath(folder, brd[chn].brdname, fn_dir);
+
+ fimage = f_map(folder, &fsize);
+
+ if (fimage == (char *) -1)
+ continue;
+
+ head = (HDR *) fimage;
+ tail = (HDR *) (fimage + fsize);
+
+ while (head <= --tail)
+ {
+ if ((!*key || str_sub(tail->title, key)) &&
+ (!len || !str_ncmp(tail->owner, author, len)))
+ {
+ xo_get(folder)->pos = tail - head;
+ chp[tag++] = chn;
+ break;
+ }
+ }
+
+ munmap(fimage, fsize);
+ }
+ } while (chead < ctail);
+
+ if (!tag)
+ {
+ free(chp);
+ vmsg("ªÅµL¤@ª«");
+ return XO_FOOT;
+ }
+
+ xo_a.pos = xo_a.top = 0;
+ xo_a.max = tag;
+ xo_a.key = 1; /* all boards */
+ /* Thor.990621: ©Ò¦³ªºclass,board¦Cªí¤U, key < 0, ¥H 1 »P¥¿±`¼Ò¦¡°Ï¤À
+ ¨Ï¨ä¤£¯à¶] XO_INIT(ùØ­±ªºclass_load), ¦p class_yank,
+ °£¤F¨¾¤î§ä¥Xªº§@ªÌ¬ÝªO¦Cªí®ø¥¢, ¤]¨¾½ð¤H */
+ xo_a.xyz = (char *) chp;
+
+ xoTmp = xz[XZ_CLASS - XO_ZONE].xo; /* Thor.980701: °O¤U­ì¨Óªºclass_xo */
+ xz[XZ_CLASS - XO_ZONE].xo = &xo_a;
+
+#ifdef AUTO_JUMPBRD
+ if (cuser.ufo & UFO_JUMPBRD)
+ class_jumpnext = 1; /* itoc.010910: ¥D°Ê¸õ¥h¤U¤@­Ó¥¼Åª¬ÝªO */
+#endif
+ xover(XZ_CLASS);
+
+ free(chp);
+ xz[XZ_CLASS - XO_ZONE].xo = xoTmp; /* Thor.980701: ÁÙ­ì class_xo */
+
+ return class_body(xo);
+}
+#endif
+
+
+static int class_binfo(xo)
+ XO *xo;
+{
+ BRD *brd;
+ short *chp;
+ int chn;
+ chp = (short *) xo->xyz + xo->pos;
+ chn = *chp;
+ brd = bshm->bcache + chn;
+ do_binfo(brd);
+ return class_init(xo);
+}
+static KeyFunc class_cb[] =
+{
+ XO_INIT, class_head,
+ XO_LOAD, class_body,
+ XO_HEAD, class_head,
+ XO_BODY, class_body,
+
+ 'r', class_browse,
+ '/', class_search,
+ '?', class_searchBM,
+ 's', class_switch,
+ 'c', class_postmode,
+ 'S', class_namemode,
+
+ 'y', class_yank,
+ 'z', class_zap,
+ 'Z', class_zapall,
+ 'v', class_visit,
+ 'V', class_unvisit,
+ '`', class_nextunread,
+ 'E', class_edit,
+ 'W', class_binfo,
+
+#ifdef AUTHOR_EXTRACTION
+ 'A', XoAuthor,
+#endif
+
+#ifdef MY_FAVORITE
+ 'a', class_addMF,
+ 'f', class_addMF,
+#endif
+
+ Ctrl('P'), class_newbrd,
+
+ 'h', class_help
+};
+
+
+int
+Class()
+{
+ /* XoClass(CH_END - 1); */
+ /* Thor.980804: ¨¾¤î ¥¼¥Î account ³y¥X class.img ©Î¨S¦³ class ªº±¡ªp */
+ if (!class_img || !XoClass(CH_END - 1))
+ {
+ vmsg("¥¼©w¸q¤À²Õ°Q½×°Ï");
+ return XEASY;
+ }
+ return 0;
+}
+
+
+void
+board_main()
+{
+ int fsize;
+
+ brh_load();
+
+ if (class_img) /* itoc.030416: ²Ä¤G¦¸¶i¤J board_main ®É¡A­n free ±¼ class_img */
+ {
+ free(class_img);
+ }
+ else /* itoc.040102: ²Ä¤@¦¸¶i¤J board_main ®É¡A¤~»Ý­nªì©l¤Æ class_flag */
+ {
+ class_flag = cuser.ufo & UFO_BRDPOST; /* ¬ÝªO¦Cªí 1:¤å³¹¼Æ 0:½s¸¹ */
+ if (!cuser.userlevel) /* guest yank all boards */
+ class_flag |= BFO_YANK;
+
+ /* ³]©w default board */
+ strcpy(currboard, BN_NULL);
+ currbno = -1;
+ }
+
+ /* class_img = f_img(CLASS_IMGFILE, &fsize); */
+ /* itoc.010413: ¨Ì·Ó ufo ¨Ó¸ü¤J¤£¦Pªº class image */
+ class_img = f_img(cuser.ufo & UFO_BRDNAME ? CLASS_IMGFILE_NAME : CLASS_IMGFILE_TITLE, &fsize);
+
+ if (class_img == NULL)
+ blog("CACHE", "class.img");
+ else
+ czh_load();
+
+ board_xo.key = CH_END;
+ class_load(&board_xo);
+
+ xz[XZ_CLASS - XO_ZONE].xo = &board_xo; /* Thor: default class_xo */
+ xz[XZ_CLASS - XO_ZONE].cb = class_cb; /* Thor: default class_xo */
+}
+
+
+int
+Boards()
+{
+ /* class_xo = &board_xo; *//* Thor: ¤w¦³ default, ¤£»Ý§@¦¹ */
+
+#ifdef AUTO_JUMPBRD
+ if (cuser.ufo & UFO_JUMPBRD)
+ class_jumpnext = 1; /* itoc.010910: ¥D°Ê¸õ¥h¤U¤@­Ó¥¼Åª¬ÝªO */
+#endif
+ xover(XZ_CLASS);
+
+ return 0;
+}
diff --git a/maple/cache.c b/maple/cache.c
new file mode 100644
index 0000000..91164ef
--- /dev/null
+++ b/maple/cache.c
@@ -0,0 +1,589 @@
+/*-------------------------------------------------------*/
+/* cache.c ( NTHU CS MapleBBS Ver 2.36 ) */
+/*-------------------------------------------------------*/
+/* target : cache up data by shared memory */
+/* create : 95/03/29 */
+/* update : 95/12/15 */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+#include <sys/ipc.h>
+#include <sys/shm.h>
+
+
+#ifdef HAVE_SEM
+#include <sys/sem.h>
+#endif
+
+
+#ifdef MODE_STAT
+UMODELOG modelog;
+time_t mode_lastchange;
+#endif
+
+
+#ifdef HAVE_SEM
+
+/* ----------------------------------------------------- */
+/* semaphore : for critical section */
+/* ----------------------------------------------------- */
+
+
+static int ap_semid;
+
+
+static void
+attach_err(shmkey)
+ int shmkey;
+{
+ char buf[20];
+
+ sprintf(buf, "key = %x", shmkey);
+ blog("shmget", buf);
+ exit(1);
+}
+
+
+void
+sem_init()
+{
+ int semid;
+
+ union semun
+ {
+ int val;
+ struct semid_ds *buf;
+ ushort *array;
+ } arg =
+ {
+ 1
+ };
+
+ semid = semget(BSEM_KEY, 1, 0);
+ if (semid == -1)
+ {
+ semid = semget(BSEM_KEY, 1, IPC_CREAT | BSEM_FLG);
+ if (semid == -1)
+ attach_err(BSEM_KEY);
+ semctl(semid, 0, SETVAL, arg);
+ }
+ ap_semid = semid;
+}
+
+
+void
+sem_lock(op)
+ int op; /* op is BSEM_ENTER or BSEM_LEAVE */
+{
+ struct sembuf sops;
+
+ sops.sem_num = 0;
+ sops.sem_flg = SEM_UNDO;
+ sops.sem_op = op;
+ semop(ap_semid, &sops, 1);
+}
+
+
+#endif /* HAVE_SEM */
+
+
+/*-------------------------------------------------------*/
+/* .UTMP cache */
+/*-------------------------------------------------------*/
+
+
+UCACHE *ushm;
+
+
+void
+ushm_init()
+{
+ ushm = shm_new(UTMPSHM_KEY, sizeof(UCACHE));
+}
+
+
+void
+utmp_mode(mode)
+ int mode;
+{
+ if (bbsmode != mode)
+ {
+#ifdef MODE_STAT
+ time_t now;
+
+ time(&now);
+ modelog.used_time[bbsmode] += (now - mode_lastchange);
+ mode_lastchange = now;
+#endif
+
+ cutmp->mode = bbsmode = mode;
+ }
+}
+
+
+int
+utmp_new(up)
+ UTMP *up;
+{
+ UCACHE *xshm;
+ UTMP *uentp, *utail;
+
+ /* --------------------------------------------------- */
+ /* semaphore : critical section */
+ /* --------------------------------------------------- */
+
+#ifdef HAVE_SEM
+ sem_lock(BSEM_ENTER);
+#endif
+
+ xshm = ushm;
+ uentp = xshm->uslot;
+ utail = uentp + MAXACTIVE;
+
+ /* uentp += (up->pid % xshm->count); */ /* hashing */
+
+ do
+ {
+ if (!uentp->pid)
+ {
+ usint offset;
+
+ offset = (void *) uentp - (void *) xshm->uslot;
+ memcpy(uentp, up, sizeof(UTMP));
+ xshm->count++;
+ if (xshm->offset < offset)
+ xshm->offset = offset;
+ cutmp = uentp;
+
+#ifdef HAVE_SEM
+ sem_lock(BSEM_LEAVE);
+#endif
+
+ return 1;
+ }
+ } while (++uentp < utail);
+
+ /* Thor:§i¶Duser¦³¤Hµn¥ý¤@¨B¤F */
+
+#ifdef HAVE_SEM
+ sem_lock(BSEM_LEAVE);
+#endif
+
+ return 0;
+}
+
+
+void
+utmp_free(up)
+ UTMP *up;
+{
+ if (!up || !up->pid)
+ return;
+
+#ifdef HAVE_SEM
+ sem_lock(BSEM_ENTER);
+#endif
+
+ up->pid = up->userno = 0;
+ ushm->count--;
+
+#ifdef HAVE_SEM
+ sem_lock(BSEM_LEAVE);
+#endif
+}
+
+
+UTMP *
+utmp_find(userno)
+ int userno;
+{
+ UTMP *uentp, *uceil;
+
+ uentp = ushm->uslot;
+ uceil = (void *) uentp + ushm->offset;
+ do
+ {
+ if (uentp->userno == userno)
+ return uentp;
+ } while (++uentp <= uceil);
+
+ return NULL;
+}
+
+
+UTMP *
+utmp_get(userno, userid) /* itoc.010306: Àˬd¨Ï¥ÎªÌ¬O§_¦b¯¸¤W */
+ int userno;
+ char *userid;
+{
+ UTMP *uentp, *uceil;
+ int seecloak;
+#ifdef HAVE_SUPERCLOAK
+ int seesupercloak;
+#endif
+
+ /* itoc.020718.µù¸Ñ: ¥Ñ©ó¦P¤@­¶ªº¦P¼Ë§@ªÌªº¾÷²v¹ê¦b¤Ó°ª¡A¦Ò¼{¬O§_°O¤U¬d¸ßµ²ªG¡A
+ µM«á¥ý¬d¥ý«eªº°O¿ý¡A­Y§ä¤£¨ì¦A¥h¨Ï¥ÎªÌ¦W³æ§ä */
+
+ seecloak = HAS_PERM(PERM_SEECLOAK);
+#ifdef HAVE_SUPERCLOAK
+ seesupercloak = cuser.ufo & UFO_SUPERCLOAK;
+#endif
+ uentp = ushm->uslot;
+ uceil = (void *) uentp + ushm->offset;
+ do
+ {
+ if (uentp->pid && /* ¤w¸gÂ÷¯¸ªº¤£Àˬd */
+ ((userno && uentp->userno == userno) || (userid && !strcmp(userid, uentp->userid))))
+ {
+ if (!seecloak && (uentp->ufo & UFO_CLOAK)) /* Áô§Î¬Ý¤£¨£ */
+ continue;
+
+#ifdef HAVE_SUPERCLOAK
+ if (!seesupercloak && (uentp->ufo & UFO_SUPERCLOAK)) /* µµÁô¬Ý¤£¨£ */
+ continue;
+#endif
+
+#ifdef HAVE_BADPAL
+ if (!seecloak && is_obad(uentp)) /* ³Q³]Ãa¤H¡A³s§O­Ó multi-login ¤]¬Ý¤£¨£ */
+ break;
+#endif
+
+ return uentp;
+ }
+ } while (++uentp <= uceil);
+
+ return NULL;
+}
+
+
+UTMP *
+utmp_seek(hdr) /* itoc.010306: Àˬd¨Ï¥ÎªÌ¬O§_¦b¯¸¤W */
+ HDR *hdr;
+{
+ if (hdr->xmode & POST_INCOME) /* POST_INCOME ©M MAIL_INCOME ¬O¬Û¦Pªº */
+ return NULL;
+ return utmp_get(0, hdr->owner);
+}
+
+
+void
+utmp_admset(userno, status) /* itoc.010811: °ÊºA³]©w½u¤W¨Ï¥ÎªÌ */
+ int userno;
+ usint status;
+{
+ UTMP *uentp, *uceil;
+ extern int ulist_userno[];
+
+ uentp = ushm->uslot;
+ uceil = (void *) uentp + ushm->offset;
+ do
+ {
+ if (uentp->userno == userno)
+ uentp->status |= status; /* ¥[¤W¸ê®Æ³QÅܰʹLªººX¼Ð */
+
+ /* itoc.041211: ·í§Ú§â¹ï¤è·s¼W/²¾°£/ÅܰʪB¤Í®É¡A
+ °£¤F­nÀ°¥L¥[¤W STATUS_PALDIRTY (¥ç§Y¥s¥L­«·s§PÂ_¥L¦Û¤vªº ulist_ftype[¥þ³¡])¡A
+ ÁÙ­n§â§Ú ulist_userno[¹ï¤è] Åܦ¨ 0 (¥ç§Y¥s§Ú¦Û¤v§ó·s ulist_ftype[¹ï¤è]) */
+ if (status == STATUS_PALDIRTY)
+ ulist_userno[uentp - ushm->uslot] = 0;
+ } while (++uentp <= uceil);
+}
+
+
+int
+utmp_count(userno, show)
+ int userno;
+ int show;
+{
+ UTMP *uentp, *uceil;
+ int count;
+
+ count = 0;
+ uentp = ushm->uslot;
+ uceil = (void *) uentp + ushm->offset;
+ do
+ {
+ if (uentp->userno == userno)
+ {
+ count++;
+ if (show)
+ {
+ prints("(%d) ¥Ø«eª¬ºA¬°: %-17.16s(¨Ó¦Û %s)\n",
+ count, bmode(uentp, 0), uentp->from);
+ }
+ }
+ } while (++uentp <= uceil);
+ return count;
+}
+
+
+UTMP *
+utmp_search(userno, order)
+ int userno;
+ int order; /* ²Ä´X­Ó */
+{
+ UTMP *uentp, *uceil;
+
+ uentp = ushm->uslot;
+ uceil = (void *) uentp + ushm->offset;
+ do
+ {
+ if (uentp->userno == userno)
+ {
+ if (--order <= 0)
+ return uentp;
+ }
+ } while (++uentp <= uceil);
+ return NULL;
+}
+
+
+#if 0
+int
+apply_ulist(fptr)
+ int (*fptr) ();
+{
+ UTMP *uentp;
+ int i, state;
+
+ uentp = ushm->uslot;
+ for (i = 0; i < USHM_SIZE; i++, uentp++)
+ {
+ if (uentp->pid)
+ if (state = (*fptr) (uentp))
+ return state;
+ }
+ return 0;
+}
+#endif
+
+
+/*-------------------------------------------------------*/
+/* .BRD cache */
+/*-------------------------------------------------------*/
+
+
+BCACHE *bshm;
+
+
+void
+bshm_init()
+{
+ int i;
+
+ /* itoc.030727: ¦b¶}±Ò bbsd ¤§«e¡AÀ³¸Ó´N­n°õ¦æ¹L account¡A
+ ©Ò¥H bshm À³¸Ó¤w³]©w¦n */
+
+ bshm = shm_new(BRDSHM_KEY, sizeof(BCACHE));
+
+ i = 0;
+ while (bshm->uptime <= 0) /* bshm ¥¼³]©w§¹¦¨¡A¤]³\¬O¥¼¶] account¡A¤]³\¬O¯¸ªø¥¿¦n¦b¶}ªO */
+ {
+ sleep(5);
+ if (++i >= 6) /* ­Y 30 ¬í¥H«áÁÙ¨S¦n¡AÂ_½uÂ÷¶} */
+ abort_bbs();
+ }
+}
+
+
+void
+bshm_reload() /* ¶}ªO¥H«á¡A­«·s¸ü¤J bshm */
+{
+ time_t *uptime;
+ int fd;
+ BRD *head, *tail;
+
+ uptime = &(bshm->uptime);
+
+ while (*uptime <= 0)
+ {
+ /* ¨ä¥L¯¸ªø¤]­è¦n¦b¶}ªO¡Aµ¥«Ý 30 ¬í */
+ sleep(30);
+ }
+
+ *uptime = -1; /* ¶}©l³]©w */
+
+ if ((fd = open(FN_BRD, O_RDONLY)) >= 0)
+ {
+ bshm->number = read(fd, bshm->bcache, MAXBOARD * sizeof(BRD)) / sizeof(BRD);
+ close(fd);
+ }
+
+ /* µ¥©Ò¦³ boards ¸ê®Æ§ó·s«á¦A³]©w uptime */
+ time(uptime);
+
+ /* itoc.040314: ªO¥D§ó§ï¬ÝªO±Ô­z©Î¬O¯¸ªø§ó§ï¬ÝªO®É¤~·|§â bpost/blast ¼g¶i .BRD ¤¤
+ ©Ò¥H .BRD ¸Ìªº bpost/blast ¥¼¥²¬O¹ïªº¡A­n­«·s initial¡C
+ initial ªº¤èªk¬O±N btime ³]¦¨ -1¡AÅý class_item() ¥h§ó·s */
+ head = bshm->bcache;
+ tail = head + bshm->number;
+ do
+ {
+ head->btime = -1;
+ } while (++head < tail);
+
+ blog("CACHE", "reload bcache");
+}
+
+
+#if 0
+int
+apply_boards(func)
+ int (*func) ();
+{
+ extern char brd_bits[];
+ BRD *bhdr;
+ int i;
+
+ for (i = 0, bhdr = bshm->bcache; i < bshm->number; i++, bhdr++)
+ {
+ if (brd_bits[i])
+ {
+ if ((*func) (bhdr) == -1)
+ return -1;
+ }
+ }
+ return 0;
+}
+#endif
+
+
+static int
+brdname_cmp(a, b)
+ BRD *a, *b;
+{
+ return str_cmp(a->brdname, b->brdname);
+}
+
+
+int
+brd_bno(bname)
+ char *bname;
+{
+ BRD xbrd, *bcache, *brdp, *bend;
+
+ bcache = bshm->bcache;
+
+ /* ¥ý¦b¬ݪO binary serach */
+
+ /* str_ncpy(xbrd.brdname, bname, sizeof(xbrd.brdname)); */
+ str_lower(xbrd.brdname, bname); /* ª½±µ´«¤p¼g¡A³o¼Ë¦b brdname_cmp() ®É·|§Ö¤@¨Ç */
+ if (bend = bsearch(&xbrd, bcache, bshm->numberOld, sizeof(BRD), brdname_cmp))
+ return bend - bcache;
+
+ /* ­Y§ä¤£¨ì¡A¦A¥h·s¬ÝªO sequential search */
+
+ brdp = bcache + bshm->numberOld;
+ bend = bcache + bshm->number;
+
+ while (brdp < bend)
+ {
+ if (!str_cmp(bname, brdp->brdname))
+ return brdp - bcache;
+
+ brdp++;
+ }
+
+ return -1;
+}
+
+
+/*-------------------------------------------------------*/
+/* movie cache */
+/*-------------------------------------------------------*/
+
+
+FCACHE *fshm;
+
+
+void
+fshm_init()
+{
+ fshm = shm_new(FILMSHM_KEY, sizeof(FCACHE));
+}
+
+
+/* ----------------------------------------------------- */
+/* itoc.020822.µù¸Ñ: */
+/* ----------------------------------------------------- */
+/* ²Ä 0 ¡ã FILM_MOVIE-1 ±i¬O¨t²Îµe­±¤Î»¡©úµe­± */
+/* ²Ä FILM_MOVIE ¡ã fmax-1 ±i¬O°ÊºA¬ÝªO */
+/* ----------------------------------------------------- */
+/* tag: */
+/* < FILM_MOVIE ¡÷ ¼½©ñ¸Ó±iµe­± */
+/* >= FILM_MOVIE ¡÷ ¶Ã¼Æ¼½©ñ FILM_MOVIE~fmax-1 ¨ä¤¤¤@±i */
+/* ----------------------------------------------------- */
+/* row: */
+/* >=0 ¡÷ ¨t²Îµe­±¡A±q (row, 0) ¶}©l¦L */
+/* <0 ¡÷ »¡©úµe­±¡A±q (0, 0) ¶}©l¦L¡A³Ì«á·| vmsg(NULL) */
+/* ----------------------------------------------------- */
+
+
+void
+film_out(tag, row)
+ int tag;
+ int row; /* -1 : help */
+{
+ int fmax, len, *shot;
+ char *film, buf[FILM_SIZ];
+
+ len = 0;
+ shot = fshm->shot;
+
+ while (!(fmax = *shot)) /* util/camera.c ¥¿¦b´«¤ù */
+ {
+ sleep(5);
+ if (++len >= 6) /* ­Y 30 ¬í¥H«áÁÙ¨S´«¦n¤ù¡A¥i¯à¬O¨S¶] camera¡Aª½±µÂ÷¶} */
+ return;
+ }
+
+ if (row <= 0)
+ clear();
+ else
+ move(row, 0);
+
+ if (tag >= FILM_MOVIE) /* °ÊºA¬ÝªO */
+ tag += time(0) % (fmax - FILM_MOVIE);
+
+ film = fshm->film;
+
+ if (tag)
+ {
+ len = shot[tag];
+ film += len;
+ len = shot[tag + 1] - len;
+ }
+ else
+ {
+ len = shot[1];
+ }
+
+ if (len >= FILM_SIZ - MOVIE_LINES)
+ return;
+
+ memcpy(buf, film, len);
+ buf[len] = '\0';
+
+ if (d_cols) /* waynesan.040831: ¨Ì¼e¿Ã¹õ¸m¤¤ */
+ {
+ char *ptr;
+ for (film = buf; *film;)
+ {
+ if (ptr = strchr(film, '\n'))
+ *ptr = '\0';
+ move(row++, (d_cols >> 1));
+ outx(film);
+ if (ptr)
+ film = ptr + 1;
+ }
+ }
+ else
+ outx(buf);
+
+ if (row < 0) /* help screen */
+ vmsg(NULL);
+
+ return;
+}
diff --git a/maple/edit.c b/maple/edit.c
new file mode 100644
index 0000000..0268123
--- /dev/null
+++ b/maple/edit.c
@@ -0,0 +1,2380 @@
+/*-------------------------------------------------------*/
+/* edit.c ( NTHU CS MapleBBS Ver 2.36 ) */
+/*-------------------------------------------------------*/
+/* target : simple ANSI/Chinese editor */
+/* create : 95/03/29 */
+/* update : 95/12/15 */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+
+/* #define VE_WIDTH (ANSILINELEN - 1) */
+/* Thor.990330: ¬°¨¾¤î¤Þ¨¥«á, ">"­nÅܦâ, ¤@¦æ·|¶W¹LANSILINELEN, ¬G¦h¯dªÅ¶¡ */
+/* itoc.010317.µù¸Ñ: ¨º»ò¹ê»Ú¤@¦C¥i¥H©ñ¤Uªº¦³®Ä¦r¼Æ¬° VE_WIDTH - 3 */
+#define VE_WIDTH (ANSILINELEN - 11)
+
+
+typedef struct textline
+{
+ struct textline *prev;
+ struct textline *next;
+ int len;
+ uschar data[ANSILINELEN];
+} textline;
+
+
+static textline *vx_ini; /* first line */
+static textline *vx_cur; /* current line */
+static textline *vx_top; /* top line in current window */
+
+
+static int ve_lno; /* current line number */
+static int ve_row; /* cursor position */
+static int ve_col;
+
+
+static int ve_mode; /* operation mode */
+
+
+#ifdef HAVE_MULTI_BYTE
+static int zhc; /* ¬O§_¦³ UFO_ZHC */
+#endif
+
+
+#ifdef HAVE_ANONYMOUS
+char anonymousid[IDLEN + 1]; /* itoc.010717: ¦Û©w°Î¦W ID */
+#endif
+
+
+#define VE_INSERT 0x01
+#define VE_ANSI 0x02
+#define VE_FOOTER 0x04
+#define VE_REDRAW 0x08
+
+#ifdef EVERY_BIFF
+#define VE_BIFF 0x10
+#endif /* Thor.980805: ¶l®t¨ì³B¨Ó«ö¹a */
+
+
+#define FN_BAK "bak"
+
+
+/* ----------------------------------------------------- */
+/* °O¾ÐÅéºÞ²z»P½s¿è³B²z */
+/* ----------------------------------------------------- */
+
+
+#ifdef DEBUG_VEDIT
+static void
+ve_abort(i)
+ int i;
+{
+ char msg[40];
+
+ sprintf(msg, "ÄY­«¤º¶Ë %d", i);
+ blog("VEDIT", msg);
+}
+
+#else
+
+#define ve_abort(n) ;
+#endif
+
+
+static void
+ve_position(cur, top)
+ textline *cur;
+ textline *top;
+{
+ int row;
+
+ row = cur->len;
+ if (ve_col > row)
+ ve_col = row;
+#ifdef HAVE_MULTI_BYTE
+ else if (zhc && ve_col < row && IS_ZHC_LO(cur->data, ve_col)) /* hightman.060504: º~¦r¾ã¦r½Õ¸` */
+ ve_col++;
+#endif
+
+ row = 0;
+ while (cur != top)
+ {
+ row++;
+ cur = cur->prev;
+ }
+ ve_row = row;
+
+ ve_mode |= VE_REDRAW;
+}
+
+
+static inline void
+ve_pageup()
+{
+ textline *cur, *top, *tmp;
+ int lno, n;
+
+ cur = vx_cur;
+ top = vx_top;
+ lno = ve_lno;
+ for (n = PAGE_SCROLL; n > 0; n--)
+ {
+ if (!(tmp = cur->prev))
+ break;
+
+ cur = tmp;
+ lno--;
+
+ if (tmp = top->prev)
+ top = tmp;
+ }
+
+ vx_cur = cur;
+ vx_top = top;
+ ve_lno = lno;
+
+ ve_position(cur, top);
+}
+
+
+static inline void
+ve_forward(n)
+ int n;
+{
+ textline *cur, *top, *tmp;
+ int lno;
+
+ cur = vx_cur;
+ top = vx_top;
+ lno = ve_lno;
+ while (n--)
+ {
+ if (!(tmp = cur->next))
+ break;
+
+ lno++;
+ cur = tmp;
+
+ if (tmp = top->next)
+ top = tmp;
+ }
+
+ vx_cur = cur;
+ vx_top = top;
+ ve_lno = lno;
+
+ ve_position(cur, top);
+}
+
+
+static inline char *
+ve_strim(s)
+ char *s;
+{
+ while (*s == ' ')
+ s++;
+ return s;
+}
+
+
+static textline *
+ve_alloc()
+{
+ textline *p;
+
+ if (p = (textline *) malloc(sizeof(textline)))
+ {
+ p->prev = NULL;
+ p->next = NULL;
+ p->len = 0;
+ p->data[0] = '\0';
+ return p;
+ }
+
+ ve_abort(13); /* °O¾ÐÅé¥Î¥ú¤F */
+ abort_bbs();
+}
+
+
+/* ----------------------------------------------------- */
+/* Thor: ansi ®y¼ÐÂà´« for color ½s¿è¼Ò¦¡ */
+/* ----------------------------------------------------- */
+
+
+static int
+ansi2n(ansix, line)
+ int ansix;
+ textline *line;
+{
+ uschar *data, *tmp;
+ int ch;
+
+ data = tmp = line->data;
+
+ while (ch = *tmp)
+ {
+ if (ch == KEY_ESC)
+ {
+ for (;;)
+ {
+ ch = *++tmp;
+ if (ch >= 'a' && ch <= 'z' /* isalpha(ch) */ )
+ {
+ tmp++;
+ break;
+ }
+ if (!ch)
+ break;
+ }
+ continue;
+ }
+ if (ansix <= 0)
+ break;
+ tmp++;
+ ansix--;
+ }
+ return tmp - data;
+}
+
+
+static int
+n2ansi(nx, line)
+ int nx;
+ textline *line;
+{
+ uschar *tmp, *nxp;
+ int ansix;
+ int ch;
+
+ tmp = nxp = line->data;
+ nxp += nx;
+ ansix = 0;
+
+ while (ch = *tmp)
+ {
+ if (ch == KEY_ESC)
+ {
+ for (;;)
+ {
+ ch = *++tmp;
+ if (ch >= 'a' && ch <= 'z' /* isalpha(ch) */ )
+ {
+ tmp++;
+ break;
+ }
+ if (!ch)
+ break;
+ }
+ continue;
+ }
+ if (tmp >= nxp)
+ break;
+ tmp++;
+ ansix++;
+ }
+ return ansix;
+}
+
+
+/* ----------------------------------------------------- */
+/* delete_line deletes 'line' from the list, */
+/* and maintains the vx_ini pointers. */
+/* ----------------------------------------------------- */
+
+
+static void
+delete_line(line)
+ textline *line;
+{
+ textline *p = line->prev;
+ textline *n = line->next;
+
+ if (p || n)
+ {
+ if (n)
+ n->prev = p;
+
+ if (p)
+ p->next = n;
+ else
+ vx_ini = n;
+
+ free(line);
+ }
+ else
+ {
+ line->data[0] = line->len = 0;
+ }
+}
+
+
+/* ----------------------------------------------------- */
+/* split 'line' right before the character pos */
+/* ----------------------------------------------------- */
+
+
+static void
+ve_split(line, pos)
+ textline *line;
+ int pos;
+{
+ int len = line->len - pos;
+
+ if (len >= 0)
+ {
+ textline *p, *n;
+ uschar *ptr;
+
+ line->len = pos;
+ p = ve_alloc();
+ p->len = len;
+ strcpy(p->data, (ptr = line->data + pos));
+ *ptr = '\0';
+
+ /* --------------------------------------------------- */
+ /* append p after line in list. keep up with last line */
+ /* --------------------------------------------------- */
+
+ if (p->next = n = line->next)
+ n->prev = p;
+ line->next = p;
+ p->prev = line;
+
+ if (line == vx_cur && pos <= ve_col)
+ {
+ vx_cur = p;
+ ve_col -= pos;
+ ve_row++;
+ ve_lno++;
+ }
+ ve_mode |= VE_REDRAW;
+ }
+}
+
+
+/* ----------------------------------------------------- */
+/* connects 'line' and the next line. returns true if: */
+/* 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
+ve_join(line)
+ textline *line;
+{
+ textline *n;
+ uschar *data, *s;
+ int sum, len;
+
+ if (!(n = line->next))
+ return 1;
+
+ if (!*ve_strim(data = n->data))
+ return 1;
+
+ len = line->len;
+ sum = len + n->len;
+ if (sum < VE_WIDTH)
+ {
+ strcpy(line->data + len, data);
+ line->len = sum;
+ delete_line(n);
+ return 1;
+ }
+
+ s = data - len + VE_WIDTH - 1;
+ while (*s == ' ' && s != data)
+ s--;
+ while (*s != ' ' && s != data)
+ s--;
+ if (s == data)
+ return 1;
+
+ ve_split(n, (s - data) + 1);
+ if (len + n->len >= VE_WIDTH)
+ {
+ ve_abort(0);
+ return 1;
+ }
+
+ ve_join(line);
+ n = line->next;
+ len = n->len;
+ if (len >= 1 && len < VE_WIDTH - 1)
+ {
+ s = n->data + len - 1;
+ if (*s != ' ')
+ {
+ *s++ = ' ';
+ *s = '\0';
+ n->len = len + 2;
+ }
+ }
+ return 0;
+}
+
+
+static void
+join_up(line)
+ textline *line;
+{
+ while (!ve_join(line))
+ {
+ line = line->next;
+ if (line == NULL)
+ {
+ ve_abort(2);
+ abort_bbs();
+ }
+ }
+}
+
+
+/* ----------------------------------------------------- */
+/* character insert / detete */
+/* ----------------------------------------------------- */
+
+
+static void
+ve_char(ch)
+ int ch;
+{
+ textline *p;
+ int col, len, mode;
+ uschar *data;
+
+ p = vx_cur;
+ len = p->len;
+ col = ve_col;
+
+ if (col > len)
+ {
+ ve_abort(1);
+ return;
+ }
+
+ data = p->data;
+ mode = ve_mode;
+
+ /* --------------------------------------------------- */
+ /* overwrite */
+ /* --------------------------------------------------- */
+
+ if ((col < len) && !(mode & VE_INSERT))
+ {
+ data[col++] = ch;
+
+ /* Thor: ansi ½s¿è, ¥i¥H overwrite, ¤£»\¨ì ansi code */
+
+ if (mode & VE_ANSI)
+ col = ansi2n(n2ansi(col, p), p);
+
+ ve_col = col;
+ return;
+ }
+
+ /* --------------------------------------------------- */
+ /* insert / append */
+ /* --------------------------------------------------- */
+
+ for (mode = len; mode >= col; mode--)
+ {
+ data[mode + 1] = data[mode];
+ }
+ data[col++] = ch;
+ ve_col = col;
+ p->len = ++len;
+
+ if (len >= VE_WIDTH - 2)
+ {
+ /* Thor.980727: ­×¥¿ editor buffer overrun °ÝÃD, ¨£«á */
+
+ ve_split(p, VE_WIDTH - 3);
+
+#if 0
+ uschar *str = data + len;
+
+ while (*--str == ' ')
+ {
+ if (str == data)
+ break;
+ }
+
+ ve_split(p, (str - data) + 1);
+#endif
+
+
+
+#if 0
+ §@ªÌ yvb (yvb) ¬ÝªO SYSOP
+ ¼ÐÃD Ãö©ó editor...
+ ®É¶¡ Sun Jun 28 11:28:02 1998
+¢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
+
+ post ¤Î mail µ¥ªº³o­Ó editor, ¦pªG§A¤@ª½©¹«á­±¥´¦r,
+ ³Ì¦h¥i¥H¥´¨ì¤@¦C 157 ¦r§a... ¦A¥´´N·|³Q­¢´«¦æ.
+
+ ¤£¹L¦pªG§A°í«ù¤´­n¦b«á­±¥[¦r... ¤]´N¬O¦A²¾¦^­è¤~¨º¦æ
+ Ä~Äò¥´¦r... ¨º¨t²Î¤´·|Åý§A¦b¸Ó¦CÄ~Äò¥[¤@­Ó¦r¥À, ¨Ã¥B
+ ´«¥X·sªº¤@¦æ¨Ó...
+
+ ­«Âгo­Ó¨BÆJ, ¨ì²Ä 170 ¦r®É, §A´N·|³QÂ_½u¤F...
+ ¨þ... °÷ÅܺA§a :P
+
+ ¨ä¹ê, ±q¥t¤@­ÓÆ[ÂI¨Ó»¡, ­Y³o¤£¬O¨t²Î¯S·N³o¼Ë¤l³]­p,
+ ¨º´Nªí¥Ü³oùئü¥G¼çÂõۥiÂÇ¥Ñ buffer overrun ªº¤è¦¡,
+ ¥i¯à¹F¦¨¤J«I¨t²Îªº¦M¾÷...
+--
+¡° ¨Ó·½: ¤ë¥ú´ËªL ¡» From: bamboo.Dorm6.NCTU.edu.tw
+
+#endif
+
+ }
+}
+
+
+static void
+delete_char(cur, col)
+ textline *cur;
+ int col;
+{
+ uschar *dst, *src;
+
+ cur->len--;
+ dst = cur->data + col;
+ for (;;)
+ {
+ src = dst + 1;
+ if (!(*dst = *src))
+ break;
+ dst = src;
+ }
+}
+
+
+static void
+ve_string(str)
+ uschar *str;
+{
+ int ch;
+
+ while (ch = *str)
+ {
+ if (isprint2(ch) || ch == KEY_ESC)
+ {
+ ve_char(ch);
+ }
+ else if (ch == '\t')
+ {
+ do
+ {
+ ve_char(' ');
+ } while (ve_col & TAB_WIDTH);
+ }
+ else if (ch == '\n')
+ {
+ ve_split(vx_cur, ve_col);
+ }
+ str++;
+ }
+}
+
+
+static void
+ve_ansi()
+{
+ int fg, bg, mode;
+ char ans[4], buf[16], *apos, *color, *tmp;
+ static char t[] = "BRGYLPCW";
+
+ mode = ve_mode | VE_REDRAW;
+ color = str_ransi;
+
+ if (mode & VE_ANSI)
+ {
+ move(b_lines - 1, 55);
+ outs("\033[1;33;40mB\033[41mR\033[42mG\033[43mY\033[44mL\033[45mP\033[46mC\033[47mW\033[m");
+ if (fg = vget(b_lines, 0, "½Ð¿é¤J «G«×/«e´º/­I´º[¥¿±`¥Õ¦r¶Â©³][0wb]¡G",
+ apos = ans, 4, LCECHO))
+ {
+ color = buf;
+ strcpy(color, "\033[");
+ if (isdigit(fg))
+ {
+ sprintf(color, "%s%c", color, *(apos++));
+ if (*apos)
+ strcat(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);
+ }
+ strcat(color, "m");
+ }
+ }
+
+ ve_mode = mode | VE_INSERT;
+ ve_string(color);
+ ve_mode = mode;
+}
+
+
+static textline *
+ve_line(this, str)
+ textline *this;
+ uschar *str;
+{
+ int cc, len;
+ uschar *data;
+ textline *line;
+
+ do
+ {
+ line = ve_alloc();
+ data = line->data;
+ len = 0;
+
+ for (;;)
+ {
+ cc = *str;
+
+ if (cc == '\n')
+ cc = 0;
+ if (cc == 0)
+ break;
+
+ str++;
+
+ if (cc == '\t')
+ {
+ do
+ {
+ *data++ = ' ';
+ len++;
+ } while ((len & TAB_WIDTH) && (len < VE_WIDTH));
+ }
+ else if (cc < ' ' && cc != KEY_ESC)
+ {
+ continue;
+ }
+ else
+ {
+ *data++ = cc;
+ len++;
+ }
+ if (len >= VE_WIDTH)
+ break;
+ }
+
+ *data = '\0';
+ line->len = len;
+ line->prev = this;
+ this = this->next = line;
+
+ } while (cc);
+
+ return this;
+}
+
+
+/* ----------------------------------------------------- */
+/* Åã¥Üñ¦WÀÉ */
+/* ----------------------------------------------------- */
+
+
+static void
+show_sign(ch) /* itoc.000319: Åã¥Üñ¦WÀɪº¤º®e */
+ char ch;
+{
+ int fd, len, i;
+ char fpath[64], buf[10], *str;
+
+ clear();
+
+ sprintf(buf, "%s.0", FN_SIGN);
+ usr_fpath(fpath, cuser.userid, buf); /* itoc.020123: ¦U­Óñ¦WÀÉÀɮפÀ¶} */
+ len = strlen(fpath) - 1;
+
+
+ for (; ch <= '6'; ch++) /* ¤»­Óñ¦WÀÉ */
+ {
+ fpath[len] = ch;
+
+ fd = open(fpath, O_RDONLY);
+ if (fd >= 0)
+ {
+ mgets(-1);
+ move(((ch - '1') %3 ) * (MAXSIGLINES + 1), 0);
+ prints("\033[1;36m¡i ñ¦WÀÉ %c ¡j\033[m\n", ch);
+
+ for (i = 1; i <= MAXSIGLINES; i++)
+ {
+ if (!(str = mgets(fd)))
+ break;
+ prints("%s\n", str);
+ }
+
+ if(((ch -'0') % 3 ) == 0)
+ break;
+ }
+ }
+}
+
+
+/* ----------------------------------------------------- */
+/* ²Å¸¹¿é¤J¤u¨ã Input Tools */
+/* ----------------------------------------------------- */
+
+
+#ifdef INPUT_TOOLS
+static void
+input_tools() /* itoc.000319: ²Å¸¹¿é¤J¤u¨ã */
+{
+ char msg1[] = {"1.¬A²Å¤è¶ô 2.½u±øªí®Ø 3.¼Æ¾Ç²Å¸¹ (PgDn/N:¤U¤@­¶)¡H[Q] "};
+ char msg2[] = {"4.¹Ï®×¼Æ¦r 5.§ÆÃ¾¦r¥À 6.ª`­µ¼ÐÂI (PgUp/P:¤W¤@­¶)¡H[Q] "};
+
+ char ansi[6][100] =
+ {
+ { /* 1.¬A²Å¤è¶ô */
+ "¢b¢c¢d¢e¢f¢g¢h¢i¢¨¢©"
+ "¢j¢k¢l¢m¢n¢o¢p¢ª¢«¡¼"
+ "¡]¡^¡a¡b¡e¡f¡i¡j¡m¡n"
+ "¡q¡r¡u¡v¡y¡z¡k¡l¡o¡p"
+ "¡_¡`¡c¡d¡g¡h¡w¡x¡{¡|"
+ },
+
+ { /* 2.½u±øªí®Ø */
+ "ùïùðùñùõùöù÷¢w¢xùù¡ü"
+ "¢z¢s¢{¢u¢q¢t¢|¢r¢}¢®"
+ "ùÝùÞùßùàùáùâùãùäùå¡\\"
+ "ùæùèùéùëùìùî¢~¢¡¢¢¢£"
+ "¢b¢v¢j¢y¢¬¢­¡ö¡÷¡ô¡õ"
+ },
+
+ { /* 3.¼Æ¾Ç²Å¸¹ */
+ "¡Ï¡Ð¡Ñ¡Ò¡Ó¡×¡Õ¡Ö¡Ø¡Ù"
+ "¡Ú¡Ý¡ã¡î¡ï¡ä¡å¡Û£k¡Ô"
+ "£U£S¡ì¡í¡ç¡æ¡è¡é£G¢X"
+ "¡­¡®¡¯¡°¢I¢C¢D¢F¢G¢H"
+ "¢J¢K¢P¢Q¢R¢T¢U¢V¢W¡±"
+ },
+
+ { /* 4.¹Ï®×¼Æ¦r */
+ "¡³¡´¡µ¡¶¡·¡¸¡º¡»¡¼¡½"
+ "¡¾¡¿¡À¡ò¡ó¢Ì¢Í¢Î¡ñ¡ð"
+ "¢¯¢°¢±¢²¢³¢´¢µ¢¶¢·¢¸"
+ "¢¹¢º¢»¢¼¢½¢¾¢¿¢À¢Á¢Â"
+ "ƵƶƷƸƹƺƻƼƽƾ"
+ },
+
+ { /* 5.§ÆÃ¾¦r¥À */
+ "£D£E£F£G£H£I£J£K£L£M"
+ "£N£O£P£Q£R£S£T£U£V£W"
+ "£X£Y£Z£[£\\£]£^£_£`£a"
+ "£b£c£d£e£f£g£h£i£j£k"
+ "£l£m£n£o£p£q£r£s¢A¢B"
+ },
+
+ { /* 6.ª`­µ¼ÐÂI */
+ "£t£u£v£w£x£y£z£{£|£}"
+ "£~£¡£¢£££¤£¥£¦£§£¨£©"
+ "£ª£«£¬£­£®£¯£°£±£²£³"
+ "£´£µ£¶£·£¸£¹£º£½£¾£¿"
+ "£»¡A¡C¡B¡I¡H¡G¡F¡u¡v"
+ }
+ };
+
+ char buf[80], tmp[6];
+ char *ptr, *str;
+ int ch, page;
+
+ /* ¿ï¤ÀÃþ */
+ ch = KEY_PGUP;
+ do
+ {
+ outz("¤º½X¿é¤J¤u¨ã¡G");
+ outs((ch == KEY_PGUP || ch == 'P') ? msg1 : msg2);
+ ch = vkey();
+ } while (ch == KEY_PGUP || ch == 'P' || ch == KEY_PGDN || ch == 'N');
+
+ if (ch < '1' || ch > '6')
+ return;
+
+ ptr = ansi[ch - '1'];
+ page = 0;
+
+ for (;;)
+ {
+ buf[0] = '\0';
+ str = ptr + page * 20; /* ¨C page ¦³¤Q­Ó¤¤¤å¦r¡A¨C­Ó¤¤¤å¦r¬O 2 char */
+
+ for (ch = 0; ch < 10; ch++)
+ {
+ sprintf(tmp, "%d.%2.2s ", ch, str + ch * 2); /* ¨C­Ó¤¤¤å¦r¬O 2 char */
+ strcat(buf, tmp);
+ }
+ strcat(buf, "(PgUp/P:¤W PgDn/N:¤U)[Q] ");
+ outz(buf);
+ ch = vkey();
+
+ if (ch == KEY_PGUP || ch == 'P')
+ {
+ if (page)
+ page--;
+ }
+ else if (ch == KEY_PGDN || ch == 'N')
+ {
+ if (page != 4)
+ page++;
+ }
+ else if (ch < '0' || ch > '9')
+ {
+ break;
+ }
+ else
+ {
+ str += (ch - '0') * 2;
+ ve_char(*str); /* ¦L¥X¤G­Ó char ¤¤¤å¦r */
+ ve_char(*++str);
+ break;
+ }
+ }
+}
+#endif
+
+
+/* ----------------------------------------------------- */
+/* ¼È¦sÀÉ TBF (Temporary Buffer File) routines */
+/* ----------------------------------------------------- */
+
+
+char *
+tbf_ask()
+{
+ static char fn_tbf[] = "buf.1";
+ int ch;
+
+ do
+ {
+ ch = vget(b_lines, 0, "½Ð¿ï¾Ü¼È¦sÀÉ(1-5)¡G", fn_tbf + 4, 2, GCARRY);
+ } while (ch < '1' || ch > '5');
+ return fn_tbf;
+}
+
+
+FILE *
+tbf_open()
+{
+ int ans;
+ char fpath[64], op[4];
+ struct stat st;
+
+ usr_fpath(fpath, cuser.userid, tbf_ask());
+ ans = 'a';
+
+ if (!stat(fpath, &st))
+ {
+ ans = vans("¼È¦sÀɤw¦³¸ê®Æ (A)ªþ¥[ (W)Âмg (Q)¨ú®ø¡H[A] ");
+ if (ans == 'q')
+ return NULL;
+
+ if (ans != 'w')
+ {
+ /* itoc.030208: Àˬd¼È¦sÀɤj¤p */
+ if (st.st_size >= 100000) /* 100KB À³¸Ó°÷¤F */
+ {
+ zmsg("¼È¦sÀɤӤj¡AµLªkªþ¥[");
+ return NULL;
+ }
+
+ ans = 'a';
+ }
+ }
+
+ op[0] = ans;
+ op[1] = '\0';
+
+ return fopen(fpath, op);
+}
+
+
+static textline *
+ve_load(this, fd)
+ textline *this;
+ int fd;
+{
+ uschar *str;
+ textline *next;
+
+ next = this->next;
+
+ mgets(-1);
+ while (str = mgets(fd))
+ {
+ this = ve_line(this, str);
+ }
+
+ this->next = next;
+ if (next)
+ next->prev = this;
+
+ return this;
+}
+
+
+static inline void
+tbf_read()
+{
+ int fd;
+ char fpath[80];
+
+ usr_fpath(fpath, cuser.userid, tbf_ask());
+
+ fd = open(fpath, O_RDONLY);
+ if (fd >= 0)
+ {
+ ve_load(vx_cur, fd);
+ close(fd);
+ }
+}
+
+
+static inline void
+tbf_write()
+{
+ FILE *fp;
+ textline *p;
+ uschar *data;
+
+ if (fp = tbf_open())
+ {
+ for (p = vx_ini; p;)
+ {
+ data = p->data;
+ p = p->next;
+ if (p || *data)
+ fprintf(fp, "%s\n", data);
+ }
+ fclose(fp);
+ }
+}
+
+
+static inline void
+tbf_erase()
+{
+ char fpath[64];
+
+ usr_fpath(fpath, cuser.userid, tbf_ask());
+ unlink(fpath);
+}
+
+
+/* ----------------------------------------------------- */
+/* ½s¿è¾¹¦Û°Ê³Æ¥÷ */
+/* ----------------------------------------------------- */
+
+
+void
+ve_backup()
+{
+ textline *p, *n;
+
+ if (p = vx_ini)
+ {
+ FILE *fp;
+ char bakfile[64];
+
+ vx_ini = NULL;
+ usr_fpath(bakfile, cuser.userid, FN_BAK);
+ if (fp = fopen(bakfile, "w"))
+ {
+ do
+ {
+ n = p->next;
+ fprintf(fp, "%s\n", p->data);
+ free(p);
+ } while (p = n);
+ fclose(fp);
+ }
+ }
+}
+
+
+void
+ve_recover()
+{
+ char fpbak[80], fpath[80];
+ int ch;
+
+ usr_fpath(fpbak, cuser.userid, FN_BAK);
+ if (dashf(fpbak))
+ {
+ ch = vans("±z¦³¤@½g¤å³¹©|¥¼§¹¦¨¡A(M)±H¨ì«H½c (S)¼g¤J¼È¦sÀÉ (Q)ºâ¤F¡H[M] ");
+ if (ch == 's')
+ {
+ usr_fpath(fpath, cuser.userid, tbf_ask());
+ rename(fpbak, fpath);
+ return;
+ }
+ else if (ch != 'q')
+ {
+ mail_self(fpbak, cuser.userid, "¥¼§¹¦¨ªº¤å³¹", 0);
+ }
+ unlink(fpbak);
+ }
+}
+
+
+/* ----------------------------------------------------- */
+/* ¤Þ¥Î¤å³¹ */
+/* ----------------------------------------------------- */
+
+
+static int
+is_quoted(str)
+ char *str; /* "--\n", "-- \n", "--", "-- " */
+{
+ if (*str == '-')
+ {
+ if (*++str == '-')
+ {
+ if (*++str == ' ')
+ str++;
+ if (*str == '\n')
+ str++;
+ if (!*str)
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+static inline int
+quote_line(str, qlimit)
+ char *str;
+ int qlimit; /* ¤¹³\´X¼h¤Þ¨¥¡H */
+{
+ int qlevel = 0;
+ int ch;
+
+ while ((ch = *str) == QUOTE_CHAR1 || ch == QUOTE_CHAR2)
+ {
+ if (*(++str) == ' ')
+ str++;
+ if (qlevel++ >= qlimit)
+ return 0;
+ }
+ while ((ch = *str) == ' ' || ch == '\t')
+ str++;
+ if (qlevel >= qlimit)
+ {
+ if (!memcmp(str, "¡° ", 3) || !memcmp(str, "==>", 3) ||
+ strstr(str, ") ´£¨ì:\n"))
+ return 0;
+ }
+ return (*str != '\n');
+}
+
+
+static void
+ve_quote(this)
+ textline *this;
+{
+ int fd, op;
+ FILE *fp;
+ textline *next;
+ char *str, buf[ANSILINELEN];
+ static char msg[] = "¿ï¾Üñ¦WÀÉ (1~6 0=¤£¥[ r=¶Ã¼Æ n=´«­¶)[0]¡G";
+
+ next = this->next;
+
+ /* --------------------------------------------------- */
+ /* ¤Þ¨¥ */
+ /* --------------------------------------------------- */
+
+ if (*quote_file)
+ {
+ op = vans("¬O§_¤Þ¥Î­ì¤å Y)¤Þ¥Î N)¤£¤Þ¥Î A)¤Þ¥Î¥þ¤å R)­«¶K¥þ¤å 1-9)¤Þ¥Î¼h¼Æ¡H[Y] ");
+
+ if (op != 'n')
+ {
+ if (fp = fopen(quote_file, "r"))
+ {
+ str = buf;
+
+ if ((op >= '1') && (op <= '9'))
+ op -= '1';
+ else if ((op != 'a') && (op != 'r'))
+ op = 1; /* default : 2 level */
+
+ if (op != 'a') /* ¥h±¼ header */
+ {
+ if (*quote_nick)
+ sprintf(buf + 128, " (%s)", quote_nick);
+ else
+ buf[128] = '\0';
+ sprintf(str, "¡° ¤Þ­z¡m%s%s¡n¤§»Ê¨¥¡G", quote_user, buf + 128);
+ this = ve_line(this, str);
+
+ while (fgets(str, ANSILINELEN, fp) && *str != '\n');
+
+ if (curredit & EDIT_LIST) /* ¥h±¼ mail list ¤§ header */
+ {
+ while (fgets(str, ANSILINELEN, fp) && (!memcmp(str, "¡° ", 3)));
+ }
+ }
+
+ if (op == 'r')
+ {
+ op = 'a';
+ }
+ else
+ {
+ *str++ = QUOTE_CHAR1;
+ *str++ = ' ';
+ }
+
+ if (op == 'a')
+ {
+ while (fgets(str, ANSILINELEN - 2, fp)) /* ¯dªÅ¶¡µ¹ "> " */
+ this = ve_line(this, buf);
+ }
+ else
+ {
+ while (fgets(str, ANSILINELEN - 2, fp)) /* ¯dªÅ¶¡µ¹ "> " */
+ {
+ if (is_quoted(str)) /* "--\n" */
+ break;
+ if (quote_line(str, op))
+ this = ve_line(this, buf);
+ }
+ }
+ fclose(fp);
+ }
+ }
+ *quote_file = '\0';
+ }
+
+ this = ve_line(this, "");
+
+ /* --------------------------------------------------- */
+ /* ñ¦WÀÉ */
+ /* --------------------------------------------------- */
+
+#ifdef HAVE_ANONYMOUS
+ if (!(currbattr & BRD_ANONYMOUS) && !(cuser.ufo & UFO_NOSIGN)) /* ¦b°Î¦WªO¤¤µL½×¬O§_°Î¦W¡A§¡¤£¨Ï¥Îñ¦WÀÉ */
+#else
+ if (!(cuser.ufo & UFO_NOSIGN)) /* itoc.000320: ¤£¨Ï¥Îñ¦WÀÉ */
+#endif
+ {
+ int topsig = 1; /* LHD.031107: ¥»­¶³Ì¤W­±ªºÃ±¦WÀÉ */
+ show_sign:
+
+ if (cuser.ufo & UFO_SHOWSIGN) /* itoc.000319: Åã¥Üñ¦WÀɪº¤º®e */
+ show_sign('0'+ topsig);
+
+ msg[38] = op = cuser.signature + '0';
+ if (fd = vget(b_lines, 0, msg, buf, 3, DOECHO))
+ {
+ if (fd == 'n') /* LHD.031007: «ön´`Àô´«­¶ */
+ {
+ topsig = (topsig + 3 ) % 6;
+ goto show_sign;
+ }
+
+ if (op != fd && ((fd >= '0' && fd <= '6') || fd == 'r'))
+ {
+ cuser.signature = fd - '0';
+ op = fd;
+ }
+ }
+
+ if (op == 'r')
+ op = (time(0) % 6) + '1';
+
+ if (op != '0')
+ {
+ char fpath[64];
+
+ sprintf(buf, "%s.%c", FN_SIGN, op);
+ usr_fpath(fpath, cuser.userid, buf); /* itoc.020123: ¦U­Óñ¦WÀÉÀɮפÀ¶} */
+
+ if ((fd = open(fpath, O_RDONLY)) >= 0)
+ {
+ op = 0;
+ mgets(-1);
+ while ((str = mgets(fd)) && (op < MAXSIGLINES))
+ {
+ if (!op)
+ this = ve_line(this, "--");
+
+ this = ve_line(this, str);
+ op++;
+ }
+ close(fd);
+ }
+ }
+ }
+
+ this->next = next;
+ if (next)
+ next->prev = this;
+}
+
+
+/* ----------------------------------------------------- */
+/* ¼f¬d user ¤Þ¨¥ªº¨Ï¥Î */
+/* ----------------------------------------------------- */
+
+
+static int
+quote_check()
+{
+ textline *p;
+ char *str;
+ int post_line;
+ int quot_line;
+ int in_quote;
+
+ post_line = quot_line = in_quote = 0;
+ for (p = vx_ini; p; p = p->next)
+ {
+ str = p->data;
+
+ /* itoc.030305: ¬°ºûÅ@ºô¸ô§»ö¡Añ¦WÀɤ]ºâ¤Þ¨¥ :p */
+
+ if (in_quote) /* ñ¦WÀÉ */
+ {
+ quot_line++;
+ }
+ else if (is_quoted(str)) /* ñ¦WÀɶ}©l */
+ {
+ in_quote = 1;
+ quot_line++;
+ }
+ else if (str[0] == QUOTE_CHAR1 && str[1] == ' ') /* ¤Þ¨¥ */
+ {
+ quot_line++;
+ }
+ else /* ¤@¯ë¤º¤å */
+ {
+ /* ªÅ¥Õ¦æ¤£ºâ post_line */
+ while (*str == ' ')
+ str++;
+ if (*str)
+ post_line++;
+ }
+ }
+
+ if ((quot_line >> 2) <= post_line) /* ¤å³¹¦æ¼Æ­n¦h©ó¤Þ¨¥¦æ¼Æ¥|¤À¤§¤@ */
+ return 0;
+
+ if (HAS_PERM(PERM_ALLADMIN))
+ return (vans("¤Þ¨¥¹L¦h (E)Ä~Äò½s¿è (W)±j¨î¼g¤J¡H[E] ") != 'w');
+
+ vmsg("¤Þ¨¥¤Ó¦h¡A½Ð«ö Ctrl+Y ¨Ó§R°£¤£¥²­n¤§¤Þ¨¥");
+ return 1;
+}
+
+
+/* ----------------------------------------------------- */
+/* ¼f¬d user µoªí¤å³¹¦r¼Æ/ª`­µ¤åªº¨Ï¥Î */
+/* ----------------------------------------------------- */
+
+
+int wordsnum; /* itoc.010408: ºâ¤å³¹¦r¼Æ */
+
+
+#ifdef ANTI_PHONETIC
+static int
+words_check()
+{
+ textline *p;
+ uschar *str, *pend;
+ int phonetic; /* ª`­µ¤å¼Æ¥Ø */
+
+ wordsnum = phonetic = 0;
+
+ for (p = vx_ini; p; p = p->next)
+ {
+ if (is_quoted(str = p->data)) /* ñ¦WÀɶ}©l */
+ break;
+
+ if (!(str[0] == QUOTE_CHAR1 && str[1] == ' ') && strncmp(str, "¡° ", 3)) /* «D¤Þ¥Î¥L¤H¤å³¹ */
+ {
+ wordsnum += p->len;
+
+ pend = str + p->len;
+ while (str < pend)
+ {
+ if (str[0] >= 0x81 && str[0] < 0xFE && str[1] >= 0x40 && str[1] <= 0xFE && str[1] != 0x7F) /* ¤¤¤å¦r BIG5+ */
+ {
+ if (str[0] == 0xA3 && str[1] >= 0x74 && str[1] <= 0xBA) /* ª`­µ¤å */
+ phonetic++;
+ str++; /* ¤¤¤å¦rÂù¦ì¤¸¡A­n¦h¥[¤@¦¸ */
+ }
+ str++;
+ }
+
+ }
+ }
+ return phonetic;
+}
+
+#else
+
+static void
+words_check()
+{
+ textline *p;
+ char *str;
+
+ wordsnum = 0;
+
+ for (p = vx_ini; p; p = p->next)
+ {
+ if (is_quoted(str = p->data)) /* ñ¦WÀɶ}©l */
+ break;
+
+ if (!(str[0] == QUOTE_CHAR1 && str[1] == ' ') && strncmp(str, "¡° ", 3)) /* «D¤Þ¥Î¥L¤H¤å³¹ */
+ wordsnum += p->len;
+ }
+}
+#endif
+
+
+/* ----------------------------------------------------- */
+/* Àɮ׳B²z¡GŪÀÉ¡B¦sÀÉ¡B¼ÐÃD¡Bñ¦WÀÉ */
+/* ----------------------------------------------------- */
+
+
+void
+ve_header(fp)
+ FILE *fp;
+{
+ time_t now;
+ char *title;
+
+ title = ve_title;
+ title[72] = '\0';
+ time(&now);
+
+ if (curredit & EDIT_MAIL)
+ {
+ fprintf(fp, "%s %s (%s)\n", str_author1, cuser.userid, cuser.username);
+ }
+ else
+ {
+#ifdef HAVE_ANONYMOUS
+ if (currbattr & BRD_ANONYMOUS && !(curredit & EDIT_RESTRICT))
+ {
+ if (vans("­n°Î¦Wµoªí¤å³¹¶Ü(Y/r)¡H[Y] ") != 'r') /* ¹w³]§ï¬°°Î¦Wµoªí */
+ {
+ do
+ {
+ if (!vget(b_lines, 0, "½Ð¿é¤J§A·Q¥ÎªºID¡Aª½±µ«ö[Enter]¥Î¹w³]¡B¿é¤Jr¥Î¯u¦W¡G",
+ anonymousid, IDLEN, DOECHO))
+ {
+ strcpy(anonymousid, STR_ANONYMOUS);/* «öEnter¡A°Î¦WID´N¥Î¹w³]ªº */
+ curredit |= EDIT_ANONYMOUS;
+ break;
+ }
+ else if (!strcmp(anonymousid, cuser.userid)) /* ¦Û©w°Î¦WID */
+ { /* ©M¦Û¤vªºID¤@¼Ë¡A¤£°Î¦Wµo¤å */
+// curredit |= EDIT_ANONYMOUS;
+ break;
+ }
+ else
+ {
+ char fpath[64];
+ usr_fpath(fpath, anonymousid, NULL);
+ if (!strcmp(anonymousid, "r"))
+ {
+ strcpy(anonymousid, cuser.userid);
+ break;
+ }
+ if (dashd(fpath)) /* Àˬd³o­ÓID¦³¨S¦³µù¥U */
+ {
+ vmsg("³o­Ó¢×¢Ò¦³¤H¥Î¤F³á¡I´«¤@­Ó§a¡C"); //°T®§¤º®e¥i¦Û¤v§ó§ï
+ }
+ else /* ²×©ó¥i¥H¥Î¦Û¤v·Q­nªºIDµo¤å¤F:P */
+ {
+ strcat(anonymousid, ".");/* ­Y¬O¦Û©wID¡A­n¦b«á­±¥[­Ó . ªí¥Ü¤£¦P*/
+ curredit |= EDIT_ANONYMOUS;
+ break;
+ }
+ }
+
+ } while (1);
+
+ }
+ }
+
+#if 0
+ if (!vget(b_lines, 0, "½Ð¿é¤J±z·Q¥ÎªºID¡A¤]¥iª½±µ«ö[Enter]¡A©Î¬O«ö[r]¥Î¯u¦W¡G", anonymousid, IDLEN, DOECHO))
+ { /* ¯d 1 byte ¥[ "." */
+ strcpy(anonymousid, STR_ANONYMOUS);
+ curredit |= EDIT_ANONYMOUS;
+ }
+ else if (strcmp(anonymousid, "r"))
+ {
+ strcat(anonymousid, "."); /* ­Y¬O¦Û©wID¡A­n¦b«á­±¥[­Ó . ªí¥Ü¤£¦P */
+ curredit |= EDIT_ANONYMOUS;
+ }
+ }
+#endif
+#endif
+ if (!(currbattr & BRD_NOSTAT) && !(curredit & EDIT_RESTRICT)) /* ¤£­p¤å³¹½g¼Æ ¤Î ¥[±K¦sÀÉ ¤£¦C¤J²Î­p½g¼Æ */
+ {
+ /* ²£¥Í²Î­p¸ê®Æ */
+
+ POSTLOG postlog;
+
+#ifdef HAVE_ANONYMOUS
+ /* Thor.980909: anonymous post mode */
+ if (curredit & EDIT_ANONYMOUS)
+ strcpy(postlog.author, anonymousid);
+ else
+
+#endif
+ strcpy(postlog.author, cuser.userid);
+
+ strcpy(postlog.board, currboard);
+ str_ncpy(postlog.title, str_ttl(title), sizeof(postlog.title));
+ postlog.date = now;
+ postlog.number = 1;
+
+ rec_add(FN_RUN_POST, &postlog, sizeof(POSTLOG));
+ }
+
+#ifdef HAVE_ANONYMOUS
+ /* Thor.980909: anonymous post mode */
+ if (curredit & EDIT_ANONYMOUS)
+ {
+ fprintf(fp, "%s %s (%s) %s %s\n",
+ str_author1, anonymousid, STR_ANONYMOUS,
+ curredit & EDIT_OUTGO ? str_post1 : str_post2, currboard);
+ }
+ else
+#endif
+
+ {
+ fprintf(fp, "%s %s (%s) %s %s\n",
+ str_author1, cuser.userid, cuser.username,
+ curredit & EDIT_OUTGO ? str_post1 : str_post2, currboard);
+ }
+ }
+ fprintf(fp, "¼ÐÃD: %s\n®É¶¡: %s\n\n", title, Btime(&now));
+}
+
+#define FOOL2011_EDIT_BANNER "\n--\n" \
+"\033[m \033[1m¢¨¢g¢© ¢p¢d¢« ¢¨¢g¢© ¢¨¢g¢© ¢¨¢g¢©\033[m\n" \
+"\033[44m \033[1;33m¥x«n¤@¤¤\033[;37;44m ¢i¢« ¢y¢i¢ª¢k ¢i¢« ¢ª¢g¢© ¢i¢« \033[1;33m¯Á¥§¤p¯¸\033[;37;44m \033[m\n"\
+" \033[1m¢ª¢g¢« ¢i ¢y¢© ¢ª¢g¢« ¢ª¢e¢« ¢ª¢g¢«\033[m\n" \
+"\033[0m Or\033[1mig\033[30min\033[37m ¡¸ \033[32msony.tfcis.org\033[m¡C\033[1;32mbbs.tfcis.org\033[m" \
+" A\033[1mut\033[30mho\033[mr\033[1m ¡¸ \033[33m%s\033[m\n" \
+" \033[mF\033[1mr\033[30mom\033[37m ¡¸ \033[m\033[1;35m%s(%s)\033[m\033[0m\n\n"
+
+void
+ve_banner(fp, modify) /* ¥[¤W¨Ó·½µ¥°T®§ */
+ FILE *fp;
+ int modify; /* 1:­×§ï 0:­ì¤å */
+{
+ /* chitsaou: IP ¥\¯à */
+ char name[40], fqdn[40], ipaddr[40];
+ unsigned long sum;
+ unsigned long base[4]={16777216,65536,256,1};
+ int i, ip[4];
+
+ /* ¨ú±oip */
+ sum = dns_a(fromhost);
+
+ for (i=0; i<=3; i++)
+ {
+ ip[i] = sum / base[i];
+ sum -= base[i]*ip[i];
+ }
+
+ sprintf(ipaddr, "%d.%d.%d.%d", ip[3], ip[2], ip[1], ip[0]);
+
+ /* ¥ý¤ñ¹ï FQDN */
+ str_lower(name, fromhost); /* itoc.011011: ¤j¤p¼g§¡¥i¡Aetc/fqdn ¸Ì­±³£­n¼g¤p¼g */
+ if (!belong_list(FN_ETC_FQDN, name, fqdn) && !belong_list(FN_ETC_HOST, name, fqdn))
+ {
+ /* ¦A¤ñ¹ï ip */
+ strcpy(name, ipaddr);
+
+ if (!belong_list(FN_ETC_HOST, name, fqdn))
+ {
+ strcpy(ipaddr, fromhost); //replace ipaddr with fromhost, to avoid ip=255.255.255.255
+ strcpy(fqdn, "¨S¦³¬G¶m"); //also mark it
+ }
+ }
+
+ if (!strcmp(ipaddr, "255.255.255.255"))
+ strcpy(ipaddr, fromhost);
+
+
+ /* itoc: «ØÄ³ banner ¤£­n¶W¹L¤T¦æ¡A¹Lªøªº¯¸Ã±¥i¯à·|³y¦¨¬Y¨Ç¨Ï¥ÎªÌªº¤Ï·P */
+
+ time_t now = time(NULL);
+ if (!modify)
+ {
+ fprintf(fp, (now >= 1301587200 && now <= 1301673600) ? FOOL2011_EDIT_BANNER : EDIT_BANNER,
+#ifdef HAVE_ANONYMOUS
+ (curredit & EDIT_ANONYMOUS) ? STR_ANONYMOUS :
+#endif
+ cuser.userid,
+#ifdef HAVE_ANONYMOUS
+ (curredit & EDIT_ANONYMOUS) ? "¶³»P¤sªº©¼ºÝ ^O^||" :
+#endif
+ ipaddr,
+#ifdef HAVE_ANONYMOUS
+ (curredit & EDIT_ANONYMOUS) ? "¤£§i¶D§A!" :
+#endif
+ fqdn);
+ }
+ else
+ {
+ fprintf(fp, MODIFY_BANNER, cuser.userid, Now());
+ }
+}
+
+
+static int
+ve_filer(fpath, ve_op)
+ char *fpath;
+ int ve_op; /* 1: ¦³ header 0,2: µL header -1: ¤£¯àÀx¦s */
+{
+ int ans;
+#ifdef ANTI_PHONETIC
+ int phoneticsnum; /* ª`­µ¤åªº¼Æ¶q */
+#endif
+
+ FILE *fp;
+ textline *p, *v;
+ char buf[80], *msg;
+
+#ifdef POPUP_ANSWER
+ char **menu;
+# ifdef HAVE_REFUSEMARK
+ char *menu1[] = {"EE", "Abort ©ñ±ó", "Title §ï¼ÐÃD", "Edit Ä~Äò½s¿è", "Read Ū¨ú¼È¦sÀÉ", "Write ¼g¤J¼È¦sÀÉ", "Delete §R°£¼È¦sÀÉ", NULL};
+ char *menu2[] = {"SE", "Save ¦sÀÉ","Bottom ¸m©³¦sÀÉ", "Abort ©ñ±ó", "Title §ï¼ÐÃD", "Edit Ä~Äò½s¿è", "Read Ū¨ú¼È¦sÀÉ", "Write ¼g¤J¼È¦sÀÉ", "Delete §R°£¼È¦sÀÉ", NULL};
+ char *menu3[] = {"SE", "Save ¦sÀÉ","Bottom ¸m©³¦sÀÉ", "Local ¦s¬°¯¸¤ºÀÉ", "XRefuse ¥[±K¦sÀÉ", "Abort ©ñ±ó", "Title §ï¼ÐÃD", "Edit Ä~Äò½s¿è", "Read Ū¨ú¼È¦sÀÉ", "Write ¼g¤J¼È¦sÀÉ", "Delete §R°£¼È¦sÀÉ", NULL};
+ char *menu4[] = {"LE", "Local ¦s¬°¯¸¤ºÀÉ", "Save ¦sÀÉ","Bottom ¸m©³¦sÀÉ", "XRefuse ¥[±K¦sÀÉ", "Abort ©ñ±ó", "Title §ï¼ÐÃD", "Edit Ä~Äò½s¿è", "Read Ū¨ú¼È¦sÀÉ", "Write ¼g¤J¼È¦sÀÉ", "Delete §R°£¼È¦sÀÉ", NULL};
+# else
+ char *menu1[] = {"EE", "Abort ©ñ±ó", "Title §ï¼ÐÃD", "Edit Ä~Äò½s¿è", "Read Ū¨ú¼È¦sÀÉ", "Write ¼g¤J¼È¦sÀÉ", "Delete §R°£¼È¦sÀÉ", NULL};
+ char *menu2[] = {"SE", "Save ¦sÀÉ", "Abort ©ñ±ó", "Title §ï¼ÐÃD", "Edit Ä~Äò½s¿è", "Read Ū¨ú¼È¦sÀÉ", "Write ¼g¤J¼È¦sÀÉ", "Delete §R°£¼È¦sÀÉ", NULL};
+ char *menu3[] = {"SE", "Save ¦sÀÉ", "Local ¦s¬°¯¸¤ºÀÉ", "Abort ©ñ±ó", "Title §ï¼ÐÃD", "Edit Ä~Äò½s¿è", "Read Ū¨ú¼È¦sÀÉ", "Write ¼g¤J¼È¦sÀÉ", "Delete §R°£¼È¦sÀÉ", NULL};
+ char *menu4[] = {"LE", "Local ¦s¬°¯¸¤ºÀÉ", "Save ¦sÀÉ", "Abort ©ñ±ó", "Title §ï¼ÐÃD", "Edit Ä~Äò½s¿è", "Read Ū¨ú¼È¦sÀÉ", "Write ¼g¤J¼È¦sÀÉ", "Delete §R°£¼È¦sÀÉ", NULL};
+# endif
+#else
+# ifdef HAVE_REFUSEMARK
+ char *msg1 = "[E]Ä~Äò (A)©ñ±ó (R/W/D)Ū¼g§R¼È¦sÀÉ¡H";
+ char *msg2 = "[S]¦sÀÉ (B) ¸m©³ (A)©ñ±ó (T)§ï¼ÐÃD (E)Ä~Äò (R/W/D)Ū¼g§R¼È¦sÀÉ¡H";
+ char *msg3 = "[S]¦sÀÉ (B) ¸m©³ (L)¯¸¤º (X)±K«Ê (A)©ñ±ó (T)§ï¼ÐÃD (E)Ä~Äò (R/W/D)Ū¼g§R¼È¦sÀÉ¡H";
+ char *msg4 = "[L]¯¸¤º (S)¦sÀÉ (B) ¸m©³ (X)±K«Ê (A)©ñ±ó (T)§ï¼ÐÃD (E)Ä~Äò (R/W/D)Ū¼g§R¼È¦sÀÉ¡H";
+# else
+ char *msg1 = "[E]Ä~Äò (A)©ñ±ó (R/W/D)Ū¼g§R¼È¦sÀÉ¡H";
+ char *msg2 = "[S]¦sÀÉ (A)©ñ±ó (T)§ï¼ÐÃD (E)Ä~Äò (R/W/D)Ū¼g§R¼È¦sÀÉ¡H";
+ char *msg3 = "[S]¦sÀÉ (L)¯¸¤º (A)©ñ±ó (T)§ï¼ÐÃD (E)Ä~Äò (R/W/D)Ū¼g§R¼È¦sÀÉ¡H";
+ char *msg4 = "[L]¯¸¤º (S)¦sÀÉ (A)©ñ±ó (T)§ï¼ÐÃD (E)Ä~Äò (R/W/D)Ū¼g§R¼È¦sÀÉ¡H";
+# endif
+#endif
+
+ ans = 0;
+
+#ifdef POPUP_ANSWER
+ if (ve_op < 0) /* itoc.010301: ·s¼W ve_op = -1 ¤£¯àÀx¦s */
+ menu = menu1;
+ else if (bbsmode != M_POST) /* ¼g«H */
+ menu = menu2;
+ else if (curredit & EDIT_OUTGO) /* Âà«HªOµo¤å */
+ menu = menu3;
+ else
+ menu = menu4;
+
+ switch (pans(3, 20, "¦sÀɿﶵ", menu))
+#else
+ if (ve_op < 0) /* itoc.010301: ·s¼W ve_op = -1 ¤£¯àÀx¦s */
+ msg = msg1;
+ else if (bbsmode != M_POST) /* ¼g«H */
+ msg = msg2;
+ else if (curredit & EDIT_OUTGO) /* Âà«HªOµo¤å */
+ msg = msg3;
+ else
+ msg = msg4;
+
+ switch (vans(msg))
+#endif
+
+ {
+ case 's':
+ if (ve_op < 0) /* itoc.010301: ¤£¯àÀx¦s */
+ return VE_FOOTER;
+ /* Thor.990111: ¤£Âà«H«h¤£¥~¬y */
+ if (HAS_PERM(PERM_INTERNET) && !(currbattr & BRD_NOTRAN))
+ curredit |= EDIT_OUTGO;
+ break;
+
+ case 'a':
+ ans = -1;
+ break;
+
+ case 'l':
+ if (ve_op < 0) /* itoc.010301: ¤£¯àÀx¦s */
+ return VE_FOOTER;
+ curredit &= ~EDIT_OUTGO;
+ break;
+
+#ifdef HAVE_REFUSEMARK
+ case 'x':
+ if (ve_op < 0) /* itoc.010301: ¤£¯àÀx¦s */
+ return VE_FOOTER;
+ curredit |= EDIT_RESTRICT;
+ curredit &= ~EDIT_OUTGO; /* ¥[±K¥²¬O local save */
+ break;
+#endif
+
+ case 'b':
+ if (ve_op < 0)
+ return VE_FOOTER;
+ if ((bbsmode == M_POST) && (bbstate & STAT_BOARD)) /* ªO¥Dµo¤å */
+ curredit |= EDIT_BOTTOM;
+ else if (HAS_PERM(PERM_INTERNET) && !(currbattr & BRD_NOTRAN))
+ curredit |= EDIT_OUTGO; /* §_«hµø¬° save */
+ break;
+
+ case 'r':
+ tbf_read();
+ return VE_REDRAW;
+
+ case 'e':
+ return VE_FOOTER;
+
+ case 'w':
+ tbf_write();
+ return VE_FOOTER;
+
+ case 'd':
+ tbf_erase();
+ return VE_FOOTER;
+
+ case 't':
+ if (ve_op > 0) /* itoc.010301: ¤£¯àÀx¦s */
+ {
+ strcpy(buf, ve_title);
+ if (!vget(b_lines, 0, "¼ÐÃD¡G", ve_title, TTLEN + 1, GCARRY))
+ strcpy(ve_title, buf);
+ }
+ return VE_FOOTER;
+
+ default:
+ if (ve_op < 0) /* itoc.010301: ¤£¯àÀx¦s */
+ return VE_FOOTER;
+ }
+
+ if (!ans)
+ {
+ if (ve_op == 1 && !(curredit & EDIT_MAIL) && quote_check())
+ return VE_FOOTER;
+
+#ifdef ANTI_PHONETIC
+ phoneticsnum = words_check();
+ if (!(curredit & EDIT_MAIL) && bbsmode != M_UFILES &&
+ (currbattr & BRD_NOPHONETIC) && phoneticsnum > 2)
+ {
+ char xdbuffer[20];
+ sprintf (xdbuffer, "½Ð¤Å¨Ï¥Îª`­µ¤å (%d)", phoneticsnum);
+ //vmsg("½Ð¤Å¨Ï¥Îª`­µ¤å");
+ vmsg (xdbuffer);
+ return VE_FOOTER;
+ }
+#endif
+
+ if (!*fpath)
+ {
+ usr_fpath(fpath, cuser.userid, fn_note);
+ }
+
+ if ((fp = fopen(fpath, "w")) == NULL)
+ {
+ ve_abort(5);
+ abort_bbs();
+ }
+
+#ifndef ANTI_PHONETIC
+ words_check(); /* itoc.010408: ºâ¤å³¹¦r¼Æ */
+#endif
+
+ if (ve_op == 1)
+ ve_header(fp);
+ }
+
+ if (p = vx_ini)
+ {
+ vx_ini = NULL;
+
+ do
+ {
+ v = p->next;
+ if (!ans)
+ {
+ msg = p->data;
+ str_trim(msg);
+ fprintf(fp, "%s\n", msg);
+ }
+ free(p);
+ } while (p = v);
+ }
+
+ if (!ans)
+ {
+ if (bbsmode == M_POST || bbsmode == M_SMAIL)
+ ve_banner(fp, 0);
+ fclose(fp);
+ }
+
+ return ans;
+}
+
+
+/* ----------------------------------------------------- */
+/* ¿Ã¹õ³B²z¡G»²§U°T®§¡BÅã¥Ü½s¿è¤º®e */
+/* ----------------------------------------------------- */
+
+
+static void
+ve_outs(text)
+ uschar *text;
+{
+ int ch;
+ uschar *tail;
+
+ tail = text + SCR_WIDTH;
+ while (ch = *text)
+ {
+ switch (ch)
+ {
+ case KEY_ESC:
+ ch = '*';
+ break;
+ }
+ outc(ch);
+
+ if (++text >= tail)
+ break;
+ }
+}
+
+
+int
+ve_subject(row, topic, dft)
+ int row;
+ char *topic;
+ char *dft;
+{
+ char *title;
+
+ title = ve_title;
+
+ if (topic)
+ {
+ sprintf(title, "Re: %s", str_ttl(topic));
+ title[TTLEN] = '\0';
+ }
+ else
+ {
+ if (dft)
+ strcpy(title, dft);
+ else
+ *title = '\0';
+ }
+
+ return vget(row, 0, "¼ÐÃD¡G", title, TTLEN + 1, GCARRY);
+}
+
+
+/* ----------------------------------------------------- */
+/* ½s¿è³B²z¡G¥Dµ{¦¡¡BÁä½L³B²z */
+/* ----------------------------------------------------- */
+
+/* ----------------------------------------------------- */
+/* vedit ¦^¶Ç -1:¨ú®ø½s¿è 0:§¹¦¨½s¿è */
+/* ----------------------------------------------------- */
+/* ve_op: */
+/* 0 => ¯Âºé½s¿èÀÉ®× */
+/* -1 => ½s¿è¦ý¤£¯àÀx¦s¡A¥Î¦b½s¿è§@ªÌ¤£¬O¦Û¤vªº¤å³¹ */
+/* 1 => ¤Þ¤å¡B¥[ñ¦WÀÉ¡A¨Ã¥[¤WÀÉÀY¡A¥Î¦bµoªí¤å³¹/¯¸¤º«H */
+/* 2 => ¤Þ¤å¡B¥[ñ¦WÀÉ¡A¤£¥[¤WÀÉÀY¡A¥Î¦b±H¯¸¥~«H */
+/* ----------------------------------------------------- */
+/* ­Y ve_op ¬O 1 ©Î 2 ®É¡A¶i¤J vedit «eÁÙ±o«ü©w curredit */
+/* ©M quote_file */
+/* ----------------------------------------------------- */
+
+int /* -1:¨ú®ø½s¿è 0:§¹¦¨½s¿è */
+vedit(fpath, ve_op)
+ char *fpath;
+ int ve_op; /* 0:¯Âºé½s¿èÀÉ®× -1:½s¿è¦ý¤£¯àÀx¦s 1:quote/header 2:quote */
+{
+ textline *vln, *tmp;
+ int cc, col, mode, margin, pos;
+
+ /* --------------------------------------------------- */
+ /* ªì©l³]©w¡G¸ü¤JÀɮסB¤Þ¥Î¤å³¹¡B³]©w½s¿è¼Ò¦¡ */
+ /* --------------------------------------------------- */
+
+ tmp = vln = ve_alloc();
+
+ if (*fpath)
+ {
+ cc = open(fpath, O_RDONLY);
+ if (cc >= 0)
+ {
+ vln = ve_load(vln, cc);
+ }
+ else
+ {
+ cc = open(fpath, O_WRONLY | O_CREAT, 0600);
+ if (cc < 0)
+ {
+ ve_abort(4);
+ abort_bbs();
+ }
+ }
+ close(cc);
+ }
+
+ /* if (ve_op) */
+ if (ve_op > 0) /* itoc.010301: ·s¼W ve_op = -1 ®É¤£¯àÀx¦s */
+ {
+ ve_quote(vln);
+ }
+
+ if (vln = tmp->next)
+ {
+ free(tmp);
+ vln->prev = NULL;
+ }
+ else
+ {
+ vln = tmp;
+ }
+
+ vx_cur = vx_top = vx_ini = vln;
+
+ ve_col = ve_row = margin = 0;
+ ve_lno = 1;
+ ve_mode = VE_INSERT | VE_REDRAW | VE_FOOTER;
+
+#ifdef HAVE_MULTI_BYTE
+ zhc = (cuser.ufo & UFO_ZHC);
+#endif
+
+ /* --------------------------------------------------- */
+ /* ¥D°j°é¡G¿Ã¹õÅã¥Ü¡BÁä½L³B²z¡BÀɮ׳B²z */
+ /* --------------------------------------------------- */
+
+ clear();
+
+ for (;;)
+ {
+ vln = vx_cur;
+ mode = ve_mode;
+ col = ve_col;
+ /* itoc.031123.µù¸Ñ: ¦pªG¶W¹L SCR_WIDTH¡A¨º»ò­¶­±©¹¥k½¡A¨Ã«O¯d¥ª­¶ªº³Ì«á 4 ¦r */
+ cc = (col < SCR_WIDTH) ? 0 : (col / (SCR_WIDTH - 4)) * (SCR_WIDTH - 4);
+ if (cc != margin)
+ {
+ mode |= VE_REDRAW;
+ margin = cc;
+ }
+
+ if (mode & VE_REDRAW)
+ {
+ ve_mode = (mode ^= VE_REDRAW);
+
+ tmp = vx_top;
+
+ for (pos = 0;; pos++)
+ {
+ move(pos, 0);
+ clrtoeol();
+ if (pos == b_lines)
+ break;
+ if (tmp)
+ {
+ if (mode & VE_ANSI)
+ outx(tmp->data);
+ else if (tmp->len > margin)
+ ve_outs(tmp->data + margin);
+ tmp = tmp->next;
+ }
+ else
+ {
+ outc('~');
+ }
+ }
+#ifdef EVERY_BIFF
+ if (!(mode & VE_BIFF))
+ {
+ if (HAS_STATUS(STATUS_BIFF))
+ ve_mode = mode |= VE_BIFF;
+ }
+#endif
+ }
+ else
+ {
+ move(ve_row, 0);
+ if (mode & VE_ANSI)
+ outx(vln->data);
+ else if (vln->len > margin)
+ ve_outs(vln->data + margin);
+ clrtoeol();
+ }
+
+ /* ------------------------------------------------- */
+ /* Åã¥Üª¬ºA¡BŪ¨úÁä½L */
+ /* ------------------------------------------------- */
+
+ if (mode & VE_ANSI) /* Thor: §@ ansi ½s¿è */
+ pos = n2ansi(col, vln); /* Thor: ansi ¤£·|¥Î¨ìcc */
+ else /* Thor: ¤£¬Oansi­n§@margin shift */
+ pos = col - margin;
+
+ if (mode & VE_FOOTER)
+ {
+ move(b_lines, 0);
+ clrtoeol();
+
+ if (cuser.ufo & UFO_VEDIT)
+ {
+ ve_mode = (mode ^= VE_FOOTER);
+ }
+ else
+ {
+ prints(FOOTER_VEDIT,
+#ifdef EVERY_BIFF
+ mode & VE_BIFF ? "¶l®t¨Ó¤F" : "½s¿è¤å³¹",
+#else
+ "½s¿è¤å³¹",
+#endif
+ mode & VE_INSERT ? "´¡¤J" : "¨ú¥N",
+ mode & VE_ANSI ? "ANSI" : "¤@¯ë",
+ ve_lno, 1 + (mode & VE_ANSI ? pos : col));
+ }
+ }
+
+ move(ve_row, pos);
+
+ve_key:
+
+ cc = vkey();
+
+ if (isprint2(cc))
+ {
+ ve_char(cc);
+ }
+ else
+ {
+ switch (cc)
+ {
+ case '\n':
+
+ ve_split(vln, col);
+ break;
+
+ case KEY_TAB:
+
+ do
+ {
+ ve_char(' ');
+ } while (ve_col & (TAB_STOP - 1));
+ break;
+
+ case KEY_INS: /* Toggle insert/overwrite */
+
+ ve_mode = mode ^ VE_INSERT;
+ continue;
+
+ case KEY_BKSP: /* backspace */
+
+ /* Thor: ¦b ANSI ½s¿è¼Ò¦¡¤U, ¤£¥i¥H«ö­Ë°h, ¤£µM·|«Ü¥i©È.... */
+
+ if (mode & VE_ANSI)
+ {
+#if 0
+ goto ve_key; /* «ö«á°hÁä´N·í¨S«ö */
+#endif
+
+ /* itoc.010322: ANSI ½s¿è®É«ö«á°hÁä¦^¨ì«D ANSI ¼Ò¦¡ */
+ mode ^= VE_ANSI;
+ clear();
+ ve_mode = mode | VE_REDRAW;
+ continue;
+ }
+
+ if (col)
+ {
+ delete_char(vln, --col);
+#ifdef HAVE_MULTI_BYTE
+ /* hightman.060504: §PÂ_²{¦b§R°£ªº¦ì¸m¬O§_¬°º~¦rªº«á¥b¬q¡A­Y¬O§R¤G¦r¤¸ */
+ if (zhc && col && IS_ZHC_LO(vln->data, col))
+ delete_char(vln, --col);
+#endif
+ ve_col = col;
+ continue;
+ }
+
+ if (!(tmp = vln->prev))
+ goto ve_key;
+
+ ve_row--;
+ ve_lno--;
+ vx_cur = tmp;
+ ve_col = tmp->len;
+ if (*ve_strim(vln->data))
+ join_up(tmp);
+ else
+ delete_line(vln);
+ ve_mode = mode | VE_REDRAW;
+ break;
+
+ case Ctrl('D'):
+ case KEY_DEL: /* delete current character */
+
+ cc = vln->len;
+ if (cc == col)
+ {
+ join_up(vln);
+ ve_mode = mode | VE_REDRAW;
+ }
+ else
+ {
+ if (cc == 0)
+ goto ve_key;
+#ifdef HAVE_MULTI_BYTE
+ /* hightman.060504: §PÂ_²{¦b§R°£ªº¦ì¸m¬O§_¬°º~¦rªº«e¥b¬q¡A­Y¬O§R¤G¦r¤¸ */
+ /* ª`·N­ì¦³ªºÂù¦â¦r§R°£«á¥i¯à¥X°ÝÃD¡A¼È®É¤£§@¥t¦æ³B²z */
+ if (zhc && col < cc - 1 && IS_ZHC_HI(vln->data[col]))
+ delete_char(vln, col);
+#endif
+ delete_char(vln, col);
+ if (mode & VE_ANSI) /* Thor: ÁöµM¼W¥[ load, ¤£¹Ledit ®É·|¤ñ¸û¦n¬Ý */
+ ve_col = ansi2n(n2ansi(col, vln), vln);
+ }
+ continue;
+
+ case KEY_LEFT:
+
+ if (col)
+ {
+ ve_col = (mode & VE_ANSI) ? ansi2n(pos - 1, vln) : col - 1;
+#ifdef HAVE_MULTI_BYTE
+ /* hightman.060504: ¥ª²¾®É¸I¨ìº~¦r²¾Âù®æ */
+ if (zhc && ve_col && IS_ZHC_LO(vln->data, ve_col))
+ ve_col--;
+#endif
+ continue;
+ }
+
+ if (!(tmp = vln->prev))
+ goto ve_key;
+
+ ve_row--;
+ ve_lno--;
+ ve_col = tmp->len;
+ vx_cur = tmp;
+ break;
+
+ case KEY_RIGHT:
+
+ if (col < vln->len)
+ {
+ ve_col = (mode & VE_ANSI) ? ansi2n(pos + 1, vln) : col + 1;
+#ifdef HAVE_MULTI_BYTE
+ /* hightman.060504: ¥k²¾®É¸I¨ìº~¦r²¾Âù®æ */
+ if (zhc && ve_col < vln->len && IS_ZHC_HI(vln->data[ve_col - 1]))
+ ve_col++;
+#endif
+ continue;
+ }
+
+ if (!(tmp = vln->next))
+ goto ve_key;
+
+ ve_row++;
+ ve_lno++;
+ ve_col = 0;
+ vx_cur = tmp;
+ break;
+
+ case KEY_HOME:
+ case Ctrl('A'):
+
+ ve_col = 0;
+ continue;
+
+ case KEY_END:
+ case Ctrl('E'):
+
+ ve_col = vln->len;
+ continue;
+
+ case KEY_UP:
+ case Ctrl('P'):
+
+ if (!(tmp = vln->prev))
+ goto ve_key;
+
+ ve_row--;
+ ve_lno--;
+ if (mode & VE_ANSI)
+ {
+ ve_col = ansi2n(pos, tmp);
+ }
+ else
+ {
+ cc = tmp->len;
+ if (col > cc)
+ ve_col = cc;
+ }
+ vx_cur = tmp;
+#ifdef HAVE_MULTI_BYTE
+ /* hightman.060504: º~¦r¾ã¦r½Õ¸` */
+ if (zhc && ve_col < tmp->len && IS_ZHC_LO(tmp->data, ve_col))
+ ve_col++;
+#endif
+ break;
+
+ case KEY_DOWN:
+ case Ctrl('N'):
+
+ if (!(tmp = vln->next))
+ goto ve_key;
+
+ ve_row++;
+ ve_lno++;
+ if (mode & VE_ANSI)
+ {
+ ve_col = ansi2n(pos, tmp);
+ }
+ else
+ {
+ cc = tmp->len;
+ if (col > cc)
+ ve_col = cc;
+ }
+ vx_cur = tmp;
+#ifdef HAVE_MULTI_BYTE
+ /* hightman.060504: º~¦r¾ã¦r½Õ¸` */
+ if (zhc && ve_col < tmp->len && IS_ZHC_LO(tmp->data, ve_col))
+ ve_col++;
+#endif
+ break;
+
+ case KEY_PGUP:
+ case Ctrl('B'):
+
+ ve_pageup();
+ continue;
+
+ case KEY_PGDN:
+ case Ctrl('F'):
+ case Ctrl('T'): /* tail of file */
+
+ ve_forward(cc == Ctrl('T') ? -1 : PAGE_SCROLL);
+ continue;
+
+ case Ctrl('S'): /* start of file */
+
+ vx_cur = vx_top = vx_ini;
+ ve_col = ve_row = 0;
+ ve_lno = 1;
+ ve_mode = mode | VE_REDRAW;
+ continue;
+
+ case Ctrl('V'): /* Toggle ANSI color */
+
+ mode ^= VE_ANSI;
+ clear();
+ ve_mode = mode | VE_REDRAW;
+ continue;
+
+ case Ctrl('X'): /* Save and exit */
+
+ /* cc = ve_filer(fpath, ve_op & 1); */
+ cc = ve_filer(fpath, ve_op); /* itoc.010301: ·s¼W ve_op = -1 ®É¤£¯àÀx¦s */
+ if (cc <= 0)
+ return cc;
+ ve_mode = mode | cc;
+ continue;
+
+ case Ctrl('Z'):
+
+ cutmp->status |= STATUS_EDITHELP;
+ xo_help("post");
+ cutmp->status ^= STATUS_EDITHELP;
+ ve_mode = mode | VE_REDRAW;
+ continue;
+
+ case Ctrl('C'):
+
+ ve_ansi();
+ break;
+
+ case Ctrl('O'): /* delete to end of file */
+
+ /* vln->len = ve_col = cc = 0; */
+ tmp = vln->next;
+ vln->next = NULL;
+ while (tmp)
+ {
+ vln = tmp->next;
+ free(tmp);
+ tmp = vln;
+ }
+ ve_mode = mode | VE_REDRAW;
+ continue;
+
+ case Ctrl('Y'): /* delete current line */
+
+ vln->len = ve_col = 0;
+ vln->data[0] = '\0'; /* Thor.981001: ±N¤º®e¤@¨Ö²M°£ */
+
+ case Ctrl('K'): /* delete to end of line */
+
+ if (cc = vln->len)
+ {
+ if (cc != col)
+ {
+ vln->len = col;
+ vln->data[col] = '\0';
+ continue;
+ }
+
+ join_up(vln);
+ }
+ else
+ {
+ tmp = vln->next;
+ if (!tmp)
+ {
+ tmp = vln->prev;
+ if (!tmp)
+ break;
+
+ if (ve_row > 0)
+ {
+ ve_row--;
+ ve_lno--;
+ }
+ }
+ if (vln == vx_top)
+ vx_top = tmp;
+ delete_line(vln);
+ vx_cur = tmp;
+ }
+
+ ve_mode = mode | VE_REDRAW;
+ break;
+
+ case Ctrl('U'):
+
+ ve_char(KEY_ESC);
+ break;
+
+#ifdef SHOW_USER_IN_TEXT
+ case Ctrl('Q'):
+ cc = vans("Åã¥Ü¨Ï¥ÎªÌ¸ê®Æ(1)id (2)¼ÊºÙ (3)¯u¹ê©m¦W¡H");
+ if (cc >= '1' && cc <= '3')
+ {
+ ve_char(KEY_ESC);
+ ve_char('*');
+ ve_char("snr"[cc - '1']);
+ }
+ ve_mode = mode | VE_FOOTER;
+ break;
+#endif
+
+#ifdef INPUT_TOOLS
+ case Ctrl('W'):
+
+ input_tools();
+ ve_mode = mode | VE_FOOTER;
+ break;
+#endif
+
+#ifdef HAVE_MULTI_BYTE
+ case Ctrl('G'):
+ /* lantw44: ¥þ«¬¦r°»´ú¤Á´« (Ctrl Áä ³£³Q¥Î¥ú¤F©Ò¥H¥Î³o­Ó) */
+ cuser.ufo ^= UFO_ZHC;
+ zhc = (cuser.ufo & UFO_ZHC);
+ if(zhc){
+ vmsg("¥þ«¬¦r°»´ú¶}±Ò");
+ }else{
+ vmsg("¥þ«¬¦r°»´úÃö³¬");
+ }
+ break;
+#endif
+
+ default:
+
+ goto ve_key;
+ }
+ }
+
+ /* ------------------------------------------------- */
+ /* ve_row / ve_lno ½Õ¾ã */
+ /* ------------------------------------------------- */
+
+ cc = ve_row;
+ if (cc < 0)
+ {
+ ve_row = 0;
+ if (vln = vx_top->prev)
+ {
+ vx_top = vln;
+ rscroll();
+ }
+ else
+ {
+ ve_abort(6);
+ }
+ }
+ else if (cc >= b_lines)
+ {
+ ve_row = b_lines - 1;
+ if (vln = vx_top->next)
+ {
+ vx_top = vln;
+ scroll();
+ }
+ else
+ {
+ ve_abort(7);
+ }
+ }
+ }
+}
diff --git a/maple/favor.c b/maple/favor.c
new file mode 100644
index 0000000..3e3717c
--- /dev/null
+++ b/maple/favor.c
@@ -0,0 +1,848 @@
+/*-------------------------------------------------------*/
+/* favorite.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : §Úªº³Ì·R */
+/* create : 00/06/16 */
+/* update : / / */
+/* author : weichung.bbs@bbs.ntit.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+
+#ifdef MY_FAVORITE
+
+static int mf_add();
+static int mf_paste();
+static int mf_load();
+
+
+extern XZ xz[];
+extern char xo_pool[];
+extern char brd_bits[]; /* itoc.010821: §PÂ_¬O§_¦³¾\Ū¬ÝªOªºÅv­­ */
+extern BCACHE *bshm;
+extern int class_flag;
+
+#ifndef ENHANCED_VISIT
+extern time_t brd_visit[];
+#endif
+
+#ifdef AUTO_JUMPBRD
+static int mf_jumpnext = 0; /* itoc.020615: ¬O§_¸õ¥h¤U¤@­Ó¥¼ÅªªO 1:­n 0:¤£­n */
+#endif
+
+
+static MF mftmp; /* for copy & paste */
+
+
+void
+mf_fpath(fpath, userid, fname)
+ char *fpath;
+ char *userid;
+ char *fname;
+{
+ char buf[64];
+
+ sprintf(buf, "MF/%s", fname);
+ usr_fpath(fpath, userid, buf);
+}
+
+
+static void
+mf_item(num, mf)
+ int num;
+ MF *mf;
+{
+ char folder[64];
+ int mftype, brdpost, bno;
+
+ mftype = mf->mftype;
+ brdpost = cuser.ufo & UFO_BRDPOST;
+
+ if (mftype & MF_FOLDER)
+ {
+ if (brdpost)
+ {
+ mf_fpath(folder, cuser.userid, mf->xname);
+ num = rec_num(folder, sizeof(MF));
+ }
+ prints("%6d%c %s %s\n", num, mftype & MF_MARK ? ')' : ' ', "¡»", mf->title);
+ }
+ else if (mftype & MF_BOARD)
+ {
+ if ((bno = brd_bno(mf->xname)) >= 0)
+ class_item(num, bno, brdpost);
+ else
+ /* itoc.010821: ¤£¨£ªº¬ÝªO¡AÅý user ¦Û¤v²M±¼¡A¦p¦¹ user ¤~ª¾¹D­þ¨Ç¬ÝªO³Q¬å¤F */
+ prints(" \033[36m<%s ¤w§ï¦W©Î³Q§R°£¡A½Ð±N¥»±¶®|§R°£>\033[m\n", mf->xname);
+ }
+ else if (mftype & MF_GEM)
+ {
+ prints("%6d%c %s %s\n",
+ brdpost ? 0 : num,
+ mftype & MF_MARK ? ')' : ' ', "¡½", mf->title);
+ }
+ else /* if (mftype & MF_LINE) */ /* qazq.040721: ¤À¹j½u */
+ {
+ prints("%6d %s\n",
+ brdpost ? 0 : num, mf->title);
+ }
+}
+
+
+static int
+mf_body(xo)
+ XO *xo;
+{
+ MF *mf;
+ int max, num, tail;
+#ifdef AUTO_JUMPBRD
+ int nextpos;
+ static int originpos = -1;
+#endif
+
+ max = xo->max;
+ if (max <= 0)
+ {
+ max = vans("§Úªº³Ì·R (A)·s¼W (P)¶K¤W (Q)Â÷¶} [Q] ");
+ switch (max)
+ {
+ case 'a':
+
+ max = mf_add(xo);
+ if (xo->max > 0)
+ return max;
+ break;
+
+ case 'p':
+
+ mf_paste(xo);
+ return mf_load(xo);
+ }
+
+ return XO_QUIT;
+ }
+
+ num = xo->top;
+ tail = num + XO_TALL;
+ if (max > tail)
+ max = tail;
+
+#ifdef AUTO_JUMPBRD
+ nextpos = 0;
+
+ /* itoc.020615: ·j´M¤U¤@­Ó¥¼Åª¬ÝªO */
+ if (mf_jumpnext)
+ {
+ BRD *bcache;
+
+ tail = xo->pos; /* ­É¥Î tail */
+ if (originpos < 0) /* ¦b¥h¤U­¶·j´M¥¼Åª¬ÝªO«e¡A°O¿ý¤@¶}©l´å¼Ð©Ò¦b */
+ originpos = tail;
+ mf = (MF *) xo_pool + tail - num;
+ bcache = bshm->bcache;
+
+ while (tail < max) /* ¥u¯à§ä¥»­¶¤¤ªº¥¼Åª¬ÝªO¡A¦]¬°¤U­¶ÁÙ¨S¸ü¤J */
+ {
+ if (mf->mftype & MF_BOARD)
+ {
+ int chn;
+ BRD *brd;
+
+ chn = brd_bno(mf->xname);
+ brd = bcache + chn;
+
+#ifdef ENHANCED_VISIT
+ /* itoc.010407: §ï¥Î³Ì«á¤@½g¤wŪ/¥¼Åª¨Ó§PÂ_ */
+ brh_get(brd->bstamp, chn);
+ if (brh_unread(brd->blast))
+#else
+ if (brd->blast > brd_visit[chn])
+#endif
+ {
+ nextpos = tail;
+ mf_jumpnext = 0;
+ originpos = -1;
+ break;
+ }
+ }
+ tail++;
+ mf++;
+ }
+
+ if (mf_jumpnext) /* ¦pªG¦b¥»­¶¨S¦³§ä¨ì¥¼Åª¬ÝªO */
+ {
+ if (max < rec_num(xo->dir, sizeof(MF))) /* ¦A¥h¤U­¶§ä */
+ return num + XO_TALL + XO_MOVE;
+
+ /* ¤w¸g¬O³Ì«á¤@­¶¤FÁÙ¬O§ä¤£¨ì¥¼Åª¬ÝªO */
+ mf_jumpnext = 0;
+ tail = originpos;
+ originpos = -1;
+ if (tail < num) /* ¦^¨ì­ì¨Ó¨º­¶ */
+ return tail + XO_MOVE;
+ }
+ }
+#endif
+
+ mf = (MF *) xo_pool;
+
+ move(3, 0);
+ do
+ {
+ mf_item(++num, mf++);
+ } while (num < max);
+ clrtobot();
+
+#ifdef AUTO_JUMPBRD
+ /* itoc.020615: ¤U¤@­Ó¥¼ÅªªO¦b¥»­¶¡A­n§â´å¼Ð²¾¹L¥h */
+ outf(FEETER_MF);
+ return nextpos ? nextpos + XO_MOVE : XO_NONE;
+#else
+ /* return XO_NONE; */
+ return XO_FOOT; /* itoc.010403: §â b_lines ¶ñ¤W feeter */
+#endif
+}
+
+
+static int
+mf_head(xo)
+ XO *xo;
+{
+ vs_head("§Úªº³Ì·R", str_site);
+ prints(NECKER_MF,
+ class_flag & UFO_BRDPOST ? "Á`¼Æ" : "½s¸¹",
+ d_cols >> 1, "", d_cols - (d_cols >> 1), "");
+ return mf_body(xo);
+}
+
+
+static int
+mf_init(xo)
+ XO *xo;
+{
+ xo_load(xo, sizeof(MF));
+ return mf_head(xo);
+}
+
+
+static int
+mf_load(xo)
+ XO *xo;
+{
+ xo_load(xo, sizeof(MF));
+ return mf_body(xo);
+}
+
+
+static int
+mf_stamp(mf)
+ MF *mf;
+{
+ char fpath[64];
+ int fd;
+
+ mf->xname[0] = 'F';
+ archiv32(mf->chrono, mf->xname + 1);
+
+ mf_fpath(fpath, cuser.userid, mf->xname);
+
+ if ((fd = open(fpath, O_WRONLY | O_CREAT | O_EXCL, 0600)) >= 0)
+ close(fd);
+
+ return fd;
+}
+
+
+static int
+mf_add(xo)
+ XO *xo;
+{
+ MF mf;
+ int ans;
+
+ ans = vans("·s¼W (B)¬ÝªO±¶®| (F)¨÷©v (G)ºëµØ°Ï±¶®| (L)¤À¹j½u [Q] ");
+
+ if (ans != 'b' && ans != 'f' && ans != 'g' && ans != 'l')
+ return XO_FOOT;
+
+ time(&mf.chrono);
+
+ if (ans == 'b' || ans == 'g')
+ {
+ BRD *brd;
+ char bname[BNLEN + 1];
+
+ if (!(brd = ask_board(bname, BRD_R_BIT, NULL)))
+ return mf_head(xo);
+
+ mf.mftype = (ans == 'b') ? MF_BOARD : MF_GEM;
+ strcpy(mf.xname, brd->brdname);
+ if (ans == 'g')
+ sprintf(mf.title, "%s ªO ºëµØ°Ï±¶®|", mf.xname);
+ }
+ else /* if (ans == 'f' || ans == 'l') */
+ {
+ if (!vget(b_lines, 0, "¼ÐÃD¡G", mf.title, BTLEN + 1, DOECHO))
+ return XO_FOOT;
+
+ mf.mftype = (ans == 'f') ? MF_FOLDER : MF_LINE;
+ if (ans == 'f')
+ {
+ if (mf_stamp(&mf) < 0)
+ return XO_FOOT;
+ }
+ }
+
+ ans = vans("¦s©ñ¦ì¸m A)·s¼W I)´¡¤J N)¤U¤@­Ó Q)Â÷¶} [A] ");
+ switch (ans)
+ {
+ case 'q':
+ break;
+
+ case 'i':
+ case 'n':
+
+ rec_ins(xo->dir, &mf, sizeof(MF), xo->pos + (ans == 'n'), 1);
+ break;
+
+ default:
+
+ rec_add(xo->dir, &mf, sizeof(MF));
+ break;
+ }
+
+ return mf_init(xo);
+}
+
+
+static void
+mf_do_delete(folder)
+ char *folder;
+{
+ MF mf;
+ char fpath[64];
+ FILE *fp;
+
+ if (!(fp = fopen(folder, "r")))
+ return;
+
+ while (fread(&mf, sizeof(MF), 1, fp) == 1)
+ {
+ if (mf.mftype & MF_FOLDER)
+ {
+ mf_fpath(fpath, cuser.userid, mf.xname);
+ mf_do_delete(fpath);
+ }
+ }
+
+ fclose(fp);
+ unlink(folder);
+}
+
+
+static int
+mf_delete(xo)
+ XO *xo;
+{
+ MF *mf;
+ int mftype;
+ char fpath[64];
+
+ mf = (MF *) xo_pool + (xo->pos - xo->top);
+ mftype = mf->mftype;
+
+ if (mftype & MF_MARK)
+ return XO_NONE;
+
+ if (vans(msg_del_ny) == 'y')
+ {
+ if (mftype & MF_FOLDER)
+ {
+ mf_fpath(fpath, cuser.userid, mf->xname);
+ mf_do_delete(fpath);
+ }
+ if (!rec_del(xo->dir, sizeof(MF), xo->pos, NULL))
+ return mf_load(xo);
+ }
+
+ return XO_FOOT;
+}
+
+
+static void
+delmf(xo, mf)
+ XO *xo;
+ MF *mf;
+{
+ if (mf->mftype & MF_FOLDER)
+ {
+ char fpath[64];
+
+ mf_fpath(fpath, cuser.userid, mf->xname);
+ mf_do_delete(fpath);
+ }
+}
+
+
+static int
+mf_rangedel(xo) /* amaki.030910: ´£¨Ñ§Úªº³Ì·R°Ï¬q§R°£ */
+ XO *xo;
+{
+ return xo_rangedel(xo, sizeof(MF), NULL, delmf);
+}
+
+
+static int
+mf_title(xo)
+ XO *xo;
+{
+ MF *mf, xmf;
+
+ mf = (MF *) xo_pool + (xo->pos - xo->top);
+ xmf = *mf;
+
+ if (!(mf->mftype & (MF_FOLDER | MF_LINE)))
+ return XO_NONE;
+
+ vget(b_lines, 0, "¼ÐÃD¡G", xmf.title, BTLEN + 1, GCARRY);
+
+ if (memcmp(mf, &xmf, sizeof(MF)) && vans(msg_sure_ny) == 'y')
+ {
+ int num;
+
+ *mf = xmf;
+ num = xo->pos;
+ rec_put(xo->dir, mf, sizeof(MF), xo->pos, NULL);
+ num++;
+ move(num - xo->top + 2, 0);
+ mf_item(num, mf);
+ }
+
+ return XO_FOOT;
+}
+
+
+static int
+mf_move(xo)
+ XO *xo;
+{
+ MF *mf;
+ char *dir, buf[40];
+ int pos, newOrder;
+
+ pos = xo->pos;
+ mf = (MF *) xo_pool + (pos - xo->top);
+
+ sprintf(buf, "½Ð¿é¤J²Ä %d ¿ï¶µªº·s¦ì¸m¡G", pos + 1);
+ if (!vget(b_lines, 0, buf, buf, 5, DOECHO))
+ return XO_FOOT;
+
+ newOrder = atoi(buf) - 1;
+ if (newOrder < 0)
+ newOrder = 0;
+ else if (newOrder >= xo->max)
+ newOrder = xo->max - 1;
+
+ if (newOrder != pos)
+ {
+ dir = xo->dir;
+ if (!rec_del(dir, sizeof(MF), pos, NULL))
+ {
+ rec_ins(dir, mf, sizeof(MF), newOrder, 1);
+ xo->pos = newOrder;
+ return mf_load(xo);
+ }
+ }
+
+ return XO_FOOT;
+}
+
+
+static int
+mf_mark(xo)
+ XO *xo;
+{
+ MF *mf;
+
+ mf = (MF *) xo_pool + (xo->pos - xo->top);
+
+ if (mf->mftype & MF_FOLDER)
+ {
+ int num;
+
+ mf->mftype ^= MF_MARK;
+ num = xo->pos;
+ rec_put(xo->dir, mf, sizeof(MF), num, NULL);
+ num++;
+ move(num - xo->top + 2, 0);
+ mf_item(num, mf);
+ }
+
+ return XO_NONE;
+}
+
+
+static int
+mf_browse(xo)
+ XO *xo;
+{
+ int type, bno;
+ char *xname, fpath[64];
+ BRD *brd;
+ MF *mf;
+
+ mf = (MF *) xo_pool + (xo->pos - xo->top);
+ type = mf->mftype;
+ xname = mf->xname;
+
+ if (type & MF_BOARD) /* ¬ÝªO±¶®| */
+ {
+ /* itoc.010726: ­Y¬O¬ÝªO¤w¸g³Q¬å©ÎÅv­­¨S¦³¤F¡A«h­n²¾°£±¶®| */
+ if ((bno = brd_bno(xname)) < 0 || !(brd_bits[bno] & BRD_R_BIT))
+ {
+ //rec_del(xo->dir, sizeof(MF), xo->pos, NULL);
+ //vmsg("¥»¬ÝªO¤w³Q§R°£©Î±z¨S¦³Åv­­¾\\Ū¥»¬ÝªO¡A¨t²Î±N¦Û°Ê²¾°£±¶®|");
+ vmsg ("¥»¬ÝªO¤w³Q§R°£©Î±z¨S¦³Åv­­¾\\Ū¥»¬ÝªO¡C");
+ return mf_load(xo);
+ }
+
+ brd = bshm->bcache + bno;
+ if(XoPost(bno)){
+ return mf_load(xo);
+ }
+ xover(XZ_POST);
+#ifndef ENHANCED_VISIT
+ time(&brd_visit[bno]);
+#endif
+
+#ifdef AUTO_JUMPBRD
+ if (cuser.ufo & UFO_JUMPBRD)
+ mf_jumpnext = 1; /* itoc.010910: ¥u¦³¦bÂ÷¶}¬ÝªO¦^¨ì¬ÝªO¦Cªí®É¤~»Ý­n¸õ¥h¤U¤@­Ó¥¼Åª¬ÝªO */
+#endif
+
+ return mf_init(xo);
+ }
+ else if (type & MF_GEM) /* ºëµØ°Ï±¶®| */
+ {
+ /* itoc.010726: ­Y¬O¬ÝªO¤w¸g³Q¬å©ÎÅv­­¨S¦³¤F¡A«h­n²¾°£±¶®| */
+ if ((type = gem_link(xname)) < 0)
+ {
+ //rec_del(xo->dir, sizeof(MF), xo->pos, NULL);
+ //vmsg("¥»¬ÝªO¤w³Q§R°£©Î±z¨S¦³Åv­­¾\\Ū¥»¬ÝªO¡A¨t²Î±N¦Û°Ê²¾°£±¶®|");
+ vmsg ("¥»¬ÝªO¤w³Q§R°£©Î±z¨S¦³Åv­­¾\\Ū¥»¬ÝªO¡C");
+ return mf_load(xo);
+ }
+
+ gem_fpath(fpath, xname, fn_dir);
+ XoGem(fpath, "ºëµØ°Ï", type);
+ return mf_init(xo);
+ }
+ else if (type & MF_FOLDER) /* ¸ê®Æ§¨ */
+ {
+ mf_fpath(fpath, cuser.userid, xname);
+ XoMF(fpath);
+ return mf_load(xo);
+ }
+
+ return XO_NONE;
+}
+
+
+static int
+mf_copy(xo)
+ XO *xo;
+{
+ MF *mf;
+
+ mf = (MF *) xo_pool + (xo->pos - xo->top);
+
+ memcpy(&mftmp, mf, sizeof(MF));
+ zmsg("«þ¨©§¹¦¨¡A¦ý¬O¨÷©v¤ºªº¸ê®Æ¤£·|³Q«þ¨©");
+
+ return XO_FOOT;
+}
+
+
+static int
+mf_paste(xo)
+ XO *xo;
+{
+ MF mf;
+ int ans;
+
+ if (!mftmp.chrono)
+ {
+ zmsg("½Ð¥ý°õ¦æ copy ©R¥O«á¦A paste");
+ return XO_FOOT;
+ }
+
+ memcpy(&mf, &mftmp, sizeof(MF));
+ time(&mf.chrono); /* ³y¤@­Ó·sªº chrono */
+
+ /* itoc.010726.µù¸Ñ: ­Y¬O MF_FOLDER¡A«h´«­ÓÀɦW¦A¶K¤W¡A¤@­Ó¨÷©v¤@­ÓÀÉ®× */
+ /* itoc.010726.µù¸Ñ: ¨÷©v½Æ»s¶K¤W¡A¸Ì­±ªºªF¦è¨Ã¨S¦³¶K¤W¡AÃi±o¼g recursive ªºµ{¦¡ :p */
+ if (mf.mftype & MF_FOLDER)
+ {
+ if (mf_stamp(&mf) < 0)
+ {
+ vmsg("¸ê®Æ¦³»~¡A½Ð­«·s½Æ»s«á¦A¶K¤W");
+ return XO_FOOT;
+ }
+ }
+
+ ans = vans("¦s©ñ¦ì¸m A)·s¼W I)´¡¤J N)¤U¤@­Ó Q)Â÷¶} [A] ");
+ switch (ans)
+ {
+ case 'q':
+ return XO_FOOT;
+
+ case 'i':
+ case 'n':
+
+ rec_ins(xo->dir, &mf, sizeof(MF), xo->pos + (ans == 'n'), 1);
+ break;
+
+ default:
+
+ rec_add(xo->dir, &mf, sizeof(MF));
+ break;
+ }
+
+ return mf_load(xo);
+}
+
+
+static int
+mf_namemode(xo)
+ XO *xo;
+{
+ cuser.ufo ^= UFO_BRDPOST;
+ cutmp->ufo = cuser.ufo;
+ class_flag ^= UFO_BRDPOST; /* ©M class ¦P¨B */
+ return mf_head(xo);
+}
+
+
+static int
+mf_edit(xo) /* itoc.010110: §Úªº³Ì·R¤¤¬ÝªO­×§ï */
+ XO *xo;
+{
+ MF *mf;
+
+ mf = (MF *) xo_pool + (xo->pos - xo->top);
+
+ if ((mf->mftype & MF_BOARD) && (HAS_PERM(PERM_ALLBOARD | PERM_BM)))
+ {
+ int bno;
+
+ bno = brd_bno(mf->xname);
+ if (bno >= 0)
+ {
+ if (!HAS_PERM(PERM_ALLBOARD))
+ brd_title(bno);
+ else
+ brd_edit(bno);
+ return mf_init(xo);
+ }
+ }
+ return XO_NONE;
+}
+
+
+static int
+mf_switch(xo)
+ XO *xo;
+{
+ Select();
+ return mf_init(xo);
+}
+
+
+static int
+mf_visit(xo) /* itoc.010402: ¬ÝªO¦Cªí³]©w¬ÝªO¤wŪ */
+ XO *xo;
+{
+ int bno;
+ MF *mf;
+
+ mf = (MF *) xo_pool + (xo->pos - xo->top);
+ bno = brd_bno(mf->xname);
+
+ if (bno >= 0) /* itoc.010110: À³¸Ó¤£·| < 0 ? */
+ {
+ BRD *brd;
+ brd = bshm->bcache + bno;
+ brh_get(brd->bstamp, bno);
+ brh_visit(0);
+#ifndef ENHANCED_VISIT
+ time(&brd_visit[bno]);
+#endif
+ if(xo->pos+1 < xo->max)
+ {
+ xo->pos++;
+ return mf_load(xo);
+ }
+ }
+ return mf_body(xo);
+}
+
+
+static int
+mf_unvisit(xo) /* itoc.010402: ¬ÝªO¦Cªí³]©w¬ÝªO¥¼Åª */
+ XO *xo;
+{
+ int bno;
+ MF *mf;
+
+ mf = (MF *) xo_pool + (xo->pos - xo->top);
+ bno = brd_bno(mf->xname);
+
+ if (bno >= 0) /* itoc.010110: À³¸Ó¤£·| < 0 ? */
+ {
+ BRD *brd;
+ brd = bshm->bcache + bno;
+ brh_get(brd->bstamp, bno);
+ brh_visit(1);
+#ifndef ENHANCED_VISIT
+ brd_visit[bno] = 0; /* itoc.010402: ³ÌªñÂsÄý®É¶¡Âk¹s¡A¨Ï¬ÝªO¦Cªí¤¤Åã¥Ü¥¼Åª */
+#endif
+ }
+ return mf_body(xo);
+}
+
+
+static int
+mf_nextunread(xo)
+ XO *xo;
+{
+ int max, pos, bno;
+ MF *mf;
+
+ max = xo->max;
+ pos = xo->pos;
+ mf = (MF *) xo_pool + (xo->pos - xo->top);
+
+ while (++pos < max)
+ {
+ bno = brd_bno((++mf)->xname);
+ if (bno >= 0 && !(brd_bits[bno] & BRD_Z_BIT)) /* ¸õ¹L¤ÀÃþ¤Î zap ±¼ªº¬ÝªO */
+ {
+ BRD *brd;
+ brd = bshm->bcache + bno;
+
+#ifdef ENHANCED_VISIT
+ /* itoc.010407: §ï¥Î³Ì«á¤@½g¤wŪ/¥¼Åª¨Ó§PÂ_ */
+ brh_get(brd->bstamp, bno);
+
+ if (brh_unread(brd->blast))
+#else
+ if (brd->blast > brd_visit[bno])
+#endif
+ return pos + XO_MOVE;
+ }
+ }
+
+ return XO_NONE;
+}
+
+
+static int
+mf_help(xo)
+ XO *xo;
+{
+ xo_help("mf");
+ return mf_head(xo);
+}
+
+static int
+mf_binfo(XO* xo){
+ MF* mf = (MF *) xo_pool + (xo->pos - xo->top);
+ int bno;
+ bno = brd_bno(mf->xname);
+ BRD *brd;
+ brd = bshm->bcache + bno;
+ do_binfo(brd);
+ return mf_init(xo);
+}
+
+static KeyFunc mf_cb[] =
+{
+ XO_INIT, mf_init,
+ XO_LOAD, mf_load,
+ XO_HEAD, mf_head,
+ XO_BODY, mf_body,
+
+ 'r', mf_browse,
+ 'd', mf_delete,
+ 'D', mf_rangedel,
+ 'o', mf_mark,
+ 'm', mf_move,
+ 'T', mf_title,
+ 'E', mf_edit,
+ 's', mf_switch,
+ 'c', mf_namemode,
+ 'v', mf_visit,
+ 'V', mf_unvisit,
+ '`', mf_nextunread,
+ 'W', mf_binfo,
+
+ Ctrl('P'), mf_add,
+ 'C', mf_copy,
+ 'g', mf_copy,
+ 'p', mf_paste,
+ Ctrl('V'), mf_paste,
+
+ 'h', mf_help
+};
+
+
+void
+XoMF(folder)
+ char *folder;
+{
+ XO *xo, *last;
+
+ last = xz[XZ_MF - XO_ZONE].xo; /* record */
+
+ xz[XZ_MF - XO_ZONE].xo = xo = xo_new(folder);
+ xo->pos = 0;
+
+#ifdef AUTO_JUMPBRD
+ if (cuser.ufo & UFO_JUMPBRD)
+ mf_jumpnext = 1; /* itoc.020615: ¥D°Ê¸õ¥h¤U¤@­Ó¥¼Åª¬ÝªO */
+#endif
+ xover(XZ_MF);
+
+ free(xo);
+ xz[XZ_MF - XO_ZONE].xo = last; /* restore */
+}
+
+
+int
+MyFavorite()
+{
+ char fpath[64];
+
+ mftmp.chrono = 0; /* ªì©l¤Æ */
+ mf_fpath(fpath, cuser.userid, FN_MF);
+ XoMF(fpath);
+
+ return 0;
+}
+
+
+void
+mf_main()
+{
+ char fpath[64];
+ XO *xo;
+
+ mf_fpath(fpath, cuser.userid, FN_MF);
+ xz[XZ_MF - XO_ZONE].xo = xo = xo_new(fpath);
+ xz[XZ_MF - XO_ZONE].cb = mf_cb;
+
+ xo->pos = 0;
+}
+#endif /* MY_FAVORITE */
diff --git a/maple/gem.c b/maple/gem.c
new file mode 100644
index 0000000..b02bdd4
--- /dev/null
+++ b/maple/gem.c
@@ -0,0 +1,1472 @@
+/*-------------------------------------------------------*/
+/* gem.c ( NTHU CS MapleBBS Ver 3.00 ) */
+/*-------------------------------------------------------*/
+/* target : ºëµØ°Ï¾\Ū¡B½s¿ï */
+/* create : 95/03/29 */
+/* update : 97/02/02 */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+
+extern XZ xz[];
+extern char xo_pool[];
+extern char brd_bits[];
+
+extern int TagNum;
+extern TagItem TagList[];
+
+
+#define GEM_WAY 3
+static int gem_way; /* 0:¥u¦L¼ÐÃD 1:¼ÐÃD¥[ÀɦW 2:¼ÐÃD¥[½sªÌ */
+
+static int GemBufferNum; /* Thor.990414: ´£«e«Å§i¡A¥Î©ógem_head */
+
+static char GemAnchor[64]; /* ©wÁã°Ïªº¸ô®| */
+static char GemSailor[20]; /* ©wÁã°Ïªº¼ÐÃD */
+
+static int gem_add_all();
+static int gem_paste();
+static int gem_anchor();
+
+
+static void
+gem_item(num, hdr, level)
+ int num;
+ HDR *hdr;
+ int level;
+{
+ int xmode, gtype;
+
+ /* ¡·¡¸¡¹¡º¡»¡¼¡½¡¾¡¿ : A1B7 ... */
+
+ xmode = hdr->xmode;
+ gtype = (char) 0xba;
+
+ /* ¥Ø¿ý¥Î¹ê¤ß¡A¤£¬O¥Ø¿ý¥ÎªÅ¤ß */
+ if (xmode & GEM_FOLDER) /* ¤å³¹:¡º ¨÷©v:¡» */
+ gtype += 1;
+
+ if (hdr->xname[0] == '@') /* ¸ê®Æ:¡¸ ¤ÀÃþ:¡¹ */
+ gtype -= 2;
+ else if (xmode & GEM_BOARD) /* ¬ÝªO:¡½ */
+ gtype += 2;
+
+ prints("%6d%c%c\241%c ", num, xmode & GEM_RESTRICT ? ')' : ' ',
+ TagNum && !Tagger(hdr->chrono, num - 1, TAG_NIN) ? '*' : ' ', gtype);
+
+ if ((xmode & GEM_RESTRICT) && !(level & GEM_M_BIT))
+ outs(MSG_DATA_CLOAK); /* itoc.000319: ­­¨î¯Å¤å³¹«O±K */
+ else if (gem_way == 0)
+ prints("%.*s\n", d_cols + 64, hdr->title);
+ else
+ prints("%-*.*s%-13s%s\n", d_cols + 46, d_cols + 45, hdr->title, (gem_way == 1 ? hdr->xname : hdr->owner), hdr->date);
+}
+
+
+static int
+gem_body(xo)
+ XO *xo;
+{
+ HDR *hdr;
+ int num, max, tail;
+
+ max = xo->max;
+ if (max <= 0)
+ {
+ outs("\n\n¡mºëµØ°Ï¡n©|¦b§l¨ú¤Ñ¦a¶¡ªº¤éºë¤ëµØ :)");
+
+ if (xo->key & GEM_W_BIT)
+ {
+ switch (vans("(A)·s¼W¸ê®Æ (P)¶K½Æ (G)®üÁã¥\\¯à [N]µL©Ò¨Æ¨Æ "))
+ {
+ case 'a':
+ max = gem_add_all(xo);
+ if (xo->max > 0)
+ return max;
+ break;
+
+ case 'p':
+ max = gem_paste(xo);
+ if (xo->max > 0)
+ return max;
+ break;
+
+ case 'g':
+ gem_anchor(xo);
+ break;
+ }
+ }
+ else
+ {
+ vmsg(NULL);
+ }
+ return XO_QUIT;
+ }
+
+ hdr = (HDR *) xo_pool;
+ num = xo->top;
+ tail = num + XO_TALL;
+ if (max > tail)
+ max = tail;
+
+ move(3, 0);
+ tail = xo->key; /* ­É¥Î tail */
+ do
+ {
+ gem_item(++num, hdr++, tail);
+ } while (num < max);
+ clrtobot();
+
+ /* return XO_NONE; */
+ return XO_FOOT; /* itoc.010403: §â b_lines ¶ñ¤W feeter */
+}
+
+
+static int
+gem_head(xo)
+ XO *xo;
+{
+ char buf[20];
+
+ vs_head("ºëµØ¤å³¹", xo->xyz);
+
+ if ((xo->key & GEM_W_BIT) && GemBufferNum > 0)
+ sprintf(buf, "(°Å¶Kï %d ½g)", GemBufferNum);
+ else
+ buf[0] = '\0';
+
+ prints(NECKER_GEM, buf, d_cols, "");
+ return gem_body(xo);
+}
+
+
+static int
+gem_toggle(xo)
+ XO *xo;
+{
+ gem_way++;
+ gem_way %= GEM_WAY;
+
+ /* ¥u¦³¯¸ªø¯à¬Ý¨ìÀɦW */
+ /* lantw44: ªO¥D´N¥i¥H¬ÝÀɦW¤F */
+ if (!(xo->key & GEM_M_BIT) && gem_way == 1)
+ gem_way++;
+
+ return gem_body(xo);
+}
+
+
+static int
+gem_init(xo)
+ XO *xo;
+{
+ xo_load(xo, sizeof(HDR));
+ return gem_head(xo);
+}
+
+
+static int
+gem_load(xo)
+ XO *xo;
+{
+ xo_load(xo, sizeof(HDR));
+ return gem_body(xo);
+}
+
+
+/* ----------------------------------------------------- */
+/* gem_check : attribute check out */
+/* ----------------------------------------------------- */
+
+
+#define GEM_PLAIN 0x01 /* ¹w´Á¬O plain text */
+
+
+static HDR * /* NULL:µLÅvŪ¨ú */
+gem_check(xo, fpath, op)
+ XO *xo;
+ char *fpath;
+ int op;
+{
+ HDR *hdr;
+ int gtype;
+
+ hdr = (HDR *) xo_pool + (xo->pos - xo->top);
+ gtype = hdr->xmode;
+
+ if ((gtype & GEM_RESTRICT) && !(xo->key & GEM_M_BIT))
+ return NULL;
+
+ if (op && (gtype & GEM_LINE))
+ return NULL;
+
+ if ((op & GEM_PLAIN) && (gtype & GEM_FOLDER))
+ return NULL;
+
+ if (fpath)
+ {
+ if (gtype & GEM_BOARD)
+ gem_fpath(fpath, hdr->xname, fn_dir);
+ else
+ hdr_fpath(fpath, xo->dir, hdr);
+ }
+ return hdr;
+}
+
+
+#if 0
+static int /* -1:¤£¬O¬ÝªOºëµØ°Ï >=0:bno */
+gem_bno(xo)
+ XO *xo;
+{
+ char *dir, *str;
+ int bno;
+
+ /* ¥Ñ xo->dir §ä¥X¥Ø«e¦b­þ¤@­ÓªOªººëµØ°Ï */
+ dir = xo->dir;
+
+ /* Àˬd¬O§_¬° gem/brd/brdname/.DIR ªº®æ¦¡¡A
+ Á×§K­Y¦b gem/.DIR ©Î usr/u/userid/gem/.DIR ·|³y¦¨¿ù»~ */
+ if (dir[0] == 'g' && dir[4] == 'b')
+ {
+ dir += 8; /* ¸õ¹L "gem/brd/" */
+ if (str = strchr(dir, '/'))
+ {
+ *str = '\0';
+ bno = brd_bno(dir);
+ *str = '/';
+ return bno;
+ }
+ }
+
+ return -1;
+}
+#endif
+
+
+/* ----------------------------------------------------- */
+/* ¸ê®Æ¤§·s¼W¡Gappend / insert */
+/* ----------------------------------------------------- */
+
+
+void
+brd2gem(brd, gem)
+ BRD *brd;
+ HDR *gem;
+{
+ memset(gem, 0, sizeof(HDR));
+ time(&gem->chrono);
+ str_stamp(gem->date, &gem->chrono);
+ strcpy(gem->xname, brd->brdname);
+ sprintf(gem->title, "%-13s%-5s%s", brd->brdname, brd->class, brd->title);
+ gem->xmode = GEM_BOARD | GEM_FOLDER;
+}
+
+
+#if 0 /* itoc.010218: ´«·sªº gem_log() */
+static void
+gem_log(folder, action, hdr)
+ char *folder;
+ char *action;
+ HDR *hdr;
+{
+ char fpath[64], buf[256];
+
+ if (hdr->xmode & (GEM_RESTRICT | GEM_RESERVED))
+ return;
+
+ str_folder(fpath, folder, "@/@log");
+ sprintf(buf, "[%s] %s (%s) %s\n%s\n\n",
+ action, hdr->xname, Now(), cuser.userid, hdr->title);
+ f_cat(fpath, buf);
+}
+#endif
+
+
+static void
+gem_log(folder, action, hdr)
+ char *folder;
+ char *action;
+ HDR *hdr;
+{
+ char fpath1[64], fpath2[64];
+ FILE *fp1, *fp2;
+
+ if (hdr->xmode & (GEM_RESTRICT | GEM_RESERVED))
+ return;
+
+ /* mv @log @log.old */
+ str_folder(fpath1, folder, "@/@log");
+ str_folder(fpath2, folder, "@/@log.old");
+ f_mv(fpath1, fpath2);
+
+ if (!(fp1 = fopen(fpath1, "a")))
+ return;
+
+ /* §â·sªº²§°Ê©ñ¦b³Ì¤W­±¡A¨Ã¨Ì²§°Ê¶¶§Ç½s¸¹ */
+
+ fprintf(fp1, "<01> %s %-12s [%s] %s\n %s\n\n", Now(),
+ cuser.userid, action, hdr->xname, hdr->title);
+
+ if (fp2 = fopen(fpath2, "r"))
+ {
+ char buf[STRLEN];
+ int i = 6; /* ±q²Ä¤G½g¶}©l */
+ int j;
+
+ while (fgets(buf, STRLEN, fp2))
+ {
+ if (++i > 63) /* ¥u«O¯d³Ì·s 20 µ§²§°Ê */
+ break;
+
+ j = i % 3;
+ if (j == 1) /* ²Ä¤@¦æ */
+ fprintf(fp1, "<%02d> %s", i / 3, buf + 5);
+ else if (j == 2) /* ²Ä¤G¦æ */
+ fprintf(fp1, "%s\n", buf);
+ /* ²Ä¤T¦æ¬OªÅ¦æ */
+ }
+ fclose(fp2);
+ }
+ fclose(fp1);
+}
+
+
+static int
+gem_add(xo, gtype)
+ XO *xo;
+ int gtype;
+{
+ int level, fd, ans;
+ char title[80], fpath[64], *dir;
+ HDR hdr;
+
+ level = xo->key;
+ if (!(level & GEM_W_BIT))
+ return XO_NONE;
+
+ if (!gtype)
+ {
+ gtype = vans((level & GEM_X_BIT) ?
+ /* "·s¼W A)rticle B)oard C)lass D)ata F)older P)aste Q)uit [Q] " : */
+ "·s¼W (A)¤å³¹ (B)¬ÝªO (C)¤ÀÃþ (D)¸ê®Æ (F)¨÷©v (L)¤À¹j (P)¶K½Æ (Q)¨ú®ø¡H[Q] " :
+ "·s¼W (A)¤å³¹ (F)¨÷©v (L)¤À¹j (P)¶K½Æ (Q)¨ú®ø¡H[Q] ");
+ }
+
+ if (gtype == 'p')
+ return gem_paste(xo);
+
+ if (gtype != 'a' && gtype != 'f' && gtype != 'l' &&
+ (!(level & GEM_X_BIT) || (gtype != 'b' && gtype != 'c' && gtype != 'd')))
+ return XO_FOOT;
+
+ dir = xo->dir;
+ fd = -1;
+
+ if (gtype == 'b')
+ {
+ BRD *brd;
+
+ if (!(brd = ask_board(fpath, BRD_L_BIT, NULL)))
+ return gem_head(xo);
+
+ brd2gem(brd, &hdr);
+ gtype = 0;
+ }
+ else
+ {
+ if (!vget(b_lines, 0, "¼ÐÃD¡G", title, TTLEN + 1, DOECHO))
+ return XO_FOOT;
+
+ if (gtype == 'c' || gtype == 'd')
+ {
+ if (!vget(b_lines, 0, "ÀɦW¡G", fpath, BNLEN + 1, DOECHO))
+ return XO_FOOT;
+
+ if (strchr(fpath, '/'))
+ {
+ zmsg("¤£¦XªkªºÀɮצWºÙ");
+ /* return XO_NONE; */
+ return XO_FOOT; /* itoc.010726: §â b_lines ¶ñ¤W feeter */
+ }
+
+ memset(&hdr, 0, sizeof(HDR));
+ time(&hdr.chrono);
+ str_stamp(hdr.date, &hdr.chrono);
+ sprintf(hdr.xname, "@%s", fpath);
+ if (gtype == 'c')
+ {
+ strcat(fpath, "/");
+ sprintf(hdr.title, "%-13s¤ÀÃþ ¡¼ %.50s", fpath, title);
+ hdr.xmode = GEM_FOLDER;
+ }
+ else
+ {
+ strcpy(hdr.title, title);
+ hdr.xmode = 0;
+ }
+ gtype = 1;
+ }
+ else
+ {
+ if ((fd = hdr_stamp(dir, gtype, &hdr, fpath)) < 0)
+ return XO_FOOT;
+ close(fd);
+
+ if (gtype == 'a')
+ {
+ if (vedit(fpath, 0)) /* Thor.981020: ª`·N³Qtalkªº°ÝÃD */
+ {
+ unlink(fpath);
+ zmsg(msg_cancel);
+ return gem_head(xo);
+ }
+ gtype = 0;
+ }
+ else if (gtype == 'f')
+ {
+ gtype = GEM_FOLDER;
+ }
+ else if (gtype == 'l')
+ {
+ gtype = GEM_LINE;
+ }
+ hdr.xmode = gtype;
+ strcpy(hdr.title, title);
+ }
+ }
+
+ /* ans = vans("¦s©ñ¦ì¸m A)ppend I)nsert N)ext Q)uit [A] "); */
+ ans = vans("¦s©ñ¦ì¸m A)¥[¨ì³Ì«á I/N)´¡¤J¥Ø«e¦ì¸m Q)Â÷¶} [A] ");
+
+ if (ans == 'q')
+ {
+ if (fd >= 0)
+ unlink(fpath);
+ return (gtype ? XO_FOOT : gem_head(xo));
+ }
+
+ strcpy(hdr.owner, cuser.userid);
+
+ if (vans("¬O§_¶i¦æ¥[±K(Y/N)¡H[N] ") == 'y')
+ hdr.xmode ^= GEM_RESTRICT;
+
+ if (ans == 'i' || ans == 'n')
+ rec_ins(dir, &hdr, sizeof(HDR), xo->pos + (ans == 'n'), 1);
+ else
+ rec_add(dir, &hdr, sizeof(HDR));
+
+ if (!(hdr.xmode & GEM_RESTRICT))
+ gem_log(dir, "·s¼W", &hdr);
+
+ return (gtype ? gem_load(xo) : gem_init(xo));
+}
+
+
+static int
+gem_add_all(xo)
+ XO *xo;
+{
+ return gem_add(xo, 0);
+}
+
+
+static int
+gem_add_article(xo) /* itoc.010419: §Ö³tÁä */
+ XO *xo;
+{
+ return gem_add(xo, 'a');
+}
+
+
+static int
+gem_add_folder(xo) /* itoc.010419: §Ö³tÁä */
+ XO *xo;
+{
+ return gem_add(xo, 'f');
+}
+
+
+/* ----------------------------------------------------- */
+/* ¸ê®Æ¤§­×§ï¡Gedit / title */
+/* ----------------------------------------------------- */
+
+
+static int
+gem_edit(xo)
+ XO *xo;
+{
+ int level;
+ char fpath[64];
+ HDR *hdr;
+
+ if (!(hdr = gem_check(xo, fpath, GEM_PLAIN)))
+ return XO_NONE;
+
+ level = xo->key;
+
+ if (!(level & GEM_W_BIT) || ((hdr->xmode & GEM_RESERVED) && !(level & GEM_X_BIT)))
+ {
+ vedit(fpath, -1);
+ }
+ else
+ {
+ if (vedit(fpath, 0) >= 0)
+ gem_log(xo->dir, "­×§ï", hdr);
+ }
+
+ return gem_head(xo);
+}
+
+
+static int
+gem_title(xo)
+ XO *xo;
+{
+ HDR *fhdr, mhdr;
+ int pos, cur;
+
+ if (!(xo->key & GEM_W_BIT) || !(fhdr = gem_check(xo, NULL, 0)))
+ return XO_NONE;
+
+ memcpy(&mhdr, fhdr, sizeof(HDR));
+
+ vget(b_lines, 0, "¼ÐÃD¡G", mhdr.title, TTLEN + 1, GCARRY);
+
+ if (xo->key & GEM_X_BIT)
+ {
+ vget(b_lines, 0, "½sªÌ¡G", mhdr.owner, IDLEN + 1, GCARRY);
+ /* vget(b_lines, 0, "¼ÊºÙ¡G", mhdr.nick, sizeof(mhdr.nick), GCARRY); */ /* ºëµØ°Ï¦¹Äæ¦ì¬°ªÅ */
+ vget(b_lines, 0, "¤é´Á¡G", mhdr.date, sizeof(mhdr.date), GCARRY);
+ }
+
+ if (memcmp(fhdr, &mhdr, sizeof(HDR)) && vans(msg_sure_ny) == 'y')
+ {
+ pos = xo->pos;
+ cur = pos - xo->top;
+
+ memcpy(fhdr, &mhdr, sizeof(HDR));
+ rec_put(xo->dir, fhdr, sizeof(HDR), pos, NULL);
+
+ move(3 + cur, 0);
+ gem_item(++pos, fhdr, xo->key);
+
+ gem_log(xo->dir, "¼ÐÃD", fhdr);
+ }
+ return XO_FOOT;
+}
+
+
+static int
+gem_refuse(xo)
+ XO *xo;
+{
+ HDR *hdr;
+ int num;
+
+ if ((xo->key & GEM_M_BIT) && (hdr = gem_check(xo, NULL, 0)))
+ {
+ hdr->xmode ^= GEM_RESTRICT;
+
+ num = xo->pos;
+ rec_put(xo->dir, hdr, sizeof(HDR), num, NULL);
+ num++;
+ move(num - xo->top + 2, 0);
+ gem_item(num, hdr, xo->key);
+ }
+
+ return XO_NONE;
+}
+
+
+static int
+gem_state(xo)
+ XO *xo;
+{
+ HDR *hdr;
+ char fpath[64];
+ struct stat st;
+
+ if ((xo->key & GEM_W_BIT) && (hdr = gem_check(xo, fpath, 0)))
+ {
+ move(12, 0);
+ clrtobot();
+ prints("\nDir : %s", xo->dir);
+ prints("\nName: %s", hdr->xname);
+ prints("\nFile: %s", fpath);
+
+ if (!stat(fpath, &st))
+ {
+ prints("\nTime: %s", Btime(&st.st_mtime));
+ prints("\nSize: %d", st.st_size);
+ }
+
+ vmsg(NULL);
+ return gem_body(xo);
+ }
+
+ return XO_NONE;
+}
+
+
+/* ----------------------------------------------------- */
+/* ¸ê®Æ¤§ÂsÄý¡Gedit / title */
+/* ----------------------------------------------------- */
+
+
+int /* -1:µLÅv­­ */
+gem_link(brdname) /* Àˬd³sµ²¥h¨ä¥L¬ÝªOºëµØ°ÏªºÅv­­ */
+ char *brdname;
+{
+ int bno, level;
+
+ if ((bno = brd_bno(brdname)) < 0 || !((bno = brd_bits[bno]) & BRD_R_BIT))
+ return -1;
+
+ level = 0;
+ if (bno & BRD_X_BIT)
+ level ^= GEM_W_BIT;
+ if (HAS_PERM(PERM_SYSOP))
+ level ^= GEM_X_BIT;
+ if (bno & BRD_M_BIT)
+ level ^= GEM_M_BIT;
+
+ return level;
+}
+
+
+static int
+gem_browse(xo)
+ XO *xo;
+{
+ HDR *hdr;
+ int op, xmode;
+ char fpath[64], title[TTLEN + 1], *ptr;
+
+ op = 0;
+
+ for (;;)
+ {
+ if (!(hdr = gem_check(xo, fpath, op)))
+ break;
+
+ xmode = hdr->xmode;
+
+ /* browse folder */
+
+ if (xmode & GEM_FOLDER)
+ {
+ strcpy(title, hdr->title);
+
+ if (xmode & GEM_BOARD)
+ {
+ if ((op = gem_link(hdr->xname)) < 0)
+ {
+ vmsg("¹ï¤£°_¡A¦¹ªOºëµØ°Ï¥u­ãªO¤Í¶i¤J¡A½Ð¦VªO¥D¥Ó½Ð¤J¹Ò³\\¥i");
+ return XO_FOOT;
+ }
+ }
+ else /* ¤@¯ë¨÷©v¤~¦³¤pªO¥D */
+ {
+ op = xo->key; /* Ä~©Ó¥À¨÷©vªºÅv­­ */
+
+ /* itoc.011217: [userA/userB ªº¦h¦ì¤pªO¥D¼Ò¦¡¤]¾A¥Î */
+ if ((ptr = strrchr(title, '[')) && is_bm(ptr + 1, cuser.userid))
+ op |= GEM_W_BIT | GEM_M_BIT;
+ }
+
+ XoGem(fpath, title, op);
+ return gem_init(xo);
+ }
+
+ /* browse article */
+
+ /* Thor.990204: ¬°¦Ò¼{more ¶Ç¦^­È */
+ if ((xmode = more(fpath, FOOTER_GEM)) < 0)
+ break;
+
+ op = GEM_PLAIN;
+
+re_key:
+ switch (xo_getch(xo, xmode))
+ {
+ case XO_BODY:
+ continue;
+
+ case '/':
+ if (vget(b_lines, 0, "·j´M¡G", hunt, sizeof(hunt), DOECHO))
+ {
+ more(fpath, FOOTER_GEM);
+ goto re_key;
+ }
+ continue;
+
+ case 'E':
+ return gem_edit(xo);
+
+ case 'C':
+ {
+ FILE *fp;
+ if (fp = tbf_open())
+ {
+ f_suck(fp, fpath);
+ fclose(fp);
+ }
+ }
+ break;
+
+ case 'h':
+ xo_help("gem");
+ break;
+ }
+ break;
+ }
+
+ return gem_head(xo);
+}
+
+
+/* ----------------------------------------------------- */
+/* ºëµØ°Ï¤§§R°£ */
+/* ----------------------------------------------------- */
+
+
+static int
+chkgem(hdr)
+ HDR *hdr;
+{
+ return (hdr->xmode & (GEM_RESTRICT | GEM_RESERVED));
+}
+
+
+static int
+vfygem(hdr, pos)
+ HDR *hdr;
+ int pos;
+{
+ return (Tagger(hdr->chrono, pos, TAG_NIN) || chkgem(hdr));
+}
+
+
+static void
+delgem(xo, hdr)
+ XO *xo;
+ HDR *hdr;
+{
+ char folder[64];
+ HDR fhdr;
+ FILE *fp;
+
+ if (hdr->xmode & GEM_FOLDER) /* ¨÷©v/¤ÀÃþ/¬ÝªO */
+ {
+ hdr_fpath(folder, xo->dir, hdr);
+
+ /* Kyo.050328: ©wÁã°Ï³Q§R°£®É­n©ÞÁã */
+ if (!strcmp(GemAnchor, folder))
+ GemAnchor[0] = '\0';
+
+ /* ¨÷©v­n¶i¤l¥Ø¿ý§R°£¡F¬ÝªO/¤ÀÃþ«h¤£»Ý­n */
+ if (hdr->xmode == GEM_FOLDER && hdr->xname[0] != '@')
+ {
+ if (fp = fopen(folder, "r"))
+ {
+ while (fread(&fhdr, sizeof(HDR), 1, fp) == 1)
+ delgem(xo, &fhdr);
+
+ fclose(fp);
+ unlink(folder);
+ }
+ }
+ }
+ else /* ¤å³¹/¸ê®Æ */
+ {
+ /* ¤å³¹­n§R°£ÀɮסF¸ê®Æ«h¤£§R°£ÀÉ®× */
+ if (hdr->xname[0] != '@')
+ {
+ hdr_fpath(folder, xo->dir, hdr);
+ unlink(folder);
+ }
+ }
+}
+
+
+static int
+gem_delete(xo)
+ XO *xo;
+{
+ HDR *hdr;
+ int xmode;
+
+ if (!(xo->key & GEM_W_BIT) || !gem_check(xo, NULL, 0))
+ return XO_NONE;
+
+ hdr = (HDR *) xo_pool + (xo->pos - xo->top);
+ xmode = hdr->xmode;
+
+ if (hdr->xmode & (GEM_RESTRICT | GEM_RESERVED))
+ return XO_NONE;
+
+ if (vans(msg_del_ny) == 'y')
+ {
+ delgem(xo, hdr);
+
+ if (!rec_del(xo->dir, sizeof(HDR), xo->pos, NULL))
+ {
+ gem_log(xo->dir, "§R°£", hdr);
+ return gem_load(xo);
+ }
+ }
+
+ return XO_FOOT;
+}
+
+
+static int
+gem_rangedel(xo) /* itoc.010726: ´£¨Ñ°Ï¬q§R°£ */
+ XO *xo;
+{
+ if (!(xo->key & GEM_W_BIT) || !gem_check(xo, NULL, 0))
+ return XO_NONE;
+
+ return xo_rangedel(xo, sizeof(HDR), chkgem, delgem);
+}
+
+
+static int
+gem_prune(xo)
+ XO *xo;
+{
+ if (!(xo->key & GEM_W_BIT))
+ return XO_NONE;
+ return xo_prune(xo, sizeof(HDR), vfygem, delgem);
+}
+
+
+/* ----------------------------------------------------- */
+/* ºëµØ°Ï¤§½Æ»s¡B¶K¤W¡B²¾°Ê */
+/* ----------------------------------------------------- */
+
+
+static char GemFolder[64];
+
+static HDR *GemBuffer;
+static int GemBufferSiz;
+/* static int GemBufferNum; */ /* Thor.990414: ´£«e«Å§iµ¹gem_head¥Î */
+
+
+/* °t¸m¨¬°÷ªºªÅ¶¡©ñ¤J header */
+
+
+static HDR *
+gbuf_malloc(num)
+ int num;
+{
+ HDR *gbuf;
+
+ GemBufferNum = num;
+ if (gbuf = GemBuffer)
+ {
+ if (GemBufferSiz < num)
+ {
+ num += (num >> 1);
+ GemBufferSiz = num;
+ GemBuffer = gbuf = (HDR *) realloc(gbuf, sizeof(HDR) * num);
+ }
+ }
+ else
+ {
+ GemBufferSiz = num;
+ GemBuffer = gbuf = (HDR *) malloc(sizeof(HDR) * num);
+ }
+
+ return gbuf;
+}
+
+
+/* static */ /* itoc.010924: µ¹ post_copy() ¥Î */
+void
+gem_buffer(dir, hdr)
+ char *dir;
+ HDR *hdr; /* NULL ¥Nªí©ñ¤J TagList, §_«h±N¶Ç¤Jªº©ñ¤J */
+{
+ int num, locus;
+ HDR *gbuf;
+
+ if (hdr)
+ {
+ num = 1;
+ }
+ else
+ {
+ num = TagNum;
+ if (num <= 0)
+ return;
+ }
+
+ gbuf = gbuf_malloc(num);
+
+ if (hdr)
+ {
+ memcpy(gbuf, hdr, sizeof(HDR));
+ }
+ else
+ {
+ locus = 0;
+ do
+ {
+ EnumTag(&gbuf[locus], dir, locus, sizeof(HDR));
+ } while (++locus < num);
+ }
+
+ strcpy(GemFolder, dir);
+}
+
+
+static int
+gem_copy(xo)
+ XO *xo;
+{
+ HDR *hdr;
+ int tag;
+
+ if (!(hdr = gem_check(xo, NULL, 0)))
+ return XO_NONE;
+
+ tag = AskTag("ºëµØ°Ï«þ¨©");
+
+ if (tag < 0)
+ return XO_FOOT;
+
+ gem_buffer(xo->dir, tag ? NULL : hdr);
+
+ zmsg("«þ¨©§¹¦¨¡A¦ý¬O¥[±K¤å³¹¤£·|³Q«þ¨©¡C[ª`·N] ¶K¤W«á¤~¯à§R°£­ì¤å¡I");
+
+ /* return XO_FOOT; */
+ return gem_head(xo); /* Thor.990414: Åý°Å¶K½g¼Æ§ó·s */
+}
+
+
+static inline int
+gem_extend(xo, num)
+ XO *xo;
+ int num;
+{
+ char *dir, fpath[64], gpath[64];
+ FILE *fp;
+ time_t chrono;
+ HDR *hdr;
+
+ if (!(hdr = gem_check(xo, fpath, GEM_PLAIN)))
+ return -1;
+
+ if (!(fp = fopen(fpath, "a")))
+ return -1;
+
+ dir = xo->dir;
+ chrono = hdr->chrono;
+
+ for (hdr = GemBuffer; num--; hdr++)
+ {
+ if ((hdr->chrono != chrono) && !(hdr->xmode & (GEM_FOLDER | GEM_RESTRICT | GEM_RESERVED)))
+ {
+ hdr_fpath(gpath, GemFolder, hdr); /* itoc.010924: ­×¥¿¤£¦P¥Ø¿ý·| extend ¥¢±Ñ */
+ fputs(str_line, fp);
+ f_suck(fp, gpath);
+ }
+ }
+
+ fclose(fp);
+ return 0;
+}
+
+
+static int /* 1: µL½a°j°é 0: ¦Xªk */
+invalid_loop(srcDir, dstDir, hdr, depth) /* itoc.010727: Àˬd¬O§_·|³y¦¨µL½a°j°é for gem_paste() */
+ char *srcDir, *dstDir;
+ HDR *hdr;
+ int depth; /* 0: »¼°j²Ä¤@°é 1: »¼°j¤¤ */
+{
+ static int valid;
+
+ int fd;
+ char fpath1[64], fpath2[64];
+ HDR fhdr;
+
+ if (!depth)
+ {
+ if (!(hdr->xmode & GEM_FOLDER)) /* plain text */
+ return 0;
+
+ str_folder(fpath1, srcDir, fn_dir);
+ str_folder(fpath2, dstDir, fn_dir);
+
+ if (strcmp(fpath1, fpath2)) /* ¸ó°Ï«þ¨©¤@©w¤£·|³y¦¨µL½a°j°é */
+ return 0;
+
+ hdr_fpath(fpath1, srcDir, hdr); /* §â¦Û¤v«þ¨ì¦Û¤v¸Ì­± */
+ if (!strcmp(fpath1, dstDir))
+ return 1;
+
+ valid = 0;
+ }
+ else
+ {
+ if (valid) /* ¦b¬Y¤@­Ó»¼°j¤¤§ä¨ì«DªkÃÒ¾Ú´N°±¤î·jÃÒ¤u§@ */
+ return 1;
+
+ hdr_fpath(fpath1, srcDir, hdr);
+ }
+
+ if ((fd = open(fpath1, O_RDONLY)) >= 0)
+ {
+ while (read(fd, &fhdr, sizeof(HDR)) == sizeof(HDR))
+ {
+ if (fhdr.xmode & GEM_FOLDER) /* plain text ¤£·|³y¦¨µL½a°j°é */
+ {
+ hdr_fpath(fpath2, srcDir, &fhdr);
+ if (!strcmp(fpath2, dstDir))
+ {
+ valid = 1;
+ return 1;
+ }
+
+ /* recursive ¦a¤@¼h¤@¼h¥Ø¿ý¶i¥hÀˬd¬O§_·|³y¦¨µL½a°j°é */
+ invalid_loop(fpath1, dstDir, &fhdr, 1);
+ }
+ }
+ close(fd);
+ }
+
+ return valid;
+}
+
+
+static void
+gem_do_paste(srcDir, dstDir, hdr, pos) /* itoc.010725: for gem_paste() */
+ char *srcDir; /* source folder */
+ char *dstDir; /* destination folder */
+ HDR *hdr; /* source hdr */
+ int pos; /* -1: ªþ¥[¦b³Ì«á >=0: ¶K¤Wªº¦ì¸m */
+{
+ int xmode, fsize;
+ char folder[64], fpath[64];
+ HDR fhdr, *data, *head, *tail;
+
+ xmode = hdr->xmode;
+
+ if (xmode & (GEM_RESTRICT | GEM_RESERVED)) /* ­­¨î¯ÅºëµØ°Ï¤£¯à½Æ»s/¶K¤W */
+ return;
+
+ if (xmode & GEM_BOARD) /* ¬ÝªO¤£¯à³Q½Æ»s/¶K¤W */
+ return;
+
+ if (xmode & GEM_FOLDER) /* ¨÷©v/¤ÀÃþ */
+ {
+ /* ¥ý·sÀɫإߦۤv³o­Ó¤ÀÃþ/¨÷©v */
+
+ /* ¦b½Æ»s/¶K¤W«á¤@«ßÅܦ¨¨÷©v¡A¦]¬°¤ÀÃþ¬O¯¸ªø±M¥Î¯S®í¥Î³~ªº */
+ if ((fsize = hdr_stamp(dstDir, 'F', &fhdr, fpath)) < 0)
+ return;
+ close(fsize);
+
+ fhdr.xmode = GEM_FOLDER;
+ }
+ else /* ¤å³¹/¸ê®Æ */
+ {
+ hdr_fpath(folder, srcDir, hdr);
+
+ /* ¦b½Æ»s/¶K¤W«á¤@«ßÅܦ¨¤å³¹¡A¦]¬°¸ê®Æ¬O¯¸ªø±M¥Î¯S®í¥Î³~ªº */
+ hdr_stamp(dstDir, HDR_COPY | 'A', &fhdr, folder);
+ }
+
+ strcpy(fhdr.owner, cuser.userid);
+ strcpy(fhdr.title, hdr->title);
+ if (pos < 0)
+ rec_add(dstDir, &fhdr, sizeof(HDR));
+ else
+ rec_ins(dstDir, &fhdr, sizeof(HDR), pos, 1);
+ gem_log(dstDir, "½Æ»s", &fhdr);
+
+ if (xmode & GEM_FOLDER) /* ¨÷©v/¤ÀÃþ */
+ {
+ /* «Ø¥ß§¹¦Û¤v³o­Ó¨÷©v/¤ÀÃþ¥H«á¡A¦A recursive ¦a¤@¼h¤@¼h¥Ø¿ý¶i¥h¤@½g¤@½g¥t¦s·sÀÉ */
+ hdr_fpath(folder, srcDir, hdr);
+ if (data = (HDR *) f_img(folder, &fsize))
+ {
+ head = data;
+ tail = data + (fsize / sizeof(HDR));
+ do
+ {
+ gem_do_paste(folder, fpath, head, -1);
+ } while (++head < tail);
+
+ free(data);
+ }
+ }
+}
+
+
+static int
+gem_paste(xo)
+ XO *xo;
+{
+ int num, ans, pos;
+ char *dir;
+ HDR *head, *tail;
+
+ if (!(xo->key & GEM_W_BIT))
+ return XO_NONE;
+
+ if (!(num = GemBufferNum))
+ {
+ zmsg("½Ð¥ý°õ¦æ copy ©R¥O«á¦A paste");
+ /* return XO_NONE; */
+ return XO_FOOT; /* itoc.010726: §â b_lines ¶ñ¤W feeter */
+ }
+
+ dir = xo->dir;
+
+ /* switch (ans = vans("¦s©ñ¦ì¸m A)ppend I)nsert N)ext E)xtend Q)uit [A] ")) */
+ switch (ans = vans("¦s©ñ¦ì¸m A)¥[¨ì³Ì«á I/N)´¡¤J¥Ø«e¦ì¸m E)ªþ¥[ÀÉ®× Q)Â÷¶} [A] "))
+ {
+ case 'q':
+ return XO_FOOT;
+
+ case 'e':
+ if (gem_extend(xo, num))
+ zmsg("[Extend Àɮתþ¥[] °Ê§@¨Ã¥¼§¹¥þ¦¨¥\\");
+ return XO_FOOT;
+
+ default:
+ pos = (ans == 'n') ? xo->pos + 1 : (ans == 'i') ? xo->pos : -1;
+
+ head = GemBuffer;
+ tail = head + num;
+ do
+ {
+ if (invalid_loop(GemFolder, dir, head, 0)) /* itoc.010727: ³y¦¨°j°éªÌ¤£¤¹³\¶K¤W */
+ {
+ vmsg("³y¦¨°j°éªº¨÷©v±NµLªk¦¬¿ý");
+ continue;
+ }
+ else
+ {
+ gem_do_paste(GemFolder, dir, head, pos);
+ if (pos >= 0) /* Insert/Next:­nÄ~Äò©¹¤U¶K Append:¤@ª½¶K¦b³Ì«á */
+ pos++;
+ }
+ } while (++head < tail);
+ }
+
+ return gem_load(xo);
+}
+
+
+static int
+gem_move(xo)
+ XO *xo;
+{
+ HDR *hdr;
+ char *dir, buf[40];
+ int pos, newOrder;
+
+ if (!(xo->key & GEM_W_BIT) || !(hdr = gem_check(xo, NULL, 0)))
+ return XO_NONE;
+
+ pos = xo->pos;
+ sprintf(buf, "½Ð¿é¤J²Ä %d ¿ï¶µªº·s¦ì¸m¡G", pos + 1);
+ if (!vget(b_lines, 0, buf, buf, 5, DOECHO))
+ return XO_FOOT;
+
+ newOrder = atoi(buf) - 1;
+ if (newOrder < 0)
+ newOrder = 0;
+ else if (newOrder >= xo->max)
+ newOrder = xo->max - 1;
+
+ if (newOrder != pos)
+ {
+ dir = xo->dir;
+ if (!rec_del(dir, sizeof(HDR), pos, NULL))
+ {
+ rec_ins(dir, hdr, sizeof(HDR), newOrder, 1);
+ xo->pos = newOrder;
+ return gem_load(xo);
+ }
+ }
+ return XO_FOOT;
+}
+
+
+static int
+gem_anchor(xo)
+ XO *xo;
+{
+ int ans;
+ char *folder;
+
+ if (!(xo->key & GEM_W_BIT)) /* Thor.981020: ¥u­nªO¥D¥H¤W§Y¥i¨Ï¥Îanchor */
+ return XO_NONE; /* Thor.981020: ¤£¶}©ñ¤@¯ë user ¨Ï¥Î¬O¬°¤F¨¾¤îªO¥D¸Õ¥X¥t¤@­Ó¤p bug :P */
+
+ ans = vans("ºëµØ°Ï A)©wÁã D)©ÞÁã J)´N¦ì Q)¨ú®ø [A] ");
+ if (ans != 'q')
+ {
+ folder = GemAnchor;
+
+ if (ans == 'j')
+ {
+ if (!*folder) /* ¨S¦³©wÁã */
+ return XO_FOOT;
+
+ XoGem(folder, "¡´ ºëµØ©wÁã°Ï ¡´", xo->key);
+ return gem_init(xo);
+ }
+ else if (ans == 'd')
+ {
+ *folder = '\0';
+ }
+ else
+ {
+ strcpy(folder, xo->dir);
+ str_ncpy(GemSailor, xo->xyz, sizeof(GemSailor));
+ }
+
+ zmsg("Áã°Ê§@§¹¦¨");
+ }
+
+ /* return XO_NONE; */
+ return XO_FOOT; /* itoc.010726: §â b_lines ¶ñ¤W feeter */
+}
+
+
+int
+gem_gather(xo)
+ XO *xo;
+{
+ HDR *hdr, *gbuf, ghdr, xhdr;
+ int tag, locus, index, rc, xmode;
+ char *dir, *folder, fpath[80];
+ FILE *fp;
+
+ folder = GemAnchor;
+
+ if (!*folder)
+ {
+ zmsg("½Ð¥ý©wÁã¥H«á¦Aª½±µ¦¬¿ý¦Ü©wÁã°Ï");
+ return XO_FOOT;
+ }
+
+ sprintf(fpath, "¦¬¿ý¦Ü©wÁã°Ï (%s)", GemSailor);
+ tag = AskTag(fpath);
+
+ if (tag < 0)
+ return XO_FOOT;
+
+ fp = NULL;
+
+ if (tag > 0)
+ {
+ switch (vans("¦ê¦C¤å³¹ 1)¦X¦¨¤@½g 2)¤À§O«ØÀÉ Q)¨ú®ø [1] "))
+ {
+ case 'q':
+ return XO_FOOT;
+
+ case '2':
+ break;
+
+ default:
+ strcpy(xhdr.title, currtitle);
+ if (!vget(b_lines, 0, "¼ÐÃD¡G", xhdr.title, TTLEN + 1, GCARRY))
+ return XO_FOOT;
+ fp = fdopen(hdr_stamp(folder, 'A', &ghdr, fpath), "w");
+ strcpy(ghdr.owner, cuser.userid);
+ strcpy(ghdr.title, xhdr.title);
+ }
+ }
+
+ dir = xo->dir;
+ hdr = tag ? &xhdr : (HDR *) xo_pool + xo->pos - xo->top;
+ rc = (*dir == 'g') ? XO_NONE : XO_FOOT;
+
+ /* gather µø¦P copy¡A¥i·Ç³Æ§@ paste */
+
+ strcpy(GemFolder, folder);
+ gbuf = gbuf_malloc((fp != NULL || tag == 0) ? 1 : tag);
+
+ /* itoc.010727.µù¸Ñ: ­Y¦X¦¨¤@½g©Î¥u¦¬¿ý¤@½g¡A«h GemBufferNum = 1¡A¤Ï¤§¬O tag ¼Æ¶q */
+
+ index = 0;
+ locus = 0;
+
+ do
+ {
+ if (tag)
+ EnumTag(hdr, dir, locus, sizeof(HDR));
+
+ xmode = hdr->xmode;
+
+ if (xmode & (GEM_RESTRICT | GEM_RESERVED)) /* ­­¨î¯ÅºëµØ°Ï¤£¯à©wÁ㦬¿ý */
+ continue;
+
+ if (!(xmode & GEM_FOLDER)) /* ¬d hdr ¬O§_ plain text (§Y¤å³¹/¸ê®Æ/µ·¸ô) */
+ {
+ hdr_fpath(fpath, dir, hdr);
+
+ if (fp) /* ¦X¦¨¤@½g */
+ {
+ f_suck(fp, fpath);
+ fputs(str_line, fp);
+ }
+ else /* ¤À§O«ØÀÉ */
+ {
+ hdr_stamp(folder, HDR_COPY | 'A', &ghdr, fpath);
+ strcpy(ghdr.owner, cuser.userid); /* ¦¬¿ý¥ÎªO¥D id */
+ strcpy(ghdr.title, hdr->title);
+ ghdr.xmode = 0; /* xmode ¬O¤å³¹ */
+
+ gbuf[index] = ghdr; /* itoc.010727: ¸õ¹L¤£¬O plain text ªº */
+ index++;
+ rec_add(folder, &ghdr, sizeof(HDR));
+ gem_log(folder, "¦¬¿ý", &ghdr);
+ }
+ }
+ } while (++locus < tag);
+
+ if (fp)
+ {
+ fclose(fp);
+ gbuf[0] = ghdr;
+ rec_add(folder, &ghdr, sizeof(HDR));
+ gem_log(folder, "·s¼W", &ghdr);
+ }
+ else
+ {
+ /* itoc.010727: tag ¼Æ¶q¦©±¼¤£¬O plain text ªº¤~¬O¯u¬O°Å¶K諸½g¼Æ */
+ /* itoc.010727.µù¸Ñ: ¦pªG GemBufferNum ¼Æ¥Ø¤£¹ï¡A¨º»ò gem_paste() ®É´N·|µo¥Í¿ù»~ */
+ GemBufferNum = index;
+ }
+
+ zmsg("¦¬¿ý§¹¦¨¡A¦ý¬O¥[±K¤å³¹¤£·|³Q¦¬¿ý");
+
+ if (rc == XO_NONE) /* ¦bºëµØ°Ï¤¤ gem_gather() ¤~­n­«Ã¸°Å¶Kï½g¼Æ¡A¦b¬ÝªO/«H½c¸Ì³£¤£¥Î */
+ {
+ /* itoc.010727: ­«Ã¸°Å¶Kï½g¼Æ */
+ move(1, 59);
+ clrtoeol();
+ prints("(°Å¶Kï %d ½g)\n", GemBufferNum);
+ }
+
+ return XO_FOOT;
+}
+
+
+static int
+gem_tag(xo)
+ XO *xo;
+{
+ HDR *hdr;
+ int pos, tag;
+
+ if ((hdr = gem_check(xo, NULL, 0)) &&
+ (tag = Tagger(hdr->chrono, pos = xo->pos, TAG_TOGGLE)))
+ {
+ move(3 + pos - xo->top, 7);
+ outc(tag > 0 ? '*' : ' ');
+ }
+
+ /* return XO_NONE; */
+ return xo->pos + 1 + XO_MOVE; /* lkchu.981201: ¸õ¦Ü¤U¤@¶µ¡A¤è«K³sÄò tag */
+}
+
+
+static int
+gem_help(xo)
+ XO *xo;
+{
+ xo_help("gem");
+ return gem_head(xo);
+}
+
+
+static KeyFunc gem_cb[] =
+{
+ XO_INIT, gem_init,
+ XO_LOAD, gem_load,
+ XO_HEAD, gem_head,
+ XO_BODY, gem_body,
+
+ 'r', gem_browse,
+
+ Ctrl('P'), gem_add_all, /* itoc.010723: gem_cb ªº¤Þ¼Æ¥u¦³ xo */
+ 'a', gem_add_article,
+ 'f', gem_add_folder,
+
+ 'E', gem_edit,
+ 'T', gem_title,
+ 'd', gem_delete,
+ 'D', gem_rangedel, /* itoc.010726: ´£¨Ñ°Ï¬q§R°£ */
+
+ 'c', gem_copy,
+ 'g', gem_gather,
+
+ Ctrl('G'), gem_anchor,
+ Ctrl('V'), gem_paste,
+ 'p', gem_paste, /* itoc.010223: ¨Ï¥ÎªÌ²ßºD c/p ¦¬¿ýºëµØ°Ï */
+
+ 't', gem_tag,
+ 'x', post_cross, /* ¦b post/mbox ¤¤³£¬O¤p¼g x Âà¬ÝªO¡A¤j¼g X Âà¨Ï¥ÎªÌ */
+ 'X', post_forward,
+ 'B', gem_toggle,
+ 'o', gem_refuse,
+ 'm', gem_move,
+ 'M', gem_move,
+
+ 'S', gem_state,
+
+ Ctrl('D'), gem_prune,
+#if 0
+ Ctrl('Q'), xo_uquery, /* ºëµØ°Ïªº hdr.owner ¤@¯ë¬O«ü¦¬¿ýªºªO¥D¡A¦Ó¤£¬O§@ªÌ */
+ Ctrl('O'), xo_usetup, /* ºëµØ°Ïªº hdr.owner ¤@¯ë¬O«ü¦¬¿ýªºªO¥D¡A¦Ó¤£¬O§@ªÌ */
+#endif
+
+ 'h', gem_help
+};
+
+
+void
+XoGem(folder, title, level)
+ char *folder;
+ char *title;
+ int level;
+{
+ XO *xo, *last;
+
+ last = xz[XZ_GEM - XO_ZONE].xo; /* record */
+
+ xz[XZ_GEM - XO_ZONE].xo = xo = xo_new(folder);
+ xo->pos = 0;
+ xo->key = level;
+ xo->xyz = title;
+
+ xover(XZ_GEM);
+
+ free(xo);
+
+ xz[XZ_GEM - XO_ZONE].xo = last; /* restore */
+}
+
+
+void
+gem_main()
+{
+ XO *xo;
+
+ xz[XZ_GEM - XO_ZONE].xo = xo = xo_new("gem/"FN_DIR);
+ xz[XZ_GEM - XO_ZONE].cb = gem_cb;
+ xo->pos = 0;
+ /* ¬ÝªOÁ`ºÞ¦b (A)nnounce ¸Ì­±¦³ GEM_X_BIT ¨Ó·s¼W¬ÝªO±¶®| */
+ xo->key = (HAS_PERM(PERM_ALLBOARD) ? (GEM_W_BIT | GEM_X_BIT | GEM_M_BIT) : 0);
+ xo->xyz = "";
+}
diff --git a/maple/mail.c b/maple/mail.c
new file mode 100644
index 0000000..93f13f0
--- /dev/null
+++ b/maple/mail.c
@@ -0,0 +1,1943 @@
+/*-------------------------------------------------------*/
+/* mail.c ( NTHU CS MapleBBS Ver 2.36 ) */
+/*-------------------------------------------------------*/
+/* target : local/internet mail routines */
+/* create : 95/03/29 */
+/* update : 95/12/15 */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+
+extern XZ xz[];
+extern char xo_pool[];
+
+
+extern UCACHE *ushm;
+
+
+/* ----------------------------------------------------- */
+/* Link List routines */
+/* ----------------------------------------------------- */
+
+
+LinkList *ll_head; /* head of link list */
+static LinkList *ll_tail; /* tail of link list */
+
+
+void
+ll_new()
+{
+ LinkList *list, *next;
+
+ list = ll_head;
+
+ while (list)
+ {
+ next = list->next;
+ free(list);
+ list = next;
+ }
+
+ ll_head = ll_tail = NULL;
+}
+
+
+void
+ll_add(name)
+ char *name;
+{
+ LinkList *node;
+ int len;
+
+ len = strlen(name) + 1;
+ node = (LinkList *) malloc(sizeof(LinkList) + len);
+ node->next = NULL;
+ memcpy(node->data, name, len);
+
+ if (ll_head)
+ ll_tail->next = node;
+ else
+ ll_head = node;
+ ll_tail = node;
+}
+
+
+int
+ll_del(name)
+ char *name;
+{
+ LinkList *list, *prev, *next;
+
+ prev = NULL;
+ for (list = ll_head; list; list = next)
+ {
+ next = list->next;
+ if (!strcmp(list->data, name))
+ {
+ if (prev == NULL)
+ ll_head = next;
+ else
+ prev->next = next;
+
+ if (list == ll_tail)
+ ll_tail = prev;
+
+ free(list);
+ return 1;
+ }
+ prev = list;
+ }
+ return 0;
+}
+
+
+int
+ll_has(name)
+ char *name;
+{
+ LinkList *list;
+
+ for (list = ll_head; list; list = list->next)
+ {
+ if (!strcmp(list->data, name))
+ return 1;
+ }
+ return 0;
+}
+
+
+void
+ll_out(row, column, msg)
+ int row, column;
+ char *msg;
+{
+ int len;
+ LinkList *list;
+
+ move(row, column);
+ clrtobot();
+ outs(msg);
+
+ column = 80;
+ for (list = ll_head; list; list = list->next)
+ {
+ msg = list->data;
+ len = strlen(msg) + 1;
+ if (column + len > 78)
+ {
+ if (++row > b_lines - 2)
+ {
+ move(b_lines, 60);
+ outs("±ÚÁc¤£¤Î³Æ¸ü...");
+ break;
+ }
+ else
+ {
+ column = len;
+ outc('\n');
+ }
+ }
+ else
+ {
+ column += len;
+ outc(' ');
+ }
+ outs(msg);
+ }
+}
+
+
+/* ----------------------------------------------------- */
+/* (direct) SMTP */
+/* ----------------------------------------------------- */
+
+
+int /* >=0:¦¨¥\ <0:¥¢±Ñ */
+bsmtp(fpath, title, rcpt, method)
+ char *fpath, *title, *rcpt;
+ int method;
+{
+ int sock;
+ time_t stamp;
+ FILE *fp, *fr, *fw;
+ char *str, buf[512], from[80], msgid[80];
+#ifdef EMAIL_JUSTIFY
+ char subject[80];
+#endif
+
+ cuser.numemails++; /* °O¿ý¨Ï¥ÎªÌ¦@±H¥X´X«Ê Internet E-mail */
+ time(&stamp);
+
+#ifdef EMAIL_JUSTIFY
+
+ /* --------------------------------------------------- */
+ /* ¨­¤À»{ÃÒ«H¨ç */
+ /* --------------------------------------------------- */
+
+ if (method & MQ_JUSTIFY)
+ {
+ fpath = FN_ETC_VALID;
+ title = subject;
+ sprintf(from, "bbsreg@%s", str_host);
+ /* itoc.010820: ¥Î cuser.tvalid ¨Ó°µ time-seed¡A¥i¥H¬Ù±¼ cuser.vtime Äæ¦ì¡A
+ ÁÙ¥i¥H¨Ï¦b¦P¤@¦¸»{ÃҮɡA©Òµo¥X¥hªº»{ÃÒ«H¼ÐÃD³£¬Û¦P¡AÁ×§K¦³¤H¦Ñ¬O¤£ª¾¹D­n¦^³Ì«á¤@«Ê */
+ archiv32(str_hash(rcpt, cuser.tvalid), buf);
+ sprintf(title, TAG_VALID " %s(%s) [VALID]", cuser.userid, buf);
+ }
+ else
+#endif
+ {
+ sprintf(from, "%s.bbs@%s", cuser.userid, str_host);
+ }
+
+ str = strchr(rcpt, '@') + 1;
+ sock = dns_smtp(str);
+ if (sock >= 0)
+ {
+ move(b_lines, 0);
+ clrtoeol();
+ prints("¡¹ ±H«Hµ¹ %s \033[5m...\033[m", rcpt);
+ refresh();
+
+ sleep(1); /* wait for mail server response */
+
+ fr = fdopen(sock, "r");
+ fw = fdopen(sock, "w");
+
+ fgets(buf, sizeof(buf), fr);
+ if (memcmp(buf, "220", 3))
+ goto smtp_error;
+ while (buf[3] == '-')
+ fgets(buf, sizeof(buf), fr);
+
+ fprintf(fw, "HELO %s\r\n", str_host);
+ fflush(fw);
+ do
+ {
+ fgets(buf, sizeof(buf), fr);
+ if (memcmp(buf, "250", 3))
+ goto smtp_error;
+ } while (buf[3] == '-');
+
+ fprintf(fw, "MAIL FROM:<%s>\r\n", from);
+ fflush(fw);
+ do
+ {
+ fgets(buf, sizeof(buf), fr);
+ if (memcmp(buf, "250", 3))
+ goto smtp_error;
+ } while (buf[3] == '-');
+
+ fprintf(fw, "RCPT TO:<%s>\r\n", rcpt);
+ fflush(fw);
+ do
+ {
+ fgets(buf, sizeof(buf), fr);
+ if (memcmp(buf, "250", 3))
+ goto smtp_error;
+ } while (buf[3] == '-');
+
+ fprintf(fw, "DATA\r\n", rcpt);
+ fflush(fw);
+ do
+ {
+ fgets(buf, sizeof(buf), fr);
+ if (memcmp(buf, "354", 3))
+ goto smtp_error;
+ } while (buf[3] == '-');
+
+ /* ------------------------------------------------- */
+ /* begin of mail header */
+ /* ------------------------------------------------- */
+
+ archiv32(stamp, msgid);
+
+ /* Thor.990125: ¾¨¥i¯àªº¹³ RFC 822 ¤Î sendmail ªº§@ªk, §K±o§O¤H¤£±µ:p */
+ fprintf(fw, "From: \"%s\" <%s>\r\nTo: %s\r\n",
+#ifdef EMAIL_JUSTIFY
+ method & MQ_JUSTIFY ? "BBS Register" :
+#endif
+ cuser.username, from, rcpt);
+ /* itoc.030411: mail ¿é¥X RFC 2047 */
+ output_rfc2047_qp(fw, "Subject: ", title, MYCHARSET, "\r\n");
+ /* itoc.030323: mail ¿é¥X RFC 2045 */
+ fprintf(fw, "X-Sender: %s (%s)\r\n"
+ "Date: %s\r\nMessage-Id: <%s@%s>\r\n"
+ "Mime-Version: 1.0\r\n"
+ "Content-Type: %s; charset=%s\r\n"
+ "Content-Transfer-Encoding: 8bit\r\n"
+ "X-Disclaimer: [%s] ¹ï¥»«H¤º®e®¤¤£­t³d\r\n\r\n",
+ cuser.userid, cuser.username,
+ Atime(&stamp), msgid, str_host,
+ method & MQ_ATTACH ? "application/x-gzip" : "text/plain", MYCHARSET,
+ str_site);
+
+#ifdef EMAIL_JUSTIFY
+ if (method & MQ_JUSTIFY) /* ¨­¤À»{ÃÒ«H¨ç */
+ {
+ fprintf(fw, " ID: %s (%s) E-mail: %s\r\n\r\n",
+ cuser.userid, cuser.username, rcpt);
+ }
+#endif
+
+ /* ------------------------------------------------- */
+ /* begin of mail body */
+ /* ------------------------------------------------- */
+
+ if (fp = fopen(fpath, "r"))
+ {
+ char *ptr;
+
+ str = buf;
+ *str++ = '.';
+ while (fgets(str, sizeof(buf) - 3, fp))
+ {
+ if (ptr = strchr(str, '\n'))
+ {
+ *ptr++ = '\r';
+ *ptr++ = '\n';
+ *ptr = '\0';
+ }
+ fputs((*str == '.' ? buf : str), fw);
+ }
+ fclose(fp);
+ }
+#ifdef HAVE_SIGNED_MAIL
+ if (!(method & MQ_JUSTIFY) && !rec_get(FN_RUN_PRIVATE, buf, 8, 0))
+ /* Thor.990413: °£¤F»{ÃÒ¨ç¥~, ¨ä¥L«H¥ó³£­n¥[sign */
+ {
+ time_t prichro;
+
+ buf[8] = '\0'; /* Thor.990413: buf ¥Î¤£¨ì¤F¡A­É¨Ó¥Î¥Î */
+ prichro = chrono32(buf);
+ archiv32(str_hash(msgid, prichro), buf);
+ fprintf(fw,"¡° X-Sign: %s$%s %s\r\n",
+ msgid, genpasswd(buf), Btime(&stamp));
+ }
+#endif
+ fputs("\r\n.\r\n", fw);
+ fflush(fw);
+
+ fgets(buf, sizeof(buf), fr);
+ if (memcmp(buf, "250", 3))
+ goto smtp_error;
+
+ fputs("QUIT\r\n", fw);
+ fflush(fw);
+ fclose(fw);
+ fclose(fr);
+ goto smtp_log;
+
+smtp_error:
+
+ /* itoc.041128.µù¸Ñ: ¤Z¬O¨«¨ì³o¸Ìªº¡Abuf ¬O¹ï¤è¦^ÂЪº¿ù»~°T®§ */
+ fclose(fr);
+ fclose(fw);
+ sprintf(from, "\t%.70s\n", buf);
+ sock = -1;
+ }
+ else
+ {
+ strcpy(from, "\tSMTP ³s½u¥¢±Ñ\n");
+ }
+
+smtp_log:
+
+ /* --------------------------------------------------- */
+ /* °O¿ý±H«H */
+ /* --------------------------------------------------- */
+
+ sprintf(buf, "%s %-13s%c> %s\n%s\t%s\n\t%s\n",
+ Btime(&stamp), cuser.userid, (method & MQ_JUSTIFY) ? '=' : '-', rcpt,
+ sock >= 0 ? "" : from, title, fpath);
+ f_cat(FN_RUN_MAIL_LOG, buf);
+
+ return sock;
+}
+
+
+#ifdef HAVE_MAIL_ZIP
+
+/* ----------------------------------------------------- */
+/* Zip mbox & board gem */
+/* ----------------------------------------------------- */
+
+
+static void
+do_forward(title, mode)
+ char *title;
+ int mode;
+{
+ int rc;
+ char *userid;
+ char addr[64], fpath[64], cmd[256];
+
+ strcpy(addr, cuser.email);
+
+ if (!vget(b_lines, 0, "½Ð¿é¤JÂà±H¦a§}¡G", addr, 60, GCARRY))
+ return;
+
+ if (not_addr(addr))
+ {
+ zmsg(err_email);
+ return;
+ }
+
+ sprintf(fpath, "½T©w±Hµ¹ [%s] ¶Ü(Y/N)¡H[N] ", addr);
+ if (vans(fpath) != 'y')
+ return;
+
+ userid = strchr(addr, '@') + 1;
+ if (dns_smtp(userid) >= 0) /* itoc.µù¸Ñ: ÁöµM bsmtp() ¤]·|°µ¡A¦ý¬O³oÃä¥ý°µ¡A¥H§KÀ£ÁY§¹¤~ª¾¹D¬OµL®Äªº¤u§@¯¸¦a§} */
+ {
+ userid = cuser.userid;
+
+ if (mode == '1') /* ­Ó¤H«H¥ó */
+ {
+ /* usr_fpath(fpath, userid, "@"); */
+ /* ¦]¬° .DIR ¤£¦b @/ ¸Ì¡A­n¤@°_¥´¥] */
+ usr_fpath(cmd, userid, fn_dir);
+ usr_fpath(fpath, userid, "@");
+ strcat(fpath, " ");
+ strcat(fpath, cmd);
+ }
+ else if (mode == '2') /* ­Ó¤HºëµØ°Ï */
+ {
+ usr_fpath(fpath, userid, "gem");
+ }
+ else if (mode == '3') /* ¬ÝªO¤å³¹ */
+ {
+ brd_fpath(fpath, currboard, NULL);
+ }
+ else /* if (mode == '4') */ /* ¬ÝªOºëµØ°Ï */
+ {
+ gem_fpath(fpath, currboard, NULL);
+ }
+
+ sprintf(cmd, "tar cfz - %s | uuencode %s.tgz > tmp/%s.tgz", fpath, userid, userid);
+ system(cmd);
+
+ sprintf(fpath, "tmp/%s.tgz", userid);
+ rc = bsmtp(fpath, title, addr, MQ_ATTACH);
+ unlink(fpath);
+
+ if (rc >= 0)
+ {
+ vmsg(msg_sent_ok);
+ return;
+ }
+ }
+
+ vmsg("«H¥óµLªk±H¹F");
+}
+
+
+int
+m_zip() /* itoc.010228: ¥´¥]¸ê®Æ */
+{
+ int ans;
+ char *name, *item, buf[80];
+
+ ans = vans("¥´¥]¸ê®Æ 1)­Ó¤H«H¥ó 2)­Ó¤HºëµØ°Ï 3)¬ÝªO¤å³¹ 4)¬ÝªOºëµØ°Ï [Q] ");
+
+ if (ans == '1' || ans == '2')
+ {
+ name = cuser.userid;
+ item = (ans == '1') ? "­Ó¤H«H¥ó" : "­Ó¤HºëµØ°Ï";
+ }
+ else if (ans == '3' || ans == '4')
+ {
+ /* itoc.µù¸Ñ: ­­©w¥u¯à¥´¥]¥Ø«e¾\Ūªº¬ÝªO¡A¤£¯à¾\Ū¯µ±K¬ÝªOªº¤H´N¤£¯à¥´¥]¸ÓªO/ºëµØ°Ï */
+ /* itoc.020612: ¬°¤F¨¾¤î POST_RESTRICT/GEM_RESTRICT ªº¤å³¹¥~¬y¡A«DªO¥D´N¤£¯à¥´¥] */
+
+ if (currbno < 0)
+ {
+ vmsg("½Ð¥ý¶i¤J±z­n¥´¥]ªº¬ÝªO¡A¦A¨Ó¦¹¥´¥]");
+ return XEASY;
+ }
+
+ if ((ans == '3' && !(bbstate & STAT_BM)) || (ans == '4' && !(bbstate & STAT_BOARD)))
+ {
+ vmsg("¥u¦³ªO¥D¤~¯à¥´¥]¬ÝªO¤å³¹¤Î¬ÝªOºëµØ°Ï");
+ return XEASY;
+ }
+
+ name = currboard;
+ item = (ans == '3') ? "¬ÝªO¤å³¹" : "¬ÝªOºëµØ°Ï";
+ }
+ else
+ {
+ return XEASY;
+ }
+
+ sprintf(buf, "½T©w­n¥´¥] %s %s¶Ü(Y/N)¡H[N] ", name, item);
+ if (vans(buf) == 'y')
+ {
+ sprintf(buf, "¡i" BBSNAME "¡j%s %s", name, item);
+ do_forward(buf, ans);
+ }
+
+ return XEASY;
+}
+#endif /* HAVE_MAIL_ZIP */
+
+
+#ifdef HAVE_SIGNED_MAIL /* Thor.990413: ´£¨ÑÅçÃÒ¥\¯à */
+int
+m_verify()
+{
+ time_t prichro; /* ¨t²Îªº private chrono */
+ time_t chrono;
+ char key[9], sign[68], *p;
+
+ vmsg("½Ð¿é¤J«H¥½ X-Sign ¥H¶i¦æÅçÃÒ");
+
+ if (!vget(b_lines, 0, "¡° X-Sign: ", sign, sizeof(sign), DOECHO))
+ return XEASY;
+
+ str_trim(sign); /* Thor: ¥h§À¤Ú¡Afor ptelnet ¦Û°Ê¥[ªÅ¥Õ */
+ p = sign;
+ while (*p == ' ') /* Thor: ¥h«eÀYªºªÅ¥Õ */
+ p++;
+
+ if (strlen(p) < 7 + 1 + PASSLEN + 1)
+ {
+ vmsg("¹q¤lñ³¹¦³»~");
+ return XEASY;
+ }
+
+ /* ¨ú±o¨t²Îªº private chrono */
+ if (rec_get(FN_RUN_PRIVATE, key, 8, 0))
+ {
+ zmsg("¥»¨t²Î¨ÃµL¹q¤lñ³¹¡A½Ð¬¢¯¸ªø");
+ return XEASY;
+ }
+ key[8] = '\0';
+ prichro = chrono32(key);
+
+ /* ¨ú±o¸Ó«Hªº chrono */
+ p[7] = '\0';
+ strcpy(key + 1, p); /* +1 for chrono32() ©Ò­nªº prefix */
+ chrono = chrono32(key);
+
+ /* ¨ú±o¸Ó«Hªº±KÆ_ */
+ archiv32(str_hash(p, prichro), key);
+ p[7 + PASSLEN + 1] = '\0';
+
+ if (chkpasswd(p + 8, key) || strcmp(p + 8 + PASSLEN + 1, Btime(&chrono)))
+ vmsg("¦¹«H¨Ã«D¥Ñ¥»¯¸©Òµo¡A½Ð¬d·Ó¡I");
+ else
+ vmsg("¦¹«H¥Ñ¥»¯¸©Òµo¥X");
+
+ return XEASY;
+}
+#endif
+
+
+/* ----------------------------------------------------- */
+/* mail routines */
+/* ----------------------------------------------------- */
+
+
+static struct
+{
+ XO mail_xo;
+ char dir[32];
+} cmbox;
+
+
+#ifdef OVERDUE_MAILDEL
+usint
+m_quota()
+{
+ usint status;
+ int fd, count, fsize, limit, xmode;
+ time_t mail_due, mark_due;
+ struct stat st;
+ HDR *head, *tail;
+ char *base, *folder, date[9];
+
+ if ((fd = open(folder = cmbox.dir, O_RDWR)) < 0)
+ return 0;
+
+ status = 0;
+ fsize = limit = 0;
+
+ if (!fstat(fd, &st) && (fsize = st.st_size) >= sizeof(HDR) &&
+ (base = (char *) malloc(fsize)))
+ {
+
+ /* flock(fd, LOCK_EX); */
+ /* Thor.981205: ¥Î fcntl ¨ú¥Nflock, POSIX¼Ð·Ç¥Îªk */
+ f_exlock(fd);
+
+ if ((fsize = read(fd, base, fsize)) >= sizeof(HDR))
+ {
+ int prune; /* number of pruned mail */
+
+ limit = time(0);
+ mail_due = limit - MAIL_DUE * 86400;
+ mark_due = limit - MARK_DUE * 86400;
+ st.st_mtime = limit + CHECK_PERIOD;
+ str_stamp(date, &st.st_mtime);
+
+ limit = cuser.userlevel;
+ limit = (limit & (PERM_ALLADMIN | PERM_MBOX)) ? MAX_BBSMAIL : (limit & PERM_VALID) ? MAX_VALIDMAIL : MAX_NOVALIDMAIL;
+
+ count = fsize / sizeof(HDR);
+
+ head = (HDR *) base;
+ tail = (HDR *) (base + fsize);
+
+ prune = 0;
+
+ do
+ {
+ /* itoc.011013.µù¸Ñ: ³o¦¸¥[¤W¤F MAIL_DELETE¡A­n¹L CHECK_PERIOD ¤U¤@¦¸¤~·|¯u¥¿§R°£ */
+ /* itoc.011013.µù¸Ñ: °£¤F m_quota() ¥H¥~¡A¦b bpop3.c ¸Ì­±¡A­Y¨ú«H®É­n¨D±q¦øªA¾¹¤W§R°£¡A¤]·|¥[¤W MAIL_DELETE ªººX¼Ð */
+
+ xmode = head->xmode;
+ if (xmode & MAIL_DELETE)
+ {
+ char fpath[64];
+
+ hdr_fpath(fpath, folder, head);
+ unlink(fpath);
+ prune--;
+ continue;
+ }
+
+ if (!(xmode & MAIL_READ))
+ status |= STATUS_BIFF;
+
+ if ((count > limit) ||
+ (head->chrono <= (xmode & MAIL_MARKED ? mark_due : mail_due)))
+ {
+ count--;
+ head->xmode = xmode | MAIL_DELETE;
+ strcpy(head->date, date);
+ status |= STATUS_MQUOTA;
+ }
+
+ if (prune)
+ head[prune] = head[0];
+
+ } while (++head < tail);
+
+ fsize += (prune * sizeof(HDR));
+ if ((fsize > 0) && (prune || (status & STATUS_MQUOTA)))
+ {
+ lseek(fd, 0, SEEK_SET);
+ write(fd, base, fsize);
+ ftruncate(fd, fsize);
+ }
+ }
+
+ /* flock(fd, LOCK_UN); */
+ /* Thor.981205: ¥Î fcntl ¨ú¥Nflock, POSIX¼Ð·Ç¥Îªk */
+ f_unlock(fd);
+
+ free(base);
+ }
+
+ close(fd);
+
+ if (fsize > limit)
+ status ^= STATUS_MAILOVER;
+ else if (fsize < sizeof(HDR))
+ unlink(folder);
+
+ return status;
+}
+#endif
+
+
+usint
+m_query(userid)
+ char *userid;
+{
+ usint status;
+ int fsize, limit;
+ HDR *head, *tail;
+ char folder[64];
+
+ status = 0;
+ usr_fpath(folder, userid, fn_dir);
+ if (head = (HDR *) f_img(folder, &fsize))
+ {
+ fsize /= sizeof(HDR);
+ tail = head + fsize;
+
+ while (--tail >= head)
+ {
+ if (!(tail->xmode & MAIL_READ))
+ {
+ status = STATUS_BIFF;
+ break;
+ }
+ }
+ free(head);
+
+ limit = HAS_PERM(PERM_ALLADMIN | PERM_MBOX) ? MAX_BBSMAIL : HAS_PERM(PERM_VALID) ? MAX_VALIDMAIL : MAX_NOVALIDMAIL;
+ if (fsize > limit)
+ status ^= STATUS_MAILOVER;
+ }
+
+ return status;
+}
+
+
+void
+m_biff(userno)
+ int userno;
+{
+ UTMP *utmp, *uceil;
+
+ utmp = ushm->uslot;
+ uceil = (void *) utmp + ushm->offset;
+ do
+ {
+ if (utmp->userno == userno)
+ {
+ utmp->status |= STATUS_BIFF;
+
+#ifdef BELL_ONCE_ONLY
+ return;
+#endif
+ }
+ } while (++utmp <= uceil);
+}
+
+
+void
+mail_hold(fpath, rcpt, title, hold)
+ char *fpath;
+ char *rcpt;
+ char *title;
+ int hold; /* -1:·í±H«H¥¢±Ñ®É¥i¥H±j­¢«O¯d */
+{
+ if (hold < 0 || vans("¬O§_¦Û¦s©³½Z(Y/N)¡H[N] ") == 'y')
+ {
+ char buf[256];
+
+ sprintf(buf, "<%s> %s", rcpt, title);
+ buf[TTLEN] = '\0';
+
+ mail_self(fpath, "[³Æ §Ñ ¿ý]", buf, MAIL_READ | MAIL_NOREPLY);
+ }
+}
+
+
+/* ----------------------------------------------------- */
+/* in boards/mail ¦^«Hµ¹­ì§@ªÌ¡AÂà«H¯¸¥ç¥i */
+/* ----------------------------------------------------- */
+
+
+static inline int
+is_host_alias(addr)
+ char *addr;
+{
+ int i;
+ char *str;
+ static char *alias[] = HOST_ALIASES;
+
+ /* check the aliases */
+
+ for (i = 0; str = alias[i]; i++)
+ {
+ if (*str && !str_cmp(addr, str))
+ return 1;
+ }
+ return 0;
+}
+
+
+/* static inline */
+int /* 1:internet mail 0:¯¸¤º±H«H(¶Ç¦^addr¬°userid) */
+mail_external(addr)
+ char *addr;
+{
+ char *str;
+
+ str = strchr(addr, '@'); /* itoc.020125.µù¸Ñ: email ¦ì§}¥u¦³­Ó ID¡Aªí¥Ü¬O¯¸¤º±H«H */
+ if (!str)
+ return 0;
+
+ if (!is_host_alias(str + 1)) /* itoc.020125: ¦pªG¤£¦b HOST_ALIAS ¤¤¡A¬°¯¸¥~±H«H */
+ return 1;
+
+ /* ¯¸¤º±H«H: ÄdºI xyz.bbs@mydomain¡A¦ý¤£¥]¬A xyz.brd@mydomain */
+
+ *str = '\0';
+ if (str > addr + 4 && !strcmp(str - 4, ".bbs")) /* ".bbs" => 4 */
+ {
+ str[-4] = '\0';
+ return 0;
+ }
+
+ return 1; /* ­Y¤£¬O *.bbs@mydomain¡A«h¥á¦^¥hÅý not_addr() µo¥Í¿ù»~ */
+}
+
+
+int /* >=0:¦¨¥\ -1:¥¢±Ñ©Î¨ú®ø */
+mail_send(rcpt)
+ char *rcpt;
+{
+ /* Thor.981105: ¶i¤J«e»Ý³]¦n quote_file */
+ /* itoc.041116: ¶i¤J«e»Ý³]¦n ve_title (¤§©Ò¥H¥Î ve_title ¬O§Æ±æ¦b vedit §ï¼ÐÃD®É¡A¤]¯à¤@°_§ï«H¥óÀÉÀYªº¼ÐÃD) */
+ HDR hdr;
+ char fpath[64], folder[64], *msg;
+ int userno; /* 0:internet_mail >0:userno */
+ FILE *fp;
+
+ if (!mail_external(rcpt)) /* ¤¤³~ÄdºI */
+ {
+ if ((userno = acct_userno(rcpt)) <= 0)
+ {
+ zmsg(err_uid);
+ return -1;
+ }
+ }
+ else
+ {
+ if (not_addr(rcpt))
+ {
+ zmsg(err_email);
+ return -1;
+ }
+ userno = 0;
+ }
+
+ utmp_mode(M_SMAIL);
+ fpath[0] = '\0';
+ curredit = EDIT_MAIL; /* Thor.981105: ª½±µ«ü©w¼g«H */
+
+ if (vedit(fpath, userno ? 1 : 2) < 0)
+ {
+ unlink(fpath);
+ vmsg(msg_cancel);
+ return -1;
+ }
+
+ if (userno)
+ {
+ usr_fpath(folder, rcpt, fn_dir);
+ hdr_stamp(folder, HDR_LINK, &hdr, fpath);
+ strcpy(hdr.owner, cuser.userid);
+ strcpy(hdr.nick, cuser.username); /* chuan: ¥[¤J nick */
+ strcpy(hdr.title, ve_title);
+ rec_add(folder, &hdr, sizeof(HDR));
+
+ if (fp = fopen(FN_RUN_MAIL_LOG, "a"))
+ {
+ fprintf(fp, "%s %-13s-> %s\n\t%s\n",
+ Btime(&hdr.chrono), cuser.userid, rcpt, ve_title);
+ fclose(fp);
+ }
+
+ msg = msg_sent_ok;
+ m_biff(userno);
+ mail_hold(fpath, rcpt, ve_title, 0);
+ }
+ else
+ {
+ clear();
+ prints("«H¥ó§Y±N±Hµ¹ %s\n¼ÐÃD¬°¡G%s\n½T©w­n±H¥X¶Ü(Y/N)¡H[Y] ", rcpt, ve_title);
+ switch (vkey())
+ {
+ case 'n':
+ case 'N':
+ msg = msg_cancel;
+ userno = -1;
+ break;
+
+ default:
+ outs("Y\n½Ðµy­Ô¡A«H¥ó¶Ç»¼¤¤...\n");
+ refresh();
+ userno = bsmtp(fpath, ve_title, rcpt, 0);
+ msg = (userno >= 0) ? msg_sent_ok : "«H¥óµLªk±H¹F¡A©³½Z³Æ¥÷¦b«H½c";
+ mail_hold(fpath, rcpt, ve_title, userno);
+ }
+ }
+
+ unlink(fpath);
+ vmsg(msg);
+ return userno;
+}
+
+
+static void
+mail_reply(hdr)
+ HDR *hdr;
+{
+ int xmode, prefix;
+
+ vs_bar("¦^ «H");
+
+ /* make the title */
+
+ sprintf(ve_title, "Re: %.64s", str_ttl(hdr->title));
+ if (!vget(2, 0, "¼ÐÃD¡G", ve_title, TTLEN + 1, GCARRY))
+ return;
+
+ prints("\n¦¬«H¤H: %s (%s)\n¼Ð ÃD: %s\n", quote_user, quote_nick, ve_title);
+
+ /* Thor: ¬°¤F¬Ù¤@¦¸ rec_put ¦^«H«h°²³]¬Ý¹L¤º®e */
+
+ xmode = hdr->xmode | MAIL_READ;
+ prefix = quote_file[0];
+
+ /* edit, then send the mail */
+
+ if (mail_send(quote_user) >= 0)
+ xmode |= MAIL_REPLIED;
+
+ if (prefix == 'u') /* user mail ¬Ý«H®É¤~¼Ð r */
+ hdr->xmode = xmode;
+}
+
+
+int
+my_send(userid) /* ¯¸¤º±H«Hµ¹ userid */
+ char *userid;
+{
+ if (HAS_PERM(PERM_DENYMAIL) || !HAS_PERM(PERM_LOCAL))
+ return XO_NONE;
+
+ vs_bar("±H «H");
+ prints("¦¬«H¤H¡G%s", userid);
+ if (vget(2, 0, "¼ÐÃD¡G", ve_title, TTLEN + 1, DOECHO))
+ {
+ *quote_file = '\0';
+ mail_send(userid);
+ }
+ return XO_HEAD;
+}
+
+
+int
+m_send()
+{
+ ACCT acct;
+
+ /* itoc.050604: ÁöµM¦b my_send() ¸Ì­±·|Àˬd¡A¦ý¬OÀ³¸Ó¦b¦¹´N¥ý¾×±¼¡A³s ID ³£µLªk¿é¤J */
+ if (HAS_PERM(PERM_DENYMAIL))
+ return XEASY;
+
+ if (acct_get(msg_uid, &acct) > 0)
+ my_send(acct.userid);
+ return 0;
+}
+
+
+int
+m_internet()
+{
+ char rcpt[60];
+
+ if (HAS_PERM(PERM_DENYMAIL))
+ return XEASY;
+
+ move(MENU_XPOS, 0);
+ clrtobot();
+
+ if (vget(15, 0, "¦¬«H¤H¡G", rcpt, sizeof(rcpt), DOECHO) &&
+ vget(17, 0, "¼ÐÃD¡G", ve_title, TTLEN + 1, DOECHO))
+ {
+ *quote_file = '\0';
+ mail_send(rcpt);
+ }
+
+ return 0;
+}
+
+
+int
+m_sysop()
+{
+ int fd;
+
+ if ((fd = open(FN_ETC_SYSOP, O_RDONLY)) >= 0)
+ {
+ int i, j;
+ char *ptr, *str;
+
+ struct SYSOPLIST
+ {
+ char userid[IDLEN + 1];
+ char duty[40];
+ } sysoplist[7]; /* °²³] 7 ­Ó¨¬¨o */
+
+ j = 0;
+ mgets(-1);
+ while (str = mgets(fd))
+ {
+ if (ptr = strchr(str, ':'))
+ {
+ *ptr = '\0';
+ do
+ {
+ i = *++ptr;
+ } while (i == ' ' || i == '\t');
+
+ if (i)
+ {
+ strcpy(sysoplist[j].userid, str);
+ strcpy(sysoplist[j++].duty, ptr);
+ }
+ }
+ }
+ close(fd);
+
+ move(12, 0);
+ clrtobot();
+ prints("%16s %-18sÅv³d¹º¤À\n%s\n", "½s¸¹", "¯¸ªø ID", msg_seperator);
+
+ for (i = 0; i < j; i++)
+ {
+ prints("%15d. \033[1;%dm%-16s%s\033[m\n",
+ i + 1, 31 + i, sysoplist[i].userid, sysoplist[i].duty);
+ }
+ prints("%-14s0. \033[1;%dmÂ÷¶}\033[m", "", 31 + j);
+
+ i = vans("½Ð¿é¤J¥N½X¡G[0] ") - '1';
+ if (i >= 0 && i < j)
+ {
+ vs_bar("±H «H");
+ prints("¦¬«H¤H¡G%s", sysoplist[i].userid);
+
+ if (vget(2, 0, "¼ÐÃD¡G", ve_title, TTLEN + 1, DOECHO))
+ {
+ *quote_file = '\0';
+ mail_send(sysoplist[i].userid);
+ }
+ }
+ }
+ return 0;
+}
+
+
+void
+mail_self(fpath, owner, title, xmode) /* itoc.011115: ±HÀÉ®×µ¹¦Û¤v */
+ char *fpath; /* Àɮ׸ô®| */
+ char *owner; /* ±H¥ó¤H */
+ char *title; /* ¶l¥ó¼ÐÃD */
+ usint xmode;
+{
+ HDR hdr;
+ char *folder;
+
+ folder = cmbox.dir;
+ hdr_stamp(folder, HDR_COPY, &hdr, fpath);
+ strcpy(hdr.owner, owner);
+ strcpy(hdr.title, title);
+ hdr.xmode = xmode;
+ rec_add(folder, &hdr, sizeof(HDR));
+}
+
+
+int
+mail_him(fpath, rcpt, title, xmode) /* itoc.041111: ±HÀÉ®×µ¹¥L¤H */
+ char *fpath; /* Àɮ׸ô®| */
+ char *rcpt; /* ¦¬¥ó¤H */
+ char *title; /* ¶l¥ó¼ÐÃD */
+ usint xmode;
+{
+ int userno;
+ HDR hdr;
+ char folder[64];
+
+ if ((userno = acct_userno(rcpt)) > 0) /* ¦¬¥ó¤H¦³¥i¯à¤£¦s¦b */
+ {
+ usr_fpath(folder, rcpt, fn_dir);
+ hdr_stamp(folder, HDR_COPY, &hdr, fpath);
+ strcpy(hdr.owner, cuser.userid);
+ strcpy(hdr.title, title);
+ hdr.xmode = xmode;
+ rec_add(folder, &hdr, sizeof(HDR));
+ m_biff(userno);
+ }
+ return userno;
+}
+
+
+/* ----------------------------------------------------- */
+/* ¸s²Õ±H«H¡B¦^«H */
+/* ----------------------------------------------------- */
+
+
+#ifdef MULTI_MAIL /* Thor.981009: ¨¾¤î·R±¡©¯¹B«H */
+
+static int
+multi_send(title)
+ char *title;
+{
+ FILE *fp;
+ HDR hdr;
+ char buf[128], fpath[64], *userid;
+ int userno, reciper, listing;
+ LinkList *wp;
+
+ vs_bar(title ? "¸s²Õ¦^«H" : "¸s²Õ±H«H");
+
+ ll_new();
+ listing = reciper = 0;
+
+ /* ¦^«H®ÉŪ¨ú mail list ¦W³æ */
+
+ if (*quote_file)
+ {
+ ll_add(quote_user);
+ reciper = 1;
+
+ fp = fopen(quote_file, "r");
+ while (fgets(buf, sizeof(buf), fp))
+ {
+ if (memcmp(buf, "¡° ", 3))
+ {
+ if (listing)
+ break;
+ }
+ else
+ {
+ userid = buf + 3;
+ if (listing)
+ {
+ strtok(userid, " \n\r");
+ do
+ {
+ if ((userno = acct_userno(userid)) && (userno != cuser.userno) &&
+ !ll_has(userid))
+ {
+ ll_add(userid);
+ reciper++;
+ }
+ } while (userid = (char *) strtok(NULL, " \n\r"));
+ }
+ else if (!memcmp(userid, "[³q§i]", 6))
+ listing = 1;
+ }
+ }
+ fclose(fp);
+ ll_out(3, 0, MSG_LL);
+ }
+
+ /* ³]©w mail list ªº¦W³æ */
+
+ reciper = pal_list(reciper);
+
+ /* ¶}©l±H«H */
+
+ move(1, 0);
+ clrtobot();
+
+ if (reciper == 1)
+ {
+ if (title)
+ sprintf(ve_title, "Re: %.64s", str_ttl(title));
+
+ if (!vget(2, 0, "¼ÐÃD¡G", ve_title, TTLEN + 1, title ? GCARRY : DOECHO))
+ return -1;
+ mail_send(ll_head->data);
+ }
+ else if (reciper >= 2 && ve_subject(2, title, "[³q§i] "))
+ {
+ usr_fpath(fpath, cuser.userid, fn_note);
+
+ if (fp = fopen(fpath, "w"))
+ {
+ fprintf(fp, "¡° [³q§i] ¦@ %d ¤H¦¬¥ó", reciper);
+ listing = 80;
+ wp = ll_head;
+
+ do
+ {
+ userid = wp->data;
+ reciper = strlen(userid) + 1;
+
+ if (listing + reciper > 75)
+ {
+ listing = reciper;
+ fprintf(fp, "\n¡°");
+ }
+ else
+ {
+ listing += reciper;
+ }
+
+ fprintf(fp, " %s", userid);
+ } while (wp = wp->next);
+
+ memset(buf, '-', 75);
+ buf[75] = '\0';
+ fprintf(fp, "\n%s\n\n", buf);
+ fclose(fp);
+ }
+
+ utmp_mode(M_SMAIL);
+ curredit = EDIT_MAIL | EDIT_LIST;
+
+ if (vedit(fpath, 1) < 0)
+ {
+ vmsg(msg_cancel);
+ unlink(fpath);
+ return -1;
+ }
+ else
+ {
+ vs_bar("±H«H¤¤...");
+
+ listing = 80;
+ wp = ll_head;
+ title = ve_title;
+ userno = 2; /* ­É¥Î userno ·í¦L¨ì­þ¤@¦æ */
+
+ do
+ {
+ userid = wp->data;
+ if (userno < b_lines) /* ³Ì¦h¦L¨ì b_lines - 1 */
+ {
+ /* ¦L¥X¥Ø«e±H¨ì­þ¤@­Ó id */
+ reciper = strlen(userid) + 1;
+ if (listing + reciper > 75)
+ {
+ listing = reciper;
+ outc('\n');
+ userno++;
+ }
+ else
+ {
+ listing += reciper;
+ outc(' ');
+ }
+ outs(userid);
+ }
+
+ usr_fpath(buf, userid, fn_dir);
+ hdr_stamp(buf, HDR_LINK, &hdr, fpath);
+ strcpy(hdr.owner, cuser.userid);
+ strcpy(hdr.title, title);
+ hdr.xmode = MAIL_MULTI;
+ rec_add(buf, &hdr, sizeof(HDR));
+ } while (wp = wp->next);
+
+ if (fp = fopen(FN_RUN_MAIL_LOG, "a"))
+ {
+ fprintf(fp, "%s %-13s-> %s\n\t%s\n",
+ Btime(&hdr.chrono), cuser.userid, "¸s²Õ±H«H", title);
+ fclose(fp);
+ }
+
+ mail_hold(fpath, "¸s²Õ±H«H", title, 0);
+ unlink(fpath);
+ vmsg(msg_sent_ok);
+ }
+ }
+ else
+ {
+ vmsg(msg_cancel);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static void
+multi_reply(hdr)
+ HDR *hdr;
+{
+ if (!multi_send(hdr->title))
+ hdr->xmode |= (MAIL_REPLIED | MAIL_READ);
+}
+
+
+int
+m_list()
+{
+ if (HAS_PERM(PERM_DENYMAIL))
+ return XEASY;
+
+ multi_send(NULL);
+
+ return 0;
+}
+
+#endif
+
+
+int
+do_mreply(hdr, noreply)
+ HDR *hdr;
+ int noreply; /* 1:­n 0:¤£­n Àˬd MAIL_NOREPLY */
+{
+ if ((noreply && (hdr->xmode & MAIL_NOREPLY)) ||
+ HAS_PERM(PERM_DENYMAIL) ||
+ !HAS_PERM(strchr(hdr->owner, '@') ? PERM_INTERNET : PERM_LOCAL))
+ {
+ return XO_FOOT;
+ }
+
+ strcpy(quote_user, hdr->owner);
+ strcpy(quote_nick, hdr->nick);
+
+#ifdef MULTI_MAIL
+ /* itoc.001125: ±`¦³¤H»~«ö¸s²Õ±H«H¡A½T©w¤@¤U */
+ if ((hdr->xmode & MAIL_MULTI) && vans(MSG_MULTIREPLY) == 'y')
+ multi_reply(hdr);
+ else
+#endif
+ mail_reply(hdr);
+
+ return XO_HEAD;
+}
+
+
+/* ----------------------------------------------------- */
+/* Mail Box call-back routines */
+/* ----------------------------------------------------- */
+
+
+static inline int
+mbox_attr(type)
+ int type;
+{
+#ifdef OVERDUE_MAILDEL
+ if (type & MAIL_DELETE)
+ return 'D';
+#endif
+
+ if (type & MAIL_REPLIED)
+ return (type & MAIL_MARKED) ? 'R' : 'r';
+
+ return "+ Mm"[type & 3];
+}
+
+
+static void
+mbox_item(num, hdr)
+ int num; /* sequence number */
+ HDR *hdr;
+{
+#ifdef OVERDUE_MAILDEL
+ int xmode;
+
+ xmode = hdr->xmode;
+ prints(xmode & MAIL_DELETE ? "%6d%c\033[1;5;37;41m%c\033[m " : "%6d%c%c ",
+ num, tag_char(hdr->chrono), mbox_attr(xmode));
+#else
+ prints("%6d%c%c ", num, tag_char(hdr->chrono), mbox_attr(hdr->xmode));
+#endif
+
+ hdr_outs(hdr, d_cols + 47);
+}
+
+
+static int
+mbox_body(xo)
+ XO *xo;
+{
+ HDR *hdr;
+ int num, max, tail;
+
+ max = xo->max;
+ if (max <= 0)
+ {
+ vmsg("±z¨S¦³¨Ó«H");
+ return XO_QUIT;
+ }
+
+ num = xo->top;
+ hdr = (HDR *) xo_pool;
+ tail = num + XO_TALL;
+ if (max > tail)
+ max = tail;
+
+ move(3, 0);
+ do
+ {
+ mbox_item(++num, hdr++);
+ } while (num < max);
+ clrtobot();
+
+ /* return XO_NONE; */
+ return XO_FOOT; /* itoc.010403: §â b_lines ¶ñ¤W feeter */
+}
+
+
+static int
+mbox_head(xo)
+ XO *xo;
+{
+ if (HAS_STATUS(STATUS_BIFF)) /* ¤@¶i¤J«H½c´N®³±¼ STATUS_BIFF */
+ cutmp->status ^= STATUS_BIFF;
+
+ vs_head("¶l¥ó¿ï³æ", str_site);
+ prints(NECKER_MBOX, d_cols, "");
+ return mbox_body(xo);
+}
+
+
+static int
+mbox_load(xo)
+ XO *xo;
+{
+ xo_load(xo, sizeof(HDR));
+ return mbox_body(xo);
+}
+
+
+static int
+mbox_init(xo)
+ XO *xo;
+{
+ xo_load(xo, sizeof(HDR));
+ return mbox_head(xo);
+}
+
+
+static int
+mbox_delete(xo)
+ XO *xo;
+{
+ int pos;
+ HDR *hdr;
+ char fpath[64], *dir;
+
+ pos = xo->pos;
+ hdr = (HDR *) xo_pool + (pos - xo->top);
+
+#ifdef OVERDUE_MAILDEL
+ /* Thor.980901: mark «á­Y³Q'D'°_¨Ó¡A«h¤@¼Ë¥i¥H delete¡A¥u¦³ MARK & no delete ¤~·|µL®Ä */
+ if ((hdr->xmode & (MAIL_MARKED | MAIL_DELETE)) == MAIL_MARKED)
+#else
+ if (hdr->xmode & MAIL_MARKED)
+#endif
+ return XO_NONE;
+
+ if (vans(msg_del_ny) == 'y')
+ {
+ dir = xo->dir;
+ currchrono = hdr->chrono;
+ if (!rec_del(dir, sizeof(HDR), xo->key == XZ_XPOST ? hdr->xid : pos, cmpchrono))
+ {
+ hdr_fpath(fpath, dir, hdr);
+ unlink(fpath);
+ return XO_LOAD;
+ }
+ }
+ return XO_FOOT;
+}
+
+
+static int
+chkmbox(hdr)
+ HDR *hdr;
+{
+ return (hdr->xmode & MAIL_MARKED);
+}
+
+
+static int
+vfymbox(hdr, pos)
+ HDR *hdr;
+ int pos;
+{
+ return (Tagger(hdr->chrono, pos, TAG_NIN) || chkmbox(hdr));
+}
+
+
+static void
+delmbox(xo, hdr)
+ XO *xo;
+ HDR *hdr;
+{
+ char fpath[64];
+
+ hdr_fpath(fpath, xo->dir, hdr);
+ unlink(fpath);
+}
+
+
+static int
+mbox_rangedel(xo)
+ XO *xo;
+{
+ return xo_rangedel(xo, sizeof(HDR), chkmbox, delmbox);
+}
+
+
+static int
+mbox_prune(xo)
+ XO *xo;
+{
+ return xo_prune(xo, sizeof(HDR), vfymbox, delmbox);
+}
+
+
+static int
+mbox_forward(xo)
+ XO *xo;
+{
+ if (HAS_PERM(PERM_DENYMAIL))
+ return XO_NONE;
+
+ return post_forward(xo); /* itoc.011230: »P post_forward() ¦@¥Î */
+}
+
+
+static int
+mbox_browse(xo) /* itoc.000513: Åý«HŪ¨ì¤@¥b¤]¯à reply mark delete */
+ XO *xo;
+{
+ HDR *hdr;
+ int pos, xmode, nmode;
+ char *dir, fpath[64];
+
+ dir = xo->dir;
+
+ for (;;)
+ {
+ pos = xo->pos;
+ hdr = (HDR *) xo_pool + (pos - xo->top);
+ xmode = hdr->xmode;
+
+ hdr_fpath(fpath, dir, hdr);
+
+ /* Thor.990204: ¬°¦Ò¼{more ¶Ç¦^­È */
+ if ((nmode = more(fpath, FOOTER_MAILER)) < 0)
+ break;
+
+ /* itoc.010324: ¨q§¹¸Ó½g¥þ³¡¥H«á­n³]¬°¤wŪ¡A¥H§K¤@ª½«öªÅ¥Õ©¹¤UŪ¡A¥u§â³Ì«á¤@½g³]¤wŪ */
+ if (nmode == 0 && !(xmode & MAIL_READ))
+ {
+ hdr->xmode = xmode | MAIL_READ;
+ rec_put(dir, hdr, sizeof(HDR), pos, NULL);
+ }
+
+ strcpy(currtitle, str_ttl(hdr->title));
+
+re_key:
+ switch (xo_getch(xo, nmode))
+ {
+ case XO_BODY:
+ continue;
+
+ case 'y':
+ case 'r':
+ strcpy(quote_file, fpath);
+ do_mreply(hdr, 1);
+ break;
+
+ case 'd':
+ if (mbox_delete(xo) == XO_LOAD)
+ return mbox_init(xo);
+ break;
+
+ case 'm':
+ /* hdr->xmode ^= MAIL_MARKED; */
+ /* ¦b mbox_browse ®É¬Ý¤£¨ì m °O¸¹¡A©Ò¥H­­¨î¥u¯à mark */
+ hdr->xmode |= MAIL_MARKED;
+ break;
+
+ case 'x': /* itoc.011231: mbox_browse ®É¥iÂà¿ý«H¥h¬ÝªO */
+ post_cross(xo);
+ break;
+
+ case 'X':
+ mbox_forward(xo);
+ break;
+
+ case '/':
+ if (vget(b_lines, 0, "·j´M¡G", hunt, sizeof(hunt), DOECHO))
+ {
+ more(fpath, FOOTER_MAILER);
+ goto re_key;
+ }
+ continue;
+
+ case 'E':
+ return mbox_edit(xo);
+
+ case 'C': /* itoc.000515: mbox_browse ®É¥i¦s¤J¼È¦sÀÉ */
+ {
+ FILE *fp;
+ if (fp = tbf_open())
+ {
+ f_suck(fp, fpath);
+ fclose(fp);
+ }
+ }
+ break;
+
+ case 'h':
+ xo_help("mbox");
+ break;
+ }
+ break;
+ }
+
+ nmode = hdr->xmode | MAIL_READ;
+ if (xmode != nmode)
+ {
+ hdr->xmode = nmode;
+ rec_put(dir, hdr, sizeof(HDR), pos, NULL);
+ }
+
+ return mbox_head(xo);
+}
+
+
+static int
+mbox_reply(xo)
+ XO *xo;
+{
+ int pos, xmode;
+ HDR *hdr;
+
+ pos = xo->pos;
+ hdr = (HDR *) xo_pool + pos - xo->top;
+ xmode = hdr->xmode;
+
+ hdr_fpath(quote_file, xo->dir, hdr);
+ do_mreply(hdr, 1);
+
+ if (hdr->xmode != xmode)
+ rec_put(xo->dir, hdr, sizeof(HDR), pos, NULL);
+
+ return XO_HEAD;
+}
+
+
+int
+mbox_edit(xo) /* itoc.010301: ¥i¥H½s¿è¦Û¤v«H½c¤¤ªº«H */
+ XO *xo;
+{
+ char fpath[64];
+ HDR *hdr;
+
+ hdr = (HDR *) xo_pool + (xo->pos - xo->top);
+ hdr_fpath(fpath, xo->dir, hdr);
+
+ curredit = EDIT_MAIL; /* Thor.981105: ª½±µ«ü©w¼g«H */
+
+ if (!strcmp(hdr->owner, cuser.userid) || HAS_PERM(PERM_ALLBOARD)) /* ¦Û¤v±Hµ¹¦Û¤vªº«H©Î¬O¯¸ªø */
+ vedit(fpath, 0);
+ else
+ vedit(fpath, -1); /* ¥u¯à½s¿è¤£¥iÀx¦s */
+
+ /* return mbox_head(xo); */
+ return XO_HEAD; /* itoc.041023: XZ_MBOX ©M XZ_XPOST ¦@¥Î mbox_edit() */
+}
+
+
+static int
+mbox_title(xo) /* itoc.020113: ¥i¥H§ï¦Û¤v«H½c¤¤ªº¼ÐÃD */
+ XO *xo;
+{
+ HDR *fhdr, mhdr;
+ int pos, cur;
+
+ pos = xo->pos;
+ cur = pos - xo->top;
+ fhdr = (HDR *) xo_pool + cur;
+ memcpy(&mhdr, fhdr, sizeof(HDR));
+
+ if (strcmp(cuser.userid, mhdr.owner) && !HAS_PERM(PERM_ALLBOARD))
+ return XO_NONE;
+
+ vget(b_lines, 0, "¼ÐÃD¡G", mhdr.title, TTLEN + 1, GCARRY);
+
+ if (HAS_PERM(PERM_ALLBOARD)) /* itoc.000213: ­ì§@ªÌ¥u¯à§ï¼ÐÃD */
+ {
+ vget(b_lines, 0, "§@ªÌ¡G", mhdr.owner, 73 /* sizeof(mhdr.owner) */, GCARRY);
+ /* Thor.980727: sizeof(mhdr.owner) = 80 ·|¶W¹L¤@¦æ */
+ vget(b_lines, 0, "¼ÊºÙ¡G", mhdr.nick, sizeof(mhdr.nick), GCARRY);
+ vget(b_lines, 0, "¤é´Á¡G", mhdr.date, sizeof(mhdr.date), GCARRY);
+ }
+
+ if (memcmp(fhdr, &mhdr, sizeof(HDR)) && vans(msg_sure_ny) == 'y')
+ {
+ *fhdr = mhdr;
+ rec_put(xo->dir, fhdr, sizeof(HDR), xo->key == XZ_XPOST ? fhdr->xid : pos, NULL);
+ move(3 + cur, 0);
+ mbox_item(++pos, fhdr);
+
+ /* itoc.010709: ­×§ï¤å³¹¼ÐÃD¶¶«K­×§ï¤º¤åªº¼ÐÃD */
+ header_replace(xo, fhdr);
+ }
+ return XO_FOOT;
+}
+
+
+static int
+mbox_mark(xo)
+ XO *xo;
+{
+ HDR *hdr;
+ int cur, pos;
+
+ pos = xo->pos;
+ cur = pos - xo->top;
+ hdr = (HDR *) xo_pool + cur;
+ move(3 + cur, 7);
+ outc(mbox_attr(hdr->xmode ^= MAIL_MARKED));
+ rec_put(xo->dir, hdr, sizeof(HDR), xo->key == XZ_XPOST ? hdr->xid : pos, NULL);
+ return XO_NONE;
+}
+
+
+static int
+mbox_tag(xo)
+ XO *xo;
+{
+ HDR *hdr;
+ int tag, pos, cur;
+
+ pos = xo->pos;
+ cur = pos - xo->top;
+ hdr = (HDR *) xo_pool + cur;
+
+ if (xo->key == XZ_XPOST)
+ pos = hdr->xid;
+
+ if (tag = Tagger(hdr->chrono, pos, TAG_TOGGLE))
+ {
+ move(3 + cur, 6);
+ outc(tag > 0 ? '*' : ' ');
+ }
+
+ /* return XO_NONE; */
+ return xo->pos + 1 + XO_MOVE; /* lkchu.981201: ¸õ¦Ü¤U¤@¶µ */
+}
+
+
+static int
+mbox_send(xo)
+ XO *xo;
+{
+ m_send();
+ return mbox_head(xo);
+}
+
+
+static int
+mbox_visit(xo)
+ XO *xo;
+{
+ int pos, fd;
+ char *dir;
+ HDR *hdr;
+
+ pos = vans("³]©w©Ò¦³«H¥ó (V)¤wŪ (Q)¨ú®ø¡H[Q] ");
+ if (pos == 'v')
+ {
+ if (HAS_STATUS(STATUS_BIFF))
+ cutmp->status ^= STATUS_BIFF;
+
+ dir = xo->dir;
+ if ((fd = open(dir, O_RDONLY)) < 0)
+ return XO_FOOT;
+
+ pos = 0;
+ mgets(-1);
+ while (hdr = mread(fd, sizeof(HDR)))
+ {
+ if (!(hdr->xmode & MAIL_READ))
+ {
+ hdr->xmode |= MAIL_READ;
+ rec_put(dir, hdr, sizeof(HDR), pos, NULL);
+ }
+ pos++;
+ }
+ close(fd);
+
+ return mbox_init(xo);
+ }
+ return XO_FOOT;
+}
+
+
+static int
+mbox_sysop(xo) /* itoc.001029.µù¸Ñ: ¤è«K¦¬ sysop/guest ªº«H */
+ XO *xo;
+{
+ if (xo == (XO *) &cmbox) /* ­Y¤w¸g¶i¤J²Ä¤G¤H«H½c¡A«h¤£¯à¦A¶i²Ä¤T¤H«H½c */
+ {
+#ifdef SYSOP_CHECK_MAIL
+ if (HAS_PERM(PERM_SYSOP)) /* itoc.001029: ­­¨î¥u¦³ SYSOPs ¥i¥H¬Ý user «H½c */
+ {
+ ACCT acct;
+ XO *xt;
+ char fpath[64];
+
+ if (acct_get(msg_uid, &acct) > 0)
+ {
+ alog("¶i¤J«H½c", acct.userid);
+ usr_fpath(fpath, acct.userid, fn_dir);
+ xz[XZ_MBOX - XO_ZONE].xo = xt = xo_new(fpath);
+ xover(XZ_MBOX);
+ free(xt);
+ xz[XZ_MBOX - XO_ZONE].xo = xo;
+ }
+ return mbox_init(xo);
+ }
+#else
+ if (HAS_PERM(PERM_ALLADMIN)) /* itoc.030914: Åý©Ò¦³ ADMINs ³£¥i¥H¬Ý sysop/guest «H½c */
+ {
+ char *userid;
+ XO *xt;
+ char fpath[64];
+
+ sprintf(fpath, "¶i¤J (1)%s (2)%s ªº«H½c¡H[1] ", str_sysop, STR_GUEST);
+ userid = (vans(fpath) == '2') ? STR_GUEST : str_sysop;
+ alog("¶i¤J«H½c", userid);
+ usr_fpath(fpath, userid, fn_dir);
+ xz[XZ_MBOX - XO_ZONE].xo = xt = xo_new(fpath);
+ xover(XZ_MBOX);
+ free(xt);
+ xz[XZ_MBOX - XO_ZONE].xo = xo;
+ return mbox_init(xo);
+ }
+#endif
+ }
+
+ return XO_NONE;
+}
+
+
+static int
+mbox_gem(xo) /* itoc.010727: ±N­Ó¤HºëµØ°Ï©M¬ÝªOºëµØ°Ï¾ã¦X */
+ XO *xo;
+{
+ char fpath[64];
+
+ usr_fpath(fpath, cuser.userid, "gem/"FN_DIR);
+ /* ­Ó¤HºëµØ°Ï¤£»Ý­n GEM_X_BIT */
+ XoGem(fpath, "­Ó¤HºëµØ°Ï", GEM_W_BIT | GEM_M_BIT);
+ return mbox_init(xo);
+}
+
+
+static int
+mbox_copy(xo) /* itoc.011025: ¨ú¥N gem_gather */
+ XO *xo;
+{
+ int tag;
+
+ tag = AskTag("«H½c¤å³¹«þ¨©");
+
+ if (tag < 0)
+ return XO_FOOT;
+
+ gem_buffer(xo->dir, tag ? NULL : (HDR *) xo_pool + (xo->pos - xo->top));
+
+ zmsg("Àɮ׼аO§¹¦¨¡C[ª`·N] «þ¨©«á¤~¯à§R°£­ì¤å¡I");
+ return mbox_gem(xo); /* «þ¨©§¹ª½±µ¶iºëµØ°Ï */
+}
+
+
+static int
+mbox_help(xo)
+ XO *xo;
+{
+ xo_help("mbox");
+ return mbox_head(xo);
+}
+
+
+static KeyFunc mbox_cb[] =
+{
+ XO_INIT, mbox_init,
+ XO_LOAD, mbox_load,
+ XO_HEAD, mbox_head,
+ XO_BODY, mbox_body,
+
+ 'r', mbox_browse,
+ 's', mbox_send,
+ 'd', mbox_delete,
+ 'D', mbox_rangedel,
+ 'x', post_cross,
+ 'X', mbox_forward,
+ 'E', mbox_edit,
+ 'T', mbox_title,
+ 'm', mbox_mark,
+ 'R', mbox_reply,
+ 'y', mbox_reply,
+ 'v', mbox_visit,
+ 'w', post_write, /* itoc.010408: ­É¥Î post_write §Y¥i */
+
+ // KEY_TAB, mbox_sysop, /* mp607: ¨S¦b¥Î¤F®³±¼ */
+ 'z', mbox_gem,
+ 'c', mbox_copy,
+ 'g', gem_gather,
+
+ 't', mbox_tag,
+
+ '~', XoXselect, /* itoc.001220: ·j´M§@ªÌ/¼ÐÃD */
+ 'S', XoXsearch, /* itoc.001220: ·j´M¬Û¦P¼ÐÃD¤å³¹ */
+ 'a', XoXauthor, /* itoc.001220: ·j´M§@ªÌ */
+ '/', XoXtitle, /* itoc.001220: ·j´M¼ÐÃD */
+ 'f', XoXfull, /* itoc.030608: ¥þ¤å·j´M */
+ 'G', XoXmark, /* itoc.010325: ·j´M mark ¤å³¹ */
+ 'i', XoXrestrict, /* chensc.060827: ·j´M¥[±K¤å³¹ */
+ 'L', XoXlocal, /* itoc.010822: ·j´M¥»¦a¤å³¹ */
+
+ Ctrl('D'), mbox_prune,
+ Ctrl('Q'), xo_uquery,
+ Ctrl('O'), xo_usetup,
+
+ 'h', mbox_help
+};
+
+
+void
+mbox_main()
+{
+ cmbox.mail_xo.pos = XO_TAIL;
+ cmbox.mail_xo.xyz = str_site;
+ usr_fpath(cmbox.dir, cuser.userid, fn_dir);
+ xz[XZ_MBOX - XO_ZONE].xo = (XO *) &cmbox;
+ xz[XZ_MBOX - XO_ZONE].cb = mbox_cb;
+}
+
+
+KeyFunc xmbox_cb[] =
+{
+ XO_INIT, xpost_init,
+ XO_LOAD, xpost_load,
+ XO_HEAD, xpost_head,
+ XO_BODY, mbox_body, /* Thor.980911: ¦@¥Î§Y¥i */
+
+ 'r', xmbox_browse,
+ 'y', mbox_reply,
+ 's', mbox_send,
+ 'x', post_cross,
+ 'X', post_forward,
+ 'E', mbox_edit,
+ 'T', mbox_title,
+ 'm', mbox_mark,
+ 'd', mbox_delete,
+ 'w', post_write, /* itoc.010408: ­É¥Î post_write §Y¥i */
+
+ 'c', mbox_copy,
+ 'g', gem_gather,
+
+ 't', mbox_tag,
+
+ '~', XoXselect,
+ 'S', XoXsearch,
+ 'a', XoXauthor,
+ '/', XoXtitle,
+ 'f', XoXfull,
+ 'G', XoXmark,
+ 'i', XoXrestrict,
+ 'L', XoXlocal,
+
+ Ctrl('Q'), xo_uquery,
+ Ctrl('O'), xo_usetup,
+
+ 'h', mbox_help
+};
diff --git a/maple/maple.p b/maple/maple.p
new file mode 100644
index 0000000..2a00057
--- /dev/null
+++ b/maple/maple.p
@@ -0,0 +1,276 @@
+/* acct.c */
+int acct_load(ACCT *acct, char *userid);
+void acct_save(ACCT *acct);
+int acct_userno(char *userid);
+int acct_get(char *msg, ACCT *acct);
+void bitmsg(char *msg, char *str, int level);
+usint bitset(usint pbits, int count, int maxon, char *msg, char *perms[]);
+void acct_show(ACCT *u, int adm);
+void acct_setup(ACCT *u, int adm);
+void acct_setperm(ACCT *u, usint leveup, usint leveldown);
+void addmoney(int addend);
+void addgold(int addend);
+int brd_new(BRD *brd);
+void brd_edit(int bno);
+void brd_title(int bno);
+void x_file(int mode, char *xlist[], char *flist[]);
+int m_trace(void);
+
+/* bbsd.c */
+void alog(char *mode, char *msg);
+void blog(char *mode, char *msg);
+void u_exit(char *mode);
+void abort_bbs(void);
+int belong_list(char *filelist, char *key, char *desc);
+
+/* bmw.c */
+int can_override(UTMP *up);
+int can_see(UTMP *my, UTMP *up);
+int bmw_send(UTMP *callee, BMW *bmw);
+void bmw_edit(UTMP *up, char *hint, BMW *bmw);
+int bmw_reply_CtrlRT(int ch);
+void bmw_reply(void);
+void bmw_rqst(void);
+void do_write(UTMP *up);
+void bmw_log(void);
+int t_bmw(void);
+int t_display(void);
+
+/* board.c */
+void brh_get(time_t bstamp, int bhno);
+int brh_unread(time_t chrono);
+void brh_visit(int mode);
+int brh_add(time_t prev, time_t chrono, time_t next);
+int bstamp2bno(time_t stamp);
+void brh_save(void);
+void brd_force(void);
+void class_item(int num, int bno, int brdpost);
+int is_bm(char *list, char *userid);
+int XoPost(int bno);
+int Select(void);
+int Class(void);
+void board_main(void);
+int Boards(void);
+BRD *has_personalbrd (char *bname);
+void brd_editlock(const char* fpath);
+void brd_viewlock(const char* fpath);
+void brd_getlockinfo(const char* fpath, char* type, char* userid, char* reason);
+void brd_setlockinfo(const char* fpath, char type, const char* userid, const char* reason);
+
+/* cache.c */
+void ushm_init(void);
+void utmp_mode(int mode);
+int utmp_new(UTMP *up);
+void utmp_free(UTMP *up);
+UTMP *utmp_find(int userno);
+UTMP *utmp_get(int userno, char *userid);
+UTMP *utmp_seek(HDR *hdr);
+void utmp_admset(int userno, usint status);
+int utmp_count(int userno, int show);
+UTMP *utmp_search(int userno, int order);
+void bshm_init(void);
+void bshm_reload(void);
+int brd_bno(char *bname);
+void fshm_init(void);
+void film_out(int tag, int row);
+
+/* edit.c */
+char *tbf_ask(void);
+FILE *tbf_open(void);
+void ve_backup(void);
+void ve_recover(void);
+void ve_header(FILE *fp);
+void ve_banner(FILE *fp, int modify);
+int ve_subject(int row, char *topic, char *dft);
+int vedit(char *fpath, int ve_op);
+
+/* favor.c */
+void mf_fpath(char *fpath, char *userid, char *fname);
+int MyFavorite(void);
+void XoMF(char *folder);
+void mf_main(void);
+
+/* gem.c */
+int gem_link(char *brdname);
+void brd2gem(BRD *brd, HDR *gem);
+void gem_buffer(char *dir, HDR *ghdr);
+int gem_gather(XO *xo);
+void XoGem(char *folder, char *title, int level);
+void gem_main(void);
+
+/* mail.c */
+void ll_new(void);
+void ll_add(char *name);
+int ll_del(char *name);
+int ll_has(char *name);
+void ll_out(int row, int column, char *msg);
+int bsmtp(char *fpath, char *title, char *rcpt, int method);
+int m_zip(void);
+int m_verify(void);
+usint m_quota(void);
+usint m_query(char *userid);
+void m_biff(int userno);
+void mail_hold(char *fpath, char *rcpt, char *title, int hold);
+int mail_external(char *addr);
+int mail_send(char *rcpt);
+int my_send(char *userid);
+int m_send(void);
+int m_internet(void);
+int m_sysop(void);
+void mail_self(char *fpath, char *owner, char *title, usint xmode);
+int mail_him(char *fpath, char *rcpt, char *title, usint xmode);
+int m_list(void);
+int do_mreply(HDR *hdr, int noreply);
+int mbox_edit(XO *xo);
+void mbox_main(void);
+
+/* menu.c */
+int pad_view(void);
+void vs_head(char *title, char *mid);
+void movie(void);
+void menu(void);
+
+/* more.c */
+char *mgets(int fd);
+void *mread(int fd, int len);
+int more(char *fpath, char *footer);
+
+/* pal.c */
+int is_mygood(int userno);
+int is_mybad(int userno);
+int is_ogood(UTMP *up);
+int is_obad(UTMP *up);
+int is_bgood(BPAL *bpal);
+int is_bbad(BPAL *bpal);
+int image_pal(char *fpath, int *pool);
+void pal_cache(void);
+void pal_sync(char *fpath);
+int pal_list(int reciper);
+void pal_edit(int key, PAL *pal, int echo);
+int pal_find(char *fpath, int userno);
+int t_pal(void);
+int t_list(void);
+
+/* post.c */
+int cmpchrono(HDR *hdr);
+void btime_update(int bno);
+void cancel_post(HDR *hdr);
+void add_post(char *brdname, char *fpath, char *title);
+int do_reply(XO *xo, HDR *hdr);
+int tag_char(int chrono);
+void hdr_outs(HDR *hdr, int cc);
+int post_edit(XO *xo);
+void header_replace(XO *xo, HDR *fhdr);
+int post_cross(XO *xo);
+int post_forward(XO *xo);
+int post_write(XO *xo);
+int post_score(XO *xo);
+void do_binfo(BRD* brd);
+
+/* talk.c */
+char *bmode(UTMP *up, int simple);
+void frienz_sync(char *fpath);
+void aloha();
+void loginNotify();
+void my_query(char *userid);
+int t_loginNotify(void);
+void loginNotify(void);
+int talk_page(UTMP *up);
+int t_pager(void);
+int t_cloak(void);
+int t_query(void);
+int t_talk(void);
+void talk_rqst(void);
+
+/* ulist.c */
+void talk_main(void);
+
+/* user.c */
+void justify_log(char *userid, char *justify);
+int u_addr(void);
+int u_register(void);
+int u_verify(void);
+int u_deny(void);
+int u_info(void);
+int u_setup(void);
+int u_lock(void);
+int u_log(void);
+int u_xfile(void);
+
+/* visio.c */
+int is_zhc_low(char *str, int pos);
+void prints(char *fmt, ...);
+void bell(void);
+void move(int x, int y);
+void refresh(void);
+void clear(void);
+void clrtoeol(void);
+void clrtobot(void);
+void outc(int ch);
+void outs(uschar *str);
+void outx(uschar *str);
+void outz(uschar *str);
+void outf(uschar *str);
+void scroll(void);
+void rscroll(void);
+void cursor_save(void);
+void cursor_restore(void);
+void save_foot(screenline *slp);
+void restore_foot(screenline *slp, int line);
+int vs_save(screenline *slp);
+void vs_restore(screenline *slp);
+int vmsg(char *msg);
+void zmsg(char *msg);
+void vs_bar(char *title);
+void vio_save(void);
+void vio_restore(void);
+int vio_holdon(void);
+void add_io(int fd, int timeout);
+int igetch(void);
+BRD *ask_board(char *board, int perm, char *msg);
+int vget(int line, int col, uschar *prompt, uschar *data, int max, int echo);
+int vans(char *prompt);
+int vkey(void);
+
+/* window.c */
+int pans(int x, int y, char *title, char **desc);
+int pmsg(char *msg);
+
+/* xover.c */
+XO *xo_new(char *path);
+XO *xo_get(char *path);
+XO *xo_get_post(char *path, BRD *brd);
+void xo_load(XO *xo, int recsiz);
+int xo_rangedel(XO *xo, int size, int (*fchk) (), void (*fdel) ());
+int Tagger(time_t chrono, int recno, int op);
+void EnumTag(void *data, char *dir, int locus, int size);
+int AskTag(char *msg);
+int xo_prune(XO *xo, int size, int (*fvfy) (), void (*fdel) ());
+int xo_uquery(XO *xo);
+int xo_usetup(XO *xo);
+int xo_getch(XO *xo, int ch);
+void xover(int cmd);
+int every_Z(int zone);
+int every_U(int zone);
+int xo_cursor(int ch, int pagemax, int num, int *pageno, int *pos, int *redraw);
+void xo_help(char *path);
+
+/* xpost.c */
+int xpost_head(XO *xo);
+int xpost_init(XO *xo);
+int xpost_load(XO *xo);
+int xpost_browse(XO *xo);
+int xmbox_browse(XO *xo);
+int XoXselect(XO *xo);
+int XoXsearch(XO *xo);
+int XoXauthor(XO *xo);
+int XoXtitle(XO *xo);
+int XoXfull(XO *xo);
+int XoXmark(XO *xo);
+int XoXrestrict(XO *xo);
+int XoXscore(XO *xo);
+int XoXlocal(XO *xo);
+int news_head(XO *xo);
+int news_init(XO *xo);
+int news_load(XO *xo);
+int XoNews(XO *xo);
diff --git a/maple/menu.c b/maple/menu.c
new file mode 100644
index 0000000..3beed19
--- /dev/null
+++ b/maple/menu.c
@@ -0,0 +1,1287 @@
+/*-------------------------------------------------------*/
+/* menu.c ( NTHU CS MapleBBS Ver 3.00 ) */
+/*-------------------------------------------------------*/
+/* target : menu/help/movie routines */
+/* create : 95/03/29 */
+/* update : 97/03/29 */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+
+extern UCACHE *ushm;
+extern FCACHE *fshm;
+
+
+#ifndef ENHANCED_VISIT
+extern time_t brd_visit[];
+#endif
+
+
+/* ----------------------------------------------------- */
+/* Â÷¶} BBS ¯¸ */
+/* ----------------------------------------------------- */
+
+
+#define FN_RUN_NOTE_PAD "run/note.pad"
+#define FN_RUN_NOTE_TMP "run/note.tmp"
+
+
+typedef struct
+{
+ time_t tpad;
+ char msg[400];
+} Pad;
+
+
+int
+pad_view()
+{
+ int fd, count;
+ Pad *pad;
+
+ if ((fd = open(FN_RUN_NOTE_PAD, O_RDONLY)) < 0)
+ return XEASY;
+
+ count = 0;
+ mgets(-1);
+
+ for (;;)
+ {
+ pad = mread(fd, sizeof(Pad));
+ if (!pad)
+ {
+ vmsg(NULL);
+ break;
+ }
+ else if (!(count % 5)) /* itoc.020122: ¦³ pad ¤~¦L */
+ {
+ clear();
+ move(0, 23);
+ prints("¡i »Ä ²¢ ­W »¶ ¯d ¨¥ ªO ¡j ²Ä %d ­¶\n\n", count / 5 + 1);
+ }
+
+ outs(pad->msg);
+ count++;
+
+ if (!(count % 5))
+ {
+ move(b_lines, 0);
+ outs("½Ð«ö [SPACE] Ä~ÄòÆ[½à¡A©Î«ö¨ä¥LÁäµ²§ô¡G ");
+ /* itoc.010127: ­×¥¿¦b°»´ú¥ª¥kÁä¥þ§Î¤U¡A«ö¥ªÁä·|¸õÂ÷¤G¼h¿ï³æªº°ÝÃD */
+
+ if (vkey() != ' ')
+ break;
+ }
+ }
+
+ close(fd);
+ return 0;
+}
+
+
+static int
+pad_draw()
+{
+ int i, cc, fdr, color;
+ FILE *fpw;
+ Pad pad;
+ char *str, buf[3][71];
+
+ /* itoc.µù¸Ñ: ¤£·Q¥Î°ª±m«×¡A¤Óªá */
+ static char pcolors[6] = {31, 32, 33, 34, 35, 36};
+
+ /* itoc.010309: ¯d¨¥ªO´£¨Ñ¤£¦PªºÃC¦â */
+ color = vans("¤ß±¡ÃC¦â 1) \033[41m \033[m 2) \033[42m \033[m 3) \033[43m \033[m "
+ "4) \033[44m \033[m 5) \033[45m \033[m 6) \033[46m \033[m [Q] ");
+
+ if (color < '1' || color > '6')
+ return XEASY;
+ else
+ color -= '1';
+
+ do
+ {
+ buf[0][0] = buf[1][0] = buf[2][0] = '\0';
+ move(MENU_XPOS, 0);
+ clrtobot();
+ outs("\n½Ð¯d¨¥ (¦Ü¦h¤T¦æ)¡A«ö[Enter]µ²§ô");
+ for (i = 0; (i < 3) &&
+ vget(16 + i, 0, "¡G", buf[i], 71, DOECHO); i++);
+ cc = vans("(S)¦sÀÉÆ[½à (E)­«·s¨Ó¹L (Q)ºâ¤F¡H[S] ");
+ if (cc == 'q' || i == 0)
+ return 0;
+ } while (cc == 'e');
+
+ time(&pad.tpad);
+
+ /* itoc.020812.µù¸Ñ: §ïª©­±ªº®É­Ô­nª`·N struct Pad.msg[] ¬O§_°÷¤j */
+ str = pad.msg;
+ sprintf(str, "¢~¢t\033[1;46m %s ¡Ð %s \033[m¢u", cuser.userid, cuser.username);
+
+ for (cc = strlen(str); cc < 60; cc += 2)
+ strcpy(str + cc, "¢w");
+ if (cc == 60)
+ str[cc++] = ' ';
+
+ sprintf(str + cc,
+ "\033[1;44m %s \033[m¢¡\n"
+ "¢x \033[1;%dm%-70s\033[m ¢x\n"
+ "¢x \033[1;%dm%-70s\033[m ¢x\n"
+ "¢¢ \033[1;%dm%-70s\033[m ¢£\n",
+ Btime(&pad.tpad),
+ pcolors[color], buf[0],
+ pcolors[color], buf[1],
+ pcolors[color], buf[2]);
+
+ f_cat(FN_RUN_NOTE_ALL, str);
+
+ if (!(fpw = fopen(FN_RUN_NOTE_TMP, "w")))
+ return 0;
+
+ fwrite(&pad, sizeof(pad), 1, fpw);
+
+ if ((fdr = open(FN_RUN_NOTE_PAD, O_RDONLY)) >= 0)
+ {
+ Pad *pp;
+
+ i = 0;
+ cc = pad.tpad - NOTE_DUE * 60 * 60;
+ mgets(-1);
+ while (pp = mread(fdr, sizeof(Pad)))
+ {
+ fwrite(pp, sizeof(Pad), 1, fpw);
+ if ((++i > NOTE_MAX) || (pp->tpad < cc))
+ break;
+ }
+ close(fdr);
+ }
+
+ fclose(fpw);
+
+ rename(FN_RUN_NOTE_TMP, FN_RUN_NOTE_PAD);
+ pad_view();
+ return 0;
+}
+
+
+static int
+goodbye()
+{
+ /* itoc.010803: ¨q±iÂ÷¯¸ªº¹Ï */
+ clear();
+ film_out(FILM_GOODBYE, 0);
+
+ switch (vans("G)ÀH­·¦Ó³u M)³ø§i¯¸ªø N)¯d¨¥ªO Q)¨ú®ø¡H[Q] "))
+ {
+ /* lkchu.990428: ¤º©w§ï¬°¤£Â÷¯¸ */
+ case 'g':
+ case 'y':
+ break;
+
+ case 'm':
+ m_sysop();
+ break;
+
+ case 'n':
+ /* if (cuser.userlevel) */
+ if (HAS_PERM(PERM_POST)) /* Thor.990118: ­n¯àpost¤~¯à¯d¨¥, ´£°ªªùÂe */
+ pad_draw();
+ break;
+
+ case 'q':
+ default:
+ /* return XEASY; */
+ return 0; /* itoc.010803: ¨q¤F FILM_GOODBYE ­n­«Ã¸ */
+ }
+
+#ifdef LOG_BMW
+ bmw_log(); /* lkchu.981201: ¤ô²y°O¿ý³B²z */
+#endif
+
+ if (!(cuser.ufo & UFO_MOTD)) /* itoc.000407: Â÷¯¸µe­±¤@¨Ö²¤Æ */
+ {
+ clear();
+ prints("¿Ë·Rªº \033[32m%s(%s)\033[m¡A§O§Ñ¤F¦A«×¥úÁ{¡i %s ¡j\n"
+ "¥H¤U¬O±z¦b¯¸¤ºªºµù¥U¸ê®Æ¡G\n",
+ cuser.userid, cuser.username, str_site);
+ acct_show(&cuser, 0);
+ vmsg(NULL);
+ }
+
+ u_exit("EXIT ");
+ exit(0);
+}
+
+
+/* ----------------------------------------------------- */
+/* help & menu processring */
+/* ----------------------------------------------------- */
+
+
+void
+vs_head(title, mid)
+ char *title, *mid;
+{
+ char buf[40], ttl[60];
+ static char fcheck[64];
+ int spc, len;
+
+ if (mid) /* xxxx_head() ³£¬O¥Î vs_head(title, str_site); */
+ {
+ clear();
+ }
+ else /* menu() ¤¤¤~¥Î vs_head(title, NULL); ¿ï³æ¤¤µL»Ý clear() */
+ {
+ move(0, 0);
+ clrtoeol();
+ mid = str_site;
+ }
+
+ len = d_cols + 69 - strlen(title) - strlen(currboard);
+
+ if (!fcheck[0])
+ usr_fpath(fcheck, cuser.userid, FN_PAYCHECK);
+
+ if (HAS_STATUS(STATUS_BIFF))
+ {
+ mid = "\033[5;41m ¶l®t¨Ó«ö¹a¤F \033[m";
+ spc = 14;
+ }
+ else if (dashf(fcheck))
+ {
+ mid = "\033[5;43m ±z¦³·s¤ä²¼³á \033[m";
+ spc = 14;
+ }
+ else if (HAS_PERM(PERM_ALLREG) &&
+ rec_num(FN_RUN_RFORM, sizeof(RFORM)))
+ {
+ mid = "\033[5;41m ¦³¤H¶ñµù¥U³æ \033[m";
+ spc = 14;
+ }
+ else
+ {
+ if ((spc = strlen(mid)) > len)
+ {
+ spc = len;
+ memcpy(ttl, mid, spc);
+ mid = ttl;
+ mid[spc] = '\0';
+ }
+ }
+
+ spc = 2 + len - spc;
+ len = 1 - spc & 1;
+ memset(buf, ' ', spc >>= 1);
+ buf[spc] = '\0';
+
+#ifdef COLOR_HEADER
+ spc = (time(0) % 7) + '1';
+ prints("\033[1;4%cm¡i%s¡j%s\033[33m%s\033[1;37;4%cm%s¡m%s¡n\033[m\n",
+ spc, title, buf, mid, spc, buf + len, currboard);
+#else
+ prints("\033[1;42m¡i%s¡j%s\033[33m%s\033[1;37;42m%s¡m%s¡n\033[m\n",
+ title, buf, mid, buf + len, currboard);
+#endif
+}
+
+
+/* ------------------------------------- */
+/* °Êµe³B²z */
+/* ------------------------------------- */
+
+
+static char feeter[160];
+
+
+/* itoc.010403: §â feeter ªº status ¿W¥ß¥X¨Ó¡A¹w³Æ¨Ñ¨ä¥L function ¥s¥Î */
+
+static void
+status_foot()
+{
+ static int orig_flag = -1;
+ static time_t uptime = -1;
+ static int orig_money = -1;
+ static int orig_gold = -1;
+ static char flagmsg[16];
+ static char coinmsg[20];
+
+ int ufo;
+ time_t now;
+
+ ufo = cuser.ufo;
+ time(&now);
+
+ /* Thor: ¦P®É Åã¥Ü ©I¥s¾¹ ¤W¯¸³qª¾ Áô¨­ */
+
+#ifdef HAVE_ALOHA
+ ufo &= UFO_PAGER | UFO_ALOHA | UFO_CLOAK | UFO_QUIET;
+ if (orig_flag != ufo)
+ {
+ orig_flag = ufo;
+ sprintf(flagmsg,
+ "%s%s%s%s",
+ (ufo & UFO_PAGER) ? "Ãö" : "¶}",
+ (ufo & UFO_ALOHA) ? "¤W" : " ",
+ (ufo & UFO_QUIET) ? "ÀR" : " ",
+ (ufo & UFO_CLOAK) ? "Áô" : " ");
+ }
+#else
+ ufo &= UFO_PAGER | UFO_CLOAK | UFO_QUIET;
+ if (orig_flag != ufo)
+ {
+ orig_flag = ufo;
+ sprintf(flagmsg,
+ "%s%s%s ",
+ (ufo & UFO_PAGER) ? "Ãö" : "¶}",
+ (ufo & UFO_QUIET) ? "ÀR" : " ",
+ (ufo & UFO_CLOAK) ? "Áô" : " ");
+ }
+#endif
+
+ if (now > uptime) /* ¹L¤F¤l©]­n§ó·s¥Í¤éºX¼Ð */
+ {
+ struct tm *ptime;
+
+ ptime = localtime(&now);
+
+ if (cuser.day == ptime->tm_mday && cuser.month == ptime->tm_mon + 1)
+ cutmp->status |= STATUS_BIRTHDAY;
+ else
+ cutmp->status &= ~STATUS_BIRTHDAY;
+
+ uptime = now + 86400 - ptime->tm_hour * 3600 - ptime->tm_min * 60 - ptime->tm_sec;
+ }
+
+ if (cuser.money != orig_money)
+ {
+ orig_money = cuser.money;
+ sprintf(coinmsg, "»È%4d%c",
+ (orig_money & 0x7FF00000) ? (orig_money >> 20) : (orig_money & 0x7FFFFC00) ? (orig_money >> 10) : orig_money,
+ (orig_money & 0x7FF00000) ? 'M' : (orig_money & 0x7FFFFC00) ? 'K' : ' ');
+ coinmsg[7] = ' ';
+ }
+ if (cuser.gold != orig_gold)
+ {
+ orig_gold = cuser.gold;
+ sprintf(coinmsg + 8, "ª÷%4d%c ",
+ (orig_gold & 0x7FF00000) ? (orig_gold >> 20) : (orig_gold & 0x7FFFFC00) ? (orig_gold >> 10) : orig_gold,
+ (orig_gold & 0x7FF00000) ? 'M' : (orig_gold & 0x7FFFFC00) ? 'K' : ' ');
+ }
+
+ /* Thor.980913.µù¸Ñ: ³Ì±`¨£©I¥s status_foot() ªº®É¾÷¬O¨C¦¸§ó·s film¡A¦b 60 ¬í¥H¤W¡A
+ ¬G¤£»Ý°w¹ï hh:mm ¨Ó¯S§O§@¤@¦r¦êÀx¦s¥H¥[³t */
+
+ ufo = (now - (uptime - 86400)) / 60; /* ­É¥Î ufo ¨Ó°µ®É¶¡(¤À) */
+
+ /* itoc.010717: §ï¤@¤U feeter ¨Ïªø«×©M FEETER_XXX ¤@­P */
+ sprintf(feeter, COLOR1 " %8.8s %02d:%02d " COLOR2 " ¤H¼Æ %-4d §Ú¬O %-12s %s [©I¥s]%-9s ",
+ fshm->today, ufo / 60, ufo % 60, total_user, cuser.userid, coinmsg, flagmsg);
+ outf(feeter);
+}
+
+
+void
+movie()
+{
+ /* Thor: it is depend on which state */
+
+ if ((bbsmode <= M_XMENU) && (cuser.ufo & UFO_MOVIE))
+ film_out(FILM_MOVIE, MENU_XNOTE);
+
+ /* itoc.010403: §â feeter ªº status ¿W¥ß¥X¨Ó */
+ status_foot();
+}
+
+
+typedef struct
+{
+ void *func;
+ /* int (*func) (); */
+ usint level;
+ int umode;
+ char *desc;
+} MENU;
+
+
+#define MENU_LOAD 1
+#define MENU_DRAW 2
+#define MENU_FILM 4
+
+
+#define PERM_MENU PERM_PURGE
+
+
+static MENU menu_main[];
+
+
+/* ----------------------------------------------------- */
+/* administrator's maintain menu */
+/* ----------------------------------------------------- */
+
+
+static MENU menu_admin[] =
+{
+ "bin/admutil.so:a_user", PERM_ALLACCT, - M_SYSTEM,
+ "User ¢« ÅU«È¸ê®Æ ¢¨",
+
+ "bin/admutil.so:a_search", PERM_ALLACCT, - M_SYSTEM,
+ "Hunt ¢« ·j´M¤ÞÀº ¢¨",
+
+ "bin/admutil.so:a_editbrd", PERM_ALLBOARD, - M_SYSTEM,
+ "QSetBoard ¢« ³]©w¬ÝªO ¢¨",
+
+ "bin/innbbs.so:a_innbbs", PERM_ALLBOARD, - M_SYSTEM,
+ "InnBBS ¢« Âà«H³]©w ¢¨",
+
+#ifdef HAVE_REGISTER_FORM
+ "bin/admutil.so:a_register", PERM_ALLREG, - M_SYSTEM,
+ "Register ¢« ¼fµù¥U³æ ¢¨",
+
+ "bin/admutil.so:a_regmerge", PERM_ALLREG, - M_SYSTEM,
+ "Merge ¢« ´_­ì¼f®Ö ¢¨",
+#endif
+
+ "bin/admutil.so:a_xfile", PERM_SYSOP, - M_XFILES,
+ "Xfile ¢« ¨t²ÎÀÉ®× ¢¨",
+
+ "bin/admutil.so:a_resetsys", PERM_ALLADMIN, - M_SYSTEM,
+ "BBSreset ¢« ­«¸m¨t²Î ¢¨",
+
+ "bin/admutil.so:a_restore", PERM_SYSOP, - M_SYSTEM,
+ "TRestore ¢« ÁÙ­ì³Æ¥÷ ¢¨",
+
+ "bin/admutil.so:a_sysload", PERM_ALLADMIN, -M_SYSTEM,
+ "Load ¢« ¨t²Î­t¸ü ¢¨",
+
+ menu_main, PERM_MENU + Ctrl('A'), M_AMENU,
+ "¨t²ÎºûÅ@"
+};
+
+
+/* ----------------------------------------------------- */
+/* mail menu */
+/* ----------------------------------------------------- */
+
+
+static int
+XoMbox()
+{
+ xover(XZ_MBOX);
+ return 0;
+}
+
+
+static MENU menu_mail[] =
+{
+ XoMbox, PERM_BASIC, M_RMAIL,
+ "Read ¢u ¾\\Ū«H¥ó ¢t",
+
+ m_send, PERM_LOCAL, M_SMAIL,
+ "Mail ¢u ¯¸¤º±H«H ¢t",
+
+#ifdef MULTI_MAIL /* Thor.981009: ¨¾¤î·R±¡©¯¹B«H */
+ m_list, PERM_LOCAL, M_SMAIL,
+ "List ¢u ¸s²Õ±H«H ¢t",
+#endif
+
+ m_internet, PERM_INTERNET, M_SMAIL,
+ "Internet ¢u ±H¨Ì©f¨à ¢t",
+
+#ifdef HAVE_SIGNED_MAIL
+ m_verify, 0, M_XMODE,
+ "Verify ¢u ÅçÃÒ«H¥ó ¢t",
+#endif
+
+#ifdef HAVE_MAIL_ZIP
+ m_zip, PERM_INTERNET, M_SMAIL,
+ "Zip ¢u ¥´¥]¸ê®Æ ¢t",
+#endif
+
+ m_sysop, 0, M_SMAIL,
+ "Yes Sir! ¢u §ë®Ñ¯¸ªø ¢t",
+
+ "bin/admutil.so:m_bm", PERM_ALLADMIN, - M_SMAIL,
+ "BM All ¢u ªO¥D³q§i ¢t", /* itoc.000512: ·s¼W m_bm */
+
+ "bin/admutil.so:m_all", PERM_ALLADMIN, - M_SMAIL,
+ "User All ¢u ¥þ¯¸³q§i ¢t", /* itoc.000512: ·s¼W m_all */
+
+ menu_main, PERM_MENU + Ctrl('A'), M_MMENU, /* itoc.020829: ©È guest ¨S¿ï¶µ */
+ "¹q¤l¶l¥ó"
+};
+
+
+/* ----------------------------------------------------- */
+/* talk menu */
+/* ----------------------------------------------------- */
+
+
+static int
+XoUlist()
+{
+ xover(XZ_ULIST);
+ return 0;
+}
+
+
+static MENU menu_talk[];
+
+
+ /* --------------------------------------------------- */
+ /* list menu */
+ /* --------------------------------------------------- */
+
+static MENU menu_list[] =
+{
+ t_pal, PERM_BASIC, M_PAL,
+ "Pal ¡÷ ªB¤Í¦W³æ ¡ö",
+
+#ifdef HAVE_LIST
+ t_list, PERM_BASIC, M_PAL,
+ "List ¡÷ ¯S§O¦W³æ ¡ö",
+#endif
+
+#ifdef HAVE_ALOHA
+ "bin/aloha.so:t_aloha", PERM_PAGE, - M_PAL,
+ "Aloha ¡÷ ¤W¯¸³qª¾ ¡ö",
+#endif
+
+#ifdef LOGIN_NOTIFY
+ t_loginNotify, PERM_PAGE, M_PAL,
+ "Notify ¡÷ ¨t²Î¨ó´M ¡ö",
+#endif
+
+ menu_talk, PERM_MENU + 'P', M_TMENU,
+ "¦UÃþ¦W³æ"
+};
+
+
+static MENU menu_talk[] =
+{
+ XoUlist, 0, M_LUSERS,
+ "Users ¡÷ ¹C«È¦W³æ ¡ö",
+
+ menu_list, PERM_BASIC, M_TMENU,
+ "ListMenu ¡÷ ³]©w¦W³æ ¡ö",
+
+ t_pager, PERM_BASIC, M_XMODE,
+ "Pager ¡÷ ¤Á´«©I¥s ¡ö",
+
+ t_cloak, PERM_CLOAK, M_XMODE,
+ "Invis ¡÷ Áô¨­±Kªk ¡ö",
+
+ t_query, 0, M_QUERY,
+ "Query ¡÷ ¬d¸ßºô¤Í ¡ö",
+
+ t_talk, PERM_PAGE, M_PAGE,
+ "Talk ¡÷ ±¡¸Üºøºø ¡ö",
+
+ /* Thor.990220: §ï±Ä¥~±¾ */
+ "bin/chat.so:t_chat", PERM_CHAT, - M_CHAT,
+ "ChatRoom ¡÷ ²³¤fÅàª÷ ¡ö",
+
+ t_display, PERM_BASIC, M_BMW,
+ "Display ¡÷ ÂsÄý¤ô²y ¡ö",
+
+ t_bmw, PERM_BASIC, M_BMW,
+ "Write ¡÷ ¦^ÅU¤ô²y ¡ö",
+
+ menu_main, PERM_MENU + 'U', M_TMENU,
+ "¥ð¶¢²á¤Ñ"
+};
+
+
+/* ----------------------------------------------------- */
+/* user menu */
+/* ----------------------------------------------------- */
+
+
+static MENU menu_user[];
+
+
+ /* --------------------------------------------------- */
+ /* register menu */
+ /* --------------------------------------------------- */
+
+static MENU menu_register[] =
+{
+// u_addr, PERM_BASIC, M_XMODE,
+// "Address ¡m ¹q¤l«H½c ¡n",
+
+
+#ifdef HAVE_REGISTER_FORM
+ u_register, PERM_BASIC, M_UFILES,
+ "Register ¡m ¶ñµù¥U³æ ¡n",
+#endif
+
+#ifdef HAVE_REGKEY_CHECK
+// u_verify, PERM_BASIC, M_UFILES,
+// "Verify ¡m ¶ñ»{ÃÒ½X ¡n",
+#endif
+
+ u_deny, PERM_BASIC, M_XMODE,
+ "Perm ¡m «ì´_Åv­­ ¡n",
+
+ menu_user, PERM_MENU + 'R', M_UMENU,
+ "µù¥U¿ï³æ"
+};
+
+
+static MENU menu_user[] =
+{
+// "bin/dict.so:main_yahoo", 0, -M_XMODE,
+// "Dictionary ¡m ­^º~¦r¨å ¡n",
+
+ u_info, PERM_BASIC, M_XMODE,
+ "Info ¡m ­Ó¤H¸ê®Æ ¡n",
+
+ u_setup, 0, M_UFILES,
+ "Habit ¡m ³ß¦n¼Ò¦¡ ¡n",
+
+ menu_register, PERM_BASIC, M_UMENU,
+ "Register ¡m µù¥U¿ï³æ ¡n",
+
+ pad_view, 0, M_READA,
+ "Note ¡m Æ[¬Ý¯d¨¥ ¡n",
+
+ /* itoc.010309: ¤£¥²Â÷¯¸¥i¥H¼g¯d¨¥ªO */
+ pad_draw, PERM_POST, M_POST,
+ "Pad ¡m ¤ß±¡¶î¾~ ¡n",
+
+ u_lock, PERM_BASIC, M_IDLE,
+ "Lock ¡m Âê©w¿Ã¹õ ¡n",
+
+ u_xfile, PERM_BASIC, M_UFILES,
+ "Xfile ¡m ­Ó¤HÀÉ®× ¡n",
+
+ u_log, PERM_BASIC, M_UFILES,
+ "ViewLog ¡m ¤W¯¸°O¿ý ¡n",
+
+ menu_main, PERM_MENU + 'H', M_UMENU,
+ "­Ó¤H³]©w"
+};
+
+
+#ifdef HAVE_EXTERNAL
+
+/* ----------------------------------------------------- */
+/* tool menu */
+/* ----------------------------------------------------- */
+
+
+static MENU menu_tool[];
+
+
+#ifdef HAVE_SONG
+ /* --------------------------------------------------- */
+ /* song menu */
+ /* --------------------------------------------------- */
+
+static MENU menu_song[] =
+{
+ "bin/song.so:XoSongLog", 0, - M_XMODE,
+ "KTV ¡ñ ÂIºq¬ö¿ý ¡ð",
+
+ "bin/song.so:XoSongMain", 0, - M_XMODE,
+ "Book ¡ñ °Û©Ò±ý¨¥ ¡ð",
+
+ "bin/song.so:XoSongSub", 0, - M_XMODE,
+ "Note ¡ñ ºq¥»§ë½Z ¡ð",
+
+ menu_tool, PERM_MENU + 'K', M_XMENU,
+ "ª±ÂIºq¾÷"
+};
+#endif
+
+
+#ifdef HAVE_GAME
+
+#if 0
+
+ itoc.010426.µù¸Ñ:
+ ¯q´¼¹CÀ¸¤£¥Î½äª÷¨î«×¡AÅýª±®aª±¦nª±ªº¡A¥u¥[¿ú¡A¤£´î¿ú¡C
+
+ itoc.010714.µù¸Ñ:
+ (a) ¨C¦¸ª±¹CÀ¸ªºÁ`´Á±æ­ÈÀ³¦b 1.01¡A¤@­Ó±ß¤W¬ù¥iª± 100 ¦¸¹CÀ¸¡A
+ ­Y±NÁ`°]²£§ë¤J¥hª±¹CÀ¸¡A«h 1.01^100 = 2.7 ­¿/¨Cª±¤@­Ó±ß¤W¡C
+ (b) ­Y¦U¶µ¾÷²v¤£§¡µ¥¡A¤]À³ºû«ù¦b 1.0 ~ 1.02 ¤§¶¡¡AÅýª±®a¤@©w¯àÁÈ¿ú¡A
+ ¥B­Y¤@ª½©ã³Ì°ª´Á±æ­Èªº¨º¤@¶µ¡A¤]¤£·|Áȱo¹L©óÂ÷ÃСC
+ (c) ­ì«h¤W¡A¾÷²v¶V§CªÌ¨ä´Á±æ­ÈÀ³¬° 1.02¡A¾÷²v¸û°ªªÌ¨ä´Á±æ­ÈÀ³¬° 1.01¡C
+
+ itoc.011011.µù¸Ñ:
+ ¬°¤FÁ×§K user multi-login ª±¨Ó¬~¿ú¡A
+ ©Ò¥H¦bª±¹CÀ¸ªº¶}©l´N­nÀˬd¬O§_­«ÂÐ login §Y if (HAS_STATUS(STATUS_COINLOCK))¡C
+
+#endif
+
+ /* --------------------------------------------------- */
+ /* game menu */
+ /* --------------------------------------------------- */
+
+static MENU menu_game[];
+
+static MENU menu_game1[] =
+{
+ "bin/liteon.so:main_liteon", 0, - M_GAME,
+ "0LightOn ¡ñ ©Ð¶¡¶}¿O ¡ð",
+
+ "bin/guessnum.so:guessNum", 0, - M_GAME,
+ "1GuessNum ¡ñ ª±²q¼Æ¦r ¡ð",
+
+ "bin/guessnum.so:fightNum", 0, - M_GAME,
+ "2FightNum ¡ñ ¤¬²q¼Æ¦r ¡ð",
+
+ "bin/km.so:main_km", 0, - M_GAME,
+ "3KongMing ¡ñ ¤Õ©ú´ÑÃÐ ¡ð",
+
+ "bin/recall.so:main_recall", 0, - M_GAME,
+ "4Recall ¡ñ ¦^¾Ð¤§§Z ¡ð",
+
+ "bin/mine.so:main_mine", 0, - M_GAME,
+ "5Mine ¡ñ ¶Ã½ò¦a¹p ¡ð",
+
+ "bin/fantan.so:main_fantan", 0, - M_GAME,
+ "6Fantan ¡ñ µfÅu±µÀs ¡ð",
+
+ "bin/dragon.so:main_dragon", 0, - M_GAME,
+ "7Dragon ¡ñ ±µÀs¹CÀ¸ ¡ð",
+
+ "bin/nine.so:main_nine", 0, - M_GAME,
+ "8Nine ¡ñ ¤Ñ¦a¤E¤E ¡ð",
+
+ menu_game, PERM_MENU + '0', M_XMENU,
+ "¯q´¼ªÅ¶¡"
+};
+
+static MENU menu_game2[] =
+{
+ "bin/dice.so:main_dice", 0, - M_GAME,
+ "0Dice ¡ñ ¨gÂY»ë¤l ¡ð",
+
+ "bin/gp.so:main_gp", 0, - M_GAME,
+ "1GoldPoker ¡ñ ª÷µP¼³§J ¡ð",
+
+ "bin/bj.so:main_bj", 0, - M_GAME,
+ "2BlackJack ¡ñ ¤G¤Q¤@ÂI ¡ð",
+
+ "bin/chessmj.so:main_chessmj", 0, - M_GAME,
+ "3ChessMJ ¡ñ ¶H´Ñ³Â±N ¡ð",
+
+ "bin/seven.so:main_seven", 0, - M_GAME,
+ "4Seven ¡ñ ½ä«°¤C±i ¡ð",
+
+ "bin/race.so:main_race", 0, - M_GAME,
+ "5Race ¡ñ ¶iÁɰ¨³õ ¡ð",
+
+ "bin/bingo.so:main_bingo", 0, - M_GAME,
+ "6Bingo ¡ñ »«ªG¤j¾Ô ¡ð",
+
+ "bin/marie.so:main_marie", 0, - M_GAME,
+ "7Marie ¡ñ ¤j¤pº¿²ú ¡ð",
+
+ "bin/bar.so:main_bar", 0, - M_GAME,
+ "8Bar ¡ñ §a¥xº¿²ú ¡ð",
+
+ menu_game, PERM_MENU + '0', M_XMENU,
+ "¹CÀ¸¼Ö¶é"
+};
+
+static MENU menu_game3[] =
+{
+ "bin/pip.so:main_pip", PERM_BASIC, - M_GAME,
+ "0Chicken ¡ñ ¹q¤l¤pÂû ¡ð",
+
+ "bin/pushbox.so:main_pushbox", 0, - M_GAME,
+ "1PushBox ¡ñ ­Ü®wµfµf ¡ð",
+
+ "bin/tetris.so:main_tetris", 0, - M_GAME,
+ "2Tetris ¡ñ «Xù´µ¶ô ¡ð",
+
+ "bin/gray.so:main_gray", 0, - M_GAME,
+ "3Gray ¡ñ ²L¦Ç¤j¾Ô ¡ð",
+
+ menu_game, PERM_MENU + '0', M_XMENU,
+ "¤Ï¤æ¯S°Ï"
+};
+
+static MENU menu_game[] =
+{
+ menu_game1, PERM_BASIC, M_XMENU,
+ "1Game ¡i ¯q´¼¤Ñ°ó ¡j",
+
+ menu_game2, PERM_BASIC, M_XMENU,
+ "2Game ¡i ¹CÀ¸¼Ö¶é ¡j",
+
+ menu_game3, PERM_BASIC, M_XMENU,
+ "3Game ¡i ¤Ï¤æ¯S°Ï ¡j",
+
+ menu_tool, PERM_MENU + '1', M_XMENU,
+ "¹CÀ¸¤H¥Í"
+};
+#endif
+
+
+#ifdef HAVE_BUY
+ /* --------------------------------------------------- */
+ /* buy menu */
+ /* --------------------------------------------------- */
+
+static MENU menu_buy[] =
+{
+ "bin/bank.so:x_bank", PERM_BASIC, - M_GAME,
+ "Bank ¡ñ «H°U»È¦æ ¡ð",
+
+ "bin/bank.so:b_invis", PERM_BASIC, - M_GAME,
+ "Invis ¡ñ Áô§Î²{¨­ ¡ð",
+
+// "bin/bank.so:b_cloak", PERM_BASIC, - M_GAME,
+// "Cloak ¡ñ µL­­Áô§Î ¡ð",
+
+ "bin/bank.so:b_changeid", PERM_BASIC, - M_GAME,
+ "ModifyID ¡ñ ¥i¥H§ïID ¡ð",
+
+
+ "bin/bank.so:b_mbox", PERM_BASIC, - M_GAME,
+ "Mbox ¡ñ «H½cµL­­ ¡ð",
+
+ "bin/bank.so:b_xempt", PERM_BASIC, - M_GAME,
+ "Xempt ¡ñ ¥Ã¤[«O¯d ¡ð",
+
+ menu_tool, PERM_MENU + 'B', M_XMENU,
+ "ª÷¿Ä¥«³õ"
+};
+#endif
+
+
+ /* --------------------------------------------------- */
+ /* other tools menu */
+ /* --------------------------------------------------- */
+
+static MENU menu_other[] =
+{
+ "bin/vote.so:vote_all", PERM_BASIC, - M_VOTE, /* itoc.010414: §ë²¼¤¤¤ß */
+ "VoteAll ¡ñ §ë²¼¤¤¤ß ¡ð",
+
+#ifdef HAVE_TIP
+ "bin/xyz.so:x_tip", 0, - M_READA,
+ "Tip ¡ñ ±Ð¾ÇºëÆF ¡ð",
+#endif
+
+#ifdef HAVE_LOVELETTER
+ "bin/xyz.so:x_loveletter", 0, - M_READA,
+ "LoveLetter ¡ñ ±¡®Ñ¼¶¼g ¡ð",
+#endif
+
+ "bin/xyz.so:x_password", PERM_VALID, - M_XMODE,
+ "Password ¡ñ §Ñ°O±K½X ¡ð",
+
+#ifdef HAVE_CLASSTABLE
+ "bin/classtable.so:main_classtable", PERM_BASIC, - M_XMODE,
+ "ClassTable ¡ñ ¥\\½Ò®É¬q ¡ð",
+#endif
+
+#ifdef HAVE_CREDIT
+ "bin/credit.so:main_credit", PERM_BASIC, - M_XMODE,
+ "MoneyNote ¡ñ °O±b¤â¥¾ ¡ð",
+#endif
+
+#ifdef HAVE_CALENDAR
+ "bin/todo.so:main_todo", PERM_BASIC, - M_XMODE,
+ "XTodo ¡ñ ­Ó¤H¦æµ{ ¡ð",
+
+ "bin/calendar.so:main_calendar", 0, - M_XMODE,
+ "YCalendar ¡ñ ¸U¦~¤ë¾ä ¡ð",
+#endif
+
+ menu_tool, PERM_MENU + Ctrl('A'), M_XMENU, /* itoc.020829: ©È guest ¨S¿ï¶µ */
+ "¨ä¥L¥\\¯à"
+};
+
+
+static MENU menu_tool[] =
+{
+#ifdef HAVE_SONG
+ menu_song, 0, M_XMENU,
+ "KTV ¡i ¯u±¡ÂIºq ¡j",
+#endif
+
+#ifdef HAVE_COSIGN
+ "bin/newbrd.so:XoNewBoard", PERM_VALID, - M_XMODE,
+ "Join ¡i ¬ÝªO³s¸p ¡j",
+#endif
+
+#ifdef HAVE_GAME
+ menu_game, PERM_BASIC, M_XMENU,
+ "Game ¡i ¹CÀ¸¤H¥Í ¡j",
+#endif
+
+#ifdef HAVE_BUY
+ menu_buy, PERM_BASIC, M_XMENU,
+ "Market ¡i ª÷¿Ä¥«³õ ¡j",
+#endif
+
+ menu_other, 0, M_XMENU,
+ "Other ¡i Âø¤CÂø¤K ¡j",
+
+ menu_main, PERM_MENU + Ctrl('A'), M_XMENU, /* itoc.020829: ©È guest ¨S¿ï¶µ */
+ "­Ó¤H¤u¨ã"
+};
+
+#endif /* HAVE_EXTERNAL */
+
+
+/* ----------------------------------------------------- */
+/* main menu */
+/* ----------------------------------------------------- */
+
+
+static int
+Gem()
+{
+ /* itoc.001109: ¬ÝªOÁ`ºÞ¦b (A)nnounce ¤U¦³ GEM_X_BIT¡A¤è«K¶}ªO */
+ XoGem("gem/"FN_DIR, "ºëµØ§G§iÄæ", (HAS_PERM(PERM_ALLBOARD) ? (GEM_W_BIT | GEM_X_BIT | GEM_M_BIT) : 0));
+ return 0;
+}
+
+
+static MENU menu_main[] =
+{
+ menu_admin, PERM_ALLADMIN, M_AMENU,
+ "0Admin £X ¨t²ÎºûÅ@°Ï £X",
+
+ Gem, 0, M_GEM,
+ "Announce £i ºëµØ¤½§GÄæ £i",
+
+ Boards, 0, M_BOARD,
+ "Boards £[ §G§i°Q½×°Ï £[",
+
+ Class, 0, M_BOARD,
+ "Class £p ¤À²Õ°Q½×¶° £p",
+
+#ifdef MY_FAVORITE
+ MyFavorite, PERM_BASIC, M_MF,
+ "Favorite £b §Úªº³Ì·R¸s £b",
+#endif
+
+ menu_mail, 0, M_MMENU,
+ "Mail £g «H¥ó¨åÂò° £g",
+
+ menu_talk, 0, M_TMENU,
+ "Talk £s ¥ð¶¢²á¤Ñ¦a £s",
+
+ menu_user, 0, M_UMENU,
+ "User £k ­Ó¤H¤u¨ã§{ £k",
+
+#ifdef HAVE_EXTERNAL
+ menu_tool, 0, M_XMENU,
+ "Xyz £c ¯S®í©Û«Ý©Ò £c",
+#endif
+
+#if 0 /* itoc.010209: ¿ï³æ«ö s ª½±µ¶i¤J Select() ´î¤Ö¿ï³æªø«× */
+ Select, 0, M_BOARD,
+ "Select £m ¿ï¾Ü¥D¬ÝªO £m",
+#endif
+
+ goodbye, 0, M_XMODE,
+ "Goodbye £_ ¤U¦¸¦A·|§a £_",
+
+ NULL, PERM_MENU + 'B', M_0MENU,
+ "¥D¥\\¯àªí"
+};
+
+
+void
+menu()
+{
+ MENU *menu, *mptr, *table[12];
+ usint level, mode;
+ int cc, cx; /* current / previous cursor position */
+ int max, mmx; /* current / previous menu max */
+ int cmd, depth;
+ char *str;
+
+ mode = MENU_LOAD | MENU_DRAW | MENU_FILM;
+ menu = menu_main;
+ level = cuser.userlevel;
+ depth = mmx = 0;
+
+ for (;;)
+ {
+ if (mode & MENU_LOAD)
+ {
+ for (max = -1;; menu++)
+ {
+ cc = menu->level;
+ if (cc & PERM_MENU)
+ {
+
+#ifdef MENU_VERBOSE
+ if (max < 0) /* §ä¤£¨ì¾A¦XÅv­­¤§¥\¯à¡A¦^¤W¤@¼h¥\¯àªí */
+ {
+ menu = (MENU *) menu->func;
+ continue;
+ }
+#endif
+
+ break;
+ }
+ if (cc && !(cc & level)) /* ¦³¸ÓÅv­­¤~¨q¥X */
+ continue;
+
+ table[++max] = menu;
+ }
+
+ if (mmx < max)
+ mmx = max;
+
+ if ((depth == 0) && HAS_STATUS(STATUS_BIFF)) /* ²Ä¤@¦¸¤W¯¸­Y¦³·s«H¡A¶i¤J Mail ¿ï³æ */
+ cmd = 'M';
+ else
+ cmd = cc ^ PERM_MENU; /* default command */
+ utmp_mode(menu->umode);
+ }
+
+ if (mode & MENU_DRAW)
+ {
+ if (mode & MENU_FILM)
+ {
+ clear();
+ movie();
+ cx = -1;
+ }
+
+ vs_head(menu->desc, NULL);
+
+ mode = 0;
+ do
+ {
+ move(MENU_XPOS + mode, MENU_YPOS + 2);
+ if (mode <= max)
+ {
+ mptr = table[mode];
+ str = mptr->desc;
+ prints("(\033[1;36m%c\033[m)", *str++);
+ outs(str);
+ }
+ clrtoeol();
+ } while (++mode <= mmx);
+
+ mmx = max;
+ mode = 0;
+ }
+
+ switch (cmd)
+ {
+ case KEY_DOWN:
+ cc = (cc == max) ? 0 : cc + 1;
+ break;
+
+ case KEY_UP:
+ cc = (cc == 0) ? max : cc - 1;
+ break;
+
+ case Ctrl('A'): /* itoc.020829: ¹w³]¿ï¶µ²Ä¤@­Ó */
+ case KEY_HOME:
+ cc = 0;
+ break;
+
+ case KEY_END:
+ cc = max;
+ break;
+
+ case KEY_PGUP:
+ cc = (cc == 0) ? max : 0;
+ break;
+
+ case KEY_PGDN:
+ cc = (cc == max) ? 0 : max;
+ break;
+
+ case '\n':
+ case KEY_RIGHT:
+ mptr = table[cc];
+ cmd = mptr->umode;
+#if 1
+ /* Thor.990212: dynamic load , with negative umode */
+ if (cmd < 0)
+ {
+ void *p = DL_get(mptr->func);
+ if (!p)
+ break;
+ mptr->func = p;
+ cmd = -cmd;
+ mptr->umode = cmd;
+ }
+#endif
+ utmp_mode(cmd);
+
+ if (cmd <= M_XMENU) /* ¤l¥Ø¿ýªº mode ­n <= M_XMENU */
+ {
+ menu->level = PERM_MENU + mptr->desc[0];
+ menu = (MENU *) mptr->func;
+
+ mode = MENU_LOAD | MENU_DRAW;
+ /* mode = MENU_LOAD | MENU_DRAW | MENU_FILM; /* itoc.010304: ¶i¤J¤l¿ï³æ­«¼· movie */
+
+ depth++;
+ continue;
+ }
+
+ {
+ int (*func) ();
+
+ func = mptr->func;
+ mode = (*func) ();
+ }
+
+ utmp_mode(menu->umode);
+
+ if (mode == XEASY)
+ {
+ outf(feeter);
+ mode = 0;
+ }
+ else
+ {
+ mode = MENU_DRAW | MENU_FILM;
+ }
+
+ cmd = mptr->desc[0];
+ continue;
+
+#ifdef EVERY_Z
+ case Ctrl('Z'):
+ every_Z(0);
+ goto every_key;
+
+ case Ctrl('U'):
+ every_U(0);
+ goto every_key;
+#endif
+
+ /* itoc.010911: Select everywhere¡A¤£¦A­­¨î¬O¦b M_0MENU */
+ case 's':
+ case Ctrl('S'):
+ utmp_mode(M_BOARD);
+ Select();
+ goto every_key;
+
+#ifdef MY_FAVORITE
+ /* itoc.010911: Favorite everywhere¡A¤£¦A­­¨î¬O¦b M_0MENU */
+ case 'f':
+ case Ctrl('F'):
+ if (cuser.userlevel) /* itoc.010407: ­nÀˬdÅv­­ */
+ {
+ utmp_mode(M_MF);
+ MyFavorite();
+ }
+ goto every_key;
+#endif
+
+ /* itoc.020301: Read currboard in M_0MENU */
+ case 'r':
+ if (bbsmode == M_0MENU)
+ {
+ if (currbno >= 0)
+ {
+ utmp_mode(M_BOARD);
+ XoPost(currbno);
+ xover(XZ_POST);
+#ifndef ENHANCED_VISIT
+ time(&brd_visit[currbno]);
+#endif
+ }
+ goto every_key;
+ }
+ goto default_key; /* ­Y¤£¦b M_0MENU ¤¤«ö r ªº¸Ü¡A­nµø¬°¤@¯ë«öÁä */
+
+every_key: /* ¯S®íÁä³B²zµ²§ô */
+ utmp_mode(menu->umode);
+ mode = MENU_DRAW | MENU_FILM;
+ cmd = table[cc]->desc[0];
+ continue;
+
+ case KEY_LEFT:
+ case 'e':
+ if (depth > 0)
+ {
+ menu->level = PERM_MENU + table[cc]->desc[0];
+ menu = (MENU *) menu->func;
+ mode = MENU_LOAD | MENU_DRAW;
+ /* mode = MENU_LOAD | MENU_DRAW | MENU_FILM; /* itoc.010304: °h¥X¤l¿ï³æ­«¼· movie */
+ depth--;
+ continue;
+ }
+ cmd = 'G';
+
+default_key:
+ default:
+
+ if (cmd >= 'a' && cmd <= 'z')
+ cmd ^= 0x20; /* Åܤj¼g */
+
+ cc = 0;
+ for (;;)
+ {
+ if (table[cc]->desc[0] == cmd)
+ break;
+ if (++cc > max)
+ {
+ cc = cx;
+ goto menu_key;
+ }
+ }
+ }
+
+ if (cc != cx) /* ­Y´å¼Ð²¾°Ê¦ì¸m */
+ {
+#ifdef CURSOR_BAR
+ if (cx >= 0)
+ {
+ move(MENU_XPOS + cx, MENU_YPOS);
+ if (cx <= max)
+ {
+ mptr = table[cx];
+ str = mptr->desc;
+ prints(" (\033[1;36m%c\033[m)%s ", *str, str + 1);
+ }
+ else
+ {
+ outs(" ");
+ }
+ }
+ move(MENU_XPOS + cc, MENU_YPOS);
+ mptr = table[cc];
+ str = mptr->desc;
+ prints(COLOR4 "> (%c)%s \033[m", *str, str + 1);
+ cx = cc;
+#else /* ¨S¦³ CURSOR_BAR */
+ if (cx >= 0)
+ {
+ move(MENU_XPOS + cx, MENU_YPOS);
+ outc(' ');
+ }
+ move(MENU_XPOS + cc, MENU_YPOS);
+ outc('>');
+ cx = cc;
+#endif
+ }
+ else /* ­Y´å¼Ðªº¦ì¸m¨S¦³ÅÜ */
+ {
+#ifdef CURSOR_BAR
+ move(MENU_XPOS + cc, MENU_YPOS);
+ mptr = table[cc];
+ str = mptr->desc;
+ prints(COLOR4 "> (%c)%s \033[m", *str, str + 1);
+#else
+ move(MENU_XPOS + cc, MENU_YPOS + 1);
+#endif
+ }
+
+menu_key:
+
+ cmd = vkey();
+ }
+}
diff --git a/maple/more.c b/maple/more.c
new file mode 100644
index 0000000..5f133ae
--- /dev/null
+++ b/maple/more.c
@@ -0,0 +1,835 @@
+/*-------------------------------------------------------*/
+/* more.c ( NTHU CS MapleBBS Ver 3.00 ) */
+/*-------------------------------------------------------*/
+/* target : simple & beautiful ANSI/Chinese browser */
+/* create : 95/03/29 */
+/* update : 97/03/29 */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+
+/* ----------------------------------------------------- */
+/* buffered file read */
+/* ----------------------------------------------------- */
+
+
+#define MORE_BUFSIZE 4096
+
+
+static uschar more_pool[MORE_BUFSIZE];
+static int more_base; /* more_pool[more_base ~ more_base+more_size] ¦³­È */
+static int more_size;
+
+
+/* ----------------------------------------------------- */
+/* mget ¾\Ū¤å¦rÀÉ¡Fmread ¾\Ū¤G¶i¦ìÀÉ */
+/* ----------------------------------------------------- */
+
+
+/* itoc.041226.µù¸Ñ: mgets() ©M more_line() ¤£¤@¼Ëªº³¡¤À¦³
+ 1. mgets ª½±µ¥Î more_pool ªºªÅ¶¡¡Fmore_line «h¬O·|§â­È¼g¤J¤@¶ô buffer
+ 2. mgets ¤£·|¦Û°ÊÂ_¦æ¡Fmore_line «h¬O·|¦Û°ÊÂ_¦æ¦b b_cols
+ ©Ò¥H mgets ¬O®³¥Î¦b¤@¨Ç¨t²ÎÀɳB²z©Î¬O edit.c¡A¦Ó more_line ¥u¥Î¦b more()
+ */
+
+
+char *
+mgets(fd)
+ int fd;
+{
+ char *pool, *base, *head, *tail;
+ int ch;
+
+ if (fd < 0)
+ {
+ more_base = more_size = 0;
+ return NULL;
+ }
+
+ pool = more_pool;
+ base = pool + more_base;
+ tail = pool + more_size;
+ head = base;
+
+ for (;;)
+ {
+ if (head >= tail)
+ {
+ if (ch = head - base)
+ memcpy(pool, base, ch);
+
+ head = pool + ch;
+ ch = read(fd, head, MORE_BUFSIZE - ch);
+
+ if (ch <= 0)
+ return NULL;
+
+ base = pool;
+ tail = head + ch;
+ more_size = tail - pool;
+ }
+
+ ch = *head;
+
+ if (ch == '\n')
+ {
+ *head++ = '\0';
+ more_base = head - pool;
+ return base;
+ }
+
+ head++;
+ }
+}
+
+
+/* use mgets(-1) to reset */
+
+
+void *
+mread(fd, len)
+ int fd, len;
+{
+ char *pool;
+ int base, size;
+
+ base = more_base;
+ size = more_size;
+ pool = more_pool;
+
+ if (size < len)
+ {
+ if (size)
+ memcpy(pool, pool + base, size);
+
+ base = read(fd, pool + size, MORE_BUFSIZE - size);
+
+ if (base <= 0)
+ return NULL;
+
+ size += base;
+ base = 0;
+ }
+
+ more_base = base + len;
+ more_size = size - len;
+
+ return pool + base;
+}
+
+
+/* ----------------------------------------------------- */
+/* more ¾\Ū¤å¦rÀÉ */
+/* ----------------------------------------------------- */
+
+
+#define STR_ANSICODE "[0123456789;"
+
+
+static uschar *fimage; /* file image begin */
+static uschar *fend; /* file image end */
+static uschar *foff; /* ¥Ø«eŪ¨ì­þ¸Ì */
+
+
+static int
+more_line(buf)
+ char *buf;
+{
+ int ch, len, bytes, in_ansi, in_chi;
+
+ len = bytes = in_ansi = in_chi = 0;
+
+ for (;;)
+ {
+ if (foff >= fend)
+ break;
+
+ ch = *foff;
+
+ /* weiyu.040802: ¦pªG³o½X¬O¤¤¤å¦rªº­º½X¡A¦ý¬O¥u³Ñ¤U¤@½XªºªÅ¶¡¥i¥H¦L¡A¨º»ò¤£­n¦L³o½X */
+ if (in_chi || IS_ZHC_HI(ch))
+ in_chi ^= 1;
+ if (in_chi && (len >= b_cols - 1 || bytes >= ANSILINELEN - 2))
+ break;
+
+ foff++;
+ bytes++;
+
+ if (ch == '\n')
+ break;
+
+ if (ch == KEY_ESC)
+ {
+ in_ansi = 1;
+ }
+ else if (in_ansi)
+ {
+ if (!strchr(STR_ANSICODE, ch))
+ in_ansi = 0;
+ }
+ else if (isprint2(ch))
+ {
+ len++;
+ }
+ else
+ {
+ ch = ' '; /* ¦L¥X¤£¨Óªº³£´«¦¨ªÅ¥Õ */
+ len++;
+ }
+
+ *buf++ = ch;
+
+ /* ­Y¤£§t±±¨î½Xªºªø«×¤w¹F b_cols ¦r¡A©Î§t±±¨î½Xªºªø«×¤w¹F ANSILINELEN-1¡A¨º»òÂ÷¶}°j°é */
+ if (len >= b_cols || bytes >= ANSILINELEN - 1)
+ {
+ /* itoc.031123: ¦pªG¬O±±¨î½X¡A§Y¨Ï¤£§t±±¨î½Xªºªø«×¤w¹F b_cols ¤F¡AÁÙ¥i¥HÄ~Äò¦Y */
+ if ((in_ansi || (foff < fend && *foff == KEY_ESC)) && bytes < ANSILINELEN - 1)
+ continue;
+
+ /* itoc.031123: ¦AÀˬd¤U¤@­Ó¦r¬O¤£¬O '\n'¡AÁ×§K«ê¦n¬O b_cols ©Î ANSILINELEN-1 ®É¡A·|¦h¸õ¤@¦CªÅ¥Õ */
+ if (foff < fend && *foff == '\n')
+ {
+ foff++;
+ bytes++;
+ }
+ break;
+ }
+ }
+
+ *buf = '\0';
+
+ return bytes;
+}
+
+
+static void
+outs_line(str) /* ¦L¥X¤@¯ë¤º®e */
+ char *str;
+{
+ int ch1, ch2, ansi;
+
+ /* ¡°³B²z¤Þ¥ÎªÌ & ¤Þ¨¥ */
+
+ ch1 = str[0];
+ ch2 = str[1];
+
+ if (ch2 == ' ' && (ch1 == QUOTE_CHAR1 || ch1 == QUOTE_CHAR2)) /* ¤Þ¨¥ */
+ {
+ ansi = 1;
+ ch1 = str[2];
+ outs((ch1 == QUOTE_CHAR1 || ch1 == QUOTE_CHAR2) ? "\033[33m" : "\033[36m"); /* ¤Þ¥Î¤@¼h/¤G¼h¤£¦PÃC¦â */
+ }
+ else if (ch1 == '\241' && ch2 == '\260') /* ¡° ¤Þ¨¥ªÌ */
+ {
+ ansi = 1;
+ outs("\033[1;36m");
+ }
+ else if ((ch1 == '/') && (ch2 == '/')) /* duck.050629: ¥[¤J"ª`¸Ñ"*/
+ {
+ ansi = 1;
+ outs("\033[1;30m");
+ }
+ else
+ ansi = 0;
+
+ /* ¦L¥X¤º®e */
+
+ if (!hunt[0])
+ {
+ outx(str);
+ }
+ else
+ {
+ int len;
+ char buf[ANSILINELEN];
+ char *ptr1, *ptr2;
+
+ len = strlen(hunt);
+ ptr2 = buf;
+ while (1)
+ {
+ if (!(ptr1 = str_sub(str, hunt)))
+ {
+ strcpy(ptr2, str);
+ break;
+ }
+
+ if (buf + ANSILINELEN - 1 <= ptr2 + (ptr1 - str) + (len + 7)) /* buf ªÅ¶¡¤£°÷ */
+ break;
+
+ str_ncpy(ptr2, str, ptr1 - str + 1);
+ ptr2 += ptr1 - str;
+ sprintf(ptr2, "\033[7m%.*s\033[m", len, ptr1);
+ ptr2 += len + 7;
+ str = ptr1 + len;
+ }
+
+ outx(buf);
+ }
+
+ if (ansi)
+ outs(str_ransi);
+}
+
+
+static void
+outs_header(str, header_len) /* ¦L¥XÀÉÀY */
+ char *str;
+ int header_len;
+{
+ static char header1[LINE_HEADER][LEN_AUTHOR1] = {"§@ªÌ", "¼ÐÃD", "®É¶¡"};
+ static char header2[LINE_HEADER][LEN_AUTHOR2] = {"µo«H¤H", "¼Ð ÃD", "µo«H¯¸"};
+ int i;
+ char *ptr, *word;
+
+ /* ³B²zÀÉÀY */
+
+ if ((header_len == LEN_AUTHOR1 && !memcmp(str, header1[0], LEN_AUTHOR1 - 1)) ||
+ (header_len == LEN_AUTHOR2 && !memcmp(str, header2[0], LEN_AUTHOR2 - 1)))
+ {
+ /* §@ªÌ/¬ÝªO ÀÉÀY¦³¤GÄæ¡A¯S§O³B²z */
+ word = str + header_len;
+ if ((ptr = strstr(word, str_post1)) || (ptr = strstr(word, str_post2)))
+ {
+ ptr[-1] = ptr[4] = '\0';
+ prints(COLOR5 " %s " COLOR6 "%-*.*s" COLOR5 " %s " COLOR6 "%-13s\033[m",
+ header1[0], d_cols + 53, d_cols + 53, word, ptr, ptr + 5);
+ }
+ else
+ {
+ /* ¤Ö¬ÝªO³oÄæ */
+ prints(COLOR5 " %s " COLOR6 "%-*.*s\033[m",
+ header1[0], d_cols + 72, d_cols + 72, word);
+ }
+ return;
+ }
+
+ for (i = 1; i < LINE_HEADER; i++)
+ {
+ if ((header_len == LEN_AUTHOR1 && !memcmp(str, header1[i], LEN_AUTHOR1 - 1)) ||
+ (header_len == LEN_AUTHOR2 && !memcmp(str, header2[i], LEN_AUTHOR2 - 1)))
+ {
+ /* ¨ä¥LÀÉÀY³£¥u¦³¤@Äæ */
+ word = str + header_len;
+ prints(COLOR5 " %s " COLOR6 "%-*.*s\033[m",
+ header1[i], d_cols + 72, d_cols + 72, word);
+ return;
+ }
+ }
+
+ /* ¦pªG¤£¬OÀÉÀY¡A´N·í¤@¯ë¤å¦r¦L¥X */
+ outs_line(str);
+}
+
+
+static inline void
+outs_footer(buf, lino, fsize)
+ char *buf;
+ int lino;
+ int fsize;
+{
+ int i;
+
+ /* P.1 ¦³ (PAGE_SCROLL + 1) ¦C¡A¨ä¥L Page ³£¬O PAGE_SCROLL ¦C */
+
+ /* prints(FOOTER_MORE, (lino - 2) / PAGE_SCROLL + 1, ((foff - fimage) * 100) / fsize); */
+
+ /* itoc.010821: ¬°¤F©M FOOTER ¹ï»ô */
+ sprintf(buf, FOOTER_MORE, (lino - 2) / PAGE_SCROLL + 1, ((foff - fimage) * 100) / fsize);
+ outs(buf);
+
+ for (i = b_cols + sizeof(COLOR1) + sizeof(COLOR2) - strlen(buf); i > 3; i--)
+ {
+ /* ¶ñº¡³Ì«áªºªÅ¥ÕÃC¦â¡A³Ì«á¯d¤@­ÓªÅ¥Õ */
+ outc(' ');
+ }
+ outs(str_ransi);
+}
+
+
+#ifdef SLIDE_SHOW
+static int slideshow; /* !=0: ¼½©ñ movie ªº³t«× */
+
+static int
+more_slideshow()
+{
+ int ch;
+
+ if (!slideshow)
+ {
+ ch = vkey();
+
+ if (ch == '@')
+ {
+ slideshow = vans("½Ð¿ï¾Ü©ñ¬Mªº³t«× 1(³ÌºC)¡ã9(³Ì§Ö)¡H¼½©ñ¤¤«ö¥ô·NÁä¥i°±¤î¼½©ñ¡G") - '0';
+ if (slideshow < 1 || slideshow > 9)
+ slideshow = 5;
+
+ ch = KEY_PGDN;
+ }
+ }
+ else
+ {
+ struct timeval tv[9] =
+ {
+ {4, 0}, {3, 0}, {2, 0}, {1, 500000}, {1, 0},
+ {0, 800000}, {0, 600000}, {0, 400000}, {0, 200000}
+ };
+
+ refresh();
+ ch = 1;
+ if (select(1, (fd_set *) &ch, NULL, NULL, tv + slideshow - 1) > 0)
+ {
+ /* ­Y¼½©ñ¤¤«ö¥ô·NÁä¡A«h°±¤î¼½©ñ */
+ slideshow = 0;
+ ch = vkey();
+ }
+ else
+ {
+ ch = KEY_PGDN;
+ }
+ }
+
+ return ch;
+}
+#endif
+
+
+#define END_MASK 0x200 /* «ö KEY_END ª½¹F³Ì«á¤@­¶ */
+
+#define HUNT_MASK 0x400
+#define HUNT_NEXT 0x001 /* «ö n ·j´M¤U¤@µ§ */
+#define HUNT_FOUND 0x002 /* «ö / ¶}©l·j´M¡A¥B¤w¸g§ä¨ì match ªº¦r¦ê */
+#define HUNT_START 0x004 /* «ö / ¶}©l·j´M¡A¥B©|¥¼§ä¨ì match ªº¦r¦ê */
+
+#define MAXBLOCK 256 /* °O¿ý´X­Ó block ªº offset¡C¥i¥[³t MAXBLOCK*32 ¦C¥H¤ºªºªø¤å¦b¤W±²/½®Éªº³t«× */
+
+/* Thor.990204: ¶Ç¦^­È -1 ¬°µLªkshow¥X
+ 0 ¬°¥þ¼Æshow§¹
+ >0 ¬°¥¼¥þshow¡A¤¤Â_©Ò«öªºkey */
+int
+more(fpath, footer)
+ char *fpath;
+ char *footer;
+{
+ char buf[ANSILINELEN];
+ int i;
+
+ uschar *headend; /* ÀÉÀYµ²§ô */
+
+ int shift; /* Áٻݭn©¹¤U²¾°Ê´X¦C */
+ int lino; /* ¥Ø«e line number */
+ int header_len; /* ÀÉÀYªºªø«×¡A¦P®É¤]¬O¯¸¤º/¯¸¥~«Hªº°Ï§O */
+ int key; /* «öÁä */
+ int cmd; /* ¤¤Â_®É©Ò«öªºÁä */
+
+ int fsize; /* Àɮפj¤p */
+ static off_t block[MAXBLOCK]; /* ¨C 32 ¦C¬°¤@­Ó block¡A°O¿ý¦¹ block ªº offset */
+
+ if (!(fimage = f_img(fpath, &fsize)))
+ return -1;
+
+ foff = fimage;
+ fend = fimage + fsize;
+
+ /* §äÀÉÀYµ²§ôªº¦a¤è */
+ for (i = 0; i < LINE_HEADER; i++)
+ {
+ if (!more_line(buf))
+ break;
+
+ /* Ū¥XÀɮײĤ@¦C¡A¨Ó§PÂ_¯¸¤º«HÁÙ¬O¯¸¥~«H */
+ if (i == 0)
+ {
+ header_len =
+ !memcmp(buf, str_author1, LEN_AUTHOR1) ? LEN_AUTHOR1 : /* ¡u§@ªÌ:¡vªí¯¸¤º¤å³¹ */
+ !memcmp(buf, str_author2, LEN_AUTHOR2) ? LEN_AUTHOR2 : /* ¡uµo«H¤H:¡vªíÂà«H¤å³¹ */
+ 0; /* ¨S¦³ÀÉÀY */
+ }
+
+ if (!*buf) /* ²Ä¤@¦¸ "\n\n" ¬OÀÉÀYªºµ²§À */
+ break;
+ }
+ headend = foff;
+
+ /* Âk¹s */
+ foff = fimage;
+
+ lino = cmd = 0;
+ block[0] = 0;
+
+#ifdef SLIDE_SHOW
+ slideshow = 0;
+#endif
+
+ if (hunt[0]) /* ¦b xxxx_browse() ½Ð¨D·j´M¦r¦ê */
+ {
+ str_lowest(hunt, hunt);
+ shift = HUNT_MASK | HUNT_START;
+ }
+ else
+ {
+ shift = b_lines;
+ }
+
+ clear();
+
+ while (more_line(buf))
+ {
+ /* ------------------------------------------------- */
+ /* ¦L¥X¤@¦Cªº¤å¦r */
+ /* ------------------------------------------------- */
+
+ /* ­º­¶«e´X¦C¤~»Ý­n³B²zÀÉÀY */
+ if (foff <= headend)
+ outs_header(buf, header_len);
+ else
+ outs_line(buf);
+
+ outc('\n');
+
+ /* ------------------------------------------------- */
+ /* ¨Ì shift ¨Ó¨M©w°Ê§@ */
+ /* ------------------------------------------------- */
+
+ /* itoc.030303.µù¸Ñ: shift ¦b¦¹ªº·N¸q
+ >0: Áٻݭn©¹¤U²¾´X¦C
+ <0: Áٻݭn©¹¤W²¾´X¦C
+ =0: µ²§ô³o­¶¡Aµ¥«Ý¨Ï¥ÎªÌ«öÁä */
+
+ if (shift > 0) /* ÁÙ­n¤U²¾ shift ¦C */
+ {
+ if (lino >= b_lines) /* ¥u¦³¦b­è¶i more¡A²Ä¤@¦¸¦L²Ä¤@­¶®É¤~¥i¯à lino <= b_lines */
+ scroll();
+
+ lino++;
+
+ if ((lino % 32 == 0) && ((i = lino >> 5) < MAXBLOCK))
+ block[i] = foff - fimage;
+
+
+ if (!(shift & (HUNT_MASK | END_MASK))) /* ¤@¯ë¸ê®ÆÅª¨ú */
+ {
+ shift--;
+ }
+ else if (shift & HUNT_MASK) /* ¦r¦ê·j´M */
+ {
+ if (shift & HUNT_NEXT) /* «ö n ·j´M¤U¤@µ§ */
+ {
+ /* ¤@§ä¨ì´N°±©ó¸Ó¦C */
+ if (str_sub(buf, hunt))
+ shift = 0;
+ }
+ else /* «ö / ¶}©l·j´M */
+ {
+ /* ­Y¦b²Ä¤G­¶¥H«á§ä¨ì¡A¤@§ä¨ì´N°±©ó¸Ó¦C¡F
+ ­Y¦b²Ä¤@­¶§ä¨ì¡A¥²¶·µ¥¨ìŪ§¹²Ä¤@­¶¤~¯à°±¤î */
+ if (shift & HUNT_START && str_sub(buf, hunt))
+ shift ^= HUNT_START | HUNT_FOUND; /* ®³±¼ HUNT_START ¨Ã¥[¤W HUNT_FOUND */
+ if (shift & HUNT_FOUND && lino >= b_lines)
+ shift = 0;
+ }
+ }
+ }
+ else if (shift < 0) /* ÁÙ­n¤W²¾ -shift ¦C */
+ {
+ shift++;
+
+ if (!shift)
+ {
+ move(b_lines, 0);
+ clrtoeol();
+
+ /* ³Ñ¤U b_lines+shift ¦C¬O rscroll¡Aoffsect ¥h¥¿½T¦ì¸m¡F³o¸Ìªº i ¬OÁ`¦@­n shift ªº¦C¼Æ */
+ for (i += b_lines; i > 0; i--)
+ more_line(buf);
+ }
+ }
+
+ if (foff >= fend) /* ¤w¸gŪ§¹¥þ³¡ªºÀÉ®× */
+ {
+ /* ­Õ­Y¬O«ö End ²¾¨ì³Ì«á¤@­¶¡A¨º»ò°±¯d¦b 100% ¦Ó¤£µ²§ô¡F§_«h¤@«ßµ²§ô */
+ if (!(shift & END_MASK))
+ break;
+ shift = 0;
+ }
+
+ if (shift) /* ÁٻݭnÄ~ÄòŪ¸ê®Æ */
+ continue;
+
+ /* ------------------------------------------------- */
+ /* ¨ì¦¹¦L§¹©Ò»Ýªº shift ¦C¡A±µ¤U¨Ó¦L¥X footer ¨Ãµ¥«Ý */
+ /* ¨Ï¥ÎªÌ«öÁä */
+ /* ------------------------------------------------- */
+
+re_key:
+
+ outs_footer(buf, lino, fsize);
+
+#ifdef SLIDE_SHOW
+ key = more_slideshow();
+#else
+ key = vkey();
+#endif
+
+ if (key == ' ' || key == KEY_PGDN || key == KEY_RIGHT || key == Ctrl('F'))
+ {
+ shift = PAGE_SCROLL;
+ }
+
+ else if (key == KEY_DOWN || key == '\n')
+ {
+ shift = 1;
+ }
+
+ else if (key == KEY_PGUP || key == Ctrl('B') || key == KEY_DEL)
+ {
+ /* itoc.010324: ¨ì¤F³Ì¶}©l¦A¤W±²ªí¥ÜÂ÷¶}¡A¨Ã¦^¶Ç 'k' (keymap[] ©w¸q¤W¤@½g) */
+ if (lino <= b_lines)
+ {
+ cmd = 'k';
+ break;
+ }
+ /* ³Ì¦h¥u¯à¤W±²¨ì¤@¶}©l */
+ i = b_lines - lino;
+ shift = BMAX(-PAGE_SCROLL, i);
+ }
+
+ else if (key == KEY_UP)
+ {
+ /* itoc.010324: ¨ì¤F³Ì¶}©l¦A¤W±²ªí¥ÜÂ÷¶}¡A¨Ã¦^¶Ç 'k' (keymap[] ©w¸q¤W¤@½g) */
+ if (lino <= b_lines)
+ {
+ cmd = 'k';
+ break;
+ }
+ shift = -1;
+ }
+
+ else if (key == KEY_END || key == '$')
+ {
+ shift = END_MASK;
+ }
+
+ else if (key == KEY_HOME || key == '0')
+ {
+ if (lino <= b_lines) /* ¤w¸g¦b³Ì¶}©l¤F */
+ shift = 0;
+ else
+ shift = -PAGE_SCROLL - 1;
+ }
+
+ else if (key == '/' || key == 'n')
+ {
+ if (key == 'n' && hunt[0]) /* ¦pªG«ö n «o¥¼¿é¤J¹L·j´M¦r¦ê¡A¨º»òµø¦P«ö / */
+ {
+ shift = HUNT_MASK | HUNT_NEXT;
+ }
+ else if (vget(b_lines, 0, "·j´M¡G", hunt, sizeof(hunt), DOECHO))
+ {
+ str_lowest(hunt, hunt);
+ shift = HUNT_MASK | HUNT_START;
+ }
+ else /* ¦pªG¨ú®ø·j´Mªº¸Ü¡A­«Ã¸ footer §Y¥i */
+ {
+ shift = 0;
+ }
+ }
+
+ else if (key == 'C') /* Thor.980405: more ®É¥i¦s¤J¼È¦sÀÉ */
+ {
+ FILE *fp;
+ if (fp = tbf_open())
+ {
+ f_suck(fp, fpath);
+ fclose(fp);
+ }
+ shift = 0; /* ­«Ã¸ footer */
+ }
+
+ else if (key == 'h')
+ {
+ screenline slt[T_LINES];
+ uschar *tmp_fimage;
+ uschar *tmp_fend;
+ uschar *tmp_foff;
+ off_t tmp_block[MAXBLOCK];
+
+ /* itoc.060420: xo_help() ·|¶i¤J²Ä¤G¦¸ more()¡A©Ò¥H­n§â©Ò¦³ static «Å§iªº³£°O¿ý¤U¨Ó */
+ tmp_fimage = fimage;
+ tmp_fend = fend;
+ tmp_foff = foff;
+ memcpy(tmp_block, block, sizeof(tmp_block));
+
+ vs_save(slt);
+ xo_help("post");
+ vs_restore(slt);
+
+ fimage = tmp_fimage;
+ fend = tmp_fend;
+ foff = tmp_foff;
+ memcpy(block, tmp_block, sizeof(block));
+
+ shift = 0;
+ }
+
+ else /* ¨ä¥LÁä³£¬O¨Ï¥ÎªÌ¤¤Â_ */
+ {
+ /* itoc.041006: ¨Ï¥ÎªÌ¤¤Â_ªº«öÁä­n > 0 (¦Ó KEY_LEFT ¬O < 0) */
+ cmd = key > 0 ? key : 'q';
+ break;
+ }
+
+ /* ------------------------------------------------- */
+ /* ¨Ï¥ÎªÌ¤w«öÁä¡A­Y break «hÂ÷¶}°j°é¡F§_«h¨Ì·Ó shift */
+ /* ªººØÃþ (¥ç§Y«öÁ䪺ºØÃþ) ¦Ó°µ¤£¦Pªº°Ê§@ */
+ /* ------------------------------------------------- */
+
+ if (shift > 0) /* ·Ç³Æ¤U²¾ shift ¦C */
+ {
+ if (shift < (HUNT_MASK | HUNT_START)) /* ¤@¯ë¤U²¾ */
+ {
+ /* itoc.041114.µù¸Ñ: ¥Ø¼Ð¬O¨q¥X lino-b_lines+1+shift ~ lino+shift ¦Cªº¤º®e¡G
+ ´N¥u­n²M footer §Y¥i¡A¨ä¥Lªº´N¥æµ¹«e­±´`§Ç¦L shift ¦Cªºµ{¦¡ */
+
+ move(b_lines, 0);
+ clrtoeol();
+
+#if 1
+ /* itoc.041116: End ªº§@ªk¨ä¹ê©M¤@¯ë¤U²¾¥i¥H¬O§¹¥þ¤@¼Ëªº¡A¦ý¬O¦pªG¹J¨ì¶Wªø¤å³¹®É¡A
+ ·|³y¦¨«e­±´`§Ç¦L shift ¦Cªºµ{¦¡´N±o¤@ª½Â½¡Aª½¨ì§ä¨ì³Ì«á¤@­¶¡A³o¼Ë·|°µ¤Ó¦h outs_line() ¥Õ¤u¡A
+ ©Ò¥H¦b¦¹¯S§OÀˬd¶Wªø¤å³¹®É¡A´N¥ý¥h§ä³Ì«á¤@­¶©Ò¦b */
+
+ if ((shift & END_MASK) && (fend - foff >= MORE_BUFSIZE)) /* ÁÙ¦³¤@°ï¨SŪ¹L¡A¤~¯S§O³B²z */
+ {
+ int totallino = lino;
+
+ /* ¥ýŪ¨ì³Ì«á¤@¦C¬Ý¬Ý¥þ³¡¦³´X¦C */
+ while (more_line(buf))
+ {
+ totallino++;
+ if ((totallino % 32 == 0) && ((i = totallino >> 5) < MAXBLOCK))
+ block[i] = foff - fimage;
+ }
+
+ /* ¥ý¦ì²¾¨ì¤W¤@­Ó block ªº§ÀºÝ */
+ i = (totallino - b_lines) >> 5;
+ if (i >= MAXBLOCK)
+ i = MAXBLOCK - 1;
+ foff = fimage + block[i];
+ i = i << 5;
+
+ /* ¦A±q¤W¤@­Ó block ªº§ÀºÝ¦ì²¾¨ì totallino-b_lines+1 ¦C */
+ for (i = totallino - b_lines - i; i > 0; i--)
+ more_line(buf);
+
+ lino = totallino - b_lines;
+ }
+#endif
+ }
+ else
+ {
+ /* '/' ±qÀY¶}©l·j´M */
+ lino = 0;
+ foff = fimage;
+ clear();
+ }
+ }
+ else if (shift < 0) /* ·Ç³Æ¤W²¾ -shift ¦C */
+ {
+ if (shift >= -PAGE_SCROLL) /* ¤W±²¼Æ¦C */
+ {
+ lino += shift;
+
+ /* itoc.041114.µù¸Ñ: ¥Ø¼Ð¬O¨q¥X lino-b_lines+1 ~ lino ¦Cªº¤º®e¡G
+ 1. ¥ý±qÀY¦ì²¾¨ì lino-b_lines+1 ¦C
+ 2. ¨ä¤¤¦³ b_lines+shift ¦C¬O¤£Åܪº¤º®e¡A¥Î rscroll ¹F¦¨
+ 3. ¦b«e­±ªº outs_line() ªº¦a¤è¦L¥X -shift ¦C
+ 4. ³Ì«á¦A¦ì²¾­è¤~ rscroll ªº¦C¼Æ
+ */
+
+ /* ¥ý¦ì²¾¨ì¤W¤@­Ó block ªº§ÀºÝ */
+ i = (lino - b_lines) >> 5;
+ if (i >= MAXBLOCK)
+ i = MAXBLOCK - 1;
+ foff = fimage + block[i];
+ i = i << 5;
+
+ /* ¦A±q¤W¤@­Ó block ªº§ÀºÝ¦ì²¾¨ì lino-b_lines+1 ¦C */
+ for (i = lino - b_lines - i; i > 0; i--)
+ more_line(buf);
+
+ for (i = shift; i < 0; i++)
+ {
+ rscroll();
+ move(0, 0);
+ clrtoeol();
+ }
+
+ i = shift;
+ }
+ else /* Home */
+ {
+ /* itoc.041226.µù¸Ñ: ¥Ø¼Ð¬O¨q¥X 1 ~ b_lines ¦Cªº¤º®e¡G
+ §@ªk´N¬O¥þ³¡³£Âk¹s¡A±qÀY¦A¦L b_lines ¦C§Y¥i */
+
+ clear();
+
+ foff = fimage;
+ lino = 0;
+ shift = b_lines;
+ }
+ }
+ else /* ­«Ã¸ footer ¨Ã re-key */
+ {
+ move(b_lines, 0);
+ clrtoeol();
+ goto re_key;
+ }
+ } /* while °j°éªºµ²§ô */
+
+ /* --------------------------------------------------- */
+ /* Àɮפw¸g¨q§¹ (cmd = 0) ©Î ¨Ï¥ÎªÌ¤¤Â_ (cmd != 0) */
+ /* --------------------------------------------------- */
+
+ free(fimage);
+
+ if (!cmd) /* ÀÉ®×¥¿±`¨q§¹¡A­n³B²z footer */
+ {
+ if (footer) /* ¦³ footer */
+ {
+ if (footer != (char *) -1)
+ outf(footer);
+ else
+ outs(str_ransi);
+ }
+ else /* ¨S¦³ footer ­n vmsg() */
+ {
+ /* lkchu.981201: ¥ý²M¤@¦¸¥H§K­«Å|Åã¥Ü */
+ move(b_lines, 0);
+ clrtoeol();
+
+ if (vmsg(NULL) == 'C') /* Thor.990204: ¯S§Oª`·N­Y¦^¶Ç 'C' ªí¥Ü¼È¦sÀÉ */
+ {
+ FILE *fp;
+
+ if (fp = tbf_open())
+ {
+ f_suck(fp, fpath);
+ fclose(fp);
+ }
+ }
+ }
+ }
+ else /* ¨Ï¥ÎªÌ¤¤Â_¡Aª½±µÂ÷¶} */
+ {
+ outs(str_ransi);
+ }
+
+ hunt[0] = '\0';
+
+ /* Thor.990204: Åýkey¥i¦^¶Ç¦Ümore¥~ */
+ return cmd;
+}
diff --git a/maple/pal.c b/maple/pal.c
new file mode 100644
index 0000000..5a6e62e
--- /dev/null
+++ b/maple/pal.c
@@ -0,0 +1,907 @@
+/*-------------------------------------------------------*/
+/* pal.c ( NTHU CS MapleBBS Ver 3.00 ) */
+/*-------------------------------------------------------*/
+/* target : pal routines */
+/* create : 95/03/29 */
+/* update : 97/03/29 */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+
+extern XZ xz[];
+extern char xo_pool[];
+
+
+static int pal_max = 0; /* §Y cutmp->pal_max¡A¦]¬°¤Ó±`ÀˬdªB¤Í¦W³æ¡A©Ò¥H°O¤U¨Ó¥[³t */
+static int *pal_pool = NULL; /* §Y cutmp->pal_spool¡A¦]¬°¤Ó±`ÀˬdªB¤Í¦W³æ¡A©Ò¥H°O¤U¨Ó¥[³t */
+
+
+/* ----------------------------------------------------- */
+/* ªB¤Í§P§O */
+/* ----------------------------------------------------- */
+
+
+static int /* 1: userno ¦b pool ¦W³æ¤W */
+belong_pal(pool, max, userno)
+ int *pool;
+ int max;
+ int userno;
+{
+ int *up, datum, mid;
+
+ up = pool;
+ while (max > 0)
+ {
+ datum = up[mid = max >> 1];
+ if (userno == datum)
+ {
+ return 1;
+ }
+ if (userno > datum)
+ {
+ up += (++mid);
+ max -= mid;
+ }
+ else
+ {
+ max = mid;
+ }
+ }
+ return 0;
+}
+
+
+int
+is_mygood(userno) /* 1: §Ú³]¹ï¤è¬°¦n¤Í */
+ int userno;
+{
+ return belong_pal(pal_pool, pal_max, userno);
+}
+
+
+int
+is_mybad(userno) /* 1: §Ú³]¹ï¤è¬°Ãa¤H */
+ int userno;
+{
+#ifdef HAVE_BADPAL
+ return belong_pal(pal_pool, pal_max, -userno);
+#else
+ return 0;
+#endif
+}
+
+
+int
+is_ogood(up) /* 1: ¹ï¤è³]§Ú¬°¦n¤Í */
+ UTMP *up;
+{
+ return belong_pal(up->pal_spool, up->pal_max, cuser.userno);
+}
+
+
+int
+is_obad(up) /* 1: ¹ï¤è³]§Ú¬°Ãa¤H */
+ UTMP *up;
+{
+#ifdef HAVE_BADPAL
+ return belong_pal(up->pal_spool, up->pal_max, -cuser.userno);
+#else
+ return 0;
+#endif
+}
+
+
+#ifdef HAVE_MODERATED_BOARD
+int
+is_bgood(bpal) /* 1: §Ú¬O¸ÓªOªºªO¦n */
+ BPAL *bpal;
+{
+ return belong_pal(bpal->pal_spool, bpal->pal_max, cuser.userno);
+}
+
+
+int
+is_bbad(bpal) /* 1: §Ú¬O¸ÓªOªºªOÃa */
+ BPAL *bpal;
+{
+#ifdef HAVE_BADPAL
+ return belong_pal(bpal->pal_spool, bpal->pal_max, -cuser.userno);
+#else
+ return 0;
+#endif
+}
+#endif
+
+
+/* ----------------------------------------------------- */
+/* ªB¤Í¦W³æ¡G·s¼W¡B§R°£¡B­×§ï¡B¸ü¤J¡B¦P¨B */
+/* ----------------------------------------------------- */
+
+
+static int
+int_cmp(a, b)
+ int *a;
+ int *b;
+{
+ return *a - *b;
+}
+
+
+int
+image_pal(fpath, pool)
+ char *fpath;
+ int *pool;
+{
+ int fsize;
+ int *plist;
+ PAL *phead, *ptail;
+ char *fimage;
+
+ if (fimage = f_img(fpath, &fsize))
+ {
+ if (fsize <= PAL_MAX * sizeof(PAL)) /* ¦pªG¶W¹L PAL_MAX¡A´N¤£¸ü¤J */
+ {
+ plist = pool;
+ phead = (PAL *) fimage;
+ ptail = (PAL *) (fimage + fsize);
+ fsize /= sizeof(PAL);
+
+ do
+ {
+ /* ­Y¬OÃa¤H¡A¦s -userno¡F­Y¬O¦n¤Í¡A¦s +userno */
+ *plist++ = (phead->ftype & PAL_BAD) ? -(phead->userno) : phead->userno;
+ } while (++phead < ptail);
+
+ if (fsize > 1)
+ qsort(pool, fsize, sizeof(int), int_cmp);
+ }
+ else
+ {
+ fsize = 0;
+ }
+ free(fimage);
+ }
+ else
+ {
+ fsize = 0;
+ }
+
+ return fsize;
+}
+
+
+void
+pal_cache()
+{
+ char fpath[64];
+
+ if (!pal_pool) /* ²Ä¤@¦¸³]©w¡A¬O¦b utmp_new() ¸Ì­± */
+ pal_pool = cutmp->pal_spool;
+
+ usr_fpath(fpath, cuser.userid, fn_pal);
+
+ pal_max = image_pal(fpath, pal_pool);
+ cutmp->pal_max = pal_max;
+}
+
+
+static int
+chkpal(pal)
+ PAL *pal;
+{
+ int userno;
+
+ userno = pal->userno;
+ return (userno > 0 && userno == acct_userno(pal->userid));
+}
+
+
+void
+pal_sync(fpath)
+ char *fpath;
+{
+ int fsize;
+
+ fsize = rec_sync(fpath, sizeof(PAL), str_cmp, chkpal);
+
+ if (fsize > PAL_MAX * sizeof(PAL))
+ vmsg(msg_list_over);
+}
+
+
+int
+pal_list(reciper)
+ int reciper;
+{
+ int userno, fd;
+ char buf[32], fpath[64];
+ ACCT acct;
+#ifdef HAVE_LIST
+ int ch;
+#endif
+
+ userno = 0;
+
+ for (;;)
+ {
+#ifdef HAVE_LIST
+ switch (ch = vget(1, 0, "(A)¼W¥[ (D)§R°£ (F)¦n¤Í (G)¸s²Õ (M)©w®× (1~5)¯S§O¦W³æ (Q)¨ú®ø¡H[M] ", buf, 3, LCECHO))
+#else
+ switch (vget(1, 0, "(A)¼W¥[ (D)§R°£ (F)¦n¤Í (G)¸s²Õ (M)©w®× (Q)¨ú®ø¡H[M] ", buf, 3, LCECHO))
+#endif
+ {
+ case 'a':
+ while (acct_get("½Ð¿é¤J¥N¸¹(¥u«ö ENTER µ²§ô·s¼W): ", &acct) > 0)
+ {
+ if (!ll_has(acct.userid))
+ {
+ ll_add(acct.userid);
+ reciper++;
+ ll_out(3, 0, MSG_LL);
+ }
+ }
+ break;
+
+ case 'd':
+ while (reciper)
+ {
+ if (!vget(1, 0, "½Ð¿é¤J¥N¸¹(¥u«ö ENTER µ²§ô§R°£): ", buf, IDLEN + 1, GET_LIST))
+ break;
+ if (ll_del(buf))
+ reciper--;
+ ll_out(3, 0, MSG_LL);
+ }
+ break;
+
+#ifdef HAVE_LIST
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ /* itoc.010923: ¤Þ¥Î¯S§O¦W³æ¡A¶¶«K¨Ï¥Î¸s²Õ±ø¥ó */
+ sprintf(buf, "%s.%c", FN_LIST, ch);
+ usr_fpath(fpath, cuser.userid, buf);
+#endif
+
+ case 'g':
+ if (userno = vget(b_lines, 0, "¸s²Õ±ø¥ó¡G", buf, 16, DOECHO))
+ str_lowest(buf, buf);
+
+ case 'f':
+#ifdef HAVE_LIST
+ if (ch == 'g' || ch == 'f')
+#endif
+ usr_fpath(fpath, cuser.userid, fn_pal);
+
+ if ((fd = open(fpath, O_RDONLY)) >= 0)
+ {
+ PAL *pal;
+ char *userid;
+
+ mgets(-1);
+ while (pal = mread(fd, sizeof(PAL)))
+ {
+ userid = pal->userid;
+ if (!ll_has(userid) &&
+ !(pal->ftype & PAL_BAD) &&
+ (!userno || str_sub(pal->ship, buf)))
+ {
+ ll_add(userid);
+ reciper++;
+ }
+ }
+ close(fd);
+ }
+ ll_out(3, 0, MSG_LL);
+ userno = 0;
+ break;
+
+ case 'q':
+ return 0;
+
+ default:
+ return reciper;
+ }
+ }
+}
+
+
+
+/* ----------------------------------------------------- */
+/* ªB¤Í¦W³æ¡G¿ï³æ¦¡¾Þ§@¬É­±´y­z */
+/* ----------------------------------------------------- */
+
+
+static int pal_add();
+
+
+static void
+pal_item(num, pal)
+ int num;
+ PAL *pal;
+{
+#ifdef CHECK_ONLINE
+ UTMP *online = utmp_get(pal->userno, NULL);
+
+ prints("%6d%c%-3s%s%-14s%s%s\n",
+ num, tag_char(pal->userno), pal->ftype & PAL_BAD ? "¢æ" : "",
+ online ? COLOR7 : "", pal->userid, online ? str_ransi : "", pal->ship);
+#else
+ prints("%6d%c%-3s%-14s%s\n",
+ num, tag_char(pal->userno), pal->ftype & PAL_BAD ? "¢æ" : "", pal->userid, pal->ship);
+#endif
+}
+
+
+static int
+pal_body(xo)
+ XO *xo;
+{
+ PAL *pal;
+ int num, max, tail;
+
+ max = xo->max;
+ if (max <= 0)
+ {
+ if (vans("­n¥æ·sªB¤Í¶Ü(Y/N)¡H[N] ") == 'y')
+ return pal_add(xo);
+ return XO_QUIT;
+ }
+
+ pal = (PAL *) xo_pool;
+ num = xo->top;
+ tail = num + XO_TALL;
+ if (max > tail)
+ max = tail;
+
+ move(3, 0);
+ do
+ {
+ pal_item(++num, pal++);
+ } while (num < max);
+ clrtobot();
+
+ /* return XO_NONE; */
+ return XO_FOOT; /* itoc.010403: §â b_lines ¶ñ¤W feeter */
+}
+
+
+static int
+pal_head(xo)
+ XO *xo;
+{
+ char *head[] = {"ªB¤Í¦W³æ", "¸s²Õ¦W³æ", "ªO¤Í¦W³æ", "­­¨î§ë²¼¦W³æ"};
+
+ vs_head(head[xo->key], str_site);
+ prints(NECKER_PAL, d_cols, "");
+ return pal_body(xo);
+}
+
+
+static int
+pal_load(xo)
+ XO *xo;
+{
+ xo_load(xo, sizeof(PAL));
+ return pal_body(xo);
+}
+
+
+static int
+pal_init(xo)
+ XO *xo;
+{
+ xo_load(xo, sizeof(PAL));
+ return pal_head(xo);
+}
+
+
+void
+pal_edit(key, pal, echo)
+ int key;
+ PAL *pal;
+ int echo;
+{
+ if (echo == DOECHO)
+ memset(pal, 0, sizeof(PAL));
+ vget(b_lines, 0, "¤Í½Ë¡G", pal->ship, sizeof(pal->ship), echo);
+#ifdef HAVE_BADPAL
+ if (key != PALTYPE_VOTE) /* ­­¨î§ë²¼¦W³æ¨S¦³Ãa¤H */
+ {
+ char buf[40];
+ sprintf (buf, "%s(Y/N)¡H[N] ", key == PALTYPE_BPAL ? "¤ô±í" : "Ãa¤H");
+// pal->ftype = vans("Ãa¤H(Y/N)¡H[N] ") == 'y' ? PAL_BAD : 0;
+ pal->ftype = vans (buf) == 'y' ? PAL_BAD : 0;
+ }
+ else
+#endif
+ pal->ftype = 0;
+}
+
+
+/* static */ /* itoc.020117: µ¹ vote.c ¥Î */
+int
+pal_find(fpath, userno) /* itoc.010923: ªB¤Í¦W³æ¤¤¬O§_¤w¦³¦¹¤H */
+ char *fpath;
+ int userno;
+{
+ PAL old;
+ int fd;
+ int rc = 0;
+
+ if ((fd = open(fpath, O_RDONLY)) >= 0)
+ {
+ while (read(fd, &old, sizeof(PAL)) == sizeof(PAL))
+ {
+ if (userno == old.userno)
+ {
+ rc = 1;
+ break;
+ }
+ }
+ close(fd);
+ }
+ return rc;
+}
+
+
+static int
+pal_add(xo)
+ XO *xo;
+{
+ ACCT acct;
+ int userno;
+
+ if (xo->max >= PAL_MAX)
+ {
+ vmsg(msg_list_over);
+ return XO_FOOT;
+ }
+
+ userno = acct_get(msg_uid, &acct);
+
+ if (userno == cuser.userno) /* lkchu.981201: ªB¤Í¦W³æ¤£¥i¥[¦Û¤v */
+ {
+ vmsg("¦Û¤v¤£¶·¥[¤JªB¤Í¦W³æ¤¤");
+ }
+ else if (pal_find(xo->dir, userno))
+ {
+ vmsg("¦W³æ¤¤¤w¦³¦¹¤H");
+ }
+ else if (userno > 0)
+ {
+ PAL pal;
+
+ pal_edit(xo->key, &pal, DOECHO);
+ strcpy(pal.userid, acct.userid);
+ pal.userno = userno;
+ rec_add(xo->dir, &pal, sizeof(PAL));
+
+ if (xo->key == PALTYPE_PAL)
+ utmp_admset(userno, STATUS_PALDIRTY);
+
+ if (xo->dir[0] == 'b' && /* ªO¤Í¤~»Ý­n±H«H */
+ vans("¬O§_µo¨ç³qª¾¥L(Y/N)¡H[N] ") == 'y')
+ {
+ char fpath[64], *title;
+ FILE *fp;
+
+ sprintf(fpath, "tmp/pal_add.%s", cuser.userid); /* ¼È¦sÀÉ */
+ if (fp = fopen(fpath, "w"))
+ {
+ title = "¥[¤JªO¤Í³qª¾";
+
+ /* ¤å³¹ÀÉÀY */
+ fprintf(fp, "%s %s (%s)\n",
+ str_author1, cuser.userid, cuser.username);
+ fprintf(fp, "¼ÐÃD: %s\n®É¶¡: %s\n\n", title, Now());
+
+ /* ¤å³¹¤º®e */
+ fprintf(fp, "%s ±N±z¥[¤J [%s] ªºªO¤Í\n\n", cuser.userid, currboard);
+ fclose(fp);
+
+ mail_him(fpath, acct.userid, title, 0);
+ unlink(fpath);
+ }
+ }
+ xo->pos = XO_TAIL; /* ©ñ¦b³Ì«á */
+ return pal_init(xo);
+ }
+
+ return pal_head(xo);
+}
+
+
+static int
+pal_delete(xo)
+ XO *xo;
+{
+ if (vans(msg_del_ny) == 'y')
+ {
+ if (!rec_del(xo->dir, sizeof(PAL), xo->pos, NULL))
+ {
+ if (xo->key == PALTYPE_PAL)
+ {
+ PAL *pal;
+ pal = (PAL *) xo_pool + (xo->pos - xo->top);
+ utmp_admset(pal->userno, STATUS_PALDIRTY);
+ }
+ return pal_load(xo);
+ }
+ }
+ return XO_FOOT;
+}
+
+
+static void
+changestatus(xo, pal)
+ XO *xo;
+ PAL *pal;
+{
+ if (xo->key == PALTYPE_PAL)
+ utmp_admset(pal->userno, STATUS_PALDIRTY);
+}
+
+
+static int
+pal_rangedel(xo)
+ XO *xo;
+{
+ return xo_rangedel(xo, sizeof(PAL), NULL, changestatus);
+}
+
+
+static int
+vfypal(pal, pos)
+ PAL *pal;
+ int pos;
+{
+ return Tagger(pal->userno, pos, TAG_NIN);
+}
+
+
+static int
+pal_prune(xo)
+ XO *xo;
+{
+ return xo_prune(xo, sizeof(PAL), vfypal, changestatus);
+}
+
+
+static int
+pal_change(xo)
+ XO *xo;
+{
+ PAL *pal, oldpal;
+ int pos, cur;
+
+ pos = xo->pos;
+ cur = pos - xo->top;
+ pal = (PAL *) xo_pool + cur;
+
+ memcpy(&oldpal, pal, sizeof(PAL));
+ pal_edit(xo->key, pal, GCARRY);
+
+ if (memcmp(&oldpal, pal, sizeof(PAL)))
+ {
+ rec_put(xo->dir, pal, sizeof(PAL), pos, NULL);
+ if (xo->key == PALTYPE_PAL)
+ utmp_admset(pal->userno, STATUS_PALDIRTY);
+ move(3 + cur, 0);
+ pal_item(++pos, pal);
+ }
+
+ return XO_FOOT;
+}
+
+
+static int
+pal_mail(xo)
+ XO *xo;
+{
+ PAL *pal;
+
+ pal = (PAL *) xo_pool + (xo->pos - xo->top);
+ return my_send(pal->userid);
+}
+
+
+static int
+pal_write(xo)
+ XO *xo;
+{
+ if (HAS_PERM(PERM_PAGE))
+ {
+ PAL *pal;
+ UTMP *up;
+
+ pal = (PAL *) xo_pool + (xo->pos - xo->top);
+
+ if (up = utmp_find(pal->userno))
+ do_write(up);
+ }
+ return XO_NONE;
+}
+
+
+static int
+pal_broadcast(xo)
+ XO *xo;
+{
+ int fd;
+ BMW bmw;
+ PAL *pal;
+ UTMP *up;
+
+ if (!HAS_PERM(PERM_PAGE))
+ return XO_NONE;
+
+ bmw.caller = NULL;
+ bmw_edit(NULL, "¡¹¼s¼½¡G", &bmw);
+
+ if (bmw.caller) /* bmw_edit() ¤¤¦^µª Yes ­n°e¥X¼s¼½ */
+ {
+ /* itoc.000213: ¥[ "> " ¬°¤F»P¤@¯ë¤ô²y°Ï¤À */
+ sprintf(bmw.userid, "%s> ", cuser.userid);
+
+ if ((fd = open(xo->dir, O_RDONLY)) >= 0)
+ {
+ mgets(-1);
+ while (pal = mread(fd, sizeof(PAL)))
+ {
+ if (pal->ftype & PAL_BAD)
+ continue;
+
+ if (!(up = utmp_find(pal->userno)))
+ continue;
+
+#ifdef HAVE_NOBROAD
+ if (up->ufo & UFO_RCVER)
+ continue;
+#endif
+
+ if (can_override(up))
+ {
+ bmw.recver = up->userno;
+ bmw_send(up, &bmw);
+ }
+ }
+ close(fd);
+ }
+ }
+
+ return XO_NONE;
+}
+
+
+#if (defined(HAVE_MODERATED_BOARD) || defined(HAVE_LIST))
+static int
+pal_cite(xo)
+ XO *xo;
+{
+ int fd, num;
+ char fpath[64], *dir;
+ PAL *pal;
+
+ fd = vans("­n¤Þ¤J (P)ªB¤Í¦W³æ "
+#ifdef HAVE_MODERATED_BOARD
+ "(B)ªO¤Í¦W³æ "
+#endif
+#ifdef HAVE_LIST
+ "(1-5)¯S§O¦W³æ"
+#endif
+ "¡H[Q] ");
+
+ if (fd == 'p')
+ {
+ usr_fpath(fpath, cuser.userid, fn_pal);
+ }
+#ifdef HAVE_MODERATED_BOARD
+ else if (fd == 'b')
+ {
+ if (currbno < 0 || !(bbstate & STAT_BOARD))
+ {
+ vmsg("±z©|¥¼¿ï©w¬ÝªO¡A©Î¬O±z¤£¬O¸ÓªOªºªO¥D");
+ return XO_FOOT;
+ }
+ brd_fpath(fpath, currboard, fn_pal);
+ }
+#endif
+#ifdef HAVE_LIST
+ else if (fd >= '1' && fd <= '5')
+ {
+ char buf[32];
+ sprintf(buf, "%s.%c", FN_LIST, fd);
+ usr_fpath(fpath, cuser.userid, buf);
+ }
+#endif
+ else
+ {
+ return XO_FOOT;
+ }
+
+ dir = xo->dir;
+ if (!strcmp(dir, fpath))
+ {
+ vmsg("¤£¯à¤Þ¤J¦P¤@¥÷¦W³æ");
+ return XO_FOOT;
+ }
+
+ if ((fd = open(fpath, O_RDONLY)) < 0)
+ return XO_FOOT;
+
+ num = PAL_MAX - xo->max;
+
+ mgets(-1);
+ while (pal = mread(fd, sizeof(PAL)))
+ {
+ if (!(pal->ftype & PAL_BAD) && !pal_find(dir, pal->userno))
+ {
+ if (--num < 0) /* itoc.001224: ¤Þ¤J¦W³æ¥u¥[¨ì PAL_MAX */
+ break;
+
+ rec_add(dir, pal, sizeof(PAL));
+ xo->pos = XO_TAIL; /* ­Y¦³¤Þ¤J¦W³æ¡A´N§â´å¼Ð©ñ¦b³Ì«á */
+ }
+ }
+ close(fd);
+
+ return pal_load(xo);
+}
+#endif
+
+
+static int
+pal_sort(xo)
+ XO *xo;
+{
+ pal_sync(xo->dir);
+ return pal_load(xo);
+}
+
+
+static int
+pal_query(xo)
+ XO *xo;
+{
+ PAL *pal;
+
+ pal = (PAL *) xo_pool + (xo->pos - xo->top);
+ move(1, 0);
+ clrtobot();
+ my_query(pal->userid);
+ return pal_head(xo);
+}
+
+
+static int
+pal_tag(xo)
+ XO *xo;
+{
+ PAL *pal;
+ int tag, pos, cur;
+
+ pos = xo->pos;
+ cur = pos - xo->top;
+ pal = (PAL *) xo_pool + cur;
+
+ if (tag = Tagger(pal->userno, pos, TAG_TOGGLE))
+ {
+ move(3 + cur, 6);
+ outc(tag > 0 ? '*' : ' ');
+ }
+
+ /* return XO_NONE; */
+ return xo->pos + 1 + XO_MOVE; /* lkchu.981201: ¸õ¦Ü¤U¤@¶µ */
+}
+
+
+static int
+pal_help(xo)
+ XO *xo;
+{
+ xo_help("pal");
+ return pal_head(xo);
+}
+
+
+KeyFunc pal_cb[] =
+{
+ XO_INIT, pal_init,
+ XO_LOAD, pal_load,
+ XO_HEAD, pal_head,
+ XO_BODY, pal_body,
+
+ 'a', pal_add,
+ 'c', pal_change,
+ 'd', pal_delete,
+ 'D', pal_rangedel,
+ 'm', pal_mail,
+ 'w', pal_write,
+ 'B', pal_broadcast,
+ 'r', pal_query,
+ Ctrl('Q'), pal_query,
+ 's', pal_sort,
+ 't', pal_tag,
+ Ctrl('D'), pal_prune,
+
+#if (defined(HAVE_MODERATED_BOARD) || defined(HAVE_LIST))
+ 'f', pal_cite,
+#endif
+
+ 'h', pal_help
+};
+
+
+int
+t_pal()
+{
+ XO *xo;
+ char fpath[64];
+
+ usr_fpath(fpath, cuser.userid, fn_pal);
+ xz[XZ_PAL - XO_ZONE].xo = xo = xo_new(fpath);
+ xo->key = PALTYPE_PAL;
+ xover(XZ_PAL);
+ free(xo);
+
+ /* itoc.041211.µù¸Ñ: ¦bÂ÷¶}ªB¤Í¦W³æ¦A¤@¨Ö¦P¨B cache ¬O¦³°ÝÃDªº¡A
+ ·í§Ú©|¥¼Â÷¶}ªB¤Í¦W³æ®É¡A¦¹®É cutmp->pal_spool ©|¥¼¦P¨B¡A
+ ¦ý³Q§Ú²§°ÊªB¤Íª¬ºAªº¹ï¤è¤w¸g¥[¤J¤F STATUS_PALDIRTY¡A
+ ­Y¹ï¤è¦b§Ú©|¥¼Â÷¶}ªB¤Í¦W³æ®É´N¥ý¦æ¶i¤J¨Ï¥ÎªÌ¦W³æ¡A
+ ³o®ÉÁöµM¥L¤w¸g¦¬¨ì§Úµ¹¥Lªº STATUS_PALDIRTY¡AµM¦Ó«o¦]¬°§Úªº pal_spool ©|¥¼¦P¨B¡A
+ ©ó¬O¥L¨Ã¨S¦³¦¨¥\Åܧó§ÚªºªB¤Íª¬ºA¡A¦ý STATUS_PALDIRTY ¤w®ø¥¢¡C
+ ­n¸Ñ¨M³o­Ó°ÝÃD¡A±o¦b²§°Ê¨C¤@µ§ªB¤Í®Éªº utmp_admset(STATUS_PALDIRTY) ¤§«e´N¥ý pal_cache()¡A
+ ¤£¹L²{¦bÁÙ¨S¦³¤H©ê«è³o°ÝÃD¡A©Ò¥H´N¬ÙÂI¤u¦n¤F :p */
+
+ pal_cache(); /* itoc.010923: Â÷¶}ªB¤Í¦W³æ¦A¤@¨Ö¦P¨B cache */
+
+ return 0;
+}
+
+
+#ifdef HAVE_LIST
+int
+t_list()
+{
+ int n;
+ char fpath[64], buf[8];
+ XO *xo;
+
+ move(MENU_XPOS, 0);
+ clrtobot();
+
+ for (n = 1; n <= 5; n++)
+ {
+ move(n + MENU_XPOS - 1, MENU_YPOS - 1);
+ prints("(\033[1;36m%d\033[m) ¸s²Õ¦W³æ.%d", n, n);
+ }
+
+ n = vans("½Ð¿ï¾ÜÀÉ®×½s¸¹¡A©Î«ö [0] ¨ú®ø¡G") - '0';
+ if (n <= 0 || n > 5)
+ return 0;
+
+ sprintf(buf, "%s.%d", FN_LIST, n);
+ usr_fpath(fpath, cuser.userid, buf);
+
+ switch (vget(b_lines, 36, "(D)§R°£ (E)½s¿è [Q]¨ú®ø¡H", buf, 3, LCECHO))
+ {
+ case 'd':
+ unlink(fpath);
+ break;
+
+ case 'e':
+ /* ­É¥Î XZ_PAL §Y¥i */
+ xz[XZ_PAL - XO_ZONE].xo = xo = xo_new(fpath);
+ xo->key = PALTYPE_LIST;
+ xover(XZ_PAL);
+ free(xo);
+ break;
+ }
+
+ return 0;
+}
+#endif
diff --git a/maple/post.c b/maple/post.c
new file mode 100644
index 0000000..a62d6b5
--- /dev/null
+++ b/maple/post.c
@@ -0,0 +1,2730 @@
+/*-------------------------------------------------------*/
+/* post.c ( NTHU CS MapleBBS Ver 2.39 ) */
+/*-------------------------------------------------------*/
+/* target : bulletin boards' routines */
+/* create : 95/03/29 */
+/* update : 96/04/05 */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+#include <sys/wait.h>
+
+
+extern BCACHE *bshm;
+extern XZ xz[];
+
+
+extern int wordsnum; /* itoc.010408: ­pºâ¤å³¹¦r¼Æ */
+extern int TagNum;
+extern char xo_pool[];
+extern char brd_bits[];
+
+
+#ifdef HAVE_ANONYMOUS
+extern char anonymousid[]; /* itoc.010717: ¦Û©w°Î¦W ID */
+#endif
+
+
+int
+cmpchrono(hdr)
+ HDR *hdr;
+{
+ return hdr->chrono == currchrono;
+}
+
+
+/* ----------------------------------------------------- */
+/* §ï¨} innbbsd Âà¥X«H¥ó¡B³s½u¬å«H¤§³B²zµ{§Ç */
+/* ----------------------------------------------------- */
+
+
+void
+btime_update(bno)
+ int bno;
+{
+ if (bno >= 0)
+ (bshm->bcache + bno)->btime = -1; /* Åý class_item() §ó·s¥Î */
+}
+
+
+#ifndef HAVE_NETTOOL
+static /* µ¹ enews.c ¥Î */
+#endif
+void
+outgo_post(hdr, board)
+ HDR *hdr;
+ char *board;
+{
+ bntp_t bntp;
+
+ memset(&bntp, 0, sizeof(bntp_t));
+
+ if (board) /* ·s«H */
+ {
+ bntp.chrono = hdr->chrono;
+ }
+ else /* cancel */
+ {
+ bntp.chrono = -1;
+ board = currboard;
+ }
+ strcpy(bntp.board, board);
+ strcpy(bntp.xname, hdr->xname);
+ strcpy(bntp.owner, hdr->owner);
+ strcpy(bntp.nick, hdr->nick);
+ strcpy(bntp.title, hdr->title);
+ rec_add("innd/out.bntp", &bntp, sizeof(bntp_t));
+}
+
+
+void
+cancel_post(hdr)
+ HDR *hdr;
+{
+ if ((hdr->xmode & POST_OUTGO) && /* ¥~Âà«H¥ó */
+ (hdr->chrono > ap_start - 7 * 86400)) /* 7 ¤Ñ¤§¤º¦³®Ä */
+ {
+ outgo_post(hdr, NULL);
+ }
+}
+
+
+static inline int /* ¦^¶Ç¤å³¹ size ¥h¦©¿ú */
+move_post(hdr, folder, by_bm) /* ±N hdr ±q folder ·h¨ì§OªºªO */
+ HDR *hdr;
+ char *folder;
+ int by_bm;
+{
+ HDR post;
+ int xmode;
+ char fpath[64], fnew[64], *board;
+ struct stat st;
+
+ xmode = hdr->xmode;
+ hdr_fpath(fpath, folder, hdr);
+
+ if (!(xmode & POST_BOTTOM)) /* ¸m©³¤å³Q¬å¤£¥Î move_post */
+ {
+#ifdef HAVE_REFUSEMARK
+ board = by_bm && !(xmode & POST_RESTRICT) ? BN_DELETED : BN_JUNK; /* ¥[±K¤å³¹¥á¥h junk */
+#else
+ board = by_bm ? BN_DELETED : BN_JUNK;
+#endif
+
+ brd_fpath(fnew, board, fn_dir);
+ hdr_stamp(fnew, HDR_LINK | 'A', &post, fpath);
+
+ /* ª½±µ½Æ»s trailing data¡Gowner(§t)¥H¤U©Ò¦³Äæ¦ì */
+
+ memcpy(post.owner, hdr->owner, sizeof(HDR) -
+ (sizeof(post.chrono) + sizeof(post.xmode) + sizeof(post.xid) + sizeof(post.xname)));
+
+ if (by_bm)
+ sprintf(post.title, "%-13s%.59s", cuser.userid, hdr->title);
+
+ if (hdr->xmode & POST_RESTRICT) post.xmode |= POST_RESTRICT;
+
+ rec_bot(fnew, &post, sizeof(HDR));
+ btime_update(brd_bno(board));
+ }
+
+ by_bm = stat(fpath, &st) ? 0 : st.st_size;
+
+ unlink(fpath);
+ btime_update(currbno);
+ cancel_post(hdr);
+
+ return by_bm;
+}
+
+
+#ifdef HAVE_DETECT_CROSSPOST
+/* ----------------------------------------------------- */
+/* §ï¨} cross post °±Åv */
+/* ----------------------------------------------------- */
+
+
+#define MAX_CHECKSUM_POST 20 /* °O¿ý³Ìªñ 20 ½g¤å³¹ªº checksum */
+#define MAX_CHECKSUM_LINE 6 /* ¥u¨ú¤å³¹«e 6 ¦æ¨Óºâ checksum */
+
+
+typedef struct
+{
+ int sum; /* ¤å³¹ªº checksum */
+ int total; /* ¦¹¤å³¹¤wµoªí´X½g */
+} CHECKSUM;
+
+
+static CHECKSUM checksum[MAX_CHECKSUM_POST];
+static int checknum = 0;
+
+
+static inline int
+checksum_add(str) /* ¦^¶Ç¥»¦æ¤å¦rªº checksum */
+ char *str;
+{
+ int sum, i, len;
+ char *ptr;
+
+ ptr = str;
+ len = strlen(str) >> 2; /* ¥uºâ«e¥|¤À¤§¤@ */
+
+ sum = 0;
+ for (i = 0; i < len; i++)
+ sum += *ptr++;
+
+ return sum;
+}
+
+
+static inline int /* 1:¬Ocross-post 0:¤£¬Ocross-post */
+checksum_put(sum)
+ int sum;
+{
+ int i;
+
+ if (sum)
+ {
+ for (i = 0; i < MAX_CHECKSUM_POST; i++)
+ {
+ if (checksum[i].sum == sum)
+ {
+ checksum[i].total++;
+
+ if (checksum[i].total > MAX_CROSS_POST)
+ return 1;
+ return 0; /* total <= MAX_CROSS_POST */
+ }
+ }
+
+ if (++checknum >= MAX_CHECKSUM_POST)
+ checknum = 0;
+ checksum[checknum].sum = sum;
+ checksum[checknum].total = 1;
+ }
+ return 0;
+}
+
+
+static int /* 1:¬Ocross-post 0:¤£¬Ocross-post */
+checksum_find(fpath)
+ char *fpath;
+{
+ int i, sum;
+ char buf[ANSILINELEN];
+ FILE *fp;
+
+ sum = 0;
+ if (fp = fopen(fpath, "r"))
+ {
+ for (i = -4;;) /* «e¥|¦C¬OÀÉÀY */
+ {
+ if (!fgets(buf, ANSILINELEN, fp))
+ break;
+
+ if (i < 0) /* ¸õ¹LÀÉÀY */
+ {
+ i++;
+ continue;
+ }
+
+ if (*buf == QUOTE_CHAR1 || *buf == '\n' || !strncmp(buf, "¡°", 2)) /* ¸õ¹L¤Þ¨¥ */
+ continue;
+
+ sum += checksum_add(buf);
+ if (++i >= MAX_CHECKSUM_LINE)
+ break;
+ }
+
+ fclose(fp);
+ }
+
+ return checksum_put(sum);
+}
+
+
+static int
+check_crosspost(fpath, bno)
+ char *fpath;
+ int bno; /* ­nÂà¥hªº¬ÝªO */
+{
+ char *blist, folder[64];
+ ACCT acct;
+ HDR hdr;
+
+ if (HAS_PERM(PERM_ALLADMIN))
+ return 0;
+
+ /* ªO¥D¦b¦Û¤vºÞ²zªº¬ÝªO¤£¦C¤J¸ó¶KÀˬd */
+ blist = (bshm->bcache + bno)->BM;
+ if (HAS_PERM(PERM_BM) && blist[0] > ' ' && is_bm(blist, cuser.userid))
+ return 0;
+
+ if (checksum_find(fpath))
+ {
+ /* ¦pªG¬O cross-post¡A¨º»òÂà¥h BN_SECURITY ¨Ãª½±µ°±Åv */
+ brd_fpath(folder, BN_SECURITY, fn_dir);
+ hdr_stamp(folder, HDR_COPY | 'A', &hdr, fpath);
+ strcpy(hdr.owner, cuser.userid);
+ strcpy(hdr.nick, cuser.username);
+ sprintf(hdr.title, "%s %s Cross-Post", cuser.userid, Now());
+ rec_bot(folder, &hdr, sizeof(HDR));
+ btime_update(brd_bno(BN_SECURITY));
+
+ bbstate &= ~STAT_POST;
+ cuser.userlevel &= ~PERM_POST;
+ cuser.userlevel |= PERM_DENYPOST;
+ if (acct_load(&acct, cuser.userid) >= 0)
+ {
+ acct.tvalid = time(NULL) + CROSSPOST_DENY_DAY * 86400;
+ acct_setperm(&acct, PERM_DENYPOST, PERM_POST);
+ }
+ board_main();
+ mail_self(FN_ETC_CROSSPOST, str_sysop, "Cross-Post °±Åv", 0);
+ vmsg("±z¦]¬°¹L«× Cross-Post ¤w³Q°±Åv");
+ return 1;
+ }
+ return 0;
+}
+#endif /* HAVE_DETECT_CROSSPOST */
+
+
+/* ----------------------------------------------------- */
+/* µoªí¡B¦^À³¡B½s¿è¡BÂà¿ý¤å³¹ */
+/* ----------------------------------------------------- */
+
+
+#ifdef HAVE_ANONYMOUS
+static void
+log_anonymous(fname)
+ char *fname;
+{
+ char buf[512];
+
+ sprintf(buf, "%s %-13s(%s)\n%-13s %s %s\n",
+ Now(), cuser.userid, fromhost, currboard, fname, ve_title);
+ f_cat(FN_RUN_ANONYMOUS, buf);
+}
+#endif
+
+
+#ifdef HAVE_UNANONYMOUS_BOARD
+static void
+do_unanonymous(fpath)
+ char *fpath;
+{
+ HDR hdr;
+ char folder[64];
+
+ brd_fpath(folder, BN_UNANONYMOUS, fn_dir);
+ hdr_stamp(folder, HDR_LINK | 'A', &hdr, fpath);
+
+ strcpy(hdr.owner, cuser.userid);
+ strcpy(hdr.title, ve_title);
+
+ rec_bot(folder, &hdr, sizeof(HDR));
+ btime_update(brd_bno(BN_UNANONYMOUS));
+}
+#endif
+
+void
+add_post(brdname, fpath, title) /* µo¤å¨ì¬ÝªO */
+ char *brdname; /* ±ý post ªº¬ÝªO */
+ char *fpath; /* Àɮ׸ô®| */
+ char *title; /* ¤å³¹¼ÐÃD */
+{
+ HDR hdr;
+ char folder[64];
+
+ brd_fpath(folder, brdname, fn_dir);
+ hdr_stamp(folder, HDR_LINK | 'A', &hdr, fpath);
+ strcpy(hdr.owner, cuser.userid);
+ strcpy(hdr.nick, cuser.username);
+ strcpy(hdr.title, title);
+ rec_bot(folder, &hdr, sizeof(HDR));
+
+ btime_update(brd_bno(brdname));
+}
+
+static int
+do_post(xo, title)
+ XO *xo;
+ char *title;
+{
+ /* Thor.981105: ¶i¤J«e»Ý³]¦n curredit ¤Î quote_file */
+ HDR hdr, buf;
+ char fpath[64], *folder, *nick, *rcpt;
+ int mode;
+ time_t spendtime, prev, chrono;
+
+ if (!(bbstate & STAT_POST))
+ {
+#ifdef NEWUSER_LIMIT
+ if (cuser.lastlogin - cuser.firstlogin < 3 * 86400)
+ vmsg("·s¤â¤W¸ô¡A¤T¤é«á©l¥i±i¶K¤å³¹");
+ else
+#endif
+ vmsg("¹ï¤£°_¡A±z¨S¦³¦b¦¹µoªí¤å³¹ªºÅv­­");
+ return XO_FOOT;
+ }
+ brd_fpath(fpath, currboard, FN_POSTLAW);
+ if (more(fpath, (char *) -1) < 0)
+ film_out(FILM_POST, 0);
+
+ prints("µoªí¤å³¹©ó¡i %s ¡j¬ÝªO", currboard);
+
+#ifdef POST_PREFIX
+ /* ­É¥Î mode¡Brcpt¡Bfpath */
+
+ if (title)
+ {
+ rcpt = NULL;
+ }
+ else /* itoc.020113: ·s¤å³¹¿ï¾Ü¼ÐÃD¤ÀÃþ */
+ {
+#define NUM_PREFIX 9
+
+ #if 0
+ char *prefix[NUM_PREFIX] = {"[¤½§i] ", "[·s»D] ", "[¶¢²á] ", "[¤å¥ó] ", "[°ÝÃD] ", "[´ú¸Õ] "};
+
+ move(21, 0);
+ outs("Ãþ§O¡G");
+ for (mode = 0; mode < NUM_PREFIX; mode++)
+ prints("%d.%s", mode + 1, prefix[mode]);
+
+ mode = vget(20, 0, "½Ð¿ï¾Ü¤å³¹Ãþ§O¡]«ö Enter ¸õ¹L¡^¡G", fpath, 3, DOECHO) - '1';
+ if (mode >= 0 && mode < NUM_PREFIX) /* ¿é¤J¼Æ¦r¿ï¶µ */
+ rcpt = prefix[mode];
+ else /* ªÅ¥Õ¸õ¹L */
+ rcpt = NULL;
+ }
+
+ #endif
+
+ FILE *fp;
+ char prefix[NUM_PREFIX][10];
+
+ brd_fpath(fpath, currboard, "prefix");
+ if (fp = fopen(fpath, "r"))
+ {
+ move(21, 0);
+// outs("Ãþ§O¡G");
+ for (mode = 0; mode < NUM_PREFIX; mode++)
+ {
+ //if (fscanf(fp, "%9s", fpath) != 1)
+ if (fgets(fpath, 11, fp) == NULL)
+ break;
+ fpath[strlen(fpath)-1] = 0;
+ /* hrs:¦]¬°³Ì«á¤@­ÓŪ¤Jªº¦r¤¸¬O\n ´«¦æ¦r¤¸ ­n¥h°£±¼ */
+ strcpy(prefix[mode], fpath);
+ if(mode == NUM_PREFIX/2)
+ move(22, 0);
+ prints("%d:\033[1;33m%s\033[m ", mode + 1, fpath);
+ }
+
+ fclose(fp);
+ mode = vget(20, 0, "½Ð¿ï¾Ü¤å³¹Ãþ§O¡]«ö Enter ¸õ¹L¡^¡G",
+ fpath, 3, DOECHO) - '1';
+ if (mode >= 0 && mode < NUM_PREFIX) /* ¿é¤J¼Æ¦r¿ï¶µ */
+// rcpt = prefix[mode];
+ rcpt = strcat (prefix[mode], " ");
+ else /* ªÅ¥Õ¸õ¹L */
+ rcpt = NULL;
+ }
+ else
+ {
+ rcpt = NULL;
+ }
+ }
+ if (!ve_subject(21, title, rcpt))
+#else
+ if (!ve_subject(21, title, NULL))
+#endif
+ return XO_HEAD;
+
+ /* ¥¼¨ã³Æ Internet Åv­­ªÌ¡A¥u¯à¦b¯¸¤ºµoªí¤å³¹ */
+ /* Thor.990111: ¨SÂà«H¥X¥hªº¬ÝªO, ¤]¥u¯à¦b¯¸¤ºµoªí¤å³¹ */
+
+ if (!HAS_PERM(PERM_INTERNET) || (currbattr & BRD_NOTRAN))
+ curredit &= ~EDIT_OUTGO;
+
+ utmp_mode(M_POST);
+ fpath[0] = '\0';
+ time(&spendtime);
+ if (vedit(fpath, 1) < 0)
+ {
+ unlink(fpath);
+ vmsg(msg_cancel);
+ return XO_HEAD;
+ }
+ spendtime = time(0) - spendtime; /* itoc.010712: Á`¦@ªáªº®É¶¡(¬í¼Æ) */
+
+ /* build filename */
+
+ folder = xo->dir;
+ hdr_stamp(folder, HDR_LINK | 'A', &hdr, fpath);
+
+ /* set owner to anonymous for anonymous board */
+
+#ifdef HAVE_ANONYMOUS
+ /* Thor.980727: lkchu·s¼W¤§[²³æªº¿ï¾Ü©Ê°Î¦W¥\¯à] */
+ if (curredit & EDIT_ANONYMOUS)
+ {
+ rcpt = anonymousid; /* itoc.010717: ¦Û©w°Î¦W ID */
+ nick = STR_ANONYMOUS;
+
+ /* Thor.980727: lkchu patch: log anonymous post */
+ /* Thor.980909: gc patch: log anonymous post filename */
+ //log_anonymous(hdr.xname);
+
+#ifdef HAVE_UNANONYMOUS_BOARD
+ do_unanonymous(fpath);
+#endif
+ }
+ else
+#endif
+ {
+ rcpt = cuser.userid;
+ nick = cuser.username;
+ }
+ title = ve_title;
+ mode = (curredit & EDIT_OUTGO) ? POST_OUTGO : 0;
+#ifdef HAVE_REFUSEMARK
+ if (curredit & EDIT_RESTRICT)
+ mode |= POST_RESTRICT;
+#endif
+
+ if (!(currbattr & BRD_NOCOUNT || wordsnum < 30))
+ mode |= POST_RECORDED;
+
+
+ hdr.xmode = mode;
+ strcpy(hdr.owner, rcpt);
+ strcpy(hdr.nick, nick);
+ strcpy(hdr.title, title);
+
+ rec_bot(folder, &hdr, sizeof(HDR));
+ btime_update(currbno);
+
+ if (mode & POST_OUTGO)
+ outgo_post(&hdr, currboard);
+
+#if 1 /* itoc.010205: post §¹¤å³¹´N°O¿ý¡A¨Ï¤£¥X²{¥¼¾\Ūªº¡Ï¸¹ */
+ chrono = hdr.chrono;
+ prev = ((mode = rec_num(folder, sizeof(HDR)) - 2) >= 0 && !rec_get(folder, &buf, sizeof(HDR), mode)) ? buf.chrono : chrono;
+ brh_add(prev, chrono, chrono);
+#endif
+
+ clear();
+ outs("¶¶§Q¶K¥X¤å³¹¡A");
+
+// if (currbattr & BRD_NOCOUNT || wordsnum < 30)
+ if (!(hdr.xmode & POST_RECORDED))
+ { /* itoc.010408: ¥H¦¹´î¤ÖÄé¤ô²{¶H */
+ outs("¤å³¹¤£¦C¤J¬ö¿ý¡A·q½Ð¥]²[¡C");
+ }
+ else
+ {
+ /* itoc.010408: ¨Ì¤å³¹ªø«×/©Ò¶O®É¶¡¨Ó¨M©w­nµ¹¦h¤Ö¿ú¡F¹ô¨î¤~·|¦³·N¸q */
+ mode = BMIN(wordsnum, spendtime) / 10; /* ¨C¤Q¦r/¬í ¤@¤¸ */
+ prints("³o¬O±zªº²Ä %d ½g¤å³¹¡A±o %d »È¡C", ++cuser.numposts, mode);
+ addmoney(mode);
+ }
+
+ /* ¦^À³¨ì­ì§@ªÌ«H½c */
+
+ if (curredit & EDIT_BOTH)
+ {
+ rcpt = quote_user;
+
+ if (strchr(rcpt, '@')) /* ¯¸¥~ */
+ mode = bsmtp(fpath, title, rcpt, 0);
+ else /* ¯¸¤º¨Ï¥ÎªÌ */
+ mode = mail_him(fpath, rcpt, title, 0);
+
+ outs(mode >= 0 ? "\n\n¦¨¥\\¦^À³¦Ü§@ªÌ«H½c" : "\n\n§@ªÌµLªk¦¬«H");
+ }
+
+ unlink(fpath);
+
+ vmsg(NULL);
+
+ return XO_INIT;
+}
+
+
+int
+do_reply(xo, hdr)
+ XO *xo;
+ HDR *hdr;
+{
+ curredit = 0;
+
+ switch (vans("¡¶ ¦^À³¦Ü (F)¬ÝªO (M)§@ªÌ«H½c (B)¤GªÌ¬Ò¬O (Q)¨ú®ø¡H[F] "))
+ {
+ case 'm':
+ hdr_fpath(quote_file, xo->dir, hdr);
+ return do_mreply(hdr, 0);
+
+ case 'q':
+ return XO_FOOT;
+
+ case 'b':
+ /* ­YµL±H«HªºÅv­­¡A«h¥u¦^¬ÝªO */
+ if (HAS_PERM(strchr(hdr->owner, '@') ? PERM_INTERNET : PERM_LOCAL))
+ curredit = EDIT_BOTH;
+ break;
+ }
+
+ /* Thor.981105: ¤£½×¬OÂà¶iªº, ©Î¬O­nÂà¥Xªº, ³£¬O§O¯¸¥i¬Ý¨ìªº, ©Ò¥H¦^«H¤]³£À³¸ÓÂà¥X */
+ if (hdr->xmode & (POST_INCOME | POST_OUTGO))
+ curredit |= EDIT_OUTGO;
+
+ hdr_fpath(quote_file, xo->dir, hdr);
+ strcpy(quote_user, hdr->owner);
+ strcpy(quote_nick, hdr->nick);
+ return do_post(xo, hdr->title);
+}
+
+
+static int
+post_reply(xo)
+ XO *xo;
+{
+ if (bbstate & STAT_POST)
+ {
+ HDR *hdr;
+
+ hdr = (HDR *) xo_pool + (xo->pos - xo->top);
+
+#ifdef HAVE_REFUSEMARK
+ if ((hdr->xmode & POST_RESTRICT) &&
+ strcmp(hdr->owner, cuser.userid) && !(bbstate & STAT_BM))
+ return XO_NONE;
+#endif
+
+ return do_reply(xo, hdr);
+ }
+ return XO_NONE;
+}
+
+
+static int
+post_add(xo)
+ XO *xo;
+{
+ curredit = EDIT_OUTGO;
+ *quote_file = '\0';
+ return do_post(xo, NULL);
+}
+
+
+/* ----------------------------------------------------- */
+/* ¦L¥X hdr ¼ÐÃD */
+/* ----------------------------------------------------- */
+
+
+int
+tag_char(chrono)
+ int chrono;
+{
+ return TagNum && !Tagger(chrono, 0, TAG_NIN) ? '*' : ' ';
+}
+
+
+#ifdef HAVE_DECLARE
+static inline int
+cal_day(date) /* itoc.010217: ­pºâ¬P´Á´X */
+ char *date;
+{
+#if 0
+ ½²°Ç¤½¦¡¬O¤@­Ó±Àºâ­þ¤@¤Ñ¬O¬P´Á´Xªº¤½¦¡.
+ ³o¤½¦¡¬O:
+ c y 26(m+1)
+ W= [---] - 2c + y + [---] + [---------] + d - 1
+ 4 4 10
+ W ¡÷ ¬°©Ò¨D¤é´Áªº¬P´Á¼Æ. (¬P´Á¤é: 0 ¬P´Á¤@: 1 ... ¬P´Á¤»: 6)
+ c ¡÷ ¬°¤wª¾¤½¤¸¦~¥÷ªº«e¨â¦ì¼Æ¦r.
+ y ¡÷ ¬°¤wª¾¤½¤¸¦~¥÷ªº«á¨â¦ì¼Æ¦r.
+ m ¡÷ ¬°¤ë¼Æ
+ d ¡÷ ¬°¤é¼Æ
+ [] ¡÷ ªí¥Ü¥u¨ú¸Ó¼Æªº¾ã¼Æ³¡¤À (¦aªO¨ç¼Æ)
+ ps.©Ò¨Dªº¤ë¥÷¦pªG¬O1¤ë©Î2¤ë,«hÀ³µø¬°¤W¤@¦~ªº13¤ë©Î14¤ë.
+ ©Ò¥H¤½¦¡¤¤mªº¨ú­È½d³ò¤£¬O1¨ì12,¦Ó¬O3¨ì14
+#endif
+
+ /* ¾A¥Î 2000/03/01 ¦Ü 2099/12/31 */
+
+ int y, m, d;
+
+ y = 10 * ((int) (date[0] - '0')) + ((int) (date[1] - '0'));
+ d = 10 * ((int) (date[6] - '0')) + ((int) (date[7] - '0'));
+ if (date[3] == '0' && (date[4] == '1' || date[4] == '2'))
+ {
+ y -= 1;
+ m = 12 + (int) (date[4] - '0');
+ }
+ else
+ {
+ m = 10 * ((int) (date[3] - '0')) + ((int) (date[4] - '0'));
+ }
+ return (-1 + y + y / 4 + 26 * (m + 1) / 10 + d) % 7;
+}
+#endif
+
+
+void
+hdr_outs(hdr, cc) /* print HDR's subject */
+ HDR *hdr;
+ int cc; /* ¦L¥X³Ì¦h cc - 1 ¦rªº¼ÐÃD */
+{
+ /* ¦^ÂÐ/Âà¿ý/­ì³Ð/¾\Ū¤¤ªº¦P¥DÃD¦^ÂÐ/¾\Ū¤¤ªº¦P¥DÃDÂà¿ý/¾\Ū¤¤ªº¦P¥DÃD­ì³Ð */
+ static char *type[6] = {"Re", "Fw", "¡º", "\033[1;33m=>", "\033[1;33m=>", "\033[1;32m¡»"};
+ uschar *title, *mark;
+ int ch, len;
+#ifdef HAVE_DECLARE
+ int square;
+#endif
+#ifdef CHECK_ONLINE
+ UTMP *online;
+#endif
+
+ /* --------------------------------------------------- */
+ /* ¦L¥X¤é´Á */
+ /* --------------------------------------------------- */
+
+#ifdef HAVE_DECLARE
+ /* itoc.010217: §ï¥Î¬P´Á´X¨Ó¤W¦â */
+ prints("\033[1;3%dm%s\033[m ", cal_day(hdr->date) + 1, hdr->date + 3);
+#else
+ outs(hdr->date + 3);
+ outc(' ');
+#endif
+
+ /* --------------------------------------------------- */
+ /* ¦L¥X§@ªÌ */
+ /* --------------------------------------------------- */
+
+#ifdef CHECK_ONLINE
+ if (online = utmp_seek(hdr))
+ outs(COLOR7);
+#endif
+
+ mark = hdr->owner;
+ len = IDLEN + 1;
+
+ while (ch = *mark)
+ {
+ if ((--len <= 0) || (ch == '@')) /* ¯¸¥~ªº§@ªÌ§â '@' ´«¦¨ '.' */
+ ch = '.';
+ outc(ch);
+
+ if (ch == '.')
+ break;
+
+ mark++;
+ }
+
+ while (len--)
+ {
+ outc(' ');
+ }
+
+#ifdef CHECK_ONLINE
+ if (online)
+ outs(str_ransi);
+#endif
+
+ /* --------------------------------------------------- */
+ /* ¦L¥X¼ÐÃDªººØÃþ */
+ /* --------------------------------------------------- */
+
+ title = str_ttl(mark = hdr->title);
+ ch = (title == mark) ? 2 : (*mark == 'R') ? 0 : 1;
+ if (!strcmp(currtitle, title))
+ ch += 3;
+ outs(type[ch]);
+ outc(' ');
+
+ /* --------------------------------------------------- */
+ /* ¦L¥X¼ÐÃD */
+ /* --------------------------------------------------- */
+
+ mark = title + cc;
+
+#ifdef HAVE_DECLARE /* Thor.980508: Declaration, ¹Á¸Õ¨Ï¬Y¨Çtitle§ó©úÅã */
+ square = 0; /* 0:¤£³B²z¤è¬A 1:­n³B²z¤è¬A */
+ if (ch < 3)
+ {
+ if (*title == '[')
+ {
+ outs("\033[1m");
+ square = 1;
+ }
+ }
+#endif
+
+ while ((cc = *title++) && (title < mark))
+ {
+#ifdef HAVE_DECLARE
+ if (square)
+ {
+ if (IS_ZHC_HI(square) || IS_ZHC_HI(cc)) /* ¤¤¤å¦rªº²Ä¤G½X­Y¬O ']' ¤£ºâ¬O¤è¬A */
+ square ^= 0x80;
+ else if (cc == ']')
+ {
+ outs("]\033[m");
+ square = 0;
+ continue;
+ }
+ }
+#endif
+
+ outc(cc);
+ }
+
+#ifdef HAVE_DECLARE
+ if (square || ch >= 3) /* Thor.980508: ÅܦâÁÙ­ì¥Î */
+#else
+ if (ch >= 3)
+#endif
+ outs("\033[m");
+
+ outc('\n');
+}
+
+
+/* ----------------------------------------------------- */
+/* ¬ÝªO¥\¯àªí */
+/* ----------------------------------------------------- */
+
+
+static int post_body();
+static int post_head();
+
+
+static int
+post_init(xo)
+ XO *xo;
+{
+ xo_load(xo, sizeof(HDR));
+ return post_head(xo);
+}
+
+
+static int
+post_load(xo)
+ XO *xo;
+{
+ xo_load(xo, sizeof(HDR));
+ return post_body(xo);
+}
+
+
+static int
+post_attr(hdr)
+ HDR *hdr;
+{
+ int mode, attr;
+
+ mode = hdr->xmode;
+
+ /* ¥Ñ©ó¸m©³¤å¨S¦³¾\Ū°O¿ý¡A©Ò¥Hµø¬°¤wŪ */
+ attr = !(mode & POST_BOTTOM) && brh_unread(hdr->chrono) ? 0 : 0x20; /* ¤w¾\Ū¬°¤p¼g¡A¥¼¾\Ū¬°¤j¼g */
+
+#ifdef HAVE_REFUSEMARK
+ if (mode & POST_RESTRICT)
+ attr |= 'X';
+ else
+#endif
+#ifdef HAVE_LABELMARK
+ if (mode & POST_DELETE)
+ attr |= 'T';
+ else
+#endif
+ if (mode & POST_MARKED)
+ attr |= 'M';
+ else if (!attr)
+ attr = '+';
+
+ return attr;
+}
+
+
+static void
+post_item(num, hdr)
+ int num;
+ HDR *hdr;
+{
+#ifdef HAVE_SCORE
+ static char scorelist[36] =
+ {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
+ 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
+ 'U', 'V', 'W', 'X', 'Y', 'Z'
+ };
+
+// prints("%6d%c%c", (hdr->xmode & POST_BOTTOM) ? -1 : num, tag_char(hdr->chrono), post_attr(hdr));
+
+// prints("%6d%c%s%c%s", (hdr->xmode & POST_BOTTOM) ? -1 : num, tag_char(hdr->chrono),
+// hdr->xmode & (POST_MARKED | POST_RESTRICT ) ? "\033[1;33m" : "", post_attr(hdr),
+// hdr->xmode & (POST_MARKED | POST_RESTRICT) ? "\033[m" : "");
+
+ if (hdr->xmode & POST_BOTTOM)
+ prints(" \033[1;33m¡¹\033[m%c%s%c%s", tag_char(hdr->chrono),
+ hdr->xmode & POST_MARKED && hdr->xmode & POST_RESTRICT ? "\033[1m" : "", post_attr(hdr),
+ hdr->xmode & POST_MARKED && hdr->xmode & POST_RESTRICT ? "\033[m" : "");
+ else
+ prints("%6d%c%s%c%s", num, tag_char(hdr->chrono),
+ hdr->xmode & POST_MARKED && hdr->xmode & POST_RESTRICT ? "\033[1m" : "", post_attr(hdr),
+ hdr->xmode & POST_MARKED && hdr->xmode & POST_RESTRICT ? "\033[m" : "");
+
+ if (hdr->xmode & POST_SCORE)
+ {
+ num = hdr->score;
+// prints("\033[1;3%cm%c\033[m ", num >= 0 ? '1' : '2', scorelist[abs(num)]);
+ if (num < 36 && num > -36) /* qazq.031013: ¥i¥H±À¨ì"Ãz"*/
+ prints("\033[1;3%cm%c\033[m ", num >= 0 ? '1' : '2', scorelist[abs(num)]);
+ else
+ prints("\033[1;3%s\033[m", num >= 0 ? "1m¡Û" : "2m¡Û");
+
+ }
+ else
+ {
+ outs(" ");
+ }
+ hdr_outs(hdr, d_cols + 46); /* ¤Ö¤@®æ¨Ó©ñ¤À¼Æ */
+#else
+ prints("%6d%c%c ", (hdr->xmode & POST_BOTTOM) ? -1 : num, tag_char(hdr->chrono), post_attr(hdr));
+ hdr_outs(hdr, d_cols + 47);
+#endif
+}
+
+
+static int
+post_body(xo)
+ XO *xo;
+{
+ HDR *hdr;
+ int num, max, tail;
+
+ max = xo->max;
+ if (max <= 0)
+ {
+ if (bbstate & STAT_POST)
+ {
+ if (vans("­n·s¼W¸ê®Æ¶Ü(Y/N)¡H[N] ") == 'y')
+ return post_add(xo);
+ }
+ else
+ {
+ vmsg("¥»¬ÝªO©|µL¤å³¹");
+ }
+ return XO_QUIT;
+ }
+
+ hdr = (HDR *) xo_pool;
+ num = xo->top;
+ tail = num + XO_TALL;
+ if (max > tail)
+ max = tail;
+
+ move(3, 0);
+ do
+ {
+ post_item(++num, hdr++);
+ } while (num < max);
+ clrtobot();
+
+ /* return XO_NONE; */
+ return XO_FOOT; /* itoc.010403: §â b_lines ¶ñ¤W feeter */
+}
+
+
+static int
+post_head(xo)
+ XO *xo;
+{
+ vs_head(currBM, xo->xyz);
+ prints(NECKER_POST, d_cols, "",
+ currbattr & BRD_NOPHONETIC ? "¢®" : "¡³",
+ currbattr & BRD_NOSCORE ? "¢®" : "¡³", bshm->mantime[currbno]);
+ return post_body(xo);
+}
+
+
+/* ----------------------------------------------------- */
+/* ¸ê®Æ¤§ÂsÄý¡Gbrowse / history */
+/* ----------------------------------------------------- */
+
+
+static int
+post_visit(xo)
+ XO *xo;
+{
+ int ans, row, max;
+ HDR *hdr;
+
+ ans = vans("³]©w©Ò¦³¤å³¹ (U)¥¼Åª (V)¤wŪ (W)«e¤wŪ«á¥¼Åª (Q)¨ú®ø¡H[Q] ");
+ if (ans == 'v' || ans == 'u' || ans == 'w')
+ {
+ row = xo->top;
+ max = xo->max - row + 3;
+ if (max > b_lines)
+ max = b_lines;
+
+ hdr = (HDR *) xo_pool + (xo->pos - row);
+ /* brh_visit(ans == 'w' ? hdr->chrono : ans == 'u'); */
+ /* weiyu.041010: ¦b¸m©³¤å¤W¿ï w µø¬°¥þ³¡¤wŪ */
+ brh_visit((ans == 'u') ? 1 : (ans == 'w' && !(hdr->xmode & POST_BOTTOM)) ? hdr->chrono : 0);
+
+ hdr = (HDR *) xo_pool;
+
+#if 0
+ row = 3;
+ do
+ {
+ move(row, 7);
+ outc(post_attr(hdr++));
+ } while (++row < max);
+#endif
+ return post_body(xo);
+ }
+ return XO_FOOT;
+}
+
+
+static void
+post_history(xo, hdr) /* ±N hdr ³o½g¥[¤J brh */
+ XO *xo;
+ HDR *hdr;
+{
+ time_t prev, chrono, next;
+ int pos, top;
+ char *dir;
+ HDR buf;
+
+ if (hdr->xmode & POST_BOTTOM) /* ¸m©³¤å¤£¥[¤J¾\Ū°O¿ý */
+ return;
+
+ chrono = hdr->chrono;
+ if (!brh_unread(chrono)) /* ¦pªG¤w¦b brh ¤¤¡A´NµL»Ý°Ê§@ */
+ return;
+
+ dir = xo->dir;
+ pos = xo->pos;
+ top = xo->top;
+
+ pos--;
+ if (pos >= top)
+ {
+ prev = hdr[-1].chrono;
+ }
+ else
+ {
+ /* amaki.040302.µù¸Ñ: ¦bµe­±¥H¤W¡A¥u¦nŪµwºÐ */
+ if (!rec_get(dir, &buf, sizeof(HDR), pos))
+ prev = buf.chrono;
+ else
+ prev = chrono;
+ }
+
+ pos += 2;
+ if (pos < top + XO_TALL && pos < xo->max)
+ {
+ next = hdr[1].chrono;
+ }
+ else
+ {
+ /* amaki.040302.µù¸Ñ: ¦bµe­±¥H¤U¡A¥u¦nŪµwºÐ */
+ if (!rec_get(dir, &buf, sizeof(HDR), pos))
+ next = buf.chrono;
+ else
+ next = chrono;
+ }
+
+ brh_add(prev, chrono, next);
+}
+
+
+static int
+post_browse(xo)
+ XO *xo;
+{
+ HDR *hdr;
+ int xmode, pos, key;
+ char *dir, fpath[64];
+
+ dir = xo->dir;
+
+ for (;;)
+ {
+ pos = xo->pos;
+ hdr = (HDR *) xo_pool + (pos - xo->top);
+ xmode = hdr->xmode;
+
+#ifdef HAVE_REFUSEMARK
+ if ((xmode & POST_RESTRICT) &&
+ strcmp(hdr->owner, cuser.userid) && !(bbstate & STAT_BM))
+ break;
+#endif
+
+ hdr_fpath(fpath, dir, hdr);
+
+ /* Thor.990204: ¬°¦Ò¼{more ¶Ç¦^­È */
+ if ((key = more(fpath, FOOTER_POST)) < 0)
+ break;
+
+ post_history(xo, hdr);
+ strcpy(currtitle, str_ttl(hdr->title));
+
+re_key:
+ switch (xo_getch(xo, key))
+ {
+ case XO_BODY:
+ continue;
+
+ case 'y':
+ case 'r':
+ if (bbstate & STAT_POST)
+ {
+ if (do_reply(xo, hdr) == XO_INIT) /* ¦³¦¨¥\¦a post ¥X¥h¤F */
+ return post_init(xo);
+ }
+ break;
+
+ case 'm':
+ if ((bbstate & STAT_BOARD) && !(xmode & POST_MARKED))
+ {
+ /* hdr->xmode = xmode ^ POST_MARKED; */
+ /* ¦b post_browse ®É¬Ý¤£¨ì m °O¸¹¡A©Ò¥H­­¨î¥u¯à mark */
+ hdr->xmode = xmode | POST_MARKED;
+ currchrono = hdr->chrono;
+ rec_put(dir, hdr, sizeof(HDR), pos, cmpchrono);
+ }
+ break;
+
+#ifdef HAVE_SCORE
+ case '%':
+ post_score(xo);
+ return post_init(xo);
+#endif
+
+ case '/':
+ if (vget(b_lines, 0, "·j´M¡G", hunt, sizeof(hunt), DOECHO))
+ {
+ more(fpath, FOOTER_POST);
+ goto re_key;
+ }
+ continue;
+
+ case 'E':
+ return post_edit(xo);
+
+ case 'C': /* itoc.000515: post_browse ®É¥i¦s¤J¼È¦sÀÉ */
+ {
+ FILE *fp;
+ if (fp = tbf_open())
+ {
+ f_suck(fp, fpath);
+ fclose(fp);
+ }
+ }
+ break;
+
+ case 'h':
+ xo_help("post");
+ break;
+ }
+ break;
+ }
+
+ return post_head(xo);
+}
+
+
+/* ----------------------------------------------------- */
+/* ºëµØ°Ï */
+/* ----------------------------------------------------- */
+
+
+static int
+post_gem(xo)
+ XO *xo;
+{
+ int level;
+ char fpath[64];
+
+ strcpy(fpath, "gem/");
+ strcpy(fpath + 4, xo->dir);
+
+ level = 0;
+ if (bbstate & STAT_BOARD)
+ level ^= GEM_W_BIT;
+ if (HAS_PERM(PERM_SYSOP))
+ level ^= GEM_X_BIT;
+ if (bbstate & STAT_BM)
+ level ^= GEM_M_BIT;
+
+ XoGem(fpath, "ºëµØ°Ï", level);
+ return post_init(xo);
+}
+
+
+/* ----------------------------------------------------- */
+/* ¶iªOµe­± */
+/* ----------------------------------------------------- */
+
+
+static int
+post_memo(xo)
+ XO *xo;
+{
+ char fpath[64];
+
+ brd_fpath(fpath, currboard, fn_note);
+ /* Thor.990204: ¬°¦Ò¼{more ¶Ç¦^­È */
+ if (more(fpath, NULL) < 0)
+ {
+ vmsg("¥»¬ÝªO©|µL¡u¶iªOµe­±¡v");
+ return XO_FOOT;
+ }
+
+ return post_head(xo);
+}
+
+
+/* ----------------------------------------------------- */
+/* ¥\¯à¡Gtag / switch / cross / forward */
+/* ----------------------------------------------------- */
+
+
+static int
+post_tag(xo)
+ XO *xo;
+{
+ HDR *hdr;
+ int tag, pos, cur;
+
+ pos = xo->pos;
+ cur = pos - xo->top;
+ hdr = (HDR *) xo_pool + cur;
+
+ if (xo->key == XZ_XPOST)
+ pos = hdr->xid;
+
+ if (tag = Tagger(hdr->chrono, pos, TAG_TOGGLE))
+ {
+ move(3 + cur, 6);
+ outc(tag > 0 ? '*' : ' ');
+ }
+
+ /* return XO_NONE; */
+ return xo->pos + 1 + XO_MOVE; /* lkchu.981201: ¸õ¦Ü¤U¤@¶µ */
+}
+
+
+static int
+post_switch(xo)
+ XO *xo;
+{
+ int bno;
+ BRD *brd;
+ char bname[BNLEN + 1];
+
+ if (brd = ask_board(bname, BRD_R_BIT, NULL))
+ {
+ if ((bno = brd - bshm->bcache) >= 0 && currbno != bno)
+ {
+ XoPost(bno);
+ return XZ_POST;
+ }
+ }
+ else
+ {
+ vmsg(err_bid);
+ }
+ return post_head(xo);
+}
+
+
+int
+post_cross(xo)
+ XO *xo;
+{
+ /* ¨Ó·½¬ÝªO */
+ char *dir, *ptr;
+ HDR *hdr, xhdr;
+
+ /* ±ýÂà¥hªº¬ÝªO */
+ int xbno;
+ usint xbattr;
+ char xboard[BNLEN + 1], xfolder[64];
+ HDR xpost;
+
+ int tag, rc, locus, finish;
+ int method; /* 0:­ì¤åÂà¸ü 1:±q¤½¶}¬ÝªO/ºëµØ°Ï/«H½cÂà¿ý¤å³¹ 2:±q¯µ±K¬ÝªOÂà¿ý¤å³¹ */
+ usint tmpbattr;
+ char tmpboard[BNLEN + 1];
+ char fpath[64], buf[ANSILINELEN];
+ FILE *fpr, *fpw;
+
+ if (!cuser.userlevel) /* itoc.000213: Á×§K guest Âà¿ý¥h sysop ªO */
+ return XO_NONE;
+
+ tag = AskTag("Âà¿ý");
+ if (tag < 0)
+ return XO_FOOT;
+
+ dir = xo->dir;
+
+ if (!ask_board(xboard, BRD_W_BIT, "\n\n\033[1;33m½Ð¬D¿ï¾A·íªº¬ÝªO¡A¤Á¤ÅÂà¿ý¶W¹L¤TªO¡C\033[m\n\n") ||
+ (*dir == 'b' && !strcmp(xboard, currboard))) /* «H½c¡BºëµØ°Ï¤¤¥i¥HÂà¿ý¦Ücurrboard */
+ return XO_HEAD;
+
+ hdr = tag ? &xhdr : (HDR *) xo_pool + (xo->pos - xo->top); /* lkchu.981201: ¾ã§åÂà¿ý */
+
+ /* ­ì§@ªÌÂà¿ý¦Û¤v¤å³¹®É¡A¥i¥H¿ï¾Ü¡u­ì¤åÂà¸ü¡v */
+ method = ((!tag && !strcmp(hdr->owner, cuser.userid))) &&
+ (vget(2, 0, "(1)­ì¤åÂà¸ü (2)Âà¿ý¤å³¹¡H[1] ", buf, 3, DOECHO) != '2') ? 0 : 1;
+
+ if (!tag) /* lkchu.981201: ¾ã§åÂà¿ý´N¤£­n¤@¤@¸ß°Ý */
+ {
+ if (method)
+ sprintf(ve_title, "[Âà¿ý] %.65s", str_ttl(hdr->title)); /* ¤w¦³ Re:/Fw: ¦r¼Ë´N¥u­n¤@­Ó Fw: */
+ else
+ strcpy(ve_title, hdr->title);
+
+ if (!vget(2, 0, "¼ÐÃD¡G", ve_title, TTLEN + 1, GCARRY))
+ return XO_HEAD;
+ }
+
+#ifdef HAVE_REFUSEMARK
+ rc = vget(2, 0, "(S)¦sÀÉ (L)¯¸¤º (X)±K«Ê (Q)¨ú®ø¡H[Q] ", buf, 3, LCECHO);
+ if (rc != 'l' && rc != 's' && rc != 'x')
+#else
+ rc = vget(2, 0, "(S)¦sÀÉ (L)¯¸¤º (Q)¨ú®ø¡H[Q] ", buf, 3, LCECHO);
+ if (rc != 'l' && rc != 's')
+#endif
+ return XO_HEAD;
+
+ if (method && *dir == 'b') /* ±q¬ÝªOÂà¥X¡A¥ýÀˬd¦¹¬ÝªO¬O§_¬°¯µ±KªO */
+ {
+ /* ­É¥Î tmpbattr */
+ tmpbattr = (bshm->bcache + currbno)->readlevel;
+ if (tmpbattr == PERM_SYSOP || tmpbattr == PERM_BOARD)
+ method = 2;
+ }
+
+
+ xbno = brd_bno(xboard);
+ xbattr = (bshm->bcache + xbno)->battr;
+
+ int xmethod = 0;
+ /* ­É¥Î tmpbattr */
+ tmpbattr = (bshm->bcache + xbno)->readlevel;
+ if (tmpbattr == PERM_SYSOP || tmpbattr == PERM_BOARD)
+ xmethod = 2;
+
+
+ /* Thor.990111: ¦b¥i¥HÂà¥X«e¡A­nÀˬd¦³¨S¦³Âà¥XªºÅv¤O? */
+ if ((rc == 's') && (!HAS_PERM(PERM_INTERNET) || (xbattr & BRD_NOTRAN)))
+ rc = 'l';
+
+ /* ³Æ¥÷ currboard */
+ if (method)
+ {
+ /* itoc.030325: ¤@¯ëÂà¿ý©I¥s ve_header¡A·|¨Ï¥Î¨ì currboard¡Bcurrbattr¡A¥ý³Æ¥÷°_¨Ó */
+ strcpy(tmpboard, currboard);
+ strcpy(currboard, xboard);
+ tmpbattr = currbattr;
+ currbattr = xbattr;
+ }
+
+ locus = 0;
+ do /* lkchu.981201: ¾ã§åÂà¿ý */
+ {
+ if (tag)
+ {
+ EnumTag(hdr, dir, locus, sizeof(HDR));
+
+ if (method)
+ sprintf(ve_title, "[Âà¿ý] %.65s", str_ttl(hdr->title)); /* ¤w¦³ Re:/Fw: ¦r¼Ë´N¥u­n¤@­Ó Fw: */
+ else
+ strcpy(ve_title, hdr->title);
+ }
+
+ if (hdr->xmode & GEM_FOLDER) /* «D plain text ¤£¯àÂà */
+ continue;
+
+#ifdef HAVE_REFUSEMARK
+ if (hdr->xmode & POST_RESTRICT)
+ continue;
+#endif
+
+ hdr_fpath(fpath, dir, hdr);
+
+#ifdef HAVE_DETECT_CROSSPOST
+ if (check_crosspost(fpath, xbno))
+ break;
+#endif
+
+ brd_fpath(xfolder, xboard, fn_dir);
+
+ time_t now;
+ time (&now);
+
+ if (method) /* ¤@¯ëÂà¿ý */
+ {
+ /* itoc.030325: ¤@¯ëÂà¿ý­n­«·s¥[¤W header */
+ fpw = fdopen(hdr_stamp(xfolder, 'A', &xpost, buf), "w");
+ ve_header(fpw);
+
+ /* itoc.040228: ¦pªG¬O±qºëµØ°ÏÂà¿ý¥X¨Óªº¸Ü¡A·|Åã¥ÜÂà¿ý¦Û [currboard] ¬ÝªO¡A
+ µM¦Ó currboard ¥¼¥²¬O¸ÓºëµØ°Ïªº¬ÝªO¡C¤£¹L¤£¬O«Ü­«­nªº°ÝÃD¡A©Ò¥H´N¤£ºÞ¤F :p */
+ fprintf(fpw, "¡° ¥»¤åÂà¿ý¦Û [%s] %s\n\n",
+ *dir == 'u' ? cuser.userid : method == 2 ? "¯µ±K" : tmpboard,
+ *dir == 'u' ? "«H½c" : "¬ÝªO");
+
+ /* Kyo.051117: ­Y¬O±q¯µ±K¬ÝªOÂà¥Xªº¤å³¹¡A§R°£¤å³¹²Ä¤@¦æ©Ò°O¿ýªº¬ÝªO¦WºÙ */
+ finish = 0;
+ if ((method == 2) && (fpr = fopen(fpath, "r")))
+ {
+ if (fgets(buf, sizeof(buf), fpr) &&
+ ((ptr = strstr(buf, str_post1)) || (ptr = strstr(buf, str_post2))) && (ptr > buf))
+ {
+ ptr[-1] = '\n';
+ *ptr = '\0';
+
+ do
+ {
+ fputs(buf, fpw);
+ } while (fgets(buf, sizeof(buf), fpr));
+ finish = 1;
+ }
+ fclose(fpr);
+ }
+ if (!finish)
+ f_suck(fpw, fpath);
+
+ fprintf(fpw, "£p \033[1;30m%s \033[0;36mÂà\033[m:±q [%s] %s¡A¤_ %s \033[m\n",
+ cuser.userid,
+ *dir == 'u' ? cuser.userid : method == 2 ? "¬YÁôÂÃ" : tmpboard,
+ *dir == 'u' ? "«H½c" : "¬ÝªO",
+ Btime (&now));
+
+ fclose(fpw);
+
+ strcpy(xpost.owner, cuser.userid);
+ strcpy(xpost.nick, cuser.username);
+
+ if (*dir != 'u' && *dir !='g')
+ {
+ fpw = fopen(fpath, "a+");
+ fprintf (fpw, "£p \033[1;30m%s \033[0;36mÂà\033[m:¨ì [%s] ¬ÝªO¡A¤_ %s \033[m\n",
+ cuser.userid,
+// (xbattr == PERM_SYSOP || xbattr == PERM_BOARD) ? "¬YÁôÂÃ" : xboard,
+ xmethod == 2 ? "¬YÁôÂÃ" : xboard,
+ Btime (&now));
+ fclose (fpw);
+ }
+
+ }
+ else /* ­ì¤åÂà¿ý */
+ {
+ /* itoc.030325: ­ì¤åÂà¿ýª½±µ copy §Y¥i */
+ hdr_stamp(xfolder, HDR_COPY | 'A', &xpost, fpath);
+
+ strcpy(xpost.owner, hdr->owner);
+ strcpy(xpost.nick, hdr->nick);
+ strcpy(xpost.date, hdr->date); /* ­ì¤åÂà¸ü«O¯d­ì¤é´Á */
+ }
+
+ strcpy(xpost.title, ve_title);
+
+ if (rc == 's')
+ xpost.xmode = POST_OUTGO;
+#ifdef HAVE_REFUSEMARK
+ else if (rc == 'x')
+ xpost.xmode = POST_RESTRICT;
+#endif
+
+ rec_bot(xfolder, &xpost, sizeof(HDR));
+
+ if (rc == 's')
+ outgo_post(&xpost, xboard);
+ } while (++locus < tag);
+
+ btime_update(xbno);
+
+ /* Thor.981205: check ³QÂ઺ªO¦³¨S¦³¦C¤J¬ö¿ý? */
+ if (!(xbattr & BRD_NOCOUNT))
+ cuser.numposts += tag ? tag : 1; /* lkchu.981201: ­nºâ tag */
+
+ /* ´_­ì currboard¡Bcurrbattr */
+ if (method)
+ {
+ strcpy(currboard, tmpboard);
+ currbattr = tmpbattr;
+ }
+
+ vmsg("Âà¿ý§¹¦¨");
+ return XO_HEAD;
+}
+
+
+int
+post_forward(xo)
+ XO *xo;
+{
+ ACCT muser;
+ int pos;
+ HDR *hdr;
+
+ if (!HAS_PERM(PERM_LOCAL))
+ return XO_NONE;
+
+ pos = xo->pos;
+ hdr = (HDR *) xo_pool + (pos - xo->top);
+
+ if (hdr->xmode & GEM_FOLDER) /* «D plain text ¤£¯àÂà */
+ return XO_NONE;
+
+#ifdef HAVE_REFUSEMARK
+ if ((hdr->xmode & POST_RESTRICT) &&
+ strcmp(hdr->owner, cuser.userid) && !(bbstate & STAT_BM))
+ return XO_NONE;
+#endif
+
+ if (acct_get("Âà¹F«H¥óµ¹¡G", &muser) > 0)
+ {
+ strcpy(quote_user, hdr->owner);
+ strcpy(quote_nick, hdr->nick);
+ hdr_fpath(quote_file, xo->dir, hdr);
+ sprintf(ve_title, "%.64s (fwd)", hdr->title);
+ move(1, 0);
+ clrtobot();
+ prints("Âà¹Fµ¹: %s (%s)\n¼Ð ÃD: %s\n", muser.userid, muser.username, ve_title);
+
+ mail_send(muser.userid);
+ *quote_file = '\0';
+ }
+ return XO_HEAD;
+}
+
+
+/* ----------------------------------------------------- */
+/* ªO¥D¥\¯à¡Gmark / delete / label */
+/* ----------------------------------------------------- */
+
+
+static int
+post_mark(xo)
+ XO *xo;
+{
+ if (bbstate & STAT_BOARD)
+ {
+ HDR *hdr;
+ int pos, cur, xmode;
+
+ pos = xo->pos;
+ cur = pos - xo->top;
+ hdr = (HDR *) xo_pool + cur;
+ xmode = hdr->xmode;
+
+#ifdef HAVE_LABELMARK
+ if (xmode & POST_DELETE) /* «Ý¬åªº¤å³¹¤£¯à mark */
+ return XO_NONE;
+#endif
+
+ hdr->xmode = xmode ^ POST_MARKED;
+ currchrono = hdr->chrono;
+ rec_put(xo->dir, hdr, sizeof(HDR), xo->key == XZ_XPOST ? hdr->xid : pos, cmpchrono);
+
+// move(3 + cur, 7);
+// outc(post_attr(hdr));
+ move (3 + cur, 0);
+ post_item(pos + 1, hdr);
+ }
+// return XO_NONE;
+ return xo->pos + 1 + XO_MOVE; /* lkchu.981201: ¸õ¦Ü¤U¤@¶µ */
+
+}
+
+
+static int
+post_bottom(xo)
+ XO *xo;
+{
+
+ if (bbstate & STAT_BOARD)
+ {
+ HDR *hdr, post;
+ char fpath[64];
+
+#define MAX_BOTTOM 7
+ int fd, fsize;
+ struct stat st;
+
+ if ((fd = open(xo->dir, O_RDONLY)) >= 0)
+ {
+ if (!fstat(fd, &st))
+ {
+ fsize = st.st_size;
+ while ((fsize -= sizeof(HDR)) >= 0)
+ {
+ lseek(fd, fsize, SEEK_SET);
+ read(fd, &post, sizeof(HDR));
+ if (!(post.xmode & POST_BOTTOM))
+ break;
+ }
+ }
+ close(fd);
+ if ((st.st_size - fsize) / sizeof(HDR) > MAX_BOTTOM)
+ {
+ vmsg("¸m©³¤å¤£¯à¶W¹L 7 ½g¡C");
+ return XO_FOOT;
+ }
+ }
+#undef MAX_BOTTOM
+ hdr = (HDR *) xo_pool + (xo->pos - xo->top);
+
+ hdr_fpath(fpath, xo->dir, hdr);
+ hdr_stamp(xo->dir, HDR_LINK | 'A', &post, fpath);
+#ifdef HAVE_REFUSEMARK
+ post.xmode = POST_BOTTOM | (hdr->xmode & POST_RESTRICT);
+#else
+ post.xmode = POST_BOTTOM;
+#endif
+ strcpy(post.owner, hdr->owner);
+ strcpy(post.nick, hdr->nick);
+ strcpy(post.title, hdr->title);
+
+ rec_add(xo->dir, &post, sizeof(HDR));
+ /* btime_update(currbno); */ /* ¤£»Ý­n¡A¦]¬°¸m©³¤å³¹¤£¦C¤J¥¼Åª */
+
+ return post_load(xo); /* ¥ß¨èÅã¥Ü¸m©³¤å³¹ */
+ }
+ return XO_NONE;
+}
+
+
+#ifdef HAVE_REFUSEMARK
+static int
+post_refuse(xo) /* itoc.010602: ¤å³¹¥[±K */
+ XO *xo;
+{
+ HDR *hdr;
+ int pos, cur;
+
+ if (!cuser.userlevel) /* itoc.020114: guest ¤£¯à¹ï¨ä¥L guest ªº¤å³¹¥[±K */
+ return XO_NONE;
+
+ pos = xo->pos;
+ cur = pos - xo->top;
+ hdr = (HDR *) xo_pool + cur;
+
+ if (!strcmp(hdr->owner, cuser.userid) || (bbstate & STAT_BM))
+ {
+ hdr->xmode ^= POST_RESTRICT;
+ currchrono = hdr->chrono;
+ rec_put(xo->dir, hdr, sizeof(HDR), xo->key == XZ_XPOST ? hdr->xid : pos, cmpchrono);
+
+// move(3 + cur, 7);
+// outc(post_attr(hdr));
+ move (3 + cur, 0);
+ post_item(pos + 1, hdr);
+ }
+
+ return XO_NONE;
+}
+#endif
+
+
+#ifdef HAVE_LABELMARK
+static int
+post_label(xo)
+ XO *xo;
+{
+ if (bbstate & STAT_BOARD)
+ {
+ HDR *hdr;
+ int pos, cur, xmode;
+
+ pos = xo->pos;
+ cur = pos - xo->top;
+ hdr = (HDR *) xo_pool + cur;
+ xmode = hdr->xmode;
+
+ if (xmode & (POST_MARKED | POST_RESTRICT | POST_BOTTOM)) /* mark ©Î ¥[±Kªº¤å³¹¤£¯à«Ý¬å */
+ return XO_NONE;
+
+ hdr->xmode = xmode ^ POST_DELETE;
+ currchrono = hdr->chrono;
+ rec_put(xo->dir, hdr, sizeof(HDR), xo->key == XZ_XPOST ? hdr->xid : pos, cmpchrono);
+
+// move(3 + cur, 7);
+// outc(post_attr(hdr));
+ move (3 + cur, 0);
+ post_item(pos + 1, hdr);
+
+ return pos + 1 + XO_MOVE; /* ¸õ¦Ü¤U¤@¶µ */
+ }
+
+ return XO_NONE;
+}
+
+
+static int
+post_delabel(xo)
+ XO *xo;
+{
+ int fdr, fsize, xmode;
+ char fnew[64], fold[64], *folder;
+ HDR *hdr;
+ FILE *fpw;
+
+ if (!(bbstate & STAT_BOARD))
+ return XO_NONE;
+
+ if (vans("½T©w­n§R°£«Ý¬å¤å³¹¶Ü(Y/N)¡H[N] ") != 'y')
+ return XO_FOOT;
+
+ folder = xo->dir;
+ if ((fdr = open(folder, O_RDONLY)) < 0)
+ return XO_FOOT;
+
+ if (!(fpw = f_new(folder, fnew)))
+ {
+ close(fdr);
+ return XO_FOOT;
+ }
+
+ fsize = 0;
+ mgets(-1);
+ while (hdr = mread(fdr, sizeof(HDR)))
+ {
+ xmode = hdr->xmode;
+
+ if (!(xmode & POST_DELETE))
+ {
+ if ((fwrite(hdr, sizeof(HDR), 1, fpw) != 1))
+ {
+ close(fdr);
+ fclose(fpw);
+ unlink(fnew);
+ return XO_FOOT;
+ }
+ fsize++;
+ }
+ else
+ {
+ /* ³s½u¬å«H */
+ cancel_post(hdr);
+
+ hdr_fpath(fold, folder, hdr);
+ unlink(fold);
+ }
+ }
+ close(fdr);
+ fclose(fpw);
+
+ sprintf(fold, "%s.o", folder);
+ rename(folder, fold);
+ if (fsize)
+ rename(fnew, folder);
+ else
+ unlink(fnew);
+
+ btime_update(currbno);
+
+ return post_load(xo);
+}
+#endif
+
+
+static int
+post_delete(xo)
+ XO *xo;
+{
+ int pos, cur, by_BM;
+ HDR *hdr;
+ char buf[80];
+
+ if (!cuser.userlevel ||
+ !strcmp(currboard, BN_DELETED) ||
+ !strcmp(currboard, BN_JUNK))
+ return XO_NONE;
+
+ pos = xo->pos;
+ cur = pos - xo->top;
+ hdr = (HDR *) xo_pool + cur;
+
+ if ((hdr->xmode & POST_MARKED) ||
+ (!(bbstate & STAT_BOARD) && strcmp(hdr->owner, cuser.userid)))
+ return XO_NONE;
+
+ by_BM = bbstate & STAT_BOARD;
+
+ if (vans(msg_del_ny) == 'y')
+ {
+ currchrono = hdr->chrono;
+
+ if (!rec_del(xo->dir, sizeof(HDR), xo->key == XZ_XPOST ? hdr->xid : pos, cmpchrono))
+ {
+ pos = move_post(hdr, xo->dir, by_BM);
+
+ if (!by_BM && !(currbattr & BRD_NOCOUNT))
+ {
+ /* itoc.010711: ¬å¤å³¹­n¦©¿ú¡AºâÀɮפj¤p */
+ pos = pos >> 3; /* ¬Û¹ï©ó post ®É wordsnum / 10 */
+
+ /* itoc.010830.µù¸Ñ: º|¬}: ­Y multi-login ¬å¤£¨ì¥t¤@°¦ªº¿ú */
+ if (cuser.money > pos)
+ cuser.money -= pos;
+ else
+ cuser.money = 0;
+
+ if (hdr->xmode & POST_RECORDED)
+ {
+ if (cuser.numposts > 0)
+ cuser.numposts--;
+ sprintf(buf, "%s¡A±zªº¤å³¹´î¬° %d ½g", MSG_DEL_OK, cuser.numposts);
+ vmsg(buf);
+ }
+ }
+
+
+ if (xo->key == XZ_XPOST)
+ {
+ vmsg("­ì¦Cªí¸g§R°£«á²V¶Ã¡A½Ð­«¶i¦ê±µ¼Ò¦¡¡I");
+ return XO_QUIT;
+ }
+ return XO_LOAD;
+ }
+ }
+ return XO_FOOT;
+}
+
+
+static int
+chkpost(hdr)
+ HDR *hdr;
+{
+ return (hdr->xmode & POST_MARKED);
+}
+
+
+static int
+vfypost(hdr, pos)
+ HDR *hdr;
+ int pos;
+{
+ return (Tagger(hdr->chrono, pos, TAG_NIN) || chkpost(hdr));
+}
+
+
+static void
+delpost(xo, hdr)
+ XO *xo;
+ HDR *hdr;
+{
+ char fpath[64];
+
+ cancel_post(hdr);
+ hdr_fpath(fpath, xo->dir, hdr);
+ unlink(fpath);
+}
+
+
+static int
+post_rangedel(xo)
+ XO *xo;
+{
+ if (!(bbstate & STAT_BOARD))
+ return XO_NONE;
+
+ btime_update(currbno);
+
+ return xo_rangedel(xo, sizeof(HDR), chkpost, delpost);
+}
+
+
+static int
+post_prune(xo)
+ XO *xo;
+{
+ int ret;
+
+ if (!(bbstate & STAT_BOARD))
+ return XO_NONE;
+
+ ret = xo_prune(xo, sizeof(HDR), vfypost, delpost);
+
+ btime_update(currbno);
+
+ if (xo->key == XZ_XPOST && ret == XO_LOAD)
+ {
+ vmsg("­ì¦Cªí¸g§å¦¸§R°£«á²V¶Ã¡A½Ð­«¶i¦ê±µ¼Ò¦¡¡I");
+ return XO_QUIT;
+ }
+
+ return ret;
+}
+
+
+static int
+post_copy(xo) /* itoc.010924: ¨ú¥N gem_gather */
+ XO *xo;
+{
+ int tag;
+ HDR *hdr;
+
+ tag = AskTag("¬ÝªO¤å³¹«þ¨©");
+
+ if (tag < 0)
+ return XO_FOOT;
+
+ hdr = (HDR *) xo_pool + (xo->pos - xo->top);
+
+ gem_buffer(xo->dir, tag ? NULL : hdr);
+
+ if (bbstate & STAT_BOARD)
+ {
+#ifdef XZ_XPOST
+ if (xo->key == XZ_XPOST)
+ {
+ zmsg("Àɮ׼аO§¹¦¨¡C[ª`·N] ±z¥²¶·¥ýÂ÷¶}¦ê±µ¼Ò¦¡¤~¯à¶i¤JºëµØ°Ï¡C");
+ return XO_FOOT;
+ }
+ else
+#endif
+ {
+ zmsg("«þ¨©§¹¦¨¡A¦ý¬O¥[±K¤å³¹¤£·|³Q«þ¨©¡C[ª`·N] ¶K¤W«á¤~¯à§R°£­ì¤å¡I");
+ return post_gem(xo); /* «þ¨©§¹ª½±µ¶iºëµØ°Ï */
+ }
+ }
+
+ zmsg("Àɮ׼аO§¹¦¨¡C[ª`·N] ±z¥u¯à¦b¾á¥ô(¤p)ªO¥D©Ò¦b©Î­Ó¤HºëµØ°Ï¶K¤W¡C");
+ return XO_FOOT;
+}
+
+
+/* ----------------------------------------------------- */
+/* ¯¸ªø¥\¯à¡Gedit / title */
+/* ----------------------------------------------------- */
+
+
+int
+post_edit(xo)
+ XO *xo;
+{
+ char fpath[64];
+ HDR *hdr;
+ FILE *fp;
+
+ hdr = (HDR *) xo_pool + (xo->pos - xo->top);
+
+ hdr_fpath(fpath, xo->dir, hdr);
+
+#if 0
+ if (HAS_PERM(PERM_ALLBOARD)) /* ¯¸ªø­×§ï */
+ {
+#ifdef HAVE_REFUSEMARK
+ if ((hdr->xmode & POST_RESTRICT) && !(bbstate & STAT_BM) && strcmp(hdr->owner, cuser.userid))
+ return XO_NONE;
+#endif
+// vedit(fpath, 0);
+ if (!vedit(fpath, 0)) /* ­Y«D¨ú®ø«h¥[¤W­×§ï¸ê°T */
+ {
+ if (fp = fopen(fpath, "a"))
+ {
+ ve_banner(fp, 1);
+ fclose(fp);
+ }
+ }
+ }
+ else
+#endif
+ if (cuser.userlevel && !strcmp(hdr->owner, cuser.userid)) /* ­ì§@ªÌ­×§ï */
+ {
+ if (!vedit(fpath, 0)) /* ­Y«D¨ú®ø«h¥[¤W­×§ï¸ê°T */
+ {
+ if (fp = fopen(fpath, "a"))
+ {
+ ve_banner(fp, 1);
+ fclose(fp);
+ }
+ }
+ }
+ else /* itoc.010301: ´£¨Ñ¨Ï¥ÎªÌ­×§ï(¦ý¤£¯àÀx¦s)¨ä¥L¤Hµoªíªº¤å³¹ */
+ {
+#ifdef HAVE_REFUSEMARK
+ if (hdr->xmode & POST_RESTRICT)
+ return XO_NONE;
+#endif
+ vedit(fpath, -1);
+ }
+
+ /* return post_head(xo); */
+ return XO_HEAD; /* itoc.021226: XZ_POST ©M XZ_XPOST ¦@¥Î post_edit() */
+}
+
+
+void
+header_replace(xo, hdr) /* itoc.010709: ­×§ï¤å³¹¼ÐÃD¶¶«K­×§ï¤º¤åªº¼ÐÃD */
+ XO *xo;
+ HDR *hdr;
+{
+ FILE *fpr, *fpw;
+ char srcfile[64], tmpfile[64], buf[ANSILINELEN];
+
+ hdr_fpath(srcfile, xo->dir, hdr);
+ strcpy(tmpfile, "tmp/");
+ strcat(tmpfile, hdr->xname);
+ f_cp(srcfile, tmpfile, O_TRUNC);
+
+ if (!(fpr = fopen(tmpfile, "r")))
+ return;
+
+ if (!(fpw = fopen(srcfile, "w")))
+ {
+ fclose(fpr);
+ return;
+ }
+
+ fgets(buf, sizeof(buf), fpr); /* ¥[¤J§@ªÌ */
+ fputs(buf, fpw);
+
+ fgets(buf, sizeof(buf), fpr); /* ¥[¤J¼ÐÃD */
+ if (!str_ncmp(buf, "¼Ð", 2)) /* ¦pªG¦³ header ¤~§ï */
+ {
+ strcpy(buf, buf[2] == ' ' ? "¼Ð ÃD: " : "¼ÐÃD: ");
+ strcat(buf, hdr->title);
+ strcat(buf, "\n");
+ }
+ fputs(buf, fpw);
+
+ while(fgets(buf, sizeof(buf), fpr)) /* ¥[¤J¨ä¥L */
+ fputs(buf, fpw);
+
+ fclose(fpr);
+ fclose(fpw);
+ f_rm(tmpfile);
+}
+
+
+static int
+post_title(xo)
+ XO *xo;
+{
+ HDR *fhdr, mhdr;
+ int pos, cur;
+
+ if (!cuser.userlevel) /* itoc.000213: Á×§K guest ¦b sysop ªO§ï¼ÐÃD */
+ return XO_NONE;
+
+ pos = xo->pos;
+ cur = pos - xo->top;
+ fhdr = (HDR *) xo_pool + cur;
+ memcpy(&mhdr, fhdr, sizeof(HDR));
+
+ if (strcmp(cuser.userid, mhdr.owner) && !HAS_PERM(PERM_ALLADMIN))
+ return XO_NONE;
+
+ vget(b_lines, 0, "¼ÐÃD¡G", mhdr.title, TTLEN + 1, GCARRY);
+
+
+ if (HAS_PERM(PERM_ALLADMIN)) /* itoc.000213: ­ì§@ªÌ¥u¯à§ï¼ÐÃD */
+ {
+ vget(b_lines, 0, "§@ªÌ¡G", mhdr.owner, 73 /* sizeof(mhdr.owner) */, GCARRY);
+ /* Thor.980727: sizeof(mhdr.owner) = 80 ·|¶W¹L¤@¦æ */
+ vget(b_lines, 0, "¼ÊºÙ¡G", mhdr.nick, sizeof(mhdr.nick), GCARRY);
+ vget(b_lines, 0, "¤é´Á¡G", mhdr.date, sizeof(mhdr.date), GCARRY);
+ }
+
+ if (memcmp(fhdr, &mhdr, sizeof(HDR)) && vans(msg_sure_ny) == 'y')
+ {
+ memcpy(fhdr, &mhdr, sizeof(HDR));
+ currchrono = fhdr->chrono;
+ rec_put(xo->dir, fhdr, sizeof(HDR), xo->key == XZ_XPOST ? fhdr->xid : pos, cmpchrono);
+
+ move(3 + cur, 0);
+ post_item(++pos, fhdr);
+
+ /* itoc.010709: ­×§ï¤å³¹¼ÐÃD¶¶«K­×§ï¤º¤åªº¼ÐÃD */
+ header_replace(xo, fhdr);
+ }
+ return XO_FOOT;
+}
+
+
+/* ----------------------------------------------------- */
+/* ÃB¥~¥\¯à¡Gwrite / score */
+/* ----------------------------------------------------- */
+
+
+int
+post_write(xo) /* itoc.010328: ¥á½u¤W§@ªÌ¤ô²y */
+ XO *xo;
+{
+ if (HAS_PERM(PERM_PAGE))
+ {
+ HDR *hdr;
+ UTMP *up;
+
+ hdr = (HDR *) xo_pool + (xo->pos - xo->top);
+
+ if (!(hdr->xmode & POST_INCOME) && (up = utmp_seek(hdr)))
+ do_write(up);
+ }
+ return XO_NONE;
+}
+
+
+#ifdef HAVE_SCORE
+
+static int curraddscore;
+static int newchrono;
+static char *newxname;
+
+static void
+addscore(hdd, ram)
+ HDR *hdd, *ram;
+{
+ /* itoc.030618: ³y¤@­Ó·sªº chrono ¤Î xname */
+ hdd->chrono = newchrono;
+ strcpy(hdd->xname, newxname);
+
+ hdd->xmode |= POST_SCORE;
+ if (curraddscore == 1)
+ {
+ if (hdd->score < 36)
+ hdd->score++;
+ }
+ else if (curraddscore == -1)
+ {
+ if (hdd->score > -36)
+ hdd->score--;
+ }
+}
+
+static
+int post_mscore(xo)
+ XO *xo;
+{
+ char *blist;
+ BRD *brd;
+ brd = bshm->bcache + currbno;
+ blist = brd->BM;
+/*if(!HAS_PERM(PERM_ALLADMIN) || strcmp(brd->class,"¯¸°È")){
+ if (is_bm(blist, cuser.userid) != 1)
+ {
+ vmsg("¥u¦³¥¿ª©¥D¥i¥H­×§ï±À¤å¼Æ");
+ return XO_NONE;
+ }
+ }*/
+ if(!HAS_PERM(PERM_ALLBOARD)){
+ if(!is_bm(blist, cuser.userid)){
+ vmsg("±z¤£¬OªO¥D¡AµLªk­×§ï±À¤å¼Æ");
+ return XO_NONE;
+ }
+ if(!HAS_PERM(PERM_MSCORE)){
+ vmsg("±z¨S¦³­×§ï±À¤å¼ÆªºÅv­­");
+ return XO_NONE;
+ }
+ }
+ int pos = xo->pos;
+ int cur = pos - xo->top;
+ HDR* hdr = (HDR *) xo_pool + cur;
+ HDR post=*hdr;
+ char togask;
+ char scoremod=0;
+ togask=vans("¬O§_ÁôÂñÀ¤å¼Æ(Y/N)¡H[N] ");
+ if(!togask){
+ togask='n';
+ }
+ if(post.xmode & POST_SCORE){
+ if(togask == 'y'){
+ scoremod=1;
+ post.xmode ^= POST_SCORE;
+ }
+ }else{
+ if(togask == 'n'){
+ scoremod=1;
+ post.xmode ^= POST_SCORE;
+ }
+ }
+ char tmpscore[4];
+ sprintf(tmpscore,"%d",post.score);
+ vget(b_lines, 0, "±À¤å¼Æ(­Y¬°©A¤å½Ð³]©w­t¼Æ)¡G", tmpscore, 4, GCARRY);
+ post.score=atoi(tmpscore);
+ if(post.score > 36){
+ post.score = 36;
+ }else if(post.score < -36){
+ post.score = -36;
+ }
+ if(post.score != hdr->score){
+ scoremod=1;
+ }
+ if(scoremod && vans(msg_sure_ny)=='y'){
+ memcpy(hdr,&post,sizeof(HDR));
+ currchrono=post.chrono;
+ rec_put(xo->dir, hdr, sizeof(HDR), xo->pos, cmpchrono);
+ post_body(xo);
+ }
+ return XO_FOOT;
+}
+
+int
+post_score(xo)
+ XO *xo;
+{
+ static time_t next = 0; /* ¤U¦¸¥iµû¤À®É¶¡ */
+ HDR *hdr;
+ HDR post;
+ int pos, cur, ans, vtlen, maxlen;
+ char *dir, *userid, *verb, fpath[64], reason[80], vtbuf[12], prompt[20];
+ FILE *fp;
+#ifdef HAVE_ANONYMOUS
+ char uid[IDLEN + 1];
+#endif
+
+ if ((currbattr & BRD_NOSCORE) || !cuser.userlevel || !(bbstate & STAT_POST)) /* µû¤Àµø¦Pµoªí¤å³¹ */
+ return XO_NONE;
+
+ pos = xo->pos;
+ cur = pos - xo->top;
+ hdr = (HDR *) xo_pool + cur;
+
+#ifdef HAVE_REFUSEMARK
+ if ((hdr->xmode & POST_RESTRICT) &&
+ strcmp(hdr->owner, cuser.userid) && !(bbstate & STAT_BM))
+ return XO_NONE;
+#endif
+
+ switch (ans = vans("¡· µû¤À 0)±µ¸Ü 1)±À¤å 2)©A¤å 3)¦Û©w±À 4)¦Û©w©A¡H [Q] "))
+ {
+ case '0':
+ verb = "3m»¡";
+ vtlen = 2;
+ break;
+
+ case '1':
+ verb = "1m±À";
+ vtlen = 2;
+ break;
+
+ case '2':
+ verb = "2m©A";
+ vtlen = 2;
+ break;
+
+ case '3':
+ case '4':
+ if (!vget(b_lines, 0, "½Ð¿é¤J°Êµü¡G", fpath, 7, DOECHO))
+ return XO_FOOT;
+ vtlen = strlen(fpath);
+ sprintf(verb = vtbuf, "%cm%s", ans - 2, fpath);
+ break;
+
+ default:
+ return XO_FOOT;
+ }
+
+ if (ans != '0' && (next - time(NULL)) > 0)
+ {
+ sprintf(fpath, "ÁÙ¦³ %d ¬í¤~¯àµû¤À³á", next - time(NULL));
+ vmsg(fpath);
+ return XO_FOOT;
+ }
+
+#ifdef HAVE_ANONYMOUS
+ if (currbattr & BRD_ANONYMOUS)
+ maxlen = 63 - IDLEN - vtlen;
+ else
+#endif
+ maxlen = 63 - strlen(cuser.userid) - vtlen;
+
+#ifdef HAVE_ANONYMOUS
+ if (currbattr & BRD_ANONYMOUS)
+ {
+ userid = uid;
+ if (!vget(b_lines, 0, "½Ð¿é¤J±z·Q¥ÎªºID¡A¤]¥iª½±µ«ö[Enter]¡A©Î¬O«ö[r]¥Î¯u¦W¡G", userid, IDLEN, DOECHO))
+ userid = STR_ANONYMOUS;
+ else if (userid[0] == 'r' && userid[1] == '\0')
+ userid = cuser.userid;
+ else
+ strcat(userid, "."); /* ¦Û©wªº¸Ü¡A³Ì«á¥[ '.' */
+ maxlen = 63 - strlen(userid) - vtlen;
+ }
+ else
+#endif
+ userid = cuser.userid;
+
+ sprintf (prompt, "¡÷ %s %-*s:", userid, strlen(verb) - 2, "");
+
+ if (!vget(b_lines, 0, prompt, reason, maxlen, DOECHO))
+ return XO_FOOT;
+
+
+ dir = xo->dir;
+ hdr_fpath(fpath, dir, hdr);
+
+ if (fp = fopen(fpath, "a"))
+ {
+ time_t now;
+ struct tm *ptime;
+
+ time(&now);
+ ptime = localtime(&now);
+
+ fprintf(fp, "¡÷ \033[36m%s \033[3%s\033[m:%-*s%02d%02d %02d:%02d\n",
+ userid, verb, maxlen, reason,
+ ptime->tm_mon + 1, ptime->tm_mday, ptime->tm_hour, ptime->tm_min);
+ fclose(fp);
+ }
+
+ ans -= '0';
+ curraddscore = 0;
+ if (ans == 0) /* »¡¸Ü */
+ {
+ curraddscore = 0;
+ }
+ if (ans == 1 || ans == 3) /* ¥[¤À */
+ {
+ if (hdr->score <= 35)
+ curraddscore = 1;
+ }
+ else if (ans == 2 || ans == 4) /* ¦©¤À */
+ {
+ if (hdr->score >= -35)
+ curraddscore = -1;
+ }
+
+ if (curraddscore)
+ next = time(NULL) + 3; /* ¨C 3 ¬í¤è¥iµû¤À¤@¦¸ */
+
+ /* ­««Ø¤@­Ó post */
+ hdr_fpath(fpath, dir, hdr);
+ hdr_stamp(dir, HDR_LINK | 'A', &post, fpath);
+
+/* ¤£¬å±¼­ì¥ýªº¤å³¹ÀɮסA¦pªG¨ä¥L¤H xo_pool ¬Oªº¡A¤]¤£·|¥X²{¼È®É©Ê¥ÛÀY¤å?
+ ¤SÂà«H out.bntp ¤¤¼gªºÀɦW¤]¬O¦W¡A©Ò¥H«O¯dÂÂÀÉ¥i¨ÏÂà«H¦¨¥\¡F
+ ¦ý³o¼Ë·|¯d¤U .DIR ¤¤¨S¦³«ü¦VªºÂÂÀÉ¡A¥u¦n¦b expire.c ¤¤²M°£¡C */
+/* unlink(fpath); */
+
+ currchrono = hdr->chrono;
+ newchrono = post.chrono;
+ newxname = post.xname;
+ rec_ref(dir, hdr, sizeof(HDR), xo->key == XZ_XPOST ?
+ hdr->xid : pos, cmpchrono, addscore);
+
+ /* ¥[¤J¦Û¤vªº¾\Ū°O¿ý */
+ brh_add(newchrono, newchrono, newchrono);
+ btime_update(currbno);
+
+ return XO_LOAD;
+
+}
+#endif /* HAVE_SCORE */
+
+
+static int
+post_help(xo)
+ XO *xo;
+{
+ xo_help("post");
+ /* return post_head(xo); */
+ return XO_HEAD; /* itoc.001029: »P xpost_help ¦@¥Î */
+}
+
+static int
+post_redir(xo)
+ XO *xo;
+{
+
+ char *blist;
+ BRD *brd;
+
+ brd = bshm->bcache + currbno;
+
+ blist = brd->BM;
+ if (strcmp(brd->class, "¯¸°È") || !HAS_PERM(PERM_ALLADMIN))
+ {
+ if (is_bm(blist, cuser.userid) != 1) /* ¥u¦³¥¿ªO¥D¥i¥H */
+ {
+ vmsg("¥u¦³¥¿ª©¥D¥i¥H­««Ø¬ÝªO¯Á¤Þ");
+ return XO_NONE;
+ }
+ }
+
+ if (bbstate & STAT_BOARD)
+ {
+ if (vans("½T©w­««Ø¬ÝªO¯Á¤Þ¶Ü¡A¤£©úÁA¦¹¥\\¯à°õ·N¨Ï¥ÎªÌ«áªG¦Û­t[N]¡H(y/N) ") == 'y')
+ {
+ if (vans("¯uªº­n­««Ø³á¡H¤å³¹§Ç·|¶Ã±¼¡B±À¤å¼Æ¦r·|¤£¨£³é¡I[N] («ör°õ¦æ¤§) ") == 'r')
+ {
+ char fpath[64];
+ brd_fpath(fpath, brd->brdname, fn_lock);
+ if(fork()==0){
+ pid_t child=fork();
+ if(child==0){
+ char buf[100];
+ sprintf (buf, "/home/bbs/brd/%s", currboard);
+ chdir(buf);
+ execl(BBSHOME"/bin/redir", "redir", "-b", NULL);
+ vmsg("µLªk°õ¦æ¯Á¤Þ­««Øµ{¦¡");
+ exit(1);
+ }else{
+ brd_setlockinfo(fpath, '0', cuser.userid, "¥¿¦b°õ¦æ¬ÝªO¯Á¤Þ­««Ø");
+ int status;
+ waitpid(child,&status,0);
+ unlink(fpath);
+ exit(WEXITSTATUS(status));
+ }
+ }
+ return XO_INIT;
+ }
+ }
+ else
+ {
+ return XO_INIT;
+ }
+ }
+ return XO_NONE;
+}
+
+static int
+post_move(xo)
+ XO *xo;
+{
+char *blist;
+ BRD *brd;
+
+ brd = bshm->bcache + currbno;
+
+ blist = brd->BM;
+
+ if(!HAS_PERM(PERM_ALLADMIN) || strcmp(brd->class,"¯¸°È")){
+ if (is_bm(blist, cuser.userid) != 1) /* ¥u¦³¥¿ªO¥D¥i¥H */
+ {
+ vmsg("¥u¦³¥¿ª©¥D¥i¥H²¾°Ê¤å³¹");
+ return;
+ }
+ }
+ HDR *hdr;
+ char *dir, buf[40];
+ int pos, newOrder;
+
+ pos = xo->pos;
+ hdr = (HDR *) xo_pool + (pos - xo->top);
+
+ sprintf(buf, "½Ð¿é¤J²Ä %d ¿ï¶µªº·s¦ì¸m¡G", pos + 1);
+ if (!vget(b_lines, 0, buf, buf, 5, DOECHO))
+ return XO_FOOT;
+
+ newOrder = atoi(buf) - 1;
+ if (newOrder < 0)
+ newOrder = 0;
+ else if (newOrder >= xo->max)
+ newOrder = xo->max - 1;
+
+ dir = xo->dir;
+ if (newOrder != pos)
+ {
+ if (!rec_del(dir, sizeof(HDR), pos, NULL))
+ {
+ rec_ins(dir, hdr, sizeof(HDR), newOrder, 1);
+ xo->pos = newOrder;
+ return XO_LOAD;
+ }
+ }
+
+ return XO_FOOT;
+}
+
+static int post_ainfo(xo)
+ XO *xo;
+{
+ char *blist;
+ BRD *brd;
+ brd = bshm->bcache + currbno;
+ blist = brd->BM;
+ int pos = xo->pos;
+ int cur = pos - xo->top;
+ HDR* hdr = (HDR *) xo_pool + cur;
+ move(19,0);
+ clrtobot(),refresh();
+ outs("\n");
+ prints("\033[1;33m[¤å³¹¼ÐÃD]\033[m %s\n",hdr->title);
+ outs("\033[1;33m[¤å³¹µû¤À]\033[m ");
+ if(hdr->score <= -36){
+ outs("³Q¼NÃz");
+ }else if(hdr->score >= 36){
+ outs("³Q±ÀÃz");
+ }else{
+ prints("%hhd",hdr->score);
+ }
+ move(21,30);
+ outs("\033[1;33m[ÀɮצWºÙ]\033[m ");
+ struct stat st;
+ char fpath[80],tosend[20];
+ sprintf(tosend,"%c/%s",hdr->xname[7],hdr->xname);
+ brd_fpath(fpath,brd->brdname,tosend);
+ if(HAS_PERM(PERM_ALLADMIN) || is_bm(blist,cuser.userid)) {
+ outs(hdr->xname);
+ move(21,65);
+ outs("\033[1;33m[Àɮפj¤p]\033[m ");
+ if(stat(fpath,&st)){
+ outs("Àɮ׬ÛÃö¸ê°TŪ¨ú¥¢±Ñ");
+ }else{
+ prints("%d",st.st_size);
+ }
+ }else{
+ outs("¥u¦³ªO¥D¥i¥HÆ[¬Ý");
+ }
+ outs("\n\033[1;33m[¤å³¹ÄÝ©Ê]\033[m ");
+ outs((hdr->xmode & POST_MARKED) ? "\033[1;32m¦³\033[m" : "\033[1;31mµL\033[m" );
+ outs(" mark ");
+ outs((hdr->xmode & POST_DELETE) ? "\033[1;32m¬O\033[m" : "\033[1;31m¤£¬O\033[m" );
+ outs("«Ý¬å¤å³¹ ");
+ outs((hdr->xmode & POST_SCORE) ? "\033[1;32m¦³\033[m" : "\033[1;31mµL\033[m" );
+ outs("µû¤À ");
+ outs((hdr->xmode & POST_RECORDED) ? "\033[1;32m¦³\033[m" : "\033[1;31mµL\033[m" );
+ outs("½Z¶O ");
+ outs((hdr->xmode & POST_BOTTOM) ? "\033[1;32m¬O\033[m" : "\033[1;31m¤£¬O\033[m" );
+ outs("¸m©³¤å³¹ ");
+ outs((hdr->xmode & POST_INCOME) ? "\033[1;32m¬O\033[m" : "\033[1;31m¤£¬O\033[m" );
+ outs("¯¸¥~«H ");
+ outs((hdr->xmode & POST_OUTGO) ? "\033[1;32m­n\033[m" : "\033[1;31m¤£\033[m" );
+ outs("Âà«H¥X¥h");
+ vmsg(NULL);
+ return post_init(xo);
+}
+
+static int post_binfo(xo)
+ XO *xo;
+{
+ BRD *brd;
+ brd = bshm->bcache + currbno;
+ do_binfo(brd);
+ return post_init(xo);
+}
+
+void do_binfo(brd)
+ BRD* brd;
+{
+ move(1,0);
+ clrtobot();
+ prints("\n\n\033[1;33m[¬ÝªO¦WºÙ]\033[m %s"
+ "\n\033[1;33m[¬ÝªO»¡©ú]\033[m [%s] %s"
+ "\n\033[1;33m[¬ÝªOÃþ«¬]\033[m %s"
+#ifdef HAVE_MODERATED_BOARD
+ "\n\033[1;33m[µoªíÅv­­]\033[m %s"
+#endif
+ "\n\033[1;33m[ªO¥D¦W³æ]\033[m %s"
+ ,brd->brdname, brd->class, brd->title
+#ifdef HAVE_MODERATED_BOARD
+ ,(brd->readlevel == PERM_SYSOP) ? "ÁôÂìݪO" :
+ (brd->readlevel == PERM_BOARD) ? "¦n¤Í¬ÝªO" :
+ (!brd->readlevel) ? "¤½¶}¬ÝªO" : "¯S®íÅv­­¬ÝªO"
+#endif
+ ,(!brd->postlevel) ? "©Ò¦³±b¸¹§¡¥iµoªí" :
+ (brd->postlevel == PERM_BASIC) ? "°£ guest ¥~§¡¥iµoªí" :
+ (brd->postlevel == PERM_POST) ? "³q¹L¨­¤À»{ÃÒ¤~¥iµoªí" : "¯S®íµoªíÅv­­"
+ ,brd->BM);
+ struct tm* tmptp;
+ tmptp=localtime(&(brd->bstamp));
+ prints("\n\033[1;33m[¬ÝªO«Ø¥ß®É¶¡]\033[m %04d/%02d/%02d %02d:%02d:%02d",
+ tmptp->tm_year+1900,tmptp->tm_mon+1,tmptp->tm_mday,tmptp->tm_hour,tmptp->tm_min,tmptp->tm_sec);
+ tmptp=localtime(&(brd->blast));
+ prints("\n\033[1;33m[³Ì«áµoªí®É¶¡]\033[m %04d/%02d/%02d %02d:%02d:%02d",
+ tmptp->tm_year+1900,tmptp->tm_mon+1,tmptp->tm_mday,tmptp->tm_hour,tmptp->tm_min,tmptp->tm_sec);
+ prints("\n\033[1;33m[¤å³¹Á`¼Æ]\033[m %d"
+ "\n\033[1;33m[§ë²¼ª¬ºA]\033[m %s"
+ ,brd->bpost,(brd->bvote == -1) ? "¦³½ä½L" : (brd->bvote == 1) ? "¦³§ë²¼" : "µL§ë²¼");
+ outs("\n\033[1;33m[Âê©wª¬ºA]\033[m ");
+ char fpath[64];
+ brd_fpath(fpath, brd->brdname, fn_lock);
+ struct stat tmpstat;
+ if(stat(fpath, &tmpstat)<0){
+ outs("¥¼Âê©w");
+ }else{
+ char userid[IDLEN + 1], reason[73];
+ char type;
+ brd_getlockinfo(fpath, &type, userid, reason);
+ prints("¥Ø«e¥Ñ %s Âê©w", userid);
+ prints("\n\033[1;33m[Âê©w²z¥Ñ]\033[m %s", reason);
+ }
+ outs("\n\n\033[1;33m[¬ÝªOÄÝ©Ê]\033[m ");
+ outs((brd->battr & BRD_NOTRAN) ? "\033[1;31mµL" : "\033[1;32m¦³" );
+ outs("\033[mÂà«H\n ");
+ outs((brd->battr & BRD_LOCAL) ? "\033[1;31m©Úµ´" : "\033[1;32m±µ¨ü" );
+ outs("\033[m¯¸¥~±H«Hµo¤å\n ");
+ outs((brd->battr & BRD_NOSTAT) ? "\033[1;31m¤£" : "\033[1;32m­n" );
+ outs("\033[m²Î­p¼öªù¸ÜÃD\n ");
+ outs((brd->battr & BRD_NOVOTE) ? "\033[1;31m¤£" : "\033[1;32m­n" );
+ outs("\033[m¤½§i§ë²¼µ²ªG\n ");
+ outs((brd->battr & BRD_ANONYMOUS) ? "\033[1;32m¥i" : "\033[1;31m¤£¥i" );
+ outs("\033[m°Î¦Wµo¤å\n ");
+#ifdef HAVE_SCORE
+ outs((brd->battr & BRD_NOSCORE) ? "\033[1;31m¤£¥i" : "\033[1;32m¥i" );
+ outs("\033[mµû¤À\n ");
+#endif
+#ifdef ANTI_PHONETIC
+ outs((brd->battr & BRD_NOPHONETIC) ? "\033[1;31m¤£¥i" : "\033[1;32m¥i" );
+ outs("\033[m¨Ï¥Îª`­µ¤å\n ");
+#endif
+ vmsg(NULL);
+}
+
+KeyFunc post_cb[] =
+{
+ XO_INIT, post_init,
+ XO_LOAD, post_load,
+ XO_HEAD, post_head,
+ XO_BODY, post_body,
+
+ 'r', post_browse,
+ 's', post_switch,
+ KEY_TAB, post_gem,
+ 'z', post_gem,
+
+ 'y', post_reply,
+ 'd', post_delete,
+ 'v', post_visit,
+ 'x', post_cross, /* ¦b post/mbox ¤¤³£¬O¤p¼g x Âà¬ÝªO¡A¤j¼g X Âà¨Ï¥ÎªÌ */
+ 'X', post_forward,
+ 't', post_tag,
+ 'E', post_edit,
+ 'T', post_title,
+ 'm', post_mark,
+ '_', post_bottom,
+ '*', post_bottom,
+ 'D', post_rangedel,
+
+#ifdef HAVE_SCORE
+ '%', post_score,
+ '#', post_mscore,
+#endif
+
+ 'w', post_write,
+
+ 'b', post_memo,
+ 'c', post_copy,
+ 'g', gem_gather,
+
+ 'I', post_ainfo,
+ 'W', post_binfo,
+
+
+ Ctrl('P'), post_add,
+ Ctrl('D'), post_prune,
+ Ctrl('Q'), xo_uquery,
+ Ctrl('O'), xo_usetup,
+
+#ifdef HAVE_REFUSEMARK
+ Ctrl('Y'), post_refuse,
+#endif
+
+#ifdef HAVE_LABELMARK
+ 'n', post_label,
+ Ctrl('N'), post_delabel,
+#endif
+
+ 'B' | XO_DL, (void *) "bin/manage.so:post_manage",
+ 'R' | XO_DL, (void *) "bin/vote.so:vote_result",
+ 'V' | XO_DL, (void *) "bin/vote.so:XoVote",
+
+#ifdef HAVE_TERMINATOR
+ Ctrl('X') | XO_DL, (void *) "bin/manage.so:post_terminator",
+#endif
+
+ '~', XoXselect, /* itoc.001220: ·j´M§@ªÌ/¼ÐÃD */
+ 'S', XoXsearch, /* itoc.001220: ·j´M¬Û¦P¼ÐÃD¤å³¹ */
+ 'a', XoXauthor, /* itoc.001220: ·j´M§@ªÌ */
+ '/', XoXtitle, /* itoc.001220: ·j´M¼ÐÃD */
+ 'f', XoXfull, /* itoc.030608: ¥þ¤å·j´M */
+ 'G', XoXmark, /* itoc.010325: ·j´M mark ¤å³¹ */
+ 'L', XoXlocal, /* itoc.010822: ·j´M¥»¦a¤å³¹ */
+ 'i', XoXrestrict, /* chensc.060827: ·j´M¥[±K¤å³¹ */
+ 'l', XoXscore, /* chensc.060827: ·j´M³Q±ÀÂˤ峹 */
+
+#ifdef HAVE_XYNEWS
+ 'u', XoNews, /* itoc.010822: ·s»D¾\Ū¼Ò¦¡ */
+#endif
+
+ Ctrl('G'), post_redir,
+ 'M', post_move,
+ 'h', post_help
+};
+
+
+KeyFunc xpost_cb[] =
+{
+ XO_INIT, xpost_init,
+ XO_LOAD, xpost_load,
+ XO_HEAD, xpost_head,
+ XO_BODY, post_body, /* Thor.980911: ¦@¥Î§Y¥i */
+
+ 'r', xpost_browse,
+ 'y', post_reply,
+ 't', post_tag,
+ 'x', post_cross,
+ 'X', post_forward,
+ 'c', post_copy,
+ 'g', gem_gather,
+ 'm', post_mark,
+ 'd', post_delete, /* Thor.980911: ¤è«KªO¥D */
+ 'E', post_edit, /* itoc.010716: ´£¨Ñ XPOST ¤¤¥i¥H½s¿è¼ÐÃD¡B¤å³¹¡A¥[±K */
+ 'T', post_title,
+#ifdef HAVE_SCORE
+ '%', post_score,
+ '#', post_mscore,
+#endif
+ 'w', post_write,
+#ifdef HAVE_REFUSEMARK
+ Ctrl('Y'), post_refuse,
+#endif
+#ifdef HAVE_LABELMARK
+ 'n', post_label,
+#endif
+
+ '~', XoXselect,
+ 'S', XoXsearch,
+ 'a', XoXauthor,
+ '/', XoXtitle,
+ 'f', XoXfull,
+ 'G', XoXmark,
+ 'L', XoXlocal,
+ 'i', XoXrestrict,
+ 'l', XoXscore,
+
+ 'I', post_ainfo,
+ 'W', post_binfo,
+
+ Ctrl('P'), post_add,
+ Ctrl('D'), post_prune,
+ Ctrl('Q'), xo_uquery,
+ Ctrl('O'), xo_usetup,
+
+ 'h', post_help /* itoc.030511: ¦@¥Î§Y¥i */
+};
+
+
+#ifdef HAVE_XYNEWS
+KeyFunc news_cb[] =
+{
+ XO_INIT, news_init,
+ XO_LOAD, news_load,
+ XO_HEAD, news_head,
+ XO_BODY, post_body,
+
+ 'r', XoXsearch,
+
+ 'h', post_help /* itoc.030511: ¦@¥Î§Y¥i */
+};
+#endif /* HAVE_XYNEWS */
diff --git a/maple/talk.c b/maple/talk.c
new file mode 100644
index 0000000..d5f654f
--- /dev/null
+++ b/maple/talk.c
@@ -0,0 +1,1424 @@
+/*-------------------------------------------------------*/
+/* talk.c ( NTHU CS MapleBBS Ver 3.00 ) */
+/*-------------------------------------------------------*/
+/* target : talk/query routines */
+/* create : 95/03/29 */
+/* update : 97/03/29 */
+/*-------------------------------------------------------*/
+
+
+#define _MODES_C_
+
+
+#include "bbs.h"
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+
+extern UCACHE *ushm;
+
+
+typedef struct
+{
+ int curcol, curln;
+ int sline, eline;
+#ifdef HAVE_MULTI_BYTE
+ int zhc;
+#endif
+} talk_win;
+
+
+/* ------------------------------------- */
+/* ¯u¹ê°Ê§@ */
+/* ------------------------------------- */
+
+
+char *
+bmode(up, simple)
+ UTMP *up;
+ int simple;
+{
+ static char modestr[32];
+ int mode;
+ char *word, *mateid;
+
+ word = ModeTypeTable[mode = up->mode];
+
+ if (simple)
+ return word;
+
+#ifdef BMW_COUNT
+ if (simple = up->bmw_count) /* ­É¥Î simple */
+ {
+ sprintf(modestr, "%d ­Ó¤ô²y", simple);
+ return modestr;
+ }
+#endif
+
+
+ if (mode < M_TALK || mode > M_IDLE) /* M_TALK(§t) »P M_IDLE(§t) ¶¡±µ mateid */
+ return word;
+
+ mateid = up->mateid;
+
+ if (mode == M_TALK)
+ {
+ /* itoc.020829: up ¦b Talk ®É¡A­Y up->mateid Áô§Î«h¬Ý¤£¨£ */
+ if (!utmp_get(0, mateid))
+ mateid = "µL¦W¤ó";
+ }
+
+// sprintf(modestr, "%s:%s", word, mateid);
+ sprintf (modestr, "%s", word);
+ return modestr;
+}
+
+
+static void
+showplans(userid)
+ char *userid;
+{
+ int i;
+ FILE *fp;
+ char buf[ANSILINELEN];
+
+ usr_fpath(buf, userid, fn_plans);
+ if (fp = fopen(buf, "r"))
+ {
+ i = MAXQUERYLINES;
+ while (i-- && fgets(buf, sizeof(buf), fp))
+ outx(buf);
+ fclose(fp);
+ }
+}
+
+static void
+do_query(acct)
+ ACCT *acct;
+{
+ UTMP *up;
+ int userno, rich;
+ char *userid;
+ char fortune[4][9] = {"¨ª³h¤^¤¢", "¤@¯ë­ÓÅé", "®a¹Ò¤p±d", "°]»Ö¦a¥D"};
+ BRD *pbrd = has_personalbrd (acct->userid);
+
+ utmp_mode(M_QUERY);
+
+ userno = acct->userno;
+ userid = acct->userid;
+// strcpy(cutmp->mateid, userid);
+
+ up = utmp_find(userno);
+ rich = acct->money >= 1000000 ? (acct->gold >= 100 ? 3 : 2) : (acct->money >= 50000 ? 1 : 0);
+
+ prints("\033[1;33m[±b¸¹]\033[m %-12s \033[1;33m[¼ÊºÙ]\033[m %-16.16s \033[1;33m[¤W¯¸]\033[m %5d ¦¸ \033[1;33m[¤å³¹]\033[m %5d ½g\n",
+ userid, acct->username, acct->numlogins, acct->numposts);
+
+ prints("\033[1;33m[»{ÃÒ]\033[m %s³q¹L»{ÃÒ \033[1;33m[°ÊºA]\033[m %-16.16s \033[1;33m[°]²£]\033[m %s \033[1;33m[­ÓªO]\033[m %s\n",
+ acct->userlevel & PERM_VALID ? "¤w¸g" : "©|¥¼",
+ (up && can_see(cutmp, up)) ? bmode(up, 1) : "¤£¦b¯¸¤W",
+ fortune[rich],
+ (pbrd && !strcmp(pbrd->class, "­Ó¤H") && pbrd->readlevel != PERM_SYSOP ? "¦³" : "¨S¦³©ÎÁôªO")/*,
+ (m_query(userid) & STATUS_BIFF) ? "¦³·s«H¥ó" : "³£¬Ý¹L¤F"*/ );
+
+ prints("\033[1;33m[¨Ó·½]\033[m (%s) %s\n",
+ Btime(&acct->lastlogin), acct->lasthost);
+
+/* prints ("\033[1;33m[¤W¯¸®É¼Æ]\033[m %s\n",
+ Bdifftime((int)difftime(time(NULL), acct->firstlogin)));*/
+
+ showplans(userid);
+ vmsg(NULL);
+}
+
+
+void
+my_query(userid)
+ char *userid;
+{
+ ACCT acct;
+
+ if (acct_load(&acct, userid) >= 0)
+ do_query(&acct);
+ else
+ vmsg(err_uid);
+}
+
+
+#ifdef HAVE_ALOHA
+static int
+chkfrienz(frienz)
+ FRIENZ *frienz;
+{
+ int userno;
+
+ userno = frienz->userno;
+ return (userno > 0 && userno == acct_userno(frienz->userid));
+}
+
+
+static int
+frienz_cmp(a, b)
+ FRIENZ *a, *b;
+{
+ return a->userno - b->userno;
+}
+
+
+void
+frienz_sync(fpath)
+ char *fpath;
+{
+ rec_sync(fpath, sizeof(FRIENZ), frienz_cmp, chkfrienz);
+}
+
+
+void
+aloha()
+{
+ UTMP *up;
+ int fd;
+ char fpath[64];
+ BMW bmw;
+ FRIENZ *frienz;
+ int userno;
+
+ usr_fpath(fpath, cuser.userid, FN_FRIENZ);
+
+ if ((fd = open(fpath, O_RDONLY)) >= 0)
+ {
+ bmw.caller = cutmp;
+ /* bmw.sender = cuser.userno; */ /* bmw.sender ¨Ì¹ï¤è¥i¥H call §Ú¡A«Ý·|¦A¨M©w */
+ strcpy(bmw.userid, cuser.userid);
+ strcpy(bmw.msg, "¡· ­è­è½ñ¶i"BBSNAME"ªºªù [¤W¯¸³qª¾] ¡·");
+
+ mgets(-1);
+ while (frienz = mread(fd, sizeof(FRIENZ)))
+ {
+ userno = frienz->userno;
+ up = utmp_find(userno);
+
+ if (up && (up->ufo & UFO_ALOHA) && !(up->status & STATUS_REJECT) && can_see(up, cutmp)) /* ¹ï¤è¬Ý¤£¨£§Ú¤£³qª¾ */
+ {
+ /* ¦n¤Í¥B¦Û¤v¨S¦³»·Â÷¹ÐÄÛ¤~¥i¥H reply */
+ bmw.sender = (is_mygood(userno) && !(cuser.ufo & UFO_QUIET)) ? cuser.userno : 0;
+ bmw.recver = userno;
+ bmw_send(up, &bmw);
+ }
+ }
+ close(fd);
+ }
+}
+#endif
+
+
+#ifdef LOGIN_NOTIFY
+extern LinkList *ll_head;
+
+
+int
+t_loginNotify()
+{
+ LinkList *wp;
+ BENZ benz;
+ char fpath[64];
+
+ /* ³]©w list ªº¦W³æ */
+
+ vs_bar("¨t²Î¨ó´Mºô¤Í");
+
+ ll_new();
+
+ if (pal_list(0))
+ {
+ wp = ll_head;
+ benz.userno = cuser.userno;
+ strcpy(benz.userid, cuser.userid);
+
+ do
+ {
+ if (strcmp(cuser.userid, wp->data)) /* ¤£¥i¨ó´M¦Û¤v */
+ {
+ usr_fpath(fpath, wp->data, FN_BENZ);
+ rec_add(fpath, &benz, sizeof(BENZ));
+ }
+ } while (wp = wp->next);
+
+ vmsg("¨ó´M³]©w§¹¦¨¡A¹ï¤è¤W¯¸®É¨t²Î·|³qª¾±z");
+ }
+ return 0;
+}
+
+
+void
+loginNotify()
+{
+ UTMP *up;
+ int fd;
+ char fpath[64];
+ BMW bmw;
+ BENZ *benz;
+ int userno;
+ int row, col; /* ­pºâ¦L¨ì­þ */
+
+ usr_fpath(fpath, cuser.userid, FN_BENZ);
+
+ if ((fd = open(fpath, O_RDONLY)) >= 0)
+ {
+ vs_bar("¨t²Î¨ó´Mºô¤Í");
+
+ bmw.caller = cutmp;
+ /* bmw.sender = cuser.userno; */ /* bmw.sender ¨Ì¹ï¤è¥i¥H call §Ú¡A«Ý·|¦A¨M©w */
+ strcpy(bmw.userid, cuser.userid);
+ strcpy(bmw.msg, "¡· ­è­è½ñ¶i"BBSNAME"ªºªù [¨t²Î¨ó´M] ¡·");
+
+ row = 1;
+ col = 0;
+ mgets(-1);
+ while (benz = mread(fd, sizeof(BENZ)))
+ {
+ /* ¦L¥X­þ¨Ç¤H¦b§ä§Ú */
+ if (row < b_lines)
+ {
+ move(row, col);
+ outs(benz->userid);
+ col += IDLEN + 1;
+ if (col > b_cols - IDLEN - 1) /* Á`¦@¥i¥H©ñ b_cols / (IDLEN + 1) Äæ */
+ {
+ row++;
+ col = 0;
+ }
+ }
+
+ userno = benz->userno;
+ up = utmp_find(userno);
+
+ if (up && !(up->status & STATUS_REJECT) && can_see(up, cutmp)) /* ¹ï¤è¬Ý¤£¨£§Ú¤£³qª¾ */
+ {
+ /* ¦n¤Í¥B¦Û¤v¨S¦³»·Â÷¹ÐÄÛ¤~¥i¥H reply */
+ bmw.sender = (is_mygood(userno) && !(cuser.ufo & UFO_QUIET)) ? cuser.userno : 0;
+ bmw.recver = userno;
+ bmw_send(up, &bmw);
+ outc('*'); /* Thor.980707: ¦³³qª¾¨ìªº¦³©Ò¤£¦P */
+ }
+ }
+ close(fd);
+ unlink(fpath);
+ vmsg("³o¨Ç¨Ï¥ÎªÌ³]±z¬°¤W¯¸¨ó´M¡A¥´ * ªí¥Ü¥Ø«e¦b¯¸¤W");
+ }
+}
+#endif
+
+
+#ifdef LOG_TALK
+static void
+talk_save()
+{
+ char fpath[64];
+
+ /* lkchu.981201: ©ñ¶i¨p¤H«H½c¤º/²M°£ */
+ usr_fpath(fpath, cuser.userid, FN_TALK_LOG);
+
+ if (!(cuser.ufo & UFO_NTLOG) && vans("¥»¦¸²á¤Ñ¬ö¿ý³B²z (M)³Æ§Ñ¿ý (C)²M°£¡H[M] ") != 'c')
+ mail_self(fpath, cuser.userid, "[³Æ §Ñ ¿ý] ²á¤Ñ¬ö¿ý", MAIL_READ | MAIL_NOREPLY);
+
+ unlink(fpath);
+}
+#endif
+
+
+/* ----------------------------------------------------- */
+/* talk sub-routines */
+/* ----------------------------------------------------- */
+
+
+static char page_requestor[40];
+#ifdef HAVE_MULTI_BYTE
+static int page_requestor_zhc;
+#endif
+
+#ifdef HAVE_MULTI_BYTE
+static uschar talk_pic[T_LINES][SCR_WIDTH + 1]; /* ¨C¦C¥i¥H¿é¤Jªº¦r¼Æ¬° SCR_WIDTH - 1 («O¯d¤G­ÓªÅ¥Õ) */
+#else
+static uschar talk_pic[T_LINES][SCR_WIDTH]; /* ¨C¦C¥i¥H¿é¤Jªº¦r¼Æ¬° SCR_WIDTH - 1 («O¯d¤@­ÓªÅ¥Õ) */
+#endif
+static int talk_len[T_LINES]; /* ¨C¦C¥Ø«e¤w¿é¤J¦h¤Ö¦r */
+
+
+static void
+talk_clearline(ln, col)
+ int ln, col;
+{
+ int i, len;
+
+ len = talk_len[ln];
+ for (i = col; i < len; i++)
+ talk_pic[ln][i] = ' ';
+
+ talk_len[ln] = col;
+}
+
+
+static void
+talk_outs(str, len)
+ uschar *str;
+ int len;
+{
+ int ch;
+ uschar *end;
+
+ /* ©M¤@¯ëªº outs() ¬O¬Û¦P¡A¥u¬O­­¨î¦L len ­Ó¦r */
+ end = str + len;
+ while (ch = *str)
+ {
+ outc(ch);
+ if (++str >= end)
+ break;
+ }
+}
+
+
+static void
+talk_nextline(twin)
+ talk_win *twin;
+{
+ int curln, max, len, i;
+
+ curln = twin->curln;
+ if (curln != twin->eline)
+ {
+ twin->curln = ++curln;
+ }
+ else /* ¤w¸g¬O³Ì«á¤@¦C¡A­n¦V¤W±²°Ê */
+ {
+ max = twin->eline;
+ for (curln = twin->sline; curln < max; curln++)
+ {
+ len = BMAX(talk_len[curln], talk_len[curln + 1]);
+ for (i = 0; i < len; i++)
+ talk_pic[curln][i] = talk_pic[curln + 1][i];
+ talk_len[curln] = talk_len[curln + 1];
+ move(curln, 0);
+ talk_outs(talk_pic[curln], talk_len[curln]);
+ clrtoeol();
+ }
+ }
+
+ /* ·sªº¤@¦C */
+ talk_clearline(curln, 0);
+
+ twin->curcol = 0;
+ move(curln, 0);
+ clrtoeol();
+}
+
+
+static void
+talk_char(twin, ch)
+ talk_win *twin;
+ int ch;
+{
+ int col, ln, len, i;
+
+ col = twin->curcol;
+ ln = twin->curln;
+ len = talk_len[ln];
+
+ if (isprint2(ch))
+ {
+ if (col >= SCR_WIDTH - 1) /* ­Y¤w¸g¥´¨ì¦C§À¡A¥ý´«¦C */
+ {
+ talk_nextline(twin);
+ col = twin->curcol;
+ ln = twin->curln;
+ len = talk_len[ln];
+ }
+
+ move(ln, col);
+ if (col >= len)
+ {
+ talk_pic[ln][col] = ch;
+ outc(ch);
+ twin->curcol = ++col;
+ talk_len[ln] = col;
+ }
+ else /* ­n insert */
+ {
+ for (i = SCR_WIDTH - 2; i > col; i--)
+ talk_pic[ln][i] = talk_pic[ln][i - 1];
+ talk_pic[ln][col] = ch;
+ if (len < SCR_WIDTH - 1)
+ len++;
+ talk_len[ln] = len;
+ talk_outs(talk_pic[ln] + col, len - col);
+ twin->curcol = ++col;
+ move(ln, col);
+ }
+ }
+ else
+ {
+ switch (ch)
+ {
+ case '\n':
+ talk_nextline(twin);
+ break;
+
+ case KEY_BKSP: /* backspace */
+ if (col > 0)
+ {
+ if (col > len)
+ {
+ /* °µ©M KEY_LEFT ¤@¼Ëªº¨Æ */
+ twin->curcol = --col;
+ move(ln, col);
+ }
+ else
+ {
+ col--;
+#ifdef HAVE_MULTI_BYTE
+ /* hightman.060504: §PÂ_²{¦b§R°£ªº¦ì¸m¬O§_¬°º~¦rªº«á¥b¬q¡A­Y¬O§R¤G¦r¤¸ */
+ if (twin->zhc && col && IS_ZHC_LO(talk_pic[ln], col))
+ {
+ col--;
+ ch = 2;
+ }
+ else
+#endif
+ ch = 1;
+ for (i = col; i < SCR_WIDTH - 1; i++)
+ talk_pic[ln][i] = talk_pic[ln][i + ch];
+ move(ln, col);
+ talk_outs(talk_pic[ln] + col, len - col);
+ twin->curcol = col;
+ talk_len[ln] = len - ch;
+ move(ln, col);
+ }
+ }
+ break;
+
+ case Ctrl('D'): /* KEY_DEL */
+ if (col < len)
+ {
+#ifdef HAVE_MULTI_BYTE
+ /* hightman.060504: §PÂ_²{¦b§R°£ªº¦ì¸m¬O§_¬°º~¦rªº«e¥b¬q¡A­Y¬O§R¤G¦r¤¸ */
+ if (twin->zhc && col < len - 1 && IS_ZHC_HI(talk_pic[ln][col]))
+ ch = 2;
+ else
+#endif
+ ch = 1;
+ for (i = col; i < SCR_WIDTH - 1; i++)
+ talk_pic[ln][i] = talk_pic[ln][i + ch];
+ move(ln, col);
+ talk_outs(talk_pic[ln] + col, len - col);
+ talk_len[ln] = len - ch;
+ move(ln, col);
+ }
+ break;
+
+ case Ctrl('B'): /* KEY_LEFT */
+ if (col > 0)
+ {
+ col--;
+#ifdef HAVE_MULTI_BYTE
+ /* hightman.060504: ¥ª²¾®É¸I¨ìº~¦r²¾Âù®æ */
+ if (twin->zhc && col && IS_ZHC_LO(talk_pic[ln], col))
+ col--;
+#endif
+ twin->curcol = col;
+ move(ln, col);
+ }
+ break;
+
+ case Ctrl('F'): /* KEY_RIGHT */
+ if (col < SCR_WIDTH - 1)
+ {
+ col++;
+#ifdef HAVE_MULTI_BYTE
+ /* hightman.060504: ¥k²¾®É¸I¨ìº~¦r²¾Âù®æ */
+ if (twin->zhc && col < SCR_WIDTH - 1 && IS_ZHC_HI(talk_pic[ln][col - 1]))
+ col++;
+#endif
+ twin->curcol = col;
+ move(ln, col);
+ }
+ break;
+
+ case Ctrl('P'): /* KEY_UP */
+ if (ln > twin->sline)
+ {
+ twin->curln = --ln;
+#ifdef HAVE_MULTI_BYTE
+ /* hightman.060504: º~¦r¾ã¦r½Õ¸` */
+ if (twin->zhc && col < SCR_WIDTH - 1 && IS_ZHC_LO(talk_pic[ln], col))
+ col++;
+#endif
+ move(ln, col);
+ }
+ break;
+
+ case Ctrl('N'): /* KEY_DOWN */
+ if (ln < twin->eline)
+ {
+ twin->curln = ++ln;
+#ifdef HAVE_MULTI_BYTE
+ /* hightman.060504: º~¦r¾ã¦r½Õ¸` */
+ if (twin->zhc && col < SCR_WIDTH - 1 && IS_ZHC_LO(talk_pic[ln], col))
+ col++;
+#endif
+ move(ln, col);
+ }
+ break;
+
+ case Ctrl('A'): /* KEY_HOME */
+ twin->curcol = 0;
+ move(ln, 0);
+ break;
+
+ case Ctrl('E'): /* KEY_END */
+ twin->curcol = len;
+ move(ln, len);
+ break;
+
+ case Ctrl('Y'): /* clear this line */
+ talk_clearline(ln, 0);
+ twin->curcol = 0;
+ move(ln, 0);
+ clrtoeol();
+ break;
+
+ case Ctrl('K'): /* clear to end of line */
+ talk_clearline(ln, col);
+ move(ln, col);
+ clrtoeol();
+ break;
+
+ case Ctrl('G'): /* bell */
+ bell();
+ break;
+ }
+ }
+}
+
+
+static void
+talk_string(twin, str)
+ talk_win *twin;
+ uschar *str;
+{
+ int ch;
+
+ while (ch = *str)
+ {
+ talk_char(twin, ch);
+ str++;
+ }
+}
+
+
+static void
+talk_speak(fd)
+ int fd;
+{
+ talk_win mywin, itswin;
+ uschar data[80];
+ char buf[80];
+ int i, ch;
+#ifdef LOG_TALK
+ char mywords[80], itswords[80], itsuserid[40];
+ FILE *fp;
+
+#if 0 /* talk log ªº algo */
+ ¹ï¸Ü¨â¤è¤À§O¬° mywin & itswin, ¥u­n¨ä¤@¦³ recv ªº¸Ü´N·|°e¤W¹ïÀ³ªº win,
+ ©Ò¥H§Ú­Ì¥²¶·­n¥ý§â¦Û¤v¥´ªº¦r»P¹ï¤è¥´ªº¦r¤À¶}¨Ó.
+
+ ©ó¬O´N¥ý«Ø¨â­Ó spool, ¤À§O±N mywin/itswin recv ªº char ©¹¦U¦Ûªº spool
+ ¸Ì¥á, ¥Ø«e³] spool ­è¦n¬O¤@¦Cªº¤j¤p, ©Ò¥H¥u­n¬O spool º¡¤F, ©Î¬O¸I¨ì´«
+ ¦æ¦r¤¸, ´N§â spool ¸Ìªº¸ê®Æ¼g¦^ log, µM«á²M±¼ spool, ¦p¦¹Ä~Äò :)
+#endif
+
+ /* lkchu: make sure that's empty */
+ mywords[0] = itswords[0] = '\0';
+
+ strcpy(itsuserid, page_requestor);
+ strtok(itsuserid, " (");
+#endif
+
+ utmp_mode(M_TALK);
+
+ ch = 58 - strlen(page_requestor);
+
+ sprintf(buf, "%s¡i%s", cuser.userid, cuser.username);
+
+ i = ch - strlen(buf);
+ if (i >= 0)
+ {
+ i = (i >> 1) + 1;
+ }
+ else
+ {
+ buf[ch] = '\0';
+ i = 1;
+ }
+ memset(data, ' ', i);
+ data[i] = '\0';
+
+ memset(&mywin, 0, sizeof(mywin));
+ memset(&itswin, 0, sizeof(itswin));
+
+ i = b_lines >> 1;
+ mywin.eline = i - 1;
+#ifdef HAVE_MULTI_BYTE
+ mywin.zhc = cuser.ufo & UFO_ZHC;
+#endif
+ itswin.curln = itswin.sline = i + 1;
+ itswin.eline = b_lines - 1;
+#ifdef HAVE_MULTI_BYTE
+ itswin.zhc = page_requestor_zhc;
+#endif
+
+ clear();
+ move(i, 0);
+ prints("\033[1;46;37m ½Í¤Ñ»¡¦a \033[45m%s%s¡j ¡» %s%s\033[m",
+ data, buf, page_requestor, data);
+ outf(FOOTER_TALK);
+ move(0, 0);
+
+ /* talk_pic °O¿ý¾ã­Óµe­±ªº¤å¦r¡Aªì©l­È¬OªÅ¥Õ */
+ memset(talk_pic, ' ', sizeof(talk_pic));
+ /* talk_len °O¿ý¾ã­Óµe­±¦U¦C¤w¸g¥Î¤F¦h¤Ö¦r */
+ memset(talk_len, 0, sizeof(talk_len));
+
+#ifdef LOG_TALK /* lkchu.981201: ²á¤Ñ°O¿ý */
+ usr_fpath(buf, cuser.userid, FN_TALK_LOG);
+ if (fp = fopen(buf, "a+"))
+ {
+ fprintf(fp, "¡i %s »P %s ¤§²á¤Ñ°O¿ý ¡j\n", cuser.userid, page_requestor);
+ fprintf(fp, "¶}©l²á¤Ñ®É¶¡ [%s]\n", Now()); /* itoc.010108: °O¿ý¶}©l²á¤Ñ®É¶¡ */
+ }
+#endif
+
+ add_io(fd, 60);
+
+ for (;;)
+ {
+ ch = vkey();
+
+#ifdef EVERY_Z
+ /* Thor.980725: talk¤¤, ctrl-z */
+ if (ch == Ctrl('Z'))
+ {
+ char buf[IDLEN + 1];
+ screenline slt[T_LINES];
+
+ /* Thor.980731: ¼È¦s mateid, ¦]¬°¥X¥h®É¥i¯à·|¥Î±¼ mateid */
+ strcpy(buf, cutmp->mateid);
+
+ vio_save(); /* Thor.980727: ¼È¦s vio_fd */
+ vs_save(slt);
+ every_Z(0);
+ vs_restore(slt);
+ vio_restore(); /* Thor.980727: ÁÙ­ì vio_fd */
+
+ /* Thor.980731: ÁÙ­ì mateid, ¦]¬°¥X¥h®É¥i¯à·|¥Î±¼ mateid */
+ strcpy(cutmp->mateid, buf);
+ continue;
+ }
+#endif
+
+ if (ch == Ctrl('D') || ch == Ctrl('C'))
+ break;
+
+ if (ch == I_OTHERDATA)
+ {
+ ch = recv(fd, data, 80, 0);
+ if (ch <= 0)
+ break;
+
+#ifdef HAVE_GAME
+ if (data[0] == Ctrl('O'))
+ { /* Thor.990219: ©I¥s¥~±¾´Ñ½L */
+ if (DL_func("bin/bwboard.so:vaBWboard", fd, 1) == -2)
+ break;
+ continue;
+ }
+#endif
+ for (i = 0; i < ch; i++)
+ {
+ talk_char(&itswin, data[i]);
+
+#ifdef LOG_TALK /* ¹ï¤è»¡ªº¸Ü */
+ switch (data[i])
+ {
+ case '\n':
+ /* lkchu.981201: ¦³´«¦C´N§â itswords ¦L¥X²M±¼ */
+ if (itswords[0] != '\0')
+ {
+ fprintf(fp, "\033[32m%s¡G%s\033[m\n", itsuserid, itswords);
+ itswords[0] = '\0';
+ }
+ break;
+
+ case KEY_BKSP: /* lkchu.981201: backspace */
+ itswords[strlen(itswords) - 1] = '\0';
+ break;
+
+ default:
+ if (isprint2(data[i]))
+ {
+ if (strlen(itswords) < sizeof(itswords))
+ {
+ strncat(itswords, (char *)&data[i], 1);
+ }
+ else /* lkchu.981201: itswords ¸Ëº¡¤F */
+ {
+ fprintf(fp, "\033[32m%s¡G%s%c\033[m\n", itsuserid, itswords, data[i]);
+ itswords[0] = '\0';
+ }
+ }
+ break;
+ }
+#endif
+
+ }
+ }
+
+#ifdef HAVE_GAME
+ else if (ch == Ctrl('O'))
+ { /* Thor.990219: ©I¥s¥~±¾´Ñ½L */
+ data[0] = ch;
+ if (send(fd, data, 1, 0) != 1)
+ break;
+ if (DL_func("bin/bwboard.so:vaBWboard", fd, 0) == -2)
+ break;
+ }
+#endif
+
+ else if (ch == Ctrl('T'))
+ {
+ if (cuser.userlevel) /* guest ¦³¥i¯à³Q¯¸ªøÁܽРTalk */
+ {
+ cuser.ufo ^= UFO_PAGER;
+ cutmp->ufo = cuser.ufo;
+ talk_string(&mywin, (cuser.ufo & UFO_PAGER) ? "¡» Ãö³¬©I¥s¾¹\n" : "¡» ¥´¶}©I¥s¾¹\n");
+ }
+ }
+
+ else
+ {
+ switch (ch)
+ {
+ case KEY_DEL:
+ ch = Ctrl('D');
+ break;
+
+ case KEY_LEFT:
+ 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;
+
+ case KEY_HOME:
+ ch = Ctrl('A');
+ break;
+
+ case KEY_END:
+ ch = Ctrl('E');
+ break;
+ }
+
+ data[0] = ch;
+ if (send(fd, data, 1, 0) != 1)
+ break;
+
+ talk_char(&mywin, ch);
+
+#ifdef LOG_TALK /* ¦Û¤v»¡ªº¸Ü */
+ switch (ch)
+ {
+ case '\n':
+ if (mywords[0] != '\0')
+ {
+ fprintf(fp, "%s¡G%s\n", cuser.userid, mywords);
+ mywords[0] = '\0';
+ }
+ break;
+
+ case KEY_BKSP:
+ mywords[strlen(mywords) - 1] = '\0';
+ break;
+
+ default:
+ if (isprint2(ch))
+ {
+ if (strlen(mywords) < sizeof(mywords))
+ {
+ strncat(mywords, (char *)&ch, 1);
+ }
+ else
+ {
+ fprintf(fp, "%s¡G%s%c\n", cuser.userid, mywords, ch);
+ mywords[0] = '\0';
+ }
+ }
+ break;
+ }
+#endif
+
+#ifdef EVERY_BIFF
+ /* Thor.980805: ¦³¤H¦b®ÇÃä«öenter¤~»Ý­ncheck biff */
+ if (ch == '\n')
+ {
+ static int old_biff;
+ int biff = HAS_STATUS(STATUS_BIFF);
+ if (biff && !old_biff)
+ talk_string(&mywin, "¡» ¾´¡I¶l®t¨Ó«ö¹a¤F¡I\n");
+ old_biff = biff;
+ }
+#endif
+ }
+ }
+
+#ifdef LOG_TALK
+ /* itoc.021205: ³Ì«á¤@¥y¸Ü­Y¨S¦³«ö ENTER ´N¤£·|³Q°O¿ý¶i talk.log¡A
+ ¦b¦¹¯S§O³B²z¡A¶¶§Ç©w¬°¥ý myword ¦A itsword¡A¦ý¥i¯à·|¬Û¤Ï */
+ if (mywords[0] != '\0')
+ fprintf(fp, "%s¡G%s\n", cuser.userid, mywords);
+ if (itswords[0] != '\0')
+ fprintf(fp, "\033[32m%s¡G%s\033[m\n", itsuserid, itswords);
+
+ fclose(fp);
+#endif
+
+ add_io(0, 60);
+}
+
+
+static void
+talk_hangup(sock)
+ int sock;
+{
+ cutmp->sockport = 0;
+ add_io(0, 60);
+ close(sock);
+}
+
+
+static char *talk_reason[] =
+{
+ "¹ï¤£°_¡A§Ú¦³¨Æ±¡¤£¯à¸ò±z talk",
+ "§Ú²{¦b«Ü¦£¡A½Ðµ¥¤@·|¨à¦A call §Ú",
+ "²{¦b¦£¤£¹L¨Ó¡Aµ¥¤@¤U§Ú·|¥D°Ê page ±z",
+ "§Ú²{¦b¤£·Q talk °Õ",
+ "«Ü·Ð«¨¡A§Ú¹ê¦b¤£·Q talk",
+
+#ifdef EVERY_Z
+ "§Úªº¼L¤Ú¥¿¦£µÛ©M§O¤HÁ¿¸Ü©O¡A¨S¦³ªÅªº¼L¤Ú¤F"
+ /* Thor.980725: for chat&talk ¥Î^z §@·Ç³Æ */
+#endif
+};
+
+
+/* return 0: ¨S¦³ talk, 1: ¦³ talk, -1: ¨ä¥L */
+
+
+int
+talk_page(up)
+ UTMP *up;
+{
+ int sock, msgsock;
+ struct sockaddr_in sin;
+ pid_t pid;
+ int ans, length;
+ char buf[60];
+#if defined(__OpenBSD__)
+ struct hostent *h;
+#endif
+
+#ifdef EVERY_Z
+ /* Thor.980725: ¬° talk & chat ¥i¥Î ^z §@·Ç³Æ */
+ if (vio_holdon())
+ {
+ vmsg("±zÁ¿¸ÜÁ¿¤@¥bÁÙ¨SÁ¿§¹­C");
+ return 0;
+ }
+#endif
+
+ pid = up->mode;
+ if (pid >= M_SYSTEM && pid <= M_CHAT)
+ {
+ vmsg("¹ï¤èµL·v²á¤Ñ");
+ return 0;
+ }
+
+ if (!(pid = up->pid) || kill(pid, 0))
+ {
+ vmsg(MSG_USR_LEFT);
+ return 0;
+ }
+
+ /* showplans(up->userid); */
+
+ if (vans("½T©w­n©M¥L/¦o½Í¤Ñ¶Ü(Y/N)¡H[N] ") != 'y')
+ return 0;
+
+ sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (sock < 0)
+ return 0;
+
+#if defined(__OpenBSD__) /* lkchu */
+
+ if (!(h = gethostbyname(str_host)))
+ return -1;
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_port = 0;
+ memcpy(&sin.sin_addr, h->h_addr, h->h_length);
+
+#else
+
+ sin.sin_family = AF_INET;
+ sin.sin_port = 0;
+ sin.sin_addr.s_addr = INADDR_ANY;
+ memset(sin.sin_zero, 0, sizeof(sin.sin_zero));
+
+#endif
+
+ length = sizeof(sin);
+ if (bind(sock, (struct sockaddr *) &sin, length) < 0 ||
+ getsockname(sock, (struct sockaddr *) &sin, &length) < 0)
+ {
+ close(sock);
+ return 0;
+ }
+
+ cutmp->sockport = sin.sin_port;
+ strcpy(cutmp->mateid, up->userid);
+ up->talker = cutmp;
+ utmp_mode(M_PAGE);
+ kill(pid, SIGUSR1);
+
+ clear();
+ prints("­º«×©I¥s %s ...\n¥i«ö Ctrl-D ¤¤¤î", up->userid);
+
+ listen(sock, 1);
+ add_io(sock, 20);
+ do
+ {
+ msgsock = igetch();
+
+ if (msgsock == Ctrl('D'))
+ {
+ talk_hangup(sock);
+ return -1;
+ }
+
+ if (msgsock == I_TIMEOUT)
+ {
+ move(0, 0);
+ outs("¦A");
+ bell();
+
+ if (kill(pid, SIGUSR1))
+ /* Thor.990201.µù¸Ñ: ¨ä¹ê³o­Ó kill¡A¤]¥u¬O¬Ý¬Ý¹ï¤è¬O¤£¬OÁÙ¦b½u¤W¦Ó¤w¡A­«µosignal¨ä¹ê¦ü¥G talk_rqst ¤£·|¦A³Q¥s */
+ {
+ talk_hangup(sock);
+ vmsg(MSG_USR_LEFT);
+ return -1;
+ }
+ }
+ } while (msgsock != I_OTHERDATA);
+
+ msgsock = accept(sock, NULL, NULL);
+ talk_hangup(sock);
+ if (msgsock == -1)
+ return -1;
+
+ length = read(msgsock, buf, sizeof(buf));
+ ans = buf[0];
+ if (ans == 'y')
+ {
+ sprintf(page_requestor, "%s (%s)", up->userid, up->username);
+#ifdef HAVE_MULTI_BYTE
+ page_requestor_zhc = up->ufo & UFO_ZHC;
+#endif
+
+ /* Thor.980814.ª`·N: ¦b¦¹¦³¤@­ÓÂû¦PÀnÁ¿ªº¥i¯à±¡ªp, ¦pªG A ¥ý page B, ¦ý¦b B ¦^À³«e«o°¨¤WÂ÷¶}, ´« page C,
+ C ©|¥¼¦^À³«e, ¦pªG B ¦^À³¤F, B ´N·|³Q accept, ¦Ó¤£¬O C.
+ ¦¹®É¦b¿Ã¹õ¤¤¥¡, ¬Ý¨ìªº page_requestor·|¬O C, ¥i¬O¨Æ¹ê¤W, talkªº¹ï¶H¬O B, ³y¦¨Âû¦PÀnÁ¿!
+ ¼È®É¤£¤©­×¥¿, ¥H§@¬°ªá¤ßªÌªºÃg»@ :P */
+
+ talk_speak(msgsock);
+ }
+ else
+ {
+ char *reply;
+
+ if (ans == ' ')
+ {
+ reply = buf;
+ reply[length] = '\0';
+ }
+ else
+ reply = talk_reason[ans - '1'];
+
+ move(4, 0);
+ outs("¡i¦^­µ¡j");
+ outs(reply);
+ }
+
+ close(msgsock);
+ cutmp->talker = NULL;
+#ifdef LOG_TALK
+ if (ans == 'y') /* itoc.000512: ¨¾¤îTalk³Q©Úµ´®É¡A²£¥Í²á¤Ñ°O¿ýªºrecord */
+ talk_save(); /* lkchu.981201: talk °O¿ý³B²z */
+#endif
+ vmsg("²á¤Ñµ²§ô");
+ return 1;
+}
+
+
+/* ----------------------------------------------------- */
+/* talk main-routines */
+/* ----------------------------------------------------- */
+
+
+int
+t_pager()
+{
+#if 0 /* itoc.010923: Á×§K»~«ö¡A¤À²M·¡¤@ÂI */
+ ¢z¢w¢w¢w¢w¢s¢w¢w¢w¢w¢w¢s¢w¢w¢w¢w¢w¢s¢w¢w¢w¢w¢w¢{
+ ¢x ¢x UFO_PAGER¢x UFO_RCVER¢x UFO_QUIET¢x
+ ¢u¢w¢w¢w¢w¢q¢w¢w¢w¢w¢w¢q¢w¢w¢w¢w¢w¢q¢w¢w¢w¢w¢w¢t
+ ¢x§¹¥þ¶}©ñ¢x ¢x ¢x ¢x
+ ¢u¢w¢w¢w¢w¢q¢w¢w¢w¢w¢w¢q¢w¢w¢w¢w¢w¢q¢w¢w¢w¢w¢w¢t
+ ¢x¦n¤Í±M½u¢x ¡³ ¢x ¢x ¢x
+ ¢u¢w¢w¢w¢w¢q¢w¢w¢w¢w¢w¢q¢w¢w¢w¢w¢w¢q¢w¢w¢w¢w¢w¢t
+ ¢x»·Â÷¹ÐÄÛ¢x ¡³ ¢x ¡³ ¢x ¡³ ¢x
+ ¢|¢w¢w¢w¢w¢r¢w¢w¢w¢w¢w¢r¢w¢w¢w¢w¢w¢r¢w¢w¢w¢w¢w¢}
+#endif
+
+ switch (vans("¤Á´«¦©¾÷¬° 1)§¹¥þ¶}©ñ 2)¦n¤Í±M½u 3)»·Â÷¹ÐÄÛ [Q] "))
+ {
+ case '1':
+#ifdef HAVE_NOBROAD
+ cuser.ufo &= ~(UFO_PAGER | UFO_RCVER | UFO_QUIET);
+#else
+ cuser.ufo &= ~(UFO_PAGER | UFO_QUIET);
+#endif
+ cutmp->ufo = cuser.ufo;
+ return 0; /* itoc.010923: ÁöµM¤£»Ý­n­«Ã¸¿Ã¹õ¡A¦ý¬O±j­¢­«Ã¸¤~¯à§ó·s feeter ¤¤ªº pager ¼Ò¦¡ */
+
+ case '2':
+ cuser.ufo |= UFO_PAGER;
+#ifdef HAVE_NOBROAD
+ cuser.ufo &= ~(UFO_RCVER | UFO_QUIET);
+#else
+ cuser.ufo &= ~UFO_QUIET;
+#endif
+ cutmp->ufo = cuser.ufo;
+ return 0;
+
+ case '3':
+#ifdef HAVE_NOBROAD
+ cuser.ufo |= (UFO_PAGER | UFO_RCVER | UFO_QUIET);
+#else
+ cuser.ufo |= (UFO_PAGER | UFO_QUIET);
+#endif
+ cutmp->ufo = cuser.ufo;
+ return 0;
+ }
+
+ return XEASY;
+}
+
+
+int
+t_cloak()
+{
+#ifdef HAVE_SUPERCLOAK
+ if (HAS_PERM(PERM_ALLADMIN))
+ {
+ switch (vans("Áô¨­¼Ò¦¡ 1)¤@¯ëÁô§Î 2)µµ¦âÁô§Î 3)¤£Áô§Î [Q] "))
+ {
+ case '1':
+ cuser.ufo |= UFO_CLOAK;
+ cuser.ufo &= ~UFO_SUPERCLOAK;
+ vmsg("¼K¼K¡A¸ú°_¨ÓÅo¡I");
+ break;
+
+ case '2':
+ cuser.ufo |= (UFO_CLOAK | UFO_SUPERCLOAK);
+ vmsg("¼K¼K¡AÂð_¨ÓÅo¡A¨ä¥L¯¸ªø¤]¬Ý¤£¨ì§Ú¡I");
+ break;
+
+ case '3':
+ cuser.ufo &= ~(UFO_CLOAK | UFO_SUPERCLOAK);
+ vmsg("­«²{¦¿´ò¤F");
+ break;
+ }
+ }
+ else
+#endif
+ if (HAS_PERM(PERM_CLOAK))
+ {
+ cuser.ufo ^= UFO_CLOAK;
+ vmsg(cuser.ufo & UFO_CLOAK ? "¼K¼K¡A¸ú°_¨ÓÅo¡I" : "­«²{¦¿´ò¤F");
+ }
+
+ cutmp->ufo = cuser.ufo;
+ return XEASY;
+}
+
+
+int
+t_query()
+{
+ ACCT acct;
+
+ vs_bar("¬d¸ßºô¤Í");
+ if (acct_get(msg_uid, &acct) > 0)
+ {
+ move(1, 0);
+ clrtobot();
+ do_query(&acct);
+ }
+
+ return 0;
+}
+
+
+static int
+talk_choose()
+{
+ UTMP *up, *ubase, *uceil;
+ int self;
+ char userid[IDLEN + 1];
+
+ ll_new();
+
+ self = cuser.userno;
+ ubase = up = ushm->uslot;
+ /* uceil = ushm->uceil; */
+ uceil = ubase + ushm->count;
+ do
+ {
+ if (up->pid && up->userno != self && up->userlevel && can_see(cutmp, up))
+ ll_add(up->userid);
+ } while (++up <= uceil);
+
+ if (!vget(1, 0, msg_uid, userid, IDLEN + 1, GET_LIST))
+ return 0;
+
+ up = ubase;
+ do
+ {
+ if (!str_cmp(userid, up->userid))
+ return up->userno;
+ } while (++up <= uceil);
+
+ return 0;
+}
+
+
+int
+t_talk()
+{
+ int tuid, unum, ucount;
+ UTMP *up;
+ char ans[4];
+
+ if (total_user <= 1)
+ {
+ zmsg("¥Ø«e½u¤W¥u¦³±z¤@¤H¡A§ÖÁܽФj®a¨Ó¥úÁ{¡i" BBSNAME "¡j§a¡I");
+ return XEASY;
+ }
+
+ tuid = talk_choose();
+ if (!tuid)
+ return 0;
+
+ /* ----------------- */
+ /* multi-login check */
+ /* ----------------- */
+
+ move(3, 0);
+ unum = 1;
+ while ((ucount = utmp_count(tuid, 0)) > 1)
+ {
+ outs("(0) ¤£·Q talk ¤F...\n");
+ utmp_count(tuid, 1);
+ vget(1, 33, "½Ð¿ï¾Ü¤@­Ó²á¤Ñ¹ï¶H [0]¡G", ans, 3, DOECHO);
+ unum = atoi(ans);
+ if (unum == 0)
+ return 0;
+ move(3, 0);
+ clrtobot();
+ if (unum > 0 && unum <= ucount)
+ break;
+ }
+
+ if (up = utmp_search(tuid, unum))
+ {
+ if (can_override(up))
+ {
+ if (tuid != up->userno)
+ vmsg(MSG_USR_LEFT);
+ else
+ talk_page(up);
+ }
+ else
+ {
+ vmsg("¹ï¤èÃö±¼©I¥s¾¹¤F");
+ }
+ }
+
+ return 0;
+}
+
+
+/* ------------------------------------- */
+/* ¦³¤H¨Ó¦êªù¤l¤F¡A¦^À³©I¥s¾¹ */
+/* ------------------------------------- */
+
+
+void
+talk_rqst()
+{
+ UTMP *up;
+ int mode, sock, ans, len, port;
+ char buf[80];
+ struct sockaddr_in sin;
+ screenline sl[T_LINES];
+#if defined(__OpenBSD__)
+ struct hostent *h;
+#endif
+
+ up = cutmp->talker;
+ if (!up)
+ return;
+
+ port = up->sockport;
+ if (!port)
+ return;
+
+ mode = bbsmode;
+ utmp_mode(M_TRQST);
+
+ vs_save(sl);
+
+ clear();
+ sprintf(page_requestor, "%s (%s)", up->userid, up->username);
+
+#ifdef EVERY_Z
+ /* Thor.980725: ¬° talk & chat ¥i¥Î ^z §@·Ç³Æ */
+
+ if (vio_holdon())
+ {
+ sprintf(buf, "%s ·Q©M±z²á¡A¤£¹L±z¥u¦³¤@±i¼L", page_requestor);
+ vmsg(buf);
+ buf[0] = ans = '6'; /* Thor.980725: ¥u¦³¤@±i¼L */
+ len = 1;
+ goto over_for;
+ }
+#endif
+
+ bell();
+ prints("±z·Q¸ò %s ²á¤Ñ¶Ü¡H(¨Ó¦Û %s)", page_requestor, up->from);
+ for (;;) /* Åý¨Ï¥ÎªÌ¥i¥H¥ý¬d¸ß­n¨D²á¤Ñªº¹ï¤è */
+ {
+ ans = vget(1, 0, "==> Q)¬d¸ß Y)²á¤Ñ N)¨ú®ø¡H[Y] ", buf, 3, LCECHO);
+ if (ans == 'q')
+ my_query(up->userid);
+ else
+ break;
+ }
+
+ len = 1;
+
+ if (ans == 'n')
+ {
+ move(2, 0);
+ clrtobot();
+ for (ans = 0; ans < 5; ans++)
+ prints("\n (%d) %s", ans + 1, talk_reason[ans]);
+ ans = vget(10, 0, "½Ð¿é¤J¿ï¶µ©Î¨ä¥L±¡¥Ñ [1]¡G\n==> ", buf + 1, 60, DOECHO);
+
+ if (ans == 0)
+ ans = '1';
+
+ if (ans >= '1' && ans <= '5')
+ {
+ buf[0] = ans;
+ }
+ else
+ {
+ buf[0] = ans = ' ';
+ len = strlen(buf);
+ }
+ }
+ else
+ {
+ buf[0] = ans = 'y';
+ }
+
+#ifdef EVERY_Z
+over_for:
+#endif
+
+ sock = socket(AF_INET, SOCK_STREAM, 0);
+
+#if defined(__OpenBSD__) /* lkchu */
+
+ if (!(h = gethostbyname(str_host)))
+ return;
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_port = port;
+ memcpy(&sin.sin_addr, h->h_addr, h->h_length);
+
+#else
+
+ sin.sin_family = AF_INET;
+ sin.sin_port = port;
+ /* sin.sin_addr.s_addr = INADDR_LOOPBACK; */
+ /* sin.sin_addr.s_addr = INADDR_ANY; */
+ /* For FreeBSD 4.x */
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ memset(sin.sin_zero, 0, sizeof(sin.sin_zero));
+
+#endif
+
+ if (!connect(sock, (struct sockaddr *) & sin, sizeof(sin)))
+ {
+ send(sock, buf, len, 0);
+
+ if (ans == 'y')
+ {
+ strcpy(cutmp->mateid, up->userid);
+#ifdef HAVE_MULTI_BYTE
+ page_requestor_zhc = up->ufo & UFO_ZHC;
+#endif
+
+ talk_speak(sock);
+ }
+ }
+
+ close(sock);
+#ifdef LOG_TALK
+ if (ans == 'y') /* mat.991011: ¨¾¤îTalk³Q©Úµ´®É¡A²£¥Í²á¤Ñ°O¿ýªºrecord */
+ talk_save(); /* lkchu.981201: talk °O¿ý³B²z */
+#endif
+ vs_restore(sl);
+ utmp_mode(mode);
+}
diff --git a/maple/ulist.c b/maple/ulist.c
new file mode 100644
index 0000000..c55fd19
--- /dev/null
+++ b/maple/ulist.c
@@ -0,0 +1,1243 @@
+/*-------------------------------------------------------*/
+/* ulist.c ( NTHU CS MapleBBS Ver 3.00 ) */
+/*-------------------------------------------------------*/
+/* target : ulist routines */
+/* create : 95/03/29 */
+/* update : 97/03/29 */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+
+extern UCACHE *ushm;
+extern XZ xz[];
+
+
+/*-------------------------------------------------------*/
+/* ¿ï³æ¦¡²á¤Ñ¤¶­± */
+/*-------------------------------------------------------*/
+
+
+static int pickup_ship = 0; /* 0:¬G¶m !=0:¤Í½Ë±Ô­z */
+
+typedef UTMP *pickup;
+
+/* ¦¹¶¶§Ç§Y¬°±Æ§Çªº¶¶§Ç */
+
+#define FTYPE_SELF 0x01
+#define FTYPE_BOTHGOOD 0x02
+#define FTYPE_MYGOOD 0x04
+#define FTYPE_OGOOD 0x08
+#define FTYPE_NORMAL 0x10
+#define FTYPE_MYBAD 0x20
+
+
+static int mygood_num; /* ¹ï¤è³]§Ú¬°¦n¤Í */
+static int ogood_num; /* §Ú³]¹ï¤è¬°¦n¤Í */
+static int brdmate_num; /* ¹ï¤è»P§Ú¬OªO¦ñ */
+
+static pickup ulist_pool[MAXACTIVE];
+/* static */ int ulist_userno[MAXACTIVE]; /* ¹ïÀ³ ushm ¤¤¦UÄæªº userno */
+static int ulist_ftype[MAXACTIVE]; /* ¹ïÀ³ ushm ¤¤¦UÄæªºªB¤ÍºØÃþ */
+
+static int ulist_init();
+static int ulist_head();
+static XO ulist_xo;
+
+
+#if 0
+static char *
+pal_ship(ftype, userno) /* itoc.020811: ¶Ç¦^ªB¤Í±Ô­z */
+ int ftype, userno;
+{
+ int fd;
+ PAL *pal;
+ char fpath[64];
+ static char palship[46];
+
+ if (ftype & (FTYPE_BOTHGOOD | FTYPE_MYGOOD | FTYPE_MYBAD)) /* ¤¬³]¦n¤Í¡B§Úªº¦n¤Í¡BÃa¤H¤~¦³¤Í½Ë±Ô­z */
+ {
+ usr_fpath(fpath, cuser.userid, fn_pal);
+ if ((fd = open(fpath, O_RDONLY)) >= 0)
+ {
+ mgets(-1);
+ while (pal = mread(fd, sizeof(PAL)))
+ {
+ if (userno == pal->userno)
+ {
+ strcpy(palship, pal->ship);
+ close(fd);
+ return palship;
+ }
+ }
+ close(fd);
+ }
+ }
+ return "";
+}
+#endif
+
+
+#if 1 /* itoc.020901: ¥Î cache ÁöµM¤ñ¸û¦n¡A¦ý¬OÆZ®ö¶O°O¾ÐÅ骺 */
+typedef struct
+{
+ int userno;
+ char ship[20]; /* ¤£»Ý­n©M PAL.ship ¤@¼Ë¤j¡A¥u­n°÷ ulist_body() Åã¥Ü§Y¥i */
+} PALSHIP;
+
+
+static char *
+pal_ship(ftype, userno) /* itoc.020811: ¶Ç¦^ªB¤Í±Ô­z */
+ int ftype, userno;
+{
+ static PALSHIP palship[PAL_MAX] = {0};
+ PALSHIP *pp;
+
+ /* itoc.020901: §â palship ¦¬¶i°O¾ÐÅé¡A¤£­n¤@ª½ I/O ¤F */
+ if (!palship[0].userno) /* initialize *palship[] */
+ {
+ int fd;
+ char fpath[64];
+ PAL *pal;
+
+ /* ¬°¨D®Ä²v¡A¨C¦¸¤W¯¸¶È°µ¤@¦¸¡A¬G­Y§ïÅܪB¤Í±Ô­z¡A­n­«·s¤W¯¸¤~¥Í®Ä */
+ usr_fpath(fpath, cuser.userid, fn_pal);
+ if ((fd = open(fpath, O_RDONLY)) >= 0)
+ {
+ pp = palship;
+ mgets(-1);
+ while (pal = mread(fd, sizeof(PAL)))
+ {
+ if (pal->ship[0]) /* ¦³¤Í½Ë¤~¦¬¤J palship[] */
+ {
+ pp->userno = pal->userno;
+ str_ncpy(pp->ship, pal->ship, sizeof(pp->ship));
+ pp++;
+ }
+ }
+ close(fd);
+ }
+ }
+
+ if (ftype & (FTYPE_BOTHGOOD | FTYPE_MYGOOD | FTYPE_MYBAD)) /* ¤¬³]¦n¤Í¡B§Úªº¦n¤Í¡BÃa¤H¤~¦³¤Í½Ë±Ô­z */
+ {
+ /* ¸g pal_sync ¥H«áªºªB¤Í¦W³æ¬O¨Ì ID ±Æ§Çªº¡A¦Ò¼{¬O§_¥Î binary search? */
+ pp = palship;
+ while (pp->userno)
+ {
+ if (pp->userno == userno)
+ return pp->ship;
+ pp++;
+ }
+ }
+ return "";
+}
+#endif
+
+static int /* qazq.030719: ¨Ï¥ÎªÌ¿ï³æ¥i¥Hª½±µ¶¢¸m */
+ulist_lock(xo)
+ XO *xo;
+{
+ utmp_mode(M_IDLE);
+ u_lock();
+ return ulist_init(xo);
+}
+
+static void
+ulist_item(num, up, slot, now, sysop)
+ int num;
+ UTMP *up;
+ int slot;
+ time_t now;
+ int sysop;
+{
+ time_t diff, ftype;
+ int userno, ufo;
+ char pager, buf[30], *fcolor;
+
+ if (!(userno = up->userno))
+ {
+ outs(" < ¦¹¦ìºô¤Í¥¿¥©Â÷¶} >\n");
+ return;
+ }
+
+ /* itoc.011022: ­Y¥Í¤é·í¤Ñ¤W¯¸¡A­É¥Î idle Äæ¦ì¨Ó©ñ¹Ø¬P */
+ if (up->status & STATUS_BIRTHDAY)
+ {
+ strcpy(buf, "\033[1;31m Ã~µV\033[m");
+ }
+ else
+ {
+ /* duck.050729: ¦Ñ¤H¼Ð°O.. */
+ if (up->userlevel & PERM_ROBOT)
+ strcpy (buf, "\033[1;34m ¾÷¾¹\033[m");
+ else
+ if (up->userlevel & PERM_ELDER)
+ strcpy (buf, "\033[32m ¦Ñ¤H\033[m");
+ else
+ if (up->userlevel & PERM_ALLADMIN)
+ strcpy (buf, "\033[1;33m ¤u¤Í\033[m");
+ else
+
+#ifdef DETAIL_IDLETIME
+ if ((diff = now - up->idle_time) >= 60) /* ¶W¹L 60 ¬í¤~ºâ¶¢¸m */
+ sprintf(buf, "%3d'%02d", diff / 60, diff % 60);
+#else
+ if (diff = up->idle_time)
+ sprintf(buf, "%2d", diff);
+#endif
+ else
+ buf[0] = '\0';
+ }
+
+ ufo = up->ufo;
+
+ /* pager ª¬ºA */
+ /* #¡G¤£±µ¨ü¥ô¦ó¤H©I¥s¡A¤]¤£±µ¨ü¥ô¦ó¤H¼s¼½ */
+ /* *¡G¥u±µ¨ü¦n¤Í©I¥s¡A¥B¥u±µ¨ü¦n¤Í¼s¼½ */
+ /* !¡G¥u±µ¨ü¦n¤Í©I¥s¡A¦ý¤£±µ¨ü¥ô¦ó¤H¼s¼½ */
+ /* -¡G±µ¨ü¥ô¦ó¤H©I¥s¡A¦ý¤£±µ¨ü¥ô¦ó¤H¼s¼½ */
+ /* ¡G³£¨S¦³´N¬O¨S¦³­­¨î°Õ */
+ if (ufo & UFO_QUIET)
+ {
+ pager = '#';
+ }
+ else if (ufo & UFO_PAGER)
+ {
+#ifdef HAVE_NOBROAD
+ if (ufo & UFO_RCVER)
+ pager = '!';
+ else
+#endif
+ pager = '*';
+ }
+#ifdef HAVE_NOBROAD
+ else if (ufo & UFO_RCVER)
+ {
+ pager = '-';
+ }
+#endif
+ else
+ {
+ pager = ' ';
+ }
+
+ ftype = ulist_ftype[slot];
+
+ fcolor =
+#ifdef HAVE_BRDMATE
+# ifdef HAVE_ANONYMOUS
+ up->mode == M_READA && !(currbattr & BRD_ANONYMOUS) && !strcmp(currboard, up->reading) ? COLOR_BRDMATE :
+# else
+ up->mode == M_READA && !strcmp(currboard, up->reading) ? COLOR_BRDMATE :
+# endif
+#endif
+ ftype & FTYPE_NORMAL ? COLOR_NORMAL :
+ ftype & FTYPE_BOTHGOOD ? COLOR_BOTHGOOD :
+ ftype & FTYPE_MYGOOD ? COLOR_MYGOOD :
+ ftype & FTYPE_OGOOD ? COLOR_OGOOD :
+ ftype & FTYPE_SELF ? COLOR_SELF :
+ ftype & FTYPE_MYBAD ? COLOR_MYBAD :
+ "";
+
+ prints("%6d%s%c\033[m%c%s%-13s%-*.*s\033[m%-*.*s%-11.10s%s\n",
+// num, ufo & UFO_CLOAK ? ')' : ' ', pager,
+ num,
+ ufo & UFO_SUPERCLOAK ? "\033[35m" : "" ,
+ tag_char(up->pid) == '*' ? '*' : ufo & UFO_CLOAK ? ')' : ' ',
+ pager,
+ fcolor, up->userid,
+ (d_cols >> 1) + 21, (d_cols >> 1) + 20, up->username,
+ d_cols - (d_cols >> 1) + 19, d_cols - (d_cols >> 1) + 18,
+ pickup_ship ? pal_ship(ftype, up->userno) :
+#ifdef GUEST_WHERE
+ (pager == ' ' || sysop || ftype & (FTYPE_SELF | FTYPE_BOTHGOOD | FTYPE_OGOOD) || !up->userlevel) ? /* ¥i¬Ý¨£ guest ªº¬G¶m */
+#else
+ (pager == ' ' || sysop || ftype & (FTYPE_SELF | FTYPE_BOTHGOOD | FTYPE_OGOOD)) ? /* ¹ï¤è³]§Ú¬°¦n¤Í¥i¬Ý¨£¹ï¤è¨Ó·½ */
+#endif
+ up->from : "*", bmode(up, 0), buf);
+}
+
+
+static int
+ulist_body(xo)
+ XO *xo;
+{
+ pickup *pp;
+ UTMP *up;
+ int num, max, tail, sysop, seecloak, slot;
+#ifdef HAVE_SUPERCLOAK
+ int seesupercloak;
+#endif
+#ifdef DETAIL_IDLETIME
+ time_t now;
+#endif
+
+ max = xo->max;
+ if (max <= 0)
+ {
+ if (vans("¥Ø«e¨S¦³¦n¤Í¤W¯¸¡A­n¬Ý¬Ý¨ä¥L¨Ï¥ÎªÌ¶Ü(Y/N)¡H[Y] ") != 'n')
+ {
+ cuser.ufo ^= UFO_PAL;
+ cutmp->ufo = cuser.ufo;
+ return ulist_init(xo);
+ }
+ return XO_QUIT;
+ }
+
+ num = xo->top;
+ pp = &ulist_pool[num];
+ tail = num + XO_TALL;
+ if (max > tail)
+ max = tail;
+
+ sysop = HAS_PERM(PERM_ALLACCT);
+ seecloak = HAS_PERM(PERM_SEECLOAK);
+#ifdef HAVE_SUPERCLOAK
+ seesupercloak = cuser.ufo & UFO_SUPERCLOAK;
+#endif
+#ifdef DETAIL_IDLETIME
+ time(&now);
+#endif
+
+ move(3, 0);
+ do
+ {
+ up = *pp;
+ slot = up - ushm->uslot;
+
+ /* itoc.011124: ¦pªG¦b ulist_body() ¤¤µo²{ userno ¤£¦X¡Aªí¥Ü¤â¤W³o¥÷¦W³æ¹ê¦b¤Ó¤F¡A±j­¢§ó·s */
+ if (ulist_userno[slot] != up->userno)
+ return ulist_init(xo);
+
+#ifdef DETAIL_IDLETIME
+ ulist_item(++num, up, slot, now, sysop);
+#else
+ ulist_item(++num, up, slot, NULL, sysop);
+#endif
+
+ pp++;
+ } while (num < max);
+ clrtobot();
+
+ /* return XO_NONE; */
+ return XO_FOOT; /* itoc.010403: §â b_lines ¶ñ¤W feeter */
+}
+
+
+static int
+ulist_cmp_userid(i, j)
+ UTMP **i, **j;
+{
+ int k = ulist_ftype[(*i) - ushm->uslot] - ulist_ftype[(*j) - ushm->uslot];
+ return k ? k : str_cmp((*i)->userid, (*j)->userid);
+}
+
+
+static int
+ulist_cmp_host(i, j)
+ UTMP **i, **j;
+{
+ int k = ulist_ftype[(*i) - ushm->uslot] - ulist_ftype[(*j) - ushm->uslot];
+ /* return k ? k : (*i)->in_addr - (*j)->in_addr; */
+ /* Kyo.050112: in_addr ¬O unsigned int (u_long)¡Aª½±µ´î·|³y¦¨ int »~§P */
+ return k ? k : (*i)->in_addr > (*j)->in_addr ? 1 : (*i)->in_addr < (*j)->in_addr ? -1 : 0;
+}
+
+
+static int
+ulist_cmp_mode(i, j)
+ UTMP **i, **j;
+{
+ int k = ulist_ftype[(*i) - ushm->uslot] - ulist_ftype[(*j) - ushm->uslot];
+ return k ? k : (*i)->mode - (*j)->mode;
+}
+
+
+#ifdef HAVE_BRDMATE
+static int
+ulist_cmp_brdmate(i, j)
+ UTMP **i, **j;
+{
+#ifdef HAVE_ANONYMOUS
+ if (!(currbattr & BRD_ANONYMOUS) || HAS_PERM(PERM_SYSOP)) /* ¾\Ū°Î¦WªO«h¤£¦C¤J */
+#endif
+ {
+ int ibrdmate = (*i)->mode == M_READA && !strcmp(currboard, (*i)->reading);
+ int jbrdmate = (*j)->mode == M_READA && !strcmp(currboard, (*j)->reading);
+
+ /* ªO¦ñÀu¥ý */
+ if (ibrdmate && !jbrdmate)
+ return -1;
+ if (jbrdmate && !ibrdmate)
+ return 1;
+ }
+
+ /* ³£¤£¬O©Î³£¬OªO¦ñªº¸Ü¡A«ö ID ±Æ§Ç */
+ return ulist_cmp_userid(i, j);
+}
+#endif
+
+
+#ifdef HAVE_BRDMATE
+#define PICKUP_WAYS 4
+#else
+#define PICKUP_WAYS 3
+#endif
+
+static int pickup_way = 0; /* ¹w³]±Æ¦C¤è¦¡ 0:¥N¸¹ 1:¬G¶m 2:°ÊºA 3:ªO¦ñ */
+
+
+static int (*ulist_cmp[PICKUP_WAYS]) () =
+{
+ ulist_cmp_userid,
+ ulist_cmp_host,
+ ulist_cmp_mode,
+#ifdef HAVE_BRDMATE
+ ulist_cmp_brdmate,
+#endif
+};
+
+
+static char *msg_pickup_way[PICKUP_WAYS] =
+{
+ "ºô¤Í¥N¸¹",
+ "«È³~¬G¶m",
+ "ºô¤Í°ÊºA",
+#ifdef HAVE_BRDMATE
+ "ªO¦ñ¥N¸¹",
+#endif
+};
+
+
+static int
+ulist_paltype(up) /* ªB¤ÍºØÃþ */
+ UTMP *up;
+{
+ const int userno = up->userno;
+
+ if (userno == cuser.userno)
+ return FTYPE_SELF;
+ if (is_mybad(userno))
+ return FTYPE_MYBAD;
+ if (is_mygood(userno))
+ return is_ogood(up) ? FTYPE_BOTHGOOD : FTYPE_MYGOOD;
+ return is_ogood(up) ? FTYPE_OGOOD : FTYPE_NORMAL;
+}
+
+
+#if 0 /* itoc.041001: ulist_init() µù¸Ñ */
+
+ 1. ushm->uslot °O¿ý¥þ¯¸ªº UTMP (¨Ï¥ÎªÌ¦W³æ¸ê®Æ)¡A³o¬OÅý©Ò¦³¤H¦@¥Îªº
+
+ 2. ¨C­Ó¤H¤â¤W¡A¦U¦³¥H¤U¤T¥÷¡G
+ ulist_pool ¬O ushm->uslot ªº¯Á¤Þ¡A°O¿ýµÛ§Ú¥i¥H¬Ý¨ì­þ¨Ç¤H
+ ulist_userno[i] °O¿ýµÛ ushm->uslot[i] ³o¦ì¤l§¤¤F½Ö
+ ulist_ftype[i] °O¿ýµÛ ushm->uslot[i] ³o¦ì¤l¬O§Úªº ¦n¤Í/Ãa¤H/¤@¯ë¤H
+
+ 3. ¦b ulist_init() ¸Ì­±¥h ushm->uslot ÂsÄý©Ò¦³¦ì¤l¡A¦Ò¼{ ushm->uslot[i]
+ ¦pªG§Ú¥i¥H¬Ý¨ì³o­Ó¤H¡A´N§â¥¦§Û¶i¨Ó ulist_pool
+ ­Y ushm->uslot[i].userno != ulist_userno[i]¡Aªí¥Ü³o­Ó¦ì¤l§¤¤F¤@­Ó·s¤W¯¸ªº¤H¡A
+ §Ú´N¥h¬d¥L¬O¤£¬O§Úªº¦n¤Í/Ãa¤H¡A¨Ã°O¿ý¦b ulist_ftype[i]¡F
+ ­Y ushm->uslot[i].userno == ulist_userno[i]¡Aªí¥Ü³o­Ó¦ì¤l¨S´«¤H¡A
+ §Ú´Nª½±µ®³ ulist_ftype[i] ¨Ó·í§@ªB¤ÍºØÃþ
+
+ 4. ¦³¤F ulist_pool[] ¥H«á¡A¦A§â ulist_pool[] ¨Ì§Ú·Q­nªº¤è¦¡±Æ§Ç¡A
+ ¦b ulist_item() ¦L¥X¨ÓªºÃC¦â«h¬O°Ñ·Ó ulist_ftype[]
+
+ 5. ­Y¦³¤H±N§Ú¦b¥LªºªB¤Í¦W³æ¤¤²§°Ê¡A¨º¥L·|§ó°Ê§Úªº cutmp->status¡A
+ ©Ò¥H·í§ÚÀˬd¨ì HAS_STATUS(STATUS_PALDIRTY)¡A§Ú´N­n­«¾ã§Úªº ulist_ftype[]
+
+#endif
+
+
+static int
+ulist_init(xo)
+ XO *xo;
+{
+ UTMP *up, *uceil;
+ pickup *pp;
+ int filter, slot, userno, paldirty;
+
+ pp = ulist_pool;
+ filter = cuser.ufo & UFO_PAL;
+ if (paldirty = HAS_STATUS(STATUS_PALDIRTY))
+ cutmp->status ^= STATUS_PALDIRTY;
+
+ slot = 0;
+ up = ushm->uslot;
+ uceil = (void *) up + ushm->offset;
+
+ mygood_num = ogood_num = brdmate_num = 0;
+
+ /* ±q ushm->uslot[] §Û¨ì ulist_pool[] */
+ do
+ {
+ userno = up->userno;
+
+ if (userno > 0)
+ {
+ /* ·s¤W¯¸ªº¨Ï¥ÎªÌ¡A¬Ý¬Ý¥L¬O­þ¤@ÃþªºªB¤Í¡FSTATUS_PALDIRTY ­«¾ã ulist_ftype[] */
+ if (ulist_userno[slot] != userno || paldirty)
+ {
+ ulist_userno[slot] = userno;
+ ulist_ftype[slot] = ulist_paltype(up);
+ }
+
+ if (can_see(cutmp, up))
+ {
+ userno = ulist_ftype[slot];
+ if (!filter || userno & (FTYPE_SELF | FTYPE_BOTHGOOD | FTYPE_MYGOOD))
+ *pp++ = up;
+
+ /* ºâ¦³´X­Ó¦n¤Í */
+ if (userno & (FTYPE_BOTHGOOD | FTYPE_MYGOOD))
+ mygood_num++;
+ if (userno & (FTYPE_BOTHGOOD | FTYPE_OGOOD))
+ ogood_num++;
+ /* ºâ¦³´X­ÓªO¦ñ */
+#ifdef HAVE_ANONYMOUS
+ if (up->mode == M_READA && !(currbattr & BRD_ANONYMOUS) &&
+ !strcmp(currboard, up->reading))
+#else
+ if (up->mode == M_READA && !strcmp(currboard, up->reading))
+#endif
+ brdmate_num++;
+ }
+ }
+ slot++;
+ } while (++up <= uceil);
+
+ xo->max = slot = pp - ulist_pool;
+
+ if (xo->pos >= slot)
+ xo->pos = xo->top = 0;
+
+ if (slot > 1)
+ qsort(ulist_pool, slot, sizeof(pickup), ulist_cmp[pickup_way]);
+
+ /* itoc.010928: ¥Ñ©ó ushm->count ±`¤£¹ï¡A©Ò¥H¥Î total_user ¨Ó®Õ¥¿¡A
+ ­è¤W¯¸®É´N±Ò©l¤Æ total_user ¬° ushm->count¡A¦¹«á­Y¨Ï¥ÎªÌ¨S¦³¨Ó¨Ï¥ÎªÌ¦W³æ¡A´N¤£§ó·s total_user */
+ if (!filter)
+ total_user = slot;
+
+ return ulist_head(xo);
+}
+
+
+static int
+ulist_neck(xo)
+ XO *xo;
+{
+ move(1, 0);
+
+ prints(" ±Æ¦C¤è¦¡¡G[\033[1m%s/%s\033[m] ¯¸¤W¤H¼Æ¡G%d "
+ COLOR_MYGOOD " §Úªº¦n¤Í¡G%d " COLOR_OGOOD " »P§Ú¬°¤Í¡G%d\033[m"
+ COLOR_BRDMATE " ªO¦ñ¡G%d\033[m",
+ msg_pickup_way[pickup_way],
+ cuser.ufo & UFO_PAL ? "¦n¤Í" : "¥þ³¡",
+ total_user, mygood_num, ogood_num, brdmate_num);
+
+ prints(NECKER_ULIST, d_cols >> 1, "", d_cols - (d_cols >> 1) + 4, pickup_ship ? "¤Í½Ë" : "¬G¶m");
+ return ulist_body(xo);
+}
+
+
+static int
+ulist_head(xo)
+ XO *xo;
+{
+ vs_head("ºô¤Í¦Cªí", str_site);
+ return ulist_neck(xo);
+}
+
+
+static int
+ulist_toggle(xo)
+ XO *xo;
+{
+ int ans, max;
+
+#ifdef HAVE_BRDMATE
+ ans = vans("±Æ¦C¤è¦¡ [1]¥N¸¹ [2]¨Ó·½ [3]°ÊºA [4]ªO¦ñ ") - '1';
+#else
+ ans = vans("±Æ¦C¤è¦¡ [1]¥N¸¹ [2]¨Ó·½ [3]°ÊºA ") - '1';
+#endif
+ if (ans >= 0 && ans < PICKUP_WAYS && ans != pickup_way) /* Thor.980705: from 0 .. PICKUP_WAYS-1 */
+ {
+ pickup_way = ans;
+ max = xo->max;
+
+ if (max > 1)
+ {
+ qsort(ulist_pool, max, sizeof(pickup), ulist_cmp[pickup_way]);
+ return ulist_neck(xo);
+ }
+ }
+
+ return XO_FOOT;
+}
+
+
+static int
+ulist_pal(xo)
+ XO *xo;
+{
+ cuser.ufo ^= UFO_PAL;
+ cutmp->ufo = cuser.ufo;
+ return ulist_init(xo);
+}
+
+
+static int
+ulist_search(xo, step)
+ XO *xo;
+ int step;
+{
+ int num, pos, max;
+ pickup *pp;
+ char buf[IDLEN + 1];
+
+
+ if (vget(b_lines, 0, "½Ð¿é¤J¥N¸¹©Î¼ÊºÙ¡G", buf, IDLEN + 1, DOECHO))
+ {
+ str_lowest(buf, buf);
+
+ pos = num = xo->pos;
+ max = xo->max;
+ pp = ulist_pool;
+ do
+ {
+ pos += step;
+ if (pos < 0) /* Thor.990124: °²³] max ¤£¬°0 */
+ pos = max - 1;
+ else if (pos >= max)
+ pos = 0;
+
+ if (str_str(pp[pos]->userid, buf) || /* lkchu.990127: §ä³¡¥÷ id ¦n¹³¤ñ¸û¦n¥Î :p */
+ str_sub(pp[pos]->username, buf)) /* Thor.990124: ¥i¥H§ä ³¡¤À nickname */
+ {
+ outf(FEETER_ULIST); /* itoc.010913: §â b_lines ¶ñ¤W feeter */
+ return pos + XO_MOVE;
+ }
+
+ } while (pos != num);
+ }
+
+ return XO_FOOT;
+}
+
+
+static int
+ulist_search_forward(xo)
+ XO *xo;
+{
+ return ulist_search(xo, 1); /* step = +1 */
+}
+
+
+static int
+ulist_search_backward(xo)
+ XO *xo;
+{
+ return ulist_search(xo, -1); /* step = -1 */
+}
+
+
+static int
+ulist_addpal(xo)
+ XO *xo;
+{
+ if (cuser.userlevel)
+ {
+ UTMP *up;
+ int userno;
+
+ up = ulist_pool[xo->pos];
+ userno = up->userno;
+ if (userno > 0 && (userno != cuser.userno) && /* lkchu.981217: ¦Û¤v¤£¥i¬°ªB¤Í */
+ !is_mygood(userno) && !is_mybad(userno)) /* ©|¥¼¦C¤JªB¤Í¦W³æ */
+ {
+ PAL pal;
+ char fpath[64];
+
+ pal_edit(PALTYPE_PAL, &pal, DOECHO);
+ pal.userno = userno;
+ strcpy(pal.userid, up->userid);
+ usr_fpath(fpath, cuser.userid, fn_pal);
+
+ /* itoc.001222: ÀˬdªB¤Í­Ó¼Æ */
+ if (rec_num(fpath, sizeof(PAL)) < PAL_MAX)
+ {
+ rec_add(fpath, &pal, sizeof(PAL));
+ pal_cache(); /* ªB¤Í¦W³æ¦P¨B */
+ utmp_admset(userno, STATUS_PALDIRTY);
+ return ulist_init(xo);
+ }
+ else
+ {
+ vmsg("±zªºªB¤Í¦W³æ¤Ó¦h¡A½Ðµ½¥[¾ã²z");
+ return XO_FOOT;
+ }
+ }
+ }
+ return XO_NONE;
+}
+
+
+static int
+cmppal(pal)
+ PAL *pal;
+{
+ return pal->userno == currchrono;
+}
+
+
+static int
+ulist_delpal(xo)
+ XO *xo;
+{
+ if (cuser.userlevel)
+ {
+ UTMP *up;
+ int userno;
+
+ up = ulist_pool[xo->pos];
+ userno = up->userno;
+ if (userno > 0 && (is_mygood(userno) || is_mybad(userno))) /* ¦bªB¤Í¦W³æ¤¤ */
+ {
+ if (vans(msg_del_ny) == 'y')
+ {
+ char fpath[64];
+
+ usr_fpath(fpath, cuser.userid, fn_pal);
+
+ currchrono = userno;
+ if (!rec_del(fpath, sizeof(PAL), 0, cmppal))
+ {
+ pal_cache(); /* ªB¤Í¦W³æ¦P¨B */
+ utmp_admset(userno, STATUS_PALDIRTY);
+ return ulist_init(xo);
+ }
+ }
+ return XO_FOOT;
+ }
+ }
+ return XO_NONE;
+}
+
+
+static int
+ulist_mail(xo)
+ XO *xo;
+{
+ char userid[IDLEN + 1];
+
+ /* ¥ý½Æ»s¤@¥÷¨ì¤â¤W¡A¥H§K¦b¼g«H®É ushm ÅܰʤF */
+ strcpy(userid, ulist_pool[xo->pos]->userid);
+
+ if (!*userid)
+ {
+ vmsg(MSG_USR_LEFT);
+ return XO_FOOT;
+ }
+
+ return my_send(userid);
+}
+
+
+static int
+ulist_query(xo)
+ XO *xo;
+{
+ move(1, 0);
+ clrtobot();
+ char *userid,*realid;
+ char buf[80];
+ int userno;
+ userid=ulist_pool[xo->pos]->userid;
+ realid=ulist_pool[xo->pos]->realid;
+ userno=ulist_pool[xo->pos]->userno;
+ if(strcmp(userid,realid)){
+ if(HAS_PERM(PERM_ALLADMIN) || cuser.userno == userno){
+ sprintf(buf,"§ïID Åv­­¡G%s ¡÷ %s",realid,userid);
+ vmsg(buf);
+ move(1, 0);
+ my_query(realid);
+ }else{
+ vmsg("³o¦ì¨Ï¥ÎªÌ¨Ï¥Î¤F§ï ID Åv­­¡A¦ý±z¨S¦³Åv­­À˵ø­ì©l ID");
+ }
+ }else{
+ my_query(userid);
+ }
+ return ulist_neck(xo);
+}
+
+
+static int
+ulist_broadcast(xo)
+ XO *xo;
+{
+ int num, sysop;
+ pickup *pp;
+ UTMP *up;
+ BMW bmw;
+
+ num = cuser.userlevel;
+ sysop = num & PERM_CAST;
+ if (!sysop && (!(num & PERM_PAGE) || !(cuser.ufo & UFO_PAL)))
+ return XO_NONE;
+
+ num = xo->max;
+ if (num <= 1) /* ¦pªG¥u¦³¦Û¤v¡A¤£¯à¼s¼½ */
+ return XO_NONE;
+
+ /* itoc.030101: ¦pªG¯¸ªø¥Îªº¬O¦n¤Í¼s¼½¡Aµø¦P¤@¯ë ID ¼s¼½ */
+ sysop = sysop && !(cuser.ufo & UFO_PAL);
+
+ bmw.caller = NULL;
+ bmw_edit(NULL, "¡¹¼s¼½¡G", &bmw);
+
+ if (bmw.caller) /* bmw_edit() ¤¤¦^µª Yes ­n°e¥X¼s¼½ */
+ {
+ /* itoc.000213: ¥[ "> " ¬°¤F»P¤@¯ë¤ô²y°Ï¤À */
+ sprintf(bmw.userid, "%s> ", cuser.userid);
+
+ pp = ulist_pool;
+ while (--num >= 0)
+ {
+ up = pp[num];
+
+ if (!sysop)
+ {
+#ifdef HAVE_NOBROAD
+ if (up->ufo & UFO_RCVER)
+ continue;
+#endif
+
+ /* itoc.011126: ­Y up-> ¤w¤U¯¸¡A³Q¨ä¥L user ©Ò¨ú¥N®É¡A
+ ·|¦³¼s¼½»~´Óªº°ÝÃD¡A±o­«·sÀˬd¬O§_¬°§Úªº¦n¤Í */
+ if (!is_mygood(up->userno))
+ continue;
+ }
+
+ if (can_override(up))
+ {
+ bmw.recver = up->userno;
+ bmw_send(up, &bmw);
+ }
+ }
+ }
+
+ return XO_NONE;
+}
+
+static int
+ulist_widecast(xo)
+ XO *xo;
+{
+ int tag, locus, pos;
+ BMW benz;
+ UTMP *up;
+ extern TagItem TagList[];
+
+ if (!HAS_PERM(PERM_PAGE))
+ return XO_NONE;
+
+ tag = AskTag("¯S©w¹ï¶H¼s¼½");
+
+ if (tag < 0)
+ return XO_FOOT;
+
+ if (!tag) /* ¦n¤Í¼s¼½©Î¯¸ªø¥þ¯¸¼s¼½ */
+ return ulist_broadcast(xo);
+
+ benz.caller = NULL;
+ bmw_edit(NULL, "¡¹¼s¼½¡G", &benz);
+ if (!benz.caller) /* bmw_edit() ¤¤¦^µª No ¤£°e¥X¼s¼½ */
+ return XO_FOOT;
+
+ /* itoc.000213: ¥[ "> " ¬°¤F»P¤@¯ë¤ô²y°Ï¤À */
+ sprintf(benz.userid, "%s> ", cuser.userid);
+
+ locus = 0;
+
+ do
+ {
+ pos = TagList[locus].recno;
+ up = ulist_pool[pos];
+ if (up->pid != TagList[locus].chrono) /* ¸Ó¦ì¤l´«¤H¤F */
+ continue;
+
+#ifdef HAVE_NOBROAD
+ if (up->ufo & UFO_RCVER)
+ continue;
+#endif
+
+ if (can_override(up))
+ {
+ benz.recver = up->userno;
+ bmw_send(up, &benz);
+ }
+ } while (++locus < tag);
+
+ return XO_FOOT;
+}
+
+
+static int
+ulist_talk(xo)
+ XO *xo;
+{
+ if (HAS_PERM(PERM_PAGE))
+ {
+ UTMP *up;
+
+ up = ulist_pool[xo->pos];
+ if (can_override(up))
+ return talk_page(up) ? ulist_head(xo) : XO_FOOT;
+ }
+ return XO_NONE;
+}
+
+
+static int
+ulist_write(xo)
+ XO *xo;
+{
+ if (HAS_PERM(PERM_PAGE))
+ {
+ UTMP *up;
+
+ if (up = ulist_pool[xo->pos])
+ do_write(up);
+ }
+ return XO_NONE;
+}
+
+
+static int
+ulist_edit(xo) /* Thor: ¥i½u¤W¬d¬Ý¤Î­×§ï¨Ï¥ÎªÌ */
+ XO *xo;
+{
+ ACCT acct;
+ char* userid=ulist_pool[xo->pos]->userid;
+ char* realid=ulist_pool[xo->pos]->realid;
+ char buf[80];
+ if(strcmp(userid,realid)){
+ sprintf(buf,"§ïID Åv­­¡G%s ¡÷ %s",realid,userid);
+ vmsg(buf);
+ }
+
+ if (!HAS_PERM(PERM_ALLACCT) || acct_load(&acct, ulist_pool[xo->pos]->realid) < 0)
+ return XO_NONE;
+
+ vs_bar("¨Ï¥ÎªÌ³]©w");
+ acct_setup(&acct, 1);
+ return ulist_head(xo);
+}
+
+
+static int
+ulist_kick(xo)
+ XO *xo;
+{
+ if (HAS_PERM(PERM_ALLACCT) || (HAS_PERM(PERM_ALLADMIN) && ulist_pool[xo->pos]->userno == cuser.userno ))
+ {
+ UTMP *up;
+ pid_t pid;
+ char buf[80];
+
+ up = ulist_pool[xo->pos];
+ if (pid = up->pid)
+ {
+ if (vans(msg_sure_ny) != 'y' || pid != up->pid)
+ return XO_FOOT;
+
+ sprintf(buf, "%s (%s)", up->userid, up->username);
+
+ if ((kill(pid, SIGTERM) == -1) && (errno == ESRCH))
+ utmp_free(up);
+ else
+ sleep(3); /* ³Q½ðªº¤H³o®É­Ô¥¿¦b¦Û§Ú¤FÂ_ */
+
+ blog("KICK", buf);
+ alog("±j¨îÂ÷½u",up->userid);
+ return ulist_init(xo);
+ }
+ }
+ return XO_NONE;
+}
+
+
+#ifdef HAVE_CHANGE_NICK
+static int
+ulist_nickchange(xo)
+ XO *xo;
+{
+ char *str, buf[UNLEN + 1];
+
+ if (!cuser.userlevel)
+ return XO_NONE;
+
+ strcpy(buf, str = cutmp->username);
+ if (vget(b_lines, 0, "½Ð¿é¤J·sªº¼ÊºÙ¡G", buf, UNLEN + 1, GCARRY))
+ {
+ if (strcmp(buf, str))
+ {
+ strcpy(str, buf);
+ strcpy(cuser.username, buf); /* ¼ÊºÙ¤]¤@¨Ö§ó´« cuser. */
+ return ulist_body(xo);
+ }
+ }
+ return XO_FOOT;
+}
+#endif
+
+
+
+#ifdef HAVE_CHANGE_FROM
+static int
+ulist_fromchange(xo)
+ XO *xo;
+{
+ char *str, buf[34];
+
+ if (!cuser.userlevel)
+ return XO_NONE;
+
+ strcpy(buf, str = cutmp->from);
+ if (vget(b_lines, 0, "½Ð¿é¤J·sªº¬G¶m¡G", buf, sizeof(cutmp->from), GCARRY))
+ {
+ if (strcmp(buf, str))
+ {
+ strcpy(str, buf);
+ return ulist_body(xo);
+ }
+ }
+
+ return XO_FOOT;
+}
+#endif
+
+#ifdef HAVE_CHANGE_ID
+static int
+ulist_idchange(xo)
+ XO *xo;
+{
+ char *str, buf[IDLEN + 1];
+
+ /* itoc.010717.µù¸Ñ: ³o¥\¯à´£¨Ñ¯¸ªø¥i¥H¦b¨Ï¥ÎªÌ¦W³æ¼È®É§ï¦Û¤vªº ID¡A
+ ¦ý¬O¥Ñ©ó ulist ¤j³¡¤À¬O¥Î userno ¨Ó§PÂ_¡A©Ò¥H¥u¦³¦n¬Ý¦Ó¤w */
+
+ if (!HAS_PERM(PERM_ALLADMIN) && !HAS_PERM(PERM_CHANGEID))
+ return XO_NONE;
+
+ strcpy(buf, str = cutmp->userid);
+ if (vget(b_lines, 0, "½Ð¿é¤J·sªº¢×¢Ò¡G", buf, IDLEN + 1 , GCARRY))
+ {
+ if (strcmp(buf, str))
+ {
+ if (!HAS_PERM(PERM_ALLADMIN))
+ {
+ if (!strcmp(buf,cuser.userid))
+ strcpy(buf,cuser.userid);
+ else
+ strcat(buf,",");
+ }
+
+ strcpy (str, buf);
+ return ulist_body(xo);
+ }
+ }
+
+ return XO_FOOT;
+}
+#endif
+
+
+static int
+ulist_cloak(xo) /* itoc.010908: §Ö³tÁô¨­ */
+ XO *xo;
+{
+ if (HAS_PERM(PERM_CLOAK))
+ {
+ cuser.ufo ^= UFO_CLOAK;
+ cutmp->ufo = cuser.ufo;
+ return ulist_init(xo);
+ }
+ return XO_NONE;
+}
+
+
+#ifdef HAVE_SUPERCLOAK
+static int
+ulist_supercloak(xo) /* itoc.010908: §Ö³tµµÁô */
+ XO *xo;
+{
+ if (cuser.ufo & UFO_SUPERCLOAK) /* ¨ú®øµµÁô¡A¤£¥²ºÞÅv­­ */
+ {
+ cuser.ufo &= ~(UFO_CLOAK | UFO_SUPERCLOAK);
+ cutmp->ufo = cuser.ufo;
+ return ulist_init(xo);
+ }
+ else if (HAS_PERM(PERM_ALLADMIN)) /* ¶i¤JµµÁô */
+ {
+ cuser.ufo |= (UFO_CLOAK | UFO_SUPERCLOAK);
+ cutmp->ufo = cuser.ufo;
+ return ulist_init(xo);
+ }
+ return XO_NONE;
+}
+#endif
+
+
+static int
+ulist_ship(xo)
+ XO *xo;
+{
+ pickup_ship = ~pickup_ship;
+ return ulist_neck(xo);
+}
+
+
+static int
+ulist_recall(xo)
+ XO *xo;
+{
+ if (cuser.userlevel)
+ {
+ t_bmw();
+ return ulist_head(xo);
+ }
+ return XO_NONE;
+}
+
+
+static int
+ulist_display(xo)
+ XO *xo;
+{
+ if (cuser.userlevel)
+ {
+ t_display();
+ return ulist_head(xo);
+ }
+ return XO_NONE;
+}
+
+
+static int
+ulist_tag(xo)
+ XO *xo;
+{
+ UTMP *up;
+ int tag, pos, cur;
+
+ pos = xo->pos;
+ cur = pos - xo->top;
+ up = ulist_pool[pos];
+
+ if (!up->pid)
+ return XO_NONE;
+
+ if (tag = Tagger(up->pid, pos, TAG_TOGGLE))
+ {
+ move(3 + cur, 6);
+ if(up->ufo & UFO_SUPERCLOAK) outs("\033[35m");
+ outc(tag > 0 ? '*' : up->ufo & UFO_CLOAK ? ')' : ' ');
+ if(up->ufo & UFO_SUPERCLOAK) outs("\033[m");
+ }
+
+ /* return XO_NONE; */
+ return xo->pos + 1 + XO_MOVE; /* lkchu.981201: ¸õ¦Ü¤U¤@¶µ */
+}
+
+static int
+ulist_help(xo)
+ XO *xo;
+{
+#if 1 /* ¯¸ªø´c·d :p */
+ if (HAS_PERM(PERM_ROBOT))
+ {
+ char ans[20];
+ if (vget(b_lines, 0, "­nµ¹¥L´X­Ó¤ô²y¡H", ans, 10, DOECHO))
+ {
+ ulist_pool[xo->pos]->bmw_count = atoi(ans);
+ return ulist_body(xo);
+ }
+ }
+#endif
+ xo_help("ulist");
+ return ulist_head(xo);
+}
+
+
+static KeyFunc ulist_cb[] =
+{
+ XO_INIT, ulist_init,
+ XO_LOAD, ulist_body,
+ XO_HEAD, ulist_head,
+ /* XO_BODY, ulist_body, */ /* ¨S¦³¥Î¨ì */
+
+ 'f', ulist_pal,
+ 'y', ulist_pal, /* itoc.010205: ¦³¤H·|§â yank ªº·N«ä¥Î¦b³o */
+ 'a', ulist_addpal,
+ 'd', ulist_delpal,
+ 'T', ulist_talk,
+ 'w', ulist_write,
+ 'l', ulist_recall, /* ¤ô²y¦^ÅU */
+ 'L', ulist_display,
+ 'r', ulist_query,
+ 'q', ulist_query, /* itoc.020109: ¨Ï¥ÎªÌ²ßºD¥Î q ¬d¸ß */
+// 'B', ulist_broadcast,
+ 'B', ulist_widecast,
+ 's', ulist_init, /* refresh status Thor: À³user­n¨D */
+ 'S', ulist_ship,
+ 'I', ulist_lock, /* qazq.030719: ¨Ï¥ÎªÌ¿ï³æª½±µ¶¢¸m */
+ Ctrl('K'), ulist_kick,
+ Ctrl('O'), ulist_edit,
+ Ctrl('Q'), ulist_query,
+
+#ifdef HAVE_CHANGE_NICK
+ Ctrl('N'), ulist_nickchange,
+#endif
+#ifdef HAVE_CHANGE_FROM
+ Ctrl('F'), ulist_fromchange,
+#endif
+#ifdef HAVE_CHANGE_ID
+ Ctrl('D'), ulist_idchange,
+#endif
+
+#if 0
+ '/', ulist_search,
+#endif
+ /* Thor.990125: ¥i«e«á·j´M, id or nickname */
+ '/', ulist_search_forward,
+ '?', ulist_search_backward,
+
+ 'm', ulist_mail,
+ KEY_TAB, ulist_toggle,
+
+ 'i', ulist_cloak,
+#ifdef HAVE_SUPERCLOAK
+ 'H', ulist_supercloak,
+#endif
+ 't', ulist_tag,
+ 'h', ulist_help
+};
+
+
+void
+talk_main()
+{
+ char fpath[64];
+
+ xz[XZ_ULIST - XO_ZONE].xo = &ulist_xo;
+ xz[XZ_ULIST - XO_ZONE].cb = ulist_cb;
+
+ /* itoc.010715: ¥Ñ©ó erevy_Z ¥i¥Hª½±µ¶i¤J bmw¡A©Ò¥H¤@¤W¯¸´N­n¸ü¤J */
+ usr_fpath(fpath, cuser.userid, fn_bmw);
+ xz[XZ_BMW - XO_ZONE].xo = xo_new(fpath);
+}
diff --git a/maple/user.c b/maple/user.c
new file mode 100644
index 0000000..4695911
--- /dev/null
+++ b/maple/user.c
@@ -0,0 +1,834 @@
+/*-------------------------------------------------------*/
+/* user.c ( NTHU CS MapleBBS Ver 3.00 ) */
+/*-------------------------------------------------------*/
+/* target : account / user routines */
+/* create : 95/03/29 */
+/* update : 96/04/05 */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+
+extern char *ufo_tbl[];
+
+
+/* ----------------------------------------------------- */
+/* »{ÃҥΨ禡 */
+/* ----------------------------------------------------- */
+
+
+void
+justify_log(userid, justify) /* itoc.010822: ®³±¼ .ACCT ¤¤ justify ³oÄæ¦ì¡A§ï°O¿ý¦b FN_JUSTIFY */
+ char *userid;
+ char *justify; /* »{ÃÒ¸ê®Æ RPY:email-reply KEY:»{ÃÒ½X POP:pop3»{ÃÒ REG:µù¥U³æ */
+{
+ char fpath[64];
+ FILE *fp;
+
+ usr_fpath(fpath, userid, FN_JUSTIFY);
+ if (fp = fopen(fpath, "a")) /* ¥Îªþ¥[ÀɮסA¥i¥H«O¦s¾ú¦¸»{ÃÒ°O¿ý */
+ {
+ fprintf(fp, "%s\n", justify);
+ fclose(fp);
+ }
+}
+
+
+static int
+ban_addr(addr)
+ char *addr;
+{
+ char *host;
+ char foo[128]; /* SoC: ©ñ¸m«ÝÀˬdªº email address */
+
+ /* Thor.991112: °O¿ý¥Î¨Ó»{ÃÒªºemail */
+ sprintf(foo, "%s # %s (%s)\n", addr, cuser.userid, Now());
+ f_cat(FN_RUN_EMAILREG, foo);
+
+ /* SoC: «O«ù­ì email ªº¤j¤p¼g */
+ str_lower(foo, addr);
+
+ /* check for acl (lower case filter) */
+
+ host = (char *) strchr(foo, '@');
+ *host = '\0';
+
+ /* *.bbs@xx.yy.zz¡B*.brd@xx.yy.zz ¤@«ß¤£±µ¨ü */
+ if (host > foo + 4 && (!str_cmp(host - 4, ".bbs") || !str_cmp(host - 4, ".brd")))
+ return 1;
+
+ /* ¤£¦b¥Õ¦W³æ¤W©Î¦b¶Â¦W³æ¤W */
+ return (!acl_has(TRUST_ACLFILE, foo, host + 1) ||
+ acl_has(UNTRUST_ACLFILE, foo, host + 1) > 0);
+}
+
+
+/* ----------------------------------------------------- */
+/* POP3 »{ÃÒ */
+/* ----------------------------------------------------- */
+
+
+#ifdef HAVE_POP3_CHECK
+
+static int /* >=0:socket -1:³s½u¥¢±Ñ */
+Get_Socket(site) /* site for hostname */
+ char *site;
+{
+ int sock;
+ struct sockaddr_in sin;
+ struct hostent *host;
+
+ sock = 110;
+
+ /* Getting remote-site data */
+
+ memset((char *) &sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(sock);
+
+ if (!(host = gethostbyname(site)))
+ sin.sin_addr.s_addr = inet_addr(site);
+ else
+ memcpy(&sin.sin_addr.s_addr, host->h_addr, host->h_length);
+
+ /* Getting a socket */
+
+ if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
+ {
+ return -1;
+ }
+
+ /* perform connecting */
+
+ if (connect(sock, (struct sockaddr *) & sin, sizeof(sin)) < 0)
+ {
+ close(sock);
+ return -1;
+ }
+
+ return sock;
+}
+
+
+static int /* 0:¦¨¥\ */
+POP3_Check(sock, account, passwd)
+ int sock;
+ char *account, *passwd;
+{
+ FILE *fsock;
+ char buf[512];
+
+ if (!(fsock = fdopen(sock, "r+")))
+ {
+ outs("\n¶Ç¦^¿ù»~­È¡A½Ð­«¸Õ´X¦¸¬Ý¬Ý\n");
+ return -1;
+ }
+
+ sock = 1;
+
+ while (1)
+ {
+ switch (sock)
+ {
+ case 1: /* Welcome Message */
+ fgets(buf, sizeof(buf), fsock);
+ break;
+
+ case 2: /* Verify Account */
+ fprintf(fsock, "user %s\r\n", account);
+ fflush(fsock);
+ fgets(buf, sizeof(buf), fsock);
+ break;
+
+ case 3: /* Verify Password */
+ fprintf(fsock, "pass %s\r\n", passwd);
+ fflush(fsock);
+ fgets(buf, sizeof(buf), fsock);
+ sock = -1;
+ break;
+
+ default: /* 0:Successful 4:Failure */
+ fprintf(fsock, "quit\r\n");
+ fclose(fsock);
+ return sock;
+ }
+
+ if (!strncmp(buf, "+OK", 3))
+ {
+ sock++;
+ }
+ else
+ {
+ outs("\n»·ºÝ¨t²Î¶Ç¦^¿ù»~°T®§¦p¤U¡G\n");
+ prints("%s\n", buf);
+ sock = -1;
+ }
+ }
+}
+
+
+static int /* -1:¤£¤ä´© 0:±K½X¿ù»~ 1:¦¨¥\ */
+do_pop3(addr) /* itoc.010821: §ï¼g¤@¤U :) */
+ char *addr;
+{
+ int sock, i;
+ char *ptr, *str, buf[80], username[80];
+ char *alias[] = {"", "pop3.", "mail.", NULL};
+ ACCT acct;
+
+ strcpy(username, addr);
+ *(ptr = strchr(username, '@')) = '\0';
+ ptr++;
+
+ clear();
+ move(2, 0);
+ prints("¥D¾÷: %s\n±b¸¹: %s\n", ptr, username);
+ outs("\033[1;5;36m³s½u»·ºÝ¥D¾÷¤¤...½Ðµy­Ô\033[m\n");
+ refresh();
+
+ for (i = 0; str = alias[i]; i++)
+ {
+ sprintf(buf, "%s%s", str, ptr); /* itoc.020120: ¥D¾÷¦WºÙ¥[¤W pop3. ¸Õ¸Õ¬Ý */
+ if ((sock = Get_Socket(buf)) >= 0) /* §ä¨ì³o¾÷¾¹¥B¹ï¤è¤ä´© POP3 */
+ break;
+ }
+
+ if (sock < 0)
+ {
+ outs("±zªº¹q¤l¶l¥ó¨t²Î¤£¤ä´© POP3 »{ÃÒ¡A¨Ï¥Î»{ÃÒ«H¨ç¨­¤À½T»{\n\n\033[1;36;5m¨t²Î°e«H¤¤...\033[m");
+ return -1;
+ }
+
+ if (vget(15, 0, "½Ð¿é¤J¥H¤W©Ò¦C¥X¤§¤u§@¯¸±b¸¹ªº±K½X¡G", buf, 20, NOECHO))
+ {
+ move(17, 0);
+ outs("\033[5;37m¨­¤À½T»{¤¤...½Ðµy­Ô\033[m\n");
+
+ if (!POP3_Check(sock, username, buf)) /* POP3 »{ÃÒ¦¨¥\ */
+ {
+ /* ´£¤ÉÅv­­ */
+ sprintf(buf, "POP: %s", addr);
+ justify_log(cuser.userid, buf);
+ strcpy(cuser.email, addr);
+ if (acct_load(&acct, cuser.userid) >= 0)
+ {
+ time(&acct.tvalid);
+ acct_setperm(&acct, PERM_VALID, 0);
+ }
+
+ /* ±H«H³qª¾¨Ï¥ÎªÌ */
+ mail_self(FN_ETC_JUSTIFIED, str_sysop, msg_reg_valid, 0);
+ cutmp->status |= STATUS_BIFF;
+ vmsg(msg_reg_valid);
+
+ close(sock);
+ return 1;
+ }
+ }
+
+ close(sock);
+
+ /* POP3 »{ÃÒ¥¢±Ñ */
+ outs("±zªº±K½X©Î³\\¥´¿ù¤F¡A¨Ï¥Î»{ÃÒ«H¨ç¨­¤À½T»{\n\n\033[1;36;5m¨t²Î°e«H¤¤...\033[m");
+ return 0;
+}
+#endif
+
+
+/* ----------------------------------------------------- */
+/* ³]©w E-mail address */
+/* ----------------------------------------------------- */
+
+
+int
+u_addr()
+{
+ char *msg, addr[64];
+
+ if (HAS_STATUS(STATUS_COINLOCK))
+ {
+ vmsg(msg_coinlock);
+ return XEASY;
+ }
+
+ /* itoc.050405: ¤£¯àÅý°±ÅvªÌ­«·s»{ÃÒ¡A¦]¬°·|§ï±¼¥Lªº tvalid (°±Åv¨ì´Á®É¶¡) */
+ if (HAS_PERM(PERM_ALLDENY))
+ {
+ vmsg("±z³Q°±Åv¡AµLªk§ï«H½c");
+ return XEASY;
+ }
+
+ film_out(FILM_EMAIL, 0);
+
+ if (vget(b_lines - 2, 0, "E-mail ¦a§}¡G", addr, sizeof(cuser.email), DOECHO))
+ {
+ if (not_addr(addr))
+ {
+ msg = err_email;
+ }
+ else if (ban_addr(addr))
+ {
+ msg = "¥»¯¸¤£±µ¨ü±zªº«H½c°µ¬°»{ÃÒ¦a§}";
+ }
+ else
+ {
+#ifdef EMAIL_JUSTIFY
+ if (vans("­×§ï E-mail ­n­«·s»{ÃÒ¡A½T©w­n­×§ï¶Ü(Y/N)¡H[Y] ") == 'n')
+ return 0;
+
+# ifdef HAVE_POP3_CHECK
+ if (vans("¬O§_¨Ï¥Î POP3 »{ÃÒ(Y/N)¡H[N] ") == 'y')
+ {
+ if (do_pop3(addr) > 0) /* ­Y POP3 »{ÃÒ¦¨¥\¡A«hÂ÷¶}¡A§_«h¥H»{ÃÒ«H±H¥X */
+ return 0;
+ }
+# endif
+
+ if (bsmtp(NULL, NULL, addr, MQ_JUSTIFY) < 0)
+ {
+ msg = "¨­¤À»{ÃÒ«H¨çµLªk±H¥X¡A½Ð¥¿½T¶ñ¼g E-mail address";
+ }
+ else
+ {
+ ACCT acct;
+
+ strcpy(cuser.email, addr);
+ cuser.userlevel &= ~PERM_ALLVALID;
+ if (acct_load(&acct, cuser.userid) >= 0)
+ {
+ strcpy(acct.email, addr);
+ acct_setperm(&acct, 0, PERM_ALLVALID);
+ }
+
+ film_out(FILM_JUSTIFY, 0);
+ prints("\n%s(%s)±z¦n¡A¥Ñ©ó±z§ó·s E-mail address ªº³]©w¡A\n\n"
+ "½Ð±z¾¨§Ö¨ì \033[44m%s\033[m ©Ò¦bªº¤u§@¯¸¦^ÂСy¨­¤À»{ÃÒ«H¨ç¡z¡C",
+ cuser.userid, cuser.username, addr);
+ msg = NULL;
+ }
+#else
+ msg = NULL;
+#endif
+
+ }
+ vmsg(msg);
+ }
+
+ return 0;
+}
+
+
+/* ----------------------------------------------------- */
+/* ¶ñ¼gµù¥U³æ */
+/* ----------------------------------------------------- */
+
+
+#ifdef HAVE_REGISTER_FORM
+
+static void
+getfield(line, len, buf, desc, hint)
+ int line, len;
+ char *hint, *desc, *buf;
+{
+ move(line, 0);
+ prints("%s%s", desc, hint);
+ vget(line + 1, 0, desc, buf, len, GCARRY);
+}
+
+
+int
+u_register()
+{
+ FILE *fn;
+ int ans;
+ RFORM rform;
+
+#ifdef JUSTIFY_PERIODICAL
+ if ((HAS_PERM(PERM_VALID) && cuser.tvalid + VALID_PERIOD - INVALID_NOTICE_PERIOD >= ap_start) || HAS_PERM(PERM_ALLADMIN))
+#else
+ if (HAS_PERM(PERM_VALID))
+#endif
+ {
+ zmsg("±zªº¨­¤À½T»{¤w¸g§¹¦¨¡A¤£»Ý¶ñ¼g¥Ó½Ðªí");
+ return XEASY;
+ }
+
+ if (fn = fopen(FN_RUN_RFORM, "rb"))
+ {
+ while (fread(&rform, sizeof(RFORM), 1, fn))
+ {
+ if ((rform.userno == cuser.userno) && !strcmp(rform.userid, cuser.userid))
+ {
+ fclose(fn);
+ zmsg("±zªºµù¥U¥Ó½Ð³æ©|¦b³B²z¤¤¡A½Ð­@¤ßµ¥­Ô");
+ return XEASY;
+ }
+ }
+ fclose(fn);
+ }
+
+ if (vans("±z½T©w­n¶ñ¼gµù¥U³æ¶Ü(Y/N)¡H[N] ") != 'y')
+ return XEASY;
+
+ move(1, 0);
+ clrtobot();
+ prints("\n%s(%s) ±z¦n¡A½Ð¾Ú¹ê¶ñ¼g¥H¤Uªº¸ê®Æ¡G\n(«ö [Enter] ±µ¨üªì©l³]©w)",
+ cuser.userid, cuser.username);
+
+ vmsg("ª`·N¡Gµù¥U³æ¥²¶·¥Î¤¤¤å¶ñ¼g¡A°£«D¦í§}¡B¾Ç®Õ©ÎªA°È³æ¦ì¦b°ê¥~");
+ vmsg("ªA°È³æ¦ìÀ³Á×§K¨Ï¥Î¤T­Ó¦r¤ºÂ²ºÙ¡A§_«h¦³¥i¯à³Q°h¥ó");
+ memset(&rform, 0, sizeof(RFORM));
+ for (;;)
+ {
+ move (5, 0);
+ prints ("\n\033[1;36m¡mªA°È³æ¦ì¡n\033[m\n");
+ prints ("\033[1;30m½d¨Ò: \033[37m¥x«n¤@¤¤98¯Å\033[30m " \
+ "©Î \033[37m¥xÆW\¤j¾Ç¸ê¤u¨t97¯Å\033[m¡A½Ð¶ñ¡u¯Å¼Æ¡v¡A¤£­n¶ñ¦~¯Å¡AÁÂÁ¡C\n");
+
+ getfield(8, 50, rform.career, "ªA°È³æ¦ì", "¾Ç®Õ¨t¯Å©Î³æ¦ì¾ºÙ");
+
+ prints ("\n\033[1;36m¡m¥Ø«e¦í§}¡n\033[m\n");
+ prints ("\033[1;30m½d¨Ò: \033[37m¥x«n¥«¥Á±Ú¸ô¤@¬q1¸¹\033[30m " \
+ "©Î \033[37m¦¨¥\\¤j¾Ç·q¤@ªÙ1001©Ð\033[m\n");
+ getfield(13, 60, rform.address, "¥Ø«e¦í§}", "¥]¬A¹ì«Ç©ÎªùµP¸¹½X");
+
+ prints ("\n\033[1;36m¡mÁpµ¸¹q¸Ü¡n\033[m\n");
+ prints ("\033[1;30m½d¨Ò: \033[37m06-2371206\033[30m " \
+ "©Î \033[37m0912345678\033[m\n");
+ getfield(18, 20, rform.phone, "³sµ¸¹q¸Ü", "¥]¬Aªø³~¼·¸¹°Ï°ì½X");
+ ans = vans("¥H¤W¸ê®Æ¬O§_¥¿½T(Y/N/Q)¡H[N] ");
+ if (ans == 'q')
+ return 0;
+ if (ans == 'y')
+ break;
+ }
+/*
+ memset(&rform, 0, sizeof(RFORM));
+ for (;;)
+ {
+ getfield(5, 50, rform.career, "ªA°È³æ¦ì¡G", "¾Ç®Õ¨t¯Å©Î³æ¦ì¾ºÙ");
+ getfield(8, 60, rform.address, "¥Ø«e¦í§}¡G", "¥]¬A¹ì«Ç©ÎªùµP¸¹½X");
+ getfield(11, 20, rform.phone, "³sµ¸¹q¸Ü¡G", "¥]¬Aªø³~¼·¸¹°Ï°ì½X");
+ ans = vans("¥H¤W¸ê®Æ¬O§_¥¿½T(Y/N/Q)¡H[N] ");
+ if (ans == 'q')
+ return 0;
+ if (ans == 'y')
+ break;
+ }
+*/
+ rform.userno = cuser.userno;
+ strcpy(rform.userid, cuser.userid);
+ time(&rform.rtime);
+ rec_add(FN_RUN_RFORM, &rform, sizeof(RFORM));
+ return 0;
+}
+#endif
+
+
+/* ----------------------------------------------------- */
+/* ¶ñ¼gµù»{½X */
+/* ----------------------------------------------------- */
+
+
+#ifdef HAVE_REGKEY_CHECK
+int
+u_verify()
+{
+ char buf[80], key[10];
+ ACCT acct;
+
+ if (HAS_PERM(PERM_VALID))
+ {
+ zmsg("±zªº¨­¤À½T»{¤w¸g§¹¦¨¡A¤£»Ý¶ñ¼g»{ÃÒ½X");
+ }
+ else
+ {
+ if (vget(b_lines, 0, "½Ð¿é¤J»{ÃÒ½X¡G", buf, 8, DOECHO))
+ {
+ archiv32(str_hash(cuser.email, cuser.tvalid), key); /* itoc.010825: ¤£¥Î¶}ÀɤF¡Aª½±µ®³ tvalid ¨Ó¤ñ´N¬O¤F */
+
+ if (str_ncmp(key, buf, 7))
+ {
+ zmsg("©êºp¡A±zªº»{ÃÒ½X¿ù»~");
+ }
+ else
+ {
+ /* ´£¤ÉÅv­­ */
+ sprintf(buf, "KEY: %s", cuser.email);
+ justify_log(cuser.userid, buf);
+ if (acct_load(&acct, cuser.userid) >= 0)
+ {
+ time(&acct.tvalid);
+ acct_setperm(&acct, PERM_VALID, 0);
+ }
+
+ /* ±H«H³qª¾¨Ï¥ÎªÌ */
+ mail_self(FN_ETC_JUSTIFIED, str_sysop, msg_reg_valid, 0);
+ cutmp->status |= STATUS_BIFF;
+ vmsg(msg_reg_valid);
+ }
+ }
+ }
+
+ return XEASY;
+}
+#endif
+
+
+/* ----------------------------------------------------- */
+/* «ì´_Åv­­ */
+/* ----------------------------------------------------- */
+
+
+int
+u_deny()
+{
+ ACCT acct;
+ time_t diff;
+ struct tm *ptime;
+ char msg[80];
+
+ if (!HAS_PERM(PERM_ALLDENY))
+ {
+ zmsg("±z¨S³Q°±Åv¡A¤£»Ý´_Åv");
+ }
+ else
+ {
+ if ((diff = cuser.tvalid - time(0)) < 0) /* °±Åv®É¶¡¨ì¤F */
+ {
+ if (acct_load(&acct, cuser.userid) >= 0)
+ {
+ time(&acct.tvalid);
+#ifdef JUSTIFY_PERIODICAL
+ /* xeon.050112: ¦b»{ÃÒ§Ö¨ì´Á«e®É Cross-Post¡AµM«á tvalid ´N·|³Q³]©w¨ì¥¼¨Ó®É¶¡¡A
+ µ¥´_Åv®É¶¡¨ì¤F¥h´_Åv¡A³o¼Ë´N¥i¥HÁ×¹L­«·s»{ÃÒ¡A©Ò¥H´_Åv«á­n­«·s»{ÃÒ¡C */
+ acct_setperm(&acct, 0, PERM_ALLVALID | PERM_ALLDENY);
+#else
+ acct_setperm(&acct, 0, PERM_ALLDENY);
+#endif
+ vmsg("¤U¦¸½Ð¤Å¦A¥Ç¡A½Ð­«·s¤W¯¸");
+ }
+ }
+ else
+ {
+ ptime = gmtime(&diff);
+ sprintf(msg, "±zÁÙ­nµ¥ %d ¦~ %d ¤Ñ %d ®É %d ¤À %d ¬í¤~¯à¥Ó½Ð´_Åv",
+ ptime->tm_year - 70, ptime->tm_yday, ptime->tm_hour, ptime->tm_min, ptime->tm_sec);
+ vmsg(msg);
+ }
+ }
+
+ return XEASY;
+}
+
+
+/* ----------------------------------------------------- */
+/* ­Ó¤H¤u¨ã */
+/* ----------------------------------------------------- */
+
+
+int
+u_info()
+{
+ char *str, username[UNLEN + 1];
+
+ if (HAS_STATUS(STATUS_COINLOCK))
+ {
+ vmsg(msg_coinlock);
+ return XEASY;
+ }
+
+ move(1, 0);
+ strcpy(username, str = cuser.username);
+ acct_setup(&cuser, 0);
+ if (strcmp(username, str))
+ memcpy(cutmp->username, str, UNLEN + 1);
+ return 0;
+}
+
+
+int
+u_setup()
+{
+ usint ulevel;
+ int len;
+
+ /* itoc.000320: ¼W´î¶µ¥Ø­n§ó§ï len ¤j¤p, ¤]§O§Ñ¤F§ï ufo.h ªººX¼Ð STR_UFO */
+
+ ulevel = cuser.userlevel;
+ if (!ulevel)
+ len = NUMUFOS_GUEST;
+ else if (ulevel & PERM_ALLADMIN)
+ len = NUMUFOS; /* ADMIN °£¤F¥i¥Î acl¡AÁÙ¶¶«K¤]¥i¥H¥ÎÁô¨­³N */
+ else if (ulevel & PERM_CLOAK)
+ len = NUMUFOS - 2; /* ¤£¯à¥ÎµµÁô¡Bacl */
+ else
+ len = NUMUFOS_USER;
+
+ cuser.ufo = cutmp->ufo = bitset(cuser.ufo, len, len, MSG_USERUFO, ufo_tbl);
+
+ return 0;
+}
+
+
+int
+u_lock()
+{
+ char buf[PSWDLEN + 1];
+
+ switch (vans("¬O§_¶i¤J¿Ã¹õÂê©wª¬ºA¡A±N¤£¯à¶Ç°e/±µ¦¬¤ô²y(Y/N/C)¡H[N] "))
+ {
+ case 'c': /* itoc.011226: ¥i¦Û¦æ¿é¤Jµo§bªº²z¥Ñ */
+ if (vget(b_lines, 0, "½Ð¿é¤Jµo§bªº²z¥Ñ¡G", cutmp->mateid, IDLEN + 1, DOECHO))
+ break;
+
+ case 'y':
+ strcpy(cutmp->mateid, "±¾¯¸");
+ break;
+
+ default:
+ return XEASY;
+ }
+
+ bbstate |= STAT_LOCK; /* lkchu.990513: Âê©w®É¤£¥i¦^¤ô²y */
+ cutmp->status |= STATUS_REJECT; /* Âê©w®É¤£¦¬¤ô²y */
+
+ clear();
+ move(5, 20);
+ prints("\033[1;33m" BBSNAME " ¶¢¸m/Âê©wª¬ºA\033[m [%s]", cuser.userid);
+
+ do
+ {
+ vget(b_lines, 0, "¡» ½Ð¿é¤J±K½X¡A¥H¸Ñ°£¿Ã¹õÂê©w¡G", buf, PSWDLEN + 1, NOECHO);
+ } while (chkpasswd(cuser.passwd, buf));
+
+ cutmp->status ^= STATUS_REJECT;
+ bbstate ^= STAT_LOCK;
+
+ return 0;
+}
+
+
+int
+u_log()
+{
+ char fpath[64];
+
+ usr_fpath(fpath, cuser.userid, FN_LOG);
+ more(fpath, NULL);
+ return 0;
+}
+
+
+/* ----------------------------------------------------- */
+/* ³]©wÀÉ®× */
+/* ----------------------------------------------------- */
+
+void
+do_diff(oldfile, filesize, newpath)
+ const void* oldfile; /* Àx¦s´NÀɮתº°O¾ÐÅé¦ì¸m */
+ const int filesize; /* ÂÂÀɮפj¤p */
+ const char* newpath; /* ·sÀɮצWºÙ */
+{
+ char folder[64], fpath[64], title[64];
+ char* board="log";
+ HDR hdr;
+ brd_fpath(folder, board, FN_DIR);
+ int fd = hdr_stamp(folder, 'A', &hdr, fpath);
+ if(fd < 0){
+ vmsg("·s¼W¤å³¹¥¢±Ñ");
+ return;
+ }
+ FILE* fp = fdopen(fd, "w");
+ sprintf(title,"¨t²ÎÀÉ®× %s ­×§ï¬ö¿ý (%s)",newpath,cuser.userid);
+ fprintf(fp, "§@ªÌ: %s (%s)\n¼ÐÃD: %s\n®É¶¡: %s\n\n",
+ STR_SYSOP, SYSOPNICK, title, Btime(&hdr.chrono));
+ fflush(fp);
+ int fdin[2];
+ pipe(fdin);
+ pid_t child;
+ child=fork();
+ if(child == 0){
+ close(fdin[1]);
+ dup2(fdin[0],0); /* ­«·s¾É¦V stdin */
+ dup2(fd,1); /* ­«·s¾É¦V stdout */
+ execlp("diff","diff","-u","-",newpath,NULL);
+ }else{
+ close(fdin[0]);
+ write(fdin[1],oldfile,filesize);
+ close(fdin[1]);
+ }
+#if 0
+ /* lantw44: ¤£»Ý­nµ¥ */
+ waitpid(child,NULL,0); /* µ¥µ{¦¡¶]§¹ */
+#endif
+ close(fd);
+
+ /* ³o¬q¬O±q util/account.c -> keeplog() §Û¨Óªº */
+
+ strcpy(hdr.title, title);
+ strcpy(hdr.owner, STR_SYSOP);
+ rec_bot(folder, &hdr, sizeof(HDR));
+}
+
+
+
+
+/* static */ /* itoc.010110: µ¹ a_xfile() ¥Î */
+void
+x_file(mode, xlist, flist)
+ int mode; /* M_XFILES / M_UFILES */
+ char *xlist[]; /* description list */
+ char *flist[]; /* filename list */
+{
+ int n, i;
+ char *fpath, *desc;
+ char buf[64];
+ int noedit;
+
+ /* lantw44: diff ¥Îªº´X­ÓÅܼơA­×§ï¨t²ÎÀɮɤ~¥Î */
+ void* oldfile;
+ int filesize;
+ struct stat st;
+
+ move(MENU_XPOS, 0);
+ clrtobot();
+ n = 0;
+ while (desc = xlist[n])
+ {
+ n++;
+ if (n <= 9) /* itoc.020123: ¤À¤GÄæ¡A¤@Äæ¤E­Ó */
+ {
+ move(n + MENU_XPOS - 1, 0);
+ clrtoeol();
+ move(n + MENU_XPOS - 1, 2);
+ }
+ else
+ {
+ move(n + MENU_XPOS - 10, 40);
+ }
+ prints("(%d) %s", n, desc);
+
+ if (mode == M_XFILES) /* Thor.980806.µù¸Ñ: ¦L¥XÀɦW */
+ {
+ if (n <= 9)
+ move(n + MENU_XPOS - 1, 22);
+ else
+ move(n + MENU_XPOS - 10, 62);
+ outs(flist[n - 1]);
+ }
+ }
+
+ vget(b_lines, 0, "½Ð¿ï¾ÜÀÉ®×½s¸¹¡A©Î«ö [0] ¨ú®ø¡G", buf, 3, DOECHO);
+ i = atoi(buf);
+ if (i <= 0 || i > n)
+ return;
+
+ n = vget(b_lines, 36, "(D)§R°£ (E)½s¿è (V)À˵ø [Q]¨ú®ø¡H", buf, 3, LCECHO);
+ if (n != 'd' && n != 'e' && n !='v')
+ return;
+
+ fpath = flist[--i];
+ if (mode == M_UFILES)
+ usr_fpath(buf, cuser.userid, fpath);
+ else{
+ strcpy(buf, fpath);
+ }
+
+ if (n == 'd')
+ {
+ if (vans(msg_sure_ny) == 'y'){
+ unlink(buf);
+ if( mode == M_XFILES ){
+ alog("§R¨t²ÎÀÉ", fpath);
+ }
+ }
+ }else if(n == 'v'){
+ if(more(buf, (char*)0)<0){
+ vmsg("ÀÉ®×¶}±Ò¥¢±Ñ (¥i¯àÀɮפ£¦s¦b)");
+ }
+ return;
+ }
+ else
+ {
+ char nodiff=0;
+ if(mode == M_XFILES){
+ if(stat(fpath,&st)<0){
+ vmsg("Àɮ׬ÛÃö¸ê°TŪ¨ú¥¢±Ñ¡A²¤¹L°õ¦æ diff");
+ nodiff=1;
+ }else{
+ filesize=st.st_size;
+ oldfile=malloc(filesize);
+ if(oldfile==NULL){
+ vmsg("malloc ¥¢±Ñ¡A²¤¹L°õ¦æ diff");
+ nodiff=1;
+ }else{
+ FILE* fp;
+ if((fp=fopen(fpath,"r"))==NULL){
+ vmsg("ÀÉ®×¶}±Ò¥¢±Ñ¡A²¤¹L°õ¦æ diff");
+ nodiff=1;
+ }else{
+ fread(oldfile,1,filesize,fp);
+ fclose(fp);
+ }
+ }
+ }
+ }
+ noedit=vedit(buf, 0);
+ vmsg(noedit ? "­ì«Ê¤£°Ê" : "§ó·s§¹²¦"); /* Thor.981020: ª`·N³Qtalkªº°ÝÃD */
+ if(noedit == 0 && mode == M_XFILES){
+ alog("§ï¨t²ÎÀÉ", fpath);
+ if(!nodiff){
+ do_diff(oldfile,filesize,fpath);
+ free(oldfile);
+ }
+ }
+ }
+}
+
+
+int
+u_xfile()
+{
+ int i;
+
+ static char *desc[] =
+ {
+ "¤W¯¸¦aÂI³]©wÀÉ",
+ "¦W¤ùÀÉ",
+ "ñ¦WÀÉ.1",
+ "ñ¦WÀÉ.2",
+ "ñ¦WÀÉ.3",
+ "ñ¦WÀÉ.4",
+ "ñ¦WÀÉ.5",
+ "ñ¦WÀÉ.6",
+ "¼È¦sÀÉ.1",
+ "¼È¦sÀÉ.2",
+ "¼È¦sÀÉ.3",
+ "¼È¦sÀÉ.4",
+ "¼È¦sÀÉ.5",
+ NULL
+ };
+
+ static char *path[] =
+ {
+ "acl",
+ "plans",
+ FN_SIGN ".1",
+ FN_SIGN ".2",
+ FN_SIGN ".3",
+ FN_SIGN ".4",
+ FN_SIGN ".5",
+ FN_SIGN ".6",
+ "buf.1",
+ "buf.2",
+ "buf.3",
+ "buf.4",
+ "buf.5"
+ };
+
+ i = HAS_PERM(PERM_ALLADMIN) ? 0 : 1;
+ x_file(M_UFILES, &desc[i], &path[i]);
+ return 0;
+}
diff --git a/maple/visio.c b/maple/visio.c
new file mode 100644
index 0000000..ca247df
--- /dev/null
+++ b/maple/visio.c
@@ -0,0 +1,2071 @@
+/*-------------------------------------------------------*/
+/* visio.c ( NTHU CS MapleBBS Ver 3.00 ) */
+/*-------------------------------------------------------*/
+/* target : VIrtual Screen Input Output routines */
+/* create : 95/03/29 */
+/* update : 96/10/10 */
+/*-------------------------------------------------------*/
+
+
+#include <stdarg.h>
+#include <arpa/telnet.h>
+
+
+#include "bbs.h"
+
+
+#define VO_MAX 3072 /* output buffer ¤j¤p */
+#define VI_MAX 256 /* input buffer ¤j¤p */
+
+
+#define INPUT_ACTIVE 0
+#define INPUT_IDLE 1
+
+
+static int cur_row, cur_col;
+static int cur_pos; /* current position with ANSI codes */
+
+
+/* ----------------------------------------------------- */
+/* º~¦r (zh-char) §PÂ_ */
+/* ----------------------------------------------------- */
+
+
+#ifdef HAVE_MULTI_BYTE
+int /* 1:¬O 0:¤£¬O */
+is_zhc_low(str, n) /* hightman.060504: §PÂ_¦r¦ê¤¤ªº²Ä n ­Ó¦r²Å¬O§_¬°º~¦rªº«á¥b¦r */
+ char *str;
+ int n;
+{
+ char *end;
+
+ end = str + n;
+ while (str < end)
+ {
+ if (IS_ZHC_HI(*str))
+ str++;
+ str++;
+ }
+
+ return (str - end);
+}
+#endif
+
+
+/* ----------------------------------------------------- */
+/* output routines */
+/* ----------------------------------------------------- */
+
+
+static uschar vo_pool[VO_MAX];
+static int vo_size;
+
+
+#ifdef VERBOSE
+static void
+telnet_flush(data, size)
+ char *data;
+ int size;
+{
+ int oset;
+
+ oset = 1;
+
+ if (select(1, NULL, &oset, NULL, NULL) <= 0)
+ {
+ abort_bbs();
+ }
+
+ xwrite(0, data, size);
+}
+
+#else
+
+# define telnet_flush(data, size) send(0, data, size, 0)
+#endif
+
+
+static void
+oflush()
+{
+ int size;
+
+ if (size = vo_size)
+ {
+ telnet_flush(vo_pool, size);
+ vo_size = 0;
+ }
+}
+
+
+static void
+output(str, len)
+ uschar *str;
+ int len;
+{
+ int size, ch;
+ uschar *data;
+
+ size = vo_size;
+ data = vo_pool;
+ if (size + len > VO_MAX - 8)
+ {
+ telnet_flush(data, size);
+ size = len;
+ }
+ else
+ {
+ data += size;
+ size += len;
+ }
+
+ while (--len >= 0)
+ {
+ ch = *str++;
+ *data++ = ch;
+ if (ch == IAC)
+ {
+ *data++ = ch;
+ size++;
+ }
+ }
+ vo_size = size;
+}
+
+
+static void
+ochar(ch)
+ int ch;
+{
+ uschar *data;
+ int size;
+
+ data = vo_pool;
+ size = vo_size;
+ if (size > VO_MAX - 2)
+ {
+ telnet_flush(data, size);
+ size = 0;
+ }
+ data[size++] = ch;
+ vo_size = size;
+}
+
+
+void
+bell()
+{
+ static char sound[1] = {Ctrl('G')};
+
+ telnet_flush(sound, sizeof(sound));
+}
+
+
+/* ----------------------------------------------------- */
+/* virtual screen */
+/* ----------------------------------------------------- */
+
+
+#define o_ansi(x) output(x, sizeof(x)-1)
+
+#define o_clear() o_ansi("\033[;H\033[2J")
+#define o_cleol() o_ansi("\033[K")
+#define o_standup() o_ansi("\033[7m")
+#define o_standdown() o_ansi("\033[m")
+
+
+static int docls;
+static int roll;
+static int scrollcnt, tc_col, tc_row;
+
+
+static screenline vbuf[T_LINES];
+static screenline *cur_slp; /* current screen line pointer */
+
+
+/* itoc.020611.µù¸Ñ: ®y¼ÐÀ³¸Ó¬O (x, y) ¦Ó¤£¬O (y, x)
+ ¦]¬° row ¬O ¦V¤U¬°¥¿¡Acolumn ¬O¦V¥k¬°¥¿¡A
+ ¦Ó +x cross +y = +z ¬O¥X¿Ã¹õ­± */
+
+void
+move(x, y)
+ int x; /* row */
+ int y; /* column */
+{
+ screenline *cslp;
+
+ if (x > b_lines)
+ return;
+
+ if (y >= b_cols)
+ y = 0;
+
+ cur_row = x;
+ if ((x += roll) > b_lines)
+ x -= b_lines + 1;
+ cur_slp = cslp = &vbuf[x];
+ cur_col = y;
+
+#if 0
+
+ /* ------------------------------------- */
+ /* ¹LÂo ANSI codes¡A­pºâ´å¼Ð¯u¥¿©Ò¦b¦ì¸m */
+ /* ------------------------------------- */
+
+ if (y)
+ {
+ int ch, ansi;
+ int len;
+ uschar *str;
+
+ ansi = 0;
+ x = y;
+ len = cslp->len;
+ str = cslp->data;
+ str[len] = '\0';
+ while (len && (ch = *str))
+ {
+ str++;
+ len--;
+
+ if (ansi)
+ {
+ x++;
+ if (ch == 'm')
+ ansi = 0;
+ continue;
+ }
+ if (ch == KEY_ESC)
+ {
+ x++;
+ ansi = 1;
+ continue;
+ }
+ y--;
+ if (y <= 0)
+ break;
+ }
+ y = x;
+ }
+#endif
+
+ cur_pos = y;
+}
+
+
+#if 0
+static void
+getxy(x, y)
+ int *x, *y;
+{
+ *x = cur_row;
+ *y = cur_col;
+}
+#endif
+
+
+/*-------------------------------------------------------*/
+/* ­pºâ slp ¤¤ len ¤§³Bªº´å¼Ð column ©Ò¦b */
+/*-------------------------------------------------------*/
+
+
+#if 0
+static int
+ansicol(slp, len)
+ screenline *slp;
+ int len;
+{
+ uschar *str;
+ int ch, ansi, col;
+
+ if (!len || !(slp->mode & SL_ANSICODE))
+ return len;
+
+ ansi = col = 0;
+ str = slp->data;
+
+ while (len-- && (ch = *str++))
+ {
+ if (ch == KEY_ESC && *str == '[')
+ {
+ ansi = 1;
+ continue;
+ }
+ if (ansi)
+ {
+ if (ch == 'm')
+ ansi = 0;
+ continue;
+ }
+ col++;
+ }
+ return col;
+}
+#endif
+
+
+static void
+rel_move(new_col, new_row)
+ int new_col, new_row;
+{
+ int was_col, was_row;
+ char buf[16];
+
+ if (new_row > b_lines || new_col >= b_cols)
+ return;
+
+ was_col = tc_col;
+ was_row = tc_row;
+
+ tc_col = new_col;
+ tc_row = new_row;
+
+ if (new_col == 0)
+ {
+ if (new_row == was_row)
+ {
+ if (was_col)
+ ochar('\r');
+ return;
+ }
+ else if (new_row == was_row + 1)
+ {
+ ochar('\n');
+ if (was_col)
+ ochar('\r');
+ return;
+ }
+ }
+
+ if (new_row == was_row)
+ {
+ if (was_col == new_col)
+ return;
+
+ if (new_col == was_col - 1)
+ {
+ ochar(KEY_BKSP);
+ return;
+ }
+ }
+
+ sprintf(buf, "\033[%d;%dH", new_row + 1, new_col + 1);
+ output(buf, strlen(buf));
+}
+
+
+static void
+standoutput(slp, ds, de)
+ screenline *slp;
+ int ds, de;
+{
+ uschar *data;
+ int sso, eso;
+
+ data = slp->data;
+ sso = slp->sso;
+ eso = slp->eso;
+
+ if (eso <= ds || sso >= de)
+ {
+ output(data + ds, de - ds);
+ return;
+ }
+
+ if (sso > ds)
+ output(data + ds, sso - ds);
+ else
+ sso = ds;
+
+ o_standup();
+ output(data + sso, BMIN(eso, de) - sso);
+ o_standdown();
+
+ if (de > eso)
+ output(data + eso, de - eso);
+}
+
+
+#define STANDOUT cur_slp->sso = cur_pos; cur_slp->mode |= SL_STANDOUT;
+#define STANDEND cur_slp->eso = cur_pos;
+
+
+#if 0
+static int standing;
+
+
+static void
+standout()
+{
+ if (!standing)
+ {
+ standing = 1;
+ cur_slp->sso = cur_slp->eso = cur_pos;
+ cur_slp->mode |= SL_STANDOUT;
+ }
+}
+
+
+static void
+standend()
+{
+ if (standing)
+ {
+ standing = 0;
+ if (cur_slp->eso < cur_pos)
+ cur_slp->eso = cur_pos;
+ }
+}
+#endif
+
+
+static void
+vs_redraw()
+{
+ screenline *slp;
+ int i, j, len, mode, width;
+
+ tc_col = tc_row = docls = scrollcnt = vo_size = i = 0;
+ o_clear();
+ for (slp = &vbuf[j = roll]; i <= b_lines; i++, j++, slp++)
+ {
+ if (j > b_lines)
+ {
+ j = 0;
+ slp = vbuf;
+ }
+
+ len = slp->len;
+ width = slp->width;
+ slp->oldlen = width;
+ mode = slp->mode &=
+ (len <= slp->sso) ? ~(SL_MODIFIED | SL_STANDOUT) : ~(SL_MODIFIED);
+ if (len)
+ {
+ rel_move(0, i);
+
+ if (mode & SL_STANDOUT)
+ standoutput(slp, 0, len);
+ else
+ output(slp->data, len);
+
+ tc_col = width;
+ }
+ }
+ rel_move(cur_col, cur_row);
+ oflush();
+}
+
+
+void
+refresh()
+{
+ screenline *slp;
+ int i, j, len, mode, width, smod, emod;
+
+ i = scrollcnt;
+
+ if (docls || abs(i) >= b_lines)
+ {
+ vs_redraw();
+ return;
+ }
+
+ if (i)
+ {
+ char buf[T_LINES];
+
+ scrollcnt = j = 0;
+ if (i < 0)
+ {
+ sprintf(buf, "\033[%dL", -i);
+ i = strlen(buf);
+ }
+ else
+ {
+ do
+ {
+ buf[j] = '\n';
+ } while (++j < i);
+ j = b_lines;
+ }
+ rel_move(0, j);
+ output(buf, i);
+ }
+
+ for (i = 0, slp = &vbuf[j = roll]; i <= b_lines; i++, j++, slp++)
+ {
+ if (j > b_lines)
+ {
+ j = 0;
+ slp = vbuf;
+ }
+
+ len = slp->len;
+ width = slp->width;
+ mode = slp->mode;
+
+ if (mode & SL_MODIFIED)
+ {
+ slp->mode = mode &=
+ (len <= slp->sso) ? ~(SL_MODIFIED | SL_STANDOUT) : ~(SL_MODIFIED);
+
+ if ((smod = slp->smod) < len)
+ {
+ emod = slp->emod + 1;
+ if (emod >= len)
+ emod = len;
+
+ rel_move(smod, i);
+
+ /* rel_move(ansicol(slp, smod), i); */
+
+ if (mode & SL_STANDOUT)
+ standoutput(slp, smod, emod);
+ else
+ output(&slp->data[smod], emod - smod);
+
+ /* tc_col = ansicol(slp, emod); */
+
+#if 0
+ if (mode & SL_ANSICODE)
+ {
+ uschar *data;
+
+ data = slp->data;
+ mode = 0;
+ len = emod;
+
+ while (len--)
+ {
+ smod = *data++;
+ if (smod == KEY_ESC)
+ {
+ mode = 1;
+ emod--;
+ continue;
+ }
+
+ if (mode)
+ {
+ if (smod == 'm')
+ mode = 0;
+ emod--;
+ }
+ }
+ }
+
+ tc_col = emod;
+#endif
+
+ tc_col = (width != len) ? width : emod;
+ }
+ }
+
+ if (slp->oldlen > width)
+ {
+ rel_move(width, i);
+ o_cleol();
+ }
+ slp->oldlen = width;
+ }
+ rel_move(cur_col, cur_row);
+ oflush();
+}
+
+
+void
+clear()
+{
+ int i;
+ screenline *slp;
+
+ docls = 1;
+ cur_pos = cur_col = cur_row = roll = i = 0;
+ cur_slp = slp = vbuf;
+ while (i++ <= b_lines)
+ {
+ /* memset(slp, 0, sizeof(screenline)); */
+ /* ¥u»Ý slp->data[0] = '\0' §Y¥i¡A¤£»Ý²M¾ã­Ó ANSILINELEN */
+ memset(slp, 0, sizeof(screenline) - ANSILINELEN + 1);
+ slp++;
+ }
+}
+
+
+void
+clrtoeol() /* clear screen to end of line (¦C§À) */
+{
+ screenline *slp = cur_slp;
+ int len;
+
+ if (len = cur_pos)
+ {
+ slp->len = len;
+ slp->width = cur_col;
+ }
+ else
+ {
+ /* ²M±¼ oldlen ¥H«áªº¥þ³¡¡Fdata ¥u»Ý²M­º byte §Y¥i */
+ memset((char *) slp + sizeof(slp->oldlen), 0, sizeof(screenline) - ANSILINELEN + 1 - sizeof(slp->oldlen));
+ }
+}
+
+
+void
+clrtobot() /* clear screen to bottom (¿Ã¹õ©³³¡) */
+{
+ screenline *slp;
+ int i, j;
+
+ i = cur_row;
+ j = i + roll;
+ slp = cur_slp;
+ while (i <= b_lines)
+ {
+ if (j > b_lines)
+ {
+ j = 0;
+ slp = vbuf;
+ }
+ /* ²M±¼ oldlen ¥H«áªº¥þ³¡¡Fdata ¥u»Ý²M­º byte §Y¥i */
+ memset((char *) slp + sizeof(slp->oldlen), 0, sizeof(screenline) - ANSILINELEN + 1 - sizeof(slp->oldlen));
+
+ i++;
+ j++;
+ slp++;
+ }
+}
+
+
+void
+outc(ch)
+ int ch;
+{
+ screenline *slp;
+ uschar *data;
+ int i, cy, pos;
+
+ static char ansibuf[16] = "\033";
+ static int ansipos = 0;
+
+ slp = cur_slp;
+ pos = cur_pos;
+
+ if (ch == '\n')
+ {
+ cy = cur_col;
+
+new_line:
+
+ ansipos = 0;
+ if (pos)
+ {
+ slp->len = pos;
+ slp->width = cy;
+
+#if 0
+ if (standing)
+ {
+ standing = 0;
+ if (pos <= slp->sso)
+ slp->mode &= ~SL_STANDOUT;
+ else if (slp->eso < pos)
+ slp->eso = pos;
+ }
+#endif
+ }
+ else
+ {
+ /* ²M±¼ oldlen ¥H«áªº¥þ³¡¡Fdata ¥u»Ý²M­º byte §Y¥i */
+ memset((char *) slp + sizeof(slp->oldlen), 0, sizeof(screenline) - ANSILINELEN + 1 - sizeof(slp->oldlen));
+ }
+
+ move(cur_row + 1, 0);
+ return;
+ }
+
+ if (ch < 0x20)
+ {
+ if (ch == KEY_ESC)
+ ansipos = 1;
+
+ return;
+ }
+
+ data = &(slp->data[pos]); /* «ü¦V¥Ø«e¿é¥X¦ì¸m */
+
+ /* -------------------- */
+ /* ¸É¨¬©Ò»Ý­nªºªÅ¥Õ¦r¤¸ */
+ /* -------------------- */
+
+ cy = slp->len - pos;
+ if (cy > 0)
+ {
+ cy = *data;
+ }
+ else
+ {
+ while (cy < 0)
+ {
+ data[cy++] = ' ';
+ }
+
+ slp->len = /* slp->width = */ pos + 1;
+ }
+
+ /* ---------------------------- */
+ /* ANSI control code ¤§¯S§O³B²z */
+ /* ---------------------------- */
+
+ if (i = ansipos)
+ {
+ if ((i < 15) &&
+ ((ch >= '0' && ch <= '9') || ch == '[' || ch == 'm' || ch == ';'))
+ {
+ ansibuf[i++] = ch;
+
+ if (ch != 'm')
+ {
+ ansipos = i;
+ return;
+ }
+
+ ch = i + pos;
+ if (ch < ANSILINELEN - 1)
+ {
+ memcpy(data, ansibuf, i);
+ slp->len = slp->emod = cur_pos = ch;
+ slp->mode |= SL_MODIFIED;
+ if (slp->smod > pos)
+ slp->smod = pos;
+ }
+ }
+ ansipos = 0;
+ return;
+ }
+
+ /* ---------------------------- */
+ /* §P©w­þ¨Ç¤å¦r»Ý­n­«·s°e¥X¿Ã¹õ */
+ /* ---------------------------- */
+
+ if ( /* !(slp->mode & SL_ANSICODE) && */ (ch != cy))
+ {
+ *data = ch;
+ cy = slp->mode;
+ if (cy & SL_MODIFIED)
+ {
+ if (slp->smod > pos)
+ slp->smod = pos;
+ if (slp->emod < pos)
+ slp->emod = pos;
+ }
+ else
+ {
+ slp->mode = cy | SL_MODIFIED;
+ slp->smod = slp->emod = pos;
+ }
+ }
+
+ cur_pos = ++pos;
+ cy = ++cur_col;
+
+ if ((pos >= ANSILINELEN) /* || (cy >= b_cols) */ )
+ goto new_line;
+
+ if (slp->width < cy)
+ slp->width = cy;
+}
+
+
+void
+outs(str)
+ uschar *str;
+{
+ int ch;
+
+ while (ch = *str)
+ {
+ outc(ch);
+ str++;
+ }
+}
+
+
+/* ----------------------------------------------------- */
+/* eXtended output: ¨q¥X user ªº name ©M nick */
+/* ----------------------------------------------------- */
+
+
+#ifdef SHOW_USER_IN_TEXT
+void
+outx(str)
+ uschar *str;
+{
+ int ch;
+
+ while (ch = *str)
+ {
+ /* itoc.020301: ESC + * + s µ¥±±¨î½X */
+ if (ch == KEY_ESC && str[1] == '*')
+ {
+ switch (str[2])
+ {
+ case 's': /* **s Åã¥Ü ID */
+ outs(cuser.userid);
+ str += 3;
+ continue;
+ case 'n': /* **n Åã¥Ü¼ÊºÙ */
+ outs(cuser.username);
+ str += 3;
+ continue;
+ case 'r': /* **r Åã¥Ü¯u¹ê©m¦W */
+ outs(cuser.realname);
+ str += 3;
+ continue;
+ }
+ }
+ outc(ch);
+ str++;
+ }
+}
+#endif
+
+
+/* ----------------------------------------------------- */
+/* clear the bottom line and show the message */
+/* ----------------------------------------------------- */
+
+
+void
+outz(str)
+ uschar *str;
+{
+ move(b_lines, 0);
+ clrtoeol();
+ outs(str);
+}
+
+
+void
+outf(str)
+ uschar *str;
+{
+ outz(str);
+ prints("%*s\033[m", d_cols, "");
+}
+
+
+void
+prints(char *fmt, ...)
+{
+ va_list args;
+ uschar buf[512], *str; /* ³Ìªø¥u¯à¦L 512 ¦r */
+ int cc;
+
+ va_start(args, fmt);
+ vsprintf(buf, fmt, args);
+ va_end(args);
+ for (str = buf; cc = *str; str++)
+ outc(cc);
+}
+
+
+void
+scroll()
+{
+ scrollcnt++;
+ if (++roll > b_lines)
+ roll = 0;
+ move(b_lines, 0);
+ clrtoeol();
+}
+
+
+void
+rscroll()
+{
+ scrollcnt--;
+ if (--roll < 0)
+ roll = b_lines;
+ move(0, 0);
+ clrtoeol();
+}
+
+
+/* ----------------------------------------------------- */
+
+
+static int old_col, old_row, old_roll;
+static int old_pos; /* Thor.990401: ¦h¦s¤@­Ó */
+
+
+/* static void */
+void /* Thor.981028: ¬°¤FÅý talk.c ¦³¤H©I¥s®É·|show¦r */
+cursor_save()
+{
+ old_col = cur_col;
+ old_row = cur_row;
+
+ old_pos = cur_pos; /* Thor.990401: ¦h¦s¤@­Ó */
+}
+
+
+/* static void */
+void /* Thor.981028: ¬°¤FÅý talk.c ¦³¤H©I¥s®É·|show¦r */
+cursor_restore()
+{
+ move(old_row, old_col);
+
+ cur_pos = old_pos; /* Thor.990401: ¦hÁÙ­ì¤@­Ó */
+}
+
+
+void
+save_foot(slp)
+ screenline *slp;
+{
+ int i;
+ int lines[3] = {0, b_lines, b_lines - 1}; /* Àx¦s³o¤T¦C */
+
+ for (i = 0; i < 3; i++)
+ {
+ move(lines[i], 0);
+ memcpy(slp + i, cur_slp, sizeof(screenline));
+ slp[i].smod = 0;
+ slp[i].emod = ANSILINELEN; /* Thor.990125:¤£½×³Ì«á¤@¦¸§ï¨ì­þ, ¥þ³¡­nø¤W */
+ slp[i].oldlen = ANSILINELEN;
+ slp[i].mode |= SL_MODIFIED;
+ }
+}
+
+
+void
+restore_foot(slp, line)
+ screenline *slp;
+ int line; /* ­n«ì´_ lines[] ¸Ì­±ªº«e´X¦C */
+{
+ int i;
+ int lines[3] = {0, b_lines, b_lines - 1}; /* «ì´_³o¤T¦C */
+
+ for (i = 0; i < line; i++)
+ {
+ move(lines[i], 0);
+ memcpy(cur_slp, slp + i, sizeof(screenline));
+ }
+}
+
+
+int
+vs_save(slp)
+ screenline *slp;
+{
+ old_roll = roll;
+ memcpy(slp, vbuf, sizeof(screenline) * (b_lines + 1));
+ return old_roll; /* itoc.030723: ¶Ç¦^¥Ø«eªº roll */
+}
+
+
+void
+vs_restore(slp)
+ screenline *slp;
+{
+ memcpy(vbuf, slp, sizeof(screenline) * (b_lines + 1));
+ roll = old_roll;
+ vs_redraw();
+}
+
+
+#if 0
+int
+imsg(msg) /* itoc.010827: ­«­n°T®§Åã¥Ü important message */
+ char *msg; /* length <= 54 */
+{
+ int i;
+ time_t now;
+ char scroller[128], spacebar[60], buf[80];
+ char alphabet[26][3] =
+ {
+ "¢é", "¢ê", "¢ë", "¢ì", "¢í", "¢î", "¢ï", "¢ð", "¢ñ",
+ "¢ò", "¢ó", "¢ô", "¢õ", "¢ö", "¢÷", "¢ø", "¢ù", "¢ú",
+ "¢û", "¢ü", "¢ý", "¢þ", "£@", "£A", "£B", "£C"
+ };
+
+ time(&now); /* ¥Î®É¶¡¨Ó random */
+
+#if 1 /* §Q¥Î¶]°¨¿O®ÄªG¨Ó´£¥Ü­«­n°T®§ */
+ i = now % 6 + 1; /* ÃC¦â½X */
+ sprintf(spacebar, "\033[%d;H", b_lines + 1); /* ²¾¦ì½X */
+ sprintf(buf, "\033[3%dm¢j¢k¢l¢m¢n¢o\033[1;37;4%dm ­«­n°T®§½Ðª`·N \033[m", i, i);
+
+ /* ¤£²M°£ b_lines¡A¨Ï¦³²H¥Xªº®ÄªG */
+ for (i = 1; i <= 47; i += 2)
+ {
+ sprintf(scroller, "%s%s", spacebar, buf); /* scroller ¶]°¨¿O */
+ strcat(spacebar, " "); /* ¤@¦¸¸õ¤G®æ¡A¼W¥[³t«× */
+ telnet_flush(scroller, strlen(scroller) + 1); /* §Y®É¿é¥X¡A¶]°¨¿O®ÄªG */
+ usleep(1000);
+ }
+#endif
+
+ i = now % 26; /* 'a' ~ 'z' ¶Ã¨ú¤@Áä */
+
+ if (msg)
+ {
+ move(b_lines, 0);
+ clrtoeol();
+ prints(COLOR1 " ¡» %-55s " COLOR2 " [½Ð«ö %s ÁäÄ~Äò] \033[m", msg, alphabet[i]);
+ }
+ else
+ {
+ move(b_lines, 27);
+ prints(COLOR1 " ¡´ ½Ð«ö %s ÁäÄ~Äò ¡´ \033[m", alphabet[i]);
+ }
+
+ i += 'a';
+ while (vkey() != i)
+ ;
+
+ return i;
+}
+#endif /* VIEW_IMSG */
+
+
+#ifdef POPUP_MESSAGE
+int
+vmsg(msg)
+ char *msg; /* length <= 54 */
+{
+ if (msg)
+ return pmsg(msg);
+
+ move(b_lines, 0);
+ outs(VMSG_NULL);
+ move(b_lines, 0); /* itoc.010127: ­×¥¿¦b°»´ú¥ª¥kÁä¥þ§Î¤U¡A«ö¥ªÁä·|¸õÂ÷¤G¼h¿ï³æªº°ÝÃD */
+ return vkey();
+}
+#else
+int
+vmsg(msg)
+ char *msg; /* length <= 54 */
+{
+ if (msg)
+ {
+ move(b_lines, 0);
+ clrtoeol();
+ prints(COLOR1 " ¡» %-55s " COLOR2 " [½Ð«ö¥ô·NÁäÄ~Äò] \033[m", msg);
+ }
+ else
+ {
+ move(b_lines, 0);
+ outs(VMSG_NULL);
+ move(b_lines, 0); /* itoc.010127: ­×¥¿¦b°»´ú¥ª¥kÁä¥þ§Î¤U¡A«ö¥ªÁä·|¸õÂ÷¤G¼h¿ï³æªº°ÝÃD */
+ }
+ return vkey();
+}
+#endif
+
+
+static inline void
+zkey() /* press any key or timeout */
+{
+ /* static */ struct timeval tv = {1, 100};
+ /* Thor.980806: man page °²³] timeval struct¬O·|§ïÅܪº */
+
+ int rset;
+
+ rset = 1;
+ select(1, (fd_set *) &rset, NULL, NULL, &tv);
+
+#if 0
+ if (select(1, &rset, NULL, NULL, &tv) > 0)
+ {
+ recv(0, &rset, sizeof(&rset), 0);
+ }
+#endif
+}
+
+
+void
+zmsg(msg) /* easy message */
+ char *msg;
+{
+ outz(msg);
+ move(b_lines, 0); /* itoc.031029: ­×¥¿¦b°»´ú¥ª¥kÁä¥þ§Î¤U¡A«ö¥ªÁä·|¸õÂ÷¤G¼h¿ï³æªº°ÝÃD */
+
+ refresh();
+ zkey();
+}
+
+
+void
+vs_bar(title)
+ char *title;
+{
+ clear();
+ prints("\033[1;33;44m¡i %s ¡j\033[m\n", title);
+}
+
+
+static void
+vs_line(msg)
+ char *msg;
+{
+ int head, tail;
+
+ if (msg)
+ head = (strlen(msg) + 1) >> 1;
+ else
+ head = 0;
+
+ tail = head;
+
+ while (head++ < 38)
+ outc('-');
+
+ if (tail)
+ {
+ outc(' ');
+ outs(msg);
+ outc(' ');
+ }
+
+ while (tail++ < 38)
+ outc('-');
+ outc('\n');
+}
+
+
+/* ----------------------------------------------------- */
+/* input routines */
+/* ----------------------------------------------------- */
+
+
+static uschar vi_pool[VI_MAX];
+static int vi_size;
+static int vi_head;
+
+
+static int vio_fd;
+
+
+#ifdef EVERY_Z
+
+static int holdon_fd; /* Thor.980727: ¸õ¥Xchat&talk¼È¦svio_fd¥Î */
+
+
+void
+vio_save()
+{
+ holdon_fd = vio_fd;
+ vio_fd = 0;
+}
+
+
+void
+vio_restore()
+{
+ vio_fd = holdon_fd;
+ holdon_fd = 0;
+}
+
+
+int
+vio_holdon()
+{
+ return holdon_fd;
+}
+#endif
+
+
+#if 0
+struct timeval
+{
+ int tv_sec; /* timeval second */
+ int tv_usec; /* timeval micro-second */
+};
+#endif
+
+static struct timeval vio_to = {60, 0};
+
+
+void
+add_io(fd, timeout)
+ int fd;
+ int timeout;
+{
+ vio_fd = fd;
+ vio_to.tv_sec = timeout;
+}
+
+
+static inline int
+iac_count(current)
+ uschar *current;
+{
+ switch (*(current + 1))
+ {
+ case DO:
+ case DONT:
+ case WILL:
+ case WONT:
+ return 3;
+
+ case SB: /* loop forever looking for the SE */
+ {
+ uschar *look = current + 2;
+
+ /* fuse.030518: ½u¤W½Õ¾ãµe­±¤j¤p¡A­«§ì b_lines */
+ if ((*look) == TELOPT_NAWS)
+ {
+ b_lines = ntohs(* (short *) (look + 3)) - 1;
+ b_cols = ntohs(* (short *) (look + 1)) - 1;
+ if (b_lines >= T_LINES)
+ b_lines = T_LINES - 1;
+ else if (b_lines < 23)
+ b_lines = 23;
+ if (b_cols >= T_COLS)
+ b_cols = T_COLS - 1;
+ else if (b_cols < 79)
+ b_cols = 79;
+ d_cols = b_cols - 79;
+ }
+
+ for (;;)
+ {
+ if ((*look++) == IAC)
+ {
+ if ((*look++) == SE)
+ {
+ return look - current;
+ }
+ }
+ }
+ }
+ }
+ return 1;
+}
+
+
+int
+igetch()
+{
+
+#define IM_TRAIL 0x01
+#define IM_REPLY 0x02 /* ^R */
+#define IM_TALK 0x04
+
+ static int imode = 0;
+ static int idle = 0;
+
+ int cc, fd, nfds, rset;
+ uschar *data;
+
+ data = vi_pool;
+ nfds = 0;
+
+ for (;;)
+ {
+ if (vi_size <= vi_head)
+ {
+ if (nfds == 0)
+ {
+ refresh();
+ fd = (imode & IM_REPLY) ? 0 : vio_fd;
+ nfds = fd + 1;
+ if (fd)
+ fd = 1 << fd;
+ }
+
+ for (;;)
+ {
+ struct timeval tv = vio_to;
+ /* Thor.980806: man page °²³] timeval ¬O·|§ïÅܪº */
+
+ rset = 1 | fd;
+ cc = select(nfds, (fd_set *) & rset, NULL, NULL, &tv /*&vio_to*/);
+ /* Thor.980806: man page °²³] timeval ¬O·|§ïÅܪº */
+
+ if (cc > 0)
+ {
+ if (fd & rset)
+ return I_OTHERDATA;
+
+ cc = recv(0, data, VI_MAX, 0);
+ if (cc > 0)
+ {
+ vi_head = (*data) == IAC ? iac_count(data) : 0;
+ if (vi_head >= cc)
+ continue;
+ vi_size = cc;
+
+#ifdef DETAIL_IDLETIME
+ if (cutmp)
+#else
+ if (idle && cutmp)
+#endif
+ {
+ idle = 0;
+
+#ifdef DETAIL_IDLETIME
+ time(&cutmp->idle_time); /* ­Y #define DETAIL_IDLETIME¡A«h idle_time ªí¥Ü¶}©l¶¢¸mªº®É¶¡(¬í) */
+#else
+ cutmp->idle_time = 0; /* ­Y #undef DETAIL_IDLETIME¡A«h idle_time ªí¥Ü¤w¸g¶¢¸m¤F¦h¤[(¤À) */
+#endif
+
+#ifdef BMW_COUNT
+ /* itoc.010421: «ö¥ô¤@Áä«á±µ¦¬¤ô²y¼Æ¦^Âk 0 */
+ cutmp->bmw_count = 0;
+#endif
+ }
+ break;
+ }
+ if ((cc == 0) || (errno != EINTR))
+ abort_bbs();
+ }
+ else if (cc == 0)
+ {
+ cc = vio_to.tv_sec;
+ if (cc < 60) /* paging timeout : ¨C 60 ¬í§ó·s¤@¦¸ idle */
+ return I_TIMEOUT;
+
+ idle += cc / 60;
+ vio_to.tv_sec = cc + 60; /* Thor.980806: ¨C¦¸timeout³£¼W¥[60¬í¡A©Ò¥H¤ù¤l·U´«·UºC¡A¦nÃi:p */
+ /* Thor.990201.µù¸Ñ: °£¤Ftalk_rqst¡Bchat¤§¥~¡A»Ý­n¦b°Ê¤@°Ê¤§«á¡A­«³]tv_sec¬°60¬í¶Ü? (¹w³]­È) */
+
+#ifdef TIME_KICKER
+ if (idle > IDLE_TIMEOUT)
+ {
+ outs("¡¹ ¶W¹L¶¢¸m®É¶¡¡I");
+ refresh();
+ abort_bbs();
+ }
+ else if (idle >= IDLE_TIMEOUT - IDLE_WARNOUT) /* itoc.001222: ¶¢¸m¹L¤[ĵ§i */
+ {
+ bell(); /* itoc.010315: ¥s¤@¤U :p */
+ prints("\033[1;5;31mĵ§i\033[m±z¤w¸g¶¢¸m¹L¤[¡A¨t²Î±N¦b %d ¤ÀÄÁ«á§â±z½ð°£¡I", IDLE_WARNOUT);
+ refresh();
+ }
+#endif
+
+#ifndef DETAIL_IDLETIME
+ cutmp->idle_time = idle;
+#endif
+
+ if (bbsmode < M_XMENU) /* ¦b menu ¸Ì­±­n´« movie */
+ {
+ movie();
+ refresh();
+ }
+ }
+ else
+ {
+ if (errno != EINTR)
+ abort_bbs();
+ }
+ }
+ }
+
+ cc = data[vi_head++];
+ if (imode & IM_TRAIL)
+ {
+ imode ^= IM_TRAIL;
+ if (cc == 0 || cc == 0x0a)
+ continue;
+ }
+
+ if (cc == 0x0d)
+ {
+ imode |= IM_TRAIL;
+ return '\n';
+ }
+
+ if (cc == 0x7f)
+ {
+ return KEY_BKSP;
+ }
+
+ if (cc == Ctrl('L'))
+ {
+ vs_redraw();
+ continue;
+ }
+
+ if ((cc == Ctrl('R')) && (bbstate & STAT_STARTED) && !(bbstate & STAT_LOCK) && !(imode & IM_REPLY))
+ /* lkchu.990513: Âê©w®É¤£¥i¦^°T */
+ {
+ signal(SIGUSR1, SIG_IGN);
+
+ /* Thor.980307: ¦b ^R ®É talk ·|¦]¨S¦³ vio_fd ¬Ý¤£¨ì I_OTHERDATA¡A¦Ó¬Ý¤£¨ì¹ï¤è¥´ªº¦r¡A©Ò¥H¦b ^R ®É¸T¤î talk */
+ imode |= IM_REPLY;
+ bmw_reply();
+ imode ^= IM_REPLY;
+
+ signal(SIGUSR1, (void *) talk_rqst);
+
+#ifdef BMW_COUNT
+ /* itoc.010907: «ö¥ô¤@Áä«á±µ¦¬¤ô²y¼Æ¦^Âk 0 */
+ cutmp->bmw_count = 0;
+#endif
+ continue;
+ }
+
+ return (cc);
+ }
+}
+
+
+#define MATCH_END 0x8000
+/* Thor.990204.µù¸Ñ: ¥NªíMATCH§¹µ², ­n¹À´N¸É¨¬, ­n¹À´Nºû«ù­ìª¬, ¤£¨q¥X¥i¯àªº­È¤F */
+
+static void
+match_title()
+{
+ move(2, 0);
+ clrtobot();
+ vs_line("¬ÛÃö¸ê°T¤@Äýªí");
+}
+
+
+static int
+match_getch()
+{
+ int ch;
+
+ outs("\n¡¹ ¦Cªí(C)Ä~Äò (Q)µ²§ô¡H[C] ");
+ ch = vkey();
+ if (ch == 'q' || ch == 'Q')
+ return ch;
+
+ move(3, 0);
+ clrtobot();
+ return 0;
+}
+
+
+/* ----------------------------------------------------- */
+/* ¿ï¾Ü board */
+/* ----------------------------------------------------- */
+
+
+static BRD *xbrd;
+
+
+BRD *
+ask_board(board, perm, msg)
+ char *board;
+ int perm;
+ char *msg;
+{
+ if (msg)
+ {
+ move(2, 0);
+ outs(msg);
+ }
+
+ if (vget(1, 0, "½Ð¿é¤J¬ÝªO¦WºÙ(«öªÅ¥ÕÁä¦Û°Ê·j´M)¡G", board, BNLEN + 1, GET_BRD | perm))
+ return xbrd;
+
+ return NULL;
+}
+
+
+static int
+vget_match(prefix, len, op)
+ char *prefix;
+ int len;
+ int op;
+{
+ char *data, *hit;
+ char newprefix[BNLEN + 1]; /* Ä~Äò¸É§¹ªºªO¦W */
+ int row, col, match;
+ int rlen; /* ¥i¸É§¹ªº³Ñ¾lªø«× */
+
+ row = 3;
+ col = match = rlen = 0;
+
+ if (op & GET_BRD)
+ {
+ usint perm;
+ int i;
+ char *bits, *n, *b;
+ BRD *head, *tail;
+
+ extern BCACHE *bshm;
+ extern char brd_bits[];
+
+ perm = op & (BRD_L_BIT | BRD_R_BIT | BRD_W_BIT);
+ bits = brd_bits;
+ head = bshm->bcache;
+ tail = head + bshm->number;
+
+ do
+ {
+ if (perm & *bits++)
+ {
+ data = head->brdname;
+
+ if (str_ncmp(prefix, data, len))
+ continue;
+
+ xbrd = head;
+
+ if ((op & MATCH_END) && !data[len])
+ {
+ strcpy(prefix, data);
+ return len;
+ }
+
+ match++;
+ hit = data;
+
+ if (op & MATCH_END)
+ continue;
+
+ if (match == 1)
+ {
+ match_title();
+ if (data[len])
+ {
+ strcpy(newprefix, data);
+ rlen = strlen(data + len);
+ }
+ }
+ else if (rlen) /* LHD.051014: ÁÙ¦³¥i¸É§¹ªº¾l¦a */
+ {
+ n = newprefix + len;
+ b = data + len;
+ for (i = 0; i < rlen && ((*n | 0x20) == (*b | 0x20)); i++, n++, b++)
+ ;
+ *n = '\0';
+ rlen = i;
+ }
+
+ move(row, col);
+ outs(data);
+
+ col += BNLEN + 1;
+ if (col > b_cols - BNLEN - 1) /* Á`¦@¥i¥H©ñ b_cols / (BNLEN + 1) Äæ */
+ {
+ col = 0;
+ if (++row >= b_lines)
+ {
+ if (match_getch() == 'q')
+ break;
+
+ move(row = 3, 0);
+ clrtobot();
+ }
+ }
+ }
+ } while (++head < tail);
+ }
+ else if (op & GET_USER)
+ {
+ struct dirent *de;
+ DIR *dirp;
+ int cc;
+ char fpath[16];
+
+ /* Thor.981203: USER name¦Ü¤Ö¥´¤@¦r, ¥Î"<="·|¤ñ¸û¦n¶Ü? */
+ if (len == 0)
+ return 0;
+
+ cc = *prefix;
+ if (cc >= 'A' && cc <= 'Z')
+ cc |= 0x20;
+ if (cc < 'a' || cc > 'z')
+ return 0;
+
+ sprintf(fpath, "usr/%c", cc);
+ dirp = opendir(fpath);
+ while (de = readdir(dirp))
+ {
+ data = de->d_name;
+ if (str_ncmp(prefix, data, len))
+ continue;
+
+ if (!match++)
+ {
+ match_title();
+ strcpy(hit = fpath, data); /* ²Ä¤@µ§²Å¦Xªº¸ê®Æ */
+ }
+
+ move(row, col);
+ outs(data);
+
+ col += IDLEN + 1;
+ if (col > b_cols - IDLEN - 1) /* Á`¦@¥i¥H©ñ b_cols / (IDLEN + 1) Äæ */
+ {
+ col = 0;
+ if (++row >= b_lines)
+ {
+ if (match_getch())
+ break;
+ row = 3;
+ }
+ }
+ }
+
+ closedir(dirp);
+ }
+ else /* Thor.990203.µù¸Ñ: GET_LIST */
+ {
+ LinkList *list;
+ extern LinkList *ll_head;
+
+ for (list = ll_head; list; list = list->next)
+ {
+ data = list->data;
+
+ if (str_ncmp(prefix, data, len))
+ continue;
+
+ if ((op & MATCH_END) && !data[len])
+ {
+ strcpy(prefix, data);
+ return len;
+ }
+
+ match++;
+ hit = data;
+
+ if (op & MATCH_END)
+ continue;
+
+ if (match == 1)
+ match_title();
+
+ move(row, col);
+ outs(data);
+
+ col += IDLEN + 1;
+ if (col > b_cols - IDLEN - 1) /* Á`¦@¥i¥H©ñ b_cols / (IDLEN + 1) Äæ */
+ {
+ col = 0;
+ if (++row >= b_lines)
+ {
+ if (match_getch())
+ break;
+ row = 3;
+ }
+ }
+ }
+ }
+
+ if (match == 1)
+ {
+ strcpy(prefix, hit);
+ return strlen(hit);
+ }
+ else if (rlen)
+ {
+ strcpy(prefix, newprefix);
+ return len + rlen;
+ }
+
+ return 0;
+}
+
+
+char lastcmd[MAXLASTCMD][80];
+
+
+/* bbs.h: Flags to getdata input function */
+/* NOECHO 0x0000 ¤£Åã¥Ü¡A¥Î©ó±K½X¨ú±o */
+/* DOECHO 0x0100 ¤@¯ëÅã¥Ü */
+/* LCECHO 0x0200 low case echo¡A´«¦¨¤p¼g */
+/* GCARRY 0x0400 ·|Åã¥Ü¤W¤@¦¸/¥Ø«eªº­È */
+
+int
+vget(line, col, prompt, data, max, echo)
+ int line, col;
+ uschar *prompt, *data;
+ int max, echo;
+{
+ int ch, len;
+ int x, y;
+ int i, next;
+ int vlen, hlen;
+
+ /* itoc.010312: ¥ý¬ö¿ý¦ì¸m ¦]¬°«á­± line ©M prompt ³£³Q§ó§ï¤F */
+ vlen = line;
+ hlen = col + strlen(prompt);
+
+ if (prompt)
+ {
+ move(line, col);
+ clrtoeol();
+ outs(prompt);
+ }
+ else
+ {
+ clrtoeol();
+ }
+
+ STANDOUT;
+
+ x = cur_row;
+ y = cur_col;
+
+ if (echo & GCARRY)
+ {
+ if (len = strlen(data))
+ outs(data);
+ }
+ else
+ {
+ len = 0;
+ }
+
+ /* --------------------------------------------------- */
+ /* ¨ú±o board / userid / on-line user */
+ /* --------------------------------------------------- */
+
+ ch = len;
+ do
+ {
+ outc(' ');
+ } while (++ch < max);
+
+ STANDEND;
+
+ line = -1;
+ col = len;
+ max--;
+
+ for (;;)
+ {
+ move(x, y + col);
+ ch = vkey();
+ if (ch == '\n')
+ {
+ data[len] = '\0';
+ if ((echo & (GET_BRD | GET_LIST)) && len > 0)
+ /* Thor.990204:­n¨D¿é¤J¥ô¤@¦r¤~¥Nªí¦Û°Ê match, §_«hºâcancel */
+ {
+ ch = len;
+ len = vget_match(data, len, echo | MATCH_END);
+ if (len > ch)
+ {
+ move(x, y);
+ outs(data);
+ }
+ else if (len == 0)
+ {
+ data[0] = '\0';
+ }
+ }
+ break;
+ }
+ if (ch == Ctrl('U'))
+ ch = KEY_ESC;
+
+ if (isprint2(ch))
+ {
+ if (ch == ' ' && (echo & (GET_USER | GET_BRD | GET_LIST)))
+ {
+ ch = vget_match(data, len, echo);
+ if (ch > len)
+ {
+ move(x, y);
+ outs(data);
+ col = len = ch;
+ }
+ continue;
+ }
+
+ if (len >= max)
+ continue;
+
+ /* ----------------------------------------------- */
+ /* insert data and display it */
+ /* ----------------------------------------------- */
+
+ prompt = &data[col];
+ i = col;
+ move(x, y + col);
+
+ for (;;)
+ {
+// outc(echo ? ch : '*');
+ outc((echo && ch != KEY_ESC) ? ch : '*');
+ next = *prompt;
+ *prompt++ = ch;
+ if (i >= len)
+ break;
+ i++;
+ ch = next;
+ }
+ col++;
+ len++;
+ continue;
+ }
+
+ /* ----------------------------------------------- */
+ /* ¿é¤J password ®É¥u¯à«ö BackSpace */
+ /* ----------------------------------------------- */
+
+ if (!echo && ch != KEY_BKSP)
+ continue;
+
+ switch (ch)
+ {
+ case Ctrl('D'):
+
+ if (col >= len)
+ continue;
+
+ col++;
+
+ case KEY_BKSP:
+
+ if (!col)
+ continue;
+
+ /* ----------------------------------------------- */
+ /* remove data and display it */
+ /* ----------------------------------------------- */
+
+ len--;
+ col--;
+#ifdef HAVE_MULTI_BYTE
+ /* hightman.060504: §PÂ_²{¦b§R°£ªº¦ì¸m¬O§_¬°º~¦rªº«á¥b¬q¡A­Y¬O§R¤G¦r¤¸ */
+ if ((cuser.ufo & UFO_ZHC) && echo && col && IS_ZHC_LO(data, col))
+ {
+ len--;
+ col--;
+ next = 2;
+ }
+ else
+#endif
+ next = 1;
+ move(x, y + col);
+ for (i = col; i < len; i++)
+ {
+ data[i] = ch = data[i + next];
+ outc(echo ? ch : '*');
+ }
+ while (next--)
+ outc(' ');
+ break;
+
+ case KEY_DEL:
+
+ if (col >= len)
+ continue;
+
+ /* ----------------------------------------------- */
+ /* remove data and display it */
+ /* ----------------------------------------------- */
+
+ len--;
+#ifdef HAVE_MULTI_BYTE
+ /* hightman.060504: §PÂ_²{¦b§R°£ªº¦ì¸m¬O§_¬°º~¦rªº«e¥b¬q¡A­Y¬O§R¤G¦r¤¸ */
+ if ((cuser.ufo & UFO_ZHC) && col < len && IS_ZHC_HI(data[col]))
+ {
+ len--;
+ next = 2;
+ }
+ else
+#endif
+ next = 1;
+ for (i = col; i < len; i++)
+ {
+ data[i] = ch = data[i + next];
+ outc(ch);
+ }
+ while (next--)
+ outc(' ');
+ break;
+
+ case KEY_LEFT:
+ case Ctrl('B'):
+ if (col)
+ {
+ col--;
+#ifdef HAVE_MULTI_BYTE
+ /* hightman.060504: ¥ª²¾®É¸I¨ìº~¦r²¾Âù®æ */
+ if ((cuser.ufo & UFO_ZHC) && col && IS_ZHC_LO(data, col))
+ col--;
+#endif
+ }
+ break;
+
+ case KEY_RIGHT:
+ case Ctrl('F'):
+ if (col < len)
+ {
+ col++;
+#ifdef HAVE_MULTI_BYTE
+ /* hightman.060504: ¥k²¾®É¸I¨ìº~¦r²¾Âù®æ */
+ if ((cuser.ufo & UFO_ZHC) && col < len && IS_ZHC_HI(data[col - 1]))
+ col++;
+#endif
+ }
+ break;
+
+ case KEY_HOME:
+ case Ctrl('A'):
+ col = 0;
+ break;
+
+ case KEY_END:
+ case Ctrl('E'):
+ col = len;
+ break;
+
+ case Ctrl('C'): /* clear / reset */
+ if (len)
+ {
+ move(x, y);
+ for (ch = 0; ch < len; ch++)
+ outc(' ');
+ col = len = 0;
+ }
+ break;
+
+ case KEY_DOWN:
+ case Ctrl('N'):
+
+ line += MAXLASTCMD - 2;
+
+ case KEY_UP:
+ case Ctrl('P'):
+
+ line = (line + 1) % MAXLASTCMD;
+ prompt = lastcmd[line];
+ col = 0;
+ move(x, y);
+
+ do
+ {
+ if (!(ch = *prompt++))
+ {
+ /* clrtoeol */
+
+ for (ch = col; ch < len; ch++)
+ outc(' ');
+ break;
+ }
+
+#if 0 /* ¤£»Ý­n¡A¦]¬° bmtad/receive_article ·| strip ¨ì ansi code */
+ if (ch == KEY_ESC) /* itoc.020601: ¤£±o¨Ï¥Î±±¨î½X */
+ ch = '*';
+#endif
+
+ outc(ch);
+ data[col] = ch;
+ } while (++col < max);
+
+ len = col;
+ break;
+
+ case Ctrl('K'): /* delete to end of line */
+ if (col < len)
+ {
+ move(x, y + col);
+ for (ch = col; ch < len; ch++)
+ outc(' ');
+ len = col;
+ }
+ break;
+ case Ctrl('G'):
+ /* lantw44: ¥þ«¬¦r°»´ú¤Á´« (Ctrl Áä ³£³Q¥Î¥ú¤F©Ò¥H¥Î³o­Ó) */
+ cuser.ufo ^= UFO_ZHC;
+ if(cuser.ufo & UFO_ZHC){
+ vmsg("¥þ«¬¦r°»´ú¶}±Ò");
+ }else{
+ vmsg("¥þ«¬¦r°»´úÃö³¬");
+ }
+ break;
+
+ /* itoc.030619: Åý vget ¤¤ÁÙ¯à«ö ^R ¿ï¤ô²y */
+ case Ctrl('R'):
+ case Ctrl('T'):
+ if (bbsmode == M_BMW_REPLY)
+ {
+ if (bmw_reply_CtrlRT(ch)) /* ¦³¦hÁû¤ô²y */
+ {
+ data[len] = '\0';
+ return ch;
+ }
+ }
+ break;
+ }
+ }
+
+ if (len >= 2 && echo)
+ {
+ for (line = MAXLASTCMD - 1; line; line--)
+ strcpy(lastcmd[line], lastcmd[line - 1]);
+ strcpy(lastcmd[0], data);
+ }
+
+ move(vlen, strlen(data) + hlen); /* itoc.010312: ©T©w²¾¨ì¸Ó¦C¦C§À¦A¦L¥X'\n' */
+ outc('\n');
+
+ ch = data[0];
+ if ((echo & LCECHO) && (ch >= 'A' && ch <= 'Z'))
+ data[0] = (ch |= 0x20);
+
+ return ch;
+}
+
+
+int
+vans(prompt)
+ char *prompt;
+{
+ char ans[3];
+
+ /* itoc.010812.µù¸Ñ: ·|¦Û°Ê´«¦¨¤p¼gªº */
+ return vget(b_lines, 0, prompt, ans, sizeof(ans), LCECHO);
+}
+
+
+int
+vkey()
+{
+ int mode;
+ int ch, last;
+
+ mode = last = 0;
+ for (;;)
+ {
+ ch = igetch();
+ if (mode == 0) /* Normal Key */
+ {
+ if (ch == KEY_ESC)
+ mode = 1;
+ else
+ return ch;
+ }
+ else if (mode == 1) /* Escape sequence */
+ {
+ if (ch == '[' || ch == 'O')
+ mode = 2;
+ else if (ch == '1' || ch == '4')
+ mode = 3;
+ else
+ return Esc(ch);
+ }
+ 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/maple/window.c b/maple/window.c
new file mode 100644
index 0000000..d5f9dee
--- /dev/null
+++ b/maple/window.c
@@ -0,0 +1,439 @@
+/*-------------------------------------------------------*/
+/* window.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : popup window menu */
+/* create : 03/02/12 */
+/* update : 03/07/23 */
+/* author : verit.bbs@bbs.yzu.edu.tw */
+/* modify : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+#ifdef HAVE_POPUPMENU
+
+static screenline slt[T_LINES];
+static int x_roll;
+
+
+/* ----------------------------------------------------- */
+/* µe­±Ã¸»s */
+/* ----------------------------------------------------- */
+
+
+static void
+draw_line(x, y, msg) /* ¦b (x, y) ªº¦ì¸m¶ë¤J msg¡A¥ª¥k¤´­n¦L¥X­ì¨Óªº±m¦â¤å¦r */
+ int x, y;
+ uschar *msg;
+{
+ uschar *str, *ptr;
+ uschar data[ANSILINELEN];
+ char color[4];
+ int ch, i;
+ int len;
+ int ansi; /* 1: ¦b ANSI ¤¤ */
+ int in_chi = 0; /* 1: ¦b¤¤¤å¦r¤¤ */
+ int fg, bg, hl; /* «e´º/­I´º/°ª±m */
+
+ hl = 0;
+ fg = 37;
+ bg = 40;
+
+ i = x + x_roll;
+ if (i > b_lines)
+ i -= b_lines + 1;
+
+ memset(data, 0, sizeof(data));
+ strncpy(data, slt[i].data, slt[i].len);
+ str = data;
+
+ move(x, 0);
+ clrtoeol();
+
+ /* ¦L¥X (x, 0) ¦Ü (x, y - 1) */
+ ansi = 0;
+ len = 0; /* ¤w¦L¥X´X­Ó¦r (¤£§t±±¨î½X) */
+ while (ch = *str++)
+ {
+ if (ch == KEY_ESC)
+ {
+ ansi = 1;
+ i = 0;
+ }
+ else if (ansi)
+ {
+ if (ch == '[')
+ {
+ }
+ else if (ch >= '0' && ch <= '9')
+ {
+ color[i] = ch;
+ if (++i >= 4)
+ i = 0;
+ }
+ else
+ {
+ color[i] = 0;
+
+ i = atoi(color);
+ if (i == 0)
+ {
+ hl = 0;
+ fg = 37;
+ bg = 40;
+ }
+ else if (i == 1)
+ hl = 1;
+ else if (i >= 30 && i <= 37)
+ fg = i;
+ else if (i >= 40 && i <= 47)
+ bg = i;
+
+ i = 0;
+
+ if (ch != ';')
+ ansi = 0;
+ }
+ }
+ else
+ {
+ if (++len >= y)
+ {
+ /* ³Ì«á¤@¦r­Y¬O¤¤¤å¦rªº­º½X¡A´N¤£¦L */
+ if (!in_chi && IS_ZHC_HI(ch))
+ {
+ outc(' ');
+ in_chi = 1;
+ }
+ else
+ {
+ outc(ch);
+ in_chi = 0;
+ }
+ outs(str_ransi);
+ break;
+ }
+
+ if (in_chi || IS_ZHC_HI(ch))
+ in_chi ^= 1;
+ }
+
+ outc(ch);
+ }
+ while (len++ < y)
+ outc(' ');
+
+ /* ¦L¥X (x, y) ¦Ü (x, y + strip_ansi_len(msg) - 1) */
+ ptr = msg;
+ ansi = 0;
+ len = 0; /* msg ªºªø«×(¤£§t±±¨î½X) */
+ while (ch = *ptr++)
+ {
+ if (ch == KEY_ESC)
+ {
+ ansi = 1;
+ }
+ else if (ansi)
+ {
+ if ((ch < '0' || ch > '9') && ch != ';' && ch != '[')
+ ansi = 0;
+ }
+ else
+ {
+ len++;
+ }
+ outc(ch);
+ }
+
+ /* ¸õ±¼ str ¤¤¶¡¤@¾ã¬q¡A¨Ã¨ú¥X³Ì«áªºÃC¦â */
+ ansi = 0;
+ while (ch = *str++)
+ {
+ if (ch == KEY_ESC)
+ {
+ ansi = 1;
+ i = 0;
+ }
+ else if (ansi)
+ {
+ if (ch == '[')
+ continue;
+ if (ch >= '0' && ch <= '9')
+ {
+ color[i] = ch;
+ if (++i >= 4)
+ i = 0;
+ }
+ else
+ {
+ color[i] = 0;
+
+ i = atoi(color);
+ if (i == 0)
+ {
+ hl = 0;
+ fg = 37;
+ bg = 40;
+ }
+ else if (i == 1)
+ hl = 1;
+ else if (i >= 30 && i <= 37)
+ fg = i;
+ else if (i >= 40 && i <= 47)
+ bg = i;
+
+ i = 0;
+
+ if (ch != ';')
+ ansi = 0;
+ }
+ }
+ else
+ {
+ if (--len < 0) /* ¸õ¹L strip_ansi_len(msg) ªºªø«× */
+ break;
+
+ if (in_chi || IS_ZHC_HI(ch))
+ in_chi ^= 1;
+ }
+ }
+
+ /* ¦L¥X (x, y + strip_ansi_len(msg)) ³o­Ó¦r¤Î«á­±ªº±±¨î½X */
+ prints("\033[%d;%d;%dm", hl, fg, bg);
+ /* ¦¹¦r­Y¬O¤¤¤å¦rªº§À½X¡A´N¤£¦L */
+ outc(in_chi ? ' ' : ch);
+
+ /* ¦L¥X (x, y + strip_ansi_len(msg) + 1) ¦Ü ¦æ§À */
+ outs(str);
+ outs(str_ransi);
+}
+
+
+#ifdef POPUP_ANSWER
+
+/* ----------------------------------------------------- */
+/* ¿ï¶µÃ¸»s */
+/* ----------------------------------------------------- */
+
+
+static int
+draw_item(x, y, desc, hotkey, mode)
+ int x, y;
+ char *desc;
+ char hotkey;
+ int mode; /* 0:²M°£¥ú´Î 1:µe¤W¥ú´Î */
+{
+ char buf[128];
+
+ sprintf(buf, " ¢x%s%c %c%c%c%-25s \033[m¢x ",
+ mode ? COLOR4 : "\033[30;47m", mode ? '>' : ' ',
+ (hotkey == *desc) ? '[' : '(', *desc,
+ (hotkey == *desc) ? ']' : ')', desc + 1);
+
+ draw_line(x, y, buf);
+}
+
+
+static int /* ¦^¶ÇÁ`¦@¦³´X­Ó¿ï¶µ */
+draw_menu(x, y, title, desc, hotkey, cur)
+ int x, y;
+ char *title;
+ char *desc[];
+ char hotkey;
+ int *cur; /* ¦^¶Ç¹w³]­È©Ò¦b¦ì¸m */
+{
+ int i, meet;
+ char buf[128];
+
+ draw_line(x++, y, " ¢~¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢¡ ");
+
+ sprintf(buf, " ¢x" COLOR4 " %-28s \033[m¢x ", title);
+ draw_line(x++, y, buf);
+
+ draw_line(x++, y, " ¢u¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢t ");
+
+ for (i = 1; desc[i]; i++)
+ {
+ meet = (desc[i][0] == hotkey);
+ draw_item(x++, y, desc[i], hotkey, meet);
+ if (meet)
+ *cur = i;
+ }
+
+ draw_line(x, y, " ¢¢¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢£ ");
+
+ /* Á×§K¦b°»´ú¥ª¥kÁä¥þ§Î¤U¡A«ö¥ªÁä·|¸õÂ÷¤G¼h¿ï³æªº°ÝÃD */
+ move(b_lines, 0);
+
+ return i - 1;
+}
+
+
+/* ----------------------------------------------------- */
+/* §ä¿ï¶µ */
+/* ----------------------------------------------------- */
+
+
+static int /* -1:§ä¤£¨ì >=0:²Ä´X­Ó¿ï¶µ */
+find_cur(ch, max, desc) /* §ä ch ³o­Ó«öÁä¬O²Ä´X­Ó¿ï¶µ */
+ int ch, max;
+ char *desc[];
+{
+ int i, cc;
+
+ if (ch >= 'A' && ch <= 'Z')
+ ch |= 0x20; /* ´«¤p¼g */
+
+ for (i = 1; i <= max; i++)
+ {
+ cc = desc[i][0];
+ if (cc >= 'A' && cc <= 'Z')
+ cc |= 0x20; /* ´«¤p¼g */
+
+ if (ch == cc)
+ return i;
+ }
+
+ return -1;
+}
+
+
+/*------------------------------------------------------ */
+/* ¸ß°Ý¿ï¶µ¡A¥i¥Î¨Ó¨ú¥N vans() */
+/*------------------------------------------------------ */
+/* x, y ¬OÂÛ¥Xµøµ¡¥ª¤W¨¤ªº (x, y) ¦ì¸m */
+/* title ¬Oµøµ¡ªº¼ÐÃD */
+/* desc ¬O¿ï¶µªº±Ô­z¡G */
+/* ²Ä¤@­Ó¦r¦ê¥²¶·¬°¨â­Ó char */
+/* ²Ä¤@­Ó¦r¤¸¥Nªí¤@¶}©l´å¼Ð°±ªº¦ì¸m */
+/* ²Ä¤G­Ó¦r¤¸¥Nªí«ö¤U KEY_LEFT ªº¹w³]¦^¶Ç­È */
+/* ¤¤¶¡ªº¦r¦ê¬O¨C­Ó¿ï¶µªº±Ô­z (­º¦r¥À¬°¼öÁä) */
+/* ³Ì«á¤@­Ó¦r¦ê¥²¶·¬° NULL */
+/*------------------------------------------------------ */
+
+
+int /* ¶Ç¦^¤p¼g¦r¥À©Î¼Æ¦r */
+pans(x, y, title, desc)
+ int x, y;
+ char *title;
+ char *desc[];
+{
+ int cur, old_cur, max, ch;
+ char hotkey;
+
+ x_roll = vs_save(slt);
+
+ hotkey = desc[0][0];
+
+ /* µe¥X¾ã­Ó¿ï³æ */
+ max = draw_menu(x, y, title, desc, hotkey, &cur);
+ x += 2;
+
+ /* ¤@¶i¤J¡A´å¼Ð°±¦b¹w³]­È */
+ old_cur = cur;
+
+ while (1)
+ {
+ switch (ch = vkey())
+ {
+ case KEY_LEFT:
+ case KEY_RIGHT:
+ case '\n':
+ vs_restore(slt);
+ ch = (ch == KEY_LEFT) ? desc[0][1] : desc[cur][0];
+ if (ch >= 'A' && ch <= 'Z')
+ ch |= 0x20; /* ¦^¶Ç¤p¼g */
+ return ch;
+
+ case KEY_UP:
+ cur = (cur == 1) ? max : cur - 1;
+ break;
+
+ case KEY_DOWN:
+ cur = (cur == max) ? 1 : cur + 1;
+ break;
+
+ case KEY_HOME:
+ cur = 1;
+ break;
+
+ case KEY_END:
+ cur = max;
+ break;
+
+ default: /* ¥h§ä©Ò«öÁä¬O­þ¤@­Ó¿ï¶µ */
+ if ((ch = find_cur(ch, max, desc)) > 0)
+ cur = ch;
+ break;
+ }
+
+ if (old_cur != cur) /* ´å¼ÐÅܰʦì¸m¤~»Ý­n­«Ã¸ */
+ {
+ draw_item(x + old_cur, y, desc[old_cur], hotkey, 0);
+ draw_item(x + cur, y, desc[cur], hotkey, 1);
+ old_cur = cur;
+ /* Á×§K¦b°»´ú¥ª¥kÁä¥þ§Î¤U¡A«ö¥ªÁä·|¸õÂ÷¤G¼h¿ï³æªº°ÝÃD */
+ move(b_lines, 0);
+ }
+ }
+}
+#endif /* POPUP_ANSWER */
+
+
+#ifdef POPUP_MESSAGE
+/*------------------------------------------------------ */
+/* ÂÛ¥X¦¡µøµ¡°T®§¡A¥i¥Î¨Ó¨ú¥N vmsg() */
+/*------------------------------------------------------ */
+
+
+int
+pmsg(msg)
+ char *msg; /* ¤£¥i¬° NULL */
+{
+ int len, x, y, i;
+ char buf[80];
+
+ x_roll = vs_save(slt);
+
+ len = strlen(msg);
+ if (len < 16) /* ¨ú msg title ¨ä¤¤¸ûªøªÌ¬° len */
+ len = 16;
+ if (len % 2) /* Åܦ¨°¸¼Æ */
+ len++;
+ x = (b_lines - 4) >> 1; /* ¸m¤¤ */
+ y = (b_cols - 8 - len) >> 1;
+
+ strcpy(buf, "¢~");
+ for (i = -4; i < len; i += 2)
+ strcat(buf, "¢w");
+ strcat(buf, "¢¡");
+ draw_line(x++, y, buf);
+
+ sprintf(buf, "¢x" COLOR4 " %-*s \033[m¢x", len, "½Ð«ö¥ô·NÁäÄ~Äò..");
+ draw_line(x++, y, buf);
+
+ strcpy(buf, "¢u");
+ for (i = -4; i < len; i += 2)
+ strcat(buf, "¢w");
+ strcat(buf, "¢t");
+ draw_line(x++, y, buf);
+
+ sprintf(buf, "¢x\033[30;47m %-*s \033[m¢x", len, msg);
+ draw_line(x++, y, buf);
+
+ strcpy(buf, "¢¢");
+ for (i = -4; i < len; i += 2)
+ strcat(buf, "¢w");
+ strcat(buf, "¢£");
+ draw_line(x++, y, buf);
+
+ move(b_lines, 0);
+
+ x = vkey();
+ vs_restore(slt);
+ return x;
+}
+#endif /* POPUP_MESSAGE */
+
+#endif /* HAVE_POPUPMENU */
diff --git a/maple/xover.c b/maple/xover.c
new file mode 100644
index 0000000..3329972
--- /dev/null
+++ b/maple/xover.c
@@ -0,0 +1,1788 @@
+/*-------------------------------------------------------*/
+/* xover.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : board/mail interactive reading routines */
+/* create : 95/03/29 */
+/* update : 95/12/15 */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+#ifdef MY_FAVORITE
+#define MSG_ZONE_SWITCH "§Ö³t¤Á´«¡G(A)ºëµØ (B)¤å³¹ (C)¬ÝªO (F)³Ì·R (M)«H¥ó (U)¨Ï¥ÎªÌ (W)¤ô²y (S)Â^«Ì¡G"
+#else
+#define MSG_ZONE_SWITCH "§Ö³t¤Á´«¡G(A)ºëµØ (B)¤å³¹ (C)¬ÝªO (M)«H¥ó (U)¨Ï¥ÎªÌ (W)¤ô²y (S)Â^«Ì¡G"
+#endif
+
+
+/* ----------------------------------------------------- */
+/* keep xover record */
+/* ----------------------------------------------------- */
+
+
+static XO *xo_root; /* root of overview list */
+
+
+XO *
+xo_new(path)
+ char *path;
+{
+ XO *xo;
+ int len;
+
+ len = strlen(path) + 1;
+
+ xo = (XO *) malloc(sizeof(XO) + len);
+
+ memcpy(xo->dir, path, len);
+
+ return xo;
+}
+
+
+XO *
+xo_get(path)
+ char *path;
+{
+ XO *xo;
+
+ for (xo = xo_root; xo; xo = xo->nxt)
+ {
+ if (!strcmp(xo->dir, path))
+ return xo;
+ }
+
+ xo = xo_new(path);
+ xo->nxt = xo_root;
+ xo_root = xo;
+ xo->xyz = NULL;
+ xo->pos = XO_TAIL; /* ²Ä¤@¦¸¶i¤J®É¡A±N´å¼Ð©ñ¦b³Ì«á­± */
+
+ return xo;
+}
+
+
+#ifdef AUTO_JUMPPOST
+XO *
+xo_get_post(path, brd) /* itoc.010910: °Ñ¦Ò xover.c xo_get()¡A¬° XoPost ¶q¨­¥´³y */
+ char *path;
+ BRD *brd;
+{
+ XO *xo;
+ time_t chrono;
+ int fd;
+ int pos, locus, mid; /* locus:¥ª«ü¼Ð mid:¤¤«ü¼Ð pos:¥k«ü¼Ð */
+
+ for (xo = xo_root; xo; xo = xo->nxt)
+ {
+ if (!strcmp(xo->dir, path))
+ return xo;
+ }
+
+ xo = xo_new(path);
+ xo->nxt = xo_root;
+ xo_root = xo;
+ xo->xyz = NULL;
+
+ /* ©|¥¼§ó·s brd->blast ©Î ³Ì«á¤@½g¤wŪ ©Î ¥u¦³¤@½g¡A«h´å¼Ðª½±µ©ñ³Ì«á */
+ if (brd->btime < 0 || !brh_unread(brd->blast) ||
+ (pos = rec_num(path, sizeof(HDR))) <= 1 || (fd = open(path, O_RDONLY)) < 0)
+ {
+ xo->pos = XO_TAIL; /* ´å¼Ð©ñ¦b³Ì«á­± */
+ return xo;
+ }
+
+ /* §ä²Ä¤@½g¥¼Åª binary search */
+ pos--;
+ locus = 0;
+ while (1)
+ {
+ if (pos <= locus + 1)
+ break;
+
+ mid = locus + ((pos - locus) >> 1);
+ lseek(fd, (off_t) (sizeof(HDR) * mid), SEEK_SET);
+ if (read(fd, &chrono, sizeof(time_t)) == sizeof(time_t))
+ {
+ if (brh_unread(chrono))
+ pos = mid;
+ else
+ locus = mid;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ /* ¯S¨Ò: ¦pªG¥k«ü¼Ð°±¯d¦b 1¡A¦³¤GºØ¥i¯à¡A¤@¬O«êŪ¨ì²Ä¤G½g¡A¥t¤@¬°³s²Ä¤@½g³£¨SŪ */
+ if (pos == 1)
+ {
+ /* Àˬd²Ä¤@½g¬O§_¤wŪ */
+ lseek(fd, (off_t) 0, SEEK_SET);
+ if (read(fd, &chrono, sizeof(time_t)) == sizeof(time_t))
+ {
+ if (brh_unread(chrono)) /* ­Y³s²Ä¤@½g¤]¥¼Åª¡Apos ½Õ¦^¥h²Ä¤@½g */
+ pos = 0;
+ }
+ }
+
+ close(fd);
+ xo->pos = pos; /* ²Ä¤@¦¸¶i¤J®É¡A±N´å¼Ð©ñ¦b²Ä¤@½g¥¼Åª */
+
+ return xo;
+}
+#endif
+
+
+#if 0
+void
+xo_free(xo)
+ XO *xo;
+{
+ char *ptr;
+
+ if (ptr = xo->xyz)
+ free(ptr);
+ free(xo);
+}
+#endif
+
+
+/* ----------------------------------------------------- */
+/* interactive menu routines */
+/* ----------------------------------------------------- */
+
+
+char xo_pool[(T_LINES - 4) * XO_RSIZ]; /* XO's data I/O pool */
+
+
+void
+xo_load(xo, recsiz)
+ XO *xo;
+ int recsiz;
+{
+ int fd, max;
+
+ max = 0;
+ if ((fd = open(xo->dir, O_RDONLY)) >= 0)
+ {
+ int pos, top;
+ struct stat st;
+
+ fstat(fd, &st);
+ max = st.st_size / recsiz;
+ if (max > 0)
+ {
+ pos = xo->pos;
+ if (pos <= 0)
+ {
+ pos = top = 0;
+ }
+ else
+ {
+ top = max - 1;
+ if (pos > top)
+ pos = top;
+ top = (pos / XO_TALL) * XO_TALL;
+ }
+ xo->pos = pos;
+ xo->top = top;
+
+ lseek(fd, (off_t) (recsiz * top), SEEK_SET);
+ read(fd, xo_pool, recsiz * XO_TALL);
+ }
+ close(fd);
+ }
+
+ xo->max = max;
+}
+
+
+int /* XO_LOAD:§R°£ XO_FOOT:¨ú®ø */
+xo_rangedel(xo, size, fchk, fdel) /* itoc.031001: °Ï¬q§R°£ */
+ XO *xo;
+ int size;
+ int (*fchk) (); /* Àˬd°Ï¬q¤¤¬O§_¦³³Q«OÅ@ªº°O¿ý 0:§R°£ 1:«OÅ@ */
+ void (*fdel) (); /* °£¤F§R°£°O¿ý¥H¥~¡AÁÙ­n°µ¨Ç¤°»ò¨Æ */
+{
+ char ans[8];
+ int head, tail;
+
+ vget(b_lines, 0, "[³]©w§R°£½d³ò] °_ÂI¡G", ans, 6, DOECHO);
+ head = atoi(ans);
+ if (head <= 0)
+ {
+ zmsg("°_ÂI¦³»~");
+ return XO_FOOT;
+ }
+
+ vget(b_lines, 28, "²×ÂI¡G", ans, 6, DOECHO);
+ tail = atoi(ans);
+ if (tail < head)
+ {
+ zmsg("²×ÂI¦³»~");
+ return XO_FOOT;
+ }
+
+ if (vget(b_lines, 41, msg_sure_ny, ans, 3, LCECHO) == 'y')
+ {
+ int fd, total;
+ char *data, *phead, *ptail;
+ struct stat st;
+
+ if ((fd = open(xo->dir, O_RDONLY)) < 0)
+ return XO_FOOT;
+
+ fstat(fd, &st);
+ total = st.st_size;
+ head = (head - 1) * size;
+ tail = tail * size;
+ if (head > total)
+ {
+ close(fd);
+ return XO_FOOT;
+ }
+ if (tail > total)
+ tail = total;
+
+ data = (char *) malloc(total);
+ read(fd, data, total);
+ close(fd);
+
+ total -= tail;
+ phead = data + head;
+ ptail = data + tail;
+
+ if (fchk || fdel)
+ {
+ char *ptr;
+ for (ptr = phead; ptr < ptail; ptr += size)
+ {
+ if (fchk && fchk(ptr)) /* ³oµ§°O¿ý«OÅ@¤£³Q¬å */
+ {
+ memcpy(phead, ptr, size);
+ phead += size;
+ head += size;
+ }
+ else if (fdel) /* °£¤F§R°£°O¿ý¡AÁÙ­n°µ fdel() */
+ {
+ fdel(xo, ptr);
+ }
+ }
+ }
+
+ memcpy(phead, ptail, total);
+
+ if ((fd = open(xo->dir, O_WRONLY | O_CREAT | O_TRUNC, 0600)) >= 0)
+ {
+ write(fd, data, total + head);
+ close(fd);
+ }
+
+ free(data);
+ return XO_LOAD;
+ }
+ return XO_FOOT;
+}
+
+
+/* ----------------------------------------------------- */
+/* Tag List ¼ÐÅÒ */
+/* ----------------------------------------------------- */
+
+
+int TagNum; /* tag's number */
+TagItem TagList[TAG_MAX]; /* ascending list */
+
+
+int
+Tagger(chrono, recno, op)
+ time_t chrono;
+ int recno;
+ int op; /* op : TAG_NIN / TOGGLE / INSERT */
+/* ----------------------------------------------------- */
+/* return 0 : not found / full */
+/* 1 : add */
+/* -1 : remove */
+/* ----------------------------------------------------- */
+{
+ int head, tail, pos, cmp;
+ TagItem *tagp;
+
+ for (head = 0, tail = TagNum - 1, tagp = TagList, cmp = 1; head <= tail;)
+ {
+ pos = (head + tail) >> 1;
+ cmp = tagp[pos].chrono - chrono;
+ if (!cmp)
+ {
+ break;
+ }
+ else if (cmp < 0)
+ {
+ head = pos + 1;
+ }
+ else
+ {
+ tail = pos - 1;
+ }
+ }
+
+ if (op == TAG_NIN)
+ {
+ if (!cmp && recno) /* µ´¹ïÄYÂÔ¡G³s recno ¤@°_¤ñ¹ï */
+ cmp = recno - tagp[pos].recno;
+ return cmp;
+ }
+
+ tail = TagNum;
+
+ if (!cmp)
+ {
+ if (op != TAG_TOGGLE)
+ return 0;
+
+ TagNum = --tail;
+ memcpy(&tagp[pos], &tagp[pos + 1], (tail - pos) * sizeof(TagItem));
+ return -1;
+ }
+
+ if (tail < TAG_MAX)
+ {
+ TagItem buf[TAG_MAX];
+
+ TagNum = tail + 1;
+ tail = (tail - head) * sizeof(TagItem);
+ tagp += head;
+ memcpy(buf, tagp, tail);
+ tagp->chrono = chrono;
+ tagp->recno = recno;
+ memcpy(++tagp, buf, tail);
+ return 1;
+ }
+
+ /* TagList is full */
+
+ bell();
+ return 0;
+}
+
+
+void
+EnumTag(data, dir, locus, size)
+ void *data;
+ char *dir;
+ int locus;
+ int size;
+{
+ rec_get(dir, data, size, TagList[locus].recno);
+}
+
+
+int
+AskTag(msg)
+ char *msg;
+/* ----------------------------------------------------- */
+/* return value : */
+/* -1 : ¨ú®ø */
+/* 0 : single article */
+/* o.w. : whole tag list */
+/* ----------------------------------------------------- */
+{
+ char buf[80];
+ int num;
+
+ num = TagNum;
+
+ if (num) /* itoc.020130: ¦³ TagNum ¤~°Ý */
+ {
+ sprintf(buf, "¡» %s A)³æ½g¤å³¹ T)¼Ð°O¤å³¹ Q)Â÷¶}¡H[%c] ", msg, num ? 'T' : 'A');
+ switch (vans(buf))
+ {
+ case 'q':
+ return -1;
+
+ case 'a':
+ return 0;
+ }
+ }
+ return num;
+}
+
+
+/* ----------------------------------------------------- */
+/* tag articles according to title / author */
+/* ----------------------------------------------------- */
+
+
+static int
+xo_tag(xo, op)
+ XO *xo;
+ int op;
+{
+ int fsize, count;
+ char *token, *fimage;
+ HDR *head, *tail;
+
+ fimage = f_map(xo->dir, &fsize);
+ if (fimage == (char *) -1)
+ return XO_NONE;
+
+ head = (HDR *) xo_pool + (xo->pos - xo->top);
+ if (op == Ctrl('A'))
+ {
+ token = head->owner;
+ op = 0;
+ }
+ else
+ {
+ token = str_ttl(head->title);
+ op = 1;
+ }
+
+ head = (HDR *) fimage;
+ tail = (HDR *) (fimage + fsize);
+
+ count = 0;
+
+ do
+ {
+ if (!strcmp(token, op ? str_ttl(head->title) : head->owner))
+ {
+ if (!Tagger(head->chrono, count, TAG_INSERT))
+ break;
+ }
+ count++;
+ } while (++head < tail);
+
+ munmap(fimage, fsize);
+ return XO_BODY;
+}
+
+
+int /* XO_LOAD:§R°£ XO_FOOT/XO_NONE:¨ú®ø */
+xo_prune(xo, size, fvfy, fdel) /* itoc.031003: ¼ÐÅÒ§R°£ */
+ XO *xo;
+ int size;
+ int (*fvfy) (); /* Àˬd°Ï¬q¤¤¬O§_¦³³Q«OÅ@ªº°O¿ý 0:§R°£ 1:«OÅ@ */
+ void (*fdel) (); /* °£¤F§R°£°O¿ý¥H¥~¡AÁÙ­n°µ¨Ç¤°»ò¨Æ */
+{
+ int fd, total, pos;
+ char *data, *phead, *ptail, *ptr;
+ char buf[80];
+ struct stat st;
+
+ if (!TagNum)
+ return XO_NONE;
+
+ sprintf(buf, "½T©w­n§R°£ %d ½g¼ÐÅÒ¶Ü(Y/N)¡H[N] ", TagNum);
+ if (vans(buf) != 'y')
+ return XO_FOOT;
+
+ if ((fd = open(xo->dir, O_RDONLY)) < 0)
+ return XO_FOOT;
+
+ fstat(fd, &st);
+ data = (char *) malloc(total = st.st_size);
+ total = read(fd, data, total);
+ close(fd);
+
+ phead = data;
+ ptail = data + total;
+ pos = 0;
+ total = 0;
+
+ for (ptr = phead; ptr < ptail; ptr += size)
+ {
+ if (fvfy(ptr, pos)) /* ³oµ§°O¿ý«OÅ@¤£³Q¬å */
+ {
+ memcpy(phead, ptr, size);
+ phead += size;
+ total += size;
+ }
+ else if (fdel) /* °£¤F§R°£°O¿ý¡AÁÙ­n°µ fdel() */
+ {
+ fdel(xo, ptr);
+ }
+ pos++;
+ }
+
+ if ((fd = open(xo->dir, O_WRONLY | O_CREAT | O_TRUNC, 0600)) >= 0)
+ {
+ write(fd, data, total);
+ close(fd);
+ }
+
+ free(data);
+
+ TagNum = 0;
+
+ return XO_LOAD;
+}
+
+
+/* ----------------------------------------------------- */
+/* Tag's batch operation routines */
+/* ----------------------------------------------------- */
+
+
+extern BCACHE *bshm; /* lkchu.981229 */
+
+
+static int
+xo_tbf(xo)
+ XO *xo;
+{
+ char fpath[128], *dir;
+ HDR *hdr, xhdr;
+ int tag, locus, xmode;
+ FILE *fp;
+
+ if (!cuser.userlevel)
+ return XO_NONE;
+
+ tag = AskTag("«þ¨©¨ì¼È¦sÀÉ");
+ if (tag < 0)
+ return XO_FOOT;
+
+ if (!(fp = tbf_open()))
+ return XO_FOOT;
+
+ hdr = tag ? &xhdr : (HDR *) xo_pool + xo->pos - xo->top;
+
+ locus = 0;
+ dir = xo->dir;
+
+ do
+ {
+ if (tag)
+ {
+ fputs(str_line, fp);
+ EnumTag(hdr, dir, locus, sizeof(HDR));
+ }
+
+ xmode = hdr->xmode;
+
+ /* itoc.000319: ­×¥¿­­¨î¯Å¤å³¹¤£±o¶×¤J¼È¦sÀÉ */
+ /* itoc.010602: GEM_RESTRICT ©M POST_RESTRICT ¤Ç°t¡A©Ò¥H¥[±K¤å³¹¤]¤£±o¶×¤J¼È¦sÀÉ */
+ if (xmode & (GEM_RESTRICT | GEM_RESERVED))
+ continue;
+
+ if (!(xmode & GEM_FOLDER)) /* ¬d hdr ¬O§_ plain text */
+ {
+ hdr_fpath(fpath, dir, hdr);
+ f_suck(fp, fpath);
+ }
+ } while (++locus < tag);
+
+ fclose(fp);
+ zmsg("«þ¨©§¹¦¨");
+
+ return XO_FOOT;
+}
+
+
+static int
+xo_forward(xo)
+ XO *xo;
+{
+ static char rcpt[64];
+ char fpath[64], folder[64], *dir, *title, *userid;
+ HDR *hdr, xhdr;
+ int tag, locus, userno, cc, xmode;
+ int method; /* Âà±H¨ì 0:¯¸¥~ >0:¦Û¤v <0:¨ä¥L¯¸¤º¨Ï¥ÎªÌ */
+
+ if (!cuser.userlevel || HAS_PERM(PERM_DENYMAIL))
+ return XO_NONE;
+
+ tag = AskTag("Âà±H");
+ if (tag < 0)
+ return XO_FOOT;
+
+ if (!rcpt[0])
+ strcpy(rcpt, cuser.email);
+
+ if (!vget(b_lines, 0, "¥Øªº¦a¡G", rcpt, sizeof(rcpt), GCARRY))
+ return XO_FOOT;
+
+ userid = cuser.userid;
+ userno = 0;
+
+ if (!mail_external(rcpt)) /* ¤¤³~ÄdºI */
+ {
+ if (!str_cmp(rcpt, userid))
+ {
+ /* userno = cuser.userno; */ /* Thor.981027: ±Hºë¿ï¶°µ¹¦Û¤v¤£³qª¾¦Û¤v */
+ method = 1;
+ }
+ else
+ {
+ if (!HAS_PERM(PERM_LOCAL))
+ return XO_FOOT;
+
+ if ((userno = acct_userno(rcpt)) <= 0)
+ {
+ zmsg(err_uid);
+ return XO_FOOT;
+ }
+ method = -1;
+ }
+
+ usr_fpath(folder, rcpt, fn_dir);
+ }
+ else
+ {
+ if (!HAS_PERM(PERM_INTERNET))
+ {
+ vmsg("±zµLªk±H«H¨ì¯¸¥~");
+ return XO_FOOT;
+ }
+
+ if (not_addr(rcpt))
+ {
+ zmsg(err_email);
+ return XO_FOOT;
+ }
+
+ method = 0;
+ }
+
+ hdr = tag ? &xhdr : (HDR *) xo_pool + xo->pos - xo->top;
+
+ dir = xo->dir;
+ title = hdr->title;
+ locus = 0;
+ cc = -1;
+
+ do
+ {
+ if (tag)
+ EnumTag(hdr, dir, locus, sizeof(HDR));
+
+ xmode = hdr->xmode;
+
+ /* itoc.000319: ­×¥¿­­¨î¯Å¤å³¹¤£±oÂà±H */
+ /* itoc.010602: GEM_RESTRICT ©M POST_RESTRICT ¤Ç°t¡A©Ò¥H¥[±K¤å³¹¤]¤£±oÂà±H */
+ if (xmode & (GEM_RESTRICT | GEM_RESERVED))
+ continue;
+
+ if (!(xmode & GEM_FOLDER)) /* ¬d hdr ¬O§_ plain text */
+ {
+ hdr_fpath(fpath, dir, hdr);
+
+ if (method) /* Âà±H¯¸¤º */
+ {
+ HDR mhdr;
+
+ if ((cc = hdr_stamp(folder, HDR_COPY, &mhdr, fpath)) < 0)
+ break;
+
+ if (method > 0) /* Âà±H¦Û¤v */
+ {
+ strcpy(mhdr.owner, "[ºë ¿ï ¶°]");
+ mhdr.xmode = MAIL_READ | MAIL_NOREPLY;
+ }
+ else /* Âà±H¨ä¥L¨Ï¥ÎªÌ */
+ {
+ strcpy(mhdr.owner, userid);
+ }
+ strcpy(mhdr.nick, cuser.username);
+ strcpy(mhdr.title, title);
+ if ((cc = rec_add(folder, &mhdr, sizeof(HDR))) < 0)
+ break;
+ }
+ else /* Âà±H¯¸¥~ */
+ {
+ if ((cc = bsmtp(fpath, title, rcpt, 0)) < 0)
+ break;
+ }
+ }
+ } while (++locus < tag);
+
+ if (userno > 0 && cc >= 0)
+ m_biff(userno);
+
+ zmsg(cc >= 0 ? msg_sent_ok : "³¡¥÷«H¥óµLªk±H¹F");
+
+ return XO_FOOT;
+}
+
+
+/* ----------------------------------------------------- */
+/* ¤å³¹§@ªÌ¬d¸ß¡BÅv­­³]©w */
+/* ----------------------------------------------------- */
+
+
+int
+xo_uquery(xo)
+ XO *xo;
+{
+ HDR *hdr;
+ char *userid;
+
+ hdr = (HDR *) xo_pool + (xo->pos - xo->top);
+ userid = hdr->owner;
+ if (strchr(userid, '.'))
+ return XO_NONE;
+
+ move(1, 0);
+ clrtobot();
+ my_query(userid);
+ return XO_HEAD;
+}
+
+
+int
+xo_usetup(xo)
+ XO *xo;
+{
+ HDR *hdr;
+ char *userid;
+ ACCT acct;
+
+ if (!HAS_PERM(PERM_ALLACCT))
+ return XO_NONE;
+
+ hdr = (HDR *) xo_pool + (xo->pos - xo->top);
+ userid = hdr->owner;
+ if (strchr(userid, '.') || (acct_load(&acct, userid) < 0))
+ return XO_NONE;
+
+ move(3, 0);
+ acct_setup(&acct, 1);
+ return XO_HEAD;
+}
+
+
+/* ----------------------------------------------------- */
+/* ¥DÃD¦¡¾\Ū */
+/* ----------------------------------------------------- */
+
+
+#define RS_TITLE 0x001 /* author/title */
+#define RS_FORWARD 0x002 /* backward */
+#define RS_RELATED 0x004
+#define RS_FIRST 0x008 /* find first article */
+#define RS_CURRENT 0x010 /* match current read article */
+#define RS_THREAD 0x020 /* search the first article */
+#define RS_SEQUENT 0x040 /* sequential read */
+#define RS_MARKED 0x080 /* marked article */
+#define RS_UNREAD 0x100 /* unread article */
+#define RS_BOARD 0x1000 /* ¥Î©ó RS_UNREAD¡A¸ò«e­±ªº¤£¥i­«Å| */
+
+#define CURSOR_FIRST (RS_RELATED | RS_TITLE | RS_FIRST)
+#define CURSOR_NEXT (RS_RELATED | RS_TITLE | RS_FORWARD)
+#define CURSOR_PREV (RS_RELATED | RS_TITLE)
+#define RELATE_FIRST (RS_RELATED | RS_TITLE | RS_FIRST | RS_CURRENT)
+#define RELATE_NEXT (RS_RELATED | RS_TITLE | RS_FORWARD | RS_CURRENT)
+#define RELATE_PREV (RS_RELATED | RS_TITLE | RS_CURRENT)
+#define THREAD_NEXT (RS_THREAD | RS_FORWARD)
+#define THREAD_PREV (RS_THREAD)
+
+/* Thor: «e«á§ämark¤å³¹, ¤è«Kª¾¹D¦³¤°»ò°ÝÃD¥¼³B²z */
+
+#define MARK_NEXT (RS_MARKED | RS_FORWARD | RS_CURRENT)
+#define MARK_PREV (RS_MARKED | RS_CURRENT)
+
+
+typedef struct
+{
+ int key; /* key stroke */
+ int map; /* the mapped threading op-code */
+} KeyMap;
+
+
+static KeyMap keymap[] =
+{
+ /* search title / author */
+
+ '?', RS_TITLE | RS_FORWARD,
+ '|', RS_TITLE,
+ 'A', RS_FORWARD,
+ 'Q', 0,
+
+ /* thread : currtitle */
+
+ '[', RS_RELATED | RS_TITLE | RS_CURRENT,
+ ']', RS_RELATED | RS_TITLE | RS_FORWARD | RS_CURRENT,
+ '=', RS_RELATED | RS_TITLE | RS_FIRST | RS_CURRENT,
+
+ /* i.e. < > : make life easier */
+
+ ',', RS_THREAD,
+ '.', RS_THREAD | RS_FORWARD,
+
+ /* thread : cursor */
+
+ '-', RS_RELATED | RS_TITLE,
+ '+', RS_RELATED | RS_TITLE | RS_FORWARD,
+ '\\', RS_RELATED | RS_TITLE | RS_FIRST,
+
+ /* Thor: marked : cursor */
+ '\'', RS_MARKED | RS_FORWARD | RS_CURRENT,
+ ';', RS_MARKED | RS_CURRENT,
+
+ /* Thor: ¦V«e§ä²Ä¤@½g¥¼Åªªº¤å³¹ */
+ /* Thor.980909: ¦V«e§ä­º½g¥¼Åª, ©Î¥½½g¤wŪ */
+ '`', RS_UNREAD /* | RS_FIRST */,
+
+ /* sequential */
+
+ ' ', RS_SEQUENT | RS_FORWARD,
+ KEY_RIGHT, RS_SEQUENT | RS_FORWARD,
+ KEY_PGDN, RS_SEQUENT | RS_FORWARD,
+ KEY_DOWN, RS_SEQUENT | RS_FORWARD,
+ /* Thor.990208: ¬°¤F¤è«K¬Ý¤å³¹¹Lµ{¤¤, ²¾¦Ü¤U½g, ÁöµM¤W¼h³Qxover¦Y±¼¤F:p */
+ 'j', RS_SEQUENT | RS_FORWARD,
+
+ KEY_UP, RS_SEQUENT,
+ KEY_PGUP, RS_SEQUENT,
+ /* Thor.990208: ¬°¤F¤è«K¬Ý¤å³¹¹Lµ{¤¤, ²¾¦Ü¤W½g, ÁöµM¤W¼h³Qxover¦Y±¼¤F:p */
+ 'k', RS_SEQUENT,
+
+ /* end of keymap */
+
+ (char) NULL, -1
+};
+
+
+static int
+xo_keymap(key)
+ int key;
+{
+ KeyMap *km;
+ int ch;
+
+ km = keymap;
+ while (ch = km->key)
+ {
+ if (ch == key)
+ break;
+ km++;
+ }
+ return km->map;
+}
+
+
+/* itoc.010913: xo_thread() ¦^¶Ç­È */
+/* XO_NONE: ¨S§ä¨ì©Î´N¬O´å¼Ð©Ò¦b¡A¤£¥Î²M b_lines */
+/* XO_FOOT: ¨S§ä¨ì©Î´N¬O´å¼Ð©Ò¦b¡A»Ý­n²M b_lines */
+/* XO_BODY: §ä¨ì¤F¡A¦ý¦b§O­¶ */
+/* -XO_NONE: §ä¨ì¤F¡A´N¦b¥»­¶¡A¤£¥Î²M b_lines */
+/* -XO_FOOT: §ä¨ì¤F¡A´N¦b¥»­¶¡A»Ý­n²M b_lines */
+
+
+static int
+xo_thread(xo, op)
+ XO *xo;
+ int op;
+{
+ static char s_author[16], s_title[32], s_unread[2] = "0";
+ char buf[80];
+
+ char *tag, *query, *title;
+ const int origpos = xo->pos, origtop = xo->top, max = xo->max;
+ int pos, match, near, neartop; /* Thor: neartop »P near ¦¨¹ï¥Î */
+ int top, bottom, step, len;
+ HDR *pool, *hdr;
+
+ match = XO_NONE;
+ pos = origpos;
+ top = origtop;
+ pool = (HDR *) xo_pool;
+ hdr = pool + (pos - top);
+ near = 0;
+ step = (op & RS_FORWARD) - 1; /* (op & RS_FORWARD) ? 1 : -1 */
+
+ if (op & RS_RELATED)
+ {
+ tag = hdr->title;
+ if (op & RS_CURRENT)
+ {
+ query = currtitle;
+ if (op & RS_FIRST)
+ {
+ if (!strcmp(query, tag)) /* ¥Ø«eªº´N¬O²Ä¤@µ§¤F */
+ return XO_NONE;
+ near = -1;
+ }
+ }
+ else
+ {
+ title = str_ttl(tag);
+ if (op & RS_FIRST)
+ {
+ if (title == tag)
+ return XO_NONE;
+ near = -1;
+ }
+ strcpy(query = buf, title);
+ }
+ }
+ else if (op & RS_UNREAD)
+ {
+ /* Thor.980909: ¸ß°Ý "­º½g¥¼Åª" ©Î "¥½½g¤wŪ" */
+
+ near = xo->dir[0];
+ if (near != 'b' && near != 'u') /* itoc.010913: ¥u¤¹³\¦b¬ÝªO/«H½c·j´M */
+ return XO_NONE; /* itoc.040916.bug: ¨S¦³­­¨î¦b«H½cºëµØ°Ï·j´M */
+
+ if (!vget(b_lines, 0, "¦V«e§ä´M 0)­º½g¥¼Åª 1)¥½½g¤wŪ ", s_unread, sizeof(s_unread), GCARRY))
+ return XO_FOOT;
+
+ if (*s_unread == '0')
+ op |= RS_FIRST;
+
+ if (near == 'b') /* search board */
+ op |= RS_BOARD;
+
+ near = -1;
+ }
+ else if (!(op & (RS_THREAD | RS_SEQUENT | RS_MARKED)))
+ {
+ if (op & RS_TITLE)
+ {
+ title = "¼ÐÃD";
+ tag = s_title;
+ len = sizeof(s_title);
+ }
+ else
+ {
+ title = "§@ªÌ";
+ tag = s_author;
+ len = sizeof(s_author);
+ }
+
+ sprintf(query = buf, "·j´M%s(%s)¡G", title, (step > 0) ? "¡õ" : "¡ô");
+ if (!vget(b_lines, 0, query, tag, len, GCARRY))
+ return XO_FOOT;
+
+ str_lowest(query, tag);
+ }
+
+ bottom = top + XO_TALL;
+ if (bottom > max)
+ bottom = max;
+
+ for (;;)
+ {
+ if (step > 0)
+ {
+ if (++pos >= max)
+ break;
+ }
+ else
+ {
+ if (--pos < 0)
+ break;
+ }
+
+ /* buffer I/O : shift sliding window scope */
+
+ if (pos < top || pos >= bottom)
+ {
+ xo->pos = pos;
+ xo_load(xo, sizeof(HDR));
+
+ top = xo->top;
+ bottom = top + XO_TALL;
+ if (bottom > max)
+ bottom = max;
+
+ hdr = pool + (pos - top);
+ }
+ else
+ {
+ hdr += step;
+ }
+
+#ifdef HAVE_REFUSEMARK
+ if ((hdr->xmode & POST_RESTRICT) &&
+ strcmp(hdr->owner, cuser.userid) && !(bbstate & STAT_BM))
+ continue;
+#endif
+
+ if (op & RS_SEQUENT)
+ {
+ match = -1;
+ break;
+ }
+
+ /* Thor: «e«á search marked ¤å³¹ */
+
+ if (op & RS_MARKED)
+ {
+ if (hdr->xmode & POST_MARKED)
+ {
+ match = -1;
+ break;
+ }
+ continue;
+ }
+
+ /* ¦V«e§ä´M§ä´M¥¼Åª/¤wۤ峹 */
+
+ if (op & RS_UNREAD)
+ {
+#define UNREAD_FUNC() (op & RS_BOARD ? brh_unread(hdr->chrono) : !(hdr->xmode & MAIL_READ))
+ if (op & RS_FIRST) /* ­º½g¥¼Åª */
+ {
+ if (UNREAD_FUNC())
+ {
+ near = pos;
+ neartop = top;
+ continue;
+ }
+ }
+ else /* ¥½½g¤wŪ */
+ {
+ if (!UNREAD_FUNC())
+ {
+ match = -1;
+ break;
+ }
+ }
+ continue;
+ }
+
+ /* ------------------------------------------------- */
+ /* ¥H¤U·j´M title / author */
+ /* ------------------------------------------------- */
+
+ if (op & (RS_TITLE | RS_THREAD))
+ {
+ title = hdr->title; /* title «ü¦V [title] field */
+ tag = str_ttl(title); /* tag «ü¦V thread's subject */
+
+ if (op & RS_THREAD)
+ {
+ if (tag == title)
+ {
+ match = -1;
+ break;
+ }
+ continue;
+ }
+ }
+ else
+ {
+ tag = hdr->owner; /* tag «ü¦V [owner] field */
+ }
+
+ if (((op & RS_RELATED) && !strncmp(tag, query, 40)) ||
+ (!(op & RS_RELATED) && str_sub(tag, query)))
+ {
+ if ((op & RS_FIRST) && tag != title)
+ {
+ near = pos; /* °O¤U³Ì±µªñ°_ÂIªº¦ì¸m */
+ neartop = top;
+ continue;
+ }
+
+ match = -1;
+ break;
+ }
+ }
+
+ /* top = xo->top = buffering ªº top */
+ /* ¦pªG match = -1 ªí¥Ü§ä¨ì¤F¡A¦Ó pos, top = ­n¥hªº¦a¤è */
+ /* ¦pªG RS_FIRST && near >= 0 ªí¥Ü§ä¨ì¤F¡A¦Ó near, neartop = ­n¥hªº¦a¤è */
+
+#define CLEAR_FOOT() (!(op & RS_RELATED) && ((op & RS_UNREAD) || !(op & (RS_THREAD | RS_SEQUENT | RS_MARKED))))
+
+ if (match < 0) /* §ä¨ì¤F */
+ {
+ xo->pos = pos; /* §â­n¥hªº¦ì¸m¶ñ¶i¥h */
+
+ if (top != origtop) /* ¦b§O­¶§ä¨ì¤F */
+ match = XO_BODY;
+ else /* ¦b¥»­¶§ä¨ì¤F */
+ match = CLEAR_FOOT() ? -XO_FOOT : -XO_NONE;
+ }
+ else if ((op & RS_FIRST) && near >= 0)/* §ä¨ì¤F */
+ {
+ xo->pos = near; /* §â­n¥hªº¦ì¸m¶ñ¶i¥h */
+
+ /* ¥Ñ©ó¬O RS_FIRST §ä²Ä¤@½g¡A©Ò¥H buffering ªº top ¥i¯à§ä¨ì¤ñ³Ì«áµ²ªG neartop §ó«e­± */
+ if (top != neartop)
+ xo_load(xo, sizeof(HDR));
+
+ if (xo->top != origtop) /* ¦b§O­¶§ä¨ì¤F */
+ match = XO_BODY;
+ else /* ¦b¥»­¶§ä¨ì¤F */
+ match = CLEAR_FOOT() ? -XO_FOOT : -XO_NONE;
+ }
+ else /* §ä¤£¨ì */
+ {
+ xo->pos = origpos; /* ÁÙ­ì­ì¨Ó¦ì¸m */
+
+ if (top != origtop) /* ¦^¥Ø«e©Ò¦b­¶ */
+ xo_load(xo, sizeof(HDR));
+
+ match = CLEAR_FOOT() ? XO_FOOT : XO_NONE;
+ }
+
+ return match;
+}
+
+
+/* Thor.990204: ¬°¦Ò¼{more ¶Ç¦^­È, ¥H«K¬Ý¤@¥b¥i¥H¥Î []...
+ ch ¬°¥ý«emore()¤¤©Ò«öªºkey */
+int
+xo_getch(xo, ch)
+ XO *xo;
+ int ch;
+{
+ int op;
+
+ if (!ch)
+ ch = vkey();
+
+ op = xo_keymap(ch);
+ if (op >= 0)
+ {
+ ch = xo_thread(xo, op);
+ if (ch != XO_NONE)
+ ch = XO_BODY; /* Ä~ÄòÂsÄý */
+ }
+
+ return ch;
+}
+
+
+/* ----------------------------------------------------- */
+/* XZ */
+/* ----------------------------------------------------- */
+
+
+extern KeyFunc pal_cb[];
+extern KeyFunc bmw_cb[];
+extern KeyFunc post_cb[];
+
+
+XZ xz[] =
+{
+ {NULL, NULL, M_BOARD, FEETER_CLASS}, /* XZ_CLASS */
+ {NULL, NULL, M_LUSERS, FEETER_ULIST}, /* XZ_ULIST */
+ {NULL, pal_cb, M_PAL, FEETER_PAL}, /* XZ_PAL */
+ {NULL, NULL, M_PAL, FEETER_ALOHA}, /* XZ_ALOHA */
+ {NULL, NULL, M_VOTE, FEETER_VOTE}, /* XZ_VOTE */
+ {NULL, bmw_cb, M_BMW, FEETER_BMW}, /* XZ_BMW */
+ {NULL, NULL, M_MF, FEETER_MF}, /* XZ_MF */
+ {NULL, NULL, M_COSIGN, FEETER_COSIGN}, /* XZ_COSIGN */
+ {NULL, NULL, M_SONG, FEETER_SONG}, /* XZ_SONG */
+ {NULL, NULL, M_READA, FEETER_NEWS}, /* XZ_NEWS */
+ {NULL, NULL, M_READA, FEETER_XPOST}, /* XZ_XPOST */
+ {NULL, NULL, M_RMAIL, FEETER_MBOX}, /* XZ_MBOX */
+ {NULL, post_cb, M_READA, FEETER_POST}, /* XZ_POST */
+ {NULL, NULL, M_GEM, FEETER_GEM} /* XZ_GEM */
+};
+
+
+static int
+xo_jump(pos, zone)
+ int pos; /* ²¾°Ê´å¼Ð¨ì number ©Ò¦bªº¯S©w¦ì¸m */
+ int zone; /* itoc.010403: §â zone ¤]¶Ç¶i¨Ó */
+{
+ char buf[6];
+
+ buf[0] = pos;
+ buf[1] = '\0';
+ vget(b_lines, 0, "¸õ¦Ü²Ä´X¶µ¡G", buf, sizeof(buf), GCARRY);
+
+#if 0
+ move(b_lines, 0);
+ clrtoeol();
+#endif
+ outf(xz[zone].feeter); /* itoc.010403: §â b_lines ¶ñ¤W feeter */
+
+ pos = atoi(buf);
+
+ if (pos > 0)
+ return XO_MOVE + pos - 1;
+
+ return XO_NONE;
+}
+
+
+/* ----------------------------------------------------- */
+/* interactive menu routines */
+/* ----------------------------------------------------- */
+
+
+void
+xover(cmd)
+ int cmd;
+{
+ int pos, num, zone, sysmode;
+ XO *xo;
+ KeyFunc *xcmd, *cb;
+
+ for (;;)
+ {
+ while (cmd != XO_NONE)
+ {
+ if (cmd == XO_FOOT)
+ {
+ outf(xz[zone].feeter); /* itoc.010403: §â b_lines ¶ñ¤W feeter */
+ break;
+ }
+
+ if (cmd >= XO_ZONE)
+ {
+ /* --------------------------------------------- */
+ /* switch zone */
+ /* --------------------------------------------- */
+
+ zone = cmd;
+ cmd -= XO_ZONE;
+ xo = xz[cmd].xo;
+ xcmd = xz[cmd].cb;
+ sysmode = xz[cmd].mode;
+
+ TagNum = 0; /* clear TagList */
+ cmd = XO_INIT;
+ utmp_mode(sysmode);
+ }
+ else if (cmd >= XO_MOVE - XO_TALL)
+ {
+ /* --------------------------------------------- */
+ /* calc cursor pos and show cursor correctly */
+ /* --------------------------------------------- */
+
+ /* cmd >= XO_MOVE - XO_TALL so ... :chuan: °²³] cmd = -1 ?? */
+
+ /* fix cursor's range */
+
+ num = xo->max - 1;
+
+ /* pos = (cmd | XO_WRAP) - (XO_MOVE + XO_WRAP); */
+ /* cmd &= XO_WRAP; */
+ /* itoc.020124: ­×¥¿¦b²Ä¤@­¶«ö PGUP¡B³Ì«á¤@­¶«ö PGDN¡B²Ä¤@¶µ«ö UP¡B³Ì«á¤@¶µ«ö DOWN ·|§â XO_WRAP ºX¼Ð®ø¥¢ */
+
+ if (cmd > XO_MOVE + (XO_WRAP >> 1)) /* XO_WRAP >> 1 »·¤j¹L¤å³¹¼Æ */
+ {
+ pos = cmd - (XO_MOVE + XO_WRAP);
+ cmd = 1; /* «ö KEY_UP ©Î KEY_DOWN */
+ }
+ else
+ {
+ pos = cmd - XO_MOVE;
+ cmd = 0;
+ }
+
+ /* pos: ­n¸õ¥hªº¶µ¥Ø cmd: ¬O§_¬° KEY_UP ©Î KEY_DOWN */
+
+ if (pos < 0)
+ {
+ /* pos = (zone == XZ_POST) ? 0 : num; *//* itoc.000304: ¾\Ū¨ì²Ä¤@½g«ö KEY_UP ©Î KEY_PGUP ¤£·|½¨ì³Ì«á */
+ pos = num ; /* itoc.020124: ½¨ì³Ì«á¤ñ¸û¤è«K¡AÀ³¸Ó¤£·|¦³¤H¬O©¹¤WŪªº :P */
+ }
+ else if (pos > num)
+ {
+ if (zone == XZ_POST)
+ pos = num; /* itoc.000304: ¾\Ū¨ì³Ì«á¤@½g«ö KEY_DOWN ©Î KEY_PGDN ¤£·|½¨ì³Ì«e */
+ else
+ pos = (cmd || pos == num + XO_TALL) ? 0 : num; /* itoc.020124: ­nÁ×§K¦pªG¦b­Ë¼Æ²Ä¤G­¶«ö KEY_PGDN¡A
+ ¦Ó³Ì«á¤@­¶½g¼Æ¤Ó¤Ö·|ª½±µ¸õ¥h²Ä¤@­¶¡A¨Ï¥ÎªÌ·|
+ ¤£ª¾¹D¦³³Ì«á¤@­¶¡A¬G¥ý¦b³Ì«á¤@¶µ°±¤@¤U */
+ }
+
+ /* check cursor's range */
+
+ cmd = xo->pos;
+
+ if (cmd == pos)
+ break;
+
+ xo->pos = pos;
+ num = xo->top;
+ if ((pos < num) || (pos >= num + XO_TALL))
+ {
+ xo->top = (pos / XO_TALL) * XO_TALL;
+ cmd = XO_LOAD; /* ¸ü¤J¸ê®Æ¨Ã¤©¥HÅã¥Ü */
+ }
+ else
+ {
+ move(3 + cmd - num, 0);
+ outc(' ');
+
+ break; /* ¥u²¾°Ê´å¼Ð */
+ }
+ }
+
+ /* ----------------------------------------------- */
+ /* °õ¦æ call-back routines */
+ /* ----------------------------------------------- */
+
+ cb = xcmd;
+ num = cmd | XO_DL; /* Thor.990220: for dynamic load */
+ for (;;)
+ {
+ pos = cb->key;
+#if 1
+ /* Thor.990220: dynamic load , with key | XO_DL */
+ if (pos == num)
+ {
+ void *p = DL_get((char *) cb->func);
+ if (p)
+ {
+ cb->func = p;
+ pos = cb->key = cmd;
+ }
+ else
+ {
+ cmd = XO_NONE;
+ break;
+ }
+ }
+#endif
+ if (pos == cmd)
+ {
+ cmd = (*(cb->func)) (xo);
+
+ if (cmd == XO_QUIT)
+ return;
+
+ break;
+ }
+
+ if (pos == 'h') /* itoc.001029: 'h' ¬O¤@¯S¨Ò¡A¥Nªí *_cb ªºµ²§ô */
+ {
+ cmd = XO_NONE; /* itoc.001029: ¥Nªí§ä¤£¨ì call-back, ¤£§@¤F! */
+ break;
+ }
+
+ cb++;
+ }
+
+ } /* Thor.990220.µù¸Ñ: end of while (cmd!=XO_NONE) */
+
+ utmp_mode(sysmode);
+ /* Thor.990220:µù¸Ñ:¥Î¨Ó¦^´_ event handle routine ¦^¨Ó«áªº¼Ò¦¡ */
+
+ pos = xo->pos;
+
+ if (xo->max > 0) /* Thor: ­Y¬OµLªF¦è´N¤£show¤F */
+ {
+ num = 3 + pos - xo->top;
+ move(num, 0);
+ outc('>');
+ }
+
+ cmd = vkey();
+
+ /* itoc.µù¸Ñ: ¥H¤U©w¸q¤F°ò¥»«öÁä¡A©Ò¿×°ò¥»«öÁä¡A´N¬O²¾°Êªº¤§Ãþªº¡A³q¥Î©ó©Ò¦³ XZ_ ªº¦a¤è */
+
+ /* ------------------------------------------------- */
+ /* °ò¥»ªº´å¼Ð²¾°Ê routines */
+ /* ------------------------------------------------- */
+
+ if (cmd == KEY_LEFT || cmd == 'e')
+ {
+ TagNum = 0; /* itoc.050413: ±qºëµØ°Ï¦^¨ì¤å³¹¦Cªí®É­n²M°£ tag */
+ return;
+ }
+ else if (xo->max <= 0) /* Thor: µLªF¦è«hµLªk²¾´å¼Ð */
+ {
+ continue;
+ }
+ else if (cmd == KEY_UP || cmd == 'k')
+ {
+ cmd = pos - 1 + XO_MOVE + XO_WRAP;
+ }
+ else if (cmd == KEY_DOWN || cmd == 'j')
+ {
+ cmd = pos + 1 + XO_MOVE + XO_WRAP;
+ }
+ else if (cmd == ' ' || cmd == KEY_PGDN || cmd == 'N')
+ {
+ cmd = pos + XO_TALL + XO_MOVE;
+ }
+ else if (cmd == KEY_PGUP || cmd == 'P')
+ {
+ cmd = pos - XO_TALL + XO_MOVE;
+ }
+ else if (cmd == KEY_HOME || cmd == '0')
+ {
+ cmd = XO_MOVE;
+ }
+ else if (cmd == KEY_END || cmd == '$')
+ {
+ cmd = xo->max - 1 + XO_MOVE ;
+ }
+ else if (cmd >= '1' && cmd <= '9')
+ {
+ cmd = xo_jump(cmd, zone);
+ }
+ else if (cmd == KEY_RIGHT || cmd == '\n')
+ {
+ cmd = 'r';
+ }
+
+ /* ------------------------------------------------- */
+ /* switch Zone */
+ /* ------------------------------------------------- */
+
+#ifdef EVERY_Z
+ else if (cmd == Ctrl('Z'))
+ {
+ cmd = every_Z(zone);
+ }
+ else if (cmd == Ctrl('U'))
+ {
+ cmd = every_U(zone);
+ }
+#endif
+
+ /* ------------------------------------------------- */
+ /* ¨ä¾lªº«öÁä */
+ /* ------------------------------------------------- */
+
+ else
+ {
+ if (zone >= XZ_XPOST) /* xo_pool ¤¤©ñªº¬O HDR */
+ {
+ /* --------------------------------------------- */
+ /* Tag */
+ /* --------------------------------------------- */
+
+ if (cmd == 'C')
+ {
+ cmd = xo_tbf(xo);
+ }
+ else if (cmd == 'F')
+ {
+ cmd = xo_forward(xo);
+ }
+ else if (cmd == Ctrl('C'))
+ {
+ if (TagNum)
+ {
+ TagNum = 0;
+ cmd = XO_BODY;
+ }
+ else
+ cmd = XO_NONE;
+ }
+ else if (cmd == Ctrl('A') || cmd == Ctrl('T'))
+ {
+ cmd = xo_tag(xo, cmd);
+ }
+
+ /* --------------------------------------------- */
+ /* ¥DÃD¦¡¾\Ū */
+ /* --------------------------------------------- */
+
+ if (zone == XZ_XPOST) /* ¦ê±µ¤¤¤£¤ä´©¥DÃD¦¡¾\Ū */
+ continue;
+
+ pos = xo_keymap(cmd);
+ if (pos >= 0) /* ¦pªG¤£¬O«ö¤è¦VÁä */
+ {
+ cmd = xo_thread(xo, pos); /* ¥h¬d¬d¬O­þ¤@ºØ thread ·j´M */
+
+ if (cmd < 0) /* ¦b¥»­¶§ä¨ì match */
+ {
+ move(num, 0);
+ outc(' ');
+ /* cmd = XO_NONE; */
+ /* itoc.010913: ¬Y¨Ç·j´M­n§â b_lines ¶ñ¤W feeter */
+ cmd = -cmd;
+ }
+ }
+ }
+
+ /* ----------------------------------------------- */
+ /* ¨ä¥Lªº¥æµ¹ call-back routine ¥h³B²z */
+ /* ----------------------------------------------- */
+
+ } /* Thor.990220.µù¸Ñ: end of vkey() handling */
+ }
+}
+
+
+/* ----------------------------------------------------- */
+/* Thor.980725: ctrl Z everywhere */
+/* ----------------------------------------------------- */
+
+
+#ifdef EVERY_Z
+int z_status = 0; /* ¶i¤J´X¼h */
+
+int
+every_Z(zone)
+ int zone; /* ¶Ç¤J©Ò¦b XZ_ZONE¡A­Y¶Ç¤J 0¡Aªí¥Ü¤£¦b xover() ¤¤ */
+{
+ int cmd, tmpbno, tmpmode;
+
+ /* itoc.000319: ³Ì¦h every_Z ¤@¼h */
+ if (z_status >= 1)
+ return XO_NONE;
+ else
+ z_status++;
+
+ cmd = zone;
+ outz(MSG_ZONE_SWITCH);
+
+ tmpbno = vkey(); /* ­É¥Î tmpbno ¨Ó´«¦¨¤p¼g */
+ if (tmpbno >= 'A' && tmpbno <= 'Z')
+ tmpbno |= 0x20;
+ switch (tmpbno)
+ {
+ case 'a':
+ cmd = XZ_GEM;
+ break;
+
+ case 'b':
+ if (currbno >= 0) /* ­Y¤w¿ï©w¬ÝªO¡A¶i¤J¬ÝªO¡A§_«h¨ì¬ÝªO¦Cªí */
+ {
+ cmd = XZ_POST;
+ break;
+ }
+
+ case 'c':
+ cmd = XZ_CLASS;
+ break;
+
+#ifdef MY_FAVORITE
+ case 'f':
+ if (cuser.userlevel)
+ cmd = XZ_MF;
+ break;
+#endif
+
+ case 'm':
+ if (cuser.userlevel)
+ cmd = XZ_MBOX;
+ break;
+
+ case 'u':
+ cmd = XZ_ULIST;
+ break;
+
+ case 'w':
+ if (cuser.userlevel)
+ cmd = XZ_BMW;
+ break;
+
+ case 's':
+ {
+ FILE *fp;
+ int i;
+ screenline slt[24];
+
+ vs_save(slt);
+
+ if (!(fp = tbf_open()))
+ {
+ vmsg("¼È¦sÀɶ}±Ò¿ù»~");
+ break;
+ }
+
+ for (i = 0; i <= b_lines; i++)
+ {
+ slt[i].data[slt[i].len] = '\0';
+ fprintf(fp, "%s\n", slt[i].data);
+ }
+
+ fclose(fp);
+ }
+ break;
+ }
+
+ if (cmd == zone) /* ©M¥Ø«e©Ò¦b zone ¤@¼Ë¡A©Î¨ú®ø */
+ {
+ z_status--;
+ return XO_FOOT; /* ­Y¦b xover() ¤¤¨ú®ø©I¥s every_Z() «h°e¦^ XO_FOOT §Y¥i­«Ã¸ */
+ }
+
+ if (cmd == XZ_POST)
+ XoPost(currbno);
+
+#ifdef MY_FAVORITE
+ if (zone == XZ_POST && (cmd == XZ_CLASS || cmd == XZ_MF))
+#else
+ if (zone == XZ_POST && cmd == XZ_CLASS)
+#endif
+ tmpbno = currbno;
+ else
+ tmpbno = -1;
+
+ tmpmode = bbsmode;
+ xover(cmd);
+ utmp_mode(tmpmode);
+
+ if (tmpbno >= 0) /* itoc.030731: ¦³¥i¯à¶i¤J§OªºªO¡A´N»Ý­n­«·s XoPost¡A·|¦A¬Ý¤@¦¸¶iªOµe­± */
+ XoPost(tmpbno);
+
+ z_status--;
+ return XO_INIT; /* »Ý­n­«·s¸ü¤J xo_pool¡A­Y¦b xover() ¤¤¤]¥iÂǦ¹­«Ã¸ */
+}
+
+
+int
+every_U(zone)
+ int zone; /* ¶Ç¤J©Ò¦b XZ_ZONE¡A­Y¶Ç¤J 0¡Aªí¥Ü¤£¦b xover() ¤¤ */
+{
+ /* itoc.000319: ³Ì¦h every_Z ¤@¼h */
+ if (z_status >= 1)
+ return XO_NONE;
+
+ if (zone != XZ_ULIST)
+ {
+ int tmpmode;
+
+ z_status++;
+ tmpmode = bbsmode;
+ xover(XZ_ULIST);
+ utmp_mode(tmpmode);
+ z_status--;
+ }
+ return XO_INIT;
+}
+#endif
+
+
+/* ----------------------------------------------------- */
+/* Ãþ XZ_* µ²ºcªº´å¼Ð²¾°Ê */
+/* ----------------------------------------------------- */
+
+
+/* ¶Ç¤J: ch, pagemax, num, pageno, cur, redraw */
+/* ¶Ç¥X: ch, pageno, cur, redraw */
+int
+xo_cursor(ch, pagemax, num, pageno, cur, redraw)
+ int ch, pagemax, num;
+ int *pageno, *cur, *redraw;
+{
+ switch (ch)
+ {
+ case KEY_LEFT:
+ case 'q':
+ return 'q';
+
+ case KEY_PGUP:
+ if (pagemax != 0)
+ {
+ if (*pageno)
+ {
+ (*pageno)--;
+ }
+ else
+ {
+ *pageno = pagemax;
+ *cur = num % XO_TALL;
+ }
+ *redraw = 1;
+ }
+ break;
+
+ case KEY_PGDN:
+ if (pagemax != 0)
+ {
+ if (*pageno == pagemax)
+ {
+ /* ¦b³Ì«á¤@¶µ°±¤@¤U */
+ if (*cur != num % XO_TALL)
+ {
+ *cur = num % XO_TALL;
+ }
+ else
+ {
+ *pageno = 0;
+ *cur = 0;
+ }
+ }
+ else
+ {
+ (*pageno)++;
+ if (*pageno == pagemax && *cur > num % XO_TALL)
+ *cur = num % XO_TALL;
+ }
+ *redraw = 1;
+ }
+ break;
+
+ case KEY_UP:
+ case 'k':
+ if (*cur == 0)
+ {
+ if (*pageno != 0)
+ {
+ *cur = XO_TALL - 1;
+ *pageno = *pageno - 1;
+ }
+ else
+ {
+ *cur = num % XO_TALL;
+ *pageno = pagemax;
+ }
+ *redraw = 1;
+ }
+ else
+ {
+ move(3 + *cur, 0);
+ outc(' ');
+ (*cur)--;
+ move(3 + *cur, 0);
+ outc('>');
+ }
+ break;
+
+ case KEY_DOWN:
+ case 'j':
+ if (*cur == XO_TALL - 1)
+ {
+ *cur = 0;
+ *pageno = (*pageno == pagemax) ? 0 : *pageno + 1;
+ *redraw = 1;
+ }
+ else if (*pageno == pagemax && *cur == num % XO_TALL)
+ {
+ *cur = 0;
+ *pageno = 0;
+ *redraw = 1;
+ }
+ else
+ {
+ move(3 + *cur, 0);
+ outc(' ');
+ (*cur)++;
+ move(3 + *cur, 0);
+ outc('>');
+ }
+ break;
+
+ case KEY_HOME:
+ case '0':
+ *pageno = 0;
+ *cur = 0;
+ *redraw = 1;
+ break;
+
+ case KEY_END:
+ case '$':
+ *pageno = pagemax;
+ *cur = num % XO_TALL;
+ *redraw = 1;
+ break;
+ }
+
+ return ch;
+}
+
+
+/* ----------------------------------------------------- */
+/* »¡©ú¤å¥ó */
+/* ----------------------------------------------------- */
+
+
+void
+xo_help(path) /* itoc.021122: »¡©ú¤å¥ó */
+ char *path;
+{
+ /* itoc.030510: ©ñ¨ì so ¸Ì­± */
+ DL_func("bin/help.so:vaHelp", path);
+}
diff --git a/maple/xpost.c b/maple/xpost.c
new file mode 100644
index 0000000..426be6f
--- /dev/null
+++ b/maple/xpost.c
@@ -0,0 +1,1203 @@
+/*-------------------------------------------------------*/
+/* xpost.c ( NTHU CS MapleBBS Ver 2.39 ) */
+/*-------------------------------------------------------*/
+/* target : bulletin boards' routines */
+/* create : 95/03/29 */
+/* update : 96/04/05 */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+
+extern XZ xz[];
+
+extern char xo_pool[];
+
+
+/*------------------------------------------------------------------------
+ Thor.980509:
+ ·sªº ¤å³¹·j´M¼Ò¦¡ ¥i«ü©w¤@ keyword, ¦C¥X©Ò¦³keyword¬ÛÃö¤§¤å³¹¦Cªí
+
+ ¦b tmp/ ¤U¶} xpost.{pid} §@¬° folder, ¥t«Ø¤@map°}¦C, ¥Î§@»P­ì post §@ map
+ °O¸ü¸Ó¤å³¹¬O¦b­ì post ªº¦ó³B, ¦p¦¹¥i§@ mark, gem, edit, title µ¥¥\¯à,
+ ¥B¯àÂ÷¶}®É¦^¦Ü¹ïÀ³¤å³¹³B
+ <¥H¤W·Qªk obsolete...>
+
+ Thor.980510:
+ «Ø¥ß¤å³¹°Q½×¦ê, like tin, ±N¤å³¹¦ê index ©ñ¤J memory ¤¤,
+ ¤£¨Ï¥Î thread, ¦]¬° thread­n¥Î folder ÀÉ...
+
+ ¤À¬°¨âºØ Mode, Title & post list
+
+ ¦ý¦Ò¼{´£¨Ñ²¤Æªº ¤W¤UÁä²¾°Ê..
+
+ O->O->O->...
+ | | |
+ o o o
+ | | |
+
+ index§tfield {next, text} §¡¬°int, °t¸m¤]¥Î int
+ ²Ä¤@¼h sorted by title, ´¡¤J®É¥Î binary search
+ ¥B MMAP only , ²Ä¤@¼hÅã¥Ü # and +
+
+ ¤£´£¨Ñ¥ô¦ó°Ï¬q§R°£°Ê§@, Á×§K²V¶Ã
+-------------------------------------------------------------------------*/
+
+#if 0 /* itoc.060206.µù¸Ñ */
+
+ ·í¨Ï¥ÎªÌ¿é¤J·j´M±ø¥ó«á¡A·|¶i¤J XoXpost() ±N xo->dir ³oÀɮפ¤©Ò°O¿ýªº©Ò¦³ HDR
+ ¤@¤@ÂsÄý¡AµM«á±Nº¡¨¬±ø¥óªº HDR ¦b xo->dir ¤¤©Ò¹ïÀ³¦ì¸m°O¿ý¦b xpostIndex[]¡A
+ ±µµÛ¶i¤J xover(XZ_XPOST) ·|©I¥s xpost_init()¡A¦A©ó xpost_pick() ±N xpostIndex[]
+ ©Ò°O¿ýªº¦ì¸m±q xo->dir §Û¨ì xo_pool[]¡C
+
+ ·í¼W¥[±ø¥ó°µ¤G¦¸·j´M®É¡A¦¹®É¤£»Ý­n±½¾ã­Ó xo->dir ¤ºªº©Ò¦³ HDR¡A¥uÂsÄý°O¿ý¦b
+ xpostIndex[] ¸Ì­±ªº¨º¨Ç¡C¥Ñ©ó¤G¦¸·j´M®É­n¬Ý¦P¤@­Ó xo->dir¡A©Ò¥H every_Z ®É
+ ­n¸T¤î¶i¤J XZ_XPOST ¤G¦¸¡C
+
+ ¤wª¾°ÝÃD¬O¡G·í¨Ï¥ÎªÌÁÙ¦b XZ_XPOST ¸Ì­±®É¡A­Y xo->dir ªº¶¶§Ç¦³²§°Ê®É (¨Ò¦p§R°£)¡A
+ ¦Ó¨Ï¥ÎªÌ­n¨D xpick_pick() ®É (¨Ò¦p½­¶¡B¤G¦¸·j´M)¡A¥Ñ©ó xpostIndex[] °O¿ýªº¬O¦b
+ xo->dir ªº¦ì¸m¡A¦¹®Éµ²ªG·|¥X¿ù¡C
+
+#endif
+
+/* ----------------------------------------------------- */
+/* ¦ê¦C·j´M¥Dµ{¦¡ */
+/* ----------------------------------------------------- */
+
+
+#ifdef EVERY_Z
+#define MSG_XYDENY "½Ð¥ý°h¥X¨Ï¥Î ^Z ¥H«eªº¦ê±µ/·s»D¥\\¯à"
+extern int z_status;
+#endif
+
+extern KeyFunc xpost_cb[];
+extern KeyFunc xmbox_cb[];
+
+static int *xpostIndex; /* Thor: first ypost pos in ypost_xo.key */
+static int comebackPos; /* °O¿ý³Ì«á */
+
+
+static char HintWord[TTLEN + 1];
+static char HintAuthor[IDLEN + 1];
+
+
+static int
+XoXpost(xo, hdr, on, off, fchk) /* Thor: eXtended post : call from post_cb */
+ XO *xo;
+ HDR *hdr; /* ·j´Mªº±ø¥ó */
+ int on, off; /* ·j´Mªº½d³ò (on~off-1) */
+ int (*fchk) (); /* ·j´Mªº¨ç¦¡ */
+{
+ int *list, fsize, max, locus, count, i;
+ char *fimage;
+ HDR *head;
+ XO *xt;
+#ifdef HAVE_XYNEWS
+ int returnPos;
+#endif
+
+ if (xo->max <= 0) /* Thor.980911: µù¸Ñ: ¥H¨¾¸U¤@ */
+ return XO_FOOT;
+
+ /* build index according to input condition */
+
+ fimage = f_map(xo->dir, &fsize);
+
+ if (fimage == (char *) -1)
+ {
+ vmsg("¥Ø«eµLªk¶}±Ò¯Á¤ÞÀÉ");
+ return XO_FOOT;
+ }
+
+ /* allocate index memory, remember free first */
+
+ /* Thor.990113: ©È°Ýtitle, authorªºÀþ¶¡¤S¦³¤Hpost */
+ max = xpostIndex ? xo->max : fsize / sizeof(HDR);
+ list = (int *) malloc(sizeof(int) * max);
+
+ count = 0; /* Á`¦@¦³´X½gº¡¨¬±ø¥ó */
+
+ if (max > off)
+ max = off;
+
+ for (i = on; i < max; i++)
+ {
+ if (xpostIndex) /* ¼W¥[±ø¥ó¦A¦¸·j´M®É¡A¥u»Ý­n§ä¦b xpostIndex[] ¸Ì­±ªº */
+ locus = xpostIndex[i];
+ else /* ¾ã­Ó xo->dir ³£±½¤@¦¸ */
+ locus = i;
+
+ head = (HDR *) fimage + locus;
+
+#ifdef HAVE_REFUSEMARK
+ if ((head->xmode & POST_RESTRICT) &&
+ strcmp(head->owner, cuser.userid) && !(bbstate & STAT_BM))
+ continue;
+#endif
+
+ /* check condition */
+ if (!(* fchk) (head, hdr))
+ continue;
+
+ list[count++] = locus;
+ }
+
+ munmap(fimage, fsize);
+
+ if (count <= 0)
+ {
+ free(list);
+ vmsg(MSG_XY_NONE);
+ return XO_FOOT;
+ }
+
+ /* ¼W¥[±ø¥ó¦A¦¸·j´M */
+ if (xpostIndex)
+ {
+ free(xpostIndex);
+ xpostIndex = list;
+
+ xo->pos = 0;
+ xo->max = count;
+
+ return xpost_init(xo);
+ }
+
+ /* ­º¦¸·j´M */
+ xpostIndex = list;
+
+ /* build XO for xpost_xo */
+
+ comebackPos = xo->pos; /* Thor: record pos, future use */
+#ifdef HAVE_XYNEWS
+ returnPos = comebackPos;
+#endif
+
+ xz[XZ_XPOST - XO_ZONE].xo = xt = xo_new(xo->dir);
+ xz[XZ_XPOST - XO_ZONE].cb = (xo->dir[0] == 'b') ? xpost_cb : xmbox_cb;
+ xt->pos = 0;
+ xt->max = count;
+ xt->xyz = xo->xyz;
+ xt->key = XZ_XPOST;
+
+ xover(XZ_XPOST);
+
+ /* set xo->pos for new location */
+
+#ifdef HAVE_XYNEWS
+ if (xz[XZ_NEWS - XO_ZONE].xo)
+ xo->pos = returnPos; /* ±q XZ_XPOST ¦^¨ì XZ_NEWS ´å¼Ð²¾¥h­ì¨Óªº¦a¤è */
+ else
+#endif
+ xo->pos = comebackPos; /* ±q XZ_XPOST ¦^¨ì XZ_POST ´å¼Ð²¾¥h­ì¨Óªº¦a¤è©Î©Ò¾\ۤ峹ªº¯u¥¿¦ì¸m */
+
+ /* free xpost_xo */
+
+ if (xt = xz[XZ_XPOST - XO_ZONE].xo)
+ {
+ free(xt);
+ xz[XZ_XPOST - XO_ZONE].xo = NULL;
+ }
+
+ /* free index memory, remember check free pointer */
+
+ if (list)
+ {
+ free(list);
+ xpostIndex = NULL;
+ }
+
+ return XO_INIT;
+}
+
+
+ /* --------------------------------------------------- */
+ /* ·j´M§@ªÌ/¼ÐÃD */
+ /* --------------------------------------------------- */
+
+
+static int /* 0:¤£º¡¨¬±ø¥ó !=0:º¡¨¬±ø¥ó */
+filter_select(head, hdr)
+ HDR *head; /* «Ý´úª« */
+ HDR *hdr; /* ±ø¥ó */
+{
+ char *title;
+ usint str4;
+
+ /* ­É¥Î hdr->xid ·í strlen(hdr->owner) */
+
+ /* Thor.981109: ¯S§Oª`·N¡A¬°¤F­°§C load¡Aauthor ¬O±qÀY match¡A¤£¬O substr match */
+ if (hdr->xid && str_ncmp(head->owner, hdr->owner, hdr->xid))
+ return 0;
+
+ if (hdr->title[0])
+ {
+ title = head->title;
+ str4 = STR4(title);
+ if (str4 == STR4(STR_REPLY) || str4 == STR4(STR_FORWARD)) /* Thor.980911: ¥ý§â Re:/Fw: °£¥~ */
+ title += 4;
+ if (!str_sub(title, hdr->title))
+ return 0;
+ }
+
+ return 1;
+}
+
+
+int
+XoXselect(xo)
+ XO *xo;
+{
+ HDR hdr;
+ char *key;
+
+#ifdef EVERY_Z
+ if (z_status && xz[XZ_XPOST - XO_ZONE].xo) /* itoc.020308: ¤£±o²Ö¿n¶i¤J¤G¦¸ */
+ {
+ vmsg(MSG_XYDENY);
+ return XO_FOOT;
+ }
+#endif
+
+ /* input condition */
+
+ key = hdr.title;
+ if (vget(b_lines, 0, MSG_XYPOST1, key, 30, DOECHO))
+ {
+ strcpy(HintWord, key);
+ str_lowest(key, key);
+ }
+ else
+ {
+ HintWord[0] = '\0';
+ }
+
+ key = hdr.owner;
+ if (vget(b_lines, 0, MSG_XYPOST2, key, IDLEN + 1, DOECHO))
+ {
+ strcpy(HintAuthor, key);
+ str_lower(key, key);
+ hdr.xid = strlen(key);
+ }
+ else
+ {
+ HintAuthor[0] = '\0';
+ hdr.xid = 0;
+ }
+
+ if (!hdr.title[0] && !hdr.xid)
+ return XO_FOOT;
+
+ return XoXpost(xo, &hdr, 0, INT_MAX, filter_select);
+}
+
+
+ /* --------------------------------------------------- */
+ /* ·j´M§@ªÌ */
+ /* --------------------------------------------------- */
+
+
+int
+XoXauthor(xo)
+ XO *xo;
+{
+ HDR hdr;
+ char *author;
+
+#ifdef EVERY_Z
+ if (z_status && xz[XZ_XPOST - XO_ZONE].xo) /* itoc.020308: ¤£±o²Ö¿n¶i¤J¤G¦¸ */
+ {
+ vmsg(MSG_XYDENY);
+ return XO_FOOT;
+ }
+#endif
+
+ author = hdr.owner;
+ if (!vget(b_lines, 0, MSG_XYPOST2, author, IDLEN + 1, DOECHO))
+ return XO_FOOT;
+
+ HintWord[0] = '\0';
+ strcpy(HintAuthor, author);
+
+ hdr.title[0] = '\0';
+ str_lower(author, author);
+ hdr.xid = strlen(author);
+
+ return XoXpost(xo, &hdr, 0, INT_MAX, filter_select);
+}
+
+
+ /* --------------------------------------------------- */
+ /* ·j´M¼ÐÃD */
+ /* --------------------------------------------------- */
+
+
+int
+XoXtitle(xo)
+ XO *xo;
+{
+ HDR hdr;
+ char *title;
+
+#ifdef EVERY_Z
+ if (z_status && xz[XZ_XPOST - XO_ZONE].xo) /* itoc.020308: ¤£±o²Ö¿n¶i¤J¤G¦¸ */
+ {
+ vmsg(MSG_XYDENY);
+ return XO_FOOT;
+ }
+#endif
+
+ title = hdr.title;
+ if (!vget(b_lines, 0, MSG_XYPOST1, title, 30, DOECHO))
+ return XO_FOOT;
+
+ strcpy(HintWord, title);
+ HintAuthor[0] = '\0';
+
+ str_lowest(title, title);
+ hdr.xid = 0;
+
+ return XoXpost(xo, &hdr, 0, INT_MAX, filter_select);
+}
+
+
+ /* --------------------------------------------------- */
+ /* ·j´M¬Û¦P¼ÐÃD */
+ /* --------------------------------------------------- */
+
+
+static int /* 0:¤£º¡¨¬±ø¥ó !=0:º¡¨¬±ø¥ó */
+filter_search(head, hdr)
+ HDR *head; /* «Ý´úª« */
+ HDR *hdr; /* ±ø¥ó */
+{
+ char *title, buf[TTLEN + 1];
+ usint str4;
+
+ title = head->title;
+ str4 = STR4(title);
+ if (str4 == STR4(STR_REPLY) || str4 == STR4(STR_FORWARD)) /* Thor.980911: ¥ý§â Re:/Fw: °£¥~ */
+ title += 4;
+ str_lowest(buf, title);
+ return !strcmp(buf, hdr->title);
+}
+
+
+int
+XoXsearch(xo)
+ XO *xo;
+{
+ HDR hdr, *mhdr;
+ char *title;
+ usint str4;
+
+#ifdef EVERY_Z
+ if (z_status && xz[XZ_XPOST - XO_ZONE].xo) /* itoc.020308: ¤£±o²Ö¿n¶i¤J¤G¦¸ */
+ {
+ vmsg(MSG_XYDENY);
+ return XO_FOOT;
+ }
+#endif
+
+ mhdr = (HDR *) xo_pool + (xo->pos - xo->top);
+
+ title = mhdr->title;
+ str4 = STR4(title);
+ if (str4 == STR4(STR_REPLY) || str4 == STR4(STR_FORWARD)) /* Thor.980911: ¥ý§â Re:/Fw: °£¥~ */
+ title += 4;
+
+ strcpy(HintWord, title);
+ HintAuthor[0] = '\0';
+
+ str_lowest(hdr.title, title);
+
+ return XoXpost(xo, &hdr, 0, INT_MAX, filter_search);
+}
+
+
+ /* --------------------------------------------------- */
+ /* ¥þ¤å·j´M */
+ /* --------------------------------------------------- */
+
+
+static char *search_folder;
+static int search_fit; /* >=0:§ä¨ì´X½g -1:¤¤Â_·j´M */
+static int search_all; /* ¤w·j´M´X½g */
+
+static int /* 0:¤£º¡¨¬±ø¥ó !=0:º¡¨¬±ø¥ó */
+filter_full(head, hdr)
+ HDR *head; /* «Ý´úª« */
+ HDR *hdr; /* ±ø¥ó */
+{
+ char buf[80], *fimage;
+ int rc, fsize;
+ struct timeval tv = {0, 10};
+
+ if (search_fit < 0) /* ¤¤Â_·j´M */
+ return 0;
+
+ if (search_all % 100 == 0) /* ¨C 100 ½g¤~³ø§i¤@¦¸¶i«× */
+ {
+ sprintf(buf, "¥Ø«e§ä¨ì \033[1;33m%d / %d\033[m ½g¡A¥þ¤å·j´M¤¤\033[5m...\033[m«ö¥ô·NÁ䤤Â_",
+ search_fit, search_all);
+ outz(buf);
+ refresh();
+ }
+ search_all++;
+
+ hdr_fpath(buf, search_folder, head);
+
+ fimage = f_map(buf, &fsize);
+ if (fimage == (char *) -1)
+ return 0;
+
+ rc = 0;
+ if (str_sub(fimage, hdr->title))
+ {
+ rc = 1;
+ search_fit++;
+ }
+
+ munmap(fimage, fsize);
+
+ /* ¨Ï¥ÎªÌ¥i¥H¤¤Â_·j´M */
+ fsize = 1;
+ if (select(1, (fd_set *) &fsize, NULL, NULL, &tv) > 0)
+ {
+ vkey();
+ search_fit = -1;
+ }
+
+ return rc;
+}
+
+
+int
+XoXfull(xo)
+ XO *xo;
+{
+ HDR hdr;
+ char *key, ans[8];
+ int head, tail;
+
+#ifdef EVERY_Z
+ if (z_status && xz[XZ_XPOST - XO_ZONE].xo) /* itoc.020308: ¤£±o²Ö¿n¶i¤J¤G¦¸ */
+ {
+ vmsg(MSG_XYDENY);
+ return XO_FOOT;
+ }
+#endif
+
+ /* input condition */
+
+ key = hdr.title;
+ if (!vget(b_lines, 0, "¤º¤åÃöÁä¦r¡G", key, 30, DOECHO))
+ return XO_FOOT;
+
+ vget(b_lines, 0, "[³]©w·j´M½d³ò] °_ÂI¡G(Enter)±qÀY¶}©l ", ans, 6, DOECHO);
+ if ((head = atoi(ans)) <= 0)
+ head = 1;
+
+ vget(b_lines, 44, "²×ÂI¡G(Enter)§ä¨ì³Ì«á ", ans, 6, DOECHO);
+ if ((tail = atoi(ans)) < head)
+ tail = INT_MAX;
+
+ head--;
+
+ sprintf(HintWord, "[¥þ¤å·j´M] %s", key);
+ HintAuthor[0] = '\0';
+ str_lowest(key, key);
+
+ search_folder = xo->dir;
+ search_fit = 0;
+ search_all = 0;
+
+ return XoXpost(xo, &hdr, head, tail, filter_full);
+}
+
+
+ /* --------------------------------------------------- */
+ /* ·j´M mark ¡B¥[±K¡B³Q±ÀÂˤ峹 */
+ /* --------------------------------------------------- */
+
+
+static int /* 0:¤£º¡¨¬±ø¥ó !=0:º¡¨¬±ø¥ó */
+filter_mark(head, hdr)
+ HDR *head; /* «Ý´úª« */
+ HDR *hdr; /* ±ø¥ó */
+{
+ return (head->xmode & POST_MARKED);
+}
+
+static int
+filter_restrict(head, hdr)
+ HDR *head;
+ HDR *hdr;
+{
+ return (head->xmode & POST_RESTRICT);
+}
+
+static int
+filter_score(head, hdr)
+ HDR *head;
+ HDR *hdr;
+{
+ return (head->xmode & POST_SCORE);
+}
+
+
+int
+XoXmark(xo)
+ XO *xo;
+{
+#ifdef EVERY_Z
+ if (z_status && xz[XZ_XPOST - XO_ZONE].xo) /* itoc.020308: ¤£±o²Ö¿n¶i¤J¤G¦¸ */
+ {
+ vmsg(MSG_XYDENY);
+ return XO_FOOT;
+ }
+#endif
+
+ strcpy(HintWord, "\033[1;33m©Ò¦³ mark ¤å³¹\033[m");
+ HintAuthor[0] = '\0';
+
+ return XoXpost(xo, NULL, 0, INT_MAX, filter_mark);
+}
+
+int
+XoXrestrict(xo)
+ XO *xo;
+{
+#ifdef EVERY_Z
+ if (z_status && xz[XZ_XPOST - XO_ZONE].xo)
+ {
+ vmsg(MSG_XYDENY);
+ return XO_FOOT;
+ }
+#endif
+
+ strcpy(HintWord, "\033[1;33m©Ò¦³¥[±K¤å³¹\033[m");
+ HintAuthor[0] = '\0';
+
+ return XoXpost(xo, NULL, 0, INT_MAX, filter_restrict);
+}
+
+int
+XoXscore(xo)
+ XO *xo;
+{
+#ifdef EVERY_Z
+ if (z_status && xz[XZ_XPOST - XO_ZONE].xo)
+ {
+ vmsg(MSG_XYDENY);
+ return XO_FOOT;
+ }
+#endif
+
+ strcpy(HintWord, "\033[1;33m©Ò¦³³Q±ÀÂˤ峹\033[m");
+ HintAuthor[0] = '\0';
+
+ return XoXpost(xo, NULL, 0, INT_MAX, filter_score);
+}
+
+
+ /* --------------------------------------------------- */
+ /* ·j´M¥»¦a */
+ /* --------------------------------------------------- */
+
+
+static int /* 0:¤£º¡¨¬±ø¥ó !=0:º¡¨¬±ø¥ó */
+filter_local(head, hdr)
+ HDR *head; /* «Ý´úª« */
+ HDR *hdr; /* ±ø¥ó */
+{
+ return !(head->xmode & POST_INCOME);
+}
+
+
+int
+XoXlocal(xo)
+ XO *xo;
+{
+ if (currbattr & BRD_NOTRAN)
+ {
+ vmsg("¥»ªO¬°¤£Âà«HªO¡A¥þ³¡³£¬O¥»¦a¤å³¹");
+ return XO_FOOT;
+ }
+
+#ifdef EVERY_Z
+ if (z_status && xz[XZ_XPOST - XO_ZONE].xo) /* itoc.020308: ¤£±o²Ö¿n¶i¤J¤G¦¸ */
+ {
+ vmsg(MSG_XYDENY);
+ return XO_FOOT;
+ }
+#endif
+
+ strcpy(HintWord, "\033[1;33m©Ò¦³«DÂà¶i¤å³¹\033[m");
+ HintAuthor[0] = '\0';
+
+ return XoXpost(xo, NULL, 0, INT_MAX, filter_local);
+}
+
+
+/* ----------------------------------------------------- */
+/* ¦ê¦C·j´M¬É­± */
+/* ----------------------------------------------------- */
+
+
+int
+xpost_head(xo)
+ XO *xo;
+{
+ vs_head("¥DÃD¦ê¦C", xo->xyz);
+
+ /* itoc.010323: ¦P®É´£¥Ü§@ªÌ/¥DÃD */
+ outs("[¦ê±µ¨t¦C] ");
+ if (*HintAuthor)
+ prints("§@ªÌ¡G%-13s ", HintAuthor);
+ if (*HintWord)
+ prints("¼ÐÃD¡G%.30s", HintWord);
+
+ prints(NECKER_XPOST, d_cols, "",
+ currbattr & BRD_NOPHONETIC ? "¢®" : "¡³",
+ currbattr & BRD_NOSCORE ? "¢®" : "¡³");
+
+ return XO_BODY;
+}
+
+
+static void
+xpost_pick(xo)
+ XO *xo;
+{
+ int *list, fsize, pos, max, top, num;
+ HDR *fimage, *hdr;
+
+ fimage = (HDR *) f_map(xo->dir, &fsize);
+ if (fimage == (HDR *) - 1)
+ return;
+
+ hdr = (HDR *) xo_pool;
+ list = xpostIndex;
+
+ pos = xo->pos;
+ xo->top = top = (pos / XO_TALL) * XO_TALL;
+ max = xo->max;
+ pos = top + XO_TALL;
+ if (max > pos)
+ max = pos;
+ num = fsize / sizeof(HDR);
+
+ do
+ {
+ pos = list[top++];
+ if (pos >= num) /* hightman.030528: Á×§K .DIR ³Q§R´î®É¡A·|¨S¦³¤å³¹¥i¥HÅã¥Ü */
+ continue;
+ memcpy(hdr, fimage + pos, sizeof(HDR));
+ hdr->xid = pos; /* ¥Î hdr->xid ¨Ó°O¿ý¨ä­ì¥ý¦b¬ÝªO¤¤ªº pos */
+ hdr++;
+ } while (top < max);
+
+ munmap(fimage, fsize);
+}
+
+
+int
+xpost_init(xo)
+ XO *xo;
+{
+ /* load into pool */
+
+ xpost_pick(xo);
+
+ return xpost_head(xo);
+}
+
+
+int
+xpost_load(xo)
+ XO *xo;
+{
+ /* load into pool */
+
+ xpost_pick(xo);
+
+ return XO_BODY;
+}
+
+
+static void
+xpost_history(xo, fhdr) /* ±N fhdr ³o½g¥[¤J brh */
+ XO *xo;
+ HDR *fhdr;
+{
+ time_t prev, chrono, next;
+ int pos;
+ char *dir;
+ HDR buf;
+
+ chrono = fhdr->chrono;
+ if (!brh_unread(chrono)) /* ¦pªG¤w¦b brh ¤¤¡A´NµL»Ý°Ê§@ */
+ return;
+
+ dir = xo->dir;
+ pos = fhdr->xid;
+
+ if (!rec_get(dir, &buf, sizeof(HDR), pos - 1))
+ prev = buf.chrono;
+ else
+ prev = chrono;
+
+ if (!rec_get(dir, &buf, sizeof(HDR), pos + 1))
+ next = buf.chrono;
+ else
+ next = chrono;
+
+ brh_add(prev, chrono, next);
+}
+
+
+int
+xpost_browse(xo)
+ XO *xo;
+{
+ HDR *hdr;
+ int key;
+ char *dir, fpath[64];
+
+ dir = xo->dir;
+
+ for (;;)
+ {
+ hdr = (HDR *) xo_pool + (xo->pos - xo->top);
+
+#if 0 /* itoc.010822: ¤£»Ý­n¡A¦b XoXpost() ¤¤¤w³Q­ç°£ */
+#ifdef HAVE_REFUSEMARK
+ xmode = hdr->xmode;
+ if ((xmode & POST_RESTRICT) &&
+ strcmp(hdr->owner, cuser.userid) && !(bbstate & STAT_BM))
+ continue;
+#endif
+#endif
+
+ hdr_fpath(fpath, dir, hdr);
+
+ /* Thor.990204: ¬°¦Ò¼{more ¶Ç¦^­È */
+ if ((key = more(fpath, FOOTER_POST)) < 0)
+ break;
+
+ comebackPos = hdr->xid;
+ /* Thor.980911: ±q¦ê±µ¼Ò¦¡¦^¨Ó®É­n¦^¨ì¬Ý¹Lªº¨º½g¤å³¹¦ì¸m */
+
+ xpost_history(xo, hdr);
+ strcpy(currtitle, str_ttl(hdr->title));
+
+re_key:
+ /* Thor.990204: ¬°¦Ò¼{more ¶Ç¦^­È */
+ if (!key)
+ key = vkey();
+
+ switch (key)
+ {
+ case KEY_UP:
+ case KEY_PGUP:
+ case '[': /* itoc.000227: ¦ê¦C·j´M¤¤¡A¦³®É·Q¥Î [ ¬Ý¤W¤@½g¤å³¹ */
+ case 'k': /* itoc.000227: ¦ê¦C·j´M¤¤¡A¦³®É·Q¥Î k ¬Ý¤W¤@½g¤å³¹ */
+ {
+ int pos = xo->pos - 1;
+
+ /* itoc.000227: Á×§K¬Ý¹LÀY */
+ if (pos < 0)
+ return xpost_head(xo);
+
+ xo->pos = pos;
+
+ if (pos <= xo->top)
+ xpost_pick(xo);
+
+ continue;
+ }
+
+ case KEY_DOWN:
+ case KEY_PGDN:
+ case ']': /* Thor.990204: ¦ê¦C·j´M¤¤¡A¦³®É·Q¥Î ] ¬Ý¤U¤@½g¤å³¹ */
+ case 'j': /* Thor.990204: ¦ê¦C·j´M¤¤¡A¦³®É·Q¥Î j ¬Ý¤U¤@½g¤å³¹ */
+ case ' ':
+ {
+ int pos = xo->pos + 1;
+
+ /* Thor.980727: ­×¥¿¬Ý¹LÀYªºbug */
+
+ if (pos >= xo->max)
+ return xpost_head(xo);
+
+ xo->pos = pos;
+
+ if (pos >= xo->top + XO_TALL)
+ xpost_pick(xo);
+
+ continue;
+ }
+
+ case 'y':
+ case 'r':
+ if (bbstate & STAT_POST)
+ {
+ if (do_reply(xo, hdr) == XO_INIT) /* ¦³¦¨¥\¦a post ¥X¥h¤F */
+ return xpost_init(xo);
+ }
+ break;
+
+ case 'm':
+ if ((bbstate & STAT_BOARD) && !(hdr->xmode & POST_MARKED))
+ {
+ /* ¦b xpost_browse ®É¬Ý¤£¨ì m °O¸¹¡A©Ò¥H­­¨î¥u¯à mark */
+ hdr->xmode ^= POST_MARKED;
+ currchrono = hdr->chrono;
+ rec_put(dir, hdr, sizeof(HDR), hdr->xid, cmpchrono);
+ }
+ break;
+
+#ifdef HAVE_SCORE
+ case '%':
+ post_score(xo);
+ return xpost_init(xo);
+#endif
+
+ case '/':
+ if (vget(b_lines, 0, "·j´M¡G", hunt, sizeof(hunt), DOECHO))
+ {
+ key = more(fpath, FOOTER_POST);
+ goto re_key;
+ }
+ continue;
+
+ case 'E':
+ return post_edit(xo);
+
+ case 'C': /* itoc.000515: xpost_browse ®É¥i¦s¤J¼È¦sÀÉ */
+ {
+ FILE *fp;
+ if (fp = tbf_open())
+ {
+ f_suck(fp, fpath);
+ fclose(fp);
+ }
+ }
+ break;
+
+ case 'h':
+ xo_help("post");
+ break;
+ }
+ break;
+ }
+
+ return xpost_head(xo);
+}
+
+
+int
+xmbox_browse(xo)
+ XO *xo;
+{
+ HDR *hdr;
+ char *dir, fpath[64];
+
+ int key;
+
+ dir = xo->dir;
+
+ for (;;)
+ {
+ hdr = (HDR *) xo_pool + (xo->pos - xo->top);
+
+ hdr_fpath(fpath, dir, hdr);
+
+ /* Thor.990204: ¬°¦Ò¼{more ¶Ç¦^­È */
+ if ((key = more(fpath, FOOTER_MAILER)) < 0)
+ break;
+
+ comebackPos = hdr->xid;
+ /* Thor.980911: ±q¦ê±µ¼Ò¦¡¦^¨Ó®É­n¦^¨ì¬Ý¹Lªº¨º½g¤å³¹¦ì¸m */
+
+ strcpy(currtitle, str_ttl(hdr->title));
+
+re_key:
+ /* Thor.990204: ¬°¦Ò¼{more ¶Ç¦^­È */
+ if (!key)
+ key = vkey();
+
+ switch (key)
+ {
+ case KEY_UP:
+ case KEY_PGUP:
+ case '[': /* itoc.000227: ¦ê¦C·j´M¤¤¡A¦³®É·Q¥Î [ ¬Ý¤W¤@½g¤å³¹ */
+ case 'k': /* itoc.000227: ¦ê¦C·j´M¤¤¡A¦³®É·Q¥Î k ¬Ý¤W¤@½g¤å³¹ */
+ {
+ int pos = xo->pos - 1;
+
+ /* itoc.000227: Á×§K¬Ý¹LÀY */
+ if (pos < 0)
+ return xpost_head(xo);
+
+ xo->pos = pos;
+
+ if (pos <= xo->top)
+ xpost_pick(xo);
+
+ continue;
+ }
+
+ case KEY_DOWN:
+ case KEY_PGDN:
+ case ']': /* Thor.990204: ¦ê¦C·j´M¤¤¡A¦³®É·Q¥Î ] ¬Ý¤U¤@½g¤å³¹ */
+ case 'j': /* Thor.990204: ¦ê¦C·j´M¤¤¡A¦³®É·Q¥Î j ¬Ý¤U¤@½g¤å³¹ */
+ case ' ':
+ {
+ int pos = xo->pos + 1;
+
+ /* Thor.980727: ­×¥¿¬Ý¹LÀYªºbug */
+
+ if (pos >= xo->max)
+ return xpost_head(xo);
+
+ xo->pos = pos;
+
+ if (pos >= xo->top + XO_TALL)
+ xpost_pick(xo);
+
+ continue;
+ }
+
+ case 'y':
+ case 'r':
+ strcpy(quote_file, fpath);
+ do_mreply(hdr, 1);
+ break;
+
+ case 'm':
+ if (!(hdr->xmode & POST_MARKED))
+ {
+ /* ¦b xmbox_browse ®É¬Ý¤£¨ì m °O¸¹¡A©Ò¥H­­¨î¥u¯à mark */
+ hdr->xmode ^= POST_MARKED;
+ currchrono = hdr->chrono;
+ rec_put(dir, hdr, sizeof(HDR), hdr->xid, cmpchrono);
+ }
+ break;
+
+ case '/':
+ if (vget(b_lines, 0, "·j´M¡G", hunt, sizeof(hunt), DOECHO))
+ {
+ key = more(fpath, FOOTER_MAILER);
+ goto re_key;
+ }
+ continue;
+
+ case 'E':
+ return mbox_edit(xo);
+
+ case 'C': /* itoc.000515: xmbox_browse ®É¥i¦s¤J¼È¦sÀÉ */
+ {
+ FILE *fp;
+ if (fp = tbf_open())
+ {
+ f_suck(fp, fpath);
+ fclose(fp);
+ }
+ }
+ break;
+
+ case 'h':
+ xo_help("mbox");
+ break;
+ }
+ break;
+ }
+
+ return xpost_head(xo);
+}
+
+
+#ifdef HAVE_XYNEWS /* itoc.010822: news ¾\Ū¼Ò¦¡ */
+
+#if 0 /* ºc·Q */
+
+¦b¤j¬ÝªO(³s½u¬ÝªO)¤¤±`±`¦³«Ü¦hÄé¤ô¤å³¹¡A¥é·Ó news ªº¾\Ū¤è¦¡¡A
+§â©Ò¦³ reply ªº¤å³¹³£¥ýÁôÂáA¥uÅã¥Ü²Ä¤@«Êµo¤å¡C
+
+²Ä¤@½ü§Q¥Î XoNews ¨Ó­ç°£ reply ¤å³¹
+²Ä¤G½ü§Q¥Î­ì¦³ªº XoXsearch ·j´M¦P¥DÃD¤å³¹
+¦p¦¹´N¥i¥H¹F¨ì·s»D¾\Ū¼Ò¦¡ªº®ÄªG
+
+#endif
+
+
+static int *newsIndex;
+
+extern KeyFunc news_cb[];
+
+
+int
+news_head(xo)
+ XO *xo;
+{
+ vs_head("·s»D¾\\Ū", xo->xyz);
+ prints(NECKER_NEWS, d_cols, "");
+ return XO_BODY;
+}
+
+
+static void
+news_pick(xo)
+ XO *xo;
+{
+ int *list, fsize, pos, max, top;
+ HDR *fimage, *hdr;
+
+ fimage = (HDR *) f_map(xo->dir, &fsize);
+ if (fimage == (HDR *) - 1)
+ return;
+
+ hdr = (HDR *) xo_pool;
+ list = newsIndex;
+
+ pos = xo->pos;
+ xo->top = top = (pos / XO_TALL) * XO_TALL;
+ max = xo->max;
+ pos = top + XO_TALL;
+ if (max > pos)
+ max = pos;
+
+ do
+ {
+ pos = list[top++];
+ memcpy(hdr, fimage + pos, sizeof(HDR));
+ /* hdr->xid = pos; */ /* ¦b XZ_NEWS ¨S¥Î¨ì xid¡A¥i¥H¦Ò¼{«O¯dµ¹ reply ½g¼Æ */
+ hdr++;
+ } while (top < max);
+
+ munmap(fimage, fsize);
+}
+
+
+int
+news_init(xo)
+ XO *xo;
+{
+ /* load into pool */
+ news_pick(xo);
+ return news_head(xo);
+}
+
+
+int
+news_load(xo)
+ XO *xo;
+{
+ /* load into pool */
+ news_pick(xo);
+ return XO_BODY;
+}
+
+
+int
+XoNews(xo) /* itoc: News reader : call from post_cb */
+ XO *xo;
+{
+ int returnPos;
+ int *list, fsize, max, count, i;
+ char *fimage;
+ HDR *head;
+ XO *xt;
+
+ if (xo->max <= 0) /* Thor.980911: µù¸Ñ: ¥H¨¾¸U¤@ */
+ return XO_FOOT;
+
+#ifdef EVERY_Z /* itoc.060206: ¥u¦³¥Î ^Z ¤~¥i¯à±q¤£¦P¬ÝªO¶i¤J·s»D¼Ò¦¡ */
+ if (xz[XZ_NEWS - XO_ZONE].xo) /* itoc.020308: ¤£±o²Ö¿n¶i¤J¤G¦¸ */
+ {
+ vmsg(MSG_XYDENY);
+ return XO_FOOT;
+ }
+#endif
+
+ /* build index according to input condition */
+
+ fimage = f_map(xo->dir, &fsize);
+
+ if (fimage == (char *) -1)
+ {
+ vmsg("¥Ø«eµLªk¶}±Ò¯Á¤ÞÀÉ");
+ return XO_FOOT;
+ }
+
+ /* allocate index memory, remember free first */
+
+ /* Thor.990113: ©È°Ýtitle, authorªºÀþ¶¡¤S¦³¤Hpost */
+ max = fsize / sizeof(HDR);
+ list = (int *) malloc(sizeof(int) * max);
+
+ count = 0;
+
+ for (i = 0; i < max; i++)
+ {
+ head = (HDR *) fimage + i;
+
+#ifdef HAVE_REFUSEMARK
+ if ((head->xmode & POST_RESTRICT) &&
+ strcmp(head->owner, cuser.userid) && !(bbstate & STAT_BM))
+ continue;
+#endif
+
+ /* check condition */
+ if (STR4(head->title) == STR4(STR_REPLY)) /* reply ªº¤å³¹¤£­n */
+ continue;
+
+ list[count++] = i;
+ }
+
+ munmap(fimage, fsize);
+
+ if (count <= 0)
+ {
+ free(list);
+ vmsg(MSG_XY_NONE);
+ return XO_FOOT;
+ }
+
+ newsIndex = list;
+
+ /* build XO for news_xo */
+
+ returnPos = xo->pos; /* Thor: record pos, future use */
+ xz[XZ_NEWS - XO_ZONE].xo = xt = xo_new(xo->dir);
+ xz[XZ_NEWS - XO_ZONE].cb = news_cb;
+ xt->pos = 0;
+ xt->max = count;
+ xt->xyz = xo->xyz;
+ xt->key = XZ_NEWS;
+
+ xover(XZ_NEWS);
+
+ /* set xo->pos for new location */
+
+ xo->pos = returnPos; /* ±q XZ_NEWS ¦^¨ì XZ_POST ´å¼Ð²¾¥h­ì¨Óªº¦a¤è */
+
+ /* free news_xo */
+
+ if (xt = xz[XZ_NEWS - XO_ZONE].xo)
+ {
+ free(xt);
+ xz[XZ_NEWS - XO_ZONE].xo = NULL;
+ }
+
+ /* free index memory, remember check free pointer */
+
+ if (list)
+ free(list);
+
+ return XO_INIT;
+}
+#endif /* HAVE_XYNEWS */
diff --git a/pip/Makefile b/pip/Makefile
new file mode 100644
index 0000000..0ba0650
--- /dev/null
+++ b/pip/Makefile
@@ -0,0 +1,64 @@
+# ------------------------------------------------------ #
+# Makefile ( NTHU CS MapleBBS Ver 3.10 ) #
+# ------------------------------------------------------ #
+# author : itoc.bbs@bbs.tnfsh.tn.edu.tw #
+# target : Makefile for PIP GAME main programs #
+# create : 01/07/24 #
+# update : / / #
+# ------------------------------------------------------ #
+
+
+# ------------------------------------------------------ #
+# ¤U¦Cªº make rules ¤£»Ý­×§ï #
+# ------------------------------------------------------ #
+
+
+OBJ = pip_basic.o pip_ending.o pip_fight.o pip_item.o pip_job.o \
+ pip_menu.o pip_pk.o pip_play.o pip_prac.o pip_quest.o pip_race.o \
+ pip_royal.o pip_stuff.o pip_visio.o pip_weapon.o pip.o
+
+
+SO = pip.so
+
+
+all:
+ @echo "Please enter 'make sys-type', "
+ @echo " make sun : for Sun-OS 4.x and maybe some BSD systems, cc or gcc"
+ @echo " make linux : for Linux"
+ @echo " make solaris : for Sun-OS 5.x gcc"
+ @echo " make sol-x86 : for Solaris 7 x86"
+ @echo " make freebsd : for BSD 4.4 systems"
+ @echo " make bsd : for BSD systems, cc or gcc, if not in the above lists"
+ @echo " make cygwin : for Microsoft Windows and Cygwin gcc"
+
+sun:
+ @$(MAKE) CC=gcc CFLAGS="-O2 -pipe -fomit-frame-pointer -Wunused -I../include" $(SO)
+
+linux:
+ @$(MAKE) CC=gcc CFLAGS="-DLINUX -O2 -pipe -fomit-frame-pointer -Wunused -I../include" $(SO)
+
+solaris:
+ @$(MAKE) CC=gcc CFLAGS="-DSOLARIS -DSYSV -O2 -pipe -fomit-frame-pointer -Wunused -I../include" $(SO)
+
+sol-x86:
+ @$(MAKE) CC=gcc CFLAGS="-DSOLARIS -DSYSV -O2 -fomit-frame-pointer -Wunused -I../include" $(SO)
+
+freebsd:
+ @$(MAKE) CC=gcc CFLAGS="-DBSD44 -O2 -pipe -fomit-frame-pointer -Wunused -I../include" $(SO)
+
+bsd:
+ @$(MAKE) CC=gcc CFLAGS="-DBSD44 -O2 -pipe -fomit-frame-pointer -Wunused -I../include" $(SO)
+
+cygwin:
+ @cd ../maple; make cygwin
+
+
+pip.so: $(OBJ)
+ ld -s -G $(OBJ) -o pip.so -L../lib -ldao
+
+
+install: $(SO)
+ install -m 0700 $? $(HOME)/bin
+
+clean:
+ rm -f $(SO) *.o *~
diff --git a/pip/pip.c b/pip/pip.c
new file mode 100644
index 0000000..83a8ac3
--- /dev/null
+++ b/pip/pip.c
@@ -0,0 +1,241 @@
+/*-------------------------------------------------------*/
+/* pip.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : ¾i¤pÂû¹CÀ¸ */
+/* create : / / */
+/* update : 01/08/14 */
+/* author : dsyan.bbs@forever.twbbs.org */
+/* modify : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+#ifdef HAVE_GAME
+
+
+#define _PIPMAIN_C_ /* ¥ý define _PIPMAIN_C_ ¦A¤Þ¶i pip.h */
+
+#include "pip.h"
+
+
+/*-------------------------------------------------------*/
+/* ¥Dµ{¦¡ */
+/*-------------------------------------------------------*/
+
+#define ALIVE (9/10) /* ´_¬¡¥H«áªº¯à¤O¬O­ì¨Óªº 90% */
+
+static void
+pip_live_again()
+{
+ vs_head("¤pÂû´_¬¡¤â³N¤¤", str_site);
+
+ /* itoc.010814: ´_¬¡¥H«á­nÅýª±®aÁÙª±ªº¤U¥h¡A©Ò¥H¤£°µ¡u¯}Ãa©Ê¡v
+ ªº­°ÄݩʡA¹³ maxhp..µ¥¤£ÅÜ */
+
+ /* °ò¥»ªº¸ê®Æ */
+ d.death = 0;
+ d.liveagain++;
+
+ /* ª¬ºAªº¼Æ­È */
+ d.relation = 0; /* ¤H©MÃdª«ªº¤¬°Ê´c¤Æ */
+ d.happy = 20; /* Åܱo¤£§Ö¼Ö */
+ d.satisfy = 20; /* Åܱo¤£º¡·N */
+
+ /* ¨­Å骺°Ñ¼Æ */
+ d.weight = (rand() % 10) + 55 + (d.bbtime / 180);
+ d.tired = 20;
+ d.sick = 20;
+ d.shit = 20;
+
+ /* µû»ù´î¤Ö */
+ d.social = d.social * ALIVE;
+ d.family = d.family * ALIVE;
+ d.hexp = d.hexp * ALIVE;
+ d.mexp = d.mexp * ALIVE;
+
+ /* Âk¹s */
+ d.hp = d.maxhp;
+ d.mp = 0;
+ d.vp = 0;
+ d.sp = 0;
+
+ vmsg("¤pÂû¤S´_¬¡¤F¡A¤£­n¦A§â¨e¾i¦º¤F³á¡I");
+
+ pip_write_file();
+}
+
+
+static int /* 1:¥Ó½Ð¦¨¥\ 0:¥¢±Ñ */
+pip_apply() /* ·s¤pÂû¥Ó½Ð */
+{
+ time_t now;
+ struct tm *ptime;
+
+ memset(&d, 0, sizeof(d));
+
+ vs_head(BBSNAME PIPNAME, str_site);
+
+ /* ¤pÂû©R¦W */
+ if (!vget(2, 0, " À°¤pÂû¨ú­Ó¦nÅ¥ªº¦W¦r§a¡G", d.name, IDLEN + 1, DOECHO))
+ return 0;
+
+ /* 1:¤½ 2:¥À */
+ d.sex = (ians(4, 3, "©Ê§O¡G(1) ¤p¤½Âû¡ñ (2) ¤p¥ÀÂû¡ð (1/2)¡H[1] ") == '2') ? 2 : 1;
+
+ move(6, 0);
+ outs(" " BBSNAME PIPNAME "ªº¹CÀ¸²{¤µ¤À¦¨¨âºØª±ªk\n");
+ outs(" ¿ï¦³µ²§½·|¦b¤pÂû 20 ·³®Éµ²§ô¹CÀ¸¡A¨Ã§iª¾¤pÂû«áÄòªºµo®i\n");
+ outs(" ¿ï¨S¦³µ²§½«h¤@ª½¾i¨ì¤pÂû¦º¤`¤~µ²§ô¹CÀ¸....");
+
+ /* 1:¤£­n¥B¥¼±B 4:­n¥B¥¼±B */
+ d.wantend = (ians(9, 0, " ±z§Æ±æ¤pÂû¹CÀ¸¬O§_­n¦³ 20 ·³µ²§½(Y/N)¡H[Y] ") == 'n') ? 1 : 4;
+
+ /* ¶}ÀYµe­± */
+ show_basic_pic(0);
+ vmsg("¤pÂû²×©ó½Ï¥Í¤F¡A½Ð¦n¦n·R¥L....");
+
+ /* ¶}ÀY³]©w¡G¨S¦³³]©wªºÄæ¦ì³£¬O¹w³] 0 */
+ time(&now);
+ ptime = localtime(&now);
+ sprintf(d.birth, "%02d/%02d/%02d", ptime->tm_year % 100, ptime->tm_mon + 1, ptime->tm_mday);
+
+ /* °ò¥»¸ê®Æ */
+ d.year = ptime->tm_year;
+ d.month = ptime->tm_mon + 1;
+ d.day = ptime->tm_mday;
+
+ /* ¨­Åé°Ñ¼Æ */
+ d.weight = rand() % 10 + 50;
+
+ /* µû»ù°Ñ¼Æ : ¹w³] 0 */
+
+ /* ¾Ô°«°Ñ¼Æ */
+ d.level = 1;
+ d.hp = d.maxhp = rand() % 20 + 40;
+ d.mp = d.maxmp = rand() % 20 + 40;
+ d.vp = d.maxvp = rand() % 20 + 40;
+ d.sp = d.maxsp = rand() % 20 + 40;
+
+ /* §Þ¯à°Ñ¼Æ : ¹w³] 0 */
+ /* ªZ¾¹°Ñ¼Æ : ¹w³] 0 */
+ /* ¯à¤O°Ñ¼Æ : ¹w³] 0 */
+
+ /* ª¬ºA¼Æ­È */
+ d.happy = rand() % 10 + 45;
+ d.satisfy = rand() % 10 + 45;
+
+ /* ­¹ª«°Ñ¼Æ */
+ d.food = 20;
+ d.cookie = 2;
+
+ /* ª««~°Ñ¼Æ : ¹w³] 0 */
+ d.money = 1000;
+
+ /* ²q®±°Ñ¼Æ : ¹w³] 0 */
+
+ /* °Ñ¨£¤ý¦Ú */
+ d.seeroyalJ = 1;
+
+ /* ±µ¨ü¨D±B·R¤H : ¹w³] 0 */
+ /* ¤u§@«ü¼Æ : ¹w³] 0 */
+
+ /* ¤@¥Í¥u¯à¾Ç±o¤@¶µ¯S®í§Þ¯à */
+ pip_learn_skill(0);
+
+ pip_write_file();
+ return 1;
+}
+
+
+static int
+pip_reborn() /* ¤pÂû­«¥Í */
+{
+ vs_head(BBSNAME PIPNAME, str_site);
+ move(4, 0);
+ outs(" Åwªï¨Ó¨ì\033[1;33m" BBSNAME "¥Íª«¬ì§Þ¬ã¨s°|\033[m\n\n");
+ outs(" ¸g§Ú­Ì½Õ¬dÅã¥Ü ¥ý«e±z¦³¾i¹L¤pÂû³á ¥i¬O³Q±z¾i¦º¤F\n\n");
+
+ if (ians(7, 3, "±z­n§Ú­ÌÅý¨e­«¥Í¶Ü(Y/N)¡H[N] ") == 'y')
+ {
+ pip_live_again();
+ return 1;
+ }
+ else
+ {
+ if (pip_apply())
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/* ¹CÀ¸¥Dµ{¦¡ */
+int
+main_pip()
+{
+ int ch;
+ char fpath[64];
+
+ /* more(PIP_PICHOME "pip.welcome", NULL); */
+ vs_head("¹q¤l¾i¤pÂû", str_site);
+
+ usr_fpath(fpath, cuser.userid, fn_pip);
+
+ if (!dashf(fpath)) /* ¤§«e¨Sª±¹L¤pÂû */
+ {
+ show_system_pic(11);
+
+ if (vkey() == 'q')
+ return 0;
+
+ if (!pip_apply())
+ return 0;
+ }
+ else
+ {
+ show_system_pic(12);
+
+ if ((ch = vkey()) == 'r')
+ {
+ if (!pip_read_backup())
+ return 0;
+ }
+ else if (ch == 'q')
+ {
+ return 0;
+ }
+ else
+ {
+ pip_read_file(cuser.userid, &d);
+
+ if (d.death == 1) /* ª±¦º¤F¥i¿ï¾Ü­«¥Í */
+ {
+ if (!pip_reborn())
+ return 0;
+ }
+ else if (d.death) /* ©ñ¥Í©Îµ²§ôªº¸Ü­n­«·s¶}©l */
+ {
+ if (!pip_apply())
+ return 0;
+ }
+ }
+ }
+
+ start_time = time(0);
+ last_time = start_time;
+
+ /* itoc.010816: ¥Ñ©ó²Ä¤@¦¸¶i¤J¥D¿ï³æ¨S¦³¤¤¶¡ªº¤pÂû¹Ï¡A©Ò¥H­n¥t¥~µ¹ */
+ clrfromto(5, 18);
+ outs("\033[34m¢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¢w¢w¢w¢w¢{\033[m");
+ show_basic_pic(21);
+ move(18, 0);
+ outs("\033[34m¢|¢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¢}\033[m");
+
+ pip_main_menu();
+
+ pip_write_file(); /* Â÷¶}¹CÀ¸¦Û°Ê¦sÀÉ */
+ return 0;
+}
+#endif /* HAVE_GAME */
diff --git a/pip/pip.h b/pip/pip.h
new file mode 100644
index 0000000..da145c7
--- /dev/null
+++ b/pip/pip.h
@@ -0,0 +1,63 @@
+/* ----------------------------------------------------- */
+/* pip.h ( NTHU CS MapleBBS Ver 3.10 ) */
+/* ----------------------------------------------------- */
+/* target : ¤pÂû data structure */
+/* create : 01/08/16 */
+/* update : / / */
+/* author : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/* ----------------------------------------------------- */
+
+
+#ifndef _PIP_H_
+#define _PIP_H_
+
+
+#if 0 /* ª©Åv«Å§i */
+
+ ®Ú¾Ú§Úªº¦ÒÃÒ¡A¹q¤lÂûªº³Ì«e¨­¬O¥Ñ [¤Ñªø¦a¤[ dsyan] ©Ò¼¶¼g¡A
+ ±µµÛ¦b [¶³³¾¤Ñ°ó fennet] ¤â¤¤°µ¤F¤@¨ÇÅܰʡA
+ «á¨Ó¦b [¬PªÅ¤Uªº©]¸Ì chiyuan] ¤j§ïª©¡A§Î¦¨¤F¬PªÅ¾Ô°«Âû¡C
+
+ ¨ä«á [­·¤§¶ð visor] ±N³oµ{¦¡ port ¨ì WindTopBBS ¨Ó¨Ï¥Î¡A
+ ¥Ø«e [»P«n¦@»R itoc] ¦b¥H³oª©µ{¦¡¬°°ò¦¤U¡A°µ¤F¤j´T«×ªº§ïª©¡C
+
+ ¦UÀɮפw¸g³Q§Ú§¹¾ã¦a§ï¹L¤F¡A¦b¬Y¨Ç¤è­±°µ¤F¤@¨Ç³Ì¨Î¤Æ¡A
+ ¥]¬Aµ{¦¡ªº­«·s¼¶¼g¡B·s idea ªº¥[¤Jµ¥µ¥¡A¤ñ¸û­«­nªºÅܰʻ¡©ú©ó¤U¡G
+
+ 1) ±N¤@­Óªñ¸U¦æªºµ{¦¡¼Ò²Õ¤Æ¡A©î´²¨ì¦U *.c ¤¤¡A¥H«á¦b°µ­×§ï®É¯à§ó¤è«K¡C
+ 2) ±N¦Uµ{¦¡¥H indent ±Æª©¡A¤O¨D¨t²ÎºûÅ@ªÌ¾\Ūµ{¦¡ªº«K§Q¡C
+ 3) struct °µÅܰʡA¥[¤J¤@¨Ç·sªºÄæ¦ì¡C
+ 4) ¾Ô°«/­×¦æ¥[´îÄݩʤ½¦¡¤§ÅܰʡC
+ 5) ¼¶¼g©ÇÃ~²£¥Í¾¹ªºµ{¦¡¡C
+ 6) ¼¶¼g¦a¹Ï²£¥Í¾¹ªºµ{¦¡¡C
+ 7) ­«·s³Ð³y·sªº§Þ¯à¬[ºc¡C
+ 8) ·s¼W¤@¨Ç¶Ã¼Æ¨Æ¥ó¡A¹³¬O¾Ç¨ì§Þ¯à©Î¬O¹J¨ì¯¸ªøµ¥¡C
+ 9) ·s¼W¥ô°È¬[ºc¡A¤É¯Å­n¸Ñ¥ô°È¡C
+ 10) §ïµ½ªZ¾¹¨t²Î¡AÅý¨C­Ó¤HªºªZ¾¹¦h¼Ë¤Æ¡C
+ 11) ²Î¤@©Ò¦³ªº¥Î¦r¤Îµe­±³B²z¡C
+ 12) ¥[¤J¤j¶qªºµù¸Ñ¡C
+ 13) ¤j´T«×´î¤Ö¤£¥²­nªº­«Ã¸¡C
+ 14) ¨ä¥L..
+
+ §Æ±æ³o¨Ç§V¤O¡A¯àµ¹±z±a¨Ó³\¦h«K§Q¡A¦pªG¦³¤°»ò·N¨£¡A¤]Åwªï¨Ó«H«ü±Ð¡C
+
+ ¥x«n¤@¤¤ »P«n¦@»R itoc.bbs@bbs.tnfsh.tn.edu.tw 2001.08.16
+
+#endif
+
+/* include Àɧ¡©R¦W¬° pipxxx.h C Àɧ¡©R¦W¬° pip_xxx.c */
+#include "pipglobal.h"
+#include "pipstruct.h"
+
+
+#define PIP_PICHOME "etc/game/pip/"
+
+
+#define LEARN_LEVEL ((d.happy+d.satisfy)/100) /* ¾Ç²ß®ÄªG»P§Ö¼Ö¤Îº¡¨¬¦¨¥¿¤ñ */
+
+
+/* itoc.010801: ¨q¥X³Ì«á¤G¦Cªº«ü¥O¦C */
+#define out_cmd(cmd_1,cmd_2) { move(b_lines - 1, 0); clrtoeol(); outs(cmd_1); \
+ move(b_lines, 0); clrtoeol(); outs(cmd_2); }
+
+#endif /* _PIP_H_ */
diff --git a/pip/pip_basic.c b/pip/pip_basic.c
new file mode 100644
index 0000000..dd3b51f
--- /dev/null
+++ b/pip/pip_basic.c
@@ -0,0 +1,350 @@
+/*-------------------------------------------------------*/
+/* pip_basic.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : °ò¥»¿ï³æ */
+/* create : / / */
+/* update : 01/08/14 */
+/* author : dsyan.bbs@forever.twbbs.org */
+/* modify : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+#ifdef HAVE_GAME
+
+#include "pip.h"
+
+
+/*-------------------------------------------------------*/
+/* °ò¥»¿ï³æ:Áý­¹ ²M¼ä ¿Ë¿Ë ¥ð®§ ´«Âûª÷ */
+/*-------------------------------------------------------*/
+
+
+int /* 1: ¨S¦Y­¹ª«¡A©ñ±ó 0: ¦Y¤F */
+pip_basic_feed() /* Áý­¹ */
+{
+ int ch;
+ int nodone; /* itoc.010731: °O¿ý¬O§_¦³¦æ°Ê */
+
+ nodone = 1;
+
+ do
+ {
+ out_cmd(COLOR1 " ¤@¯ë " COLOR2 " [1]¦Y¶º [2]¹s­¹ [3]®Ñ¥» [4]ª±¨ã [5]Ūª« [Q]¸õ¥X \033[m",
+ COLOR1 " ÃÄ«~ " COLOR2 " [a]¤jÁÙ [b]ÆFªÛ [c]¸É¤Y [d]¤Hçx [e]¶Â¥É [f]³·½¬ [Q]¸õ¥X \033[m");
+
+ switch (ch = vkey())
+ {
+ case '1': /* ¦Y¶º */
+ if (d.food <= 0)
+ {
+ vmsg("¨S¦³­¹ª«Åo..§Ö¥h¶R§a¡I");
+ break;
+ }
+ d.food--;
+ d.hp += 50;
+ if (d.hp > d.maxhp)
+ {
+ d.hp = d.maxhp;
+ d.weight += rand() % 2;
+ }
+ nodone = 0;
+ if ((d.bbtime / 60 / 30) < 3) /* ¥¼º¡¤T·³ */
+ show_feed_pic(11);
+ else
+ show_feed_pic(12);
+ vmsg("¨C¦Y¤@¦¸­¹ª«·|«ì´_Åé¤O50³á!");
+ break;
+
+ case '2': /* ¹s­¹ */
+ if (d.cookie <= 0)
+ {
+ vmsg("¹s­¹¦Y¥úÅo..§Ö¥h¶R§a¡I");
+ break;
+ }
+ d.cookie--;
+ d.hp += 100;
+ if (d.hp > d.maxhp)
+ {
+ d.hp = d.maxhp;
+ d.weight += rand() % 2 + 2;
+ }
+ else
+ {
+ d.weight += (rand() % 2 + 1);
+ }
+ d.happy += (rand() % 3 + 4);
+ d.satisfy += rand() % 3 + 2;
+ nodone = 0;
+ if (rand() % 2)
+ show_feed_pic(21);
+ else
+ show_feed_pic(22);
+ vmsg("¦Y¹s­¹®e©ö­D³á...");
+ break;
+
+ case '3': /* ®Ñ¥» */
+ if (d.book <= 0)
+ {
+ vmsg("¨S¦³®Ñ¥»Åo..§Ö¥h¶R§a¡I");
+ break;
+ }
+ d.book--;
+ d.happy -= 5;
+ d.wisdom+= 20;
+ d.art += 20;
+ nodone = 0;
+ show_feed_pic(31);
+ vmsg("¶}¨÷¦³¯q");
+ break;
+
+ case '4': /* ª±¨ã */
+ if (d.toy <= 0)
+ {
+ vmsg("¨S¦³ª±¨ãÅo..§Ö¥h¶R§a¡I");
+ break;
+ }
+ d.toy--;
+ d.happy += 20;
+ d.satisfy += 20;
+ nodone = 0;
+ show_feed_pic(41);
+ vmsg("ª±ª±¨ãªº¤p«Ä¤£·|ÅÜÃa");
+ break;
+
+ case '5': /* Ūª« */
+ if (d.playboy <= 0)
+ {
+ vmsg("¨S¦³½Ò¥~Ūª«Åo..§Ö¥h¶R§a¡I");
+ break;
+ }
+ if ((d.bbtime / 60 / 30) < 5)
+ {
+ /* itoc.010801: ¤­·³¥H«á¤~¯à¬Ý :p */
+ vmsg("«Ê­±¤W¼gµÛ 5 ¸T ¢æ");
+ break;
+ }
+ d.playboy--;
+ /* itoc.010801: ¼W¥[¸o´c¡A¦ý§Ö¼Ö/º¡·N¤j¶q¤W¤É */
+ d.happy = 100;
+ d.satisfy = 100;
+ d.art += 5;
+ d.sin += 30;
+ nodone = 0;
+ show_feed_pic(51);
+ vmsg("©I©I¡AÁÙ¦n¨S¤H¬Ý¨£");
+ break;
+
+ case 'a': /* ¤jÁÙ */
+ if (d.pill <= 0)
+ {
+ vmsg("¨S¦³¤jÁÙ¤¦Åo..§Ö¥h¶R§a¡I");
+ break;
+ }
+ d.pill--;
+ d.hp += 1000;
+ if (d.hp > d.maxhp)
+ d.hp = d.maxhp;
+ nodone = 0;
+ show_feed_pic(61);
+ vmsg("¨C¦Y¤@¦¸¤jÁÙ¤¦·|«ì´_Åé¤O 1000 ³á!");
+ break;
+
+ case 'b':
+ if (d.medicine <= 0)
+ {
+ vmsg("¨S¦³ÆFªÛÅo..§Ö¥h¶R§a¡I");
+ break;
+ }
+ d.medicine--;
+ d.mp += 1000;
+ if (d.mp > d.maxmp)
+ d.mp = d.maxmp;
+ nodone = 0;
+ show_feed_pic(71);
+ vmsg("¨C¦Y¤@¦¸ÆFªÛ·|«ì´_ªk¤O 1000 ³á!");
+ break;
+
+ case 'c': /* ¸É¤Y */
+ if (d.burger <= 0)
+ {
+ vmsg("¨S¦³¤j¸É¤Y¤F­C! §Ö¥h¶R§a..");
+ break;
+ }
+ d.burger--;
+ d.vp += 1000;
+ if (d.vp > d.maxvp)
+ d.vp = d.maxvp;
+ nodone = 0;
+ show_feed_pic(81);
+ vmsg("¨C¦Y¤@¦¸¸É¤Y·|«ì´_²¾°Ê 1000 ³á!");
+ break;
+
+ case 'd': /* ¤Hçx */
+ if (d.ginseng <= 0)
+ {
+ vmsg("¨S¦³¤d¦~¤Hçx­C! §Ö¥h¶R§a..");
+ break;
+ }
+ d.ginseng--;
+ d.sp += 1000;
+ if (d.sp > d.maxsp)
+ d.sp = d.maxsp;
+ nodone = 0;
+ show_feed_pic(91);
+ vmsg("¨C¦Y¤@¦¸¤Hçx·|«ì´_¤º¤O 1000 ³á!");
+ break;
+
+ case 'e': /* ¶Â¥É */
+ if (d.paste <= 0)
+ {
+ vmsg("¨S¦³¶Â¥ÉÂ_Äò»I­C! §Ö¥h¶R§a..");
+ break;
+ }
+ d.snowgrass--;
+ d.hp = d.maxhp;
+ d.tired = 0;
+ d.sick = 0;
+ nodone = 0;
+ show_feed_pic(101);
+ vmsg("¶Â¥ÉÂ_Äò»I..¶W·¥´Îªº­ò...");
+ break;
+
+ case 'f': /* ³·½¬ */
+ if (d.snowgrass <= 0)
+ {
+ vmsg("¨S¦³¤Ñ¤s³·½¬­C! §Ö¥h¶R§a..");
+ break;
+ }
+ d.snowgrass--;
+ d.hp = d.maxhp;
+ d.mp = d.maxmp;
+ d.vp = d.maxvp;
+ d.sp = d.maxsp;
+ d.tired = 0;
+ d.sick = 0;
+ nodone = 0;
+ show_feed_pic(111);
+ vmsg("¤Ñ¤s³·½¬..¶W·¥´Îªº­ò...");
+ break;
+ }
+ } while (ch != 'q' && ch != KEY_LEFT);
+
+ return nodone;
+}
+
+
+int
+pip_basic_takeshower() /* ¬~¾þ */
+{
+ d.shit -= 20;
+ if (d.shit < 0)
+ d.shit = 0;
+
+ d.hp -= rand() % 2 + 3;
+
+ switch(rand() % 3)
+ {
+ case 0:
+ show_usual_pic(1);
+ vmsg("§Ú¬O°®²bªº¤pÂû cccc....");
+ break;
+
+ case 1:
+ show_usual_pic(7);
+ vmsg("°¨±í ¶â¡ã¡ã");
+ break;
+
+ case 2:
+ show_usual_pic(2);
+ vmsg("§Ú·R¬~¾þ lalala....");
+ break;
+ }
+ return 0;
+}
+
+
+int
+pip_basic_takerest() /* ¥ð®§ */
+{
+ count_tired(5, 20, 1, 100, 0); /* «ì´_¯h³Ò */
+ d.shit++;
+ d.hp += d.maxhp / 10;
+ if (d.hp > d.maxhp)
+ d.hp = d.maxhp;
+
+ show_usual_pic(5);
+ vmsg("¦A«ö¤@¤U§Ú´N°_§ÉÅo....");
+ show_usual_pic(6);
+ vmsg("³Þ³Þ³Þ..¸Ó°_§ÉÅo......");
+ return 0;
+}
+
+
+int
+pip_basic_kiss() /* ¿Ë¿Ë */
+{
+ if (rand() % 2)
+ {
+ d.happy += rand() % 3 + 4;
+ d.satisfy += rand() % 2 + 1;
+ }
+ else
+ {
+ d.happy += rand() % 2 + 1;
+ d.satisfy += rand() % 3 + 4;
+ }
+ count_tired(1, 2, 0, 100, 1);
+ d.shit += rand() % 5 + 4;
+ d.relation += rand() % 2;
+
+ show_usual_pic(3);
+
+ if (d.shit < 60)
+ vmsg("¨Ó¹À! Ôq¤@­Ó.....");
+ else
+ vmsg("¿Ë¤Ó¦h¤]¬O·|ż¦ºªº³á....");
+
+ return 0;
+}
+
+
+int
+pip_money()
+{
+ int money;
+ char buf[80];
+
+ if (HAS_STATUS(STATUS_COINLOCK))
+ {
+ vmsg(msg_coinlock);
+ return 0;
+ }
+
+ /* itoc.µù¸Ñ: ¤§©Ò¥H¤£´£¨Ñ¤pÂû¹ô´«»È¹ôªº­ì¦]¬O¦]¬°¤pÂû¥i¥HÀx¦s/Ū¨ú¶i«× */
+
+ clrfromto(6, 18);
+ prints("±z¨­¤W¦³ %d »È¹ô,Âûª÷ %d ¤¸\n", cuser.money, d.money);
+ outs("\n¤@»È¹ô´«¤@Âûª÷­ò¡I\n");
+
+ do
+ {
+ if (!vget(10, 0, "­n´«¦h¤Ö»È¹ô¡H[Q] ", buf, 10, DOECHO))
+ return 0;
+ money = atol(buf);
+ } while (money <= 0 || money > cuser.money);
+
+ sprintf(buf, "¬O§_­nÂà´« %d »È¹ô ¬° %d Âûª÷(Y/N)¡H[N] ", money, money);
+ if (ians(11, 0, buf) == 'y')
+ {
+ cuser.money -= money;
+ d.money += money;
+ sprintf(buf, "±z¨­¤W¦³ %d ¦¸»È¹ô,Âûª÷ %d ¤¸", cuser.money, d.money);
+ vmsg(buf);
+ return 1;
+ }
+ vmsg("¨ú®ø....");
+ return 0;
+}
+#endif /* HAVE_GAME */
diff --git a/pip/pip_ending.c b/pip/pip_ending.c
new file mode 100644
index 0000000..25a981d
--- /dev/null
+++ b/pip/pip_ending.c
@@ -0,0 +1,1040 @@
+/*-------------------------------------------------------*/
+/* pip_ending.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : µ²§½¨ç¦¡ */
+/* create : / / */
+/* update : 01/08/14 */
+/* author : dsyan.bbs@forever.twbbs.org */
+/* modify : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+#ifdef HAVE_GAME
+
+#include "pip.h"
+
+
+/* ------------------------------------------------------- */
+/* µ²§½°Ñ¼Æ³]©w */
+/* ------------------------------------------------------- */
+
+
+struct endingset
+{
+ char *girl; /* ¤k¥Íµ²§½ªºÂ¾·~ */
+ char *boy; /* ¨k¥Íµ²§½ªºÂ¾·~ */
+ int grade; /* µû¤À */
+};
+typedef struct endingset endingset;
+
+
+/* ¸U¯àÃþ */
+struct endingset endmodeallpurpose[] =
+{
+ "¤k©Ê¾·~", "¨k¥Í¾·~", 0,
+ "¦¨¬°³o­Ó°ê®a·s¤k¤ý", "¦¨¬°³o­Ó°ê®a·s°ê¤ý", 500,
+ "¦¨¬°°ê®aªº®_¬Û", "¦¨¬°°ê®aªº®_¬Û", 400,
+ "¦¨¬°±Ð·|¤¤ªº¤j¥D±Ð", "¦¨¬°±Ð·|¤¤ªº±Ð©v", 350,
+ "¦¨¬°°ê®aªº¤j¦Ú", "¦¨¬°°ê®aªº¤j¦Ú", 320,
+ "¦¨¬°¤@¦ì³Õ¤h", "¦¨¬°¤@¦ì³Õ¤h", 300,
+ "¦¨¬°±Ð·|¤¤ªº­×¤k", "¦¨¬°±Ð·|¤¤ªº¯«¤÷", 150,
+ "¦¨¬°ªk®x¤Wªºªk©x", "¦¨¬°ªk®x¤Wªºªk©x", 200,
+ "¦¨¬°ª¾¦Wªº¾ÇªÌ", "¦¨¬°ª¾¦Wªº¾ÇªÌ", 120,
+ "¦¨¬°¤@¦W¤k©x", "¦¨¬°¤@¦W¨k©x", 100,
+ "¦b¨|¥®°|¤u§@", "¦b¨|¥®°|¤u§@", 100,
+ "¦b®ÈÀ]¤u§@", "¦b®ÈÀ]¤u§@", 100,
+ "¦b¹A³õ¤u§@", "¦b¹A³õ¤u§@", 100,
+ "¦bÀ\\ÆU¤u§@", "¦bÀ\\ÆU¤u§@", 100,
+ "¦b±Ð°ó¤u§@", "¦b±Ð°ó¤u§@", 100,
+ "¦b¦aÅu¤u§@", "¦b¦aÅu¤u§@", 100,
+ "¦b¥ï¤ì³õ¤u§@", "¦b¥ï¤ì³õ¤u§@", 100,
+ "¦b¬ü®e°|¤u§@", "¦b¬ü®e°|¤u§@", 100,
+ "¦b¬¼Ây°Ï¤u§@", "¦b¬¼Ây°Ï¤u§@", 100,
+ "¦b¤u¦a¤u§@", "¦b¤u¦a¤u§@", 100,
+ "¦b¹Ó¶é¤u§@", "¦b¹Ó¶é¤u§@", 100,
+ "¾á¥ô®a®x±Ð®v¤u§@", "¾á¥ô®a®x±Ð®v¤u§@", 100,
+ "¦b°s®a¤u§@", "¦b°s®a¤u§@", 100,
+ "¦b°s©±¤u§@", "¦b°s©±¤u§@", 100,
+ "¦b¤j©]Á`·|¤u§@", "¦b¤j©]Á`·|¤u§@", 100,
+ "¦b®a¤¤À°¦£", "¦b®a¤¤À°¦£", 50,
+ "¦b¨|¥®°|­Ý®t", "¦b¨|¥®°|­Ý®t", 50,
+ "¦b®ÈÀ]­Ý®t", "¦b®ÈÀ]­Ý®t", 50,
+ "¦b¹A³õ­Ý®t", "¦b¹A³õ­Ý®t", 50,
+ "¦bÀ\\ÆU­Ý®t", "¦bÀ\\ÆU­Ý®t", 50,
+ "¦b±Ð°ó­Ý®t", "¦b±Ð°ó­Ý®t", 50,
+ "¦b¦aÅu­Ý®t", "¦b¦aÅu­Ý®t", 50,
+ "¦b¥ï¤ì³õ­Ý®t", "¦b¥ï¤ì³õ­Ý®t", 50,
+ "¦b¬ü®e°|­Ý®t", "¦b¬ü®e°|­Ý®t", 50,
+ "¦b¬¼Ây°Ï­Ý®t", "¦b¬¼Ây°Ï­Ý®t", 50,
+ "¦b¤u¦a­Ý®t", "¦b¤u¦a­Ý®t", 50,
+ "¦b¹Ó¶é­Ý®t", "¦b¹Ó¶é­Ý®t", 50,
+ "¾á¥ô®a®x±Ð®v­Ý®t", "¾á¥ô®a®x±Ð®v­Ý®t", 50,
+ "¦b°s®a­Ý®t", "¦b°s®a­Ý®t", 50,
+ "¦b°s©±­Ý®t", "¦b°s©±­Ý®t", 50,
+ "¦b¤j©]Á`·|­Ý®t", "¦b¤j©]Á`·|­Ý®t", 50,
+ NULL, NULL, 0
+};
+
+
+/* ¾Ô°«Ãþ */
+struct endingset endmodecombat[] =
+{
+ "¤k©Ê¾·~", "¨k¥Í¾·~", 0,
+ "³Q«Ê¬°«iªÌ ¾Ô¤h«¬", "³Q«Ê¬°«iªÌ ¾Ô¤h«¬", 420,
+ "³Q©ÞÀ¬°¤@°êªº±N­x", "³Q©ÞÀ¬°¤@°êªº±N­x", 300,
+ "·í¤W°ê®aªñ½Ã¶¤¶¤ªø", "·í¤W°ê®aªñ½Ã¶¤¶¤ªø", 200,
+ "·í¤FªZ³N¦Ñ®v", "·í¤FªZ³N¦Ñ®v", 150,
+ "Åܦ¨ÃM¤h³ø®Ä°ê®a", "Åܦ¨ÃM¤h³ø®Ä°ê®a", 160,
+ "§ë¨­­x®È¦¨¬°¤h§L", "§ë¨­­x®È¦¨¬°¤h§L", 80,
+ "Åܦ¨¼úª÷Ây¤H", "Åܦ¨¼úª÷Ây¤H", 10,
+ "¥H¶Ä§L¤u§@ºû¥Í", "¥H¶Ä§L¤u§@ºû¥Í", 10,
+ NULL, NULL, 0
+};
+
+
+/* Å]ªkÃþ */
+struct endingset endmodemagic[] =
+{
+ "¤k©Ê¾·~", "¨k¥Í¾·~", 0,
+ "³Q«Ê¬°«iªÌ Å]ªk«¬", "³Q«Ê¬°«iªÌ Å]ªk«¬", 420,
+ "³Q¸u¬°¤ý®cÅ]ªk®v", "³Q¸u¬°¤ý©xÅ]ªk®v", 280,
+ "·í¤FÅ]ªk¦Ñ®v", "·í¤FÅ]ªk¦Ñ®v", 160,
+ "Åܦ¨¤@¦ìÅ]¾É¤h", "Åܦ¨¤@¦ìÅ]¾É¤h", 180,
+ "·í¤FÅ]ªk®v", "·í¤FÅ]ªk®v", 120,
+ "¥H¥e¤RÀ°¤Hºâ©R¬°¥Í", "¥H¥e¤RÀ°¤Hºâ©R¬°¥Í", 40,
+ "¦¨¬°¤@­ÓÅ]³N®v", "¦¨¬°¤@­ÓÅ]³N®v", 20,
+ "¦¨¬°µóÀYÃÀ¤H", "¦¨¬°µóÀYÃÀ¤H", 10,
+ NULL, NULL, 0
+};
+
+
+/* ªÀ¥æÃþ */
+struct endingset endmodesocial[] =
+{
+ "¤k©Ê¾·~", "¨k¥Í¾·~", 0,
+ "¦¨¬°°ê¤ýªºÃd¦m", "¦¨¬°¤k¤ýªº¾t°¨·Ý", 170,
+ "³Q¬D¿ï¦¨¬°¤ý¦m", "³Q¿ï¤¤·í¤k¤ýªº¤Ò´B", 260,
+ "¦¨¬°§BÀ諸¤Ò¤H", "¦¨¬°¤F¤k§BÀ諸¤Ò´B", 130,
+ "¦¨¬°´I»¨ªº©d¤l", "¦¨¬°¤k´I»¨ªº¤Ò´B", 100,
+ "¦¨¬°°Ó¤Hªº©d¤l", "¦¨¬°¤k°Ó¤Hªº¤Ò´B", 80,
+ "¦¨¬°¹A¤Hªº©d¤l", "¦¨¬°¤k¹A¤Hªº¤Ò´B", 80,
+ "¦¨¬°¦a¥Dªº±¡°ü", "¦¨¬°¤k¦a¥Dªº±¡¤Ò", -40,
+ NULL, NULL, 0
+};
+
+
+/* ÃÀ³NÃþ */
+struct endingset endmodeart[] =
+{
+ "¤k©Ê¾·~", "¨k¥Í¾·~", 0,
+ "¦¨¬°¤F¤p¤¡", "¦¨¬°¤F¤p¤¡", 100,
+ "¦¨¬°¤F§@®a", "¦¨¬°¤F§@®a", 100,
+ "¦¨¬°¤Fµe®a", "¦¨¬°¤Fµe®a", 100,
+ "¦¨¬°¤F»RÁЮa", "¦¨¬°¤F»RÁЮa", 100,
+ NULL, NULL, 0
+};
+
+
+/* ·t¶ÂÃþ */
+struct endingset endmodeblack[] =
+{
+ "¤k©Ê¾·~", "¨k¥Í¾·~", 0,
+ "Åܦ¨¤F¤kÅ]¤ý", "Åܦ¨¤F¤jÅ]¤ý", -1000,
+ "²V¦¨¤F¤Ó©f", "²V¦¨¤F¬yª]", -350,
+ "°µ¤F¢á¢Û¤k¤ýªº¤u§@", "°µ¤F¢á¢Û°ê¤ýªº¤u§@", -150,
+ "·í¤F¶Âµóªº¤j©j", "·í¤F¶Âµóªº¦Ñ¤j", -500,
+ "Åܦ¨°ª¯Å±@°ü", "Åܦ¨°ª¯Å±¡¤Ò", -350,
+ "Åܦ¨¶B´Û®v¶B´Û§O¤H", "Åܦ¨ª÷¥úÄÒÄF§O¤H¿ú", -350,
+ "¥H¬yÅaªº¤u§@¥Í¬¡", "¥H¤û­¦ªº¤u§@¥Í¬¡", -350,
+ NULL, NULL, 0
+};
+
+
+/* ®a¨ÆÃþ */
+struct endingset endmodefamily[] =
+{
+ "¤k©Ê¾·~", "¨k¥Í¾·~", 0,
+ "¥¿¦b·s®Q­×¦æ", "¥¿¦b·s­¦­×¦æ", 50,
+ "¥¿¦b®a¸Ì¶¢®Ì", "¥¿¦b®a¸Ì¶¢®Ì", 10,
+ NULL, NULL, 0
+};
+
+
+/*-------------------------------------------------------*/
+/* µ²§½¨ç¦¡ */
+/*-------------------------------------------------------*/
+
+
+/* ¤u§@³õ©Ò§PÂ_ */
+static int
+pip_max_worktime() /* workind: ¦b­þ¸Ì¤u§@³Ì¦h¦¸ */
+{
+ int workind, times; /* ´X¦¸ */
+
+ times = 20; /* ­Y¨S¦³¶W¹L 20 ¦¸ªº¤u§@³õ©Ò¡A«h¶Ç¦^ workind = 0 */
+ workind = 0;
+
+ if (d.workA > times)
+ {
+ times = d.workA;
+ workind = 1;
+ }
+ if (d.workB > times)
+ {
+ times = d.workB;
+ workind = 2;
+ }
+ if (d.workC > times)
+ {
+ times = d.workC;
+ workind = 3;
+ }
+ if (d.workD > times)
+ {
+ times = d.workD;
+ workind = 4;
+ }
+ if (d.workE > times)
+ {
+ times = d.workE;
+ workind = 5;
+ }
+ if (d.workF > times)
+ {
+ times = d.workF;
+ workind = 6;
+ }
+ if (d.workG > times)
+ {
+ times = d.workG;
+ workind = 7;
+ }
+ if (d.workH > times)
+ {
+ times = d.workH;
+ workind = 8;
+ }
+ if (d.workI > times)
+ {
+ times = d.workI;
+ workind = 9;
+ }
+ if (d.workJ > times)
+ {
+ times = d.workJ;
+ workind = 10;
+ }
+ if (d.workK > times)
+ {
+ times = d.workK;
+ workind = 11;
+ }
+ if (d.workL > times)
+ {
+ times = d.workL;
+ workind = 12;
+ }
+ if (d.workM > times)
+ {
+ times = d.workM;
+ workind = 13;
+ }
+ if (d.workN > times)
+ {
+ times = d.workN;
+ workind = 14;
+ }
+ if (d.workO > times)
+ {
+ times = d.workO;
+ workind = 16;
+ }
+ if (d.workP > times)
+ {
+ times = d.workP;
+ workind = 16;
+ }
+
+ return workind;
+}
+
+
+/* µ²§½§PÂ_ */
+static int /* return 1:·t¶Â 2:ÃÀ³N 3:¸U¯à 4:¾Ô¤h 5:Å]ªk 6:ªÀ¥æ 7:®a¨Æ */
+pip_future_decide(modeallpurpose)
+ int *modeallpurpose; /* ¦pªG¬O¸U¯àµ²§½¡A¨º»òÁÙ­n return ¬O­þ¤@Ãþªº¸U¯à */
+{
+ *modeallpurpose = 0; /* ¹w³] 0 */
+
+ /* ·t¶Â */
+ if ((d.etchics == 0 && d.sin >= 100) || (d.etchics > 0 && d.etchics < 50 && d.sin >= 250))
+ {
+ return 1;
+ }
+
+ /* ÃÀ³N */
+ if (d.art > d.hexp && d.art > d.mexp && d.art > d.hskill &&
+ d.art > d.mskill && d.art > d.social && d.art > d.family &&
+ d.art > d.homework && d.art > d.wisdom && d.art > d.charm &&
+ d.art > d.belief && d.art > d.manners && d.art > d.speech &&
+ d.art > d.cook && d.art > d.love)
+ {
+ return 2;
+ }
+
+ /* ¾Ô°« */
+ if (d.hexp >= d.social && d.hexp >= d.mexp && d.hexp >= d.family)
+ {
+ *modeallpurpose = 1;
+ if (d.hexp > d.social + 50 || d.hexp > d.mexp + 50 || d.hexp > d.family + 50)
+ return 4;
+ return 3;
+ }
+
+ /* Å]ªk */
+ if (d.mexp >= d.hexp && d.mexp >= d.social && d.mexp >= d.family)
+ {
+ *modeallpurpose = 2;
+ if (d.mexp > d.hexp || d.mexp > d.social || d.mexp > d.family)
+ return 5;
+ return 3;
+ }
+
+ /* ªÀ¥æ */
+ if (d.social >= d.hexp && d.social >= d.mexp && d.social >= d.family)
+ {
+ *modeallpurpose = 3;
+ if (d.social > d.hexp + 50 || d.social > d.mexp + 50 || d.social > d.family + 50)
+ return 6;
+ return 3;
+ }
+
+ /* ®a¨Æ */
+ *modeallpurpose = 4;
+ if (d.family > d.hexp + 50 || d.family > d.mexp + 50 || d.family > d.social + 50)
+ return 7;
+ return 3;
+}
+
+
+/* µ²±Bªº§PÂ_ */
+static int /* return grade */
+pip_marry_decide()
+{
+ if (d.lover) /* °Ó¤H */
+ {
+ /* d.lover = 3 4 5 6 7:°Ó¤H */
+ return 80;
+ }
+
+ if (d.royalJ >= d.relation)
+ {
+ if (d.royalJ >= 100)
+ {
+ d.lover = 1; /* ¤ý¤l */
+ return 200;
+ }
+ }
+ else
+ {
+ if (d.relation >= 100)
+ {
+ d.lover = 2; /* ¤÷¿Ë©Î¥À¿Ë */
+ return 0;
+ }
+ }
+
+ /* d.lover = 0; */ /* ³æ¨­ */
+ return 40;
+}
+
+
+static int
+pip_endwith_black(buf) /* ·t¶Â */
+ char *buf;
+{
+ int m;
+
+ if (d.sin > 500 && d.mexp > 500) /* Å]¤ý */
+ m = 1;
+ else if (d.hexp > 600) /* ¬yª] */
+ m = 2;
+ else if (d.speech > 100 && d.art >= 80) /* SM */
+ m = 3;
+ else if (d.hexp > 320 && d.character > 200 && d.charm < 200) /* ¶Âµó¦Ñ¤j */
+ m = 4;
+ else if (d.character > 200 && d.charm > 200 && d.speech > 70 && d.toman > 70) /* °ª¯Å±@°ü */
+ m = 5;
+ else if (d.wisdom > 450) /* ¶BÄF®v */
+ m = 6;
+ else /* ¬yÅa */
+ m = 7;
+
+ if (d.sex == 1)
+ strcpy(buf, endmodeblack[m].boy);
+ else
+ strcpy(buf, endmodeblack[m].girl);
+
+ return endmodeblack[m].grade;
+}
+
+
+static int
+pip_endwith_social(buf) /* ªÀ¥æ */
+ char *buf;
+{
+ int m;
+
+ if (d.social > 600)
+ m = (d.charm > 500) ? 1 : 2;
+ else if (d.social > 450)
+ m = 1;
+ else if (d.social > 380)
+ m = (d.character > d.charm) ? 3 : 4;
+ else if (d.social > 250)
+ m = (d.wisdom > d.affect) ? 5 : 6;
+ else
+ m = 7;
+
+ d.lover = 10;
+
+ if (d.sex == 1)
+ strcpy(buf, endmodesocial[m].boy);
+ else
+ strcpy(buf, endmodesocial[m].girl);
+
+ return endmodesocial[m].grade;
+}
+
+
+static int
+pip_endwith_magic(buf) /* Å]ªk */
+ char *buf;
+{
+ int m;
+
+ if (d.mexp > 800)
+ m = (d.affect > d.wisdom && d.affect > d.belief && d.etchics > 100) ? 1 : 2;
+ else if (d.mexp > 600)
+ m = (d.speech >= 350) ? 3 : 4;
+ else if (d.mexp > 500)
+ m = 5;
+ else if (d.mexp > 300)
+ m = 6;
+ else
+ m = (d.character > 200) ? 7 : 8;
+
+ if (d.sex == 1)
+ strcpy(buf, endmodemagic[m].boy);
+ else
+ strcpy(buf, endmodemagic[m].girl);
+
+ return endmodemagic[m].grade;
+}
+
+
+static int
+pip_endwith_combat(buf) /* ¾Ô°« */
+ char *buf;
+{
+ int m;
+
+ if (d.hexp > 1500)
+ m = (d.affect > d.wisdom && d.affect > d.belief && d.etchics > 100) ? 1 : 2;
+ else if (d.hexp > 1000)
+ m = (d.character > 300 && d.etchics > 50) ? 3 : 4;
+ else if (d.hexp > 800)
+ m = (d.vp > 500) ? 5 : 6;
+ else
+ m = (d.attack > 200) ? 7 : 8;
+
+ if (d.sex == 1)
+ strcpy(buf, endmodecombat[m].boy);
+ else
+ strcpy(buf, endmodecombat[m].girl);
+
+ return endmodecombat[m].grade;
+}
+
+
+static int
+pip_endwith_family(buf) /* ®a¨Æ */
+ char *buf;
+{
+ int m;
+
+ if (d.relation < 50)
+ m = 1;
+ else
+ m = 2;
+
+ if (d.sex == 1)
+ strcpy(buf, endmodefamily[m].boy);
+ else
+ strcpy(buf, endmodefamily[m].girl);
+
+ return endmodefamily[m].grade;
+}
+
+
+static int
+pip_endwith_allpurpose(buf, mode) /* ¸U¯à */
+ char *buf;
+ int mode;
+{
+ int m;
+ int point, workind;
+
+ /* ¨Ì¬O­þ¤@Ãþªº¸U¯à¡A¨Ó¨M©w point ÂI¼Æ */
+
+ if (mode == 1)
+ point = d.hexp;
+ else if (mode == 2)
+ point = d.mexp;
+ else if (mode == 3)
+ point = d.social;
+ else if (mode == 4)
+ point = d.family;
+
+ if (point > 1000)
+ {
+ m = (d.character > 1000) ? 1 : 2;
+ }
+ else if (point > 800)
+ {
+ m = (d.belief > d.etchics && d.belief > d.wisdom) ? 3 :
+ (d.etchics > d.belief && d.etchics > d.wisdom) ? 4 : 5;
+ }
+ else if (point > 500)
+ {
+ m = (d.belief > d.etchics && d.belief > d.wisdom) ? 6 :
+ (d.etchics > d.belief && d.etchics > d.wisdom) ? 7 : 8;
+ }
+ else if (point > 300)
+ {
+ workind = pip_max_worktime();
+ m = (workind < 2) ? 9 : 8 + workind;
+ }
+ else
+ {
+ workind = pip_max_worktime();
+ m = (workind < 2) ? 25 : 24 + workind;
+ }
+
+ if (d.sex == 1)
+ strcpy(buf, endmodeallpurpose[m].boy);
+ else
+ strcpy(buf, endmodeallpurpose[m].girl);
+
+ return endmodeallpurpose[m].grade;
+}
+
+
+static int
+pip_endwith_art(buf) /* ÃÀ³N */
+ char *buf;
+{
+ int m;
+
+ if (d.speech > 100)
+ m = 1;
+ else if (d.wisdom > 400)
+ m = 2;
+ else if (d.classI > d.classJ)
+ m = 3;
+ else
+ m = 4;
+
+ if (d.sex == 1)
+ strcpy(buf, endmodeart[m].boy);
+ else
+ strcpy(buf, endmodeart[m].girl);
+
+ return endmodeart[m].grade;
+}
+
+
+/* ------------------------------------------------------- */
+/* µ²§½¨M©w */
+/* ------------------------------------------------------- */
+
+static void
+pip_endwith_decide(endbuf1, endbuf2, endmode, endgrade)
+ char *endbuf1, *endbuf2;
+ int *endmode, *endgrade;
+{
+ char *name[8][2] =
+ {
+ {"¨kªº", "¤kªº"},
+ {"¶ùµ¹¤ý¤l", "°ù¤F¤½¥D"},
+ {"¶ùµ¹±z", "°ù¤F±z"},
+ {"¶ùµ¹°Ó¤H¢Ï", "°ù¤F¤k°Ó¤H¢Ï"},
+ {"¶ùµ¹°Ó¤H¢Ð", "°ù¤F¤k°Ó¤H¢Ð"},
+ {"¶ùµ¹°Ó¤H¢Ñ", "°ù¤F¤k°Ó¤H¢Ñ"},
+ {"¶ùµ¹°Ó¤H¢Ò", "°ù¤F¤k°Ó¤H¢Ò"},
+ {"¶ùµ¹°Ó¤H¢Ó", "°ù¤F¤k°Ó¤H¢Ó"}
+ };
+
+ int modeallpurpose;
+
+ /* ³B²z endbuf1 */
+ *endmode = pip_future_decide(&modeallpurpose);
+ switch (*endmode)
+ {
+ /* 1:·t¶Â 2:ÃÀ³N 3:¸U¯à 4:¾Ô¤h 5:Å]ªk 6:ªÀ¥æ 7:®a¨Æ */
+ case 1:
+ *endgrade = pip_endwith_black(endbuf1);
+ break;
+ case 2:
+ *endgrade = pip_endwith_art(endbuf1);
+ break;
+ case 3:
+ *endgrade = pip_endwith_allpurpose(endbuf1, modeallpurpose);
+ break;
+ case 4:
+ *endgrade = pip_endwith_combat(endbuf1);
+ break;
+ case 5:
+ *endgrade = pip_endwith_magic(endbuf1);
+ break;
+ case 6:
+ *endgrade = pip_endwith_social(endbuf1);
+ break;
+ case 7:
+ *endgrade = pip_endwith_family(endbuf1);
+ break;
+ }
+
+ *endgrade += pip_marry_decide();
+
+ /* ³B²z endbuf2 */
+ if (d.lover >= 1 && d.lover <= 7)
+ {
+ if (d.sex == 1)
+ strcpy(endbuf2, name[d.lover][1]);
+ else
+ strcpy(endbuf2, name[d.lover][0]);
+ }
+ else if (d.lover == 10) /* ªÀ¥æÃþªºÂ¾·~¦P®É¤]¬O±B«Ãª¬ªp */
+ {
+ strcpy(endbuf2, endbuf1);
+ }
+ else /* if (d.lover == 0) */
+ {
+ if (d.sex == 1)
+ strcpy(endbuf2, "°ù¤F¦P¦æªº¤k«Ä");
+ else
+ strcpy(endbuf2, "¶ùµ¹¤F¦P¦æªº¨k¥Í");
+ }
+}
+
+
+static void
+pip_ending_grade(endgrade)
+ int endgrade;
+{
+ clrfromto(1, 23);
+ move(8, 17);
+ outs("\033[1;36m·PÁ±zª±§¹¾ã­Ó" BBSNAME "¤pÂûªº¹CÀ¸\033[m");
+ move(10, 17);
+ outs("\033[1;37m¸g¹L¨t²Î­pºâªºµ²ªG¡G\033[m");
+ move(12, 17);
+ prints("\033[1;36m±zªº¤pÂû\033[37m%s\033[36mÁ`±o¤À¡×\033[1;5;33m%d\033[m", d.name, endgrade);
+}
+
+
+int
+pip_ending_screen() /* µ²§½µe­± */
+{
+ char endbuf1[50], endbuf2[50];
+ int endgrade = 0;
+ int endmode = 0;
+
+ pip_endwith_decide(endbuf1, endbuf2, &endmode, &endgrade);
+
+ clear();
+ move(1, 0);
+ outs(" \033[1;33mùÝùùùùùùùßùÝùùùù¢¡ùßùÝùùùùùù¢¡ùÝùùùùùùùßùÝùùùù¢¡ùߢ~ùùùùùù¢¡\033[m\n");
+ outs(" \033[1;37mùø ùøùø ùøùøùø ùøùø ùøùø ùøùøùø ùø\033[m\n");
+ outs(" \033[1;33mùø ùùùâùø ùøùøùø ¢~¢¡ùøùãùùùßùÝùåùø ùøùøùø ùÝùùùß\033[m\n");
+ outs(" \033[1;32mùø ùùùâùø ùø ùøùø ¢¢¢£ùøùÝùùùåùãùßùø ùø ùøùø ¢¢¢£ùø\033[m\n");
+ outs(" \033[1;37mùø ùøùø ùø ùøùø ùøùø ùøùø ùø ùøùø ùø\033[m\n");
+ outs(" \033[1;35mùãùùùùùùùåùãùù¢¢ùùùåùãùùùùùù¢£ùãùùùùùùùåùãùù¢¢ùùù墢ùùùùùù¢£\033[m\n");
+ outs(" \033[1;31m¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w\033[41;37m " BBSNAME PIPNAME "µ²§½³ø§i\033[0;1;31m¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w\033[m\n");
+ outs(" \033[1;36m ³o­Ó®É¶¡¤£ª¾¤£Ä±¦aÁÙ¬O¨ìÁ{¤F..\033[m\n");
+ prints(" \033[1;37m \033[33m%s\033[37m ±oÂ÷¶}±zªº·Å·xÃh©ê¡A¦Û¤v¤@°¦Âû¦b¥~­±¨D¥Í¦s¤F..\033[m\n", d.name);
+ outs(" \033[1;36m ¦b±z·ÓÅU±Ð¾É¥Lªº³o¬q®É¥ú¡AÅý¥L±µÄ²¤F«Ü¦h»â°ì¡A°ö¾i¤F«Ü¦hªº¯à¤O..\033[m\n");
+ prints(" \033[1;37m ¦]¬°³o¨Ç¡AÅý¤pÂû \033[33m%s\033[37m ¤§«áªº¥Í¬¡¡AÅܱo§ó¦hªö¦h«º¤F..\033[m\n", d.name);
+ outs(" \033[1;36m ¹ï©ó±zªºÃö¤ß¡A±zªº¥I¥X¡A±z©Ò¦³ªº·R..\033[m\n");
+ prints(" \033[1;37m \033[33m%s\033[37m ·|¥Ã»·³£»Ê°O¦b¤ßªº..\033[m", d.name);
+ vmsg("±µ¤U¨Ó¬Ý¥¼¨Óµo®i");
+
+ clrfromto(7, 19);
+ outs(" \033[1;34m¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w\033[44;37m " BBSNAME PIPNAME "¥¼¨Óµo®i\033[0;1;34m¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w\033[m\n");
+ prints(" \033[1;36m ³z¹L¤ô´¹²y¡AÅý§Ú­Ì¤@°_¨Ó¬Ý \033[33m%s\033[36m ªº¥¼¨Óµo®i§a..\033[m\n", d.name);
+ prints(" \033[1;37m¤pÂû \033[33m%s\033[37m «á¨Ó%s..\033[m\n", d.name, endbuf1);
+ prints(" \033[1;37m¦Ü©ó¤pÂûªº±B«Ãª¬ªp¡A¥L«á¨Ó%s¡A±B«Ãºâ¬O«Ü¬üº¡..\033[m\n", endbuf2);
+ outs(" \033[1;36m¶â..³o¬O¤@­Ó¤£¿ùªºµ²§½­ò..\033[m");
+ vmsg("§Ú·Q±z¤@©w«Ü·P°Ê§a");
+
+ show_ending_pic(0);
+ vmsg("¬Ý¤@¬Ý¤À¼ÆÅo");
+
+ pip_ending_grade(endgrade);
+ vmsg("¤U¤@­¶¬O¤pÂû¸ê®Æ¡A»°§Ö copy ¤U¨Ó°µ¬ö©À");
+
+ pip_query_self();
+ vmsg("Åwªï¦A¨Ó¬D¾Ô..");
+
+ pipdie("\033[1;31m¹CÀ¸µ²§ôÅo..\033[m ", 3);
+ return 0;
+}
+
+
+/* ------------------------------------------------------- */
+/* ÀH¾÷¾÷½t */
+/* ------------------------------------------------------- */
+
+
+int /* 1:±µ¨ü¨D±B 0:©Úµ´¨D±B */
+pip_marriage_offer() /* ¨D±B */
+{
+ char buf[128];
+ int money, who;
+ char *name[5][2] = {{"¤k°Ó¤H¢Ï", "°Ó¤H¢Ï"}, {"¤k°Ó¤H¢Ð", "°Ó¤H¢Ð"}, {"¤k°Ó¤H¢Ñ", "°Ó¤H¢Ñ"}, {"¤k°Ó¤H¢Ò", "°Ó¤H¢Ò"}, {"¤k°Ó¤H¢Ó", "°Ó¤H¢Ó"}};
+
+ do
+ {
+ who = rand() % 5;
+ } while (d.lover == (who + 3)); /* ¨Ó¨D±BªÌ­n¤£¬O²{¦bªº¥¼±B©d */
+
+ money = rand() % 5000 + 4000;
+ sprintf(buf, " %s±a¨Ó¤Fª÷¿ú %d¡A­n¦V±zªº¤pÂû¨D±B¡A±zÄ@·N¶Ü(Y/N)¡H[N] ", name[who][d.sex - 1], money);
+ if (ians(b_lines - 1, 0, buf) == 'y')
+ {
+ if (d.wantend != 1 && d.wantend != 4)
+ {
+ sprintf(buf, " £«¡ã¤§«e¤w¸g¦³±B¬ù¤F¡A±z½T©w­n¸Ñ°£Â±B¬ù¡A§ï¥ß±B¬ù¶Ü(Y/N)¡H[N] ");
+ if (ians(b_lines - 1, 0, buf) != 'y')
+ {
+ d.social += 10; /* ºû«ù±B¬ù¥[ªÀ¥æ */
+ vmsg("ÁÙ¬Oºû«ù±B¬ù¦n¤F..");
+ return 0;
+ }
+ d.social -= rand() % 50 + 100; /* ¤ù­±·´±ó±B¬ù­°ªÀ¥æ */
+ }
+ d.charm -= rand() % 5 + 20;
+ d.lover = who + 3;
+ d.relation -= 20;
+ if (d.relation < 0)
+ d.relation = 0;
+ if (d.wantend < 4)
+ d.wantend = 2;
+ else
+ d.wantend = 5;
+ vmsg("§Ú·Q¹ï¤è¬O¤@­Ó«Ü¦nªº¦ñ«Q..");
+ d.money += money;
+ return 1;
+ }
+ else
+ {
+ d.charm += rand() % 5 + 20;
+ d.relation += 20;
+ if (d.wantend == 1 || d.wantend == 4)
+ vmsg("§ÚÁÙ¦~»´..¤ß±¡ÁÙ¤£©w..");
+ else
+ vmsg("§Ú¦­¤w¦³±B¬ù¤F..¹ï¤£°_..");
+ return 0;
+ }
+}
+
+
+int /* 1:³Q¥e¤R 0:©ñ±ó©Î¨S¿ú */
+pip_meet_divine() /* ¥e¤R®v¨Ó³X */
+{
+ char buf[80];
+ int money;
+
+ clrfromto(6, 17);
+ move(7, 14);
+ outs("\033[1;33;5m¥n¥n¥n..\033[0;1;37m¬ðµM¶Ç¨Ó°}°}ªººVªùÁn..\033[m");
+ move(9, 14);
+ outs("\033[1;37;46m ­ì¨Ó¬O¶³¹C¥|®üªº¥e¤R®v¨Ó³X¤F....... \033[m");
+ vmsg("¶}ªùÅý¥L¶i¨Ó§a..");
+
+ money = 300 * (d.bbtime / 60 / 30 + 1); /* ¦~¬ö¶V¤j¡A­nªáªº¿ú¶V¦h */
+ if (d.money >= money)
+ {
+ sprintf(buf, "±z­nªá %d ¤¸¥e¤R¶Ü(Y/N)¡H[N] ", money);
+ if (ians(11, 14, buf) == 'y')
+ {
+ sprintf(buf, "±zªº¤pÂû%s¥H«á¥i¯àªº¨­¤À¬O", d.name);
+ switch (rand() % 4) /* ÀH«K§Ë¤@ºØ */
+ {
+ /* ¥H¤U©Ò strcat ªº end message ¬O¨k¤k¬Û¦Pªº */
+ case 0:
+ strcat(buf, endmodemagic[2 + rand() % 5].girl);
+ break;
+ case 1:
+ strcat(buf, endmodecombat[2 + rand() % 6].girl);
+ break;
+ case 2:
+ strcat(buf, endmodeart[2 + rand() % 6].girl);
+ break;
+ case 3:
+ strcat(buf, endmodeallpurpose[6 + rand() % 15].girl);
+ break;
+ }
+ d.money -= money;
+
+ move(13, 14);
+ outs("\033[1;33m¦b§Ú¥e¤Rµ²ªG¬Ý¨Ó..\033[m");
+ move(15, 14);
+ outs(buf);
+ vmsg("ÁÂÁ´fÅU¡A¦³½t¦A¨£­±¤F¡A¤£·Ç¤£¯à©Ç§Ú³á");
+ return 1;
+ }
+ else
+ {
+ vmsg("±z¤£·Q¥e¤R°Ú¡H..¯u¥i±¤..¨º¥u¦³µ¥¤U¦¸§a..");
+ }
+ }
+ else
+ {
+ vmsg("±zªº¿ú¤£°÷³á..¯u¬O¥i±¤..µ¥¤U¦¸§a..");
+ }
+ return 0;
+}
+
+
+int
+pip_meet_sysop() /* itoc.000416: ¹J¤W¯¸ªø¤j¤j */
+{
+ char msg[5][40] =
+ {
+ "¬ðµM¤ô­±¾_°Ê¤F°_¨Ó..", "¤@°}­»®ðÀH­·¦Ó¦Ü..",
+ "³ç..¦³¤H©ç©ç±zªºªÓ»H..", "©P³ò¬ðµM¼ö¾x¤F°_¨Ó..",
+ "»·»·¦a¬Ý¨ì¤@­Ó¤H¼v.."
+ };
+
+ clrfromto(6, 17);
+ move(7, 14);
+ outs(msg[rand() % 5]); /* ¶Ã¼Æ¨M©w¥X³õ±Ô­z */
+ vmsg("­ì¨Ó¬O»®»®¦³¦Wªº" SYSOPNICK "¥X²{¤F");
+ move(9, 14);
+
+ switch (rand() % 4) /* ÀH¾÷¨M©w¯¸ªø¨Æ¥ó (¥H¤£¦P¶Ã¼Æ¼W¥[½ì¨ý) */
+ {
+ case 0:
+ if (d.weight > 50)
+ {
+ d.weight -= 20;
+ outs("±z¤Ó­D¤F¡A§Ú¬°±z¶i¦æ©â¯×¤â³N..");
+ }
+ else
+ {
+ d.weight += 20;
+ outs("±z¤Ó½G¤F¡A§Ú¬°±z¶i¦æ¼WªÎ¤â³N..");
+ }
+ break;
+
+ case 1:
+ d.money += 1000;
+ outs("¯¸ªø¤µ¤Ñ¤ß±¡¦n¡A°e±z¤@¨ÇÂûª÷·í¦n®Æªº..");
+ break;
+
+ case 2:
+ d.hp = d.maxhp;
+ d.mp = d.maxmp;
+ d.vp = d.maxvp;
+ d.sp = d.maxsp;
+ outs(SYSOPNICK "°e±z¤@Áû¤d¦~¯QÂû¤Y¡A±zªºÅé¤O§¹¥þ«ì´_..");
+ break;
+
+ case 3:
+ if (d.money >= 100000)
+ {
+ if (ians(9, 14, "±z­nªá 100000 ¤¸±µ¨ü±K©v³e³»¤jªk¶Ü(Y/N)¡H[N] ") == 'y')
+ {
+ /* ÄݩʤW¤É 5% */
+ d.money -= 100000;
+ d.maxhp = d.maxhp * 105 / 100;
+ d.maxmp = d.maxmp * 105 / 100;
+ d.maxsp = d.maxsp * 105 / 100;
+ d.maxsp = d.maxsp * 105 / 100;
+ d.attack = d.attack * 105 / 100;
+ d.resist = d.resist * 105 / 100;
+ d.speed = d.speed * 105 / 100;
+ d.character = d.character * 105 / 100;
+ d.love = d.love * 105 / 100;
+ d.wisdom = d.wisdom * 105 / 100;
+ d.art = d.art * 105 / 100;
+ d.brave = d.brave * 105 / 100;
+ d.homework = d.homework * 105 / 100;
+ move(11, 14);
+ outs("¸g¹L¤j¯«ªº³e³»¤§«á¡A±zµo²{©Ò¦³¯à¤O³£¤Wª@¤F..");
+ }
+ else
+ {
+ move(11, 14);
+ outs("±z¤£·Q­n°Ú¡H..¯u¥i±¤¡A¨º¥u¦³µ¥¤U¦¸§a..");
+ }
+ }
+ else
+ {
+ outs("¤j¯«Ä±±o©M±z¨S¦³½t¥÷..");
+ }
+ break;
+ }
+
+ vmsg("¤@Â಴¡A" SYSOPNICK "´N¤£¨£¤F..");
+ return 0;
+}
+
+
+int
+pip_meet_smith() /* itoc.021101: ¹J¤WÅK¦K */
+{
+ int randnum;
+ char *equip;
+
+ clrfromto(6, 17);
+ move(7, 14);
+ outs("¤p¥ë¤l­n¥h­þ¨à°Ú¡H");
+ vmsg("¦³¤H¦b­I«á¥s¤F±z¤@Án¡A­ì¨ÓÅK¦K¥ý¥Í");
+ move(9, 14);
+ outs("±z¥Îªº¬O¤°»òÄê¸Ë³Æ¡AÅý§Ú°e±z¤@¥ó·sªº§a");
+
+ randnum = rand();
+
+ switch (randnum % 5)
+ {
+ case 0: /* ÀY³¡ªZ¾¹ */
+ randnum = randnum % 10;
+ d.weaponhead += randnum;
+ pip_weapon_wear(0, randnum);
+ equip = d.equiphead;
+ if (!*equip)
+ strcpy(equip, "ÅK¦Kªº¿Oªw");
+ break;
+
+ case 1: /* ¤â³¡ªZ¾¹ */
+ randnum = randnum % 10;
+ d.weaponhand += randnum;
+ pip_weapon_wear(1, randnum);
+ equip = d.equiphand;
+ if (!*equip)
+ strcpy(equip, "ÅK¦Kªº®±®M");
+ break;
+
+ case 2: /* ¬ÞµPªZ¾¹ */
+ randnum = randnum % 10;
+ d.weaponshield += randnum;
+ pip_weapon_wear(2, randnum);
+ equip = d.equipshield;
+ if (!*equip)
+ strcpy(equip, "ÅK¦Kªº¬Þ¥Ò");
+ break;
+
+ case 3: /* ¨­ÅéªZ¾¹ */
+ randnum = randnum % 10;
+ d.weaponbody += randnum;
+ pip_weapon_wear(3, randnum);
+ equip = d.equipbody;
+ if (!*equip)
+ strcpy(equip, "ÅK¦Kªº¹D³T");
+ break;
+
+ case 4: /* ¸}³¡ªZ¾¹ */
+ randnum = randnum % 10;
+ d.weaponfoot += randnum;
+ pip_weapon_wear(4, randnum);
+ equip = d.equipfoot;
+ if (!*equip)
+ strcpy(equip, "ÅK¦KªºÅ@×H");
+ break;
+ }
+
+ while (!vget(b_lines, 0, "½Ð¬°¸Ë³Æ¨ú­Ó·s¦W¦r¡G", equip, 11, GCARRY))
+ ;
+
+ vmsg("ÅK¦K©ç©ç±zªºªÓ»H¡A²H²HÂ÷¶}");
+ return 0;
+}
+
+
+int
+pip_meet_angel() /* itoc.010814: ¹J¨ì¤Ñ¨Ï */
+{
+ clear();
+ show_system_pic(0);
+ move(17, 10);
+ prints("\033[1;36m¿Ë·Rªº\033[1;33m%s ¡ã\033[m", d.name);
+ move(18, 10);
+ outs("\033[1;37m¬Ý¨ì±z³o¼Ë§V¤Oªº°ö¾i¦Û¤vªº¯à¤O Åý§Ú¤ß¤¤¤Q¤Àªº°ª¿³³á..\033[m");
+ move(19, 10);
+ outs("\033[1;36m¤p¤Ñ¨Ï§Ú¨M©wµ¹±z¼ú½à¹ªÀy¹ªÀy °½°½¦aÀ°§U±z¤@¤U..^_^\033[m");
+ move(20, 10);
+
+ switch (rand() % 8)
+ {
+ case 1:
+ outs("\033[1;33m§Ú±NÀ°±zªº¦U¶µ¯à¤O¥þ³¡´£¤É¦Ê¤À¤§¤­³á..\033[m");
+ d.maxhp = d.maxhp * 105 / 100;
+ d.hp = d.maxhp;
+ d.maxmp = d.maxmp * 105 / 100;
+ d.mp = d.maxmp;
+ d.maxvp = d.maxvp * 105 / 100;
+ d.vp = d.maxvp;
+ d.maxsp = d.maxsp * 105 / 100;
+ d.sp = d.maxsp;
+ d.attack = d.attack * 105 / 100;
+ d.resist = d.resist * 105 / 100;
+ d.speed = d.speed * 105 / 100;
+ d.character = d.character * 105 / 100;
+ d.love = d.love * 105 / 100;
+ d.wisdom = d.wisdom * 105 / 100;
+ d.art = d.art * 105 / 100;
+ d.brave = d.brave * 105 / 100;
+ d.homework = d.homework * 105 / 100;
+ break;
+
+ case 2:
+ case 3:
+ outs("\033[1;33m§Ú±NÀ°±zªº¾Ô°«¯à¤O¥þ³¡´£¤É¦Ê¤À¤§¤Q³á..\033[m");
+ d.attack = d.attack * 110 / 100;
+ d.resist = d.resist * 110 / 100;
+ d.speed = d.speed * 110 / 100;
+ d.brave = d.brave * 110 / 100;
+ break;
+
+ case 4:
+ case 5:
+ outs("\033[1;33m§Ú±NÀ°±zªº¥Í©R¡Bªk¤O¡B²¾°Ê¡B¤º¤O¥þ³¡´£¤É¦Ê¤À¤§¤K³á..\033[m");
+ d.maxhp = d.maxhp * 108 / 100;
+ d.hp = d.maxhp;
+ d.maxmp = d.maxmp * 108 / 100;
+ d.mp = d.maxmp;
+ d.maxvp = d.maxvp * 108 / 100;
+ d.vp = d.maxvp;
+ d.maxsp = d.maxsp * 108 / 100;
+ d.sp = d.maxsp;
+ break;
+
+ case 6:
+ case 7:
+ outs("\033[1;33m§Ú±NÀ°±zªº·P¨ü¯à¤O¥þ³¡´£¤É¦Ê¤À¤§¤G¤Q³á..\033[m");
+ d.character = d.character * 120 / 100;
+ d.love = d.love * 120 / 100;
+ d.wisdom = d.wisdom * 120 / 100;
+ d.art = d.art * 120 / 100;
+ d.homework = d.homework * 120 / 100;
+ break;
+ }
+
+ vmsg("½ÐÄ~Äò¥[ªo³á..");
+ return 0;
+}
+#endif /* HAVE_GAME */
diff --git a/pip/pip_fight.c b/pip/pip_fight.c
new file mode 100644
index 0000000..b19d311
--- /dev/null
+++ b/pip/pip_fight.c
@@ -0,0 +1,1888 @@
+/* ----------------------------------------------------- */
+/* pip_fight.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/* ----------------------------------------------------- */
+/* target : ¾Ô°«¿ï³æ */
+/* create : / / */
+/* update : 01/08/14 */
+/* author : dsyan.bbs@forever.twbbs.org */
+/* modify : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/* ----------------------------------------------------- */
+
+
+#include "bbs.h"
+
+#ifdef HAVE_GAME
+
+#include "pip.h"
+
+
+/*-------------------------------------------------------*/
+/* ¤É¯Å¨ç¦¡ */
+/*-------------------------------------------------------*/
+
+
+void
+pip_levelup(success)
+ int success; /* 1:¦¨¥\¸Ñ¨M¤É¯Å¥ô°È 0:¥¢±Ñ */
+{
+ int level;
+
+ d.quest = 0;
+
+ level = d.level;
+ d.exp -= level * 100;
+ d.level = ++level;
+
+ /* itoc.010730: ¬°¤F¼W¥[¾Ô°«ªº¥²­n©Ê¡Amaxhp maxmp maxvp maxsp
+ ³o¨ÇÄÝ©ÊÀ³¸Ó¥u¦b exp ¼W¥[¤É¯Å«á¡A¤~¯à¤j¶q¼W¥[ */
+
+ /* itoc.010730: ¦b³]©w©Çª«®É½Ðª`·N¡G¦b¨C¦¸³£¦¨¥\¸Ñ¨M¤É¯Å¥ô°È¤§¤U
+ level ¬O n ¯Åªº¤pÂû¡A¨ä maxhp/maxmp/maxvp/maxsp ´Á±æ­È¬ù¬O 0.75*(n^2) */
+
+ d.maxhp += rand() % level;
+ d.maxmp += rand() % level;
+ d.maxvp += rand() % level;
+ d.maxsp += rand() % level;
+
+ if (success) /* ¦pªG¦¨¥\¸Ñ¨M¤É¯Å¥ô°È¡A¦å¥[¤ñ¸û¦h */
+ {
+ d.maxhp += level;
+ d.maxmp += level;
+ d.maxvp += level;
+ d.maxsp += level;
+ vmsg("¥ô°È§¹¦¨¡Aµ¥¯Å´£¤É¤F");
+ }
+ else
+ {
+ vmsg("¥ô°È¥¢±Ñ¡Aµ¥¯Å´£¤É¤F");
+ }
+
+ /* ¤É¯Å«á¸Éº¡¦å */
+ d.hp = d.maxhp;
+ d.mp = d.maxmp;
+ d.vp = d.maxvp;
+ d.sp = d.maxsp;
+}
+
+
+/* itoc.010731: Àˬd¸gÅç­È¬O§_¤w¸g¹F¤É¯Å¼Ð·Ç */
+static void
+pip_check_levelup()
+{
+ /* itoc.010731: ¨C¤É¤@¯Å­n (µ¥¯Å * 100) ªº¸gÅç­È */
+ /* µ¥¯Å n ©Çª«ªº exp = n*5 (­ì«h¤W¥´20°¦©Çª«¤É¤@¯Å) */
+
+ /* itoc.020114: ­­¨îµ¥¯Å¤W­­¡A¦]¬°©~µM¦³¤Hª±¨ì¼Æ¦Ê¸U¯Å¡A¹CÀ¸ªº³]­p³£³Q¯}Ãa¤F */
+
+ if ((d.level < 100) && (d.exp >= d.level * 100))
+ {
+ /* itoc.021031: ¹F¤É¯Å¼Ð·Ç®É·|¨ú±o¤@­Ó¤É¯Å¥ô°È¡A¸Ñ§¹¥ô°È¤~¯à¤É¯Å */
+ if (d.quest)
+ vmsg("¤w¹F¤É¯Å¼Ð·Ç¡A¦ý±z¥²¶·°õ¦æ©Î©ñ±ó¥ô°È¤~¯à¤É¯Å");
+ else
+ pip_quest_new();
+ }
+}
+
+
+/*-------------------------------------------------------*/
+/* ¾Ô°«¯S°Ï */
+/*-------------------------------------------------------*/
+
+
+/* itoc.010731.µù¸Ñ: ¥H¤U©Ò¦³Ãþ¦ü value * (110 - rand() % 20) / 100; ªºªF¦è
+ ´N¬O½d³ò¦b value ªº 90% ~ 110%¡A¨ä´Á±æ­È¬° value */
+
+/* itoc.010731: ¥[±j¨¾¿m */
+/* resistmore = 40 ªí¥Ü¹ï¤è§ðÀ»Åܬ° 60%¡Aresistmore = -20 ªí¥Ü¹ï¤è§ðÀ»Åܬ° 120% */
+
+static int d_resistmore; /* ¤pÂû¥[±j¨¾¿m */
+static int m_resistmore; /* ©Çª«¥[±j¨¾¿m */
+
+static int d_nodone; /* 1:¤pÂûÁÙ¨S°Ê§@ 0:¤pÂû¤w°õ¦æ§¹²¦ */
+
+
+ /*-----------------------------------------------------*/
+ /* ©Çª«²£¥Í¾¹ */
+ /*-----------------------------------------------------*/
+
+
+/* m.name[13] attribute hp maxhp attack spirit magic armor dodge money exp pic */
+static playrule m; /* °O¿ý©Çª« */
+
+
+/* itoc.010731: ¬°¤FÁ×§K©Çª«ªº¸ê®Æ¤ÓÃe¤j¡A¦Y¤Ó¦h¸ê·½¡A¼g¤@¤ä©Çª«²£¥Í¾¹ */
+static void
+badman_generate(area)
+ int area; /* ¶Ç¤J°Ï°ì¨Ó²£¥Í©Çª« */
+{
+ int level; /* ©Çª«ªºµ¥¯Å */
+
+ level = rand(); /* ­É¥Î level·í¶Ã¼Æ¡A¦]¬°¦P¾lªº¼Æ³£¤£¦P¡A©Ò¥H¥u¥Î¤@¦¸ rand() ´N¦n¤F */
+
+ memset(&m, 0, sizeof(playrule)); /* ªì©l¤Æ */
+
+ /* [1]ª¢¤§¬}¸] [2]¥_¤è¦B­ì [3]¥j¥N¿ò¸ñ [4]¤H¤u®qÀ¬ [5]¦aº»¤§ªù [6]ª÷±e¸s«L */
+ /* itoc.010731: ¦U°Ï©Çª«ªº name/attribute/pic ¤£¦P¡A¥H¤Îµ¥¯Å½d³ò¤]¤£¦P */
+
+ /* itoc.010731: µ¥¯Å¬° n ¯Åªº©Çª«¡A¨ä´Á±æ
+ maxhp = 0.75*(n^2) (©Mª±®a¤@¼Ë)
+ attack/spirit/magic/armor/dodge = n*10
+ money = n*10
+ exp = n*5 (­ì«h¤W¥´20°¦©Çª«¤É¤@¯Å) */
+
+ /* itoc.010731: ª`·N m.name ªø«×¬O 13 (¤»­Ó¤¤¤å¦r) */
+ /* itoc.010731: ²{¦b¦U¦a°Ï³£¥u¦³¤@±i¹Ï¡A©Ò¥H m.pic ¥u¦n¥Î«ü©wªº */
+
+ switch (area)
+ {
+ case '1':
+ {
+ char race[3][7] = {"Às¤H", "ª¢Å]", "¤õµK"};
+ char title[4][7] = {"©x§L", "¦u½Ã", "¤h§L", "Åo¹Æ"};
+
+ sprintf(m.name, "%s%s", race[level % 3], title[level % 4]);
+ level = d.level - 10 + level % 5; /* Ãø¼y§C */
+ if (level <= 5)
+ level = 5;
+ m.maxhp = level * level / 2 + 30;
+ m.attack = level * 8;
+ m.spirit = level * 8;
+ m.magic = level * 8;
+ m.armor = level * 8;
+ m.dodge = level * 8;
+ m.money = level * 8;
+ m.exp = level * 3; /* ©Ò¦³Äݩʳ£¤ñ¸û®t¡A·íµM¸gÅç­È¤ñ´Á±æ­È¤Ö */
+ m.attribute = -4; /* ª¢¨t */
+ m.pic = 101 + rand() % 3;
+ }
+ break;
+
+ case '2':
+ {
+ char color[7][5] = {"¶Â", "¥Õ", "¬õ", "ºñ", "ÂÅ", "ª÷", "¤W¥j"};
+ char race[4][9] = {"¦BÅ]", "³·©Ç", "¦BÅú", "ªø¤ò¶H"};
+
+ sprintf(m.name, "%s%s", color[level % 7], race[level % 4]);
+ level = d.level - 10 + level % 10; /* Ãø«×§C */
+ if (level <= 5)
+ level = 5;
+ m.maxhp = level * level + 30; /* ¦å¤ñ´Á±æ­È¦h */
+ m.attack = level * 10;
+ m.spirit = level * 10;
+ m.magic = level * 10;
+ m.armor = level * 10;
+ m.dodge = level * 10;
+ m.money = level * 12; /* ¿ú¤ñ¸û¦h */
+ m.exp = level * 5;
+ m.attribute = -3; /* ¦B¨t */
+ m.pic = 201 + rand() % 3;
+ }
+ break;
+
+ case '3':
+ {
+ char color[5][3] = {"ª÷", "¤ì", "¤ô", "¤õ", "¤g"};
+
+ sprintf(m.name, "%s¤¸¯À", color[level % 5]);
+ level = d.level - 10 + level % 20; /* Ãø«×¤¤ */
+ if (level <= 5)
+ level = 5;
+ m.maxhp = level * level * 3 / 4 + 30;
+ m.attack = level * 12; /* ª«²z§ðÀ»¤ñ´Á±æ­È±j */
+ m.spirit = level * 8; /* ¤º¤O«ü¼Æ¤ñ´Á±æ­È®t */
+ m.magic = level * 15; /* Å]ªk¤O¶q¤ñ´Á±æ­È±j«Ü¦h */
+ m.armor = level * 8; /* Å@¥Ò±j«×¤ñ´Á±æ­È®t */
+ m.dodge = level * 10;
+ m.money = level * 10;
+ m.exp = level * 6; /* ¸gÅç­È¤ñ¸û¦h */
+ m.attribute = 0;
+ m.pic = 301 + rand() % 3;
+ }
+ break;
+
+ case '4':
+ {
+ char title[5][5] = {"­^«i", "¯«ªZ", "¦Ê¾Ô", "±`³Ó", "¸U¯à"};
+ char race[8][5] = {"ÃM¤h", "ªZ¤h", "§ÔªÌ", "¼C«È", "µs¸é", "¹¬«Q", "§Å®v", "ªª®v"};
+
+ sprintf(m.name, "%s%s", title[level % 5], race[level % 8]);
+ level = d.level + level % 10; /* Ãø«×°ª */
+ m.maxhp = level * level * 2 + 30; /* ¦å¤ñ´Á±æ­È¦h«Ü¦h */
+ m.attack = level * 10;
+ m.spirit = level * 10;
+ m.magic = level * 10;
+ m.armor = level * 12; /* Å@¥Ò±j«×¤ñ´Á±æ­È°ª */
+ m.dodge = level * 12; /* °{Á׫ü¼Æ¤ñ´Á±æ­È°ª */
+ m.money = level * 10;
+ m.exp = level * 13 / 2; /* ¸gÅç­È¤ñ¸û¦h */
+ m.attribute = 0;
+ m.pic = 401 + rand() % 3;
+ }
+ break;
+
+ case '5':
+ {
+ char title[3][7] = {"³g¦Yªº", "¸ô¹Lªº", "·Rª±ªº"};
+ char race[5][7] = {"«ÕÆF", "§l¦å°­", "¶ÂµL±`", "¥ÕµL±`", "¤p°­"};
+
+ sprintf(m.name, "%s%s", title[level % 3], race[level % 5]);
+ level = d.level + level % 20; /* Ãø«×°ª */
+ m.maxhp = level * level * 3 / 2 + 30; /* ¥þ³¡³£«Ü±j */
+ m.attack = level * 15;
+ m.spirit = level * 15;
+ m.magic = level * 15;
+ m.armor = level * 15;
+ m.dodge = level * 15;
+ m.money = level * 15; /* ¿ú¦h«Ü¦h */
+ m.exp = level * 7; /* ¸gÅç­È¦h«Ü¦h */
+ m.attribute = 0;
+ m.pic = 501 + rand() % 3;
+ }
+ break;
+
+ case '6':
+ {
+ /* itoc.010814: ª÷±e¸s«L¶Ç */
+ int num;
+ char name[27][13] =
+ {
+ "­³¤pÄ_", "¬qÅA", "¨f¶³", "´å©Z¤§", "µê¦Ë",
+ "¤pÀs¤k", "­J´´", "¼}®e´_", "²ö¤j", "©¨¤£¸s",
+ "°K©Ó§Ó", "¶À»T", "¥Û¯}¤Ñ", "¥ª§NÁI", "ª÷½üªk¤ý",
+ "¥Oª°¨R", "±iµL§Ò", "¤@¿O", "·¨¹L", "¬x¤C¤½",
+ "¶ÀÃÄ®v", "¼Ú¶§®p", "³ì®p", "³¢¹t", "¥ô§Ú¦æ",
+ "©P§B³q", "ªF¤è¤£±Ñ"
+ };
+
+ /* m.attr: +1:Å@¨­ +2:blitz +3:§lºë +4:®± +5:¼C +6:¤M +7:·t¾¹ (°Ñ¦Ò pip_attack_skill())
+ -1:ªvÀø -2:¹p -3:¦B -4:ª¢ -5:¤g -6:­· -7:¨s·¥ */
+
+ int attr[27] =
+ {
+ +7, +3, +6, +3, +4,
+ +5, +6, -7, +6, +3,
+ +5, -1, -4, -3, -5,
+ +5, +4, +4, +5, +4,
+ -1, +2, +4, +4, +3,
+ +4, -6
+ };
+
+ num = level % 27; /* «ü©w¤@°¦ */
+ strcpy(m.name, name[num]);
+ m.attribute = attr[num]; /* «ü©w¾Õªø§Þ¯à */
+ num++; /* Á×§K«Ý·|¦P¾l¼Æ¬° 0 */
+
+ level = d.level + num + rand() % 20; /* ¶V«á­±ªº¤Hª«¡AÃø«×¶V°ª */
+ m.maxhp = level * level * 2 + 500 * (rand() % num);
+ m.attack = level * (15 + (rand() % num));
+ m.spirit = level * (15 + (rand() % num));
+ m.magic = level * (15 + (rand() % num));
+ m.armor = level * (15 + (rand() % num));
+ m.dodge = level * (15 + (rand() % num));
+ m.money = level * 20; /* ¿ú¦h«Ü¦h */
+ m.exp = level * 10; /* ¸gÅç­È¦h«Ü¦h */
+ m.pic = 101 + 100 * (rand() % 5) + rand() % 3; /* 101~501 102~502 103~503 ¤Q¤­¿ï¤@ */
+ }
+ break;
+ }
+
+ m.hp = m.maxhp; /* ¦å¸Éº¡ */
+}
+
+
+ /*-----------------------------------------------------*/
+ /* ¾Ô°«§Þ¯à°Ñ¼Æ */
+ /*-----------------------------------------------------*/
+
+
+/* skillset: smode sno sbasic name[13] needhp needmp needvp needsp addtired effect pic message[41] */
+
+/* itoc.010820: ¯S®íÂø¶µ§Þ¯à */
+
+struct skillset skillXYZlist[] =
+{
+ /* Âø¶µªº®ÄªG¬Oµ{¦¡­q©w hp mp vp sp tir eff pic message */
+ +0, 0x0000, 7, "¯S®í§Þ¯à", 0, 0, 0, 0, 0, 0, 0, "¯S®í§Þ¯à",
+ +0, 0x0001, 0x0000, "¥ª¥k¤¬·i", 0, 0, 0, 0, 0, 0, 0, "±z¦bµL²á¤§»Ú¡A¦Û³Ð¤F¥ª¤â©M¥k¤â¥´¬[ªº¤èªk", /* d_dr ¤j¶q¤W¤É */
+ +0, 0x0002, 0x0000, "¤O©Þ¤sªe", 0, 0, 0, 0, 0, 0, 0, "±zªºÅé½è¯àÅý±z¶°®ð¬Æ§Ö", /* d_sr ¤j¶q¤W¤É */
+ +0, 0x0004, 0x0000, "Å]³êºëÆF", 0, 0, 0, 0, 0, 0, 0, "±z©M¤Ñ¦aºëÆFñ¤U«´¬ù¡A¬I®iÅ]ªk«Â¤O¤j¼W", /* d_mr ¤j¶q¤W¤É */
+ +0, 0x0008, 0x0000, "§Ö°¨¥[Ã@", 0, 0, 0, 0, 0, 0, 0, "±z¥i¦b¼Ä§Ú¤§¶¡§Ö³t¬ï±ô¡A¦p¤JµL¤H¤§¦a", /* d_hr ¤j¶q¤W¤É */
+
+ +0, 0x0010, 0x0000, "´MÀs³Z", 0, 0, 0, 0, 0, 0, 0, "´MÀs³ôÀ¬¤§¾Ç¡A¤µ¤é¥þ³Q±z®©³z¤F", /* ¾Ô°«¸ô¤W¥i¥H¦^´_¦å */
+ +0, 0x0020, 0x0000, "±o¨Ó³t", 0, 0, 0, 0, 0, 0, 0, "±z¦b³Á·í³Ò«eÅé·|¤F³t­¹ªº§Þ¥©", /* ¾Ô°«¤¤¦YªF¦è¤£¯Ó¦^¦X¼Æ */
+ +0, 0x0040, 0x0000, "Àu¥ý§ðÀ»", 0, 0, 0, 0, 0, 0, 0, "±q¦¹¥H«á¡A¾Ô³õ¤W±zÁ`¯à§Ö¤H¤@¨B", /* ¨C¦¸¾Ô°«¥²¥ý§ðÀ» */
+};
+
+
+/* itoc.010801: ±j«×³]©w: ¥Ñ¯ÓÂI (mp/vp/sp) Á`¦X¨Ó¨M©w®ÄªG (effect)¡Aneedmp->effect¡A¹ïÀ³¦p¤U
+ 20->10 30->20 50->40 70->100 100->150 250->350 400->600 600->900 900->1500 ½Ð¾A·í¨Ï¥Î¤º´¡ªk
+ ­Y¸Ó§Þ¯à¯ÓÂI¥­§¡¦b¤GºØ¥H¤WªºÂI¼Æ¡A¨º»ò®ÄªG­n¥´­Ó§é¦©¡A§Y¯ÂªZ¥\©Î¯ÂÅ]ªk¤ñ¸û±j */
+
+/* ¤TºØªZ¥\ skillA ~ skillC */
+
+struct skillset skillAlist[] =
+{
+ /* Å@¨­ªº®ÄªG¬O¼W¥[ d_resistmore hp mp vp sp tir eff pic message */
+ +1, 0x0000, 4, "Å@¨­", 0, 0, 0, 0, 0, 0, 0, "Å@¨­¦Cªí",
+ +1, 0x0001, 0x0000, "ÅK¥¬­m", 0, 0, 0, 80, 6, 60, 100, "¥þ¨­¤W¤U³ò¶µÛ¤@°}ª÷¥ú",
+ +1, 0x0002, 0x0001, "ª÷ÄÁ¸n", 0, 0, 0, 150, 7, 80, 100, "¤ÖªLª÷­è¤£Ãa¤§¨­",
+
+ +1, 0x0004, 0x0000, "¤æÂà¬P²¾", 0, 0, 90, 5, 6, 70, 100, "¤æÂà¬P²¾¡A¤G»ö¤Æ¥|¶H",
+ +1, 0x0008, 0x0004, "°®©[¤j®¿²¾", 0, 0, 120, 100, 7, 90, 100, "°®©[¤j®¿²¾¡A¥|¶H¤Æ¤K¨ö",
+};
+
+struct skillset skillBlist[] =
+{
+ /* »´¥\¯Ó¤º¤O¡A¦^´_²¾°Ê¤O hp mp vp sp tir eff pic message */
+ +2, 0x0000, 3, "»´¥\\", 0, 0, 0, 0, 0, 0, 0, "»´¥\\¦Cªí",
+ +2, 0x0001, 0x0000, "ªZ·íÂܶ³±è", 0, 0, -30, 15, 2, 0, 110, "ªZ·í§Ì¤l©Ò¾ÕªøªºÂܶ³±è",
+ +2, 0x0002, 0x0001, "¯«¦æ¦ÊÅÜ", 0, 0,-100, 60, 3, 0, 110, "§ÚÅܧÚÅܧÚÅÜÅÜÅÜ",
+ +2, 0x0004, 0x0003, "­âªi·L¨B", 0, 0,-200, 110, 3, 0, 110, "«ö·Ó¤K¨ö¡A±z¨Ï¥X­âªi·L¨B",
+};
+
+struct skillset skillClist[] =
+{
+ /* ¤ßªk¼W¥[¯h³Ò¡A¦^´_¤º¤O hp mp vp sp tir eff pic message */
+ +3, 0x0000, 12, "¤ßªk", 0, 0, 0, 0, 0, 0, 0, "¤ßªk¦Cªí",
+ +3, 0x0001, 0x0000, "¯«·Ó¸g", 0, 0, 0, -20, 10, 50, 120, "¶g¨­¹B°_¤F¯«·Ó¯u¸g",
+ +3, 0x0002, 0x0000, "µµÁø¯«¥\\", 0, 0, 0, -60, 20, 50, 120, "±z¹B°_µµÁø¯«¥\\¡AÁy¦â¬õ¼í",
+ +3, 0x0004, 0x0000, "¤E³±¯u¸g", 0, 0, 0,-200, 28, 50, 120, "¤ý­«¶§ªº±o·Nµ´©Û¡Ð¤E³±¯u¸g",
+ +3, 0x0008, 0x0000, "¤E¶§¯u¸g", 0, 0, 0,-250, 30, 50, 120, "¤E¶§¯«¥\\¡AÀH¤ß¦Óµo",
+
+ +3, 0x0010, 0x0000, "¤pµL¬Û¥\\", 0, 0, 0, -40, 15, 50, 120, "¤pµL¬Û¥\\¡A³±¶§½Õ©M",
+ +3, 0x0020, 0x0010, "¬~Åè¸g", 0, 0, 0, -80, 23, 50, 120, "±z¬I®i¥X¶Ç»¡¤¤ªº¬~Åè¸g",
+ +3, 0x0040, 0x0030, "¤Q¤Kªd°¸", 0, 0, 0,-180, 27, 50, 120, "±z±q¤Q¤K´Lªd°¸¤¤©Ò°Ñ³zªº¤º¥\\",
+ +3, 0x0080, 0x0070, "©öµ¬¸g", 0, 0, 0,-360, 35, 50, 120, "¤ÖªL¤£¶Ç¤§¾Ç¡Ð¹F¼¯©öµ¬",
+
+ /* ¥Î¨ä¥L´« sp */
+ +3, 0x0100, 0x0000, "¯«¤ì¤ý¹©", 15, 0, 0, -30, 1, 50, 120, "±z¨Ï¥X¬r¡A¨Ã¦ø¾÷§l¤F¹ï¤è¤@¨Ç¤º¤O",
+ +3, 0x0200, 0x0100, "¤Æ¥\\¤jªk", 0, 35, 0, -60, 1, 50, 120, "±z±q¼Ä¤â¨­¤W¤Æ¨Ó¤F¤@µ·µ·¤º¤O",
+ +3, 0x0400, 0x0300, "§l¬P¤jªk", 0, 55, 0,-150, 7, 50, 120, "¦b§l¯Ç¹ï¤è¤º¤O¤§¾l¡A±z±o¦n¦n¥ð®§¤@µf",
+ +3, 0x0800, 0x0700, "¥_­ß¯«¥\\", 0, 100, 0,-180, 1, 50, 120, "µL·N¤§¤¤¡A±z±q¹ï¤è§l¨ú¤F¤j¶q¤º¤O",
+};
+
+struct skillset skillDlist[] =
+{
+ /* ®±ªk¥D­n¯Ó¤º¤O hp mp vp sp tir eff pic message */
+ +4, 0x0000, 16, "®±ªk", 0, 0, 0, 0, 0, 0, 0, "®±ªk¦Cªí",
+ +4, 0x0001, 0x0000, "¤ÖªLªø®±", 0, 0, 0, 50, 10, 40, 130, "¤ÖªLªø®±¡Aªêªê¥Í­·", /* ¤ÖªL¨t´xªk¥þ«÷¤º¤O */
+ +4, 0x0002, 0x0001, "ùº~®±", 0, 0, 0, 70, 10, 100, 130, "¤ÖªL¤Q¤Kùº~",
+ +4, 0x0004, 0x0003, "¥ñªê®±", 0, 0, 0, 100, 10, 150, 130, "«n¤s¥ñªê",
+ +4, 0x0008, 0x0007, "¯ë­Y´x", 0, 0, 0, 150, 10, 200, 130, "¤ÖªL¯ë­Y´x",
+ +4, 0x0010, 0x000F, "¤j¤Oª÷­è«ü", 0, 0, 0, 250, 10, 350, 130, "¤ÖªL¤j¤Oª÷­è«ü",
+ +4, 0x0020, 0x001F, "µL¬Û§T«ü", 0, 0, 0, 400, 10, 600, 130, "¤ÖªLµL¬Û§T«ü",
+ +4, 0x0040, 0x001F, "¤»¯ß¯«¼C", 0, 0, 200, 100, 7, 400, 130, "¤j²z¬q®a¤»¯ß¯«¼C",
+ +4, 0x0080, 0x0003, "ÅfµM®ø»î´x", 30, 0, 0, 100, 12, 300, 130, "±z¶Ë¤ß¦aÀ»¥X¤@´x", /* ­Ý¯Ó¦å! */
+
+ +4, 0x0100, 0x0000, "ªZ·í´xªk", 0, 0, 30, 30, 6, 40, 130, "±z¥´¥XªZ·í¬£³Ì°ò¥»ªº´xªk", /* ªZ·í¨t´xªk­Ý¯Ó²¾°Ê¤O */
+ +4, 0x0200, 0x0100, "¤Ó·¥®±", 0, 0, 180, 80, 6, 350, 130, "¤Ó·¥®±ªk¡A¥H¬X§J­è",
+ +4, 0x0400, 0x0100, "¾K®±", 0, 0, 120, 60, 10, 200, 130, "³Ü¤F¤@¤f°s¡A±zÁx¤l¤j¤F°_¨Ó",
+
+ +4, 0x0800, 0x0000, "¤Ñ°¨¬y¬P®±", 0, 30, 30, 30, 8, 120, 130, "°Ú°Ú¡A¤Ñ°¨¬y¡¹®±", /* ©Ç©ÇÃþ´xªk­Ý¯Ó²¾°Ê¤O¤Îªk¤O */
+ +4, 0x1000, 0x0800, "Àt¬£®ð¥\\", 0, 60, 60, 60, 8, 200, 130, "¦Y§Ú¤@°OÀt¬£®ð¥\\",
+ +4, 0x2000, 0x1800, "¤¸®ð¥É", 0, 120, 120, 120, 8, 450, 130, "¤@¶ô¤¸®ð­¸¥X",
+ +4, 0x4000, 0x3800, "¬É¤ý®±", 0, 240, 240, 240, 8, 950, 130, "¬Ý§Ú¤­­¿ªº¬É¤ý®±",
+
+ +4, 0x8000, 0x7FFF, "­°Às¤Q¤K´x", 0, 0, 0, 900, 15, 1500,130, "¤®Às¦³®¬¡I", /* ²×·¥®±ªk¡A¤º¤O­n²`«p°Ú */
+};
+
+struct skillset skillElist[] =
+{
+ /* ¼Cªk¥D­n¯Ó²¾°Ê¤O¡A¨ä¦¸¤º¤O hp mp vp sp tir eff pic message */
+ +5, 0x0000, 16, "¼Cªk", 0, 0, 0, 0, 0, 0, 0, "¼Cªk¦Cªí",
+ +5, 0x0001, 0x0000, "ªZ·í¼Cªk", 0, 0, 30, 20, 6, 35, 140, "ªZ·í¼Cªk¡A»D¦W¤Ñ¤U", /* ªZ·í¨t¼Cªk¡A­º­«²¾°Ê¤O */
+ +5, 0x0002, 0x0001, "µê¼v¼Cªk", 0, 0, 50, 20, 6, 90, 140, "µê¼v¼Cªk¡A¼C¦Ü¼v¦Ü",
+ +5, 0x0004, 0x0003, "¥ñ¿ª¼Cªk", 0, 0, 70, 20, 6, 120, 140, "¥ñ¿ª³±¶§¡A¼C¦b¤H¦b",
+ +5, 0x0008, 0x0007, "¥È¶§¼Cªk", 0, 0, 100, 30, 6, 150, 140, "¥È¶§µ´¤õ¡A¼Cµo®ðµo",
+ +5, 0x0010, 0x000F, "¨Rµê¼Cªk", 0, 0, 150, 30, 6, 220, 140, "¨Rµê¼Cªk¡A¤@®ð¨þ¦¨",
+ +5, 0x0020, 0x001F, "¨â»ö¼Cªk", 0, 0, 250, 30, 6, 365, 140, "¨â»ö¼Cªk¡A¨â»ö¥|¶H",
+ +5, 0x0040, 0x003F, "¤Ó·¥¼Cªk", 0, 0, 400, 30, 5, 620, 140, "¤Ó·¥¼Cªk¡A¥H¬X§J­è",
+ +5, 0x0080, 0x003F, "§P©xµ§", 0, 0, 200, 200, 7, 580, 140, "§P©x¤§µ§¡Aµ´¤£¤â³n",
+
+ +5, 0x0100, 0x0000, "¤Ñ¤k´²ªá", 10, 0, 30, 20, 6, 40, 140, "¤Ñ¤k´²ªá¡A¤Ñªá¶Ã¼Y",
+ +5, 0x0200, 0x0100, "¨gªá¼Cªk", 40, 0, 100, 40, 7, 250, 140, "¨gªá¼Cªk¡A¼C¤£µêµo",
+ +5, 0x0400, 0x0300, "¥ÈÅK¼Cªk", 80, 0, 70, 120, 7, 350, 140, "¥È¼C­«¼C¡A¤O¹D¤Q¨¬",
+ +5, 0x0800, 0x0700, "¿W©t¤E¼C", 0, 0, 250, 70, 12, 400, 140, "¿W©t¤E¼C¡A¨D±Ñ¤£¯à",
+
+ +5, 0x1000, 0x0000, "¹@¨¸¼Cªk", 100, 0, 100, 100, 15, 450, 140, "±ý½m¯«¥\\¡A¥²¥ý¦Û®c", /* ¯Ó¤£¤Ö¦å¡A©Ò¥H«Â¤O¥[±j */
+ +5, 0x2000, 0x1000, "¸ªªáÄ_¨å", 300, 0, 250, 200, 15, 1200,140, "­Y¤£¦Û®c¡A¤]¯à¦¨¥\\",
+
+ +5, 0x4000, 0x3000, "µL¦W¼C³Z", 0, 50, 250, 200, 6, 700, 140, "µL¦W­^¶¯¡A¤Q¤ÀÅQ¹D",
+ +5, 0x8000, 0x7FFF, "¸U¼CÂk©v", 0, 150, 700, 150, 5, 1500,140, "¦¹¨èµL¼C³Ó¦³¼C", /* ²×·¥¼Cªk¡AÄݩʭn¸U¯à°Ú */
+};
+
+struct skillset skillFlist[] =
+{
+ /* ¤Mªk­Ý¯Ó²¾°Ê¤O¤Î¤º¤O hp mp vp sp tir eff pic message */
+ +6, 0x0000, 5, "¤Mªk", 0, 0, 0, 0, 0, 0, 0, "¤Mªk¦Cªí",
+ +6, 0x0001, 0x0000, "¦è¥Ê¤Mªk", 0, 0, 40, 40, 6, 100, 150, "±z¥Ñ¤Á¦è¥Ê¤¤©ÒÅé·|ªº¤Mªk",
+ +6, 0x0002, 0x0000, "¦å¤M¸g", 15, 0, 70, 70, 7, 200, 152, "¦å¤MªgµÛ±zªº¦å¡A¨g®«µL¤ñ",
+ +6, 0x0004, 0x0000, "­J®a¤Mªk", 0, 0, 170, 160, 6, 400, 152, "­J®a®a¶Ç¤Mªk",
+ +6, 0x0008, 0x0000, "°aÁÕµæ¤M", 0, 0, 210, 230, 6, 600, 151, "¤W¥j¶À«Ò®É¥N©Ò¬y¶Ç¤U¨Óªº¤Mªk",
+ +6, 0x0010, 0x0000, "²r¤û«CÀs±Ù", 0, 50, 400, 150, 7, 800, 153, "¶Â·t®Æ²z¬É³Ì±jµ´§Þ",
+ +6, 0x0020, 0x0000, "ÅRÆE¨g¤M", 25, 30, 300, 350, 7, 1000,152, "ÅRÆE¨g¤M¡A¤@¬y",
+};
+
+
+struct skillset skillGlist[] =
+{
+ /* ·t¾¹¥D­n¯Ó²¾°Ê¤O¡A¬r­Ý·l¤@ÂI¦å hp mp vp sp tir eff pic message */
+ /* ·t¾¹ªº¤½¦¡¤ñ¸û¤£¤@¼Ë¡A¾A¦X§C¯à¤OªÌ¨Ï¥Î¡A¦P¼Ë¯ÓÂI¤U¡Aeffect ¤ñ¥¿±`­È°ª¤@¨Ç */
+ +7, 0x0000, 5, "·t¾¹£»¬r", 0, 0, 0, 0, 0, 0, 0, "·t¾¹¦Cªí",
+ +7, 0x0001, 0x0000, "»X¦½ÃÄ", 5, 0, 20, 0, 5, 25, 160, "¨Ï¤H©ü°g¤@¬q®É¶¡ªº»X¦½ÃÄ",
+ +7, 0x0002, 0x0001, "Àn¤ù", 10, 0, 40, 0, 5, 55, 160, "¨Ï¤H¤WÅ}ªºÀn¤ù",
+ +7, 0x0004, 0x0003, "·nÀY¤Y", 40, 0, 160, 0, 5, 350, 160, "§tµÛ¥¤¼Lªº·nÀY¤½¤l",
+ +7, 0x0008, 0x0007, "¦w«D¥L©R", 80, 0, 320, 0, 5, 650, 160, "«UºÙ¦B¶ôªº¦w¤½¤l",
+
+ +7, 0x0010, 0x0000, "³S¸Ì¼C", 0, 0, 20, 5, 5, 25, 161, "³S¤f¤¤®g¥X±z¨Æ¥ýÂænªº§Ö¼C",
+ +7, 0x0020, 0x0010, "­¸½À¨«¥Û", 0, 0, 40, 10, 5, 55, 161, "±zÂY¥Xº¡¤Ñªº¦º·×¥Û",
+ +7, 0x0040, 0x0030, "¯º¸ÌÂäM", 0, 0, 160, 40, 5, 350, 161, "·L¯º¤§¶¡¡A±z¬ðµMѶ¥X¤@¤M",
+ +7, 0x0080, 0x0070, "§t¨F®g¼v", 0, 0, 320, 80, 5, 650, 162, "º©¤Ñ­¸¨F¤§¤¤¡A¥u¨£¼Ä¤H¤¤¤F±zªº·t¾¹",
+};
+
+
+/* ¤CÃþªk³N spellA ~ spellG */
+/* ­Y needhp <0 «hªí¥Ü¸É hp¡A¾lÃþ±À */
+/* itoc.010801: ¦U¨tªº Top ªk³N»¡©ú­n©ãÃý :p */
+
+struct skillset spellAlist[] =
+{
+ /* ªvÀøªk³N hp mp vp sp tir eff pic message */
+ -1, 0x0000, 12, "ªvÀøªk³N", 0, 0, 0, 0, 0, 0, 0, "ªvÀøªk³N",
+
+ /* ¸É¦åÅ]ªk */
+ -1, 0x0001, 0x0000, "°ò¥»®ðÀø", -40, 50, 0, 0, 2, 0, 200, "±z·Pı¨ì·x©M¤F¨Ç",
+ -1, 0x0002, 0x0001, "¾®¯«Âk¤¸", -150, 100, 0, 0, 2, 0, 200, "³¬¥Ø¾i¯«¡A·Pı¦n¤F¤@ÂI",
+ -1, 0x0004, 0x0002, "¤¸ÆFÂk¤ß", -350, 250, 0, 0, 2, 0, 200, "ºë¯«±M¤ß¤F°_¨Ó",
+ -1, 0x0008, 0x0004, "¤­®ð´Â¤¸", -900, 600, 0, 0, 2, 0, 200, "±z§l¦¬¤F¤j¦ÛµMªº¤O¶q",
+
+ /* ¸É²¾°Ê¤O/¯h³ÒÅ]ªk */
+ -1, 0x0010, 0x0000, "²M¤ßÅ]©G", 0, 50, -40, 0, -5, 0, 200, "±zªº¯h³Ò«ì´_¤F",
+ -1, 0x0020, 0x0010, "¤k¯«§È¯§", 0, 100, -150, 0, -10, 0, 200, "±z·Pı¨ì¦³¤H°½°½¦a«OÅ@µÛ±z",
+ -1, 0x0040, 0x0030, "¦Ü¸t¥ú¨~", 0, 250, -350, 0, -15, 0, 200, "¤@°}¸t¥ú³ò¶¨­®Ç",
+ -1, 0x0080, 0x0070, "¤Ñ¨Ï¥[«ù", 0, 600, -900, 0, -20, 0, 200, "¤Ñ¨Ïªº¤O¶q¥[«ù¦b±z¨­¤W",
+
+ /* ¸É¦å/²¾°Ê¤O/¯h³ÒÅ]ªk¡A­n¥ý¾Ç·|«e­±¤GºØ¹ïÀ³ªºªk³N */
+ -1, 0x0100, 0x0011, "¸t¦ö", -40, 120, -40, 0, -5, 0, 200, "±z±o¨ì¨Ó¦Û¤W«Òªº¯¬ºÖ",
+ -1, 0x0200, 0x0033, "­·¶³", -150, 240, -150, 0, -5, 0, 200, "±z±o¨ì¨Ó¦Û­·¶³ªº¯¬ºÖ",
+ -1, 0x0400, 0x0077, "¬PªÅ", -350, 600, -350, 0, -5, 0, 200, "±z±o¨ì¨Ó¦Û¬PªÅªº¯¬ºÖ",
+ -1, 0x0800, 0x00FF, "¥Õªê", -9999, 2000, -9999, 0, -999, 0, 200, "¥Õªê¤Ñ­°¡A¤ß¤§©Ò¦V", /* ¥þº¡ */
+};
+
+struct skillset spellBlist[] =
+{
+ /* ¹p¨tªk³N hp mp vp sp tir eff pic message */
+ -2, 0x0000, 7, "¹p¨tªk³N", 0, 0, 0, 0, 0, 0, 0, "¹p¨tªk³N",
+ -2, 0x0001, 0x0000, "¹p©G", 0, 30, 0, 0, 2, 20, 210, "±z¬I®i¤F¹p©G",
+ -2, 0x0002, 0x0001, "¤­¹p©G", 0, 50, 0, 0, 2, 40, 210, "±z¬I®i¤F¤­¹p©G",
+ -2, 0x0004, 0x0003, "¤Ñ¹pºô", 0, 70, 0, 0, 2, 100, 210, "±z¬I®i¤F¤Ñ¹pºô",
+ -2, 0x0008, 0x0007, "ºÆ¨g¤§¹p", 0, 100, 0, 0, 3, 150, 210, "±z¬I®i¤FºÆ¨g¤§¹p",
+ -2, 0x0010, 0x000F, "¹p¯«¤§»R", 0, 150, 0, 0, 3, 200, 210, "±z¬I®i¤F¹p¯«¤§»R",
+ -2, 0x0020, 0x001F, "Ãz¹p­°Á{", 0, 250, 0, 0, 3, 300, 210, "±z¬I®i¤FÃz¹p­°Á{",
+ -2, 0x0040, 0x003F, "¯«Às", 0, 400, 0, 0, 4, 600, 210, "¯«ÀsÂ\\§À¡A¹p¹q¬ÛÀH",
+};
+
+struct skillset spellClist[] =
+{
+ /* ¦B¨tªk³N hp mp vp sp tir eff pic message */
+ -3, 0x0000, 7, "¦B¨tªk³N", 0, 0, 0, 0, 0, 0, 0, "¦B¨tªk³N",
+ -3, 0x0001, 0x0000, "¦B©G", 0, 30, 0, 0, 2, 20, 220, "±z¬I®i¤F¦B©G",
+ -3, 0x0002, 0x0001, "´H¦B©G", 0, 50, 0, 0, 2, 40, 220, "±z¬I®i¤F´H¦B©G",
+ -3, 0x0004, 0x0003, "¥È¦B©G", 0, 70, 0, 0, 2, 100, 220, "±z¬I®i¤F¥È¦B©G",
+ -3, 0x0008, 0x0007, "¦B­·¼É", 0, 100, 0, 0, 3, 150, 220, "±z¬I®i¤F¦B­·¼É",
+ -3, 0x0010, 0x000F, "­·¹p¦B¤Ñ", 0, 150, 0, 0, 3, 200, 220, "±z¬I®i¤F­·¹p¦B¤Ñ",
+ -3, 0x0020, 0x001F, "µ´¹ï¹s«×", 0, 250, 0, 0, 3, 300, 220, "±z¬I®i¤Fµ´¹ï¹s«×",
+ -3, 0x0040, 0x003F, "¦B¯«", 0, 400, 0, 0, 4, 600, 220, "¦B¯«¤§»R¡A­·«BµLªý",
+};
+
+struct skillset spellDlist[] =
+{
+ /* ª¢¨tªk³N hp mp vp sp tir eff pic message */
+ -4, 0x0000, 7, "ª¢¨tªk³N", 0, 0, 0, 0, 0, 0, 0, "ª¢¨tªk³N",
+ -4, 0x0001, 0x0000, "ª¢©G", 0, 30, 0, 0, 2, 20, 230, "±z¬I®i¤Fª¢©G",
+ -4, 0x0002, 0x0001, "ª¢±þ©G", 0, 50, 0, 0, 2, 40, 230, "±z¬I®i¤Fª¢±þ©G",
+ -4, 0x0004, 0x0003, "·Òº»¯u¤õ", 0, 70, 0, 0, 2, 100, 230, "±z¬I®i¤F·Òº»¯u¤õ",
+ -4, 0x0008, 0x0007, "¦aº»·~¤õ", 0, 100, 0, 0, 3, 150, 230, "±z¬I®i¤F¦aº»·~¤õ",
+ -4, 0x0010, 0x000F, "ª¢Å]¦aº»", 0, 150, 0, 0, 3, 200, 230, "±z¬I®i¤Fª¢Å]¦aº»",
+ -4, 0x0020, 0x001F, "¤õÀs©Û³ê", 0, 250, 0, 0, 3, 300, 230, "±z¬I®i¤F¤õÀs©Û³ê",
+ -4, 0x0040, 0x003F, "¦¶³¶", 0, 400, 0, 0, 4, 600, 230, "¦¶³¶®i¯Í¡A¨ÓªÌ¥²¦º",
+};
+
+struct skillset spellElist[] =
+{
+ /* ¤g¨tªk³N hp mp vp sp tir eff pic message */
+ -5, 0x0000, 7, "¤g¨tªk³N", 0, 0, 0, 0, 0, 0, 0, "¤g¨tªk³N",
+ -5, 0x0001, 0x0000, "¤g©G", 0, 30, 0, 0, 2, 20, 240, "±z¬I®i¤F¤g©G",
+ -5, 0x0002, 0x0001, "­¸©¥³N", 0, 50, 0, 0, 2, 40, 240, "±z¬I®i¤F­¸©¥³N",
+ -5, 0x0004, 0x0003, "¦aµõ¤Ñ±Y", 0, 70, 0, 0, 2, 100, 240, "±z¬I®i¤F¦aµõ¤Ñ±Y",
+ -5, 0x0008, 0x0007, "®õ¤sÀ£³»", 0, 100, 0, 0, 3, 150, 240, "±z¬I®i¤F®õ¤sÀ£³»",
+ -5, 0x0010, 0x000F, "¤gÀs¥l³ê", 0, 150, 0, 0, 3, 200, 240, "±z¬I®i¤F¤gÀs¥l³ê",
+ -5, 0x0020, 0x001F, "¤g¦a¯«©ú", 0, 250, 0, 0, 3, 300, 240, "±z¬I®i¤F¤g¦a¯«©ú",
+ -5, 0x0040, 0x003F, "¥ÈªZ", 0, 400, 0, 0, 4, 600, 240, "¥ÈªZ¦A²{¡A§ðÀ»µL­­",
+};
+
+struct skillset spellFlist[] =
+{
+ /* ­·¨tªk³N hp mp vp sp tir eff pic message */
+ -6, 0x0000, 7, "­·¨tªk³N", 0, 0, 0, 0, 0, 0, 0, "­·¨tªk³N",
+ -6, 0x0001, 0x0000, "­·©G", 0, 30, 0, 0, 2, 20, 250, "±z¬I®i¤F­·©G",
+ -6, 0x0002, 0x0001, "±Û­·©G", 0, 50, 0, 0, 2, 40, 250, "±z¬I®i¤F±Û­·©G",
+ -6, 0x0004, 0x0003, "¨g­·³N", 0, 70, 0, 0, 2, 100, 250, "±z¬I®i¤F¨g­·³N",
+ -6, 0x0008, 0x0007, "Às±²­·", 0, 100, 0, 0, 3, 150, 250, "±z¬I®i¤FÀs±²­·",
+ -6, 0x0010, 0x000F, "­·±²´Ý¶³", 0, 150, 0, 0, 3, 200, 250, "±z¬I®i¤F­·±²´Ý¶³",
+ -6, 0x0020, 0x001F, "­·ªá³·¤ë", 0, 250, 0, 0, 3, 300, 250, "±z¬I®i¤F­·ªá³·¤ë",
+ -6, 0x0040, 0x003F, "«CÀs", 0, 400, 0, 0, 4, 600, 250, "«CÀs·í¤¤¡A¯«­·¯S§ð",
+};
+
+struct skillset spellGlist[] =
+{
+ /* ¨s·¥ªk³N hp mp vp sp tir eff pic message */
+ -7, 0x0000, 6, "¨s·¥ªk³N", 0, 0, 0, 0, 0, 0, 0, "¨s·¥ªk³N", /* ´c·d¨tªºÅ]ªk :p */
+ -7, 0x0001, 0x0000, "®½¦å¤¤¤ß", 0,2000, 9999, 9999, 0, 0, 260, "®½¦å¤@©R¡A±Ï¤H¤@³U", /* ªvÀø */
+ -7, 0x0002, 0x0000, "¤â¾÷", 0, 800, 0, 0, 0, 1200,260, "T28 ¥[¤¤µØ¹q«H¡A¶W±j¹qºÏªi", /* ¹p */
+ -7, 0x0004, 0x0000, "¤ô§N¦¡­·®°", 0, 800, 0, 0, 0, 1200,260, "¶WÀWµ´¹ï¨S°ÝÃD", /* ¦B */
+ -7, 0x0008, 0x0000, "®Ö¼u¿Ä¦X", 0, 800, 0, 0, 0, 1200,260, "®Ö¤l¼uÃz¬µ¤F", /* ª¢ */
+ -7, 0x0010, 0x0000, "¤g¥Û¬y", 0, 800, 0, 0, 0, 1200,260, "¥xÆW¦W²£¡Ð¤g¥Û¬y", /* ¤g */
+ -7, 0x0020, 0x0000, "·s¦Ë­·", 0, 800, 0, 0, 0, 1200,260, "³y´N¤F·s¦Ë¦Ì¯»", /* ­· */
+};
+
+
+ /*-----------------------------------------------------*/
+ /* §Þ¯à¾Ç²ß¨ç¦¡ */
+ /*-----------------------------------------------------*/
+
+
+/* itoc.010801: ¾Ç¨ì·s§Þ¯à */
+int /* 0:¨S¦³¾Ç¨ì 1:¾Ç¨ì */
+pip_learn_skill(smode)
+ int smode; /* skill mode 0:Âø¶µ >0:ªZ¥\ <0:Å]ªk */
+{
+ int num;
+ char buf[80];
+
+ /* itoc.010801: ¥ý¶Ã¼Æ¨M©w¸Ó¨t§Þ¯àªº¨ä¤¤¤@¶µ¡AµM«áÀˬd¤pÂû¬O§_¤w¸g·|³o§Þ¯à¤F */
+ /* ¦pªG¤£·|¦Ó¥B¤w¸g¾Ç¨ì¦¹§Þ¯à¤§°ò¥»§Þ¯à¡A¨º»ò±NÀò±o¦¹¤@·s§Þ¯à */
+
+ /* itoc.020129.³]­p§Þ¥©: ­Y skill?list[].sno = skill?list[].sbasic¡A¨º»ò¨Ï¥ÎªÌ´NµLªk±q
+ pip_learn_skill() ¾Ç¨ì¦¹§Þ¯à¡A©Ò¥H¥i¥H¥Ñ¯S®í¨Æ¥ó¨Ó¾Ç²ß */
+
+ switch (smode)
+ {
+ case 0: /* ¯S®í */
+ num = rand() % skillXYZlist[0].sbasic + 1;
+ if ((d.skillXYZ & skillXYZlist[num].sno) || ((d.skillXYZ & skillXYZlist[num].sbasic) != skillXYZlist[num].sbasic))
+ return 0;
+ d.skillXYZ |= skillXYZlist[num].sno;
+ strcpy(buf, skillXYZlist[num].msg);
+ break;
+
+ case 1: /* Å@¨­ */
+ num = rand() % skillAlist[0].sbasic + 1;
+ if ((d.skillA & skillAlist[num].sno) || ((d.skillA & skillAlist[num].sbasic) != skillAlist[num].sbasic))
+ return 0;
+ d.skillA |= skillAlist[num].sno;
+ sprintf(buf, "±z»â®©¤FÅ@¨­¡Ð%s", skillAlist[num].name);
+ break;
+
+ case 2: /* »´¥\ */
+ num = rand() % skillBlist[0].sbasic + 1;
+ if ((d.skillB & skillBlist[num].sno) || ((d.skillB & skillBlist[num].sbasic) != skillBlist[num].sbasic))
+ return 0;
+ d.skillB |= skillBlist[num].sno;
+ sprintf(buf, "±z»â®©¤F»´¥\\¡Ð%s", skillBlist[num].name);
+ break;
+
+ case 3: /* ¤ßªk */
+ num = rand() % skillClist[0].sbasic + 1;
+ if ((d.skillC & skillClist[num].sno) || ((d.skillC & skillClist[num].sbasic) != skillClist[num].sbasic))
+ return 0;
+ d.skillC |= skillClist[num].sno;
+ sprintf(buf, "±z»â®©¤F¤ßªk¡Ð%s", skillClist[num].name);
+ break;
+
+ case 4: /* ®±ªk */
+ num = rand() % skillDlist[0].sbasic + 1;
+ if ((d.skillD & skillDlist[num].sno) || ((d.skillD & skillDlist[num].sbasic) != skillDlist[num].sbasic))
+ return 0;
+ d.skillD |= skillDlist[num].sno;
+ sprintf(buf, "±z»â®©¤F®±ªk¡Ð%s", skillDlist[num].name);
+ break;
+
+ case 5: /* ¼Cªk */
+ num = rand() % skillElist[0].sbasic + 1;
+ if ((d.skillE & skillElist[num].sno) || ((d.skillE & skillElist[num].sbasic) != skillElist[num].sbasic))
+ return 0;
+ d.skillE |= skillElist[num].sno;
+ sprintf(buf, "±z»â®©¤F¼Cªk¡Ð%s", skillElist[num].name);
+ break;
+
+ case 6: /* ¤Mªk */
+ num = rand() % skillFlist[0].sbasic + 1;
+ if ((d.skillF & skillFlist[num].sno) || ((d.skillF & skillFlist[num].sbasic) != skillFlist[num].sbasic))
+ return 0;
+ d.skillF |= skillFlist[num].sno;
+ sprintf(buf, "±z»â®©¤F¤Mªk¡Ð%s", skillFlist[num].name);
+ break;
+
+ case 7: /* ·t¾¹ */
+ num = rand() % skillGlist[0].sbasic + 1;
+ if ((d.skillG & skillGlist[num].sno) || ((d.skillG & skillGlist[num].sbasic) != skillGlist[num].sbasic))
+ return 0;
+ d.skillG |= skillGlist[num].sno;
+ sprintf(buf, "±z»â®©·t¾¹¡Ð%s", skillGlist[num].name);
+ break;
+
+
+ case -1: /* ªvÀøªk³N */
+ num = rand() % spellAlist[0].sbasic + 1;
+ if ((d.spellA & spellAlist[num].sno) || ((d.spellA & spellAlist[num].sbasic) != spellAlist[num].sbasic))
+ return 0;
+ d.spellA |= spellAlist[num].sno;
+ sprintf(buf, "±z¾Ç·|¤F¥ÕÅ]ªk¡Ð%s", spellAlist[num].name);
+ break;
+
+ case -2: /* ¹p¨tªk³N */
+ num = rand() % spellBlist[0].sbasic + 1;
+ if ((d.spellB & spellBlist[num].sno) || ((d.spellB & spellBlist[num].sbasic) != spellBlist[num].sbasic))
+ return 0;
+ d.spellB |= spellBlist[num].sno;
+ sprintf(buf, "±z¾Ç·|¤F¹pÅ]ªk¡Ð%s", spellBlist[num].name);
+ break;
+
+ case -3: /* ¦B¨tªk³N */
+ num = rand() % spellClist[0].sbasic + 1;
+ if ((d.spellC & spellClist[num].sno) || ((d.spellC & spellClist[num].sbasic) != spellClist[num].sbasic))
+ return 0;
+ d.spellC |= spellClist[num].sno;
+ sprintf(buf, "±z¾Ç·|¤F¦BÅ]ªk¡Ð%s", spellClist[num].name);
+ break;
+
+
+ case -4: /* ª¢¨tªk³N */
+ num = rand() % spellDlist[0].sbasic + 1;
+ if ((d.spellD & spellDlist[num].sno) || ((d.spellD & spellDlist[num].sbasic) != spellDlist[num].sbasic))
+ return 0;
+ d.spellD |= spellDlist[num].sno;
+ sprintf(buf, "±z¾Ç·|¤F¤õÅ]ªk¡Ð%s", spellDlist[num].name);
+ break;
+
+ case -5: /* ¤g¨tªk³N */
+ num = rand() % spellElist[0].sbasic + 1;
+ if ((d.spellE & spellElist[num].sno) || ((d.spellE & spellElist[num].sbasic) != spellElist[num].sbasic))
+ return 0;
+ d.spellE |= spellElist[num].sno;
+ sprintf(buf, "±z¾Ç·|¤F¤gÅ]ªk¡Ð%s", spellElist[num].name);
+ break;
+
+ case -6: /* ­·¨tªk³N */
+ num = rand() % spellFlist[0].sbasic + 1;
+ if ((d.spellF & spellFlist[num].sno) || ((d.spellF & spellFlist[num].sbasic) != spellFlist[num].sbasic))
+ return 0;
+ d.spellF |= spellFlist[num].sno;
+ sprintf(buf, "±z¾Ç·|¤F­·Å]ªk¡Ð%s", spellFlist[num].name);
+ break;
+
+ case -7: /* ¨s·¥ªk³N */
+ num = rand() % spellGlist[0].sbasic + 1;
+ if ((d.spellG & spellGlist[num].sno) || ((d.spellG & spellGlist[num].sbasic) != spellGlist[num].sbasic))
+ return 0;
+ d.spellG |= spellGlist[num].sno;
+ sprintf(buf, "±zÁA¸Ñ¤F¶ÂÅ]ªk¡Ð%s", spellGlist[num].name);
+ break;
+ }
+
+ vmsg(buf);
+ return 1;
+}
+
+
+ /*-----------------------------------------------------*/
+ /* ¾Ô°«§Þ¯à¿ï³æ */
+ /*-----------------------------------------------------*/
+
+
+/* ¦³¾Ç·|¸Ó§Þ¯à¡A¥B¦å¡Bªk¤O°÷¤~¯à¬I®i */
+#define can_useskill(n) ((skill & p[n].sno) && (p[n].needhp < d.hp) && (p[n].needmp <= d.mp) && (p[n].needvp <= d.vp) && (p[n].needsp <= d.sp))
+
+
+/* itoc.010729: §Þ¯àµøµ¡ */
+static void
+pip_skill_doing_menu(p, dr, sr, mr, hr) /* §Þ¯àµe­± */
+ struct skillset *p;
+ int dr; /* ¶Ë®`¤O damage rate */
+ int sr; /* ¤º¤O±j«× spirit rate */
+ int mr; /* Å]ªk±j«× magic rate */
+ int hr; /* ©R¤¤²v hit rate */
+{
+ int n, ch;
+ char ans[5];
+ usint skill;
+
+ switch (p[0].smode)
+ {
+ case 1: /* Å@¨­ */
+ skill = d.skillA;
+ break;
+
+ case 2: /* »´¥\ */
+ skill = d.skillB;
+ break;
+
+ case 3: /* ¤ßªk */
+ skill = d.skillC;
+ break;
+
+ case 4: /* ®±¥\ */
+ skill = d.skillD;
+ break;
+
+ case 5: /* ¼Cªk */
+ skill = d.skillE;
+ break;
+
+ case 6: /* ¤Mªk */
+ skill = d.skillF;
+ break;
+
+ case 7: /* ·t¾¹ */
+ skill = d.skillG;
+ break;
+
+ case -1: /* ªvÀøªk³N */
+ skill = d.spellA;
+ break;
+
+ case -2: /* ¹p¨tªk³N */
+ skill = d.spellB;
+ break;
+
+ case -3: /* ¦B¨tªk³N */
+ skill = d.spellC;
+ break;
+
+ case -4: /* ª¢¨tªk³N */
+ skill = d.spellD;
+ break;
+
+ case -5: /* ¤g¨tªk³N */
+ skill = d.spellE;
+ break;
+
+ case -6: /* ­·¨tªk³N */
+ skill = d.spellF;
+ break;
+
+ case -7: /* ¨s·¥ªk³N */
+ skill = d.spellG;
+ break;
+
+ default: /* ¥H§K·N¥~µo¥Í */
+ skill = 0;
+ }
+
+ clrfromto(7, 16);
+ prints("\033[1;31m¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢t\033[37;41m ¥i¥Î[%s]¤@Äýªí \033[0;1;31m¢u¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w\033[m", p[0].name);
+
+ n = 1;
+ while (n <= p[0].sbasic) /* p[0].sbasic Àx¦s¦³³o¨t¦³´X­Ó§Þ¯à */
+ {
+ if (can_useskill(n))
+ {
+ /* ¤À¥|Äæ¡A¨CÄæ¤K­Ó¡A¦U¨t§Þ¯à³Ì¦h 32 ­Ó */
+ if (n <= 8)
+ move(n + 7, 4);
+ else if (n <= 16)
+ move(n - 1, 20);
+ else if (n <= 24)
+ move(n - 9, 36);
+ else
+ move(n - 17, 52);
+
+ prints("%2d.%s", n, p[n].name);
+ }
+
+ n++;
+ }
+
+ while (1)
+ {
+ if (vget(16, 0, " ±z·Q¨Ï¥Î¨º¤@©Û©O¡H[Q]©ñ±ó¡G", ans, 3, DOECHO))
+ {
+ if (ans[0] == 'q')
+ {
+ show_badman_pic(m.pic);
+ return;
+ }
+ else
+ {
+ ch = atoi(ans);
+ if (ch > 0 && ch < n && can_useskill(ch))
+ {
+ break;
+ }
+ }
+ }
+ }
+
+ d.hp -= p[ch].needhp;
+ d.mp -= p[ch].needmp;
+ d.vp -= p[ch].needvp;
+ d.sp -= p[ch].needsp;
+ d.tired += p[ch].addtired;
+
+ /* itoc.010801: ¥u»Ý­nÀˬd¬O§_Ãz¤W­­¡A¤£¥²Àˬd¬O§_ < 0¡A¦]¬°«e­±Àˬd¹L¤F */
+ if (d.hp > d.maxhp)
+ d.hp = d.maxhp;
+ if (d.mp > d.maxmp)
+ d.mp = d.maxmp;
+ if (d.vp > d.maxvp)
+ d.vp = d.maxvp;
+ if (d.sp > d.maxsp)
+ d.sp = d.maxsp;
+ if (d.tired < 0)
+ d.tired = 0;
+
+ /* itoc.010801: ¤ñ¸û */
+ /* ¦b©Çª«¨S¦³¨¾¿m¤U¡A´¶³q§ðÀ»ªº´Á±æ¶Ë®`¬° d_dr */
+ /* ¦b©Çª«¨S¦³¨¾¿m¤U¡A¥þ¤O¤@À»ªº´Á±æ¶Ë®`¬° 120% * d_dr */
+ /* ¦b©Çª«¨S¦³¨¾¿m¤U¡A§Þ¯à§ðÀ»ªº´Á±æ¶Ë®`¬° p[ch].effect + d_dr (©Î d_mr d_sr d_hr)*/
+
+ switch (p[ch].smode)
+ {
+ /* itoc.010729: random ÅܤƶV¤jªº¡A´N®e©ö miss */
+
+ case 1: /* Å@¨­: ¥Î¤F¥H«á¥[±j¨¾¿m */
+ /* ¥[±j¨¾¿m¬O d_resistmore + 40%¡A©Ò¥H p[ch].effect À³¦Ü¤Ö > 40 */
+ d_resistmore = p[ch].effect * (125 - rand() % 50) / 100; /* ­ì®ÄªGªº 75% ~ 125% */
+ if (d_resistmore >= 100)
+ d_resistmore = 99; /* ­Y d_resistmore > 100¡A¤Ï¦ÓÅÜ¥[¦å¤F! */
+ break;
+
+ case 2: /* »´¥\ */
+ /* vp¦b«e­±¤w¸g¥[¹L */
+ break;
+
+ case 3: /* ¤ßªk */
+ /* sp¦b«e­±¤w¸g¥[¹L */
+ break;
+
+ case 4: /* ®±ªk */
+ m.hp -= p[ch].effect * (120 - rand() % 40) / 100 + (dr + sr) / 2;
+ break;
+
+ case 5: /* ¼Cªk */
+ m.hp -= p[ch].effect * (140 - rand() % 80) / 100 + (dr + hr) / 2;
+ break;
+
+ case 6: /* ¤Mªk */
+ m.hp -= p[ch].effect * (160 - rand() % 120) / 100 + (sr + hr) / 2;
+ break;
+
+ case 7: /* ·t¾¹ */
+ /* ·t¾¹»P¯à¤O¤j­PµLÃö¡A¾A¦X§C¯à¤OªÌ¨Ï¥Î */
+ if (hr > d.level * 5)
+ m.hp -= p[ch].effect * (120 - rand() % 40) / 100; /* 100% effect */
+ else if (hr > 0)
+ m.hp -= p[ch].effect * (80 - rand() % 40) / 100; /* 60% effect */
+ else
+ m.hp -= p[ch].effect * (50 - rand() % 40) / 100; /* 30% effect */
+ break;
+
+
+ case -1: /* ªvÀøªk³N */
+ /* hp¦b«e­±¤w¸g¥[¹L */
+ break;
+
+ case -2: /* ¹p¨tªk³N */
+ m.hp -= p[ch].effect * (140 - rand() % 80) / 100 + mr;
+ break;
+
+ case -3: /* ¦B¨tªk³N */
+ m.hp -= p[ch].effect * (120 - rand() % 40) / 100 + mr;
+ break;
+
+ case -4: /* ª¢¨tªk³N */
+ m.hp -= p[ch].effect * (200 - rand() % 200) / 100 + mr; /* ­ì®ÄªGªº 1% ~ 200%¡AÅܤƶW¤j! */
+ break;
+
+ case -5: /* ¤g¨tªk³N */
+ m.hp -= p[ch].effect * (110 - rand() % 20) / 100 + mr;
+ break;
+
+ case -6: /* ­·¨tªk³N */
+ m.hp -= p[ch].effect * (130 - rand() % 60) / 100 + mr;
+ break;
+
+ case -7: /* ¨s·¥ªk³N */
+ /* itoc.010801: ©M¤@¯ëÅ]ªk¤£¦Pªº¬O¡A¨s·¥ªk³N¤£³æ¬Ý mr ¤]¬Ý dr sr hr */
+ m.hp -= p[ch].effect * (140 - rand() % 80) / 100 + (mr * 2 + dr + sr + hr) / 5;
+ break;
+ }
+
+ show_fight_pic(p[ch].pic);
+ vmsg(p[ch].msg);
+ d_nodone = 0; /* ¦æ°Êµ²§ô */
+}
+
+
+/* itoc.010729: ¶i¤J¨Ï¥Î§Þ¯à¿ï³æ */
+static void
+pip_skill_menu(dr, sr, mr, hr) /* ¾Ô°«¤¤§Þ¯àªºÀ³¥Î */
+ int dr; /* ¶Ë®`¤O damage rate */
+ int sr; /* ¤º¤O±j«× spirit rate */
+ int mr; /* Å]ªk±j«× magic rate */
+ int hr; /* ©R¤¤²v hit rate */
+{
+ while (d_nodone)
+ {
+ out_cmd(COLOR1 " ªZ¥\\¿ï³æ " COLOR2 " [1]Å@¨­ [2]»´¥\\ [3]¤ßªk [4]®±ªk [5]¼Cªk [6]¤Mªk [7]·t¾¹ [Q]©ñ±ó \033[m",
+ COLOR1 " ªk³N¿ï³æ " COLOR2 " [A]ªvÀø [B]¹p¨t [C]¦B¨t [D]ª¢¨t [E]¤g¨t [F]­·¨t [G]¯S®í [Q]©ñ±ó \033[m");
+
+ switch (vkey())
+ {
+ case 'q':
+ return;
+
+ case '1':
+ pip_skill_doing_menu(skillAlist, dr, sr, mr, hr);
+ break;
+
+ case '2':
+ pip_skill_doing_menu(skillBlist, dr, sr, mr, hr);
+ break;
+
+ case '3':
+ pip_skill_doing_menu(skillClist, dr, sr, mr, hr);
+ break;
+
+ case '4':
+ pip_skill_doing_menu(skillDlist, dr, sr, mr, hr);
+ break;
+
+ case '5':
+ pip_skill_doing_menu(skillElist, dr, sr, mr, hr);
+ break;
+
+ case '6':
+ pip_skill_doing_menu(skillFlist, dr, sr, mr, hr);
+ break;
+
+ case '7':
+ pip_skill_doing_menu(skillGlist, dr, sr, mr, hr);
+ break;
+
+ case 'a':
+ pip_skill_doing_menu(spellAlist, dr, sr, mr, hr);
+ break;
+
+ case 'b':
+ pip_skill_doing_menu(spellBlist, dr, sr, mr, hr);
+ break;
+
+ case 'c':
+ pip_skill_doing_menu(spellClist, dr, sr, mr, hr);
+ break;
+
+ case 'd':
+ pip_skill_doing_menu(spellDlist, dr, sr, mr, hr);
+ break;
+
+ case 'e':
+ pip_skill_doing_menu(spellElist, dr, sr, mr, hr);
+ break;
+
+ case 'f':
+ pip_skill_doing_menu(spellFlist, dr, sr, mr, hr);
+ break;
+
+ case 'g':
+ pip_skill_doing_menu(spellGlist, dr, sr, mr, hr);
+ break;
+ }
+ }
+}
+
+
+ /*-----------------------------------------------------*/
+ /* ¾Ô°«©Çª«§Þ¯à§ðÀ» */
+ /*-----------------------------------------------------*/
+
+
+/* itoc.010801: ¥Ñ©ó©Çª«§Þ¯à§ðÀ»¡A¤£»Ý­n¿ï³æ¡A©Ò¥H­n©M¤pÂû§Þ¯à§ðÀ»¤À¶}¼g */
+
+static void
+pip_attack_skill(dr, sr, mr, hr) /* ©Çª«§Þ¯à§ðÀ» */
+ int dr; /* ¶Ë®`¤O damage rate */
+ int sr; /* ¤º¤O±j«× spirit rate */
+ int mr; /* Å]ªk±j«× magic rate */
+ int hr; /* ©R¤¤²v hit rate */
+{
+ int num, mankey;
+ char buf[80];
+
+ num = rand(); /* itoc.010801: ¦]¬°¦P¾lªº¼Æ¤£¦P¡A©Ò¥H¥Î¦P¤@­Ó¶Ã¼Æ¨Ó¬Ù¸ê·½ */
+
+ /* itoc.010801: ©Çª«ªº§Þ¯à¤£±Ä¥Î¦© mp/vp/sp ³oºØ¨î«×¡A¦Ó¬O¥Ñ¾÷²v¨Ó¬I®i */
+
+ if (m.attribute && (num % 2))
+ {
+ mankey = m.attribute; /* 50% ©Çª«¨Ï¥Î¦Û¤v©Ò¾Õªøªº§Þ¯à */
+ }
+ else
+ {
+ mankey = rand() % 14 - 7; /* ¦pªG¨S¦³«ü©w¾Õªø§Þ¯à¡A¨º»ò´N¶Ã¼Æ²£¥Í¤@¨t§Þ¯à */
+ if (!mankey)
+ mankey = 7;
+ }
+
+ /* itoc.010801: ¤ñ¸û */
+ /* ¦b¤pÂû¨S¦³¨¾¿m¤U¡A©Çª«´¶³q§ðÀ»ªº´Á±æ¶Ë®`¬° m_dr */
+ /* ¦b¤pÂû¨S¦³¨¾¿m¤U¡A©Çª«¥þ¤O¤@À»ªº´Á±æ¶Ë®`¬° 120% * m_dr */
+ /* ¦b¤pÂû¨S¦³¨¾¿m¤U¡A©Çª«§Þ¯à§ðÀ»ªº´Á±æ¶Ë®`¬° 150% * m_mr (©Î m_dr¡Bm_sr) */
+
+ /* itoc.010814: ©Çª«ªº§Þ¯à¤j­P¤W©M¤pÂû¤@¼Ë¡A¥u¬O©Çª«¨S¦³¸É vp/sp¡A©Ò¥H»´¥\/¤ßªk­n´«¦¨§Oªº */
+
+ switch (mankey)
+ {
+ case 1: /* Å@¨­ */
+ /* ¥[±j¨¾¿m¬O m_resistmore + 40%¡A©Ò¥H»´¥\ effect À³¦Ü¤Ö > 40¡A¥B­n < 100 */
+ m_resistmore = 40 + num % 60;
+ vmsg("¹ï¤è¥[±j¨¾¿m¡A¥þ¨­°Ê¤F°_¨Ó");
+ break;
+
+ case 2: /* »´¥\ ¡Ð °{¹qŧÀ» */
+ if (hr * (100 - num % 30) / 10 > d.level)
+ {
+ /* °{¹q©_ŧªº¯S¦â´N¬O©M©Çª«¥»¨­¦Û¤vªº¯à¤O¨Ã¨S¦³Ãö«Y(¦pªG©_ŧ¦¨¥\ªº¸Ü) */
+ d.hp -= d.maxhp / 3;
+ vmsg("¢Ð¢ô¢ñ¢ü£C¡I¹ï¤è¦V±z°{¹qŧÀ»");
+ }
+ else
+ {
+ m.hp -= m.maxhp / 5;
+ if (m.hp <= 0)
+ m.hp = 1;
+ vmsg("¹ï¤è¥ø¹Ïµo°Ê°{¹q©_ŧ¡A¦ý¨S¯à¦¨¥\\¡A¤Ï¦Ó³y¦¨¥L¦Û¤vªº·l¶Ë");
+ }
+ break;
+
+ case 3: /* ¤ßªk ¡Ð §lºë */
+ /* itoc.010801: ©Çª«¦å¶V¦h¡A§l±o¶V¦h */
+ m.hp += m.maxhp / 8;
+ d.hp -= m.maxhp / 8;
+ d.mp -= m.maxhp / 10;
+ d.vp -= m.maxhp / 12;
+ d.sp -= m.maxhp / 14;
+ if (m.hp > m.maxhp)
+ m.hp = m.maxhp;
+ if (d.mp < 0)
+ d.mp = 0;
+ if (d.vp < 0)
+ d.vp = 0;
+ if (d.sp < 0)
+ d.sp = 0;
+ sprintf(buf, "%s¬½¬½¦a«r¤F±z¤@¤f¡AÃø¹D³o´N¬O¶Ç»¡¤¤ªº§lºë³N¡H", m.name);
+ vmsg(buf);
+ break;
+
+ case 4: /* ®±ªk */
+ vmsg("¹j¤s¥´¤û¡A¹ï¤è¥´±o±z§¾ºu§¿¬y");
+ d.hp -= (dr + sr) * (125 + num % 50) / 200;
+ break;
+
+ case 5: /* ¼Cªk */
+ if (num % 3 == 0)
+ {
+ /* itoc.010801: °µ¤@¨Ç©Ç©Çªº®ÄªG */
+ char name[3][9] = {"¤@Â_³gÜÒ", "¤GÂ_·R±ý", "¤TÂ_·Ð´o"};
+
+ for (num = 0; num < 3; num++)
+ {
+ sprintf(buf, "¤Ñ¹P¯«¼C %s", name[num]); /* itoc: §f¬}»«£»¤Ñ¹P¯«¼C */
+ vmsg(buf);
+ }
+ }
+ else
+ {
+ vmsg("º¡¤Ñªá«B¡A±z¤¤¤F¹ï¤è¤@¼C");
+ }
+ d.hp -= (dr + hr) * (125 + num % 50) / 200;
+ break;
+
+ case 6: /* ¤Mªk */
+ vmsg("±z¤¤¤F¹ï¤âªº¤Ñ´Ý¤Mªk¡I");
+ d.hp -= (sr + hr) * (125 + num % 50) / 200;
+ break;
+
+ case 7: /* ·t¾¹ */
+ vmsg("²¨©¿¤§¤¤¡A±z¤¤¤F¹ï¤âªº·t¾¹¡I");
+ d.hp -= hr * (85 + num % 30) / 100; /* ¤@¯ë§Þ¯à§ðÀ»¬O 150% m_hr¡A¦ý·t¾¹¤ñ¸û®t¡A¥u¦³ 100% */
+ break;
+
+ case -1: /* ªvÀøªk³N */
+ m.hp += m.maxhp * (40 + rand() % 30) / 100; /* ¦^´_ 40%~70% ªº¦å */
+ if (m.hp > m.maxhp)
+ {
+ m.hp = m.maxhp;
+ vmsg("¤@°}¸t¥ú·ÓÄ£¹ï¤è¡A¥Lªºª¬ªp§¹¥þ«ì´_¤F");
+ }
+ else
+ {
+ vmsg("¹ï¤è¨Ï¥ÎÅ]ªkªvÀø¤F¦Û¤v");
+ }
+ break;
+
+ case -2: /* ¹p¨tªk³N */
+ case -3: /* ¦B¨tªk³N */
+ case -4: /* ª¢¨tªk³N */
+ case -5: /* ¤g¨tªk³N */
+ case -6: /* ­·¨tªk³N */
+ /* itoc.010814: ©Ò¦³ªº§ðÀ»ªk³N¦X¨Ö¦b¤@°_³B²z */
+ {
+ /* itoc.010814: ¨C¨tªk³N¦U 2 °¦¥H°t¦X mankey ¨Ó¿ï¥l³êÃ~ */
+ char name[10][9] = {"¹q®ð½Þ", "¹p¥úÃ~", "´H¦B°­", "­á¤ô©Ç", "¼ö¤õÅ]", "¤õµK²y", "«ã¤g¦ä", "¥ÛÀY©Ç", "¼É­·§¯", "°g»î°­"};
+
+ num = mr * (125 + num % 50) / 100; /* ­É¥Î num ·í¶Ë®`ÂI¼Æ */
+ d.hp -= num;
+ sprintf(buf, "¹ï¤è©Û´«¤F%s¡A±z¨ü¶Ë¤F %d ÂI", name[-2 * (mankey + 2) + (num % 2)], num);
+ vmsg(buf);
+ }
+ break;
+
+ case -7: /* ¨s·¥ªk³N */
+ /* ¸Éº¡¦å¤S§ðÀ»! */
+ m.hp = m.maxhp;
+ d.hp -= (dr + sr + mr + hr) * (75 + num % 50) / 800;
+ sprintf(buf, "%s¨Ï¥Î¤F¤ÑÅ]¸ÑÅé¡A±z§¹¥þ¤£¬O¹ï¤â", m.name);
+ vmsg(buf);
+ break;
+
+ default:
+ /* itoc.010814: ¤£¸Ó¦³¤£¦A¤W¦C½d³ò¤¤ªº³á :p */
+ sprintf(buf, "½Ð§i¶D¯¸ªø¡A¡i%s¡jªºÄݩʡi%d¡j³]©w¿ù»~", m.name, m.attribute);
+ vmsg(buf);
+ break;
+ }
+}
+
+
+ /*-----------------------------------------------------*/
+ /* ¾Ô°«ª«²z§ðÀ» */
+ /*-----------------------------------------------------*/
+
+
+/* itoc.010731: ©Çª«©M¤pÂûªºª«²z§ðÀ»¡A¥i¥H¼g¦¨¦P¤@¤ä¨ç¦¡ */
+
+static void
+pip_attack_normal(who, dr, hr) /* itoc.010731: ´¶³q§ðÀ» */
+ int who; /* §Ú¬O½Ö 1: ¤pÂû¤U§ðÀ»«ü¥O 0: ©Çª«¤U§ðÀ»«ü¥O */
+ int dr; /* ¶Ë®`¤O damage rate */
+ int hr; /* ©R¤¤²v hit rate */
+{
+ int injure;
+ char buf[80];
+
+ injure = hr * (150 - rand() % 100) / 100; /* ¨M©w¬O§_©R¤¤ */
+
+ if (who) /* ¤pÂû§ðÀ» */
+ {
+ d_resistmore = 0;
+ d_nodone = 0;
+ d.tired += 1 + rand() % 2;
+
+ if (hr > d.level * 10)
+ {
+ /* ¦b¹ï¤è¨S¦³¨¾¿m¤U¡A´¶³q§ðÀ»ªº´Á±æ¶Ë®`¬° dr */
+ injure = dr * (110 - rand() % 20) * (100 - m_resistmore) / 10000;
+
+ if (injure > 0)
+ {
+ m.hp -= injure;
+ d.hexp += rand() % 2 + 2;
+ d.hskill += rand() % 2 + 1;
+ sprintf(buf, "´¶³q§ðÀ»¡A%s¥Í©R¤O´î§C %d", m.name, injure);
+ }
+ else
+ {
+ sprintf(buf, "±zªº§ðÀ»Â²ª½¬O´À%s§ìÄo", m.name);
+ }
+ }
+ else
+ {
+ strcpy(buf, "³ºµM¨S¥´¤¤");
+ }
+ }
+ else /* ©Çª«§ðÀ» */
+ {
+ m_resistmore = 0;
+
+ if (injure > d.level * 10)
+ {
+ injure = dr * (110 - rand() % 20) * (100 - d_resistmore) / 10000;
+
+ if (injure > 0)
+ {
+ d.hp -= injure;
+ sprintf(buf, "%s´¶³q§ðÀ»¡A±z¥Í©R¤O´î§C %d", m.name, injure);
+ }
+ else
+ {
+ strcpy(buf, "±z§¹¥þ¬Ý¤£°_¹ï¤èªº§ðÀ»");
+ }
+ }
+ else
+ {
+ strcpy(buf, "±z°{¸ú¹L¹ï¤èªº§ðÀ»");
+ }
+ }
+
+ vmsg(buf);
+}
+
+
+static void
+pip_attack_aggressive(who, dr, hr) /* itoc.010731: ¥þ¤O¤@À» */
+ int who; /* §Ú¬O½Ö 1: ¤pÂû¤U§ðÀ»«ü¥O 0: ©Çª«¤U§ðÀ»«ü¥O */
+ int dr; /* ¶Ë®`¤O damage rate */
+ int hr; /* ©R¤¤²v hit rate */
+{
+ int injure;
+ char buf[80];
+
+ injure = hr * (200 - rand() % 200) / 100; /* ¨M©w¬O§_©R¤¤¡A¥þ¤O¤@À»ªº¶Ã¼Æ¼vÅT¤ñ¸û¤j */
+
+ if (who) /* ¤pÂû§ðÀ» */
+ {
+ d_resistmore = 0;
+ d_nodone = 0;
+ d.hp -= 5; /* ¥þ¤O¤@À»­n¦©¦å¥B¤ñ¸û®e©ö²Ö */
+ d.tired += 1 + rand() % 3;
+
+ if (hr > d.level * 10)
+ {
+ /* ¦b¹ï¤è¨S¦³¨¾¿m¤U¡A¥þ¤O§ðÀ»ªº´Á±æ¶Ë®`¬° 120% * dr */
+ injure = dr * (130 - rand() % 20) * (100 - m_resistmore) / 10000;
+
+ if (injure > 0)
+ {
+ m.hp -= injure;
+ d.hexp += rand() % 3 + 2;
+ d.hskill += rand() % 3 + 1;
+ sprintf(buf, "±z¥þ¤O¤@À»¡A%s¥Í©R¤O´î§C %d", m.name, injure);
+ }
+ else
+ {
+ sprintf(buf, "±zªº§ðÀ»Â²ª½¬O´À%s§ìÄo", m.name);
+ }
+ }
+ else
+ {
+ strcpy(buf, "³ºµM¨S¥´¤¤");
+ }
+ }
+ else /* ©Çª«§ðÀ» */
+ {
+ m_resistmore = 0;
+
+ if (injure > d.level * 10)
+ {
+ injure = dr * (130 - rand() % 20) * (100 - d_resistmore) / 10000;
+
+ if (injure > 0)
+ {
+ d.hp -= injure;
+ sprintf(buf, "%s¥þ¤O¤@À»¡A±z¥Í©R¤O´î§C %d", m.name, injure);
+ }
+ else
+ {
+ strcpy(buf, "±z§¹¥þ¬Ý¤£°_¹ï¤èªº§ðÀ»");
+ }
+ }
+ else
+ {
+ strcpy(buf, "±z°{¸ú¹L¹ï¤èªº§ðÀ»");
+ }
+ }
+
+ vmsg(buf);
+}
+
+
+
+/*-------------------------------------------------------*/
+/* ¹ï¾Ô¥D¨ç¦¡ */
+/*-------------------------------------------------------*/
+
+
+static void
+pip_vs_showing()
+{
+ int color;
+ char inbuf1[20], inbuf2[20];
+
+ clear();
+ move(0, 0);
+
+ prints("\033[1;41m " BBSNAME PIPNAME " ¡ã\033[32m%s\033[37m%-13s \033[m\n",
+ d.sex == 1 ? "¡ñ" : (d.sex == 2 ? "¡ð" : "¡H"), d.name);
+
+ /* itoc.010801: ¿Ã¹õ¤W¤è¨q¥X¤pÂûªº¸ê®Æ */
+
+ if (d.tired >= 80)
+ color = 31;
+ else if (d.tired >= 60 && d.tired < 80)
+ color = 33;
+ else
+ color = 37;
+
+ sprintf(inbuf1, "%d%s/%d%s", d.hp > 1000 ? d.hp / 1000 : d.hp,
+ d.hp > 1000 ? "K" : "", d.maxhp > 1000 ? d.maxhp / 1000 : d.maxhp,
+ d.maxhp > 1000 ? "K" : "");
+ sprintf(inbuf2, "%d%s/%d%s", d.mp > 1000 ? d.mp / 1000 : d.mp,
+ d.mp > 1000 ? "K" : "", d.maxmp > 1000 ? d.maxmp / 1000 : d.maxmp,
+ d.maxmp > 1000 ? "K" : "");
+
+ outs("\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¢w¢w¢w¢w¢w¢w¢w¢w¢w¢{\033[m\n");
+ prints("\033[1;31m¢x\033[33m¥Í ©R:\033[37m%-12s\033[33mªk ¤O:\033[37m%-12s\033[33m¯h ³Ò:\033[%dm%-12d\033[33mª÷ ¿ú:\033[37m%-12d\033[31m¢x\033[m\n", inbuf1, inbuf2, color, d.tired, d.money);
+ prints("\033[1;31m¢x\033[33m§ð À»:\033[37m%-12d\033[33m¨¾ ¿m:\033[37m%-12d\033[33m³t «×:\033[37m%-12d\033[33m¸g Åç:\033[37m%-12d\033[31m¢x\033[m\n", d.attack, d.resist, d.speed, d.exp);
+ prints("\033[1;31m¢x\033[33m­¹ ª«:\033[37m%-12d\033[33m¤j¸É¤Y:\033[37m%-12d\033[33m¹s ­¹:\033[37m%-12d\033[33mÆF ªÛ:\033[37m%-12d\033[31m¢x\033[m\n", d.food, d.burger, d.cookie, d.medicine);
+ outs("\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¢w¢w¢w¢w¢w¢w¢w¢w¢w¢}\033[m");
+
+ /* itoc.010801: ¿Ã¹õ¤¤¶¡ 7~16 ¦C ¨q¥X©Çª«ªº¹ÏÀÉ */
+ show_badman_pic(m.pic);
+
+ /* itoc.010801: ¿Ã¹õ¤U¤è¨q¥X©Çª«ªº¸ê®Æ */
+
+ sprintf(inbuf1, "%d%s/%d%s", m.hp > 1000 ? m.hp / 1000 : m.hp,
+ m.hp > 1000 ? "K" : "", m.maxhp > 1000 ? m.maxhp / 1000 : m.maxhp,
+ m.maxhp > 1000 ? "K" : "");
+
+ move(18, 0);
+ outs("\033[1;34m¢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¢w¢w¢w¢w¢w¢{\033[m\n");
+ prints("\033[1;34m¢x\033[32m©m ¦W:\033[37m%-12s\033[32m¥Í ©R:\033[37m%-12s\033[32m¨¾ Å@:\033[37m%-12d\033[32m°{ Á×:\033[37m%-12d\033[34m¢x\033[m\n", m.name, inbuf1, m.armor, m.dodge);
+ prints("\033[1;34m¢x\033[32m§ð À»:\033[37m%-12d\033[32m¤º ¤O:\033[37m%-12d\033[32mÅ] ªk:\033[37m%-12d\033[32mª÷ ¿ú:\033[37m%-12d\033[34m¢x\033[m\n", m.attack, m.spirit, m.magic, m.money);
+ outs("\033[1;34m¢|¢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¢}\033[m\n");
+
+ out_cmd("", COLOR1 " ¾Ô°«©R¥O " COLOR2 " [1]´¶³q [2]¥þ¤O [3]§Þ¯à [4]¨¾¿m [5]¸É¥R [6]·Ò§¯ [A]¦Û°Ê [Q]°k©R \033[m");
+}
+
+
+static void
+pip_vs_ending(winorlost, area)
+ int winorlost; /* 2:K¦º©Çª« 1:©Çª«°k¶] -1:¤pÂû°k¶] -2:¤pÂû³QK¦º */
+ char area; /* !=0: K©Çª« 0:¦¬Ã¬©u¤ñÁÉ */
+{
+ int mode;
+
+ mode = winorlost;
+ if (!area)
+ mode += 8;
+
+ clrfromto(7, 16);
+ move(8, 0);
+
+ /* itoc.010731: ¦¬Ã¬©u¤ñÁÉ­Y¬O¿é¤F¡A«h«ì´_¤@¨Ç¦å¡F­Y¬OŤF¡A«ì´_¥þ³¡¦å */
+ /* itoc.010731: ¾Ô³Ó¥[¸gÅç­È/¿ú¡A¾Ô±Ñ¦©¿ú/ÄݩʡF¦ý¦¬Ã¬©u¾Ô±Ñ¤£¦©¿ú/ÄÝ©Ê */
+
+ switch (mode)
+ {
+ case 10: /* ¦¬Ã¬©u KO */
+ d.tired = 0;
+ d.hp = d.maxhp;
+ d.hexp += rand() % 3 + 2;
+ d.mexp += rand() % 3 + 2;
+ d.exp += m.exp;
+ outs(" \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¢{\033[m\n");
+ prints(" \033[1;31m¢x \033[37mªZ³N¤j·|ªº¤pÂû\033[33m%-13s \033[31m¢x\033[m\n", d.name);
+ prints(" \033[1;31m¢x \033[37m¥´±Ñ¤F±j«lªº¹ï¤â\033[32m%-13s \033[31m¢x\033[m\n", m.name);
+ outs(" \033[1;31m¢x \033[37m«i´±©M¸gÅç³£¤W¤É¤F¤£¤Ö \033[31m¢x\033[m\n");
+ outs(" \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¢}\033[m");
+ vmsg("±z¥´±Ñ¤F¤@­Ó±jµwªº³Ã¥ë");
+ break;
+
+ case 9: /* ¦¬Ã¬©u win */
+ d.tired = 0;
+ d.hp = d.maxhp;
+ d.hexp += rand() % 2 + 1;
+ d.mexp += rand() % 2 + 1;
+ d.exp += m.exp * (50 + rand() % 20) / 100; /* ±o 60% ªº¸gÅç­È */
+ outs(" \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¢{\033[m\n");
+ prints(" \033[1;31m¢x \033[37mªZ³N¤j·|ªº¤pÂû\033[33m%-13s \033[31m¢x\033[m\n", d.name);
+ prints(" \033[1;31m¢x \033[37m¥´±Ñ¤F±j«lªº¹ï¤â\033[32m%-13s \033[31m¢x\033[m\n", m.name);
+ outs(" \033[1;31m¢x \033[37m«i´±©M¸gÅç³£¤W¤É¤F¤@¨Ç \033[31m¢x\033[m\n");
+ outs(" \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¢}\033[m");
+ vmsg("¤£­t²³±æ¦a¡A±z¸Ñ¨M¤F³o­ÓÃøÄñªº¼Ä¤â");
+ break;
+
+ case 7: /* ¦¬Ã¬©u lose */
+ d.tired = 50;
+ d.hp = d.maxhp / 3;
+ d.hexp -= rand() % 2 + 1;
+ d.mexp -= rand() % 2 + 1;
+ if (d.hexp < 0)
+ d.hexp = 0;
+ if (d.mexp < 0)
+ d.mexp = 0;
+ outs(" \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¢{\033[m\n");
+ prints(" \033[1;31m¢x \033[37mªZ³N¤j·|ªº¤pÂû\033[33m%-13s \033[31m¢x\033[m\n", d.name);
+ prints(" \033[1;31m¢x \033[37m³Q\033[32m%-13s\033[37m¹ï¤â¥´±o¸¨ªá¬y¤ô \033[31m¢x\033[m\n", m.name);
+ outs(" \033[1;31m¢x \033[37m¨M©w¦^®a¦n¦n¦AÁë½m \033[31m¢x\033[m\n");
+ outs(" \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¢}\033[m");
+ vmsg("¸¨¶]ªº±z¤ß¤¤¬Û·í¤£¬O¨ý¹D");
+ break;
+
+ case 6: /* ¦¬Ã¬©u KO-ed */
+ d.tired = 50;
+ d.hp = d.maxhp / 3;
+ d.hexp -= rand() % 3 + 2;
+ d.mexp -= rand() % 3 + 2;
+ if (d.hexp < 0)
+ d.hexp = 0;
+ if (d.mexp < 0)
+ d.mexp = 0;
+ outs(" \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¢{\033[m\n");
+ prints(" \033[1;31m¢x \033[37mªZ³N¤j·|ªº¤pÂû\033[33m%-13s \033[31m¢x\033[m\n", d.name);
+ prints(" \033[1;31m¢x \033[37m§¹¥þ¤£¬O\033[32m%-13s\033[37mªº¹ï¤â \033[31m¢x\033[m\n", m.name);
+ outs(" \033[1;31m¢x \033[37mµo»}©ú¦~ÁÙ­n±²¤g­«¨Ó \033[31m¢x\033[m\n");
+ outs(" \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¢}\033[m");
+ vmsg("±z½ö¦b¦a¤W©a©a¤@®§");
+ break;
+
+ case 2: /* KO ©Çª« */
+ d.money += m.money;
+ d.exp += m.exp;
+ d.exp += m.exp;
+ d.brave += rand() % 4 + 3;
+ outs(" \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¢{\033[m\n");
+ prints(" \033[1;31m¢x \033[37m­^«iªº¤pÂû\033[33m%-13s \033[31m¢x\033[m\n", d.name);
+ prints(" \033[1;31m¢x \033[37m¥´±Ñ¤F¨¸´cªº©Çª«\033[32m%-13s \033[31m¢x\033[m\n", m.name);
+ outs(" \033[1;31m¢x \033[37m«i´±©M¸gÅç³£¤W¤É¤F¤£¤Ö \033[31m¢x\033[m\n");
+ outs(" \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¢}\033[m");
+ vmsg("¹ï¤è¦º±¼¤F°Ú¡A©Ò¥H±zÀò³Ó¤F³á");
+ break;
+
+ case 1: /* ©Çª«°k¶] */
+ d.money += m.money * (30 + rand() % 20) / 100; /* ±o 40% ªº¿ú */
+ d.exp += m.exp * (50 + rand() % 20) / 100; /* ±o 60% ªº¸gÅç­È */
+ d.brave += rand() % 3 + 2;
+ outs(" \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¢{\033[m\n");
+ prints(" \033[1;31m¢x \033[37m­^«iªº¤pÂû\033[33m%-13s \033[31m¢x\033[m\n", d.name);
+ prints(" \033[1;31m¢x \033[37m¥´±Ñ¤F¨¸´cªº©Çª«\033[32m%-13s \033[31m¢x\033[m\n", m.name);
+ outs(" \033[1;31m¢x \033[37m«i´±©M¸gÅç³£¤W¤É¤F¤@¨Ç \033[31m¢x\033[m\n");
+ outs(" \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¢}\033[m");
+ vmsg("¹ï¤è¥ý°{¤F..¦ý±¼¤F¤@¨Ç¿úµ¹±z...");
+ break;
+
+ case -1: /* ¤pÂû°k¶] */
+ d.money -= d.level * 5 + rand() % 100;
+ d.brave -= rand() % 3 + 2;
+ if (d.money < 0)
+ d.money = 0;
+ if (d.brave < 0)
+ d.brave = 0;
+ outs(" \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¢{\033[m\n");
+ prints(" \033[1;31m¢x \033[37m¥i¼¦ªº¤pÂû\033[33m%-13s \033[31m¢x\033[m\n", d.name);
+ prints(" \033[1;31m¢x \033[37m¦b»P\033[32m%-13s\033[37mªº¾Ô°«¤¤ \033[31m¢x\033[m\n", m.name);
+ outs(" \033[1;31m¢x \033[37m¤£©¯¦a¥´¿é¤F¡A¦b¦¹¯S§OÃø¹L.......... \033[31m¢x\033[m\n");
+ outs(" \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¢}\033[m");
+ vmsg("¤pÂû¥´¿é¤F....");
+ break;
+
+ case -2: /* ¤pÂû KO-ed */
+ outs(" \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¢{\033[m\n");
+ prints(" \033[1;31m¢x \033[37m¥i¼¦ªº¤pÂû\033[33m%-13s \033[31m¢x\033[m\n", d.name);
+ prints(" \033[1;31m¢x \033[37m¦b»P\033[32m%-13s\033[37mªº¾Ô°«¤¤ \033[31m¢x\033[m\n", m.name);
+ outs(" \033[1;31m¢x \033[37m¤£©¯¦a°}¤`¤F¡A¦b¦¹¯S§OÀq«s.......... \033[31m¢x\033[m\n");
+ outs(" \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¢}\033[m");
+ vmsg("¤pÂû°}¤`¤F....");
+ pipdie("\033[1;31m¾Ô°«¤¤³Q¥´¦º¤F...\033[m ", 1);
+ break;
+ }
+}
+
+
+/* static */ /* itoc.010731: µ¹¦¬Ã¬©u¹ï¾Ô¥Î */
+int /* 1: ¤pÂû win 0: ¤pÂû lose */
+pip_vs_man(p, area)
+ struct playrule p; /* ¶Ç¤J©Çª«¸ê®Æ */
+ int area; /* !=0: K©Çª«®Éªº°Ï°ì 0: ¦¬Ã¬©u¤ñÁÉ */
+{
+ int d_dr; /* ¤pÂû¥´©Çª« ª«²z§ðÀ»«ü¼Æ damage rate */
+ int d_sr; /* ¤pÂû¥´©Çª« ªZ¥\§ðÀ»«ü¼Æ spirit rate */
+ int d_mr; /* ¤pÂû¥´©Çª« Å]ªk§ðÀ»«ü¼Æ magic rate */
+ int d_hr; /* ¤pÂû¥´©Çª« ª«²z/ªZ¥\/Å]ªk§ðÀ»©R¤¤²v hit rate */
+
+ int m_dr; /* ©Çª«¥´¤pÂû ª«²z§ðÀ»«ü¼Æ damage rate */
+ int m_sr; /* ©Çª«¥´¤pÂû ªZ¥\§ðÀ»«ü¼Æ spirit rate */
+ int m_mr; /* ©Çª«¥´¤pÂû Å]ªk§ðÀ»«ü¼Æ magic rate */
+ int m_hr; /* ©Çª«¥´¤pÂû ª«²z/ªZ¥\/Å]ªk§ðÀ»©R¤¤²v hit rate */
+
+ int randnum; /* random number */
+ int pipkey; /* ¤pÂû¤Uªº«ü¥O */
+ int mankey; /* ©Çª«¤Uªº«ü¥O */
+ char buf[80];
+
+ /* ²£¥Í©Çª«¸ê®Æ */
+ if (area) /* K©Çª« */
+ {
+ badman_generate(area);
+ }
+ else /* ¦¬Ã¬©u¤ñÁÉ */
+ {
+ strcpy(m.name, p.name);
+ m.attribute = p.attribute;
+ m.hp = p.hp;
+ m.maxhp = p.maxhp;
+ m.attack = p.attack;
+ m.spirit = p.spirit;
+ m.magic = p.magic;
+ m.armor = p.armor;
+ m.dodge = p.dodge;
+ m.money = p.money;
+ m.pic = p.pic;
+ }
+
+ /* itoc.010731: ¤@¶}©l´Nºâ¦n¤@¨Ç°Ñ¼Æ¡AµM«áµ¥¤@¤Uª½±µ¶Ç¤J¨ç¼Æ¨D¶Ë®`­È */
+ d_dr = d.attack + (d.hskill + d.hexp) / 10 - m.armor;
+ d_sr = (d.attack + d.brave) / 2 + (d.hskill + d.hexp) / 10 - m.armor;
+ d_mr = d.mskill + (d.immune + d.mexp) / 10 - m.armor;
+ d_hr = d.speed + (d.hskill + d.hexp) / 10 - m.dodge;
+ m_dr = m.attack - d.resist;
+ m_sr = m.spirit - (d.speed + d.resist) / 2;
+ m_mr = m.magic - d.immune;
+ m_hr = m.attack - d.speed;
+
+ /* itoc.020718: ­nÀˬd³o¨Ç°Ñ¼Æ¡A½T©w¬O¥¿ªº */
+ if (d_dr <= 0)
+ d_dr = 1;
+ if (d_sr <= 0)
+ d_sr = 1;
+ if (d_mr <= 0)
+ d_mr = 1;
+ if (d_hr <= 0)
+ d_hr = 1;
+ if (m_dr <= 0)
+ m_dr = 1;
+ if (m_sr <= 0)
+ m_sr = 1;
+ if (m_mr <= 0)
+ m_mr = 1;
+ if (m_hr <= 0)
+ m_hr = 1;
+
+ /* itoc.010820: ¦pªG¦³¯S®í§Þ¯à¡A¥i¥H¦A¥[¦¨ */
+ if (d.skillXYZ & 0x0001) /* ¥ª¥k¤¬·i */
+ d_dr += d.level << 3;
+ else if (d.skillXYZ & 0x0002) /* ¤O©Þ¤sªe */
+ d_sr += d.level << 3;
+ else if (d.skillXYZ & 0x0004) /* Å]³êºëÆF */
+ d_mr += d.level << 3;
+ else if (d.skillXYZ & 0x0008) /* §Ö°¨¥[Ã@ */
+ d_hr += d.level << 3;
+
+ /* itoc.010730: ¨M©w½Ö¥ý§ðÀ» */
+ if (d.skillXYZ & 0x0040) /* Àu¥ý§ðÀ» */
+ {
+ d_nodone = 1;
+ }
+ else
+ {
+ d_nodone = rand() % 10 > 2; /* 30% ¥Ñ©Çª«¥ý§ðÀ» */
+ if (!d_nodone)
+ {
+ sprintf(buf, "±z³Q%s°½Å§¤F", m.name);
+ vmsg(buf);
+ }
+ }
+
+ d_resistmore = 0;
+ m_resistmore = 0;
+ pipkey = 0;
+
+ for (;;) /* µL½a°j°é */
+ {
+ /* ¨q¥X¾Ô°«¥Dµe­± */
+ pip_vs_showing();
+
+ while (d_nodone)
+ {
+ if (pipkey != 'a') /* itoc.010820: ¦Û°Ê§ðÀ» */
+ pipkey = vkey();
+
+ switch (pipkey) /* ¤pÂû¤U«ü¥O */
+ {
+ case 'a': /* ¦Û°Ê§ðÀ»¥u°õ¦æ´¶³q§ðÀ» */
+ case '1': /* ´¶³q§ðÀ» */
+ show_fight_pic(1);
+ pip_attack_normal(1, d_dr, d_hr);
+ break;
+
+ case '2': /* ¥þ¤O¤@À» */
+ if (d.hp > 5)
+ {
+ show_fight_pic(2);
+ pip_attack_aggressive(1, d_dr, d_hr);
+ }
+ else
+ {
+ vmsg("±zÅé¤O³o»ò®t¤FÁÙ·Q¥þ¤O¤@À»");
+ }
+ break;
+
+ case '3': /* §Þ¯à: ªZ¥\/Å]ªk */
+ pip_skill_menu(d_dr, d_sr, d_mr, d_hr);
+ if (d_nodone) /* ¶i¤J skill_menu ¥X¨Ó­YÁÙ¬O d_nodone¡Aªí¥Ü©ñ±ó¨Ï¥Î§Þ¯à¡A­n­«Ã¸«ü¥O */
+ out_cmd("", COLOR1 " ¾Ô°«©R¥O " COLOR2 " [1]´¶³q [2]¥þ¤O [3]§Þ¯à [4]¨¾¿m [5]¸É¥R [6]·Ò§¯ [A]¦Û°Ê [Q]°k©R \033[m");
+ break;
+
+ case '4': /* ¨¾¿m */
+ d_resistmore = 40; /* ©Çª«§ðÀ»´î¤Ö 40% */
+ d.tired += rand() % 2 + 1;
+ d_nodone = 0;
+ show_fight_pic(3);
+ vmsg("¤pÂû¥[±j¨¾¿m°Õ....");
+ break;
+
+ case '5': /* ¦Y¸É«~ */
+ d_nodone = pip_basic_feed(); /* feed §¹¶Ç¦^ 0 ªí¥Ü¦Y§¹¤F¡A¶Ç¦^ 1 ªí¥Ü©ñ±ó¨S¦Y */
+ pip_vs_showing(); /* itoc.010801: »Ý­n§¹¾ã­«Ã¸ */
+
+ if (d.skillXYZ & 0x0020) /* ±o¨Ó³t */
+ d_nodone = 1;
+ break;
+
+ case '6': /* ·Ò§¯ */
+ if (m.hp < m.maxhp * (d.level - rand() % 150) / 100)
+ {
+ d.mexp += 8;
+ m.hp = 0;
+ vmsg("®·®»¦¨¥\\¡A¦WÁn¼W¥[");
+ }
+ else
+ {
+ vmsg("®·®»¥¢±Ñ");
+ }
+ d_nodone = 0;
+ break;
+
+ case 'q': /* °k¶] */
+ pip_vs_ending(-1, area);
+ return 0;
+ }
+ }
+
+ /* ¤pÂû§ðÀ»«á¡A»Ý­nÀˬd¤pÂû/©Çª«ª¬ºA */
+ if (m.hp <= 0) /* ©Çª«¦º±¼¤F */
+ {
+ pip_vs_ending(2, area);
+ return 1;
+ }
+ else if (d.tired >= 100)
+ {
+ /* ¤pÂû²Ö¦º¤F¡A¨¾¿m¤O¤j­° */
+ vmsg("±z¤w¸g¤Ó¯h²Ö¤F¡A¨¾¿m¤O¤j´T­°§C");
+ d_resistmore = -100; /* ©Çª«§ðÀ»¥[ 100% */
+ }
+
+
+ /* ¨ì¦¹¤pÂû¤w¤U§¹«ü¥O¡A¸Ó©Çª«¤U«ü¥O */
+
+ /* ¨M©w©Çª«¤Uªº«ü¥O */
+ randnum = rand() % 100;
+ if (m.attribute) /* ¦³¯S®íªº§Þ¯à */
+ {
+ if (randnum < 40)
+ mankey = 1; /* 40% ´¶³q§ðÀ» */
+ else if (randnum < 50)
+ mankey = 2; /* 10% ¥þ¤O§ðÀ» */
+ else if (randnum < 90)
+ mankey = 3; /* 40% §Þ¯à§ðÀ» */
+ else if (randnum < 98)
+ mankey = 4; /* 8% ¥[±j¨¾¿m */
+ else
+ mankey = 5; /* 2% °k¤§¤Ô¤Ô */
+ }
+ else
+ {
+ if (randnum < 40)
+ mankey = 1; /* 40% ´¶³q§ðÀ» */
+ else if (randnum < 65)
+ mankey = 2; /* 25% ¥þ¤O§ðÀ» */
+ else if (randnum < 90)
+ mankey = 3; /* 25% §Þ¯à§ðÀ» */
+ else if (randnum < 98)
+ mankey = 4; /* 8% ¥[±j¨¾¿m */
+ else
+ mankey = 5; /* 2% °k¤§¤Ô¤Ô */
+ }
+
+ switch (mankey)
+ {
+ case 2: /* ©Çª«¥þ¤O§ðÀ» */
+ if (m.hp > 5) /* ­Y©Çª«¦å¤£¨ì¤­ÂI¡AÅÜ´¶³q§ðÀ» */
+ {
+ show_fight_pic(52);
+ pip_attack_aggressive(0, m_dr, m_hr);
+ break;
+ }
+
+ case 1: /* ©Çª«´¶³q§ðÀ» */
+ show_fight_pic(51);
+ pip_attack_normal(0, m_dr, m_hr);
+ break;
+
+ case 3: /* ©Çª«§Þ¯à§ðÀ» */
+ pip_attack_skill(m_dr, m_sr, m_mr, m_hr);
+ break;
+
+ case 4: /* ©Çª«¨¾¿m */
+ m_resistmore = 40; /* ¤pÂû§ðÀ»´î¤Ö 40% */
+ break;
+
+ case 5: /* ©Çª«°k¶] */
+ pip_vs_ending(1, area);
+ return 1;
+ }
+
+ /* ©Çª«§ðÀ»«á¡A¥u»Ý­nÀˬd¤pÂûª¬ºA */
+
+ if (d.hp <= 0) /* ¤pÂû¦º±¼¤F */
+ {
+ pip_vs_ending(-2, area);
+ return 0;
+ }
+
+ d_nodone = 1; /* ¤S¸Ó¤pÂû¤F */
+
+ } /* µ²§ô for °j°é¡A¾Ô°«µ²§ô */
+}
+
+
+/*-------------------------------------------------------*/
+/* ¦a¹Ï²£¥Í¾¹ */
+/*-------------------------------------------------------*/
+
+
+/* itoc.041017: ¬°¤F¦h¼Ë©Ê¡A¼g¤@­Ó¦a¹Ï²£¥Í¾¹ */
+
+/* ¦b³o¶¡©Ð¶¡¸Ì¦³­þ¨Ç¤è¦V¥i¥H«e¶i */
+#define MAP_EAST 0x01
+#define MAP_WEST 0x02
+#define MAP_SOUTH 0x04
+#define MAP_NORTH 0x08
+
+
+static int /* ¶Ç¦^¥»©Ð¶¡¦³­þ¨Ç¤è¦V¥i¥H¨« */
+map_generate()
+{
+ int direction;
+ int map[16] =
+ {
+ MAP_EAST | MAP_WEST | MAP_SOUTH | MAP_NORTH,
+ MAP_EAST, MAP_WEST, MAP_SOUTH, MAP_NORTH,
+ MAP_EAST | MAP_WEST, MAP_EAST | MAP_SOUTH, MAP_EAST | MAP_NORTH, MAP_WEST | MAP_SOUTH, MAP_WEST | MAP_NORTH, MAP_SOUTH | MAP_NORTH,
+ MAP_EAST | MAP_WEST | MAP_SOUTH, MAP_EAST | MAP_WEST | MAP_NORTH, MAP_EAST | MAP_SOUTH | MAP_NORTH, MAP_WEST | MAP_SOUTH | MAP_NORTH,
+ MAP_EAST | MAP_WEST | MAP_SOUTH | MAP_NORTH
+ };
+
+ /* ¨M©w³o¶¡©Ð¶¡¦³­þ¨Ç¥X¤f */
+
+ direction = map[rand() & 15]; /* ¨M©w³o¶¡©Ð¶¡¦³­þ¨Ç¥X¤f */
+ move(16, 0);
+ prints(" ¥X¤f¡G\033[1m%s%s%s%s\033[m",
+ direction & MAP_EAST ? "\033[31mªF " : "",
+ direction & MAP_WEST ? "\033[32m¦è " : "",
+ direction & MAP_SOUTH ? "\033[33m«n " : "",
+ direction & MAP_NORTH ? "\033[34m¥_" : "");
+
+ return direction;
+}
+
+
+/*-------------------------------------------------------*/
+/* ¾Ô°«¥D¿ï³æ */
+/*-------------------------------------------------------*/
+
+
+int
+pip_fight_menu()
+{
+ int ch, area, direction;
+
+ show_badman_pic(0);
+ out_cmd(COLOR1 " ¥Ò°Ï " COLOR2 " [1]ª¢¤§¬}¸] [2]¥_¤è¦B­ì [3]¥j¥N¿ò¸ñ [Q]Â÷¶} \033[m",
+ COLOR1 " ¤A°Ï " COLOR2 " [4]¤H¤u®qÀ¬ [5]¦aº»¤§ªù [6]ª÷±e¸s«L [Q]Â÷¶} \033[m");
+
+ while (1)
+ {
+ area = vkey();
+ if (area == 'q')
+ return 0;
+ if (area >= '1' && area <= '6')
+ break;
+ }
+
+ clrfromto(7, 17);
+
+ while (d.hp > 0)
+ {
+ direction = map_generate();
+ out_cmd("", COLOR1 " °Ï°ì " COLOR2 " [F]Áý­¹ [E/W/S/N]ªF/¦è/«n/¥_ [Q]¦^®a \033[m");
+
+re_key:
+ ch = vkey();
+
+ if (ch == 'q')
+ break;
+
+ if (ch == 'f') /* Áý­¹ */
+ {
+ pip_basic_feed();
+ continue;
+ }
+
+
+ /* ¨ä¥L«öÁä³£¬O¤è¦V */
+
+ ch = ch == 'e' ? MAP_EAST : ch == 'w' ? MAP_WEST : ch == 's' ? MAP_SOUTH : ch == 'n' ? MAP_NORTH : 0;
+ if (!(ch & direction)) /* ³o­Ó¤è¦V¤£¯à«e¶i */
+ goto re_key; /* ¤£»Ý­n­«·s²£¥Í¦a¹Ï */
+
+ if ((ch == 'e' && !(direction & MAP_EAST)) || (ch == 'e' && !(direction & MAP_EAST)) ||
+ (ch == 'e' && !(direction & MAP_EAST)) || (ch == 'e' && !(direction & MAP_EAST)))
+
+ if (d.vp < 10) /* ¾Ô°«¦a¹Ï¤¤²¾°Ê¦©²¾°Ê¤O¡A¦Ü¤Ö 10 ¤~¯à²¾°Ê */
+ {
+ vmsg("±z¤w²Ö±oµLªk¦A¦æ°Ê");
+ continue; /* ¥i¥H¥ÎÁý­¹Ä~Äò */
+ }
+
+ d.vp -= 2;
+
+ if (rand() % 3) /* ¤T¤À¤§¤Gªº¾÷·|¹J¨ì¼Ä¤H */
+ {
+ pip_vs_man(m, area); /* ¶i¤J¾Ô°« */
+ }
+ else
+ {
+ if (d.skillXYZ & 0x0010) /* ´MÀs³Z */
+ {
+ d.hp += (d.maxhp >> 2);
+ if (d.hp > d.maxhp)
+ d.hp = d.maxhp;
+ vmsg("±zªu¸ô¬°¤H§®ºt¤Ñ¾÷");
+ }
+ else
+ {
+ vmsg("¨Sµo¥Í¥ô¦ó¨Æ¡I");
+ }
+ }
+ }
+
+ /* itoc.010730: ¦bÂ÷¶}¾Ô°«¿ï³æ®É¤~¤@¨ÖÀˬd¬O§_¤É¯Å */
+ pip_check_levelup();
+
+ return 0;
+}
+#endif /* HAVE_GAME */
diff --git a/pip/pip_item.c b/pip/pip_item.c
new file mode 100644
index 0000000..0f00d01
--- /dev/null
+++ b/pip/pip_item.c
@@ -0,0 +1,256 @@
+/* ----------------------------------------------------- */
+/* pip_item.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/* ----------------------------------------------------- */
+/* target : ¤pÂû item */
+/* create : / / */
+/* update : 01/08/14 */
+/* author : dsyan.bbs@forever.twbbs.org */
+/* modify : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/* ----------------------------------------------------- */
+
+
+#include "bbs.h"
+
+#ifdef HAVE_GAME
+
+#include "pip.h"
+
+
+struct itemset pipfoodlist[] =
+{
+ /* name msgbuy msgfeed price */
+ 0, "ª««~¦W", "ÁʶR¶·ª¾", "¨Ï¥Î¶·ª¾", 0,
+ 1, "¦n¦Yªº­¹ª«", "Åé¤O«ì´_ 50", "¨C¦Y¤@¦¸­¹ª«·|«ì´_Åé¤O 50 ³á", 50,
+ 2, "¬ü¨ýªº¹s­¹", "Åé¤O«ì´_ 100", "°£¤F«ì´_Åé¤O¡A¤pÂû¤]·|§ó§Ö¼Ö", 120,
+ 0, NULL, NULL, NULL, 0
+};
+
+
+struct itemset pipmedicinelist[] =
+{
+ /* name msgbuy msgfeed price */
+ 0, "ª««~¦W", "ÁʶR¶·ª¾", "¨Ï¥Î¶·ª¾", 0,
+ 1, "¸É¦å¤jÁÙ¤¦", "Åé¤O«ì´_ 1000", "«ì´_¤j¶q¬y¥¢Åé¤Oªº¨}¤è", 1000,
+ 2, "¬Ã¶QªºÆFªÛ", "ªk¤O«ì´_ 1000", "«ì´_¤j¶q¬y¥¢ªk¤Oªº¨}¤è", 1000,
+ 3, "¦n¥Î¤j¸É¤Y", "²¾°Ê«ì´_ 1000", "«ì´_¤j¶q¬y¥¢²¾°Êªº¨}¤è", 1000,
+ 4, "¤d¦~¤H°Ñ¤ý", "¤º¤O«ì´_ 1000", "«ì´_¤j¶q¬y¥¢¤º¤Oªº¨}¤è", 1000,
+ 5, "¶Â¥ÉÂ_Äò»I", "Åé¤O§¹¥þ«ì´_", "¶Ç»¡¤¤¯à±N©Ò¦³¨ü¶Ë«ì´_ªºÃħ÷", 5000,
+ 6, "¤Ñ¤s³·½¬", "ª¬ºA§¹¥þ«ì´_", "ªF¥_¤Ñ¤s¤~¦³³·½¬¤l", 10000,
+ 0, NULL, NULL, NULL, 0
+};
+
+
+struct itemset pipotherlist[] =
+{
+ /* name msgbuy msgfeed price */
+ 0, "ª««~¦W", "ÁʶR¶·ª¾", "¨Ï¥Î¶·ª¾", 0,
+ 1, "¦Ê¬ì¥þ®Ñ", "ª¾ÃѪº¨Ó·½", "®Ñ¥»Åý¤pÂû§óÁo©ú§ó¦³®ð½è°Õ", 3000,
+ 2, "¼Ö°ªª±¨ã²Õ", "§Ö¼Öº¡·N«×", "ª±¨ãÅý¤pÂû§ó§Ö¼Ö°Õ", 300,
+ 3, "»Õ¼ÓÂø»x", "º¡¨¬ªº§Ö·P", "®Ñ¤¤¦Û¦³ÃC¦p¥É°Õ", 500,
+ 0, NULL, NULL, NULL, 0
+};
+
+
+/* ------------------------------------------------------- */
+/* ª««~ÁʶR¨ç¦¡ */
+/* ------------------------------------------------------- */
+
+
+int
+pip_buy_item(mode, p, oldnum)
+ int mode;
+ int oldnum[];
+ struct itemset *p;
+{
+ char *shopname[4] = {"©±¦W", "«K§Q°Ó©±", "ªø¬KÃľQ", "©]¸Ì®Ñ§½"};
+ char buf[128], genbuf[20];
+ int oldmoney; /* ¶i°Ó©±«e­ì¦³¿ú */
+ int total; /* ÁʶR/³c½æ­Ó¼Æ */
+ int ch, choice;
+
+ oldmoney = d.money;
+
+ /* ¨q¥X²£«~¦Cªí */
+ clrfromto(6, 18);
+ outs("\033[1;31m ¢w\033[41;37m ½s¸¹\033[0;1;31m¢w\033[41;37m °Ó «~\033[0;1;31m¢w¢w\033[41;37m ®Ä ¯à\033[0;1;31m¢w¢w\033[41;37m »ù ®æ\033[0;1;31m¢w\033[37;41m ¾Ö¦³¼Æ¶q\033[0;1;31m¢w\033[m\n\n");
+ for (ch = 1; ch <= oldnum[0]; ch++)
+ {
+ prints(" \033[1;35m[\033[37m%2d\033[35m] \033[36m%-10s \033[37m%-14s \033[1;33m%-10d \033[1;32m%-9d \033[m\n",
+ p[ch].num, p[ch].name, p[ch].msgbuy, p[ch].price, oldnum[ch]);
+ }
+
+ do
+ {
+ sprintf(buf, COLOR1 " ±Ä¶R " COLOR2 " (%8s) [B]¶R¤Jª««~ [S]½æ¥Xª««~ [Q]¸õ¥X \033[m", shopname[mode]);
+ out_cmd("", buf);
+
+ switch (ch = vkey())
+ {
+ case 'b':
+ sprintf(buf, "·Q­n¶R¤JÔ£©O¡H[0]©ñ±ó¶R¤J [1¡ã%d]ª««~°Ó¸¹¡G", oldnum[0]);
+ choice = ians(b_lines - 2, 0, buf) - '0';
+ if (choice >= 1 && choice <= oldnum[0])
+ {
+ sprintf(buf, "±z­n¶R¤Jª««~ [%s] ¦h¤Ö­Ó©O(1-%d)¡H[Q] ", p[choice].name, d.money / p[choice].price);
+ vget(b_lines - 2, 0, buf, genbuf, 6, DOECHO);
+ total = atoi(genbuf);
+
+ if (total <= 0)
+ {
+ vmsg("©ñ±ó¶R¤J...");
+ }
+ else if (d.money < total * p[choice].price)
+ {
+ vmsg("±zªº¿ú¨S¦³¨º»ò¦h³á..");
+ }
+ else
+ {
+ sprintf(buf, "½T©w¶R¤JÁ`»ù¬° %d ªºª««~ [%s] ¼Æ¶q %d ­Ó¶Ü(Y/N)¡H[N] ", total * p[choice].price, p[choice].name, total);
+ if (ians(b_lines - 2, 0, buf) == 'y')
+ {
+ oldnum[choice] += total;
+ d.money -= total * p[choice].price;
+
+ /* itoc.010816: §ó·s¾Ö¦³¼Æ¶q */
+ move(7 + choice, 0);
+ prints(" \033[1;35m[\033[37m%2d\033[35m] \033[36m%-10s \033[37m%-14s \033[1;33m%-10d \033[1;32m%-9d \033[m",
+ p[choice].num, p[choice].name, p[choice].msgbuy, p[choice].price, oldnum[choice]);
+
+ vmsg(p[choice].msguse);
+ }
+ else
+ {
+ vmsg("©ñ±ó¶R¤J...");
+ }
+ }
+ }
+ else
+ {
+ sprintf(buf, "©ñ±ó¶R¤J.....");
+ vmsg(buf);
+ }
+ break;
+
+ case 's':
+ sprintf(buf, "·Q­n½æ¥XÔ£©O¡H[0]©ñ±ó½æ¥X [1¡ã%d]ª««~°Ó¸¹: ", oldnum[0]);
+ choice = ians(b_lines - 2, 0, buf) - '0';
+ if (choice >= 1 && choice <= oldnum[0])
+ {
+ sprintf(buf, "±z­n½æ¥Xª««~ [%s] ¦h¤Ö­Ó©O(1-%d)¡H[Q] ", p[choice].name, oldnum[choice]);
+ vget(b_lines - 2, 0, buf, genbuf, 6, DOECHO);
+ total = atoi(genbuf);
+
+ if (total <= 0)
+ {
+ vmsg("©ñ±ó½æ¥X...");
+ }
+ else if (total > oldnum[choice])
+ {
+ sprintf(buf, "±zªº [%s] ¨S¦³¨º»ò¦h­Ó³á", p[choice].name);
+ vmsg(buf);
+ }
+ else
+ {
+ sprintf(buf, "½T©w½æ¥XÁ`»ù¬° %d ªºª««~ [%s] ¼Æ¶q %d ­Ó¶Ü(Y/N)¡H[N] ", total * p[choice].price * 4 / 5, p[choice].name, total);
+ if (ians(b_lines - 2, 0, buf) == 'y')
+ {
+ oldnum[choice] -= total;
+ d.money += total * p[choice].price * 8 / 10;
+
+ /* itoc.010816: §ó·s¾Ö¦³¼Æ¶q */
+ move(7 + choice, 0);
+ prints(" \033[1;35m[\033[37m%2d\033[35m] \033[36m%-10s \033[37m%-14s \033[1;33m%-10d \033[1;32m%-9d \033[m",
+ p[choice].num, p[choice].name, p[choice].msgbuy, p[choice].price, oldnum[choice]);
+
+ sprintf(buf, "¦ÑÁ󮳨«¤F±zªº %d ­Ó%s", total, p[choice].name);
+ vmsg(buf);
+ }
+ else
+ {
+ vmsg("©ñ±ó½æ¥X...");
+ }
+ }
+ }
+ else
+ {
+ sprintf(buf, "©ñ±ó½æ¥X.....");
+ vmsg(buf);
+ }
+ break;
+
+ case 'q':
+ case KEY_LEFT:
+ sprintf(buf, "ª÷¿ú¥æ©ö¦@ %d ¤¸,Â÷¶} %s ", oldmoney - d.money, shopname[mode]);
+ vmsg(buf);
+ break;
+ }
+
+ /* itoc.010816: ®ø±¼ ians() vget() ¯d¤Uªº´ÝÀe */
+ move (b_lines - 2, 0);
+ clrtoeol();
+
+ } while (ch != 'q' && ch != KEY_LEFT);
+
+ return 0;
+}
+
+
+/*-------------------------------------------------------*/
+/* °Ó©±¿ï³æ:­¹ª« ¹s­¹ ¤j¸É¤Y ª±¨ã ®Ñ¥» */
+/*-------------------------------------------------------*/
+
+/*-------------------------------------------------------*/
+/* ¨ç¦¡®w */
+/*-------------------------------------------------------*/
+
+int
+pip_store_food()
+{
+ int num[3];
+ num[0] = 2;
+ num[1] = d.food;
+ num[2] = d.cookie;
+ pip_buy_item(1, pipfoodlist, num);
+ d.food = num[1];
+ d.cookie = num[2];
+ return 0;
+}
+
+
+int
+pip_store_medicine()
+{
+ int num[7];
+ num[0] = 6;
+ num[1] = d.pill;
+ num[2] = d.medicine;
+ num[3] = d.burger;
+ num[4] = d.ginseng;
+ num[5] = d.paste;
+ num[6] = d.snowgrass;
+ pip_buy_item(2, pipmedicinelist, num);
+ d.pill = num[1];
+ d.medicine = num[2];
+ d.burger = num[3];
+ d.ginseng = num[4];
+ d.paste = num[5];
+ d.snowgrass = num[6];
+ return 0;
+}
+
+
+int
+pip_store_other()
+{
+ int num[4];
+ num[0] = 3;
+ num[1] = d.book;
+ num[2] = d.toy;
+ num[3] = d.playboy;
+ pip_buy_item(3, pipotherlist, num);
+ d.book = num[1];
+ d.toy = num[2];
+ d.playboy = num[3];
+ return 0;
+}
+#endif /* HAVE_GAME */
diff --git a/pip/pip_job.c b/pip/pip_job.c
new file mode 100644
index 0000000..f4575e6
--- /dev/null
+++ b/pip/pip_job.c
@@ -0,0 +1,910 @@
+/*-------------------------------------------------------*/
+/* pip_job.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : ¥´¤u */
+/* create : / / */
+/* update : 01/08/15 */
+/* author : dsyan.bbs@forever.twbbs.org */
+/* modify : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+#ifdef HAVE_GAME
+
+#include "pip.h"
+
+
+/*-------------------------------------------------------*/
+/* ¨ç¦¡®w */
+/*-------------------------------------------------------*/
+
+
+/* itoc.010815: ¼g¤@°¦¤½¥Îªº function */
+
+static int /* ¶Ç¦^: -1:©ñ±ó 0~100:¦¨¥\ª¬ªp */
+pip_job_function(classgrade, tired_prob, tired_base, pic)
+ int classgrade; /* ¨­¤ßª¬ªp: 0% ~ 100 % */
+ int tired_prob; /* ­pºâ¯h³Òªº¾÷²v */
+ int tired_base; /* ­pºâ¯h³Òªº©³¼Æ */
+ int pic; /* ­n¨qªº¹Ï */
+{
+ int grade;
+
+ /* ¦]¬°ÁÙ¨S update¡Alearn_skill ¥i¯à < 0 */
+ if (LEARN_LEVEL < 0)
+ {
+ vmsg("±z¤w¸g²Ö¨ìÃz¤F");
+ return -1;
+ }
+
+ grade = classgrade * LEARN_LEVEL;
+
+ /* grade À³¸Ó¥u±q 0~100% */
+ if (grade < 0)
+ grade = 0;
+ else if (grade > 100)
+ grade = 100;
+
+ /* ©Ò¦³¤u§@³£·|§ïÅܪºÄÝ©Ê */
+ count_tired(tired_prob, tired_base, 1, 100, 1); /* ¼W¥[¯h³Ò»P¦~ÄÖ¦³Ãö */
+ d.shit += rand() % 3 + 5;
+ d.hp -= rand() % 2 + 4;
+ d.happy -= rand() % 3 + 4;
+ d.satisfy -= rand() % 3 + 4;
+
+ show_job_pic(pic);
+ return grade; /* ¦^¶Ç¤u§@µ²ªG: 0:¥¢±Ñ³z¤F ~ 100:§¹¥þ¦¨¥\ */
+}
+
+
+/*-------------------------------------------------------*/
+/* ¥´¤u¿ï³æ:®a¨Æ ­W¤u ®a±Ð ¦aÅu */
+/*-------------------------------------------------------*/
+
+
+int
+pip_job_workA()
+{
+ /* ¢u¢w¢w¢w¢w¢q¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢t */
+ /* ¢x®a®xºÞ²z¢x+ «Ý¤H±µª« ±½¦a¬~¦ç ²i¶¹ ¿Ë¤lÃö«Y ®a¨Æµû»ù ¢x */
+ /* ¢x ¢x- ·P¨ü ¢x */
+ /* ¢u¢w¢w¢w¢w¢q¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢t */
+
+ int class;
+
+ class = d.hp * 100 / d.maxhp - d.tired;
+ if ((class = pip_job_function(class, 2, 5, 11)) < 0)
+ return 0;
+
+ if (class >= 75)
+ {
+ class = 4;
+ d.money += 80 + (d.homework + d.cook) / 50;
+ vmsg("®a¨Æ«Ü¦¨¥\\³á..¦h¤@ÂI¿úµ¹±z..");
+ }
+ else if (class >= 50)
+ {
+ class = 3;
+ d.money += 60 + (d.homework + d.cook) / 55;
+ vmsg("®a¨ÆÁÙÆZ¶¶§Qªº­ò..¶â¶â..");
+ }
+ else if (class >= 25)
+ {
+ class = 2;
+ d.money += 40 + (d.homework + d.cook) / 60;
+ vmsg("®a¨Æ´¶´¶³q³q°Õ..¥i¥H§ó¦nªº..¥[ªo..");
+ }
+ else
+ {
+ class = 1;
+ d.money += 20 + (d.homework + d.cook) / 65;
+ vmsg("®a¨Æ«ÜÁV¿|³á..³o¼Ë¤£¦æ°Õ..");
+ }
+
+ d.toman += rand() % 2;
+ d.homework += rand() % 2 + class;
+ d.cook += rand() % 2 + class;
+ d.relation += rand() % 2;
+ d.family += class;
+
+ d.affect -= rand() % 5;
+ if (d.affect < 0)
+ d.affect = 0;
+
+ d.workA++;
+ return 0;
+}
+
+
+int
+pip_job_workB()
+{
+ /* ¢u¢w¢w¢w¢w¢q¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢t */
+ /* ¢x¨|¥®°| ¢x+ «Ý¤H±µª« ·R¤ß ·P¨ü ¢x */
+ /* ¢x ¢x- ¾y¤O ¢x */
+ /* ¢u¢w¢w¢w¢w¢q¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢t */
+
+ int class;
+
+ class = d.hp * 100 / d.maxhp - d.tired;
+ if ((class = pip_job_function(class, 3, 7, 21)) < 0)
+ return 0;
+
+ if (class >= 75)
+ {
+ class = 4;
+ d.money += 150 + (d.toman + d.love) / 50;
+ vmsg("·í«O©i«Ü¦¨¥\\³á..¤U¦¸¦A¨Ó³á..");
+ }
+ else if (class >= 50)
+ {
+ class = 3;
+ d.money += 120 + (d.toman + d.love) / 55;
+ vmsg("«O©iÁÙ·íªº¤£¿ù­ò..¶â¶â..");
+ }
+ else if (class >= 25)
+ {
+ class = 2;
+ d.money += 100 + (d.toman + d.love) / 60;
+ vmsg("¤pªB¤Í«Ü¥Ö³á..¥[ªo..");
+ }
+ else
+ {
+ class = 1;
+ d.money += 80 + (d.toman + d.love) / 65;
+ vmsg("«ÜÁV¿|³á..³s¤pªB¤Í³£¸n¤£¦í..");
+ }
+
+ d.toman += rand() % 3;
+ d.love += rand() % 3 + class;
+ d.affect += class;
+
+ d.charm -= rand() % 5;
+ if (d.charm < 0)
+ d.charm = 0;
+
+ /* itoc.010824: ¶Ã¼Æ¾Ç·|·t¾¹ */
+ if (rand() % 30 == 0)
+ pip_learn_skill(7);
+
+ d.workB++;
+ return 0;
+}
+
+
+int
+pip_job_workC()
+{
+ /* ¢u¢w¢w¢w¢w¢q¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢t */
+ /* ¢x®ÈÀ] ¢x+ ±½¦a¬~¦ç ²i¶¹ ®a¨Æµû»ù ¢x */
+ /* ¢x ¢x- µL ¢x */
+ /* ¢u¢w¢w¢w¢w¢q¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢t */
+
+ int class;
+
+ class = d.hp * 100 / d.maxhp - d.tired;
+ if ((class = pip_job_function(class, 4, 8, 31)) < 0)
+ return 0;
+
+ if (class >= 75)
+ {
+ class = 4;
+ d.money += 250 + (d.homework + d.cook) / 50;
+ vmsg("®ÈÀ]¨Æ·~»]»]¤é¤W..§Æ±æ±z¦A¹L¨ÓÀ°¦£..");
+ }
+ else if (class >= 50)
+ {
+ class = 3;
+ d.money += 200 + (d.homework + d.cook) / 55;
+ vmsg("®ÈÀ]ÁÙÆZ¶¶§Qªº­ò..¶â¶â..");
+ }
+ else if (class >= 25)
+ {
+ class = 2;
+ d.money += 150 + (d.homework + d.cook) / 60;
+ vmsg("´¶´¶³q³q°Õ..¥i¥H§ó¦nªº..¥[ªo..");
+ }
+ else
+ {
+ class = 1;
+ d.money += 100 + (d.homework + d.cook) / 65;
+ vmsg("³o­Ó«ÜÁV¿|³á..³o¼Ë¤£¦æ°Õ..");
+ }
+
+ d.homework += rand() % 2 + class;
+ d.cook += rand() % 2 + class;
+ d.family += class;
+
+ d.workC++;
+ return 0;
+}
+
+
+int
+pip_job_workD()
+{
+ /* ¢u¢w¢w¢w¢w¢q¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢t */
+ /* ¢x¹A³õ ¢x+ µL ¢x */
+ /* ¢x ¢x- ®ð½è ¢x */
+ /* ¢u¢w¢w¢w¢w¢q¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢t */
+
+ int class;
+
+ class = d.hp * 100 / d.maxhp - d.tired;
+ if ((class = pip_job_function(class, 6, 12, 41)) < 0)
+ return 0;
+
+ if (class >= 75)
+ {
+ d.money += 250 + (d.attack + d.resist) / 50;
+ vmsg("¤û¦Ïªøªº¦n¦n³á..§Æ±æ±z¦A¨ÓÀ°¦£..");
+ }
+ else if (class >= 50)
+ {
+ d.money += 210 + (d.attack + d.resist) / 55;
+ vmsg("¨þ¨þ..ÁÙ¤£¿ù³á..");
+ }
+ else if (class >= 25)
+ {
+ d.money += 160 + (d.attack + d.resist) / 60;
+ vmsg("´¶´¶³q³q°Õ..¥i¥H§ó¦nªº..");
+ }
+ else
+ {
+ d.money += 120 + (d.attack + d.resist) / 65;
+ vmsg("±z¤£¤Ó¾A¦X¹A³õªº¤u§@..");
+ }
+
+ d.character -= rand() % 5;
+ if (d.character < 0)
+ d.character = 0;
+
+ d.workD++;
+ return 0;
+}
+
+
+int
+pip_job_workE()
+{
+ /* ¢u¢w¢w¢w¢w¢q¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢t */
+ /* ¢xÀ\ÆU ¢x+ ±½¦a¬~¦ç ²i¶¹ ¢x */
+ /* ¢x ¢x- µL ¢x */
+ /* ¢u¢w¢w¢w¢w¢q¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢t */
+
+ int class;
+
+ class = d.cook / 6 - d.tired;
+ if ((class = pip_job_function(class, 4, 9, 51)) < 0)
+ return 0;
+
+ if (class >= 75)
+ {
+ class = 4;
+ d.money += 250 + (d.homework + d.cook) / 50;
+ vmsg("«È¤H³£»¡¤Ó¦n¦Y¤F..¦A¨Ó¤@½L§a..");
+ }
+ else if (class >= 50)
+ {
+ class = 3;
+ d.money += 200 + (d.homework + d.cook) / 55;
+ vmsg("µNªºÁÙ¤£¿ù¦Y­ò..");
+ }
+ else if (class >= 25)
+ {
+ class = 2;
+ d.money += 150 + (d.homework + d.cook) / 60;
+ vmsg("´¶´¶³q³q°Õ..¥i¥H§ó¦nªº..");
+ }
+ else
+ {
+ class = 1;
+ d.money += 100 + (d.homework + d.cook) / 65;
+ vmsg("¼pÃÀ«Ý¥[±j³á..");
+ }
+
+ d.homework += rand() % 2 + class;
+ d.cook += rand() % 5 + class;
+
+ d.workE++;
+ return 0;
+}
+
+
+int
+pip_job_workF()
+{
+ /* ¢u¢w¢w¢w¢w¢q¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢t */
+ /* ¢x±Ð°ó ¢x+ ·R¤ß ¹D¼w «H¥õ ¢x */
+ /* ¢x ¢x- ¸oÄ^ ¢x */
+ /* ¢u¢w¢w¢w¢w¢q¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢t */
+
+ int class;
+
+ class = d.hp * 100 / d.maxhp - d.tired;
+ if ((class = pip_job_function(class, 3, 6, 61)) < 0)
+ return 0;
+
+ if (class >= 75)
+ {
+ class = 4;
+ d.money += 100 + (d.etchics + d.belief) / 50;
+ vmsg("«D±`ÁÂÁ±z³á..¯u¬O±o¤Oªº§U¤â");
+ }
+ else if (class >= 50)
+ {
+ class = 3;
+ d.money += 75 + (d.etchics + d.belief) / 55;
+ vmsg("ÁÂÁ±zªº¼ö¤ßÀ°¦£..");
+ }
+ else if (class >= 25)
+ {
+ class = 2;
+ d.money += 50 + (d.etchics + d.belief) / 60;
+ vmsg("¯uªº«Ü¦³·R¤ß°Õ..¤£¹L¦³ÂI¤p²Öªº¼Ë¤l..");
+ }
+ else
+ {
+ class = 1;
+ d.money += 25 + (d.etchics + d.belief) / 65;
+ vmsg("¨Ó©^Äm¤£¿ù..¦ý¤]¤£¯à¥´²V£«..");
+ }
+
+ d.love += rand() % 2 + class;
+ d.etchics += rand() % 4 + class;
+ d.belief += rand() % 4 + class;
+
+ d.sin -= rand() % 9;
+ if (d.sin < 0)
+ d.sin = 0;
+
+ d.workF++;
+ return 0;
+}
+
+
+int
+pip_job_workG()
+{
+ /* ¢u¢w¢w¢w¢w¢q¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢t */
+ /* ¢x¦aÅu ¢x+ «Ý¤H±µª« ¾y¤O ½Í¦R ³t«× ¢x */
+ /* ¢x ¢x- µL ¢x */
+ /* ¢u¢w¢w¢w¢w¢q¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢t */
+
+ int class;
+
+ class = d.hp * 100 / d.maxhp - d.tired;
+ if ((class = pip_job_function(class, 5, 10, 71)) < 0)
+ return 0;
+
+ d.money += 200 + (d.charm + d.speech) * class / 5000;
+ vmsg("Â\\¦aÅu­n¸úĵ¹î°Õ..:p");
+
+ d.toman += rand() % 2;
+ d.charm += rand() % 2;
+ d.speed += rand() % 2 + 1;
+ d.speech += rand() % 2 + 1;
+
+ d.workG++;
+ return 0;
+}
+
+
+int
+pip_job_workH()
+{
+ /* ¢u¢w¢w¢w¢w¢q¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢t */
+ /* ¢x¥ï¤ì³õ ¢x+ §ðÀ»¤O ¢x */
+ /* ¢x ¢x- ®ð½è ¢x */
+ /* ¢u¢w¢w¢w¢w¢q¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢t */
+
+ int class;
+
+ if (d.bbtime < 1800 * 1)
+ {
+ vmsg("¤pÂû¤Ó¤p¤F¡A¤@·³¥H«á¦A¨Ó§a..");
+ return 0;
+ }
+
+ class = d.hp * 100 / d.maxhp - d.tired;
+ if ((class = pip_job_function(class, 7, 14, 81)) < 0)
+ return 0;
+
+ if (class >= 75)
+ {
+ class = 4;
+ d.money += 350 + (d.maxhp + d.attack) / 50;
+ vmsg("±zµÃ¤O«Ü¦n­ò..");
+ }
+ else if (class >= 50)
+ {
+ class = 3;
+ d.money += 300 + (d.maxhp + d.attack) / 55;
+ vmsg("¬å¤F¤£¤Ö¾ð³á..");
+ }
+ else if (class >= 25)
+ {
+ class = 2;
+ d.money += 250 + (d.maxhp + d.attack) / 60;
+ vmsg("´¶´¶³q³q°Õ..¥i¥H§ó¦nªº..");
+ }
+ else
+ {
+ class = 1;
+ d.money += 200 + (d.maxhp + d.attack) / 65;
+ vmsg("«Ý¥[±j³á..ÁëÁå¦A¨Ó§a..");
+ }
+
+ d.attack += rand() % 2 + class;
+
+ d.character -= rand() % 5;
+ if (d.character < 0)
+ d.character = 0;
+
+ d.workH++;
+ return 0;
+}
+
+
+int
+pip_job_workI()
+{
+ /* ¢u¢w¢w¢w¢w¢q¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢t */
+ /* ¢x¬ü®e°| ¢x+ ÃÀ³N ·P¨ü ¢x */
+ /* ¢x ¢x- µL ¢x */
+ /* ¢u¢w¢w¢w¢w¢q¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢t */
+
+ int class;
+
+ if (d.bbtime < 1800 * 1)
+ {
+ vmsg("¤pÂû¤Ó¤p¤F¡A¤@·³¥H«á¦A¨Ó§a..");
+ return 0;
+ }
+
+ class = d.art / 6 - d.tired;
+ if ((class = pip_job_function(class, 5, 10, 91)) < 0)
+ return 0;
+
+ if (class >= 75)
+ {
+ class = 4;
+ d.money += 400 + (d.art + d.affect) / 50;
+ vmsg("«È¤H³£«Ü³ßÅwÅý±z°µ³y«¬­ò..");
+ }
+ else if (class >= 50)
+ {
+ class = 3;
+ d.money += 360 + (d.art + d.affect) / 55;
+ vmsg("°µªº¤£¿ù³á..»á¦³¤Ñ¥÷..");
+ }
+ else if (class >= 25)
+ {
+ class = 2;
+ d.money += 320 + (d.art + d.affect) / 60;
+ vmsg("°¨°¨ªêªê°Õ..¦A¥[ªo¤@ÂI..");
+ }
+ else
+ {
+ class = 1;
+ d.money += 250 + (d.art + d.affect) / 65;
+ vmsg("«Ý¥[±j³á..¥H«á¦A¨Ó§a..");
+ }
+
+ d.art += rand() % 3 + class;
+ d.affect += rand() % 2 + class;
+
+ d.workI++;
+ return 0;
+}
+
+
+int
+pip_job_workJ()
+{
+ /* ¢u¢w¢w¢w¢w¢q¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢t */
+ /* ¢x¬¼Ây°Ï ¢x+ §ðÀ»¤O ³t«× ¢x */
+ /* ¢x ¢x- ®ð½è ·R¤ß ¢x */
+ /* ¢u¢w¢w¢w¢w¢q¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢t */
+
+ int class;
+
+ if (d.bbtime < 1800 * 2)
+ {
+ vmsg("¤pÂû¤Ó¤p¤F¡A¤G·³¥H«á¦A¨Ó§a..");
+ return 0;
+ }
+
+ class = d.hp * 100 / d.maxhp - d.tired;
+ if ((class = pip_job_function(class, 6, 13, 101)) < 0)
+ return 0;
+
+ if (class >= 75)
+ {
+ class = 4;
+ d.money += 300 + (d.attack + d.speed) / 50;
+ vmsg("±z¬O§¹¬üªºÂy¤H..");
+ }
+ else if (class >= 50)
+ {
+ class = 3;
+ d.money += 270 + (d.attack + d.speed) / 55;
+ vmsg("¦¬ÀòÁÙ¤£¿ù³á..¥i¥H¹¡À\\¤@¹y¤F..");
+ }
+ else if (class >= 25)
+ {
+ class = 2;
+ d.money += 240 + (d.attack + d.speed) / 60;
+ vmsg("¬¼Ây¬OÅé¤O»P´¼¤Oªºµ²¦X..");
+ }
+ else
+ {
+ class = 1;
+ d.money += 210 + (d.attack + d.speed) / 65;
+ vmsg("§Þ³N®t±j¤H·N..¦A¥[ªo³á..");
+ }
+
+ d.attack += rand() % 2 + class;
+ d.speed += rand() % 2 + class;
+
+ d.character -= rand() % 5;
+ if (d.character < 0)
+ d.character = 0;
+ d.love -= rand() % 5;
+ if (d.love < 0)
+ d.love = 0;
+
+ d.workJ++;
+ return 0;
+}
+
+
+int
+pip_job_workK()
+{
+ /* ¢u¢w¢w¢w¢w¢q¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢t */
+ /* ¢x¤u¦a ¢x+ ¨¾¿m¤O ¢x */
+ /* ¢x ¢x- ¾y¤O ¢x */
+ /* ¢u¢w¢w¢w¢w¢q¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢t */
+
+ int class;
+
+ if (d.bbtime < 1800 * 2)
+ {
+ vmsg("¤pÂû¤Ó¤p¤F¡A¤G·³¥H«á¦A¨Ó§a..");
+ return 0;
+ }
+
+ class = d.hp * 100 / d.maxhp - d.tired;
+ if ((class = pip_job_function(class, 7, 15, 111)) < 0)
+ return 0;
+
+ if (class >= 75)
+ {
+ class = 4;
+ d.money += 250 + (d.maxhp + d.resist) / 50;
+ vmsg("¤uµ{«Ü§¹¬ü..ÁÂÁ¤F..");
+ }
+ else if (class >= 50)
+ {
+ class = 3;
+ d.money += 220 + (d.maxhp + d.resist) / 55;
+ vmsg("¤uµ{©|ºÙ¶¶§Q..¨¯­W¤F..");
+ }
+ else if (class >= 25)
+ {
+ class = 2;
+ d.money += 200 + (d.maxhp + d.resist) / 60;
+ vmsg("¤uµ{®t±j¤H·N..¦A¥[ªo³á..");
+ }
+ else
+ {
+ class = 1;
+ d.money += 160 + (d.maxhp + d.resist) / 65;
+ vmsg("£­..«Ý¥[±j«Ý¥[±j..");
+ }
+
+ d.resist += rand() % 2 + class;
+
+ d.charm -= rand() % 5;
+ if (d.charm < 0)
+ d.charm = 0;
+
+ d.workK++;
+ return 0;
+}
+
+
+int
+pip_job_workL()
+{
+ /* ¢u¢w¢w¢w¢w¢q¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢t */
+ /* ¢x¹Ó¶é ¢x+ «i´± §ÜÅ]¯à¤O ·P¨ü ¢x */
+ /* ¢x ¢x- ¾y¤O ¢x */
+ /* ¢u¢w¢w¢w¢w¢q¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢t */
+
+ int class;
+
+ if (d.bbtime < 1800 * 3)
+ {
+ vmsg("¤pÂû¤Ó¤p¤F¡A¤T·³¥H«á¦A¨Ó§a..");
+ return 0;
+ }
+
+ class = d.hp * 100 / d.maxhp - d.tired;
+ if ((class = pip_job_function(class, 4, 8, 121)) < 0)
+ return 0;
+
+ if (class >= 75)
+ {
+ class = 4;
+ d.money += 200 + (d.brave + d.affect) / 50;
+ vmsg("¦u¹Ó¦¨¥\\³á..¦hÁ¤F");
+ }
+ else if (class >= 50)
+ {
+ class = 3;
+ d.money += 150 + (d.brave + d.affect) / 55;
+ vmsg("¦u¹ÓÁٺ⦨¥\\³á..Á°Õ..");
+ }
+ else if (class >= 25)
+ {
+ class = 2;
+ d.money += 120 + (d.brave + d.affect) / 60;
+ vmsg("¦u¹ÓÁÙºâ®t±j¤H·N³á..¥[ªo..");
+ }
+ else
+ {
+ class = 1;
+ d.money += 80 + (d.brave + d.affect) / 65;
+ vmsg("§Ú¤]¤£¤è«K»¡Ô£¤F..½Ð¦A¥[ªo..");
+ }
+
+ d.brave += rand() % 4 + class;
+ d.immune += rand() % 3 + class;
+ d.affect += class;
+
+ d.charm -= rand() % 5;
+ if (d.charm < 0)
+ d.charm = 0;
+
+ d.workL++;
+ return 0;
+}
+
+
+int
+pip_job_workM()
+{
+ /* ¢u¢w¢w¢w¢w¢q¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢t */
+ /* ¢x®a®x±Ð®v¢x+ ´¼¤O ½Í¦R ¢x */
+ /* ¢x ¢x- µL ¢x */
+ /* ¢u¢w¢w¢w¢w¢q¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢t */
+
+ int class;
+
+ if (d.bbtime < 1800 * 4)
+ {
+ vmsg("¤pÂû¤Ó¤p¤F¡A¥|·³¥H«á¦A¨Ó§a..");
+ return 0;
+ }
+
+ class = d.hp * 100 / d.maxhp - d.tired;
+ if ((class = pip_job_function(class, 3, 7, 131)) < 0)
+ return 0;
+
+ d.money += 50 + (d.wisdom + d.character) * class / 5000;
+ vmsg("®a±Ð»´ÃP..·íµM¿ú´N¤Ö¤@ÂIÅo");
+
+ d.wisdom += rand() % 2 + 3;
+ d.speech += rand() % 2 + 1;
+
+ d.workM++;
+ return 0;
+}
+
+
+int
+pip_job_workN()
+{
+ /* ¢u¢w¢w¢w¢w¢q¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢t */
+ /* ¢x°s©± ¢x+ ¾y¤O ½Í¦R ²i¶¹ ¢x */
+ /* ¢x ¢x- ´¼¤O ªÀ¥æµû»ù ¢x */
+ /* ¢u¢w¢w¢w¢w¢q¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢t */
+
+ int class;
+
+ if (d.bbtime < 1800 * 5)
+ {
+ vmsg("¤pÂû¤Ó¤p¤F¡A¤­·³¥H«á¦A¨Ó§a..");
+ return 0;
+ }
+
+ class = d.charm / 6 - d.tired;
+ if ((class = pip_job_function(class, 5, 11, 141)) < 0)
+ return 0;
+
+ if (class >= 75)
+ {
+ class = 4;
+ d.money += 500 + (d.charm + d.speech) / 50;
+ vmsg("«Ü¬õ­ò..");
+ }
+ else if (class >= 50)
+ {
+ class = 3;
+ d.money += 400 + (d.charm + d.speech) / 55;
+ vmsg("ÆZ¨üÅwªïªº­C..");
+ }
+ else if (class >= 25)
+ {
+ class = 2;
+ d.money += 300 + (d.charm + d.speech) / 60;
+ vmsg("«Ü¥­¤Z°Õ..¦ý°¨°¨ªêªê..");
+ }
+ else
+ {
+ class = 1;
+ d.money += 200 + (d.charm + d.speech) / 65;
+ vmsg("´A¤O¤£°÷°Õ..½Ð¥[ªo..");
+ }
+
+ d.charm += rand() % 3 + class;
+ d.speech += rand() % 2 + class;
+ d.cook += class;
+
+ d.wisdom -= rand() % 5;
+ if (d.wisdom < 0)
+ d.wisdom = 0;
+ d.social -= rand() % 5;
+ if (d.social < 0)
+ d.social = 0;
+
+ d.workN++;
+ return 0;
+}
+
+
+int
+pip_job_workO()
+{
+ /* ¢u¢w¢w¢w¢w¢q¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢t */
+ /* ¢x°s®a ¢x+ ¾y¤O ¸oÄ^ ¢x */
+ /* ¢x ¢x- «Ý¤H±µª« ¹D¼w ¿Ë¤lÃö«Y «H¥õ ªÀ¥æµû»ù ¢x */
+ /* ¢u¢w¢w¢w¢w¢q¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢t */
+
+ int class;
+
+ if (d.bbtime < 1800 * 5)
+ {
+ vmsg("¤pÂû¤Ó¤p¤F¡A¤­·³¥H«á¦A¨Ó§a..");
+ return 0;
+ }
+
+ class = d.charm / 6 - d.tired;
+ if ((class = pip_job_function(class, 6, 12, 151)) < 0)
+ return 0;
+
+ if (class >= 75)
+ {
+ class = 4;
+ d.money += 600 + (d.charm + d.speech) / 50;
+ vmsg("±z¬O¥»©±ªº¬õµP­ò..");
+ }
+ else if (class >= 50)
+ {
+ class = 3;
+ d.money += 500 + (d.charm + d.speech) / 55;
+ vmsg("±zÆZ¨üÅwªïªº­C..");
+ }
+ else if (class >= 25)
+ {
+ class = 2;
+ d.money += 400 + (d.charm + d.speech) / 60;
+ vmsg("«Ü¥­¤Z..¦ý°¨°¨ªêªê°Õ..");
+ }
+ else
+ {
+ class = 1;
+ d.money += 300 + (d.charm + d.speech) / 65;
+ vmsg("­ü..´A¤O¤£°÷°Õ..");
+ }
+
+ d.charm += rand() % 4 + class;
+ d.sin += rand() % 4 + class;
+
+ d.toman -= rand() % 5;
+ if (d.toman < 0)
+ d.toman = 0;
+ d.etchics -= rand() % 5;
+ if (d.etchics < 0)
+ d.etchics = 0;
+ d.relation -= rand() % 5;
+ if (d.relation < 0)
+ d.relation = 0;
+ d.belief -= rand() % 5;
+ if (d.belief < 0)
+ d.belief = 0;
+ d.social -= rand() % 5;
+ if (d.social < 0)
+ d.social = 0;
+
+ d.workO++;
+ return 0;
+}
+
+
+int
+pip_job_workP()
+{
+ /* ¢u¢w¢w¢w¢w¢q¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢t */
+ /* ¢x©]Á`·| ¢x+ ¾y¤O ½Í¦R ¸oÄ^ ¢x */
+ /* ¢x ¢x- «Ý¤H±µª« ®ð½è ¹D¼w ¿Ë¤lÃö«Y «H¥õ ªÀ¥æµû»ù ¢x */
+ /* ¢u¢w¢w¢w¢w¢q¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢t */
+
+ int class;
+
+ if (d.bbtime < 1800 * 6)
+ {
+ vmsg("¤pÂû¤Ó¤p¤F¡A¤»·³¥H«á¦A¨Ó§a..");
+ return 0;
+ }
+
+ class = (d.charm + d.art - d.belief) / 6 - d.tired;
+ if ((class = pip_job_function(class, 6, 12, 161)) < 0)
+ return 0;
+
+ if (class >= 75)
+ {
+ class = 4;
+ d.money += 1000 + (d.charm + d.speech) / 50;
+ vmsg("±z¬O¥»©]Á`·|³Ì°{«Gªº¬P¬P­ò..");
+ }
+ else if (class >= 50)
+ {
+ class = 3;
+ d.money += 800 + (d.charm + d.speech) / 55;
+ vmsg("¶â¶â..±zÆZ¨üÅwªïªº­C..");
+ }
+ else if (class >= 25)
+ {
+ class = 2;
+ d.money += 600 + (d.charm + d.speech) / 60;
+ vmsg("­n¥[ªo¤F°Õ..¦ý´¶´¶°Õ..");
+ }
+ else
+ {
+ class = 1;
+ d.money += 400 + (d.charm + d.speech) / 65;
+ vmsg("­ü..¤£¦æ°Õ..");
+ }
+
+ d.charm += rand() % 5 + class;
+ d.speech += rand() % 2 + class;
+ d.sin += rand() % 6 + class;
+
+ d.toman -= rand() % 5;
+ if (d.toman < 0)
+ d.toman = 0;
+ d.character -= rand() % 5;
+ if (d.character < 0)
+ d.character = 0;
+ d.etchics -= rand() % 5;
+ if (d.etchics < 0)
+ d.etchics = 0;
+ d.relation -= rand() % 5;
+ if (d.relation < 0)
+ d.relation = 0;
+ d.belief -= rand() % 5;
+ if (d.belief < 0)
+ d.belief = 0;
+ d.social -= rand() % 5;
+ if (d.social < 0)
+ d.social = 0;
+
+ d.workP++;
+ return 0;
+}
+#endif /* HAVE_GAME */
diff --git a/pip/pip_menu.c b/pip/pip_menu.c
new file mode 100644
index 0000000..c4df517
--- /dev/null
+++ b/pip/pip_menu.c
@@ -0,0 +1,809 @@
+/*-------------------------------------------------------*/
+/* pip_menu.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : ¿ï³æ¨ç¦¡ */
+/* create : / / */
+/* update : 01/08/15 */
+/* author : dsyan.bbs@forever.twbbs.org */
+/* modify : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+#ifdef HAVE_GAME
+
+#include "pip.h"
+
+
+/*-------------------------------------------------------*/
+/* ¿ï³æ */
+/*-------------------------------------------------------*/
+
+
+/* ¥D¿ï³æ [1]°ò¥» [2]³}µó [3]­×¦æ [4]ª±¼Ö [5]¥´¤u [6]¯S®í [7]¨t²Î */
+
+int pip_basic_menu(), pip_store_menu(), pip_practice_menu(), pip_play_menu();
+int pip_job_menu(), pip_special_menu(), pip_system_menu();
+
+static struct pipcommands pipmainlist[] =
+{
+ pip_basic_menu, '1',
+ pip_store_menu, '2',
+ pip_practice_menu, '3',
+ pip_play_menu, '4',
+ pip_job_menu, '5',
+ pip_special_menu, '6',
+ pip_system_menu, '7',
+ NULL, '\0'
+};
+
+
+/* °ò¥»¿ï³æ [1]Áý­¹ [2]²M¼ä [3]¥ð®§ [4]¿Ë¿Ë [5]´«¿ú */
+
+int pip_basic_feed(), pip_basic_takeshower(), pip_basic_takerest(), pip_basic_kiss(), pip_money();
+
+static struct pipcommands pipbasiclist[] =
+{
+ pip_basic_feed, '1',
+ pip_basic_takeshower, '2',
+ pip_basic_takerest, '3',
+ pip_basic_kiss, '4',
+ pip_money, '5',
+ NULL, '\0'
+};
+
+
+/* ³}µó ¡i¤é±`¥Î«~¡j[1]«K§Q°Ó©± [2]¦Ê¯óÃľQ [3]©]¸Ì®Ñ§½ */
+/* ¿ï³æ ¡iªZ¾¹¦Ê³f¡j[A]ÀY³¡¸Ë³Æ [B]¤â³¡¸Ë³Æ [C]¬ÞµP¸Ë³Æ [D]¨­Åé¸Ë³Æ [E]¸}³¡¸Ë³Æ */
+
+int pip_store_food(), pip_store_medicine(), pip_store_other();
+int pip_store_weapon_head(), pip_store_weapon_hand(), pip_store_weapon_shield();
+int pip_store_weapon_body(), pip_store_weapon_foot();
+
+static struct pipcommands pipstorelist[] =
+{
+ pip_store_food, '1',
+ pip_store_medicine, '2',
+ pip_store_other, '3',
+ pip_store_weapon_head, 'a',
+ pip_store_weapon_hand, 'b',
+ pip_store_weapon_shield,'c',
+ pip_store_weapon_body, 'd',
+ pip_store_weapon_foot, 'e',
+ NULL, '\0'
+};
+
+
+/* ­×¦æ [A]¬ì¾Ç(1) [B]¸Öµü(1) [C]¯«¾Ç(1) [D]­x¾Ç(1) [E]¼C³N(1) */
+/* ¿ï³æ [F]®æ°«(1) [G]Å]ªk(1) [H]§»ö(1) [I]øµe(1) [J]»RÁÐ(1) */
+
+int pip_practice_classA(), pip_practice_classB(), pip_practice_classC(), pip_practice_classD(), pip_practice_classE();
+int pip_practice_classF(), pip_practice_classG(), pip_practice_classH(), pip_practice_classI(), pip_practice_classJ();
+
+static struct pipcommands pippracticelist[] =
+{
+ pip_practice_classA, 'a',
+ pip_practice_classB, 'b',
+ pip_practice_classC, 'c',
+ pip_practice_classD, 'd',
+ pip_practice_classE, 'e',
+ pip_practice_classF, 'f',
+ pip_practice_classG, 'g',
+ pip_practice_classH, 'h',
+ pip_practice_classI, 'i',
+ pip_practice_classJ, 'j',
+ NULL, '\0'
+};
+
+
+/* ª±¼Ö¿ï³æ [1]´²¨B [2]¹B°Ê [3]¬ù·| [4]²q®± [5]®È¹C [6]­¥¥~ [7]°Ûºq */
+
+int pip_play_stroll(), pip_play_sport(), pip_play_date(), pip_play_guess();
+int pip_play_outing(), pip_play_kite(), pip_play_KTV();
+
+static struct pipcommands pipplaylist[] =
+{
+ pip_play_stroll, '1',
+ pip_play_sport, '2',
+ pip_play_date, '3',
+ pip_play_guess, '4',
+ pip_play_outing, '5',
+ pip_play_kite, '6',
+ pip_play_KTV, '7',
+ NULL, '\0'
+};
+
+
+/* ¥´¤u [A]®a¨Æ [B]«O©i [C]®ÈÀ] [D]¹A³õ [E]À\ÆU [F]±Ð°ó [G]¦aÅu [H]¥ï¤ì */
+/* ¿ï³æ [I]¬ü¾v [J]Ây¤H [K]¤u¦a [L]¦u¹Ó [M]®a±Ð [N]°s®a [O]°s©± [P]©]Á`·| */
+
+int pip_job_workA(), pip_job_workB(), pip_job_workC(), pip_job_workD();
+int pip_job_workE(), pip_job_workF(), pip_job_workG(), pip_job_workH();
+int pip_job_workI(), pip_job_workJ(), pip_job_workK(), pip_job_workL();
+int pip_job_workM(), pip_job_workN(), pip_job_workO(), pip_job_workP();
+
+static struct pipcommands pipjoblist[] =
+{
+ pip_job_workA, 'a',
+ pip_job_workB, 'b',
+ pip_job_workC, 'c',
+ pip_job_workD, 'd',
+ pip_job_workE, 'e',
+ pip_job_workF, 'f',
+ pip_job_workG, 'g',
+ pip_job_workH, 'h',
+ pip_job_workI, 'i',
+ pip_job_workJ, 'j',
+ pip_job_workK, 'k',
+ pip_job_workL, 'l',
+ pip_job_workM, 'm',
+ pip_job_workN, 'n',
+ pip_job_workO, 'o',
+ pip_job_workP, 'p',
+ NULL, '\0'
+};
+
+
+/* ¯S®í¿ï³æ [1]Âå°| [2]¾ã®e [3]¾Ô°« [4]¬Ó®c [5]¥ô°È [6]¹ï¾Ô */
+
+int pip_see_doctor(), pip_change_weight(), pip_fight_menu(), pip_go_palace(), pip_quest_menu(), pip_pk_menu();
+
+static struct pipcommands pipspeciallist[] =
+{
+ pip_see_doctor, '1',
+ pip_change_weight, '2',
+ pip_fight_menu, '3',
+ pip_go_palace, '4',
+ pip_quest_menu, '5',
+ pip_pk_menu, '6',
+ NULL, '\0'
+};
+
+
+/* ¨t²Î¿ï³æ [1]¸Ô²Ó¸ê®Æ [2]«ô³X¥L¤H [3]¤pÂû©ñ¥Í [4]¯S§OªA°È [S]Àx¦s¶i«× [L]Ū¨ú¶i«× */
+
+int pip_query_self(), pip_query(), pip_system_freepip(), pip_system_service();
+int pip_write_backup(), pip_read_backup();
+
+static struct pipcommands pipsystemlist[] =
+{
+ pip_query_self, '1',
+ pip_query, '2',
+ pip_system_freepip, '3',
+ pip_system_service, '4',
+ pip_write_backup, 's',
+ pip_read_backup, 'l',
+ NULL, '\0'
+};
+
+
+/*-------------------------------------------------------*/
+/* «ü¥O¿ï³æ */
+/*-------------------------------------------------------*/
+
+
+/* ¦b b_lines ©M b_lines - 1 ³o¤G¦æ³£¬O«ü¥O¿ï³æ */
+/* ­Y«ü¥O²Ä¤G¦æ¡A«h²Ä¤@Äæ¦Û¥Õ */
+/* °Ñ¦Ò global.h FEETER */
+
+/* ¶È¨Ñ pip_do_menu() ¨Ï¥Î */
+
+static char *menuname[8][2] =
+{
+ {"",
+ COLOR1 " ¿ï³æ " COLOR2 " [1]°ò¥» [2]³}µó [3]­×¦æ [4]ª±¼Ö [5]¥´¤u [6]¯S®í [7]¨t²Î [Q]Â÷¶} \033[m"},
+
+ {"",
+ COLOR1 " °ò¥» " COLOR2 " [1]Áý­¹ [2]²M¼ä [3]¥ð®§ [4]¿Ë¿Ë [5]´«¿ú [Q]¸õ¥X \033[m"},
+
+ {COLOR1 " ³}µó " COLOR2 " ¤é±`¥Î«~ [1]«K§Q°Ó©± [2]¦Ê¯óÃľQ [3]©]¸Ì®Ñ§½ [Q]¸õ¥X \033[m",
+ COLOR1 " ±ÄÁÊ " COLOR2 " ªZ¾¹¦Ê³f [A]ÀY³¡¸Ë³Æ [B]¤â³¡¸Ë³Æ [C]¬ÞµP¸Ë³Æ [D]¨­Åé¸Ë³Æ [E]¸}³¡¸Ë³Æ \033[m"},
+
+ {COLOR1 " ­×¦æ " COLOR2 " [A]¬ì¾Ç [B]¸Öµü [C]¯«¾Ç [D]­x¾Ç [E]¼C³N \033[m",
+ COLOR1 " ­W½m " COLOR2 " [F]®æ°« [G]Å]ªk [H]§»ö [I]øµe [J]»RÁÐ [Q]¸õ¥X \033[m"},
+
+ {"",
+ COLOR1 " ª±¼Ö " COLOR2 " [1]´²¨B [2]¹B°Ê [3]¬ù·| [4]²q®± [5]®È¹C [6]­¥¥~ [7]°Ûºq [Q]¸õ¥X \033[m"},
+
+ {COLOR1 " ¥´¤u " COLOR2 " [A]®a¨Æ [B]«O©i [C]®ÈÀ] [D]¹A³õ [E]À\\ÆU [F]±Ð°ó [G]¦aÅu [H]¥ï¤ì [Q]¸õ¥X\033[m",
+ COLOR1 " ÁÈ¿ú " COLOR2 " [I]¬ü¾v [J]Ây¤H [K]¤u¦a [L]¦u¹Ó [M]®a±Ð [N]°s®a [O]°s©± [P]©]Á`·| \033[m"},
+
+ {"",
+ COLOR1 " ¯S®í " COLOR2 " [1]Âå°| [2]¾ã®e [3]¾Ô°« [4]¬Ó®c [5]¥ô°È [6]¹ï¾Ô [Q]¸õ¥X \033[m"},
+
+ {COLOR1 " ªA°È " COLOR2 " [1]¸Ô²Ó¸ê®Æ [2]«ô³X¥L¤H [3]¤pÂû©ñ¥Í [4]¯S§OªA°È \033[m",
+ COLOR1 " ¨t²Î " COLOR2 " [S]Àx¦s¶i«× [L]Ū¨ú¶i«× [Q]¸õ¥X \033[m"}
+};
+
+
+/*-------------------------------------------------------*/
+/* ¿ï³æ¨ç¦¡ */
+/*-------------------------------------------------------*/
+
+
+ /*-----------------------------------------------------*/
+ /* ±`¾n¨ç¦¡ */
+ /*-----------------------------------------------------*/
+
+
+#define PIP_CHECK_PERIOD 60 /* ¨C 60 ¬íÀˬd¤@¦¸ */
+
+static int /* ¦^¶Ç ·³¼Æ */
+pip_time_update()
+{
+ int oldtm, tm;
+
+ /* ©T©w®É¶¡°µªº¨Æ */
+
+ if ((time(0) - last_time) >= PIP_CHECK_PERIOD)
+ {
+ do
+ {
+ d.shit += rand() % 3 + 3; /* ¤£°µ¨Æ¡AÁÙ¬O·|ÅÜżªº */
+ d.tired -= 2; /* ¤£°µ¨Æ¡A¯h³Ò·íµM´î§C°Õ */
+ d.sick += rand() % 4 - 2; /* ¤£°µ¨Æ¡A¯f®ð·|ÀH¾÷²v¼W¥[´î¤Ö©Î¼W¥[¤Ö³\ */
+ d.happy += rand() % 4 - 2; /* ¤£°µ¨Æ¡A§Ö¼Ö·|ÀH¾÷²v¼W¥[´î¤Ö©Î¼W¥[¤Ö³\ */
+ d.satisfy += rand() % 4 - 2; /* ¤£°µ¨Æ¡Aº¡¨¬·|ÀH¾÷²v¼W¥[´î¤Ö©Î¼W¥[¤Ö³\ */
+ d.hp -= rand() % 3 + d.sick / 10; /* ¤£°µ¨Æ¡A¨{¤l¤]·|¾j«§¡A¤]·|¦]¥Í¯f­°§C¤@ÂI */
+
+ last_time += PIP_CHECK_PERIOD; /* ¤U¦¸§ó·s®É¶¡ */
+ } while ((time(0) - last_time) >= PIP_CHECK_PERIOD);
+
+ /* Àˬd¦~ÄÖ */
+
+ oldtm = d.bbtime / 60 / 30; /* §ó·s«e´X·³¤F */
+ d.bbtime += time(0) - start_time; /* §ó·s¤pÂûªº®É¶¡(¦~ÄÖ) */
+ start_time = time(0);
+ tm = d.bbtime / 60 / 30; /* §ó·s«á´X·³¤F */
+
+ /* itoc.010815.µù¸Ñ: ¦pªG¤pÂû¤@ª½¦b¦¸¿ï³æ¤¤(¨Ò¦p¾Ô°«­×¦æ)¡A
+ ¨º»ò·|¦]¬°«Ü¤[(¶W¹L30¤ÀÄÁ§Y¤@·³)¨S¦³°õ¦æ pip_time_update() ¦Ó¤@¦¸¥[¦n¦h·³ */
+
+ /* itoc.010815: ¤@¦¸¹L¦n¦h·³·|¤Ö¥[¦n¦h¦¸¹L¥Í¤éªº¦n³B¡A¤£¤©­×¥¿¡A§@¬°­h«Ý¤pÂûªº³B»@ :p */
+
+ if (tm != oldtm) /* ·³¼Æ§ó·s«e«á¦pªG¤£¦P¡Aªí¥Üªø¤j¤F */
+ {
+ /* ªø¤j®Éªº¼W¥[§ïÅÜ­È */
+ count_tired(1, 7, 0, 100, 0); /* «ì´_¯h³Ò */
+ d.happy += rand() % 5 + 5;
+ d.satisfy += rand() % 5;
+ d.wisdom += 10;
+ d.character += rand() % 5;
+ d.money += 500;
+ d.seeroyalJ = 1; /* ¤@¦~¥i¥H¨£¤ý¤l¤@¦¸ */
+ pip_write_file(); /* ¦Û°ÊÀx¦s */
+
+ vs_head("¹q¤l¾i¤pÂû", str_site);
+ show_basic_pic(20); /* ¥Í¤é§Ö¼Ö */
+ vmsg("¤pÂû¹L¥Í¤é¤F");
+
+ /* ¦¬Ã¬©u */
+ if (tm % 2 == 0) /* ¤G¦~¤@¦¸¦¬Ã¬©u */
+ pip_race_main();
+
+ /* µ²§½ */
+ if (tm >= 21 && (d.wantend == 4 || d.wantend == 5 || d.wantend == 6)) /* ª±¨ì 20 ·³ */
+ pip_ending_screen();
+ }
+ }
+ else
+ {
+ tm = d.bbtime / 60 / 30; /* ¦pªG¨S¦³ update¡A¤]­n¦^¶Ç tm(·³¼Æ) */
+ }
+
+ /* °¸µo¨Æ¥ó */
+
+ oldtm = rand() % 2000; /* ­É¥Î oldtm °µ¶Ã¼Æ */
+ if (oldtm == 0 && tm >= 15 && d.charm >= 300 && d.character >= 300)
+ pip_marriage_offer(); /* °Ó¤H¨Ó¨D±B */
+ else if (oldtm > 1998)
+ pip_meet_divine(); /* ÀH¾÷¹J¨ì¥e¤R®v */
+ else if (oldtm > 1996)
+ pip_meet_sysop(); /* ÀH¾÷¹J¨ì SYSOP */
+ else if (oldtm > 1994)
+ pip_meet_smith(); /* ÀH¾÷¹J¨ìÅK¦K */
+
+ /* Àˬd¤@¨Ç±`Åܰʪº­È¬O§_Ãz±¼ */
+ /* itoc.010815: ¦b pip_time_update() ¤¤¤£¥²Àˬd shit/tired/sick >100 ©Î happy/satisfy/hp < 0
+ ¦Ó¦b pip_refresh_screen() Àˬd¨Ã¶¶¹D«Å§i¦º¤` */
+
+ if (d.shit < 0)
+ d.shit = 0;
+ if (d.tired < 0)
+ d.tired = 0;
+ if (d.sick < 0)
+ d.sick = 0;
+
+ if (d.happy > 100)
+ d.happy = 100;
+ if (d.satisfy > 100)
+ d.satisfy = 100;
+ if (d.hp > d.maxhp)
+ d.hp = d.maxhp;
+
+ return tm;
+}
+
+
+static int /* -1:¦º¤` 0:¨S¨Æ */
+pip_refresh_screen(menunum, mode) /* ­«Ã¸¾ã­Óµe­± */
+ int menunum; /* ¿ï³æ½s¸¹ */
+ int mode; /* µe­±ºØÃþ 0:¤@¯ë 1:Áý­¹ 2:¥´¤u 3:­×¦æ */
+{
+ int tm, age, pic;
+ char inbuf2[20], inbuf3[20], inbuf4[20], inbuf5[20], inbuf6[20], inbuf7[20];
+
+ char yo[12][5] =
+ {
+ "½Ï¥Í", "À¦¨à", "¥®¨à", "¨àµ£", "¤Ö¦~", "«C¦~",
+ "¦¨¦~", "§§¦~", "§ó¦~", "¦Ñ¦~", "¥jµ}", "¯«¥P"
+ };
+
+ tm = pip_time_update(); /* ©T©w®É¶¡­n°µªº¨Æ */
+
+ if (tm == 0) /* ½Ï¥Í */
+ age = 0;
+ else if (tm == 1) /* À¦¨à */
+ age = 1;
+ else if (tm <= 5) /* ¥®¨à */
+ age = 2;
+ else if (tm <= 12) /* ¨àµ£ */
+ age = 3;
+ else if (tm <= 15) /* ¤Ö¦~ */
+ age = 4;
+ else if (tm <= 18) /* «C¦~ */
+ age = 5;
+ else if (tm <= 35) /* ¦¨¦~ */
+ age = 6;
+ else if (tm <= 45) /* §§¦~ */
+ age = 7;
+ else if (tm <= 60) /* §ó¦~ */
+ age = 8;
+ else if (tm <= 70) /* ¦Ñ¦~ */
+ age = 9;
+ else if (tm <= 100) /* ¥jµ} */
+ age = 10;
+ else /* ¯«¥P */
+ age = 11;
+
+ sprintf(inbuf2, "%-4d/%4d", d.hp, d.maxhp);
+ sprintf(inbuf3, "%-4d/%4d", d.mp, d.maxmp);
+ sprintf(inbuf4, "%-4d/%4d", d.vp, d.maxvp);
+ sprintf(inbuf5, "%-4d/%4d", d.sp, d.maxsp);
+ sprintf(inbuf6, "%-4d/%4d", d.shit, d.sick);
+ sprintf(inbuf7, "%-4d/%4d", d.happy, d.satisfy);
+
+ if (menunum)
+ {
+ clear();
+ }
+ else /* itoc.010816: ¦pªG¬O¶i¤J¥D¿ï³æ¡A¤£­n²M¤¤¶¡ 5~18 ¦C */
+ {
+ clrfromto(0, 4);
+ clrfromto(19, b_lines);
+ }
+
+ /* ¿Ã¹õ¤W­± (0~4¦C) Åã¥ÜÂI¼Æ */
+
+ move(0, 0);
+ prints(COLOR1 " ¸ê®Æ " COLOR2 " %s %-15s \033[m\n", d.sex == 1 ? "¡ñ" : "¡ð", d.name);
+
+ /* itoc,010802: ¬°¤F¬Ý²M·¡¤@ÂI¡A©Ò¥H prints() ¸Ì­±ªº¤Þ¼Æ´N¤£Â_¦æ¼g¦b¸Ó¦C³Ì«á */
+ prints("\033[1;32m[ª¬ ºA] \033[37m%-9s\033[32m [¥Í ¤é] \033[37m%-9s\033[32m [¦~ ÄÖ] \033[37m%-9d\033[32m [ª÷ ¿ú] \033[37m%-9d\033[m\n", yo[age], d.birth, tm, d.money);
+ prints("\033[1;32m[¥Í ©R] \033[35m%-9s\033[32m [ªk ¤O] \033[34m%-9s\033[32m [²¾°Ê¤O] \033[36m%-9s\033[32m [¤º ¤O] \033[31m%-9s\033[m\n", inbuf2, inbuf3, inbuf4, inbuf5);
+ prints("\033[1;32m[Åé ­«] \033[37m%-9d\033[32m [¯h ³Ò] \033[37m%-9d\033[32m [ż¡þ¯f] \033[37m%-9s\033[32m [§Ö¡þº¡] \033[37m%-9s\033[m\n", d.weight, d.tired, inbuf6, inbuf7);
+
+ if (mode == 0) /* ¤@¯ëµe­± */
+ {
+ char *hint[3] =
+ {
+ "\033[1;35m[¯¸ªø¤ê]:\033[37m­n¦h¦hª`·N¤pÂûªº¯h³Ò«×©M¯f®ð¡A¥H§K²Ö¦º¯f¦º\033[m\n",
+ "\033[1;35m[¯¸ªø¤ê]:\033[37mÀH®Éª`·N¤pÂûªº¥Í©R¼Æ­È­ò¡I\033[m\n",
+ "\033[1;35m[¯¸ªø¤ê]:\033[37m§Ö§Ö¼Ö¼Öªº¤pÂû¤~¬O©¯ºÖªº¤pÂû.....\033[m\n"
+ };
+ outs(hint[rand() % 3]);
+ }
+ else if (mode == 1) /* Áý­¹ */
+ {
+ sprintf(inbuf2, "%-4d/%4d", d.food, d.cookie);
+ sprintf(inbuf3, "%-4d/%4d", d.pill, d.medicine);
+ sprintf(inbuf4, "%-4d/%4d", d.burger, d.ginseng);
+ sprintf(inbuf5, "%-4d/%4d", d.paste, d.snowgrass);
+ prints("\033[1;32m[­¹¡þ¹s] \033[37m%-9s\033[32m [ÁÙ¡þÆF] \033[37m%-9s\033[32m [¸É¡þçx] \033[37m%-9s\033[32m [»I¡þ½¬] \033[37m%-9s\033[m\n", inbuf2, inbuf3, inbuf4, inbuf5);
+ }
+ else if (mode == 2) /* ¥´¤u */
+ {
+ prints("\033[1;36m[·R¤ß]\033[37m%-5d\033[36m[´¼¼z]\033[37m%-5d\033[36m[®ð½è]\033[37m%-5d\033[36m[ÃÀ³N]\033[37m%-5d\033[36m[¹D¼w]\033[37m%-5d\033[36m[«i´±]\033[37m%-5d\033[36m[®a¨Æ]\033[37m%-5d\n\033[m",
+ d.love, d.wisdom, d.character, d.art, d.etchics, d.brave, d.homework);
+ }
+ else if (mode == 3) /* ­×¦æ */
+ {
+ prints("\033[1;36m[´¼¼z]\033[37m%-5d\033[36m[®ð½è]\033[37m%-5d\033[36m[ÃÀ³N]\033[37m%-5d\033[36m[«i´±]\033[37m%-5d\033[36m[§ðÀ»]\033[37m%-5d\033[36m[¨¾¿m]\033[37m%-5d\033[36m[³t«×]\033[37m%-5d\n\033[m",
+ d.wisdom, d.character, d.art, d.brave, d.attack, d.resist, d.speed);
+ }
+
+ /* ¿Ã¹õ¤¤¶¡ (5~18¦C) Åã¥Ü¹Ï */
+
+ tm *= 10;
+
+ if (menunum) /* itoc.010816: ¦pªG¬O¶i¥X¥D¿ï³æ¡A¤£»Ý­n­«Ã¸¤¤¶¡ªº¹Ï */
+ {
+ /* ¶i¥X¥D¿ï³æ¤£­«Ã¸¤¤¶¡ªº¹Ï·|¦³¤@­Ó¤p bug¡A´N¬O²Ä¤@¦¸¶i¨Ó¥D¿ï³æ¨S¦³¹Ï¡A¤£¹L¥i¥H¬Ù¤U¤j¶q­«Ã¸ */
+
+ move(5, 0);
+ outs("\033[34m¢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¢w¢w¢w¢w¢{\033[m");
+
+ /* ¥Ñ¦~ÄÖ¤ÎÅé­«¨Ó¨M©w¤pÂû¤é±`¥Í¬¡°_©~ªº¹Ï */
+
+ /* Åé­«ªº¨M©w¥i¥H°Ñ¦Ò¤U­±¤@ÂIªºµ{¦¡»¡©ú */
+ if (d.weight < tm + 30)
+ pic = 1; /* ½G */
+ else if (d.weight < tm + 90)
+ pic = 2; /* ¤¤µ¥ */
+ else
+ pic = 3; /* ­D */
+
+ switch (age)
+ {
+ case 0:
+ case 1:
+ case 2:
+ show_basic_pic(pic); /* pic1~3 */
+ break;
+
+ case 3:
+ case 4:
+ show_basic_pic(pic + 3); /* pic4~6 */
+ break;
+
+ case 5:
+ case 6:
+ show_basic_pic(pic + 6); /* pic7~9 */
+ break;
+
+ case 7:
+ case 8:
+ show_basic_pic(pic + 9); /* pic10~12 */
+ break;
+
+ case 9:
+ case 10:
+ show_basic_pic(pic + 12); /* pic13~15 */
+ break;
+
+ case 11:
+ show_basic_pic(pic + 15); /* pic16~18 */
+ break;
+ }
+
+ move(18, 0);
+ outs("\033[34m¢|¢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¢}\033[m");
+ }
+
+ /* ¿Ã¹õ¤U¤è (19~b_lines¦C) Åã¥Ü¥Ø«eª¬ºA¡A¨Ã¶¶«KÀˬd¬O§_¦º¤` */
+
+ move(19, 0);
+ outs("\033[1;34m¢w\033[37;44m ª¬ ºA \033[0;1;34m¢w\033[m\n");
+
+ /* itoc.010801: ­É¥Î age Àˬdª¬ºA¡A¥Ñ°ª¤@¸ôÀˬd¨ì¤¤¡B¥Ñ§C¤@¸ôÀˬd¨ì¤¤ */
+
+ age = d.shit; /* ÁT«K¶V¤Ö¶V¦n¡A²z·Q age = 0 */
+ if (age >= 100)
+ {
+ pipdie("\033[1;31m«z¡ã¯ä¦º¤F\033[m ", 1);
+ return -1;
+ }
+ else if (age >= 80)
+ {
+ outs("\033[1;35m§Ö¯ä¦º¤F\033[m ");
+ d.sick += 4;
+ }
+ else if (age >= 60)
+ {
+ outs("\033[1;33m«Ü¯ä¤F»¡\033[m ");
+ }
+ else if (age >= 40)
+ {
+ outs("¦³ÂI¯ä¯ä ");
+ }
+ else if (age == 0)
+ {
+ outs("°®²b¤pÂû ");
+ }
+
+ age = d.hp * 100 / d.maxhp; /* age = ¦åº¡ªº¤ñ¨Ò % */
+ if (age >= 90)
+ {
+ outs("\033[1;33m¼µ¼µªº»¡\033[m ");
+ }
+ else if (age >= 80)
+ {
+ outs("¨{¤l¹¡¹¡ ");
+ }
+ else if (age <= 40)
+ {
+ outs("\033[1;33m·Q¦YªF¦è\033[m ");
+ }
+ else if (age <= 0)
+ {
+ pipdie("\033[1;31m¶ã¡ã¾j¦º¤F\033[m ", 1);
+ return -1;
+ }
+ else if (age <= 20)
+ {
+ outs("\033[1;35m§Ö¾j©ü¤F\033[m ");
+ d.sick += 3;
+ d.happy -= 5;
+ d.satisfy -= 3;
+ }
+
+ age = d.tired; /* ¯h³Ò¶V§C¶V¦n¡A²z·Q age = 0 */
+ if (age >= 100)
+ {
+ pipdie("\033[1;31m£«¡ã²Ö¦º¤F\033[m ", 1);
+ return -1;
+ }
+ else if (age >= 80)
+ {
+ outs("\033[1;35m¯uªº«Ü²Ö\033[m ");
+ d.sick += 5;
+ }
+ else if (age >= 60)
+ {
+ outs("\033[1;33m¦³ÂI¤p²Ö\033[m ");
+ }
+ else if (age <= 10)
+ {
+ outs("ºë¯«¬Û·í´Î ");
+ }
+ else if (age <= 20)
+ {
+ outs("ºë¯««Ü¦n ");
+ }
+
+ age = d.weight - tm; /* ²z·QÅé­« age = 60 §Y d.wight = 60 + 10 * tm */
+ /* ª`·N¦¹®É tm ¤w¸g * 10 ¹L¤F */
+ if (age >= 130)
+ {
+ pipdie("\033[1;31m¶ã¡ãªÎ¦º¤F\033[m ", 1);
+ return -1;
+ }
+ else if (age >= 110)
+ {
+ outs("\033[1;35m¤Ó­D¤F°Õ\033[m ");
+ d.sick += 3;
+ if (d.speed > 2)
+ d.speed -= 2;
+ else
+ d.speed = 0;
+ }
+ else if (age >= 90)
+ {
+ outs("\033[1;33m¦³ÂI¤p­D\033[m ");
+ }
+ else if (age <= -10)
+ {
+ pipdie("\033[1;31m:~~ ½G¦º¤F\033[m ", 1);
+ return -1;
+ }
+ else if (age <= 10)
+ {
+ outs("\033[1;33m¦³ÂI¤p½G\033[m ");
+ }
+ else if (age <= 30)
+ {
+ outs("\033[1;35m¤Ó½G¤F³á\033[m ");
+ }
+
+ age = d.sick; /* ¯e¯f¶V§C¶V¦n¡A²z·Q age = 0 */
+ if (age >= 100)
+ {
+ pipdie("\033[1;31m¯f¦º¤F°Õ :~~\033[m ", 1);
+ return -1;
+ }
+ else if (age >= 75)
+ {
+ outs("\033[1;35m¥¿¯f­«¤¤\033[m ");
+ d.sick += 5;
+ count_tired(1, 15, 1, 100, 1);
+ }
+ else if (age >= 50)
+ {
+ outs("\033[1;33m¥Í¯f¤F°Õ\033[m ");
+ count_tired(1, 8, 1, 100, 1);
+ }
+
+ age = d.happy; /* §Ö¼Ö¶V°ª¶V¦n¡A²z·Q age = 100 */
+ if (age >= 90)
+ {
+ outs("§Ö¼Ö°Õ.. ");
+ }
+ else if (age >= 80)
+ {
+ outs("§Ö¼Ö°Õ.. ");
+ }
+ else if (age <= 10)
+ {
+ outs("\033[1;35m«Ü¤£§Ö¼Ö\033[m ");
+ }
+ else if (age <= 20)
+ {
+ outs("\033[1;33m¤£¤Ó§Ö¼Ö\033[m ");
+ }
+
+ age = d.satisfy; /* º¡¨¬¶V°ª¶V¦n¡A²z·Q age = 100 */
+ if (age >= 90)
+ {
+ outs("º¡¨¬°Õ.. ");
+ }
+ else if (age >= 80)
+ {
+ outs("º¡¨¬°Õ.. ");
+ }
+ else if (age <= 10)
+ {
+ outs("\033[1;35m«Ü¤£º¡¨¬\033[m ");
+ }
+ else if (age <= 20)
+ {
+ outs("\033[1;33m¤£¤Óº¡¨¬\033[m ");
+ }
+
+ return 0;
+}
+
+
+ /*-----------------------------------------------------*/
+ /* ¿ï³æ¥D¨ç¦¡ */
+ /*-----------------------------------------------------*/
+
+/* Ãþ¦ü menu.c ªº¥\¯à */
+
+static int
+pip_do_menu(menunum, menumode, cmdtable)
+ int menunum; /* ­þ¤@­¶¿ï³æ */
+ int menumode; /* ­þ¤@Ãþµe­± */
+ struct pipcommands cmdtable[]; /* «ü¥O¶° */
+{
+ int ch, key;
+ struct pipcommands *cmd;
+
+ while (1)
+ {
+ /* §PÂ_¬O§_¦º¤`¡A¦º±¼§Y¸õ¦^¤W¤@¼h */
+ if (d.death)
+ return 0;
+
+ /* µe­±­«Ã¸¡A¨Ã§P©w«á¬O§_¦º¤`¡A¦º±¼§Y¸õ¦^¤W¤@¼h */
+ if (pip_refresh_screen(menunum, menumode))
+ return 0;
+
+ /* ¦L¥X³Ì«á¤G¦C«ü¥O¦C */
+ out_cmd(menuname[menunum][0], menuname[menunum][1]);
+
+ switch (ch = vkey())
+ {
+ case KEY_LEFT:
+ case 'q':
+ return 0;
+
+ default:
+#if 0
+ /* itoc.010815: ´«¤p¼g */
+ if (ch >= 'A' && ch <= 'Z')
+ ch |= 0x20;
+#endif
+
+ cmd = cmdtable;
+ for (; key = cmd->key; cmd++)
+ {
+ if (ch == key)
+ {
+ cmd->fptr();
+ break; /* itoc.010815: °õ¦æ§¹µ{¦¡­n­«·s«öÁä */
+ }
+ }
+ break;
+ }
+ }
+
+ return 0;
+}
+
+
+ /*-----------------------------------------------------*/
+ /* ¥D¿ï³æ: °ò¥» ³}µó ­×¦æ ª±¼Ö ¥´¤u ¯S®í */
+ /*-----------------------------------------------------*/
+
+int
+pip_main_menu()
+{
+ pip_do_menu(0, 0, pipmainlist);
+ return 0;
+}
+
+
+ /*-----------------------------------------------------*/
+ /* °ò¥»¿ï³æ: Áý­¹ ²M¼ä ¿Ë¿Ë ¥ð®§ */
+ /*-----------------------------------------------------*/
+
+int
+pip_basic_menu()
+{
+ pip_do_menu(1, 0, pipbasiclist);
+ return 0;
+}
+
+
+ /*-----------------------------------------------------*/
+ /* °Ó©±¿ï³æ: ­¹ª« ¹s­¹ ¤j¸É¤Y ª±¨ã ®Ñ¥» */
+ /*-----------------------------------------------------*/
+
+int
+pip_store_menu()
+{
+ pip_do_menu(2, 1, pipstorelist);
+ return 0;
+}
+
+
+ /*-----------------------------------------------------*/
+ /* ­×¦æ¿ï³æ: ©À®Ñ ½mªZ ­×¦æ */
+ /*-----------------------------------------------------*/
+
+int
+pip_practice_menu()
+{
+ pip_do_menu(3, 3, pippracticelist);
+ return 0;
+}
+
+
+ /*-----------------------------------------------------*/
+ /* ª±¼Ö¿ï³æ: ´²¨B ®È¹C ¹B°Ê ¬ù·| ²q®± */
+ /*-----------------------------------------------------*/
+
+int
+pip_play_menu()
+{
+ pip_do_menu(4, 0, pipplaylist);
+ return 0;
+}
+
+
+ /*-----------------------------------------------------*/
+ /* ¥´¤u¿ï³æ: ®a¨Æ ­W¤u ®a±Ð ¦aÅu */
+ /*-----------------------------------------------------*/
+
+int
+pip_job_menu()
+{
+ pip_do_menu(5, 2, pipjoblist);
+ return 0;
+}
+
+
+ /*-----------------------------------------------------*/
+ /* ¯S®í¿ï³æ: ¬Ý¯f ´îªÎ ¾Ô°« «ô³X ´Â¨£ */
+ /*-----------------------------------------------------*/
+
+int
+pip_special_menu()
+{
+ pip_do_menu(6, 0, pipspeciallist);
+ return 0;
+}
+
+
+ /*-----------------------------------------------------*/
+ /* ¨t²Î¿ï³æ: ­Ó¤H¸ê®Æ ¤pÂû©ñ¥Í ¯S§OªA°È */
+ /*-----------------------------------------------------*/
+
+int
+pip_system_menu()
+{
+ pip_do_menu(7, 0, pipsystemlist);
+ return;
+}
+#endif /* HAVE_GAME */
diff --git a/pip/pip_pk.c b/pip/pip_pk.c
new file mode 100644
index 0000000..f177899
--- /dev/null
+++ b/pip/pip_pk.c
@@ -0,0 +1,801 @@
+/* ----------------------------------------------------- */
+/* pip_pk.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/* ----------------------------------------------------- */
+/* target : PK ¹ï¾Ô¿ï³æ */
+/* create : 02/02/17 */
+/* update : / / */
+/* author : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/* ----------------------------------------------------- */
+
+
+#include "bbs.h"
+
+#ifdef HAVE_GAME
+
+#include "pip.h"
+
+
+#if 0
+
+ 0. ª±ªk¬O¶i¤J¹q¤lÂû¹CÀ¸¥H«á¿ï PK¡AµM«á¤@¤H¿ï 1)»W¶Õ«Ýµo¡A
+ ¥t¤@¤H¿ï 2)¤U¬D¾Ô®Ñ¿é¤J«eªÌªº ID §Y¥i¶}©l¹ï¾Ô¡C
+
+ 1. PK ªº®É­Ô¥Îªº­È¬O ptmp-> ¸Ì­±ªº¡A©Ò¥H¹ï¾Ô§¹ d. ¨Ã¤£·|§ïÅÜ¡C
+ 2. ¬°¼W¥[½ì¨ý¡A¹ï¾Ôªº§ðÀ»¤è¦¡¸û pip_fight.c ¬°¦h¼Ë¡C
+ 3. ¹ï¾Ô®É¤£¥i¦Y¸É«~¡C (¥i§ï)
+ 4. ¹ï¾Ô®É skillXYZ ¨S¦³¯S§O®Ä¥Î¡C (¥i§ï)
+
+ 5. pip_pk_skill() ¤]¬O°½Ãi°µªk¡A·Q§ïªº¤H¦A¦Û¤v±q pip_fight.c §Û¹L¨Ó¡C
+
+#endif
+
+/*-------------------------------------------------------*/
+/* pip PK cache */
+/*-------------------------------------------------------*/
+
+
+static PCACHE *pshm;
+static PTMP *cp; /* §Úªº¤pÂû */
+static PTMP *ep; /* ¹ï¤âªº¤pÂû */
+
+
+static void
+pip_pkshm_init()
+{
+ pshm = shm_new(PIPSHM_KEY, sizeof(PCACHE));
+}
+
+
+static int
+pip_ptmp_new(pp)
+ PTMP *pp;
+{
+ PTMP *pentp, *ptail;
+
+ /* --------------------------------------------------- */
+ /* semaphore : critical section */
+ /* --------------------------------------------------- */
+
+#ifdef HAVE_SEM
+ sem_lock(BSEM_ENTER);
+#endif
+
+ pentp = pshm->pslot;
+ ptail = pentp + MAX_PIPPK_USER;
+
+ do
+ {
+ if (!pentp->inuse) /* §ä¨ì¤@­ÓªÅ¦ì¤l¡Acp-> «ü¦V */
+ {
+ memcpy(pentp, pp, sizeof(PTMP));
+ cp = pentp;
+
+#ifdef HAVE_SEM
+ sem_lock(BSEM_LEAVE);
+#endif
+
+ return 1;
+ }
+ } while (++pentp < ptail);
+
+ /* Thor:§i¶Duser¦³¤Hµn¥ý¤@¨B¤F */
+
+#ifdef HAVE_SEM
+ sem_lock(BSEM_LEAVE);
+#endif
+
+ return 0;
+}
+
+
+static int
+pip_ptmp_setup()
+{
+ PTMP ptmp;
+
+ memset(&ptmp, 0, sizeof(PTMP));
+
+ /* °ò¥»ÄÝ©Ê */
+ strcpy(ptmp.name, d.name);
+ strcpy(ptmp.userid, cuser.userid);
+
+ ptmp.sex = d.sex;
+ ptmp.level = d.level;
+
+ /* ¦å¸Éº¡ */
+ ptmp.hp = d.maxhp;
+ ptmp.mp = d.maxmp;
+ ptmp.vp = d.maxvp;
+ ptmp.sp = d.maxsp;
+ ptmp.maxhp = d.maxhp;
+ ptmp.maxmp = d.maxmp;
+ ptmp.maxvp = d.maxvp;
+ ptmp.maxsp = d.maxsp;
+
+ /* ¦UºØ§ðÀ»¯à¤O */
+ ptmp.combat = d.attack + (d.resist >> 2); /* ª«²z¨­¬q: ¨M©w¡u¦×·i¡B¨¾¿m¡vªº±j«× */
+ ptmp.magic = d.immune + (d.mskill >> 2); /* Å]ªk³y¸Ú: ¨M©w¡uªk³N-¦U¨t¡vªº±j«× */
+ ptmp.speed = d.speed + (d.hskill >> 2); /* ±Ó±¶§Þ¥©: ¨M©w¡u§Þ¯à-Å@¨­¡B§Þ¯à-»´¥\¡B§Þ¯à-¼Cªk¡vªº±j«× */
+ ptmp.spirit = d.brave + (d.etchics >> 2); /* ¤º¤O±j«×: ¨M©w¡u§Þ¯à-¤ßªk¡B§Þ¯à-®±ªk¡B§Þ¯à-¤Mªk¡vªº±j«× */
+ ptmp.charm = d.charm + (d.art >> 2); /* °Ê·P¾y¤O: ¨M©w¡u¾y´b¡B¥l³ê¡vªº±j«× */
+ ptmp.oral = d.speech + (d.manners >> 2); /* ¤f­YÄaªe: ¨M©w¡u»¡ªA¡Bº´°Ê¡vªº±j«× */
+ ptmp.cook = d.cook + (d.homework >> 2); /* ¬ü¨ý²i½Õ: ¨M©w¡u§Þ¯à-·t¾¹¡Bªk³N-ªvÀø¡v */
+
+ if (!pip_ptmp_new(&ptmp))
+ {
+ vmsg("©êºp¡APK ³õ«Èº¡¤F³á");
+ return 0;
+ }
+
+ return 1;
+}
+
+
+static void
+pip_ptmp_free()
+{
+ if (!cp || !cp->inuse)
+ return;
+
+#ifdef HAVE_SEM
+ sem_lock(BSEM_ENTER);
+#endif
+
+ cp->inuse = 0;
+
+#ifdef HAVE_SEM
+ sem_lock(BSEM_LEAVE);
+#endif
+}
+
+
+static PTMP *
+pip_ptmp_get(userid, inuse)
+ char *userid;
+ int inuse; /* 1:§ä¡u»W¶Õ«Ýµo¡vªº¤H¨Ó¬D¾Ô 2:§ä¡u¤U¬D¾Ô®Ñ¡vªº¬D¾ÔªÌ¦^À³ */
+{
+ PTMP *pentp, *ptail;
+
+ pentp = pshm->pslot;
+ ptail = pentp + MAX_PIPPK_USER;
+ do
+ {
+ if (pentp->inuse == inuse && !str_cmp(pentp->userid, userid))
+ return pentp;
+ } while (++pentp < ptail);
+
+ return NULL;
+}
+
+
+static void
+pip_ptmp_show()
+{
+ int max;
+ PTMP *pentp, *ptail;
+
+ clrfromto(7, 16);
+ move(10, 5);
+
+ max = 0;
+ pentp = pshm->pslot;
+ ptail = pentp + MAX_PIPPK_USER;
+ do
+ {
+ if (pentp->inuse)
+ {
+ max++;
+ prints("\033[1;3%dm%-20s\033[m", 2 + pentp->inuse, pentp->userid);
+
+ if (max % 4 == 0)
+ move(10 + max % 4, 5);
+ }
+ } while (++pentp < ptail);
+
+ move(8, 5);
+ prints("\033[1;31m¾Ô°«¤¤ \033[33m»W¶Õ«Ýµo \033[34m¬D¾ÔªÌµ¥«Ý¦^À³\033[m"
+ " ¥Ø«e³õ¤l¸Ì¦³ \033[1;36m%d/%d\033[m °¦Âû", max, MAX_PIPPK_USER);
+}
+
+
+/*-------------------------------------------------------*/
+/* ¹ï¾Ô¥D¨ç¦¡ */
+/*-------------------------------------------------------*/
+
+
+ /*-----------------------------------------------------*/
+ /* ½ü¬y±±¨î */
+ /*-----------------------------------------------------*/
+
+
+static void
+pip_pk_turn() /* ´«¹ï¤è */
+{
+ cp->done = 1;
+ ep->done = 0;
+}
+
+
+ /*-----------------------------------------------------*/
+ /* µe­±Åã¥Ü */
+ /*-----------------------------------------------------*/
+
+
+static void
+pip_pk_showfoot()
+{
+ out_cmd("", COLOR1 " ¾Ô°«©R¥O " COLOR2 " [1]¦×·i [2]§Þ¯à [3]¾y´b [4]¥l³ê [5]»¡ªA [6]º´°Ê [Q]»{¿é \033[m");
+}
+
+
+static void
+pip_pk_showing()
+{
+ int pic;
+ char inbuf1[20], inbuf2[20], inbuf3[20], inbuf4[20];
+
+ clear();
+ move(0, 0);
+
+ prints("\033[1;41m " BBSNAME PIPNAME " ¡ã\033[32m%s\033[37m%-13s \033[m\n",
+ cp->sex == 1 ? "¡ñ" : (cp->sex == 2 ? "¡ð" : "¡H"), cp->name);
+
+ /* ¿Ã¹õ¤W¤è¨q¥X§Úªº¤pÂû¸ê®Æ */
+
+ sprintf(inbuf1, "%d%s/%d%s", cp->hp > 1000 ? cp->hp / 1000 : cp->hp,
+ cp->hp > 1000 ? "K" : "", cp->maxhp > 1000 ? cp->maxhp / 1000 : cp->maxhp,
+ cp->maxhp > 1000 ? "K" : "");
+ sprintf(inbuf2, "%d%s/%d%s", cp->mp > 1000 ? cp->mp / 1000 : cp->mp,
+ cp->mp > 1000 ? "K" : "", cp->maxmp > 1000 ? cp->maxmp / 1000 : cp->maxmp,
+ cp->maxmp > 1000 ? "K" : "");
+ sprintf(inbuf3, "%d%s/%d%s", cp->vp > 1000 ? cp->vp / 1000 : cp->vp,
+ cp->vp > 1000 ? "K" : "", cp->maxvp > 1000 ? cp->maxvp / 1000 : cp->maxvp,
+ cp->maxvp > 1000 ? "K" : "");
+ sprintf(inbuf4, "%d%s/%d%s", cp->sp > 1000 ? cp->sp / 1000 : cp->sp,
+ cp->sp > 1000 ? "K" : "", cp->maxsp > 1000 ? cp->maxsp / 1000 : cp->maxsp,
+ cp->maxsp > 1000 ? "K" : "");
+
+ outs("\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¢w¢w¢w¢w¢w¢w¢w¢w¢w¢{\033[m\n");
+ prints("\033[1;31m¢x\033[33m¥Í ©R:\033[37m%-12s\033[33mªk ¤O:\033[37m%-12s\033[33m²¾°Ê¤O:\033[37m%-12s\033[33m¤º ¤O:\033[37m%-12s\033[31m¢x\033[m\n", inbuf1, inbuf2, inbuf3, inbuf4);
+ prints("\033[1;31m¢x\033[33m§ð À»:\033[37m%-12d\033[33mÅ] ªk:\033[37m%-12d\033[33m±Ó ±¶:\033[37m%-12d\033[33mªZ ³N:\033[37m%-12d\033[31m¢x\033[m\n", cp->combat, cp->magic, cp->speed, cp->spirit);
+ prints("\033[1;31m¢x\033[33m¾y ¤O:\033[37m%-12d\033[33m¤f ¤~:\033[37m%-12d\033[33m²i ½Õ:\033[37m%-12d\033[33mµ¥ ¯Å:\033[37m%-12d\033[31m¢x\033[m\n", cp->charm, cp->oral, cp->cook, cp->level);
+ outs("\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¢w¢w¢w¢w¢w¢w¢w¢w¢w¢}\033[m");
+
+ /* ¿Ã¹õ¤¤¶¡ 7~16 ¦C ¨q¥X©Çª«ªº¹ÏÀÉ */
+ pic = 101 + 100 * (rand() % 5) + rand() % 3; /* 101~501 102~502 103~503 ¤Q¤­¿ï¤@ */
+ show_badman_pic(pic);
+
+ /* ¿Ã¹õ¤U¤è¨q¥X¹ï¤èªº¤pÂû¸ê®Æ */
+
+ sprintf(inbuf1, "%d%s/%d%s", ep->hp > 1000 ? ep->hp / 1000 : ep->hp,
+ ep->hp > 1000 ? "K" : "", ep->maxhp > 1000 ? ep->maxhp / 1000 : ep->maxhp,
+ ep->maxhp > 1000 ? "K" : "");
+ sprintf(inbuf2, "%d%s/%d%s", ep->mp > 1000 ? ep->mp / 1000 : ep->mp,
+ ep->mp > 1000 ? "K" : "", ep->maxmp > 1000 ? ep->maxmp / 1000 : ep->maxmp,
+ ep->maxmp > 1000 ? "K" : "");
+ sprintf(inbuf3, "%d%s/%d%s", ep->vp > 1000 ? ep->vp / 1000 : ep->vp,
+ ep->vp > 1000 ? "K" : "", ep->maxvp > 1000 ? ep->maxvp / 1000 : ep->maxvp,
+ ep->maxvp > 1000 ? "K" : "");
+ sprintf(inbuf4, "%d%s/%d%s", ep->sp > 1000 ? ep->sp / 1000 : ep->sp,
+ ep->sp > 1000 ? "K" : "", ep->maxsp > 1000 ? ep->maxsp / 1000 : ep->maxsp,
+ ep->maxsp > 1000 ? "K" : "");
+
+ move(18, 0);
+ outs("\033[1;34m¢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¢w¢w¢w¢w¢w¢{\033[m\n");
+ prints("\033[1;34m¢x\033[32m©m ¦W:\033[37m%-12s\033[32m¢× ¢Ò:\033[37m%-12s\033[32m©Ê §O:\033[37m%-12s\033[32mµ¥ ¯Å:\033[37m%-12d\033[34m¢x\033[m\n", ep->name, ep->userid, ep->sex == 1 ? "¡ñ" : (ep->sex == 2 ? "¡ð" : "¡H"), ep->level);
+ prints("\033[1;34m¢x\033[32m¥Í ©R:\033[37m%-12s\033[32mªk ¤O:\033[37m%-12s\033[32m²¾°Ê¤O:\033[37m%-12s\033[32m¤º ¤O:\033[37m%-12s\033[34m¢x\033[m\n", inbuf1, inbuf2, inbuf3, inbuf4);
+ outs("\033[1;34m¢|¢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¢}\033[m\n");
+
+ pip_pk_showfoot();
+}
+
+
+static void
+pip_pk_ending()
+{
+ clrfromto(7, 16);
+ move(8, 0);
+
+ if (cp->hp > 0)
+ {
+ outs(" \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¢{\033[m\n");
+ prints(" \033[1;31m¢x \033[37mªZ³N¤j·|ªº¤pÂû\033[33m%-13s \033[31m¢x\033[m\n", cp->name);
+ prints(" \033[1;31m¢x \033[37m¥´±Ñ¤F±j«lªº¹ï¤â\033[32m%-13s \033[31m¢x\033[m\n", ep->name);
+ outs(" \033[1;31m¢x \033[37m«i´±©M¸gÅç³£¤W¤É¤F¤£¤Ö \033[31m¢x\033[m\n");
+ outs(" \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¢}\033[m");
+ vmsg("±z¥´±Ñ¤F¤@­Ó±jµwªº³Ã¥ë");
+ d.exp += ep->level;
+ }
+ else
+ {
+ outs(" \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¢{\033[m\n");
+ prints(" \033[1;31m¢x \033[37mªZ³N¤j·|ªº¤pÂû\033[33m%-13s \033[31m¢x\033[m\n", cp->name);
+ prints(" \033[1;31m¢x \033[37m³Q\033[32m%-13s\033[37m¹ï¤â¥´±o¸¨ªá¬y¤ô \033[31m¢x\033[m\n", ep->name);
+ outs(" \033[1;31m¢x \033[37m¨M©w¦^®a¦n¦n¦AÁë½m \033[31m¢x\033[m\n");
+ outs(" \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¢}\033[m");
+ vmsg("³Q¥´±Ñªº±z¤ß¤¤¬Û·í¤£¬O¨ý¹D");
+ d.exp -= cp->level;
+ }
+}
+
+
+ /*-----------------------------------------------------*/
+ /* ¦æ°Ê¨ç¦¡ */
+ /*-----------------------------------------------------*/
+
+
+static void
+pip_pk_combat() /* ¦×·i */
+{
+ int injure;
+
+ injure = cp->combat - (ep->combat >> 2);
+ if (injure > 0)
+ {
+ ep->hp -= injure;
+ show_fight_pic(1);
+ vmsg("±z³y¦¨¤F¹ï¤èªº¶Ë®`");
+ }
+ else
+ {
+ show_fight_pic(2);
+ vmsg("±zªº§ðÀ»¦b¹ï¤è²´¸Ì²ª½¬O·kÄo");
+ }
+ pip_pk_turn();
+}
+
+
+static void
+pip_pk_skill() /* §Þ¯à: ªZ¥\/Å]ªk */
+{
+ /* itoc.020217: Ãi±o¼g¹³ pip_fight.c ¸Ì­±¨ººØªº¤F¡A¦³¿³½ìªº¤H¦Û¤v§ÛµÛ§ï :p */
+
+ /* §Þ¯à¤£¦©ÂI¡A¦ý®ÄªG¤ñ¸û®t(¥u¦³¨ä¥Lªº¹ï¥b) */
+
+ int ch, class;
+ int injure[5] = {125, 200, 300, 450, 750};
+
+ out_cmd(COLOR1 " ªZ¥\\¿ï³æ " COLOR2 " [1]Å@¨­ [2]»´¥\\ [3]¤ßªk [4]®±ªk [5]¼Cªk [6]¤Mªk [7]·t¾¹ [Q]©ñ±ó \033[m",
+ COLOR1 " ªk³N¿ï³æ " COLOR2 " [A]ªvÀø [B]¹p¨t [C]¦B¨t [D]ª¢¨t [E]¤g¨t [F]­·¨t [G]¯S®í [Q]©ñ±ó \033[m");
+
+ for (;;)
+ {
+ ch = vkey();
+
+ if (ch == 'q')
+ {
+ pip_pk_showfoot();
+ return;
+ }
+
+ else if (ch == '1') /* Å@¨­ */
+ {
+ class = rand() % 10;
+ if (class == 0) /* 10% ªº¾÷²v¤Ï¼u§ðÀ»¡A¥i¦A«×§ðÀ» */
+ {
+ ep->hp -= cp->speed >> 3;
+ vmsg("¹ï¤âªº§ðÀ»¤Ï¼u¦^¥Lªº¨­¤W");
+ }
+ else if (class <= 2) /* 20% ªº¾÷²v¨Ï¹ï¤èª«²z§ðÀ»¥Ã»·­°§C¡A¥i¦A«×§ðÀ» */
+ {
+ ep->combat = ep->combat * 4 / 5;
+ vmsg("¹ï¤èªº¤â§á¨ì¤F¡A¬Ý¨Óµu®É¶¡¤º¤£·|«ì´_");
+ }
+ else /* 70% ªº¾÷²v¤°»ò³£¨S°µ */
+ {
+ vmsg("±z±Ä¨ú¨¾¿m±¹¬I");
+ break;
+ }
+ pip_pk_showfoot();
+ return;
+ }
+
+ else if (ch == '2') /* »´¥\ */
+ {
+ class = cp->speed >> 9;
+ if (class > 4)
+ class = 4;
+
+ cp->vp += injure[class]; /* ¸É vp ¤]­É¥Î injure[] */
+ if (cp->vp > cp->maxvp)
+ cp->vp = cp->maxvp;
+
+ vmsg("ºë¯«¹¡º¡¡A·Ç³Æ¦A¾Ô");
+ break;
+ }
+
+ else if (ch == '3') /* ¤ßªk */
+ {
+ class = cp->spirit >> 9;
+ if (class > 4)
+ class = 4;
+
+ cp->sp += injure[class]; /* ¸É sp ¤]­É¥Î injure[] */
+ if (cp->sp > cp->maxsp)
+ cp->sp = cp->maxsp;
+
+ vmsg("¬¡¤O¥R¨K¡A·Ç³Æ¦A¾Ô");
+ break;
+ }
+
+ else if (ch == '4') /* ®±ªk */
+ {
+ class = cp->spirit >> 9;
+ if (class > 4)
+ class = 4;
+
+ ep->hp -= injure[class] * (75 + rand() % 50) / 100;
+ vmsg("¥þ¨­ºë¤O¶°¤¤©ó´x¤W¡A¾Ä¤O¤@À»");
+ break;
+ }
+
+ else if (ch == '5') /* ¼Cªk */
+ {
+ class = cp->speed >> 9;
+ if (class > 4)
+ class = 4;
+
+ ep->hp -= injure[class] * (50 + rand() % 100) / 100;
+ vmsg("§Ö¼C±Ù¶Ã³Â¡A¯«¼CÂô¦¿´ò");
+ break;
+ }
+
+ else if (ch == '6') /* ¤Mªk */
+ {
+ class = cp->spirit >> 9;
+ if (class > 4)
+ class = 4;
+
+ ep->hp -= injure[class] * (30 + rand() % 140) / 100;
+ vmsg("¥þ¨­ºë¤O¶°¤¤©ó´x¤W¡A¾Ä¤O¤@À»");
+ break;
+ }
+
+ else if (ch == '7') /* ·t¾¹ */
+ {
+ class = cp->cook >> 9;
+ if (class > 4)
+ class = 4;
+
+ ep->hp -= injure[class] * (80 + rand() % 40) / 100;
+ vmsg("±zªºµæÀa¤¤¤U¤F¬rÃÄ¡A¹ï¤â¤£ª`·N´N¦Y¤F¤U¥h");
+ break;
+ }
+
+ else if (ch == 'a') /* ªvÀø */
+ {
+ class = cp->cook >> 10; /* ¸É hp ªºªùÂe¤ñ¸û°ª */
+ if (class > 4)
+ class = 4;
+
+ cp->hp += injure[class]; /* ¸É hp ¤]­É¥Î injure[] */
+ if (cp->hp > cp->maxhp)
+ cp->hp = cp->maxhp;
+
+ vmsg("¥R¹q¥H«á¡A¦A«×¥Xµo");
+ break;
+ }
+
+ else if (ch >= 'b' && ch <= 'f') /* ¦U¨tªk³N */
+ {
+ char buf[64];
+ char name[5][3] = {"¹p", "¦B", "ª¢", "¤g", "­·"};
+
+ class = cp->magic >> 9;
+ if (class > 4)
+ class = 4;
+
+ ep->hp -= injure[class] * (50 + rand() % 100) / 100;
+ sprintf(buf, "±z¬I®i¤F%s¨tªk³N¡A«Â¤O¤Q¨¬", name[ch - 'b']);
+ vmsg(buf);
+ break;
+ }
+
+ else if (ch == 'g') /* ¯S®í */
+ {
+ class = cp->magic >> 9;
+ if (class > 4)
+ class = 4;
+
+ cp->mp += injure[class]; /* ¸É mp ¤]­É¥Î injure[] */
+ if (cp->mp > cp->maxmp)
+ cp->mp = cp->maxmp;
+
+ vmsg("¯à¶q¥R¶ñ¡AÅ]¤O¦A²{");
+ break;
+ }
+ }
+
+ pip_pk_turn();
+}
+
+
+static void
+pip_pk_charm() /* ¾y´b: ¯Ó hp */
+{
+ int class;
+ char buf[80];
+ char name[5][9] = {"¤Z¤Ò«U¤l", "¦â±¡¨g", "¤p°­", "Às¯«", "¯¸ªø"};
+ int injure[5] = {250, 400, 600, 900, 1500};
+ int needhp[5] = {350, 600, 900, 1500, 3200};
+
+ class = cp->charm >> 8; /* ¬Û¹ï©ó¥H¤U¤TªÌ¡A¨ä©Ò»ÝªùÂe¤ñ¸û§C´N¥i¥H¬I®i±j¤j¾y´b³N¡A¦ý¯Óªº¬O hp */
+ if (class > 4)
+ class = 4;
+
+ if (cp->hp >= needhp[class])
+ {
+ cp->hp -= needhp[class];
+ ep->hp -= injure[class] * (75 + rand() % 50) / 100;
+ sprintf(buf, "¤@¸s%s¦b±zªº«ü¨Ï¤§¤U¡A«÷©R§ðÀ»¹ï¤è", name[class]);
+ vmsg(buf);
+ pip_pk_turn();
+ }
+ else
+ {
+ vmsg("±z¥þ¨­³£¬O¶Ë¤f¡AÁÙ·Q¾y´b½Ö");
+ pip_pk_showfoot();
+ }
+}
+
+
+static void
+pip_pk_summon() /* ¥l³ê: ¯Ó mp */
+{
+ int class;
+ char buf[80];
+ char name[5][9] = {"¥vµÜ©i", "ªêÀY¸Á", "²rªê", "»·¥j¥¨Às", "¦º¯«¼»¥¹"};
+ int injure[5] = {250, 400, 600, 900, 1500};
+ int needmp[5] = {350, 600, 900, 1500, 3200};
+
+ class = cp->charm >> 9;
+ if (class > 4)
+ class = 4;
+
+ if (cp->mp >= needmp[class])
+ {
+ cp->mp -= needmp[class];
+ ep->hp -= injure[class] * (75 + rand() % 50) / 100;
+ sprintf(buf, "±z¥l³ê¥X%s¡A­«­«¦aµ¹¤F¹ï¤è¤@À»", name[class]);
+ vmsg(buf);
+ pip_pk_turn();
+ }
+ else
+ {
+ vmsg("±z·P¨ì¥þ¨­¯h¾Î¡A¤°»ò¤]¥l³ê¤£¥X¨Ó");
+ pip_pk_showfoot();
+ }
+}
+
+
+static void
+pip_pk_convince() /* »¡ªA: ¯Ó vp */
+{
+ int class;
+ char buf[80];
+ char name[5][9] = {"­±¨ã©Ç¤H", "¾uÅ\\ÀY©Ç", "¬µ³J¶W¤H", "ªF®üÀs¤ý", "»ô¤Ñ¤j¸t"};
+ int injure[5] = {250, 400, 600, 900, 1500};
+ int needvp[5] = {350, 600, 900, 1500, 3200};
+
+ class = cp->oral >> 9;
+ if (class > 4)
+ class = 4;
+
+ if (cp->vp >= needvp[class])
+ {
+ cp->vp -= needvp[class];
+ ep->hp -= injure[class] * (75 + rand() % 50) / 100;
+ sprintf(buf, "±z¦¨¥\\¦a»¡ªA%s¨Ó§U±z¤@Áu¤§¤O", name[class]);
+ vmsg(buf);
+ pip_pk_turn();
+ }
+ else
+ {
+ vmsg("»·¤è¶Ç¨Ó¤@°}Án­µ¡G·Q»¡ªA§Ú¡A¦Aµ¥¤@¦Ê¦~§a");
+ pip_pk_showfoot();
+ }
+}
+
+
+static void
+pip_pk_incite() /* º´°Ê: ¯Ó sp */
+{
+ int class;
+ char buf[80];
+ char name[5][9] = {"¥¨ÃÇ©Ç", "¥¬­C¾|", "¦aº»¤ü", "¼Q¤õÀs", "¿K¤Ñ¨Ï"};
+ int injure[5] = {250, 400, 600, 900, 1500};
+ int needsp[5] = {350, 600, 900, 1500, 3200};
+
+ class = cp->oral >> 9;
+ if (class > 4)
+ class = 4;
+
+ if (cp->sp >= needsp[class])
+ {
+ cp->sp -= needsp[class];
+ ep->hp -= injure[class] * (40 + rand() % 120) / 100; /* Åܲ§©Ê¸û«e­±¤TªÌ¬°°ª */
+ sprintf(buf, "±z«i´±¦aº´°Ê%s¾ã­Ó±Ú¸s¨Ó¹ï¥I¼Ä¤H", name[class]);
+ vmsg(buf);
+ pip_pk_turn();
+ }
+ else
+ {
+ vmsg("²³¤H¤£¬°©Ò°Ê¡A±zªº­pµe¥¢±Ñ¤F");
+ pip_pk_showfoot();
+ }
+}
+
+
+static void
+pip_pk_man() /* ½ü¨ì§Ú¤U«ü¥O */
+{
+ /* ¨q¥X¾Ô°«¥Dµe­± */
+ pip_pk_showing();
+
+ while (!cp->done)
+ {
+ switch (vkey())
+ {
+ case '1': /* ¦×·i */
+ pip_pk_combat();
+ break;
+
+ case '2': /* §Þ¯à: ªZ¥\/Å]ªk */
+ pip_pk_skill();
+ break;
+
+ case '3': /* ¾y´b */
+ pip_pk_charm();
+ break;
+
+ case '4': /* ¥l³ê */
+ pip_pk_summon();
+ break;
+
+ case '5': /* »¡ªA */
+ pip_pk_convince();
+ break;
+
+ case '6': /* º´°Ê */
+ pip_pk_incite();
+ break;
+
+ case 'q': /* »{¿é */
+ cp->hp = 0;
+ pip_pk_turn();
+ break;
+ }
+ }
+}
+
+
+static void
+pip_pk_wait() /* µ¥«Ý¹ï¤è¤U«ü¥O */
+{
+ int fd;
+ struct timeval tv = {1, 100};
+
+ /* ¨q¥X¾Ô°«¥Dµe­± */
+ pip_pk_showing();
+
+ outz("µ¥«Ý¹ï¤èªº§ðÀ» Q)»{¿é");
+ refresh();
+
+ while (!ep->done)
+ {
+ fd = 1;
+ if (select(1, (fd_set *) &fd, NULL, NULL, &tv) > 0)
+ {
+ if (vkey() == 'q')
+ {
+ cp->done = 1; /* ¤G¤H³£±j­¢µ²§ô¦æ°Ê */
+ ep->done = 1;
+ cp->hp = 0; /* ¦³¨ÆÂ÷¶}ºâ¿é */
+ break;
+ }
+ }
+ }
+}
+
+
+/*-------------------------------------------------------*/
+/* ¹ï¾Ô¥D¿ï³æ */
+/*-------------------------------------------------------*/
+
+
+int
+pip_pk_menu()
+{
+ int ch;
+ char userid[IDLEN + 1];
+ struct timeval tv = {1, 100};
+
+ /* itoc.020327: ¦³­ÓÆZ¤jªº°ÝÃD¬O¡A¦pªG¹ï¾Ô¨ì¤@¥b¡A¨ä¤¤¤@¤HÂ_½u¤F¡A
+ ¥t¥~¤@¤H·|¶i¤J°j°é¡A¥u¯à«ö Q Â÷¶} */
+
+ if (d.hp <= 0)
+ return XEASY;
+
+ pip_pkshm_init();
+
+ pip_ptmp_show();
+
+ ch = ians(b_lines, 0, "¡· ¤pÂû¹ï¾Ô 1)»W¶Õ«Ýµo 2)¤U¬D¾Ô®Ñ [Q]Â÷¶} ");
+ if (ch == '1')
+ {
+ /* ³]©w cp-> */
+ if (!pip_ptmp_setup())
+ return XEASY;
+
+ cp->inuse = 1;
+
+ outz("µ¥­Ô¬D¾Ô¤¤ Q)Â÷¶}");
+ refresh();
+ do
+ {
+ ch = 1;
+ if (select(1, (fd_set *) &ch, NULL, NULL, &tv) > 0)
+ {
+ if (vkey() == 'q')
+ {
+ pip_ptmp_free();
+ return XEASY;
+ }
+ }
+ } while (!*cp->mateid);
+
+ if (!(ep = pip_ptmp_get(cp->mateid, 2)))
+ {
+ pip_ptmp_free();
+ return XEASY;
+ }
+ cp->done = 0; /* ³Q¬D¾ÔªÌ¥ý¦æ°Ê¡A¬D¾ÔªÌ´N·|³qª¾¥L */
+ cp->inuse = -1; /* ¨â¤è³£¶i¤J¾Ô°« */
+ ep->inuse = -1;
+ }
+ else if (ch == '2')
+ {
+ /* ¨M©w PK ¹ï¾Ô¹ï¶H ep-> */
+ if (!vget(b_lines, 0, msg_uid, userid, IDLEN + 1, DOECHO) ||
+ !str_cmp(cuser.userid, userid) ||
+ !(ep = pip_ptmp_get(userid, 1)))
+ {
+ return XEASY;
+ }
+
+ /* ³]©w cp-> */
+ if (!pip_ptmp_setup())
+ return XEASY;
+
+ strcpy(cp->mateid, userid);
+ strcpy(ep->mateid, cuser.userid);
+ cp->done = 1; /* ¬D¾ÔªÌ«á¦æ°Ê¡A´N·|¥D°Ê³qª¾³Q¬D¾ÔªÌ */
+ cp->inuse = 2;
+ }
+ else
+ {
+ return XEASY;
+ }
+
+ ch = d.tired;
+
+ for (;;) /* Âù¤è§ð¨¾ */
+ {
+ pip_pk_man();
+ if (ep->hp <= 0 || cp->hp <= 0)
+ break;
+
+ pip_pk_wait();
+ if (cp->hp <= 0 || ep->hp <= 0)
+ break;
+ }
+
+ /* µ²ªG§PÂ_ */
+ pip_pk_ending();
+
+ pip_ptmp_free();
+
+ d.tired = ch; /* ¥H§K PK ¥´¤Ó¤[¡APK µ²§ô«á¨Ó¦] tired ¹L°ª¦º±¼¤F */
+ return 0;
+}
+#endif /* HAVE_GAME */
diff --git a/pip/pip_play.c b/pip/pip_play.c
new file mode 100644
index 0000000..4d73d59
--- /dev/null
+++ b/pip/pip_play.c
@@ -0,0 +1,352 @@
+/*-------------------------------------------------------*/
+/* pip_play.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : ª±¼Ö¿ï³æ */
+/* create : / / */
+/* update : 01/08/15 */
+/* author : dsyan.bbs@forever.twbbs.org */
+/* modify : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+#ifdef HAVE_GAME
+
+#include "pip.h"
+
+
+/*-------------------------------------------------------*/
+/* ª±¼Ö¿ï³æ:´²¨B ®È¹C ¹B°Ê ¬ù·| ²q®± */
+/*-------------------------------------------------------*/
+
+
+int
+pip_play_stroll() /* ´²¨B */
+{
+ /* ¹w³]§ïÅÜ­È¡A­Y¦³°¸µo¨Æ¥ó¡A¥t¥~¥[¦¨©ó¤U */
+ count_tired(3, 3, 1, 100, 0); /* ¼W¥[¯h³Ò */
+ d.happy += rand() % 3 + 3;
+ d.satisfy += rand() % 2 + 1;
+ d.shit += rand() % 3 + 2;
+ d.hp -= rand() % 3 + 2;
+
+ switch (rand() % 10)
+ {
+ case 0:
+ d.happy += 6;
+ d.satisfy += 6;
+ show_play_pic(1);
+ vmsg("¹J¨ìªB¤ÍÅo ¯u¦n.... ^_^");
+ break;
+
+ case 1:
+ d.happy += 4;
+ d.satisfy += 8;
+ show_play_pic(2);
+ vmsg(d.sex == 1 ? "¬Ý¨ìº}«Gªº¤k¥ÍÅo ¯u¦n.... ^_^" : "¬Ý¨ì­^«Tªº¨k¥ÍÅo ¯u¦n.... ^_^");
+ break;
+
+ case 2:
+ d.money += 100;
+ d.happy += 4;
+ show_play_pic(3);
+ vmsg("¾ß¨ì¤F100¤¸¤F..­C­C­C....");
+ break;
+
+ case 3:
+ d.happy -= 10;
+ d.satisfy -= 3;
+ show_play_pic(4);
+ if (d.money > 50)
+ {
+ d.money -= 50;
+ vmsg("±¼¤F50¤¸¤F..¶ã¶ã¶ã....");
+ }
+ else
+ {
+ d.money = 0;
+ vmsg("¿ú±¼¥ú¥ú¤F..¶ã¶ã¶ã....");
+ }
+ break;
+
+ case 4:
+ d.happy += 3;
+ show_play_pic(5);
+ if (d.money > 50)
+ {
+ d.money -= 50;
+ vmsg("¥Î¤F50¤¸¤F..¤£¥i¥H½|§Ú³á....");
+ }
+ else
+ {
+ d.money = 0;
+ vmsg("¿ú³Q§Ú°½¥Î¥ú¥ú¤F..:p");
+ }
+ break;
+
+ case 5:
+ d.toy++;
+ show_play_pic(6);
+ vmsg("¦n´Î³á¡A¾ß¨ìª±¨ã¤F»¡.....");
+ break;
+
+ case 6:
+ d.cookie++;
+ show_play_pic(7);
+ vmsg("¦n´Î³á¡A¾ß¨ì»æ°®¤F»¡.....");
+ break;
+
+ case 7:
+ d.satisfy -= 5;
+ d.shit += 5;
+ show_play_pic(9);
+ vmsg("¯u¬O­Ë·° ¥i¥H¥h¶R·R°ê¼ú¨é");
+ break;
+
+ default:
+ show_play_pic(8);
+ vmsg("¨S¦³¯S§Oªº¨Æµo¥Í°Õ.....");
+ break;
+ }
+
+ if (d.happy > 100)
+ d.happy = 100;
+ if (d.satisfy > 100)
+ d.satisfy = 100;
+
+ return 0;
+}
+
+
+int
+pip_play_sport() /* ¹B°Ê */
+{
+ count_tired(3, 8, 1, 100, 1);
+ d.speed += 2 + rand() % 3;
+ d.weight -= rand() % 3 + 2;
+ d.shit += rand() % 5 + 10;
+ d.hp -= rand() % 2 + 8;
+ d.satisfy += rand() % 2 + 3;
+ if (d.satisfy > 100)
+ d.satisfy = 100;
+
+ show_play_pic(10);
+ vmsg("¹B°Ê¦n³B¦h¦h°Õ...");
+
+ return 0;
+}
+
+
+int
+pip_play_date() /* ¬ù·| */
+{
+ if (d.money < 150)
+ {
+ vmsg("¿ú¤£°÷¦h°Õ¡I¬ù·|Á`±oªáÂI¿ú¿ú");
+ }
+ else
+ {
+ count_tired(3, 6, 1, 100, 1);
+ d.money -= 150;
+ d.shit += rand() % 3 + 5;
+ d.hp -= rand() % 4 + 8;
+ d.character += rand() % 3 + 1;
+ d.happy += rand() % 5 + 12;
+ d.satisfy += rand() % 5 + 7;
+
+ if (d.happy > 100)
+ d.happy = 100;
+ if (d.satisfy > 100)
+ d.satisfy = 100;
+
+ show_play_pic(11);
+ vmsg("¬ù·|¥h ©I©I");
+ }
+ return 0;
+}
+
+
+int
+pip_play_outing() /* ­¥¹C */
+{
+ if (d.money < 250)
+ {
+ vmsg("¿ú¤£°÷¦h°Õ¡I®È¹CÁ`±oªáÂI¿ú¿ú");
+ }
+ else
+ {
+ count_tired(10, 45, 0, 100, 0);
+ d.money -= 250;
+ d.weight += rand() % 2 + 1;
+ d.hp -= rand() % 7 + 15;
+ d.character += rand() % 5 + 5;
+ d.happy += rand() % 10 + 12;
+ d.satisfy += rand() % 10 + 10;
+
+ if (d.happy > 100)
+ d.happy = 100;
+ if (d.satisfy > 100)
+ d.satisfy = 100;
+
+ switch (rand() % 4)
+ {
+ case 0:
+ d.art += rand() % 2;
+ show_play_pic(12);
+ vmsg(rand() % 2 ? "¤ß¤¤¦³¤@ªÑ²H²Hªº·Pı ¦nµÎªA³á...." : "¶³¤ô ¶~±¡ ¤ß±¡¦n¦h¤F.....");
+ break;
+
+ case 1:
+ d.art += rand() % 3;
+ show_play_pic(13);
+ vmsg(rand() % 2 ? "¦³¤s¦³¤ô¦³¸¨¤é §Î¦¨¤@´T¬üÄRªºµe.." : "¬ÝµÛ¬ÝµÛ ¥þ¨­¯h¾Î³£¤£¨£Åo..");
+ break;
+
+ case 2:
+ d.love += rand() % 3;
+ show_play_pic(14);
+ vmsg(rand() % 2 ? "¬Ý ¤Ó¶§§Ö¨S¤J¤ô¤¤Åo..." : "¯u¬O¤@´T¬ü´º");
+ break;
+
+ case 3:
+ d.hp += d.maxhp;
+ show_play_pic(15);
+ vmsg(rand() % 2 ? "Åý§Ú­ÌºÆ¨g¦b©]¸Ìªº®üÅy§a....©I©I.." : "²D²nªº®ü­·ªï­±Å§¨Ó ³Ì³ßÅw³oºØ·Pı¤F....");
+ }
+
+ /* ÀH¾÷¹J¨ì¤Ñ¨Ï */
+ if (rand() % 301 == 0)
+ pip_meet_angel();
+ }
+
+ return 0;
+}
+
+
+int
+pip_play_kite() /* ­·ºå */
+{
+ count_tired(4, 4, 1, 100, 0);
+ d.weight += (rand() % 2 + 2);
+ d.shit += rand() % 5 + 6;
+ d.hp -= rand() % 2 + 7;
+ d.affect += rand() % 4 + 6;
+ d.happy += rand() % 5 + 10;
+ d.satisfy += rand() % 3 + 12;
+
+ if (d.happy > 100)
+ d.happy = 100;
+ if (d.satisfy > 100)
+ d.satisfy = 100;
+
+ show_play_pic(16);
+ vmsg("©ñ­·ºå¯u¦nª±°Õ...");
+ return 0;
+}
+
+
+int
+pip_play_KTV() /* KTV */
+{
+ if (d.money < 250)
+ {
+ vmsg("¿ú¤£°÷¦h°Õ¡I°ÛºqÁ`±oªáÂI¿ú¿ú");
+ }
+ else
+ {
+ count_tired(10, 10, 1, 100, 0);
+ d.money -= 250;
+ d.shit += rand() % 5 + 6;
+ d.hp += rand() % 2 + 6;
+ d.art += rand() % 4 + 3;
+ d.happy += rand() % 3 + 20;
+ d.satisfy += rand() % 2 + 20;
+
+ if (d.happy > 100)
+ d.happy = 100;
+ if (d.satisfy > 100)
+ d.satisfy = 100;
+
+ show_play_pic(17);
+ vmsg("¤G°¦¦Ñªê..¤G°¦¦Ñªê..¶]±o§Ö..¶]±o§Ö..");
+ }
+ return 0;
+}
+
+
+static void
+guess_pip_lose()
+{
+ d.winn++;
+ d.shit += rand() % 3 + 2;
+ d.hp -= rand() % 2 + 3;
+ d.satisfy--;
+ d.happy -= 2;
+ outs("¤pÂû¿é¤F....~>_<~");
+ show_guess_pic(2);
+}
+
+
+static void
+guess_pip_tie()
+{
+ d.tiee++;
+ count_tired(2, 2, 1, 100, 1);
+ d.shit += rand() % 3 + 2;
+ d.hp -= rand() % 2 + 3;
+ d.satisfy++;
+ d.happy++;
+ outs("¥­¤â........-_-");
+ show_guess_pic(3);
+}
+
+
+static void
+guess_pip_win()
+{
+ d.losee++;
+ count_tired(2, 2, 1, 100, 1);
+ d.shit += rand() % 3 + 2;
+ d.hp -= rand() % 2 + 3;
+ d.satisfy += rand() % 3 + 2;
+ d.happy += rand() % 3 + 5;
+ outs("¤pÂûĹÅo....*^_^*");
+ show_guess_pic(1);
+}
+
+
+int
+pip_play_guess() /* ²q®±µ{¦¡ */
+{
+ int mankey; /* §Ú¥Xªº¤â */
+ int pipkey; /* ¤pÂû¥Xªº¤â */
+ char msg[3][5] = {"°Å¤M", "¥ÛÀY", "¥¬ "};
+
+ out_cmd("", COLOR1 " ²q®± " COLOR2 " [1]§Ú¥X°Å¤M [2]§Ú¥X¥ÛÀY [3]§Ú¥X¥¬°Õ [Q]¸õ¥X \033[m");
+
+ /* itoc.010814: ¥i¥H¤@ª½²q®± */
+ while (1)
+ {
+ /* §Ú¥ý¥X */
+ mankey = vkey() - '1';
+ if (mankey < 0 || mankey > 2)
+ return 0;
+
+ /* ¤pÂû¦A¥X */
+ pipkey = rand() % 3;
+
+ /* ¦b b_lines - 2 ¨q¥þ³¡ªº³Ó­t°T®§ */
+ move(b_lines - 2, 0);
+ prints("±z¡G%s ¤pÂû¡G%s ", msg[mankey], msg[pipkey]);
+
+ /* §P©w³Ó­t */
+ if (mankey == pipkey) /* ¥­¤â */
+ guess_pip_tie();
+ else if (pipkey == mankey + 1 || pipkey == mankey - 2) /* ¤pÂû³Ó */
+ guess_pip_win();
+ else /* ¤pÂû±Ñ */
+ guess_pip_lose();
+ }
+}
+#endif /* HAVE_GAME */
diff --git a/pip/pip_prac.c b/pip/pip_prac.c
new file mode 100644
index 0000000..c1937f4
--- /dev/null
+++ b/pip/pip_prac.c
@@ -0,0 +1,522 @@
+/*-------------------------------------------------------*/
+/* pip_prac.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : ­×¦æ¿ï³æ */
+/* create : / / */
+/* update : 01/08/14 */
+/* author : dsyan.bbs@forever.twbbs.org */
+/* modify : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+#ifdef HAVE_GAME
+
+#include "pip.h"
+
+
+/*-------------------------------------------------------*/
+/* ­×¦æ¿ï³æ:©À®Ñ ½mªZ ­×¦æ */
+/*-------------------------------------------------------*/
+
+/*-------------------------------------------------------*/
+/* ¸ê®Æ®w */
+/*-------------------------------------------------------*/
+
+static char *classrank[6] = {"¨S¦³", "ªì¯Å", "¤¤¯Å", "°ª¯Å", "¶i¶¥", "±M·~"};
+
+static int classmoney[11][2] =
+{
+ {0, 0}, {60, 110}, {70, 120}, {70, 120}, {80, 130}, {70, 120},
+ {60, 110}, {90, 140}, {70, 120}, {70, 120}, {80, 130}
+};
+
+static int classvariable[11][4] =
+{
+ {0, 0, 0, 0},
+ {5, 5, 4, 4}, {5, 7, 6, 4}, {5, 7, 6, 4}, {5, 6, 5, 4}, {7, 5, 4, 6},
+ {7, 5, 4, 6}, {6, 5, 4, 6}, {6, 6, 5, 4}, {5, 5, 4, 7}, {7, 5, 4, 7}
+};
+
+static char classword[11][5][41] = /* ­­¤G¤Q­Ó¤¤¤å¦r */
+{
+ {"½Ò¦W", "¦¨¥\\¤@", "¦¨¥\\¤G", "¥¢±Ñ¤@", "¥¢±Ñ¤G"},
+
+ {"¦ÛµM¬ì¾Ç", "¥¿¦b¥Î¥\\Ū®Ñ¤¤..", "§Ú¬OÁo©úº¡¤ÀÂû",
+ "³oÃD«ç»ò¬Ý¤£À´«¨..©Ç¤F", "°á¤£§¹¤F :~~~~~~"},
+
+ {"­ð¸Ö§ºµü", "§É«e©ú¤ë¥ú..ºÃ¬O¦a¤WÁ÷..", "¬õ¨§¥Í«n°ê..¬K¨Óµo´XªK..",
+ "£°..¤W½Ò¤£­n¬y¤f¤ô", "ÁÙ²V³á..§Ö­I­ð¸Ö¤T¦Ê­º"},
+
+ {"¯«¾Ç±Ð¨|", "«¢¹p¸ô¨È «¢¹p¸ô¨È", "Åý§Ú­Ìªï±µ¤Ñ°ó¤§ªù",
+ "£°..¦b·F¹À£«¡HÁÙ¤£¦n¦n°á", "¯«¾Ç«ÜÄYµÂªº..½Ð¦n¦n¾Ç..:("},
+
+ {"­x¾Ç±Ð¨|", "®]¤l§Lªk¬O¤¤°ê§Lªk®Ñ..", "±q­x³ø°ê¡A§Ú­n±a§L¥h¥´¥M",
+ "¤°»ò°}§Î£«¡H²V¶Ã°}§Î¡H @_@", "³s¤T°ê§Ó³£ª±¤£¦n¡AÁÙ·Q¥´¥M¡H"},
+
+ {"¼C¹D§Þ³N", "¬Ý§Úªº¼F®`..", "§Ú¨ë §Ú¨ë §Ú¨ë¨ë¨ë..",
+ "¼C­n®³Ã­¤@ÂI°Õ..", "¦b¨ë¦a¹«£«¡H¼C®³°ª¤@ÂI"},
+
+ {"®æ°«¾Ô§Þ", "¦Ù¦×¬O¦Ù¦× ©I©I..", "¤Q¤K»É¤H¦æ®ð´²..",
+ "¸}¦A½ð°ª¤@ÂI°Õ..", "®±ÀY«ç»ò³o»ò¨S¤O£«.."},
+
+ {"Å]ªk±Ð¨|", "§ÚÅÜ §ÚÅÜ §ÚÅÜÅÜÅÜ..", "³DÁx¡ÏÁµ»i§À¡Ï¹«¤ú¡ÏÃÊßï¡×¡H¡H",
+ "¤p¤ß±½©ª¤£­n¶Ã´§..", "£°¡ã¤f¤ô¤£­n¬y¨ì¤ô´¹²y¤W.."},
+
+ {"§»ö±Ð¨|", "­n·í°¦¦³Â§»ªªºÂû..", "¼Ú¶Ù­ò..£«­ù£«¨§..",
+ "«ç»ò¾Ç¤£·|£«¡H¤Ñ§r..", "¨«°_¸ô¨Ó¨S¨«¼Ë..¤Ñ£«.."},
+
+ {"øµe§Þ¥©", "«Ü¤£¿ù­ò..¦³¬ü³N¤Ñ¥÷..", "³o´TµeªºÃC¦â·f°tªº«Ü¦n..",
+ "¤£­n°­µe²Å°Õ..­n¥[ªo..", "¤£­n«rµeµ§°Õ..ÃaÃa¤pÂû³á.."},
+
+ {"»RÁЧޥ©", "¬ü±o´N¹³¤@°¦¤ÑÃZ³á..", "»RÁвӭM«Ü¦n³á..",
+ "¨­Åé¦A¬X³n¤@ÂI..", "«ô°U¤£­n³o»ò²Ê¾|.."}
+};
+
+
+/*-------------------------------------------------------*/
+/* ¨ç¦¡®w */
+/*-------------------------------------------------------*/
+
+
+static int
+pip_practice_gradeup(classnum, classgrade, newgrade) /* ­×¦æµ¥¯Å´£¤É */
+ int classnum; /* ½Ò¸¹ */
+ int classgrade; /* ¦~¯Å */
+ int newgrade; /* ·s¦~¯Å */
+{
+ /* itoc.0108802: ¬°¬Ù­pºâ¡Anewgrade ±q 0 ¶}©lºâ¡Aclassgrade ±q 1 ¶}©lºâ */
+ if (newgrade >= classgrade && newgrade < 5)
+ {
+ char buf[80];
+ sprintf(buf, "¤U¦¸´«¤W [%8s%4s½Òµ{]", classword[classnum][0], classrank[newgrade + 1]);
+ vmsg(buf);
+ }
+ return 0;
+}
+
+
+/* ¶Ç¤J:½Ò¸¹ µ¥¯Å ¥Í©R §Ö¼Ö º¡¨¬ żż ¶Ç¦^:ÅܼÆ12345 ¶Ç¦^: -1:©ñ±ó 0:¥¢±Ñ 1:¦¨¥\ */
+static int
+pip_practice_function(classnum, classgrade, pic1, pic2, change1, change2, change3, change4, change5)
+ int classnum; /* ­×¦æºØÃþ */
+ int classgrade; /* ­×¦æµ¥¯Å */
+ int pic1, pic2; /* ¹ÏÀÉ */
+ int *change1; /* ¥D­nÄݩʼW¥[ */
+ int *change2; /* ¦¸­nÄݩʼW¥[ */
+ int *change3; /* ªþ¥[ÄݩʼW¥[ */
+ int *change4; /* ¬Û«gÄݩʴî¤Ö */
+ int *change5; /* ¬Û¥¸Äݩʴî¤Ö */
+{
+ int grade, success;
+ char buf[80];
+
+ /* itoc.010803: Àˬd classgrade¡AÁ×§K·N¥~ */
+ /* ¦]¬°ÁÙ¨S update¡Alearn_skill ¥i¯à < 0 */
+ if (LEARN_LEVEL < 0)
+ {
+ vmsg("±z¤w¸g²Ö¨ìÃz¤F");
+ return -1;
+ }
+
+ /* itoc.010803: classgrade À³¸Ó¥u±q 1~5 ¯Å */
+ if (classgrade < 0)
+ grade = 1;
+ else if (classgrade > 5)
+ grade = 5;
+ else
+ grade = classgrade;
+
+ /* ¿úªººâªk */
+ success = grade * classmoney[classnum][0] + classmoney[classnum][1]; /* ­É¥Î success */
+ sprintf(buf, " [%8s%4s½Òµ{]­nªá %d¤¸¡A½T©w­n¶Ü(Y/N)¡H[Y] ", classword[classnum][0], classrank[grade], success);
+
+ if (ians(b_lines - 2, 0, buf) == 'n')
+ return -1;
+ if (d.money < success)
+ {
+ vmsg("«Ü©êºp¡A±zªº¿ú¤£°÷³á");
+ return -1;
+ }
+ count_tired(4, 5, 1, 100, 1);
+ d.money -= success;
+
+ /* ¦¨¥\»P§_ªº§PÂ_ */
+ success = (d.hp / 2 + rand() % 20 > d.tired); /* 1: ¦¨¥\ 0: ¥¢±Ñ */
+
+ d.hp -= rand() % 5 + classvariable[classnum][0];
+ d.happy -= rand() % 5 + classvariable[classnum][1];
+ d.satisfy -= rand() % 5 + classvariable[classnum][2];
+ d.shit += rand() % 5 + classvariable[classnum][3];
+
+ /* ¥[ªºÂI¼Æ¦¨¥\¬O¥¢±Ñªº 1.5 ­¿¡A¦©ªºÂI¼Æ¥¢±Ñ¬O¦¨¥\ªº 1.5 ­¿ */
+ /* learn_skill ¥i±q 2%~100% */
+ *change1 = (7 + 6 * (rand() % grade)) * 2 * LEARN_LEVEL / (3 - success); /* ¥D­nÄݩʥ[´Á±æ­È 3*classgrade+4 (­Y­×¦æ¦¨¥\¥B°²³] learn_level = 100%) */
+ *change2 = (5 + 4 * (rand() % grade)) * 2 * LEARN_LEVEL / (3 - success); /* ¦¸­nÄݩʥ[´Á±æ­È 2*classgrade+3 (­Y­×¦æ¦¨¥\¥B°²³] learn_level = 100%) */
+ *change3 = (3 + 2 * (rand() % grade)) * 2 * LEARN_LEVEL / (3 - success); /* ªþ¥[Äݩʥ[´Á±æ­È classgrade+2 (­Y­×¦æ¦¨¥\¥B°²³] learn_level = 100%) */
+
+ *change4 = (5 + rand() % grade) * 2 / (1 + success); /* ¬Û«gÄݩʦ©´Á±æ­È classgrade/2+4.5 (­Y­×¦æ¦¨¥\) */
+ *change5 = (5 + rand() % grade) * 2 / (2 + success); /* ¬Û¥¸Äݩʦ©´Á±æ­È classgrade/3+3 (­Y­×¦æ¦¨¥\) */
+
+ /* ¶Ã¼Æ¿ï¤@­Ó¹Ï¨Ó¨q */
+ if (rand() % 2)
+ show_practice_pic(pic1);
+ else
+ show_practice_pic(pic2);
+
+ vmsg(classword[classnum][3 - 2 * success + rand() % 2]); /* ²Ä¤@¤G­Ó¬O¦¨¥\°T®§¡A¤T¥|­Ó¬O¥¢±Ñ°T®§ */
+ return success;
+}
+
+
+/*-------------------------------------------------------*/
+/* ­×¦æ¿ï³æ:©À®Ñ ½mªZ ­×¦æ */
+/*-------------------------------------------------------*/
+
+
+/* itoc.010802: ¦UÃþ classgrage ªº¬É©w¬O¤pÂûªº¬Y¶µÄÝ©Ê / 200¡A·íµM¤]¥i¥H¥[Åv³B²z */
+
+int
+pip_practice_classA()
+{
+ /* ¢z¢w¢w¢w¢w¢s¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢{ */
+ /* ¢x¦ÛµM¬ì¾Ç¢x¥¿ÄݩʡG´¼¤O¡B§ÜÅ]¡B±q¯Ê ¢x */
+ /* ¢x ¢u¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢t */
+ /* ¢x ¢x­tÄݩʡG«H¥õ¡B±q¯Ê ¢x */
+ /* ¢|¢w¢w¢w¢w¢r¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢} */
+
+ int class;
+ int change1, change2, change3, change4, change5;
+
+ class = (d.wisdom * 3 + d.immune * 2) / 1000 + 1; /* ¬ì¾Ç */
+
+ if (pip_practice_function(1, class, 11, 12, &change1, &change2, &change3, &change4, &change5) < 0)
+ return 0;
+
+ d.wisdom += change1;
+ d.immune += change2;
+ d.belief -= change4;
+ d.classA++;
+
+ if (d.belief < 0)
+ d.belief = 0;
+
+ /* itoc.010802: ¶Ã¼Æ¾Ç·|¨s·¥ªk³N */
+ if (rand() % 30 == 0)
+ pip_learn_skill(-7);
+
+ pip_practice_gradeup(1, class, (d.wisdom * 3 + d.immune * 2) / 1000);
+ return 0;
+}
+
+
+int
+pip_practice_classB()
+{
+ /* ¢z¢w¢w¢w¢w¢s¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢{ */
+ /* ¢x ¸Öµü ¢x¥¿ÄݩʡG·P¨ü¡B®ð½è¡BÃÀ³N ¢x */
+ /* ¢x ¢u¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢t */
+ /* ¢x ¢x­tÄݩʡG±q¯Ê¡B§ÜÅ] ¢x */
+ /* ¢|¢w¢w¢w¢w¢r¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢} */
+
+ int class;
+ int change1, change2, change3, change4, change5;
+
+ class = (d.affect * 3 + d.character * 2 + d.art) / 1200 + 1; /* ¸Öµü */
+
+ if (pip_practice_function(2, class, 21, 22, &change1, &change2, &change3, &change4, &change5) < 0)
+ return 0;
+
+ d.affect += change1;
+ d.character += change2;
+ d.art += change3;
+ d.immune -= change5;
+ d.classB++;
+
+ if (d.immune < 0)
+ d.immune = 0;
+
+ /* itoc.010814: ¶Ã¼Æ¾Ç·|¤ßªk */
+ if (rand() % 10 == 0)
+ pip_learn_skill(3);
+
+ pip_practice_gradeup(2, class, (d.affect * 3 + d.character * 2 + d.art) / 1200);
+ return 0;
+}
+
+
+int
+pip_practice_classC()
+{
+ /* ¢z¢w¢w¢w¢w¢s¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢{ */
+ /* ¢x ¯«¾Ç ¢x¥¿ÄݩʡG«H¥õ¡B§ÜÅ]¡B´¼¤O ¢x */
+ /* ¢x ¢u¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢t */
+ /* ¢x ¢x­tÄݩʡG§ðÀ»¡B±q¯Ê ¢x */
+ /* ¢|¢w¢w¢w¢w¢r¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢} */
+
+ int class;
+ int change1, change2, change3, change4, change5;
+
+ class = (d.belief * 3 + d.immune * 2 + d.wisdom) / 1200 + 1; /* ¯«¾Ç */
+
+ if (pip_practice_function(3, class, 31, 32, &change1, &change2, &change3, &change4, &change5) < 0)
+ return 0;
+
+ d.belief += change1;
+ d.immune += change2;
+ d.wisdom += change3;
+ d.attack -= change4;
+ d.classC++;
+
+ if (d.attack < 0)
+ d.attack = 0;
+
+ /* itoc.010802: ¶Ã¼Æ¾Ç·|ªvÀøªk³N */
+ if (rand() % 10 == 0)
+ pip_learn_skill(-1);
+
+ pip_practice_gradeup(3, class, (d.belief * 3 + d.immune * 2 + d.wisdom) / 1200);
+ return 0;
+}
+
+
+int
+pip_practice_classD()
+{
+ /* ¢z¢w¢w¢w¢w¢s¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢{ */
+ /* ¢x ­x¾Ç ¢x¥¿ÄݩʡG¾Ô°«§Þ³N¡B´¼¤O¡B±q¯Ê ¢x */
+ /* ¢x ¢u¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢t */
+ /* ¢x ¢x­tÄݩʡG·P¨ü¡B±q¯Ê ¢x */
+ /* ¢|¢w¢w¢w¢w¢r¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢} */
+
+ int class;
+ int change1, change2, change3, change4, change5;
+
+ class = (d.hskill * 3 + d.wisdom * 2) / 1000 + 1;
+
+ if (pip_practice_function(4, class, 41, 42, &change1, &change2, &change3, &change4, &change5) < 0)
+ return 0;
+
+ d.hskill += change1;
+ d.wisdom += change2;
+ d.affect -= change4;
+ d.classD++;
+
+ if (d.affect < 0)
+ d.affect = 0;
+
+ /* itoc.010814: ¶Ã¼Æ¾Ç·|Å@¨­ */
+ if (rand() % 10 == 0)
+ pip_learn_skill(1);
+
+ pip_practice_gradeup(4, class, (d.hskill * 3 + d.wisdom * 2) / 1000);
+ return 0;
+}
+
+
+int
+pip_practice_classE()
+{
+ /* ¢z¢w¢w¢w¢w¢s¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢{ */
+ /* ¢x ¼C³N ¢x¥¿ÄݩʡG§ðÀ»¡B¾Ô°«§Þ³N¡B¨¾¿m ¢x */
+ /* ¢x ¢u¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢t */
+ /* ¢x ¢x­tÄݩʡG·P¨ü¡B±q¯Ê ¢x */
+ /* ¢|¢w¢w¢w¢w¢r¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢} */
+
+ int class;
+ int change1, change2, change3, change4, change5;
+
+ class = (d.attack * 3 + d.hskill * 2 + d.resist) / 1200 + 1;
+
+ if (pip_practice_function(5, class, 51, 52, &change1, &change2, &change3, &change4, &change5) < 0)
+ return 0;
+
+ d.attack += change1;
+ d.hskill += change2;
+ d.resist += change3;
+ d.affect -= change4;
+ d.classE++;
+
+ if (d.affect < 0)
+ d.affect = 0;
+
+ /* itoc.010802: ¶Ã¼Æ¾Ç·|¼Cªk */
+ if (rand() % 10 == 0)
+ pip_learn_skill(5);
+
+ pip_practice_gradeup(5, class, (d.attack * 3 + d.hskill * 2 + d.resist) / 1200);
+ return 0;
+}
+
+
+int
+pip_practice_classF()
+{
+ /* ¢z¢w¢w¢w¢w¢s¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢{ */
+ /* ¢x ®æ°« ¢x¥¿ÄݩʡG¨¾¿m¡B³t«×¡B§ðÀ» ¢x */
+ /* ¢x ¢u¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢t */
+ /* ¢x ¢x­tÄݩʡG·P¨ü¡B±q¯Ê ¢x */
+ /* ¢|¢w¢w¢w¢w¢r¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢} */
+
+ int class;
+ int change1, change2, change3, change4, change5;
+
+ class = (d.resist * 3 + d.speed * 2 + d.attack) / 1200 + 1;
+
+ if (pip_practice_function(6, class, 61, 62, &change1, &change2, &change3, &change4, &change5) < 0)
+ return 0;
+
+ d.resist += change1;
+ d.speed += change2;
+ d.attack += change3;
+ d.affect -= change4;
+ d.classF++;
+
+ if (d.affect < 0)
+ d.affect = 0;
+
+ /* itoc.010802: ¶Ã¼Æ¾Ç·|®±ªk */
+ if (rand() % 10 == 0)
+ pip_learn_skill(4);
+
+ pip_practice_gradeup(6, class, (d.resist * 3 + d.speed * 2 + d.attack) / 1200);
+ return 0;
+}
+
+
+int
+pip_practice_classG()
+{
+ /* ¢z¢w¢w¢w¢w¢s¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢{ */
+ /* ¢x Å]ªk ¢x¥¿ÄݩʡGÅ]ªk§Þ³N¡B§ÜÅ]¡B±q¯Ê ¢x */
+ /* ¢x ¢u¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢t */
+ /* ¢x ¢x­tÄݩʡG§ðÀ»¡B³t«× ¢x */
+ /* ¢|¢w¢w¢w¢w¢r¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢} */
+
+ int class;
+ int change1, change2, change3, change4, change5;
+
+ class = (d.mskill * 3 + d.immune * 2) / 1000 + 1;
+
+ if (pip_practice_function(7, class, 71, 72, &change1, &change2, &change3, &change4, &change5) < 0)
+ return 0;
+
+ d.mskill += change1;
+ d.immune += change2;
+ d.attack -= change4;
+ d.speed -= change5;
+ d.classG++;
+
+ if (d.attack < 0)
+ d.attack = 0;
+ if (d.speed < 0)
+ d.speed = 0;
+
+ /* itoc.010802: ¶Ã¼Æ¾Ç·|¤­¨tÅ]ªk¤§¤@ */
+ if (rand() % 7 == 0)
+ pip_learn_skill(- 2 - rand() % 5);
+
+ pip_practice_gradeup(7, class, (d.mskill * 3 + d.immune * 2) / 1000);
+ return 0;
+}
+
+
+int
+pip_practice_classH()
+{
+ /* ¢z¢w¢w¢w¢w¢s¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢{ */
+ /* ¢x §»ö ¢x¥¿ÄݩʡG§»ö¡B®ð½è¡B½Í¦R ¢x */
+ /* ¢x ¢u¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢t */
+ /* ¢x ¢x­tÄݩʡG³t«×¡B±q¯Ê ¢x */
+ /* ¢|¢w¢w¢w¢w¢r¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢} */
+
+ int class;
+ int change1, change2, change3, change4, change5;
+
+ class = (d.manners * 3 + d.character * 2 + d.speech) / 1200 + 1;
+
+ if (pip_practice_function(8, class, 81, 82, &change1, &change2, &change3, &change4, &change5) < 0)
+ return 0;
+
+ d.manners += change1;
+ d.character += change2;
+ d.speech += change3;
+ d.speed -= change4;
+ d.classH++;
+
+ if (d.speed < 0)
+ d.speed = 0;
+
+ pip_practice_gradeup(8, class, (d.manners * 3 + d.character * 2 + d.speech) / 1200);
+ return 0;
+}
+
+
+int
+pip_practice_classI()
+{
+ /* ¢z¢w¢w¢w¢w¢s¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢{ */
+ /* ¢x øµe ¢x¥¿ÄݩʡGÃÀ³N¡B·P¨ü¡B±q¯Ê ¢x */
+ /* ¢x ¢u¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢t */
+ /* ¢x ¢x­tÄݩʡG±q¯Ê¡B±q¯Ê ¢x */
+ /* ¢|¢w¢w¢w¢w¢r¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢} */
+
+ int class;
+ int change1, change2, change3, change4, change5;
+
+ class = (d.art * 3 + d.character * 2) / 1000 + 1;
+
+ if (pip_practice_function(9, class, 91, 92, &change1, &change2, &change3, &change4, &change5) < 0)
+ return 0;
+
+ d.art += change1;
+ d.character += change2;
+ d.classI++;
+
+ /* itoc.010814: ¶Ã¼Æ¾Ç·|¤Mªk */
+ if (rand() % 10 == 0)
+ pip_learn_skill(6);
+
+ pip_practice_gradeup(9, class, (d.art * 3 + d.character * 2) / 1000);
+ return 0;
+}
+
+
+int
+pip_practice_classJ()
+{
+ /* ¢z¢w¢w¢w¢w¢s¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢{ */
+ /* ¢x »RÁÐ ¢x¥¿ÄݩʡGÃÀ³N¡B¾y¤O¡B®ð½è ¢x */
+ /* ¢x ¢u¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢t */
+ /* ¢x ¢x­tÄݩʡG§ðÀ»¡BÅ]ªk§Þ³N ¢x */
+ /* ¢|¢w¢w¢w¢w¢r¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¢} */
+
+ int class;
+ int change1, change2, change3, change4, change5;
+
+ class = (d.art * 3 + d.charm * 2 + d.character) / 1200 + 1;
+
+ if (pip_practice_function(10, class, 101, 102, &change1, &change2, &change3, &change4, &change5) < 0)
+ return 0;
+
+ d.art += change1;
+ d.charm += change2;
+ d.character += change3;
+ d.attack -= change4;
+ d.mskill -= change5;
+ d.classJ++;
+
+ if (d.attack < 0)
+ d.attack = 0;
+ if (d.mskill < 0)
+ d.mskill = 0;
+
+ /* itoc.010802: ¶Ã¼Æ¾Ç·|»´¥\ */
+ if (rand() % 10 == 0)
+ pip_learn_skill(2);
+
+ pip_practice_gradeup(10, class, (d.art * 3 + d.charm * 2 + d.character) / 1200);
+ return 0;
+}
+#endif /* HAVE_GAME */
diff --git a/pip/pip_quest.c b/pip/pip_quest.c
new file mode 100644
index 0000000..50f3a82
--- /dev/null
+++ b/pip/pip_quest.c
@@ -0,0 +1,418 @@
+/* ----------------------------------------------------- */
+/* pip_quest.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/* ----------------------------------------------------- */
+/* target : ¾Ô°«¿ï³æ */
+/* create : 01/12/22 */
+/* update : / / */
+/* author : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/* ----------------------------------------------------- */
+
+
+#include "bbs.h"
+
+#ifdef HAVE_GAME
+
+#include "pip.h"
+
+
+/*-------------------------------------------------------*/
+/* ©Ò¦³¥ô°È: ¦^¶Ç 1 ªí¥Ü§¹¦¨ ¦^¶Ç 0 ªí¥Ü¨S¦³§¹¦¨ */
+/*-------------------------------------------------------*/
+
+
+/* ¼g¥ô°È«D±`²³æ¡G¼g¤@­Ó¨ç¦¡ pip_quest_??()¡A¨Ã¥[¤J quest_cb¡A
+ ¨Ã§ï PIPQUEST_NUM¡A³Ì«á½s¿è¤@­Ó etc/game/pip/quest/pic?? §Y¥i */
+
+
+ /*-----------------------------------------------------*/
+ /* pip_quest_1~99 ¨ú±oª««~ªº¥ô°È */
+ /*-----------------------------------------------------*/
+
+
+static int
+pip_quest_1() /* ¨ú±o°ò¦¸Ë³Æ */
+{
+ /* ¥þ¨­¨C­Ó³¡¦ì³£¬ïµÛ¸Ë³Æ´Nºâ¦X®æ */
+ if (d.weaponhead && d.weaponhand && d.weaponshield && d.weaponbody && d.weaponfoot)
+ {
+ /* ­ì¸Ë³Æ²¾°£ */
+ d.weaponhead = d.weaponhand = d.weaponshield = d.weaponbody = d.weaponfoot = 0;
+ d.equiphead[0] = d.equiphand[0] = d.equipshield[0] = d.equipbody[0] = d.equipfoot[0] = '\0';
+
+ d.tired = 0;
+ vmsg("±z¤£¦A·P¨ì¯h¾Î¤F");
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static int
+pip_quest_2() /* ¨ú±o¶Àª÷¸Ë³Æ */
+{
+ /* ¥þ¨­¨C­Ó³¡¦ìªº¸Ë³Æ¦WºÙ³£¥]§t¡uª÷¡v³o¦r´Nºâ¦X®æ */
+ if (strstr(d.equiphead, "ª÷") && strstr(d.equiphand, "ª÷") &&
+ strstr(d.equipshield, "ª÷") && strstr(d.equipbody, "ª÷") && strstr(d.equipfoot, "ª÷"))
+ {
+ /* ­ì¸Ë³Æ²¾°£ */
+ d.weaponhead = d.weaponhand = d.weaponshield = d.weaponbody = d.weaponfoot = 0;
+ d.equiphead[0] = d.equiphand[0] = d.equipshield[0] = d.equipbody[0] = d.equipfoot[0] = '\0';
+
+ d.happy = 100;
+ d.tired = 0;
+ vmsg("±zªººë¯«¦Ê­¿");
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static int
+pip_quest_3() /* ¨ú±oÃħ÷ */
+{
+ /* ¨ú±o¤jÁÙ¤¦¡BÆFªÛ¡B¤j¸É¤Y¡B¤d¦~¤Hçx¡B¶Â¥ÉÂ_Äò»I¡B¤Ñ¤s³·½¬¦U¤@ */
+ if (d.pill && d.medicine && d.burger && d.ginseng && d.paste && d.snowgrass)
+ {
+ d.pill--;
+ d.medicine--;
+ d.burger--;
+ d.ginseng--;
+ d.paste--;
+ d.snowgrass--;
+ d.happy = 100;
+ vmsg("§U¤H¬°§Ö¼Ö¤§¥»");
+ return 1;
+ }
+
+ return 0;
+}
+
+
+ /*-----------------------------------------------------*/
+ /* pip_quest_101~199 ÄݩʹF¦¨ªº¥ô°È */
+ /*-----------------------------------------------------*/
+
+
+static int
+pip_quest_101() /* §¹¥þ°·±d */
+{
+ /* ¹F¨ì¤£¯h²Ö¡B¤£¥Í¯f¡B¤£»êż */
+ if (d.tired == 0 && d.sick == 0 && d.shit == 0)
+ {
+ d.food++;
+ d.cookie++;
+ vmsg("¯u´Î¡A¤k¯«½ç±z¤@¨Ç­¹ª«");
+ return 1;
+ }
+
+ return 0;
+}
+
+
+ /*-----------------------------------------------------*/
+ /* pip_quest_201~299 ¥´±Ñ©Çª«ªº¥ô°È */
+ /*-----------------------------------------------------*/
+
+
+static int
+pip_quest_201() /* ¥´­Ë¯f¬r */
+{
+ /* ¥´­Ë¤@°¦¯à¤O¤ñ¦Û¤v°ªªº©Çª« */
+
+ int level;
+ playrule m;
+
+ strcpy(m.name, "Åܺدf¬r");
+ level = d.level + 5;
+ m.hp = m.maxhp = 100 + level * level;
+ m.attack = m.spirit = m.magic = m.armor = m.dodge = level * 15;
+ m.money = 0;
+ m.exp = 0;
+ m.attribute = +7; /* ±Mªø: ¨s·¥ªk³N */
+ m.pic = 004;
+
+ if (pip_vs_man(m, 0)) /* ¤pÂû¹ï¾Ô¼Ä¤H (­É¥ÎªZ³N¤j·|) */
+ {
+ d.hexp += d.level;
+ d.mexp += d.level;
+ vmsg("±z¥´±Ñ¤F³o°¦Åܺدf¬r¡Aµû»ù´£¤É");
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static int
+pip_quest_202() /* ¤Q¤j´c¤H */
+{
+ /* ¨Ì§Ç¥´­Ë¼Æ°¦©Çª« */
+ /* ­Y¥´±Ñ¤Q¤j´c¤H¡A¦WÁn´£°ª¡F¤Ï¤§¡A½m¦¨¶ù¦ç¯«¥\ */
+
+ int i;
+ struct playrule badmanlist[] = /* ¤Q¤j´c¤H¬Û·í©óµ¥¯Å 40 ~ 50 ªº©Çª« */
+ {
+ /* name[13] attribute hp maxhp attack spirit magic armor dodge money exp pic */
+ /* ·R¥Î§lºë */ "§õ¤j¼L", +3, 1875, 1875, 500, 460, 350, 500, 500, 0, 220, 001,
+ /* ·R¥Î blitz */ "³±¤E«Õ", +2, 1850, 1850, 520, 470, 320, 420, 630, 0, 220, 002,
+ /* ·R¥Î·t¾¹ */ "«¢«¢¨à", +7, 1750, 1750, 450, 450, 370, 400, 520, 0, 220, 003,
+ /* ·R¥Îª¢¨tÅ]ªk */ "±O¼b¼b", -4, 1635, 1635, 430, 340, 640, 360, 470, 0, 220, 004,
+ /* §ðÀ»¤O¯S±j */ "§ù ±þ", 0, 2400, 2400, 610, 570, 280, 550, 500, 0, 250, 005,
+ };
+
+ if (ians(b_lines - 1, 0, "¤Q¤j´c¤H¬Ý°_¨Ó¶W±j¡A±z­n§äÀ°¤â¶Ü(Y/N)¡H[Y] ") != 'n')
+ {
+ if (ians(b_lines - 1, 0, "½Ð½Ö·íÀ°¤â¡H(1)¿PªF¤Ñ (2)¿P¦è¤Ñ (3)¿P«n¤Ñ (4)¿P¥_¤Ñ ") == '3')
+ {
+ /* itoc.050320: ­Yµ¥¯Å¤Ó§C®É±µ¨ì³o­Ó¥ô°È·|¥´¤£Ä¹¡A©Ò¥H­n´£¨Ñ½â©Û :p */
+ vmsg("¦b¤j«L¿P«n¤ÑªºÀ°§U¤§¤U¡A±z¦¨¥\\¦a°£¥h¤Q¤j´c¤H");
+ return 1;
+ }
+ else
+ {
+ vmsg("¥L©Úµ´¤F±zªº½Ð¨D¡A¬Ý¨Ó±z¥u¦n¦Û¤v¤W¤F");
+ }
+ }
+
+ for (i = 0; i< 5; i++)
+ {
+ if (!pip_vs_man(badmanlist[i], 0)) /* ¤pÂû¹ï¾Ô¼Ä¤H (­É¥ÎªZ³N¤j·|) */
+ {
+ vmsg("±z³Q¤Q¤j´c¤H³ò§ð¡A¥þ¨­ºë¯ß­ÑÂ_");
+ d.hp = 1;
+ d.mp = 0;
+ d.vp = 0;
+ d.sp = 0;
+
+ /* ¶ù¦ç¯«¥\: ®³ maxmp ¥h´« maxsp */
+ i = rand() % 10;
+ if (d.maxmp > i)
+ {
+ d.maxmp -= i;
+ d.maxsp += i;
+ vmsg("¦b¯«Âå¸U¬K¬yªºªvÀø¤§¤U¡A±z¤Ï½m¦¨¶ù¦ç¯«¥\\");
+ }
+
+ return 0;
+ }
+ }
+
+ d.hexp += 100;
+ d.mexp += 100;
+ vmsg("±z¦¨¥\\¦a°£¥h´c¤H¨¦ªº©Ò¦³Ãa³J¡A¦WÁn¤j´T´£¤É");
+ return 1;
+}
+
+
+ /*-----------------------------------------------------*/
+ /* pip_quest_301~399 ¼Æ¾Ç­pºâªº¥ô°È */
+ /*-----------------------------------------------------*/
+
+
+static int
+pip_quest_301() /* ¤À°t¯]Ä_ */
+{
+ char ans[3];
+
+ vget(b_lines, 0, "§ÚÀ³¸Ó¥i¥H¤À¨ì´X­Ó¯]Ä_©O¡H", ans, 3, DOECHO);
+ if (atoi(ans) == 2)
+ {
+ d.social += 10;
+ vmsg("Åý§Ú·Q·Q¬Ý°Ú¡I¾³¡A¨S¿ù¡A±z¹ê¦b¤ÓÁo©ú¤F");
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static int
+pip_quest_302() /* 0.9999... ´`Àô¤p¼Æ */
+{
+ int num;
+ char ans1[4], ans2[4];
+
+ if (vget(b_lines, 0, "´`Àô¤p¼Æ 0.9999... ¤Æ¬°¤À¼Æ¡A¤À¥À¬O ", ans1, 4, DOECHO) &&
+ vget(b_lines, 0, "´`Àô¤p¼Æ 0.9999... ¤Æ¬°¤À¼Æ¡A¤À¤l¬O ", ans2, 4, DOECHO))
+ {
+ if ((num = atoi(ans1)) && (num == atoi(ans2))) /* ¤À¥À¤£¯à¬° 0 */
+ {
+ d.wisdom += 10;
+ vmsg("¨S¿ù¡A0.9999... ´N¬O 1");
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+
+ /*-----------------------------------------------------*/
+ /* pip_quest_401~499 ®æ¨¥ªY½àªº¥ô°È */
+ /*-----------------------------------------------------*/
+
+
+static int
+pip_quest_401() /* ºA«×¦Ê¤À¦Ê */
+{
+ if (ians(b_lines - 1, 0, "1)ª¾ÃÑ 2)§V¤O 3)ºA«× ") == '3')
+ {
+ d.affect += 5;
+ d.toman += 5;
+ vmsg("¬Oªº¡A°ß¦³¦Ê¤À¦ÊªººA«×¤~¯àÀò±o²³¤Hªº´L·q");
+ return 1;
+ }
+
+ return 0;
+}
+
+
+ /*-----------------------------------------------------*/
+ /* quest_cb[] ¥ô°È¦Cªí */
+ /*-----------------------------------------------------*/
+
+
+#define PIPQUEST_NUM 9
+
+
+/* static KeyFunc quest_cb[] = */
+static KeyFunc quest_cb[PIPQUEST_NUM + 1] = /* §â PIPQUEST_NUM «ü©w¶i¥h¡A¦pªG¦³¿ù¡A¥i¥H¦b compile ¤¤¬Ý¥X */
+{
+ /* ´M§äª««~ */
+ 1, pip_quest_1,
+ 2, pip_quest_2,
+ 3, pip_quest_3,
+
+ /* ÄݩʹF¦¨ */
+ 101, pip_quest_101,
+
+ /* ¥´±Ñ©Çª« */
+ 201, pip_quest_201,
+ 202, pip_quest_202,
+
+ /* ¼Æ¾Ç°ÝÃD */
+ 301, pip_quest_301,
+ 302, pip_quest_302,
+
+ /* ®æ¨¥ªY½à */
+ 401, pip_quest_401,
+
+ 0, NULL /* µ²§ô¥ô°È¦Cªí */
+};
+
+
+/*-------------------------------------------------------*/
+/* ¥ô°È¨ç¦¡ */
+/*-------------------------------------------------------*/
+
+
+static int /* 1:¬d¸ß¥ô°È 0:¨S¦³¥ô°È */
+pip_quest_query(quest) /* ¬d¸ßÂÂ¥ô°È */
+ int quest; /* ¥ô°È½s¸¹ */
+{
+ if (!quest && !(quest = d.quest))
+ {
+ vmsg("±z¥Ø«e¨S¦³¥ô°È¦b¨­");
+ return 0;
+ }
+ show_quest_pic(quest);
+ return 1;
+}
+
+
+int /* 1:¨ú±o·s¥ô°È 0:¨ú®ø¨ú±o©Î¤w¦³¥ô°È */
+pip_quest_new() /* ¨ú±o·s¥ô°È */
+{
+ if (ians(b_lines - 1, 0, "±z¤w¹F¤É¯Å¼Ð·Ç¡AÄ@·N±µ¨üªø¦Ñ«ü¬£¥ô°È¶Ü(Y/N)¡H[N] ") == 'y')
+ {
+ d.quest = quest_cb[rand() % PIPQUEST_NUM].key;
+ pip_quest_query(d.quest);
+ vmsg("¥h§a¡A°õ¦æ³o­Ó¥ô°Èµ²§ô«á§Ú´N½á¤©±z§ó°ªªº¯à¤O");
+ return 1;
+ }
+ return 0;
+}
+
+
+static int /* 1:¥ô°È§¹¦¨ 0:¥ô°È¥¢±Ñ */
+pip_quest_done() /* §¹¦¨¥ô°È */
+{
+ KeyFunc *cb;
+ int key;
+
+ if (!d.quest)
+ {
+ vmsg("±z¥Ø«e¨S¦³¥ô°È¦b¨­");
+ return 0;
+ }
+
+ /* itoc.µù¸Ñ: ¬O¤£¬O¦Ò¼{´« binary search? */
+ for (cb = quest_cb; (key = cb->key); cb++)
+ {
+ if (key == d.quest)
+ {
+ key = (*(cb->func)) (); /* 1:§¹¦¨ 0:¥¢±Ñ */
+ pip_levelup(key);
+ return key;
+ }
+ }
+
+ vmsg("½Ð§i¶D¯¸ªø¡A§ä¤£¨ì¦¹¥ô°Èªºµ{¦¡"); /* À³¸Ó¤£¥i¯à¥X²{ */
+ return 0;
+}
+
+
+static int /* 1:©ñ±óÂÂ¥ô°È 0:¨ú®ø©ñ±ó©Î¨S¦³¥ô°È */
+pip_quest_abort() /* ©ñ±óÂÂ¥ô°È */
+{
+ if (!d.quest)
+ {
+ vmsg("±z¥Ø«e¨S¦³¥ô°È¦b¨­");
+ }
+ else if (ians(b_lines - 1, 0, "±z½T©w­n©ñ±ó²{¦³ªº¥ô°È¶Ü(Y/N)¡H[N] ") == 'y')
+ {
+ pip_levelup(0);
+ return 1;
+ }
+ else
+ {
+ vmsg("ÁÙ¬O¤£­n©ñ±ó¦n¤F");
+ }
+ return 0;
+}
+
+
+/*-------------------------------------------------------*/
+/* ¥ô°È¥D¿ï³æ */
+/*-------------------------------------------------------*/
+
+
+int
+pip_quest_menu()
+{
+ while (1)
+ {
+ out_cmd("", COLOR1 " ¥ô°È " COLOR2 " [1]§¹¦¨ [2]¬d¸ß [3]©ñ±ó [Q]¸õ¥X \033[m");
+
+ switch (vkey())
+ {
+ case 'q':
+ return 0;
+
+ case '1':
+ pip_quest_done();
+ break;
+
+ case '2':
+ pip_quest_query(0);
+ break;
+
+ case '3':
+ pip_quest_abort();
+ break;
+ }
+ }
+}
+#endif /* HAVE_GAME */
diff --git a/pip/pip_race.c b/pip/pip_race.c
new file mode 100644
index 0000000..e12dc6f
--- /dev/null
+++ b/pip/pip_race.c
@@ -0,0 +1,287 @@
+/*-------------------------------------------------------*/
+/* pip_race.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : ¦¬Ã¬©u¤ñÁÉ */
+/* create : / / */
+/* update : 03/03/31 */
+/* author : dsyan.bbs@forever.twbbs.org */
+/* modify : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+#ifdef HAVE_GAME
+
+#include "pip.h"
+
+
+/*-------------------------------------------------------*/
+/* ³­Áɪ̦W³æ */
+/*-------------------------------------------------------*/
+
+
+static char racename[4][9] = {"ªZ°«¤j·|", "ÃÀ³N¤j®i", "¬Ó®a»R·|", "²i¶¹¤jÁÉ"};
+
+
+/* name[13] attribute hp maxhp attack spirit magic armor dodge money exp pic */
+/* °Ñ¦Ò badman_generate() ¤¤ªºµù¸Ñ¡A¦³©Çª«µ¥¯Åªº´Á±æ­È */
+struct playrule racemanlist[] =
+{
+ /* ¬Û·í©óµ¥¯Å 05 ªº©Çª« */ "¯üÄR¸­¶ð", 0, 30, 30, 50, 50, 50, 50, 50, 50, 25, 001,
+ /* ¬Û·í©óµ¥¯Å 10 ªº©Çª« */ "µá¼Ú§Q®R", 0, 95, 95, 99, 99, 99, 99, 99, 99, 50, 002,
+ /* ¬Û·í©óµ¥¯Å 20 ªº©Çª« */ "ªü©g´µ¥Õ", 0, 300, 300, 350, 40, 40, 200, 200, 200, 100, 003,
+ /* ¬Û·í©óµ¥¯Å 40 ªº©Çª« */ "©¬¦h¹p¦è", 0, 1200, 1200, 350, 600, 200, 600, 100, 400, 200, 004,
+ /* ¬Û·í©óµ¥¯Å 60 ªº©Çª« */ "¥d¬ü©Ô¬ü", 0, 2700, 2700, 600, 600, 600, 600, 600, 600, 300, 005,
+ /* ¬Û·í©óµ¥¯Å 90 ªº©Çª« */ "¥§¥j©Ô´µ", 0, 6100, 6100, 900, 799, 999, 799, 999, 900, 450, 006,
+};
+
+static int player[3]; /* ¤T¦ì³­ÁÉ¿ï¤âªº¸¹½X¡A±j«×¬O player[2] > player[1] > player[0] */
+
+
+/*-------------------------------------------------------*/
+/* ªZ°«¤j·| */
+/*-------------------------------------------------------*/
+
+
+/* itoc.030331.¹CÀ¸³]­p: ©I¥s pip_vs_man() ³o¨ç¦¡¶i¤J¾Ô°«µe­±¡A¥´Ä¹¦h¤Ö¤H¨Óºâ¦¨ÁZ */
+
+static int /* ¦^¶Ç: ŤF´X­Ó¤H >=3:«a­x 2:¨È­x 1:©u­x <=0:³Ì«á¤@¦W */
+pip_race_eventA()
+{
+ int i, winorlost;
+ char buf[80];
+
+ /* ±q racemanlist ¤»¦ì¤¤¬D¥X¤T­Ó³­ÁÉªÌ */
+ player[0] = rand() % 2;
+ player[1] = rand() % 2 + 2;
+ player[2] = rand() % 2 + 4;
+
+ winorlost = 0;
+ for (i = 0; i < 3; i++)
+ {
+ sprintf(buf, "±zªº²Ä %d ­Ó¹ï¤â¬O%s", i + 1, racemanlist[player[i]].name);
+ vmsg(buf);
+
+ if (pip_vs_man(racemanlist[player[i]], 0)) /* ¤pÂû¹ï¾Ô¼Ä¤H */
+ winorlost++; /* Àò³Ó */
+ }
+
+ return winorlost;
+}
+
+
+/*-------------------------------------------------------*/
+/* ÃÀ³N¤j®i */
+/*-------------------------------------------------------*/
+
+
+/* itoc.030331.¹CÀ¸³]­p: §¹¥þ¥Ñ d.art ©M d.charm ¨Ó¨M©wÃÀ³N¤j®iªº¦¨ÁZ */
+
+static int /* ¦^¶Ç: ŤF´X­Ó¤H >=3:«a­x 2:¨È­x 1:©u­x <=0:³Ì«á¤@¦W */
+pip_race_eventB()
+{
+ /* ±q racemanlist ¤»¦ì¤¤¬D¥X¤T­Ó³­ÁÉªÌ */
+ player[0] = rand() % 2 + 4;
+ player[1] = rand() % 2;
+ player[2] = rand() % 2 + 2;
+
+ /* ª½±µ¬Ý¯à¤O¡A¨S¦³¤ñÁɹLµ{ */
+ return ((d.art * 2 + d.character) / 600);
+}
+
+
+/*-------------------------------------------------------*/
+/* ¬Ó®a»R·| */
+/*-------------------------------------------------------*/
+
+
+/* itoc.030331.¹CÀ¸³]­p: §¹¥þ¥Ñ d.art ©M d.charm ¨Ó¨M©w¬Ó®a»R·|ªº¦¨ÁZ */
+
+static int /* ¦^¶Ç: ŤF´X­Ó¤H >=3:«a­x 2:¨È­x 1:©u­x <=0:³Ì«á¤@¦W */
+pip_race_eventC()
+{
+ /* ±q racemanlist ¤»¦ì¤¤¬D¥X¤T­Ó³­ÁÉªÌ */
+ player[0] = rand() % 2 + 2;
+ player[1] = rand() % 2 + 4;
+ player[2] = rand() % 2;
+
+ /* ª½±µ¬Ý¯à¤O¡A¨S¦³¤ñÁɹLµ{ */
+ return ((d.art * 2 + d.charm) / 600);
+}
+
+
+/*-------------------------------------------------------*/
+/* ²i¶¹¤jÁÉ */
+/*-------------------------------------------------------*/
+
+
+/* itoc.030331.¹CÀ¸³]­p: ­ì«h¤W¬O¥Ñ d.cook ©M d.affect ¨Ó¨M©w²i¶¹¤jÁɪº¦¨ÁZ¡A
+ ¦ý¬O¦pªGµæ¦â©M¥Ø«eªºª¬ºA§k¦Xªº¸Ü¦³¥[¤À®ÄªG¡A¤Ï¤§«h¦³¦©¤À®ÄªG */
+
+static int /* ¦^¶Ç: ŤF´X­Ó¤H >=3:«a­x 2:¨È­x 1:©u­x <=0:³Ì«á¤@¦W */
+pip_race_eventD()
+{
+ int winorlost;
+
+ /* ±q racemanlist ¤»¦ì¤¤¬D¥X¤T­Ó³­ÁÉªÌ */
+ player[0] = rand() % 2 + 4;
+ player[1] = rand() % 2 + 2;
+ player[2] = rand() % 2;
+
+ winorlost = ians(b_lines - 1, 0, "±z·QµN­þºØ¤f¨ýªºµæ¦â¡H0)®a±` 1)»Ä 2)²¢ 3)­W 4)»¶ [0] ");
+ if (winorlost == '1')
+ winorlost = 70 - d.satisfy * 2; /* ¶V¤£º¡¨¬µN¥X¨Óªºµæ¤~¶V±a¦³¾L¨ý (¤£º¡¨¬·|¦Y¾L) */
+ else if (winorlost == '2')
+ winorlost = d.happy * 2 - 130; /* ¶V¬O§Ö¼ÖµN¥X¨Óªºµæ¤~¶V±a¦³²¢«× («Ü§Ö¼Ö·|²¢»e) */
+ else if (winorlost == '3')
+ winorlost = d.shit * 2 - 130; /* ¶V¬O»êżµN¥X¨Óªºµæ¤~¶V±a¦³­W³B (¤j«K¬O­Wªº:p) */
+ else if (winorlost == '4')
+ winorlost = d.sick * 2 - 130; /* ¶V¬O¥Í¯fµN¥X¨Óªºµæ¤~¶V±a¦³»¶­» (¥Í¯f¨ýı¤£ÆF) */
+ else
+ winorlost = 0; /* ®a±`µæ»Pª¬ºAµLÃö */
+
+ winorlost += d.cook * 2 + d.affect;
+
+ return (winorlost / 600);
+}
+
+
+/*-------------------------------------------------------*/
+/* ¤ñÁɵ²ªG */
+/*-------------------------------------------------------*/
+
+
+static void
+pip_race_ending(winorlost, mode)
+ int winorlost; /* ŤF´X­Ó¤H >=3:«a­x 2:¨È­x 1:©u­x <=0:³Ì«á¤@¦W */
+ int mode; /* °Ñ¥[­þ¤@ºØ¤ñÁÉ */
+{
+ char *name1, *name2, *name3, *name4;
+ char buf[80];
+
+ if (winorlost <= 0) /* ³Ì«á¤@¦W */
+ {
+ name1 = racemanlist[player[2]].name;
+ name2 = racemanlist[player[1]].name;
+ name3 = racemanlist[player[0]].name;
+ name4 = d.name;
+ }
+ else if (winorlost == 1) /* ©u­x¼úª÷ 2000 */
+ {
+ name1 = racemanlist[player[2]].name;
+ name2 = racemanlist[player[1]].name;
+ name3 = d.name;
+ name4 = racemanlist[player[0]].name;
+ d.money += 2000;
+ }
+ else if (winorlost == 2) /* ¨È­x¼úª÷ 5000 */
+ {
+ name1 = racemanlist[player[2]].name;
+ name2 = d.name;
+ name3 = racemanlist[player[1]].name;
+ name4 = racemanlist[player[0]].name;
+ d.money += 5000;
+ }
+ else /* «a­x¼úª÷ 10000 */
+ {
+ name1 = d.name;
+ name2 = racemanlist[player[2]].name;
+ name3 = racemanlist[player[1]].name;
+ name4 = racemanlist[player[0]].name;
+ d.money += 10000;
+ }
+
+ clear();
+ move(6, 13);
+ prints("\033[1;37m¡ã¡ã¡ã\033[32m¥»©¡ %s µ²ªG´¦¾å\033[37m¡ã¡ã¡ã\033[m", racename[mode - 1]);
+ move(8, 15);
+ prints("\033[1;41m «a­x \033[0;1m¡ã\033[1;33m%-10s\033[36m ¼úª÷ %d\033[m", name1, 10000);
+ move(10, 15);
+ prints("\033[1;41m ¨È­x \033[0;1m¡ã\033[1;33m%-10s\033[36m ¼úª÷ %d\033[m", name2, 5000);
+ move(12, 15);
+ prints("\033[1;41m ©u­x \033[0;1m¡ã\033[1;33m%-10s\033[36m ¼úª÷ %d\033[m", name3, 2000);
+ move(14, 15);
+ prints("\033[1;41m ³Ì«á \033[0;1m¡ã\033[1;33m%-10s\033[36m\033[0m", name4);
+ sprintf(buf, "¤µ¦~ªº%sµ²§ôÅo «á¦~¦A¨Ó§a..", racename[mode - 1]);
+ vmsg(buf);
+}
+
+
+/*-------------------------------------------------------*/
+/* ¦¬Ã¬©u¤ñÁÉ */
+/*-------------------------------------------------------*/
+
+
+int /* !=0:°Ñ¥[ªº¶µ¥Ø 0:¤£°Ñ¥[ */
+pip_race_main() /* ¦¬Ã¬©u */
+{
+ int ch;
+ int winorlost; /* ŤF´X­Ó¤H >=3:«a­x 2:¨È­x 1:©u­x <=0:³Ì«á¤@¦W */
+
+ clear();
+ move(10, 14);
+ outs("\033[1;33m¥m©N¥m©N¡ã ¨¯­Wªº¶l®tÀ°§Ú­Ì°e«H¨Ó¤F³á...\033[m");
+ vmsg("¶â §â«H¥´¶}¬Ý¬Ý§a...");
+
+ show_resultshow_pic(0);
+
+ move(b_lines - 2, 0);
+ prints("[A]%s [B]%s [C]%s [D]%s [Q]©ñ±ó¡G", racename[0], racename[1], racename[2], racename[3]);
+ do
+ {
+ ch = vkey();
+ } while (ch != 'q' && (ch < 'a' || ch > 'd'));
+
+ if (ch == 'q')
+ {
+ vmsg("¤µ¦~¤£°Ñ¥[°Õ.....:(");
+ d.happy -= rand() % 10 + 10;
+ d.satisfy -= rand() % 10 + 10;
+ d.relation -= rand() % 10;
+ return 0;
+ }
+
+ ch -= 'a' - 1;
+ show_resultshow_pic(ch);
+ vmsg("¤µ¦~¦@¦³¥|¤H°ÑÁÉ¡ã²{¦b¤ñÁɶ}©l");
+
+ switch (ch)
+ {
+ case 1: /* ªZ°«¤j·| */
+ winorlost = pip_race_eventA();
+ d.hexp += rand() % 10 + 20 * winorlost;
+ d.exp += rand() % 10 + d.level * winorlost;
+ break;
+
+ case 2: /* ÃÀ³N¤j®i */
+ winorlost = pip_race_eventB();
+ d.art += rand() % 10 + 20 * winorlost;
+ d.character += rand() % 10 + 20 * winorlost;
+ break;
+
+ case 3: /* ¬Ó®a»R·| */
+ winorlost = pip_race_eventC();
+ d.art += rand() % 10 + 20 * winorlost;
+ d.charm += rand() % 10 + 20 * winorlost;
+ break;
+
+ case 4: /* ²i¶¹¤jÁÉ */
+ winorlost = pip_race_eventD();
+ d.cook += rand() % 10 + 20 * winorlost;
+ d.family += rand() % 10 + 20 * winorlost;
+ break;
+ }
+
+ pip_race_ending(winorlost, ch);
+
+ /* ¦pªG°Ñ¥[ªº¸Ü¡A«ì´_©Ò¦³ÄÝ©Ê */
+ d.tired = 0;
+ d.hp = d.maxhp;
+ d.happy += rand() % 20;
+ d.satisfy += rand() % 20;
+ d.relation += rand() % 10;
+
+ return ch;
+}
+#endif /* HAVE_GAME */
diff --git a/pip/pip_royal.c b/pip/pip_royal.c
new file mode 100644
index 0000000..4364419
--- /dev/null
+++ b/pip/pip_royal.c
@@ -0,0 +1,200 @@
+/* ----------------------------------------------------- */
+/* pip_royal.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/* ----------------------------------------------------- */
+/* target : ¤pÂû royal */
+/* create : / / */
+/* update : 01/08/14 */
+/* author : dsyan.bbs@forever.twbbs.org */
+/* modify : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/* ----------------------------------------------------- */
+
+
+#include "bbs.h"
+
+#ifdef HAVE_GAME
+
+#include "pip.h"
+
+
+/* royalset: num name needmode needvalue addtoman maxtoman words1 words2 */
+
+struct royalset royallist[] =
+{
+ "T", "«ô³X¹ï¶H", 0, 0, 0, 0, NULL, NULL,
+ "A", "¬Ó«°ÃM§L³s", 1, 10, 15, 100, "±z¯u¦n¡A¨Ó³­§Ú²á¤Ñ..", "¦u½Ã¬PªÅªº¦w¥þ¬O«Ü¨¯­Wªº..",
+ "B", "¢¯¢¯¢¶¯S°È", 1, 100, 25, 200, "¯u¬O§»ªªº¤pÂû..§Ú³ßÅw..", "¯S°È´N¬O¯µ±K«OÅ@¯¸ªø¦w¥þªº¤H..",
+ "C", "Âí°ê¤j±N­x", 1, 200, 30, 250, "·í¦~¨º­Ó¾Ô§Ð«Üºë±m³á..", "±z¯u¬O°ª¶QÀu¶®ªº¤pÂû..",
+ "D", "°Ñ¿ÑÁ`°Èªø", 1, 300, 35, 300, "§ÚÀ°¯¸ªøºÞ²z³o­Ó°ê®a­ò..", "±zªºÁn­µ«Ü¦nÅ¥­C..§Ú«Ü³ßÅw³á..:)",
+ "E", "ºÞ²z°Æ¯¸ªø", 1, 400, 35, 300, "±z«Ü¦³±Ð¾i­ò¡I«Ü°ª¿³»{Ãѱz..", "Àu¶®ªº±z¡A½ÐÅý§ÚÀ°±z¬èºÖ..",
+ "F", "¨t²Î¯¸ªø", 1, 500, 40, 350, "±z¦n¥i·R³á..§Ú³ßÅw±z­ò..", "¹ï°Õ..¥H«á­n¦h¦h¨Ó©M§Úª±³á..",
+ "G", "µ{¦¡¯¸ªø", 1, 550, 40, 350, "§i¶D±z­ò¡A¸ò±zÁ¿¸Ü«Ü§Ö¼Ö³á..", "¨Ó¡A§¤§Ú½¥»\\¤W¡AÅ¥§ÚÁ¿¬G¨Æ..",
+ "H", SYSOPNICK, 1, 600, 50, 400, "¤@¯¸¤§ªø³d¥ô­«¤j§r..:)..", "ÁÂÁ±zÅ¥§ÚÁ¿¸Ü..¥H«á­n¦h¨Ó³á..",
+ "I", "ºÆ¨gÄé¤ô¸s", 2, 60, 20, 150, "¤£¿ù­ò..ÆZ¾÷ÆFªº³á..«Ü¥i·R..", "¨Ó¡A§Ú­Ì¤@°_¨ÓÄé¤ô§a..",
+ "J", "«C¦~«ÓªZ©x", 0, 0, 0, 0, "±z¦n¡A§Ú¬OªZ©x¡A­è±qÃä¹Ò¦^¨Ó", "§Æ±æ¤U¦¸Áٯਣ¨ì±z..:)",
+ NULL, NULL, 0, 0, 0, 0, NULL, NULL
+};
+
+
+static int
+pip_go_palace_screen(p)
+ struct royalset *p;
+{
+ char inbuf1[128], inbuf2[20];
+ char *needmode[3] = {" ", "§»öªí²{¡Ö", "½Í¦R§Þ¥©¡Ö"};
+ int n, a, b, choice, change;
+ int save[11];
+
+ /* ¨q¥X©Ò¦³¥i¥H«ô³Xªº¤H */
+ clear();
+ show_palace_pic(0);
+ move(13, 0);
+ outs(" \033[1;31m¢z¢w¢w¢w¢w¢w¢w¢t\033[37;41m ¨Ó¨ìÁ`¥q¥O³¡¤F ½Ð¿ï¾Ü±z±ý«ô³Xªº¹ï¶H\033[0;1;31m¢u¢w¢w¢w¢w¢w¢w¢{\033[m\n");
+ outs(" \033[1;31m¢x ¢x\033[m\n");
+
+ for (n = 0; n < 5; n++)
+ {
+ a = 2 * n + 1;
+ b = 2 * n + 2;
+ sprintf(inbuf1, "%-10s%3d", needmode[p[a].needmode], p[a].needvalue);
+
+ if (n == 4) /* ¤ý¤l */
+ sprintf(inbuf2, "%-10s", needmode[p[b].needmode]);
+ else
+ sprintf(inbuf2, "%-10s%3d", needmode[p[b].needmode], p[b].needvalue);
+
+ if ((d.seeroyalJ == 1 && n == 4) || (n != 4))
+ {
+ prints(" \033[1;31m¢x\033[36m(\033[37m%s\033[36m)\033[33m%-10s \033[37m%-14s \033[36m(\033[37m%s\033[36m)\033[33m%-10s \033[37m%-14s \033[31m¢x\033[m\n",
+ p[a].num, p[a].name, inbuf1, p[b].num, p[b].name, inbuf2);
+ }
+ else
+ {
+ prints(" \033[1;31m¢x\033[36m(\033[37m%s\033[36m)\033[33m%-10s \033[37m%-14s \033[31m¢x\033[0m\n",
+ p[a].num, p[a].name, inbuf1);
+ }
+ }
+ outs(" \033[1;31m¢x ¢x\033[m\n");
+ outs(" \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¢w¢w¢w¢w¢}\033[m");
+
+ while (1)
+ {
+ sprintf(inbuf1, COLOR1 " [¥Í©R¤O] %6d/%6d [¯h³Ò«×] %6d \033[m", d.hp, d.maxhp, d.tired);
+ out_cmd(inbuf1, COLOR1 " °Ñ¨£¿ï³æ " COLOR2 " [¦r¥À]¿ï¾Ü±ý«ô³Xªº¤Hª« [Q]Â÷¶}Á`¥q¥O³¡ \033[m");
+
+ choice = vkey();
+ if (choice == 'q' || choice == KEY_LEFT)
+ {
+ vmsg("Â÷¶}" BBSNAME "Á`¥q¥O³¡.....");
+ return 0;
+ }
+
+ /* ±N¦U¤Hª«¤w¸gµ¹»Pªº¼Æ­È¥ýÀx¦s°_¨Ó*/
+ save[1] = d.royalA; /* from¦u½Ã */
+ save[2] = d.royalB; /* fromªñ½Ã */
+ save[3] = d.royalC; /* from±N­x */
+ save[4] = d.royalD; /* from¤j¦Ú */
+ save[5] = d.royalE; /* from²½¥q */
+ save[6] = d.royalF; /* fromÃd¦m */
+ save[7] = d.royalG; /* from¤ý¦m */
+ save[8] = d.royalH; /* from°ê¤ý */
+ save[9] = d.royalI; /* from¤p¤¡ */
+ save[10] = d.royalJ; /* from¤ý¤l */
+
+ choice -= 'a' - 1;
+
+ if ((choice >= 1 && choice <= 10 && d.seeroyalJ == 1) || (choice >= 1 && choice <= 9 && d.seeroyalJ == 0))
+ {
+ d.social += rand() % 3 + 3;
+ d.hp -= rand() % 5 + 6;
+ d.tired += rand() % 5 + 8;
+
+ if ((p[choice].needmode == 0) || (p[choice].needmode == 1 && d.manners >= p[choice].needvalue) ||
+ (p[choice].needmode == 2 && d.speech >= p[choice].needvalue))
+ {
+ if (choice >= 1 && choice <= 9 && save[choice] >= p[choice].maxtoman)
+ {
+ vmsg(rand() % 2 ? "¯à©M³o»ò°¶¤jªº±zÁ¿¸Ü¯u¬Oºa©¯£«..." : "«Ü°ª¿³±z¨Ó«ô³X§Ú¡A¦ý§Ú¤£¯àµ¹±z¤°»ò¤F..");
+ }
+ else
+ {
+ if (choice >= 1 && choice <= 8) /* ®Ê¨£©x­û¡A¼W¥[«Ý¤H±µª« */
+ {
+ switch (choice)
+ {
+ case 1:
+ change = d.character / 5;
+ break;
+ case 2:
+ change = d.character / 8;
+ break;
+ case 3:
+ change = d.charm / 5;
+ break;
+ case 4:
+ change = d.wisdom / 10;
+ break;
+ case 5:
+ change = d.belief / 10;
+ break;
+ case 6:
+ change = d.speech / 10;
+ break;
+ case 7:
+ change = d.social / 10;
+ break;
+ case 8:
+ change = d.hexp / 10;
+ break;
+ }
+
+ if (change > p[choice].addtoman) /* ¦pªG¤j©ó¨C¦¸ªº¼W¥[³Ì¤j¶q */
+ change = p[choice].addtoman;
+ else if ((change + save[choice]) >= p[choice].maxtoman) /* ¦pªG¥[¤W­ì¥ýªº¤§«á¤j©ó©Ò¯àµ¹ªº©Ò¦³­È®É */
+ change = p[choice].maxtoman - save[choice];
+
+ save[choice] += change;
+ d.toman += change;
+ }
+ else if (choice == 9) /* §ä¤p¤¡ */
+ {
+ save[9] = 0;
+ d.social -= 13 + rand() % 4;
+ d.affect += 13 + rand() % 4;
+ }
+ else if (choice == 10 && d.seeroyalJ == 1) /* «ô³X¤ý¤l */
+ {
+ save[10] += 15 + rand() % 4;
+ d.seeroyalJ = 0;
+ }
+
+ vmsg(rand() % 2 ? p[choice].words1 : p[choice].words2);
+ }
+ }
+ else
+ {
+ vmsg(rand() % 2 ? "§Ú¤£©M³o¼ËªºÂû½Í¸Ü...." : "¨S±Ð¾iªºÂû¡A¦A¥h¾Ç¾Ç§»ö§a....");
+ }
+ }
+
+ d.royalA = save[1];
+ d.royalB = save[2];
+ d.royalC = save[3];
+ d.royalD = save[4];
+ d.royalE = save[5];
+ d.royalF = save[6];
+ d.royalG = save[7];
+ d.royalH = save[8];
+ d.royalI = save[9];
+ d.royalJ = save[10];
+ }
+}
+
+
+int
+pip_go_palace() /* °Ñ¨£ */
+{
+ pip_go_palace_screen(royallist);
+ return 0;
+}
+#endif /* HAVE_GAME */
+
diff --git a/pip/pip_stuff.c b/pip/pip_stuff.c
new file mode 100644
index 0000000..4ad3bbc
--- /dev/null
+++ b/pip/pip_stuff.c
@@ -0,0 +1,670 @@
+/*-------------------------------------------------------*/
+/* pip_stuff.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : ¤É¯Å¡B¨t²Î¡B¯S®í¿ï³æµ¥Âø¤CÂø¤K¨ç¦¡ */
+/* create : / / */
+/* update : 01/08/14 */
+/* author : dsyan.bbs@forever.twbbs.org */
+/* modify : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+#ifdef HAVE_GAME
+
+#include "pip.h"
+
+
+/*-------------------------------------------------------*/
+/* ¸ê®Æ¦s¨ú */
+/*-------------------------------------------------------*/
+
+
+void
+pip_write_file() /* ¹CÀ¸¼g¸ê®Æ¤JÀÉ®× */
+{
+ int fd;
+ char fpath[64];
+
+ usr_fpath(fpath, cuser.userid, fn_pip);
+ fd = open(fpath, O_WRONLY | O_CREAT, 0600); /* fpath ¤£¥²¤w¸g¦s¦b */
+ write(fd, &d, sizeof(CHICKEN));
+ close(fd);
+}
+
+
+int /* >=0:¦¨¥\ <0:¥¢±Ñ */
+pip_read_file(userid, p) /* ¹CÀ¸Åª¸ê®Æ¥XÀÉ®× */
+ char *userid;
+ struct CHICKEN *p;
+{
+ int fd;
+ char fpath[64];
+
+ usr_fpath(fpath, userid, fn_pip);
+ fd = open(fpath, O_RDONLY); /* fpath ¥²¶·¤w¸g¦s¦b */
+ if (fd >= 0)
+ {
+ read(fd, p, sizeof(CHICKEN));
+ close(fd);
+ }
+ return fd;
+}
+
+
+int /* 1: ¦¨¥\¼g¤J 0: ©ñ±ó */
+pip_write_backup() /* ¤pÂû¶i«×³Æ¥÷ */
+{
+ char *files[4] = {"¨S¦³", "¶i«×¤@", "¶i«×¤G", "¶i«×¤T"};
+ char buf[80], fpath[64];
+ int ch;
+
+ show_basic_pic(101);
+
+ ch = ians(b_lines - 2, 0, "Àx¦s [1] ¶i«×¤@ [2] ¶i«×¤G [3] ¶i«×¤T [Q] ©ñ±ó¡G[Q] ") - '0';
+
+ if (ch < 1 || ch > 3)
+ {
+ vmsg("©ñ±óÀx¦s¹CÀ¸³Æ¥÷");
+ return 0;
+ }
+
+ sprintf(buf, "½T©w­nÀx¦s©ó [%s] ÀÉ®×¶Ü(Y/N)¡H[N] ", files[ch]);
+ if (ians(b_lines - 2, 0, buf) != 'y')
+ {
+ vmsg("©ñ±óÀx¦sÀÉ®×");
+ return 0;
+ }
+
+ sprintf(buf, "Àx¦s [%s] ÀÉ®×§¹¦¨¤F", files[ch]);
+ vmsg(buf);
+
+ sprintf(buf, "%s.bak%d", fn_pip, ch);
+ usr_fpath(fpath, cuser.userid, buf);
+ ch = open(fpath, O_WRONLY | O_CREAT, 0600); /* fpath ¤£¥²¤w¸g¦s¦b */
+ write(ch, &d, sizeof(CHICKEN));
+ close(ch);
+
+ return 1;
+}
+
+
+int /* 1: ¦¨¥\Ū¥X 0: ©ñ±ó */
+pip_read_backup() /* ¤pÂû³Æ¥÷Ū¨ú */
+{
+ char *files[4] = {"¨S¦³", "¶i«×¤@", "¶i«×¤G", "¶i«×¤T"};
+ char buf[80], fpath[64];
+ int ch, fd;
+
+ show_basic_pic(102);
+
+ ch = ians(b_lines - 2, 0, "Ū¨ú [1] ¶i«×¤@ [2] ¶i«×¤G [3] ¶i«×¤T [Q] ©ñ±ó¡G[Q] ") - '0';
+
+ if (ch < 1 || ch > 3)
+ {
+ vmsg("©ñ±óŪ¨ú¹CÀ¸³Æ¥÷");
+ return 0;
+ }
+
+ sprintf(buf, "%s.bak%d", fn_pip, ch);
+ usr_fpath(fpath, cuser.userid, buf);
+
+ fd = open(fpath, O_RDONLY); /* fpath ¥²¶·¤w¸g¦s¦b */
+ if (fd >= 0)
+ {
+ sprintf(buf, "½T©w­nŪ¨ú©ó [%s] ÀÉ®×¶Ü(Y/N)¡H[N] ", files[ch]);
+ if (ians(b_lines - 2, 0, buf) == 'y')
+ {
+ read(fd, &d, sizeof(CHICKEN));
+ close(fd);
+ sprintf(buf, "Ū¨ú [%s] ÀÉ®×§¹¦¨¤F", files[ch]);
+ vmsg(buf);
+ return 1;
+ }
+ vmsg("©ñ±óŪ¨úÀÉ®×");
+ close(fd);
+ return 0;
+ }
+ else
+ {
+ sprintf(buf, "ÀÉ®× [%s] ¤£¦s¦b", files[ch]);
+ vmsg(buf);
+ return 0;
+ }
+}
+
+
+/*-------------------------------------------------------*/
+/* ¤pÂûª¬ºA¨ç¦¡ */
+/*-------------------------------------------------------*/
+
+
+void
+pipdie(msg, diemode) /* ¤pÂû¦º¤` */
+ char *msg;
+ int diemode;
+{
+ vs_head("¹q¤l¾i¤pÂû", str_site);
+
+ if (diemode == 1)
+ {
+ show_die_pic(1);
+ vmsg("¦º¯«¨Ó±a¨«¤pÂû¤F");
+ vs_head("¹q¤l¾i¤pÂû", str_site);
+ show_die_pic(2);
+ move(14, 20);
+ prints("¥i¼¦ªº¤pÂû\033[1;31m%s\033[m", msg);
+ vmsg(BBSNAME "«s±¥¤¤....");
+ }
+ else if (diemode == 2)
+ {
+ show_die_pic(3);
+ vmsg("¶ã¶ã¶ã..§Ú³Q¥á±ó¤F.....");
+ }
+ else if (diemode == 3)
+ {
+ show_die_pic(0);
+ vmsg("¹CÀ¸µ²§ôÅo..");
+ }
+
+ d.death = diemode;
+ pip_write_file();
+}
+
+
+int
+count_tired(prob, base, mode, mul, cal) /* itoc.010803: ¨Ì·Ó¶Ç¤Jªº¤Þ¼Æ¨Ó¼W´î¯h³Ò«× */
+ int prob; /* ¾÷²v */
+ int base; /* ©³¼Æ */
+ int mode; /* Ãþ«¬ 1:©M¦~ÄÖ¦³Ãö 0:©M¦~ÄÖµLÃö */
+ int mul; /* ¥[Åv (¥H % ¨Ó­p 100->1) */
+ int cal; /* 1:¥[¯h³Ò 0:´î¯h³Ò */
+{
+ int tiredvary; /* §ïÅÜ­È */
+
+ /* ¥ýºâ§ïÅܶq */
+ tiredvary = rand() % prob + base;
+
+ if (mode) /* ©M¦~ÄÖ¦³Ãö */
+ {
+ int tm; /* ¦~ÄÖ */
+ tm = d.bbtime / 60 / 30;
+
+ /* itoc.010803: ¦~¬ö¶V¤p¡A¥[¯h³Ò¤ñ¸û¤Ö¡A«ì´_¯h³Ò¤]¤ñ¸û§Ö */
+ /* ª`·N¤£¯à¼g¦¨ tiredvary *= 6 / 5; ³á :p */
+
+ if (tm <= 3) /* 0~3 ·³ */
+ {
+ tiredvary = cal ? tiredvary * 16 / 15 : tiredvary * 6 / 5;
+ }
+ else if (tm <= 7) /* 4~7 ·³ */
+ {
+ tiredvary = cal ? tiredvary * 11 / 10 : tiredvary * 8 / 7;
+ }
+ else if (tm <= 10) /* 8~10 ·³ */
+ {
+ tiredvary = cal ? tiredvary * 8 / 7 : tiredvary * 11 / 10;
+ }
+ else /* 11 ·³¥H¤W */
+ {
+ tiredvary = cal ? tiredvary * 6 / 5 : tiredvary * 16 / 15;
+ }
+ }
+
+ /* ¦Aºâ¥[Åv */
+ if (cal)
+ {
+ d.tired += tiredvary * mul / 100;
+ if (d.tired > 100)
+ d.tired = 100;
+ }
+ else
+ {
+ d.tired -= tiredvary; /* ¦©­È¤£¦A¥[Åv¤F */
+ if (d.tired < 0)
+ d.tired = 0;
+ }
+}
+
+
+/*-------------------------------------------------------*/
+/* ¯S®í¿ï³æ:¬Ý¯f ´îªÎ */
+/*-------------------------------------------------------*/
+
+
+int /* 1:¬Ý§¹Âå¥Í 0:¨S¯f¨ÓÂå°|´c·d */
+pip_see_doctor() /* ¬ÝÂå¥Í */
+{
+ char buf[256];
+ long savemoney;
+ savemoney = d.sick * 25;
+ if (d.sick <= 0)
+ {
+ vmsg("«z­ù..¨S¯f¨ÓÂå°|·F¹À..³Q½|¤F..¶ã~~");
+ d.character -= rand() % 3 + 1;
+ if (d.character < 0)
+ d.character = 0;
+ d.happy -= (rand() % 3 + 3);
+ d.satisfy -= rand() % 3 + 2;
+ }
+ else if (d.money < savemoney)
+ {
+ sprintf(buf, "±zªº¯f­nªá %d ¤¸³á....±z¤£°÷¿ú°Õ...", savemoney);
+ vmsg(buf);
+ }
+ else
+ {
+ d.tired -= rand() % 10 + 20;
+ if (d.tired < 0)
+ d.tired = 0;
+ d.sick = 0;
+ d.money = d.money - savemoney;
+ move(4, 0);
+ show_special_pic(1);
+ vmsg("ÃĨì¯f°£..¨S¦³°Æ§@¥Î!!");
+ return 1;
+ }
+ return 0;
+}
+
+
+int /* 1:¾ã®e 0:¨S¦³¾ã®e */
+pip_change_weight() /* ¼W­D/´îªÎ */
+{
+ char buf[80];
+ int weightmp;
+
+ show_special_pic(2);
+
+ out_cmd("", COLOR1 " ¬ü®e " COLOR2 " [1]¶Ç²Î¼W­D [2]§Ö³t¼W­D [3]¶Ç²Î´îªÎ [4]§Ö³t´îªÎ [Q]¸õ¥X \033[m");
+
+ switch (vkey())
+ {
+ case '1':
+ if (d.money < 80)
+ {
+ vmsg("¶Ç²Î¼W­D­n80¤¸³á....±z¤£°÷¿ú°Õ...");
+ }
+ else
+ {
+ if (ians(b_lines - 1, 0, " »Ýªá¶O 80 ¤¸¡]3¡ã5¤½¤ç¡^¡A½T©w¶Ü(Y/N)¡H[N] ") == 'Y')
+ {
+ weightmp = 3 + rand() % 3;
+ d.weight += weightmp;
+ d.money -= 80;
+ d.hp -= rand() % 2 + 3;
+ show_special_pic(3);
+ sprintf(buf, "Á`¦@¼W¥[¤F %d ¤½¤ç", weightmp);
+ vmsg(buf);
+ return 1;
+ }
+ else
+ {
+ vmsg("¦^¤ßÂà·NÅo.....");
+ }
+ }
+ break;
+
+ case '2':
+ vget(b_lines - 1, 0, " ¼W¤@¤½¤ç­n 30 ¤¸¡A±z­n¼W¦h¤Ö¤½¤ç©O¡H[½Ð¶ñ¼Æ¦r]¡G", buf, 4, DOECHO);
+ weightmp = atoi(buf);
+ if (weightmp <= 0)
+ {
+ vmsg("¿é¤J¦³»~..©ñ±óÅo...");
+ }
+ else if (d.money > (weightmp * 30))
+ {
+ sprintf(buf, " ¼W¥[ %d ¤½¤ç¡AÁ`¦@»Ýªá¶O¤F %d ¤¸¡A½T©w¶Ü(Y/N)¡H[N] ", weightmp, weightmp * 30);
+ if (ians(b_lines - 1, 0, buf) == 'y')
+ {
+ d.money -= weightmp * 30;
+ d.weight += weightmp;
+ count_tired(5, 8, 0, 100, 1);
+ d.hp -= (rand() % 2 + 3);
+ d.sick += rand() % 10 + 5;
+ show_special_pic(3);
+ sprintf(buf, "Á`¦@¼W¥[¤F %d ¤½¤ç", weightmp);
+ vmsg(buf);
+ return 1;
+ }
+ else
+ {
+ vmsg("¦^¤ßÂà·NÅo.....");
+ }
+ }
+ else
+ {
+ vmsg("±z¿ú¨S¨º»ò¦h°Õ.......");
+ }
+ break;
+
+ case '3':
+ if (d.money < 80)
+ {
+ vmsg("¶Ç²Î´îªÎ­n80¤¸³á....±z¤£°÷¿ú°Õ...");
+ }
+ else
+ {
+ if (ians(b_lines - 1, 0, "»Ýªá¶O 80¤¸(3¡ã5¤½¤ç)¡A½T©w¶Ü(Y/N)¡H[N] ") == 'y')
+ {
+ weightmp = 3 + rand() % 3;
+ d.weight -= weightmp;
+ if (d.weight <= 0)
+ d.weight = 1;
+ d.money -= 100;
+ d.hp -= rand() % 2 + 3;
+ show_special_pic(4);
+ sprintf(buf, "Á`¦@´î¤Ö¤F %d ¤½¤ç", weightmp);
+ vmsg(buf);
+ return 1;
+ }
+ else
+ {
+ vmsg("¦^¤ßÂà·NÅo.....");
+ }
+ }
+ break;
+
+ case '4':
+ vget(b_lines - 1, 0, " ´î¤@¤½¤ç­n 30 ¤¸¡A±z­n´î¦h¤Ö¤½¤ç©O¡H[½Ð¶ñ¼Æ¦r]¡G", buf, 4, DOECHO);
+ weightmp = atoi(buf);
+ if (weightmp <= 0)
+ {
+ vmsg("¿é¤J¦³»~..©ñ±óÅo...");
+ }
+ else if (d.weight <= weightmp)
+ {
+ vmsg("±z¨S¨º»ò­«³á.....");
+ }
+ else if (d.money > (weightmp * 30))
+ {
+ sprintf(buf, " ´î¤Ö %d ¤½¤ç¡AÁ`¦@»Ýªá¶O¤F %d ¤¸¡A½T©w¶Ü(Y/N)¡H[N] ", weightmp, weightmp * 30);
+ if (ians(b_lines - 1, 0, buf) == 'y')
+ {
+ d.money -= weightmp * 30;
+ d.weight -= weightmp;
+ count_tired(5, 8, 0, 100, 1);
+ d.hp -= (rand() % 2 + 3);
+ d.sick += rand() % 10 + 5;
+ show_special_pic(4);
+ sprintf(buf, "Á`¦@´î¤Ö¤F %d ¤½¤ç", weightmp);
+ vmsg(buf);
+ return 1;
+ }
+ else
+ {
+ vmsg("¦^¤ßÂà·NÅo.....");
+ }
+ }
+ else
+ {
+ vmsg("±z¿ú¨S¨º»ò¦h°Õ.......");
+ }
+ break;
+ }
+ return 0;
+}
+
+
+/*-------------------------------------------------------*/
+/* ¨t²Î¿ï³æ: ­Ó¤H¸ê®Æ «ô³X ¤pÂû©ñ¥Í ¯S§OªA°È */
+/*-------------------------------------------------------*/
+
+
+static int /* 0: ¨S¦³¾i¤pÂû 1: ¦³¾i¤pÂû */
+pip_data_list(userid) /* ¬Ý¬Y¤H¤pÂû¸Ô²Ó¸ê®Æ */
+ char *userid;
+{
+ char buf1[20], buf2[20], buf3[20], buf4[20];
+ int ch, page;
+ struct CHICKEN chicken;
+
+ if (!strcmp(cuser.userid, userid)) /* itoc.021031: ¦pªG¬d¸ß¦Û¤v¡A±q°O¾ÐÅé¥s²{¦b­È */
+ {
+ memcpy(&chicken, &d, sizeof(CHICKEN));
+ }
+ else if (pip_read_file(userid, &chicken) < 0)
+ {
+ vmsg("¥L¨S¦³¾i¤pÂû³á");
+ return 0;
+ }
+
+ page = 1;
+
+ do
+ {
+ clear();
+ move(1, 0);
+
+ /* itoc,010802: ¬°¤F¬Ý²M·¡¤@ÂI¡A©Ò¥H prints() ¸Ì­±ªº¤Þ¼Æ´N¤£Â_¦æ¼g¦b¸Ó¦C³Ì«á */
+
+ if (page == 1)
+ {
+ outs("\033[1;31m ¢~¢t\033[41;37m °ò¥»¸ê®Æ \033[0;1;31m¢u¢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;31m ¢x\033[33m¡Ì©m ¦W : \033[37m%-10s\033[33m¡Ì¥Í ¤é : \033[37m%-10s\033[33m¡Ì©Ê §O : \033[37m%-10s\033[31m ¢x\033[m\n", chicken.name, chicken.birth, chicken.sex == 1 ? "¡ñ" : "¡ð");
+ prints("\033[1;31m ¢x\033[33m¡Ìª¬ ºA : \033[37m%-10s\033[33m¡Ì´_¬¡¦¸¼Æ : \033[37m%-10d\033[33m¡Ì¦~ ÄÖ : \033[37m%-10d\033[31m ¢x\033[m\n", chicken.death == 1 ? "¦º¤`" : chicken.death == 2 ? "©ß±ó" : chicken.death == 3 ? "µ²§ô" : "¥¿±`", chicken.liveagain, chicken.bbtime / 60 / 30);
+
+ outs("\033[1;31m ¢u¢t\033[41;37m ª¬ºA«ü¼Æ \033[0;1;31m¢u¢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\033[m\n");
+ prints("\033[1;31m ¢x\033[33m¡Ì¿Ë¤lÃö«Y : \033[37m%-10d\033[33m¡Ì§Ö ¼Ö «× : \033[37m%-10d\033[33m¡Ìº¡ ·N «× : \033[37m%-10d\033[31m ¢x\033[m\n", chicken.relation, chicken.happy, chicken.satisfy);
+ prints("\033[1;31m ¢x\033[33m¡ÌÅÊ·R«ü¼Æ : \033[37m%-10d\033[33m¡Ì«H ¥õ : \033[37m%-10d\033[33m¡Ì¸o Ä^ : \033[37m%-10d\033[31m ¢x\033[m\n", chicken.fallinlove, chicken.belief, chicken.sin);
+ prints("\033[1;31m ¢x\033[33m¡Ì·P ¨ü : \033[37m%-10d\033[33m¡Ì : \033[37m%-10s\033[33m¡Ì : \033[37m%-10s\033[31m ¢x\033[m\n", chicken.affect, "", "");
+
+ outs("\033[1;31m ¢u¢t\033[41;37m °·±d«ü¼Æ \033[0;1;31m¢u¢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\033[m\n");
+ prints("\033[1;31m ¢x\033[33m¡ÌÅé ­« : \033[37m%-10d\033[33m¡Ì¯h ³Ò «× : \033[37m%-10d\033[33m¡Ì¯f ®ð : \033[37m%-10d\033[31m ¢x\033[m\n", chicken.weight, chicken.tired, chicken.sick);
+ prints("\033[1;31m ¢x\033[33m¡Ì²M ¼ä «× : \033[37m%-10d\033[33m¡Ì : \033[37m%-10s\033[33m¡Ì : \033[37m%-10s\033[31m ¢x\033[m\n", chicken.shit, "", "");
+
+ outs("\033[1;31m ¢u¢t\033[41;37m ¹CÀ¸­I´º \033[0;1;31m¢u¢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\033[m\n");
+ prints("\033[1;31m ¢x\033[33m¡Ì²q ®± Ĺ : \033[37m%-10d\033[33m¡Ì²q ®± ¿é : \033[37m%-10d\033[33m¡Ì²q®±¥­¤â : \033[37m%-10d\033[31m ¢x\033[m\n", chicken.winn, chicken.losee, chicken.tiee);
+
+ outs("\033[1;31m ¢u¢t\033[41;37m µû»ù°Ñ¼Æ \033[0;1;31m¢u¢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\033[m\n");
+ prints("\033[1;31m ¢x\033[33m¡ÌªÀ¥æµû»ù : \033[37m%-10d\033[33m¡Ì®a¨Æµû»ù : \033[37m%-10d\033[33m¡Ì¾Ô°«µû»ù : \033[37m%-10d\033[31m ¢x\033[m\n", chicken.social, chicken.family, chicken.hexp);
+ prints("\033[1;31m ¢x\033[33m¡ÌÅ]ªkµû»ù : \033[37m%-10d\033[33m¡Ì : \033[37m%-10s\033[33m¡Ì : \033[37m%-10s\033[31m ¢x\033[m\n", chicken.hexp, "", "");
+
+ outs("\033[1;31m ¢u¢t\033[41;37m µû»ù°Ñ¼Æ \033[0;1;31m¢u¢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\033[m\n");
+ prints("\033[1;31m ¢x\033[33m¡ÌªÀ¥æµû»ù : \033[37m%-10d\033[33m¡Ì®a¨Æµû»ù : \033[37m%-10d\033[33m¡Ì¾Ô°«µû»ù : \033[37m%-10d\033[31m ¢x\033[m\n", chicken.social, chicken.family, chicken.hexp);
+ prints("\033[1;31m ¢x\033[33m¡ÌÅ]ªkµû»ù : \033[37m%-10d\033[33m¡Ì : \033[37m%-10s\033[33m¡Ì : \033[37m%-10s\033[31m ¢x\033[m\n", chicken.hexp, "", "");
+
+ outs("\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¢w¢w¢w¢w¢w¢w¢£\033[m\n");
+ move(b_lines - 2, 0);
+ outs(" \033[1;36m²Ä¤@­¶\033[37m/\033[36m¦@¤T­¶\033[m\n");
+ }
+ else if (page == 2)
+ {
+ outs("\033[1;31m ¢~¢t\033[41;37m ¯à¤O°Ñ¼Æ \033[0;1;31m¢u¢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;31m ¢x\033[33m¡Ì«Ý¤H±µª« : \033[37m%-10d\033[33m¡Ì®ð ½è «× : \033[37m%-10d\033[33m¡Ì·R ¤ß : \033[37m%-10d\033[31m ¢x\033[m\n", chicken.toman, chicken.character, chicken.love);
+ prints("\033[1;31m ¢x\033[33m¡Ì´¼ ¤O : \033[37m%-10d\033[33m¡ÌÃÀ³N¯à¤O : \033[37m%-10d\033[33m¡Ì¹D ¼w : \033[37m%-10d\033[31m ¢x\033[m\n", chicken.wisdom, chicken.art, chicken.etchics);
+ prints("\033[1;31m ¢x\033[33m¡Ì«i ´± : \033[37m%-10d\033[33m¡Ì±½¦a¬~¦ç : \033[37m%-10d\033[33m¡Ì¾y ¤O : \033[37m%-10d\033[31m ¢x\033[m\n", chicken.brave, chicken.homework, chicken.charm);
+ prints("\033[1;31m ¢x\033[33m¡Ì§ »ö : \033[37m%-10d\033[33m¡Ì½Í ¦R : \033[37m%-10d\033[33m¡Ì²i ¶¹ : \033[37m%-10d\033[31m ¢x\033[m\n", chicken.manners, chicken.speech, chicken.cook);
+ prints("\033[1;31m ¢x\033[33m¡Ì§ð À» ¤O : \033[37m%-10d\033[33m¡Ì¨¾ ¿m ¤O : \033[37m%-10d\033[33m¡Ì³t «× : \033[37m%-10d\033[31m ¢x\033[m\n", chicken.attack, chicken.resist, chicken.speed);
+ prints("\033[1;31m ¢x\033[33m¡Ì¾Ô°«§Þ³N : \033[37m%-10d\033[33m¡ÌÅ]ªk§Þ³N : \033[37m%-10d\033[33m¡Ì§ÜÅ]¯à¤O : \033[37m%-10d\033[31m ¢x\033[m\n", chicken.hskill, chicken.mskill, chicken.immune);
+
+ sprintf(buf1, "%d%s/%d%s", chicken.hp > 1000 ? chicken.hp / 1000 : chicken.hp, chicken.hp > 1000 ? "K" : "", /* HP */
+ chicken.maxhp > 1000 ? chicken.maxhp / 1000 : chicken.maxhp, chicken.maxhp > 1000 ? "K" : "");
+ sprintf(buf2, "%d%s/%d%s", chicken.mp > 1000 ? chicken.mp / 1000 : chicken.mp, chicken.mp > 1000 ? "K" : "", /* MP */
+ chicken.maxmp > 1000 ? chicken.maxmp / 1000 : chicken.maxmp, chicken.maxmp > 1000 ? "K" : "");
+ sprintf(buf3, "%d%s/%d%s", chicken.vp > 1000 ? chicken.vp / 1000 : chicken.vp, chicken.vp > 1000 ? "K" : "", /* VP */
+ chicken.maxvp > 1000 ? chicken.maxvp / 1000 : chicken.maxvp, chicken.maxvp > 1000 ? "K" : "");
+ sprintf(buf4, "%d%s/%d%s", chicken.sp > 1000 ? chicken.sp / 1000 : chicken.sp, chicken.sp > 1000 ? "K" : "", /* SP */
+ chicken.maxsp > 1000 ? chicken.maxsp / 1000 : chicken.maxsp, chicken.maxsp > 1000 ? "K" : "");
+
+ outs("\033[1;31m ¢u¢t\033[41;37m ¾Ô°««ü¼Ð \033[0;1;31m¢u¢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\033[m\n");
+ prints("\033[1;31m ¢x\033[33m¡Ìµ¥ ¯Å : \033[37m%-10d\033[33m¡Ì¸g Åç ­È : \033[37m%-10d\033[33m¡Ì ¦å : \033[37m%-10s\033[31m ¢x\033[m\n", chicken.level, chicken.exp, buf1);
+ prints("\033[1;31m ¢x\033[33m¡Ìªk ¤O : \033[37m%-10s\033[33m¡Ì²¾ °Ê ¤O : \033[37m%-10s\033[33m¡Ì¤º ¤O : \033[37m%-10s\033[31m ¢x\033[m\n", buf2, buf3, buf4);
+
+ outs("\033[1;31m ¢u¢t\033[41;37m ­¹ª«®w¦s \033[0;1;31m¢u¢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\033[m\n");
+ prints("\033[1;31m ¢x\033[33m¡Ì­¹ ª« : \033[37m%-10d\033[33m¡Ì¹s ­¹ : \033[37m%-10d\033[33m¡Ì : \033[37m%-10s\033[31m ¢x\033[m\n", chicken.food, chicken.cookie, "");
+ prints("\033[1;31m ¢x\033[33m¡Ì¤j ÁÙ ¤¦ : \033[37m%-10d\033[33m¡ÌÆF ªÛ : \033[37m%-10d\033[33m¡Ì¤j ¸É ¤Y : \033[37m%-10d\033[31m ¢x\033[m\n", chicken.pill, chicken.medicine, chicken.burger);
+ prints("\033[1;31m ¢x\033[33m¡Ì¤H çx : \033[37m%-10d\033[33m¡ÌÂ_ Äò »I : \033[37m%-10d\033[33m¡Ì³· ½¬ : \033[37m%-10d\033[31m ¢x\033[m\n", chicken.ginseng, chicken.paste, chicken.snowgrass);
+
+ outs("\033[1;31m ¢u¢t\033[41;37m ª««~®w¦s \033[0;1;31m¢u¢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\033[m\n");
+ prints("\033[1;31m ¢x\033[33m¡Ìª÷ ¿ú : \033[37m%-10d\033[33m¡Ì : \033[37m%-10s\033[33m¡Ì : \033[37m%-10s\033[31m ¢x\033[m\n", chicken.money, "", "");
+ prints("\033[1;31m ¢x\033[33m¡Ì®Ñ ¥» : \033[37m%-10d\033[33m¡Ìª± ¨ã : \033[37m%-10d\033[33m¡Ì½Ò¥~Ūª« : \033[37m%-10d\033[31m ¢x\033[m\n", chicken.book, chicken.toy, chicken.playboy);
+
+
+ outs("\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¢w¢w¢w¢w¢w¢w¢£\033[m\n");
+ move(b_lines - 2, 0);
+ outs(" \033[1;36m²Ä¤G­¶\033[37m/\033[36m¦@¤T­¶\033[m\n");
+ }
+ else /* if (page == 3) */
+ {
+ outs("\033[1;31m ¢~¢t\033[41;37m °Ñ¨£¤ý¦Ú \033[0;1;31m¢u¢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;31m ¢x\033[33m¡Ì¦u½Ã¦n·P : \033[37m%-10d\033[33m¡Ìªñ½Ã¦n·P : \033[37m%-10d\033[33m¡Ì±N­x¦n·P : \033[37m%-10d\033[31m ¢x\033[m\n", chicken.royalA, chicken.royalB, chicken.royalC);
+ prints("\033[1;31m ¢x\033[33m¡Ì¤j¦Ú¦n·P : \033[37m%-10d\033[33m¡Ì²½¥q¦n·P : \033[37m%-10d\033[33m¡ÌÃd¦m¦n·P : \033[37m%-10d\033[31m ¢x\033[m\n", chicken.royalD, chicken.royalE, chicken.royalF);
+ prints("\033[1;31m ¢x\033[33m¡Ì¤ý¦m¦n·P : \033[37m%-10d\033[33m¡Ì°ê¤ý¦n·P : \033[37m%-10d\033[33m¡Ì¤p¤¡¦n·P : \033[37m%-10d\033[31m ¢x\033[m\n", chicken.royalG, chicken.royalH, chicken.royalI);
+
+ outs("\033[1;31m ¢u¢t\033[41;37m ¤u§@¦¸¼Æ \033[0;1;31m¢u¢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\033[m\n");
+ prints("\033[1;31m ¢x\033[33m¡Ì®a¨Æ¦¸¼Æ : \033[37m%-10d\033[33m¡Ì«O©i¦¸¼Æ : \033[37m%-10d\033[33m¡Ì®È©±¦¸¼Æ : \033[37m%-10d\033[31m ¢x\033[m\n", chicken.workA, chicken.workB, chicken.workC);
+ prints("\033[1;31m ¢x\033[33m¡Ì¹A³õ¦¸¼Æ : \033[37m%-10d\033[33m¡ÌÀ\\ÆU¦¸¼Æ : \033[37m%-10d\033[33m¡Ì±Ð°ó¦¸¼Æ : \033[37m%-10d\033[31m ¢x\033[m\n", chicken.workD, chicken.workE, chicken.workF);
+ prints("\033[1;31m ¢x\033[33m¡Ì¦aÅu¦¸¼Æ : \033[37m%-10d\033[33m¡Ì¥ï¤ì¦¸¼Æ : \033[37m%-10d\033[33m¡Ì¬ü¾v¦¸¼Æ : \033[37m%-10d\033[31m ¢x\033[m\n", chicken.workG, chicken.workH, chicken.workI);
+ prints("\033[1;31m ¢x\033[33m¡ÌÂy¤H¦¸¼Æ : \033[37m%-10d\033[33m¡Ì¤u¦a¦¸¼Æ : \033[37m%-10d\033[33m¡Ì¦u¹Ó¦¸¼Æ : \033[37m%-10d\033[31m ¢x\033[m\n", chicken.workJ, chicken.workK, chicken.workL);
+ prints("\033[1;31m ¢x\033[33m¡Ì®a±Ð¦¸¼Æ : \033[37m%-10d\033[33m¡Ì°s®a¦¸¼Æ : \033[37m%-10d\033[33m¡Ì°s©±¦¸¼Æ : \033[37m%-10d\033[31m ¢x\033[m\n", chicken.workM, chicken.workN, chicken.workO);
+ prints("\033[1;31m ¢x\033[33m¡Ì©] Á` ·| : \033[37m%-10d\033[33m¡Ì : \033[37m%-10s\033[33m¡Ì : \033[37m%-10s\033[31m ¢x\033[m\n", chicken.workP, "", "");
+
+ outs("\033[1;31m ¢u¢t\033[41;37m ¤W½Ò¦¸¼Æ \033[0;1;31m¢u¢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\033[m\n");
+ prints("\033[1;31m ¢x\033[33m¡Ì¦ÛµM¬ì¾Ç : \033[37m%-10d\033[33m¡Ì­ð¸Ö§ºµü : \033[37m%-10d\033[33m¡Ì¯«¾Ç±Ð¨| : \033[37m%-10d\033[31m ¢x\033[m\n", chicken.classA, chicken.classB, chicken.classC);
+ prints("\033[1;31m ¢x\033[33m¡Ì­x¾Ç±Ð¨| : \033[37m%-10d\033[33m¡Ì¼C¹D§Þ³N : \033[37m%-10d\033[33m¡Ì®æ°«¾Ô§Þ : \033[37m%-10d\033[31m ¢x\033[m\n", chicken.classD, chicken.classE, chicken.classF);
+ prints("\033[1;31m ¢x\033[33m¡ÌÅ]ªk±Ð¨| : \033[37m%-10d\033[33m¡Ì§»ö±Ð¨| : \033[37m%-10d\033[33m¡Ìøµe§Þ¥© : \033[37m%-10d\033[31m ¢x\033[m\n", chicken.classG, chicken.classH, chicken.classI);
+ prints("\033[1;31m ¢x\033[33m¡Ì»RÁЧޥ© : \033[37m%-10d\033[33m¡Ì : \033[37m%-10s\033[33m¡Ì : \033[37m%-10s\033[31m ¢x\033[m\n", chicken.classJ, "", "");
+
+ outs("\033[1;31m ¢u¢t\033[41;37m ¸Ë³Æ¦Cªí \033[0;1;31m¢u¢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\033[m\n");
+ prints("\033[1;31m ¢x\033[33m¡ÌÀY³¡¸Ë³Æ : \033[37m%-10s\033[33m¡Ì¤â³¡¸Ë³Æ : \033[37m%-10s\033[33m¡Ì¬ÞµP¸Ë³Æ : \033[37m%-10s\033[31m ¢x\033[m\n", chicken.equiphead, chicken.equiphand, chicken.equipshield);
+ prints("\033[1;31m ¢x\033[33m¡Ì¨­Åé¸Ë³Æ : \033[37m%-10s\033[33m¡Ì¸}³¡¸Ë³Æ : \033[37m%-10s\033[33m¡Ì \033[37m%-10s\033[31m ¢x\033[m\n", chicken.equipbody, chicken.equipfoot, "");
+
+ outs("\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¢w¢w¢w¢w¢w¢w¢£\033[m\n");
+ move(b_lines - 2, 0);
+ outs(" \033[1;36m²Ä¤T­¶\033[37m/\033[36m¦@¤T­¶\033[m\n");
+ }
+
+ out_cmd("", COLOR1 " ¬d¸ß " COLOR2 " [¡ô/PgUP]©¹¤W¤@­¶ [¡õ/PgDN]©¹¤U¤@­¶ [Q]Â÷¶} \033[m");
+
+ switch (ch = vkey())
+ {
+ case KEY_UP:
+ case KEY_PGUP:
+ if (page > 1)
+ page--;
+ break;
+
+ default:
+ if (page < 3)
+ page++;
+ break;
+ }
+ } while (ch != 'q' && ch != KEY_LEFT);
+
+ return 1;
+}
+
+
+int
+pip_query_self() /* ¬d¸ß¦Û¤v */
+{
+ pip_data_list(cuser.userid);
+ return 0;
+}
+
+
+int /* 1:«ô³X¦¨¥\ 0:¨S³o°¦Âû */
+pip_query() /* «ô³X¤pÂû */
+{
+ int uno;
+ char uid[IDLEN + 1];
+
+ vs_bar("«ô³X¦P¦ñ");
+ if (vget(1, 0, msg_uid, uid, IDLEN + 1, GET_USER))
+ {
+ move(2, 0);
+ if (uno = acct_userno(uid))
+ {
+ pip_data_list(uid);
+ return 1;
+ }
+ else
+ {
+ outs(err_uid);
+ clrtoeol();
+ }
+ }
+ return 0;
+}
+
+
+int /* 1:©ñ¥Í 0:Äò¾i */
+pip_system_freepip()
+{
+ char buf[80];
+
+ if (ians(b_lines - 2, 0, "¯uªº­n©ñ¥Í¶Ü(Y/N)¡H[N] ") == 'y')
+ {
+ sprintf(buf, "\033[1;31m%s ³Q¬½¤ßªº %s ¥á±¼¤F~\033[m", d.name, cuser.userid);
+ pipdie(buf, 2);
+ return 1;
+ }
+ return 0;
+}
+
+
+int
+pip_system_service()
+{
+ int choice;
+ char buf[128];
+
+ out_cmd("", COLOR1 " ªA°È " COLOR2 " [1]©R¦W¤j®v [2]Åܩʤâ³N [3]µ²§½³]§½ [Q]Â÷¶} \033[m");
+
+ switch (vkey())
+ {
+ case '1':
+ vget(b_lines - 2, 0, "À°¤pÂû­«·s¨ú­Ó¦n¦W¦r¡G", buf, 11, DOECHO);
+ if (!buf[0])
+ {
+ vmsg("µ¥¤@¤U·Q¦n¦A¨Ó¦n¤F :)");
+ }
+ else
+ {
+ strcpy(d.name, buf);
+ vmsg("¶â¶â ´«¤@­Ó·sªº¦W¦r³á...");
+ }
+ break;
+
+ case '2': /* ÅÜ©Ê */
+ if (d.sex == 1) /* 1:¤½ 2:¥À */
+ {
+ choice = 2; /* ¤½-->¥À */
+ sprintf(buf, "±N¤pÂû¥Ñ¡ñÅܩʦ¨¡ðªº¶Ü(Y/N)¡H[N] ");
+ }
+ else
+ {
+ choice = 1; /* ¥À-->¤½ */
+ sprintf(buf, "±N¤pÂû¥Ñ¡ðÅܩʦ¨¡ñªº¶Ü(Y/N)¡H[N] ");
+ }
+ if (ians(b_lines - 2, 0, buf) == 'y')
+ {
+ d.sex = choice;
+ vmsg("Åܩʤâ³N§¹²¦...");
+ }
+ break;
+
+ case '3':
+ /* 1:¤£­n¥B¥¼±B 4:­n¥B¥¼±B */
+ if (d.wantend == 1 || d.wantend == 2 || d.wantend == 3)
+ {
+ choice = 3; /* ¨S¦³-->¦³ */
+ sprintf(buf, "±N¤pÂû¹CÀ¸§ï¦¨¡i¦³20·³µ²§½¡j(Y/N)¡H[N] ");
+ }
+ else
+ {
+ choice = -3; /* ¦³-->¨S¦³ */
+ sprintf(buf, "±N¤pÂû¹CÀ¸§ï¦¨¡i¨S¦³20·³µ²§½¡j(Y/N)¡H[N] ");
+ }
+ if (ians(b_lines - 2, 0, buf) == 'y')
+ {
+ d.wantend += choice;
+ vmsg("¹CÀ¸µ²§½³]©w§¹²¦...");
+ }
+ break;
+ }
+ return 0;
+}
+#endif /* HAVE_GAME */
diff --git a/pip/pip_visio.c b/pip/pip_visio.c
new file mode 100644
index 0000000..b063775
--- /dev/null
+++ b/pip/pip_visio.c
@@ -0,0 +1,299 @@
+/*-------------------------------------------------------*/
+/* pipvisio.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : ¾i¤pÂû¹CÀ¸ */
+/* create : / / */
+/* update : 01/08/03 */
+/* author : dsyan.bbs@forever.twbbs.org */
+/* modify : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/* picture: tiball.bbs@bbs.nhctc.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+#ifdef HAVE_GAME
+
+#include "pip.h"
+
+
+#if 0 /* itoc.010803.µù¸Ñ:¿Ã¹õ°t¸m */
+
+³Ì«e­± ¤@¨Ç¸ê®ÆÅã¥Ü
+
+¤¤¶¡ ¹Ï
+
+b_lines - 2 ¸ß°Ý¦C
+b_lines - 1 «ü¥O¦C
+b_lines «ü¥O¦C
+
+#endif
+
+
+
+/*-------------------------------------------------------*/
+/* «ü¥O¨ú±o */
+/*-------------------------------------------------------*/
+
+
+int /* itoc.010802: ¨ú¥N vans("Y/N¡H[Y] ") ³oÃþ°ÝÃD¥Îªº¨ç¦¡ */
+ians(x, y, msg)
+ int x, y; /* itoc.010803: ¤@¯ë®³ (b_lines - 2, 0) ¨Ó·í¸ß°Ý¦C */
+ char *msg;
+{
+ move(x, 0); /* ²M±¼¾ã¦C */
+ clrtoeol();
+ move(x, y);
+ outs(msg);
+ outs("\033[47m \033[m");
+ move(x, y + strlen(msg)); /* ²¾¼Ð²¾°Ê®Ø®Ø¤º */
+ return vkey(); /* ¥u«öÁä¡A¤£»Ý­n«ö ENTER */
+
+#if 0
+ if (ch >= 'A' && ch <= 'Z')
+ ch |= 0x20; /* ´«¤p¼g */
+#endif
+}
+
+
+/*-------------------------------------------------------*/
+/* ¿Ã¹õ±±¨î */
+/*-------------------------------------------------------*/
+
+
+void
+clrfromto(from, to) /* ²M°£ from~to ¦C¡A³Ì«á´å¼Ð°±¯d¦b (from, 0) */
+ int from, to;
+{
+ while (to >= from)
+ {
+ move(to, 0);
+ clrtoeol();
+ to--;
+ }
+}
+
+
+static int /* 1: Àɮצb 0: Àɮפ£¦b */
+show_file(fpath, ln, lines) /* ±q²Ä ln ¦C¶}©l¦LÀÉ®× lines ¦C */
+ char *fpath;
+ int ln, lines;
+{
+ FILE *fp;
+ char buf[ANSILINELEN];
+ int i;
+
+ if ((fp = fopen(fpath, "r")))
+ {
+ clrfromto(ln, ln + lines);
+ i = lines;
+ while (fgets(buf, ANSILINELEN, fp) && i--)
+ outs(buf);
+ fclose(fp);
+ return 1;
+ }
+
+ /* itoc.010802: ¦^³øÀɮ׿ò¥¢ */
+ sprintf(buf, "%s ¿ò¥¢¡A½Ð§iª¾¯¸ªø", fpath);
+ zmsg(buf);
+ return 0;
+}
+
+
+/*-------------------------------------------------------*/
+/* ¤pÂû¹Ï§Î°Ï */
+/*-------------------------------------------------------*/
+
+
+/* itoc.010801: ½Ð¨Ì·Ó¦U show_xxx_pic() ¤¤ show_file(buf, ln, lines) ªº lines ¨Ó±±¨î¹Ï®×¦C¼Æ */
+/* itoc.010801: ¥¿±`ª¬ªp¤U³£¬O 10 ¦C (­è¦n¤]¬O°ÊºA¬ÝªOªº¦C¼Æ)¡C­Y ln=0 ¥B lines=b_lines+1 ªí¥Ü¾ã­Ó¿Ã¹õ¥þ²M */
+
+
+/* -------------------- */
+/* ¤@¯ëªº¹Ï¡A¦b 7~16 ¦C */
+/* -------------------- */
+
+int
+show_basic_pic(i)
+ int i;
+{
+ char buf[64];
+ sprintf(buf, PIP_PICHOME "basic/pic%d", i);
+ return show_file(buf, 7, 10);
+}
+
+
+int
+show_feed_pic(i) /* ¦YªF¦è */
+ int i;
+{
+ char buf[64];
+ sprintf(buf, PIP_PICHOME "feed/pic%d", i);
+ return show_file(buf, 7, 10);
+}
+
+
+int
+show_usual_pic(i) /* ¥­±`ª¬ºA */
+ int i;
+{
+ char buf[64];
+ sprintf(buf, PIP_PICHOME "usual/pic%d", i);
+ return show_file(buf, 7, 10);
+}
+
+
+int
+show_special_pic(i) /* ¯S®í¿ï³æ */
+ int i;
+{
+ char buf[64];
+ sprintf(buf, PIP_PICHOME "special/pic%d", i);
+ return show_file(buf, 7, 10);
+}
+
+int
+show_practice_pic(i) /* ­×¦æ¥Îªº¹Ï */
+ int i;
+{
+ char buf[64];
+ sprintf(buf, PIP_PICHOME "practice/pic%d", i);
+ return show_file(buf, 7, 10);
+}
+
+
+int
+show_job_pic(i) /* ¥´¤uªºshow¹Ï */
+ int i;
+{
+ char buf[64];
+ sprintf(buf, PIP_PICHOME "job/pic%d", i);
+ return show_file(buf, 7, 10);
+}
+
+
+int
+show_play_pic(i) /* ¥ð¶¢ªº¹Ï */
+ int i;
+{
+ char buf[64];
+ sprintf(buf, PIP_PICHOME "play/pic%d", i);
+ return show_file(buf, 7, 10);
+}
+
+
+int
+show_guess_pic(i) /* ²q®±¥Î */
+ int i;
+{
+ char buf[64];
+ sprintf(buf, PIP_PICHOME "guess/pic%d", i);
+ return show_file(buf, 7, 10);
+}
+
+
+int
+show_badman_pic(i) /* ©Çª« */
+ int i;
+{
+ char buf[64];
+
+/* itoc.010731: picabc abc ¬O½s¸¹
+ ¦Ê¦ì¼Æ a ¬O¤ÀÃþ¡A¤Q¦ì¼Æ­Ó¦ì¼Æ bc ¬O¸ÓÃþ¹Ïªº½s¸¹ */
+
+ sprintf(buf, PIP_PICHOME "badman/pic%03d", i);
+ return show_file(buf, 7, 10);
+}
+
+
+int
+show_fight_pic(i) /* ¥´¬[ */
+ int i;
+{
+ char buf[64];
+ sprintf(buf, PIP_PICHOME "fight/pic%d", i);
+ return show_file(buf, 7, 10);
+}
+
+
+int
+show_resultshow_pic(i) /* ¦¬Ã¬©u */
+ int i;
+{
+ char buf[64];
+ sprintf(buf, PIP_PICHOME "resultshow/pic%d", i);
+ return show_file(buf, 7, 10);
+}
+
+
+int
+show_quest_pic(i) /* ¥ô°È */
+ int i;
+{
+ char buf[64];
+ sprintf(buf, PIP_PICHOME "quest/pic%d", i);
+ return show_file(buf, 7, 10);
+}
+
+
+/* -------------------------- */
+/* ¤@¨Ç¯S®íªº¹Ï¡A¨q¦b 1~10 ¦C */
+/* -------------------------- */
+
+int
+show_weapon_pic(i) /* ªZ¾¹¥Î */
+ int i;
+{
+ char buf[64];
+ sprintf(buf, PIP_PICHOME "weapon/pic%d", i);
+ return show_file(buf, 1, 10);
+}
+
+
+int
+show_palace_pic(i) /* °Ñ¨£¤ý¦Ú¥Î */
+ int i;
+{
+ char buf[64];
+ sprintf(buf, PIP_PICHOME "palace/pic%d", i);
+ return show_file(buf, 1, 10);
+}
+
+
+/* ------------------------------------- */
+/* ¤@¨Ç¯S®íªº¹Ï¡A¨q¦b 4~19(b_lines-4) ¦C */
+/* ------------------------------------- */
+
+int
+show_system_pic(i) /* ¨t²Î */
+ int i;
+{
+ char buf[64];
+ sprintf(buf, PIP_PICHOME "system/pic%d", i);
+ return show_file(buf, 4, 16);
+}
+
+
+int
+show_ending_pic(i) /* µ²§ô */
+ int i;
+{
+ char buf[64];
+ sprintf(buf, PIP_PICHOME "ending/pic%d", i);
+ return show_file(buf, 4, 16);
+}
+
+
+/* -------------------------- */
+/* ¤@¨Ç¯S®íªº¹Ï¡A¨q¦b¾ã­Ó¿Ã¹õ */
+/* -------------------------- */
+
+int
+show_die_pic(i) /* ¦º¤` */
+ int i;
+{
+ char buf[64];
+ sprintf(buf, PIP_PICHOME "die/pic%d", i);
+ return show_file(buf, 0, b_lines + 1);
+}
+#endif /* HAVE_GAME */
diff --git a/pip/pip_weapon.c b/pip/pip_weapon.c
new file mode 100644
index 0000000..532abe4
--- /dev/null
+++ b/pip/pip_weapon.c
@@ -0,0 +1,241 @@
+/* ----------------------------------------------------- */
+/* pip_weapon.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/* ----------------------------------------------------- */
+/* target : ¤pÂû weapon structure */
+/* create : / / */
+/* update : 01/08/15 */
+/* author : dsyan.bbs@forever.twbbs.org */
+/* modify : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/* ----------------------------------------------------- */
+
+
+#include "bbs.h"
+
+#ifdef HAVE_GAME
+
+#include "pip.h"
+
+
+/* ------------------------------------------------------- */
+/* ªZ¾¹ÁʶR¨ç¦¡ */
+/* ------------------------------------------------------- */
+
+
+/* name[11] quality cost */
+static weapon p[9]; /* °O¿ýªZ¾¹ */
+
+
+/* itoc.021031: ¬°¤F¼W¥[¹CÀ¸ªº¦h¼Ë©Ê¡A¼g¤@¤äªZ¾¹²£¥Í¾¹ */
+static void
+weapon_generate(type)
+ int type; /* ­þ¤@³¡¤À¸Ë³Æ */
+{
+ int i, num;
+
+ char adje[14][5] = {"·lÃa", "¦A¥Í", "¤G¤â", "µ´ª©", "¶ì½¦", "¤û¥Ö", "¿ûÅK", "¶Àª÷", "¯S¯Å", "±OÀs", "§Ñ±¡", "±OÀs", "­¸¤Ñ", "¶Ç©_"};
+ char prep[13][3] = {"¯}", "Äê", "³¾", "¤§", "¨g", "¯P", "¬¯", "¸t", "Å]", "Ä_", "¥ú", "¯«", ""};
+ char noun[5][9][5] =
+ {
+ /* ÀY³¡ªZ¾¹ */ "´U", "ÀY²¯", "ÀY¸n", "ÀY¤y", "ÀY¹¢", "¦Õ¾÷", "²´Ãè", "¾vãT", "¶µÁå",
+ /* ¤â³¡ªZ¾¹ */ "¼C", "¤M", "§ú", "´Î", "ºj", "¥Ù", "¤}", "Âñ", "§æ¤â",
+ /* ¬ÞµPªZ¾¹ */ "¿ö", "¬Þ", "§Ù«ü", "¤â®M", "¤âÀô", "Áu³¹", "¬ÞµP", "½Ò¥»", "Á¿¸q",
+ /* ¨­ÅéªZ¾¹ */ "²¯¥Ò", "«`¥Ò", "¥Ö¥Ò", "©Ü­·", "®M¸Ë", "¬v¸Ë", "¦çªA", "¢â«ò", "¤ò¦ç",
+ /* ¸}³¡ªZ¾¹ */ "¾c", "¹u", "®j", "¼i", "¶³", "½ü", "Äû", "´à", "½ñ"
+ };
+
+ for (i = 0; i < 9; i++)
+ {
+ /* ¨Ì¯à¤O¤Î¤âÀYªº¿ú¨Ó¨M©wªZ¾¹ªº¦nÃa */
+
+ if (d.money < 12)
+ {
+ p[i].quality = 1;
+ p[i].cost = d.money;
+ }
+ else
+ {
+ num = d.money / 1000 + 1;
+ if (num > 300)
+ num = 300;
+ num = rand() % num + 1;
+ p[i].quality = num;
+ p[i].cost = 3 * num * num;
+ }
+
+ num = rand(); /* ¥Î¦P¤@¶Ã¼Æ¨Ó¨M©w adj+prep+noun¡A©Ò¥H mod ªº¼Æ¤£­n¤@¼Ë */
+ /* ¨Ì­þ¤@³¡¤À¸Ë³Æ¨Ó¨M©wªZ¾¹¦WºÙ¡Aª`·N¦r¦êªø«× */
+ sprintf(p[i].name, "%s%s%s", adje[num % 14], prep[num % 13], noun[type][num % 9]);
+ }
+}
+
+
+void
+pip_weapon_wear(type, variance) /* ¸Ë³ÆªZ¾¹¡A­pºâ¯à¤Oªº§ïÅÜ */
+ int type; /* ­þ¤@³¡¤À¸Ë³Æ */
+ int variance; /* ·sªZ¾¹ªº«~½è®t²§ */
+{
+ /* ¨Ì¸Ë³Æ³¡¦ì¤£¦P¨Ó§ïÅÜ«ü¼Æ */
+ if (type == 0) /* ÀY³¡ªZ¾¹ */
+ {
+ d.speed += variance;
+ d.immune += variance;
+ }
+ else if (type == 1) /* ¤â³¡ªZ¾¹ */
+ {
+ d.attack += variance;
+ d.immune += variance;
+ }
+ else if (type == 2) /* ¬ÞµPªZ¾¹ */
+ {
+ d.attack += variance;
+ d.resist += variance;
+ }
+ else if (type == 3) /* ¨­ÅéªZ¾¹ */
+ {
+ d.resist += variance;
+ d.immune += variance;
+ }
+ else if (type == 4) /* ¸}³¡ªZ¾¹ */
+ {
+ d.attack += variance;
+ d.speed += variance;
+ }
+}
+
+
+static int
+pip_weapon_doing_menu(quality, type, name) /* ªZ¾¹ÁʶRµe­± */
+ int quality; /* ¶Ç¤J¥Ø«e°tÀ¹ */
+ int type; /* ­þ¤@³¡¤À¸Ë³Æ */
+ char *name;
+{
+ char menutitle[5][11] = {"ÀY³¡¸Ë³Æ°Ï", "¤â³¡¸Ë³Æ°Ï", "¬ÞµP¸Ë³Æ°Ï", "¨­Åé¸Ë³Æ°Ï", "¸}³¡¸Ë³Æ°Ï"};
+ char buf[80];
+ int n;
+
+ /* ¶Ã¼Æ²£¥ÍªZ¾¹ */
+ weapon_generate(type);
+
+ /* ¦L¥XªZ¾¹¦Cªí */
+ vs_head(menutitle[type], str_site);
+ show_weapon_pic(0);
+ move(11, 0);
+ outs(" \033[1;37;41m [NO] [ªZ¾¹¦WºÙ] [«~½è] [°â»ù] \033[m\n");
+
+ /* ¦L¥XªZ¾¹³æ¶µ */
+ for (n = 0; n < 9; n++)
+ prints(" %d %-10s %6d %6d\n", n, p[n].name, p[n].quality, p[n].cost);
+
+ /* ¿ï³æ³B²z */
+ while (1)
+ {
+ out_cmd("", COLOR1 " ±Ä¶R " COLOR2 " (­x¤õ³c¤l) [B]ÁʶRªZ¾¹ [E]±j¤ÆªZ¾¹ [D]©ß±óªZ¾¹ [Q]¸õ¥X \033[m");
+
+ switch (vkey())
+ {
+ case 'b':
+ sprintf(buf, "±z¦³ %d ¤¸¡A·Q­nÁʶRÔ£©O¡H[Q] ", d.money);
+ n = ians(b_lines - 2, 1, buf) - '0';
+
+ if (n >= 0 && n < 9)
+ {
+ sprintf(buf, "½T©w­nÁʶR»ù­È %d ¤¸ ªº%s¶Ü(Y/N)¡H[N] ", p[n].cost, p[n].name);
+ if (ians(b_lines - 2, 1, buf) == 'y')
+ {
+ /* ´«ªZ¾¹ */
+ d.money -= p[n].cost;
+ strcpy(name, p[n].name);
+ pip_weapon_wear(type, p[n].quality - quality);
+ quality = p[n].quality;
+
+ sprintf(buf, "¤pÂû¤w¸g¸Ë°t¤W%s¤F", name);
+ vmsg(buf);
+ }
+ else
+ {
+ vmsg("©ñ±óÁʶR");
+ }
+ }
+ break;
+
+ case 'e':
+ n = quality * 100;
+ if (quality && d.money >= n)
+ {
+ sprintf(buf, "½T©w­nªá %d ¤¸¨Ó´£¤É%sªº¼ç¯à¶Ü(Y/N)¡H[N] ", n, name);
+ if (ians(b_lines - 2, 1, buf) == 'y')
+ {
+ /* «~½è¶V¦nªºªZ¾¹±j¤Æ¦¬¶O¶V°ª */
+ d.money -= n;
+ quality++;
+ pip_weapon_wear(type, 1);
+ }
+ }
+ break;
+
+ case 'd':
+ sprintf(buf, "½T©w­n©ß±ó%s¶Ü(Y/N)¡H[N] ", name);
+ if (ians(b_lines - 2, 1, buf) == 'y')
+ {
+ pip_weapon_wear(type, -quality);
+ name[0] = '\0';
+ quality = 0;
+ }
+ break;
+
+ case 'q':
+ case KEY_LEFT:
+ return quality;
+ }
+
+ /* itoc.010816: ®ø±¼ ians() ¯d¤Uªº´ÝÀe */
+ move (b_lines - 2, 0);
+ clrtoeol();
+ }
+}
+
+
+/*-------------------------------------------------------*/
+/* ªZ¾¹°Ó©±¿ï³æ: ¦U³¡¦ì */
+/*-------------------------------------------------------*/
+
+
+int
+pip_store_weapon_head() /* ÀY³¡ªZ¾¹ */
+{
+ d.weaponhead = pip_weapon_doing_menu(d.weaponhead, 0, d.equiphead);
+ return 0;
+}
+
+
+int
+pip_store_weapon_hand() /* ¤â³¡ªZ¾¹ */
+{
+ d.weaponhand = pip_weapon_doing_menu(d.weaponhand, 1, d.equiphand);
+ return 0;
+}
+
+
+int
+pip_store_weapon_shield() /* ¬ÞµPªZ¾¹ */
+{
+ d.weaponshield = pip_weapon_doing_menu(d.weaponshield, 2, d.equipshield);
+ return 0;
+}
+
+
+int
+pip_store_weapon_body() /* ¨­ÅéªZ¾¹ */
+{
+ d.weaponbody = pip_weapon_doing_menu(d.weaponbody, 3, d.equipbody);
+ return 0;
+}
+
+
+int
+pip_store_weapon_foot() /* ¸}³¡ªZ¾¹ */
+{
+ d.weaponfoot = pip_weapon_doing_menu(d.weaponfoot, 4, d.equipfoot);
+ return 0;
+}
+#endif /* HAVE_GAME */
diff --git a/pip/pipglobal.h b/pip/pipglobal.h
new file mode 100644
index 0000000..fc9ad18
--- /dev/null
+++ b/pip/pipglobal.h
@@ -0,0 +1,59 @@
+/*-------------------------------------------------------*/
+/* pip_global.h ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : global definitions & variables */
+/* create : 01/07/25 */
+/* update : 01/08/02 */
+/* author : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#ifndef _PIP_GLOBAL_H_
+#define _PIP_GLOBAL_H_
+
+
+#ifdef _PIPMAIN_C_
+# define VAR
+# define INI(x) = x
+#else
+# define VAR extern
+# define INI(x)
+#endif
+
+
+/* ----------------------------------------------------- */
+/* GLOBAL DEFINITION */
+/* ----------------------------------------------------- */
+
+
+ /* --------------------------------------------------- */
+ /* ¹CÀ¸¦WºÙ³]©w */
+ /* --------------------------------------------------- */
+
+
+#define PIPNAME "Ãdª«Âû"
+
+
+ /* --------------------------------------------------- */
+ /* ­Ó¤H¥Ø¿ýÀɦW³]©w */
+ /* --------------------------------------------------- */
+
+
+#define FN_PIP "chicken"
+
+VAR char *fn_pip INI(FN_PIP);
+
+
+/* ----------------------------------------------------- */
+/* GLOBAL VARIABLE */
+/* ----------------------------------------------------- */
+
+
+VAR struct CHICKEN d; /* ¤pÂûªº¸ê®Æ */
+
+VAR time_t start_time; /* ¥»¦¸¹CÀ¸¶}©l®É¶¡ */
+VAR time_t last_time; /* ¤W¦¸§ó·s®É¶¡ */
+
+#undef VAR
+
+#endif /* _PIP_GLOBAL_H_ */
diff --git a/pip/pipstruct.h b/pip/pipstruct.h
new file mode 100644
index 0000000..818bf3d
--- /dev/null
+++ b/pip/pipstruct.h
@@ -0,0 +1,430 @@
+/* ----------------------------------------------------- */
+/* pip_struct.h ( NTHU CS MapleBBS Ver 3.10 ) */
+/* ----------------------------------------------------- */
+/* target : ¤pÂû data structure */
+/* create : / / */
+/* update : 01/08/14 */
+/* author : dsyan.bbs@@forever.twbbs.org */
+/* modify : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/* ----------------------------------------------------- */
+
+
+#ifndef _PIP_STRUCT_H_
+#define _PIP_STRUCT_H_
+
+
+/* ------------------------------------------------------- */
+/* ¤pÂû°Ñ¼Æ³]©w */
+/* ------------------------------------------------------- */
+
+
+struct CHICKEN
+{
+ /* ---©m¦W¤Î¥Í¤é--- */
+ char name[IDLEN + 1]; /* ©m ¦W */
+ char birth[9]; /* ¥Í ¤é */
+
+ /* ---¤pÂûªº®É¶¡--- */
+ time_t bbtime; /* ª±¤pÂûªºÁ`®É¶¡(¬í) */
+ /* itoc.010804: ¥Ø«eªº³]©w¬O 30 ¤À(§Y30*60¬í) ¬°¤@·³ */
+
+ /* ¥H¤U¥þ¬O integer */
+ /* ¨C¤@ºØÃþ«¬³£«O¯d¦Ü¤QÄæ¥H·Ç³ÆÂX¥R */
+ /* ¨S¦³µù¸Ñªº³o¨Ç³£¬O«O¯d¡A¨S¦³¨Ï¥ÎªºÄæ¦ì */
+
+ /* ---°ò¥»¸ê®Æ--- */
+ int year; /* ¥Í¤é ¦~ */
+ int month; /* ¥Í¤é ¤ë */
+ int day; /* ¥Í¤é ¤é */
+ int sex; /* ©Ê §O 1:¡ñ 2:¡ð */
+ int death; /* ª¬ ºA 1:¦º¤` 2:©ß±ó 3:µ²§ô */
+ int liveagain; /* ´_¬¡¦¸¼Æ */
+ int wantend; /* 20·³µ²§½ 1:¤£­n¥B¥¼±B 2:¤£­n¥B¤w±B 3:¤£­n¥B·í²Ä¤TªÌ 4:­n¥B¥¼±B 5:­n¥B¤w±B 6:­n¥B·í²Ä¤TªÌ */
+ int lover; /* ·R¤H 0:¨S¦³ 1:Å]¤ý 2:Às±Ú 3:A 4:B 5:C 6:D 7:E */
+ int seeroyalJ; /* ¬O§_¥i¥H¹J¤W¤ý¤l/¤½¥D 1:¥i¥H(¤ý¤l¤w¸g¦^°ê¤F) 0:¤£¯à(¤ý¤lÁÙ¦bÃäæ) */
+ int quest; /* ¥ô °È 0:µL¥ô°È !=0:¥ô°È½s¸¹ */
+
+ /* ---ª¬ºA«ü¼Æ--- */
+ /* itoc.010730: ³o¨Ç«ü¼Æ¦b¤u§@/¾Ç²ß/¹Cª±¤¤§ïÅÜ */
+ int relation; /* ¿Ë¤lÃö«Y (¤H©MÃdª«ªº¤¬°ÊÃö«Y) */
+ int happy; /* §Ö ¼Ö «× */
+ int satisfy; /* º¡ ·N «× */
+ int fallinlove; /* ÅÊ·R«ü¼Æ */
+ int belief; /* «H ¥õ */
+ int sin; /* ¸o Ä^ */
+ int affect; /* ·P ¨ü */
+ int state7;
+ int state8;
+ int state9;
+
+ /* ---°·±d«ü¼Æ--- */
+ /* itoc.010730: ³o¨Ç«ü¼Æ¦b¤u§@/¾Ç²ß/¹Cª±¤¤§ïÅÜ */
+ int weight; /* Åé ­« */
+ int tired; /* ¯h ³Ò «× */
+ int sick; /* ¯f ®ð */
+ int shit; /* ²M ¼ä «× */
+ int body4;
+ int body5;
+ int body6;
+ int winn; /* ²q®±Ä¹ªº¦¸¼Æ */
+ int losee; /* ²q®±¿éªº¦¸¼Æ */
+ int tiee; /* ²q®±¥­¤âªº¦¸¼Æ */
+
+ /* ---µû»ù°Ñ¼Æ--- */
+ int social; /* ªÀ¥æµû»ù */
+ int family; /* ®a¨Æµû»ù */
+ int hexp; /* ¾Ô°«µû»ù */
+ int mexp; /* Å]ªkµû»ù */
+ int value4;
+ int value5;
+ int value6;
+ int value7;
+ int value8;
+ int value9;
+
+ /* ---¯à¤O°Ñ¼Æ--- */
+ /* itoc.010730: ³o¨Ç«ü¼Æ¦b¾Ç²ß¤¤¤j¶q§ïÅÜ¡A¦b¤u§@¤¤·L¶q½Õ¾ã */
+ int toman; /* «Ý¤H±µª« */
+ int character; /* ®ð ½è «× */
+ int love; /* ·R ¤ß */
+ int wisdom; /* ´¼ ¤O */
+ int art; /* ÃÀ³N¯à¤O */
+ int etchics; /* ¹D ¼w */
+ int brave; /* «i ´± */
+ int homework; /* ±½¦a¬~¦ç */
+ int charm; /* ¾y ¤O */
+ int manners; /* § »ö */
+ int speech; /* ½Í ¦R */
+ int cook; /* ²i ¶¹ */
+ int attack; /* §ð À» ¤O */
+ int resist; /* ¨¾ ¿m ¤O */
+ int speed; /* ³t «× */
+ int hskill; /* ¾Ô°«§Þ³N */
+ int mskill; /* Å]ªk§Þ³N */
+ int immune; /* §ÜÅ]¯à¤O */
+ int learn18;
+ int learn19;
+
+ /* ---¾Ô°««ü¼Ð--- (ÀH¤É¯Å¦Ó¼W¥[) */
+ /* itoc.010730: ¬°¤F¼W¥[¾Ô°«ªº¥²­n©Ê¡Amaxhp maxmp maxvp maxsp
+ ³o¨ÇÄÝ©ÊÀ³¸Ó¥u¦b exp ¼W¥[¤É¯Å«á¡A¤~¯à¤j¶q¼W¥[ */
+ /* itoc.010804: ¥Ø«e®e³\¦b¬Y¨Çª¬ªp¤U maxhp ¥H 0~3 ÂIªº³t«×¼W¥[ */
+
+ int level; /* µ¥ ¯Å */
+ int exp; /* ¸g Åç ­È */
+ int hp; /* Health Point ¦å */
+ int maxhp; /* ³Ì¤j¦å */
+ int mp; /* Mana Point ªk¤O */
+ int maxmp; /* ³Ì¤jªk¤O */
+ int vp; /* moVe Point ²¾°Ê¤O */
+ int maxvp; /* ³Ì¤j²¾°Ê¤O */
+ int sp; /* Spirit Point ¤º¤O */
+ int maxsp; /* ³Ì¤j¤º¤O */
+
+ /* ---©Ò¾Ç·|§Þ¯à--- */ /* bitwise operation */
+ usint skillA; /* §Þ¯à: Å@¨­ */
+ usint skillB; /* §Þ¯à: »´¥\ */
+ usint skillC; /* §Þ¯à: ¤ßªk */
+ usint skillD; /* §Þ¯à: ®±ªk */
+ usint skillE; /* §Þ¯à: ¼Cªk */
+ usint skillF; /* §Þ¯à: ¤Mªk */
+ usint skillG; /* §Þ¯à: ·t¾¹¡B¬r */
+ usint skill7;
+ usint skill8;
+ usint skillXYZ; /* ¯S®í§Þ¯à */
+ usint spellA; /* ªvÀøªk³N */
+ usint spellB; /* ¹p¨tªk³N */
+ usint spellC; /* ¦B¨tªk³N */
+ usint spellD; /* ª¢¨tªk³N */
+ usint spellE; /* ¤g¨tªk³N */
+ usint spellF; /* ­·¨tªk³N */
+ usint spellG; /* ¨s·¥ªk³N */
+ usint spell7;
+ usint spell8;
+ usint spell9;
+
+ /* ---ªZ¾¹ªº°Ñ¼Æ--- */
+ int weaponhead; /* ÀY³¡ªZ¾¹ */
+ int weaponhand; /* ¤â³¡ªZ¾¹ */
+ int weaponshield; /* ¬ÞµPªZ¾¹ */
+ int weaponbody; /* ¨­ÅéªZ¾¹ */
+ int weaponfoot; /* ¸}³¡ªZ¾¹ */
+ int weapon5;
+ int weapon6;
+ int weapon7;
+ int weapon8;
+ int weapon9;
+
+ /* ---¦YªºªF¦è--- */
+ int food; /* ­¹ ª« */
+ int cookie; /* ¹s ­¹ */
+ int eat2;
+ int pill; /* ¤j ÁÙ ¤¦ : ¸É¦å */
+ int medicine; /* ÆF ªÛ : ¸Éªk¤O */
+ int burger; /* ¤j ¸É ¤Y : ¸É²¾°Ê¤O */
+ int ginseng; /* ¤d¦~¤Hçx : ¸É¤º¤O */
+ int paste; /* ¶Â¥ÉÂ_Äò»I : ¦å¥þº¡ */
+ int snowgrass; /* ¤Ñ¤s³·½¬ : ³q³q¥þº¡ */
+ int eat9;
+
+ /* ---¾Ö¦³ªºªF¦è--- */
+ int money; /* ª÷ ¿ú */
+ int book; /* ®Ñ ¥» */
+ int toy; /* ª± ¨ã */
+ int playboy; /* ½Ò¥~Ūª« */
+ int thing4;
+ int thing5;
+ int thing6;
+ int thing7;
+ int thing8;
+ int thing9;
+
+ /* ---°Ñ¨£¤ý¦Ú-- */
+ int royalA; /* ©M ¦u½Ã ªº¦n·P */
+ int royalB; /* ©M ªñ½Ã ªº¦n·P */
+ int royalC; /* ©M ±N­x ªº¦n·P */
+ int royalD; /* ©M ¤j¦Ú ªº¦n·P */
+ int royalE; /* ©M ²½¥q ªº¦n·P */
+ int royalF; /* ©M Ãd¦m ªº¦n·P */
+ int royalG; /* ©M ¤ý¦m ªº¦n·P */
+ int royalH; /* ©M °ê¤ý ªº¦n·P */
+ int royalI; /* ©M ¤p¤¡ ªº¦n·P */
+ int royalJ; /* ©M ¤ý¤l/¤½¥D ªº¦n·P */
+
+ /* -------¤u§@¦¸¼Æ-------- */
+ int workA; /* ®a¨Æ */
+ int workB; /* «O©i */
+ int workC; /* ®È©± */
+ int workD; /* ¹A³õ */
+ int workE; /* À\ÆU */
+ int workF; /* ±Ð°ó */
+ int workG; /* ¦aÅu */
+ int workH; /* ¥ï¤ì */
+ int workI; /* ¬ü¾v */
+ int workJ; /* Ây¤H */
+ int workK; /* ¤u¦a */
+ int workL; /* ¦u¹Ó */
+ int workM; /* ®a±Ð */
+ int workN; /* °s®a */
+ int workO; /* °s©± */
+ int workP; /* ©]Á`·| */
+ int work16;
+ int work17;
+ int work18;
+ int work19;
+
+ /* -------¤W½Ò¦¸¼Æ-------- */
+ int classA; /* ¦ÛµM¬ì¾Ç */
+ int classB; /* ­ð¸Ö§ºµü */
+ int classC; /* ¯«¾Ç±Ð¨| */
+ int classD; /* ­x¾Ç±Ð¨| */
+ int classE; /* ¼C¹D§Þ³N */
+ int classF; /* ®æ°«¾Ô§Þ */
+ int classG; /* Å]ªk±Ð¨| */
+ int classH; /* §»ö±Ð¨| */
+ int classI; /* øµe§Þ¥© */
+ int classJ; /* »RÁЧޥ© */
+ int class10;
+ int class11;
+ int class12;
+ int class13;
+ int class14;
+ int class15;
+ int class16;
+ int class17;
+ int class18;
+ int class19;
+
+ /* ---ªZ¾¹ªº¦WºÙ--- */
+ char equiphead[11]; /* ÀY³¡ªZ¾¹¦WºÙ */
+ char equiphand[11]; /* ¤â³¡ªZ¾¹¦WºÙ */
+ char equipshield[11]; /* ¬ÞµPªZ¾¹¦WºÙ */
+ char equipbody[11]; /* ¨­ÅéªZ¾¹¦WºÙ */
+ char equipfoot[11]; /* ¸}³¡ªZ¾¹¦WºÙ */
+ char equip5[11];
+ char equip6[11];
+ char equip7[11];
+ char equip8[11];
+ char equip9[11];
+};
+typedef struct CHICKEN CHICKEN;
+
+
+/* ------------------------------------------------------- */
+/* ª««~°Ñ¼Æ³]©w */
+/* ------------------------------------------------------- */
+
+struct itemset
+{
+ int num; /* ½s¸¹ */
+ char *name; /* ¦W¦r */
+ char *msgbuy; /* ¥\¥Î */
+ char *msguse; /* »¡©ú */
+ int price; /* »ù®æ */
+};
+typedef struct itemset itemset;
+
+
+/* ------------------------------------------------------- */
+/* °Ñ¨£¤ý¦Ú°Ñ¼Æ³]©w */
+/* ------------------------------------------------------- */
+
+struct royalset
+{
+ char *num; /* ¥N½X */
+ char *name; /* ¤ý¦Úªº¦W¦r */
+ int needmode; /* »Ý­nªºmode *//* 0:¤£»Ý­n 1:§»ö 2:½Í¦R */
+ int needvalue; /* »Ý­nªºvalue */
+ int addtoman; /* ³Ì¤jªº¼W¥[¶q */
+ int maxtoman; /* ®w¦s¶q */
+ char *words1;
+ char *words2;
+};
+typedef struct royalset royalset;
+
+
+/* ------------------------------------------------------- */
+/* §Þ¯à°Ñ¼Æ³]©w */
+/* ------------------------------------------------------- */
+
+
+#if 0 /* itoc.010729.»¡©ú */
+
+ smode ­n¹ïÀ³ struce CHICKEN ªºªº skill/spell
+ smode = +1 ¬° skillA¡Asmode = +2 ¬° skillB
+ smode = -1 ¬° spellA¡Asmode = -2 ¬° spellB
+
+ sno ¬O¦¹§Þ¯àªº½s¸¹¡A¨Ò¦p 0x01 ¬O¼Cªk¥Ò¡A0x02 ¬O¼Cªk¤A¡A0x04 ¬O¼Cªk¤þ (ª`·N¬O bit operation)
+ sbasic «h¬O¦¹§Þ¯àªº°ò¥»§Þ¯à¡A­Y­n¥ý¾Ç²ß¼Cªk¥Ò¤þ¥H«á¤~¯à¾Ç²ß¼Cªk¤B¡A¨º»ò¼Cªk¤Bªº sbasic = 0x01 | 0x04 = 0x05
+
+ needhp/nedmp/addtired ­Y¬O¥¿ªº¡A´N¬O¦©¦å/¦©ªk¤O/¥[¯h³Ò
+
+#endif
+
+
+struct skillset
+{
+ int smode; /* skill mode 0:¯S®í >0:ªZ¥\ <0:Å]ªk */
+ usint sno; /* skill number */
+ usint sbasic; /* basic skill */
+ char name[13]; /* §Þ¯àªº¦W¦r¡A­­¨î¤»­Ó¤¤¤å¦r */
+ int needhp; /* ¥Í©R¤Oªº§ïÅÜ */
+ int needmp; /* ªk¤Oªº§ïÅÜ */
+ int needvp; /* ²¾°Ê¤Oªº§ïÅÜ */
+ int needsp; /* ¤º¤Oªº§ïÅÜ */
+ int addtired; /* ¯h³Ò­Èªº§ïÅÜ */
+ int effect; /* ®ÄªG/±j®z */
+ int pic; /* ¹ÏÀÉ */
+ char msg[41]; /* ¨Ï¥Î§Þ¯àªº»¡©ú¡A­­¨î20­Ó¤¤¤å¦r */
+};
+typedef struct skillset skillset;
+
+
+/* ------------------------------------------------------- */
+/* ¿ï³æªº³]©w */
+/* ------------------------------------------------------- */
+
+struct pipcommands
+{
+ int (*fptr) ();
+ int key;
+};
+typedef struct pipcommands pipcommands;
+
+
+/* ------------------------------------------------------- */
+/* ©Çª«°Ñ¼Æ³]©w */
+/* ------------------------------------------------------- */
+
+struct playrule
+{
+ /* itoc.010731: ©Çª«¥u¦³ hp ³oºØ¦å¡A¤£»Ý­n mp/vp/sp/tired¡A
+ ¥¦ªº§Þ¯à§ðÀ»µL­­¡A¦ý¬O©Çª«ªº§ðÀ»¼Ò¦¡¬O¥Ñ¶Ã¼Æ¨M©w¡A¦p¦¹¥i²³æ¤Æ */
+
+ /* itoc.010731: µ¥¯Å¬° n ¯Åªº©Çª«¡A¨ä´Á±æ
+ maxhp = 0.75*(n^2) (©Mª±®a¤@¼Ë)
+ attack/spirit/magic/armor/dodge = 10*n
+ money = 10*n
+ exp = 5*n (­ì«h¤W¥´20°¦©Çª«¤É¤@¯Å) */
+
+ char name[13]; /* ¦W¦r¡A­­¨î¤»­Ó¤¤¤å¦r */
+ int attribute; /* ·R¥Îªº§ðÀ»§Þ¯à 0:µL >0:ªZ¥\ <0:Å]ªk */
+ int hp; /* ¦å */
+ int maxhp; /* ³Ì¤j¦å */
+
+ /* ¤À§O»P©Çª« ª«²z/ªZ¥\/Å]ªk §ðÀ»¤pÂû¥¿¬ÛÃö */
+ int attack; /* ª«²z§ðÀ»¯à¤O */
+ int spirit; /* ¤º¤O«ü¼Æ¡A§Þ¯à§ðÀ»¯à¤O */
+ int magic; /* Å]ªk«ü¼Æ¡Aªk³N§ðÀ»¯à¤O */
+
+ /* ¤À§O»P¤pÂû¥´©Çª«ªº ¶Ë®`/©R¤¤²v ­t¬ÛÃö */
+ int armor; /* ¨¾Å@«ü¼Æ¡A©Ó¨ü§ðÀ»ªº¯à¤O */
+ int dodge; /* °{Á׫ü¼Æ¡A°{Á×§ðÀ»ªº¯à¤O */
+
+ /* ¥´¦º©Çª«ªº¼úÀy */
+ int money; /* ¥´¦º©Çª«±o¨ìªº°]Ä_ */
+ int exp; /* ¥´¦º©Çª«±o¨ìªº¸gÅç­È */
+
+ int pic; /* ¹ÏÀÉ */
+};
+typedef struct playrule playrule;
+
+
+/* ------------------------------------------------------- */
+/* ªZ¾¹°Ñ¼Æ³]©w */
+/* ------------------------------------------------------- */
+
+struct weapon
+{
+ char name[11]; /* ¦WºÙ¡A­­¨î¤­­Ó¤¤¤å¦r */
+ int quality; /* «~½è */
+ int cost; /* »ù®æ */
+};
+typedef struct weapon weapon;
+
+
+/* ------------------------------------------------------- */
+/* PK ¹ï¾Ô°Ñ¼Æ³]©w */
+/* ------------------------------------------------------- */
+
+
+#define MAX_PIPPK_USER 10 /* ³Ì¦h¦P®É¦³ 10 ¤H¦b PK ³õ¤¤ */
+
+struct PTMP
+{
+ char inuse; /* 0:¥¼¨Ï¥Î 1:»W¶Õ«Ýµo 2:¤U¬D¾Ô®Ñ -1:¾Ô°«¤¤ */
+ char done; /* 0:¥¼¦æ°Ê 1:¤w¦æ°Ê */
+ char name[IDLEN + 1]; /* ©m¦W */
+ char userid[IDLEN + 1]; /* ¦Û¤vªº ID */
+ char mateid[IDLEN + 1]; /* ¹ï¤âªº ID */
+
+ int sex; /* ©Ê§O */
+ int level; /* µ¥¯Å */
+
+ int hp; /* Health Point ¦å */
+ int maxhp; /* ³Ì¤j¦å */
+ int mp; /* Mana Point ªk¤O */
+ int maxmp; /* ³Ì¤jªk¤O */
+ int vp; /* moVe Point ²¾°Ê¤O */
+ int maxvp; /* ³Ì¤j²¾°Ê¤O */
+ int sp; /* Spirit Point ¤º¤O */
+ int maxsp; /* ³Ì¤j¤º¤O */
+
+ int combat; /* ª«²z¨­¬q: ¨M©w¡u¦×·i¡B¨¾¿m¡vªº±j«× */
+ int magic; /* Å]ªk³y¸Ú: ¨M©w¡uªk³N-¦U¨t¡vªº±j«× */
+ int speed; /* ±Ó±¶§Þ¥©: ¨M©w¡u§Þ¯à-Å@¨­¡B§Þ¯à-»´¥\¡B§Þ¯à-¼Cªk¡vªº±j«× */
+ int spirit; /* ¤º¤O±j«×: ¨M©w¡u§Þ¯à-¤ßªk¡B§Þ¯à-®±ªk¡B§Þ¯à-¤Mªk¡vªº±j«× */
+ int charm; /* °Ê·P¾y¤O: ¨M©w¡u¾y´b¡B¥l³ê¡vªº±j«× */
+ int oral; /* ¤f­YÄaªe: ¨M©w¡u»¡ªA¡Bº´°Ê¡vªº±j«× */
+ int cook; /* ¬ü¨ý²i½Õ: ¨M©w¡u§Þ¯à-·t¾¹¡Bªk³N-ªvÀø¡v */
+};
+typedef struct PTMP PTMP;
+
+
+struct PCACHE
+{
+ PTMP pslot[MAX_PIPPK_USER]; /* PTMP slots */
+};
+typedef struct PCACHE PCACHE;
+
+#endif /* _PIP_STRUCT_H_ */
diff --git a/sh/bmta.sh b/sh/bmta.sh
new file mode 100755
index 0000000..fb667ab
--- /dev/null
+++ b/sh/bmta.sh
@@ -0,0 +1,8 @@
+#!/bin/sh
+#¥»µ{¦¡¥Î¨Ó¤ÀªR³s±µ bbs ªº smtp ³s±µ¨Ó·½¼Æ¶q
+cat /dev/null | awk 'BEGIN {printf("%10s %-20s\n", "³s½u¦¸¼Æ", "³s½u¨Ó·½")} {} END{}'
+
+cat /home/bbs/run/bmta.log.* | grep CONN | sort -k 3 -r | awk '{print $3}'| awk 'BEGIN {} {Number[$1]++} END {
+ for(course in Number)
+ printf("%10d %-20s\n", Number[course], course)
+}' | sort -n -r
diff --git a/sh/killbbs.sh b/sh/killbbs.sh
new file mode 100755
index 0000000..71b1517
--- /dev/null
+++ b/sh/killbbs.sh
@@ -0,0 +1,13 @@
+#!/bin/sh
+# ²M°£¯¸¤W¨Ï¥ÎªÌ»Pshared memory
+kill `ps -auxwww | grep bbsd | awk '{print $2}'`
+
+# for freebsd only
+for i in `ipcs | grep bbs | awk '{print $3}'`
+do
+ if [ $OSTYPE = "FreeBSD" ]; then
+ ipcrm -M $i
+ fi
+done
+
+# Linux ½Ð¥Î ipcs ¤Î ipcrm shm
diff --git a/sh/killhigh.sh b/sh/killhigh.sh
new file mode 100755
index 0000000..3e37cb8
--- /dev/null
+++ b/sh/killhigh.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+ps aux | awk '$3 > 30 {print $2}' | xargs kill -9
+# ¬å±¼¦Y¸ê·½ > 30 ªº process
diff --git a/sh/killtop.sh b/sh/killtop.sh
new file mode 100755
index 0000000..970147c
--- /dev/null
+++ b/sh/killtop.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+kill -9 `top | grep -w bbsd | grep RUN | awk '{print $1}'`
diff --git a/sh/routin.sh b/sh/routin.sh
new file mode 100755
index 0000000..57f6985
--- /dev/null
+++ b/sh/routin.sh
@@ -0,0 +1,45 @@
+#!/bin/bash
+####################################################################
+#¬ÝªO±Æ¦æº]²Î­p
+basedir=/home/bbs/manage
+logfile=/home/bbs/gem/@/@hotboard
+hotweek=/home/bbs/gem/@/@hotweek
+hotmonth=/home/bbs/gem/@/@hotmonth
+yestarday=`date --date='1 day ago' +%y%m%d`
+if [ ! -d "$basedir/brd" ]; then
+ mkdir -p $basedir/brd
+fi
+cp /home/bbs/run/brd_usies.log $basedir/brd/$yestarday.log
+cat /home/bbs/run/brd_usies.log|awk '{print $1}'|uniq -c|awk '{printf "%-15s%s\n" ,$2,$1}'|sort -rn -k 2 > $basedir/board.log
+echo "¬ÝªO¦WºÙ °ÑÆ[¤H¦¸"|awk '{printf("%-15s%s\n",$1,$2)}' > $logfile
+echo "¦W¦¸ ¬ÝªO¦WºÙ °ÑÆ[¤H¦¸"|awk '{printf("%-8s%-15s%s\n",$1,$2,$3)}' > $hotweek
+echo "¦W¦¸ ¬ÝªO¦WºÙ °ÑÆ[¤H¦¸"|awk '{printf("%-8s%-15s%s\n",$1,$2,$3)}' > $hotmonth
+echo "-----------------------------" >> $logfile
+echo "-----------------------------" >> $hotweek
+echo "-----------------------------" >> $hotmonth
+ls -l /home/bbs/brd|awk '$1 !~ /total/ {print $9}'|sed 's/\///' > $basedir/boardlist
+echo "¥Ø«e¬ÝªO­Ó¼Æ:`cat $basedir/boardlist|wc -l`" >> $logfile
+cat $basedir/board.log >> $logfile
+today=`date +%y%m%d`
+find $basedir/brd -mtime -7 -type f -exec cat '{}' \;|awk '{print $1}'|sort|uniq -c|awk '{printf "%-15s%s\n" ,$2,$1}'|sort -rn -k 2 > $basedir/week
+cat -b $basedir/week >> $hotweek
+find $basedir/brd -mtime -30 -type f -exec cat '{}' \;|awk '{print $1}'|sort|uniq -c|awk '{printf "%-15s%s\n" ,$2,$1}'|sort -rn -k 2 > $basedir/month
+cat -b $basedir/month >> $hotmonth
+boards=`cat $basedir/boardlist`
+for board in $boards
+do
+ judge=`cat $basedir/board.log|grep "^$board"`
+ if [ "$judge" == "" ]; then
+ echo "$board 0"|awk '{printf("%-15s%s\n",$1,$2)}' >> $logfile
+ fi
+ judge=`cat $basedir/week|grep "^$board"`
+ if [ "$judge" == "" ]; then
+ echo "$board 0"|awk '{printf("%-15s%s\n",$1,$2)}' >> $hotweek
+ fi
+ judge=`cat $basedir/month|grep "^$board"`
+ if [ "$judge" == "" ]; then
+ echo "$board 0"|awk '{printf("%-15s%s\n",$1,$2)}' >> $hotmonth
+ fi
+done
+}
+#####################################################################
diff --git a/so/Makefile b/so/Makefile
new file mode 100644
index 0000000..8422e52
--- /dev/null
+++ b/so/Makefile
@@ -0,0 +1,64 @@
+# ------------------------------------------------------ #
+# Makefile ( NTHU CS MapleBBS Ver 2.36 ) #
+# ------------------------------------------------------ #
+# author : opus.bbs@bbs.cs.nthu.edu.tw #
+# target : Makefile for MapleBBS main programs #
+# create : 95/03/29 #
+# update : 95/12/15 #
+# ------------------------------------------------------ #
+
+
+# ------------------------------------------------------ #
+# ¤U¦Cªº make rules ¤£»Ý­×§ï #
+# ------------------------------------------------------ #
+
+
+SO = admutil.so aloha.so bank.so chat.so help.so innbbs.so manage.so newbrd.so \
+ song.so vote.so xyz.so \
+ calendar.so classtable.so credit.so todo.so
+
+
+.SUFFIXES:
+.SUFFIXES: .c .o .so
+
+.c.o: ; $(CC) $(CFLAGS) -c $*.c
+.o.so: ; ld -s -G $*.o -o $*.so -L../lib -ldao
+
+
+all:
+ @echo "Please enter 'make sys-type', "
+ @echo " make sun : for Sun-OS 4.x and maybe some BSD systems, cc or gcc"
+ @echo " make linux : for Linux"
+ @echo " make solaris : for Sun-OS 5.x gcc"
+ @echo " make sol-x86 : for Solaris 7 x86"
+ @echo " make freebsd : for BSD 4.4 systems"
+ @echo " make bsd : for BSD systems, cc or gcc, if not in the above lists"
+ @echo " make cygwin : for Microsoft Windows and Cygwin gcc"
+
+sun:
+ @$(MAKE) CC=gcc CFLAGS="-O2 -pipe -fomit-frame-pointer -Wunused -I../include" $(SO)
+
+linux:
+ @$(MAKE) CC=gcc CFLAGS="-DLINUX -O2 -pipe -fomit-frame-pointer -Wunused -I../include" $(SO)
+
+solaris:
+ @$(MAKE) CC=gcc CFLAGS="-DSOLARIS -DSYSV -O2 -pipe -fomit-frame-pointer -Wunused -I../include" $(SO)
+
+sol-x86:
+ @$(MAKE) CC=gcc CFLAGS="-DSOLARIS -DSYSV -O2 -fomit-frame-pointer -Wunused -I../include" $(SO)
+
+freebsd:
+ @$(MAKE) CC=gcc CFLAGS="-DBSD44 -O2 -pipe -fomit-frame-pointer -Wunused -I../include" $(SO)
+
+bsd:
+ @$(MAKE) CC=gcc CFLAGS="-DBSD44 -O2 -pipe -fomit-frame-pointer -Wunused -I../include" $(SO)
+
+cygwin:
+ @cd ../maple; make cygwin
+
+
+install: $(SO)
+ install -m 0700 $? $(HOME)/bin
+
+clean:
+ rm -f *.so *.o *~
diff --git a/so/admutil.c b/so/admutil.c
new file mode 100644
index 0000000..9034f6b
--- /dev/null
+++ b/so/admutil.c
@@ -0,0 +1,883 @@
+/*-------------------------------------------------------*/
+/* admutil.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : ¯¸ªø«ü¥O */
+/* create : 95/03/29 */
+/* update : 01/03/01 */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+
+extern BCACHE *bshm;
+extern UCACHE *ushm;
+
+
+/* ----------------------------------------------------- */
+/* ¯¸°È«ü¥O */
+/* ----------------------------------------------------- */
+
+
+int
+a_user()
+{
+ int ans;
+ ACCT acct;
+
+ move(1, 0);
+ clrtobot();
+
+ while (ans = acct_get(msg_uid, &acct))
+ {
+ if (ans > 0)
+ acct_setup(&acct, 1);
+ }
+ return 0;
+}
+
+
+int
+a_search() /* itoc.010902: ¼É¤O·j´M¨Ï¥ÎªÌ */
+{
+ ACCT acct;
+ char c;
+ char key[30];
+
+ if (!vget(b_lines, 0, "½Ð¿é¤JÃöÁä¦r(©m¦W/¼ÊºÙ/¨Ó·½/«H½c)¡G", key, 30, DOECHO))
+ return XEASY;
+
+ /* itoc.010929.µù¸Ñ: ¯u¬O¦³°÷¼É¤O :p ¦Ò¼{¥ý¥Ñ reaper °µ¥X¤@­Ó .PASSWDS ¦A¥h§ä */
+
+ for (c = 'a'; c <= 'z'; c++)
+ {
+ char buf[64];
+ struct dirent *de;
+ DIR *dirp;
+
+ sprintf(buf, "usr/%c", c);
+ if (!(dirp = opendir(buf)))
+ continue;
+
+ while (de = readdir(dirp))
+ {
+ if (acct_load(&acct, de->d_name) < 0)
+ continue;
+
+ if (strstr(acct.realname, key) || strstr(acct.username, key) ||
+ strstr(acct.lasthost, key) || strstr(acct.email, key))
+ {
+ move(1, 0);
+ acct_setup(&acct, 1);
+
+ if (vans("¬O§_Ä~Äò·j´M¤U¤@µ§¡H[N] ") != 'y')
+ {
+ closedir(dirp);
+ goto end_search;
+ }
+ }
+ }
+ closedir(dirp);
+ }
+end_search:
+ vmsg("·j´M§¹²¦");
+ return 0;
+}
+
+
+int
+a_editbrd() /* itoc.010929: ­×§ï¬ÝªO¿ï¶µ */
+{
+ int bno;
+ BRD *brd;
+ char bname[BNLEN + 1];
+
+ if (brd = ask_board(bname, BRD_L_BIT, NULL))
+ {
+ bno = brd - bshm->bcache;
+ brd_edit(bno);
+ }
+ else
+ {
+ vmsg(err_bid);
+ }
+
+ return 0;
+}
+
+
+int
+a_xfile() /* ³]©w¨t²ÎÀÉ®× */
+{
+ static char *desc[] =
+ {
+ "¬ÝªO¤å³¹´Á­­",
+
+ "¨­¤À»{ÃÒ«H¨ç",
+ "»{ÃÒ³q¹L³qª¾",
+ "­«·s»{ÃÒ³qª¾",
+
+#ifdef HAVE_DETECT_CROSSPOST
+ "¸ó¶K°±Åv³qª¾",
+#endif
+
+ "¤£¶®¦W³æ",
+ "¯¸°È¦W³æ",
+
+ "¸`¤é",
+
+#ifdef HAVE_WHERE
+ "¬G¶m ID",
+ "¬G¶m IP",
+ "¬G¶m FQDN",
+#endif
+
+#ifdef HAVE_TIP
+ "¨C¤é¤p¯µ³Z",
+#endif
+
+#ifdef HAVE_LOVELETTER
+ "±¡®Ñ²£¥Í¾¹¤å®w",
+#endif
+
+ "»{ÃÒ¥Õ¦W³æ",
+ "»{ÃҶ¦W³æ",
+
+ "¦¬«H¥Õ¦W³æ",
+ "¦¬«H¶Â¦W³æ",
+
+#ifdef HAVE_LOGIN_DENIED
+ "©Úµ´³s½u¦W³æ",
+#endif
+
+ NULL
+ };
+
+ static char *path[] =
+ {
+ FN_ETC_EXPIRE,
+
+ FN_ETC_VALID,
+ FN_ETC_JUSTIFIED,
+ FN_ETC_REREG,
+
+#ifdef HAVE_DETECT_CROSSPOST
+ FN_ETC_CROSSPOST,
+#endif
+
+ FN_ETC_BADID,
+ FN_ETC_SYSOP,
+
+ FN_ETC_FEAST,
+
+#ifdef HAVE_WHERE
+ FN_ETC_IDHOME,
+ FN_ETC_HOST,
+ FN_ETC_FQDN,
+#endif
+
+#ifdef HAVE_TIP
+ FN_ETC_TIP,
+#endif
+
+#ifdef HAVE_LOVELETTER
+ FN_ETC_LOVELETTER,
+#endif
+
+ TRUST_ACLFILE,
+ UNTRUST_ACLFILE,
+
+ MAIL_ACLFILE,
+ UNMAIL_ACLFILE,
+
+#ifdef HAVE_LOGIN_DENIED
+ BBS_ACLFILE,
+#endif
+ };
+
+ x_file(M_XFILES, desc, path);
+ return 0;
+}
+
+int
+a_sysload()
+{
+ char buf[80];
+
+ double load[3];
+ getloadavg(load, 3);
+
+ /* ³o¤T­Ó¼Æ­È¤À§Oªí¥Ü¹L¥h 1, 5, 15 ¤ÀÄÁªº­t¸ü */
+ sprintf(buf, "¨t²Î­t¸ü %.2f %.2f %.2f", load[0], load[1], load[2]);
+ vmsg(buf);
+ return XEASY;
+}
+
+
+int
+a_resetsys() /* ­«¸m */
+{
+ switch (vans("¡· ¨t²Î­«³] 1)°ÊºA¬ÝªO 2)¤ÀÃþ¸s²Õ 3)«ü¦W¤Î¾×«H 4)¥þ³¡ S)ync¡G[Q] "))
+ {
+ case '1':
+ system("bin/camera");
+ break;
+
+ case '2':
+ system("bin/account -nokeeplog");
+ brh_save();
+ board_main();
+ break;
+
+ case '3':
+ system("kill -1 `cat run/bmta.pid`; kill -1 `cat run/bguard.pid`");
+ break;
+
+ case '4':
+ system("kill -1 `cat run/bmta.pid`; kill -1 `cat run/bguard.pid`; bin/account -nokeeplog; bin/camera");
+ brh_save();
+ board_main();
+ break;
+
+ case 's':
+ /*
+ move(1,0), clrtobot();
+ outs("³o¶µ¥\\¯à·|­n¨D§@·~¨t²Î±N«O¯d¦b°O¾ÐÅ餤ªºÀɮץߧY¼g¤JÀÉ®×\n"
+ "¦pªG¦¹®É¦³¤j¶q¸ê®Æ¼g¤J¡A¥i¯à»Ý­n¸ûªøªº®É¶¡\n");
+ vmsg(NULL);
+ */
+ vmsg("³o¶µ¥\\¯à·|­n¨D§@·~¨t²Î±N«O¯d¦b°O¾ÐÅ餤ªºÀɮץߧY¼g¤JÀÉ®×");
+ if(fork()==0){
+ sync();
+ exit(0);
+ }else{
+ vmsg("³o­Ó¨t²Î©I¥s¤w¦b­I´º°õ¦æ");
+ }
+ return XO_HEAD;
+ }
+
+ return XEASY;
+}
+
+
+/* ----------------------------------------------------- */
+/* ÁÙ­ì³Æ¥÷ÀÉ */
+/* ----------------------------------------------------- */
+
+
+static void
+show_availability(type) /* ±N BAKPATH ¸Ì­±©Ò¦³¥i¨ú¦^³Æ¥÷ªº¥Ø¿ý¦L¥X¨Ó */
+ char *type;
+{
+ int tlen, len, col;
+ char *fname, fpath[64];
+ struct dirent *de;
+ DIR *dirp;
+ FILE *fp;
+
+ if (dirp = opendir(BAKPATH))
+ {
+ col = 0;
+ tlen = strlen(type);
+
+ sprintf(fpath, "tmp/restore.%s", cuser.userid);
+ fp = fopen(fpath, "w");
+ fputs("¡° ¥i¨Ñ¨ú¦^ªº³Æ¥÷¦³¡G\n\n", fp);
+
+ while (de = readdir(dirp))
+ {
+ fname = de->d_name;
+ if (!strncmp(fname, type, tlen))
+ {
+ len = strlen(fname) + 2;
+ if (SCR_WIDTH - col < len)
+ {
+ fputc('\n', fp);
+ col = 0;
+ }
+ else
+ {
+ col += len;
+ }
+ fprintf(fp, "%s ", fname);
+ }
+ }
+
+ fputc('\n', fp);
+ fclose(fp);
+ closedir(dirp);
+
+ more(fpath, (char *) -1);
+ unlink(fpath);
+ }
+}
+
+
+int
+a_restore()
+{
+ int ch;
+ char *type, *ptr;
+ char *tpool[3] = {"brd", "gem", "usr"};
+ char date[20], brdname[BNLEN + 1], src[64], cmd[256];
+ ACCT acct;
+ BPAL *bpal;
+
+ ch = vans("¡· ÁÙ­ì³Æ¥÷ 1)¬ÝªO 2)ºëµØ°Ï 3)¨Ï¥ÎªÌ¡G[Q] ") - '1';
+ if (ch < 0 || ch >= 3)
+ return XEASY;
+
+ type = tpool[ch];
+ show_availability(type);
+
+ if (vget(b_lines, 0, "­n¨ú¦^ªº³Æ¥÷¥Ø¿ý¡G", date, 20, DOECHO))
+ {
+ /* Á×§K¯¸ªø¥´¤F¤@­Ó¦s¦bªº¥Ø¿ý¡A¦ý¬O©M type ¤£¦X */
+ if (strncmp(date, type, strlen(type)))
+ return 0;
+
+ sprintf(src, BAKPATH"/%s", date);
+ if (!dashd(src))
+ return 0;
+ ptr = strchr(src, '\0');
+
+ clear();
+ move(3, 0);
+ outs("±ýÁÙ­ì³Æ¥÷ªº¬ÝªO/¨Ï¥ÎªÌ¥²¶·¤w¦s¦b¡C\n"
+ "­Y¸Ó¬ÝªO/¨Ï¥ÎªÌ¤w§R°£¡A½Ð¥ý­«·s¶}³]/µù¥U¤@­Ó¦P¦Wªº¬ÝªO/¨Ï¥ÎªÌ¡C\n"
+ "ÁÙ­ì³Æ¥÷®É½Ð½T»{¸Ó¬ÝªOµL¤H¨Ï¥Î/¨Ï¥ÎªÌ¤£¦b½u¤W");
+
+ if (ch == 0 || ch == 1)
+ {
+ if (!ask_board(brdname, BRD_L_BIT, NULL))
+ return 0;
+ sprintf(ptr, "/%s%s.tgz", ch == 0 ? "" : "brd/", brdname);
+ }
+ else /* if (ch == 2) */
+ {
+ if (acct_get(msg_uid, &acct) <= 0)
+ return 0;
+ type = acct.userid;
+ str_lower(type, type);
+ sprintf(ptr, "/%c/%s.tgz", *type, type);
+ }
+
+ if (!dashf(src))
+ {
+ /* Àɮפ£¦s¦b¡A³q±`¬O¦]¬°³Æ¥÷ÂI®É¸Ó¬ÝªO/¨Ï¥ÎªÌ¤w³Q§R°£¡A©Î¬O·í®É®Ú¥»´NÁÙ¨S¦³¸Ó¬ÝªO/¨Ï¥ÎªÌ */
+ vmsg("³Æ¥÷Àɮפ£¦s¦b¡A½Ð¸Õ¸Õ¨ä¥L®É¶¡ÂIªº³Æ¥÷");
+ return 0;
+ }
+
+ if (vans("ÁÙ­ì³Æ¥÷«á¡A¥Ø«e©Ò¦³¸ê®Æ³£·|¬y¥¢¡A½Ð°È¥²½T©w(Y/N)¡H[N] ") != 'y')
+ return 0;
+
+ alog("ÁÙ­ì³Æ¥÷", src);
+
+ /* ¸ÑÀ£ÁY */
+ if (ch == 0)
+ ptr = "brd";
+ else if (ch == 1)
+ ptr = "gem/brd";
+ else /* if (ch == 2) */
+ sprintf(ptr = date, "usr/%c", *type);
+ sprintf(cmd, "tar xfz %s -C %s/", src, ptr);
+ /* system(cmd); */
+
+#if 1 /* Åý¯¸ªø¤â°Ê°õ¦æ */
+ move(7, 0);
+ outs("\n½Ð¥H bbs ¨­¤Àµn¤J¤u§@¯¸¡A¨Ã©ó\033[1;36m®a¥Ø¿ý\033[m°õ¦æ\n\n\033[1;33m");
+ outs(cmd);
+ outs("\033[m\n\n");
+#endif
+
+ /* tar §¹¥H«á¡AÁÙ­n°µªº¨Æ */
+ if (vans("¡· «ü¥O Y)¤w¦¨¥\\°õ¦æ¥H¤W«ü¥O Q)©ñ±ó°õ¦æ¡G[Q] ") == 'y')
+ {
+ if (ch == 0) /* ÁÙ­ì¬ÝªO®É¡A­n§ó·sªO¤Í */
+ {
+ if ((ch = brd_bno(brdname)) >= 0)
+ {
+ brd_fpath(src, brdname, fn_pal);
+ bpal = bshm->pcache + ch;
+ bpal->pal_max = image_pal(src, bpal->pal_spool);
+ }
+ }
+ else if (ch == 2) /* ÁÙ­ì¨Ï¥ÎªÌ®É¡A¤£ÁÙ­ì userno */
+ {
+ ch = acct.userno;
+ if (acct_load(&acct, type) >= 0)
+ {
+ acct.userno = ch;
+ acct_save(&acct);
+ }
+ }
+ vmsg("ÁÙ­ì³Æ¥÷¦¨¥\\");
+ return 0;
+ }
+ }
+
+ vmsg(msg_cancel);
+ return 0;
+}
+
+
+#ifdef HAVE_REGISTER_FORM
+
+/* ----------------------------------------------------- */
+/* ³B²z Register Form */
+/* ----------------------------------------------------- */
+
+
+static int
+scan_register_form(fd)
+ int fd;
+{
+ static char logfile[] = FN_RUN_RFORM_LOG;
+ static char *reason[] =
+ {
+ "¿é¤J¯u¹ê©m¦W", "¸Ô¹ê¶ñ¼g¥Ó½Ðªí", "¸Ô¶ñ¦í§}¸ê®Æ", "¸Ô¶ñ³sµ¸¹q¸Ü",
+ "¸Ô¶ñªA°È³æ¦ì¡B©Î¾Ç®Õ¨t¯Å", "¥Î¤¤¤å¶ñ¼g¥Ó½Ð³æ",
+#ifdef EMAIL_JUSTIFY /* waynesan.040327: ¦³ E-mail »{ÃÒ¤~¦³¦¹¶µ */
+ "±Ä¥Î E-mail »{ÃÒ",
+#endif
+ NULL
+ };
+
+ ACCT acct;
+ RFORM rform;
+ HDR hdr;
+ FILE *fout;
+
+ int op, n;
+ char buf[256], *agent, *userid, *str;
+ char folder[64], fpath[64];
+
+ vs_bar("¼f®Ö¨Ï¥ÎªÌµù¥U¸ê®Æ");
+ agent = cuser.userid;
+
+ while (read(fd, &rform, sizeof(RFORM)) == sizeof(RFORM))
+ {
+ userid = rform.userid;
+ move(2, 0);
+ prints("¥Ó½Ð¥N¸¹: %s (¥Ó½Ð®É¶¡¡G%s)\n", userid, Btime(&rform.rtime));
+ prints("ªA°È³æ¦ì: %s\n", rform.career);
+ prints("¥Ø«e¦í§}: %s\n", rform.address);
+ prints("³sµ¸¹q¸Ü: %s\n%s\n", rform.phone, msg_seperator);
+ clrtobot();
+
+ if ((acct_load(&acct, userid) < 0) || (acct.userno != rform.userno))
+ {
+ vmsg("¬dµL¦¹¤H");
+ op = 'd';
+ }
+ else
+ {
+ acct_show(&acct, 2);
+
+#ifdef JUSTIFY_PERIODICAL
+ if (acct.userlevel & PERM_VALID && acct.tvalid + VALID_PERIOD - INVALID_NOTICE_PERIOD >= acct.lastlogin)
+#else
+ if (acct.userlevel & PERM_VALID)
+#endif
+ {
+ vmsg("¦¹±b¸¹¤w¸g§¹¦¨µù¥U");
+ op = 'd';
+ }
+ else if (acct.userlevel & PERM_ALLDENY)
+ {
+ /* itoc.050405: ¤£¯àÅý°±ÅvªÌ­«·s»{ÃÒ¡A¦]¬°·|§ï±¼¥Lªº tvalid (°±Åv¨ì´Á®É¶¡) */
+ vmsg("¦¹±b¸¹¥Ø«e³Q°±Åv¤¤");
+ op = 'd';
+ }
+ else
+ {
+ op = vans("¬O§_±µ¨ü(Y/N/Q/Del/Skip)¡H[S] ");
+ }
+ }
+
+ switch (op)
+ {
+ case 'y':
+
+ /* ´£¤ÉÅv­­ */
+ sprintf(buf, "REG: %s:%s:%s:by %s", rform.phone, rform.career, rform.address, agent);
+ justify_log(acct.userid, buf);
+ time(&acct.tvalid);
+ /* itoc.041025: ³o­Ó acct_setperm() ¨Ã¨S¦³ºò¸ò¦b acct_load() «á­±¡A¤¤¶¡¹j¤F¤@­Ó vans()¡A
+ ³o¥i¯à³y¦¨®³Â acct ¥hÂл\·s .ACCT ªº°ÝÃD¡C¤£¹L¦]¬°¬O¯¸ªø¤~¦³ªºÅv­­¡A©Ò¥H´N¤£§ï¤F */
+ acct_setperm(&acct, PERM_VALID, 0);
+
+ /* ±H«H³qª¾¨Ï¥ÎªÌ */
+ usr_fpath(folder, userid, fn_dir);
+ hdr_stamp(folder, HDR_LINK, &hdr, FN_ETC_JUSTIFIED);
+ strcpy(hdr.title, msg_reg_valid);
+ strcpy(hdr.owner, str_sysop);
+ rec_add(folder, &hdr, sizeof(HDR));
+
+ strcpy(rform.agent, agent);
+ rec_add(logfile, &rform, sizeof(RFORM));
+
+ m_biff(rform.userno);
+ alog("µù¥U¦¨¥\\",userid);
+
+ break;
+
+ case 'q': /* ¤Ó²Ö¤F¡Aµ²§ô¥ð®§ */
+
+ do
+ {
+ rec_add(FN_RUN_RFORM, &rform, sizeof(RFORM));
+ } while (read(fd, &rform, sizeof(RFORM)) == sizeof(RFORM));
+
+ case 'd':
+ break;
+
+ case 'n':
+
+ move(9, 0);
+ prints("½Ð´£¥X°h¦^¥Ó½Ðªí­ì¦]¡A©Î±q¥H¤U²M³æ¿ï¨ú¤@¶µ¡A¥¼¿é¤Jª½±µ«ö <enter> «h·|¨ú®ø\n\n");
+ for (n = 0; str = reason[n]; n++)
+ prints("%d) ½Ð%s\n", n, str);
+ clrtobot();
+
+ if (op = vget(b_lines, 0, "°h¦^­ì¦]¡G", buf, 60, DOECHO))
+ {
+ int i;
+
+ i = op - '0';
+ if (i >= 0 && i < n)
+ strcpy(buf, reason[i]);
+
+ usr_fpath(folder, acct.userid, fn_dir);
+ if (fout = fdopen(hdr_stamp(folder, 0, &hdr, fpath), "w"))
+ {
+ fprintf(fout, "\t¥Ñ©ó±z´£¨Ñªº¸ê®Æ¤£°÷¸Ô¹ê¡AµLªk½T»{¨­¤À¡A"
+ "\n\n\t½Ð­«·s¶ñ¼gµù¥Uªí³æ¡G%s¡C\n", buf);
+ fclose(fout);
+
+ strcpy(hdr.owner, agent);
+ strcpy(hdr.title, "[°h¥ó] ½Ð±z­«·s¶ñ¼gµù¥Uªí³æ");
+ rec_add(folder, &hdr, sizeof(HDR));
+ }
+
+ strcpy(rform.reply, buf); /* ²z¥Ñ */
+ strcpy(rform.agent, agent);
+ rec_add(logfile, &rform, sizeof(RFORM));
+
+ char buf2[256];
+ sprintf(buf2,"%-13s%s",userid,buf);
+ alog("µù¥U°h¥ó",buf2);
+
+ break;
+ }
+
+ default: /* put back to regfile */
+
+ rec_add(FN_RUN_RFORM, &rform, sizeof(RFORM));
+ }
+ }
+}
+
+
+int
+a_register()
+{
+ int num;
+ char buf[80];
+
+ num = rec_num(FN_RUN_RFORM, sizeof(RFORM));
+ if (num <= 0)
+ {
+ zmsg("¥Ø«e¨ÃµL·sµù¥U¸ê®Æ");
+ return XEASY;
+ }
+
+ sprintf(buf, "¦@¦³ %d µ§¸ê®Æ¡A¶}©l¼f®Ö¶Ü(Y/N)¡H[N] ", num);
+ num = XEASY;
+
+ if (vans(buf) == 'y')
+ {
+ sprintf(buf, "%s.tmp", FN_RUN_RFORM);
+ if (dashf(buf))
+ {
+ vmsg("¨ä¥L SYSOP ¤]¦b¼f®Öµù¥U¥Ó½Ð³æ");
+ }
+ else
+ {
+ int fd;
+
+ rename(FN_RUN_RFORM, buf);
+ fd = open(buf, O_RDONLY);
+ if (fd >= 0)
+ {
+ scan_register_form(fd);
+ close(fd);
+ unlink(buf);
+ num = 0;
+ }
+ else
+ {
+ vmsg("µLªk¶}±Òµù¥U¸ê®Æ¤u§@ÀÉ");
+ }
+ }
+ }
+ return num;
+}
+
+
+int
+a_regmerge() /* itoc.000516: Â_½u®Éµù¥U³æ­×´_ */
+{
+ char fpath[64];
+ FILE *fp;
+
+ sprintf(fpath, "%s.tmp", FN_RUN_RFORM);
+ if (dashf(fpath))
+ {
+ vmsg("½Ð¥ý½T©w¤wµL¨ä¥L¯¸ªø¦b¼f®Öµù¥U³æ¡A¥H§Kµo¥ÍÄY­«·N¥~¡I");
+
+ if (vans("½T©w­n±Ò°Êµù¥U³æ­×´_¥\\¯à(Y/N)¡H[N] ") == 'y')
+ {
+ if (fp = fopen(FN_RUN_RFORM, "a"))
+ {
+ f_suck(fp, fpath);
+ fclose(fp);
+ unlink(fpath);
+ }
+ vmsg("³B²z§¹²¦¡A¥H«á½Ð¤p¤ß¡I");
+ alog("´_­ì¼f®Ö","");
+ }
+ }
+ else
+ {
+ zmsg("¥Ø«e¨ÃµL­×´_µù¥U³æ¤§¥²­n");
+ }
+ return XEASY;
+}
+#endif /* HAVE_REGISTER_FORM */
+
+
+/* ----------------------------------------------------- */
+/* ±H«Hµ¹¥þ¯¸¨Ï¥ÎªÌ/ªO¥D */
+/* ----------------------------------------------------- */
+
+
+static void
+add_to_list(list, id)
+ char *list;
+ char *id; /* ¥¼¥² end with '\0' */
+{
+ char *i;
+
+ /* ¥ýÀˬd¥ý«eªº list ¸Ì­±¬O§_¤w¸g¦³¤F¡A¥H§K­«ÂÐ¥[¤J */
+ for (i = list; *i; i += IDLEN + 1)
+ {
+ if (!strncmp(i, id, IDLEN))
+ return;
+ }
+
+ /* ­Y¤§«eªº list ¨S¦³¡A¨º»òª½±µªþ¥[¦b list ³Ì«á */
+ str_ncpy(i, id, IDLEN + 1);
+}
+
+
+static void
+make_bm_list(list)
+ char *list;
+{
+ BRD *head, *tail;
+ char *ptr, *str, buf[BMLEN + 1];
+
+ /* ¥h bshm ¤¤§ì¥X©Ò¦³ brd->BM */
+
+ head = bshm->bcache;
+ tail = head + bshm->number;
+ do /* ¦Ü¤Ö¦³ note ¤@ªO¡A¤£¥²¹ï¬ÝªO°µÀˬd */
+ {
+ ptr = buf;
+ strcpy(ptr, head->BM);
+
+ while (*ptr) /* §â brd->BM ¤¤ bm1/bm2/bm3/... ¦U­Ó bm §ì¥X¨Ó */
+ {
+ if (str = strchr(ptr, '/'))
+ *str = '\0';
+ add_to_list(list, ptr);
+ if (!str)
+ break;
+ ptr = str + 1;
+ }
+ } while (++head < tail);
+}
+
+
+static void
+make_all_list(list)
+ char *list;
+{
+ int fd;
+ SCHEMA schema;
+
+ if ((fd = open(FN_SCHEMA, O_RDONLY)) < 0)
+ return;
+
+ while (read(fd, &schema, sizeof(SCHEMA)) == sizeof(SCHEMA))
+ add_to_list(list, schema.userid);
+
+ close(fd);
+}
+
+
+static void
+send_list(title, fpath, list)
+ char *title; /* «H¥óªº¼ÐÃD */
+ char *fpath; /* «H¥óªºÀÉ®× */
+ char *list; /* ±H«Hªº¦W³æ */
+{
+ char folder[64], *ptr;
+ HDR mhdr;
+
+ for (ptr = list; *ptr; ptr += IDLEN + 1)
+ {
+ usr_fpath(folder, ptr, fn_dir);
+ if (hdr_stamp(folder, HDR_LINK, &mhdr, fpath) >= 0)
+ {
+ strcpy(mhdr.owner, str_sysop);
+ strcpy(mhdr.title, title);
+ mhdr.xmode = 0;
+ rec_add(folder, &mhdr, sizeof(HDR));
+ }
+ }
+}
+
+
+static void
+biff_bm()
+{
+ UTMP *utmp, *uceil;
+
+ utmp = ushm->uslot;
+ uceil = (void *) utmp + ushm->offset;
+ do
+ {
+ if (utmp->pid && (utmp->userlevel & PERM_BM))
+ utmp->status |= STATUS_BIFF;
+ } while (++utmp <= uceil);
+}
+
+
+static void
+biff_all()
+{
+ UTMP *utmp, *uceil;
+
+ utmp = ushm->uslot;
+ uceil = (void *) utmp + ushm->offset;
+ do
+ {
+ if (utmp->pid)
+ utmp->status |= STATUS_BIFF;
+ } while (++utmp <= uceil);
+}
+
+
+int
+m_bm()
+{
+ char *list, fpath[64];
+ FILE *fp;
+ int size;
+
+ if (vans("­n±H«Hµ¹¥þ¯¸©Ò¦³ªO¥D(Y/N)¡H[N] ") != 'y')
+ return XEASY;
+
+ strcpy(ve_title, "[ªO¥D³q§i] ");
+ if (!vget(1, 0, "¼ÐÃD¡G", ve_title, TTLEN + 1, GCARRY))
+ return 0;
+
+ usr_fpath(fpath, cuser.userid, "sysmail");
+ if (fp = fopen(fpath, "w"))
+ {
+ fprintf(fp, "¡° [ªO¥D³q§i] ¯¸ªø³q§i¡A¦¬«H¤H¡G¦UªO¥D\n");
+ fprintf(fp, "-------------------------------------------------------------------------\n");
+ fclose(fp);
+ }
+
+ curredit = EDIT_MAIL;
+ *quote_file = '\0';
+ if (vedit(fpath, 1) >= 0)
+ {
+ vmsg("»Ý­n¤@¬qÆZªøªº®É¶¡¡A½Ð­@¤ßµ¥«Ý");
+
+ size = (IDLEN + 1) * MAXBOARD * 4; /* °²³]¨CªO¥|­ÓªO¥D¤w¨¬°÷ */
+ if (list = (char *) malloc(size))
+ {
+ memset(list, 0, size);
+
+ make_bm_list(list);
+ send_list(ve_title, fpath, list);
+
+ free(list);
+ biff_bm();
+ }
+ }
+ else
+ {
+ vmsg(msg_cancel);
+ }
+
+ unlink(fpath);
+
+ return 0;
+}
+
+
+int
+m_all()
+{
+ char *list, fpath[64];
+ FILE *fp;
+ int size;
+
+ if (vans("­n±H«Hµ¹¥þ¯¸¨Ï¥ÎªÌ(Y/N)¡H[N] ") != 'y')
+ return XEASY;
+
+ strcpy(ve_title, "[¨t²Î³q§i] ");
+ if (!vget(1, 0, "¼ÐÃD¡G", ve_title, TTLEN + 1, GCARRY))
+ return 0;
+
+ usr_fpath(fpath, cuser.userid, "sysmail");
+ if (fp = fopen(fpath, "w"))
+ {
+ fprintf(fp, "¡° [¨t²Î³q§i] ¯¸ªø³q§i¡A¦¬«H¤H¡G¥þ¯¸¨Ï¥ÎªÌ\n");
+ fprintf(fp, "-------------------------------------------------------------------------\n");
+ fclose(fp);
+ }
+
+ curredit = EDIT_MAIL;
+ *quote_file = '\0';
+ if (vedit(fpath, 1) >= 0)
+ {
+ vmsg("»Ý­n¤@¬qÆZªøªº®É¶¡¡A½Ð­@¤ßµ¥«Ý");
+
+ size = (IDLEN + 1) * rec_num(FN_SCHEMA, sizeof(SCHEMA));
+ if (list = (char *) malloc(size))
+ {
+ memset(list, 0, size);
+
+ make_all_list(list);
+ send_list(ve_title, fpath, list);
+
+ free(list);
+ biff_all();
+ }
+ }
+ else
+ {
+ vmsg(msg_cancel);
+ }
+
+ unlink(fpath);
+
+ return 0;
+}
diff --git a/so/aloha.c b/so/aloha.c
new file mode 100644
index 0000000..fad0d6b
--- /dev/null
+++ b/so/aloha.c
@@ -0,0 +1,445 @@
+/*-------------------------------------------------------*/
+/* aloha.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : aloha routines */
+/* create : 95/03/29 */
+/* update : 00/01/02 */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+#ifdef HAVE_ALOHA
+
+extern XZ xz[];
+extern char xo_pool[];
+
+
+/* ----------------------------------------------------- */
+/* ¤W¯¸³qª¾¦W³æ */
+/* ----------------------------------------------------- */
+
+
+static int
+cmpfrienz(frienz)
+ FRIENZ *frienz;
+{
+ return frienz->userno == cuser.userno;
+}
+
+
+static void
+delbenz(xo, aloha)
+ XO *xo;
+ ALOHA *aloha;
+{
+ char fpath[64];
+
+ usr_fpath(fpath, aloha->userid, FN_FRIENZ);
+ while (!rec_del(fpath, sizeof(FRIENZ), 0, cmpfrienz))
+ ;
+}
+
+
+static int
+aloha_find(fpath, userno)
+ char *fpath;
+ int userno;
+{
+ ALOHA old;
+ int fd;
+ int rc = 0;
+
+ if ((fd = open(fpath, O_RDONLY)) >= 0)
+ {
+ while (read(fd, &old, sizeof(ALOHA)) == sizeof(ALOHA))
+ {
+ if (userno == old.userno)
+ {
+ rc = 1;
+ break;
+ }
+ }
+ close(fd);
+ }
+ return rc;
+}
+
+
+static int
+chkaloha(aloha)
+ ALOHA *aloha;
+{
+ int userno;
+
+ userno = aloha->userno;
+ return (userno > 0 && userno == acct_userno(aloha->userid));
+}
+
+
+static void
+aloha_sync(fpath)
+ char *fpath;
+{
+ int fsize;
+
+ outz(MSG_CHKDATA);
+ refresh();
+
+ fsize = rec_sync(fpath, sizeof(ALOHA), str_cmp, chkaloha);
+
+ if (fsize > ALOHA_MAX * sizeof(ALOHA))
+ vmsg(msg_list_over);
+}
+
+
+/* ----------------------------------------------------- */
+/* ¤W¯¸³qª¾¦W³æ¡G¿ï³æ¦¡¾Þ§@¬É­±´y­z */
+/* ----------------------------------------------------- */
+
+
+static int aloha_add();
+
+
+static void
+aloha_item(num, aloha)
+ int num;
+ ALOHA *aloha;
+{
+#ifdef CHECK_ONLINE
+ UTMP *online = utmp_get(aloha->userno, NULL);
+
+ prints("%6d%c %s%-14s%s\n", num, tag_char(aloha->userno),
+ online ? COLOR7 : "", aloha->userid, online ? str_ransi : "");
+#else
+ prints("%6d%c %-14s\n", num, tag_char(aloha->userno), aloha->userid);
+#endif
+}
+
+
+static int
+aloha_body(xo)
+ XO *xo;
+{
+ ALOHA *aloha;
+ int num, max, tail;
+
+ max = xo->max;
+ if (max <= 0)
+ {
+ if (vans("­n·s¼W¦W³æ¶Ü(Y/N)¡H[N] ") == 'y')
+ return aloha_add(xo);
+ return XO_QUIT;
+ }
+
+ aloha = (ALOHA *) xo_pool;
+ num = xo->top;
+ tail = num + XO_TALL;
+ if (max > tail)
+ max = tail;
+
+ move(3, 0);
+ do
+ {
+ aloha_item(++num, aloha++);
+ } while (num < max);
+ clrtobot();
+
+ /* return XO_NONE; */
+ return XO_FOOT; /* itoc.010403: §â b_lines ¶ñ¤W feeter */
+}
+
+
+static int
+aloha_head(xo)
+ XO *xo;
+{
+ vs_head("¤W¯¸³qª¾", str_site);
+ prints(NECKER_ALOHA, d_cols, "");
+ return aloha_body(xo);
+}
+
+
+static int
+aloha_load(xo)
+ XO *xo;
+{
+ xo_load(xo, sizeof(ALOHA));
+ return aloha_body(xo);
+}
+
+
+static int
+aloha_init(xo)
+ XO *xo;
+{
+ xo_load(xo, sizeof(ALOHA));
+ return aloha_head(xo);
+}
+
+
+static int
+aloha_loadpal(xo)
+ XO *xo;
+{
+ int fd, quota;
+ char fpath[64];
+ FRIENZ frienz;
+ PAL pal;
+ ALOHA aloha;
+
+ if (vans("­n¤Þ¤J¦n¤Í¦W³æ¶Ü(Y/N)¡H[N] ") == 'y')
+ {
+ usr_fpath(fpath, cuser.userid, FN_PAL);
+ if ((fd = open(fpath, O_RDONLY)) >= 0)
+ {
+ /* itoc.001224: ¤Þ¤J¦W³æ¥u¥[¨ì ALOHA_MAX */
+ quota = ALOHA_MAX - xo->max;
+
+ memset(&frienz, 0, sizeof(FRIENZ));
+ frienz.userno = cuser.userno;
+ strcpy(frienz.userid, cuser.userid);
+
+ while (read(fd, &pal, sizeof(PAL)) == sizeof(PAL))
+ {
+ if (!aloha_find(xo->dir, pal.userno))
+ {
+ memset(&aloha, 0, sizeof(ALOHA));
+ aloha.userno = pal.userno;
+ strcpy(aloha.userid, pal.userid);
+ rec_add(xo->dir, &aloha, sizeof(ALOHA));
+
+ usr_fpath(fpath, aloha.userid, FN_FRIENZ);
+ rec_add(fpath, &frienz, sizeof(FRIENZ));
+
+ /* ¥u­n¦³¤Þ¤J¥ô¦ó¤@¤H¡A´N§â´å¼Ð©ñ¦b³Ì«á */
+ xo->pos = XO_TAIL;
+
+ if (quota < 0)
+ break;
+ }
+ }
+ close(fd);
+ return aloha_load(xo);
+ }
+ }
+ return XO_FOOT;
+}
+
+
+static int
+aloha_add(xo)
+ XO *xo;
+{
+ int userno;
+ char fpath[64];
+ FRIENZ frienz;
+ ACCT acct;
+ ALOHA aloha;
+
+ if (xo->max >= ALOHA_MAX)
+ {
+ vmsg(msg_list_over);
+ return XO_FOOT;
+ }
+
+ if ((userno = acct_get(msg_uid, &acct)) <= 0)
+ return aloha_head(xo);
+
+ if (userno == cuser.userno)
+ {
+ vmsg("¦Û¤v¤£¶·¥[¤J¤W¯¸³qª¾¦W³æ¤¤");
+ return aloha_head(xo);
+ }
+
+ if (!aloha_find(xo->dir, userno))
+ {
+ memset(&aloha, 0, sizeof(ALOHA));
+ aloha.userno = userno;
+ strcpy(aloha.userid, acct.userid);
+ rec_add(xo->dir, &aloha, sizeof(ALOHA));
+
+ memset(&frienz, 0, sizeof(FRIENZ));
+ frienz.userno = cuser.userno;
+ strcpy(frienz.userid, cuser.userid);
+ usr_fpath(fpath, aloha.userid, FN_FRIENZ);
+ rec_add(fpath, &frienz, sizeof(FRIENZ));
+ }
+ xo->pos = XO_TAIL;
+ xo_load(xo, sizeof(ALOHA));
+
+ return aloha_head(xo);
+}
+
+
+static int
+aloha_delete(xo)
+ XO *xo;
+{
+ if (vans(msg_del_ny) == 'y')
+ {
+ char fpath[64];
+ ALOHA *aloha;
+
+ aloha = (ALOHA *) xo_pool + (xo->pos - xo->top);
+
+ usr_fpath(fpath, aloha->userid, FN_FRIENZ);
+ /* itoc.030310: µù¸Ñ: ©È frienz ¸Ì­±¦³­«ÂЪº */
+ while (!rec_del(fpath, sizeof(FRIENZ), 0, cmpfrienz))
+ ;
+
+ rec_del(xo->dir, sizeof(ALOHA), xo->pos, NULL);
+ return aloha_init(xo);
+ }
+ return XO_FOOT;
+}
+
+
+static int
+aloha_rangedel(xo)
+ XO *xo;
+{
+ return xo_rangedel(xo, sizeof(ALOHA), NULL, delbenz);
+}
+
+
+static int
+vfyaloha(aloha, pos)
+ ALOHA *aloha;
+ int pos;
+{
+ return Tagger(aloha->userno, pos, TAG_NIN);
+}
+
+
+static int
+aloha_prune(xo)
+ XO *xo;
+{
+ return xo_prune(xo, sizeof(ALOHA), vfyaloha, delbenz);
+}
+
+
+static int
+aloha_mail(xo)
+ XO *xo;
+{
+ ALOHA *aloha;
+
+ aloha = (ALOHA *) xo_pool + (xo->pos - xo->top);
+ return my_send(aloha->userid);
+}
+
+
+static int
+aloha_write(xo)
+ XO *xo;
+{
+ if (HAS_PERM(PERM_PAGE))
+ {
+ ALOHA *aloha;
+ UTMP *up;
+
+ aloha = (ALOHA *) xo_pool + (xo->pos - xo->top);
+
+ if (up = utmp_find(aloha->userno))
+ do_write(up);
+ }
+ return XO_NONE;
+}
+
+
+static int
+aloha_query(xo)
+ XO *xo;
+{
+ ALOHA *aloha;
+
+ aloha = (ALOHA *) xo_pool + (xo->pos - xo->top);
+ move(1, 0);
+ clrtobot();
+
+ my_query(aloha->userid);
+ return aloha_head(xo);
+}
+
+
+static int
+aloha_sort(xo)
+ XO *xo;
+{
+ aloha_sync(xo->dir);
+ return aloha_init(xo);
+}
+
+
+static int
+aloha_tag(xo)
+ XO *xo;
+{
+ ALOHA *aloha;
+ int tag, pos, cur;
+
+ pos = xo->pos;
+ cur = pos - xo->top;
+ aloha = (ALOHA *) xo_pool + cur;
+
+ if (tag = Tagger(aloha->userno, pos, TAG_TOGGLE))
+ {
+ move(3 + cur, 6);
+ outc(tag > 0 ? '*' : ' ');
+ }
+
+ /* return XO_NONE; */
+ return xo->pos + 1 + XO_MOVE; /* lkchu.981201: ¸õ¦Ü¤U¤@¶µ */
+}
+
+
+static int
+aloha_help(xo)
+ XO *xo;
+{
+ xo_help("aloha");
+ return aloha_head(xo);
+}
+
+
+static KeyFunc aloha_cb[] =
+{
+ XO_INIT, aloha_init,
+ XO_LOAD, aloha_load,
+ XO_HEAD, aloha_head,
+ XO_BODY, aloha_body,
+
+ 'a', aloha_add,
+ 'd', aloha_delete,
+ 'm', aloha_mail,
+ 'w', aloha_write,
+ 'D', aloha_rangedel,
+ 'f', aloha_loadpal,
+
+ 'r', aloha_query,
+ Ctrl('Q'), aloha_query,
+
+ 's', aloha_sort,
+ 't', aloha_tag,
+ Ctrl('D'), aloha_prune,
+
+ 'h', aloha_help
+};
+
+
+int
+t_aloha()
+{
+ XO *xo;
+ char fpath[64];
+
+ usr_fpath(fpath, cuser.userid, FN_ALOHA);
+ xz[XZ_ALOHA - XO_ZONE].xo = xo = xo_new(fpath);
+ xz[XZ_ALOHA - XO_ZONE].cb = aloha_cb;
+ xover(XZ_ALOHA);
+ free(xo);
+ return 0;
+}
+#endif /* HAVE_ALOHA */
diff --git a/so/bank.c b/so/bank.c
new file mode 100644
index 0000000..0b4a472
--- /dev/null
+++ b/so/bank.c
@@ -0,0 +1,521 @@
+/*-------------------------------------------------------*/
+/* bank.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : »È¦æ¡BÁʶRÅv­­¥\¯à */
+/* create : 01/07/16 */
+/* update : / / */
+/* author : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+#ifdef HAVE_BUY
+
+static void
+x_give()
+{
+ int way, dollar;
+ char userid[IDLEN + 1], buf[80];
+ char folder[64], fpath[64], reason[40];
+ HDR hdr;
+ FILE *fp;
+ time_t now;
+ PAYCHECK paycheck;
+
+ if (!vget(13, 0, "±z­n§â¿úÂ൹½Ö©O¡H", userid, IDLEN + 1, DOECHO))
+ if (!vget(13, 0, "±z­n§â¿úÂ൹½Ö©O¡H", userid, IDLEN + 1, DOECHO) ||
+ !str_cmp(userid, STR_GUEST))
+ return;
+
+ if (acct_userno(userid) <= 0)
+ {
+ vmsg(err_uid);
+ return;
+ }
+
+ way = vget(15, 0, "Âà±b 1)Âà»È¹ô 2)Âàª÷¹ô¡G", buf, 3, DOECHO) - '1';
+ if (way < 0 || way > 1)
+ return;
+
+ do
+ {
+ if (!vget(17, 0, "­nÂà¦h¤Ö¿ú¹L¥h¡H", buf, 9, DOECHO)) /* ³Ì¦hÂà 99999999 Á×§K·¸¦ì */
+ return;
+
+ dollar = atoi(buf);
+
+ if (!way)
+ {
+ if (dollar > cuser.money)
+ dollar = cuser.money; /* ¥þÂà¹L¥h */
+ }
+ else
+ {
+ if (dollar > cuser.gold)
+ dollar = cuser.gold; /* ¥þÂà¹L¥h */
+ }
+ } while (dollar <= 1); /* ¤£¯à¥uÂà 1¡A·|¥þÅܤâÄò¶O */
+
+ if (!vget(19, 0, "½Ð¿é¤J²z¥Ñ¡G", reason, 40, DOECHO))
+ strcpy(reason, "¿ú¤Ó¦h");
+
+ sprintf(buf, "¬O§_­nÂà±bµ¹ %s %s¹ô %d (Y/N)¡H[N] ", userid, !way ? "»È" : "ª÷", dollar);
+ if (vget(21, 0, buf, fpath, 3, LCECHO) == 'y')
+ {
+ if (!way)
+ cuser.money -= dollar;
+ else
+ cuser.gold -= dollar;
+
+ dollar -= dollar / 10 + ((dollar % 10) ? 1 : 0); /* 10% ¤âÄò¶O */
+
+ /* itoc.020831: ¥[¤J¶×¿ú°O¿ý */
+ time(&now);
+ sprintf(buf, "%-13sÂ൹ %-13s­p %d %s (%s)\n",
+ cuser.userid, userid, dollar, !way ? "»È" : "ª÷", Btime(&now));
+ f_cat(FN_RUN_BANK_LOG, buf);
+
+ usr_fpath(folder, userid, fn_dir);
+ if (fp = fdopen(hdr_stamp(folder, 0, &hdr, fpath), "w"))
+ {
+ fprintf(fp, "%s %s (%s)\n¼ÐÃD: Âà±b³qª¾\n®É¶¡: %s\n\n",
+ str_author1, cuser.userid, cuser.username, Btime(&now));
+ fprintf(fp, "%s\n¥Lªº²z¥Ñ¬O¡G%s\n\n½Ð±z¦Üª÷¿Ä¤¤¤ß±N¤ä²¼§I²{", buf, reason);
+ fclose(fp);
+
+ strcpy(hdr.title, "Âà±b³qª¾");
+ strcpy(hdr.owner, cuser.userid);
+ rec_add(folder, &hdr, sizeof(HDR));
+ }
+
+ memset(&paycheck, 0, sizeof(PAYCHECK));
+ time(&paycheck.tissue);
+ if (!way)
+ paycheck.money = dollar;
+ else
+ paycheck.gold = dollar;
+ sprintf(paycheck.reason, "[Âà±b] %s", cuser.userid);
+ usr_fpath(fpath, userid, FN_PAYCHECK);
+ rec_add(fpath, &paycheck, sizeof(PAYCHECK));
+
+ sprintf(buf, "±z¨­¤W¦³»È¹ô %d ¤¸¡Aª÷¹ô %d ¤¸", cuser.money, cuser.gold);
+ vmsg(buf);
+ }
+ else
+ {
+ vmsg("¨ú®ø¥æ©ö");
+ }
+}
+
+
+#define GOLD2MONEY 900000 /* ª÷¹ô¡÷»È¹ô ¶×²v */
+#define MONEY2GOLD 1100000 /* »È¹ô¡÷ª÷¹ô ¶×²v */
+
+static void
+x_exchange()
+{
+ int way, gold, money;
+ char buf[80], ans[8];
+
+ move(13, 0);
+ prints("»È¹ô¡÷ª÷¹ô = %d¡G1 ª÷¹ô¡÷»È¹ô = 1¡G%d", MONEY2GOLD, GOLD2MONEY);
+
+ way = vget(15, 0, "¶×§I 1)»È¹ô¡÷ª÷¹ô 2)ª÷¹ô¡÷»È¹ô¡G", ans, 3, DOECHO) - '1';
+
+ if (!way)
+ money = cuser.money / MONEY2GOLD;
+ else if (way == 1)
+ money = cuser.gold;
+ else
+ return;
+
+ if (!way)
+ sprintf(buf, "±z­n±N»È¹ô§I´«¦¨¦h¤Ö­Óª÷¹ô©O¡H[1 - %d] ", money);
+ else
+ sprintf(buf, "±z­n§I´«¦h¤Ö­Óª÷¹ô¦¨¬°»È¹ô©O¡H[1 - %d] ", money);
+
+ if (!vget(17, 0, buf, ans, 4, DOECHO)) /* ªø«×¤ñ¸ûµu¡AÁ×§K·¸¦ì */
+ return;
+
+ gold = atoi(ans);
+ if (gold <= 0 || gold > money)
+ return;
+
+ if (!way)
+ {
+ if (gold > (INT_MAX - cuser.gold))
+ {
+ vmsg("±z´«¤Ó¦h¿úÅo¡ã·|·¸¦ìªº¡I");
+ return;
+ }
+ money = gold * MONEY2GOLD;
+ sprintf(buf, "¬O§_­n§I´«»È¹ô %d ¤¸ ¬°ª÷¹ô %d (Y/N)¡H[N] ", money, gold);
+ }
+ else
+ {
+ money = gold * GOLD2MONEY;
+ if (money > (INT_MAX - cuser.money))
+ {
+ vmsg("±z´«¤Ó¦h¿úÅo¡ã·|·¸¦ìªº¡I");
+ return;
+ }
+ sprintf(buf, "¬O§_­n§I´«ª÷¹ô %d ¤¸ ¬°»È¹ô %d (Y/N)¡H[N] ", gold, money);
+ }
+
+ if (vget(19, 0, buf, ans, 3, LCECHO) == 'y')
+ {
+ if (!way)
+ {
+ cuser.money -= money;
+ addgold(gold);
+ }
+ else
+ {
+ cuser.gold -= gold;
+ addmoney(money);
+ }
+ sprintf(buf, "±z¨­¤W¦³»È¹ô %d ¤¸¡Aª÷¹ô %d ¤¸", cuser.money, cuser.gold);
+ vmsg(buf);
+ }
+ else
+ {
+ vmsg("¨ú®ø¥æ©ö");
+ }
+}
+
+
+static void
+x_cash()
+{
+ int fd, money, gold;
+ char fpath[64], buf[64];
+ FILE *fp;
+ PAYCHECK paycheck;
+
+ usr_fpath(fpath, cuser.userid, FN_PAYCHECK);
+ if ((fd = open(fpath, O_RDONLY)) < 0)
+ {
+ vmsg("±z¥Ø«e¨S¦³¤ä²¼¥¼§I²{");
+ return;
+ }
+
+ usr_fpath(buf, cuser.userid, "cashed");
+ fp = fopen(buf, "w");
+ fputs("¥H¤U¬O±zªº¤ä²¼§I´«²M³æ¡G\n\n", fp);
+
+ money = gold = 0;
+ while (read(fd, &paycheck, sizeof(PAYCHECK)) == sizeof(PAYCHECK))
+ {
+ if (paycheck.money < (INT_MAX - money)) /* Á×§K·¸¦ì */
+ money += paycheck.money;
+ else
+ money = INT_MAX;
+ if (paycheck.gold < (INT_MAX - gold)) /* Á×§K·¸¦ì */
+ gold += paycheck.gold;
+ else
+ gold = INT_MAX;
+
+ fprintf(fp, "%s %s %d »È %d ª÷\n",
+ Btime(&paycheck.tissue), paycheck.reason, paycheck.money, paycheck.gold);
+ }
+ close(fd);
+ unlink(fpath);
+
+ fprintf(fp, "\n±z¦@§I²{ %d »È %d ª÷\n", money, gold);
+ fclose(fp);
+
+ addmoney(money);
+ addgold(gold);
+
+ more(buf, NULL);
+ unlink(buf);
+}
+
+
+int
+x_bank()
+{
+ char ans[3];
+
+ if (HAS_STATUS(STATUS_COINLOCK))
+ {
+ vmsg(msg_coinlock);
+ return XEASY;
+ }
+
+ vs_bar("«H°U»È¦æ");
+ move(2, 0);
+
+ /* itoc.011208: ¥H¨¾¸U¤@ */
+ if (cuser.money < 0)
+ cuser.money = 0;
+ if (cuser.gold < 0)
+ cuser.gold = 0;
+
+ outs("\033[1;36m ùúùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùû\n");
+ prints(" ùø\033[32m±z²{¦b¦³»È¹ô \033[33m%12d\033[32m ¤¸¡Aª÷¹ô \033[33m%12d\033[32m ¤¸\033[36m ùø\n",
+ cuser.money, cuser.gold);
+ outs(" ùàùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùâ\n"
+ " ùø ¥Ø«e»È¦æ´£¨Ñ¤U¦C´X¶µªA°È¡G ùø\n"
+ " ùø\033[33m1.\033[37m Âà±b -- Âà±bµ¹¨ä¥L¤H (©â¨ú 10% ¤âÄò¶O) \033[36m ùø\n"
+ " ùø\033[33m2.\033[37m ¶×§I -- »È¹ô/ª÷¹ô §I´« (©â¨ú 10% ¤âÄò¶O) \033[36m ùø\n"
+ " ùø\033[33m3.\033[37m §I²{ -- ¤ä²¼§I²{ \033[36m ùø\n"
+ " ùüùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùý\033[m");
+
+ vget(11, 0, "½Ð¿é¤J±z»Ý­nªºªA°È¡G", ans, 3, DOECHO);
+ if (ans[0] == '1')
+ x_give();
+ else if (ans[0] == '2')
+ x_exchange();
+ else if (ans[0] == '3')
+ x_cash();
+
+ return 0;
+}
+
+
+int
+b_invis()
+{
+ if (HAS_STATUS(STATUS_COINLOCK))
+ {
+ vmsg(msg_coinlock);
+ return XEASY;
+ }
+
+ if (cuser.ufo & UFO_CLOAK)
+ {
+ if (vans("¬O§_²{¨­(Y/N)¡H[N] ") != 'y')
+ return XEASY;
+ /* ²{¨­§K¶O */
+ }
+ else
+ {
+ if (HAS_PERM(PERM_CLOAK))
+ {
+ if (vans("¬O§_Áô§Î(Y/N)¡H[N] ") != 'y')
+ return XEASY;
+ /* ¦³µL­­Áô§ÎÅv­­ªÌ§K¶O */
+ }
+ else
+ {
+ if (cuser.gold < 10)
+ {
+ vmsg("­n 10 ª÷¹ô¤~¯àÁô§Î³á");
+ return XEASY;
+ }
+ if (vans("¬O§_ªá 10 ª÷¹ôÁô§Î(Y/N)¡H[N] ") != 'y')
+ return XEASY;
+ cuser.gold -= 10;
+ }
+ }
+
+ cuser.ufo ^= UFO_CLOAK;
+ cutmp->ufo ^= UFO_CLOAK; /* ufo ­n¦P¨B */
+
+ return XEASY;
+}
+
+
+static void
+buy_level(userlevel) /* itoc.010830: ¥u¦s level Äæ¦ì¡A¥H§KÅܰʨì¦b½u¤W§ó°Êªº»{ÃÒÄæ¦ì */
+ usint userlevel;
+{
+ if (!HAS_STATUS(STATUS_DATALOCK)) /* itoc.010811: ­n¨S¦³³Q¯¸ªøÂê©w¡A¤~¯à¼g¤J */
+ {
+ int fd;
+ char fpath[80];
+ ACCT tuser;
+
+ usr_fpath(fpath, cuser.userid, fn_acct);
+ fd = open(fpath, O_RDWR);
+ if (fd >= 0)
+ {
+ if (read(fd, &tuser, sizeof(ACCT)) == sizeof(ACCT))
+ {
+ tuser.userlevel |= userlevel;
+ lseek(fd, (off_t) 0, SEEK_SET);
+ write(fd, &tuser, sizeof(ACCT));
+ vmsg("±z¤w¸gÀò±oÅv­­¡A½Ð­«·s¤W¯¸");
+ }
+ close(fd);
+ }
+ }
+}
+
+
+int
+b_cloak()
+{
+ if (HAS_STATUS(STATUS_COINLOCK))
+ {
+ vmsg(msg_coinlock);
+ return XEASY;
+ }
+
+ if (HAS_PERM(PERM_CLOAK))
+ {
+ vmsg("±z¤w¸g¯àµL­­Áô§Î¤F");
+ }
+ else
+ {
+ if (cuser.gold < 1000)
+ {
+ vmsg("­n 1000 ª÷¹ô¤~¯àÁʶRµL­­Áô§ÎÅv­­³á");
+ }
+ else if (vans("¬O§_ªá 1000 ª÷¹ôÁʶRµL­­Áô§ÎÅv­­(Y/N)¡H[N] ") == 'y')
+ {
+ cuser.gold -= 1000;
+ buy_level(PERM_CLOAK);
+ }
+ }
+
+ return XEASY;
+}
+
+int
+b_changeid()
+{
+ char ans[3];
+
+ if (HAS_STATUS(STATUS_COINLOCK))
+ {
+ vmsg(msg_coinlock);
+ return XEASY;
+ }
+
+ if (HAS_PERM(PERM_CHANGEID))
+ {
+ vmsg("±z¤w¸g¯à§ïID¤F");
+ return XEASY;
+ }
+
+ vs_bar("ÁʶR§ó§ïIDÅv­­");
+ move(2, 0);
+
+ /* itoc.011208: ¥H¨¾¸U¤@ */
+ if (cuser.money < 0)
+ cuser.money = 0;
+ if (cuser.gold < 0)
+ cuser.gold = 0;
+
+ outs("ÁʶR¦¹Åv­­ªÌ¡A½Ð¦P·N¿í¦u¥H¤U³W«h¡G\n\n"
+ " 1.±ý§ó§ïID½Ð©ó¨Ï¥ÎªÌ¦W³æ«ö¤U ^D\n"
+ " 2.§ó§ï«áªºID ³Ì«á¤è·|¥X²{','¦r¤¸ ¥H¥Ü°Ï§O ¨Ò¦p \"chensc,\"\n"
+ " 3.¥Ñ©ó§ó§ïID«á ¨Ï¥ÎªÌ¸ê®Æ¤£·|Åã¥Ü ½Ð¤Å¥H¦¹À¸§Ë¥L¤H\n"
+ " 4.½Ð¤Å±NID§ï¬°¤£¶®¦r²´\n"
+ " 5.¦p¹H¤Ï¤W­z³W«h ¯¸°È±o¥H±j­¢Â÷½u³B¤§ ¸g¥Ó¶D±¡¸`­«¤jªÌ¨ú®øÅv­­\n\n"
+ "³Ì«áÁÙ¬O§Æ±æ¤j®a¯à°÷ª±ªº´r§Ö^^");
+
+ vget(11, 0, "Ū§¹¥H¤W³W«h¡A±z­nªá 6 ª÷¹ôÁʶR§ó§ïIDªºÅv­­¶Ü¡Hy/n [n]¡G", ans, 3, DOECHO);
+ if (ans[0] == 'y')
+ {
+ if (cuser.gold < 6)
+ {
+ vmsg("­n 6 ª÷¹ô¤~¯àÁʶR§ó§ïIDªºÅv­­³á");
+ }
+ else
+ {
+ cuser.gold -= 6;
+ cclog("BUY_CHANGEID",cuser.userid);
+ buy_level(PERM_CHANGEID);
+ }
+ }
+ return 0;
+}
+
+
+
+
+int
+b_mbox()
+{
+ if (HAS_STATUS(STATUS_COINLOCK))
+ {
+ vmsg(msg_coinlock);
+ return XEASY;
+ }
+
+ if (HAS_PERM(PERM_MBOX))
+ {
+ vmsg("±zªº«H½c¤w¸g¨S¦³¤W­­¤F");
+ }
+ else
+ {
+ if (cuser.gold < 1000)
+ {
+ vmsg("­n 1000 ª÷¹ô¤~¯àÁʶR«H½cµL­­Åv­­³á");
+ }
+ else if (vans("¬O§_ªá 1000 ª÷¹ôÁʶR«H½cµL­­Åv­­(Y/N)¡H[N] ") == 'y')
+ {
+ cuser.gold -= 1000;
+ buy_level(PERM_MBOX);
+ }
+ }
+
+ return XEASY;
+}
+
+
+int
+b_xempt()
+{
+ if (HAS_STATUS(STATUS_COINLOCK))
+ {
+ vmsg(msg_coinlock);
+ return XEASY;
+ }
+
+ if (HAS_PERM(PERM_XEMPT))
+ {
+ vmsg("±zªº±b¸¹¤w¸g¥Ã¤[«O¯d¤F");
+ }
+ else
+ {
+ if (cuser.gold < 1000)
+ {
+ vmsg("­n 1000 ª÷¹ô¤~¯àÁʶR±b¸¹¥Ã¤[«O¯dÅv­­³á");
+ }
+ else if (vans("¬O§_ªá 1000 ª÷¹ôÁʶR±b¸¹¥Ã¤[«O¯dÅv­­(Y/N)¡H[N] ") == 'y')
+ {
+ cuser.gold -= 1000;
+ buy_level(PERM_XEMPT);
+ }
+ }
+
+ return XEASY;
+}
+
+
+#if 0 /* ¤£´£¨ÑÁʶR¦Û±þ¥\¯à */
+int
+b_purge()
+{
+ if (HAS_STATUS(STATUS_COINLOCK))
+ {
+ vmsg(msg_coinlock);
+ return XEASY;
+ }
+
+ if (HAS_PERM(PERM_PURGE))
+ {
+ vmsg("¨t²Î¦b¤U¦¸©w´Á²M±b¸¹®É¡A±N²M°£¦¹ ID");
+ }
+ else
+ {
+ if (cuser.gold < 1000)
+ {
+ vmsg("­n 1000 ª÷¹ô¤~¯à¦Û±þ³á");
+ }
+ else if (vans("¬O§_ªá 1000 ª÷¹ô¦Û±þ(Y/N)¡H[N] ") == 'y')
+ {
+ cuser.gold -= 1000;
+ buy_level(PERM_PURGE);
+ }
+ }
+
+ return XEASY;
+}
+#endif
+#endif /* HAVE_BUY */
diff --git a/so/calendar.c b/so/calendar.c
new file mode 100644
index 0000000..d9fd66e
--- /dev/null
+++ b/so/calendar.c
@@ -0,0 +1,359 @@
+/*-------------------------------------------------------*/
+/* calendar.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : ¸U¦~¾ä */
+/* create : 02/08/31 */
+/* update : / / */
+/* author : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+#ifdef HAVE_CALENDAR
+
+/* $NetBSD: cal.c,v 1.10 1998/07/28 19:26:09 mycroft Exp $ */
+
+/*
+ * Copyright (c) 1989, 1993, 1994 The Regents of the University of
+ * California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by Kim Letkeman.
+ *
+ * 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. 3. All advertising
+ * materials mentioning features or use of this software must display the
+ * following acknowledgement: This product includes software developed by the
+ * University of California, Berkeley and its contributors. 4. Neither the
+ * name of the University nor the names of its contributors may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ */
+
+
+#define TM_YEAR_BASE 1900
+
+#define THURSDAY 4 /* for reformation */
+#define SATURDAY 6 /* 1 Jan 1 was a Saturday */
+
+#define FIRST_MISSING_DAY 639799 /* 3 Sep 1752 */
+#define NUMBER_MISSING_DAYS 11 /* 11 day correction */
+
+#define MAXDAYS 42 /* max slots in a month array */
+#define SPACE -1 /* used in day array */
+
+
+static int days_in_month[2][13] =
+{
+ {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
+ {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
+};
+
+static int sep1752[MAXDAYS] =
+{
+ SPACE, SPACE, 1, 2, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30,
+ SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
+ SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
+ SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
+};
+
+static int empty[MAXDAYS] =
+{
+ SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
+ SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
+ SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
+ SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
+ SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
+ SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
+};
+
+static char *month_names[12] =
+{
+ "¤@¤ë", "¤G¤ë", "¤T¤ë", "¥|¤ë", "¤­¤ë", "¤»¤ë",
+ "¤C¤ë", "¤K¤ë", "¤E¤ë", "¤Q¤ë", "¤Q¤@¤ë", "¤Q¤G¤ë"
+};
+
+static char *day_headings = "¤é ¤@ ¤G ¤T ¥| ¤­ ¤»";
+
+/* leap year -- account for gregorian reformation in 1752 */
+#define leap_year(yr) ((yr) <= 1752 ? !((yr) % 4) : (!((yr) % 4) && ((yr) % 100)) || !((yr) % 400))
+
+/* number of centuries since 1700, not inclusive */
+#define centuries_since_1700(yr) ((yr) > 1700 ? (yr) / 100 - 17 : 0)
+
+/* number of centuries since 1700 whose modulo of 400 is 0 */
+#define quad_centuries_since_1700(yr) ((yr) > 1600 ? ((yr) - 1600) / 400 : 0)
+
+/* number of leap years between year 1 and this year, not inclusive */
+#define leap_years_since_year_1(yr) ((yr) / 4 - centuries_since_1700(yr) + quad_centuries_since_1700(yr))
+
+#define DAY_LEN 3 /* 3 spaces per day */
+#define WEEK_LEN 20 /* 7 * 3 - one space at the end */
+#define HEAD_SEP 2 /* spaces between day headings */
+
+
+/*
+ * day_in_year -- return the 1 based day number within the year
+ */
+static int
+day_in_year(day, month, year)
+ int day, month, year;
+{
+ int i, leap;
+
+ leap = leap_year(year);
+ for (i = 1; i < month; i++)
+ day += days_in_month[leap][i];
+ return (day);
+}
+
+
+/*
+ * day_in_week return the 0 based day number for any date from 1 Jan. 1 to 31
+ * Dec. 9999. Assumes the Gregorian reformation eliminates 3 Sep. 1752
+ * through 13 Sep. 1752. Returns Thursday for all missing days.
+ */
+static int
+day_in_week(day, month, year)
+ int day, month, year;
+{
+ int temp;
+
+ temp = (year - 1) * 365 + leap_years_since_year_1(year - 1) + day_in_year(day, month, year);
+ if (temp < FIRST_MISSING_DAY)
+ return ((temp - 1 + SATURDAY) % 7);
+ if (temp >= (FIRST_MISSING_DAY + NUMBER_MISSING_DAYS))
+ return (((temp - 1 + SATURDAY) - NUMBER_MISSING_DAYS) % 7);
+ return (THURSDAY);
+}
+
+
+/*
+ * day_array -- Fill in an array of 42 integers with a calendar. Assume for
+ * a moment that you took the (maximum) 6 rows in a calendar and stretched
+ * them out end to end. You would have 42 numbers or spaces. This routine
+ * builds that array for any month from Jan. 1 through Dec. 9999.
+ */
+static void
+day_array(month, year, days)
+ int month, year;
+ int *days;
+{
+ int day, dw, dm;
+
+ if (month == 9 && year == 1752)
+ {
+ memmove(days, sep1752, MAXDAYS * sizeof(int));
+ return;
+ }
+ memmove(days, empty, MAXDAYS * sizeof(int));
+ dm = days_in_month[leap_year(year)][month];
+ dw = day_in_week(1, month, year);
+ day = 1;
+ while (dm--)
+ days[dw++] = day++;
+}
+
+
+static char *
+ascii_day(p, day)
+ char *p;
+ int day;
+{
+ static char *aday[] =
+ {
+ "",
+ " 1", " 2", " 3", " 4", " 5", " 6", " 7",
+ " 8", " 9", "10", "11", "12", "13", "14",
+ "15", "16", "17", "18", "19", "20", "21",
+ "22", "23", "24", "25", "26", "27", "28",
+ "29", "30", "31",
+ };
+
+ if (day == SPACE)
+ {
+ memset(p, ' ', DAY_LEN);
+ p += DAY_LEN;
+ }
+ else
+ {
+ *p++ = aday[day][0];
+ *p++ = aday[day][1];
+ *p++ = ' ';
+ }
+
+ return p;
+}
+
+
+static void
+monthly(year, month)
+ int year, month;
+{
+ int col, row, len, days[MAXDAYS];
+ char *p, buf[80];
+
+ day_array(month, year, days);
+ len = snprintf(buf, sizeof(buf), "%s %d", month_names[month - 1], year);
+
+ vs_bar("¸U¦~¤ë¾ä");
+ move(2, 5);
+ outs("­Y¥¼¿é¤J¤ë¥÷¥i¬d¸ß¦~¾ä");
+ move(4, 6);
+ prints("\033[1;35m%*s%s", (WEEK_LEN - len) / 2, "", buf);
+ move(6, 6);
+ prints("\033[1;36m%s\033[m", day_headings);
+
+ for (row = 0; row < 6; row++)
+ {
+ for (col = 0, p = buf; col < 7; col++)
+ {
+ if (col == 0) /* ¬P´Á¤é */
+ {
+ move(7 + row, 6);
+ strcpy(p, "\033[1;31m");
+ p += 7;
+ }
+ else if (col == 1) /* ¬P´Á¤@¡ã¤­ */
+ {
+ strcpy(p, "\033[37m");
+ p += 5;
+ }
+ else if (col == 6) /* ¬P´Á¤» */
+ {
+ strcpy(p, "\033[32m");
+ p += 5;
+ }
+ p = ascii_day(p, days[row * 7 + col]);
+ }
+ strcpy(p, "\033[m\n");
+ outs(buf);
+ }
+
+ vmsg(NULL);
+}
+
+
+static void
+center(fp, str, len, separate)
+ FILE *fp;
+ char *str;
+ int len;
+ int separate;
+{
+ len -= strlen(str);
+ fprintf(fp, "%*s%s%*s", len / 2, "", str, len / 2 + len % 2, "");
+ if (separate)
+ fprintf(fp, "%*s", separate, "");
+}
+
+
+static void
+yearly(fpath, year)
+ char *fpath;
+ int year;
+{
+ int col, *dp, i, month, row, which_cal;
+ int days[12][MAXDAYS];
+ char *p, buf[80];
+ FILE *fp;
+
+ /* ¦~¾ä·|¶W¹L¤@­¶¡A¥Î more() ªº */
+
+ if (fp = fopen(fpath, "w"))
+ {
+ sprintf(buf, "%d", year);
+ center(fp, buf, WEEK_LEN * 3 + HEAD_SEP * 2, 0);
+ fprintf(fp, "\n");
+ for (i = 0; i < 12; i++)
+ day_array(i + 1, year, days[i]);
+ memset(buf, ' ', sizeof(buf) - 1);
+ buf[sizeof(buf) - 1] = '\0';
+ for (month = 0; month < 12; month += 3)
+ {
+ center(fp, month_names[month], WEEK_LEN, HEAD_SEP);
+ center(fp, month_names[month + 1], WEEK_LEN, HEAD_SEP);
+ center(fp, month_names[month + 2], WEEK_LEN, 0);
+ fprintf(fp, "\n%s%*s%s%*s%s\n",
+ day_headings, HEAD_SEP, "", day_headings, HEAD_SEP, "", day_headings);
+ for (row = 0; row < 6; row++)
+ {
+ for (which_cal = 0; which_cal < 3; which_cal++)
+ {
+ p = buf + which_cal * (WEEK_LEN + 2);
+ dp = &days[month + which_cal][row * 7];
+ for (col = 0; col < 7; col++)
+ p = ascii_day(p, *dp++);
+ }
+ *p = '\0';
+ fprintf(fp, "%s\n", buf);
+ }
+ }
+ fprintf(fp, "\n");
+ fclose(fp);
+
+ more(fpath, NULL);
+ unlink(fpath);
+ }
+}
+
+
+int
+main_calendar()
+{
+ int year, month;
+ time_t now;
+ struct tm *ptime;
+ char fpath[64], ans[5];
+
+ time(&now);
+ ptime = localtime(&now);
+ year = ptime->tm_year + 1900;
+ month = ptime->tm_mon + 1;
+ sprintf(fpath, "tmp/%s.calendar", cuser.userid);
+
+ for (;;)
+ {
+ if (month) /* ¤ë¾ä */
+ monthly(year, month);
+ else /* ¦~¾ä */
+ yearly(fpath, year);
+
+ if (!vget(b_lines, 0, "½Ð¿é¤J­n¬d¸ßªº¦~¥÷¡G", ans, 5, DOECHO))
+ return 0;
+ year = atoi(ans);
+ if (year < 1 || year > 9999)
+ return 0;
+
+ if (!vget(b_lines, 0, "½Ð¿é¤J­n¬d¸ßªº¤ë¥÷¡G", ans, 3, DOECHO))
+ {
+ month = 0;
+ }
+ else
+ {
+ month = atoi(ans);
+ if (month < 1 || month > 12)
+ return 0;
+ }
+ }
+}
+#endif /* HAVE_CALENDAR */
diff --git a/so/chat.c b/so/chat.c
new file mode 100644
index 0000000..db833e2
--- /dev/null
+++ b/so/chat.c
@@ -0,0 +1,827 @@
+/*-------------------------------------------------------*/
+/* chat.c ( NTHU CS MapleBBS Ver 2.36 ) */
+/*-------------------------------------------------------*/
+/* target : chat client for xchatd */
+/* create : 95/03/29 */
+/* update : 95/12/15 */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+
+static char chatroom[IDLEN]; /* Chat-Room Name */
+static int chatline; /* Where to display message now */
+static char chatopic[48];
+static FILE *frec;
+
+
+#define stop_line (b_lines - 2)
+
+
+extern char *bmode();
+
+
+static void
+chat_topic()
+{
+ move(0, 0);
+ prints(" %s«Ç¡G%-14s ¸ÜÃD¡G%-48s",
+ (frec ? "¿ý­µ" : "½Í¤Ñ"), chatroom, chatopic);
+}
+
+
+static void
+printchatline(msg)
+ char *msg;
+{
+ int line;
+
+ line = chatline;
+ move(line, 0);
+ outs(msg);
+ outc('\n');
+
+ if (frec)
+ fprintf(frec, "%s\n", msg);
+
+ if (++line == stop_line)
+ line = 2;
+ move(line, 0);
+ outs("¡÷");
+ clrtoeol();
+ chatline = line;
+}
+
+
+static void
+chat_record()
+{
+ FILE *fp;
+ time_t now;
+ char buf[80];
+
+ time(&now);
+
+ if (fp = frec)
+ {
+ fprintf(fp, "%s\nµ²§ô¡G%s\n", msg_seperator, Btime(&now));
+ fclose(fp);
+ frec = NULL;
+ printchatline("¡» ¿ý­µ§¹²¦¡I");
+ }
+ else
+ {
+#ifdef EVERY_Z
+ /* Thor.980602: ¥Ñ©ó tbf_ask() »Ý°ÝÀɦW¡A¦¹®É·|¥Î¨ì igetch()¡A
+ ¬°¤F¨¾¤î I_OTHERDATA ³y¦¨·í¦í¡A¦b¦¹¥Î every_Z() ªº¤è¦¡¡A
+ ¥ý«O¦s vio_fd¡A«Ý°Ý§¹«á¦AÁÙ­ì */
+
+ vio_save(); /* Thor.980602: ¼È¦s vio_fd */
+#endif
+
+ usr_fpath(buf, cuser.userid, tbf_ask());
+
+#ifdef EVERY_Z
+ vio_restore(); /* Thor.980602: ÁÙ­ì vio_fd */
+#endif
+
+ move(b_lines, 0);
+ clrtoeol();
+
+ fp = fopen(buf, "a");
+ if (fp)
+ {
+ fprintf(fp, "¥DÃD: %s\n¥]´[: %s\n¿ý­µ: %s (%s)\n¶}©l: %s\n%s\n",
+ chatopic, chatroom, cuser.userid, cuser.username,
+ Btime(&now), msg_seperator);
+ printchatline("¡» ¶}©l¿ý­µÅo¡I");
+ frec = fp;
+ }
+ else
+ {
+ printchatline("¡» ¿ý­µ¾÷¬G»Ù¤F¡A½Ð³qª¾¯¸ªøºû­×");
+ }
+ }
+ bell();
+ chat_topic();
+}
+
+
+static void
+chat_clear()
+{
+ int line;
+
+ for (line = 2; line < stop_line; line++)
+ {
+ move(line, 0);
+ clrtoeol();
+ }
+ chatline = stop_line - 1;
+ printchatline("");
+}
+
+
+static void
+print_chatid(chatid)
+ char *chatid;
+{
+ move(b_lines - 1, 0);
+ outs(chatid);
+ outc(':');
+}
+
+
+static inline int
+chat_send(fd, buf)
+ int fd;
+ char *buf;
+{
+ int len;
+
+ len = strlen(buf);
+ return (send(fd, buf, len, 0) == len);
+}
+
+
+static inline int
+chat_recv(fd, chatid)
+ int fd;
+ char *chatid;
+{
+ static char buf[512];
+ static int bufstart = 0;
+ int cc, len;
+ char *bptr, *str;
+
+ bptr = buf;
+ cc = bufstart;
+ len = sizeof(buf) - cc - 1;
+ if ((len = recv(fd, bptr + cc, len, 0)) <= 0)
+ return -1;
+ cc += len;
+
+ for (;;)
+ {
+ len = strlen(bptr);
+
+ if (len >= cc)
+ { /* wait for trailing data */
+ memcpy(buf, bptr, len);
+ bufstart = len;
+ break;
+ }
+ if (*bptr == '/')
+ {
+ str = bptr + 1;
+ fd = *str++;
+
+ if (fd == 'c')
+ {
+ chat_clear();
+ }
+ else if (fd == 'n')
+ {
+ str_ncpy(chatid, str, 9);
+
+ /* Thor.980819: ¶¶«K´«¤@¤U mateid ¦n¤F... */
+ str_ncpy(cutmp->mateid, str, sizeof(cutmp->mateid));
+
+ print_chatid(chatid);
+ clrtoeol();
+ }
+ else if (fd == 'r')
+ {
+ str_ncpy(chatroom, str, sizeof(chatroom));
+ chat_topic();
+ }
+ else if (fd == 't')
+ {
+ str_ncpy(chatopic, str, sizeof(chatopic));
+ chat_topic();
+ }
+ }
+ else
+ {
+ printchatline(bptr);
+ }
+
+ cc -= ++len;
+ if (cc <= 0)
+ {
+ bufstart = 0;
+ break;
+ }
+ bptr += len;
+ }
+
+ return 0;
+}
+
+
+static void
+chat_pager(arg)
+ char *arg;
+{
+ cuser.ufo ^= UFO_PAGER;
+ cutmp->ufo ^= UFO_PAGER;
+ /* Thor.980805: ¸Ñ¨Mufo ¦P¨B°ÝÃD */
+
+ sprintf(arg, "¡» ±zªº©I¥s¾¹¤w¸g%s¤F!",
+ cuser.ufo & UFO_PAGER ? "Ãö³¬" : "¥´¶}");
+ printchatline(arg);
+}
+
+
+#if 0
+/* Thor.980727: ©M /flag ½Äkey */
+static void
+chat_write(arg)
+ char *arg;
+{
+ int uno;
+ UTMP *up;
+ char *str;
+ CallMsg cmsg;
+
+ strtok(arg, STR_SPACE);
+ if ((str = strtok(NULL, STR_SPACE)) && (uno = acct_userno(str)) > 0)
+ {
+ cmsg.recver = uno; /* ¥ý°O¤U userno §@¬° check */
+ if (up = utmp_find(uno))
+ {
+ if (can_override(up))
+ {
+ if (str = strtok(NULL, "\n")) /* Thor.980725:§ì¾ã¥y¸Ü */
+ { /* Thor.980724: ±q my_write §ï¹L¨Ó */
+ int len;
+ char buf[80];
+ extern char fpmsg[];
+ /* Thor.980722: msg file¥[¤W¦Û¤v»¡ªº¸Ü */
+
+ sprintf(fpmsg + 4, "%s-", cuser.userid);
+ /* Thor.980722: ­É¥Î len·í¤@¤Ufd :p */
+ len = open(fpmsg, O_WRONLY | O_CREAT | O_APPEND, 0600);
+ sprintf(buf, "µ¹%s¡G%s\n", up->userid, str);
+ write(len, buf, strlen(buf));
+ close(len);
+
+ sprintf(buf, "%s(%s", cuser.userid, cuser.username);
+ len = strlen(str);
+ buf[71 - len] = '\0';
+ sprintf(cmsg.msg, "\033[1;33;46m¡¹ %s) \033[37;45m %s \033[m", buf, str);
+
+ cmsg.caller = cutmp;
+ cmsg.sender = cuser.userno;
+
+ if (do_write(up, &cmsg))
+ printchatline("¡» ¹ï¤è¤w¸gÂ÷¥h");
+ }
+ else
+ {
+ printchatline("¡» §O¥u¯w²´¡A»¡¨Ç¸Ü§a¡I");
+ }
+ }
+ else
+ {
+ printchatline("¡» ¹ï¤è§â¦Õ¦·Ý³¦í»¡¡G¡y§Ú¨SÅ¥¨ì¡K¡K§Ú¨SÅ¥¨ì¡K¡K¡z");
+ }
+ }
+ else
+ {
+ printchatline("¡» ¹ï¤è¤£¦b¯¸¤W");
+ }
+ }
+ else
+ {
+ printchatline(err_uid);
+ }
+}
+
+
+static int
+printuserent(uentp)
+ user_info *uentp;
+{
+ static char uline[80];
+ static int cnt;
+ char pline[30];
+ int cloak;
+
+ if (!uentp)
+ {
+ if (cnt)
+ printchatline(uline);
+ memset(uline, 0, 80);
+ return cnt = 0;
+ }
+ cloak = uentp->ufo & UFO_CLOAK;
+ if (cloak && !HAS_PERM(PERM_SEECLOAK))
+ return 0;
+
+ sprintf(pline, " %-13s%c%-10s", uentp->userid,
+ cloak ? '#' : ' ', bmode(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
+chat_users()
+{ /* ¦]¬°¤H¼Æ°Ê»³¤W¦Ê¡A·N¸q¤£¤j */
+ printchatline("");
+ printchatline("¡i " BBSNAME "¹C«È¦Cªí ¡j");
+ printchatline(MSG_CHAT_ULIST);
+
+ if (apply_ulist(printuserent) == -1)
+ printchatline("ªÅµL¤@¤H");
+
+ printuserent(NULL);
+}
+#endif
+
+
+struct chat_command
+{
+ char *cmdname; /* Char-room command length */
+ void (*cmdfunc) (); /* Pointer to function */
+};
+
+
+struct chat_command chat_cmdtbl[] =
+{
+ {"pager", chat_pager},
+ {"tape", chat_record},
+
+#if 0
+ /* Thor.980727: ©M /flag ½Äkey */
+ {"fire", chat_write},
+
+ {"users", chat_users},
+#endif
+
+ {NULL, NULL}
+};
+
+
+static inline int
+chat_cmd_match(buf, str)
+ char *buf;
+ char *str;
+{
+ int c1, c2;
+
+ for (;;)
+ {
+ c1 = *str++;
+ if (!c1)
+ break;
+
+ c2 = *buf++;
+ if (!c2 || c2 == ' ' || c2 == '\n')
+ break;
+
+ if (c2 >= 'A' && c2 <= 'Z')
+ c2 |= 0x20;
+
+ if (c1 != c2)
+ return 0;
+ }
+
+ return 1;
+}
+
+
+static inline int
+chat_cmd(fd, buf)
+ int fd;
+ char *buf;
+{
+ struct chat_command *cmd;
+ char *key;
+
+ buf++;
+ for (cmd = chat_cmdtbl; key = cmd->cmdname; cmd++)
+ {
+ if (chat_cmd_match(buf, key))
+ {
+ cmd->cmdfunc(buf);
+ return '/';
+ }
+ }
+
+ return 0;
+}
+
+
+extern char lastcmd[MAXLASTCMD][80];
+
+#define CHAT_YPOS 10
+
+
+int
+t_chat()
+{
+ ACCT acct;
+ int ch, cfd, cmdpos, cmdcol;
+ char *ptr, buf[80], chatid[9];
+ struct sockaddr_in sin;
+#if defined(__OpenBSD__)
+ struct hostent *h;
+#endif
+
+#ifdef CHAT_SECURE
+ extern char passbuf[];
+#endif
+
+#ifdef EVERY_Z
+ /* Thor.980725: ¬° talk & chat ¥i¥Î ^z §@·Ç³Æ */
+ if (vio_holdon())
+ {
+ vmsg("±zÁ¿¸ÜÁ¿¤@¥bÁÙ¨SÁ¿§¹­C");
+ return -1;
+ }
+#endif
+
+#if defined(__OpenBSD__)
+
+ if (!(h = gethostbyname(str_host)))
+ return -1;
+
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(CHAT_PORT);
+ memcpy(&sin.sin_addr, h->h_addr, h->h_length);
+
+#else
+
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(CHAT_PORT);
+ /* sin.sin_addr.s_addr = INADDR_LOOPBACK; */
+ /* sin.sin_addr.s_addr = INADDR_ANY; */
+ /* for FreeBSD 4.x */
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ memset(sin.sin_zero, 0, sizeof(sin.sin_zero));
+
+#endif
+
+ cfd = socket(AF_INET, SOCK_STREAM, 0);
+ if (cfd < 0)
+ return -1;
+
+ if (connect(cfd, (struct sockaddr *) & sin, sizeof sin))
+ {
+ close(cfd);
+ blog("CHAT ", "connect");
+ return -1;
+ }
+
+ for (;;)
+ {
+ ch = vget(b_lines, 0, "½Ð¿é¤J²á¤Ñ¥N¸¹¡G", chatid, 9, DOECHO);
+ if (ch == '/')
+ {
+ continue;
+ }
+ else if (!ch)
+ {
+ /* str_ncpy(chatid, cuser.userid, sizeof(chatid)); */
+ close(cfd); /* itoc.010322: ¤j³¡¤À³£¬O»~«ö¨ì Talk->Chat §ï¦¨¹w³]¬°Â÷¶} */
+ return 0;
+ }
+ else
+ {
+ /* itoc.010528: ¤£¥i¥H¥Î§O¤Hªº id °µ¬°²á¤Ñ¥N¸¹ */
+ if (acct_load(&acct, chatid) >= 0 && acct.userno != cuser.userno)
+ {
+ vmsg("©êºp³o­Ó¥N¸¹¦³¤Hµù¥U¬° id¡A©Ò¥H±z¤£¯à·í¦¨²á¤Ñ¥N¸¹");
+ continue;
+ }
+ /* Thor.980911: chatid¤¤¤£¥i¥HªÅ¥Õ, ¨¾¤î parse¿ù»~ */
+ for(ch = 0; ch < 8; ch++)
+ {
+ if (chatid[ch] == ' ')
+ break;
+ else if (!chatid[ch]) /* Thor.980921: ¦pªG0ªº¸Ü´Nµ²§ô */
+ ch = 8;
+ }
+ if (ch < 8)
+ continue;
+ }
+
+#ifdef CHAT_SECURE /* Thor.980729: secured chat room */
+
+#if 0
+ §@ªÌ opus (¤H¥Í¦³¨ý¬O²MÅw) ¯¸¤º sysopplan
+ ¼ÐÃD Re: Ãö©ó chatroom
+ ®É¶¡ Wed Jul 30 03:14:56 1997
+ ¢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
+
+ > passwd ¬O³sÄòªº«DªÅ¥Õ¦r¤¸¶Ü??
+
+ ¤T­Ó°Ñ¼Æ userid + chatid + passwd ¤¤, userid/chatid ¬Ò¤£§t space,
+ ¦Ó passwd ¥i¥]§t space, ©Ò¥H¥²¶·±N¥¦Â\¦b²Ä¤T­Ó¦ì¸m¥H«K token-parsing¡C
+
+ > ¥t¥~, ACCT ¤¤ªº passwd ¬OPASSLEN¦Û°Ê¸ÉªÅ¥ÕÁÙ¬O­n¤â°Ê¥[?
+ > ·|¦Û°Ê¸Éº¡¶Ü?
+
+ Unix ªº crypt ³Ì¦h¥u¨ú«e 14 ­Ó¦r, ©Ò¥H¤£¥[¥ç¥i¡C
+ ³o­Ó¦a¤è °Ñ·Ó user login ªº¦a¤è¼g´N¦n¤F¡C
+
+ --
+ ¡° Origin: ·¬¾ôÅæ¯¸(bbs.cs.nthu.edu.tw) From: thcs-8.cs.nthu.edu.tw
+#endif
+
+ /* Thor.980819: ®³±¼userno */
+ /* Thor.980730: passwd §ï¬°³Ì«áªº°Ñ¼Æ */
+ /* Thor.980813: passwd §ï¬°¯u¥¿ªº password */
+ /* Thor.980813: xchatd¤¤, chatid ¦Û°Ê¸õ¹LªÅ¥Õ, ©Ò¥H¦³ªÅ¥Õ·| invalid login */
+#if 0
+ sprintf(buf, "/! %d %s %s %s\n",
+ cuser.userno, cuser.userid, chatid, cuser.passwd);
+ cuser.userno, cuser.userid, chatid, passbuf);
+#endif
+ sprintf(buf, "/! %s %s %s\n",
+ cuser.userid, chatid, passbuf);
+
+#else
+ sprintf(buf, "/! %d %d %s %s\n",
+ cuser.userno, cuser.userlevel, cuser.userid, chatid);
+#endif
+
+ chat_send(cfd, buf);
+ if (recv(cfd, buf, 3, 0) != 3)
+ return 0;
+
+ if (!strcmp(buf, CHAT_LOGIN_OK))
+ break;
+ else if (!strcmp(buf, CHAT_LOGIN_EXISTS))
+ ptr = "³o­Ó¥N¸¹¤w¸g¦³¤H¥Î¤F";
+ else if (!strcmp(buf, CHAT_LOGIN_INVALID))
+ ptr = "³o­Ó¥N¸¹¬O¿ù»~ªº";
+ else if (!strcmp(buf, CHAT_LOGIN_BOGUS))
+ { /* Thor: ¸T¤î¬Û¦P¤G¤H¶i¤J */
+ close(cfd);
+ vmsg("½Ð¤Å¬£»º¡u¤À¨­¡v¶i¤J½Í¤Ñ«Ç");
+ return 0;
+ }
+ move(b_lines - 1, 0);
+ outs(ptr);
+ clrtoeol();
+ bell();
+ }
+
+ clear();
+ move(1, 0);
+ outs(msg_seperator);
+ move(stop_line, 0);
+ outs(msg_seperator);
+ print_chatid(chatid);
+ memset(ptr = buf, 0, sizeof(buf));
+ chatline = 2;
+ cmdcol = 0;
+ cmdpos = -1;
+
+ add_io(cfd, 60);
+
+ strcpy(cutmp->mateid, chatid);
+
+ for (;;)
+ {
+ move(b_lines - 1, cmdcol + CHAT_YPOS);
+ ch = vkey();
+
+ if (ch == I_OTHERDATA)
+ { /* incoming */
+ if (chat_recv(cfd, chatid) == -1)
+ break;
+ continue;
+ }
+
+ if (isprint2(ch))
+ {
+ if (cmdcol < 68)
+ {
+ if (ptr[cmdcol])
+ { /* insert */
+ int i;
+
+ for (i = cmdcol; ptr[i] && i < 68; i++);
+ ptr[i + 1] = '\0';
+ for (; i > cmdcol; i--)
+ ptr[i] = ptr[i - 1];
+ }
+ else
+ { /* append */
+ ptr[cmdcol + 1] = '\0';
+ }
+ ptr[cmdcol] = ch;
+ move(b_lines - 1, cmdcol + CHAT_YPOS);
+ outs(&ptr[cmdcol++]);
+ }
+ continue;
+ }
+
+ if (ch == '\n')
+ {
+#ifdef EVERY_BIFF
+ /* Thor.980805: ¦³¤H¦b®ÇÃä«öenter¤~»Ý­ncheck biff */
+ static int old_biff;
+ int biff = HAS_STATUS(STATUS_BIFF);
+ if (biff && !old_biff)
+ printchatline("¡» ¾´! ¶l®t¨Ó«ö¹a¤F!");
+ old_biff = biff;
+#endif
+ if (ch = *ptr)
+ {
+ if (ch == '/')
+ ch = chat_cmd(cfd, ptr);
+
+ /* Thor.980602: ¦³­Ó­nª`·Nªº¤p¦a¤è, ­ì¥»¦pªG¬O¡y/¡z,
+ ·|¨q¥X /helpªºµe­±,
+ ²{¦b¥´ /, ·|Åܦ¨ /p ¤Á´« pager */
+
+ /* Thor.980925: «O¯d ptr ³Ì­ì©l¼Ë, ¤£¥[ /n */
+ for (cmdpos = MAXLASTCMD - 1; cmdpos; cmdpos--)
+ strcpy(lastcmd[cmdpos], lastcmd[cmdpos - 1]);
+ strcpy(lastcmd[0], ptr);
+
+ if (ch != '/')
+ {
+ strcat(ptr, "\n");
+ if (!chat_send(cfd, ptr))
+ break;
+ }
+ if (*ptr == '/' && ptr[1] == 'b')
+ break;
+
+#if 0
+ for (cmdpos = MAXLASTCMD - 1; cmdpos; cmdpos--)
+ strcpy(lastcmd[cmdpos], lastcmd[cmdpos - 1]);
+ strcpy(lastcmd[0], ptr);
+#endif
+
+ *ptr = '\0';
+ cmdcol = 0;
+ cmdpos = -1;
+ move(b_lines - 1, CHAT_YPOS);
+ clrtoeol();
+ }
+ continue;
+ }
+
+ if (ch == KEY_BKSP)
+ {
+ if (cmdcol)
+ {
+ ch = cmdcol;
+ cmdcol--;
+#ifdef HAVE_MULTI_BYTE
+ /* hightman.060504: §PÂ_²{¦b§R°£ªº¦ì¸m¬O§_¬°º~¦rªº«á¥b¬q¡A­Y¬O§R¤G¦r¤¸ */
+ if ((cuser.ufo & UFO_ZHC) && cmdcol && IS_ZHC_LO(ptr, cmdcol))
+ cmdcol--;
+#endif
+ strcpy(ptr + cmdcol, ptr + ch);
+ move(b_lines - 1, cmdcol + CHAT_YPOS);
+ outs(ptr + cmdcol);
+ clrtoeol();
+ }
+ continue;
+ }
+
+ if (ch == KEY_DEL)
+ {
+ if (ptr[cmdcol])
+ {
+#ifdef HAVE_MULTI_BYTE
+ /* hightman.060504: §PÂ_²{¦b§R°£ªº¦ì¸m¬O§_¬°º~¦rªº«e¥b¬q¡A­Y¬O§R¤G¦r¤¸ */
+ if ((cuser.ufo & UFO_ZHC) && ptr[cmdcol + 1] && IS_ZHC_HI(ptr[cmdcol]))
+ ch = 2;
+ else
+#endif
+ ch = 1;
+ strcpy(ptr + cmdcol, ptr + cmdcol + ch);
+ move(b_lines - 1, cmdcol + CHAT_YPOS);
+ outs(ptr + cmdcol);
+ clrtoeol();
+ }
+ continue;
+ }
+
+ if (ch == Ctrl('D'))
+ {
+ chat_send(cfd, "/b\n"); /* /bye Â÷¶} */
+ break;
+ }
+
+ if (ch == Ctrl('C')) /* itoc.µù¸Ñ: ²M°£ input ¾ã¦æ */
+ {
+ *ptr = '\0';
+ cmdcol = 0;
+ move(b_lines - 1, CHAT_YPOS);
+ clrtoeol();
+ continue;
+ }
+
+ if (ch == KEY_HOME || ch == Ctrl('A'))
+ {
+ cmdcol = 0;
+ continue;
+ }
+
+ if (ch == KEY_END || ch == Ctrl('E'))
+ {
+ cmdcol = strlen(ptr);
+ continue;
+ }
+
+ if (ch == KEY_LEFT)
+ {
+ if (cmdcol)
+ {
+ cmdcol--;
+#ifdef HAVE_MULTI_BYTE
+ /* hightman.060504: ¥ª²¾®É¸I¨ìº~¦r²¾Âù®æ */
+ if ((cuser.ufo & UFO_ZHC) && cmdcol && IS_ZHC_LO(ptr, cmdcol))
+ cmdcol--;
+#endif
+ }
+ continue;
+ }
+
+ if (ch == KEY_RIGHT)
+ {
+ if (ptr[cmdcol])
+ {
+ cmdcol++;
+#ifdef HAVE_MULTI_BYTE
+ /* hightman.060504: ¥k²¾®É¸I¨ìº~¦r²¾Âù®æ */
+ if ((cuser.ufo & UFO_ZHC) && ptr[cmdcol] && IS_ZHC_HI(ptr[cmdcol - 1]))
+ cmdcol++;
+#endif
+ }
+ continue;
+ }
+
+#ifdef EVERY_Z
+ /* Thor: Chat ¤¤«ö ctrl-z */
+ if (ch == Ctrl('Z'))
+ {
+ char buf[IDLEN + 1];
+ screenline slt[T_LINES];
+
+ /* Thor.980731: ¼È¦s mateid, ¦]¬°¥X¥h®É¥i¯à·|¥Î±¼ mateid */
+ strcpy(buf, cutmp->mateid);
+
+ vio_save(); /* Thor.980727: ¼È¦s vio_fd */
+ vs_save(slt);
+ every_Z(0);
+ vs_restore(slt);
+ vio_restore(); /* Thor.980727: ÁÙ­ì vio_fd */
+
+ /* Thor.980731: ÁÙ­ì mateid, ¦]¬°¥X¥h®É¥i¯à·|¥Î±¼ mateid */
+ strcpy(cutmp->mateid, buf);
+ continue;
+ }
+#endif
+
+ if (ch == KEY_DOWN)
+ {
+ cmdpos += MAXLASTCMD - 2;
+ ch = KEY_UP;
+ }
+
+ if (ch == KEY_UP)
+ {
+ cmdpos++;
+ cmdpos %= MAXLASTCMD;
+ strcpy(ptr, lastcmd[cmdpos]);
+ move(b_lines - 1, CHAT_YPOS);
+ outs(ptr);
+ clrtoeol();
+ cmdcol = strlen(ptr);
+ }
+ }
+
+ if (frec)
+ chat_record();
+
+ close(cfd);
+ add_io(0, 60);
+ cutmp->mateid[0] = '\0';
+ return 0;
+}
diff --git a/so/classtable.c b/so/classtable.c
new file mode 100644
index 0000000..4f6ef16
--- /dev/null
+++ b/so/classtable.c
@@ -0,0 +1,311 @@
+/*-------------------------------------------------------*/
+/* classtable.c ( YZU WindTopBBS Ver 3.02 ) */
+/*-------------------------------------------------------*/
+/* target : ¥\½Òªí */
+/* create : / / */
+/* update : 02/07/12 */
+/* author : */
+/* modify : itoc.bbs@bbs.ee.nctu.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+#ifdef HAVE_CLASSTABLE
+
+/* ----------------------------------------------------- */
+/* classtable.c ¤¤¹B¥Îªº¸ê®Æµ²ºc */
+/* ----------------------------------------------------- */
+
+
+#define MAX_WEEKDAY 6 /* ¤@¬P´Á¦³ 6 ¤Ñ */
+#define MAX_DAYCLASS 16 /* ¤@¤Ñ¦³ 16 ¸` */
+
+
+typedef struct
+{
+ char name[9]; /* ½Ò¦W */
+ char teacher[9]; /* ±Ð®v */
+ char class[5]; /* ±Ð«Ç */
+ char objid[7]; /* ½Ò¸¹ */
+} CLASS;
+
+
+typedef struct
+{
+ CLASS table[MAX_WEEKDAY][MAX_DAYCLASS]; /* ¤@¬P´Á MAX_WEEKDAY * MAX_DAYCLASS °ó½Ò */
+} CLASS_TABLE;
+
+
+typedef struct
+{
+ char c_class[5]; /* ²Ä´X¸` */
+ char c_start[6]; /* ¤W½Ò®É¶¡ */
+ char c_break[6]; /* ¤U½Ò®É¶¡ */
+} CLOCK;
+
+
+static CLOCK class_time[MAX_DAYCLASS] = /* ½Ò°ó®É¶¡ */
+{
+ {" ¤@ ", "06:00", "06:50"},
+ {" ¤G ", "07:00", "07:50"},
+ {" ¤T ", "08:00", "08:50"},
+ {" ¥| ", "09:00", "09:50"},
+ {" ¤­ ", "10:10", "11:00"},
+ {" ¤» ", "11:10", "12:00"},
+ {" ¤C ", "12:30", "13:20"},
+ {" ¤K ", "13:30", "14:20"},
+ {" ¤E ", "14:30", "15:20"},
+ {" ¤Q ", "15:40", "16:30"},
+ {"¤Q¤@", "16:40", "17:30"},
+ {"¤Q¤G", "17:40", "18:30"},
+ {"¤Q¤T", "18:30", "19:20"},
+ {"¤Q¥|", "19:30", "20:20"},
+ {"¤Q¤­", "20:30", "21:20"},
+ {"¤Q¤»", "21:30", "22:20"}
+};
+
+
+/* ----------------------------------------------------- */
+/* CLASS ³B²z¨ç¼Æ */
+/* ----------------------------------------------------- */
+
+
+static void
+class_show(x, y, class)
+ int x, y;
+ CLASS *class;
+{
+ move(x, y);
+ prints("½Ò¦W¡G%s", class->name);
+ move(x + 1, y);
+ prints("±Ð®v¡G%s", class->teacher);
+ move(x + 2, y);
+ prints("±Ð«Ç¡G%s", class->class);
+ move(x + 3, y);
+ prints("½Ò¸¹¡G%s", class->objid);
+}
+
+
+static void
+class_edit(class)
+ CLASS *class;
+{
+ int echo;
+
+ echo = *(class->name) ? GCARRY : DOECHO;
+ vget(4, 0, "½Ò¦W¡G", class->name, sizeof(class->name), echo);
+ vget(5, 0, "±Ð®v¡G", class->teacher, sizeof(class->teacher), echo);
+ vget(6, 0, "±Ð«Ç¡G", class->class, sizeof(class->class), echo);
+ vget(7, 0, "½Ò¸¹¡G", class->objid, sizeof(class->objid), echo);
+}
+
+
+static int /* 1:¥¿½T 0:¿ù»~ */
+class_number(day, class) /* ¶Ç¦^¬P´Á´X²Ä´X¸` */
+ int *day;
+ int *class;
+{
+ char ans[5];
+
+ move(2, 0);
+ outs("503 ªí¥Ü¬P´Á¤­²Ä¤T¸`");
+ *day = vget(3, 0, "¤W½Ò®É¶¡¡G", ans, 4, DOECHO) - '1'; /* 503 ªí¥Ü¬P´Á¤­²Ä¤T¸` */
+ *class = atoi(ans + 1) - 1;
+ if (*day > MAX_WEEKDAY - 1 || *day < 0 || *class > MAX_DAYCLASS - 1 || *class < 0)
+ return 0;
+
+ return 1;
+}
+
+
+/* ----------------------------------------------------- */
+/* CLASS_TABLE ³B²z¨ç¼Æ */
+/* ----------------------------------------------------- */
+
+
+static void
+table_file(fpath, table) /* §â table ¼g¤J FN_CLASSTBL_LOG */
+ char *fpath;
+ CLASS_TABLE *table;
+{
+ int i, j;
+ FILE *fp;
+
+ fp = fopen(fpath, "w");
+
+ fprintf(fp, " ¬P´Á¤@ ¬P´Á¤G ¬P´Á¤T ¬P´Á¥| ¬P´Á¤­ ¬P´Á¤»\n");
+ for (i = 0; i < MAX_DAYCLASS; i++)
+ {
+ fprintf(fp, "²Ä%s¸` ", class_time[i].c_class);
+ for (j = 0; j < MAX_WEEKDAY; j++)
+ fprintf(fp, "%-8.8s ", table->table[j][i].name);
+
+ fprintf(fp, "\n %s ", class_time[i].c_start);
+ for (j = 0; j < MAX_WEEKDAY; j++)
+ fprintf(fp, "%-8.8s ", table->table[j][i].teacher);
+
+ fprintf(fp, "\n ¡õ ");
+ for (j = 0; j < MAX_WEEKDAY; j++)
+ fprintf(fp, "%-8.8s ", table->table[j][i].class);
+
+ fprintf(fp, "\n %s ", class_time[i].c_break);
+ for (j = 0; j < MAX_WEEKDAY; j++)
+ fprintf(fp, "%-8.8s ", table->table[j][i].objid);
+
+ fprintf(fp, "\n\n");
+ }
+ fclose(fp);
+}
+
+
+static void
+table_show(table)
+ CLASS_TABLE *table;
+{
+ char fpath[64];
+
+ usr_fpath(fpath, cuser.userid, FN_CLASSTBL_LOG);
+ table_file(fpath, table);
+ more(fpath, NULL);
+}
+
+
+static void
+table_mail(table)
+ CLASS_TABLE *table;
+{
+ char fpath[64];
+
+ usr_fpath(fpath, cuser.userid, FN_CLASSTBL_LOG);
+ table_file(fpath, table);
+ mail_self(fpath, cuser.userid, "­Ó¤H¥\\½Òªí", MAIL_READ);
+}
+
+
+static void
+table_edit(table)
+ CLASS_TABLE *table;
+{
+ int i, j;
+
+ vs_bar("½s¿è­Ó¤H¥\\½Òªí");
+
+ if (class_number(&i, &j))
+ {
+ class_edit(&(table->table[i][j]));
+ class_show(10, 0, &(table->table[i][j]));
+ }
+}
+
+
+static void
+table_del(table)
+ CLASS_TABLE *table;
+{
+ int i, j;
+
+ vs_bar("§R°£­Ó¤H¥\\½Òªí");
+
+ if (!class_number(&i, &j))
+ return;
+
+ class_show(10, 0, &(table->table[i][j]));
+
+ if (vans(msg_sure_ny) == 'y')
+ memset(&(table->table[i][j]), 0, sizeof(CLASS));
+}
+
+
+static void
+table_copy(table)
+ CLASS_TABLE *table;
+{
+ int i, j, x, y;
+
+ vs_bar("­Ó¤H¥\\½Òªí");
+
+ move(9, 0);
+ outs("¨Ó·½¡G");
+ if (!class_number(&i, &j))
+ return;
+
+ class_show(10, 0, &(table->table[i][j]));
+
+ move(9, 39);
+ outs("¥Øªº¡G");
+ if (!class_number(&x, &y))
+ return;
+
+ class_show(10, 39, &(table->table[x][y]));
+
+ if (vans(msg_sure_ny) == 'y')
+ memcpy(&(table->table[x][y]), &(table->table[i][j]), sizeof(CLASS));
+}
+
+
+int
+main_classtable()
+{
+ char fpath[64];
+ CLASS_TABLE newtable, oldtable, *ptr;
+
+ usr_fpath(fpath, cuser.userid, FN_CLASSTBL);
+ ptr = &newtable;
+
+ if (rec_get(fpath, ptr, sizeof(CLASS_TABLE), 0))
+ memset(ptr, 0, sizeof(CLASS_TABLE));
+ memcpy(&oldtable, ptr, sizeof(CLASS_TABLE));
+
+ for (;;)
+ {
+ switch (vans("½Òªí¨t²Î (E/C/D)½s¿è/½Æ»s/§R°£ P)¦L¥X K)¥þ¬å S)¦sÀÉ M)«H½c Q)Â÷¶} [Q] "))
+ {
+ case 'e':
+ table_edit(ptr);
+ break;
+ case 'd':
+ table_del(ptr);
+ break;
+ case 'c':
+ table_copy(ptr);
+ break;
+
+ case 'p':
+ table_show(ptr);
+ break;
+ case 'm':
+ table_mail(ptr);
+ break;
+
+ case 's':
+ rec_put(fpath, ptr, sizeof(CLASS_TABLE), 0, NULL);
+ memcpy(&oldtable, ptr, sizeof(CLASS_TABLE));
+ vmsg("Àx¦s§¹¦¨");
+ break;
+ case 'k':
+ if (vans(msg_sure_ny) == 'y')
+ {
+ unlink(fpath);
+ memset(ptr, 0, sizeof(CLASS_TABLE));
+ memset(&oldtable, 0, sizeof(CLASS_TABLE));
+ }
+ break;
+
+ default:
+ goto end_loop;
+ }
+ }
+
+end_loop:
+
+ /* Àˬd·s¬O§_¤@¼Ë¡A­Y¤£¤@¼Ë­n°Ý¬O§_¦sÀÉ */
+ if (memcmp(&oldtable, ptr, sizeof(CLASS_TABLE)))
+ {
+ if (vans("¬O§_Àx¦s(Y/N)¡H[Y] ") != 'n')
+ rec_put(fpath, ptr, sizeof(CLASS_TABLE), 0, NULL);
+ }
+
+ return 0;
+}
+#endif /* HAVE_CLASSTABLE */
diff --git a/so/credit.c b/so/credit.c
new file mode 100644
index 0000000..933a5e8
--- /dev/null
+++ b/so/credit.c
@@ -0,0 +1,274 @@
+/*-------------------------------------------------------*/
+/* credit.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : °O±b¥»¡A°O¿ý¥Í¬¡¤¤ªº¦¬¤J¤ä¥X */
+/* create : 99/12/18 */
+/* update : 02/01/26 */
+/* author : wildcat@wd.twbbs.org */
+/* recast : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+#ifdef HAVE_CREDIT
+
+/* ----------------------------------------------------- */
+/* credit.c ¤¤¹B¥Îªº¸ê®Æµ²ºc */
+/* ----------------------------------------------------- */
+
+typedef struct
+{
+ int year; /* ¦~ */
+ char month; /* ¤ë */
+ char day; /* ¤é */
+
+ char flag; /* ¤ä¥X/¦¬¤J */
+ int money; /* ª÷ÃB */
+ char useway; /* Ãþ§O(­¹¦ç¦í¦æ¨|¼Ö) */
+ char desc[112]; /* »¡©ú */ /* ³o¤Óªø¤F¡A«O¯dµ¹¨ä¥LÄæ¦ì¨Ï¥Î */
+} CREDIT;
+
+
+#define CREDIT_OUT 0x1 /* ¤ä¥X */
+#define CREDIT_IN 0x2 /* ¦¬¤J */
+
+#define CREDIT_OTHER 0 /* ¨ä¥L */
+#define CREDIT_EAT 1 /* ­¹ */
+#define CREDIT_WEAR 2 /* ¦ç */
+#define CREDIT_LIVE 3 /* ¦í */
+#define CREDIT_MOVE 4 /* ¦æ */
+#define CREDIT_EDU 5 /* ¨| */
+#define CREDIT_PLAY 6 /* ¼Ö */
+
+static char fpath[64]; /* FN_CREDIT Àɮ׸ô®| */
+
+
+static void
+credit_head()
+{
+ vs_head("°O±b¤â¥¾", str_site);
+ prints(NECKER_CREDIT, d_cols, "");
+}
+
+
+static void
+credit_body(page)
+ int page;
+{
+ CREDIT credit;
+ char *way[] = {"¨ä¥L", "[­¹]", "[¦ç]", "[¦í]", "[¦æ]", "[¨|]", "[¼Ö]"};
+ int fd;
+
+ move(1, 65);
+ prints("²Ä %2d ­¶", page + 1);
+
+ move(3, 0);
+ clrtobot();
+
+ if ((fd = open(fpath, O_RDONLY)) >= 0)
+ {
+ int pos, n;
+
+ pos = page * XO_TALL; /* ¤@­¶¦³ XO_TALL µ§ */
+ n = XO_TALL;
+
+ while (n)
+ {
+ lseek(fd, (off_t) (sizeof(CREDIT) * pos), SEEK_SET);
+ if (read(fd, &credit, sizeof(CREDIT)) == sizeof(CREDIT))
+ {
+ n--;
+ pos++;
+ prints("%6d %04d/%02d/%02d %s %8d %4s %.*s\n",
+ pos, credit.year, credit.month, credit.day,
+ credit.flag == CREDIT_OUT ? "\033[1;32m¤ä¥X\033[m" : "\033[1;31m¦¬¤J\033[m",
+ credit.money,
+ credit.flag == CREDIT_OUT ? way[credit.useway] : " ",
+ d_cols + 46, credit.desc);
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ close(fd);
+ }
+}
+
+
+static int
+credit_add()
+{
+ CREDIT credit;
+ char buf[80];
+
+ move(3, 0);
+ clrtobot();
+
+ memset(&credit, 0, sizeof(CREDIT));
+
+ if (vget(5, 0, "¦¬¤ä (1)¦¬¤J (2)¤ä¥X [2] ", buf, 3, DOECHO) == '1')
+ credit.flag = CREDIT_IN;
+ else
+ credit.flag = CREDIT_OUT;
+
+ vget(6, 0, "®É¶¡ (¦~¥÷) ", buf, 5, DOECHO);
+ credit.year = atoi(buf);
+
+ vget(7, 0, "®É¶¡ (¤ë¥÷) ", buf, 3, DOECHO);
+ credit.month = atoi(buf);
+
+ vget(8, 0, "®É¶¡ (¤é´Á) ", buf, 3, DOECHO);
+ credit.day = atoi(buf);
+
+ vget(9, 0, "ª÷¿ú (¤¸) ", buf, 9, DOECHO);
+ credit.money = atoi(buf);
+
+ if (credit.flag == CREDIT_OUT) /* ¤ä¥X¤~¦³°O¿ý¥Î³~ */
+ {
+ int useway;
+
+ useway = vget(10, 0, "¥Î³~ 0)¨ä¥L 1)­¹ 2)¦ç 3)¦í 4)¦æ 5)¨| 6)¼Ö [0] ", buf, 3, DOECHO) - '0';
+ if (useway > 6 || useway < 0)
+ useway = 0;
+ credit.useway = useway;
+ }
+
+ vget(11, 0, "»¡©ú¡G", credit.desc, 51, DOECHO);
+
+ rec_add(fpath, &credit, sizeof(CREDIT));
+ return 1;
+}
+
+
+static int
+credit_delete()
+{
+ int pos;
+ char buf[4];
+
+ vget(b_lines, 0, "­n§R°£²Ä´Xµ§¸ê®Æ¡G", buf, 4, DOECHO);
+ pos = atoi(buf);
+
+ if (rec_num(fpath, sizeof(CREDIT)) < pos)
+ {
+ vmsg("±z·d¿ùÅo¡A¨S¦³³oµ§¸ê®Æ");
+ return 0;
+ }
+
+ rec_del(fpath, sizeof(CREDIT), pos - 1, NULL);
+ return 1;
+}
+
+
+static int
+credit_count()
+{
+ CREDIT *credit;
+ struct stat st;
+ int fd;
+ int way[7], moneyin, moneyout;
+
+ if ((fd = open(fpath, O_RDONLY)) >= 0 && !fstat(fd, &st) && st.st_size > 0)
+ {
+ memset(way, 0, sizeof(way));
+ moneyin = 0;
+
+ mgets(-1);
+ while (credit = mread(fd, sizeof(CREDIT)))
+ {
+ if (credit->flag == CREDIT_OUT) /* ¤ä¥X¤~¦³°O¿ý¥Î³~ */
+ way[credit->useway] += credit->money;
+ else
+ moneyin += credit->money;
+ }
+ close(fd);
+
+ for (fd = 0; fd <= 6; fd++)
+ moneyout += way[fd];
+
+ move(3, 0);
+ clrtobot();
+
+ move(7, 0);
+ prints(" \033[1;31mÁ`¦¬¤J %12d ¤¸\033[m\n", moneyin);
+ prints(" \033[1;32mÁ`¤ä¥X %12d ¤¸\033[m\n\n", moneyout);
+
+ prints("ªá¦b \033[1;36m [­¹] %12d ¤¸ \033[32m [¦ç] %12d ¤¸\033[m\n", way[CREDIT_EAT], way[CREDIT_WEAR]);
+ prints(" \033[1;31m [¦í] %12d ¤¸ \033[33m [¦æ] %12d ¤¸\033[m\n", way[CREDIT_LIVE], way[CREDIT_MOVE]);
+ prints(" \033[1;35m [¨|] %12d ¤¸ \033[37m [¼Ö] %12d ¤¸\033[m\n", way[CREDIT_EDU], way[CREDIT_PLAY]);
+ prints(" \033[1;34m ¨ä¥L %12d ¤¸\033[m", way[CREDIT_OTHER]);
+
+ vmsg(NULL);
+ return 1;
+ }
+
+ vmsg("±z¨S¦³°O±b°O¿ý");
+ return 0;
+}
+
+
+
+int
+main_credit()
+{
+ int page, redraw;
+ char buf[3];
+
+ credit_head();
+
+ usr_fpath(fpath, cuser.userid, FN_CREDIT);
+ page = 0;
+ redraw = 1;
+
+ for (;;)
+ {
+ if (redraw)
+ credit_body(page);
+
+ switch (vans("°O±b¤â¥¾ C)´«­¶ 1)·s¼W 2)§R°£ 3)¥þ§R 4)Á`­p Q)Â÷¶} [Q] "))
+ {
+ case 'c':
+ vget(b_lines, 0, "¸õ¨ì²Ä´X­¶¡G", buf, 3, DOECHO);
+ redraw = atoi(buf) - 1;
+
+ if (page != redraw && redraw >= 0 &&
+ redraw <= (rec_num(fpath, sizeof(CREDIT)) - 1) / XO_TALL)
+ {
+ page = redraw;
+ redraw = 1;
+ }
+ else
+ {
+ redraw = 0;
+ }
+ break;
+
+ case '1':
+ redraw = credit_add();
+ break;
+
+ case '2':
+ redraw = credit_delete();
+ break;
+
+ case '3':
+ if (vans(MSG_SURE_NY) == 'y')
+ {
+ unlink(fpath);
+ return 0;
+ }
+ break;
+
+ case '4':
+ redraw = credit_count();
+ break;
+
+ default:
+ return 0;
+ }
+ }
+}
+#endif /* HAVE_CREDIT */
diff --git a/so/help.c b/so/help.c
new file mode 100644
index 0000000..b64f778
--- /dev/null
+++ b/so/help.c
@@ -0,0 +1,207 @@
+/*-------------------------------------------------------*/
+/* help.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : help »¡©ú¤å¥ó */
+/* create : 03/05/10 */
+/* update : / / */
+/* author : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+
+static void
+do_help(path) /* itoc.021122: »¡©ú¤å¥ó */
+ char *path;
+{
+ char *str;
+ char fpath[64];
+ int num, pageno, pagemax, redraw, reload;
+ int ch, cur, i;
+ struct stat st;
+ PAL *pal;
+
+ /* »¡©ú¤å¥ó³£©ñ¦b etc/help/ ¤U */
+ sprintf(fpath, "etc/help/%s/%s", path, fn_dir);
+ str = strchr(fpath, '.');
+
+ reload = 1;
+ pageno = 0;
+ cur = 0;
+ pal = NULL;
+
+ do
+ {
+ if (reload)
+ {
+ if (stat(fpath, &st) == -1)
+ return;
+
+ i = st.st_size;
+ num = (i / sizeof(PAL)) - 1;
+ if (num < 0)
+ return;
+
+ if ((ch = open(fpath, O_RDONLY)) >= 0)
+ {
+ pal = pal ? (PAL *) realloc(pal, i) : (PAL *) malloc(i);
+ read(ch, pal, i);
+ close(ch);
+ }
+
+ pagemax = num / XO_TALL;
+ reload = 0;
+ redraw = 1;
+ }
+
+ if (redraw)
+ {
+ /* itoc.µù¸Ñ: ºÉ¶q°µ±o¹³ xover ®æ¦¡ */
+ vs_head("»¡©ú¤å¥ó", str_site);
+ prints(NECKER_HELP, d_cols, "");
+
+ i = pageno * XO_TALL;
+ ch = BMIN(num, i + b_lines - 4);
+ move(3, 0);
+ do
+ {
+ prints("%6d %-14s%s\n", i + 1, pal[i].userid, pal[i].ship);
+ i++;
+ } while (i <= ch);
+
+ outf(FEETER_HELP);
+ move(3 + cur, 0);
+ outc('>');
+ redraw = 0;
+ }
+
+ ch = vkey();
+ switch (ch)
+ {
+ case KEY_RIGHT:
+ case '\n':
+ case ' ':
+ case 'r':
+ i = cur + pageno * XO_TALL;
+ strcpy(str, pal[i].userid);
+ more(fpath, NULL);
+ strcpy(str, fn_dir);
+ redraw = 1;
+ break;
+
+ case Ctrl('P'):
+ if (HAS_PERM(PERM_ALLADMIN))
+ {
+ PAL new;
+
+ memset(&new, 0, sizeof(PAL));
+
+ if (vget(b_lines, 0, "¼ÐÃD¡G", new.ship, sizeof(new.ship), DOECHO) &&
+ vget(b_lines, 0, "ÀɮסG", new.userid, IDLEN + 1, DOECHO))
+ {
+ strcpy(str, new.userid);
+ i = vedit(fpath, 0);
+ strcpy(str, fn_dir);
+ if (!i)
+ {
+ rec_add(fpath, &new, sizeof(PAL));
+ num++;
+ cur = num % XO_TALL; /* ´å¼Ð©ñ¦b·s¥[¤Jªº³o½g */
+ pageno = num / XO_TALL;
+ reload = 1;
+ }
+ }
+ redraw = 1;
+ }
+ break;
+
+ case 'd':
+ if (HAS_PERM(PERM_ALLADMIN))
+ {
+ if (vans(msg_del_ny) == 'y')
+ {
+ i = cur + pageno * XO_TALL;
+ strcpy(str, pal[i].userid);
+ unlink(fpath);
+ strcpy(str, fn_dir);
+ rec_del(fpath, sizeof(PAL), i, NULL);
+ cur = i ? ((i - 1) % XO_TALL) : 0; /* ´å¼Ð©ñ¦b¬å±¼ªº«e¤@½g */
+ reload = 1;
+ }
+ redraw = 1;
+ }
+ break;
+
+ case 'T':
+ if (HAS_PERM(PERM_ALLADMIN))
+ {
+ i = cur + pageno * XO_TALL;
+ if (vget(b_lines, 0, "¼ÐÃD¡G", pal[i].ship, sizeof(pal[0].ship), GCARRY))
+ rec_put(fpath, &pal[i], sizeof(PAL), i, NULL);
+ redraw = 1;
+ }
+ break;
+
+ case 'E':
+ if (!HAS_STATUS(STATUS_EDITHELP)) /* ­Y¬O±q vedit ®É¶i¨Ó help «h¤£¯à¦A vedit */
+ {
+ i = cur + pageno * XO_TALL;
+ strcpy(str, pal[i].userid);
+ vedit(fpath, HAS_PERM(PERM_ALLADMIN) ? 0 : -1);
+ strcpy(str, fn_dir);
+ redraw = 1;
+ }
+ break;
+
+ case 'm':
+ if (HAS_PERM(PERM_ALLADMIN))
+ {
+ char buf[40], ans[5];
+
+ i = cur + pageno * XO_TALL;
+ sprintf(buf, "½Ð¿é¤J²Ä %d ¿ï¶µªº·s¦ì¸m¡G", i + 1);
+ if (vget(b_lines, 0, buf, ans, 5, DOECHO))
+ {
+ redraw = atoi(ans) - 1; /* ­É¥Î redraw */
+ if (redraw < 0)
+ redraw = 0;
+ else if (redraw > num)
+ redraw = num;
+
+ if (redraw != i)
+ {
+ if (!rec_del(fpath, sizeof(PAL), i, NULL))
+ {
+ rec_ins(fpath, &pal[i], sizeof(PAL), redraw, 1);
+ cur = redraw % XO_TALL;
+ pageno = redraw / XO_TALL;
+ }
+ reload = 1;
+ }
+ }
+ redraw = 1;
+ }
+ break;
+
+ default:
+ ch = xo_cursor(ch, pagemax, num, &pageno, &cur, &redraw);
+ break;
+ }
+ } while (ch != 'q');
+
+ free(pal);
+}
+
+
+#include <stdarg.h>
+
+int
+vaHelp(pvar)
+ va_list pvar;
+{
+ char *path;
+ path = va_arg(pvar, char *);
+ do_help(path);
+ return 0;
+}
diff --git a/so/innbbs.c b/so/innbbs.c
new file mode 100644
index 0000000..089430c
--- /dev/null
+++ b/so/innbbs.c
@@ -0,0 +1,701 @@
+/*-------------------------------------------------------*/
+/* innbbs.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : Âà«H³]©w */
+/* create : 04/04/25 */
+/* update : / / */
+/* author : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+
+extern BCACHE *bshm;
+
+
+/* ----------------------------------------------------- */
+/* nodelist.bbs ¤l¨ç¦¡ */
+/* ----------------------------------------------------- */
+
+
+static void
+nl_item(num, nl)
+ int num;
+ nodelist_t *nl;
+{
+ prints("%6d %-13s%-*.*s %s(%d)\n", num,
+ nl->name, d_cols + 45, d_cols + 45, nl->host, nl->xmode & INN_USEIHAVE ? "IHAVE" : "POST", nl->port);
+}
+
+
+static void
+nl_query(nl)
+ nodelist_t *nl;
+{
+ move(3, 0);
+ clrtobot();
+ prints("\n\nÂà«H¯¸¥x¡G%s\n¯¸¥x¦ì§}¡G%s\n¯¸¥x¨ó©w¡G%s(%d)\n³Q Áý «H¡G%s",
+ nl->name, nl->host, nl->xmode & INN_USEIHAVE ? "IHAVE" : "POST", nl->port, nl->xmode & INN_FEEDED ? "¬O" : "§_");
+ vmsg(NULL);
+}
+
+
+static int /* 1:¦¨¥\ 0:¥¢±Ñ */
+nl_add(fpath, old, pos)
+ char *fpath;
+ nodelist_t *old;
+ int pos;
+{
+ nodelist_t nl;
+ int ch, port;
+ char ans[8];
+ char msg1[] = "¨ó©w¡G(1)IHAVE (2)POST [1] ";
+ char msg2[] = "¦¹¯¸¥x·|¥D°ÊÁý«Hµ¹¥»¯¸¶Ü(Y/N)¡H[N] ";
+
+ if (old)
+ memcpy(&nl, old, sizeof(nodelist_t));
+ else
+ memset(&nl, 0, sizeof(nodelist_t));
+
+ if (vget(b_lines, 0, "­^¤å¯¸¦W¡G", nl.name, sizeof(nl.name), GCARRY) &&
+ vget(b_lines, 0, "¯¸§}¡G", nl.host, /* sizeof(nl.host) */ 70, GCARRY))
+ {
+ msg1[24] = (nl.xmode & INN_USEPOST) ? '2' : '1'; /* ·s¼W¸ê®Æ¹w³] INN_HAVE */
+ ch = vans(msg1);
+ if (ch != '1' && ch != '2')
+ ch = msg1[24];
+
+ if (ch == '1')
+ {
+ nl.xmode = INN_USEIHAVE | INN_FEEDED; /* IHAVE ¤@©w¬O³QÁý«H */
+ vget(b_lines, 0, "Port¡G[7777] ", ans, 6, DOECHO);
+ if ((port = atoi(ans)) <= 0)
+ port = 7777;
+ }
+ else /* if (ch == '2') */
+ {
+ nl.xmode = INN_USEPOST;
+ vget(b_lines, 0, "Port¡G[119] ", ans, 6, DOECHO);
+ if ((port = atoi(ans)) <= 0)
+ port = 119;
+
+ msg2[32] = (old && old->xmode & INN_FEEDED) ? 'Y' : 'N'; /* ·s¼W¸ê®Æ¹w³]¤£Áý«H */
+ ch = vans(msg2);
+ if (ch != 'y' && ch != 'n')
+ ch = msg2[32] | 0x20;
+
+ if (ch == 'y')
+ nl.xmode |= INN_FEEDED;
+ }
+ nl.port = port;
+
+ if (old)
+ rec_put(fpath, &nl, sizeof(nodelist_t), pos, NULL);
+ else
+ rec_add(fpath, &nl, sizeof(nodelist_t));
+ return 1;
+ }
+ return 0;
+}
+
+
+static int
+nl_cmp(a, b)
+ nodelist_t *a, *b;
+{
+ /* ¨Ì name ±Æ§Ç */
+ return str_cmp(a->name, b->name);
+}
+
+
+static int
+nl_search(nl, key)
+ nodelist_t *nl;
+ char *key;
+{
+ return (int) (str_str(nl->name, key) || str_str(nl->host, key));
+}
+
+
+/* ----------------------------------------------------- */
+/* newsfeeds.bbs ¤l¨ç¦¡ */
+/* ----------------------------------------------------- */
+
+
+static void
+nf_item(num, nf)
+ int num;
+ newsfeeds_t *nf;
+{
+ int bno;
+ BRD *brd;
+ char outgo, income;
+
+ if ((bno = brd_bno(nf->board)) >= 0)
+ {
+ if (nf->xmode & INN_ERROR)
+ {
+ outgo = income = '?';
+ }
+ else
+ {
+ brd = bshm->bcache + bno;
+ outgo = brd->battr & BRD_NOTRAN ? ' ' : '<';
+ income = nf->xmode & INN_NOINCOME ? ' ': '>';
+ }
+ }
+ else
+ {
+ outgo = income = 'X';
+ }
+
+ prints("%6d %-13s%-*.*s %c-%c %-13s %.7s\n", num,
+ nf->path, d_cols + 32, d_cols + 32, nf->newsgroup, outgo, income, nf->board, nf->charset);
+}
+
+
+static void
+nf_query(nf)
+ newsfeeds_t *nf;
+{
+ nodelist_t nl;
+ int fd;
+ int rc = 0;
+ BRD *brd;
+ char *outgo, *income;
+
+ /* §ä¥X¸Ó¯¸¥x¦b nodelist.bbs ¤¤ªº¸ê°T */
+ if ((fd = open("innd/nodelist.bbs", O_RDONLY)) >= 0)
+ {
+ while (read(fd, &nl, sizeof(nodelist_t)) == sizeof(nodelist_t))
+ {
+ if (!strcmp(nl.name, nf->path))
+ {
+ rc = 1;
+ break;
+ }
+ }
+ close(fd);
+ }
+ if (!rc)
+ {
+ memset(&nl, 0, sizeof(nodelist_t));
+ strcpy(nl.host, "\033[1;33m¦¹¯¸¥x¤£¦b nodelist.bbs ¤¤\033[m");
+ }
+
+ /* ¬ÝªOª¬ºA */
+ if ((rc = brd_bno(nf->board)) >= 0)
+ {
+ brd = bshm->bcache + rc;
+ outgo = brd->battr & BRD_NOTRAN ? "\033[1;33m¤£Âà¥X\033[m" : "Âà¥X";
+ income = nf->xmode & INN_NOINCOME ? "¥B\033[1;33m¤£Âà¶i\033[m" : "¥BÂà¶i";
+ }
+ else
+ {
+ outgo = "\033[1;33m¦¹¬ÝªO¤£¦s¦b\033[m";
+ income = "";
+ }
+
+ move(3, 0);
+ clrtobot();
+ prints("\n\nÂà«H¯¸¥x¡G%s\n¯¸¥x¦ì§}¡G%s\n¯¸¥x¨ó©w¡G%s(%d)\n"
+ "Âà«H¸s²Õ¡G%s%s\n¥»¯¸¬ÝªO¡G%s (%s%s)\n¨Ï¥Î¦r¶°¡G%s",
+ nf->path, nl.host, nl.xmode & INN_USEIHAVE ? "IHAVE" : "POST", nl.port,
+ nf->newsgroup, nf->xmode & INN_ERROR ? " (\033[1;33m¦¹¸s²Õ¤£¦s¦b\033[m)" : "",
+ nf->board, outgo, income, nf->charset);
+ if (rc && !(nl.xmode & INN_FEEDED))
+ prints("\n¥Ø«e½g¼Æ¡G%d", nf->high);
+ vmsg(NULL);
+}
+
+
+static int /* 1:¦¨¥\ 0:¥¢±Ñ */
+nf_add(fpath, old, pos)
+ char *fpath;
+ newsfeeds_t *old;
+ int pos;
+{
+ newsfeeds_t nf;
+ int high;
+ char ans[12];
+ BRD *brd;
+
+ if (old)
+ memcpy(&nf, old, sizeof(newsfeeds_t));
+ else
+ {
+ memset(&nf, 0, sizeof(newsfeeds_t));
+ nf.high = INT_MAX; /* ²Ä¤@¦¸¨ú«H±j­¢ reload */
+ }
+
+ if ((brd = ask_board(nf.board, BRD_L_BIT, NULL)) &&
+ vget(b_lines, 0, "­^¤å¯¸¦W¡G", nf.path, sizeof(nf.path), GCARRY) &&
+ vget(b_lines, 0, "¸s²Õ¡G", nf.newsgroup, /* sizeof(nf.newsgroup) */ 70, GCARRY))
+ {
+ if (!vget(b_lines, 0, "¦r¶° [" MYCHARSET "]¡G", nf.charset, sizeof(nf.charset), GCARRY))
+ str_ncpy(nf.charset, MYCHARSET, sizeof(nf.charset));
+ nf.xmode = (vans("¬O§_Âà¶i(Y/N)¡H[Y] ") == 'n') ? INN_NOINCOME : 0;
+
+ if (vans("¬O§_§ó§ïÂà«Hªº high-number ³]©w¡A³o³]©w¹ï³QÁý«Hªº¸s²ÕµL®Ä(Y/N)¡H[N] ") == 'y')
+ {
+ sprintf(ans, "%d", nf.high);
+ vget(b_lines, 0, "¥Ø«e½g¼Æ¡G", ans, 11, GCARRY);
+ if ((high = atoi(ans)) >= 0)
+ nf.high = high;
+ }
+
+ if (old)
+ rec_put(fpath, &nf, sizeof(newsfeeds_t), pos, NULL);
+ else
+ rec_add(fpath, &nf, sizeof(newsfeeds_t));
+
+ if ((brd->battr & BRD_NOTRAN) && vans("¥»ªOÄݩʥثe¬°¤£Âà¥X¡A¬O§_§ï¬°Âà¥X(Y/N)¡H[Y] ") != 'n')
+ {
+ high = brd - bshm->bcache;
+ brd->battr &= ~BRD_NOTRAN;
+ rec_put(FN_BRD, brd, sizeof(BRD), high, NULL);
+ }
+
+ return 1;
+ }
+ return 0;
+}
+
+
+static int
+nf_cmp(a, b)
+ newsfeeds_t *a, *b;
+{
+ /* path/newsgroup ¥æ¤e¤ñ¹ï */
+ int k = str_cmp(a->path, b->path);
+ return k ? k : str_cmp(a->newsgroup, b->newsgroup);
+}
+
+
+static int
+nf_search(nf, key)
+ newsfeeds_t *nf;
+ char *key;
+{
+ return (int) (str_str(nf->newsgroup, key) || str_str(nf->board, key));
+}
+
+
+/* ----------------------------------------------------- */
+/* ncmperm.bbs ¤l¨ç¦¡ */
+/* ----------------------------------------------------- */
+
+
+static void
+ncm_item(num, ncm)
+ int num;
+ ncmperm_t *ncm;
+{
+ prints("%6d %-*.*s%-23.23s %s\n", num,
+ d_cols + 44, d_cols + 44, ncm->issuer, ncm->type, ncm->perm ? "¡³" : "¢®");
+}
+
+
+static void
+ncm_query(ncm)
+ ncmperm_t *ncm;
+{
+ move(3, 0);
+ clrtobot();
+ prints("\n\nµo¦æ¯¸¥x¡G%s\n¬å«HºØÃþ¡G%s\n¤¹³\\¬å«H¡G%s",
+ ncm->issuer, ncm->type, ncm->perm ? "¡³" : "¢®");
+ vmsg(NULL);
+}
+
+
+static int /* 1:¦¨¥\ 0:¥¢±Ñ */
+ncm_add(fpath, old, pos)
+ char *fpath;
+ ncmperm_t *old;
+ int pos;
+{
+ ncmperm_t ncm;
+
+ if (old)
+ memcpy(&ncm, old, sizeof(ncmperm_t));
+ else
+ memset(&ncm, 0, sizeof(ncmperm_t));
+
+ if (vget(b_lines, 0, "µo¦æ¡G", ncm.issuer, /* sizeof(ncm.issuer) */ 70, GCARRY) &&
+ vget(b_lines, 0, "ºØÃþ¡G", ncm.type, sizeof(ncm.type), GCARRY))
+ {
+ ncm.perm = (vans("¤¹³\\¦¹ NCM message ¬å«H(Y/N)¡H[N] ") == 'y');
+
+ if (old)
+ rec_put(fpath, &ncm, sizeof(ncmperm_t), pos, NULL);
+ else
+ rec_add(fpath, &ncm, sizeof(ncmperm_t));
+ return 1;
+ }
+ return 0;
+}
+
+
+static int
+ncm_cmp(a, b)
+ ncmperm_t *a, *b;
+{
+ /* issuer/type ¥æ¤e¤ñ¹ï */
+ int k = str_cmp(a->issuer, b->issuer);
+ return k ? k : str_cmp(a->type, b->type);
+}
+
+
+static int
+ncm_search(ncm, key)
+ ncmperm_t *ncm;
+ char *key;
+{
+ return (int) (str_str(ncm->issuer, key) || str_str(ncm->type, key));
+}
+
+
+/* ----------------------------------------------------- */
+/* spamrule.bbs ¤l¨ç¦¡ */
+/* ----------------------------------------------------- */
+
+
+static char *
+spam_compare(xmode)
+ int xmode;
+{
+ if (xmode & INN_SPAMADDR)
+ return "§@ªÌ";
+ if (xmode & INN_SPAMNICK)
+ return "¼ÊºÙ";
+ if (xmode & INN_SPAMSUBJECT)
+ return "¼ÐÃD";
+ if (xmode & INN_SPAMPATH)
+ return "¸ô®|";
+ if (xmode & INN_SPAMMSGID)
+ return "MSID";
+ if (xmode & INN_SPAMBODY)
+ return "¥»¤å";
+ if (xmode & INN_SPAMSITE)
+ return "²Õ´";
+ if (xmode & INN_SPAMPOSTHOST)
+ return "¨Ó·½";
+ return "¡H¡H";
+}
+
+
+static void
+spam_item(num, spam)
+ int num;
+ spamrule_t *spam;
+{
+ char *path, *board;
+
+ path = spam->path;
+ board = spam->board;
+ prints("%6d %-13s%-13s[%s] ¥]§t %.*s\n",
+ num, *path ? path : "©Ò¦³¯¸¥x", *board ? board : "©Ò¦³¬ÝªO",
+ spam_compare(spam->xmode), d_cols + 30, spam->detail);
+}
+
+
+static void
+spam_query(spam)
+ spamrule_t *spam;
+{
+ char *path, *board;
+
+ path = spam->path;
+ board = spam->board;
+
+ move(3, 0);
+ clrtobot();
+ prints("\n\n¾A¥Î¯¸¥x¡G%s\n¾A¥Î¬ÝªO¡G%s\n¤ñ¸û¶µ¥Ø¡G%s\n¤ñ¸û¤º®e¡G%s",
+ *path ? path : "©Ò¦³¯¸¥x", *board ? board : "©Ò¦³¬ÝªO", spam_compare(spam->xmode), spam->detail);
+ vmsg("­Yº¡¨¬¦¹³W«h¡A·|³Qµø¬°¼s§i¦ÓµLªkÂà«H¶i¨Ó");
+}
+
+
+static int /* 1:¦¨¥\ 0:¥¢±Ñ */
+spam_add(fpath, old, pos)
+ char *fpath;
+ spamrule_t *old;
+ int pos;
+{
+ spamrule_t spam;
+
+ if (old)
+ memcpy(&spam, old, sizeof(spamrule_t));
+ else
+ memset(&spam, 0, sizeof(spamrule_t));
+
+ vget(b_lines, 0, "­^¤å¯¸¦W¡G", spam.path, sizeof(spam.path), GCARRY);
+ ask_board(spam.board, BRD_L_BIT, NULL);
+
+ switch (vans("¾×«H³W«h 1)§@ªÌ 2)¼ÊºÙ 3)¼ÐÃD 4)¸ô®| 5)MSGID 6)¥»¤å 7)²Õ´ 8)¨Ó·½ [Q] "))
+ {
+ case '1':
+ spam.xmode = INN_SPAMADDR;
+ break;
+ case '2':
+ spam.xmode = INN_SPAMNICK;
+ break;
+ case '3':
+ spam.xmode = INN_SPAMSUBJECT;
+ break;
+ case '4':
+ spam.xmode = INN_SPAMPATH;
+ break;
+ case '5':
+ spam.xmode = INN_SPAMMSGID;
+ break;
+ case '6':
+ spam.xmode = INN_SPAMBODY;
+ break;
+ case '7':
+ spam.xmode = INN_SPAMSITE;
+ break;
+ case '8':
+ spam.xmode = INN_SPAMPOSTHOST;
+ break;
+ default:
+ return 0;
+ }
+
+ if (vget(b_lines, 0, "¥]§t¡G", spam.detail, /* sizeof(spam.detail) */ 70, GCARRY))
+ {
+ if (old)
+ rec_put(fpath, &spam, sizeof(spamrule_t), pos, NULL);
+ else
+ rec_add(fpath, &spam, sizeof(spamrule_t));
+ return 1;
+ }
+ return 0;
+}
+
+
+static int
+spam_cmp(a, b)
+ spamrule_t *a, *b;
+{
+ /* path/board/xmode/detail ¥æ¤e¤ñ¹ï */
+ int i = strcmp(a->path, b->path);
+ int j = strcmp(a->board, b->board);
+ int k = a->xmode - b->xmode;
+ return i ? i : j ? j : k ? k : str_cmp(a->detail, b->detail);
+}
+
+
+static int
+spam_search(spam, key)
+ spamrule_t *spam;
+ char *key;
+{
+ return (int) (str_str(spam->detail, key));
+}
+
+
+/* ----------------------------------------------------- */
+/* Âà«H³]©w¥D¨ç¦¡ */
+/* ----------------------------------------------------- */
+
+
+int
+a_innbbs()
+{
+ int num, pageno, pagemax, redraw, reload;
+ int ch, cur, i, dirty;
+ struct stat st;
+ char *data;
+ int recsiz;
+ char *fpath;
+ char buf[40];
+ void (*item_func)(), (*query_func)();
+ int (*add_func)(), (*sync_func)(), (*search_func)();
+
+ vs_bar("Âà«H³]©w");
+ more("etc/innbbs.hlp", (char *) -1);
+
+ switch (vans("½Ð¿ï¾Ü 1)Âà¤å¯¸¥x¦Cªí 2)Âà¤å¬ÝªO¦Cªí 3)NoCeM¾×¤å³W«h 4)¼s§i¤å¦W³æ¡G[Q] "))
+ {
+ case '1':
+ fpath = "innd/nodelist.bbs";
+ recsiz = sizeof(nodelist_t);
+ item_func = nl_item;
+ query_func = nl_query;
+ add_func = nl_add;
+ sync_func = nl_cmp;
+ search_func = nl_search;
+ break;
+
+ case '2':
+ fpath = "innd/newsfeeds.bbs";
+ recsiz = sizeof(newsfeeds_t);
+ item_func = nf_item;
+ query_func = nf_query;
+ add_func = nf_add;
+ sync_func = nf_cmp;
+ search_func = nf_search;
+ break;
+
+ case '3':
+ fpath = "innd/ncmperm.bbs";
+ recsiz = sizeof(ncmperm_t);
+ item_func = ncm_item;
+ query_func = ncm_query;
+ add_func = ncm_add;
+ sync_func = ncm_cmp;
+ search_func = ncm_search;
+ break;
+
+ case '4':
+ fpath = "innd/spamrule.bbs";
+ recsiz = sizeof(spamrule_t);
+ item_func = spam_item;
+ query_func = spam_query;
+ add_func = spam_add;
+ sync_func = spam_cmp;
+ search_func = spam_search;
+ break;
+
+ default:
+ return 0;
+ }
+
+ dirty = 0; /* 1:¦³·s¼W/§R°£¸ê®Æ */
+ reload = 1;
+ pageno = 0;
+ cur = 0;
+ data = NULL;
+
+ do
+ {
+ if (reload)
+ {
+ if (stat(fpath, &st) == -1)
+ {
+ if (!add_func(fpath, NULL, -1))
+ return 0;
+ dirty = 1;
+ continue;
+ }
+
+ i = st.st_size;
+ num = (i / recsiz) - 1;
+ if (num < 0)
+ {
+ if (!add_func(fpath, NULL, -1))
+ return 0;
+ dirty = 1;
+ continue;
+ }
+
+ if ((ch = open(fpath, O_RDONLY)) >= 0)
+ {
+ data = data ? (char *) realloc(data, i) : (char *) malloc(i);
+ read(ch, data, i);
+ close(ch);
+ }
+
+ pagemax = num / XO_TALL;
+ reload = 0;
+ redraw = 1;
+ }
+
+ if (redraw)
+ {
+ /* itoc.µù¸Ñ: ºÉ¶q°µ±o¹³ xover ®æ¦¡ */
+ vs_head("Âà«H³]©w", str_site);
+ prints(NECKER_INNBBS, d_cols, "");
+
+ i = pageno * XO_TALL;
+ ch = BMIN(num, i + XO_TALL - 1);
+ move(3, 0);
+ do
+ {
+ item_func(i + 1, data + i * recsiz);
+ i++;
+ } while (i <= ch);
+
+ outf(FEETER_INNBBS);
+ move(3 + cur, 0);
+ outc('>');
+ redraw = 0;
+ }
+
+ ch = vkey();
+ switch (ch)
+ {
+ case KEY_RIGHT:
+ case '\n':
+ case ' ':
+ case 'r':
+ i = cur + pageno * XO_TALL;
+ query_func(data + i * recsiz);
+ redraw = 1;
+ break;
+
+ case Ctrl('P'):
+ if (add_func(fpath, NULL, -1))
+ {
+ dirty = 1;
+ num++;
+ cur = num % XO_TALL; /* ´å¼Ð©ñ¦b·s¥[¤Jªº³o½g */
+ pageno = num / XO_TALL;
+ reload = 1;
+ }
+ redraw = 1;
+ break;
+
+ case 'd':
+ if (vans(msg_del_ny) == 'y')
+ {
+ dirty = 1;
+ i = cur + pageno * XO_TALL;
+ rec_del(fpath, recsiz, i, NULL);
+ cur = i ? ((i - 1) % XO_TALL) : 0; /* ´å¼Ð©ñ¦b¬å±¼ªº«e¤@½g */
+ reload = 1;
+ }
+ redraw = 1;
+ break;
+
+ case 'E':
+ i = cur + pageno * XO_TALL;
+ if (add_func(fpath, data + i * recsiz, i))
+ {
+ dirty = 1;
+ reload = 1;
+ }
+ redraw = 1;
+ break;
+
+ case '/':
+ if (vget(b_lines, 0, "ÃöÁä¦r¡G", buf, sizeof(buf), DOECHO))
+ {
+ str_lower(buf, buf);
+ for (i = pageno * XO_TALL + cur + 1; i <= num; i++) /* ±q´å¼Ð¤U¤@­Ó¶}©l§ä */
+ {
+ if (search_func(data + i * recsiz, buf))
+ {
+ pageno = i / XO_TALL;
+ cur = i % XO_TALL;
+ break;
+ }
+ }
+ }
+ redraw = 1;
+ break;
+
+ default:
+ ch = xo_cursor(ch, pagemax, num, &pageno, &cur, &redraw);
+ break;
+ }
+ } while (ch != 'q');
+
+ free(data);
+
+ if (dirty)
+ rec_sync(fpath, recsiz, sync_func, NULL);
+ return 0;
+}
diff --git a/so/manage.c b/so/manage.c
new file mode 100644
index 0000000..44c1e93
--- /dev/null
+++ b/so/manage.c
@@ -0,0 +1,762 @@
+/*-------------------------------------------------------*/
+/* manage.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : ¬ÝªOºÞ²z */
+/* create : 95/03/29 */
+/* update : 96/04/05 */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+
+extern BCACHE *bshm;
+
+
+#ifdef HAVE_TERMINATOR
+/* ----------------------------------------------------- */
+/* ¯¸ªø¥\¯à : ©Ø·¬¸¨¸­±Ù */
+/* ----------------------------------------------------- */
+
+
+extern char xo_pool[];
+
+
+#define MSG_TERMINATOR "¡m©Ø·¬¸¨¸­±Ù¡n"
+
+int
+post_terminator(xo) /* Thor.980521: ²×·¥¤å³¹§R°£¤jªk */
+ XO *xo;
+{
+ int mode, type;
+ HDR *hdr;
+ char keyOwner[80], keyTitle[TTLEN + 1], buf[80];
+
+ if (!HAS_PERM(PERM_ALLBOARD))
+ return XO_FOOT;
+
+ mode = vans(MSG_TERMINATOR "§R°£ (1)¥»¤å§@ªÌ (2)¥»¤å¼ÐÃD (3)¦Û©w¡H[Q] ") - '0';
+
+ if (mode == 1)
+ {
+ hdr = (HDR *) xo_pool + (xo->pos - xo->top);
+ strcpy(keyOwner, hdr->owner);
+ }
+ else if (mode == 2)
+ {
+ hdr = (HDR *) xo_pool + (xo->pos - xo->top);
+ strcpy(keyTitle, str_ttl(hdr->title)); /* ®³±¼ Re: */
+ }
+ else if (mode == 3)
+ {
+ if (!vget(b_lines, 0, "§@ªÌ¡G", keyOwner, 73, DOECHO))
+ mode ^= 1;
+ if (!vget(b_lines, 0, "¼ÐÃD¡G", keyTitle, TTLEN + 1, DOECHO))
+ mode ^= 2;
+ }
+ else
+ {
+ return XO_FOOT;
+ }
+
+ type = vans(MSG_TERMINATOR "§R°£ (1)Âà«HªO (2)«DÂà«HªO (3)©Ò¦³¬ÝªO¡H[Q] ");
+ if (type < '1' || type > '3')
+ return XO_FOOT;
+
+ sprintf(buf, "§R°£%s¡G%.35s ©ó%sªO¡A½T©w¶Ü(Y/N)¡H[N] ",
+ mode == 1 ? "§@ªÌ" : mode == 2 ? "¼ÐÃD" : "±ø¥ó",
+ mode == 1 ? keyOwner : mode == 2 ? keyTitle : "¦Û©w",
+ type == '1' ? "Âà«H" : type == '2' ? "«DÂà«H" : "©Ò¦³¬Ý");
+
+ if (vans(buf) == 'y')
+ {
+ BRD *bhdr, *head, *tail;
+ char tmpboard[BNLEN + 1];
+
+ /* Thor.980616: °O¤U currboard¡A¥H«K´_­ì */
+ strcpy(tmpboard, currboard);
+
+ head = bhdr = bshm->bcache;
+ tail = bhdr + bshm->number;
+ do /* ¦Ü¤Ö¦³ note ¤@ªO */
+ {
+ int fdr, fsize, xmode;
+ FILE *fpw;
+ char fpath[64], fnew[64], fold[64];
+ HDR *hdr;
+
+ xmode = head->battr;
+ if ((type == '1' && (xmode & BRD_NOTRAN)) || (type == '2' && !(xmode & BRD_NOTRAN)))
+ continue;
+
+ /* Thor.980616: §ó§ï currboard¡A¥H cancel post */
+ strcpy(currboard, head->brdname);
+
+ sprintf(buf, MSG_TERMINATOR "¬ÝªO¡G%s \033[5m...\033[m", currboard);
+ outz(buf);
+ refresh();
+
+ brd_fpath(fpath, currboard, fn_dir);
+
+ if ((fdr = open(fpath, O_RDONLY)) < 0)
+ continue;
+
+ if (!(fpw = f_new(fpath, fnew)))
+ {
+ close(fdr);
+ continue;
+ }
+
+ fsize = 0;
+ mgets(-1);
+ while (hdr = mread(fdr, sizeof(HDR)))
+ {
+ xmode = hdr->xmode;
+
+ if ((xmode & POST_MARKED) ||
+ ((mode & 1) && strcmp(keyOwner, hdr->owner)) ||
+ ((mode & 2) && strcmp(keyTitle, str_ttl(hdr->title))))
+ {
+ if ((fwrite(hdr, sizeof(HDR), 1, fpw) != 1))
+ {
+ fclose(fpw);
+ close(fdr);
+ goto contWhileOuter;
+ }
+ fsize++;
+ }
+ else
+ {
+ /* ­Y¬°¬ÝªO´N³s½u¬å«H */
+
+ cancel_post(hdr);
+ hdr_fpath(fold, fpath, hdr);
+ unlink(fold);
+ }
+ }
+ close(fdr);
+ fclose(fpw);
+
+ sprintf(fold, "%s.o", fpath);
+ rename(fpath, fold);
+ if (fsize)
+ rename(fnew, fpath);
+ else
+ contWhileOuter:
+ unlink(fnew);
+ } while (++head < tail);
+
+ /* ÁÙ­ì currboard */
+ strcpy(currboard, tmpboard);
+ return XO_LOAD;
+ }
+
+ return XO_FOOT;
+}
+#endif /* HAVE_TERMINATOR */
+
+
+/* ----------------------------------------------------- */
+/* ªO¥D¥\¯à : ­×§ïªO¦W */
+/* ----------------------------------------------------- */
+
+
+static int
+post_brdtitle(xo)
+ XO *xo;
+{
+ BRD *oldbrd, newbrd;
+
+ oldbrd = bshm->bcache + currbno;
+ memcpy(&newbrd, oldbrd, sizeof(BRD));
+
+ /* itoc.µù¸Ñ: ¨ä¹ê©I¥s brd_title(bno) ´N¥i¥H¤F¡A¨S®t¡AÆZ·F¤@¤U¦n¤F :p */
+ if (vans("¬O§_­×§ï¤¤¤åªO¦W±Ô­z(Y/N)¡H[N] ") == 'y')
+ {
+ vget(b_lines, 0, "¬ÝªO¥DÃD¡G", newbrd.title, BTLEN + 1, GCARRY);
+
+ if (memcmp(&newbrd, oldbrd, sizeof(BRD)) && vans(msg_sure_ny) == 'y')
+ {
+ memcpy(oldbrd, &newbrd, sizeof(BRD));
+ rec_put(FN_BRD, &newbrd, sizeof(BRD), currbno, NULL);
+ return XO_HEAD; /* itoc.011125: ­n­«Ã¸¤¤¤åªO¦W */
+ }
+ }
+
+ return XO_FOOT;
+}
+
+
+/* ----------------------------------------------------- */
+/* ªO¥D¥\¯à : ­×§ï¶iªOµe­± */
+/* ----------------------------------------------------- */
+
+
+static int
+post_memo_edit(xo)
+ XO *xo;
+{
+ int mode;
+ char fpath[64];
+
+ mode = vans("¶iªOµe­± (D)§R°£ (E)­×§ï (Q)¨ú®ø¡H[E] ");
+
+ if (mode != 'q')
+ {
+ brd_fpath(fpath, currboard, fn_note);
+
+ if (mode == 'd')
+ {
+ unlink(fpath);
+ return XO_FOOT;
+ }
+
+ if (vedit(fpath, 0)) /* Thor.981020: ª`·N³Qtalkªº°ÝÃD */
+ vmsg(msg_cancel);
+ return XO_HEAD;
+ }
+ return XO_FOOT;
+}
+
+#ifdef POST_PREFIX
+/* ----------------------------------------------------- */
+/* ªO¥D¥\¯à : ­×§ïµo¤åÃþ§O */
+/* ----------------------------------------------------- */
+
+
+static int
+post_prefix_edit(xo)
+ XO *xo;
+{
+#define NUM_PREFIX 9
+ int i;
+ FILE *fp;
+ char fpath[64], buf[20], prefix[NUM_PREFIX][20], *menu[NUM_PREFIX + 3];
+ char *prefix_def[NUM_PREFIX] = /* ¹w³]ªºÃþ§O */
+ {
+ "[¤½§i]", "[´ú¸Õ]", "[¶¢²á]", "[Äé¤ô]", "[µL²á]",
+ "[¥´²V]", "[¬ö©À]", "[³Ð§@]", "[¤å¥ó]"
+ };
+ char modified=0; /* lantw44: §PÂ_¦³µL­×§ï¹L */
+
+ if (!(bbstate & STAT_BOARD))
+ return XO_NONE;
+
+ i = vans("Ãþ§O (D)§R°£ (E)­×§ï (Q)¨ú®ø¡H [E] ");
+
+ if (i == 'q')
+ return XO_FOOT;
+
+ brd_fpath(fpath, currboard, "prefix");
+
+ if (i == 'd')
+ {
+ unlink(fpath);
+ return XO_FOOT;
+ }
+
+ i = 0;
+
+ if (fp = fopen(fpath, "r"))
+ {
+ for (; i < NUM_PREFIX; i++)
+ {
+ //if (fscanf(fp, "%10s", buf) != 1)
+ if (fgets(buf, 11, fp) == NULL)
+ break;
+ buf[strlen(buf)-1]='\0'; /* lantw44:¦Y±¼³Ì«á¤@­Ó\n */
+ sprintf(prefix[i], "%d.%s", i + 1, buf);
+ }
+ fclose(fp);
+ }
+
+ /* ¶ñº¡¦Ü NUM_PREFIX ­Ó */
+ for (; i < NUM_PREFIX; i++)
+ sprintf(prefix[i], "%d.%s", i + 1, prefix_def[i]);
+
+ menu[0] = "10";
+ for (i = 1; i <= NUM_PREFIX; i++)
+ menu[i] = prefix[i - 1];
+ menu[NUM_PREFIX + 1] = "0.Â÷¶}";
+ menu[NUM_PREFIX + 2] = NULL;
+
+ do
+ {
+ /* ¦b popupmenu ¸Ì­±«ö ¥ªÁä Â÷¶} */
+ i = pans(3, 20, "¤å³¹Ãþ§O", menu) - '0';
+ if (i >= 1 && i <= NUM_PREFIX)
+ {
+ strcpy(buf, prefix[i - 1] + 2);
+ if (vget(b_lines, 0, "Ãþ§O¡G", buf, 10, GCARRY)){
+ if(strcmp(prefix[i - 1] + 2,buf)){
+ modified=1;
+ }
+ strcpy(prefix[i - 1] + 2, buf);
+ }
+ }
+ } while (i);
+
+ if(!modified || vans("½T©w­nÀx¦sÀÉ®×¶Ü(Y/N)¡H[Y] ") == 'n'){
+ vmsg("­ì«Ê¤£°Ê");
+ return XO_FOOT;
+ }
+
+ if (fp = fopen(fpath, "w"))
+ {
+ for (i = 0; i < NUM_PREFIX; i++)
+ fprintf(fp, "%s\n", prefix[i] + 2);
+ fclose(fp);
+ vmsg("§ó·s§¹²¦");
+ }else{
+ vmsg("ÀÉ®×¼g¤J¥¢±Ñ");
+ }
+
+ return XO_FOOT;
+}
+#endif /* POST_PREFIX */
+/* ----------------------------------------------------- */
+/* ªO¥D¥\¯à : ­×§ïµo¤åºõ»â */
+/* ----------------------------------------------------- */
+
+
+static int
+post_postlaw_edit(xo) /* ªO¥D¦Û©w¤å³¹µoªíºõ»â */
+ XO *xo;
+{
+ int mode;
+ char fpath[64];
+
+ mode = vans("¤å³¹µoªíºõ»â (D)§R°£ (E)­×§ï (Q)¨ú®ø¡H[E] ");
+
+ if (mode != 'q')
+ {
+ brd_fpath(fpath, currboard, FN_POSTLAW);
+
+ if (mode == 'd')
+ {
+ unlink(fpath);
+ return XO_FOOT;
+ }
+ if (vedit(fpath, 0)) /* Thor.981020: ª`·N³Qtalkªº°ÝÃD */
+ vmsg(msg_cancel);
+ return XO_HEAD;
+ }
+ return XO_FOOT;
+}
+
+
+/* ----------------------------------------------------- */
+/* ªO¥D¥\¯à : ¬ÝªOÄÝ©Ê */
+/* ----------------------------------------------------- */
+
+
+#ifdef HAVE_SCORE
+static int
+post_battr_noscore(xo)
+ XO *xo;
+{
+ BRD *oldbrd, newbrd;
+
+ oldbrd = bshm->bcache + currbno;
+ memcpy(&newbrd, oldbrd, sizeof(BRD));
+
+ switch (vans("¶}©ñµû¤À (1)¤¹³\\ (2)¤£³\\ (Q)¨ú®ø¡H[Q] "))
+ {
+ case '1':
+ newbrd.battr &= ~BRD_NOSCORE;
+ break;
+ case '2':
+ newbrd.battr |= BRD_NOSCORE;
+ break;
+ default:
+ return XO_FOOT;
+ }
+
+ if (memcmp(&newbrd, oldbrd, sizeof(BRD)) && vans(msg_sure_ny) == 'y')
+ {
+ memcpy(oldbrd, &newbrd, sizeof(BRD));
+ rec_put(FN_BRD, &newbrd, sizeof(BRD), currbno, NULL);
+ }
+
+ return XO_FOOT;
+}
+#endif /* HAVE_SCORE */
+
+
+#ifdef ANTI_PHONETIC
+static int
+post_battr_nophonetic(xo)
+ XO *xo;
+{
+ BRD *oldbrd, newbrd;
+
+ oldbrd = bshm->bcache + currbno;
+ memcpy(&newbrd, oldbrd, sizeof(BRD));
+
+ switch (vans("1)¥i¥H¥Îª`­µ¤å 2)¤£¥i¥Îª`­µ¤å [Q] "))
+ {
+ case '1':
+ newbrd.battr &= ~BRD_NOPHONETIC;
+ break;
+ case '2':
+ newbrd.battr |= BRD_NOPHONETIC;
+ break;
+ default:
+ return XO_FOOT;
+ }
+
+ if (memcmp(&newbrd, oldbrd, sizeof(BRD)) && vans(msg_sure_ny) == 'y')
+ {
+ memcpy(oldbrd, &newbrd, sizeof(BRD));
+ rec_put(FN_BRD, &newbrd, sizeof(BRD), currbno, NULL);
+ }
+
+ return XO_FOOT;
+}
+#endif /* ANTI_PHONETIC */
+/* ----------------------------------------------------- */
+/* ªO¥D¥\¯à : ­×§ïªO¥D¦W³æ */
+/* ----------------------------------------------------- */
+
+
+static int
+post_changeBM(xo)
+ XO *xo;
+{
+ char buf[80], userid[IDLEN + 2], *blist;
+ BRD *oldbrd, newbrd;
+ ACCT acct;
+ int BMlen, len;
+
+ oldbrd = bshm->bcache + currbno;
+
+ if (strcmp(oldbrd->class, "­Ó¤H") &&
+ strcmp(oldbrd->class, "ªÀ¹Î") &&
+ strcmp(oldbrd->class, "¹ÎÅé"))
+ {
+ vmsg ("¤½¶}ªO¤£µ¹§ï³é");
+ return XO_FOOT;
+ }
+
+ blist = oldbrd->BM;
+ if (is_bm(blist, cuser.userid) != 1) /* ¥u¦³¥¿ªO¥D¥i¥H³]©wªO¥D¦W³æ */
+ return XO_FOOT;
+
+ memcpy(&newbrd, oldbrd, sizeof(BRD));
+
+ move(3, 0);
+ clrtobot();
+
+ move(8, 0);
+ prints("¥Ø«eªO¥D¬° %s\n½Ð¿é¤J·sªºªO¥D¦W³æ¡A©Î«ö [Return] ¤£§ï", oldbrd->BM);
+
+ strcpy(buf, oldbrd->BM);
+ BMlen = strlen(buf);
+
+ while (vget(10, 0, "½Ð¿é¤J°ÆªO¥D¡Aµ²§ô½Ð«ö Enter¡A²M±¼©Ò¦³°ÆªO¥D½Ð¥´¡uµL¡v¡G", userid, IDLEN + 1, DOECHO))
+ {
+ if (!strcmp(userid, "µL"))
+ {
+ strcpy(buf, cuser.userid);
+ BMlen = strlen(buf);
+ }
+ else if (is_bm(buf, userid)) /* §R°£Â¦³ªºªO¥D */
+ {
+ len = strlen(userid);
+ if (!str_cmp(cuser.userid, userid))
+ {
+ vmsg("¤£¥i¥H±N¦Û¤v²¾¥XªO¥D¦W³æ");
+ continue;
+ }
+ else if (!str_cmp(buf + BMlen - len, userid)) /* ¦W³æ¤W³Ì«á¤@¦ì¡AID «á­±¤£±µ '/' */
+ {
+ buf[BMlen - len - 1] = '\0'; /* §R°£ ID ¤Î«e­±ªº '/' */
+ len++;
+ }
+ else /* ID «á­±·|±µ '/' */
+ {
+ str_lower(userid, userid);
+ strcat(userid, "/");
+ len++;
+ blist = str_str(buf, userid);
+ strcpy(blist, blist + len);
+ }
+ BMlen -= len;
+ }
+ else if (acct_load(&acct, userid) >= 0 && !is_bm(buf, userid)) /* ¿é¤J·sªO¥D */
+ {
+ len = strlen(userid) + 1; /* '/' + userid */
+ if (BMlen + len > BMLEN)
+ {
+ vmsg("ªO¥D¦W³æ¹Lªø¡AµLªk±N³o ID ³]¬°ªO¥D");
+ continue;
+ }
+ sprintf(buf + BMlen, "/%s", acct.userid);
+ BMlen += len;
+
+ acct_setperm(&acct, PERM_BM, 0);
+ }
+ move(8, 0);
+ prints("¥Ø«eªO¥D¬° %s", buf);
+ clrtoeol();
+ }
+ strcpy(newbrd.BM, buf);
+
+ if (memcmp(&newbrd, oldbrd, sizeof(BRD)) && vans(msg_sure_ny) == 'y')
+ {
+ memcpy(oldbrd, &newbrd, sizeof(BRD));
+ rec_put(FN_BRD, &newbrd, sizeof(BRD), currbno, NULL);
+
+ sprintf(currBM, "ªO¥D¡G%s", newbrd.BM);
+ return XO_HEAD; /* ­n­«Ã¸ÀÉÀYªºªO¥D */
+ }
+
+ return XO_BODY;
+}
+
+
+#ifdef HAVE_MODERATED_BOARD
+/* ----------------------------------------------------- */
+/* ªO¥D¥\¯à : ¬ÝªOÅv­­ */
+/* ----------------------------------------------------- */
+
+
+static int
+post_brdlevel(xo)
+ XO *xo;
+{
+ BRD *oldbrd, newbrd;
+
+ oldbrd = bshm->bcache + currbno;
+ memcpy(&newbrd, oldbrd, sizeof(BRD));
+
+ switch (vans("1)¤½¶}¬ÝªO 2)¯µ±K¬ÝªO 3)¦n¤Í¬ÝªO¡H[Q] "))
+ {
+ case '1': /* ¤½¶}¬ÝªO */
+ newbrd.readlevel = 0;
+ newbrd.postlevel = PERM_POST;
+ newbrd.battr &= ~(BRD_NOSTAT | BRD_NOVOTE);
+ break;
+
+ case '2': /* ¯µ±K¬ÝªO */
+ newbrd.readlevel = PERM_SYSOP;
+ newbrd.postlevel = 0;
+ newbrd.battr |= (BRD_NOSTAT | BRD_NOVOTE);
+ break;
+
+ case '3': /* ¦n¤Í¬ÝªO */
+ newbrd.readlevel = PERM_BOARD;
+ newbrd.postlevel = 0;
+ newbrd.battr |= (BRD_NOSTAT | BRD_NOVOTE);
+ break;
+
+ default:
+ return XO_FOOT;
+ }
+
+ if (memcmp(&newbrd, oldbrd, sizeof(BRD)) && vans(msg_sure_ny) == 'y')
+ {
+ memcpy(oldbrd, &newbrd, sizeof(BRD));
+ rec_put(FN_BRD, &newbrd, sizeof(BRD), currbno, NULL);
+ }
+
+ return XO_FOOT;
+}
+#endif /* HAVE_MODERATED_BOARD */
+
+
+#ifdef HAVE_MODERATED_BOARD
+/* ----------------------------------------------------- */
+/* ªO¤Í¦W³æ¡Gmoderated board */
+/* ----------------------------------------------------- */
+
+
+static void
+bpal_cache(fpath)
+ char *fpath;
+{
+ BPAL *bpal;
+
+ bpal = bshm->pcache + currbno;
+ bpal->pal_max = image_pal(fpath, bpal->pal_spool);
+}
+
+
+extern XZ xz[];
+
+
+static int
+XoBM(xo)
+ XO *xo;
+{
+ XO *xt;
+ char fpath[64];
+
+ brd_fpath(fpath, currboard, fn_pal);
+ xz[XZ_PAL - XO_ZONE].xo = xt = xo_new(fpath);
+ xt->key = PALTYPE_BPAL;
+ xover(XZ_PAL); /* Thor: ¶ixover«e, pal_xo ¤@©w­n ready */
+
+ /* build userno image to speed up, maybe upgreade to shm */
+
+ bpal_cache(fpath);
+
+ free(xt);
+
+ return XO_INIT;
+}
+#endif /* HAVE_MODERATED_BOARD */
+
+static int
+post_usies(xo)
+ XO *xo;
+{
+ char fpath[64];
+
+ brd_fpath(fpath, currboard, "usies");
+ if (more(fpath, (char *) -1) >= 0 &&
+ vans("½Ð°Ý¬O§_§R°£³o¨Ç¬ÝªO¾\\Ū°O¿ý(Y/N)¡H[N] ") == 'y')
+ unlink(fpath);
+
+ return XO_HEAD;
+}
+
+static int post_extern(xo)
+ XO* xo;
+{
+ char buf[80];
+ BRD *oldbrd, newbrd;
+ oldbrd = bshm->bcache + currbno;
+ memcpy(&newbrd, oldbrd, sizeof(BRD));
+ sprintf(buf,"¬O§_±N¶Ç°e¨ì %s.brd@%s ªº¤å³¹±i¶K©ó¥»¬ÝªO(Y/N)¡H[Q] ",oldbrd->brdname,MYHOSTNAME);
+ switch(vans(buf)){
+ case 'n':
+ newbrd.battr |= BRD_LOCAL;
+ break;
+ case 'y':
+ newbrd.battr &= ~(BRD_LOCAL);
+ break;
+ default:
+ return XO_FOOT;
+ }
+ if (memcmp(&newbrd, oldbrd, sizeof(BRD)) && vans(msg_sure_ny) == 'y')
+ {
+ memcpy(oldbrd, &newbrd, sizeof(BRD));
+ rec_put(FN_BRD, &newbrd, sizeof(BRD), currbno, NULL);
+ }
+ return XO_FOOT;
+}
+
+static int post_lock(xo)
+ XO* xo;
+{
+ BRD *brd;
+ brd = bshm->bcache + currbno;
+ char fpath[64];
+ brd_fpath(fpath, brd->brdname, fn_lock);
+ brd_editlock(fpath);
+ return XO_FOOT;
+}
+
+/* ----------------------------------------------------- */
+/* ªO¥D¿ï³æ */
+/* ----------------------------------------------------- */
+
+
+int
+post_manage(xo)
+ XO *xo;
+{
+#ifdef POPUP_ANSWER
+ char *menu[] =
+ {
+ "BQ",
+ "BTitle ­×§ï¬ÝªO¥DÃD",
+ "WMemo ½s¿è¶iªOµe­±",
+#ifdef POST_PREFIX
+ "RPrefix ½s¿è¤å³¹Ãþ§O",
+#endif
+ "PostLaw ½s¿èµo¤åºõ»â",
+ "Manager ¼W´î°ÆªO¥D",
+ "ExtPost ³]©w¯¸¥~±H«Hµo¤å",
+ "ZLock ºÞ²z¬ÝªOÂê©wª¬ºA",
+ "Usies Æ[¹î¬ÝªO¾\\Ū°O¿ý",
+# ifdef HAVE_SCORE
+ "Score ³]©w¥i§_µû¤À",
+# endif
+# ifdef ANTI_PHONETIC
+ "Juyinwenª`­µ¤å³]©w",
+# endif
+# ifdef HAVE_MODERATED_BOARD
+ "Level ¤½¶}/¦n¤Í/¯µ±K",
+ "OPal ªO¤Í¦W³æ",
+# endif
+ NULL
+ };
+#else
+ char *menu = "¡· ªO¥D¿ï³æ (B)¥DÃD (W)¶iªO (R)Ãþ§O (P)ºõ»â (M)°ÆªO (E)¦¬«H (Z)Âê©w (U)°O¿ý"
+# ifdef HAVE_SCORE
+ " (S)µû¤À"
+# endif
+# ifdef ANTI_PHONETIC
+ " (P)ª`­µ"
+# endif
+# ifdef HAVE_MODERATED_BOARD
+ " (L)Åv­­ (O)ªO¤Í"
+# endif
+ "¡H[Q] ";
+#endif
+
+ if (!(bbstate & STAT_BOARD))
+ return XO_NONE;
+
+#ifdef POPUP_ANSWER
+ switch (pans(3, 20, "ªO¥D¿ï³æ", menu))
+#else
+ switch (vans(menu))
+#endif
+ {
+ case 'b':
+ return post_brdtitle(xo);
+
+ case 'w':
+ return post_memo_edit(xo);
+#ifdef POST_PREFIX
+ case 'r':
+ return post_prefix_edit(xo);
+#endif
+ case 'p':
+ return post_postlaw_edit(xo);
+
+ case 'm':
+ return post_changeBM(xo);
+
+ case 'e':
+ return post_extern(xo);
+
+ case 'z':
+ return post_lock(xo);
+
+ case 'u':
+ return post_usies(xo);
+
+#ifdef HAVE_SCORE
+ case 's':
+ return post_battr_noscore(xo);
+#endif
+#ifdef ANTI_PHONETIC
+ case 'j':
+ return post_battr_nophonetic(xo);
+#endif
+#ifdef HAVE_MODERATED_BOARD
+ case 'l':
+ return post_brdlevel(xo);
+
+ case 'o':
+ return XoBM(xo);
+#endif
+ }
+
+ return XO_FOOT;
+}
+
diff --git a/so/newbrd.c b/so/newbrd.c
new file mode 100644
index 0000000..d5e0abf
--- /dev/null
+++ b/so/newbrd.c
@@ -0,0 +1,906 @@
+/*-------------------------------------------------------*/
+/* newbrd.c ( YZU_CSE WindTop BBS ) */
+/*-------------------------------------------------------*/
+/* target : ³s¸p¥\¯à */
+/* create : 00/01/02 */
+/* update : 02/04/29 */
+/*-------------------------------------------------------*/
+/* run/newbrd/_/.DIR - newbrd control header */
+/* run/newbrd/_/@/@_ - newbrd description file */
+/* run/newbrd/_/@/G_ - newbrd voted id loG file */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+
+#ifdef HAVE_COSIGN
+
+extern XZ xz[];
+extern char xo_pool[];
+extern BCACHE *bshm; /* itoc.010805: ¶}·sªO¥Î */
+
+static int nbrd_add();
+static int nbrd_body();
+static int nbrd_head();
+
+static char *split_line = "\033[33m¢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";
+
+
+typedef struct
+{
+ char userid[IDLEN + 1];
+ char email[60];
+} LOG;
+
+
+static int
+cmpbtime(nbrd)
+ NBRD *nbrd;
+{
+ return nbrd->btime == currchrono;
+}
+
+
+static char
+nbrd_attr(nbrd)
+ NBRD *nbrd;
+{
+ int xmode = nbrd->mode;
+
+ /* µ§¹º¶V¤Öªº¡A¶V¶É¦Vµ²®× */
+ if (xmode & NBRD_FINISH)
+ return ' ';
+ if (xmode & NBRD_END)
+ return '-';
+#ifdef SYSOP_START_COSIGN
+ if (xmode & NBRD_START)
+ return '+';
+ else
+ return 'x';
+#else
+ return '+';
+#endif
+}
+
+
+static int
+nbrd_stamp(folder, nbrd, fpath)
+ char *folder;
+ NBRD *nbrd;
+ char *fpath;
+{
+ char *fname;
+ char *family = NULL;
+ int rc;
+ int token;
+
+ fname = fpath;
+ while (rc = *folder++)
+ {
+ *fname++ = rc;
+ if (rc == '/')
+ family = fname;
+ }
+
+ fname = family;
+ *family++ = '@';
+
+ token = time(0);
+
+ archiv32(token, family);
+
+ rc = open(fpath, O_WRONLY | O_CREAT | O_EXCL, 0600);
+ nbrd->btime = token;
+ str_stamp(nbrd->date, &nbrd->btime);
+ strcpy(nbrd->xname, fname);
+
+ return rc;
+}
+
+
+static void
+nbrd_fpath(fpath, folder, nbrd)
+ char *fpath;
+ char *folder;
+ NBRD *nbrd;
+{
+ char *str;
+ int cc;
+
+ while (cc = *folder++)
+ {
+ *fpath++ = cc;
+ if (cc == '/')
+ str = fpath;
+ }
+ strcpy(str, nbrd->xname);
+}
+
+
+static int
+nbrd_init(xo)
+ XO *xo;
+{
+ xo_load(xo, sizeof(NBRD));
+ return nbrd_head(xo);
+}
+
+
+static int
+nbrd_load(xo)
+ XO *xo;
+{
+ xo_load(xo, sizeof(NBRD));
+ return nbrd_body(xo);
+}
+
+
+static void
+nbrd_item(num, nbrd)
+ int num;
+ NBRD *nbrd;
+{
+ prints("%6d %c %-5s %-13s [%s] %.*s\n",
+ num, nbrd_attr(nbrd), nbrd->date + 3, nbrd->owner,
+ (nbrd->mode & NBRD_NEWBOARD) ? nbrd->brdname : "\033[1;33m¥»¯¸¤½§ë\033[m", d_cols + 20, nbrd->title);
+}
+
+
+static int
+nbrd_body(xo)
+ XO *xo;
+{
+ NBRD *nbrd;
+ int num, max, tail;
+
+ max = xo->max;
+ if (max <= 0)
+ {
+ if (vans("­n·s¼W³s¸p¶µ¥Ø¶Ü(Y/N)¡H[N] ") == 'y')
+ return nbrd_add(xo);
+ return XO_QUIT;
+ }
+
+ nbrd = (NBRD *) xo_pool;
+ num = xo->top;
+ tail = num + XO_TALL;
+
+ if (max > tail)
+ max = tail;
+
+ move(3, 0);
+ do
+ {
+ nbrd_item(++num, nbrd++);
+ } while (num < max);
+ clrtobot();
+
+ return XO_FOOT;
+}
+
+
+static int
+nbrd_head(xo)
+ XO *xo;
+{
+ vs_head("³s¸p¨t²Î", str_site);
+ prints(NECKER_COSIGN, d_cols, "");
+ return nbrd_body(xo);
+}
+
+
+static int
+nbrd_find(fpath, brdname)
+ char *fpath, *brdname;
+{
+ NBRD old;
+ int fd;
+ int rc = 0;
+
+ if ((fd = open(fpath, O_RDONLY)) >= 0)
+ {
+ while (read(fd, &old, sizeof(NBRD)) == sizeof(NBRD))
+ {
+ if (!str_cmp(old.brdname, brdname) && !(old.mode & NBRD_FINISH))
+ {
+ rc = 1;
+ break;
+ }
+ }
+ close(fd);
+ }
+ return rc;
+}
+
+
+static int
+nbrd_add(xo)
+ XO *xo;
+{
+ int fd, ans, days, numbers;
+ char *dir, fpath[64], path[64];
+ char *brdname, *class, *title;
+ FILE *fp;
+ NBRD nbrd;
+
+ if (HAS_PERM(PERM_ALLADMIN))
+ {
+ ans = vans("³s¸p¼Ò¦¡ 1)¶}·sªO 2)°O¦W 3)µL°O¦W¡G[Q] ");
+ if (ans < '1' || ans > '3')
+ return xo->max ? XO_FOOT : nbrd_body(xo); /* itoc.020122: ¦pªG¨S¦³¥ô¦ó³s¸p¡A­n¦^¨ì nbrd_body() */
+ /* itoc.030613: ¨ä¹ê¤U­±ªº return XO_FOOT; ¤]À³¸Ó³o¼Ë§ï */
+ }
+ else if (HAS_PERM(PERM_POST))
+ {
+ /* ¤@¬q¨Ï¥ÎªÌ¥u¯à¶}·sªO³s¸p */
+ ans = '1';
+ }
+ else
+ {
+ vmsg("¹ï¤£°_¡A¥»¬ÝªO¬O°ßŪªº");
+ return XO_FOOT;
+ }
+
+ memset(&nbrd, 0, sizeof(NBRD));
+
+ brdname = nbrd.brdname;
+ class = nbrd.class;
+ title = nbrd.title;
+
+ if (ans == '1') /* ·sªO³s¸p */
+ {
+ if (!vget(b_lines, 0, "­^¤åªO¦W¡G", brdname, sizeof(nbrd.brdname), DOECHO))
+ return XO_FOOT;
+
+ if (brd_bno(brdname) >= 0 || !valid_brdname(brdname))
+ {
+ vmsg("¤w¦³¦¹ªO©ÎªO¦W¤£¦Xªk");
+ return XO_FOOT;
+ }
+ if (nbrd_find(xo->dir, brdname))
+ {
+ vmsg("¥¿¦b³s¸p¤¤");
+ return XO_FOOT;
+ }
+
+ if (!vget(b_lines, 0, "¬ÝªO¤ÀÃþ¡G", class, sizeof(nbrd.class), DOECHO) ||
+ !vget(b_lines, 0, "¬ÝªO¥DÃD¡G", title, sizeof(nbrd.title), DOECHO))
+ return XO_FOOT;
+
+ days = NBRD_DAY_BRD;
+ numbers = NBRD_NUM_BRD;
+
+#ifdef SYSOP_START_COSIGN
+ nbrd.mode = NBRD_NEWBOARD;
+#else
+ nbrd.mode = NBRD_NEWBOARD | NBRD_START;
+#endif
+ }
+ else /* ¨ä¥L³s¸p */
+ {
+ char tmp[8];
+
+ if (!vget(b_lines, 0, "³s¸p¥DÃD¡G", title, sizeof(nbrd.title), DOECHO))
+ return XO_FOOT;
+
+ /* ³s¸p¤é´Á³Ì¦h 30 ¤Ñ¡A³s¸p¤H¼Æ³Ì¦h 500 ¤H */
+ if (!vget(b_lines, 0, "³s¸p¤Ñ¼Æ¡G", tmp, 5, DOECHO))
+ return XO_FOOT;
+ days = atoi(tmp);
+ if (days > 30 || days < 1)
+ return XO_FOOT;
+ if (!vget(b_lines, 0, "³s¸p¤H¼Æ¡G", tmp, 6, DOECHO))
+ return XO_FOOT;
+ numbers = atoi(tmp);
+ if (numbers > 500 || numbers < 1)
+ return XO_FOOT;
+
+ nbrd.mode = (ans == '2') ? (NBRD_OTHER | NBRD_START) : (NBRD_OTHER | NBRD_START | NBRD_ANONYMOUS);
+ }
+
+ vmsg("¶}©l½s¿è [¬ÝªO»¡©ú»PªO¥D©ê­t©Î³s¸p­ì¦]]");
+ sprintf(path, "tmp/%s.nbrd", cuser.userid); /* ³s¸p­ì¦]ªº¼È¦sÀÉ®× */
+ if (fd = vedit(path, 0))
+ {
+ unlink(path);
+ vmsg(msg_cancel);
+ return nbrd_head(xo);
+ }
+
+ dir = xo->dir;
+ if ((fd = nbrd_stamp(dir, &nbrd, fpath)) < 0)
+ return nbrd_head(xo);
+ close(fd);
+
+ nbrd.etime = nbrd.btime + days * 86400;
+ nbrd.total = numbers;
+ strcpy(nbrd.owner, cuser.userid);
+
+ fp = fopen(fpath, "a");
+ fprintf(fp, "§@ªÌ: %s (%s) ¯¸¤º: ³s¸p¨t²Î\n", cuser.userid, cuser.username);
+ fprintf(fp, "¼ÐÃD: %s\n", title);
+ fprintf(fp, "®É¶¡: %s\n\n", Now());
+
+ if (ans == '1')
+ {
+ fprintf(fp, "­^¤åªO¦W¡G%s\n", brdname);
+ fprintf(fp, "¬ÝªO¤ÀÃþ¡G%s\n", class);
+ fprintf(fp, "¬ÝªO¥DÃD¡G%s\n", title);
+ fprintf(fp, "ªO¥D¦WºÙ¡G%s\n", cuser.userid);
+ fprintf(fp, "¹q¤l«H½c¡G%s\n", cuser.email);
+ }
+ else
+ {
+ fprintf(fp, "³s¸p¥DÃD¡G%s\n", title);
+ }
+ fprintf(fp, "Á|¿ì¤é´Á¡G%s\n", nbrd.date);
+ fprintf(fp, "¨ì´Á¤Ñ¼Æ¡G%d\n", days);
+ fprintf(fp, "»Ý³s¸p¤H¡G%d\n", numbers);
+ fprintf(fp, split_line);
+ fprintf(fp, "³s¸p»¡©ú¡G\n");
+ f_suck(fp, path);
+ unlink(path);
+ fprintf(fp, split_line);
+ fclose(fp);
+
+ rec_add(dir, &nbrd, sizeof(NBRD));
+
+#ifdef SYSOP_START_COSIGN
+ vmsg(ans == '1' ? "°e¥æ¥Ó½Ð¤F¡A½Ðµ¥­Ô®Ö­ã§a" : "³s¸p¶}©l¤F¡I");
+#else
+ vmsg("³s¸p¶}©l¤F¡I");
+#endif
+
+ char buf[80];
+ sprintf (buf, "[¶}©l³s¸p] %s ¬ÝªO", brdname);
+ add_post("newboard", fpath, buf);
+
+ return nbrd_init(xo);
+}
+
+
+static int
+nbrd_seek(fpath)
+ char *fpath;
+{
+ LOG old;
+ int fd;
+ int rc = 0;
+
+ if ((fd = open(fpath, O_RDONLY)) >= 0)
+ {
+ while (read(fd, &old, sizeof(LOG)) == sizeof(LOG))
+ {
+ if (!strcmp(old.userid, cuser.userid) || !str_cmp(old.email, cuser.email))
+ {
+ rc = 1;
+ break;
+ }
+ }
+ close(fd);
+ }
+ return rc;
+}
+
+
+static void
+addreply(hdd, ram)
+ NBRD *hdd, *ram;
+{
+ if (--hdd->total <= 0)
+ {
+ if (hdd->mode & NBRD_NEWBOARD) /* ·sªO³s¸p±¾ END */
+ hdd->mode |= NBRD_END;
+ else /* ¨ä¥L³s¸p±¾ FINISH */
+ hdd->mode |= NBRD_FINISH;
+ }
+}
+
+
+static int
+nbrd_reply(xo)
+ XO *xo;
+{
+ NBRD *nbrd;
+ char *fname, fpath[64], reason[80];
+ LOG mail;
+
+ nbrd = (NBRD *) xo_pool + (xo->pos - xo->top);
+ fname = NULL;
+
+ if (nbrd->mode & (NBRD_FINISH | NBRD_END))
+ return XO_NONE;
+
+#ifdef SYSOP_START_COSIGN
+ if (!(nbrd->mode & NBRD_START))
+ {
+ vmsg("©|¥¼¶}©l³s¸p");
+ return XO_FOOT;
+ }
+#endif
+
+ if (time(0) >= nbrd->etime)
+ {
+ currchrono = nbrd->btime;
+ if (nbrd->mode & NBRD_NEWBOARD) /* ·sªO³s¸p±¾ END */
+ {
+ if (!(nbrd->mode & NBRD_END))
+ {
+ nbrd->mode ^= NBRD_END;
+ currchrono = nbrd->btime;
+ rec_put(xo->dir, nbrd, sizeof(NBRD), xo->pos, cmpbtime);
+ }
+ }
+ else /* ¨ä¥L³s¸p±¾ FINISH */
+ {
+ if (!(nbrd->mode & NBRD_FINISH))
+ {
+ nbrd->mode ^= NBRD_FINISH;
+ currchrono = nbrd->btime;
+ rec_put(xo->dir, nbrd, sizeof(NBRD), xo->pos, cmpbtime);
+ }
+ }
+ vmsg("³s¸p¤w¸gºI¤î¤F");
+ return XO_FOOT;
+ }
+
+
+ /* --------------------------------------------------- */
+ /* Àˬd¬O§_¤w¸g³s¸p¹L */
+ /* --------------------------------------------------- */
+
+ nbrd_fpath(fpath, xo->dir, nbrd);
+ fname = strrchr(fpath, '@');
+ *fname = 'G';
+
+ if (nbrd_seek(fpath))
+ {
+ vmsg("±z¤w¸g³s¸p¹L¤F¡I");
+ return XO_FOOT;
+ }
+
+ /* --------------------------------------------------- */
+ /* ¶}©l³s¸p */
+ /* --------------------------------------------------- */
+
+ *fname = '@';
+
+ if (vans("­n¥[¤J³s¸p¶Ü(Y/N)¡H[N] ") == 'y' &&
+ vget(b_lines, 0, "§Ú¦³¸Ü­n»¡¡G", reason, 65, DOECHO))
+ {
+ FILE *fp;
+
+ if (fp = fopen(fpath, "a"))
+ {
+ if (nbrd->mode & NBRD_ANONYMOUS)
+ fprintf(fp, "%3d -> " STR_ANONYMOUS "\n %s\n", nbrd->total, reason);
+ else
+ fprintf(fp, "%3d -> %s (%s)\n %s\n", nbrd->total, cuser.userid, cuser.email, reason);
+ fclose(fp);
+ }
+
+ currchrono = nbrd->btime;
+ rec_ref(xo->dir, nbrd, sizeof(NBRD), xo->pos, cmpbtime, addreply);
+
+ memset(&mail, 0, sizeof(LOG));
+ strcpy(mail.userid, cuser.userid);
+ strcpy(mail.email, cuser.email);
+ *fname = 'G';
+ rec_add(fpath, &mail, sizeof(LOG));
+
+ vmsg("¥[¤J³s¸p§¹¦¨");
+
+ if (nbrd->mode & NBRD_END)
+ {
+ char buf[80];
+ sprintf (buf, "[³s¸p¦¨¥\\] ¯¸°È½Ð¨ì³s¸p¾÷¶}±Ò %s ¬ÝªO", nbrd->brdname);
+ add_post("newboard", fpath, buf);
+ }
+ return nbrd_init(xo);
+ }
+
+ return XO_FOOT;
+}
+
+
+#ifdef SYSOP_START_COSIGN
+static int
+nbrd_start(xo)
+ XO *xo;
+{
+ NBRD *nbrd;
+ char fpath[64], buf[80], tmp[10];
+ time_t etime;
+
+ if (!HAS_PERM(PERM_ALLBOARD))
+ return XO_NONE;
+
+ nbrd = (NBRD *) xo_pool + (xo->pos - xo->top);
+
+ if (nbrd->mode & (NBRD_FINISH | NBRD_END | NBRD_START))
+ return XO_NONE;
+
+ if (vans("½Ð½T©w¶}©l³s¸p(Y/N)¡H[N] ") != 'y')
+ return XO_FOOT;
+
+ nbrd_fpath(fpath, xo->dir, nbrd);
+ etime = time(0) + NBRD_DAY_BRD * 86400;
+
+ str_stamp(tmp, &etime);
+ sprintf(buf, "¶}©l³s¸p¡G ¨ì´Á¤é´Á¡G%s\n", tmp);
+ f_cat(fpath, buf);
+ f_cat(fpath, split_line);
+
+ nbrd->etime = etime;
+ nbrd->mode ^= NBRD_START;
+ currchrono = nbrd->btime;
+ rec_put(xo->dir, nbrd, sizeof(NBRD), xo->pos, cmpbtime);
+
+ return nbrd_head(xo);
+}
+#endif
+
+
+static int
+nbrd_finish(xo)
+ XO *xo;
+{
+ NBRD *nbrd;
+ char fpath[64], path[64];
+ int fd;
+ FILE *fp;
+
+ if (!HAS_PERM(PERM_ALLBOARD))
+ return XO_NONE;
+
+ nbrd = (NBRD *) xo_pool + (xo->pos - xo->top);
+
+ if (nbrd->mode & NBRD_FINISH)
+ return XO_NONE;
+
+ if (vans("½Ð½T©wµ²§ô³s¸p(Y/N)¡H[N] ") != 'y')
+ return XO_FOOT;
+
+ vmsg("½Ð½s¿èµ²§ô³s¸p­ì¦]");
+ sprintf(path, "tmp/%s", cuser.userid); /* ³s¸p­ì¦]ªº¼È¦sÀÉ®× */
+ if (fd = vedit(path, 0))
+ {
+ unlink(path);
+ vmsg(msg_cancel);
+ return nbrd_head(xo);
+ }
+
+ nbrd_fpath(fpath, xo->dir, nbrd);
+
+ f_cat(fpath, "µ²§ô³s¸p­ì¦]¡G\n\n");
+ fp = fopen(fpath, "a");
+ f_suck(fp, path);
+ fclose(fp);
+ f_cat(fpath, split_line);
+ unlink(path);
+
+ nbrd->mode ^= NBRD_FINISH;
+ currchrono = nbrd->btime;
+ rec_put(xo->dir, nbrd, sizeof(NBRD), xo->pos, cmpbtime);
+
+ return nbrd_head(xo);
+}
+
+
+static int /* 1:¶}ªO¦¨¥\ */
+nbrd_newbrd(nbrd) /* ¶}·sªO */
+ NBRD *nbrd;
+{
+ BRD newboard;
+ ACCT acct;
+
+ /* itoc.030519: Á×§K­«Âж}ªO */
+ if (brd_bno(nbrd->brdname) >= 0)
+ {
+ vmsg("¤w¦³¦¹ªO");
+ return 1;
+ }
+
+ memset(&newboard, 0, sizeof(BRD));
+
+ /* itoc.010805: ·s¬ÝªO¹w³] battr = ¤£Âà«H; postlevel = PERM_POST; ¬ÝªOªO¥D¬°´£°_³s¸pªÌ */
+ newboard.battr = BRD_NOTRAN;
+ newboard.postlevel = PERM_POST;
+ strcpy(newboard.brdname, nbrd->brdname);
+ strcpy(newboard.class, nbrd->class);
+ strcpy(newboard.title, nbrd->title);
+ strcpy(newboard.BM, nbrd->owner);
+
+ if (acct_load(&acct, nbrd->owner) >= 0)
+ acct_setperm(&acct, PERM_BM, 0);
+
+ if (brd_new(&newboard) < 0)
+ return 0;
+
+ vmsg("·sªO¦¨¥ß¡A°OµÛ¥[¤J¤ÀÃþ¸s²Õ");
+ return 1;
+}
+
+
+static int
+nbrd_open(xo) /* itoc.010805: ¶}·sªO³s¸p¡A³s¸p§¹²¦¶}·s¬ÝªO */
+ XO *xo;
+{
+ NBRD *nbrd;
+
+ if (!HAS_PERM(PERM_ALLBOARD))
+ return XO_NONE;
+
+ nbrd = (NBRD *) xo_pool + (xo->pos - xo->top);
+
+ if (nbrd->mode & NBRD_FINISH || !(nbrd->mode & NBRD_NEWBOARD))
+ return XO_NONE;
+
+ if (vans("½Ð½T©w¶}±Ò¬ÝªO(Y/N)¡H[N] ") == 'y')
+ {
+ if (nbrd_newbrd(nbrd))
+ {
+ nbrd->mode ^= NBRD_FINISH;
+ currchrono = nbrd->btime;
+ rec_put(xo->dir, nbrd, sizeof(NBRD), xo->pos, cmpbtime);
+ }
+ return nbrd_head(xo);
+ }
+
+ return XO_FOOT;
+}
+
+
+static int
+nbrd_browse(xo)
+ XO *xo;
+{
+ int key;
+ NBRD *nbrd;
+ char fpath[80];
+
+ /* itoc.010304: ¬°¤FÅý¾\Ū¨ì¤@¥b¤]¥i¥H¥[¤J³s¸p¡A¦Ò¼{ more ¶Ç¦^­È */
+ for (;;)
+ {
+ nbrd = (NBRD *) xo_pool + (xo->pos - xo->top);
+ nbrd_fpath(fpath, xo->dir, nbrd);
+
+ if ((key = more(fpath, FOOTER_COSIGN)) < 0)
+ break;
+
+ if (!key)
+ key = vkey();
+
+ switch (key)
+ {
+ case KEY_UP:
+ case KEY_PGUP:
+ case '[':
+ case 'k':
+ key = xo->pos - 1;
+
+ if (key < 0)
+ break;
+
+ xo->pos = key;
+
+ if (key <= xo->top)
+ {
+ xo->top = (key / XO_TALL) * XO_TALL;
+ nbrd_load(xo);
+ }
+ continue;
+
+ case KEY_DOWN:
+ case KEY_PGDN:
+ case ']':
+ case 'j':
+ case ' ':
+ key = xo->pos + 1;
+
+ if (key >= xo->max)
+ break;
+
+ xo->pos = key;
+
+ if (key >= xo->top + XO_TALL)
+ {
+ xo->top = (key / XO_TALL) * XO_TALL;
+ nbrd_load(xo);
+ }
+ continue;
+
+ case 'y':
+ case 'r':
+ nbrd_reply(xo);
+ break;
+
+ case 'h':
+ xo_help("cosign");
+ break;
+ }
+ break;
+ }
+
+ return nbrd_head(xo);
+}
+
+
+static int
+nbrd_delete(xo)
+ XO *xo;
+{
+ NBRD *nbrd;
+ char *fname, fpath[80];
+ char *list = "@G"; /* itoc.µù¸Ñ: ²M newbrd file */
+
+ nbrd = (NBRD *) xo_pool + (xo->pos - xo->top);
+ if (strcmp(cuser.userid, nbrd->owner) && !HAS_PERM(PERM_ALLBOARD))
+ return XO_NONE;
+
+ if (vans(msg_del_ny) != 'y')
+ return XO_FOOT;
+
+ nbrd_fpath(fpath, xo->dir, nbrd);
+ fname = strrchr(fpath, '@');
+ while (*fname = *list++)
+ {
+ unlink(fpath); /* Thor: ½T©w¦W¦r´N¬å */
+ }
+
+ currchrono = nbrd->btime;
+ rec_del(xo->dir, sizeof(NBRD), xo->pos, cmpbtime);
+ return nbrd_init(xo);
+}
+
+
+static int
+nbrd_edit(xo)
+ XO *xo;
+{
+ if (HAS_PERM(PERM_ALLBOARD))
+ {
+ char fpath[64];
+ NBRD *nbrd;
+
+ nbrd = (NBRD *) xo_pool + (xo->pos - xo->top);
+ nbrd_fpath(fpath, xo->dir, nbrd);
+ vedit(fpath, 0);
+ return nbrd_head(xo);
+ }
+
+ return XO_NONE;
+}
+
+
+static int
+nbrd_setup(xo)
+ XO *xo;
+{
+ int numbers;
+ char ans[6];
+ NBRD *nbrd, newnh;
+
+ if (!HAS_PERM(PERM_ALLBOARD))
+ return XO_NONE;
+
+ vs_bar("³s¸p³]©w");
+ nbrd = (NBRD *) xo_pool + (xo->pos - xo->top);
+ memcpy(&newnh, nbrd, sizeof(NBRD));
+
+ prints("¬ÝªO¦WºÙ¡G%s\n¬ÝªO»¡©ú¡G%4.4s %s\n³s¸pµo°_¡G%s\n",
+ newnh.brdname, newnh.class, newnh.title, newnh.owner);
+ prints("¶}©l®É¶¡¡G%s\n", Btime(&newnh.btime));
+ prints("µ²§ô®É¶¡¡G%s\n", Btime(&newnh.etime));
+ prints("ÁٻݤH¼Æ¡G%d\n", newnh.total);
+
+ if (vget(8, 0, "(E)³]©w (Q)¨ú®ø¡H[Q] ", ans, 3, LCECHO) == 'e')
+ {
+ vget(11, 0, MSG_BID, newnh.brdname, BNLEN + 1, GCARRY);
+ vget(12, 0, "¬ÝªO¤ÀÃþ¡G", newnh.class, sizeof(newnh.class), GCARRY);
+ vget(13, 0, "¬ÝªO¥DÃD¡G", newnh.title, sizeof(newnh.title), GCARRY);
+ sprintf(ans, "%d", newnh.total);
+ vget(14, 0, "³s¸p¤H¼Æ¡G", ans, 6, GCARRY);
+ numbers = atoi(ans);
+ if (numbers <= 500 && numbers >= 1)
+ newnh.total = numbers;
+
+ if (memcmp(&newnh, nbrd, sizeof(newnh)) && vans(msg_sure_ny) == 'y')
+ {
+ memcpy(nbrd, &newnh, sizeof(NBRD));
+ currchrono = nbrd->btime;
+ rec_put(xo->dir, nbrd, sizeof(NBRD), xo->pos, cmpbtime);
+ }
+ }
+
+ return nbrd_head(xo);
+}
+
+
+static int
+nbrd_uquery(xo)
+ XO *xo;
+{
+ NBRD *nbrd;
+
+ nbrd = (NBRD *) xo_pool + (xo->pos - xo->top);
+
+ move(1, 0);
+ clrtobot();
+ my_query(nbrd->owner);
+ return nbrd_head(xo);
+}
+
+
+static int
+nbrd_usetup(xo)
+ XO *xo;
+{
+ NBRD *nbrd;
+ ACCT acct;
+
+ if (!HAS_PERM(PERM_ALLACCT))
+ return XO_NONE;
+
+ nbrd = (NBRD *) xo_pool + (xo->pos - xo->top);
+ if (acct_load(&acct, nbrd->owner) < 0)
+ return XO_NONE;
+
+ move(3, 0);
+ acct_setup(&acct, 1);
+ return nbrd_head(xo);
+}
+
+
+static int
+nbrd_help(xo)
+ XO *xo;
+{
+ xo_help("cosign");
+ return nbrd_head(xo);
+}
+
+
+static KeyFunc nbrd_cb[] =
+{
+ XO_INIT, nbrd_init,
+ XO_LOAD, nbrd_load,
+ XO_HEAD, nbrd_head,
+ XO_BODY, nbrd_body,
+
+ 'y', nbrd_reply,
+ 'r', nbrd_browse,
+ 'o', nbrd_open,
+#ifdef SYSOP_START_COSIGN
+ 's', nbrd_start,
+#endif
+ 'c', nbrd_finish,
+ 'd', nbrd_delete,
+ 'E', nbrd_edit,
+ 'B', nbrd_setup,
+
+ Ctrl('P'), nbrd_add,
+ Ctrl('Q'), nbrd_uquery,
+ Ctrl('O'), nbrd_usetup,
+
+ 'h', nbrd_help
+};
+
+
+int
+XoNewBoard()
+{
+ XO *xo;
+ char fpath[64];
+
+ sprintf(fpath, "run/newbrd/%s", fn_dir);
+ xz[XZ_COSIGN - XO_ZONE].xo = xo = xo_new(fpath);
+ xz[XZ_COSIGN - XO_ZONE].cb = nbrd_cb;
+ xo->key = XZ_COSIGN;
+ xover(XZ_COSIGN);
+ free(xo);
+
+ return 0;
+}
+#endif /* HAVE_COSIGN */
diff --git a/so/song.c b/so/song.c
new file mode 100644
index 0000000..13b9cea
--- /dev/null
+++ b/so/song.c
@@ -0,0 +1,752 @@
+/*-------------------------------------------------------*/
+/* song.c ( YZU_CSE WindTop BBS ) */
+/*-------------------------------------------------------*/
+/* target : song ordering routines */
+/* create : / / */
+/* update : 01/12/18 */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+
+#ifdef HAVE_SONG
+
+extern BCACHE *bshm;
+extern XZ xz[];
+extern char xo_pool[];
+extern char brd_bits[];
+
+
+static void XoSong();
+
+
+#define SONG_SRC "<~Src~>"
+#define SONG_DES "<~Des~>"
+#define SONG_SAY "<~Say~>"
+#define SONG_END "<~End~>"
+
+
+#ifdef LOG_SONG_USIES
+static int /* -1:¨S§ä¨ì >=0:pos */
+song_usies_find(fpath, chrono, songdata)
+ char *fpath;
+ time_t chrono;
+ SONGDATA *songdata;
+{
+ int fd, pos;
+ int rc = -1;
+
+ if ((fd = open(fpath, O_RDONLY)) >= 0)
+ {
+ pos = 0;
+ while (read(fd, songdata, sizeof(SONGDATA)) == sizeof(SONGDATA))
+ {
+ if (songdata->chrono == chrono)
+ {
+ rc = pos;
+ break;
+ }
+ pos++;
+ }
+ close(fd);
+ }
+ return rc;
+}
+
+
+static void
+song_usies_log(chrono, title)
+ time_t chrono;
+ char *title;
+{
+ SONGDATA songdata;
+ int pos;
+ char *dir;
+
+ dir = FN_RUN_SONGUSIES;
+
+ if ((pos = song_usies_find(dir, chrono, &songdata)) >= 0)
+ {
+ songdata.count++;
+ rec_put(dir, &songdata, sizeof(SONGDATA), pos, NULL);
+ }
+ else
+ {
+ songdata.chrono = chrono;
+ songdata.count = 1;
+ strcpy(songdata.title, title);
+ rec_add(dir, &songdata, sizeof(SONGDATA));
+ }
+}
+#endif
+
+
+static int swaped; /* ¥»¤å¬O§_¦³ <~Src~> µ¥ */
+
+static int
+song_swap(str, src, des)
+ char *str;
+ char *src;
+ char *des;
+{
+ char *ptr;
+ char buf[ANSILINELEN];
+
+ if (ptr = strstr(str, src))
+ {
+ *ptr = '\0';
+ ptr += strlen(src);
+ /* sprintf(buf, "%s%s%s", str, des, ptr); */
+ snprintf(buf, ANSILINELEN, "%s%s%s", str, des, ptr); /* swap ¥i¯à·|¶W¹L ANSILINELEN */
+ strcpy(str, buf);
+
+ /* return 1; */
+ return ++swaped;
+ }
+
+ return 0;
+}
+
+
+static void
+song_quote(fpr, fpw, src, des, say) /* ±q fpr Ū¥X¤º¤å¡A§â <~Src~> µ¥´À´«±¼¡A¼g¤J fpw */
+ FILE *fpr, *fpw;
+ char *src, *des, *say;
+{
+ char buf[ANSILINELEN];
+
+ swaped = 0;
+ while (fgets(buf, sizeof(buf), fpr))
+ {
+ if (strstr(buf, SONG_END))
+ break;
+
+ while (song_swap(buf, SONG_SRC, src));
+ while (song_swap(buf, SONG_DES, des));
+ while (song_swap(buf, SONG_SAY, say));
+ fputs(buf, fpw);
+ }
+
+ if (!swaped) /* itoc.011115: ¦pªG¥»¤å¨S¦³ <~Src~>¡A«h¦b³Ì«á¥[¤W */
+ {
+ /* ¦b³Ì«á¤@¦æ¥[¤W <~Src~> ·Q¹ï <~Des~> »¡ <~Say~> */
+ strcpy(buf, "\033[1;33m" SONG_SRC " ·Q¹ï " SONG_DES " »¡ " SONG_SAY "\033[m\n");
+ song_swap(buf, SONG_SRC, src);
+ song_swap(buf, SONG_DES, des);
+ song_swap(buf, SONG_SAY, say);
+ fputs(buf, fpw);
+ }
+
+ /* ¦bÀɮ׳̫á¥[¤WÂIºq®É¶¡ */
+ fprintf(fpw, "\n--\n%s\n", Now());
+}
+
+
+#define GEM_FILE 0x01 /* ¹w´Á¬OÀÉ®× */
+
+
+static HDR * /* NULL:µLÅvŪ¨ú */
+song_check(xo, fpath, op)
+ XO *xo;
+ char *fpath;
+ int op;
+{
+ HDR *hdr;
+ int gtype;
+
+ hdr = (HDR *) xo_pool + (xo->pos - xo->top);
+ gtype = hdr->xmode;
+
+ if ((gtype & GEM_RESTRICT) && !(xo->key & GEM_M_BIT))
+ return NULL;
+
+ if ((op & GEM_FILE) && (gtype & GEM_FOLDER))
+ return NULL;
+
+ if (fpath)
+ {
+ if (gtype & GEM_BOARD)
+ gem_fpath(fpath, hdr->xname, fn_dir);
+ else
+ hdr_fpath(fpath, xo->dir, hdr);
+ }
+ return hdr;
+}
+
+
+static void
+song_item(num, hdr, level)
+ int num;
+ HDR *hdr;
+ int level;
+{
+ int xmode, gtype;
+
+ xmode = hdr->xmode;
+ gtype = (char) 0xba;
+
+ /* ¥Ø¿ý¥Î¹ê¤ß¡A¤£¬O¥Ø¿ý¥ÎªÅ¤ß */
+ if (xmode & GEM_FOLDER) /* ¤å³¹:¡º ¨÷©v:¡» */
+ gtype += 1;
+
+ if (hdr->xname[0] == '@') /* ¸ê®Æ:¡¸ ¤ÀÃþ:¡¹ */
+ gtype -= 2;
+ else if (xmode & GEM_BOARD) /* ¬ÝªO:¡½ */
+ gtype += 2;
+
+ prints("%6d%c \241%c ", num, xmode & GEM_RESTRICT ? ')' : ' ', gtype);
+
+ if ((xmode & GEM_RESTRICT) && !(level & GEM_M_BIT))
+ outs(MSG_DATA_CLOAK);
+ else
+ prints("%.*s\n", d_cols + 64, hdr->title);
+}
+
+
+static int
+song_body(xo)
+ XO *xo;
+{
+ HDR *hdr;
+ int num, max, tail;
+
+ max = xo->max;
+ if (max <= 0)
+ {
+ outs("\n\n¡mºq¥»¡n©|¦b§l¨ú¤Ñ¦a¶¡ªº¤éºë¤ëµØ :)");
+ vmsg(NULL);
+ return XO_QUIT;
+ }
+
+ hdr = (HDR *) xo_pool;
+ num = xo->top;
+ tail = num + XO_TALL;
+ if (max > tail)
+ max = tail;
+
+ move(3, 0);
+ tail = xo->key; /* ­É¥Î tail */
+ do
+ {
+ song_item(++num, hdr++, tail);
+ } while (num < max);
+ clrtobot();
+
+ /* return XO_NONE; */
+ return XO_FOOT; /* itoc.010403: §â b_lines ¶ñ¤W feeter */
+}
+
+
+static int
+song_head(xo)
+ XO *xo;
+{
+ vs_head("ºëµØ¤å³¹", xo->xyz);
+ prints(NECKER_SONG, d_cols, "");
+ return song_body(xo);
+}
+
+
+static int
+song_init(xo)
+ XO *xo;
+{
+ xo_load(xo, sizeof(HDR));
+ return song_head(xo);
+}
+
+
+static int
+song_load(xo)
+ XO *xo;
+{
+ xo_load(xo, sizeof(HDR));
+ return song_body(xo);
+}
+
+
+/* ----------------------------------------------------- */
+/* ¸ê®Æ¤§·s¼W¡Gappend / insert */
+/* ----------------------------------------------------- */
+
+
+static int
+song_browse(xo)
+ XO *xo;
+{
+ HDR *hdr;
+ int op, xmode;
+ char fpath[64], title[TTLEN + 1];
+
+ op = 0;
+
+ for (;;)
+ {
+ if (!(hdr = song_check(xo, fpath, op)))
+ break;
+
+ xmode = hdr->xmode;
+
+ /* browse folder */
+
+ if (xmode & GEM_FOLDER)
+ {
+ if (xmode & GEM_BOARD)
+ {
+ if ((op = gem_link(hdr->xname)) < 0)
+ {
+ vmsg("¹ï¤£°_¡A¦¹ªOºëµØ°Ï¥u­ãªO¤Í¶i¤J¡A½Ð¦VªO¥D¥Ó½Ð¤J¹Ò³\\¥i");
+ return XO_FOOT;
+ }
+ }
+ else /* ¤@¯ë¨÷©v */
+ {
+ op = xo->key; /* Ä~©Ó¥À¨÷©vªºÅv­­ */
+ }
+
+ strcpy(title, hdr->title);
+ XoSong(fpath, title, op);
+ return song_init(xo);
+ }
+
+ /* browse article */
+
+ /* Thor.990204: ¬°¦Ò¼{more ¶Ç¦^­È */
+ if ((xmode = more(fpath, FOOTER_GEM)) < 0)
+ break;
+
+ op = GEM_FILE;
+
+re_key:
+ switch (xo_getch(xo, xmode))
+ {
+ case XO_BODY:
+ continue;
+
+ case '/':
+ if (vget(b_lines, 0, "·j´M¡G", hunt, sizeof(hunt), DOECHO))
+ {
+ more(fpath, FOOTER_GEM);
+ goto re_key;
+ }
+ continue;
+
+ case 'C':
+ {
+ FILE *fp;
+ if (fp = tbf_open())
+ {
+ f_suck(fp, fpath);
+ fclose(fp);
+ }
+ }
+ break;
+
+ case 'h':
+ xo_help("song");
+ break;
+ }
+ break;
+ }
+
+ return song_head(xo);
+}
+
+
+static int
+count_ktv() /* itoc.021102: ktv ªO¸Ì­±¤w¦³´X½gªºÂIºq°O¿ý */
+{
+ int count, fd;
+ time_t yesterday;
+ char folder[64];
+ HDR *hdr;
+
+ brd_fpath(folder, BN_KTV, fn_dir);
+ if ((fd = open(folder, O_RDONLY)) < 0)
+ return 0;
+
+ mgets(-1);
+ count = 0;
+ yesterday = time(0) - 86400;
+ while (hdr = mread(fd, sizeof(HDR)))
+ {
+ /* §Y¨Ï¬O°Î¦WÂIºq¡Auserid ¤´°O¿ý¦b hdr.owner + IDLEN + 1 */
+ if (!strcmp(hdr->owner + IDLEN + 1, cuser.userid) &&
+ hdr->chrono > yesterday)
+ {
+ if (++count >= 3) /* ­­¨îÂI¤T­º */
+ break;
+ }
+ }
+ close(fd);
+
+ return count;
+}
+
+
+static int
+song_order(xo)
+ XO *xo;
+{
+#ifdef HAVE_ANONYMOUS
+ int annoy;
+#endif
+ char fpath[64], des[20], say[57], buf[64];
+ HDR *hdr, xpost;
+ FILE *fpr, *fpw;
+
+ /* itoc.µù¸Ñ: song_order µø¦P post */
+ if (!HAS_PERM(PERM_POST))
+ return XO_NONE;
+
+ /* itoc.010831: ÂIºq­n¿ú¡A©Ò¥H­n¸T¤î multi-login */
+ if (HAS_STATUS(STATUS_COINLOCK))
+ {
+ vmsg(msg_coinlock);
+ return XO_FOOT;
+ }
+
+ if (count_ktv() >= 3) /* ­­¨îÂI¤T­º */
+ {
+ vmsg("¹L¥h¤G¤Q¥|¤p®É¤º±z¤wÂI¿ï¹L¦hºq¦±");
+ return XO_FOOT;
+ }
+
+ if (cuser.money < 1000)
+ {
+ vmsg("­n 1000 »È¹ô¤~¯àÂIºq¨ì¬ÝªO³á");
+ return XO_FOOT;
+ }
+
+ if (!(hdr = song_check(xo, fpath, GEM_FILE)))
+ return XO_NONE;
+
+ if (!vget(b_lines, 0, "ÂIºqµ¹½Ö¡G", des, sizeof(des), DOECHO))
+ return XO_FOOT;
+ if (!vget(b_lines, 0, "·Q»¡ªº¸Ü¡G", say, sizeof(say), DOECHO))
+ return XO_FOOT;
+
+#ifdef HAVE_ANONYMOUS
+ annoy = vans("·Q­n°Î¦W¶Ü(Y/N)¡H[N] ") == 'y';
+#endif
+
+ if (vans("½T©wÂIºq¶Ü(Y/N)¡H[Y] ") == 'n')
+ return XO_FOOT;
+
+ if (!(fpr = fopen(fpath, "r")))
+ return XO_FOOT;
+
+ cuser.money -= 1000;
+
+#ifdef LOG_SONG_USIES
+ song_usies_log(hdr->chrono, hdr->title);
+#endif
+
+ /* ¥[¤J¤å³¹ÀÉ®× */
+
+ brd_fpath(fpath, BN_KTV, fn_dir);
+
+ if (fpw = fdopen(hdr_stamp(fpath, 'A', &xpost, buf), "w"))
+ {
+#ifdef HAVE_ANONYMOUS
+ song_quote(fpr, fpw, annoy ? STR_ANONYMOUS : cuser.userid, des, say);
+#else
+ song_quote(fpr, fpw, cuser.userid, des, say);
+#endif
+ fclose(fpw);
+ }
+
+ fclose(fpr);
+
+ /* ¥[¤J .DIR record */
+
+#ifdef HAVE_ANONYMOUS
+ strcpy(xpost.owner, annoy ? STR_ANONYMOUS : cuser.userid);
+ /* §Y¨Ï¬O°Î¦WÂIºq¡Auserid ¤´°O¿ý¦b hdr.owner + IDLEN + 1 */
+ strcpy(xpost.owner + IDLEN + 1, cuser.userid);
+#else
+ strcpy(xpost.owner, cuser.userid);
+#endif
+ strcpy(xpost.nick, xpost.owner);
+ sprintf(xpost.title, "%s ÂIµ¹ %s", xpost.owner, des);
+ rec_bot(fpath, &xpost, sizeof(HDR));
+
+ btime_update(brd_bno(BN_KTV));
+
+ return XO_FOOT;
+}
+
+
+static int
+song_send(xo)
+ XO *xo;
+{
+ char fpath[64], say[57], buf[64];
+ HDR *hdr, xpost;
+ FILE *fpr, *fpw;
+ ACCT acct;
+
+ /* itoc.µù¸Ñ: song_send µø¦P mail */
+ if (!HAS_PERM(PERM_LOCAL))
+ return XO_NONE;
+
+ if (!(hdr = song_check(xo, fpath, GEM_FILE)))
+ return XO_NONE;
+
+ if (acct_get("ÂIºqµ¹½Ö¡G", &acct) < 1) /* acct_get ¥i¯à·| clear¡A©Ò¥H­n­«Ã¸ */
+ return song_head(xo);
+
+ if (!vget(b_lines, 0, "·Q»¡ªº¸Ü¡G", say, sizeof(say), DOECHO))
+ strcpy(say, ".........");
+
+ if (vans("½T©wÂIºq¶Ü(Y/N)¡H[Y] ") == 'n')
+ return song_head(xo); /* acct_get ¥i¯à·| clear¡A©Ò¥H­n­«Ã¸ */
+
+#ifdef LOG_SONG_USIES
+ song_usies_log(hdr->chrono, hdr->title);
+#endif
+
+ /* ¥[¤J¤å³¹ÀÉ®× */
+
+ if (!(fpr = fopen(fpath, "r")))
+ return song_head(xo); /* acct_get ¥i¯à·| clear¡A©Ò¥H­n­«Ã¸ */
+
+ usr_fpath(fpath, acct.userid, fn_dir);
+
+ if (fpw = fdopen(hdr_stamp(fpath, 0, &xpost, buf), "w"))
+ {
+ song_quote(fpr, fpw, cuser.userid, acct.userid, say);
+ fclose(fpw);
+ }
+
+ fclose(fpr);
+
+ /* ¥[¤J .DIR record */
+
+ strcpy(xpost.owner, cuser.userid);
+ strcpy(xpost.nick, cuser.username);
+ sprintf(xpost.title, "%s ÂIºqµ¹±z", cuser.userid);
+ rec_add(fpath, &xpost, sizeof(HDR));
+
+ mail_hold(buf, acct.userid, xpost.title, 0);
+ m_biff(acct.userno); /* ­Y¹ï¤è¦b½u¤W¡A«h­n³qª¾¦³·s«H */
+
+ return song_head(xo); /* acct_get ¥i¯à·| clear¡A©Ò¥H­n­«Ã¸ */
+}
+
+
+static int
+song_internet(xo)
+ XO *xo;
+{
+ int rc;
+ char fpath[64], rcpt[64], des[20], say[57];
+ HDR *hdr;
+ FILE *fpr, *fpw;
+
+ /* itoc.µù¸Ñ: song_internet µø¦P internet_mail */
+ if (!HAS_PERM(PERM_INTERNET))
+ return XO_NONE;
+
+ if (!(hdr = song_check(xo, fpath, GEM_FILE)))
+ return XO_NONE;
+
+ if (!vget(b_lines, 0, "¥Øªº¦a¡G", rcpt, sizeof(rcpt), DOECHO))
+ return XO_FOOT;
+ if (!strchr(rcpt, '@'))
+ return XO_FOOT;
+
+ if (!vget(b_lines, 0, "ÂIºqµ¹½Ö¡G", des, sizeof(des), DOECHO))
+ return XO_FOOT;
+ if (!vget(b_lines, 0, "·Q»¡ªº¸Ü¡G", say, sizeof(say), DOECHO))
+ strcpy(say, ".........");
+
+ if (vans("½T©wÂIºq¶Ü(Y/N)¡H[Y] ") == 'n')
+ return XO_FOOT;
+
+#ifdef LOG_SONG_USIES
+ song_usies_log(hdr->chrono, hdr->title);
+#endif
+
+ /* ¥[¤J¤å³¹ÀÉ®× */
+
+ if (!(fpr = fopen(fpath, "r")))
+ return XO_FOOT;
+
+ sprintf(fpath, "tmp/song_internet.%s", cuser.userid);
+ fpw = fopen(fpath, "w");
+
+ song_quote(fpr, fpw, cuser.userid, des, say);
+
+ fclose(fpr);
+ fclose(fpw);
+
+ /* ±Hµ¹¹ï¤è */
+
+ rc = bsmtp(fpath, "ÂIºqµ¹±z", rcpt, 0);
+ vmsg(rc >= 0 ? msg_sent_ok : "«H¥óµLªk±H¹F¡A©³½Z³Æ¥÷¦b«H½c");
+
+ mail_hold(fpath, rcpt, hdr->title, rc);
+ unlink(fpath);
+
+ return XO_FOOT;
+}
+
+
+static int
+song_edit(xo)
+ XO *xo;
+{
+ char fpath[64];
+
+ if (!song_check(xo, fpath, GEM_FILE))
+ return XO_NONE;
+
+ if (xo->key & GEM_W_BIT)
+ vedit(fpath, 0);
+ else
+ vedit(fpath, -1); /* itoc.010403: Åý¤@¯ë¨Ï¥ÎªÌ¤]¥i¥H edit ¬Ý±±¨î½X */
+ return song_head(xo);
+}
+
+
+static int
+song_title(xo)
+ XO *xo;
+{
+ HDR *fhdr, mhdr;
+ int pos, cur;
+
+ if (!(xo->key & GEM_W_BIT) || !(fhdr = song_check(xo, NULL, 0)))
+ return XO_NONE;
+
+ memcpy(&mhdr, fhdr, sizeof(HDR));
+
+ vget(b_lines, 0, "¼ÐÃD¡G", mhdr.title, TTLEN + 1, GCARRY);
+
+ if (xo->key & GEM_X_BIT)
+ {
+ vget(b_lines, 0, "½sªÌ¡G", mhdr.owner, IDLEN + 1, GCARRY);
+ /* vget(b_lines, 0, "¼ÊºÙ¡G", mhdr.nick, sizeof(mhdr.nick), GCARRY); */ /* ºëµØ°Ï¦¹Äæ¦ì¬°ªÅ */
+ vget(b_lines, 0, "¤é´Á¡G", mhdr.date, sizeof(mhdr.date), GCARRY);
+ }
+
+ if (memcmp(fhdr, &mhdr, sizeof(HDR)) && vans(msg_sure_ny) == 'y')
+ {
+ pos = xo->pos;
+ cur = pos - xo->top;
+
+ memcpy(fhdr, &mhdr, sizeof(HDR));
+ rec_put(xo->dir, fhdr, sizeof(HDR), pos, NULL);
+
+ move(3 + cur, 0);
+ song_item(++pos, fhdr, xo->key);
+
+ }
+ return XO_FOOT;
+}
+
+
+static int
+song_help(xo)
+ XO *xo;
+{
+ xo_help("song");
+ return song_head(xo);
+}
+
+
+static KeyFunc song_cb[] =
+{
+ XO_INIT, song_init,
+ XO_LOAD, song_load,
+ XO_HEAD, song_head,
+ XO_BODY, song_body,
+
+ 'r', song_browse,
+ 'o', song_order,
+ 'm', song_send,
+ 'M', song_internet,
+
+ 'E', song_edit,
+ 'T', song_title,
+
+ 'h', song_help
+};
+
+
+static void
+XoSong(folder, title, level)
+ char *folder;
+ char *title;
+ int level;
+{
+ XO *xo, *last;
+
+ last = xz[XZ_SONG - XO_ZONE].xo; /* record */
+
+ xz[XZ_SONG - XO_ZONE].xo = xo = xo_new(folder);
+ xz[XZ_SONG - XO_ZONE].cb = song_cb;
+ xo->pos = 0;
+ xo->key = level;
+ xo->xyz = title;
+ strcpy(currBM, "ªO¥D¡G¨t²ÎºÞ²zªÌ");
+
+ xover(XZ_SONG);
+
+ free(xo);
+
+ xz[XZ_SONG - XO_ZONE].xo = last; /* restore */
+}
+
+
+int
+XoSongMain()
+{
+ int level;
+ char fpath[64];
+
+ gem_fpath(fpath, BN_KTV, fn_dir);
+
+ if (HAS_PERM(PERM_SYSOP))
+ level = GEM_W_BIT | GEM_X_BIT | GEM_M_BIT;
+ else if (HAS_PERM(PERM_ALLBOARD))
+ level = GEM_W_BIT | GEM_M_BIT;
+ else
+ level = 0;
+
+ XoSong(fpath, "ÂIºq¨t²Î", level);
+ return 0;
+}
+
+
+int
+XoSongSub()
+{
+ int bno;
+
+ if ((bno = brd_bno(BN_REQUEST)) >= 0)
+ {
+ XoPost(bno);
+ xover(XZ_POST);
+ return 0;
+ }
+ return XEASY;
+}
+
+
+int
+XoSongLog()
+{
+ int bno;
+
+ if ((bno = brd_bno(BN_KTV)) >= 0)
+ {
+ XoPost(bno);
+ xover(XZ_POST);
+ return 0;
+ }
+ return XEASY;
+}
+#endif /* HAVE_SONG */
diff --git a/so/todo.c b/so/todo.c
new file mode 100644
index 0000000..b99e589
--- /dev/null
+++ b/so/todo.c
@@ -0,0 +1,365 @@
+/*-------------------------------------------------------*/
+/* todo.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : ¦æ¨Æ¾ä */
+/* create : 00/09/13 */
+/* update : 03/08/24 */
+/* author : DavidYu.bbs@ptt2.twbbs.org */
+/* recast : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+#ifdef HAVE_CALENDAR
+
+#define HEADER_COLOR "\033[1;44m"
+#define CALENDAR_COLOR "\033[30;47m"
+#define CALENDAR_TODAY "\033[30;42m"
+
+
+/*-------------------------------------------------------*/
+/* ¤é´Á³B²z¨ç¦¡ */
+/*-------------------------------------------------------*/
+
+
+static int /* ³o­Ó¤ë¦³´X¤Ñ */
+month_day(y, m)
+ int y, m;
+{
+ static int day[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+ return ((m == 2) && ((y % 4 == 0 && y % 100 != 0) || (y % 400 == 0))) ? 29 : day[m - 1];
+}
+
+
+static time_t
+make_time(year, month, day)
+ int year, month, day;
+{
+ struct tm ptime;
+
+ ptime.tm_sec = 0;
+ ptime.tm_min = 0;
+ ptime.tm_hour = 0;
+ ptime.tm_mday = day;
+ ptime.tm_mon = month - 1;
+ ptime.tm_year = year - 1900;
+ ptime.tm_isdst = 0;
+#ifndef CYGWIN
+ ptime.tm_zone = "GMT";
+ ptime.tm_gmtoff = 0;
+#endif
+
+ return mktime(&ptime);
+}
+
+
+static time_t
+parse_date(date, year, month, day)
+ char *date; /* ¶Ç¤J date ®æ¦¡¬° 2003/08/25 */
+ int *year, *month, *day; /* ¶Ç¥X time_t year month day */
+{
+ char *yy, *mm, *dd;
+
+ yy = strtok(date, "/");
+ mm = strtok(NULL, "/");
+ dd = strtok(NULL, "");
+ if (!yy || !mm || !dd)
+ return 0;
+
+ *year = atoi(yy);
+ *month = atoi(mm);
+ *day = atoi(dd);
+ if (*year < 1 || *month < 1 || *month > 12 || *day < 1 || *day > 31)
+ return 0;
+
+ return make_time(*year, *month, *day);
+}
+
+
+/*-------------------------------------------------------*/
+/* ¨Æ¥ó³B²z¨ç¦¡ */
+/*-------------------------------------------------------*/
+
+
+typedef struct EVENT
+{
+ time_t chrono; /* µ´¹ï®É¶¡ */
+ int year, month, day; /* ¤é´Á: ¦~/¤ë/¤é */
+ char content[40]; /* ±Ô­z */
+ struct EVENT *next;
+} EVENT;
+
+
+static void
+event_insert(head, insert)
+ EVENT *head, *insert;
+{
+ EVENT *p, *next;
+
+ for (p = head; (next = p->next) && (next->chrono < insert->chrono); p = next)
+ ;
+ insert->next = p->next;
+ p->next = insert;
+}
+
+
+static void
+event_free(head)
+ EVENT *head;
+{
+ EVENT *n;
+
+ while (head)
+ {
+ n = head->next;
+ free(head);
+ head = n;
+ }
+}
+
+
+static EVENT *
+event_read(today)
+ time_t today;
+{
+ FILE *fp;
+ char buf[80];
+ EVENT head;
+
+ head.next = NULL;
+
+ usr_fpath(buf, cuser.userid, FN_TODO);
+ if (fp = fopen(buf, "r"))
+ {
+ while (fgets(buf, sizeof(buf), fp))
+ {
+ time_t chrono;
+ int year, month, day;
+ char *date, *content;
+ EVENT *t;
+
+ if (buf[0] == '#')
+ continue;
+
+ date = strtok(buf, " \n");
+ content = strtok(NULL, "\n");
+ if (!date || !content)
+ continue;
+
+ if ((chrono = parse_date(date, &year, &month, &day)) && (chrono >= today))
+ {
+ t = (EVENT *) malloc(sizeof(EVENT));
+
+ t->chrono = chrono;
+ t->year = year;
+ t->month = month;
+ t->day = day;
+ for (; *content == ' '; content++)
+ ;
+ str_ncpy(t->content, content, sizeof(t->content));
+
+ event_insert(&head, t);
+ }
+ }
+ fclose(fp);
+ }
+
+ return head.next;
+}
+
+
+/*-------------------------------------------------------*/
+/* ¤ë¾ä²£¥Í¾¹ */
+/*-------------------------------------------------------*/
+
+
+static char **
+AllocCalBuffer(line, len)
+ int line, len;
+{
+ int i;
+ char **p;
+
+ p = malloc(sizeof(char *) * line);
+ p[0] = malloc(sizeof(char) * line * len);
+ p[0][0] = '\0';
+ for (i = 1; i < line; i++)
+ {
+ p[i] = p[i - 1] + len;
+ p[i][0] = '\0';
+ }
+ return p;
+}
+
+
+static int
+GenerateCalendar(calendar, y, m, tm_mon, tm_mday) /* ²£¥Í¤ë¾ä */
+ char **calendar;
+ int y, m; /* ­n²£¥Í´X¦~´X¤ëªº¤ë¾ä */
+ int tm_mon, tm_mday; /* ¤µ¤Ñ¬O´X¤ë´X¤é */
+{
+ static char week_str[7][3] = {"¤é", "¤@", "¤G", "¤T", "¥|", "¤­", "¤»"};
+ static char month_color[12][8] =
+ {
+ "\033[1;32m", "\033[1;33m", "\033[1;35m", "\033[1;36m",
+ "\033[1;32m", "\033[1;33m", "\033[1;35m", "\033[1;36m",
+ "\033[1;32m", "\033[1;33m", "\033[1;35m", "\033[1;36m"
+ };
+ static char *month_str[12] =
+ {
+ "¤@¤ë ", "¤G¤ë ", "¤T¤ë ", "¥|¤ë ", "¤­¤ë ", "¤»¤ë ",
+ "¤C¤ë ", "¤K¤ë ", "¤E¤ë ", "¤Q¤ë ", "¤Q¤@¤ë", "¤Q¤G¤ë"
+ };
+
+ char *p;
+ int monthday, wday;
+ int i, line;
+ time_t first_day;
+ struct tm *ptime;
+
+ line = 0;
+ first_day = make_time(y, m, 1);
+ ptime = localtime(&first_day);
+
+ /* week day banner */
+ p = calendar[line];
+ p += sprintf(p, " %s ", HEADER_COLOR);
+ for (i = 0; i < 7; i++)
+ p += sprintf(p, "%s ", week_str[i]);
+ p += sprintf(p, "\033[m");
+
+ /* indent for first line */
+ p = calendar[++line];
+ p += sprintf(p, " %s ", CALENDAR_COLOR);
+ for (i = 0, wday = ptime->tm_wday; i < wday; i++)
+ p += sprintf(p, " ");
+
+ monthday = month_day(y, m);
+ for (i = 1; i <= monthday; i++, wday = (wday + 1) % 7)
+ {
+ if (m == tm_mon && i == tm_mday)
+ p += sprintf(p, "%s%2d%s", CALENDAR_TODAY, i, CALENDAR_COLOR);
+ else
+ p += sprintf(p, "%2d", i);
+
+ if (wday == 6)
+ {
+ p += sprintf(p, " \033[m");
+ p = calendar[++line];
+
+ if (line >= 2 && line <= 4) /* show month */
+ p += sprintf(p, "%s%2.2s\033[m %s ", month_color[m - 1], month_str[m - 1] + (line - 2) * 2, CALENDAR_COLOR);
+ else if (i < monthday)
+ p += sprintf(p, " %s ", CALENDAR_COLOR);
+ }
+ else
+ {
+ *p++ = ' ';
+ }
+ }
+
+ /* fill up the last line */
+ if (wday)
+ {
+ for (wday = 7 - wday; wday; wday--)
+ p += sprintf(p, " ");
+ p += sprintf(p, "\033[m");
+ }
+
+ return line + 1;
+}
+
+
+/*-------------------------------------------------------*/
+/* ¥Dµ{¦¡ */
+/*-------------------------------------------------------*/
+
+
+int
+main_todo()
+{
+ char **calendar, fpath[64];
+ time_t now, today;
+ struct tm *ptime, ntime;
+ int i, y, m;
+ int lines; /* ¥Ø«e¦³´X¦æ¨Æ¥ó */
+ EVENT *head, *e;
+
+ /* initialize date */
+ time(&now);
+ ptime = localtime(&now);
+ ntime = *ptime;
+ y = ntime.tm_year + 1900;
+ m = ntime.tm_mon + 1;
+ today = now - (now % 86400);
+
+ /* read event */
+ head = e = event_read(today);
+
+ /* generate calendar */
+ lines = 0;
+
+ calendar = AllocCalBuffer(21, 128); /* ¤T­Ó¤ë³Ì¦h­n 21 ¦C */
+ for (i = 0; i < 3; i++) /* ¨C¦¸¨q¥X¤T­Ó¤ëªº¦æ¨Æ¾ä */
+ {
+ lines += GenerateCalendar(calendar + lines, y, m, ntime.tm_mon + 1, ntime.tm_mday) + 1;
+ if (m == 12)
+ {
+ y++;
+ m = 1;
+ }
+ else
+ {
+ m++;
+ }
+ }
+
+ /* output */
+ vs_bar("¦æ¨Æ¾ä");
+
+ today /= 86400;
+
+ for (i = 0; i < 21; i++)
+ {
+ outs(calendar[i]);
+ if (i >= 2)
+ {
+ if (e)
+ {
+ prints("%*s\033[1;37m(%3d)\033[m %02d/%02d %s",
+ (i % 7) ? 5 : 31, "", e->chrono / 86400 - today, e->month, e->day, e->content);
+ e = e->next;
+ }
+ }
+ else if (i == 0)
+ {
+ prints(" \033[1;37m²{¦b¬O %d/%02d/%02d %2d:%02d:%02d%cm\033[m",
+ ntime.tm_year + 1900, ntime.tm_mon + 1, ntime.tm_mday,
+ (ntime.tm_hour == 0 || ntime.tm_hour == 12) ? 12 : ntime.tm_hour % 12,
+ ntime.tm_min, ntime.tm_sec,
+ ntime.tm_hour >= 12 ? 'p' : 'a');
+ }
+ outc('\n');
+ }
+ event_free(head);
+
+ /* edit */
+ switch (vans("¦æ¨Æ¾ä (D)§R°£ (E)­×§ï (Q)¨ú®ø¡H[Q] "))
+ {
+ case 'e':
+ more("etc/todo.welcome", NULL);
+ usr_fpath(fpath, cuser.userid, FN_TODO);
+ vedit(fpath, 0);
+ break;
+
+ case 'd':
+ usr_fpath(fpath, cuser.userid, FN_TODO);
+ unlink(fpath);
+ break;
+ }
+
+ return 0;
+}
+#endif /* HAVE_CALENDAR */
diff --git a/so/vote.c b/so/vote.c
new file mode 100644
index 0000000..7ea7318
--- /dev/null
+++ b/so/vote.c
@@ -0,0 +1,1336 @@
+/*-------------------------------------------------------*/
+/* vote.c ( NTHU CS MapleBBS Ver 2.36 ) */
+/*-------------------------------------------------------*/
+/* target : boards' vote routines */
+/* create : 95/03/29 */
+/* update : 95/12/15 */
+/*-------------------------------------------------------*/
+/* brd/_/.VCH : Vote Control Header ¥Ø«e©Ò¦³§ë²¼¯Á¤Þ */
+/* brd/_/@vote : vote history ¹L¥hªº§ë²¼¾ú¥v */
+/* brd/_/@/@_ : vote description §ë²¼»¡©ú */
+/* brd/_/@/I_ : vote selection Items §ë²¼¿ï¶µ */
+/* brd/_/@/O_ : users' Opinions ¨Ï¥ÎªÌ¦³¸Ü­n»¡ */
+/* brd/_/@/L_ : can vote List ¥i§ë²¼¦W³æ */
+/* brd/_/@/G_ : voted id loG file ¤w»â²¼¦W³æ */
+/* brd/_/@/Z_ : final/temporary result §ë²¼µ²ªG */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+
+extern BCACHE *bshm;
+extern XZ xz[];
+extern char xo_pool[];
+
+
+static char *
+vch_fpath(fpath, folder, vch)
+ char *fpath, *folder;
+ VCH *vch;
+{
+ /* VCH ©M HDR ªº xname Äæ¦ì¤Ç°t¡A©Ò¥Hª½±µ­É¥Î hdr_fpath() */
+ hdr_fpath(fpath, folder, (HDR *) vch);
+ return strrchr(fpath, '@');
+}
+
+
+static int vote_add();
+
+
+int
+vote_result(xo)
+ XO *xo;
+{
+ char fpath[64];
+
+ setdirpath(fpath, xo->dir, "@/@vote");
+ /* Thor.990204: ¬°¦Ò¼{more ¶Ç¦^­È */
+ if (more(fpath, NULL) >= 0)
+ return XO_HEAD; /* XZ_POST ©M XZ_VOTE ¦@¥Î vote_result() */
+
+ vmsg("¥Ø«e¨S¦³¥ô¦ó¶}²¼ªºµ²ªG");
+ return XO_FOOT;
+}
+
+
+static void
+vote_item(num, vch)
+ int num;
+ VCH *vch;
+{
+ prints("%6d%c%c%c%c%c %-9.8s%-12s %.44s\n",
+ num, tag_char(vch->chrono), vch->vgamble, vch->vsort, vch->vpercent, vch->vprivate,
+ vch->cdate, vch->owner, vch->title);
+}
+
+
+static int
+vote_body(xo)
+ XO *xo;
+{
+ VCH *vch;
+ int num, max, tail;
+
+ max = xo->max;
+ if (max <= 0)
+ {
+ if (bbstate & STAT_BOARD)
+ {
+ if (vans("­nÁ|¿ì§ë²¼¶Ü(Y/N)¡H[N] ") == 'y')
+ return vote_add(xo);
+ }
+ else
+ {
+ vmsg("¥Ø«e¨ÃµL§ë²¼Á|¦æ");
+ }
+ return XO_QUIT;
+ }
+
+ vch = (VCH *) xo_pool;
+ num = xo->top;
+ tail = num + XO_TALL;
+ if (max > tail)
+ max = tail;
+
+ move(3, 0);
+ do
+ {
+ vote_item(++num, vch++);
+ } while (num < max);
+ clrtobot();
+
+ /* return XO_NONE; */
+ return XO_FOOT; /* itoc.010403: §â b_lines ¶ñ¤W feeter */
+}
+
+
+static int
+vote_head(xo)
+ XO *xo;
+{
+ vs_head(currBM, "§ë²¼©Ò");
+ prints(NECKER_VOTE, d_cols, "");
+ return vote_body(xo);
+}
+
+
+static int
+vote_init(xo)
+ XO *xo;
+{
+ xo_load(xo, sizeof(VCH));
+ return vote_head(xo);
+}
+
+
+static int
+vote_load(xo)
+ XO *xo;
+{
+ xo_load(xo, sizeof(VCH));
+ return vote_body(xo);
+}
+
+
+static void
+vch_edit(vch, item, echo)
+ VCH *vch;
+ int item; /* vlist ¦³´X¶µ */
+ int echo;
+{
+ int num;
+ char ans[8], buf[80];
+
+ if (echo == DOECHO) /* ¥u¦³·s¼W®É¤~¯à¨M©w¬O§_¬°½ä½L */
+ vch->vgamble = (vget(b_lines - 6, 0, "¬O§_¬°½ä½L(Y/N)¡H[N] ", ans, 3, LCECHO) == 'y') ? '$' : ' ';
+
+ if (vch->vgamble == ' ')
+ {
+ sprintf(buf, "½Ð°Ý¨C¤H³Ì¦h¥i§ë´X²¼¡H([1]¡ã%d)¡G", item);
+ vget(b_lines - 5, 0, buf, ans, 3, DOECHO);
+ num = atoi(ans);
+ if (num < 1)
+ num = 1;
+ else if (num > item)
+ num = item;
+ vch->maxblt = num;
+ }
+ else if (echo == DOECHO) /* ¥u¦³·s¼W®É¤~¯à§ïÅܽä½Lªº²¼»ù */
+ {
+ /* ½ä½L´N¥u¯à¿ï¤@¶µ */
+ vch->maxblt = 1;
+
+ vget(b_lines - 5, 0, "½Ð°Ý¨C²¼°â»ù¦h¤Ö»È¹ô¡H(100¡ã100000)¡G", ans, 7, DOECHO);
+ num = atoi(ans);
+ if (num < 100)
+ num = 100;
+ else if (num > 100000)
+ num = 100000;
+ vch->price = num;
+ }
+
+ vget(b_lines - 4, 0, "¥»¶µ§ë²¼¶i¦æ´X¤p®É (¦Ü¤Ö¤@¤p®É)¡H[1] ", ans, 5, DOECHO);
+ num = atoi(ans);
+ if (num < 1)
+ num = 1;
+ vch->vclose = vch->chrono + num * 3600;
+ str_stamp(vch->cdate, &vch->vclose);
+
+ if (vch->vgamble == ' ') /* ½ä½L¤@©w±Æ§Ç¡B¤ÎÅã¥Ü¦Ê¤À¤ñ */
+ {
+ vch->vsort = (vget(b_lines - 3, 0, "¶}²¼µ²ªG¬O§_±Æ§Ç(Y/N)¡H[N] ", ans, 3, LCECHO) == 'y') ? 's' : ' ';
+ vch->vpercent = (vget(b_lines - 2, 0, "¶}²¼µ²ªG¬O§_Åã¥Ü¦Ê¤À¤ñ¨Ò(Y/N)¡H[N] ", ans, 3, LCECHO) == 'y') ? '%' : ' ';
+ }
+ else
+ {
+ vch->vsort = 's';
+ vch->vpercent = '%';
+ }
+
+ vch->vprivate = (vget(b_lines - 1, 0, "¬O§_­­¨î§ë²¼¦W³æ(Y/N)¡H[N] ", ans, 3, LCECHO) == 'y') ? ')' : ' ';
+}
+
+
+static int
+vlist_edit(vlist)
+ vitem_t vlist[];
+{
+ int item;
+ char buf[80];
+
+ clear();
+
+ outs("½Ð¨Ì§Ç¿é¤J¿ï¶µ (³Ì¦h 32 ¶µ)¡A«ö ENTER µ²§ô¡G");
+
+ strcpy(buf, " ) ");
+ for (;;)
+ {
+ item = 0;
+ for (;;)
+ {
+ buf[0] = radix32[item];
+ if (!vget((item & 15) + 3, (item / 16) * 40, buf, vlist[item], sizeof(vitem_t), GCARRY) ||
+ (++item >= MAX_CHOICES))
+ break;
+ }
+ if (item && vans("¬O§_­«·s¿é¤J¿ï¶µ(Y/N)¡H[N] ") != 'y')
+ break;
+ }
+ return item;
+}
+
+
+static int
+vlog_seek(fpath)
+ char *fpath;
+{
+ VLOG old;
+ int fd;
+ int rc = 0;
+
+ if ((fd = open(fpath, O_RDONLY)) >= 0)
+ {
+ while (read(fd, &old, sizeof(VLOG)) == sizeof(VLOG))
+ {
+ if (!strcmp(old.userid, cuser.userid))
+ {
+ rc = 1;
+ break;
+ }
+ }
+ close(fd);
+ }
+ return rc;
+}
+
+
+static int
+vote_add(xo)
+ XO *xo;
+{
+ VCH vch;
+ int fd, item;
+ char *dir, *str, fpath[64], title[TTLEN + 1];
+ vitem_t vlist[MAX_CHOICES];
+ BRD *brd;
+
+ if (!(bbstate & STAT_BOARD))
+ return XO_NONE;
+
+ if (!vget(b_lines, 0, "¼ÐÃD¡G", title, TTLEN + 1, DOECHO))
+ return xo->max ? XO_FOOT : vote_body(xo); /* itoc.011125: ¦pªG¨S¦³¥ô¦ó§ë²¼¡A­n¦^¨ì vote_body() */
+ /* return XO_FOOT; */
+
+ dir = xo->dir;
+ if ((fd = hdr_stamp(dir, 0, (HDR *) &vch, fpath)) < 0)
+ {
+ vmsg("µLªk«Ø¥ß§ë²¼»¡©úÀÉ");
+ return XO_FOOT;
+ }
+
+ close(fd);
+ vmsg("¶}©l½s¿è [§ë²¼»¡©ú]");
+ fd = vedit(fpath, 0); /* Thor.981020: ª`·N³Qtalkªº°ÝÃD */
+ if (fd)
+ {
+ unlink(fpath);
+ vmsg("¨ú®ø§ë²¼");
+ return vote_head(xo);
+ }
+
+ strcpy(vch.title, title);
+ str = strrchr(fpath, '@');
+
+ /* --------------------------------------------------- */
+ /* §ë²¼¿ï¶µÀÉ : Item */
+ /* --------------------------------------------------- */
+
+ memset(vlist, 0, sizeof(vlist));
+ item = vlist_edit(vlist);
+
+ *str = 'I';
+ if ((fd = open(fpath, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0)
+ {
+ vmsg("µLªk«Ø¥ß§ë²¼¿ï¶µÀÉ");
+ return vote_head(xo);
+ }
+ write(fd, vlist, item * sizeof(vitem_t));
+ close(fd);
+
+ vch_edit(&vch, item, DOECHO);
+
+ strcpy(vch.owner, cuser.userid);
+
+ brd = bshm->bcache + currbno;
+
+ brd->bvote++;
+ if (brd->bvote >= 0)
+ brd->bvote = (vch.vgamble == '$') ? -1 : 1;
+ vch.bstamp = brd->bstamp;
+
+ rec_add(dir, &vch, sizeof(vch));
+
+ vmsg("¶}©l§ë²¼¤F¡I");
+ return vote_init(xo);
+}
+
+
+static int
+vote_edit(xo)
+ XO *xo;
+{
+ int pos;
+ VCH *vch, vxx;
+ char *dir, fpath[64];
+
+ /* Thor: for ­×§ï§ë²¼¿ï¶µ */
+ int fd, item;
+ vitem_t vlist[MAX_CHOICES];
+ char *fname;
+
+ if (!(bbstate & STAT_BOARD))
+ return XO_NONE;
+
+ pos = xo->pos;
+ dir = xo->dir;
+ vch = (VCH *) xo_pool + (pos - xo->top);
+
+ /* Thor: ­×§ï§ë²¼¥DÃD */
+
+ vxx = *vch;
+
+ if (!vget(b_lines, 0, "¼ÐÃD¡G", vxx.title, TTLEN + 1, GCARRY))
+ return XO_FOOT;
+
+ fname = vch_fpath(fpath, dir, vch);
+ vedit(fpath, 0); /* Thor.981020: ª`·N³Qtalkªº°ÝÃD */
+
+ /* Thor: ­×§ï§ë²¼¿ï¶µ */
+
+ memset(vlist, 0, sizeof(vlist));
+ *fname = 'I';
+ if ((fd = open(fpath, O_RDONLY)) >= 0)
+ {
+ read(fd, vlist, sizeof(vlist));
+ close(fd);
+ }
+
+ item = vlist_edit(vlist);
+
+ if ((fd = open(fpath, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0)
+ {
+ vmsg("µLªk«Ø¥ß§ë²¼¿ï¶µÀÉ");
+ return vote_head(xo);
+ }
+ write(fd, vlist, item * sizeof(vitem_t));
+ close(fd);
+
+ vch_edit(&vxx, item, GCARRY);
+
+ if (memcmp(&vxx, vch, sizeof(VCH)))
+ {
+ if (vans("½T©w­n­×§ï³o¶µ§ë²¼¶Ü(Y/N)¡H[N] ") == 'y')
+ {
+ *vch = vxx;
+ currchrono = vch->chrono;
+ rec_put(dir, vch, sizeof(VCH), pos, cmpchrono);
+ }
+ }
+
+ return vote_head(xo);
+}
+
+
+static int
+vote_query(xo)
+ XO *xo;
+{
+ char *dir, *fname, fpath[64], buf[80];
+ VCH *vch;
+ int cc, pos;
+
+ if (!(bbstate & STAT_BOARD))
+ return XO_NONE;
+
+ pos = xo->pos;
+ dir = xo->dir;
+ vch = (VCH *) xo_pool + (pos - xo->top);
+
+ fname = vch_fpath(fpath, dir, vch);
+ more(fpath, (char *) -1);
+
+ *fname = 'G';
+ sprintf(buf, "¦@¦³ %d ¤H°Ñ¥[§ë²¼¡A½T©w­n±N¶}²¼®É¶¡§ï´Á(Y/N)¡H[N] ", rec_num(fpath, sizeof(VLOG)));
+ if (vans(buf) == 'y')
+ {
+ vget(b_lines, 0, "½Ð§ó§ï¶}²¼®É¶¡(-n´£«en¤p®É/+m©µ«ám¤p®É/0¤£§ï)¡G", buf, 5, DOECHO);
+ if (cc = atoi(buf))
+ {
+ vch->vclose = vch->vclose + cc * 3600;
+ str_stamp(vch->cdate, &vch->vclose);
+ currchrono = vch->chrono;
+ rec_put(dir, vch, sizeof(VCH), pos, cmpchrono);
+ }
+ }
+
+ return vote_head(xo);
+}
+
+
+static int
+vfyvch(vch, pos)
+ VCH *vch;
+ int pos;
+{
+ return Tagger(vch->chrono, pos, TAG_NIN);
+}
+
+
+static void
+delvch(xo, vch)
+ XO *xo;
+ VCH *vch;
+{
+ int fd;
+ char fpath[64], buf[64], *fname;
+ char *list = "@IOLGZ"; /* itoc.µù¸Ñ: ²M vote file */
+ VLOG vlog;
+ PAYCHECK paycheck;
+
+ fname = vch_fpath(fpath, xo->dir, vch);
+
+ if (vch->vgamble == '$') /* itoc.050313: ¦pªG¬O½ä½L³Q§R°£¡A¨º»ò­n°h½äª÷ */
+ {
+ *fname = 'G';
+
+ if ((fd = open(fpath, O_RDONLY)) >= 0)
+ {
+ memset(&paycheck, 0, sizeof(PAYCHECK));
+ time(&paycheck.tissue);
+ sprintf(paycheck.reason, "[°h´Ú] %s", currboard);
+
+ while (read(fd, &vlog, sizeof(VLOG)) == sizeof(VLOG))
+ {
+ paycheck.money = vlog.numvotes * vch->price;
+ usr_fpath(buf, vlog.userid, FN_PAYCHECK);
+ rec_add(buf, &paycheck, sizeof(PAYCHECK));
+ }
+ }
+ close(fd);
+ }
+
+ while (*fname = *list++)
+ unlink(fpath); /* Thor: ½T©w¦W¦r´N¬å */
+}
+
+
+
+static int
+vote_delete(xo)
+ XO *xo;
+{
+ int pos;
+ VCH *vch;
+
+ if (!(bbstate & STAT_BOARD))
+ return XO_NONE;
+
+ pos = xo->pos;
+ vch = (VCH *) xo_pool + (pos - xo->top);
+
+ if (vans(msg_del_ny) == 'y')
+ {
+ delvch(xo, vch);
+
+ currchrono = vch->chrono;
+ rec_del(xo->dir, sizeof(VCH), pos, cmpchrono);
+ return vote_load(xo);
+ }
+
+ return XO_FOOT;
+}
+
+
+static int
+vote_rangedel(xo)
+ XO *xo;
+{
+ if (!(bbstate & STAT_BOARD))
+ return XO_NONE;
+
+ return xo_rangedel(xo, sizeof(VCH), NULL, delvch);
+}
+
+
+static int
+vote_prune(xo)
+ XO *xo;
+{
+ if (!(bbstate & STAT_BOARD))
+ return XO_NONE;
+
+ return xo_prune(xo, sizeof(VCH), vfyvch, delvch);
+}
+
+
+static int
+vote_pal(xo) /* itoc.020117: ½s¿è­­¨î§ë²¼¦W³æ */
+ XO *xo;
+{
+ char *fname, fpath[64];
+ VCH *vch;
+ XO *xt;
+
+ if (!(bbstate & STAT_BOARD))
+ return XO_NONE;
+
+ vch = (VCH *) xo_pool + (xo->pos - xo->top);
+
+ if (vch->vprivate != ')')
+ return XO_NONE;
+
+ fname = vch_fpath(fpath, xo->dir, vch);
+ *fname = 'L';
+
+ xz[XZ_PAL - XO_ZONE].xo = xt = xo_new(fpath);
+ xt->key = PALTYPE_VOTE;
+ xover(XZ_PAL); /* Thor: ¶ixover«e, pal_xo ¤@©w­n ready */
+
+ free(xt);
+ return vote_init(xo);
+}
+
+
+static int
+vote_join(xo)
+ XO *xo;
+{
+ VCH *vch, vbuf;
+ VLOG vlog;
+ int count, fd;
+ usint choice;
+ char *dir, *fname, fpath[64], buf[80], ans[4], *slist[MAX_CHOICES];
+ vitem_t vlist[MAX_CHOICES];
+
+ if (HAS_STATUS(STATUS_COINLOCK))
+ {
+ vmsg(msg_coinlock);
+ return XO_FOOT;
+ }
+
+ vch = (VCH *) xo_pool + (xo->pos - xo->top);
+
+ /* --------------------------------------------------- */
+ /* Àˬd¬O§_¤w¸gµ²§ô§ë²¼ */
+ /* --------------------------------------------------- */
+
+ if (time(0) > vch->vclose)
+ {
+ vmsg("§ë²¼¤w¸gºI¤î¤F¡A½ÐÀR­Ô¶}²¼");
+ return XO_FOOT;
+ }
+
+ /* --------------------------------------------------- */
+ /* Àˬd¬O§_¦³¨¬°÷¿ú */
+ /* --------------------------------------------------- */
+
+ if (vch->vgamble == '$')
+ {
+ if (cuser.money < vch->price)
+ {
+ vmsg("±zªº¿ú¤£°÷°Ñ¥[½ä½L");
+ return XO_FOOT;
+ }
+ if (vans("¬O§_°Ñ¥[½ä½L(Y/N)¡H[N] ") != 'y')
+ return XO_FOOT;
+ }
+
+ /* --------------------------------------------------- */
+ /* §ë²¼ÀÉ®× */
+ /* --------------------------------------------------- */
+
+ dir = xo->dir;
+ fname = vch_fpath(fpath, dir, vch);
+
+ /* --------------------------------------------------- */
+ /* Àˬd¬O§_¤w¸g§ë¹L²¼ */
+ /* --------------------------------------------------- */
+
+ if (vch->vgamble == ' ') /* itoc.031101: ½ä½L¥i¥H¤@ª½¤Uª` */
+ {
+ *fname = 'G';
+ if (vlog_seek(fpath))
+ {
+ vmsg("±z¤w¸g§ë¹L²¼¤F¡I");
+ return XO_FOOT;
+ }
+ if (vans("¬O§_°Ñ¥[§ë²¼(Y/N)¡H[N] ") != 'y')
+ return XO_FOOT;
+ }
+
+ /* --------------------------------------------------- */
+ /* Àˬd¬O§_¦b§ë²¼¦W³æ¤¤ */
+ /* --------------------------------------------------- */
+
+ if (vch->vprivate == ')') /* itoc.020117: ¨p¤H§ë²¼ */
+ {
+ *fname = 'L';
+
+ if (!pal_find(fpath, cuser.userno) &&
+ !(bbstate & STAT_BOARD)) /* ¥Ñ©ó¨Ã¤£¯à§â¦Û¤v¥[¤JªB¤Í¦W³æ¡A©Ò¥H­n¦hÀˬd¬O§_¬°ªO¥D */
+ {
+ vmsg("±z¨S¦³¨üÁÜ¥»¦¸¨p¤H§ë²¼¡I");
+ return XO_FOOT;
+ }
+ }
+
+ /* --------------------------------------------------- */
+ /* ¶}©l§ë²¼¡AÅã¥Ü§ë²¼»¡©ú */
+ /* --------------------------------------------------- */
+
+ *fname = '@';
+ more(fpath, NULL);
+
+ /* --------------------------------------------------- */
+ /* ¸ü¤J§ë²¼¿ï¶µÀÉ */
+ /* --------------------------------------------------- */
+
+ *fname = 'I';
+ if ((fd = open(fpath, O_RDONLY)) < 0)
+ {
+ vmsg("µLªkŪ¨ú§ë²¼¿ï¶µÀÉ");
+ return vote_head(xo);
+ }
+ count = read(fd, vlist, sizeof(vlist)) / sizeof(vitem_t);
+ close(fd);
+
+ for (fd = 0; fd < count; fd++)
+ slist[fd] = (char *) &vlist[fd];
+
+ /* --------------------------------------------------- */
+ /* ¶i¦æ§ë²¼ */
+ /* --------------------------------------------------- */
+
+ choice = 0;
+ sprintf(buf, "§ë¤U¯«¸tªº %d ²¼", vch->maxblt); /* Thor: Åã¥Ü³Ì¦h´X²¼ */
+ vs_bar(buf);
+ outs("§ë²¼¥DÃD¡G");
+ for (;;)
+ {
+ choice = bitset(choice, count, vch->maxblt, vch->title, slist);
+
+ if (vch->vgamble == ' ') /* ¤@¯ë§ë²¼¤~¯à¼g·N¨£ */
+ vget(b_lines - 1, 0, "§Ú¦³¸Ü­n»¡¡G", buf, 60, DOECHO);
+
+ fd = vans("§ë²¼ (Y)½T©w (N)­«¨Ó (Q)¨ú®ø¡H[N] ");
+
+ if (fd == 'q')
+ return vote_head(xo);
+
+ if ((fd == 'y') && (vch->vgamble == ' ' || choice)) /* ­Y¬O½ä½L«h¤@©w­n¿ï */
+ break;
+ }
+
+ /* --------------------------------------------------- */
+ /* °O¿ýµ²ªG¡G¤@²¼¤]¥¼§ëªº±¡ªp ==> ¬Û·í©ó§ë¼o²¼ */
+ /* --------------------------------------------------- */
+
+ if (vch->vgamble == '$')
+ {
+ /* ½ä½L¥i¥H¶R¤J¦h±i */
+ for (;;)
+ {
+ sprintf(buf, "¨C±i½ä²¼ %d »È¹ô¡A½Ð°Ý­n¶R´X±i¡H[1] ", vch->price);
+ vget(b_lines, 0, buf, ans, 3, DOECHO); /* ³Ì¦h¶R 99 ±i¡AÁ×§K·¸¦ì */
+
+ if (time(0) > vch->vclose) /* ¦]¬°¦³­Ó vget¡A©Ò¥HÁÙ­n¦AÀˬd¤@¦¸ */
+ {
+ vmsg("§ë²¼¤w¸gºI¤î¤F¡A½ÐÀR­Ô¶}²¼");
+ return vote_head(xo);
+ }
+
+ if ((count = atoi(ans)) < 1)
+ count = 1;
+ fd = count * vch->price;
+ if (cuser.money >= fd)
+ break;
+ }
+ }
+ else
+ {
+ /* ¤@¯ë§ë²¼´N¬O¤@±i²¼ */
+ count = 1;
+ }
+
+ /* ½T©w§ë²¼©|¥¼ºI¤î */
+ /* itoc.050514: ¦]¬°ªO¥D¥i¥H§ïÅܶ}²¼®É¶¡¡A¬°¤FÁ×§K¨Ï¥ÎªÌ·|Àt¦b vget() ©Î¬O
+ §Q¥Î xo_pool[] ¥¼¦P¨B¨Ó³WÁ× time(0) > vclose ªºÀˬd¡A©Ò¥H´N±o­«·s¸ü¤J VCH */
+ if (rec_get(dir, &vbuf, sizeof(VCH), xo->pos) || vch->chrono != vch->chrono || time(0) > vbuf.vclose)
+ {
+ vmsg("§ë²¼¤w¸gºI¤î¤F¡A½ÐÀR­Ô¶}²¼");
+ return vote_init(xo);
+ }
+
+ if (vch->vgamble == '$')
+ {
+ cuser.money -= fd; /* fd ¬O­n¥Iªº½äª÷ */
+ }
+ else if (*buf) /* ¤@¯ë§ë²¼¤~¯à¼g¤J¨Ï¥ÎªÌ·N¨£ */
+ {
+ FILE *fp;
+
+ *fname = 'O';
+ if (fp = fopen(fpath, "a"))
+ {
+ fprintf(fp, "¡E%-12s¡G%s\n", cuser.userid, buf);
+ fclose(fp);
+ }
+ }
+
+ /* ¥[¤J°O¿ýÀÉ */
+ memset(&vlog, 0, sizeof(VLOG));
+ strcpy(vlog.userid, cuser.userid);
+ vlog.numvotes = count;
+ vlog.choice = choice;
+ *fname = 'G';
+ rec_add(fpath, &vlog, sizeof(VLOG));
+
+ vmsg("§ë²¼§¹¦¨¡I");
+ return vote_head(xo);
+}
+
+
+struct Tchoice
+{
+ int count;
+ vitem_t vitem;
+};
+
+
+static int
+TchoiceCompare(i, j)
+ struct Tchoice *i, *j;
+{
+ return j->count - i->count;
+}
+
+
+static char * /* NULL:¥¢±Ñ(ÁÙ¨S¦³¤H§ë²¼) */
+draw_vote(fpath, folder, vch, preview) /* itoc.030906: §ë²¼µ²ªG (»P account.c:draw_vote() ®æ¦¡¬Û¦P) */
+ char *fpath;
+ char *folder;
+ VCH *vch;
+ int preview; /* 1:¹wÄý 0:¶}²¼ */
+{
+ struct Tchoice choice[MAX_CHOICES];
+ FILE *fp;
+ char *fname;
+ int total, items, num, fd, ticket, bollt;
+ VLOG vlog;
+
+ fname = vch_fpath(fpath, folder, vch);
+
+ /* vote item */
+
+ *fname = 'I';
+
+ items = 0;
+ if (fp = fopen(fpath, "r"))
+ {
+ while (fread(&choice[items].vitem, sizeof(vitem_t), 1, fp) == 1)
+ {
+ choice[items].count = 0;
+ items++;
+ }
+ fclose(fp);
+ }
+
+ if (items == 0)
+ return NULL;
+
+ /* ²Ö­p§ë²¼µ²ªG */
+
+ *fname = 'G';
+ bollt = 0; /* Thor: Á`²¼¼ÆÂk¹s */
+ total = 0;
+
+ if ((fd = open(fpath, O_RDONLY)) >= 0)
+ {
+ while (read(fd, &vlog, sizeof(VLOG)) == sizeof(VLOG))
+ {
+ for (ticket = vlog.choice, num = 0; ticket && num < items; ticket >>= 1, num++)
+ {
+ if (ticket & 1)
+ {
+ choice[num].count += vlog.numvotes;
+ bollt += vlog.numvotes;
+ }
+ }
+ total++;
+ }
+ close(fd);
+ }
+
+ /* ²£¥Í¶}²¼µ²ªG */
+
+ *fname = 'Z';
+ if (!(fp = fopen(fpath, "w")))
+ return NULL;
+
+ fprintf(fp, "\n\033[1;34m%s\033[m\n\n"
+ "\033[1;32m¡» [%s] ¬ÝªO§ë²¼¡G%s\033[m\n\nÁ|¿ìªO¥D¡G%s\n\nÁ|¿ì¤é´Á¡G%s\n\n",
+ msg_seperator, currboard, vch->title, vch->owner, Btime(&vch->chrono));
+ fprintf(fp, "¶}²¼¤é´Á¡G%s\n\n\033[1;32m¡» §ë²¼¥DÃD¡G\033[m\n\n", Btime(&vch->vclose));
+
+ *fname = '@';
+ f_suck(fp, fpath);
+
+ fprintf(fp, "\n\033[1;32m¡» §ë²¼µ²ªG¡G¨C¤H¥i§ë %d ²¼¡A¦@ %d ¤H°Ñ¥[¡A§ë¥X %d ²¼\033[m\n\n",
+ vch->maxblt, total, bollt);
+
+ if (vch->vsort == 's')
+ qsort(choice, items, sizeof(struct Tchoice), TchoiceCompare);
+
+ if (vch->vpercent == '%')
+ fd = BMAX(1, bollt);
+ else
+ fd = 0;
+
+ if (preview && vch->vgamble == ' ') /* ¥u¦³¹wÄý½ä½L¤~»Ý­nÅã¥Ü½ß²v */
+ preview = 0;
+
+ for (num = 0; num < items; num++)
+ {
+ ticket = choice[num].count;
+ if (preview) /* Åã¥Ü¥[¶R¤@±i®Éªº½ß²v */
+ fprintf(fp, " %-36s%5d ²¼ (%4.1f%%) ½ß²v 1:%.3f\n", &choice[num].vitem, ticket, 100.0 * ticket / fd, 0.9 * (bollt + 1) / (ticket + 1));
+ else if (fd)
+ fprintf(fp, " %-36s%5d ²¼ (%4.1f%%)\n", &choice[num].vitem, ticket, 100.0 * ticket / fd);
+ else
+ fprintf(fp, " %-36s%5d ²¼\n", &choice[num].vitem, ticket);
+ }
+
+ /* other opinions */
+
+ *fname = 'O';
+ fputs("\n\033[1;32m¡» §Ú¦³¸Ü­n»¡¡G\033[m\n\n", fp);
+ f_suck(fp, fpath);
+ fputs("\n", fp);
+ fclose(fp);
+
+ /* ³Ì«á¶Ç¦^ªº fpath §Y¬°§ë²¼µ²ªGÀÉ */
+ *fname = 'Z';
+ return fname;
+}
+
+
+static int
+vote_view(xo)
+ XO *xo;
+{
+ char fpath[64];
+ VCH *vch;
+
+ vch = (VCH *) xo_pool + (xo->pos - xo->top);
+
+ if (bbstate & STAT_BOARD || vch->vgamble == '$')
+ {
+ if (draw_vote(fpath, xo->dir, vch, 1))
+ {
+ more(fpath, NULL);
+ unlink(fpath);
+ return vote_head(xo);
+ }
+
+ vmsg("¥Ø«e©|¥¼¦³¤H§ë²¼");
+ return XO_FOOT;
+ }
+
+ return XO_NONE;
+}
+
+
+static void
+keeplog(fnlog, board, title)
+ char *fnlog;
+ char *board;
+ char *title;
+{
+ HDR hdr;
+ char folder[64], fpath[64];
+ FILE *fp;
+
+ if (!dashf(fnlog)) /* Kudo.010804: Àɮ׬OªÅªº´N¤£ keeplog */
+ return;
+
+ brd_fpath(folder, board, fn_dir);
+
+ if (fp = fdopen(hdr_stamp(folder, 'A', &hdr, fpath), "w"))
+ {
+ fprintf(fp, "§@ªÌ: %s (%s)\n¼ÐÃD: %s\n®É¶¡: %s\n\n",
+ str_sysop, SYSOPNICK, title, Btime(&hdr.chrono));
+ f_suck(fp, fnlog);
+ fclose(fp);
+
+ strcpy(hdr.title, title);
+ strcpy(hdr.owner, str_sysop);
+ rec_bot(folder, &hdr, sizeof(HDR));
+
+ btime_update(brd_bno(board));
+ }
+}
+
+
+static void
+vlog_pay(fpath, choice, fp, vch)/* ½ß¿úµ¹©ã¹ïªº¨Ï¥ÎªÌ */
+ char *fpath; /* °O¿ýÀɸô®| */
+ usint choice; /* ¥¿½Tªºµª®× */
+ FILE *fp; /* ¼g¤JªºÀÉ®× */
+ VCH *vch;
+{
+ int fd;
+ int correct, bollt; /* ©ã¹ï/¥þ³¡ ªº²¼¼Æ */
+ int single, money;
+ char buf[64];
+ VLOG vlog;
+ PAYCHECK paycheck;
+
+ if ((fd = open(fpath, O_RDONLY)) >= 0)
+ {
+ /* ²Ä¤@°éºâ¥X½ß²v */
+ correct = bollt = 0;
+ while (read(fd, &vlog, sizeof(VLOG)) == sizeof(VLOG))
+ {
+ bollt += vlog.numvotes;
+ if (vlog.choice == choice)
+ correct += vlog.numvotes;
+ }
+
+ /* µ¹ªO¥D©âÀY 1% */
+ money = (INT_MAX / vch->price) * 100; /* BioStar.050626: Á×§K·¸¦ì */
+ money = (bollt > money) ? INT_MAX : vch->price / 100 * bollt;
+ fprintf(fp, "ªO¥D %s ©âÀY¡A¥iÀò±o %d »È¹ô\n", vch->owner, money);
+
+ memset(&paycheck, 0, sizeof(PAYCHECK));
+ time(&paycheck.tissue);
+ paycheck.money = money;
+ sprintf(paycheck.reason, "[©âÀY] %s", currboard);
+ usr_fpath(buf, vch->owner, FN_PAYCHECK);
+ rec_add(buf, &paycheck, sizeof(PAYCHECK));
+
+ if (correct) /* ¦pªG¨S¤H©ã¤¤¡A´N¤£»Ý­nµo¿ú */
+ {
+ /* µo¼úª÷¡A¨t²Î©â 10% ªºµ| */
+ single = (float) vch->price * 0.9 * bollt / correct;
+ fprintf(fp, "¨C±i¥iÀò %d »È¹ô¡A©ã¹ïªº¨Ï¥ÎªÌ¦³¡G\n", single);
+
+ /* ²Ä¤G°é¶}©lµo¿ú */
+ lseek(fd, (off_t) 0, SEEK_SET);
+ while (read(fd, &vlog, sizeof(VLOG)) == sizeof(VLOG))
+ {
+ if (vlog.choice == choice)
+ {
+ money = INT_MAX / single; /* BioStar.050626: Á×§K·¸¦ì */
+ money = (vlog.numvotes > money) ? INT_MAX : single * vlog.numvotes;
+ fprintf(fp, "%s ¶R¤F %d ±i¡A¦@¥iÀò±o %d »È¹ô\n", vlog.userid, vlog.numvotes, money);
+
+ paycheck.money = money;
+ sprintf(paycheck.reason, "[½ä½L] %s", currboard);
+ usr_fpath(buf, vlog.userid, FN_PAYCHECK);
+ rec_add(buf, &paycheck, sizeof(PAYCHECK));
+ }
+ }
+ }
+
+ close(fd);
+ }
+}
+
+
+static int
+vote_open(xo)
+ XO *xo;
+{
+ int pos, fd, count;
+ char *dir, *fname, fpath[64], buf[80];
+ usint choice;
+ char *slist[MAX_CHOICES];
+ vitem_t vlist[MAX_CHOICES];
+ VCH *vch;
+ FILE *fp;
+
+ if (!(bbstate & STAT_BOARD))
+ return XO_NONE;
+
+ pos = xo->pos;
+ vch = (VCH *) xo_pool + (pos - xo->top);
+
+ if (time(NULL) < vch->vclose)
+ {
+ if (vans("©|¥¼¨ì­ì©w¶}²¼®É¶¡¡A½T©w­n´£¦­¶}²¼(Y/N)¡H[N] ") != 'y')
+ return XO_FOOT;
+ }
+
+ dir = xo->dir;
+
+ /* §ë²¼µ²ªG */
+
+ if (!(fname = draw_vote(fpath, dir, vch, 0)))
+ {
+ vmsg("¥Ø«e©|¥¼¦³¤H§ë²¼");
+ return XO_FOOT;
+ }
+
+ if (vch->vgamble == '$') /* ½ä½L */
+ {
+ /* ªO¥D¿é¤Jµ²ªG¡A¨Ã¼g¤J§ë²¼µ²ªG */
+ while (!vget(b_lines, 0, "½Ð¿é¤J¥¿½Tµª®×¡G", buf, 60, DOECHO))
+ ;
+
+ /* ¸ü¤J§ë²¼¿ï¶µÀÉ */
+ *fname = 'I';
+ if ((fd = open(fpath, O_RDONLY)) >= 0)
+ {
+ count = read(fd, vlist, sizeof(vlist)) / sizeof(vitem_t);
+ close(fd);
+
+ for (fd = 0; fd < count; fd++)
+ slist[fd] = (char *) &vlist[fd];
+
+ /* ªO¥D¿ï¥X¥¿½Tµª®× */
+ choice = 0;
+ vs_bar("¿ï¾Ü¥¿½Tµª®×");
+ outs("§ë²¼¥DÃD¡G");
+ for (;;)
+ {
+ choice = bitset(choice, count, vch->maxblt, vch->title, slist);
+
+ fd = vans("¶}²¼ (Y)½T©w (N)­«¨Ó (Q)¨ú®ø¡H[N] ");
+
+ if (fd == 'q')
+ {
+ *fname = 'Z';
+ unlink(fpath);
+ return vote_head(xo);
+ }
+
+ if (fd == 'y' && choice) /* ­Y¬O½ä½L«h¤@©w­n¿ï */
+ break;
+ }
+
+ /* ¶}©lµo¿ú */
+ *fname = 'Z';
+ if (fp = fopen(fpath, "a"))
+ {
+ fprintf(fp, "ªO¥D¤½§Gµª®×¡G%s\n\n", buf);
+
+ *fname = 'G';
+ vlog_pay(fpath, choice, fp, vch);
+
+ fputs("\n", fp);
+ fclose(fp);
+ }
+
+ /* ¶}²¼µ²ªG */
+ *fname = 'Z';
+ }
+ }
+
+ /* ±N¶}²¼µ²ªG post ¨ì [BN_RECORD] »P ¥»¬ÝªO */
+
+ if (!(currbattr & BRD_NOVOTE))
+ {
+ sprintf(buf, "[°O¿ý] %s <<¬ÝªO¿ï±¡³ø¾É>>", currboard);
+ keeplog(fpath, BN_RECORD, buf);
+ }
+
+ keeplog(fpath, currboard, "[°O¿ý] ¿ï±¡³ø¾É");
+
+ /* §ë²¼µ²ªGªþ¥[¨ì @vote */
+
+ setdirpath(buf, dir, "@/@vote");
+ if (fp = fopen(fpath, "a"))
+ {
+ f_suck(fp, buf);
+ fclose(fp);
+ rename(fpath, buf);
+ }
+
+ /* ¶}§¹²¼´N§R°£ */
+ vch->vgamble = ' '; /* ¥O¬°«D½ä½L¡A¦p¦¹¦b delvch ¸Ì­±´N¤£·|°h½äª÷ */
+ delvch(xo, vch);
+
+ currchrono = vch->chrono;
+ rec_del(dir, sizeof(VCH), pos, cmpchrono);
+
+ vmsg("¶}²¼§¹²¦");
+ return vote_init(xo);
+}
+
+
+static int
+vote_tag(xo)
+ XO *xo;
+{
+ VCH *vch;
+ int tag, pos, cur;
+
+ pos = xo->pos;
+ cur = pos - xo->top;
+ vch = (VCH *) xo_pool + cur;
+
+ if (tag = Tagger(vch->chrono, pos, TAG_TOGGLE))
+ {
+ move(3 + cur, 6);
+ outc(tag > 0 ? '*' : ' ');
+ }
+
+ /* return XO_NONE; */
+ return xo->pos + 1 + XO_MOVE; /* lkchu.981201: ¸õ¦Ü¤U¤@¶µ */
+}
+
+
+static int
+vote_help(xo)
+ XO *xo;
+{
+ xo_help("vote");
+ return vote_head(xo);
+}
+
+
+static KeyFunc vote_cb[] =
+{
+ XO_INIT, vote_init,
+ XO_LOAD, vote_load,
+ XO_HEAD, vote_head,
+ XO_BODY, vote_body,
+
+ 'r', vote_join, /* itoc.010901: «ö¥kÁä¤ñ¸û¤è«K */
+ 'v', vote_join,
+ 'R', vote_result,
+
+ 'V', vote_view,
+ 'E', vote_edit,
+ 'o', vote_pal,
+ 'd', vote_delete,
+ 'D', vote_rangedel,
+ 't', vote_tag,
+ 'b', vote_open,
+
+ Ctrl('D'), vote_prune,
+ Ctrl('G'), vote_pal,
+ Ctrl('P'), vote_add,
+ Ctrl('Q'), vote_query,
+
+ 'h', vote_help
+};
+
+
+int
+XoVote(xo)
+ XO *xo;
+{
+ char fpath[64];
+
+ /* ¦³ post Åv§Qªº¤~¯à°Ñ¥[§ë²¼ */
+ /* ¦Ó¥B­nÁ×§K guest ¦b sysop ªO§ë²¼ */
+
+ if (!(bbstate & STAT_POST) || !cuser.userlevel)
+ return XO_NONE;
+
+ setdirpath(fpath, xo->dir, FN_VCH);
+ if (!(bbstate & STAT_BOARD) && !rec_num(fpath, sizeof(VCH)))
+ {
+ vmsg("¥Ø«e¨S¦³§ë²¼Á|¦æ");
+ return XO_FOOT;
+ }
+
+ xz[XZ_VOTE - XO_ZONE].xo = xo = xo_new(fpath);
+ xz[XZ_VOTE - XO_ZONE].cb = vote_cb;
+ xover(XZ_VOTE);
+ free(xo);
+
+ return XO_INIT;
+}
+
+
+int
+vote_all() /* itoc.010414: §ë²¼¤¤¤ß */
+{
+ typedef struct
+ {
+ char brdname[BNLEN + 1];
+ char class[BCLEN + 1];
+ char title[BTLEN + 1];
+ char BM[BMLEN + 1];
+ char bvote;
+ } vbrd_t;
+
+ extern char brd_bits[];
+ char *str;
+ char fpath[64];
+ int num, pageno, pagemax, redraw;
+ int ch, cur;
+ BRD *bhead, *btail;
+ XO *xo;
+ vbrd_t vbrd[MAXBOARD], *vb;
+
+ bhead = bshm->bcache;
+ btail = bhead + bshm->number;
+ cur = 0;
+ num = 0;
+
+ do
+ {
+ str = &brd_bits[cur];
+ ch = *str;
+ if (bhead->bvote && (ch & BRD_W_BIT))
+ {
+ vb = vbrd + num;
+ strcpy(vb->brdname, bhead->brdname);
+ strcpy(vb->class, bhead->class);
+ strcpy(vb->title, bhead->title);
+ strcpy(vb->BM, bhead->BM);
+ vb->bvote = bhead->bvote;
+ num++;
+ }
+ cur++;
+ } while (++bhead < btail);
+
+ if (!num)
+ {
+ vmsg("¥Ø«e¯¸¤º¨Ã¨S¦³¥ô¦ó§ë²¼");
+ return XEASY;
+ }
+
+ num--;
+ pagemax = num / XO_TALL;
+ pageno = 0;
+ cur = 0;
+ redraw = 1;
+
+ do
+ {
+ if (redraw)
+ {
+ /* itoc.µù¸Ñ: ºÉ¶q°µ±o¹³ xover ®æ¦¡ */
+ vs_head("§ë²¼¤¤¤ß", str_site);
+ prints(NECKER_VOTEALL, d_cols >> 1, "", d_cols - (d_cols >> 1), "");
+
+ redraw = pageno * XO_TALL; /* ­É¥Î redraw */
+ ch = BMIN(num, redraw + XO_TALL - 1);
+ move(3, 0);
+ do
+ {
+ vb = vbrd + redraw;
+ /* itoc.010909: ªO¦W¤Óªøªº§R±¼¡B¥[¤ÀÃþÃC¦â¡C°²³] BCLEN = 4 */
+ prints("%6d %-13s\033[1;3%dm%-5s\033[m%s %-*.*s %.*s\n",
+ redraw + 1, vb->brdname,
+ vb->class[3] & 7, vb->class,
+ vb->bvote > 0 ? ICON_VOTED_BRD : ICON_GAMBLED_BRD,
+ (d_cols >> 1) + 34, (d_cols >> 1) + 33, vb->title, d_cols - (d_cols >> 1) + 13, vb->BM);
+
+ redraw++;
+ } while (redraw <= ch);
+
+ outf(FEETER_VOTEALL);
+ move(3 + cur, 0);
+ outc('>');
+ redraw = 0;
+ }
+
+ switch (ch = vkey())
+ {
+ case KEY_RIGHT:
+ case '\n':
+ case ' ':
+ case 'r':
+ vb = vbrd + (cur + pageno * XO_TALL);
+
+ /* itoc.060324: µ¥¦P¶i¤J·sªº¬ÝªO¡AXoPost() ¦³°µªº¨Æ¡A³o¸Ì´X¥G³£­n°µ */
+ if (!vb->brdname[0]) /* ¤w§R°£ªº¬ÝªO */
+ break;
+
+ redraw = brd_bno(vb->brdname); /* ­É¥Î redraw */
+ ch = brd_bits[redraw];
+
+ /* ³B²zÅv­­ */
+ if (ch & BRD_X_BIT)
+ bbstate |= STAT_BOARD;
+ else
+ bbstate &= ~STAT_BOARD;
+
+ /* itoc.050613.µù¸Ñ: ¤H®ðªº´î¤Ö¤£¬O¦bÂ÷¶}¬ÝªO®É¡A¦Ó¬O¦b¶i¤J·sªº¬ÝªO©Î¬OÂ÷¯¸®É¡A
+ ³o¬O¬°¤FÁ×§K switch ¸õ¬ÝªO·|ºâ¿ù¤H®ð */
+ if (currbno >= 0)
+ bshm->mantime[currbno]--; /* °h¥X¤W¤@­ÓªO */
+ bshm->mantime[redraw]++; /* ¶i¤J·sªºªO */
+
+ currbno = redraw;
+ bhead = bshm->bcache + currbno;
+ currbattr = bhead->battr;
+ strcpy(currboard, bhead->brdname);
+ str = bhead->BM;
+ sprintf(currBM, "ªO¥D¡G%s", *str <= ' ' ? "¼x¨D¤¤" : str);
+#ifdef HAVE_BRDMATE
+ strcpy(cutmp->reading, currboard);
+#endif
+
+ sprintf(fpath, "brd/%s/%s", currboard, FN_VCH);
+ xz[XZ_VOTE - XO_ZONE].xo = xo = xo_new(fpath);
+ xz[XZ_VOTE - XO_ZONE].cb = vote_cb;
+ xover(XZ_VOTE);
+ free(xo);
+ redraw = 1;
+ break;
+
+ default:
+ ch = xo_cursor(ch, pagemax, num, &pageno, &cur, &redraw);
+ break;
+ }
+ } while (ch != 'q');
+
+ return 0;
+}
diff --git a/so/xyz.c b/so/xyz.c
new file mode 100644
index 0000000..23fdd29
--- /dev/null
+++ b/so/xyz.c
@@ -0,0 +1,213 @@
+/*-------------------------------------------------------*/
+/* xyz.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : Âø¤CÂø¤Kªº¥~±¾ */
+/* create : 01/03/01 */
+/* update : / / */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+
+#ifdef HAVE_TIP
+
+/* ----------------------------------------------------- */
+/* ¨C¤é¤p¯µ³Z */
+/* ----------------------------------------------------- */
+
+int
+x_tip()
+{
+ int i, j;
+ char msg[128];
+ FILE *fp;
+
+ if (!(fp = fopen(FN_ETC_TIP, "r")))
+ return XEASY;
+
+ fgets(msg, 128, fp);
+ j = atoi(msg); /* ²Ä¤@¦æ°O¿ýÁ`½g¼Æ */
+ i = time(0) % j + 1;
+ j = 0;
+
+ while (j < i) /* ¨ú²Ä i ­Ó tip */
+ {
+ fgets(msg, 128, fp);
+ if (msg[0] == '#')
+ j++;
+ }
+
+ move(12, 0);
+ clrtobot();
+ fgets(msg, 128, fp);
+ prints("\033[1;36m¨C¤é¤p¯¦³Z¡G\033[m\n");
+ prints(" %s", msg);
+ fgets(msg, 128, fp);
+ prints(" %s", msg);
+ vmsg(NULL);
+ fclose(fp);
+ return 0;
+}
+#endif /* HAVE_TIP */
+
+
+#ifdef HAVE_LOVELETTER
+
+/* ----------------------------------------------------- */
+/* ±¡®Ñ²£¥Í¾¹ */
+/* ----------------------------------------------------- */
+
+int
+x_loveletter()
+{
+ FILE *fp;
+ int start_show; /* 1:¶}©l¨q */
+ int style; /* 0:¶}ÀY 1:¥¿¤å 2:µ²§À */
+ int line;
+ char buf[128];
+ char header[3][5] = {"head", "body", "foot"}; /* ¶}ÀY¡B¥¿¤å¡Bµ²§À */
+ int num[3];
+
+ /* etc/loveletter «e¬q¬O#head ¤¤¬q¬O#body «á¬q¬O#foot */
+ /* ¦æ¼Æ¤W­­¡G#head¤­¦æ #body¤K¦æ #foot¤­¦æ */
+
+ if (!(fp = fopen(FN_ETC_LOVELETTER, "r")))
+ return XEASY;
+
+ /* «e¤T¦æ°O¿ý½g¼Æ */
+ fgets(buf, 128, fp);
+ num[0] = atoi(buf + 5);
+ num[1] = atoi(buf + 5);
+ num[2] = atoi(buf + 5);
+
+ /* ¨M©w­n¿ï²Ä´X½g */
+ line = time(0);
+ num[0] = line % num[0];
+ num[1] = (line >> 1) % num[1];
+ num[2] = (line >> 2) % num[2];
+
+ vs_bar("±¡®Ñ²£¥Í¾¹");
+
+ start_show = style = line = 0;
+
+ while (fgets(buf, 128, fp))
+ {
+ if (*buf == '#')
+ {
+ if (!strncmp(buf + 1, header[style], 4)) /* header[] ªø«×³£¬O 5 bytes */
+ num[style]--;
+
+ if (num[style] < 0) /* ¤w¸g fget ¨ì­n¿ïªº³o½g¤F */
+ {
+ outc('\n');
+ start_show = 1;
+ style++;
+ }
+ else
+ {
+ start_show = 0;
+ }
+ continue;
+ }
+
+ if (start_show)
+ {
+ if (line >= (b_lines - 5)) /* ¶W¹L¿Ã¹õ¤j¤p¤F */
+ break;
+
+ outs(buf);
+ line++;
+ }
+ }
+
+ fclose(fp);
+ vmsg(NULL);
+
+ return 0;
+}
+#endif /* HAVE_LOVELETTER */
+
+
+/* ----------------------------------------------------- */
+/* ±K½X§Ñ°O¡A­«³]±K½X */
+/* ----------------------------------------------------- */
+
+
+int
+x_password()
+{
+ int i;
+ ACCT acct;
+ FILE *fp;
+ char fpath[80], email[60], passwd[PSWDLEN + 1];
+ time_t now;
+
+ vmsg("·í¨ä¥L¨Ï¥ÎªÌ§Ñ°O±K½X®É¡A­«°e·s±K½X¦Ü¸Ó¨Ï¥ÎªÌªº«H½c");
+
+ if (acct_get(msg_uid, &acct) > 0)
+ {
+ time(&now);
+
+ if (acct.lastlogin > now - 86400 * 10)
+ {
+ vmsg("¸Ó¨Ï¥ÎªÌ¥²¶·¤Q¤Ñ¥H¤W¥¼¤W¯¸¤è¥i­«°e±K½X");
+ return 0;
+ }
+
+ vget(b_lines - 2, 0, "½Ð¿é¤J»{ÃҮɪº Email¡G", email, 40, DOECHO);
+
+ if (str_cmp(acct.email, email))
+ {
+ vmsg("³o¤£¬O¸Ó¨Ï¥ÎªÌ»{ÃҮɥΪº Email");
+ return 0;
+ }
+
+ if (not_addr(email) || !mail_external(email))
+ {
+ vmsg(err_email);
+ return 0;
+ }
+
+ vget(b_lines - 1, 0, "½Ð¿é¤J¯u¹ê©m¦W¡G", fpath, RNLEN + 1, DOECHO);
+ if (strcmp(acct.realname, fpath))
+ {
+ vmsg("³o¤£¬O¸Ó¨Ï¥ÎªÌªº¯u¹ê©m¦W");
+ return 0;
+ }
+
+ if (vans("¸ê®Æ¥¿½T¡A½Ð½T»{¬O§_²£¥Í·s±K½X(Y/N)¡H[N] ") != 'y')
+ return 0;
+
+ sprintf(fpath, "%s §ï¤F %s ªº±K½X", cuser.userid, acct.userid);
+ blog("PASSWD", fpath);
+
+ /* ¶Ã¼Æ²£¥Í A~Z ²Õ¦Xªº±K½X¤K½X */
+ for (i = 0; i < PSWDLEN; i++)
+ passwd[i] = rnd(26) + 'A';
+ passwd[PSWDLEN] = '\0';
+
+ /* ­«·s acct_load ¸ü¤J¤@¦¸¡AÁ×§K¹ï¤è¦b vans() ®Éµn¤J·|¦³¬~¿úªº®ÄªG */
+ if (acct_load(&acct, acct.userid) >= 0)
+ {
+ str_ncpy(acct.passwd, genpasswd(passwd), PASSLEN + 1);
+ acct_save(&acct);
+ }
+
+ sprintf(fpath, "tmp/sendpass.%s", cuser.userid);
+ if (fp = fopen(fpath, "w"))
+ {
+ fprintf(fp, "%s ¬°±z¥Ó½Ð¤F·s±K½X\n\n", cuser.userid);
+ fprintf(fp, BBSNAME "ID : %s\n\n", acct.userid);
+ fprintf(fp, BBSNAME "·s±K½X : %s\n", passwd);
+ fclose(fp);
+
+ bsmtp(fpath, BBSNAME "·s±K½X", email, 0);
+ unlink(fpath);
+
+ vmsg("·s±K½X¤w±H¨ì¸Ó»{ÃÒ«H½c");
+ }
+ }
+
+ return 0;
+}
diff --git a/util/Makefile b/util/Makefile
new file mode 100644
index 0000000..f191168
--- /dev/null
+++ b/util/Makefile
@@ -0,0 +1,59 @@
+# ------------------------------------------------------- #
+# util/Makefile ( NTHU CS MapleBBS Ver 3.10 ) #
+# ------------------------------------------------------- #
+# target : Makefile for ±H«H¡B²Î­p¡B³Æ¥÷¡B¨t²ÎºûÅ@¤u¨ã #
+# create : 95/03/29 #
+# update : 95/12/15 #
+# ------------------------------------------------------- #
+
+
+# ------------------------------------------------------ #
+# ¤U¦Cªº make rules ¤£»Ý­×§ï #
+# ------------------------------------------------------ #
+
+
+EXE = account acl-sort bbsmail bquota brdmail camera counter changeperm \
+ gem-index give_paycheck hdr-dump mailpost outgo poststat reaper redir setperm setusr \
+ showACCT showBRD showDIR showperm showUSR topgem topsong topusr
+
+
+all:
+ @echo "Please enter 'make sys-type', "
+ @echo " make sun : for Sun-OS 4.x and maybe some BSD systems, cc or gcc"
+ @echo " make linux : for Linux"
+ @echo " make solaris : for Sun-OS 5.x gcc"
+ @echo " make sol-x86 : for Solaris 7 x86"
+ @echo " make freebsd : for BSD 4.4 systems"
+ @echo " make bsd : for BSD systems, cc or gcc, if not in the above lists"
+ @echo " make cygwin : for Microsoft Windows and Cygwin gcc"
+
+sun:
+ @$(MAKE) CC=gcc CFLAGS="-O2 -pipe -fomit-frame-pointer -Wunused -I../include" LDFLAGS="-s -L../lib -ldao" $(EXE)
+
+linux:
+ @$(MAKE) CC=gcc CFLAGS="-O2 -pipe -I../include -fomit-frame-pointer -Wunused" LDFLAGS="-s -L../lib -ldao -lcrypt -lresolv" $(EXE)
+
+solaris:
+ @$(MAKE) CC=gcc CFLAGS="-O2 -pipe -I../include -fomit-frame-pointer -Wunused" LDFLAGS="-s -L../lib -ldao -lsocket -lresolv -lnsl -L/usr/ucblib -lucb -R/usr/ucblib" $(EXE)
+
+sol-x86:
+ @$(MAKE) CC=gcc CFLAGS="-O2 -I../include -fomit-frame-pointer -Wunused" LDFLAGS="-s -L../lib -ldao -lsocket -lresolv -lnsl" $(EXE)
+
+freebsd:
+ @$(MAKE) CC=gcc CFLAGS="-O2 -pipe -I../include -fomit-frame-pointer -Wunused" LDFLAGS="-s -L../lib -ldao -lcrypt" $(EXE)
+
+bsd:
+ @$(MAKE) CC=gcc CFLAGS="-O2 -pipe -I../include -fomit-frame-pointer -Wunused" LDFLAGS="-s -L../lib -ldao" $(EXE)
+
+cygwin:
+ @$(MAKE) CC=gcc CFLAGS="-O2 -pipe -I../include -fomit-frame-pointer -Wunused" LDFLAGS="-s -L../lib -ldao -lcrypt -lresolv -lcygipc" $(EXE)
+
+
+.c: ; $(CC) -o $@ $@.c $(CFLAGS) $(LDFLAGS)
+
+
+install: $(EXE)
+ install -m 0700 $? $(HOME)/bin
+
+clean:
+ rm -f $(EXE) *.exe *.o *~
diff --git a/util/account.c b/util/account.c
new file mode 100644
index 0000000..f68eff6
--- /dev/null
+++ b/util/account.c
@@ -0,0 +1,1181 @@
+/*-------------------------------------------------------*/
+/* util/account.c ( NTHU CS MapleBBS Ver 3.00 ) */
+/*-------------------------------------------------------*/
+/* target : ¤W¯¸¤H¦¸²Î­p¡B¨t²Î¸ê®Æ³Æ¥÷¡B¶}²¼ */
+/* create : 95/03/29 */
+/* update : 97/03/29 */
+/*-------------------------------------------------------*/
+/* syntax : ¥»µ{¦¡©y¥H crontab °õ¦æ¡A³]©w®É¶¡¬°¨C¤p®É */
+/* 1-5 ¤À ¤§¶¡ */
+/*-------------------------------------------------------*/
+/* notice : brdshm (board shared memory) synchronize */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+#include <sys/ipc.h>
+#include <sys/shm.h>
+
+
+#define MAX_LINE 16
+#define ADJUST_M 10 /* adjust back 10 minutes */
+
+
+/* itoc.011004.µù¸Ñ: ¥Î - ¥Nªí³o¦¸ªº¡A¥Î = ¥Nªí¤W¦¸ªº¡C¦b gem/@/ ¤U¦³«Ü¦h³o¼Ëªº½d¨Ò */
+
+static char fn_today[] = "gem/@/@-act"; /* ¤µ¤é¤W¯¸¤H¦¸²Î­p */
+static char fn_yesday[] = "gem/@/@=act"; /* ¬Q¤é¤W¯¸¤H¦¸²Î­p */
+static char log_file[] = FN_RUN_USIES "=";
+
+static time_t now; /* °õ¦æµ{¦¡ªº®É¶¡ */
+
+
+/* ----------------------------------------------------- */
+/* ¶}²¼¡Gshm ³¡¥÷¶·»P cache.c ¬Û®e */
+/* ----------------------------------------------------- */
+
+
+static BCACHE *bshm;
+
+
+static int
+boardname_cmp(a, b)
+ BRD *a, *b;
+{
+ return str_cmp(a->brdname, b->brdname);
+}
+
+
+static void
+fix_brd()
+{
+ BRD allbrd[MAXBOARD], brd;
+ FILE *fp;
+ int i, num;
+
+ if (!(fp = fopen(FN_BRD, "r")))
+ return;
+
+ num = 0;
+ for (i = 0; i < MAXBOARD; i++)
+ {
+ if (fread(&brd, sizeof(BRD), 1, fp) != 1)
+ break;
+
+ if (!*brd.brdname) /* ¦¹ªO¤w³Q§R°£ */
+ continue;
+
+ memcpy(&allbrd[num], &brd, sizeof(BRD));
+ num++;
+ }
+
+ fclose(fp);
+
+ /* itoc.041110: ¦b²Ä¤@¦¸¸ü¤J bshm ®É¡A±N bno ¨ÌªO¦W±Æ§Ç */
+ if (num > 1)
+ qsort(allbrd, num, sizeof(BRD), boardname_cmp);
+
+ unlink(FN_BRD);
+
+ if (num)
+ rec_add(FN_BRD, allbrd, sizeof(BRD) * num);
+}
+
+
+#ifdef HAVE_MODERATED_BOARD
+static int
+int_cmp(a, b)
+ int *a, *b;
+{
+ return *a - *b;
+}
+#endif
+
+
+static void
+init_allbrd()
+{
+ BRD *head, *tail;
+#ifdef HAVE_MODERATED_BOARD
+ int fd;
+ char fpath[64];
+ BPAL *bpal;
+#endif
+
+ head = bshm->bcache;
+ tail = head + bshm->number;
+#ifdef HAVE_MODERATED_BOARD
+ bpal = bshm->pcache;
+#endif
+
+ do
+ {
+ /* itoc.040314: ªO¥D§ó§ï¬ÝªO±Ô­z©Î¬O¯¸ªø§ó§ï¬ÝªO®É¤~·|§â bpost/blast ¼g¶i .BRD ¤¤
+ ©Ò¥H .BRD ¸Ìªº bpost/blast ¥¼¥²¬O¹ïªº¡A­n­«·s initial¡C
+ initial ªº¤èªk¬O±N btime ³]¦¨ -1¡AÅý class_item() ¥h§ó·s */
+ head->btime = -1;
+
+#ifdef HAVE_MODERATED_BOARD
+ brd_fpath(fpath, head->brdname, FN_PAL);
+ if ((fd = open(fpath, O_RDONLY)) >= 0)
+ {
+ struct stat st;
+ PAL *pal, *up;
+ int count;
+
+ fstat(fd, &st);
+ if (pal = (PAL *) malloc(count = st.st_size))
+ {
+ count = read(fd, pal, count) / sizeof(PAL);
+ if (count > 0 && count <= PAL_MAX)
+ {
+ int *userno;
+ int c = count;
+
+ userno = bpal->pal_spool;
+ up = pal;
+ do
+ {
+ *userno++ = (up->ftype & PAL_BAD) ? -(up->userno) : up->userno;
+ up++;
+ } while (--c);
+
+ if (count > 1)
+ qsort(bpal->pal_spool, count, sizeof(int), int_cmp);
+ bpal->pal_max = count;
+ }
+ free(pal);
+ }
+ close(fd);
+ }
+
+ bpal++;
+#endif
+
+ } while (++head < tail);
+}
+
+
+static void
+init_bshm()
+{
+ time_t *uptime;
+ int n, turn;
+
+ bshm = shm_new(BRDSHM_KEY, sizeof(BCACHE));
+ uptime = &(bshm->uptime);
+
+ turn = 0;
+ for (;;)
+ {
+ n = *uptime;
+ if (n > 0) /* bshm ¤w initial §¹¦¨ */
+ return;
+
+ if (n < 0)
+ {
+ if (++turn < 30)
+ {
+ sleep(2);
+ continue;
+ }
+ }
+
+ *uptime = -1;
+
+ fix_brd(); /* itoc.030725: ¦b²Ä¤@¦¸¸ü¤J bshm «e¡A¥ý¾ã²z .BRD */
+
+ if ((n = open(FN_BRD, O_RDONLY)) >= 0)
+ {
+ turn = read(n, bshm->bcache, MAXBOARD * sizeof(BRD)) / sizeof(BRD);
+ close(n);
+ bshm->number = bshm->numberOld = turn;
+
+ init_allbrd();
+ }
+
+ /* µ¥©Ò¦³ boards ¸ê®Æ§ó·s«á¦A³]©w uptime */
+
+ time(uptime);
+ fprintf(stderr, "[account]\tCACHE\treload bcache\n");
+ return;
+ }
+}
+
+
+/* ----------------------------------------------------- */
+/* keep log in board */
+/* ----------------------------------------------------- */
+
+
+static void
+update_btime(brdname)
+ char *brdname;
+{
+ BRD *brdp, *bend;
+
+ brdp = bshm->bcache;
+ bend = brdp + bshm->number;
+ do
+ {
+ if (!strcmp(brdname, brdp->brdname))
+ {
+ brdp->btime = -1;
+ break;
+ }
+ } while (++brdp < bend);
+}
+
+
+static void
+keeplog(fnlog, board, title, mode)
+ char *fnlog;
+ char *board;
+ char *title;
+ int mode; /* 0:load 1:rename 2:unlink */
+{
+ HDR hdr;
+ char folder[64], fpath[64];
+ int fd;
+ FILE *fp;
+
+ if (!dashf(fnlog)) /* Kudo.010804: Àɮ׬OªÅªº´N¤£ keeplog */
+ return;
+
+ if (!board)
+ board = BN_RECORD;
+
+ brd_fpath(folder, board, FN_DIR);
+ fd = hdr_stamp(folder, 'A', &hdr, fpath);
+ if (fd < 0)
+ return;
+
+ if (mode == 1)
+ {
+ close(fd);
+ /* rename(fnlog, fpath); */
+ f_mv(fnlog, fpath); /* Thor.990409: ¥i¸ópartition */
+ }
+ else
+ {
+ fp = fdopen(fd, "w");
+ fprintf(fp, "§@ªÌ: %s (%s)\n¼ÐÃD: %s\n®É¶¡: %s\n\n",
+ STR_SYSOP, SYSOPNICK, title, Btime(&hdr.chrono));
+ f_suck(fp, fnlog);
+ fclose(fp);
+ if (mode)
+ unlink(fnlog);
+ }
+
+ strcpy(hdr.title, title);
+ strcpy(hdr.owner, STR_SYSOP);
+ rec_bot(folder, &hdr, sizeof(HDR));
+
+ update_btime(board);
+}
+
+
+/* ----------------------------------------------------- */
+/* build vote result */
+/* ----------------------------------------------------- */
+
+
+struct Tchoice
+{
+ int count;
+ vitem_t vitem;
+};
+
+
+static int
+TchoiceCompare(i, j)
+ struct Tchoice *i, *j;
+{
+ return j->count - i->count;
+}
+
+
+static int
+draw_vote(brd, fpath, vch)
+ BRD *brd; /* Thor: ¶Ç¤J BRD, ¥i¬d battr */
+ char *fpath;
+ VCH *vch;
+{
+ struct Tchoice choice[MAX_CHOICES];
+ FILE *fp;
+ char *fname, *bid, buf[80];
+ int total, items, num, fd, ticket, bollt;
+ VLOG vlog;
+ char *list = "@IOLGZ"; /* itoc.µù¸Ñ: ²M vote file */
+
+ bid = brd->brdname;
+ fname = strrchr(fpath, '@');
+
+ /* vote item */
+
+ *fname = 'I';
+
+ items = 0;
+ if (fp = fopen(fpath, "r"))
+ {
+ while (fread(&choice[items].vitem, sizeof(vitem_t), 1, fp) == 1)
+ {
+ choice[items].count = 0;
+ items++;
+ }
+ fclose(fp);
+ }
+
+ if (items == 0)
+ return 0;
+
+ /* ²Ö­p§ë²¼µ²ªG */
+
+ *fname = 'G';
+ bollt = 0; /* Thor: Á`²¼¼ÆÂk¹s */
+ total = 0;
+
+ if ((fd = open(fpath, O_RDONLY)) >= 0)
+ {
+ while (read(fd, &vlog, sizeof(VLOG)) == sizeof(VLOG))
+ {
+ for (ticket = vlog.choice, num = 0; ticket && num < items; ticket >>= 1, num++)
+ {
+ if (ticket & 1)
+ {
+ choice[num].count += vlog.numvotes;
+ bollt += vlog.numvotes;
+ }
+ }
+ total++;
+ }
+ close(fd);
+ }
+
+ /* ²£¥Í¶}²¼µ²ªG */
+
+ *fname = 'Z';
+ if (!(fp = fopen(fpath, "w")))
+ return 0;
+
+ fprintf(fp, "\n\033[1;34m" MSG_SEPERATOR "\033[m\n\n"
+ "\033[1;32m¡» [%s] ¬ÝªO§ë²¼¡G%s\033[m\n\nÁ|¿ìªO¥D¡G%s\n\nÁ|¿ì¤é´Á¡G%s\n\n",
+ bid, vch->title, vch->owner, Btime(&vch->chrono));
+ fprintf(fp, "¶}²¼¤é´Á¡G%s\n\n\033[1;32m¡» §ë²¼¥DÃD¡G\033[m\n\n", Btime(&vch->vclose));
+
+ *fname = '@';
+ f_suck(fp, fpath);
+
+ fprintf(fp, "\n\033[1;32m¡» §ë²¼µ²ªG¡G¨C¤H¥i§ë %d ²¼¡A¦@ %d ¤H°Ñ¥[¡A§ë¥X %d ²¼\033[m\n\n",
+ vch->maxblt, total, bollt);
+
+ if (vch->vsort == 's')
+ qsort(choice, items, sizeof(struct Tchoice), TchoiceCompare);
+
+ if (vch->vpercent == '%')
+ fd = BMAX(1, bollt);
+ else
+ fd = 0;
+
+ for (num = 0; num < items; num++)
+ {
+ ticket = choice[num].count;
+ if (fd)
+ fprintf(fp, " %-36s%5d ²¼ (%4.1f%%)\n", &choice[num].vitem, ticket, 100.0 * ticket / fd);
+ else
+ fprintf(fp, " %-36s%5d ²¼\n", &choice[num].vitem, ticket);
+ }
+
+ /* other opinions */
+
+ *fname = 'O';
+ fputs("\n\033[1;32m¡» §Ú¦³¸Ü­n»¡¡G\033[m\n\n", fp);
+ f_suck(fp, fpath);
+ fputs("\n", fp);
+ fclose(fp);
+
+ fp = fopen(fpath, "w"); /* Thor: ¥Î O_ ¼È¦s¤@¤U¤U... */
+ *fname = 'Z';
+ f_suck(fp, fpath);
+ sprintf(buf, "brd/%s/@/@vote", bid);
+ f_suck(fp, buf);
+ fclose(fp);
+ *fname = 'O';
+ rename(fpath, buf);
+
+ /* ±N¶}²¼µ²ªG post ¨ì [BN_RECORD] »P ¥»¬ÝªO */
+
+ *fname = 'Z';
+
+ /* Thor: ­Y¬ÝªOÄݩʬ° BRD_NOVOTE¡A«h¤£ post ¨ì [BN_RECORD] */
+
+ if (!(brd->battr & BRD_NOVOTE))
+ {
+ sprintf(buf, "[°O¿ý] %s <<¬ÝªO¿ï±¡³ø¾É>>", bid);
+ keeplog(fpath, NULL, buf, 0);
+ }
+
+ keeplog(fpath, bid, "[°O¿ý] ¿ï±¡³ø¾É", 2);
+
+ while (*fname = *list++)
+ unlink(fpath); /* Thor: ½T©w¦W¦r´N¬å */
+
+ return 1;
+}
+
+
+static int /* 0:¤£»Ý¼g¦^.BRD !=0:»Ý¼g¦^.BRD */
+draw_board(brd)
+ BRD *brd;
+{
+ int fd, fsize, alive;
+ int oldbvote, newbvote;
+ VCH *vch, *head, *tail;
+ char *fname, fpath[64], buf[64];
+ struct stat st;
+
+ /* ¥Ñ account ¨Ó maintain brd->bvote */
+
+ oldbvote = brd->bvote;
+
+ brd_fpath(fpath, brd->brdname, FN_VCH);
+
+ if ((fd = open(fpath, O_RDWR | O_APPEND, 0600)) < 0 || fstat(fd, &st) || (fsize = st.st_size) <= 0)
+ {
+ if (fd >= 0)
+ {
+ close(fd);
+ unlink(fpath);
+ }
+ brd->bvote = 0;
+ return oldbvote;
+ }
+
+ vch = (VCH *) malloc(fsize);
+
+ /* flock(fd, LOCK_EX); */
+ /* Thor.981205: ¥Î fcntl ¨ú¥Nflock, POSIX¼Ð·Ç¥Îªk */
+ f_exlock(fd);
+
+ read(fd, vch, fsize);
+
+ strcpy(buf, fpath);
+ fname = strrchr(buf, '.');
+ *fname++ = '@';
+ *fname++ = '/';
+
+ head = vch;
+ tail = (VCH *) ((char *)vch + fsize);
+
+ alive = 0;
+ newbvote = 0;
+
+ do
+ {
+ if (head->vclose < now && head->vgamble == ' ') /* ½ä½L¤£Åý account ¶} */
+ {
+ strcpy(fname, head->xname);
+ draw_vote(brd, buf, head);/* Thor: ¶Ç¤J BRD, ¥i¬d battr */
+ head->chrono = 0;
+ }
+ else
+ {
+ alive++;
+
+ if (head->vgamble == '$')
+ newbvote = -1;
+ }
+ } while (++head < tail);
+
+
+ if (alive && alive != fsize / sizeof(VCH))
+ {
+ ftruncate(fd, 0);
+ head = vch;
+ do
+ {
+ if (head->chrono)
+ {
+ write(fd, head, sizeof(VCH));
+ }
+ } while (++head < tail);
+ }
+
+ /* flock(fd, LOCK_UN); */
+ /* Thor.981205: ¥Î fcntl ¨ú¥Nflock, POSIX¼Ð·Ç¥Îªk */
+ f_unlock(fd);
+
+ close(fd);
+
+ free(vch);
+
+ if (!alive)
+ unlink(fpath);
+ else if (!newbvote)
+ newbvote = 1; /* ¥u¦³§ë²¼¡A¨S¦³½ä½L */
+
+ if (oldbvote != newbvote)
+ {
+ brd->bvote = newbvote;
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static void
+closepolls()
+{
+ BRD *bcache, *head, *tail;
+ int dirty;
+
+ dirty = 0;
+
+ head = bcache = bshm->bcache;
+ tail = head + bshm->number;
+ do
+ {
+ dirty |= draw_board(head);
+ } while (++head < tail);
+
+ if (!dirty)
+ return;
+
+ /* write back the shm cache data */
+
+ if ((dirty = open(FN_BRD, O_WRONLY | O_CREAT, 0600)) < 0)
+ return;
+
+ /* flock(dirty, LOCK_EX); */
+ /* Thor.981205: ¥Î fcntl ¨ú¥Nflock, POSIX¼Ð·Ç¥Îªk */
+ f_exlock(dirty);
+
+ write(dirty, bcache, (char *)tail - (char *)bcache);
+
+ /* flock(dirty, LOCK_UN); */
+ /* Thor.981205: ¥Î fcntl ¨ú¥Nflock, POSIX¼Ð·Ç¥Îªk */
+ f_unlock(dirty);
+
+ close(dirty);
+ time(&bshm->uptime);
+}
+
+
+/* ----------------------------------------------------- */
+/* build Class image */
+/* ----------------------------------------------------- */
+
+
+#define CLASS_RUNFILE "run/class.run"
+
+
+static ClassHeader *chx[CH_MAX];
+static int chn;
+static BRD *bhead, *btail;
+
+
+static int
+class_parse(key)
+ char *key;
+{
+ char *str, *ptr, fpath[64];
+ ClassHeader *chp;
+ HDR hdr;
+ int i, len, count;
+ FILE *fp;
+
+ strcpy(fpath, "gem/@/@");
+ str = fpath + sizeof("gem/@/@") - 1;
+ for (ptr = key;; ptr++)
+ {
+ i = *ptr;
+ if (i == '/')
+ i = 0;
+ *str = i;
+ if (!i)
+ break;
+ str++;
+ }
+
+ len = ptr - key;
+
+ /* search classes */
+
+ for (i = 1; i < chn; i++)
+ {
+ str = chx[i]->title;
+ if (str[len] == '/' && !memcmp(key, str, len))
+ return CH_END - i;
+ }
+
+ /* build classes */
+
+ if (fp = fopen(fpath, "r"))
+ {
+ int ans;
+ struct stat st;
+
+ if (fstat(fileno(fp), &st) || (count = st.st_size / sizeof(HDR)) <= 0)
+ {
+ fclose(fp);
+ return CH_END;
+ }
+
+ /* itoc.030723: Àˬd Class ¼Æ¶q¬O§_¶W¹L CH_MAX */
+ if (chn >= CH_MAX - 1)
+ {
+ static int show_error = 0;
+ if (!show_error) /* ¿ù»~°T®§¥u¦L¤@¦¸ */
+ {
+ fprintf(stderr, "[account]\t½Ð¥[¤j±zªº CH_MAX ©w¸q¡ACH_MAX ¥²¶·¶W¹L Class ªº¼Æ¶q\n");
+ show_error = 1;
+ }
+ fclose(fp);
+ return CH_END;
+ }
+
+ chx[chn++] = chp = (ClassHeader *) malloc(sizeof(ClassHeader) + count * sizeof(short));
+ memset(chp->title, 0, CH_TTLEN);
+ strcpy(chp->title, key);
+
+ ans = chn;
+ count = 0;
+
+ while (fread(&hdr, sizeof(hdr), 1, fp) == 1)
+ {
+ if (hdr.xmode & GEM_BOARD)
+ {
+ BRD *bp;
+
+ i = -1;
+ str = hdr.xname;
+ bp = bhead;
+
+ for (;;)
+ {
+ i++;
+ if (!str_cmp(str, bp->brdname))
+ break;
+
+ if (++bp >= btail)
+ {
+ i = -1;
+ break;
+ }
+ }
+
+ if (i < 0)
+ continue;
+ }
+ else
+ {
+ /* recursive ¦a¤@¼h¤@¼h¶i¥h«Ø Class */
+ i = class_parse(hdr.title);
+
+ if (i == CH_END)
+ continue;
+ }
+
+ chp->chno[count++] = i;
+ }
+
+ fclose(fp);
+
+ chp->count = count;
+ return -ans;
+ }
+
+ return CH_END;
+}
+
+
+static int
+brdname_cmp(i, j)
+ short *i, *j;
+{
+ return str_cmp(bhead[*i].brdname, bhead[*j].brdname);
+}
+
+
+static int
+brdtitle_cmp(i, j) /* itoc.010413: ¨Ì¬ÝªO¤¤¤å±Ô­z±Æ§Ç */
+ short *i, *j;
+{
+ /* return strcmp(bhead[*i].title, bhead[*j].title); */
+
+ /* itoc.010413: ¤ÀÃþ/ªO¦W¥æ¤e¤ñ¹ï */
+ int k = strcmp(bhead[*i].class, bhead[*j].class);
+ return k ? k : str_cmp(bhead[*i].brdname, bhead[*j].brdname);
+}
+
+
+static void
+class_sort(cmp)
+ int (*cmp) ();
+{
+ ClassHeader *chp;
+ int i, j, max;
+ BRD *bp;
+
+ max = bshm->number;
+ bhead = bp = bshm->bcache;
+ btail = bp + max;
+
+ chp = (ClassHeader *) malloc(sizeof(ClassHeader) + max * sizeof(short));
+
+ for (i = j = 0; i < max; i++, bp++)
+ {
+ if (bp->brdname)
+ chp->chno[j++] = i;
+ }
+
+ chp->count = j;
+
+ qsort(chp->chno, j, sizeof(short), cmp);
+
+ memset(chp->title, 0, CH_TTLEN);
+ strcpy(chp->title, "Boards");
+ chx[chn++] = chp;
+}
+
+
+static void
+class_image()
+{
+ int i, times;
+ FILE *fp;
+ short len, pos[CH_MAX];
+ ClassHeader *chp;
+
+ for (times = 2; times > 0; times--) /* itoc.010413: ²£¥Í¤G¥÷ class image */
+ {
+ chn = 0;
+ class_sort(times == 1 ? brdname_cmp : brdtitle_cmp);
+ class_parse(CLASS_INIFILE);
+
+ if (chn < 2) /* lkchu.990106: ©|¨S¦³¤ÀÃþ */
+ return;
+
+ len = sizeof(short) * (chn + 1);
+
+ for (i = 0; i < chn; i++)
+ {
+ pos[i] = len;
+ len += CH_TTLEN + chx[i]->count * sizeof(short);
+ }
+
+ pos[i++] = len;
+
+ if (fp = fopen(CLASS_RUNFILE, "w"))
+ {
+ fwrite(pos, sizeof(short), i, fp);
+ for (i = 0; i < chn; i++)
+ {
+ chp = chx[i];
+ fwrite(chp->title, 1, CH_TTLEN + chp->count * sizeof(short), fp);
+ free(chp);
+ }
+ fclose(fp);
+
+ rename(CLASS_RUNFILE, times == 1 ? CLASS_IMGFILE_NAME : CLASS_IMGFILE_TITLE);
+ }
+ }
+
+ bshm->min_chn = -chn;
+}
+
+
+/* ----------------------------------------------------- */
+/* ¤W¯¸¤H¼Æ²Î­p */
+/* ----------------------------------------------------- */
+
+
+static void
+error(fpath)
+ char *fpath;
+{
+ printf("can not open [%s]\n", fpath);
+ /* exit(1); */ /* itoc.011004: ¤W¯¸¤H¦¸²Î­p¥¢±Ñ¡AµL»Ý¤¤Â_ account °õ¦æ */
+}
+
+
+static void
+ansi_puts(fp, buf, mode)
+ FILE *fp;
+ char *buf, mode;
+{
+ static char state = '0';
+
+ if (state != mode)
+ fprintf(fp, "\033[3%cm", state = mode);
+ if (*buf)
+ {
+ fprintf(fp, buf);
+ *buf = '\0';
+ }
+}
+
+
+static void
+draw_usies(ptime)
+ struct tm *ptime;
+{
+ int fact, hour, max, item, total, i, j, over;
+ char buf[256];
+ FILE *fp, *fpw;
+ int act[26]; /* act[0~23]:0~23®É¦U¤p®Éªº¤W¯¸¤H¦¸ act[24]:°±¯d²Ö­p®É¶¡ act[25]:²Ö¿n¤H¦¸ */
+
+ static char act_file[] = "run/var/act";
+ static char run_file[] = FN_RUN_USIES;
+ static char tmp_file[] = FN_RUN_USIES ".tmp";
+
+ rename(run_file, tmp_file);
+ if (!(fp = fopen(tmp_file, "r")))
+ {
+ /* error(tmp_file); */ /* itoc.011004.µù¸Ñ: ¨S¦³ tmp_file ªí¥Ü¨S¦³ run_file¡Aªí¥Ü±q¤W¦¸¶] account ¨ì²{¦b¡A */
+ return; /* ¨S¦³¤H login ¹L bbs¡C³q±`µo¥Í¦b¤â°Ê¶] account ÀWÁc®É¡C */
+ }
+
+ if (!(fpw = fopen(log_file, "a")))
+ {
+ fclose(fp);
+ error(log_file); /* itoc.011004.µù¸Ñ: log_file ¬O¬Q¤Ñ run_file¡C¦pªG¬Q¤Ñ¾ã¤Ñ³£¨S¦³¤H login ¹L bbs¡A */
+ return; /* ´N·|µo¥Í¨S¦³ log_file ªºª¬ªp */
+ }
+
+ if ((fact = open(act_file, O_RDWR | O_CREAT, 0600)) < 0)
+ {
+ fclose(fp);
+ fclose(fpw);
+ error(act_file); /* itoc.011004.µù¸Ñ: ³£¤w¸g¦³ O_CREAT ¦pªGÁÙ¨S¦³ act_file ªº¸Ü..¦n¦Û¬°¤§§a :P */
+ return;
+ }
+
+ memset(act, 0, sizeof(act));
+
+ if (ptime->tm_hour != 0)
+ {
+ read(fact, act, sizeof(act));
+ lseek(fact, 0, SEEK_SET);
+ }
+
+ while (fgets(buf, sizeof(buf), fp))
+ {
+ fputs(buf, fpw);
+
+ if (!memcmp(buf + 24, "ENTER", 5))
+ {
+ hour = atoi(buf + 15);
+ if (hour >= 0 && hour <= 23)
+ act[hour]++;
+ continue;
+ }
+
+ if (!memcmp(buf + 43, "Stay:", 5))
+ {
+ if (hour = atoi(buf + 49))
+ {
+ act[24] += hour;
+ act[25]++;
+ }
+ continue;
+ }
+ }
+ fclose(fp);
+ fclose(fpw);
+ unlink(tmp_file);
+
+ write(fact, act, sizeof(act));
+ close(fact);
+
+ for (i = max = total = 0; i < 24; i++)
+ {
+ total += act[i]; /* itoc.030415.µù¸Ñ: act[25] ¥¼¥²µ¥©ó total¡A¦³¤H¤]³\¤£¥¿±`Â÷¯¸ */
+ if (act[i] > max)
+ max = act[i];
+ }
+
+ item = max / MAX_LINE + 1;
+ over = max > 1000;
+
+ if (!(fp = fopen(fn_today, "w")))
+ {
+ error(fn_today);
+ return;
+ }
+
+ /* Thor.990329: y2k */
+ fprintf(fp, "\t\t\t \033[1;33;46m [%02d/%02d/%02d] ¤W¯¸¤H¦¸²Î­p \033[40m\n",
+ ptime->tm_year % 100, ptime->tm_mon + 1, ptime->tm_mday);
+
+ for (i = MAX_LINE + 1; i > 0; i--)
+ {
+ strcpy(buf, " ");
+ for (j = 0; j < 24; j++)
+ {
+ max = item * i;
+ hour = act[j];
+ if (hour && (max > hour) && (max - item <= hour))
+ {
+ ansi_puts(fp, buf, '3');
+ if (over)
+ hour = (hour + 5) / 10;
+ fprintf(fp, "%-3d", hour);
+ }
+ else if (max <= hour)
+ {
+ ansi_puts(fp, buf, '1');
+ fprintf(fp, "¢i ");
+ }
+ else
+ strcat(buf, " ");
+ }
+ fprintf(fp, "\n");
+ }
+
+ if (act[25] == 0)
+ act[25] = 1; /* Thor.980928: lkchu patch: ¨¾¤î°£¼Æ¬°0 */
+
+ fprintf(fp, "\033[34m"
+ " ùæùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùè\n \033[32m"
+ "0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23\n\n"
+ "\t%s\t\033[35mÁ`¦@¤W¯¸¤H¦¸¡G\033[37m%-9d\033[35m¥­§¡¨Ï¥Î®É¶¡¡G\033[37m%d\033[m\n",
+ over ? "\033[35m³æ¦ì¡G\033[37m10 ¤H" : "", total, act[24] / act[25] + 1);
+ fclose(fp);
+}
+
+
+/* ----------------------------------------------------- */
+/* À£ÁYµ{¦¡ */
+/* ----------------------------------------------------- */
+
+
+static void
+gzip(source, target, stamp)
+ char *source, *target, *stamp;
+{
+ char buf[128];
+
+ if (dashf(source))
+ {
+ sprintf(buf, "/usr/bin/gzip -n log/%s%s", target, stamp);
+ /* rename(source, buf + 17); */
+ f_mv(source, buf + 17); /* Thor.990409: ¥i¸ó partition */
+ system(buf);
+ }
+}
+
+
+/* ----------------------------------------------------- */
+/* ²£¥ÍÅçÃÒ«Hªº private key */
+/* ----------------------------------------------------- */
+
+
+#ifdef HAVE_SIGNED_MAIL
+static void
+private_key(ymd)
+ char *ymd;
+{
+ srandom(time(NULL));
+
+#if (PRIVATE_KEY_PERIOD == 0)
+ if (!dashf(FN_RUN_PRIVATE))
+#else
+ if (!dashf(FN_RUN_PRIVATE) || (random() % PRIVATE_KEY_PERIOD) == 0)
+#endif
+ {
+ int i, j;
+ char buf[80];
+
+ sprintf(buf, "log/prikey%s", ymd);
+ f_mv(FN_RUN_PRIVATE, buf);
+ i = 8;
+ for (;;)
+ {
+ j = random() & 0xff;
+ if (!j)
+ continue;
+ buf[--i] = j;
+ if (i == 0)
+ break;
+ }
+ rec_add(FN_RUN_PRIVATE, buf, 8);
+ }
+}
+#endif
+
+
+/* ----------------------------------------------------- */
+/* ¥Dµ{¦¡ */
+/* ----------------------------------------------------- */
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct tm ntime, *ptime;
+ FILE *fp;
+
+ now = time(NULL); /* ¤@¶}©l´N­n°¨¤W°O¿ý®É¶¡ */
+
+ setgid(BBSGID);
+ setuid(BBSUID);
+ chdir(BBSHOME);
+ umask(077);
+
+ /* --------------------------------------------------- */
+ /* ªì©l¤Æ board shm ¥Î¨Ó«Ø Class ¤Î¶}²¼ */
+ /* --------------------------------------------------- */
+
+ init_bshm();
+
+ /* --------------------------------------------------- */
+ /* build Class image */
+ /* --------------------------------------------------- */
+
+ class_image();
+
+ /* --------------------------------------------------- */
+ /* ¨t²Î¶}²¼ */
+ /* --------------------------------------------------- */
+
+ closepolls();
+
+ /* --------------------------------------------------- */
+ /* ¸ê®Æ²Î­p®É¶¡ */
+ /* --------------------------------------------------- */
+
+ /* itoc.030911: ­Y¥[¤F°Ñ¼Æ¡Aªí¥Ü¤£¬O¦b crontab ¸Ì¶]ªº¡A¨º»ò¤£°µ¸ê®Æ²Î­p */
+ if (argc != 1)
+ exit(0);
+
+ /* ntime ¬O¤µ¤Ñ */
+ ptime = localtime(&now);
+ memcpy(&ntime, ptime, sizeof(ntime)); /* ¥ý¦s°_¨Ó¡A¦]¬°ÁÙ­n°µ¤@¦¸ localtime() */
+
+ /* ptime ¬O¬Q¤Ñ */
+ /* itoc.011004.µù¸Ñ: ¥Ñ©ó account ¬Oºâ«e¤@¤p®É²Î­p¡A©Ò¥H¦b¹s®É®É­n§â®ÉÄÁÂà¦^ 10 ¤ÀÄÁ¡A¨ì¬Q¤Ñ¥h */
+ /* itoc.030911.µù¸Ñ: ©Ò¥H account ¥²¶·¦b¨C¤p®Éªº 1-10 ¤À¤º°õ¦æ */
+ now -= ADJUST_M * 60; /* back to ancent */
+ ptime = localtime(&now);
+
+ /* --------------------------------------------------- */
+ /* ¤W¯¸¤H¦¸²Î­p */
+ /* --------------------------------------------------- */
+
+ draw_usies(ptime);
+
+ /* --------------------------------------------------- */
+ /* ¸ê®ÆÀ£ÁY³Æ¥÷¡B¼öªù¸ÜÃD²Î­p */
+ /* --------------------------------------------------- */
+
+ if (ntime.tm_hour == 0)
+ {
+ char date[16], ymd[16];
+ char title[80];
+
+ sprintf(ymd, "-%02d%02d%02d",
+ ptime->tm_year % 100, ptime->tm_mon + 1, ptime->tm_mday); /* Thor.990329: y2k */
+
+ sprintf(date, "[%d ¤ë %d ¤é] ", ptime->tm_mon + 1, ptime->tm_mday);
+
+
+ /* ------------------------------------------------- */
+ /* ¨C¤é­â±á¹s®É°µªº¨Æ */
+ /* ------------------------------------------------- */
+
+ gzip(log_file, "usies", ymd); /* ³Æ¥÷©Ò¦³ [¤W¯¸] °O¿ý */
+
+#ifdef HAVE_SIGNED_MAIL
+ private_key(ymd);
+#endif
+
+ sprintf(title, "%s¤å³¹½g¼Æ²Î­p", date);
+ keeplog(FN_RUN_POST_LOG, BN_SECURITY, title, 2);
+
+ sprintf(title, "%s¯¸°È¦æ¬°°O¿ý", date);
+ keeplog(FN_RUN_ADMIN, BN_SECURITY, title, 2);
+
+#if 0
+ sprintf(title, "%s±H«H°O¿ý", date);
+ keeplog(FN_RUN_MAIL_LOG, BN_SECURITY, title, 2);
+#endif
+#if 0
+#ifdef HAVE_ANONYMOUS
+ sprintf(title, "%s°Î¦W¤å³¹µoªí", date);
+ keeplog(FN_RUN_ANONYMOUS, BN_SECURITY, title, 2);
+#endif
+#endif
+
+#ifdef HAVE_BUY
+ sprintf(title, "%s¶×¿ú°O¿ý", date);
+ keeplog(FN_RUN_BANK_LOG, BN_SECURITY, title, 2);
+#endif
+
+ system("grep OVER " BMTA_LOGFILE " | cut -f2 | cut -d' ' -f2- | sort | uniq -c > run/over.log");
+ sprintf(title, "%sE-Mail over max connection ²Î­p", date);
+ keeplog("run/over.log", BN_SECURITY, title, 2);
+
+ sprintf(title, "%s»Ä²¢­W»¶¯d¨¥ªO", date);
+ keeplog(FN_RUN_NOTE_ALL, NULL, title, 2);
+
+ if (fp = fopen(fn_yesday, "w"))
+ {
+ f_suck(fp, fn_today);
+ fclose(fp);
+ }
+ sprintf(title, "%s¤W¯¸¤H¦¸²Î­p", date);
+ keeplog(fn_today, NULL, title, 1);
+
+
+ /* ------------------------------------------------- */
+ /* ¨C¶g¤@­â±á¹s®É°µªº¨Æ */
+ /* ------------------------------------------------- */
+
+ if (ntime.tm_wday == 0)
+ {
+ sprintf(title, "%s¥»¶g¼öªù¸ÜÃD", date);
+ keeplog("gem/@/@-week", NULL, title, 0);
+
+ sprintf(title, "%s°½ÃiªO¥D²Î­p", date);
+ keeplog(FN_RUN_LAZYBM, BN_SECURITY, title, 2);
+
+ sprintf(title, "%s¯S®íÅv­­¨Ï¥ÎªÌ¦Cªí", date);
+ keeplog(FN_RUN_MANAGER, BN_SECURITY, title, 2);
+
+ sprintf(title, "%sªø´Á¥¼¤W¯¸³Q²M°£ªº¨Ï¥ÎªÌ¦Cªí", date);
+ keeplog(FN_RUN_REAPER, BN_SECURITY, title, 2);
+
+ sprintf(title, "%s¦P¤@ email »{ÃÒ¦h¦¸", date);
+ keeplog(FN_RUN_EMAILADDR, BN_SECURITY, title, 2);
+ }
+
+
+ /* ------------------------------------------------- */
+ /* ¨C¤ë¤@¤é­â±á¹s®É°µªº¨Æ */
+ /* ------------------------------------------------- */
+
+ if (ntime.tm_mday == 1)
+ {
+ sprintf(title, "%s¥»¤ë¼öªù¸ÜÃD", date);
+ keeplog("gem/@/@-month", NULL, title, 0);
+ }
+
+
+ /* ------------------------------------------------- */
+ /* ¨C¦~¤@¤ë¤@¤é­â±á¹s®É°µªº¨Æ */
+ /* ------------------------------------------------- */
+
+ if (ntime.tm_yday == 1)
+ {
+ sprintf(title, "%s¦~«×¼öªù¸ÜÃD", date);
+ keeplog("gem/@/@-year", NULL, title, 0);
+ }
+ }
+
+ exit(0);
+}
diff --git a/util/acl-sort.c b/util/acl-sort.c
new file mode 100644
index 0000000..f230a96
--- /dev/null
+++ b/util/acl-sort.c
@@ -0,0 +1,130 @@
+/*-------------------------------------------------------*/
+/* util/acl-sort.c ( NTHU CS MapleBBS Ver 3.00 ) */
+/*-------------------------------------------------------*/
+/* target : sort [Access Control List] */
+/* create : 98/03/29 */
+/* update : 98/03/29 */
+/*-------------------------------------------------------*/
+/* syntax : acl-sort <file> */
+/*-------------------------------------------------------*/
+
+
+#include <stdio.h>
+#include "splay.h"
+
+
+typedef struct
+{
+ int domain;
+ char text[0];
+} AclText;
+
+
+static int
+at_cmp(x, y)
+ AclText *x;
+ AclText *y;
+{
+ char *tail1, *tail2;
+ int c1, c2, diff;
+
+ tail1 = x->text + x->domain;
+ tail2 = y->text + y->domain;
+
+ for (;;)
+ {
+ c1 = *tail1--;
+ if (c1 == '@')
+ c1 = 0;
+ else if (c1 >= 'A' && c1 <= 'Z')
+ c1 |= 32;
+
+ c2 = *tail2--;
+ if (c2 == '@')
+ c2 = 0;
+ else if (c2 >= 'A' && c2 <= 'Z')
+ c2 |= 32;
+
+ if (diff = c1 - c2)
+ return (diff);
+ }
+}
+
+
+static void
+at_out(top)
+ SplayNode *top;
+{
+ AclText *at;
+
+ if (top == NULL)
+ return;
+
+ at_out(top->left);
+
+ at = (AclText *) top->data;
+ fputs(at->text + 1, stdout);
+
+ at_out(top->right);
+}
+
+
+static void
+acl_sort(fpath)
+ char *fpath;
+{
+ FILE *fp;
+ int len, domain;
+ AclText *at;
+ SplayNode *top;
+ char *str, buf[256];
+
+ if (!(fp = fopen(fpath, "r")))
+ return;
+
+ top = NULL;
+
+ while (fgets(buf, sizeof(buf) - 2, fp))
+ {
+ str = buf;
+ if (*str <= '#')
+ continue;
+
+ while (*++str > ' ')
+ ;
+
+ domain = str - buf;
+
+
+ while (*str)
+ str++;
+
+ len = str - buf;
+
+ at = (AclText *) malloc(sizeof(AclText) + len + 2);
+ at->domain = domain;
+ at->text[0] = '\0';
+ strcpy(at->text + 1, buf);
+
+ top = splay_in(top, at, at_cmp);
+ }
+
+ at_out(top);
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ if (argc != 2)
+ {
+ printf("Usage:\t%s file\n", argv[0]);
+ }
+ else
+ {
+ acl_sort(argv[1]);
+ }
+ exit(0);
+}
diff --git a/util/backup/Makefile b/util/backup/Makefile
new file mode 100644
index 0000000..5bc70a4
--- /dev/null
+++ b/util/backup/Makefile
@@ -0,0 +1,57 @@
+# ------------------------------------------------------- #
+# util/Makefile ( NTHU CS MapleBBS Ver 3.10 ) #
+# ------------------------------------------------------- #
+# target : Makefile for ±H«H¡B²Î­p¡B³Æ¥÷¡B¨t²ÎºûÅ@¤u¨ã #
+# create : 95/03/29 #
+# update : 95/12/15 #
+# ------------------------------------------------------- #
+
+
+# ------------------------------------------------------ #
+# ¤U¦Cªº make rules ¤£»Ý­×§ï #
+# ------------------------------------------------------ #
+
+
+EXE = backupacct backupbrd backupgem backupoth backupusr restoreacct restorebrd restoregem restoreusr
+
+
+all:
+ @echo "Please enter 'make sys-type', "
+ @echo " make sun : for Sun-OS 4.x and maybe some BSD systems, cc or gcc"
+ @echo " make linux : for Linux"
+ @echo " make solaris : for Sun-OS 5.x gcc"
+ @echo " make sol-x86 : for Solaris 7 x86"
+ @echo " make freebsd : for BSD 4.4 systems"
+ @echo " make bsd : for BSD systems, cc or gcc, if not in the above lists"
+ @echo " make cygwin : for Microsoft Windows and Cygwin gcc"
+
+sun:
+ @$(MAKE) CC=gcc CFLAGS="-O2 -pipe -fomit-frame-pointer -Wunused -I../../include" LDFLAGS="-s -L../../lib -ldao" $(EXE)
+
+linux:
+ @$(MAKE) CC=gcc CFLAGS="-O2 -pipe -I../../include -fomit-frame-pointer -Wunused" LDFLAGS="-s -L../../lib -ldao" $(EXE)
+
+solaris:
+ @$(MAKE) CC=gcc CFLAGS="-O2 -pipe -I../../include -fomit-frame-pointer -Wunused" LDFLAGS="-s -L../../lib -ldao -lsocket -lnsl -L/usr/ucblib -lucb" $(EXE)
+
+sol-x86:
+ @$(MAKE) CC=gcc CFLAGS="-O2 -I../../include -fomit-frame-pointer -Wunused" LDFLAGS="-s -L../../lib -ldao -lsocket -lnsl" $(EXE)
+
+freebsd:
+ @$(MAKE) CC=gcc CFLAGS="-O2 -pipe -I../../include -fomit-frame-pointer -Wunused" LDFLAGS="-s -L../../lib -ldao" $(EXE)
+
+bsd:
+ @$(MAKE) CC=gcc CFLAGS="-O2 -pipe -I../../include -fomit-frame-pointer -Wunused" LDFLAGS="-s -L../../lib -ldao" $(EXE)
+
+cygwin:
+ @$(MAKE) CC=gcc CFLAGS="-O2 -pipe -I../../include -fomit-frame-pointer -Wunused" LDFLAGS="-s -L../../lib -ldao" $(EXE)
+
+
+.c: ; $(CC) -o $@ $@.c $(CFLAGS) $(LDFLAGS)
+
+
+install: $(EXE)
+ install -m 0700 $? $(HOME)/bin
+
+clean:
+ rm -f $(EXE) *.exe *.o *~
diff --git a/util/backup/backupacct.c b/util/backup/backupacct.c
new file mode 100644
index 0000000..f503767
--- /dev/null
+++ b/util/backup/backupacct.c
@@ -0,0 +1,58 @@
+/*-------------------------------------------------------*/
+/* util/backupacct.c ( NTHU MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : ³Æ¥÷©Ò¦³¨Ï¥ÎªÌ .ACCT */
+/* create : 01/10/19 */
+/* update : / / */
+/* author : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+
+int
+main()
+{
+ struct dirent *de;
+ DIR *dirp;
+ char *ptr, ch;
+ char fpath[128], bakpath[128], cmd[256];
+ time_t now;
+ struct tm *ptime;
+
+ time(&now);
+ ptime = localtime(&now);
+ /* «Ø¥ß³Æ¥÷¸ô®|¥Ø¿ý */
+ sprintf(fpath, "%s/acct%02d%02d%02d", BAKPATH, ptime->tm_year % 100, ptime->tm_mon + 1, ptime->tm_mday);
+ mkdir(fpath, 0755);
+
+ /* §ïÅÜÅv­­¨Ï ftp ¶ÇÀɤ£·|º|¶Ç */
+ sprintf(cmd, "cp %s/%s %s/; chmod 644 %s/%s", BBSHOME, FN_SCHEMA, fpath, fpath, FN_SCHEMA);
+ system(cmd);
+
+ for (ch = 'a'; ch <= 'z'; ch++)
+ {
+ sprintf(cmd, "%s/usr/%c", BBSHOME, ch);
+ if (chdir(cmd) || !(dirp = opendir(".")))
+ exit(-1);
+
+ sprintf(bakpath, "%s/%c", fpath, ch);
+ mkdir(bakpath, 0755);
+
+ /* §â¦U¨Ï¥ÎªÌªº .ACCT ³£«þ¨©¨ì acct/*userid/ */
+ while (de = readdir(dirp))
+ {
+ ptr = de->d_name;
+
+ if (ptr[0] > ' ' && ptr[0] != '.')
+ {
+ sprintf(cmd, "cp %s/%s %s/%s.ACCT", ptr, FN_ACCT, bakpath, ptr);
+ system(cmd);
+ }
+ }
+ closedir(dirp);
+ }
+
+ exit(0);
+}
diff --git a/util/backup/backupbrd.c b/util/backup/backupbrd.c
new file mode 100644
index 0000000..22518c4
--- /dev/null
+++ b/util/backup/backupbrd.c
@@ -0,0 +1,51 @@
+/*-------------------------------------------------------*/
+/* util/backupbrd.c ( NTHU MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : ³Æ¥÷©Ò¦³¬ÝªO¸ê®Æ */
+/* create : 01/10/19 */
+/* update : / / */
+/* author : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+
+int
+main()
+{
+ struct dirent *de;
+ DIR *dirp;
+ char *ptr;
+ char bakpath[128], cmd[256];
+ time_t now;
+ struct tm *ptime;
+
+ if (chdir(BBSHOME "/brd") || !(dirp = opendir(".")))
+ exit(-1);
+
+ /* «Ø¥ß³Æ¥÷¸ô®|¥Ø¿ý */
+ time(&now);
+ ptime = localtime(&now);
+ sprintf(bakpath, "%s/brd%02d%02d%02d", BAKPATH, ptime->tm_year % 100, ptime->tm_mon + 1, ptime->tm_mday);
+ mkdir(bakpath, 0755);
+
+ /* §ïÅÜÅv­­¨Ï ftp ¶ÇÀɤ£·|º|¶Ç */
+ sprintf(cmd, "cp %s/%s %s/; chmod 644 %s/%s", BBSHOME, FN_BRD, bakpath, bakpath, FN_BRD);
+ system(cmd);
+
+ /* §â¦U¬ÝªO¤À§OÀ£ÁY¦¨¤@­ÓÀ£ÁYÀÉ */
+ while (de = readdir(dirp))
+ {
+ ptr = de->d_name;
+
+ if (ptr[0] > ' ' && ptr[0] != '.')
+ {
+ sprintf(cmd, "tar cfz %s/%s.tgz %s", bakpath, ptr, ptr);
+ system(cmd);
+ }
+ }
+ closedir(dirp);
+
+ exit(0);
+}
diff --git a/util/backup/backupgem.c b/util/backup/backupgem.c
new file mode 100644
index 0000000..bcf49ec
--- /dev/null
+++ b/util/backup/backupgem.c
@@ -0,0 +1,77 @@
+/*-------------------------------------------------------*/
+/* util/backupgem.c ( NTHU MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : ³Æ¥÷©Ò¦³ºëµØ°Ï¸ê®Æ */
+/* create : 01/10/19 */
+/* update : / / */
+/* author : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+
+int
+main()
+{
+ struct dirent *de;
+ DIR *dirp;
+ char *ptr;
+ char bakpath[128], cmd[256];
+ time_t now;
+ struct tm *ptime;
+
+ if (chdir(BBSHOME "/gem") || !(dirp = opendir(".")))
+ exit(-1);
+
+ /* «Ø¥ß³Æ¥÷¸ô®|¥Ø¿ý */
+ time(&now);
+ ptime = localtime(&now);
+ sprintf(bakpath, "%s/gem%02d%02d%02d", BAKPATH, ptime->tm_year % 100, ptime->tm_mon + 1, ptime->tm_mday);
+ mkdir(bakpath, 0755);
+
+ sprintf(cmd, "cp %s %s/", FN_DIR, bakpath);
+ system(cmd);
+
+ /* §â 0~9 @ A~V ¤À§OÀ£ÁY¦¨¤@­ÓÀ£ÁYÀÉ */
+ while (de = readdir(dirp))
+ {
+ ptr = de->d_name;
+
+ /* ¬ÝªOªººëµØ°Ï¥t¥~³Æ¥÷ */
+ if (!strcmp(ptr, "brd"))
+ continue;
+
+ if (ptr[0] > ' ' && ptr[0] != '.')
+ {
+ sprintf(cmd, "tar cfz %s/%s.tgz %s", bakpath, ptr, ptr);
+ system(cmd);
+ }
+ }
+ closedir(dirp);
+
+
+ /* ³Æ¥÷¬ÝªO */
+
+ if (chdir(BBSHOME "/gem/brd") || !(dirp = opendir(".")))
+ exit(-1);
+
+ /* «Ø¥ß³Æ¥÷¸ô®|¥Ø¿ý */
+ sprintf(bakpath, "%s/brd", bakpath);
+ mkdir(bakpath, 0755);
+
+ /* §â¦U¬ÝªO¤À§OÀ£ÁY¦¨¤@­ÓÀ£ÁYÀÉ */
+ while (de = readdir(dirp))
+ {
+ ptr = de->d_name;
+
+ if (ptr[0] > ' ' && ptr[0] != '.')
+ {
+ sprintf(cmd, "tar cfz %s/%s.tgz %s", bakpath, ptr, ptr);
+ system(cmd);
+ }
+ }
+ closedir(dirp);
+
+ exit(0);
+}
diff --git a/util/backup/backupoth.c b/util/backup/backupoth.c
new file mode 100644
index 0000000..cfe3eb3
--- /dev/null
+++ b/util/backup/backupoth.c
@@ -0,0 +1,40 @@
+/*-------------------------------------------------------*/
+/* util/backupoth.c ( YZU WindTopBBS Ver 3.00 ) */
+/*-------------------------------------------------------*/
+/* target : ³Æ¥÷©Ò¦³¨Ï¥ÎªÌ¸ê®Æ */
+/* create : 95/03/29 */
+/* update : 97/03/29 */
+/* author : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+
+int
+main()
+{
+ int i;
+ char *str;
+ char bakpath[128], cmd[256];
+ time_t now;
+ struct tm *ptime;
+ char *folders[] = {"bin", "etc", "innd", "run", "src", NULL};
+
+ time(&now);
+ ptime = localtime(&now);
+
+ sprintf(bakpath, "%s/oth%02d%02d%02d", BAKPATH, ptime->tm_year % 100, ptime->tm_mon + 1, ptime->tm_mday);
+ mkdir(bakpath, 0755);
+
+ for (i = 0; str = folders[i]; i++)
+ {
+ if (*str)
+ {
+ sprintf(cmd, "tar cfz %s/%s.tgz %s/%s", bakpath, str, BBSHOME, str);
+ system(cmd);
+ }
+ }
+
+ exit(0);
+}
diff --git a/util/backup/backupusr.c b/util/backup/backupusr.c
new file mode 100644
index 0000000..4b488c3
--- /dev/null
+++ b/util/backup/backupusr.c
@@ -0,0 +1,58 @@
+/*-------------------------------------------------------*/
+/* util/backupusr.c ( NTHU MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : ³Æ¥÷©Ò¦³¨Ï¥ÎªÌ¸ê®Æ */
+/* create : 01/10/19 */
+/* update : / / */
+/* author : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+
+int
+main()
+{
+ struct dirent *de;
+ DIR *dirp;
+ char *ptr, ch;
+ char fpath[128], bakpath[128], cmd[256];
+ time_t now;
+ struct tm *ptime;
+
+ time(&now);
+ ptime = localtime(&now);
+ /* «Ø¥ß³Æ¥÷¸ô®|¥Ø¿ý */
+ sprintf(fpath, "%s/usr%02d%02d%02d", BAKPATH, ptime->tm_year % 100, ptime->tm_mon + 1, ptime->tm_mday);
+ mkdir(fpath, 0755);
+
+ /* §ïÅÜÅv­­¨Ï ftp ¶ÇÀɤ£·|º|¶Ç */
+ sprintf(cmd, "cp %s/%s %s/; chmod 644 %s/%s", BBSHOME, FN_SCHEMA, fpath, fpath, FN_SCHEMA);
+ system(cmd);
+
+ for (ch = 'a'; ch <= 'z'; ch++)
+ {
+ sprintf(cmd, "%s/usr/%c", BBSHOME, ch);
+ if (chdir(cmd) || !(dirp = opendir(".")))
+ exit(-1);
+
+ sprintf(bakpath, "%s/%c", fpath, ch);
+ mkdir(bakpath, 0755);
+
+ /* §â¦U¨Ï¥ÎªÌ¤À§OÀ£ÁY¦¨¤@­ÓÀ£ÁYÀÉ */
+ while (de = readdir(dirp))
+ {
+ ptr = de->d_name;
+
+ if (ptr[0] > ' ' && ptr[0] != '.')
+ {
+ sprintf(cmd, "tar cfz %s/%s.tgz %s", bakpath, ptr, ptr);
+ system(cmd);
+ }
+ }
+ closedir(dirp);
+ }
+
+ exit(0);
+}
diff --git a/util/backup/restoreacct.c b/util/backup/restoreacct.c
new file mode 100644
index 0000000..ee3a952
--- /dev/null
+++ b/util/backup/restoreacct.c
@@ -0,0 +1,58 @@
+/*-------------------------------------------------------*/
+/* util/restoreacct.c ( NTHU MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : ÁÙ­ì©Ò¦³¨Ï¥ÎªÌ .ACCT */
+/* create : 02/03/26 */
+/* update : / / */
+/* author : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#if 0
+
+ BAKPATH ¤U·|¦³«Ü¦h¤£¦P¤é´Á³Æ¥÷ªº .ACCT ¸ê®Æ¡A¨Ò¦p acct010101 acct010102
+ §â­n´_­ìªº¨º¥÷§ó¦W¬° acct (mv acct010101 acct)
+ °õ¦æ¥»µ{¦¡§Y¥i¥þ³¡´_­ì
+
+#endif
+
+
+#include "bbs.h"
+
+
+int
+main()
+{
+ struct dirent *de;
+ DIR *dirp;
+ char *ptr, ch;
+ char bakpath[128], cmd[256];
+
+ strcpy(bakpath, BAKPATH "/acct");
+ if (chdir(bakpath))
+ exit(-1);
+
+ sprintf(cmd, "cp %s %s/%s; chmod 600 %s/%s", FN_SCHEMA, BBSHOME, FN_SCHEMA, BBSHOME, FN_SCHEMA);
+ system(cmd);
+
+ for (ch = 'a'; ch <= 'z'; ch++)
+ {
+ sprintf(cmd, "%s/usr/%c", BBSHOME, ch);
+ if (chdir(cmd) || !(dirp = opendir(".")))
+ exit(-1);
+
+ while (de = readdir(dirp))
+ {
+ ptr = de->d_name;
+
+ if (ptr[0] > ' ' && ptr[0] != '.')
+ {
+ sprintf(cmd, "cp %s/%c/%s.ACCT %s/%s", bakpath, ch, ptr, ptr, FN_ACCT);
+ system(cmd);
+ }
+ }
+ closedir(dirp);
+ }
+
+ exit(0);
+}
diff --git a/util/backup/restorebrd.c b/util/backup/restorebrd.c
new file mode 100644
index 0000000..a3914c9
--- /dev/null
+++ b/util/backup/restorebrd.c
@@ -0,0 +1,56 @@
+/*-------------------------------------------------------*/
+/* util/restorebrd.c ( NTHU MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : ÁÙ­ì©Ò¦³¬ÝªO¸ê®Æ */
+/* create : 01/10/26 */
+/* update : / / */
+/* author : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#if 0
+
+ BAKPATH ¤U·|¦³«Ü¦h¤£¦P¤é´Á³Æ¥÷ªº¬ÝªO¸ê®Æ¡A¨Ò¦p brd010101 brd010102
+ §â­n´_­ìªº¨º¥÷§ó¦W¬° brd (mv brd010101 brd)
+ °õ¦æ¥»µ{¦¡§Y¥i¥þ³¡´_­ì
+
+#endif
+
+
+#include "bbs.h"
+
+
+int
+main()
+{
+ struct dirent *de;
+ DIR *dirp;
+ char *ptr;
+ char brdpath[128], cmd[256];
+
+ if (chdir(BAKPATH "/brd") || !(dirp = opendir(".")))
+ exit(-1);
+
+ sprintf(cmd, "cp %s %s/%s; chmod 600 %s/%s", FN_BRD, BBSHOME, FN_BRD, BBSHOME, FN_BRD);
+ system(cmd);
+
+ strcpy(brdpath, BBSHOME "/brd");
+ mkdir(brdpath, 0700);
+
+ while (de = readdir(dirp))
+ {
+ ptr = de->d_name;
+
+ if (!strcmp(ptr, "BRD"))
+ continue;
+
+ if (ptr[0] > ' ' && ptr[0] != '.')
+ {
+ sprintf(cmd, "tar xfz %s -C %s/", ptr, brdpath);
+ system(cmd);
+ }
+ }
+ closedir(dirp);
+
+ exit(0);
+}
diff --git a/util/backup/restoregem.c b/util/backup/restoregem.c
new file mode 100644
index 0000000..93cf871
--- /dev/null
+++ b/util/backup/restoregem.c
@@ -0,0 +1,80 @@
+/*-------------------------------------------------------*/
+/* util/restoregem.c ( NTHU MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : ÁÙ­ì©Ò¦³ºëµØ°Ï¸ê®Æ */
+/* create : 01/10/26 */
+/* update : / / */
+/* author : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#if 0
+
+ BAKPATH ¤U·|¦³«Ü¦h¤£¦P¤é´Á³Æ¥÷ªººëµØ°Ï¸ê®Æ¡A¨Ò¦p gem010101 gem010102
+ §â­n´_­ìªº¨º¥÷§ó¦W¬° gem (mv gem010101 gem)
+ °õ¦æ¥»µ{¦¡§Y¥i¥þ³¡´_­ì
+
+#endif
+
+
+#include "bbs.h"
+
+
+int
+main()
+{
+ struct dirent *de;
+ DIR *dirp;
+ char *ptr;
+ char gempath[128], cmd[256];
+
+ if (chdir(BAKPATH "/gem") || !(dirp = opendir(".")))
+ exit(-1);
+
+ strcpy(gempath, BBSHOME "/gem");
+ mkdir(gempath, 0700);
+
+ sprintf(cmd, "cp %s %s/", FN_DIR, gempath);
+ system(cmd);
+
+ /* §â 0~9 @ A~V ¤À§O¸ÑÀ£ÁY¦^¨Ó */
+ while (de = readdir(dirp))
+ {
+ ptr = de->d_name;
+
+ /* ¬ÝªOªººëµØ°Ï¥t¥~¸ÑÀ£ÁY */
+ if (!strcmp(ptr, "brd"))
+ continue;
+
+ if (ptr[0] > ' ' && ptr[0] != '.')
+ {
+ sprintf(cmd, "tar xfz %s -C %s/", ptr, gempath);
+ system(cmd);
+ }
+ }
+ closedir(dirp);
+
+
+ /* ¦^´_¬ÝªO */
+
+ if (chdir(BAKPATH "/gem/brd") || !(dirp = opendir(".")))
+ exit(-1);
+
+ strcat(gempath, "/brd");
+ mkdir(gempath, 0700);
+
+ /* §â¦U¬ÝªO¤À§O¸ÑÀ£ÁY¦^¨Ó */
+ while (de = readdir(dirp))
+ {
+ ptr = de->d_name;
+
+ if (ptr[0] > ' ' && ptr[0] != '.')
+ {
+ sprintf(cmd, "tar xfz %s -C %s/", ptr, gempath);
+ system(cmd);
+ }
+ }
+ closedir(dirp);
+
+ exit(0);
+}
diff --git a/util/backup/restoreusr.c b/util/backup/restoreusr.c
new file mode 100644
index 0000000..d87ffe5
--- /dev/null
+++ b/util/backup/restoreusr.c
@@ -0,0 +1,64 @@
+/*-------------------------------------------------------*/
+/* util/restoreusr.c ( NTHU MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : ÁÙ­ì©Ò¦³¨Ï¥ÎªÌ¸ê®Æ */
+/* create : 01/10/26 */
+/* update : / / */
+/* author : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#if 0
+
+ BAKPATH ¤U·|¦³«Ü¦h¤£¦P¤é´Á³Æ¥÷ªº¨Ï¥ÎªÌ¸ê®Æ¡A¨Ò¦p usr010101 usr010102
+ §â­n´_­ìªº¨º¥÷§ó¦W¬° usr (mv usr010101 usr)
+ °õ¦æ¥»µ{¦¡§Y¥i¥þ³¡´_­ì
+
+#endif
+
+
+#include "bbs.h"
+
+
+int
+main()
+{
+ struct dirent *de;
+ DIR *dirp;
+ char *ptr, ch;
+ char usrpath[128], cmd[256];
+
+ if (chdir(BAKPATH "/usr"))
+ exit(-1);
+
+ sprintf(cmd, "cp %s %s/%s; chmod 600 %s/%s", FN_SCHEMA, BBSHOME, FN_SCHEMA, BBSHOME, FN_SCHEMA);
+ system(cmd);
+
+ strcpy(usrpath, BBSHOME "/usr");
+ mkdir(usrpath, 0700);
+
+ for (ch = 'a'; ch <= 'z'; ch++)
+ {
+ sprintf(cmd, "%s/usr/%c", BAKPATH, ch);
+ if (chdir(cmd) || !(dirp = opendir(".")))
+ exit(-1);
+
+ sprintf(cmd, "%s/%c", usrpath, ch);
+ mkdir(cmd, 0700);
+
+ /* §â¦U¨Ï¥ÎªÌ¤À§O¸ÑÀ£ÁY¦^¨Ó */
+ while (de = readdir(dirp))
+ {
+ ptr = de->d_name;
+
+ if (ptr[0] > ' ' && ptr[0] != '.')
+ {
+ sprintf(cmd, "tar xfz %s -C %s/%c/", ptr, usrpath, ch);
+ system(cmd);
+ }
+ }
+ closedir(dirp);
+ }
+
+ exit(0);
+}
diff --git a/util/bbsmail.c b/util/bbsmail.c
new file mode 100644
index 0000000..55a9c05
--- /dev/null
+++ b/util/bbsmail.c
@@ -0,0 +1,330 @@
+/*-------------------------------------------------------*/
+/* util/bbsmail.c ( NTHU CS MapleBBS Ver 3.00 ) */
+/*-------------------------------------------------------*/
+/* target : ¥Ñ Internet ±H«Hµ¹ BBS ¯¸¤º¨Ï¥ÎªÌ */
+/* create : 95/03/29 */
+/* update : 97/03/29 */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+#include <sysexits.h>
+
+#define ANTI_HTMLMAIL /* itoc.021014: ¾× html_mail */
+#define ANTI_NOTMYCHARSETMAIL /* itoc.030513: ¾× not-mycharset mail */
+
+
+static void
+mailog(msg)
+ char *msg;
+{
+ FILE *fp;
+
+ if (fp = fopen(BMTA_LOGFILE, "a"))
+ {
+ time_t now;
+ struct tm *p;
+
+ time(&now);
+ p = localtime(&now);
+ fprintf(fp, "%02d/%02d %02d:%02d:%02d <bbsmail> %s\n",
+ p->tm_mon + 1, p->tm_mday,
+ p->tm_hour, p->tm_min, p->tm_sec,
+ msg);
+ fclose(fp);
+ }
+}
+
+
+/* ----------------------------------------------------- */
+/* user¡Gshm ³¡¥÷¶·»P cache.c ¬Û®e */
+/* ----------------------------------------------------- */
+
+
+static UCACHE *ushm;
+
+
+static inline void
+init_ushm()
+{
+ ushm = shm_new(UTMPSHM_KEY, sizeof(UCACHE));
+}
+
+
+static inline void
+bbs_biff(userid)
+ char *userid;
+{
+ UTMP *utmp, *uceil;
+ usint offset;
+
+ offset = ushm->offset;
+ if (offset > (MAXACTIVE - 1) * sizeof(UTMP)) /* Thor.980805: ¤£µMcall¤£¨ì */
+ offset = (MAXACTIVE - 1) * sizeof(UTMP);
+
+ utmp = ushm->uslot;
+ uceil = (void *) utmp + offset;
+
+ do
+ {
+ if (!str_cmp(utmp->userid, userid))
+ utmp->status |= STATUS_BIFF;
+ } while (++utmp <= uceil);
+}
+
+
+/* ----------------------------------------------------- */
+/* ¥Dµ{¦¡ */
+/* ----------------------------------------------------- */
+
+
+static int
+mail2bbs(userid)
+ char *userid;
+{
+ HDR hdr;
+ char buf[512], title[256], sender[256], owner[256], nick[256], folder[64];
+ char *str, *ptr, decode;
+ int fd;
+ FILE *fp;
+
+ /* check if the userid is in our bbs now */
+
+ usr_fpath(folder, userid, NULL);
+ if (!dashd(folder))
+ {
+ sprintf(buf, "BBS user <%s> not existed", userid);
+ mailog(buf);
+ return EX_NOUSER;
+ }
+ strcat(folder, "/" FN_DIR);
+
+ if (rec_num(folder, sizeof(HDR)) >= MAX_BBSMAIL)
+ {
+ sprintf(buf, "BBS user <%s> over-spammed", userid);
+ mailog(buf);
+ return EX_NOUSER;
+ }
+
+ /* parse header */
+
+ title[0] = sender[0] = owner[0] = nick[0] = '\0';
+ decode = 0;
+
+ while (fgets(buf, sizeof(buf), stdin))
+ {
+start:
+ if (!memcmp(buf, "From", 4))
+ {
+ if ((str = strchr(buf, '<')) && (ptr = strrchr(str, '>')))
+ {
+ if (str[-1] == ' ')
+ str[-1] = '\0';
+
+ if (strchr(++str, '@'))
+ *ptr = '\0';
+ else /* ¥Ñ local host ±H«H */
+ strcpy(ptr, "@" MYHOSTNAME);
+
+ if (ptr = (char *) strchr(buf, ' '))
+ {
+ while (*++ptr == ' ')
+ ;
+ }
+
+ if (ptr && *ptr == '"')
+ {
+ char *right;
+
+ if (right = strrchr(++ptr, '"'))
+ *right = '\0';
+
+ str_decode(ptr);
+ sprintf(sender, "%s (%s)", str, ptr);
+ strcpy(nick, ptr);
+ strcpy(owner, str);
+ }
+ else /* Thor.980907: ¨S¦³ finger name, ¯S§O³B²z */
+ {
+ strcpy(sender, str);
+ strcpy(owner, str);
+ }
+ }
+ else
+ {
+ strtok(buf, " \t\n\r");
+ strcpy(sender, (char *) strtok(NULL, " \t\n\r"));
+
+ if (!strchr(sender, '@')) /* ¥Ñ local host ±H«H */
+ strcat(sender, "@" MYHOSTNAME);
+ strcpy(owner, sender);
+ }
+
+ /* itoc.040804: ¾×«H¶Â¥Õ¦W³æ */
+ str_lower(buf, owner); /* «O«ù­ì email ªº¤j¤p¼g */
+ if (ptr = (char *) strchr(buf, '@'))
+ {
+ *ptr++ = '\0';
+
+ if (!acl_has(MAIL_ACLFILE, buf, ptr) ||
+ acl_has(UNMAIL_ACLFILE, buf, ptr) > 0)
+ {
+ sprintf(buf, "SPAM %s", sender);
+ mailog(buf);
+ return EX_NOUSER;
+ }
+ }
+ }
+
+ else if (!memcmp(buf, "Subject: ", 9))
+ {
+ str_ansi(title, buf + 9, sizeof(title));
+ /* str_decode(title); */
+ /* LHD.051106: ­Y¥i¯à¸g RFC 2047 QP encode «h¦³¥i¯à¦h¦æ subject */
+ if (strstr(buf + 9, "=?"))
+ {
+ while (fgets(buf, sizeof(buf), stdin))
+ {
+ if (buf[0] == ' ' || buf[0] == '\t') /* ²Ä¤G¦æ¥H«á·|¥HªÅ¥Õ©Î TAB ¶}ÀY */
+ str_ansi(title + strlen(title), strstr(buf, "=?"), sizeof(title));
+ else
+ {
+ str_decode(title);
+ goto start;
+ }
+ }
+ }
+ }
+
+ else if (!memcmp(buf, "Content-Type: ", 14))
+ {
+ str = buf + 14;
+
+#ifdef ANTI_HTMLMAIL
+ /* ¤@¯ë BBS ¨Ï¥ÎªÌ³q±`¥u±H¤å¦r¶l¥ó©Î¬O±q¨ä¥L BBS ¯¸±H¤å³¹¨ì¦Û¤vªº«H½c
+ ¦Ó¼s§i«H¥ó³q±`¬O html ®æ¦¡©Î¬O¸Ì­±¦³§¨±a¨ä¥LÀÉ®×
+ §Q¥Î¶l¥óªºÀÉÀY¦³ Content-Type: ªºÄݩʧⰣ¤F text/plain (¤å¦r¶l¥ó) ªº«H¥ó³£¾×¤U¨Ó */
+ if (*str != '\0' && str_ncmp(str, "text/plain", 10))
+ {
+ sprintf(buf, "ANTI-HTML [%d] %s => %s", getppid(), sender, userid);
+ mailog(buf);
+ return EX_NOUSER;
+ }
+#endif
+
+#ifdef ANTI_NOTMYCHARSETMAIL
+ {
+ char charset[32];
+ mm_getcharset(str, charset, sizeof(charset));
+ if (str_cmp(charset, MYCHARSET) && str_cmp(charset, "us-ascii"))
+ {
+ sprintf(buf, "ANTI-NONMYCHARSET [%d] %s => %s", getppid(), sender, userid);
+ mailog(buf);
+ return EX_NOUSER;
+ }
+ }
+#endif
+ }
+
+ else if (!memcmp(buf, "Content-Transfer-Encoding: ", 27))
+ {
+ mm_getencode(buf + 27, &decode);
+ }
+
+ else if (buf[0] == '\n')
+ {
+ break;
+ }
+ }
+
+ /* allocate a file for the new mail */
+
+ fd = hdr_stamp(folder, 0, &hdr, buf);
+ hdr.xmode = MAIL_INCOME;
+
+ str_ncpy(hdr.owner, owner, sizeof(hdr.owner));
+ str_ncpy(hdr.nick, nick, sizeof(hdr.nick));
+ if (!title[0])
+ sprintf(title, "¨Ó¦Û %.64s", sender);
+ str_ncpy(hdr.title, title, sizeof(hdr.title));
+
+ /* copy the stdin to the specified file */
+
+ fp = fdopen(fd, "w");
+
+ fprintf(fp, "§@ªÌ: %s\n¼ÐÃD: %s\n®É¶¡: %s\n\n",
+ sender, title, Btime(&hdr.chrono));
+
+ while (fgets(buf, sizeof(buf), stdin))
+ {
+ if (decode && ((fd = mmdecode(buf, decode, buf)) > 0))
+ buf[fd] = '\0';
+
+ fputs(buf, fp);
+ }
+
+ fclose(fp);
+
+ /* append the record to the .DIR */
+
+ rec_add(folder, &hdr, sizeof(HDR));
+
+ bbs_biff(userid); /* itoc.021113: ³qª¾ user ¦³·s«H¥ó */
+
+ /* Thor.980827: ¥[¤W parent process id¡A¥H«K§ì©U§£«H */
+ sprintf(buf, "[%d] %s => %s", getppid(), sender, userid);
+ mailog(buf);
+
+ return 0;
+}
+
+
+static void
+sig_catch(sig)
+ int sig;
+{
+ char buf[512];
+
+ while (fgets(buf, sizeof(buf), stdin))
+ ;
+ sprintf(buf, "signal [%d]", sig);
+ mailog(buf);
+ exit(0);
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ char buf[512];
+
+ /* argv[1] is userid in bbs */
+
+ if (argc < 2)
+ {
+ printf("Usage:\t%s <bbs_userid>\n", argv[0]);
+ exit(-1);
+ }
+
+ setgid(BBSGID);
+ setuid(BBSUID);
+ chdir(BBSHOME);
+
+ signal(SIGBUS, sig_catch);
+ signal(SIGSEGV, sig_catch);
+ signal(SIGPIPE, sig_catch);
+
+ init_ushm();
+ str_lower(buf, argv[1]); /* §â userid ´«¦¨¤p¼g */
+
+ if (mail2bbs(buf))
+ {
+ /* eat mail queue */
+ while (fgets(buf, sizeof(buf), stdin))
+ ;
+ }
+ exit(0);
+}
diff --git a/util/bquota.c b/util/bquota.c
new file mode 100644
index 0000000..505bbc7
--- /dev/null
+++ b/util/bquota.c
@@ -0,0 +1,385 @@
+/*-------------------------------------------------------*/
+/* util/bquota.c ( NTHU CS MapleBBS Ver 3.00 ) */
+/*-------------------------------------------------------*/
+/* target : BBS user quota maintain & mail expire */
+/* create : 95/03/29 */
+/* update : 97/03/29 */
+/*-------------------------------------------------------*/
+/* syntax : bquota */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+
+#ifdef OVERDUE_MAILDEL
+
+#include <sys/resource.h>
+
+#undef VERBOSE
+#define LOG_FILE "run/bquota.log"
+
+
+#define MAX_SIZE 20000 /* ¶W¹L 20k bytes ´N§R°£ */
+
+
+/* itoc.011002.µù¸Ñ: XXXX_DUE ¦b config.h ¤¤©w¸q */
+/* itoc.011002: ¦³¥²­n§â´»°²ªº bquota ©µªø®É¶¡¶Ü¡H */
+
+static time_t file_due;
+static time_t mail_due;
+static time_t mark_due;
+
+
+static FILE *flog;
+
+
+/* ----------------------------------------------------- */
+/* synchronize folder & files */
+/* ----------------------------------------------------- */
+
+
+#define SYNC_DB_SIZE 1024
+
+
+typedef struct
+{
+ time_t chrono;
+ char exotic;
+} SyncMail;
+
+
+static SyncMail *xlist;
+static int xsize;
+
+
+static int
+sync_cmp(s1, s2)
+ SyncMail *s1, *s2;
+{
+ return s1->chrono - s2->chrono;
+}
+
+
+static int
+bquota(userid)
+ char *userid;
+{
+ HDR hdr;
+ char *fname, *str, fpath[80], fnew[80], fold[80];
+ int bonus, cc, count, xmode;
+ time_t due, chrono;
+ FILE *fpr, *fpw;
+ struct stat st;
+ DIR *dirp;
+ struct dirent *de;
+
+ SyncMail *tlist, *tsync;
+ int tsize;
+
+ bonus = 0;
+ sprintf(fpath, "%s/@", userid);
+
+ if (!(dirp = opendir(fpath)))
+ {
+ fprintf(flog, "dir: %s\n", fpath);
+ rmdir(userid);
+ return bonus;
+ }
+
+ fname = strchr(fpath, '@') + 1;
+ *fname++ = '/';
+ count = 0;
+
+ tlist = xlist;
+ tsize = xsize;
+
+ while (de = readdir(dirp))
+ {
+ str = de->d_name;
+ cc = *str;
+ if (cc == '.')
+ continue;
+
+ strcpy(fname, str);
+
+ if (cc != '@')
+ {
+ unlink(fpath);
+#ifdef VERBOSE
+ fprintf(flog, "NAME: %s\n", fpath);
+#endif
+ continue;
+ }
+
+ chrono = chrono32(str);
+
+ if (chrono < mark_due)
+ {
+#ifdef VERBOSE
+ fprintf(flog, "OLD : %s\n", fpath);
+#endif
+ unlink(fpath);
+ bonus++;
+ continue;
+ }
+
+ if (chrono < file_due)
+ {
+ if (!stat(fpath, &st) && (st.st_size >= MAX_SIZE || st.st_size <= 0))
+ {
+#ifdef VERBOSE
+ fprintf(flog, "BIG : %s\n", fpath);
+#endif
+ unlink(fpath);
+ bonus++;
+ continue;
+ }
+ }
+
+ if (count >= tsize)
+ {
+ xsize = (tsize += count);
+ xlist = tlist = (SyncMail *) realloc(tlist, sizeof(SyncMail) * tsize);
+ }
+ tlist[count].chrono = chrono;
+ tlist[count].exotic = 1;
+ count++;
+ }
+ closedir(dirp);
+
+ if (count > 1)
+ qsort(tlist, count, sizeof(SyncMail), sync_cmp);
+
+ sprintf(fold, "%s/.DIR", userid);
+
+ if (fpr = fopen(fold, "r"))
+ {
+ cc = 0;
+ if (fpw = (FILE *) f_new(fold, fnew))
+ {
+ while (fread(&hdr, sizeof(HDR), 1, fpr) == 1)
+ {
+ tsync = (SyncMail *) bsearch(&hdr.chrono,
+ tlist, count, sizeof(SyncMail), sync_cmp);
+
+ if (!tsync)
+ continue;
+
+ tsync->exotic = 0;
+
+ xmode = hdr.xmode;
+
+ if (xmode & MAIL_MARKED)
+ due = mark_due;
+ else
+ due = mail_due;
+
+ if (hdr.chrono < due)
+ {
+ strcpy(fname, hdr.xname);
+ unlink(fpath);
+ bonus++;
+#ifdef VERBOSE
+ fprintf(flog, "DUE : %s\n", fpath);
+#endif
+ }
+ else
+ {
+ if (fwrite(&hdr, sizeof(HDR), 1, fpw) != 1)
+ {
+ cc = count = -1;
+ break;
+ }
+ cc++;
+ }
+ }
+
+ fclose(fpw);
+ if (count < 0)
+ unlink(fnew);
+ }
+ fclose(fpr);
+
+ if (cc > 0)
+ rename(fnew, fold);
+ else
+ unlink(fold);
+ }
+
+ *fname++ = '@';
+ for (cc = 0; cc < count; cc++)
+ {
+ if (tlist[cc].exotic)
+ {
+ archiv32(tlist[cc].chrono, fname);
+ unlink(fpath);
+ bonus++;
+#ifdef VERBOSE
+ fprintf(flog, "SYNC: %s\n", fpath);
+#endif
+ }
+ }
+
+ /* maintain userid/... */
+
+ fname -= 3;
+ *fname = '\0';
+ if (dirp = opendir(fpath))
+ {
+ due = file_due;
+ while (de = readdir(dirp))
+ {
+ str = de->d_name;
+
+ if (str[0] == 'b' && str[1] == 'u') /* buf.? */
+ {
+ strcpy(fname, str);
+
+ stat(fpath, &st);
+ if ((st.st_mtime < due) || (st.st_size > 3000) || (st.st_size <= 0))
+ {
+#ifdef VERBOSE
+ fprintf(flog, "FILE: %s\n", fpath);
+#endif
+ unlink(fpath);
+ bonus++;
+ continue;
+ }
+ }
+ else if (!strcmp(str, "log"))
+ {
+ strcpy(fname, str);
+ stat(fpath, &st);
+ if ((st.st_mtime < due) || (st.st_size <= 0))
+ {
+ unlink(fpath);
+ bonus++;
+ continue;
+ }
+
+#define ULOG_SIZE 3072
+
+ if ((st.st_size >= ULOG_SIZE) && (cc = open(fpath, O_RDWR)) >= 0)
+ {
+ char buf[ULOG_SIZE];
+
+ lseek(cc, st.st_size - ULOG_SIZE, 0);
+ count = read(cc, buf, sizeof(buf));
+ if (str = strchr(buf, '\n'))
+ {
+ str++;
+ count -= str - buf;
+ lseek(cc, 0, 0);
+ write(cc, str, count);
+ ftruncate(cc, count);
+ bonus++;
+ }
+ close(cc);
+ }
+
+#undef ULOG_SIZE
+
+ continue;
+ }
+ }
+
+ closedir(dirp);
+ }
+
+ return bonus;
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ DIR *dirp;
+ struct dirent *de;
+ char *fname, fpath[64];
+ time_t start, end;
+ int bonus, visit;
+ struct rlimit rl;
+
+ setuid(BBSUID);
+ setgid(BBSGID);
+
+ chdir(BBSHOME);
+
+ rl.rlim_cur = rl.rlim_max = 0;
+ setrlimit(RLIMIT_CORE, &rl);
+ time(&start);
+
+ if (argc == 2 && (argc = argv[1][0]) >= 'a' && argc <= 'z')
+ {
+ flog = stderr;
+ }
+ else
+ {
+ flog = fopen(LOG_FILE, "w");
+ argc = 'a' + (start / 86400) % 26; /* ¨C¹j 26 ¤Ñ bquota ¤@¦¸ */
+ }
+
+ /* visit the second hierarchy */
+
+ sprintf(fpath, "usr/%c", argc);
+ fprintf(flog, "# visit: %s\n\n", fpath);
+
+ if (chdir(fpath) || (!(dirp = opendir("."))))
+ {
+ fprintf(flog, "## unable to enter user home\n");
+ fclose(flog);
+ exit(-1);
+ }
+
+ /* traverse user home */
+
+ file_due = start - FILE_DUE * 86400;
+ mail_due = start - MAIL_DUE * 86400;
+ mark_due = start - MARK_DUE * 86400;
+
+ xlist = (SyncMail *) malloc(SYNC_DB_SIZE * sizeof(SyncMail));
+ xsize = SYNC_DB_SIZE;
+
+ bonus = 0;
+ visit = 0;
+
+ fprintf(flog, "\nbegin\n");
+
+ while (de = readdir(dirp))
+ {
+ fname = de->d_name;
+ if (*fname != '.' && *fname > ' ')
+ {
+#ifdef VERBOSE
+ fprintf(flog, "\n[%s]\n", fname);
+#endif
+ bonus += bquota(fname);
+ visit++;
+ }
+ }
+ closedir(dirp);
+
+ time(&end);
+ fprintf(flog, "# ¶}©l®É¶¡¡G%s\n", Btime(&start));
+ fprintf(flog, "# µ²§ô®É¶¡¡G%s\n", Btime(&end));
+ end -= start;
+ start = end % 60;
+ end /= 60;
+ fprintf(flog, "# Á`­p¯Ó®É¡G%d:%d:%d\n", end / 60, end % 60, start);
+ fprintf(flog, "# µù¥U¤H¼Æ¡G%d\n", visit);
+ fprintf(flog, "# ²M°£ÀɮסG%d\n", bonus);
+ fclose(flog);
+ exit(0);
+}
+
+#else
+int
+main()
+{
+ printf("You should define OVERDUE_MAILDEL first.\n");
+ return -1;
+}
+#endif /* HAVE_NETTOOL */
diff --git a/util/brdmail.c b/util/brdmail.c
new file mode 100644
index 0000000..1656431
--- /dev/null
+++ b/util/brdmail.c
@@ -0,0 +1,334 @@
+/*-------------------------------------------------------*/
+/* util/brdmail.c ( NTHU CS MapleBBS Ver 3.00 ) */
+/*-------------------------------------------------------*/
+/* target : ¥Ñ Internet ±H«Hµ¹ BBS ¯¸¤º¬ÝªO¡Aµø¬° post */
+/* create : 95/03/29 */
+/* update : 97/03/29 */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+#include <sysexits.h>
+
+#define ANTI_HTMLMAIL /* itoc.021014: ¾× html_mail */
+#define ANTI_NOTMYCHARSETMAIL /* itoc.030513: ¾× not-mycharset mail */
+
+
+static void
+mailog(msg)
+ char *msg;
+{
+ FILE *fp;
+
+ if (fp = fopen(BMTA_LOGFILE, "a"))
+ {
+ time_t now;
+ struct tm *p;
+
+ time(&now);
+ p = localtime(&now);
+ fprintf(fp, "%02d/%02d %02d:%02d:%02d <brdmail> %s\n",
+ p->tm_mon + 1, p->tm_mday,
+ p->tm_hour, p->tm_min, p->tm_sec,
+ msg);
+ fclose(fp);
+ }
+}
+
+
+/* ----------------------------------------------------- */
+/* board¡Gshm ³¡¥÷¶·»P cache.c ¬Û®e */
+/* ----------------------------------------------------- */
+
+
+static BCACHE *bshm;
+
+
+static void
+init_bshm()
+{
+ /* itoc.030727: ¦b¶}±Ò bbsd ¤§«e¡AÀ³¸Ó´N­n°õ¦æ¹L account¡A
+ ©Ò¥H bshm À³¸Ó¤w³]©w¦n */
+
+ bshm = shm_new(BRDSHM_KEY, sizeof(BCACHE));
+
+ if (bshm->uptime <= 0) /* bshm ¥¼³]©w§¹¦¨ */
+ exit(0);
+}
+
+
+static BRD *
+brd_get(bname)
+ char *bname;
+{
+ BRD *bhdr, *tail;
+
+ bhdr = bshm->bcache;
+ tail = bhdr + bshm->number;
+ do
+ {
+ if (!str_cmp(bname, bhdr->brdname))
+ return bhdr;
+ } while (++bhdr < tail);
+ return NULL;
+}
+
+
+/* ----------------------------------------------------- */
+/* ¥Dµ{¦¡ */
+/* ----------------------------------------------------- */
+
+
+static int
+mail2brd(brd)
+ BRD *brd;
+{
+ HDR hdr;
+ char buf[512], title[256], sender[256], owner[256], nick[256], folder[64];
+ char *str, *ptr, decode, *brdname;
+ int fd;
+ FILE *fp;
+
+ /* check if the brdname is in our bbs now */
+
+ brdname = brd->brdname;
+
+ if (brd->battr & BRD_LOCAL){
+ sprintf(buf, "BBS brd <%s> denied", brdname);
+ mailog(buf);
+ return EX_NOUSER;
+ }
+
+ brd_fpath(folder, brdname, NULL);
+ if (!dashd(folder))
+ {
+ sprintf(buf, "BBS brd <%s> not existed", brdname);
+ mailog(buf);
+ return EX_NOUSER;
+ }
+ strcat(folder, "/" FN_DIR);
+
+ /* parse header */
+
+ title[0] = sender[0] = owner[0] = nick[0] = '\0';
+ decode = 0;
+
+ while (fgets(buf, sizeof(buf), stdin))
+ {
+start:
+ if (!memcmp(buf, "From", 4))
+ {
+ if ((str = strchr(buf, '<')) && (ptr = strrchr(str, '>')))
+ {
+ if (str[-1] == ' ')
+ str[-1] = '\0';
+
+ if (strchr(++str, '@'))
+ *ptr = '\0';
+ else /* ¥Ñ local host ±H«H */
+ strcpy(ptr, "@" MYHOSTNAME);
+
+ if (ptr = (char *) strchr(buf, ' '))
+ {
+ while (*++ptr == ' ')
+ ;
+ }
+
+ if (ptr && *ptr == '"')
+ {
+ char *right;
+
+ if (right = strrchr(++ptr, '"'))
+ *right = '\0';
+
+ str_decode(ptr);
+ sprintf(sender, "%s (%s)", str, ptr);
+ strcpy(nick, ptr);
+ strcpy(owner, str);
+ }
+ else /* Thor.980907: ¨S¦³ finger name, ¯S§O³B²z */
+ {
+ strcpy(sender, str);
+ strcpy(owner, str);
+ }
+ }
+ else
+ {
+ strtok(buf, " \t\n\r");
+ strcpy(sender, (char *) strtok(NULL, " \t\n\r"));
+
+ if (!strchr(sender, '@')) /* ¥Ñ local host ±H«H */
+ strcat(sender, "@" MYHOSTNAME);
+ strcpy(owner, sender);
+ }
+
+ /* itoc.040804: ¾×«H¶Â¥Õ¦W³æ */
+ str_lower(buf, owner); /* «O«ù­ì email ªº¤j¤p¼g */
+ if (ptr = (char *) strchr(buf, '@'))
+ {
+ *ptr++ = '\0';
+
+ if (!acl_has(MAIL_ACLFILE, buf, ptr) ||
+ acl_has(UNMAIL_ACLFILE, buf, ptr) > 0)
+ {
+ sprintf(buf, "SPAM %s", sender);
+ mailog(buf);
+ return EX_NOUSER;
+ }
+ }
+ }
+
+ else if (!memcmp(buf, "Subject: ", 9))
+ {
+ str_ansi(title, buf + 9, sizeof(title));
+ /* str_decode(title); */
+ /* LHD.051106: ­Y¥i¯à¸g RFC 2047 QP encode «h¦³¥i¯à¦h¦æ subject */
+ if (strstr(buf + 9, "=?"))
+ {
+ while (fgets(buf, sizeof(buf), stdin))
+ {
+ if (buf[0] == ' ' || buf[0] == '\t') /* ²Ä¤G¦æ¥H«á·|¥HªÅ¥Õ©Î TAB ¶}ÀY */
+ str_ansi(title + strlen(title), strstr(buf, "=?"), sizeof(title));
+ else
+ {
+ str_decode(title);
+ goto start;
+ }
+ }
+ }
+ }
+
+ else if (!memcmp(buf, "Content-Type: ", 14))
+ {
+ str = buf + 14;
+
+#ifdef ANTI_HTMLMAIL
+ /* ¤@¯ë BBS ¨Ï¥ÎªÌ³q±`¥u±H¤å¦r¶l¥ó©Î¬O±q¨ä¥L BBS ¯¸±H¤å³¹¨ì¦Û¤vªº«H½c
+ ¦Ó¼s§i«H¥ó³q±`¬O html ®æ¦¡©Î¬O¸Ì­±¦³§¨±a¨ä¥LÀÉ®×
+ §Q¥Î¶l¥óªºÀÉÀY¦³ Content-Type: ªºÄݩʧⰣ¤F text/plain (¤å¦r¶l¥ó) ªº«H¥ó³£¾×¤U¨Ó */
+ if (*str != '\0' && str_ncmp(str, "text/plain", 10))
+ {
+ sprintf(buf, "ANTI-HTML [%d] %s => %s", getppid(), sender, brdname);
+ mailog(buf);
+ return EX_NOUSER;
+ }
+#endif
+
+#ifdef ANTI_NOTMYCHARSETMAIL
+ {
+ char charset[32];
+ mm_getcharset(str, charset, sizeof(charset));
+ if (str_cmp(charset, MYCHARSET) && str_cmp(charset, "us-ascii"))
+ {
+ sprintf(buf, "ANTI-NONMYCHARSET [%d] %s => %s", getppid(), sender, brdname);
+ mailog(buf);
+ return EX_NOUSER;
+ }
+ }
+#endif
+ }
+
+ else if (!memcmp(buf, "Content-Transfer-Encoding: ", 27))
+ {
+ mm_getencode(buf + 27, &decode);
+ }
+
+ else if (buf[0] == '\n')
+ {
+ break;
+ }
+ }
+
+ /* allocate a file for the new post */
+
+ fd = hdr_stamp(folder, 'A', &hdr, buf);
+ hdr.xmode = POST_INCOME;
+
+ str_ncpy(hdr.owner, owner, sizeof(hdr.owner));
+ str_ncpy(hdr.nick, nick, sizeof(hdr.nick));
+ if (!title[0])
+ sprintf(title, "¨Ó¦Û %.64s", sender);
+ str_ncpy(hdr.title, title, sizeof(hdr.title));
+
+ /* copy the stdin to the specified file */
+
+ fp = fdopen(fd, "w");
+
+ fprintf(fp, "µo«H¤H: %.50s ¬ÝªO: %s\n¼Ð ÃD: %.72s\nµo«H¯¸: %s\n\n",
+ sender, brdname, title, Btime(&hdr.chrono));
+
+ while (fgets(buf, sizeof(buf), stdin))
+ {
+ if (decode && ((fd = mmdecode(buf, decode, buf)) > 0))
+ buf[fd] = '\0';
+
+ fputs(buf, fp);
+ }
+
+ fclose(fp);
+
+ /* append the record to the .DIR */
+
+ rec_bot(folder, &hdr, sizeof(HDR));
+
+ /* amaki.040311: ­nÅýclass_item()§ó·s¥Î */
+ brd->btime = -1;
+
+ /* Thor.0827: ¥[¤W parent process id¡A¥H«K§ì©U§£«H */
+ sprintf(buf, "[%d] %s => %s", getppid(), sender, brdname);
+ mailog(buf);
+
+ return 0;
+}
+
+
+static void
+sig_catch(sig)
+ int sig;
+{
+ char buf[512];
+
+ while (fgets(buf, sizeof(buf), stdin))
+ ;
+ sprintf(buf, "signal [%d]", sig);
+ mailog(buf);
+ exit(0);
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ char buf[512];
+ BRD *brd;
+
+ /* argv[1] is brdname in bbs */
+
+ if (argc < 2)
+ {
+ printf("Usage:\t%s <bbs_brdname>\n", argv[0]);
+ exit(-1);
+ }
+
+ setgid(BBSGID);
+ setuid(BBSUID);
+ chdir(BBSHOME);
+
+ signal(SIGBUS, sig_catch);
+ signal(SIGSEGV, sig_catch);
+ signal(SIGPIPE, sig_catch);
+
+ init_bshm();
+ brd = brd_get(argv[1]);
+
+ if (!brd || mail2brd(brd))
+ {
+ /* eat mail queue */
+ while (fgets(buf, sizeof(buf), stdin))
+ ;
+ }
+ exit(0);
+}
diff --git a/util/camera.c b/util/camera.c
new file mode 100644
index 0000000..079b9e9
--- /dev/null
+++ b/util/camera.c
@@ -0,0 +1,395 @@
+/*-------------------------------------------------------*/
+/* util/camera.c ( NTHU CS MapleBBS Ver 3.00 ) */
+/*-------------------------------------------------------*/
+/* target : «Ø¥ß [°ÊºA¬ÝªO] cache */
+/* create : 95/03/29 */
+/* update : 97/03/29 */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+
+static char *list[] = /* src/include/struct.h */
+{
+ "opening.0", /* FILM_OPENING0 */
+ "opening.1", /* FILM_OPENING1 */
+ "opening.2", /* FILM_OPENING2 */
+ "goodbye", /* FILM_GOODBYE */
+ "notify", /* FILM_NOTIFY */
+ "mquota", /* FILM_MQUOTA */
+ "mailover", /* FILM_MAILOVER */
+ "mgemover", /* FILM_MGEMOVER */
+ "birthday", /* FILM_BIRTHDAY */
+ "apply", /* FILM_APPLY */
+ "justify", /* FILM_JUSTIFY */
+ "re-reg", /* FILM_REREG */
+ "e-mail", /* FILM_EMAIL */
+ "newuser", /* FILM_NEWUSER */
+ "tryout", /* FILM_TRYOUT */
+ "post", /* FILM_POST */
+ NULL /* FILM_MOVIE */
+};
+
+
+static void
+str_strip(str) /* itoc.060417: ±N°ÊºA¬ÝªO¨C¦Cªº¼e«×Õt¦b SCR_WIDTH */
+ char *str;
+{
+ int ch, ansi, len;
+
+ /* ­Y°ÊºA¬ÝªO¦³¤@¦Cªº¼e«×¶W¹L SCR_WIDTH¡A·|Åã¥Ü¤G¦C¡A³y¦¨±Æª©¿ù»~ (¥D­n¬OÂIºqªº³¡¤À)
+ ©Ò¥H´N°®¯Ü§â¶W¹L SCR_WIDTH ªº³¡¤À§R°£ (¦b¦¹¤£¦Ò¼{¼e¿Ã¹õ) */
+ /* ­Y¥»¦C¤¤¦³ \033*s ©Î \033*n¡AÅã¥Ü¥X¨Ó·|§óªø¡A©Ò¥H­n¯S§O³B²z */
+
+ ansi = len = 0;
+ while (ch = *str++)
+ {
+ if (ch == '\n')
+ {
+ break;
+ }
+ else if (ch == '\033')
+ {
+ ansi = 1;
+ }
+ else if (ansi)
+ {
+ if (ch == '*') /* KEY_ESC + * + s/n ¨q¥X ID/username¡A¦Ò¼{³Ì¤jªø«× */
+ {
+ ansi = 0;
+ len += BMAX(IDLEN, UNLEN) - 1;
+ if (len > SCR_WIDTH)
+ {
+ *str = '\0';
+ break;
+ }
+ }
+ else if ((ch < '0' || ch > '9') && ch != ';' && ch != '[')
+ ansi = 0;
+ }
+ else
+ {
+ if (++len > SCR_WIDTH)
+ {
+ *str = '\0';
+ break;
+ }
+ }
+ }
+}
+
+
+static FCACHE image;
+static int number; /* ¥Ø«e¤w mirror ´X½g¤F */
+static int total; /* ¥Ø«e¤w mirror ´X byte ¤F */
+
+
+static int /* 1:¦¨¥\ 0:¤w¶W¹L½g¼Æ©Î®e¶q */
+mirror(fpath, line)
+ char *fpath;
+ int line; /* 0:¨t²Î¤å¥ó¡A¤£­­¨î¦C¼Æ !=0:°ÊºA¬ÝªO¡Aline ¦C */
+{
+ int size, i;
+ char buf[FILM_SIZ];
+ char tmp[ANSILINELEN];
+ struct stat st;
+ FILE *fp;
+
+ /* ­Y¤w¸g¶W¹L³Ì¤j½g¼Æ¡A«h¤£¦AÄ~Äò mirror */
+ if (number >= MOVIE_MAX - 1)
+ return 0;
+
+ if (stat(fpath, &st))
+ size = -1;
+ else
+ size = st.st_size;
+
+ if (size > 0 && size < FILM_SIZ && (fp = fopen(fpath, "r")))
+ {
+ size = i = 0;
+ while (fgets(tmp, ANSILINELEN, fp))
+ {
+ str_strip(tmp);
+
+ strcpy(buf + size, tmp);
+ size += strlen(tmp);
+
+ if (line)
+ {
+ /* °ÊºA¬ÝªO¡A³Ì¦h line ¦C */
+ if (++i >= line)
+ break;
+ }
+ }
+ fclose(fp);
+
+ if (i != line)
+ {
+ /* °ÊºA¬ÝªO¡A­Y¤£¨ì line ¦C¡A­n¶ñº¡ line ¦C */
+ for (; i < line; i++)
+ {
+ buf[size] = '\n';
+ size++;
+ }
+ buf[size] = '\0';
+ }
+ }
+
+ if (size <= 0 || size >= FILM_SIZ)
+ {
+ if (line) /* ¦pªG¬O °ÊºA¬ÝªO/ÂIºq ¯ÊÀɮסA´N¤£ mirror */
+ return 1;
+
+ /* ¦pªG¬O¨t²Î¤å¥ó¥X¿ùªº¸Ü¡A­n¸É¤W¥h */
+ sprintf(buf, "½Ð§i¶D¯¸ªøÀÉ®× %s ¿ò¥¢©Î¬O¹L¤j", fpath);
+ size = strlen(buf);
+ }
+
+ size++; /* Thor.980804: +1 ±Nµ²§Àªº '\0' ¤]ºâ¤J */
+
+ i = total + size;
+ if (i >= MOVIE_SIZE) /* ­Y¥[¤J³o½g·|¶W¹L¥þ³¡®e¶q¡A«h¤£ mirror */
+ return 0;
+
+ memcpy(image.film + total, buf, size);
+ image.shot[++number] = total = i;
+
+ return 1;
+}
+
+
+static void
+do_gem(folder) /* itoc.011105: §â¬ÝªO/ºëµØ°Ïªº¤å³¹¦¬¶i movie */
+ char *folder; /* index ¸ô®| */
+{
+ char fpath[64];
+ FILE *fp;
+ HDR hdr;
+
+ if (fp = fopen(folder, "r"))
+ {
+ while (fread(&hdr, sizeof(HDR), 1, fp) == 1)
+ {
+ if (hdr.xmode & (GEM_RESTRICT | GEM_RESERVED | GEM_BOARD)) /* ­­¨î¯Å¡B¬ÝªO ¤£©ñ¤J movie ¤¤ */
+ continue;
+
+ hdr_fpath(fpath, folder, &hdr);
+
+ if (hdr.xmode & GEM_FOLDER) /* ¹J¨ì¨÷©v«h°j°é¶i¥h¦¬ movie */
+ {
+ do_gem(fpath);
+ }
+ else /* plain text */
+ {
+ if (!mirror(fpath, MOVIE_LINES))
+ break;
+ }
+ }
+ fclose(fp);
+ }
+}
+
+
+static void
+lunar_calendar(key, now, ptime) /* itoc.050528: ¥Ñ¶§¾äºâ¹A¾ä¤é´Á */
+ char *key;
+ time_t *now;
+ struct tm *ptime;
+{
+#if 0 /* Table ªº·N¸q */
+
+ (1) "," ¥u¬O¤À¹j¡A¤è«Kµ¹¤H¾\Ū¦Ó¤w
+ (2) «e 12 ­Ó byte ¤À§O¥Nªí¹A¾ä 1-12 ¤ë¬°¤j¤ë©Î¬O¤p¤ë¡C"L":¤j¤ë¤T¤Q¤Ñ¡A"-":¤p¤ë¤G¤Q¤E¤Ñ
+ (3) ²Ä 14 ­Ó byte ¥Nªí¤µ¦~¹A¾ä¶|¤ë¡C"X":µL¶|¤ë¡A"123456789:;<" ¤À§O¥Nªí¹A¾ä 1-12 ¬O¶|¤ë
+ (4) ²Ä 16-20 ­Ó bytes ¥Nªí¤µ¦~¹A¾ä·s¦~¬O­þ¤Ñ¡C¨Ò¦p "02:15" ªí¥Ü¶§¾ä¤G¤ë¤Q¤­¤é¬O¹A¾ä·s¦~
+
+#endif
+
+ #define TABLE_INITAIL_YEAR 2005
+ #define TABLE_FINAL_YEAR 2011
+
+ char Table[TABLE_FINAL_YEAR - TABLE_INITAIL_YEAR + 1][21] =
+ {
+ "-L-L-LL-L-L-,X,02:09", /* 2005 Âû¦~ */
+ "L-L-L-L-LL-L,7,01:29", /* 2006 ª¯¦~ */
+ "--L--L-LLL-L,X,02:18", /* 2007 ½Þ¦~ */
+ "L--L--L-LL-L,X,02:07", /* 2008 ¹«¦~ */
+ "LL--L--L-L-L,5,01:26", /* 2009 ¤û¦~ */
+ "L-L-L--L-L-L,X,02:14", /* 2010 ªê¦~ */
+ "L-LL-L--L-L-,X,02:03", /* 2011 ¨ß¦~ */
+ };
+
+ char year[21];
+
+ time_t nyd; /* ¤µ¦~ªº¹A¾ä·s¦~ */
+ struct tm ntime;
+
+ int i;
+ int Mon, Day;
+ int leap; /* 0:¥»¦~µL¶|¤ë */
+
+ /* ¥ý§ä¥X¤µ¤Ñ¬O¹A¾ä­þ¤@¦~ */
+
+ memcpy(&ntime, ptime, sizeof(ntime));
+
+ for (i = TABLE_FINAL_YEAR - TABLE_INITAIL_YEAR; i >= 0; i--)
+ {
+ strcpy(year, Table[i]);
+ ntime.tm_year = TABLE_INITAIL_YEAR - 1900 + i;
+ ntime.tm_mday = atoi(year + 18);
+ ntime.tm_mon = atoi(year + 15) - 1;
+ nyd = mktime(&ntime);
+
+ if (*now >= nyd)
+ break;
+ }
+
+ /* ¦A±q¹A¾ä¥¿¤ëªì¤@¶}©l¼Æ¨ì¤µ¤Ñ */
+
+ leap = (year[13] == 'X') ? 0 : 1;
+
+ Mon = Day = 1;
+ for (i = (*now - nyd) / 86400; i > 0; i--)
+ {
+ if (++Day > (year[Mon - 1] == 'L' ? 30: 29))
+ {
+ Mon++;
+ Day = 1;
+
+ if (leap == 2)
+ {
+ leap = 0;
+ Mon--;
+ year[Mon - 1] = '-'; /* ¶|¤ë¥²¤p¤ë */
+ }
+ else if (year[13] == Mon + '0')
+ {
+ if (leap == 1) /* ¤U¤ë¬O¶|¤ë */
+ leap++;
+ }
+ }
+ }
+
+ sprintf(key, "%02d:%02d", Mon, Day);
+}
+
+
+static char *
+do_today()
+{
+ FILE *fp;
+ char buf[80], *ptr1, *ptr2, *ptr3, *today;
+ char key1[6]; /* mm/dd: ¶§¾ä mm¤ëdd¤é */
+ char key2[6]; /* mm/#A: ¶§¾ä mm¤ëªº²Ä#­Ó¬P´ÁA */
+ char key3[6]; /* MM\DD: ¹A¾ä MM¤ëDD¤é */
+ time_t now;
+ struct tm *ptime;
+ static char feast[64];
+
+ time(&now);
+ ptime = localtime(&now);
+ sprintf(key1, "%02d/%02d", ptime->tm_mon + 1, ptime->tm_mday);
+ sprintf(key2, "%02d/%d%c", ptime->tm_mon + 1, (ptime->tm_mday - 1) / 7 + 1, ptime->tm_wday + 'A');
+ lunar_calendar(key3, &now, ptime);
+
+ today = image.today;
+ sprintf(today, "%s %.2s", key1, "¤é¤@¤G¤T¥|¤­¤»" + (ptime->tm_wday << 1));
+
+ if (fp = fopen(FN_ETC_FEAST, "r"))
+ {
+ while (fgets(buf, 80, fp))
+ {
+ if (buf[0] == '#')
+ continue;
+
+ if ((ptr1 = strtok(buf, " \t\n")) && (ptr2 = strtok(NULL, " \t\n")))
+ {
+ if (!strcmp(ptr1, key1) || !strcmp(ptr1, key2) || !strcmp(ptr1, key3))
+ {
+ str_ncpy(today, ptr2, sizeof(image.today));
+
+ if (ptr3 = strtok(NULL, " \t\n"))
+ sprintf(feast, "etc/feasts/%s", ptr3);
+ if (!dashf(feast))
+ feast[0] = '\0';
+
+ break;
+ }
+ }
+ }
+ fclose(fp);
+ }
+
+ return feast;
+}
+
+
+int
+main()
+{
+ int i;
+ char *fname, *str, *feast, fpath[64];
+ FCACHE *fshm;
+
+ setgid(BBSGID);
+ setuid(BBSUID);
+ chdir(BBSHOME);
+
+ number = total = 0;
+
+ /* --------------------------------------------------- */
+ /* ¤µ¤Ñ¸`¤é */
+ /* --------------------------------------------------- */
+
+ feast = do_today();
+
+ /* --------------------------------------------------- */
+ /* ¸ü¤J±`¥Îªº¤å¥ó¤Î help */
+ /* --------------------------------------------------- */
+
+ strcpy(fpath, "gem/@/@");
+ fname = fpath + 7;
+
+ for (i = 0; str = list[i]; i++)
+ {
+ if (i >= FILM_OPENING0 && i <= FILM_OPENING2 && feast[0]) /* ­Y¬O¸`¤é¡A¶}ÀYµe­±¥Î¸Ó¸`¤éªºµe­± */
+ {
+ mirror(feast, 0);
+ }
+ else
+ {
+ strcpy(fname, str);
+ mirror(fpath, 0);
+ }
+ }
+
+ /* itoc.µù¸Ñ: ¨ì³o¸Ì¥H«á¡AÀ³¸Ó¤w¦³ FILM_MOVIE ½g */
+
+ /* --------------------------------------------------- */
+ /* ¸ü¤J°ÊºA¬ÝªO */
+ /* --------------------------------------------------- */
+
+ /* itoc.µù¸Ñ: °ÊºA¬ÝªO¤ÎÂIºq¥»¦X­p¥u¦³ MOVIE_MAX - FILM_MOVIE - 1 ½g¤~·|³Q¦¬¶i movie */
+
+ sprintf(fpath, "gem/brd/%s/@/@note", BN_CAMERA); /* °ÊºA¬ÝªOªº¸s²ÕÀɮצWºÙÀ³©R¦W¬° @note */
+ do_gem(fpath); /* §â [note] ºëµØ°Ï¦¬¶i movie */
+
+#ifdef HAVE_SONG_CAMERA
+ brd_fpath(fpath, BN_KTV, FN_DIR);
+ do_gem(fpath); /* §â³QÂIªººq¦¬¶i movie */
+#endif
+
+ /* --------------------------------------------------- */
+ /* resolve shared memory */
+ /* --------------------------------------------------- */
+
+ image.shot[0] = number; /* Á`¦@¦³´X¤ù */
+
+ fshm = (FCACHE *) shm_new(FILMSHM_KEY, sizeof(FCACHE));
+ memcpy(fshm, &image, sizeof(FCACHE));
+
+ /* printf("%d/%d films, %d/%d bytes\n", number, MOVIE_MAX, total, MOVIE_SIZE); */
+
+ exit(0);
+}
diff --git a/util/changeperm.c b/util/changeperm.c
new file mode 100644
index 0000000..f293c81
--- /dev/null
+++ b/util/changeperm.c
@@ -0,0 +1,58 @@
+/*-------------------------------------------------------*/
+/* util/changeperm.c ( NTHU CS MapleBBS Ver 3.00 ) */
+/*-------------------------------------------------------*/
+/* target : ¥h°£¨C¦ì¨Ï¥ÎªÌ§ó§ïIDªºÅv­­ */
+/* author : chensc.bbs@sony.tfcis.org */
+/* create : 06/09/03 */
+/* update : */
+/*-------------------------------------------------------*/
+
+#include "bbs.h"
+
+int
+main()
+{
+ printf("Be SURE to change all user's perm, continue? y/n : ");
+ if(getchar() !='y')
+ exit(1);
+
+ int i,fd;
+ SCHEMA *usr;
+ struct stat st;
+
+ chdir(BBSHOME);
+
+ if ((fd = open(FN_SCHEMA, O_RDONLY)) < 0)
+ {
+ printf("ERROR at open file\n");
+ exit(1);
+ }
+
+ fstat(fd, &st);
+ usr = (SCHEMA *) malloc(st.st_size);
+ read(fd, usr, st.st_size);
+ close(fd);
+
+ fd = st.st_size / sizeof(SCHEMA);
+
+ for (i = 0; i < fd; i++)
+ {
+ ACCT cuser;
+ char fpath[64];
+
+ usr_fpath(fpath, usr[i].userid, FN_ACCT);
+ if (rec_get(fpath, &cuser, sizeof(cuser), 0) < 0)
+ {
+ printf("%s: read error (maybe no such id?)\n", usr[i].userid);
+ continue;
+ }
+
+ if (HAS_PERM(PERM_CHANGEID))
+ cuser.userlevel ^= PERM_CHANGEID;
+
+ if (rec_put(fpath, &cuser, sizeof(cuser), 0, NULL) < 0)
+ printf("%s: write error\n", usr[i].userid);
+
+ printf("%s : done !\n",usr[i].userid);
+ }
+}
diff --git a/util/counter.c b/util/counter.c
new file mode 100644
index 0000000..8620d15
--- /dev/null
+++ b/util/counter.c
@@ -0,0 +1,112 @@
+/*-------------------------------------------------------*/
+/* util/counter.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : ¾ú¥v­y¸ñ */
+/* create : 03/03/03 */
+/* update : / / */
+/* author : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+
+#define OUTFILE_COUNTER "gem/@/@-counter"
+#define FN_RUN_COUNTER "run/var/counter"
+
+
+typedef struct
+{
+ time_t uptime; /* §ó·s®É¶¡ */
+
+ int total_acct; /* ¯¸¤WÁ`¦@¦³¦h¤Ö¨Ï¥ÎªÌµù¥U */
+ int online_usr; /* ½u¤W¦P®É¦³¦h¤Ö¨Ï¥ÎªÌ */
+ int today_usr; /* ¤µ¤éÁ`¦@¦³´X­Ó¤H¦¸¤W¯¸ */
+ int online_usr_every_hour[24];/* ¤µ¤é¦U¾ãÂI½u¤W¦³¦h¤Ö¨Ï¥ÎªÌ */
+ int total_brd; /* ¯¸¤WÁ`¦@¦³¦h¤Ö¬ÝªO */
+
+ char ident[12]; /* ¯d¥Õ¥HÂX¥R¥Î */
+} COUNTER;
+
+
+#define break_record(new, old) (new > old + old / 20) /* ·í·s¬ö¿ý¶W¹L¬ö¿ý 5% ¥H¤W®É¡A´N§ï¼g¬ö¿ý */
+
+
+int
+main()
+{
+ UCACHE *ushm;
+ COUNTER counter;
+
+ int num;
+ char *fname, date[20];
+ FILE *fp;
+ time_t now;
+ struct tm *ptime;
+
+ chdir(BBSHOME);
+
+ if (!(fp = fopen(OUTFILE_COUNTER, "a+")))
+ return -1;
+
+ fname = FN_RUN_COUNTER;
+
+ memset(&counter, 0, sizeof(COUNTER));
+ rec_get(fname, &counter, sizeof(COUNTER), 0);
+
+ counter.uptime = time(&now);
+ ptime = localtime(&now);
+ sprintf(date, "¡i%02d/%02d/%02d %02d:%02d¡j",
+ ptime->tm_year % 100, ptime->tm_mon + 1, ptime->tm_mday, ptime->tm_hour, ptime->tm_min);
+
+
+ /* µù¥U¤H¼Æ */
+ num = rec_num(FN_SCHEMA, sizeof(SCHEMA));
+ if (break_record(num, counter.total_acct))
+ {
+ fprintf(fp, "¡¹ %s \033[31m¥»¯¸µù¥U¤H¼Æ\033[m¨g¶P¶W¹L \033[1;31m%d\033[m ¤H\n", date, num);
+ counter.total_acct = num;
+ }
+
+ /* ½u¤W¤H¼Æ */
+ ushm = shm_new(UTMPSHM_KEY, sizeof(UCACHE));
+ num = ushm->count;
+ if (break_record(num, counter.online_usr))
+ {
+ fprintf(fp, "¡· %s \033[32m¦P®É½u¤W¤H¼Æ\033[m­º¦¸¹F¨ì \033[1;32m%d\033[m ¤H\n", date, num);
+ counter.online_usr = num;
+ }
+ counter.online_usr_every_hour[ptime->tm_hour] = num; /* ¥»¤p®Éªº½u¤W¤H¼Æ¡A´N®³³o¦¸ sample ­È */
+
+ /* ¥»¤é¤W¯¸¤H¦¸ */
+ if (ptime->tm_hour == 23) /* ¨C¤Ñ­pºâ¤@¦¸¥»¤é¤W¯¸¤H¦¸ */
+ {
+ int i;
+
+ /* itoc.µù¸Ñ: ³o­Ó­È¬O¤£¥¿½Tªº¡A¦]¬°¥u¬O®³¨C­Ó¤p®É sample ªº©M¡A
+ ¦Ó¥BÁÙ°²³]¨C­Ó¤H¥­§¡¦b¯¸¤W®É¶¡¬O 60 ¤ÀÄÁ */
+ num = 0;
+ for (i = 0; i < 24; i++)
+ num += counter.online_usr_every_hour[i];
+
+ if (break_record(num, counter.today_usr))
+ {
+ fprintf(fp, "¡» %s \033[33m³æ¤é¤W¯¸¤H¦¸\033[m¥¿¦¡¬ð¯} \033[1;33m%d\033[m ¤H\n", date, num);
+ counter.today_usr = num;
+ }
+ }
+
+ /* ¬ÝªO­Ó¼Æ */
+ num = rec_num(FN_BRD, sizeof(BRD));
+ if (break_record(num, counter.total_brd))
+ {
+ fprintf(fp, "¡¸ %s \033[34m¥»¯¸¬ÝªO­Ó¼Æ\033[m«Å§G°ª¹F \033[1;34m%d\033[m ­Ó\n", date, num);
+ counter.total_brd = num;
+ }
+
+
+ rec_put(fname, &counter, sizeof(COUNTER), 0, NULL);
+
+ fclose(fp);
+ return 0;
+}
diff --git a/util/expire.c b/util/expire.c
new file mode 100644
index 0000000..85ba130
--- /dev/null
+++ b/util/expire.c
@@ -0,0 +1,490 @@
+/*-------------------------------------------------------*/
+/* util/expire.c ( NTHU CS MapleBBS Ver 3.00 ) */
+/*-------------------------------------------------------*/
+/* target : ¦Û°Ê¬å«H¤u¨ãµ{¦¡ */
+/* create : 95/03/29 */
+/* update : 97/03/29 */
+/*-------------------------------------------------------*/
+/* syntax : expire [day max min board] */
+/* notice : ¥[¤W board ®É¡A¥i expire+sync ¬Y¤@ board */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+
+#if 0 /* itoc.030325: ²©ö»¡©ú */
+
+ expire.c ¥]§t¤GºØ°Ê§@¡Gexpire ©M sync
+
+ 1) ©Ò¿×¡u¹L´Á¡v¡A´N¬O ¤å³¹¤é´Á¹L¤[/¬ÝªO½g¼Æ¹L¦h
+
+ 2) ©Ò¿×¡uexpire¡v¡A´N¬O¦b .DIR ¯Á¤Þ¤¤§ä¥X¹L´Áªº¤å³¹¡A§â³o hdr ±q¯Á¤Þ¤¤²¾°£¡A
+ ¨Ã±N¸ÓÀÉ®×§R°£
+
+ 3) ©Ò¿×¡usync¡v¡A¬°¤FÁ×§KµwºÐ¦³¦h¾lªº©U§£¡A¨t²Î¨C 32 ¤Ñ·|¹ï¬ÝªO¤ºªºÀɮפ@¤@
+ À˵ø¡A­Y¨ä¤£¦b .DIR ¤¤¡Aªí¥Ü³o­ÓÀɮפw¸g±q¯Á¤Þ¤¤¿ò¥¢¤F¡A¦¹®É¨t²Î·|ª½±µ±N
+ ³oÀÉ®×§R°£
+
+#endif
+
+
+#define DEF_DAYS 730 /* ¹w³]²M°£¶W¹L 730 ¤Ñªº¤å³¹ */
+#define DEF_MAXP 5000 /* ¹w³]²M°£¶W¹L 5000 ½gªº¤å³¹ */
+#define DEF_MINP 500 /* ¹w³]§C©ó 500 ½gªº¬ÝªO¤£¬å¤å³¹ */
+
+
+#define EXPIRE_LOG "run/expire.log"
+
+
+typedef struct
+{
+ char bname[BNLEN + 1]; /* board ID */
+ int days; /* expired days */
+ int maxp; /* max post */
+ int minp; /* min post */
+} Life;
+
+
+static int
+life_cmp(a, b)
+ Life *a, *b;
+{
+ return str_cmp(a->bname, b->bname);
+}
+
+
+/* ----------------------------------------------------- */
+/* board¡Gshm ³¡¥÷¶·»P cache.c ¬Û®e */
+/* ----------------------------------------------------- */
+
+
+static BCACHE *bshm;
+
+
+static void
+init_bshm()
+{
+ /* itoc.030727: ¦b¶}±Ò bbsd ¤§«e¡AÀ³¸Ó´N­n°õ¦æ¹L account¡A
+ ©Ò¥H bshm À³¸Ó¤w³]©w¦n */
+
+ bshm = shm_new(BRDSHM_KEY, sizeof(BCACHE));
+
+ if (bshm->uptime <= 0) /* bshm ¥¼³]©w§¹¦¨ */
+ exit(0);
+}
+
+
+/* ----------------------------------------------------- */
+/* synchronize folder & files */
+/* ----------------------------------------------------- */
+
+
+static time_t synctime;
+
+
+typedef struct
+{
+ time_t chrono;
+ char prefix; /* Àɮתº family */
+ char exotic; /* 1:¤£¦b.DIR¤¤ 0:¦b.DIR¤¤ */
+} SyncData;
+
+
+static SyncData *sync_pool;
+static int sync_size, sync_head;
+
+
+#define SYNC_DB_SIZE 2048
+
+
+static int
+sync_cmp(a, b)
+ SyncData *a, *b;
+{
+ return a->chrono - b->chrono;
+}
+
+
+static void
+sync_init(fname)
+ char *fname;
+{
+ int ch, prefix;
+ time_t chrono;
+ char *str, fpath[80];
+ struct dirent *de;
+ DIR *dirp;
+
+ SyncData *xpool;
+ int xsize, xhead;
+
+ if (xpool = sync_pool)
+ {
+ xsize = sync_size;
+ }
+ else
+ {
+ xpool = (SyncData *) malloc(SYNC_DB_SIZE * sizeof(SyncData));
+ xsize = SYNC_DB_SIZE;
+ }
+
+ xhead = 0;
+
+ ch = strlen(fname);
+ memcpy(fpath, fname, ch);
+ fname = fpath + ch;
+ *fname++ = '/';
+ fname[1] = '\0';
+
+ /* itoc.030325.µù¸Ñ: ¥ý§â brd/brdname/?/* ªº©Ò¦³Àɮ׳£¥á¶i¥h xpool[]
+ ³o¥÷Æ[¹î¦W³æ¤¤¡AµM«á¦^¨ì expire() ¤¤Àˬd¸ÓÀɮ׬O§_¦b .DIR ¤¤
+ ­Y¦b .DIR ¤¤¡A´N§â exotic ³]¦^ 0
+ ³Ì«á¦b sync_check() ¤¤§âÆ[¹î¦W³æ¤¤ exotic ÁÙ¬O 1 ªºÀɮ׳£§R°£ */
+
+ ch = '0';
+ for (;;)
+ {
+ *fname = ch++;
+
+ if (dirp = opendir(fpath))
+ {
+ while (de = readdir(dirp))
+ {
+ str = de->d_name;
+ prefix = *str;
+ if (prefix == '.')
+ continue;
+
+ chrono = chrono32(str);
+
+ if (chrono > synctime) /* ³o¬O³Ìªñ­èµoªíªº¤å³¹¡A¤£»Ý­n¥[¤J xpool[] ¥h sync */
+ continue;
+
+ if (xhead >= xsize)
+ {
+ xsize += (xsize >> 1);
+ xpool = (SyncData *) realloc(xpool, xsize * sizeof(SyncData));
+ }
+
+ xpool[xhead].chrono = chrono;
+ xpool[xhead].prefix = prefix;
+ xpool[xhead].exotic = 1; /* ¥ý¥þ³¡¥O¬° 1¡A©ó expire() ¤¤¦A§ï¦^ 0 */
+ xhead++;
+ }
+
+ closedir(dirp);
+ }
+
+ if (ch == 'W')
+ break;
+
+ if (ch == '9' + 1)
+ ch = 'A';
+ }
+
+ if (xhead > 1)
+ qsort(xpool, xhead, sizeof(SyncData), sync_cmp);
+
+ sync_pool = xpool;
+ sync_size = xsize;
+ sync_head = xhead;
+}
+
+
+static void
+sync_check(flog, fname)
+ FILE *flog;
+ char *fname;
+{
+ char *str, fpath[80];
+ SyncData *xpool, *xtail;
+ time_t cc;
+
+ if ((cc = sync_head) <= 0)
+ return;
+
+ xpool = sync_pool;
+ xtail = xpool + cc;
+
+ sprintf(fpath, "%s/ /", fname);
+ str = strchr(fpath, ' ');
+ fname = str + 3;
+
+ do
+ {
+ if (xtail->exotic)
+ {
+ cc = xtail->chrono;
+ fname[-1] = xtail->prefix;
+ *str = radix32[cc & 31];
+ archiv32(cc, fname);
+ unlink(fpath);
+
+ fprintf(flog, "-\t%s\n", fpath);
+ }
+ } while (--xtail >= xpool);
+}
+
+
+static void
+expire(flog, life, sync)
+ FILE *flog;
+ Life *life;
+ int sync;
+{
+ HDR hdr;
+ struct stat st;
+ char fpath[128], fnew[128], index[128], *fname, *bname, *str;
+ int done, keep, total;
+ FILE *fpr, *fpw;
+
+ int days, maxp, minp;
+ int duetime;
+
+ SyncData *xpool, *xsync;
+ int xhead;
+
+ days = life->days;
+ maxp = life->maxp;
+ minp = life->minp;
+ bname = life->bname;
+
+ fprintf(flog, "%s\n", bname);
+
+ sprintf(index, "%s/.DIR", bname);
+ if (!(fpr = fopen(index, "r")))
+ {
+ fprintf(flog, "\tError open file: %s\n", index);
+ return;
+ }
+
+ fpw = f_new(index, fnew);
+ if (!fpw)
+ {
+ fprintf(flog, "\tExclusive lock: %s\n", fnew);
+ fclose(fpr);
+ return;
+ }
+
+ if (sync)
+ {
+ sync_init(bname);
+ xpool = sync_pool;
+ xhead = sync_head;
+ if (xhead <= 0)
+ sync = 0;
+ else
+ fprintf(flog, "\t%d files to sync\n\n", xhead);
+ }
+
+ strcpy(fpath, index);
+ str = (char *) strrchr(fpath, '.');
+ fname = str + 1;
+ *fname++ = '/';
+
+ done = 1;
+ duetime = synctime - days * 86400;
+
+ fstat(fileno(fpr), &st);
+ total = st.st_size / sizeof(hdr);
+
+ while (fread(&hdr, sizeof(hdr), 1, fpr) == 1)
+ {
+ if (hdr.xmode & (POST_MARKED | POST_BOTTOM) || total <= minp)
+ keep = 1;
+ else if (hdr.chrono < duetime || total > maxp)
+ keep = 0;
+ else
+ keep = 1;
+
+ if (sync && (hdr.chrono < synctime))
+ {
+ if (xsync = (SyncData *) bsearch(&hdr.chrono, xpool, xhead, sizeof(SyncData), sync_cmp))
+ {
+ xsync->exotic = 0; /* ³o½g¦b .DIR ¤¤¡A¤£ sync */
+ }
+ else
+ {
+ keep = 0; /* ¤@«ß¤£«O¯d */
+ }
+ }
+
+ if (keep)
+ {
+ if (fwrite(&hdr, sizeof(hdr), 1, fpw) != 1)
+ {
+ fprintf(flog, "\tError in write DIR.n: %s\n", hdr.xname);
+ done = 0;
+ sync = 0; /* Thor.990127: ¨S§@¦¨, ´N§O¬å¤F§a */
+ break;
+ }
+ }
+ else
+ {
+ *str = hdr.xname[7];
+ strcpy(fname, hdr.xname);
+ unlink(fpath);
+ fprintf(flog, "\t%s\n", fname);
+ total--;
+ }
+ }
+ fclose(fpr);
+ fclose(fpw);
+
+ if (done)
+ {
+ sprintf(fpath, "%s.o", index);
+ if (!rename(index, fpath))
+ {
+ if (rename(fnew, index))
+ rename(fpath, index); /* ´«¦^¨Ó */
+ }
+ }
+ unlink(fnew);
+
+ if (sync)
+ sync_check(flog, bname);
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ FILE *fp;
+ int number, count;
+ Life db, table[MAXBOARD], *key;
+ char *ptr, *bname, buf[256];
+ BRD *brdp, *bend;
+
+ /* itoc.030325: ­n¹À¤£«ü©w°Ñ¼Æ¡A­n¹À«ü©w©Ò¦³°Ñ¼Æ */
+ if (argc != 1 && argc != 5)
+ {
+ printf("%s [day max min board]\n", argv[0]);
+ exit(-1);
+ }
+
+ /* ­YµL«ü©w°Ñ¼Æ¡A«h¥Î¹w³]­È */
+ db.days = ((argc == 5) && (number = atoi(argv[1])) > 0) ? number : DEF_DAYS;
+ db.maxp = ((argc == 5) && (number = atoi(argv[2])) > 0) ? number : DEF_MAXP;
+ db.minp = ((argc == 5) && (number = atoi(argv[3])) > 0) ? number : DEF_MINP;
+
+ /* --------------------------------------------------- */
+ /* load expire.conf */
+ /* --------------------------------------------------- */
+
+ setgid(BBSGID);
+ setuid(BBSUID);
+ chdir(BBSHOME);
+
+ init_bshm();
+
+ count = 0;
+ if (argc != 5) /* ­Y¨S¦³«ü©w°Ñ¼Æ¡A¤~»Ý­n¥hŪ expire.conf */
+ {
+ if (fp = fopen(FN_ETC_EXPIRE, "r"))
+ {
+ while (fgets(buf, sizeof(buf), fp))
+ {
+ if (buf[0] == '#')
+ continue;
+
+ bname = (char *) strtok(buf, " \t\r\n");
+ if (bname && *bname)
+ {
+ ptr = (char *) strtok(NULL, " \t\r\n");
+ if (ptr && (number = atoi(ptr)) > 0)
+ {
+ key = &(table[count]);
+ strcpy(key->bname, bname);
+ key->days = number;
+ key->maxp = db.maxp;
+ key->minp = db.minp;
+
+ ptr = (char *) strtok(NULL, " \t\r\n");
+ if (ptr && (number = atoi(ptr)) > 0)
+ {
+ key->maxp = number;
+
+ ptr = (char *) strtok(NULL, " \t\r\n");
+ if (ptr && (number = atoi(ptr)) > 0)
+ key->minp = number;
+ }
+
+ /* expire.conf ¥i¯à¨S¦³ºûÅ@¦Ó¶W¹L MAXBOARD ­ÓªO */
+ if (++count >= MAXBOARD)
+ break;
+ }
+ }
+ }
+ fclose(fp);
+ }
+
+ if (count > 1)
+ qsort(table, count, sizeof(Life), life_cmp);
+ }
+
+ /* --------------------------------------------------- */
+ /* visit all boards */
+ /* --------------------------------------------------- */
+
+ fp = fopen(EXPIRE_LOG, "w");
+
+ chdir("brd");
+
+ synctime = time(NULL) - 10 * 60; /* ¤Q¤ÀÄÁ¤ºªº·s¤å³¹¤£»Ý­n sync */
+ number = synctime / 86400;
+
+ brdp = bshm->bcache;
+ bend = brdp + bshm->number;
+ do
+ {
+ bname = brdp->brdname;
+ if (!*bname)
+ continue;
+
+ {
+ struct tm *ptime;
+ time_t now;
+
+ time(&now);
+ ptime = localtime(&now);
+
+ if (ptime->tm_wday == 0) /* ¨C¶g¤@¬å usies */
+ {
+ sprintf(buf, "%s/%s", bname, "usies");
+ unlink(buf);
+ }
+ }
+
+ /* Thor.981027: ¥[¤W board ®É¡A¥i expire+sync ¬Y¤@ board */
+ if (argc == 5)
+ {
+ if (str_cmp(argv[4], bname))
+ continue;
+
+ number = 0; /* ±j­¢ sync ³oªO */
+ }
+
+ if (count)
+ {
+ key = (Life *) bsearch(bname, table, count, sizeof(Life), life_cmp);
+ if (!key)
+ key = &db;
+ }
+ else
+ {
+ key = &db;
+ }
+
+ strcpy(key->bname, bname); /* ´«¦¨¥¿½Tªº¤j¤p¼g */
+ expire(fp, key, !(number & 31)); /* ¨C¹j 32 ¤Ñ sync ¤@¦¸ */
+ number++;
+ brdp->btime = -1;
+ } while (++brdp < bend);
+
+ fclose(fp);
+ exit(0);
+}
diff --git a/util/gem-index.c b/util/gem-index.c
new file mode 100644
index 0000000..d08f811
--- /dev/null
+++ b/util/gem-index.c
@@ -0,0 +1,245 @@
+/*-------------------------------------------------------*/
+/* util/gem-index.c ( NTHU CS MapleBBS Ver 2.39 ) */
+/*-------------------------------------------------------*/
+/* target : ºëµØ°Ï¯Á¤Þµ{¦¡ (man index) */
+/* create : 95/03/29 */
+/* update : 95/08/08 */
+/*-------------------------------------------------------*/
+/* syntax : gem-index [board] */
+/* [board] ¦³­È ==> ¥u¶]¸Ó board */
+/* ªÅªº ==> ©Ò¦³ªº boards ³£¶] */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+#define COLOR_INDEX /* Thor.980307: ¥[¤WÃC¦â¸Õ¸Õ¬O§_¤ñ¸û©ö§ä */
+
+
+#define GINDEX_LOG (BBSHOME "/run/gindex.log")
+
+
+#define CHRONO_INDEX 1
+#define CHRONO_LOG 2
+
+
+static char fn_index[] = "@/@index";
+static char fn_log[] = "@/@log";
+
+
+static int gem_default;
+static int ndir;
+static int nfile;
+static char pgem[128], pndx[128], pool[128];
+static FILE *flog;
+
+
+/* visit the hierarchy recursively */
+
+
+static void
+gindex(level, toc, fpath, fndx)
+ int level;
+ char *toc;
+ char *fpath;
+ FILE *fndx;
+{
+ int count, xmode;
+ char *fname, *ptr, buf[128];
+ FILE *fgem;
+ HDR hdr;
+
+ if (level > 7) /* endless loop ? */
+ {
+ fprintf(flog, "level: %d [%s]\n", level, fpath);
+ return;
+ }
+
+ if (!level)
+ {
+ fprintf(flog, "%-14s", fpath); /* report */
+ sprintf(pool, "%s/.DIR", fpath);
+ fpath = pool;
+ strcpy(pgem, fpath);
+ }
+
+ if (!(fgem = fopen(fpath, "r")))
+ return;
+
+ fname = fpath;
+ while (xmode = *fname++)
+ {
+ if (xmode == '/')
+ ptr = fname;
+ }
+ if (*ptr != '.')
+ ptr -= 2;
+ fname = ptr;
+
+ if (!fndx)
+ {
+ strcpy(fname, "@/@ing--");
+ fndx = fopen(fpath, "w");
+ if (!fndx)
+ {
+ fclose(fgem);
+ return;
+ }
+ fprintf(fndx, "§Ç¸¹\t\t\tºëµØ°Ï¥DÃD\n"
+ "-------------------------------------------------------------\n");
+ strcpy(pndx, fpath);
+ gem_default = ndir = nfile = 0;
+ }
+
+ count = 0;
+ while (fread(&hdr, sizeof(hdr), 1, fgem) == 1)
+ {
+ count++;
+ xmode = hdr.xmode;
+
+ /* Àˬd¬O§_¬°¯Á¤Þ¡B²§°Ê */
+ if (!level && hdr.chrono <= CHRONO_LOG)
+ {
+ gem_default |= hdr.chrono;
+ continue;
+ }
+
+ if (xmode & GEM_FOLDER)
+ ndir++;
+ else
+ nfile++;
+
+ if (xmode & GEM_RESTRICT)
+ continue;
+
+ sprintf(buf, "%.*s%3d. ", level * 4, toc, count);
+
+#ifdef COLOR_INDEX
+ /* Thor.980307: ¥[¤WÃC¦â¸Õ¸Õ¬O§_¤ñ¸û©ö§ä */
+ if (xmode & GEM_FOLDER)
+ fprintf(fndx, "%s\033[1;37;%dm%s\033[m\n", buf, 41 + (level % 6) , hdr.title);
+ else
+ fprintf(fndx, "%s%s\n", buf, hdr.title);
+#else
+ if (xmode & GEM_FOLDER)
+ fprintf(fndx, "*%s%s\n", buf + 1, hdr.title);
+ else
+ fprintf(fndx, "%*d. %s\n", 4 * level + 3, count, hdr.title);
+#endif
+
+ if ((xmode & (GEM_FOLDER | GEM_BOARD)) == GEM_FOLDER) /* ¤@¯ë¨÷©v */
+ {
+ ptr = hdr.xname; /* F1234567 */
+ sprintf(fname, "%c/%s", (*ptr == '@' ? '@' : ptr[7]), ptr);
+ gindex(level + 1, buf, fpath, fndx);
+ }
+ }
+
+ if (!level)
+ {
+ fclose(fndx);
+ strcpy(fname, fn_index);
+ rename(pndx, fpath);
+
+ /* report */
+
+ fprintf(flog, "==> d: %d\tf: %d\n", ndir, nfile);
+
+ xmode = gem_default;
+ if (xmode != (CHRONO_INDEX | CHRONO_LOG)) /* ¤w¦³¯Á¤Þ¤Î²§°Ê */
+ {
+ sprintf(pool, "%s.o", pgem);
+ sprintf(pndx, "%s.n", pgem);
+ if (fndx = fopen(pndx, "w"))
+ {
+ memset(&hdr, 0, sizeof(HDR));
+ hdr.xmode = GEM_RESERVED;
+ strcpy(hdr.owner, SYSOPNICK);
+
+ if (!(xmode & CHRONO_INDEX))
+ {
+ hdr.chrono = CHRONO_INDEX;
+ strcpy(hdr.xname, fn_index + 2);
+ strcpy(hdr.title, "ºëµØ°Ï¯Á¤Þ");
+ fwrite(&hdr, sizeof(hdr), 1, fndx);
+ }
+
+ if (!(xmode & CHRONO_LOG))
+ {
+ hdr.chrono = CHRONO_LOG;
+ strcpy(hdr.xname, fn_log + 2);
+ strcpy(hdr.title, "ºëµØ°Ï²§°Ê");
+ fwrite(&hdr, sizeof(hdr), 1, fndx);
+ }
+
+ fseek(fgem, (off_t) 0, SEEK_SET);
+
+ while (fread(&hdr, sizeof(hdr), 1, fgem) == 1)
+ {
+ fwrite(&hdr, sizeof(hdr), 1, fndx);
+ }
+
+ fclose(fndx);
+ fclose(fgem);
+ rename(pgem, pool);
+ rename(pndx, pgem);
+ return;
+ }
+ }
+ }
+
+ fclose(fgem);
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ DIR *dirp;
+ struct dirent *de;
+ char *fname, fpath[80];
+
+ umask(077);
+ chdir(BBSHOME "/gem");
+
+ if (argc > 1)
+ {
+ flog = stderr;
+ sprintf(fpath, "brd/%s", argv[1]);
+ gindex(0, "", fpath, NULL);
+ exit(0);
+ }
+
+ if (!(flog = fopen(GINDEX_LOG, "w")))
+ exit(-1);
+
+ /* visit the top folder */
+
+ gindex(0, "", ".", NULL);
+
+ /* visit the second hierarchy for all boards */
+
+ strcpy(fpath, "brd");
+ if (!(dirp = opendir(fpath)))
+ {
+ fprintf(flog, "## unable to visit [gem/brd]\n");
+ fclose(flog);
+ exit(-1);
+ }
+
+ fpath[3] = '/';
+ while (de = readdir(dirp))
+ {
+ fname = de->d_name;
+ if (fname[1] && *fname != '.')
+ {
+ strcpy(fpath + 4, fname);
+ gindex(0, "", fpath, NULL);
+ }
+ }
+ closedir(dirp);
+ fclose(flog);
+ exit(0);
+}
diff --git a/util/give_paycheck.c b/util/give_paycheck.c
new file mode 100644
index 0000000..2c1d906
--- /dev/null
+++ b/util/give_paycheck.c
@@ -0,0 +1,91 @@
+/*-------------------------------------------------------*/
+/* util/give_paycheck.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : µoµ¹¥þ¯¸¨Ï¥ÎªÌ¤ä²¼ */
+/* create : 05/04/18 */
+/* update : / / */
+/* author : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#if 0
+
+ µ¹ itoc ¤@±i 100 »È 200 ª÷ ªº¤ä²¼
+ % ~/bin/give_paycheck 100 200 itoc
+
+ µ¹ sysop ¤@±i 100 »È 0 ª÷ ªº¤ä²¼
+ % ~/bin/give_paycheck 100 0 sysop
+
+ µ¹¥þ¯¸¨Ï¥ÎªÌ¦U¤@±i 50 »È 30 ª÷ ªº¤ä²¼
+ % ~/bin/give_paycheck 50 30
+
+ µ¹¥þ¯¸¨Ï¥ÎªÌ¤ä²¼­n°õ¦æ¤@¬q®É¶¡¡A½Ð­@¤ßµ¥­Ô
+
+#endif
+
+
+#include "bbs.h"
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int money, gold;
+ char c, *str, buf[64];
+ struct dirent *de;
+ DIR *dirp;
+ PAYCHECK paycheck;
+
+ if (argc == 3 || argc == 4)
+ {
+ money = atoi(argv[1]);
+ gold = atoi(argv[2]);
+ if (money < 0 || gold < 0)
+ printf("money and gold should not be negative.\n");
+ }
+ else
+ {
+ printf("Usage: %s money gold [userid]\n", argv[0]);
+ return -1;
+ }
+
+ memset(&paycheck, 0, sizeof(PAYCHECK));
+ time(&paycheck.tissue);
+ paycheck.money = money;
+ paycheck.gold = gold;
+ //strcpy(paycheck.reason, "[µo¿ú] ¯¸ªø");
+ strcpy(paycheck.reason, "¨Ä¨Ä¯¸¥Á¦³¬õ¥]");
+
+ if (argc == 4)
+ {
+ chdir(BBSHOME);
+ usr_fpath(buf, argv[3], FN_PAYCHECK);
+ rec_add(buf, &paycheck, sizeof(PAYCHECK));
+ return 0;
+ }
+
+ for (c = 'a'; c <= 'z'; c++)
+ {
+ sprintf(buf, BBSHOME "/usr/%c", c);
+ chdir(buf);
+
+ if (!(dirp = opendir(".")))
+ continue;
+
+ while (de = readdir(dirp))
+ {
+ str = de->d_name;
+ if (*str <= ' ' || *str == '.')
+ continue;
+
+ sprintf(buf, "%s/" FN_PAYCHECK, str);
+ rec_add(buf, &paycheck, sizeof(PAYCHECK));
+ }
+
+ closedir(dirp);
+ }
+
+ return 0;
+}
diff --git a/util/hdr-dump.c b/util/hdr-dump.c
new file mode 100644
index 0000000..544acd8
--- /dev/null
+++ b/util/hdr-dump.c
@@ -0,0 +1,43 @@
+/*-------------------------------------------------------*/
+/* util/hdr-dump.c ( NTHU CS MapleBBS Ver 2.36 ) */
+/*-------------------------------------------------------*/
+/* target : ¬ÝªO¼ÐÃDªí */
+/* create : 95/03/29 */
+/* update : 95/12/15 */
+/*-------------------------------------------------------*/
+/* Usage: hdr-dump .DIR */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int fd, count;
+ HDR hdr;
+
+ if (argc < 2)
+ {
+ printf("Usage:\t%s .DIR\n", argv[0]);
+ exit(1);
+ }
+
+ if ((fd = open(argv[1], O_RDONLY)) < 0)
+ {
+ printf("error open file\n");
+ exit(1);
+ }
+
+ count = 0;
+ while (read(fd, &hdr, sizeof(HDR)) == sizeof(HDR))
+ {
+ count++;
+ printf("%04d %c/%s %s (%s) %s %s\n", count, hdr.xname[7], hdr.xname, hdr.owner, hdr.nick, hdr.date, hdr.title);
+ }
+ close(fd);
+
+ exit(0);
+}
diff --git a/util/mailpost.c b/util/mailpost.c
new file mode 100644
index 0000000..5ee88a6
--- /dev/null
+++ b/util/mailpost.c
@@ -0,0 +1,202 @@
+/*-------------------------------------------------------*/
+/* util/mailpost.c ( NTHU CS MapleBBS Ver 2.36 ) */
+/*-------------------------------------------------------*/
+/* target : ¼f®Ö¨­¤À»{ÃÒ«H¨ç¤§¦^«H */
+/* create : 95/03/29 */
+/* update : 97/03/29 */
+/*-------------------------------------------------------*/
+/* notice : brdshm (board shared memory) synchronize */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+
+static void
+mailog(msg)
+ char *msg;
+{
+ FILE *fp;
+
+ if (fp = fopen(BMTA_LOGFILE, "a"))
+ {
+ time_t now;
+ struct tm *p;
+
+ time(&now);
+ p = localtime(&now);
+ fprintf(fp, "%02d/%02d %02d:%02d:%02d <mailpost> %s\n",
+ p->tm_mon + 1, p->tm_mday,
+ p->tm_hour, p->tm_min, p->tm_sec,
+ msg);
+ fclose(fp);
+ }
+}
+
+
+/* ----------------------------------------------------- */
+/* °O¿ýÅçÃÒ¸ê®Æ¡Guser ¦³¥i¯à¥¿¦b½u¤W¡A¬G¼g¤JÀÉ®×¥H«O©P¥þ */
+/* ----------------------------------------------------- */
+
+
+static int
+is_badid(userid)
+ char *userid;
+{
+ int ch;
+ char *str;
+
+ ch = strlen(userid);
+ if (ch < 2 || ch > IDLEN)
+ return 1;
+
+ if (!is_alpha(*userid))
+ return 1;
+
+ str = userid;
+ while (ch = *(++str))
+ {
+ if (!is_alnum(ch))
+ return 1;
+ }
+ return 0;
+}
+
+
+static void
+justify_user(userid, email)
+ char *userid, *email;
+{
+ char fpath[64];
+ HDR hdr;
+ FILE *fp;
+
+ /* ±H»{ÃÒ³q¹L«Hµ¹¨Ï¥ÎªÌ */
+ usr_fpath(fpath, userid, FN_DIR);
+ if (!hdr_stamp(fpath, HDR_LINK, &hdr, FN_ETC_JUSTIFIED))
+ {
+ strcpy(hdr.title, "±z¤w¸g³q¹L¨­¤À»{ÃÒ¤F¡I");
+ strcpy(hdr.owner, STR_SYSOP);
+ hdr.xmode = MAIL_NOREPLY;
+ rec_add(fpath, &hdr, sizeof(HDR));
+ }
+
+ /* °O¿ý¦b FN_JUSTIFY */
+ usr_fpath(fpath, userid, FN_JUSTIFY);
+ if (fp = fopen(fpath, "a"))
+ {
+ fprintf(fp, "RPY: %s\n", email);
+ fclose(fp);
+ }
+}
+
+
+static void
+verify_user(str)
+ char *str;
+{
+ int fd;
+ char *ptr, *next, fpath[64];
+ ACCT acct;
+
+ /* itoc.µù¸Ñ: "userid(regkey) [VALID]" */
+
+ if ((ptr = strchr(str, '(')) && (next = strchr(ptr + 1, ')')))
+ {
+ *ptr = '\0';
+ *next = '\0';
+
+ if (!is_badid(str) && !str_ncmp(next + 1, " [VALID]", 8))
+ {
+ /* ¨ì¦¹®æ¦¡³£¥¿½T */
+
+ usr_fpath(fpath, str, FN_ACCT);
+ if ((fd = open(fpath, O_RDWR, 0600)) >= 0)
+ {
+ if (read(fd, &acct, sizeof(ACCT)) == sizeof(ACCT))
+ {
+ if (str_hash(acct.email, acct.tvalid) == chrono32(ptr)) /* regkey ¥¿½T */
+ {
+ /* ´£¤ÉÅv­­ */
+ acct.userlevel |= PERM_VALID;
+ time(&acct.tvalid);
+ lseek(fd, (off_t) 0, SEEK_SET);
+ write(fd, &acct, sizeof(ACCT));
+
+ justify_user(str, acct.email);
+ }
+ }
+ close(fd);
+ }
+ }
+ }
+}
+
+
+/* ----------------------------------------------------- */
+/* ¥Dµ{¦¡ */
+/* ----------------------------------------------------- */
+
+
+static void
+mailpost()
+{
+ int count;
+ char *ptr, buf[512];
+
+ /* ¥u»Ý­n§ä Subject: ªºÀÉÀY */
+
+ count = 0;
+ while ((++count < 20) && fgets(buf, sizeof(buf), stdin)) /* ³Ì¦h fgets 20 ¦¸¡AÁ×§K¨S¦³ subject */
+ {
+ if (!str_ncmp(buf, "Subject: ", 9))
+ {
+ str_decode(buf);
+
+ /* itoc.µù¸Ñ: mail.c: TAG_VALID " userid(regkey) [VALID]" */
+ if (ptr = strstr(buf, TAG_VALID " "))
+ {
+ /* gslin.990101: TAG_VALID ªø«×¤£¤@©w */
+ verify_user(ptr + sizeof(TAG_VALID));
+ }
+ break;
+ }
+ }
+}
+
+
+static void
+sig_catch(sig)
+ int sig;
+{
+ char buf[512];
+
+ while (fgets(buf, sizeof(buf), stdin))
+ ;
+ sprintf(buf, "signal [%d]", sig);
+ mailog(buf);
+ exit(0);
+}
+
+
+int
+main()
+{
+ char buf[512];
+
+ setgid(BBSGID);
+ setuid(BBSUID);
+ chdir(BBSHOME);
+
+ signal(SIGBUS, sig_catch);
+ signal(SIGSEGV, sig_catch);
+ signal(SIGPIPE, sig_catch);
+
+ mailpost();
+
+ /* eat mail queue */
+ while (fgets(buf, sizeof(buf), stdin))
+ ;
+
+ exit(0);
+}
diff --git a/util/outgo.c b/util/outgo.c
new file mode 100644
index 0000000..862d2db
--- /dev/null
+++ b/util/outgo.c
@@ -0,0 +1,70 @@
+/*-------------------------------------------------------*/
+/* util/outgo.c ( YZU CSE WindTop BBS ) */
+/*-------------------------------------------------------*/
+/* target : ¦Û°Ê°e«Hµ{¦¡ */
+/* create : 00/06/22 */
+/* update : / / */
+/*-------------------------------------------------------*/
+/* syntax : outgo [board] [start] [end] */
+/* NOTICE : ±N¬ÝªO¤å³¹­«·s°e¨ì news */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+
+static inline void
+outgo_post(hdr, board)
+ HDR *hdr;
+ char *board;
+{
+ bntp_t bntp;
+
+ memset(&bntp, 0, sizeof(bntp_t));
+ bntp.chrono = hdr->chrono;
+ strcpy(bntp.board, board);
+ strcpy(bntp.xname, hdr->xname);
+ strcpy(bntp.owner, hdr->owner);
+ strcpy(bntp.nick, hdr->nick);
+ strcpy(bntp.title, hdr->title);
+ rec_add("innd/out.bntp", &bntp, sizeof(bntp_t));
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ char fpath[128];
+ int start, end, fd;
+ char *board;
+ HDR hdr;
+
+ if (argc > 3)
+ {
+ chdir(BBSHOME);
+
+ board = argv[1];
+ start = atoi(argv[2]);
+ end = atoi(argv[3]);
+ brd_fpath(fpath, board, FN_DIR);
+ if (fd = open(fpath, O_RDONLY))
+ {
+ lseek(fd, (off_t) ((start - 1) * sizeof(HDR)), SEEK_SET);
+ while (read(fd, &hdr, sizeof(HDR)) == sizeof(HDR) && start <= end)
+ {
+ if (!(hdr.xmode & POST_INCOME))
+ outgo_post(&hdr, board);
+ start++;
+ }
+ close(fd);
+ }
+ }
+ else
+ {
+ printf("usage: %s ¬ÝªO °_ÂI ²×ÂI\n", argv[0]);
+ }
+
+ return 0;
+}
diff --git a/util/poststat.c b/util/poststat.c
new file mode 100644
index 0000000..4fc1c2c
--- /dev/null
+++ b/util/poststat.c
@@ -0,0 +1,471 @@
+/*-------------------------------------------------------*/
+/* util/poststat.c ( NTHU CS MapleBBS Ver 3.00 ) */
+/*-------------------------------------------------------*/
+/* target : ²Î­p¤µ¤é¡B¶g¡B¤ë¡B¦~¼öªù¸ÜÃD */
+/* create : 95/03/29 */
+/* update : 97/08/29 */
+/*-------------------------------------------------------*/
+/* syntax : poststat [day] */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+
+static char *myfile[] = {"day", "week", "month", "year"};
+static int mycount[4] = {7, 4, 12};
+static int mytop[] = {10, 50, 100, 100};
+static char *mytitle[] = {"¤é¤Q", "¶g¤­¤Q", "¤ë¦Ê", "¦~«×¦Ê"};
+
+
+#define FN_RUN_POST_AUTHOR "run/var/post.author"
+#define FN_RUN_POST_OLD "run/var/post.old"
+#define FN_RUN_DAY_0 "run/var/day.0"
+
+
+#define HASHSIZE 1024 /* 2's power */
+#define TOPCOUNT 200
+#define LOWER_BOUND 4
+
+
+/* POSTLOG + next rec */
+static
+struct postrec
+{
+ char author[IDLEN + 1]; /* author name */
+ char board[BNLEN + 1]; /* board name */
+ char title[66]; /* title name */
+ time_t date; /* last post's date */
+ int number; /* post number */
+ struct postrec *next; /* next rec */
+} *bucket[HASHSIZE];
+
+
+static POSTLOG top[TOPCOUNT], *tp;
+
+
+static int
+hash(key)
+ char *key;
+{
+ int i, ch, value = 0;
+
+ for (i = 0; (ch = key[i]) && i < 80; i++)
+ {
+ value = (value << 5) - value + ch;
+ }
+
+ return value;
+}
+
+
+/* ---------------------------------- */
+/* hash structure : array + link list */
+/* ---------------------------------- */
+
+
+static void
+search(t)
+ POSTLOG *t;
+{
+ struct postrec *p, *q, *s;
+ int i, found = 0;
+
+ i = hash(t->title) & (HASHSIZE - 1);
+ q = NULL;
+ p = bucket[i];
+ while (p && (!found))
+ {
+ if (!strcmp(p->title, t->title) && !strcmp(p->board, t->board))
+ found = 1;
+ else
+ {
+ q = p;
+ p = p->next;
+ }
+ }
+ if (found)
+ {
+ p->number += t->number;
+ if (p->date < t->date) /* ¨ú¸ûªñ¤é´Á */
+ p->date = t->date;
+ }
+ else
+ {
+ s = (struct postrec *) malloc(sizeof(struct postrec));
+ memcpy(s, t, sizeof(POSTLOG));
+ s->next = NULL;
+ if (q == NULL)
+ bucket[i] = s;
+ else
+ q->next = s;
+ }
+}
+
+
+static int
+sort(pp, count)
+ struct postrec *pp;
+{
+ int i, j;
+
+ for (i = 0; i <= count; i++)
+ {
+ if (pp->number > top[i].number)
+ {
+ if (count < TOPCOUNT - 1)
+ count++;
+ for (j = count - 1; j >= i; j--)
+ memcpy(&top[j + 1], &top[j], sizeof(POSTLOG));
+
+ memcpy(&top[i], pp, sizeof(POSTLOG));
+ break;
+ }
+ }
+ return count;
+}
+
+
+static void
+load_stat(fname)
+ char *fname;
+{
+ FILE *fp;
+
+ if (fp = fopen(fname, "r"))
+ {
+ int count = fread(top, sizeof(POSTLOG), TOPCOUNT, fp);
+ fclose(fp);
+ while (count)
+ search(&top[--count]);
+ }
+}
+
+
+static void
+poststat(mytype)
+ int mytype;
+{
+ FILE *fp;
+ char buf[40], *p;
+ char curfile[80] = FN_RUN_DAY_0;
+ struct postrec *pp;
+ int i, j;
+
+ if (mytype < 0)
+ {
+ FILE *fpw;
+
+ /* --------------------------------------- */
+ /* load .post and statictic processing */
+ /* --------------------------------------- */
+
+ unlink(FN_RUN_POST_OLD);
+ rename(FN_RUN_POST, FN_RUN_POST_OLD);
+ if (!(fp = fopen(FN_RUN_POST_OLD, "r")))
+ return;
+
+ if (!(fpw = fopen(FN_RUN_POST_AUTHOR, "a")))
+ {
+ fclose(fp);
+ return;
+ }
+
+ mytype = 0;
+ load_stat(curfile);
+
+ while (fread(top, sizeof(POSTLOG), 1, fp) == 1)
+ {
+ fwrite(top, sizeof(POSTLOG), 1, fpw);
+ search(top);
+ }
+ fclose(fp);
+ fclose(fpw);
+ }
+ else
+ {
+ /* ---------------------------------------------- */
+ /* load previous results and statictic processing */
+ /* ---------------------------------------------- */
+
+ i = mycount[mytype];
+ p = myfile[mytype];
+ while (i)
+ {
+ sprintf(buf, "run/var/%s.%d", p, i);
+ sprintf(curfile, "run/var/%s.%d", p, --i);
+ load_stat(curfile);
+ rename(curfile, buf);
+ }
+ mytype++;
+ }
+
+ /* ---------------------------------------------- */
+ /* sort top 100 issue and save results */
+ /* ---------------------------------------------- */
+
+ memset(top, 0, sizeof(top));
+ for (i = j = 0; i < HASHSIZE; i++)
+ {
+ for (pp = bucket[i]; pp; pp = pp->next)
+ {
+
+#ifdef DEBUG
+ printf("Title : %s, Board: %s\nPostNo : %d, Author: %s\n",
+ pp->title, pp->board, pp->number, pp->author);
+#endif
+
+ j = sort(pp, j);
+ }
+ }
+
+ p = myfile[mytype];
+ sprintf(curfile, "run/var/%s.0", p);
+ if (fp = fopen(curfile, "w"))
+ {
+ fwrite(top, sizeof(POSTLOG), j, fp);
+ fclose(fp);
+ }
+
+ /* --------------------------------------------------- */
+ /* report file : gem/@/@- */
+ /* --------------------------------------------------- */
+
+ sprintf(curfile, BBSHOME "/gem/@/@-%s", p);
+ if (fp = fopen(curfile, "w"))
+ {
+ int max, cnt;
+
+ fprintf(fp, "\t\t\033[1;34m-----\033[37m=====\033[41m ¥»%s¤j¼öªù¸ÜÃD \033[40m=====\033[34m-----\033[0m\n\n",
+ mytitle[mytype]);
+
+ max = mytop[mytype];
+ p = buf + 5;
+ for (i = cnt = 0; (cnt < max) && (i < j); i++)
+ {
+ tp = &top[i];
+ strcpy(buf, Btime(&(tp->date)));
+ buf[23] = '\0';
+ fprintf(fp,
+ "\033[1;31m%3d. \033[33m¬ÝªO : \033[32m%-16s\033[35m¡m%s¡n\033[36m%4d ½g\033[33m%+16s\n"
+ " \033[33m¼ÐÃD : \033[0;44;37m%-60.60s\033[40m\n",
+ ++cnt, tp->board, p, tp->number, tp->author, tp->title);
+ }
+ fclose(fp);
+ }
+
+ /* free statistics */
+
+ for (i = 0; i < HASHSIZE; i++)
+ {
+ struct postrec *next;
+
+ for (pp = bucket[i]; pp; pp = next)
+ {
+ next = pp->next;
+ free(pp);
+ }
+ bucket[i] = NULL;
+ }
+}
+
+
+#include "splay.h"
+
+
+typedef struct PostText
+{
+ struct PostText *ptnext;
+ int count;
+ char title[0];
+} PostText;
+
+
+typedef struct PostAuthor
+{
+ struct PostAuthor *panext;
+ PostText *text;
+ int count;
+ int hash;
+ char author[0];
+} PostAuthor;
+
+
+static int
+pa_cmp(x, y)
+ PostAuthor *x;
+ PostAuthor *y;
+{
+ int dif;
+
+ dif = y->count - x->count;
+ if (dif)
+ return dif;
+ return strcmp(x->author, y->author);
+}
+
+
+static void
+pa_out(top, fp)
+ SplayNode *top;
+ FILE *fp;
+{
+ PostAuthor *pa;
+ PostText *text;
+
+ if (top == NULL)
+ return;
+
+ pa_out(top->left, fp);
+
+ pa = (PostAuthor *) top->data;
+ if (pa->count <= LOWER_BOUND)
+ return;
+
+ fprintf(fp, "\n>%6d %s\n", pa->count, pa->author);
+ for (text = pa->text; text; text = text->ptnext)
+ {
+ fprintf(fp, "%7d %.70s\n", text->count, text->title);
+ }
+
+ pa_out(top->right, fp);
+}
+
+
+static void
+post_author()
+{
+ int cc, i, len;
+ char *str;
+ FILE *fp;
+ POSTLOG post;
+ PostAuthor **paht, *pahe;
+ PostText *text;
+ SplayNode *patop;
+
+ unlink(FN_RUN_POST_OLD);
+ rename(FN_RUN_POST_AUTHOR, FN_RUN_POST_OLD);
+ if (!(fp = fopen(FN_RUN_POST_OLD, "r")))
+ return;
+
+ paht = (PostAuthor **) calloc(sizeof(PostAuthor *), HASHSIZE);
+
+ while (fread(&post, sizeof(post), 1, fp) == 1)
+ {
+ cc = hash(str = post.author);
+ pahe = paht[i = cc & (HASHSIZE - 1)];
+
+ for (;;)
+ {
+ if (pahe == NULL)
+ {
+ len = strlen(str) + 1;
+ pahe = (PostAuthor *) malloc(sizeof(PostAuthor) + len);
+ pahe->panext = paht[i];
+ pahe->text = NULL;
+ pahe->count = 1;
+ pahe->hash = cc;
+ memcpy(pahe->author, str, len);
+ paht[i] = pahe;
+ break;
+ }
+
+ if ((cc == pahe->hash) && !strcmp(str, pahe->author))
+ {
+ pahe->count++;
+ break;
+ }
+
+ pahe = pahe->panext;
+ }
+
+ text = pahe->text;
+ str = post.title;
+ for (;;)
+ {
+ if (text == NULL)
+ {
+ len = strlen(str) + 1;
+ text = (PostText *) malloc(sizeof(PostText) + len + BNLEN + 1);
+ text->ptnext = pahe->text;
+ text->count = 1;
+ sprintf(text->title, "%-13s%s", post.board, str);
+ /* memcpy(text->title, str, len); */
+ pahe->text = text;
+ break;
+ }
+
+ if (!strcmp(str, text->title + BNLEN + 1))
+ {
+ text->count ++;
+ break;
+ }
+
+ text = text->ptnext;
+ }
+ }
+ fclose(fp);
+
+ /* splay sort */
+
+ patop = NULL;
+
+ for (i = 0; i < HASHSIZE; i++)
+ {
+ for (pahe = paht[i]; pahe; pahe = pahe->panext)
+ {
+ patop = splay_in(patop, pahe, pa_cmp);
+ }
+ }
+
+ /* report */
+
+ if (fp = fopen(FN_RUN_POST_LOG, "w"))
+ {
+ pa_out(patop, fp);
+ fclose(fp);
+ }
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ time_t now;
+ struct tm *ptime;
+
+ chdir(BBSHOME);
+ umask(077);
+
+ if (argc == 2)
+ {
+ argc = atoi(argv[1]);
+ if (argc == 100)
+ post_author();
+ else
+ poststat(argc);
+ exit(0);
+ }
+
+ time(&now);
+ ptime = localtime(&now);
+ argc = ptime->tm_hour;
+
+ if (argc == 0)
+ {
+ if (ptime->tm_mday == 1)
+ poststat(2);
+ if (ptime->tm_wday == 0)
+ poststat(1);
+ poststat(0);
+ }
+
+ poststat(-1);
+
+ if (argc == 23)
+ post_author();
+
+ exit(0);
+}
diff --git a/util/reaper.c b/util/reaper.c
new file mode 100644
index 0000000..f4bf4c9
--- /dev/null
+++ b/util/reaper.c
@@ -0,0 +1,647 @@
+/*-------------------------------------------------------*/
+/* util/reaper.c ( NTHU CS MapleBBS Ver 3.00 ) */
+/*-------------------------------------------------------*/
+/* target : ¨Ï¥ÎªÌ±b¸¹©w´Á²M²z */
+/* create : 95/03/29 */
+/* update : 97/03/29 */
+/*-------------------------------------------------------*/
+/* syntax : reaper */
+/*-------------------------------------------------------*/
+/* notice : ~bbs/usr/@/ - expired users's data */
+/* run/reaper - list of expired users */
+/* run/manager - list of managers */
+/* run/lazybm - list of lazy bm */
+/* run/emailaddr - list of same email addr */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+
+/* Thor.980930: ­Y­nÃö³¬¥H¤U¥\¯à®É¥Î undef §Y¥i */
+
+#define CHECK_LAZYBM /* Àˬd°½ÃiªO¥D */
+#define EADDR_GROUPING /* Àˬd¦@¥Î email */
+
+
+#ifdef CHECK_LAZYBM
+#define DAY_LAZYBM 7 /* 7 ¤Ñ¥H¤W¥¼¤W¯¸ªºªO¥D§Y°O¿ý */
+#endif
+
+#ifdef EADDR_GROUPING
+#define EMAIL_REG_LIMIT 3 /* 3 ­Ó¥H¤W¨Ï¥ÎªÌ¥Î¦P¤@ email §Y°O¿ý */
+#endif
+
+
+/* «O¯d±b¸¹´Á­­ -- ¾Ç´Á¤¤ */
+#define DAY_NEWUSR 7 /* µn¤J¤£¶W¹L¤T¦¸ªº¨Ï¥ÎªÌ«O¯d 7 ¤Ñ */
+#define DAY_FORFUN 120 /* ¥¼§¹¦¨¨­¤À»{ÃÒªº¨Ï¥ÎªÌ«O¯d 120 ¤Ñ */
+#define DAY_OCCUPY 120 /* ¤w§¹¦¨¨­¤À»{ÃÒªº¨Ï¥ÎªÌ«O¯d 120 ¤Ñ */
+
+/* «O¯d±b¸¹´Á­­ -- ´»°² */
+#define VAC_NEWUSR 7 /* µn¤J¤£¶W¹L¤T¦¸ªº¨Ï¥ÎªÌ«O¯d 7 ¤Ñ */
+#define VAC_FORFUN 180 /* ¥¼§¹¦¨¨­¤À»{ÃÒªº¨Ï¥ÎªÌ«O¯d 180 ¤Ñ */
+#define VAC_OCCUPY 180 /* ¤w§¹¦¨¨­¤À»{ÃÒªº¨Ï¥ÎªÌ«O¯d 180 ¤Ñ */
+
+
+static time_t due_newusr;
+static time_t due_forfun;
+static time_t due_occupy;
+
+
+static int visit = 0; /* Á` ID ªº¼Æ¥Ø */
+static int prune = 0; /* ³Q²M°£IDªº¼Æ¥Ø */
+static int manager = 0; /* ºÞ²zªÌªº¼Æ¥Ø */
+static int invalid = 0; /* ¥¼»{ÃÒ³q¹LIDªº¼Æ¥Ø */
+
+
+static FILE *flog;
+static FILE *flst;
+
+#ifdef CHECK_LAZYBM
+static time_t due_lazybm;
+static int lazybm = 0; /* °½ÃiªO¥Dªº¼Æ¥Ø */
+static FILE *fbm;
+#endif
+
+#ifdef EADDR_GROUPING
+static FILE *faddr;
+#endif
+
+
+/* ----------------------------------------------------- */
+/* ²MªÅ .USR ¸Ì­±ªºÄæ¦ì */
+/* ----------------------------------------------------- */
+
+
+static int funo;
+static int max_uno;
+
+
+static void
+userno_free(uno)
+ int uno;
+{
+ off_t off;
+ int fd;
+ static SCHEMA schema; /* itoc.031216.µù¸Ñ: ¥Î static ¬O¦]¬° schema.userid ­n¤@ª½³Q²M¦¨¥þ¹s */
+
+ fd = funo;
+
+ /* flock(fd, LOCK_EX); */
+ /* Thor.981205: ¥Î fcntl ¨ú¥Nflock, POSIX¼Ð·Ç¥Îªk */
+ f_exlock(fd);
+
+ time(&schema.uptime);
+ off = (uno - 1) * sizeof(SCHEMA);
+ if (lseek(fd, off, SEEK_SET) < 0)
+ exit(2);
+ if (write(fd, &schema, sizeof(SCHEMA)) != sizeof(SCHEMA))
+ exit(2);
+
+ /* flock(fd, LOCK_UN); */
+ /* Thor.981205: ¥Î fcntl ¨ú¥Nflock, POSIX¼Ð·Ç¥Îªk */
+ f_unlock(fd);
+}
+
+
+/* ----------------------------------------------------- */
+/* Åã¥Ü¥Î¨ç¦¡ */
+/* ----------------------------------------------------- */
+
+
+static void
+levelmsg(str, level)
+ char *str;
+ int level;
+{
+ static char perm[] = STR_PERM;
+ int len = 32;
+ char *p = perm;
+
+ do
+ {
+ *str = (level & 1) ? *p : '-';
+ p++;
+ str++;
+ level >>= 1;
+ } while (--len);
+ *str = '\0';
+}
+
+
+static void
+datemsg(str, chrono)
+ char *str;
+ time_t *chrono;
+{
+ struct tm *t;
+
+ t = localtime(chrono);
+ /* Thor.990329: y2k */
+ sprintf(str, "%02d/%02d/%02d%3d:%02d:%02d ",
+ t->tm_year % 100, t->tm_mon + 1, t->tm_mday,
+ t->tm_hour, t->tm_min, t->tm_sec);
+}
+
+
+/*-------------------------------------------------------*/
+/* ¬ÝªOÅv­­³¡¤À¶·»P board.c ¬Û®e */
+/*-------------------------------------------------------*/
+
+
+static BCACHE *bshm;
+
+
+static void
+init_bshm()
+{
+ /* itoc.030727: ¦b¶}±Ò bbsd ¤§«e¡AÀ³¸Ó´N­n°õ¦æ¹L account¡A
+ ©Ò¥H bshm À³¸Ó¤w³]©w¦n */
+
+ bshm = shm_new(BRDSHM_KEY, sizeof(BCACHE));
+
+ if (bshm->uptime <= 0) /* bshm ¥¼³]©w§¹¦¨ */
+ exit(0);
+}
+
+
+static int num_bm = 0;
+static char all_bm[MAXBOARD * BMLEN / 3][IDLEN + 1]; /* ¨CªO¦Ü¦h¦³ (BMLEN / 3) ­ÓªO¥D */
+
+
+static int
+bm_cmp(a, b) /* ¨ä¹ê´N¬O str_cmp */
+ char *a, *b;
+{
+ return str_cmp(a, b);
+}
+
+
+static char * /* 1: ¦b¥þ¯¸ªO¥D¦W³æ¤º */
+check_allBM(userid)
+ char *userid; /* lower case userid */
+{
+ return bsearch(userid, all_bm, num_bm, IDLEN + 1, bm_cmp);
+}
+
+
+static void
+collect_allBM()
+{
+ BRD *head, *tail;
+ char *ptr, *str, buf[BMLEN + 1];
+
+ /* ¥h bshm ¤¤§ì¥X©Ò¦³ brd->BM */
+
+ head = bshm->bcache;
+ tail = head + bshm->number;
+ do /* ¦Ü¤Ö¦³ note ¤@ªO¡A¤£¥²¹ï¬ÝªO°µÀˬd */
+ {
+ ptr = buf;
+ strcpy(ptr, head->BM);
+
+ while (*ptr) /* §â brd->BM ¤¤ bm1/bm2/bm3/... ¦U­Ó bm §ì¥X¨Ó */
+ {
+ if (str = strchr(ptr, '/'))
+ *str = '\0';
+ if (!check_allBM(ptr))
+ {
+ strcpy(all_bm[num_bm], ptr);
+ num_bm++;
+ qsort(all_bm, num_bm, IDLEN + 1, bm_cmp);
+ }
+ if (!str)
+ break;
+ ptr = str + 1;
+ }
+ } while (++head < tail);
+}
+
+
+/* ----------------------------------------------------- */
+/* Àˬd¦@¥Î email */
+/* ----------------------------------------------------- */
+
+#ifdef EADDR_GROUPING
+/*
+ Thor.980930: ±N¦P¤@email addrªºaccount, ¦¬¶°°_¨Ó¨Ã¦Cªí ¤u§@­ì²z:
+ 1. _hash() ±N email addr ¼Æ­È¤Æ
+ 2.¥Îbinary search, §ä¨ì«happend userno, §ä¤£¨ì«h insert ·sentry
+ 3.±N userno list ¾ã²z¦C¥X
+
+ ¸ê®Æµ²ºc: chain: int hash, int link_userno plist: int next_userno
+
+ ¼È®É¹w¦ôemail addrÁ`¼Æ¤£¶W¹L100000,
+ ¼È®É¹w¦ôuserÁ`¼Æ¤£¶W¹L 100000
+ */
+
+typedef struct
+{
+ int hash;
+ int link;
+} Chain;
+
+static Chain *chain;
+static int *plist;
+static int numC;
+
+static void
+eaddr_group(userno, eaddr)
+ int userno;
+ char *eaddr;
+{
+ int left, right, mid, i;
+ int hash = str_hash(eaddr, 0);
+
+ left = 0;
+ right = numC - 1;
+ for (;;)
+ {
+ int cmp;
+ Chain *cptr;
+
+ if (left > right) /* Thor.980930: §ä¨S */
+ {
+ for (i = numC; i > left; i--)
+ chain[i] = chain[i - 1];
+
+ cptr = &chain[left];
+ cptr->hash = hash;
+ cptr->link = userno;
+ plist[userno] = 0; /* Thor: tail */
+ numC++;
+ break;
+ }
+
+ mid = (left + right) >> 1;
+ cptr = &chain[mid];
+ cmp = hash - cptr->hash;
+
+ if (!cmp)
+ {
+ plist[userno] = cptr->link;
+ cptr->link = userno;
+ break;
+ }
+
+ if (cmp < 0)
+ right = mid - 1;
+ else
+ left = mid + 1;
+ }
+}
+
+
+static void
+report_eaddr_group()
+{
+ int i, j, cnt;
+ off_t off;
+ int fd;
+ SCHEMA s;
+
+ fprintf(faddr, "Email registration over %d times list\n\n", EMAIL_REG_LIMIT);
+
+ for (i = 0; i < numC; i++)
+ {
+ for (cnt = 0, j = chain[i].link; j; cnt++, j = plist[j])
+ ;
+
+ if (cnt > EMAIL_REG_LIMIT)
+ {
+ fprintf(faddr, "\n> %d\n", chain[i].hash);
+ for (j = chain[i].link; j; j = plist[j])
+ {
+ off = (j - 1) * sizeof(SCHEMA);
+ if (lseek(funo, off, SEEK_SET) < 0)
+ {
+ fprintf(faddr, "==> %d) can't lseek\n", j);
+ continue;
+ }
+ if (read(funo, &s, sizeof(SCHEMA)) != sizeof(SCHEMA))
+ {
+ fprintf(faddr, "==> %d) can't read\n", j);
+ continue;
+ }
+ else
+ {
+ ACCT acct;
+ char buf[256];
+
+ if (s.userid[0] <= ' ')
+ {
+ fprintf(faddr, "==> %d) has been reapered\n", j);
+ continue;
+ }
+ usr_fpath(buf, s.userid, FN_ACCT);
+ fd = open(buf, O_RDONLY);
+ if (fd < 0)
+ {
+ fprintf(faddr, "==> %d)%-13s can't open\n", j, s.userid);
+ continue;
+ }
+ if (read(fd, &acct, sizeof(ACCT)) != sizeof(ACCT))
+ {
+ fprintf(faddr, "==> %d)%-13s can't read\n", j, s.userid);
+ continue;
+ }
+ close(fd);
+
+ datemsg(buf, &acct.lastlogin);
+ fprintf(faddr, "%5d) %-13s%s[%d]\t%s\n", acct.userno, acct.userid, buf, acct.numlogins, acct.email);
+ }
+ }
+ }
+ }
+}
+#endif
+
+
+/* ----------------------------------------------------- */
+/* ¥Dµ{¦¡ */
+/* ----------------------------------------------------- */
+
+
+static void
+reaper(fpath, lowid)
+ char *fpath;
+ char *lowid;
+{
+ int fd, login, userno;
+ usint ulevel;
+ time_t life;
+ char buf[256], data[40];
+ ACCT acct;
+
+ sprintf(buf, "%s/" FN_ACCT, fpath);
+ fd = open(buf, O_RDWR);
+ if (fd < 0)
+ { /* Thor.981001: ¥[¨Ç log */
+ fprintf(flog, "acct can't open %-13s ==> %s\n", lowid, buf);
+ return;
+ }
+
+ if (read(fd, &acct, sizeof(ACCT)) != sizeof(ACCT))
+ {
+ fprintf(flog, "acct can't read %-13s ==> %s\n", lowid, buf);
+ close(fd);
+ return;
+ }
+
+ ulevel = acct.userlevel;
+
+ /* ¦³ PERM_BM ¦ý¤£¦b¥þ¯¸ªO¥D¦W³æ¤º¡A²¾°£¨ä PERM_BM */
+ if ((ulevel & PERM_BM) && !check_allBM(lowid))
+ {
+ ulevel ^= PERM_BM;
+ acct.userlevel = ulevel;
+ lseek(fd, 0, SEEK_SET);
+ write(fd, &acct, sizeof(ACCT));
+ }
+
+ close(fd);
+
+ userno = acct.userno;
+
+ if ((userno <= 0) || (userno > max_uno))
+ {
+ fprintf(flog, "%5d) %-13s ==> %s\n", userno, acct.userid, buf);
+ return;
+ }
+
+ life = acct.lastlogin; /* ¥²¤£¬° 0 */
+ login = acct.numlogins;
+
+#ifdef EADDR_GROUPING
+ if (ulevel & PERM_VALID) /* Thor.980930: ¥u¬Ý³q¹L»{ÃÒªº email, ¥þºâ */
+ eaddr_group(userno, acct.email);
+#endif
+
+ if (ulevel & (PERM_XEMPT | PERM_BM | PERM_ALLADMIN)) /* ¦³³o¨ÇÅv­­ªÌ¤£¬å */
+ {
+ datemsg(buf, &acct.lastlogin);
+ levelmsg(data, ulevel);
+ fprintf(flst, "%5d) %-13s%s[%s] %d\n", userno, acct.userid, buf, data, login);
+ manager++;
+
+#ifdef CHECK_LAZYBM
+ if ((ulevel & PERM_BM) && life < due_lazybm)
+ {
+ fprintf(fbm, "%5d) %-13s%s %d\n", userno, acct.userid, buf, login);
+ lazybm++;
+ }
+#endif
+ }
+ else if (ulevel) /* guest.ulevel == 0, ¥Ã»·«O¯d */
+ {
+ if (ulevel & PERM_PURGE) /* lkchu.990221: ¡u²M°£±b¸¹¡v */
+ {
+ life = 0;
+ }
+ else if (ulevel & PERM_DENYLOGIN) /* itoc.010927: ¦³¡u¸T¤î¤W¯¸¡vªº¤£¬å¡A¦ý­Y¦³¡u²M°£±b¸¹¡vÅv­­·Ó¬å */
+ {
+ /* life = 1; */ /* ¤£»Ý­n¡A«e­±¦³¤F */
+ }
+ else if (ulevel & PERM_VALID)
+ {
+ if (life < due_occupy)
+ life = 0;
+ }
+ else
+ {
+ if (login <= 3 && life < due_newusr)
+ life = 0;
+ else if (life < due_forfun)
+ life = 0;
+ else
+ invalid++;
+ }
+
+ if (!life)
+ {
+ sprintf(buf, "usr/@/%s", lowid);
+
+ while (rename(fpath, buf))
+ {
+ extern int errno;
+
+ fprintf(flog, "rename %s ==> %s : %d\n", fpath, buf, errno);
+ sprintf(buf, "usr/@/%s.%d", lowid, ++life);
+ }
+
+ userno_free(userno);
+ datemsg(buf, &acct.lastlogin);
+ fprintf(flog, "%5d) %-13s%s%d\n", userno, acct.userid, buf, login);
+ prune++;
+ }
+ }
+
+ visit++;
+}
+
+
+static void
+traverse(fpath)
+ char *fpath;
+{
+ DIR *dirp;
+ struct dirent *de;
+ char *fname, *str;
+
+ /* visit the second hierarchy */
+
+ if (!(dirp = opendir(fpath)))
+ {
+ fprintf(flog, "## unable to enter hierarchy [%s]\n", fpath);
+ return;
+ }
+
+ for (str = fpath; *str; str++);
+ *str++ = '/';
+
+ while (de = readdir(dirp))
+ {
+ fname = de->d_name;
+ if (fname[0] > ' ' && fname[0] != '.')
+ {
+ strcpy(str, fname);
+ reaper(fpath, fname);
+ }
+ }
+ closedir(dirp);
+}
+
+
+int
+main()
+{
+ int ch;
+ time_t start, end;
+ struct tm *ptime;
+ struct stat st;
+ char *fname, fpath[256];
+
+ setuid(BBSUID);
+ setgid(BBSGID);
+ chdir(BBSHOME);
+ umask(077);
+
+ flog = fopen(FN_RUN_REAPER, "w");
+ if (flog == NULL)
+ exit(1);
+
+ flst = fopen(FN_RUN_MANAGER, "w");
+ if (flst == NULL)
+ exit(1);
+
+#ifdef CHECK_LAZYBM
+ fbm = fopen(FN_RUN_LAZYBM, "w");
+ if (fbm == NULL)
+ exit(1);
+#endif
+
+#ifdef EADDR_GROUPING
+ faddr = fopen(FN_RUN_EMAILADDR, "w");
+ if (faddr == NULL)
+ exit(1);
+#endif
+
+#ifdef EADDR_GROUPING
+ funo = open(FN_SCHEMA, O_RDWR | O_CREAT, 0600); /* Thor.980930: for read name */
+#else
+ funo = open(FN_SCHEMA, O_WRONLY | O_CREAT, 0600);
+#endif
+
+ if (funo < 0)
+ exit(1);
+
+ /* °²³]²M°£±b¸¹´Á¶¡¡A·sµù¥U¤H¼Æ¤£·|¶W¹L 300 ¤H */
+
+ fstat(funo, &st);
+ max_uno = st.st_size / sizeof(SCHEMA) + 300;
+
+ init_bshm();
+ collect_allBM();
+
+ time(&start);
+ ptime = localtime(&start);
+
+ /* itoc.011002.µù¸Ñ: ¤£¯à¦b¤@¶}¾Ç´N°¨¤W apply ÄY®æªº®É¶¡­­¨î¡A
+ §_«h«Ü¦h user ·|¦]¬°¾ã­Ó´»°²¨S¦³¤W¯¸¡A¦b¤@¶}¾Ç´N³Q reaper ±¼ */
+
+ if ((ptime->tm_mon >= 6 && ptime->tm_mon <= 8) || /* 7 ¤ë¨ì 9 ¤ë¬O´»°² */
+ (ptime->tm_mon >= 1 && ptime->tm_mon <= 2)) /* 2 ¤ë¨ì 3 ¤ë¬O´H°² */
+ {
+ due_newusr = start - VAC_NEWUSR * 86400;
+ due_forfun = start - VAC_FORFUN * 86400;
+ due_occupy = start - VAC_OCCUPY * 86400;
+ }
+ else
+ {
+ due_newusr = start - DAY_NEWUSR * 86400;
+ due_forfun = start - DAY_FORFUN * 86400;
+ due_occupy = start - DAY_OCCUPY * 86400;
+ }
+
+#ifdef CHECK_LAZYBM
+ due_lazybm = start - DAY_LAZYBM * 86400;
+#endif
+
+#ifdef EADDR_GROUPING
+ chain = (Chain *) malloc(max_uno * sizeof(Chain));
+ plist = (int *)malloc((max_uno + 1) * sizeof(int));
+ if (!chain || !plist)
+ {
+ fprintf(faddr, "out of memory....\n");
+ exit(1);
+ }
+#endif
+
+ strcpy(fname = fpath, "usr/@");
+ mkdir(fname, 0700);
+ fname = (char *)strchr(fname, '@');
+
+ /* visit the first hierarchy */
+
+ for (ch = 'a'; ch <= 'z'; ch++)
+ {
+ fname[0] = ch;
+ fname[1] = '\0';
+ traverse(fpath);
+ }
+
+#ifdef EADDR_GROUPING
+ report_eaddr_group(); /* Thor.980930: before close funo */
+#endif
+
+ close(funo);
+
+ fprintf(flst, "\nManager: %d\n", manager);
+ fclose(flst);
+
+ time(&end);
+ fprintf(flog, "# ¶}©l®É¶¡¡G%s\n", Btime(&start));
+ fprintf(flog, "# µ²§ô®É¶¡¡G%s\n", Btime(&end));
+ end -= start;
+ start = end % 60;
+ end /= 60;
+ fprintf(flog, "# Á`­p¯Ó®É¡G%d:%d:%d\n", end / 60, end % 60, start);
+ fprintf(flog, "# µù¥U¤H¼Æ¡G%d\n", visit); /* ¥¼²M°£«eªºÁ`¼Æ */
+ fprintf(flog, "# ²M°£¤H¼Æ¡G%d\n", prune);
+ fprintf(flog, "# ¥¼»{ÃҼơG%d\n", invalid);
+ fclose(flog);
+
+#ifdef CHECK_LAZYBM
+ fprintf(fbm, "\nLazy BM for %d days: %d\n", DAY_LAZYBM, lazybm);
+ fclose(fbm);
+#endif
+
+#ifdef EADDR_GROUPING
+ free(chain);
+ free(plist);
+ fclose(faddr);
+#endif
+
+ exit(0);
+}
diff --git a/util/redir.c b/util/redir.c
new file mode 100644
index 0000000..33f5de7
--- /dev/null
+++ b/util/redir.c
@@ -0,0 +1,358 @@
+/*-------------------------------------------------------*/
+/* util/redir.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : ¦Û°Ê­««Ø.DIRµ{¦¡ */
+/* create : 99/10/07 */
+/* update : 04/11/29 */
+/* author : Thor.bbs@bbs.cs.nthu.edu.tw */
+/* modify : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+/* input : scan current directory */
+/* output : generate .DIR.re */
+/*-------------------------------------------------------*/
+
+
+#if 0
+
+ ¦¹µ{¦¡¥i¥H­««Ø¬ÝªO¡BºëµØ°Ï¡B«H½cªº .DIR
+ ·| scan current directory ¨Ó²£¥Í .DIR.re
+ ¥H chrono ¨Ó±Æ§Ç
+
+#endif
+
+
+#include "bbs.h"
+
+
+#define FNAME_DB_SIZE 2048
+
+
+typedef char FNAME[9];
+static FNAME *n_pool;
+static int n_size, n_head;
+
+
+static int
+pool_add(fname)
+ FNAME fname;
+{
+ char *p;
+
+ /* initial pool */
+ if (!n_pool)
+ {
+ n_pool = (FNAME *) malloc(FNAME_DB_SIZE * sizeof(FNAME));
+ n_size = FNAME_DB_SIZE;
+ n_head = 0;
+ }
+
+ if (n_head >= n_size)
+ {
+ n_size += (n_size >> 1);
+ n_pool = (FNAME *) realloc(n_pool, n_size * sizeof(FNAME));
+ }
+
+ p = n_pool[n_head];
+
+ if (strlen(fname) != 8)
+ return -1;
+
+ strcpy(p, fname);
+
+ n_head++;
+
+ return 0;
+}
+
+
+static int type; /* 'b':¬ÝªO 'g':ºëµØ°Ï 'm':«H½c */
+
+
+static HDR *
+article_parse(fname)
+ char *fname;
+{
+ char buf[ANSILINELEN], *ptr1, *ptr2, *ptr3;
+ FILE *fp;
+ static HDR hdr;
+
+ memset(&hdr, 0, sizeof(HDR));
+
+ /* fill in chrono/date/xmode/xid/xname */
+ hdr.chrono = chrono32(fname);
+ str_stamp(hdr.date, &hdr.chrono);
+ strcpy(hdr.xname, fname);
+ if (type == 'm')
+ hdr.xmode = MAIL_READ;
+
+ if (*fname == 'F') /* ¦pªG¬O¨÷©v¡A¥Ñ©ó¨S¦³¨ä¥L¸ê°T¤F¡A©Ò¥H¥u¯àÀH«Kµ¹ */
+ {
+ hdr.xmode = GEM_FOLDER;
+ strcpy(hdr.owner, STR_SYSOP);
+ strcpy(hdr.nick, SYSOPNICK);
+ strcpy(hdr.title, "·É®ü¬B¿ò");
+ return &hdr;
+ }
+
+ sprintf(buf, "%c/%s", (type == 'm') ? '@' : fname[7], fname);
+ if (fp = fopen(buf, "r"))
+ {
+ if (fgets(buf, sizeof(buf), fp))
+ {
+ if (ptr1 = strchr(buf, '\n'))
+ ptr1 = '\0';
+
+ if (!strncmp(buf, STR_AUTHOR1 " ", LEN_AUTHOR1 + 1))
+ ptr1 = buf + LEN_AUTHOR1 + 1;
+ else if (!strncmp(buf, STR_AUTHOR2 " ", LEN_AUTHOR2 + 1))
+ ptr1 = buf + LEN_AUTHOR2 + 1;
+ else
+ ptr1 = NULL;
+
+ if (ptr1 && *ptr1)
+ {
+ ptr2 = strchr(ptr1 + 1, '@');
+ ptr3 = strchr(ptr1 + 1, '(');
+
+ if (ptr2 && (!ptr3 || ptr2 < ptr3)) /* ¦b¼ÊºÙ¸Ì­±ªº @ ¤£ºâ¬O email */
+ {
+ str_from(ptr1, hdr.owner, hdr.nick);
+ hdr.xmode |= POST_INCOME; /* also MAIL_INCOME */
+ }
+ else if (ptr3)
+ {
+ ptr3[-1] = '\0';
+ str_ncpy(hdr.owner, ptr1, sizeof(hdr.owner));
+ if (ptr2 = strchr(ptr3 + 1, ')'))
+ {
+ *ptr2 = '\0';
+ str_ncpy(hdr.nick, ptr3 + 1, sizeof(hdr.nick));
+ }
+ }
+ }
+
+ if (fgets(buf, sizeof(buf), fp))
+ {
+ if (ptr1 = strchr(buf, '\n'))
+ *ptr1 = '\0';
+
+ if (!strncmp(buf, "¼ÐÃD: ", LEN_AUTHOR1 + 1))
+ ptr1 = buf + LEN_AUTHOR1 + 1;
+ else if (!strncmp(buf, "¼Ð ÃD: ", LEN_AUTHOR2 + 1))
+ ptr1 = buf + LEN_AUTHOR2 + 1;
+ else
+ ptr1 = NULL;
+
+ if (ptr1 && *ptr1) /* ¦³¼ÐÃDÄæ¦ì */
+ str_ncpy(hdr.title, ptr1, sizeof(hdr.title));
+ }
+ }
+
+ fclose(fp);
+ }
+
+ return &hdr;
+}
+
+
+static char *allindex = ".DIR.tmp";
+
+
+static void
+allindex_collect()
+{
+ int i;
+ char *fname, fpath[64];
+ FILE *fp;
+
+ /* ±N©Ò¦³ªº F* ³£¼g¤J¤@­Ó¼È¦sÀÉ */
+ if (fp = fopen(allindex, "w"))
+ {
+ for (i = 0; i < n_head; i++)
+ {
+ fname = n_pool[i];
+
+ if (*fname != 'F')
+ continue;
+
+ sprintf(fpath, "%c/%s", fname[7], fname);
+ f_suck(fp, fpath);
+ }
+ fclose(fp);
+ }
+}
+
+
+static int
+allindex_search(fname)
+ char *fname;
+{
+ HDR old;
+ int fd;
+ int rc = 0;
+
+ if ((fd = open(allindex, O_RDONLY)) >= 0)
+ {
+ while (read(fd, &old, sizeof(HDR)) == sizeof(HDR))
+ {
+ if (!strcmp(fname, old.xname))
+ {
+ rc = 1;
+ break;
+ }
+ }
+ close(fd);
+ }
+ return rc;
+}
+
+
+static int
+fname_cmp(s1, s2)
+ char *s1, *s2;
+{
+ return strcmp(s1 + 1, s2 + 1);
+}
+
+
+static void
+usage(argv)
+ char *argv[];
+{
+ char *str = argv[0];
+
+ printf("Usage: ½Ð«ü©w°Ñ¼Æ\n");
+ printf(" ­««Ø ¬ÝªO¤å³¹ ¯Á¤Þ½Ð°õ¦æ %s -b\n", str);
+ printf(" ­««ØºëµØ°Ï¤å³¹¯Á¤Þ½Ð°õ¦æ %s -g\n", str);
+ printf(" ­««Ø «H½c«H¥ó ¯Á¤Þ½Ð°õ¦æ %s -m\n", str);
+ printf("°õ¦æµ²§ô¥H«á¡A¦A±N .DIR.re Âл\\ .DIR §Y¥i\n");
+
+ exit(0);
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int i;
+ char *fname, buf[10];
+ FILE *fp;
+ HDR *hdr;
+ struct dirent *de;
+ DIR *dirp;
+
+ if (argc != 2)
+ usage(argv);
+
+ type = argv[1][1];
+ if (type != 'b' && type != 'g' && type != 'm')
+ usage(argv);
+
+ /* readdir 0-9A-V(¬ÝªO¡BºëµØ°Ï) ©Î @(«H½c) */
+
+ buf[1] = '\0';
+
+ for (i = 0; i < 32; i++)
+ {
+ *buf = (type == 'm') ? '@' : radix32[i];
+
+ if (dirp = opendir(buf))
+ {
+ while (de = readdir(dirp))
+ {
+ fname = de->d_name;
+ if (type == 'b' && *fname != 'A') /* ¬ÝªO¥²¬O A1234567 */
+ continue;
+ if (type == 'g' && *fname != 'A' && *fname != 'F') /* ºëµØ°Ï¥²¬O A1234567 ©Î F1234567 */
+ continue;
+ if (type == 'm' && *fname != '@') /* «H½c¥²¬O @1234567 */
+ continue;
+
+ if (pool_add(fname) < 0)
+ printf("Bad article/folder name %c/%s\n", *buf, fname);
+ }
+ closedir(dirp);
+ }
+
+ if (type == 'm') /* «H½c¥u»Ý­n±½ @ ³o­Ó¥Ø¿ý */
+ break;
+ }
+
+ if (n_head)
+ {
+ /* ¬ÝªO/«H½c ´Nª½±µ§â n_pool ¸Ì­±ªº©Ò¦³ÀÉ®×¥[¶i .DIR §Y¥i */
+ /* ºëµØ°Ïªº¸Ü¡A«h¬O¥u»Ý­n§â¨S¦³¦b¨ä¥L F* ¸Ì­±ªº¥[¤J .DIR */
+
+ /* qsort chrono */
+ if (n_head > 1)
+ qsort(n_pool, n_head, sizeof(FNAME), fname_cmp);
+
+ if (type == 'g')
+ allindex_collect();
+
+ /* generate .DIR.re */
+ if (fp = fopen(".DIR.re", "w"))
+ {
+ /* for each file/folder */
+ for (i = 0; i < n_head; i++)
+ {
+ fname = n_pool[i];
+
+ if (type == 'g' && allindex_search(fname)) /* ¤w¸g¦b¨ä¥L¨÷©v¸Ì­±¤F¡A¨º´N¤£·|¬O¦b .DIR ¸Ì­± */
+ continue;
+
+ /* parse header */
+ if (hdr = article_parse(fname))
+ fwrite(hdr, sizeof(HDR), 1, fp);
+ }
+ fclose(fp);
+
+ /* add */
+ time_t now;
+ struct tm *ptime;
+ time(&now);
+ ptime = localtime(&now);
+ char cmd[100], oldfname[60];
+ sprintf(oldfname, ".DIR.before.%02d%02d%02d-%02d%02d%02d",
+ ptime->tm_year % 100, ptime->tm_mon + 1, ptime->tm_mday,
+ ptime->tm_hour, ptime->tm_min, ptime->tm_sec);
+ sprintf (cmd, "cp .DIR %s", oldfname);
+ system (cmd);
+
+ FILE *fpold = fopen (oldfname, "r");
+
+ int posold = 0;
+ HDR hdrold, hdrnew;
+
+ while (!rec_get(oldfname, &hdrold, sizeof(HDR), posold))
+ {
+ fp = fopen (".DIR.re", "a+b");
+ int posnew = 0;
+ while (!rec_get(".DIR.re", &hdrnew, sizeof(HDR), posnew))
+ {
+ if (!strcmp(hdrnew.xname, hdrold.xname))
+ {
+ hdrnew.xmode = hdrold.xmode;
+ hdrnew.score = hdrold.score;
+ rec_put(".DIR.re", &hdrnew, sizeof(HDR), posnew, NULL);
+ }
+ posnew ++;
+ }
+ fclose (fp);
+ posold++;
+ }
+
+ fclose (fpold);
+ system ("cp .DIR.re .DIR");
+ /* above */
+
+ }
+
+ if (type == 'g')
+ unlink(allindex);
+ }
+
+ return 0;
+}
diff --git a/util/rmbadmail.c b/util/rmbadmail.c
new file mode 100644
index 0000000..6973e96
--- /dev/null
+++ b/util/rmbadmail.c
@@ -0,0 +1,318 @@
+/*-------------------------------------------------------*/
+/* util/rmbadmail.c ( YZU WindTop 2000) */
+/*-------------------------------------------------------*/
+/* author : visor.bbs@bbs.yzu.edu.tw */
+/* target : §R°£[«H½c|¬ÝªO]¥¼¦b .DIR ¸Ìªº«H¥ó */
+/* create : 00/08/30 */
+/* update : 01/02/16 */
+/*-------------------------------------------------------*/
+/* syntax : rmbadmail */
+/*-------------------------------------------------------*/
+
+
+#undef FAKE_IO
+#include "bbs.h"
+
+
+static int reserve, r_size, ulink, u_size;
+
+
+static void
+reaper(fpath, lowid)
+ char *fpath;
+ char *lowid;
+{
+ int fd, size, check;
+ char buf[256], *fname, folder[128], *ptr;
+ DIR *dirp;
+ struct dirent *de;
+ HDR *head, *tail, *base;
+ struct stat st;
+ time_t now;
+
+ now = time(0) - 60;
+
+ printf("> processing account %-20s ", lowid);
+
+ sprintf(buf, "%s/.DIR", fpath);
+
+ if ((fd = open(buf, O_RDONLY)) >= 0)
+ {
+ fstat(fd, &st);
+ size = st.st_size / sizeof(HDR);
+
+ if (size <= 0)
+ {
+ base = NULL;
+ }
+ else
+ {
+ base = (HDR *) malloc(sizeof(HDR) * size);
+ tail = base + size;
+ read(fd, base, sizeof(HDR) * size);
+ }
+
+ close(fd);
+ }
+ else
+ {
+ size = 0;
+ base = NULL;
+ }
+
+ printf("total mail : %d\n", size);
+ sprintf(folder, "%s/@", fpath);
+
+ if (!(dirp = opendir(folder)))
+ {
+ if (base)
+ free(base);
+ return;
+ }
+
+ ptr = strchr(folder, '@') + 1;
+ *ptr++ = '/';
+
+ while (de = readdir(dirp))
+ {
+ check = 0;
+ fname = de->d_name;
+ if (fname[0] > ' ' && fname[0] != '.')
+ {
+ if (base)
+ {
+ for (head = base; head < tail; head++)
+ {
+ if (!strcmp(head->xname, fname))
+ {
+ check = 1;
+ break;
+ }
+ }
+ }
+ if (!check)
+ {
+ strcpy(ptr, fname);
+ if (!(!stat(folder, &st) && (st.st_atime > now)))
+ {
+ u_size += st.st_size;
+ ulink++;
+ printf("file is not in HDR : %s : unlink !\n", fname);
+ printf("--> unlinking %s\n", folder);
+
+#ifndef FAKE_IO
+ unlink(folder);
+#endif
+ }
+ else
+ {
+ r_size += st.st_size;
+ reserve++;
+ printf("file is not in HDR : %s : reserve !\n", fname);
+ }
+ }
+ }
+ }
+ closedir(dirp);
+ free(base);
+}
+
+
+static void
+expire(fpath, lowid)
+ char *fpath;
+ char *lowid;
+{
+ int fd, size, check;
+ char buf[256], *fname, folder[128], *ptr, *str;
+ DIR *dirp;
+ struct dirent *de;
+ HDR *head, *tail, *base;
+ struct stat st;
+ time_t now;
+
+ now = time(0) - 60;
+
+ printf("> processing board %-20s ", lowid);
+
+ sprintf(buf, "%s/.DIR", fpath);
+ if ((fd = open(buf, O_RDONLY)) >= 0)
+ {
+ fstat(fd, &st);
+ size = st.st_size / sizeof(HDR);
+ if (size <= 0)
+ {
+ base = NULL;
+ }
+ else
+ {
+ base = (HDR *) malloc(sizeof(HDR) * size);
+ tail = base + size;
+ read(fd, base, sizeof(HDR) * size);
+ }
+ close(fd);
+ }
+ else
+ {
+ base = NULL;
+ size = 0;
+ }
+ printf("total article : %d\n", size);
+ sprintf(folder, "%s/@", fpath);
+
+ str = strchr(folder, '@');
+ ptr = str + 1;
+ *ptr++ = '/';
+ *str = '0';
+
+ while (1)
+ {
+ if ((dirp = opendir(folder)))
+ {
+ while (de = readdir(dirp))
+ {
+ check = 0;
+ fname = de->d_name;
+ if (fname[0] > ' ' && fname[0] != '.')
+ {
+ if (base)
+ {
+ for (head = base; head < tail; head++)
+ {
+ if (!strcmp(head->xname, fname))
+ {
+ check = 1;
+ break;
+ }
+ }
+ }
+ if (!check)
+ {
+ strcpy(ptr, fname);
+ if (!(!stat(folder, &st) && (st.st_atime > now)))
+ {
+ u_size += st.st_size;
+ ulink++;
+ printf("file is not in HDR : %s : unlink !\n", fname);
+ printf("--> unlinking %s\n", folder);
+
+#ifndef FAKE_IO
+ unlink(folder);
+#endif
+ }
+ else
+ {
+ r_size += st.st_size;
+ reserve++;
+ printf("file is not in HDR : %s : reserve !\n", fname);
+ }
+ }
+ }
+ }
+ closedir(dirp);
+ }
+ if (++(*str) == ('9' + 1))
+ *str = 'A';
+ if ((*str) == 'W')
+ break;
+ }
+ free(base);
+}
+
+
+static void
+traverse(fpath, mode)
+ char *fpath;
+ int mode;
+{
+ DIR *dirp;
+ struct dirent *de;
+ char *fname, *str;
+
+ if (!(dirp = opendir(fpath)))
+ {
+ return;
+ }
+ for (str = fpath; *str; str++);
+ *str++ = '/';
+
+ while (de = readdir(dirp))
+ {
+ fname = de->d_name;
+ if (fname[0] > ' ' && fname[0] != '.')
+ {
+ strcpy(str, fname);
+ if (mode == 1)
+ reaper(fpath, fname);
+ else if (mode == 2)
+ expire(fpath, fname);
+ }
+ }
+ closedir(dirp);
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int ch;
+ char *fname, fpath[256], buf[32];
+
+ chdir(BBSHOME);
+
+ if (argc > 1 && !strncmp(argv[1], "-a", 2))
+ {
+ if (argc > 2)
+ {
+ str_lower(buf, argv[2]);
+ sprintf(fpath, "usr/%c/%s", *buf, buf);
+ if (!access(fpath, 0))
+ reaper(fpath, buf);
+ else
+ printf("error open account %s\n", buf);
+ }
+ else
+ {
+ strcpy(fname = fpath, "usr/@");
+ fname = (char *) strchr(fname, '@');
+ for (ch = 'a'; ch <= 'z'; ch++)
+ {
+ fname[0] = ch;
+ fname[1] = '\0';
+ traverse(fpath, 1);
+ }
+ for (ch = '0'; ch <= '9'; ch++)
+ {
+ fname[0] = ch;
+ fname[1] = '\0';
+ traverse(fpath, 1);
+ }
+ }
+ }
+ else if (argc > 1 && !strncmp(argv[1], "-b", 2))
+ {
+ if (argc > 2)
+ {
+ strcpy(buf, argv[2]);
+ sprintf(fpath, "brd/%s", buf);
+ if (!access(fpath, 0))
+ expire(fpath, buf);
+ else
+ printf("error open board %s\n", buf);
+ }
+ else
+ {
+ strcpy(fpath, "brd");
+ traverse(fpath, 2);
+ }
+ }
+ else
+ {
+ printf("syntax : rmbadmail [-a|-b] [account|board]\n");
+ }
+ printf("total unlink %10d unlink size : %10d\n", ulink, u_size);
+ printf("total reserve %10d reserve size : %10d\n", reserve, r_size);
+ return 0;
+}
diff --git a/util/setperm.c b/util/setperm.c
new file mode 100644
index 0000000..281e06c
--- /dev/null
+++ b/util/setperm.c
@@ -0,0 +1,88 @@
+/*-------------------------------------------------------*/
+/* util/setperm.c ( NTHU CS MapleBBS Ver 3.00 ) */
+/*-------------------------------------------------------*/
+/* target : ³]©w¨Ï¥ÎªÌÅv­­ */
+/* author : gslin@abpe.org */
+/* create : 00/07/28 */
+/* update : */
+/*-------------------------------------------------------*/
+
+
+#if 0
+ ±N itoc ¤Î sysop ³]¬°©Ò¦³Åv­­³£¦³:
+ setperm -1 itoc sysop
+ setperm 11111111111111111111111111111111 itoc sysop
+
+ ±N itoc ³]¬°¥u¦³°ò¥»Åv­­:
+ setperm 1 itoc
+ setperm 00000000000000000000000000000001 itoc
+
+ ±N guest ³]¬°¨S¦³Åv­­:
+ setperm 0 guest
+ setperm 00000000000000000000000000000000 guest
+#endif
+
+
+#include "bbs.h"
+
+
+static void
+usage(msg)
+ char *msg;
+{
+ printf("Usage: %s Perm32 UserID1 [UserID2] ...\n", msg);
+ exit(1);
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int i;
+ usint userlevel;
+
+ if (argc < 3)
+ usage(argv[0]);
+
+ if (strlen(argv[1]) != 32)
+ {
+ userlevel = (usint) atoi(argv[1]);
+ }
+ else
+ {
+ userlevel = 0;
+
+ for (i = 0; i < 32; i++)
+ {
+ char c = argv[1][i];
+
+ if (c != '0' && c != '1')
+ usage(argv[0]);
+
+ userlevel <<= 1;
+ userlevel |= c - '0';
+ }
+ }
+
+ chdir(BBSHOME);
+
+ for (i = 2; i < argc; i++)
+ {
+ ACCT cuser;
+ char fpath[64];
+
+ usr_fpath(fpath, argv[i], FN_ACCT);
+ if (rec_get(fpath, &cuser, sizeof(cuser), 0) < 0)
+ {
+ printf("%s: read error (maybe no such id?)\n", argv[i]);
+ continue;
+ }
+
+ cuser.userlevel = userlevel;
+
+ if (rec_put(fpath, &cuser, sizeof(cuser), 0, NULL) < 0)
+ printf("%s: write error\n", argv[i]);
+ }
+}
diff --git a/util/setusr.c b/util/setusr.c
new file mode 100644
index 0000000..2407574
--- /dev/null
+++ b/util/setusr.c
@@ -0,0 +1,166 @@
+/*-------------------------------------------------------*/
+/* util/setusr.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : ³]©w¨Ï¥ÎªÌ¸ê®Æ */
+/* author : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/* create : 01/07/16 */
+/* update : */
+/*-------------------------------------------------------*/
+
+
+#if 0
+ ³]©w itoc ªº ©m¦W¬°¤ý¤j©ú ¼ÊºÙ¬°¤j©ú »È¹ô¦³1000 ª÷¹ô¦³500
+ setusr -r ¤ý¤j©ú -n ¤j©ú -m 1000 -g 500 itoc
+
+ ³]©w itoc ªºÅv­­
+ setusr -p 11100...1101 itoc (Åv­­¦³ 32 = NUMPERMS ºØ)
+ ^^^^^ 32 ­Ó 0 ©M 1
+
+ ³]©w itoc ªº²ßºD
+ setusr -f 11100...1101 itoc (²ßºD¦³ 27 = NUMUFOS ºØ)
+ ^^^^^ 27 ­Ó 0 ©M 1
+
+ ¸Ó¨Ï¥ÎªÌ¥²¶·¤£¦b½u¤W¤~¦³®Ä
+#endif
+
+
+#include "bbs.h"
+
+
+#define MAXUSIES 9 /* ¦@¦³ 9 ºØ¥i¥H§ïªº */
+
+static void
+usage(msg)
+ char *msg;
+{
+ int i, len;
+ char buf[80];
+ char *usies[MAXUSIES] =
+ {
+ "r realname", "n username", "m money", "g gold", "# userno",
+ "e email", "j 1/0(justify)", "p userlevel", "f ufo"
+ };
+
+
+ printf("Usage: %s [-%s] [-%s] [-%s] ... [-%s] UserID\n",
+ msg, usies[0], usies[1], usies[2], usies[MAXUSIES - 1]);
+ len = strlen(msg);
+ sprintf(buf, "%%%ds-%%s\n", len + MAXUSIES);
+ for (i = 0; i < MAXUSIES; i++)
+ printf(buf, "", usies[i]);
+}
+
+
+static usint
+bitcfg(len, str) /* config bits */
+ int len; /* ¸ÓÄæ¦ìªºªø«× */
+ char *str; /* optarg */
+{
+ int i;
+ char c;
+ usint bits;
+
+ if (strlen(str) != len) /* ª½±µµ¹­Ó¼Æ¦r */
+ {
+ bits = (usint) atoi(str);
+ }
+ else
+ {
+ bits = 0;
+ for (i = 0; i < len; i++)
+ {
+ c = str[i];
+
+ if (c != '0' && c != '1')
+ {
+ printf("bit ¤@©w­n¬O 0 ©Î 1\n");
+ exit(1);
+ }
+
+ bits <<= 1;
+ bits |= c - '0';
+ }
+ }
+
+ return bits;
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int c;
+ char *userid, fpath[64];
+ ACCT acct;
+
+ if (argc < 4 || argc % 2) /* argc ­n¬O°¸¼Æ */
+ {
+ usage(argv[0]);
+ exit(1);
+ }
+
+ chdir(BBSHOME);
+
+ userid = argv[argc - 1];
+ usr_fpath(fpath, userid, FN_ACCT);
+ if (rec_get(fpath, &acct, sizeof(ACCT), 0) < 0)
+ {
+ printf("%s: read error (maybe no such id?)\n", userid);
+ exit(1);
+ }
+
+ while ((c = getopt(argc, argv, "r:n:m:g:#:e:j:p:f:")) != -1)
+ {
+ switch (c)
+ {
+ case 'r': /* realname */
+ strcpy(acct.realname, optarg);
+ break;
+
+ case 'n': /* username */
+ strcpy(acct.username, optarg);
+ break;
+
+ case 'm': /* money */
+ acct.money = atoi(optarg);
+ break;
+
+ case 'g': /* gold */
+ acct.gold = atoi(optarg);
+ break;
+
+ case '#': /* userno */
+ acct.userno = atoi(optarg);
+ break;
+
+ case 'e': /* email */
+ strcpy(acct.email, optarg);
+ break;
+
+ case 'j': /* justify */
+ if (atoi(optarg)) /* »{ÃÒ³q¹L */
+ acct.userlevel |= PERM_VALID;
+ else
+ acct.userlevel &= ~PERM_VALID;
+ time(&acct.tvalid);
+ break;
+
+ case 'p': /* userlevel */
+ acct.userlevel = bitcfg(NUMPERMS, optarg);
+ break;
+
+ case 'f': /* ufo */
+ acct.ufo = bitcfg(NUMUFOS, optarg);
+ break;
+
+ default:
+ usage(argv[0]);
+ exit(0);
+ }
+ }
+
+ if (rec_put(fpath, &acct, sizeof(ACCT), 0, NULL) < 0)
+ printf("%s: write error\n", userid);
+}
diff --git a/util/showACCT.c b/util/showACCT.c
new file mode 100644
index 0000000..9df3d4b
--- /dev/null
+++ b/util/showACCT.c
@@ -0,0 +1,116 @@
+/*-------------------------------------------------------*/
+/* util/showACCT.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : Åã¥Ü¨Ï¥ÎªÌ¸ê®Æ */
+/* create : 01/07/16 */
+/* update : */
+/* author : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+
+//#undef SHOW_PASSWORD /* Åã¥Ü±K½X («Ü¯Ó®É¶¡) */
+
+
+
+//#ifdef SHOW_PASSWORD
+
+#define GUESS_LEN 8 /* ¥u´ú¤T½X(§t)¥H¤Uªº±K½X²Õ¦X (³Ì¦h¬O PSWDLEN) */
+
+static char *
+_bitmsg(str, level)
+ char *str;
+ int level;
+{
+ int cc;
+ int num;
+ static char msg[33];
+
+ num = 0;
+ while (cc = *str)
+ {
+ msg[num] = (level & 1) ? cc : '-';
+ level >>= 1;
+ str++;
+ num++;
+ }
+ msg[num] = 0;
+
+ return msg;
+}
+
+
+static inline void
+showACCT(acct)
+ ACCT *acct;
+{
+ char msg1[40], msg2[40], msg3[40], msg4[40], msg5[40], msg6[40];
+
+ strcpy(msg1, _bitmsg(STR_PERM, acct->userlevel));
+ strcpy(msg2, _bitmsg(STR_UFO, acct->ufo));
+ strcpy(msg3, Btime(&(acct->firstlogin)));
+ strcpy(msg4, Btime(&(acct->lastlogin)));
+ strcpy(msg5, Btime(&(acct->tcheck)));
+ strcpy(msg6, Btime(&(acct->tvalid)));
+
+ printf("> ------------------------------------------------------------------------------------------ \n"
+ "½s¸¹: %-15d [ID]: %-15s ©m¦W: %-15s ¼ÊºÙ: %-15s \n"
+ "Åv­­: %-37s ³]©w: %-37s \n"
+ "ñ¦W: %-37d ©Ê§O: %-15.2s \n"
+ "»È¹ô: %-15d ª÷¹ô: %-15d ¥Í¤é: %02d/%02d/%02d \n"
+ "¤W¯¸: %-15d ¤å³¹: %-15d µo«H: %-15d \n"
+ "­º¦¸: %-37s ¤W¦¸: %-30s \n"
+ "Àˬd: %-37s ³q¹L: %-30s \n"
+ "µn¤J: %-30s \n"
+ "«H½c: %-60s \n",
+ acct->userno, acct->userid, acct->realname, acct->username,
+ msg1, msg2,
+ acct->signature, "¡H¡ñ¡ð" + (acct->sex << 1),
+ acct->money, acct->gold, acct->year, acct->month, acct->day,
+ acct->numlogins, acct->numposts, acct->numemails,
+ msg3, msg4,
+ msg5, msg6,
+ acct->lasthost,
+ acct->email);
+
+//#ifdef SHOW_PASSWORD
+ printf("%s\n", acct->passwd);
+//#endif
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ int i;
+
+ if (argc < 2)
+ {
+ printf("Usage: %s UserID1 [UserID2] ...\n", argv[0]);
+ return -1;
+ }
+
+ chdir(BBSHOME);
+
+ for (i = 1; i < argc; i++)
+ {
+ ACCT acct;
+ char fpath[64];
+
+ usr_fpath(fpath, argv[i], FN_ACCT);
+ if (rec_get(fpath, &acct, sizeof(ACCT), 0) < 0)
+ {
+ printf("%s: read error (maybe no such id?)\n", argv[i]);
+ continue;
+ }
+
+ showACCT(&acct);
+ }
+ printf("> ------------------------------------------------------------------------------------------ \n");
+
+ return 0;
+}
diff --git a/util/showBRD.c b/util/showBRD.c
new file mode 100644
index 0000000..c5fae0a
--- /dev/null
+++ b/util/showBRD.c
@@ -0,0 +1,79 @@
+/*-------------------------------------------------------*/
+/* util/showBRD.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : show board info */
+/* create : 01/10/05 */
+/* update : / / */
+/* author : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+/* syntax : showBRD [target_board] */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+
+static void
+_bitmsg(msg, str, level)
+ char *msg, *str;
+ int level;
+{
+ int cc;
+
+ printf(msg);
+ while (cc = *str)
+ {
+ printf("%c", (level & 1) ? cc : '-');
+ level >>= 1;
+ str++;
+ }
+ printf("\n");
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int show_allbrd;
+ BRD brd;
+ FILE *fp;
+
+ if (argc < 2)
+ show_allbrd = 1;
+ else
+ show_allbrd = 0;
+
+ chdir(BBSHOME);
+
+ if (!(fp = fopen(FN_BRD, "r")))
+ return -1;
+
+ int boards = 0;
+
+ while (fread(&brd, sizeof(BRD), 1, fp) == 1)
+ {
+ if (show_allbrd || !str_cmp(brd.brdname, argv[1]))
+ {
+ printf("¬ÝªO¦WºÙ¡G%-13s ¬ÝªO¼ÐÃD¡G[%s] %s\n", brd.brdname, brd.class, brd.title);
+ printf("§ë²¼ª¬ºA¡G%-13d ¬ÝªOªO¥D¡G%s\n", brd.bvote, brd.BM);
+ _bitmsg(MSG_READPERM, STR_PERM, brd.readlevel);
+ _bitmsg(MSG_POSTPERM, STR_PERM, brd.postlevel);
+ _bitmsg(MSG_BRDATTR, STR_BATTR, brd.battr);
+ printf("¤å³¹½g¼Æ¡G%d\n", brd.bpost);
+ printf("¶}ªO®É¶¡¡G%s\n", Btime(&brd.bstamp));
+ printf(".DIR®É¶¡¡G%s\n", Btime(&brd.btime));
+ printf("³Ì«á¤@½g¡G%s\n", Btime(&brd.blast));
+
+ boards ++;
+ if (!show_allbrd)
+ break;
+ }
+ }
+
+ fclose(fp);
+
+ printf ("Total Boards = %d\n", boards);
+ return 0;
+}
diff --git a/util/showDIR.c b/util/showDIR.c
new file mode 100644
index 0000000..bbf403c
--- /dev/null
+++ b/util/showDIR.c
@@ -0,0 +1,81 @@
+/*-------------------------------------------------------*/
+/* util/showDIR.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : Åã¥Ü .DIR ¸ê®Æ */
+/* create : 03/05/24 */
+/* update : / / */
+/* author : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+
+static char *
+_bitmsg(str, level)
+ char *str;
+ int level;
+{
+ int cc;
+ int num;
+ static char msg[33];
+
+ num = 0;
+ while (cc = *str)
+ {
+ msg[num] = (level & 1) ? cc : '-';
+ level >>= 1;
+ str++;
+ num++;
+ }
+ msg[num] = 0;
+
+ return msg;
+}
+
+
+static inline void
+showHDR(hdr)
+ HDR *hdr;
+{
+ char msg1[40], msg2[40];
+
+ strcpy(msg1, Btime(&(hdr->chrono)));
+ strcpy(msg2, _bitmsg("0123456789ABCDEFGHIJKLMNOPQRSTUV", hdr->xmode));
+ printf("> ------------------------------------------------------------------------------------------ \n"
+ "®É¶¡: %s\nmode: %s\nÀÉ®×: %s\n§@ªÌ: %s\n¼ÊºÙ: %s\n¤é´Á: %s\n¥DÃD: %s\n",
+ msg1, msg2, hdr->xname, hdr->owner, hdr->nick, hdr->date, hdr->title, hdr->title);
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int pos;
+ char *fname;
+ HDR hdr;
+
+ if (argc < 2)
+ {
+ printf("Usage: %s .DIR_path\n", argv[0]);
+ exit(1);
+ }
+
+ fname = argv[1];
+ if (strcmp(fname + strlen(fname) - 4, FN_DIR))
+ {
+ printf("This is not a .DIR file.\n");
+ exit(1);
+ }
+
+ pos = 0;
+ while (!rec_get(fname, &hdr, sizeof(HDR), pos))
+ {
+ showHDR(&hdr);
+ pos++;
+ }
+
+ printf("> ------------------------------------------------------------------------------------------ \n");
+}
diff --git a/util/showUSR.c b/util/showUSR.c
new file mode 100644
index 0000000..915bc8e
--- /dev/null
+++ b/util/showUSR.c
@@ -0,0 +1,57 @@
+/*-------------------------------------------------------*/
+/* util/showUSR.c ( NTHU CS MapleBBS Ver 3.00 ) */
+/*-------------------------------------------------------*/
+/* target : ¨q¥X FN_SCHEMA */
+/* create : / / */
+/* update : 02/11/03 */
+/*-------------------------------------------------------*/
+/* syntax : showUSR */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+
+int
+main()
+{
+ int fd, n;
+ SCHEMA *usr;
+ struct stat st;
+
+ chdir(BBSHOME);
+
+ if ((fd = open(FN_SCHEMA, O_RDONLY)) < 0)
+ {
+ printf("ERROR at open file\n");
+ exit(1);
+ }
+
+ fstat(fd, &st);
+ usr = (SCHEMA *) malloc(st.st_size);
+ read(fd, usr, st.st_size);
+ close(fd);
+
+ printf("\n%s ==> %d bytes\n", FN_SCHEMA, st.st_size);
+
+ fd = st.st_size / sizeof(SCHEMA);
+ for (n = 0; n < fd; n++)
+ {
+ /* userno: ¦b .USR ¤¤¬O²Ä´X­Ó slot */
+ /* uptime: µù¥Uªº®É¶¡ (­Y ID ¬OªÅ¥Õ«h¬O³Q reaper ±¼ªº®É¶¡) */
+ /* userid: ID (­Y¬OªÅ¥Õªí¥Ü¦¹¤H³Q reaper ¤F) */
+
+ printf("userno:%d uptime:%s userid:%-12.12s\n",
+ n + 1, Btime(&usr[n].uptime), usr[n].userid);
+
+ if (n % 23 == 22) /* ¨C 23 µ§«ö¥ô·NÁäÄ~Äò */
+ {
+ printf("-== Press ENTER to continue and 'q + ENTER' to quit ==-\n");
+ if (getchar() == 'q')
+ break;
+ }
+ }
+
+ free(usr);
+ return 0;
+}
diff --git a/util/showperm.c b/util/showperm.c
new file mode 100644
index 0000000..cca85e1
--- /dev/null
+++ b/util/showperm.c
@@ -0,0 +1,53 @@
+/*-------------------------------------------------------*/
+/* util/showperm.c ( NTHU CS MapleBBS Ver 3.00 ) */
+/*-------------------------------------------------------*/
+/* target : Åã¥Ü¾Ö¦³¬YºØÅv­­ªº¨Ï¥ÎªÌ */
+/* author : chensc.bbs@sony.tfcis.org */
+/* create : 06/09/03 */
+/* update : */
+/*-------------------------------------------------------*/
+
+#include "bbs.h"
+
+int
+main()
+{
+ int i,fd;
+ SCHEMA *usr;
+ struct stat st;
+
+ chdir(BBSHOME);
+
+ if ((fd = open(FN_SCHEMA, O_RDONLY)) < 0)
+ {
+ printf("ERROR at open file\n");
+ exit(1);
+ }
+
+ fstat(fd, &st);
+ usr = (SCHEMA *) malloc(st.st_size);
+ read(fd, usr, st.st_size);
+ close(fd);
+
+ fd = st.st_size / sizeof(SCHEMA);
+
+ for (i = 0; i < fd; i++)
+ {
+ ACCT cuser;
+ char fpath[64];
+
+ usr_fpath(fpath, usr[i].userid, FN_ACCT);
+ if (rec_get(fpath, &cuser, sizeof(cuser), 0) < 0)
+ {
+ // printf("%s: read error (maybe no such id?)\n", usr[i].userid);
+ continue;
+ }
+
+ if(HAS_PERM(PERM_CHANGEID))
+ printf("%s\n",usr[i].userid);
+
+ if (rec_put(fpath, &cuser, sizeof(cuser), 0, NULL) < 0)
+ printf("%s: write error\n", usr[i].userid);
+
+ }
+}
diff --git a/util/topgem.c b/util/topgem.c
new file mode 100644
index 0000000..3e047d7
--- /dev/null
+++ b/util/topgem.c
@@ -0,0 +1,161 @@
+/*-------------------------------------------------------*/
+/* util/gem-expire.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : ­pºâ¥¼½sºëµØ°Ï & ¥¼½s¤Ñ¼ÆºëµØ°Ï±Æ¦æº] */
+/* create : 99/11/26 */
+/* update : 01/08/27 */
+/* author : Jimmy.bbs@whshs.twbbs.org */
+/* modify : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+/* syntax : gem-expire */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+#define OUTFILE_GEMEMPTY "gem/@/@-gem_empty"
+#define OUTFILE_GEMOVERDUE "gem/@/@-gem_overdue"
+
+
+/*-------------------------------------------------------*/
+/* BRD shm ³¡¤À¶·»P cache.c ¬Û®e */
+/*-------------------------------------------------------*/
+
+
+static BCACHE *bshm;
+
+
+static void
+init_bshm()
+{
+ /* itoc.030727: ¦b¶}±Ò bbsd ¤§«e¡AÀ³¸Ó´N­n°õ¦æ¹L account¡A
+ ©Ò¥H bshm À³¸Ó¤w³]©w¦n */
+
+ bshm = shm_new(BRDSHM_KEY, sizeof(BCACHE));
+
+ if (bshm->uptime <= 0) /* bshm ¥¼³]©w§¹¦¨ */
+ exit(0);
+}
+
+
+/*-------------------------------------------------------*/
+/* ¥Dµ{¦¡ */
+/*-------------------------------------------------------*/
+
+
+typedef struct
+{
+ int day; /* ´X¤Ñ¨S¾ã²zºëµØ°Ï */
+ char brdname[BNLEN + 1]; /* ªO¦W */
+ char BM[BMLEN + 1]; /* ªO¥D */
+} BRDDATA;
+
+
+static int
+int_cmp(a, b)
+ BRDDATA *a, *b;
+{
+ return (b->day - a->day); /* ¥Ñ¤j±Æ¨ì¤p */
+}
+
+
+static BRDDATA board[MAXBOARD];
+static int locus = 0; /* Á`¦@°O¿ý¤F´X­ÓªO */
+
+
+static void
+topgem()
+{
+ time_t now;
+ struct stat st;
+ BRD *bhdr, *tail;
+ char fpath[64], *brdname;
+
+ time(&now);
+
+ bhdr = bshm->bcache;
+ tail = bhdr + bshm->number;
+ do
+ {
+ /* ¸õ¹L¤£¦C¤J±Æ¦æº]ªº¬ÝªO */
+ if ((bhdr->readlevel | bhdr->postlevel) >= (PERM_VALID << 1)) /* (BASIC + ... + VALID) < (VALID << 1) */
+ continue;
+
+ brdname = bhdr->brdname;
+ sprintf(fpath, "gem/brd/%s/@/@log", brdname);
+
+ if (stat(fpath, &st) != -1) /* ¦³ºëµØ°ÏªÌÀˬd´X¤Ñ¥¼½s */
+ board[locus].day = (now - st.st_mtime) / 86400;
+ else /* µLºëµØ°ÏªÌ */
+ board[locus].day = 999;
+ strcpy(board[locus].brdname, brdname);
+ strcpy(board[locus].BM, bhdr->BM);
+
+ locus++;
+ } while (++bhdr < tail);
+
+ qsort(board, locus, sizeof(BRDDATA), int_cmp);
+}
+
+
+static void
+write_data()
+{
+ time_t now;
+ struct tm *ptime;
+ FILE *fpe, *fpo;
+ int i, m, n;
+
+ time(&now);
+ ptime = localtime(&now);
+
+ fpe = fopen(OUTFILE_GEMEMPTY, "w");
+ fpo = fopen(OUTFILE_GEMOVERDUE, "w");
+
+ fprintf(fpe,
+ " \033[1;34m-----\033[37m=====\033[41m ¬ÝªOºëµØ°Ï¥¼½s¤§¬ÝªO (¦Ü %d ¤ë %d ¤é¤î) \033[;1;37m=====\033[34m-----\033[m\n"
+ " \033[1;42m ¦W¦¸ \033[44m ¬ÝªO¦WºÙ \033[42m ºëµØ°Ï¥¼½s \033[44m ªO ¥D \033[m\n",
+ ptime->tm_mon + 1, ptime->tm_mday);
+
+ fprintf(fpo,
+ " \033[1;34m-----\033[37m=====\033[41m ¬ÝªOºëµØ°Ï¥¼½s¤Ñ¼Æ¤§¬ÝªO (¦Ü %d ¤ë %d ¤é¤î) \033[;1;37m=====\033[34m-----\033[m\n"
+ " \033[1;42m ¦W¦¸ \033[44m ¬ÝªO¦WºÙ \033[42m ºëµØ°Ï¥¼½s¤Ñ¼Æ \033[44m ªO ¥D \033[m\n",
+ ptime->tm_mon + 1, ptime->tm_mday);
+
+ m = 1;
+ n = 1;
+
+ for (i = 0; i < locus; i++)
+ {
+ if (board[i].day == 999)
+ {
+ fprintf(fpe, " %3d %12s %s %.20s\n",
+ m, board[i].brdname, "©|¥¼½s¿èºëµØ°Ï", board[i].BM);
+ m++;
+ }
+ else
+ {
+ fprintf(fpo, " %s%3d %12s %4d %.20s\033[m\n",
+ n <= 3 ? "\033[1m" : (n <= 10 ? "\033[1;31m" : "\033[m"),
+ n, board[i].brdname, board[i].day, board[i].BM);
+ n++;
+ }
+ }
+
+ fclose(fpe);
+ fclose(fpo);
+}
+
+
+int
+main()
+{
+ chdir(BBSHOME);
+
+ init_bshm();
+
+ topgem();
+ write_data();
+
+ return 0;
+}
diff --git a/util/topsong.c b/util/topsong.c
new file mode 100644
index 0000000..948f325
--- /dev/null
+++ b/util/topsong.c
@@ -0,0 +1,91 @@
+/*-------------------------------------------------------*/
+/* util/topsong.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : ºq¥»¨Ï¥Î±Æ¦W */
+/* create : 01/09/28 */
+/* update : / / */
+/* author : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+/* syntax : topsong */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+#ifdef LOG_SONG_USIES
+
+
+#define OUTFILE_TOPSONG "gem/@/@-topsong"
+
+
+static void
+write_data(songs, num)
+ SONGDATA *songs;
+ int num;
+{
+ int n;
+ FILE *fp;
+
+ if (!(fp = fopen(OUTFILE_TOPSONG, "w")))
+ return;
+
+ fprintf(fp, " \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[m\n");
+
+ for (n = 0; n < 50 && n < num; n++) /* ¥u¨ú«e 50 ¦W */
+ {
+ fprintf(fp, " %5d. %-38.38s %4d ¦¸\033[m\n",
+ n + 1, songs[n].title, songs[n].count);
+ }
+
+ fclose(fp);
+}
+
+
+static int
+count_cmp(b, a)
+ SONGDATA *a, *b;
+{
+ return (a->count - b->count);
+}
+
+
+int
+main()
+{
+ int fd, size;
+ struct stat st;
+ SONGDATA *songs;
+
+ chdir(BBSHOME);
+
+ if ((fd = open(FN_RUN_SONGUSIES, O_RDWR)) < 0)
+ return 0;
+
+ if (!fstat(fd, &st) && (size = st.st_size) >= sizeof(SONGDATA))
+ {
+ songs = (SONGDATA *) malloc(size);
+ size = read(fd, songs, size);
+
+ qsort(songs, size / sizeof(SONGDATA), sizeof(SONGDATA), count_cmp);
+
+ lseek(fd, 0, SEEK_SET);
+ write(fd, songs, size);
+ ftruncate(fd, size);
+
+ write_data(songs, size / sizeof(SONGDATA));
+ free(songs);
+ }
+
+ close(fd);
+ return 0;
+}
+
+#else
+int
+main()
+{
+ printf("You shoule define LOG_SONG_USIES first.\n");
+ return 0;
+}
+#endif /* LOG_SONG_USIES */
diff --git a/util/topusr.c b/util/topusr.c
new file mode 100644
index 0000000..f126be0
--- /dev/null
+++ b/util/topusr.c
@@ -0,0 +1,751 @@
+/*-------------------------------------------------------*/
+/* util/topusr.c ( NTHU CS MapleBBS Ver 3.00 ) */
+/*-------------------------------------------------------*/
+/* target : ¨Ï¥ÎªÌ¸ê®Æ²Î­p¤Î±Æ¦W */
+/* create : 99/03/29 */
+/* update : 01/10/01 */
+/* modify : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+/* itoc.µù¸Ñ: ±N ¤W¯¸¦¸¼Æ/Äé¤ô¦¸¼Æ/»È¹ô/ª÷¹ô/¦~ÄÖ/¬P®y/©Ê§O/¹Ø¬P ¤@¨Ö²Î­p¡A§K±o±`±`¦b¶} .ACCT */
+
+#include "bbs.h"
+
+
+#define OUTFILE_TOPLOGIN BBSHOME"/gem/@/@-toplogin"
+#define OUTFILE_TOPPOST BBSHOME"/gem/@/@-toppost"
+#define OUTFILE_TOPMONEY BBSHOME"/gem/@/@-topmoney"
+#define OUTFILE_TOPGOLD BBSHOME"/gem/@/@-topgold"
+#define OUTFILE_AGE BBSHOME"/gem/@/@-age"
+#define OUTFILE_STAR BBSHOME"/gem/@/@-star"
+#define OUTFILE_SEX BBSHOME"/gem/@/@-sex"
+#define OUTFILE_BIRTHDAY BBSHOME"/gem/@/@-birthday"
+#define TMPFILE_BIRTHMON BBSHOME"/tmp/birthmon"
+
+
+/*-------------------------------------------------------*/
+/* author : mat.bbs@fall.twbbs.org */
+/* modify : gslin@abpe.org */
+/* target : ¤W¯¸¦¸¼Æ¡BÄé¤ô¦¸¼Æ¡Bª÷»È¹ô±Æ¦æº] */
+/*-------------------------------------------------------*/
+
+
+#define TOPNUM 36
+#define TOPNUM_HALF (TOPNUM / 2)
+
+
+typedef struct
+{
+ char userid[IDLEN + 1];
+ char username[UNLEN + 1];
+ int num; /* ¼Æ­È */
+ int rank; /* ¦W¦¸ */
+} DATA;
+
+static DATA toplogins[TOPNUM], topposts[TOPNUM], topmoney[TOPNUM], topgold[TOPNUM];
+
+
+static int
+sort_compare(p1, p2)
+ const void *p1;
+ const void *p2;
+{
+ int k;
+ DATA *a1, *a2;
+
+ a1 = (DATA *) p1;
+ a2 = (DATA *) p2;
+
+ k = a2->num - a1->num;
+ return k ? k : str_cmp(a1->userid, a2->userid);
+}
+
+
+static DATA *
+findmin(src)
+ DATA *src;
+{
+ int i;
+ DATA *p;
+
+ p = src;
+ for (i = 0; i < TOPNUM; i++)
+ {
+ if (src[i].num < p->num)
+ p = src + i;
+ }
+ return p;
+}
+
+
+static void
+sort_rank(data) /* µ¹©w¦W¦¸ */
+ DATA *data; /* ¤w¨Ì data->num ±Æ§Ç */
+{
+ int i, rank;
+
+ data[0].rank = rank = 1;
+ for (i = 1; i < TOPNUM; i++)
+ {
+ if (data[i].num != data[i - 1].num)
+ rank = i + 1;
+ data[i].rank = rank;
+ }
+}
+
+
+static void
+merge_id_nick(dst, userid, nick)
+ char *dst;
+ char *userid;
+ char *nick;
+{
+ if (*userid)
+ {
+ sprintf(dst, "%s (%s)", userid, nick);
+
+ if (strlen(dst) > 25)
+ dst[25] = '\0';
+ }
+ else
+ dst[0] = '\0';
+}
+
+
+static void
+write_data(fpath, title, data)
+ char *fpath;
+ char *title;
+ DATA *data;
+{
+ FILE *fp;
+ char buf[256];
+ int i, num1, num2;
+
+ sort_rank(data);
+
+ if (!(fp = fopen(fpath, "w")))
+ return;
+
+ i = 12 - (strlen(title) >> 1);
+ sprintf(buf, " \033[1;33m¡³ ¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w¡÷ \033[41m%%%ds%%s%%%ds\033[40m ¡ö¢w¢w¢w¢w¢w¢w¢w¢w¢w¢w ¡³\033[m\n\n", i, i);
+ fprintf(fp, buf, "", title, "");
+
+ fprintf(fp,
+ "\033[1;37m¦W¦¸ \033[33m¥N¸¹(¼ÊºÙ) \033[36m¼Æ¶q\033[m "
+ "\033[1;37m¦W¦¸ \033[33m¥N¸¹(¼ÊºÙ) \033[36m¼Æ¶q\033[m\n"
+ "\033[1;32m%s\033[m\n", MSG_SEPERATOR);
+
+ for (i = 0; i < TOPNUM_HALF; i++)
+ {
+ char buf1[80], buf2[80];
+
+ merge_id_nick(buf1, data[i].userid, data[i].username);
+ merge_id_nick(buf2, data[i + TOPNUM_HALF].userid,
+ data[i + TOPNUM_HALF].username);
+
+ /* itoc.010408: ¸Ñ¨M¿ú¤Ó¦h¡Aµe­±Ãz±¼ªº°ÝÃD */
+ num1 = data[i].num / 1000000;
+ num2 = data[i + TOPNUM_HALF].num / 1000000;
+ if (num2) /* ¨º»ò data[i].num ¤]¥²©w > 10^6 */
+ {
+ fprintf(fp, "[%2d] %-25s %5dM [%2d] %-25s %5dM\n",
+ data[i].rank, buf1, num1,
+ data[i + TOPNUM_HALF].rank, buf2, num2);
+ }
+ else if (num1)
+ {
+ fprintf(fp, "[%2d] %-25s %5dM [%2d] %-25s %6d\n",
+ data[i].rank, buf1, num1,
+ data[i + TOPNUM_HALF].rank, buf2, data[i + TOPNUM_HALF].num);
+ }
+ else
+ {
+ fprintf(fp, "[%2d] %-25s %6d [%2d] %-25s %6d\n",
+ data[i].rank, buf1, data[i].num,
+ data[i + TOPNUM_HALF].rank, buf2, data[i + TOPNUM_HALF].num);
+ }
+ }
+
+ fprintf(fp, "\n");
+ fclose(fp);
+}
+
+
+static inline void
+topusr(acct)
+ ACCT *acct;
+{
+ DATA *p;
+
+ if ((p = findmin(&toplogins))->num < acct->numlogins)
+ {
+ str_ncpy(p->userid, acct->userid, sizeof(p->userid));
+ str_ncpy(p->username, acct->username, sizeof(p->username));
+ p->num = acct->numlogins;
+ }
+
+ if ((p = findmin(&topposts))->num < acct->numposts)
+ {
+ str_ncpy(p->userid, acct->userid, sizeof(p->userid));
+ str_ncpy(p->username, acct->username, sizeof(p->username));
+ p->num = acct->numposts;
+ }
+
+ if ((p = findmin(&topmoney))->num < acct->money)
+ {
+ str_ncpy(p->userid, acct->userid, sizeof(p->userid));
+ str_ncpy(p->username, acct->username, sizeof(p->username));
+ p->num = acct->money;
+ }
+
+ if ((p = findmin(&topgold))->num < acct->gold)
+ {
+ str_ncpy(p->userid, acct->userid, sizeof(p->userid));
+ str_ncpy(p->username, acct->username, sizeof(p->username));
+ p->num = acct->gold;
+ }
+}
+
+
+/*-------------------------------------------------------*/
+/* author : wsyfish.bbs@fpg.m4.ntu.edu.tw */
+/* target : ¯¸¤W¦~Äֲέp */
+/*-------------------------------------------------------*/
+
+
+#define MAX_LINE 16
+
+static int act_age[25]; /* 24ºØ¦~ÄÖ¡BÁ`¦X */
+
+
+static inline void
+count_age(acct, year)
+ ACCT *acct;
+ int year;
+{
+ int age;
+
+ age = year - 11 - acct->year; /* acct->year ¬O¥Á°ê¦~¥÷ */
+
+ /* ¥u²Î­p 10~33 ·³ */
+ if (age >= 10 && age <= 10 + 24 - 1)
+ {
+ act_age[age - 10]++;
+ act_age[24]++;
+ }
+}
+
+
+static void
+fouts(fp, buf, mode)
+ FILE *fp;
+ char buf[], mode;
+{
+ static char state = '0';
+
+ if (state != mode)
+ fprintf(fp, "\033[3%cm", state = mode);
+ if (buf[0])
+ {
+ fprintf(fp, buf);
+ buf[0] = 0;
+ }
+}
+
+
+static inline void
+write_age(fpath, year, month, day)
+ char *fpath;
+ int year, month, day;
+{
+ char buf[256];
+ FILE *fp;
+ int i, j;
+ int age, max, item, maxage, totalage;
+
+ if ((fp = fopen(fpath, "w")) == NULL)
+ {
+ printf("cann't open %s\n", fpath);
+ return;
+ }
+
+ max = maxage = totalage = 0;
+ for (i = 0; i < 24; i++)
+ {
+ totalage += act_age[i] * (i + 10);
+ if (act_age[i] > max)
+ {
+ max = act_age[i];
+ maxage = i;
+ }
+ }
+
+ item = max / MAX_LINE + 1;
+
+ fprintf(fp, "\t\t\t \033[1;33;45m " BBSNAME " ¦~Äֲέp [%02d/%02d/%02d] \033[m\n\n",
+ year % 100, month, day);
+
+ for (i = MAX_LINE + 1; i > 0; i--)
+ {
+ strcpy(buf, " ");
+ for (j = 0; j < 24; j++)
+ {
+ max = item * i;
+ age = act_age[j];
+ if (age && (max > age) && (max - item <= age))
+ {
+ fouts(fp, buf, '7');
+ fprintf(fp, "%-3d", age);
+ }
+ else if (max <= age)
+ {
+ fouts(fp, buf, '4');
+ fprintf(fp, "¢i ");
+ }
+ else
+ strcat(buf, " ");
+ }
+ fprintf(fp, "\n");
+ }
+
+ fprintf(fp, " \033[1;35mùæùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùè \n"
+ " \033[1;32m10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33\n"
+ "\t\t \033[36m¦³®Ä²Î­p¤H¦¸¡G\033[37m%-9d\033[36m¥­§¡¦~ÄÖ¡G\033[37m%d\033[40;0m\n",
+ act_age[24], (int) totalage / (act_age[24] ? act_age[24] : 1));
+
+ fclose(fp);
+}
+
+
+/*-------------------------------------------------------*/
+/* author : wsyfish.bbs@fpg.m4.ntu.edu.tw */
+/* target : ¯¸¤W¬P®y²Î­p */
+/*-------------------------------------------------------*/
+
+
+static int act_star[13]; /* ¤Q¤G¬P®y¡B¨ä¥L */
+
+
+static inline void
+count_star(acct)
+ ACCT *acct;
+{
+ switch (acct->month)
+ {
+ case 1:
+ if (acct->day <= 19)
+ act_star[9]++;
+ else
+ act_star[10]++;
+ break;
+ case 2:
+ if (acct->day <= 18)
+ act_star[10]++;
+ else
+ act_star[11]++;
+ break;
+ case 3:
+ if (acct->day <= 20)
+ act_star[11]++;
+ else
+ act_star[0]++;
+ break;
+ case 4:
+ if (acct->day <= 19)
+ act_star[0]++;
+ else
+ act_star[1]++;
+ break;
+ case 5:
+ if (acct->day <= 20)
+ act_star[1]++;
+ else
+ act_star[2]++;
+ break;
+ case 6:
+ if (acct->day <= 21)
+ act_star[2]++;
+ else
+ act_star[3]++;
+ break;
+ case 7:
+ if (acct->day <= 22)
+ act_star[3]++;
+ else
+ act_star[4]++;
+ break;
+ case 8:
+ if (acct->day <= 22)
+ act_star[4]++;
+ else
+ act_star[5]++;
+ break;
+ case 9:
+ if (acct->day <= 22)
+ act_star[5]++;
+ else
+ act_star[6]++;
+ break;
+ case 10:
+ if (acct->day <= 23)
+ act_star[6]++;
+ else
+ act_star[7]++;
+ break;
+ case 11:
+ if (acct->day <= 22)
+ act_star[7]++;
+ else
+ act_star[8]++;
+ break;
+ case 12:
+ if (acct->day <= 21)
+ act_star[8]++;
+ else
+ act_star[9]++;
+ break;
+ default:
+ act_star[12]++;
+ }
+}
+
+
+static inline void
+write_star(fpath, year, month, day)
+ char *fpath;
+ int year, month, day;
+{
+ FILE *fp;
+ int i, j;
+ int max, item, maxstar;
+
+ char name[13][7] =
+ {
+ "¨d¦Ï®y",
+ "ª÷¤û®y",
+ "Âù¤l®y",
+ "¥¨ÃÉ®y",
+ "·à¤l®y",
+ "³B¤k®y",
+ "¤Ñ¯¯®y",
+ "¤ÑÃÈ®y",
+ "®g¤â®y",
+ "¼¯½~®y",
+ "¤ô²~®y",
+ "Âù³½®y",
+ "¤£¥i¦Ò"
+ };
+
+ char blk[10][3] =
+ {
+ " ", "¢j", "¢k", "¢l", "¢m",
+ "¢n", "¢o", "¢p", "¢i", "¢i",
+ };
+
+
+ if ((fp = fopen(fpath, "w")) == NULL)
+ {
+ printf("cann't open %s\n", fpath);
+ return;
+ }
+
+ max = maxstar = 0;
+
+ for (i = 0; i < 13; i++)
+ {
+ if (act_star[i] > max)
+ {
+ max = act_star[i];
+ maxstar = i;
+ }
+ }
+
+ item = max / 30 + 1;
+
+ fprintf(fp, "\t\t\t \033[1;33;45m " BBSNAME " ¬P®y²Î­p [%02d/%02d/%02d] \033[m\n\n",
+ year % 100, month, day);
+
+ for (i = 0; i < 13; i++)
+ {
+ fprintf(fp, " \033[1;37m%s ", name[i]);
+ fprintf(fp, "%s", i%2 ==1 ? "\033[0;36m" : "\033[0;34m");
+ for (j = 0; j < act_star[i] / item; j++)
+ {
+ fprintf(fp, "%2s", blk[9]);
+ }
+ fprintf(fp, "%2s \033[1;37m%d\033[m\n", blk[(act_star[i] % item) * 10 / item],
+ act_star[i]);
+ }
+
+ fclose(fp);
+}
+
+
+/*-------------------------------------------------------*/
+/* author : BioStar.bbs@micro.bio.ncue.edu.tw */
+/* target : ¯¸¤W©Ê§O²Î­p */
+/*-------------------------------------------------------*/
+
+
+static int act_sex[3]; /* ¤¤©Ê¡B¨k©Ê¡B¤k©Ê */
+
+
+static inline void
+count_sex(acct)
+ ACCT *acct;
+{
+ switch (acct->sex)
+ {
+ case 1:
+ case 2:
+ act_sex[acct->sex]++;
+ break;
+ default:
+ act_sex[0]++;
+ }
+}
+
+
+static inline void
+write_sex(fpath, year, month, day)
+ char *fpath;
+ int year, month, day;
+{
+ FILE *fp;
+ int i, j;
+ int max, item, maxsex;
+
+ char name[3][7] =
+ {
+ " ¤¤©Ê",
+ " ¨k©Ê",
+ " ¤k©Ê"
+ };
+
+ char blk[10][3] =
+ {
+ " ", "¢j", "¢k", "¢l", "¢m",
+ "¢n", "¢o", "¢p", "¢i", "¢i",
+ };
+
+
+ if ((fp = fopen(fpath, "w")) == NULL)
+ {
+ printf("cann't open %s\n", fpath);
+ return;
+ }
+
+ max = maxsex = 0;
+
+ for (i = 0; i < 3; i++)
+ {
+ if (act_sex[i] > max)
+ {
+ max = act_sex[i];
+ maxsex = i;
+ }
+ }
+
+ item = max / 30 + 1;
+
+ fprintf(fp, "\t\t\t \033[1;33;45m " BBSNAME " ©Ê§O²Î­p [%02d/%02d/%02d] \033[m\n\n",
+ year % 100, month, day);
+
+ for (i = 0; i < 3; i++)
+ {
+ fprintf(fp, " \033[1;37m%s ", name[i]);
+ fprintf(fp, "%s", i%2 ==1 ? "\033[0;36m" : "\033[0;34m");
+ for (j = 0; j < act_sex[i] / item; j++)
+ {
+ fprintf(fp, "%2s", blk[9]);
+ }
+ fprintf(fp, "%2s \033[1;37m%d\033[m\n", blk[(act_sex[i] % item) * 10 / item],
+ act_sex[i]);
+ }
+
+ fclose(fp);
+}
+
+/*-------------------------------------------------------*/
+/* author : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/* target : ¯¸¤W¹Ø¬P²Î­p */
+/*-------------------------------------------------------*/
+
+
+static inline void
+write_birthday(fpath, year, month, day)
+ char *fpath;
+ int year, month, day;
+{
+ FILE *fp;
+
+ if (!(fp = fopen(fpath, "w")))
+ return;
+
+ fprintf(fp, "\t\t\t \033[1;33;45m " BBSNAME " ¥»¤é¹Ø¬P [%02d/%02d/%02d] \033[m\n\n",
+ year % 100, month, day);
+
+ fclose(fp);
+}
+
+
+static inline void
+write_birthmon(fpath, year, month, day)
+ char *fpath;
+ int year, month, day;
+{
+ FILE *fp;
+
+ /* §â¥»¤ë¹Ø¬P±µ¦b OUTFILE_BIRTHDAY «á­± */
+
+ if (!(fp = fopen(fpath, "a")))
+ return;
+
+ fprintf(fp, "\n\n\t\t\t \033[1;33;45m " BBSNAME " ¥»¤ë¹Ø¬P [%02d/%02d/%02d] \033[m\n\n",
+ year % 100, month, day);
+
+ f_suck(fp, TMPFILE_BIRTHMON);
+ unlink(TMPFILE_BIRTHMON);
+
+ fclose(fp);
+}
+
+
+static inline void
+check_birth(fpath, acct, year, month, day)
+ char *fpath;
+ ACCT *acct;
+ int year, month, day; /* ¤µ¤Ñ¬O´X¦~´X¤ë´X¤é */
+{
+ static int birthday_num = 0; /* ¦³´X­Ó¤H¤µ¤Ñ¥Í¤é */
+ static int birthmon_num = 0; /* ¦³´X­Ó¤H¥»¤ë¥Í¤é */
+
+ if (acct->month == month)
+ {
+ FILE *fp;
+ char buf[50];
+
+ if (acct->day == day) /* ¥»¤é¹Ø¬P */
+ {
+ if (!(fp = fopen(fpath, "a")))
+ return;
+
+ birthday_num++;
+ merge_id_nick(buf, acct->userid, acct->username);
+
+ fprintf(fp, "[%3d] %-25s ¤W¯¸¦¸¼Æ: %-5d ¤å³¹½g¼Æ: %-5d %2d ·³¥Í¤é\n",
+ birthday_num, buf, acct->numlogins, acct->numposts, year - 11 - acct->year);
+
+ fclose(fp);
+ }
+ else /* ¥»¤ë¹Ø¬P */
+ {
+ if (!(fp = fopen(TMPFILE_BIRTHMON, "a")))
+ return;
+
+ birthmon_num++;
+ merge_id_nick(buf, acct->userid, acct->username);
+
+ fprintf(fp, "[%3d] %-25s ¤W¯¸¦¸¼Æ: %-5d ¤å³¹½g¼Æ: %-5d ¥Í: %02d/%02d\n",
+ birthmon_num, buf, acct->numlogins, acct->numposts, month, acct->day);
+
+ fclose(fp);
+ }
+ }
+}
+
+
+/*-------------------------------------------------------*/
+/* ¥Dµ{¦¡ */
+/*-------------------------------------------------------*/
+
+
+int
+main()
+{
+ char c;
+ int year, month, day;
+ time_t now;
+ struct tm *ptime;
+
+ memset(&toplogins, 0, sizeof(toplogins));
+ memset(&topposts, 0, sizeof(topposts));
+ memset(&topmoney, 0, sizeof(topmoney));
+ memset(&topgold, 0, sizeof(topgold));
+
+ memset(act_age, 0, sizeof(act_age));
+ memset(act_star, 0, sizeof(act_star));
+ memset(act_sex, 0, sizeof(act_sex));
+
+ now = time(NULL);
+ ptime = localtime(&now);
+
+ year = ptime->tm_year;
+ month = ptime->tm_mon + 1;
+ day = ptime->tm_mday;
+
+ write_birthday(OUTFILE_BIRTHDAY, year, month, day);
+
+ for (c = 'a'; c <= 'z'; c++)
+ {
+ char buf[64];
+ struct dirent *de;
+ DIR *dirp;
+
+ sprintf(buf, BBSHOME "/usr/%c", c);
+ chdir(buf);
+
+ if (!(dirp = opendir(".")))
+ continue;
+
+ while (de = readdir(dirp))
+ {
+ ACCT acct;
+ int fd;
+ char *fname;
+
+ fname = de->d_name;
+ if (*fname <= ' ' || *fname == '.')
+ continue;
+
+ sprintf(buf, "%s/.ACCT", fname);
+ if ((fd = open(buf, O_RDONLY)) < 0)
+ continue;
+
+ read(fd, &acct, sizeof(ACCT));
+ close(fd);
+
+ topusr(&acct);
+ count_age(&acct, year);
+ count_star(&acct);
+ count_sex(&acct);
+ check_birth(OUTFILE_BIRTHDAY, &acct, year, month, day);
+ }
+
+ closedir(dirp);
+ }
+
+ write_birthmon(OUTFILE_BIRTHDAY, year, month, day);
+
+ qsort(toplogins, TOPNUM, sizeof(DATA), sort_compare);
+ write_data(OUTFILE_TOPLOGIN, "¤W¯¸¦¸¼Æ±Æ¦æº]", &toplogins);
+
+ qsort(topposts, TOPNUM, sizeof(DATA), sort_compare);
+ write_data(OUTFILE_TOPPOST, "Äé¤ô½g¼Æ­^¶¯º]", &topposts);
+
+ qsort(topmoney, TOPNUM, sizeof(DATA), sort_compare);
+ write_data(OUTFILE_TOPMONEY, "»È¹ô´I¯Î«Ê¯«º]", &topmoney);
+
+ qsort(topgold, TOPNUM, sizeof(DATA), sort_compare);
+ write_data(OUTFILE_TOPGOLD, "ª÷¹ô´I»¨§ì¨gº]", &topgold);
+
+ write_age(OUTFILE_AGE, year, month, day);
+
+ write_star(OUTFILE_STAR, year, month, day);
+
+ write_sex(OUTFILE_SEX, year, month, day);
+
+ return 0;
+}
diff --git a/util/tran/Makefile b/util/tran/Makefile
new file mode 100644
index 0000000..bd62ab8
--- /dev/null
+++ b/util/tran/Makefile
@@ -0,0 +1,66 @@
+# ------------------------------------------------------- #
+# util/tran/Makefile ( NTHU CS MapleBBS Ver 3.10 ) #
+# ------------------------------------------------------- #
+# target : Makefile for ±H«H¡B²Î­p¡B³Æ¥÷¡B¨t²ÎºûÅ@¤u¨ã #
+# create : 01/03/02 #
+# update : / / #
+# ------------------------------------------------------- #
+
+
+# ------------------------------------------------------ #
+# ¤U¦Cªº make rules ¤£»Ý­×§ï #
+# ------------------------------------------------------ #
+
+
+EXE = brd2gem transacct transbrd \
+ ats2bmw ats2brd ats2gem ats2mf ats2pal ats2usr \
+ cola2brd cola2gem cola2post cola2usr \
+ fb2brd fb2gem fb2pal fb2usr \
+ mag2brd mag2gem mag2usr \
+ snap2usr snap2brd \
+ sob2brd sob2gem sob2pal sob2usr \
+ wd2bmw wd2brd wd2gem wd2list wd2mf wd2pal wd2pip wd2usr \
+ windtop2brd windtop2pip windtop2usr
+
+all:
+ @echo "Please enter 'make sys-type', "
+ @echo " make sun : for Sun-OS 4.x and maybe some BSD systems, cc or gcc"
+ @echo " make linux : for Linux"
+ @echo " make solaris : for Sun-OS 5.x gcc"
+ @echo " make sol-x86 : for Solaris 7 x86"
+ @echo " make freebsd : for BSD 4.4 systems"
+ @echo " make bsd : for BSD systems, cc or gcc, if not in the above lists"
+ @echo " make cygwin : for Microsoft Windows and Cygwin gcc"
+
+sun:
+ @$(MAKE) CC=gcc CFLAGS="-O2 -pipe -fomit-frame-pointer -Wunused -I../../include" LDFLAGS="-s -L../../lib -ldao" $(EXE)
+
+linux:
+ @$(MAKE) CC=gcc CFLAGS="-O2 -pipe -I../../include -fomit-frame-pointer -Wunused" LDFLAGS="-s -L../../lib -ldao" $(EXE)
+
+solaris:
+ @$(MAKE) CC=gcc CFLAGS="-O2 -pipe -I../../include -fomit-frame-pointer -Wunused" LDFLAGS="-s -L../../lib -ldao -lsocket -lnsl -L/usr/ucblib -lucb" $(EXE)
+
+sol-x86:
+ @$(MAKE) CC=gcc CFLAGS="-O2 -I../../include -fomit-frame-pointer -Wunused" LDFLAGS="-s -L../../lib -ldao -lsocket -lnsl" $(EXE)
+
+freebsd:
+ @$(MAKE) CC=gcc CFLAGS="-O2 -pipe -I../../include -fomit-frame-pointer -Wunused" LDFLAGS="-s -L../../lib -ldao" $(EXE)
+
+bsd:
+ @$(MAKE) CC=gcc CFLAGS="-O2 -pipe -I../../include -fomit-frame-pointer -Wunused" LDFLAGS="-s -L../../lib -ldao" $(EXE)
+
+cygwin:
+ @$(MAKE) CC=gcc CFLAGS="-O2 -pipe -I../../include -fomit-frame-pointer -Wunused" LDFLAGS="-s -L../../lib -ldao" $(EXE)
+
+
+.c: ; $(CC) -o $@ $@.c $(CFLAGS) $(LDFLAGS)
+
+
+install: $(EXE)
+# ¤£½Æ»s¨ì bin/ ¤U
+# install -m 0700 $? $(HOME)/bin
+ @echo "ok!"
+
+clean:
+ rm -f $(EXE) *.exe *.o *~
diff --git a/util/tran/ats.h b/util/tran/ats.h
new file mode 100644
index 0000000..89a98b6
--- /dev/null
+++ b/util/tran/ats.h
@@ -0,0 +1,274 @@
+/*-------------------------------------------------------*/
+/* util/tran/ats.h */
+/*-------------------------------------------------------*/
+/* target : ATS ¦Ü Maple 3.02 Âà´« */
+/* create : 02/10/26 */
+/* author : ernie@micro8.ee.nthu.edu.tw */
+/* modify : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#if 0
+
+ 1. ³]©w OLD_BBSHOME¡BFN_PASSWD¡BFN_BOARD
+ 2. ­×§ï©Ò¦³ªº old struct
+
+ 3. ¥²¶·¦b brd Âà§¹¤~¥i¥HÂà´« gem
+ 4. ¥²¶·¦b usr ¤Î brd ³£Âà§¹¤~¥i¥HÂà´« mf
+ 5. ¥²¶·¦b usr ¤Î brd ³£Âà§¹¤~¥i¥HÂà´« pal
+ 6. ¥²¶·¦b usr Âà§¹¤~¥i¥HÂà´« bmw
+ 7. «ØÄ³Âà´«¶¶§Ç¬° usr -> brd -> gem -> mf -> pal -> bmw
+
+#endif
+
+
+#include "bbs.h"
+
+/* --------------------- ½Ðª`·N ³o¤­ªÌ¥u¯à¾Ü¤@©w¸q ---------------------- */
+#define NEW_STATION /* ©w¸q¬° ·s«Ø/¼Ð·Ç ¯¸¥x */
+#undef OLD_ATSVERSION /* ©w¸q¬°Âªº¨È¯¸ª©¥» (1.20a ¥H«á) */
+ /* ±ýÅýª©¥»¨Ï¥Î³Ì¤p¨Ï¥ÎªÌ¸ê®Æµ²ºc */
+ /* ¥²»Ý°õ¦æÂà´«µ{¦¡ single_multi_st */
+#undef TRANS_FROM_SOB /* ©w¸q¬°¨FÅyÂà´« */
+#undef TRANS_FROM_FB3 /* ©w¸q¬°¤õ³¾Âà´« */
+#undef TRANS_FROM_COLA /* ©w¸q¬°¥i¼ÖÂà´« */
+/* ---------------------------------------------------------------------- */
+
+#ifdef TRANS_FROM_FB3
+ #undef ENCPASSLEN /* ©w¸q­Y¤õ³¾Âà´« ¦³µL³]©w ENCPASSLEN */
+#endif
+#ifndef OLD_ATSVERSION
+#undef MIN_USEREC_STRUCT /* ©w¸q¨Ï¥Î³Ì¤p¨Ï¥ÎªÌ¸ê®Æµ²ºc ¦¹¥\¯à */
+#endif /* ¤£«ØÄ³¨Ï¥Î ·|¥¢¥h³\¦h¥\¯à °£«D¯¸ */
+ /* ¥x¦³ÄY­«µwÅé­­¨î ¥B¤£·Q¶]¤Ó¦h¥\¯à */
+
+
+#define OLD_BBSHOME "/home/bbs/bbsrs" /* SOB */
+#define FN_PASSWD "/home/bbs/bbsrs/.PASSWDS" /* SOB */
+#define FN_BOARD "/home/bbs/bbsrs/.BOARDS" /* SOB */
+
+
+#undef HAVE_PERSONAL_GEM /* SOB ¬O¨S¦³­Ó¤HºëµØ°Ïªº */
+
+
+#define ASTRLEN 80 /* Length of most string data */
+#define ABTLEN 48 /* Length of board title */
+#define ATTLEN 72 /* Length of title */
+#define ANAMELEN 40 /* Length of username/realname */
+#define AFNLEN 33 /* Length of filename */
+#define AIDLEN 12 /* Length of bid/uid */
+
+ #define APASSLEN 14 /* Length of encrypted passwd field for ATS */
+
+#define AREGLEN 38 /* Length of registration data */
+
+/* ----------------------------------------------------- */
+/* .PASSWDS struct : 1024 bytes */
+/* ----------------------------------------------------- */
+
+struct userec {
+ char userid[AIDLEN + 1];
+ char realname[20];
+ char username[24];
+ char passwd[APASSLEN];
+ uschar uflag;
+ usint userlevel;
+ unsigned long int numlogins;
+ unsigned long int numposts;
+ time_t firstlogin;
+ time_t lastlogin;
+ char lasthost[80];
+ char remoteuser[8];
+ char email[50];
+ char address[50];
+ char justify[AREGLEN + 1];
+ uschar month;
+ uschar day;
+ uschar year;
+ uschar sex;
+ uschar state;
+
+ int havemoney;
+
+#ifndef MIN_USEREC_STRUCT
+ int havetoolsnumber;
+ int havetools[20];
+ int addexp;
+ usint nowlevel;
+ char working[20];
+
+ uschar hp;
+ uschar str;
+ uschar mgc;
+ uschar skl;
+ uschar spd;
+ uschar luk;
+ uschar def;
+ uschar mdf;
+
+ uschar spcskl[6];
+ uschar wepnlv[2][10];
+ uschar curwepnlv[2][1];
+ uschar curwepnatt;
+ uschar curwepnhit;
+ uschar curwepnweg;
+ uschar curwepnspc[4];
+
+ char lover[AIDLEN+1];
+ char commander;
+ char belong[21];
+ char curwepclass[10];
+ char class[7];
+#endif
+
+#ifndef NO_USE_MULTI_STATION
+ char station[AIDLEN+1];
+#endif
+
+#ifndef MIN_USEREC_STRUCT
+ char classsex;
+ char wephavespc[5];
+
+ char cmdrname[AIDLEN+1];
+ char class_spc_skll[6];
+#endif
+
+ usint welcomeflag;
+
+#ifndef MIN_USEREC_STRUCT
+ int win;
+ int lost;
+ int test_exp;
+#endif
+
+ char tty_name[20];
+ char extra_mode[10];
+
+#ifndef MIN_USEREC_STRUCT
+ char class_spc_test[32];
+ char mov;
+ char toki_level;
+
+ int will_value;
+ int effect_value;
+ int belive_value;
+ int leader_value;
+
+ char action_value;
+#endif
+
+#ifndef NO_USE_MULTI_STATION
+ char station_member[40];
+#endif
+
+ uschar now_stno;
+ usint good_posts;
+
+#ifdef CHANGE_USER_MAIL_LIMIT
+ int max_mail_number;
+ int max_mail_kbytes;
+#endif
+
+#ifdef TRANS_FROM_COLA
+ usint staytime;
+ #ifdef CHANGE_USER_MAIL_LIMIT
+ int backup_int[41];
+ #else
+ int backup_int[43];
+ #endif
+#else
+ #ifdef CHANGE_USER_MAIL_LIMIT
+ int backup_int[42];
+ #else
+ int backup_int[44];
+ #endif
+#endif
+
+#ifndef MIN_USEREC_STRUCT
+ int ara_money;
+#endif
+
+#ifdef TRANS_FROM_COLA
+ char blood;
+ char normal_post;
+ char backup_char[118];
+#else
+ char normal_post;
+ char backup_char[119];
+#endif
+
+#ifndef MIN_USEREC_STRUCT
+ int turn;
+#endif
+};
+typedef struct userec userec;
+
+/* ----------------------------------------------------- */
+/* DIR of board struct */
+/* ----------------------------------------------------- */
+
+#ifndef TRANS_FROM_FB3 /* struct size = 256 bytes */
+ #ifndef TRANS_FROM_COLA
+struct fileheader {
+ char filename[AFNLEN-1]; /* M.109876543210.A */
+ char report; /* Dopin : ·s¨î´£³ø */
+ char savemode; /* file save mode */
+ char owner[AIDLEN + 2]; /* uid[.] */
+ char date[6]; /* [02/02] or space(5) */
+ char title[ATTLEN];
+ uschar goodpost; /* ±ÀÂˤ峹 */
+ uschar filemode; /* must be last field @ boards.c */
+};
+ #else
+struct fileheader { /* For Cola BBS */
+ char filename[ASTRLEN];
+ char owner[ASTRLEN];
+ char title[ASTRLEN];
+ char date[6]; /* ¸É¤W for ATS/SOB */
+ char savemode;
+ uschar filemode;
+ char report;
+ uschar goodpost;
+ char backup_char[6]; /* ¨ì³o¸Ì */
+};
+ #endif
+#else
+struct fileheader { /* This structure is used to hold data in */
+ char filename[ASTRLEN-2]; /* the DIR files */
+ char report; /* ¨È¯¸´£³ø */
+ char savemode; /* file save mode */
+ char owner[ASTRLEN-6];
+ char date[6];
+ char title[ASTRLEN];
+ unsigned level;
+ unsigned char accessed[10];
+ uschar goodpost; /* ±ÀÂˤ峹 */
+ uschar filemode; /* must be last field @ boards.c */
+};
+#endif
+
+typedef struct fileheader fileheader;
+
+
+/* ----------------------------------------------------- */
+/* BOARDS struct : Standard 656 bytes */
+/* ----------------------------------------------------- */
+
+struct boardheader {
+ char brdname[(AIDLEN+1)*2]; /* bid */
+ char title[ABTLEN + 1];
+ char BM[AIDLEN * 3 + 3]; /* BMs' uid, token '/' */
+ char pad[11];
+ time_t bupdate; /* note update time */
+ char pad2[3];
+ uschar bvote; /* Vote flags */
+ time_t vtime; /* Vote close time */
+ usint level;
+ char document[128 * 3]; /* add extra document */
+ char station[16];
+ char sysop[16];
+ char pastbrdname[16];
+ char yankflags[16];
+ char backup[64];
+};
+typedef struct boardheader boardheader;
diff --git a/util/tran/ats2bmw.c b/util/tran/ats2bmw.c
new file mode 100644
index 0000000..3cd1ee4
--- /dev/null
+++ b/util/tran/ats2bmw.c
@@ -0,0 +1,143 @@
+/*-------------------------------------------------------*/
+/* util/transbmw.c */
+/*-------------------------------------------------------*/
+/* target : WD ¦Ü Maple 3.02 ¤ô²y°O¿ýÂà´« */
+/* create : 02/01/22 */
+/* update : / / */
+/* author : itoc.bbs@bbs.ee.nctu.edu.tw */
+/*-------------------------------------------------------*/
+/* syntax : transbmw */
+/*-------------------------------------------------------*/
+
+
+#if 0
+
+ 1. ­×§ï transbmw()
+
+ ps. ¨Ï¥Î«e½Ð¥ý¦æ³Æ¥÷¡Ause on ur own risk. µ{¦¡©å¦H½Ð¥]²[ :p
+ ps. ·PÁ lkchu ªº Maple 3.02 for FreeBSD
+
+#endif
+
+
+#include "ats.h"
+
+
+/* ----------------------------------------------------- */
+/* 3.02 functions */
+/* ----------------------------------------------------- */
+
+
+static void
+_mail_self(userid, fpath, owner, title) /* itoc.011115: ±HÀÉ®×µ¹¦Û¤v */
+ char *userid; /* ¦¬¥óªÌ */
+ char *fpath; /* Àɮ׸ô®| */
+ char *owner; /* ±H¥ó¤H */
+ char *title; /* ¶l¥ó¼ÐÃD */
+{
+ HDR fhdr;
+ char folder[64];
+
+ usr_fpath(folder, userid, FN_DIR);
+ close(hdr_stamp(folder, HDR_LINK, &fhdr, fpath));
+ str_ncpy(fhdr.owner, owner, sizeof(fhdr.owner));
+ str_ncpy(fhdr.title, title, sizeof(fhdr.title));
+ fhdr.xmode = 0;
+ rec_add(folder, &fhdr, sizeof(fhdr));
+}
+
+
+/* ----------------------------------------------------- */
+/* Âà´«¥Dµ{¦¡ */
+/* ----------------------------------------------------- */
+
+
+static void
+transbmw(userid)
+ char *userid;
+{
+ ACCT acct;
+ int fd;
+ char buf[64],buf2[64],buf3[64];
+
+
+ printf("¶}©l³Æ¥÷ %-15s ¤ô²y°O¿ý¡®¦n¤Í¦W³æ¡®§Úªº³Ì·R\n",acct.userid);
+ /* sob ªº usr ¥Ø¿ý¦³¤À¤j¤p¼g¡A©Ò¥H­n¥ý¨ú±o¤j¤p¼g */
+ usr_fpath(buf, userid, FN_ACCT);
+ if ((fd = open(buf, O_RDONLY)) >= 0)
+ {
+ read(fd, &acct, sizeof(ACCT));
+ close(fd);
+ }
+ else
+ {
+ return;
+ }
+
+ sprintf(buf, OLD_BBSHOME"/home/%s/writelog", acct.userid); /* ªº¤ô²y°O¿ý */
+
+ if (dashf(buf))
+ _mail_self(acct.userid, buf, "[³Æ¥÷]", "¤ô²y°O¿ý");
+
+ sprintf(buf2, OLD_BBSHOME"/home/%s/overrides", acct.userid); /* ªº¦n¤Í¦W³æ */
+
+ if (dashf(buf2))
+ _mail_self(acct.userid, buf2, "[³Æ¥÷]", "¦n¤Í¦W³æ");
+
+ sprintf(buf3, OLD_BBSHOME"/home/%s/favor_boards", acct.userid); /* ªº§Úªº³Ì·R */
+
+ if (dashf(buf3))
+ _mail_self(acct.userid, buf3, "[³Æ¥÷]", "§Úªº³Ì·R");
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ char c;
+ char buf[64];
+ struct dirent *de;
+ DIR *dirp;
+
+ /* argc == 1 Âà¥þ³¡¨Ï¥ÎªÌ */
+ /* argc == 2 Âà¬Y¯S©w¨Ï¥ÎªÌ */
+
+ if (argc > 2)
+ {
+ printf("Usage: %s [target_user]\n", argv[0]);
+ exit(-1);
+ }
+
+ chdir(BBSHOME);
+
+ if (argc == 2)
+ {
+ transbmw(argv[1]);
+ exit(1);
+ }
+
+ /* Âà´«¨Ï¥ÎªÌ¤ô²y°O¿ý */
+ for (c = 'a'; c <= 'z'; c++)
+ {
+ sprintf(buf, "usr/%c", c);
+
+ if (!(dirp = opendir(buf)))
+ continue;
+
+ while (de = readdir(dirp))
+ {
+ char *str;
+
+ str = de->d_name;
+ if (*str <= ' ' || *str == '.')
+ continue;
+
+ transbmw(str);
+ }
+
+ closedir(dirp);
+ }
+ return 0;
+}
diff --git a/util/tran/ats2brd.c b/util/tran/ats2brd.c
new file mode 100644
index 0000000..3904a9e
--- /dev/null
+++ b/util/tran/ats2brd.c
@@ -0,0 +1,233 @@
+/*-------------------------------------------------------*/
+/* util/transbrd.c */
+/*-------------------------------------------------------*/
+/* target : Maple Sob 2.36 ¦Ü Maple 3.02 ¬ÝªOÂà´« */
+/* .BOARDS => .BRD */
+/* create : / / */
+/* update : 98/06/14 */
+/* author : ernie@micro8.ee.nthu.edu.tw */
+/* modify : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+/* syntax : transbrd [target_board] */
+/*-------------------------------------------------------*/
+
+#if 0
+
+ 1. ­×§ï struct boardheader ¤Î transbrd()
+ (boardheader ¨âª©©w¸qªº¦r¦êªø«×¤£¤@¡A½Ð¦Û¦æ´«¦¨¼Æ¦r)
+ 2. §ë²¼¤£Âà´«
+ 3. ¶iªOµe­± copy
+ 4. ¦p¦³»Ý­n½Ð chmod 644 `find PATH -perm 600`
+ 5. ¶} gem ¥Ø¿ý gem/target_board/? ¦ý¤£Âà´« gem
+ 6. ¤£·|§ó·s bshm¡A¨Ï¥Î«á½Ð¦Û¦æ§ó·s
+ 7. Âà´««á½Ð¤â°Ê³]¬ÝªOÅv­­
+
+ ps. Use on ur own risk.
+
+#endif
+
+
+#include "ats.h"
+
+
+static time_t
+trans_hdr_chrono(filename)
+ char *filename;
+{
+ char time_str[11];
+
+ /* M.1087654321.A ©Î M.987654321.A */
+ str_ncpy(time_str, filename + 2, filename[2] == '1' ? 11 : 10);
+
+ return (time_t) atoi(time_str);
+}
+
+
+static void
+trans_hdr_stamp(folder, t, hdr, fpath)
+ char *folder;
+ time_t t;
+ HDR *hdr;
+ char *fpath;
+{
+ FILE *fp;
+ char *fname, *family;
+ int rc;
+
+ fname = fpath;
+ while (rc = *folder++)
+ {
+ *fname++ = rc;
+ if (rc == '/')
+ family = fname;
+ }
+ fname = family + 1;
+ *fname++ = '/';
+ *fname++ = 'A';
+
+ for (;;)
+ {
+ *family = radix32[t & 31];
+ archiv32(t, fname);
+
+ if (fp = fopen(fpath, "r"))
+ {
+ fclose(fp);
+ t++;
+ }
+ else
+ {
+ memset(hdr, 0, sizeof(HDR));
+ hdr->chrono = t;
+ str_stamp(hdr->date, &hdr->chrono);
+ strcpy(hdr->xname, --fname);
+ break;
+ }
+ }
+}
+
+
+/* ----------------------------------------------------- */
+/* Âà´«¥Dµ{¦¡ */
+/* ----------------------------------------------------- */
+
+
+static void
+transbrd(bh)
+ boardheader *bh;
+{
+ static time_t stamp = 0;
+
+ int fd;
+ char index[64], folder[64], buf[64], fpath[64];
+ fileheader fh;
+ HDR hdr;
+ BRD newboard;
+ time_t chrono;
+
+ printf("Âà´« %s ¬ÝªO\n", bh->brdname);
+
+ brd_fpath(buf, bh->brdname, NULL);
+ if (dashd(buf))
+ {
+ printf("%s ¤w¸g¦³¦¹¬ÝªO\n", bh->brdname);
+ return;
+ }
+
+ if (!stamp)
+ time(&stamp);
+
+ /* Âà´« .BRD */
+
+ memset(&newboard, 0, sizeof(newboard));
+ str_ncpy(newboard.brdname, bh->brdname, sizeof(newboard.brdname));
+ str_ncpy(newboard.class, bh->title, sizeof(newboard.class));
+ str_ncpy(newboard.title, bh->title, sizeof(newboard.title));
+
+ str_ncpy(newboard.BM, bh->BM, sizeof(newboard.BM));
+ newboard.bstamp = stamp++;
+ newboard.battr = BRD_NOTRAN; /* ¹w³]¤£Âà«H */
+ newboard.readlevel = 0;
+ newboard.postlevel = PERM_POST;
+
+ rec_add(FN_BRD, &newboard, sizeof(newboard)); /* §O§Ñ¤F¥Î brd2gem.c ¨ÓÂà´« Class */
+
+ /* ¶}·s¥Ø¿ý */
+
+ sprintf(fpath, "gem/brd/%s", newboard.brdname);
+ mak_dirs(fpath);
+ mak_dirs(fpath + 4);
+
+ /* Âà´«¶iªOµe­± */
+
+ sprintf(buf, OLD_BBSHOME "/boards/%s/notes", bh->brdname);
+
+ if (dashf(buf))
+ {
+ brd_fpath(fpath, newboard.brdname, FN_NOTE);
+ f_cp(buf, fpath, O_TRUNC);
+ }
+
+ /* Âà´«¤å³¹ */
+
+ sprintf(index, OLD_BBSHOME "/boards/%s/.DIR", bh->brdname); /* 廼 .DIR */
+ brd_fpath(folder, newboard.brdname, ".DIR"); /* ·sªº .DIR */
+
+ if ((fd = open(index, O_RDONLY)) >= 0)
+ {
+ while (read(fd, &fh, sizeof(fh)) == sizeof(fh))
+ {
+ sprintf(buf, OLD_BBSHOME "/boards/%s/%s", bh->brdname, fh.filename);
+ if (dashf(buf)) /* ¤å³¹Àɮצb¤~°µÂà´« */
+ {
+ /* Âà´«¤å³¹ .DIR */
+ memset(&hdr, 0, sizeof(HDR));
+ chrono = trans_hdr_chrono(fh.filename);
+ trans_hdr_stamp(folder, chrono, &hdr, fpath);
+ str_ncpy(hdr.owner, fh.owner, sizeof(hdr.owner));
+ str_ansi(hdr.title, fh.title, sizeof(hdr.title));
+ hdr.xmode = (fh.filemode & 0x2) ? POST_MARKED : 0;
+ rec_add(folder, &hdr, sizeof(HDR));
+
+ /* «þ¨©ÀÉ®× */
+ f_cp(buf, fpath, O_TRUNC);
+ }
+ }
+ close(fd);
+ }
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int fd;
+ int count=0;
+ boardheader bh;
+
+ /* argc == 1 Âà¥þ³¡ªO */
+ /* argc == 2 Âà¬Y¯S©wªO */
+
+ if (argc > 2)
+ {
+ printf("Usage: %s [target_board]\n", argv[0]);
+ exit(-1);
+ }
+
+ chdir(BBSHOME);
+
+ if (!dashf(FN_BOARD))
+ {
+ printf("ERROR! Can't open " FN_BOARD "\n");
+ exit(-1);
+ }
+ if (!dashd(OLD_BBSHOME "/boards"))
+ {
+ printf("ERROR! Can't open " OLD_BBSHOME "/boards\n");
+ exit(-1);
+ }
+
+ if ((fd = open(FN_BOARD, O_RDONLY)) >= 0)
+ {
+ while (read(fd, &bh, sizeof(bh)) == sizeof(bh))
+ {
+ if (argc == 1)
+ {
+ transbrd(&bh);
+ count++;
+ }
+ else if (!strcmp(bh.brdname, argv[1]))
+ {
+ transbrd(&bh);
+ count++;
+ exit(1);
+ }
+ }
+ close(fd);
+ }
+
+ printf("\n\nÁ`¦@Âà´« %d ­Ó¬ÝªO\n\n",count);
+ exit(0);
+}
diff --git a/util/tran/ats2gem.c b/util/tran/ats2gem.c
new file mode 100644
index 0000000..691f88f
--- /dev/null
+++ b/util/tran/ats2gem.c
@@ -0,0 +1,250 @@
+/*-------------------------------------------------------*/
+/* util/transman.c */
+/*-------------------------------------------------------*/
+/* target : Maple Sob 2.36 ¦Ü Maple 3.02 ºëµØ°ÏÂà´« */
+/* create : 98/06/15 */
+/* update : 02/10/26 */
+/* author : ernie@micro8.ee.nthu.edu.tw */
+/* modify : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+/* syntax : transman [target_board] */
+/*-------------------------------------------------------*/
+
+
+#if 0
+
+ 1. µ{¦¡¤£¶}¥Ø¿ý¡A¨Ï¥Î«e¥ý½T©w gem/target_board/? ¥Ø¿ý¦s¦b
+ if not¡A¥ý¶}·sªO or transbrd
+ 2. ¥uÂà M.*.A ¤Î D.*.A¡A¨ä¥L link ¤£Âà´«
+ 3. ¦p¦³»Ý­n½Ð¥ý chmod 644 `find PATH -perm 600`
+
+ ps. User on ur own risk.
+
+#endif
+
+
+#include "ats.h"
+
+
+/* ----------------------------------------------------- */
+/* Âà´«ºëµØ°Ï */
+/* ----------------------------------------------------- */
+
+
+static time_t
+trans_hdr_chrono(filename)
+ char *filename;
+{
+ char time_str[11];
+
+ /* M.1087654321.A ©Î M.987654321.A */
+ str_ncpy(time_str, filename + 2, filename[2] == '1' ? 11 : 10);
+
+ return (time_t) atoi(time_str);
+}
+
+
+static void
+trans_man_stamp(folder, token, hdr, fpath, time)
+ char *folder;
+ int token;
+ HDR *hdr;
+ char *fpath;
+ time_t time;
+{
+ char *fname, *family;
+ int rc;
+
+ fname = fpath;
+ while (rc = *folder++)
+ {
+ *fname++ = rc;
+ if (rc == '/')
+ family = fname;
+ }
+ if (*family != '.')
+ {
+ fname = family;
+ family -= 2;
+ }
+ else
+ {
+ fname = family + 1;
+ *fname++ = '/';
+ }
+
+ *fname++ = token;
+
+ *family = radix32[time & 31];
+ archiv32(time, fname);
+
+ if (rc = open(fpath, O_WRONLY | O_CREAT | O_EXCL, 0600))
+ {
+ memset(hdr, 0, sizeof(HDR));
+ hdr->chrono = time;
+ str_stamp(hdr->date, &hdr->chrono);
+ strcpy(hdr->xname, --fname);
+ close(rc);
+ }
+ return;
+}
+
+
+/* ----------------------------------------------------- */
+/* Âà´«¥Dµ{¦¡ */
+/* ----------------------------------------------------- */
+
+
+static void
+transman(index, folder)
+ char *index, *folder;
+{
+ static int count = 100;
+
+ int fd;
+ char *ptr, buf[1024], fpath[64];
+ fileheader fh;
+ HDR hdr;
+ time_t chrono;
+
+ if ((fd = open(index, O_RDONLY)) >= 0)
+ {
+ while (read(fd, &fh, sizeof(fh)) == sizeof(fh))
+ {
+ strcpy(buf, index);
+ ptr = strrchr(buf, '/') + 1;
+ strcpy(ptr, fh.filename);
+
+ if (*fh.filename == 'M' && dashf(buf)) /* ¥uÂà M.xxxx.A ¤Î D.xxxx.a */
+ {
+ /* Âà´«¤å³¹ .DIR */
+ memset(&hdr, 0, sizeof(HDR));
+ chrono = trans_hdr_chrono(fh.filename);
+ trans_man_stamp(folder, 'A', &hdr, fpath, chrono);
+ str_ncpy(hdr.owner, fh.owner, sizeof(hdr.owner));
+ str_ncpy(hdr.title, fh.title + 3, sizeof(hdr.title));
+
+ hdr.xmode = 0;
+ if (strstr(fh.title + 3, "[Hide]"))
+ hdr.xmode |= GEM_RESTRICT;
+
+ if (strstr(fh.title + 3, "[ÁôÂÃ]"))
+ hdr.xmode |= GEM_RESTRICT;
+
+ rec_add(folder, &hdr, sizeof(HDR));
+
+ /* «þ¨©ÀÉ®× */
+ f_cp(buf, fpath, O_TRUNC);
+ }
+ else if (*fh.filename == 'D' && dashd(buf))
+ {
+ char sub_index[512];
+
+ /* Âà´«¤å³¹ .DIR */
+ memset(&hdr, 0, sizeof(HDR));
+ chrono = ++count; /* WD ªº¥Ø¿ý©R¦W¤ñ¸û©_©Ç¡A¥u¦n¦Û¤vµ¹¼Æ¦r */
+ trans_man_stamp(folder, 'F', &hdr, fpath, chrono);
+ hdr.xmode = GEM_FOLDER;
+ if (strstr(fh.title + 3, "[Hide]"))
+ hdr.xmode |= GEM_RESTRICT;
+ str_ncpy(hdr.owner, fh.owner, sizeof(hdr.owner));
+ str_ncpy(hdr.title, fh.title + 3, sizeof(hdr.title));
+ rec_add(folder, &hdr, sizeof(HDR));
+
+ /* recursive ¶i¥hÂà´«¤l¥Ø¿ý */
+ strcpy(sub_index, buf);
+ ptr = strrchr(sub_index, '/') + 1;
+ sprintf(ptr, "%s/.DIR", fh.filename);
+ transman(sub_index, fpath);
+ }
+ }
+ close(fd);
+ }
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int fd,count=0,i;
+ char *brdname, index[64], folder[64];
+ char bbname[30][30];
+ boardheader bh;
+
+ /* argc == 1 Âà¥þ³¡ªO */
+ /* argc == 2 Âà¬Y¯S©wªO */
+
+ if (argc > 2)
+ {
+ printf("Usage: %s [target_board]\n", argv[0]);
+ exit(-1);
+ }
+
+ chdir(BBSHOME);
+
+ if (!dashf(FN_BOARD))
+ {
+ printf("ERROR! Can't open " FN_BOARD "\n");
+ exit(-1);
+ }
+ if (!dashd(OLD_BBSHOME "/man/boards"))
+ {
+ printf("ERROR! Can't open " OLD_BBSHOME "/man/boards\n");
+ exit(-1);
+ }
+
+ if (argc == 1)
+ {
+ if ((fd = open(FN_BOARD, O_RDONLY)) >= 0)
+ {
+ while (read(fd, &bh, sizeof(bh)) == sizeof(bh))
+ {
+ brdname = bh.brdname;
+
+ sprintf(folder, "gem/brd/%s", brdname);
+ if (!dashd(folder))
+ {
+ printf("ERROR! %s not exist. New it first.\n", folder);
+ strcpy(bbname[count] , brdname );
+ count++;
+ continue;
+ }
+
+ sprintf(index, OLD_BBSHOME "/man/boards/%s/.DIR", brdname);
+ sprintf(folder, "gem/brd/%s/%s", brdname, FN_DIR);
+
+ printf("Âà´« %s ºëµØ°Ï\n", brdname);
+ transman(index, folder);
+ }
+ close(fd);
+ }
+ }
+ else
+ {
+ brdname = argv[1];
+
+ sprintf(folder, "gem/brd/%s", brdname);
+ if (!dashd(folder))
+ {
+ printf("ERROR! %s not exist. New it first.\n", folder);
+ exit(-1);
+ }
+
+ sprintf(index, OLD_BBSHOME "/man/boards/%s/.DIR", brdname);
+ sprintf(folder, "gem/brd/%s/%s", brdname, FN_DIR);
+
+ printf("Âà´« %s ºëµØ°Ï\n", brdname);
+ transman(index, folder);
+
+ exit(1);
+ }
+
+ printf("\n\n¤£¦s¦bºëµØ°Ï¡A½ÐÀˬd¡C\n\n");
+ for(i=0;i<count;i++)
+ printf("%s\n",bbname[i]);
+
+ printf("\n");
+ exit(0);
+}
diff --git a/util/tran/ats2mf.c b/util/tran/ats2mf.c
new file mode 100644
index 0000000..a4630cf
--- /dev/null
+++ b/util/tran/ats2mf.c
@@ -0,0 +1,173 @@
+/*-------------------------------------------------------*/
+/* util/transfavor.c */
+/*-------------------------------------------------------*/
+/* target : WD ¦Ü Maple 3.02 §Úªº³Ì·RÂà´« */
+/* create : 01/09/15 */
+/* update : / / */
+/* author : itoc.bbs@bbs.ee.nctu.edu.tw */
+/*-------------------------------------------------------*/
+/* syntax : transfavor */
+/*-------------------------------------------------------*/
+
+
+#if 0
+
+ 1. ­×§ï transmf()
+ 2. Âà´«¦n¤Í¦W³æ¤§«e¡A±z¥²¶·¥ýÂà´«§¹¬ÝªO¤Î¨Ï¥ÎªÌ¡C
+
+ ps. ¨Ï¥Î«e½Ð¥ý¦æ³Æ¥÷¡Ause on ur own risk. µ{¦¡©å¦H½Ð¥]²[ :p
+ ps. ·PÁ lkchu ªº Maple 3.02 for FreeBSD
+
+#endif
+
+
+#include "ats.h"
+
+#ifdef MY_FAVORITE
+
+
+/* ----------------------------------------------------- */
+/* 3.02 functions */
+/* ----------------------------------------------------- */
+
+
+static void
+_mf_fpath(fpath, userid, fname)
+ char *fpath;
+ char *userid; /* lower ID */
+ char *fname;
+{
+ if (fname)
+ sprintf(fpath, "usr/%c/%s/MF/%s", userid[0], userid, fname);
+ else
+ sprintf(fpath, "usr/%c/%s/MF", userid[0], userid);
+}
+
+
+/* ----------------------------------------------------- */
+/* Âà´«¥Dµ{¦¡ */
+/* ----------------------------------------------------- */
+
+
+static void
+transmf(userid)
+ char *userid;
+{
+ ACCT acct;
+ FILE *fp;
+ int fd, num;
+ char fpath[64], buf[64];
+ char *str, brdname[IDLEN + 1];
+ MF mf;
+
+ /* «Ø¥ß¥Ø¿ý */
+ _mf_fpath(fpath, userid, NULL);
+ mkdir(fpath, 0700);
+
+ /* sob ªº usr ¥Ø¿ý¦³¤À¤j¤p¼g¡A©Ò¥H­n¥ý¨ú±o¤j¤p¼g */
+ usr_fpath(buf, userid, FN_ACCT);
+ if ((fd = open(buf, O_RDONLY)) >= 0)
+ {
+ read(fd, &acct, sizeof(ACCT));
+ close(fd);
+ }
+ else
+ {
+ return;
+ }
+
+ sprintf(buf, OLD_BBSHOME"/home/%s/favor_boards", acct.userid); /* ªº§Úªº³Ì·R */
+
+ printf("Âà´« %s ¡G§Úªº³Ì·R\n",acct.userid);
+
+ if (!(fp = fopen(buf, "r")))
+ return;
+
+ _mf_fpath(fpath, userid, FN_MF);
+ num = 0;
+
+ while (fgets(brdname, IDLEN + 1, fp))
+ {
+ for (str = brdname; *str; str++)
+ {
+ if (*str <= ' ')
+ {
+ *str = '\0';
+ break;
+ }
+ }
+
+ brd_fpath(buf, brdname, NULL);
+ if (dashd(buf)) /* ªº½T¦³³o­ÓªO */
+ {
+ mf.chrono = ++num;
+ mf.mftype = MF_BOARD;
+ str_ncpy(mf.xname, brdname, sizeof(mf.xname));
+ mf.title[0] = '\0'; /* ¬ÝªO±¶®|¨S¦³ mf.title */
+ rec_add(fpath, &mf, sizeof(MF));
+ }
+ }
+
+ fclose(fp);
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ char c;
+ char buf[64];
+ struct dirent *de;
+ DIR *dirp;
+
+ /* argc == 1 Âà¥þ³¡¨Ï¥ÎªÌ */
+ /* argc == 2 Âà¬Y¯S©w¨Ï¥ÎªÌ */
+
+ if (argc > 2)
+ {
+ printf("Usage: %s [target_user]\n", argv[0]);
+ exit(-1);
+ }
+
+ chdir(BBSHOME);
+
+ if (argc == 2)
+ {
+ transmf(argv[1]);
+ exit(1);
+ }
+
+ /* Âà´«¨Ï¥ÎªÌ§Úªº³Ì·R */
+ for (c = 'a'; c <= 'z'; c++)
+ {
+ sprintf(buf, "usr/%c", c);
+
+ if (!(dirp = opendir(buf)))
+ continue;
+
+ while (de = readdir(dirp))
+ {
+ char *str;
+
+ str = de->d_name;
+ if (*str <= ' ' || *str == '.')
+ continue;
+
+ transmf(str);
+ }
+
+ closedir(dirp);
+ }
+ return 0;
+}
+
+#else
+int
+main()
+{
+ printf("You should define MY_FAVORITE first.\n");
+ return -1;
+}
+#endif /* MY_FAVORITE */
diff --git a/util/tran/ats2pal.c b/util/tran/ats2pal.c
new file mode 100644
index 0000000..8a32647
--- /dev/null
+++ b/util/tran/ats2pal.c
@@ -0,0 +1,185 @@
+/*-------------------------------------------------------*/
+/* util/transpal.c */
+/*-------------------------------------------------------*/
+/* target : SOB ¦Ü Maple 3.02 (¬ÝªO)¦n¤Í¦W³æÂà´« */
+/* create : 01/09/08 */
+/* update : / / */
+/* author : itoc.bbs@bbs.ee.nctu.edu.tw */
+/*-------------------------------------------------------*/
+/* syntax : transpal */
+/*-------------------------------------------------------*/
+
+
+#if 0
+
+ 1. ³]©w OLD_BBSHOME (sob config.h)
+ 2. ­×§ï struct FRIEND ©M transfer_pal() transfer_brdpal()
+ 3. Âà´«(¬ÝªO)¦n¤Í¦W³æ¤§«e¡A±z¥²¶·¥ýÂà´«§¹¬ÝªO¤Î¨Ï¥ÎªÌ¡C
+
+ ps. ¨Ï¥Î«e½Ð¥ý¦æ³Æ¥÷¡Ause on ur own risk. µ{¦¡©å¦H½Ð¥]²[ :p
+ ps. ·PÁ lkchu ªº Maple 3.02 for FreeBSD
+
+#endif
+
+
+#include "bbs.h"
+
+
+#define OLD_BBSHOME "/home/bbs/bbsrs" /* 2.36 */
+
+
+/* ----------------------------------------------------- */
+/* 3.02 functions */
+/* ----------------------------------------------------- */
+
+
+static int
+acct_uno(userid)
+ char *userid;
+{
+ int fd;
+ int userno;
+ char fpath[80];
+
+ usr_fpath(fpath, userid, FN_ACCT);
+ fd = open(fpath, O_RDONLY);
+ if (fd >= 0)
+ {
+ read(fd, &userno, sizeof(userno));
+ close(fd);
+ return userno;
+ }
+ return -1;
+}
+
+
+/* ----------------------------------------------------- */
+
+
+static void
+transfer_pal(userid)
+ char *userid;
+{
+ ACCT acct;
+ FILE *fp;
+ int fd, friend_userno;
+ char fpath[64], buf[64], friend_userid[80];
+ PAL pal;
+
+ usr_fpath(fpath, userid, FN_PAL); /* ·sªº¦n¤Í¦W³æ */
+
+ /* sob ªº usr ¥Ø¿ý¦³¤À¤j¤p¼g¡A©Ò¥H­n¥ý¨ú±o¤j¤p¼g */
+ usr_fpath(buf, userid, FN_ACCT);
+ if ((fd = open(buf, O_RDONLY)) >= 0)
+ {
+ read(fd, &acct, sizeof(ACCT));
+ close(fd);
+ }
+ sprintf(buf, OLD_BBSHOME"/home/%s/overrides", acct.userid); /* ªº¦n¤Í¦W³æ */
+
+ if (dashf(fpath))
+ unlink(fpath); /* ²M±¼­««Ø */
+
+ if (!(fp = fopen(buf, "r")))
+ return;
+
+ while (fscanf(fp, "%s", friend_userid) == 1)
+ {
+ if ((friend_userno = acct_uno(friend_userid)) >= 0)
+ {
+ memset(&pal, 0, sizeof(PAL));
+ str_ncpy(pal.userid, friend_userid, sizeof(pal.userid));
+ pal.ftype = 0;
+ str_ncpy(pal.ship, "", sizeof(pal.ship));
+ pal.userno = friend_userno;
+ rec_add(fpath, &pal, sizeof(PAL));
+ }
+ }
+
+ fclose(fp);
+}
+
+
+static void
+transfer_brdpal(userid)
+ char *userid;
+{
+ FILE *fp;
+ int friend_userno;
+ char fpath[64], buf[64], friend_userid[80];
+ PAL pal;
+
+ brd_fpath(fpath, userid, FN_PAL); /* ·sªº¬ÝªO¦n¤Í¦W³æ */
+ sprintf(buf, OLD_BBSHOME"/boards/%s/permlist", userid); /* ªº¬ÝªO¦n¤Í¦W³æ */
+
+ if (dashf(fpath))
+ unlink(fpath); /* ²M±¼­««Ø */
+
+ if (!(fp = fopen(buf, "r")))
+ return;
+
+ while (fscanf(fp, "%s", friend_userid) == 1)
+ {
+ if ((friend_userno = acct_uno(friend_userid)) >= 0)
+ {
+ memset(&pal, 0, sizeof(PAL));
+ str_ncpy(pal.userid, friend_userid, sizeof(pal.userid));
+ pal.ftype = 0;
+ str_ncpy(pal.ship, "", sizeof(pal.ship));
+ pal.userno = friend_userno;
+ rec_add(fpath, &pal, sizeof(PAL));
+ }
+ }
+
+ fclose(fp);
+}
+
+
+int
+main()
+{
+ char c, *str;
+ char buf[64];
+ struct dirent *de;
+ DIR *dirp;
+
+ chdir(BBSHOME);
+
+ printf("\n\n[¦n¤Í¦W³æ]\n\n");
+ /* Âà´«¨Ï¥ÎªÌ¦n¤Í¦W³æ */
+ for (c = 'a'; c <= 'z'; c++)
+ {
+ sprintf(buf, "usr/%c", c);
+
+ if (!(dirp = opendir(buf)))
+ continue;
+
+ while (de = readdir(dirp))
+ {
+ str = de->d_name;
+ if (*str <= ' ' || *str == '.')
+ continue;
+
+ transfer_pal(str);
+ }
+ closedir(dirp);
+ }
+
+ printf("\n\n[ªO¤Í¦W³æ]\n\n");
+ /* Âà´«¬ÝªO¦n¤Í¦W³æ */
+ if (!(dirp = opendir("brd")))
+ return 0;
+
+ while (de = readdir(dirp))
+ {
+ str = de->d_name;
+ if (*str <= ' ' || *str == '.')
+ continue;
+
+ transfer_brdpal(str);
+ }
+
+ closedir(dirp);
+
+ return 0;
+}
diff --git a/util/tran/ats2usr.c b/util/tran/ats2usr.c
new file mode 100644
index 0000000..7385249
--- /dev/null
+++ b/util/tran/ats2usr.c
@@ -0,0 +1,592 @@
+/*-------------------------------------------------------*/
+/* util/transusr.c */
+/*-------------------------------------------------------*/
+/* target : Maple Sob 2.36 ¦Ü Maple 3.02 ¨Ï¥ÎªÌÂà´« */
+/* .PASSWDS => .USR .ACCT */
+/* create : 98/06/14 */
+/* update : 02/10/26 */
+/* author : ernie@micro8.ee.nthu.edu.tw */
+/* modify : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+/* syntax : transusr */
+/*-------------------------------------------------------*/
+
+
+#if 0
+
+ 1. ­×§ï struct userec ¤Î creat_dirs()
+ (userec ¨âª©©w¸qªº¦r¦êªø«×¤£¤@¡A½Ð¦Û¦æ´«¦¨¼Æ¦r)
+ 2. °£ plans ÀɦW¡A¦n¤Í¦W³æ¡B¼È¦sÀɵ¥³£¤£Âà´«
+ 3. Sob ¦³¤E­Óñ¦WÀÉ¡A¥uÂà«e¤T­Ó
+ 4. «H½c¤¤ªº internet mail ¦p¦³»Ý­n½Ð¥ý chmod 644 `find PATH -perm 600`
+
+ ps. ¨Ï¥Î«e½Ð¥ý¦æ³Æ¥÷¡Ause on ur own risk. µ{¦¡©å¦H½Ð¥]²[ :p
+ ps. ·PÁ lkchu ªº Maple 3.02 for FreeBSD
+
+#endif
+
+
+#include "ats.h"
+
+
+/* ----------------------------------------------------- */
+/* Âà´« .ACCT */
+/* ----------------------------------------------------- */
+
+
+static inline int
+is_bad_userid(userid)
+ char *userid;
+{
+ register char ch;
+
+ if (strlen(userid) < 2)
+ return 1;
+
+ if (!isalpha(*userid))
+ return 1;
+
+ if (!str_cmp(userid, "new"))
+ return 1;
+
+ while (ch = *(++userid))
+ {
+ if (!isalnum(ch))
+ return 1;
+ }
+ return 0;
+}
+
+
+static inline int
+uniq_userno(fd)
+ int fd;
+{
+ char buf[4096];
+ int userno, size;
+ SCHEMA *sp; /* record length 16 ¥i¾ã°£ 4096 */
+
+ userno = 1;
+
+ while ((size = read(fd, buf, sizeof(buf))) > 0)
+ {
+ sp = (SCHEMA *) buf;
+ do
+ {
+ if (sp->userid[0] == '\0')
+ {
+ lseek(fd, -size, SEEK_CUR);
+ return userno;
+ }
+ userno++;
+ size -= sizeof(SCHEMA);
+ sp++;
+ } while (size);
+ }
+
+ return userno;
+}
+
+
+#define LEVEL_BASIC 000000000001 /* °ò¥»Åv¤O */
+#define LEVEL_CHAT 000000000002 /* ¶i¤J²á¤Ñ«Ç */
+#define LEVEL_PAGE 000000000004 /* §ä¤H²á¤Ñ */
+#define LEVEL_POST 000000000010 /* µoªí¤å³¹ */
+#define LEVEL_LOGINOK 000000000020 /* µù¥Uµ{§Ç»{ÃÒ */
+#define LEVEL_MAILLIMIT 000000000040 /* «H¥óµL¤W­­ */
+#define LEVEL_CLOAK 000000000100 /* Áô¨­³N */
+#define LEVEL_SEECLOAK 000000000200 /* ¬Ý¨£§ÔªÌ */
+#define LEVEL_XEMPT 000000000400 /* ¥Ã¤[«O¯d±b¸¹ */
+#define LEVEL_BM 000000002000 /* ªO¥D */
+#define LEVEL_ACCOUNTS 000000004000 /* ±b¸¹Á`ºÞ */
+#define LEVEL_CHATROOM 000000010000 /* ²á¤Ñ«ÇÁ`ºÞ */
+#define LEVEL_BOARD 000000020000 /* ¬ÝªOÁ`ºÞ */
+#define LEVEL_SYSOP 000000040000 /* ¯¸ªø */
+
+
+static inline usint
+trans_acct_level(perm)
+ usint perm;
+{
+ usint userlevel;
+
+ userlevel = 0;
+
+ if (perm & LEVEL_BASIC)
+ userlevel |= PERM_BASIC;
+
+ if (perm & LEVEL_CHAT)
+ userlevel |= PERM_CHAT;
+
+ if (perm & LEVEL_PAGE)
+ userlevel |= PERM_PAGE;
+
+ if (perm & LEVEL_POST)
+ userlevel |= PERM_POST;
+
+ if (perm & LEVEL_LOGINOK)
+ userlevel |= PERM_VALID;
+
+ if (perm & LEVEL_MAILLIMIT)
+ userlevel |= PERM_MBOX;
+
+ if (perm & LEVEL_CLOAK)
+ userlevel |= PERM_CLOAK;
+
+ if (perm & LEVEL_SEECLOAK)
+ userlevel |= PERM_SEECLOAK;
+
+ if (perm & LEVEL_XEMPT)
+ userlevel |= PERM_XEMPT;
+
+ if (perm & LEVEL_BM)
+ userlevel |= PERM_BM;
+/*
+ if (perm & LEVEL_ACCOUNTS)
+ userlevel |= PERM_ACCOUNTS;
+
+ if (perm & LEVEL_CHATROOM)
+ userlevel |= PERM_CHATROOM;
+
+ if (perm & LEVEL_BOARD)
+ userlevel |= PERM_BOARD;
+
+ if (perm & LEVEL_SYSOP)
+ userlevel |= PERM_SYSOP;
+*/
+
+ return userlevel;
+}
+
+
+static inline void
+creat_dirs(old)
+ userec *old;
+{
+ ACCT new;
+ SCHEMA slot;
+ int fd;
+ char fpath[64];
+
+ memset(&new, 0, sizeof(new));
+ memset(&slot, 0, sizeof(slot));
+
+ str_ncpy(new.userid, old->userid, sizeof(new.userid));
+ str_ncpy(new.passwd, old->passwd, sizeof(new.passwd));
+ str_ncpy(new.realname, old->realname, sizeof(new.realname));
+ str_ncpy(new.username, old->username, sizeof(new.username));
+ new.userlevel = trans_acct_level(old->userlevel);
+ new.ufo = UFO_DEFAULT_NEW;
+ new.signature = 0;
+ new.year = (old->year-11);
+ new.month = old->month;
+ new.day = old->day;
+ new.sex = old->sex ? 1 : 0;
+ new.money = 1000; /* ¹w³]»È¹ô = 1000 ª÷¹ô = 0 */
+ new.gold = 0;
+ new.numlogins = old->numlogins;
+ new.numposts = old->numposts;
+ new.numemails = 0;
+ new.firstlogin = old->firstlogin;
+ new.lastlogin = old->lastlogin;
+ new.tcheck = time(&new.tvalid);
+
+ str_ncpy(new.lasthost, old->lasthost, sizeof(new.lasthost));
+ str_ncpy(new.email, old->email, sizeof(new.email));
+
+ slot.uptime = time(0);
+ strcpy(slot.userid, new.userid);
+
+ fd = open(FN_SCHEMA, O_RDWR | O_CREAT, 0600);
+ new.userno = uniq_userno(fd);
+ write(fd, &slot, sizeof(slot));
+ close(fd);
+
+ usr_fpath(fpath, new.userid, NULL);
+ mkdir(fpath, 0700);
+ strcat(fpath, "/@");
+ mkdir(fpath, 0700);
+ usr_fpath(fpath, new.userid, "MF");
+ mkdir(fpath, 0700);
+ usr_fpath(fpath, new.userid, "gem"); /* itoc.010727: ­Ó¤HºëµØ°Ï */
+ mak_links(fpath);
+
+ usr_fpath(fpath, new.userid, ".ACCT");
+ fd = open(fpath, O_WRONLY | O_CREAT, 0600);
+ write(fd, &new, sizeof(ACCT));
+ close(fd);
+}
+
+
+/* ----------------------------------------------------- */
+/* Âà´«»{ÃÒ¸ê®Æ */
+/* ----------------------------------------------------- */
+
+
+static inline void
+trans_justify(old)
+ userec *old;
+{
+ char fpath[64];
+ FILE *fp;
+
+ usr_fpath(fpath, old->userid, FN_JUSTIFY);
+ if (fp = fopen(fpath, "a"))
+ {
+ fprintf(fp, "RPY: %s\n", old->justify); /* Âà´«¹w³]¥H email »{ÃÒ */
+ fclose(fp);
+ }
+}
+
+
+/* ----------------------------------------------------- */
+/* Âഫñ¦WÀÉ¡B­pµeÀÉ */
+/* ----------------------------------------------------- */
+static void
+_mail_self(userid, fpath, owner, title) /* itoc.011115: ±HÀÉ®×µ¹¦Û¤v */
+ char *userid; /* ¦¬¥óªÌ */
+ char *fpath; /* Àɮ׸ô®| */
+ char *owner; /* ±H¥ó¤H */
+ char *title; /* ¶l¥ó¼ÐÃD */
+{
+ HDR fhdr;
+ char folder[64];
+
+ usr_fpath(folder, userid, FN_DIR);
+ close(hdr_stamp(folder, HDR_LINK, &fhdr, fpath));
+ str_ncpy(fhdr.owner, owner, sizeof(fhdr.owner));
+ str_ncpy(fhdr.title, title, sizeof(fhdr.title));
+ fhdr.xmode = 0;
+ rec_add(folder, &fhdr, sizeof(fhdr));
+}
+
+
+static inline void
+trans_sig(old)
+ userec *old;
+{
+ int i;
+ char buf[64], fpath[64], f_sig[20],buf1[20];
+
+ for (i = 1; i <= 3; i++) /* Maple 3.0 ¥u¦³¤T­Óñ¦W */
+ {
+ sprintf(buf, OLD_BBSHOME "/home/%s/sig.%d", old->userid, i); /* ªºÃ±¦WÀÉ */
+ if (dashf(buf))
+ {
+ sprintf(f_sig, "%s.%d", FN_SIGN, i);
+ usr_fpath(fpath, old->userid, f_sig);
+ f_cp(buf, fpath, O_TRUNC);
+ }
+ }
+ for(i=4;i<=10;i++)
+ {
+ sprintf(buf, OLD_BBSHOME "/home/%s/sig.%d", old->userid, i);
+ sprintf(buf1,"ñ¦WÀÉ sig.%d",i);
+ if (dashf(buf))
+ _mail_self(old->userid, buf, "[³Æ¥÷]", buf1);
+ }
+}
+
+
+static inline void
+trans_plans(old)
+ userec *old;
+{
+ char buf[64], fpath[64];
+
+ sprintf(buf, OLD_BBSHOME "/home/%s/plans", old->userid);
+ if (dashf(buf))
+ {
+ usr_fpath(fpath, old->userid, FN_PLANS);
+ f_cp(buf, fpath, O_TRUNC);
+ }
+ return;
+}
+
+
+/* ----------------------------------------------------- */
+/* Âà´««H¥ó */
+/* ----------------------------------------------------- */
+
+
+static time_t
+trans_hdr_chrono(filename)
+ char *filename;
+{
+ char time_str[11];
+
+ /* M.1087654321.A ©Î M.987654321.A */
+ str_ncpy(time_str, filename + 2, filename[2] == '1' ? 11 : 10);
+
+ return (time_t) atoi(time_str);
+}
+
+
+static inline void
+trans_mail(old)
+ userec *old;
+{
+ int fd;
+ char index[64], folder[64], buf[64], fpath[64];
+ fileheader fh;
+ HDR hdr;
+
+ sprintf(index, OLD_BBSHOME "/home/%s/.DIR", old->userid);
+ usr_fpath(folder, old->userid, FN_DIR);
+
+ if ((fd = open(index, O_RDONLY)) >= 0)
+ {
+ while (read(fd, &fh, sizeof(fh)) == sizeof(fh))
+ {
+ sprintf(buf, OLD_BBSHOME "/home/%s/%s", old->userid, fh.filename);
+
+ if (dashf(buf)) /* ¤å³¹Àɮצb¤~°µÂà´« */
+ {
+ time_t chrono;
+ char new_name[10] = "@";
+
+ /* Âà´«¤å³¹ .DIR */
+ memset(&hdr, 0, sizeof(HDR));
+ chrono = trans_hdr_chrono(fh.filename);
+ new_name[1] = radix32[chrono & 31];
+ archiv32(chrono, new_name + 1);
+
+ hdr.chrono = chrono;
+ str_ncpy(hdr.xname, new_name, sizeof(hdr.xname));
+ str_ncpy(hdr.owner, strstr(fh.owner, "[³Æ.") ? "[³Æ§Ñ¿ý]" : fh.owner, sizeof(hdr.owner)); /* [³Æ.§Ñ.¿ý] */
+ str_ncpy(hdr.title, fh.title, sizeof(hdr.title));
+ str_stamp(hdr.date, &hdr.chrono);
+ hdr.xmode = (fh.filemode & 0x2) ? (MAIL_MARKED | MAIL_READ) : MAIL_READ; /* ³]¬°¤wŪ */
+
+ rec_add(folder, &hdr, sizeof(HDR));
+
+ /* «þ¨©ÀÉ®× */
+ usr_fpath(fpath, old->userid, "@/");
+ strcat(fpath, new_name);
+ f_cp(buf, fpath, O_TRUNC);
+ }
+ }
+ close(fd);
+ }
+}
+
+
+/* ----------------------------------------------------- */
+/* Âà´«­Ó¤HºëµØ°Ï */
+/* ----------------------------------------------------- */
+
+
+#ifdef HAVE_PERSONAL_GEM
+static void
+trans_man_stamp(folder, token, hdr, fpath, time)
+ char *folder;
+ int token;
+ HDR *hdr;
+ char *fpath;
+ time_t time;
+{
+ char *fname, *family;
+ int rc;
+
+ fname = fpath;
+ while (rc = *folder++)
+ {
+ *fname++ = rc;
+ if (rc == '/')
+ family = fname;
+ }
+ if (*family != '.')
+ {
+ fname = family;
+ family -= 2;
+ }
+ else
+ {
+ fname = family + 1;
+ *fname++ = '/';
+ }
+
+ *fname++ = token;
+
+ *family = radix32[time & 31];
+ archiv32(time, fname);
+
+ if (rc = open(fpath, O_WRONLY | O_CREAT | O_EXCL, 0600))
+ {
+ memset(hdr, 0, sizeof(HDR));
+ hdr->chrono = time;
+ str_stamp(hdr->date, &hdr->chrono);
+ strcpy(hdr->xname, --fname);
+ close(rc);
+ }
+ return;
+}
+
+
+static void
+transman(index, folder)
+ char *index, *folder;
+{
+ static int count = 100;
+
+ int fd;
+ char *ptr, buf[256], fpath[64];
+ fileheader fh;
+ HDR hdr;
+ time_t chrono;
+
+ if ((fd = open(index, O_RDONLY)) >= 0)
+ {
+ while (read(fd, &fh, sizeof(fh)) == sizeof(fh))
+ {
+ strcpy(buf, index);
+ ptr = strrchr(buf, '/') + 1;
+ strcpy(ptr, fh.filename);
+
+ if (*fh.filename == 'M' && dashf(buf)) /* ¥uÂà M.xxxx.A ¤Î D.xxxx.a */
+ {
+ /* Âà´«¤å³¹ .DIR */
+ memset(&hdr, 0, sizeof(HDR));
+ chrono = trans_hdr_chrono(fh.filename);
+ trans_man_stamp(folder, 'A', &hdr, fpath, chrono);
+ hdr.xmode = 0;
+ str_ncpy(hdr.owner, fh.owner, sizeof(hdr.owner));
+ str_ncpy(hdr.title, fh.title + 3, sizeof(hdr.title));
+ rec_add(folder, &hdr, sizeof(HDR));
+
+ /* «þ¨©ÀÉ®× */
+ f_cp(buf, fpath, O_TRUNC);
+ }
+ else if (*fh.filename == 'D' && dashd(buf))
+ {
+ char sub_index[256];
+
+ /* Âà´«¤å³¹ .DIR */
+ memset(&hdr, 0, sizeof(HDR));
+ chrono = ++count; /* WD ªº¥Ø¿ý©R¦W¤ñ¸û©_©Ç¡A¥u¦n¦Û¤vµ¹¼Æ¦r */
+ trans_man_stamp(folder, 'F', &hdr, fpath, chrono);
+ hdr.xmode = GEM_FOLDER;
+ str_ncpy(hdr.owner, fh.owner, sizeof(hdr.owner));
+ str_ncpy(hdr.title, fh.title + 3, sizeof(hdr.title));
+ rec_add(folder, &hdr, sizeof(HDR));
+
+ /* recursive ¶i¥hÂà´«¤l¥Ø¿ý */
+ strcpy(sub_index, buf);
+ ptr = strrchr(sub_index, '/') + 1;
+ sprintf(ptr, "%s/.DIR", fh.filename);
+ transman(sub_index, fpath);
+ }
+ }
+ close(fd);
+ }
+}
+#endif
+
+
+/* ----------------------------------------------------- */
+/* Âà´«¥Dµ{¦¡ */
+/* ----------------------------------------------------- */
+
+
+static void
+transusr(user)
+ userec *user;
+{
+ char buf[64];
+
+ printf("Âà´« %s ¨Ï¥ÎªÌ\n", user->userid);
+
+ if (is_bad_userid(user->userid))
+ {
+ printf("%s ¤£¬O¦Xªk ID\n", user->userid);
+ return;
+ }
+
+ usr_fpath(buf, user->userid, NULL);
+ if (dashd(buf))
+ {
+ printf("%s ¤w¸g¦³¦¹ ID\n", user->userid);
+ return;
+ }
+
+ sprintf(buf, OLD_BBSHOME "/home/%s", user->userid);
+ if (!dashd(buf))
+ {
+ printf("%s ªºÀɮפ£¦s¦b\n", user->userid);
+ return;
+ }
+
+ creat_dirs(user);
+ trans_justify(user);
+ trans_sig(user);
+ trans_plans(user);
+ trans_mail(user);
+
+
+#ifdef HAVE_PERSONAL_GEM
+ sprintf(buf, OLD_BBSHOME "/home/%s/man", user->userid);
+ if (dashd(buf))
+ {
+ char index[64], folder[64];
+
+ sprintf(index, "%s/.DIR", buf);
+ usr_fpath(folder, user->userid, "gem/" FN_DIR);
+ transman(index, folder);
+ }
+#endif
+
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int fd,count=0;
+ userec user;
+
+ /* argc == 1 Âà¥þ³¡¨Ï¥ÎªÌ */
+ /* argc == 2 Âà¬Y¯S©w¨Ï¥ÎªÌ */
+
+ if (argc > 2)
+ {
+ printf("Usage: %s [target_user]\n", argv[0]);
+ exit(-1);
+ }
+
+ chdir(BBSHOME);
+
+ if (!dashf(FN_PASSWD))
+ {
+ printf("ERROR! Can't open " FN_PASSWD "\n");
+ exit(-1);
+ }
+ if (!dashd(OLD_BBSHOME "/home"))
+ {
+ printf("ERROR! Can't open " OLD_BBSHOME "/home\n");
+ exit(-1);
+ }
+
+ if ((fd = open(FN_PASSWD, O_RDONLY)) >= 0)
+ {
+ while (read(fd, &user, sizeof(user)) == sizeof(user))
+ {
+ if (argc == 1)
+ {
+ transusr(&user);
+ count++;
+ }
+ else if (!strcmp(user.userid, argv[1]))
+ {
+ transusr(&user);
+ count++;
+ exit(1);
+ }
+ }
+ close(fd);
+ }
+
+ printf("\n\n¥þ³¡Âà´« %d ¦ì¨Ï¥ÎªÌ¡C\n\n",count);
+ exit(0);
+}
diff --git a/util/tran/brd2gem.c b/util/tran/brd2gem.c
new file mode 100644
index 0000000..3473d74
--- /dev/null
+++ b/util/tran/brd2gem.c
@@ -0,0 +1,104 @@
+/*-------------------------------------------------------*/
+/* util/brd2gem.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : ¬ÝªO¤ÀÃþ¨ìºëµØ°Ï */
+/* create : 01/09/09 */
+/* update : 03/02/13 */
+/* author : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+/* syntax : brd2gem ¤ÀÃþ Classname */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+
+#if 0 /* ¨Ï¥Î¤èªk */
+
+ ³o­Óµ{¦¡¬O®³¨Óµ¹ bbs ª©¥»Âà´«®É¡A¦Û°Ê«Ø¥ß @Class ¥Îªº¡C
+ ©Î¬O¦b»~¬å¤ÀÃþ®É¡A¤]¥i¥H®³¥X¨Ó­««Ø @Class ¥Î¡C
+
+ °²³]­n§â©Ò¦³¤ÀÃþ¬°¡u¨t²Î¡v©M¡u¯¸¤º¡vªº¬ÝªO³£©ñ¦b¡uBBS¡v³o­Ó¤ÀÃþ¸Ì­±¡A
+ ¥H¤Î§â©Ò¦³¤ÀÃþ¬°¡u­Ó¤H¡vªº¬ÝªO³£©ñ¦b¡uPersonal¡v³o­Ó¤ÀÃþ¸Ì­±¡C
+
+ 1. ¤W BBS ¯¸¡A¦b (A)nnounce/Class ¸Ì­± Ctrl+P ¿ï (C)¡A«Ø¥ßÀɦW¬° BBS ªº¤ÀÃþ (¼ÐÃD¥ô·N)¡C
+ 2. ¤W BBS ¯¸¡A¦b (A)nnounce/Class ¸Ì­± Ctrl+P ¿ï (C)¡A«Ø¥ßÀɦW¬° Personal ªº¤ÀÃþ (¼ÐÃD¥ô·N)¡C
+ 3. ¦b¤u§@¯¸¤¤¥H bbs ¨­¤À°õ¦æ
+ % ~bbs/src/util/tran/brd2gem ¨t²Î BBS
+ % ~bbs/src/util/tran/brd2gem ¯¸¤º BBS
+ % ~bbs/src/util/tran/brd2gem ­Ó¤H Personal
+
+#endif
+
+
+static void
+brd_2_gem(brd, gem)
+ BRD *brd;
+ HDR *gem;
+{
+ memset(gem, 0, sizeof(HDR));
+ time(&gem->chrono);
+ strcpy(gem->xname, brd->brdname);
+ sprintf(gem->title, "%-13s%-5s%s", brd->brdname, brd->class, brd->title);
+ gem->xmode = GEM_BOARD | GEM_FOLDER;
+
+#ifdef HAVE_MODERATED_BOARD
+ /* ¯µ±KªO¡B¦n¤ÍªO */
+ if (brd->readlevel == PERM_SYSOP || brd->readlevel == PERM_BOARD)
+ gem->xmode |= GEM_RESTRICT;
+#endif
+}
+
+
+static int
+hdr_cmp(a, b)
+ HDR *a;
+ HDR *b;
+{
+ /* itoc.010413: ¤ÀÃþ/ªO¦W¥æ¤e¤ñ¹ï */
+ int k = strncmp(a->title + BNLEN + 1, b->title + BNLEN + 1, BCLEN);
+ return k ? k : str_cmp(a->xname, b->xname);
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int fd;
+ char folder[64];
+ BRD brd;
+ HDR hdr;
+
+ chdir(BBSHOME);
+
+ if (argc != 3)
+ {
+ printf("Usage: %s ¤ÀÃþ Classname\n", argv[0]);
+ exit(-1);
+ }
+
+ if (strlen(argv[1]) > BCLEN || strlen(argv[2]) > BNLEN)
+ {
+ printf("¡u¤ÀÃþ¡v­nµu©ó %d¡AClassname ­nµu©ó %d\n", BCLEN, BNLEN);
+ exit(-1);
+ }
+
+ sprintf(folder, "gem/@/@%s", argv[2]);
+
+ if ((fd = open(FN_BRD, O_RDONLY)) >= 0)
+ {
+ while (read(fd, &brd, sizeof(BRD)) == sizeof(BRD))
+ {
+ if (!strcmp(brd.class, argv[1]))
+ {
+ brd_2_gem(&brd, &hdr);
+ rec_add(folder, &hdr, sizeof(HDR));
+ }
+ }
+ close(fd);
+ }
+
+ rec_sync(folder, sizeof(HDR), hdr_cmp, NULL);
+}
diff --git a/util/tran/cola.h b/util/tran/cola.h
new file mode 100644
index 0000000..9bfa18f
--- /dev/null
+++ b/util/tran/cola.h
@@ -0,0 +1,74 @@
+/*-------------------------------------------------------*/
+/* util/cola.h */
+/*-------------------------------------------------------*/
+/* target : Cola ¦Ü Maple 3.02 Âà´« */
+/* create : 03/02/11 */
+/* update : / / */
+/* author : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#if 0
+
+ 0. ½Ð¥ý©ó Cola §â©Ò¦³¬ÝªO­^¤å¦W¦r§ï¦b 12 ¦r¥H¤º¡C
+
+ 1. ¥²¶·¦b brd Âà§¹¤~¥i¥HÂà´« gem
+ 2. «ØÄ³Âà´«¶¶§Ç¬° usr -> brd -> gem -> post
+
+ 3. ³]©w COLABBS_HOME¡BCOLABBS_BOARDS¡BCOLABBS_MAN¡BFN_BOARD
+
+#endif
+
+
+#include "bbs.h"
+
+
+#define COLABBS_HOME "/tmp/home" /* ªº Cola BBS ªº¨Ï¥ÎªÌ¥Ø¿ý */
+#define COLABBS_BOARDS "/tmp/boards" /* ªº Cola BBS ªº¬ÝªO¥Ø¿ý */
+#define COLABBS_MAN "/tmp/man" /* ªº Cola BBS ªººëµØ°Ï¥Ø¿ý */
+#define FN_BOARD "/tmp/.boards" /* ªº Cola BBS ªº .boards */
+
+
+/* ----------------------------------------------------- */
+/* old .PASSWDS struct : 512 bytes */
+/* ----------------------------------------------------- */
+
+/* itoc.030211: ¥uÂà´« userid ©M passwd */
+typedef struct
+{
+ char userid[13];
+ char blank1;
+ char passwd[14];
+ char username[40];
+ char realname[80];
+ char blank2[364];
+} userec;
+
+
+/* ----------------------------------------------------- */
+/* old DIR of board struct : 128 bytes */
+/* ----------------------------------------------------- */
+
+typedef struct
+{
+ char filename[34]; /* M.9876543210.A */
+ char blank1[46];
+ char owner[14]; /* userid[.] */
+ char blank2[57];
+ char date[6]; /* [08/27] or space(5) */
+ char title[74];
+ char blank3[25];
+} fileheader;
+
+
+/* ----------------------------------------------------- */
+/* old BOARDS struct : 512 bytes */
+/* ----------------------------------------------------- */
+
+/* itoc.030211: ¥uÂà´« brdname ©M title */
+typedef struct
+{
+ char brdname[13];
+ char blank1[147];
+ char title[96];
+} boardheader;
diff --git a/util/tran/cola2brd.c b/util/tran/cola2brd.c
new file mode 100644
index 0000000..0f4d4de
--- /dev/null
+++ b/util/tran/cola2brd.c
@@ -0,0 +1,212 @@
+/*-------------------------------------------------------*/
+/* util/cola2brd.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : Cola ¦Ü Maple 3.02 ¬ÝªOÂà´« */
+/* create : 03/02/11 */
+/* update : / / */
+/* author : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "cola.h"
+
+
+static inline time_t
+trans_hdr_chrono(filename)
+ char *filename;
+{
+ char time_str[11];
+
+ /* M.1087654321.A ©Î M.987654321.A */
+ str_ncpy(time_str, filename + 2, filename[2] == '1' ? 11 : 10);
+
+ return (time_t) atoi(time_str);
+}
+
+
+static inline void
+trans_hdr_stamp(folder, t, hdr, fpath)
+ char *folder;
+ time_t t;
+ HDR *hdr;
+ char *fpath;
+{
+ FILE *fp;
+ char *fname, *family;
+ int rc;
+
+ fname = fpath;
+ while (rc = *folder++)
+ {
+ *fname++ = rc;
+ if (rc == '/')
+ family = fname;
+ }
+ fname = family + 1;
+ *fname++ = '/';
+ *fname++ = 'A';
+
+ for (;;)
+ {
+ *family = radix32[t & 31];
+ archiv32(t, fname);
+
+ if (fp = fopen(fpath, "r"))
+ {
+ fclose(fp);
+ t++;
+ }
+ else
+ {
+ memset(hdr, 0, sizeof(HDR));
+ hdr->chrono = t;
+ str_stamp(hdr->date, &hdr->chrono);
+ strcpy(hdr->xname, --fname);
+ break;
+ }
+ }
+}
+
+
+/* ----------------------------------------------------- */
+/* Âà´«¥Dµ{¦¡ */
+/* ----------------------------------------------------- */
+
+
+static void
+transbrd(bh)
+ boardheader *bh;
+{
+ static time_t stamp = 0;
+
+ int fd;
+ char fpath[64], folder[64], index[64], buf[64];
+ fileheader fh;
+ BRD newboard;
+ HDR hdr;
+ time_t chrono;
+
+ printf("Âà´« %s ¬ÝªO\n", bh->brdname);
+
+ brd_fpath(buf, bh->brdname, NULL);
+ if (dashd(buf))
+ {
+ printf("%s ¤w¸g¦³¦¹¬ÝªO\n", bh->brdname);
+ return;
+ }
+
+ if (!stamp)
+ time(&stamp);
+
+ /* Âà´« .BRD */
+
+ memset(&newboard, 0, sizeof(newboard));
+ str_ncpy(newboard.brdname, bh->brdname, sizeof(newboard.brdname));
+ str_ncpy(newboard.class, bh->title + 2, sizeof(newboard.class));
+ str_ncpy(newboard.title, bh->title + 12, sizeof(newboard.title));
+ /* newboard.BM[0] = '\0'; */ /* ªO¥D¤£Âà´« */
+ newboard.bstamp = stamp++;
+ newboard.battr = BRD_NOTRAN;
+ newboard.readlevel = 0;
+ newboard.postlevel = PERM_POST;
+
+ rec_add(FN_BRD, &newboard, sizeof(newboard)); /* §O§Ñ¤F¥Î brd2gem.c ¨ÓÂà´« Class */
+
+ /* ¶}·s¥Ø¿ý */
+
+ sprintf(fpath, "gem/brd/%s", newboard.brdname);
+ mak_dirs(fpath);
+ mak_dirs(fpath + 4);
+
+ /* Âà´«¶iªOµe­± */
+
+ sprintf(buf, COLABBS_BOARDS "/%s/.Welcome", bh->brdname);
+
+ if (dashf(buf))
+ {
+ brd_fpath(fpath, newboard.brdname, FN_NOTE);
+ f_cp(buf, fpath, O_TRUNC);
+ }
+
+ /* Âà´«¤å³¹ */
+
+ sprintf(index, COLABBS_BOARDS "/%s/.DIR", bh->brdname); /* 廼 .DIR */
+ brd_fpath(folder, newboard.brdname, ".DIR"); /* ·sªº .DIR */
+
+ if ((fd = open(index, O_RDONLY)) >= 0)
+ {
+ while (read(fd, &fh, sizeof(fh)) == sizeof(fh))
+ {
+ sprintf(buf, COLABBS_BOARDS "/%s/%s", bh->brdname, fh.filename);
+
+ if (dashf(buf)) /* ¤å³¹Àɮצb¤~°µÂà´« */
+ {
+ /* Âà´«¤å³¹ .DIR */
+ memset(&hdr, 0, sizeof(HDR));
+ chrono = trans_hdr_chrono(fh.filename);
+ trans_hdr_stamp(folder, chrono, &hdr, fpath);
+ str_ncpy(hdr.owner, fh.owner, sizeof(hdr.owner));
+ str_ansi(hdr.title, fh.title + 3, sizeof(hdr.title));
+ hdr.xmode = 0;
+ rec_add(folder, &hdr, sizeof(HDR));
+
+ /* «þ¨©ÀÉ®× */
+ f_cp(buf, fpath, O_TRUNC);
+ }
+ }
+ close(fd);
+ }
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int fd;
+ boardheader bh;
+
+ /* argc == 1 Âà¥þ³¡ªO */
+ /* argc == 2 Âà¬Y¯S©wªO */
+
+ if (argc > 2)
+ {
+ printf("Usage: %s [target_board]\n", argv[0]);
+ return -1;
+ }
+
+ chdir(BBSHOME);
+
+ if (!dashf(FN_BOARD))
+ {
+ printf("ERROR! Can't open " FN_BOARD "\n");
+ return -1;
+ }
+ if (!dashd(COLABBS_BOARDS))
+ {
+ printf("ERROR! Can't open " COLABBS_BOARDS "\n");
+ return -1;
+ }
+
+ if ((fd = open(FN_BOARD, O_RDONLY)) >= 0)
+ {
+ while (read(fd, &bh, sizeof(bh)) == sizeof(bh))
+ {
+ bh.brdname[BNLEN] = '\0'; /* itoc.030211: Cola ªº¬ÝªO¦W¥i¯à¶W¹L BNLEN */
+
+ if (argc == 1)
+ {
+ transbrd(&bh);
+ }
+ else if (!strcmp(bh.brdname, argv[1]))
+ {
+ transbrd(&bh);
+ return 1;
+ }
+ }
+ close(fd);
+ }
+
+ return 0;
+}
diff --git a/util/tran/cola2gem.c b/util/tran/cola2gem.c
new file mode 100644
index 0000000..4d93c14
--- /dev/null
+++ b/util/tran/cola2gem.c
@@ -0,0 +1,211 @@
+/*-------------------------------------------------------*/
+/* util/cola2gem.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : Cola ¦Ü Maple 3.02 ºëµØ°ÏÂà´« */
+/* create : 03/02/11 */
+/* update : / / */
+/* author : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "cola.h"
+
+
+/* ----------------------------------------------------- */
+/* Âà´«ºëµØ°Ï */
+/* ----------------------------------------------------- */
+
+
+static time_t
+trans_hdr_chrono(filename)
+ char *filename;
+{
+ char time_str[11];
+
+ /* M.1087654321.A ©Î M.987654321.A */
+ str_ncpy(time_str, filename + 2, filename[2] == '1' ? 11 : 10);
+
+ return (time_t) atoi(time_str);
+}
+
+
+static void
+trans_man_stamp(folder, token, hdr, fpath, time)
+ char *folder;
+ int token;
+ HDR *hdr;
+ char *fpath;
+ time_t time;
+{
+ char *fname, *family;
+ int rc;
+
+ fname = fpath;
+ while (rc = *folder++)
+ {
+ *fname++ = rc;
+ if (rc == '/')
+ family = fname;
+ }
+ if (*family != '.')
+ {
+ fname = family;
+ family -= 2;
+ }
+ else
+ {
+ fname = family + 1;
+ *fname++ = '/';
+ }
+
+ *fname++ = token;
+
+ *family = radix32[time & 31];
+ archiv32(time, fname);
+
+ if (rc = open(fpath, O_WRONLY | O_CREAT | O_EXCL, 0600))
+ {
+ memset(hdr, 0, sizeof(HDR));
+ hdr->chrono = time;
+ str_stamp(hdr->date, &hdr->chrono);
+ strcpy(hdr->xname, --fname);
+ close(rc);
+ }
+ return;
+}
+
+
+/* ----------------------------------------------------- */
+/* Âà´«¥Dµ{¦¡ */
+/* ----------------------------------------------------- */
+
+
+static void
+transman(index, folder)
+ char *index, *folder;
+{
+ int fd;
+ char *ptr, buf[256], fpath[64];
+ fileheader fh;
+ HDR hdr;
+ time_t chrono;
+
+ if ((fd = open(index , O_RDONLY)) >= 0)
+ {
+ while (read(fd, &fh, sizeof(fh)) == sizeof(fh))
+ {
+ strcpy(buf, index);
+ ptr = strrchr(buf, '/') + 1;
+ strcpy(ptr, fh.filename);
+
+ if (*fh.filename == 'M' && dashf(buf)) /* ¥uÂà M.xxxx.A ¤Î D.xxxx.a */
+ {
+ /* Âà´«¤å³¹ .DIR */
+ memset(&hdr, 0, sizeof(HDR));
+ chrono = trans_hdr_chrono(fh.filename);
+ trans_man_stamp(folder, 'A', &hdr, fpath, chrono);
+ hdr.xmode = 0;
+ str_ncpy(hdr.owner, fh.owner, sizeof(hdr.owner));
+ str_ncpy(hdr.title, fh.title + 3, sizeof(hdr.title));
+ rec_add(folder, &hdr, sizeof(HDR));
+
+ /* «þ¨©ÀÉ®× */
+ f_cp(buf, fpath, O_TRUNC);
+ }
+ else if (*fh.filename == 'D' && dashd(buf))
+ {
+ char sub_index[256];
+
+ /* Âà´«¤å³¹ .DIR */
+ memset(&hdr, 0, sizeof(HDR));
+ chrono = trans_hdr_chrono(fh.filename);
+ trans_man_stamp(folder, 'F', &hdr, fpath, chrono);
+ hdr.xmode = GEM_FOLDER;
+ str_ncpy(hdr.owner, fh.owner, sizeof(hdr.owner));
+ str_ncpy(hdr.title, fh.title + 3, sizeof(hdr.title));
+ rec_add(folder, &hdr, sizeof(HDR));
+
+ /* recursive ¶i¥hÂà´«¤l¥Ø¿ý */
+ strcpy(sub_index, buf);
+ ptr = strrchr(sub_index, '/') + 1;
+ sprintf(ptr, "%s/.DIR", fh.filename);
+ transman(sub_index, fpath);
+ }
+ }
+ close(fd);
+ }
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int fd;
+ char *brdname, index[64], folder[64];
+ boardheader bh;
+
+ /* argc == 1 Âà¥þ³¡ªO */
+ /* argc == 2 Âà¬Y¯S©wªO */
+
+ if (argc > 2)
+ {
+ printf("Usage: %s [target_board]\n", argv[0]);
+ exit(-1);
+ }
+
+ chdir(BBSHOME);
+
+ if (!dashf(FN_BOARD))
+ {
+ printf("ERROR! Can't open " FN_BOARD "\n");
+ exit(-1);
+ }
+
+ if (argc == 1)
+ {
+ if ((fd = open(FN_BOARD, O_RDONLY)) >= 0)
+ {
+ while (read(fd, &bh, sizeof(bh)) == sizeof(bh))
+ {
+ brdname = bh.brdname;
+
+ sprintf(folder, "gem/brd/%s", brdname);
+ if (!dashd(folder))
+ {
+ printf("ERROR! %s not exist. New it first.\n", folder);
+ continue;
+ }
+
+ sprintf(index, COLABBS_MAN "/%s/.DIR", brdname);
+ sprintf(folder, "gem/brd/%s/%s", brdname, FN_DIR);
+
+ printf("Âà´« %s ºëµØ°Ï\n", brdname);
+ transman(index, folder);
+ }
+ close(fd);
+ }
+ }
+ else
+ {
+ brdname = argv[1];
+
+ sprintf(folder, "gem/brd/%s", brdname);
+ if (!dashd(folder))
+ {
+ printf("ERROR! %s not exist. New it first.\n", folder);
+ exit(-1);
+ }
+
+ sprintf(index, COLABBS_MAN "/%s/.DIR", brdname);
+ sprintf(folder, "gem/brd/%s/%s", brdname, FN_DIR);
+
+ printf("Âà´« %s ºëµØ°Ï\n", brdname);
+ transman(index, folder);
+
+ exit(1);
+ }
+
+ exit(0);
+}
diff --git a/util/tran/cola2post.c b/util/tran/cola2post.c
new file mode 100644
index 0000000..f62ae6e
--- /dev/null
+++ b/util/tran/cola2post.c
@@ -0,0 +1,223 @@
+/*-------------------------------------------------------*/
+/* util/cola2post.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : Cola ¦Ü Maple 3.02 ¬ÝªO¤å³¹®æ¦¡Âà´« */
+/* create : 03/02/21 */
+/* update : / / */
+/* author : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+
+#if 0
+
+ ¥H¤U³o­Ó¬O ColaBBS ¤å³¹Àɮתº½d¨Ò
+
+*[m*[47;34m §@ªÌ *[44;37m userid (¼ÊºÙ) *[47;34m «H°Ï *[44;37m SYSOP *[m\n\r
+*[47;34m ¼ÐÃD *[44;37m Re: °Õ°Õ°Õ°Õ°Õ°Õ *[m\n\r
+*[47;34m ®É¶¡ *[44;37m Fri Mar 15 11:33:20 2002 *[m\n\r
+*[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*[m\n\r
+\n\r
+¤å³¹¤º®e²Ä¤@¦æ\n\r
+¤å³¹¤º®e²Ä¤G¦æ\n\r
+
+ ­n§ï¦¨³o¼Ë
+
+§@ªÌ: userid (¼ÊºÙ) ¯¸¤º: SYSOP\n
+¼ÐÃD: Re: °Õ°Õ°Õ°Õ°Õ°Õ\n
+®É¶¡: Fri Mar 15 11:33:20 2002\n
+\n
+¤å³¹¤º®e²Ä¤@¦æ\n
+¤å³¹¤º®e²Ä¤G¦æ\n
+
+#endif
+
+
+static void
+reaper(fpath)
+ char *fpath;
+{
+ FILE *fpr, *fpw;
+ char src[256], dst[256];
+ char fnew[64];
+ char *ptr;
+ int i;
+
+ if (!(fpr = fopen(fpath, "r")))
+ return;
+
+ sprintf(fnew, "%s.new", fpath);
+ fpw = fopen(fnew, "w");
+
+ i = 0;
+ while (fgets(src, 256, fpr))
+ {
+ if (ptr = strchr(src, '\r'))
+ *ptr = '\0';
+ if (ptr = strchr(src, '\n'))
+ *ptr = '\0';
+
+ if (i < 4) /* «e¥|¦æÀÉÀY */
+ {
+ i++;
+
+ if (*src == '\033') /* ¦¹¬°ÀÉÀY */
+ {
+ str_ansi(dst, src, sizeof(dst)); /* ¥h±¼ ANSI */
+
+ if (i <= 3 && dst[0] == ' ' && dst[5] == ' ') /* §@ªÌ: */
+ {
+ dst[5] = ':';
+ fprintf(fpw, "%.78s\n", dst + 1); /* ¥h°£²Ä¤@®æªÅ¥Õ */
+ continue;
+ }
+ else if (i == 4 && !strncmp(dst, "¢w¢w¢w¢w", 8))
+ {
+ /* ¤À¹j½u¤£­n¤F */
+ continue;
+ }
+ }
+ }
+
+ fprintf(fpw, "%s\n", src); /* ¤º®e·Ó§Û */
+ }
+
+ fclose(fpr);
+ fclose(fpw);
+
+ unlink(fpath);
+ rename(fnew, fpath);
+}
+
+
+static void
+expireBrd(brdname)
+ char *brdname;
+{
+ int fd;
+ char folder[64], fpath[64];
+ HDR hdr;
+
+ printf("Âà´« %s ¬ÝªO\n", brdname);
+
+ brd_fpath(folder, brdname, FN_DIR);
+
+ if ((fd = open(folder, O_RDONLY)) >= 0)
+ {
+ while (read(fd, &hdr, sizeof(HDR)) == sizeof(HDR))
+ {
+ sprintf(fpath, "brd/%s/%c/%s", brdname, hdr.xname[7], hdr.xname);
+ reaper(fpath);
+ }
+ close(fd);
+ }
+}
+
+
+static void
+expireGem(brdname)
+ char *brdname;
+{
+ int i;
+ char c;
+ char fpath[64], *str;
+ struct dirent *de;
+ DIR *dirp;
+
+ printf("Âà´« %s ºëµØ°Ï\n", brdname);
+
+ for (i = 0; i < 32; i++)
+ {
+ c = radix32[i];
+ sprintf(fpath, "gem/brd/%s/%c", brdname, c);
+
+ if (!(dirp = opendir(fpath)))
+ continue;
+
+ while (de = readdir(dirp))
+ {
+ str = de->d_name;
+ if (*str <= ' ' || *str == '.' || *str == 'F')
+ continue;
+
+ sprintf(fpath, "gem/brd/%s/%c/%s", brdname, c, str);
+ reaper(fpath);
+ }
+
+ closedir(dirp);
+ }
+}
+
+
+static void
+expireUsr(userid)
+ char *userid;
+{
+ int fd;
+ char folder[64], fpath[64];
+ HDR hdr;
+
+ printf("Âà´« %s «H¥ó\n", userid);
+
+ usr_fpath(folder, userid, FN_DIR);
+
+ if ((fd = open(folder, O_RDONLY)) >= 0)
+ {
+ while (read(fd, &hdr, sizeof(HDR)) == sizeof(HDR))
+ {
+ sprintf(fpath, "usr/%c/%s/@/%s", *userid, userid, hdr.xname);
+ reaper(fpath);
+ }
+ close(fd);
+ }
+}
+
+
+int
+main()
+{
+ int fd;
+ char c, *str, buf[64];
+ BRD brd;
+ struct dirent *de;
+ DIR *dirp;
+
+ chdir(BBSHOME);
+
+ /* Âà´«¬ÝªO/ºëµØ°Ï¤å³¹ */
+
+ if ((fd = open(FN_BRD, O_RDONLY)) >= 0)
+ {
+ while (read(fd, &brd, sizeof(BRD)) == sizeof(BRD))
+ {
+ str = brd.brdname;
+ expireBrd(str);
+ expireGem(str);
+ }
+ close(fd);
+ }
+
+ /* Âà´«¨Ï¥ÎªÌ«H¥ó */
+
+ for (c = 'a'; c <= 'z'; c++)
+ {
+ sprintf(buf, "usr/%c", c);
+ if (!(dirp = opendir(buf)))
+ continue;
+
+ while (de = readdir(dirp))
+ {
+ str = de->d_name;
+ if (*str <= ' ' || *str == '.')
+ continue;
+
+ expireUsr(str);
+ }
+
+ closedir(dirp);
+ }
+
+ return 0;
+}
diff --git a/util/tran/cola2usr.c b/util/tran/cola2usr.c
new file mode 100644
index 0000000..51bac0e
--- /dev/null
+++ b/util/tran/cola2usr.c
@@ -0,0 +1,291 @@
+/*-------------------------------------------------------*/
+/* util/cola2usr.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : Cola ¦Ü Maple 3.02 ¨Ï¥ÎªÌÂà´« */
+/* create : 03/02/11 */
+/* update : / / */
+/* author : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "cola.h"
+
+
+/* ----------------------------------------------------- */
+/* Âà´« .ACCT */
+/* ----------------------------------------------------- */
+
+
+static inline int
+is_bad_userid(userid)
+ char *userid;
+{
+ register char ch;
+
+ if (strlen(userid) < 2)
+ return 1;
+
+ if (!isalpha(*userid))
+ return 1;
+
+ if (!str_cmp(userid, "new"))
+ return 1;
+
+ while (ch = *(++userid))
+ {
+ if (!isalnum(ch))
+ return 1;
+ }
+ return 0;
+}
+
+
+static inline int
+uniq_userno(fd)
+ int fd;
+{
+ char buf[4096];
+ int userno, size;
+ SCHEMA *sp; /* record length 16 ¥i¾ã°£ 4096 */
+
+ userno = 1;
+
+ while ((size = read(fd, buf, sizeof(buf))) > 0)
+ {
+ sp = (SCHEMA *) buf;
+ do
+ {
+ if (sp->userid[0] == '\0')
+ {
+ lseek(fd, -size, SEEK_CUR);
+ return userno;
+ }
+ userno++;
+ size -= sizeof(SCHEMA);
+ sp++;
+ } while (size);
+ }
+
+ return userno;
+}
+
+
+static inline void
+creat_dirs(old)
+ userec *old;
+{
+ ACCT new;
+ SCHEMA slot;
+ int fd;
+ char fpath[64];
+ time_t now;
+
+ time(&now);
+
+ memset(&new, 0, sizeof(new));
+ memset(&slot, 0, sizeof(slot));
+
+ str_ncpy(new.userid, old->userid, sizeof(new.userid));
+ str_ncpy(new.passwd, old->passwd, sizeof(new.passwd));
+ str_ncpy(new.username, old->username, sizeof(new.username));
+ str_ncpy(new.realname, old->realname, sizeof(new.realname));
+ new.userlevel = PERM_DEFAULT;
+ new.ufo = UFO_DEFAULT_NEW;
+ new.numlogins = 1;
+ new.firstlogin = now;
+ new.lastlogin = now;
+ new.tcheck = now;
+ new.tvalid = now;
+
+ slot.uptime = now;
+ strcpy(slot.userid, new.userid);
+
+ fd = open(FN_SCHEMA, O_RDWR | O_CREAT, 0600);
+ new.userno = uniq_userno(fd);
+ write(fd, &slot, sizeof(slot));
+ close(fd);
+
+ usr_fpath(fpath, new.userid, NULL);
+ mkdir(fpath, 0700);
+ strcat(fpath, "/@");
+ mkdir(fpath, 0700);
+ usr_fpath(fpath, new.userid, "MF");
+ mkdir(fpath, 0700);
+ usr_fpath(fpath, new.userid, "gem"); /* itoc.010727: ­Ó¤HºëµØ°Ï */
+ mak_links(fpath);
+
+ usr_fpath(fpath, new.userid, ".ACCT");
+ fd = open(fpath, O_WRONLY | O_CREAT, 0600);
+ write(fd, &new, sizeof(ACCT));
+ close(fd);
+}
+
+
+/* ----------------------------------------------------- */
+/* Âഫñ¦WÀÉ¡B­pµeÀÉ */
+/* ----------------------------------------------------- */
+
+
+static inline void
+trans_sig(old)
+ userec *old;
+{
+ char buf[64], fpath[64], f_sig[20];
+
+ sprintf(buf, COLABBS_HOME "/%s/signatures", old->blank2);
+ if (dashf(buf))
+ {
+ sprintf(f_sig, "%s.1", FN_SIGN);
+ usr_fpath(fpath, old->userid, f_sig);
+ f_cp(buf, fpath, O_TRUNC);
+ }
+}
+
+
+static inline void
+trans_plans(old)
+ userec *old;
+{
+ char buf[64], fpath[64];
+
+ sprintf(buf, COLABBS_HOME "/%s/PLANS", old->blank2);
+ if (dashf(buf))
+ {
+ usr_fpath(fpath, old->userid, FN_PLANS);
+ f_cp(buf, fpath, O_TRUNC);
+ }
+}
+
+
+/* ----------------------------------------------------- */
+/* Âà´««H¥ó */
+/* ----------------------------------------------------- */
+
+
+static time_t
+trans_hdr_chrono(filename)
+ char *filename;
+{
+ char time_str[11];
+
+ /* M.1087654321.A ©Î M.987654321.A */
+ str_ncpy(time_str, filename + 2, filename[2] == '1' ? 11 : 10);
+
+ return (time_t) atoi(time_str);
+}
+
+
+static inline void
+trans_mail(old)
+ userec *old;
+{
+ int fd;
+ char *ptr, index[64], folder[64], buf[64], fpath[64];
+ fileheader fh;
+ HDR hdr;
+ time_t chrono;
+
+ sprintf(index, COLABBS_HOME "/%s/mail/.DIR", old->blank2);
+ usr_fpath(folder, old->userid, FN_DIR);
+
+ if ((fd = open(index, O_RDONLY)) >= 0)
+ {
+ while (read(fd, &fh, sizeof(fh)) == sizeof(fh))
+ {
+ sprintf(buf, COLABBS_HOME "/%s/mail/%s", old->blank2, fh.filename);
+
+ if (dashf(buf)) /* ¤å³¹Àɮצb¤~°µÂà´« */
+ {
+ char new_name[10] = "@";
+
+ /* Âà´«¤å³¹ .DIR */
+ memset(&hdr, 0, sizeof(HDR));
+ chrono = trans_hdr_chrono(fh.filename);
+ new_name[1] = radix32[chrono & 31];
+ archiv32(chrono, new_name + 1);
+
+ hdr.chrono = chrono;
+ str_ncpy(hdr.xname, new_name, sizeof(hdr.xname));
+ str_ncpy(hdr.owner, fh.owner, sizeof(hdr.owner));
+ if (ptr = strchr(hdr.owner, ' '))
+ *ptr = '\0';
+ str_ncpy(hdr.title, fh.title + 3, sizeof(hdr.title));
+ str_stamp(hdr.date, &hdr.chrono);
+ hdr.xmode = MAIL_READ; /* ³]¬°¤wŪ */
+
+ rec_add(folder, &hdr, sizeof(HDR));
+
+ /* «þ¨©ÀÉ®× */
+ usr_fpath(fpath, old->userid, "@/");
+ strcat(fpath, new_name);
+ f_cp(buf, fpath, O_TRUNC);
+ }
+ }
+ close(fd);
+ }
+}
+
+
+/* ----------------------------------------------------- */
+/* Âà´«¥Dµ{¦¡ */
+/* ----------------------------------------------------- */
+
+
+static void
+transusr(user)
+ userec *user;
+{
+ char buf[64];
+
+ printf("Âà´« %s ¨Ï¥ÎªÌ\n", user->userid);
+
+ if (is_bad_userid(user->userid))
+ {
+ printf("%s ¤£¬O¦Xªk ID\n", user->userid);
+ return;
+ }
+
+ usr_fpath(buf, user->userid, NULL);
+ if (dashd(buf))
+ {
+ printf("%s ¤w¸g¦³¦¹ ID\n", user->userid);
+ return;
+ }
+
+ creat_dirs(user);
+ trans_plans(user);
+ trans_sig(user);
+ trans_mail(user);
+}
+
+
+int
+main()
+{
+ char *str, buf[64];
+ struct dirent *de;
+ DIR *dirp;
+ userec user;
+
+ chdir(BBSHOME);
+
+ if (!(dirp = opendir(COLABBS_HOME)))
+ return -1;
+
+ while (de = readdir(dirp))
+ {
+ str = de->d_name;
+ if (*str <= ' ' || *str == '.')
+ continue;
+
+ sprintf(buf, COLABBS_HOME "/%s/USERDATA.DAT", str);
+ rec_get(buf, &user, sizeof(user), 0);
+ strcpy(user.blank2, str); /* ­É¥Î°µ¬° path */
+
+ transusr(&user);
+ }
+
+ closedir(dirp);
+
+ return 0;
+}
diff --git a/util/tran/fb.h b/util/tran/fb.h
new file mode 100644
index 0000000..12c5fab
--- /dev/null
+++ b/util/tran/fb.h
@@ -0,0 +1,154 @@
+/*----------------------------------------------------------*/
+/* util/fb/fb.h */
+/*----------------------------------------------------------*/
+/* target : firebird 3.0 Âà Maple 3.x */
+/* create : 00/11/22 */
+/* update : / / */
+/* author : hightman@263.net */
+/*----------------------------------------------------------*/
+
+
+#if 0
+
+ 1. ³]©w OLD_BBSHOME¡BFN_PASSWD¡BFN_BOARD
+ 2. ­×§ï©Ò¦³ªº old struct
+
+ 3. ¥²¶·¦b brd Âà§¹¤~¥i¥HÂà´« gem
+ 4. ¥²¶·¦b usr ¤Î brd ³£Âà§¹¤~¥i¥HÂà´« pal
+ 5. «ØÄ³Âà´«¶¶§Ç¬° usr -> brd -> gem ->pal
+
+#endif
+
+
+#include "bbs.h"
+
+
+#define OLD_BBSHOME "/home/oldbbs" /* FB */
+#define FN_PASSWD "/home/oldbbs/.PASSWDS" /* FB */
+#define FN_BOARD "/home/oldbbs/.BOARDS" /* FB */
+
+#define NOBOARD "1002"
+
+
+/* ----------------------------------------------------- */
+/* ·sºX¼Ð/Åv­­¹ïÀ³ */
+/* ----------------------------------------------------- */
+
+struct BITS
+{
+ int old;
+ int new;
+};
+typedef struct BITS BITS;
+
+
+#ifdef TRANS_BITS_BRD
+static BITS flag[] =
+{
+ {0x2, BRD_NOZAP},
+ {0x8, BRD_ANONYMOUS},
+ {0x4, BRD_NOTRAN}
+};
+#endif
+
+
+#ifdef TRANS_BITS_PERM
+static BITS perm[] =
+{
+ {0x00000001, PERM_BASIC}, /* BASIC */
+ {0x00000002, PERM_CHAT}, /* CHAT */
+ {0x00000004, PERM_PAGE}, /* PAGE */
+ {0x00000008, PERM_POST}, /* POST */
+ {0x00000010, PERM_VALID}, /* LOGIN */
+ {0x00000020, PERM_DENYPOST}, /* DENYPOST */
+ {0x00000040, PERM_CLOAK}, /* CLOAK */
+ {0x00000080, PERM_SEECLOAK}, /* SEECLOAK */
+ {0x00000100, PERM_XEMPT}, /* XEMPT */
+ {0x00000400, PERM_BM}, /* BM */
+ {0x00000800, PERM_ACCOUNTS}, /* ACCOUNTS */
+ {0x00001000, PERM_CHATROOM}, /* CHATROOM */
+ {0x00002000, PERM_BOARD}, /* BOARD */
+ {0x00004000, PERM_SYSOP} /* SYSOP */
+};
+#endif
+
+
+/* ----------------------------------------------------- */
+/* old .PASSWDS struct : 512 bytes */
+/* ----------------------------------------------------- */
+
+struct userec
+{ /* Structure used to hold information in */
+ char userid[15];
+ time_t firstlogin;
+ char lasthost[16];
+ unsigned int numlogins;
+ unsigned int numposts;
+ char flags[2];
+ char passwd[14]; /* ­Y¬O MD5¡A­n±N 14 §ï¦¨ 35 */
+ char username[40];
+ char ident[40];
+ char termtype[16];
+ char reginfo[80 - 16];
+ unsigned int userlevel;
+ time_t lastlogin;
+ time_t stay;
+ char realname[40];
+ char address[80];
+ char email[80 - 12];
+ unsigned int nummails;
+ time_t lastjustify;
+ char gender;
+ unsigned char birthyear;
+ unsigned char birthmonth;
+ unsigned char birthday;
+ int signature;
+ unsigned int userdefine;
+ time_t notedate;
+ int noteline;
+};
+typedef struct userec userec;
+
+
+/* ----------------------------------------------------- */
+/* old DIR of board struct : 256 bytes */
+/* ----------------------------------------------------- */
+
+struct fileheader
+{ /* This structure is used to hold data in */
+ char filename[80]; /* the DIR files */
+ char owner[80];
+ char title[80];
+ unsigned level;
+ unsigned char accessed[12]; /* struct size = 256 bytes */
+};
+typedef struct fileheader fileheader;
+
+
+/* ----------------------------------------------------- */
+/* old BOARDS struct : 276 bytes */
+/* ----------------------------------------------------- */
+
+struct boardheader
+{ /* This structure is used to hold data i n */
+ char filename[80]; /* the BOARDS files */
+ char owner[80 - 60];
+ char BM[80 - 1];
+ char flag;
+ char title[80];
+ unsigned level;
+ unsigned char accessed[12];
+};
+typedef struct boardheader boardheader;
+
+
+/* ----------------------------------------------------- */
+/* old FRIEND struct : 128 bytes */
+/* ----------------------------------------------------- */
+
+struct FRIEND
+{
+ char id[13];
+ char exp[40];
+};
+typedef struct FRIEND FRIEND;
diff --git a/util/tran/fb2brd.c b/util/tran/fb2brd.c
new file mode 100644
index 0000000..8c8d66e
--- /dev/null
+++ b/util/tran/fb2brd.c
@@ -0,0 +1,234 @@
+/*-------------------------------------------------------*/
+/* util/fb/fb2brd.c */
+/*-------------------------------------------------------*/
+/* target : firebird 3.0 Âà Maple 3.x ¬ÝªO */
+/* .BOARDS => .BRD */
+/* create : 00/11/22 */
+/* update : / / */
+/* author : hightman@263.net */
+/* modify : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#define TRANS_BITS_BRD
+#define TRANS_BITS_PERM
+
+
+#include "fb.h"
+
+
+static inline void
+trans_hdr_stamp(folder, t, hdr, fpath)
+ char *folder;
+ time_t t;
+ HDR *hdr;
+ char *fpath;
+{
+ FILE *fp;
+ char *fname, *family;
+ int rc;
+
+ fname = fpath;
+ while (rc = *folder++)
+ {
+ *fname++ = rc;
+ if (rc == '/')
+ family = fname;
+ }
+ fname = family + 1;
+ *fname++ = '/';
+ *fname++ = 'A';
+
+ for (;;)
+ {
+ *family = radix32[t & 31];
+ archiv32(t, fname);
+
+ if (fp = fopen(fpath, "r"))
+ {
+ fclose(fp);
+ t++;
+ }
+ else
+ {
+ memset(hdr, 0, sizeof(HDR));
+ hdr->chrono = t;
+ str_stamp(hdr->date, &hdr->chrono);
+ strcpy(hdr->xname, --fname);
+ break;
+ }
+ }
+}
+
+
+/* ----------------------------------------------------- */
+/* Âà´«¥Dµ{¦¡ */
+/* ----------------------------------------------------- */
+
+
+static void
+transbrd(bh)
+ boardheader *bh;
+{
+ static time_t stamp = 0;
+
+ int fd;
+ char *ptr, index[64], folder[64], buf[64], fpath[64];
+ fileheader fh;
+ HDR hdr;
+ BRD newboard;
+ BITS *p;
+ time_t chrono;
+
+ printf("Âà´« %s ¬ÝªO\n", bh->filename);
+
+ brd_fpath(buf, bh->filename, NULL);
+ if (dashd(buf))
+ {
+ printf("%s ¤w¸g¦³¦¹¬ÝªO\n", bh->filename);
+ return;
+ }
+
+ if (!stamp)
+ time(&stamp);
+
+ /* Âà´« .BRD */
+
+ memset(&newboard, 0, sizeof(BRD));
+ str_ncpy(newboard.brdname, bh->filename, sizeof(newboard.brdname));
+ str_ncpy(newboard.class, (bh->flag |= 0x4) ? "¡ô¡õ" : "¡÷¡ö", sizeof(newboard.class));
+ str_ncpy(newboard.title, bh->title + 10, sizeof(newboard.title));
+ newboard.bstamp = stamp++;
+ if (bh->BM[0] > ' ')
+ {
+ /* ±N©Ò¦³ªº ',' ´«¦¨ '/' */
+ for (ptr = bh->BM; *ptr; ptr++)
+ {
+ if (*ptr == ',')
+ *ptr = '/';
+ }
+ str_ncpy(newboard.BM, bh->BM, sizeof(newboard.BM));
+ }
+ for (p = flag; p->old; p++)
+ {
+ if (bh->flag & p->old)
+ newboard.battr |= p->new;
+ }
+ for (p = perm; p->old; p++)
+ {
+ if (bh->level & p->old)
+ newboard.readlevel |= p->new;
+ }
+ newboard.postlevel = newboard.readlevel;
+ rec_add(FN_BRD, &newboard, sizeof(BRD));
+
+ /* ¶}·s¥Ø¿ý */
+
+ sprintf(fpath, "gem/brd/%s", newboard.brdname);
+ mak_dirs(fpath);
+ mak_dirs(fpath + 4);
+
+ /* Âà´«¶iªOµe­± */
+
+ sprintf(buf, OLD_BBSHOME "/boards/%s/notes", bh->filename);
+
+ if (dashf(buf))
+ {
+ brd_fpath(fpath, newboard.brdname, FN_NOTE);
+ f_cp(buf, fpath, O_TRUNC);
+ }
+
+ /* Âà´«§ë²¼µ²ªG */
+
+ sprintf(buf, OLD_BBSHOME "/boards/%s/results", bh->filename);
+
+ if (dashf(buf))
+ {
+ brd_fpath(fpath, newboard.brdname, "@/@vote");
+ f_cp(buf, fpath, O_TRUNC);
+ }
+
+ /* Âà´«¤å³¹ */
+
+ sprintf(index, OLD_BBSHOME "/boards/%s/.DIR", bh->filename); /* 廼 .DIR */
+ brd_fpath(folder, newboard.brdname, ".DIR"); /* ·sªº .DIR */
+
+ if ((fd = open(index, O_RDONLY)) >= 0)
+ {
+ while (read(fd, &fh, sizeof(fh)) == sizeof(fh))
+ {
+ sprintf(buf, OLD_BBSHOME "/boards/%s/%s", bh->filename, fh.filename);
+ if (dashf(buf)) /* ¤å³¹Àɮצb¤~°µÂà´« */
+ {
+ struct stat st;
+
+ /* Âà´«¤å³¹ .DIR */
+ memset(&hdr, 0, sizeof(HDR));
+ stat(buf, &st);
+ chrono = st.st_mtime;
+ trans_hdr_stamp(folder, chrono, &hdr, fpath);
+ str_ncpy(hdr.owner, fh.owner, sizeof(hdr.owner));
+ str_ansi(hdr.title, fh.title, sizeof(hdr.title));
+ hdr.xmode = 0;
+ rec_add(folder, &hdr, sizeof(HDR));
+
+ /* «þ¨©ÀÉ®× */
+ f_cp(buf, fpath, O_TRUNC);
+ }
+ }
+ close(fd);
+ }
+
+
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int fd;
+ boardheader bh;
+
+ /* argc == 1 Âà¥þ³¡ªO */
+ /* argc == 2 Âà¬Y¯S©wªO */
+
+ if (argc > 2)
+ {
+ printf("Usage: %s [target_board]\n", argv[0]);
+ exit(-1);
+ }
+
+ chdir(BBSHOME);
+
+ if (!dashf(FN_BOARD))
+ {
+ printf("ERROR! Can't open " FN_BOARD "\n");
+ exit(-1);
+ }
+ if (!dashd(OLD_BBSHOME "/boards"))
+ {
+ printf("ERROR! Can't open " OLD_BBSHOME "/boards\n");
+ exit(-1);
+ }
+
+ if ((fd = open(FN_BOARD, O_RDONLY)) >= 0)
+ {
+ while (read(fd, &bh, sizeof(bh)) == sizeof(bh))
+ {
+ if (argc == 1)
+ {
+ transbrd(&bh);
+ }
+ else if (!strcmp(bh.filename, argv[1]))
+ {
+ transbrd(&bh);
+ exit(1);
+ }
+ }
+ close(fd);
+ }
+
+ exit(0);
+}
diff --git a/util/tran/fb2gem.c b/util/tran/fb2gem.c
new file mode 100644
index 0000000..f4488b9
--- /dev/null
+++ b/util/tran/fb2gem.c
@@ -0,0 +1,381 @@
+/*-------------------------------------------------------*/
+/* util/fb/fb2gem.c */
+/*-------------------------------------------------------*/
+/* target : firebird 3.0 Âà Maple 3.x ºëµØ°Ï */
+/* 0Announce => gem/@Class */
+/* create : 00/11/22 */
+/* update : / / */
+/* author : hightman@263.net */
+/*----------------------------------------------------------*/
+
+
+#include "fb.h"
+
+
+static int ndir;
+static int nfile;
+static time_t chrono;
+static int level;
+static char gpath[1024];
+static char tmp_name[128];
+
+static int mandex(char *board, char *fpath, int deep);
+static void Names_class(char *fpath);
+
+static char *fn_names = ".Names";
+
+
+/* ------------------------------------------------------------- */
+/* «Ø¥ß gem/.DIR ¤Î gem/@/@Class */
+/* ------------------------------------------------------------- */
+
+
+static void
+new_class()
+{
+ HDR hdr;
+
+ memset(&hdr, 0, sizeof(HDR));
+ time(&hdr.chrono);
+ strcpy(hdr.owner, STR_SYSOP);
+ strcpy(hdr.nick, SYSOPNICK);
+ str_stamp(hdr.date, &hdr.chrono);
+ strcpy(hdr.xname, "@Class");
+ strcpy(hdr.title, "Class/ ¬ÝªOºëµØ°Ï");
+ hdr.xmode = GEM_FOLDER;
+ rec_add("gem/.DIR", &hdr, sizeof(HDR));
+}
+
+
+/* ------------------------------------------------------------- */
+/* Tran_Group */
+/* ------------------------------------------------------------- */
+
+
+static void
+tran_group(title, fname, flag)
+ char *title;
+ char *fname;
+ int flag;
+{
+ HDR hdr;
+ char buf[1024];
+
+ memset(&hdr, 0, sizeof(HDR));
+ time(&hdr.chrono);
+ strcpy(hdr.owner, STR_SYSOP);
+ strcpy(hdr.nick, SYSOPNICK);
+ str_stamp(hdr.date, &hdr.chrono);
+
+ if (flag == 1) /* ¨÷©v */
+ {
+ strcpy(tmp_name, fname);
+
+ sprintf(hdr.xname, "@%s", fname);
+ str_ncpy(hdr.title, title, sizeof(hdr.title));
+ hdr.xmode = GEM_FOLDER;
+ rec_add("gem/@/@Class", &hdr, sizeof(HDR));
+
+ sprintf(buf, "%s/0Announce/groups/%s/%s", OLD_BBSHOME, fname, fn_names);
+ Names_class(buf);
+ }
+ else if (flag == 2) /* ¬ÝªO */
+ {
+ strcpy(hdr.xname, fname);
+ str_ncpy(hdr.title, title, sizeof(hdr.title));
+ hdr.xmode = GEM_BOARD | GEM_FOLDER;
+
+ sprintf(buf, "gem/@/@%s", tmp_name);
+ rec_add(buf, &hdr, sizeof(HDR));
+
+ sprintf(buf, "%s/0Announce/groups/%s/%s/%s", OLD_BBSHOME, tmp_name, fname, fn_names);
+ mandex(fname, buf, 0);
+ }
+}
+
+
+/* ------------------------------------------------------------- */
+/* Names Path ... */
+/* ------------------------------------------------------------- */
+
+
+static void
+Names_class(fpath)
+ char *fpath;
+{
+ FILE *fp;
+ char *ptr, buf[256];
+ char title[TTLEN + 1], fname[32];
+ int flag;
+
+ if (fp = fopen(fpath, "r"))
+ {
+ while (fgets(buf, sizeof(buf), fp))
+ {
+ if (ptr = strchr(buf, '\n'))
+ *ptr = '\0';
+
+ if (!memcmp(buf, "Name=", 5))
+ {
+ str_ncpy(title, buf + 5, sizeof(title));
+ }
+ else if (!memcmp(buf, "Path=", 5))
+ {
+ ptr = buf + 7;
+ if (!memcmp(ptr, "../", 3) || (*ptr == '/') || !strcmp(ptr, ".index") || (*ptr == '\0'))
+ continue;
+
+ str_ncpy(fname, ptr, sizeof(fname));
+ if (strstr(fname, "GROUP"))
+ flag = 1;
+ else
+ flag = 2;
+
+ tran_group(title, fname, flag);
+ }
+ }
+
+ fclose(fp);
+ }
+}
+
+
+/* ------------------------------------------------------------- */
+/* Group */
+/* ------------------------------------------------------------- */
+
+
+static void
+group_class()
+{
+ char fpath[1024];
+
+ sprintf(fpath, "%s/0Announce/groups/%s", OLD_BBSHOME, fn_names);
+ Names_class(fpath);
+}
+
+
+/* ------------------------------------------------------------- */
+/* 0Announce */
+/* ------------------------------------------------------------- */
+
+
+static int
+mandex(board, fpath, deep)
+ char *board;
+ char *fpath;
+ int deep;
+{
+ FILE *fgem, *fxx;
+ char *fname, *gname, *ptr, *str, buf[256], site[64];
+ struct stat st;
+ int cc, xmode, gport;
+ HDR ghdr;
+
+ if (board)
+ {
+ level = 0;
+ if (strcmp(board, NOBOARD))
+ {
+ sprintf(gpath, "gem/brd/%s", board);
+ mkdir(gpath, 0700);
+ }
+ cc = '0';
+ for (;;)
+ {
+ if (!strcmp(board, NOBOARD))
+ sprintf(gpath, "gem/%c", cc);
+ else
+ sprintf(gpath, "gem/brd/%s/%c", board, cc);
+ mkdir(gpath, 0700);
+ if (cc == '9')
+ cc = '@';
+ else
+ {
+ if (++cc == 'W')
+ break;
+ }
+ }
+ if (!strcmp(board, NOBOARD))
+ sprintf(gpath, "gem/.DIR");
+ else
+ sprintf(gpath, "gem/brd/%s/.DIR", board);
+ ndir = nfile = 0;
+ chrono = 10000;
+ }
+
+ if (!(gname = strrchr(gpath, '/')))
+ return -1;
+ if (gname[1] == '.')
+ gname++;
+ else
+ gname--;
+
+ if (!(fxx = fopen(gpath, "a")))
+ return -1;
+
+ if (!(fgem = fopen(fpath, "r")))
+ {
+ fclose(fxx);
+ return -1;
+ }
+
+ if (deep > level)
+ level = deep;
+
+ gport = -1;
+ fname = strrchr(fpath, '.');
+
+ while (fgets(buf, sizeof(buf), fgem))
+ {
+ if (gport < 0)
+ {
+ *fname = xmode = gport = 0;
+ memset(&ghdr, 0, sizeof(HDR));
+ }
+ ptr = buf + 5;
+ if (str = strchr(buf, '\n'))
+ *str = '\0';
+ if (!memcmp(buf, "Name=", 5))
+ {
+ if (!memcmp(ptr, "¡ó ", 3) || !memcmp(ptr, "¡ô ", 3) || !memcmp(ptr, "¡ñ", 3) ||
+ !memcmp(ptr, "¡ð ", 3) || !memcmp(ptr, "¡Ñ ", 3))
+ {
+ ptr += 3;
+ }
+ else if (!memcmp(ptr, "¡õ ", 3) || !memcmp(ptr, "¡ö ", 3))
+ {
+ ptr += 3;
+ gport = 70;
+ }
+ if (*ptr == '#')
+ {
+ if (*++ptr == ' ')
+ ptr++;
+ xmode = GEM_RESTRICT;
+ }
+ str_ncpy(ghdr.title, ptr, sizeof(ghdr.title));
+ }
+ else if (!memcmp(buf, "Edit=", 5)) /* server */
+ {
+ if (gport)
+ {
+ str = site;
+ do
+ {
+ cc = *ptr++;
+ if (cc >= 'A' && cc <= 'Z')
+ cc |= 0x20;
+ *str++ = cc;
+ } while (cc);
+ }
+ else
+ {
+ str_ncpy(ghdr.owner, ptr, sizeof(ghdr.owner));
+ }
+ }
+ else if (!memcmp(buf, "Date=", 5)) /* port */
+ {
+ if (gport)
+ gport = atoi(ptr);
+ else if (ptr[2] != '/' || ptr[5] != '/')
+ str_ncpy(ghdr.date, ptr, sizeof(ghdr.date));
+ }
+ else if (!memcmp(buf, "Path=", 5))
+ {
+ ptr += 2;
+ if (!memcmp(ptr, "../", 3) || (*ptr == '/') || !strcmp(ptr, ".index"))
+ {
+ printf("\tskip: %s, %s\n", fpath, ptr);
+ gport = -1;
+ continue;
+ }
+ if (!memcmp(ptr, "groups", 6) && deep == 0)
+ {
+ printf("GROUPS! wait...\n");
+ group_class();
+ gport = -1;
+ continue;
+ }
+ ghdr.chrono = ++chrono;
+
+ if (gport) /* Gopher ´N·í¦¨¤@¯ë¤å³¹©Î¨÷©v */
+ {
+ xmode = 0;
+ if (*ptr == '1')
+ xmode |= GEM_FOLDER;
+ sprintf(ghdr.xname, "%s/%s", site, ptr);
+ }
+ else
+ {
+ strcpy(fname, ptr);
+ if (stat(fpath, &st))
+ {
+ gport = -1;
+ continue;
+ }
+ archiv32(chrono, gname + 3);
+ *gname = gname[9];
+ gname[1] = '/';
+ str = ghdr.date;
+ if (!*str)
+ {
+ struct tm *p = localtime(&st.st_mtime);
+ sprintf(str, "%02d/%02d/%02d", p->tm_year % 100, p->tm_mon + 1, p->tm_mday);
+ }
+ if (S_ISREG(st.st_mode))/* file */
+ {
+ gname[2] = 'A';
+ strcpy(ghdr.xname, gname + 2);
+ link(fpath, gpath);
+ nfile++;
+ }
+ else if (S_ISDIR(st.st_mode)) /* dir */
+ {
+ gname[2] = 'F';
+ strcpy(ghdr.xname, gname + 2);
+ strcat(fpath, "/");
+ strcat(fpath, fn_names);
+ if (mandex(NULL, fpath, deep + 1))
+ {
+ gport = -1;
+ continue;
+ }
+ xmode |= GEM_FOLDER;
+ ndir++;
+ }
+ else
+ {
+ gport = -1;
+ continue;
+ }
+ }
+ ghdr.xmode = xmode;
+ fwrite(&ghdr, sizeof(HDR), 1, fxx);
+ gport = -1;
+ }
+ }
+ fclose(fxx);
+ fclose(fgem);
+
+ if (board) /* report */
+ printf("\td:%d\tf:%d\t(%d)\n", ndir, nfile, level);
+
+ return 0;
+}
+
+
+int
+main()
+{
+ char fpath[1024];
+
+ chdir(BBSHOME);
+
+ new_class();
+
+ sprintf(fpath, "%s/0Announce/%s", OLD_BBSHOME, fn_names);
+ mandex(NOBOARD, fpath, 0);
+
+ return 0;
+}
diff --git a/util/tran/fb2pal.c b/util/tran/fb2pal.c
new file mode 100644
index 0000000..3aae6c7
--- /dev/null
+++ b/util/tran/fb2pal.c
@@ -0,0 +1,177 @@
+/*-------------------------------------------------------*/
+/* util/transpal.c */
+/*-------------------------------------------------------*/
+/* target : WD ¦Ü Maple 3.02 (¬ÝªO)¦n¤Í¦W³æÂà´« */
+/* create : 01/09/08 */
+/* update : / / */
+/* author : itoc.bbs@bbs.ee.nctu.edu.tw */
+/*-------------------------------------------------------*/
+/* syntax : transpal */
+/*-------------------------------------------------------*/
+
+
+#if 0
+
+ 1. ­×§ï struct FRIEND ©M transpal() transbrdpal()
+ 2. Âà´«(¬ÝªO)¦n¤Í¦W³æ¤§«e¡A±z¥²¶·¥ýÂà´«§¹¬ÝªO¤Î¨Ï¥ÎªÌ¡C
+
+ ps. ¨Ï¥Î«e½Ð¥ý¦æ³Æ¥÷¡Ause on ur own risk. µ{¦¡©å¦H½Ð¥]²[ :p
+ ps. ·PÁ lkchu ªº Maple 3.02 for FreeBSD
+
+#endif
+
+
+#include "fb.h"
+
+
+static char uperid[80]; /* ¤j¼gªº ID */
+
+static void /* ´«¤j¼g */
+str_uper(dst, src)
+ char *dst, *src;
+{
+ int ch;
+ do
+ {
+ ch = *src++;
+ if (ch >= 'a' && ch <= 'z')
+ ch = ch - 32;
+ *dst++ = ch;
+ } while (ch);
+}
+
+
+/* ----------------------------------------------------- */
+/* 3.02 functions */
+/* ----------------------------------------------------- */
+
+
+static int
+acct_uno(userid)
+ char *userid;
+{
+ int fd;
+ int userno;
+ char fpath[64];
+
+ usr_fpath(fpath, userid, FN_ACCT);
+ fd = open(fpath, O_RDONLY);
+ if (fd >= 0)
+ {
+ read(fd, &userno, sizeof(userno));
+ close(fd);
+ return userno;
+ }
+ return -1;
+}
+
+
+/* ----------------------------------------------------- */
+/* Âà´«¥Dµ{¦¡ */
+/* ----------------------------------------------------- */
+
+
+static void
+transpal(userid)
+ char *userid;
+{
+ ACCT acct;
+ int fd, friend_userno, i;
+ char fpath[64], buf[64];
+ PAL pal;
+ FRIEND friend;
+
+ /* FireBird ¬O¥Î¤j¼g id */
+ str_uper(uperid, userid);
+
+ /* FB ªº usr ¥Ø¿ý¦³¤À¤j¤p¼g¡A©Ò¥H­n¥ý¨ú±o¤j¤p¼g */
+ usr_fpath(buf, userid, FN_ACCT);
+ if ((fd = open(buf, O_RDONLY)) >= 0)
+ {
+ read(fd, &acct, sizeof(ACCT));
+ close(fd);
+ }
+ else
+ {
+ return;
+ }
+
+ usr_fpath(fpath, userid, FN_PAL); /* ·sªº¦n¤Í¦W³æ */
+
+ if (dashf(fpath))
+ unlink(fpath); /* ²M±¼­««Ø */
+
+ for (i = 0; i <= 1; i++) /* ¦n¤Í¦W³æ/Ãa¤H¦W³æ ¤G¥÷ */
+ {
+ sprintf(buf, OLD_BBSHOME "/home/%c/%s/%s", *uperid, acct.userid, i ? "friends" : "rejects"); /* ªº¦n¤Í¦W³æ */
+
+ if ((fd = open(buf, O_RDONLY)) < 0)
+ return;
+
+ while (read(fd, &friend, sizeof(FRIEND)) == sizeof(FRIEND))
+ {
+ if ((friend_userno = acct_uno(friend.id)) >= 0 &&
+ strcmp(friend.id, acct.userid))
+ {
+ memset(&pal, 0, sizeof(PAL));
+ str_ncpy(pal.userid, friend.id, sizeof(pal.userid));
+ pal.ftype = i ? 0 : PAL_BAD; /* ¦n¤Í vs ·l¤Í */
+ str_ncpy(pal.ship, friend.exp, sizeof(pal.ship));
+ pal.userno = friend_userno;
+ rec_add(fpath, &pal, sizeof(PAL));
+ }
+ }
+ close(fd);
+ }
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ char c, *str;
+ char buf[64];
+ struct dirent *de;
+ DIR *dirp;
+
+ /* argc == 1 Âà¥þ³¡¨Ï¥ÎªÌ¤Î¬ÝªO */
+ /* argc == 2 Âà¬Y¯S©w¨Ï¥ÎªÌ */
+
+ if (argc > 2)
+ {
+ printf("Usage: %s [target_user]\n", argv[0]);
+ exit(-1);
+ }
+
+ chdir(BBSHOME);
+
+ if (argc == 2)
+ {
+ transpal(argv[1]);
+ exit(1);
+ }
+
+ /* Âà´«¨Ï¥ÎªÌ¦n¤Í¦W³æ */
+ for (c = 'a'; c <= 'z'; c++)
+ {
+ sprintf(buf, "usr/%c", c);
+
+ if (!(dirp = opendir(buf)))
+ continue;
+
+ while (de = readdir(dirp))
+ {
+ str = de->d_name;
+ if (*str <= ' ' || *str == '.')
+ continue;
+
+ transpal(str);
+ }
+
+ closedir(dirp);
+ }
+
+ return 0;
+}
diff --git a/util/tran/fb2usr.c b/util/tran/fb2usr.c
new file mode 100644
index 0000000..41f8893
--- /dev/null
+++ b/util/tran/fb2usr.c
@@ -0,0 +1,328 @@
+/*-------------------------------------------------------*/
+/* util/fb/fb2usr.c */
+/*-------------------------------------------------------*/
+/* target : firebird 3.0 Âà Maple 3.x ¨Ï¥ÎªÌ¸ê®Æ */
+/* .PASSWDS => .USR .ACCT */
+/* create : 00/11/22 */
+/* update : / / */
+/* author : hightman@263.net */
+/* modify : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#define TRANS_BITS_PERM
+
+#include "fb.h"
+
+
+static char uperid[80]; /* ¤j¼gªº ID */
+
+
+/* ----------------------------------------------------- */
+/* Âà´« .ACCT */
+/* ----------------------------------------------------- */
+
+
+static inline int
+is_bad_userid(userid)
+ char *userid;
+{
+ register char ch;
+
+ if (strlen(userid) < 2)
+ return 1;
+
+ if (!isalpha(*userid))
+ return 1;
+
+ if (!str_cmp(userid, "new"))
+ return 1;
+
+ while (ch = *(++userid))
+ {
+ if (!isalnum(ch))
+ return 1;
+ }
+ return 0;
+}
+
+
+static inline int
+uniq_userno(fd)
+ int fd;
+{
+ char buf[4096];
+ int userno, size;
+ SCHEMA *sp; /* record length 16 ¥i¾ã°£ 4096 */
+
+ userno = 1;
+
+ while ((size = read(fd, buf, sizeof(buf))) > 0)
+ {
+ sp = (SCHEMA *) buf;
+ do
+ {
+ if (sp->userid[0] == '\0')
+ {
+ lseek(fd, -size, SEEK_CUR);
+ return userno;
+ }
+ userno++;
+ size -= sizeof(SCHEMA);
+ sp++;
+ } while (size);
+ }
+
+ return userno;
+}
+
+
+static void /* ´«¤j¼g */
+str_uper(dst, src)
+ char *dst, *src;
+{
+ int ch;
+ do
+ {
+ ch = *src++;
+ if (ch >= 'a' && ch <= 'z')
+ ch = ch - 32;
+ *dst++ = ch;
+ } while (ch);
+}
+
+
+static inline void
+creat_dirs(old)
+ userec *old;
+{
+ ACCT new;
+ SCHEMA slot;
+ int fd;
+ char fpath[64];
+ BITS *p;
+
+ memset(&new, 0, sizeof(ACCT));
+ memset(&slot, 0, sizeof(slot));
+
+ str_ncpy(new.userid, old->userid, sizeof(new.userid));
+ str_ncpy(new.passwd, old->passwd, sizeof(new.passwd));
+ str_ncpy(new.realname, old->realname, sizeof(new.realname));
+ str_ncpy(new.username, old->username, sizeof(new.username));
+
+ for (p = perm; p->old; p++)
+ {
+ if (old->userlevel & p->old)
+ new.userlevel |= p->new;
+ }
+ new.ufo = UFO_DEFAULT_NEW;
+ new.signature = old->signature;
+
+ new.year = old->birthyear;
+ new.month = old->birthmonth;
+ new.day = old->birthday;
+ new.sex = 1;
+ new.money = 0;
+ new.gold = 0;
+
+ new.numlogins = old->numlogins;
+ new.numposts = old->numposts;
+ new.numemails = old->nummails;
+
+ new.firstlogin = old->firstlogin;
+ new.lastlogin = old->lastlogin;
+ time(&new.tcheck);
+ time(&new.tvalid);
+
+ str_ncpy(new.lasthost, old->lasthost, sizeof(new.lasthost));
+ str_ncpy(new.email, old->email, sizeof(new.email));
+
+ slot.uptime = time(0);
+ strcpy(slot.userid, new.userid);
+
+ fd = open(FN_SCHEMA, O_RDWR | O_CREAT, 0600);
+ new.userno = uniq_userno(fd);
+ write(fd, &slot, sizeof(slot));
+ close(fd);
+
+ usr_fpath(fpath, new.userid, NULL);
+ mkdir(fpath, 0700);
+ strcat(fpath, "/@");
+ mkdir(fpath, 0700);
+ usr_fpath(fpath, new.userid, "MF");
+ mkdir(fpath, 0700);
+ usr_fpath(fpath, new.userid, "gem"); /* itoc.010727: ­Ó¤HºëµØ°Ï */
+ mak_links(fpath);
+
+ usr_fpath(fpath, new.userid, ".ACCT");
+ fd = open(fpath, O_WRONLY | O_CREAT, 0600);
+ write(fd, &new, sizeof(ACCT));
+ close(fd);
+}
+
+
+/* ----------------------------------------------------- */
+/* Âà´««H¥ó */
+/* ----------------------------------------------------- */
+
+
+static inline void
+trans_mail(old)
+ userec *old;
+{
+ int fd;
+ char index[64], folder[64], buf[64], fpath[64];
+ fileheader fh;
+ HDR hdr;
+ time_t chrono;
+
+ time(&chrono);
+
+ sprintf(index, OLD_BBSHOME "/mail/%c/%s/.DIR", *uperid, old->userid);
+ usr_fpath(folder, old->userid, FN_DIR);
+
+ if ((fd = open(index, O_RDONLY)) >= 0)
+ {
+ while (read(fd, &fh, sizeof(fh)) == sizeof(fh))
+ {
+ sprintf(buf, OLD_BBSHOME "/mail/%c/%s/%s", *uperid, old->userid, fh.filename);
+
+ if (dashf(buf)) /* ¤å³¹Àɮצb¤~°µÂà´« */
+ {
+ char new_name[10] = "@";
+
+ /* Âà´«¤å³¹ .DIR */
+ memset(&hdr, 0, sizeof(HDR));
+ chrono++;
+ new_name[1] = radix32[chrono & 31];
+ archiv32(chrono, new_name + 1);
+
+ hdr.chrono = chrono;
+ str_ncpy(hdr.xname, new_name, sizeof(hdr.xname));
+ str_ncpy(hdr.owner, fh.owner, sizeof(hdr.owner));
+ str_ncpy(hdr.title, fh.title, sizeof(hdr.title));
+ str_stamp(hdr.date, &chrono);
+ hdr.xmode = MAIL_READ; /* ³]¬°¤wŪ */
+
+ rec_add(folder, &hdr, sizeof(HDR));
+
+ /* «þ¨©ÀÉ®× */
+ usr_fpath(fpath, old->userid, "@/");
+ strcat(fpath, new_name);
+ f_cp(buf, fpath, O_TRUNC);
+ }
+ }
+ close(fd);
+ }
+
+
+}
+
+
+/* ----------------------------------------------------- */
+/* Âà´«¥Dµ{¦¡ */
+/* ----------------------------------------------------- */
+
+
+static void
+transusr(user)
+ userec *user;
+{
+ char buf[64], fpath[64];
+
+ printf("Âà´« %s ¨Ï¥ÎªÌ\n", user->userid);
+
+ if (is_bad_userid(user->userid))
+ {
+ printf("%s ¤£¬O¦Xªk ID\n", user->userid);
+ return;
+ }
+
+ usr_fpath(buf, user->userid, NULL);
+ if (dashd(buf))
+ {
+ printf("%s ¤w¸g¦³¦¹ ID\n", user->userid);
+ return;
+ }
+
+ /* FireBird ¬O¥Î¤j¼g id */
+ str_uper(uperid, user->userid);
+
+ sprintf(buf, OLD_BBSHOME "/home/%c/%s", *uperid, user->userid);
+ if (!dashd(buf))
+ {
+ printf("%s ªºÀɮפ£¦s¦b\n", user->userid);
+ return;
+ }
+
+ /* Âà´« .ACCT */
+ creat_dirs(user);
+
+ /* Âà´«­pµeÀÉ/ñ¦WÀÉ */
+ sprintf(buf, OLD_BBSHOME "/home/%c/%s/plans", *uperid, user->userid);
+ if (dashf(buf))
+ {
+ usr_fpath(fpath, user->userid, FN_PLANS);
+ f_cp(buf, fpath, O_TRUNC);
+ }
+ sprintf(buf, OLD_BBSHOME "/home/%c/%s/signatures", *uperid, user->userid);
+ if (dashf(buf))
+ {
+ usr_fpath(fpath, user->userid, FN_SIGN".1");
+ f_cp(buf, fpath, O_TRUNC);
+ }
+
+ /* Âà´««H¥ó */
+ trans_mail(user);
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int fd;
+ userec user;
+
+ /* argc == 1 Âà¥þ³¡¨Ï¥ÎªÌ */
+ /* argc == 2 Âà¬Y¯S©w¨Ï¥ÎªÌ */
+
+ if (argc > 2)
+ {
+ printf("Usage: %s [target_user]\n", argv[0]);
+ exit(-1);
+ }
+
+ chdir(BBSHOME);
+
+ if (!dashf(FN_PASSWD))
+ {
+ printf("ERROR! Can't open " FN_PASSWD "\n");
+ exit(-1);
+ }
+ if (!dashd(OLD_BBSHOME "/home"))
+ {
+ printf("ERROR! Can't open " OLD_BBSHOME "/home\n");
+ exit(-1);
+ }
+
+ if ((fd = open(FN_PASSWD, O_RDONLY)) >= 0)
+ {
+ while (read(fd, &user, sizeof(user)) == sizeof(user))
+ {
+ if (argc == 1)
+ {
+ transusr(&user);
+ }
+ else if (!strcmp(user.userid, argv[1]))
+ {
+ transusr(&user);
+ exit(1);
+ }
+ }
+ close(fd);
+ }
+
+ exit(0);
+}
diff --git a/util/tran/mag.h b/util/tran/mag.h
new file mode 100644
index 0000000..8c6f934
--- /dev/null
+++ b/util/tran/mag.h
@@ -0,0 +1,111 @@
+/*-------------------------------------------------------*/
+/* util/mag.h */
+/*-------------------------------------------------------*/
+/* target : Magic ¦Ü Maple 3.02 ¨Ï¥ÎªÌÂà´« */
+/* create : 02/01/03 */
+/* update : / / */
+/* author : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#if 0
+
+ 1. ³]©w FN_PASSWD¡BFN_BOARD¡BOLD_MAILPATH¡BOLD_BOARDPATH¡BOLD_MANPATH
+ 2. ­×§ï©Ò¦³ªº old struct
+
+ 3. §â ¯¸ªº mail ²¾¨ì OLD_MAILPATH
+ 4. §â ¯¸ªº boards/brdname ²¾¨ì OLD_BOARDPATH/brdname
+ 5. §â ¯¸ªº 0Announce/groups/ooxx.faq/brdname ²¾¨ì OLD_MANPATH/brdname
+
+ 6. ¥²¶·¦b brd Âà§¹¤~¥i¥HÂà´« gem
+ 7. «ØÄ³Âà´«¶¶§Ç¬° usr -> brd -> gem
+
+#endif
+
+
+#include "bbs.h"
+
+
+#define FN_PASSWDS "/home/oldbbs/.PASSWDS"
+#define FN_BOARDS "/home/oldbbs/.BOARDS"
+#define OLD_MAILPATH "/home/oldbbs/mail"
+#define OLD_BOARDPATH "/home/oldbbs/boards"
+#define OLD_MANPATH "/home/oldbbs/groups"
+
+
+/* ----------------------------------------------------- */
+/* old .PASSWDS struct : 512 bytes */
+/* ----------------------------------------------------- */
+
+
+typedef struct
+{
+ char userid[14];
+ time_t firstlogin;
+ char termtype[16];
+ unsigned int numlogins;
+ unsigned int numposts;
+ char flags[2];
+ char passwd[14];
+ char username[40];
+ char ident[40];
+ char lasthost[40];
+ char realemail[40];
+ unsigned userlevel;
+ time_t lastlogin;
+ time_t stay;
+ char realname[40];
+ char address[80];
+ char email[80];
+ int signature;
+ unsigned int userdefine;
+ int editor;
+ unsigned int showfile;
+ int magic;
+ int addmagic;
+ uschar bmonth;
+ uschar bday;
+ uschar byear;
+ uschar sex;
+ int money;
+ int bank;
+ int lent;
+
+ int card;
+ uschar mind;
+ int unused1;
+ usint unsign_0000;
+ usint unsign_ffff;
+} userec;
+
+
+/* ----------------------------------------------------- */
+/* old DIR of board struct : 256 bytes */
+/* ----------------------------------------------------- */
+
+
+typedef struct /* the DIR files */
+{
+ char filename[80];
+ char owner[80];
+ char title[80];
+ unsigned level;
+ unsigned char accessed[12];
+} fileheader;
+
+
+/* ----------------------------------------------------- */
+/* old BOARDS struct : 256 bytes */
+/* ----------------------------------------------------- */
+
+
+typedef struct /* the BOARDS files */
+{
+ char filename[80];
+ char owner[60];
+ char BM[19];
+ char flag;
+ char title[80];
+ unsigned level;
+ unsigned char accessed[12];
+} boardheader;
diff --git a/util/tran/mag2brd.c b/util/tran/mag2brd.c
new file mode 100644
index 0000000..43004ef
--- /dev/null
+++ b/util/tran/mag2brd.c
@@ -0,0 +1,239 @@
+/*-------------------------------------------------------*/
+/* util/transbrd.c */
+/*-------------------------------------------------------*/
+/* target : Magic ¦Ü Maple 3.02 ¬ÝªOÂà´« */
+/* .BOARDS => .BRD */
+/* create : 02/09/09 */
+/* update : / / */
+/* author : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#if 0
+
+ 0. ¾A¥Î Magic/FireBird Âà Maple ºëµØ°Ï
+ 1. µ{¦¡¤£¶}¥Ø¿ý¡A¨Ï¥Î«e¥ý½T©w gem/target_board/? ¥Ø¿ý¦s¦b
+ if not¡A¥ý¶}·sªO or transbrd
+
+ ps. User on ur own risk.
+
+#endif
+
+
+#include "mag.h"
+
+
+/*-------------------------------------------------------*/
+/* basic function */
+/*-------------------------------------------------------*/
+
+
+static inline time_t
+trans_hdr_chrono(filename)
+ char *filename;
+{
+ char time_str[11];
+
+ /* M.1087654321.A ©Î M.987654321.A */
+ str_ncpy(time_str, filename + 2, filename[2] == '1' ? 11 : 10);
+
+ return (time_t) atoi(time_str);
+}
+
+
+static inline void
+trans_hdr_stamp(folder, t, hdr, fpath)
+ char *folder;
+ time_t t;
+ HDR *hdr;
+ char *fpath;
+{
+ FILE *fp;
+ char *fname, *family;
+ int rc;
+
+ fname = fpath;
+ while (rc = *folder++)
+ {
+ *fname++ = rc;
+ if (rc == '/')
+ family = fname;
+ }
+ fname = family + 1;
+ *fname++ = '/';
+ *fname++ = 'A';
+
+ for (;;)
+ {
+ *family = radix32[t & 31];
+ archiv32(t, fname);
+
+ if (fp = fopen(fpath, "r"))
+ {
+ fclose(fp);
+ t++;
+ }
+ else
+ {
+ memset(hdr, 0, sizeof(HDR));
+ hdr->chrono = t;
+ str_stamp(hdr->date, &hdr->chrono);
+ strcpy(hdr->xname, --fname);
+ break;
+ }
+ }
+}
+
+
+static void
+trans_owner(hdr, old)
+ HDR *hdr;
+ char *old;
+{
+ char *left, *right;
+ char owner[128];
+
+ str_ncpy(owner, old, sizeof(owner));
+
+ if (strchr(owner, '.')) /* innbbsd ==> bbs */
+ {
+ /* ®æ¦¡: itoc.bbs@bbs.tnfsh.tn.edu.tw (§Úªº¼ÊºÙ) */
+
+ hdr->xmode = POST_INCOME;
+
+ left = strchr(owner, '(');
+ right = strrchr(owner, ')');
+
+ if (!left || !right)
+ {
+ str_ncpy(hdr->owner, "§H¦W", sizeof(hdr->owner));
+ }
+ else
+ {
+ *(left - 1) = '\0';
+ str_ncpy(hdr->owner, owner, sizeof(hdr->owner));
+ *right = '\0';
+ str_ncpy(hdr->nick, left + 1, sizeof(hdr->nick));
+ }
+ }
+ else if (left = strchr(owner, '(')) /* local post */
+ {
+ /* ®æ¦¡: itoc (§Úªº¼ÊºÙ) */
+
+ *(left - 1) = '\0';
+ str_ncpy(hdr->owner, owner, sizeof(hdr->owner));
+ if (right = strchr(owner, ')'))
+ {
+ *right = '\0';
+ str_ncpy(hdr->nick, left + 1, sizeof(hdr->nick));
+ }
+ }
+ else
+ {
+ /* ®æ¦¡: itoc */
+
+ str_ncpy(hdr->owner, owner, sizeof(hdr->owner));
+ }
+}
+
+
+/*-------------------------------------------------------*/
+/* Âà´«µ{¦¡ */
+/*-------------------------------------------------------*/
+
+
+static void
+trans_brd(bh)
+ boardheader *bh;
+{
+ static time_t stamp = 0;
+
+ int fd;
+ char *brdname, index[64], folder[64], buf[64], fpath[64];
+ fileheader fh;
+ HDR hdr;
+ BRD brd;
+ time_t chrono;
+
+ brdname = bh->filename;
+ if (strlen(brdname) > BNLEN)
+ {
+ printf("%s name is too long!\n", brdname);
+ return;
+ }
+
+ if (!stamp)
+ time(&stamp);
+
+ /* Âà´« .BRD */
+ memset(&brd, 0, sizeof(BRD));
+ str_ncpy(brd.brdname, brdname, sizeof(brd.brdname));
+ str_ncpy(brd.class, bh->title + 2, sizeof(brd.class));
+ str_ncpy(brd.title, bh->title + 13, sizeof(brd.title));
+ /* str_ncpy(brd.BM, bh->BM, sizeof(brd.BM)); */ /* ¨S¦³ bh->BM? ¯¸ªø¤â°Ê§ï */
+ brd.bstamp = stamp++;
+ brd.readlevel = 0; /* ¥ý¹w³] read/post level¡A¯¸ªø¦A¦Û¤v¤â°Ê§ï */
+ brd.postlevel = PERM_POST;
+ brd.battr = BRD_NOTRAN;
+ rec_add(FN_BRD, &brd, sizeof(BRD));
+
+ /* «Ø¥Ø¿ý */
+ sprintf(buf, "gem/brd/%s", brdname);
+ mak_dirs(buf);
+ mak_dirs(buf + 4);
+
+ /* Âà´«¶iªOµe­± */
+ sprintf(index, "%s/%s/notes", OLD_BOARDPATH, brdname);
+ brd_fpath(folder, brdname, FN_NOTE);
+ f_cp(index, folder, O_TRUNC);
+
+ /* Âà´«§ë²¼°O¿ý */
+ sprintf(index, "%s/%s/results", OLD_BOARDPATH, brdname);
+ sprintf(folder, "brd/%s/@/@vote", brdname);
+ f_cp(index, folder, O_TRUNC);
+
+ /* Âà´« .DIR */
+ sprintf(index, "%s/%s/.DIR", OLD_BOARDPATH, brdname); /* 廼 .DIR */
+ brd_fpath(folder, brdname, FN_DIR); /* ·sªº .DIR */
+
+ if ((fd = open(index, O_RDONLY)) >= 0)
+ {
+ while (read(fd, &fh, sizeof(fh)) == sizeof(fh))
+ {
+ sprintf(buf, OLD_BOARDPATH "/%s/%s", brdname, fh.filename);
+
+ chrono = trans_hdr_chrono(fh.filename);
+ trans_hdr_stamp(folder, chrono, &hdr, fpath);
+ trans_owner(&hdr, fh.owner);
+ str_ansi(hdr.title, fh.title, sizeof(hdr.title));
+ if (fh.accessed[0] & 0x8) /* FILE_MARDKED */
+ hdr.xmode |= POST_MARKED;
+ rec_add(folder, &hdr, sizeof(HDR));
+
+ /* «þ¨©ÀÉ®× */
+ f_cp(buf, fpath, O_TRUNC);
+ }
+ close(fd);
+ }
+
+ printf("%s transfer ok!\n", brdname);
+}
+
+
+int
+main()
+{
+ int fd;
+ boardheader bh;
+
+ chdir(BBSHOME);
+
+ if ((fd = open(FN_BOARDS, O_RDONLY)) >= 0)
+ {
+ while (read(fd, &bh, sizeof(bh)) == sizeof(bh))
+ trans_brd(&bh);
+ close(fd);
+ }
+
+ return 0;
+}
diff --git a/util/tran/mag2gem.c b/util/tran/mag2gem.c
new file mode 100644
index 0000000..04b692d
--- /dev/null
+++ b/util/tran/mag2gem.c
@@ -0,0 +1,180 @@
+/*-------------------------------------------------------*/
+/* util/transman.c */
+/*-------------------------------------------------------*/
+/* target : Magic ¦Ü Maple 3.02 ºëµØ°ÏÂà´« */
+/* create : 01/10/03 */
+/* update : / / */
+/* author : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#if 0
+
+ 0. ¾A¥Î Magic/Napoleon Âà Maple ºëµØ°Ï
+ 1. µ{¦¡¤£¶}¥Ø¿ý¡A¨Ï¥Î«e¥ý½T©w gem/target_board/? ¥Ø¿ý¦s¦b
+ if not¡A¥ý¶}·sªO or transbrd
+
+ ps. User on ur own risk.
+
+#endif
+
+
+#include "mag.h"
+
+
+/* ----------------------------------------------------- */
+/* basic functions */
+/* ----------------------------------------------------- */
+
+
+static void
+merge_msg(msg) /* fget() ¦r¦ê³Ì«á¦³ '\n' ­n³B²z±¼ */
+ char *msg;
+{
+ int end;
+
+ end = 0;
+ while (end < 80)
+ {
+ if (msg[end] == '\n')
+ {
+ msg[end] = '\0';
+ return;
+ }
+
+ end++;
+ }
+ msg[end] = '\0'; /* ±j¨îÂ_¦b 80 ¦r */
+}
+
+
+static int /* 'A':¤å³¹ 'F':¥Ø¿ý */
+get_record(src, src_folder, title, path, num) /* Ū¥X .Name */
+ char *src;
+ char *src_folder;
+ char *title, *path;
+ int num;
+{
+ FILE *fp;
+ char Name[128], Path[128], buf[128];
+ int i, j;
+
+ if (fp = fopen(src_folder, "r"))
+ {
+ /* «e¤T¦æ¨S¥Î */
+ j = num * 4 + 3;
+ for (i = 0; i < j; i++)
+ {
+ fgets(buf, 80, fp);
+ }
+
+ /* ¨C­Ó¯Á¤Þ¦³¥|¦æ */
+ fgets(Name, 80, fp);
+ fgets(Path, 80, fp);
+ fgets(buf, 80, fp);
+ fgets(buf, 80, fp);
+
+ fclose(fp);
+
+ if (Name[0] != '#' && Path[0] != '#')
+ {
+ merge_msg(Name);
+ merge_msg(Path);
+
+ strcpy(title, Name + 5);
+ strcpy(path, Path + 7);
+
+ sprintf(buf, "%s/%s", src, path);
+
+ if (dashf(buf))
+ return 'A';
+ else if (dashd(buf))
+ return 'F';
+ }
+ }
+
+ return 0;
+}
+
+
+/* ----------------------------------------------------- */
+/* Âà´«µ{¦¡ */
+/* ----------------------------------------------------- */
+
+
+static void
+transman(brdname, src, dst_folder)
+ char *brdname; /* ¬ÝªOªO¦W */
+ char *src; /* ¯¸ªº¥Ø¿ý */
+ char *dst_folder; /* ·s¯¸ªº .DIR ©Î FXXXXXXX */
+{
+ int num;
+ int type;
+ char src_folder[80]; /* ¯¸ªº .Names */
+ char sub_src[80];
+ char sub_dst_folder[80];
+ char title[80], path[80];
+ char cmd[256];
+ HDR hdr;
+
+ num = 0;
+ sprintf(src_folder, "%s/.Names", src);
+
+ while ((type = get_record(src, src_folder, title, path, num)))
+ {
+ if (type == 'A') /* ¤å¥ó */
+ {
+ close(hdr_stamp(dst_folder, 'A', &hdr, cmd));
+ str_ncpy(hdr.title, title, sizeof(hdr.title));
+ rec_add(dst_folder, &hdr, sizeof(HDR));
+
+ sprintf(cmd, "cp %s/%s gem/brd/%s/%c/%s", src, path, brdname, hdr.xname[7], hdr.xname);
+ system(cmd);
+ }
+ else if (type == 'F') /* ¥Ø¿ý */
+ {
+ close(hdr_stamp(dst_folder, 'F', &hdr, cmd));
+ hdr.xmode = GEM_FOLDER;
+ str_ncpy(hdr.title, title, sizeof(hdr.title));
+ rec_add(dst_folder, &hdr, sizeof(HDR));
+
+ sprintf(sub_src, "%s/%s", src, path);
+ sprintf(sub_dst_folder, "gem/brd/%s/%c/%s", brdname, hdr.xname[7], hdr.xname);
+ transman(brdname, sub_src, sub_dst_folder);
+ }
+ num++;
+ }
+}
+
+
+int
+main()
+{
+ int fd;
+ char src[64], dst_folder[64];
+ BRD brd;
+
+ chdir(BBSHOME);
+
+ if ((fd = open(FN_BRD, O_RDONLY)) >= 0)
+ {
+ while (read(fd, &brd, sizeof(BRD)) == sizeof(BRD))
+ {
+ sprintf(src, OLD_MANPATH "/%s", brd.brdname);
+
+ if (dashd(src))
+ {
+ sprintf(dst_folder, "gem/brd/%s", brd.brdname);
+
+ if (dashd(dst_folder))
+ {
+ sprintf(dst_folder, "gem/brd/%s/.DIR", brd.brdname);
+ transman(brd.brdname, src, dst_folder); /* ·s¯¸³£­n¦³³o­Ó¬ÝªO¤~Âà */
+ }
+ }
+ }
+ close(fd);
+ }
+
+ exit(0);
+}
diff --git a/util/tran/mag2usr.c b/util/tran/mag2usr.c
new file mode 100644
index 0000000..10b729c
--- /dev/null
+++ b/util/tran/mag2usr.c
@@ -0,0 +1,259 @@
+/*-------------------------------------------------------*/
+/* util/transusr.c */
+/*-------------------------------------------------------*/
+/* target : Magic ¦Ü Maple 3.02 ¨Ï¥ÎªÌÂà´« */
+/* create : 02/09/09 */
+/* update : / / */
+/* author : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#if 0
+
+ 0. ¾A¥Î Magic Âà .ACCT ¤Î«H½c
+
+ ps. User on ur own risk.
+
+#endif
+
+
+#include "mag.h"
+
+
+/*-------------------------------------------------------*/
+/* Âà´«.ACCT */
+/*-------------------------------------------------------*/
+
+
+static void
+trans_acct()
+{
+ int fd, count;
+ char *userid, buf[64];
+ userec old;
+ ACCT new;
+ SCHEMA slot;
+
+ count = 1; /* userno ±q 1 °_ºâ */
+
+ if ((fd = open(FN_PASSWDS, O_RDONLY)) >= 0)
+ {
+ while (read(fd, &old, sizeof(old)) == sizeof(old))
+ {
+ if (!isalpha(old.userid[0]))
+ continue;
+
+ new.userno = count;
+ count++;
+
+ str_ncpy(new.userid, old.userid, sizeof(new.userid));
+ str_ncpy(new.passwd, old.passwd, sizeof(new.passwd));
+ str_ncpy(new.realname, old.realname, sizeof(new.realname));
+ str_ncpy(new.username, old.username, sizeof(new.username));
+
+ new.userlevel = PERM_BASIC | PERM_CHAT | PERM_PAGE | PERM_POST | PERM_VALID;
+ new.ufo = UFO_DEFAULT_NEW;
+
+ new.signature = 0;
+ new.year = old.byear;
+ new.month = old.bmonth;
+ new.day = old.bday;
+ new.sex = 0;
+
+ new.money = old.money;
+ new.gold = 0;
+
+ new.numlogins = old.numlogins;
+ new.numposts = old.numposts;
+ new.numemails = 0;
+
+ new.firstlogin = old.firstlogin;
+ new.lastlogin = old.lastlogin;
+ new.tcheck = time(&new.tvalid);
+
+ str_ncpy(new.lasthost, old.lasthost, sizeof(new.lasthost));
+ str_ncpy(new.email, old.email, sizeof(new.email));
+
+ userid = new.userid;
+ usr_fpath(buf, userid, NULL);
+ mkdir(buf, 0700);
+ strcat(buf, "/@");
+ mkdir(buf, 0700);
+ usr_fpath(buf, userid, "gem"); /* itoc.010727: ­Ó¤HºëµØ°Ï */
+ mak_links(buf); /* itoc.010924: ´î¤Ö­Ó¤HºëµØ°Ï¥Ø¿ý */
+#ifdef MY_FAVORITE
+ usr_fpath(buf, userid, "MF");
+ mkdir(buf, 0700);
+#endif
+
+ usr_fpath(buf, userid, FN_ACCT);
+ rec_add(buf, &new, sizeof(ACCT));
+
+ memset(&slot, 0, sizeof(SCHEMA));
+ slot.uptime = count;
+ memcpy(slot.userid, new.userid, IDLEN);
+ rec_add(FN_SCHEMA, &slot, sizeof(SCHEMA));
+ }
+ close(fd);
+ }
+}
+
+
+/*-------------------------------------------------------*/
+/* Âà´««H½c */
+/*-------------------------------------------------------*/
+
+
+static time_t
+trans_hdr_chrono(filename)
+ char *filename;
+{
+ char time_str[11];
+
+ /* M.1087654321.A ©Î M.987654321.A */
+ str_ncpy(time_str, filename + 2, filename[2] == '1' ? 11 : 10);
+
+ return (time_t) atoi(time_str);
+}
+
+
+static void
+trans_owner(hdr, old)
+ HDR *hdr;
+ char *old;
+{
+ char *left, *right;
+ char owner[128];
+
+ str_ncpy(owner, old, sizeof(owner));
+
+ if (strchr(owner, '.')) /* innbbsd ==> bbs */
+ {
+ /* ®æ¦¡: itoc.bbs@bbs.tnfsh.tn.edu.tw (§Úªº¼ÊºÙ) */
+
+ left = strchr(owner, '(');
+ right = strrchr(owner, ')');
+
+ if (!left || !right)
+ {
+ str_ncpy(hdr->owner, "§H¦W", sizeof(hdr->owner));
+ }
+ else
+ {
+ *(left - 1) = '\0';
+ str_ncpy(hdr->owner, owner, sizeof(hdr->owner));
+ *right = '\0';
+ str_ncpy(hdr->nick, left + 1, sizeof(hdr->nick));
+ }
+ }
+ else if (left = strchr(owner, '(')) /* local post */
+ {
+ /* ®æ¦¡: itoc (§Úªº¼ÊºÙ) */
+
+ *(left - 1) = '\0';
+ str_ncpy(hdr->owner, owner, sizeof(hdr->owner));
+ if (right = strchr(owner, ')'))
+ {
+ *right = '\0';
+ str_ncpy(hdr->nick, left + 1, sizeof(hdr->nick));
+ }
+ }
+ else
+ {
+ /* ®æ¦¡: itoc */
+
+ str_ncpy(hdr->owner, owner, sizeof(hdr->owner));
+ }
+}
+
+
+static void
+trans_mail(userid)
+ char *userid;
+{
+ int fd;
+ char ch, index[64], folder[64], buf[64], fpath[64];
+ fileheader fh;
+ HDR hdr;
+ time_t chrono;
+
+ ch = userid[0];
+ if (ch >= 'a' && ch <= 'z') /* ´«¤j¼g */
+ ch -= 32;
+
+ sprintf(index, "%s/%c/%s/.DIR", OLD_MAILPATH, ch, userid); /* 廼 header */
+ usr_fpath(folder, userid, FN_DIR); /* ·sªº header */
+
+ if ((fd = open(index, O_RDONLY)) >= 0)
+ {
+ while (read(fd, &fh, sizeof(fh)) == sizeof(fh))
+ {
+ sprintf(buf, "%s/%c/%s/%s", OLD_MAILPATH, ch, userid, fh.filename);
+
+ if (dashf(buf)) /* ¤å³¹Àɮצb¤~°µÂà´« */
+ {
+ char new_name[10] = "@";
+
+ /* Âà´«¤å³¹ .DIR */
+ memset(&hdr, 0, sizeof(HDR));
+ chrono = trans_hdr_chrono(fh.filename);
+ new_name[1] = radix32[chrono & 31];
+ archiv32(chrono, new_name + 1);
+
+ hdr.chrono = chrono;
+ str_ncpy(hdr.xname, new_name, sizeof(hdr.xname));
+ trans_owner(&hdr, fh.owner);
+ str_ncpy(hdr.title, fh.title, sizeof(hdr.title));
+ str_stamp(hdr.date, &hdr.chrono);
+ hdr.xmode = MAIL_READ;
+ if (fh.accessed[0] & 0x8) /* FILE_MARDKED */
+ hdr.xmode |= POST_MARKED;
+
+ rec_add(folder, &hdr, sizeof(HDR));
+
+ /* «þ¨©ÀÉ®× */
+ usr_fpath(fpath, userid, "@/");
+ strcat(fpath, new_name);
+ f_cp(buf, fpath, O_TRUNC);
+ }
+ }
+ close(fd);
+ }
+}
+
+
+int
+main()
+{
+ char c, *userid;
+ char buf[64];
+ struct dirent *de;
+ DIR *dirp;
+
+ chdir(BBSHOME);
+
+ /* ¥ýÂà .ACCT¡A«Ø¥Ø¿ý */
+ trans_acct();
+
+ /* ¥Ñ¦³®Äªº¨Ï¥ÎªÌ ID ¨ÓÂà´««H½c */
+ for (c = 'a'; c <= 'z'; c++)
+ {
+ sprintf(buf, "usr/%c", c);
+
+ if (!(dirp = opendir(buf)))
+ continue;
+
+ while (de = readdir(dirp))
+ {
+ userid = de->d_name;
+ if (*userid <= ' ' || *userid == '.')
+ continue;
+
+ trans_mail(userid);
+ }
+
+ closedir(dirp);
+ }
+
+ return 0;
+}
diff --git a/util/tran/snap.h b/util/tran/snap.h
new file mode 100644
index 0000000..3be2bcc
--- /dev/null
+++ b/util/tran/snap.h
@@ -0,0 +1,110 @@
+/*-------------------------------------------------------*/
+/* util/snap.h */
+/*-------------------------------------------------------*/
+/* target : Maple Âà´« */
+/* create : 98/12/15 */
+/* update : 02/04/29 */
+/* author : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#if 0
+
+ 0. «O¯d­ì¨Ó brd/ gem/ usr/ .USR¡A¨ä¾l´«¦¨·sª©ªº
+
+ 1. §Q¥Î snap2brd Âà´« .BRD
+
+ 2. §Q¥Î snap2usr Âà´« .ACCT
+
+ 3. ±N·sª©ªº gem/@/ ¤Uªº³o¨ÇÀɮ׽ƻs¹L¨Ó
+ @apply @e-mail @goodbye @index @justify @newuser @opening.0
+ @opening.1 @opening.2 @post @re-reg @tryout @welcome
+
+ 4. ¤W BBS ¯¸¡A¦b (A)nnounce ¸Ì­±¡A«Ø¥H¤U¤G­Ó¨÷©vªº©Ò¦³¸ê®Æ
+ {¸ÜÃD} ¼öªù°Q½×
+ {±Æ¦æ} ²Î­p¸ê®Æ
+
+#endif
+
+
+#include "bbs.h"
+
+#define MAK_DIRS /* «Ø¥Ø¿ý MF/ ¤Î gem/ */
+
+
+/* ----------------------------------------------------- */
+/* (ªº) ¨Ï¥ÎªÌ±b¸¹ .ACCT struct */
+/* ----------------------------------------------------- */
+
+
+typedef struct /* ­n©Mª©µ{¦¡ struct ¤@¼Ë */
+{
+ int userno; /* unique positive code */
+ char userid[13];
+ char passwd[14];
+ uschar signature;
+ char realname[20];
+ char username[24];
+ usint userlevel;
+ int numlogins;
+ int numposts;
+ usint ufo;
+ time_t firstlogin;
+ time_t lastlogin;
+ time_t staytime; /* Á`¦@°±¯d®É¶¡ */
+ time_t tcheck; /* time to check mbox/pal */
+ char lasthost[32];
+ int numemail; /* ±Hµo Inetrnet E-mail ¦¸¼Æ */
+ time_t tvalid; /* ³q¹L»{ÃÒ¡B§ó§ï mail address ªº®É¶¡ */
+ char email[60];
+ char address[60];
+ char justify[60]; /* FROM of replied justify mail */
+ char vmail[60]; /* ³q¹L»{ÃÒ¤§ email */
+ char ident[140 - 20];
+ time_t vtime; /* validate time */
+} userec;
+
+
+/* ----------------------------------------------------- */
+/* (ªº) ¨Ï¥ÎªÌ²ßºD ufo */
+/* ----------------------------------------------------- */
+
+/* old UFO */ /* ­n©Mª©µ{¦¡ struct ¤@¼Ë */
+
+#define HABIT_COLOR BFLAG(0) /* true if the ANSI color mode open */
+#define HABIT_MOVIE BFLAG(1) /* true if show movie */
+#define HABIT_BRDNEW BFLAG(2) /* ·s¤å³¹¼Ò¦¡ */
+#define HABIT_BNOTE BFLAG(3) /* Åã¥Ü¶iªOµe­± */
+#define HABIT_VEDIT BFLAG(4) /* ²¤Æ½s¿è¾¹ */
+#define HABIT_PAGER BFLAG(5) /* Ãö³¬©I¥s¾¹ */
+#define HABIT_QUIET BFLAG(6) /* µ²Ãf¦b¤H¹Ò¡A¦ÓµL¨®°¨³Ù */
+#define HABIT_PAL BFLAG(7) /* true if show pals only */
+#define HABIT_ALOHA BFLAG(8) /* ¤W¯¸®É¥D°Ê³qª¾¦n¤Í */
+#define HABIT_MOTD BFLAG(9) /* ²¤Æ¶i¯¸µe­± */
+#define HABIT_CLOAK BFLAG(19) /* true if cloak was ON */
+#define HABIT_ACL BFLAG(20) /* true if ACL was ON */
+#define HABIT_MPAGER BFLAG(10) /* lkchu.990428: ¹q¤l¶l¥ó¶Ç©I */
+#define HABIT_NWLOG BFLAG(11) /* lkchu.990510: ¤£¦s¹ï¸Ü¬ö¿ý */
+#define HABIT_NTLOG BFLAG(12) /* lkchu.990510: ¤£¦s²á¤Ñ¬ö¿ý */
+
+
+/* ----------------------------------------------------- */
+/* (廼) BOARDS struct */
+/* ----------------------------------------------------- */
+
+typedef struct
+{
+ char brdname[13]; /* board ID */
+ char title[49];
+ char BM[37]; /* BMs' uid, token '/' */
+
+ uschar bvote; /* ¦@¦³´X¶µ§ë²¼Á|¦æ¤¤ */
+
+ time_t bstamp; /* «Ø¥ß¬ÝªOªº®É¶¡, unique */
+ usint readlevel; /* ¾\ۤ峹ªºÅv­­ */
+ usint postlevel; /* µoªí¤å³¹ªºÅv­­ */
+ usint battr; /* ¬ÝªOÄÝ©Ê */
+ time_t btime; /* .DIR ªº st_mtime */
+ int bpost; /* ¦@¦³´X½g post */
+ time_t blast; /* ³Ì«á¤@½g post ªº®É¶¡ */
+} boardheader;
diff --git a/util/tran/snap2brd.c b/util/tran/snap2brd.c
new file mode 100644
index 0000000..fd63dd2
--- /dev/null
+++ b/util/tran/snap2brd.c
@@ -0,0 +1,59 @@
+/*-------------------------------------------------------*/
+/* util/snap2brd.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : M3 BRD Âà´«µ{¦¡ */
+/* create : 03/07/09 */
+/* update : / / */
+/* author : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "snap.h"
+
+
+#define FN_BRD_TMP ".BRD.tmp"
+
+int
+main()
+{
+ boardheader bh;
+ BRD brd;
+ FILE *fp;
+
+ chdir(BBSHOME);
+
+ if (!(fp = fopen(FN_BRD, "r")))
+ return -1;
+
+ while (fread(&bh, sizeof(bh), 1, fp) == 1)
+ {
+ if (!*bh.brdname) /* ¦¹ªO¤w³Q§R°£ */
+ continue;
+
+ memset(&brd, 0, sizeof(BRD));
+
+ /* Âà´«ªº°Ê§@¦b¦¹ */
+ str_ncpy(brd.brdname, bh.brdname, sizeof(brd.brdname));
+ str_ncpy(brd.class, "¡H¡H", sizeof(brd.class)); /* ¤ÀÃþ­n­«³] */
+ str_ncpy(brd.title, bh.title + 3, sizeof(brd.title)); /* ¸õ¹L "¡¼ " */
+ str_ncpy(brd.BM, bh.BM, sizeof(brd.BM));
+ brd.bvote = bh.bvote ? 1 : 0;
+ brd.bstamp = bh.bstamp;
+ brd.readlevel = bh.readlevel;
+ brd.postlevel = bh.postlevel;
+ brd.battr = bh.battr;
+ brd.btime = 0;
+ brd.bpost = 0;
+ brd.blast = 0;
+
+ rec_add(FN_BRD_TMP, &brd, sizeof(BRD));
+ }
+
+ fclose(fp);
+
+ /* §R°£Âªº¡A§â·sªº§ó¦W */
+ unlink(FN_BRD);
+ rename(FN_BRD_TMP, FN_BRD);
+
+ return 0;
+}
diff --git a/util/tran/snap2usr.c b/util/tran/snap2usr.c
new file mode 100644
index 0000000..14d76fb
--- /dev/null
+++ b/util/tran/snap2usr.c
@@ -0,0 +1,174 @@
+/*-------------------------------------------------------*/
+/* util/snap2usr.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : M3 ACCT Âà´«µ{¦¡ */
+/* create : 98/12/15 */
+/* update : 02/04/29 */
+/* author : mat.bbs@fall.twbbs.org */
+/* modify : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+/* syntax : snap2usr [userid] */
+/*-------------------------------------------------------*/
+
+
+#include "snap.h"
+
+
+static usint
+transfer_ufo(oldufo)
+ usint oldufo;
+{
+ usint ufo;
+
+ ufo = 0;
+
+ if (oldufo & HABIT_MOVIE)
+ ufo |= UFO_MOVIE;
+
+ if (oldufo & HABIT_BNOTE)
+ ufo |= UFO_BRDNOTE;
+
+ if (oldufo & HABIT_VEDIT)
+ ufo |= UFO_VEDIT;
+
+ if (oldufo & HABIT_MOTD)
+ ufo |= UFO_MOTD;
+
+ if (oldufo & HABIT_PAGER)
+ ufo |= UFO_PAGER;
+
+ if (oldufo & HABIT_QUIET)
+ ufo |= UFO_QUIET;
+
+ if (oldufo & HABIT_PAL)
+ ufo |= UFO_PAL;
+
+ if (oldufo & HABIT_ALOHA)
+ ufo |= UFO_ALOHA;
+
+ if (oldufo & HABIT_NWLOG)
+ ufo |= UFO_NWLOG;
+
+ if (oldufo & HABIT_NTLOG)
+ ufo |= UFO_NTLOG;
+
+ if (oldufo & HABIT_CLOAK)
+ ufo |= UFO_CLOAK;
+
+ if (oldufo & HABIT_ACL)
+ ufo |= UFO_ACL;
+
+ return ufo;
+}
+
+
+/* ----------------------------------------------------- */
+/* Âà´«¥Dµ{¦¡ */
+/* ----------------------------------------------------- */
+
+
+static void
+trans_acct(old, new)
+ userec *old;
+ ACCT *new;
+{
+ memset(new, 0, sizeof(ACCT));
+
+ new->userno = old->userno;
+
+ str_ncpy(new->userid, old->userid, sizeof(new->userid));
+ str_ncpy(new->passwd, old->passwd, sizeof(new->passwd));
+ str_ncpy(new->realname, old->realname, sizeof(new->realname));
+ str_ncpy(new->username, old->username, sizeof(new->username));
+
+ new->userlevel = old->userlevel;
+ new->ufo = transfer_ufo(old->ufo); /* itoc.010917: ufo Äæ¦ì¥¼¥²¤@¼Ë */
+ new->signature = old->signature;
+
+ new->year = 0; /* µ¹ªì©l­È */
+ new->month = 0;
+ new->day = 0;
+ new->sex = 0;
+ new->money = 100;
+ new->gold = 1;
+
+ new->numlogins = old->numlogins;
+ new->numposts = old->numposts;
+ new->numemails = old->numemail;
+
+ new->firstlogin = old->firstlogin;
+ new->lastlogin = old->lastlogin;
+ new->tcheck = old->tcheck;
+ new->tvalid = old->tvalid;
+
+ str_ncpy(new->lasthost, old->lasthost, sizeof(new->lasthost));
+ str_ncpy(new->email, old->email, sizeof(new->email));
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ ACCT new;
+ char c;
+
+ if (argc > 2)
+ {
+ printf("Usage: %s [userid]\n", argv[0]);
+ return -1;
+ }
+
+ for (c = 'a'; c <= 'z'; c++)
+ {
+ char buf[64];
+ struct dirent *de;
+ DIR *dirp;
+
+ sprintf(buf, BBSHOME "/usr/%c", c);
+ chdir(buf);
+
+ if (!(dirp = opendir(".")))
+ continue;
+
+ while (de = readdir(dirp))
+ {
+ userec old;
+ int fd;
+ char *str;
+
+ str = de->d_name;
+ if (*str <= ' ' || *str == '.')
+ continue;
+
+ if ((argc == 2) && str_cmp(str, argv[1]))
+ continue;
+
+#ifdef MAK_DIRS
+ sprintf(buf, "%s/MF", str);
+ mkdir(buf, 0700);
+ sprintf(buf, "%s/gem", str);
+ mak_links(buf);
+#endif
+
+ sprintf(buf, "%s/" FN_ACCT, str);
+ if ((fd = open(buf, O_RDONLY)) < 0)
+ continue;
+
+ read(fd, &old, sizeof(userec));
+ close(fd);
+ unlink(buf); /* itoc.010831: ¬å±¼­ì¨Óªº FN_ACCT */
+
+ trans_acct(&old, &new);
+
+ fd = open(buf, O_WRONLY | O_CREAT, 0600); /* itoc.010831: ­««Ø·sªº FN_ACCT */
+ write(fd, &new, sizeof(ACCT));
+ close(fd);
+ }
+
+ closedir(dirp);
+ }
+
+ return 0;
+}
diff --git a/util/tran/sob.h b/util/tran/sob.h
new file mode 100644
index 0000000..73efade
--- /dev/null
+++ b/util/tran/sob.h
@@ -0,0 +1,96 @@
+/*-------------------------------------------------------*/
+/* util/sob.h */
+/*-------------------------------------------------------*/
+/* target : SOB ¦Ü Maple 3.02 Âà´« */
+/* create : 02/10/26 */
+/* author : ernie@micro8.ee.nthu.edu.tw */
+/* modify : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#if 0
+
+ 1. ³]©w OLD_BBSHOME¡BFN_PASSWD¡BFN_BOARD
+ 2. ­×§ï©Ò¦³ªº old struct
+
+ 3. ¥²¶·¦b brd Âà§¹¤~¥i¥HÂà´« gem
+ 4. ¥²¶·¦b usr ¤Î brd ³£Âà§¹¤~¥i¥HÂà´« pal
+ 5. «ØÄ³Âà´«¶¶§Ç¬° usr -> brd -> gem ->pal
+
+#endif
+
+
+#include "bbs.h"
+
+
+#define OLD_BBSHOME "/home/oldbbs" /* SOB */
+#define FN_PASSWD "/home/oldbbs/.PASSWDS" /* SOB */
+#define FN_BOARD "/home/oldbbs/.BOARDS" /* SOB */
+
+
+#undef HAVE_PERSONAL_GEM /* SOB ¬O¨S¦³­Ó¤HºëµØ°Ïªº */
+
+
+/* ----------------------------------------------------- */
+/* old .PASSWDS struct : 256 bytes */
+/* ----------------------------------------------------- */
+
+struct userec
+{
+ char userid[13];
+ char realname[20];
+ char username[24];
+ char passwd[14];
+ uschar uflag;
+ usint userlevel;
+ unsigned short numlogins;
+ unsigned short numposts;
+ time_t firstlogin;
+ time_t lastlogin;
+ char lasthost[16];
+ char remoteuser[8];
+ char email[50];
+ char address[50];
+ char justify[39];
+ uschar month;
+ uschar day;
+ uschar year;
+ uschar sex;
+ uschar state;
+};
+typedef struct userec userec;
+
+
+/* ----------------------------------------------------- */
+/* old DIR of board struct : 128 bytes */
+/* ----------------------------------------------------- */
+
+struct fileheader
+{
+ char filename[33]; /* M.9876543210.A */
+ char savemode; /* file save mode */
+ char owner[14]; /* uid[.] */
+ char date[6]; /* [02/02] or space(5) */
+ char title[73];
+ uschar filemode; /* must be last field @ boards.c */
+};
+typedef struct fileheader fileheader;
+
+
+/* ----------------------------------------------------- */
+/* old BOARDS struct : 128 bytes */
+/* ----------------------------------------------------- */
+
+struct boardheader
+{
+ char brdname[13]; /* bid */
+ char title[49];
+ char BM[39]; /* BMs' uid, token '/' */
+ char pad[11];
+ time_t bupdate; /* note update time */
+ char pad2[3];
+ uschar bvote; /* Vote flags */
+ time_t vtime; /* Vote close time */
+ usint level;
+};
+typedef struct boardheader boardheader;
diff --git a/util/tran/sob2brd.c b/util/tran/sob2brd.c
new file mode 100644
index 0000000..4f70b45
--- /dev/null
+++ b/util/tran/sob2brd.c
@@ -0,0 +1,228 @@
+/*-------------------------------------------------------*/
+/* util/transbrd.c */
+/*-------------------------------------------------------*/
+/* target : Maple Sob 2.36 ¦Ü Maple 3.02 ¬ÝªOÂà´« */
+/* .BOARDS => .BRD */
+/* create : / / */
+/* update : 98/06/14 */
+/* author : ernie@micro8.ee.nthu.edu.tw */
+/* modify : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+/* syntax : transbrd [target_board] */
+/*-------------------------------------------------------*/
+
+#if 0
+
+ 1. ­×§ï struct boardheader ¤Î transbrd()
+ (boardheader ¨âª©©w¸qªº¦r¦êªø«×¤£¤@¡A½Ð¦Û¦æ´«¦¨¼Æ¦r)
+ 2. §ë²¼¤£Âà´«
+ 3. ¶iªOµe­± copy
+ 4. ¦p¦³»Ý­n½Ð chmod 644 `find PATH -perm 600`
+ 5. ¶} gem ¥Ø¿ý gem/target_board/? ¦ý¤£Âà´« gem
+ 6. ¤£·|§ó·s bshm¡A¨Ï¥Î«á½Ð¦Û¦æ§ó·s
+ 7. Âà´««á½Ð¤â°Ê³]¬ÝªOÅv­­
+
+ ps. Use on ur own risk.
+
+#endif
+
+
+#include "sob.h"
+
+
+static time_t
+trans_hdr_chrono(filename)
+ char *filename;
+{
+ char time_str[11];
+
+ /* M.1087654321.A ©Î M.987654321.A */
+ str_ncpy(time_str, filename + 2, filename[2] == '1' ? 11 : 10);
+
+ return (time_t) atoi(time_str);
+}
+
+
+static void
+trans_hdr_stamp(folder, t, hdr, fpath)
+ char *folder;
+ time_t t;
+ HDR *hdr;
+ char *fpath;
+{
+ FILE *fp;
+ char *fname, *family;
+ int rc;
+
+ fname = fpath;
+ while (rc = *folder++)
+ {
+ *fname++ = rc;
+ if (rc == '/')
+ family = fname;
+ }
+ fname = family + 1;
+ *fname++ = '/';
+ *fname++ = 'A';
+
+ for (;;)
+ {
+ *family = radix32[t & 31];
+ archiv32(t, fname);
+
+ if (fp = fopen(fpath, "r"))
+ {
+ fclose(fp);
+ t++;
+ }
+ else
+ {
+ memset(hdr, 0, sizeof(HDR));
+ hdr->chrono = t;
+ str_stamp(hdr->date, &hdr->chrono);
+ strcpy(hdr->xname, --fname);
+ break;
+ }
+ }
+}
+
+
+/* ----------------------------------------------------- */
+/* Âà´«¥Dµ{¦¡ */
+/* ----------------------------------------------------- */
+
+
+static void
+transbrd(bh)
+ boardheader *bh;
+{
+ static time_t stamp = 0;
+
+ int fd;
+ char index[64], folder[64], buf[64], fpath[64];
+ fileheader fh;
+ HDR hdr;
+ BRD newboard;
+ time_t chrono;
+
+ printf("Âà´« %s ¬ÝªO\n", bh->brdname);
+
+ brd_fpath(buf, bh->brdname, NULL);
+ if (dashd(buf))
+ {
+ printf("%s ¤w¸g¦³¦¹¬ÝªO\n", bh->brdname);
+ return;
+ }
+
+ if (!stamp)
+ time(&stamp);
+
+ /* Âà´« .BRD */
+
+ memset(&newboard, 0, sizeof(newboard));
+ str_ncpy(newboard.brdname, bh->brdname, sizeof(newboard.brdname));
+ str_ncpy(newboard.class, bh->title, sizeof(newboard.class));
+ str_ncpy(newboard.title, bh->title + 4, sizeof(newboard.title));
+ str_ncpy(newboard.BM, bh->BM, sizeof(newboard.BM));
+ newboard.bstamp = stamp++;
+ newboard.battr = BRD_NOTRAN; /* ¹w³]¤£Âà«H */
+ newboard.readlevel = 0;
+ newboard.postlevel = PERM_POST;
+
+ rec_add(FN_BRD, &newboard, sizeof(newboard)); /* §O§Ñ¤F¥Î brd2gem.c ¨ÓÂà´« Class */
+
+ /* ¶}·s¥Ø¿ý */
+
+ sprintf(fpath, "gem/brd/%s", newboard.brdname);
+ mak_dirs(fpath);
+ mak_dirs(fpath + 4);
+
+ /* Âà´«¶iªOµe­± */
+
+ sprintf(buf, OLD_BBSHOME "/boards/%s/notes", bh->brdname);
+
+ if (dashf(buf))
+ {
+ brd_fpath(fpath, newboard.brdname, FN_NOTE);
+ f_cp(buf, fpath, O_TRUNC);
+ }
+
+ /* Âà´«¤å³¹ */
+
+ sprintf(index, OLD_BBSHOME "/boards/%s/.DIR", bh->brdname); /* 廼 .DIR */
+ brd_fpath(folder, newboard.brdname, ".DIR"); /* ·sªº .DIR */
+
+ if ((fd = open(index, O_RDONLY)) >= 0)
+ {
+ while (read(fd, &fh, sizeof(fh)) == sizeof(fh))
+ {
+ sprintf(buf, OLD_BBSHOME "/boards/%s/%s", bh->brdname, fh.filename);
+ if (dashf(buf)) /* ¤å³¹Àɮצb¤~°µÂà´« */
+ {
+ /* Âà´«¤å³¹ .DIR */
+ memset(&hdr, 0, sizeof(HDR));
+ chrono = trans_hdr_chrono(fh.filename);
+ trans_hdr_stamp(folder, chrono, &hdr, fpath);
+ str_ncpy(hdr.owner, fh.owner, sizeof(hdr.owner));
+ str_ansi(hdr.title, fh.title, sizeof(hdr.title));
+ hdr.xmode = (fh.filemode & 0x2) ? POST_MARKED : 0;
+ rec_add(folder, &hdr, sizeof(HDR));
+
+ /* «þ¨©ÀÉ®× */
+ f_cp(buf, fpath, O_TRUNC);
+ }
+ }
+ close(fd);
+ }
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int fd;
+ boardheader bh;
+
+ /* argc == 1 Âà¥þ³¡ªO */
+ /* argc == 2 Âà¬Y¯S©wªO */
+
+ if (argc > 2)
+ {
+ printf("Usage: %s [target_board]\n", argv[0]);
+ exit(-1);
+ }
+
+ chdir(BBSHOME);
+
+ if (!dashf(FN_BOARD))
+ {
+ printf("ERROR! Can't open " FN_BOARD "\n");
+ exit(-1);
+ }
+ if (!dashd(OLD_BBSHOME "/boards"))
+ {
+ printf("ERROR! Can't open " OLD_BBSHOME "/boards\n");
+ exit(-1);
+ }
+
+ if ((fd = open(FN_BOARD, O_RDONLY)) >= 0)
+ {
+ while (read(fd, &bh, sizeof(bh)) == sizeof(bh))
+ {
+ if (argc == 1)
+ {
+ transbrd(&bh);
+ }
+ else if (!strcmp(bh.brdname, argv[1]))
+ {
+ transbrd(&bh);
+ exit(1);
+ }
+ }
+ close(fd);
+ }
+
+ exit(0);
+}
diff --git a/util/tran/sob2gem.c b/util/tran/sob2gem.c
new file mode 100644
index 0000000..5a78186
--- /dev/null
+++ b/util/tran/sob2gem.c
@@ -0,0 +1,233 @@
+/*-------------------------------------------------------*/
+/* util/transman.c */
+/*-------------------------------------------------------*/
+/* target : Maple Sob 2.36 ¦Ü Maple 3.02 ºëµØ°ÏÂà´« */
+/* create : 98/06/15 */
+/* update : 02/10/26 */
+/* author : ernie@micro8.ee.nthu.edu.tw */
+/* modify : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+/* syntax : transman [target_board] */
+/*-------------------------------------------------------*/
+
+
+#if 0
+
+ 1. µ{¦¡¤£¶}¥Ø¿ý¡A¨Ï¥Î«e¥ý½T©w gem/target_board/? ¥Ø¿ý¦s¦b
+ if not¡A¥ý¶}·sªO or transbrd
+ 2. ¥uÂà M.*.A ¤Î D.*.A¡A¨ä¥L link ¤£Âà´«
+ 3. ¦p¦³»Ý­n½Ð¥ý chmod 644 `find PATH -perm 600`
+
+ ps. User on ur own risk.
+
+#endif
+
+
+#include "sob.h"
+
+
+/* ----------------------------------------------------- */
+/* Âà´«ºëµØ°Ï */
+/* ----------------------------------------------------- */
+
+
+static time_t
+trans_hdr_chrono(filename)
+ char *filename;
+{
+ char time_str[11];
+
+ /* M.1087654321.A ©Î M.987654321.A */
+ str_ncpy(time_str, filename + 2, filename[2] == '1' ? 11 : 10);
+
+ return (time_t) atoi(time_str);
+}
+
+
+static void
+trans_man_stamp(folder, token, hdr, fpath, time)
+ char *folder;
+ int token;
+ HDR *hdr;
+ char *fpath;
+ time_t time;
+{
+ char *fname, *family;
+ int rc;
+
+ fname = fpath;
+ while (rc = *folder++)
+ {
+ *fname++ = rc;
+ if (rc == '/')
+ family = fname;
+ }
+ if (*family != '.')
+ {
+ fname = family;
+ family -= 2;
+ }
+ else
+ {
+ fname = family + 1;
+ *fname++ = '/';
+ }
+
+ *fname++ = token;
+
+ *family = radix32[time & 31];
+ archiv32(time, fname);
+
+ if (rc = open(fpath, O_WRONLY | O_CREAT | O_EXCL, 0600))
+ {
+ memset(hdr, 0, sizeof(HDR));
+ hdr->chrono = time;
+ str_stamp(hdr->date, &hdr->chrono);
+ strcpy(hdr->xname, --fname);
+ close(rc);
+ }
+ return;
+}
+
+
+/* ----------------------------------------------------- */
+/* Âà´«¥Dµ{¦¡ */
+/* ----------------------------------------------------- */
+
+
+static void
+transman(index, folder)
+ char *index, *folder;
+{
+ static int count = 100;
+
+ int fd;
+ char *ptr, buf[256], fpath[64];
+ fileheader fh;
+ HDR hdr;
+ time_t chrono;
+
+ if ((fd = open(index, O_RDONLY)) >= 0)
+ {
+ while (read(fd, &fh, sizeof(fh)) == sizeof(fh))
+ {
+ strcpy(buf, index);
+ ptr = strrchr(buf, '/') + 1;
+ strcpy(ptr, fh.filename);
+
+ if (*fh.filename == 'M' && dashf(buf)) /* ¥uÂà M.xxxx.A ¤Î D.xxxx.a */
+ {
+ /* Âà´«¤å³¹ .DIR */
+ memset(&hdr, 0, sizeof(HDR));
+ chrono = trans_hdr_chrono(fh.filename);
+ trans_man_stamp(folder, 'A', &hdr, fpath, chrono);
+ hdr.xmode = 0;
+ str_ncpy(hdr.owner, fh.owner, sizeof(hdr.owner));
+ str_ncpy(hdr.title, fh.title + 3, sizeof(hdr.title));
+ rec_add(folder, &hdr, sizeof(HDR));
+
+ /* «þ¨©ÀÉ®× */
+ f_cp(buf, fpath, O_TRUNC);
+ }
+ else if (*fh.filename == 'D' && dashd(buf))
+ {
+ char sub_index[256];
+
+ /* Âà´«¤å³¹ .DIR */
+ memset(&hdr, 0, sizeof(HDR));
+ chrono = ++count; /* WD ªº¥Ø¿ý©R¦W¤ñ¸û©_©Ç¡A¥u¦n¦Û¤vµ¹¼Æ¦r */
+ trans_man_stamp(folder, 'F', &hdr, fpath, chrono);
+ hdr.xmode = GEM_FOLDER;
+ str_ncpy(hdr.owner, fh.owner, sizeof(hdr.owner));
+ str_ncpy(hdr.title, fh.title + 3, sizeof(hdr.title));
+ rec_add(folder, &hdr, sizeof(HDR));
+
+ /* recursive ¶i¥hÂà´«¤l¥Ø¿ý */
+ strcpy(sub_index, buf);
+ ptr = strrchr(sub_index, '/') + 1;
+ sprintf(ptr, "%s/.DIR", fh.filename);
+ transman(sub_index, fpath);
+ }
+ }
+ close(fd);
+ }
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int fd;
+ char *brdname, index[64], folder[64];
+ boardheader bh;
+
+ /* argc == 1 Âà¥þ³¡ªO */
+ /* argc == 2 Âà¬Y¯S©wªO */
+
+ if (argc > 2)
+ {
+ printf("Usage: %s [target_board]\n", argv[0]);
+ exit(-1);
+ }
+
+ chdir(BBSHOME);
+
+ if (!dashf(FN_BOARD))
+ {
+ printf("ERROR! Can't open " FN_BOARD "\n");
+ exit(-1);
+ }
+ if (!dashd(OLD_BBSHOME "/man/boards"))
+ {
+ printf("ERROR! Can't open " OLD_BBSHOME "/man/boards\n");
+ exit(-1);
+ }
+
+ if (argc == 1)
+ {
+ if ((fd = open(FN_BOARD, O_RDONLY)) >= 0)
+ {
+ while (read(fd, &bh, sizeof(bh)) == sizeof(bh))
+ {
+ brdname = bh.brdname;
+
+ sprintf(folder, "gem/brd/%s", brdname);
+ if (!dashd(folder))
+ {
+ printf("ERROR! %s not exist. New it first.\n", folder);
+ continue;
+ }
+
+ sprintf(index, OLD_BBSHOME "/man/boards/%s/.DIR", brdname);
+ sprintf(folder, "gem/brd/%s/%s", brdname, FN_DIR);
+
+ printf("Âà´« %s ºëµØ°Ï\n", brdname);
+ transman(index, folder);
+ }
+ close(fd);
+ }
+ }
+ else
+ {
+ brdname = argv[1];
+
+ sprintf(folder, "gem/brd/%s", brdname);
+ if (!dashd(folder))
+ {
+ printf("ERROR! %s not exist. New it first.\n", folder);
+ exit(-1);
+ }
+
+ sprintf(index, OLD_BBSHOME "/man/boards/%s/.DIR", brdname);
+ sprintf(folder, "gem/brd/%s/%s", brdname, FN_DIR);
+
+ printf("Âà´« %s ºëµØ°Ï\n", brdname);
+ transman(index, folder);
+
+ exit(1);
+ }
+
+ exit(0);
+}
diff --git a/util/tran/sob2pal.c b/util/tran/sob2pal.c
new file mode 100644
index 0000000..5c6a293
--- /dev/null
+++ b/util/tran/sob2pal.c
@@ -0,0 +1,182 @@
+/*-------------------------------------------------------*/
+/* util/transpal.c */
+/*-------------------------------------------------------*/
+/* target : SOB ¦Ü Maple 3.02 (¬ÝªO)¦n¤Í¦W³æÂà´« */
+/* create : 01/09/08 */
+/* update : / / */
+/* author : itoc.bbs@bbs.ee.nctu.edu.tw */
+/*-------------------------------------------------------*/
+/* syntax : transpal */
+/*-------------------------------------------------------*/
+
+
+#if 0
+
+ 1. ³]©w OLD_BBSHOME (sob config.h)
+ 2. ­×§ï struct FRIEND ©M transfer_pal() transfer_brdpal()
+ 3. Âà´«(¬ÝªO)¦n¤Í¦W³æ¤§«e¡A±z¥²¶·¥ýÂà´«§¹¬ÝªO¤Î¨Ï¥ÎªÌ¡C
+
+ ps. ¨Ï¥Î«e½Ð¥ý¦æ³Æ¥÷¡Ause on ur own risk. µ{¦¡©å¦H½Ð¥]²[ :p
+ ps. ·PÁ lkchu ªº Maple 3.02 for FreeBSD
+
+#endif
+
+
+#include "bbs.h"
+
+
+#define OLD_BBSHOME "/home/oldbbs" /* 2.36 */
+
+
+/* ----------------------------------------------------- */
+/* 3.02 functions */
+/* ----------------------------------------------------- */
+
+
+static int
+acct_uno(userid)
+ char *userid;
+{
+ int fd;
+ int userno;
+ char fpath[64];
+
+ usr_fpath(fpath, userid, FN_ACCT);
+ fd = open(fpath, O_RDONLY);
+ if (fd >= 0)
+ {
+ read(fd, &userno, sizeof(userno));
+ close(fd);
+ return userno;
+ }
+ return -1;
+}
+
+
+/* ----------------------------------------------------- */
+
+
+static void
+transfer_pal(userid)
+ char *userid;
+{
+ ACCT acct;
+ FILE *fp;
+ int fd, friend_userno;
+ char fpath[64], buf[64], friend_userid[80];
+ PAL pal;
+
+ usr_fpath(fpath, userid, FN_PAL); /* ·sªº¦n¤Í¦W³æ */
+
+ /* sob ªº usr ¥Ø¿ý¦³¤À¤j¤p¼g¡A©Ò¥H­n¥ý¨ú±o¤j¤p¼g */
+ usr_fpath(buf, userid, FN_ACCT);
+ if ((fd = open(buf, O_RDONLY)) >= 0)
+ {
+ read(fd, &acct, sizeof(ACCT));
+ close(fd);
+ }
+ sprintf(buf, OLD_BBSHOME"/home/%s/overrides", acct.userid); /* ªº¦n¤Í¦W³æ */
+
+ if (dashf(fpath))
+ unlink(fpath); /* ²M±¼­««Ø */
+
+ if (!(fp = fopen(buf, "r")))
+ return;
+
+ while (fscanf(fp, "%s", friend_userid) == 1)
+ {
+ if ((friend_userno = acct_uno(friend_userid)) >= 0)
+ {
+ memset(&pal, 0, sizeof(PAL));
+ str_ncpy(pal.userid, friend_userid, sizeof(pal.userid));
+ pal.ftype = 0;
+ str_ncpy(pal.ship, "", sizeof(pal.ship));
+ pal.userno = friend_userno;
+ rec_add(fpath, &pal, sizeof(PAL));
+ }
+ }
+
+ fclose(fp);
+}
+
+
+static void
+transfer_brdpal(userid)
+ char *userid;
+{
+ FILE *fp;
+ int friend_userno;
+ char fpath[64], buf[64], friend_userid[80];
+ PAL pal;
+
+ brd_fpath(fpath, userid, FN_PAL); /* ·sªº¬ÝªO¦n¤Í¦W³æ */
+ sprintf(buf, OLD_BBSHOME"/boards/%s/visable", userid); /* ªº¬ÝªO¦n¤Í¦W³æ */
+
+ if (dashf(fpath))
+ unlink(fpath); /* ²M±¼­««Ø */
+
+ if (!(fp = fopen(buf, "r")))
+ return;
+
+ while (fscanf(fp, "%s", friend_userid) == 1)
+ {
+ if ((friend_userno = acct_uno(friend_userid)) >= 0)
+ {
+ memset(&pal, 0, sizeof(PAL));
+ str_ncpy(pal.userid, friend_userid, sizeof(pal.userid));
+ pal.ftype = 0;
+ str_ncpy(pal.ship, "", sizeof(pal.ship));
+ pal.userno = friend_userno;
+ rec_add(fpath, &pal, sizeof(PAL));
+ }
+ }
+
+ fclose(fp);
+}
+
+
+int
+main()
+{
+ char c, *str;
+ char buf[64];
+ struct dirent *de;
+ DIR *dirp;
+
+ chdir(BBSHOME);
+
+ /* Âà´«¨Ï¥ÎªÌ¦n¤Í¦W³æ */
+ for (c = 'a'; c <= 'z'; c++)
+ {
+ sprintf(buf, "usr/%c", c);
+
+ if (!(dirp = opendir(buf)))
+ continue;
+
+ while (de = readdir(dirp))
+ {
+ str = de->d_name;
+ if (*str <= ' ' || *str == '.')
+ continue;
+
+ transfer_pal(str);
+ }
+ closedir(dirp);
+ }
+
+ /* Âà´«¬ÝªO¦n¤Í¦W³æ */
+ if (!(dirp = opendir("brd")))
+ return 0;
+
+ while (de = readdir(dirp))
+ {
+ str = de->d_name;
+ if (*str <= ' ' || *str == '.')
+ continue;
+
+ transfer_brdpal(str);
+ }
+ closedir(dirp);
+
+ return 0;
+}
diff --git a/util/tran/sob2usr.c b/util/tran/sob2usr.c
new file mode 100644
index 0000000..cbaa8a4
--- /dev/null
+++ b/util/tran/sob2usr.c
@@ -0,0 +1,564 @@
+/*-------------------------------------------------------*/
+/* util/transusr.c */
+/*-------------------------------------------------------*/
+/* target : Maple Sob 2.36 ¦Ü Maple 3.02 ¨Ï¥ÎªÌÂà´« */
+/* .PASSWDS => .USR .ACCT */
+/* create : 98/06/14 */
+/* update : 02/10/26 */
+/* author : ernie@micro8.ee.nthu.edu.tw */
+/* modify : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+/* syntax : transusr */
+/*-------------------------------------------------------*/
+
+
+#if 0
+
+ 1. ­×§ï struct userec ¤Î creat_dirs()
+ (userec ¨âª©©w¸qªº¦r¦êªø«×¤£¤@¡A½Ð¦Û¦æ´«¦¨¼Æ¦r)
+ 2. °£ plans ÀɦW¡A¦n¤Í¦W³æ¡B¼È¦sÀɵ¥³£¤£Âà´«
+ 3. Sob ¦³¤E­Óñ¦WÀÉ¡A¥uÂà«e¤T­Ó
+ 4. «H½c¤¤ªº internet mail ¦p¦³»Ý­n½Ð¥ý chmod 644 `find PATH -perm 600`
+
+ ps. ¨Ï¥Î«e½Ð¥ý¦æ³Æ¥÷¡Ause on ur own risk. µ{¦¡©å¦H½Ð¥]²[ :p
+ ps. ·PÁ lkchu ªº Maple 3.02 for FreeBSD
+
+#endif
+
+
+#include "sob.h"
+
+
+/* ----------------------------------------------------- */
+/* Âà´« .ACCT */
+/* ----------------------------------------------------- */
+
+
+static inline int
+is_bad_userid(userid)
+ char *userid;
+{
+ register char ch;
+
+ if (strlen(userid) < 2)
+ return 1;
+
+ if (!isalpha(*userid))
+ return 1;
+
+ if (!str_cmp(userid, "new"))
+ return 1;
+
+ while (ch = *(++userid))
+ {
+ if (!isalnum(ch))
+ return 1;
+ }
+ return 0;
+}
+
+
+static inline int
+uniq_userno(fd)
+ int fd;
+{
+ char buf[4096];
+ int userno, size;
+ SCHEMA *sp; /* record length 16 ¥i¾ã°£ 4096 */
+
+ userno = 1;
+
+ while ((size = read(fd, buf, sizeof(buf))) > 0)
+ {
+ sp = (SCHEMA *) buf;
+ do
+ {
+ if (sp->userid[0] == '\0')
+ {
+ lseek(fd, -size, SEEK_CUR);
+ return userno;
+ }
+ userno++;
+ size -= sizeof(SCHEMA);
+ sp++;
+ } while (size);
+ }
+
+ return userno;
+}
+
+
+#define LEVEL_BASIC 000000000001 /* °ò¥»Åv¤O */
+#define LEVEL_CHAT 000000000002 /* ¶i¤J²á¤Ñ«Ç */
+#define LEVEL_PAGE 000000000004 /* §ä¤H²á¤Ñ */
+#define LEVEL_POST 000000000010 /* µoªí¤å³¹ */
+#define LEVEL_LOGINOK 000000000020 /* µù¥Uµ{§Ç»{ÃÒ */
+#define LEVEL_MAILLIMIT 000000000040 /* «H¥óµL¤W­­ */
+#define LEVEL_CLOAK 000000000100 /* Áô¨­³N */
+#define LEVEL_SEECLOAK 000000000200 /* ¬Ý¨£§ÔªÌ */
+#define LEVEL_XEMPT 000000000400 /* ¥Ã¤[«O¯d±b¸¹ */
+#define LEVEL_BM 000000002000 /* ªO¥D */
+#define LEVEL_ACCOUNTS 000000004000 /* ±b¸¹Á`ºÞ */
+#define LEVEL_CHATROOM 000000010000 /* ²á¤Ñ«ÇÁ`ºÞ */
+#define LEVEL_BOARD 000000020000 /* ¬ÝªOÁ`ºÞ */
+#define LEVEL_SYSOP 000000040000 /* ¯¸ªø */
+
+
+static inline usint
+trans_acct_level(perm)
+ usint perm;
+{
+ usint userlevel;
+
+ userlevel = 0;
+
+ if (perm & LEVEL_BASIC)
+ userlevel |= PERM_BASIC;
+
+ if (perm & LEVEL_CHAT)
+ userlevel |= PERM_CHAT;
+
+ if (perm & LEVEL_PAGE)
+ userlevel |= PERM_PAGE;
+
+ if (perm & LEVEL_POST)
+ userlevel |= PERM_POST;
+
+ if (perm & LEVEL_LOGINOK)
+ userlevel |= PERM_VALID;
+
+ if (perm & LEVEL_MAILLIMIT)
+ userlevel |= PERM_MBOX;
+
+ if (perm & LEVEL_CLOAK)
+ userlevel |= PERM_CLOAK;
+
+ if (perm & LEVEL_SEECLOAK)
+ userlevel |= PERM_SEECLOAK;
+
+ if (perm & LEVEL_XEMPT)
+ userlevel |= PERM_XEMPT;
+
+ if (perm & LEVEL_BM)
+ userlevel |= PERM_BM;
+
+ if (perm & LEVEL_ACCOUNTS)
+ userlevel |= PERM_ACCOUNTS;
+
+ if (perm & LEVEL_CHATROOM)
+ userlevel |= PERM_CHATROOM;
+
+ if (perm & LEVEL_BOARD)
+ userlevel |= PERM_BOARD;
+
+ if (perm & LEVEL_SYSOP)
+ userlevel |= PERM_SYSOP;
+
+ return userlevel;
+}
+
+
+static inline void
+creat_dirs(old)
+ userec *old;
+{
+ ACCT new;
+ SCHEMA slot;
+ int fd;
+ char fpath[64];
+
+ memset(&new, 0, sizeof(new));
+ memset(&slot, 0, sizeof(slot));
+
+ str_ncpy(new.userid, old->userid, sizeof(new.userid));
+ str_ncpy(new.passwd, old->passwd, sizeof(new.passwd));
+ str_ncpy(new.realname, old->realname, sizeof(new.realname));
+ str_ncpy(new.username, old->username, sizeof(new.username));
+ new.userlevel = trans_acct_level(old->userlevel);
+ new.ufo = UFO_DEFAULT_NEW;
+ new.signature = 0;
+ new.year = old->year;
+ new.month = old->month;
+ new.day = old->day;
+ new.sex = old->sex ? 1 : 0;
+ new.money = 1000; /* ¹w³]»È¹ô = 1000 ª÷¹ô = 0 */
+ new.gold = 0;
+ new.numlogins = old->numlogins;
+ new.numposts = old->numposts;
+ new.numemails = 0;
+ new.firstlogin = old->firstlogin;
+ new.lastlogin = old->lastlogin;
+ new.tcheck = time(&new.tvalid);
+ str_ncpy(new.lasthost, old->lasthost, sizeof(new.lasthost));
+ str_ncpy(new.email, old->email, sizeof(new.email));
+
+ slot.uptime = time(0);
+ strcpy(slot.userid, new.userid);
+
+ fd = open(FN_SCHEMA, O_RDWR | O_CREAT, 0600);
+ new.userno = uniq_userno(fd);
+ write(fd, &slot, sizeof(slot));
+ close(fd);
+
+ usr_fpath(fpath, new.userid, NULL);
+ mkdir(fpath, 0700);
+ strcat(fpath, "/@");
+ mkdir(fpath, 0700);
+ usr_fpath(fpath, new.userid, "MF");
+ mkdir(fpath, 0700);
+ usr_fpath(fpath, new.userid, "gem"); /* itoc.010727: ­Ó¤HºëµØ°Ï */
+ mak_links(fpath);
+
+ usr_fpath(fpath, new.userid, ".ACCT");
+ fd = open(fpath, O_WRONLY | O_CREAT, 0600);
+ write(fd, &new, sizeof(ACCT));
+ close(fd);
+}
+
+
+/* ----------------------------------------------------- */
+/* Âà´«»{ÃÒ¸ê®Æ */
+/* ----------------------------------------------------- */
+
+
+static inline void
+trans_justify(old)
+ userec *old;
+{
+ char fpath[64];
+ FILE *fp;
+
+ usr_fpath(fpath, old->userid, FN_JUSTIFY);
+ if (fp = fopen(fpath, "a"))
+ {
+ fprintf(fp, "RPY: %s\n", old->justify); /* Âà´«¹w³]¥H email »{ÃÒ */
+ fclose(fp);
+ }
+}
+
+
+/* ----------------------------------------------------- */
+/* Âഫñ¦WÀÉ¡B­pµeÀÉ */
+/* ----------------------------------------------------- */
+
+
+static inline void
+trans_sig(old)
+ userec *old;
+{
+ int i;
+ char buf[64], fpath[64], f_sig[20];
+
+ for (i = 1; i <= 3; i++) /* Maple 3.0 ¥u¦³¤T­Óñ¦W */
+ {
+ sprintf(buf, OLD_BBSHOME "/home/%s/sig.%d", old->userid, i); /* ªºÃ±¦WÀÉ */
+ if (dashf(buf))
+ {
+ sprintf(f_sig, "%s.%d", FN_SIGN, i);
+ usr_fpath(fpath, old->userid, f_sig);
+ f_cp(buf, fpath, O_TRUNC);
+ }
+ }
+
+ return;
+}
+
+
+static inline void
+trans_plans(old)
+ userec *old;
+{
+ char buf[64], fpath[64];
+
+ sprintf(buf, OLD_BBSHOME "/home/%s/plans", old->userid);
+ if (dashf(buf))
+ {
+ usr_fpath(fpath, old->userid, FN_PLANS);
+ f_cp(buf, fpath, O_TRUNC);
+ }
+ return;
+}
+
+
+/* ----------------------------------------------------- */
+/* Âà´««H¥ó */
+/* ----------------------------------------------------- */
+
+
+static time_t
+trans_hdr_chrono(filename)
+ char *filename;
+{
+ char time_str[11];
+
+ /* M.1087654321.A ©Î M.987654321.A */
+ str_ncpy(time_str, filename + 2, filename[2] == '1' ? 11 : 10);
+
+ return (time_t) atoi(time_str);
+}
+
+
+static inline void
+trans_mail(old)
+ userec *old;
+{
+ int fd;
+ char index[64], folder[64], buf[64], fpath[64];
+ fileheader fh;
+ HDR hdr;
+
+ sprintf(index, OLD_BBSHOME "/home/%s/.DIR", old->userid);
+ usr_fpath(folder, old->userid, FN_DIR);
+
+ if ((fd = open(index, O_RDONLY)) >= 0)
+ {
+ while (read(fd, &fh, sizeof(fh)) == sizeof(fh))
+ {
+ sprintf(buf, OLD_BBSHOME "/home/%s/%s", old->userid, fh.filename);
+
+ if (dashf(buf)) /* ¤å³¹Àɮצb¤~°µÂà´« */
+ {
+ time_t chrono;
+ char new_name[10] = "@";
+
+ /* Âà´«¤å³¹ .DIR */
+ memset(&hdr, 0, sizeof(HDR));
+ chrono = trans_hdr_chrono(fh.filename);
+ new_name[1] = radix32[chrono & 31];
+ archiv32(chrono, new_name + 1);
+
+ hdr.chrono = chrono;
+ str_ncpy(hdr.xname, new_name, sizeof(hdr.xname));
+ str_ncpy(hdr.owner, strstr(fh.owner, "[³Æ.") ? "[³Æ§Ñ¿ý]" : fh.owner, sizeof(hdr.owner)); /* [³Æ.§Ñ.¿ý] */
+ str_ncpy(hdr.title, fh.title, sizeof(hdr.title));
+ str_stamp(hdr.date, &hdr.chrono);
+ hdr.xmode = (fh.filemode & 0x2) ? (MAIL_MARKED | MAIL_READ) : MAIL_READ; /* ³]¬°¤wŪ */
+
+ rec_add(folder, &hdr, sizeof(HDR));
+
+ /* «þ¨©ÀÉ®× */
+ usr_fpath(fpath, old->userid, "@/");
+ strcat(fpath, new_name);
+ f_cp(buf, fpath, O_TRUNC);
+ }
+ }
+ close(fd);
+ }
+}
+
+
+/* ----------------------------------------------------- */
+/* Âà´«­Ó¤HºëµØ°Ï */
+/* ----------------------------------------------------- */
+
+
+#ifdef HAVE_PERSONAL_GEM
+static void
+trans_man_stamp(folder, token, hdr, fpath, time)
+ char *folder;
+ int token;
+ HDR *hdr;
+ char *fpath;
+ time_t time;
+{
+ char *fname, *family;
+ int rc;
+
+ fname = fpath;
+ while (rc = *folder++)
+ {
+ *fname++ = rc;
+ if (rc == '/')
+ family = fname;
+ }
+ if (*family != '.')
+ {
+ fname = family;
+ family -= 2;
+ }
+ else
+ {
+ fname = family + 1;
+ *fname++ = '/';
+ }
+
+ *fname++ = token;
+
+ *family = radix32[time & 31];
+ archiv32(time, fname);
+
+ if (rc = open(fpath, O_WRONLY | O_CREAT | O_EXCL, 0600))
+ {
+ memset(hdr, 0, sizeof(HDR));
+ hdr->chrono = time;
+ str_stamp(hdr->date, &hdr->chrono);
+ strcpy(hdr->xname, --fname);
+ close(rc);
+ }
+ return;
+}
+
+
+static void
+transman(index, folder)
+ char *index, *folder;
+{
+ static int count = 100;
+
+ int fd;
+ char *ptr, buf[256], fpath[64];
+ fileheader fh;
+ HDR hdr;
+ time_t chrono;
+
+ if ((fd = open(index, O_RDONLY)) >= 0)
+ {
+ while (read(fd, &fh, sizeof(fh)) == sizeof(fh))
+ {
+ strcpy(buf, index);
+ ptr = strrchr(buf, '/') + 1;
+ strcpy(ptr, fh.filename);
+
+ if (*fh.filename == 'M' && dashf(buf)) /* ¥uÂà M.xxxx.A ¤Î D.xxxx.a */
+ {
+ /* Âà´«¤å³¹ .DIR */
+ memset(&hdr, 0, sizeof(HDR));
+ chrono = trans_hdr_chrono(fh.filename);
+ trans_man_stamp(folder, 'A', &hdr, fpath, chrono);
+ hdr.xmode = 0;
+ str_ncpy(hdr.owner, fh.owner, sizeof(hdr.owner));
+ str_ncpy(hdr.title, fh.title + 3, sizeof(hdr.title));
+ rec_add(folder, &hdr, sizeof(HDR));
+
+ /* «þ¨©ÀÉ®× */
+ f_cp(buf, fpath, O_TRUNC);
+ }
+ else if (*fh.filename == 'D' && dashd(buf))
+ {
+ char sub_index[256];
+
+ /* Âà´«¤å³¹ .DIR */
+ memset(&hdr, 0, sizeof(HDR));
+ chrono = ++count; /* WD ªº¥Ø¿ý©R¦W¤ñ¸û©_©Ç¡A¥u¦n¦Û¤vµ¹¼Æ¦r */
+ trans_man_stamp(folder, 'F', &hdr, fpath, chrono);
+ hdr.xmode = GEM_FOLDER;
+ str_ncpy(hdr.owner, fh.owner, sizeof(hdr.owner));
+ str_ncpy(hdr.title, fh.title + 3, sizeof(hdr.title));
+ rec_add(folder, &hdr, sizeof(HDR));
+
+ /* recursive ¶i¥hÂà´«¤l¥Ø¿ý */
+ strcpy(sub_index, buf);
+ ptr = strrchr(sub_index, '/') + 1;
+ sprintf(ptr, "%s/.DIR", fh.filename);
+ transman(sub_index, fpath);
+ }
+ }
+ close(fd);
+ }
+}
+#endif
+
+
+/* ----------------------------------------------------- */
+/* Âà´«¥Dµ{¦¡ */
+/* ----------------------------------------------------- */
+
+
+static void
+transusr(user)
+ userec *user;
+{
+ char buf[64];
+
+ printf("Âà´« %s ¨Ï¥ÎªÌ\n", user->userid);
+
+ if (is_bad_userid(user->userid))
+ {
+ printf("%s ¤£¬O¦Xªk ID\n", user->userid);
+ return;
+ }
+
+ usr_fpath(buf, user->userid, NULL);
+ if (dashd(buf))
+ {
+ printf("%s ¤w¸g¦³¦¹ ID\n", user->userid);
+ return;
+ }
+
+ sprintf(buf, OLD_BBSHOME "/home/%s", user->userid);
+ if (!dashd(buf))
+ {
+ printf("%s ªºÀɮפ£¦s¦b\n", user->userid);
+ return;
+ }
+
+ creat_dirs(user);
+ trans_justify(user);
+ trans_sig(user);
+ trans_plans(user);
+ trans_mail(user);
+
+
+#ifdef HAVE_PERSONAL_GEM
+ sprintf(buf, OLD_BBSHOME "/home/%s/man", user->userid);
+ if (dashd(buf))
+ {
+ char index[64], folder[64];
+
+ sprintf(index, "%s/.DIR", buf);
+ usr_fpath(folder, user->userid, "gem/" FN_DIR);
+ transman(index, folder);
+ }
+#endif
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int fd;
+ userec user;
+
+ /* argc == 1 Âà¥þ³¡¨Ï¥ÎªÌ */
+ /* argc == 2 Âà¬Y¯S©w¨Ï¥ÎªÌ */
+
+ if (argc > 2)
+ {
+ printf("Usage: %s [target_user]\n", argv[0]);
+ exit(-1);
+ }
+
+ chdir(BBSHOME);
+
+ if (!dashf(FN_PASSWD))
+ {
+ printf("ERROR! Can't open " FN_PASSWD "\n");
+ exit(-1);
+ }
+ if (!dashd(OLD_BBSHOME "/home"))
+ {
+ printf("ERROR! Can't open " OLD_BBSHOME "/home\n");
+ exit(-1);
+ }
+
+ if ((fd = open(FN_PASSWD, O_RDONLY)) >= 0)
+ {
+ while (read(fd, &user, sizeof(user)) == sizeof(user))
+ {
+ if (argc == 1)
+ {
+ transusr(&user);
+ }
+ else if (!strcmp(user.userid, argv[1]))
+ {
+ transusr(&user);
+ exit(1);
+ }
+ }
+ close(fd);
+ }
+
+ exit(0);
+}
diff --git a/util/tran/transacct.c b/util/tran/transacct.c
new file mode 100644
index 0000000..ac50c59
--- /dev/null
+++ b/util/tran/transacct.c
@@ -0,0 +1,212 @@
+/*-------------------------------------------------------*/
+/* util/transacct.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : M3 ACCT Âà´«µ{¦¡ */
+/* create : 98/12/15 */
+/* update : 02/04/29 */
+/* author : mat.bbs@fall.twbbs.org */
+/* modify : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+/* syntax : transacct [userid] */
+/*-------------------------------------------------------*/
+
+
+#if 0
+
+ ¨Ï¥Î¤èªk¡G
+
+ 0. For Maple 3.X To Maple 3.X
+
+ 1. ¨Ï¥Î«e½Ð¥ý§Q¥Î backupacct.c ³Æ¥÷ .ACCT
+
+ 2. ½Ð¦Û¦æ§ï struct NEW ©M struct OLD
+
+ 3. ¨Ì¤£¦Pªº NEW¡BOLD ¨Ó§ï trans_acct()¡C
+
+#endif
+
+
+#include "bbs.h"
+
+
+/* ----------------------------------------------------- */
+/* (·sªº) ¨Ï¥ÎªÌ±b¸¹ .ACCT struct */
+/* ----------------------------------------------------- */
+
+
+typedef struct /* ­n©M·sª©µ{¦¡ struct ¤@¼Ë */
+{
+ int userno; /* unique positive code */
+
+ char userid[IDLEN + 1]; /* ID */
+ char passwd[PASSLEN + 1]; /* ±K½X */
+ char realname[RNLEN + 1]; /* ¯u¹ê©m¦W */
+ char username[UNLEN + 1]; /* ¼ÊºÙ */
+
+ usint userlevel; /* Åv­­ */
+ usint ufo; /* user favor option */
+ uschar signature; /* ¹w³]ñ¦WÀÉ */
+
+ char year; /* ¥Í¤é(¥Á°ê¦~) */
+ char month; /* ¥Í¤é(¤ë) */
+ char day; /* ¥Í¤é(¤é) */
+ char sex; /* ©Ê§O 0:¤¤©Ê ©_¼Æ:¨k©Ê °¸¼Æ:¤k©Ê */
+ int money; /* »È¹ô */
+ int gold; /* ª÷¹ô */
+
+ int numlogins; /* ¤W¯¸¦¸¼Æ */
+ int numposts; /* µoªí¦¸¼Æ */
+ int numemails; /* ±Hµo Inetrnet E-mail ¦¸¼Æ */
+
+ time_t firstlogin; /* ²Ä¤@¦¸¤W¯¸®É¶¡ */
+ time_t lastlogin; /* ¤W¤@¦¸¤W¯¸®É¶¡ */
+ time_t tcheck; /* ¤W¦¸ check «H½c/¦n¤Í¦W³æªº®É¶¡ */
+ time_t tvalid; /* ³q¹L»{ÃÒªº®É¶¡ */
+
+ char lasthost[30]; /* ¤W¦¸µn¤J¨Ó·½ */
+ char email[60]; /* ¥Ø«eµn°Oªº¹q¤l«H½c */
+} NEW;
+
+
+/* ----------------------------------------------------- */
+/* (ªº) ¨Ï¥ÎªÌ±b¸¹ .ACCT struct */
+/* ----------------------------------------------------- */
+
+
+typedef struct /* ­n©Mª©µ{¦¡ struct ¤@¼Ë */
+{
+ int userno; /* unique positive code */
+
+ char userid[IDLEN + 1]; /* ID */
+ char passwd[PASSLEN + 1]; /* ±K½X */
+ char realname[RNLEN + 1]; /* ¯u¹ê©m¦W */
+ char username[UNLEN + 1]; /* ¼ÊºÙ */
+
+ usint userlevel; /* Åv­­ */
+ usint ufo; /* user favor option */
+ uschar signature; /* ¹w³]ñ¦WÀÉ */
+
+ char year; /* ¥Í¤é(¥Á°ê¦~) */
+ char month; /* ¥Í¤é(¤ë) */
+ char day; /* ¥Í¤é(¤é) */
+ char sex; /* ©Ê§O 0:¤¤©Ê ©_¼Æ:¨k©Ê °¸¼Æ:¤k©Ê */
+ int money; /* »È¹ô */
+ int gold; /* ª÷¹ô */
+
+ int numlogins; /* ¤W¯¸¦¸¼Æ */
+ int numposts; /* µoªí¦¸¼Æ */
+ int numemails; /* ±Hµo Inetrnet E-mail ¦¸¼Æ */
+
+ time_t firstlogin; /* ²Ä¤@¦¸¤W¯¸®É¶¡ */
+ time_t lastlogin; /* ¤W¤@¦¸¤W¯¸®É¶¡ */
+ time_t tcheck; /* ¤W¦¸ check «H½c/¦n¤Í¦W³æªº®É¶¡ */
+ time_t tvalid; /* ³q¹L»{ÃÒªº®É¶¡ */
+
+ char lasthost[30]; /* ¤W¦¸µn¤J¨Ó·½ */
+ char email[60]; /* ¥Ø«eµn°Oªº¹q¤l«H½c */
+} OLD;
+
+
+/* ----------------------------------------------------- */
+/* Âà´«¥Dµ{¦¡ */
+/* ----------------------------------------------------- */
+
+
+static void
+trans_acct(old, new)
+ OLD *old;
+ NEW *new;
+{
+ memset(new, 0, sizeof(NEW));
+
+ new->userno = old->userno;
+
+ str_ncpy(new->userid, old->userid, sizeof(new->userid));
+ str_ncpy(new->passwd, old->passwd, sizeof(new->passwd));
+ str_ncpy(new->realname, old->realname, sizeof(new->realname));
+ str_ncpy(new->username, old->username, sizeof(new->username));
+
+ new->userlevel = old->userlevel;
+ new->ufo = old->ufo;
+ new->signature = old->signature;
+
+ new->year = old->year;
+ new->month = old->month;
+ new->day = old->day;
+ new->sex = old->sex;
+ new->money = old->money;
+ new->gold = old->gold;
+
+ new->numlogins = old->numlogins;
+ new->numposts = old->numposts;
+ new->numemails = old->numemails;
+
+ new->firstlogin = old->firstlogin;
+ new->lastlogin = old->lastlogin;
+ new->tcheck = old->tcheck;
+ new->tvalid = old->tvalid;
+
+ str_ncpy(new->lasthost, old->lasthost, sizeof(new->lasthost));
+ str_ncpy(new->email, old->email, sizeof(new->email));
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ NEW new;
+ char c;
+
+ if (argc > 2)
+ {
+ printf("Usage: %s [userid]\n", argv[0]);
+ return -1;
+ }
+
+ for (c = 'a'; c <= 'z'; c++)
+ {
+ char buf[64];
+ struct dirent *de;
+ DIR *dirp;
+
+ sprintf(buf, BBSHOME "/usr/%c", c);
+ chdir(buf);
+
+ if (!(dirp = opendir(".")))
+ continue;
+
+ while (de = readdir(dirp))
+ {
+ OLD old;
+ int fd;
+ char *str;
+
+ str = de->d_name;
+ if (*str <= ' ' || *str == '.')
+ continue;
+
+ if ((argc == 2) && str_cmp(str, argv[1]))
+ continue;
+
+ sprintf(buf, "%s/" FN_ACCT, str);
+ if ((fd = open(buf, O_RDONLY)) < 0)
+ continue;
+
+ read(fd, &old, sizeof(OLD));
+ close(fd);
+ unlink(buf); /* itoc.010831: ¬å±¼­ì¨Óªº FN_ACCT */
+
+ trans_acct(&old, &new);
+
+ fd = open(buf, O_WRONLY | O_CREAT, 0600); /* itoc.010831: ­««Ø·sªº FN_ACCT */
+ write(fd, &new, sizeof(NEW));
+ close(fd);
+ }
+
+ closedir(dirp);
+ }
+
+ return 0;
+}
diff --git a/util/tran/transbrd.c b/util/tran/transbrd.c
new file mode 100644
index 0000000..23207c9
--- /dev/null
+++ b/util/tran/transbrd.c
@@ -0,0 +1,125 @@
+/*-------------------------------------------------------*/
+/* util/transacct.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : M3 BRD Âà´«µ{¦¡ */
+/* create : 05/05/19 */
+/* update : / / */
+/* author : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#if 0
+
+ ¨Ï¥Î¤èªk¡G
+
+ 0. For Maple 3.X To Maple 3.X
+
+ 1. ¨Ï¥Î«e½Ð¥ý³Æ¥÷ .BRD
+
+ 2. ½Ð¦Û¦æ§ï struct NEW ©M struct OLD
+
+#endif
+
+
+#include "bbs.h"
+
+
+/* ----------------------------------------------------- */
+/* (·sªº) ¬ÝªO .BRD struct */
+/* ----------------------------------------------------- */
+
+
+typedef struct /* ­n©M·sª©µ{¦¡ struct ¤@¼Ë */
+{
+ char brdname[BNLEN + 1]; /* board name */
+ char class[BCLEN + 1];
+ char title[BTLEN + 1];
+ char BM[BMLEN + 1]; /* BMs' uid, token '/' */
+
+ char bvote; /* 0:µL§ë²¼ -1:¦³½ä½L(¥i¯à¦³§ë²¼) 1:¦³§ë²¼ */
+
+ time_t bstamp; /* «Ø¥ß¬ÝªOªº®É¶¡, unique */
+ usint readlevel; /* ¾\ۤ峹ªºÅv­­ */
+ usint postlevel; /* µoªí¤å³¹ªºÅv­­ */
+ usint battr; /* ¬ÝªOÄÝ©Ê */
+ time_t btime; /* -1:bpost/blast »Ý­n§ó·s */
+ int bpost; /* ¦@¦³´X½g post */
+ time_t blast; /* ³Ì«á¤@½g post ªº®É¶¡ */
+} NEW;
+
+
+/* ----------------------------------------------------- */
+/* (ªº) ¬ÝªO .BRD struct */
+/* ----------------------------------------------------- */
+
+
+typedef struct /* ­n©Mª©µ{¦¡ struct ¤@¼Ë */
+{
+ char brdname[BNLEN + 1]; /* board name */
+ char class[BCLEN + 1];
+ char title[BTLEN + 1];
+ char BM[BMLEN + 1]; /* BMs' uid, token '/' */
+
+ char bvote; /* 0:µL§ë²¼ -1:¦³½ä½L(¥i¯à¦³§ë²¼) 1:¦³§ë²¼ */
+
+ time_t bstamp; /* «Ø¥ß¬ÝªOªº®É¶¡, unique */
+ usint readlevel; /* ¾\ۤ峹ªºÅv­­ */
+ usint postlevel; /* µoªí¤å³¹ªºÅv­­ */
+ usint battr; /* ¬ÝªOÄÝ©Ê */
+ time_t btime; /* -1:bpost/blast »Ý­n§ó·s */
+ int bpost; /* ¦@¦³´X½g post */
+ time_t blast; /* ³Ì«á¤@½g post ªº®É¶¡ */
+} OLD;
+
+
+/* ----------------------------------------------------- */
+/* Âà´«¥Dµ{¦¡ */
+/* ----------------------------------------------------- */
+
+
+#define FN_BRD_TMP ".BRD.tmp"
+
+
+int
+main()
+{
+ int fd;
+ OLD old;
+ NEW new;
+
+ chdir(BBSHOME);
+
+ if ((fd = open(FN_BRD, O_RDONLY)) >= 0)
+ {
+ while (read(fd, &old, sizeof(OLD)) == sizeof(OLD))
+ {
+ if (!*old.brdname) /* ¦¹ªO¤w³Q§R°£ */
+ continue;
+
+ memset(&new, 0, sizeof(NEW));
+
+ /* Âà´«ªº°Ê§@¦b¦¹ */
+ str_ncpy(new.brdname, old.brdname, sizeof(new.brdname));
+ str_ncpy(new.class, old.class, sizeof(new.class));
+ str_ncpy(new.title, old.title, sizeof(new.title));
+ str_ncpy(new.BM, old.BM, sizeof(new.BM));
+ new.bvote = old.bvote;
+ new.bstamp = old.bstamp;
+ new.readlevel = old.readlevel;
+ new.postlevel = old.postlevel;
+ new.battr = old.battr;
+ new.btime = old.btime;
+ new.bpost = old.bpost;
+ new.blast = old.blast;
+
+ rec_add(FN_BRD_TMP, &new, sizeof(NEW));
+ }
+ close(fd);
+ }
+
+ /* §R°£Âªº¡A§â·sªº§ó¦W */
+ unlink(FN_BRD);
+ rename(FN_BRD_TMP, FN_BRD);
+
+ return 0;
+}
diff --git a/util/tran/wd.h b/util/tran/wd.h
new file mode 100644
index 0000000..8fc22ea
--- /dev/null
+++ b/util/tran/wd.h
@@ -0,0 +1,141 @@
+/*-------------------------------------------------------*/
+/* util/wd.h */
+/*-------------------------------------------------------*/
+/* target : WD ¦Ü Maple 3.02 Âà´« */
+/* create : 02/01/03 */
+/* author : ernie@micro8.ee.nthu.edu.tw */
+/* modify : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#if 0
+
+ 1. ³]©w OLD_BBSHOME¡BFN_PASSWD¡BFN_BOARD
+ 2. ­×§ï©Ò¦³ªº old struct
+
+ 3. ¥²¶·¦b brd Âà§¹¤~¥i¥HÂà´« gem
+ 4. ¥²¶·¦b usr ¤Î brd ³£Âà§¹¤~¥i¥HÂà´« mf
+ 5. ¥²¶·¦b usr ¤Î brd ³£Âà§¹¤~¥i¥HÂà´« pal
+ 6. ¥²¶·¦b usr Âà§¹¤~¥i¥HÂà´« bmw pip list
+ 7. «ØÄ³Âà´«¶¶§Ç¬° usr -> brd -> gem -> mf -> pal -> bmw -> pip -> list
+
+#endif
+
+
+#include "bbs.h"
+
+
+#define OLD_BBSHOME "/home/oldbbs" /* WD */
+#define FN_PASSWD "/home/oldbbs/.PASSWDS" /* WD */
+#define FN_BOARD "/home/oldbbs/.BOARDS" /* WD */
+
+
+#define HAVE_PERSONAL_GEM /* WD ¬O¦³­Ó¤HºëµØ°Ïªº */
+
+
+/* ----------------------------------------------------- */
+/* old .PASSWDS struct : 512 bytes */
+/* ----------------------------------------------------- */
+
+struct userec
+{
+ char userid[13]; /* ¨Ï¥ÎªÌ¦WºÙ 13 bytes */
+ char realname[20]; /* ¯u¹ê©m¦W 20 bytes */
+ char username[24]; /* ¼ÊºÙ 24 bytes */
+ char passwd[14]; /* ±K½X 14 bytes */
+ uschar uflag; /* ¨Ï¥ÎªÌ¿ï¶µ 1 byte */
+ usint userlevel; /* ¨Ï¥ÎªÌÅv­­ 4 bytes */
+ ushort numlogins; /* ¤W¯¸¦¸¼Æ 2 bytes */
+ ushort numposts; /* POST¦¸¼Æ 2 bytes */
+ time_t firstlogin; /* µù¥U®É¶¡ 4 bytes */
+ time_t lastlogin; /* «e¦¸¤W¯¸ 4 bytes */
+ char lasthost[24]; /* ¤W¯¸¦aÂI 24 bytes */
+ char vhost[24]; /* µêÀÀºô§} 24 bytes */
+ char email[50]; /* E-MAIL 50 bytes */
+ char address[50]; /* ¦a§} 50 bytes */
+ char justify[39]; /* µù¥U¸ê®Æ 39 bytes */
+ uschar month; /* ¥X¥Í¤ë¥÷ 1 byte */
+ uschar day; /* ¥X¥Í¤é´Á 1 byte */
+ uschar year; /* ¥X¥Í¦~¥÷ 1 byte */
+ uschar sex; /* ©Ê§O 1 byte */
+ uschar state; /* ª¬ºA?? 1 byte */
+ usint habit; /* ³ß¦n³]©w 4 bytes */
+ uschar pager; /* ¤ß±¡ÃC¦â 1 bytes */
+ uschar invisible; /* Áô¨­¼Ò¦¡ 1 bytes */
+ usint exmailbox; /* «H½c«Ê¼Æ 4 bytes */
+ usint exmailboxk; /* «H½cK¼Æ 4 bytes */
+ usint toquery; /* ¦n©_«× 4 bytes */
+ usint bequery; /* ¤H®ð«× 4 bytes */
+ char toqid[13]; /* «e¦¸¬d½Ö 13 bytes */
+ char beqid[13]; /* «e¦¸³Q½Ö¬d 13 bytes */
+ unsigned long int totaltime; /* ¤W½uÁ`®É¼Æ 8 bytes */
+ usint sendmsg; /* µo°T®§¦¸¼Æ 4 bytes */
+ usint receivemsg; /* ¦¬°T®§¦¸¼Æ 4 bytes */
+ unsigned long int goldmoney; /* ­·¹Ðª÷¹ô 8 bytes */
+ unsigned long int silvermoney; /* »È¹ô 8 bytes */
+ unsigned long int exp; /* ¸gÅç­È 8 bytes */
+ time_t dtime; /* ¦s´Ú®É¶¡ 4 bytes */
+ int limitmoney; /* ª÷¿ú¤U­­ 4 bytes */
+};
+typedef struct userec userec;
+
+
+/* ----------------------------------------------------- */
+/* old DIR of board struct : 128 bytes */
+/* ----------------------------------------------------- */
+
+struct fileheader
+{
+ char filename[33]; /* M.9876543210.A */
+ char savemode; /* file save mode */
+ char owner[14]; /* uid[.] */
+ char date[6]; /* [02/02] or space(5) */
+ char title[73];
+ uschar filemode; /* must be last field @ boards.c */
+};
+typedef struct fileheader fileheader;
+
+
+/* ----------------------------------------------------- */
+/* old BOARDS struct : 512 bytes */
+/* ----------------------------------------------------- */
+
+struct boardheader
+{
+ char brdname[13]; /* ¬ÝªO­^¤å¦WºÙ 13 bytes */
+ char title[49]; /* ¬ÝªO¤¤¤å¦WºÙ 49 bytes */
+ char BM[39]; /* ªO¥DID©M"/" 39 bytes */
+ usint brdattr; /* ¬ÝªOªºÄÝ©Ê 4 bytes */
+ time_t bupdate; /* note update time 4 bytes */
+ uschar bvote; /* Vote flags 1 bytes */
+ time_t vtime; /* Vote close time 4 bytes */
+ usint level; /* ¥i¥H¬Ý¦¹ªOªºÅv­­ 4 bytes */
+ unsigned long int totalvisit; /* Á`«ô³X¤H¼Æ 8 bytes */
+ unsigned long int totaltime; /* Á`°±¯d®É¶¡ 8 bytes */
+ char lastvisit[13]; /* ³Ì«á¬Ý¸ÓªOªº¤H 13 bytes */
+ time_t opentime; /* ¶}ªO®É¶¡ 4 bytes */
+ time_t lastime; /* ³Ì«á«ô³X®É¶¡ 4 bytes */
+ char passwd[14]; /* ±K½X 14 bytes */
+ unsigned long int postotal; /* Á`¤ô¶q :p 8 bytes */
+ usint maxpost; /* ¤å³¹¤W­­ 4 bytes */
+ usint maxtime; /* ¤å³¹«O¯d®É¶¡ 4 bytes */
+ char desc[3][80]; /* ¤¤¤å´y­z 240 bytes */
+ char pad[87];
+};
+typedef struct boardheader boardheader;
+
+
+/* ----------------------------------------------------- */
+/* old FRIEND struct : 128 bytes */
+/* ----------------------------------------------------- */
+
+struct FRIEND
+{
+ char userid[33]; /* list name/userid */
+ char savemode;
+ char owner[14]; /* bbcall */
+ char date[6]; /* birthday */
+ char desc[73]; /* list/user description */
+ uschar ftype; /* mode: PAL, BAD */
+};
+typedef struct FRIEND FRIEND;
diff --git a/util/tran/wd2bmw.c b/util/tran/wd2bmw.c
new file mode 100644
index 0000000..36c5a7f
--- /dev/null
+++ b/util/tran/wd2bmw.c
@@ -0,0 +1,131 @@
+/*-------------------------------------------------------*/
+/* util/transbmw.c */
+/*-------------------------------------------------------*/
+/* target : WD ¦Ü Maple 3.02 ¤ô²y°O¿ýÂà´« */
+/* create : 02/01/22 */
+/* update : / / */
+/* author : itoc.bbs@bbs.ee.nctu.edu.tw */
+/*-------------------------------------------------------*/
+/* syntax : transbmw */
+/*-------------------------------------------------------*/
+
+
+#if 0
+
+ 1. ­×§ï transbmw()
+
+ ps. ¨Ï¥Î«e½Ð¥ý¦æ³Æ¥÷¡Ause on ur own risk. µ{¦¡©å¦H½Ð¥]²[ :p
+ ps. ·PÁ lkchu ªº Maple 3.02 for FreeBSD
+
+#endif
+
+
+#include "wd.h"
+
+
+/* ----------------------------------------------------- */
+/* 3.02 functions */
+/* ----------------------------------------------------- */
+
+
+static void
+_mail_self(userid, fpath, owner, title) /* itoc.011115: ±HÀÉ®×µ¹¦Û¤v */
+ char *userid; /* ¦¬¥óªÌ */
+ char *fpath; /* Àɮ׸ô®| */
+ char *owner; /* ±H¥ó¤H */
+ char *title; /* ¶l¥ó¼ÐÃD */
+{
+ HDR fhdr;
+ char folder[64];
+
+ usr_fpath(folder, userid, FN_DIR);
+ close(hdr_stamp(folder, HDR_LINK, &fhdr, fpath));
+ str_ncpy(fhdr.owner, owner, sizeof(fhdr.owner));
+ str_ncpy(fhdr.title, title, sizeof(fhdr.title));
+ fhdr.xmode = 0;
+ rec_add(folder, &fhdr, sizeof(fhdr));
+}
+
+
+/* ----------------------------------------------------- */
+/* Âà´«¥Dµ{¦¡ */
+/* ----------------------------------------------------- */
+
+
+static void
+transbmw(userid)
+ char *userid;
+{
+ ACCT acct;
+ int fd;
+ char buf[64];
+
+ /* sob ªº usr ¥Ø¿ý¦³¤À¤j¤p¼g¡A©Ò¥H­n¥ý¨ú±o¤j¤p¼g */
+ usr_fpath(buf, userid, FN_ACCT);
+ if ((fd = open(buf, O_RDONLY)) >= 0)
+ {
+ read(fd, &acct, sizeof(ACCT));
+ close(fd);
+ }
+ else
+ {
+ return;
+ }
+
+ sprintf(buf, OLD_BBSHOME"/home/%s/writelog", acct.userid); /* ªº¤ô²y°O¿ý */
+
+ if (dashf(buf))
+ _mail_self(acct.userid, buf, "[³Æ§Ñ¿ý]", "¼ö½u\033[41m°O¿ý\033[m");
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ char c;
+ char buf[64];
+ struct dirent *de;
+ DIR *dirp;
+
+ /* argc == 1 Âà¥þ³¡¨Ï¥ÎªÌ */
+ /* argc == 2 Âà¬Y¯S©w¨Ï¥ÎªÌ */
+
+ if (argc > 2)
+ {
+ printf("Usage: %s [target_user]\n", argv[0]);
+ exit(-1);
+ }
+
+ chdir(BBSHOME);
+
+ if (argc == 2)
+ {
+ transbmw(argv[1]);
+ exit(1);
+ }
+
+ /* Âà´«¨Ï¥ÎªÌ¤ô²y°O¿ý */
+ for (c = 'a'; c <= 'z'; c++)
+ {
+ sprintf(buf, "usr/%c", c);
+
+ if (!(dirp = opendir(buf)))
+ continue;
+
+ while (de = readdir(dirp))
+ {
+ char *str;
+
+ str = de->d_name;
+ if (*str <= ' ' || *str == '.')
+ continue;
+
+ transbmw(str);
+ }
+
+ closedir(dirp);
+ }
+ return 0;
+}
diff --git a/util/tran/wd2brd.c b/util/tran/wd2brd.c
new file mode 100644
index 0000000..fb7bff3
--- /dev/null
+++ b/util/tran/wd2brd.c
@@ -0,0 +1,289 @@
+/*-------------------------------------------------------*/
+/* util/transbrd.c */
+/*-------------------------------------------------------*/
+/* target : WD ¦Ü Maple 3.02 ¬ÝªOÂà´« */
+/* .BOARDS => .BRD */
+/* create : 98/06/15 */
+/* update : 02/01/05 */
+/* author : ernie@micro8.ee.nthu.edu.tw */
+/* modify : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+#if 0
+
+ 1. ­×§ï struct boardheader ¤Î transbrd()
+ (boardheader ¨âª©©w¸qªº¦r¦êªø«×¤£¤@¡A½Ð¦Û¦æ´«¦¨¼Æ¦r)
+ 2. WD ¬ÝªO¤ÀÃþ(boardheader.title «e 4 bytes)±Ë±ó
+ 3. §ë²¼¤£Âà´«
+ 4. ¶iªOµe­±ª½±µ copy
+ 5. ¦p¦³»Ý­n½Ð chmod 644 `find PATH -perm 600`
+ 6. ¶} gem ¥Ø¿ý gem/target_board/? ¦ý¤£Âà´« gem
+ 7. ¤£·|§ó·s bshm¡A¨Ï¥Î«á½Ð¦Û¦æ§ó·s
+
+ ps. Use on ur own risk.
+
+#endif
+
+
+#include "wd.h"
+
+
+static inline usint
+trans_brd_battr(brdattr) /* itoc.010426: Âà´«¬ÝªOÄÝ©Ê */
+ usint brdattr;
+{
+ usint battr;
+
+ battr = 0;
+
+ if (brdattr & 00001)
+ battr |= BRD_NOZAP;
+
+ if (brdattr & 00002)
+ battr |= BRD_NOCOUNT;
+
+ if (brdattr & 00004)
+ battr |= BRD_NOTRAN;
+
+#ifdef HAVE_ANONYMOUS
+ if (brdattr & 00100)
+ battr |= BRD_ANONYMOUS;
+#endif
+
+#ifdef HAVE_MODERATED_BOARD
+ if (brdattr & (00020 | 01000)) /* ÁôÂêO§ë²¼¤£¤½§i */
+ battr |= BRD_NOVOTE;
+#endif
+
+ return battr;
+}
+
+
+static inline usint
+trans_brd_rlevel(brdattr) /* itoc.010426: Âà´«¬ÝªO¾\ŪÅv­­ */
+ usint brdattr;
+{
+#ifdef HAVE_MODERATED_BOARD
+ if (brdattr & 00020) /* ÁôÂêO */
+ return PERM_SYSOP;
+ else if (brdattr & 01000) /* ¦n¤ÍªO */
+ return PERM_BOARD;
+ else /* ¤@¯ë¬ÝªO */
+#endif
+ return 0;
+}
+
+
+static inline usint
+trans_brd_plevel(brdattr) /* itoc.010426: Âà´«¬ÝªOµoªíÅv­­ */
+ usint brdattr;
+{
+#ifdef HAVE_MODERATED_BOARD
+ if (brdattr & 00020) /* ÁôÂêO */
+ return 0;
+ else if (brdattr & 01000) /* ¦n¤ÍªO */
+ return 0;
+ else /* ¤@¯ë¬ÝªO */
+#endif
+ return PERM_POST; /* ¤@¯ë¬ÝªO¹w³]¬° POST_POST */
+}
+
+
+static inline time_t
+trans_hdr_chrono(filename)
+ char *filename;
+{
+ char time_str[11];
+
+ /* M.1087654321.A ©Î M.987654321.A */
+ str_ncpy(time_str, filename + 2, filename[2] == '1' ? 11 : 10);
+
+ return (time_t) atoi(time_str);
+}
+
+
+static inline void
+trans_hdr_stamp(folder, t, hdr, fpath)
+ char *folder;
+ time_t t;
+ HDR *hdr;
+ char *fpath;
+{
+ FILE *fp;
+ char *fname, *family;
+ int rc;
+
+ fname = fpath;
+ while (rc = *folder++)
+ {
+ *fname++ = rc;
+ if (rc == '/')
+ family = fname;
+ }
+ fname = family + 1;
+ *fname++ = '/';
+ *fname++ = 'A';
+
+ for (;;)
+ {
+ *family = radix32[t & 31];
+ archiv32(t, fname);
+
+ if (fp = fopen(fpath, "r"))
+ {
+ fclose(fp);
+ t++;
+ }
+ else
+ {
+ memset(hdr, 0, sizeof(HDR));
+ hdr->chrono = t;
+ str_stamp(hdr->date, &hdr->chrono);
+ strcpy(hdr->xname, --fname);
+ break;
+ }
+ }
+}
+
+
+/* ----------------------------------------------------- */
+/* Âà´«¥Dµ{¦¡ */
+/* ----------------------------------------------------- */
+
+
+static void
+transbrd(bh)
+ boardheader *bh;
+{
+ static time_t stamp = 0;
+
+ int fd;
+ char index[64], folder[64], buf[64], fpath[64];
+ fileheader fh;
+ HDR hdr;
+ BRD newboard;
+ time_t chrono;
+
+ printf("Âà´« %s ¬ÝªO\n", bh->brdname);
+
+ brd_fpath(buf, bh->brdname, NULL);
+ if (dashd(buf))
+ {
+ printf("%s ¤w¸g¦³¦¹¬ÝªO\n", bh->brdname);
+ return;
+ }
+
+ if (!stamp)
+ time(&stamp);
+
+ /* Âà´« .BRD */
+
+ memset(&newboard, 0, sizeof(newboard));
+ str_ncpy(newboard.brdname, bh->brdname, sizeof(newboard.brdname));
+ str_ncpy(newboard.class, bh->title, sizeof(newboard.class));
+ str_ncpy(newboard.title, bh->title + 7, sizeof(newboard.title));
+ str_ncpy(newboard.BM, bh->BM, sizeof(newboard.BM));
+ newboard.bstamp = stamp++;
+ newboard.battr = trans_brd_battr(bh->brdattr);
+ newboard.readlevel = trans_brd_rlevel(bh->brdattr);
+ newboard.postlevel = trans_brd_plevel(bh->brdattr);
+
+ rec_add(FN_BRD, &newboard, sizeof(newboard)); /* §O§Ñ¤F¥Î brd2gem.c ¨ÓÂà´« Class */
+
+ /* ¶}·s¥Ø¿ý */
+
+ sprintf(fpath, "gem/brd/%s", newboard.brdname);
+ mak_dirs(fpath);
+ mak_dirs(fpath + 4);
+
+ /* Âà´«¶iªOµe­± */
+
+ sprintf(buf, OLD_BBSHOME "/boards/%s/notes", bh->brdname);
+
+ if (dashf(buf))
+ {
+ brd_fpath(fpath, newboard.brdname, FN_NOTE);
+ f_cp(buf, fpath, O_TRUNC);
+ }
+
+ /* Âà´«¤å³¹ */
+
+ sprintf(index, OLD_BBSHOME "/boards/%s/.DIR", bh->brdname); /* 廼 .DIR */
+ brd_fpath(folder, newboard.brdname, ".DIR"); /* ·sªº .DIR */
+
+ if ((fd = open(index, O_RDONLY)) >= 0)
+ {
+ while (read(fd, &fh, sizeof(fh)) == sizeof(fh))
+ {
+ sprintf(buf, OLD_BBSHOME "/boards/%s/%s", bh->brdname, fh.filename);
+ if (dashf(buf)) /* ¤å³¹Àɮצb¤~°µÂà´« */
+ {
+ /* Âà´«¤å³¹ .DIR */
+ memset(&hdr, 0, sizeof(HDR));
+ chrono = trans_hdr_chrono(fh.filename);
+ trans_hdr_stamp(folder, chrono, &hdr, fpath);
+ str_ncpy(hdr.owner, fh.owner, sizeof(hdr.owner));
+ str_ansi(hdr.title, fh.title, sizeof(hdr.title));
+ hdr.xmode = (fh.filemode & 0x2) ? POST_MARKED : 0;
+ rec_add(folder, &hdr, sizeof(HDR));
+
+ /* «þ¨©ÀÉ®× */
+ f_cp(buf, fpath, O_TRUNC);
+ }
+ }
+ close(fd);
+ }
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int fd;
+ boardheader bh;
+
+ /* argc == 1 Âà¥þ³¡ªO */
+ /* argc == 2 Âà¬Y¯S©wªO */
+
+ if (argc > 2)
+ {
+ printf("Usage: %s [target_board]\n", argv[0]);
+ exit(-1);
+ }
+
+ chdir(BBSHOME);
+
+ if (!dashf(FN_BOARD))
+ {
+ printf("ERROR! Can't open " FN_BOARD "\n");
+ exit(-1);
+ }
+ if (!dashd(OLD_BBSHOME "/boards"))
+ {
+ printf("ERROR! Can't open " OLD_BBSHOME "/boards\n");
+ exit(-1);
+ }
+
+ if ((fd = open(FN_BOARD, O_RDONLY)) >= 0)
+ {
+ while (read(fd, &bh, sizeof(bh)) == sizeof(bh))
+ {
+ if (argc == 1)
+ {
+ transbrd(&bh);
+ }
+ else if (!strcmp(bh.brdname, argv[1]))
+ {
+ transbrd(&bh);
+ exit(1);
+ }
+ }
+ close(fd);
+ }
+
+
+
+ exit(0);
+}
diff --git a/util/tran/wd2gem.c b/util/tran/wd2gem.c
new file mode 100644
index 0000000..8649450
--- /dev/null
+++ b/util/tran/wd2gem.c
@@ -0,0 +1,232 @@
+/*-------------------------------------------------------*/
+/* util/transman.c */
+/*-------------------------------------------------------*/
+/* target : Ptt WD ¦Ü Maple 3.02 ºëµØ°ÏÂà´« */
+/* create : 98/06/15 */
+/* update : 02/01/05 */
+/* author : ernie@micro8.ee.nthu.edu.tw */
+/* modify : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#if 0
+
+ 0. ¾A¥Î WD Ptt Âà maple ºëµØ°Ï¡C
+ 1. µ{¦¡¤£¶}¥Ø¿ý¡A¨Ï¥Î«e¥ý½T©w gem/target_board/? ¥Ø¿ý¦s¦b
+ if not¡A¥ý¶}·sªO or transbrd
+ 2. ¥uÂà M.*.A ¤Î D.*.A¡A¨ä¥L link ¤£Âà´«
+ 3. ¦p¦³»Ý­n½Ð¥ý chmod 644 `find PATH -perm 600`
+
+ ps. User on ur own risk.
+
+#endif
+
+
+#include "wd.h"
+
+
+/* ----------------------------------------------------- */
+/* Âà´«ºëµØ°Ï */
+/* ----------------------------------------------------- */
+
+
+static time_t
+trans_hdr_chrono(filename)
+ char *filename;
+{
+ char time_str[11];
+
+ /* M.1087654321.A ©Î M.987654321.A */
+ str_ncpy(time_str, filename + 2, filename[2] == '1' ? 11 : 10);
+
+ return (time_t) atoi(time_str);
+}
+
+
+static void
+trans_man_stamp(folder, token, hdr, fpath, time)
+ char *folder;
+ int token;
+ HDR *hdr;
+ char *fpath;
+ time_t time;
+{
+ char *fname, *family;
+ int rc;
+
+ fname = fpath;
+ while (rc = *folder++)
+ {
+ *fname++ = rc;
+ if (rc == '/')
+ family = fname;
+ }
+ if (*family != '.')
+ {
+ fname = family;
+ family -= 2;
+ }
+ else
+ {
+ fname = family + 1;
+ *fname++ = '/';
+ }
+
+ *fname++ = token;
+
+ *family = radix32[time & 31];
+ archiv32(time, fname);
+
+ if (rc = open(fpath, O_WRONLY | O_CREAT | O_EXCL, 0600))
+ {
+ memset(hdr, 0, sizeof(HDR));
+ hdr->chrono = time;
+ str_stamp(hdr->date, &hdr->chrono);
+ strcpy(hdr->xname, --fname);
+ close(rc);
+ }
+ return;
+}
+
+
+/* ----------------------------------------------------- */
+/* Âà´«¥Dµ{¦¡ */
+/* ----------------------------------------------------- */
+
+
+static void
+transman(index, folder)
+ char *index, *folder;
+{
+ static int count = 100;
+
+ int fd;
+ char *ptr, buf[256], fpath[64];
+ fileheader fh;
+ HDR hdr;
+ time_t chrono;
+
+ if ((fd = open(index, O_RDONLY)) >= 0)
+ {
+ while (read(fd, &fh, sizeof(fh)) == sizeof(fh))
+ {
+ strcpy(buf, index);
+ ptr = strrchr(buf, '/') + 1;
+ strcpy(ptr, fh.filename);
+
+ if (*fh.filename == 'M' && dashf(buf)) /* ¥uÂà M.xxxx.A ¤Î D.xxxx.a */
+ {
+ /* Âà´«¤å³¹ .DIR */
+ memset(&hdr, 0, sizeof(HDR));
+ chrono = trans_hdr_chrono(fh.filename);
+ trans_man_stamp(folder, 'A', &hdr, fpath, chrono);
+ hdr.xmode = 0;
+ str_ncpy(hdr.owner, fh.owner, sizeof(hdr.owner));
+ str_ncpy(hdr.title, fh.title + 3, sizeof(hdr.title));
+ rec_add(folder, &hdr, sizeof(HDR));
+
+ /* «þ¨©ÀÉ®× */
+ f_cp(buf, fpath, O_TRUNC);
+ }
+ else if (*fh.filename == 'D' && dashd(buf))
+ {
+ char sub_index[256];
+
+ /* Âà´«¤å³¹ .DIR */
+ memset(&hdr, 0, sizeof(HDR));
+ chrono = ++count; /* WD ªº¥Ø¿ý©R¦W¤ñ¸û©_©Ç¡A¥u¦n¦Û¤vµ¹¼Æ¦r */
+ trans_man_stamp(folder, 'F', &hdr, fpath, chrono);
+ hdr.xmode = GEM_FOLDER;
+ str_ncpy(hdr.owner, fh.owner, sizeof(hdr.owner));
+ str_ncpy(hdr.title, fh.title + 3, sizeof(hdr.title));
+ rec_add(folder, &hdr, sizeof(HDR));
+
+ /* recursive ¶i¥hÂà´«¤l¥Ø¿ý */
+ strcpy(sub_index, buf);
+ ptr = strrchr(sub_index, '/') + 1;
+ sprintf(ptr, "%s/.DIR", fh.filename);
+ transman(sub_index, fpath);
+ }
+ }
+ close(fd);
+ }
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int fd;
+ char *brdname, index[64], folder[64];
+ boardheader bh;
+
+ /* argc == 1 Âà¥þ³¡ªO */
+ /* argc == 2 Âà¬Y¯S©wªO */
+
+ if (argc > 2)
+ {
+ printf("Usage: %s [target_board]\n", argv[0]);
+ exit(-1);
+ }
+
+ chdir(BBSHOME);
+
+ if (!dashf(FN_BOARD))
+ {
+ printf("ERROR! Can't open " FN_BOARD "\n");
+ exit(-1);
+ }
+ if (!dashd(OLD_BBSHOME "/man/boards"))
+ {
+ printf("ERROR! Can't open " OLD_BBSHOME "/man/boards\n");
+ exit(-1);
+ }
+
+ if (argc == 1)
+ {
+ if ((fd = open(FN_BOARD, O_RDONLY)) >= 0)
+ {
+ while (read(fd, &bh, sizeof(bh)) == sizeof(bh))
+ {
+ brdname = bh.brdname;
+
+ sprintf(folder, "gem/brd/%s", brdname);
+ if (!dashd(folder))
+ {
+ printf("ERROR! %s not exist. New it first.\n", folder);
+ continue;
+ }
+
+ sprintf(index, OLD_BBSHOME "/man/boards/%s/.DIR", brdname);
+ sprintf(folder, "gem/brd/%s/%s", brdname, FN_DIR);
+
+ printf("Âà´« %s ºëµØ°Ï\n", brdname);
+ transman(index, folder);
+ }
+ close(fd);
+ }
+ }
+ else
+ {
+ brdname = argv[1];
+
+ sprintf(folder, "gem/brd/%s", brdname);
+ if (!dashd(folder))
+ {
+ printf("ERROR! %s not exist. New it first.\n", folder);
+ exit(-1);
+ }
+
+ sprintf(index, OLD_BBSHOME "/man/boards/%s/.DIR", brdname);
+ sprintf(folder, "gem/brd/%s/%s", brdname, FN_DIR);
+
+ printf("Âà´« %s ºëµØ°Ï\n", brdname);
+ transman(index, folder);
+
+ exit(1);
+ }
+
+ exit(0);
+}
diff --git a/util/tran/wd2list.c b/util/tran/wd2list.c
new file mode 100644
index 0000000..235b9b1
--- /dev/null
+++ b/util/tran/wd2list.c
@@ -0,0 +1,172 @@
+/*-------------------------------------------------------*/
+/* util/translist.c */
+/*-------------------------------------------------------*/
+/* target : WD ¦Ü Maple 3.02 ¯S®í¦W³æÂà´« */
+/* create : 02/01/26 */
+/* update : / / */
+/* author : itoc.bbs@bbs.ee.nctu.edu.tw */
+/*-------------------------------------------------------*/
+/* syntax : translist */
+/*-------------------------------------------------------*/
+
+
+#if 0
+
+ 1. ­×§ï translist()
+ 2. ¥uÂà list.1 ~ list.5
+
+ ps. ¨Ï¥Î«e½Ð¥ý¦æ³Æ¥÷¡Ause on ur own risk. µ{¦¡©å¦H½Ð¥]²[ :p
+ ps. ·PÁ lkchu ªº Maple 3.02 for FreeBSD
+
+#endif
+
+
+#include "wd.h"
+
+#ifdef HAVE_LIST
+
+/* ----------------------------------------------------- */
+/* 3.02 functions */
+/* ----------------------------------------------------- */
+
+
+static int
+acct_uno(userid)
+ char *userid;
+{
+ int fd;
+ int userno;
+ char fpath[64];
+
+ usr_fpath(fpath, userid, FN_ACCT);
+ fd = open(fpath, O_RDONLY);
+ if (fd >= 0)
+ {
+ read(fd, &userno, sizeof(userno));
+ close(fd);
+ return userno;
+ }
+ return -1;
+}
+
+
+/* ----------------------------------------------------- */
+/* Âà´«¥Dµ{¦¡ */
+/* ----------------------------------------------------- */
+
+
+static void
+translist(userid)
+ char *userid;
+{
+ ACCT acct;
+ int pos, fd, friend_userno, i;
+ char fpath[64], buf[64];
+ PAL pal;
+ FRIEND friend;
+
+ /* sob ªº usr ¥Ø¿ý¦³¤À¤j¤p¼g¡A©Ò¥H­n¥ý¨ú±o¤j¤p¼g */
+ usr_fpath(buf, userid, FN_ACCT);
+ if ((fd = open(buf, O_RDONLY)) >= 0)
+ {
+ read(fd, &acct, sizeof(ACCT));
+ close(fd);
+ }
+
+ for (i = 1; i <= 5; i++) /* ¥uÂà list.1 ~ list.5 */
+ {
+ sprintf(buf, "%s.%d", FN_LIST, i);
+ usr_fpath(fpath, userid, buf); /* ·sªº¯S®í¦W³æ */
+ sprintf(buf, OLD_BBSHOME "/home/%s/list.%d", acct.userid, i);/* ªº¯S®í¦W³æ */
+
+ if (dashf(fpath))
+ unlink(fpath); /* ²M±¼­««Ø */
+
+ pos = 0;
+ fd = open(buf, O_RDONLY);
+ if (fd < 0)
+ continue;
+
+ while (fd)
+ {
+ lseek(fd, (off_t) (sizeof(FRIEND) * pos), SEEK_SET);
+ if (read(fd, &friend, sizeof(FRIEND)) == sizeof(FRIEND))
+ {
+ if ((friend_userno = acct_uno(friend.userid)) >= 0)
+ {
+ str_ncpy(pal.userid, friend.userid, sizeof(pal.userid));
+ pal.ftype = 0; /* ¯S®í¦W³æ¤@«ß¬°¦n¤Í */
+ str_ncpy(pal.ship, friend.desc, sizeof(pal.ship));
+ pal.userno = friend_userno;
+ rec_add(fpath, &pal, sizeof(PAL));
+ }
+ pos++;
+ }
+ else
+ {
+ close(fd);
+ break;
+ }
+ }
+ }
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ char c;
+ char buf[64];
+ struct dirent *de;
+ DIR *dirp;
+
+ /* argc == 1 Âà¥þ³¡¨Ï¥ÎªÌ */
+ /* argc == 2 Âà¬Y¯S©w¨Ï¥ÎªÌ */
+
+ if (argc > 2)
+ {
+ printf("Usage: %s [target_user]\n", argv[0]);
+ exit(-1);
+ }
+
+ chdir(BBSHOME);
+
+ if (argc == 2)
+ {
+ translist(argv[1]);
+ exit(1);
+ }
+
+ /* Âà´«¨Ï¥ÎªÌ¯S®í¦W³æ */
+ for (c = 'a'; c <= 'z'; c++)
+ {
+ sprintf(buf, "usr/%c", c);
+
+ if (!(dirp = opendir(buf)))
+ continue;
+
+ while (de = readdir(dirp))
+ {
+ char *str;
+
+ str = de->d_name;
+ if (*str <= ' ' || *str == '.')
+ continue;
+
+ translist(str);
+ }
+
+ closedir(dirp);
+ }
+ return 0;
+}
+#else
+int
+main()
+{
+ printf("You should define HAVE_LIST first.\n");
+ return -1;
+}
+#endif /* HAVE_LIST */
diff --git a/util/tran/wd2mf.c b/util/tran/wd2mf.c
new file mode 100644
index 0000000..c08c230
--- /dev/null
+++ b/util/tran/wd2mf.c
@@ -0,0 +1,171 @@
+/*-------------------------------------------------------*/
+/* util/transfavor.c */
+/*-------------------------------------------------------*/
+/* target : WD ¦Ü Maple 3.02 §Úªº³Ì·RÂà´« */
+/* create : 01/09/15 */
+/* update : / / */
+/* author : itoc.bbs@bbs.ee.nctu.edu.tw */
+/*-------------------------------------------------------*/
+/* syntax : transfavor */
+/*-------------------------------------------------------*/
+
+
+#if 0
+
+ 1. ­×§ï transmf()
+ 2. Âà´«(¬ÝªO)¦n¤Í¦W³æ¤§«e¡A±z¥²¶·¥ýÂà´«§¹¬ÝªO¤Î¨Ï¥ÎªÌ¡C
+
+ ps. ¨Ï¥Î«e½Ð¥ý¦æ³Æ¥÷¡Ause on ur own risk. µ{¦¡©å¦H½Ð¥]²[ :p
+ ps. ·PÁ lkchu ªº Maple 3.02 for FreeBSD
+
+#endif
+
+
+#include "wd.h"
+
+#ifdef MY_FAVORITE
+
+
+/* ----------------------------------------------------- */
+/* 3.02 functions */
+/* ----------------------------------------------------- */
+
+
+static void
+_mf_fpath(fpath, userid, fname)
+ char *fpath;
+ char *userid; /* lower ID */
+ char *fname;
+{
+ if (fname)
+ sprintf(fpath, "usr/%c/%s/MF/%s", userid[0], userid, fname);
+ else
+ sprintf(fpath, "usr/%c/%s/MF", userid[0], userid);
+}
+
+
+/* ----------------------------------------------------- */
+/* Âà´«¥Dµ{¦¡ */
+/* ----------------------------------------------------- */
+
+
+static void
+transmf(userid)
+ char *userid;
+{
+ ACCT acct;
+ FILE *fp;
+ int fd, num;
+ char fpath[64], buf[64];
+ char *str, brdname[BNLEN + 1];
+ MF mf;
+
+ /* «Ø¥ß¥Ø¿ý */
+ _mf_fpath(fpath, userid, NULL);
+ mkdir(fpath, 0700);
+
+ /* sob ªº usr ¥Ø¿ý¦³¤À¤j¤p¼g¡A©Ò¥H­n¥ý¨ú±o¤j¤p¼g */
+ usr_fpath(buf, userid, FN_ACCT);
+ if ((fd = open(buf, O_RDONLY)) >= 0)
+ {
+ read(fd, &acct, sizeof(ACCT));
+ close(fd);
+ }
+ else
+ {
+ return;
+ }
+
+ sprintf(buf, OLD_BBSHOME"/home/%s/favorite", acct.userid); /* ªº§Úªº³Ì·R */
+
+ if (!(fp = fopen(buf, "r")))
+ return;
+
+ _mf_fpath(fpath, userid, FN_MF);
+ num = 0;
+
+ while (fgets(brdname, BNLEN + 1, fp))
+ {
+ for (str = brdname; *str; str++)
+ {
+ if (*str <= ' ')
+ {
+ *str = '\0';
+ break;
+ }
+ }
+
+ brd_fpath(buf, brdname, NULL);
+ if (dashd(buf)) /* ªº½T¦³³o­ÓªO */
+ {
+ mf.chrono = ++num;
+ mf.mftype = MF_BOARD;
+ str_ncpy(mf.xname, brdname, sizeof(mf.xname));
+ mf.title[0] = '\0'; /* ¬ÝªO±¶®|¨S¦³ mf.title */
+ rec_add(fpath, &mf, sizeof(MF));
+ }
+ }
+
+ fclose(fp);
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ char c;
+ char buf[64];
+ struct dirent *de;
+ DIR *dirp;
+
+ /* argc == 1 Âà¥þ³¡¨Ï¥ÎªÌ¤Î¬ÝªO */
+ /* argc == 2 Âà¬Y¯S©w¨Ï¥ÎªÌ */
+
+ if (argc > 2)
+ {
+ printf("Usage: %s [target_user]\n", argv[0]);
+ exit(-1);
+ }
+
+ chdir(BBSHOME);
+
+ if (argc == 2)
+ {
+ transmf(argv[1]);
+ exit(1);
+ }
+
+ /* Âà´«¨Ï¥ÎªÌ§Úªº³Ì·R */
+ for (c = 'a'; c <= 'z'; c++)
+ {
+ sprintf(buf, "usr/%c", c);
+
+ if (!(dirp = opendir(buf)))
+ continue;
+
+ while (de = readdir(dirp))
+ {
+ char *str;
+
+ str = de->d_name;
+ if (*str <= ' ' || *str == '.')
+ continue;
+
+ transmf(str);
+ }
+
+ closedir(dirp);
+ }
+ return 0;
+}
+
+#else
+int
+main()
+{
+ printf("You should define MY_FAVORITE first.\n");
+ return -1;
+}
+#endif /* MY_FAVORITE */
diff --git a/util/tran/wd2pal.c b/util/tran/wd2pal.c
new file mode 100644
index 0000000..c1f1d75
--- /dev/null
+++ b/util/tran/wd2pal.c
@@ -0,0 +1,213 @@
+/*-------------------------------------------------------*/
+/* util/transpal.c */
+/*-------------------------------------------------------*/
+/* target : WD ¦Ü Maple 3.02 (¬ÝªO)¦n¤Í¦W³æÂà´« */
+/* create : 01/09/08 */
+/* update : / / */
+/* author : itoc.bbs@bbs.ee.nctu.edu.tw */
+/*-------------------------------------------------------*/
+/* syntax : transpal */
+/*-------------------------------------------------------*/
+
+
+#if 0
+
+ 1. ­×§ï struct FRIEND ©M transpal() transbrdpal()
+ 2. Âà´«(¬ÝªO)¦n¤Í¦W³æ¤§«e¡A±z¥²¶·¥ýÂà´«§¹¬ÝªO¤Î¨Ï¥ÎªÌ¡C
+
+ ps. ¨Ï¥Î«e½Ð¥ý¦æ³Æ¥÷¡Ause on ur own risk. µ{¦¡©å¦H½Ð¥]²[ :p
+ ps. ·PÁ lkchu ªº Maple 3.02 for FreeBSD
+
+#endif
+
+
+#include "wd.h"
+
+
+/* ----------------------------------------------------- */
+/* 3.02 functions */
+/* ----------------------------------------------------- */
+
+
+static int
+acct_uno(userid)
+ char *userid;
+{
+ int fd;
+ int userno;
+ char fpath[64];
+
+ usr_fpath(fpath, userid, FN_ACCT);
+ fd = open(fpath, O_RDONLY);
+ if (fd >= 0)
+ {
+ read(fd, &userno, sizeof(userno));
+ close(fd);
+ return userno;
+ }
+ return -1;
+}
+
+
+/* ----------------------------------------------------- */
+/* Âà´«¥Dµ{¦¡ */
+/* ----------------------------------------------------- */
+
+
+static void
+transpal(userid)
+ char *userid;
+{
+ ACCT acct;
+ int fd, friend_userno;
+ char fpath[64], buf[64];
+ PAL pal;
+ FRIEND friend;
+
+ usr_fpath(fpath, userid, FN_PAL); /* ·sªº¦n¤Í¦W³æ */
+
+ /* sob ªº usr ¥Ø¿ý¦³¤À¤j¤p¼g¡A©Ò¥H­n¥ý¨ú±o¤j¤p¼g */
+ usr_fpath(buf, userid, FN_ACCT);
+ if ((fd = open(buf, O_RDONLY)) >= 0)
+ {
+ read(fd, &acct, sizeof(ACCT));
+ close(fd);
+ }
+ else
+ {
+ return;
+ }
+
+ sprintf(buf, OLD_BBSHOME "/home/%s/pal", acct.userid); /* ªº¦n¤Í¦W³æ */
+
+ if (dashf(fpath))
+ unlink(fpath); /* ²M±¼­««Ø */
+
+ if ((fd = open(buf, O_RDONLY)) < 0)
+ return;
+
+ while (read(fd, &friend, sizeof(FRIEND)) == sizeof(FRIEND))
+ {
+ if ((friend_userno = acct_uno(friend.userid)) >= 0 &&
+ strcmp(friend.userid, acct.userid))
+ {
+ memset(&pal, 0, sizeof(PAL));
+ str_ncpy(pal.userid, friend.userid, sizeof(pal.userid));
+ pal.ftype = (friend.savemode & 0x02) ? 0 : PAL_BAD; /* ¦n¤Í vs ·l¤Í */
+ str_ncpy(pal.ship, friend.desc, sizeof(pal.ship));
+ pal.userno = friend_userno;
+ rec_add(fpath, &pal, sizeof(PAL));
+ }
+ }
+ close(fd);
+}
+
+
+static void
+transbrdpal(brdname)
+ char *brdname;
+{
+ int pos, fd, friend_userno;
+ char fpath[64], buf[64];
+ PAL pal;
+ FRIEND friend;
+
+ brd_fpath(fpath, brdname, FN_PAL); /* ·sªº¦n¤Í¦W³æ */
+ sprintf(buf, OLD_BBSHOME "/boards/%s/.LIST", brdname);/* ªº¦n¤Í¦W³æ */
+
+ if (dashf(fpath))
+ unlink(fpath); /* ²M±¼­««Ø */
+
+ pos = 0;
+ if ((fd = open(buf, O_RDONLY)) < 0)
+ return;
+
+ while (fd)
+ {
+ lseek(fd, (off_t) (sizeof(FRIEND) * pos), SEEK_SET);
+ if (read(fd, &friend, sizeof(FRIEND)) == sizeof(FRIEND))
+ {
+ if ((friend_userno = acct_uno(friend.userid)) >= 0)
+ {
+ memset(&pal, 0, sizeof(PAL));
+ str_ncpy(pal.userid, friend.userid, sizeof(pal.userid));
+ pal.ftype = 0; /* ¬ÝªO¦n¤Í¤@©w¬O¦n¤Í */
+ str_ncpy(pal.ship, friend.desc, sizeof(pal.ship));
+ pal.userno = friend_userno;
+ rec_add(fpath, &pal, sizeof(PAL));
+ }
+ pos++;
+ }
+ else
+ {
+ close(fd);
+ break;
+ }
+ }
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ char c, *str;
+ char buf[64];
+ struct dirent *de;
+ DIR *dirp;
+
+ /* argc == 1 Âà¥þ³¡¨Ï¥ÎªÌ¤Î¬ÝªO */
+ /* argc == 2 Âà¬Y¯S©w¨Ï¥ÎªÌ */
+
+ if (argc > 2)
+ {
+ printf("Usage: %s [target_user]\n", argv[0]);
+ exit(-1);
+ }
+
+ chdir(BBSHOME);
+
+ if (argc == 2)
+ {
+ transpal(argv[1]);
+ exit(1);
+ }
+
+ /* Âà´«¨Ï¥ÎªÌ¦n¤Í¦W³æ */
+ for (c = 'a'; c <= 'z'; c++)
+ {
+ sprintf(buf, "usr/%c", c);
+
+ if (!(dirp = opendir(buf)))
+ continue;
+
+ while (de = readdir(dirp))
+ {
+ str = de->d_name;
+ if (*str <= ' ' || *str == '.')
+ continue;
+
+ transpal(str);
+ }
+
+ closedir(dirp);
+ }
+
+ /* Âà´«¬ÝªO¦n¤Í¦W³æ */
+ if (!(dirp = opendir("brd")))
+ return 0;
+
+ while (de = readdir(dirp))
+ {
+ str = de->d_name;
+ if (*str <= ' ' || *str == '.')
+ continue;
+
+ transbrdpal(str);
+ }
+
+ closedir(dirp);
+
+ return 0;
+}
diff --git a/util/tran/wd2pip.c b/util/tran/wd2pip.c
new file mode 100644
index 0000000..82041db
--- /dev/null
+++ b/util/tran/wd2pip.c
@@ -0,0 +1,498 @@
+/*-------------------------------------------------------*/
+/* util/transpip.c */
+/*-------------------------------------------------------*/
+/* target : WD ¦Ü Maple 3.02 ¤pÂû¸ê®ÆÂà´« */
+/* create : 02/01/26 */
+/* update : / / */
+/* author : itoc.bbs@bbs.ee.nctu.edu.tw */
+/*-------------------------------------------------------*/
+/* syntax : transpip */
+/*-------------------------------------------------------*/
+
+
+#if 0
+
+ 1. ­×§ï transpip()
+ 2. ¥uÂà chicken¡Achicken.bak* ´N¤£Âà¤F
+
+ ps. ¨Ï¥Î«e½Ð¥ý¦æ³Æ¥÷¡Ause on ur own risk. µ{¦¡©å¦H½Ð¥]²[ :p
+ ps. ·PÁ lkchu ªº Maple 3.02 for FreeBSD
+
+#endif
+
+
+#include "wd.h"
+
+#ifdef HAVE_GAME
+
+#include "../../pip/pipstruct.h" /* ¤Þ¤J·s¤pÂûªº°Ñ¼Æ³]©w */
+
+
+/* ----------------------------------------------------- */
+/* ¤pÂû°Ñ¼Æ³]©w */
+/* ----------------------------------------------------- */
+
+
+struct chicken
+{
+ /* ---°ò¥»ªº¸ê®Æ--- */
+ char name[20]; /* ©m ¦W */
+ char birth[21]; /* ¥Í ¤é */
+ int year; /* ¥Í¤é ¦~ */
+ int month; /* ¥Í¤é ¤ë */
+ int day; /* ¥Í¤é ¤é */
+ int sex; /* ©Ê §O 1:¡ñ 2:¡ð */
+ int death; /* 1: ¦º¤` 2:©ß±ó 3:µ²§½ */
+ int nodone; /* 1: ¥¼°µ */
+ int relation; /* ¨â¤HÃö«Y */
+ int liveagain; /* ´_¬¡¦¸¼Æ */
+ int dataB;
+ int dataC;
+ int dataD;
+ int dataE;
+
+ /* ---¨­Å骺°Ñ¼Æ--- */
+ int hp; /* Åé ¤O */
+ int maxhp; /* ³Ì¤jÅé¤O */
+ int weight; /* Åé ­« */
+ int tired; /* ¯h ³Ò «× */
+ int sick; /* ¯f ®ð */
+ int shit; /* ²M ¼ä «× */
+ int wrist; /* µÃ ¤O */
+ int bodyA;
+ int bodyB;
+ int bodyC;
+ int bodyD;
+ int bodyE;
+
+ /* ---µû»ùªº°Ñ¼Æ--- */
+ int social; /* ªÀ¥æµû»ù */
+ int family; /* ®a¨Æµû»ù */
+ int hexp; /* ¾Ô°«µû»ù */
+ int mexp; /* Å]ªkµû»ù */
+ int tmpA;
+ int tmpB;
+ int tmpC;
+ int tmpD;
+ int tmpE;
+
+ /* ---¾Ô°«¥Î°Ñ¼Æ--- */
+ int mp; /* ªk ¤O */
+ int maxmp; /* ³Ì¤jªk¤O */
+ int attack; /* §ð À» ¤O */
+ int resist; /* ¨¾ ¿m ¤O */
+ int speed; /* ³t «× */
+ int hskill; /* ¾Ô°«§Þ³N */
+ int mskill; /* Å]ªk§Þ³N */
+ int mresist; /* §ÜÅ]¯à¤O */
+ int magicmode; /* Å]ªk«¬ºA */
+ int fightB;
+ int fightC;
+ int fightD;
+ int fightE;
+
+
+ /* ---ªZ¾¹ªº°Ñ¼Æ--- */
+ int weaponhead; /* ÀY³¡ªZ¾¹ */
+ int weaponrhand; /* ¥k¤âªZ¾¹ */
+ int weaponlhand; /* ¥ª¤âªZ¾¹ */
+ int weaponbody; /* ¨­ÅéªZ¾¹ */
+ int weaponfoot; /* ¸}ªºªZ¾¹ */
+ int weaponA;
+ int weaponB;
+ int weaponC;
+ int weaponD;
+ int weaponE;
+
+ /* ---¦U¯à¤O°Ñ¼Æ--- */
+ int toman; /* «Ý¤H±µª« */
+ int character; /* ®ð ½è «× */
+ int love; /* ·R ¤ß */
+ int wisdom; /* ´¼ ¼z */
+ int art; /* ÃÀ³N¯à¤O */
+ int etchics; /* ¹D ¼w */
+ int brave; /* «i ´± */
+ int homework; /* ±½¦a¬~¦ç */
+ int charm; /* ¾y ¤O */
+ int manners; /* § »ö */
+ int speech; /* ½Í ¦R */
+ int cookskill; /* ²i ¶¹ */
+ int learnA;
+ int learnB;
+ int learnC;
+ int learnD;
+ int learnE;
+
+
+ /* ---¦Uª¬ºA¼Æ­È--- */
+ int happy; /* §Ö ¼Ö «× */
+ int satisfy; /* º¡ ·N «× */
+ int fallinlove; /* ÅÊ·R«ü¼Æ */
+ int belief; /* «H ¥õ */
+ int offense; /* ¸o Ä^ */
+ int affect; /* ·P ¨ü */
+ int stateA;
+ int stateB;
+ int stateC;
+ int stateD;
+ int stateE;
+
+ /* ---¦YªºªF¦è°Õ--- */
+ int food; /* ­¹ ª« */
+ int medicine; /* ÆF ªÛ */
+ int bighp; /* ¤j ¸É ¤Y */
+ int cookie; /* ¹s ­¹ */
+ int ginseng; /* ¤d¦~¤Hçx */
+ int snowgrass; /* ¤Ñ¤s³·½¬ */
+ int eatC;
+ int eatD;
+ int eatE;
+
+ /* ---¾Ö¦³ªºªF¦è--- */
+ int book; /* ®Ñ ¥» */
+ int playtool; /* ª± ¨ã */
+ int money; /* ª÷ ¿ú */
+ int thingA;
+ int thingB;
+ int thingC;
+ int thingD;
+ int thingE;
+
+ /* ---²q®±ªº°Ñ¼Æ--- */
+ int winn;
+ int losee;
+
+ /* ---°Ñ¨£¤ý¦Ú-- */
+ int royalA; /* from¦u½Ã */
+ int royalB; /* fromªñ½Ã */
+ int royalC; /* from±N­x */
+ int royalD; /* from¤j¦Ú */
+ int royalE; /* from²½¥q */
+ int royalF; /* fromÃd¦m */
+ int royalG; /* from¤ý¦m */
+ int royalH; /* from°ê¤ý */
+ int royalI; /* from¤p¤¡ */
+ int royalJ; /* from¤ý¤l */
+ int seeroyalJ; /* ¬O§_¤w¸g¬Ý¹L¤ý¤l¤F */
+ int seeA;
+ int seeB;
+ int seeC;
+ int seeD;
+ int seeE;
+
+ /* ---µ²§½---- */
+ int wantend; /* 20·³µ²§½ 1:¤£­n¥B¥¼±B 2:¤£­n¥B¤w±B 3:¤£­n¥B·í²Ä¤TªÌ 4:­n¥B¥¼±B 5:­n¥B¤w±B 6:­n¥B·í²Ä¤TªÌ */
+ int lover; /* ·R¤H 0:¨S¦³ 1:Å]¤ý 2:Às±Ú 3:A 4:B 5:C 6:D 7:E */
+
+ /* -------¤u§@¦¸¼Æ-------- */
+ int workA; /* ®a¨Æ */
+ int workB; /* «O©i */
+ int workC; /* ®È©± */
+ int workD; /* ¹A³õ */
+ int workE; /* À\ÆU */
+ int workF; /* ±Ð°ó */
+ int workG; /* ¦aÅu */
+ int workH; /* ¥ï¤ì */
+ int workI; /* ¬ü¾v */
+ int workJ; /* Ây¤H */
+ int workK; /* ¤u¦a */
+ int workL; /* ¦u¹Ó */
+ int workM; /* ®a±Ð */
+ int workN; /* °s®a */
+ int workO; /* °s©± */
+ int workP; /* ©]Á`·| */
+ int workQ;
+ int workR;
+ int workS;
+ int workT;
+ int workU;
+ int workV;
+ int workW;
+ int workX;
+ int workY;
+ int workZ;
+
+ /* -------¤W½Ò¦¸¼Æ-------- */
+ int classA;
+ int classB;
+ int classC;
+ int classD;
+ int classE;
+ int classF;
+ int classG;
+ int classH;
+ int classI;
+ int classJ;
+ int classK;
+ int classL;
+ int classM;
+ int classN;
+ int classO;
+
+ /* ---¤pÂûªº®É¶¡--- */
+ time_t bbtime;
+};
+typedef struct chicken chicken;
+
+
+/* ----------------------------------------------------- */
+/* Âà´«¥Dµ{¦¡ */
+/* ----------------------------------------------------- */
+
+
+static void
+transpip(userid)
+ char *userid;
+{
+ ACCT acct;
+ int fd;
+ char fpath[64];
+ FILE *fp;
+ struct chicken d; /* ¤pÂû */
+ struct CHICKEN p; /* ·s¤pÂû */
+
+
+ /* sob ªº usr ¥Ø¿ý¦³¤À¤j¤p¼g¡A©Ò¥H­n¥ý¨ú±o¤j¤p¼g */
+ usr_fpath(fpath, userid, FN_ACCT);
+ if ((fd = open(fpath, O_RDONLY)) >= 0)
+ {
+ read(fd, &acct, sizeof(ACCT));
+ close(fd);
+ }
+ sprintf(fpath, OLD_BBSHOME "/home/%s/chicken", acct.userid); /* ªº¤pÂû¸ê®Æ */
+
+ if (fp = fopen(fpath, "r"))
+ {
+ /* Ū¥X¤pÂû¸ê®Æ */
+
+ fgets(fpath, 20, fp);
+ d.bbtime = (time_t) atoi(fpath);
+
+ fscanf(fp,
+ "%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d "
+ "%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d "
+ "%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d "
+ "%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d "
+ "%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d "
+ "%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d "
+ "%d %s %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d "
+ "%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d "
+ "%d %d %d",
+ &(d.year), &(d.month), &(d.day), &(d.sex), &(d.death), &(d.nodone), &(d.relation), &(d.liveagain), &(d.dataB), &(d.dataC), &(d.dataD), &(d.dataE),
+ &(d.hp), &(d.maxhp), &(d.weight), &(d.tired), &(d.sick), &(d.shit), &(d.wrist), &(d.bodyA), &(d.bodyB), &(d.bodyC), &(d.bodyD), &(d.bodyE),
+ &(d.social), &(d.family), &(d.hexp), &(d.mexp), &(d.tmpA), &(d.tmpB), &(d.tmpC), &(d.tmpD), &(d.tmpE),
+ &(d.mp), &(d.maxmp), &(d.attack), &(d.resist), &(d.speed), &(d.hskill), &(d.mskill), &(d.mresist), &(d.magicmode), &(d.fightB), &(d.fightC), &(d.fightD), &(d.fightE),
+ &(d.weaponhead), &(d.weaponrhand), &(d.weaponlhand), &(d.weaponbody), &(d.weaponfoot), &(d.weaponA), &(d.weaponB), &(d.weaponC), &(d.weaponD), &(d.weaponE),
+ &(d.toman), &(d.character), &(d.love), &(d.wisdom), &(d.art), &(d.etchics), &(d.brave), &(d.homework), &(d.charm), &(d.manners), &(d.speech), &(d.cookskill), &(d.learnA), &(d.learnB), &(d.learnC), &(d.learnD), &(d.learnE),
+ &(d.happy), &(d.satisfy), &(d.fallinlove), &(d.belief), &(d.offense), &(d.affect), &(d.stateA), &(d.stateB), &(d.stateC), &(d.stateD), &(d.stateE),
+ &(d.food), &(d.medicine), &(d.bighp), &(d.cookie), &(d.ginseng), &(d.snowgrass), &(d.eatC), &(d.eatD), &(d.eatE),
+ &(d.book), &(d.playtool), &(d.money), &(d.thingA), &(d.thingB), &(d.thingC), &(d.thingD), &(d.thingE),
+ &(d.winn), &(d.losee),
+ &(d.royalA), &(d.royalB), &(d.royalC), &(d.royalD), &(d.royalE), &(d.royalF), &(d.royalG), &(d.royalH), &(d.royalI), &(d.royalJ), &(d.seeroyalJ), &(d.seeA), &(d.seeB), &(d.seeC), &(d.seeD), &(d.seeE),
+ &(d.wantend), &(d.lover), d.name,
+ &(d.classA), &(d.classB), &(d.classC), &(d.classD), &(d.classE),
+ &(d.classF), &(d.classG), &(d.classH), &(d.classI), &(d.classJ),
+ &(d.classK), &(d.classL), &(d.classM), &(d.classN), &(d.classO),
+ &(d.workA), &(d.workB), &(d.workC), &(d.workD), &(d.workE),
+ &(d.workF), &(d.workG), &(d.workH), &(d.workI), &(d.workJ),
+ &(d.workK), &(d.workL), &(d.workM), &(d.workN), &(d.workO),
+ &(d.workP), &(d.workQ), &(d.workR), &(d.workS), &(d.workT),
+ &(d.workU), &(d.workV), &(d.workW), &(d.workX), &(d.workY), &(d.workZ));
+
+ fclose(fp);
+
+ /* Âà´«¤pÂû¸ê®Æ */
+
+ memset(&p, 0, sizeof(p));
+
+ str_ncpy(p.name, d.name, sizeof(p.name));
+ sprintf(p.birth, "%02d/%02d/%02d", d.year % 100, d.month, d.day);
+
+ p.bbtime = d.bbtime;
+
+ p.year = d.year;
+ p.month = d.month;
+ p.day = d.day;
+ p.sex = d.sex;
+ p.death = d.death;
+ p.liveagain = d.liveagain;
+ p.wantend = d.wantend;
+ p.lover = d.lover;
+ p.seeroyalJ = d.seeroyalJ;
+ p.quest = 0; /* ¹q¤lÂû¨S¦³¥ô°È */
+
+ p.relation = d.relation;
+ p.happy = d.happy;
+ p.satisfy = p.satisfy;
+ p.fallinlove = d.fallinlove;
+ p.belief = d.belief;
+ p.sin = d.offense;
+ p.affect = d.affect;
+
+ p.weight = d.weight;
+ p.tired = d.tired;
+ p.sick = d.sick;
+ p.shit = d.shit;
+
+ p.social = d.social;
+ p.family = d.family;
+ p.hexp = d.hexp;
+ p.mexp = d.mexp;
+
+ p.toman = d.toman;
+ p.character = d.character;
+ p.love = d.love;
+ p.wisdom = d.wisdom;
+ p.art = d.art;
+ p.etchics = d.etchics;
+ p.brave = d.brave;
+ p.homework = d.homework;
+ p.charm = d.charm;
+ p.manners = d.manners;
+ p.speech = d.speech;
+ p.cook = d.cookskill;
+ p.attack = d.attack;
+ p.resist = d.resist;
+ p.speed = d.speed;
+ p.hskill = d.hskill;
+ p.mskill = d.mskill;
+ p.immune = d.mresist;
+
+ p.level = 1; /* ±q 1 ¯Å¶}©l */
+ p.exp = 0;
+ p.hp = d.hp;
+ p.maxhp = d.maxhp;
+ p.mp = d.mp;
+ p.maxmp = d.maxmp;
+ p.vp = d.hp; /* ¹q¤lÂû¨S¦³ vp/sp ®³ hp/mp ¨Ó®M */
+ p.maxvp = d.maxhp;
+ p.sp = d.mp;
+ p.maxsp = d.maxmp;
+
+ /* ¹q¤lÂû¨S¦³§Þ¯à¡A¹w³]¬° 0 */
+
+ /* ªZ¾¹³q³q­«¸m¬° 0¡A¥H§KªZ¾¹¦Cªí¤£¦P */
+
+ p.food = d.food;
+ p.cookie = d.cookie;
+ p.pill = 0;
+ p.medicine = d.medicine;
+ p.burger = d.bighp;
+ p.ginseng = d.ginseng;
+ p.paste = 0;
+ p.snowgrass = d.snowgrass;
+
+ p.money = d.money;
+ p.book = d.book;
+ p.toy = d.playtool;
+ p.playboy = 0;
+
+ p.royalA = d.royalA;
+ p.royalB = d.royalB;
+ p.royalC = d.royalC;
+ p.royalD = d.royalD;
+ p.royalE = d.royalE;
+ p.royalF = d.royalF;
+ p.royalG = d.royalG;
+ p.royalH = d.royalH;
+ p.royalI = d.royalI;
+ p.royalJ = d.royalJ;
+
+ p.workA = d.workA;
+ p.workB = d.workB;
+ p.workC = d.workC;
+ p.workD = d.workD;
+ p.workE = d.workE;
+ p.workF = d.workF;
+ p.workG = d.workG;
+ p.workH = d.workH;
+ p.workI = d.workI;
+ p.workJ = d.workJ;
+ p.workK = d.workK;
+ p.workL = d.workL;
+ p.workM = d.workM;
+ p.workN = d.workN;
+ p.workO = d.workO;
+ p.workP = d.workP;
+
+ p.winn = d.winn;
+ p.losee = d.losee;
+ p.classA = d.classA;
+ p.classB = d.classB;
+ p.classC = d.classC;
+ p.classD = d.classD;
+ p.classE = d.classE;
+ p.classF = d.classF;
+ p.classG = d.classG;
+ p.classH = d.classH;
+ p.classI = d.classI;
+ p.classJ = d.classJ;
+
+ /* ¼g¤J·s¤pÂû¸ê®Æ */
+
+ usr_fpath(fpath, acct.userid, "chicken"); /* ·sªº¤pÂû¸ê®Æ */
+ fd = open(fpath, O_WRONLY | O_CREAT, 0600);
+ write(fd, &p, sizeof(CHICKEN));
+ close(fd);
+ }
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ char c;
+ char buf[64];
+ struct dirent *de;
+ DIR *dirp;
+
+ /* argc == 1 Âà¥þ³¡¨Ï¥ÎªÌ */
+ /* argc == 2 Âà¬Y¯S©w¨Ï¥ÎªÌ */
+
+ if (argc > 2)
+ {
+ printf("Usage: %s [target_user]\n", argv[0]);
+ exit(-1);
+ }
+
+ chdir(BBSHOME);
+
+ if (argc == 2)
+ {
+ transpip(argv[1]);
+ exit(1);
+ }
+
+ /* Âà´«¨Ï¥ÎªÌ¤pÂû¸ê®Æ */
+ for (c = 'a'; c <= 'z'; c++)
+ {
+ sprintf(buf, "usr/%c", c);
+
+ if (!(dirp = opendir(buf)))
+ continue;
+
+ while (de = readdir(dirp))
+ {
+ char *str;
+
+ str = de->d_name;
+ if (*str <= ' ' || *str == '.')
+ continue;
+
+ transpip(str);
+ }
+
+ closedir(dirp);
+ }
+ return 0;
+}
+#else
+int
+main()
+{
+ printf("You should define HAVE_GAME first.\n");
+ return -1;
+}
+#endif /* HAVE_GAME */
diff --git a/util/tran/wd2usr.c b/util/tran/wd2usr.c
new file mode 100644
index 0000000..ea4143b
--- /dev/null
+++ b/util/tran/wd2usr.c
@@ -0,0 +1,597 @@
+/*-------------------------------------------------------*/
+/* util/transusr.c */
+/*-------------------------------------------------------*/
+/* target : WD ¦Ü Maple 3.02 ¨Ï¥ÎªÌÂà´« */
+/* .PASSWDS => .USR .ACCT */
+/* create : 98/06/14 */
+/* update : 02/01/05 */
+/* author : ernie@micro8.ee.nthu.edu.tw */
+/* modify : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#if 0
+
+ 1. ­×§ï struct userec ¤Î creat_dirs()
+ (userec ¨âª©©w¸qªº¦r¦êªø«×¤£¤@¡A½Ð¦Û¦æ´«¦¨¼Æ¦r)
+ 2. °£ plans ÀɦW¡A¦n¤Í¦W³æ¡B¼È¦sÀɵ¥³£¤£Âà´«
+ 3. Sob ¦³¤E­Óñ¦WÀÉ¡A¥uÂà«e¤T­Ó
+ 4. «H½c¤¤ªº internet mail ¦p¦³»Ý­n½Ð¥ý chmod 644 `find PATH -perm 600`
+
+ ps. ¨Ï¥Î«e½Ð¥ý¦æ³Æ¥÷¡Ause on ur own risk. µ{¦¡©å¦H½Ð¥]²[ :p
+ ps. ·PÁ lkchu ªº Maple 3.02 for FreeBSD
+
+#endif
+
+
+#include "wd.h"
+
+
+/* ----------------------------------------------------- */
+/* Âà´« .ACCT */
+/* ----------------------------------------------------- */
+
+
+static inline int
+is_bad_userid(userid)
+ char *userid;
+{
+ register char ch;
+
+ if (strlen(userid) < 2)
+ return 1;
+
+ if (!isalpha(*userid))
+ return 1;
+
+ if (!str_cmp(userid, "new"))
+ return 1;
+
+ while (ch = *(++userid))
+ {
+ if (!isalnum(ch))
+ return 1;
+ }
+ return 0;
+}
+
+
+static inline int
+uniq_userno(fd)
+ int fd;
+{
+ char buf[4096];
+ int userno, size;
+ SCHEMA *sp; /* record length 16 ¥i¾ã°£ 4096 */
+
+ userno = 1;
+
+ while ((size = read(fd, buf, sizeof(buf))) > 0)
+ {
+ sp = (SCHEMA *) buf;
+ do
+ {
+ if (sp->userid[0] == '\0')
+ {
+ lseek(fd, -size, SEEK_CUR);
+ return userno;
+ }
+ userno++;
+ size -= sizeof(SCHEMA);
+ sp++;
+ } while (size);
+ }
+
+ return userno;
+}
+
+
+#define LEVEL_BASIC 000000000001 /* °ò¥»Åv¤O */
+#define LEVEL_CHAT 000000000002 /* ¶i¤J²á¤Ñ«Ç */
+#define LEVEL_PAGE 000000000004 /* §ä¤H²á¤Ñ */
+#define LEVEL_POST 000000000010 /* µoªí¤å³¹ */
+#define LEVEL_LOGINOK 000000000020 /* µù¥Uµ{§Ç»{ÃÒ */
+#define LEVEL_MAILLIMIT 000000000040 /* «H¥óµL¤W­­ */
+#define LEVEL_CLOAK 000000000100 /* Áô¨­³N */
+#define LEVEL_SEECLOAK 000000000200 /* ¬Ý¨£§ÔªÌ */
+#define LEVEL_XEMPT 000000000400 /* ¥Ã¤[«O¯d±b¸¹ */
+#define LEVEL_BM 000000002000 /* ªO¥D */
+#define LEVEL_ACCOUNTS 000000004000 /* ±b¸¹Á`ºÞ */
+#define LEVEL_CHATROOM 000000010000 /* ²á¤Ñ«ÇÁ`ºÞ */
+#define LEVEL_BOARD 000000020000 /* ¬ÝªOÁ`ºÞ */
+#define LEVEL_SYSOP 000000040000 /* ¯¸ªø */
+
+
+static inline usint
+trans_acct_level(perm)
+ usint perm;
+{
+ usint userlevel;
+
+ userlevel = 0;
+
+ if (perm & LEVEL_BASIC)
+ userlevel |= PERM_BASIC;
+
+ if (perm & LEVEL_CHAT)
+ userlevel |= PERM_CHAT;
+
+ if (perm & LEVEL_PAGE)
+ userlevel |= PERM_PAGE;
+
+ if (perm & LEVEL_POST)
+ userlevel |= PERM_POST;
+
+ if (perm & LEVEL_LOGINOK)
+ userlevel |= PERM_VALID;
+
+ if (perm & LEVEL_MAILLIMIT)
+ userlevel |= PERM_MBOX;
+
+ if (perm & LEVEL_CLOAK)
+ userlevel |= PERM_CLOAK;
+
+ if (perm & LEVEL_SEECLOAK)
+ userlevel |= PERM_SEECLOAK;
+
+ if (perm & LEVEL_XEMPT)
+ userlevel |= PERM_XEMPT;
+
+ if (perm & LEVEL_BM)
+ userlevel |= PERM_BM;
+
+ if (perm & LEVEL_ACCOUNTS)
+ userlevel |= PERM_ACCOUNTS;
+
+ if (perm & LEVEL_CHATROOM)
+ userlevel |= PERM_CHATROOM;
+
+ if (perm & LEVEL_BOARD)
+ userlevel |= PERM_BOARD;
+
+ if (perm & LEVEL_SYSOP)
+ userlevel |= PERM_SYSOP;
+
+ return userlevel;
+}
+
+
+#define OLDUFO_MOVIE 000000000001 /* ¶}/Ãö°ÊºA¬ÝªO */
+#define OLDUFO_COLOR 000000000002 /* ±m¦â/¶Â¥Õ¤Á´« */
+#define OLDUFO_NOTE 000000000004 /* Åã¥Ü¯d¨¥ªO */
+#define OLDUFO_ALARM 000000000010 /* ¥bÂI³ø®É */
+#define OLDUFO_BELL 000000000020 /* Án­µ */
+#define OLDUFO_BOARDLIST 000000000040 /* ¬ÝªO¦CªíÅã¥Ü¤å³¹¼Æ©Î¬O½s¸¹ */
+#define OLDUFO_SEELOG 000000000100 /* ¤W¯¸³£¥h¬Ý¬ö¿ý±Æ¦W? */
+#define OLDUFO_CYCLE 000000000200 /* ´`Àô¦¡¾\Ū */
+#define OLDUFO_RPG 000000000400
+#define OLDUFO_FEELING 000000001000
+#define OLDUFO_FROM 000000002000
+#define OLDUFO_NOTEMONEY 000000004000
+#define OLDUFO_ALREADYSET 000000010000 /* ¨C¦¸¤W¯¸³£³]©w? */
+#define OLDUFO_BIG5GB 000000020000 /* use big5 code or gb */
+
+
+static inline usint
+trans_acct_ufo(oldufo)
+ usint oldufo;
+{
+ usint ufo;
+
+ ufo = UFO_DEFAULT_NEW;
+
+ if (oldufo & OLDUFO_MOVIE)
+ ufo |= UFO_MOVIE;
+ else
+ ufo &= ~UFO_MOVIE;
+
+ if (oldufo & OLDUFO_NOTE)
+ ufo &= ~UFO_MOTD;
+ else
+ ufo |= UFO_MOTD;
+
+ return ufo;
+}
+
+
+static inline void
+creat_dirs(old)
+ userec *old;
+{
+ ACCT new;
+ SCHEMA slot;
+ int fd;
+ char fpath[64];
+
+ memset(&new, 0, sizeof(new));
+ memset(&slot, 0, sizeof(slot));
+
+ str_ncpy(new.userid, old->userid, sizeof(new.userid));
+ str_ncpy(new.passwd, old->passwd, sizeof(new.passwd));
+ str_ncpy(new.realname, old->realname, sizeof(new.realname));
+ str_ncpy(new.username, old->username, sizeof(new.username));
+ new.userlevel = trans_acct_level(old->userlevel);
+ new.ufo = trans_acct_ufo(old->habit);
+ new.signature = 0;
+ new.year = old->year - 11; /* ¦è¤¸´«¦¨¥Á°ê */
+ new.month = old->month;
+ new.day = old->day;
+ new.sex = 1 - (old->sex % 2); /* (0)¸¯®æ (1)©j±µ (2)©³­} (3)¬ü¬Ü (4)Á¦¨û (5)ªü«¼ (6)´Óª« (7)Äqª« */
+ new.money = old->silvermoney;
+ new.gold = old->goldmoney;
+ new.numlogins = old->numlogins;
+ new.numposts = old->numposts;
+ new.numemails = 0;
+ new.firstlogin = old->firstlogin;
+ new.lastlogin = old->lastlogin;
+ new.tcheck = time(&new.tvalid);
+ str_ncpy(new.lasthost, old->lasthost, sizeof(new.lasthost));
+ str_ncpy(new.email, old->email, sizeof(new.email));
+
+ slot.uptime = time(0);
+ strcpy(slot.userid, new.userid);
+
+ fd = open(FN_SCHEMA, O_RDWR | O_CREAT, 0600);
+ new.userno = uniq_userno(fd);
+ write(fd, &slot, sizeof(slot));
+ close(fd);
+
+ usr_fpath(fpath, new.userid, NULL);
+ mkdir(fpath, 0700);
+ strcat(fpath, "/@");
+ mkdir(fpath, 0700);
+ usr_fpath(fpath, new.userid, "MF");
+ mkdir(fpath, 0700);
+ usr_fpath(fpath, new.userid, "gem"); /* itoc.010727: ­Ó¤HºëµØ°Ï */
+ mak_links(fpath);
+
+ usr_fpath(fpath, new.userid, ".ACCT");
+ fd = open(fpath, O_WRONLY | O_CREAT, 0600);
+ write(fd, &new, sizeof(ACCT));
+ close(fd);
+}
+
+
+/* ----------------------------------------------------- */
+/* Âà´«»{ÃÒ¸ê®Æ */
+/* ----------------------------------------------------- */
+
+
+static inline void
+trans_justify(old)
+ userec *old;
+{
+ char fpath[64];
+ FILE *fp;
+
+ usr_fpath(fpath, old->userid, FN_JUSTIFY);
+ if (fp = fopen(fpath, "a"))
+ {
+ fprintf(fp, "RPY: %s\n", old->justify); /* Âà´«¹w³]¥H email »{ÃÒ */
+ fclose(fp);
+ }
+}
+
+
+/* ----------------------------------------------------- */
+/* Âഫñ¦WÀÉ¡B­pµeÀÉ */
+/* ----------------------------------------------------- */
+
+
+static inline void
+trans_sig(old)
+ userec *old;
+{
+ int i;
+ char buf[64], fpath[64], f_sig[20];
+
+ for (i = 1; i <= 3; i++) /* Maple 3.0 ¥u¦³¤T­Óñ¦W */
+ {
+ sprintf(buf, OLD_BBSHOME "/home/%s/sig.%d", old->userid, i); /* ªºÃ±¦WÀÉ */
+ if (dashf(buf))
+ {
+ sprintf(f_sig, "%s.%d", FN_SIGN, i);
+ usr_fpath(fpath, old->userid, f_sig);
+ f_cp(buf, fpath, O_TRUNC);
+ }
+ }
+}
+
+
+static inline void
+trans_plans(old)
+ userec *old;
+{
+ char buf[64], fpath[64];
+
+ sprintf(buf, OLD_BBSHOME "/home/%s/plans", old->userid);
+ if (dashf(buf))
+ {
+ usr_fpath(fpath, old->userid, FN_PLANS);
+ f_cp(buf, fpath, O_TRUNC);
+ }
+}
+
+
+/* ----------------------------------------------------- */
+/* Âà´««H¥ó */
+/* ----------------------------------------------------- */
+
+
+static time_t
+trans_hdr_chrono(filename)
+ char *filename;
+{
+ char time_str[11];
+
+ /* M.1087654321.A ©Î M.987654321.A */
+ str_ncpy(time_str, filename + 2, filename[2] == '1' ? 11 : 10);
+
+ return (time_t) atoi(time_str);
+}
+
+
+static inline void
+trans_mail(old)
+ userec *old;
+{
+ int fd;
+ char index[64], folder[64], buf[64], fpath[64];
+ fileheader fh;
+ HDR hdr;
+ time_t chrono;
+
+ sprintf(index, OLD_BBSHOME "/home/%s/.DIR", old->userid);
+ usr_fpath(folder, old->userid, FN_DIR);
+
+ if ((fd = open(index, O_RDONLY)) >= 0)
+ {
+ while (read(fd, &fh, sizeof(fh)) == sizeof(fh))
+ {
+ sprintf(buf, OLD_BBSHOME "/home/%s/%s", old->userid, fh.filename);
+
+ if (dashf(buf)) /* ¤å³¹Àɮצb¤~°µÂà´« */
+ {
+ char new_name[10] = "@";
+
+ /* Âà´«¤å³¹ .DIR */
+ memset(&hdr, 0, sizeof(HDR));
+ chrono = trans_hdr_chrono(fh.filename);
+ new_name[1] = radix32[chrono & 31];
+ archiv32(chrono, new_name + 1);
+
+ hdr.chrono = chrono;
+ str_ncpy(hdr.xname, new_name, sizeof(hdr.xname));
+ str_ncpy(hdr.owner, strstr(fh.owner, "[³Æ.") ? "[³Æ§Ñ¿ý]" : fh.owner, sizeof(hdr.owner)); /* [³Æ.§Ñ.¿ý] */
+ str_ncpy(hdr.title, fh.title, sizeof(hdr.title));
+ str_stamp(hdr.date, &hdr.chrono);
+ hdr.xmode = (fh.filemode & 0x2) ? (MAIL_MARKED | MAIL_READ) : MAIL_READ; /* ³]¬°¤wŪ */
+
+ rec_add(folder, &hdr, sizeof(HDR));
+
+ /* «þ¨©ÀÉ®× */
+ usr_fpath(fpath, old->userid, "@/");
+ strcat(fpath, new_name);
+ f_cp(buf, fpath, O_TRUNC);
+ }
+ }
+ close(fd);
+ }
+}
+
+
+/* ----------------------------------------------------- */
+/* Âà´«­Ó¤HºëµØ°Ï */
+/* ----------------------------------------------------- */
+
+
+#ifdef HAVE_PERSONAL_GEM
+static void
+trans_man_stamp(folder, token, hdr, fpath, time)
+ char *folder;
+ int token;
+ HDR *hdr;
+ char *fpath;
+ time_t time;
+{
+ char *fname, *family;
+ int rc;
+
+ fname = fpath;
+ while (rc = *folder++)
+ {
+ *fname++ = rc;
+ if (rc == '/')
+ family = fname;
+ }
+ if (*family != '.')
+ {
+ fname = family;
+ family -= 2;
+ }
+ else
+ {
+ fname = family + 1;
+ *fname++ = '/';
+ }
+
+ *fname++ = token;
+
+ *family = radix32[time & 31];
+ archiv32(time, fname);
+
+ if (rc = open(fpath, O_WRONLY | O_CREAT | O_EXCL, 0600))
+ {
+ memset(hdr, 0, sizeof(HDR));
+ hdr->chrono = time;
+ str_stamp(hdr->date, &hdr->chrono);
+ strcpy(hdr->xname, --fname);
+ close(rc);
+ }
+ return;
+}
+
+
+static void
+transman(index, folder)
+ char *index, *folder;
+{
+ static int count = 100;
+
+ int fd;
+ char *ptr, buf[256], fpath[64];
+ fileheader fh;
+ HDR hdr;
+ time_t chrono;
+
+ if ((fd = open(index, O_RDONLY)) >= 0)
+ {
+ while (read(fd, &fh, sizeof(fh)) == sizeof(fh))
+ {
+ strcpy(buf, index);
+ ptr = strrchr(buf, '/') + 1;
+ strcpy(ptr, fh.filename);
+
+ if (*fh.filename == 'M' && dashf(buf)) /* ¥uÂà M.xxxx.A ¤Î D.xxxx.a */
+ {
+ /* Âà´«¤å³¹ .DIR */
+ memset(&hdr, 0, sizeof(HDR));
+ chrono = trans_hdr_chrono(fh.filename);
+ trans_man_stamp(folder, 'A', &hdr, fpath, chrono);
+ hdr.xmode = 0;
+ str_ncpy(hdr.owner, fh.owner, sizeof(hdr.owner));
+ str_ncpy(hdr.title, fh.title + 3, sizeof(hdr.title));
+ rec_add(folder, &hdr, sizeof(HDR));
+
+ /* «þ¨©ÀÉ®× */
+ f_cp(buf, fpath, O_TRUNC);
+ }
+ else if (*fh.filename == 'D' && dashd(buf))
+ {
+ char sub_index[256];
+
+ /* Âà´«¤å³¹ .DIR */
+ memset(&hdr, 0, sizeof(HDR));
+ chrono = ++count; /* WD ªº¥Ø¿ý©R¦W¤ñ¸û©_©Ç¡A¥u¦n¦Û¤vµ¹¼Æ¦r */
+ trans_man_stamp(folder, 'F', &hdr, fpath, chrono);
+ hdr.xmode = GEM_FOLDER;
+ str_ncpy(hdr.owner, fh.owner, sizeof(hdr.owner));
+ str_ncpy(hdr.title, fh.title + 3, sizeof(hdr.title));
+ rec_add(folder, &hdr, sizeof(HDR));
+
+ /* recursive ¶i¥hÂà´«¤l¥Ø¿ý */
+ strcpy(sub_index, buf);
+ ptr = strrchr(sub_index, '/') + 1;
+ sprintf(ptr, "%s/.DIR", fh.filename);
+ transman(sub_index, fpath);
+ }
+ }
+ close(fd);
+ }
+}
+#endif
+
+
+/* ----------------------------------------------------- */
+/* Âà´«¥Dµ{¦¡ */
+/* ----------------------------------------------------- */
+
+
+static void
+transusr(user)
+ userec *user;
+{
+ char buf[64];
+
+ printf("Âà´« %s ¨Ï¥ÎªÌ\n", user->userid);
+
+ if (is_bad_userid(user->userid))
+ {
+ printf("%s ¤£¬O¦Xªk ID\n", user->userid);
+ return;
+ }
+
+ usr_fpath(buf, user->userid, NULL);
+ if (dashd(buf))
+ {
+ printf("%s ¤w¸g¦³¦¹ ID\n", user->userid);
+ return;
+ }
+
+ sprintf(buf, OLD_BBSHOME "/home/%s", user->userid);
+ if (!dashd(buf))
+ {
+ printf("%s ªºÀɮפ£¦s¦b\n", user->userid);
+ return;
+ }
+
+ creat_dirs(user);
+ trans_justify(user);
+ trans_sig(user);
+ trans_plans(user);
+ trans_mail(user);
+
+
+#ifdef HAVE_PERSONAL_GEM
+ sprintf(buf, OLD_BBSHOME "/home/%s/man", user->userid);
+ if (dashd(buf))
+ {
+ char index[64], folder[64];
+
+ sprintf(index, "%s/.DIR", buf);
+ usr_fpath(folder, user->userid, "gem/" FN_DIR);
+ transman(index, folder);
+ }
+#endif
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int fd;
+ userec user;
+
+ /* argc == 1 Âà¥þ³¡¨Ï¥ÎªÌ */
+ /* argc == 2 Âà¬Y¯S©w¨Ï¥ÎªÌ */
+
+ if (argc > 2)
+ {
+ printf("Usage: %s [target_user]\n", argv[0]);
+ exit(-1);
+ }
+
+ chdir(BBSHOME);
+
+ if (!dashf(FN_PASSWD))
+ {
+ printf("ERROR! Can't open " FN_PASSWD "\n");
+ exit(-1);
+ }
+ if (!dashd(OLD_BBSHOME "/home"))
+ {
+ printf("ERROR! Can't open " OLD_BBSHOME "/home\n");
+ exit(-1);
+ }
+
+ if ((fd = open(FN_PASSWD, O_RDONLY)) >= 0)
+ {
+ while (read(fd, &user, sizeof(user)) == sizeof(user))
+ {
+ if (argc == 1)
+ {
+ transusr(&user);
+ }
+ else if (!strcmp(user.userid, argv[1]))
+ {
+ transusr(&user);
+ exit(1);
+ }
+ }
+ close(fd);
+ }
+
+ exit(0);
+}
diff --git a/util/tran/windtop.h b/util/tran/windtop.h
new file mode 100644
index 0000000..80f59de
--- /dev/null
+++ b/util/tran/windtop.h
@@ -0,0 +1,131 @@
+/*-------------------------------------------------------*/
+/* util/windtop.h */
+/*-------------------------------------------------------*/
+/* target : WindTop ¦Ü Maple Âà´« */
+/* create : 03/06/30 */
+/* update : / / */
+/* author : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#if 0
+
+ 0. «O¯d­ì¨Ó brd/ gem/ usr/ .USR¡A¨ä¾l´«¦¨·sª©ªº
+
+ 1. ³]©w FN_BOARD
+
+ 2. §Q¥Î windtop2brd Âà´« .BRD
+
+ 3. §Q¥Î windtop2usr Âà´« .ACCT
+
+ 4. §Q¥Î windtop2pip Âà´« chicken
+
+ 5. ±N·sª©ªº gem/@/ ¤Uªº³o¨ÇÀɮ׽ƻs¹L¨Ó
+ @apply @e-mail @goodbye @index @justify @newuser @opening.0
+ @opening.1 @opening.2 @post @re-reg @tryout @welcome
+
+ 6. ¤W BBS ¯¸¡A¦b (A)nnounce ¸Ì­±¡A«Ø¥H¤U¤G­Ó¨÷©vªº©Ò¦³¸ê®Æ
+ {¸ÜÃD} ¼öªù°Q½×
+ {±Æ¦æ} ²Î­p¸ê®Æ
+
+#endif
+
+
+#include "bbs.h"
+
+
+#define FN_BOARD "/tmp/.BRD" /* WindTop BBS ªº .BRD */
+
+
+/* ----------------------------------------------------- */
+/* old ACCT struct */
+/* ----------------------------------------------------- */
+
+
+typedef struct
+{
+ int userno; /* unique positive code */
+ char userid[13]; /* userid */
+ char passwd[14];; /* user password crypt by DES */
+ uschar signature; /* user signature number */
+ char realname[20]; /* user realname */
+ char username[24]; /* user nickname */
+ usint userlevel; /* user perm */
+ int numlogins; /* user login times */
+ int numposts; /* user post times */
+ usint ufo; /* user basic flags */
+ time_t firstlogin; /* user first login time */
+ time_t lastlogin; /* user last login time */
+ time_t staytime; /* user total stay time */
+ time_t tcheck; /* time to check mbox/pal */
+ char lasthost[32]; /* user last login remote host */
+ int numemail; /* ±Hµo Inetrnet E-mail ¦¸¼Æ */
+ time_t tvalid; /* ³q¹L»{ÃÒ¡B§ó§ï mail address ªº®É¶¡ */
+ char email[60]; /* user email */
+ char address[60]; /* user address */
+ char justify[60]; /* FROM of replied justify mail */
+ char vmail[60]; /* ³q¹L»{ÃÒ¤§ email */
+ time_t deny; /* user violatelaw time */
+ int request; /* ÂIºq¨t²Î */
+ usint ufo2; /* ©µ¦ùªº­Ó¤H³]©w */
+ char ident[108]; /* user remote host ident */
+ time_t vtime; /* validate time */
+} userec;
+
+
+/* ----------------------------------------------------- */
+/* old BRD struct */
+/* ----------------------------------------------------- */
+
+
+typedef struct
+{
+ char brdname[13]; /* board ID */
+ char title[43];
+ char color;
+ char class[5];
+ char BM[37]; /* BMs' uid, token '/' */
+
+ uschar bvote; /* ¦@¦³´X¶µ§ë²¼Á|¦æ¤¤ */
+
+ time_t bstamp; /* «Ø¥ß¬ÝªOªº®É¶¡, unique */
+ usint readlevel; /* ¾\ۤ峹ªºÅv­­ */
+ usint postlevel; /* µoªí¤å³¹ªºÅv­­ */
+ usint battr; /* ¬ÝªOÄÝ©Ê */
+ time_t btime; /* .DIR ªº st_mtime */
+ int bpost; /* ¦@¦³´X½g post */
+ time_t blast; /* ³Ì«á¤@½g post ªº®É¶¡ */
+ usint expiremax; /* Expire Max Post */
+ usint expiremin; /* Expire Min Post */
+ usint expireday; /* Expire old Post */
+ usint n_reads; /* ¬ÝªO¾\Ū²Ö­p times/hour */
+ usint n_posts; /* ¬ÝªOµoªí²Ö­p times/hour */
+ usint n_news; /* ¬ÝªOÂà«H²Ö­p times/hour */
+ usint n_bans; /* ¬ÝªOÀÉ«H²Ö­p times/hour */
+ char reserve[100]; /* «O¯d¥¼¥Î */
+} boardheader;
+
+
+/* ----------------------------------------------------- */
+/* old BRD battr */
+/* ----------------------------------------------------- */
+
+
+#define BATTR_NOZAP 0x0001 /* ¤£¥i zap */
+#define BATTR_NOTRAN 0x0002 /* ¤£Âà«H */
+#define BATTR_NOCOUNT 0x0004 /* ¤£­p¤å³¹µoªí½g¼Æ */
+#define BATTR_NOSTAT 0x0008 /* ¤£¯Ç¤J¼öªù¸ÜÃD²Î­p */
+#define BATTR_NOVOTE 0x0010 /* ¤£¤½§G§ë²¼µ²ªG©ó [sysop] ªO */
+#define BATTR_ANONYMOUS 0x0020 /* °Î¦W¬ÝªO */
+#define BATTR_NOFORWARD 0x0040 /* lkchu.981201: ¤£¥iÂà±H */
+#define BATTR_LOGEMAIL 0x0080 /* ¦Û°Êªþ¥[e-mail */
+#define BATTR_NOBAN 0x0100 /* ¤£¾×«H */
+#define BATTR_NOLOG 0x0200 /* ¤£¬ö¿ý¯¸¤º¹Hªk */
+#define BATTR_NOCNTCROSS 0x0400 /* ¤£¬ö¿ý cross post */
+#define BATTR_NOREPLY 0x0800 /* ¤£¯à¦^¤å³¹ */
+#define BATTR_NOLOGREAD 0x1000 /* ¤£¬ö¿ý¬Ýª©¾\Ū²v */
+#define BATTR_CHECKWATER 0x2000 /* ¬ö¿ýÄé¤ô¦¸¼Æ */
+#define BATTR_CHANGETITLE 0x4000 /* ª©¥D­×§ïª©¦W */
+#define BATTR_MODIFY 0x8000 /* ¨Ï¥ÎªÌ­×§ï¤å³¹ */
+#define BATTR_PRH 0x10000 /* ±ÀÂˤ峹 */
+#define BATTR_NOTOTAL 0x20000 /* ¤£²Î­p¬ÝªO¨Ï¥Î¬ö¿ý */
diff --git a/util/tran/windtop2brd.c b/util/tran/windtop2brd.c
new file mode 100644
index 0000000..6cdc951
--- /dev/null
+++ b/util/tran/windtop2brd.c
@@ -0,0 +1,88 @@
+/*-------------------------------------------------------*/
+/* util/wintop2brd.c */
+/*-------------------------------------------------------*/
+/* target : WindTop .BRD Âà´« */
+/* create : 03/06/30 */
+/* update : / / */
+/* author : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "windtop.h"
+
+
+static usint
+trans_battr(oldbattr)
+ usint oldbattr;
+{
+ usint battr;
+
+ battr = 0;
+
+ if (oldbattr & BATTR_NOZAP)
+ battr |= BRD_NOZAP;
+
+ if (oldbattr & BATTR_NOTRAN)
+ battr |= BRD_NOTRAN;
+
+ if (oldbattr & BATTR_NOCOUNT)
+ battr |= BRD_NOCOUNT;
+
+ if (oldbattr & BATTR_NOSTAT)
+ battr |= BRD_NOSTAT;
+
+ if (oldbattr & BATTR_NOVOTE)
+ battr |= BRD_NOVOTE;
+
+ if (oldbattr & BATTR_ANONYMOUS)
+ battr |= BRD_ANONYMOUS;
+
+ return battr;
+}
+
+
+int
+main()
+{
+ int fd;
+ BRD brd;
+ boardheader bh;
+ char buf[256];
+
+ chdir(BBSHOME);
+
+ unlink(FN_BRD);
+
+ if ((fd = open(FN_BOARD, O_RDONLY)) >= 0)
+ {
+ while (read(fd, &bh, sizeof(bh)) == sizeof(bh))
+ {
+ if (*bh.brdname)
+ {
+ /* Âà´« .BRD */
+ memset(&brd, 0, sizeof(BRD));
+
+ str_ncpy(brd.brdname, bh.brdname, sizeof(brd.brdname));
+ str_ncpy(brd.class, bh.class, sizeof(brd.class));
+ str_ncpy(brd.title, bh.title, sizeof(brd.title));
+ str_ncpy(brd.BM, bh.BM, sizeof(brd.BM));
+ brd.bvote = bh.bvote ? 1 : 0;
+ brd.bstamp = bh.bstamp;
+ brd.readlevel = bh.readlevel;
+ brd.postlevel = bh.postlevel;
+ brd.battr = trans_battr(bh.battr);
+
+ rec_add(FN_BRD, &brd, sizeof(BRD));
+
+ /* «Ø¥ß expire.conf */
+ if (bh.expireday && bh.expiremax && bh.expiremin)
+ {
+ sprintf(buf, "%s\t%d\t%d\t%d\n", brd.brdname, bh.expireday, bh.expiremax, bh.expiremin);
+ f_cat("etc/expire.conf", buf);
+ }
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/util/tran/windtop2pip.c b/util/tran/windtop2pip.c
new file mode 100644
index 0000000..0b32c0a
--- /dev/null
+++ b/util/tran/windtop2pip.c
@@ -0,0 +1,490 @@
+/*-------------------------------------------------------*/
+/* util/transpip.c */
+/*-------------------------------------------------------*/
+/* target : WD ¦Ü Maple 3.02 ¤pÂû¸ê®ÆÂà´« */
+/* create : 02/01/26 */
+/* update : / / */
+/* author : itoc.bbs@bbs.ee.nctu.edu.tw */
+/*-------------------------------------------------------*/
+/* syntax : transpip */
+/*-------------------------------------------------------*/
+
+
+#if 0
+
+ 1. ­×§ï transpip()
+ 2. ¥uÂà chicken¡Achicken.bak* ´N¤£Âà¤F
+
+ ps. ¨Ï¥Î«e½Ð¥ý¦æ³Æ¥÷¡Ause on ur own risk. µ{¦¡©å¦H½Ð¥]²[ :p
+ ps. ·PÁ lkchu ªº Maple 3.02 for FreeBSD
+
+#endif
+
+
+#include "windtop.h"
+
+#ifdef HAVE_GAME
+
+#include "../../pip/pipstruct.h" /* ¤Þ¤J·s¤pÂûªº°Ñ¼Æ³]©w */
+
+
+/* ----------------------------------------------------- */
+/* ¤pÂû°Ñ¼Æ³]©w */
+/* ----------------------------------------------------- */
+
+
+struct chicken
+{
+ /* ---°ò¥»ªº¸ê®Æ--- */
+ char name[20]; /* ©m ¦W */
+ char birth[21]; /* ¥Í ¤é */
+ int year; /* ¥Í¤é ¦~ */
+ int month; /* ¥Í¤é ¤ë */
+ int day; /* ¥Í¤é ¤é */
+ int sex; /* ©Ê §O 1:¡ñ 2:¡ð */
+ int death; /* 1: ¦º¤` 2:©ß±ó 3:µ²§½ */
+ int nodone; /* 1: ¥¼°µ */
+ int relation; /* ¨â¤HÃö«Y */
+ int liveagain; /* ´_¬¡¦¸¼Æ */
+ int dataB;
+ int dataC;
+ int dataD;
+ int dataE;
+
+ /* ---¨­Å骺°Ñ¼Æ--- */
+ int hp; /* Åé ¤O */
+ int maxhp; /* ³Ì¤jÅé¤O */
+ int weight; /* Åé ­« */
+ int tired; /* ¯h ³Ò «× */
+ int sick; /* ¯f ®ð */
+ int shit; /* ²M ¼ä «× */
+ int wrist; /* µÃ ¤O */
+ int bodyA;
+ int bodyB;
+ int bodyC;
+ int bodyD;
+ int bodyE;
+
+ /* ---µû»ùªº°Ñ¼Æ--- */
+ int social; /* ªÀ¥æµû»ù */
+ int family; /* ®a¨Æµû»ù */
+ int hexp; /* ¾Ô°«µû»ù */
+ int mexp; /* Å]ªkµû»ù */
+ int tmpA;
+ int tmpB;
+ int tmpC;
+ int tmpD;
+ int tmpE;
+
+ /* ---¾Ô°«¥Î°Ñ¼Æ--- */
+ int mp; /* ªk ¤O */
+ int maxmp; /* ³Ì¤jªk¤O */
+ int attack; /* §ð À» ¤O */
+ int resist; /* ¨¾ ¿m ¤O */
+ int speed; /* ³t «× */
+ int hskill; /* ¾Ô°«§Þ³N */
+ int mskill; /* Å]ªk§Þ³N */
+ int mresist; /* §ÜÅ]¯à¤O */
+ int magicmode; /* Å]ªk«¬ºA */
+ int fightB;
+ int fightC;
+ int fightD;
+ int fightE;
+
+
+ /* ---ªZ¾¹ªº°Ñ¼Æ--- */
+ int weaponhead; /* ÀY³¡ªZ¾¹ */
+ int weaponrhand; /* ¥k¤âªZ¾¹ */
+ int weaponlhand; /* ¥ª¤âªZ¾¹ */
+ int weaponbody; /* ¨­ÅéªZ¾¹ */
+ int weaponfoot; /* ¸}ªºªZ¾¹ */
+ int weaponA;
+ int weaponB;
+ int weaponC;
+ int weaponD;
+ int weaponE;
+
+ /* ---¦U¯à¤O°Ñ¼Æ--- */
+ int toman; /* «Ý¤H±µª« */
+ int character; /* ®ð ½è «× */
+ int love; /* ·R ¤ß */
+ int wisdom; /* ´¼ ¼z */
+ int art; /* ÃÀ³N¯à¤O */
+ int etchics; /* ¹D ¼w */
+ int brave; /* «i ´± */
+ int homework; /* ±½¦a¬~¦ç */
+ int charm; /* ¾y ¤O */
+ int manners; /* § »ö */
+ int speech; /* ½Í ¦R */
+ int cookskill; /* ²i ¶¹ */
+ int learnA;
+ int learnB;
+ int learnC;
+ int learnD;
+ int learnE;
+
+
+ /* ---¦Uª¬ºA¼Æ­È--- */
+ int happy; /* §Ö ¼Ö «× */
+ int satisfy; /* º¡ ·N «× */
+ int fallinlove; /* ÅÊ·R«ü¼Æ */
+ int belief; /* «H ¥õ */
+ int offense; /* ¸o Ä^ */
+ int affect; /* ·P ¨ü */
+ int stateA;
+ int stateB;
+ int stateC;
+ int stateD;
+ int stateE;
+
+ /* ---¦YªºªF¦è°Õ--- */
+ int food; /* ­¹ ª« */
+ int medicine; /* ÆF ªÛ */
+ int bighp; /* ¤j ¸É ¤Y */
+ int cookie; /* ¹s ­¹ */
+ int ginseng; /* ¤d¦~¤Hçx */
+ int snowgrass; /* ¤Ñ¤s³·½¬ */
+ int eatC;
+ int eatD;
+ int eatE;
+
+ /* ---¾Ö¦³ªºªF¦è--- */
+ int book; /* ®Ñ ¥» */
+ int playtool; /* ª± ¨ã */
+ int money; /* ª÷ ¿ú */
+ int thingA;
+ int thingB;
+ int thingC;
+ int thingD;
+ int thingE;
+
+ /* ---²q®±ªº°Ñ¼Æ--- */
+ int winn;
+ int losee;
+
+ /* ---°Ñ¨£¤ý¦Ú-- */
+ int royalA; /* from¦u½Ã */
+ int royalB; /* fromªñ½Ã */
+ int royalC; /* from±N­x */
+ int royalD; /* from¤j¦Ú */
+ int royalE; /* from²½¥q */
+ int royalF; /* fromÃd¦m */
+ int royalG; /* from¤ý¦m */
+ int royalH; /* from°ê¤ý */
+ int royalI; /* from¤p¤¡ */
+ int royalJ; /* from¤ý¤l */
+ int seeroyalJ; /* ¬O§_¤w¸g¬Ý¹L¤ý¤l¤F */
+ int seeA;
+ int seeB;
+ int seeC;
+ int seeD;
+ int seeE;
+
+ /* ---µ²§½---- */
+ int wantend; /* 20·³µ²§½ 1:¤£­n¥B¥¼±B 2:¤£­n¥B¤w±B 3:¤£­n¥B·í²Ä¤TªÌ 4:­n¥B¥¼±B 5:­n¥B¤w±B 6:­n¥B·í²Ä¤TªÌ */
+ int lover; /* ·R¤H 0:¨S¦³ 1:Å]¤ý 2:Às±Ú 3:A 4:B 5:C 6:D 7:E */
+
+ /* -------¤u§@¦¸¼Æ-------- */
+ int workA; /* ®a¨Æ */
+ int workB; /* «O©i */
+ int workC; /* ®È©± */
+ int workD; /* ¹A³õ */
+ int workE; /* À\ÆU */
+ int workF; /* ±Ð°ó */
+ int workG; /* ¦aÅu */
+ int workH; /* ¥ï¤ì */
+ int workI; /* ¬ü¾v */
+ int workJ; /* Ây¤H */
+ int workK; /* ¤u¦a */
+ int workL; /* ¦u¹Ó */
+ int workM; /* ®a±Ð */
+ int workN; /* °s®a */
+ int workO; /* °s©± */
+ int workP; /* ©]Á`·| */
+ int workQ;
+ int workR;
+ int workS;
+ int workT;
+ int workU;
+ int workV;
+ int workW;
+ int workX;
+ int workY;
+ int workZ;
+
+ /* -------¤W½Ò¦¸¼Æ-------- */
+ int classA;
+ int classB;
+ int classC;
+ int classD;
+ int classE;
+ int classF;
+ int classG;
+ int classH;
+ int classI;
+ int classJ;
+ int classK;
+ int classL;
+ int classM;
+ int classN;
+ int classO;
+
+ /* ---¤pÂûªº®É¶¡--- */
+ time_t bbtime;
+};
+typedef struct chicken chicken;
+
+
+/* ----------------------------------------------------- */
+/* Âà´«¥Dµ{¦¡ */
+/* ----------------------------------------------------- */
+
+
+static void
+transpip(userid)
+ char *userid;
+{
+ int fd;
+ char fpath[64], buf[20];
+ FILE *fp;
+ struct chicken d; /* ¤pÂû */
+ struct CHICKEN p; /* ·s¤pÂû */
+
+
+ usr_fpath(fpath, userid, "chicken");
+
+ if (fp = fopen(fpath, "r"))
+ {
+ /* Ū¥X¤pÂû¸ê®Æ */
+
+ fgets(buf, 20, fp);
+ d.bbtime = (time_t) atoi(buf);
+
+ fscanf(fp,
+ "%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d "
+ "%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d "
+ "%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d "
+ "%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d "
+ "%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d "
+ "%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d "
+ "%d %s %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d "
+ "%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d "
+ "%d %d %d",
+ &(d.year), &(d.month), &(d.day), &(d.sex), &(d.death), &(d.nodone), &(d.relation), &(d.liveagain), &(d.dataB), &(d.dataC), &(d.dataD), &(d.dataE),
+ &(d.hp), &(d.maxhp), &(d.weight), &(d.tired), &(d.sick), &(d.shit), &(d.wrist), &(d.bodyA), &(d.bodyB), &(d.bodyC), &(d.bodyD), &(d.bodyE),
+ &(d.social), &(d.family), &(d.hexp), &(d.mexp), &(d.tmpA), &(d.tmpB), &(d.tmpC), &(d.tmpD), &(d.tmpE),
+ &(d.mp), &(d.maxmp), &(d.attack), &(d.resist), &(d.speed), &(d.hskill), &(d.mskill), &(d.mresist), &(d.magicmode), &(d.fightB), &(d.fightC), &(d.fightD), &(d.fightE),
+ &(d.weaponhead), &(d.weaponrhand), &(d.weaponlhand), &(d.weaponbody), &(d.weaponfoot), &(d.weaponA), &(d.weaponB), &(d.weaponC), &(d.weaponD), &(d.weaponE),
+ &(d.toman), &(d.character), &(d.love), &(d.wisdom), &(d.art), &(d.etchics), &(d.brave), &(d.homework), &(d.charm), &(d.manners), &(d.speech), &(d.cookskill), &(d.learnA), &(d.learnB), &(d.learnC), &(d.learnD), &(d.learnE),
+ &(d.happy), &(d.satisfy), &(d.fallinlove), &(d.belief), &(d.offense), &(d.affect), &(d.stateA), &(d.stateB), &(d.stateC), &(d.stateD), &(d.stateE),
+ &(d.food), &(d.medicine), &(d.bighp), &(d.cookie), &(d.ginseng), &(d.snowgrass), &(d.eatC), &(d.eatD), &(d.eatE),
+ &(d.book), &(d.playtool), &(d.money), &(d.thingA), &(d.thingB), &(d.thingC), &(d.thingD), &(d.thingE),
+ &(d.winn), &(d.losee),
+ &(d.royalA), &(d.royalB), &(d.royalC), &(d.royalD), &(d.royalE), &(d.royalF), &(d.royalG), &(d.royalH), &(d.royalI), &(d.royalJ), &(d.seeroyalJ), &(d.seeA), &(d.seeB), &(d.seeC), &(d.seeD), &(d.seeE),
+ &(d.wantend), &(d.lover), d.name,
+ &(d.classA), &(d.classB), &(d.classC), &(d.classD), &(d.classE),
+ &(d.classF), &(d.classG), &(d.classH), &(d.classI), &(d.classJ),
+ &(d.classK), &(d.classL), &(d.classM), &(d.classN), &(d.classO),
+ &(d.workA), &(d.workB), &(d.workC), &(d.workD), &(d.workE),
+ &(d.workF), &(d.workG), &(d.workH), &(d.workI), &(d.workJ),
+ &(d.workK), &(d.workL), &(d.workM), &(d.workN), &(d.workO),
+ &(d.workP), &(d.workQ), &(d.workR), &(d.workS), &(d.workT),
+ &(d.workU), &(d.workV), &(d.workW), &(d.workX), &(d.workY), &(d.workZ));
+
+ fclose(fp);
+
+ /* Âà´«¤pÂû¸ê®Æ */
+
+ memset(&p, 0, sizeof(p));
+
+ str_ncpy(p.name, d.name, sizeof(p.name));
+ sprintf(p.birth, "%02d/%02d/%02d", d.year % 100, d.month, d.day);
+
+ p.bbtime = d.bbtime;
+
+ p.year = d.year;
+ p.month = d.month;
+ p.day = d.day;
+ p.sex = d.sex;
+ p.death = d.death;
+ p.liveagain = d.liveagain;
+ p.wantend = d.wantend;
+ p.lover = d.lover;
+ p.seeroyalJ = d.seeroyalJ;
+ p.quest = 0; /* ¹q¤lÂû¨S¦³¥ô°È */
+
+ p.relation = d.relation;
+ p.happy = d.happy;
+ p.satisfy = p.satisfy;
+ p.fallinlove = d.fallinlove;
+ p.belief = d.belief;
+ p.sin = d.offense;
+ p.affect = d.affect;
+
+ p.weight = d.weight;
+ p.tired = d.tired;
+ p.sick = d.sick;
+ p.shit = d.shit;
+
+ p.social = d.social;
+ p.family = d.family;
+ p.hexp = d.hexp;
+ p.mexp = d.mexp;
+
+ p.toman = d.toman;
+ p.character = d.character;
+ p.love = d.love;
+ p.wisdom = d.wisdom;
+ p.art = d.art;
+ p.etchics = d.etchics;
+ p.brave = d.brave;
+ p.homework = d.homework;
+ p.charm = d.charm;
+ p.manners = d.manners;
+ p.speech = d.speech;
+ p.cook = d.cookskill;
+ p.attack = d.attack;
+ p.resist = d.resist;
+ p.speed = d.speed;
+ p.hskill = d.hskill;
+ p.mskill = d.mskill;
+ p.immune = d.mresist;
+
+ p.level = 1; /* ±q 1 ¯Å¶}©l */
+ p.exp = 0;
+ p.hp = d.hp;
+ p.maxhp = d.maxhp;
+ p.mp = d.mp;
+ p.maxmp = d.maxmp;
+ p.vp = d.hp; /* ¹q¤lÂû¨S¦³ vp/sp ®³ hp/mp ¨Ó®M */
+ p.maxvp = d.maxhp;
+ p.sp = d.mp;
+ p.maxsp = d.maxmp;
+
+ /* ¹q¤lÂû¨S¦³§Þ¯à¡A¹w³]¬° 0 */
+
+ /* ªZ¾¹³q³q­«¸m¬° 0¡A¥H§KªZ¾¹¦Cªí¤£¦P */
+
+ p.food = d.food;
+ p.cookie = d.cookie;
+ p.pill = 0;
+ p.medicine = d.medicine;
+ p.burger = d.bighp;
+ p.ginseng = d.ginseng;
+ p.paste = 0;
+ p.snowgrass = d.snowgrass;
+
+ p.money = d.money;
+ p.book = d.book;
+ p.toy = d.playtool;
+ p.playboy = 0;
+
+ p.royalA = d.royalA;
+ p.royalB = d.royalB;
+ p.royalC = d.royalC;
+ p.royalD = d.royalD;
+ p.royalE = d.royalE;
+ p.royalF = d.royalF;
+ p.royalG = d.royalG;
+ p.royalH = d.royalH;
+ p.royalI = d.royalI;
+ p.royalJ = d.royalJ;
+
+ p.workA = d.workA;
+ p.workB = d.workB;
+ p.workC = d.workC;
+ p.workD = d.workD;
+ p.workE = d.workE;
+ p.workF = d.workF;
+ p.workG = d.workG;
+ p.workH = d.workH;
+ p.workI = d.workI;
+ p.workJ = d.workJ;
+ p.workK = d.workK;
+ p.workL = d.workL;
+ p.workM = d.workM;
+ p.workN = d.workN;
+ p.workO = d.workO;
+ p.workP = d.workP;
+
+ p.winn = d.winn;
+ p.losee = d.losee;
+ p.classA = d.classA;
+ p.classB = d.classB;
+ p.classC = d.classC;
+ p.classD = d.classD;
+ p.classE = d.classE;
+ p.classF = d.classF;
+ p.classG = d.classG;
+ p.classH = d.classH;
+ p.classI = d.classI;
+ p.classJ = d.classJ;
+
+ /* ¼g¤J·s¤pÂû¸ê®Æ */
+
+ unlink(fpath); /* ­««Ø­Ó·sªº */
+ fd = open(fpath, O_WRONLY | O_CREAT, 0600);
+ write(fd, &p, sizeof(CHICKEN));
+ close(fd);
+ }
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ char c;
+ char buf[64];
+ struct dirent *de;
+ DIR *dirp;
+
+ /* argc == 1 Âà¥þ³¡¨Ï¥ÎªÌ */
+ /* argc == 2 Âà¬Y¯S©w¨Ï¥ÎªÌ */
+
+ if (argc > 2)
+ {
+ printf("Usage: %s [target_user]\n", argv[0]);
+ exit(-1);
+ }
+
+ chdir(BBSHOME);
+
+ if (argc == 2)
+ {
+ transpip(argv[1]);
+ exit(1);
+ }
+
+ /* Âà´«¨Ï¥ÎªÌ¤pÂû¸ê®Æ */
+ for (c = 'a'; c <= 'z'; c++)
+ {
+ sprintf(buf, "usr/%c", c);
+
+ if (!(dirp = opendir(buf)))
+ continue;
+
+ while (de = readdir(dirp))
+ {
+ char *str;
+
+ str = de->d_name;
+ if (*str <= ' ' || *str == '.')
+ continue;
+
+ transpip(str);
+ }
+
+ closedir(dirp);
+ }
+ return 0;
+}
+#else
+int
+main()
+{
+ printf("You should define HAVE_GAME first.\n");
+ return -1;
+}
+#endif /* HAVE_GAME */
diff --git a/util/tran/windtop2usr.c b/util/tran/windtop2usr.c
new file mode 100644
index 0000000..a5830db
--- /dev/null
+++ b/util/tran/windtop2usr.c
@@ -0,0 +1,192 @@
+/*-------------------------------------------------------*/
+/* util/windtop2usr.c */
+/*-------------------------------------------------------*/
+/* target : WindTop .ACCT Âà´« */
+/* create : 03/06/30 */
+/* update : / / */
+/* author : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "windtop.h"
+
+#define MAK_DIRS /* «Ø¥Ø¿ý MF/ ¤Î gem/ */
+
+
+#define OLDUFO_PAGER BFLAG(5) /* Ãö³¬©I¥s¾¹ */
+#define OLDUFO_QUIET BFLAG(6) /* µ²Ãf¦b¤H¹Ò¡A¦ÓµL¨®°¨³Ù */
+#define OLDUFO_MAXMSG BFLAG(7) /* °T®§¤W­­©Ú¦¬°T®§ */
+#define OLDUFO_FORWARD BFLAG(8) /* ¦Û°ÊÂà±H */
+#define OLDUFO_CLASSTABLE BFLAG(9) /* ¥\½Òªí³qª¾ */
+#define OLDUFO_BROADCAST BFLAG(14) /* ©Ú¦¬¼s¼½ */
+#define OLDUFO_HIDEDN BFLAG(18) /* ÁôÂèӷ½ */
+#define OLDUFO_CLOAK BFLAG(19) /* true if cloak was ON */
+#define OLDUFO_WEB BFLAG(22) /* visor.020325: WEB */
+#define OLDUFO_MPAGER BFLAG(10) /* lkchu.990428: ¹q¤l¶l¥ó¶Ç©I */
+#define OLDUFO_MESSAGE BFLAG(23) /* visor.991030: °T®§¥þÃö */
+#define OLDUFO_PAGER1 BFLAG(26) /* visor.991030: ©I¥s¾¹¥þÃö */
+
+#define OLDUFO2_COLOR BFLAG(0) /* true if the ANSI color mode open */
+#define OLDUFO2_MOVIE BFLAG(1) /* true if show movie */
+#define OLDUFO2_BRDNEW BFLAG(2) /* ·s¤å³¹¼Ò¦¡ */
+#define OLDUFO2_BNOTE BFLAG(3) /* Åã¥Ü¶iªOµe­± */
+#define OLDUFO2_VEDIT BFLAG(4) /* ²¤Æ½s¿è¾¹ */
+#define OLDUFO2_PAL BFLAG(5) /* true if show pals only */
+#define OLDUFO2_MOTD BFLAG(6) /* ²¤Æ¶i¯¸µe­± */
+#define OLDUFO2_MIME BFLAG(7) /* MIME ¸Ñ½X */
+#define OLDUFO2_SIGN BFLAG(8) /* ñ¦WÀÉ */
+#define OLDUFO2_SHOWUSER BFLAG(9) /* Åã¥Ü ID ©M ¼ÊºÙ */
+#define OLDUFO2_PRH BFLAG(10) /* Åã¥Ü±ÀÂˤ峹¤À¼Æ */
+#define OLDUFO2_SHIP BFLAG(11) /* visor.991030: ¦n¤Í´y­z */
+#define OLDUFO2_NWLOG BFLAG(12) /* lkchu.990510: ¤£¦s¹ï¸Ü¬ö¿ý */
+#define OLDUFO2_NTLOG BFLAG(13) /* lkchu.990510: ¤£¦s²á¤Ñ¬ö¿ý */
+#define OLDUFO2_CIRCLE BFLAG(14) /* ´`Àô¾\Ū */
+#define OLDUFO2_ORIGUI BFLAG(15) /* Ãö³¬­·¤§¶ð¶W¬¯¤¶­± */
+#define OLDUFO2_DEF_ANONY BFLAG(16) /* ¹w³]¤£°Î¦W */
+#define OLDUFO2_DEF_LEAVE BFLAG(17) /* ¹w³]¤£Â÷¯¸ */
+#define OLDUFO2_ACL BFLAG(24) /* true if ACL was ON */
+#define OLDUFO2_REALNAME BFLAG(28) /* visor.991030: ¯u¹ê©m¦W */
+
+
+static usint
+trans_ufo(oldufo, oldufo2)
+ usint oldufo, oldufo2;
+{
+ usint ufo;
+
+ ufo = 0;
+
+ if (oldufo2 & OLDUFO2_MOVIE)
+ ufo |= UFO_MOVIE;
+
+ if (oldufo2 & OLDUFO2_BRDNEW)
+ ufo |= UFO_BRDPOST;
+
+ if (oldufo2 & OLDUFO2_BNOTE)
+ ufo |= UFO_BRDNOTE;
+
+ if (oldufo2 & OLDUFO2_VEDIT)
+ ufo |= UFO_VEDIT;
+
+ if (oldufo2 & OLDUFO2_MOTD)
+ ufo |= UFO_MOTD;
+
+ if (oldufo & OLDUFO_PAGER)
+ ufo |= UFO_PAGER;
+
+ if (oldufo & OLDUFO_BROADCAST)
+ ufo |= UFO_RCVER;
+
+ if (oldufo & OLDUFO_QUIET)
+ ufo |= UFO_QUIET;
+
+ if (oldufo2 & OLDUFO2_PAL)
+ ufo |= UFO_PAL;
+
+ ufo |= UFO_ALOHA; /* ¹w³] */
+
+ /* ufo |= UFO_BMWDISPLAY; */ /* ¹w³]¤£­n */
+
+ if (oldufo2 & OLDUFO2_NWLOG)
+ ufo |= UFO_NWLOG;
+
+ if (oldufo2 & OLDUFO2_NTLOG)
+ ufo |= UFO_NTLOG;
+
+ ufo |= UFO_NOSIGN; /* ¹w³] */
+
+ /* ufo |= UFO_SHOWSIGN; */ /* ¹w³]¤£­n */
+
+ if (oldufo & OLDUFO_CLOAK)
+ ufo |= UFO_CLOAK;
+
+ if (oldufo2 & OLDUFO2_ACL)
+ ufo |= UFO_ACL;
+
+ return ufo;
+}
+
+
+int
+main()
+{
+ ACCT new;
+ char c;
+
+ for (c = 'a'; c <= 'z'; c++)
+ {
+ char buf[64];
+ struct dirent *de;
+ DIR *dirp;
+
+ sprintf(buf, BBSHOME "/usr/%c", c);
+ chdir(buf);
+
+ if (!(dirp = opendir(".")))
+ continue;
+
+ while (de = readdir(dirp))
+ {
+ userec old;
+ int fd;
+ char *str;
+
+ str = de->d_name;
+ if (*str <= ' ' || *str == '.')
+ continue;
+
+#ifdef MAK_DIRS
+ sprintf(buf, "%s/MF", str);
+ mkdir(buf, 0700);
+ sprintf(buf, "%s/gem", str);
+ mak_links(buf);
+#endif
+
+ sprintf(buf, "%s/" FN_ACCT, str);
+ if ((fd = open(buf, O_RDONLY)) < 0)
+ continue;
+
+ read(fd, &old, sizeof(userec));
+ close(fd);
+ unlink(buf); /* itoc.010831: ¬å±¼­ì¨Óªº FN_ACCT */
+
+ memset(&new, 0, sizeof(ACCT));
+
+ new.userno = old.userno;
+
+ str_ncpy(new.userid, old.userid, sizeof(new.userid));
+ str_ncpy(new.passwd, old.passwd, sizeof(new.passwd));
+ str_ncpy(new.realname, old.realname, sizeof(new.realname));
+ str_ncpy(new.username, old.username, sizeof(new.username));
+
+ new.userlevel = old.userlevel;
+ new.ufo = trans_ufo(old.ufo, old.ufo2);
+ new.signature = old.signature;
+
+ new.year = 0;
+ new.month = 0;
+ new.day = 0;
+ new.sex = 1; /* µ¹ªì©l­È */
+ new.money = 100;
+ new.gold = 1;
+
+ new.numlogins = old.numlogins;
+ new.numposts = old.numposts;
+ new.numemails = old.numemail;
+
+ new.firstlogin = old.firstlogin;
+ new.lastlogin = old.lastlogin;
+ new.tcheck = old.tcheck;
+ new.tvalid = old.tvalid;
+
+ str_ncpy(new.lasthost, old.lasthost, sizeof(new.lasthost));
+ str_ncpy(new.email, old.email, sizeof(new.email));
+
+ fd = open(buf, O_WRONLY | O_CREAT, 0600); /* itoc.010831: ­««Ø·sªº FN_ACCT */
+ write(fd, &new, sizeof(ACCT));
+ close(fd);
+ }
+
+ closedir(dirp);
+ }
+}
diff --git a/util/umodestat.c b/util/umodestat.c
new file mode 100644
index 0000000..a7aaa89
--- /dev/null
+++ b/util/umodestat.c
@@ -0,0 +1,69 @@
+/*-------------------------------------------------------*/
+/* util/modestat.c ( NTHU CS MapleBBS Ver 3.00 ) */
+/*-------------------------------------------------------*/
+/* target : ²Î­p¨Ï¥ÎªÌ°ÊºA */
+/* create : 95/11/21 */
+/* update : 97/11/21 */
+/*-------------------------------------------------------*/
+/* syntax : modestat [log_filename] */
+/*-------------------------------------------------------*/
+
+
+#define _MODES_C_
+
+#include "bbs.h"
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+#ifdef MODE_STAT
+ char *fname;
+ FILE *fp;
+ UMODELOG mlog;
+ register int i, c;
+ char buf[80];
+ time_t sum;
+
+ if (argc < 2)
+ {
+ chdir(BBSHOME);
+ fname = FN_RUN_MODE_CUR;
+ }
+ else
+ fname = argv[1];
+
+ if (!(fp = fopen(fname, "rb")))
+ {
+ perror("Can't open file");
+ exit(1);
+ }
+
+ while (fread(&mlog, sizeof(UMODELOG), 1, fp))
+ {
+ for (i = c = 0, sum = 0; i <= M_MAX; i++)
+ {
+ struct tm *tt;
+
+ sum += mlog.used_time[i];
+ tt = localtime(&mlog.used_time[i]);
+ sprintf(buf + (c * 22), "%-12s%02d¤À%02d¬í ", ModeTypeTable[i], tt->tm_min, tt->tm_sec);
+
+ if (++c == 3)
+ {
+ printf(buf);
+ printf("\n");
+ c = 0;
+ }
+ }
+ printf("Á`¦@°±¯d®É¶¡: %s\n", Btime(&sum));
+ }
+
+
+ fclose(fp);
+#endif /* MODE_STAT */
+
+ exit(0);
+}
diff --git a/util/uno/0readme b/util/uno/0readme
new file mode 100644
index 0000000..f8eb919
--- /dev/null
+++ b/util/uno/0readme
@@ -0,0 +1,22 @@
+ ¥H¤U´X¤äµ{¦¡¬O®³¨Ó¸Ñ¨M Maple3 userno ­«ÂаÝÃDªº¡G
+
+ collect_uno ±q usr/_/* ©³¤U©Ò¦³ªº .ACCT ¦¬¶°¥þ¯¸ªº userno¡A°O¿ý©ó all_user_uno
+ conflict_uno ¥i¥H§â all_user_uno ªºµ²ªG²³æ¤ÀªR¡A¨q¥X­«ÂЪº userno
+ fix_uno ­«³]©Ò¦³¤Hªº userno¡A¨Ã­««Ø pal/list.?/aloha/frienz/benz
+
+ ¥¿½T¨Ï¥Îªº¶¶§Ç¡G
+
+ 1. ½Ð¥ý³Æ¥÷ usr/
+ 2. °õ¦æ«e¥ý½ð¤HÃö¯¸
+
+ 3. °õ¦æ collect_uno
+ 4. °õ¦æ conflict_uno
+ 5. ¥i¥H¬Ý¨ìµ²ªG¬O§_¦³½s¸¹­«ÂÐ
+
+ 6. ¦pªG¦³½s¸¹­«ÂЪº¸Ü¡A°õ¦æ fix_uno
+
+ 7. ¦A«×°õ¦æ collect_uno
+ 8. ¦A«×°õ¦æ conflict_uno
+ 9. ¬Ý¬Ý­«¾ã«áªºµ²ªG¬O§_ÁÙ¦³½s¸¹­«ÂÐ
+
+ 10. §¹¦¨¡A­«·s¶}¯¸
diff --git a/util/uno/Makefile b/util/uno/Makefile
new file mode 100644
index 0000000..b8141ae
--- /dev/null
+++ b/util/uno/Makefile
@@ -0,0 +1,59 @@
+# ------------------------------------------------------- #
+# util/uno/Makefile ( NTHU CS MapleBBS Ver 3.10 ) #
+# ------------------------------------------------------- #
+# target : Makefile for ±H«H¡B²Î­p¡B³Æ¥÷¡B¨t²ÎºûÅ@¤u¨ã #
+# create : 01/03/02 #
+# update : / / #
+# ------------------------------------------------------- #
+
+
+# ------------------------------------------------------ #
+# ¤U¦Cªº make rules ¤£»Ý­×§ï #
+# ------------------------------------------------------ #
+
+
+EXE = collect_uno conflict_uno fix_uno
+
+
+all:
+ @echo "Please enter 'make sys-type', "
+ @echo " make sun : for Sun-OS 4.x and maybe some BSD systems, cc or gcc"
+ @echo " make linux : for Linux"
+ @echo " make solaris : for Sun-OS 5.x gcc"
+ @echo " make sol-x86 : for Solaris 7 x86"
+ @echo " make freebsd : for BSD 4.4 systems"
+ @echo " make bsd : for BSD systems, cc or gcc, if not in the above lists"
+ @echo " make cygwin : for Microsoft Windows and Cygwin gcc"
+
+sun:
+ @$(MAKE) CC=gcc CFLAGS="-O2 -pipe -fomit-frame-pointer -Wunused -I../../include" LDFLAGS="-s -L../../lib -ldao" $(EXE)
+
+linux:
+ @$(MAKE) CC=gcc CFLAGS="-O2 -pipe -I../../include -fomit-frame-pointer -Wunused" LDFLAGS="-s -L../../lib -ldao" $(EXE)
+
+solaris:
+ @$(MAKE) CC=gcc CFLAGS="-O2 -pipe -I../../include -fomit-frame-pointer -Wunused" LDFLAGS="-s -L../../lib -ldao -lsocket -lnsl -L/usr/ucblib -lucb" $(EXE)
+
+sol-x86:
+ @$(MAKE) CC=gcc CFLAGS="-O2 -I../../include -fomit-frame-pointer -Wunused" LDFLAGS="-s -L../../lib -ldao -lsocket -lnsl" $(EXE)
+
+freebsd:
+ @$(MAKE) CC=gcc CFLAGS="-O2 -pipe -I../../include -fomit-frame-pointer -Wunused" LDFLAGS="-s -L../../lib -ldao" $(EXE)
+
+bsd:
+ @$(MAKE) CC=gcc CFLAGS="-O2 -pipe -I../../include -fomit-frame-pointer -Wunused" LDFLAGS="-s -L../../lib -ldao" $(EXE)
+
+cygwin:
+ @$(MAKE) CC=gcc CFLAGS="-O2 -pipe -I../../include -fomit-frame-pointer -Wunused" LDFLAGS="-s -L../../lib -ldao" $(EXE)
+
+
+.c: ; $(CC) -o $@ $@.c $(CFLAGS) $(LDFLAGS)
+
+
+install: $(EXE)
+# ¤£½Æ»s¨ì bin/ ¤U
+# install -m 0700 $? $(HOME)/bin
+ @echo "ok!"
+
+clean:
+ rm -f $(EXE) *.exe *.o *~
diff --git a/util/uno/collect_uno.c b/util/uno/collect_uno.c
new file mode 100644
index 0000000..4a15323
--- /dev/null
+++ b/util/uno/collect_uno.c
@@ -0,0 +1,77 @@
+/*-------------------------------------------------------*/
+/* util/collect_uno.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : ¦¬¶°¥þ¯¸©Ò¦³¤Hªº userno */
+/* create : 04/10/16 */
+/* update : / / */
+/* author : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+
+typedef struct
+{
+ int userno;
+ char userid[IDLEN + 1];
+} COLLECTION;
+
+
+static void
+collect_uno(userid)
+ char *userid;
+{
+ int fd;
+ char fpath[64];
+ ACCT acct;
+ COLLECTION collection;
+
+ usr_fpath(fpath, userid, FN_ACCT);
+ if ((fd = open(fpath, O_RDONLY)) >= 0)
+ {
+ read(fd, &acct, sizeof(ACCT));
+ close(fd);
+
+ memset(&collection, 0, sizeof(COLLECTION));
+ collection.userno = acct.userno;
+ strcpy(collection.userid, acct.userid);
+ rec_add("tmp/all_user_uno", &collection, sizeof(COLLECTION));
+ }
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ char c;
+ char *userid, fpath[64];
+ struct dirent *de;
+ DIR *dirp;
+
+ chdir(BBSHOME);
+
+ unlink("tmp/all_user_uno");
+
+ for (c = 'a'; c <= 'z'; c++)
+ {
+ sprintf(fpath, "usr/%c", c);
+
+ if (!(dirp = opendir(fpath)))
+ continue;
+
+ while (de = readdir(dirp))
+ {
+ userid = de->d_name;
+ if (*userid <= ' ' || *userid == '.')
+ continue;
+ collect_uno(userid);
+ }
+
+ closedir(dirp);
+ }
+
+ return 0;
+}
diff --git a/util/uno/conflict_uno.c b/util/uno/conflict_uno.c
new file mode 100644
index 0000000..2e59940
--- /dev/null
+++ b/util/uno/conflict_uno.c
@@ -0,0 +1,65 @@
+/*-------------------------------------------------------*/
+/* util/conflict_uno.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : ¤ÀªR all_user_uno ¬Ý¬Ý¬O§_¦³­«ÂЪº userno */
+/* create : 04/10/16 */
+/* update : / / */
+/* author : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+
+typedef struct
+{
+ int userno;
+ char userid[IDLEN + 1];
+} COLLECTION;
+
+
+static int
+cmp_collection(i, j)
+ COLLECTION *i, *j;
+{
+ return i->userno - j->userno;
+}
+
+
+int
+main()
+{
+ int fd, n;
+ COLLECTION *usr;
+ struct stat st;
+
+ chdir(BBSHOME);
+
+ if ((fd = open("tmp/all_user_uno", O_RDONLY)) < 0)
+ {
+ printf("±z¥²¶·¥ý°õ¦æ collect_uno\n");
+ return -1;
+ }
+
+ fstat(fd, &st);
+ n = st.st_size;
+ usr = (COLLECTION *) malloc(n);
+ read(fd, usr, n);
+ close(fd);
+
+ fd = n / sizeof(COLLECTION);
+ if (fd > 1)
+ {
+ qsort(usr, fd, sizeof(COLLECTION), cmp_collection);
+
+ for (n = 1; n < fd; n++)
+ {
+ if (usr[n].userno == usr[n - 1].userno)
+ printf("%d %s <====> %s\n", usr[n].userno, usr[n - 1].userid, usr[n].userid);
+ }
+ }
+
+ free(usr);
+
+ return 0;
+}
diff --git a/util/uno/fix_uno.c b/util/uno/fix_uno.c
new file mode 100644
index 0000000..67097c8
--- /dev/null
+++ b/util/uno/fix_uno.c
@@ -0,0 +1,520 @@
+/*-------------------------------------------------------*/
+/* util/fix_uno.c ( NTHU CS MapleBBS Ver 3.10 ) */
+/*-------------------------------------------------------*/
+/* target : ­««Ø©Ò¦³¨Ï¥ÎªÌªº userno */
+/* create : 04/10/16 */
+/* update : / / */
+/* author : itoc.bbs@bbs.tnfsh.tn.edu.tw */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+
+#define VERBOSE /* ¬O§_Åã¥Ü¸Ô²Ó°T®§ */
+
+#ifdef VERBOSE
+#define DEBUG(arg) printf arg
+#else
+#define DEBUG(arg) ;
+#endif
+
+
+#define FN_OLDACCT "olduserno" /* °O¿ý¥þ¯¸Âªº userno */
+
+
+typedef struct
+{
+ int userno;
+ char userid[IDLEN + 1];
+} UNO;
+
+
+/*-------------------------------------------------------*/
+/* ­««Ø .ACCT ¤Î .USR */
+/*-------------------------------------------------------*/
+
+
+static int
+new_acct(userid)
+ char *userid;
+{
+ static int userno = 1; /* userno ±q 1 ¶}©l */
+ static time_t now = 100000; /* ÀH«Kµ¹¤@­Ó®É¶¡ */
+
+ char fpath[64];
+ ACCT acct;
+ SCHEMA slot;
+ UNO uno;
+
+ usr_fpath(fpath, userid, FN_ACCT);
+ if (rec_get(fpath, &acct, sizeof(ACCT), 0) < 0)
+ {
+ /* ¦pªG§ä¤£¨ì .ACCT¡A­n§R°£©Ò¦³¦W³æ */
+ usr_fpath(fpath, userid, FN_PAL);
+ unlink(fpath);
+#ifdef HAVE_LIST
+ usr_fpath(fpath, userid, FN_LIST);
+ unlink(fpath);
+#endif
+#ifdef HAVE_ALOHA
+ usr_fpath(fpath, userid, FN_ALOHA);
+ unlink(fpath);
+#endif
+
+ DEBUG(("±Â¤© %s ·sªº userno ¥¢±Ñ => µLªkŪ¨ú¸Ó¨Ï¥ÎªÌªº¸ê®Æ\n", userid));
+ return;
+ }
+
+ /* ±N­ì¥»ªº userno ³Æ¥÷µ¹¤ô²y¦CªíÂà´«¨Ï¥Î */
+ memset(&uno, 0, sizeof(UNO));
+ uno.userno = acct.userno;
+ str_ncpy(uno.userid, acct.userid, sizeof(uno.userid));
+ rec_add(FN_OLDACCT, &uno, sizeof(UNO));
+
+ acct.userno = userno++;
+ unlink(fpath);
+ rec_add(fpath, &acct, sizeof(ACCT));
+
+ memset(&slot, 0, sizeof(SCHEMA));
+ slot.uptime = now++;
+ memcpy(slot.userid, acct.userid, IDLEN);
+ rec_add(FN_SCHEMA, &slot, sizeof(SCHEMA));
+
+ DEBUG(("±Â¤© %s ·sªº userno ¦¨¥\\\n", userid));
+}
+
+
+/*-------------------------------------------------------*/
+/* ¬d userno */
+/*-------------------------------------------------------*/
+
+
+static int new_num;
+static UNO *new_uno;
+
+
+static int
+uno_cmp_userid(a, b)
+ UNO *a, *b;
+{
+ return strcmp(a->userid, b->userid);
+}
+
+
+static void
+collect_new_uno()
+{
+ int fd, num;
+ SCHEMA slot;
+
+ if ((new_num = rec_num(FN_SCHEMA, sizeof(SCHEMA))) > 0)
+ {
+ new_uno = (UNO *) malloc(new_num * sizeof(UNO));
+
+ if ((fd = open(FN_SCHEMA, O_RDONLY)) >= 0)
+ {
+ num = 0;
+ while (read(fd, &slot, sizeof(SCHEMA)) == sizeof(SCHEMA) && num < new_num)
+ {
+ new_uno[num].userno = num + 1;
+ str_ncpy(new_uno[num].userid, slot.userid, sizeof(new_uno[num].userid)); /* slot.userid ¤£§t '\0' */
+ num++;
+ }
+ close(fd);
+ }
+
+ if (new_num > 1)
+ qsort(new_uno, new_num, sizeof(UNO), uno_cmp_userid);
+ }
+}
+
+
+static int
+acct_uno(userid) /* ¥Î ID §ä·sªº userno */
+ char *userid;
+{
+ UNO uno, *find;
+
+ str_ncpy(uno.userid, userid, sizeof(uno.userid)); /* ¨ä¹ê¥Î strcpy §Y¥i¡A¦ý¥H¨¾¸U¤@ */
+ if (find = bsearch(&uno, new_uno, new_num, sizeof(UNO), uno_cmp_userid))
+ return find->userno;
+ return 0;
+}
+
+
+static int old_num;
+static UNO *old_uno;
+
+
+static int
+uno_cmp_userno(a, b)
+ UNO *a, *b;
+{
+ return a->userno - b->userno;
+}
+
+
+static void
+collect_old_uno()
+{
+ int fsize;
+
+ if (old_uno = (UNO *) f_img(FN_OLDACCT, &fsize))
+ {
+ old_num = fsize / sizeof(UNO);
+ if (old_num > 1)
+ qsort(old_uno, old_num, sizeof(UNO), uno_cmp_userno);
+ }
+}
+
+
+static int
+acct_uno2(olduno) /* ¥Îªº userno §ä·sªº userno */
+ int olduno;
+{
+ UNO uno, *find;
+
+ uno.userno = olduno;
+ if (find = bsearch(&uno, old_uno, old_num, sizeof(UNO), uno_cmp_userno))
+ return acct_uno(find->userid);
+ return 0;
+}
+
+
+/*-------------------------------------------------------*/
+/* ­««Ø pal/list.?/aloha/benz/bpal */
+/*-------------------------------------------------------*/
+
+
+#define BENZ_MAX 512 /* °²³]¨C­Ó¤Hªº¨t²Î¨ó´M¤£¶W¹L 512 ¤H */
+
+static int rec_max;
+static char *rec_pool;
+
+
+static int
+new_pal(userid)
+ char *userid;
+{
+ int fd, num;
+ char folder[64];
+ PAL pal;
+
+ usr_fpath(folder, userid, FN_PAL);
+ if ((fd = open(folder, O_RDONLY)) >= 0)
+ {
+ num = 0;
+ while (read(fd, &pal, sizeof(PAL)) == sizeof(PAL) && num < rec_max)
+ {
+ if ((pal.userno = acct_uno(pal.userid)) > 0)
+ {
+ memcpy(rec_pool + num * sizeof(PAL), &pal, sizeof(PAL));
+ num++;
+ }
+ }
+ close(fd);
+
+ unlink(folder);
+ rec_add(folder, rec_pool, num * sizeof(PAL));
+ DEBUG(("¦¨¥\\­««Ø %s ªºªB¤Í¦W³æ¡A¦@ %d ¤H\n", userid, num));
+ }
+}
+
+
+#ifdef HAVE_LIST
+static int
+new_list(userid)
+ char *userid;
+{
+ int fd, ch, num;
+ char folder[64], fname[16];
+ PAL pal;
+
+ for (ch = '1'; ch <= '5'; ch++)
+ {
+ sprintf(fname, "%s.%c", FN_LIST, ch);
+ usr_fpath(folder, userid, fname);
+ if ((fd = open(folder, O_RDONLY)) >= 0)
+ {
+ num = 0;
+ while (read(fd, &pal, sizeof(PAL)) == sizeof(PAL) && num < rec_max)
+ {
+ if ((pal.userno = acct_uno(pal.userid)) > 0)
+ {
+ memcpy(rec_pool + num * sizeof(PAL), &pal, sizeof(PAL));
+ num++;
+ }
+ }
+ close(fd);
+
+ unlink(folder);
+ rec_add(folder, rec_pool, num * sizeof(PAL));
+ DEBUG(("¦¨¥\\­««Ø %s ªº¯S®í¦W³æ¡A¦@ %d ¤H\n", userid, num));
+ }
+ }
+}
+#endif
+
+
+#ifdef HAVE_ALOHA
+static int
+new_aloha(userid)
+ char *userid;
+{
+ int fd, num;
+ char folder[64];
+ ALOHA aloha;
+
+ FRIENZ frienz;
+ char fpath[64];
+
+ usr_fpath(folder, userid, FN_ALOHA);
+ if ((fd = open(folder, O_RDONLY)) >= 0)
+ {
+ /* ·Ç³Æ¦n­n¥[¤J¹ï¤èªº frienz */
+ memset(&frienz, 0, sizeof(FRIENZ));
+ strcpy(frienz.userid, userid);
+ if ((frienz.userno = acct_uno(userid)) > 0)
+ {
+ num = 0;
+ while (read(fd, &aloha, sizeof(ALOHA)) == sizeof(ALOHA) && num < rec_max)
+ {
+ if ((aloha.userno = acct_uno(aloha.userid)) > 0)
+ {
+ memcpy(rec_pool + num * sizeof(ALOHA), &aloha, sizeof(ALOHA));
+ num++;
+
+ /* §â¦Û¤v¥[¤J¹ï¤èªº frienz ¤¤ */
+ usr_fpath(fpath, aloha.userid, FN_FRIENZ);
+ rec_add(fpath, &frienz, sizeof(FRIENZ));
+ DEBUG(("¦¨¥\\­««Ø %s ªº¤W¯¸³qª¾¦W³æ¡A¦@ %d ¤H\n", userid, num));
+ }
+ }
+ }
+ close(fd);
+
+ unlink(folder);
+ rec_add(folder, rec_pool, num * sizeof(ALOHA));
+ }
+}
+#endif
+
+
+#ifdef LOGIN_NOTIFY
+static int
+new_benz(userid)
+ char *userid;
+{
+ int fd, num;
+ char folder[64];
+ BENZ benz;
+
+ usr_fpath(folder, userid, FN_BENZ);
+ if ((fd = open(folder, O_RDONLY)) >= 0)
+ {
+ num = 0;
+ while (read(fd, &benz, sizeof(BENZ)) == sizeof(BENZ) && num < rec_max)
+ {
+ if ((benz.userno = acct_uno(benz.userid)) > 0)
+ {
+ memcpy(rec_pool + num * sizeof(BENZ), &benz, sizeof(BENZ));
+ num++;
+ }
+ }
+ close(fd);
+
+ unlink(folder);
+ rec_add(folder, rec_pool, num * sizeof(BENZ));
+ DEBUG(("¦¨¥\\­««Ø %s ªº¨t²Î¨ó´M¦W³æ¡A¦@ %d ¤H\n", userid, num));
+ }
+}
+#endif
+
+
+static int
+new_bmw(userid)
+ char *userid;
+{
+ int fsize;
+ char folder[64];
+ BMW *data, *head, *tail;
+
+ usr_fpath(folder, userid, FN_BMW);
+ if (data = (BMW *) f_img(folder, &fsize))
+ {
+ head = data;
+ tail = data + (fsize / sizeof(BMW));
+ do
+ {
+ head->sender = acct_uno2(head->sender);
+ head->recver = acct_uno2(head->recver);
+ } while (++head < tail);
+
+ unlink(folder);
+ rec_add(folder, data, fsize);
+ free(data);
+
+ DEBUG(("¦¨¥\\­««Ø %s ªº¤ô²y¦Cªí¡A¦@ %d ­Ó\n", userid, fsize / sizeof(BMW)));
+ }
+}
+
+
+#ifdef HAVE_MODERATED_BOARD
+static int
+new_bpal(brdname)
+ char *brdname;
+{
+ int fd, num;
+ char folder[64];
+ PAL pal;
+
+ brd_fpath(folder, brdname, FN_PAL);
+ if ((fd = open(folder, O_RDONLY)) >= 0)
+ {
+ num = 0;
+ while (read(fd, &pal, sizeof(PAL)) == sizeof(PAL) && num < rec_max)
+ {
+ if ((pal.userno = acct_uno(pal.userid)) > 0)
+ {
+ memcpy(rec_pool + num * sizeof(PAL), &pal, sizeof(PAL));
+ num++;
+ }
+ }
+ close(fd);
+
+ unlink(folder);
+ rec_add(folder, rec_pool, num * sizeof(PAL));
+ DEBUG(("¦¨¥\\­««Ø %s ªºªO¤Í¦W³æ¡A¦@ %d ¤H\n", brdname, num));
+ }
+}
+#endif
+
+
+/*-------------------------------------------------------*/
+/* ¥D¨ç¦¡ */
+/*-------------------------------------------------------*/
+
+
+int
+main()
+{
+ char c;
+ char *userid, fpath[64];
+ struct dirent *de;
+ DIR *dirp;
+#ifdef HAVE_MODERATED_BOARD
+ FILE *fp;
+#endif
+
+ chdir(BBSHOME);
+
+ /* itoc.050113: ¥ýµe¤@¶ô°O¾ÐÅé¨Ó¦s¡A³Ì«á¦A¤@¦¸¼g¦^¡A¸`¬Ù I/O */
+ rec_max = PAL_MAX;
+#ifdef HAVE_ALOHA
+ if (rec_max < ALOHA_MAX)
+ rec_max = ALOHA_MAX;
+#endif
+#ifdef LOGIN_NOTIFY
+ if (rec_max < BENZ_MAX)
+ rec_max = BENZ_MAX;
+#endif
+ rec_pool = (char *) malloc(REC_SIZ * rec_max);
+
+ /*-----------------------------------------------------*/
+ /* ²Ä¤@°é: ±Â¤©·sªº userno¡A¨Ã§R°£ frienz */
+ /*-----------------------------------------------------*/
+
+ unlink(FN_SCHEMA);
+
+ for (c = 'a'; c <= 'z'; c++)
+ {
+ printf("±Â¤©·sªº userno: ¶}©l³B²z %c ¶}ÀYªº ID\n", c);
+ sprintf(fpath, "usr/%c", c);
+
+ if (!(dirp = opendir(fpath)))
+ continue;
+
+ while (de = readdir(dirp))
+ {
+ userid = de->d_name;
+ if (*userid <= ' ' || *userid == '.')
+ continue;
+
+ /* ±N .ACCT ´«·sªº userno¡A¨Ã¼g¦^ .USR */
+ new_acct(userid);
+
+#ifdef HAVE_ALOHA
+ /* §R°£ frienz */
+ usr_fpath(fpath, userid, FN_FRIENZ);
+ unlink(fpath);
+#endif
+ }
+
+ closedir(dirp);
+ }
+
+ collect_new_uno();
+ collect_old_uno();
+ printf("±Â¤©©Ò¦³¤H·sªº userno §¹¦¨¡A¥þ¯¸¦@ %d ¤H\n", new_num);
+
+
+ /*-----------------------------------------------------*/
+ /* ²Ä¤G°é: ­««Ø©Ò¦³¤Hªº pal/list.?/aloha/benz/bmw */
+ /*-----------------------------------------------------*/
+
+ for (c = 'a'; c <= 'z'; c++)
+ {
+ printf("­««Ø·sªº pal/list/aloha/benz: ¶}©l³B²z %c ¶}ÀYªº ID\n", c);
+ sprintf(fpath, "usr/%c", c);
+
+ if (!(dirp = opendir(fpath)))
+ continue;
+
+ while (de = readdir(dirp))
+ {
+ userid = de->d_name;
+ if (*userid <= ' ' || *userid == '.')
+ continue;
+
+ new_pal(userid);
+#ifdef HAVE_LIST
+ new_list(userid);
+#endif
+#ifdef HAVE_ALOHA
+ new_aloha(userid);
+#endif
+#ifdef LOGIN_NOTIFY
+ new_benz(userid);
+#endif
+ new_bmw(userid);
+ }
+
+ closedir(dirp);
+ }
+
+
+#ifdef HAVE_MODERATED_BOARD
+ /*-----------------------------------------------------*/
+ /* ²Ä¤T°é: ­««Ø©Ò¦³¬ÝªOªº bpal */
+ /*-----------------------------------------------------*/
+
+ printf("­««Ø·sªº bpal\n");
+ if (fp = fopen(FN_BRD, "r"))
+ {
+ BRD brd;
+ while (fread(&brd, sizeof(BRD), 1, fp) == 1)
+ {
+ if (*brd.brdname)
+ new_bpal(brd.brdname);
+ }
+ }
+#endif
+
+ free(rec_pool);
+ free(new_uno);
+ free(old_uno);
+ unlink(FN_OLDACCT);
+
+ return 0;
+}
diff --git a/util/webc.c b/util/webc.c
new file mode 100644
index 0000000..176e1ad
--- /dev/null
+++ b/util/webc.c
@@ -0,0 +1,137 @@
+/*-------------------------------------------------------*/
+/* util/webc.c ( NTHU CS MapleBBS Ver 3.00 ) */
+/*-------------------------------------------------------*/
+/* target : WEB client (command-line mode) */
+/* create : 95/03/29 */
+/* update : 97/03/29 */
+/*-------------------------------------------------------*/
+/* syntax : webc file host URL [port] */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
+
+static char pool[4096];
+
+
+static int
+webc(file, host, path, port)
+ char *file;
+ char *host;
+ char *path;
+ int port;
+{
+ int cc, sock, tlen;
+ FILE *fp;
+ char *xhead, *xtail, buf[120], tag[8];
+
+ sock = dns_open(host, port);
+ if (sock < 0)
+ return -1;
+
+ sprintf(buf, "GET %s\n\n", path);
+ cc = strlen(buf);
+ if (send(sock, buf, cc, 0) != cc)
+ {
+ close(sock);
+ return -1;
+ }
+
+ xhead = pool;
+ xtail = pool;
+ tlen = 0;
+
+ strcpy(buf, file);
+ strcat(buf, "-");
+ fp = fopen(buf, "w");
+
+ for (;;)
+ {
+ if (xhead >= xtail)
+ {
+ xhead = pool;
+ cc = recv(sock, xhead, sizeof(pool), 0);
+ if (cc <= 0)
+ break;
+ xtail = xhead + cc;
+ }
+
+ cc = *xhead++;
+ if (cc == '<')
+ {
+ tlen = 1;
+ continue;
+ }
+
+ if (tlen)
+ {
+ /* support <br> and <P> */
+
+ if (cc == '>')
+ {
+ if (tlen == 3 && !str_ncmp(tag, "br", 2))
+ {
+ fputc('\n', fp);
+ }
+ else if (tlen == 2 && !str_ncmp(tag, "P", 1))
+ {
+ fputc('\n', fp);
+ fputc('\n', fp);
+ }
+
+ tlen = 0;
+ continue;
+ }
+
+ if (tlen <= 2)
+ {
+ tag[tlen - 1] = cc;
+ }
+
+ tlen++;
+ continue;
+ }
+
+ if (cc != '\r')
+ fputc(cc, fp);
+ }
+
+ close(sock);
+
+ fputc('\n', fp);
+ fclose(fp);
+ rename(buf, file);
+ return cc;
+}
+
+
+/* ----------------------------------------------------- */
+/* main routines */
+/* ----------------------------------------------------- */
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ if (argc < 4 || argc > 5)
+ {
+ printf("Usage: %s file host URL [port]\n", argv[0]);
+ return -1;
+ }
+
+ close(0);
+ close(1);
+ close(2);
+
+ dns_init();
+
+ webc(argv[1], argv[2], argv[3], argc == 4 ? 80 : atoi(argv[4]));
+ return 0;
+}