aboutsummaryrefslogtreecommitdiffstats
path: root/FICS
diff options
context:
space:
mode:
Diffstat (limited to 'FICS')
-rw-r--r--FICS/.depend343
-rw-r--r--FICS/GNU_LICENSE339
-rw-r--r--FICS/Makedist6
-rw-r--r--FICS/Makefile50
-rw-r--r--FICS/Makefile.alpha33
-rw-r--r--FICS/Makefile.bak645
-rw-r--r--FICS/Makefile.bsd50
-rw-r--r--FICS/Makefile.clean50
-rw-r--r--FICS/Makefile.common67
-rw-r--r--FICS/Makefile.common.bak61
-rw-r--r--FICS/Makefile.in110
-rw-r--r--FICS/Makefile.in.orig110
-rw-r--r--FICS/Makefile.next28
-rw-r--r--FICS/Makefile.sgi40
-rw-r--r--FICS/Makefile.sun445
-rw-r--r--FICS/Makefile.sun540
-rw-r--r--FICS/Makefile.sunos-glibc51
-rw-r--r--FICS/Makefile.ultrix31
-rw-r--r--FICS/Makefile.usl29
-rw-r--r--FICS/README103
-rw-r--r--FICS/README-UPGRADE_FROM_1.2.313
-rw-r--r--FICS/README.NEW172
-rw-r--r--FICS/README.timeseal16
-rw-r--r--FICS/README_LEGAL23
-rw-r--r--FICS/README_Nash37
-rw-r--r--FICS/acconfig.h49
-rw-r--r--FICS/acconfig.h.orig49
-rw-r--r--FICS/adminproc.c1596
-rw-r--r--FICS/adminproc.h71
-rw-r--r--FICS/algcheck.c465
-rw-r--r--FICS/algcheck.h41
-rw-r--r--FICS/autoconfig.h171
-rw-r--r--FICS/board.c1001
-rw-r--r--FICS/board.h123
-rw-r--r--FICS/channel.c101
-rw-r--r--FICS/channel.h40
-rw-r--r--FICS/command.c1080
-rw-r--r--FICS/command.h140
-rw-r--r--FICS/command.h.orig135
-rw-r--r--FICS/command_list.h272
-rw-r--r--FICS/common.h56
-rw-r--r--FICS/comproc.c1638
-rw-r--r--FICS/comproc.c.orig1625
-rw-r--r--FICS/comproc.h74
-rw-r--r--FICS/comproc.old.c3394
-rw-r--r--FICS/config.h107
-rw-r--r--FICS/config.h.dist107
-rw-r--r--FICS/dfree.c226
-rw-r--r--FICS/eco.c428
-rw-r--r--FICS/eco.h12
-rw-r--r--FICS/fics_addplayer.c167
-rw-r--r--FICS/ficsmain.c183
-rw-r--r--FICS/ficsmain.c.orig171
-rw-r--r--FICS/ficsmain.h61
-rw-r--r--FICS/formula.c593
-rw-r--r--FICS/formula.h55
-rw-r--r--FICS/gamedb.c1795
-rw-r--r--FICS/gamedb.h209
-rw-r--r--FICS/gameproc.c1745
-rw-r--r--FICS/gameproc.c.orig2826
-rw-r--r--FICS/gameproc.h66
-rw-r--r--FICS/get_tcp_conn.c153
-rw-r--r--FICS/get_tcp_conn.h13
-rw-r--r--FICS/get_tcp_conn.proto.h4
-rw-r--r--FICS/gmon.outbin0 -> 319604 bytes
-rw-r--r--FICS/hashtbl.h97
-rw-r--r--FICS/legal.c38
-rw-r--r--FICS/legal.h31
-rw-r--r--FICS/lists.c470
-rw-r--r--FICS/lists.h44
-rw-r--r--FICS/makerank.c217
-rw-r--r--FICS/matchproc.c1081
-rw-r--r--FICS/matchproc.h35
-rw-r--r--FICS/memmove.c120
-rw-r--r--FICS/movecheck.c1297
-rw-r--r--FICS/movecheck.h61
-rw-r--r--FICS/multicol.c153
-rw-r--r--FICS/multicol.c.orig153
-rw-r--r--FICS/multicol.h41
-rw-r--r--FICS/network.c622
-rw-r--r--FICS/network.c.orig657
-rw-r--r--FICS/network.h89
-rw-r--r--FICS/obsproc.c1528
-rw-r--r--FICS/obsproc.h57
-rw-r--r--FICS/playerdb.c2542
-rw-r--r--FICS/playerdb.h289
-rw-r--r--FICS/rating_conv.c73
-rw-r--r--FICS/rating_conv.h11
-rw-r--r--FICS/ratings.c1512
-rw-r--r--FICS/ratings.c.orig1432
-rw-r--r--FICS/ratings.h80
-rw-r--r--FICS/rmalloc.c87
-rw-r--r--FICS/rmalloc.h38
-rw-r--r--FICS/shutdown.c262
-rw-r--r--FICS/shutdown.h14
-rw-r--r--FICS/stdinclude.h174
-rw-r--r--FICS/stdinclude.h.orig164
-rw-r--r--FICS/talkproc.c1247
-rw-r--r--FICS/talkproc.h56
-rw-r--r--FICS/utils.c1049
-rw-r--r--FICS/utils.c.orig991
-rw-r--r--FICS/utils.h116
-rw-r--r--FICS/utils.h.orig115
-rw-r--r--FICS/variable.c899
-rw-r--r--FICS/variable.h56
-rw-r--r--FICS/vers.h31
-rw-r--r--FICS/vers.h.orig31
107 files changed, 41594 insertions, 0 deletions
diff --git a/FICS/.depend b/FICS/.depend
new file mode 100644
index 0000000..98ae35d
--- /dev/null
+++ b/FICS/.depend
@@ -0,0 +1,343 @@
+adminproc.o : adminproc.c stdinclude.h /usr/include/sys/types.h /usr/include/machine/endian.h \
+ /usr/include/sys/cdefs.h /usr/include/machine/ansi.h /usr/include/machine/types.h \
+ /usr/include/strings.h /usr/include/string.h /usr/include/sys/dir.h /usr/include/dirent.h \
+ /usr/include/sys/dirent.h /usr/include/sys/stat.h /usr/include/sys/time.h /usr/include/time.h \
+ /usr/include/stdio.h /usr/include/stdarg.h /usr/include/machine/stdarg.h /usr/include/stdlib.h \
+ /usr/include/sys/socket.h /usr/include/netinet/in.h /usr/include/arpa/inet.h \
+ /usr/include/netdb.h /usr/include/unistd.h /usr/include/sys/unistd.h /usr/include/ctype.h \
+ /usr/include/sys/errno.h /usr/include/signal.h /usr/include/sys/signal.h /usr/include/machine/signal.h \
+ /usr/include/math.h /usr/include/sys/file.h /usr/include/sys/fcntl.h /usr/include/sys/ioctl.h \
+ /usr/include/sys/ttycom.h /usr/include/sys/ioccom.h /usr/include/sys/filio.h \
+ /usr/include/sys/sockio.h /usr/include/sys/wait.h common.h vers.h legal.h network.h \
+ command.h variable.h adminproc.h playerdb.h lists.h gamedb.h board.h gameproc.h \
+ obsproc.h ratings.h utils.h multicol.h rmalloc.h talkproc.h comproc.h /usr/include/sys/param.h \
+ /usr/include/sys/syslimits.h /usr/include/machine/param.h /usr/include/machine/limits.h
+algcheck.o : algcheck.c stdinclude.h /usr/include/sys/types.h /usr/include/machine/endian.h \
+ /usr/include/sys/cdefs.h /usr/include/machine/ansi.h /usr/include/machine/types.h \
+ /usr/include/strings.h /usr/include/string.h /usr/include/sys/dir.h /usr/include/dirent.h \
+ /usr/include/sys/dirent.h /usr/include/sys/stat.h /usr/include/sys/time.h /usr/include/time.h \
+ /usr/include/stdio.h /usr/include/stdarg.h /usr/include/machine/stdarg.h /usr/include/stdlib.h \
+ /usr/include/sys/socket.h /usr/include/netinet/in.h /usr/include/arpa/inet.h \
+ /usr/include/netdb.h /usr/include/unistd.h /usr/include/sys/unistd.h /usr/include/ctype.h \
+ /usr/include/sys/errno.h /usr/include/signal.h /usr/include/sys/signal.h /usr/include/machine/signal.h \
+ /usr/include/math.h /usr/include/sys/file.h /usr/include/sys/fcntl.h /usr/include/sys/ioctl.h \
+ /usr/include/sys/ttycom.h /usr/include/sys/ioccom.h /usr/include/sys/filio.h \
+ /usr/include/sys/sockio.h /usr/include/sys/wait.h common.h vers.h legal.h algcheck.h \
+ board.h movecheck.h utils.h multicol.h
+board.o : board.c stdinclude.h /usr/include/sys/types.h /usr/include/machine/endian.h \
+ /usr/include/sys/cdefs.h /usr/include/machine/ansi.h /usr/include/machine/types.h \
+ /usr/include/strings.h /usr/include/string.h /usr/include/sys/dir.h /usr/include/dirent.h \
+ /usr/include/sys/dirent.h /usr/include/sys/stat.h /usr/include/sys/time.h /usr/include/time.h \
+ /usr/include/stdio.h /usr/include/stdarg.h /usr/include/machine/stdarg.h /usr/include/stdlib.h \
+ /usr/include/sys/socket.h /usr/include/netinet/in.h /usr/include/arpa/inet.h \
+ /usr/include/netdb.h /usr/include/unistd.h /usr/include/sys/unistd.h /usr/include/ctype.h \
+ /usr/include/sys/errno.h /usr/include/signal.h /usr/include/sys/signal.h /usr/include/machine/signal.h \
+ /usr/include/math.h /usr/include/sys/file.h /usr/include/sys/fcntl.h /usr/include/sys/ioctl.h \
+ /usr/include/sys/ttycom.h /usr/include/sys/ioccom.h /usr/include/sys/filio.h \
+ /usr/include/sys/sockio.h /usr/include/sys/wait.h common.h vers.h legal.h board.h \
+ playerdb.h command.h variable.h lists.h gamedb.h utils.h multicol.h
+channel.o : channel.c stdinclude.h /usr/include/sys/types.h /usr/include/machine/endian.h \
+ /usr/include/sys/cdefs.h /usr/include/machine/ansi.h /usr/include/machine/types.h \
+ /usr/include/strings.h /usr/include/string.h /usr/include/sys/dir.h /usr/include/dirent.h \
+ /usr/include/sys/dirent.h /usr/include/sys/stat.h /usr/include/sys/time.h /usr/include/time.h \
+ /usr/include/stdio.h /usr/include/stdarg.h /usr/include/machine/stdarg.h /usr/include/stdlib.h \
+ /usr/include/sys/socket.h /usr/include/netinet/in.h /usr/include/arpa/inet.h \
+ /usr/include/netdb.h /usr/include/unistd.h /usr/include/sys/unistd.h /usr/include/ctype.h \
+ /usr/include/sys/errno.h /usr/include/signal.h /usr/include/sys/signal.h /usr/include/machine/signal.h \
+ /usr/include/math.h /usr/include/sys/file.h /usr/include/sys/fcntl.h /usr/include/sys/ioctl.h \
+ /usr/include/sys/ttycom.h /usr/include/sys/ioccom.h /usr/include/sys/filio.h \
+ /usr/include/sys/sockio.h /usr/include/sys/wait.h common.h vers.h legal.h channel.h \
+ network.h command.h variable.h rmalloc.h playerdb.h lists.h
+command.o : command.c stdinclude.h /usr/include/sys/types.h /usr/include/machine/endian.h \
+ /usr/include/sys/cdefs.h /usr/include/machine/ansi.h /usr/include/machine/types.h \
+ /usr/include/strings.h /usr/include/string.h /usr/include/sys/dir.h /usr/include/dirent.h \
+ /usr/include/sys/dirent.h /usr/include/sys/stat.h /usr/include/sys/time.h /usr/include/time.h \
+ /usr/include/stdio.h /usr/include/stdarg.h /usr/include/machine/stdarg.h /usr/include/stdlib.h \
+ /usr/include/sys/socket.h /usr/include/netinet/in.h /usr/include/arpa/inet.h \
+ /usr/include/netdb.h /usr/include/unistd.h /usr/include/sys/unistd.h /usr/include/ctype.h \
+ /usr/include/sys/errno.h /usr/include/signal.h /usr/include/sys/signal.h /usr/include/machine/signal.h \
+ /usr/include/math.h /usr/include/sys/file.h /usr/include/sys/fcntl.h /usr/include/sys/ioctl.h \
+ /usr/include/sys/ttycom.h /usr/include/sys/ioccom.h /usr/include/sys/filio.h \
+ /usr/include/sys/sockio.h /usr/include/sys/wait.h common.h vers.h legal.h rmalloc.h \
+ command.h variable.h command_list.h comproc.h matchproc.h talkproc.h lists.h \
+ gameproc.h obsproc.h adminproc.h playerdb.h ratings.h eco.h rating_conv.h shutdown.h \
+ movecheck.h board.h ficsmain.h config.h utils.h multicol.h gamedb.h network.h \
+ /usr/include/sys/param.h /usr/include/sys/syslimits.h /usr/include/machine/param.h \
+ /usr/include/machine/limits.h
+talkproc.o : talkproc.c stdinclude.h /usr/include/sys/types.h /usr/include/machine/endian.h \
+ /usr/include/sys/cdefs.h /usr/include/machine/ansi.h /usr/include/machine/types.h \
+ /usr/include/strings.h /usr/include/string.h /usr/include/sys/dir.h /usr/include/dirent.h \
+ /usr/include/sys/dirent.h /usr/include/sys/stat.h /usr/include/sys/time.h /usr/include/time.h \
+ /usr/include/stdio.h /usr/include/stdarg.h /usr/include/machine/stdarg.h /usr/include/stdlib.h \
+ /usr/include/sys/socket.h /usr/include/netinet/in.h /usr/include/arpa/inet.h \
+ /usr/include/netdb.h /usr/include/unistd.h /usr/include/sys/unistd.h /usr/include/ctype.h \
+ /usr/include/sys/errno.h /usr/include/signal.h /usr/include/sys/signal.h /usr/include/machine/signal.h \
+ /usr/include/math.h /usr/include/sys/file.h /usr/include/sys/fcntl.h /usr/include/sys/ioctl.h \
+ /usr/include/sys/ttycom.h /usr/include/sys/ioccom.h /usr/include/sys/filio.h \
+ /usr/include/sys/sockio.h /usr/include/sys/wait.h common.h vers.h legal.h talkproc.h \
+ comproc.h command.h variable.h utils.h multicol.h ficsmain.h config.h playerdb.h \
+ lists.h network.h rmalloc.h gamedb.h board.h gameproc.h obsproc.h formula.h \
+ /usr/include/sys/resource.h
+comproc.o : comproc.c stdinclude.h /usr/include/sys/types.h /usr/include/machine/endian.h \
+ /usr/include/sys/cdefs.h /usr/include/machine/ansi.h /usr/include/machine/types.h \
+ /usr/include/strings.h /usr/include/string.h /usr/include/sys/dir.h /usr/include/dirent.h \
+ /usr/include/sys/dirent.h /usr/include/sys/stat.h /usr/include/sys/time.h /usr/include/time.h \
+ /usr/include/stdio.h /usr/include/stdarg.h /usr/include/machine/stdarg.h /usr/include/stdlib.h \
+ /usr/include/sys/socket.h /usr/include/netinet/in.h /usr/include/arpa/inet.h \
+ /usr/include/netdb.h /usr/include/unistd.h /usr/include/sys/unistd.h /usr/include/ctype.h \
+ /usr/include/sys/errno.h /usr/include/signal.h /usr/include/sys/signal.h /usr/include/machine/signal.h \
+ /usr/include/math.h /usr/include/sys/file.h /usr/include/sys/fcntl.h /usr/include/sys/ioctl.h \
+ /usr/include/sys/ttycom.h /usr/include/sys/ioccom.h /usr/include/sys/filio.h \
+ /usr/include/sys/sockio.h /usr/include/sys/wait.h common.h vers.h legal.h talkproc.h \
+ comproc.h command.h variable.h utils.h multicol.h ficsmain.h config.h playerdb.h \
+ lists.h network.h rmalloc.h gamedb.h board.h gameproc.h obsproc.h ratings.h \
+ formula.h eco.h /usr/include/sys/resource.h
+matchproc.o : matchproc.c stdinclude.h /usr/include/sys/types.h /usr/include/machine/endian.h \
+ /usr/include/sys/cdefs.h /usr/include/machine/ansi.h /usr/include/machine/types.h \
+ /usr/include/strings.h /usr/include/string.h /usr/include/sys/dir.h /usr/include/dirent.h \
+ /usr/include/sys/dirent.h /usr/include/sys/stat.h /usr/include/sys/time.h /usr/include/time.h \
+ /usr/include/stdio.h /usr/include/stdarg.h /usr/include/machine/stdarg.h /usr/include/stdlib.h \
+ /usr/include/sys/socket.h /usr/include/netinet/in.h /usr/include/arpa/inet.h \
+ /usr/include/netdb.h /usr/include/unistd.h /usr/include/sys/unistd.h /usr/include/ctype.h \
+ /usr/include/sys/errno.h /usr/include/signal.h /usr/include/sys/signal.h /usr/include/machine/signal.h \
+ /usr/include/math.h /usr/include/sys/file.h /usr/include/sys/fcntl.h /usr/include/sys/ioctl.h \
+ /usr/include/sys/ttycom.h /usr/include/sys/ioccom.h /usr/include/sys/filio.h \
+ /usr/include/sys/sockio.h /usr/include/sys/wait.h common.h vers.h legal.h talkproc.h \
+ comproc.h command.h variable.h utils.h multicol.h ficsmain.h config.h playerdb.h \
+ lists.h network.h rmalloc.h gamedb.h board.h gameproc.h obsproc.h ratings.h \
+ formula.h eco.h /usr/include/sys/resource.h
+fics_addplayer.o : fics_addplayer.c stdinclude.h /usr/include/sys/types.h /usr/include/machine/endian.h \
+ /usr/include/sys/cdefs.h /usr/include/machine/ansi.h /usr/include/machine/types.h \
+ /usr/include/strings.h /usr/include/string.h /usr/include/sys/dir.h /usr/include/dirent.h \
+ /usr/include/sys/dirent.h /usr/include/sys/stat.h /usr/include/sys/time.h /usr/include/time.h \
+ /usr/include/stdio.h /usr/include/stdarg.h /usr/include/machine/stdarg.h /usr/include/stdlib.h \
+ /usr/include/sys/socket.h /usr/include/netinet/in.h /usr/include/arpa/inet.h \
+ /usr/include/netdb.h /usr/include/unistd.h /usr/include/sys/unistd.h /usr/include/ctype.h \
+ /usr/include/sys/errno.h /usr/include/signal.h /usr/include/sys/signal.h /usr/include/machine/signal.h \
+ /usr/include/math.h /usr/include/sys/file.h /usr/include/sys/fcntl.h /usr/include/sys/ioctl.h \
+ /usr/include/sys/ttycom.h /usr/include/sys/ioccom.h /usr/include/sys/filio.h \
+ /usr/include/sys/sockio.h /usr/include/sys/wait.h common.h vers.h legal.h utils.h \
+ multicol.h playerdb.h command.h variable.h lists.h
+ficsmain.o : ficsmain.c stdinclude.h /usr/include/sys/types.h /usr/include/machine/endian.h \
+ /usr/include/sys/cdefs.h /usr/include/machine/ansi.h /usr/include/machine/types.h \
+ /usr/include/strings.h /usr/include/string.h /usr/include/sys/dir.h /usr/include/dirent.h \
+ /usr/include/sys/dirent.h /usr/include/sys/stat.h /usr/include/sys/time.h /usr/include/time.h \
+ /usr/include/stdio.h /usr/include/stdarg.h /usr/include/machine/stdarg.h /usr/include/stdlib.h \
+ /usr/include/sys/socket.h /usr/include/netinet/in.h /usr/include/arpa/inet.h \
+ /usr/include/netdb.h /usr/include/unistd.h /usr/include/sys/unistd.h /usr/include/ctype.h \
+ /usr/include/sys/errno.h /usr/include/signal.h /usr/include/sys/signal.h /usr/include/machine/signal.h \
+ /usr/include/math.h /usr/include/sys/file.h /usr/include/sys/fcntl.h /usr/include/sys/ioctl.h \
+ /usr/include/sys/ttycom.h /usr/include/sys/ioccom.h /usr/include/sys/filio.h \
+ /usr/include/sys/sockio.h /usr/include/sys/wait.h common.h vers.h legal.h ficsmain.h \
+ config.h network.h command.h variable.h playerdb.h lists.h ratings.h utils.h \
+ multicol.h board.h talkproc.h comproc.h shutdown.h eco.h /usr/include/sys/param.h \
+ /usr/include/sys/syslimits.h /usr/include/machine/param.h /usr/include/machine/limits.h
+formula.o : formula.c /usr/include/ctype.h /usr/include/sys/cdefs.h config.h \
+ formula.h playerdb.h command.h variable.h stdinclude.h /usr/include/sys/types.h \
+ /usr/include/machine/endian.h /usr/include/machine/ansi.h /usr/include/machine/types.h \
+ /usr/include/strings.h /usr/include/string.h /usr/include/sys/dir.h /usr/include/dirent.h \
+ /usr/include/sys/dirent.h /usr/include/sys/stat.h /usr/include/sys/time.h /usr/include/time.h \
+ /usr/include/stdio.h /usr/include/stdarg.h /usr/include/machine/stdarg.h /usr/include/stdlib.h \
+ /usr/include/sys/socket.h /usr/include/netinet/in.h /usr/include/arpa/inet.h \
+ /usr/include/netdb.h /usr/include/unistd.h /usr/include/sys/unistd.h /usr/include/sys/errno.h \
+ /usr/include/signal.h /usr/include/sys/signal.h /usr/include/machine/signal.h \
+ /usr/include/math.h /usr/include/sys/file.h /usr/include/sys/fcntl.h /usr/include/sys/ioctl.h \
+ /usr/include/sys/ttycom.h /usr/include/sys/ioccom.h /usr/include/sys/filio.h \
+ /usr/include/sys/sockio.h /usr/include/sys/wait.h lists.h gamedb.h board.h common.h \
+ vers.h legal.h utils.h multicol.h rmalloc.h network.h ratings.h
+gamedb.o : gamedb.c stdinclude.h /usr/include/sys/types.h /usr/include/machine/endian.h \
+ /usr/include/sys/cdefs.h /usr/include/machine/ansi.h /usr/include/machine/types.h \
+ /usr/include/strings.h /usr/include/string.h /usr/include/sys/dir.h /usr/include/dirent.h \
+ /usr/include/sys/dirent.h /usr/include/sys/stat.h /usr/include/sys/time.h /usr/include/time.h \
+ /usr/include/stdio.h /usr/include/stdarg.h /usr/include/machine/stdarg.h /usr/include/stdlib.h \
+ /usr/include/sys/socket.h /usr/include/netinet/in.h /usr/include/arpa/inet.h \
+ /usr/include/netdb.h /usr/include/unistd.h /usr/include/sys/unistd.h /usr/include/ctype.h \
+ /usr/include/sys/errno.h /usr/include/signal.h /usr/include/sys/signal.h /usr/include/machine/signal.h \
+ /usr/include/math.h /usr/include/sys/file.h /usr/include/sys/fcntl.h /usr/include/sys/ioctl.h \
+ /usr/include/sys/ttycom.h /usr/include/sys/ioccom.h /usr/include/sys/filio.h \
+ /usr/include/sys/sockio.h /usr/include/sys/wait.h common.h vers.h legal.h ficsmain.h \
+ config.h gamedb.h board.h playerdb.h command.h variable.h lists.h gameproc.h \
+ utils.h multicol.h rmalloc.h eco.h network.h
+gameproc.o : gameproc.c stdinclude.h /usr/include/sys/types.h /usr/include/machine/endian.h \
+ /usr/include/sys/cdefs.h /usr/include/machine/ansi.h /usr/include/machine/types.h \
+ /usr/include/strings.h /usr/include/string.h /usr/include/sys/dir.h /usr/include/dirent.h \
+ /usr/include/sys/dirent.h /usr/include/sys/stat.h /usr/include/sys/time.h /usr/include/time.h \
+ /usr/include/stdio.h /usr/include/stdarg.h /usr/include/machine/stdarg.h /usr/include/stdlib.h \
+ /usr/include/sys/socket.h /usr/include/netinet/in.h /usr/include/arpa/inet.h \
+ /usr/include/netdb.h /usr/include/unistd.h /usr/include/sys/unistd.h /usr/include/ctype.h \
+ /usr/include/sys/errno.h /usr/include/signal.h /usr/include/sys/signal.h /usr/include/machine/signal.h \
+ /usr/include/math.h /usr/include/sys/file.h /usr/include/sys/fcntl.h /usr/include/sys/ioctl.h \
+ /usr/include/sys/ttycom.h /usr/include/sys/ioccom.h /usr/include/sys/filio.h \
+ /usr/include/sys/sockio.h /usr/include/sys/wait.h common.h vers.h legal.h command.h \
+ variable.h ficsmain.h config.h playerdb.h lists.h gamedb.h board.h gameproc.h \
+ obsproc.h movecheck.h utils.h multicol.h ratings.h rmalloc.h comproc.h matchproc.h \
+ eco.h network.h
+obsproc.o : obsproc.c stdinclude.h /usr/include/sys/types.h /usr/include/machine/endian.h \
+ /usr/include/sys/cdefs.h /usr/include/machine/ansi.h /usr/include/machine/types.h \
+ /usr/include/strings.h /usr/include/string.h /usr/include/sys/dir.h /usr/include/dirent.h \
+ /usr/include/sys/dirent.h /usr/include/sys/stat.h /usr/include/sys/time.h /usr/include/time.h \
+ /usr/include/stdio.h /usr/include/stdarg.h /usr/include/machine/stdarg.h /usr/include/stdlib.h \
+ /usr/include/sys/socket.h /usr/include/netinet/in.h /usr/include/arpa/inet.h \
+ /usr/include/netdb.h /usr/include/unistd.h /usr/include/sys/unistd.h /usr/include/ctype.h \
+ /usr/include/sys/errno.h /usr/include/signal.h /usr/include/sys/signal.h /usr/include/machine/signal.h \
+ /usr/include/math.h /usr/include/sys/file.h /usr/include/sys/fcntl.h /usr/include/sys/ioctl.h \
+ /usr/include/sys/ttycom.h /usr/include/sys/ioccom.h /usr/include/sys/filio.h \
+ /usr/include/sys/sockio.h /usr/include/sys/wait.h common.h vers.h legal.h command.h \
+ variable.h ficsmain.h config.h playerdb.h lists.h gamedb.h board.h gameproc.h \
+ obsproc.h movecheck.h utils.h multicol.h ratings.h rmalloc.h comproc.h matchproc.h \
+ formula.h eco.h network.h
+legal.o : legal.c
+lists.o : lists.c lists.h /usr/include/stdio.h /usr/include/sys/types.h /usr/include/machine/endian.h \
+ /usr/include/sys/cdefs.h /usr/include/machine/ansi.h /usr/include/machine/types.h \
+ common.h vers.h legal.h multicol.h command.h variable.h stdinclude.h /usr/include/strings.h \
+ /usr/include/string.h /usr/include/sys/dir.h /usr/include/dirent.h /usr/include/sys/dirent.h \
+ /usr/include/sys/stat.h /usr/include/sys/time.h /usr/include/time.h /usr/include/stdarg.h \
+ /usr/include/machine/stdarg.h /usr/include/stdlib.h /usr/include/sys/socket.h \
+ /usr/include/netinet/in.h /usr/include/arpa/inet.h /usr/include/netdb.h /usr/include/unistd.h \
+ /usr/include/sys/unistd.h /usr/include/ctype.h /usr/include/sys/errno.h /usr/include/signal.h \
+ /usr/include/sys/signal.h /usr/include/machine/signal.h /usr/include/math.h \
+ /usr/include/sys/file.h /usr/include/sys/fcntl.h /usr/include/sys/ioctl.h /usr/include/sys/ttycom.h \
+ /usr/include/sys/ioccom.h /usr/include/sys/filio.h /usr/include/sys/sockio.h \
+ /usr/include/sys/wait.h utils.h playerdb.h ratings.h rmalloc.h talkproc.h gamedb.h \
+ board.h comproc.h
+makerank.o : makerank.c /usr/include/stdio.h /usr/include/sys/types.h /usr/include/machine/endian.h \
+ /usr/include/sys/cdefs.h /usr/include/machine/ansi.h /usr/include/machine/types.h \
+ /usr/include/stdlib.h /usr/include/string.h /usr/include/ctype.h config.h
+movecheck.o : movecheck.c stdinclude.h /usr/include/sys/types.h /usr/include/machine/endian.h \
+ /usr/include/sys/cdefs.h /usr/include/machine/ansi.h /usr/include/machine/types.h \
+ /usr/include/strings.h /usr/include/string.h /usr/include/sys/dir.h /usr/include/dirent.h \
+ /usr/include/sys/dirent.h /usr/include/sys/stat.h /usr/include/sys/time.h /usr/include/time.h \
+ /usr/include/stdio.h /usr/include/stdarg.h /usr/include/machine/stdarg.h /usr/include/stdlib.h \
+ /usr/include/sys/socket.h /usr/include/netinet/in.h /usr/include/arpa/inet.h \
+ /usr/include/netdb.h /usr/include/unistd.h /usr/include/sys/unistd.h /usr/include/ctype.h \
+ /usr/include/sys/errno.h /usr/include/signal.h /usr/include/sys/signal.h /usr/include/machine/signal.h \
+ /usr/include/math.h /usr/include/sys/file.h /usr/include/sys/fcntl.h /usr/include/sys/ioctl.h \
+ /usr/include/sys/ttycom.h /usr/include/sys/ioccom.h /usr/include/sys/filio.h \
+ /usr/include/sys/sockio.h /usr/include/sys/wait.h common.h vers.h legal.h movecheck.h \
+ board.h algcheck.h gamedb.h utils.h multicol.h network.h command.h variable.h \
+ playerdb.h lists.h
+multicol.o : multicol.c stdinclude.h /usr/include/sys/types.h /usr/include/machine/endian.h \
+ /usr/include/sys/cdefs.h /usr/include/machine/ansi.h /usr/include/machine/types.h \
+ /usr/include/strings.h /usr/include/string.h /usr/include/sys/dir.h /usr/include/dirent.h \
+ /usr/include/sys/dirent.h /usr/include/sys/stat.h /usr/include/sys/time.h /usr/include/time.h \
+ /usr/include/stdio.h /usr/include/stdarg.h /usr/include/machine/stdarg.h /usr/include/stdlib.h \
+ /usr/include/sys/socket.h /usr/include/netinet/in.h /usr/include/arpa/inet.h \
+ /usr/include/netdb.h /usr/include/unistd.h /usr/include/sys/unistd.h /usr/include/ctype.h \
+ /usr/include/sys/errno.h /usr/include/signal.h /usr/include/sys/signal.h /usr/include/machine/signal.h \
+ /usr/include/math.h /usr/include/sys/file.h /usr/include/sys/fcntl.h /usr/include/sys/ioctl.h \
+ /usr/include/sys/ttycom.h /usr/include/sys/ioccom.h /usr/include/sys/filio.h \
+ /usr/include/sys/sockio.h /usr/include/sys/wait.h common.h vers.h legal.h multicol.h \
+ utils.h rmalloc.h
+network.o : network.c stdinclude.h /usr/include/sys/types.h /usr/include/machine/endian.h \
+ /usr/include/sys/cdefs.h /usr/include/machine/ansi.h /usr/include/machine/types.h \
+ /usr/include/strings.h /usr/include/string.h /usr/include/sys/dir.h /usr/include/dirent.h \
+ /usr/include/sys/dirent.h /usr/include/sys/stat.h /usr/include/sys/time.h /usr/include/time.h \
+ /usr/include/stdio.h /usr/include/stdarg.h /usr/include/machine/stdarg.h /usr/include/stdlib.h \
+ /usr/include/sys/socket.h /usr/include/netinet/in.h /usr/include/arpa/inet.h \
+ /usr/include/netdb.h /usr/include/unistd.h /usr/include/sys/unistd.h /usr/include/ctype.h \
+ /usr/include/sys/errno.h /usr/include/signal.h /usr/include/sys/signal.h /usr/include/machine/signal.h \
+ /usr/include/math.h /usr/include/sys/file.h /usr/include/sys/fcntl.h /usr/include/sys/ioctl.h \
+ /usr/include/sys/ttycom.h /usr/include/sys/ioccom.h /usr/include/sys/filio.h \
+ /usr/include/sys/sockio.h /usr/include/sys/wait.h /usr/include/arpa/telnet.h \
+ ficsmain.h common.h vers.h legal.h utils.h multicol.h playerdb.h command.h variable.h \
+ lists.h network.h rmalloc.h config.h
+playerdb.o : playerdb.c stdinclude.h /usr/include/sys/types.h /usr/include/machine/endian.h \
+ /usr/include/sys/cdefs.h /usr/include/machine/ansi.h /usr/include/machine/types.h \
+ /usr/include/strings.h /usr/include/string.h /usr/include/sys/dir.h /usr/include/dirent.h \
+ /usr/include/sys/dirent.h /usr/include/sys/stat.h /usr/include/sys/time.h /usr/include/time.h \
+ /usr/include/stdio.h /usr/include/stdarg.h /usr/include/machine/stdarg.h /usr/include/stdlib.h \
+ /usr/include/sys/socket.h /usr/include/netinet/in.h /usr/include/arpa/inet.h \
+ /usr/include/netdb.h /usr/include/unistd.h /usr/include/sys/unistd.h /usr/include/ctype.h \
+ /usr/include/sys/errno.h /usr/include/signal.h /usr/include/sys/signal.h /usr/include/machine/signal.h \
+ /usr/include/math.h /usr/include/sys/file.h /usr/include/sys/fcntl.h /usr/include/sys/ioctl.h \
+ /usr/include/sys/ttycom.h /usr/include/sys/ioccom.h /usr/include/sys/filio.h \
+ /usr/include/sys/sockio.h /usr/include/sys/wait.h common.h vers.h legal.h command.h \
+ variable.h comproc.h playerdb.h lists.h rmalloc.h utils.h multicol.h network.h \
+ ficsmain.h config.h talkproc.h gamedb.h board.h ratings.h
+ratings.o : ratings.c stdinclude.h /usr/include/sys/types.h /usr/include/machine/endian.h \
+ /usr/include/sys/cdefs.h /usr/include/machine/ansi.h /usr/include/machine/types.h \
+ /usr/include/strings.h /usr/include/string.h /usr/include/sys/dir.h /usr/include/dirent.h \
+ /usr/include/sys/dirent.h /usr/include/sys/stat.h /usr/include/sys/time.h /usr/include/time.h \
+ /usr/include/stdio.h /usr/include/stdarg.h /usr/include/machine/stdarg.h /usr/include/stdlib.h \
+ /usr/include/sys/socket.h /usr/include/netinet/in.h /usr/include/arpa/inet.h \
+ /usr/include/netdb.h /usr/include/unistd.h /usr/include/sys/unistd.h /usr/include/ctype.h \
+ /usr/include/sys/errno.h /usr/include/signal.h /usr/include/sys/signal.h /usr/include/machine/signal.h \
+ /usr/include/math.h /usr/include/sys/file.h /usr/include/sys/fcntl.h /usr/include/sys/ioctl.h \
+ /usr/include/sys/ttycom.h /usr/include/sys/ioccom.h /usr/include/sys/filio.h \
+ /usr/include/sys/sockio.h /usr/include/sys/wait.h common.h vers.h legal.h playerdb.h \
+ command.h variable.h lists.h ratings.h gamedb.h board.h comproc.h ficsmain.h \
+ config.h utils.h multicol.h
+rmalloc.o : rmalloc.c stdinclude.h /usr/include/sys/types.h /usr/include/machine/endian.h \
+ /usr/include/sys/cdefs.h /usr/include/machine/ansi.h /usr/include/machine/types.h \
+ /usr/include/strings.h /usr/include/string.h /usr/include/sys/dir.h /usr/include/dirent.h \
+ /usr/include/sys/dirent.h /usr/include/sys/stat.h /usr/include/sys/time.h /usr/include/time.h \
+ /usr/include/stdio.h /usr/include/stdarg.h /usr/include/machine/stdarg.h /usr/include/stdlib.h \
+ /usr/include/sys/socket.h /usr/include/netinet/in.h /usr/include/arpa/inet.h \
+ /usr/include/netdb.h /usr/include/unistd.h /usr/include/sys/unistd.h /usr/include/ctype.h \
+ /usr/include/sys/errno.h /usr/include/signal.h /usr/include/sys/signal.h /usr/include/machine/signal.h \
+ /usr/include/math.h /usr/include/sys/file.h /usr/include/sys/fcntl.h /usr/include/sys/ioctl.h \
+ /usr/include/sys/ttycom.h /usr/include/sys/ioccom.h /usr/include/sys/filio.h \
+ /usr/include/sys/sockio.h /usr/include/sys/wait.h common.h vers.h legal.h
+utils.o : utils.c stdinclude.h /usr/include/sys/types.h /usr/include/machine/endian.h \
+ /usr/include/sys/cdefs.h /usr/include/machine/ansi.h /usr/include/machine/types.h \
+ /usr/include/strings.h /usr/include/string.h /usr/include/sys/dir.h /usr/include/dirent.h \
+ /usr/include/sys/dirent.h /usr/include/sys/stat.h /usr/include/sys/time.h /usr/include/time.h \
+ /usr/include/stdio.h /usr/include/stdarg.h /usr/include/machine/stdarg.h /usr/include/stdlib.h \
+ /usr/include/sys/socket.h /usr/include/netinet/in.h /usr/include/arpa/inet.h \
+ /usr/include/netdb.h /usr/include/unistd.h /usr/include/sys/unistd.h /usr/include/ctype.h \
+ /usr/include/sys/errno.h /usr/include/signal.h /usr/include/sys/signal.h /usr/include/machine/signal.h \
+ /usr/include/math.h /usr/include/sys/file.h /usr/include/sys/fcntl.h /usr/include/sys/ioctl.h \
+ /usr/include/sys/ttycom.h /usr/include/sys/ioccom.h /usr/include/sys/filio.h \
+ /usr/include/sys/sockio.h /usr/include/sys/wait.h common.h vers.h legal.h utils.h \
+ multicol.h playerdb.h command.h variable.h lists.h network.h rmalloc.h config.h
+variable.o : variable.c stdinclude.h /usr/include/sys/types.h /usr/include/machine/endian.h \
+ /usr/include/sys/cdefs.h /usr/include/machine/ansi.h /usr/include/machine/types.h \
+ /usr/include/strings.h /usr/include/string.h /usr/include/sys/dir.h /usr/include/dirent.h \
+ /usr/include/sys/dirent.h /usr/include/sys/stat.h /usr/include/sys/time.h /usr/include/time.h \
+ /usr/include/stdio.h /usr/include/stdarg.h /usr/include/machine/stdarg.h /usr/include/stdlib.h \
+ /usr/include/sys/socket.h /usr/include/netinet/in.h /usr/include/arpa/inet.h \
+ /usr/include/netdb.h /usr/include/unistd.h /usr/include/sys/unistd.h /usr/include/ctype.h \
+ /usr/include/sys/errno.h /usr/include/signal.h /usr/include/sys/signal.h /usr/include/machine/signal.h \
+ /usr/include/math.h /usr/include/sys/file.h /usr/include/sys/fcntl.h /usr/include/sys/ioctl.h \
+ /usr/include/sys/ttycom.h /usr/include/sys/ioccom.h /usr/include/sys/filio.h \
+ /usr/include/sys/sockio.h /usr/include/sys/wait.h common.h vers.h legal.h variable.h \
+ playerdb.h command.h lists.h utils.h multicol.h ficsmain.h config.h rmalloc.h \
+ board.h talkproc.h comproc.h
+vers.o : vers.c
+eco.o : eco.c stdinclude.h /usr/include/sys/types.h /usr/include/machine/endian.h \
+ /usr/include/sys/cdefs.h /usr/include/machine/ansi.h /usr/include/machine/types.h \
+ /usr/include/strings.h /usr/include/string.h /usr/include/sys/dir.h /usr/include/dirent.h \
+ /usr/include/sys/dirent.h /usr/include/sys/stat.h /usr/include/sys/time.h /usr/include/time.h \
+ /usr/include/stdio.h /usr/include/stdarg.h /usr/include/machine/stdarg.h /usr/include/stdlib.h \
+ /usr/include/sys/socket.h /usr/include/netinet/in.h /usr/include/arpa/inet.h \
+ /usr/include/netdb.h /usr/include/unistd.h /usr/include/sys/unistd.h /usr/include/ctype.h \
+ /usr/include/sys/errno.h /usr/include/signal.h /usr/include/sys/signal.h /usr/include/machine/signal.h \
+ /usr/include/math.h /usr/include/sys/file.h /usr/include/sys/fcntl.h /usr/include/sys/ioctl.h \
+ /usr/include/sys/ttycom.h /usr/include/sys/ioccom.h /usr/include/sys/filio.h \
+ /usr/include/sys/sockio.h /usr/include/sys/wait.h board.h gamedb.h command.h \
+ variable.h playerdb.h lists.h gameproc.h obsproc.h utils.h multicol.h common.h \
+ vers.h legal.h config.h
+rating_conv.o : rating_conv.c rating_conv.h common.h vers.h legal.h stdinclude.h \
+ /usr/include/sys/types.h /usr/include/machine/endian.h /usr/include/sys/cdefs.h \
+ /usr/include/machine/ansi.h /usr/include/machine/types.h /usr/include/strings.h \
+ /usr/include/string.h /usr/include/sys/dir.h /usr/include/dirent.h /usr/include/sys/dirent.h \
+ /usr/include/sys/stat.h /usr/include/sys/time.h /usr/include/time.h /usr/include/stdio.h \
+ /usr/include/stdarg.h /usr/include/machine/stdarg.h /usr/include/stdlib.h /usr/include/sys/socket.h \
+ /usr/include/netinet/in.h /usr/include/arpa/inet.h /usr/include/netdb.h /usr/include/unistd.h \
+ /usr/include/sys/unistd.h /usr/include/ctype.h /usr/include/sys/errno.h /usr/include/signal.h \
+ /usr/include/sys/signal.h /usr/include/machine/signal.h /usr/include/math.h \
+ /usr/include/sys/file.h /usr/include/sys/fcntl.h /usr/include/sys/ioctl.h /usr/include/sys/ttycom.h \
+ /usr/include/sys/ioccom.h /usr/include/sys/filio.h /usr/include/sys/sockio.h \
+ /usr/include/sys/wait.h command.h variable.h utils.h multicol.h
+shutdown.o : shutdown.c stdinclude.h /usr/include/sys/types.h /usr/include/machine/endian.h \
+ /usr/include/sys/cdefs.h /usr/include/machine/ansi.h /usr/include/machine/types.h \
+ /usr/include/strings.h /usr/include/string.h /usr/include/sys/dir.h /usr/include/dirent.h \
+ /usr/include/sys/dirent.h /usr/include/sys/stat.h /usr/include/sys/time.h /usr/include/time.h \
+ /usr/include/stdio.h /usr/include/stdarg.h /usr/include/machine/stdarg.h /usr/include/stdlib.h \
+ /usr/include/sys/socket.h /usr/include/netinet/in.h /usr/include/arpa/inet.h \
+ /usr/include/netdb.h /usr/include/unistd.h /usr/include/sys/unistd.h /usr/include/ctype.h \
+ /usr/include/sys/errno.h /usr/include/signal.h /usr/include/sys/signal.h /usr/include/machine/signal.h \
+ /usr/include/math.h /usr/include/sys/file.h /usr/include/sys/fcntl.h /usr/include/sys/ioctl.h \
+ /usr/include/sys/ttycom.h /usr/include/sys/ioccom.h /usr/include/sys/filio.h \
+ /usr/include/sys/sockio.h /usr/include/sys/wait.h common.h vers.h legal.h shutdown.h \
+ command.h variable.h ficsmain.h network.h playerdb.h lists.h utils.h multicol.h
diff --git a/FICS/GNU_LICENSE b/FICS/GNU_LICENSE
new file mode 100644
index 0000000..a43ea21
--- /dev/null
+++ b/FICS/GNU_LICENSE
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/FICS/Makedist b/FICS/Makedist
new file mode 100644
index 0000000..f4ef7b4
--- /dev/null
+++ b/FICS/Makedist
@@ -0,0 +1,6 @@
+# Makefile for tar
+
+distrib:
+ tar cvf - GNU* Make* *.[c,h] READ* | compress > FICS.src_beta-1.2.17.tar.Z
+
+
diff --git a/FICS/Makefile b/FICS/Makefile
new file mode 100644
index 0000000..a23b3d9
--- /dev/null
+++ b/FICS/Makefile
@@ -0,0 +1,50 @@
+# Makefile for FICS
+# This was originally written under NeXTSTEP
+#
+
+# Turn on optimization
+# With optimization on you may get errors during compiling.
+OPTFLAGS=
+
+# Turn on debugging
+#DEBUGFLAGS=-DDEBUG
+
+# Anything else you care to flag?
+#OTHERFLAGS=-traditional-cpp -Wall -g
+OTHERFLAGS=-Wall -g -pg
+
+# Which architectures are you compiling for? (NeXTSTEP)
+# ARCHFLAGS=-arch m68k -arch i386
+
+PROGRAM=fics
+
+
+CC=gcc
+#cc doesn't seem to be supported at present.
+
+CFLAGS=${SYSFLAG} ${OPTFLAGS} ${DEBUGFLAGS} ${OTHERFLAGS} ${ARCHFLAGS}
+LNFLAGS=-lm -lcompat -lcrypt
+
+# dfree is temporary, for debugging only, and works only if you are using
+# the GNU C library --mann
+#MORESRCS=dfree.c
+#MOREOBJS=dfree.o
+#you may have to hash out the above two
+
+# This is just for versions distributed with timeseal.
+#MORESRCS=timeseal.c
+#MOREOBJS=timeseal.o
+
+# Define the system below that you are compiling on.
+# Set the flags for your system
+
+SYSFLAG=-DNETBSD #-DTIMESEAL #-DIGNORE_ECO
+
+# The following is needed when using gcc and glibc, as on caissa.
+#MAKEDEPMORECFLAGS= -I/usr/local/include -I/usr/local/lib/gcc-lib/sparc-sun-sunos4.1.3/2.6.3/include
+
+include ./Makefile.common
+# include ./Makedist
+
+#run makedepend to fill out the info below
+# DO NOT DELETE THIS LINE -- make depend depends on it.
diff --git a/FICS/Makefile.alpha b/FICS/Makefile.alpha
new file mode 100644
index 0000000..80c1584
--- /dev/null
+++ b/FICS/Makefile.alpha
@@ -0,0 +1,33 @@
+# Makefile for FICS
+# This version is for the Alpha under Digital OSF/1 Unix
+#
+# Define the system below that you are compiling on.
+#SYSFLAG= -DSYSV -DSYSTEM_AIX
+SYSFLAG=
+
+# Set the flags for your system
+
+# Turn on optimization
+# With optimization on you may get errors during compiling.
+#OPTFLAGS=-O
+
+# Turn on debugging
+DEBUGFLAGS= -DDEBUG -g
+
+# Anything else you care to flag?
+#OTHERFLAGS= -qlanglvl=saa
+OTHERFLAGS= -DHAVE_STDARG
+
+# Which architectures are you compiling for? (NeXTSTEP)
+# ARCHFLAGS=-arch m68k -arch i386
+
+PROGRAM=fics
+
+
+#CC=gcc -ansi -pedantic -O3 -pipe -fomit-frame-pointer
+#CC=xlc
+CC=cc
+CFLAGS=${SYSFLAG} ${OPTFLAGS} ${DEBUGFLAGS} ${OTHERFLAGS} ${ARCHFLAGS}
+LNFLAGS=-lm
+
+include ./Makefile.common
diff --git a/FICS/Makefile.bak b/FICS/Makefile.bak
new file mode 100644
index 0000000..7380eaa
--- /dev/null
+++ b/FICS/Makefile.bak
@@ -0,0 +1,645 @@
+# Makefile for FICS
+# This was originally written under NeXTSTEP
+#
+# Define the system below that you are compiling on.
+
+SYSFLAG=-DSYSTEM_SUN4
+
+# Set the flags for your system
+
+# Turn on optimization
+# With optimization on you may get errors during compiling.
+OPTFLAGS=
+
+# Turn on debugging
+#DEBUGFLAGS=-DDEBUG
+
+# Anything else you care to flag?
+#OTHERFLAGS=-traditional-cpp -Wall -g
+OTHERFLAGS=-Wall -g
+
+# Which architectures are you compiling for? (NeXTSTEP)
+# ARCHFLAGS=-arch m68k -arch i386
+
+PROGRAM=fics
+
+
+CC=gcc
+#cc doesn't seem to be supported at present.
+
+CFLAGS=${SYSFLAG} ${OPTFLAGS} ${DEBUGFLAGS} ${OTHERFLAGS} ${ARCHFLAGS}
+LNFLAGS=-lm
+
+# dfree is temporary, for debugging only, and works only if you are using
+# the GNU C library --mann
+#MORESRCS=dfree.c
+#MOREOBJS=dfree.o
+#you may have to hash out the above two
+
+# This is just for versions distributed with timeseal.
+MORESRCS=timeseal.c
+MOREOBJS=timeseal.o
+SYSFLAG=-DSYSTEM_SUN4 -DTIMESEAL
+
+# The following is needed when using gcc and glibc, as on caissa.
+MAKEDEPMORECFLAGS= -I/usr/local/include -I/usr/local/lib/gcc-lib/sparc-sun-sunos4.1.3/2.6.3/include
+
+include ./Makefile.common
+# include ./Makedist
+
+#run makedepend to fill out the info below
+# DO NOT DELETE THIS LINE -- make depend depends on it.
+
+adminproc.o: stdinclude.h /usr/local/include/sys/types.h
+adminproc.o: /usr/local/include/features.h /usr/local/include/sys/cdefs.h
+adminproc.o: /usr/local/include/stubs.h /usr/local/include/gnu/types.h
+adminproc.o: /usr/local/include/sys/time.h /usr/local/include/stddef.h
+adminproc.o: /usr/local/include/strings.h /usr/local/include/string.h
+adminproc.o: /usr/local/include/sys/dir.h /usr/local/include/dirent.h
+adminproc.o: /usr/local/include/dirstream.h /usr/local/include/sys/stat.h
+adminproc.o: /usr/local/include/statbuf.h /usr/local/include/stdio.h stdarg.h
+adminproc.o: /usr/local/include/stdlib.h /usr/local/include/errno.h
+adminproc.o: /usr/local/include/errnos.h /usr/local/include/sys/socket.h
+adminproc.o: /usr/local/include/netinet/in.h /usr/local/include/endian.h
+adminproc.o: /usr/local/include/bytesex.h /usr/local/include/arpa/inet.h
+adminproc.o: /usr/local/include/netdb.h /usr/local/include/unistd.h
+adminproc.o: /usr/local/include/posix_opt.h /usr/local/include/confname.h
+adminproc.o: /usr/local/include/ctype.h /usr/local/include/sys/errno.h
+adminproc.o: /usr/local/include/signal.h /usr/local/include/sigset.h
+adminproc.o: /usr/local/include/math.h /usr/local/include/huge_val.h
+adminproc.o: /usr/local/include/__math.h /usr/local/include/sys/time.h
+adminproc.o: /usr/local/include/sys/file.h /usr/local/include/sys/fcntl.h
+adminproc.o: /usr/local/include/sys/ioctl.h /usr/local/include/ioctls.h
+adminproc.o: /usr/local/include/sys/ttydefaults.h
+adminproc.o: /usr/local/include/sys/wait.h /usr/local/include/waitflags.h
+adminproc.o: /usr/local/include/waitstatus.h common.h vers.h legal.h
+adminproc.o: network.h command.h variable.h adminproc.h playerdb.h gamedb.h
+adminproc.o: board.h gameproc.h ratings.h utils.h multicol.h rmalloc.h
+adminproc.o: talkproc.h comproc.h
+algcheck.o: stdinclude.h /usr/local/include/sys/types.h
+algcheck.o: /usr/local/include/features.h /usr/local/include/sys/cdefs.h
+algcheck.o: /usr/local/include/stubs.h /usr/local/include/gnu/types.h
+algcheck.o: /usr/local/include/sys/time.h /usr/local/include/stddef.h
+algcheck.o: /usr/local/include/strings.h /usr/local/include/string.h
+algcheck.o: /usr/local/include/sys/dir.h /usr/local/include/dirent.h
+algcheck.o: /usr/local/include/dirstream.h /usr/local/include/sys/stat.h
+algcheck.o: /usr/local/include/statbuf.h /usr/local/include/stdio.h stdarg.h
+algcheck.o: /usr/local/include/stdlib.h /usr/local/include/errno.h
+algcheck.o: /usr/local/include/errnos.h /usr/local/include/sys/socket.h
+algcheck.o: /usr/local/include/netinet/in.h /usr/local/include/endian.h
+algcheck.o: /usr/local/include/bytesex.h /usr/local/include/arpa/inet.h
+algcheck.o: /usr/local/include/netdb.h /usr/local/include/unistd.h
+algcheck.o: /usr/local/include/posix_opt.h /usr/local/include/confname.h
+algcheck.o: /usr/local/include/ctype.h /usr/local/include/sys/errno.h
+algcheck.o: /usr/local/include/signal.h /usr/local/include/sigset.h
+algcheck.o: /usr/local/include/math.h /usr/local/include/huge_val.h
+algcheck.o: /usr/local/include/__math.h /usr/local/include/sys/time.h
+algcheck.o: /usr/local/include/sys/file.h /usr/local/include/sys/fcntl.h
+algcheck.o: /usr/local/include/sys/ioctl.h /usr/local/include/ioctls.h
+algcheck.o: /usr/local/include/sys/ttydefaults.h
+algcheck.o: /usr/local/include/sys/wait.h /usr/local/include/waitflags.h
+algcheck.o: /usr/local/include/waitstatus.h common.h vers.h legal.h
+algcheck.o: algcheck.h board.h movecheck.h utils.h multicol.h
+board.o: stdinclude.h /usr/local/include/sys/types.h
+board.o: /usr/local/include/features.h /usr/local/include/sys/cdefs.h
+board.o: /usr/local/include/stubs.h /usr/local/include/gnu/types.h
+board.o: /usr/local/include/sys/time.h /usr/local/include/stddef.h
+board.o: /usr/local/include/strings.h /usr/local/include/string.h
+board.o: /usr/local/include/sys/dir.h /usr/local/include/dirent.h
+board.o: /usr/local/include/dirstream.h /usr/local/include/sys/stat.h
+board.o: /usr/local/include/statbuf.h /usr/local/include/stdio.h stdarg.h
+board.o: /usr/local/include/stdlib.h /usr/local/include/errno.h
+board.o: /usr/local/include/errnos.h /usr/local/include/sys/socket.h
+board.o: /usr/local/include/netinet/in.h /usr/local/include/endian.h
+board.o: /usr/local/include/bytesex.h /usr/local/include/arpa/inet.h
+board.o: /usr/local/include/netdb.h /usr/local/include/unistd.h
+board.o: /usr/local/include/posix_opt.h /usr/local/include/confname.h
+board.o: /usr/local/include/ctype.h /usr/local/include/sys/errno.h
+board.o: /usr/local/include/signal.h /usr/local/include/sigset.h
+board.o: /usr/local/include/math.h /usr/local/include/huge_val.h
+board.o: /usr/local/include/__math.h /usr/local/include/sys/time.h
+board.o: /usr/local/include/sys/file.h /usr/local/include/sys/fcntl.h
+board.o: /usr/local/include/sys/ioctl.h /usr/local/include/ioctls.h
+board.o: /usr/local/include/sys/ttydefaults.h /usr/local/include/sys/wait.h
+board.o: /usr/local/include/waitflags.h /usr/local/include/waitstatus.h
+board.o: common.h vers.h legal.h board.h playerdb.h command.h variable.h
+board.o: gamedb.h utils.h multicol.h
+channel.o: stdinclude.h /usr/local/include/sys/types.h
+channel.o: /usr/local/include/features.h /usr/local/include/sys/cdefs.h
+channel.o: /usr/local/include/stubs.h /usr/local/include/gnu/types.h
+channel.o: /usr/local/include/sys/time.h /usr/local/include/stddef.h
+channel.o: /usr/local/include/strings.h /usr/local/include/string.h
+channel.o: /usr/local/include/sys/dir.h /usr/local/include/dirent.h
+channel.o: /usr/local/include/dirstream.h /usr/local/include/sys/stat.h
+channel.o: /usr/local/include/statbuf.h /usr/local/include/stdio.h stdarg.h
+channel.o: /usr/local/include/stdlib.h /usr/local/include/errno.h
+channel.o: /usr/local/include/errnos.h /usr/local/include/sys/socket.h
+channel.o: /usr/local/include/netinet/in.h /usr/local/include/endian.h
+channel.o: /usr/local/include/bytesex.h /usr/local/include/arpa/inet.h
+channel.o: /usr/local/include/netdb.h /usr/local/include/unistd.h
+channel.o: /usr/local/include/posix_opt.h /usr/local/include/confname.h
+channel.o: /usr/local/include/ctype.h /usr/local/include/sys/errno.h
+channel.o: /usr/local/include/signal.h /usr/local/include/sigset.h
+channel.o: /usr/local/include/math.h /usr/local/include/huge_val.h
+channel.o: /usr/local/include/__math.h /usr/local/include/sys/time.h
+channel.o: /usr/local/include/sys/file.h /usr/local/include/sys/fcntl.h
+channel.o: /usr/local/include/sys/ioctl.h /usr/local/include/ioctls.h
+channel.o: /usr/local/include/sys/ttydefaults.h /usr/local/include/sys/wait.h
+channel.o: /usr/local/include/waitflags.h /usr/local/include/waitstatus.h
+channel.o: common.h vers.h legal.h channel.h network.h command.h variable.h
+channel.o: rmalloc.h playerdb.h
+command.o: stdinclude.h /usr/local/include/sys/types.h
+command.o: /usr/local/include/features.h /usr/local/include/sys/cdefs.h
+command.o: /usr/local/include/stubs.h /usr/local/include/gnu/types.h
+command.o: /usr/local/include/sys/time.h /usr/local/include/stddef.h
+command.o: /usr/local/include/strings.h /usr/local/include/string.h
+command.o: /usr/local/include/sys/dir.h /usr/local/include/dirent.h
+command.o: /usr/local/include/dirstream.h /usr/local/include/sys/stat.h
+command.o: /usr/local/include/statbuf.h /usr/local/include/stdio.h stdarg.h
+command.o: /usr/local/include/stdlib.h /usr/local/include/errno.h
+command.o: /usr/local/include/errnos.h /usr/local/include/sys/socket.h
+command.o: /usr/local/include/netinet/in.h /usr/local/include/endian.h
+command.o: /usr/local/include/bytesex.h /usr/local/include/arpa/inet.h
+command.o: /usr/local/include/netdb.h /usr/local/include/unistd.h
+command.o: /usr/local/include/posix_opt.h /usr/local/include/confname.h
+command.o: /usr/local/include/ctype.h /usr/local/include/sys/errno.h
+command.o: /usr/local/include/signal.h /usr/local/include/sigset.h
+command.o: /usr/local/include/math.h /usr/local/include/huge_val.h
+command.o: /usr/local/include/__math.h /usr/local/include/sys/time.h
+command.o: /usr/local/include/sys/file.h /usr/local/include/sys/fcntl.h
+command.o: /usr/local/include/sys/ioctl.h /usr/local/include/ioctls.h
+command.o: /usr/local/include/sys/ttydefaults.h /usr/local/include/sys/wait.h
+command.o: /usr/local/include/waitflags.h /usr/local/include/waitstatus.h
+command.o: common.h vers.h legal.h rmalloc.h command.h variable.h
+command.o: command_list.h comproc.h matchproc.h talkproc.h lists.h gameproc.h
+command.o: adminproc.h playerdb.h ratings.h eco.h movecheck.h ficsmain.h
+command.o: config.h utils.h multicol.h gamedb.h board.h network.h
+talkproc.o: stdinclude.h /usr/local/include/sys/types.h
+talkproc.o: /usr/local/include/features.h /usr/local/include/sys/cdefs.h
+talkproc.o: /usr/local/include/stubs.h /usr/local/include/gnu/types.h
+talkproc.o: /usr/local/include/sys/time.h /usr/local/include/stddef.h
+talkproc.o: /usr/local/include/strings.h /usr/local/include/string.h
+talkproc.o: /usr/local/include/sys/dir.h /usr/local/include/dirent.h
+talkproc.o: /usr/local/include/dirstream.h /usr/local/include/sys/stat.h
+talkproc.o: /usr/local/include/statbuf.h /usr/local/include/stdio.h stdarg.h
+talkproc.o: /usr/local/include/stdlib.h /usr/local/include/errno.h
+talkproc.o: /usr/local/include/errnos.h /usr/local/include/sys/socket.h
+talkproc.o: /usr/local/include/netinet/in.h /usr/local/include/endian.h
+talkproc.o: /usr/local/include/bytesex.h /usr/local/include/arpa/inet.h
+talkproc.o: /usr/local/include/netdb.h /usr/local/include/unistd.h
+talkproc.o: /usr/local/include/posix_opt.h /usr/local/include/confname.h
+talkproc.o: /usr/local/include/ctype.h /usr/local/include/sys/errno.h
+talkproc.o: /usr/local/include/signal.h /usr/local/include/sigset.h
+talkproc.o: /usr/local/include/math.h /usr/local/include/huge_val.h
+talkproc.o: /usr/local/include/__math.h /usr/local/include/sys/time.h
+talkproc.o: /usr/local/include/sys/file.h /usr/local/include/sys/fcntl.h
+talkproc.o: /usr/local/include/sys/ioctl.h /usr/local/include/ioctls.h
+talkproc.o: /usr/local/include/sys/ttydefaults.h
+talkproc.o: /usr/local/include/sys/wait.h /usr/local/include/waitflags.h
+talkproc.o: /usr/local/include/waitstatus.h common.h vers.h legal.h
+talkproc.o: talkproc.h comproc.h command.h variable.h utils.h multicol.h
+talkproc.o: ficsmain.h config.h playerdb.h network.h rmalloc.h channel.h
+talkproc.o: gamedb.h board.h gameproc.h lists.h
+talkproc.o: /usr/local/include/sys/resource.h
+talkproc.o: /usr/local/include/resourcebits.h
+comproc.o: stdinclude.h /usr/local/include/sys/types.h
+comproc.o: /usr/local/include/features.h /usr/local/include/sys/cdefs.h
+comproc.o: /usr/local/include/stubs.h /usr/local/include/gnu/types.h
+comproc.o: /usr/local/include/sys/time.h /usr/local/include/stddef.h
+comproc.o: /usr/local/include/strings.h /usr/local/include/string.h
+comproc.o: /usr/local/include/sys/dir.h /usr/local/include/dirent.h
+comproc.o: /usr/local/include/dirstream.h /usr/local/include/sys/stat.h
+comproc.o: /usr/local/include/statbuf.h /usr/local/include/stdio.h stdarg.h
+comproc.o: /usr/local/include/stdlib.h /usr/local/include/errno.h
+comproc.o: /usr/local/include/errnos.h /usr/local/include/sys/socket.h
+comproc.o: /usr/local/include/netinet/in.h /usr/local/include/endian.h
+comproc.o: /usr/local/include/bytesex.h /usr/local/include/arpa/inet.h
+comproc.o: /usr/local/include/netdb.h /usr/local/include/unistd.h
+comproc.o: /usr/local/include/posix_opt.h /usr/local/include/confname.h
+comproc.o: /usr/local/include/ctype.h /usr/local/include/sys/errno.h
+comproc.o: /usr/local/include/signal.h /usr/local/include/sigset.h
+comproc.o: /usr/local/include/math.h /usr/local/include/huge_val.h
+comproc.o: /usr/local/include/__math.h /usr/local/include/sys/time.h
+comproc.o: /usr/local/include/sys/file.h /usr/local/include/sys/fcntl.h
+comproc.o: /usr/local/include/sys/ioctl.h /usr/local/include/ioctls.h
+comproc.o: /usr/local/include/sys/ttydefaults.h /usr/local/include/sys/wait.h
+comproc.o: /usr/local/include/waitflags.h /usr/local/include/waitstatus.h
+comproc.o: common.h vers.h legal.h talkproc.h comproc.h command.h variable.h
+comproc.o: utils.h multicol.h ficsmain.h config.h playerdb.h network.h
+comproc.o: rmalloc.h channel.h gamedb.h board.h gameproc.h ratings.h
+comproc.o: formula.h lists.h eco.h /usr/local/include/sys/resource.h
+comproc.o: /usr/local/include/resourcebits.h
+matchproc.o: stdinclude.h /usr/local/include/sys/types.h
+matchproc.o: /usr/local/include/features.h /usr/local/include/sys/cdefs.h
+matchproc.o: /usr/local/include/stubs.h /usr/local/include/gnu/types.h
+matchproc.o: /usr/local/include/sys/time.h /usr/local/include/stddef.h
+matchproc.o: /usr/local/include/strings.h /usr/local/include/string.h
+matchproc.o: /usr/local/include/sys/dir.h /usr/local/include/dirent.h
+matchproc.o: /usr/local/include/dirstream.h /usr/local/include/sys/stat.h
+matchproc.o: /usr/local/include/statbuf.h /usr/local/include/stdio.h stdarg.h
+matchproc.o: /usr/local/include/stdlib.h /usr/local/include/errno.h
+matchproc.o: /usr/local/include/errnos.h /usr/local/include/sys/socket.h
+matchproc.o: /usr/local/include/netinet/in.h /usr/local/include/endian.h
+matchproc.o: /usr/local/include/bytesex.h /usr/local/include/arpa/inet.h
+matchproc.o: /usr/local/include/netdb.h /usr/local/include/unistd.h
+matchproc.o: /usr/local/include/posix_opt.h /usr/local/include/confname.h
+matchproc.o: /usr/local/include/ctype.h /usr/local/include/sys/errno.h
+matchproc.o: /usr/local/include/signal.h /usr/local/include/sigset.h
+matchproc.o: /usr/local/include/math.h /usr/local/include/huge_val.h
+matchproc.o: /usr/local/include/__math.h /usr/local/include/sys/time.h
+matchproc.o: /usr/local/include/sys/file.h /usr/local/include/sys/fcntl.h
+matchproc.o: /usr/local/include/sys/ioctl.h /usr/local/include/ioctls.h
+matchproc.o: /usr/local/include/sys/ttydefaults.h
+matchproc.o: /usr/local/include/sys/wait.h /usr/local/include/waitflags.h
+matchproc.o: /usr/local/include/waitstatus.h common.h vers.h legal.h
+matchproc.o: talkproc.h comproc.h command.h variable.h utils.h multicol.h
+matchproc.o: ficsmain.h config.h playerdb.h network.h rmalloc.h channel.h
+matchproc.o: gamedb.h board.h gameproc.h ratings.h formula.h lists.h eco.h
+matchproc.o: /usr/local/include/sys/resource.h
+matchproc.o: /usr/local/include/resourcebits.h
+fics_addplayer.o: stdinclude.h /usr/local/include/sys/types.h
+fics_addplayer.o: /usr/local/include/features.h
+fics_addplayer.o: /usr/local/include/sys/cdefs.h /usr/local/include/stubs.h
+fics_addplayer.o: /usr/local/include/gnu/types.h
+fics_addplayer.o: /usr/local/include/sys/time.h /usr/local/include/stddef.h
+fics_addplayer.o: /usr/local/include/strings.h /usr/local/include/string.h
+fics_addplayer.o: /usr/local/include/sys/dir.h /usr/local/include/dirent.h
+fics_addplayer.o: /usr/local/include/dirstream.h
+fics_addplayer.o: /usr/local/include/sys/stat.h /usr/local/include/statbuf.h
+fics_addplayer.o: /usr/local/include/stdio.h stdarg.h
+fics_addplayer.o: /usr/local/include/stdlib.h /usr/local/include/errno.h
+fics_addplayer.o: /usr/local/include/errnos.h /usr/local/include/sys/socket.h
+fics_addplayer.o: /usr/local/include/netinet/in.h /usr/local/include/endian.h
+fics_addplayer.o: /usr/local/include/bytesex.h /usr/local/include/arpa/inet.h
+fics_addplayer.o: /usr/local/include/netdb.h /usr/local/include/unistd.h
+fics_addplayer.o: /usr/local/include/posix_opt.h
+fics_addplayer.o: /usr/local/include/confname.h /usr/local/include/ctype.h
+fics_addplayer.o: /usr/local/include/sys/errno.h /usr/local/include/signal.h
+fics_addplayer.o: /usr/local/include/sigset.h /usr/local/include/math.h
+fics_addplayer.o: /usr/local/include/huge_val.h /usr/local/include/__math.h
+fics_addplayer.o: /usr/local/include/sys/time.h /usr/local/include/sys/file.h
+fics_addplayer.o: /usr/local/include/sys/fcntl.h
+fics_addplayer.o: /usr/local/include/sys/ioctl.h /usr/local/include/ioctls.h
+fics_addplayer.o: /usr/local/include/sys/ttydefaults.h
+fics_addplayer.o: /usr/local/include/sys/wait.h
+fics_addplayer.o: /usr/local/include/waitflags.h
+fics_addplayer.o: /usr/local/include/waitstatus.h common.h vers.h legal.h
+fics_addplayer.o: utils.h multicol.h playerdb.h command.h variable.h
+ficsmain.o: stdinclude.h /usr/local/include/sys/types.h
+ficsmain.o: /usr/local/include/features.h /usr/local/include/sys/cdefs.h
+ficsmain.o: /usr/local/include/stubs.h /usr/local/include/gnu/types.h
+ficsmain.o: /usr/local/include/sys/time.h /usr/local/include/stddef.h
+ficsmain.o: /usr/local/include/strings.h /usr/local/include/string.h
+ficsmain.o: /usr/local/include/sys/dir.h /usr/local/include/dirent.h
+ficsmain.o: /usr/local/include/dirstream.h /usr/local/include/sys/stat.h
+ficsmain.o: /usr/local/include/statbuf.h /usr/local/include/stdio.h stdarg.h
+ficsmain.o: /usr/local/include/stdlib.h /usr/local/include/errno.h
+ficsmain.o: /usr/local/include/errnos.h /usr/local/include/sys/socket.h
+ficsmain.o: /usr/local/include/netinet/in.h /usr/local/include/endian.h
+ficsmain.o: /usr/local/include/bytesex.h /usr/local/include/arpa/inet.h
+ficsmain.o: /usr/local/include/netdb.h /usr/local/include/unistd.h
+ficsmain.o: /usr/local/include/posix_opt.h /usr/local/include/confname.h
+ficsmain.o: /usr/local/include/ctype.h /usr/local/include/sys/errno.h
+ficsmain.o: /usr/local/include/signal.h /usr/local/include/sigset.h
+ficsmain.o: /usr/local/include/math.h /usr/local/include/huge_val.h
+ficsmain.o: /usr/local/include/__math.h /usr/local/include/sys/time.h
+ficsmain.o: /usr/local/include/sys/file.h /usr/local/include/sys/fcntl.h
+ficsmain.o: /usr/local/include/sys/ioctl.h /usr/local/include/ioctls.h
+ficsmain.o: /usr/local/include/sys/ttydefaults.h
+ficsmain.o: /usr/local/include/sys/wait.h /usr/local/include/waitflags.h
+ficsmain.o: /usr/local/include/waitstatus.h common.h vers.h legal.h
+ficsmain.o: ficsmain.h config.h network.h command.h variable.h channel.h
+ficsmain.o: playerdb.h ratings.h utils.h multicol.h board.h talkproc.h
+ficsmain.o: comproc.h eco.h
+formula.o: /usr/local/include/ctype.h /usr/local/include/features.h
+formula.o: /usr/local/include/sys/cdefs.h /usr/local/include/stubs.h config.h
+formula.o: formula.h playerdb.h command.h variable.h gamedb.h
+formula.o: /usr/local/include/sys/time.h board.h common.h vers.h legal.h
+formula.o: stdinclude.h /usr/local/include/sys/types.h
+formula.o: /usr/local/include/gnu/types.h /usr/local/include/stddef.h
+formula.o: /usr/local/include/strings.h /usr/local/include/string.h
+formula.o: /usr/local/include/sys/dir.h /usr/local/include/dirent.h
+formula.o: /usr/local/include/dirstream.h /usr/local/include/sys/stat.h
+formula.o: /usr/local/include/statbuf.h /usr/local/include/stdio.h stdarg.h
+formula.o: /usr/local/include/stdlib.h /usr/local/include/errno.h
+formula.o: /usr/local/include/errnos.h /usr/local/include/sys/socket.h
+formula.o: /usr/local/include/netinet/in.h /usr/local/include/endian.h
+formula.o: /usr/local/include/bytesex.h /usr/local/include/arpa/inet.h
+formula.o: /usr/local/include/netdb.h /usr/local/include/unistd.h
+formula.o: /usr/local/include/posix_opt.h /usr/local/include/confname.h
+formula.o: /usr/local/include/sys/errno.h /usr/local/include/signal.h
+formula.o: /usr/local/include/sigset.h /usr/local/include/math.h
+formula.o: /usr/local/include/huge_val.h /usr/local/include/__math.h
+formula.o: /usr/local/include/sys/time.h /usr/local/include/sys/file.h
+formula.o: /usr/local/include/sys/fcntl.h /usr/local/include/sys/ioctl.h
+formula.o: /usr/local/include/ioctls.h /usr/local/include/sys/ttydefaults.h
+formula.o: /usr/local/include/sys/wait.h /usr/local/include/waitflags.h
+formula.o: /usr/local/include/waitstatus.h utils.h multicol.h rmalloc.h
+formula.o: lists.h
+gamedb.o: stdinclude.h /usr/local/include/sys/types.h
+gamedb.o: /usr/local/include/features.h /usr/local/include/sys/cdefs.h
+gamedb.o: /usr/local/include/stubs.h /usr/local/include/gnu/types.h
+gamedb.o: /usr/local/include/sys/time.h /usr/local/include/stddef.h
+gamedb.o: /usr/local/include/strings.h /usr/local/include/string.h
+gamedb.o: /usr/local/include/sys/dir.h /usr/local/include/dirent.h
+gamedb.o: /usr/local/include/dirstream.h /usr/local/include/sys/stat.h
+gamedb.o: /usr/local/include/statbuf.h /usr/local/include/stdio.h stdarg.h
+gamedb.o: /usr/local/include/stdlib.h /usr/local/include/errno.h
+gamedb.o: /usr/local/include/errnos.h /usr/local/include/sys/socket.h
+gamedb.o: /usr/local/include/netinet/in.h /usr/local/include/endian.h
+gamedb.o: /usr/local/include/bytesex.h /usr/local/include/arpa/inet.h
+gamedb.o: /usr/local/include/netdb.h /usr/local/include/unistd.h
+gamedb.o: /usr/local/include/posix_opt.h /usr/local/include/confname.h
+gamedb.o: /usr/local/include/ctype.h /usr/local/include/sys/errno.h
+gamedb.o: /usr/local/include/signal.h /usr/local/include/sigset.h
+gamedb.o: /usr/local/include/math.h /usr/local/include/huge_val.h
+gamedb.o: /usr/local/include/__math.h /usr/local/include/sys/time.h
+gamedb.o: /usr/local/include/sys/file.h /usr/local/include/sys/fcntl.h
+gamedb.o: /usr/local/include/sys/ioctl.h /usr/local/include/ioctls.h
+gamedb.o: /usr/local/include/sys/ttydefaults.h /usr/local/include/sys/wait.h
+gamedb.o: /usr/local/include/waitflags.h /usr/local/include/waitstatus.h
+gamedb.o: common.h vers.h legal.h ficsmain.h config.h gamedb.h board.h
+gamedb.o: playerdb.h command.h variable.h gameproc.h utils.h multicol.h
+gamedb.o: rmalloc.h eco.h network.h
+gameproc.o: stdinclude.h /usr/local/include/sys/types.h
+gameproc.o: /usr/local/include/features.h /usr/local/include/sys/cdefs.h
+gameproc.o: /usr/local/include/stubs.h /usr/local/include/gnu/types.h
+gameproc.o: /usr/local/include/sys/time.h /usr/local/include/stddef.h
+gameproc.o: /usr/local/include/strings.h /usr/local/include/string.h
+gameproc.o: /usr/local/include/sys/dir.h /usr/local/include/dirent.h
+gameproc.o: /usr/local/include/dirstream.h /usr/local/include/sys/stat.h
+gameproc.o: /usr/local/include/statbuf.h /usr/local/include/stdio.h stdarg.h
+gameproc.o: /usr/local/include/stdlib.h /usr/local/include/errno.h
+gameproc.o: /usr/local/include/errnos.h /usr/local/include/sys/socket.h
+gameproc.o: /usr/local/include/netinet/in.h /usr/local/include/endian.h
+gameproc.o: /usr/local/include/bytesex.h /usr/local/include/arpa/inet.h
+gameproc.o: /usr/local/include/netdb.h /usr/local/include/unistd.h
+gameproc.o: /usr/local/include/posix_opt.h /usr/local/include/confname.h
+gameproc.o: /usr/local/include/ctype.h /usr/local/include/sys/errno.h
+gameproc.o: /usr/local/include/signal.h /usr/local/include/sigset.h
+gameproc.o: /usr/local/include/math.h /usr/local/include/huge_val.h
+gameproc.o: /usr/local/include/__math.h /usr/local/include/sys/time.h
+gameproc.o: /usr/local/include/sys/file.h /usr/local/include/sys/fcntl.h
+gameproc.o: /usr/local/include/sys/ioctl.h /usr/local/include/ioctls.h
+gameproc.o: /usr/local/include/sys/ttydefaults.h
+gameproc.o: /usr/local/include/sys/wait.h /usr/local/include/waitflags.h
+gameproc.o: /usr/local/include/waitstatus.h common.h vers.h legal.h command.h
+gameproc.o: variable.h ficsmain.h config.h playerdb.h gamedb.h board.h
+gameproc.o: gameproc.h movecheck.h utils.h multicol.h ratings.h rmalloc.h
+gameproc.o: comproc.h matchproc.h formula.h eco.h network.h
+lists.o: lists.h common.h vers.h legal.h multicol.h command.h variable.h
+lists.o: utils.h /usr/local/include/stdio.h playerdb.h ratings.h rmalloc.h
+lists.o: gamedb.h /usr/local/include/sys/time.h /usr/local/include/features.h
+lists.o: /usr/local/include/sys/cdefs.h /usr/local/include/stubs.h board.h
+lists.o: stdinclude.h /usr/local/include/sys/types.h
+lists.o: /usr/local/include/gnu/types.h /usr/local/include/stddef.h
+lists.o: /usr/local/include/strings.h /usr/local/include/string.h
+lists.o: /usr/local/include/sys/dir.h /usr/local/include/dirent.h
+lists.o: /usr/local/include/dirstream.h /usr/local/include/sys/stat.h
+lists.o: /usr/local/include/statbuf.h stdarg.h /usr/local/include/stdlib.h
+lists.o: /usr/local/include/errno.h /usr/local/include/errnos.h
+lists.o: /usr/local/include/sys/socket.h /usr/local/include/netinet/in.h
+lists.o: /usr/local/include/endian.h /usr/local/include/bytesex.h
+lists.o: /usr/local/include/arpa/inet.h /usr/local/include/netdb.h
+lists.o: /usr/local/include/unistd.h /usr/local/include/posix_opt.h
+lists.o: /usr/local/include/confname.h /usr/local/include/ctype.h
+lists.o: /usr/local/include/sys/errno.h /usr/local/include/signal.h
+lists.o: /usr/local/include/sigset.h /usr/local/include/math.h
+lists.o: /usr/local/include/huge_val.h /usr/local/include/__math.h
+lists.o: /usr/local/include/sys/time.h /usr/local/include/sys/file.h
+lists.o: /usr/local/include/sys/fcntl.h /usr/local/include/sys/ioctl.h
+lists.o: /usr/local/include/ioctls.h /usr/local/include/sys/ttydefaults.h
+lists.o: /usr/local/include/sys/wait.h /usr/local/include/waitflags.h
+lists.o: /usr/local/include/waitstatus.h comproc.h
+makerank.o: /usr/local/include/stdio.h /usr/local/include/string.h
+makerank.o: /usr/local/include/features.h /usr/local/include/sys/cdefs.h
+makerank.o: /usr/local/include/stubs.h /usr/local/include/stddef.h
+makerank.o: /usr/local/include/ctype.h
+movecheck.o: stdinclude.h /usr/local/include/sys/types.h
+movecheck.o: /usr/local/include/features.h /usr/local/include/sys/cdefs.h
+movecheck.o: /usr/local/include/stubs.h /usr/local/include/gnu/types.h
+movecheck.o: /usr/local/include/sys/time.h /usr/local/include/stddef.h
+movecheck.o: /usr/local/include/strings.h /usr/local/include/string.h
+movecheck.o: /usr/local/include/sys/dir.h /usr/local/include/dirent.h
+movecheck.o: /usr/local/include/dirstream.h /usr/local/include/sys/stat.h
+movecheck.o: /usr/local/include/statbuf.h /usr/local/include/stdio.h stdarg.h
+movecheck.o: /usr/local/include/stdlib.h /usr/local/include/errno.h
+movecheck.o: /usr/local/include/errnos.h /usr/local/include/sys/socket.h
+movecheck.o: /usr/local/include/netinet/in.h /usr/local/include/endian.h
+movecheck.o: /usr/local/include/bytesex.h /usr/local/include/arpa/inet.h
+movecheck.o: /usr/local/include/netdb.h /usr/local/include/unistd.h
+movecheck.o: /usr/local/include/posix_opt.h /usr/local/include/confname.h
+movecheck.o: /usr/local/include/ctype.h /usr/local/include/sys/errno.h
+movecheck.o: /usr/local/include/signal.h /usr/local/include/sigset.h
+movecheck.o: /usr/local/include/math.h /usr/local/include/huge_val.h
+movecheck.o: /usr/local/include/__math.h /usr/local/include/sys/time.h
+movecheck.o: /usr/local/include/sys/file.h /usr/local/include/sys/fcntl.h
+movecheck.o: /usr/local/include/sys/ioctl.h /usr/local/include/ioctls.h
+movecheck.o: /usr/local/include/sys/ttydefaults.h
+movecheck.o: /usr/local/include/sys/wait.h /usr/local/include/waitflags.h
+movecheck.o: /usr/local/include/waitstatus.h common.h vers.h legal.h
+movecheck.o: movecheck.h algcheck.h board.h gamedb.h utils.h multicol.h
+movecheck.o: network.h command.h variable.h playerdb.h
+multicol.o: stdinclude.h /usr/local/include/sys/types.h
+multicol.o: /usr/local/include/features.h /usr/local/include/sys/cdefs.h
+multicol.o: /usr/local/include/stubs.h /usr/local/include/gnu/types.h
+multicol.o: /usr/local/include/sys/time.h /usr/local/include/stddef.h
+multicol.o: /usr/local/include/strings.h /usr/local/include/string.h
+multicol.o: /usr/local/include/sys/dir.h /usr/local/include/dirent.h
+multicol.o: /usr/local/include/dirstream.h /usr/local/include/sys/stat.h
+multicol.o: /usr/local/include/statbuf.h /usr/local/include/stdio.h stdarg.h
+multicol.o: /usr/local/include/stdlib.h /usr/local/include/errno.h
+multicol.o: /usr/local/include/errnos.h /usr/local/include/sys/socket.h
+multicol.o: /usr/local/include/netinet/in.h /usr/local/include/endian.h
+multicol.o: /usr/local/include/bytesex.h /usr/local/include/arpa/inet.h
+multicol.o: /usr/local/include/netdb.h /usr/local/include/unistd.h
+multicol.o: /usr/local/include/posix_opt.h /usr/local/include/confname.h
+multicol.o: /usr/local/include/ctype.h /usr/local/include/sys/errno.h
+multicol.o: /usr/local/include/signal.h /usr/local/include/sigset.h
+multicol.o: /usr/local/include/math.h /usr/local/include/huge_val.h
+multicol.o: /usr/local/include/__math.h /usr/local/include/sys/time.h
+multicol.o: /usr/local/include/sys/file.h /usr/local/include/sys/fcntl.h
+multicol.o: /usr/local/include/sys/ioctl.h /usr/local/include/ioctls.h
+multicol.o: /usr/local/include/sys/ttydefaults.h
+multicol.o: /usr/local/include/sys/wait.h /usr/local/include/waitflags.h
+multicol.o: /usr/local/include/waitstatus.h common.h vers.h legal.h
+multicol.o: multicol.h utils.h rmalloc.h
+network.o: stdinclude.h /usr/local/include/sys/types.h
+network.o: /usr/local/include/features.h /usr/local/include/sys/cdefs.h
+network.o: /usr/local/include/stubs.h /usr/local/include/gnu/types.h
+network.o: /usr/local/include/sys/time.h /usr/local/include/stddef.h
+network.o: /usr/local/include/strings.h /usr/local/include/string.h
+network.o: /usr/local/include/sys/dir.h /usr/local/include/dirent.h
+network.o: /usr/local/include/dirstream.h /usr/local/include/sys/stat.h
+network.o: /usr/local/include/statbuf.h /usr/local/include/stdio.h stdarg.h
+network.o: /usr/local/include/stdlib.h /usr/local/include/errno.h
+network.o: /usr/local/include/errnos.h /usr/local/include/sys/socket.h
+network.o: /usr/local/include/netinet/in.h /usr/local/include/endian.h
+network.o: /usr/local/include/bytesex.h /usr/local/include/arpa/inet.h
+network.o: /usr/local/include/netdb.h /usr/local/include/unistd.h
+network.o: /usr/local/include/posix_opt.h /usr/local/include/confname.h
+network.o: /usr/local/include/ctype.h /usr/local/include/sys/errno.h
+network.o: /usr/local/include/signal.h /usr/local/include/sigset.h
+network.o: /usr/local/include/math.h /usr/local/include/huge_val.h
+network.o: /usr/local/include/__math.h /usr/local/include/sys/time.h
+network.o: /usr/local/include/sys/file.h /usr/local/include/sys/fcntl.h
+network.o: /usr/local/include/sys/ioctl.h /usr/local/include/ioctls.h
+network.o: /usr/local/include/sys/ttydefaults.h /usr/local/include/sys/wait.h
+network.o: /usr/local/include/waitflags.h /usr/local/include/waitstatus.h
+network.o: /usr/local/include/arpa/telnet.h common.h vers.h legal.h utils.h
+network.o: multicol.h playerdb.h command.h variable.h network.h rmalloc.h
+network.o: timeseal.h
+playerdb.o: stdinclude.h /usr/local/include/sys/types.h
+playerdb.o: /usr/local/include/features.h /usr/local/include/sys/cdefs.h
+playerdb.o: /usr/local/include/stubs.h /usr/local/include/gnu/types.h
+playerdb.o: /usr/local/include/sys/time.h /usr/local/include/stddef.h
+playerdb.o: /usr/local/include/strings.h /usr/local/include/string.h
+playerdb.o: /usr/local/include/sys/dir.h /usr/local/include/dirent.h
+playerdb.o: /usr/local/include/dirstream.h /usr/local/include/sys/stat.h
+playerdb.o: /usr/local/include/statbuf.h /usr/local/include/stdio.h stdarg.h
+playerdb.o: /usr/local/include/stdlib.h /usr/local/include/errno.h
+playerdb.o: /usr/local/include/errnos.h /usr/local/include/sys/socket.h
+playerdb.o: /usr/local/include/netinet/in.h /usr/local/include/endian.h
+playerdb.o: /usr/local/include/bytesex.h /usr/local/include/arpa/inet.h
+playerdb.o: /usr/local/include/netdb.h /usr/local/include/unistd.h
+playerdb.o: /usr/local/include/posix_opt.h /usr/local/include/confname.h
+playerdb.o: /usr/local/include/ctype.h /usr/local/include/sys/errno.h
+playerdb.o: /usr/local/include/signal.h /usr/local/include/sigset.h
+playerdb.o: /usr/local/include/math.h /usr/local/include/huge_val.h
+playerdb.o: /usr/local/include/__math.h /usr/local/include/sys/time.h
+playerdb.o: /usr/local/include/sys/file.h /usr/local/include/sys/fcntl.h
+playerdb.o: /usr/local/include/sys/ioctl.h /usr/local/include/ioctls.h
+playerdb.o: /usr/local/include/sys/ttydefaults.h
+playerdb.o: /usr/local/include/sys/wait.h /usr/local/include/waitflags.h
+playerdb.o: /usr/local/include/waitstatus.h common.h vers.h legal.h command.h
+playerdb.o: variable.h comproc.h playerdb.h rmalloc.h utils.h multicol.h
+playerdb.o: network.h ficsmain.h config.h channel.h gamedb.h board.h
+playerdb.o: ratings.h
+ratings.o: stdinclude.h /usr/local/include/sys/types.h
+ratings.o: /usr/local/include/features.h /usr/local/include/sys/cdefs.h
+ratings.o: /usr/local/include/stubs.h /usr/local/include/gnu/types.h
+ratings.o: /usr/local/include/sys/time.h /usr/local/include/stddef.h
+ratings.o: /usr/local/include/strings.h /usr/local/include/string.h
+ratings.o: /usr/local/include/sys/dir.h /usr/local/include/dirent.h
+ratings.o: /usr/local/include/dirstream.h /usr/local/include/sys/stat.h
+ratings.o: /usr/local/include/statbuf.h /usr/local/include/stdio.h stdarg.h
+ratings.o: /usr/local/include/stdlib.h /usr/local/include/errno.h
+ratings.o: /usr/local/include/errnos.h /usr/local/include/sys/socket.h
+ratings.o: /usr/local/include/netinet/in.h /usr/local/include/endian.h
+ratings.o: /usr/local/include/bytesex.h /usr/local/include/arpa/inet.h
+ratings.o: /usr/local/include/netdb.h /usr/local/include/unistd.h
+ratings.o: /usr/local/include/posix_opt.h /usr/local/include/confname.h
+ratings.o: /usr/local/include/ctype.h /usr/local/include/sys/errno.h
+ratings.o: /usr/local/include/signal.h /usr/local/include/sigset.h
+ratings.o: /usr/local/include/math.h /usr/local/include/huge_val.h
+ratings.o: /usr/local/include/__math.h /usr/local/include/sys/time.h
+ratings.o: /usr/local/include/sys/file.h /usr/local/include/sys/fcntl.h
+ratings.o: /usr/local/include/sys/ioctl.h /usr/local/include/ioctls.h
+ratings.o: /usr/local/include/sys/ttydefaults.h /usr/local/include/sys/wait.h
+ratings.o: /usr/local/include/waitflags.h /usr/local/include/waitstatus.h
+ratings.o: common.h vers.h legal.h playerdb.h command.h variable.h ratings.h
+ratings.o: gamedb.h board.h comproc.h lists.h ficsmain.h config.h utils.h
+ratings.o: multicol.h
+rmalloc.o: stdinclude.h /usr/local/include/sys/types.h
+rmalloc.o: /usr/local/include/features.h /usr/local/include/sys/cdefs.h
+rmalloc.o: /usr/local/include/stubs.h /usr/local/include/gnu/types.h
+rmalloc.o: /usr/local/include/sys/time.h /usr/local/include/stddef.h
+rmalloc.o: /usr/local/include/strings.h /usr/local/include/string.h
+rmalloc.o: /usr/local/include/sys/dir.h /usr/local/include/dirent.h
+rmalloc.o: /usr/local/include/dirstream.h /usr/local/include/sys/stat.h
+rmalloc.o: /usr/local/include/statbuf.h /usr/local/include/stdio.h stdarg.h
+rmalloc.o: /usr/local/include/stdlib.h /usr/local/include/errno.h
+rmalloc.o: /usr/local/include/errnos.h /usr/local/include/sys/socket.h
+rmalloc.o: /usr/local/include/netinet/in.h /usr/local/include/endian.h
+rmalloc.o: /usr/local/include/bytesex.h /usr/local/include/arpa/inet.h
+rmalloc.o: /usr/local/include/netdb.h /usr/local/include/unistd.h
+rmalloc.o: /usr/local/include/posix_opt.h /usr/local/include/confname.h
+rmalloc.o: /usr/local/include/ctype.h /usr/local/include/sys/errno.h
+rmalloc.o: /usr/local/include/signal.h /usr/local/include/sigset.h
+rmalloc.o: /usr/local/include/math.h /usr/local/include/huge_val.h
+rmalloc.o: /usr/local/include/__math.h /usr/local/include/sys/time.h
+rmalloc.o: /usr/local/include/sys/file.h /usr/local/include/sys/fcntl.h
+rmalloc.o: /usr/local/include/sys/ioctl.h /usr/local/include/ioctls.h
+rmalloc.o: /usr/local/include/sys/ttydefaults.h /usr/local/include/sys/wait.h
+rmalloc.o: /usr/local/include/waitflags.h /usr/local/include/waitstatus.h
+rmalloc.o: common.h vers.h legal.h
+utils.o: stdinclude.h /usr/local/include/sys/types.h
+utils.o: /usr/local/include/features.h /usr/local/include/sys/cdefs.h
+utils.o: /usr/local/include/stubs.h /usr/local/include/gnu/types.h
+utils.o: /usr/local/include/sys/time.h /usr/local/include/stddef.h
+utils.o: /usr/local/include/strings.h /usr/local/include/string.h
+utils.o: /usr/local/include/sys/dir.h /usr/local/include/dirent.h
+utils.o: /usr/local/include/dirstream.h /usr/local/include/sys/stat.h
+utils.o: /usr/local/include/statbuf.h /usr/local/include/stdio.h stdarg.h
+utils.o: /usr/local/include/stdlib.h /usr/local/include/errno.h
+utils.o: /usr/local/include/errnos.h /usr/local/include/sys/socket.h
+utils.o: /usr/local/include/netinet/in.h /usr/local/include/endian.h
+utils.o: /usr/local/include/bytesex.h /usr/local/include/arpa/inet.h
+utils.o: /usr/local/include/netdb.h /usr/local/include/unistd.h
+utils.o: /usr/local/include/posix_opt.h /usr/local/include/confname.h
+utils.o: /usr/local/include/ctype.h /usr/local/include/sys/errno.h
+utils.o: /usr/local/include/signal.h /usr/local/include/sigset.h
+utils.o: /usr/local/include/math.h /usr/local/include/huge_val.h
+utils.o: /usr/local/include/__math.h /usr/local/include/sys/time.h
+utils.o: /usr/local/include/sys/file.h /usr/local/include/sys/fcntl.h
+utils.o: /usr/local/include/sys/ioctl.h /usr/local/include/ioctls.h
+utils.o: /usr/local/include/sys/ttydefaults.h /usr/local/include/sys/wait.h
+utils.o: /usr/local/include/waitflags.h /usr/local/include/waitstatus.h
+utils.o: common.h vers.h legal.h utils.h multicol.h playerdb.h command.h
+utils.o: variable.h network.h rmalloc.h config.h
+variable.o: stdinclude.h /usr/local/include/sys/types.h
+variable.o: /usr/local/include/features.h /usr/local/include/sys/cdefs.h
+variable.o: /usr/local/include/stubs.h /usr/local/include/gnu/types.h
+variable.o: /usr/local/include/sys/time.h /usr/local/include/stddef.h
+variable.o: /usr/local/include/strings.h /usr/local/include/string.h
+variable.o: /usr/local/include/sys/dir.h /usr/local/include/dirent.h
+variable.o: /usr/local/include/dirstream.h /usr/local/include/sys/stat.h
+variable.o: /usr/local/include/statbuf.h /usr/local/include/stdio.h stdarg.h
+variable.o: /usr/local/include/stdlib.h /usr/local/include/errno.h
+variable.o: /usr/local/include/errnos.h /usr/local/include/sys/socket.h
+variable.o: /usr/local/include/netinet/in.h /usr/local/include/endian.h
+variable.o: /usr/local/include/bytesex.h /usr/local/include/arpa/inet.h
+variable.o: /usr/local/include/netdb.h /usr/local/include/unistd.h
+variable.o: /usr/local/include/posix_opt.h /usr/local/include/confname.h
+variable.o: /usr/local/include/ctype.h /usr/local/include/sys/errno.h
+variable.o: /usr/local/include/signal.h /usr/local/include/sigset.h
+variable.o: /usr/local/include/math.h /usr/local/include/huge_val.h
+variable.o: /usr/local/include/__math.h /usr/local/include/sys/time.h
+variable.o: /usr/local/include/sys/file.h /usr/local/include/sys/fcntl.h
+variable.o: /usr/local/include/sys/ioctl.h /usr/local/include/ioctls.h
+variable.o: /usr/local/include/sys/ttydefaults.h
+variable.o: /usr/local/include/sys/wait.h /usr/local/include/waitflags.h
+variable.o: /usr/local/include/waitstatus.h common.h vers.h legal.h
+variable.o: variable.h playerdb.h command.h utils.h multicol.h ficsmain.h
+variable.o: config.h rmalloc.h board.h
+eco.o: stdinclude.h /usr/local/include/sys/types.h
+eco.o: /usr/local/include/features.h /usr/local/include/sys/cdefs.h
+eco.o: /usr/local/include/stubs.h /usr/local/include/gnu/types.h
+eco.o: /usr/local/include/sys/time.h /usr/local/include/stddef.h
+eco.o: /usr/local/include/strings.h /usr/local/include/string.h
+eco.o: /usr/local/include/sys/dir.h /usr/local/include/dirent.h
+eco.o: /usr/local/include/dirstream.h /usr/local/include/sys/stat.h
+eco.o: /usr/local/include/statbuf.h /usr/local/include/stdio.h stdarg.h
+eco.o: /usr/local/include/stdlib.h /usr/local/include/errno.h
+eco.o: /usr/local/include/errnos.h /usr/local/include/sys/socket.h
+eco.o: /usr/local/include/netinet/in.h /usr/local/include/endian.h
+eco.o: /usr/local/include/bytesex.h /usr/local/include/arpa/inet.h
+eco.o: /usr/local/include/netdb.h /usr/local/include/unistd.h
+eco.o: /usr/local/include/posix_opt.h /usr/local/include/confname.h
+eco.o: /usr/local/include/ctype.h /usr/local/include/sys/errno.h
+eco.o: /usr/local/include/signal.h /usr/local/include/sigset.h
+eco.o: /usr/local/include/math.h /usr/local/include/huge_val.h
+eco.o: /usr/local/include/__math.h /usr/local/include/sys/time.h
+eco.o: /usr/local/include/sys/file.h /usr/local/include/sys/fcntl.h
+eco.o: /usr/local/include/sys/ioctl.h /usr/local/include/ioctls.h
+eco.o: /usr/local/include/sys/ttydefaults.h /usr/local/include/sys/wait.h
+eco.o: /usr/local/include/waitflags.h /usr/local/include/waitstatus.h board.h
+eco.o: gamedb.h command.h variable.h playerdb.h gameproc.h utils.h multicol.h
+eco.o: common.h vers.h legal.h config.h
+timeseal.o: /usr/local/include/stdio.h /usr/local/include/stdlib.h
+timeseal.o: /usr/local/include/features.h /usr/local/include/sys/cdefs.h
+timeseal.o: /usr/local/include/stubs.h /usr/local/include/stddef.h
+timeseal.o: /usr/local/include/errno.h /usr/local/include/errnos.h
+timeseal.o: /usr/local/include/strings.h /usr/local/include/string.h
+timeseal.o: playerdb.h command.h variable.h network.h board.h gamedb.h
+timeseal.o: /usr/local/include/sys/time.h
diff --git a/FICS/Makefile.bsd b/FICS/Makefile.bsd
new file mode 100644
index 0000000..525f579
--- /dev/null
+++ b/FICS/Makefile.bsd
@@ -0,0 +1,50 @@
+# Makefile for FICS
+# This was originally written under NeXTSTEP
+#
+
+# Turn on optimization
+# With optimization on you may get errors during compiling.
+OPTFLAGS=
+
+# Turn on debugging
+#DEBUGFLAGS=-DDEBUG
+
+# Anything else you care to flag?
+#OTHERFLAGS=-traditional-cpp -Wall -g
+OTHERFLAGS=-Wall -g
+
+# Which architectures are you compiling for? (NeXTSTEP)
+# ARCHFLAGS=-arch m68k -arch i386
+
+PROGRAM=fics
+
+
+CC=gcc
+#cc doesn't seem to be supported at present.
+
+CFLAGS=${SYSFLAG} ${OPTFLAGS} ${DEBUGFLAGS} ${OTHERFLAGS} ${ARCHFLAGS}
+LNFLAGS=-lm -lcompat -lcrypt
+
+# dfree is temporary, for debugging only, and works only if you are using
+# the GNU C library --mann
+#MORESRCS=dfree.c
+#MOREOBJS=dfree.o
+#you may have to hash out the above two
+
+# This is just for versions distributed with timeseal.
+MORESRCS=timeseal.c
+MOREOBJS=timeseal.o
+
+# Define the system below that you are compiling on.
+# Set the flags for your system
+
+SYSFLAG=-DNETBSD -DTIMESEAL
+
+# The following is needed when using gcc and glibc, as on caissa.
+#MAKEDEPMORECFLAGS= -I/usr/local/include -I/usr/local/lib/gcc-lib/sparc-sun-sunos4.1.3/2.6.3/include
+
+include ./Makefile.common
+# include ./Makedist
+
+#run makedepend to fill out the info below
+# DO NOT DELETE THIS LINE -- make depend depends on it.
diff --git a/FICS/Makefile.clean b/FICS/Makefile.clean
new file mode 100644
index 0000000..525f579
--- /dev/null
+++ b/FICS/Makefile.clean
@@ -0,0 +1,50 @@
+# Makefile for FICS
+# This was originally written under NeXTSTEP
+#
+
+# Turn on optimization
+# With optimization on you may get errors during compiling.
+OPTFLAGS=
+
+# Turn on debugging
+#DEBUGFLAGS=-DDEBUG
+
+# Anything else you care to flag?
+#OTHERFLAGS=-traditional-cpp -Wall -g
+OTHERFLAGS=-Wall -g
+
+# Which architectures are you compiling for? (NeXTSTEP)
+# ARCHFLAGS=-arch m68k -arch i386
+
+PROGRAM=fics
+
+
+CC=gcc
+#cc doesn't seem to be supported at present.
+
+CFLAGS=${SYSFLAG} ${OPTFLAGS} ${DEBUGFLAGS} ${OTHERFLAGS} ${ARCHFLAGS}
+LNFLAGS=-lm -lcompat -lcrypt
+
+# dfree is temporary, for debugging only, and works only if you are using
+# the GNU C library --mann
+#MORESRCS=dfree.c
+#MOREOBJS=dfree.o
+#you may have to hash out the above two
+
+# This is just for versions distributed with timeseal.
+MORESRCS=timeseal.c
+MOREOBJS=timeseal.o
+
+# Define the system below that you are compiling on.
+# Set the flags for your system
+
+SYSFLAG=-DNETBSD -DTIMESEAL
+
+# The following is needed when using gcc and glibc, as on caissa.
+#MAKEDEPMORECFLAGS= -I/usr/local/include -I/usr/local/lib/gcc-lib/sparc-sun-sunos4.1.3/2.6.3/include
+
+include ./Makefile.common
+# include ./Makedist
+
+#run makedepend to fill out the info below
+# DO NOT DELETE THIS LINE -- make depend depends on it.
diff --git a/FICS/Makefile.common b/FICS/Makefile.common
new file mode 100644
index 0000000..36b2857
--- /dev/null
+++ b/FICS/Makefile.common
@@ -0,0 +1,67 @@
+#this is the common part of the Makefile regardless of which system
+# you compile on.
+
+#need to sort out lame including of all objects in all programs - DAV
+
+
+SRCS= adminproc.c algcheck.c board.c channel.c command.c talkproc.c \
+ comproc.c matchproc.c fics_addplayer.c ficsmain.c formula.c \
+ gamedb.c gameproc.c obsproc.c legal.c lists.c makerank.c \
+ movecheck.c multicol.c network.c playerdb.c ratings.c \
+ rmalloc.c utils.c variable.c vers.c eco.c rating_conv.c \
+ shutdown.c ${MORESRCS}
+
+OBJS= network.o lists.o formula.o playerdb.o command.o talkproc.o \
+ comproc.o matchproc.o movecheck.o ratings.o gamedb.o channel.o \
+ utils.o rmalloc.o legal.o vers.o variable.o board.o gameproc.o \
+ algcheck.o adminproc.o multicol.o eco.o rating_conv.o shutdown.o \
+ obsproc.o
+
+VERSION=echo 'char SGS_VERS[]=""; char VERS_NUM[]="1.6.2"; char COMP_DATE[]="'`date`'";'
+
+PROGRAM=fics
+ADDPLAYER=fics_addplayer
+#DELPLAYER=fics_delplayer
+#MAILSERVER=fics_mailproc
+#MAILPROC=mailproc
+MAKERANK=makerank
+#MAKEDEPEND=makedepend
+MAKEDEPEND=mkdep
+#DEPFLAG=--
+DEPFLAG=
+
+#all: ${PROGRAM} ${MAILSERVER} ${ADDPLAYER} ${DELPLAYER} ${MAILPROC}
+all: ${PROGRAM} ${MAKERANK} ${ADDPLAYER}
+
+${PROGRAM}: ficsmain.o $(OBJS) $(MOREOBJS)
+ $(CC) ficsmain.o $(CFLAGS) $(OBJS) $(MOREOBJS) $(LNFLAGS) -o ${PROGRAM}
+
+#${MAILSERVER}: fics_mailproc.o $(OBJS)
+# $(CC) fics_mailproc.o $(CFLAGS) $(OBJS) $(LNFLAGS) -o ${MAILSERVER}
+
+${ADDPLAYER}: fics_addplayer.o $(OBJS) $(MOREOBJS)
+ $(CC) fics_addplayer.o $(CFLAGS) $(OBJS) $(MOREOBJS) $(LNFLAGS) -o ${ADDPLAYER}
+
+#${DELPLAYER}: fics_delplayer.o $(OBJS)
+# $(CC) fics_delplayer.o $(CFLAGS) $(OBJS) $(LNFLAGS) -o ${DELPLAYER}
+
+#${MAILPROC}: mailproc.o lock.o network.o rmalloc.o
+# $(CC) mailproc.o lock.o network.o rmalloc.o $(CFLAGS) -o ${MAILPROC}
+# $(CC) mailproc.o lock.o network.o rmalloc.o memmove.o $(CFLAGS) -o ${MAILPROC}
+
+${MAKERANK}: makerank.o
+ $(CC) makerank.o $(CFLAGS) $(LNFLAGS) -o ${MAKERANK}
+
+vers.c:
+ ${VERSION} >vers.c
+
+depend:
+ $(MAKEDEPEND) ${DEPFLAG} ${CFLAGS} ${MAKEDEPMORECFLAGS} ${DEPFLAG} ${SRCS}
+
+clean:
+# rm -f *.o ${PROGRAM} ${MAILSERVER} ${ADDPLAYER} ${MAILPROC} ${DELPLAYER} *~ vers.c
+ rm -f *.o ${PROGRAM} ${ADDPLAYER} ${MAKERANK} *~ vers.c
+
+ @ echo "Project cleaned."
+
+
diff --git a/FICS/Makefile.common.bak b/FICS/Makefile.common.bak
new file mode 100644
index 0000000..a522690
--- /dev/null
+++ b/FICS/Makefile.common.bak
@@ -0,0 +1,61 @@
+#this is the common part of the Makefile regardless of which system
+# you compile on.
+
+#need to sort out lame including of all objects in all programs - DAV
+
+
+SRCS=adminproc.c algcheck.c board.c channel.c command.c talkproc.c \
+ comproc.c matchproc.c fics_addplayer.c ficsmain.c formula.c \
+ gamedb.c gameproc.c legal.c lists.c makerank.c \
+ movecheck.c multicol.c network.c playerdb.c ratings.c \
+ rmalloc.c utils.c variable.c vers.c eco.c rating_conv.c ${MORESRCS}
+
+OBJS=network.o lists.o formula.o playerdb.o command.o talkproc.o \
+ comproc.o matchproc.o movecheck.o ratings.o gamedb.o channel.o \
+ utils.o rmalloc.o legal.o vers.o variable.o board.o gameproc.o \
+ algcheck.o adminproc.o multicol.o eco.o rating_conv.o
+
+VERSION=echo 'char SGS_VERS[]=""; char VERS_NUM[]="1.3.3"; char COMP_DATE[]="'`date`'";'
+
+PROGRAM=fics
+ADDPLAYER=fics_addplayer
+#DELPLAYER=fics_delplayer
+#MAILSERVER=fics_mailproc
+#MAILPROC=mailproc
+MAKERANK=makerank
+
+#all: ${PROGRAM} ${MAILSERVER} ${ADDPLAYER} ${DELPLAYER} ${MAILPROC}
+all: ${PROGRAM} ${MAKERANK} ${ADDPLAYER}
+
+${PROGRAM}: ficsmain.o $(OBJS) $(MOREOBJS)
+ $(CC) ficsmain.o $(CFLAGS) $(OBJS) $(MOREOBJS) $(LNFLAGS) -o ${PROGRAM}
+
+#${MAILSERVER}: fics_mailproc.o $(OBJS)
+# $(CC) fics_mailproc.o $(CFLAGS) $(OBJS) $(LNFLAGS) -o ${MAILSERVER}
+
+${ADDPLAYER}: fics_addplayer.o $(OBJS) $(MOREOBJS)
+ $(CC) fics_addplayer.o $(CFLAGS) $(OBJS) $(MOREOBJS) $(LNFLAGS) -o ${ADDPLAYER}
+
+#${DELPLAYER}: fics_delplayer.o $(OBJS)
+# $(CC) fics_delplayer.o $(CFLAGS) $(OBJS) $(LNFLAGS) -o ${DELPLAYER}
+
+#${MAILPROC}: mailproc.o lock.o network.o rmalloc.o
+# $(CC) mailproc.o lock.o network.o rmalloc.o $(CFLAGS) -o ${MAILPROC}
+# $(CC) mailproc.o lock.o network.o rmalloc.o memmove.o $(CFLAGS) -o ${MAILPROC}
+
+${MAKERANK}: makerank.o
+ $(CC) makerank.o $(CFLAGS) $(LNFLAGS) -o ${MAKERANK}
+
+vers.c:
+ ${VERSION} >vers.c
+
+depend:
+ makedepend -- ${CFLAGS} ${MAKEDEPMORECFLAGS} -- ${SRCS}
+
+clean:
+# rm -f *.o ${PROGRAM} ${MAILSERVER} ${ADDPLAYER} ${MAILPROC} ${DELPLAYER} *~ vers.c
+ rm -f *.o ${PROGRAM} ${ADDPLAYER} ${MAKERANK} *~ vers.c
+
+ @ echo "Project cleaned."
+
+
diff --git a/FICS/Makefile.in b/FICS/Makefile.in
new file mode 100644
index 0000000..7b3bff1
--- /dev/null
+++ b/FICS/Makefile.in
@@ -0,0 +1,110 @@
+##
+## fics - An internet chess server.
+## Copyright (C) 1993 Richard V. Nash
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+##
+## This is the Makefile template; see machine dependant config.h for
+## more configuration options.
+
+
+SHELL = /bin/sh
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+
+##
+## Where to install FICS
+##
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+bindir = ${exec_prefix}/bin
+
+
+##
+## Binaries and Options to use
+##
+CC = @CC@
+CFLAGS = @CFLAGS@ -DTIMESEAL -I${srcdir}
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+LIBS = @LIBS@
+LNFLAGS = @LDFLAGS@ ${LIBS}
+
+
+##
+## Version information
+##
+VERSION=echo 'char SGS_VERS[]=""; char VERS_NUM[]="1.2.3";'
+
+
+SRCS=adminproc.c algcheck.c board.c channel.c command.c talkproc.c comproc.c \
+ fics_addplayer.c ficsmain.c formula.c gamedb.c gameproc.c \
+ get_tcp_conn.c legal.c lists.c makerank.c \
+ movecheck.c multicol.c network.c playerdb.c ratings.c \
+ rmalloc.c utils.c variable.c vers.c eco.c matchproc.c \
+ timeseal.c ${MORESRCS}
+
+OBJS=network.o lists.o formula.o playerdb.o command.o talkproc.o comproc.o movecheck.o \
+ ratings.o gamedb.o channel.o utils.o rmalloc.o legal.o vers.o variable.o \
+ board.o gameproc.o algcheck.o adminproc.o \
+ get_tcp_conn.o multicol.o eco.o matchproc.o timeseal.o ${MOREOBJS}
+
+VERSION=echo 'char SGS_VERS[]=""; char VERS_NUM[]="1.2.18"; char COMP_DATE[]="'`date`'";'
+
+PROGRAM=fics
+ADDPLAYER=fics_addplayer
+#DELPLAYER=fics_delplayer
+#MAILSERVER=fics_mailproc
+#MAILPROC=mailproc
+MAKERANK=makerank
+
+ALL= ${PROGRAM} ${ADDPLAYER} ${MAKERANK}
+
+#all: ${PROGRAM} ${MAILSERVER} ${ADDPLAYER} ${DELPLAYER} ${MAILPROC}
+all: ${ALL}
+
+${PROGRAM}: ficsmain.o $(OBJS)
+ $(CC) ficsmain.o $(CFLAGS) $(OBJS) $(LNFLAGS) -o ${PROGRAM}
+
+#${MAILSERVER}: fics_mailproc.o $(OBJS)
+# $(CC) fics_mailproc.o $(CFLAGS) $(OBJS) $(LNFLAGS) -o ${MAILSERVER}
+
+${ADDPLAYER}: fics_addplayer.o $(OBJS)
+ $(CC) fics_addplayer.o $(CFLAGS) $(OBJS) $(LNFLAGS) -o ${ADDPLAYER}
+
+#${DELPLAYER}: fics_delplayer.o $(OBJS)
+# $(CC) fics_delplayer.o $(CFLAGS) $(OBJS) $(LNFLAGS) -o ${DELPLAYER}
+
+#${MAILPROC}: mailproc.o lock.o network.o rmalloc.o
+# $(CC) mailproc.o lock.o network.o rmalloc.o $(CFLAGS) -o ${MAILPROC}
+# $(CC) mailproc.o lock.o network.o rmalloc.o memmove.o $(CFLAGS) -o ${MAILPROC}
+
+${MAKERANK}: makerank.o
+ $(CC) makerank.o $(CFLAGS) $(LNFLAGS) -o ${MAKERANK}
+
+vers.c:
+ ${VERSION} >vers.c
+
+depend:
+ makedepend -- ${CFLAGS} ${MAKEDEPMORECFLAGS} -- ${SRCS}
+
+clean:
+ rm -f *.o ${ALL} *~ vers.c
+ @ echo "Project cleaned."
+
+distclean: clean
+ rm -rf Makefile autoconfig.h config.h config.status config.cache \
+ config.log
+
+install:
+ for f in ${ALL}; do ${INSTALL} $$f ${bindir}; done
diff --git a/FICS/Makefile.in.orig b/FICS/Makefile.in.orig
new file mode 100644
index 0000000..7b3bff1
--- /dev/null
+++ b/FICS/Makefile.in.orig
@@ -0,0 +1,110 @@
+##
+## fics - An internet chess server.
+## Copyright (C) 1993 Richard V. Nash
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+##
+## This is the Makefile template; see machine dependant config.h for
+## more configuration options.
+
+
+SHELL = /bin/sh
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+
+##
+## Where to install FICS
+##
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+bindir = ${exec_prefix}/bin
+
+
+##
+## Binaries and Options to use
+##
+CC = @CC@
+CFLAGS = @CFLAGS@ -DTIMESEAL -I${srcdir}
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+LIBS = @LIBS@
+LNFLAGS = @LDFLAGS@ ${LIBS}
+
+
+##
+## Version information
+##
+VERSION=echo 'char SGS_VERS[]=""; char VERS_NUM[]="1.2.3";'
+
+
+SRCS=adminproc.c algcheck.c board.c channel.c command.c talkproc.c comproc.c \
+ fics_addplayer.c ficsmain.c formula.c gamedb.c gameproc.c \
+ get_tcp_conn.c legal.c lists.c makerank.c \
+ movecheck.c multicol.c network.c playerdb.c ratings.c \
+ rmalloc.c utils.c variable.c vers.c eco.c matchproc.c \
+ timeseal.c ${MORESRCS}
+
+OBJS=network.o lists.o formula.o playerdb.o command.o talkproc.o comproc.o movecheck.o \
+ ratings.o gamedb.o channel.o utils.o rmalloc.o legal.o vers.o variable.o \
+ board.o gameproc.o algcheck.o adminproc.o \
+ get_tcp_conn.o multicol.o eco.o matchproc.o timeseal.o ${MOREOBJS}
+
+VERSION=echo 'char SGS_VERS[]=""; char VERS_NUM[]="1.2.18"; char COMP_DATE[]="'`date`'";'
+
+PROGRAM=fics
+ADDPLAYER=fics_addplayer
+#DELPLAYER=fics_delplayer
+#MAILSERVER=fics_mailproc
+#MAILPROC=mailproc
+MAKERANK=makerank
+
+ALL= ${PROGRAM} ${ADDPLAYER} ${MAKERANK}
+
+#all: ${PROGRAM} ${MAILSERVER} ${ADDPLAYER} ${DELPLAYER} ${MAILPROC}
+all: ${ALL}
+
+${PROGRAM}: ficsmain.o $(OBJS)
+ $(CC) ficsmain.o $(CFLAGS) $(OBJS) $(LNFLAGS) -o ${PROGRAM}
+
+#${MAILSERVER}: fics_mailproc.o $(OBJS)
+# $(CC) fics_mailproc.o $(CFLAGS) $(OBJS) $(LNFLAGS) -o ${MAILSERVER}
+
+${ADDPLAYER}: fics_addplayer.o $(OBJS)
+ $(CC) fics_addplayer.o $(CFLAGS) $(OBJS) $(LNFLAGS) -o ${ADDPLAYER}
+
+#${DELPLAYER}: fics_delplayer.o $(OBJS)
+# $(CC) fics_delplayer.o $(CFLAGS) $(OBJS) $(LNFLAGS) -o ${DELPLAYER}
+
+#${MAILPROC}: mailproc.o lock.o network.o rmalloc.o
+# $(CC) mailproc.o lock.o network.o rmalloc.o $(CFLAGS) -o ${MAILPROC}
+# $(CC) mailproc.o lock.o network.o rmalloc.o memmove.o $(CFLAGS) -o ${MAILPROC}
+
+${MAKERANK}: makerank.o
+ $(CC) makerank.o $(CFLAGS) $(LNFLAGS) -o ${MAKERANK}
+
+vers.c:
+ ${VERSION} >vers.c
+
+depend:
+ makedepend -- ${CFLAGS} ${MAKEDEPMORECFLAGS} -- ${SRCS}
+
+clean:
+ rm -f *.o ${ALL} *~ vers.c
+ @ echo "Project cleaned."
+
+distclean: clean
+ rm -rf Makefile autoconfig.h config.h config.status config.cache \
+ config.log
+
+install:
+ for f in ${ALL}; do ${INSTALL} $$f ${bindir}; done
diff --git a/FICS/Makefile.next b/FICS/Makefile.next
new file mode 100644
index 0000000..90de8f0
--- /dev/null
+++ b/FICS/Makefile.next
@@ -0,0 +1,28 @@
+# Makefile for FICS
+# This was originally written under NeXTSTEP
+#
+# Define the system below that you are compiling on.
+SYSFLAG=-DSYSTEM_NEXT
+
+# Set the flags for your system
+
+# Turn on optimization
+# OPTFLAGS=-O
+
+# Turn on debugging
+DEBUGFLAGS=-g -DDEBUG
+
+# Anything else you care to flag?
+OTHERFLAGS=-Wall
+
+# Which architectures are you compiling for? (NeXTSTEP)
+# ARCHFLAGS=-arch m68k -arch i386
+
+PROGRAM=fics
+
+
+CC=cc
+CFLAGS=${SYSFLAG} ${OPTFLAGS} ${DEBUGFLAGS} ${OTHERFLAGS} ${ARCHFLAGS}
+LNFLAGS=-lm
+
+include ./Makefile.common
diff --git a/FICS/Makefile.sgi b/FICS/Makefile.sgi
new file mode 100644
index 0000000..7b7b202
--- /dev/null
+++ b/FICS/Makefile.sgi
@@ -0,0 +1,40 @@
+# Makefile for FICS
+# This was originally written under NeXTSTEP
+#
+# Define the system below that you are compiling on.
+SYSFLAG=-DSGI
+# Set the flags for your system
+
+# Turn on optimization
+# With optimization on you may get errors during compiling.
+OPTFLAGS=
+
+# Turn on debugging
+#DEBUGFLAGS=-DDEBUG
+
+# Anything else you care to flag?
+#OTHERFLAGS=-traditional-cpp -Wall -g
+OTHERFLAGS=-Wall -g
+
+# Which architectures are you compiling for? (NeXTSTEP)
+# ARCHFLAGS=-arch m68k -arch i386
+
+PROGRAM=fics
+
+
+CC=gcc
+#cc doesn't seem to be supported at present.
+
+CFLAGS=${SYSFLAG} ${OPTFLAGS} ${DEBUGFLAGS} ${OTHERFLAGS} ${ARCHFLAGS}
+LNFLAGS=-lm
+
+
+# The following is needed when using gcc and glibc, as on caissa.
+
+MAKEDEPMORECFLAGS= -I/usr/local/include -I/usr/local/lib/gcc-lib/sparc-sun-sunos4.1.3/2.6.3/include
+
+include ./Makefile.common
+# include ./Makedist
+
+#run makedepend to fill out the info below
+# DO NOT DELETE THIS LINE -- make depend depends on it.
diff --git a/FICS/Makefile.sun4 b/FICS/Makefile.sun4
new file mode 100644
index 0000000..a82e6b3
--- /dev/null
+++ b/FICS/Makefile.sun4
@@ -0,0 +1,45 @@
+# Makefile for FICS
+# This was originally written under NeXTSTEP
+#
+# Define the system below that you are compiling on.
+SYSFLAG=-DSYSTEM_SUN4
+# Set the flags for your system
+
+# Turn on optimization
+# With optimization on you may get errors during compiling.
+OPTFLAGS=
+
+# Turn on debugging
+#DEBUGFLAGS=-DDEBUG
+
+# Anything else you care to flag?
+#OTHERFLAGS=-traditional-cpp -Wall -g
+OTHERFLAGS=-Wall -g
+
+# Which architectures are you compiling for? (NeXTSTEP)
+# ARCHFLAGS=-arch m68k -arch i386
+
+PROGRAM=fics
+
+
+CC=gcc
+#cc doesn't seem to be supported at present.
+
+CFLAGS=${SYSFLAG} ${OPTFLAGS} ${DEBUGFLAGS} ${OTHERFLAGS} ${ARCHFLAGS}
+LNFLAGS=-lm
+
+# dfree is temporary, for debugging only, and works only if you are using
+# the GNU C library --mann
+#MORESRCS=dfree.c
+#MOREOBJS=dfree.o
+#you may have to hash out the above two
+
+# The following is needed when using gcc and glibc, as on caissa.
+
+MAKEDEPMORECFLAGS= -I/usr/local/include -I/usr/local/lib/gcc-lib/sparc-sun-sunos4.1.3/2.6.3/include
+
+include ./Makefile.common
+# include ./Makedist
+
+#run makedepend to fill out the info below
+# DO NOT DELETE THIS LINE -- make depend depends on it.
diff --git a/FICS/Makefile.sun5 b/FICS/Makefile.sun5
new file mode 100644
index 0000000..a87dd9b
--- /dev/null
+++ b/FICS/Makefile.sun5
@@ -0,0 +1,40 @@
+# Makefile for FICS
+# This was originally written under NeXTSTEP
+#
+# Define the system below that you are compiling on.
+SYSFLAG=-DSYSTEM_SUN5
+# Set the flags for your system
+
+# Turn on optimization
+# With optimization on you may get errors during compiling.
+OPTFLAGS=
+
+# Turn on debugging
+#DEBUGFLAGS=-DDEBUG
+
+# Anything else you care to flag?
+#OTHERFLAGS=-traditional-cpp -Wall -g
+OTHERFLAGS=-Wall -g
+
+# Which architectures are you compiling for? (NeXTSTEP)
+# ARCHFLAGS=-arch m68k -arch i386
+
+PROGRAM=fics
+
+
+CC=gcc
+#cc doesn't seem to be supported at present.
+
+CFLAGS=${SYSFLAG} ${OPTFLAGS} ${DEBUGFLAGS} ${OTHERFLAGS} ${ARCHFLAGS}
+LNFLAGS=-lm
+
+
+# The following is needed when using gcc and glibc, as on caissa.
+
+MAKEDEPMORECFLAGS= -I/usr/local/include -I/usr/local/lib/gcc-lib/sparc-sun-sunos4.1.3/2.6.3/include
+
+include ./Makefile.common
+# include ./Makedist
+
+#run makedepend to fill out the info below
+# DO NOT DELETE THIS LINE -- make depend depends on it.
diff --git a/FICS/Makefile.sunos-glibc b/FICS/Makefile.sunos-glibc
new file mode 100644
index 0000000..48b70d5
--- /dev/null
+++ b/FICS/Makefile.sunos-glibc
@@ -0,0 +1,51 @@
+# Makefile for FICS
+# This was originally written under NeXTSTEP
+#
+SYSFLAG=-DSYSTEM_SUN4 -DTIMESEAL
+
+# Turn on optimization
+# With optimization on you may get errors during compiling.
+OPTFLAGS=
+
+# Turn on debugging
+DEBUGFLAGS=-DDEBUG
+
+# Anything else you care to flag?
+#OTHERFLAGS=-traditional-cpp -Wall -g
+OTHERFLAGS=-Wall -g
+
+# Which architectures are you compiling for? (NeXTSTEP)
+# ARCHFLAGS=-arch m68k -arch i386
+
+PROGRAM=fics
+
+
+CC=gcc
+#cc doesn't seem to be supported at present.
+
+CFLAGS=${SYSFLAG} ${OPTFLAGS} ${DEBUGFLAGS} ${OTHERFLAGS} ${ARCHFLAGS} -I/usr/local/glibc-include
+LNFLAGS=-lm -L/usr/local/lib/glibc
+
+# dfree is temporary, for debugging only, and works only if you are using
+# the GNU C library --mann
+#MORESRCS=dfree.c
+#MOREOBJS=dfree.o
+#you may have to hash out the above two
+
+# This is just for versions distributed with timeseal.
+MORESRCS=timeseal.c
+MOREOBJS=timeseal.o
+
+# Define the system below that you are compiling on.
+# Set the flags for your system
+
+SYSFLAG=-DSYSTEM_SUN4 -DTIMESEAL
+
+# The following is needed when using gcc and glibc, as on caissa.
+MAKEDEPMORECFLAGS= -I/usr/local/include -I/usr/local/lib/gcc-lib/sparc-sun-sunos4.1.3/2.6.3/include
+
+include ./Makefile.common
+# include ./Makedist
+
+#run makedepend to fill out the info below
+# DO NOT DELETE THIS LINE -- make depend depends on it.
diff --git a/FICS/Makefile.ultrix b/FICS/Makefile.ultrix
new file mode 100644
index 0000000..2b5f688
--- /dev/null
+++ b/FICS/Makefile.ultrix
@@ -0,0 +1,31 @@
+# Makefile for FICS
+# This was originally written under NeXTSTEP
+#
+# Define the system below that you are compiling on.
+# SYSFLAG=-DSYSTEM_NEXT
+SYSFLAG=-DSYSTEM_ULTRIX
+
+# Set the flags for your system
+
+# Turn on optimization
+# There seems to be an optimization bug which screws up
+# ratings calculation.
+# OPTFLAGS=-O
+
+# Turn on debugging
+DEBUGFLAGS=-DDEBUG
+
+# Anything else you care to flag?
+# OTHERFLAGS=-Wall
+
+# Which architectures are you compiling for? (NeXTSTEP)
+#ARCHFLAGS=-arch m68k -arch i386
+
+PROGRAM=fics
+
+
+CC=cc
+CFLAGS=${SYSFLAG} ${OPTFLAGS} ${DEBUGFLAGS} ${OTHERFLAGS} ${ARCHFLAGS}
+LNFLAGS=-lm
+
+include ./Makefile.common
diff --git a/FICS/Makefile.usl b/FICS/Makefile.usl
new file mode 100644
index 0000000..0af7418
--- /dev/null
+++ b/FICS/Makefile.usl
@@ -0,0 +1,29 @@
+# Makefile for FICS
+# This was originally written under NeXTSTEP
+#
+# Define the system below that you are compiling on.
+SYSFLAG=-DSYSTEM_USL
+
+# Set the flags for your system
+
+# Turn on optimization
+# With optimization on you may get errors during compiling.
+# OPTFLAGS=-O
+
+# Turn on debugging
+#DEBUGFLAGS=-g -DDEBUG
+
+# Anything else you care to flag?
+#OTHERFLAGS=-Wall
+
+# Which architectures are you compiling for? (NeXTSTEP)
+# ARCHFLAGS=-arch m68k -arch i386
+
+PROGRAM=fics
+
+
+CC=cc
+CFLAGS=${SYSFLAG} ${OPTFLAGS} ${DEBUGFLAGS} ${OTHERFLAGS} ${ARCHFLAGS}
+LNFLAGS=-lm
+
+include ./Makefile.common
diff --git a/FICS/README b/FICS/README
new file mode 100644
index 0000000..5a76069
--- /dev/null
+++ b/FICS/README
@@ -0,0 +1,103 @@
+README file for FICS - Free Internet Chess Server
+
+Please report any changes back to chess@ics.onenet.net. The code is
+very volatile right now, so keep gratuitous changes to a minimum. DO NOT
+send mail about the code to Richard Nash. He is no longer working on the
+FICS project. (We thank him for all his past work!)
+
+HOW TO START YOUR CHESS SERVER
+
+A. INTRODUCTION
+ First off, you should be familiar with programming in C and with the
+nuances of the system on which you will be running the server. The
+documentation is sparse, so often the code is the best place to go to find
+the answers.
+
+B. Compiling the code
+ 1. cd FICS
+ 2. Edit the file config.h to set the directories where the program's
+ data can be found.
+ 3. Copy the Makefile for your system to Makefile. At the time of writing
+ this there are only a few machines represented, so you may have to edit.
+ If you create a new Makefile that works on your machine, please mail
+ it back to the above address so that we can include it in the distribution.
+ NB. You may have to # out dfree.c and dfree.o in the makefile - they are
+ for debug perposes only (sun4).
+ 4. type 'makedepend' type 'make'
+ 5. If it doesn't compile, well...do your best. If you modify the code to
+ get it to compile on your machine, use #ifdef's wisely and send the
+ changes back to us so that we can include them in the distribution.
+ Also check the code for already defined ifdefs.
+ 6. Once code is built, start the server with "fics", the default port
+ is 5000. To specify another, use "fics -p #". login as AdminGuy
+ with no password and set his password. :-) Good luck.
+
+
+C. Registering with the ratings server.
+*** THERE IS NO RATINGS SERVER IN OPERATION AT PRESENT ***
+** The procedures below were for FICS 1.0 and are now obsolete **
+** Distributed ratings may come in a later version **
+
+ If you want to be hooked in with the distributed ratings server, and I
+hope everyone does, send mail to XXXXXXXXXXX to get your password and to
+confirm to and from email addresses.
+
+D. Configuring for the ratings server.
+ Once you have your email address, the servers email address and your secret
+code, copy the file config/hostinfo.client.format to config/hostinfo.client.
+Then edit that file and replace the appropriate entry with the correct
+information. Here is what an example should look like.
+
+client fics_client@foobar.com
+fics_server@XXXXXXX.XXX MyPassword 0 0
+
+ The next part is a little tricky, and you may need root privileges on your
+machine to do it. The goal here is this... To get the contents of the mail
+sent to fics_client piped into the fics_mailproc program. If there is no way
+you are getting root privileges, go right to way #4.
+
+1. One way is to create a mail alias that does it.
+
+ Here is the pertanent line from my aliases file.
+
+fics_client: "|/Users/nash/Source/FICS/FICS-distrib/fics_mailproc client"
+
+The '|' character causes mail to fics_client to get sent to the program that
+follows. The 'client' parameter to the fics_mailproc program is important
+because the fics_mailproc program is two-faced. It can run as a client or as
+the server.
+
+ Once you are set up, the ratings server should send you accounts for all
+of the registered network players.
+
+2. Another way is to create a user called fics_client with the same UID as
+ the user you will be running FICS as. This is to allow fics and
+ fics_mailproc to have the same uid. Then, in /etc/crontab put an entry
+ that periodically calls the 'mailproc' program. Here is my entry:
+0,15,30,45 * * * * nash /Users/nash/Source/FICS/FICS-distrib/mailproc /usr/spool/mail/fics_client "/Users/nash/Source/FICS/FICS-distrib/fics_mailproc client"
+
+3. Yet another way is to simply run mailproc by hand when you want mail to
+ be picked up. In a C-shell this works:
+while (1)
+ /Users/nash/Source/FICS/FICS-distrib/mailproc /usr/spool/mail/fics_client "/Users/nash/Source/FICS/FICS-distrib/fics_mailproc client"
+ sleep 900
+end
+
+4. There is yet one more way to set up a connection to the rating's server
+ if there is no way to get the mail client set up. It may be lest reliable
+ than the mail way of doing things because it doesn't have store and
+ forward capability if network errors occur during transmission. But it
+ will probably work. The mailproc program can be a daemon wait for
+ connections on a port and then send what it gets to a program. For
+ example:
+mailproc 5001 "/Users/nash/Source/FICS/FICS-distrib/fics_mailproc client"
+ Will wait for the server to connect and send the mail directly. Of course
+ you will need to set this up with the server administrator so that he
+ knows to send directly rather than through mail.
+
+E. Adding local players.
+ Of course this is your server and you have the write to include or exclude
+players as you like. To add a player user the fics_addplayer function or
+use addplayer when logged in as an ADMIN. To remove a player, just put *
+in the password field of their file. They won't be able to log in.
+
diff --git a/FICS/README-UPGRADE_FROM_1.2.3 b/FICS/README-UPGRADE_FROM_1.2.3
new file mode 100644
index 0000000..8a5d044
--- /dev/null
+++ b/FICS/README-UPGRADE_FROM_1.2.3
@@ -0,0 +1,13 @@
+If you are running 1.2.3, congratulations on 1) putting up with it until now
+and 2) getting a very fine upgrade! :-)
+
+BEWARE: You must remove all login logs from the data/stats/player-data
+directory. They have changed in format greatly. They will cause a crash. :-)
+
+ cd FICSDIR/data/stats/player_data
+ rm */*.logons
+ cd ..
+ rm logons
+
+Should do it.
+
diff --git a/FICS/README.NEW b/FICS/README.NEW
new file mode 100644
index 0000000..6b58291
--- /dev/null
+++ b/FICS/README.NEW
@@ -0,0 +1,172 @@
+
+Versions up to 1.2.16 and between 1.2.23 and 1.3.2 are approximate.
+Information on changes may not be exact or complete.
+
+1.6.2 News bug fixed (Marsalis), caused crash and corrupted index file.
+ ECO brought back (Sparky), bug fixed for use with scratch games
+ (uninitialized moveList caused crash). Examine can now
+ be used to load board positions (Sparky). Works with
+ scratch games also.
+
+1.6.1 News rewritten (Marsalis), Much faster code. Some crash
+ protection code (writing player files after every 'set'
+ command for example) has been supressed for speed.
+ Bug in new playerfile code fixed (sanity checks added to
+ avoid crashes).
+
+1.6 Playerfile formats completely recoded (DAV) new and old
+ formats recognized. New format is written always. The
+ new format has expanded functionallity, more compact form,
+ and much faster processing.
+
+1.5.2 Ghosts hopefully killed; no more timeseal sitting;
+ Oldmoves implementation changes; game structure shrunk.
+ fics_hostname now takes it's contents from SERVER_NAME,
+ which should nwo be a DNS resolvable hostname.
+ fics_addplayer new account message modernized to include
+ brief instructions on how to connect to the server and
+ and warn about the use of anauthorzied duplicate accounts.
+ both adminproc.c and fics_addplayer modified.
+
+1.5.1 Fixes in journal commands, some bughouse bugs.
+
+1.5.0 Journals allowed. Some fixes for NULL v. NULL games.
+
+1.4.0 Initial bughouse version.
+
+1.3.3 clearing and showing individual messages.
+
+1.3.2 fixes to open file bugs;
+ clearing and showing messages from a particular player.
+
+1.3.1 kibitz level; tell/whisper bugs and similar fixed;
+ noplay and gnotify lists.
+
+1.3.0 new admin structure for coordination, responding to suggestions, etc;
+ got rid of annoyingly large version numbers :-).
+
+1.2.23-1.2.24 ???
+
+1.2.22 Examining of history games supported.
+
+1.2.21 Language variable; can't refresh private games;
+ Got rid of "You are not observing" message when you are observing.
+ New news interface; cnewsi, cnewsf, and news show on startup.
+ Percent of life stats.
+
+1.2.20 New help interface; index command.
+
+1.2.19 Examine mode (without history game support)
+ ECO and NIC codes now supported for history games and for
+ interactive reporting on games in process.
+ PGN format now supported for email of game scores.
+ Direct sendmail support provided with the SENDMAILPROG
+ definition in config.h, which overrides any MAILPROGRAM
+ definition. Provides for more reliable and efficient operation
+ under heavy load.
+
+1.2.18 variable mailmess: mails messages to you.
+ messages give the user feedback on what was sent.
+ simul commands simallabort, simadjourn, simalladjourn
+ (aliases saa sadj saadj)
+ simabort given alias sa
+ cshout and it prompts fixed
+ it gives better feedback
+ can restart simuls (or untimed games in simul form) with simmatch
+ game format reduced in size
+1.2.17 asetpasswd now sends mail to the player telling him the new
+ password.
+ extra info in up for admins
+ annunreg seen by all admins
+ hostinfo and lock removed as was fics_delplayer, fics_mailproc
+ and mail_proc.
+ see messages from one person, clear messages from a person
+ More simul bug fixes.
+ simprev (alias sp), goboard (alias go, goto), gonum commands.
+ allow admin to reset RD; ratings may be set with 0 games played.
+1.2.16 Many simul bugs fixed, one fatal bug corrected which caused
+ a crash during a simul.
+ unobserve command.
+ MAILPROGRAM replaced by "sendmail -t" and appropriate "To:"
+ and subject headers. This should help with blank mail
+ message problem. mailhelp and mailsource still depend on MAILPROGRAM
+ stuff added for SUN5 compilation
+ best info shown in who
+1.2.15 Simul bug fixed - losing connection led to a crash
+ adjudicate written (forcedraw, forcewin, forceres commands removed)
+ hostinfo removed from addplayer
+1.2.14 Simul code made operational.
+ games list ordered.
+ tally code disabled
+1.2.13 forcedraw, forcewin, forceres commands
+ asetv command replaces pose
+1.2.12 bug fixes.
+ pose weakened.
+1.2.11 First stable version of FICS. unnotify and unalias had big problems
+ that led to crashes and corrupt player files.
+ siteinfo removed - caused infinite lag
+ mailmess
+1.2.10 bug fixes, max length enforced in alias to stop a crash.
+1.2.9 autoboot out code added.
+1.2.8 aliases fixed, @ added, can alias system commands
+ flagging without material claims a draw, not a win.
+1.2.7 filter list operational.
+ multicol.c used to drop 2048 bytes
+1.2.6 rank and best, makerank
+ time for next shout shown.
+ bug fix to timelog
+ login/logouts logged
+ addcomment and showcomment
+1.2.5 Summon written.
+ ban list
+ (TD) in who
+ board styles fixed
+ shout quotas, it and shout modified
+ admin command to toggle (*)
+ fixes to up
+ admins can observe anything
+1.2.4 td list
+ qtell, getpi, commands for TD
+ asetblitz, asetstd
+ news,anews
+ wild chess made rated, best shows wild, new file
+ asetwild
+ next
+ SGI compilation possible.
+1.2.3 lists
+ (FM) (GM) (IM) (C) shown in who
+ raisedead given a second argument to rename a player
+ ahelp, info written
+ help files split into help and info
+ shutdown fixed
+1.2.2 internal version
+1.2.1 bug fixes.
+ admin gods can only use pose.
+ siteinfo shown
+1.2 notifiedby flag
+ remplayer, asethandle, raisedead written
+ nuke improved
+ query disabled
+1.1 who bug fixed - caused a crash if more than 10 players on.
+ ++++ shown in who for unreg players
+ early version of nuke.
+ (*) shown if level 10 admin
+ level 60 given title Assistant superuser.
+ who A, who l, who U, who R
+ Look of announce and other stuff improved.
+1.0 Original Nash code.
+-----------------------------------------------------------------------------
+Here is a more stable, hopefully more portable release of the
+FICS source code with many features added.
+
+People who have helped improve this from previous releases
+(March 1st 1995 onward) include (FICS names):
+DAV, foxbat, grimm, Hawk, hersco, loon, mann, Shane, Sparky, vek
+SORRY if you feel you should have been on this list but were forgotten.
+
+And Most thanks to red (Richard Nash) for the original code!
+ And Micheal Moore for the original idea of an ICS.
+
+-- Uploaded by Shane Hudson (Shane on FICS).
+Thanks to everyone for their support of FICS.
+
diff --git a/FICS/README.timeseal b/FICS/README.timeseal
new file mode 100644
index 0000000..3696fdf
--- /dev/null
+++ b/FICS/README.timeseal
@@ -0,0 +1,16 @@
+/*
+
+ The timeseal package must NEVER be distributed without
+ permission from Henrik Gram <u940456@daimi.aau.dk>
+
+ It's security is based SOLELY on keeping the crypt alg.
+ secret.
+
+ Both the timeseal server and client code was written
+ entirely by Henrik Gram.
+
+ Only the FICS sites at chess.onenet.net and *.daimi.aau.dk
+ are allowed to have it and use it - and NEVER charge for
+ its use nor for the distribution of the timeseal client.
+
+*/
diff --git a/FICS/README_LEGAL b/FICS/README_LEGAL
new file mode 100644
index 0000000..655d207
--- /dev/null
+++ b/FICS/README_LEGAL
@@ -0,0 +1,23 @@
+fics - An internet chess server.
+
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+
+You may also contact the author(s) at
+ Richard Nash: email - nash@visus.com
+ phone - (412)-488-3600
+
diff --git a/FICS/README_Nash b/FICS/README_Nash
new file mode 100644
index 0000000..9f6fa1b
--- /dev/null
+++ b/FICS/README_Nash
@@ -0,0 +1,37 @@
+From nash@nomos.com Tue May 30 07:56:37 1995
+Content-Type: text/plain
+Mime-Version: 1.0 (NeXT Mail 3.3 v118.2)
+From: "Richard V. Nash" <nash@nomos.com>
+Date: Tue, 30 May 95 08:52:10 -0400
+To: danke%daimi.aau.dk.kris%dgate.org.chess%onenet.net.romeo@dcs.warwick.ac.uk
+Subject: Please remove nash@nomos.com and visus.com from help files
+Content-Length: 1130
+Status: RO
+X-Lines: 25
+
+Hello Chess Server Administrators,
+
+ I, nash@nomos.com, am getting dozens of emails everyday inquiring about
+chess servers. I would like to stop this. Could you please remove any
+reference to nash@nomos.com or nash@visus.com from the help files associated
+with FICS. This is in both your running systems and versions of the code that
+is being passed around or placed in ftp'able directories.
+
+ There are also references to ftp.visus.com in help files. These should also
+be removed.
+
+ Could you also pass this message along to anybody that you know who may
+have a copy of FICS but who I've missed on my search for FICS's.
+
+ That said, thanks to all of you for taking the FICS code and making it a
+success! I'm so happy that my work to keep chess servers available and free
+did not fail.
+
+Rich
+-----------------------+---------------------------+
+| Richard V. Nash | NOMOS Corporation |
+| nash@nomos.com | Suite 400 |
+| Tel. (412)-934-5477 | 2591 Wexford-Bayne Road |
+| Fax. (412)-934-5488 | Sewickley, PA 15143 |
++----------------------+---------------------------+
+
diff --git a/FICS/acconfig.h b/FICS/acconfig.h
new file mode 100644
index 0000000..a7f3dd8
--- /dev/null
+++ b/FICS/acconfig.h
@@ -0,0 +1,49 @@
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+
+ This file contains descriptive text for the C preprocessor macros
+ that are missing in the original acconfig.h of the autoconf
+ distribution, but used in the configure.in of the FICS distribution.
+
+ It is used *only* by the autoheader program contained in the
+ autoconf distribution.
+
+
+ Leave the following blank line there!! Autoheader needs it. */
+
+
+/* Define this to be the return value of the time() function. */
+#undef time_t
+
+/* Undefine, if your compiler doesn't support prototypes */
+#undef HAVE_PROTOTYPES
+
+/* Define, if you have a statfs() function which fills a struct
+ statfs, declared in sys/vfs.h. (NeXTStep) */
+#undef HAVE_STATFS_FILLING_STRUCT_STATFS
+
+/* Define, if you have a statfs() function which fills a struct
+ fs_data, declared in sys/mount.h. (Ultrix) */
+#undef HAVE_STATFS_FILLING_STRUCT_FS_DATA
+
+/* Define, if you have crypt() and it is declared in either
+ crypt.h or unistd.h. */
+#undef HAVE_CRYPT_DECLARED
+
+/* Define this to be tv_usec, if your struct rusage has
+ a member ru_utime.tv_usec.
+ Define this to be tv_nsec, if your struct rusage has
+ a member ru_utime.tv_nsec. */
+#undef TV_USEC
diff --git a/FICS/acconfig.h.orig b/FICS/acconfig.h.orig
new file mode 100644
index 0000000..a7f3dd8
--- /dev/null
+++ b/FICS/acconfig.h.orig
@@ -0,0 +1,49 @@
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+
+ This file contains descriptive text for the C preprocessor macros
+ that are missing in the original acconfig.h of the autoconf
+ distribution, but used in the configure.in of the FICS distribution.
+
+ It is used *only* by the autoheader program contained in the
+ autoconf distribution.
+
+
+ Leave the following blank line there!! Autoheader needs it. */
+
+
+/* Define this to be the return value of the time() function. */
+#undef time_t
+
+/* Undefine, if your compiler doesn't support prototypes */
+#undef HAVE_PROTOTYPES
+
+/* Define, if you have a statfs() function which fills a struct
+ statfs, declared in sys/vfs.h. (NeXTStep) */
+#undef HAVE_STATFS_FILLING_STRUCT_STATFS
+
+/* Define, if you have a statfs() function which fills a struct
+ fs_data, declared in sys/mount.h. (Ultrix) */
+#undef HAVE_STATFS_FILLING_STRUCT_FS_DATA
+
+/* Define, if you have crypt() and it is declared in either
+ crypt.h or unistd.h. */
+#undef HAVE_CRYPT_DECLARED
+
+/* Define this to be tv_usec, if your struct rusage has
+ a member ru_utime.tv_usec.
+ Define this to be tv_nsec, if your struct rusage has
+ a member ru_utime.tv_nsec. */
+#undef TV_USEC
diff --git a/FICS/adminproc.c b/FICS/adminproc.c
new file mode 100644
index 0000000..ed96d4a
--- /dev/null
+++ b/FICS/adminproc.c
@@ -0,0 +1,1596 @@
+/*
+ fics - An internet chess server
+ Copyright (c) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ Continued development of this software is done by the GNU ICS
+ development team. Contact <chess@caissa.onenet.net> with questions.
+
+
+ adminproc.c - All administrative commands and related functions */
+
+
+#include "stdinclude.h"
+#include "common.h"
+#include "network.h"
+#include "adminproc.h"
+#include "command.h"
+#include "playerdb.h"
+#include "gamedb.h"
+#include "gameproc.h"
+#include "obsproc.h"
+#include "ratings.h"
+#include "utils.h"
+#include "rmalloc.h"
+#include "talkproc.h"
+#include "comproc.h"
+#include "multicol.h"
+#include <sys/param.h>
+
+#define PASSLEN 4
+
+PUBLIC int num_anews = -1;
+
+/*
+ * adjudicate
+ *
+ * Usage: adjudicate white_player black_player result
+ *
+ * Adjudicates a saved (stored) game between white_player and black_player.
+ * The result is one of: abort, draw, white, black. "Abort" cancels the game
+ * (no win, loss or draw), "white" gives white_player the win, "black" gives
+ * black_player the win, and "draw" gives a draw.
+ */
+PUBLIC int com_adjudicate(int p, param_list param)
+{
+ int wp, wconnected, bp, bconnected, g, inprogress, confused = 0;
+
+ ASSERT(parray[p].adminLevel >= ADMIN_ADMIN);
+ if (!FindPlayer(p, param[0].val.word, &wp, &wconnected))
+ return COM_OK;
+ if (!FindPlayer(p, param[1].val.word, &bp, &bconnected)) {
+ if (!wconnected)
+ player_remove(wp);
+ return COM_OK;
+ }
+
+ inprogress = ((parray[wp].game >=0) &&(parray[wp].opponent == bp));
+
+ if (inprogress) {
+ g = parray[wp].game;
+ } else {
+ g = game_new();
+ if (game_read(g, wp, bp) < 0) {
+ confused = 1;
+ pprintf(p, "There is no stored game %s vs. %s\n", parray[wp].name, parray[bp].name);
+ } else {
+ garray[g].white = wp;
+ garray[g].black = bp;
+ }
+ }
+ if (!confused) {
+ if (strstr("abort", param[2].val.word) != NULL) {
+ game_ended(g, WHITE, END_ADJABORT);
+
+ pcommand(p, "message %s Your game \"%s vs. %s\" has been aborted.",
+ parray[wp].name, parray[wp].name, parray[bp].name);
+
+ pcommand(p, "message %s Your game \"%s vs. %s\" has been aborted.",
+ parray[bp].name, parray[wp].name, parray[bp].name);
+ } else if (strstr("draw", param[2].val.word) != NULL) {
+ game_ended(g, WHITE, END_ADJDRAW);
+
+ pcommand(p, "message %s Your game \"%s vs. %s\" has been adjudicated "
+ "as a draw", parray[wp].name, parray[wp].name, parray[bp].name);
+
+ pcommand(p, "message %s Your game \"%s vs. %s\" has been adjudicated "
+ "as a draw", parray[bp].name, parray[wp].name, parray[bp].name);
+ } else if (strstr("white", param[2].val.word) != NULL) {
+ game_ended(g, WHITE, END_ADJWIN);
+
+ pcommand(p, "message %s Your game \"%s vs. %s\" has been adjudicated "
+ "as a win", parray[wp].name, parray[wp].name, parray[bp].name);
+
+ pcommand(p, "message %s Your game \"%s vs. %s\" has been adjudicated "
+ "as a loss", parray[bp].name, parray[wp].name, parray[bp].name);
+ } else if (strstr("black", param[2].val.word) != NULL) {
+ game_ended(g, BLACK, END_ADJWIN);
+ pcommand(p, "message %s Your game \"%s vs. %s\" has been adjudicated "
+ "as a loss", parray[wp].name, parray[wp].name, parray[bp].name);
+
+ pcommand(p, "message %s Your game \"%s vs. %s\" has been adjudicated "
+ "as a win", parray[bp].name, parray[wp].name, parray[bp].name);
+ } else {
+ confused = 1;
+ pprintf(p, "Result must be one of: abort draw white black\n");
+ }
+ }
+ if (!confused) {
+ pprintf(p, "Game adjudicated.\n");
+ if (!inprogress) {
+ game_delete(wp, bp);
+ } else {
+ return (COM_OK);
+ }
+ }
+ game_remove(g);
+ if (!wconnected)
+ player_remove(wp);
+ if (!bconnected)
+ player_remove(bp);
+ return COM_OK;
+}
+
+/*
+ * create_news_file: Creates either a general or and admin news
+ * file, depending upon the admin switch.
+ */
+PRIVATE int create_news_file(int p, param_list param, int admin)
+{
+ FILE *fp;
+ char filename[MAX_FILENAME_SIZE];
+
+ ASSERT(parray[p].adminLevel >= ADMIN_ADMIN);
+
+ if (admin) {
+ if (param[0].val.integer > num_anews)
+ pprintf(p, "There must be an admin news index #%d before you can create the file.", param[0].val.integer);
+ else {
+ sprintf(filename, "%s/adminnews.%d", news_dir, param[0].val.integer);
+ fp = fopen(filename, "w");
+ fprintf(fp, "%s\n", param[1].val.string);
+ fclose(fp);
+ }
+ } else {
+ if (param[0].val.integer > num_news)
+ pprintf(p, "There must be a news index #%d before you can create the file.", param[0].val.integer);
+ else {
+ sprintf(filename, "%s/news.%d", news_dir, param[0].val.integer);
+ fp = fopen(filename, "w");
+ fprintf(fp, "%s\n", param[1].val.string);
+ fclose(fp);
+ }
+ }
+
+ return COM_OK;
+}
+
+PRIVATE int add_item(char *new_item, char *filename)
+{
+ FILE *new_fp, *old_fp;
+ char tmp_file[MAX_FILENAME_SIZE];
+ char junk[MAX_LINE_SIZE];
+
+ sprintf(tmp_file, "%s/.tmp.idx", news_dir);
+ new_fp = fopen(tmp_file, "w");
+ old_fp = fopen(filename, "r");
+
+ if (!new_fp || !old_fp)
+ return 0;
+
+ fprintf(new_fp, "%s", new_item);
+ while (1) {
+ fgets(junk, MAX_LINE_SIZE, old_fp);
+ if (feof(old_fp))
+ break;
+ fprintf(new_fp, "%s", junk);
+ }
+ fclose(new_fp);
+ fclose(old_fp);
+ remove(filename);
+ rename(tmp_file, filename);
+
+ return 1;
+}
+
+/*
+ * create_news_index: Adds a new item to either the general or admin news
+ * index file, depending upon the admin switch.
+ */
+PRIVATE int create_news_index(int p, param_list param, int admin)
+{
+ char filename[MAX_FILENAME_SIZE];
+ char new_item[MAX_LINE_SIZE];
+
+ ASSERT(parray[p].adminLevel >= ADMIN_ADMIN);
+
+ if (admin) {
+ if (strlen(param[0].val.string) > 50)
+ pprintf(p, "Sorry, you must limit an index to 50 charaters! Admin news index not created.\n");
+ else {
+ num_anews++;
+ sprintf(new_item, "%d %d %s\n", (int) time(0), num_anews, param[0].val.string);
+ sprintf(filename, "%s/newadminnews.index", news_dir);
+ if (add_item(new_item, filename)) {
+ pprintf(p, "Index for admin news item #%d created.\n", num_anews);
+ pprintf(p, "Please use 'canewsf' to include more info.\n");
+ } else
+ pprintf(p, "Something went wrong creating item.\nNotify Marsalis.\n");
+ }
+ } else {
+ if (strlen(param[0].val.string) > 50)
+ pprintf(p, "Sorry, you must limit an index to 50 charaters! News index not created.\n");
+ else {
+ num_news++;
+ sprintf(filename, "%s/newnews.index", news_dir);
+ sprintf(new_item, "%d %d %s\n", (int) time(0), num_news, param[0].val.string);
+ if (add_item(new_item, filename)) {
+ pprintf(p, "Index for news item #%d created.\n", num_news);
+ pprintf(p, "Please use 'cnewsf' to include more info.\n");
+ } else
+ pprintf(p, "Something went wrong creating item.\nNotify Marsalis.\n");
+ }
+ }
+
+ return COM_OK;
+}
+
+/* cnewsi
+ *
+ * Usage: cnewsi message
+ *
+ *
+ * This command adds a new item to the news index. The message is limited to
+ * 45 characters for formating purposes. In essence, the news index works
+ * like a newspaper headline, giving the user enough information to know
+ * whether they should read the entire news file for that item. After
+ * creating the news item, the command reports the news item number along
+ * with a reminder to create a news file if necessary.
+ */
+PUBLIC int com_cnewsi(int p, param_list param)
+{
+ return create_news_index(p, param, 0);
+}
+
+/* cnewsf
+ *
+ * Usage: cnewsf # message
+ *
+ * This command allows you to add additional information about a news item
+ * that had previously been created using 'cnewsi'. The '#' is the number
+ * of the news index and 'message' is the additional text. You can also
+ * modify a previous news item description and thus update the news item
+ * easily.
+ */
+PUBLIC int com_cnewsf(int p, param_list param)
+{
+ return create_news_file(p, param, 0);
+}
+
+PUBLIC int com_canewsi(int p, param_list param)
+{
+ return create_news_index(p, param, 1);
+}
+
+PUBLIC int com_canewsf(int p, param_list param)
+{
+ return create_news_file(p, param, 1);
+}
+
+/*
+ * anews
+ *
+ *
+ * Usage: anews [#, all]
+ *
+ * This command is used to display anews (admin news) entries. The
+ * entries are numbered. "Anews #" displays that anews item. "Anews
+ * all" will display all items.
+ *
+ */
+PUBLIC int com_anews(int p, param_list param)
+{
+ FILE *fp;
+ char filename[MAX_FILENAME_SIZE];
+ char junk[MAX_LINE_SIZE];
+ char *junkp;
+ int crtime, found = 0;
+ char count[10];
+
+ sprintf(filename, "%s/newadminnews.index", news_dir);
+ fp = fopen(filename, "r");
+ if (!fp) {
+ fprintf(stderr, "Cant find news index.\n");
+ return COM_OK;
+ }
+
+ if (param[0].type == 0) {
+
+ /* no params - then just display index over news */
+ sprintf(filename, "%s/newadminnews.index", news_dir);
+ pprintf(p, "Index of recent admin news items:\n");
+ fgets(junk, MAX_LINE_SIZE, fp);
+ sscanf(junk, "%d %s", &crtime, count);
+ rscan_news2(fp, p, 9);
+ junkp = junk;
+ junkp = nextword(junkp);
+ junkp = nextword(junkp);
+ pprintf(p, "%3s (%s) %s", count, fix_time(strltime(&crtime)), junkp);
+ fclose(fp);
+
+ } else if ((param[0].type == TYPE_WORD) && !strcmp(param[0].val.word, "all")) {
+ /* param all - displays all news items */
+ pprintf(p, "Index of all admin news items:\n");
+ fgets(junk, MAX_LINE_SIZE, fp);
+ sscanf(junkp, "%d %s", &crtime, count);
+ rscan_news(fp, p, 0);
+ junkp = junk;
+ junkp = nextword(junkp);
+ junkp = nextword(junkp);
+ pprintf(p, "%3s (%s) %s", count, fix_time(strltime(&crtime)), junkp);
+ fclose(fp);
+
+ } else {
+
+ while (!feof(fp) && !found) {
+ junkp = junk;
+ fgets(junk, MAX_LINE_SIZE, fp);
+ if (feof(fp))
+ break;
+ if (strlen(junk) > 1) {
+ sscanf(junkp, "%d %s", &crtime, count);
+ if (!strcmp(count, param[0].val.word)) {
+ found = 1;
+ junkp = nextword(junkp);
+ junkp = nextword(junkp);
+ pprintf(p, "ANEWS %3s (%s) %s\n", count, fix_time(strltime(&crtime)),
+ junkp);
+ }
+ }
+ }
+ fclose(fp);
+ if (!found) {
+ pprintf(p, "Bad index number!\n");
+ return COM_OK;
+ }
+ /* file exists - show it */
+ sprintf(filename, "%s/adminnews.%s", news_dir, param[0].val.word);
+ fp = fopen(filename, "r");
+ if (!fp) {
+ pprintf(p, "No more info.\n");
+ return COM_OK;
+ }
+ fclose(fp);
+ sprintf(filename, "adminnews.%s", param[0].val.word);
+ if (psend_file(p, news_dir, filename) < 0) {
+ pprintf(p, "Internal error - couldn't send news file!\n");
+ }
+ }
+ return COM_OK;
+}
+
+PUBLIC int strcmpwild(char *mainstr, char *searchstr)
+{
+ int i;
+
+ if (strlen(mainstr) < strlen(searchstr))
+ return 1;
+ for (i = 0; i < strlen(mainstr); i++) {
+ if (searchstr[i] == '*')
+ return 0;
+ if (mainstr[i] != searchstr[i])
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * chkip
+ *
+ * Usage: chkip ip_address
+ *
+ * This command returns the names of all users currently logged on
+ * from a given IP address.
+ */
+PUBLIC int com_checkIP(int p, param_list param)
+{
+ char *ipstr = param[0].val.word;
+ int p1;
+
+ ASSERT(parray[p].adminLevel >= ADMIN_ADMIN);
+ pprintf(p, "Matches the following player(s): \n\n");
+ for (p1 = 0; p1 < p_num; p1++)
+ if (!strcmpwild(dotQuad(parray[p1].thisHost), ipstr) && (parray[p1].status != PLAYER_EMPTY))
+ pprintf(p, "%16.16s %s\n", parray[p1].name, dotQuad(parray[p1].thisHost));
+ return COM_OK;
+}
+
+PUBLIC int com_checkSOCKET(int p, param_list param)
+{
+ int fd = param[0].val.integer;
+ int p1, flag;
+
+ ASSERT(parray[p].adminLevel >= ADMIN_ADMIN);
+ flag = 0;
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (parray[p1].socket == fd) {
+ flag = 1;
+ pprintf(p, "Socket %d is used by %s\n", fd, parray[p1].name);
+ }
+ }
+ if (!flag)
+ pprintf(p, "Socket %d is unused!\n", fd);
+ return COM_OK;
+}
+
+/*
+ * chkpl
+ *
+ * Usage: chkpl handle
+ *
+ * This command displays server information about a given user. Items
+ * displayed are:
+ *
+ * number X in parray of size Y
+ * name
+ * login
+ * fullName
+ * emailAddress
+ * socket
+ * registered
+ * last_tell
+ * last_channel
+ * logon_time
+ * adminLevel
+ * thisHost
+ * lastHost
+ * num_comments
+ */
+PUBLIC int com_checkPLAYER(int p, param_list param)
+{
+ char *player = param[0].val.word;
+ int p1;
+
+ ASSERT(parray[p].adminLevel >= ADMIN_ADMIN);
+ p1 = player_search(p, param[0].val.word);
+ if (!p1)
+ return COM_OK;
+ if (p1 < 0) {
+ p1 = (-p1) - 1;
+ pprintf(p, "%s is not logged in.\n", player);
+ stolower(player);
+ pprintf(p, "name = %s\n", parray[p1].name);
+ pprintf(p, "login = %s\n", parray[p1].login);
+ pprintf(p, "fullName = %s\n", (parray[p1].fullName ? parray[p1].fullName : "(none)"));
+ pprintf(p, "emailAddress = %s\n", (parray[p1].emailAddress ? parray[p1].emailAddress : "(none)"));
+ pprintf(p, "adminLevel = %d\n", parray[p1].adminLevel);
+/* pprintf(p, "network_player = %d\n", parray[p1].network_player); */
+ pprintf(p, "lastHost = %s\n", dotQuad(parray[p1].lastHost));
+ pprintf(p, "num_comments = %d\n", parray[p1].num_comments);
+
+ player_remove(p1);
+ return COM_OK;
+ } else {
+ p1 = p1 - 1;
+ pprintf(p, "%s is number %d in parray of size %d\n", player, p1, p_num + 1);
+ pprintf(p, "name = %s\n", parray[p1].name);
+ pprintf(p, "login = %s\n", parray[p1].login);
+ pprintf(p, "fullName = %s\n", parray[p1].fullName ? parray[p1].fullName : "(none)");
+ pprintf(p, "emailAddress = %s\n", parray[p1].emailAddress ? parray[p1].emailAddress : "(none)");
+ pprintf(p, "socket = %d\n", parray[p1].socket);
+ pprintf(p, "registered = %d\n", parray[p1].registered);
+ pprintf(p, "last_tell = %d\n", parray[p1].last_tell);
+ pprintf(p, "last_channel = %d\n", parray[p1].last_channel);
+ pprintf(p, "logon_time = %s", ctime((time_t *) &parray[p1].logon_time));
+ pprintf(p, "adminLevel = %d\n", parray[p1].adminLevel);
+/* pprintf(p, "network_player = %d\n", parray[p1].network_player); */
+ pprintf(p, "thisHost = %s\n", dotQuad(parray[p1].thisHost));
+ pprintf(p, "lastHost = %s\n", dotQuad(parray[p1].lastHost));
+ pprintf(p, "num_comments = %d\n", parray[p1].num_comments);
+
+ }
+ return COM_OK;
+}
+
+/*
+ * chkts
+ *
+ * Usage: chkts
+ *
+ * This command displays all current users who are using timeseal.
+ */
+PUBLIC int com_checkTIMESEAL(int p, param_list param)
+{
+ int p1, count = 0;
+
+ ASSERT(parray[p].adminLevel >= ADMIN_ADMIN);
+ pprintf(p, "The following player(s) are using timeseal:\n\n");
+
+#ifdef TIMESEAL
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (parray[p1].status != PLAYER_EMPTY
+ && con[parray[p1].socket].timeseal) {
+ pprintf(p, "%s\n", parray[p1].name);
+ count++;
+ }
+ }
+ pprintf(p, "\nNumber of people using timeseal: %d\n", count);
+#endif
+
+ return COM_OK;
+}
+
+PUBLIC int com_checkGAME (int p,param_list param)
+{
+ int p1,g,link;
+ char tmp[10 + 1 + 7]; /* enough to store number 'black: ' and \0 */
+ int startTime;
+ multicol *m;
+ int found = 0;
+
+ ASSERT(parray[p].adminLevel >= ADMIN_ADMIN);
+
+ if (g_num == 0) {
+ pprintf (p,"No games are currently linked into the garray structure.\n");
+ return COM_OK;
+ }
+
+ if (param[0].type == TYPE_WORD) { /* a player name */
+ if ((p1 = player_find_part_login(param[0].val.word)) < 0) {
+ pprintf(p, "%s doesn't appear to be logged in.\n", param[0].val.word);
+ pprintf(p,"Searching through garray to find matching game numbers.\n");
+ pprintf(p,"Use chkgame <number> to view the results.\n");
+ m = multicol_start(g_num*2); /* Obviously no more than that */
+ for (g = 0; g < g_num; g++) {
+ multicol_store(m,tmp);
+ if (!(strcasecmp (garray[g].white_name,param[0].val.word))) {
+ sprintf (tmp,"White: %d",g);
+ multicol_store(m,tmp);
+ found = 1;
+ }
+ if (!(strcasecmp (garray[g].black_name,param[0].val.word))) {
+ sprintf (tmp,"Black: %d",g);
+ multicol_store(m,tmp);
+ found = 1;
+ }
+ }
+ if (found)
+ multicol_pprint(m,p,parray[p].d_width,2);
+ else
+ pprintf(p,"No matching games were found.\n");
+ multicol_end(m);
+ return COM_OK;
+ }
+
+ if ((g = parray[p1].game) < 0) {
+ pprintf(p,"%s doesn't appear to be playing a game.\n",parray[p1].name);
+ pprintf(p,"Searching through garray to find matching game numbers.\n");
+ pprintf(p,"Use chkgame <number> to view the results.\n");
+ m = multicol_start(g_num*2); /* Obviously no more than that */
+ for (g = 0; g < g_num; g++) {
+ if ((garray[g].white) == p1) {
+ sprintf (tmp,"White: %d",g);
+ multicol_store(m,tmp);
+ found = 1;
+ }
+ if ((garray[g].black) == p1) {
+ sprintf (tmp,"Black: %d",g);
+ multicol_store(m,tmp);
+ found = 1;
+ }
+ }
+ if (found)
+ multicol_pprint(m,p,parray[p].d_width,2);
+ else
+ pprintf (p,"No matching games were found.\n");
+ multicol_end(m);
+ return COM_OK;
+ }
+ } else {
+ if (((g = param[0].val.integer - 1) < 0) || (g >= g_num)) {
+ pprintf (p, "The current range of game numbers is 1 to %d.\n",g_num);
+ return COM_OK;
+ }
+ }
+ startTime = untenths(garray[g].timeOfStart);
+ pprintf (p,"Current stored info for game %d (garray[%d]):\n",g+1,g);
+ pprintf (p,"Initial white time: %d Initial white increment %d\n",
+ garray[g].wInitTime/600,garray[g].wIncrement/10);
+ pprintf (p,"Initial black time: %d Initial black increment %d\n",
+ garray[g].bInitTime/600,garray[g].bIncrement/10);
+ pprintf (p,"Time of starting: %s\n",strltime (&startTime));
+ pprintf (p,"Game is: %s (%d) vs. %s (%d)\n",garray[g].white_name,garray[g].white_rating,
+ garray[g].black_name,garray[g].black_rating);
+ pprintf (p,"White parray entry: %d Black parray entry %d\n",garray[g].white,garray[g].black);
+ if ((link = garray[g].link) >= 0) {
+ pprintf (p,"Bughouse linked to game: %d\n",garray[g].link + 1);
+ pprintf (p,"Partner is playing game: %s (%d) vs. %s (%d)\n",garray[link].white_name,garray[link].white_rating,
+ garray[link].black_name,garray[link].black_rating);
+ } else
+ pprintf (p,"Game is not bughouse or link to partner's game not found.\n");
+ pprintf (p,"Game is %s\n",(garray[g].rated) ? "rated" : "unrated");
+ pprintf (p,"Game is %s\n",(garray[g].private) ? "private" : "not private");
+ pprintf (p,"Games is of type %s\n",
+ garray[g].type == TYPE_UNTIMED ? "untimed" :
+ garray[g].type == TYPE_BLITZ ? "blitz" :
+ garray[g].type == TYPE_STAND ? "standard" :
+ garray[g].type == TYPE_NONSTANDARD ? "non-standard" :
+ garray[g].type == TYPE_WILD ? "wild" :
+ garray[g].type == TYPE_LIGHT ? "lightning" :
+ garray[g].type == TYPE_BUGHOUSE ? "bughouse" :
+ "Unknown - Error!");
+ pprintf (p,"%d halfmove(s) have been made\n",garray[g].numHalfMoves);
+ if (garray[g].status == GAME_ACTIVE)
+ game_update_time(g);
+ pprintf (p,"White's time %s Black's time ",
+ tenth_str((garray[g].wTime > 0 ? garray[g].wTime : 0), 0));
+ pprintf (p,"%s\n",
+ tenth_str((garray[g].bTime > 0 ? garray[g].bTime : 0), 0));
+ pprintf (p,"The clock is%sticking\n",
+ (garray[g].clockStopped || (garray[g].status != GAME_ACTIVE)) ? " not " : " ");
+ pprintf (p,"Game status: %s\n",
+ garray[g].status == GAME_EMPTY ? "GAME_EMPTY" :
+ garray[g].status == GAME_NEW ? "GAME_NEW" :
+ garray[g].status == GAME_ACTIVE ? "GAME_ACTIVE" :
+ garray[g].status == GAME_EXAMINE ? "GAME_EXAMINE" :
+ "Unknown - Error!");
+ return COM_OK;
+}
+
+/*
+ * remplayer
+ *
+ * Usage: remplayer name
+ *
+ * Removes an account. A copy of its files are saved under .rem.* which can
+ * be found in the appropriate directory (useful in case of an accident).
+ *
+ * The account's details, messages, games and logons are all saved as
+ * 'zombie' files. These zombie accounts are not listed in handles or
+ * totals.
+ */
+PUBLIC int com_remplayer(int p, param_list param)
+{
+ char *player = param[0].val.word;
+ char playerlower[MAX_LOGIN_NAME];
+ int p1, lookup;
+
+ ASSERT(parray[p].adminLevel >= ADMIN_ADMIN);
+ strcpy(playerlower, player);
+ stolower(playerlower);
+ p1 = player_new();
+ lookup = player_read(p1, playerlower);
+ if (!lookup) {
+ if ((parray[p].adminLevel <= parray[p1].adminLevel) && !player_ishead(p)) {
+ pprintf(p, "You can't remove an admin with a level higher than or equal to yourself.\n");
+ player_remove(p1);
+ return COM_OK;
+ }
+ }
+ player_remove(p1);
+ if (lookup) {
+ pprintf(p, "No player by the name %s is registered.\n", player);
+ return COM_OK;
+ }
+ if (player_find_bylogin(playerlower) >= 0) {
+ pprintf(p, "A player by that name is logged in.\n");
+ return COM_OK;
+ }
+ if (!player_kill(playerlower)) {
+ pprintf(p, "Player %s removed.\n", player);
+ UpdateRank(TYPE_BLITZ, NULL, NULL, player);
+ UpdateRank(TYPE_STAND, NULL, NULL, player);
+ UpdateRank(TYPE_WILD, NULL, NULL, player);
+ } else {
+ pprintf(p, "Remplayer failed.\n");
+ }
+ return COM_OK;
+}
+
+/*
+ * raisedead
+ *
+ * Usage: raisedead oldname [newname]
+ *
+ * Restores an account that has been previously removed using "remplayer".
+ * The zombie files from which it came are removed. Under most
+ * circumstances, you restore the account to the same handle it had
+ * before (oldname). However, in some circumstances you may need to
+ * restore the account to a different handle, in which case you include
+ * "newname" as the new handle. After "raisedead", you may need to use the
+ * "asetpasswd" command to get the player started again as a registered
+ * user, especially if the account had been locked
+ * by setting the password to *.
+ */
+PUBLIC int com_raisedead(int p, param_list param)
+{
+ char *player = param[0].val.word;
+ char playerlower[MAX_LOGIN_NAME], newplayerlower[MAX_LOGIN_NAME];
+
+ int p1, p2, lookup;
+
+ ASSERT(parray[p].adminLevel >= ADMIN_ADMIN);
+ strcpy(playerlower, player);
+ stolower(playerlower);
+ if (player_find_bylogin(playerlower) >= 0) {
+ pprintf(p, "A player by that name is logged in.\n");
+ pprintf(p, "Can't raise until they leave.\n");
+ return COM_OK;
+ }
+ p1 = player_new();
+ lookup = player_read(p1, playerlower);
+ player_remove(p1);
+ if (!lookup) {
+ pprintf(p, "A player by the name %s is already registered.\n", player);
+ pprintf(p, "Obtain a new handle for the dead person.\n");
+ pprintf(p, "Then use raisedead [oldname] [newname].\n");
+ return COM_OK;
+ }
+ if (param[1].type == TYPE_NULL) {
+ if (!player_raise(playerlower)) {
+ pprintf(p, "Player %s raised from dead.\n", player);
+
+ p1 = player_new();
+ if (!(lookup = player_read(p1, playerlower))) {
+ if (parray[p1].s_stats.rating > 0)
+ UpdateRank(TYPE_STAND, player, &parray[p1].s_stats, player);
+ if (parray[p1].b_stats.rating > 0)
+ UpdateRank(TYPE_BLITZ, player, &parray[p1].b_stats, player);
+ if (parray[p1].w_stats.rating > 0)
+ UpdateRank(TYPE_WILD, player, &parray[p1].w_stats, player);
+ }
+ player_remove(p1);
+ } else {
+ pprintf(p, "Raisedead failed.\n");
+ }
+ return COM_OK;
+ } else {
+ char *newplayer = param[1].val.word;
+ strcpy(newplayerlower, newplayer);
+ stolower(newplayerlower);
+ if (player_find_bylogin(newplayerlower) >= 0) {
+ pprintf(p, "A player by the requested name is logged in.\n");
+ pprintf(p, "Can't reincarnate until they leave.\n");
+ return COM_OK;
+ }
+ p2 = player_new();
+ lookup = player_read(p2, newplayerlower);
+ player_remove(p2);
+ if (!lookup) {
+ pprintf(p, "A player by the name %s is already registered.\n", player);
+ pprintf(p, "Obtain another new handle for the dead person.\n");
+ return COM_OK;
+ }
+ if (!player_reincarn(playerlower, newplayerlower)) {
+ pprintf(p, "Player %s reincarnated to %s.\n", player, newplayer);
+ p2 = player_new();
+ if (!(lookup = player_read(p2, newplayerlower))) {
+ strfree(parray[p2].name);
+ parray[p2].name = strdup(newplayer);
+ player_save(p2);
+ if (parray[p2].s_stats.rating > 0)
+ UpdateRank(TYPE_STAND, newplayer, &parray[p2].s_stats, newplayer);
+ if (parray[p2].b_stats.rating > 0)
+ UpdateRank(TYPE_BLITZ, newplayer, &parray[p2].b_stats, newplayer);
+ if (parray[p2].w_stats.rating > 0)
+ UpdateRank(TYPE_WILD, newplayer, &parray[p2].w_stats, newplayer);
+ }
+ player_remove(p2);
+ } else {
+ pprintf(p, "Raisedead failed.\n");
+ }
+ }
+ return COM_OK;
+}
+
+/*
+ * addplayer
+ *
+ * Usage: addplayer playername emailaddress realname
+ *
+ * Adds a local player to the server with the handle of "playername". For
+ * example:
+ *
+ * addplayer Hawk u940456@daimi.aau.dk Henrik Gram
+ */
+PUBLIC int com_addplayer(int p, param_list param)
+{
+ char text[2048];
+ char *newplayer = param[0].val.word;
+ char *newname = param[2].val.string;
+ char *newemail = param[1].val.word;
+ char password[PASSLEN + 1];
+ char newplayerlower[MAX_LOGIN_NAME];
+ char salt[3];
+ int p1, lookup;
+ int i;
+
+ ASSERT(parray[p].adminLevel >= ADMIN_ADMIN);
+ if (strlen(newplayer) >= MAX_LOGIN_NAME) {
+ pprintf(p, "Player name is too long\n");
+ return COM_OK;
+ }
+ if (strlen(newplayer) < 3) {
+ pprintf(p, "Player name is too short\n");
+ return COM_OK;
+ }
+ if (!alphastring(newplayer)) {
+ pprintf(p, "Illegal characters in player name. Only A-Za-z allowed.\n");
+ return COM_OK;
+ }
+ strcpy(newplayerlower, newplayer);
+ stolower(newplayerlower);
+ p1 = player_new();
+ lookup = player_read(p1, newplayerlower);
+ if (!lookup) {
+ pprintf(p, "A player by the name %s is already registered.\n", newplayerlower);
+ player_remove(p1);
+ return COM_OK;
+ }
+ parray[p1].name = strdup(newplayer);
+ parray[p1].login = strdup(newplayerlower);
+ parray[p1].fullName = strdup(newname);
+ parray[p1].emailAddress = strdup(newemail);
+ if (strcmp(newemail, "none")) {
+ for (i = 0; i < PASSLEN; i++) {
+ password[i] = 'a' + rand() % 26;
+ }
+ password[i] = '\0';
+ salt[0] = 'a' + rand() % 26;
+ salt[1] = 'a' + rand() % 26;
+ salt[2] = '\0';
+ parray[p1].passwd = strdup(crypt(password, salt));
+ } else {
+ password[0] = '\0';
+ parray[p1].passwd = strdup(password);
+ }
+ parray[p1].registered = 1;
+/* parray[p1].network_player = 0; */
+ parray[p1].rated = 1;
+ player_add_comment(p, p1, "Player added by addplayer.");
+ player_save(p1);
+ player_remove(p1);
+ pprintf(p, "Added: >%s< >%s< >%s< >%s<\n", newplayer, newname, newemail, password);
+ if (strcmp(newemail, "none")) {
+/*
+ sprintf(text, "\nYou have been added as a local player.\n\nLogin Name: "
+ "%s\nFull Name: %s\nEmail Address: %s\nInitial Password: "
+ "%s\n\nIf any of this information is incorrect, please "
+ "contact the administrator\nto get it corrected.\n\n"
+ "Please write down your password.\n\nRegards,\n\nThe FICS admins\n",
+ newplayer, newname, newemail, password);
+*/
+
+ sprintf(text, "\nYour player account has been created.\n\n"
+ "Login Name: %s\nFull Name: %s\nEmail Address: %s\nInitial Password: %s\n\n"
+ "If any of this information is incorrect, please contact the administrator\n"
+ "to get it corrected.\n\n"
+ "You may change your password with the password command on the the server.\n"
+ "\nPlease be advised that if this is an unauthorized duplicate account for\n"
+ "you, by using it you take the risk of being banned from accessing this\n"
+ "chess server.\n\nTo connect to the server and use this account:\n\n"
+ " telnet %s 5000\n\nand enter your handle name and password.\n\n"
+ "Regards,\n\nThe FICS admins\n",
+ newplayer, newname, newemail, password, fics_hostname);
+
+ mail_string_to_address(newemail, "FICS Account Created", text);
+ if ((p1 = player_find_part_login(newplayer)) >= 0) {
+ pprintf_prompt(p1, "\n\nYou are now registered! Confirmation together with\npassword is sent to your email address.\n\n");
+ return COM_OK;
+ }
+ return COM_OK;
+ } else {
+ if ((p1 = player_find_part_login(newplayer)) >= 0) {
+ pprintf_prompt(p1, "\n\nYou are now registered! Your have NO password!\n\n");
+ return COM_OK;
+ }
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_pose(int p, param_list param)
+{
+ int p1;
+
+ ASSERT(parray[p].adminLevel >= ADMIN_ADMIN);
+ if ((p1 = player_find_part_login(param[0].val.word)) < 0) {
+ pprintf(p, "%s is not logged in.\n", param[0].val.word);
+ return COM_OK;
+ }
+ if ((parray[p].adminLevel <= parray[p1].adminLevel) && !player_ishead(p)) {
+ pprintf(p, "You can only pose as players below your adminlevel.\n");
+ return COM_OK;
+ }
+ pprintf(p, "Command issued as %s\n", parray[p1].name);
+ pcommand(p1, "%s\n", param[1].val.string);
+ return COM_OK;
+}
+
+/*
+ * asetv
+ *
+ * Usage: asetv user instructions
+ *
+ * This command executes "set" instructions as if they had been made by the
+ * user indicated. For example, "asetv DAV shout 0" would set DAV's shout
+ * variable to 0.
+ */
+PUBLIC int com_asetv(int p, param_list param)
+{
+ int p1;
+
+ ASSERT(parray[p].adminLevel >= ADMIN_ADMIN);
+ if ((p1 = player_find_part_login(param[0].val.word)) < 0) {
+ pprintf(p, "%s is not logged in.\n", param[0].val.word);
+ return COM_OK;
+ }
+ if ((parray[p].adminLevel <= parray[p1].adminLevel) && !player_ishead(p)) {
+ pprintf(p, "You can only aset players below your adminlevel.\n");
+ return COM_OK;
+ }
+ pprintf(p, "Command issued as %s\n", parray[p1].name);
+ pcommand(p1, "set %s\n", param[1].val.string);
+ return COM_OK;
+}
+
+/*
+ * announce
+ *
+ * Usage: announce message
+ *
+ * Broadcasts your message to all logged on users. Announcements reach all
+ * users and cannot be censored in any way (such as by "set shout 0").
+ */
+PUBLIC int com_announce(int p, param_list param)
+{
+ int p1;
+ int count = 0;
+
+ ASSERT(parray[p].adminLevel >= ADMIN_ADMIN);
+ if (!printablestring(param[0].val.string)) {
+ pprintf(p, "Your message contains some unprintable character(s).\n");
+ return COM_OK;
+ }
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (p1 == p)
+ continue;
+ if (parray[p1].status != PLAYER_PROMPT)
+ continue;
+ count++;
+ pprintf_prompt(p1, "\n\n **ANNOUNCEMENT** from %s: %s\n\n", parray[p].name, param[0].val.string);
+ }
+ pprintf(p, "\n(%d) **ANNOUNCEMENT** from %s: %s\n\n", count, parray[p].name, param[0].val.string);
+ return COM_OK;
+}
+
+/*
+ * annunreg
+ *
+ * Usage: annunreg message
+ *
+ * Broadcasts your message to all logged on unregistered users, and admins,
+ * too. Announcements reach all unregistered users and admins and cannot be
+ * censored in any way (such as by "set shout 0").
+ */
+PUBLIC int com_annunreg(int p, param_list param)
+{
+ int p1;
+ int count = 0;
+
+ ASSERT(parray[p].adminLevel >= ADMIN_ADMIN);
+ if (!printablestring(param[0].val.string)) {
+ pprintf(p, "Your message contains some unprintable character(s).\n");
+ return COM_OK;
+ }
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (p1 == p)
+ continue;
+ if (parray[p1].status != PLAYER_PROMPT)
+ continue;
+ if ((parray[p1].registered) && (parray[p1].adminLevel < ADMIN_ADMIN))
+ continue;
+ count++;
+ pprintf_prompt(p1, "\n\n **UNREG ANNOUNCEMENT** from %s: %s\n\n", parray[p].name, param[0].val.string);
+ }
+ pprintf(p, "\n(%d) **UNREG ANNOUNCEMENT** from %s: %s\n\n", count, parray[p].name, param[0].val.string);
+ return COM_OK;
+}
+
+PUBLIC int com_muzzle(int p, param_list param)
+{
+ pprintf(p, "Obsolete command: Please use +muzzle and -muzzle.\n");
+ return COM_OK;
+}
+
+PUBLIC int com_cmuzzle(int p, param_list param)
+{
+ pprintf(p, "Obsolete command: Please use +cmuzzle and -cmuzzle.\n");
+ return COM_OK;
+}
+
+/*
+ * asetpasswd
+ *
+ * Usage: asetpasswd player {password,*}
+ *
+ * This command sets the password of the player to the password given.
+ * If '*' is specified then the player's account is locked, and no password
+ * will work until a new one is set by asetpasswd.
+ *
+ * If the player is connected, he is told of the new password and the name
+ * of the admin who changed it, or likewise of his account status. An
+ * email message is mailed to the player's email address as well.
+ */
+PUBLIC int com_asetpasswd(int p, param_list param)
+{
+ int p1, connected;
+ char subject[400], text[10100];
+ char salt[3];
+
+ ASSERT(parray[p].adminLevel >= ADMIN_ADMIN);
+
+ if (!FindPlayer(p, param[0].val.word, &p1, &connected))
+ return COM_OK;
+
+ if ((parray[p].adminLevel <= parray[p1].adminLevel) && !player_ishead(p)) {
+ pprintf(p, "You can only set password for players below your adminlevel.\n");
+ if (!connected)
+ player_remove(p1);
+ return COM_OK;
+ }
+ if (!parray[p1].registered) {
+ pprintf(p, "You cannot set the password of an unregistered player!\n");
+ return COM_OK;
+ }
+ if (parray[p1].passwd)
+ rfree(parray[p1].passwd);
+ if (param[1].val.word[0] == '*') {
+ parray[p1].passwd = strdup(param[1].val.word);
+ pprintf(p, "Account %s locked!\n", parray[p1].name);
+ sprintf(text, "Password of %s is now useless. Your account at our FICS has been locked.\n", parray[p1].name);
+ } else {
+ salt[0] = 'a' + rand() % 26;
+ salt[1] = 'a' + rand() % 26;
+ salt[2] = '\0';
+ parray[p1].passwd = strdup(crypt(param[1].val.word, salt));
+ sprintf(text, "Password of %s changed to \"%s\".\n", parray[p1].name, param[1].val.word);
+ pprintf(p, "%s", text);
+ }
+ if (param[1].val.word[0] == '*') {
+ sprintf(subject, "FICS: %s has locked your account.", parray[p].name);
+ if (connected)
+ pprintf_prompt(p1, "\n%s\n", subject);
+ } else {
+ sprintf(subject, "FICS: %s has changed your password.", parray[p].name);
+ if (connected)
+ pprintf_prompt(p1, "\n%s\n", subject);
+ }
+ mail_string_to_address(parray[p1].emailAddress, subject, text);
+ player_save(p1);
+ if (!connected)
+ player_remove(p1);
+ return COM_OK;
+}
+
+/*
+ * asetemail
+ *
+ * Usage: asetemail player [address]
+ *
+ * Sets the email address of the player to the address given. If the
+ * address is omited, then the player's email address is cleared. The
+ * person's email address is revealed to them when they use the "finger"
+ * command, but no other users -- except admins -- will have another
+ * player's email address displayed.
+ */
+PUBLIC int com_asetemail(int p, param_list param)
+{
+ int p1, connected;
+
+ ASSERT(parray[p].adminLevel >= ADMIN_ADMIN);
+
+ if (!FindPlayer(p, param[0].val.word, &p1, &connected))
+ return COM_OK;
+
+ if ((parray[p].adminLevel <= parray[p1].adminLevel) && !player_ishead(p)) {
+ pprintf(p, "You can only set email addr for players below your adminlevel.\n");
+ if (!connected)
+ player_remove(p1);
+ return COM_OK;
+ }
+ if (parray[p1].emailAddress)
+ rfree(parray[p1].emailAddress);
+ if (param[1].type == TYPE_NULL) {
+ parray[p1].emailAddress = NULL;
+ pprintf(p, "Email address for %s removed\n", parray[p1].name);
+ } else {
+ parray[p1].emailAddress = strdup(param[1].val.word);
+ pprintf(p, "Email address of %s changed to \"%s\".\n", parray[p1].name, param[1].val.word);
+ }
+ player_save(p1);
+ if (connected) {
+ if (param[1].type == TYPE_NULL) {
+ pprintf_prompt(p1, "\n\n%s has removed your email address.\n\n", parray[p].name);
+ } else {
+ pprintf_prompt(p1, "\n\n%s has changed your email address.\n\n", parray[p].name);
+ }
+ } else {
+ player_remove(p1);
+ }
+ return COM_OK;
+}
+
+/*
+ * asetrealname
+ *
+ * Usage: asetrealname user newname
+ *
+ * This command sets the user's real name (as displayed to admins on finger
+ * notes) to "newname".
+ */
+PUBLIC int com_asetrealname(int p, param_list param)
+{
+ int p1, connected;
+
+ ASSERT(parray[p].adminLevel >= ADMIN_ADMIN);
+ if (!FindPlayer(p, param[0].val.word, &p1, &connected))
+ return COM_OK;
+
+ if ((parray[p].adminLevel <= parray[p1].adminLevel) && !player_ishead(p)) {
+ pprintf(p, "You can only set real names for players below your adminlevel.\n");
+ if (!connected)
+ player_remove(p1);
+ return COM_OK;
+ }
+ if (parray[p1].fullName)
+ rfree(parray[p1].fullName);
+ if (param[1].type == TYPE_NULL) {
+ parray[p1].fullName = NULL;
+ pprintf(p, "Real name for %s removed\n", parray[p1].name);
+ } else {
+ parray[p1].fullName = strdup(param[1].val.word);
+ pprintf(p, "Real name of %s changed to \"%s\".\n", parray[p1].name, param[1].val.word);
+ }
+ player_save(p1);
+ if (connected) {
+ if (param[1].type == TYPE_NULL) {
+ pprintf_prompt(p1, "\n\n%s has removed your real name.\n\n", parray[p].name);
+ } else {
+ pprintf_prompt(p1, "\n\n%s has changed your real name.\n\n", parray[p].name);
+ }
+ } else {
+ player_remove(p1);
+ }
+ return COM_OK;
+}
+
+/*
+ * asethandle
+ *
+ * Usage: asethandle oldname newname
+ *
+ * This command changes the handle of the player from oldname to
+ * newname. The various player information, messages, logins, comments
+ * and games should be automatically transferred to the new account.
+ */
+PUBLIC int com_asethandle(int p, param_list param)
+{
+ char *player = param[0].val.word;
+ char *newplayer = param[1].val.word;
+ char playerlower[MAX_LOGIN_NAME], newplayerlower[MAX_LOGIN_NAME];
+ int p1;
+
+ ASSERT(parray[p].adminLevel >= ADMIN_ADMIN);
+ strcpy(playerlower, player);
+ stolower(playerlower);
+ strcpy(newplayerlower, newplayer);
+ stolower(newplayerlower);
+ if (player_find_bylogin(playerlower) >= 0) {
+ pprintf(p, "A player by that name is logged in.\n");
+ return COM_OK;
+ }
+ if (player_find_bylogin(newplayerlower) >= 0) {
+ pprintf(p, "A player by that new name is logged in.\n");
+ return COM_OK;
+ }
+ p1 = player_new();
+ if (player_read(p1, playerlower)) {
+ pprintf(p, "No player by the name %s is registered.\n", player);
+ player_remove(p1);
+ return COM_OK;
+ } else {
+ if ((parray[p].adminLevel <= parray[p1].adminLevel) && !player_ishead(p)) {
+ pprintf(p, "You can't set handles for an admin with a level higher than or equal to yourself.\n");
+ player_remove(p1);
+ return COM_OK;
+ }
+ }
+ player_remove(p1);
+
+ p1 = player_new();
+ if ((!player_read(p1, newplayerlower)) && (strcmp(playerlower, newplayerlower))) {
+ pprintf(p, "Sorry that handle is already taken.\n");
+ player_remove(p1);
+ return COM_OK;
+ }
+ player_remove(p1);
+
+ if ((!player_rename(playerlower, newplayerlower)) && (!player_read(p1, newplayerlower))) {
+ pprintf(p, "Player %s renamed to %s.\n", player, newplayer);
+ strfree(parray[p1].name);
+ parray[p1].name = strdup(newplayer);
+ player_save(p1);
+ if (parray[p1].s_stats.rating > 0)
+ UpdateRank(TYPE_STAND, newplayer, &parray[p1].s_stats, player);
+ if (parray[p1].b_stats.rating > 0)
+ UpdateRank(TYPE_BLITZ, newplayer, &parray[p1].b_stats, player);
+ if (parray[p1].w_stats.rating > 0)
+ UpdateRank(TYPE_WILD, newplayer, &parray[p1].w_stats, player);
+ } else {
+ pprintf(p, "Asethandle failed.\n");
+ }
+ player_remove(p1);
+ return COM_OK;
+}
+
+/*
+ * asetadmin
+ *
+ * Usage: asetadmin player AdminLevel
+ *
+ * Sets the admin level of the player with the following restrictions.
+ * 1. You can only set the admin level of players lower than yourself.
+ * 2. You can only set the admin level to a level that is lower than
+ * yourself.
+ */
+PUBLIC int com_asetadmin(int p, param_list param)
+{
+ int p1, connected, oldlevel;
+
+ ASSERT(parray[p].adminLevel >= ADMIN_GOD);
+ if (!FindPlayer(p, param[0].val.word,&p1, &connected))
+ return COM_OK;
+
+ if ((parray[p].adminLevel <= parray[p1].adminLevel) && !player_ishead(p)) {
+ pprintf(p, "You can only set adminlevel for players below your adminlevel.\n");
+ if (!connected)
+ player_remove(p1);
+ return COM_OK;
+ }
+ if ((parray[p1].login) == (parray[p].login)) {
+ pprintf(p, "You can't change your own adminlevel.\n");
+ return COM_OK;
+ }
+ if ((param[1].val.integer >= parray[p].adminLevel) && !player_ishead(p)) {
+ pprintf(p, "You can't promote someone to or above your adminlevel.\n");
+ if (!connected)
+ player_remove(p1);
+ return COM_OK;
+ }
+ oldlevel = parray[p1].adminLevel;
+ parray[p1].adminLevel = param[1].val.integer;
+ pprintf(p, "Admin level of %s set to %d.\n", parray[p1].name, parray[p1].adminLevel);
+ player_save(p1);
+ if (connected) {
+ pprintf_prompt(p1, "\n\n%s has set your admin level to %d.\n\n", parray[p].name, parray[p1].adminLevel);
+ } else {
+ player_remove(p1);
+ }
+ return COM_OK;
+}
+
+PRIVATE void SetRating(int p1, param_list param, statistics *s)
+{
+ s->rating = param[1].val.integer;
+ if (s->ltime == 0L)
+ s->sterr = 70.0;
+
+ if (param[2].type == TYPE_INT) {
+ s->win = param[2].val.integer;
+ if (param[3].type == TYPE_INT) {
+ s->los = param[3].val.integer;
+ if (param[4].type == TYPE_INT) {
+ s->dra = param[4].val.integer;
+ if (param[5].type == TYPE_INT) {
+ s->sterr = (double) param[5].val.integer;
+ }
+ }
+ }
+ }
+ s->num = s->win + s->los + s->dra;
+ if (s->num == 0) {
+ s->ltime = 0L;
+#if 0
+ s->dra = 1;
+ s->num = 1;
+#endif
+ } else {
+ s->ltime = time(0);
+ }
+}
+
+/*
+ * asetblitz
+ *
+ * Usage: asetblitz handle rating won lost drew RD
+ *
+ * This command allows admins to set a user's statistics for Blitz games.
+ * The parameters are self-explanatory: rating, # of wins, # of losses,
+ * # of draws, and ratings deviation.
+ */
+PUBLIC int com_asetblitz(int p, param_list param)
+{
+ int p1, connected;
+
+ ASSERT(parray[p].adminLevel >= ADMIN_ADMIN);
+ if (!FindPlayer(p, param[0].val.word, &p1, &connected))
+ return COM_OK;
+
+ SetRating(p1, param, &parray[p1].b_stats);
+ player_save(p1);
+ UpdateRank(TYPE_BLITZ, parray[p1].name, &parray[p1].b_stats,
+ parray[p1].name);
+ pprintf(p, "Blitz rating for %s modified.\n", parray[p1].name);
+ if (!connected)
+ player_remove(p1);
+ return COM_OK;
+}
+
+/*
+ * asetwild
+ *
+ * Usage: asetwild handle rating won lost drew RD
+ *
+ * This command allows admins to set a user's statistics for Wild games.
+ * The parameters are self-explanatory: rating, # of wins, # of losses,
+ * # of draws, and ratings deviation.
+ */
+PUBLIC int com_asetwild(int p, param_list param)
+{
+ int p1, connected;
+
+ ASSERT(parray[p].adminLevel >= ADMIN_ADMIN);
+ if (!FindPlayer(p, param[0].val.word, &p1, &connected))
+ return COM_OK;
+
+ SetRating(p1, param, &parray[p1].w_stats);
+ player_save(p1);
+ UpdateRank(TYPE_WILD, parray[p1].name, &parray[p1].w_stats,
+ parray[p1].name);
+ pprintf(p, "Wild rating for %s modified.\n", parray[p1].name);
+ if (!connected)
+ player_remove(p1);
+ return COM_OK;
+}
+
+/*
+ * asetstd
+ *
+ * Usage: asetstd handle rating won lost drew RD
+ *
+ * This command allows admins to set a user's statistics for Standard games.
+ * The parameters are self-explanatory: rating, # of wins, # of losses, # of
+ * draws, and ratings deviation.
+ */
+PUBLIC int com_asetstd(int p, param_list param)
+{
+ int p1, connected;
+
+ ASSERT(parray[p].adminLevel >= ADMIN_ADMIN);
+ if (!FindPlayer(p, param[0].val.word, &p1, &connected))
+ return COM_OK;
+
+ SetRating(p1, param, &parray[p1].s_stats);
+ player_save(p1);
+ UpdateRank(TYPE_STAND, parray[p1].name, &parray[p1].s_stats,
+ parray[p1].name);
+ pprintf(p, "Standard rating for %s modified.\n", parray[p1].name);
+ if (!connected)
+ player_remove(p1);
+ return COM_OK;
+}
+
+/*
+ * asetlight
+ *
+ * Usage: asetlight handle rating won lost drew RD
+ *
+ * This command allows admins to set a user's statistics for Lightning games.
+ * The parameters are self-explanatory: rating, # of wins, # of losses, # of
+ * draws, and ratings deviation.
+ */
+PUBLIC int com_asetlight(int p, param_list param)
+{
+ int p1, connected;
+
+ ASSERT(parray[p].adminLevel >= ADMIN_ADMIN);
+ if (!FindPlayer(p, param[0].val.word, &p1, &connected))
+ return COM_OK;
+
+ SetRating(p1, param, &parray[p1].l_stats);
+ player_save(p1);
+ pprintf(p, "Lightning rating for %s modified.\n", parray[p1].name);
+ if (!connected)
+ player_remove(p1);
+ return COM_OK;
+}
+
+/*
+ * nuke
+ *
+ * Usage: nuke user
+ *
+ * This command disconnects the user from the server. The user is informed
+ * that she/he has been nuked by the admin named and a comment is
+ * automatically placed in the user's files (if she/he is a registered
+ * user, of course).
+ */
+PUBLIC int com_nuke(int p, param_list param)
+{
+ int p1, fd;
+
+ ASSERT(parray[p].adminLevel >= ADMIN_ADMIN);
+ if ((p1 = player_find_part_login(param[0].val.word)) < 0) {
+ pprintf(p, "%s isn't logged in.\n", param[0].val.word);
+ } else {
+ if ((parray[p].adminLevel > parray[p1].adminLevel) || player_ishead(p)) {
+ pprintf(p, "Nuking: %s\n", param[0].val.word);
+ pprintf(p, "Please leave a comment explaining why %s was nuked.\n", parray[p1].name);
+ pprintf(p1, "\n\n**** You have been kicked out by %s! ****\n\n", parray[p].name);
+ pcommand(p, "addcomment %s Nuked\n", parray[p1].name);
+ fd = parray[p1].socket;
+ process_disconnection(fd);
+ net_close_connection(fd);
+ return COM_OK;
+ } else {
+ pprintf(p, "You need a higher adminlevel to nuke %s!\n", param[0].val.word);
+ }
+ }
+ return COM_OK;
+}
+
+/*
+ * summon
+ *
+ * Usage: summon player
+ *
+ * This command gives a beep and a message to the player indicating that you
+ * want to talk with him/her. The command is useful for waking someone up,
+ * for example a sleepy admin or an ignorant player.
+ */
+PUBLIC int com_summon(int p, param_list param)
+{
+ int p1;
+ ASSERT(parray[p].adminLevel >= ADMIN_ADMIN);
+ if ((p1 = player_find_part_login(param[0].val.word)) < 0) {
+ pprintf(p, "%s isn't logged in.\n", param[0].val.word);
+ return COM_OK;
+ } else {
+ pprintf(p1, "\a\n");
+ pprintf_highlight(p1, "%s", parray[p].name);
+ pprintf_prompt(p1, " needs to talk with you. Use tell %s <message> to reply.\a\n", parray[p].name);
+ pprintf(p, "Summoning sent to %s.\n", parray[p1].name);
+ return COM_OK;
+ }
+}
+
+/*
+ * addcomment
+ *
+ * Usage: addcomment user comment
+ *
+ * Places "comment" in the user's comments. If a user has comments, the
+ * number of comments is indicated to admins using the "finger" command.
+ * The comments themselves are displayed by the "showcomments" command.
+ */
+PUBLIC int com_addcomment(int p, param_list param)
+{
+ int p1, connected;
+
+ ASSERT(parray[p].adminLevel >= ADMIN_ADMIN);
+ if (!FindPlayer(p, param[0].val.word, &p1, &connected))
+ return COM_OK;
+
+ if (player_add_comment(p, p1, param[1].val.string)) {
+ pprintf(p, "Error adding comment!\n");
+ } else {
+ pprintf(p, "Comment added for %s.\n", parray[p1].name);
+ player_save(p1);
+ }
+ if (!connected)
+ player_remove(p1);
+ return COM_OK;
+}
+
+/*
+ * showcomment
+ *
+ * Usage: showcomment user
+ *
+ * This command will display all of the comments added to the user's account.
+ */
+PUBLIC int com_showcomment(int p, param_list param)
+{
+ int p1, connected;
+ ASSERT(parray[p].adminLevel >= ADMIN_ADMIN);
+ ASSERT(param[0].type == TYPE_WORD);
+
+ if (!FindPlayer(p, param[0].val.word, &p1, &connected))
+ return COM_OK;
+ player_show_comments(p, p1);
+ if (!connected)
+ player_remove(p1);
+ return COM_OK;
+}
+
+/*
+ * admin
+ *
+ * Usage: admin
+ *
+ * This command toggles your admin symbol (*) on/off. This symbol appears
+ * in who listings.
+ */
+PUBLIC int com_admin(int p, param_list param)
+{
+ ASSERT(parray[p].adminLevel >= ADMIN_ADMIN);
+ parray[p].i_admin = !(parray[p].i_admin);
+ if (parray[p].i_admin) {
+ pprintf(p, "Admin mode (*) is now shown\n");
+ } else {
+ pprintf(p, "Admin mode (*) is now not shown\n");
+ }
+ return COM_OK;
+}
+
+/*
+ * quota
+ *
+ * Usage: quota [n]
+ *
+ * The command sets the number of seconds (n) for the shout quota, which
+ * affects only those persons on the shout quota list. If no parameter
+ * (n) is given, the current setting is displayed.
+ */
+PUBLIC int com_quota(int p, param_list param)
+{
+ ASSERT(parray[p].adminLevel >= ADMIN_ADMIN);
+ if (param[0].type == TYPE_NULL) {
+ pprintf(p, "The current shout quota is 2 shouts per %d seconds.\n", quota_time);
+ return COM_OK;
+ }
+ quota_time = param[0].val.integer;
+ pprintf(p, "The shout quota is now 2 shouts per %d seconds.\n", quota_time);
+ return COM_OK;
+}
+
+
+/*
+ * asetmaxplayer
+ *
+ * Usage: asetmaxplayer [n]
+ *
+ * The command sets the maximum number of players (n) who can connect.
+ */
+PUBLIC int com_asetmaxplayer(int p, param_list param)
+{
+ ASSERT(parray[p].adminLevel >= ADMIN_ADMIN);
+ if (param[0].type != TYPE_NULL) {
+ pprintf(p, "Previously %d total conenctions allowed...\n", max_connections);
+ max_connections = param[0].val.integer;
+ if ((max_connections > MAX_PLAYER) || (max_connections > getdtablesize()-4)) {
+ max_connections = MIN(MAX_PLAYER, getdtablesize()-4);
+ pprintf (p, "Value too high. System OS limits us to %d.\n",
+ max_connections);
+ pprintf (p, "For saftey's sake, it should not be higher than %d.\n",
+ max_connections-2);
+ }
+ }
+ pprintf(p,
+ "There are currently %d regular and %d admin connections available,\n",
+ max_connections-10, 10 );
+ pprintf(p,
+ "with %d maximum logins before unregistered login restrictions begin.\n",
+ MAX(max_connections-50, 200) );
+ pprintf(p, "Total allowed connections: %d.\n", max_connections );
+ return COM_OK;
+}
diff --git a/FICS/adminproc.h b/FICS/adminproc.h
new file mode 100644
index 0000000..c6a21a7
--- /dev/null
+++ b/FICS/adminproc.h
@@ -0,0 +1,71 @@
+/* adminproc.h
+ *
+ */
+
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name email yy/mm/dd Change
+ Richard Nash 93/10/22 Created
+*/
+
+#ifndef _ADMINPROC_H
+#define _ADMINPROC_H
+
+extern int num_anews;
+
+extern int com_addplayer();
+extern int com_adjudicate(int, param_list);
+extern int com_nuke();
+extern int com_pose();
+extern int com_asetv();
+extern int com_announce();
+extern int com_annunreg();
+extern int com_muzzle();
+extern int com_cmuzzle();
+extern int com_asetrealname();
+extern int com_asetpasswd();
+extern int com_asetemail();
+extern int com_asethandle();
+extern int com_asetadmin();
+extern int server_shutdown();
+extern int com_asetblitz();
+extern int com_asetwild();
+extern int com_asetstd();
+extern int com_asetlight();
+extern int com_checkIP();
+extern int com_checkGAME();
+extern int com_checkPLAYER();
+extern int com_checkSOCKET();
+extern int com_checkTIMESEAL();
+extern int com_remplayer();
+extern int com_raisedead();
+extern int strcmpwild();
+extern int com_anews();
+extern int com_cnewsi();
+extern int com_cnewsf();
+extern int com_canewsi();
+extern int com_canewsf();
+extern int com_createadmnews();
+extern int com_summon();
+extern int com_addcomment();
+extern int com_showcomment();
+extern int com_admin();
+extern int com_asetmaxplayer();
+extern int com_quota();
+
+#endif /* _ADMINPROC_H */
+
diff --git a/FICS/algcheck.c b/FICS/algcheck.c
new file mode 100644
index 0000000..10fad0c
--- /dev/null
+++ b/FICS/algcheck.c
@@ -0,0 +1,465 @@
+/* algcheck.c
+ *
+ */
+
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name email yy/mm/dd Change
+ Richard Nash 93/10/22 Created
+*/
+
+#include "stdinclude.h"
+
+#include "common.h"
+#include "algcheck.h"
+#include "movecheck.h"
+#include "board.h"
+#include "utils.h"
+
+/* Well, lets see if I can list the possibilities
+ * Piece moves
+ * Ne4
+ * Nxe4
+ * Nce4
+ * Ncxe4
+ * R2f3
+ * R2xf3
+ * Special pawn moves
+ * e4
+ * ed
+ * exd
+ * exd5
+ * ed5
+ * Drop moves (bughouse, board edit)
+ * P@f7 P*f7
+ * #f7 #Nf7
+ * (o-o, o-o-o) Castling is handled earlier, so don't worry about that
+ * Of course any of these can have a + or ++ or = string on the end, just
+ * cut that off.
+ */
+
+/* f - file
+ * r - rank
+ * p - piece
+ * x - x
+ * @ - drop character (bughouse)
+ */
+char *alg_list[] = {
+ "fxfr", "pxfr", /* These two get confused in case of bishop */
+ "ffr", "pfr", /* These two get confused in case of bishop */
+ "pffr",
+ "pfxfr",
+ "prfr",
+ "prxfr",
+ "fr",
+ "ff",
+ "fxf",
+ "p@fr",
+ "#fr",
+ "#pfr",
+ NULL
+};
+
+#define ALG_UNKNOWN -1
+/* #define ALG_DROP -2 IanO: this is in board.h, used in movecheck.c */
+
+PRIVATE int get_move_info(char *str, int *piece, int *ff, int *fr, int *tf, int *tr, int *bishconfusion)
+{
+ char tmp[1024];
+ char *s;
+ int i, j, len;
+ char c;
+ int matchVal = -1;
+ int lpiece, lff, lfr, ltf, ltr;
+
+ *bishconfusion = 0;
+ strcpy(tmp, str);
+ if ((s = index(tmp, '+'))) { /* Cut off any check marks */
+ *s = '\0';
+ }
+ if ((s = index(tmp, '='))) { /* Cut off any promotion marks */
+ *s = '\0';
+ }
+ *piece = *ff = *fr = *tf = *tr = ALG_UNKNOWN;
+ len = strlen(tmp);
+ for (i = 0; alg_list[i]; i++) {
+ lpiece = lff = lfr = ltf = ltr = ALG_UNKNOWN;
+ if (strlen(alg_list[i]) != len)
+ continue;
+ for (j = len - 1; j >= 0; j--) {
+ switch (alg_list[i][j]) {
+ case 'f':
+ if ((tmp[j] < 'a') || (tmp[j] > 'h'))
+ goto nomatch;
+ if (ltf == ALG_UNKNOWN)
+ ltf = tmp[j] - 'a';
+ else
+ lff = tmp[j] - 'a';
+ break;
+ case 'r':
+ if ((tmp[j] < '1') || (tmp[j] > '8'))
+ goto nomatch;
+ if (ltr == ALG_UNKNOWN)
+ ltr = tmp[j] - '1';
+ else
+ lfr = tmp[j] - '1';
+ break;
+ case 'p':
+ if (isupper(tmp[j]))
+ c = tolower(tmp[j]);
+ else
+ c = tmp[j];
+ if (c == 'k')
+ lpiece = KING;
+ else if (c == 'q')
+ lpiece = QUEEN;
+ else if (c == 'r')
+ lpiece = ROOK;
+ else if (c == 'b')
+ lpiece = BISHOP;
+ else if (c == 'n')
+ lpiece = KNIGHT;
+ else if (c == 'p')
+ lpiece = PAWN;
+ else
+ goto nomatch;
+ break;
+ case 'x':
+ if ((tmp[j] != 'x') && (tmp[j] != 'X'))
+ goto nomatch;
+ break;
+ case '@':
+ if (tmp[j] != '@' && tmp[j] != '*')
+ goto nomatch;
+ lff = lfr = ALG_DROP;
+ break;
+ case '#':
+ if (tmp[j] != '#')
+ goto nomatch;
+ lff = lfr = ALG_DROP;
+ break;
+ default:
+ fprintf(stderr, "Unknown character in algebraic parsing\n");
+ break;
+ }
+ }
+ if (lpiece == ALG_UNKNOWN)
+ lpiece = PAWN;
+ if (lpiece == PAWN && (lfr == ALG_UNKNOWN)) { /* ffr or ff */
+ if (lff != ALG_UNKNOWN) {
+ if (lff == ltf)
+ goto nomatch;
+ if ((lff - ltf != 1) && (ltf - lff != 1))
+ goto nomatch;
+ }
+ }
+ *piece = lpiece; /* We have a match */
+ *tf = ltf;
+ *tr = ltr;
+ *ff = lff;
+ *fr = lfr;
+ if (matchVal != -1) {
+ /* We have two matches, it must be that Bxc4 vs. bxc4 problem */
+ /* Or it could be the Bc4 vs bc4 problem */
+ *bishconfusion = 1;
+ }
+ matchVal = i;
+nomatch:;
+ }
+ if (matchVal != -1)
+ return MS_ALG;
+ else
+ return MS_NOTMOVE;
+}
+
+PUBLIC int alg_is_move(char *mstr)
+{
+ int piece, ff, fr, tf, tr, bc;
+
+ return get_move_info(mstr, &piece, &ff, &fr, &tf, &tr, &bc);
+}
+
+/* We already know it is algebraic, get the move squares */
+PUBLIC int alg_parse_move(char *mstr, game_state_t * gs, move_t * mt)
+{
+ int f, r, tmpr, posf, posr, posr2;
+ int piece, ff, fr, tf, tr, bc;
+
+ if (get_move_info(mstr, &piece, &ff, &fr, &tf, &tr, &bc) != MS_ALG) {
+ fprintf(stderr, "FICS: Shouldn't try to algebraicly parse non-algabraic move string.\n");
+ return MOVE_ILLEGAL;
+ }
+ /* Resolve ambiguities in to-ness */
+ if (tf == ALG_UNKNOWN)
+ return MOVE_AMBIGUOUS; /* Must always know to file */
+ if (tr == ALG_UNKNOWN) {
+ posr = posr2 = ALG_UNKNOWN;
+ if (piece != PAWN)
+ return MOVE_AMBIGUOUS;
+ if (ff == ALG_UNKNOWN)
+ return MOVE_AMBIGUOUS;
+ /* Need to find pawn on ff that can take to tf and fill in ranks */
+ for (InitPieceLoop(gs->board, &f, &r, gs->onMove);
+ NextPieceLoop(gs->board, &f, &r, gs->onMove);) {
+ if ((ff != ALG_UNKNOWN) && (ff != f))
+ continue;
+ if (piecetype(gs->board[f][r]) != piece)
+ continue;
+ if (gs->onMove == WHITE) {
+ tmpr = r + 1;
+ } else {
+ tmpr = r - 1;
+ }
+/* if ((gs->board[tf][tmpr] == NOPIECE) ||
+ (iscolor(gs->board[tf][tmpr], gs->onMove))) continue;*/
+/* patch from Soso, added by Sparky 3/16/95 */
+ if (gs->board[tf][tmpr] == NOPIECE) {
+ if ((gs->ep_possible[((gs->onMove == WHITE) ? 0 : 1)][ff]) != (tf - ff))
+ continue;
+ } else {
+ if (iscolor(gs->board[tf][tmpr], gs->onMove))
+ continue;
+ }
+
+ if (legal_andcheck_move(gs, f, r, tf, tmpr)) {
+ if ((posr != ALG_UNKNOWN) && (posr2 != ALG_UNKNOWN))
+ return MOVE_AMBIGUOUS;
+ posr = tmpr;
+ posr2 = r;
+ }
+ }
+ tr = posr;
+ fr = posr2;
+ } else if (bc) { /* Could be bxc4 or Bxc4, tr is known */
+ ff = ALG_UNKNOWN;
+ fr = ALG_UNKNOWN;
+ for (InitPieceLoop(gs->board, &f, &r, gs->onMove);
+ NextPieceLoop(gs->board, &f, &r, gs->onMove);) {
+ if ((piecetype(gs->board[f][r]) != PAWN) && (piecetype(gs->board[f][r]) != BISHOP))
+ continue;
+ if (legal_andcheck_move(gs, f, r, tf, tr)) {
+ if ((piecetype(gs->board[f][r]) == PAWN) && (f != 1))
+ continue;
+ if ((ff != ALG_UNKNOWN) && (fr != ALG_UNKNOWN))
+ return (MOVE_AMBIGUOUS);
+ ff = f;
+ fr = r;
+ }
+ }
+ } else { /* The from position is unknown */
+ posf = ALG_UNKNOWN;
+ posr = ALG_UNKNOWN;
+ if ((ff == ALG_UNKNOWN) || (fr == ALG_UNKNOWN)) {
+ /* Need to find a piece that can go to tf, tr */
+ for (InitPieceLoop(gs->board, &f, &r, gs->onMove);
+ NextPieceLoop(gs->board, &f, &r, gs->onMove);) {
+ if ((ff != ALG_UNKNOWN) && (ff != f))
+ continue;
+ if ((fr != ALG_UNKNOWN) && (fr != r))
+ continue;
+ if (piecetype(gs->board[f][r]) != piece)
+ continue;
+ if (legal_andcheck_move(gs, f, r, tf, tr)) {
+ if ((posf != ALG_UNKNOWN) && (posr != ALG_UNKNOWN))
+ return MOVE_AMBIGUOUS;
+ posf = f;
+ posr = r;
+ }
+ }
+ } else if (ff == ALG_DROP) {
+ if (legal_andcheck_move(gs, ALG_DROP, piece, tf, tr)) {
+ posf = ALG_DROP;
+ posr = piece;
+ }
+ }
+ ff = posf;
+ fr = posr;
+ }
+ if ((tf == ALG_UNKNOWN) || (tr == ALG_UNKNOWN) ||
+ (ff == ALG_UNKNOWN) || (fr == ALG_UNKNOWN))
+ return MOVE_ILLEGAL;
+ mt->fromFile = ff;
+ mt->fromRank = fr;
+ mt->toFile = tf;
+ mt->toRank = tr;
+ return MOVE_OK;
+}
+
+/* A assumes the move has yet to be made on the board */
+/* this is the old stupid function, we are testing one from soso...
+PUBLIC char *alg_unparse( game_state_t *gs, move_t *mt )
+{
+ static char mStr[20];
+
+ if ((piecetype(gs->board[mt->fromFile][mt->fromRank]) == KING) &&
+ ((mt->fromFile == 4) && (mt->toFile == 6)) )
+ return "o-o";
+ if ((piecetype(gs->board[mt->fromFile][mt->fromRank]) == KING) &&
+ ((mt->fromFile == 4) && (mt->toFile == 2)) )
+ return "o-o-o";
+
+ sprintf( mStr, "%c%d%c%d", mt->fromFile+'a', mt->fromRank+1,
+ mt->toFile+'a', mt->toRank+1 );
+ return mStr;
+}
+*/
+
+
+/* A assumes the move has yet to be made on the board */
+
+
+/* Soso: rewrote alg_unparse function.
+ * Algebraic deparser - sets the mStr variable with move description
+ * in short notation. Used in last move report and in 'moves' command.
+ */
+
+PUBLIC char *alg_unparse(game_state_t * gs, move_t * mt)
+{
+ static char mStr[20];
+ char tmp[20];
+ int piece, f, r;
+ int ambig, r_ambig, f_ambig;
+ game_state_t fakeMove;
+
+ if (mt->fromFile == ALG_DROP) {
+ piece = mt->fromRank;
+ } else {
+ piece = piecetype(gs->board[mt->fromFile][mt->fromRank]);
+ }
+
+ if ((piece == KING) && ((mt->fromFile == 4) && (mt->toFile == 6))) {
+ strcpy(mStr, "O-O");
+ goto check;
+ }
+ if ((piece == KING) && ((mt->fromFile == 4) && (mt->toFile == 2))) {
+ strcpy(mStr, "O-O-O");
+ goto check;
+ }
+ strcpy(mStr, "");
+ switch (piece) {
+ case PAWN:
+ if (mt->fromFile == ALG_DROP) {
+ strcpy(mStr,"P");
+ } else if (mt->fromFile != mt->toFile) {
+ sprintf(tmp, "%c", mt->fromFile + 'a');
+ strcpy(mStr, tmp);
+ }
+ break;
+ case KNIGHT:
+ strcpy(mStr, "N");
+ break;
+ case BISHOP:
+ strcpy(mStr, "B");
+ break;
+ case ROOK:
+ strcpy(mStr, "R");
+ break;
+ case QUEEN:
+ strcpy(mStr, "Q");
+ break;
+ case KING:
+ strcpy(mStr, "K");
+ break;
+ default:
+ strcpy(mStr, "");
+ break;
+ }
+
+ if (mt->fromFile == ALG_DROP) {
+ strcat(mStr, DROP_STR);
+ } else {
+ /* Checks for ambiguity in short notation ( Ncb3, R8e8 or so) */
+ if (piece != PAWN) {
+ ambig = r_ambig = f_ambig = 0;
+ for (r = 0; r < 8; r++)
+ for (f = 0; f < 8; f++) {
+ if ((gs->board[f][r] != NOPIECE) && iscolor(gs->board[f][r], gs->onMove)
+ && (piecetype(gs->board[f][r]) == piece) &&
+ ((f != mt->fromFile) || (r != mt->fromRank))) {
+ if (legal_move(gs, f, r, mt->toFile, mt->toRank)) {
+ fakeMove = *gs;
+ fakeMove.board[f][r] = NOPIECE;
+ fakeMove.onMove = CToggle(fakeMove.onMove);
+ gs->onMove = CToggle(gs->onMove);
+
+ /* New bracketing below to try to fix 'ambiguous move' bug. */
+ if ((in_check(gs)) || !in_check(&fakeMove)) {
+ ambig++;
+ }
+ if (f == mt->fromFile) {
+ f_ambig++;
+ ambig++;
+ }
+ if (r == mt->fromRank) {
+ r_ambig++;
+ ambig++;
+ }
+ gs->onMove = CToggle(gs->onMove);
+ }
+ }
+ }
+ if (ambig > 0) {
+ /* Ambiguity in short notation, need to add file,rank or _both_ in
+ notation */
+ if (f_ambig == 0) {
+ sprintf(tmp, "%c", mt->fromFile + 'a');
+ strcat(mStr, tmp);
+ } else if (r_ambig == 0) {
+ sprintf(tmp, "%d", mt->fromRank + 1);
+ strcat(mStr, tmp);
+ } else {
+ sprintf(tmp, "%c%d", mt->fromFile + 'a', mt->fromRank + 1);
+ strcat(mStr, tmp);
+ }
+ }
+ }
+ if ((gs->board[mt->toFile][mt->toRank] != NOPIECE) ||
+ ((piece == PAWN) && (mt->fromFile != mt->toFile))) {
+ strcat(mStr, "x");
+ }
+ }
+ sprintf(tmp, "%c%d", mt->toFile + 'a', mt->toRank + 1);
+ strcat(mStr, tmp);
+
+ if ((piece == PAWN) && (mt->piecePromotionTo != NOPIECE)) {
+ strcat(mStr, "="); /* = before promoting piece */
+ switch (piecetype(mt->piecePromotionTo)) {
+ case KNIGHT:
+ strcat(mStr, "N");
+ break;
+ case BISHOP:
+ strcat(mStr, "B");
+ break;
+ case ROOK:
+ strcat(mStr, "R");
+ break;
+ case QUEEN:
+ strcat(mStr, "Q");
+ break;
+ default:
+ break;
+ }
+ }
+check:;
+ fakeMove = *gs;
+ execute_move(&fakeMove, mt, 0);
+ fakeMove.onMove = CToggle(fakeMove.onMove);
+ if (in_check(&fakeMove)) {
+ strcat(mStr, "+");
+ }
+ return mStr;
+}
diff --git a/FICS/algcheck.h b/FICS/algcheck.h
new file mode 100644
index 0000000..60d971b
--- /dev/null
+++ b/FICS/algcheck.h
@@ -0,0 +1,41 @@
+/* algcheck.h
+ *
+ */
+
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name email yy/mm/dd Change
+ Richard Nash 93/10/22 Created
+*/
+
+#ifndef _ALGCHECK_H
+#define _ALGCHECK_H
+
+#ifndef _BOARD_H
+#include "board.h"
+#endif
+
+#define DROP_CHAR '@' /* used by algcheck.c and movecheck.c */
+#define DROP_STR "@"
+
+extern int alg_is_move(char *);
+extern int alg_parse_move(char *, game_state_t *, move_t *);
+extern char *alg_unparse(game_state_t *, move_t *);
+
+/* extern int tolower(); */
+
+#endif /* _ALGCHECK_H */
diff --git a/FICS/autoconfig.h b/FICS/autoconfig.h
new file mode 100644
index 0000000..f3b4d46
--- /dev/null
+++ b/FICS/autoconfig.h
@@ -0,0 +1,171 @@
+/* autoconfig.h. Generated automatically by configure. */
+/* autoconfig.h.in. Generated automatically from configure.in by autoheader. */
+
+/* Define to empty if the keyword does not work. */
+/* #undef const */
+
+/* Define if you don't have vprintf but do have _doprnt. */
+/* #undef HAVE_DOPRNT */
+
+/* Define if you have <sys/wait.h> that is POSIX.1 compatible. */
+#define HAVE_SYS_WAIT_H 1
+
+/* Define if your struct tm has tm_zone. */
+#define HAVE_TM_ZONE 1
+
+/* Define if you don't have tm_zone but do have the external array
+ tzname. */
+/* #undef HAVE_TZNAME */
+
+/* Define if you have the vprintf function. */
+#define HAVE_VPRINTF 1
+
+/* Define if you have the wait3 system call. */
+#define HAVE_WAIT3 1
+
+/* Define to `long' if <sys/types.h> doesn't define. */
+/* #undef off_t */
+
+/* Define as the return type of signal handlers (int or void). */
+#define RETSIGTYPE void
+
+/* Define to `unsigned' if <sys/types.h> doesn't define. */
+/* #undef size_t */
+
+/* Define if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Define if you can safely include both <sys/time.h> and <time.h>. */
+#define TIME_WITH_SYS_TIME 1
+
+/* Define if your <sys/time.h> declares struct tm. */
+/* #undef TM_IN_SYS_TIME */
+
+/* Define this to be the return value of the time() function. */
+/* #undef time_t */
+
+/* Define, if you have a statfs() function which fills a struct
+ statfs, declared in sys/vfs.h. (NeXTStep) */
+/* #undef HAVE_STATFS_FILLING_STRUCT_STATFS */
+
+/* Define, if you have a statfs() function which fills a struct
+ fs_data, declared in sys/mount.h. (Ultrix) */
+/* #undef HAVE_STATFS_FILLING_STRUCT_FS_DATA */
+
+/* Define, if you have crypt() and it is declared in either
+ crypt.h or unistd.h. */
+/* #undef HAVE_CRYPT_DECLARED */
+
+/* Define this to be tv_usec, if your struct rusage has
+ a member ru_utime.tv_usec.
+ Define this to be tv_nsec, if your struct rusage has
+ a member ru_utime.tv_nsec. */
+#define TV_USEC tv_usec
+
+/* Define if you have the crypt function. */
+#define HAVE_CRYPT 1
+
+/* Define if you have the gethostname function. */
+#define HAVE_GETHOSTNAME 1
+
+/* Define if you have the gettimeofday function. */
+#define HAVE_GETTIMEOFDAY 1
+
+/* Define if you have the malloc_debug function. */
+/* #undef HAVE_MALLOC_DEBUG */
+
+/* Define if you have the malloc_size function. */
+/* #undef HAVE_MALLOC_SIZE */
+
+/* Define if you have the select function. */
+#define HAVE_SELECT 1
+
+/* Define if you have the socket function. */
+#define HAVE_SOCKET 1
+
+/* Define if you have the statfs function. */
+/* #undef HAVE_STATFS */
+
+/* Define if you have the strcspn function. */
+#define HAVE_STRCSPN 1
+
+/* Define if you have the strdup function. */
+#define HAVE_STRDUP 1
+
+/* Define if you have the strstr function. */
+#define HAVE_STRSTR 1
+
+/* Define if you have the <bstring.h> header file. */
+/* #undef HAVE_BSTRING_H */
+
+/* Define if you have the <crypt.h> header file. */
+/* #undef HAVE_CRYPT_H */
+
+/* Define if you have the <dirent.h> header file. */
+#define HAVE_DIRENT_H 1
+
+/* Define if you have the <fcntl.h> header file. */
+#define HAVE_FCNTL_H 1
+
+/* Define if you have the <math.h> header file. */
+#define HAVE_MATH_H 1
+
+/* Define if you have the <ndir.h> header file. */
+/* #undef HAVE_NDIR_H */
+
+/* Define if you have the <sys/cred.h> header file. */
+/* #undef HAVE_SYS_CRED_H */
+
+/* Define if you have the <sys/dir.h> header file. */
+/* #undef HAVE_SYS_DIR_H */
+
+/* Define if you have the <sys/file.h> header file. */
+#define HAVE_SYS_FILE_H 1
+
+/* Define if you have the <sys/filio.h> header file. */
+#define HAVE_SYS_FILIO_H 1
+
+/* Define if you have the <sys/ioctl.h> header file. */
+#define HAVE_SYS_IOCTL_H 1
+
+/* Define if you have the <sys/mount.h> header file. */
+#define HAVE_SYS_MOUNT_H 1
+
+/* Define if you have the <sys/ndir.h> header file. */
+/* #undef HAVE_SYS_NDIR_H */
+
+/* Define if you have the <sys/param.h> header file. */
+#define HAVE_SYS_PARAM_H 1
+
+/* Define if you have the <sys/resource.h> header file. */
+#define HAVE_SYS_RESOURCE_H 1
+
+/* Define if you have the <sys/rusage.h> header file. */
+/* #undef HAVE_SYS_RUSAGE_H */
+
+/* Define if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define if you have the <sys/time.h> header file. */
+#define HAVE_SYS_TIME_H 1
+
+/* Define if you have the <sys/vfs.h> header file. */
+#define HAVE_SYS_VFS_H 1
+
+/* Define if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define if you have the crypt library (-lcrypt). */
+/* #undef HAVE_LIBCRYPT */
+
+/* Define if you have the m library (-lm). */
+#define HAVE_LIBM 1
+
+/* Define if you have the nsl library (-lnsl). */
+#define HAVE_LIBNSL 1
+
+/* Define if you have the resolv library (-lresolv). */
+#define HAVE_LIBRESOLV 1
+
+/* Define if you have the socket library (-lsocket). */
+/* #undef HAVE_LIBSOCKET */
diff --git a/FICS/board.c b/FICS/board.c
new file mode 100644
index 0000000..efffcfb
--- /dev/null
+++ b/FICS/board.c
@@ -0,0 +1,1001 @@
+/* board.c
+ *
+ */
+
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name email yy/mm/dd Change
+ Richard Nash 93/10/22 Created
+*/
+
+#include "stdinclude.h"
+
+#include "common.h"
+#include "board.h"
+#include "playerdb.h"
+#include "gamedb.h"
+#include "utils.h"
+
+extern int style1();
+extern int style2();
+extern int style3();
+extern int style4();
+extern int style5();
+extern int style6();
+extern int style7();
+extern int style8();
+extern int style9();
+extern int style10();
+extern int style11();
+extern int style12();
+extern int style13();
+
+PUBLIC char *wpstring[] = {" ", "P", "N", "B", "R", "Q", "K"};
+PUBLIC char *bpstring[] = {" ", "p", "n", "b", "r", "q", "k"};
+
+PUBLIC int pieceValues[7] = {0, 1, 3, 3, 5, 9, 0};
+PUBLIC int (*styleFuncs[MAX_STYLES]) () = {
+ style1,
+ style2,
+ style3,
+ style4,
+ style5,
+ style6,
+ style7,
+ style8,
+ style9,
+ style10,
+ style11,
+ style12,
+ style13
+};
+
+PRIVATE const int mach_type = (1<<7) | (1<<8) | (1<<9) | (1<<10) | (1<<11);
+#define IsMachineStyle(n) (((1<<(n)) & mach_type) != 0)
+
+PRIVATE char bstring[MAX_BOARD_STRING_LEGTH];
+
+PUBLIC int board_init(game_state_t *b, char *category, char *board)
+{
+ int retval;
+ int wval;
+
+ if (!category || !board || !category[0] || !board[0])
+ retval = board_read_file("standard", "standard", b);
+ else {
+ if (!strcmp(category, "wild")) {
+ if (sscanf(board, "%d", &wval) == 1 && wval >= 1 && wval <= 4)
+ wild_update(wval);
+ }
+ retval = board_read_file(category, board, b);
+ }
+ b->gameNum = -1;
+ return retval;
+}
+
+PUBLIC void board_calc_strength(game_state_t *b, int *ws, int *bs)
+{
+ int r, f;
+ int *p;
+
+ *ws = *bs = 0;
+ for (f = 0; f < 8; f++) {
+ for (r = 0; r < 8; r++) {
+ if (colorval(b->board[r][f]) == WHITE)
+ p = ws;
+ else
+ p = bs;
+ *p += pieceValues[piecetype(b->board[r][f])];
+ }
+ }
+ for (r = PAWN; r <= QUEEN; r++) {
+ *ws += b->holding[0][r-1] * pieceValues[r];
+ *bs += b->holding[1][r-1] * pieceValues[r];
+ }
+}
+
+PRIVATE char *holding_str(int *holding)
+{
+ static char tmp[30];
+ int p,i,j;
+
+ i = 0;
+ for (p = PAWN; p <= QUEEN; p++)
+ {
+ for (j = 0; j < holding[p-1]; j++)
+ {
+ tmp[i++] = wpstring[p][0];
+ }
+ }
+ tmp[i] = '\0';
+ return tmp;
+}
+
+PRIVATE char *append_holding_machine(char *buf, int g, int c, int p)
+{
+ game_state_t *gs = &garray[g].game_state;
+ char tmp[50];
+
+ sprintf(tmp, "<b1> game %d white [%s] black [", g+1, holding_str(gs->holding[0]));
+ strcat(tmp, holding_str(gs->holding[1]));
+ strcat(buf, tmp);
+ if (p) {
+ sprintf(tmp, "] <- %c%s\n", "WB"[c], wpstring[p]);
+ strcat(buf, tmp);
+ } else
+ strcat(buf, "]\n");
+ return buf;
+}
+
+PRIVATE char *append_holding_display(char *buf, game_state_t *gs, int white)
+{
+ if (white)
+ strcat(buf, "White holding: [");
+ else
+ strcat(buf, "Black holding: [");
+ strcat(buf, holding_str(gs->holding[white ? 0 : 1]));
+ strcat(buf, "]\n");
+ return buf;
+}
+
+PUBLIC void update_holding(int g, int pieceCaptured)
+{
+ int p = piecetype(pieceCaptured);
+ int c = colorval(pieceCaptured);
+ game_state_t *gs = &garray[g].game_state;
+ int pp, pl;
+ char tmp1[80], tmp2[80];
+
+ if (c == WHITE) {
+ c = 0;
+ pp = garray[g].white;
+ } else {
+ c = 1;
+ pp = garray[g].black;
+ }
+ gs->holding[c][p-1]++;
+ tmp1[0] = '\0';
+ append_holding_machine(tmp1, g, c, p);
+ sprintf(tmp2, "Game %d %s received: %s -> [%s]\n", g+1,
+ parray[pp].name, wpstring[p], holding_str(gs->holding[c]));
+ for (pl = 0; pl < p_num; pl++) {
+ if (parray[pl].status == PLAYER_EMPTY)
+ continue;
+ if (player_is_observe(pl, g) || (parray[pl].game == g)) {
+ pprintf_prompt(pl, IsMachineStyle(parray[pl].style) ? tmp1 : tmp2);
+ }
+ }
+}
+
+/* Globals used for each board */
+PRIVATE char *wName, *bName;
+PRIVATE int wTime, bTime;
+PRIVATE int orient;
+PRIVATE int forPlayer;
+PRIVATE int myTurn; /* 1 = my turn, 0 = observe, -1 = other turn */
+ /* 2 = examiner, -2 = observing examiner */
+ /* -3 = just send position (spos/refresh) */
+
+PUBLIC char *board_to_string(char *wn, char *bn,
+ int wt, int bt,
+ game_state_t *b, move_t *ml, int style,
+ int orientation, int relation,
+ int p)
+{
+ int bh = (b->gameNum >= 0 && garray[b->gameNum].link >= 0);
+ wName = wn;
+ bName = bn;
+ wTime = wt;
+ bTime = bt;
+ orient = orientation;
+ myTurn = relation;
+
+ forPlayer = p;
+ if ((style < 0) || (style >= MAX_STYLES))
+ return NULL;
+
+ if (style != 11) { /* game header */
+ sprintf(bstring, "Game %d (%s vs. %s)\n\n",
+ b->gameNum + 1,
+ garray[b->gameNum].white_name,
+ garray[b->gameNum].black_name);
+ } else
+ bstring[0] = '\0';
+ if (bh && !IsMachineStyle(style))
+ append_holding_display(bstring, b, orientation==BLACK);
+ if (styleFuncs[style] (b, ml))
+ return NULL;
+ if (bh) {
+ if (IsMachineStyle(style))
+ append_holding_machine(bstring, b->gameNum, 0, 0);
+ else
+ append_holding_display(bstring, b, orientation==WHITE);
+ }
+ return bstring;
+}
+
+PUBLIC char *move_and_time(move_t *m)
+{
+ static char tmp[20];
+ sprintf(tmp, "%-7s (%s)", m->algString, tenth_str(m->tookTime, 0));
+ return tmp;
+}
+
+/* The following take the game state and whole move list */
+
+PRIVATE int genstyle(game_state_t *b, move_t *ml, char *wp[], char *bp[],
+ char *wsqr, char *bsqr,
+ char *top, char *mid, char *start, char *end, char *label,
+ char *blabel)
+{
+ int f, r, count;
+ char tmp[80];
+ int first, last, inc;
+ int ws, bs;
+
+ board_calc_strength(b, &ws, &bs);
+ if (orient == WHITE) {
+ first = 7;
+ last = 0;
+ inc = -1;
+ } else {
+ first = 0;
+ last = 7;
+ inc = 1;
+ }
+ strcat(bstring, top);
+ for (f = first, count = 7; f != last + inc; f += inc, count--) {
+ sprintf(tmp, " %d %s", f + 1, start);
+ strcat(bstring, tmp);
+ for (r = last; r != first - inc; r = r - inc) {
+ if (square_color(r, f) == WHITE)
+ strcat(bstring, wsqr);
+ else
+ strcat(bstring, bsqr);
+ if (piecetype(b->board[r][f]) == NOPIECE) {
+ if (square_color(r, f) == WHITE)
+ strcat(bstring, bp[0]);
+ else
+ strcat(bstring, wp[0]);
+ } else {
+ if (colorval(b->board[r][f]) == WHITE)
+ strcat(bstring, wp[piecetype(b->board[r][f])]);
+ else
+ strcat(bstring, bp[piecetype(b->board[r][f])]);
+ }
+ }
+ sprintf(tmp, "%s", end);
+ strcat(bstring, tmp);
+ switch (count) {
+ case 7:
+ sprintf(tmp, " Move # : %d (%s)", b->moveNum, CString(b->onMove));
+ strcat(bstring, tmp);
+ break;
+ case 6:
+/* if ((b->moveNum > 1) || (b->onMove == BLACK)) { */
+/* The change from the above line to the one below is a kludge by hersco. */
+ if (garray[b->gameNum].numHalfMoves > 0) {
+/* loon: think this fixes the crashing ascii board on takeback bug */
+ sprintf(tmp, " %s Moves : '%s'", CString(CToggle(b->onMove)),
+ move_and_time(&ml[garray[b->gameNum].numHalfMoves - 1]));
+ strcat(bstring, tmp);
+ }
+ break;
+ case 5:
+ break;
+ case 4:
+ sprintf(tmp, " Black Clock : %s", tenth_str(((bTime > 0) ? bTime : 0), 1));
+ strcat(bstring, tmp);
+ break;
+ case 3:
+ sprintf(tmp, " White Clock : %s", tenth_str(((wTime > 0) ? wTime : 0), 1));
+ strcat(bstring, tmp);
+ break;
+ case 2:
+ sprintf(tmp, " Black Strength : %d", bs);
+ strcat(bstring, tmp);
+ break;
+ case 1:
+ sprintf(tmp, " White Strength : %d", ws);
+ strcat(bstring, tmp);
+ break;
+ case 0:
+ break;
+ }
+ strcat(bstring, "\n");
+ if (count != 0)
+ strcat(bstring, mid);
+ else
+ strcat(bstring, top);
+ }
+ if (orient == WHITE)
+ strcat(bstring, label);
+ else
+ strcat(bstring, blabel);
+ return 0;
+}
+
+/* Experimental ANSI board for colour representation */
+PUBLIC int style13(game_state_t *b, move_t *ml)
+{
+ static char *wp[] = {" ", "\033[37m\033[1m P ", "\033[37m\033[1m N ", "\033[37m\033[1m B ", "\033[37m\033[1m R ", "\033[37m\033[1m Q ", "\033[37m\033[1m K "};
+ static char *bp[] = {" ", "\033[21m\033[37m P ", "\033[21m\033[37m N ", "\033[21m\033[37m B ", "\033[21m\033[37m R ", "\033[21m\033[37m Q ", "\033[21m\033[37m K "};
+ static char *wsqr = "\033[40m";
+ static char *bsqr = "\033[45m";
+ static char *top = "\t+------------------------+\n";
+ static char *mid = "";
+ static char *start = "|";
+ static char *end = "\033[0m|";
+ static char *label = "\t a b c d e f g h\n";
+ static char *blabel = "\t h g f e d c b a\n";
+
+ return genstyle(b, ml, wp, bp, wsqr, bsqr, top, mid, start, end, label, blabel);
+}
+
+/* Standard ICS */
+PUBLIC int style1(game_state_t *b, move_t *ml)
+{
+ static char *wp[] = {" |", " P |", " N |", " B |", " R |", " Q |", " K |"};
+ static char *bp[] = {" |", " *P|", " *N|", " *B|", " *R|", " *Q|", " *K|"};
+ static char *wsqr = "";
+ static char *bsqr = "";
+ static char *top = "\t---------------------------------\n";
+ static char *mid = "\t|---+---+---+---+---+---+---+---|\n";
+ static char *start = "|";
+ static char *end = "";
+ static char *label = "\t a b c d e f g h\n";
+ static char *blabel = "\t h g f e d c b a\n";
+
+ return genstyle(b, ml, wp, bp, wsqr, bsqr, top, mid, start, end, label, blabel);
+}
+
+/* USA-Today Sports Center-style board */
+PUBLIC int style2(game_state_t *b, move_t *ml)
+{
+ static char *wp[] = {"+ ", "P ", "N ", "B ", "R ", "Q ", "K "};
+ static char *bp[] = {"- ", "p' ", "n' ", "b' ", "r' ", "q' ", "k' "};
+ static char *wsqr = "";
+ static char *bsqr = "";
+ static char *top = "";
+ static char *mid = "";
+ static char *start = "";
+ static char *end = "";
+ static char *label = "\ta b c d e f g h\n";
+ static char *blabel = "\th g f e d c b a\n";
+
+ return genstyle(b, ml, wp, bp, wsqr, bsqr, top, mid, start, end, label, blabel);
+}
+
+/* Experimental vt-100 ANSI board for dark backgrounds */
+PUBLIC int style3(game_state_t *b, move_t *ml)
+{
+ static char *wp[] = {" ", " P ", " N ", " B ", " R ", " Q ", " K "};
+ static char *bp[] = {" ", " *P", " *N", " *B", " *R", " *Q", " *K"};
+ static char *wsqr = "\033[0m";
+ static char *bsqr = "\033[7m";
+ static char *top = "\t+------------------------+\n";
+ static char *mid = "";
+ static char *start = "|";
+ static char *end = "\033[0m|";
+ static char *label = "\t a b c d e f g h\n";
+ static char *blabel = "\t h g f e d c b a\n";
+
+ return genstyle(b, ml, wp, bp, wsqr, bsqr, top, mid, start, end, label, blabel);
+}
+
+/* Experimental vt-100 ANSI board for light backgrounds */
+PUBLIC int style4(game_state_t *b, move_t *ml)
+{
+ static char *wp[] = {" ", " P ", " N ", " B ", " R ", " Q ", " K "};
+ static char *bp[] = {" ", " *P", " *N", " *B", " *R", " *Q", " *K"};
+ static char *wsqr = "\033[7m";
+ static char *bsqr = "\033[0m";
+ static char *top = "\t+------------------------+\n";
+ static char *mid = "";
+ static char *start = "|";
+ static char *end = "\033[0m|";
+ static char *label = "\t a b c d e f g h\n";
+ static char *blabel = "\t h g f e d c b a\n";
+
+ return genstyle(b, ml, wp, bp, wsqr, bsqr, top, mid, start, end, label, blabel);
+}
+
+/* Style suggested by ajpierce@med.unc.edu */
+PUBLIC int style5(game_state_t *b, move_t *ml)
+{
+ static char *wp[] = {" ", " o ", " :N:", " <B>", " |R|", " {Q}", " =K="};
+ static char *bp[] = {" ", " p ", " :n:", " <b>", " |r|", " {q}", " =k="};
+ static char *wsqr = "";
+ static char *bsqr = "";
+ static char *top = " . . . . . . . . .\n";
+ static char *mid = " . . . . . . . . .\n";
+ static char *start = "";
+ static char *end = "";
+ static char *label = "\t a b c d e f g h\n";
+ static char *blabel = "\t h g f e d c b a\n";
+
+ return genstyle(b, ml, wp, bp, wsqr, bsqr, top, mid, start, end, label, blabel);
+}
+
+/* Email Board suggested by Thomas Fought (tlf@rsch.oclc.org) */
+PUBLIC int style6(game_state_t *b, move_t *ml)
+{
+ static char *wp[] = {" |", " wp |", " WN |", " WB |", " WR |", " WQ |", " WK |"};
+ static char *bp[] = {" |", " bp |", " BN |", " BB |", " BR |", " BQ |", " BK |"};
+ static char *wsqr = "";
+ static char *bsqr = "";
+ static char *top = "\t-----------------------------------------\n";
+ static char *mid = "\t-----------------------------------------\n";
+ static char *start = "|";
+ static char *end = "";
+ static char *label = "\t A B C D E F G H\n";
+ static char *blabel = "\t H G F E D C B A\n";
+
+ return genstyle(b, ml, wp, bp, wsqr, bsqr, top, mid, start, end, label, blabel);
+}
+
+/* Miniature board */
+PUBLIC int style7(game_state_t *b, move_t *ml)
+{
+ static char *wp[] = {" ", " P", " N", " B", " R", " Q", " K"};
+ static char *bp[] = {" -", " p", " n", " b", " r", " q", " k"};
+ static char *wsqr = "";
+ static char *bsqr = "";
+ static char *top = "\t:::::::::::::::::::::\n";
+ static char *mid = "";
+ static char *start = "..";
+ static char *end = " ..";
+ static char *label = "\t a b c d e f g h\n";
+ static char *blabel = "\t h g f e d c b a\n";
+
+ return genstyle(b, ml, wp, bp, wsqr, bsqr, top, mid, start, end, label, blabel);
+}
+
+/* ICS interface maker board-- raw data dump */
+PUBLIC int style8(game_state_t *b, move_t *ml)
+{
+ char tmp[80];
+ int f, r;
+ int ws, bs;
+
+ board_calc_strength(b, &ws, &bs);
+ sprintf(tmp, "#@#%03d%-16s%s%-16s%s", b->gameNum + 1,
+ garray[b->gameNum].white_name,
+ (orient == WHITE) ? "*" : ":",
+ garray[b->gameNum].black_name,
+ (orient == WHITE) ? ":" : "*");
+ strcat(bstring, tmp);
+ for (r = 0; r < 8; r++) {
+ for (f = 0; f < 8; f++) {
+ if (b->board[f][r] == NOPIECE) {
+ strcat(bstring, " ");
+ } else {
+ if (colorval(b->board[f][r]) == WHITE)
+ strcat(bstring, wpstring[piecetype(b->board[f][r])]);
+ else
+ strcat(bstring, bpstring[piecetype(b->board[f][r])]);
+ }
+ }
+ }
+ sprintf(tmp, "%03d%s%02d%02d%05d%05d%-7s(%s)@#@\n",
+ garray[b->gameNum].numHalfMoves / 2 + 1,
+ (b->onMove == WHITE) ? "W" : "B",
+ ws,
+ bs,
+ (wTime + 5) / 10,
+ (bTime + 5) / 10,
+ garray[b->gameNum].numHalfMoves ?
+ ml[garray[b->gameNum].numHalfMoves - 1].algString :
+ "none",
+ garray[b->gameNum].numHalfMoves ?
+ tenth_str(ml[garray[b->gameNum].numHalfMoves - 1].tookTime, 0) :
+ "0:00");
+ strcat(bstring, tmp);
+ return 0;
+}
+
+/* last 2 moves only (previous non-verbose mode) */
+PUBLIC int style9(game_state_t *b, move_t *ml)
+{
+ int i, count;
+ char tmp[80];
+ int startmove;
+
+ sprintf(tmp, "\nMove %-23s%s\n",
+ garray[b->gameNum].white_name,
+ garray[b->gameNum].black_name);
+ strcat(bstring, tmp);
+ sprintf(tmp, "---- -------------- --------------\n");
+ strcat(bstring, tmp);
+ startmove = ((garray[b->gameNum].numHalfMoves - 3) / 2) * 2;
+ if (startmove < 0)
+ startmove = 0;
+ for (i = startmove, count = 0;
+ i < garray[b->gameNum].numHalfMoves && count < 4;
+ i++, count++) {
+ if (!(i & 0x01)) {
+ sprintf(tmp, " %2d ", i / 2 + 1);
+ strcat(bstring, tmp);
+ }
+ sprintf(tmp, "%-23s", move_and_time(&ml[i]));
+ strcat(bstring, tmp);
+ if (i & 0x01)
+ strcat(bstring, "\n");
+ }
+ if (i & 0x01)
+ strcat(bstring, "\n");
+ return 0;
+}
+
+/* Sleator's 'new and improved' raw dump format... */
+PUBLIC int style10(game_state_t *b, move_t *ml)
+{
+ int f, r;
+ char tmp[80];
+ int ws, bs;
+
+ board_calc_strength(b, &ws, &bs);
+ sprintf(tmp, "<10>\n");
+ strcat(bstring, tmp);
+ for (r = 7; r >= 0; r--) {
+ strcat(bstring, "|");
+ for (f = 0; f < 8; f++) {
+ if (b->board[f][r] == NOPIECE) {
+ strcat(bstring, " ");
+ } else {
+ if (colorval(b->board[f][r]) == WHITE)
+ strcat(bstring, wpstring[piecetype(b->board[f][r])]);
+ else
+ strcat(bstring, bpstring[piecetype(b->board[f][r])]);
+ }
+ }
+ strcat(bstring, "|\n");
+ }
+ strcat(bstring, (b->onMove == WHITE) ? "W " : "B ");
+ if (garray[b->gameNum].numHalfMoves) {
+ sprintf(tmp, "%d ",
+ ml[garray[b->gameNum].numHalfMoves - 1].doublePawn);
+ } else {
+ sprintf(tmp, "-1 ");
+ }
+ strcat(bstring, tmp);
+ sprintf(tmp, "%d %d %d %d %d\n",
+ !(b->wkmoved || b->wkrmoved),
+ !(b->wkmoved || b->wqrmoved),
+ !(b->bkmoved || b->bkrmoved),
+ !(b->bkmoved || b->bqrmoved),
+ (garray[b->gameNum].numHalfMoves - ((b->lastIrreversable == -1) ? 0 :
+ b->lastIrreversable)));
+ strcat(bstring, tmp);
+ sprintf(tmp, "%d %s %s %d %d %d %d %d %d %d %d %s (%s) %s %d\n",
+ b->gameNum,
+ garray[b->gameNum].white_name,
+ garray[b->gameNum].black_name,
+ myTurn,
+ garray[b->gameNum].wInitTime / 600,
+ garray[b->gameNum].wIncrement / 10,
+ ws,
+ bs,
+ (wTime + 5) / 10,
+ (bTime + 5) / 10,
+ garray[b->gameNum].numHalfMoves / 2 + 1,
+ garray[b->gameNum].numHalfMoves ?
+ ml[garray[b->gameNum].numHalfMoves - 1].moveString :
+ "none",
+ garray[b->gameNum].numHalfMoves ?
+ tenth_str(ml[garray[b->gameNum].numHalfMoves - 1].tookTime, 0) :
+ "0:00",
+ garray[b->gameNum].numHalfMoves ?
+ ml[garray[b->gameNum].numHalfMoves - 1].algString :
+ "none",
+ (orient == WHITE) ? 0 : 1);
+ strcat(bstring, tmp);
+ sprintf(tmp, ">10<\n");
+ strcat(bstring, tmp);
+ return 0;
+}
+
+/* Same as 8, but with verbose moves ("P/e3-e4", instead of "e4") */
+PUBLIC int style11(game_state_t *b, move_t *ml)
+{
+ char tmp[80];
+ int f, r;
+ int ws, bs;
+
+ board_calc_strength(b, &ws, &bs);
+ sprintf(tmp, "#@#%03d%-16s%s%-16s%s", b->gameNum,
+ garray[b->gameNum].white_name,
+ (orient == WHITE) ? "*" : ":",
+ garray[b->gameNum].black_name,
+ (orient == WHITE) ? ":" : "*");
+ strcat(bstring, tmp);
+ for (r = 0; r < 8; r++) {
+ for (f = 0; f < 8; f++) {
+ if (b->board[f][r] == NOPIECE) {
+ strcat(bstring, " ");
+ } else {
+ if (colorval(b->board[f][r]) == WHITE)
+ strcat(bstring, wpstring[piecetype(b->board[f][r])]);
+ else
+ strcat(bstring, bpstring[piecetype(b->board[f][r])]);
+ }
+ }
+ }
+ sprintf(tmp, "%03d%s%02d%02d%05d%05d%-7s(%s)@#@\n",
+ garray[b->gameNum].numHalfMoves / 2 + 1,
+ (b->onMove == WHITE) ? "W" : "B",
+ ws,
+ bs,
+ (wTime + 5) / 10,
+ (bTime + 5) / 10,
+ garray[b->gameNum].numHalfMoves ?
+ ml[garray[b->gameNum].numHalfMoves - 1].moveString :
+ "none",
+ garray[b->gameNum].numHalfMoves ?
+ tenth_str(ml[garray[b->gameNum].numHalfMoves - 1].tookTime, 0) :
+ "0:00");
+ strcat(bstring, tmp);
+ return 0;
+}
+
+/* Similar to style 10. See the "style12" help file for information */
+PUBLIC int style12(game_state_t *b, move_t *ml)
+{
+ int f, r;
+ char tmp[80];
+ int ws, bs;
+
+ board_calc_strength(b, &ws, &bs);
+ sprintf(bstring, "<12> ");
+ for (r = 7; r >= 0; r--) {
+ for (f = 0; f < 8; f++) {
+ if (b->board[f][r] == NOPIECE) {
+ strcat(bstring, "-");
+ } else {
+ if (colorval(b->board[f][r]) == WHITE)
+ strcat(bstring, wpstring[piecetype(b->board[f][r])]);
+ else
+ strcat(bstring, bpstring[piecetype(b->board[f][r])]);
+ }
+ }
+ strcat(bstring, " ");
+ }
+ strcat(bstring, (b->onMove == WHITE) ? "W " : "B ");
+ if (garray[b->gameNum].numHalfMoves) {
+ sprintf(tmp, "%d ",
+ ml[garray[b->gameNum].numHalfMoves - 1].doublePawn);
+ } else {
+ sprintf(tmp, "-1 ");
+ }
+ strcat(bstring, tmp);
+ sprintf(tmp, "%d %d %d %d %d ",
+ !(b->wkmoved || b->wkrmoved),
+ !(b->wkmoved || b->wqrmoved),
+ !(b->bkmoved || b->bkrmoved),
+ !(b->bkmoved || b->bqrmoved),
+ (garray[b->gameNum].numHalfMoves - ((b->lastIrreversable == -1) ? 0 : b->lastIrreversable)));
+ strcat(bstring, tmp);
+ sprintf(tmp, "%d %s %s %d %d %d %d %d %d %d %d %s (%s) %s %d\n",
+ b->gameNum + 1,
+ garray[b->gameNum].white_name,
+ garray[b->gameNum].black_name,
+ myTurn,
+ garray[b->gameNum].wInitTime / 600,
+ garray[b->gameNum].wIncrement / 10,
+ ws,
+ bs,
+ (wTime + 5) / 10,
+ (bTime + 5) / 10,
+ garray[b->gameNum].numHalfMoves / 2 + 1,
+ garray[b->gameNum].numHalfMoves ?
+ ml[garray[b->gameNum].numHalfMoves - 1].moveString :
+ "none",
+ garray[b->gameNum].numHalfMoves ?
+ tenth_str(ml[garray[b->gameNum].numHalfMoves - 1].tookTime, 0) :
+ "0:00",
+ garray[b->gameNum].numHalfMoves ?
+ ml[garray[b->gameNum].numHalfMoves - 1].algString :
+ "none", (orient == WHITE) ? 0 : 1);
+
+ strcat(bstring, tmp);
+ return 0;
+}
+
+PUBLIC int board_read_file(char *category, char *gname, game_state_t *gs)
+{
+ int f, r;
+ FILE *fp;
+ char fname[MAX_FILENAME_SIZE + 1];
+ int c;
+ int onNewLine = 1;
+ int onColor = -1;
+ int onPiece = -1;
+ int onFile = -1;
+ int onRank = -1;
+
+ sprintf(fname, "%s/%s/%s", board_dir, category, gname);
+ fp = fopen(fname, "r");
+ if (!fp)
+ return 1;
+
+ for (f = 0; f < 8; f++)
+ for (r = 0; r < 8; r++)
+ gs->board[f][r] = NOPIECE;
+ for (f = 0; f < 2; f++) {
+ for (r = 0; r < 8; r++)
+ gs->ep_possible[f][r] = 0;
+ for (r = PAWN; r <= QUEEN; r++)
+ gs->holding[f][r-1] = 0;
+ }
+ gs->wkmoved = gs->wqrmoved = gs->wkrmoved = 0;
+ gs->bkmoved = gs->bqrmoved = gs->bkrmoved = 0;
+ gs->onMove = -1;
+ gs->moveNum = 1;
+ gs->lastIrreversable = -1;
+ while (!feof(fp)) {
+ c = fgetc(fp);
+ if (onNewLine) {
+ if (c == 'W') {
+ onColor = WHITE;
+ if (gs->onMove < 0)
+ gs->onMove = WHITE;
+ } else if (c == 'B') {
+ onColor = BLACK;
+ if (gs->onMove < 0)
+ gs->onMove = BLACK;
+ } else if (c == '#') {
+ while (!feof(fp) && c != '\n')
+ c = fgetc(fp); /* Comment line */
+ continue;
+ } else { /* Skip any line we don't understand */
+ while (!feof(fp) && c != '\n')
+ c = fgetc(fp);
+ continue;
+ }
+ onNewLine = 0;
+ } else {
+ switch (c) {
+ case 'P':
+ onPiece = PAWN;
+ break;
+ case 'R':
+ onPiece = ROOK;
+ break;
+ case 'N':
+ onPiece = KNIGHT;
+ break;
+ case 'B':
+ onPiece = BISHOP;
+ break;
+ case 'Q':
+ onPiece = QUEEN;
+ break;
+ case 'K':
+ onPiece = KING;
+ break;
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ case 'g':
+ case 'h':
+ onFile = c - 'a';
+ onRank = -1;
+ break;
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ onRank = c - '1';
+ if (onFile >= 0 && onColor >= 0 && onPiece >= 0)
+ gs->board[onFile][onRank] = onPiece | onColor;
+ break;
+ case '#':
+ while (!feof(fp) && c != '\n')
+ c = fgetc(fp); /* Comment line */
+ case '\n':
+ onNewLine = 1;
+ onColor = -1;
+ onPiece = -1;
+ onFile = -1;
+ onRank = -1;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ fclose(fp);
+ return 0;
+}
+
+#define WHITE_SQUARE 1
+#define BLACK_SQUARE 0
+#define ANY_SQUARE -1
+#define SquareColor(f, r) ((f ^ r) & 1)
+
+PRIVATE void place_piece(board_t b, int piece, int squareColor)
+{
+ int r, f;
+ int placed = 0;
+
+ if (iscolor(piece, BLACK))
+ r = 7;
+ else
+ r = 0;
+
+ while (!placed) {
+ if (squareColor == ANY_SQUARE) {
+ f = rand() % 8;
+ } else {
+ f = (rand() % 4) * 2;
+ if (SquareColor(f, r) != squareColor)
+ f++;
+ }
+ if ((b)[f][r] == NOPIECE) {
+ (b)[f][r] = piece;
+ placed = 1;
+ }
+ }
+}
+
+PUBLIC void wild_update(int style)
+{
+ int f, r, i;
+ board_t b;
+
+ for (f = 0; f < 8; f++)
+ for (r = 0; r < 8; r++)
+ b[f][r] = NOPIECE;
+ for (f = 0; f < 8; f++) {
+ b[f][1] = W_PAWN;
+ b[f][6] = B_PAWN;
+ }
+ switch (style) {
+ case 1:
+ if (rand() & 0x01) {
+ b[4][0] = W_KING;
+ b[3][0] = W_QUEEN;
+ } else {
+ b[3][0] = W_KING;
+ b[4][0] = W_QUEEN;
+ }
+ if (rand() & 0x01) {
+ b[4][7] = B_KING;
+ b[3][7] = B_QUEEN;
+ } else {
+ b[3][7] = B_KING;
+ b[4][7] = B_QUEEN;
+ }
+ b[0][0] = b[7][0] = W_ROOK;
+ b[0][7] = b[7][7] = B_ROOK;
+ /* Must do bishops before knights to be sure opposite colored squares are
+ available. */
+ place_piece(b, W_BISHOP, WHITE_SQUARE);
+ place_piece(b, W_BISHOP, BLACK_SQUARE);
+ place_piece(b, W_KNIGHT, ANY_SQUARE);
+ place_piece(b, W_KNIGHT, ANY_SQUARE);
+ place_piece(b, B_BISHOP, WHITE_SQUARE);
+ place_piece(b, B_BISHOP, BLACK_SQUARE);
+ place_piece(b, B_KNIGHT, ANY_SQUARE);
+ place_piece(b, B_KNIGHT, ANY_SQUARE);
+ break;
+ case 2:
+ place_piece(b, W_KING, ANY_SQUARE);
+ place_piece(b, W_QUEEN, ANY_SQUARE);
+ place_piece(b, W_ROOK, ANY_SQUARE);
+ place_piece(b, W_ROOK, ANY_SQUARE);
+ place_piece(b, W_BISHOP, ANY_SQUARE);
+ place_piece(b, W_BISHOP, ANY_SQUARE);
+ place_piece(b, W_KNIGHT, ANY_SQUARE);
+ place_piece(b, W_KNIGHT, ANY_SQUARE);
+ /* Black mirrors White */
+ for (i = 0; i < 8; i++) {
+ b[i][7] = b[i][0] | BLACK;
+ }
+ break;
+ case 3:
+ /* Generate White king on random square plus random set of pieces */
+ place_piece(b, W_KING, ANY_SQUARE);
+ for (i = 0; i < 8; i++) {
+ if (b[i][0] != W_KING) {
+ b[i][0] = (rand() % 4) + 2;
+ }
+ }
+ /* Black mirrors White */
+ for (i = 0; i < 8; i++) {
+ b[i][7] = b[i][0] | BLACK;
+ }
+ break;
+ case 4:
+ /* Generate White king on random square plus random set of pieces */
+ place_piece(b, W_KING, ANY_SQUARE);
+ for (i = 0; i < 8; i++) {
+ if (b[i][0] != W_KING) {
+ b[i][0] = (rand() % 4) + 2;
+ }
+ }
+ /* Black has same set of pieces, but randomly permuted, except that Black
+ must have the same number of bishops on white squares as White has on
+ black squares, and vice versa. So we must place Black's bishops first
+ to be sure there are enough squares left of the correct color. */
+ for (i = 0; i < 8; i++) {
+ if (b[i][0] == W_BISHOP) {
+ place_piece(b, B_BISHOP, !SquareColor(i, 0));
+ }
+ }
+ for (i = 0; i < 8; i++) {
+ if (b[i][0] != W_BISHOP) {
+ place_piece(b, b[i][0] | BLACK, ANY_SQUARE);
+ }
+ }
+ break;
+ default:
+ return;
+ break;
+ }
+ {
+ FILE *fp;
+ char fname[MAX_FILENAME_SIZE + 1];
+ int onPiece;
+
+ sprintf(fname, "%s/wild/%d", board_dir, style);
+ fp = fopen(fname, "w");
+ if (!fp) {
+ fprintf(stderr, "FICS: Can't write file name %s\n", fname);
+ return;
+ }
+ fprintf(fp, "W:");
+ onPiece = -1;
+ for (r = 1; r >= 0; r--) {
+ for (f = 0; f < 8; f++) {
+ if (onPiece < 0 || b[f][r] != onPiece) {
+ onPiece = b[f][r];
+ fprintf(fp, " %s", wpstring[piecetype(b[f][r])]);
+ }
+ fprintf(fp, " %c%c", f + 'a', r + '1');
+ }
+ }
+ fprintf(fp, "\nB:");
+ onPiece = -1;
+ for (r = 6; r < 8; r++) {
+ for (f = 0; f < 8; f++) {
+ if (onPiece < 0 || b[f][r] != onPiece) {
+ onPiece = b[f][r];
+ fprintf(fp, " %s", wpstring[piecetype(b[f][r])]);
+ }
+ fprintf(fp, " %c%c", f + 'a', r + '1');
+ }
+ }
+ fprintf(fp, "\n");
+ fclose(fp);
+ }
+}
+
+PUBLIC void wild_init()
+{
+ wild_update(1);
+ wild_update(2);
+ wild_update(3);
+ wild_update(4);
+}
diff --git a/FICS/board.h b/FICS/board.h
new file mode 100644
index 0000000..5db4fb9
--- /dev/null
+++ b/FICS/board.h
@@ -0,0 +1,123 @@
+/* board.h
+ *
+ */
+
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name email yy/mm/dd Change
+ Richard Nash 93/10/22 Created
+*/
+
+#ifndef _BOARD_H
+#define _BOARD_H
+
+#define WHITE 0x00
+#define BLACK 0x80
+#define CString( c ) (((c) == WHITE) ? "White" : "Black" )
+#define CToggle( c ) (((c) == BLACK) ? WHITE : BLACK )
+
+/* These are indexes into an array so their values are not arbitrary */
+#define NOPIECE 0x00
+#define PAWN 0x01
+#define KNIGHT 0x02
+#define BISHOP 0x03
+#define ROOK 0x04
+#define QUEEN 0x05
+#define KING 0x06
+
+#define MAX_BOARD_STRING_LEGTH 1280 /* Abitrarily 80 * 16 */
+#define MAX_STYLES 13
+
+#define W_PAWN (PAWN | WHITE)
+#define W_KNIGHT (KNIGHT | WHITE)
+#define W_BISHOP (BISHOP | WHITE)
+#define W_ROOK (ROOK | WHITE)
+#define W_QUEEN (QUEEN | WHITE)
+#define W_KING (KING | WHITE)
+
+#define B_PAWN (PAWN | BLACK)
+#define B_KNIGHT (KNIGHT | BLACK)
+#define B_BISHOP (BISHOP | BLACK)
+#define B_ROOK (ROOK | BLACK)
+#define B_QUEEN (QUEEN | BLACK)
+#define B_KING (KING | BLACK)
+
+#define isblack(p) ((p) & BLACK)
+#define iswhite(p) (!isblack(p))
+#define iscolor(p,color) (((p) & BLACK) == (color))
+#define piecetype(p) ((p) & 0x7f)
+#define colorval(p) ((p) & 0x80)
+#define square_color(r,f) ((((r)+(f)) & 0x01) ? BLACK : WHITE)
+
+extern int pieceValues[7];
+
+/* Treated as [file][rank] */
+typedef int board_t[8][8];
+
+typedef struct _game_state_t {
+ board_t board;
+ /* for bughouse */
+ int holding[2][5];
+ /* For castling */
+ unsigned char wkmoved, wqrmoved, wkrmoved;
+ unsigned char bkmoved, bqrmoved, bkrmoved;
+ /* for ep */
+ int ep_possible[2][8];
+ /* For draws */
+ int lastIrreversable;
+ int onMove;
+ int moveNum;
+ /* Game num not saved, must be restored when read */
+ int gameNum;
+} game_state_t;
+
+#define ALG_DROP -2
+
+/* bughouse: if a drop move, then fromFile is ALG_DROP and fromRank is piece */
+
+typedef struct _move_t {
+ int color;
+ int fromFile, fromRank;
+ int toFile, toRank;
+ int pieceCaptured;
+ int piecePromotionTo;
+ int enPassant; /* 0 = no, 1=higher -1= lower */
+ int doublePawn; /* Only used for board display */
+ char moveString[8];
+ char algString[8];
+ unsigned char FENpos[74]; /* This replaces the boardList. */
+ unsigned atTime;
+ unsigned tookTime;
+} move_t;
+
+#define MoveToHalfMove( gs ) ((((gs)->moveNum - 1) * 2) + (((gs)->onMove == WHITE) ? 0 : 1))
+
+extern char *wpstring[];
+extern char *bpstring[];
+
+extern int board_init(game_state_t *, char *, char *);
+extern void board_calc_strength(game_state_t *, int *, int *);
+extern void update_holding(int, int);
+extern char *board_to_string(char *, char *, int, int, game_state_t *, move_t *, int, int, int, int);
+extern char *move_and_time(move_t *);
+extern int board_read_file(char *, char *, game_state_t *);
+extern void wild_update(int);
+extern void wild_init(void);
+
+extern int fgetc();
+
+#endif
diff --git a/FICS/channel.c b/FICS/channel.c
new file mode 100644
index 0000000..8ada4b9
--- /dev/null
+++ b/FICS/channel.c
@@ -0,0 +1,101 @@
+/* channel.c
+ *
+ */
+
+/* THIS SOURCE FILE IS NOW OBSOLETE - DAV */
+
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name email yy/mm/dd Change
+ Richard Nash 93/10/22 Created
+*/
+
+#include "stdinclude.h"
+
+#include "common.h"
+#include "channel.h"
+#include "network.h"
+#include "rmalloc.h"
+#include "playerdb.h"
+
+#if 0 /* ARGH!!! 256 * 256 * 4 = 256K! */
+/* old cheesy (as loon would put it :) ) code removed */
+PUBLIC int *channels[MAX_CHANNELS];
+PUBLIC int numOn[MAX_CHANNELS];
+
+PUBLIC void channel_init()
+{
+ int i;
+ for (i = 0; i < MAX_CHANNELS; i++) {
+ channels[i] = rmalloc(max_connections * sizeof(int));
+ numOn[i] = 0;
+ }
+}
+
+PUBLIC int on_channel(int ch, int p)
+{
+ int i;
+
+ for (i = 0; i < numOn[ch]; i++)
+ if (p == channels[ch][i])
+ return 1;
+ return 0;
+}
+
+PUBLIC int channel_remove(int ch, int p)
+{
+ int i, found;
+
+ found = -1;
+ for (i = 0; i < numOn[ch] && found < 0; i++)
+ if (p == channels[ch][i])
+ found = i;
+ if (found < 0)
+ return 1;
+ for (i = found; i < numOn[ch] - 1; i++)
+ channels[ch][i] = channels[ch][i + 1];
+ numOn[ch] = numOn[ch] - 1;
+ --parray[p].nochannels;
+ return 0;
+}
+
+PUBLIC int channel_add(int ch, int p)
+{
+ if (numOn[ch] >= MAX_CHANNELS)
+ return 1;
+ if (on_channel(ch, p))
+ return 2;
+ if ((parray[p].nochannels == MAX_INCHANNELS) && (parray[p].adminLevel == 0)) {
+ return 3;
+ }
+ channels[ch][numOn[ch]] = p;
+ numOn[ch]++;
+ parray[p].nochannels++;
+ return 0;
+}
+
+/* this piece of replacement code appears in talkproc.c */
+PUBLIC int on_channel(int ch, int p)
+{
+ char tmp[10]; /* 9 digits ought to be enough :) */
+
+ sprintf (tmp,"%d",ch);
+ return in_list(p, L_CHANNEL,ch ) /* since needs ch converted to a string keep
+ hidden from view */
+}
+
+#endif
diff --git a/FICS/channel.h b/FICS/channel.h
new file mode 100644
index 0000000..9de4808
--- /dev/null
+++ b/FICS/channel.h
@@ -0,0 +1,40 @@
+/* channel.h
+ *
+ */
+
+/* THIS SOURCE FILE IS NOW OBSOLETE - DAV */
+
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name email yy/mm/dd Change
+ Richard Nash 93/10/22 Created
+*/
+
+#ifndef _CHANNEL_H
+#define _CHANNEL_H
+
+#define MAX_CHANNELS 256
+
+extern int *channels[MAX_CHANNELS];
+extern int numOn[MAX_CHANNELS];
+
+extern void channel_init(void);
+extern int on_channel(int, int);
+extern int channel_remove(int, int);
+extern int channel_add(int, int);
+
+#endif /* _CHANNEL_H */
diff --git a/FICS/command.c b/FICS/command.c
new file mode 100644
index 0000000..a53f305
--- /dev/null
+++ b/FICS/command.c
@@ -0,0 +1,1080 @@
+/* command.c
+ *
+ */
+
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name email yy/mm/dd Change
+ Richard Nash 93/10/22 Created
+*/
+
+#include "stdinclude.h"
+
+#include "common.h"
+#include "rmalloc.h"
+#include "command.h"
+#include "command_list.h"
+#include "movecheck.h"
+#include "ficsmain.h"
+#include "config.h"
+#include "utils.h"
+#include "playerdb.h"
+#include "gamedb.h"
+#include "gameproc.h"
+#include "obsproc.h"
+#include "ratings.h"
+#include "vers.h"
+#include "network.h"
+#include "shutdown.h"
+#include <sys/param.h>
+#ifdef TIMESEAL
+#include "timeseal.h"
+#endif
+/*#include "hostinfo.h" */
+
+PUBLIC char *mess_dir = DEFAULT_MESS;
+PUBLIC char *index_dir = DEFAULT_INDEX;
+PUBLIC char *help_dir[NUM_LANGS] = {DEFAULT_HELP, HELP_SPANISH, HELP_FRENCH, HELP_DANISH};
+PUBLIC char *comhelp_dir = DEFAULT_COMHELP;
+PUBLIC char *info_dir = DEFAULT_INFO;
+PUBLIC char *adhelp_dir = DEFAULT_ADHELP;
+PUBLIC char *uscf_dir = DEFAULT_USCF;
+PUBLIC char *stats_dir = DEFAULT_STATS;
+PUBLIC char *player_dir = DEFAULT_PLAYERS;
+PUBLIC char *adj_dir = DEFAULT_ADJOURNED;
+PUBLIC char *hist_dir = DEFAULT_HISTORY;
+PUBLIC char *journal_dir = DEFAULT_JOURNAL;
+PUBLIC char *board_dir = DEFAULT_BOARDS;
+PUBLIC char *def_prompt = DEFAULT_PROMPT;
+PUBLIC char *source_dir = DEFAULT_SOURCE;
+PUBLIC char *lists_dir = DEFAULT_LISTS;
+PUBLIC char *news_dir = DEFAULT_NEWS;
+PUBLIC char *usage_dir[NUM_LANGS] = {DEFAULT_USAGE, USAGE_SPANISH, USAGE_FRENCH, USAGE_DANISH};
+
+PUBLIC char *hadmin_handle = HADMINHANDLE;
+char *hadmin_email = HADMINEMAIL;
+char *reg_addr = REGMAIL;
+
+PUBLIC int startuptime;
+PUBLIC char fics_hostname[81];
+PUBLIC int player_high;
+PUBLIC int game_high;
+PUBLIC int MailGameResult;
+
+PUBLIC int commanding_player = -1; /* The player whose command your in */
+
+PRIVATE int lastCommandFound = -1;
+
+/* grimm */
+int player_notify_departure(int p);
+/* added for warnings */
+
+/* Copies command into comm, and returns pointer to parameters in
+ * parameters
+ */
+PRIVATE int parse_command(char *com_string,
+ char **comm,
+ char **parameters)
+{
+ *comm = com_string;
+ *parameters = eatword(com_string);
+ if (**parameters != '\0') {
+ **parameters = '\0';
+ (*parameters)++;
+ *parameters = eatwhite(*parameters);
+ }
+ if (strlen(*comm) >= MAX_COM_LENGTH) {
+ return COM_BADCOMMAND;
+ }
+ return COM_OK;
+}
+
+PUBLIC int alias_lookup(char *tmp, alias_type *alias_list, int numalias)
+/* numalias is the maximum number to search through */
+{
+ int i;
+
+ for (i = 0; (alias_list[i].comm_name && i < numalias); i++) {
+ if (!strcmp(tmp, alias_list[i].comm_name))
+ return i;
+ }
+ return -1; /* not found */
+}
+
+PUBLIC int alias_count(alias_type *alias_list)
+{
+ int i;
+
+ for (i = 0; alias_list[i].comm_name; i++);
+ return i;
+}
+
+
+/* Puts alias substitution into alias_string */
+PRIVATE void alias_substitute(alias_type *alias_list, int num_alias,
+ char *com_str, char outalias[])
+{
+ char *s = com_str;
+ char name[MAX_COM_LENGTH];
+ char *t = name;
+ int i = 0;
+ char *atpos, *aliasval;
+
+ /* Get first word of command, terminated by whitespace or by containing
+ punctuation */
+ while (*s && !iswhitespace(*s)) {
+ if (i++ >= MAX_COM_LENGTH) {
+ strcpy(outalias, com_str);
+ return;
+ }
+ if (ispunct(*t++ = *s++))
+ break;
+ }
+ *t = '\0';
+ if (*s && iswhitespace(*s))
+ s++;
+
+ i = alias_lookup(name, alias_list, num_alias);
+ if (i < 0) {
+ strcpy(outalias, com_str);
+ return;
+ }
+ aliasval = alias_list[i].alias;
+
+ /* See if alias contains an @ */
+ atpos = strchr(aliasval, '@');
+ if (atpos != NULL) {
+ strncpy(outalias, aliasval, atpos - aliasval);
+ outalias[atpos - aliasval] = '\0';
+ strcat(outalias, s);
+ strcat(outalias, atpos + 1);
+ } else {
+ strcpy(outalias, aliasval);
+ if (*s) {
+ strcat(outalias, " ");
+ strcat(outalias, s);
+ }
+ }
+}
+
+/* Returns pointer to command that matches */
+PRIVATE int match_command(char *comm, int p)
+{
+ int i = 0;
+ int gotIt = -1;
+ int len = strlen(comm);
+
+ while (command_list[i].comm_name) {
+ if (!strncmp(command_list[i].comm_name, comm, len)
+ && parray[p].adminLevel >= command_list[i].adminLevel) {
+ if (gotIt >= 0)
+ return -COM_AMBIGUOUS;
+ gotIt = i;
+ }
+ i++;
+ }
+ if (in_list(p, L_REMOVEDCOM, command_list[gotIt].comm_name)) {
+ pprintf(p, "Due to a bug - this command has been temporarily removed.\n");
+ return -COM_FAILED;
+ }
+ if (gotIt >= 0) {
+ lastCommandFound = gotIt;
+ return gotIt;
+ }
+ return -COM_FAILED;
+}
+
+/* Gets the parameters for this command */
+PRIVATE int get_parameters(int command, char *parameters, param_list params)
+{
+ int i, parlen;
+ int paramLower;
+ char c;
+ static char punc[2];
+
+ punc[1] = '\0'; /* Holds punc parameters */
+ for (i = 0; i < MAXNUMPARAMS; i++)
+ (params)[i].type = TYPE_NULL; /* Set all parameters to NULL */
+ parlen = strlen(command_list[command].param_string);
+ for (i = 0; i < parlen; i++) {
+ c = command_list[command].param_string[i];
+ if (isupper(c)) {
+ paramLower = 0;
+ c = tolower(c);
+ } else {
+ paramLower = 1;
+ }
+ switch (c) {
+ case 'w':
+ case 'o': /* word or optional word */
+ parameters = eatwhite(parameters);
+ if (!*parameters)
+ return (c == 'o' ? COM_OK : COM_BADPARAMETERS);
+ (params)[i].val.word = parameters;
+ (params)[i].type = TYPE_WORD;
+ if (ispunct(*parameters)) {
+ punc[0] = *parameters;
+ (params)[i].val.word = punc;
+ parameters++;
+ if (*parameters && iswhitespace(*parameters))
+ parameters++;
+ } else {
+ parameters = eatword(parameters);
+ if (*parameters != '\0') {
+ *parameters = '\0';
+ parameters++;
+ }
+ }
+ if (paramLower)
+ stolower((params)[i].val.word);
+ break;
+
+ case 'd':
+ case 'p': /* optional or required integer */
+ parameters = eatwhite(parameters);
+ if (!*parameters)
+ return (c == 'p' ? COM_OK : COM_BADPARAMETERS);
+ if (sscanf(parameters, "%d", &(params)[i].val.integer) != 1)
+ return COM_BADPARAMETERS;
+ (params)[i].type = TYPE_INT;
+ parameters = eatword(parameters);
+ if (*parameters != '\0') {
+ *parameters = '\0';
+ parameters++;
+ }
+ break;
+
+ case 'i':
+ case 'n': /* optional or required word or integer */
+ parameters = eatwhite(parameters);
+ if (!*parameters)
+ return (c == 'n' ? COM_OK : COM_BADPARAMETERS);
+ if (sscanf(parameters, "%d", &(params)[i].val.integer) != 1) {
+ (params)[i].val.word = parameters;
+ (params)[i].type = TYPE_WORD;
+ } else {
+ (params)[i].type = TYPE_INT;
+ }
+ if (ispunct(*parameters)) {
+ punc[0] = *parameters;
+ (params)[i].val.word = punc;
+ (params)[i].type = TYPE_WORD;
+ parameters++;
+ if (*parameters && iswhitespace(*parameters))
+ parameters++;
+ } else {
+ parameters = eatword(parameters);
+ if (*parameters != '\0') {
+ *parameters = '\0';
+ parameters++;
+ }
+ }
+ if ((params)[i].type == TYPE_WORD)
+ if (paramLower)
+ stolower((params)[i].val.word);
+ break;
+
+ case 's':
+ case 't': /* optional or required string to end */
+ if (!*parameters)
+ return (c == 't' ? COM_OK : COM_BADPARAMETERS);
+ (params)[i].val.string = parameters;
+ (params)[i].type = TYPE_STRING;
+ while (*parameters)
+ parameters = nextword(parameters);
+ if (paramLower)
+ stolower((params)[i].val.string);
+ break;
+ }
+ }
+ if (*parameters)
+ return COM_BADPARAMETERS;
+ else
+ return COM_OK;
+}
+
+PRIVATE void printusage(int p, char *command_str)
+{
+ int i, parlen, UseLang = parray[p].language;
+ int command;
+ char c;
+
+ char *filenames[1000]; /* enough for all usage names */
+
+ if ((command = match_command(command_str, p)) < 0) {
+ pprintf(p, " UNKNOWN COMMAND\n");
+ return;
+ }
+
+/*Usage added by DAV 11/19/95 */
+ /* First lets check if we have a text usage file for it */
+
+ i = search_directory(usage_dir[UseLang], command_str, filenames, 1000);
+ if (i == 0) { /* nope none in current Lang */
+ if (UseLang != LANG_DEFAULT) {
+ i += search_directory(usage_dir[LANG_DEFAULT], command_str, filenames, 1000);
+ if (i > 0) {
+ pprintf(p, "No usage available in %s; using %s instead.\n",
+ Language(UseLang), Language(LANG_DEFAULT));
+ UseLang = LANG_DEFAULT;
+ }
+ }
+ }
+
+ if (i != 0) {
+ if ((i == 1) || (!strcmp(*filenames, command_str))) { /* found it? then send */
+ if (psend_file(p, usage_dir[UseLang], *filenames)) {
+ /* we should never reach this unless the file was just deleted */
+ pprintf(p, "Usage file %s could not be found! ", *filenames);
+ pprintf(p, "Please inform an admin of this. Thank you.\n");
+ /* no need to print 'system' usage - should never happen */
+ }
+ return;
+ }
+ }
+
+ /* print the default 'system' usage files (which aren't much help!) */
+
+ pprintf(p, "Usage: %s", command_list[lastCommandFound].comm_name);
+
+ parlen = strlen(command_list[command].param_string);
+ for (i = 0; i < parlen; i++) {
+ c = command_list[command].param_string[i];
+ if (isupper(c))
+ c = tolower(c);
+ switch (c) {
+ case 'w': /* word */
+ pprintf(p, " word");
+ break;
+ case 'o': /* optional word */
+ pprintf(p, " [word]");
+ break;
+ case 'd': /* integer */
+ pprintf(p, " integer");
+ break;
+ case 'p': /* optional integer */
+ pprintf(p, " [integer]");
+ break;
+ case 'i': /* word or integer */
+ pprintf(p, " {word, integer}");
+ break;
+ case 'n': /* optional word or integer */
+ pprintf(p, " [{word, integer}]");
+ break;
+ case 's': /* string to end */
+ pprintf(p, " string");
+ break;
+ case 't': /* optional string to end */
+ pprintf(p, " [string]");
+ break;
+ }
+ }
+ pprintf(p, "\nSee 'help %s' for a complete description.\n", command_list[lastCommandFound].comm_name);
+}
+
+PUBLIC int process_command(int p, char *com_string, char **cmd)
+{
+ int which_command, retval;
+ param_list params;
+ static char alias_string1[MAX_STRING_LENGTH * 4], alias_string2[MAX_STRING_LENGTH * 4];
+ char *comm, *parameters;
+
+#ifdef DEBUG
+ if (strcasecmp(parray[p].name, parray[p].login)) {
+ fprintf(stderr, "FICS: PROBLEM Name=%s, Login=%s\n", parray[p].name, parray[p].login);
+ }
+#endif
+ if (!com_string)
+ return COM_FAILED;
+#ifdef DEBUG
+ fprintf(stderr, "%s, %s, %d: >%s<\n", parray[p].name, parray[p].login, parray[p].socket, com_string);
+#endif
+ alias_substitute(parray[p].alias_list, parray[p].numAlias,
+ com_string, alias_string1);
+ alias_substitute(g_alias_list, 999,
+ alias_string1, alias_string2);
+#ifdef DEBUG
+ if (strcmp(com_string, alias_string2) != 0)
+ fprintf(stderr, "%s -alias-: >%s<\n", parray[p].name, alias_string2);
+#endif
+ if ((retval = parse_command(alias_string2, &comm, &parameters)))
+ return retval;
+ if (is_move(comm))
+ return COM_ISMOVE;
+ stolower(comm); /* All commands are case-insensitive */
+ *cmd = comm;
+ if ((which_command = match_command(comm, p)) < 0)
+ return -which_command;
+ if (parray[p].adminLevel < command_list[which_command].adminLevel) {
+ return COM_RIGHTS;
+ }
+ if ((retval = get_parameters(which_command, parameters, params)))
+ return retval;
+ return command_list[which_command].comm_func(p, params);
+}
+
+PRIVATE int process_login(int p, char *loginname)
+{
+ int problem = 1;
+
+ loginname = eatwhite(loginname);
+
+ if (!*loginname) { /* do something in here? */
+ } else {
+ char *loginnameii = strdup(loginname);
+ stolower(loginname);
+ if (!alphastring(loginname)) {
+ pprintf(p, "\nSorry, names can only consist of lower and upper case letters. Try again.\n");
+ } else if (strlen(loginname) < 3) {
+ pprintf(p, "\nA name should be at least three characters long! Try again.\n");
+ } else if (strlen(loginname) > 17) {
+ pprintf(p, "\nSorry, names may be at most 17 characters long. Try again.\n");
+ } else if (in_list(p, L_BAN, loginnameii)) {
+ pprintf(p, "\nPlayer \"%s\" is banned.\n", loginname);
+ rfree(loginnameii);
+ return COM_LOGOUT;
+ } else if ((!in_list(p, L_ADMIN, loginnameii)) && (player_count(0) >= max_connections - 10)) {
+ psend_raw_file(p, mess_dir, MESS_FULL);
+ rfree(loginnameii);
+ return COM_LOGOUT;
+ } else {
+ problem = 0;
+ if (player_read(p, loginname)) {
+ strcpy(parray[p].name, loginnameii);
+ if (in_list(p, L_FILTER, dotQuad(parray[p].thisHost))) {
+ pprintf(p, "\nDue to abusive behavior, nobody from your site may login.\n");
+ pprintf(p, "If you wish to use this server please email %s\n", reg_addr);
+ pprintf(p, "Include details of a nick-name to be called here, e-mail address and your real name.\n");
+ pprintf(p, "We will send a password to you. Thanks.\n");
+ rfree(loginnameii);
+ return COM_LOGOUT;
+ }
+ if ((player_count(0)) >= MAX(max_connections - 60, 200)) {
+ psend_raw_file(p, mess_dir, MESS_FULL_UNREG);
+ rfree(loginnameii);
+ return COM_LOGOUT;
+ }
+ pprintf_noformat(p, "\n\"%s\" is not a registered name. You may use this name to play unrated games.\n(After logging in, do \"help register\" for more info on how to register.)\n\nPress return to enter the FICS as \"%s\":",
+ parray[p].name, parray[p].name);
+ } else {
+ pprintf_noformat(p, "\n\"%s\" is a registered name. If it is yours, type the password.\nIf not, just hit return to try another name.\n\npassword: ", parray[p].name);
+ }
+ parray[p].status = PLAYER_PASSWORD;
+ turn_echo_off(parray[p].socket);
+ rfree(loginnameii);
+ if (strcasecmp(loginname, parray[p].name)) {
+ pprintf(p, "\nYou've got a bad name field in your playerfile -- please report this to an admin!\n");
+ rfree(loginnameii);
+ return COM_LOGOUT;
+ }
+ if ((parray[p].adminLevel != 0) && (!in_list(p, L_ADMIN, parray[p].name))) {
+ pprintf(p, "\nYou've got a bad playerfile -- please report this to an admin!\n");
+ pprintf(p, "Your handle is missing!");
+ pprintf(p, "Please log on as an unreg until an admin can correct this.\n");
+ rfree(loginnameii);
+ return COM_LOGOUT;
+ }
+ if ((parray[p].registered) && (parray[p].fullName == NULL)) {
+ pprintf(p, "\nYou've got a bad playerfile -- please report this to an admin!\n");
+ pprintf(p, "Your FullName is missing!");
+ pprintf(p, "Please log on as an unreg until an admin can correct this.\n");
+ rfree(loginnameii);
+ return COM_LOGOUT;
+ }
+ if ((parray[p].registered) && (parray[p].emailAddress == NULL)) {
+ pprintf(p, "\nYou've got a bad playerfile -- please report this to an admin!\n");
+ pprintf(p, "Your Email address is missing\n");
+ pprintf(p, "Please log on as an unreg until an admin can correct this.\n");
+ rfree(loginnameii);
+ return COM_LOGOUT;
+ }
+ }
+ }
+
+ if (problem) {
+ psend_raw_file(p, mess_dir, MESS_LOGIN);
+ pprintf(p, "login: ");
+ }
+ return 0;
+}
+
+void boot_out(int p, int p1)
+{
+ int fd;
+ pprintf(p, "\n **** %s is already logged in - kicking them out. ****\n", parray[p].name);
+ pprintf(p1, "**** %s has arrived - you can't both be logged in. ****\n", parray[p].name);
+ fd = parray[p1].socket;
+ process_disconnection(fd);
+ net_close_connection(fd);
+}
+
+PUBLIC void rscan_news(FILE *fp, int p, int lc) {
+
+ char junk[MAX_LINE_SIZE], count[10];
+ int crtime;
+ char *junkp;
+
+ fgets(junk, MAX_LINE_SIZE, fp);
+ if (feof(fp))
+ return;
+ sscanf(junk, "%d %s", &crtime, count);
+
+ if (crtime - lc < 0)
+ return;
+ else {
+ rscan_news(fp, p, lc);
+ junkp = junk;
+ junkp = nextword(junkp);
+ junkp = nextword(junkp);
+ pprintf(p, "%3s (%s) %s", count, fix_time(strltime(&crtime)), junkp);
+ }
+}
+
+PRIVATE void check_news(int p, int admin)
+{
+ FILE *fp;
+ char filename[MAX_FILENAME_SIZE];
+ char junk[MAX_LINE_SIZE];
+ char *junkp;
+ int crtime;
+ int lc = player_lastconnect(p);
+ char count[10];
+
+ if (admin) {
+
+ sprintf(filename, "%s/newadminnews.index", news_dir);
+ fp = fopen(filename, "r");
+ if (!fp) {
+ fprintf(stderr, "Can't find admin news index.\n");
+ return;
+ }
+
+ if (num_anews == -1) {
+ num_anews = count_lines(fp);
+ fclose(fp);
+ fp = fopen(filename, "r");
+ }
+
+ fgets(junk, MAX_LINE_SIZE, fp);
+ sscanf(junk, "%d %s", &crtime, count);
+ if ((crtime - lc) < 0) {
+ pprintf(p, "There are no new admin news items since your last login.\n\n");
+ fclose(fp);
+ return;
+ } else {
+ pprintf(p, "Index of new admin news items:\n");
+ rscan_news(fp, p, lc);
+ junkp = junk;
+ junkp = nextword(junkp);
+ junkp = nextword(junkp);
+ pprintf(p, "%3s (%s) %s", count, fix_time(strltime(&crtime)), junkp);
+ pprintf(p, "(\"anews %d\" will display the most recent admin news file)\n", num_anews);
+ }
+
+ } else {
+
+ sprintf(filename, "%s/newnews.index", news_dir);
+ fp = fopen(filename, "r");
+ if (!fp) {
+ fprintf(stderr, "Can't find news index.\n");
+ return;
+ }
+
+ if (num_news == -1) {
+ num_news = count_lines(fp);
+ fclose(fp);
+ fp = fopen(filename, "r");
+ }
+
+ fgets(junk, MAX_LINE_SIZE, fp);
+ sscanf(junk, "%d %s", &crtime, count);
+ if ((crtime - lc) < 0) {
+ pprintf(p, "There are no new news items since your last login (%s).\n", strltime(&lc));
+ fclose(fp);
+ return;
+ } else {
+ pprintf(p, "Index of new news items:\n");
+ rscan_news(fp, p, lc);
+ junkp = junk;
+ junkp = nextword(junkp);
+ junkp = nextword(junkp);
+ pprintf(p, "%3s (%s) %s", count, fix_time(strltime(&crtime)), junkp);
+ pprintf(p, "(\"news %d\" will display the most recent admin news file)\n", num_news);
+ }
+ }
+
+ fclose(fp);
+}
+
+PRIVATE int process_password(int p, char *password)
+{
+ int p1;
+ char salt[3];
+ int fd;
+ unsigned int fromHost;
+ int messnum;
+/*
+ struct hostent *hp;
+*/
+ int dummy; /* (to hold a return value) */
+
+ turn_echo_on(parray[p].socket);
+
+/* if (password[0] == '\n') {
+ parray[p].status = PLAYER_LOGIN;
+ return 0;
+ }*/
+
+ if (parray[p].passwd && parray[p].registered) {
+ salt[0] = parray[p].passwd[0];
+ salt[1] = parray[p].passwd[1];
+ salt[2] = '\0';
+ if (strcmp(crypt(password, salt), parray[p].passwd)) {
+ fd = parray[p].socket;
+ fromHost = parray[p].thisHost;
+ player_clear(p);
+ parray[p].logon_time = parray[p].last_command_time = time(0);
+ parray[p].status = PLAYER_LOGIN;
+ parray[p].socket = fd;
+ parray[p].thisHost = fromHost;
+ if (*password)
+ pprintf(p, "\n\n**** Invalid password! ****\n\n");
+ return COM_LOGOUT;
+ }
+ }
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (parray[p1].name != NULL) {
+ if ((!strcasecmp(parray[p].name, parray[p1].name)) && (p != p1)) {
+ if (parray[p].registered == 0) {
+ pprintf(p, "\n*** Sorry %s is already logged in ***\n", parray[p].name);
+ return COM_LOGOUT;
+ }
+ boot_out(p, p1);
+ }
+ }
+ }
+ if (parray[p].adminLevel > 0) {
+ psend_raw_file(p, mess_dir, MESS_ADMOTD);
+ } else {
+ psend_raw_file(p, mess_dir, MESS_MOTD);
+ }
+ if (!parray[p].passwd && parray[p].registered)
+ pprintf(p, "\n*** You have no password. Please set one with the password command.");
+ if (!parray[p].registered)
+ psend_raw_file(p, mess_dir, MESS_UNREGISTERED);
+ parray[p].status = PLAYER_PROMPT;
+ player_write_login(p);
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (p1 == p)
+ continue;
+ if (parray[p1].status != PLAYER_PROMPT)
+ continue;
+#if 0 /* deleted by mann */
+ if (parray[p1].thisHost == parray[p].thisHost) {
+ fprintf(stderr, "FICS: Players %s and %s - same host: %s\n",
+ parray[p].name, parray[p1].name, dotQuad(parray[p].thisHost));
+ }
+#endif
+ if (!parray[p1].i_login)
+ continue;
+ if (parray[p1].adminLevel > 0) {
+/*
+ hp = gethostbyaddr((const char*)&(parray[p].thisHost), sizeof(parray[p].thisHost), AF_INET);
+*/
+ pprintf_prompt(p1, "\n[%s (%s: %s) has connected.]\n", parray[p].name,
+ (parray[p].registered ? "R" : "U"),
+ dotQuad(parray[p].thisHost));
+ } else {
+ pprintf_prompt(p1, "\n[%s has connected.]\n", parray[p].name);
+ }
+ }
+ parray[p].num_comments = player_num_comments(p);
+ messnum = player_num_messages(p);
+
+/* loon: don't send unreg any news. when you change this, feel free to
+ put all the news junk in one source file:) no reason for identical code
+ in command.c and comproc.c */
+ if (parray[p].registered) {
+ check_news(p, 0);
+ if (parray[p].adminLevel > 0) {
+ pprintf(p, "\n");
+ check_news(p, 1);
+ }
+ }
+ if (messnum) {
+ pprintf(p, "\nYou have %d messages.\nUse \"messages\" to display them, or \"clearmessages\" to remove them.\n", messnum);
+ }
+ player_notify_present(p);
+ player_notify(p, "arrived", "arrival");
+ showstored(p);
+ if (parray[p].registered && (parray[p].lastHost != 0) &&
+ (parray[p].lastHost != parray[p].thisHost)) {
+#if 0 /* removed by DAV - we don't need to know this rubbish */
+ fprintf(stderr, "FICS: Player %s: Last login: %s ", parray[p].name,
+ dotQuad(parray[p].lastHost));
+ fprintf(stderr, "This login: %s\n", dotQuad(parray[p].thisHost));
+#endif
+ pprintf(p, "\nPlayer %s: Last login: %s ", parray[p].name,
+ dotQuad(parray[p].lastHost));
+ pprintf(p, "This login: %s", dotQuad(parray[p].thisHost));
+ }
+ parray[p].lastHost = parray[p].thisHost;
+ if (parray[p].registered && !parray[p].timeOfReg)
+ parray[p].timeOfReg = time(0);
+ parray[p].logon_time = parray[p].last_command_time = time(0);
+ dummy = check_and_print_shutdown(p); /*Tells the user if we are going to shutdown */
+ pprintf(p, "\n%s", parray[p].prompt);
+ return 0;
+}
+
+PRIVATE int process_prompt(int p, char *command)
+{
+ int retval;
+ char *cmd = "";
+
+ command = eattailwhite(eatwhite(command));
+ if (!*command) {
+ pprintf(p, "%s", parray[p].prompt);
+ return COM_OK;
+ }
+ retval = process_command(p, command, &cmd);
+ switch (retval) {
+ case COM_OK:
+ retval = COM_OK;
+ pprintf(p, "%s", parray[p].prompt);
+ break;
+ case COM_OK_NOPROMPT:
+ retval = COM_OK;
+ break;
+ case COM_ISMOVE:
+ retval = COM_OK;
+
+#ifdef TIMESEAL
+ if (parray[p].game >= 0 && garray[parray[p].game].status == GAME_ACTIVE
+ && parray[p].side == garray[parray[p].game].game_state.onMove
+ && garray[parray[p].game].flag_pending != FLAG_NONE) {
+ ExecuteFlagCmd(parray[p].game, con[parray[p].socket].time);
+ }
+#endif
+ process_move(p, command);
+ pprintf(p, "%s", parray[p].prompt);
+
+ break;
+ case COM_RIGHTS:
+ pprintf(p, "%s: Insufficient rights.\n", cmd);
+ pprintf(p, "%s", parray[p].prompt);
+ retval = COM_OK;
+ break;
+ case COM_AMBIGUOUS:
+/* pprintf(p, "%s: Ambiguous command.\n", cmd); */
+ {
+ int len = strlen(cmd);
+ int i = 0;
+ pprintf(p, "Ambiguous command. Matches:");
+ while (command_list[i].comm_name) {
+ if ((!strncmp(command_list[i].comm_name, cmd, len)) &&
+ (parray[p].adminLevel >= command_list[i].adminLevel)) {
+ pprintf(p, " %s", command_list[i].comm_name);
+ }
+ i++;
+ }
+ }
+ pprintf(p, "\n%s", parray[p].prompt);
+ retval = COM_OK;
+ break;
+ case COM_BADPARAMETERS:
+ printusage(p, command_list[lastCommandFound].comm_name);
+ pprintf(p, "%s", parray[p].prompt);
+ retval = COM_OK;
+ break;
+ case COM_FAILED:
+ case COM_BADCOMMAND:
+ pprintf(p, "%s: Command not found.\n", cmd);
+ retval = COM_OK;
+ pprintf(p, "%s", parray[p].prompt);
+ break;
+ case COM_LOGOUT:
+ retval = COM_LOGOUT;
+ break;
+ }
+ return retval;
+}
+
+/* Return 1 to disconnect */
+PUBLIC int process_input(int fd, char *com_string)
+{
+ int p = player_find(fd);
+ int retval = 0;
+
+ if (p < 0) {
+ fprintf(stderr, "FICS: Input from a player not in array!\n");
+ return -1;
+ }
+
+ commanding_player = p;
+ parray[p].last_command_time = time(0);
+
+ switch (parray[p].status) {
+ case PLAYER_EMPTY:
+ fprintf(stderr, "FICS: Command from an empty player!\n");
+ break;
+ case PLAYER_NEW:
+ fprintf(stderr, "FICS: Command from a new player!\n");
+ break;
+ case PLAYER_INQUEUE:
+ /* Ignore input from player in queue */
+ break;
+ case PLAYER_LOGIN:
+ retval = process_login(p, com_string);
+ if (retval == COM_LOGOUT && com_string != NULL)
+ fprintf (stderr, "%s tried to log in and failed.\n", com_string);
+ break;
+ case PLAYER_PASSWORD:
+ retval = process_password(p, com_string);
+ break;
+ case PLAYER_PROMPT:
+ parray[p].busy[0] = '\0';
+ /* added this to stop buggy admin levels; shane */
+ if (parray[p].adminLevel < 10)
+ parray[p].adminLevel = 0;
+ retval = process_prompt(p, com_string);
+ break;
+ }
+
+ commanding_player = -1;
+ return retval;
+}
+
+PUBLIC int process_new_connection(int fd, unsigned int fromHost)
+{
+ int p = player_new();
+
+ parray[p].status = PLAYER_LOGIN;
+ parray[p].socket = fd;
+ parray[p].thisHost = fromHost;
+ parray[p].logon_time = time(0);
+ psend_raw_file(p, mess_dir, MESS_WELCOME);
+ pprintf(p, "Head admin : %s Complaints to : %s\n", hadmin_handle, hadmin_email);
+ pprintf(p, "Server location: %s Server version : %s\n", fics_hostname, VERS_NUM);
+ psend_raw_file(p, mess_dir, MESS_LOGIN);
+ pprintf(p, "login: ");
+ return 0;
+}
+
+PUBLIC int process_disconnection(int fd)
+{
+ int p = player_find(fd);
+ int p1;
+
+ if (p < 0) {
+ fprintf(stderr, "FICS: Disconnect from a player not in array!\n");
+ return -1;
+ }
+ if ((parray[p].game >=0) &&(garray[parray[p].game].status == GAME_EXAMINE)) {
+ pcommand(p, "unexamine");
+ }
+ if ((parray[p].game >=0) &&in_list(p, L_ABUSER, parray[p].name)) {
+ pcommand(p, "resign");
+ }
+ if (parray[p].status == PLAYER_PROMPT) {
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (p1 == p)
+ continue;
+ if (parray[p1].status != PLAYER_PROMPT)
+ continue;
+ if (!parray[p1].i_login)
+ continue;
+ pprintf_prompt(p1, "\n[%s has disconnected.]\n", parray[p].name);
+ }
+ player_notify(p, "departed", "departure");
+ player_notify_departure(p);
+ player_write_logout(p);
+ if (parray[p].registered) {
+ parray[p].totalTime += time(0) - parray[p].logon_time;
+ player_save(p);
+ } else { /* delete unreg history file */
+ char fname[MAX_FILENAME_SIZE];
+ sprintf(fname, "%s/player_data/%c/%s.games", stats_dir, parray[p].login[0], parray[p].login);
+ unlink(fname);
+ }
+ }
+ player_remove(p);
+ return 0;
+}
+
+/* Called every few seconds */
+PUBLIC int process_heartbeat(int *fd)
+{
+ static int last_space = 0;
+ static int last_comfile = 0;
+/* static int last_ratings = 0; */
+ static int lastcalled = 0;
+ int time_since_last;
+ int p;
+/* static int rpid = 0; */
+ int now = time(0);
+
+/* game_update_times(); */
+
+ if (lastcalled == 0)
+ time_since_last = 0;
+ else
+ time_since_last = now - lastcalled;
+ lastcalled = now;
+
+ /* Check for timed out connections */
+ for (p = 0; p < p_num; p++) {
+ if (((parray[p].status == PLAYER_LOGIN) ||
+ (parray[p].status == PLAYER_PASSWORD)) &&
+ (player_idle(p) > MAX_LOGIN_IDLE)) {
+ pprintf(p, "\n**** LOGIN TIMEOUT ****\n");
+ *fd = parray[p].socket;
+ return COM_LOGOUT;
+ }
+ if ((parray[p].status == PLAYER_PROMPT) &&
+ (player_idle(p) > MAX_IDLE_TIME) &&
+ (parray[p].adminLevel == 0) &&
+ (!in_list(p, L_TD, parray[p].name))) {
+ pprintf(p, "\n**** Auto-logout because you were idle "
+ "more than one hour. ****\n");
+ *fd = parray[p].socket;
+ return COM_LOGOUT;
+ }
+ }
+/* loon: turning this fork off temporarily (28 Oct) for lag */
+#if 0
+ if (rpid) { /* Rating calculating going on */
+ int statusp;
+
+ if (wait3(&statusp, WNOHANG, NULL) == rpid) {
+ fprintf(stderr, "FICS: Reinitting statistics.\n");
+ rating_init(); /* Child finished, get the results. */
+ rpid = 0;
+ }
+ }
+ /* Recalc ratings every 3 hours */
+ /* This is done because the ratings stats and players can get out of sync
+ if there is a system crash. */
+ /* This is done as a child process and read results when complete */
+ if (last_ratings == 0)
+ last_ratings = (now - (5 * 60 * 60)) + 120; /* Do one in 2 minutes */
+ else {
+ if (last_ratings + 6 * 60 * 60 < now) {
+ last_ratings = now;
+ rpid = fork();
+ if (rpid < 0) {
+ fprintf(stderr, "FICS: Couldn't fork\n");
+ } else {
+ if (rpid == 0) { /* The child */
+ rating_recalc();
+ exit(0);
+ fprintf(stderr, "Recalc process should never get here!\n");
+ ASSERT(0);
+ }
+ }
+ }
+ }
+#endif
+ /* Check for the communication file from mail updates every 10 minutes */
+ /* That is probably too often, but who cares */
+ if (MailGameResult) {
+ if (last_comfile == 0)
+ last_comfile = now;
+ else {
+ if (last_comfile + 10 * 60 < now) {
+ last_comfile = now;
+ /* Check for com file */
+/* hostinfo_checkcomfile(); */
+ }
+ }
+ }
+ if (last_space == 0)
+ last_space = now;
+ else {
+ if (last_space + 60 < now) {/* Check the disk space every minute */
+ last_space = now;
+ if (available_space() < 1000000) {
+ server_shutdown(60, " **** Disk space is dangerously low!!! ****\n");
+ }
+ /* Check for com file */
+ /* hostinfo_checkcomfile(); */
+ }
+ }
+ ShutHeartBeat();
+ return COM_OK;
+}
+
+PUBLIC void commands_init()
+{
+ FILE *fp, *afp;
+ char fname[MAX_FILENAME_SIZE];
+ int i = 0;
+
+ sprintf(fname, "%s/commands", comhelp_dir);
+ fp = fopen(fname, "w");
+ if (!fp) {
+ fprintf(stderr, "FICS: Could not write commands help file.\n");
+ return;
+ }
+ sprintf(fname, "%s/admin_commands", adhelp_dir);
+ afp = fopen(fname, "w");
+ if (!afp) {
+ fprintf(stderr, "FICS: Could not write admin_commands help file.\n");
+ fclose(fp);
+ return;
+ }
+ while (command_list[i].comm_name) {
+ if (command_list[i].adminLevel >= ADMIN_ADMIN) {
+ fprintf(afp, "%s\n", command_list[i].comm_name);
+ } else {
+ fprintf(fp, "%s\n", command_list[i].comm_name);
+ }
+ i++;
+ }
+ fclose(fp);
+ fclose(afp);
+}
+
+/* Need to save rated games */
+PUBLIC void TerminateCleanup()
+{
+ int p1;
+ int g;
+
+ for (g = 0; g < g_num; g++) {
+ if (garray[g].status != GAME_ACTIVE)
+ continue;
+ if (garray[g].rated) {
+ game_ended(g, WHITE, END_ADJOURN);
+ }
+ }
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (parray[p1].status == PLAYER_EMPTY)
+ continue;
+ pprintf(p1, "\n **** Server shutting down immediately. ****\n\n");
+ if (parray[p1].status != PLAYER_PROMPT) {
+ close(parray[p1].socket);
+ } else {
+ pprintf(p1, "Logging you out.\n");
+ psend_raw_file(p1, mess_dir, MESS_LOGOUT);
+ player_write_logout(p1);
+ if (parray[p1].registered)
+ parray[p1].totalTime += time(0) - parray[p1].logon_time;
+ player_save(p1);
+ }
+ }
+}
diff --git a/FICS/command.h b/FICS/command.h
new file mode 100644
index 0000000..c4e5648
--- /dev/null
+++ b/FICS/command.h
@@ -0,0 +1,140 @@
+/* command.h
+ *
+ */
+
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name email yy/mm/dd Change
+ Richard Nash 93/10/22 Created
+*/
+
+#ifndef _COMMAND_H
+#define _COMMAND_H
+
+#include "variable.h"
+#include "stdinclude.h"
+
+extern char *mess_dir;
+extern char *index_dir;
+extern char *help_dir[NUM_LANGS];
+extern char *comhelp_dir;
+extern char *info_dir;
+extern char *adhelp_dir;
+extern char *uscf_dir;
+extern char *stats_dir;
+extern char *config_dir;
+extern char *player_dir;
+extern char *adj_dir;
+extern char *hist_dir;
+extern char *journal_dir;
+extern char *board_dir;
+extern char *def_prompt;
+extern char *source_dir;
+extern char *lists_dir;
+extern char *news_dir;
+extern char *usage_dir[NUM_LANGS];
+
+extern char *hadmin_handle;
+
+extern int startuptime;
+extern char fics_hostname[81];
+extern int player_high;
+extern int game_high;
+extern int MailGameResult;
+
+/* Maximum length of a login name */
+#define MAX_LOGIN_NAME 20
+
+/* Maximum number of parameters per command */
+#define MAXNUMPARAMS 10
+
+/* Maximum string length of a single command word */
+#define MAX_COM_LENGTH 50
+
+/* Maximum string length of the whole command line */
+#define MAX_STRING_LENGTH 1024
+
+#define COM_OK 0
+#define COM_FAILED 1
+#define COM_ISMOVE 2
+#define COM_AMBIGUOUS 3
+#define COM_BADPARAMETERS 4
+#define COM_BADCOMMAND 5
+#define COM_LOGOUT 6
+#define COM_FLUSHINPUT 7
+#define COM_RIGHTS 8
+#define COM_OK_NOPROMPT 9
+
+#define ADMIN_USER 0
+#define ADMIN_ADMIN 10
+#define ADMIN_MASTER 20
+#define ADMIN_DEMIGOD 60
+#define ADMIN_GOD 100
+
+#define TYPE_NULL 0
+/*#define TYPE_NULL NULL <-- this is WRONG WRONG WRONG! --mann 5/10/95 */
+#define TYPE_WORD 1
+#define TYPE_STRING 2
+#define TYPE_INT 3
+typedef struct u_parameter {
+ int type;
+ union {
+ char *word;
+ char *string;
+ int integer;
+ } val;
+} parameter;
+
+typedef parameter param_list[MAXNUMPARAMS];
+
+typedef struct s_command_type {
+ char *comm_name;
+ char *param_string;
+ int (*comm_func)();
+ int adminLevel;
+} command_type;
+
+typedef struct s_alias_type {
+ char *comm_name;
+ char *alias;
+} alias_type;
+
+extern int commanding_player; /* The player whose command you're in */
+
+extern int process_input(int, char *);
+extern int process_new_connection(int, unsigned int);
+extern int process_disconnection(int);
+/* extern int process_incomplete(int, char *); */
+extern int process_heartbeat(int *);
+
+extern void commands_init(void);
+
+extern void TerminateCleanup(void);
+extern int process_command(int, char *, char **);
+
+
+extern int alias_lookup();
+extern int gethostname();
+
+#if defined(SGI) || defined(NETBSD)
+#else
+extern int wait3();
+/* extern char tolower(); */
+/* extern int sscanf(); */
+#endif
+
+#endif /* _COMMAND_H */
diff --git a/FICS/command.h.orig b/FICS/command.h.orig
new file mode 100644
index 0000000..1d5d47f
--- /dev/null
+++ b/FICS/command.h.orig
@@ -0,0 +1,135 @@
+/* command.h
+ *
+ */
+
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name email yy/mm/dd Change
+ Richard Nash 93/10/22 Created
+*/
+
+#include "variable.h"
+#ifndef _COMMAND_H
+#define _COMMAND_H
+
+extern char *mess_dir;
+extern char *index_dir;
+extern char *help_dir[NUM_LANGS];
+extern char *comhelp_dir;
+extern char *info_dir;
+extern char *adhelp_dir;
+extern char *stats_dir;
+extern char *config_dir;
+extern char *player_dir;
+extern char *adj_dir;
+extern char *hist_dir;
+extern char *board_dir;
+extern char *def_prompt;
+extern char *source_dir;
+extern char *lists_dir;
+extern char *news_dir;
+
+extern char *hadmin_handle;
+
+extern int startuptime;
+extern char fics_hostname[81];
+extern int player_high;
+extern int game_high;
+extern int MailGameResult;
+
+/* Maximum length of a login name */
+#define MAX_LOGIN_NAME 20
+
+/* Maximum number of parameters per command */
+#define MAXNUMPARAMS 10
+
+/* Maximum string length of a single command word */
+#define MAX_COM_LENGTH 50
+
+/* Maximum string length of the whole command line */
+#define MAX_STRING_LENGTH 1024
+
+#define COM_OK 0
+#define COM_FAILED 1
+#define COM_ISMOVE 2
+#define COM_AMBIGUOUS 3
+#define COM_BADPARAMETERS 4
+#define COM_BADCOMMAND 5
+#define COM_LOGOUT 6
+#define COM_FLUSHINPUT 7
+#define COM_RIGHTS 8
+#define COM_OK_NOPROMPT 9
+
+#define ADMIN_USER 0
+#define ADMIN_ADMIN 10
+#define ADMIN_MASTER 20
+#define ADMIN_DEMIGOD 60
+#define ADMIN_GOD 100
+
+#define TYPE_NULL 0
+/*#define TYPE_NULL NULL <-- this is WRONG WRONG WRONG! --mann 5/10/95 */
+#define TYPE_WORD 1
+#define TYPE_STRING 2
+#define TYPE_INT 3
+typedef struct u_parameter {
+ int type;
+ union {
+ char *word;
+ char *string;
+ int integer;
+ } val;
+} parameter;
+
+typedef parameter param_list[MAXNUMPARAMS];
+
+typedef struct s_command_type {
+ char *comm_name;
+ char *param_string;
+ int (*comm_func)();
+ int adminLevel;
+} command_type;
+
+typedef struct s_alias_type {
+ char *comm_name;
+ char *alias;
+} alias_type;
+
+extern int commanding_player; /* The player whose command you're in */
+
+extern int process_input(int, char *);
+extern int process_new_connection(int, unsigned int);
+extern int process_disconnection(int);
+/* extern int process_incomplete(int, char *); */
+extern int process_heartbeat(int *);
+
+extern void commands_init(void);
+
+extern void TerminateCleanup(void);
+extern int process_command(int, char *, char **);
+
+
+extern int alias_lookup();
+extern int gethostname();
+
+#if defined(SGI)
+#else
+extern int wait3();
+/* extern char tolower(); */
+/* extern int sscanf(); */
+#endif
+
+#endif /* _COMMAND_H */
diff --git a/FICS/command_list.h b/FICS/command_list.h
new file mode 100644
index 0000000..104bf81
--- /dev/null
+++ b/FICS/command_list.h
@@ -0,0 +1,272 @@
+/* command_list.h
+ *
+ */
+
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name email yy/mm/dd Change
+ Richard Nash 93/10/22 Created
+*/
+
+#ifndef _COMMAND_LIST_H
+#define _COMMAND_LIST_H
+
+#include "comproc.h"
+#include "matchproc.h"
+#include "talkproc.h"
+#include "lists.h"
+#include "gameproc.h"
+#include "obsproc.h"
+#include "adminproc.h"
+#include "playerdb.h"
+#include "ratings.h"
+#include "eco.h"
+#include "rating_conv.h"
+#include "shutdown.h"
+
+/*
+ Parameter string format
+ w - a word
+ o - an optional word
+ d - integer
+ p - optional integer
+ i - word or integer
+ n - optional word or integer
+ s - string to end
+ t - optional string to end
+
+ If the parameter option is given in lower case then the parameter is
+ converted to lower case before being passsed to the function. If it is
+ in upper case, then the parameter is passed as typed.
+ */
+/* Try to keep this list in alpha order, that is the way it is shown to
+ * the 'help commands' command.
+ */
+ /* Name Options Functions Security */
+PUBLIC command_type command_list[] = {
+
+ {"abort", "", com_abort, ADMIN_USER },
+ {"accept", "n", com_accept, ADMIN_USER },
+ {"addlist", "ww", com_addlist, ADMIN_USER },
+ {"adjourn", "", com_adjourn, ADMIN_USER },
+ {"alias", "oT", com_alias, ADMIN_USER },
+ {"allobservers", "n", com_allobservers, ADMIN_USER },
+ {"assess", "oo", com_assess, ADMIN_USER },
+ {"backward", "p", com_backward, ADMIN_USER },
+ {"bell", "", com_bell, ADMIN_USER },
+ {"best", "o", com_best, ADMIN_USER },
+ {"boards", "o", com_boards, ADMIN_USER },
+/* {"channel", "p", com_channel, ADMIN_USER }, */
+ {"clearmessages", "n", com_clearmessages, ADMIN_USER },
+ {"convert_bcf", "d", com_CONVERT_BCF, ADMIN_USER },
+ {"convert_elo", "d", com_CONVERT_ELO, ADMIN_USER },
+ {"convert_uscf", "d", com_CONVERT_USCF, ADMIN_USER },
+ {"cshout", "S", com_cshout, ADMIN_USER },
+ {"date", "", com_date, ADMIN_USER },
+ {"decline", "n", com_decline, ADMIN_USER },
+ {"draw", "", com_draw, ADMIN_USER },
+ {"eco", "n", com_eco, ADMIN_USER },
+ {"examine", "on", com_examine, ADMIN_USER },
+ {"finger", "o", com_stats, ADMIN_USER },
+ {"flag", "", com_flag, ADMIN_USER },
+ {"flip", "", com_flip, ADMIN_USER },
+ {"forward", "p", com_forward, ADMIN_USER },
+ {"games", "o", com_games, ADMIN_USER },
+ {"getpi", "w", com_getpi, ADMIN_USER },
+ {"goboard", "w", com_goboard, ADMIN_USER },
+ {"gonum", "d", com_gonum, ADMIN_USER },
+ {"handles", "w", com_handles, ADMIN_USER },
+ {"hbest", "o", com_hbest, ADMIN_USER },
+ {"help", "o", com_help, ADMIN_USER },
+ {"history", "o", com_history, ADMIN_USER },
+ {"hrank", "oo", com_hrank, ADMIN_USER },
+ {"inchannel", "n", com_inchannel, ADMIN_USER },
+ {"index", "o", com_index, ADMIN_USER },
+ {"info", "", com_info, ADMIN_USER },
+ {"it", "T", com_it, ADMIN_USER },
+ {"journal", "o", com_journal, ADMIN_USER },
+ {"jsave", "wwi", com_jsave, ADMIN_USER },
+ {"kibitz", "S", com_kibitz, ADMIN_USER },
+ {"limits", "", com_limits, ADMIN_USER },
+ {"llogons", "", com_llogons, ADMIN_USER },
+/* {"load", "ww", com_load, ADMIN_USER }, */
+ {"logons", "o", com_logons, ADMIN_USER },
+ {"mailhelp", "o", com_mailhelp, ADMIN_USER },
+ {"mailmess", "", com_mailmess, ADMIN_USER },
+ {"mailmoves", "n", com_mailmoves, ADMIN_USER },
+ {"mailoldmoves", "o", com_mailoldmoves, ADMIN_USER },
+ {"mailsource", "o", com_mailsource, ADMIN_USER },
+ {"mailstored", "wi", com_mailstored, ADMIN_USER },
+ {"match", "wt", com_match, ADMIN_USER },
+ {"messages", "nT", com_messages, ADMIN_USER },
+ {"mexamine", "w", com_mexamine, ADMIN_USER },
+ {"moretime", "d", com_moretime, ADMIN_USER },
+ {"moves", "n", com_moves, ADMIN_USER },
+ {"news", "o", com_news, ADMIN_USER },
+ {"next", "", com_more, ADMIN_USER },
+ {"observe", "n", com_observe, ADMIN_USER },
+ {"oldmoves", "o", com_oldmoves, ADMIN_USER },
+ {"open", "", com_open, ADMIN_USER },
+ {"partner", "o", com_partner, ADMIN_USER },
+ {"password", "WW", com_password, ADMIN_USER },
+ {"pause", "", com_pause, ADMIN_USER },
+ {"pending", "", com_pending, ADMIN_USER },
+ {"prefresh", "", com_prefresh, ADMIN_USER },
+ {"promote", "w", com_promote, ADMIN_USER },
+ {"ptell", "S", com_ptell, ADMIN_USER },
+ {"qtell", "iS", com_qtell, ADMIN_USER },
+ {"quit", "", com_quit, ADMIN_USER },
+ {"rank", "oo", com_rank, ADMIN_USER },
+ {"refresh", "n", com_refresh, ADMIN_USER },
+ {"revert", "", com_revert, ADMIN_USER },
+ {"resign", "o", com_resign, ADMIN_USER },
+ {"say", "S", com_say, ADMIN_USER },
+ {"servers", "", com_servers, ADMIN_USER },
+ {"set", "wT", com_set, ADMIN_USER },
+ {"shout", "T", com_shout, ADMIN_USER },
+ {"showlist", "o", com_showlist, ADMIN_USER },
+ {"simabort", "", com_simabort, ADMIN_USER },
+ {"simallabort", "", com_simallabort,ADMIN_USER },
+ {"simadjourn", "", com_simadjourn, ADMIN_USER },
+ {"simalladjourn", "", com_simalladjourn,ADMIN_USER },
+ {"simgames", "o", com_simgames, ADMIN_USER },
+ {"simmatch", "w", com_simmatch, ADMIN_USER },
+ {"simnext", "", com_simnext, ADMIN_USER },
+ {"simopen", "", com_simopen, ADMIN_USER },
+ {"simpass", "", com_simpass, ADMIN_USER },
+ {"simprev", "", com_simprev, ADMIN_USER },
+ {"smoves", "wi", com_smoves, ADMIN_USER },
+ {"sposition", "ww", com_sposition, ADMIN_USER },
+ {"statistics", "", com_statistics, ADMIN_USER },
+ {"stored", "o", com_stored, ADMIN_USER },
+ {"style", "d", com_style, ADMIN_USER },
+ {"sublist", "ww", com_sublist, ADMIN_USER },
+ {"switch", "", com_switch, ADMIN_USER },
+ {"takeback", "p", com_takeback, ADMIN_USER },
+ {"tell", "nS", com_tell, ADMIN_USER },
+ {"time", "n", com_time, ADMIN_USER },
+ {"unalias", "w", com_unalias, ADMIN_USER },
+ {"unexamine", "", com_unexamine, ADMIN_USER },
+ {"unobserve", "n", com_unobserve, ADMIN_USER },
+ {"unpause", "", com_unpause, ADMIN_USER },
+ {"uptime", "", com_uptime, ADMIN_USER },
+ {"uscf", "o", com_uscf, ADMIN_USER },
+ {"variables", "o", com_variables, ADMIN_USER },
+ {"whenshut", "", com_whenshut, ADMIN_USER },
+ {"whisper", "S", com_whisper, ADMIN_USER },
+ {"who", "T", com_who, ADMIN_USER },
+ {"withdraw", "n", com_withdraw, ADMIN_USER },
+ {"xtell", "wS", com_xtell, ADMIN_USER },
+ {"znotify", "", com_znotify, ADMIN_USER },
+
+ {"addcomment", "wS", com_addcomment, ADMIN_ADMIN },
+ {"addplayer", "WWS", com_addplayer, ADMIN_ADMIN },
+ {"adjudicate", "www", com_adjudicate, ADMIN_ADMIN },
+ {"ahelp", "o", com_adhelp, ADMIN_ADMIN },
+ {"admin", "", com_admin, ADMIN_ADMIN },
+ {"anews", "o", com_anews, ADMIN_ADMIN },
+ {"announce", "S", com_announce, ADMIN_ADMIN },
+ {"annunreg", "S", com_annunreg, ADMIN_ADMIN },
+ {"asetv", "wS", com_asetv, ADMIN_ADMIN },
+ {"asetadmin", "wd", com_asetadmin, ADMIN_ADMIN },
+ {"asetblitz", "wdpppp",com_asetblitz, ADMIN_ADMIN },
+ {"asetemail", "wO", com_asetemail, ADMIN_ADMIN },
+ {"asethandle", "WW", com_asethandle, ADMIN_ADMIN },
+ {"asetlight", "wdpppp",com_asetlight, ADMIN_ADMIN },
+ {"asetpasswd", "wW", com_asetpasswd, ADMIN_ADMIN },
+ {"asetrealname", "wT", com_asetrealname, ADMIN_ADMIN },
+ {"asetstd", "wdpppp",com_asetstd, ADMIN_ADMIN },
+ {"asetwild", "wdpppp",com_asetwild, ADMIN_ADMIN },
+ {"chkip", "w", com_checkIP, ADMIN_ADMIN },
+ {"chkgame", "i", com_checkGAME, ADMIN_ADMIN },
+ {"chkpl", "w", com_checkPLAYER, ADMIN_ADMIN },
+ {"chksc", "d", com_checkSOCKET, ADMIN_ADMIN },
+ {"chkts", "", com_checkTIMESEAL, ADMIN_ADMIN },
+ {"cmuzzle", "o", com_cmuzzle, ADMIN_ADMIN },
+ {"cnewsi", "S", com_cnewsi, ADMIN_ADMIN },
+ {"cnewsf", "dS", com_cnewsf, ADMIN_ADMIN },
+ {"canewsi", "S", com_canewsi, ADMIN_ADMIN },
+ {"canewsf", "dS", com_canewsf, ADMIN_ADMIN },
+ {"muzzle", "o", com_muzzle, ADMIN_ADMIN },
+ {"nuke", "w", com_nuke, ADMIN_ADMIN },
+ {"pose", "wS", com_pose, ADMIN_GOD },
+ {"asetmaxplayers", "p", com_asetmaxplayer, ADMIN_ADMIN },
+ {"quota", "p", com_quota, ADMIN_ADMIN },
+ {"raisedead", "WO", com_raisedead, ADMIN_ADMIN },
+ {"remplayer", "w", com_remplayer, ADMIN_ADMIN },
+ {"rerank", "w", com_fixrank, ADMIN_ADMIN },
+ {"showcomment", "w", com_showcomment, ADMIN_ADMIN },
+ {"shutdown", "oT", com_shutdown, ADMIN_ADMIN },
+ {"summon", "w", com_summon, ADMIN_ADMIN },
+
+ {NULL, NULL, NULL, ADMIN_USER}
+};
+
+PUBLIC alias_type g_alias_list[] = {
+ {"comment", "addcomment"},
+ {"adhelp", "ahelp"},
+ {"w", "who"},
+ {"h", "help"},
+ {"t", "tell"},
+ {"m", "match"},
+ {"go", "goboard"},
+ {"goto", "goboard"},
+ {"f", "finger"},
+ {"a", "accept"},
+ {"saa", "simallabort"},
+ {"saab", "simallaabort"},
+ {"sab", "simabort"},
+ {"sadj", "simadjourn"},
+ {"saadj", "simalladjourn"},
+ {"sh", "shout"},
+ {"sn", "simnext"},
+ {"sp", "simprev"},
+ {"vars", "variables"},
+ {"g", "games"},
+ {"players", "who a"},
+ {"p", "who a"},
+ {"pl", "who a"},
+ {"o", "observe"},
+ {"r", "refresh"},
+ {"re", "refresh"}, /* So r/re doesn't resign! */
+ {"ch", "channel"},
+ {"cls", "help cls"},
+ {"in", "inchannel"},
+ {".", "tell ."},
+ {",", "tell ,"},
+ {"`", "tell ."},
+ {"!", "shout"},
+ {"I", "it"},
+ {"i", "it"},
+ {":", "it"},
+ {"?", "help"},
+ {"exit", "quit"},
+ {"logout", "quit"},
+ {"bye", "quit"},
+ {"*", "kibitz"},
+ {"#", "whisper"},
+ {"ma", "match"},
+ {"more", "next"},
+ {"n", "next"},
+ {"znotl", "znotify"},
+ {"+", "addlist"},
+ {"-", "sublist"},
+ {"=", "showlist"},
+ {NULL, NULL}
+};
+
+#endif /* _COMMAND_LIST_H */
diff --git a/FICS/common.h b/FICS/common.h
new file mode 100644
index 0000000..b8afce6
--- /dev/null
+++ b/FICS/common.h
@@ -0,0 +1,56 @@
+/*
+ * File: common.h
+ * Copyright 1993, Richard V. Nash
+ */
+
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name email yy/mm/dd Change
+ Richard Nash 93/10/22 Created
+*/
+
+#ifndef _COMMON_H
+#define _COMMON_H
+
+#include "vers.h"
+#include "legal.h"
+
+#define PUBLIC
+#define PRIVATE static
+
+#ifndef NULL
+#define NULL ((void *)0)
+#endif
+
+#define SWAP(a,b,type) {\
+ type tmp; \
+ tmp = (a);\
+ (a) = (b);\
+ (b) = tmp;\
+}
+
+#ifdef DEBUG
+#define ASSERT(expression) \
+ while (((expression) ? 0 : \
+ (fprintf (stderr, "Assertion failed: file %s, line %d.\n", \
+ __FILE__, __LINE__), \
+ abort (), 0)))
+#else
+#define ASSERT(expression) while(0)
+#endif /* DEBUG */
+
+#endif /* _COMMON_H */
diff --git a/FICS/comproc.c b/FICS/comproc.c
new file mode 100644
index 0000000..b2d251c
--- /dev/null
+++ b/FICS/comproc.c
@@ -0,0 +1,1638 @@
+/* comproc.c
+ *
+ */
+
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name email yy/mm/dd Change
+ Richard Nash 93/10/22 Created
+ foxbat 95/03/11 added filters in cmatch.
+*/
+
+#include "stdinclude.h"
+
+#include "common.h"
+#include "talkproc.h"
+#include "comproc.h"
+#include "command.h"
+#include "utils.h"
+#include "ficsmain.h"
+#include "config.h"
+#include "playerdb.h"
+#include "network.h"
+#include "rmalloc.h"
+#include "variable.h"
+#include "gamedb.h"
+#include "gameproc.h"
+#include "obsproc.h"
+#include "board.h"
+/* #include "hostinfo.h" */
+#include "multicol.h"
+#include "ratings.h"
+#include "formula.h"
+#include "lists.h"
+#include "eco.h"
+#include "network.h"
+#include <string.h>
+
+#include <sys/resource.h>
+
+/* grimm */
+#if defined(SGI)
+#else
+/* int system(char *arg); */
+#endif
+
+const none = 0;
+const blitz_rat = 1;
+const std_rat = 2;
+const wild_rat = 3;
+const light_rat = 4;
+
+
+PUBLIC int com_rating_recalc(int p, param_list param)
+{
+ ASSERT(parray[p].adminLevel >= ADMIN_ADMIN);
+ rating_recalc();
+ return COM_OK;
+}
+
+PUBLIC int com_more(int p, param_list param)
+{
+ pmore_file(p);
+ return COM_OK;
+}
+
+PUBLIC int num_news = -1;
+
+PUBLIC void rscan_news2(FILE *fp, int p, int num)
+{
+ char junk[MAX_LINE_SIZE], count[10];
+ int crtime;
+ char *junkp;
+
+ if (num == 0)
+ return;
+
+ fgets(junk, MAX_LINE_SIZE, fp);
+ if (feof(fp))
+ return;
+ sscanf(junk, "%d %s", &crtime, count);
+ rscan_news2(fp, p, num - 1);
+ junkp = junk;
+ junkp = nextword(junkp);
+ junkp = nextword(junkp);
+ pprintf(p, "%3s (%s) %s", count, fix_time(strltime(&crtime)), junkp);
+}
+
+PUBLIC int com_news(int p, param_list param)
+{
+ FILE *fp;
+ char filename[MAX_FILENAME_SIZE];
+ char junk[MAX_LINE_SIZE];
+ char *junkp;
+ int crtime, found = 0;
+ char count[10];
+
+ sprintf(filename, "%s/newnews.index", news_dir);
+ fp = fopen(filename, "r");
+
+ if (!fp) {
+ fprintf(stderr, "Can\'t find news index.\n");
+ return COM_OK;
+ }
+
+ if (param[0].type == 0) {
+
+ /* no params - then just display index of last ten news items */
+
+ pprintf(p,"Index of recent news items:\n");
+ fgets(junk, MAX_LINE_SIZE, fp);
+ sscanf(junk, "%d %s", &crtime, count);
+ rscan_news2(fp, p, 9);
+ junkp = junk;
+ junkp = nextword(junkp);
+ junkp = nextword(junkp);
+ pprintf(p, "%3s (%s) %s", count, fix_time(strltime(&crtime)), junkp);
+ fclose(fp);
+
+ } else if ((param[0].type == TYPE_WORD) && !strcmp(param[0].val.word,"all")) {
+
+ /* param all - displays index all news items */
+
+ pprintf(p, "Index of all news items:\n");
+ fgets(junk, MAX_LINE_SIZE, fp);
+ sscanf(junk, "%d %s", &crtime, count);
+ rscan_news(fp, p, 0);
+ junkp = junk;
+ junkp = nextword(junkp);
+ junkp = nextword(junkp);
+ pprintf(p, "%3s (%s) %s", count, fix_time(strltime(&crtime)), junkp);
+ fclose(fp);
+
+ } else {
+
+ /* check if the specific news file exist in index */
+
+ while (!feof(fp) && !found) {
+ junkp = junk;
+ fgets(junk, MAX_LINE_SIZE, fp);
+ if (feof(fp))
+ break;
+ sscanf(junkp, "%d %s", &crtime, count);
+ if (!strcmp(count, param[0].val.word)) {
+ found = 1;
+ junkp = nextword(junkp);
+ junkp = nextword(junkp);
+ pprintf(p, "NEWS %3s (%s) %s\n", count, fix_time(strltime(&crtime)),
+ junkp);
+ }
+ }
+
+ fclose(fp);
+
+ if (!found) {
+ pprintf(p, "Bad index number!\n");
+ return COM_OK;
+ }
+
+ /* file exists - show it */
+ sprintf(filename, "%s/news.%s", news_dir, param[0].val.word);
+ fp = fopen(filename, "r");
+ if (!fp) {
+ pprintf(p, "No more info.\n");
+ return COM_OK;
+ }
+
+ fclose(fp);
+
+ sprintf(filename, "news.%s", param[0].val.word);
+ if (psend_file(p, news_dir, filename) < 0) {
+ pprintf(p, "Internal error - couldn't send news file!\n");
+ }
+ }
+
+ return COM_OK;
+}
+
+PUBLIC int com_quit(int p, param_list param)
+{
+ if ((parray[p].game >= 0) && (garray[parray[p].game].status == GAME_EXAMINE)) {
+ pcommand(p, "unexamine");
+ }
+
+ if (parray[p].game >= 0) {
+ pprintf(p, "You can't quit while you are playing a game.\nType 'resign' to resign the game, or you can request an abort with 'abort'.\n");
+ return COM_OK;
+ }
+ psend_logoutfile(p, mess_dir, MESS_LOGOUT);
+ return COM_LOGOUT;
+}
+
+PUBLIC int com_set(int p, param_list param)
+{
+ int result;
+ int which;
+ char *val;
+
+ if (param[1].type == TYPE_NULL)
+ val = NULL;
+ else
+ val = param[1].val.string;
+ result = var_set(p, param[0].val.word, val, &which);
+ switch (result) {
+ case VAR_OK:
+ break;
+ case VAR_BADVAL:
+ pprintf(p, "Bad value given for variable %s.\n", param[0].val.word);
+ break;
+ case VAR_NOSUCH:
+ pprintf(p, "No such variable name %s.\n", param[0].val.word);
+ break;
+ case VAR_AMBIGUOUS:
+ pprintf(p, "Ambiguous variable name %s.\n", param[0].val.word);
+ break;
+ }
+ /* player_save(p); */
+ return COM_OK;
+}
+
+PUBLIC int FindPlayer(int p, char* name, int *p1, int *connected)
+{
+ *p1 = player_search(p, name);
+ if (*p1 == 0)
+ return 0;
+ if (*p1 < 0) { /* player had to be connected and will be
+ removed later */
+ *connected = 0;
+ *p1 = (-*p1) - 1;
+ } else {
+ *connected = 1;
+ *p1 = *p1 - 1;
+ }
+ return 1;
+}
+
+PRIVATE void com_stats_andify(int *numbers, int howmany, char *dest)
+{
+ char tmp[10];
+
+ *dest = '\0';
+ while (howmany--) {
+ sprintf(tmp, "%d", numbers[howmany]);
+ strcat(dest, tmp);
+ if (howmany > 1)
+ sprintf(tmp, ", ");
+ else if (howmany == 1)
+ sprintf(tmp, " and ");
+ else
+ sprintf(tmp, ".\n");
+ strcat(dest, tmp);
+ }
+ return;
+}
+
+PRIVATE void com_stats_rating(char *hdr, statistics * stats, char *dest)
+{
+ char tmp[100];
+
+ sprintf(dest, "%-10s%4s %5.1f %4d %4d %4d %4d",
+ hdr, ratstr(stats->rating), stats->sterr, stats->win, stats->los, stats->dra, stats->num);
+ if (stats->whenbest) {
+ sprintf(tmp, " %d", stats->best);
+ strcat(dest, tmp);
+ strftime(tmp, sizeof(tmp), " (%d-%b-%y)", localtime((time_t *) & stats->whenbest));
+ strcat(dest, tmp);
+ }
+ strcat(dest, "\n");
+ return;
+}
+
+PUBLIC int com_stats(int p, param_list param)
+{
+ int g, i, t;
+ int p1, connected;
+ char line[255], tmp[255];
+ int numbers[MAX_OBSERVE > MAX_SIMUL ? MAX_OBSERVE : MAX_SIMUL];
+ int onTime;
+
+ if (param[0].type == TYPE_WORD) {
+ if (!FindPlayer(p, param[0].val.word, &p1, &connected))
+ return COM_OK;
+ } else {
+ p1 = p;
+ connected = 1;
+ }
+
+ sprintf(line, "\nStatistics for %-11s ", parray[p1].name);
+ if ((connected) && (parray[p1].status == PLAYER_PROMPT)) {
+ sprintf(tmp, "On for: %s", hms_desc(player_ontime(p1)));
+ strcat(line, tmp);
+ sprintf(tmp, " Idle: %s\n", hms_desc(player_idle(p1)));
+ } else {
+ if ((t = player_lastdisconnect(p1)))
+ sprintf(tmp, "(Last disconnected %s):\n", strltime(&t));
+ else
+ sprintf(tmp, "(Never connected.)\n");
+ }
+ strcat(line, tmp);
+ pprintf(p, "%s", line);
+ if (parray[p1].simul_info.numBoards) {
+ for (i = 0, t = 0; i < parray[p1].simul_info.numBoards; i++) {
+ if ((numbers[t] = parray[p1].simul_info.boards[i] + 1) != 0)
+ t++;
+ }
+ pprintf(p, "%s is giving a simul: game%s ", parray[p1].name, ((t > 1) ? "s" : ""));
+ com_stats_andify(numbers, t, tmp);
+ pprintf(p, tmp);
+ } else if (parray[p1].game >= 0) {
+ g = parray[p1].game;
+ if (garray[g].status == GAME_EXAMINE) {
+ pprintf(p, "(Examining game %d: %s vs. %s)\n", g + 1,
+ garray[g].white_name, garray[g].black_name);
+ } else {
+ pprintf(p, "(playing game %d: %s vs. %s)\n", g + 1,
+ parray[garray[g].white].name, parray[garray[g].black].name);
+ if (garray[g].link >= 0) {
+ pprintf(p, "(partner is playing game %d: %s vs. %s)\n", garray[g].link + 1,
+ parray[garray[garray[g].link].white].name,
+ parray[garray[garray[g].link].black].name);
+ }
+ }
+ }
+ if (parray[p1].num_observe) {
+ for (i = 0, t = 0; i < parray[p1].num_observe; i++) {
+ g = parray[p1].observe_list[i];
+ if ((g != -1) && ((parray[p].adminLevel >= ADMIN_ADMIN) || (garray[g].private == 0)))
+ numbers[t++] = g + 1;
+ }
+ if (t) {
+ pprintf(p, "%s is observing game%s ", parray[p1].name, ((t > 1) ? "s" : ""));
+ com_stats_andify(numbers, t, tmp);
+ pprintf(p, tmp);
+ }
+ }
+ if (parray[p1].busy[0]) {
+ pprintf(p, "(%s %s)\n", parray[p1].name, parray[p1].busy);
+ }
+ if (!parray[p1].registered) {
+ pprintf(p, "%s is NOT a registered player.\n\n", parray[p1].name);
+ } else {
+ pprintf(p, "\n rating RD win loss draw total best\n");
+ com_stats_rating("Blitz", &parray[p1].b_stats, tmp);
+ pprintf(p, tmp);
+ com_stats_rating("Standard", &parray[p1].s_stats, tmp);
+ pprintf(p, tmp);
+ com_stats_rating("Lightning", &parray[p1].l_stats, tmp);
+ pprintf(p, tmp);
+ com_stats_rating("Wild", &parray[p1].w_stats, tmp);
+ pprintf(p, tmp);
+ }
+ pprintf(p, "\n");
+ if (parray[p1].adminLevel > 0) {
+ pprintf(p, "Admin Level: ");
+ switch (parray[p1].adminLevel) {
+ case 5:
+ pprintf(p, "Authorized Helper Person\n");
+ break;
+ case 10:
+ pprintf(p, "Administrator\n");
+ break;
+ case 15:
+ pprintf(p, "Help File Librarian/Administrator\n");
+ break;
+ case 20:
+ pprintf(p, "Master Administrator\n");
+ break;
+ case 50:
+ pprintf(p, "Master Help File Librarian/Administrator\n");
+ break;
+ case 60:
+ pprintf(p, "Assistant Super User\n");
+ break;
+ case 100:
+ pprintf(p, "Super User\n");
+ break;
+ default:
+ pprintf(p, "%d\n", parray[p1].adminLevel);
+ break;
+ }
+ }
+ if (parray[p].adminLevel > 0)
+ pprintf(p, "Full Name : %s\n", (parray[p1].fullName ? parray[p1].fullName : "(none)"));
+ if (((p1 == p) && (parray[p1].registered)) || (parray[p].adminLevel > 0))
+ pprintf(p, "Address : %s\n", (parray[p1].emailAddress ? parray[p1].emailAddress : "(none)"));
+ if (parray[p].adminLevel > 0) {
+ pprintf(p, "Host : %s\n",
+ dotQuad(connected ? parray[p1].thisHost : parray[p1].lastHost));
+ }
+ if ((parray[p].adminLevel > 0) && (parray[p1].registered))
+ if (parray[p1].num_comments)
+ pprintf(p, "Comments : %d\n", parray[p1].num_comments);
+
+ if (connected && parray[p1].registered && (p==p1 ||
+ (parray[p].adminLevel > 0))) {
+ char *timeToStr = ctime((time_t *) &parray[p1].timeOfReg);
+
+ timeToStr[strlen(timeToStr)-1]='\0';
+ pprintf(p, "\n");
+ onTime = (time(0) - parray[p1].logon_time) + parray[p1].totalTime;
+
+ pprintf(p, "Total time on-line: %s\n", hms_desc(onTime) );
+ pprintf(p, "%% of life on-line: %3.1f (since %s)\n",
+ (double)((onTime*100)/(double)(time(0)-parray[p1].timeOfReg)),
+ timeToStr);
+ }
+
+#ifdef TIMESEAL
+ if (connected)
+ pprintf(p, "\nTimeseal : %s\n",
+ (con[parray[p1].socket].timeseal) ? "On" : "Off");
+ if ((parray[p].adminLevel > 0) && (connected)) {
+ if (findConnection(parray[p1].socket) && con[parray[p1].socket].timeseal) {
+ pprintf(p, "Unix acc : %s\nSystem/OS : %s\n",
+ con[parray[p1].socket].user,
+ con[parray[p1].socket].sys);
+ }
+ }
+#endif
+
+ if (parray[p1].num_plan) {
+ pprintf(p, "\n");
+ for (i = 0; i < parray[p1].num_plan; i++)
+ pprintf(p, "%2d: %s\n", i + 1, (parray[p1].planLines[i] != NULL) ? parray[p1].planLines[i] : "");
+ }
+ if (!connected)
+ player_remove(p1);
+ return COM_OK;
+}
+
+PUBLIC int com_password(int p, param_list param)
+{
+ char *oldpassword = param[0].val.word;
+ char *newpassword = param[1].val.word;
+ char salt[3];
+
+ if (!parray[p].registered) {
+ pprintf(p, "Setting a password is only for registered players.\n");
+ return COM_OK;
+ }
+ if (parray[p].passwd) {
+ salt[0] = parray[p].passwd[0];
+ salt[1] = parray[p].passwd[1];
+ salt[2] = '\0';
+ if (strcmp(crypt(oldpassword, salt), parray[p].passwd)) {
+ pprintf(p, "Incorrect password, password not changed!\n");
+ return COM_OK;
+ }
+ rfree(parray[p].passwd);
+ parray[p].passwd = NULL;
+ }
+ salt[0] = 'a' + rand() % 26;
+ salt[1] = 'a' + rand() % 26;
+ salt[2] = '\0';
+ parray[p].passwd = strdup(crypt(newpassword, salt));
+ pprintf(p, "Password changed to \"%s\".\n", newpassword);
+ return COM_OK;
+}
+
+PUBLIC int com_uptime(int p, param_list param)
+{
+ unsigned long uptime = time(0) - startuptime;
+ struct rusage ru;
+ int days = (uptime / (60*60*24));
+ int hours = ((uptime % (60*60*24)) / (60*60));
+ int mins = (((uptime % (60*60*24)) % (60*60)) / 60);
+ int secs = (((uptime % (60*60*24)) % (60*60)) % 60);
+
+ pprintf(p, "Server location: %s Server version : %s\n", fics_hostname,VERS_NUM);
+ pprintf(p, "The server has been up since %s.\n", strltime(&startuptime));
+ if ((days==0) && (hours==0) && (mins==0)) {
+ pprintf(p, "(Up for %d second%s)\n",
+ secs, (secs==1) ? "" : "s");
+ } else if ((days==0) && (hours==0)) {
+ pprintf(p, "(Up for %d minute%s and %d second%s)\n",
+ mins, (mins==1) ? "" : "s",
+ secs, (secs==1) ? "" : "s");
+ } else if (days==0) {
+ pprintf(p, "(Up for %d hour%s, %d minute%s and %d second%s)\n",
+ hours, (hours==1) ? "" : "s",
+ mins, (mins==1) ? "" : "s",
+ secs, (secs==1) ? "" : "s");
+ } else {
+ pprintf(p, "(Up for %d day%s, %d hour%s, %d minute%s and %d second%s)\n",
+ days, (days==1) ? "" : "s",
+ hours, (hours==1) ? "" : "s",
+ mins, (mins==1) ? "" : "s",
+ secs, (secs==1) ? "" : "s");
+ }
+ pprintf(p, "\nAllocs: %u Frees: %u Allocs In Use: %u\n",
+ malloc_count, free_count, malloc_count - free_count);
+ if (parray[p].adminLevel >= ADMIN_ADMIN) {
+ pprintf(p, "\nplayer size:%d, game size:%d, con size:%d, g_num:%d\n",
+ sizeof(player), sizeof(game), net_consize(), g_num);
+ getrusage(RUSAGE_SELF, &ru);
+ pprintf(p, "pagesize = %d, maxrss = %d, total = %d\n", getpagesize(), ru.ru_maxrss, getpagesize() * ru.ru_maxrss);
+ }
+ pprintf(p, "\nPlayer limit: %d\n", max_connections-10);
+ pprintf(p, "\nThere are currently %d players, with a high of %d since last restart.\n", player_count(1), player_high);
+ pprintf(p, "There are currently %d games, with a high of %d since last restart.\n", game_count(), game_high);
+ pprintf(p, "\nCompiled on %s\n", COMP_DATE);
+ return COM_OK;
+}
+
+PUBLIC int com_date(int p, param_list param)
+{
+ int t = time(0);
+ pprintf(p, "Local time - %s\n", strltime(&t));
+ pprintf(p, "Greenwich time - %s\n", strgtime(&t));
+ return COM_OK;
+}
+
+char *inout_string[] = {
+ "login", "logout"
+};
+
+PRIVATE int plogins(p, fname)
+int p;
+char *fname;
+{
+ FILE *fp;
+ int inout, thetime, registered;
+ char loginName[MAX_LOGIN_NAME + 1];
+ char ipstr[20];
+
+ fp = fopen(fname, "r");
+ if (!fp) {
+ pprintf(p, "Sorry, no login information available.\n");
+ return COM_OK;
+ }
+ while (!feof(fp)) {
+ if (fscanf(fp, "%d %s %d %d %s\n", &inout, loginName, &thetime,
+ &registered, ipstr) != 5) {
+ fprintf(stderr, "FICS: Error in login info format. %s\n", fname);
+ fclose(fp);
+ return COM_OK;
+ }
+ pprintf(p, "%s: %-17s %-6s", strltime(&thetime), loginName,
+ inout_string[inout]);
+ if (parray[p].adminLevel > 0) {
+ pprintf(p, " from %s\n", ipstr);
+ } else
+ pprintf(p, "\n");
+ }
+ fclose(fp);
+ return COM_OK;
+}
+
+PUBLIC int com_llogons(int p, param_list param)
+{
+ char fname[MAX_FILENAME_SIZE];
+
+ sprintf(fname, "%s/%s", stats_dir, STATS_LOGONS);
+ return plogins(p, fname);
+}
+
+PUBLIC int com_logons(int p, param_list param)
+{
+ char fname[MAX_FILENAME_SIZE];
+
+ if (param[0].type == TYPE_WORD) {
+ sprintf(fname, "%s/player_data/%c/%s.%s", stats_dir, param[0].val.word[0], param[0].val.word, STATS_LOGONS);
+ } else {
+ sprintf(fname, "%s/player_data/%c/%s.%s", stats_dir, parray[p].login[0], parray[p].login, STATS_LOGONS);
+ }
+ return plogins(p, fname);
+}
+
+#define WHO_OPEN 0x01
+#define WHO_CLOSED 0x02
+#define WHO_RATED 0x04
+#define WHO_UNRATED 0x08
+#define WHO_FREE 0x10
+#define WHO_PLAYING 0x20
+#define WHO_REGISTERED 0x40
+#define WHO_UNREGISTERED 0x80
+#define WHO_BUGTEAM 0x100
+
+#define WHO_ALL 0xff
+
+void AddPlayerLists (int p1, char *ptmp)
+{
+ if ((parray[p1].adminLevel >= 10) && (parray[p1].i_admin))
+ strcat(ptmp, "(*)");
+ if (in_list(p1, L_COMPUTER, parray[p1].name))
+ strcat(ptmp, "(C)");
+ if (in_list(p1, L_FM, parray[p1].name))
+ strcat(ptmp, "(FM)");
+ if (in_list(p1, L_IM, parray[p1].name))
+ strcat(ptmp, "(IM)");
+ if (in_list(p1, L_GM, parray[p1].name))
+ strcat(ptmp, "(GM)");
+ if (in_list(p1, L_TD, parray[p1].name))
+ strcat(ptmp, "(TD)");
+ if (in_list(p1, L_TEAMS, parray[p1].name))
+ strcat(ptmp, "(T)");
+ if (in_list(p1, L_BLIND, parray[p1].name))
+ strcat(ptmp, "(B)");
+}
+
+PRIVATE void who_terse(int p, int num, int *plist, int type)
+{
+ char ptmp[80 + 20]; /* for highlight */
+ multicol *m = multicol_start(PARRAY_SIZE);
+ int i;
+ int p1;
+ int rat;
+
+ /* altered DAV 3/15/95 */
+
+ for (i = 0; i < num; i++) {
+ p1 = plist[i];
+
+ if (type == blitz_rat)
+ rat = parray[p1].b_stats.rating;
+ else if (type == wild_rat)
+ rat = parray[p1].w_stats.rating;
+ else if (type == std_rat)
+ rat = parray[p1].s_stats.rating;
+ else if (type == light_rat)
+ rat = parray[p1].l_stats.rating;
+
+ if (type == none) {
+ sprintf(ptmp, " ");
+ } else {
+ sprintf(ptmp, "%-4s", ratstrii(rat, parray[p1].registered));
+ if (parray[p1].simul_info.numBoards) {
+ strcat(ptmp, "~");
+ } else if ((parray[p1].game >= 0) && (garray[parray[p1].game].status == GAME_EXAMINE)) {
+ strcat(ptmp, "#");
+ } else if (parray[p1].game >= 0) {
+ strcat(ptmp, "^");
+ } else if (!parray[p1].open) {
+ strcat(ptmp, ":");
+ } else if (player_idle(p1) > 300) {
+ strcat(ptmp, ".");
+ } else {
+ strcat(ptmp, " ");
+ }
+ }
+
+ if (p == p1) {
+ psprintf_highlight(p, ptmp + strlen(ptmp), "%s", parray[p1].name);
+ } else {
+ strcat(ptmp, parray[p1].name);
+ }
+
+ AddPlayerLists(p1, ptmp);
+ multicol_store(m, ptmp);
+ }
+ multicol_pprint(m, p, 80, 2);
+ multicol_end(m);
+ pprintf(p, "\n %d players displayed (of %d). (*) indicates system administrator.\n", num, player_count(1));
+}
+
+PRIVATE void who_verbose(p, num, plist)
+int p;
+int num;
+int plist[];
+{
+ int i, p1;
+ char playerLine[255], tmp[255]; /* +8 for highlight */
+ char p1WithAttrs[255];
+
+ pprintf(p,
+ " +---------------------------------------------------------------+\n"
+ );
+ pprintf(p,
+ " | User Standard Blitz On for Idle |\n"
+ );
+ pprintf(p,
+ " +---------------------------------------------------------------+\n"
+ );
+
+ for (i = 0; i < num; i++) {
+ p1 = plist[i];
+
+ strcpy(playerLine, " |");
+
+ if (parray[p1].game >= 0)
+ sprintf(tmp, "%3d", parray[p1].game + 1);
+ else
+ sprintf(tmp, " ");
+ strcat(playerLine, tmp);
+
+ if (!parray[p1].open)
+ sprintf(tmp, "X");
+ else
+ sprintf(tmp, " ");
+ strcat(playerLine, tmp);
+
+ if (parray[p1].registered)
+ if (parray[p1].rated) {
+ sprintf(tmp, " ");
+ } else {
+ sprintf(tmp, "u");
+ }
+ else
+ sprintf(tmp, "U");
+ strcat(playerLine, tmp);
+
+ /* Modified by hersco to include lists in 'who v.' */
+ strcpy (p1WithAttrs, parray[p1].name);
+ AddPlayerLists(p1, p1WithAttrs);
+ p1WithAttrs[17] = '\0';
+
+ /* Modified by DAV 3/15/95 */
+ if (p == p1) {
+ strcpy(tmp, " ");
+ psprintf_highlight(p, tmp + strlen(tmp), "%-17s", p1WithAttrs);
+ } else {
+ sprintf(tmp, " %-17s", p1WithAttrs);
+ }
+ strcat(playerLine, tmp);
+
+ sprintf(tmp, " %4s %-4s %5s ",
+ ratstrii(parray[p1].s_stats.rating, parray[p1].registered),
+ ratstrii(parray[p1].b_stats.rating, parray[p1].registered),
+ hms(player_ontime(p1), 0, 0, 0));
+ strcat(playerLine, tmp);
+
+ if (player_idle(p1) >= 60) {
+ sprintf(tmp, "%5s |\n", hms(player_idle(p1), 0, 0, 0));
+ } else {
+ sprintf(tmp, " |\n");
+ }
+ strcat(playerLine, tmp);
+ pprintf(p, "%s", playerLine);
+ }
+
+ pprintf(p,
+ " | |\n"
+ );
+ pprintf(p,
+ " | %3d Players Displayed |\n",
+ num
+ );
+ pprintf(p,
+ " +---------------------------------------------------------------+\n"
+ );
+}
+
+PRIVATE void who_winloss(p, num, plist)
+int p;
+int num;
+int plist[];
+{
+ int i, p1;
+ char playerLine[255], tmp[255]; /* for highlight */
+ char p1WithAttrs[255];
+
+ pprintf(p,
+ "Name Stand win loss draw Blitz win loss draw idle\n"
+ );
+ pprintf(p,
+ "---------------- ----- ------------- ----- ------------- ----\n"
+ );
+
+ for (i = 0; i < num; i++) {
+ playerLine[0] = '\0';
+ p1 = plist[i];
+
+ /* Modified by hersco to include lists in 'who n.' */
+ strcpy (p1WithAttrs, parray[p1].name);
+ AddPlayerLists(p1, p1WithAttrs);
+ p1WithAttrs[17] = '\0';
+
+ if (p1 == p) {
+ psprintf_highlight(p, playerLine, "%-17s", p1WithAttrs);
+ } else {
+ sprintf(playerLine, "%-17s", p1WithAttrs);
+ }
+ sprintf(tmp, " %4s %4d %4d %4d ",
+ ratstrii(parray[p1].s_stats.rating, parray[p1].registered),
+ (int) parray[p1].s_stats.win,
+ (int) parray[p1].s_stats.los,
+ (int) parray[p1].s_stats.dra);
+ strcat(playerLine, tmp);
+
+ sprintf(tmp, "%4s %4d %4d %4d ",
+ ratstrii(parray[p1].b_stats.rating, parray[p1].registered),
+ (int) parray[p1].b_stats.win,
+ (int) parray[p1].b_stats.los,
+ (int) parray[p1].b_stats.dra);
+ strcat(playerLine, tmp);
+
+ if (player_idle(p1) >= 60) {
+ sprintf(tmp, "%5s\n", hms(player_idle(p1), 0, 0, 0));
+ } else {
+ sprintf(tmp, " \n");
+ }
+ strcat(playerLine, tmp);
+
+ pprintf(p, "%s", playerLine);
+ }
+ pprintf(p, " %3d Players Displayed.\n", num);
+}
+
+PRIVATE int who_ok(int p, unsigned int sel_bits)
+{
+ int p2;
+ if (parray[p].status != PLAYER_PROMPT)
+ return 0;
+ if (sel_bits == WHO_ALL)
+ return 1;
+ if (sel_bits & WHO_OPEN)
+ if (!parray[p].open)
+ return 0;
+ if (sel_bits & WHO_CLOSED)
+ if (parray[p].open)
+ return 0;
+ if (sel_bits & WHO_RATED)
+ if (!parray[p].rated)
+ return 0;
+ if (sel_bits & WHO_UNRATED)
+ if (parray[p].rated)
+ return 0;
+ if (sel_bits & WHO_FREE)
+ if (parray[p].game >= 0)
+ return 0;
+ if (sel_bits & WHO_PLAYING)
+ if (parray[p].game < 0)
+ return 0;
+ if (sel_bits & WHO_REGISTERED)
+ if (!parray[p].registered)
+ return 0;
+ if (sel_bits & WHO_UNREGISTERED)
+ if (parray[p].registered)
+ return 0;
+ if (sel_bits & WHO_BUGTEAM) {
+ p2 = parray[p].partner;
+ if (p2 < 0 || parray[p2].partner != p)
+ return 0;
+ }
+ return 1;
+}
+
+
+PRIVATE int blitz_cmp(const void *pp1, const void *pp2)
+{
+ register int p1 = *(int *) pp1;
+ register int p2 = *(int *) pp2;
+ if (parray[p1].status != PLAYER_PROMPT) {
+ if (parray[p2].status != PLAYER_PROMPT)
+ return 0;
+ else
+ return -1;
+ }
+ if (parray[p2].status != PLAYER_PROMPT)
+ return 1;
+ if (parray[p1].b_stats.rating > parray[p2].b_stats.rating)
+ return -1;
+ if (parray[p1].b_stats.rating < parray[p2].b_stats.rating)
+ return 1;
+ if (parray[p1].registered > parray[p2].registered)
+ return -1;
+ if (parray[p1].registered < parray[p2].registered)
+ return 1;
+ return strcmp(parray[p1].login, parray[p2].login);
+}
+
+PRIVATE int light_cmp(const void *pp1, const void *pp2)
+{
+ register int p1 = *(int *) pp1;
+ register int p2 = *(int *) pp2;
+ if (parray[p1].status != PLAYER_PROMPT) {
+ if (parray[p2].status != PLAYER_PROMPT)
+ return 0;
+ else
+ return -1;
+ }
+ if (parray[p2].status != PLAYER_PROMPT)
+ return 1;
+ if (parray[p1].l_stats.rating > parray[p2].l_stats.rating)
+ return -1;
+ if (parray[p1].l_stats.rating < parray[p2].l_stats.rating)
+ return 1;
+ if (parray[p1].registered > parray[p2].registered)
+ return -1;
+ if (parray[p1].registered < parray[p2].registered)
+ return 1;
+ return strcmp(parray[p1].login, parray[p2].login);
+}
+
+PRIVATE int stand_cmp(const void *pp1, const void *pp2)
+{
+ register int p1 = *(int *) pp1;
+ register int p2 = *(int *) pp2;
+ if (parray[p1].status != PLAYER_PROMPT) {
+ if (parray[p2].status != PLAYER_PROMPT)
+ return 0;
+ else
+ return -1;
+ }
+ if (parray[p2].status != PLAYER_PROMPT)
+ return 1;
+ if (parray[p1].s_stats.rating > parray[p2].s_stats.rating)
+ return -1;
+ if (parray[p1].s_stats.rating < parray[p2].s_stats.rating)
+ return 1;
+ if (parray[p1].registered > parray[p2].registered)
+ return -1;
+ if (parray[p1].registered < parray[p2].registered)
+ return 1;
+ return strcmp(parray[p1].login, parray[p2].login);
+}
+
+PRIVATE int wild_cmp(const void *pp1, const void *pp2)
+{
+ register int p1 = *(int *) pp1;
+ register int p2 = *(int *) pp2;
+ if (parray[p1].status != PLAYER_PROMPT) {
+ if (parray[p2].status != PLAYER_PROMPT)
+ return 0;
+ else
+ return -1;
+ }
+ if (parray[p2].status != PLAYER_PROMPT)
+ return 1;
+ if (parray[p1].w_stats.rating > parray[p2].w_stats.rating)
+ return -1;
+ if (parray[p1].w_stats.rating < parray[p2].w_stats.rating)
+ return 1;
+ if (parray[p1].registered > parray[p2].registered)
+ return -1;
+ if (parray[p1].registered < parray[p2].registered)
+ return 1;
+ return strcmp(parray[p1].login, parray[p2].login);
+}
+
+PRIVATE int alpha_cmp(const void *pp1, const void *pp2)
+{
+ register int p1 = *(int *) pp1;
+ register int p2 = *(int *) pp2;
+ if (parray[p1].status != PLAYER_PROMPT) {
+ if (parray[p2].status != PLAYER_PROMPT)
+ return 0;
+ else
+ return -1;
+ }
+ if (parray[p2].status != PLAYER_PROMPT)
+ return 1;
+ return strcmp(parray[p1].login, parray[p2].login);
+}
+
+PUBLIC void sort_players(int players[PARRAY_SIZE],
+ int ((*cmp_func) (const void *, const void *)))
+{
+ int i;
+
+ for (i = 0; i < p_num; i++) {
+ players[i] = i;
+ }
+ qsort(players, p_num, sizeof(int), cmp_func);
+}
+
+/* This is the of the most compliclicated commands in terms of parameters */
+PUBLIC int com_who(int p, param_list param)
+{
+ int style = 0;
+ float stop_perc = 1.0;
+ float start_perc = 0;
+ unsigned int sel_bits = WHO_ALL;
+ int sortlist[PARRAY_SIZE], plist[PARRAY_SIZE];
+ int ((*cmp_func) (const void *, const void *)) = blitz_cmp;
+ int startpoint;
+ int stoppoint;
+ int i, len;
+ int tmpI, tmpJ;
+ char c;
+ int p1, count, num_who;
+ int sort_type = blitz_rat;
+
+ if (param[0].type != TYPE_NULL) {
+ len = strlen(param[0].val.string);
+ for (i = 0; i < len; i++) {
+ c = param[0].val.string[i];
+ if (isdigit(c)) {
+ if (i == 0 || !isdigit(param[0].val.string[i - 1])) {
+ tmpI = c - '0';
+ if (tmpI == 1) {
+ start_perc = 0.0;
+ stop_perc = 0.333333;
+ } else if (tmpI == 2) {
+ start_perc = 0.333333;
+ stop_perc = 0.6666667;
+ } else if (tmpI == 3) {
+ start_perc = 0.6666667;
+ stop_perc = 1.0;
+ } else if ((i == len - 1) || (!isdigit(param[0].val.string[i + 1])))
+ return COM_BADPARAMETERS;
+ } else {
+ tmpI = c - '0';
+ tmpJ = param[0].val.string[i - 1] - '0';
+ if (tmpI == 0)
+ return COM_BADPARAMETERS;
+ if (tmpJ > tmpI)
+ return COM_BADPARAMETERS;
+ start_perc = ((float) tmpJ - 1.0) / (float) tmpI;
+ stop_perc = ((float) tmpJ) / (float) tmpI;
+ }
+ } else {
+ switch (c) {
+ case ' ':
+ case '\n':
+ case '\t':
+ break;
+ case 'o':
+ if (sel_bits == WHO_ALL)
+ sel_bits = WHO_OPEN;
+ else
+ sel_bits |= WHO_OPEN;
+ break;
+ case 'r':
+ if (sel_bits == WHO_ALL)
+ sel_bits = WHO_RATED;
+ else
+ sel_bits |= WHO_RATED;
+ break;
+ case 'f':
+ if (sel_bits == WHO_ALL)
+ sel_bits = WHO_FREE;
+ else
+ sel_bits |= WHO_FREE;
+ break;
+ case 'a':
+ if (sel_bits == WHO_ALL)
+ sel_bits = WHO_FREE | WHO_OPEN;
+ else
+ sel_bits |= (WHO_FREE | WHO_OPEN);
+ break;
+ case 'R':
+ if (sel_bits == WHO_ALL)
+ sel_bits = WHO_REGISTERED;
+ else
+ sel_bits |= WHO_REGISTERED;
+ break;
+ case 'l': /* Sort order */
+ cmp_func = alpha_cmp;
+ sort_type = none;
+ break;
+ case 'A': /* Sort order */
+ cmp_func = alpha_cmp;
+ break;
+ case 'w': /* Sort order */
+ cmp_func = wild_cmp;
+ sort_type = wild_rat;
+ break;
+ case 's': /* Sort order */
+ cmp_func = stand_cmp;
+ sort_type = std_rat;
+ break;
+ case 'b': /* Sort order */
+ cmp_func = blitz_cmp;
+ sort_type = blitz_rat;
+ break;
+ case 'L': /* Sort order */
+ cmp_func = light_cmp;
+ sort_type = light_rat;
+ break;
+ case 't': /* format */
+ style = 0;
+ break;
+ case 'v': /* format */
+ style = 1;
+ break;
+ case 'n': /* format */
+ style = 2;
+ break;
+ case 'U':
+ if (sel_bits == WHO_ALL)
+ sel_bits = WHO_UNREGISTERED;
+ else
+ sel_bits |= WHO_UNREGISTERED;
+ break;
+ case 'B':
+ if (sel_bits == WHO_ALL)
+ sel_bits = WHO_BUGTEAM;
+ else
+ sel_bits |= WHO_BUGTEAM;
+ break;
+ default:
+ return COM_BADPARAMETERS;
+ break;
+ }
+ }
+ }
+ }
+ sort_players(sortlist, cmp_func);
+ count = 0;
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (!who_ok(sortlist[p1], sel_bits))
+ continue;
+ count++;
+ }
+ startpoint = floor((float) count * start_perc);
+ stoppoint = ceil((float) count * stop_perc) - 1;
+ num_who = 0;
+ count = 0;
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (!who_ok(sortlist[p1], sel_bits))
+ continue;
+ if ((count >= startpoint) && (count <= stoppoint)) {
+ plist[num_who++] = sortlist[p1];
+ }
+ count++;
+ }
+
+ if (num_who == 0) {
+ pprintf(p, "No logged in players match the flags in your who request.\n");
+ return COM_OK;
+ }
+
+ switch (style) {
+ case 0: /* terse */
+ who_terse(p, num_who, plist, sort_type);
+ break;
+ case 1: /* verbose */
+ who_verbose(p, num_who, plist);
+ break;
+ case 2: /* win-loss */
+ who_winloss(p, num_who, plist);
+ break;
+ default:
+ return COM_BADPARAMETERS;
+ break;
+ }
+
+ return COM_OK;
+}
+
+PUBLIC int com_refresh(int p, param_list param)
+{
+ int g, p1;
+
+ if (param[0].type == TYPE_NULL) {
+ if (parray[p].game >= 0) {
+ send_board_to(parray[p].game, p);
+ } else { /* Do observing in here */
+ if (parray[p].num_observe) {
+ for (g = 0; g < parray[p].num_observe; g++) {
+ send_board_to(parray[p].observe_list[g], p);
+ }
+ } else {
+ pprintf(p, "You are neither playing, observing nor examining a game.\n");
+ return COM_OK;
+ }
+ }
+ } else {
+ g = GameNumFromParam (p, &p1, &param[0]);
+ if (g < 0)
+ return COM_OK;
+ if ((g >= g_num) || ((garray[g].status != GAME_ACTIVE)
+ && (garray[g].status != GAME_EXAMINE))) {
+ pprintf(p, "No such game.\n");
+ } else if (garray[g].private && parray[p].adminLevel==ADMIN_USER) {
+ pprintf (p, "Sorry, game %d is a private game.\n", g+1);
+ } else {
+ if (garray[g].private)
+ pprintf(p, "Refreshing PRIVATE game %d\n", g+1);
+ send_board_to(g, p);
+ }
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_prefresh(int p, param_list param)
+{
+ int retval, part = parray[p].partner;
+
+ if (part < 0) {
+ pprintf(p, "You do not have a partner.\n");
+ return COM_OK;
+ }
+ retval = pcommand (p, "refresh %s", parray[part].name);
+ if (retval == COM_OK)
+ return COM_OK_NOPROMPT;
+ else
+ return retval;
+}
+
+PUBLIC int com_open(int p, param_list param)
+{
+ int retval;
+ ASSERT(param[0].type == TYPE_NULL);
+ if ((retval = pcommand(p, "set open")) != COM_OK)
+ return retval;
+ else
+ return COM_OK_NOPROMPT;
+}
+
+PUBLIC int com_simopen(int p, param_list param)
+{
+ int retval;
+ ASSERT(param[0].type == TYPE_NULL);
+ if ((retval = pcommand(p, "set simopen")) != COM_OK)
+ return retval;
+ else
+ return COM_OK_NOPROMPT;
+}
+
+PUBLIC int com_bell(int p, param_list param)
+{
+ int retval;
+ ASSERT(param[0].type == TYPE_NULL);
+ if ((retval = pcommand(p, "set bell")) != COM_OK)
+ return retval;
+ else
+ return COM_OK_NOPROMPT;
+}
+
+PUBLIC int com_flip(int p, param_list param)
+{
+ int retval;
+ ASSERT(param[0].type == TYPE_NULL);
+ if ((retval = pcommand(p, "set flip")) != COM_OK)
+ return retval;
+ else
+ return COM_OK_NOPROMPT;
+}
+
+PUBLIC int com_highlight(int p, param_list param)
+{
+ pprintf(p, "Obsolete command. Please do set highlight <0-15>.\n");
+ return COM_OK;
+}
+
+PUBLIC int com_style(int p, param_list param)
+{
+ int retval;
+ ASSERT(param[0].type == TYPE_INT);
+ if ((retval = pcommand(p, "set style %d", param[0].val.integer)) != COM_OK)
+ return retval;
+ else
+ return COM_OK_NOPROMPT;
+}
+
+PUBLIC int com_promote(int p, param_list param)
+{
+ int retval;
+ ASSERT(param[0].type == TYPE_WORD);
+ if ((retval = pcommand(p, "set promote %s", param[0].val.word)) != COM_OK)
+ return retval;
+ else
+ return COM_OK_NOPROMPT;
+}
+
+PUBLIC int com_alias(int p, param_list param)
+{
+ int al;
+
+ if (param[0].type == TYPE_NULL) {
+ for (al = 0; al < parray[p].numAlias; al++) {
+ pprintf(p, "%s -> %s\n", parray[p].alias_list[al].comm_name,
+ parray[p].alias_list[al].alias);
+ }
+ return COM_OK;
+ }
+ al = alias_lookup(param[0].val.word, parray[p].alias_list, parray[p].numAlias);
+ if (param[1].type == TYPE_NULL) {
+ if (al < 0) {
+ pprintf(p, "You have no alias named '%s'.\n", param[0].val.word);
+ } else {
+ pprintf(p, "%s -> %s\n", parray[p].alias_list[al].comm_name,
+ parray[p].alias_list[al].alias);
+ }
+ } else {
+ if (al < 0) {
+ if (parray[p].numAlias >= MAX_ALIASES - 1) {
+ pprintf(p, "You have your maximum of %d aliases.\n", MAX_ALIASES - 1);
+ } else {
+
+ if (!strcmp(param[0].val.string, "quit")) { /* making sure they
+ can't alias quit */
+ pprintf(p, "You can't alias this command.\n");
+ } else if (!strcmp(param[0].val.string, "unalias")) { /* making sure they
+ can't alias unalias
+ :) */
+ pprintf(p, "You can't alias this command.\n");
+ } else {
+ parray[p].alias_list[parray[p].numAlias].comm_name =
+ strdup(param[0].val.word);
+ parray[p].alias_list[parray[p].numAlias].alias =
+ strdup(param[1].val.string);
+ parray[p].numAlias++;
+ pprintf(p, "Alias set.\n");
+
+ }
+ }
+ } else {
+ rfree(parray[p].alias_list[al].alias);
+ parray[p].alias_list[al].alias = strdup(param[1].val.string);
+ pprintf(p, "Alias replaced.\n");
+ }
+ parray[p].alias_list[parray[p].numAlias].comm_name = NULL;
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_unalias(int p, param_list param)
+{
+ int al;
+ int i;
+
+ ASSERT(param[0].type == TYPE_WORD);
+ al = alias_lookup(param[0].val.word, parray[p].alias_list, parray[p].numAlias);
+ if (al < 0) {
+ pprintf(p, "You have no alias named '%s'.\n", param[0].val.word);
+ } else {
+ rfree(parray[p].alias_list[al].comm_name);
+ rfree(parray[p].alias_list[al].alias);
+ for (i = al; i < parray[p].numAlias; i++) {
+ parray[p].alias_list[i].comm_name = parray[p].alias_list[i + 1].comm_name;
+ parray[p].alias_list[i].alias = parray[p].alias_list[i + 1].alias;
+ }
+ parray[p].numAlias--;
+ parray[p].alias_list[parray[p].numAlias].comm_name = NULL;
+ pprintf(p, "Alias removed.\n");
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_servers(int p, param_list param)
+{
+/*
+ int i;
+
+ ASSERT(param[0].type == TYPE_NULL);
+ if (numServers == 0) {
+ */ pprintf(p, "There are no other servers known to this server.\n");
+ return COM_OK;
+}
+ /* pprintf(p, "There are %d known servers.\n", numServers); pprintf(p, "(Not
+ all of these may be active)\n"); pprintf(p, "%-30s%-7s\n", "HOST",
+ "PORT"); for (i = 0; i < numServers; i++) pprintf(p, "%-30s%-7d\n",
+ serverNames[i], serverPorts[i]); return COM_OK; } */
+
+PUBLIC int com_index(int p, param_list param)
+{
+ int i;
+ char index_default[] = "_index";
+ char *iwant, *filenames[100]; /* enough for all index filenames */
+
+ if (param[0].type == TYPE_NULL) {
+ iwant = index_default;
+ } else {
+ iwant = param[0].val.word;
+ if (!safestring(iwant)) {
+ pprintf(p, "Illegal character in category %s.\n", iwant);
+ return COM_OK;
+ }
+ }
+
+ i = search_directory(index_dir, iwant, filenames, 1000);
+ if (i == 0) {
+ pprintf(p, "No index entry for \"%s\".\n", iwant);
+ } else if ((i == 1) || !strcmp(*filenames, iwant)) {
+ if (psend_file(p, index_dir, *filenames)) {
+ /* we should never reach this unless the file was just deleted */
+ pprintf(p, "Index file %s could not be found! ", *filenames);
+ pprintf(p, "Please inform an admin of this. Thank you.\n");
+ }
+ } else {
+ pprintf(p, "Matches:");
+ display_directory(p, filenames, i);
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_help(int p, param_list param)
+{
+ int i, UseLang = parray[p].language;
+ char help_default[] = "_help";
+ char *iwant, *filenames[1000]; /* enough for all helpfile names */
+
+ if (param[0].type == TYPE_NULL) {
+ iwant = help_default;
+ } else {
+ iwant = param[0].val.word;
+ if (!safestring(iwant)) {
+ pprintf(p, "Illegal character in command %s.\n", iwant);
+ return COM_OK;
+ }
+ }
+ i = search_directory(help_dir[UseLang], iwant, filenames, 1000);
+ if (i == 0) {
+ if (UseLang != LANG_DEFAULT) {
+ i += search_directory(help_dir[LANG_DEFAULT], iwant, filenames, 1000);
+ if (i > 0) {
+ pprintf(p, "No help available in %s; using %s instead.\n",
+ Language(UseLang), Language(LANG_DEFAULT));
+ UseLang = LANG_DEFAULT;
+ }
+ }
+ if (i==0) {
+ pprintf(p, "No help available on \"%s\".\n", iwant);
+ return COM_OK;
+ }
+ }
+ if ((i == 1) || !strcmp(*filenames, iwant)) {
+ if (psend_file(p, help_dir[UseLang], *filenames)) {
+ /* we should never reach this unless the file was just deleted */
+ pprintf(p, "Helpfile %s could not be found! ", *filenames);
+ pprintf(p, "Please inform an admin of this. Thank you.\n");
+ }
+ } else {
+ pprintf(p, "Matches:");
+ display_directory(p, filenames, i);
+ pprintf(p, "[Type \"info\" for a list of FICS general information files.]\n");
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_info(int p, param_list param)
+{
+ int n;
+ char *filenames[1000];
+
+ if ((n = search_directory(info_dir, NULL, filenames, 1000)) > 0)
+ display_directory(p, filenames, n);
+ return COM_OK;
+}
+
+PRIVATE int FindAndShowFile(int p, param_list param, char *dir)
+{
+ int i;
+ static char nullify = '\0';
+ char *iwant, *filenames[1000]; /* enough for all helpfile names */
+
+ if (param[0].type == TYPE_NULL) {
+ iwant = &nullify;
+ } else {
+ iwant = param[0].val.word;
+ if (!safestring(iwant)) {
+ pprintf(p, "Illegal character in filename %s.\n", iwant);
+ return COM_OK;
+ }
+ }
+
+ i = search_directory(dir, iwant, filenames, 1000);
+ if (i == 0) {
+ pprintf(p, "No information available on \"%s\".\n", iwant);
+ } else if ((i == 1) || !strcmp(*filenames, iwant)) {
+ if (psend_file(p, dir, *filenames)) {
+ /* we should never reach this unless the file was just deleted */
+ pprintf(p, "File %s could not be found! ", *filenames);
+ pprintf(p, "Please inform an admin of this. Thank you.\n");
+ }
+ } else {
+ if (*iwant)
+ pprintf(p, "Matches:\n");
+ display_directory(p, filenames, i);
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_uscf(int p, param_list param)
+{
+ return FindAndShowFile (p, param, uscf_dir);
+}
+
+PUBLIC int com_adhelp(int p, param_list param)
+{
+ return FindAndShowFile (p, param, adhelp_dir);
+}
+
+PUBLIC int com_mailsource(int p, param_list param)
+{
+ static char nullify = '\0';
+ char *iwant, *buffer[1000];
+ char subj[81], fname[MAX_FILENAME_SIZE];
+ int count;
+
+ if (!parray[p].registered) {
+ pprintf(p, "Only registered people can use the mailsource command.\n");
+ return COM_OK;
+ }
+
+ if (param[0].type == TYPE_NULL) {
+ iwant = &nullify;
+ } else {
+ iwant = param[0].val.word;
+ }
+
+ count = search_directory(source_dir, iwant, buffer, 1000);
+ if (count == 0) {
+ pprintf(p, "Found no source file matching \"%s\".\n", iwant);
+ } else if ((count == 1) || !strcmp(iwant, *buffer)) {
+ sprintf(subj, "FICS source file from server %s: %s",fics_hostname,*buffer);
+ sprintf(fname, "%s/%s",source_dir, *buffer);
+ mail_file_to_user (p, subj, fname);
+ pprintf(p, "Source file %s sent to %s\n", *buffer, parray[p].emailAddress);
+ } else {
+ pprintf(p, "Found %d source files matching that:\n", count);
+ if (*iwant)
+ display_directory(p, buffer, count);
+ else { /* this junk is to get *.c *.h */
+ multicol *m = multicol_start(count);
+ char *s;
+ int i;
+ for (i=0; i < count; i++) {
+ if (((s = buffer[i] + strlen(buffer[i]) - 2) >= buffer[i]) && (!strcmp(s, ".c") || !strcmp(s, ".h")))
+ multicol_store(m, buffer[i]);
+ }
+ multicol_pprint(m, p, 78, 1);
+ multicol_end(m);
+ }
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_mailhelp(int p, param_list param)
+{ /* Sparky */
+ char subj[81], fname[MAX_FILENAME_SIZE];
+ char *iwant, *buffer[1000];
+
+ int lang = parray[p].language;
+ int count;
+ static char nullify = '\0';
+
+ if (!parray[p].registered) {
+ pprintf(p, "Only registered people can use the mailhelp command.\n");
+ return COM_OK;
+ }
+ if (param[0].type == TYPE_NULL)
+ iwant = &nullify;
+ else
+ iwant = param[0].val.word;
+
+ count = search_directory(help_dir[lang], iwant, buffer, 1000);
+ if (count == 0 && lang != LANG_DEFAULT) {
+ count += search_directory(help_dir[LANG_DEFAULT], iwant, buffer, 1000);
+ if (count > 0) {
+ pprintf(p, "No help available in %s; using %s instead.\n",
+ Language(lang), Language(LANG_DEFAULT));
+ lang = LANG_DEFAULT;
+ }
+ }
+ if (count == 0) {
+ pprintf(p, "Found no help file matching \"%s\".\n", iwant);
+ } else if (count == 1 || !strcmp(*buffer, iwant)) {
+ sprintf(subj, "FICS help file from server %s: %s", fics_hostname, *buffer);
+ sprintf(fname, "%s/%s",help_dir[lang], *buffer);
+ mail_file_to_user (p, subj, fname);
+ pprintf(p, "Help file %s sent to %s\n", *buffer, parray[p].emailAddress);
+ } else {
+ pprintf(p, "Found %d helpfiles matching that:\n", count);
+ display_directory(p, buffer, count);
+ }
+
+ return COM_OK;
+}
+
+PUBLIC int com_handles(int p, param_list param)
+{
+ char *buffer[1000];
+ char pdir[MAX_FILENAME_SIZE];
+ int count;
+
+ sprintf(pdir, "%s/%c", player_dir, param[0].val.word[0]);
+ count = search_directory(pdir, param[0].val.word, buffer, 1000);
+ pprintf(p, "Found %d names.\n", count);
+ if (count > 0)
+ display_directory(p, buffer, count);
+ return COM_OK;
+}
+
+PUBLIC int com_getpi(int p, param_list param)
+{
+ int p1;
+
+ if (!in_list(p, L_TD, parray[p].name)) {
+ pprintf(p, "Only TD programs are allowed to use this command.\n");
+ return COM_OK;
+ }
+ if (((p1 = player_find_bylogin(param[0].val.word)) < 0) || (parray[p1].registered == 0)) {
+ /* Darkside suggested not to return anything */
+ return COM_OK;
+ }
+ if (!parray[p1].registered) {
+ pprintf(p, "*getpi %s -1 -1 -1*\n", parray[p1].name);
+ } else {
+ pprintf(p, "*getpi %s %d %d %d*\n", parray[p1].name,
+ parray[p1].w_stats.rating,
+ parray[p1].b_stats.rating,
+ parray[p1].s_stats.rating);
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_getps(int p, param_list param)
+{
+ int p1;
+
+ if ((((p1 = player_find_bylogin(param[0].val.word)) < 0) || (parray[p1].registered == 0)) || (parray[p1].game < 0)) {
+
+ pprintf(p, "*status %s 1*\n", param[0].val.word);
+ return COM_OK;
+ }
+ pprintf(p, "*status %s 0 %s*\n", parray[p1].name, parray[(parray[p1].opponent)].name);
+ return COM_OK;
+}
+PUBLIC int com_limits(int p, param_list param)
+{
+ pprintf(p, "\nCurrent hardcoded limits:\n");
+ pprintf(p, " Max number of players: %d\n", MAX_PLAYER);
+ pprintf(p, " Max number of channels and max capacity: %d\n", MAX_CHANNELS);
+ pprintf(p, " Max number of channels one can be in: %d\n", MAX_INCHANNELS);
+ pprintf(p, " Max number of people on the notify list: %d\n", MAX_NOTIFY);
+ pprintf(p, " Max number of aliases: %d\n", MAX_ALIASES - 1);
+ pprintf(p, " Max number of games you can observe at a time: %d\n", MAX_OBSERVE);
+ pprintf(p, " Max number of requests pending: %d\n", MAX_PENDING);
+ pprintf(p, " Max number of people on the censor list: %d\n", MAX_CENSOR);
+ pprintf(p, " Max number of people in a simul game: %d\n", MAX_SIMUL);
+ pprintf(p, " Max number of messages one can receive: %d\n", MAX_MESSAGES);
+ pprintf(p, " Min number of games to be active: %d\n", PROVISIONAL);
+ if ((parray[p].adminLevel < ADMIN_ADMIN) && (!titled_player(p,parray[p].login))) {
+ pprintf(p, " Size of journal (entries): %d\n", MAX_JOURNAL);
+ } else {
+ pprintf(p, " Size of journal (entries): 26\n");
+ }
+ pprintf(p, "\nAdmin settable limits:\n");
+ pprintf(p, " Shout quota gives two shouts per %d seconds.\n", quota_time);
+ return COM_OK;
+}
diff --git a/FICS/comproc.c.orig b/FICS/comproc.c.orig
new file mode 100644
index 0000000..2ec74e3
--- /dev/null
+++ b/FICS/comproc.c.orig
@@ -0,0 +1,1625 @@
+/* comproc.c
+ *
+ */
+
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name email yy/mm/dd Change
+ Richard Nash 93/10/22 Created
+ foxbat 95/03/11 added filters in cmatch.
+*/
+
+#include "stdinclude.h"
+
+#include "common.h"
+#include "talkproc.h"
+#include "comproc.h"
+#include "command.h"
+#include "utils.h"
+#include "ficsmain.h"
+#include "config.h"
+#include "playerdb.h"
+#include "network.h"
+#include "rmalloc.h"
+#include "channel.h"
+#include "variable.h"
+#include "gamedb.h"
+#include "gameproc.h"
+#include "board.h"
+/* #include "hostinfo.h" */
+#include "multicol.h"
+#include "ratings.h"
+#include "formula.h"
+#include "lists.h"
+#include "eco.h"
+#include "network.h"
+#include <string.h>
+
+#include <sys/resource.h>
+
+/* grimm */
+#if defined(SGI)
+#else
+/* int system(char *arg); */
+#endif
+
+const none = 0;
+const blitz_rat = 1;
+const std_rat = 2;
+const wild_rat = 3;
+
+
+PUBLIC int com_rating_recalc(int p, param_list param)
+{
+ ASSERT(parray[p].adminLevel >= ADMIN_ADMIN);
+ rating_recalc();
+ return COM_OK;
+}
+
+PUBLIC int com_more(int p, param_list param)
+{
+/* in_push(IN_HELP); */
+ pmore_file(p);
+ return COM_OK;
+}
+
+PUBLIC int num_news = -1;
+
+PUBLIC int com_news(int p, param_list param)
+{
+ FILE *fp;
+ char filename[MAX_FILENAME_SIZE];
+ char junk[MAX_LINE_SIZE];
+ char *junkp;
+ int crtime, found = 0;
+ char count[10];
+
+ if (num_news == -1) {
+ sprintf(filename, "%s/news.index", news_dir);
+ fp = fopen(filename, "r");
+ if (!fp) {
+ fprintf(stderr, "Cant find news index.\n");
+ return COM_OK;
+ }
+ num_news = count_lines(fp);
+ fclose(fp); /* It's customary to close files after use! */
+ }
+
+ if (param[0].type == 0) {
+ /* no params - then just display index over news */
+ sprintf(filename, "%s/news.index", news_dir);
+ fp = fopen(filename, "r");
+ if (!fp) {
+ fprintf(stderr, "Can't find news index.\n");
+ return COM_OK;
+ }
+ pprintf(p,"Index of recent news items:\n");
+ while (!feof(fp)) {
+ junkp = junk;
+ fgets(junk, MAX_LINE_SIZE, fp);
+ if (feof(fp))
+ break;
+ if (strlen(junk) > 1) {
+ sscanf(junkp, "%d %s", &crtime, count);
+ if (atoi(count) > num_news - 10) {
+ junkp = nextword(junkp);
+ junkp = nextword(junkp);
+ pprintf(p, "%3s (%s) %s", count, strltime(&crtime), junkp);
+ }
+ }
+ }
+ fclose(fp);
+ } else if ((param[0].type == TYPE_WORD) && !strcmp(param[0].val.word, "all")) {
+ /* param all - displays all news items */
+ sprintf(filename, "%s/news.index", news_dir);
+ fp = fopen(filename, "r");
+ if (!fp) {
+ fprintf(stderr, "Can't find news index.\n");
+ return COM_OK;
+ }
+ pprintf(p, "Index of all news items:\n");
+ while (!feof(fp)) {
+ junkp = junk;
+ fgets(junk, MAX_LINE_SIZE, fp);
+ if (feof(fp))
+ break;
+ if (strlen(junk) > 1) {
+ sscanf(junkp, "%d %s", &crtime, count);
+ junkp = nextword(junkp);
+ junkp = nextword(junkp);
+ pprintf(p, "%3s (%s) %s", count, strltime(&crtime), junkp);
+ }
+ }
+ fclose(fp); /* It's customary to close files after use! */
+ } else { /* check if the specific news file exist in index */
+ sprintf(filename, "%s/news.index", news_dir);
+ fp = fopen(filename, "r");
+ if (!fp) {
+ fprintf(stderr, "Can't find news index.\n");
+ return COM_OK;
+ }
+ while (!feof(fp) && !found) {
+ junkp = junk;
+ fgets(junk, MAX_LINE_SIZE, fp);
+ if (feof(fp))
+ break;
+ if (strlen(junk) > 1) {
+ sscanf(junkp, "%d %s", &crtime, count);
+ if (!strcmp(count, param[0].val.word)) {
+ found = 1;
+ junkp = nextword(junkp);
+ junkp = nextword(junkp);
+ pprintf(p, "NEWS %3s (%s) %s\n", count, strltime(&crtime), junkp);
+ }
+ }
+ }
+ fclose(fp);
+ if (!found) {
+ pprintf(p, "Bad index number!\n");
+ return COM_OK;
+ }
+ /* file exists - show it */
+ sprintf(filename, "%s/news.%s", news_dir, param[0].val.word);
+ fp = fopen(filename, "r");
+ if (!fp) {
+ pprintf(p, "No more info.\n");
+ return COM_OK;
+ }
+ fclose(fp);
+ sprintf(filename, "news.%s", param[0].val.word);
+ if (psend_file(p, news_dir, filename) < 0) {
+ pprintf(p, "Internal error - couldn't send news file!\n");
+ }
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_quit(int p, param_list param)
+{
+ if ((parray[p].game >= 0) && (garray[parray[p].game].status == GAME_EXAMINE)) {
+ pcommand(p, "unexamine");
+ }
+
+ if (parray[p].game >= 0) {
+ pprintf(p, "You can't quit while you are playing a game.\nType 'resign' to resign the game, or you can request an abort with 'abort'.\n");
+ return COM_OK;
+ }
+ psend_logoutfile(p, mess_dir, MESS_LOGOUT);
+ return COM_LOGOUT;
+}
+
+PUBLIC int com_set(int p, param_list param)
+{
+ int result;
+ int which;
+ char *val;
+
+ if (param[1].type == TYPE_NULL)
+ val = NULL;
+ else
+ val = param[1].val.string;
+ result = var_set(p, param[0].val.word, val, &which);
+ switch (result) {
+ case VAR_OK:
+ break;
+ case VAR_BADVAL:
+ pprintf(p, "Bad value given for variable %s.\n", param[0].val.word);
+ break;
+ case VAR_NOSUCH:
+ pprintf(p, "No such variable name %s.\n", param[0].val.word);
+ break;
+ case VAR_AMBIGUOUS:
+ pprintf(p, "Ambiguous variable name %s.\n", param[0].val.word);
+ break;
+ }
+ player_save(p);
+ return COM_OK;
+}
+
+PUBLIC int FindPlayer(int p, parameter * param, int *p1, int *connected)
+{
+ if (param->type == TYPE_WORD) {
+ *p1 = player_search(p, param->val.word);
+ if (*p1 == 0)
+ return 0;
+ if (*p1 < 0) { /* player had to be connected and will be
+ removed later */
+ *connected = 0;
+ *p1 = (-*p1) - 1;
+ } else {
+ *connected = 1;
+ *p1 = *p1 - 1;
+ }
+ } else {
+ *p1 = p;
+ *connected = 1;
+ }
+ return 1;
+}
+
+PRIVATE void com_stats_andify(int *numbers, int howmany, char *dest)
+{
+ char tmp[10];
+
+ *dest = '\0';
+ while (howmany--) {
+ sprintf(tmp, "%d", numbers[howmany]);
+ strcat(dest, tmp);
+ if (howmany > 1)
+ sprintf(tmp, ", ");
+ else if (howmany == 1)
+ sprintf(tmp, " and ");
+ else
+ sprintf(tmp, ".\n");
+ strcat(dest, tmp);
+ }
+ return;
+}
+
+PRIVATE void com_stats_rating(char *hdr, statistics * stats, char *dest)
+{
+ char tmp[100];
+
+ sprintf(dest, "%-10s%4s %5.1f %4d %4d %4d %4d",
+ hdr, ratstr(stats->rating), stats->sterr, stats->win, stats->los, stats->dra, stats->num);
+ if (stats->whenbest) {
+ sprintf(tmp, " %d", stats->best);
+ strcat(dest, tmp);
+ strftime(tmp, sizeof(tmp), " (%d-%b-%y)", localtime((time_t *) & stats->whenbest));
+ strcat(dest, tmp);
+ }
+ strcat(dest, "\n");
+ return;
+}
+
+PUBLIC int com_stats(int p, param_list param)
+{
+ int g, i, t;
+ int p1, connected;
+ char line[255], tmp[255];
+ int numbers[MAX_OBSERVE > MAX_SIMUL ? MAX_OBSERVE : MAX_SIMUL];
+ int days, hours, mins, secs, onTime;
+
+ if (!FindPlayer(p, &param[0], &p1, &connected))
+ return COM_OK;
+
+ sprintf(line, "\nStatistics for %-11s ", parray[p1].name);
+ if ((connected) && (parray[p1].status == PLAYER_PROMPT)) {
+ sprintf(tmp, "On for: %s", hms(player_ontime(p1), 0, 0, 0));
+ strcat(line, tmp);
+ sprintf(tmp, " Idle: %s\n", hms(player_idle(p1), 0, 0, 0));
+ } else {
+ if ((t = player_lastdisconnect(p1)))
+ sprintf(tmp, "(Last disconnected %s):\n", strltime(&t));
+ else
+ sprintf(tmp, "(Never connected.)\n");
+ }
+ strcat(line, tmp);
+ pprintf(p, "%s", line);
+ if (parray[p1].simul_info.numBoards) {
+ for (i = 0, t = 0; i < parray[p1].simul_info.numBoards; i++) {
+ if ((numbers[t] = parray[p1].simul_info.boards[i] + 1) != 0)
+ t++;
+ }
+ pprintf(p, "%s is giving a simul: game%s ", parray[p1].name, ((t > 1) ? "s" : ""));
+ com_stats_andify(numbers, t, tmp);
+ pprintf(p, tmp);
+ } else if (parray[p1].game >= 0) {
+ g = parray[p1].game;
+ if (garray[g].status == GAME_EXAMINE) {
+ pprintf(p, "(Examining game %d: %s vs. %s)\n", g + 1,
+ garray[g].white_name, garray[g].black_name);
+ } else {
+ pprintf(p, "(playing game %d: %s vs. %s)\n", g + 1,
+ parray[garray[g].white].name, parray[garray[g].black].name);
+ }
+ }
+ if (parray[p1].num_observe) {
+ for (i = 0, t = 0; i < parray[p1].num_observe; i++) {
+ g = parray[p1].observe_list[i];
+ if ((g != -1) && ((parray[p].adminLevel >= ADMIN_ADMIN) || (garray[g].private == 0)))
+ numbers[t++] = g + 1;
+ }
+ if (t) {
+ pprintf(p, "%s is observing game%s ", parray[p1].name, ((t > 1) ? "s" : ""));
+ com_stats_andify(numbers, t, tmp);
+ pprintf(p, tmp);
+ }
+ }
+ if (parray[p1].busy[0]) {
+ pprintf(p, "(%s %s)\n", parray[p1].name, parray[p1].busy);
+ }
+ if (!parray[p1].registered) {
+ pprintf(p, "%s is NOT a registered player.\n\n", parray[p1].name);
+ } else {
+ pprintf(p, "\n rating RD win loss draw total best\n");
+ com_stats_rating("Blitz", &parray[p1].b_stats, tmp);
+ pprintf(p, tmp);
+ com_stats_rating("Standard", &parray[p1].s_stats, tmp);
+ pprintf(p, tmp);
+ com_stats_rating("Wild", &parray[p1].w_stats, tmp);
+ pprintf(p, tmp);
+ }
+ pprintf(p, "\n");
+ if (parray[p1].adminLevel > 0) {
+ pprintf(p, "Admin Level: ");
+ switch (parray[p1].adminLevel) {
+/*
+ case 0:
+ pprintf(p, "Normal User\n");
+ break;
+ case 5:
+ pprintf(p, "Extra Cool User\n"); vek wants to be 5
+ break;
+
+ Forget it - you can do some admin stuff if your level is > than 0 - DAV
+
+*/
+ case 5:
+ pprintf(p, "Authorized Helper Person\n");
+ break;
+ case 10:
+ pprintf(p, "Administrator\n");
+ break;
+ case 15:
+ pprintf(p, "Help File Librarian/Administrator\n");
+ break;
+ case 20:
+ pprintf(p, "Master Administrator\n");
+ break;
+ case 50:
+ pprintf(p, "Master Help File Librarian/Administrator\n");
+ break;
+ case 60:
+ pprintf(p, "Assistant Super User\n");
+ break;
+ case 100:
+ pprintf(p, "Super User\n");
+ break;
+ default:
+ pprintf(p, "%d\n", parray[p1].adminLevel);
+ break;
+ }
+ }
+ if (parray[p].adminLevel > 0)
+ pprintf(p, "Full Name : %s\n", (parray[p1].fullName ? parray[p1].fullName : "(none)"));
+ if (((p1 == p) && (parray[p1].registered)) || (parray[p].adminLevel > 0))
+ pprintf(p, "Address : %s\n", (parray[p1].emailAddress ? parray[p1].emailAddress : "(none)"));
+ if (parray[p].adminLevel > 0) {
+ pprintf(p, "Host : %s\n",
+/*
+ ((hp = gethostbyaddr((const char*) (connected ? &parray[p1].thisHost : &parray[p1].lastHost), sizeof(parray[p1].thisHost), AF_INET)) == 0) ? "" : hp->h_name,
+*/
+ dotQuad(connected ? parray[p1].thisHost : parray[p1].lastHost));
+ }
+ if ((parray[p].adminLevel > 0) && (parray[p1].registered))
+ if (parray[p1].num_comments)
+ pprintf(p, "Comments : %d\n", parray[p1].num_comments);
+
+ if (connected && parray[p1].registered && (p==p1 ||
+ (parray[p].adminLevel > 0))) {
+ char *timeToStr = ctime((time_t *) &parray[p1].timeOfReg);
+
+ timeToStr[strlen(timeToStr)-1]='\0';
+ pprintf(p, "\n");
+ onTime = (time(0) - parray[p1].logon_time) + parray[p1].totalTime;
+ days = (onTime / (60*60*24));
+ hours = ((onTime % (60*60*24)) / (60*60));
+ mins = (((onTime % (60*60*24)) % (60*60)) / 60);
+ secs = (((onTime % (60*60*24)) % (60*60)) % 60);
+ if ((days==0) && (hours==0) && (mins==0)) {
+ pprintf(p, "Total time on-line: %d second%s\n",
+ secs, (secs==1) ? "" : "s");
+ } else if ((days==0) && (hours==0)) {
+ pprintf(p, "Total time on-line: %d minute%s and %d second%s\n",
+ mins, (mins==1) ? "" : "s",
+ secs, (secs==1) ? "" : "s");
+ } else if (days==0) {
+ pprintf(p, "Total time on-line: %d hour%s, %d minute%s and %d "
+ "second%s\n",
+ hours, (hours==1) ? "" : "s",
+ mins, (mins==1) ? "" : "s",
+ secs, (secs==1) ? "" : "s");
+ } else {
+ pprintf(p, "Total time on-line: %d day%s, %d hour%s, %d minute%s "
+ "and %d second%s\n",
+ days, (days==1) ? "" : "s",
+ hours, (hours==1) ? "" : "s",
+ mins, (mins==1) ? "" : "s",
+ secs, (secs==1) ? "" : "s");
+ }
+ pprintf(p, "%% of life on-line: %3.1f (since %s)\n",
+ (double)((onTime*100)/(double)(time(0)-parray[p1].timeOfReg)),
+ timeToStr);
+ }
+
+#ifdef TIMESEAL
+
+ if (connected)
+ pprintf(p, "\nTimeseal : %s\n", (con[parray[p1].socket].timeseal)
+? "On" : "Off");
+ if ((parray[p].adminLevel > 0) && (connected)) {
+ if (findConnection(parray[p1].socket)) {
+ pprintf(p, "Unix acc : %s\nSystem/OS : %s\n",
+ con[parray[p1].socket].user,
+ con[parray[p1].socket].sys);
+ }
+ }
+
+#endif
+
+ if (parray[p1].num_plan) {
+ pprintf(p, "\n");
+ for (i = 0; i < parray[p1].num_plan; i++)
+ pprintf(p, "%2d: %s\n", i + 1, (parray[p1].planLines[i] != NULL) ? parray[p1].planLines[i] : "");
+ }
+ if (!connected)
+ player_remove(p1);
+ return COM_OK;
+}
+
+PUBLIC int com_variables(int p, param_list param)
+{
+ int p1, connected;
+ int i;
+
+ if (!FindPlayer(p, &param[0], &p1, &connected))
+ return COM_OK;
+
+ pprintf(p, "Variable settings of %s:\n", parray[p1].name);
+/* if (parray[p1].fullName)
+ pprintf(p, " Realname: %s\n", parray[p1].fullName);
+*/
+ if (parray[p1].uscfRating)
+ pprintf(p, " USCF: %d\n", parray[p1].uscfRating);
+ pprintf(p, " time=%-3d inc=%-3d private=%d Lang=%s\n",
+ parray[p1].d_time, parray[p1].d_inc, parray[p1].private,
+ Language(parray[p1].language));
+ pprintf(p, " rated=%d ropen=%d open=%d simopen=%d\n",
+ parray[p1].rated, parray[p1].ropen, parray[p1].open, parray[p1].sopen);
+ pprintf(p, " shout=%d cshout=%d kib=%d tell=%d notifiedby=%d\n",
+ parray[p1].i_shout, parray[p1].i_cshout, parray[p1].i_kibitz, parray[p1].i_tell, parray[p1].notifiedby);
+ pprintf(p, " pin=%d gin=%d style=%-3d flip=%d\n",
+ parray[p1].i_login, parray[p1].i_game, parray[p1].style + 1, parray[p1].flip);
+ pprintf(p, " highlight=%d bell=%d auto=%d mailmess=%d pgn=%d\n",
+ parray[p1].highlight, parray[p1].bell, parray[p1].automail, parray[p1].i_mailmess, parray[p1].pgn);
+ pprintf(p, " width=%-3d height=%-3d\n",
+ parray[p1].d_width, parray[p1].d_height);
+ if (parray[p1].prompt && parray[p1].prompt != def_prompt)
+ pprintf(p, " Prompt: %s\n", parray[p1].prompt);
+
+ { /* added code to print channels */
+ int count = 0;
+ for (i = 0; i < MAX_CHANNELS; i++) {
+ if (on_channel(i, p1)) {
+ if (!count)
+ pprintf(p, "\n Channels:");
+ pprintf(p, " %d", i);
+ count++;
+ }
+ }
+ if (count)
+ pprintf(p, "\n");
+ }
+/* if (parray[p1].numAlias && (p == p1)) {
+ pprintf(p, "\n Aliases:\n");
+ for (i = 0; i < parray[p1].numAlias; i++) {
+ pprintf(p, " %s %s\n", parray[p1].alias_list[i].comm_name,
+ parray[p1].alias_list[i].alias);
+ }
+ }
+*/
+ if (parray[p1].num_formula) {
+ pprintf(p, "\n");
+ for (i = 0; i < parray[p1].num_formula; i++) {
+ if (parray[p1].formulaLines[i] != NULL)
+ pprintf(p, " f%d: %s\n", i + 1, parray[p1].formulaLines[i]);
+ else
+ pprintf(p, " f%d:\n", i + 1);
+ }
+ }
+ if (parray[p1].formula != NULL)
+ pprintf(p, "\nFormula: %s\n", parray[p1].formula);
+
+ if (!connected)
+ player_remove(p1);
+ return COM_OK;
+}
+
+PUBLIC int com_password(int p, param_list param)
+{
+ char *oldpassword = param[0].val.word;
+ char *newpassword = param[1].val.word;
+ char salt[3];
+
+ if (!parray[p].registered) {
+ pprintf(p, "Setting a password is only for registered players.\n");
+ return COM_OK;
+ }
+ if (parray[p].passwd) {
+ salt[0] = parray[p].passwd[0];
+ salt[1] = parray[p].passwd[1];
+ salt[2] = '\0';
+ if (strcmp(crypt(oldpassword, salt), parray[p].passwd)) {
+ pprintf(p, "Incorrect password, password not changed!\n");
+ return COM_OK;
+ }
+ rfree(parray[p].passwd);
+ parray[p].passwd = NULL;
+ }
+ salt[0] = 'a' + rand() % 26;
+ salt[1] = 'a' + rand() % 26;
+ salt[2] = '\0';
+ parray[p].passwd = strdup(crypt(newpassword, salt));
+ pprintf(p, "Password changed to \"%s\".\n", newpassword);
+ return COM_OK;
+}
+
+PUBLIC int com_uptime(int p, param_list param)
+{
+ unsigned long uptime = time(0) - startuptime;
+ struct rusage ru;
+ int days = (uptime / (60*60*24));
+ int hours = ((uptime % (60*60*24)) / (60*60));
+ int mins = (((uptime % (60*60*24)) % (60*60)) / 60);
+ int secs = (((uptime % (60*60*24)) % (60*60)) % 60);
+
+ pprintf(p, "Server location: %s Server version : %s\n", fics_hostname,VERS_NUM);
+ pprintf(p, "The server has been up since %s.\n", strltime(&startuptime));
+ if ((days==0) && (hours==0) && (mins==0)) {
+ pprintf(p, "(Up for %d second%s)\n",
+ secs, (secs==1) ? "" : "s");
+ } else if ((days==0) && (hours==0)) {
+ pprintf(p, "(Up for %d minute%s and %d second%s)\n",
+ mins, (mins==1) ? "" : "s",
+ secs, (secs==1) ? "" : "s");
+ } else if (days==0) {
+ pprintf(p, "(Up for %d hour%s, %d minute%s and %d second%s)\n",
+ hours, (hours==1) ? "" : "s",
+ mins, (mins==1) ? "" : "s",
+ secs, (secs==1) ? "" : "s");
+ } else {
+ pprintf(p, "(Up for %d day%s, %d hour%s, %d minute%s and %d second%s)\n",
+ days, (days==1) ? "" : "s",
+ hours, (hours==1) ? "" : "s",
+ mins, (mins==1) ? "" : "s",
+ secs, (secs==1) ? "" : "s");
+ }
+ pprintf(p, "\nAllocs: %u Frees: %u Allocs In Use: %u\n",
+ malloc_count, free_count, malloc_count - free_count);
+ if (parray[p].adminLevel >= ADMIN_ADMIN) {
+ pprintf(p, "\nplayer size:%d, game size:%d, con size:%d, g_num:%d\n",
+ sizeof(player), sizeof(game), net_consize(), g_num);
+ getrusage(RUSAGE_SELF, &ru);
+ pprintf(p, "pagesize = %d, maxrss = %d, total = %d\n", getpagesize(), ru.ru_maxrss, getpagesize() * ru.ru_maxrss);
+ }
+ pprintf(p, "\nPlayer limit: %d\n", max_connections);
+ pprintf(p, "\nThere are currently %d players, with a high of %d since last restart.\n", player_count(), player_high);
+ pprintf(p, "There are currently %d games, with a high of %d since last restart.\n", game_count(), game_high);
+ pprintf(p, "\nCompiled on %s\n", COMP_DATE);
+ return COM_OK;
+}
+
+PUBLIC int com_date(int p, param_list param)
+{
+ int t = time(0);
+ pprintf(p, "Local time - %s\n", strltime(&t));
+ pprintf(p, "Greenwich time - %s\n", strgtime(&t));
+ return COM_OK;
+}
+
+char *inout_string[] = {
+ "login", "logout"
+};
+
+PRIVATE int plogins(p, fname)
+int p;
+char *fname;
+{
+ FILE *fp;
+ int inout, thetime, registered;
+ char loginName[MAX_LOGIN_NAME + 1];
+ char ipstr[20];
+
+ fp = fopen(fname, "r");
+ if (!fp) {
+ pprintf(p, "Sorry, no login information available.\n");
+ return COM_OK;
+ }
+ while (!feof(fp)) {
+ if (fscanf(fp, "%d %s %d %d %s\n", &inout, loginName, &thetime,
+ &registered, ipstr) != 5) {
+ fprintf(stderr, "FICS: Error in login info format. %s\n", fname);
+ fclose(fp);
+ return COM_OK;
+ }
+ pprintf(p, "%s: %-17s %-6s", strltime(&thetime), loginName,
+ inout_string[inout]);
+ if (parray[p].adminLevel > 0) {
+ pprintf(p, " from %s\n", ipstr);
+ } else
+ pprintf(p, "\n");
+ }
+ fclose(fp);
+ return COM_OK;
+}
+
+PUBLIC int com_llogons(int p, param_list param)
+{
+ char fname[MAX_FILENAME_SIZE];
+
+ sprintf(fname, "%s/%s", stats_dir, STATS_LOGONS);
+ return plogins(p, fname);
+}
+
+PUBLIC int com_logons(int p, param_list param)
+{
+ char fname[MAX_FILENAME_SIZE];
+
+ if (param[0].type == TYPE_WORD) {
+ sprintf(fname, "%s/player_data/%c/%s.%s", stats_dir, param[0].val.word[0], param[0].val.word, STATS_LOGONS);
+ } else {
+ sprintf(fname, "%s/player_data/%c/%s.%s", stats_dir, parray[p].login[0], parray[p].login, STATS_LOGONS);
+ }
+ return plogins(p, fname);
+}
+
+#define WHO_OPEN 0x01
+#define WHO_CLOSED 0x02
+#define WHO_RATED 0x04
+#define WHO_UNRATED 0x08
+#define WHO_FREE 0x10
+#define WHO_PLAYING 0x20
+#define WHO_REGISTERED 0x40
+#define WHO_UNREGISTERED 0x80
+
+PRIVATE void who_terse(int p, int num, int *plist, int type)
+{
+ char ptmp[80 + 20]; /* for highlight */
+ multicol *m = multicol_start(PARRAY_SIZE);
+ int i;
+ int p1;
+ int rat;
+
+ /* altered DAV 3/15/95 */
+
+ for (i = 0; i < num; i++) {
+ p1 = plist[i];
+ if (type == blitz_rat)
+ rat = parray[p1].b_stats.rating;
+ if (type == wild_rat)
+ rat = parray[p1].w_stats.rating;
+ if (type == std_rat)
+ rat = parray[p1].s_stats.rating;
+
+ if (type == none) {
+ sprintf(ptmp, " ");
+ } else {
+ sprintf(ptmp, "%-4s", ratstrii(rat, parray[p1].registered));
+ if (parray[p1].simul_info.numBoards) {
+ strcat(ptmp, "~");
+ } else if ((parray[p1].game >= 0) && (garray[parray[p1].game].status == GAME_EXAMINE)) {
+ strcat(ptmp, "#");
+ } else if (parray[p1].game >= 0) {
+ strcat(ptmp, "^");
+ } else if (!parray[p1].open) {
+ strcat(ptmp, ":");
+ } else if (player_idle(p1) > 300) {
+ strcat(ptmp, ".");
+ } else {
+ strcat(ptmp, " ");
+ }
+ }
+ if (p == p1) {
+ psprintf_highlight(p, ptmp + strlen(ptmp), "%s", parray[p1].name);
+ } else {
+ strcat(ptmp, parray[p1].name);
+ }
+ if ((parray[p1].adminLevel >= 10) && (parray[p1].i_admin))
+ strcat(ptmp, "(*)");
+ if (in_list("computer", parray[p1].name))
+ strcat(ptmp, "(C)");
+/* grimm's fishlist
+ if (in_list("fish", parray[p1].name)) strcat(ptmp, "(Fish)");
+*/
+ if (in_list("fm", parray[p1].name))
+ strcat(ptmp, "(FM)");
+ if (in_list("im", parray[p1].name))
+ strcat(ptmp, "(IM)");
+ if (in_list("gm", parray[p1].name))
+ strcat(ptmp, "(GM)");
+ if (in_list("td", parray[p1].name))
+ strcat(ptmp, "(TD)");
+ multicol_store(m, ptmp);
+ }
+ multicol_pprint(m, p, 80, 2);
+ multicol_end(m);
+ pprintf(p, "\n %d Players displayed (of %d). (*) indicates system administrator.\n", num, player_count());
+}
+
+PRIVATE void who_verbose(p, num, plist)
+int p;
+int num;
+int plist[];
+{
+ int i, p1;
+ char playerLine[255], tmp[255]; /* +8 for highlight */
+
+ pprintf(p,
+ " +---------------------------------------------------------------+\n"
+ );
+ pprintf(p,
+ " | User Standard Blitz On for Idle |\n"
+ );
+ pprintf(p,
+ " +---------------------------------------------------------------+\n"
+ );
+
+ for (i = 0; i < num; i++) {
+ p1 = plist[i];
+
+ strcpy(playerLine, " |");
+
+ if (parray[p1].game >= 0)
+ sprintf(tmp, "%3d", parray[p1].game + 1);
+ else
+ sprintf(tmp, " ");
+ strcat(playerLine, tmp);
+
+ if (!parray[p1].open)
+ sprintf(tmp, "X");
+ else
+ sprintf(tmp, " ");
+ strcat(playerLine, tmp);
+
+ if (parray[p1].registered)
+ if (parray[p1].rated) {
+ sprintf(tmp, " ");
+ } else {
+ sprintf(tmp, "u");
+ }
+ else
+ sprintf(tmp, "U");
+ strcat(playerLine, tmp);
+
+ /* Modified by DAV 3/15/95 */
+ if (p == p1) {
+ strcpy(tmp, " ");
+ psprintf_highlight(p, tmp + strlen(tmp), "%-17s", parray[p1].name);
+ } else {
+ sprintf(tmp, " %-17s", parray[p1].name);
+ }
+ strcat(playerLine, tmp);
+
+ sprintf(tmp, " %4s %-4s %5s ",
+ ratstrii(parray[p1].s_stats.rating, parray[p1].registered),
+ ratstrii(parray[p1].b_stats.rating, parray[p1].registered),
+ hms(player_ontime(p1), 0, 0, 0));
+ strcat(playerLine, tmp);
+
+ if (player_idle(p1) >= 60) {
+ sprintf(tmp, "%5s |\n", hms(player_idle(p1), 0, 0, 0));
+ } else {
+ sprintf(tmp, " |\n");
+ }
+ strcat(playerLine, tmp);
+ pprintf(p, "%s", playerLine);
+ }
+
+ pprintf(p,
+ " | |\n"
+ );
+ pprintf(p,
+ " | %3d Players Displayed |\n",
+ num
+ );
+ pprintf(p,
+ " +---------------------------------------------------------------+\n"
+ );
+}
+
+PRIVATE void who_winloss(p, num, plist)
+int p;
+int num;
+int plist[];
+{
+ int i, p1;
+ char playerLine[255], tmp[255]; /* for highlight */
+
+ pprintf(p,
+ "Name Stand win loss draw Blitz win loss draw idle\n"
+ );
+ pprintf(p,
+ "---------------- ----- ------------- ----- ------------- ----\n"
+ );
+
+ for (i = 0; i < num; i++) {
+ p1 = plist[i];
+ if (p1 == p) {
+ psprintf_highlight(p, playerLine, "%-17s", parray[p1].name);
+ } else {
+ sprintf(playerLine, "%-17s", parray[p1].name);
+ }
+ sprintf(tmp, " %4s %4d %4d %4d ",
+ ratstrii(parray[p1].s_stats.rating, parray[p1].registered),
+ (int) parray[p1].s_stats.win,
+ (int) parray[p1].s_stats.los,
+ (int) parray[p1].s_stats.dra);
+ strcat(playerLine, tmp);
+
+ sprintf(tmp, "%4s %4d %4d %4d ",
+ ratstrii(parray[p1].b_stats.rating, parray[p1].registered),
+ (int) parray[p1].b_stats.win,
+ (int) parray[p1].b_stats.los,
+ (int) parray[p1].b_stats.dra);
+ strcat(playerLine, tmp);
+
+ if (player_idle(p1) >= 60) {
+ sprintf(tmp, "%5s\n", hms(player_idle(p1), 0, 0, 0));
+ } else {
+ sprintf(tmp, " \n");
+ }
+ strcat(playerLine, tmp);
+
+ pprintf(p, "%s", playerLine);
+ }
+ pprintf(p, " %3d Players Displayed.\n", num);
+}
+
+PRIVATE int who_ok(p, sel_bits)
+int p;
+unsigned int sel_bits;
+{
+ if (parray[p].status != PLAYER_PROMPT)
+ return 0;
+ if (sel_bits == 0xff)
+ return 1;
+ if (sel_bits & WHO_OPEN)
+ if (!parray[p].open)
+ return 0;
+ if (sel_bits & WHO_CLOSED)
+ if (parray[p].open)
+ return 0;
+ if (sel_bits & WHO_RATED)
+ if (!parray[p].rated)
+ return 0;
+ if (sel_bits & WHO_UNRATED)
+ if (parray[p].rated)
+ return 0;
+ if (sel_bits & WHO_FREE)
+ if (parray[p].game >= 0)
+ return 0;
+ if (sel_bits & WHO_PLAYING)
+ if (parray[p].game < 0)
+ return 0;
+ if (sel_bits & WHO_REGISTERED)
+ if (!parray[p].registered)
+ return 0;
+ if (sel_bits & WHO_UNREGISTERED)
+ if (parray[p].registered)
+ return 0;
+ return 1;
+}
+
+
+PRIVATE int blitz_cmp(const void *pp1, const void *pp2)
+{
+ register int p1 = *(int *) pp1;
+ register int p2 = *(int *) pp2;
+ if (parray[p1].status != PLAYER_PROMPT) {
+ if (parray[p2].status != PLAYER_PROMPT)
+ return 0;
+ else
+ return -1;
+ }
+ if (parray[p2].status != PLAYER_PROMPT)
+ return 1;
+ if (parray[p1].b_stats.rating > parray[p2].b_stats.rating)
+ return -1;
+ if (parray[p1].b_stats.rating < parray[p2].b_stats.rating)
+ return 1;
+ if (parray[p1].registered > parray[p2].registered)
+ return -1;
+ if (parray[p1].registered < parray[p2].registered)
+ return 1;
+ return strcmp(parray[p1].login, parray[p2].login);
+}
+
+PRIVATE int stand_cmp(const void *pp1, const void *pp2)
+{
+ register int p1 = *(int *) pp1;
+ register int p2 = *(int *) pp2;
+ if (parray[p1].status != PLAYER_PROMPT) {
+ if (parray[p2].status != PLAYER_PROMPT)
+ return 0;
+ else
+ return -1;
+ }
+ if (parray[p2].status != PLAYER_PROMPT)
+ return 1;
+ if (parray[p1].s_stats.rating > parray[p2].s_stats.rating)
+ return -1;
+ if (parray[p1].s_stats.rating < parray[p2].s_stats.rating)
+ return 1;
+ if (parray[p1].registered > parray[p2].registered)
+ return -1;
+ if (parray[p1].registered < parray[p2].registered)
+ return 1;
+ return strcmp(parray[p1].login, parray[p2].login);
+}
+
+PRIVATE int wild_cmp(const void *pp1, const void *pp2)
+{
+ register int p1 = *(int *) pp1;
+ register int p2 = *(int *) pp2;
+ if (parray[p1].status != PLAYER_PROMPT) {
+ if (parray[p2].status != PLAYER_PROMPT)
+ return 0;
+ else
+ return -1;
+ }
+ if (parray[p2].status != PLAYER_PROMPT)
+ return 1;
+ if (parray[p1].w_stats.rating > parray[p2].w_stats.rating)
+ return -1;
+ if (parray[p1].w_stats.rating < parray[p2].w_stats.rating)
+ return 1;
+ if (parray[p1].registered > parray[p2].registered)
+ return -1;
+ if (parray[p1].registered < parray[p2].registered)
+ return 1;
+ return strcmp(parray[p1].login, parray[p2].login);
+}
+
+PRIVATE int alpha_cmp(const void *pp1, const void *pp2)
+{
+ register int p1 = *(int *) pp1;
+ register int p2 = *(int *) pp2;
+ if (parray[p1].status != PLAYER_PROMPT) {
+ if (parray[p2].status != PLAYER_PROMPT)
+ return 0;
+ else
+ return -1;
+ }
+ if (parray[p2].status != PLAYER_PROMPT)
+ return 1;
+ return strcmp(parray[p1].login, parray[p2].login);
+}
+
+PUBLIC void sort_players(int players[PARRAY_SIZE],
+ int ((*cmp_func) (const void *, const void *)))
+{
+ int i;
+
+ for (i = 0; i < p_num; i++) {
+ players[i] = i;
+ }
+ qsort(players, p_num, sizeof(int), cmp_func);
+}
+
+/* This is the of the most compliclicated commands in terms of parameters */
+PUBLIC int com_who(int p, param_list param)
+{
+ int style = 0;
+ float stop_perc = 1.0;
+ float start_perc = 0;
+ unsigned int sel_bits = 0xff;
+ int sortlist[PARRAY_SIZE], plist[PARRAY_SIZE];
+ int ((*cmp_func) (const void *, const void *)) = blitz_cmp;
+ int startpoint;
+ int stoppoint;
+ int i, len;
+ int tmpI, tmpJ;
+ char c;
+ int p1, count, num_who;
+ int sort_type = blitz_rat;
+
+ if (param[0].type == TYPE_WORD) {
+ len = strlen(param[0].val.word);
+ for (i = 0; i < len; i++) {
+ c = param[0].val.word[i];
+ if (isdigit(c)) {
+ if (i == 0 || !isdigit(param[0].val.word[i - 1])) {
+ tmpI = c - '0';
+ if (tmpI == 1) {
+ start_perc = 0.0;
+ stop_perc = 0.333333;
+ } else if (tmpI == 2) {
+ start_perc = 0.333333;
+ stop_perc = 0.6666667;
+ } else if (tmpI == 3) {
+ start_perc = 0.6666667;
+ stop_perc = 1.0;
+ } else if ((i == len - 1) || (!isdigit(param[0].val.word[i + 1])))
+ return COM_BADPARAMETERS;
+ } else {
+ tmpI = c - '0';
+ tmpJ = param[0].val.word[i - 1] - '0';
+ if (tmpI == 0)
+ return COM_BADPARAMETERS;
+ if (tmpJ > tmpI)
+ return COM_BADPARAMETERS;
+ start_perc = ((float) tmpJ - 1.0) / (float) tmpI;
+ stop_perc = ((float) tmpJ) / (float) tmpI;
+ }
+ } else {
+ switch (c) {
+ case 'o':
+ if (sel_bits == 0xff)
+ sel_bits = WHO_OPEN;
+ else
+ sel_bits |= WHO_OPEN;
+ break;
+ case 'r':
+ if (sel_bits == 0xff)
+ sel_bits = WHO_RATED;
+ else
+ sel_bits |= WHO_RATED;
+ break;
+ case 'f':
+ if (sel_bits == 0xff)
+ sel_bits = WHO_FREE;
+ else
+ sel_bits |= WHO_FREE;
+ break;
+ case 'a':
+ if (sel_bits == 0xff)
+ sel_bits = WHO_FREE | WHO_OPEN;
+ else
+ sel_bits |= (WHO_FREE | WHO_OPEN);
+ break;
+ case 'R':
+ if (sel_bits == 0xff)
+ sel_bits = WHO_REGISTERED;
+ else
+ sel_bits |= WHO_REGISTERED;
+ break;
+ case 'l': /* Sort order */
+ cmp_func = alpha_cmp;
+ sort_type = none;
+ break;
+ case 'A': /* Sort order */
+ cmp_func = alpha_cmp;
+ break;
+ case 'w': /* Sort order */
+ cmp_func = wild_cmp;
+ sort_type = wild_rat;
+ break;
+ case 's': /* Sort order */
+ cmp_func = stand_cmp;
+ sort_type = std_rat;
+ break;
+ case 'b': /* Sort order */
+ cmp_func = blitz_cmp;
+ sort_type = blitz_rat;
+ break;
+ case 't': /* format */
+ style = 0;
+ break;
+ case 'v': /* format */
+ style = 1;
+ break;
+ case 'n': /* format */
+ style = 2;
+ break;
+ case 'U':
+ if (sel_bits == 0xff)
+ sel_bits = WHO_UNREGISTERED;
+ else
+ sel_bits |= WHO_UNREGISTERED;
+ break;
+ default:
+ return COM_BADPARAMETERS;
+ break;
+ }
+ }
+ }
+ }
+ sort_players(sortlist, cmp_func);
+ count = 0;
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (!who_ok(sortlist[p1], sel_bits))
+ continue;
+ count++;
+ }
+ startpoint = floor((float) count * start_perc);
+ stoppoint = ceil((float) count * stop_perc) - 1;
+ num_who = 0;
+ count = 0;
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (!who_ok(sortlist[p1], sel_bits))
+ continue;
+ if ((count >= startpoint) && (count <= stoppoint)) {
+ plist[num_who++] = sortlist[p1];
+ }
+ count++;
+ }
+ if (num_who == 0) {
+ pprintf(p, "No logged in players match the flags in your who request.\n");
+ return COM_OK;
+ }
+ switch (style) {
+ case 0: /* terse */
+ who_terse(p, num_who, plist, sort_type);
+ break;
+ case 1: /* verbose */
+ who_verbose(p, num_who, plist);
+ break;
+ case 2: /* win-loss */
+ who_winloss(p, num_who, plist);
+ break;
+ default:
+ return COM_BADPARAMETERS;
+ break;
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_refresh(int p, param_list param)
+{
+ int g, p1;
+
+ if (param[0].type == TYPE_NULL) {
+ if (parray[p].game >= 0) {
+ send_board_to(parray[p].game, p);
+ } else { /* Do observing in here */
+ if (parray[p].num_observe) {
+ for (g = 0; g < parray[p].num_observe; g++) {
+ send_board_to(parray[p].observe_list[g], p);
+ }
+ } else {
+ pprintf(p, "You are neither playing, observing nor examining a game.\n");
+ return COM_OK;
+ }
+ }
+ } else {
+ g = GameNumFromParam (p, &p1, &param[0]);
+ if (g < 0)
+ return COM_OK;
+ if ((g >= g_num) || ((garray[g].status != GAME_ACTIVE)
+ && (garray[g].status != GAME_EXAMINE))) {
+ pprintf(p, "No such game.\n");
+ } else if (garray[g].private && parray[p].adminLevel==ADMIN_USER) {
+ pprintf (p, "Sorry, game %d is a private game.\n", g+1);
+ } else {
+ if (garray[g].private)
+ pprintf(p, "Refreshing PRIVATE game %d\n", g+1);
+ send_board_to(g, p);
+ }
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_open(int p, param_list param)
+{
+ int retval;
+ ASSERT(param[0].type == TYPE_NULL);
+ if ((retval = pcommand(p, "set open")) != COM_OK)
+ return retval;
+ else
+ return COM_OK_NOPROMPT;
+}
+
+PUBLIC int com_simopen(int p, param_list param)
+{
+ int retval;
+ ASSERT(param[0].type == TYPE_NULL);
+ if ((retval = pcommand(p, "set simopen")) != COM_OK)
+ return retval;
+ else
+ return COM_OK_NOPROMPT;
+}
+
+PUBLIC int com_bell(int p, param_list param)
+{
+ int retval;
+ ASSERT(param[0].type == TYPE_NULL);
+ if ((retval = pcommand(p, "set bell")) != COM_OK)
+ return retval;
+ else
+ return COM_OK_NOPROMPT;
+}
+
+PUBLIC int com_flip(int p, param_list param)
+{
+ int retval;
+ ASSERT(param[0].type == TYPE_NULL);
+ if ((retval = pcommand(p, "set flip")) != COM_OK)
+ return retval;
+ else
+ return COM_OK_NOPROMPT;
+}
+
+PUBLIC int com_highlight(int p, param_list param)
+{
+ pprintf(p, "Obsolete command. Please do set highlight <0-15>.\n");
+ return COM_OK;
+}
+
+PUBLIC int com_style(int p, param_list param)
+{
+ int retval;
+ ASSERT(param[0].type == TYPE_INT);
+ if ((retval = pcommand(p, "set style %d", param[0].val.integer)) != COM_OK)
+ return retval;
+ else
+ return COM_OK_NOPROMPT;
+}
+
+PUBLIC int com_promote(int p, param_list param)
+{
+ int retval;
+ ASSERT(param[0].type == TYPE_WORD);
+ if ((retval = pcommand(p, "set promote %s", param[0].val.word)) != COM_OK)
+ return retval;
+ else
+ return COM_OK_NOPROMPT;
+}
+
+PUBLIC int com_alias(int p, param_list param)
+{
+ int al;
+
+ if (param[0].type == TYPE_NULL) {
+ for (al = 0; al < parray[p].numAlias; al++) {
+ pprintf(p, "%s -> %s\n", parray[p].alias_list[al].comm_name,
+ parray[p].alias_list[al].alias);
+ }
+ return COM_OK;
+ }
+ al = alias_lookup(param[0].val.word, parray[p].alias_list, parray[p].numAlias);
+ if (param[1].type == TYPE_NULL) {
+ if (al < 0) {
+ pprintf(p, "You have no alias named '%s'.\n", param[0].val.word);
+ } else {
+ pprintf(p, "%s -> %s\n", parray[p].alias_list[al].comm_name,
+ parray[p].alias_list[al].alias);
+ }
+ } else {
+ if (al < 0) {
+ if (parray[p].numAlias >= MAX_ALIASES - 1) {
+ pprintf(p, "You have your maximum of %d aliases.\n", MAX_ALIASES - 1);
+ } else {
+
+ if (!strcmp(param[0].val.string, "quit")) { /* making sure they
+ can't alias quit */
+ pprintf(p, "You can't alias this command.\n");
+ } else if (!strcmp(param[0].val.string, "unalias")) { /* making sure they
+ can't alias unalias
+ :) */
+ pprintf(p, "You can't alias this command.\n");
+ } else {
+ parray[p].alias_list[parray[p].numAlias].comm_name =
+ strdup(param[0].val.word);
+ parray[p].alias_list[parray[p].numAlias].alias =
+ strdup(param[1].val.string);
+ parray[p].numAlias++;
+ pprintf(p, "Alias set.\n");
+
+ }
+ }
+ } else {
+ rfree(parray[p].alias_list[al].alias);
+ parray[p].alias_list[al].alias = strdup(param[1].val.string);
+ pprintf(p, "Alias replaced.\n");
+ }
+ parray[p].alias_list[parray[p].numAlias].comm_name = NULL;
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_unalias(int p, param_list param)
+{
+ int al;
+ int i;
+
+ ASSERT(param[0].type == TYPE_WORD);
+ al = alias_lookup(param[0].val.word, parray[p].alias_list, parray[p].numAlias);
+ if (al < 0) {
+ pprintf(p, "You have no alias named '%s'.\n", param[0].val.word);
+ } else {
+ rfree(parray[p].alias_list[al].comm_name);
+ rfree(parray[p].alias_list[al].alias);
+ for (i = al; i < parray[p].numAlias; i++) {
+ parray[p].alias_list[i].comm_name = parray[p].alias_list[i + 1].comm_name;
+ parray[p].alias_list[i].alias = parray[p].alias_list[i + 1].alias;
+ }
+ parray[p].numAlias--;
+ parray[p].alias_list[parray[p].numAlias].comm_name = NULL;
+ pprintf(p, "Alias removed.\n");
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_servers(int p, param_list param)
+{
+/*
+ int i;
+
+ ASSERT(param[0].type == TYPE_NULL);
+ if (numServers == 0) {
+ */ pprintf(p, "There are no other servers known to this server.\n");
+ return COM_OK;
+}
+ /* pprintf(p, "There are %d known servers.\n", numServers); pprintf(p, "(Not
+ all of these may be active)\n"); pprintf(p, "%-30s%-7s\n", "HOST",
+ "PORT"); for (i = 0; i < numServers; i++) pprintf(p, "%-30s%-7d\n",
+ serverNames[i], serverPorts[i]); return COM_OK; } */
+
+PUBLIC int com_index(int p, param_list param)
+{
+ int i;
+ char index_default[] = "_index";
+ char *iwant, *filenames[100]; /* enough for all index filenames */
+
+ if (param[0].type == TYPE_NULL) {
+ iwant = index_default;
+ } else {
+ iwant = param[0].val.word;
+ if (!safestring(iwant)) {
+ pprintf(p, "Illegal character in category %s.\n", iwant);
+ return COM_OK;
+ }
+ }
+
+ i = search_directory(index_dir, iwant, filenames, 1000);
+ if (i == 0) {
+ pprintf(p, "No index entry for \"%s\".\n", iwant);
+ } else if ((i == 1) || !strcmp(*filenames, iwant)) {
+ if (psend_file(p, index_dir, *filenames)) {
+ /* we should never reach this unless the file was just deleted */
+ pprintf(p, "Index file %s could not be found! ", *filenames);
+ pprintf(p, "Please inform an admin of this. Thank you.\n");
+ }
+ } else {
+ pprintf(p, "Matches:");
+ display_directory(p, filenames, i);
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_help(int p, param_list param)
+{
+ int i, UseLang = parray[p].language;
+ char help_default[] = "_help";
+ char *iwant, *filenames[1000]; /* enough for all helpfile names */
+
+ if (param[0].type == TYPE_NULL) {
+ iwant = help_default;
+ } else {
+ iwant = param[0].val.word;
+ if (!safestring(iwant)) {
+ pprintf(p, "Illegal character in command %s.\n", iwant);
+ return COM_OK;
+ }
+ }
+ i = search_directory(help_dir[UseLang], iwant, filenames, 1000);
+ if (i == 0) {
+ if (UseLang != LANG_DEFAULT) {
+ i += search_directory(help_dir[LANG_DEFAULT], iwant, filenames, 1000);
+ if (i > 0) {
+ pprintf(p, "No help available in %s; using %s instead.\n",
+ Language(UseLang), Language(LANG_DEFAULT));
+ UseLang = LANG_DEFAULT;
+ }
+ }
+ if (i==0) {
+ pprintf(p, "No help available on \"%s\".\n", iwant);
+ return COM_OK;
+ }
+ }
+ if ((i == 1) || !strcmp(*filenames, iwant)) {
+ if (psend_file(p, help_dir[UseLang], *filenames)) {
+ /* we should never reach this unless the file was just deleted */
+ pprintf(p, "Helpfile %s could not be found! ", *filenames);
+ pprintf(p, "Please inform an admin of this. Thank you.\n");
+ }
+ } else {
+ pprintf(p, "Matches:");
+ display_directory(p, filenames, i);
+ pprintf(p, "[Type \"info\" for a list of FICS general information files.]\n");
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_info(int p, param_list param)
+{
+ int n;
+ char *filenames[1000];
+
+ if ((n = search_directory(info_dir, NULL, filenames, 1000)) > 0)
+ display_directory(p, filenames, n);
+ return COM_OK;
+}
+
+PUBLIC int com_adhelp(int p, param_list param)
+{
+ int i;
+ static char nullify = '\0';
+ char *iwant, *filenames[1000]; /* enough for all helpfile names */
+
+ if (param[0].type == TYPE_NULL) {
+ iwant = &nullify;
+ } else {
+ iwant = param[0].val.word;
+ if (!safestring(iwant)) {
+ pprintf(p, "Illegal character in command %s.\n", iwant);
+ return COM_OK;
+ }
+ }
+
+ i = search_directory(adhelp_dir, iwant, filenames, 1000);
+ if (i == 0) {
+ pprintf(p, "No help available on \"%s\".\n", iwant);
+ } else if ((i == 1) || !strcmp(*filenames, iwant)) {
+ if (psend_file(p, adhelp_dir, *filenames)) {
+ /* we should never reach this unless the file was just deleted */
+ pprintf(p, "Helpfile %s could not be found! ", *filenames);
+ pprintf(p, "Please inform an admin of this. Thank you.\n");
+ }
+ } else {
+ if (*iwant)
+ pprintf(p, "Matches:\n");
+ display_directory(p, filenames, i);
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_mailsource(int p, param_list param)
+{
+ static char nullify = '\0';
+ char *iwant, *buffer[1000];
+ char subj[81], fname[MAX_FILENAME_SIZE];
+ int count;
+
+ if (!parray[p].registered) {
+ pprintf(p, "Only registered people can use the mailsource command.\n");
+ return COM_OK;
+ }
+
+ if (param[0].type == TYPE_NULL) {
+ iwant = &nullify;
+ } else {
+ iwant = param[0].val.word;
+ }
+
+ count = search_directory(source_dir, iwant, buffer, 1000);
+ if (count == 0) {
+ pprintf(p, "Found no source file matching \"%s\".\n", iwant);
+ } else if ((count == 1) || !strcmp(iwant, *buffer)) {
+ sprintf(subj, "FICS source file from server %s: %s",fics_hostname,*buffer);
+ sprintf(fname, "%s/%s",source_dir, *buffer);
+ mail_file_to_user (p, subj, fname);
+ pprintf(p, "Source file %s sent to %s\n", *buffer, parray[p].emailAddress);
+ } else {
+ pprintf(p, "Found %d source files matching that:\n", count);
+ if (*iwant)
+ display_directory(p, buffer, count);
+ else { /* this junk is to get *.c *.h */
+ multicol *m = multicol_start(count);
+ char *s;
+ int i;
+ for (i=0; i < count; i++) {
+ if (((s = buffer[i] + strlen(buffer[i]) - 2) >= buffer[i]) && (!strcmp(s, ".c") || !strcmp(s, ".h")))
+ multicol_store(m, buffer[i]);
+ }
+ multicol_pprint(m, p, 78, 1);
+ multicol_end(m);
+ }
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_mailhelp(int p, param_list param)
+{ /* Sparky */
+ char subj[81], fname[MAX_FILENAME_SIZE];
+ char *iwant, *buffer[1000];
+
+ int count;
+ static char nullify = '\0';
+
+ if (!parray[p].registered) {
+ pprintf(p, "Only registered people can use the mailhelp command.\n");
+ return COM_OK;
+ }
+ if (param[0].type == TYPE_NULL)
+ iwant = &nullify;
+ else
+ iwant = param[0].val.word;
+
+ count = search_directory(help_dir[parray[p].language], iwant, buffer, 1000);
+ if (count == 0) {
+ pprintf(p, "Found no help file matching \"%s\".\n", iwant);
+ } else if (count == 1) {
+ sprintf(subj, "FICS help file from server %s: %s", fics_hostname, *buffer);
+ sprintf(fname, "%s/%s",help_dir[parray[p].language], *buffer);
+ mail_file_to_user (p, subj, fname);
+ pprintf(p, "Help file %s sent to %s\n", *buffer, parray[p].emailAddress);
+ } else {
+ pprintf(p, "Found %d helpfiles matching that:\n", count);
+ display_directory(p, buffer, count);
+ }
+
+ return COM_OK;
+}
+
+PUBLIC int com_handles(int p, param_list param)
+{
+ char *buffer[1000];
+ char pdir[MAX_FILENAME_SIZE];
+ int count;
+
+ sprintf(pdir, "%s/%c", player_dir, param[0].val.word[0]);
+ count = search_directory(pdir, param[0].val.word, buffer, 1000);
+ pprintf(p, "Found %d names.\n", count);
+ if (count > 0)
+ display_directory(p, buffer, count);
+ return COM_OK;
+}
+
+PUBLIC int com_getpi(int p, param_list param)
+{
+ int p1;
+
+ if (!in_list("td", parray[p].name)) {
+ pprintf(p, "Only TD programs are allowed to use this command.\n");
+ return COM_OK;
+ }
+ if (((p1 = player_find_bylogin(param[0].val.word)) < 0) || (parray[p1].registered == 0)) {
+ /* Darkside suggested not to return anything */
+ return COM_OK;
+ }
+ if (!parray[p1].registered) {
+ pprintf(p, "*getpi %s -1 -1 -1*\n", parray[p1].name);
+ } else {
+ pprintf(p, "*getpi %s %d %d %d*\n", parray[p1].name,
+ parray[p1].w_stats.rating,
+ parray[p1].b_stats.rating,
+ parray[p1].s_stats.rating);
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_getps(int p, param_list param)
+{
+ int p1;
+
+ if ((((p1 = player_find_bylogin(param[0].val.word)) < 0) || (parray[p1].registered == 0)) || (parray[p1].game < 0)) {
+ pprintf(p, "*status %s 1*\n", param[0].val.word);
+ return COM_OK;
+ }
+ pprintf(p, "*status %s 0 %s*\n", parray[p1].name, parray[(parray[p1].opponent)].name);
+ return COM_OK;
+}
+PUBLIC int com_limits(int p, param_list param)
+{
+ pprintf(p, "\nCurrent hardcoded limits:\n");
+ pprintf(p, " Max number of players: %d\n", MAX_PLAYER);
+ pprintf(p, " Max number of channels and max capacity: %d\n", MAX_CHANNELS);
+ pprintf(p, " Max number of channels one can be in: %d\n", MAX_INCHANNELS);
+ pprintf(p, " Max number of people on the notify list: %d\n", MAX_NOTIFY);
+ pprintf(p, " Max number of aliases: %d\n", MAX_ALIASES - 1);
+ pprintf(p, " Max number of games you can observe at a time: %d\n", MAX_OBSERVE);
+ pprintf(p, " Max number of requests pending: %d\n", MAX_PENDING);
+ pprintf(p, " Max number of people on the censor list: %d\n", MAX_CENSOR);
+ pprintf(p, " Max number of people in a simul game: %d\n", MAX_SIMUL);
+ pprintf(p, " Max number of messages one can receive: %d\n", MAX_MESSAGES);
+ pprintf(p, " Min number of games to be active: %d\n", PROVISIONAL);
+ pprintf(p, "\nAdmin settable limits:\n");
+ pprintf(p, " Quota list gives two shouts per %d seconds.\n", quota_time);
+ return COM_OK;
+}
diff --git a/FICS/comproc.h b/FICS/comproc.h
new file mode 100644
index 0000000..20c68d4
--- /dev/null
+++ b/FICS/comproc.h
@@ -0,0 +1,74 @@
+/* comproc.h
+ *
+ */
+
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name email yy/mm/dd Change
+ Richard Nash 93/10/22 Created
+*/
+
+#ifndef _COMPROC_H
+#define _COMPROC_H
+
+extern const none; /* = 0; */
+extern const blitz_rat; /* = 1; */
+extern const std_rat; /* = 2; */
+extern const wild_rat; /* = 3; */
+extern const light_rat;
+extern int com_rating_recalc();
+extern int com_more();
+extern void rscan_news(FILE *, int, int);
+extern void rscan_news2(FILE *, int, int);
+extern int num_news; /* The number of news items in the index file. */
+
+extern int com_quit();
+extern int com_index();
+extern int com_help();
+extern int com_info();
+extern int com_adhelp();
+extern int com_uscf();
+extern int com_set();
+extern int FindPlayer();
+extern int com_stats();
+extern int com_password();
+extern int com_uptime();
+extern int com_date();
+extern int com_llogons();
+extern int com_logons();
+extern int com_who();
+extern int com_refresh();
+extern int com_prefresh();
+extern int com_open();
+extern int com_simopen();
+extern int com_bell();
+extern int com_flip();
+extern int com_highlight();
+extern int com_style();
+extern int com_promote();
+extern int com_alias();
+extern int com_unalias();
+extern int com_servers();
+extern int com_mailsource();
+extern int com_mailhelp();
+extern int com_handles();
+extern int com_news();
+extern int com_getpi();
+extern int com_getps();
+extern int com_limits();
+
+#endif /* _COMPROC_H */
diff --git a/FICS/comproc.old.c b/FICS/comproc.old.c
new file mode 100644
index 0000000..92386ed
--- /dev/null
+++ b/FICS/comproc.old.c
@@ -0,0 +1,3394 @@
+/* comproc.c
+ *
+ */
+
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name email yy/mm/dd Change
+ Richard Nash 93/10/22 Created
+ foxbat 95/03/11 added filters in cmatch.
+*/
+
+#include "stdinclude.h"
+
+#include "common.h"
+#include "comproc.h"
+#include "command.h"
+#include "utils.h"
+#include "ficsmain.h"
+#include "config.h"
+#include "playerdb.h"
+#include "network.h"
+#include "rmalloc.h"
+#include "channel.h"
+#include "variable.h"
+#include "gamedb.h"
+#include "gameproc.h"
+#include "board.h"
+/* #include "hostinfo.h" */
+#include "multicol.h"
+#include "ratings.h"
+#include "formula.h"
+#include "lists.h"
+#include "eco.h"
+#include <string.h>
+
+#include <sys/resource.h>
+
+/* grimm */
+#if defined(SGI)
+#else
+/* int system(char *arg); */
+#endif
+
+const none = 0;
+const blitz_rat = 1;
+const std_rat = 2;
+const wild_rat = 3;
+
+int quota_time;
+
+PUBLIC int com_rating_recalc(int p, param_list param)
+{
+ ASSERT(parray[p].adminLevel >= ADMIN_ADMIN);
+ rating_recalc();
+ return COM_OK;
+}
+
+PUBLIC int com_more(int p, param_list param)
+{
+/* in_push(IN_HELP); */
+ pmore_file(p);
+ return COM_OK;
+}
+
+PUBLIC int com_news(int p, param_list param)
+{
+ FILE *fp;
+ char filename[MAX_FILENAME_SIZE];
+ char junk[MAX_LINE_SIZE];
+ char *junkp;
+ int crtime;
+ char count[10];
+ int flag, len;
+
+ if (((param[0].type == 0) || (!strcmp(param[0].val.word, "all")))) {
+
+/* no params - then just display index over news */
+
+ pprintf(p, "\n **** BULLETIN BOARD ****\n\n");
+ sprintf(filename, "%s/news.index", news_dir);
+ fp = fopen(filename, "r");
+ if (!fp) {
+ fprintf(stderr, "Can't find news index.\n");
+ return COM_OK;
+ }
+ flag = 0;
+ while (!feof(fp)) {
+ junkp = junk;
+ fgets(junk, MAX_LINE_SIZE, fp);
+ if (feof(fp))
+ break;
+ if ((len = strlen(junk)) > 1) {
+ junk[len - 1] = '\0';
+ sscanf(junkp, "%d %s", &crtime, count);
+ junkp = nextword(junkp);
+ junkp = nextword(junkp);
+ if (((param[0].type == TYPE_WORD) && (!strcmp(param[0].val.word, "all")))) {
+ pprintf(p, "%3s (%s) %s\n", count, strltime(&crtime), junkp);
+ flag = 1;
+ } else {
+ if ((crtime - player_lastconnect(p)) > 0) {
+ pprintf(p, "%3s (%s) %s\n", count, strltime(&crtime), junkp);
+ flag = 1;
+ }
+ }
+ }
+ }
+ fclose(fp);
+ crtime = player_lastconnect(p);
+ if (!flag) {
+ pprintf(p, "There are no news since your last login (%s).\n", strltime(&crtime));
+ } else {
+ pprintf(p, "\n");
+ }
+ } else {
+
+/* check if the specific news file exist in index */
+
+ sprintf(filename, "%s/news.index", news_dir);
+ fp = fopen(filename, "r");
+ if (!fp) {
+ fprintf(stderr, "Can't find news index.\n");
+ return COM_OK;
+ }
+ flag = 0;
+ while ((!feof(fp)) && (!flag)) {
+ junkp = junk;
+ fgets(junk, MAX_LINE_SIZE, fp);
+ if (feof(fp))
+ break;
+ if ((len = strlen(junk)) > 1) {
+ junk[len - 1] = '\0';
+ sscanf(junkp, "%d %s", &crtime, count);
+ if (!strcmp(count, param[0].val.word)) {
+ flag = 1;
+ junkp = nextword(junkp);
+ junkp = nextword(junkp);
+ pprintf(p, "\nNEWS %3s (%s)\n\n %s\n\n", count, strltime(&crtime), junkp);
+ }
+ }
+ }
+ fclose(fp);
+ if (!flag) {
+ pprintf(p, "Bad index number!\n");
+ return COM_OK;
+ }
+/* file exists - show it */
+
+ sprintf(filename, "%s/news.%s", news_dir, param[0].val.word);
+ fp = fopen(filename, "r");
+ if (!fp) {
+ pprintf(p, "No more info.\n");
+ return COM_OK;
+ }
+ fclose(fp);
+ sprintf(filename, "news.%s", param[0].val.word);
+ if (psend_file(p, news_dir, filename) < 0) {
+ pprintf(p, "Internal error - couldn't send news file!\n");
+ }
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_quit(int p, param_list param)
+{
+ if ((parray[p].game >= 0) && (garray[parray[p].game].status == GAME_EXAMINE)) {
+ pcommand(p, "unexamine");
+ }
+
+ if (parray[p].game >= 0) {
+ pprintf(p, "You can't quit while you are playing a game.\nType 'resign' to resign the game, or you can request an abort with 'abort'.\n");
+ return COM_OK;
+ }
+ psend_file(p, mess_dir, MESS_LOGOUT);
+ return COM_LOGOUT;
+}
+
+/*
+
+PUBLIC int com_query(int p, param_list param)
+{
+ int p1;
+ int count = 0;
+
+ if (!parray[p].registered) {
+ pprintf(p, "Only registered players can use the query command.\n");
+ return COM_OK;
+ }
+ if (parray[p].muzzled) {
+ pprintf(p, "You are muzzled.\n");
+ return COM_OK;
+ }
+ if (!printablestring(param[0].val.string)) {
+ pprintf(p, "Your message contains some unprintable character(s).\n");
+ return COM_OK;
+ }
+ if (!parray[p].query_log) {
+ parray[p].query_log = tl_new(5);
+ } else {
+ if (tl_numinlast(parray[p].query_log, 60 * 60) >= 2) {
+ pprintf(p, "Your can only query twice per hour.\n");
+ return COM_OK;
+ }
+ }
+ in_push(IN_SHOUT);
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (p1 == p)
+ continue;
+ if (parray[p1].status != PLAYER_PROMPT)
+ continue;
+ if (player_censored(p1, p))
+ continue;
+ count++;
+ if (parray[p1].highlight) {
+ pprintf_prompt(p1, "\n\033[7m%s queries:\033[0m %s\n", parray[p].name,
+ param[0].val.string);
+ } else {
+ pprintf_prompt(p1, "\n%s queries: %s\n", parray[p].name,
+ param[0].val.string);
+ }
+ }
+ pprintf(p, "Query heard by %d player(s).\n", count);
+ tl_logevent(parray[p].query_log, 1);
+ in_pop();
+ return COM_OK;
+}
+
+*/
+
+int CheckShoutQuota(int p)
+{
+ int timenow = time(0);
+ int timeleft = 0;
+ if (in_list("quota", parray[p].name)) {
+ if ((timeleft = timenow - parray[p].lastshout_a) < quota_time) {
+ return (quota_time - timeleft);
+ } else {
+ return 0;
+ }
+ } else {
+ return 0;
+ }
+}
+
+PUBLIC int com_shout(int p, param_list param)
+{
+ int p1;
+ int count = 0;
+ int timeleft; /* time left for quota if applicable */
+
+ if (!parray[p].registered) {
+ pprintf(p, "Only registered players can use the shout command.\n");
+ return COM_OK;
+ }
+ if (parray[p].muzzled) {
+ pprintf(p, "You are muzzled.\n");
+ return COM_OK;
+ }
+ if (param[0].type == TYPE_NULL) {
+ if (in_list("quota", parray[p].name)) {
+ pprintf(p, "[You are on the quota list.]\n");
+ if ((timeleft = CheckShoutQuota(p))) {
+ pprintf(p, "Next shout available in %d seconds.\n", timeleft);
+ } else {
+ pprintf(p, "Your next shout is ready for use.\n");
+ }
+ } else {
+ pprintf(p, "[You are not on the quota list.]\n");
+ pprintf(p, "Please specify what it is you want to shout.\n");
+ }
+ return COM_OK;
+ }
+ if ((timeleft = CheckShoutQuota(p))) {
+ pprintf(p, "[You are on the quota list.]\n");
+ pprintf(p, "Shout not sent. Next shout in %d seconds.\n", timeleft);
+ return COM_OK;
+ }
+ parray[p].lastshout_a = parray[p].lastshout_b;
+ parray[p].lastshout_b = time(0);
+ if (!printablestring(param[0].val.string)) {
+ pprintf(p, "Your message contains some unprintable character(s).\n");
+ return COM_OK;
+ }
+/* in_push(IN_SHOUT); */
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (p1 == p)
+ continue;
+ if (parray[p1].status != PLAYER_PROMPT)
+ continue;
+ if (!parray[p1].i_shout)
+ continue;
+ if (player_censored(p1, p))
+ continue;
+ count++;
+ pprintf_prompt(p1, "\n%s shouts: %s\n", parray[p].name,
+ param[0].val.string);
+ }
+ pprintf(p, "(%d) %s shouts: %s\n", count, parray[p].name,
+ param[0].val.string);
+/* in_pop(); */
+ if ((in_list("quota", parray[p].name)) && (timeleft = CheckShoutQuota(p))) {
+ pprintf(p, "[You are on the quota list.]\n");
+ pprintf(p, "Next shout in %d seconds.\n", timeleft);
+ return COM_OK;
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_cshout(int p, param_list param)
+{
+ int p1;
+ int count = 0;
+
+ if (!parray[p].registered) {
+ pprintf(p, "Only registered players can use the cshout command.\n");
+ return COM_OK;
+ }
+ if (parray[p].cmuzzled) {
+ pprintf(p, "You are c-muzzled.\n");
+ return COM_OK;
+ }
+ if (!printablestring(param[0].val.string)) {
+ pprintf(p, "Your message contains some unprintable character(s).\n");
+ return COM_OK;
+ }
+/* in_push(IN_SHOUT); */
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (p1 == p)
+ continue;
+ if (parray[p1].status != PLAYER_PROMPT)
+ continue;
+ if (!parray[p1].i_cshout)
+ continue;
+ if (player_censored(p1, p))
+ continue;
+ count++;
+ pprintf_prompt(p1, "\n%s c-shouts: %s\n", parray[p].name,
+ param[0].val.string);
+ }
+ pprintf(p, "(%d) %s c-shouts: %s\n", count, parray[p].name,
+ param[0].val.string);
+/* in_pop(); */
+ return COM_OK;
+}
+
+PUBLIC int com_it(int p, param_list param)
+{
+ int p1;
+ int count = 0;
+ int timeleft;
+
+ if (!parray[p].registered) {
+ pprintf(p, "Only registered players can use the it command.\n");
+ return COM_OK;
+ }
+ if (parray[p].muzzled) {
+ pprintf(p, "You are muzzled.\n");
+ return COM_OK;
+ }
+ if (param[0].type == TYPE_NULL) {
+ if (in_list("quota", parray[p].name)) {
+ pprintf(p, "[You are on the quota list.]\n");
+ if ((timeleft = CheckShoutQuota(p))) {
+ pprintf(p, "Next shout available in %d seconds.\n", timeleft);
+ } else {
+ pprintf(p, "Your next shout is ready for use.\n");
+ }
+ } else {
+ pprintf(p, "[You are not on the quota list.]\n");
+ pprintf(p, "Please specify what it is you want to shout.\n");
+ }
+ return COM_OK;
+ }
+ if ((timeleft = CheckShoutQuota(p))) {
+ pprintf(p, "[You are on the quota list.]\n");
+ pprintf(p, "Shout not sent. Next shout in %d seconds.\n", timeleft);
+ return COM_OK;
+ }
+ parray[p].lastshout_a = parray[p].lastshout_b;
+ parray[p].lastshout_b = time(0);
+
+ if (!printablestring(param[0].val.string)) {
+ pprintf(p, "Your message contains some unprintable character(s).\n");
+ return COM_OK;
+ }
+/* in_push(IN_SHOUT); */
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (p1 == p)
+ continue;
+ if (parray[p1].status != PLAYER_PROMPT)
+ continue;
+ if (!parray[p1].i_shout)
+ continue;
+ if (player_censored(p1, p))
+ continue;
+ count++;
+ if ((!strncmp(param[0].val.string, "\'", 1)) ||
+ (!strncmp(param[0].val.string, ",", 1)) ||
+ (!strncmp(param[0].val.string, ".", 1))) {
+ pprintf_prompt(p1, "\n--> %s%s\n", parray[p].name,
+ param[0].val.string);
+ } else {
+ pprintf_prompt(p1, "\n--> %s %s\n", parray[p].name,
+ param[0].val.string);
+ }
+ }
+ if ((!strncmp(param[0].val.string, "\'", 1)) ||
+ (!strncmp(param[0].val.string, ",", 1)) ||
+ (!strncmp(param[0].val.string, ".", 1))) {
+ pprintf(p, "(%d) --> %s%s\n", count, parray[p].name, param[0].val.string);
+ } else {
+ pprintf(p, "(%d) --> %s %s\n", count, parray[p].name, param[0].val.string);
+ }
+/* in_pop(); */
+ if ((in_list("quota", parray[p].name)) && (timeleft = CheckShoutQuota(p))) {
+ pprintf(p, "[You are on the quota list.]\n");
+ pprintf(p, "Next shout in %d seconds.\n", timeleft);
+ return COM_OK;
+ }
+ return COM_OK;
+}
+
+#define TELL_TELL 0
+#define TELL_SAY 1
+#define TELL_WHISPER 2
+#define TELL_KIBITZ 3
+#define TELL_CHANNEL 4
+PRIVATE int tell(int p, int p1, char *msg, int why, int ch)
+{
+ char tmp[MAX_LINE_SIZE];
+
+ if (!printablestring(msg)) {
+ pprintf(p, "Your message contains some unprintable character(s).\n");
+ return COM_OK;
+ }
+/* if (p1 == p) {
+ * pprintf(p, "Quit talking to yourself! It's embarrassing.\n");
+ * return COM_OK;
+ * }
+ */
+ if ((!parray[p1].i_tell) && (!parray[p].registered)) {
+ pprintf(p, "Player \"%s\" isn't listening to unregistered tells.\n",
+ parray[p1].name);
+ return COM_OK;
+ }
+ if ((player_censored(p1, p)) && (parray[p].adminLevel == 0)) {
+ pprintf(p, "Player \"%s\" is censoring you.\n", parray[p1].name);
+ return COM_OK;
+ }
+/* in_push(IN_TELL); */
+ switch (why) {
+ case TELL_SAY:
+ pprintf_highlight(p1, "\n%s", parray[p].name);
+ pprintf_prompt(p1, " says: %s\n", msg);
+ break;
+ case TELL_WHISPER:
+ pprintf(p1, "\n%s", parray[p].name);
+ pprintf_prompt(p1, " whispers: %s\n", msg);
+ break;
+ case TELL_KIBITZ:
+ pprintf(p1, "\n%s", parray[p].name);
+ pprintf_prompt(p1, " kibitzes: %s\n", msg);
+ break;
+ case TELL_CHANNEL:
+ pprintf(p1, "\n%s", parray[p].name);
+ pprintf_prompt(p1, "(%d): %s\n", ch, msg);
+ break;
+ case TELL_TELL:
+ default:
+ if (parray[p1].highlight) {
+ pprintf_highlight(p1, "\n%s", parray[p].name);
+ } else {
+ pprintf(p1, "\n%s", parray[p].name);
+ }
+ pprintf_prompt(p1, " tells you: %s\n", msg);
+ break;
+ }
+ tmp[0] = '\0';
+ if (!(parray[p1].busy[0] == '\0')) {
+ sprintf(tmp, ", who %s (idle: %d minutes)", parray[p1].busy,
+ ((player_idle(p1) % 3600) / 60));
+ } else {
+ if (((player_idle(p1) % 3600) / 60) > 2) {
+ sprintf(tmp, ", who has been idle %d minutes", ((player_idle(p1) % 3600) / 60));
+ }
+ /* else sprintf(tmp," "); */
+ }
+ if ((why == TELL_SAY) || (why == TELL_TELL)) {
+ pprintf(p, "(told %s%s)\n", parray[p1].name,
+ (((parray[p1].game>=0) && (garray[parray[p1].game].status == GAME_EXAMINE))
+ ? ", who is examining a game" :
+ (parray[p1].game >= 0 && (parray[p1].game != parray[p].game))
+ ? ", who is playing" : tmp));
+ parray[p].last_tell = p1;
+ }
+/* in_pop(); */
+ return COM_OK;
+}
+
+PRIVATE int chtell(int p, int ch, char *msg)
+{
+ int p1;
+ int i, count = 0, listening = 0;
+
+ if ((ch == 0) && (parray[p].adminLevel == 0)) {
+ pprintf(p, "Only admins may send tells to channel 0.\n");
+ return COM_OK;
+ }
+ if (ch < 0) {
+ pprintf(p, "The lowest channel number is 0.\n");
+ return COM_OK;
+ }
+ if (ch >= MAX_CHANNELS) {
+ pprintf(p, "The maximum channel number is %d.\n", MAX_CHANNELS - 1);
+ return COM_OK;
+ }
+/* in_push(IN_TELL); */
+ for (i = 0; i < numOn[ch]; i++) {
+ p1 = channels[ch][i];
+ if (p1 == p) {
+ listening = 1;
+ continue;
+ }
+ if (player_censored(p1, p))
+ continue;
+ if ((parray[p1].status == PLAYER_PASSWORD)
+ || (parray[p1].status == PLAYER_LOGIN))
+ continue;
+ tell(p, p1, msg, TELL_CHANNEL, ch);
+ count++;
+ }
+ if (count) {
+ /* parray[p].last_tell = -1; */
+ parray[p].last_channel = ch;
+ }
+ pprintf(p, "(%d->(%d))", ch, count);
+ if (!listening)
+ pprintf(p, " (You're not listening to channel %d.)", ch);
+ pprintf(p, "\n");
+/* in_pop(); */
+ return COM_OK;
+}
+
+PUBLIC int com_whisper(int p, param_list param)
+{
+ int g;
+ int p1;
+ int count = 0;
+
+ if (!parray[p].num_observe && parray[p].game < 0) {
+ pprintf(p, "You are not playing or observing a game.\n");
+ return COM_OK;
+ }
+ if (!parray[p].registered && (parray[p].game == -1)) {
+ pprintf(p, "You must be registered to whisper other people's games.\n");
+ return COM_OK;
+ }
+ if (parray[p].game >= 0)
+ g = parray[p].game;
+ else
+ g = parray[p].observe_list[0];
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (p1 == p)
+ continue;
+ if (parray[p1].status != PLAYER_PROMPT)
+ continue;
+ if (player_is_observe(p1, g)) {
+ tell(p, p1, param[0].val.string, TELL_WHISPER, 0);
+ if ((parray[p].adminLevel >= ADMIN_ADMIN) || !garray[g].private)
+ count++;
+ }
+ }
+ pprintf(p, "whispered to %d.\n", count);
+ return COM_OK;
+}
+
+PUBLIC int com_kibitz(int p, param_list param)
+{
+ int g;
+ int p1;
+ int count = 0;
+
+ if (!parray[p].num_observe && parray[p].game < 0) {
+ pprintf(p, "You are not playing or observing a game.\n");
+ return COM_OK;
+ }
+ if (!parray[p].registered && (parray[p].game == -1)) {
+ pprintf(p, "You must be registered to kibitz other people's games.\n");
+ return COM_OK;
+ }
+ if (parray[p].game >= 0)
+ g = parray[p].game;
+ else
+ g = parray[p].observe_list[0];
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (p1 == p)
+ continue;
+ if (parray[p1].status != PLAYER_PROMPT)
+ continue;
+ if (player_is_observe(p1, g) || parray[p1].game == g) {
+ tell(p, p1, param[0].val.string, TELL_KIBITZ, 0);
+ if ((parray[p].adminLevel >= ADMIN_ADMIN) || !garray[g].private || (parray[p1].game == g))
+ count++;
+ }
+ }
+ pprintf(p, "kibitzed to %d.\n", count);
+ return COM_OK;
+}
+
+PUBLIC int com_tell(int p, param_list param)
+{
+ int p1;
+
+ if (param[0].type == TYPE_NULL)
+ return COM_BADPARAMETERS;
+ if (param[0].type == TYPE_WORD) {
+ stolower(param[0].val.word);
+ if (!strcmp(param[0].val.word, ".")) {
+ if (parray[p].last_tell < 0) {
+ pprintf(p, "No one to tell anything to.\n");
+ return COM_OK;
+ } else {
+ return tell(p, parray[p].last_tell, param[1].val.string, TELL_TELL, 0);
+ }
+ }
+ if (!strcmp(param[0].val.word, ",")) {
+ if (parray[p].last_channel < 0) {
+ pprintf(p, "No previous channel.\n");
+ return COM_OK;
+ } else {
+ return chtell(p, parray[p].last_channel, param[1].val.string);
+ }
+ }
+ p1 = player_find_part_login(param[0].val.word);
+ if ((p1 < 0) || (parray[p1].status == PLAYER_PASSWORD)
+ || (parray[p1].status == PLAYER_LOGIN)) {
+ pprintf(p, "No user named \"%s\" is logged in.\n", param[0].val.word);
+ return COM_OK;
+ }
+ return tell(p, p1, param[1].val.string, TELL_TELL, 0);
+ } else { /* Channel */
+ return chtell(p, param[0].val.integer, param[1].val.string);
+ }
+}
+
+PUBLIC int com_xtell(int p, param_list param)
+{
+ int p1;
+ char *msg;
+ char tmp[2048];
+
+ msg = param[1].val.string;
+ p1 = player_find_part_login(param[0].val.word);
+ if ((p1 < 0) || (parray[p1].status == PLAYER_PASSWORD)
+ || (parray[p1].status == PLAYER_LOGIN)) {
+ pprintf(p, "No user named \"%s\" is logged in.\n", param[0].val.word);
+ return COM_OK;
+ }
+ if (!printablestring(msg)) {
+ pprintf(p, "Your message contains some unprintable character(s).\n");
+ return COM_OK;
+ }
+ if ((!parray[p1].i_tell) && (!parray[p].registered)) {
+ pprintf(p, "Player \"%s\" isn't listening to unregistered tells.\n",
+ parray[p1].name);
+ return COM_OK;
+ }
+ if ((player_censored(p1, p)) && (parray[p].adminLevel == 0)) {
+ pprintf(p, "Player \"%s\" is censoring you.\n", parray[p1].name);
+ return COM_OK;
+ }
+ if (parray[p1].highlight) {
+ pprintf_highlight(p1, "\n%s", parray[p].name);
+ } else {
+ pprintf(p1, "\n%s", parray[p].name);
+ }
+ pprintf_prompt(p1, " tells you: %s\n", msg);
+
+ tmp[0] = '\0';
+ if (!(parray[p1].busy[0] == '\0')) {
+ sprintf(tmp, ", who %s (idle: %d minutes)", parray[p1].busy,
+ ((player_idle(p1) % 3600) / 60));
+ } else {
+ if (((player_idle(p1) % 3600) / 60) > 2) {
+ sprintf(tmp, ", who has been idle %d minutes", ((player_idle(p1) % 3600) / 60));
+ }
+ }
+ pprintf(p, "(told %s%s)\n", parray[p1].name,
+ (((parray[p1].game>=0) && (garray[parray[p1].game].status == GAME_EXAMINE))
+ ? ", who is examining a game" :
+ (parray[p1].game >= 0 && (parray[p1].game != parray[p].game))
+ ? ", who is playing" : tmp));
+ return COM_OK;
+}
+
+PUBLIC int com_say(int p, param_list param)
+{
+ if (parray[p].opponent < 0) {
+ if (parray[p].last_opponent < 0) {
+ pprintf(p, "No one to say anything to, try tell.\n");
+ return COM_OK;
+ } else {
+ return tell(p, parray[p].last_opponent, param[0].val.string, TELL_SAY, 0);
+ }
+ }
+ return tell(p, parray[p].opponent, param[0].val.string, TELL_SAY, 0);
+}
+
+PUBLIC int com_set(int p, param_list param)
+{
+ int result;
+ int which;
+ char *val;
+
+ if (param[1].type == TYPE_NULL)
+ val = NULL;
+ else
+ val = param[1].val.string;
+ result = var_set(p, param[0].val.word, val, &which);
+ switch (result) {
+ case VAR_OK:
+ break;
+ case VAR_BADVAL:
+ pprintf(p, "Bad value given for variable %s.\n", param[0].val.word);
+ break;
+ case VAR_NOSUCH:
+ pprintf(p, "No such variable name %s.\n", param[0].val.word);
+ break;
+ case VAR_AMBIGUOUS:
+ pprintf(p, "Ambiguous variable name %s.\n", param[0].val.word);
+ break;
+ }
+ player_save(p);
+ return COM_OK;
+}
+
+PUBLIC int FindPlayer(int p, parameter * param, int *p1, int *connected)
+{
+ if (param->type == TYPE_WORD) {
+ *p1 = player_search(p, param->val.word);
+ if (*p1 == 0)
+ return 0;
+ if (*p1 < 0) { /* player had to be connected and will be
+ removed later */
+ *connected = 0;
+ *p1 = (-*p1) - 1;
+ } else {
+ *connected = 1;
+ *p1 = *p1 - 1;
+ }
+ } else {
+ *p1 = p;
+ *connected = 1;
+ }
+ return 1;
+}
+
+PRIVATE void com_stats_andify(int *numbers, int howmany, char *dest)
+{
+ char tmp[10];
+
+ *dest = '\0';
+ while (howmany--) {
+ sprintf(tmp, "%d", numbers[howmany]);
+ strcat(dest, tmp);
+ if (howmany > 1)
+ sprintf(tmp, ", ");
+ else if (howmany == 1)
+ sprintf(tmp, " and ");
+ else
+ sprintf(tmp, ".\n");
+ strcat(dest, tmp);
+ }
+ return;
+}
+
+PRIVATE void com_stats_rating(char *hdr, statistics * stats, char *dest)
+{
+ char tmp[100];
+
+ sprintf(dest, "%-10s%4s %5.1f %4d %4d %4d %4d",
+ hdr, ratstr(stats->rating), stats->sterr, stats->win, stats->los, stats->dra, stats->num);
+ if (stats->whenbest) {
+ sprintf(tmp, " %d", stats->best);
+ strcat(dest, tmp);
+ strftime(tmp, sizeof(tmp), " (%d-%b-%y)", localtime((time_t *) & stats->whenbest));
+ strcat(dest, tmp);
+ }
+ strcat(dest, "\n");
+ return;
+}
+
+PUBLIC int com_stats(int p, param_list param)
+{
+ int g, i, t;
+ int p1, connected;
+ char line[255], tmp[255];
+ int numbers[MAX_OBSERVE > MAX_SIMUL ? MAX_OBSERVE : MAX_SIMUL];
+
+ if (!FindPlayer(p, &param[0], &p1, &connected))
+ return COM_OK;
+
+ sprintf(line, "\nStatistics for %-11s ", parray[p1].name);
+ if ((connected) && (parray[p1].status == PLAYER_PROMPT)) {
+ sprintf(tmp, "On for: %s", hms(player_ontime(p1), 0, 0, 0));
+ strcat(line, tmp);
+ sprintf(tmp, " Idle: %s\n", hms(player_idle(p1), 0, 0, 0));
+ } else {
+ if ((t = player_lastdisconnect(p1)))
+ sprintf(tmp, "(Last disconnected %s):\n", strltime(&t));
+ else
+ sprintf(tmp, "(Never connected.)\n");
+ }
+ strcat(line, tmp);
+ pprintf(p, "%s", line);
+ if (parray[p1].simul_info.numBoards) {
+ for (i = 0, t = 0; i < parray[p1].simul_info.numBoards; i++) {
+ if ((numbers[t] = parray[p1].simul_info.boards[i] + 1) != 0)
+ t++;
+ }
+ pprintf(p, "%s is giving a simul: game%s ", parray[p1].name, ((t > 1) ? "s" : ""));
+ com_stats_andify(numbers, t, tmp);
+ pprintf(p, tmp);
+ } else if (parray[p1].game >= 0) {
+ g = parray[p1].game;
+ if (garray[g].status == GAME_EXAMINE) {
+ pprintf(p, "(Examining game %d: %s vs. %s)\n", g + 1,
+ parray[garray[g].white].name, parray[garray[g].black].name);
+ } else {
+ pprintf(p, "(playing game %d: %s vs. %s)\n", g + 1,
+ parray[garray[g].white].name, parray[garray[g].black].name);
+ }
+ }
+ if (parray[p1].num_observe) {
+ for (i = 0, t = 0; i < parray[p1].num_observe; i++) {
+ g = parray[p1].observe_list[i];
+ if ((g != -1) && ((parray[p].adminLevel >= ADMIN_ADMIN) || (garray[g].private == 0)))
+ numbers[t++] = g + 1;
+ }
+ if (t) {
+ pprintf(p, "%s is observing game%s ", parray[p1].name, ((t > 1) ? "s" : ""));
+ com_stats_andify(numbers, t, tmp);
+ pprintf(p, tmp);
+ }
+ }
+ if (parray[p1].busy[0]) {
+ pprintf(p, "(%s %s)\n", parray[p1].name, parray[p1].busy);
+ }
+ if (!parray[p1].registered) {
+ pprintf(p, "%s is NOT a registered player.\n\n", parray[p1].name);
+ } else {
+ pprintf(p, "\n rating RD win loss draw total best\n");
+ com_stats_rating("Blitz", &parray[p1].b_stats, tmp);
+ pprintf(p, tmp);
+ com_stats_rating("Standard", &parray[p1].s_stats, tmp);
+ pprintf(p, tmp);
+ com_stats_rating("Wild", &parray[p1].w_stats, tmp);
+ pprintf(p, tmp);
+ }
+ pprintf(p, "\n");
+ if (parray[p1].adminLevel > 0) {
+ pprintf(p, "Admin Level: ");
+ switch (parray[p1].adminLevel) {
+/*
+ case 0:
+ pprintf(p, "Normal User\n");
+ break;
+ case 5:
+ pprintf(p, "Extra Cool User\n"); vek wants to be 5
+ break;
+
+ Forget it - you can do some admin stuff if your level is > than 0 - DAV
+
+*/
+ case 5:
+ pprintf(p, "Authorized Helper Person\n");
+ break;
+ case 10:
+ pprintf(p, "Administrator\n");
+ break;
+ case 15:
+ pprintf(p, "Help File Librarian/Administrator\n");
+ break;
+ case 20:
+ pprintf(p, "Master Administrator\n");
+ break;
+ case 50:
+ pprintf(p, "Master Help File Librarian/Administrator\n");
+ break;
+ case 60:
+ pprintf(p, "Assistant Super User\n");
+ break;
+ case 100:
+ pprintf(p, "Super User\n");
+ break;
+ default:
+ pprintf(p, "%d\n", parray[p1].adminLevel);
+ break;
+ }
+ }
+ if (parray[p].adminLevel > 0)
+ pprintf(p, "Full Name : %s\n", (parray[p1].fullName ? parray[p1].fullName : "(none)"));
+ if (((p1 == p) && (parray[p1].registered)) || (parray[p].adminLevel > 0))
+ pprintf(p, "Address : %s\n", (parray[p1].emailAddress ? parray[p1].emailAddress : "(none)"));
+ if (parray[p].adminLevel > 0) {
+ pprintf(p, "Host : %s\n",
+/*
+ ((hp = gethostbyaddr((const char*) (connected ? &parray[p1].thisHost : &parray[p1].lastHost), sizeof(parray[p1].thisHost), AF_INET)) == 0) ? "" : hp->h_name,
+*/
+ dotQuad(connected ? parray[p1].thisHost : parray[p1].lastHost));
+ }
+ if ((parray[p].adminLevel > 0) && (parray[p1].registered))
+ if (parray[p1].num_comments)
+ pprintf(p, "Comments : %d\n", parray[p1].num_comments);
+
+ if (parray[p1].num_plan) {
+ pprintf(p, "\n");
+ for (i = 0; i < parray[p1].num_plan; i++)
+ pprintf(p, "%2d: %s\n", i + 1, (parray[p1].planLines[i] != NULL) ? parray[p1].planLines[i] : "");
+ }
+ if (!connected)
+ player_remove(p1);
+ return COM_OK;
+}
+
+
+
+PUBLIC int com_variables(int p, param_list param)
+{
+ int p1, connected;
+ int i;
+
+ if (!FindPlayer(p, &param[0], &p1, &connected))
+ return COM_OK;
+
+ pprintf(p, "Variable settings of %s:\n", parray[p1].name);
+/* if (parray[p1].fullName)
+ pprintf(p, " Realname: %s\n", parray[p1].fullName);
+*/
+ if (parray[p1].uscfRating)
+ pprintf(p, " USCF: %d\n", parray[p1].uscfRating);
+ pprintf(p, " time=%-3d inc=%-3d private=%d\n",
+ parray[p1].d_time, parray[p1].d_inc, parray[p1].private);
+ pprintf(p, " rated=%d ropen=%d open=%d simopen=%d\n",
+ parray[p1].rated, parray[p1].ropen, parray[p1].open, parray[p1].sopen);
+ pprintf(p, " shout=%d cshout=%d kib=%d tell=%d notifiedby=%d\n",
+ parray[p1].i_shout, parray[p1].i_cshout, parray[p1].i_kibitz, parray[p1].i_tell, parray[p1].notifiedby);
+ pprintf(p, " pin=%d gin=%d style=%-3d flip=%d\n",
+ parray[p1].i_login, parray[p1].i_game, parray[p1].style + 1, parray[p1].flip);
+ pprintf(p, " highlight=%d bell=%d auto=%d mailmess=%d pgn=%d\n",
+ parray[p1].highlight, parray[p1].bell, parray[p1].automail, parray[p1].i_mailmess, parray[p1].pgn);
+ pprintf(p, " width=%-3d height=%-3d\n",
+ parray[p1].d_width, parray[p1].d_height);
+ if (parray[p1].prompt && parray[p1].prompt != def_prompt)
+ pprintf(p, " Prompt: %s\n", parray[p1].prompt);
+
+ { /* added code to print channels */
+ int count = 0;
+ for (i = 0; i < MAX_CHANNELS; i++) {
+ if (on_channel(i, p1)) {
+ if (!count)
+ pprintf(p, "\n Channels:");
+ pprintf(p, " %d", i);
+ count++;
+ }
+ }
+ if (count)
+ pprintf(p, "\n");
+ }
+/* if (parray[p1].numAlias && (p == p1)) {
+ pprintf(p, "\n Aliases:\n");
+ for (i = 0; i < parray[p1].numAlias; i++) {
+ pprintf(p, " %s %s\n", parray[p1].alias_list[i].comm_name,
+ parray[p1].alias_list[i].alias);
+ }
+ }
+*/
+ if (parray[p1].num_formula) {
+ pprintf(p, "\n");
+ for (i = 0; i < parray[p1].num_formula; i++) {
+ if (parray[p1].formulaLines[i] != NULL)
+ pprintf(p, " f%d: %s\n", i + 1, parray[p1].formulaLines[i]);
+ else
+ pprintf(p, " f%d:\n", i + 1);
+ }
+ }
+ if (parray[p1].formula != NULL)
+ pprintf(p, "\nFormula: %s\n", parray[p1].formula);
+
+ if (!connected)
+ player_remove(p1);
+ return COM_OK;
+}
+
+
+
+PUBLIC int com_password(int p, param_list param)
+{
+ char *oldpassword = param[0].val.word;
+ char *newpassword = param[1].val.word;
+ char salt[3];
+
+ if (!parray[p].registered) {
+ pprintf(p, "Setting a password is only for registered players.\n");
+ return COM_OK;
+ }
+ if (parray[p].passwd) {
+ salt[0] = parray[p].passwd[0];
+ salt[1] = parray[p].passwd[1];
+ salt[2] = '\0';
+ if (strcmp(crypt(oldpassword, salt), parray[p].passwd)) {
+ pprintf(p, "Incorrect password, password not changed!\n");
+ return COM_OK;
+ }
+ rfree(parray[p].passwd);
+ parray[p].passwd = NULL;
+ }
+ salt[0] = 'a' + rand() % 26;
+ salt[1] = 'a' + rand() % 26;
+ salt[2] = '\0';
+ parray[p].passwd = strdup(crypt(newpassword, salt));
+ pprintf(p, "Password changed to \"%s\".\n", newpassword);
+ return COM_OK;
+}
+
+PUBLIC int com_uptime(int p, param_list param)
+{
+ unsigned long uptime = time(0) - startuptime;
+ struct rusage ru;
+ int days = (uptime / (60*60*24));
+ int hours = ((uptime % (60*60*24)) / (60*60));
+ int mins = (((uptime % (60*60*24)) % (60*60)) / 60);
+ int secs = (((uptime % (60*60*24)) % (60*60)) % 60);
+
+ pprintf(p, "Server location: %s Server version : %s\n", fics_hostname,VERS_NUM);
+ pprintf(p, "The server has been up since %s.\n", strltime(&startuptime));
+ if ((days==0) && (hours==0) && (mins==0)) {
+ pprintf(p, "(Up for %d second%s)\n",
+ secs, (secs==1) ? "" : "s");
+ } else if ((days==0) && (hours==0)) {
+ pprintf(p, "(Up for %d minute%s and %d second%s)\n",
+ mins, (mins==1) ? "" : "s",
+ secs, (secs==1) ? "" : "s");
+ } else if (days==0) {
+ pprintf(p, "(Up for %d hour%s, %d minute%s and %d second%s)\n",
+ hours, (hours==1) ? "" : "s",
+ mins, (mins==1) ? "" : "s",
+ secs, (secs==1) ? "" : "s");
+ } else {
+ pprintf(p, "(Up for %d day%s, %d hour%s, %d minute%s and %d second%s)\n",
+ days, (days==1) ? "" : "s",
+ hours, (hours==1) ? "" : "s",
+ mins, (mins==1) ? "" : "s",
+ secs, (secs==1) ? "" : "s");
+ }
+ pprintf(p, "\nAllocs: %u Frees: %u Allocs In Use: %u\n",
+ malloc_count, free_count, malloc_count - free_count);
+ if (parray[p].adminLevel >= ADMIN_ADMIN) {
+ pprintf(p, "\nplayer size:%d, game size:%d, con size:%d, g_num:%d\n",
+ sizeof(player), sizeof(game), net_consize(), g_num);
+ getrusage(RUSAGE_SELF, &ru);
+ pprintf(p, "pagesize = %d, maxrss = %d, total = %d\n", getpagesize(), ru.ru_maxrss, getpagesize() * ru.ru_maxrss);
+ }
+ pprintf(p, "\nPlayer limit: %d\n", max_connections);
+ pprintf(p, "\nThere are currently %d players, with a high of %d since last restart.\n", player_count(), player_high);
+ pprintf(p, "There are currently %d games, with a high of %d since last restart.\n", game_count(), game_high);
+ pprintf(p, "\nCompiled on %s\n", COMP_DATE);
+ return COM_OK;
+}
+
+PUBLIC int com_date(int p, param_list param)
+{
+ int t = time(0);
+ pprintf(p, "Local time - %s\n", strltime(&t));
+ pprintf(p, "Greenwich time - %s\n", strgtime(&t));
+ return COM_OK;
+}
+
+char *inout_string[] = {
+ "login", "logout"
+};
+
+PRIVATE int plogins(p, fname)
+int p;
+char *fname;
+{
+ FILE *fp;
+ int inout, thetime, registered;
+ char loginName[MAX_LOGIN_NAME + 1];
+ char ipstr[20];
+
+ fp = fopen(fname, "r");
+ if (!fp) {
+ pprintf(p, "Sorry, no login information available.\n");
+ return COM_OK;
+ }
+ while (!feof(fp)) {
+ if (fscanf(fp, "%d %s %d %d %s\n", &inout, loginName, &thetime,
+ &registered, ipstr) != 5) {
+ fprintf(stderr, "FICS: Error in login info format. %s\n", fname);
+ fclose(fp);
+ return COM_OK;
+ }
+ pprintf(p, "%s: %-17s %-6s", strltime(&thetime), loginName,
+ inout_string[inout]);
+ if (parray[p].adminLevel > 0) {
+ pprintf(p, " from %s\n", ipstr);
+ } else
+ pprintf(p, "\n");
+ }
+ fclose(fp);
+ return COM_OK;
+}
+
+PUBLIC int com_llogons(int p, param_list param)
+{
+ char fname[MAX_FILENAME_SIZE];
+
+ sprintf(fname, "%s/%s", stats_dir, STATS_LOGONS);
+ return plogins(p, fname);
+}
+
+PUBLIC int com_logons(int p, param_list param)
+{
+ char fname[MAX_FILENAME_SIZE];
+
+ if (param[0].type == TYPE_WORD) {
+ sprintf(fname, "%s/player_data/%c/%s.%s", stats_dir, param[0].val.word[0], param[0].val.word, STATS_LOGONS);
+ } else {
+ sprintf(fname, "%s/player_data/%c/%s.%s", stats_dir, parray[p].login[0], parray[p].login, STATS_LOGONS);
+ }
+ return plogins(p, fname);
+}
+
+#define WHO_OPEN 0x01
+#define WHO_CLOSED 0x02
+#define WHO_RATED 0x04
+#define WHO_UNRATED 0x08
+#define WHO_FREE 0x10
+#define WHO_PLAYING 0x20
+#define WHO_REGISTERED 0x40
+#define WHO_UNREGISTERED 0x80
+
+PRIVATE void who_terse(int p, int num, int *plist, int type)
+{
+ char ptmp[80 + 20]; /* for highlight */
+ multicol *m = multicol_start(PARRAY_SIZE);
+ int i;
+ int p1;
+ int rat;
+
+ /* altered DAV 3/15/95 */
+
+ for (i = 0; i < num; i++) {
+ p1 = plist[i];
+ if (type == blitz_rat)
+ rat = parray[p1].b_stats.rating;
+ if (type == wild_rat)
+ rat = parray[p1].w_stats.rating;
+ if (type == std_rat)
+ rat = parray[p1].s_stats.rating;
+
+ if (type == none) {
+ sprintf(ptmp, " ");
+ } else {
+ sprintf(ptmp, "%-4s", ratstrii(rat, parray[p1].registered));
+ if (parray[p1].simul_info.numBoards) {
+ strcat(ptmp, "~");
+ } else if ((parray[p1].game >= 0) && (garray[parray[p1].game].status == GAME_EXAMINE)) {
+ strcat(ptmp, "#");
+ } else if (parray[p1].game >= 0) {
+ strcat(ptmp, "^");
+ } else if (!parray[p1].open) {
+ strcat(ptmp, ":");
+ } else if (player_idle(p1) > 300) {
+ strcat(ptmp, ".");
+ } else {
+ strcat(ptmp, " ");
+ }
+ }
+ if (p == p1) {
+ psprintf_highlight(p, ptmp + strlen(ptmp), "%s", parray[p1].name);
+ } else {
+ strcat(ptmp, parray[p1].name);
+ }
+ if ((parray[p1].adminLevel >= 10) && (parray[p1].i_admin))
+ strcat(ptmp, "(*)");
+ if (in_list("computer", parray[p1].name))
+ strcat(ptmp, "(C)");
+/* grimm's fishlist
+ if (in_list("fish", parray[p1].name)) strcat(ptmp, "(Fish)");
+*/
+ if (in_list("fm", parray[p1].name))
+ strcat(ptmp, "(FM)");
+ if (in_list("im", parray[p1].name))
+ strcat(ptmp, "(IM)");
+ if (in_list("gm", parray[p1].name))
+ strcat(ptmp, "(GM)");
+ if (in_list("td", parray[p1].name))
+ strcat(ptmp, "(TD)");
+ multicol_store(m, ptmp);
+ }
+ multicol_pprint(m, p, 80, 2);
+ multicol_end(m);
+ pprintf(p, "\n %d Players displayed (of %d). (*) indicates system administrator.\n", num, player_count());
+}
+
+PRIVATE void who_verbose(p, num, plist)
+int p;
+int num;
+int plist[];
+{
+ int i, p1;
+ char playerLine[255], tmp[255]; /* +8 for highlight */
+
+ pprintf(p,
+ " +---------------------------------------------------------------+\n"
+ );
+ pprintf(p,
+ " | User Standard Blitz On for Idle |\n"
+ );
+ pprintf(p,
+ " +---------------------------------------------------------------+\n"
+ );
+
+ for (i = 0; i < num; i++) {
+ p1 = plist[i];
+
+ strcpy(playerLine, " |");
+
+ if (parray[p1].game >= 0)
+ sprintf(tmp, "%3d", parray[p1].game + 1);
+ else
+ sprintf(tmp, " ");
+ strcat(playerLine, tmp);
+
+ if (!parray[p1].open)
+ sprintf(tmp, "X");
+ else
+ sprintf(tmp, " ");
+ strcat(playerLine, tmp);
+
+ if (parray[p1].registered)
+ if (parray[p1].rated) {
+ sprintf(tmp, " ");
+ } else {
+ sprintf(tmp, "u");
+ }
+ else
+ sprintf(tmp, "U");
+ strcat(playerLine, tmp);
+
+ /* Modified by DAV 3/15/95 */
+ if (p == p1) {
+ strcpy(tmp, " ");
+ psprintf_highlight(p, tmp + strlen(tmp), "%-17s", parray[p1].name);
+ } else {
+ sprintf(tmp, " %-17s", parray[p1].name);
+ }
+ strcat(playerLine, tmp);
+
+ sprintf(tmp, " %4s %-4s %5s ",
+ ratstrii(parray[p1].s_stats.rating, parray[p1].registered),
+ ratstrii(parray[p1].b_stats.rating, parray[p1].registered),
+ hms(player_ontime(p1), 0, 0, 0));
+ strcat(playerLine, tmp);
+
+ if (player_idle(p1) >= 60) {
+ sprintf(tmp, "%5s |\n", hms(player_idle(p1), 0, 0, 0));
+ } else {
+ sprintf(tmp, " |\n");
+ }
+ strcat(playerLine, tmp);
+ pprintf(p, "%s", playerLine);
+ }
+
+ pprintf(p,
+ " | |\n"
+ );
+ pprintf(p,
+ " | %3d Players Displayed |\n",
+ num
+ );
+ pprintf(p,
+ " +---------------------------------------------------------------+\n"
+ );
+}
+
+PRIVATE void who_winloss(p, num, plist)
+int p;
+int num;
+int plist[];
+{
+ int i, p1;
+ char playerLine[255], tmp[255]; /* for highlight */
+
+ pprintf(p,
+ "Name Stand win loss draw Blitz win loss draw idle\n"
+ );
+ pprintf(p,
+ "---------------- ----- ------------- ----- ------------- ----\n"
+ );
+
+ for (i = 0; i < num; i++) {
+ p1 = plist[i];
+ if (p1 == p) {
+ psprintf_highlight(p, playerLine, "%-17s", parray[p1].name);
+ } else {
+ sprintf(playerLine, "%-17s", parray[p1].name);
+ }
+ sprintf(tmp, " %4s %4d %4d %4d ",
+ ratstrii(parray[p1].s_stats.rating, parray[p1].registered),
+ (int) parray[p1].s_stats.win,
+ (int) parray[p1].s_stats.los,
+ (int) parray[p1].s_stats.dra);
+ strcat(playerLine, tmp);
+
+ sprintf(tmp, "%4s %4d %4d %4d ",
+ ratstrii(parray[p1].b_stats.rating, parray[p1].registered),
+ (int) parray[p1].b_stats.win,
+ (int) parray[p1].b_stats.los,
+ (int) parray[p1].b_stats.dra);
+ strcat(playerLine, tmp);
+
+ if (player_idle(p1) >= 60) {
+ sprintf(tmp, "%5s\n", hms(player_idle(p1), 0, 0, 0));
+ } else {
+ sprintf(tmp, " \n");
+ }
+ strcat(playerLine, tmp);
+
+ pprintf(p, "%s", playerLine);
+ }
+ pprintf(p, " %3d Players Displayed.\n", num);
+}
+
+PRIVATE int who_ok(p, sel_bits)
+int p;
+unsigned int sel_bits;
+{
+ if (parray[p].status != PLAYER_PROMPT)
+ return 0;
+ if (sel_bits == 0xff)
+ return 1;
+ if (sel_bits & WHO_OPEN)
+ if (!parray[p].open)
+ return 0;
+ if (sel_bits & WHO_CLOSED)
+ if (parray[p].open)
+ return 0;
+ if (sel_bits & WHO_RATED)
+ if (!parray[p].rated)
+ return 0;
+ if (sel_bits & WHO_UNRATED)
+ if (parray[p].rated)
+ return 0;
+ if (sel_bits & WHO_FREE)
+ if (parray[p].game >= 0)
+ return 0;
+ if (sel_bits & WHO_PLAYING)
+ if (parray[p].game < 0)
+ return 0;
+ if (sel_bits & WHO_REGISTERED)
+ if (!parray[p].registered)
+ return 0;
+ if (sel_bits & WHO_UNREGISTERED)
+ if (parray[p].registered)
+ return 0;
+ return 1;
+}
+
+
+PRIVATE int blitz_cmp(const void *pp1, const void *pp2)
+{
+ register int p1 = *(int *) pp1;
+ register int p2 = *(int *) pp2;
+ if (parray[p1].status != PLAYER_PROMPT) {
+ if (parray[p2].status != PLAYER_PROMPT)
+ return 0;
+ else
+ return -1;
+ }
+ if (parray[p2].status != PLAYER_PROMPT)
+ return 1;
+ if (parray[p1].b_stats.rating > parray[p2].b_stats.rating)
+ return -1;
+ if (parray[p1].b_stats.rating < parray[p2].b_stats.rating)
+ return 1;
+ if (parray[p1].registered > parray[p2].registered)
+ return -1;
+ if (parray[p1].registered < parray[p2].registered)
+ return 1;
+ return strcmp(parray[p1].login, parray[p2].login);
+}
+
+PRIVATE int stand_cmp(const void *pp1, const void *pp2)
+{
+ register int p1 = *(int *) pp1;
+ register int p2 = *(int *) pp2;
+ if (parray[p1].status != PLAYER_PROMPT) {
+ if (parray[p2].status != PLAYER_PROMPT)
+ return 0;
+ else
+ return -1;
+ }
+ if (parray[p2].status != PLAYER_PROMPT)
+ return 1;
+ if (parray[p1].s_stats.rating > parray[p2].s_stats.rating)
+ return -1;
+ if (parray[p1].s_stats.rating < parray[p2].s_stats.rating)
+ return 1;
+ if (parray[p1].registered > parray[p2].registered)
+ return -1;
+ if (parray[p1].registered < parray[p2].registered)
+ return 1;
+ return strcmp(parray[p1].login, parray[p2].login);
+}
+
+PRIVATE int wild_cmp(const void *pp1, const void *pp2)
+{
+ register int p1 = *(int *) pp1;
+ register int p2 = *(int *) pp2;
+ if (parray[p1].status != PLAYER_PROMPT) {
+ if (parray[p2].status != PLAYER_PROMPT)
+ return 0;
+ else
+ return -1;
+ }
+ if (parray[p2].status != PLAYER_PROMPT)
+ return 1;
+ if (parray[p1].w_stats.rating > parray[p2].w_stats.rating)
+ return -1;
+ if (parray[p1].w_stats.rating < parray[p2].w_stats.rating)
+ return 1;
+ if (parray[p1].registered > parray[p2].registered)
+ return -1;
+ if (parray[p1].registered < parray[p2].registered)
+ return 1;
+ return strcmp(parray[p1].login, parray[p2].login);
+}
+
+PRIVATE int alpha_cmp(const void *pp1, const void *pp2)
+{
+ register int p1 = *(int *) pp1;
+ register int p2 = *(int *) pp2;
+ if (parray[p1].status != PLAYER_PROMPT) {
+ if (parray[p2].status != PLAYER_PROMPT)
+ return 0;
+ else
+ return -1;
+ }
+ if (parray[p2].status != PLAYER_PROMPT)
+ return 1;
+ return strcmp(parray[p1].login, parray[p2].login);
+}
+
+PUBLIC void sort_players(int players[PARRAY_SIZE],
+ int ((*cmp_func) (const void *, const void *)))
+{
+ int i;
+
+ for (i = 0; i < p_num; i++) {
+ players[i] = i;
+ }
+ qsort(players, p_num, sizeof(int), cmp_func);
+}
+
+/* This is the of the most compliclicated commands in terms of parameters */
+PUBLIC int com_who(int p, param_list param)
+{
+ int style = 0;
+ float stop_perc = 1.0;
+ float start_perc = 0;
+ unsigned int sel_bits = 0xff;
+ int sortlist[PARRAY_SIZE], plist[PARRAY_SIZE];
+ int ((*cmp_func) (const void *, const void *)) = blitz_cmp;
+ int startpoint;
+ int stoppoint;
+ int i, len;
+ int tmpI, tmpJ;
+ char c;
+ int p1, count, num_who;
+ int sort_type = blitz_rat;
+
+ if (param[0].type == TYPE_WORD) {
+ len = strlen(param[0].val.word);
+ for (i = 0; i < len; i++) {
+ c = param[0].val.word[i];
+ if (isdigit(c)) {
+ if (i == 0 || !isdigit(param[0].val.word[i - 1])) {
+ tmpI = c - '0';
+ if (tmpI == 1) {
+ start_perc = 0.0;
+ stop_perc = 0.333333;
+ } else if (tmpI == 2) {
+ start_perc = 0.333333;
+ stop_perc = 0.6666667;
+ } else if (tmpI == 3) {
+ start_perc = 0.6666667;
+ stop_perc = 1.0;
+ } else if ((i == len - 1) || (!isdigit(param[0].val.word[i + 1])))
+ return COM_BADPARAMETERS;
+ } else {
+ tmpI = c - '0';
+ tmpJ = param[0].val.word[i - 1] - '0';
+ if (tmpI == 0)
+ return COM_BADPARAMETERS;
+ if (tmpJ > tmpI)
+ return COM_BADPARAMETERS;
+ start_perc = ((float) tmpJ - 1.0) / (float) tmpI;
+ stop_perc = ((float) tmpJ) / (float) tmpI;
+ }
+ } else {
+ switch (c) {
+ case 'o':
+ if (sel_bits == 0xff)
+ sel_bits = WHO_OPEN;
+ else
+ sel_bits |= WHO_OPEN;
+ break;
+ case 'r':
+ if (sel_bits == 0xff)
+ sel_bits = WHO_RATED;
+ else
+ sel_bits |= WHO_RATED;
+ break;
+ case 'f':
+ if (sel_bits == 0xff)
+ sel_bits = WHO_FREE;
+ else
+ sel_bits |= WHO_FREE;
+ break;
+ case 'a':
+ if (sel_bits == 0xff)
+ sel_bits = WHO_FREE | WHO_OPEN;
+ else
+ sel_bits |= (WHO_FREE | WHO_OPEN);
+ break;
+ case 'R':
+ if (sel_bits == 0xff)
+ sel_bits = WHO_REGISTERED;
+ else
+ sel_bits |= WHO_REGISTERED;
+ break;
+ case 'l': /* Sort order */
+ cmp_func = alpha_cmp;
+ sort_type = none;
+ break;
+ case 'A': /* Sort order */
+ cmp_func = alpha_cmp;
+ break;
+ case 'w': /* Sort order */
+ cmp_func = wild_cmp;
+ sort_type = wild_rat;
+ break;
+ case 's': /* Sort order */
+ cmp_func = stand_cmp;
+ sort_type = std_rat;
+ break;
+ case 'b': /* Sort order */
+ cmp_func = blitz_cmp;
+ sort_type = blitz_rat;
+ break;
+ case 't': /* format */
+ style = 0;
+ break;
+ case 'v': /* format */
+ style = 1;
+ break;
+ case 'n': /* format */
+ style = 2;
+ break;
+ case 'U':
+ if (sel_bits == 0xff)
+ sel_bits = WHO_UNREGISTERED;
+ else
+ sel_bits |= WHO_UNREGISTERED;
+ break;
+ default:
+ return COM_BADPARAMETERS;
+ break;
+ }
+ }
+ }
+ }
+ sort_players(sortlist, cmp_func);
+ count = 0;
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (!who_ok(sortlist[p1], sel_bits))
+ continue;
+ count++;
+ }
+ startpoint = floor((float) count * start_perc);
+ stoppoint = ceil((float) count * stop_perc) - 1;
+ num_who = 0;
+ count = 0;
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (!who_ok(sortlist[p1], sel_bits))
+ continue;
+ if ((count >= startpoint) && (count <= stoppoint)) {
+ plist[num_who++] = sortlist[p1];
+ }
+ count++;
+ }
+ if (num_who == 0) {
+ pprintf(p, "No logged in players match the flags in your who request.\n");
+ return COM_OK;
+ }
+ switch (style) {
+ case 0: /* terse */
+ who_terse(p, num_who, plist, sort_type);
+ break;
+ case 1: /* verbose */
+ who_verbose(p, num_who, plist);
+ break;
+ case 2: /* win-loss */
+ who_winloss(p, num_who, plist);
+ break;
+ default:
+ return COM_BADPARAMETERS;
+ break;
+ }
+ return COM_OK;
+}
+
+
+
+
+PRIVATE int notorcen(int p, param_list param, int *num, int max,
+ char **list, char *listname)
+{
+ int i, p1, connected;
+
+ if (param[0].type != TYPE_WORD) {
+ if (!*num) {
+ pprintf(p, "Your %s list is empty.\n", listname);
+ return COM_OK;
+ } else
+ pprintf(p, "-- Your %s list contains %d names: --", listname, *num);
+ /* New code to print names in columns */
+ {
+ multicol *m = multicol_start(MAX_NOTIFY + 1);
+ for (i = 0; i < *num; i++)
+ multicol_store_sorted(m, list[i]);
+ multicol_pprint(m, p, 78, 2);
+ multicol_end(m);
+ }
+ return COM_OK;
+ }
+ if (*num >= max) {
+ pprintf(p, "Sorry, your %s list is already full.\n", listname);
+ return COM_OK;
+ }
+ if (!FindPlayer(p, &param[0], &p1, &connected))
+ return COM_OK;
+
+ for (i = 0; i < *num; i++) {
+ if (!strcasecmp(list[i], param[0].val.word)) {
+ pprintf(p, "Your %s list already includes %s.\n",
+ listname, parray[p1].name);
+ if (!connected)
+ player_remove(p1);
+ return COM_OK;
+ }
+ }
+ if (p1 == p) {
+ pprintf(p, "You can't %s yourself.\n", listname);
+ return COM_OK;
+ }
+ list[*num] = strdup(parray[p1].name);
+ ++(*num);
+ pprintf(p, "%s is now on your %s list.\n", parray[p1].name, listname);
+ if (!connected)
+ player_remove(p1);
+ return COM_OK;
+}
+
+
+PRIVATE int unnotorcen(int p, param_list param, int *num, int max,
+ char **list, char *listname)
+{
+ char *pname = NULL;
+ int i, j;
+ int unc = 0;
+
+ if (param[0].type == TYPE_WORD) {
+ pname = param[0].val.word;
+ }
+ for (i = 0; i < *num; i++) {
+ if (!pname || !strcasecmp(pname, list[i])) {
+ pprintf(p, "%s is removed from your %s list.\n",
+ list[i], listname);
+ rfree(list[i]);
+ list[i] = NULL;
+ unc++;
+ }
+ }
+ if (unc) {
+ i = 0;
+ j = 0;
+ while (j < *num) {
+ if (list[j] != NULL) {
+ list[i++] = list[j];
+ }
+ j++;
+ }
+ while (i < j) {
+ list[i++] = NULL;
+ }
+ (*num) -= unc;
+ } else {
+ pprintf(p, "No one was removed from your %s list.\n", listname);
+ }
+ return COM_OK;
+}
+
+
+PUBLIC int com_notify(int p, param_list param)
+{
+ return notorcen(p, param, &parray[p].num_notify, MAX_NOTIFY,
+ parray[p].notifyList, "notify");
+}
+
+PUBLIC int com_censor(int p, param_list param)
+{
+ return notorcen(p, param, &parray[p].num_censor, MAX_CENSOR,
+ parray[p].censorList, "censor");
+}
+
+PUBLIC int com_unnotify(int p, param_list param)
+{
+ return unnotorcen(p, param, &parray[p].num_notify, MAX_NOTIFY,
+ parray[p].notifyList, "notify");
+}
+
+PUBLIC int com_uncensor(int p, param_list param)
+{
+ return unnotorcen(p, param, &parray[p].num_censor, MAX_CENSOR,
+ parray[p].censorList, "censor");
+}
+
+
+PUBLIC int com_channel(int p, param_list param)
+{
+ int i, err;
+
+ if (param[0].type == TYPE_NULL) { /* Turn off all channels */
+ for (i = 0; i < MAX_CHANNELS; i++) {
+ if (!channel_remove(i, p))
+ pprintf(p, "Channel %d turned off.\n", i);
+ }
+ } else {
+ i = param[0].val.integer;
+ if ((i == 0) && (parray[p].adminLevel == 0)) {
+ pprintf(p, "Only admins may join channel 0.\n");
+ return COM_OK;
+ }
+ if (i < 0) {
+ pprintf(p, "The lowest channel number is 0.\n");
+ return COM_OK;
+ }
+ if (i >= MAX_CHANNELS) {
+ pprintf(p, "The maximum channel number is %d.\n", MAX_CHANNELS - 1);
+ return COM_OK;
+ }
+ if (on_channel(i, p)) {
+ if (!channel_remove(i, p))
+ pprintf(p, "Channel %d turned off.\n", i);
+ } else {
+ if (!(err = channel_add(i, p)))
+ pprintf(p, "Channel %d turned on.\n", i);
+ else {
+ if (err == 1)
+ pprintf(p, "Channel %d is already full.\n", i);
+ if (err == 2)
+ pprintf(p, "Maximum channel number exceeded.\n");
+ }
+ }
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_inchannel(int p, param_list param)
+{
+ int c1, c2;
+ int i, j, count = 0;
+
+ if (param[0].type == TYPE_NULL) { /* List everyone on every channel */
+ c1 = -1;
+ c2 = -1;
+ } else if (param[1].type == TYPE_NULL) { /* One parameter */
+ c1 = param[0].val.integer;
+ if (c1 < 0) {
+ pprintf(p, "The lowest channel number is 0.\n");
+ return COM_OK;
+ }
+ c2 = -1;
+ } else { /* Two parameters */
+ c1 = param[0].val.integer;
+ c2 = param[2].val.integer;
+ if ((c1 < 0) || (c2 < 0)) {
+ pprintf(p, "The lowest channel number is 0.\n");
+ return COM_OK;
+ }
+ pprintf(p, "Two parameter inchannel is not implemented.\n");
+ return COM_OK;
+ }
+ if ((c1 >= MAX_CHANNELS) || (c2 >= MAX_CHANNELS)) {
+ pprintf(p, "The maximum channel number is %d.\n", MAX_CHANNELS - 1);
+ return COM_OK;
+ }
+ for (i = 0; i < MAX_CHANNELS; i++) {
+ if (numOn[i] && ((c1 < 0) || (i == c1))) {
+ pprintf(p, "Channel %d:", i);
+ for (j = 0; j < numOn[i]; j++) {
+ pprintf(p, " %s", parray[channels[i][j]].name);
+ }
+ count++;
+ pprintf(p, "\n");
+ }
+ }
+ if (!count) {
+ if (c1 < 0)
+ pprintf(p, "No channels in use.\n");
+ else
+ pprintf(p, "Channel not in use.\n");
+ }
+ return COM_OK;
+}
+
+
+PUBLIC int create_new_match(int white_player, int black_player,
+ int wt, int winc, int bt, int binc,
+ int rated, char *category, char *board,
+ int white)
+{
+ int g = game_new(), p;
+ char outStr[1024];
+ int reverse = 0;
+
+ if (g < 0)
+ return COM_FAILED;
+ if (white == 0) {
+ reverse = 1;
+ } else if (white == -1) {
+ if (!bt) {
+ if (parray[white_player].lastColor == parray[black_player].lastColor) {
+ if ((parray[white_player].num_white - parray[white_player].num_black) >
+ (parray[black_player].num_white - parray[black_player].num_black))
+ reverse = 1;
+ } else if (parray[white_player].lastColor == WHITE)
+ reverse = 1;
+ } else
+ reverse = 1; /* Challenger is always white in unbalanced
+ match */
+ }
+ if (reverse) {
+ int tmp = white_player;
+ white_player = black_player;
+ black_player = tmp;
+ }
+ player_remove_request(white_player, black_player, PEND_MATCH);
+ player_remove_request(black_player, white_player, PEND_MATCH);
+ player_remove_request(white_player, black_player, PEND_SIMUL);
+ player_remove_request(black_player, white_player, PEND_SIMUL);
+ player_decline_offers(white_player, -1, PEND_MATCH);
+ player_withdraw_offers(white_player, -1, PEND_MATCH);
+ player_decline_offers(black_player, -1, PEND_MATCH);
+ player_withdraw_offers(black_player, -1, PEND_MATCH);
+ player_withdraw_offers(white_player, -1, PEND_SIMUL);
+ player_withdraw_offers(black_player, -1, PEND_SIMUL);
+
+ wt = wt * 60; /* To Seconds */
+ bt = bt * 60;
+ garray[g].white = white_player;
+ garray[g].black = black_player;
+ strcpy(garray[g].white_name, parray[white_player].name);
+ strcpy(garray[g].black_name, parray[black_player].name);
+ garray[g].status = GAME_ACTIVE;
+ garray[g].type = game_isblitz(wt / 60, winc, bt / 60, binc, category, board);
+ if ((garray[g].type == TYPE_UNTIMED) || (garray[g].type == TYPE_NONSTANDARD))
+ garray[g].rated = 0;
+ else
+ garray[g].rated = rated;
+ garray[g].private = parray[white_player].private ||
+ parray[black_player].private;
+ garray[g].white = white_player;
+ if (garray[g].type == TYPE_BLITZ) {
+ garray[g].white_rating = parray[white_player].b_stats.rating;
+ garray[g].black_rating = parray[black_player].b_stats.rating;
+ } else if (garray[g].type == TYPE_WILD) {
+ garray[g].white_rating = parray[white_player].w_stats.rating;
+ garray[g].black_rating = parray[black_player].w_stats.rating;
+ } else {
+ garray[g].white_rating = parray[white_player].s_stats.rating;
+ garray[g].black_rating = parray[black_player].s_stats.rating;
+ }
+ if (board_init(&garray[g].game_state, category, board)) {
+ pprintf(white_player, "PROBLEM LOADING BOARD. Game Aborted.\n");
+ pprintf(black_player, "PROBLEM LOADING BOARD. Game Aborted.\n");
+ fprintf(stderr, "FICS: PROBLEM LOADING BOARD %s %s. Game Aborted.\n",
+ category, board);
+ }
+ garray[g].game_state.gameNum = g;
+ garray[g].wTime = wt * 10;
+ garray[g].wInitTime = wt * 10;
+ garray[g].wIncrement = winc * 10;
+ if (bt == 0) {
+ garray[g].bTime = wt * 10;
+ } else {
+ garray[g].bTime = bt * 10;
+ }
+ garray[g].bInitTime = bt * 10;
+ garray[g].bIncrement = binc * 10;
+ if (garray[g].game_state.onMove == BLACK) { /* Start with black */
+ garray[g].numHalfMoves = 1;
+ garray[g].moveListSize = 1;
+ garray[g].moveList = (move_t *) rmalloc(sizeof(move_t));
+ garray[g].moveList[0].fromFile = -1;
+ garray[g].moveList[0].fromRank = -1;
+ garray[g].moveList[0].toFile = -1;
+ garray[g].moveList[0].toRank = -1;
+ garray[g].moveList[0].color = WHITE;
+ strcpy(garray[g].moveList[0].moveString, "NONE");
+ strcpy(garray[g].moveList[0].algString, "NONE");
+ } else {
+ garray[g].numHalfMoves = 0;
+ garray[g].moveListSize = 0;
+ garray[g].moveList = NULL;
+ }
+ garray[g].timeOfStart = tenth_secs();
+ garray[g].startTime = tenth_secs();
+ garray[g].lastMoveTime = garray[g].startTime;
+ garray[g].lastDecTime = garray[g].startTime;
+ garray[g].clockStopped = 0;
+ sprintf(outStr, "\n{Game %d (%s vs. %s) Creating %s %s match.}\n",
+ g + 1, parray[white_player].name,
+ parray[black_player].name,
+ rstr[garray[g].rated],
+ bstr[garray[g].type]);
+ pprintf(white_player, "%s", outStr);
+ pprintf(black_player, "%s", outStr);
+
+ for (p = 0; p < p_num; p++) {
+ if ((p == white_player) || (p == black_player))
+ continue;
+ if (parray[p].status != PLAYER_PROMPT)
+ continue;
+ if (!parray[p].i_game)
+ continue;
+ pprintf_prompt(p, "%s", outStr);
+ }
+ parray[white_player].game = g;
+ parray[white_player].opponent = black_player;
+ parray[white_player].side = WHITE;
+ parray[white_player].promote = QUEEN;
+ parray[black_player].game = g;
+ parray[black_player].opponent = white_player;
+ parray[black_player].side = BLACK;
+ parray[black_player].promote = QUEEN;
+ send_boards(g);
+
+ strcpy(garray[g].boardList[garray[g].numHalfMoves], boardToFEN(g));
+
+ return COM_OK;
+}
+
+PRIVATE int accept_match(int p, int p1)
+{
+ int g, adjourned, foo;
+ int wt, winc, bt, binc, rated, white;
+ char *category, *board;
+ pending *pend;
+ char tmp[100];
+
+ unobserveAll(p); /* stop observing when match starts */
+ unobserveAll(p1);
+
+ pend = &parray[p].p_from_list[player_find_pendfrom(p, p1, PEND_MATCH)];
+ wt = pend->param1;
+ winc = pend->param2;
+ bt = pend->param3;
+ binc = pend->param4;
+ rated = pend->param5;
+ category = pend->char1;
+ board = pend->char2;
+ white = (pend->param6 == -1) ? -1 : 1 - pend->param6;
+
+ pprintf(p, "You accept the challenge of %s.\n", parray[p1].name);
+ pprintf(p1, "\n%s accepts your challenge.\n", parray[p].name);
+ player_remove_request(p, p1, -1);
+ player_remove_request(p1, p, -1);
+
+ while ((foo = player_find_pendto(p, -1, -1)) != -1) {
+ foo = parray[p].p_to_list[foo].whoto;
+ pprintf_prompt(foo, "\n%s, who was challenging you, has joined a match with %s.\n", parray[p].name, parray[p1].name);
+ pprintf(p, "Challenge to %s withdrawn.\n", parray[foo].name);
+ player_remove_request(p, foo, -1);
+ }
+
+ while ((foo = player_find_pendto(p1, -1, -1)) != -1) {
+ foo = parray[p1].p_to_list[foo].whoto;
+ pprintf_prompt(foo, "\n%s, who was challenging you, has joined a match with %s.\n", parray[p1].name, parray[p].name);
+ pprintf(p1, "Challenge to %s withdrawn.\n", parray[foo].name);
+ player_remove_request(p1, foo, -1);
+ }
+
+ while ((foo = player_find_pendfrom(p, -1, -1)) != -1) {
+ foo = parray[p].p_from_list[foo].whofrom;
+ pprintf_prompt(foo, "\n%s, whom you were challenging, has joined a match with %s.\n", parray[p].name, parray[p1].name);
+ pprintf(p, "Challenge from %s removed.\n", parray[foo].name);
+ player_remove_request(foo, p, -1);
+ }
+
+ while ((foo = player_find_pendfrom(p1, -1, -1)) != -1) {
+ foo = parray[p1].p_from_list[foo].whofrom;
+ pprintf_prompt(foo, "\n%s, whom you were challenging, has joined a match with %s.\n", parray[p1].name, parray[p].name);
+ pprintf(p1, "Challenge from %s removed.\n", parray[foo].name);
+ player_remove_request(foo, p1, -1);
+ }
+
+ g = game_new();
+ adjourned = 0;
+ if (game_read(g, p, p1) >= 0)
+ adjourned = 1;
+ else if (game_read(g, p1, p) >= 0) {
+ int swap;
+ adjourned = 1;
+ swap = p;
+ p = p1;
+ p1 = swap;
+ }
+ if (!adjourned) { /* no adjourned game, so begin a new game */
+ game_remove(g);
+
+ if (create_new_match(p, p1, wt, winc, bt, binc, rated, category, board, white) != COM_OK) {
+ sprintf(tmp, "There was a problem creating the new match.\n");
+ pprintf(p, tmp);
+ pprintf_prompt(p1, tmp);
+ }
+ } else { /* resume adjourned game */
+ game_delete(p, p1);
+
+ sprintf(tmp, "{Game %d (%s vs. %s) Continuing %s %s match.}\n", g + 1, parray[p].name, parray[p1].name, rstr[garray[g].rated], bstr[garray[g].type]);
+ pprintf(p, tmp);
+ pprintf(p1, tmp);
+
+ garray[g].white = p;
+ garray[g].black = p1;
+ garray[g].status = GAME_ACTIVE;
+ garray[g].startTime = tenth_secs();
+ garray[g].lastMoveTime = garray[g].startTime;
+ garray[g].lastDecTime = garray[g].startTime;
+ parray[p].game = g;
+ parray[p].opponent = p1;
+ parray[p].side = WHITE;
+ parray[p1].game = g;
+ parray[p1].opponent = p;
+ parray[p1].side = BLACK;
+ send_boards(g);
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_match(int p, param_list param)
+{
+ int adjourned; /* adjourned game? */
+ int g; /* more adjourned game junk */
+ int p1;
+ int pendfrom, pendto;
+ int ppend, p1pend;
+ int wt = -1; /* white start time */
+ int winc = -1; /* white increment */
+ int bt = -1; /* black start time */
+ int binc = -1; /* black increment */
+ int rated = -1; /* 1 = rated, 0 = unrated */
+ int white = -1; /* 1 = want white, 0 = want black */
+ char category[100], board[100], parsebuf[100];
+ char strFormula[MAX_STRING_LENGTH];
+ char *val;
+ int type;
+ int confused = 0;
+ char *colorstr[] = {"", "[black] ", "[white] "};
+ char *adjustr[] = {"", " (adjourned)"};
+
+ if ((parray[p].game >= 0) && (garray[parray[p].game].status == GAME_EXAMINE)) {
+ pprintf(p, "You can't challenge while you are examining a game.\n");
+ return COM_OK;
+ }
+ if (parray[p].game >= 0) {
+ pprintf(p, "You can't challenge while you are playing a game.\n");
+ return COM_OK;
+ }
+ stolower(param[0].val.word);
+ p1 = player_find_part_login(param[0].val.word);
+ if (p1 < 0) {
+ pprintf(p, "No user named \"%s\" is logged in.\n", param[0].val.word);
+ return COM_OK;
+ }
+
+ if (p1 == p) { /* Allowing to match yourself to enter analysis mode */
+ pprintf(p, "Starting a game in examine mode.\n");
+ {
+ int g = game_new();
+
+ unobserveAll(p);
+
+ player_decline_offers(p, -1, PEND_MATCH);
+ player_withdraw_offers(p, -1, PEND_MATCH);
+ player_withdraw_offers(p, -1, PEND_SIMUL);
+
+ garray[g].wInitTime = garray[g].wIncrement = 0;
+ garray[g].bInitTime = garray[g].bIncrement = 0;
+ garray[g].timeOfStart = tenth_secs();
+ garray[g].wTime = garray[g].bTime = 0;
+ garray[g].rated = 0;
+ garray[g].clockStopped = 0;
+ garray[g].type = TYPE_UNTIMED;
+ garray[g].white = garray[g].black = p;
+ garray[g].status = GAME_EXAMINE;
+ garray[g].startTime = tenth_secs();
+ garray[g].lastMoveTime = garray[g].startTime;
+ garray[g].lastDecTime = garray[g].startTime;
+
+ parray[p].side = WHITE; /* oh well... */
+ parray[p].game = g;
+
+ category[0]='\0'; board[0]='\0';
+ if (board_init(&garray[g].game_state, category, board)) {
+ pprintf(p, "PROBLEM LOADING BOARD. Game Aborted.\n");
+ fprintf(stderr, "FICS: PROBLEM LOADING BOARD %s %s. Game Aborted.\n",
+ category, board);
+ }
+ garray[g].game_state.gameNum = g;
+ strcpy(garray[g].white_name, parray[p].name);
+ strcpy(garray[g].black_name, parray[p].name);
+ garray[g].white_rating = garray[g].black_rating = parray[p].s_stats.rating;
+
+ send_boards(g);
+
+ strcpy(garray[g].boardList[garray[g].numHalfMoves], boardToFEN(g));
+
+ }
+ return COM_OK;
+ }
+
+ if (parray[p].open == 0) {
+ parray[p].open = 1;
+ pprintf(p, "Setting you open for matches.\n");
+ }
+ if (player_censored(p1, p)) {
+ pprintf(p, "Player \"%s\" is censoring you.\n", parray[p1].name);
+ return COM_OK;
+ }
+ if (player_censored(p, p1)) {
+ pprintf(p, "You are censoring \"%s\".\n", parray[p1].name);
+ return COM_OK;
+ }
+ if (!parray[p1].open) {
+ pprintf(p, "Player \"%s\" is not open to match requests.\n", parray[p1].name);
+ return COM_OK;
+ }
+ if (parray[p1].game >= 0) {
+ pprintf(p, "Player \"%s\" is involved in another game.\n", parray[p1].name);
+ return COM_OK;
+ }
+/* look for an adjourned game between p and p1 */
+ g = game_new();
+ adjourned = ((game_read(g, p, p1) < 0) && (game_read(g, p1, p) < 0)) ? 0 : 1;
+ if (adjourned) {
+ type = garray[g].type;
+ wt = garray[g].wInitTime / 600;
+ bt = garray[g].bInitTime / 600;
+ winc = garray[g].wIncrement / 10;
+ binc = garray[g].bIncrement / 10;
+ rated = garray[g].rated;
+ }
+ game_remove(g);
+
+ pendto = player_find_pendto(p, p1, PEND_MATCH);
+ pendfrom = player_find_pendfrom(p, p1, PEND_MATCH);
+ category[0] = '\0';
+ board[0] = '\0';
+
+ if (!adjourned) {
+ if (param[1].type != TYPE_NULL) {
+ int numba; /* temp for atoi() */
+
+ val = param[1].val.string;
+ while (!confused && (sscanf(val, " %99s", parsebuf) == 1)) {
+ val = eatword(eatwhite(val));
+ if ((category[0] != '\0') && (board[0] == '\0'))
+ strcpy(board, parsebuf);
+ else if (isdigit(*parsebuf)) {
+ if ((numba = atoi(parsebuf)) < 0) {
+ pprintf(p, "You can't specify negative time controls.\n");
+ return COM_OK;
+ } else if (wt == -1) {
+ wt = numba;
+ } else if (winc == -1) {
+ winc = numba;
+ } else if (bt == -1) {
+ bt = numba;
+ } else if (binc == -1) {
+ binc = numba;
+ } else {
+ confused = 1;
+ }
+ } else if (strstr("rated", parsebuf) != NULL) {
+ if (rated == -1)
+ rated = 1;
+ else
+ confused = 1;
+ } else if (strstr("unrated", parsebuf) != NULL) {
+ if (rated == -1)
+ rated = 0;
+ else
+ confused = 1;
+ } else if (strstr("white", parsebuf) != NULL) {
+ if (white == -1)
+ white = 1;
+ else
+ confused = 1;
+ } else if (strstr("black", parsebuf) != NULL) {
+ if (white == -1)
+ white = 0;
+ else
+ confused = 1;
+ } else if (category[0] == '\0')
+ strcpy(category, parsebuf);
+ else
+ confused = 1;
+ }
+ if (confused) {
+ pprintf(p, "Can't interpret %s in match command.\n", parsebuf);
+ return COM_OK;
+ }
+ }
+ rated = ((rated == -1) ? parray[p].rated : rated) && parray[p1].registered && parray[p].registered;
+ if (winc == -1)
+ winc = (wt == -1) ? parray[p].d_inc : 0; /* match 5 == match 5 0 */
+ if (wt == -1)
+ wt = parray[p].d_time;
+ if (bt == -1)
+ bt = 0;
+ if (binc == -1)
+ binc = winc;
+
+ if (category[0] && !board[0]) {
+ pprintf(p, "You must specify a board and a category.\n");
+ return COM_OK;
+ }
+ if (category[0]) {
+ char fname[MAX_FILENAME_SIZE];
+
+ sprintf(fname, "%s/%s/%s", board_dir, category, board);
+ if (!file_exists(fname)) {
+ pprintf(p, "No such category/board: %s/%s\n", category, board);
+ return COM_OK;
+ }
+ }
+ if ((pendfrom < 0) && (parray[p1].ropen == 0) && (rated != parray[p1].rated)) {
+ pprintf(p, "%s only wants to play %s games.\n", parray[p1].name,
+ rstr[parray[p1].rated]);
+ pprintf(p1, "Ignoring %srated match request from %s.\n",
+ (parray[p1].rated ? "un" : ""), parray[p].name);
+ return COM_OK;
+ }
+ type = game_isblitz(wt, winc, bt, binc, category, board);
+ if (rated && (type == TYPE_STAND || type == TYPE_BLITZ || type == TYPE_WILD)) {
+ if (parray[p].network_player == parray[p1].network_player) {
+ rated = 1;
+ } else {
+ pprintf(p, "Network vs. local player forced to not rated\n");
+ rated = 0;
+ }
+ }
+ if (rated && (type == TYPE_NONSTANDARD)) {
+ pprintf(p, "Game is non-standard - reverting to unrated\n");
+ rated = 0;
+ }
+ if (rated && (type == TYPE_UNTIMED)) {
+ pprintf(p, "Game is untimed - reverting to unrated\n");
+ rated = 0;
+ }
+ /* Now check formula. */
+ if ((pendfrom < 0 || param[1].type != TYPE_NULL) &&
+ !GameMatchesFormula(p, p1, wt, winc, bt, binc, rated, type, strFormula)) {
+ pprintf(p, "Match request does not fit formula for %s:\n",
+ parray[p1].name);
+ pprintf(p, "%s's formula: %s\n", parray[p1].name, parray[p1].formula);
+ pprintf(p, "Evaluated: %s\n", strFormula);
+ pprintf_prompt(p1, "Ignoring (formula): %s (%d) %s (%d) %s.\n",
+ parray[p].name,
+ GetRating(&parray[p], type),
+ parray[p1].name,
+ GetRating(&parray[p1], type),
+ game_str(rated, wt * 60, winc, bt * 60, binc, category, board));
+ return COM_OK;
+ }
+ /* Ok match offer will be made */
+
+ } /* adjourned games shouldn't have to worry
+ about that junk? */
+ if (pendto >= 0) {
+ pprintf(p, "Updating offer already made to \"%s\".\n", parray[p1].name);
+ }
+ if (pendfrom >= 0) {
+ if (pendto >= 0) {
+ pprintf(p, "Internal error\n");
+ fprintf(stderr, "FICS: This shouldn't happen. You can't have a match pending from and to the same person.\n");
+ return COM_OK;
+ }
+ if (adjourned || ((wt == parray[p].p_from_list[pendfrom].param1) &&
+ (winc == parray[p].p_from_list[pendfrom].param2) &&
+ (bt == parray[p].p_from_list[pendfrom].param3) &&
+ (binc == parray[p].p_from_list[pendfrom].param4) &&
+ (rated == parray[p].p_from_list[pendfrom].param5) &&
+ ((white == -1) || (white + parray[p].p_from_list[pendfrom].param6 == 1)) &&
+ (!strcmp(category, parray[p].p_from_list[pendfrom].char1)) &&
+ (!strcmp(board, parray[p].p_from_list[pendfrom].char2)))) {
+ /* Identical match, should accept! */
+ accept_match(p, p1);
+ return COM_OK;
+ } else {
+ player_remove_pendfrom(p, p1, PEND_MATCH);
+ player_remove_pendto(p1, p, PEND_MATCH);
+ }
+ }
+ if (pendto < 0) {
+ ppend = player_new_pendto(p);
+ if (ppend < 0) {
+ pprintf(p, "Sorry you can't have any more pending matches.\n");
+ return COM_OK;
+ }
+ p1pend = player_new_pendfrom(p1);
+ if (p1pend < 0) {
+ pprintf(p, "Sorry %s can't have any more pending matches.\n", parray[p1].name);
+ parray[p].num_to = parray[p].num_to - 1;
+ return COM_OK;
+ }
+ } else {
+ ppend = pendto;
+ p1pend = player_find_pendfrom(p1, p, PEND_MATCH);
+ }
+ parray[p].p_to_list[ppend].param1 = wt;
+ parray[p].p_to_list[ppend].param2 = winc;
+ parray[p].p_to_list[ppend].param3 = bt;
+ parray[p].p_to_list[ppend].param4 = binc;
+ parray[p].p_to_list[ppend].param5 = rated;
+ parray[p].p_to_list[ppend].param6 = white;
+ strcpy(parray[p].p_to_list[ppend].char1, category);
+ strcpy(parray[p].p_to_list[ppend].char2, board);
+ parray[p].p_to_list[ppend].type = PEND_MATCH;
+ parray[p].p_to_list[ppend].whoto = p1;
+ parray[p].p_to_list[ppend].whofrom = p;
+
+ parray[p1].p_from_list[p1pend].param1 = wt;
+ parray[p1].p_from_list[p1pend].param2 = winc;
+ parray[p1].p_from_list[p1pend].param3 = bt;
+ parray[p1].p_from_list[p1pend].param4 = binc;
+ parray[p1].p_from_list[p1pend].param5 = rated;
+ parray[p1].p_from_list[p1pend].param6 = white;
+ strcpy(parray[p1].p_from_list[p1pend].char1, category);
+ strcpy(parray[p1].p_from_list[p1pend].char2, board);
+ parray[p1].p_from_list[p1pend].type = PEND_MATCH;
+ parray[p1].p_from_list[p1pend].whoto = p1;
+ parray[p1].p_from_list[p1pend].whofrom = p;
+
+ if (pendfrom >= 0) {
+ pprintf(p, "Declining offer from %s and offering new match parameters.\n", parray[p1].name);
+ pprintf(p1, "\n%s declines your match offer a match with these parameters:", parray[p].name);
+ }
+ if (pendto >= 0) {
+ pprintf(p, "Updating match request to: ");
+ pprintf(p1, "\n%s updates the match request.\n", parray[p].name);
+ } else {
+ pprintf(p, "Issuing: ");
+ pprintf(p1, "\n", parray[p].name);
+ }
+
+ pprintf(p, "%s (%s) %s", parray[p].name,
+ ratstrii(GetRating(&parray[p], type), parray[p].registered),
+ colorstr[white + 1]);
+ pprintf_highlight(p, "%s", parray[p1].name);
+ pprintf(p, " (%s) %s%s.\n",
+ ratstrii(GetRating(&parray[p1], type), parray[p1].registered),
+ game_str(rated, wt * 60, winc, bt * 60, binc, category, board),
+ adjustr[adjourned]);
+ pprintf(p1, "Challenge: ");
+ pprintf_highlight(p1, "%s", parray[p].name);
+ pprintf(p1, " (%s) %s", ratstrii(GetRating(&parray[p], type), parray[p].registered), colorstr[white + 1]);
+ pprintf(p1, "%s (%s) %s%s.\n", parray[p1].name,
+ ratstrii(GetRating(&parray[p1], type), parray[p1].registered),
+ game_str(rated, wt * 60, winc, bt * 60, binc, category, board),
+ adjustr[adjourned]);
+ if (parray[p1].bell == 1)
+ pprintf_noformat(p1, "\007");
+ if (in_list("computer", parray[p].name)) {
+ pprintf(p1, "--** %s is a ", parray[p].name);
+ pprintf_highlight(p1, "computer");
+ pprintf(p1, " **--\n");
+ }
+ if (in_list("computer", parray[p1].name)) {
+ pprintf(p, "--** %s is a ", parray[p1].name);
+ pprintf_highlight(p, "computer");
+ pprintf(p, " **--\n");
+ }
+ if (in_list("abuser", parray[p].name)) {
+ pprintf(p1, "--** %s is in the ", parray[p].name);
+ pprintf_highlight(p1, "abuser");
+ pprintf(p1, " list **--\n");
+ }
+ if (in_list("abuser", parray[p1].name)) {
+ pprintf(p, "--** %s is in the ", parray[p1].name);
+ pprintf_highlight(p, "abuser");
+ pprintf(p, " list **--\n");
+ }
+ if (rated) {
+ int win, draw, loss;
+ double newsterr;
+
+ rating_sterr_delta(p1, p, type, time(0), RESULT_WIN, &win, &newsterr);
+ rating_sterr_delta(p1, p, type, time(0), RESULT_DRAW, &draw, &newsterr);
+ rating_sterr_delta(p1, p, type, time(0), RESULT_LOSS, &loss, &newsterr);
+ pprintf(p1, "Your %s rating will change: Win: %s%d, Draw: %s%d, Loss: %s%d\n",
+ bstr[type],
+ (win >= 0) ? "+" : "", win,
+ (draw >= 0) ? "+" : "", draw,
+ (loss >= 0) ? "+" : "", loss);
+ pprintf(p1, "Your new RD will be %5.1f\n", newsterr);
+
+ rating_sterr_delta(p, p1, type, time(0), RESULT_WIN, &win, &newsterr);
+ rating_sterr_delta(p, p1, type, time(0), RESULT_DRAW, &draw, &newsterr);
+ rating_sterr_delta(p, p1, type, time(0), RESULT_LOSS, &loss, &newsterr);
+ pprintf(p, "Your %s rating will change: Win: %s%d, Draw: %s%d, Loss: %s%d\n",
+ bstr[type],
+ (win >= 0) ? "+" : "", win,
+ (draw >= 0) ? "+" : "", draw,
+ (loss >= 0) ? "+" : "", loss);
+ pprintf(p, "Your new RD will be %5.1f\n", newsterr);
+ }
+ pprintf_prompt(p1, "You can \"accept\" or \"decline\", or propose different parameters.\n");
+ return COM_OK;
+}
+
+PUBLIC int com_accept(int p, param_list param)
+{
+ int acceptNum = -1;
+ int type = -1;
+ int p1;
+
+ if (parray[p].num_from == 0) {
+ pprintf(p, "You have no offers to accept.\n");
+ return COM_OK;
+ }
+ if (param[0].type == TYPE_NULL) {
+ if (parray[p].num_from != 1) {
+ pprintf(p, "You have more than one offer to accept.\nUse \"pending\" to see them and \"accept n\" to choose which one.\n");
+ return COM_OK;
+ }
+ acceptNum = 0;
+ } else if (param[0].type == TYPE_INT) {
+ if ((param[0].val.integer < 1) || (param[0].val.integer > parray[p].num_from)) {
+ pprintf(p, "Out of range. Use \"pending\" to see the list of offers.\n");
+ return COM_OK;
+ }
+ acceptNum = param[0].val.integer - 1;
+ } else if (param[0].type == TYPE_WORD) {
+ if (!strcmp(param[0].val.word, "draw")) {
+ type = PEND_DRAW;
+ } else if (!strcmp(param[0].val.word, "pause")) {
+ type = PEND_PAUSE;
+ } else if (!strcmp(param[0].val.word, "adjourn")) {
+ type = PEND_ADJOURN;
+ } else if (!strcmp(param[0].val.word, "abort")) {
+ type = PEND_ABORT;
+ } else if (!strcmp(param[0].val.word, "takeback")) {
+ type = PEND_TAKEBACK;
+ }
+ if (!strcmp(param[0].val.word, "simmatch")) {
+ type = PEND_SIMUL;
+ }
+ if (!strcmp(param[0].val.word, "switch")) {
+ type = PEND_SWITCH;
+ }
+ if (!strcmp(param[0].val.word, "all")) {
+ while (parray[p].num_from != 0) {
+ pcommand(p, "accept 1");
+ }
+ return COM_OK;
+ }
+ if (type > 0) {
+ if ((acceptNum = player_find_pendfrom(p, -1, type)) < 0) {
+ pprintf(p, "There are no pending %s offers.\n", param[0].val.word);
+ return COM_OK;
+ }
+ } else { /* Word must be a name */
+ p1 = player_find_part_login(param[0].val.word);
+ if (p1 < 0) {
+ pprintf(p, "No user named \"%s\" is logged in.\n", param[0].val.word);
+ return COM_OK;
+ }
+ if ((acceptNum = player_find_pendfrom(p, p1, -1)) < 0) {
+ pprintf(p, "There are no pending offers from %s.\n", parray[p1].name);
+ return COM_OK;
+ }
+ }
+ }
+ switch (parray[p].p_from_list[acceptNum].type) {
+ case PEND_MATCH:
+ accept_match(p, parray[p].p_from_list[acceptNum].whofrom);
+ return (COM_OK);
+ break;
+ case PEND_DRAW:
+ pcommand(p, "draw");
+ break;
+ case PEND_PAUSE:
+ pcommand(p, "pause");
+ break;
+ case PEND_ABORT:
+ pcommand(p, "abort");
+ break;
+ case PEND_TAKEBACK:
+ pcommand(p, "takeback %d", parray[p].p_from_list[acceptNum].param1);
+ break;
+ case PEND_SIMUL:
+ pcommand(p, "simmatch %s",
+ parray[parray[p].p_from_list[acceptNum].whofrom].name);
+ break;
+ case PEND_SWITCH:
+ pcommand(p, "switch");
+ break;
+ case PEND_ADJOURN:
+ pcommand(p, "adjourn");
+ break;
+ }
+ return COM_OK_NOPROMPT;
+}
+
+
+
+PUBLIC int com_decline(int p, param_list param)
+{
+ int declineNum;
+ int p1 = -1, type = -1;
+ int count;
+
+ if (parray[p].num_from == 0) {
+ pprintf(p, "You have no pending offers from other players.\n");
+ return COM_OK;
+ }
+ if (param[0].type == TYPE_NULL) {
+ if (parray[p].num_from == 1) {
+ p1 = parray[p].p_from_list[0].whofrom;
+ type = parray[p].p_from_list[0].type;
+ } else {
+ pprintf(p, "You have more than one pending offer. Please specify which one\nyou wish to decline.\n'Pending' will give you the list.\n");
+ return COM_OK;
+ }
+ } else {
+ if (param[0].type == TYPE_WORD) {
+ /* Draw adjourn match takeback abort or <name> */
+ if (!strcmp(param[0].val.word, "match")) {
+ type = PEND_MATCH;
+ } else if (!strcmp(param[0].val.word, "draw")) {
+ type = PEND_DRAW;
+ } else if (!strcmp(param[0].val.word, "pause")) {
+ type = PEND_PAUSE;
+ } else if (!strcmp(param[0].val.word, "abort")) {
+ type = PEND_ABORT;
+ } else if (!strcmp(param[0].val.word, "takeback")) {
+ type = PEND_TAKEBACK;
+ } else if (!strcmp(param[0].val.word, "adjourn")) {
+ type = PEND_ADJOURN;
+ } else if (!strcmp(param[0].val.word, "switch")) {
+ type = PEND_SWITCH;
+ } else if (!strcmp(param[0].val.word, "simul")) {
+ type = PEND_SIMUL;
+ } else if (!strcmp(param[0].val.word, "all")) {
+ } else {
+ p1 = player_find_part_login(param[0].val.word);
+ if (p1 < 0) {
+ pprintf(p, "No user named \"%s\" is logged in.\n", param[0].val.word);
+ return COM_OK;
+ }
+ }
+ } else { /* Must be an integer */
+ declineNum = param[0].val.integer - 1;
+ if (declineNum >= parray[p].num_from || declineNum < 0) {
+ pprintf(p, "Invalid offer number. Must be between 1 and %d.\n", parray[p].num_from);
+ return COM_OK;
+ }
+ p1 = parray[p].p_from_list[declineNum].whofrom;
+ type = parray[p].p_from_list[declineNum].type;
+ }
+ }
+ count = player_decline_offers(p, p1, type);
+ if (count != 1)
+ pprintf(p, "%d offers declined\n", count);
+ return COM_OK;
+}
+
+PUBLIC int com_withdraw(int p, param_list param)
+{
+ int withdrawNum;
+ int p1 = -1, type = -1;
+ int count;
+
+ if (parray[p].num_to == 0) {
+ pprintf(p, "You have no pending offers to other players.\n");
+ return COM_OK;
+ }
+ if (param[0].type == TYPE_NULL) {
+ if (parray[p].num_to == 1) {
+ p1 = parray[p].p_to_list[0].whoto;
+ type = parray[p].p_to_list[0].type;
+ } else {
+ pprintf(p, "You have more than one pending offer. Please specify which one\nyou wish to withdraw.\n'Pending' will give you the list.\n");
+ return COM_OK;
+ }
+ } else {
+ if (param[0].type == TYPE_WORD) {
+ /* Draw adjourn match takeback abort or <name> */
+ if (!strcmp(param[0].val.word, "match")) {
+ type = PEND_MATCH;
+ } else if (!strcmp(param[0].val.word, "draw")) {
+ type = PEND_DRAW;
+ } else if (!strcmp(param[0].val.word, "pause")) {
+ type = PEND_PAUSE;
+ } else if (!strcmp(param[0].val.word, "abort")) {
+ type = PEND_ABORT;
+ } else if (!strcmp(param[0].val.word, "takeback")) {
+ type = PEND_TAKEBACK;
+ } else if (!strcmp(param[0].val.word, "adjourn")) {
+ type = PEND_ADJOURN;
+ } else if (!strcmp(param[0].val.word, "switch")) {
+ type = PEND_SWITCH;
+ } else if (!strcmp(param[0].val.word, "simul")) {
+ type = PEND_SIMUL;
+ } else if (!strcmp(param[0].val.word, "all")) {
+ } else {
+ p1 = player_find_part_login(param[0].val.word);
+ if (p1 < 0) {
+ pprintf(p, "No user named \"%s\" is logged in.\n", param[0].val.word);
+ return COM_OK;
+ }
+ }
+ } else { /* Must be an integer */
+ withdrawNum = param[0].val.integer - 1;
+ if (withdrawNum >= parray[p].num_to || withdrawNum < 0) {
+ pprintf(p, "Invalid offer number. Must be between 1 and %d.\n", parray[p].num_to);
+ return COM_OK;
+ }
+ p1 = parray[p].p_to_list[withdrawNum].whoto;
+ type = parray[p].p_to_list[withdrawNum].type;
+ }
+ }
+ count = player_withdraw_offers(p, p1, type);
+ if (count != 1)
+ pprintf(p, "%d offers withdrawn\n", count);
+ return COM_OK;
+}
+
+PUBLIC int com_pending(int p, param_list param)
+{
+ int i;
+
+ if (!parray[p].num_to) {
+ pprintf(p, "There are no offers pending TO other players.\n");
+ } else {
+ pprintf(p, "Offers TO other players:\n");
+ for (i = 0; i < parray[p].num_to; i++) {
+ pprintf(p, " ");
+ player_pend_print(p, &parray[p].p_to_list[i]);
+ }
+ }
+ if (!parray[p].num_from) {
+ pprintf(p, "\nThere are no offers pending FROM other players.\n");
+ } else {
+ pprintf(p, "\nOffers FROM other players:\n");
+ for (i = 0; i < parray[p].num_from; i++) {
+ pprintf(p, " %d: ", i + 1);
+ player_pend_print(p, &parray[p].p_from_list[i]);
+ }
+ pprintf(p, "\nIf you wish to accept any of these offers type 'accept n'\nor just 'accept' if there is only one offer.\n");
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_refresh(int p, param_list param)
+{
+ int g, p1;
+
+ if (param[0].type == TYPE_NULL) {
+ if (parray[p].game >= 0) {
+ send_board_to(parray[p].game, p);
+ } else { /* Do observing in here */
+ if (parray[p].num_observe) {
+ for (g = 0; g < parray[p].num_observe; g++) {
+ send_board_to(parray[p].observe_list[g], p);
+ }
+ } else {
+ pprintf(p, "You are neither playing nor observing a game.\n");
+ return COM_OK;
+ }
+ }
+ } else {
+ g = GameNumFromParam (p, &p1, &param[0]);
+ if (g < 0)
+ return COM_OK;
+ if ((g >= g_num) || ((garray[g].status != GAME_ACTIVE)
+ && (garray[g].status != GAME_EXAMINE))) {
+ pprintf(p, "No such game.\n");
+ } else if (garray[g].private && parray[p].adminLevel==ADMIN_USER) {
+ pprintf (p, "Sorry, game %d is a private game.\n", g+1);
+ } else {
+ if (garray[g].private)
+ pprintf(p, "Refreshing PRIVATE game %d\n", g+1);
+ send_board_to(g, p);
+ }
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_open(int p, param_list param)
+{
+ int retval;
+ ASSERT(param[0].type == TYPE_NULL);
+ if ((retval = pcommand(p, "set open")) != COM_OK)
+ return retval;
+ else
+ return COM_OK_NOPROMPT;
+}
+
+PUBLIC int com_simopen(int p, param_list param)
+{
+ int retval;
+ ASSERT(param[0].type == TYPE_NULL);
+ if ((retval = pcommand(p, "set simopen")) != COM_OK)
+ return retval;
+ else
+ return COM_OK_NOPROMPT;
+}
+
+PUBLIC int com_bell(int p, param_list param)
+{
+ int retval;
+ ASSERT(param[0].type == TYPE_NULL);
+ if ((retval = pcommand(p, "set bell")) != COM_OK)
+ return retval;
+ else
+ return COM_OK_NOPROMPT;
+}
+
+PUBLIC int com_flip(int p, param_list param)
+{
+ int retval;
+ ASSERT(param[0].type == TYPE_NULL);
+ if ((retval = pcommand(p, "set flip")) != COM_OK)
+ return retval;
+ else
+ return COM_OK_NOPROMPT;
+}
+
+PUBLIC int com_highlight(int p, param_list param)
+{
+ pprintf(p, "Obsolete command. Please do set highlight <0-15>.\n");
+ return COM_OK;
+}
+
+PUBLIC int com_style(int p, param_list param)
+{
+ int retval;
+ ASSERT(param[0].type == TYPE_INT);
+ if ((retval = pcommand(p, "set style %d", param[0].val.integer)) != COM_OK)
+ return retval;
+ else
+ return COM_OK_NOPROMPT;
+}
+
+PUBLIC int com_promote(int p, param_list param)
+{
+ int retval;
+ ASSERT(param[0].type == TYPE_WORD);
+ if ((retval = pcommand(p, "set promote %s", param[0].val.word)) != COM_OK)
+ return retval;
+ else
+ return COM_OK_NOPROMPT;
+}
+
+PUBLIC int com_alias(int p, param_list param)
+{
+ int al;
+
+ if (param[0].type == TYPE_NULL) {
+ for (al = 0; al < parray[p].numAlias; al++) {
+ pprintf(p, "%s -> %s\n", parray[p].alias_list[al].comm_name,
+ parray[p].alias_list[al].alias);
+ }
+ return COM_OK;
+ }
+ al = alias_lookup(param[0].val.word, parray[p].alias_list, parray[p].numAlias);
+ if (param[1].type == TYPE_NULL) {
+ if (al < 0) {
+ pprintf(p, "You have no alias named '%s'.\n", param[0].val.word);
+ } else {
+ pprintf(p, "%s -> %s\n", parray[p].alias_list[al].comm_name,
+ parray[p].alias_list[al].alias);
+ }
+ } else {
+ if (al < 0) {
+ if (parray[p].numAlias >= MAX_ALIASES - 1) {
+ pprintf(p, "You have your maximum of %d aliases.\n", MAX_ALIASES - 1);
+ } else {
+
+ if (!strcmp(param[0].val.string, "quit")) { /* making sure they
+ can't alias quit */
+ pprintf(p, "You can't alias this command.\n");
+ } else if (!strcmp(param[0].val.string, "unalias")) { /* making sure they
+ can't alias unalias
+ :) */
+ pprintf(p, "You can't alias this command.\n");
+ } else {
+ parray[p].alias_list[parray[p].numAlias].comm_name =
+ strdup(param[0].val.word);
+ parray[p].alias_list[parray[p].numAlias].alias =
+ strdup(param[1].val.string);
+ parray[p].numAlias++;
+ pprintf(p, "Alias set.\n");
+
+ }
+ }
+ } else {
+ rfree(parray[p].alias_list[al].alias);
+ parray[p].alias_list[al].alias = strdup(param[1].val.string);
+ pprintf(p, "Alias replaced.\n");
+ }
+ parray[p].alias_list[parray[p].numAlias].comm_name = NULL;
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_unalias(int p, param_list param)
+{
+ int al;
+ int i;
+
+ ASSERT(param[0].type == TYPE_WORD);
+ al = alias_lookup(param[0].val.word, parray[p].alias_list, parray[p].numAlias);
+ if (al < 0) {
+ pprintf(p, "You have no alias named '%s'.\n", param[0].val.word);
+ } else {
+ rfree(parray[p].alias_list[al].comm_name);
+ rfree(parray[p].alias_list[al].alias);
+ for (i = al; i < parray[p].numAlias; i++) {
+ parray[p].alias_list[i].comm_name = parray[p].alias_list[i + 1].comm_name;
+ parray[p].alias_list[i].alias = parray[p].alias_list[i + 1].alias;
+ }
+ parray[p].numAlias--;
+ parray[p].alias_list[parray[p].numAlias].comm_name = NULL;
+ pprintf(p, "Alias removed.\n");
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_servers(int p, param_list param)
+{
+/*
+ int i;
+
+ ASSERT(param[0].type == TYPE_NULL);
+ if (numServers == 0) {
+ */ pprintf(p, "There are no other servers known to this server.\n");
+ return COM_OK;
+}
+ /* pprintf(p, "There are %d known servers.\n", numServers); pprintf(p, "(Not
+ all of these may be active)\n"); pprintf(p, "%-30s%-7s\n", "HOST",
+ "PORT"); for (i = 0; i < numServers; i++) pprintf(p, "%-30s%-7d\n",
+ serverNames[i], serverPorts[i]); return COM_OK; } */
+PUBLIC int com_sendmessage(int p, param_list param)
+{
+ int p1, connected = 1;
+
+ if (!parray[p].registered) {
+ pprintf(p, "You are not registered and cannot send messages.\n");
+ return COM_OK;
+ }
+ if ((param[0].type == TYPE_NULL) || (param[1].type == TYPE_NULL)) {
+ pprintf(p, "No message sent.\n");
+ return COM_OK;
+ }
+ if (!FindPlayer(p, &param[0], &p1, &connected))
+ return COM_OK;
+
+ if ((player_censored(p1, p)) && (parray[p].adminLevel == 0)) {
+ pprintf(p, "Player \"%s\" is censoring you.\n", parray[p1].name);
+ return COM_OK;
+ }
+ if (player_add_message(p1, p, param[1].val.string)) {
+ pprintf(p, "Couldn't send message to %s. Message buffer full.\n",
+ parray[p1].name);
+ } else {
+ if (connected)
+ pprintf_prompt(p1, "\n%s just sent you a message.\n", parray[p].name);
+ }
+ if (!connected)
+ player_remove(p1);
+ return COM_OK;
+}
+
+
+PUBLIC int com_messages(int p, param_list param)
+{
+ if (param[0].type != TYPE_NULL) {
+ if (param[1].type != TYPE_NULL)
+ return com_sendmessage(p, param);
+ else {
+ ShowMsgsBySender(p, param);
+ return COM_OK;
+ }
+ }
+ if (player_num_messages(p) <= 0) {
+ pprintf(p, "You have no messages.\n");
+ return COM_OK;
+ }
+ pprintf(p, "Messages:\n");
+ player_show_messages(p);
+ return COM_OK;
+}
+
+PUBLIC int com_clearmessages(int p, param_list param)
+{
+ if (player_num_messages(p) <= 0) {
+ pprintf(p, "You have no messages.\n");
+ return COM_OK;
+ }
+ if (param[0].type == TYPE_NULL) {
+ pprintf(p, "Messages cleared.\n");
+ player_clear_messages(p);
+ return COM_OK;
+ }
+ ClearMsgsBySender(p, param);
+ return COM_OK;
+}
+
+PUBLIC int com_help(int p, param_list param)
+{
+ int i;
+ static char nullify = '\0';
+ char *iwant, *filenames[1000]; /* enough for all helpfile names */
+
+ if (param[0].type == TYPE_NULL) {
+ iwant = &nullify;
+ } else {
+ iwant = param[0].val.word;
+ if (!safestring(iwant)) {
+ pprintf(p, "Illegal character in command %s.\n", iwant);
+ return COM_OK;
+#if 0
+ } else {
+ char sublist[][8] = {"talk", "play", ""};
+
+ for (i=0; (sublist[i][0] != '\0') && strcmp(iwant, sublist[i]); i++);
+ if (sublist[i][0] != '\0') {
+ char searchdir[MAX_FILENAME_SIZE];
+ pprintf(p, "The following \"%s\" commands are available.\n\n",
+ sublist[i]);
+ sprintf(searchdir, "%s/../%s_help", comhelp_dir, sublist[i]);
+ i = search_directory(searchdir, NULL, filenames, 1000);
+ display_directory(p, filenames, i);
+ pprintf(p, "\nType \"help [command]\" for more specific help.\n");
+ return COM_OK;
+ }
+#endif
+ }
+ }
+
+ i = search_directory((*iwant) ? help_dir : comhelp_dir, iwant, filenames, 1000);
+ if (i == 0) {
+ pprintf(p, "No help available on \"%s\".\n", iwant);
+ } else if ((i == 1) || !strcmp(*filenames, iwant)) {
+ if (psend_file(p, help_dir, *filenames)) {
+ /* we should never reach this unless the file was just deleted */
+ pprintf(p, "Helpfile %s could not be found! ", *filenames);
+ pprintf(p, "Please inform an admin of this. Thank you.\n");
+ }
+ } else {
+ if (*iwant)
+ pprintf(p, "Matches:");
+ display_directory(p, filenames, i);
+ pprintf(p, "[Type \"info\" for a list of FICS general information files.]\n");
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_info(int p, param_list param)
+{
+ int n;
+ char *filenames[1000];
+
+ if ((n = search_directory(info_dir, NULL, filenames, 1000)) > 0)
+ display_directory(p, filenames, n);
+ return COM_OK;
+}
+
+PUBLIC int com_adhelp(int p, param_list param)
+{
+ int i;
+ static char nullify = '\0';
+ char *iwant, *filenames[1000]; /* enough for all helpfile names */
+
+ if (param[0].type == TYPE_NULL) {
+ iwant = &nullify;
+ } else {
+ iwant = param[0].val.word;
+ if (!safestring(iwant)) {
+ pprintf(p, "Illegal character in command %s.\n", iwant);
+ return COM_OK;
+ }
+ }
+
+ i = search_directory(adhelp_dir, iwant, filenames, 1000);
+ if (i == 0) {
+ pprintf(p, "No help available on \"%s\".\n", iwant);
+ } else if ((i == 1) || !strcmp(*filenames, iwant)) {
+ if (psend_file(p, adhelp_dir, *filenames)) {
+ /* we should never reach this unless the file was just deleted */
+ pprintf(p, "Helpfile %s could not be found! ", *filenames);
+ pprintf(p, "Please inform an admin of this. Thank you.\n");
+ }
+ } else {
+ if (*iwant)
+ pprintf(p, "Matches:\n");
+ display_directory(p, filenames, i);
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_mailsource(int p, param_list param)
+{
+ static char nullify = '\0';
+ char *iwant, *buffer[1000];
+/* char command[MAX_FILENAME_SIZE]; */
+ char subj[81], fname[MAX_FILENAME_SIZE];
+ int count;
+
+ if (!parray[p].registered) {
+ pprintf(p, "Only registered people can use the mailsource command.\n");
+ return COM_OK;
+ }
+
+ if (param[0].type == TYPE_NULL) {
+ iwant = &nullify;
+ } else {
+ iwant = param[0].val.word;
+ }
+
+ count = search_directory(source_dir, iwant, buffer, 1000);
+ if (count == 0) {
+ pprintf(p, "Found no source file matching \"%s\".\n", iwant);
+ } else if ((count == 1) || !strcmp(iwant, *buffer)) {
+ /* sprintf(command, "%s -s\"FICS sourcefile: %s\" %s < %s/%s&", MAILPROGRAM,
+ *buffer, parray[p].emailAddress, source_dir, *buffer);
+ system(command); */
+
+ sprintf(subj, "FICS source file from server %s: %s", fics_hostname, *buffer);
+ sprintf(fname, "%s/%s",source_dir, *buffer);
+ mail_file_to_user (p, subj, fname);
+ pprintf(p, "Source file %s sent to %s\n", *buffer, parray[p].emailAddress);
+ } else {
+ pprintf(p, "Found %d source files matching that:\n", count);
+ if (*iwant)
+ display_directory(p, buffer, count);
+ else { /* this junk is to get *.c *.h */
+ multicol *m = multicol_start(count);
+ char *s;
+ int i;
+ for (i=0; i < count; i++) {
+ if (((s = buffer[i] + strlen(buffer[i]) - 2) >= buffer[i]) && (!strcmp(s, ".c") || !strcmp(s, ".h")))
+ multicol_store(m, buffer[i]);
+ }
+ multicol_pprint(m, p, 78, 1);
+ multicol_end(m);
+ }
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_mailhelp(int p, param_list param)
+{ /* Sparky */
+ /* FILE *fp; char tmp[MAX_LINE_SIZE]; char fname[MAX_FILENAME_SIZE]; */
+ char command[MAX_FILENAME_SIZE];
+ char subj[81], fname[MAX_FILENAME_SIZE];
+ char *iwant, *buffer[1000];
+
+ int count;
+ static char nullify = '\0';
+
+ if (!parray[p].registered) {
+ pprintf(p, "Only registered people can use the mailhelp command.\n");
+ return COM_OK;
+ }
+ if (param[0].type == TYPE_NULL)
+ iwant = &nullify;
+ else
+ iwant = param[0].val.word;
+
+ count = search_directory(help_dir, iwant, buffer, 1000);
+ if (count == 0) {
+ pprintf(p, "Found no help file matching \"%s\".\n", iwant);
+ } else if (count == 1) {
+ /* sprintf(command, "%s -s \"FICS helpfile: %s\" %s < %s/%s&", MAILPROGRAM,
+ *buffer, parray[p].emailAddress, help_dir, *buffer);
+ system(command);a */
+
+
+ sprintf(subj, "FICS help file from server %s: %s", fics_hostname, *buffer);
+ sprintf(fname, "%s/%s",help_dir, *buffer);
+ mail_file_to_user (p, subj, fname);
+
+ pprintf(p, "Help file %s sent to %s\n", *buffer, parray[p].emailAddress);
+ } else {
+ pprintf(p, "Found %d helpfiles matching that:\n", count);
+ display_directory(p, buffer, count);
+ }
+
+ return COM_OK;
+}
+
+PUBLIC int com_mailmess(int p, param_list param)
+{
+ char command[MAX_FILENAME_SIZE];
+ char *buffer[1000];
+ char mdir[MAX_FILENAME_SIZE];
+ char filename[MAX_FILENAME_SIZE];
+ char subj[81], fname[MAX_FILENAME_SIZE];
+
+
+ if (!parray[p].registered) {
+ pprintf(p, "Only registered people can use the mailmess command.\n");
+ return COM_OK;
+ }
+ sprintf(filename, "%s.messages", parray[p].login);
+ sprintf(mdir, "%s/player_data/%c/", stats_dir, parray[p].login[0]);
+
+ if (search_directory(mdir, filename, buffer, 1000)) {
+/* Sprintf(command, "%s -s \"Your FICS messages\" %s < %s%s&", MAILPROGRAM,
+ parray[p].emailAddress, mdir, filename);
+ system(command); */
+
+ sprintf(subj, "Your FICS messages from server %s", fics_hostname);
+ sprintf(fname, "%s/%s", mdir, filename);
+ mail_file_to_user (p, subj, fname);
+
+
+
+ pprintf(p, "Messages sent to %s\n", parray[p].emailAddress);
+ } else {
+ pprintf(p, "You have no messages.\n");
+ }
+ return COM_OK;
+
+}
+
+PUBLIC int com_handles(int p, param_list param)
+{
+ char *buffer[1000];
+ char pdir[MAX_FILENAME_SIZE];
+ int count;
+
+ sprintf(pdir, "%s/%c", player_dir, param[0].val.word[0]);
+ count = search_directory(pdir, param[0].val.word, buffer, 1000);
+ pprintf(p, "Found %d names.\n", count);
+ if (count > 0)
+ display_directory(p, buffer, count);
+ return COM_OK;
+}
+
+PUBLIC int com_znotify(int p, param_list param)
+{
+ int p1, count = 0;
+
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (player_notified(p, p1)) {
+ if (!count)
+ pprintf(p, "Present company on your notify list:\n ");
+ pprintf(p, " %s", parray[p1].name);
+ count++;
+ }
+ }
+ if (count)
+ pprintf(p, ".\n");
+ else
+ pprintf(p, "No one from your notify list is logged on.\n");
+
+ count = 0;
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (player_notified(p1, p)) {
+ if (!count)
+ pprintf(p,
+ "The following players have you on their notify list:\n ");
+ pprintf(p, " %s", parray[p1].name);
+ count++;
+ }
+ }
+ if (count)
+ pprintf(p, ".\n");
+ else
+ pprintf(p, "No one logged in has you on their notify list.\n");
+ return COM_OK;
+}
+
+PUBLIC int com_qtell(int p, param_list param)
+{
+ int p1;
+ char tmp[MAX_STRING_LENGTH];
+ char dummy[2];
+ char buffer1[MAX_STRING_LENGTH]; /* no highlight and no bell */
+ char buffer2[MAX_STRING_LENGTH]; /* no highlight and bell */
+ char buffer3[MAX_STRING_LENGTH]; /* highlight and no bell */
+ char buffer4[MAX_STRING_LENGTH]; /* highlight and and bell */
+ int i, count;
+/* FILE *fp; */
+
+ if (!in_list("td", parray[p].name)) {
+ pprintf(p, "Only TD programs are allowed to use this command.\n");
+ return COM_OK;
+ }
+ strcpy(buffer1, ":\0");
+ strcpy(buffer2, ":\0");
+ strcpy(buffer3, ":\0");
+ strcpy(buffer4, ":\0");
+
+ sprintf(tmp, "%s", param[1].val.string);
+ for (i = 0, count = 0; ((tmp[i] != '\0') && (count < 1029));) {
+ if ((tmp[i] == '\\') && (tmp[i + 1] == 'n')) {
+ strcat(buffer1, "\n:");
+ strcat(buffer2, "\n:");
+ strcat(buffer3, "\n:");
+ strcat(buffer4, "\n:");
+ count += 2;
+ i += 2;
+ } else if ((tmp[i] == '\\') && (tmp[i + 1] == 'b')) {
+ strcat(buffer2, "\007");
+ strcat(buffer4, "\007");
+ count++;
+ i += 2;
+ } else if ((tmp[i] == '\\') && (tmp[i + 1] == 'H')) {
+ strcat(buffer3, "\033[7m");
+ strcat(buffer4, "\033[7m");
+ count += 4;
+ i += 2;
+ } else if ((tmp[i] == '\\') && (tmp[i + 1] == 'h')) {
+ strcat(buffer3, "\033[0m");
+ strcat(buffer4, "\033[0m");
+ count += 4;
+ i += 2;
+ } else {
+ dummy[0] = tmp[i];
+ dummy[1] = '\0';
+ strcat(buffer1, dummy);
+ strcat(buffer2, dummy);
+ strcat(buffer3, dummy);
+ strcat(buffer4, dummy);
+ count++;
+ i++;
+ }
+ }
+
+ if (param[0].type == TYPE_WORD) {
+/*
+ fp = fopen("/tmp/fics-log", "a");
+ fprintf(fp, "PLAYER \"%s\" - MESSAGE \"%s\"\n", param[0].val.word, param[1].val.string);
+ fclose(fp);
+*/
+ if ((p1 = player_find_bylogin(param[0].val.word)) < 0) {
+ pprintf(p, "*qtell %s 1*\n", param[0].val.word);
+ return COM_OK;
+ }
+ pprintf_prompt(p1, "\n%s\n", (parray[p1].highlight && parray[p1].bell) ? buffer4 :
+ (parray[p1].highlight && !parray[p1].bell) ? buffer3 :
+ (!parray[p1].highlight && parray[p1].bell) ? buffer2 :
+ buffer1);
+ pprintf(p, "*qtell %s 0*\n", parray[p1].name);
+
+ } else {
+ int p1;
+ int i;
+ int ch = param[0].val.integer;
+
+/*
+ fp = fopen("/tmp/fics-log", "a");
+ fprintf(fp, "CHANNEL \"%d\" - MESSAGE \"%s\"\n", param[0].val.integer, param[1].val.string);
+ fclose(fp);
+*/
+
+ if (ch == 0) {
+ pprintf(p, "*qtell %d 1*\n", param[0].val.integer);
+ return COM_OK;
+ }
+ if (ch < 0) {
+ pprintf(p, "*qtell %d 1*\n", param[0].val.integer);
+ return COM_OK;
+ }
+ if (ch >= MAX_CHANNELS) {
+ pprintf(p, "*qtell %d 1*\n", param[0].val.integer);
+ return COM_OK;
+ }
+ for (i = 0; i < numOn[ch]; i++) {
+ p1 = channels[ch][i];
+ if (p1 == p)
+ continue;
+ if (player_censored(p1, p))
+ continue;
+ if ((parray[p1].status == PLAYER_PASSWORD)
+ || (parray[p1].status == PLAYER_LOGIN))
+ continue;
+ pprintf_prompt(p1, "\n%s\n", (parray[p1].highlight && parray[p1].bell) ? buffer4 :
+ (parray[p1].highlight && !parray[p1].bell) ? buffer3 :
+ (!parray[p1].highlight && parray[p1].bell) ? buffer2 :
+ buffer1);
+ }
+ pprintf(p, "*qtell %d 0*\n", param[0].val.integer);
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_getpi(int p, param_list param)
+{
+ int p1;
+
+ if (!in_list("td", parray[p].name)) {
+ pprintf(p, "Only TD programs are allowed to use this command.\n");
+ return COM_OK;
+ }
+ if (((p1 = player_find_bylogin(param[0].val.word)) < 0) || (parray[p1].registered == 0)) {
+ /* Darkside suggested not to return anything */
+ return COM_OK;
+ }
+ if (!parray[p1].registered) {
+ pprintf(p, "*getpi %s -1 -1 -1*\n", parray[p1].name);
+ } else {
+ pprintf(p, "*getpi %s %d %d %d*\n", parray[p1].name,
+ parray[p1].w_stats.rating,
+ parray[p1].b_stats.rating,
+ parray[p1].s_stats.rating);
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_getps(int p, param_list param)
+{
+ int p1;
+
+ if ((((p1 = player_find_bylogin(param[0].val.word)) < 0) || (parray[p1].registered == 0)) || (parray[p1].game < 0)) {
+ pprintf(p, "*status %s 1*\n", param[0].val.word);
+ return COM_OK;
+ }
+ pprintf(p, "*status %s 0 %s*\n", parray[p1].name, parray[(parray[p1].opponent)].name);
+ return COM_OK;
+}
+PUBLIC int com_limits(int p, param_list param)
+{
+ pprintf(p, "\nCurrent hardcoded limits:\n");
+ pprintf(p, " Max number of players: %d\n", MAX_PLAYER);
+ pprintf(p, " Max number of channels and max capacity: %d\n", MAX_CHANNELS);
+ pprintf(p, " Max number of channels one can be in: %d\n", MAX_INCHANNELS);
+ pprintf(p, " Max number of people on the notify list: %d\n", MAX_NOTIFY);
+ pprintf(p, " Max number of aliases: %d\n", MAX_ALIASES);
+ pprintf(p, " Max number of games you can observe at a time: %d\n", MAX_OBSERVE);
+ pprintf(p, " Max number of requests pending: %d\n", MAX_PENDING);
+ pprintf(p, " Max number of people on the censor list: %d\n", MAX_CENSOR);
+ pprintf(p, " Max number of people in a simul game: %d\n", MAX_SIMUL);
+ pprintf(p, " Max number of messages one can receive: %d\n", MAX_MESSAGES);
+ pprintf(p, " Min number of games to be active: %d\n", PROVISIONAL);
+ pprintf(p, "\nAdmin settable limits:\n");
+ pprintf(p, " Quota list gives two shouts per %d seconds.\n", quota_time);
+ return COM_OK;
+}
diff --git a/FICS/config.h b/FICS/config.h
new file mode 100644
index 0000000..0257657
--- /dev/null
+++ b/FICS/config.h
@@ -0,0 +1,107 @@
+/* config.h
+ *
+ */
+
+/* Configure file locations in this include file. */
+
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name email yy/mm/dd Change
+ Richard Nash 94/03/08 Created
+ Sparky 95/12/30 Beautified.
+*/
+
+#ifndef _CONFIG_H
+#define _CONFIG_H
+
+/* CONFIGURE THIS: The port on which the server binds */
+
+#define DEFAULT_PORT 5000
+
+/* Must be a dns recognisable host name */
+
+#define SERVER_HOSTNAME "fics.somewhere.domain"
+
+/* At AFICS we just use 'fics' but for your server you might want to change this
+ eg to BICS, EICS, DICS etc */
+
+#define SERVER_NAME "Xfics" /* for pgn output */
+
+#define SERVER_LOCATION "Unconfigured City, Country" /* for pgn output */
+
+
+/* Which is the default language for help files, see variable.h for the
+ current available settings */
+
+#define LANG_DEFAULT LANG_ENGLISH
+
+/* CONFIGURE THESE: Locations of the data, players, and games directories */
+/* These must be absolute paths because some mail daemons may be called */
+/* from outside the pwd */
+
+#define DEFAULT_MESS "/usr/home/fics/FICS.DIST/data/messages"
+#define DEFAULT_INDEX "/usr/home/fics/FICS.DIST/data/index"
+#define DEFAULT_HELP "/usr/home/fics/FICS.DIST/data/help"
+#define DEFAULT_COMHELP "/usr/home/fics/FICS.DIST/data/com_help"
+#define HELP_SPANISH "/usr/home/fics/FICS.DIST/data/Spanish"
+#define HELP_FRENCH "/usr/home/fics/FICS.DIST/data/French"
+#define HELP_DANISH "/usr/home/fics/FICS.DIST/data/Danish"
+#define DEFAULT_INFO "/usr/home/fics/FICS.DIST/data/info"
+#define DEFAULT_ADHELP "/usr/home/fics/FICS.DIST/data/admin"
+#define DEFAULT_USCF "/usr/home/fics/FICS.DIST/data/uscf"
+#define DEFAULT_STATS "/usr/home/fics/FICS.DIST/data/stats"
+#define DEFAULT_CONFIG "/usr/home/fics/FICS.DIST/config"
+#define DEFAULT_PLAYERS "/usr/home/fics/FICS.DIST/players"
+#define DEFAULT_ADJOURNED "/usr/home/fics/FICS.DIST/games/adjourned"
+#define DEFAULT_HISTORY "/usr/home/fics/FICS.DIST/games/history"
+#define DEFAULT_JOURNAL "/usr/home/fics/FICS.DIST/games/journal"
+#define DEFAULT_BOARDS "/usr/home/fics/FICS.DIST/data/boards"
+#define DEFAULT_SOURCE "/usr/home/fics/FICS.DIST/FICS"
+#define DEFAULT_LISTS "/usr/home/fics/FICS.DIST/data/lists"
+#define DEFAULT_NEWS "/usr/home/fics/FICS.DIST/data/news"
+#define DEFAULT_BOOK "/usr/home/fics/FICS.DIST/data/book"
+#define MESS_FULL "/usr/home/fics/FICS.DIST/data/messages/full"
+#define MESS_FULL_UNREG "/usr/home/fics/FICS.DIST/data/messages/full_unreg"
+#define DEFAULT_USAGE "/usr/home/fics/FICS.DIST/data/usage"
+#define USAGE_SPANISH "/usr/home/fics/FICS.DIST/data/usage_spanish"
+#define USAGE_FRENCH "/usr/home/fics/FICS.DIST/data/usage_french"
+#define USAGE_DANISH "/usr/home/fics/FICS.DIST/data/usage_danish"
+
+
+/* Where the standard ucb mail program is */
+
+#define MAILPROGRAM "/usr/ucb/mail"
+
+/* SENDMAILPROG is a faster and more reliable means of sending mail if
+ defined. Make sure your system mailer agent is defined here properly
+ for your system with respect to name, location and options. These may
+ differ significatly depending on the type of system and what mailer is
+ installed */
+/* The floowing works fine for SunOS4.1.X with berkeley sendmail */
+
+/* #define SENDMAILPROG "/usr/lib/sendmail -t" */
+
+/* Details of the head admin */
+
+#define HADMINHANDLE "AdminGuy"
+#define HADMINEMAIL "AdminGuy@this.place"
+
+/* Registration mail address */
+
+#define REGMAIL "AdminGuy@this.place"
+
+#endif /* _CONFIG_H */
diff --git a/FICS/config.h.dist b/FICS/config.h.dist
new file mode 100644
index 0000000..0257657
--- /dev/null
+++ b/FICS/config.h.dist
@@ -0,0 +1,107 @@
+/* config.h
+ *
+ */
+
+/* Configure file locations in this include file. */
+
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name email yy/mm/dd Change
+ Richard Nash 94/03/08 Created
+ Sparky 95/12/30 Beautified.
+*/
+
+#ifndef _CONFIG_H
+#define _CONFIG_H
+
+/* CONFIGURE THIS: The port on which the server binds */
+
+#define DEFAULT_PORT 5000
+
+/* Must be a dns recognisable host name */
+
+#define SERVER_HOSTNAME "fics.somewhere.domain"
+
+/* At AFICS we just use 'fics' but for your server you might want to change this
+ eg to BICS, EICS, DICS etc */
+
+#define SERVER_NAME "Xfics" /* for pgn output */
+
+#define SERVER_LOCATION "Unconfigured City, Country" /* for pgn output */
+
+
+/* Which is the default language for help files, see variable.h for the
+ current available settings */
+
+#define LANG_DEFAULT LANG_ENGLISH
+
+/* CONFIGURE THESE: Locations of the data, players, and games directories */
+/* These must be absolute paths because some mail daemons may be called */
+/* from outside the pwd */
+
+#define DEFAULT_MESS "/usr/home/fics/FICS.DIST/data/messages"
+#define DEFAULT_INDEX "/usr/home/fics/FICS.DIST/data/index"
+#define DEFAULT_HELP "/usr/home/fics/FICS.DIST/data/help"
+#define DEFAULT_COMHELP "/usr/home/fics/FICS.DIST/data/com_help"
+#define HELP_SPANISH "/usr/home/fics/FICS.DIST/data/Spanish"
+#define HELP_FRENCH "/usr/home/fics/FICS.DIST/data/French"
+#define HELP_DANISH "/usr/home/fics/FICS.DIST/data/Danish"
+#define DEFAULT_INFO "/usr/home/fics/FICS.DIST/data/info"
+#define DEFAULT_ADHELP "/usr/home/fics/FICS.DIST/data/admin"
+#define DEFAULT_USCF "/usr/home/fics/FICS.DIST/data/uscf"
+#define DEFAULT_STATS "/usr/home/fics/FICS.DIST/data/stats"
+#define DEFAULT_CONFIG "/usr/home/fics/FICS.DIST/config"
+#define DEFAULT_PLAYERS "/usr/home/fics/FICS.DIST/players"
+#define DEFAULT_ADJOURNED "/usr/home/fics/FICS.DIST/games/adjourned"
+#define DEFAULT_HISTORY "/usr/home/fics/FICS.DIST/games/history"
+#define DEFAULT_JOURNAL "/usr/home/fics/FICS.DIST/games/journal"
+#define DEFAULT_BOARDS "/usr/home/fics/FICS.DIST/data/boards"
+#define DEFAULT_SOURCE "/usr/home/fics/FICS.DIST/FICS"
+#define DEFAULT_LISTS "/usr/home/fics/FICS.DIST/data/lists"
+#define DEFAULT_NEWS "/usr/home/fics/FICS.DIST/data/news"
+#define DEFAULT_BOOK "/usr/home/fics/FICS.DIST/data/book"
+#define MESS_FULL "/usr/home/fics/FICS.DIST/data/messages/full"
+#define MESS_FULL_UNREG "/usr/home/fics/FICS.DIST/data/messages/full_unreg"
+#define DEFAULT_USAGE "/usr/home/fics/FICS.DIST/data/usage"
+#define USAGE_SPANISH "/usr/home/fics/FICS.DIST/data/usage_spanish"
+#define USAGE_FRENCH "/usr/home/fics/FICS.DIST/data/usage_french"
+#define USAGE_DANISH "/usr/home/fics/FICS.DIST/data/usage_danish"
+
+
+/* Where the standard ucb mail program is */
+
+#define MAILPROGRAM "/usr/ucb/mail"
+
+/* SENDMAILPROG is a faster and more reliable means of sending mail if
+ defined. Make sure your system mailer agent is defined here properly
+ for your system with respect to name, location and options. These may
+ differ significatly depending on the type of system and what mailer is
+ installed */
+/* The floowing works fine for SunOS4.1.X with berkeley sendmail */
+
+/* #define SENDMAILPROG "/usr/lib/sendmail -t" */
+
+/* Details of the head admin */
+
+#define HADMINHANDLE "AdminGuy"
+#define HADMINEMAIL "AdminGuy@this.place"
+
+/* Registration mail address */
+
+#define REGMAIL "AdminGuy@this.place"
+
+#endif /* _CONFIG_H */
diff --git a/FICS/dfree.c b/FICS/dfree.c
new file mode 100644
index 0000000..ebd9e4d
--- /dev/null
+++ b/FICS/dfree.c
@@ -0,0 +1,226 @@
+/* Free a block of memory allocated by `malloc'.
+ Copyright 1990, 1991, 1992, 1994 Free Software Foundation, Inc.
+ Written May 1989 by Mike Haertel.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA.
+
+ The author may be reached (Email) at the address mike@ai.mit.edu,
+ or (US mail) as Mike Haertel c/o Free Software Foundation. */
+
+#ifndef _MALLOC_INTERNAL
+#define _MALLOC_INTERNAL
+#include <malloc.h>
+#endif
+
+/* Debugging hook for free. */
+void (*__free_hook) __P ((__ptr_t __ptr));
+
+/* List of blocks allocated by memalign. */
+struct alignlist *_aligned_blocks = NULL;
+
+/* Return memory to the heap.
+ Like `free' but don't call a __free_hook if there is one. */
+void
+_free_internal (ptr)
+ __ptr_t ptr;
+{
+ int type;
+ __malloc_size_t block, blocks;
+ register __malloc_size_t i;
+ struct list *prev, *next;
+
+ block = BLOCK (ptr);
+
+ type = _heapinfo[block].busy.type;
+ switch (type)
+ {
+ case 0:
+ /* Get as many statistics as early as we can. */
+ --_chunks_used;
+ _bytes_used -= _heapinfo[block].busy.info.size * BLOCKSIZE;
+ _bytes_free += _heapinfo[block].busy.info.size * BLOCKSIZE;
+
+ /* Find the free cluster previous to this one in the free list.
+ Start searching at the last block referenced; this may benefit
+ programs with locality of allocation. */
+ i = _heapindex;
+ if (i > block)
+ while (i > block)
+ i = _heapinfo[i].free.prev;
+ else
+ {
+ do
+ i = _heapinfo[i].free.next;
+ while (i > 0 && i < block);
+ i = _heapinfo[i].free.prev;
+ }
+
+ /* Determine how to link this block into the free list. */
+ if (block == i + _heapinfo[i].free.size)
+ {
+ /* Coalesce this block with its predecessor. */
+ _heapinfo[i].free.size += _heapinfo[block].busy.info.size;
+ block = i;
+ }
+ else
+ {
+ /* Really link this block back into the free list. */
+ _heapinfo[block].free.size = _heapinfo[block].busy.info.size;
+ _heapinfo[block].free.next = _heapinfo[i].free.next;
+ _heapinfo[block].free.prev = i;
+ _heapinfo[i].free.next = block;
+ _heapinfo[_heapinfo[block].free.next].free.prev = block;
+ ++_chunks_free;
+ }
+
+ /* Now that the block is linked in, see if we can coalesce it
+ with its successor (by deleting its successor from the list
+ and adding in its size). */
+ if (block + _heapinfo[block].free.size == _heapinfo[block].free.next)
+ {
+ _heapinfo[block].free.size
+ += _heapinfo[_heapinfo[block].free.next].free.size;
+ _heapinfo[block].free.next
+ = _heapinfo[_heapinfo[block].free.next].free.next;
+ _heapinfo[_heapinfo[block].free.next].free.prev = block;
+ --_chunks_free;
+ }
+
+ /* Now see if we can return stuff to the system. */
+ blocks = _heapinfo[block].free.size;
+ if (blocks >= FINAL_FREE_BLOCKS && block + blocks == _heaplimit
+ && (*__morecore) (0) == ADDRESS (block + blocks))
+ {
+ register __malloc_size_t bytes = blocks * BLOCKSIZE;
+ _heaplimit -= blocks;
+ (*__morecore) (-bytes);
+ _heapinfo[_heapinfo[block].free.prev].free.next
+ = _heapinfo[block].free.next;
+ _heapinfo[_heapinfo[block].free.next].free.prev
+ = _heapinfo[block].free.prev;
+ block = _heapinfo[block].free.prev;
+ --_chunks_free;
+ _bytes_free -= bytes;
+ }
+
+ /* Set the next search to begin at this block. */
+ _heapindex = block;
+ break;
+
+ default:
+ /* Do some of the statistics. */
+ --_chunks_used;
+ _bytes_used -= 1 << type;
+ ++_chunks_free;
+ _bytes_free += 1 << type;
+
+ /* Get the address of the first free fragment in this block. */
+ prev = (struct list *) ((char *) ADDRESS (block) +
+ (_heapinfo[block].busy.info.frag.first << type));
+
+ if (_heapinfo[block].busy.info.frag.nfree == (BLOCKSIZE >> type) - 1)
+ {
+ /* If all fragments of this block are free, remove them
+ from the fragment list and free the whole block. */
+ next = prev;
+ for (i = 1; i < (__malloc_size_t) (BLOCKSIZE >> type); ++i)
+ next = next->next;
+ prev->prev->next = next;
+ if (next != NULL)
+ next->prev = prev->prev;
+ _heapinfo[block].busy.type = 0;
+ _heapinfo[block].busy.info.size = 1;
+
+ /* Keep the statistics accurate. */
+ ++_chunks_used;
+ _bytes_used += BLOCKSIZE;
+ _chunks_free -= BLOCKSIZE >> type;
+ _bytes_free -= BLOCKSIZE;
+
+ free (ADDRESS (block));
+ }
+ else if (_heapinfo[block].busy.info.frag.nfree != 0)
+ {
+ /* If some fragments of this block are free, link this
+ fragment into the fragment list after the first free
+ fragment of this block. */
+
+ /****** Test for no double frees --mann ********/
+ {
+#include <stdio.h>
+ struct list* tmp = prev;
+
+ while (tmp != NULL && BLOCK(tmp) == BLOCK(ptr)) {
+ if (tmp == ptr) {
+ fprintf(stderr, "Freeing already freed memory!\n");
+ abort();
+ }
+ tmp = tmp->next;
+ }
+ }
+ /************************************************/
+
+ next = (struct list *) ptr;
+ next->next = prev->next;
+ next->prev = prev;
+ prev->next = next;
+ if (next->next != NULL)
+ next->next->prev = next;
+ ++_heapinfo[block].busy.info.frag.nfree;
+ }
+ else
+ {
+ /* No fragments of this block are free, so link this
+ fragment into the fragment list and announce that
+ it is the first free fragment of this block. */
+ prev = (struct list *) ptr;
+ _heapinfo[block].busy.info.frag.nfree = 1;
+ _heapinfo[block].busy.info.frag.first = (unsigned long int)
+ ((unsigned long int) ((char *) ptr - (char *) NULL)
+ % BLOCKSIZE >> type);
+ prev->next = _fraghead[type].next;
+ prev->prev = &_fraghead[type];
+ prev->prev->next = prev;
+ if (prev->next != NULL)
+ prev->next->prev = prev;
+ }
+ break;
+ }
+}
+
+/* Return memory to the heap. */
+void
+free (ptr)
+ __ptr_t ptr;
+{
+ register struct alignlist *l;
+
+ if (ptr == NULL)
+ return;
+
+ for (l = _aligned_blocks; l != NULL; l = l->next)
+ if (l->aligned == ptr)
+ {
+ l->aligned = NULL; /* Mark the slot in the list as free. */
+ ptr = l->exact;
+ break;
+ }
+
+ if (__free_hook != NULL)
+ (*__free_hook) (ptr);
+ else
+ _free_internal (ptr);
+}
diff --git a/FICS/eco.c b/FICS/eco.c
new file mode 100644
index 0000000..80b1196
--- /dev/null
+++ b/FICS/eco.c
@@ -0,0 +1,428 @@
+#include "stdinclude.h"
+#include "board.h"
+#include "gamedb.h"
+#include "command.h"
+#include "playerdb.h"
+#include "gameproc.h"
+#include "obsproc.h"
+#include "utils.h"
+#include "common.h"
+#include "config.h"
+
+PUBLIC char *book_dir = DEFAULT_BOOK;
+
+typedef struct {
+ char ECO[4];
+ char FENpos[80];
+} ECO_entry;
+
+typedef struct {
+ char NIC[6];
+ char FENpos[80];
+} NIC_entry;
+
+typedef struct {
+ char LONG[80];
+ char FENpos[80];
+} LONG_entry;
+
+ECO_entry *ECO_book[1096];
+NIC_entry *NIC_book[1096];
+LONG_entry *LONG_book[4096];
+
+int ECO_entries, NIC_entries, LONG_entries;
+
+char *boardToFEN(int g)
+{
+ int i,j;
+ static char FENstring[80];
+ int FENcount = 0;
+ int space = 0;
+
+ for(i=7; i>=0; i--) {
+ for(j=0; j<8; j++) {
+ switch(garray[g].game_state.board[j][i]) {
+ case W_PAWN:
+ if (space>0) {
+ FENstring[FENcount++]=space+'0';
+ space=0;
+ }
+ FENstring[FENcount++]='P';
+ break;
+ case W_ROOK:
+ if (space>0) {
+ FENstring[FENcount++]=space+'0';
+ space=0;
+ }
+ FENstring[FENcount++]='R';
+ break;
+ case W_KNIGHT:
+ if (space>0) {
+ FENstring[FENcount++]=space+'0';
+ space=0;
+ }
+ FENstring[FENcount++]='N';
+ break;
+ case W_BISHOP:
+ if (space>0) {
+ FENstring[FENcount++]=space+'0';
+ space=0;
+ }
+ FENstring[FENcount++]='B';
+ break;
+ case W_QUEEN:
+ if (space>0) {
+ FENstring[FENcount++]=space+'0';
+ space=0;
+ }
+ FENstring[FENcount++]='Q';
+ break;
+ case W_KING:
+ if (space>0) {
+ FENstring[FENcount++]=space+'0';
+ space=0;
+ }
+ FENstring[FENcount++]='K';
+ break;
+ case B_PAWN:
+ if (space>0) {
+ FENstring[FENcount++]=space+'0';
+ space=0;
+ }
+ FENstring[FENcount++]='p';
+ break;
+ case B_ROOK:
+ if (space>0) {
+ FENstring[FENcount++]=space+'0';
+ space=0;
+ }
+ FENstring[FENcount++]='r';
+ break;
+ case B_KNIGHT:
+ if (space>0) {
+ FENstring[FENcount++]=space+'0';
+ space=0;
+ }
+ FENstring[FENcount++]='n';
+ break;
+ case B_BISHOP:
+ if (space>0) {
+ FENstring[FENcount++]=space+'0';
+ space=0;
+ }
+ FENstring[FENcount++]='b';
+ break;
+ case B_QUEEN:
+ if (space>0) {
+ FENstring[FENcount++]=space+'0';
+ space=0;
+ }
+ FENstring[FENcount++]='q';
+ break;
+ case B_KING:
+ if (space>0) {
+ FENstring[FENcount++]=space+'0';
+ space=0;
+ }
+ FENstring[FENcount++]='k';
+ break;
+ default:
+ space++;
+ break;
+ }
+ }
+ if (space>0) {
+ FENstring[FENcount++]=space+'0';
+ space=0;
+ }
+ FENstring[FENcount++]='/';
+ }
+ FENstring[--FENcount]=' ';
+ FENstring[++FENcount]=(garray[g].game_state.onMove==WHITE) ? 'w' : 'b';
+ FENstring[++FENcount]='\0';
+ return FENstring;
+}
+
+void ECO_init()
+{
+ FILE *fp;
+ char tmp[1024];
+ char *ptmp= tmp;
+ char FENpos[73], ECO[4], onMove[2];
+ char filename[1024];
+ int i=0;
+
+ sprintf(filename, "%s/eco999.idx", book_dir);
+ fp= fopen(filename, "r");
+ if (!fp) {
+ fprintf(stderr, "Could not open ECO file\n");
+ exit(1);
+ }
+ while (!feof(fp)) {
+ strcpy(ptmp, "");
+ fgets(ptmp, 1024, fp);
+ if (feof(fp)) continue;
+ sscanf(ptmp, "%[\x21-z] %s", FENpos, onMove);
+ sprintf(FENpos, "%s %s", FENpos, onMove);
+ strcpy(ptmp, "");
+ fgets(ptmp, 1024, fp);
+ if (feof(fp)) continue;
+ sscanf(ptmp, "%[0-z]", ECO);
+ ECO_book[i]= (ECO_entry *) malloc(sizeof(ECO_entry));
+ if (ECO_book[i]==NULL) {
+ fprintf(stderr, "Cound not alloc mem for ECO entry %d.\n", i);
+ exit(1);
+ }
+ strcpy(ECO_book[i]->ECO, ECO);
+ strcpy(ECO_book[i]->FENpos, FENpos);
+ ++i;
+ }
+ fclose(fp);
+ ECO_book[i]=NULL;
+ fprintf(stderr, "%d entries in ECO book\n", i);
+ ECO_entries = i;
+
+ while (--i >= 0)
+ if (ECO_book[i] == NULL)
+ fprintf(stderr, "ERROR! ECO book position number %d is NULL.", i);
+}
+
+void NIC_init()
+{
+ FILE *fp;
+ char tmp[1024];
+ char *ptmp= tmp;
+ char FENpos[73], NIC[6], onMove[2];
+ char filename[1024];
+ int i=0;
+
+ sprintf(filename, "%s/nic999.idx", book_dir);
+ fp= fopen(filename, "r");
+ if (!fp) {
+ fprintf(stderr, "Could not open NIC file\n");
+ exit(1);
+ }
+ while (!feof(fp)) {
+ strcpy(ptmp, "");
+ fgets(ptmp, 1024, fp);
+ if (feof(fp)) continue;
+ sscanf(ptmp, "%[\x21-z] %s", FENpos, onMove);
+ sprintf(FENpos, "%s %s", FENpos, onMove);
+ strcpy(ptmp, "");
+ fgets(ptmp, 1024, fp);
+ if (feof(fp)) continue;
+ sscanf(ptmp, "%[.-z]", NIC);
+ NIC_book[i]= (NIC_entry *) malloc(sizeof(NIC_entry));
+ if (NIC_book[i]==NULL) {
+ fprintf(stderr, "Cound not alloc mem for NIC entry %d.\n", i);
+ exit(1);
+ }
+ strcpy(NIC_book[i]->NIC, NIC);
+ strcpy(NIC_book[i]->FENpos, FENpos);
+ ++i;
+ }
+ fclose(fp);
+ NIC_book[i]=NULL;
+ fprintf(stderr, "%d entries in NIC book\n", i);
+ NIC_entries = i;
+}
+
+void LONG_init()
+{
+ FILE *fp;
+ char tmp[1024];
+ char *ptmp= tmp;
+ char FENpos[73], LONG[256], onMove[2];
+ char filename[1024];
+ int i=0;
+
+ sprintf(filename, "%s/long999.idx", book_dir);
+ fp= fopen(filename, "r");
+ if (!fp) {
+ fprintf(stderr, "Could not open LONG file\n");
+ exit(1);
+ }
+ while (!feof(fp)) {
+ strcpy(ptmp, "");
+ fgets(ptmp, 1024, fp);
+ if (feof(fp)) continue;
+ sscanf(ptmp, "%[\x21-z] %s", FENpos, onMove);
+ sprintf(FENpos, "%s %s", FENpos, onMove);
+ strcpy(ptmp, "");
+ fgets(ptmp, 1024, fp);
+ if (feof(fp)) continue;
+ sscanf(ptmp, "%[^*\n]", LONG);
+ LONG_book[i]= (LONG_entry *) malloc(sizeof(LONG_entry));
+ if (LONG_book[i]==NULL) {
+ fprintf(stderr, "Cound not alloc mem for LONG entry %d.\n", i);
+ exit(1);
+ }
+ strcpy(LONG_book[i]->LONG, LONG);
+ strcpy(LONG_book[i]->FENpos, FENpos);
+ ++i;
+ }
+ fclose(fp);
+ LONG_book[i]=NULL;
+ fprintf(stderr, "%d entries in LONG book\n", i);
+ LONG_entries = i;
+}
+
+void BookInit()
+{
+ ECO_init();
+ NIC_init();
+ LONG_init();
+}
+
+char *getECO(int g)
+{
+ static char ECO[4];
+
+#ifndef IGNORE_ECO
+
+ int i, flag, l = 0, r = ECO_entries - 1, x;
+
+
+ if ((parray[garray[g].white].private) || (parray[garray[g].black].private)) {
+ strcpy(ECO, "---");
+ return ECO;
+ } else {
+ if (garray[g].type == TYPE_WILD) {
+ strcpy(ECO, "---");
+ return ECO;
+ } else if (garray[g].moveList == NULL) {
+ strcpy(ECO, "***");
+ return ECO;
+ } else {
+ strcpy(ECO, "A00");
+ }
+ }
+
+ for (flag=0,i=garray[g].numHalfMoves; (i>0 && !flag); i--) {
+ l = 0;
+ r = ECO_entries - 1;
+ while ((r >= l) && !flag) {
+ x = (l+r)/2;
+ if ((strcmp(garray[g].moveList[i].FENpos, ECO_book[x]->FENpos)) < 0)
+ r = x - 1;
+ else
+ l = x + 1;
+ if (!strcmp(garray[g].moveList[i].FENpos, ECO_book[x]->FENpos)) {
+ strcpy(ECO, ECO_book[x]->ECO);
+ flag=1;
+ }
+ }
+ }
+#else
+
+ strcpy(ECO, "---");
+
+#endif
+
+
+ return ECO;
+}
+
+PUBLIC int com_eco(int p, param_list param)
+{
+
+#ifndef IGNORE_ECO
+
+ int i, flag = 0, x, l, r;
+ int g1, p1;
+
+
+ if (param[0].type == TYPE_NULL) { /* own game */
+ if (parray[p].game < 0) {
+ pprintf(p, "You are not playing or examining a game.\n");
+ return COM_OK;
+ }
+ g1=parray[p].game;
+ if (garray[g1].status != GAME_EXAMINE && !pIsPlaying(p))
+ return COM_OK;
+ } else {
+ g1 = GameNumFromParam (p, &p1, &param[0]);
+ if (g1 < 0) return COM_OK;
+ if ((g1 >= g_num) || ((garray[g1].status != GAME_ACTIVE) &&
+ (garray[g1].status != GAME_EXAMINE))) {
+ pprintf(p, "There is no such game.\n");
+ return COM_OK;
+ }
+ }
+
+ if ((((parray[garray[g1].white].private) ||
+ (parray[garray[g1].black].private))) &&
+ (parray[p].adminLevel==0)) {
+ pprintf(p, "Sorry - that game is private.\n");
+ return COM_OK;
+ } else {
+ if (garray[g1].type == TYPE_WILD) {
+ pprintf(p, "That game is a wild game.\n");
+ return COM_OK;
+ }
+ }
+
+ pprintf(p, "Info about game %d: \"%s vs. %s\"\n\n", g1+1,
+ garray[g1].white_name,
+ garray[g1].black_name);
+
+ if (garray[g1].moveList==NULL) {
+ return COM_OK;
+ }
+
+ for (flag=0,i=garray[g1].numHalfMoves; (i>0 && !flag); i--) {
+ l = 0;
+ r = ECO_entries - 1;
+ while ((r >= l) && !flag) {
+ x = (l+r)/2;
+ if ((strcmp(garray[g1].moveList[i].FENpos, ECO_book[x]->FENpos)) < 0)
+ r = x - 1;
+ else
+ l = x + 1;
+ if (!strcmp(garray[g1].moveList[i].FENpos, ECO_book[x]->FENpos)) {
+ pprintf(p, " ECO[%3d]: %s\n", i, ECO_book[x]->ECO);
+ flag=1;
+ }
+ }
+ }
+
+ for (flag=0, i=garray[g1].numHalfMoves; ((i>0) && (!flag)); i--) {
+ l = 0;
+ r = NIC_entries - 1;
+ while ((r >=l) && !flag) {
+ x = (l+r)/2;
+ if ((strcmp(garray[g1].moveList[i].FENpos, NIC_book[x]->FENpos)) < 0)
+ r = x - 1;
+ else
+ l = x + 1;
+ if (!strcmp(garray[g1].moveList[i].FENpos, NIC_book[x]->FENpos)) {
+ pprintf(p, " NIC[%3d]: %s\n", i, NIC_book[x]->NIC);
+ flag=1;
+ }
+ }
+ }
+
+ for (flag=0, i=garray[g1].numHalfMoves; ((i>0) && (!flag)); i--) {
+ l = 0;
+ r = LONG_entries - 1;
+ while ((r >=l) && !flag) {
+ x = (l+r)/2;
+ if ((strcmp(garray[g1].moveList[i].FENpos, LONG_book[x]->FENpos)) < 0)
+ r = x - 1;
+ else
+ l = x + 1;
+ if (!strcmp(garray[g1].moveList[i].FENpos, LONG_book[x]->FENpos)) {
+ pprintf(p, " LONG[%3d]: %s\n", i, LONG_book[x]->LONG);
+ flag=1;
+ }
+ }
+ }
+
+#else
+
+ pprintf(p, "ECO not available... out of service!.\n");
+
+#endif
+
+ return COM_OK;
+}
diff --git a/FICS/eco.h b/FICS/eco.h
new file mode 100644
index 0000000..92e8d0b
--- /dev/null
+++ b/FICS/eco.h
@@ -0,0 +1,12 @@
+#ifndef _ECO_H
+#define _ECO_H
+
+extern char *boardToFEN();
+extern void ECO_init();
+extern void NIC_init();
+extern void LONG_init();
+extern void BookInit();
+extern int com_eco();
+extern char *getECO();
+
+#endif
diff --git a/FICS/fics_addplayer.c b/FICS/fics_addplayer.c
new file mode 100644
index 0000000..709f741
--- /dev/null
+++ b/FICS/fics_addplayer.c
@@ -0,0 +1,167 @@
+/* fics_addplayer.c
+ *
+ */
+
+/*
+ fics - An internet chess server.
+ Copyright (C) 1994 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name email yy/mm/dd Change
+ Richard Nash 94/03/07 Created
+*/
+
+#include "stdinclude.h"
+
+#include "common.h"
+#include "utils.h"
+#include "playerdb.h"
+/* #include "hostinfo.h" */
+#include "command.h"
+
+extern time_t time();
+extern int printf();
+
+PRIVATE void usage(char *progname)
+{
+ fprintf(stderr, "Usage: %s [-l] [-n] UserName FullName EmailAddress\n", progname);
+ exit(1);
+}
+
+/* Parameters */
+int local = 1;
+char *funame = NULL, *fname = NULL, *email = NULL;
+
+#define PASSLEN 4
+PUBLIC int main(int argc, char *argv[])
+{
+ int i;
+ int p;
+ char password[PASSLEN + 1];
+ char salt[3];
+ char text[2048];
+
+ for (i = 1; i < argc; i++) {
+ if (argv[i][0] == '-') {
+ switch (argv[i][1]) {
+ case 'l':
+ local = 1;
+ break;
+ case 'n':
+ local = 0;
+ break;
+ default:
+ usage(argv[0]);
+ break;
+ }
+ } else {
+ if (!funame)
+ funame = argv[i];
+ else if (!fname)
+ fname = argv[i];
+ else if (!email)
+ email = argv[i];
+ else
+ usage(argv[0]);
+ }
+ }
+ if (!funame || !fname || !email)
+ usage(argv[0]);
+
+ /* Add the player here */
+ if (strlen(funame) >= MAX_LOGIN_NAME) {
+ fprintf(stderr, "Player name is too long\n");
+ exit(0);
+ }
+ if (strlen(funame) < 3) {
+ fprintf(stderr, "Player name is too short\n");
+ exit(0);
+ }
+ if (!alphastring(funame)) {
+ fprintf(stderr, "Illegal characters in player name. Only A-Za-z allowed.\n");
+ exit(0);
+ }
+
+/* loon: see if we can deliver mail to this email address. */
+/*
+ printf("Verifying email address %s...\n", email);
+ if (check_emailaddr(email)) {
+ fprintf(stderr, "The email address %s looks bad.\n", email);
+ exit(0);
+ }
+*/
+/*
+ if (local)
+ iamserver = 0;
+ else
+ iamserver = 1;
+*/
+/* if (hostinfo_init()) {
+ if (iamserver) {
+ fprintf(stderr, "Can't read from hostinfo file.\n");
+ fprintf(stderr, "Remember you need -l for local.\n");
+ exit(1);
+ } else {
+ }
+ } */
+ player_init(0);
+ srand(time(0));
+ p = player_new();
+ if (!player_read(p, funame)) {
+ fprintf(stderr, "%s already exists.\n", funame);
+ exit(0);
+ }
+ parray[p].name = strdup(funame);
+ parray[p].login = strdup(funame);
+ stolower(parray[p].login);
+ parray[p].fullName = strdup(fname);
+ parray[p].emailAddress = strdup(email);
+ for (i = 0; i < PASSLEN; i++) {
+ password[i] = 'a' + rand() % 26;
+ }
+ password[i] = '\0';
+ salt[0] = 'a' + rand() % 26;
+ salt[1] = 'a' + rand() % 26;
+ salt[2] = '\0';
+ parray[p].passwd = strdup(crypt(password, salt));
+ parray[p].registered = 1;
+/* parray[p].network_player = !local; */
+ parray[p].rated = 1;
+ player_save(p);
+/* changed by Sparky 12/15/95 Dropped reference to 'Network' players
+ and spiffed it up */
+/*
+ printf("Added %s player >%s< >%s< >%s< >%s<\n", local ? "local" : "network",
+ funame, fname, email, password);
+ sprintf(text, "\nYou have been added as a %s player.\nIf this is a network account it may take a while to show up on all of the\nclients.\n\nLogin Name: %s\nFull Name: %s\nEmail Address: %s\nInitial Password: %s\n\nIf any of this information is incorrect, please contact the administrator\nto get it corrected.\nPlease write down your password, as it will be your initial passoword\non all of the servers.\n", local ? "local" : "network", funame, fname, email, password);
+*/
+
+ printf("Added player account: >%s< >%s< >%s< >%s<\n",
+ funame, fname, email, password);
+
+ sprintf(text, "\nYour player account has been created.\n\n"
+ "Login Name: %s\nFull Name: %s\nEmail Address: %s\nInitial Password: %s\n\n"
+ "If any of this information is incorrect, please contact the administrator\n"
+ "to get it corrected.\n\n"
+ "You may change your password with the password command on the the server.\n"
+ "\nPlease be advised that if this is an unauthorized duplicate account for\n"
+ "you, by using it you take the risk of being banned from accessing this\n"
+ "chess server.\n\nTo connect to the server and use this account:\n\n"
+ " telnet %s 5000\n\nand enter your handle name and password.\n\n"
+ "Regards,\n\nThe FICS admins\n",
+ funame, fname, email, password, fics_hostname);
+
+ mail_string_to_address(email, "FICS Account Created", text);
+ exit(0);
+}
diff --git a/FICS/ficsmain.c b/FICS/ficsmain.c
new file mode 100644
index 0000000..b1882f3
--- /dev/null
+++ b/FICS/ficsmain.c
@@ -0,0 +1,183 @@
+/* ficsmain.c
+ *
+ */
+
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name email yy/mm/dd Change
+ Richard Nash 93/10/22 Created
+*/
+
+#include "stdinclude.h"
+
+#include "common.h"
+#include "ficsmain.h"
+#include "config.h"
+#include "network.h"
+#include "command.h"
+#include "playerdb.h"
+#include "ratings.h"
+#include "utils.h"
+/*#include "hostinfo.h" */
+#include "board.h"
+#include "talkproc.h"
+#include "comproc.h"
+#include "shutdown.h"
+#ifndef IGNORE_ECO
+#include "eco.h"
+#endif
+#include <sys/param.h>
+
+/* Arguments */
+PUBLIC int port;
+PUBLIC int withConsole;
+
+/* grimm */
+void player_array_init(void);
+/* for warning */
+
+PRIVATE void usage(char *progname)
+{
+ fprintf(stderr, "Usage: %s [-p port] [-C] [-h]\n", progname);
+ fprintf(stderr, "\t\t-p port\t\tSpecify port. (Default=5000)\n");
+ fprintf(stderr, "\t\t-C\t\tStart with console player connected to stdin.\n");
+ fprintf(stderr, "\t\t-h\t\tDisplay this information.\n");
+ exit(1);
+}
+
+PRIVATE void GetArgs(int argc, char *argv[])
+{
+ int i;
+
+ port = DEFAULT_PORT;
+ withConsole = 0;
+
+ for (i = 1; i < argc; i++) {
+ if (argv[i][0] == '-') {
+ switch (argv[i][1]) {
+ case 'p':
+ if (i == argc - 1)
+ usage(argv[0]);
+ i++;
+ if (sscanf(argv[i], "%d", &port) != 1)
+ usage(argv[0]);
+ break;
+ case 'C':
+ fprintf(stderr, "-C Not implemented!\n");
+ exit(1);
+ withConsole = 1;
+ break;
+ case 'h':
+ usage(argv[0]);
+ break;
+ }
+ } else {
+ usage(argv[0]);
+ }
+ }
+}
+
+PRIVATE void main_event_loop(void) {
+int current_socket;
+char command_string[MAX_STRING_LENGTH];
+
+ while (1) {
+ ngc2(command_string, HEARTBEATTIME);
+ if (process_heartbeat(&current_socket) == COM_LOGOUT) {
+ process_disconnection(current_socket);
+ net_close_connection(current_socket);
+ }
+ }
+}
+
+void TerminateServer(int sig)
+{
+ fprintf(stderr, "FICS: Got signal %d\n", sig);
+ output_shut_mess();
+ TerminateCleanup();
+ net_close();
+ exit(1);
+}
+
+void BrokenPipe(int sig)
+{
+ fprintf(stderr, "FICS: Got Broken Pipe\n");
+}
+
+PUBLIC int main(int argc, char *argv[])
+{
+
+#ifdef DEBUG
+#ifdef HAVE_MALLOC_DEBUG
+ malloc_debug(16);
+#endif
+#endif
+ GetArgs(argc, argv);
+ signal(SIGTERM, TerminateServer);
+ signal(SIGINT, TerminateServer);
+ signal(SIGPIPE, BrokenPipe);
+ if (net_init(port)) {
+ fprintf(stderr, "FICS: Network initialize failed on port %d.\n", port);
+ exit(1);
+ }
+ startuptime = time(0);
+/* Using the value defined in config.h now instead of the real hostname.
+ This is used as the return address in email, so it needs to be a real
+ DNS resolvable hostname! Sparky */
+/* gethostname(fics_hostname, 80); */
+ strcpy ( fics_hostname, SERVER_HOSTNAME );
+ quota_time = 60;
+ player_high = 0;
+ game_high = 0;
+ srand(startuptime);
+ fprintf(stderr, "FICS: Initialized on port %d at %s.\n", port, strltime(&startuptime));
+/* iamserver = 0; */
+/* if (hostinfo_init()) {
+ MailGameResult = 0;
+ fprintf(stderr, "FICS: No ratings server connection.\n");
+ } else {
+ if (iamserver) {
+ fprintf(stderr, "FICS: I don't know how to be a ratings server, refusing.\n");
+ MailGameResult = 0;
+ } else {
+ fprintf(stderr, "FICS: Ratings server set to: %s\n", hArray[0].email_address);
+ MailGameResult = 1;
+ }
+ } */
+/* if (mail_log_file)
+ fclose(mail_log_file); */
+ fprintf(stderr, "FICS: commands_init()\n");
+ commands_init();
+/* fprintf(stderr, "FICS: channel_init()\n");
+ channel_init(); */
+ fprintf(stderr, "FICS: rating_init()\n");
+ rating_init();
+ fprintf(stderr, "FICS: wild_init()\n");
+ wild_init();
+#ifndef IGNORE_ECO
+ fprintf(stderr, "FICS: book init()\n");
+ BookInit();
+#endif
+ fprintf(stderr, "FICS: player_array_init()\n");
+ player_array_init();
+ player_init(withConsole);
+ main_event_loop();
+ fprintf(stderr, "FICS: Closing down.\n");
+ output_shut_mess();
+ net_close();
+ return 0;
+}
diff --git a/FICS/ficsmain.c.orig b/FICS/ficsmain.c.orig
new file mode 100644
index 0000000..d8ad0a1
--- /dev/null
+++ b/FICS/ficsmain.c.orig
@@ -0,0 +1,171 @@
+/* ficsmain.c
+ *
+ */
+
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name email yy/mm/dd Change
+ Richard Nash 93/10/22 Created
+*/
+
+#include "stdinclude.h"
+
+#include "common.h"
+#include "ficsmain.h"
+#include "config.h"
+#include "network.h"
+#include "command.h"
+#include "channel.h"
+#include "playerdb.h"
+#include "ratings.h"
+#include "utils.h"
+/*#include "hostinfo.h" */
+#include "board.h"
+#include "talkproc.h"
+#include "comproc.h"
+#include "eco.h"
+
+/* Arguments */
+PUBLIC int port;
+PUBLIC int withConsole;
+
+/* grimm */
+void player_array_init(void);
+/* for warning */
+
+PRIVATE void usage(char *progname)
+{
+ fprintf(stderr, "Usage: %s [-p port] [-C] [-h]\n", progname);
+ fprintf(stderr, "\t\t-p port\t\tSpecify port. (Default=5000)\n");
+ fprintf(stderr, "\t\t-C\t\tStart with console player connected to stdin.\n");
+ fprintf(stderr, "\t\t-h\t\tDisplay this information.\n");
+ exit(1);
+}
+
+PRIVATE void GetArgs(int argc, char *argv[])
+{
+ int i;
+
+ port = DEFAULT_PORT;
+ withConsole = 0;
+
+ for (i = 1; i < argc; i++) {
+ if (argv[i][0] == '-') {
+ switch (argv[i][1]) {
+ case 'p':
+ if (i == argc - 1)
+ usage(argv[0]);
+ i++;
+ if (sscanf(argv[i], "%d", &port) != 1)
+ usage(argv[0]);
+ break;
+ case 'C':
+ fprintf(stderr, "-C Not implemented!\n");
+ exit(1);
+ withConsole = 1;
+ break;
+ case 'h':
+ usage(argv[0]);
+ break;
+ }
+ } else {
+ usage(argv[0]);
+ }
+ }
+}
+
+PRIVATE void main_event_loop(void) {
+int current_socket;
+char command_string[MAX_STRING_LENGTH];
+
+ while (1) {
+ ngc2(command_string, HEARTBEATTIME);
+ if (process_heartbeat(&current_socket) == COM_LOGOUT) {
+ process_disconnection(current_socket);
+ net_close_connection(current_socket);
+ }
+ }
+}
+
+void TerminateServer(int sig)
+{
+ fprintf(stderr, "FICS: Got signal %d\n", sig);
+ TerminateCleanup();
+ net_close();
+ exit(1);
+}
+
+void BrokenPipe(int sig)
+{
+ fprintf(stderr, "FICS: Got Broken Pipe\n");
+}
+
+PUBLIC int main(int argc, char *argv[])
+{
+#ifdef SYSTEM_NEXT
+#ifdef DEBUG
+ malloc_debug(16);
+#endif
+#endif
+ GetArgs(argc, argv);
+ signal(SIGTERM, TerminateServer);
+ signal(SIGINT, TerminateServer);
+ signal(SIGPIPE, BrokenPipe);
+ if (net_init(port)) {
+ fprintf(stderr, "FICS: Network initialize failed on port %d.\n", port);
+ exit(1);
+ }
+ startuptime = time(0);
+ gethostname(fics_hostname, 80);
+ quota_time = 60;
+ player_high = 0;
+ game_high = 0;
+ srand(startuptime);
+ fprintf(stderr, "FICS: Initialized on port %d at %s.\n", port, strltime(&startuptime));
+/* iamserver = 0; */
+/* if (hostinfo_init()) {
+ MailGameResult = 0;
+ fprintf(stderr, "FICS: No ratings server connection.\n");
+ } else {
+ if (iamserver) {
+ fprintf(stderr, "FICS: I don't know how to be a ratings server, refusing.\n");
+ MailGameResult = 0;
+ } else {
+ fprintf(stderr, "FICS: Ratings server set to: %s\n", hArray[0].email_address);
+ MailGameResult = 1;
+ }
+ } */
+/* if (mail_log_file)
+ fclose(mail_log_file); */
+ fprintf(stderr, "FICS: commands_init()\n");
+ commands_init();
+ fprintf(stderr, "FICS: channel_init()\n");
+ channel_init();
+ fprintf(stderr, "FICS: rating_init()\n");
+ rating_init();
+ fprintf(stderr, "FICS: wild_init()\n");
+ wild_init();
+ fprintf(stderr, "FICS: book init()\n");
+ BookInit();
+ fprintf(stderr, "FICS: player_array_init()\n");
+ player_array_init();
+ player_init(withConsole);
+ main_event_loop();
+ fprintf(stderr, "FICS: Closing down.\n");
+ net_close();
+ return 0;
+}
diff --git a/FICS/ficsmain.h b/FICS/ficsmain.h
new file mode 100644
index 0000000..dd7d98d
--- /dev/null
+++ b/FICS/ficsmain.h
@@ -0,0 +1,61 @@
+/* ficsmain.h
+ *
+ */
+
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name email yy/mm/dd Change
+ Richard Nash 93/10/22 Created
+*/
+
+#ifndef _FICSMAIN_H
+#define _FICSMAIN_H
+
+/* Heartbead functions occur approx in this time, including checking for
+ * new connections and decrementing timeleft counters. */
+#define HEARTBEATTIME 2
+
+/* Number of seconds that an idle connection can stand at login or password
+ * prompt. */
+#define MAX_LOGIN_IDLE 120
+
+/* Players who have been idle for more than 1 hour is logged out */
+#define MAX_IDLE_TIME 3600
+
+/* This is the socket that the current command came in on */
+extern int current_socket;
+
+#define DEFAULT_PROMPT "fics% "
+
+#define MESS_WELCOME "welcome"
+#define MESS_LOGIN "login"
+#define MESS_LOGOUT "logout"
+#define MESS_MOTD "motd"
+#define MESS_ADMOTD "admotd"
+#define MESS_UNREGISTERED "unregistered"
+
+#define STATS_MESSAGES "messages"
+#define STATS_LOGONS "logons"
+#define STATS_GAMES "games"
+#define STATS_JOURNAL "journal"
+
+/* Arguments */
+extern int port;
+extern int withConsole;
+extern time_t time();
+
+#endif /* _FICSMAIN_H */
diff --git a/FICS/formula.c b/FICS/formula.c
new file mode 100644
index 0000000..dbdd798
--- /dev/null
+++ b/FICS/formula.c
@@ -0,0 +1,593 @@
+/* Formula program for FICS. Written by Dave Herscovici
+ (dhersco@math.nps.navy.mil)
+ Edited by DAV to include wild,non-std and untimed
+ lightning flags
+
+ Operators allowed, in order of precedence:
+ ! (not), - (unary),
+ *, /,
+ +, -,
+ <, <=, =<, >, >=, =>, (<= and =< are equivalent, as are >= and =>)
+ =, ==, !=, <>, (two ways of saying both equals and not equals)
+ &, &&, (both "and")
+ |, ||, (both "or").
+
+ Parentheses () are also allowed, as is a pound sign '#' for comments.
+ The program will divide by a fudge factor of .001 instead of 0.
+
+ Variables allowed:
+ time, inc, rating, myrating, rated, blitz, standard, lightning, registered,
+ assesswin, assessdraw, assessloss, timeseal,
+ ratingdiff = rating - myrating, private, wild, untimed, nonstandard,
+ maxtime(n) = maximum time n moves will take (in seconds),
+ mymaxtime(n) = same, but just count my time.
+ f1, f2, f3, f4, f5, f6, f7, f8, f9.
+
+ The code for translating blitz and standard variables may have to be
+ redone. F1 through f9 are user-defined formula variables. They can
+ be used to avoid having to retype your entire formula when you want
+ to change one part of it, or to compensate for the lack of a 'mood'
+ variable.
+
+ For example:
+ set f1 rated & time=5 & inc=0 # rated 5 minute games
+ set f2 rating - myrating
+ set f3 # this line is a random comment.
+ set f4 f2>400 # I want a REAL fight
+
+ Then you can type:
+ set formula f1 # rated 5 min. games only
+ set formula etime >= 10 & f2 > -100 # long games, decent competition
+ set formula f1 & !f4
+ or set formula f2 >= 0 | blitz
+ depending on your mood.
+
+ Further modifications could account for starting positions, time
+ odds games, provisional or established opponents, etc. Maybe f0
+ should be reserved for your formula upon logging in; i.e. if f0
+ is set, your formula is automatically set to f0 when you log in.
+*/
+
+#include <ctype.h>
+#include "config.h"
+#include "formula.h"
+#include "playerdb.h"
+#include "gamedb.h"
+#include "command.h"
+#include "common.h"
+#include "stdinclude.h"
+#include "utils.h"
+#include "rmalloc.h"
+#include "lists.h"
+#include "network.h"
+#include "ratings.h"
+
+extern player parray[];
+
+PRIVATE char *GetPlayersFormula (player *pWho, int clause)
+{
+ if (clause==MAX_FORMULA) return (pWho->formula);
+ else return (pWho->formulaLines[clause]);
+}
+
+/* In MoveIndexPastString, *i is treated as the index into string[];
+ this function returns 1 if the character string beginning with
+ string[*i] match *text, and 0 otherwise. In the former case, *i
+ is incremented to move the index past *text.
+*/
+int MoveIndexPastString (char *string, int *i, char *text)
+{
+ int n = strlen(text);
+ if (strncasecmp(text, string + *i, n)) return (0);
+ *i += n;
+ return (n);
+} /* end of function MoveIndexPastString. */
+
+/* GetRating simply chooses between blitz, standard and ratings. */
+int GetRating(player *p, int gametype)
+{
+ if (gametype == TYPE_BLITZ) return (p->b_stats.rating);
+ else if (gametype == TYPE_STAND) return (p->s_stats.rating);
+ else if (gametype == TYPE_WILD) return (p->w_stats.rating);
+ else if (gametype == TYPE_LIGHT) return (p->l_stats.rating);
+ else if (gametype == TYPE_BUGHOUSE) return (p->bug_stats.rating);
+ else return 0;
+} /* end of function GetRating. */
+
+int GetNumberInsideParens (game *g, int clause, int *i, int *token,
+ int eval)
+{
+ char *string = GetPlayersFormula(&parray[g->black], clause);
+ int ret;
+
+ while (string[*i] != '\0' && isspace(string[*i])) (*i)++;
+ if (!MoveIndexPastString(string, i, "(")) return (ERR_BADCHAR);
+
+ ret = CheckFormula(g, clause, i, OPTYPE_PAREN, token, eval);
+ if (ret != ERR_NONE)
+ return (ret);
+ if (MoveIndexPastString(string, i, ")")) return (ERR_NONE);
+ else return (ERR_PAREN);
+}
+
+int Maxtime (game *g, int numMoves, int numPlayers)
+{
+ int max;
+
+ if ((g->bInitTime == g->wInitTime) && (g->bIncrement == g->wIncrement)) {
+ max = numPlayers * (60 * g->wInitTime + numMoves * g->wIncrement);
+ if ((g->type != TYPE_UNTIMED) && (g->wInitTime == 0))
+ max +=10 * numPlayers; /* 0 x start with ten secs */
+
+ } else if (numPlayers == 2) {
+ max = 60 * (g->wInitTime + g->bInitTime)
+ + numMoves * (g->wIncrement + g->bIncrement);
+ if ((g->type != TYPE_UNTIMED) && (g->wInitTime == 0))
+ max +=10; /* 0 x start with ten secs */
+ if ((g->type != TYPE_UNTIMED) && (g->bInitTime == 0))
+ max +=10; /* 0 x start with ten secs */
+
+ } else {
+ max = 60 * g->bInitTime + numMoves * g->bIncrement;
+ if ((g->type != TYPE_UNTIMED) && (g->bInitTime == 0))
+ max +=10; /* 0 x start with ten secs */
+ }
+
+ return max;
+}
+
+
+/* The black player in game *g has been challenged. S/he has a list
+ of formulas, and we're checking the one given by the index clause
+ (MAX_FORMULA denotes the actual formula; if clause is smaller,
+ we're looking at one of the user-defined formulas). We're at
+ position *i in the formula. If we find a legitimate variable there
+ and eval is 1, VarToToken puts its value in *tok; if eval is 0,
+ just move past the variable. Returns 1 or 0, depending on whether
+ a legitimate variable was found.
+*/
+int VarToToken (game *g, int clause, char *string, int *i, int *tok, int eval)
+{
+ player *me = &parray[g->black];
+ player *you = &parray[g->white];
+ int index=0, c;
+ double dummy_sterr;
+
+ /* We list possible variables with the longest names first. */
+ if (MoveIndexPastString(string, i, "registered")) *tok=you->registered;
+ else if (MoveIndexPastString(string, i, "ratingdiff"))
+ *tok = GetRating(you, g->type) - GetRating(me, g->type);
+ else if (MoveIndexPastString(string, i, "assessloss"))
+ {
+ if (g->rated)
+ rating_sterr_delta(g->black, g->white, g->type,
+ time(0), RESULT_LOSS, tok, &dummy_sterr);
+ else *tok = 0;
+ }
+ else if (MoveIndexPastString(string, i, "assessdraw"))
+ {
+ if (g->rated)
+ rating_sterr_delta(g->black, g->white, g->type,
+ time(0), RESULT_DRAW, tok, &dummy_sterr);
+ else *tok = 0;
+ }
+ else if (MoveIndexPastString(string, i, "assesswin"))
+ {
+ if (g->rated)
+ rating_sterr_delta(g->black, g->white, g->type,
+ time(0), RESULT_WIN, tok, &dummy_sterr);
+ else *tok = 0;
+ }
+ else if (MoveIndexPastString(string, i, "mymaxtime"))
+ {
+ if (GetNumberInsideParens(g, clause, i, tok, eval) != ERR_NONE)
+ return (0);
+ *tok = Maxtime (g, *tok, 1);
+ }
+#ifdef TIMESEAL
+ else if (MoveIndexPastString(string, i, "timeseal"))
+ *tok = con[you->socket].timeseal;
+#endif
+ else if (MoveIndexPastString(string, i, "myrating"))
+ *tok = GetRating(me, g->type);
+ else if (MoveIndexPastString(string, i, "computer"))
+ *tok = in_list(-1, L_COMPUTER, you->name);
+ else if (MoveIndexPastString(string, i, "standard"))
+ *tok = (g->type == TYPE_STAND);
+ else if (MoveIndexPastString(string, i, "private"))
+ *tok = you->private;
+ else if (MoveIndexPastString(string, i, "maxtime"))
+ {
+ if (GetNumberInsideParens(g, clause, i, tok, eval) != ERR_NONE)
+ return (0);
+ *tok = Maxtime (g, *tok, 2);
+ }
+ else if (MoveIndexPastString(string, i, "abuser"))
+ *tok = in_list(-1, L_ABUSER, you->name);
+ else if (MoveIndexPastString(string, i, "rating"))
+ *tok = GetRating(you, g->type);
+ else if (MoveIndexPastString(string, i, "rated")) *tok=g->rated;
+ else if (MoveIndexPastString(string, i, "blitz")) *tok = (g->type == TYPE_BLITZ);
+ else if (MoveIndexPastString(string, i, "wild")) *tok = (g->type == TYPE_WILD);
+ else if (MoveIndexPastString(string, i, "lightning")) *tok = (g->type == TYPE_LIGHT);
+ else if (MoveIndexPastString(string, i, "nonstandard")) *tok = (g->type == TYPE_NONSTANDARD);
+ else if (MoveIndexPastString(string, i, "untimed")) *tok = (g->type == TYPE_UNTIMED);
+ else if (MoveIndexPastString(string, i, "time")) *tok=g->wInitTime;
+ else if (MoveIndexPastString(string, i, "inc")) *tok=g->wIncrement;
+ else if (tolower(string[*i])=='f' && isdigit(string[*i+1]) &&
+ (c = (string[*i+1]-'1')) >= 0 && clause > c)
+ {
+ *i += 2;
+ return (!CheckFormula (g, c, &index, OPTYPE_NONE, tok, eval));
+ }
+ else return (0);
+ return (1);
+} /* end of function VarToToken. */
+
+/* ScanForOp checks for an operator at position *i in string[]. */
+int ScanForOp (char *string, int *i)
+{
+ while (string[*i] != '\0' && isspace(string[*i])) (*i)++;
+
+ if (string[*i] == '\0') return (OP_NONE);
+ if (string[*i] == '#') return (OP_NONE);
+ if (string[*i] == ')') return (OP_RTPAREN);
+
+ /* Two char operators and longer first. */
+ if (MoveIndexPastString(string, i, "and")) return (OP_AND);
+ if (MoveIndexPastString(string, i, "or")) return (OP_OR);
+ if (MoveIndexPastString(string, i, "||")) return (OP_OR);
+ if (MoveIndexPastString(string, i, "&&")) return (OP_AND);
+ if (MoveIndexPastString(string, i, "==")) return (OP_EQ);
+ if (MoveIndexPastString(string, i, "!=")) return (OP_NEQ);
+ if (MoveIndexPastString(string, i, "<>")) return (OP_NEQ);
+ if (MoveIndexPastString(string, i, ">=")) return (OP_GE);
+ if (MoveIndexPastString(string, i, "=>")) return (OP_GE);
+ if (MoveIndexPastString(string, i, "<=")) return (OP_LE);
+ if (MoveIndexPastString(string, i, "=<")) return (OP_LE);
+
+ /* One char operators now. */
+ if (MoveIndexPastString(string, i, "|")) return (OP_OR);
+ if (MoveIndexPastString(string, i, "&")) return (OP_AND);
+ if (MoveIndexPastString(string, i, ">")) return (OP_GT);
+ if (MoveIndexPastString(string, i, "<")) return (OP_LT);
+ if (MoveIndexPastString(string, i, "=")) return (OP_EQ);
+ if (MoveIndexPastString(string, i, "+")) return (OP_PLUS);
+ if (MoveIndexPastString(string, i, "-")) return (OP_MINUS);
+ if (MoveIndexPastString(string, i, "*")) return (OP_MULT);
+ if (MoveIndexPastString(string, i, "/")) return (OP_DIV);
+ return (OP_BAD);
+} /* end of function ScanForOp. */
+
+/* OpType returns the precedence of the operator op. */
+int OpType (int op)
+{
+ switch (op)
+ {
+ case OP_BAD: return (OPTYPE_BAD);
+ case OP_NONE: return (OPTYPE_NONE);
+ case OP_RTPAREN: return (OPTYPE_RPAREN);
+ case OP_OR: return (OPTYPE_OR);
+ case OP_AND: return (OPTYPE_AND);
+ case OP_EQ: case OP_NEQ: return (OPTYPE_COMPEQ);
+ case OP_GT: case OP_GE: case OP_LT: case OP_LE: return (OPTYPE_COMPGL);
+ case OP_PLUS: case OP_MINUS: return (OPTYPE_ADD);
+ case OP_MULT: case OP_DIV: return (OPTYPE_MULT);
+ case OP_NEGATE: case OP_NOT: return (OPTYPE_UNARY);
+ default: return (OPTYPE_BAD);
+ }
+} /* end of function OpType. */
+
+/* In EvalBinaryOp, *left is the left operand; and op is the
+ current operator. *g and clause specify which formula string to
+ look at (we're checking parray[g->black].formulaLines[clause]),
+ and *i tells us where we are in the string. We look for a right
+ operand from position *i in the string, and place the expression
+ (*left op right) in *left. For example, if *left=6, op =
+ OP_MULT, and we pull off right = 4, we replace *left with
+ 6*4=24. Returns 0 if no error; otherwise indicates the error.
+*/
+int EvalBinaryOp (int *left, int op, game *g, int clause, int *i)
+{
+ int right, ret;
+ if ((op==OP_OR) && (*left != 0)) /* Nothing to decide. */
+ {
+ *left = 1;
+ return (CheckFormula (g, clause, i, OpType(op), &right, 0));
+ }
+ else if ((op==OP_AND) && (*left == 0)) /* Nothing to decide. */
+ return (CheckFormula (g, clause, i, OpType(op), &right, 0));
+
+ else {
+ ret = CheckFormula (g, clause, i, OpType(op), &right, 1);
+ if (ret != ERR_NONE) return (ret);
+ }
+ switch (op)
+ {
+ default: case OP_BAD: return (ERR_BADOP);
+ case OP_NONE: case OP_RTPAREN: return (ERR_NONE);
+ case OP_OR: *left = (*left || right); return (ERR_NONE);
+ case OP_AND: *left = (*left && right); return (ERR_NONE);
+ case OP_EQ: *left = (*left == right); return (ERR_NONE);
+ case OP_NEQ: *left = (*left != right); return (ERR_NONE);
+ case OP_GT: *left = (*left > right); return (ERR_NONE);
+ case OP_GE: *left = (*left >= right); return (ERR_NONE);
+ case OP_LT: *left = (*left < right); return (ERR_NONE);
+ case OP_LE: *left = (*left <= right); return (ERR_NONE);
+ case OP_PLUS: *left += right; return (ERR_NONE);
+ case OP_MINUS: *left -= right; return (ERR_NONE);
+ case OP_MULT: *left *= right; return (ERR_NONE);
+ case OP_DIV:
+ if (right != 0)
+ {
+ *left /= right;
+ return (ERR_NONE);
+ }
+ else
+ {
+ pprintf(g->black, "Dividing by %lf instead or zero in formula.\n",
+FUDGE_FACTOR);
+ *left /= FUDGE_FACTOR;
+ return (ERR_NONE);
+ }
+ }
+} /* end of function EvalBinaryOp. */
+
+/* If eval is 1, ScanForNumber evaluates the number at position *i
+ in the formula given by *g and clause, and place this value in
+ *token. op_type is the precedence of the operator preceding the
+ *i'th position. If we come to an operator of higher precedence,
+ we must keep going before we can leave this function. If eval
+ is 0, just move past the number we would evaluate. Returns 0 if
+ no error; otherwise return code indicates the error.
+*/
+int ScanForNumber (game *g, int clause, int *i, int op_type,
+ int *token, int eval)
+{
+ char *string = GetPlayersFormula (&parray[g->black], clause);
+ char c;
+ int ret;
+
+ while ((string[*i] != '\0') && (isspace(string[*i]))) (*i)++;
+ switch (c = string[*i])
+ {
+ case '\0': case '#':
+ if (op_type != OPTYPE_NONE) return (ERR_EOF);
+ else *token=1;
+ return (ERR_NONE);
+
+ case ')':
+ if (op_type != OPTYPE_PAREN) return (ERR_PAREN);
+ else *token=1;
+ return(ERR_NONE);
+
+ case '(': return GetNumberInsideParens(g,clause,i,token,eval);
+
+ case '-': case '!':
+ ++(*i);
+ if (c==string[*i]) return(ERR_UNARY);
+ ret = ScanForNumber(g,clause,i,OPTYPE_UNARY,token,eval);
+ if (ret != ERR_NONE)
+ return (ret);
+ if (c == '-') *token = -(*token);
+ else if (c == '!') *token = !(*token);
+ return (ERR_NONE);
+
+ default:
+ if (isdigit(string[*i]))
+ {
+ *token = 0;
+
+ do *token = 10 * (*token) + string[(*i)++] - '0';
+ while (isdigit(string[*i]));
+
+ while (string[*i] != '\0' && isspace(string[*i])) (*i)++;
+ if (MoveIndexPastString (string, i, "minutes"))
+ *token *= 60;
+ return (ERR_NONE);
+ }
+ else if (isalpha(string[*i]))
+ {
+ if (!VarToToken (g, clause, string, i, token, eval))
+ return (ERR_BADVAR);
+ return(ERR_NONE);
+ }
+ else return (ERR_NONESENSE);
+ }
+} /* end of function ScanForNumber. */
+
+/* If eval is 1, CheckFormula looks for the next token in the
+ formula given by *g, clause, and *i; usually this is the right
+ side of an expression whose operator has precedence OpType; if
+ eval is 0, just go to the end of an expression. Return 0 if no
+ error; otherwise, return error type.
+*/
+int CheckFormula (game *g, int clause, int *i, int op_type,
+ int *result, int eval)
+{
+ int token, ret, nextOp, lastPos;
+ char *string = GetPlayersFormula (&parray[g->black], clause);
+
+ if (string == NULL)
+ {
+ *result = 1;
+ return (ERR_NONE);
+ }
+ ret = ScanForNumber (g, clause, i, op_type, &token, eval);
+ if (ret != ERR_NONE)
+ return (ret);
+ lastPos = *i;
+ nextOp = ScanForOp (string, i);
+ while (OpType(nextOp) > op_type) /* higher precedence. */
+ {
+ if (nextOp==OP_RTPAREN) return (ERR_PAREN);
+ if (!eval)
+ ret = CheckFormula(g, clause, i, OpType(nextOp), &token, 0);
+ else ret = EvalBinaryOp (&token, nextOp, g, clause, i);
+ if (ret != ERR_NONE) return (ret);
+ lastPos = *i;
+ nextOp = ScanForOp (string, i);
+ }
+ if (nextOp == OP_BAD) return(ERR_BADOP);
+ *result = token;
+
+ /* move back unless we're at the end or at a right paren, in which
+ case we never went forward. */
+ if (nextOp != OP_NONE && nextOp != OP_RTPAREN) *i=lastPos;
+ return (ERR_NONE);
+} /* end of function CheckFormula. */
+
+/* which clauses are relevant for a player's formula. */
+PRIVATE int ChooseClauses(player *who, char *formula)
+{
+ int i, which, ret=0;
+
+ if (formula == NULL)
+ return ret;
+ for (i=0; formula[i] != '\0' && formula[i] != '#'; i++) {
+ if (formula[i] != 'f' || (i > 0 && isalnum(formula[i-1]))
+ || !isdigit(formula[i+1]))
+ continue;
+ sscanf(&formula[i], "f%d", &which);
+ ret |= (1 << (which - 1));
+ }
+ /* now scan clauses found as part of the formula. */
+ for (i = MAX_FORMULA - 1; i >= 0; i--) {
+ if (ret & (1 << i)) {
+ ret |= ChooseClauses(who, who->formulaLines[i]);
+ }
+ }
+ return ret;
+} /* end of function ChooseClauses. */
+
+void ExplainFormula (game *g, textlist **clauses)
+{
+ int i, which, dummy_index, value;
+ char txt[20];
+ player *challenged = &parray[g->black];
+ textlist **Cur = clauses;
+
+ which = ChooseClauses(challenged, challenged->formula);
+
+ for (i = 0; i < MAX_FORMULA; i++) {
+ if ((which & (1 << i)) == 0)
+ continue;
+ dummy_index = 0;
+ CheckFormula (g, i, &dummy_index, OPTYPE_NONE, &value, 1);
+ sprintf(txt, "%d", value);
+ SaveTextListEntry (Cur, txt, i);
+ Cur = &(*Cur)->next;
+ }
+}
+
+#if 0
+void ExplainFormula (game *g, textlist *eval)
+{
+ int i, newPos, value;
+ char Head[MAX_STRING_LENGTH],
+ Tail[MAX_STRING_LENGTH];
+
+ strcpy (strRet, GetPlayersFormula(&parray[g->black], clause));
+ for (i=0; strRet[i] != '\0'; i++)
+ {
+ if (strRet[i] == '#')
+ {
+ strRet[i] = '\0';
+ break;
+ }
+ if (!isalpha(strRet[i])) continue;
+ newPos = i;
+ if (!VarToToken(g, clause, strRet, &newPos, &value, 1))
+ i = newPos;
+ else
+ {
+ strcpy (Head, strRet);
+ strcpy (Tail, strRet + newPos);
+ Head[i] = '\0';
+
+ sprintf(strRet, "%s%d%s", Head, value, Tail);
+ while (isalpha(strRet[i])) i++;
+ }
+ }
+}
+#endif
+
+/* GameMatchesFormula converts parameters to a game structure
+ and passes a pointer to this game to CheckFormula for
+ evaluation. It returns the evaluated formula.
+*/
+PUBLIC int GameMatchesFormula (int w, int b, int wTime, int wInc, int bTime,
+ int bInc, int rated, int gametype, textlist **clauseList)
+{
+ game g;
+ int index=0, ret;
+
+ g.white = w; g.black = b;
+ g.wInitTime = wTime; g.bInitTime = bTime;
+ g.wIncrement = wInc; g.bIncrement = bInc;
+ g.rated = rated;
+ g.type = gametype;
+
+ if (CheckFormula(&g, MAX_FORMULA, &index, OPTYPE_NONE, &ret, 1) != ERR_NONE)
+ return (0);
+ if (ret == 0) ExplainFormula (&g, clauseList);
+ return (ret);
+} /* end of function GameMatchesFormula. */
+
+/* SetValidFormula sets a clause of player p and creates a game structure
+ to check whether that new formula is legitimate. If so, return 1;
+ otherwise, reset the formula and return 0.
+*/
+int SetValidFormula (int p, int clause, char *string)
+{
+ game g;
+ int index=0, ret, err=ERR_NONE;
+ char *Old=NULL, **Cur;
+ player *me = &parray[p];
+
+ if (clause==MAX_FORMULA)
+ Cur = &me->formula;
+ else
+ Cur = &me->formulaLines[clause];
+
+ Old = *Cur;
+
+ if (string != NULL) {
+ string = eatwhite(string);
+ *Cur = (*string != '\0' ? strdup (string) : NULL);
+ }
+ else *Cur = NULL;
+
+ if (*Cur==NULL) {
+ if (Old != NULL) rfree(Old);
+ return 1;
+ }
+
+ g.white = g.black = p;
+ g.wInitTime = g.bInitTime = me->d_time;
+ g.wIncrement = g.bIncrement = me->d_inc;
+ g.rated = me->rated;
+ g.type = TYPE_BLITZ;
+ err = CheckFormula (&g, clause, &index, OPTYPE_NONE, &ret, 0);
+
+ if (err != ERR_NONE) {
+ /* Bad formula; reset it. */
+ rfree(*Cur);
+ *Cur = Old;
+ } else {
+ if (Old != NULL) rfree(Old);
+ }
+ return (err == ERR_NONE);
+}
+
+PUBLIC void ShowClauses (int p, int p1, textlist *clauses)
+{
+ textlist *Cur;
+
+ if (clauses != NULL)
+ pprintf(p, "\n");
+ for (Cur = clauses; Cur != NULL; Cur = Cur->next) {
+ pprintf(p, "f%d=%s: %s\n", Cur->index + 1, Cur->text,
+ parray[p1].formulaLines[Cur->index]);
+ }
+}
diff --git a/FICS/formula.h b/FICS/formula.h
new file mode 100644
index 0000000..17417c5
--- /dev/null
+++ b/FICS/formula.h
@@ -0,0 +1,55 @@
+#ifndef FORMULA_H
+#define FORMULA_H
+
+#define OP_BAD -1
+#include "playerdb.h"
+#include "gamedb.h"
+
+#define OP_NONE 0
+#define OP_RTPAREN 1
+#define OP_OR 2
+#define OP_AND 3
+#define OP_EQ 4
+#define OP_NEQ 5
+#define OP_GT 6
+#define OP_GE 7
+#define OP_LT 8
+#define OP_LE 9
+#define OP_PLUS 10
+#define OP_MINUS 11
+#define OP_MULT 12
+#define OP_DIV 13
+#define OP_NEGATE 14
+#define OP_NOT 15
+
+#define OPTYPE_BAD 0
+#define OPTYPE_NONE 1
+#define OPTYPE_RPAREN 2
+#define OPTYPE_PAREN 3
+#define OPTYPE_OR 4
+#define OPTYPE_AND 5
+#define OPTYPE_COMPEQ 6
+#define OPTYPE_COMPGL 7
+#define OPTYPE_ADD 8
+#define OPTYPE_MULT 9
+#define OPTYPE_UNARY 10
+
+#define ERR_NONE 0
+#define ERR_EOF 1
+#define ERR_UNARY 2
+#define ERR_BADCHAR 3
+#define ERR_BADVAR 4
+#define ERR_BADOP 5
+#define ERR_NONESENSE 6
+#define ERR_DIVBYZERO 7
+#define ERR_PAREN 8
+
+#define FUDGE_FACTOR 1e-3
+
+extern int GetRating (player *, int);
+extern int GameMatchesFormula (int, int, int, int, int, int, int, int, textlist **);
+extern int CheckFormula (game *, int, int *, int, int *, int);
+extern void ShowClauses (int, int, textlist *);
+
+#endif
+
diff --git a/FICS/gamedb.c b/FICS/gamedb.c
new file mode 100644
index 0000000..03c677d
--- /dev/null
+++ b/FICS/gamedb.c
@@ -0,0 +1,1795 @@
+/* gamedb.c
+ *
+ */
+
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name email yy/mm/dd Change
+ Richard Nash 93/10/22 Created
+*/
+
+#include "stdinclude.h"
+
+#include "common.h"
+#include "ficsmain.h"
+#include "config.h"
+#include "gamedb.h"
+#include "playerdb.h"
+#include "gameproc.h"
+#include "command.h"
+#include "utils.h"
+#include "rmalloc.h"
+#include "eco.h"
+#include "network.h"
+
+PUBLIC game *garray = NULL;
+PUBLIC int g_num = 0;
+
+PRIVATE int get_empty_slot()
+/* this method is awful! how about allocation as we need it and freeing
+ afterwards! */
+{
+ int i;
+
+ for (i = 0; i < g_num; i++) {
+ if (garray[i].status == GAME_EMPTY)
+ return i;
+ }
+ g_num++;
+ if (!garray) {
+ garray = (game *) rmalloc(sizeof(game) * g_num);
+ } else {
+ garray = (game *) rrealloc(garray, sizeof(game) * g_num);
+ } /* yeah great, bet this causes lag! - DAV*/
+ garray[g_num - 1].status = GAME_EMPTY;
+ return g_num - 1;
+}
+
+PUBLIC int game_new()
+{
+ int new = get_empty_slot();
+ game_zero(new);
+ return new;
+}
+
+PUBLIC int game_zero(int g)
+{
+ garray[g].white = -1;
+ garray[g].black = -1;
+/* garray[g].old_white = -1;
+ garray[g].old_black = -1;
+*/
+ garray[g].status = GAME_NEW;
+ garray[g].link = -1;
+ garray[g].rated = 0;
+ garray[g].private = 0;
+ garray[g].result = END_NOTENDED;
+ garray[g].type = TYPE_UNTIMED;
+ garray[g].passes = 0;
+ board_init(&garray[g].game_state, NULL, NULL);
+ garray[g].game_state.gameNum = g;
+ garray[g].numHalfMoves = 0;
+ garray[g].moveListSize = 0;
+ garray[g].moveList = NULL;
+ garray[g].examMoveListSize = 0;
+ garray[g].examMoveList = NULL;
+ garray[g].wInitTime = 300; /* 5 minutes */
+ garray[g].wIncrement = 0;
+ garray[g].bInitTime = 300; /* 5 minutes */
+ garray[g].bIncrement = 0;
+#ifdef TIMESEAL
+ garray[g].flag_pending = FLAG_NONE;
+ garray[g].flag_check_time = 0L;
+#endif
+ garray[g].white_name[0] = '\0';
+ garray[g].black_name[0] = '\0';
+ garray[g].white_rating = 0;
+ garray[g].black_rating = 0;
+ garray[g].revertHalfMove = 0;
+ return 0;
+}
+
+PUBLIC int game_free(int g)
+{
+ if (garray[g].moveListSize)
+ rfree(garray[g].moveList);
+ if (garray[g].examMoveListSize)
+ rfree(garray[g].examMoveList);
+ garray[g].moveListSize = 0;
+ garray[g].examMoveListSize = 0;
+ return 0;
+}
+
+PUBLIC int game_clear(int g)
+{
+ game_free(g);
+ game_zero(g);
+ return 0;
+}
+
+PUBLIC int game_remove(int g)
+{
+ /* Should remove game from players observation list */
+ game_clear(g);
+ garray[g].status = GAME_EMPTY;
+ return 0;
+}
+
+/* old moves not stored now - uses smoves */
+PUBLIC int game_finish(int g)
+{
+ player_game_ended(g); /* Alert playerdb that game ended */
+/* NewOldGame(g); */
+/* game_free(g) */
+ game_remove(g);
+ return 0;
+}
+
+#if 0
+PUBLIC void PosToBoardList (int g)
+{
+ if (garray[g].numHalfMoves >= garray[g].boardListSize) {
+ garray[g].boardListSize += 50;
+ if (garray[g].boardListSize <= 50)
+ garray[g].boardList =
+ (boardList_t *) rmalloc(garray[g].boardListSize * sizeof(boardList_t));
+ else
+ garray[g].boardList = (boardList_t *) rrealloc (garray[g].boardList,
+ garray[g].boardListSize * sizeof(boardList_t));
+ }
+ if (garray[g].boardList != NULL)
+ strcpy(garray[g].boardList[garray[g].numHalfMoves], boardToFEN(g));
+ else
+ fprintf(stderr, "Board List could not be allocated for game %d.\n", g+1);
+}
+#endif
+
+PUBLIC void MakeFENpos (int g, char *FEN)
+{
+ strcpy(FEN, boardToFEN(g));
+}
+
+PUBLIC char *game_time_str(int wt, int winc, int bt, int binc)
+{
+ static char tstr[50];
+
+ if ((!wt) && (!winc)) { /* Untimed */
+ strcpy(tstr, "");
+ return tstr;
+ }
+ if ((wt == bt) && (winc == binc)) {
+ sprintf(tstr, " %d %d", wt, winc);
+ } else {
+ sprintf(tstr, " %d %d : %d %d", wt, winc, bt, binc);
+ }
+ return tstr;
+}
+
+PUBLIC char *bstr[] = {"untimed", "blitz", "standard", "non-standard", "wild", "lightning", "bughouse"};
+PUBLIC char *rstr[] = {"unrated", "rated"};
+
+PUBLIC char *game_str(int rated, int wt, int winc, int bt, int binc,
+ char *cat, char *board)
+{
+ static char tstr[200];
+
+ if (cat && cat[0] && board && board[0] &&
+ (strcmp(cat, "standard") || strcmp(board, "standard"))) {
+ sprintf(tstr, "%s %s%s Loaded from %s/%s",
+ rstr[rated],
+ bstr[game_isblitz(wt / 60, winc, bt / 60, binc, cat, board)],
+ game_time_str(wt / 60, winc, bt / 60, binc),
+ cat, board);
+ } else {
+ sprintf(tstr, "%s %s%s",
+ rstr[rated],
+ bstr[game_isblitz(wt / 60, winc, bt / 60, binc, cat, board)],
+ game_time_str(wt / 60, winc, bt / 60, binc));
+ }
+ return tstr;
+}
+
+PUBLIC int game_isblitz(int wt, int winc, int bt, int binc,
+ char *cat, char *board)
+{
+ int total;
+
+ if (cat && cat[0] && board && board[0] && (!strcmp(cat, "wild")))
+ return TYPE_WILD;
+ if (cat && cat[0] && board && board[0] &&
+ (strcmp(cat, "standard") || strcmp(board, "standard")))
+ return TYPE_NONSTANDARD;
+ if (((wt == 0) && (winc == 0)) || ((bt == 0) && (binc == 0)))
+ /* nonsense if one is timed and one is not */
+ return TYPE_UNTIMED;
+ if ((wt != bt) || (winc != binc))
+ return TYPE_NONSTANDARD;
+ total = wt * 60 + winc * 40;
+ if (total < 180) /* 3 minute */
+ return TYPE_LIGHT;
+ if (total >= 900) /* 15 minutes */
+ return TYPE_STAND;
+ else
+ return TYPE_BLITZ;
+}
+
+PUBLIC void send_board_to(int g, int p)
+{
+ char *b;
+ int side;
+ int relation;
+
+/* since we know g and p, figure out our relationship to this game */
+
+ side = WHITE;
+ if (garray[g].status == GAME_EXAMINE) {
+ if (parray[p].game == g) {
+ relation = 2;
+ } else {
+ relation = -2;
+ }
+ } else {
+ if (parray[p].game == g) {
+ side = parray[p].side;
+ relation = ((side == garray[g].game_state.onMove) ? 1 : -1);
+ } else {
+ relation = 0;
+ }
+ }
+
+ if (parray[p].flip) { /* flip board? */
+ if (side == WHITE)
+ side = BLACK;
+ else
+ side = WHITE;
+ }
+ game_update_time(g);
+ b = board_to_string(garray[g].white_name,
+ garray[g].black_name,
+ garray[g].wTime,
+ garray[g].bTime,
+ &garray[g].game_state,
+ (garray[g].status == GAME_EXAMINE) ?
+ garray[g].examMoveList : garray[g].moveList,
+ parray[p].style,
+ side, relation, p);
+
+#ifdef TIMESEAL
+
+ if (con[parray[p].socket].timeseal) {
+ if (parray[p].bell) {
+ pprintf_noformat(p, "\007\n[G]\n%s", b);
+ } else {
+ pprintf_noformat(p, "\n[G]\n%s", b);
+ }
+ } else {
+ if (parray[p].bell) {
+ pprintf_noformat(p, "\007\n%s", b);
+ } else {
+ pprintf_noformat(p, "\n%s", b);
+ }
+ }
+
+#else
+
+ if (parray[p].bell) {
+ pprintf_noformat(p, "\007\n%s", b);
+ } else {
+ pprintf_noformat(p, "\n%s", b);
+ }
+
+#endif
+
+ if (p != commanding_player) {
+ pprintf(p, "%s", parray[p].prompt);
+ }
+}
+
+PUBLIC void send_boards(int g)
+{
+ int p;
+ simul_info_t *simInfo = &parray[garray[g].white].simul_info;
+
+ if (simInfo->numBoards == 0 || simInfo->boards[simInfo->onBoard] == g)
+ for (p = 0; p < p_num; p++) {
+ if (parray[p].status == PLAYER_EMPTY)
+ continue;
+ if (player_is_observe(p, g) || (parray[p].game == g))
+ send_board_to(g, p);
+ }
+}
+
+PUBLIC void game_update_time(int g)
+{
+ unsigned now, timesince;
+
+ if (garray[g].clockStopped)
+ return;
+ if (garray[g].type == TYPE_UNTIMED)
+ return;
+ now = tenth_secs();
+ timesince = now - garray[g].lastDecTime;
+ if (garray[g].game_state.onMove == WHITE) {
+ garray[g].wTime -= timesince;
+ } else {
+ garray[g].bTime -= timesince;
+ }
+ garray[g].lastDecTime = now;
+}
+
+PUBLIC void game_update_times()
+{
+ int g;
+
+ for (g = 0; g < g_num; g++) {
+ if (garray[g].status != GAME_ACTIVE)
+ continue;
+ if (garray[g].clockStopped)
+ continue;
+ game_update_time(g);
+ }
+}
+
+#if 0 /* oldgame is obsolete - games are stored in history */
+PRIVATE int oldGameArray[MAXOLDGAMES];
+PRIVATE int numOldGames = 0;
+
+PRIVATE int RemoveOldGame(int g)
+{
+ int i;
+
+ for (i = 0; i < numOldGames; i++) {
+ if (oldGameArray[i] == g)
+ break;
+ }
+ if (i == numOldGames)
+ return -1; /* Not found! */
+ for (; i < numOldGames - 1; i++)
+ oldGameArray[i] = oldGameArray[i + 1];
+ numOldGames--;
+ game_remove(g);
+ return 0;
+}
+
+PRIVATE int AddOldGame(int g)
+{
+ if (numOldGames == MAXOLDGAMES) /* Remove the oldest */
+ RemoveOldGame(oldGameArray[0]);
+ oldGameArray[numOldGames] = g;
+ numOldGames++;
+ return 0;
+}
+
+PUBLIC int FindOldGameFor(int p)
+{
+ int i;
+
+ if (p == -1)
+ return numOldGames - 1;
+ for (i = numOldGames - 1; i >= 0; i--) {
+ if (garray[oldGameArray[i]].old_white == p)
+ return oldGameArray[i];
+ if (garray[oldGameArray[i]].old_black == p)
+ return oldGameArray[i];
+ }
+ return -1;
+}
+
+/* This just removes the game if both players have new-old games */
+PUBLIC int RemoveOldGamesForPlayer(int p)
+{
+ int g;
+
+ g = FindOldGameFor(p);
+ if (g < 0)
+ return 0;
+ if (garray[g].old_white == p)
+ garray[g].old_white = -1;
+ if (garray[g].old_black == p)
+ garray[g].old_black = -1;
+ if ((garray[g].old_white == -1) && (garray[g].old_black == -1)) {
+ RemoveOldGame(g);
+ }
+ return 0;
+}
+
+/* This recycles any old games for players who disconnect */
+PUBLIC int ReallyRemoveOldGamesForPlayer(int p)
+{
+ int g;
+
+ g = FindOldGameFor(p);
+ if (g < 0)
+ return 0;
+ RemoveOldGame(g);
+ return 0;
+}
+
+PUBLIC int NewOldGame(int g)
+{
+ RemoveOldGamesForPlayer(garray[g].white);
+ RemoveOldGamesForPlayer(garray[g].black);
+ garray[g].old_white = garray[g].white;
+ garray[g].old_black = garray[g].black;
+ garray[g].status = GAME_STORED;
+ AddOldGame(g);
+ return 0;
+}
+#endif
+
+PUBLIC char *EndString(int g, int personal)
+{
+/* personal 0 == White checkmated; personal 1 == loon checkmated */
+
+ static char endstr[200];
+ char *blackguy, *whiteguy;
+ static char blackstr[] = "Black";
+ static char whitestr[] = "White";
+
+ blackguy = (personal ? garray[g].black_name : blackstr);
+ whiteguy = (personal ? garray[g].white_name : whitestr);
+
+ switch (garray[g].result) {
+ case END_CHECKMATE:
+ sprintf(endstr, "%s checkmated",
+ garray[g].winner == WHITE ? blackguy : whiteguy);
+ break;
+ case END_RESIGN:
+ sprintf(endstr, "%s resigned",
+ garray[g].winner == WHITE ? blackguy : whiteguy);
+ break;
+ case END_FLAG:
+ sprintf(endstr, "%s ran out of time",
+ garray[g].winner == WHITE ? blackguy : whiteguy);
+ break;
+ case END_AGREEDDRAW:
+ sprintf(endstr, "Game drawn by mutual agreement");
+ break;
+ case END_BOTHFLAG:
+ sprintf(endstr, "Game drawn because both players ran out of time");
+ break;
+ case END_REPETITION:
+ sprintf(endstr, "Game drawn by repetition");
+ break;
+ case END_50MOVERULE:
+ sprintf(endstr, "Draw by the 50 move rule");
+ break;
+ case END_ADJOURN:
+ sprintf(endstr, "Game adjourned by mutual agreement");
+ break;
+ case END_LOSTCONNECTION:
+ sprintf(endstr, "%s lost connection, game adjourned",
+ garray[g].winner == WHITE ? whiteguy : blackguy);
+ break;
+ case END_ABORT:
+ sprintf(endstr, "Game aborted by mutual agreement");
+ break;
+ case END_STALEMATE:
+ sprintf(endstr, "Stalemate.");
+ break;
+ case END_NOTENDED:
+ sprintf(endstr, "Still in progress");
+ break;
+ case END_COURTESY:
+ sprintf(endstr, "Game courtesyaborted by %s",
+ garray[g].winner == WHITE ? whiteguy : blackguy);
+ break;
+ case END_COURTESYADJOURN:
+ sprintf(endstr, "Game courtesyadjourned by %s",
+ garray[g].winner == WHITE ? whiteguy : blackguy);
+ break;
+ case END_NOMATERIAL:
+ sprintf(endstr, "Game drawn because neither player has mating material");
+ break;
+ case END_FLAGNOMATERIAL:
+ sprintf(endstr, "%s ran out of time and %s has no material to mate",
+ garray[g].winner == WHITE ? blackguy : whiteguy,
+ garray[g].winner == WHITE ? whiteguy : blackguy);
+ break;
+ case END_ADJDRAW:
+ sprintf(endstr, "Game drawn by adjudication");
+ break;
+ case END_ADJWIN:
+ sprintf(endstr, "%s wins by adjudication",
+ garray[g].winner == WHITE ? whiteguy : blackguy);
+ break;
+ case END_ADJABORT:
+ sprintf(endstr, "Game aborted by adjudication");
+ break;
+ default:
+ sprintf(endstr, "???????");
+ break;
+ }
+
+ return (endstr);
+}
+
+PUBLIC char *EndSym(int g)
+{
+ static char *symbols[] = {"1-0", "0-1", "1/2-1/2", "*"};
+
+ switch (garray[g].result) {
+ case END_CHECKMATE:
+ case END_RESIGN:
+ case END_FLAG:
+ case END_ADJWIN:
+ return ((garray[g].winner == WHITE) ? symbols[0] : symbols[1]);
+ break;
+ case END_AGREEDDRAW:
+ case END_BOTHFLAG:
+ case END_REPETITION:
+ case END_50MOVERULE:
+ case END_STALEMATE:
+ case END_NOMATERIAL:
+ case END_FLAGNOMATERIAL:
+ case END_ADJDRAW:
+ return (symbols[2]);
+ break;
+ }
+
+ return (symbols[3]);
+}
+
+/* This should be enough to hold any game up to at least 250 moves
+ * If we overwrite this, the server will crash.
+ */
+#define GAME_STRING_LEN 16000
+PRIVATE char gameString[GAME_STRING_LEN];
+PUBLIC char *movesToString(int g, int pgn)
+{
+ char tmp[160];
+ int wr, br;
+ int i, col;
+ unsigned curTime;
+ char *serv_loc = SERVER_LOCATION;
+ char *serv_name = SERVER_NAME;
+
+ wr = garray[g].white_rating;
+ br = garray[g].black_rating;
+
+
+ curTime = untenths(garray[g].timeOfStart);
+
+ if (pgn) {
+ sprintf(gameString,
+ "\n[Event \"%s %s %s game\"]\n"
+ "[Site \"%s, %s\"]\n",
+ serv_name,rstr[garray[g].rated], bstr[garray[g].type],serv_name,serv_loc);
+ strftime(tmp, sizeof(tmp),
+ "[Date \"%Y.%m.%d\"]\n"
+ "[Time \"%H:%M:%S\"]\n",
+ localtime((time_t *) &curTime));
+ strcat(gameString, tmp);
+ sprintf(tmp,
+ "[Round \"-\"]\n"
+ "[White \"%s\"]\n"
+ "[Black \"%s\"]\n"
+ "[WhiteElo \"%d\"]\n"
+ "[BlackElo \"%d\"]\n",
+ garray[g].white_name, garray[g].black_name, wr, br);
+ strcat(gameString, tmp);
+ sprintf(tmp,
+ "[TimeControl \"%d+%d\"]\n"
+ "[Mode \"ICS\"]\n"
+ "[Result \"%s\"]\n\n",
+ garray[g].wInitTime / 10, garray[g].wIncrement / 10, EndSym(g));
+ strcat(gameString, tmp);
+
+ col = 0;
+ for (i = 0; i < garray[g].numHalfMoves; i++) {
+ if (!(i % 2)) {
+ if ((col += sprintf(tmp, "%d. ", i / 2 + 1)) > 70) {
+ strcat(gameString, "\n");
+ col = 0;
+ }
+ strcat(gameString, tmp);
+ }
+ if ((col += sprintf(tmp, "%s ", (garray[g].status == GAME_EXAMINE) ? garray[g].examMoveList[i].algString : garray[g].moveList[i].algString)) > 70) {
+ strcat(gameString, "\n");
+ col = 0;
+ }
+ strcat(gameString, tmp);
+ }
+ strcat(gameString, "\n");
+
+ } else {
+
+ sprintf(gameString, "\n%s ", garray[g].white_name);
+ if (wr > 0) {
+ sprintf(tmp, "(%d) ", wr);
+ } else {
+ sprintf(tmp, "(UNR) ");
+ }
+ strcat(gameString, tmp);
+ sprintf(tmp, "vs. %s ", garray[g].black_name);
+ strcat(gameString, tmp);
+ if (br > 0) {
+ sprintf(tmp, "(%d) ", br);
+ } else {
+ sprintf(tmp, "(UNR) ");
+ }
+ strcat(gameString, tmp);
+ strcat(gameString, "--- ");
+ strcat(gameString, (char*) (localtime((time_t *) &curTime)));
+ if (garray[g].rated) {
+ strcat(gameString, "\nRated ");
+ } else {
+ strcat(gameString, "\nUnrated ");
+ }
+ if (garray[g].type == TYPE_BLITZ) {
+ strcat(gameString, "Blitz ");
+ } else if (garray[g].type == TYPE_LIGHT) {
+ strcat(gameString, "Lighting ");
+ } else if (garray[g].type == TYPE_BUGHOUSE) {
+ strcat(gameString, "Bughouse ");
+ } else if (garray[g].type == TYPE_STAND) {
+ strcat(gameString, "Standard ");
+ } else if (garray[g].type == TYPE_WILD) {
+ strcat(gameString, "Wild ");
+ } else if (garray[g].type == TYPE_NONSTANDARD) {
+ strcat(gameString, "Non-standard ");
+ } else {
+ strcat(gameString, "Untimed ");
+ }
+ strcat(gameString, "match, initial time: ");
+ if ((garray[g].bInitTime != garray[g].wInitTime) || (garray[g].wIncrement != garray[g].bIncrement)) { /* different starting times */
+ sprintf(tmp, "%d minutes, increment: %d seconds AND %d minutes, increment: %d seconds.\n\n", garray[g].wInitTime / 600, garray[g].wIncrement / 10, garray[g].bInitTime / 600, garray[g].bIncrement / 10);
+ } else {
+ sprintf(tmp, "%d minutes, increment: %d seconds.\n\n", garray[g].wInitTime / 600, garray[g].wIncrement / 10);
+ }
+ strcat(gameString, tmp);
+ sprintf(tmp, "Move %-19s%-19s\n", garray[g].white_name, garray[g].black_name);
+ strcat(gameString, tmp);
+ strcat(gameString, "---- ---------------- ----------------\n");
+
+ for (i = 0; i < garray[g].numHalfMoves; i += 2) {
+ if (i + 1 < garray[g].numHalfMoves) {
+ sprintf(tmp, "%3d. %-16s ", i / 2 + 1,
+ (garray[g].status == GAME_EXAMINE) ?
+ move_and_time(&garray[g].examMoveList[i]) :
+ move_and_time(&garray[g].moveList[i]));
+ strcat(gameString, tmp);
+ sprintf(tmp, "%-16s\n",
+ (garray[g].status == GAME_EXAMINE) ?
+ move_and_time(&garray[g].examMoveList[i + 1]) :
+ move_and_time(&garray[g].moveList[i + 1]));
+ } else {
+ sprintf(tmp, "%3d. %-16s\n", i / 2 + 1,
+ (garray[g].status == GAME_EXAMINE) ?
+ move_and_time(&garray[g].examMoveList[i]) :
+ move_and_time(&garray[g].moveList[i]));
+ }
+ strcat(gameString, tmp);
+ if (strlen(gameString) > GAME_STRING_LEN - 100) { /* Bug out if getting
+ close to filling this
+ string */
+ return gameString;
+ }
+ }
+
+ strcat(gameString, " ");
+ }
+
+ sprintf(tmp, "{%s} %s\n", EndString(g, 0), EndSym(g));
+ strcat(gameString, tmp);
+
+ return gameString;
+}
+
+PUBLIC void game_disconnect(int g, int p)
+{
+ game_ended(g, (garray[g].white == p) ? WHITE : BLACK, END_LOSTCONNECTION);
+}
+
+PUBLIC int CharToPiece(char c)
+{
+ switch (c) {
+ case 'P':return W_PAWN;
+ case 'p':
+ return B_PAWN;
+ case 'N':
+ return W_KNIGHT;
+ case 'n':
+ return B_KNIGHT;
+ case 'B':
+ return W_BISHOP;
+ case 'b':
+ return B_BISHOP;
+ case 'R':
+ return W_ROOK;
+ case 'r':
+ return B_ROOK;
+ case 'Q':
+ return W_QUEEN;
+ case 'q':
+ return B_QUEEN;
+ case 'K':
+ return W_KING;
+ case 'k':
+ return B_KING;
+ default:
+ return NOPIECE;
+ }
+}
+
+PUBLIC int PieceToChar(int piece)
+{
+ switch (piece) {
+ case W_PAWN:return 'P';
+ case B_PAWN:
+ return 'p';
+ case W_KNIGHT:
+ return 'N';
+ case B_KNIGHT:
+ return 'n';
+ case W_BISHOP:
+ return 'B';
+ case B_BISHOP:
+ return 'b';
+ case W_ROOK:
+ return 'R';
+ case B_ROOK:
+ return 'r';
+ case W_QUEEN:
+ return 'Q';
+ case B_QUEEN:
+ return 'q';
+ case W_KING:
+ return 'K';
+ case B_KING:
+ return 'k';
+ default:
+ return ' ';
+ }
+}
+
+/* One line has everything on it */
+PRIVATE int WriteMoves(FILE * fp, move_t *m)
+{
+ unsigned long MoveInfo = (m->color == BLACK);
+ int piece, castle;
+ int useFile = 0, useRank = 0, check = 0;
+ int i;
+
+ castle = (m->moveString[0] == 'o');
+ if (castle)
+ piece = KING;
+ else
+ piece = piecetype(CharToPiece(m->moveString[0]));
+
+ MoveInfo = (MoveInfo <<= 3) | piece;
+ MoveInfo = (MoveInfo <<= 3) | m->fromFile;
+ MoveInfo = (MoveInfo <<= 3) | m->fromRank;
+ MoveInfo = (MoveInfo <<= 3) | m->toFile;
+ MoveInfo = (MoveInfo <<= 3) | m->toRank;
+ MoveInfo = (MoveInfo <<= 3) | (m->pieceCaptured & 7);
+ MoveInfo = (MoveInfo <<= 3) | (m->piecePromotionTo & 7);
+ MoveInfo = (MoveInfo <<= 1) | (m->enPassant != 0);
+
+ /* Are we using from-file or from-rank in algString? */
+ i = strlen(m->algString) - 1;
+ if (m->algString[i] == '+') {
+ check = 1;
+ i--;
+ }
+ if (piece != PAWN && !castle) {
+ i -= 2;
+ if (i < 0)
+ return -1;
+ if (m->algString[i] == 'x')
+ i--;
+ if (i < 0)
+ return -1;
+ if (isdigit(m->algString[i])) {
+ useRank = 2;
+ i--;
+ }
+ if (i < 0)
+ return -1;
+ useFile = (islower(m->algString[i]) ? 4 : 0);
+ }
+ MoveInfo = (MoveInfo << 3) | useFile | useRank | check;
+ fprintf(fp, "%lx %x %x\n", MoveInfo, m->tookTime, m->atTime);
+
+#if 0
+ fprintf(fp, "%d %d %d %d %d %d %d %d %d \"%s\" \"%s\" %u %u\n",
+ m->color, m->fromFile, m->fromRank, m->toFile, m->toRank,
+ m->pieceCaptured, m->piecePromotionTo, m->enPassant, m->doublePawn,
+ m->moveString, m->algString, m->atTime, m->tookTime);
+#endif
+ return 0;
+}
+
+PRIVATE int ReadMove(FILE * fp, move_t *m)
+{
+ char line[MAX_GLINE_SIZE];
+ fgets(line, MAX_GLINE_SIZE - 1, fp);
+ if (sscanf(line, "%d %d %d %d %d %d %d %d %d \"%[^\"]\" \"%[^\"]\" %u %u\n",
+ &m->color, &m->fromFile, &m->fromRank, &m->toFile, &m->toRank,
+ &m->pieceCaptured, &m->piecePromotionTo, &m->enPassant, &m->doublePawn,
+ m->moveString, m->algString, &m->atTime, &m->tookTime) != 13)
+ return -1;
+ return 0;
+}
+
+PRIVATE void WriteGameState(FILE * fp, game_state_t *gs)
+{
+ int i, j;
+
+ for (i = 0; i < 8; i++)
+ for (j = 0; j < 8; j++) {
+ fprintf(fp, "%c", PieceToChar(gs->board[i][j]));
+ }
+ fprintf(fp, "%d %d %d %d %d %d",
+ gs->wkmoved, gs->wqrmoved, gs->wkrmoved,
+ gs->bkmoved, gs->bqrmoved, gs->bkrmoved);
+ for (i = 0; i < 8; i++)
+ fprintf(fp, " %d %d", gs->ep_possible[0][i], gs->ep_possible[1][i]);
+ fprintf(fp, " %d %d %d\n", gs->lastIrreversable, gs->onMove, gs->moveNum);
+}
+
+PRIVATE int ReadGameState(FILE * fp, game_state_t *gs, int version)
+{
+ int i, j;
+ char pieceChar;
+ int wkmoved, wqrmoved, wkrmoved, bkmoved, bqrmoved, bkrmoved;
+
+ if (version == 0) {
+ for (i = 0; i < 8; i++)
+ for (j = 0; j < 8; j++)
+ if (fscanf(fp, "%d ", &gs->board[i][j]) != 1)
+ return -1;
+ } else {
+ getc(fp); /* Skip past a newline. */
+ for (i = 0; i < 8; i++)
+ for (j = 0; j < 8; j++) {
+ pieceChar = getc(fp);
+ gs->board[i][j] = CharToPiece(pieceChar);
+ }
+ }
+ if (fscanf(fp, "%d %d %d %d %d %d",
+ &wkmoved, &wqrmoved, &wkrmoved,
+ &bkmoved, &bqrmoved, &bkrmoved) != 6)
+ return -1;
+ gs->wkmoved = wkmoved;
+ gs->wqrmoved = wqrmoved;
+ gs->wkrmoved = wkrmoved;
+ gs->bkmoved = bkmoved;
+ gs->bqrmoved = bqrmoved;
+ gs->bkrmoved = bkrmoved;
+ for (i = 0; i < 8; i++)
+ if (fscanf(fp, " %d %d", &gs->ep_possible[0][i], &gs->ep_possible[1][i]) != 2)
+ return -1;
+ if (fscanf(fp, " %d %d %d\n", &gs->lastIrreversable, &gs->onMove, &gs->moveNum) != 3)
+ return -1;
+ return 0;
+}
+
+PUBLIC int got_attr_value(int g, char *attr, char *value, FILE * fp, char *file)
+{
+ int i;
+
+ if (!strcmp(attr, "w_init:")) {
+ garray[g].wInitTime = atoi(value);
+ } else if (!strcmp(attr, "w_inc:")) {
+ garray[g].wIncrement = atoi(value);
+ } else if (!strcmp(attr, "b_init:")) {
+ garray[g].bInitTime = atoi(value);
+ } else if (!strcmp(attr, "b_inc:")) {
+ garray[g].bIncrement = atoi(value);
+ } else if (!strcmp(attr, "white_name:")) {
+ strcpy(garray[g].white_name, value);
+ } else if (!strcmp(attr, "black_name:")) {
+ strcpy(garray[g].black_name, value);
+ } else if (!strcmp(attr, "white_rating:")) {
+ garray[g].white_rating = atoi(value);
+ } else if (!strcmp(attr, "black_rating:")) {
+ garray[g].black_rating = atoi(value);
+ } else if (!strcmp(attr, "result:")) {
+ garray[g].result = atoi(value);
+ } else if (!strcmp(attr, "timestart:")) {
+ garray[g].timeOfStart = atoi(value);
+ } else if (!strcmp(attr, "w_time:")) {
+ garray[g].wTime = atoi(value);
+ } else if (!strcmp(attr, "b_time:")) {
+ garray[g].bTime = atoi(value);
+ } else if (!strcmp(attr, "clockstopped:")) {
+ garray[g].clockStopped = atoi(value);
+ } else if (!strcmp(attr, "rated:")) {
+ garray[g].rated = atoi(value);
+ } else if (!strcmp(attr, "private:")) {
+ garray[g].private = atoi(value);
+ } else if (!strcmp(attr, "type:")) {
+ garray[g].type = atoi(value);
+ } else if (!strcmp(attr, "halfmoves:")) {
+ garray[g].numHalfMoves = atoi(value);
+ if (garray[g].numHalfMoves == 0)
+ return 0;
+ garray[g].moveListSize = garray[g].numHalfMoves;
+ garray[g].moveList = (move_t *) rmalloc(sizeof(move_t) * garray[g].moveListSize);
+ for (i = 0; i < garray[g].numHalfMoves; i++) {
+ if (ReadMove(fp, &garray[g].moveList[i])) {
+ fprintf(stderr, "FICS: Trouble reading moves from %s.\n", file);
+ return -1;
+ }
+ }
+ } else if (!strcmp(attr, "gamestate:")) { /* Value meaningless */
+ if (garray[g].status != GAME_EXAMINE &&
+ ReadGameState(fp, &garray[g].game_state, 0)) {
+ fprintf(stderr, "FICS: Trouble reading game state from %s.\n", file);
+ return -1;
+ }
+ } else {
+ fprintf(stderr, "FICS: Error bad attribute >%s< from file %s\n", attr, file);
+ }
+ return 0;
+}
+
+void ReadOneV1Move(FILE * fp, move_t *m)
+{
+ int i;
+ char PieceChar;
+ int useFile, useRank, check, piece;
+ unsigned long MoveInfo;
+
+ fscanf(fp, "%lx %x %x", &MoveInfo, &m->tookTime, &m->atTime);
+ check = MoveInfo & 1;
+ useRank = MoveInfo & 2;
+ useFile = MoveInfo & 4;
+ MoveInfo >>= 3;
+ m->enPassant = MoveInfo & 1; /* may have to negate later. */
+ MoveInfo >>= 1;
+ m->piecePromotionTo = MoveInfo & 7; /* may have to change color. */
+ MoveInfo >>= 3;
+ m->pieceCaptured = MoveInfo & 7; /* may have to change color. */
+ MoveInfo >>= 3;
+ m->toRank = MoveInfo & 7;
+ MoveInfo >>= 3;
+ m->toFile = MoveInfo & 7;
+ MoveInfo >>= 3;
+ m->fromRank = MoveInfo & 7;
+ MoveInfo >>= 3;
+ m->fromFile = MoveInfo & 7;
+ MoveInfo >>= 3;
+ piece = MoveInfo & 7;
+ m->color = (MoveInfo & 8) ? BLACK : WHITE;
+ if (m->pieceCaptured != NOPIECE) {
+ if (m->color == BLACK)
+ m->pieceCaptured |= WHITE;
+ else
+ m->pieceCaptured |= BLACK;
+ }
+ if (piece == PAWN) {
+ PieceChar = 'P';
+ if ((m->toRank == 3 && m->fromRank == 1)
+ || (m->toRank == 4 && m->fromRank == 6))
+ m->doublePawn = m->toFile;
+ else
+ m->doublePawn = -1;
+ if (m->pieceCaptured)
+ sprintf(m->algString, "%cx%c%d", 'a' + m->fromFile,
+ 'a' + m->toFile, m->toRank + 1);
+ else
+ sprintf(m->algString, "%c%d", 'a' + m->toFile, m->toRank + 1);
+ if (m->piecePromotionTo != 0) {
+ if (m->piecePromotionTo == KNIGHT)
+ strcat(m->algString, "=N");
+ else if (m->piecePromotionTo == BISHOP)
+ strcat(m->algString, "=B");
+ else if (m->piecePromotionTo == ROOK)
+ strcat(m->algString, "=R");
+ else if (m->piecePromotionTo == QUEEN)
+ strcat(m->algString, "=Q");
+ m->piecePromotionTo |= m->color;
+ }
+ if (m->enPassant)
+ m->enPassant = m->toFile - m->fromFile;
+ } else {
+ m->doublePawn = -1;
+ PieceChar = PieceToChar(piecetype(piece) | WHITE);
+ if (PieceChar == 'K' && m->fromFile == 4 && m->toFile == 6) {
+ strcpy(m->algString, "O-O");
+ strcpy(m->moveString, "o-o");
+ } else if (PieceChar == 'K' && m->fromFile == 4 && m->toFile == 2) {
+ strcpy(m->algString, "O-O-O");
+ strcpy(m->moveString, "o-o-o");
+ } else {
+ i = 0;
+ m->algString[i++] = PieceChar;
+ if (useFile)
+ m->algString[i++] = 'a' + m->fromFile;
+ if (useRank)
+ m->algString[i++] = '1' + m->fromRank;
+ if (m->pieceCaptured != 0)
+ m->algString[i++] = 'x';
+ m->algString[i++] = 'a' + m->toFile;
+ m->algString[i++] = '1' + m->toRank;
+ m->algString[i] = '\0';
+ }
+ }
+ if (m->algString[0] != 'O')
+ sprintf(m->moveString, "%c/%c%d-%c%d", PieceChar, 'a' + m->fromFile,
+ m->fromRank + 1, 'a' + m->toFile, m->toRank + 1);
+ if (check)
+ strcat(m->algString, "+");
+}
+
+int ReadV1Moves(game *g, FILE * fp)
+{
+ int i;
+
+ g->moveListSize = g->numHalfMoves;
+ g->moveList = (move_t *) rmalloc(sizeof(move_t) * g->moveListSize);
+ for (i = 0; i < g->numHalfMoves; i++) {
+ ReadOneV1Move(fp, &g->moveList[i]);
+ }
+ return 0;
+}
+
+int ReadV1GameFmt(game *g, FILE * fp, char *file, int version)
+{
+ fscanf(fp, "%s %s", g->white_name, g->black_name);
+ fscanf(fp, "%d %d", &g->white_rating, &g->black_rating);
+ fscanf(fp, "%d %d %d %d", &g->wInitTime, &g->wIncrement,
+ &g->bInitTime, &g->bIncrement);
+ if ((version < 3) && (!(g->bInitTime)))
+ g->bInitTime = g->wInitTime;
+ /*PRE-V3 assumed bInitTime was 0 if balanced clocks*/
+ fscanf(fp, "%lx", &g->timeOfStart);
+ fscanf(fp, "%d %d", &g->wTime, &g->bTime);
+
+/* fixing an (apparently) old bug: winner not saved */
+ if (version > 1)
+ fscanf(fp, "%d %d", &g->result, &g->winner);
+ else
+ fscanf(fp, "%d", &g->result);
+
+ fscanf(fp, "%d %d %d %d", &g->private, &g->type,
+ &g->rated, &g->clockStopped);
+ fscanf(fp, "%d", &g->numHalfMoves);
+ ReadV1Moves(g, fp);
+ if (g->status != GAME_EXAMINE
+ && ReadGameState(fp, &g->game_state, version)) {
+ fprintf(stderr, "FICS: Trouble reading game state from %s.\n", file);
+ return -1;
+ }
+ return 0;
+}
+
+PUBLIC int ReadGameAttrs(FILE * fp, char *fname, int g)
+{
+ int len;
+ int version = 0;
+ char *attr, *value;
+ char line[MAX_GLINE_SIZE];
+
+ fgets(line, MAX_GLINE_SIZE - 1, fp);
+
+ if (line[0] == 'v') {
+ sscanf(line, "%*c %d", &version);
+ }
+ if (version > 0) {
+ ReadV1GameFmt(&garray[g], fp, fname, version);
+ }
+ /* Read the game file here */
+ else
+ do {
+ if ((len = strlen(line)) <= 1) {
+ fgets(line, MAX_GLINE_SIZE - 1, fp);
+ continue;
+ }
+ line[len - 1] = '\0';
+ attr = eatwhite(line);
+ if (attr[0] == '#')
+ continue; /* Comment */
+ value = eatword(attr);
+ if (!*value) {
+ fprintf(stderr, "FICS: Error reading file %s\n", fname);
+ fgets(line, MAX_GLINE_SIZE - 1, fp);
+ continue;
+ }
+ *value = '\0';
+ value++;
+ value = eatwhite(value);
+ if (!*value) {
+ fprintf(stderr, "FICS: Error reading file %s\n", fname);
+ fgets(line, MAX_GLINE_SIZE - 1, fp);
+ continue;
+ }
+ stolower(attr);
+ if (got_attr_value(g, attr, value, fp, fname)) {
+ return -1;
+ }
+ fgets(line, MAX_GLINE_SIZE - 1, fp);
+ } while (!feof(fp));
+ if (!(garray[g].bInitTime))
+ garray[g].bInitTime = garray[g].wInitTime;
+ return 0;
+}
+
+PUBLIC int game_read(int g, int wp, int bp)
+{
+ FILE *fp;
+ char fname[MAX_FILENAME_SIZE];
+
+ garray[g].white = wp;
+ garray[g].black = bp;
+/* garray[g].old_white = -1;
+ garray[g].old_black = -1;
+*/
+ garray[g].moveListSize = 0;
+ garray[g].game_state.gameNum = g;
+ strcpy(garray[g].white_name, parray[wp].name);
+ strcpy(garray[g].black_name, parray[bp].name);
+ if (garray[g].type == TYPE_BLITZ) {
+ garray[g].white_rating = parray[wp].b_stats.rating;
+ garray[g].black_rating = parray[bp].b_stats.rating;
+ } else if (garray[g].type == TYPE_WILD) {
+ garray[g].white_rating = parray[wp].w_stats.rating;
+ garray[g].black_rating = parray[bp].w_stats.rating;
+ } else if (garray[g].type == TYPE_LIGHT) {
+ garray[g].white_rating = parray[wp].l_stats.rating;
+ garray[g].black_rating = parray[bp].l_stats.rating;
+ } else if (garray[g].type == TYPE_BUGHOUSE) {
+ garray[g].white_rating = parray[wp].bug_stats.rating;
+ garray[g].black_rating = parray[bp].bug_stats.rating;
+ } else {
+ garray[g].white_rating = parray[wp].s_stats.rating;
+ garray[g].black_rating = parray[bp].s_stats.rating;
+ }
+ sprintf(fname, "%s/%c/%s-%s", adj_dir, parray[wp].login[0],
+ parray[wp].login, parray[bp].login);
+ fp = fopen(fname, "r");
+ if (!fp) {
+ return -1;
+ }
+ if (ReadGameAttrs(fp, fname, g) < 0) {
+ fclose(fp);
+ return -1;
+ }
+ fclose(fp);
+
+ if (garray[g].result == END_ADJOURN
+ || garray[g].result == END_COURTESYADJOURN)
+ garray[g].result = END_NOTENDED;
+ garray[g].status = GAME_ACTIVE;
+ garray[g].startTime = tenth_secs();
+ garray[g].lastMoveTime = garray[g].startTime;
+ garray[g].lastDecTime = garray[g].startTime;
+ /* Need to do notification and pending cleanup */
+ return 0;
+}
+
+PUBLIC int game_delete(int wp, int bp)
+{
+ char fname[MAX_FILENAME_SIZE];
+ char lname[MAX_FILENAME_SIZE];
+
+ sprintf(fname, "%s/%c/%s-%s", adj_dir, parray[wp].login[0],
+ parray[wp].login, parray[bp].login);
+ sprintf(lname, "%s/%c/%s-%s", adj_dir, parray[bp].login[0],
+ parray[wp].login, parray[bp].login);
+ unlink(fname);
+ unlink(lname);
+ return 0;
+}
+
+void WriteGameFile(FILE * fp, int g)
+{
+ int i;
+ game *gg = &garray[g];
+ player *wp = &parray[gg->white], *bp = &parray[gg->black];
+
+ fprintf(fp, "v %d\n", GAMEFILE_VERSION);
+ fprintf(fp, "%s %s\n", wp->name, bp->name);
+ fprintf(fp, "%d %d\n", gg->white_rating, gg->black_rating);
+ fprintf(fp, "%d %d %d %d\n", gg->wInitTime, gg->wIncrement,
+ gg->bInitTime, gg->bIncrement);
+ fprintf(fp, "%lx\n", gg->timeOfStart);
+/* fprintf(fp, "%d %d\n", gg->wTime, gg->bTime); */
+#ifdef TIMESEAL
+ fprintf(fp, "%d %d\n",
+ (con[wp->socket].timeseal ? gg->wRealTime/100 : gg->wTime),
+ (con[bp->socket].timeseal ? gg->bRealTime/100 : gg->bTime));
+#endif
+ fprintf(fp, "%d %d\n", gg->result, gg->winner);
+ fprintf(fp, "%d %d %d %d\n", gg->private, gg->type,
+ gg->rated, gg->clockStopped);
+ fprintf(fp, "%d\n", gg->numHalfMoves);
+ for (i = 0; i < garray[g].numHalfMoves; i++) {
+ WriteMoves(fp, &garray[g].moveList[i]);
+ }
+ WriteGameState(fp, &garray[g].game_state);
+}
+
+PUBLIC int game_save(int g)
+{
+ FILE *fp;
+ player *wp, *bp;
+ game *gg = &garray[g];
+ char fname[MAX_FILENAME_SIZE];
+ char lname[MAX_FILENAME_SIZE];
+
+ wp = &parray[gg->white];
+ bp = &parray[gg->black];
+ sprintf(fname, "%s/%c/%s-%s", adj_dir, wp->login[0],
+ wp->login, bp->login);
+ sprintf(lname, "%s/%c/%s-%s", adj_dir, bp->login[0],
+ wp->login, bp->login);
+ fp = fopen(fname, "w");
+ if (!fp) {
+ fprintf(stderr, "FICS: Problem opening file %s for write\n", fname);
+ return -1;
+ }
+ WriteGameFile(fp, g);
+#if 0
+ fprintf(fp, "W_Init: %d\n", garray[g].wInitTime);
+ fprintf(fp, "W_Inc: %d\n", garray[g].wIncrement);
+ fprintf(fp, "B_Init: %d\n", garray[g].bInitTime);
+ fprintf(fp, "B_Inc: %d\n", garray[g].bIncrement);
+ fprintf(fp, "white_name: %s\n", wp->name);
+ fprintf(fp, "black_name: %s\n", bp->name);
+ fprintf(fp, "white_rating: %d\n", garray[g].white_rating);
+ fprintf(fp, "black_rating: %d\n", garray[g].black_rating);
+ fprintf(fp, "result: %d\n", garray[g].result);
+ fprintf(fp, "TimeStart: %d\n", (int) garray[g].timeOfStart);
+ fprintf(fp, "W_Time: %d\n", garray[g].wTime);
+ fprintf(fp, "B_Time: %d\n", garray[g].bTime);
+ fprintf(fp, "ClockStopped: %d\n", garray[g].clockStopped);
+ fprintf(fp, "Rated: %d\n", garray[g].rated);
+ fprintf(fp, "Private: %d\n", garray[g].private);
+ fprintf(fp, "Type: %d\n", garray[g].type);
+ fprintf(fp, "HalfMoves: %d\n", garray[g].numHalfMoves);
+ for (i = 0; i < garray[g].numHalfMoves; i++) {
+ WriteMoves(fp, &garray[g].moveList[i]);
+ }
+ fprintf(fp, "GameState: IsNext\n");
+ WriteGameState(fp, &garray[g].game_state);
+#endif
+ fclose(fp);
+ /* Create link for easier stored game finding */
+ if (bp->login[0] != wp->login[0])
+ link(fname, lname);
+ return 0;
+}
+
+#if 0
+PRIVATE int history_game_save(int g, char *fname)
+{
+ FILE *fp;
+ int wp, bp;
+ int i;
+
+ wp = garray[g].white;
+ bp = garray[g].black;
+ fp = fopen(fname, "w");
+ if (!fp) {
+ fprintf(stderr, "FICS: Problem openning file %s for write\n", fname);
+ return -1;
+ }
+ fprintf(fp, "W_Init: %d\n", garray[g].wInitTime);
+ fprintf(fp, "W_Inc: %d\n", garray[g].wIncrement);
+ fprintf(fp, "B_Init: %d\n", garray[g].bInitTime);
+ fprintf(fp, "B_Inc: %d\n", garray[g].bIncrement);
+ fprintf(fp, "TimeStart: %d\n", (int) garray[g].timeOfStart);
+ fprintf(fp, "W_Time: %d\n", garray[g].wTime);
+ fprintf(fp, "B_Time: %d\n", garray[g].bTime);
+ fprintf(fp, "ClockStopped: %d\n", garray[g].clockStopped);
+ fprintf(fp, "Rated: %d\n", garray[g].rated);
+ fprintf(fp, "Private: %d\n", garray[g].private);
+ fprintf(fp, "Type: %d\n", garray[g].type);
+ fprintf(fp, "HalfMoves: %d\n", garray[g].numHalfMoves);
+ for (i = 0; i < garray[g].numHalfMoves; i++) {
+ WriteMoves(fp, &garray[g].moveList[i]);
+ }
+ fprintf(fp, "GameState: IsNext\n");
+ WriteGameState(fp, &garray[g].game_state);
+ fclose(fp);
+ return 0;
+}
+#endif
+
+PRIVATE long OldestHistGame(char *login)
+{
+ FILE *fp;
+ char pFile[MAX_FILENAME_SIZE];
+ long when;
+
+ sprintf(pFile, "%s/player_data/%c/%s.%s", stats_dir,
+ login[0], login, STATS_GAMES);
+ fp = fopen(pFile, "r");
+
+ if (fp == NULL) {
+ sprintf(pFile, "%s/player_data/%c/.rem.%s.%s", stats_dir,
+ login[0], login, STATS_GAMES);
+ fp = fopen(pFile, "r");
+ }
+ if (fp != NULL) {
+ fscanf(fp, "%*d %*c %*d %*c %*d %*s %*s %*d %*d %*d %*d %*s %*s %ld",
+ &when);
+ fclose(fp);
+ return when;
+ } else
+ return 0L;
+}
+
+PRIVATE void RemoveHistGame(char *file, int maxlines)
+{
+ FILE *fp;
+ char GameFile[MAX_FILENAME_SIZE];
+ char Opponent[MAX_LOGIN_NAME];
+ char line[MAX_LINE_SIZE];
+ long When, oppWhen;
+ int count = 0;
+
+ fp = fopen(file, "r");
+ if (fp == NULL)
+ return;
+
+ fgets(line, MAX_LINE_SIZE - 1, fp);
+ sscanf(line, "%*d %*c %*d %*c %*d %s %*s %*d %*d %*d %*d %*s %*s %ld",
+ Opponent, &When);
+ count++;
+
+ while (!feof(fp)) {
+ fgets(line, MAX_LINE_SIZE - 1, fp);
+ if (!feof(fp))
+ count++;
+ }
+ fclose(fp);
+
+ stolower(Opponent);
+ if (count > maxlines) {
+ truncate_file(file, maxlines);
+
+ oppWhen = OldestHistGame(Opponent);
+ if (oppWhen > When || oppWhen <= 0L) {
+ sprintf(GameFile, "%s/%ld/%ld", hist_dir, When % 100, When);
+ unlink(GameFile);
+ }
+ }
+}
+
+PUBLIC void RemHist(char *who)
+{
+ FILE *fp;
+ char fName[MAX_FILENAME_SIZE];
+ char Opp[MAX_LOGIN_NAME];
+ long When, oppWhen;
+
+ sprintf(fName, "%s/player_data/%c/%s.%s", stats_dir,
+ who[0], who, STATS_GAMES);
+ fp = fopen(fName, "r");
+ if (fp != NULL) {
+ while (!feof(fp)) {
+ fscanf(fp, "%*d %*c %*d %*c %*d %s %*s %*d %*d %*d %*d %*s %*s %ld",
+ Opp, &When);
+ stolower(Opp);
+ oppWhen = OldestHistGame(Opp);
+ if (oppWhen > When || oppWhen <= 0L) {
+ sprintf(fName, "%s/%ld/%ld", hist_dir, When % 100, When);
+ unlink(fName);
+ }
+ }
+ }
+}
+
+PRIVATE void write_g_out(int g, char *file, int maxlines, int isDraw,
+ char *EndSymbol, char *name, time_t *now)
+{
+ FILE *fp;
+ int wp, bp;
+ int wr, br;
+ char type[4];
+ char tmp[2048];
+ char *ptmp = tmp;
+ char cResult;
+ int count = -1;
+ char *goteco;
+
+ wp = garray[g].white;
+ bp = garray[g].black;
+
+ if (garray[g].private) {
+ type[0] = 'p';
+ } else {
+ type[0] = ' ';
+ }
+ if (garray[g].type == TYPE_BLITZ) {
+ wr = parray[wp].b_stats.rating;
+ br = parray[bp].b_stats.rating;
+ type[1] = 'b';
+ } else if (garray[g].type == TYPE_WILD) {
+ wr = parray[wp].w_stats.rating;
+ br = parray[bp].w_stats.rating;
+ type[1] = 'w';
+ } else if (garray[g].type == TYPE_STAND) {
+ wr = parray[wp].s_stats.rating;
+ br = parray[bp].s_stats.rating;
+ type[1] = 's';
+ } else if (garray[g].type == TYPE_LIGHT) {
+ wr = parray[wp].l_stats.rating;
+ br = parray[bp].l_stats.rating;
+ type[1] = 'l';
+ } else if (garray[g].type == TYPE_BUGHOUSE) {
+ wr = parray[wp].bug_stats.rating;
+ br = parray[bp].bug_stats.rating;
+ type[1] = 'd';
+ } else {
+ wr = 0;
+ br = 0;
+ if (garray[g].type == TYPE_NONSTANDARD)
+ type[1] = 'n';
+ else
+ type[1] = 'u';
+ }
+ if (garray[g].rated) {
+ type[2] = 'r';
+ } else {
+ type[2] = 'u';
+ }
+ type[3] = '\0';
+
+ fp = fopen(file, "r");
+ if (fp) {
+ while (!feof(fp))
+ fgets(tmp, 1024, fp);
+ sscanf(ptmp, "%d", &count);
+ fclose(fp);
+ }
+ count = (count + 1) % 100;
+
+ fp = fopen(file, "a");
+ if (!fp)
+ return;
+
+ goteco = getECO(g);
+
+/* Counter Result MyRating MyColor OppRating OppName [pbr 2 12 2 12] ECO End Date */
+ if (name == parray[wp].name) {
+ if (isDraw)
+ cResult = '=';
+ else if (garray[g].winner == WHITE)
+ cResult = '+';
+ else
+ cResult = '-';
+
+ fprintf(fp, "%d %c %d W %d %s %s %d %d %d %d %s %s %ld\n",
+ count, cResult, wr, br, parray[bp].name, type,
+ garray[g].wInitTime, garray[g].wIncrement,
+ garray[g].bInitTime, garray[g].bIncrement,
+ goteco,
+ EndSymbol,
+ (long) *now);
+ } else {
+ if (isDraw)
+ cResult = '=';
+ else if (garray[g].winner == BLACK)
+ cResult = '+';
+ else
+ cResult = '-';
+
+ fprintf(fp, "%d %c %d B %d %s %s %d %d %d %d %s %s %ld\n",
+ count, cResult, br, wr, parray[wp].name, type,
+ garray[g].wInitTime, garray[g].wIncrement,
+ garray[g].bInitTime, garray[g].bIncrement,
+ goteco,
+ EndSymbol,
+ (long) *now);
+ }
+ fclose(fp);
+
+ RemoveHistGame(file, maxlines);
+/*
+ if ((name == parray[wp].name) && (parray[wp].registered)) {
+ sprintf(tmp, "%s/%c/%s.%d", adj_dir, parray[wp].login[0], parray[wp].login, count);
+ history_game_save(g, tmp);
+ }
+ if ((name == parray[bp].name) && (parray[bp].registered)) {
+ sprintf(tmp, "%s/%c/%s.%d", adj_dir, parray[bp].login[0], parray[bp].login, count);
+ history_game_save(g, tmp);
+ }
+*/
+}
+
+/* Find from_spot in journal list - return 0 if corrupted */
+
+PUBLIC int journal_get_info(int p,char from_spot,char* WhiteName, int* WhiteRating,
+ char* BlackName, int* BlackRating, char* type,int* t,int* i,char* eco,
+ char* ending,char* result, char *fname)
+{
+ char count;
+ FILE *fp;
+
+ fp = fopen(fname, "r");
+ if (!fp) {
+ fprintf (stderr, "Corrupt journal file! %s\n",fname);
+ pprintf (p, "The journal file is corrupt! See an admin.\n");
+ return 0;
+ }
+ while (!feof(fp)) {
+ if (fscanf(fp, "%c %s %d %s %d %s %d %d %s %s %s\n",
+ &count,
+ WhiteName,
+ &(*WhiteRating),
+ BlackName,
+ &(*BlackRating),
+ type,
+ &(*t), &(*i),
+ eco,
+ ending,
+ result) != 11) {
+ fprintf(stderr, "FICS: Error in journal info format. %s\n", fname);
+ pprintf(p, "The journal file is corrupt! Error in internal format.\n");
+ fclose(fp);
+ return 0;
+ }
+ if (tolower(count) == from_spot) {
+ fclose(fp);
+ return 1;
+ }
+ }
+ fclose(fp);
+ return 0;
+}
+
+PUBLIC void addjournalitem(int p,char count2,char* WhiteName2, int WhiteRating2,
+ char* BlackName2, int BlackRating2, char* type2,int t2,int i2,char* eco2,
+ char* ending2,char* result2, char* fname)
+
+{
+ int WhiteRating, BlackRating;
+ int t, i;
+ char WhiteName[MAX_LOGIN_NAME + 1];
+ char BlackName[MAX_LOGIN_NAME + 1];
+ char type[100];
+ char eco[100];
+ char ending[100];
+ char count;
+ char result[100];
+ int have_output=0;
+ char fname2[MAX_FILENAME_SIZE];
+
+ FILE *fp;
+ FILE *fp2;
+
+ strcpy (fname2,fname);
+ strcat (fname2,".w");
+ fp2 = fopen(fname2, "w");
+ if (!fp2) {
+ fprintf(stderr, "FICS: Problem opening file %s for write\n", fname);
+ pprintf (p, "Couldn't update journal! Report this to an admin.\n");
+ return;
+ }
+ fp = fopen(fname, "r");
+ if (!fp) { /* Empty? */
+ fprintf(fp2, "%c %s %d %s %d %s %d %d %s %s %s\n",
+ count2, WhiteName2, WhiteRating2, BlackName2, BlackRating2,
+ type2, t2, i2, eco2, ending2,
+ result2);
+ fclose (fp2);
+ rename (fname2, fname);
+ return;
+ } else {
+ while (!feof(fp)) {
+ if (fscanf(fp, "%c %s %d %s %d %s %d %d %s %s %s\n",
+ &count,
+ WhiteName,
+ &WhiteRating,
+ BlackName,
+ &BlackRating,
+ type,
+ &t, &i,
+ eco,
+ ending,
+ result) != 11) {
+ fprintf(stderr, "FICS: Error in journal info format - aborting. %s\n", fname);
+ fclose(fp);
+ fclose(fp2);
+ return;
+ }
+ if ((count >= count2) && (!have_output)) {
+ fprintf(fp2, "%c %s %d %s %d %s %d %d %s %s %s\n",
+ count2,
+ WhiteName2,
+ WhiteRating2,
+ BlackName2,
+ BlackRating2,
+ type2,
+ t2, i2,
+ eco2,
+ ending2,
+ result2);
+ have_output = 1;
+ }
+ if (count != count2) {
+ fprintf(fp2, "%c %s %d %s %d %s %d %d %s %s %s\n",
+ count,
+ WhiteName,
+ WhiteRating,
+ BlackName,
+ BlackRating,
+ type,
+ t, i,
+ eco,
+ ending,
+ result);
+ }
+ }
+ if (!have_output) { /* Haven't written yet */
+ fprintf(fp2, "%c %s %d %s %d %s %d %d %s %s %s\n",
+ count2,
+ WhiteName2,
+ WhiteRating2,
+ BlackName2,
+ BlackRating2,
+ type2,
+ t2, i2,
+ eco2,
+ ending2,
+ result2);
+ }
+ }
+ fclose(fp);
+ fclose(fp2);
+ rename(fname2, fname);
+ return;
+}
+
+PUBLIC int pjournal(int p, int p1, char *fname)
+{
+ FILE *fp;
+ int WhiteRating, BlackRating;
+ int t, i;
+ char WhiteName[MAX_LOGIN_NAME + 1];
+ char BlackName[MAX_LOGIN_NAME + 1];
+ char type[100];
+ char eco[100];
+ char ending[100];
+ char count;
+ char result[100];
+
+ fp = fopen(fname, "r");
+ if (!fp) {
+ pprintf(p, "Sorry, no journal information available.\n");
+ return COM_OK;
+ }
+ pprintf(p, "Journal for %s:\n", parray[p1].name);
+ pprintf(p, " White Rating Black Rating Type ECO End Result\n");
+ while (!feof(fp)) {
+ if (fscanf(fp, "%c %s %d %s %d %s %d %d %s %s %s\n",
+ &count,
+ WhiteName,
+ &WhiteRating,
+ BlackName,
+ &BlackRating,
+ type,
+ &t, &i,
+ eco,
+ ending,
+ result) != 11) {
+ fprintf(stderr, "FICS: Error in journal info format. %s\n", fname);
+ fclose(fp);
+ return COM_OK;
+ }
+ WhiteName[13] = '\0'; /* only first 13 chars in name */
+ BlackName[13] = '\0';
+ pprintf(p, "%c: %-13s %4d %-13s %4d [%3s%3d%4d] %s %3s %-7s\n",
+ count, WhiteName, WhiteRating,
+ BlackName, BlackRating,
+ type, t / 600, i / 10, eco, ending,
+ result);
+ }
+ fclose(fp);
+ return COM_OK;
+}
+
+PUBLIC int pgames(int p, int p1, char *fname)
+{
+ FILE *fp;
+ time_t t;
+ int MyRating, OppRating;
+ int wt, wi, bt, bi;
+ char OppName[MAX_LOGIN_NAME + 1];
+ char type[100];
+ char eco[100];
+ char ending[100];
+ char MyColor[2];
+ int count;
+ char result[2];
+
+ fp = fopen(fname, "r");
+ if (!fp) {
+ pprintf(p, "Sorry, no game information available.\n");
+ return COM_OK;
+ }
+ pprintf(p, "History for %s:\n", parray[p1].name);
+ pprintf(p, " Opponent Type ECO End Date\n");
+ while (!feof(fp)) {
+ if (fscanf(fp, "%d %s %d %s %d %s %s %d %d %d %d %s %s %ld\n",
+ &count,
+ result,
+ &MyRating,
+ MyColor,
+ &OppRating,
+ OppName,
+ type,
+ &wt, &wi,
+ &bt, &bi,
+ eco,
+ ending,
+ (long *) &t) != 14) {
+ fprintf(stderr, "FICS: Error in games info format. %s\n", fname);
+ fclose(fp);
+ return COM_OK;
+ }
+ OppName[13] = '\0'; /* only first 13 chars in name */
+ pprintf(p, "%2d: %s %4d %s %4d %-13s [%3s%3d%4d] %s %3s %s",
+ count, result, MyRating, MyColor,
+ OppRating, OppName,
+ type, wt / 600, wi / 10, eco, ending,
+ ctime(&t));
+ }
+ fclose(fp);
+ return COM_OK;
+}
+
+PUBLIC void game_write_complete(int g, int isDraw, char *EndSymbol)
+{
+ char fname[MAX_FILENAME_SIZE];
+ int wp = garray[g].white, bp = garray[g].black;
+ time_t now = time(NULL);
+ int fd;
+ FILE *fp;
+
+ do {
+ sprintf(fname, "%s/%ld/%ld", hist_dir, (long) now % 100, (long) now);
+ fd = open(fname, O_WRONLY | O_CREAT | O_EXCL, 0644);
+ if (fd == EEXIST)
+ now++;
+ } while (fd == EEXIST);
+
+ if (fd >= 0) {
+ fp = fdopen(fd, "w");
+ if (fp != NULL)
+ WriteGameFile(fp, g);
+ else
+ fprintf(stderr, "Trouble writing history file %s", fname);
+ fclose(fp);
+ close(fd);
+ }
+ sprintf(fname, "%s/player_data/%c/%s.%s", stats_dir,
+ parray[wp].login[0], parray[wp].login, STATS_GAMES);
+ write_g_out(g, fname, 10, isDraw, EndSymbol, parray[wp].name, &now);
+ sprintf(fname, "%s/player_data/%c/%s.%s", stats_dir,
+ parray[bp].login[0], parray[bp].login, STATS_GAMES);
+ write_g_out(g, fname, 10, isDraw, EndSymbol, parray[bp].name, &now);
+}
+
+PUBLIC int game_count(void)
+{
+ int g, count = 0;
+
+ for (g = 0; g < g_num; g++) {
+ if ((garray[g].status == GAME_ACTIVE) || (garray[g].status == GAME_EXAMINE))
+ count++;
+ }
+ if (count > game_high)
+ game_high = count;
+ return count;
+}
+
diff --git a/FICS/gamedb.h b/FICS/gamedb.h
new file mode 100644
index 0000000..a7f027c
--- /dev/null
+++ b/FICS/gamedb.h
@@ -0,0 +1,209 @@
+/* gamedb.h
+ *
+ */
+
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name email yy/mm/dd Change
+ Richard Nash 93/10/22 Created
+*/
+
+#ifndef _GAMEDB_H
+#define _GAMEDB_H
+
+#include <time.h>
+#include "board.h"
+
+extern char *bstr[];
+extern char *rstr[];
+
+#define GAMEFILE_VERSION 3
+#define MAX_GLINE_SIZE 1024
+
+#define REL_GAME 0
+#define REL_SPOS 1
+#define REL_REFRESH 2
+#define REL_EXAMINE 3
+
+#define GAME_EMPTY 0
+#define GAME_NEW 1
+#define GAME_ACTIVE 2
+#define GAME_EXAMINE 3 /* examine is now 3 */
+
+/*
+#define GAME_STORED 3
+#define GAME_EXAMINE 4
+*/
+
+#define TYPE_UNTIMED 0
+#define TYPE_BLITZ 1
+#define TYPE_STAND 2
+#define TYPE_NONSTANDARD 3
+#define TYPE_WILD 4
+#define TYPE_LIGHT 5
+#define TYPE_BUGHOUSE 6
+
+#ifdef TIMESEAL
+#define FLAG_CHECKING -1
+#define FLAG_NONE 0
+#define FLAG_CALLED 1
+#define FLAG_ABORT 2
+#endif
+
+#define END_CHECKMATE 0
+#define END_RESIGN 1
+#define END_FLAG 2
+#define END_AGREEDDRAW 3
+#define END_REPETITION 4
+#define END_50MOVERULE 5
+#define END_ADJOURN 6
+#define END_LOSTCONNECTION 7
+#define END_ABORT 8
+#define END_STALEMATE 9
+#define END_NOTENDED 10
+#define END_COURTESY 11
+#define END_BOTHFLAG 12
+#define END_NOMATERIAL 13
+#define END_FLAGNOMATERIAL 14
+#define END_ADJDRAW 15
+#define END_ADJWIN 16
+#define END_ADJABORT 17
+#define END_COURTESYADJOURN 18
+
+#if 0
+typedef unsigned char boardList_t[74];
+#endif
+
+typedef struct _game {
+ /* Saved in the game file */
+ int wInitTime, wIncrement;
+ int bInitTime, bIncrement;
+ time_t timeOfStart;
+#ifdef TIMESEAL
+ int wLastRealTime;
+ int wRealTime;
+ int wTimeWhenReceivedMove;
+ int wTimeWhenMoved;
+ int bLastRealTime;
+ int bRealTime;
+ int bTimeWhenReceivedMove;
+ int bTimeWhenMoved;
+ int flag_pending;
+ unsigned long flag_check_time;
+#endif
+ int wTime;
+ int bTime;
+ int clockStopped;
+ int rated;
+ int private;
+ int type;
+ int passes; /* For simul's */
+ int numHalfMoves;
+ move_t *moveList; /* primary movelist */
+ unsigned char FENstartPos[74]; /* Save the starting position. */
+ game_state_t game_state;
+/* This is now in moveList, and FENstartPos. -- hersco */
+/*
+ int boardListSize;
+ boardList_t *boardList;
+ boardList_t boardList[500];
+*/
+ char white_name[18]; /* to hold the playername even after he disconnects */
+ char black_name[18];
+ int white_rating;
+ int black_rating;
+
+ /* Not saved in game file */
+ int revertHalfMove;
+ int totalHalfMoves;
+ int white;
+ int black;
+ /* int old_white; */ /* Contains the old game player number */
+ /* int old_black; */ /* Contains the old game player number */
+ int link;
+ int status;
+ int moveListSize; /* Total allocated in *moveList */
+ int examHalfMoves;
+ move_t *examMoveList; /* extra movelist for examine */
+ int examMoveListSize;
+
+ unsigned startTime; /* The relative time the game started */
+ unsigned lastMoveTime; /* Last time a move was made */
+ unsigned lastDecTime; /* Last time a players clock was decremented */
+
+ int result;
+ int winner;
+
+} game;
+
+extern game *garray;
+extern int g_num;
+
+extern int game_new(void);
+extern int game_zero(int);
+extern int game_free(int);
+extern int game_clear(int);
+extern int game_remove(int);
+extern int game_finish(int);
+extern void MakeFENpos (int, char *);
+
+extern char *game_time_str(int, int, int, int);
+extern char *game_str(int, int, int, int, int, char *, char *);
+extern int game_isblitz(int, int, int, int, char *, char *);
+
+extern void send_board_to(int, int);
+extern void send_boards(int);
+extern void game_update_time(int);
+extern void game_update_times(void);
+
+/* #define MAXOLDGAMES 50
+
+extern int FindOldGameFor(int);
+extern int RemoveOldGamesForPlayer(int);
+extern int ReallyRemoveOldGamesForPlayer(int);
+extern int NewOldGame(int);
+Kill this oldgame rubbish - wastes memory - DAV */
+
+extern char *movesToString(int, int);
+extern char *EndSym(int);
+extern char *EndString(int, int);
+
+extern void game_disconnect(int, int);
+
+extern int CharToPiece(char);
+extern int PieceToChar(int);
+extern int got_attr_value();
+extern int ReadGameAttrs();
+extern int game_read(int, int, int);
+extern int game_delete(int, int);
+extern int game_save(int);
+extern void RemHist (char *);
+
+extern int journal_get_info(int,char,char*,int*,
+ char*,int*,char*,int*,int*,char*,
+ char*,char*,char*);
+extern void addjournalitem(int,char,char*,int,char*,int,char*,int,int,
+ char*, char*, char* ,char*);
+extern int pgames(int, int, char *);
+extern int pjournal(int, int, char *);
+extern void game_write_complete(int, int, char *);
+extern int game_count(void);
+
+/* extern time_t time(); */
+
+#endif
+
diff --git a/FICS/gameproc.c b/FICS/gameproc.c
new file mode 100644
index 0000000..cf07200
--- /dev/null
+++ b/FICS/gameproc.c
@@ -0,0 +1,1745 @@
+/* gameproc.c
+ *
+ */
+
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name email yy/mm/dd Change
+ Richard Nash 93/10/22 Created
+ Dave Herscovici 95/11/26 Split into two files;
+ Second is obsproc.c.
+*/
+
+#include "stdinclude.h"
+
+#include "common.h"
+#include "command.h"
+#include "ficsmain.h"
+#include "config.h"
+#include "playerdb.h"
+#include "gamedb.h"
+#include "gameproc.h"
+#include "obsproc.h"
+#include "movecheck.h"
+#include "utils.h"
+#include "ratings.h"
+#include "rmalloc.h"
+#include "comproc.h"
+#include "matchproc.h"
+#include "eco.h"
+#include "network.h"
+#include "lists.h"
+/* #include "hostinfo.h" */
+
+PUBLIC void game_ended(int g, int winner, int why)
+{
+ char outstr[200];
+ char tmp[200];
+ int p;
+ int gl = garray[g].link;
+ int rate_change = 0;
+ int isDraw = 0;
+ int whiteResult;
+ char winSymbol[10];
+ char EndSymbol[10];
+ char *NameOfWinner, *NameOfLoser;
+ int beingplayed = 0; /* i.e. it wasn't loaded for adjudication */
+
+ beingplayed = (parray[garray[g].black].game == g);
+
+ sprintf(outstr, "\n{Game %d (%s vs. %s) ", g + 1,
+ parray[garray[g].white].name,
+ parray[garray[g].black].name);
+ garray[g].result = why;
+ garray[g].winner = winner;
+ if (winner == WHITE) {
+ whiteResult = RESULT_WIN;
+ strcpy(winSymbol, "1-0");
+ NameOfWinner = parray[garray[g].white].name;
+ NameOfLoser = parray[garray[g].black].name;
+ } else {
+ whiteResult = RESULT_LOSS;
+ strcpy(winSymbol, "0-1");
+ NameOfWinner = parray[garray[g].black].name;
+ NameOfLoser = parray[garray[g].white].name;
+ }
+ switch (why) {
+ case END_CHECKMATE:
+ sprintf(tmp, "%s checkmated} %s\n", NameOfLoser, winSymbol);
+ strcpy(EndSymbol, "Mat");
+ rate_change = 1;
+ break;
+ case END_RESIGN:
+ sprintf(tmp, "%s resigns} %s\n", NameOfLoser, winSymbol);
+ strcpy(EndSymbol, "Res");
+ rate_change = 1;
+ break;
+ case END_FLAG:
+ sprintf(tmp, "%s forfeits on time} %s\n", NameOfLoser, winSymbol);
+ strcpy(EndSymbol, "Fla");
+ rate_change = 1;
+ break;
+ case END_STALEMATE:
+ sprintf(tmp, "Game drawn by stalemate} 1/2-1/2\n");
+ isDraw = 1;
+ strcpy(EndSymbol, "Sta");
+ rate_change = 1;
+ whiteResult = RESULT_DRAW;
+ break;
+ case END_AGREEDDRAW:
+ sprintf(tmp, "Game drawn by mutual agreement} 1/2-1/2\n");
+ isDraw = 1;
+ strcpy(EndSymbol, "Agr");
+ rate_change = 1;
+ whiteResult = RESULT_DRAW;
+ break;
+ case END_BOTHFLAG:
+ sprintf(tmp, "Game drawn because both players ran out of time} 1/2-1/2\n");
+ isDraw = 1;
+ strcpy(EndSymbol, "Fla");
+ rate_change = 1;
+ whiteResult = RESULT_DRAW;
+ break;
+ case END_REPETITION:
+ sprintf(tmp, "Game drawn by repetition} 1/2-1/2\n");
+ isDraw = 1;
+ strcpy(EndSymbol, "Rep");
+ rate_change = 1;
+ whiteResult = RESULT_DRAW;
+ break;
+ case END_50MOVERULE:
+ sprintf(tmp, "Game drawn by the 50 move rule} 1/2-1/2\n");
+ isDraw = 1;
+ strcpy(EndSymbol, "50");
+ rate_change = 1;
+ whiteResult = RESULT_DRAW;
+ break;
+ case END_ADJOURN:
+ if (gl >= 0) {
+ sprintf(tmp, "Bughouse game aborted.} *\n");
+ whiteResult = RESULT_ABORT;
+ } else {
+ sprintf(tmp, "Game adjourned by mutual agreement} *\n");
+ game_save(g);
+ }
+ break;
+ case END_LOSTCONNECTION:
+ sprintf(tmp, "%s lost connection; game ", NameOfWinner);
+ if (parray[garray[g].white].registered && parray[garray[g].black].registered
+ && gl < 0) {
+ sprintf(tmp, "adjourned} *\n");
+ game_save(g);
+ } else
+ sprintf(tmp, "aborted} *\n");
+ whiteResult = RESULT_ABORT;
+ break;
+ case END_ABORT:
+ sprintf(tmp, "Game aborted by mutual agreement} *\n");
+ whiteResult = RESULT_ABORT;
+ break;
+ case END_COURTESY:
+ sprintf(tmp, "Game courtesyaborted by %s} *\n", NameOfWinner);
+ whiteResult = RESULT_ABORT;
+ break;
+ case END_COURTESYADJOURN:
+ if (gl >= 0) {
+ sprintf(tmp, "Bughouse game courtesyaborted by %s.} *\n", NameOfWinner);
+ whiteResult = RESULT_ABORT;
+ } else {
+ sprintf(tmp, "Game courtesyadjourned by %s} *\n", NameOfWinner);
+ game_save(g);
+ }
+ break;
+ case END_NOMATERIAL:
+ /* Draw by insufficient material (e.g., lone K vs. lone K) */
+ sprintf(tmp, "Neither player has mating material} 1/2-1/2\n");
+ isDraw = 1;
+ strcpy(EndSymbol, "NM ");
+ rate_change = 1;
+ whiteResult = RESULT_DRAW;
+ break;
+ case END_FLAGNOMATERIAL:
+ sprintf(tmp, "%s ran out of time and %s has no material to mate} 1/2-1/2\n",
+ NameOfLoser, NameOfWinner);
+ isDraw = 1;
+ strcpy(EndSymbol, "TM ");
+ rate_change = 1;
+ whiteResult = RESULT_DRAW;
+ break;
+ case END_ADJWIN:
+ sprintf(tmp, "%s wins by adjudication} %s\n", NameOfWinner, winSymbol);
+ strcpy(EndSymbol, "Adj");
+ rate_change = 1;
+ break;
+ case END_ADJDRAW:
+ sprintf(tmp, "Game drawn by adjudication} 1/2-1/2\n");
+ isDraw = 1;
+ strcpy(EndSymbol, "Adj");
+ rate_change = 1;
+ whiteResult = RESULT_DRAW;
+ break;
+ case END_ADJABORT:
+ sprintf(tmp, "Game aborted by adjudication} *\n");
+ whiteResult = RESULT_ABORT;
+ break;
+ default:
+ sprintf(tmp, "Hmm, the game ended and I don't know why} *\n");
+ break;
+ }
+ strcat(outstr, tmp);
+ if (beingplayed) {
+ pprintf_noformat(garray[g].white, outstr);
+ pprintf_noformat(garray[g].black, outstr);
+ if (parray[garray[g].white].bell)
+ pprintf (garray[g].white, "\007");
+ if (parray[garray[g].black].bell)
+ pprintf (garray[g].black, "\007");
+
+ garray[g].link = -1; /*IanO: avoids recursion */
+ if (gl >= 0 && garray[gl].link >= 0) {
+ pprintf_noformat(garray[gl].white, outstr);
+ pprintf_noformat(garray[gl].black, outstr);
+ game_ended(gl, CToggle(winner), why);
+ }
+
+ for (p = 0; p < p_num; p++) {
+ if ((p == garray[g].white) || (p == garray[g].black))
+ continue;
+ if (parray[p].status != PLAYER_PROMPT)
+ continue;
+ if (!parray[p].i_game && !player_is_observe(p, g))
+ continue;
+ pprintf_noformat(p, outstr);
+/* if (parray[p].bell)
+ pprintf (p, "\007"); removed - caused annoyance */
+ pprintf_prompt(p, "");
+ }
+ }
+ if ((garray[g].rated) && (rate_change)) {
+ /* Adjust ratings */
+ rating_update(g);
+#if 0
+ if (parray[garray[g].white].network_player &&
+ parray[garray[g].black].network_player) {
+ if (MailGameResult) { /* Send to ratings server */
+ if (isDraw) {
+ /* hostinfo_mailresults(garray[g].type == TYPE_BLITZ ? "blitz" :
+ garray[g].type == TYPE_WILD ? "wild " : "stand", "
+ parray[garray[g].white].name, parray[garray[g].black].name,
+ "draw"); */
+ } else {
+/* hostinfo_mailresults(garray[g].type == TYPE_BLITZ ? "blitz" : garray[g].type == TYPE_WILD ? "wild " : "stand",
+ parray[garray[g].white].name,
+ parray[garray[g].black].name,
+ (winner == WHITE) ? parray[garray[g].white].name :
+ parray[garray[g].black].name); */
+ }
+ }
+ }
+#endif
+
+ } else {
+ if (beingplayed) {
+ pprintf(garray[g].white, "No ratings adjustment done.\n");
+ pprintf(garray[g].black, "No ratings adjustment done.\n");
+ }
+ }
+ if (rate_change && gl < 0)
+ game_write_complete(g, isDraw, EndSymbol);
+ /* Mail off the moves */
+ if (parray[garray[g].white].automail) {
+ pcommand(garray[g].white, "mailmoves");
+ }
+ if (parray[garray[g].black].automail) {
+ pcommand(garray[g].black, "mailmoves");
+ }
+ parray[garray[g].white].num_white++;
+ parray[garray[g].white].lastColor = WHITE;
+ parray[garray[g].black].num_black++;
+ parray[garray[g].black].lastColor = BLACK;
+ parray[garray[g].white].last_opponent = garray[g].black;
+ parray[garray[g].black].last_opponent = garray[g].white;
+ if (beingplayed) {
+ parray[garray[g].white].game = -1;
+ parray[garray[g].black].game = -1;
+ parray[garray[g].white].opponent = -1;
+ parray[garray[g].black].opponent = -1;
+ if (garray[g].white != commanding_player)
+ pprintf_prompt(garray[g].white, "");
+ if (garray[g].black != commanding_player)
+ pprintf_prompt(garray[g].black, "");
+ if (parray[garray[g].white].simul_info.numBoards) {
+ player_simul_over(garray[g].white, g, whiteResult);
+ }
+ }
+ game_finish(g);
+}
+
+PRIVATE int was_promoted(game *g, int f, int r)
+{
+#define BUGHOUSE_PAWN_REVERT 1
+#ifdef BUGHOUSE_PAWN_REVERT
+ int i;
+
+ for (i = g->numHalfMoves-2; i > 0; i -= 2) {
+ if (g->moveList[i].toFile == f && g->moveList[i].toRank == r) {
+ if (g->moveList[i].piecePromotionTo)
+ return 1;
+ if (g->moveList[i].fromFile == ALG_DROP)
+ return 0;
+ f = g->moveList[i].fromFile;
+ r = g->moveList[i].fromRank;
+ }
+ }
+#endif
+ return 0;
+}
+
+PUBLIC int pIsPlaying (int p)
+{
+ int g = parray[p].game;
+ int p1 = parray[p].opponent;
+
+ if (g < 0 || garray[g].status == GAME_EXAMINE) {
+ pprintf (p, "You are not playing a game.\n");
+ return 0;
+ } else if (garray[g].white != p && garray[g].black != p) {
+ /* oh oh; big bad game bug. */
+ fprintf (stderr, "BUG: Player %s playing game %d according to parray,"
+ "\n but not according to garray.\n", parray[p].name, g+1);
+ pprintf (p, "Disconnecting you from game number %d.\n", g+1);
+ parray[p].game = -1;
+ if (p1 >= 0 && parray[p1].game == g
+ && garray[g].white != p1 && garray[g].black != p1) {
+ pprintf (p1, "Disconnecting you from game number %d.\n", g+1);
+ parray[p1].game = -1;
+ }
+ return 0;
+ }
+ else return 1;
+}
+
+PUBLIC void process_move(int p, char *command)
+{
+ int g;
+ move_t move;
+ int result;
+ unsigned now;
+ int len; /* loon: for lame promotion hack */
+ int i;
+
+ if (parray[p].game < 0) {
+ pprintf(p, "You are not playing or examining a game.\n");
+ return;
+ }
+ player_decline_offers(p, -1, -PEND_SIMUL);
+ g = parray[p].game;
+
+ if (garray[g].status != GAME_EXAMINE) {
+ if (!pIsPlaying) return;
+
+ if (parray[p].side != garray[g].game_state.onMove) {
+ pprintf(p, "It is not your move.\n");
+ return;
+ }
+ if (garray[g].clockStopped) {
+ pprintf(p, "Game clock is paused, use \"unpause\" to resume.\n");
+ return;
+ }
+ }
+ if ((len = strlen(command)) > 1) {
+ if (command[len - 2] == '=') {
+ switch (tolower(command[strlen(command) - 1])) {
+ case 'n':
+ parray[p].promote = KNIGHT;
+ break;
+ case 'b':
+ parray[p].promote = BISHOP;
+ break;
+ case 'r':
+ parray[p].promote = ROOK;
+ break;
+ case 'q':
+ parray[p].promote = QUEEN;
+ break;
+ default:
+ pprintf(p, "Don't understand that move.\n");
+ return;
+ break;
+ }
+ }
+ }
+ switch (parse_move(command, &garray[g].game_state, &move, parray[p].promote)) {
+ case MOVE_ILLEGAL:
+ pprintf(p, "Illegal move.\n");
+ return;
+ break;
+ case MOVE_AMBIGUOUS:
+ pprintf(p, "Ambiguous move.\n");
+ return;
+ break;
+ default:
+ break;
+ }
+
+ if (garray[g].status == GAME_EXAMINE) {
+ garray[g].numHalfMoves++;
+ if (garray[g].numHalfMoves > garray[g].examMoveListSize) {
+ garray[g].examMoveListSize += 20; /* Allocate 20 moves at a time */
+ if (!garray[g].examMoveList) {
+ garray[g].examMoveList = (move_t *) rmalloc(sizeof(move_t) * garray[g].examMoveListSize);
+ } else {
+ garray[g].examMoveList = (move_t *) rrealloc(garray[g].examMoveList, sizeof(move_t) * garray[g].examMoveListSize);
+ }
+ }
+ result = execute_move(&garray[g].game_state, &move, 1);
+ move.atTime = now;
+ move.tookTime = 0;
+ MakeFENpos(g, move.FENpos);
+ garray[g].examMoveList[garray[g].numHalfMoves - 1] = move;
+ /* roll back time */
+ if (garray[g].game_state.onMove == WHITE) {
+ garray[g].wTime += (garray[g].lastDecTime - garray[g].lastMoveTime);
+ } else {
+ garray[g].bTime += (garray[g].lastDecTime - garray[g].lastMoveTime);
+ }
+ now = tenth_secs();
+ if (garray[g].numHalfMoves == 0)
+ garray[g].timeOfStart = now;
+ garray[g].lastMoveTime = now;
+ garray[g].lastDecTime = now;
+
+ } else { /* real game */
+ i = parray[p].opponent;
+ if (parray[i].simul_info.numBoards &&
+ (parray[i].simul_info.boards[parray[i].simul_info.onBoard] != g)) {
+ pprintf(p, "It isn't your turn: wait until the simul giver is at your board.\n");
+ return;
+ }
+#ifdef TIMESEAL
+
+ if (con[parray[p].socket].timeseal) { /* does he use timeseal? */
+ if (parray[p].side == WHITE) {
+ garray[g].wLastRealTime = garray[g].wRealTime;
+ garray[g].wTimeWhenMoved = con[parray[p].socket].time;
+ if (((garray[g].wTimeWhenMoved - garray[g].wTimeWhenReceivedMove) < 0) ||
+ (garray[g].wTimeWhenReceivedMove == 0)) {
+ /* might seem weird - but could be caused by a person moving BEFORE
+ he receives the board pos (this is possible due to lag) but it's
+ safe to say he moved in 0 secs :-) */
+ garray[g].wTimeWhenReceivedMove = garray[g].wTimeWhenMoved;
+ } else {
+ garray[g].wRealTime -= garray[g].wTimeWhenMoved - garray[g].wTimeWhenReceivedMove;
+ }
+ } else if (parray[p].side == BLACK) {
+ garray[g].bLastRealTime = garray[g].bRealTime;
+ garray[g].bTimeWhenMoved = con[parray[p].socket].time;
+ if (((garray[g].bTimeWhenMoved - garray[g].bTimeWhenReceivedMove) < 0) ||
+ (garray[g].bTimeWhenReceivedMove == 0)) {
+ /* might seem weird - but could be caused by a person moving BEFORE
+ he receives the board pos (this is possible due to lag) but it's
+ safe to say he moved in 0 secs :-) */
+ garray[g].bTimeWhenReceivedMove = garray[g].bTimeWhenMoved;
+ } else {
+ garray[g].bRealTime -= garray[g].bTimeWhenMoved - garray[g].bTimeWhenReceivedMove;
+ }
+ }
+ }
+ /* we need to reset the opp's time for receiving the board since the
+ timeseal decoder only alters the time if it's 0 Otherwise the time
+ would be changed if the player did a refresh which would screw up
+ the timings */
+ if (parray[p].side == WHITE) {
+ garray[g].bTimeWhenReceivedMove = 0;
+ } else {
+ garray[g].wTimeWhenReceivedMove = 0;
+ }
+
+#endif
+
+ game_update_time(g);
+
+ /* maybe add autoflag here in the future? */
+
+#ifdef TIMESEAL
+
+ if (con[parray[p].socket].timeseal) { /* does he use timeseal? */
+ if (parray[p].side == WHITE) {
+ garray[g].wRealTime += garray[g].wIncrement * 100;
+ garray[g].wTime = garray[g].wRealTime / 100; /* remember to conv to
+ tenth secs */
+ } else if (parray[p].side == BLACK) {
+ garray[g].bRealTime += garray[g].bIncrement * 100; /* conv to ms */
+ garray[g].bTime = garray[g].bRealTime / 100; /* remember to conv to
+ tenth secs */
+ }
+ } else {
+ if (garray[g].game_state.onMove == BLACK) {
+ garray[g].bTime += garray[g].bIncrement;
+ }
+ if (garray[g].game_state.onMove == WHITE) {
+ garray[g].wTime += garray[g].wIncrement;
+ }
+ }
+
+#else
+
+ if (garray[g].game_state.onMove == BLACK) {
+ garray[g].bTime += garray[g].bIncrement;
+ }
+ if (garray[g].game_state.onMove == WHITE) {
+ garray[g].wTime += garray[g].wIncrement;
+ }
+#endif
+
+ /* Do the move */
+ garray[g].numHalfMoves++;
+ if (garray[g].numHalfMoves > garray[g].moveListSize) {
+ garray[g].moveListSize += 20; /* Allocate 20 moves at a time */
+ if (!garray[g].moveList) {
+ garray[g].moveList = (move_t *) rmalloc(sizeof(move_t) * garray[g].moveListSize);
+ } else {
+ garray[g].moveList = (move_t *) rrealloc(garray[g].moveList, sizeof(move_t) * garray[g].moveListSize);
+ }
+ }
+ result = execute_move(&garray[g].game_state, &move, 1);
+ if (result == MOVE_OK && garray[g].link >= 0 && move.pieceCaptured != NOPIECE) {
+ /* transfer captured piece to partner */
+ /* check if piece reverts to a pawn */
+ if (was_promoted(&garray[g], move.toFile, move.toRank))
+ update_holding(garray[g].link, colorval(move.pieceCaptured) | PAWN);
+ else
+ update_holding(garray[g].link, move.pieceCaptured);
+ }
+ now = tenth_secs();
+ move.atTime = now;
+ if (garray[g].numHalfMoves > 1) {
+ move.tookTime = move.atTime - garray[g].lastMoveTime;
+ } else {
+ move.tookTime = move.atTime - garray[g].startTime;
+ }
+ garray[g].lastMoveTime = now;
+ garray[g].lastDecTime = now;
+
+#ifdef TIMESEAL
+
+ if (con[parray[p].socket].timeseal) { /* does he use timeseal? */
+ if (parray[p].side == WHITE) {
+ move.tookTime = (garray[parray[p].game].wTimeWhenMoved -
+ garray[parray[p].game].wTimeWhenReceivedMove) / 100;
+ } else {
+ move.tookTime = (garray[parray[p].game].bTimeWhenMoved -
+ garray[parray[p].game].bTimeWhenReceivedMove) / 100;
+ }
+ }
+#endif
+
+ MakeFENpos(g, move.FENpos);
+ garray[g].moveList[garray[g].numHalfMoves - 1] = move;
+ }
+
+ send_boards(g);
+
+ if (result == MOVE_ILLEGAL) {
+ pprintf(p, "Internal error, illegal move accepted!\n");
+ }
+ if ((result == MOVE_OK) && (garray[g].status == GAME_EXAMINE)) {
+ int p1;
+
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (parray[p1].status != PLAYER_PROMPT)
+ continue;
+ if (player_is_observe(p1, g) || parray[p1].game == g) {
+ pprintf_prompt(p1, "%s moves: %s\n", parray[p].name, move.algString);
+ }
+ }
+ }
+ if (result == MOVE_CHECKMATE) {
+ if (garray[g].status == GAME_EXAMINE) {
+ int p1;
+
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (parray[p1].status != PLAYER_PROMPT)
+ continue;
+ if (player_is_observe(p1, g) || parray[p1].game == g) {
+ pprintf(p1, "%s has been checkmated.\n",
+ (CToggle(garray[g].game_state.onMove) == BLACK) ? "White" : "Black");
+ }
+ }
+ } else {
+ game_ended(g, CToggle(garray[g].game_state.onMove), END_CHECKMATE);
+ }
+ }
+ if (result == MOVE_STALEMATE) {
+ if (garray[g].status == GAME_EXAMINE) {
+ int p1;
+
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (parray[p1].status != PLAYER_PROMPT)
+ continue;
+ if (player_is_observe(p1, g) || parray[p1].game == g) {
+ pprintf(p1, "Stalemate.\n");
+ }
+ }
+ } else {
+ game_ended(g, CToggle(garray[g].game_state.onMove), END_STALEMATE);
+ }
+ }
+ if (result == MOVE_NOMATERIAL) {
+ if (garray[g].status == GAME_EXAMINE) {
+ int p1;
+
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (parray[p1].status != PLAYER_PROMPT)
+ continue;
+ if (player_is_observe(p1, g) || parray[p1].game == g) {
+ pprintf(p1, "No mating material.\n");
+ }
+ }
+ } else {
+ game_ended(g, CToggle(garray[g].game_state.onMove), END_NOMATERIAL);
+ }
+ }
+}
+
+PUBLIC int com_resign(int p, param_list param)
+{
+ int g, o, oconnected;
+
+ if (param[0].type == TYPE_NULL) {
+ g = parray[p].game;
+ if (!pIsPlaying(p)) {
+ return COM_OK;
+ } else {
+ player_decline_offers(p, -1, -1);
+ game_ended(g, (garray[g].white == p) ? BLACK : WHITE, END_RESIGN);
+ }
+ } else if (FindPlayer(p, param[0].val.word, &o, &oconnected)) {
+ g = game_new();
+ if (game_read(g, p, o) < 0) {
+ if (game_read(g, o, p) < 0) {
+ pprintf(p, "You have no stored game with %s\n", parray[o].name);
+ if (!oconnected)
+ player_remove(o);
+ return COM_OK;
+ } else {
+ garray[g].white = o;
+ garray[g].black = p;
+ }
+ } else {
+ garray[g].white = p;
+ garray[g].black = o;
+ }
+ pprintf(p, "You resign your stored game with %s\n", parray[o].name);
+ game_delete(garray[g].white, garray[g].black);
+ game_ended(g, (garray[g].white == p) ? BLACK : WHITE, END_RESIGN);
+ pcommand(p, "message %s I have resigned our stored game \"%s vs. %s.\"",
+ parray[o].name,
+ parray[garray[g].white].name,
+ parray[garray[g].black].name);
+ if (!oconnected)
+ player_remove(o);
+ }
+ return COM_OK;
+}
+
+int Check50MoveRule (int p, int g)
+{
+ int num_reversible = garray[g].numHalfMoves;
+
+ if (garray[g].game_state.lastIrreversable >= 0) {
+ num_reversible -= garray[g].game_state.lastIrreversable;
+ }
+ if (num_reversible > 99) {
+ game_ended(g, (garray[g].white == p) ? BLACK : WHITE, END_50MOVERULE);
+ return 1;
+ }
+ return 0;
+}
+
+char *GetFENpos (int g, int half_move)
+{
+ if (half_move < 0)
+ return garray[g].FENstartPos;
+ else return garray[g].moveList[half_move].FENpos;
+}
+
+int CheckRepetition (int p, int g)
+{
+ int move_num;
+ int flag1 = 1, flag2 = 1;
+ char *pos1 = GetFENpos (g, garray[g].numHalfMoves - 1);
+ char *pos2 = GetFENpos (g, garray[g].numHalfMoves);
+ char *pos;
+
+ if (garray[g].numHalfMoves < 8) /* can't have three repeats any quicker. */
+ return 0;
+
+ for (move_num = garray[g].game_state.lastIrreversable;
+ move_num < garray[g].numHalfMoves - 1; move_num++) {
+ pos = GetFENpos (g, move_num);
+ if (strlen(pos1) == strlen(pos) && !strcmp(pos1, pos))
+ flag1++;
+ if (strlen(pos2) == strlen(pos) && !strcmp(pos2, pos))
+ flag2++;
+ }
+ if (flag1 >= 3 || flag2 >= 3) {
+ if (player_find_pendfrom(p, parray[p].opponent, PEND_DRAW) >= 0) {
+ player_remove_request(parray[p].opponent, p, PEND_DRAW);
+ player_decline_offers(p, -1, -1);
+ }
+ game_ended(g, (garray[g].white == p) ? BLACK : WHITE, END_REPETITION);
+ return 1;
+ }
+ else return 0;
+}
+
+PUBLIC int com_draw(int p, param_list param)
+{
+ int p1, g = parray[p].game;
+
+ ASSERT(param[0].type == TYPE_NULL);
+ if (!pIsPlaying(p)) {
+ return COM_OK;
+ }
+ if (Check50MoveRule (p, g) || CheckRepetition(p, g)) {
+ return COM_OK;
+ }
+ p1 = parray[p].opponent;
+ if (parray[p1].simul_info.numBoards &&
+ parray[p1].simul_info.boards[parray[p1].simul_info.onBoard] !=
+ g) {
+ pprintf(p, "You can only make requests when the simul player is at your board.\n");
+ return COM_OK;
+ }
+ if (player_find_pendfrom(p, parray[p].opponent, PEND_DRAW) >= 0) {
+ player_remove_request(parray[p].opponent, p, PEND_DRAW);
+ player_decline_offers(p, -1, -1);
+ game_ended(g, (garray[g].white == p) ? BLACK : WHITE, END_AGREEDDRAW);
+ } else {
+ pprintf(parray[p].opponent, "\n");
+ pprintf_highlight(parray[p].opponent, "%s", parray[p].name);
+ pprintf_prompt(parray[p].opponent, " offers you a draw.\n");
+ pprintf(p, "Draw request sent.\n");
+ player_add_request(p, parray[p].opponent, PEND_DRAW, 0);
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_pause(int p, param_list param)
+{
+ int g;
+ int now;
+
+ ASSERT(param[0].type == TYPE_NULL);
+ if (!pIsPlaying(p)) {
+ return COM_OK;
+ }
+ g = parray[p].game;
+ if (garray[g].wTime == 0) {
+ pprintf(p, "You can't pause untimed games.\n");
+ return COM_OK;
+ }
+ if (garray[g].clockStopped) {
+ pprintf(p, "Game is already paused, use \"unpause\" to resume.\n");
+ return COM_OK;
+ }
+ if (player_find_pendfrom(p, parray[p].opponent, PEND_PAUSE) >= 0) {
+ player_remove_request(parray[p].opponent, p, PEND_PAUSE);
+ garray[g].clockStopped = 1;
+ /* Roll back the time */
+ if (garray[g].game_state.onMove == WHITE) {
+ garray[g].wTime += (garray[g].lastDecTime - garray[g].lastMoveTime);
+ } else {
+ garray[g].bTime += (garray[g].lastDecTime - garray[g].lastMoveTime);
+ }
+ now = tenth_secs();
+ if (garray[g].numHalfMoves == 0)
+ garray[g].timeOfStart = now;
+ garray[g].lastMoveTime = now;
+ garray[g].lastDecTime = now;
+ send_boards(g);
+ pprintf_prompt(parray[p].opponent, "\n%s accepted pause. Game clock paused.\n",
+ parray[p].name);
+ pprintf(p, "Game clock paused.\n");
+ } else {
+ pprintf(parray[p].opponent, "\n");
+ pprintf_highlight(parray[p].opponent, "%s", parray[p].name);
+ pprintf_prompt(parray[p].opponent, " requests to pause the game.\n");
+ pprintf(p, "Pause request sent.\n");
+ player_add_request(p, parray[p].opponent, PEND_PAUSE, 0);
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_unpause(int p, param_list param)
+{
+ int now;
+ int g;
+
+ ASSERT(param[0].type == TYPE_NULL);
+ if (!pIsPlaying(p)) {
+ return COM_OK;
+ }
+ g = parray[p].game;
+ if (!garray[g].clockStopped) {
+ pprintf(p, "Game is not paused.\n");
+ return COM_OK;
+ }
+ garray[g].clockStopped = 0;
+ now = tenth_secs();
+ if (garray[g].numHalfMoves == 0)
+ garray[g].timeOfStart = now;
+ garray[g].lastMoveTime = now;
+ garray[g].lastDecTime = now;
+ send_boards(g);
+ pprintf(p, "Game clock resumed.\n");
+ pprintf_prompt(parray[p].opponent, "\nGame clock resumed.\n");
+ return COM_OK;
+}
+
+PUBLIC int com_abort(int p, param_list param)
+{
+ int p1, g, myColor, yourColor, myGTime, yourGTime;
+ int courtesyOK = 1;
+
+ ASSERT(param[0].type == TYPE_NULL);
+
+ g = parray[p].game;
+ if (!pIsPlaying(p))
+ return COM_OK;
+
+ p1 = parray[p].opponent;
+ if (p == garray[g].white) {
+ myColor = WHITE;
+ yourColor = BLACK;
+ myGTime = garray[g].wTime;
+ yourGTime = garray[g].bTime;
+ } else {
+ myColor = BLACK;
+ yourColor = WHITE;
+ myGTime = garray[g].bTime;
+ yourGTime = garray[g].wTime;
+ }
+ if (parray[p1].simul_info.numBoards &&
+ parray[p1].simul_info.boards[parray[p1].simul_info.onBoard] != g) {
+ pprintf(p, "You can only make requests when the simul player is at your board.\n");
+ return COM_OK;
+ }
+ if (player_find_pendfrom(p, p1, PEND_ABORT) >= 0) {
+ player_remove_request(p1, p, PEND_ABORT);
+ player_decline_offers(p, -1, -1);
+ game_ended(g, yourColor, END_ABORT);
+ } else {
+ game_update_time(g);
+
+#ifdef TIMESEAL
+ if (con[parray[p].socket].timeseal
+ && garray[g].game_state.onMove == myColor
+ && garray[g].flag_pending == FLAG_ABORT) {
+ /* It's my move, opponent has asked for abort; I lagged out,
+ my timeseal prevented courtesyabort, and I sent an abort
+ request before acknowledging (and processing) my opponent's
+ courtesyabort. OK, let's abort already :-). */
+ player_decline_offers(p, -1, -1);
+ game_ended(g, yourColor, END_ABORT);
+ }
+
+ if (con[parray[p1].socket].timeseal) { /* opp uses timeseal? */
+
+ int yourRealTime = (myColor == WHITE ? garray[g].bRealTime
+ : garray[g].wRealTime);
+ if (myGTime > 0 && yourGTime <= 0 && yourRealTime > 0) {
+ /* Override courtesyabort; opponent still has time. Check for lag. */
+ courtesyOK = 0;
+
+ if (garray[g].game_state.onMove != myColor
+ && garray[g].flag_pending != FLAG_CHECKING) {
+ /* Opponent may be lagging; let's ask. */
+ garray[g].flag_pending = FLAG_ABORT;
+ garray[g].flag_check_time = time(0);
+ pprintf(p, "Opponent has timeseal; trying to courtesyabort.\n");
+ pprintf(p1, "\n[G]\n");
+ return COM_OK;
+ }
+ }
+ }
+#endif
+
+ if (myGTime > 0 && yourGTime <= 0 && courtesyOK) {
+ /* player wants to abort + opponent is out of time = courtesyabort */
+ pprintf(p, "Since you have time, and your opponent has none, the game has been aborted.");
+ pprintf(p1, "Your opponent has aborted the game rather than calling your flag.");
+ player_decline_offers(p, -1, -1);
+ game_ended(g, myColor, END_COURTESY);
+ } else {
+ pprintf(p1, "\n");
+ pprintf_highlight(p1, "%s", parray[p].name);
+ pprintf(p1, " would like to abort the game; ");
+ pprintf_prompt(p1, "type \"abort\" to accept.\n");
+ pprintf(p, "Abort request sent.\n");
+ player_add_request(p, p1, PEND_ABORT, 0);
+ }
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_courtesyabort(int p, param_list param)
+{
+ pprintf(p, "Courtesyabort is obsolete; use \"abort\" instead.\n");
+ return COM_OK;
+}
+
+PUBLIC int com_courtesyadjourn(int p, param_list param)
+{
+ pprintf(p, "Use \"adjourn\" to courtesyadjourn a game.\n");
+ return COM_OK;
+}
+
+PRIVATE int player_has_mating_material(game_state_t *gs, int color)
+{
+ int i, j;
+ int piece;
+ int minor_pieces = 0;
+
+ for (i = 0; i < 8; i++)
+ for (j = 0; j < 8; j++) {
+ piece = gs->board[i][j];
+ switch (piecetype(piece)) {
+ case BISHOP:
+ case KNIGHT:
+ if (iscolor(piece, color))
+ minor_pieces++;
+ break;
+ case KING:
+ case NOPIECE:
+ break;
+ default:
+ if (iscolor(piece, color))
+ return 1;
+ }
+ }
+ return ((minor_pieces > 1) ? 1 : 0);
+}
+
+PUBLIC int com_flag(int p, param_list param)
+{
+ int g;
+ int myColor;
+
+ ASSERT(param[0].type == TYPE_NULL);
+ if (!pIsPlaying(p)) {
+ return COM_OK;
+ }
+ g = parray[p].game;
+ myColor = (p == garray[g].white ? WHITE : BLACK);
+ if (garray[g].type == TYPE_UNTIMED) {
+ pprintf(p, "You can't flag an untimed game.\n");
+ return COM_OK;
+ }
+ if (garray[g].numHalfMoves < 2) {
+ pprintf(p, "You cannot flag before both players have moved.\nUse abort instead.\n");
+ return COM_OK;
+ }
+ game_update_time(g);
+
+#ifdef TIMESEAL
+
+ {
+ int myTime, yourTime, opp = parray[p].opponent, serverTime;
+
+ if (con[parray[p].socket].timeseal) { /* does caller use timeseal? */
+ myTime = (myColor == WHITE ? garray[g].wRealTime
+ : garray[g].bRealTime);
+ } else {
+ myTime = (myColor == WHITE ? garray[g].wTime : garray[g].bTime);
+ }
+ serverTime = (myColor == WHITE ? garray[g].bTime : garray[g].wTime);
+
+ if (con[parray[opp].socket].timeseal) { /* opp uses timeseal? */
+ yourTime = (myColor == WHITE ? garray[g].bRealTime
+ : garray[g].wRealTime);
+ } else {
+ yourTime = serverTime;
+ }
+
+ /* the clocks to compare are now in myTime and yourTime */
+
+ if ((myTime <= 0) && (yourTime <= 0)) {
+ player_decline_offers(p, -1, -1);
+ game_ended(g, myColor, END_BOTHFLAG);
+ return COM_OK;
+ }
+ if (yourTime > 0) {
+ /* Opponent still has time, but if that's only because s/he
+ * may be lagging, we should ask for an acknowledgement and then
+ * try to call the flag. */
+
+ if (serverTime <= 0 && garray[g].game_state.onMove != myColor
+ && garray[g].flag_pending != FLAG_CHECKING) {
+
+ /* server time thinks opponent is down, but RealTIme disagrees.
+ * ask client to acknowledge it's alive. */
+
+ garray[g].flag_pending = FLAG_CALLED;
+ garray[g].flag_check_time = time(0);
+ pprintf(p, "Opponent has timeseal; checking if (s)he's lagging.\n");
+ pprintf (opp, "\n[G]\n");
+ return COM_OK;
+ }
+
+ /* if we're here, it means one of:
+ * 1. the server agrees opponent has time, whether lagging or not.
+ * 2. opp. has timeseal (if yourTime != serverTime), had time left
+ * after the last move (yourTime > 0), and it's still your move.
+ * 3. we're currently checking a flag call after having receiving
+ * acknowledgement from the other timeseal (and would have reset
+ * yourTime if the flag were down). */
+
+ pprintf(p, "Your opponent is not out of time!\n");
+ return COM_OK;
+ }
+ }
+
+#else
+
+ if ((garray[g].wTime <= 0) && (garray[g].bTime <= 0)) {
+ player_decline_offers(p, -1, -1);
+ game_ended(g, myColor, END_BOTHFLAG);
+ return COM_OK;
+ }
+ if (myColor == WHITE) {
+ if (garray[g].bTime > 0) {
+ pprintf(p, "Your opponent is not out of time!\n");
+ return COM_OK;
+ }
+ } else {
+ if (garray[g].wTime > 0) {
+ pprintf(p, "Your opponent is not out of time!\n");
+ return COM_OK;
+ }
+ }
+
+#endif
+
+ player_decline_offers(p, -1, -1);
+ if (player_has_mating_material(&garray[g].game_state, myColor))
+ game_ended(g, myColor, END_FLAG);
+ else
+ game_ended(g, myColor, END_FLAGNOMATERIAL);
+ return COM_OK;
+}
+
+PUBLIC int com_adjourn(int p, param_list param)
+{
+ int p1, g, myColor, yourColor;
+
+ ASSERT(param[0].type == TYPE_NULL);
+ if (!pIsPlaying(p))
+ return COM_OK;
+
+ p1 = parray[p].opponent;
+ g = parray[p].game;
+ if (!(parray[p].registered && parray[p1].registered)) {
+ pprintf(p, "Both players must be registered to adjorn a game. Use \"abort\".\n");
+ return COM_OK;
+ }
+ if (garray[g].link >= 0) {
+ pprintf(p, "Bughouse games cannot be adjourned.\n");
+ return COM_OK;
+ }
+ myColor = (p == garray[g].white ? WHITE : BLACK);
+ yourColor = (myColor == WHITE ? BLACK : WHITE);
+
+ if (player_find_pendfrom(p, p1, PEND_ADJOURN) >= 0) {
+ player_remove_request(p1, p, PEND_ADJOURN);
+ player_decline_offers(p, -1, -1);
+ game_ended(parray[p].game, yourColor, END_ADJOURN);
+ } else {
+ game_update_time(g);
+ if (((myColor == WHITE) && (garray[g].wTime > 0) && (garray[g].bTime <= 0))
+ || ((myColor == BLACK) && (garray[g].bTime > 0) && (garray[g].wTime <= 0))) {
+/* player wants to adjourn + opponent is out of time = courtesyadjourn */
+ pprintf(p, "Since you have time, and your opponent has none, the game has been adjourned.");
+ pprintf(p1, "Your opponent has adjourned the game rather than calling your flag.");
+ player_decline_offers(p, -1, -1);
+ game_ended(g, myColor, END_COURTESYADJOURN);
+ } else {
+ pprintf(p1, "\n");
+ pprintf_highlight(p1, "%s", parray[p].name);
+ pprintf(p1, " would like to adjourn the game; ");
+ pprintf_prompt(p1, "type \"adjourn\" to accept.\n");
+ pprintf(p, "Adjourn request sent.\n");
+ player_add_request(p, p1, PEND_ADJOURN, 0);
+ }
+ }
+ return COM_OK;
+}
+#if 0 /* this might as well die now */
+PUBLIC int com_load(int p, param_list param)
+{
+ pprintf(p, "Obsolete command, please use match <opponent>.\n");
+ return COM_OK;
+}
+#endif
+
+PUBLIC int com_takeback(int p, param_list param)
+{
+ int nHalfMoves = 1;
+ int from;
+ int g, i;
+ int p1;
+
+ if (!pIsPlaying(p)) {
+ return COM_OK;
+ }
+ p1 = parray[p].opponent;
+ if (parray[p1].simul_info.numBoards &&
+ parray[p1].simul_info.boards[parray[p1].simul_info.onBoard] !=
+ parray[p].game) {
+ pprintf(p, "You can only make requests when the simul player is at your board.\n");
+ return COM_OK;
+ }
+ g = parray[p].game;
+ if (garray[g].link >= 0) {
+ pprintf(p, "Takeback not implemented for bughouse games yet.\n");
+ return COM_OK;
+ }
+ if (param[0].type == TYPE_INT) {
+ nHalfMoves = param[0].val.integer;
+ }
+ if ((from = player_find_pendfrom(p, parray[p].opponent, PEND_TAKEBACK)) >= 0) {
+ player_remove_request(parray[p].opponent, p, PEND_TAKEBACK);
+ if (parray[p].p_from_list[from].param1 == nHalfMoves) {
+ /* Doing the takeback */
+ player_decline_offers(p, -1, -PEND_SIMUL);
+ for (i = 0; i < nHalfMoves; i++) {
+ if (backup_move(g, REL_GAME) != MOVE_OK) {
+ pprintf(garray[g].white, "Can only backup %d moves\n", i);
+ pprintf(garray[g].black, "Can only backup %d moves\n", i);
+ break;
+ }
+ }
+
+#ifdef TIMESEAL
+
+ garray[g].wTimeWhenReceivedMove = 0;
+ garray[g].bTimeWhenReceivedMove = 0;
+
+#endif
+
+ send_boards(g);
+ } else {
+ if (garray[g].numHalfMoves < nHalfMoves) {
+ pprintf(p, "There are only %d half moves in your game.\n", garray[g].numHalfMoves);
+ pprintf_prompt(parray[p].opponent, "\n%s has declined the takeback request.\n", parray[p].name, nHalfMoves);
+ return COM_OK;
+ }
+ pprintf(p, "You disagree on the number of half-moves to takeback.\n");
+ pprintf(p, "Alternate takeback request sent.\n");
+ pprintf_prompt(parray[p].opponent, "\n%s proposes a different number (%d) of half-move(s).\n", parray[p].name, nHalfMoves);
+ player_add_request(p, parray[p].opponent, PEND_TAKEBACK, nHalfMoves);
+ }
+ } else {
+ if (garray[g].numHalfMoves < nHalfMoves) {
+ pprintf(p, "There are only %d half moves in your game.\n", garray[g].numHalfMoves);
+ return COM_OK;
+ }
+ pprintf(parray[p].opponent, "\n");
+ pprintf_highlight(parray[p].opponent, "%s", parray[p].name);
+ pprintf_prompt(parray[p].opponent, " would like to take back %d half move(s).\n",
+ nHalfMoves);
+ pprintf(p, "Takeback request sent.\n");
+ player_add_request(p, parray[p].opponent, PEND_TAKEBACK, nHalfMoves);
+ }
+ return COM_OK;
+}
+
+
+PUBLIC int com_switch(int p, param_list param)
+{
+ int g = parray[p].game;
+ int tmp, now;
+ int p1;
+ char *strTmp;
+
+ if (!pIsPlaying(p)) {
+ return COM_OK;
+ }
+ p1 = parray[p].opponent;
+ if (parray[p1].simul_info.numBoards &&
+ parray[p1].simul_info.boards[parray[p1].simul_info.onBoard] != g) {
+ pprintf(p, "You can only make requests when the simul player is at your board.\n");
+ return COM_OK;
+ }
+ if (garray[g].link >= 0) {
+ pprintf(p, "Switch not implemented for bughouse games.\n");
+ return COM_OK;
+ }
+ if (player_find_pendfrom(p, parray[p].opponent, PEND_SWITCH) >= 0) {
+ player_remove_request(parray[p].opponent, p, PEND_SWITCH);
+ /* Doing the switch */
+ player_decline_offers(p, -1, -PEND_SIMUL);
+
+ tmp = garray[g].white;
+ garray[g].white = garray[g].black;
+ garray[g].black = tmp;
+ parray[p].side = (parray[p].side == WHITE) ? BLACK : WHITE;
+ strTmp = strdup(garray[g].white_name);
+ strcpy(garray[g].white_name, garray[g].black_name);
+ strcpy(garray[g].black_name, strTmp);
+ strfree(strTmp);
+
+ parray[parray[p].opponent].side =
+ (parray[parray[p].opponent].side == WHITE) ? BLACK : WHITE;
+ /* Roll back the time */
+ if (garray[g].game_state.onMove == WHITE) {
+ garray[g].wTime += (garray[g].lastDecTime - garray[g].lastMoveTime);
+ } else {
+ garray[g].bTime += (garray[g].lastDecTime - garray[g].lastMoveTime);
+ }
+ now = tenth_secs();
+ if (garray[g].numHalfMoves == 0)
+ garray[g].timeOfStart = now;
+ garray[g].lastMoveTime = now;
+ garray[g].lastDecTime = now;
+ send_boards(g);
+ return COM_OK;
+ }
+ if (garray[g].rated && garray[g].numHalfMoves > 0) {
+ pprintf(p, "You cannot switch sides once a rated game is underway.\n");
+ return COM_OK;
+ }
+ pprintf(parray[p].opponent, "\n");
+ pprintf_highlight(parray[p].opponent, "%s", parray[p].name);
+ pprintf_prompt(parray[p].opponent, " would like to switch sides.\nType \"accept\" to switch sides, or \"decline\" to refuse.\n");
+ pprintf(p, "Switch request sent.\n");
+ player_add_request(p, parray[p].opponent, PEND_SWITCH, 0);
+ return COM_OK;
+}
+
+PUBLIC int com_time(int p, param_list param)
+{
+ int p1, g;
+
+ if (param[0].type == TYPE_NULL) {
+ g = parray[p].game;
+ if (!pIsPlaying(p)) {
+ return COM_OK;
+ }
+ } else {
+ g = GameNumFromParam(p, &p1, &param[0]);
+ if (g < 0)
+ return COM_OK;
+ }
+ if ((g < 0) || (g >= g_num) || (garray[g].status != GAME_ACTIVE)) {
+ pprintf(p, "There is no such game.\n");
+ return COM_OK;
+ }
+ game_update_time(g);
+ pprintf(p, "White (%s) : %d mins, %d secs\n",
+ parray[garray[g].white].name,
+ garray[g].wTime / 600,
+ (garray[g].wTime - ((garray[g].wTime / 600) * 600)) / 10);
+ pprintf(p, "Black (%s) : %d mins, %d secs\n",
+ parray[garray[g].black].name,
+ garray[g].bTime / 600,
+ (garray[g].bTime - ((garray[g].bTime / 600) * 600)) / 10);
+ return COM_OK;
+}
+
+PUBLIC int com_boards(int p, param_list param)
+{
+ char *category = NULL;
+ char dname[MAX_FILENAME_SIZE];
+ DIR *dirp;
+#ifdef USE_DIRENT
+ struct dirent *dp;
+#else
+ struct direct *dp;
+#endif
+
+ if (param[0].type == TYPE_WORD)
+ category = param[0].val.word;
+ if (category) {
+ pprintf(p, "Boards Available For Category %s:\n", category);
+ sprintf(dname, "%s/%s", board_dir, category);
+ } else {
+ pprintf(p, "Categories Available:\n");
+ sprintf(dname, "%s", board_dir);
+ }
+ dirp = opendir(dname);
+ if (!dirp) {
+ pprintf(p, "No such category %s, try \"boards\".\n", category);
+ return COM_OK;
+ }
+
+/* YUK! what a mess, how about printing an ordered directory? - DAV*/
+
+ for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
+ if (!strcmp(dp->d_name, "."))
+ continue;
+ if (!strcmp(dp->d_name, ".."))
+ continue;
+ pprintf(p, "%s\n", dp->d_name);
+ }
+ closedir(dirp);
+ return COM_OK;
+}
+
+PUBLIC int com_simmatch(int p, param_list param)
+{
+ int p1, g, adjourned;
+ int num;
+ char tmp[100];
+
+ if ((parray[p].game >=0) &&(garray[parray[p].game].status == GAME_EXAMINE)) {
+ pprintf(p, "You are still examining a game.\n");
+ return COM_OK;
+ }
+ p1 = player_find_part_login(param[0].val.word);
+ if (p1 < 0) {
+ pprintf(p, "No user named \"%s\" is logged in.\n", param[0].val.word);
+ return COM_OK;
+ }
+ if (p == p1) {
+ pprintf(p, "You can't simmatch yourself!\n");
+ return COM_OK;
+ }
+ if (player_find_pendfrom(p, p1, PEND_SIMUL) >= 0) {
+ player_remove_request(p, p1, PEND_MATCH);
+ player_remove_request(p1, p, PEND_MATCH);
+ player_remove_request(p, p1, PEND_SIMUL);
+ player_remove_request(p1, p, PEND_SIMUL);
+ player_withdraw_offers(p, -1, PEND_SIMUL);
+ player_decline_offers(p1, -1, PEND_SIMUL);
+ player_withdraw_offers(p1, -1, PEND_SIMUL);
+ player_decline_offers(p, -1, PEND_MATCH);
+ player_withdraw_offers(p, -1, PEND_MATCH);
+ player_decline_offers(p1, -1, PEND_MATCH);
+ player_withdraw_offers(p1, -1, PEND_MATCH);
+ player_decline_offers(p, -1, PEND_PARTNER);
+ player_withdraw_offers(p, -1, PEND_PARTNER);
+ player_decline_offers(p1, -1, PEND_PARTNER);
+ player_withdraw_offers(p1, -1, PEND_PARTNER);
+
+ /* Accepting Simul ! */
+
+ if (parray[p].simul_info.numBoards >= MAX_SIMUL) {
+ pprintf(p, "You are already playing the maximum of %d boards.\n", MAX_SIMUL);
+ pprintf(p1, "Simul request removed, boards filled.\n");
+ return COM_OK;
+ }
+ unobserveAll(p); /* stop observing when match starts */
+ unobserveAll(p1);
+
+ g = game_new();
+ adjourned = 0;
+ if (game_read(g, p, p1) >= 0)
+ adjourned = 1;
+ if (!adjourned) { /* no adjourned game, so begin a new game */
+ game_remove(g);
+
+ if (create_new_match(p, p1, 0, 0, 0, 0, 0, "standard", "standard", 1)) {
+ pprintf(p, "There was a problem creating the new match.\n");
+ pprintf_prompt(p1, "There was a problem creating the new match.\n");
+ return COM_OK;
+ }
+ } else { /* resume adjourned game */
+ game_delete(p, p1);
+
+ sprintf(tmp, "{Game %d (%s vs. %s) Continuing %s %s simul.}\n", g + 1, parray[p].name, parray[p1].name, rstr[garray[g].rated], bstr[garray[g].type]);
+ pprintf(p, tmp);
+ pprintf(p1, tmp);
+
+ garray[g].white = p;
+ garray[g].black = p1;
+ garray[g].status = GAME_ACTIVE;
+ garray[g].startTime = tenth_secs();
+ garray[g].lastMoveTime = garray[g].startTime;
+ garray[g].lastDecTime = garray[g].startTime;
+ parray[p].game = g;
+ parray[p].opponent = p1;
+ parray[p].side = WHITE;
+ parray[p1].game = g;
+ parray[p1].opponent = p;
+ parray[p1].side = BLACK;
+ send_boards(g);
+ }
+
+ num = parray[p].simul_info.numBoards;
+ parray[p].simul_info.results[num] = -1;
+ parray[p].simul_info.boards[num] = parray[p].game;
+ parray[p].simul_info.numBoards++;
+ if (parray[p].simul_info.numBoards > 1 &&
+ parray[p].simul_info.onBoard >= 0)
+ player_goto_board(p, parray[p].simul_info.onBoard);
+ else
+ parray[p].simul_info.onBoard = 0;
+ return COM_OK;
+ }
+ if (player_find_pendfrom(p, -1, PEND_SIMUL) >= 0) {
+ pprintf(p, "You cannot be the simul giver and request to join another simul.\nThat would just be too confusing for me and you.\n");
+ return COM_OK;
+ }
+ if (parray[p].simul_info.numBoards) {
+ pprintf(p, "You cannot be the simul giver and request to join another simul.\nThat would just be too confusing for me and you.\n");
+ return COM_OK;
+ }
+ if (parray[p].game >=0) {
+ pprintf(p, "You are already playing a game.\n");
+ return COM_OK;
+ }
+ if (!parray[p1].sopen) {
+ pprintf_highlight(p, "%s", parray[p1].name);
+ pprintf(p, " is not open to receiving simul requests.\n");
+ return COM_OK;
+ }
+ if (parray[p1].simul_info.numBoards >= MAX_SIMUL) {
+ pprintf_highlight(p, "%s", parray[p1].name);
+ pprintf(p, " is already playing the maximum of %d boards.\n", MAX_SIMUL);
+ return COM_OK;
+ }
+/* loon: checking for some crazy situations we can't allow :) */
+
+ if ((parray[p1].game >=0) &&(parray[p1].simul_info.numBoards == 0)) {
+ pprintf_highlight(p, "%s", parray[p1].name);
+ if (parray[garray[parray[p1].game].white].simul_info.numBoards) {
+ pprintf(p, " is playing in ");
+ pprintf_highlight(p, "%s", parray[parray[p1].opponent].name);
+ pprintf(p, "'s simul, and can't accept.\n");
+ } else {
+ pprintf(p, " can't begin a simul while playing a non-simul game.\n");
+ }
+ return COM_OK;
+ }
+/* loon: this was (p, p1, PEND_SIMUL) but player_add_request needs 4 args...
+if 0 is the incorrect 4th arg, please fix :) */
+
+ g = game_new(); /* Check if an adjourned untimed game */
+ adjourned = ((game_read(g, p, p1) < 0) && (game_read(g, p1, p) < 0)) ? 0 : 1;
+ if (adjourned) {
+ if (!(garray[g].type == TYPE_UNTIMED))
+ adjourned = 0;
+ }
+ game_remove(g);
+
+ if (player_add_request(p, p1, PEND_SIMUL, 0)) {
+ pprintf(p, "Maximum number of pending actions reached. Your request was not sent.\nTry again later.\n");
+ return COM_OK;
+ } else {
+ pprintf(p1, "\n");
+ pprintf_highlight(p1, "%s", parray[p].name);
+ if (adjourned) {
+ pprintf_prompt(p1, " requests to continue an adjourned simul game.\n");
+ pprintf(p, "Request to resume simul sent. Adjourned game found.\n");
+ } else {
+ pprintf_prompt(p1, " requests to join a simul match with you.\n");
+ pprintf(p, "Simul match request sent.\n");
+ }
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_goboard(int p, param_list param)
+{
+ int on, g, p1;
+
+ if (!parray[p].simul_info.numBoards) {
+ pprintf(p, "You are not giving a simul.\n");
+ return COM_OK;
+ }
+ p1 = player_find_part_login(param[0].val.word);
+ if (p1 < 0) {
+ pprintf(p, "No user named \"%s\" is logged in.\n", param[0].val.word);
+ return COM_OK;
+ }
+ if (p == p1) {
+ pprintf(p, "You can't goboard yourself!\n");
+ return COM_OK;
+ }
+ on = parray[p].simul_info.onBoard;
+ g = parray[p].simul_info.boards[on];
+ if (p1 == garray[g].black) {
+ pprintf(p, "You are already at that board!\n");
+ return COM_OK;
+ }
+ if (parray[p].simul_info.numBoards > 1) {
+ player_decline_offers(p, -1, -PEND_SIMUL);
+ if (player_goto_simulgame_bynum(p, parray[p1].game) !=-1) {
+ if (g >= 0) {
+ pprintf(garray[g].black, "\n");
+ pprintf_highlight(garray[g].black, "%s", parray[p].name);
+ pprintf_prompt(garray[g].black, " has moved away from your board.\n");
+ }
+ }
+ } else
+ pprintf(p, "You are only playing one board!\n");
+ return COM_OK;
+}
+
+PUBLIC int com_gonum(int p, param_list param)
+{
+ int on, g, gamenum;
+
+ if (!parray[p].simul_info.numBoards) {
+ pprintf(p, "You are not giving a simul.\n");
+ return COM_OK;
+ }
+ on = parray[p].simul_info.onBoard;
+ g = parray[p].simul_info.boards[on];
+ gamenum = param[0].val.integer - 1;
+ if (gamenum < 0)
+ gamenum = 0;
+ if (on == gamenum) {
+ pprintf(p, "You are already at that board!\n");
+ return COM_OK;
+ }
+ if (parray[p].simul_info.numBoards > 1) {
+ player_decline_offers(p, -1, -PEND_SIMUL);
+ if (player_goto_simulgame_bynum(p, gamenum) != -1) {
+ if (g >= 0) {
+ pprintf(garray[g].black, "\n");
+ pprintf_highlight(garray[g].black, "%s", parray[p].name);
+ pprintf_prompt(garray[g].black, " has moved away from your board.\n");
+ }
+ }
+ } else
+ pprintf(p, "You are only playing one board!\n");
+ return COM_OK;
+}
+
+PUBLIC int com_simnext(int p, param_list param)
+{
+ int on, g;
+
+ if (!parray[p].simul_info.numBoards) {
+ pprintf(p, "You are not giving a simul.\n");
+ return COM_OK;
+ }
+ if (parray[p].simul_info.numBoards > 1) {
+ player_decline_offers(p, -1, -PEND_SIMUL);
+ on = parray[p].simul_info.onBoard;
+ g = parray[p].simul_info.boards[on];
+ if (g >= 0) {
+ pprintf(garray[g].black, "\n");
+ pprintf_highlight(garray[g].black, "%s", parray[p].name);
+ pprintf_prompt(garray[g].black, " is moving away from your board.\n");
+ player_goto_next_board(p);
+ }
+ } else
+ pprintf(p, "You are only playing one board!\n");
+ return COM_OK;
+}
+
+
+PUBLIC int com_simprev(int p, param_list param)
+{
+ int on, g;
+
+ if (!parray[p].simul_info.numBoards) {
+ pprintf(p, "You are not giving a simul.\n");
+ return COM_OK;
+ }
+ if (parray[p].simul_info.numBoards > 1) {
+ player_decline_offers(p, -1, -PEND_SIMUL);
+ on = parray[p].simul_info.onBoard;
+ g = parray[p].simul_info.boards[on];
+ if (g >= 0) {
+ pprintf(garray[g].black, "\n");
+ pprintf_highlight(garray[g].black, "%s", parray[p].name);
+ pprintf_prompt(garray[g].black, " is moving back to the previous board.\n");
+ }
+ player_goto_prev_board(p);
+ } else
+ pprintf(p, "You are only playing one board!\n");
+ return COM_OK;
+}
+
+PUBLIC int com_simgames(int p, param_list param)
+{
+ int p1 = p;
+
+ if (param[0].type == TYPE_WORD) {
+ if ((p1 = player_find_part_login(param[0].val.word)) < 0) {
+ pprintf(p, "No player named %s is logged in.\n", param[0].val.word);
+ return COM_OK;
+ }
+ }
+ if (p1 == p)
+ pprintf(p, "You are playing %d simultaneous games.\n",
+ player_num_active_boards(p1));
+ else
+ pprintf(p, "%s is playing %d simultaneous games.\n", parray[p1].name,
+ player_num_active_boards(p1));
+ return COM_OK;
+}
+
+PUBLIC int com_simpass(int p, param_list param)
+{
+ int g, p1, on;
+
+ if (!pIsPlaying(p)) {
+ return COM_OK;
+ }
+ g = parray[p].game;
+ p1 = garray[g].white;
+ if (!parray[p1].simul_info.numBoards) {
+ pprintf(p, "You are not participating in a simul.\n");
+ return COM_OK;
+ }
+ if (p == p1) {
+ pprintf(p, "You are the simul holder and cannot pass!\n");
+ return COM_OK;
+ }
+ if (player_num_active_boards(p1) == 1) {
+ pprintf(p, "This is the only game, so passing is futile.\n");
+ return COM_OK;
+ }
+ on = parray[p1].simul_info.onBoard;
+ if (parray[p1].simul_info.boards[on] != g) {
+ pprintf(p, "You cannot pass until the simul holder arrives!\n");
+ return COM_OK;
+ }
+ if (garray[g].passes >= MAX_SIMPASS) {
+ if (parray[p].bell)
+ pprintf(p, "\a");
+ pprintf(p, "You have reached your maximum of %d pass(es).\n", MAX_SIMPASS);
+ pprintf(p, "Please move IMMEDIATELY!\n");
+ pprintf_highlight(p1, "%s", parray[p].name);
+ pprintf_prompt(p1, " tried to pass, but is out of passes.\n");
+ return COM_OK;
+ }
+ player_decline_offers(p, -1, -PEND_SIMUL);
+
+ garray[g].passes++;
+ pprintf(p, "You have passed and have %d pass(es) left.\n",
+ (MAX_SIMPASS - garray[g].passes));
+ pprintf_highlight(p1, "%s", parray[p].name);
+ pprintf_prompt(p1, " has decided to pass and has %d pass(es) left.\n",
+ (MAX_SIMPASS - garray[g].passes));
+ player_goto_next_board(p1);
+ return COM_OK;
+}
+
+PUBLIC int com_simabort(int p, param_list param)
+{
+ if (!parray[p].simul_info.numBoards) {
+ pprintf(p, "You are not giving a simul.\n");
+ return COM_OK;
+ }
+ player_decline_offers(p, -1, -PEND_SIMUL);
+ game_ended(parray[p].simul_info.boards[parray[p].simul_info.onBoard],
+ WHITE, END_ABORT);
+ return COM_OK;
+}
+
+PUBLIC int com_simallabort(int p, param_list param)
+{
+ int i;
+
+ if (!parray[p].simul_info.numBoards) {
+ pprintf(p, "You are not giving a simul.\n");
+ return COM_OK;
+ }
+ player_decline_offers(p, -1, -PEND_SIMUL);
+ for (i = 0; i < parray[p].simul_info.numBoards; i++) {
+ if (parray[p].simul_info.boards[i] >= 0) {
+ game_ended(parray[p].simul_info.boards[i],
+ WHITE, END_ABORT);
+ }
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_simadjourn(int p, param_list param)
+{
+ if (!parray[p].simul_info.numBoards) {
+ pprintf(p, "You are not giving a simul.\n");
+ return COM_OK;
+ }
+ player_decline_offers(p, -1, -PEND_SIMUL);
+ game_ended(parray[p].simul_info.boards[parray[p].simul_info.onBoard],
+ WHITE, END_ADJOURN);
+ return COM_OK;
+}
+
+PUBLIC int com_simalladjourn(int p, param_list param)
+{
+ int i;
+
+ if (!parray[p].simul_info.numBoards) {
+ pprintf(p, "You are not giving a simul.\n");
+ return COM_OK;
+ }
+ player_decline_offers(p, -1, -PEND_SIMUL);
+ for (i = 0; i < parray[p].simul_info.numBoards; i++) {
+ if (parray[p].simul_info.boards[i] >= 0) {
+ game_ended(parray[p].simul_info.boards[i],
+ WHITE, END_ADJOURN);
+ }
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_moretime(int p, param_list param)
+{
+ int g, increment;
+
+ ASSERT(param[0].type == TYPE_INT);
+ if ((parray[p].game >=0) &&(garray[parray[p].game].status == GAME_EXAMINE)) {
+ pprintf(p, "You cannot use moretime in an examined game.\n");
+ return COM_OK;
+ }
+ increment = param[0].val.integer;
+ if (increment <= 0) {
+ pprintf(p, "Moretime requires an integer value greater than zero.\n");
+ return COM_OK;
+ }
+ if (!pIsPlaying(p)) {
+ return COM_OK;
+ }
+ if (increment > 600) {
+ pprintf(p, "Moretime has a maximum limit of 600 seconds.\n");
+ increment = 600;
+ }
+ g = parray[p].game;
+ if (garray[g].white == p) {
+ garray[g].bTime += increment * 10;
+#ifdef TIMESEAL
+ garray[g].bRealTime += increment * 10 * 100;
+#endif
+ pprintf(p, "%d seconds were added to your opponents clock\n",
+ increment);
+ pprintf_prompt(parray[p].opponent,
+ "\nYour opponent has added %d seconds to your clock.\n",
+ increment);
+ }
+ if (garray[g].black == p) {
+ garray[g].wTime += increment * 10;;
+#ifdef TIMESEAL
+ garray[g].wRealTime += increment * 10 * 100;
+#endif
+ pprintf(p, "%d seconds were added to your opponents clock\n",
+ increment);
+ pprintf_prompt(parray[p].opponent,
+ "\nYour opponent has added %d seconds to your clock.\n",
+ increment);
+ }
+ return COM_OK;
+}
+
diff --git a/FICS/gameproc.c.orig b/FICS/gameproc.c.orig
new file mode 100644
index 0000000..832f5c9
--- /dev/null
+++ b/FICS/gameproc.c.orig
@@ -0,0 +1,2826 @@
+/* gameproc.c
+ *
+ */
+
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name email yy/mm/dd Change
+ Richard Nash 93/10/22 Created
+*/
+
+#include "stdinclude.h"
+
+#include "common.h"
+#include "command.h"
+#include "ficsmain.h"
+#include "config.h"
+#include "playerdb.h"
+#include "gamedb.h"
+#include "gameproc.h"
+#include "movecheck.h"
+#include "utils.h"
+#include "ratings.h"
+#include "rmalloc.h"
+#include "comproc.h"
+#include "matchproc.h"
+#include "formula.h"
+#include "eco.h"
+#include "network.h"
+/* #include "hostinfo.h" */
+
+PUBLIC void game_ended(int g, int winner, int why)
+{
+ char outstr[200];
+ char tmp[200];
+ int p;
+ int rate_change = 0;
+ int isDraw = 0;
+ int whiteResult;
+ char winSymbol[10];
+ char EndSymbol[10];
+ char *NameOfWinner, *NameOfLoser;
+ int beingplayed = 0; /* i.e. it wasn't loaded for adjudication */
+
+ beingplayed = (parray[garray[g].black].game == g);
+
+ sprintf(outstr, "\n{Game %d (%s vs. %s) ", g + 1,
+ parray[garray[g].white].name,
+ parray[garray[g].black].name);
+ garray[g].result = why;
+ garray[g].winner = winner;
+ if (winner == WHITE) {
+ whiteResult = RESULT_WIN;
+ strcpy(winSymbol, "1-0");
+ NameOfWinner = parray[garray[g].white].name;
+ NameOfLoser = parray[garray[g].black].name;
+ } else {
+ whiteResult = RESULT_LOSS;
+ strcpy(winSymbol, "0-1");
+ NameOfWinner = parray[garray[g].black].name;
+ NameOfLoser = parray[garray[g].white].name;
+ }
+ switch (why) {
+ case END_CHECKMATE:
+ sprintf(tmp, "%s checkmated} %s\n", NameOfLoser, winSymbol);
+ strcpy(EndSymbol, "Mat");
+ rate_change = 1;
+ break;
+ case END_RESIGN:
+ sprintf(tmp, "%s resigns} %s\n", NameOfLoser, winSymbol);
+ strcpy(EndSymbol, "Res");
+ rate_change = 1;
+ break;
+ case END_FLAG:
+ sprintf(tmp, "%s forfeits on time} %s\n", NameOfLoser, winSymbol);
+ strcpy(EndSymbol, "Fla");
+ rate_change = 1;
+ break;
+ case END_STALEMATE:
+ sprintf(tmp, "Game drawn by stalemate} 1/2-1/2\n");
+ isDraw = 1;
+ strcpy(EndSymbol, "Sta");
+ rate_change = 1;
+ whiteResult = RESULT_DRAW;
+ break;
+ case END_AGREEDDRAW:
+ sprintf(tmp, "Game drawn by mutual agreement} 1/2-1/2\n");
+ isDraw = 1;
+ strcpy(EndSymbol, "Agr");
+ rate_change = 1;
+ whiteResult = RESULT_DRAW;
+ break;
+ case END_BOTHFLAG:
+ sprintf(tmp, "Game drawn because both players ran out of time} 1/2-1/2\n");
+ isDraw = 1;
+ strcpy(EndSymbol, "Fla");
+ rate_change = 1;
+ whiteResult = RESULT_DRAW;
+ break;
+ case END_REPETITION:
+ sprintf(tmp, "Game drawn by repetition} 1/2-1/2\n");
+ isDraw = 1;
+ strcpy(EndSymbol, "Rep");
+ rate_change = 1;
+ whiteResult = RESULT_DRAW;
+ break;
+ case END_50MOVERULE:
+ sprintf(tmp, "Game drawn by the 50 move rule} 1/2-1/2\n");
+ isDraw = 1;
+ strcpy(EndSymbol, "50");
+ rate_change = 1;
+ whiteResult = RESULT_DRAW;
+ break;
+ case END_ADJOURN:
+ sprintf(tmp, "Game adjourned by mutual agreement} *\n");
+ game_save(g);
+ break;
+ case END_LOSTCONNECTION:
+ sprintf(tmp, "%s lost connection; game ", NameOfWinner);
+ if (parray[garray[g].white].registered && parray[garray[g].black].registered) {
+ sprintf(tmp, "adjourned} *\n");
+ game_save(g);
+ } else
+ sprintf(tmp, "aborted} *\n");
+ whiteResult = RESULT_ABORT;
+ break;
+ case END_ABORT:
+ sprintf(tmp, "Game aborted by mutual agreement} *\n");
+ whiteResult = RESULT_ABORT;
+ break;
+ case END_COURTESY:
+ sprintf(tmp, "Game courtesyaborted by %s} *\n", NameOfWinner);
+ whiteResult = RESULT_ABORT;
+ break;
+ case END_COURTESYADJOURN:
+ sprintf(tmp, "Game courtesyadjourned by %s} *\n", NameOfWinner);
+ game_save(g);
+ break;
+ case END_NOMATERIAL:
+ /* Draw by insufficient material (e.g., lone K vs. lone K) */
+ sprintf(tmp, "Neither player has mating material} 1/2-1/2\n");
+ isDraw = 1;
+ strcpy(EndSymbol, "NM ");
+ rate_change = 1;
+ whiteResult = RESULT_DRAW;
+ break;
+ case END_FLAGNOMATERIAL:
+ sprintf(tmp, "%s ran out of time and %s has no material to mate} 1/2-1/2\n",
+ NameOfLoser, NameOfWinner);
+ isDraw = 1;
+ strcpy(EndSymbol, "TM ");
+ rate_change = 1;
+ whiteResult = RESULT_DRAW;
+ break;
+ case END_ADJWIN:
+ sprintf(tmp, "%s wins by adjudication} %s\n", NameOfWinner, winSymbol);
+ strcpy(EndSymbol, "Adj");
+ rate_change = 1;
+ break;
+ case END_ADJDRAW:
+ sprintf(tmp, "Game drawn by adjudication} 1/2-1/2\n");
+ isDraw = 1;
+ strcpy(EndSymbol, "Adj");
+ rate_change = 1;
+ whiteResult = RESULT_DRAW;
+ break;
+ case END_ADJABORT:
+ sprintf(tmp, "Game aborted by adjudication} *\n");
+ whiteResult = RESULT_ABORT;
+ break;
+ default:
+ sprintf(tmp, "Hmm, the game ended and I don't know why} *\n");
+ break;
+ }
+ strcat(outstr, tmp);
+ if (beingplayed) {
+ pprintf_noformat(garray[g].white, outstr);
+ pprintf_noformat(garray[g].black, outstr);
+
+ for (p = 0; p < p_num; p++) {
+ if ((p == garray[g].white) || (p == garray[g].black))
+ continue;
+ if (parray[p].status != PLAYER_PROMPT)
+ continue;
+ if (!parray[p].i_game && !player_is_observe(p, g))
+ continue;
+ pprintf_noformat(p, outstr);
+ pprintf_prompt(p, "");
+ }
+ }
+ if ((garray[g].rated) && (rate_change)) {
+ /* Adjust ratings */
+ rating_update(g);
+ if (parray[garray[g].white].network_player &&
+ parray[garray[g].black].network_player) {
+ if (MailGameResult) { /* Send to ratings server */
+ if (isDraw) {
+ /* hostinfo_mailresults(garray[g].type == TYPE_BLITZ ? "blitz" :
+ garray[g].type == TYPE_WILD ? "wild " : "stand", "
+ parray[garray[g].white].name, parray[garray[g].black].name,
+ "draw"); */
+ } else {
+/* hostinfo_mailresults(garray[g].type == TYPE_BLITZ ? "blitz" : garray[g].type == TYPE_WILD ? "wild " : "stand",
+ parray[garray[g].white].name,
+ parray[garray[g].black].name,
+ (winner == WHITE) ? parray[garray[g].white].name :
+ parray[garray[g].black].name); */
+ }
+ }
+ }
+ } else {
+ if (beingplayed) {
+ pprintf(garray[g].white, "No ratings adjustment done.\n");
+ pprintf(garray[g].black, "No ratings adjustment done.\n");
+ }
+ }
+ if (rate_change)
+ game_write_complete(g, isDraw, EndSymbol);
+ /* Mail off the moves */
+ if (parray[garray[g].white].automail) {
+ pcommand(garray[g].white, "mailmoves");
+ }
+ if (parray[garray[g].black].automail) {
+ pcommand(garray[g].black, "mailmoves");
+ }
+ parray[garray[g].white].num_white++;
+ parray[garray[g].white].lastColor = WHITE;
+ parray[garray[g].black].num_black++;
+ parray[garray[g].black].lastColor = BLACK;
+ parray[garray[g].white].last_opponent = garray[g].black;
+ parray[garray[g].black].last_opponent = garray[g].white;
+ if (beingplayed) {
+ parray[garray[g].white].game = -1;
+ parray[garray[g].black].game = -1;
+ parray[garray[g].white].opponent = -1;
+ parray[garray[g].black].opponent = -1;
+ if (garray[g].white != commanding_player)
+ pprintf_prompt(garray[g].white, "");
+ if (garray[g].black != commanding_player)
+ pprintf_prompt(garray[g].black, "");
+ if (parray[garray[g].white].simul_info.numBoards) {
+ player_simul_over(garray[g].white, g, whiteResult);
+ }
+ }
+ game_finish(g);
+}
+
+PUBLIC void process_move(int p, char *command)
+{
+ int g;
+ move_t move;
+ int result;
+ unsigned now;
+ int len; /* loon: for lame promotion hack */
+ int i;
+
+ if (parray[p].game <0) {
+ pprintf(p, "You are not playing a game.\n");
+ return;
+ }
+ player_decline_offers(p, -1, -PEND_SIMUL);
+ g = parray[p].game;
+
+ if (garray[g].status != GAME_EXAMINE) {
+ if (parray[p].side != garray[g].game_state.onMove) {
+ pprintf(p, "It is not your move.\n");
+ return;
+ }
+ if (garray[g].clockStopped) {
+ pprintf(p, "Game clock is paused, use \"unpause\" to resume.\n");
+ return;
+ }
+ }
+ if ((len = strlen(command)) > 1) {
+ if (command[len - 2] == '=') {
+ switch (tolower(command[strlen(command) - 1])) {
+ case 'n':
+ parray[p].promote = KNIGHT;
+ break;
+ case 'b':
+ parray[p].promote = BISHOP;
+ break;
+ case 'r':
+ parray[p].promote = ROOK;
+ break;
+ case 'q':
+ parray[p].promote = QUEEN;
+ break;
+ default:
+ pprintf(p, "Don't understand that move.\n");
+ return;
+ break;
+ }
+ }
+ }
+ switch (parse_move(command, &garray[g].game_state, &move, parray[p].promote)) {
+ case MOVE_ILLEGAL:
+ pprintf(p, "Illegal move.\n");
+ return;
+ break;
+ case MOVE_AMBIGUOUS:
+ pprintf(p, "Ambiguous move.\n");
+ return;
+ break;
+ default:
+ break;
+ }
+
+ if (garray[g].status == GAME_EXAMINE) {
+ garray[g].numHalfMoves++;
+ if (garray[g].numHalfMoves > garray[g].examMoveListSize) {
+ garray[g].examMoveListSize += 20; /* Allocate 20 moves at a time */
+ if (!garray[g].examMoveList) {
+ garray[g].examMoveList = (move_t *) rmalloc(sizeof(move_t) * garray[g].examMoveListSize);
+ } else {
+ garray[g].examMoveList = (move_t *) rrealloc(garray[g].examMoveList, sizeof(move_t) * garray[g].examMoveListSize);
+ }
+ }
+ result = execute_move(&garray[g].game_state, &move, 1);
+ move.atTime = now;
+ move.tookTime = 0;
+ garray[g].examMoveList[garray[g].numHalfMoves - 1] = move;
+ /* roll back time */
+ if (garray[g].game_state.onMove == WHITE) {
+ garray[g].wTime += (garray[g].lastDecTime - garray[g].lastMoveTime);
+ } else {
+ garray[g].bTime += (garray[g].lastDecTime - garray[g].lastMoveTime);
+ }
+ now = tenth_secs();
+ if (garray[g].numHalfMoves == 0)
+ garray[g].timeOfStart = now;
+ garray[g].lastMoveTime = now;
+ garray[g].lastDecTime = now;
+
+ } else { /* real game */
+ i = parray[p].opponent;
+ if (parray[i].simul_info.numBoards &&
+ (parray[i].simul_info.boards[parray[i].simul_info.onBoard] != g)) {
+ pprintf(p, "It isn't your turn: wait until the simul giver is at your board.\n");
+ return;
+ }
+#ifdef TIMESEAL
+
+
+ if (con[parray[p].socket].timeseal) { /* does he use timeseal? */
+ if (parray[p].side == WHITE) {
+ garray[g].wLastRealTime = garray[g].wRealTime;
+ garray[g].wTimeWhenMoved = con[parray[p].socket].time;
+ if (((garray[g].wTimeWhenMoved - garray[g].wTimeWhenReceivedMove) < 0) ||
+ (garray[g].wTimeWhenReceivedMove == 0)) {
+ /* might seem weird - but could be caused by a person moving BEFORE
+ he receives the board pos (this is possible due to lag) but it's
+ safe to say he moved in 0 secs :-) */
+ garray[g].wTimeWhenReceivedMove = garray[g].wTimeWhenMoved;
+ } else {
+ garray[g].wRealTime -= garray[g].wTimeWhenMoved - garray[g].wTimeWhenReceivedMove;
+ }
+ } else if (parray[p].side == BLACK) {
+ garray[g].bLastRealTime = garray[g].bRealTime;
+ garray[g].bTimeWhenMoved = con[parray[p].socket].time;
+ if (((garray[g].bTimeWhenMoved - garray[g].bTimeWhenReceivedMove) < 0) ||
+ (garray[g].bTimeWhenReceivedMove == 0)) {
+ /* might seem weird - but could be caused by a person moving BEFORE
+ he receives the board pos (this is possible due to lag) but it's
+ safe to say he moved in 0 secs :-) */
+ garray[g].bTimeWhenReceivedMove = garray[g].bTimeWhenMoved;
+ } else {
+ garray[g].bRealTime -= garray[g].bTimeWhenMoved - garray[g].bTimeWhenReceivedMove;
+ }
+ }
+ }
+ if (parray[p].side == WHITE) {
+ /* we need to reset the opp's time for receiving the board since the
+ timeseal decoder only alters the time if it's 0 Otherwise the time
+ would be changed if the player did a refresh which would screw up
+ the timings */
+ garray[g].bTimeWhenReceivedMove = 0;
+ } else {
+ /* we need to reset the opp's time for receiving the board since the
+ timeseal decoder only alters the time if it's 0 Otherwise the time
+ would be changed if the player did a refresh which would screw up
+ the timings */
+ garray[g].wTimeWhenReceivedMove = 0;
+ }
+
+#endif
+
+ game_update_time(g);
+
+ /* maybe add autoflag here in the future? */
+
+#ifdef TIMESEAL
+
+ if (con[parray[p].socket].timeseal) { /* does he use timeseal? */
+ if (parray[p].side == WHITE) {
+ garray[g].wRealTime += garray[g].wIncrement * 100;
+ garray[g].wTime = garray[g].wRealTime / 100; /* remember to conv to
+ tenth secs */
+ } else if (parray[p].side == BLACK) {
+ garray[g].bRealTime += garray[g].bIncrement * 100; /* conv to ms */
+ garray[g].bTime = garray[g].bRealTime / 100; /* remember to conv to
+ tenth secs */
+ }
+ } else {
+ if (garray[g].game_state.onMove == BLACK) {
+ garray[g].bTime += garray[g].bIncrement;
+ }
+ if (garray[g].game_state.onMove == WHITE) {
+ garray[g].wTime += garray[g].wIncrement;
+ }
+ }
+
+#else
+
+ if (garray[g].game_state.onMove == BLACK) {
+ garray[g].bTime += garray[g].bIncrement;
+ }
+ if (garray[g].game_state.onMove == WHITE) {
+ garray[g].wTime += garray[g].wIncrement;
+ }
+#endif
+
+ /* Do the move */
+ garray[g].numHalfMoves++;
+ if (garray[g].numHalfMoves > garray[g].moveListSize) {
+ garray[g].moveListSize += 20; /* Allocate 20 moves at a time */
+ if (!garray[g].moveList) {
+ garray[g].moveList = (move_t *) rmalloc(sizeof(move_t) * garray[g].moveListSize);
+ } else {
+ garray[g].moveList = (move_t *) rrealloc(garray[g].moveList, sizeof(move_t) * garray[g].moveListSize);
+ }
+ }
+ result = execute_move(&garray[g].game_state, &move, 1);
+ now = tenth_secs();
+ move.atTime = now;
+ if (garray[g].numHalfMoves > 1) {
+ move.tookTime = move.atTime - garray[g].lastMoveTime;
+ } else {
+ move.tookTime = move.atTime - garray[g].startTime;
+ }
+ garray[g].lastMoveTime = now;
+ garray[g].lastDecTime = now;
+
+#ifdef TIMESEAL
+
+ if (con[parray[p].socket].timeseal) { /* does he use timeseal? */
+ if (parray[p].side == WHITE) {
+ move.tookTime = (garray[parray[p].game].wTimeWhenMoved -
+ garray[parray[p].game].wTimeWhenReceivedMove) / 100;
+ } else {
+ move.tookTime = (garray[parray[p].game].bTimeWhenMoved -
+ garray[parray[p].game].bTimeWhenReceivedMove) / 100;
+ }
+ }
+#endif
+
+ garray[g].moveList[garray[g].numHalfMoves - 1] = move;
+ }
+
+ send_boards(g);
+
+ strcpy(garray[g].boardList[garray[g].numHalfMoves], boardToFEN(g));
+
+ if (result == MOVE_ILLEGAL) {
+ pprintf(p, "Internal error, illegal move accepted!\n");
+ }
+ if ((result == MOVE_OK) && (garray[g].status == GAME_EXAMINE)) {
+ int p1;
+
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (parray[p1].status != PLAYER_PROMPT)
+ continue;
+ if (player_is_observe(p1, g) || parray[p1].game == g) {
+ pprintf_prompt(p1, "%s moves: %s\n", parray[p].name, move.algString);
+ }
+ }
+ }
+ if (result == MOVE_CHECKMATE) {
+ if (garray[g].status == GAME_EXAMINE) {
+ int p1;
+
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (parray[p1].status != PLAYER_PROMPT)
+ continue;
+ if (player_is_observe(p1, g) || parray[p1].game == g) {
+ pprintf(p1, "%s has been checkmated.\n",
+ (CToggle(garray[g].game_state.onMove) == BLACK) ? "White" : "Black");
+ }
+ }
+ } else {
+ game_ended(g, CToggle(garray[g].game_state.onMove), END_CHECKMATE);
+ }
+ }
+ if (result == MOVE_STALEMATE) {
+ if (garray[g].status == GAME_EXAMINE) {
+ int p1;
+
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (parray[p1].status != PLAYER_PROMPT)
+ continue;
+ if (player_is_observe(p1, g) || parray[p1].game == g) {
+ pprintf(p1, "Stalemate.\n");
+ }
+ }
+ } else {
+ game_ended(g, CToggle(garray[g].game_state.onMove), END_STALEMATE);
+ }
+ }
+ if (result == MOVE_NOMATERIAL) {
+ if (garray[g].status == GAME_EXAMINE) {
+ int p1;
+
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (parray[p1].status != PLAYER_PROMPT)
+ continue;
+ if (player_is_observe(p1, g) || parray[p1].game == g) {
+ pprintf(p1, "No mating material.\n");
+ }
+ }
+ } else {
+ game_ended(g, CToggle(garray[g].game_state.onMove), END_NOMATERIAL);
+ }
+ }
+}
+
+int GameNumFromParam(int p, int *p1, parameter *param)
+{
+ if (param->type == TYPE_WORD) {
+ *p1 = player_find_part_login(param->val.word);
+ if (*p1 < 0) {
+ pprintf(p, "No user named \"%s\" is logged in.\n", param->val.word);
+ return -1;
+ }
+ if (parray[*p1].game <0)
+ pprintf(p, "%s is not playing a game.\n", parray[*p1].name);
+ return parray[*p1].game;
+ } else { /* Must be an integer */
+ *p1 = -1;
+ if (param->val.integer <= 0)
+ pprintf(p, "%d is not a valid game number.\n", param->val.integer);
+ return param->val.integer - 1;
+ }
+}
+
+PUBLIC int com_resign(int p, param_list param)
+{
+ int g, o, oconnected, confused = 0;
+
+ if (param[0].type == TYPE_NULL) {
+ g = parray[p].game;
+ if ((g < 0) || (garray[g].status == GAME_EXAMINE)) {
+ pprintf(p, "You are not playing a game.\n");
+ } else {
+ player_decline_offers(p, -1, -1);
+ game_ended(g, (garray[g].white == p) ? BLACK : WHITE, END_RESIGN);
+ }
+ } else if (FindPlayer(p, &param[0], &o, &oconnected)) {
+ g = game_new();
+ if (game_read(g, p, o) < 0) {
+ if (game_read(g, o, p) < 0) {
+ confused = 1;
+ pprintf(p, "You have no stored game with %s\n", parray[o].name);
+ } else {
+ garray[g].white = o;
+ garray[g].black = p;
+ }
+ } else {
+ garray[g].white = p;
+ garray[g].black = o;
+ }
+ if (!confused) {
+ pprintf(p, "You resign your stored game with %s\n", parray[o].name);
+ game_delete(garray[g].white, garray[g].black);
+ game_ended(g, (garray[g].white == p) ? BLACK : WHITE, END_RESIGN);
+ pcommand(p, "message %s I have resigned our stored game \"%s vs. %s.\"",
+ parray[o].name,
+ parray[garray[g].white].name,
+ parray[garray[g].black].name);
+ if (!oconnected)
+ player_remove(o);
+ }
+ }
+ return COM_OK;
+}
+
+/*
+PUBLIC int com_resign(int p, param_list param)
+{
+ ASSERT(param[0].type == TYPE_NULL);
+ if ((parray[p].game <0) || (garray[parray[p].game].status == GAME_EXAMINE)) {
+ pprintf(p, "You are not playing a game.\n");
+ return COM_OK;
+ }
+ player_decline_offers(p, -1, -1);
+ game_ended(parray[p].game, (garray[parray[p].game].white == p) ? BLACK : WHITE, END_RESIGN);
+ return COM_OK;
+}
+*/
+
+PUBLIC int com_draw(int p, param_list param)
+{
+ int p1;
+ int move_num = 0;
+ int i = 0, flag = 0;
+
+ ASSERT(param[0].type == TYPE_NULL);
+ if ((parray[p].game <0) ||(garray[parray[p].game].status == GAME_EXAMINE)) {
+ pprintf(p, "You are not playing a game.\n");
+ return COM_OK;
+ }
+ if (garray[parray[p].game].numHalfMoves - ((garray[parray[p].game].game_state.lastIrreversable == -1) ? 0 : garray[parray[p].game].game_state.lastIrreversable) > 99) {
+ game_ended(parray[p].game, (garray[parray[p].game].white == p) ? BLACK : WHITE, END_50MOVERULE);
+ return COM_OK;
+ }
+ if (garray[parray[p].game].game_state.lastIrreversable != -1)
+ move_num = garray[parray[p].game].game_state.lastIrreversable;
+
+ for (i = (garray[parray[p].game].numHalfMoves - 1); i <= garray[parray[p].game].numHalfMoves; i++) {
+ flag = 0;
+ if (garray[parray[p].game].game_state.lastIrreversable != -1)
+ move_num = garray[parray[p].game].game_state.lastIrreversable;
+ for (; move_num <= garray[parray[p].game].numHalfMoves; move_num++) {
+ if ((strlen(garray[parray[p].game].boardList[move_num])) !=
+ (strlen(garray[parray[p].game].boardList[i])))
+ continue;
+
+ if (!strcmp(garray[parray[p].game].boardList[move_num],
+ garray[parray[p].game].boardList[i]))
+ flag++;
+ }
+ if (flag >= 3) {
+ if (player_find_pendfrom(p, parray[p].opponent, PEND_DRAW) >= 0) {
+ player_remove_request(parray[p].opponent, p, PEND_DRAW);
+ player_decline_offers(p, -1, -1);
+ }
+ game_ended(parray[p].game, (garray[parray[p].game].white == p) ? BLACK : WHITE, END_REPETITION);
+ return COM_OK;
+ }
+ }
+
+ p1 = parray[p].opponent;
+ if (parray[p1].simul_info.numBoards &&
+ parray[p1].simul_info.boards[parray[p1].simul_info.onBoard] !=
+ parray[p].game) {
+ pprintf(p, "You can only make requests when the simul player is at your board.\n");
+ return COM_OK;
+ }
+ if (player_find_pendfrom(p, parray[p].opponent, PEND_DRAW) >= 0) {
+ player_remove_request(parray[p].opponent, p, PEND_DRAW);
+ player_decline_offers(p, -1, -1);
+ game_ended(parray[p].game, (garray[parray[p].game].white == p) ? BLACK : WHITE, END_AGREEDDRAW);
+ } else {
+ pprintf(parray[p].opponent, "\n");
+ pprintf_highlight(parray[p].opponent, "%s", parray[p].name);
+ pprintf_prompt(parray[p].opponent, " offers you a draw.\n");
+ pprintf(p, "Draw request sent.\n");
+ player_add_request(p, parray[p].opponent, PEND_DRAW, 0);
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_pause(int p, param_list param)
+{
+ int g;
+ int now;
+
+ ASSERT(param[0].type == TYPE_NULL);
+ if ((parray[p].game <0) ||(garray[parray[p].game].status == GAME_EXAMINE)) {
+ pprintf(p, "You are not playing a game.\n");
+ return COM_OK;
+ }
+ g = parray[p].game;
+ if (garray[g].wTime == 0) {
+ pprintf(p, "You can't pause untimed games.\n");
+ return COM_OK;
+ }
+ if (garray[g].clockStopped) {
+ pprintf(p, "Game is already paused, use \"unpause\" to resume.\n");
+ return COM_OK;
+ }
+ if (player_find_pendfrom(p, parray[p].opponent, PEND_PAUSE) >= 0) {
+ player_remove_request(parray[p].opponent, p, PEND_PAUSE);
+ garray[g].clockStopped = 1;
+ /* Roll back the time */
+ if (garray[g].game_state.onMove == WHITE) {
+ garray[g].wTime += (garray[g].lastDecTime - garray[g].lastMoveTime);
+ } else {
+ garray[g].bTime += (garray[g].lastDecTime - garray[g].lastMoveTime);
+ }
+ now = tenth_secs();
+ if (garray[g].numHalfMoves == 0)
+ garray[g].timeOfStart = now;
+ garray[g].lastMoveTime = now;
+ garray[g].lastDecTime = now;
+ send_boards(g);
+ pprintf_prompt(parray[p].opponent, "\n%s accepted pause. Game clock paused.\n",
+ parray[p].name);
+ pprintf(p, "Game clock paused.\n");
+ } else {
+ pprintf(parray[p].opponent, "\n");
+ pprintf_highlight(parray[p].opponent, "%s", parray[p].name);
+ pprintf_prompt(parray[p].opponent, " requests to pause the game.\n");
+ pprintf(p, "Pause request sent.\n");
+ player_add_request(p, parray[p].opponent, PEND_PAUSE, 0);
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_unpause(int p, param_list param)
+{
+ int now;
+ int g;
+
+ ASSERT(param[0].type == TYPE_NULL);
+ if ((parray[p].game <0) ||(garray[parray[p].game].status == GAME_EXAMINE)) {
+ pprintf(p, "You are not playing a game.\n");
+ return COM_OK;
+ }
+ g = parray[p].game;
+ if (!garray[g].clockStopped) {
+ pprintf(p, "Game is not paused.\n");
+ return COM_OK;
+ }
+ garray[g].clockStopped = 0;
+ now = tenth_secs();
+ if (garray[g].numHalfMoves == 0)
+ garray[g].timeOfStart = now;
+ garray[g].lastMoveTime = now;
+ garray[g].lastDecTime = now;
+ send_boards(g);
+ pprintf(p, "Game clock resumed.\n");
+ pprintf_prompt(parray[p].opponent, "\nGame clock resumed.\n");
+ return COM_OK;
+}
+
+PUBLIC int com_abort(int p, param_list param)
+{
+ int p1, g, myColor, yourColor;
+
+ ASSERT(param[0].type == TYPE_NULL);
+
+ g = parray[p].game;
+ if ((g < 0) || (garray[parray[p].game].status == GAME_EXAMINE)) {
+ pprintf(p, "You are not playing a game.\n");
+ return COM_OK;
+ }
+ p1 = parray[p].opponent;
+ myColor = (p == garray[g].white ? WHITE : BLACK);
+ yourColor = (myColor == WHITE ? BLACK : WHITE);
+
+ if (parray[p1].simul_info.numBoards &&
+ parray[p1].simul_info.boards[parray[p1].simul_info.onBoard] != g) {
+ pprintf(p, "You can only make requests when the simul player is at your board.\n");
+ return COM_OK;
+ }
+ if (player_find_pendfrom(p, p1, PEND_ABORT) >= 0) {
+ player_remove_request(p1, p, PEND_ABORT);
+ player_decline_offers(p, -1, -1);
+ game_ended(g, yourColor, END_ABORT);
+ } else {
+ game_update_time(g);
+
+#ifdef TIMESEAL
+
+ if (con[parray[parray[p].opponent].socket].timeseal) { /* opp uses timeseal? */
+
+ /* If opponent has timeseal - we have no use for courtesyabort since
+ courtesyabort was used when opponent lagged out of time which cannot
+ happen with timeseal - only courtesyadjourn should be possible.
+ Thus: the request can only be for a normal abort of the game. */
+
+ pprintf(p1, "\n");
+ pprintf_highlight(p1, "%s", parray[p].name);
+ pprintf_prompt(p1, " would like to abort the game.\n");
+ pprintf(p, "Abort request sent.\n");
+ player_add_request(p, p1, PEND_ABORT, 0);
+ } else {
+ if ((myColor == WHITE && garray[g].wTime > 0 && garray[g].bTime <= 0)
+ || (myColor == BLACK && garray[g].bTime > 0 && garray[g].wTime <= 0)) {
+ /* player wants to abort + opponent is out of time = courtesyabort */
+ pprintf(p, "Since you have time, and your opponent has none, the game has been aborted.");
+ pprintf(p1, "Your opponent has aborted the game rather than calling your flag.");
+ player_decline_offers(p, -1, -1);
+ game_ended(g, myColor, END_COURTESY);
+ } else {
+ pprintf(p1, "\n");
+ pprintf_highlight(p1, "%s", parray[p].name);
+ pprintf_prompt(p1, " would like to abort the game.\n");
+ pprintf(p, "Abort request sent.\n");
+ player_add_request(p, p1, PEND_ABORT, 0);
+ }
+ }
+
+#else
+
+ if ((myColor == WHITE && garray[g].wTime > 0 && garray[g].bTime <= 0)
+ || (myColor == BLACK && garray[g].bTime > 0 && garray[g].wTime <= 0)) {
+ /* player wants to abort + opponent is out of time = courtesyabort */
+ pprintf(p, "Since you have time, and your opponent has none, the game has been aborted.");
+ pprintf(p1, "Your opponent has aborted the game rather than calling your flag.");
+ player_decline_offers(p, -1, -1);
+ game_ended(g, myColor, END_COURTESY);
+ } else {
+ pprintf(p1, "\n");
+ pprintf_highlight(p1, "%s", parray[p].name);
+ pprintf(p1, " would like to abort the game; ");
+ pprintf_prompt(p1, "type \"abort\" to accept.\n");
+ pprintf(p, "Abort request sent.\n");
+ player_add_request(p, p1, PEND_ABORT, 0);
+ }
+
+#endif
+
+ }
+ return COM_OK;
+}
+
+#if 0
+int Courtesy(int p, int type)
+{
+ int g;
+
+ if (parray[p].game <0) {
+ pprintf(p, "You are not playing a game.\n");
+ return COM_OK;
+ }
+ g = parray[p].game;
+ if (garray[g].wInitTime == 0) {
+ if (type == END_COURTESY)
+ pprintf(p, "You can't courtesy abort an untimed game, just resign.\n");
+ else if (type == END_COURTESYADJOURN)
+ pprintf(p, "You can't courtesy adjourn an untimed game, just resign.\n");
+ return COM_OK;
+ }
+ game_update_time(g);
+ if (((garray[g].white == p) && (garray[g].bTime <= 0) && (garray[g].wTime > 0)) ||
+ ((garray[g].black == p) && (garray[g].wTime <= 0) && (garray[g].bTime > 0))) {
+ player_decline_offers(p, -1, -1);
+ game_ended(parray[p].game, (garray[g].white == p) ? WHITE : BLACK, type);
+ } else {
+ if (type == END_COURTESY)
+ pprintf(p, "Game not aborted.\n");
+ else if (type == END_COURTESYADJOURN)
+ pprintf(p, "Game not adjourned.\n");
+ pprintf(p, "Either your opponent still has time, or you are out of time also.\n");
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_courtesyabort(int p, param_list param)
+{
+ ASSERT(param[0].type == TYPE_NULL);
+ return Courtesy(p, END_COURTESY);
+}
+
+PUBLIC int com_courtesyadjourn(int p, param_list param)
+{
+ ASSERT(param[0].type == TYPE_NULL);
+ if (!garray[parray[p].game].rated) {
+ pprintf(p, "You cannot adjourn an unrated game. Use \"courtesyabort\".\n");
+ return COM_OK;
+ }
+ return Courtesy(p, END_COURTESYADJOURN);
+}
+#endif
+
+PUBLIC int com_courtesyabort(int p, param_list param)
+{
+ pprintf(p, "Courtesyabort is obsolete; use \"abort\" instead.\n");
+ return COM_OK;
+}
+
+PUBLIC int com_courtesyadjourn(int p, param_list param)
+{
+ pprintf(p, "Use \"adjourn\" to courtesyadjourn a game.\n");
+ return COM_OK;
+}
+
+PRIVATE int player_has_mating_material(game_state_t *gs, int color)
+{
+ int i, j;
+ int piece;
+ int minor_pieces = 0;
+
+ for (i = 0; i < 8; i++)
+ for (j = 0; j < 8; j++) {
+ piece = gs->board[i][j];
+ switch (piecetype(piece)) {
+ case BISHOP:
+ case KNIGHT:
+ if (iscolor(piece, color))
+ minor_pieces++;
+ break;
+ case KING:
+ case NOPIECE:
+ break;
+ default:
+ if (iscolor(piece, color))
+ return 1;
+ }
+ }
+ return ((minor_pieces > 1) ? 1 : 0);
+}
+
+PUBLIC int com_flag(int p, param_list param)
+{
+ int g;
+ int myColor;
+
+ ASSERT(param[0].type == TYPE_NULL);
+ if ((parray[p].game <0) ||(garray[parray[p].game].status == GAME_EXAMINE)) {
+ pprintf(p, "You are not playing a game.\n");
+ return COM_OK;
+ }
+ g = parray[p].game;
+ myColor = (p == garray[g].white ? WHITE : BLACK);
+ if (garray[g].wInitTime == 0) {
+ pprintf(p, "You can't flag an untimed game.\n");
+ return COM_OK;
+ }
+ if (garray[g].numHalfMoves < 2) {
+ pprintf(p, "You cannot flag if your opponent has not moved.\nYou can use abort instead.\n");
+ return COM_OK;
+ }
+ game_update_time(g);
+
+#ifdef TIMESEAL
+
+ {
+ int wt, bt;
+
+ if (con[parray[p].socket].timeseal) { /* do caller use timeseal? */
+ if (myColor == WHITE) {
+ wt = garray[g].wRealTime;
+ } else {
+ bt = garray[g].bRealTime;
+ }
+ } else {
+ if (myColor == WHITE) {
+ wt = garray[g].wTime;
+ } else {
+ bt = garray[g].bTime;
+ }
+ }
+
+ if (con[parray[parray[p].opponent].socket].timeseal) { /* opp uses timeseal? */
+ if (myColor == WHITE) {
+ bt = garray[g].bRealTime;
+ } else {
+ wt = garray[g].wRealTime;
+ }
+ } else {
+ if (myColor == WHITE) {
+ bt = garray[g].bTime;
+ } else {
+ wt = garray[g].wTime;
+ }
+ }
+
+ /* the clocks to compare is now in wt and bt */
+
+ if ((wt <= 0) && (bt <= 0)) {
+ player_decline_offers(p, -1, -1);
+ game_ended(g, myColor, END_BOTHFLAG);
+ return COM_OK;
+ }
+ if (myColor == WHITE) {
+ if (bt > 0) {
+ pprintf(p, "Your opponent is not out of time!\n");
+ return COM_OK;
+ }
+ } else {
+ if (wt > 0) {
+ pprintf(p, "Your opponent is not out of time!\n");
+ return COM_OK;
+ }
+ }
+ }
+
+#else
+
+ if ((garray[g].wTime <= 0) && (garray[g].bTime <= 0)) {
+ player_decline_offers(p, -1, -1);
+ game_ended(g, myColor, END_BOTHFLAG);
+ return COM_OK;
+ }
+ if (myColor == WHITE) {
+ if (garray[g].bTime > 0) {
+ pprintf(p, "Your opponent is not out of time!\n");
+ return COM_OK;
+ }
+ } else {
+ if (garray[g].wTime > 0) {
+ pprintf(p, "Your opponent is not out of time!\n");
+ return COM_OK;
+ }
+ }
+
+#endif
+
+ player_decline_offers(p, -1, -1);
+ if (player_has_mating_material(&garray[g].game_state, myColor))
+ game_ended(g, myColor, END_FLAG);
+ else
+ game_ended(g, myColor, END_FLAGNOMATERIAL);
+ return COM_OK;
+}
+
+PUBLIC int com_adjourn(int p, param_list param)
+{
+ int p1, g, myColor, yourColor;
+
+ ASSERT(param[0].type == TYPE_NULL);
+ g = parray[p].game;
+ if ((g < 0) || (garray[parray[p].game].status == GAME_EXAMINE)) {
+ pprintf(p, "You are not playing a game.\n");
+ return COM_OK;
+ }
+ p1 = parray[p].opponent;
+ if (!(parray[p].registered && parray[p1].registered)) {
+ pprintf(p, "Both players must be registered to adjorn a game. Use \"abort\".\n");
+ return COM_OK;
+ }
+ myColor = (p == garray[g].white ? WHITE : BLACK);
+ yourColor = (myColor == WHITE ? BLACK : WHITE);
+
+ if (player_find_pendfrom(p, p1, PEND_ADJOURN) >= 0) {
+ player_remove_request(p1, p, PEND_ADJOURN);
+ player_decline_offers(p, -1, -1);
+ game_ended(parray[p].game, yourColor, END_ADJOURN);
+ } else {
+ game_update_time(g);
+ if (((myColor == WHITE) && (garray[g].wTime > 0) && (garray[g].bTime <= 0))
+ || ((myColor == BLACK) && (garray[g].bTime > 0) && (garray[g].wTime <= 0))) {
+/* player wants to adjourn + opponent is out of time = courtesyadjourn */
+ pprintf(p, "Since you have time, and your opponent has none, the game has been adjourned.");
+ pprintf(p1, "Your opponent has adjourned the game rather than calling your flag.");
+ player_decline_offers(p, -1, -1);
+ game_ended(g, myColor, END_COURTESYADJOURN);
+ } else {
+ pprintf(p1, "\n");
+ pprintf_highlight(p1, "%s", parray[p].name);
+ pprintf(p1, " would like to adjourn the game; ");
+ pprintf_prompt(p1, "type \"adjourn\" to accept.\n");
+ pprintf(p, "Adjourn request sent.\n");
+ player_add_request(p, p1, PEND_ADJOURN, 0);
+ }
+ }
+ return COM_OK;
+}
+
+PRIVATE int gamesortfunc(const void *i, const void *j)
+{
+ return (GetRating(&parray[garray[*(int *) i].white], garray[*(int *) i].type) +
+ GetRating(&parray[garray[*(int *) i].black], garray[*(int *) i].type) +
+ ((garray[*(int *) i].status == GAME_EXAMINE) ? 10000 : 0) -
+ GetRating(&parray[garray[*(int *) j].white], garray[*(int *) j].type) -
+ GetRating(&parray[garray[*(int *) j].black], garray[*(int *) j].type) -
+ ((garray[*(int *) j].status == GAME_EXAMINE) ? 10000 : 0));
+}
+
+
+PUBLIC int com_games(int p, param_list param)
+{
+ int i, j;
+ int wp, bp;
+ int ws, bs;
+ int selected = 0;
+ int count = 0;
+ int totalcount;
+ char *s = NULL;
+ int slen = 0;
+ int *sortedgames; /* for qsort */
+
+ totalcount = game_count();
+ if (totalcount == 0) {
+ pprintf(p, "There are no games in progress.\n");
+ } else {
+ sortedgames = rmalloc(totalcount * sizeof(int)); /* for qsort */
+
+ if (param[0].type == TYPE_WORD) {
+ s = param[0].val.word;
+ slen = strlen(s);
+ selected = atoi(s);
+ if (selected < 0)
+ selected = 0;
+ }
+ for (i = 0; i < g_num; i++) {
+ if ((garray[i].status != GAME_ACTIVE) && (garray[i].status != GAME_EXAMINE))
+ continue;
+ if ((selected) && (selected != i + 1))
+ continue; /* not selected game number */
+ wp = garray[i].white;
+ bp = garray[i].black;
+ if ((!selected) && s && strncasecmp(s, garray[i].white_name, slen) &&
+ strncasecmp(s, garray[i].black_name, slen))
+ continue; /* player names did not match */
+ sortedgames[count++] = i;
+ }
+ if (!count)
+ pprintf(p, "No matching games were found (of %d in progress).\n", totalcount);
+ else {
+ qsort(sortedgames, count, sizeof(int), gamesortfunc);
+ pprintf(p, "\n");
+ for (j = 0; j < count; j++) {
+ i = sortedgames[j];
+ wp = garray[i].white;
+ bp = garray[i].black;
+ board_calc_strength(&garray[i].game_state, &ws, &bs);
+ if (garray[i].status != GAME_EXAMINE) {
+ pprintf_noformat(p, "%2d %4s %-11.11s %4s %-10.10s [%c%c%c%3d %3d] ",
+ i + 1,
+ ratstrii(GetRating(&parray[wp],
+ garray[i].type),
+ parray[wp].registered),
+ parray[wp].name,
+ ratstrii(GetRating(&parray[bp],
+ garray[i].type),
+ parray[bp].registered),
+ parray[bp].name,
+ (garray[i].private) ? 'p' : ' ',
+ *bstr[garray[i].type],
+ *rstr[garray[i].rated],
+ garray[i].wInitTime / 600,
+ garray[i].wIncrement / 10);
+ game_update_time(i);
+ pprintf_noformat(p, "%6s -",
+ tenth_str((garray[i].wTime > 0 ? garray[i].wTime : 0), 0));
+ pprintf_noformat(p, "%6s (%2d-%2d) %c: %2d\n",
+ tenth_str((garray[i].bTime > 0 ? garray[i].bTime : 0), 0),
+ ws, bs,
+ (garray[i].game_state.onMove == WHITE) ? 'W' : 'B',
+ garray[i].game_state.moveNum);
+ } else {
+ pprintf_noformat(p, "%2d (Exam. %4d %-11.11s %4d %-10.10s) [%c%c%c%3d %3d] ",
+ i + 1,
+ garray[i].white_rating,
+ garray[i].white_name,
+ garray[i].black_rating,
+ garray[i].black_name,
+ (garray[i].private) ? 'p' : ' ',
+ *bstr[garray[i].type],
+ *rstr[garray[i].rated],
+ garray[i].wInitTime / 600,
+ garray[i].wIncrement / 10);
+ pprintf_noformat(p, "%c: %2d\n",
+ (garray[i].game_state.onMove == WHITE) ? 'W' : 'B',
+ garray[i].game_state.moveNum);
+ }
+ }
+ if (count < totalcount)
+ pprintf(p, "\n %d game%s displayed (of %d in progress).\n", count,
+ (count == 1) ? "" : "s", totalcount);
+ else
+ pprintf(p, "\n %d game%s displayed.\n", totalcount, (totalcount == 1) ? "" : "s");
+ }
+ rfree(sortedgames);
+ }
+ return COM_OK;
+}
+
+PRIVATE int do_observe(int p, int obgame)
+{
+ if ((garray[obgame].private) && (parray[p].adminLevel < ADMIN_ADMIN)) {
+ pprintf(p, "Sorry, game %d is a private game.\n", obgame + 1);
+ return COM_OK;
+ }
+ if ((garray[obgame].white == p) || (garray[obgame].black == p)) {
+ if (garray[obgame].status != GAME_EXAMINE) {
+ pprintf(p, "You cannot observe a game that you are playing.\n");
+ return COM_OK;
+ }
+ }
+ if (player_is_observe(p, obgame)) {
+ pprintf(p, "Removing game %d from observation list.\n", obgame + 1);
+ player_remove_observe(p, obgame);
+ } else {
+ if (!player_add_observe(p, obgame)) {
+ pprintf(p, "You are now observing game %d.\n", obgame + 1);
+ send_board_to(obgame, p);
+ } else {
+ pprintf(p, "You are already observing the maximum number of games.\n");
+ }
+ }
+ return COM_OK;
+}
+
+PUBLIC void unobserveAll(int p)
+{
+ int i;
+
+ for (i = 0; i < parray[p].num_observe; i++) {
+ pprintf(p, "Removing game %d from observation list.\n", parray[p].observe_list[i] + 1);
+ }
+ parray[p].num_observe = 0;
+ return;
+}
+
+PUBLIC int com_unobserve(int p, param_list param)
+{
+ int gNum, p1;
+
+ if (param[0].type == TYPE_NULL) {
+ unobserveAll(p);
+ return COM_OK;
+ }
+ gNum = GameNumFromParam(p, &p1, &param[0]);
+ if (gNum < 0)
+ return COM_OK;
+ if (!player_is_observe(p, gNum)) {
+ pprintf(p, "You are not observing game %d.\n", gNum);
+ } else {
+ player_remove_observe(p, gNum);
+ pprintf(p, "Removing game %d from observation list.\n", gNum + 1);
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_observe(int p, param_list param)
+{
+ int i;
+ int p1, obgame;
+
+ if ((parray[p].game >=0) &&(garray[parray[p].game].status == GAME_EXAMINE)) {
+ pprintf(p, "You are still examining a game.\n");
+ return COM_OK;
+ }
+ if (param[0].type == TYPE_NULL) {
+ unobserveAll(p);
+ return COM_OK;
+ }
+ obgame = GameNumFromParam(p, &p1, &param[0]);
+ if (obgame < 0)
+ return COM_OK;
+
+ if ((obgame >= g_num) || ((garray[obgame].status != GAME_ACTIVE) &&
+ (garray[obgame].status != GAME_EXAMINE))) {
+ pprintf(p, "There is no such game.\n");
+ return COM_OK;
+ }
+ if ((p1 >= 0) && parray[p1].simul_info.numBoards) {
+ for (i = 0; i < parray[p1].simul_info.numBoards; i++)
+ if (parray[p1].simul_info.boards[i] >= 0)
+ do_observe(p, parray[p1].simul_info.boards[i]);
+ } else {
+ do_observe(p, obgame);
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_allobservers(int p, param_list param)
+{
+ int obgame;
+ int p1;
+ int start, end;
+ int g;
+ int first;
+
+ if (param[0].type == TYPE_NULL) {
+ obgame = -1;
+ } else {
+ obgame = GameNumFromParam(p, &p1, &param[0]);
+ if (obgame < 0)
+ return COM_OK;
+ }
+ if (obgame == -1) {
+ start = 0;
+ end = g_num;
+ } else if ((obgame >= g_num) || ((obgame < g_num)
+ && ((garray[obgame].status != GAME_ACTIVE)
+ && (garray[obgame].status != GAME_EXAMINE)))) {
+ pprintf(p, "There is no such game.\n");
+ return COM_OK;
+ } else {
+ start = obgame;
+ end = obgame + 1;
+ }
+
+ /* list games being played */
+
+ for (g = start; g < end; g++) {
+ if ((garray[g].status == GAME_ACTIVE) &&
+ ((parray[p].adminLevel > 0) || (garray[g].private == 0))) {
+ for (first = 1, p1 = 0; p1 < p_num; p1++) {
+ if ((parray[p1].status != PLAYER_EMPTY) && (player_is_observe(p1, g))) {
+ if (first) {
+ pprintf(p, "Observing %2d [%s vs. %s]:",
+ g + 1,
+ parray[garray[g].white].name,
+ parray[garray[g].black].name);
+ first = 0;
+ }
+ pprintf(p, " %s%s", (parray[p1].game >=0) ? "#" : "", parray[p1].name);
+ }
+ }
+ if (!first)
+ pprintf(p, "\n");
+ }
+ }
+
+ /* list games being examined last */
+
+ for (g = start; g < end; g++) {
+ if ((garray[g].status == GAME_EXAMINE) &&
+ ((parray[p].adminLevel > 0) || (garray[g].private == 0))) {
+ for (first = 1, p1 = 0; p1 < p_num; p1++) {
+ if ((parray[p1].status != PLAYER_EMPTY) && (player_is_observe(p1, g) ||
+ (parray[p1].game == g))) {
+ if (first) {
+ if (strcmp(garray[g].white_name, garray[g].black_name)) {
+ pprintf(p, "Examining %2d [%s vs %s]:", g + 1,
+ garray[g].white_name, garray[g].black_name);
+ } else {
+ pprintf(p, "Examining %2d (scratch):", g + 1);
+ }
+ first = 0;
+ }
+ pprintf(p, " %s%s", (parray[p1].game == g) ? "#" : "", parray[p1].name);
+ }
+ }
+ if (!first)
+ pprintf(p, "\n");
+ }
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_unexamine(int p, param_list param)
+{
+ int g, p1, flag = 0;
+
+ if ((parray[p].game <0) ||(garray[parray[p].game].status != GAME_EXAMINE)) {
+ pprintf(p, "You are not examining any games.\n");
+ return COM_OK;
+ }
+ g = parray[p].game;
+ parray[p].game = -1;
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (parray[p1].status != PLAYER_PROMPT)
+ continue;
+ if ((parray[p1].game == g) &&(p != p1)) {
+ /* ok - there are other examiners to take over the game */
+ flag = 1;
+ }
+ if ((player_is_observe(p1, g)) || (parray[p1].game == g)) {
+ pprintf(p1, "%s stopped examining game %d.\n", parray[p].name, g + 1);
+ }
+ }
+ if (!flag) {
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (parray[p1].status != PLAYER_PROMPT)
+ continue;
+ if (player_is_observe(p1, g)) {
+ pprintf(p1, "There are no examiners.\n");
+ pcommand(p1, "unobserve %d", g + 1);
+ }
+ }
+ game_remove(g);
+ }
+ pprintf(p, "You are no longer examining game %d.\n", g + 1);
+ return COM_OK;
+}
+
+PUBLIC int com_mexamine(int p, param_list param)
+{
+ int g, p1, p2;
+
+ if ((parray[p].game <0) ||(garray[parray[p].game].status != GAME_EXAMINE)) {
+ pprintf(p, "You are not examining any games.\n");
+ return COM_OK;
+ }
+ p1 = player_find_part_login(param[0].val.word);
+ if (p1 < 0) {
+ pprintf(p, "No user named \"%s\" is logged in.\n", param[0].val.word);
+ return COM_OK;
+ }
+ g = parray[p].game;
+ if (!player_is_observe(p1, g)) {
+ pprintf(p, "%s must observe the game you are analysing.\n", parray[p1].name);
+ return COM_OK;
+ } else {
+ if (parray[p1].game >=0) {
+ pprintf(p, "%s is already analysing the game.\n", parray[p1].name);
+ return COM_OK;
+ }
+ /* if we get here - let's make him examiner of the game */
+ unobserveAll(p1); /* fix for Xboard */
+ player_decline_offers(p1, -1, PEND_MATCH);
+ player_withdraw_offers(p1, -1, PEND_MATCH);
+ player_withdraw_offers(p1, -1, PEND_SIMUL);
+
+ parray[p1].game = g; /* yep - it really is that easy :-) */
+ pprintf(p1, "You are now examiner of game %d.\n", g + 1);
+ send_board_to(g, p1); /* pos not changed - but fixes Xboard */
+ for (p2 = 0; p2 < p_num; p2++) {
+ if (parray[p2].status != PLAYER_PROMPT)
+ continue;
+ if (p2 == p1)
+ continue;
+ if ((player_is_observe(p2, g)) || (parray[p2].game == g)) {
+ pprintf_prompt(p2, "%s is now examiner of game %d.\n", parray[p1].name, g + 1);
+ }
+ }
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_moves(int p, param_list param)
+{
+ int g;
+ int p1;
+
+ if (param[0].type == TYPE_NULL) {
+ if (parray[p].game >=0) {
+ g = parray[p].game;
+ } else if (parray[p].num_observe) {
+ for (g = 0; g < parray[p].num_observe; g++) {
+ pprintf(p, "%s\n", movesToString(parray[p].observe_list[g], 0));
+ }
+ return COM_OK;
+ } else {
+ pprintf(p, "You are neither playing, observing nor examining a game.\n");
+ return COM_OK;
+ }
+ } else {
+ g = GameNumFromParam(p, &p1, &param[0]);
+ if (g < 0)
+ return COM_OK;
+ }
+ if ((g < 0) || (g >= g_num) || ((garray[g].status != GAME_ACTIVE) &&
+ (garray[g].status != GAME_EXAMINE))) {
+ pprintf(p, "There is no such game.\n");
+ return COM_OK;
+ }
+ if ((garray[g].white != p) && (garray[g].black != p) && (garray[g].private) && (parray[p].adminLevel < ADMIN_ADMIN)) {
+ pprintf(p, "Sorry, that is a private game.\n");
+ return COM_OK;
+ }
+ pprintf(p, "%s\n", movesToString(g, 0)); /* pgn may break interfaces? */
+ return COM_OK;
+}
+
+PUBLIC int com_mailmoves(int p, param_list param)
+{
+ int g;
+ int p1;
+ char subj[81];
+
+ if (!parray[p].registered) {
+ pprintf(p, "Only registered people can use the mailhelp command.\n");
+ return COM_OK;
+ }
+
+ if (param[0].type == TYPE_NULL) {
+ if (parray[p].game >=0) {
+ g = parray[p].game;
+ } else {
+ pprintf(p, "You are neither playing, observing nor examining a game.\n");
+ return COM_OK;
+ }
+ } else {
+ g = GameNumFromParam(p, &p1, &param[0]);
+ if (g < 0)
+ return COM_OK;
+ }
+ if ((g < 0) || (g >= g_num) || ((garray[g].status != GAME_ACTIVE) && (garray[g].status != GAME_EXAMINE))) {
+ pprintf(p, "There is no such game.\n");
+ return COM_OK;
+ }
+ if ((garray[g].white != p) && (garray[g].black != p) && (garray[g].private) && (parray[p].adminLevel < ADMIN_ADMIN)) {
+ pprintf(p, "Sorry, that is a private game.\n");
+ return COM_OK;
+ }
+ sprintf(subj, "FICS game report %s vs %s", garray[g].white_name, garray[g].black_name);
+ if (mail_string_to_user(p, subj, movesToString(g, parray[p].pgn))) {
+ pprintf(p, "Moves NOT mailed, perhaps your address is incorrect.\n");
+ } else {
+ pprintf(p, "Moves mailed.\n");
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_oldmoves(int p, param_list param)
+{
+ int g;
+ int p1 = p;
+
+ if (param[0].type == TYPE_NULL) {
+ p1 = p;
+ } else if (param[0].type == TYPE_WORD) {
+ p1 = player_find_part_login(param[0].val.word);
+ if (p1 < 0) {
+ pprintf(p, "No user named \"%s\" is logged in.\n", param[0].val.word);
+ return COM_OK;
+ }
+ }
+ g = FindOldGameFor(p1);
+ if (g < 0) {
+ pprintf(p, "There is no old game for %s.\n", parray[p1].name);
+ return COM_OK;
+ }
+ if ((garray[g].white != p) && (garray[g].black != p) && (garray[g].private) && (parray[p].adminLevel < ADMIN_ADMIN)) {
+ pprintf(p, "Sorry, that is a private game.\n");
+ return COM_OK;
+ }
+ pprintf(p, "%s\n", movesToString(g, 0)); /* pgn may break interfaces? */
+ return COM_OK;
+}
+
+PUBLIC int com_mailoldmoves(int p, param_list param)
+{
+ int g;
+ int p1 = p;
+ char subj[81];
+
+ if (!parray[p].registered) {
+ pprintf(p, "Only registered people can use the mailhelp command.\n");
+ return COM_OK;
+ }
+
+ if (param[0].type == TYPE_NULL) {
+ p1 = p;
+ } else if (param[0].type == TYPE_WORD) {
+ p1 = player_find_part_login(param[0].val.word);
+ if (p1 < 0) {
+ pprintf(p, "No user named \"%s\" is logged in.\n", param[0].val.word);
+ return COM_OK;
+ }
+ }
+ g = FindOldGameFor(p1);
+ if (g < 0) {
+ pprintf(p, "There is no old game for %s.\n", parray[p1].name);
+ return COM_OK;
+ }
+ if ((garray[g].white != p) && (garray[g].black != p) && (garray[g].private) && (parray[p].adminLevel < ADMIN_ADMIN)) {
+ pprintf(p, "Sorry, that is a private game.\n");
+ return COM_OK;
+ }
+ sprintf(subj, "FICS game report %s vs %s", garray[g].white_name, garray[g].black_name);
+ if (mail_string_to_user(p, subj, movesToString(g, parray[p].pgn))) {
+ pprintf(p, "Moves NOT mailed, perhaps your address is incorrect.\n");
+ } else {
+ pprintf(p, "Moves mailed.\n");
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_load(int p, param_list param)
+{
+ pprintf(p, "Obsolete command, please use match <opponent>.\n");
+ return COM_OK;
+}
+
+void ExamineScratch(int p)
+{
+ char category, board;
+ int g = game_new();
+
+ unobserveAll(p);
+
+ player_decline_offers(p, -1, PEND_MATCH);
+ player_withdraw_offers(p, -1, PEND_MATCH);
+ player_withdraw_offers(p, -1, PEND_SIMUL);
+
+ garray[g].wInitTime = garray[g].wIncrement = 0;
+ garray[g].bInitTime = garray[g].bIncrement = 0;
+ garray[g].timeOfStart = tenth_secs();
+ garray[g].wTime = garray[g].bTime = 0;
+ garray[g].rated = 0;
+ garray[g].clockStopped = 0;
+ garray[g].type = TYPE_UNTIMED;
+ garray[g].white = garray[g].black = p;
+ garray[g].status = GAME_EXAMINE;
+ garray[g].startTime = tenth_secs();
+ garray[g].lastMoveTime = garray[g].startTime;
+ garray[g].lastDecTime = garray[g].startTime;
+ garray[g].totalHalfMoves = 0;
+
+ parray[p].side = WHITE; /* oh well... */
+ parray[p].game = g;
+
+ category = board = '\0';
+ if (board_init(&garray[g].game_state, &category, &board)) {
+ pprintf(p, "PROBLEM LOADING BOARD. Game Aborted.\n");
+ fprintf(stderr, "FICS: PROBLEM LOADING BOARD. Game Aborted.\n");
+ }
+ garray[g].game_state.gameNum = g;
+ strcpy(garray[g].white_name, parray[p].name);
+ strcpy(garray[g].black_name, parray[p].name);
+
+ send_boards(g);
+
+ strcpy(garray[g].boardList[garray[g].numHalfMoves], boardToFEN(g));
+}
+
+PRIVATE int ExamineStored(FILE * fp, int p, char *filename)
+{
+ int g;
+ char category[100], board[100];
+ game *gg;
+
+ unobserveAll(p);
+
+ player_decline_offers(p, -1, PEND_MATCH);
+ player_withdraw_offers(p, -1, PEND_MATCH);
+ player_withdraw_offers(p, -1, PEND_SIMUL);
+
+ g = game_new();
+ gg = &garray[g];
+ category[0] = '\0';
+ board[0] = '\0';
+ if (board_init(&gg->game_state, category, board)) {
+ pprintf(p, "PROBLEM LOADING BOARD. Game Aborted.\n");
+ fprintf(stderr, "FICS: PROBLEM LOADING BOARD %s %s. Game Aborted.\n",
+ category, board);
+ return -1;
+ }
+ gg->status = GAME_EXAMINE;
+ if (ReadGameAttrs(fp, filename, g) < 0) {
+ pprintf(p, "Gamefile is corrupt; please notify an admin.\n");
+ return -1;
+ }
+ gg->totalHalfMoves = gg->numHalfMoves;
+ gg->numHalfMoves = 0;
+ gg->revertHalfMove = 0;
+ gg->white = p;
+ gg->black = p;
+ gg->game_state.gameNum = g;
+
+ gg->startTime = tenth_secs();
+ gg->lastMoveTime = gg->startTime;
+ gg->lastDecTime = gg->startTime;
+
+ parray[p].side = WHITE; /* oh well... */
+ parray[p].game = g;
+
+ send_boards(g);
+
+ strcpy(gg->boardList[gg->numHalfMoves], boardToFEN(g));
+ return g;
+}
+
+void ExamineAdjourned(int p, int p1, int p2)
+{
+ FILE *fp;
+ char filename[1024];
+ char *p1Login, *p2Login;
+ int g;
+
+ p1Login = parray[p1].login;
+ p2Login = parray[p2].login;
+
+ sprintf(filename, "%s/%c/%s-%s", adj_dir, *p1Login, p1Login, p2Login);
+ fp = fopen(filename, "r");
+ if (!fp) {
+ sprintf(filename, "%s/%c/%s-%s", adj_dir, *p2Login, p1Login, p2Login);
+ fp = fopen(filename, "r");
+ if (!fp) {
+ sprintf(filename, "%s/%c/%s-%s", adj_dir, *p2Login, p2Login, p1Login);
+ fp = fopen(filename, "r");
+ if (!fp) {
+ sprintf(filename, "%s/%c/%s-%s", adj_dir, *p1Login, p2Login, p1Login);
+ fp = fopen(filename, "r");
+ if (!fp) {
+ pprintf(p, "No stored game between \"%s\" and \"%s\".\n",
+ parray[p1].name, parray[p2].name);
+ return;
+ }
+ }
+ }
+ }
+ g = ExamineStored(fp, p, filename);
+ fclose(fp);
+
+ if (g >= 0) {
+ if (garray[g].white_name[0] == '\0')
+ strcpy(garray[g].white_name, p1Login);
+ if (garray[g].black_name[0] == '\0')
+ strcpy(garray[g].black_name, p2Login);
+ }
+ return;
+}
+
+char *FindHistory(int p, int p1, int game)
+{
+ FILE *fpHist;
+ static char fileName[MAX_FILENAME_SIZE];
+ int index;
+ long when;
+
+ sprintf(fileName, "%s/player_data/%c/%s.%s", stats_dir,
+ parray[p1].login[0], parray[p1].login, STATS_GAMES);
+ fpHist = fopen(fileName, "r");
+ if (fpHist == NULL) {
+ pprintf(p, "No games in history for %s.\n", parray[p1].name);
+ return(NULL);
+ }
+ do {
+ fscanf(fpHist, "%d %*c %*d %*c %*d %*s %*s %*d %*d %*d %*d %*s %*s %ld",
+ &index, &when);
+ } while (!feof(fpHist) && index != game);
+
+ if (feof(fpHist)) {
+ pprintf(p, "There is no history game %d for %s.\n", game, parray[p1].name);
+ fclose(fpHist);
+ return(NULL);
+ }
+ fclose(fpHist);
+
+ sprintf(fileName, "%s/%ld/%ld", hist_dir, when % 100, when);
+ return(fileName);
+}
+
+void ExamineHistory(int p, int p1, int game)
+{
+ char *fileName;
+
+ fileName = FindHistory(p, p1, game);
+ if (fileName != NULL) {
+ FILE *fpGame = fopen(fileName, "r");
+ if (fpGame == NULL) {
+ pprintf(p, "History game %d not available for %s.\n", game, parray[p1].name);
+ } else {
+ ExamineStored(fpGame, p, fileName);
+ }
+ }
+ return;
+}
+
+PUBLIC int com_examine(int p, param_list param)
+{
+ int p1, p2 = p, p1conn, p2conn = 1;
+
+ if ((parray[p].game >=0) &&(garray[parray[p].game].status == GAME_EXAMINE)) {
+ pprintf(p, "You are already examining a game.\n");
+ } else if (parray[p].game >=0) {
+ pprintf(p, "You are playing a game.\n");
+ } else if (param[0].type == TYPE_NULL) {
+ ExamineScratch(p);
+ } else if (param[0].type == TYPE_WORD) {
+ if (!FindPlayer(p, &param[0], &p1, &p1conn))
+ return COM_OK;
+
+ if (param[1].type == TYPE_INT)
+ ExamineHistory(p, p1, param[1].val.integer);
+ else {
+ if (param[1].type == TYPE_WORD
+ && !FindPlayer(p, &param[1], &p2, &p2conn))
+ return COM_OK;
+
+ ExamineAdjourned(p, p1, p2);
+ if (!p2conn)
+ player_remove(p2);
+ }
+ if (!p1conn)
+ player_remove(p1);
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_stored(int p, param_list param)
+{
+ DIR *dirp;
+#ifdef USE_DIRENT
+ struct dirent *dp;
+#else
+ struct direct *dp;
+#endif
+ int p1, connected;
+ char dname[MAX_FILENAME_SIZE];
+#if 0 /* replacing all the code below with a
+ FindPlayer call; This stuff was buggy
+ anyway. */
+ if (param[0].type == TYPE_WORD) {
+ if ((p1 = player_find_part_login(param[0].val.word)) < 0) { /* not logged in */
+ connected = 0;
+ p1 = player_new();
+ if (player_read(p1, param[0].val.word)) {
+ player_remove(p1);
+ pprintf(p, "There is no player by that name.\n");
+ return COM_OK;
+ }
+ } else {
+ connected = 1;
+ }
+ } else {
+ p1 = p;
+ connected = 1;
+ }
+#endif
+ if (!FindPlayer(p, &param[0], &p1, &connected))
+ return COM_OK;
+
+ sprintf(dname, "%s/%c", adj_dir, parray[p1].login[0]);
+ dirp = opendir(dname);
+ if (!dirp) {
+ pprintf(p, "Player %s has no games stored.\n", parray[p1].name);
+ if (!connected)
+ player_remove(p1);
+ return COM_OK;
+ }
+ pprintf(p, "Stored games for %s:\n", parray[p1].name);
+ for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
+ if (file_has_pname(dp->d_name, parray[p1].login)) {
+ pprintf(p, " %s vs. %s\n", file_wplayer(dp->d_name), file_bplayer(dp->d_name));
+ }
+ }
+
+ closedir(dirp);
+ pprintf(p, "\n");
+ if (!connected)
+ player_remove(p1);
+ return COM_OK;
+}
+
+PUBLIC int com_mailstored(int p, param_list param)
+{
+ int wp, wconnected, bp, bconnected, gotit = 0;
+ int g = -1;
+
+ if (!FindPlayer(p, &param[0], &wp, &wconnected))
+ return (COM_OK);
+
+ if (param[1].type == TYPE_INT) { /* look for a game from history */
+ char *fileName = FindHistory(p, wp, param[1].val.integer);
+ if (fileName != NULL) {
+ FILE *fpGame = fopen(fileName, "r");
+ if (fpGame == NULL) {
+ pprintf(p, "History game %d not available for %s.\n", param[1].val.integer, parray[wp].name);
+ } else {
+ g = game_new();
+ if (ReadGameAttrs(fpGame, fileName, g) < 0)
+ pprintf(p, "Gamefile is corrupt; please notify an admin.\n");
+ else
+ gotit = 1;
+ }
+ fclose(fpGame);
+ }
+ } else { /* look for a stored game between the players */
+ if (FindPlayer(p, &param[1], &bp, &bconnected)) {
+
+ g = game_new();
+ if (game_read(g, wp, bp) >= 0) { /* look for a game white-black, */
+ gotit = 1;
+ } else if (game_read(g, bp, wp) >= 0) { /* or black-white */
+ gotit = 1;
+ } else {
+ pprintf(p, "There is no stored game %s vs. %s\n", parray[wp].name, parray[bp].name);
+ }
+ if (!bconnected)
+ player_remove(bp);
+ }
+ }
+
+ if (gotit) {
+ if (strcasecmp(parray[p].name, garray[g].white_name) && strcasecmp(parray[p]
+.name, garray[g].black_name) && garray[g].private && (parray[p].adminLevel < ADMIN_ADMIN)) {
+ pprintf(p, "Sorry, that is a private game.\n");
+ } else {
+ char subj[81];
+ if (param[1].type == TYPE_INT)
+ sprintf(subj, "FICS history game: %s %d", parray[wp].name, param[1].val.integer);
+ else
+ sprintf(subj, "FICS adjourned game %s vs %s", garray[g].white_name, garray[g].black_name);
+ if (mail_string_to_user(p, subj, movesToString(g, parray[p].pgn)))
+ pprintf(p, "Moves NOT mailed, perhaps your address is incorrect.\n");
+ else
+ pprintf(p, "Moves mailed.\n");
+ }
+ }
+ if (!wconnected)
+ player_remove(wp);
+ if (g != -1)
+ game_remove(g);
+ return(COM_OK);
+}
+
+/* it would be good to write a FindStored and nuke all the game_read(foo,bar)
+ game_read(bar,foo) etc. this stuff is a mess... */
+
+PUBLIC int com_smoves(int p, param_list param)
+{
+ int wp, wconnected, bp, bconnected, gotit = 0;
+ int g = -1;
+
+ if (!FindPlayer(p, &param[0], &wp, &wconnected))
+ return(COM_OK);
+
+ if (param[1].type == TYPE_INT) {
+/* look for a game from history */
+ char *fileName = FindHistory(p, wp, param[1].val.integer);
+ if (fileName != NULL) {
+ FILE *fpGame = fopen(fileName, "r");
+ if (fpGame == NULL) {
+ pprintf(p, "History game %d not available for %s.\n", param[1].val.integer, parray[wp].name);
+ } else {
+ g = game_new();
+ if (ReadGameAttrs(fpGame, fileName, g) < 0) {
+ pprintf(p, "Gamefile is corrupt; please notify an admin.\n");
+ } else {
+ gotit = 1;
+ }
+ }
+ fclose(fpGame);
+ }
+ } else {
+/* look for a stored game between the players */
+ if (FindPlayer(p, &param[1], &bp, &bconnected)) {
+
+ g = game_new();
+ if (game_read(g, wp, bp) >= 0) { /* look for a game white-black, */
+ gotit = 1;
+ } else if (game_read(g, bp, wp) >= 0) { /* or black-white */
+ gotit = 1;
+ } else {
+ pprintf(p, "There is no stored game %s vs. %s\n", parray[wp].name, parray[bp].name);
+ }
+ if (!bconnected)
+ player_remove(bp);
+ }
+ }
+
+ if (gotit) {
+ if (strcasecmp(parray[p].name, garray[g].white_name) && strcasecmp(parray[p].name, garray[g].black_name) && garray[g].private && (parray[p].adminLevel < ADMIN_ADMIN)) {
+ pprintf(p, "Sorry, that is a private game.\n");
+ } else {
+ pprintf(p, "%s\n", movesToString(g, 0));
+ }
+ }
+ if (!wconnected)
+ player_remove(wp);
+ if (g != -1)
+ game_remove(g);
+ return(COM_OK);
+}
+
+PUBLIC int com_sposition(int p, param_list param)
+{
+ int wp, wconnected, bp, bconnected, confused = 0;
+ int g;
+
+ if (!FindPlayer(p, &param[0], &wp, &wconnected))
+ return (COM_OK);
+ if (!FindPlayer(p, &param[1], &bp, &bconnected))
+ return COM_OK;
+
+ g = game_new();
+ if (game_read(g, wp, bp) < 0) { /* if no game white-black, */
+ if (game_read(g, bp, wp) < 0) { /* look for black-white */
+ confused = 1;
+ pprintf(p, "There is no stored game %s vs. %s\n", parray[wp].name, parray[bp].name);
+ } else {
+ int tmp;
+ tmp = wp;
+ wp = bp;
+ bp = tmp;
+ tmp = wconnected;
+ wconnected = bconnected;
+ bconnected = tmp;
+ }
+ }
+ if (!confused) {
+ if ((wp != p) && (bp != p) && (garray[g].private) && (parray[p].adminLevel < ADMIN_ADMIN)) {
+ pprintf(p, "Sorry, that is a private game.\n");
+ } else {
+ garray[g].white = wp;
+ garray[g].black = bp;
+ garray[g].startTime = tenth_secs();
+ garray[g].lastMoveTime = garray[g].startTime;
+ garray[g].lastDecTime = garray[g].startTime;
+ pprintf(p, "Position of stored game %s vs. %s\n", parray[wp].name, parray[bp].name);
+ send_board_to(g, p);
+ }
+ }
+ game_remove(g);
+ if (!wconnected)
+ player_remove(wp);
+ if (!bconnected)
+ player_remove(bp);
+ return COM_OK;
+}
+
+PUBLIC int com_forward(int p, param_list param)
+{
+ int nHalfMoves = 1;
+ int g, i;
+ int p1;
+ unsigned now;
+
+ if (!((parray[p].game >=0) &&(garray[parray[p].game].status == GAME_EXAMINE))) {
+ pprintf(p, "You are not examining any games.\n");
+ return COM_OK;
+ }
+ g = parray[p].game;
+ if (!strcmp(garray[g].white_name, garray[g].black_name)) {
+ pprintf(p, "You cannot go forward; no moves are stored.\n");
+ return COM_OK;
+ }
+ if (param[0].type == TYPE_INT) {
+ nHalfMoves = param[0].val.integer;
+ }
+ if (garray[g].numHalfMoves > garray[g].revertHalfMove) {
+ pprintf(p, "No more moves.\n");
+ return COM_OK;
+ }
+ if (garray[g].numHalfMoves < garray[g].totalHalfMoves) {
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (parray[p1].status != PLAYER_PROMPT)
+ continue;
+ if (player_is_observe(p1, g) || parray[p1].game == g) {
+ pprintf(p1, "%s goes forward %d move%s.\n",
+ parray[p].name, nHalfMoves, (nHalfMoves == 1) ? "" : "s");
+ }
+ }
+ }
+ for (i = 0; i < nHalfMoves; i++) {
+ if (garray[g].numHalfMoves < garray[g].totalHalfMoves) {
+ execute_move(&garray[g].game_state, &garray[g].moveList[garray[g].numHalfMoves], 1);
+ if (garray[g].numHalfMoves + 1 > garray[g].examMoveListSize) {
+ garray[g].examMoveListSize += 20; /* Allocate 20 moves at a
+ time */
+ if (!garray[g].examMoveList) {
+ garray[g].examMoveList = (move_t *) rmalloc(sizeof(move_t) * garray[g].examMoveListSize);
+ } else {
+ garray[g].examMoveList = (move_t *) rrealloc(garray[g].examMoveList, sizeof(move_t) * garray[g].examMoveListSize);
+ }
+ }
+ garray[g].examMoveList[garray[g].numHalfMoves] = garray[g].moveList[garray[g].numHalfMoves];
+ garray[g].revertHalfMove++;
+ garray[g].numHalfMoves++;
+ } else {
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (parray[p1].status != PLAYER_PROMPT)
+ continue;
+ if (player_is_observe(p1, g) || parray[p1].game == g) {
+ pprintf(p1, "End of game.\n");
+ }
+ }
+ break;
+ }
+ }
+ /* roll back time */
+ if (garray[g].game_state.onMove == WHITE) {
+ garray[g].wTime += (garray[g].lastDecTime - garray[g].lastMoveTime);
+ } else {
+ garray[g].bTime += (garray[g].lastDecTime - garray[g].lastMoveTime);
+ }
+ now = tenth_secs();
+ if (garray[g].numHalfMoves == 0)
+ garray[g].timeOfStart = now;
+ garray[g].lastMoveTime = now;
+ garray[g].lastDecTime = now;
+ send_boards(g);
+ return COM_OK;
+}
+
+PUBLIC int com_backward(int p, param_list param)
+{
+ int nHalfMoves = 1;
+ int g, i;
+ int p1;
+ unsigned now;
+
+ if (!((parray[p].game >=0) &&(garray[parray[p].game].status == GAME_EXAMINE))) {
+ pprintf(p, "You are not examining any games.\n");
+ return COM_OK;
+ }
+ g = parray[p].game;
+ if (param[0].type == TYPE_INT) {
+ nHalfMoves = param[0].val.integer;
+ }
+ if (garray[g].numHalfMoves != 0) {
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (parray[p1].status != PLAYER_PROMPT)
+ continue;
+ if (player_is_observe(p1, g) || parray[p1].game == g) {
+ pprintf(p1, "%s backs up %d move%s.\n",
+ parray[p].name, nHalfMoves, (nHalfMoves == 1) ? "" : "s");
+ }
+ }
+ }
+ for (i = 0; i < nHalfMoves; i++) {
+ if (backup_move(g, REL_EXAMINE) != MOVE_OK) {
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (parray[p1].status != PLAYER_PROMPT)
+ continue;
+ if (player_is_observe(p1, g) || parray[p1].game == g) {
+ pprintf(p1, "Beginning of game.\n");
+ }
+ }
+
+ break;
+ }
+ }
+ if (garray[g].numHalfMoves < garray[g].revertHalfMove) {
+ garray[g].revertHalfMove = garray[g].numHalfMoves;
+ }
+ /* roll back time */
+ if (garray[g].game_state.onMove == WHITE) {
+ garray[g].wTime += (garray[g].lastDecTime - garray[g].lastMoveTime);
+ } else {
+ garray[g].bTime += (garray[g].lastDecTime - garray[g].lastMoveTime);
+ }
+ now = tenth_secs();
+ if (garray[g].numHalfMoves == 0)
+ garray[g].timeOfStart = now;
+ garray[g].lastMoveTime = now;
+ garray[g].lastDecTime = now;
+ send_boards(g);
+ return COM_OK;
+}
+
+PUBLIC int com_revert(int p, param_list param)
+{
+ int nHalfMoves = 1;
+ int g, i;
+ int p1;
+ unsigned now;
+
+ if (!((parray[p].game >=0) &&(garray[parray[p].game].status == GAME_EXAMINE))) {
+ pprintf(p, "You are not examining any games.\n");
+ return COM_OK;
+ }
+ g = parray[p].game;
+ nHalfMoves = garray[g].numHalfMoves - garray[g].revertHalfMove;
+ if (nHalfMoves == 0) {
+ pprintf(p, "Already at mainline.\n");
+ return COM_OK;
+ }
+ if (nHalfMoves < 0) { /* eek - should NEVER happen! */
+ fprintf(stderr, "OUCH! in com_revert: nHalfMoves < 0\n");
+ return COM_OK;
+ }
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (parray[p1].status != PLAYER_PROMPT)
+ continue;
+ if (player_is_observe(p1, g) || parray[p1].game == g) {
+ pprintf(p1, "%s reverts to mainline.\n", parray[p].name);
+ }
+ }
+ for (i = 0; i < nHalfMoves; i++) {
+ backup_move(g, REL_EXAMINE);/* should never return error */
+ }
+ /* roll back time */
+ if (garray[g].game_state.onMove == WHITE) {
+ garray[g].wTime += (garray[g].lastDecTime - garray[g].lastMoveTime);
+ } else {
+ garray[g].bTime += (garray[g].lastDecTime - garray[g].lastMoveTime);
+ }
+ now = tenth_secs();
+ if (garray[g].numHalfMoves == 0)
+ garray[g].timeOfStart = now;
+ garray[g].lastMoveTime = now;
+ garray[g].lastDecTime = now;
+ send_boards(g);
+ return COM_OK;
+}
+
+PUBLIC int com_takeback(int p, param_list param)
+{
+ int nHalfMoves = 1;
+ int from;
+ int g, i;
+ int p1;
+
+ if ((parray[p].game <0) ||(garray[parray[p].game].status == GAME_EXAMINE)) {
+ pprintf(p, "You are not playing a game.\n");
+ return COM_OK;
+ }
+ p1 = parray[p].opponent;
+ if (parray[p1].simul_info.numBoards &&
+ parray[p1].simul_info.boards[parray[p1].simul_info.onBoard] !=
+ parray[p].game) {
+ pprintf(p, "You can only make requests when the simul player is at your board.\n");
+ return COM_OK;
+ }
+ g = parray[p].game;
+ if (param[0].type == TYPE_INT) {
+ nHalfMoves = param[0].val.integer;
+ }
+ if ((from = player_find_pendfrom(p, parray[p].opponent, PEND_TAKEBACK)) >= 0) {
+ player_remove_request(parray[p].opponent, p, PEND_TAKEBACK);
+ if (parray[p].p_from_list[from].param1 == nHalfMoves) {
+ /* Doing the takeback */
+ player_decline_offers(p, -1, -PEND_SIMUL);
+ for (i = 0; i < nHalfMoves; i++) {
+ if (backup_move(g, REL_GAME) != MOVE_OK) {
+ pprintf(garray[g].white, "Can only backup %d moves\n", i);
+ pprintf(garray[g].black, "Can only backup %d moves\n", i);
+ break;
+ }
+ }
+
+#ifdef TIMESEAL
+
+ garray[g].wTimeWhenReceivedMove = 0;
+ garray[g].bTimeWhenReceivedMove = 0;
+
+#endif
+
+ send_boards(g);
+ } else {
+ if (garray[g].numHalfMoves < nHalfMoves) {
+ pprintf(p, "There are only %d half moves in your game.\n", garray[g].numHalfMoves);
+ pprintf_prompt(parray[p].opponent, "\n%s has declined the takeback request.\n", parray[p].name, nHalfMoves);
+ return COM_OK;
+ }
+ pprintf(p, "You disagree on the number of half-moves to takeback.\n");
+ pprintf(p, "Alternate takeback request sent.\n");
+ pprintf_prompt(parray[p].opponent, "\n%s proposes a different number (%d) of half-move(s).\n", parray[p].name, nHalfMoves);
+ player_add_request(p, parray[p].opponent, PEND_TAKEBACK, nHalfMoves);
+ }
+ } else {
+ if (garray[g].numHalfMoves < nHalfMoves) {
+ pprintf(p, "There are only %d half moves in your game.\n", garray[g].numHalfMoves);
+ return COM_OK;
+ }
+ pprintf(parray[p].opponent, "\n");
+ pprintf_highlight(parray[p].opponent, "%s", parray[p].name);
+ pprintf_prompt(parray[p].opponent, " would like to take back %d half move(s).\n",
+ nHalfMoves);
+ pprintf(p, "Takeback request sent.\n");
+ player_add_request(p, parray[p].opponent, PEND_TAKEBACK, nHalfMoves);
+ }
+ return COM_OK;
+}
+
+
+PUBLIC int com_switch(int p, param_list param)
+{
+ int g = parray[p].game;
+ int tmp, now;
+ int p1;
+ char *strTmp;
+
+ if ((g < 0) || (garray[g].status == GAME_EXAMINE)) {
+ pprintf(p, "You are not playing a game.\n");
+ return COM_OK;
+ }
+ p1 = parray[p].opponent;
+ if (parray[p1].simul_info.numBoards &&
+ parray[p1].simul_info.boards[parray[p1].simul_info.onBoard] != g) {
+ pprintf(p, "You can only make requests when the simul player is at your board.\n");
+ return COM_OK;
+ }
+ if (player_find_pendfrom(p, parray[p].opponent, PEND_SWITCH) >= 0) {
+ player_remove_request(parray[p].opponent, p, PEND_SWITCH);
+ /* Doing the switch */
+ player_decline_offers(p, -1, -PEND_SIMUL);
+
+ tmp = garray[g].white;
+ garray[g].white = garray[g].black;
+ garray[g].black = tmp;
+ parray[p].side = (parray[p].side == WHITE) ? BLACK : WHITE;
+ strTmp = strdup(garray[g].white_name);
+ strcpy(garray[g].white_name, garray[g].black_name);
+ strcpy(garray[g].black_name, strTmp);
+ strfree(strTmp);
+
+ parray[parray[p].opponent].side =
+ (parray[parray[p].opponent].side == WHITE) ? BLACK : WHITE;
+ /* Roll back the time */
+ if (garray[g].game_state.onMove == WHITE) {
+ garray[g].wTime += (garray[g].lastDecTime - garray[g].lastMoveTime);
+ } else {
+ garray[g].bTime += (garray[g].lastDecTime - garray[g].lastMoveTime);
+ }
+ now = tenth_secs();
+ if (garray[g].numHalfMoves == 0)
+ garray[g].timeOfStart = now;
+ garray[g].lastMoveTime = now;
+ garray[g].lastDecTime = now;
+ send_boards(g);
+ return COM_OK;
+ }
+ if (garray[g].rated && garray[g].numHalfMoves > 0) {
+ pprintf(p, "You cannot switch sides once a rated game is underway.\n");
+ return COM_OK;
+ }
+ pprintf(parray[p].opponent, "\n");
+ pprintf_highlight(parray[p].opponent, "%s", parray[p].name);
+ pprintf_prompt(parray[p].opponent, " would like to switch sides.\nType \"accept\" to switch sides, or \"decline\" to refuse.\n");
+ pprintf(p, "Switch request sent.\n");
+ player_add_request(p, parray[p].opponent, PEND_SWITCH, 0);
+ return COM_OK;
+}
+
+
+PUBLIC int com_history(int p, param_list param)
+{
+ int p1, connected;
+ char fname[MAX_FILENAME_SIZE];
+
+ if (!FindPlayer(p, &param[0], &p1, &connected))
+ return COM_OK;
+
+ sprintf(fname, "%s/player_data/%c/%s.%s", stats_dir, parray[p1].login[0],
+ parray[p1].login, STATS_GAMES);
+ pgames(p, p1, fname);
+ if (!connected)
+ player_remove(p1);
+ return COM_OK;
+}
+
+PUBLIC int com_time(int p, param_list param)
+{
+ int p1, g;
+
+ if (param[0].type == TYPE_NULL) {
+ g = parray[p].game;
+ if ((g < 0) || (garray[g].status == GAME_EXAMINE)) {
+ pprintf(p, "You are not playing a game.\n");
+ return COM_OK;
+ }
+ } else {
+ g = GameNumFromParam(p, &p1, &param[0]);
+ if (g < 0)
+ return COM_OK;
+ }
+ if ((g < 0) || (g >= g_num) || (garray[g].status != GAME_ACTIVE)) {
+ pprintf(p, "There is no such game.\n");
+ return COM_OK;
+ }
+ game_update_time(g);
+ pprintf(p, "White (%s) : %d mins, %d secs\n",
+ parray[garray[g].white].name,
+ garray[g].wTime / 600,
+ (garray[g].wTime - ((garray[g].wTime / 600) * 600)) / 10);
+ pprintf(p, "Black (%s) : %d mins, %d secs\n",
+ parray[garray[g].black].name,
+ garray[g].bTime / 600,
+ (garray[g].bTime - ((garray[g].bTime / 600) * 600)) / 10);
+ return COM_OK;
+}
+
+PUBLIC int com_boards(int p, param_list param)
+{
+ char *category = NULL;
+ char dname[MAX_FILENAME_SIZE];
+ DIR *dirp;
+#ifdef USE_DIRENT
+ struct dirent *dp;
+#else
+ struct direct *dp;
+#endif
+
+ if (param[0].type == TYPE_WORD)
+ category = param[0].val.word;
+ if (category) {
+ pprintf(p, "Boards Available For Category %s:\n", category);
+ sprintf(dname, "%s/%s", board_dir, category);
+ } else {
+ pprintf(p, "Categories Available:\n");
+ sprintf(dname, "%s", board_dir);
+ }
+ dirp = opendir(dname);
+ if (!dirp) {
+ pprintf(p, "No such category %s, try \"boards\".\n", category);
+ return COM_OK;
+ }
+ for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
+ if (!strcmp(dp->d_name, "."))
+ continue;
+ if (!strcmp(dp->d_name, ".."))
+ continue;
+ pprintf(p, "%s\n", dp->d_name);
+ }
+ closedir(dirp);
+ return COM_OK;
+}
+
+PUBLIC int com_simmatch(int p, param_list param)
+{
+ int p1, g, adjourned;
+ int num;
+ char tmp[100];
+
+ if ((parray[p].game >=0) &&(garray[parray[p].game].status == GAME_EXAMINE)) {
+ pprintf(p, "You are still examining a game.\n");
+ return COM_OK;
+ }
+ p1 = player_find_part_login(param[0].val.word);
+ if (p1 < 0) {
+ pprintf(p, "No user named \"%s\" is logged in.\n", param[0].val.word);
+ return COM_OK;
+ }
+ if (p == p1) {
+ pprintf(p, "You can't simmatch yourself!\n");
+ return COM_OK;
+ }
+ if (player_find_pendfrom(p, p1, PEND_SIMUL) >= 0) {
+ player_remove_request(p, p1, PEND_MATCH);
+ player_remove_request(p1, p, PEND_MATCH);
+ player_remove_request(p, p1, PEND_SIMUL);
+ player_remove_request(p1, p, PEND_SIMUL);
+ player_withdraw_offers(p, -1, PEND_SIMUL);
+ player_decline_offers(p1, -1, PEND_SIMUL);
+ player_withdraw_offers(p1, -1, PEND_SIMUL);
+ player_decline_offers(p, -1, PEND_MATCH);
+ player_withdraw_offers(p, -1, PEND_MATCH);
+ player_decline_offers(p1, -1, PEND_MATCH);
+ player_withdraw_offers(p1, -1, PEND_MATCH);
+
+
+
+ /* Accepting Simul ! */
+
+
+ if (parray[p].simul_info.numBoards >= MAX_SIMUL) {
+ pprintf(p, "You are already playing the maximum of %d boards.\n", MAX_SIMUL);
+ pprintf(p1, "Simul request removed, boards filled.\n");
+ return COM_OK;
+ }
+ unobserveAll(p); /* stop observing when match starts */
+ unobserveAll(p1);
+
+ g = game_new();
+ adjourned = 0;
+ if (game_read(g, p, p1) >= 0)
+ adjourned = 1;
+ if (!adjourned) { /* no adjourned game, so begin a new game */
+ game_remove(g);
+
+ if (create_new_match(p, p1, 0, 0, 0, 0, 0, "standard", "standard", 1)) {
+ pprintf(p, "There was a problem creating the new match.\n");
+ pprintf_prompt(p1, "There was a problem creating the new match.\n");
+ return COM_OK;
+ }
+ } else { /* resume adjourned game */
+ game_delete(p, p1);
+
+ sprintf(tmp, "{Game %d (%s vs. %s) Continuing %s %s simul.}\n", g + 1, parray[p].name, parray[p1].name, rstr[garray[g].rated], bstr[garray[g].type]);
+ pprintf(p, tmp);
+ pprintf(p1, tmp);
+
+ garray[g].white = p;
+ garray[g].black = p1;
+ garray[g].status = GAME_ACTIVE;
+ garray[g].startTime = tenth_secs();
+ garray[g].lastMoveTime = garray[g].startTime;
+ garray[g].lastDecTime = garray[g].startTime;
+ parray[p].game = g;
+ parray[p].opponent = p1;
+ parray[p].side = WHITE;
+ parray[p1].game = g;
+ parray[p1].opponent = p;
+ parray[p1].side = BLACK;
+ send_boards(g);
+ }
+
+ num = parray[p].simul_info.numBoards;
+ parray[p].simul_info.results[num] = -1;
+ parray[p].simul_info.boards[num] = parray[p].game;
+ parray[p].simul_info.numBoards++;
+ if (parray[p].simul_info.numBoards > 1 &&
+ parray[p].simul_info.onBoard >= 0)
+ player_goto_board(p, parray[p].simul_info.onBoard);
+ else
+ parray[p].simul_info.onBoard = 0;
+ return COM_OK;
+ }
+ if (player_find_pendfrom(p, -1, PEND_SIMUL) >= 0) {
+ pprintf(p, "You cannot be the simul giver and request to join another simul.\nThat would just be too confusing for me and you.\n");
+ return COM_OK;
+ }
+ if (parray[p].simul_info.numBoards) {
+ pprintf(p, "You cannot be the simul giver and request to join another simul.\nThat would just be too confusing for me and you.\n");
+ return COM_OK;
+ }
+ if (parray[p].game >=0) {
+ pprintf(p, "You are already playing a game.\n");
+ return COM_OK;
+ }
+ if (!parray[p1].sopen) {
+ pprintf_highlight(p, "%s", parray[p1].name);
+ pprintf(p, " is not open to receiving simul requests.\n");
+ return COM_OK;
+ }
+ if (parray[p1].simul_info.numBoards >= MAX_SIMUL) {
+ pprintf_highlight(p, "%s", parray[p1].name);
+ pprintf(p, " is already playing the maximum of %d boards.\n", MAX_SIMUL);
+ return COM_OK;
+ }
+/* loon: checking for some crazy situations we can't allow :) */
+
+ if ((parray[p1].game >=0) &&(parray[p1].simul_info.numBoards == 0)) {
+ pprintf_highlight(p, "%s", parray[p1].name);
+ if (parray[garray[parray[p1].game].white].simul_info.numBoards) {
+ pprintf(p, " is playing in ");
+ pprintf_highlight(p, "%s", parray[parray[p1].opponent].name);
+ pprintf(p, "'s simul, and can't accept.\n");
+ } else {
+ pprintf(p, " can't begin a simul while playing a non-simul game.\n");
+ }
+ return COM_OK;
+ }
+/* loon: this was (p, p1, PEND_SIMUL) but player_add_request needs 4 args...
+if 0 is the incorrect 4th arg, please fix :) */
+
+ g = game_new(); /* Check if an adjourned untimed game */
+ adjourned = ((game_read(g, p, p1) < 0) && (game_read(g, p1, p) < 0)) ? 0 : 1;
+ if (adjourned) {
+ if (!(garray[g].type == TYPE_UNTIMED))
+ adjourned = 0;
+ }
+ game_remove(g);
+
+ if (player_add_request(p, p1, PEND_SIMUL, 0)) {
+ pprintf(p, "Maximum number of pending actions reached. Your request was not sent.\nTry again later.\n");
+ return COM_OK;
+ } else {
+ pprintf(p1, "\n");
+ pprintf_highlight(p1, "%s", parray[p].name);
+ if (adjourned) {
+ pprintf_prompt(p1, " requests to continue an adjourned simul game.\n");
+ pprintf(p, "Request to resume simul sent. Adjourned game found.\n");
+ } else {
+ pprintf_prompt(p1, " requests to join a simul match with you.\n");
+ pprintf(p, "Simul match request sent.\n");
+ }
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_goboard(int p, param_list param)
+{
+ int on, g, p1;
+
+ if (!parray[p].simul_info.numBoards) {
+ pprintf(p, "You are not giving a simul.\n");
+ return COM_OK;
+ }
+ p1 = player_find_part_login(param[0].val.word);
+ if (p1 < 0) {
+ pprintf(p, "No user named \"%s\" is logged in.\n", param[0].val.word);
+ return COM_OK;
+ }
+ if (p == p1) {
+ pprintf(p, "You can't goboard yourself!\n");
+ return COM_OK;
+ }
+ on = parray[p].simul_info.onBoard;
+ g = parray[p].simul_info.boards[on];
+ if (p1 == garray[g].black) {
+ pprintf(p, "You are already at that board!\n");
+ return COM_OK;
+ }
+ if (parray[p].simul_info.numBoards > 1) {
+ player_decline_offers(p, -1, -PEND_SIMUL);
+ if (player_goto_simulgame_bynum(p, parray[p1].game) !=-1) {
+ if (g >= 0) {
+ pprintf(garray[g].black, "\n");
+ pprintf_highlight(garray[g].black, "%s", parray[p].name);
+ pprintf_prompt(garray[g].black, " has moved away from your board.\n");
+ }
+ }
+ } else
+ pprintf(p, "You are only playing one board!\n");
+ return COM_OK;
+}
+
+PUBLIC int com_gonum(int p, param_list param)
+{
+ int on, g, gamenum;
+
+ if (!parray[p].simul_info.numBoards) {
+ pprintf(p, "You are not giving a simul.\n");
+ return COM_OK;
+ }
+ on = parray[p].simul_info.onBoard;
+ g = parray[p].simul_info.boards[on];
+ gamenum = param[0].val.integer - 1;
+ if (gamenum < 0)
+ gamenum = 0;
+ if (on == gamenum) {
+ pprintf(p, "You are already at that board!\n");
+ return COM_OK;
+ }
+ if (parray[p].simul_info.numBoards > 1) {
+ player_decline_offers(p, -1, -PEND_SIMUL);
+ if (player_goto_simulgame_bynum(p, gamenum) != -1) {
+ if (g >= 0) {
+ pprintf(garray[g].black, "\n");
+ pprintf_highlight(garray[g].black, "%s", parray[p].name);
+ pprintf_prompt(garray[g].black, " has moved away from your board.\n");
+ }
+ }
+ } else
+ pprintf(p, "You are only playing one board!\n");
+ return COM_OK;
+}
+
+PUBLIC int com_simnext(int p, param_list param)
+{
+ int on, g;
+
+ if (!parray[p].simul_info.numBoards) {
+ pprintf(p, "You are not giving a simul.\n");
+ return COM_OK;
+ }
+ if (parray[p].simul_info.numBoards > 1) {
+ player_decline_offers(p, -1, -PEND_SIMUL);
+ on = parray[p].simul_info.onBoard;
+ g = parray[p].simul_info.boards[on];
+ if (g >= 0) {
+ pprintf(garray[g].black, "\n");
+ pprintf_highlight(garray[g].black, "%s", parray[p].name);
+ pprintf_prompt(garray[g].black, " is moving away from your board.\n");
+ player_goto_next_board(p);
+ }
+ } else
+ pprintf(p, "You are only playing one board!\n");
+ return COM_OK;
+}
+
+
+PUBLIC int com_simprev(int p, param_list param)
+{
+ int on, g;
+
+ if (!parray[p].simul_info.numBoards) {
+ pprintf(p, "You are not giving a simul.\n");
+ return COM_OK;
+ }
+ if (parray[p].simul_info.numBoards > 1) {
+ player_decline_offers(p, -1, -PEND_SIMUL);
+ on = parray[p].simul_info.onBoard;
+ g = parray[p].simul_info.boards[on];
+ if (g >= 0) {
+ pprintf(garray[g].black, "\n");
+ pprintf_highlight(garray[g].black, "%s", parray[p].name);
+ pprintf_prompt(garray[g].black, " is moving back to the previous board.\n");
+ }
+ player_goto_prev_board(p);
+ } else
+ pprintf(p, "You are only playing one board!\n");
+ return COM_OK;
+}
+
+PUBLIC int com_simgames(int p, param_list param)
+{
+ int p1 = p;
+
+ if (param[0].type == TYPE_WORD) {
+ if ((p1 = player_find_part_login(param[0].val.word)) < 0) {
+ pprintf(p, "No player named %s is logged in.\n", param[0].val.word);
+ return COM_OK;
+ }
+ }
+ if (p1 == p)
+ pprintf(p, "You are playing %d simultaneous games.\n",
+ player_num_active_boards(p1));
+ else
+ pprintf(p, "%s is playing %d simultaneous games.\n", parray[p1].name,
+ player_num_active_boards(p1));
+ return COM_OK;
+}
+
+PUBLIC int com_simpass(int p, param_list param)
+{
+ int g, p1, on;
+
+ if (parray[p].game <0) {
+ pprintf(p, "You are not playing a game.\n");
+ return COM_OK;
+ }
+ g = parray[p].game;
+ p1 = garray[g].white;
+ if (!parray[p1].simul_info.numBoards) {
+ pprintf(p, "You are not participating in a simul.\n");
+ return COM_OK;
+ }
+ if (p == p1) {
+ pprintf(p, "You are the simul holder and cannot pass!\n");
+ return COM_OK;
+ }
+ if (player_num_active_boards(p1) == 1) {
+ pprintf(p, "This is the only game, so passing is futile.\n");
+ return COM_OK;
+ }
+ on = parray[p1].simul_info.onBoard;
+ if (on != g) {
+ pprintf(p, "You cannot pass until the simul holder arrives!\n");
+ return COM_OK;
+ }
+ if (garray[g].passes >= MAX_SIMPASS) {
+ if (parray[p].bell)
+ pprintf(p, "\a");
+ pprintf(p, "You have reached your maximum of %d passes.\n", MAX_SIMPASS);
+ pprintf(p, "Please move IMMEDIATELY!\n");
+ pprintf_highlight(p1, "%s", parray[p].name);
+ pprintf_prompt(p1, " tried to pass, but is out of passes.\n");
+ return COM_OK;
+ }
+ player_decline_offers(p, -1, -PEND_SIMUL);
+
+ garray[g].passes++;
+ pprintf(p, "You have passed and have %d passes left.\n",
+ (MAX_SIMPASS - garray[g].passes));
+ pprintf_highlight(p1, "%s", parray[p].name);
+ pprintf_prompt(p1, " has decided to pass and has %d passes left.\n",
+ (MAX_SIMPASS - garray[g].passes));
+ player_goto_next_board(p1);
+ return COM_OK;
+}
+
+PUBLIC int com_simabort(int p, param_list param)
+{
+ if (!parray[p].simul_info.numBoards) {
+ pprintf(p, "You are not giving a simul.\n");
+ return COM_OK;
+ }
+ player_decline_offers(p, -1, -PEND_SIMUL);
+ game_ended(parray[p].simul_info.boards[parray[p].simul_info.onBoard],
+ WHITE, END_ABORT);
+ return COM_OK;
+}
+
+PUBLIC int com_simallabort(int p, param_list param)
+{
+ int i;
+
+ if (!parray[p].simul_info.numBoards) {
+ pprintf(p, "You are not giving a simul.\n");
+ return COM_OK;
+ }
+ player_decline_offers(p, -1, -PEND_SIMUL);
+ for (i = 0; i < parray[p].simul_info.numBoards; i++) {
+ if (parray[p].simul_info.boards[i] >= 0) {
+ game_ended(parray[p].simul_info.boards[i],
+ WHITE, END_ABORT);
+ }
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_simadjourn(int p, param_list param)
+{
+ if (!parray[p].simul_info.numBoards) {
+ pprintf(p, "You are not giving a simul.\n");
+ return COM_OK;
+ }
+ player_decline_offers(p, -1, -PEND_SIMUL);
+ game_ended(parray[p].simul_info.boards[parray[p].simul_info.onBoard],
+ WHITE, END_ADJOURN);
+ return COM_OK;
+}
+
+PUBLIC int com_simalladjourn(int p, param_list param)
+{
+ int i;
+
+ if (!parray[p].simul_info.numBoards) {
+ pprintf(p, "You are not giving a simul.\n");
+ return COM_OK;
+ }
+ player_decline_offers(p, -1, -PEND_SIMUL);
+ for (i = 0; i < parray[p].simul_info.numBoards; i++) {
+ if (parray[p].simul_info.boards[i] >= 0) {
+ game_ended(parray[p].simul_info.boards[i],
+ WHITE, END_ADJOURN);
+ }
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_moretime(int p, param_list param)
+{
+ int g, increment;
+
+ ASSERT(param[0].type == TYPE_INT);
+ if ((parray[p].game >=0) &&(garray[parray[p].game].status == GAME_EXAMINE)) {
+ pprintf(p, "You cannot use moretime in an examined game.\n");
+ return COM_OK;
+ }
+ increment = param[0].val.integer;
+ if (increment <= 0) {
+ pprintf(p, "Moretime requires an integer value greater than zero.\n");
+ return COM_OK;
+ }
+ if (parray[p].game <0) {
+ pprintf(p, "You are not playing a game.\n");
+ return COM_OK;
+ }
+ if (increment > 600) {
+ pprintf(p, "Moretime has a maximum limit of 600 seconds.\n");
+ increment = 600;
+ }
+ g = parray[p].game;
+ if (garray[g].white == p) {
+ garray[g].bTime += increment * 10;
+#ifdef TIMESEAL
+ garray[g].bRealTime += increment * 10 * 100;
+#endif
+ pprintf(p, "%d seconds were added to your opponents clock\n",
+ increment);
+ pprintf_prompt(parray[p].opponent,
+ "\nYour opponent has added %d seconds to your clock.\n",
+ increment);
+ }
+ if (garray[g].black == p) {
+ garray[g].wTime += increment * 10;;
+#ifdef TIMESEAL
+ garray[g].wRealTime += increment * 10 * 100;
+#endif
+ pprintf(p, "%d seconds were added to your opponents clock\n",
+ increment);
+ pprintf_prompt(parray[p].opponent,
+ "\nYour opponent has added %d seconds to your clock.\n",
+ increment);
+ }
+ return COM_OK;
+}
diff --git a/FICS/gameproc.h b/FICS/gameproc.h
new file mode 100644
index 0000000..6feacf1
--- /dev/null
+++ b/FICS/gameproc.h
@@ -0,0 +1,66 @@
+/* gameproc.h
+ *
+ */
+
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name email yy/mm/dd Change
+ Richard Nash 93/10/22 Created
+*/
+
+#ifndef _GAMEPROC_H
+#define _GAMEPROC_H
+
+#define MAX_SIMPASS 3
+#define MAX_JOURNAL 10
+
+extern void game_ended();
+extern int pIsPlaying();
+extern void process_move();
+extern int com_resign();
+extern int com_draw();
+extern int com_pause();
+extern int com_unpause();
+extern int com_abort();
+extern int com_games();
+
+extern int com_courtesyabort();
+extern int com_courtesyadjourn();
+extern int com_load();
+extern int com_stored();
+extern int com_adjourn();
+extern int com_flag();
+extern int com_takeback();
+extern int com_switch();
+extern int com_time();
+extern int com_boards();
+
+extern int com_simmatch();
+extern int com_simnext();
+extern int com_simprev();
+extern int com_goboard();
+extern int com_gonum();
+extern int com_simgames();
+extern int com_simpass();
+extern int com_simabort();
+extern int com_simallabort();
+extern int com_simadjourn();
+extern int com_simalladjourn();
+
+extern int com_moretime();
+
+#endif /* _GAMEPROC_H */
diff --git a/FICS/get_tcp_conn.c b/FICS/get_tcp_conn.c
new file mode 100644
index 0000000..904da47
--- /dev/null
+++ b/FICS/get_tcp_conn.c
@@ -0,0 +1,153 @@
+/*
+** Routines to open a TCP connection
+**
+** New version that supports the old (pre 4.2 BSD) socket calls,
+** and systems with the old (pre 4.2 BSD) hostname lookup stuff.
+** Compile-time options are:
+**
+** OLDSOCKET - different args for socket() and connect()
+**
+** Erik E. Fair <fair@ucbarpa.berkeley.edu>
+**
+*/
+
+/* Uncomment the following line if you are using old socket calls */
+/* #define OLDSOCKET */
+
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netdb.h>
+#include "get_tcp_conn.h"
+#include "get_tcp_conn.proto.h"
+#include "stdinclude.h"
+
+extern int errno;
+extern int close();
+
+
+
+/*
+** Take the name of an internet host in ASCII (this may either be its
+** official host name or internet number (with or without enclosing
+** backets [])), and return a list of internet addresses.
+**
+** returns NULL for failure to find the host name in the local database,
+** or for a bad internet address spec.
+*/
+unsigned long **
+ name_to_address(char *host)
+{
+ static u_long *host_addresses[2];
+ static u_long haddr;
+
+ if (host == (char *) NULL) {
+ return ((u_long **) NULL);
+ }
+ host_addresses[0] = &haddr;
+ host_addresses[1] = (u_long *) NULL;
+
+ /* * Is this an ASCII internet address? (either of [10.0.0.78] or *
+ 10.0.0.78). We get away with the second test because hostnames * and
+ domain labels are not allowed to begin in numbers. * (cf. RFC952,
+ RFC882). */
+ if (*host == '[' || isdigit(*host)) {
+ char namebuf[128];
+ register char *cp = namebuf;
+
+ /* * strip brackets [] or anything else we don't want. */
+ while (*host != '\0' && cp < &namebuf[sizeof(namebuf)]) {
+ if (isdigit(*host) || *host == '.')
+ *cp++ = *host++; /* copy */
+ else
+ host++; /* skip */
+ }
+ *cp = '\0';
+ haddr = inet_addr(namebuf);
+ return (&host_addresses[0]);
+ } else {
+ struct hostent *hstp = gethostbyname(host);
+
+ if (hstp == NULL) {
+ return ((u_long **) NULL);/* no such host */
+ }
+ if (hstp->h_length != sizeof(u_long))
+ abort(); /* this is fundamental */
+ return ((u_long **) hstp->h_addr_list);
+ }
+}
+
+u_short
+gservice(char *serv, char *proto)
+{
+ if (serv == (char *) NULL || proto == (char *) NULL)
+ return ((u_short) 0);
+
+ if (isdigit(*serv)) {
+ return (htons((u_short) (atoi(serv))));
+ } else {
+ struct servent *srvp = getservbyname(serv, proto);
+
+ if (srvp == (struct servent *) NULL)
+ return ((u_short) 0);
+ return ((u_short) srvp->s_port);
+ }
+}
+
+/*
+** given a host name (either name or internet address) and port number
+** give us a TCP connection to the
+** requested service at the requested host (or give us FAIL).
+*/
+int get_tcp_conn(host, port)
+char *host;
+int port;
+{
+ register int sock;
+ u_long **addrlist;
+ struct sockaddr_in sadr;
+#ifdef OLDSOCKET
+ struct sockproto sp;
+
+ sp.sp_family = (u_short) AF_INET;
+ sp.sp_protocol = (u_short) IPPROTO_TCP;
+#endif
+
+ if ((addrlist = name_to_address(host)) == (u_long **) NULL) {
+ return (NOHOST);
+ }
+ sadr.sin_family = (u_short) AF_INET; /* Only internet for now */
+ sadr.sin_port = htons(port);
+
+ for (; *addrlist != (u_long *) NULL; addrlist++) {
+ bcopy((caddr_t) * addrlist, (caddr_t) & sadr.sin_addr,
+ sizeof(sadr.sin_addr));
+
+#ifdef OLDSOCKET
+ if ((sock = socket(SOCK_STREAM, &sp, (struct sockaddr *) NULL, 0)) < 0)
+ return (FAIL);
+
+ if (connect(sock, (struct sockaddr *) & sadr) < 0) {
+#else
+ if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
+ return (FAIL);
+
+ if (connect(sock, (struct sockaddr *) & sadr, sizeof(sadr)) < 0) {
+#endif
+ int e_save = errno;
+
+ fprintf(stderr, "%s [%s] port %d: %d\n", host,
+ inet_ntoa(sadr.sin_addr), port, errno);
+ (void) close(sock); /* dump descriptor */
+ errno = e_save;
+ } else
+ return (sock);
+ }
+ return (FAIL);
+}
diff --git a/FICS/get_tcp_conn.h b/FICS/get_tcp_conn.h
new file mode 100644
index 0000000..01320ae
--- /dev/null
+++ b/FICS/get_tcp_conn.h
@@ -0,0 +1,13 @@
+/*
+** Return codes from get_tcp_conn().
+*/
+#define FAIL (-1) /* routine failed */
+#define NOHOST (FAIL-1) /* no such host */
+#define NOSERVICE (FAIL-2) /* no such service */
+
+extern int get_tcp_conn(char *, int);
+extern unsigned long **name_to_address(char *);
+
+/* extern int bcopy(); */
+extern int socket();
+extern int connect();
diff --git a/FICS/get_tcp_conn.proto.h b/FICS/get_tcp_conn.proto.h
new file mode 100644
index 0000000..ebddbb4
--- /dev/null
+++ b/FICS/get_tcp_conn.proto.h
@@ -0,0 +1,4 @@
+
+/* get_tcp_conn.c */
+unsigned long **name_to_address(char *);
+u_short gservice(char *, char *);
diff --git a/FICS/gmon.out b/FICS/gmon.out
new file mode 100644
index 0000000..2f2b945
--- /dev/null
+++ b/FICS/gmon.out
Binary files differ
diff --git a/FICS/hashtbl.h b/FICS/hashtbl.h
new file mode 100644
index 0000000..6e1e401
--- /dev/null
+++ b/FICS/hashtbl.h
@@ -0,0 +1,97 @@
+#ifndef __FICSHashTable_h
+#define __FICSHashTable_h
+#ifdef __GNUC__
+# pragma interface
+#endif
+//-----------------------------------------------------------------------------
+// FICSHashTable.h
+//
+// A general-purpose C++ hash table object.
+//
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+// $Id: FICSHashTable.h,v 1.0 1995/04/25 21:50:44 mabshier Exp $
+// $Log: FICSHashTable.h,v $
+// $Renamed file to HASHTBL.H to accomodate MSDOS file convention rename for Unix
+// Initial revision
+//
+//-----------------------------------------------------------------------------
+
+#include <FICSContainer.h>
+#include <FICSHash.h>
+
+class FICSHashAdaptor;
+
+class FICSHashTable : public FICSContainer
+ {
+public:
+ unsigned long const DEFAULT_CAPACITY = 47;
+ unsigned long const LOAD_FACTOR_SCALE = 16;
+ unsigned long const LOAD_FACTOR_SHIFT = 4;
+ unsigned long const LOAD_FACTOR_DEFAULT = 16;
+
+private:
+ FICSHashAdaptor* adaptor;
+ unsigned long max_load_factor; // Scaled * 16.
+ unsigned long table_count; // Number of entries.
+ unsigned long table_size; // Number of slots.
+ void** table;
+ void expand( unsigned long new_capacity );
+
+public:
+ FICSHashTable(
+ FICSHashAdaptor&,
+ unsigned long capacity = DEFAULT_CAPACITY,
+ unsigned long max_load = LOAD_FACTOR_DEFAULT );
+virtual ~FICSHashTable();
+virtual size_t vcount() const;
+ size_t count() const { return table_count; }
+ size_t capacity() const { return table_size; }
+virtual void empty();
+virtual bool findObj( void const* keyPtr, void const*& infoPtr ) const;
+virtual bool storeObj( void const* keyPtr, void const* infoPtr,
+ FICSContainer::Unique u=FICSContainer::UNIQUE );
+virtual bool removeObj( void const* keyPtr ); // Removes 1st entry with key.
+virtual int removeAllObj(void const* keyPtr); // Removes all, returns count.
+
+// These return a pointer to the hash_rec, or 0.
+virtual void const* findObj( void const* keyPtr ) const;
+virtual void const* findOrStoreObj( void const* keyPtr, void const* infoPtr );
+
+friend class FICSHashTableIterator;
+ FICSHashTableIterator* iterator() const;
+virtual FICSIterator* newIterator() const;
+
+private: // Illegal
+ FICSHashTable( FICSHashTable const& ) {} // No copy constructor.
+ void operator=(FICSHashTable const&) {}// No assignment.
+
+protected:
+ FICSHashValue getHash( void const* hash_rec ) const;
+ FICSHashValue hash( void const* key ) const;
+ void setLink( void* hash_rec, void const* hash_link ) const;
+ void const* getLink( void const* hash_rec ) const;
+ bool equal(void const* hash_rec,FICSHashValue,void const* key) const;
+ void deleteHashRec( void* );
+ void* newHashRec( FICSHashValue hash_val, void const* key_ptr,
+ void const* info, void const* link );
+ void const* getInfo( void const* hash_rec ) const;
+ void const* getKey( void const* hash_rec ) const;
+ void fakeEmpty() { table_count = 0; }
+ };
+
+
+class FICSHashTableIterator : public FICSIterator
+ {
+private:
+ FICSHashTable const& table;
+ size_t next_slot;
+ void const* curr_ptr;
+public:
+ void reset() { next_slot = 0; curr_ptr = 0; }
+ FICSHashTableIterator( FICSHashTable const& x ):table(x) { reset(); }
+virtual ~FICSHashTableIterator();
+virtual bool next( void*& );
+ };
+
+#endif // __FICSHashTable_h
diff --git a/FICS/legal.c b/FICS/legal.c
new file mode 100644
index 0000000..5079683
--- /dev/null
+++ b/FICS/legal.c
@@ -0,0 +1,38 @@
+/* legal.c
+ *
+ */
+
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name email yy/mm/dd Change
+ Richard Nash 93/10/22 Created
+*/
+
+char legalNotice[] = "\n\
+ fics - An internet chess server.\n\
+ Copyright (C) 1993 Richard V. Nash\n\
+\n\
+ This program is free software; you can redistribute it and/or modify\n\
+ it under the terms of the GNU General Public License as published by\n\
+ the Free Software Foundation; either version 2 of the License, or\n\
+ (at your option) any later version.\n\
+\n\
+ This program is distributed in the hope that it will be useful,\n\
+ but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n\
+ GNU General Public License for more details.\n\
+";
diff --git a/FICS/legal.h b/FICS/legal.h
new file mode 100644
index 0000000..5219938
--- /dev/null
+++ b/FICS/legal.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 1993 by Richard V. Nash
+ * All rights reserved world wide.
+ */
+
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name email yy/mm/dd Change
+ Richard Nash 93/10/22 Created
+*/
+
+#ifndef _LEGAL_H
+#define _LEGAL_H
+
+extern char legalNotice[];
+
+#endif /* _LEGAL_H */
diff --git a/FICS/lists.c b/FICS/lists.c
new file mode 100644
index 0000000..7e3748d
--- /dev/null
+++ b/FICS/lists.c
@@ -0,0 +1,470 @@
+/* lists.c
+
+ * New global lists code
+ *
+ * Part of: FICS
+ *
+ * Added by Shaney, 29 May 1995 :)
+ * sometime in Oct 1995: changed things a little (loon)
+ *
+
+*/
+
+#include "lists.h"
+#include "common.h"
+#include "multicol.h"
+#include "command.h"
+#include "utils.h"
+#include "playerdb.h"
+#include "ratings.h"
+#include "rmalloc.h"
+#include "talkproc.h"
+#include "gamedb.h"
+#include "stdinclude.h"
+#include "comproc.h"
+
+#include <string.h>
+
+List *firstGlobalList = NULL;
+
+PRIVATE ListTable ListArray[] =
+{{P_HEAD, "admin"},
+{P_GOD, "removedcom"},
+{P_ADMIN, "filter"},
+{P_ADMIN, "ban"},
+{P_ADMIN, "abuser"},
+{P_ADMIN, "muzzle"},
+{P_ADMIN, "cmuzzle"},
+{P_PUBLIC, "fm"},
+{P_PUBLIC, "im"},
+{P_PUBLIC, "gm"},
+{P_PUBLIC, "blind"},
+{P_PUBLIC, "teams"},
+{P_PUBLIC, "computer"},
+{P_PUBLIC, "td"},
+{P_PERSONAL, "censor"},
+{P_PERSONAL, "gnotify"},
+{P_PERSONAL, "noplay"},
+{P_PERSONAL, "notify"},
+{P_PERSONAL, "channel"},
+{0, NULL}};
+
+/* find a list. loads from disk if not in memory. */
+PRIVATE List *list_find(int p, enum ListWhich l)
+{
+ List *prev, *tempList, **starter;
+ int personal;
+ int count = 0;
+
+ personal = ListArray[l].rights == P_PERSONAL;
+ starter = personal ? &(parray[p].lists) : &firstGlobalList;
+
+ for (prev = NULL, tempList = *starter; tempList != NULL; tempList = tempList->next) {
+ if (l == tempList->which) {
+ if (prev != NULL) {
+ prev->next = tempList->next;
+ tempList->next = *starter;
+ *starter = tempList;
+ }
+ return tempList;
+ }
+ prev = tempList;
+ }
+
+ tempList = rmalloc(sizeof(List));
+ if (tempList == NULL)
+ return NULL;
+
+ if (!personal) { /* now we have to load the list */
+ FILE *fp;
+ char listmember[100];
+ char filename[MAX_FILENAME_SIZE];
+
+ /* fprintf(stderr,"SHANEDEBUG: Adding %s list\n", ListArray[l].name); */
+
+ sprintf(filename, "%s/%s", lists_dir, ListArray[l].name);
+ fp = fopen(filename, "r");
+ if (!fp) {
+ rfree(tempList);
+ return NULL;
+ }
+ while (!feof(fp)) {
+ if (fgets(listmember, 100, fp) != NULL) {
+ listmember[strlen(listmember) - 1] = '\0';
+ tempList->member[count++] = strdup(listmember);
+ }
+ }
+ fclose(fp);
+ }
+ tempList->which = l;
+ tempList->numMembers = count;
+ tempList->next = *starter;
+ *starter = tempList;
+ return tempList;
+}
+
+/* add item to list */
+PUBLIC int list_add(int p, enum ListWhich l, char *s)
+{
+ List *gl = list_find(p, l);
+
+ if (gl) {
+ if (gl->numMembers < MAX_GLOBAL_LIST_SIZE) {
+ gl->member[gl->numMembers] = strdup(s);
+ gl->numMembers++;
+ return 0;
+ } else {
+ return 1;
+ }
+ } else {
+ return 1;
+ }
+}
+
+/* remove item from list */
+PUBLIC int list_sub(int p, enum ListWhich l, char *s)
+{
+ List *gl = list_find(p, l);
+
+ if (gl) {
+ int i, found = -1;
+ for (i = 0; i < gl->numMembers; i++)
+ if (!strcasecmp(s, gl->member[i])) {
+ found = i;
+ break;
+ }
+ if (found == -1)
+ return 1;
+ rfree(gl->member[found]);
+ for (i = found; i < (gl->numMembers - 1); i++)
+ gl->member[i] = gl->member[i + 1];
+ gl->numMembers--;
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+/* pretty cheesy: print each member of a list, 1 per line */
+PUBLIC void list_print(FILE * fp, int p, enum ListWhich l)
+{
+ int i;
+ List *gl = list_find(p, l);
+
+ if (gl) {
+ for (i = 0; i < gl->numMembers; i++)
+ fprintf(fp, "%s\n", gl->member[i]);
+ }
+}
+
+/* return size of a list */
+PUBLIC int list_size(int p, enum ListWhich l)
+{
+ List *gl = list_find(p, l);
+
+ if (gl)
+ return (gl->numMembers);
+ else
+ return 0;
+}
+
+/* find list by name, doesn't have to be the whole name */
+PRIVATE List *list_findpartial(int p, char *which, int gonnado)
+{
+ List *gl;
+ int i, foundit, slen;
+
+ slen = strlen(which);
+ for (i = 0, foundit = -1; ListArray[i].name != NULL; i++) {
+ if (!strncasecmp(ListArray[i].name, which, slen)) {
+ if (foundit == -1)
+ foundit = i;
+ else
+ return NULL; /* ambiguous */
+ }
+ }
+
+ if (foundit != -1) {
+ int rights = ListArray[foundit].rights;
+ int youlose = 0;
+
+ switch (rights) { /* check rights */
+ case P_HEAD:
+ if (gonnado && !player_ishead(p))
+ youlose = 1;
+ break;
+ case P_GOD:
+ if ((gonnado && (parray[p].adminLevel < ADMIN_GOD)) ||
+ (!gonnado && (parray[p].adminLevel < ADMIN_ADMIN)))
+ youlose = 1;
+ break;
+ case P_ADMIN:
+ if (parray[p].adminLevel < ADMIN_ADMIN)
+ youlose = 1;
+ break;
+ case P_PUBLIC:
+ if (gonnado && (parray[p].adminLevel < ADMIN_ADMIN))
+ youlose = 1;
+ break;
+ }
+ if (youlose) {
+ pprintf(p, "\"%s\" is not an appropriate list name or you have insufficient rights.\n", which);
+ return NULL;
+ }
+ gl = list_find(p, foundit);
+ } else {
+ pprintf(p, "\"%s\" does not match any list name.\n", which);
+ return NULL;
+ }
+ return gl;
+}
+
+/* see if something is in a list */
+PUBLIC int in_list(int p, enum ListWhich which, char *member)
+{
+ List *gl;
+ int i;
+ int filterList = (which == L_FILTER);
+
+ gl = list_find(p, which);
+ if ((gl == NULL) || (member == NULL))
+ return 0;
+ for (i = 0; i < gl->numMembers; i++) {
+ if (filterList) {
+ if (!strncasecmp(member, gl->member[i], strlen(gl->member[i])))
+ return 1;
+ } else {
+ if (!strcasecmp(member, gl->member[i]))
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* add or subtract something to/from a list */
+PUBLIC int list_addsub(int p, char* list, char* who, int addsub)
+{
+ int p1, connected, loadme, personal, ch;
+ char *listname, *member;
+ List *gl;
+ char *yourthe, *addrem;
+
+ gl = list_findpartial(p, list, addsub);
+ if (!gl)
+ return COM_OK;
+
+ personal = ListArray[gl->which].rights == P_PERSONAL;
+ loadme = (gl->which != L_FILTER) && (gl->which != L_REMOVEDCOM) && (gl->which != L_CHANNEL);
+ listname = ListArray[gl->which].name;
+ yourthe = personal ? "your" : "the";
+ addrem = (addsub == 1) ? "added to" : "removed from";
+
+ if (loadme) {
+ if (!FindPlayer(p, who, &p1, &connected)) {
+ if (addsub == 1)
+ return COM_OK;
+ member = who; /* allow sub removed/renamed player */
+ loadme = 0;
+ } else
+ member = parray[p1].name;
+ } else {
+ member = who;
+ }
+
+ if (addsub == 1) { /* add to list */
+
+ if (gl->which == L_CHANNEL) {
+
+ if (sscanf (who,"%d",&ch) == 1) {
+
+ if ((!in_list(p,L_ADMIN,parray[p].name)) && (ch == 0)) {
+ pprintf(p, "Only admins may join channel 0.\n");
+ return COM_OK;
+ }
+
+ if (ch > (MAX_CHANNELS - 1)) {
+ pprintf(p,"The maximum channel number is %d.\n",MAX_CHANNELS-1);
+ return COM_OK;
+ }
+ } else {
+ pprintf (p,"Your channel to add must be a number between 0 and %d.\n",MAX_CHANNELS - 1);
+ return COM_OK;
+ }
+ }
+ if (in_list(p, gl->which, member)) {
+ pprintf(p, "[%s] is already on %s %s list.\n", member, yourthe, listname);
+ if (loadme && !connected)
+ player_remove(p1);
+ return COM_OK;
+ }
+ if (list_add(p, gl->which, member)) {
+ pprintf(p, "Sorry, %s %s list is full.\n", yourthe, listname);
+ if (loadme && !connected)
+ player_remove(p1);
+ return COM_OK;
+ }
+ } else if (addsub == 2) { /* subtract from list */
+ if (!in_list(p, gl->which, member)) {
+ pprintf(p, "[%s] is not in %s %s list.\n", member, yourthe, listname);
+ if (loadme && !connected)
+ player_remove(p1);
+ return COM_OK;
+ }
+ list_sub(p, gl->which, member);
+ }
+ pprintf(p, "[%s] %s %s %s list.\n", member, addrem, yourthe, listname);
+
+ if (!personal) {
+ FILE *fp;
+ char filename[MAX_FILENAME_SIZE];
+
+ switch (gl->which) {
+ case L_MUZZLE:
+ case L_CMUZZLE:
+ case L_ABUSER:
+ case L_BAN:
+ pprintf(p, "Please leave a comment to explain why %s was %s the %s list.\n", member, addrem, listname);
+ pcommand(p, "addcomment %s %s %s list.\n", member, addrem, listname);
+ break;
+ case L_COMPUTER:
+ if (parray[p1].b_stats.rating > 0)
+ UpdateRank(TYPE_BLITZ, member, &parray[p1].b_stats, member);
+ if (parray[p1].s_stats.rating > 0)
+ UpdateRank(TYPE_STAND, member, &parray[p1].s_stats, member);
+ if (parray[p1].w_stats.rating > 0)
+ UpdateRank(TYPE_WILD, member, &parray[p1].w_stats, member);
+ break;
+ case L_ADMIN:
+ if (addsub == 1) { /* adding to list */
+ parray[p1].adminLevel = 10;
+ pprintf(p, "%s has been given an admin level of 10 - change with asetadmin.\n", member);
+ } else {
+ parray[p1].adminLevel = 0;
+ }
+ break;
+ case L_FILTER:
+ pprintf(p, "Please leave a message for filter to explain why site %s was %s filter list.\n", member, addrem);
+ break;
+ case L_REMOVEDCOM:
+ pprintf(p, "Please leave a message on anews to explain why %s was %s removedcom list.\n", member, addrem);
+ break;
+ default:
+ break;
+ }
+ if (loadme && connected) {
+ pprintf_prompt(p1, "You have been %s the %s list by %s.\n", addrem, listname, parray[p].name);
+ }
+ sprintf(filename, "%s/%s", lists_dir, listname);
+ fp = fopen(filename, "w");
+ if (fp == NULL) {
+ fprintf(stderr, "Couldn't save %s list.\n", listname);
+ } else {
+ int i;
+ for (i = 0; i < gl->numMembers; i++)
+ fprintf(fp, "%s\n", gl->member[i]);
+ fclose(fp);
+ }
+ }
+ if (loadme || (gl->which == L_ADMIN)) {
+ player_save(p1);
+ }
+ if (loadme && !connected) {
+ player_remove(p1);
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_addlist(int p, param_list param)
+{
+ return list_addsub(p, param[0].val.word, param[1].val.word, 1);
+}
+
+PUBLIC int com_sublist(int p,param_list param)
+{
+ return list_addsub(p, param[0].val.word, param[1].val.word, 2);
+}
+
+PUBLIC int com_showlist(int p, param_list param)
+{
+ List *gl;
+ int i, rights;
+
+ char *rightnames[] = {"EDIT HEAD, READ ADMINS", "EDIT GODS, READ ADMINS", "READ/WRITE ADMINS", "PUBLIC", "PERSONAL"};
+
+ if (param[0].type == 0) { /* Show all lists */
+ pprintf(p, "Lists:\n\n");
+ for (i = 0; ListArray[i].name != NULL; i++) {
+ rights = ListArray[i].rights;
+ if ((rights > P_ADMIN) || (parray[p].adminLevel >= ADMIN_ADMIN))
+ pprintf(p, "%-20s is %s\n", ListArray[i].name, rightnames[rights]);
+ }
+ } else { /* find match in index */
+ gl = list_findpartial(p, param[0].val.word, 0);
+ if (!gl) {
+ return COM_OK;
+ }
+ rights = ListArray[gl->which].rights;
+ /* display the list */
+ {
+ multicol *m = multicol_start(gl->numMembers);
+
+ pprintf(p, "-- %s list: %d %s --", ListArray[gl->which].name,
+ gl->numMembers,
+ ((!(strcmp(ListArray[gl->which].name,"filter"))) ? "ips" : (!(strcmp(ListArray[gl->which].name,"removedcom"))) ? "commands" : (!(strcmp(ListArray[gl->which].name,"channel"))) ? "channels" : "names"));
+ for (i = 0; i < gl->numMembers; i++)
+ multicol_store_sorted(m, gl->member[i]);
+ multicol_pprint(m, p, 78, 2);
+ multicol_end(m);
+ }
+ }
+ return COM_OK;
+}
+
+PUBLIC int list_channels(int p,int p1)
+{
+ List *gl;
+ int i, rights;
+
+ gl = list_findpartial(p1, "channel", 0);
+ if (!gl) {
+ return 1;
+ }
+ rights = ListArray[gl->which].rights;
+ /* display the list */
+ if (gl->numMembers == 0)
+ return 1;
+ {
+ multicol *m = multicol_start(gl->numMembers);
+
+ for (i = 0; i < gl->numMembers; i++)
+ multicol_store_sorted(m, gl->member[i]);
+ multicol_pprint(m, p, 78, 1);
+ multicol_end(m);
+ }
+ return 0;
+}
+
+/* free the memory used by a list */
+PUBLIC void list_free(List * gl)
+{
+ int i;
+ List *temp;
+
+ while (gl) {
+ for (i = 0; i < gl->numMembers; i++) {
+ strfree(gl->member[i]);
+ }
+ temp = gl->next;
+ rfree(gl);
+ gl = temp;
+ }
+}
+
+PUBLIC int titled_player(int p,char* name)
+{
+ if ((in_list(p, L_FM, name)) || (in_list(p, L_IM, name)) || (in_list(p, L_GM, name))) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
diff --git a/FICS/lists.h b/FICS/lists.h
new file mode 100644
index 0000000..8b34a69
--- /dev/null
+++ b/FICS/lists.h
@@ -0,0 +1,44 @@
+/* lists.h */
+
+#include <stdio.h>
+
+#ifndef _LISTS_H
+#define _LISTS_H
+
+/* yes, it's all cheesy.. there is no significance to the order, but make
+ sure it matches the order in lists.c */
+
+enum ListWhich {L_ADMIN = 0, L_REMOVEDCOM, L_FILTER, L_BAN, L_ABUSER,
+L_MUZZLE, L_CMUZZLE, L_FM, L_IM, L_GM, L_BLIND, L_TEAMS, L_COMPUTER,
+L_TD, L_CENSOR, L_GNOTIFY, L_NOPLAY, L_NOTIFY, L_CHANNEL};
+
+enum ListPerm {P_HEAD = 0, P_GOD, P_ADMIN, P_PUBLIC, P_PERSONAL};
+
+typedef struct {enum ListPerm rights; char *name;} ListTable;
+
+/* max. names in one list: 200 */
+#define MAX_GLOBAL_LIST_SIZE 200
+
+typedef struct _List List;
+struct _List {
+ enum ListWhich which;
+ int numMembers;
+ char *member[MAX_GLOBAL_LIST_SIZE];
+ struct _List *next;
+};
+
+extern int com_addlist();
+extern int com_sublist();
+extern int com_showlist();
+
+extern void list_free(List *);
+extern int in_list(int, enum ListWhich, char *);
+extern int list_size(int p, enum ListWhich);
+extern int list_add(int, enum ListWhich, char *);
+extern int list_addsub (int,char*,char*,int);
+extern int list_sub(int, enum ListWhich, char *);
+extern void list_print(FILE *, int, enum ListWhich);
+extern int titled_player(int, char *);
+extern int list_channels(int,int);
+
+#endif /* _LISTS_H */
diff --git a/FICS/makerank.c b/FICS/makerank.c
new file mode 100644
index 0000000..fe04fb7
--- /dev/null
+++ b/FICS/makerank.c
@@ -0,0 +1,217 @@
+#define COMPUTER_FILE DEFAULT_LISTS "/computer"
+#define MAX_LOGIN_NAME 21
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "config.h"
+
+char *rnames[] = {"std", "blitz", "wild", "lightning"};
+
+typedef struct _ratings {
+ int rating;
+ int num;
+} ratings;
+
+typedef struct _Entry {
+ char name[MAX_LOGIN_NAME];
+ ratings r[4];
+ int computer;
+} ENTRY;
+
+ENTRY **list;
+ENTRY **sortme;
+
+int GetPlayerInfo(char *fileName, ENTRY *e)
+{
+ FILE *fp;
+ char line[100];
+ char field[20];
+ char NameWithCase[30];
+ int i, done = 0;
+
+ e->computer = 0;
+ for (i = 0; i < 4; i++) {
+ e->r[i].num = 0;
+ e->r[i].rating = 0;
+ }
+
+ fp = fopen(fileName, "r");
+ fgets(line, 99, fp);
+ if (feof(fp))
+ return(0);
+ if (!strcmp(line, "v 1\n")) {
+ fgets(line, 99, fp);
+ sscanf(line, "%s", e->name);
+ fgets(line, 99, fp);
+ fgets(line, 99, fp);
+ fgets(line, 99, fp);
+ if (fscanf(fp, "%u %*u %*u %*u %u %*u %*u %*u %*u %u %*u %*u %*u %u %*u %*u %*u %*u %u %*u %*u %*u %u %*u %*u %*u %*u %u %*u %*u %*u %u %*u %*u %*u %*u", &(e->r[0].num), &(e->r[0].rating), &(e->r[1].num), &(e->r[1].rating), &(e->r[2].num), &(e->r[2].rating), &(e->r[3].num), &(e->r[3].rating)) != 8)
+ fprintf(stderr, "OOPS: couldn't parse player file %s.\n", fileName);
+ } else do {
+ sscanf(line, "%s", field);
+ if (!strcmp(field, "Name:")) {
+ sscanf(line, "%*s %s", NameWithCase);
+ if (strcasecmp(e->name, NameWithCase))
+ printf("TROUBLE: %s's handle is listed as %s.\n",
+ e->name, NameWithCase);
+ else
+ strcpy(e->name, NameWithCase);
+ } else if (!strcmp(field, "S_NUM:")) {
+ sscanf(line, "%*s %d", &(e->r[0].num));
+ } else if (!strcmp(field, "B_NUM:")) {
+ sscanf(line, "%*s %d", &(e->r[1].num));
+ } else if (!strcmp(field, "W_NUM:")) {
+ sscanf(line, "%*s %d", &(e->r[2].num));
+ } else if (!strcmp(field, "L_NUM:")) {
+ sscanf(line, "%*s %d", &(e->r[3].num));
+ } else if (!strcmp(field, "S_RATING:")) {
+ sscanf(line, "%*s %d", &(e->r[0].rating));
+ } else if (!strcmp(field, "B_RATING:")) {
+ sscanf(line, "%*s %d", &(e->r[1].rating));
+ } else if (!strcmp(field, "W_RATING:")) {
+ sscanf(line, "%*s %d", &(e->r[2].rating));
+ } else if (!strcmp(field, "L_RATING:")) {
+ sscanf(line, "%*s %d", &(e->r[3].rating));
+ } else if (!strcmp(field, "Network:")) {
+ done = 1;
+ }
+ fgets(line, 99, fp);
+ } while (!done && !feof(fp));
+ fclose(fp);
+ return (1);
+}
+
+int LoadEntries(void)
+{
+ int listsize;
+ FILE *fpPlayerList;
+ char letter1;
+ char command[90];
+ char pathInput[80];
+ ENTRY e;
+ int len, n = 0;
+
+ listsize = 100;
+ list = malloc(sizeof(ENTRY *)*listsize);
+
+ for (letter1 = 'a'; letter1 <= 'z'; letter1++) {
+ printf("Loading %c's.\n", letter1);
+ sprintf(pathInput, "%s/%c", DEFAULT_PLAYERS, letter1);
+ sprintf(command, "ls -1 %s", pathInput);
+ fpPlayerList = popen(command, "r");
+ if (fpPlayerList == NULL)
+ continue;
+ while (1) {
+ fgets(e.name, MAX_LOGIN_NAME, fpPlayerList);
+ if (feof(fpPlayerList))
+ break;
+ len = strlen(e.name);
+ e.name[len - 1] = '\0';
+ if (e.name[0] != letter1)
+ printf("File %c/%s: wrong directory.\n", letter1, e.name);
+ else {
+ sprintf(pathInput, "%s/%c/%s", DEFAULT_PLAYERS, letter1, e.name);
+ if (GetPlayerInfo(pathInput, &e)) {
+ if ((list[n] = malloc(sizeof(ENTRY))) == NULL) {
+ fprintf(stderr, "malloc() failed!\n");
+ } else {
+ memcpy(list[n], &e, sizeof(ENTRY));
+ n++;
+ if (n == listsize) {
+ listsize += 100;
+ list = realloc(list, listsize*sizeof(ENTRY *));
+ }
+ }
+ }
+ }
+ }
+ pclose(fpPlayerList);
+ }
+ return (n);
+}
+
+int SetComputers(int n)
+{
+ FILE *fpComp;
+ int i = 0;
+ char line[100], comp[30];
+
+ sprintf(line, "sort -f %s", COMPUTER_FILE);
+ fpComp = popen(line, "r");
+ if (fpComp == NULL)
+ return 0;
+ while (i < n) {
+ fgets(comp, 29, fpComp);
+ if (feof(fpComp))
+ break;
+ comp[strlen(comp) - 1] = '\0';
+
+ while (i < n && strcasecmp(list[i]->name, comp) < 0)
+ i++;
+
+ if (i < n && strcasecmp(list[i]->name, comp) == 0) {
+ list[i++]->computer = 1;
+ }
+ }
+ pclose(fpComp);
+ return(1);
+}
+
+int rtype;
+
+int sortfunc(const void *i, const void *j)
+{
+ int n = (*(ENTRY **)j)->r[rtype].rating - (*(ENTRY **)i)->r[rtype].rating;
+ return n ? n : strcasecmp((*(ENTRY **)i)->name, (*(ENTRY **)j)->name);
+}
+
+void makerank(void)
+{
+ int sortnum, sortmesize, i, n;
+ FILE *fp;
+ char fName[200];
+
+ printf("Loading players\n");
+ n = LoadEntries();
+ printf("Found %d players.\n", n);
+ printf("Setting computers.\n");
+ SetComputers(n);
+
+ for (rtype = 0; rtype < 4; rtype++) {
+ sortnum = 0; sortmesize = 100;
+ sortme = malloc(sizeof(ENTRY *)*sortmesize);
+
+ for (i = 0; i < n; i++) {
+ if (list[i]->r[rtype].rating) {
+ sortme[sortnum++] = list[i];
+ if (sortnum == sortmesize) {
+ sortmesize += 100;
+ sortme = realloc(sortme, sortmesize*sizeof(ENTRY *));
+ }
+ }
+ }
+ printf("Sorting %d %s.\n", sortnum, rnames[rtype]);
+ qsort(sortme, sortnum, sizeof(ENTRY *), sortfunc);
+ printf("Saving to file.\n");
+ sprintf(fName, "%s/rank.%s", DEFAULT_STATS, rnames[rtype]);
+ fp = fopen(fName, "w");
+ for (i = 0; i < sortnum; i++)
+ fprintf(fp, "%s %d %d %d\n", sortme[i]->name, sortme[i]->r[rtype].rating, sortme[i]->r[rtype].num, sortme[i]->computer);
+ fclose(fp);
+ free(sortme);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ if (argc > 1) {
+ printf("usage: %s.\n", argv[0]);
+ exit(0);
+ } else {
+ makerank();
+ }
+ return (0);
+}
+
diff --git a/FICS/matchproc.c b/FICS/matchproc.c
new file mode 100644
index 0000000..05a2aff
--- /dev/null
+++ b/FICS/matchproc.c
@@ -0,0 +1,1081 @@
+/* matchproc.c
+ *
+ */
+
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name email yy/mm/dd Change
+ hersco dhersco@stmarys-ca.edu 95/7/24 Created
+*/
+
+#include "stdinclude.h"
+
+#include "common.h"
+#include "talkproc.h"
+#include "comproc.h"
+#include "command.h"
+#include "utils.h"
+#include "ficsmain.h"
+#include "config.h"
+#include "playerdb.h"
+#include "network.h"
+#include "rmalloc.h"
+#include "variable.h"
+#include "gamedb.h"
+#include "gameproc.h"
+#include "obsproc.h"
+#include "board.h"
+/* #include "hostinfo.h" */
+#include "multicol.h"
+#include "ratings.h"
+#include "formula.h"
+#include "lists.h"
+#include "eco.h"
+#include <string.h>
+
+#include <sys/resource.h>
+
+PUBLIC int create_new_match(int white_player, int black_player,
+ int wt, int winc, int bt, int binc,
+ int rated, char *category, char *board,
+ int white)
+{
+ int g = game_new(), p;
+ char outStr[1024];
+ int reverse = 0;
+
+ if (g < 0)
+ return COM_FAILED;
+ if (white == 0) {
+ reverse = 1;
+ } else if (white == -1) {
+ if ((wt == bt) && (winc == binc)) {
+ if (parray[white_player].lastColor == parray[black_player].lastColor) {
+ if ((parray[white_player].num_white - parray[white_player].num_black) >
+ (parray[black_player].num_white - parray[black_player].num_black))
+ reverse = 1;
+ } else if (parray[white_player].lastColor == WHITE)
+ reverse = 1;
+ } else
+ reverse = 1; /* Challenger is always white in unbalanced
+ match */
+ }
+ if (reverse) {
+ int tmp = white_player;
+ white_player = black_player;
+ black_player = tmp;
+ }
+ player_remove_request(white_player, black_player, PEND_MATCH);
+ player_remove_request(black_player, white_player, PEND_MATCH);
+ player_remove_request(white_player, black_player, PEND_SIMUL);
+ player_remove_request(black_player, white_player, PEND_SIMUL);
+ player_decline_offers(white_player, -1, PEND_MATCH);
+ player_withdraw_offers(white_player, -1, PEND_MATCH);
+ player_decline_offers(black_player, -1, PEND_MATCH);
+ player_withdraw_offers(black_player, -1, PEND_MATCH);
+ player_withdraw_offers(white_player, -1, PEND_SIMUL);
+ player_withdraw_offers(black_player, -1, PEND_SIMUL);
+
+ wt = wt * 60; /* To Seconds */
+ bt = bt * 60;
+ garray[g].white = white_player;
+ garray[g].black = black_player;
+ strcpy(garray[g].white_name, parray[white_player].name);
+ strcpy(garray[g].black_name, parray[black_player].name);
+ garray[g].status = GAME_ACTIVE;
+ garray[g].type = game_isblitz(wt / 60, winc, bt / 60, binc, category, board);
+ if ((garray[g].type == TYPE_UNTIMED) || (garray[g].type == TYPE_NONSTANDARD))
+ garray[g].rated = 0;
+ else
+ garray[g].rated = rated;
+ garray[g].private = parray[white_player].private ||
+ parray[black_player].private;
+ garray[g].white = white_player;
+ if (garray[g].type == TYPE_BLITZ) {
+ garray[g].white_rating = parray[white_player].b_stats.rating;
+ garray[g].black_rating = parray[black_player].b_stats.rating;
+ } else if (garray[g].type == TYPE_WILD) {
+ garray[g].white_rating = parray[white_player].w_stats.rating;
+ garray[g].black_rating = parray[black_player].w_stats.rating;
+ } else if (garray[g].type == TYPE_LIGHT) {
+ garray[g].white_rating = parray[white_player].l_stats.rating;
+ garray[g].black_rating = parray[black_player].l_stats.rating;
+ } else if (garray[g].type == TYPE_BUGHOUSE) {
+ garray[g].white_rating = parray[white_player].bug_stats.rating;
+ garray[g].black_rating = parray[black_player].bug_stats.rating;
+ } else {
+ garray[g].white_rating = parray[white_player].s_stats.rating;
+ garray[g].black_rating = parray[black_player].s_stats.rating;
+ }
+ if (board_init(&garray[g].game_state, category, board)) {
+ pprintf(white_player, "PROBLEM LOADING BOARD. Game Aborted.\n");
+ pprintf(black_player, "PROBLEM LOADING BOARD. Game Aborted.\n");
+ fprintf(stderr, "FICS: PROBLEM LOADING BOARD %s %s. Game Aborted.\n",
+ category, board);
+ }
+ garray[g].game_state.gameNum = g;
+ garray[g].wTime = wt * 10;
+ garray[g].wInitTime = wt * 10;
+ garray[g].wIncrement = winc * 10;
+ garray[g].bTime = bt * 10;
+ if (garray[g].type != TYPE_UNTIMED) {
+ if (wt == 0)
+ garray[g].wTime = 100;
+ if (bt == 0)
+ garray[g].bTime = 100;
+ } /* 0 x games start with 10 seconds */
+
+#ifdef TIMESEAL
+
+ garray[g].wRealTime = garray[g].wTime * 100;
+ garray[g].bRealTime = garray[g].bTime * 100;
+ garray[g].wTimeWhenReceivedMove = 0;
+ garray[g].bTimeWhenReceivedMove = 0;
+
+#endif
+ garray[g].bInitTime = bt * 10;
+ garray[g].bIncrement = binc * 10;
+ if (garray[g].game_state.onMove == BLACK) { /* Start with black */
+ garray[g].numHalfMoves = 1;
+ garray[g].moveListSize = 1;
+ garray[g].moveList = (move_t *) rmalloc(sizeof(move_t));
+ garray[g].moveList[0].fromFile = -1;
+ garray[g].moveList[0].fromRank = -1;
+ garray[g].moveList[0].toFile = -1;
+ garray[g].moveList[0].toRank = -1;
+ garray[g].moveList[0].color = WHITE;
+ strcpy(garray[g].moveList[0].moveString, "NONE");
+ strcpy(garray[g].moveList[0].algString, "NONE");
+ } else {
+ garray[g].numHalfMoves = 0;
+ garray[g].moveListSize = 0;
+ garray[g].moveList = NULL;
+ }
+ garray[g].timeOfStart = tenth_secs();
+ garray[g].startTime = tenth_secs();
+ garray[g].lastMoveTime = garray[g].startTime;
+ garray[g].lastDecTime = garray[g].startTime;
+ garray[g].clockStopped = 0;
+ sprintf(outStr, "\n{Game %d (%s vs. %s) Creating %s %s match.}\n",
+ g + 1, parray[white_player].name,
+ parray[black_player].name,
+ rstr[garray[g].rated],
+ bstr[garray[g].type]);
+ pprintf(white_player, "%s", outStr);
+ pprintf(black_player, "%s", outStr);
+
+ for (p = 0; p < p_num; p++) {
+ int gnw, gnb;
+ if ((p == white_player) || (p == black_player))
+ continue;
+ if (parray[p].status != PLAYER_PROMPT)
+ continue;
+ if (parray[p].i_game)
+ pprintf_prompt(p, "%s", outStr);
+ gnw = in_list(p, L_GNOTIFY, parray[white_player].login);
+ gnb = in_list(p, L_GNOTIFY, parray[black_player].login);
+ if (gnw || gnb) {
+ pprintf(p, "Game notification: ");
+ if (gnw)
+ pprintf_highlight(p, parray[white_player].name);
+ else
+ pprintf(p, parray[white_player].name);
+ pprintf(p, " (%s) vs. ",
+ ratstr(GetRating(&parray[white_player], garray[g].type)));
+ if (gnb)
+ pprintf_highlight(p, parray[black_player].name);
+ else
+ pprintf(p, parray[black_player].name);
+ pprintf_prompt(p, " (%s) %s %s %d %d\n",
+ ratstr(GetRating(&parray[black_player], garray[g].type)),
+ rstr[garray[g].rated], bstr[garray[g].type],
+ garray[g].wInitTime/600, garray[g].wIncrement/10);
+ }
+ }
+ parray[white_player].game = g;
+ parray[white_player].opponent = black_player;
+ parray[white_player].side = WHITE;
+ parray[white_player].promote = QUEEN;
+ parray[black_player].game = g;
+ parray[black_player].opponent = white_player;
+ parray[black_player].side = BLACK;
+ parray[black_player].promote = QUEEN;
+ send_boards(g);
+ MakeFENpos(g, garray[g].FENstartPos);
+
+ return COM_OK;
+}
+
+PRIVATE int accept_match(int p, int p1)
+{
+ int g, adjourned, foo, which;
+ int wt, winc, bt, binc, rated, white;
+ char category[50], board[50];
+ pending *pend;
+ char tmp[100];
+ int bh=0, pp, pp1;
+
+ unobserveAll(p); /* stop observing when match starts */
+ unobserveAll(p1);
+
+ which = player_find_pendfrom(p, p1, PEND_MATCH);
+ pend = &parray[p].p_from_list[which];
+ wt = pend->param1;
+ winc = pend->param2;
+ bt = pend->param3;
+ binc = pend->param4;
+ rated = pend->param5;
+ strcpy (category, pend->char1);
+ strcpy (board, pend->char2);
+ white = (pend->param6 == -1) ? -1 : 1 - pend->param6;
+
+ pprintf(p, "You accept the challenge of %s.\n", parray[p1].name);
+ pprintf(p1, "\n%s accepts your challenge.\n", parray[p].name);
+ player_remove_request(p, p1, -1);
+ player_remove_request(p1, p, -1);
+
+ while ((which = player_find_pendto(p, -1, -1)) != -1) {
+ foo = parray[p].p_to_list[which].whoto;
+ pprintf_prompt(foo, "\n%s, who was challenging you, has joined a match with %s.\n", parray[p].name, parray[p1].name);
+ pprintf(p, "Challenge to %s withdrawn.\n", parray[foo].name);
+ player_remove_request(p, foo, -1);
+ }
+
+ while ((which = player_find_pendto(p1, -1, -1)) != -1) {
+ foo = parray[p1].p_to_list[which].whoto;
+ pprintf_prompt(foo, "\n%s, who was challenging you, has joined a match with %s.\n", parray[p1].name, parray[p].name);
+ pprintf(p1, "Challenge to %s withdrawn.\n", parray[foo].name);
+ player_remove_request(p1, foo, -1);
+ }
+
+ while ((which = player_find_pendfrom(p, -1, -1)) != -1) {
+ foo = parray[p].p_from_list[which].whofrom;
+ pprintf_prompt(foo, "\n%s, whom you were challenging, has joined a match with %s.\n", parray[p].name, parray[p1].name);
+ pprintf(p, "Challenge from %s removed.\n", parray[foo].name);
+ player_remove_request(foo, p, -1);
+ }
+
+ while ((which = player_find_pendfrom(p1, -1, -1)) != -1) {
+ foo = parray[p1].p_from_list[which].whofrom;
+ pprintf_prompt(foo, "\n%s, whom you were challenging, has joined a match with %s.\n", parray[p1].name, parray[p].name);
+ pprintf(p1, "Challenge from %s removed.\n", parray[foo].name);
+ player_remove_request(foo, p1, -1);
+ }
+
+ if (game_isblitz(wt, winc, bt, binc, category, board) == TYPE_WILD &&
+ strcmp(board, "bughouse") == 0) {
+ bh = 1;
+
+ if ((pp = parray[p].partner) >= 0 &&
+ (pp1 = parray[p1].partner) >= 0) {
+ unobserveAll(pp); /* stop observing when match starts */
+ unobserveAll(pp1);
+
+ pprintf(pp, "\nYour partner accepts the challenge of %s.\n", parray[p1].name);
+ pprintf(pp1, "\nYour partner %s's challenge was accepted.\n", parray[p].name);
+
+ while ((which = player_find_pendto(pp, -1, -1)) != -1) {
+ foo = parray[pp].p_to_list[which].whoto;
+ pprintf_prompt(foo, "\n%s, who was challenging you, has joined a match with %s.\n", parray[pp].name, parray[pp1].name);
+ pprintf(pp, "Challenge to %s withdrawn.\n", parray[foo].name);
+ player_remove_request(pp, foo, -1);
+ }
+
+ while ((which = player_find_pendto(pp1, -1, -1)) != -1) {
+ foo = parray[pp1].p_to_list[which].whoto;
+ pprintf_prompt(foo, "\n%s, who was challenging you, has joined a match with %s.\n", parray[pp1].name, parray[pp].name);
+ pprintf(pp1, "Challenge to %s withdrawn.\n", parray[foo].name);
+ player_remove_request(pp1, foo, -1);
+ }
+
+ while ((which = player_find_pendfrom(pp, -1, -1)) != -1) {
+ foo = parray[pp].p_from_list[which].whofrom;
+ pprintf_prompt(foo, "\n%s, whom you were challenging, has joined a match with %s.\n", parray[pp].name, parray[pp1].name);
+ pprintf(pp, "Challenge from %s removed.\n", parray[foo].name);
+ player_remove_request(foo, pp, -1);
+ }
+
+ while ((which = player_find_pendfrom(pp1, -1, -1)) != -1) {
+ foo = parray[pp1].p_from_list[which].whofrom;
+ pprintf_prompt(foo, "\n%s, whom you were challenging, has joined a match with %s.\n", parray[pp1].name, parray[pp].name);
+ pprintf(pp1, "Challenge from %s removed.\n", parray[foo].name);
+ player_remove_request(foo, pp1, -1);
+ }
+ } else {
+ return COM_OK;
+ }
+ }
+
+ g = game_new();
+ adjourned = 0;
+ if (game_read(g, p, p1) >= 0)
+ adjourned = 1;
+ else if (game_read(g, p1, p) >= 0) {
+ int swap;
+ adjourned = 1;
+ swap = p;
+ p = p1;
+ p1 = swap;
+ }
+ if (!adjourned) { /* no adjourned game, so begin a new game */
+ game_remove(g);
+
+ if (create_new_match(p, p1, wt, winc, bt, binc, rated, category, board, white) != COM_OK) {
+ sprintf(tmp, "There was a problem creating the new match.\n");
+ pprintf(p, tmp);
+ pprintf_prompt(p1, tmp);
+ } else if (bh) {
+ white = (parray[p].side == WHITE ? 0 : 1);
+ if (create_new_match(pp, pp1, wt, winc, bt, binc, rated, category, board, white) != COM_OK) {
+/* sprintf(tmp, "There was a problem creating the new match.\n"); */
+ pprintf_prompt(pp, tmp);
+ pprintf_prompt(pp1, tmp);
+ sprintf(tmp, "There was a problem creating your partner's match.\n");
+ pprintf(p, tmp);
+ pprintf_prompt(p1, tmp);
+ /* abort first game p-p1 IanO: abort_game()? */
+ } else {
+ int g1 = parray[p].game;
+ int g2 = parray[pp].game;
+
+ garray[g1].link = g2;
+ garray[g2].link = g1;
+
+ sprintf(tmp, "\nYour partner is playing game %d (%s vs. %s).\n",
+ g2 + 1, garray[g2].white_name, garray[g2].black_name);
+ pprintf(p, tmp);
+ pprintf_prompt(p1, tmp);
+ sprintf(tmp, "\nYour partner is playing game %d (%s vs. %s).\n",
+ g1 + 1, garray[g1].white_name, garray[g1].black_name);
+ pprintf_prompt(pp, tmp);
+ pprintf_prompt(pp1, tmp);
+ }
+ }
+ } else { /* resume adjourned game */
+ game_delete(p, p1);
+
+ sprintf(tmp, "{Game %d (%s vs. %s) Continuing %s %s match.}\n",
+ g+1, parray[p].name, parray[p1].name,
+ rstr[garray[g].rated], bstr[garray[g].type]);
+ pprintf(p, tmp);
+ pprintf(p1, tmp);
+
+ garray[g].white = p;
+ garray[g].black = p1;
+ garray[g].status = GAME_ACTIVE;
+ garray[g].result = END_NOTENDED;
+ garray[g].startTime = tenth_secs();
+ garray[g].lastMoveTime = garray[g].startTime;
+ garray[g].lastDecTime = garray[g].startTime;
+ parray[p].game = g;
+ parray[p].opponent = p1;
+ parray[p].side = WHITE;
+ parray[p1].game = g;
+ parray[p1].opponent = p;
+ parray[p1].side = BLACK;
+
+#ifdef TIMESEAL
+
+ garray[g].wRealTime = garray[g].wTime * 100;
+ garray[g].bRealTime = garray[g].bTime * 100;
+ garray[g].wTimeWhenReceivedMove = 0;
+ garray[g].bTimeWhenReceivedMove = 0;
+
+#endif
+
+ send_boards(g);
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_match(int p, param_list param)
+{
+ int adjourned; /* adjourned game? */
+ int g; /* more adjourned game junk */
+ int p1;
+ int bh=0, pp, pp1;
+ int pendfrom, pendto;
+ int ppend, p1pend;
+ int wt = -1; /* white start time */
+ int winc = -1; /* white increment */
+ int bt = -1; /* black start time */
+ int binc = -1; /* black increment */
+ int rated = -1; /* 1 = rated, 0 = unrated */
+ int white = -1; /* 1 = want white, 0 = want black */
+ char category[100], board[100], parsebuf[100];
+ char *val;
+ textlist *clauses = NULL;
+ int type;
+ int confused = 0;
+ char *colorstr[] = {"", "[black] ", "[white] "};
+ char *adjustr[] = {"", " (adjourned)"};
+
+ if ((parray[p].game >= 0) && (garray[parray[p].game].status == GAME_EXAMINE)) {
+ pprintf(p, "You can't challenge while you are examining a game.\n");
+ return COM_OK;
+ }
+ if (parray[p].game >= 0) {
+ pprintf(p, "You can't challenge while you are playing a game.\n");
+ return COM_OK;
+ }
+ stolower(param[0].val.word);
+ p1 = player_find_part_login(param[0].val.word);
+ if (p1 < 0) {
+ pprintf(p, "No user named \"%s\" is logged in.\n", param[0].val.word);
+ return COM_OK;
+ }
+ if (p1 == p) { /* Allowing to match yourself to enter
+ analysis mode */
+ ExamineScratch (p, param);
+ return COM_OK;
+ }
+ if (parray[p].open == 0) {
+ parray[p].open = 1;
+ pprintf(p, "Setting you open for matches.\n");
+ }
+ if (!parray[p1].open) {
+ pprintf(p, "Player \"%s\" is not open to match requests.\n", parray[p1].name);
+ return COM_OK;
+ }
+ if (parray[p1].game >= 0) {
+ pprintf(p, "Player \"%s\" is involved in another game.\n", parray[p1].name);
+ return COM_OK;
+ }
+/* look for an adjourned game between p and p1 */
+ g = game_new();
+ adjourned = (game_read(g, p, p1) >= 0) || (game_read(g, p1, p) >= 0);
+ if (adjourned) {
+ type = garray[g].type;
+ wt = garray[g].wInitTime / 600;
+ bt = garray[g].bInitTime / 600;
+ winc = garray[g].wIncrement / 10;
+ binc = garray[g].bIncrement / 10;
+ rated = garray[g].rated;
+ }
+ game_remove(g);
+
+ pendto = player_find_pendto(p, p1, PEND_MATCH);
+ pendfrom = player_find_pendfrom(p, p1, PEND_MATCH);
+ category[0] = '\0';
+ board[0] = '\0';
+
+ if (!adjourned) {
+ if (in_list(p1, L_NOPLAY, parray[p].login)) {
+ pprintf(p, "You are on %s's noplay list.\n", parray[p1].name);
+ return COM_OK;
+ }
+ if (player_censored(p1, p)) {
+ pprintf(p, "Player \"%s\" is censoring you.\n", parray[p1].name);
+ return COM_OK;
+ }
+ if (player_censored(p, p1)) {
+ pprintf(p, "You are censoring \"%s\".\n", parray[p1].name);
+ return COM_OK;
+ }
+ if (param[1].type != TYPE_NULL) {
+ int numba; /* temp for atoi() */
+
+ val = param[1].val.string;
+ while (!confused && (sscanf(val, " %99s", parsebuf) == 1)) {
+ val = eatword(eatwhite(val));
+ if ((category[0] != '\0') && (board[0] == '\0'))
+ strcpy(board, parsebuf);
+ else if (isdigit(*parsebuf)) {
+ if ((numba = atoi(parsebuf)) < 0) {
+ pprintf(p, "You can't specify negative time controls.\n");
+ return COM_OK;
+ } else if (numba > 1000) {
+ pprintf(p, "You can't specify time or inc above 1000.\n");
+ return COM_OK;
+ } else if (wt == -1) {
+ wt = numba;
+ } else if (winc == -1) {
+ winc = numba;
+ } else if (bt == -1) {
+ bt = numba;
+ } else if (binc == -1) {
+ binc = numba;
+ } else {
+ confused = 1;
+ }
+ } else if (strstr("rated", parsebuf) != NULL) {
+ if (rated == -1)
+ rated = 1;
+ else
+ confused = 1;
+ } else if (strstr("unrated", parsebuf) != NULL) {
+ if (rated == -1)
+ rated = 0;
+ else
+ confused = 1;
+ } else if (strstr("white", parsebuf) != NULL) {
+ if (white == -1)
+ white = 1;
+ else
+ confused = 1;
+ } else if (strstr("black", parsebuf) != NULL) {
+ if (white == -1)
+ white = 0;
+ else
+ confused = 1;
+ } else if (category[0] == '\0')
+ strcpy(category, parsebuf);
+ else
+ confused = 1;
+ }
+ if (confused) {
+ pprintf(p, "Can't interpret %s in match command.\n", parsebuf);
+ return COM_OK;
+ }
+ }
+ rated = ((rated == -1) ? parray[p].rated : rated) && parray[p1].registered && parray[p].registered;
+ if (winc == -1)
+ winc = (wt == -1) ? parray[p].d_inc : 0; /* match 5 == match 5 0 */
+ if (wt == -1)
+ wt = parray[p].d_time;
+ if (bt == -1)
+ bt = wt;
+ if (binc == -1)
+ binc = winc;
+
+ if (!strcmp(category,"bughouse")) { /* save mentioning wild */
+ (strcpy(board,"bughouse"));
+ (strcpy(category,"wild"));
+ }
+ if (category[0] && !board[0]) {
+ pprintf(p, "You must specify a board and a category.\n");
+ return COM_OK;
+ }
+ if (category[0]) {
+ char fname[MAX_FILENAME_SIZE];
+
+ sprintf(fname, "%s/%s/%s", board_dir, category, board);
+ if (!file_exists(fname)) {
+ pprintf(p, "No such category/board: %s/%s\n", category, board);
+ return COM_OK;
+ }
+ }
+ if ((pendfrom < 0) && (parray[p1].ropen == 0) && (rated != parray[p1].rated)) {
+ pprintf(p, "%s only wants to play %s games.\n", parray[p1].name,
+ rstr[parray[p1].rated]);
+ pprintf_highlight(p1, "Ignoring");
+ pprintf(p1, " %srated match request from %s.\n",
+ (parray[p1].rated ? "un" : ""), parray[p].name);
+ return COM_OK;
+ }
+ type = game_isblitz(wt, winc, bt, binc, category, board);
+
+#if 0
+ if (rated && (type == TYPE_STAND || type == TYPE_BLITZ || type == TYPE_WILD)) {
+ if (parray[p].network_player == parray[p1].network_player) {
+ rated = 1;
+ } else {
+ pprintf(p, "Network vs. local player forced to not rated\n");
+ rated = 0;
+ }
+ }
+#endif
+
+ if (rated && (type == TYPE_WILD) && (!strcmp(board,"bughouse"))) {
+ pprintf(p, "Game is bughouse - reverting to unrated\n");
+ rated = 0; /* will need to kill wild and make TYPE_BUGHOUSE */
+ }
+ if (rated && (type == TYPE_NONSTANDARD)) {
+ pprintf(p, "Game is non-standard - reverting to unrated\n");
+ rated = 0;
+ }
+ if (rated && (type == TYPE_UNTIMED)) {
+ pprintf(p, "Game is untimed - reverting to unrated\n");
+ rated = 0;
+ }
+ /* Now check formula. */
+ if ((pendfrom < 0 || param[1].type != TYPE_NULL)
+ && !GameMatchesFormula(p, p1, wt, winc, bt, binc,
+ rated, type, &clauses)) {
+ pprintf(p, "Match request does not fit formula for %s:\n",
+ parray[p1].name);
+ pprintf(p, "%s's formula: %s\n", parray[p1].name, parray[p1].formula);
+ ShowClauses (p, p1, clauses);
+ ClearTextList(clauses);
+ pprintf_highlight(p1, "Ignoring");
+ pprintf_prompt(p1, " (formula): %s (%d) %s (%d) %s.\n",
+ parray[p].name,
+ GetRating(&parray[p], type),
+ parray[p1].name,
+ GetRating(&parray[p1], type),
+ game_str(rated, wt * 60, winc, bt * 60, binc, category, board));
+ return COM_OK;
+ }
+ if (type == TYPE_WILD && strcmp(board, "bughouse") == 0) {
+ bh = 1;
+ pp = parray[p].partner;
+ pp1 = parray[p1].partner;
+
+ if (pp < 0) {
+ pprintf(p, "You have no partner for bughouse.\n");
+ return COM_OK;
+ }
+ if (pp1 < 0) {
+ pprintf(p, "Your opponent has no partner for bughouse.\n");
+ return COM_OK;
+ }
+ if (pp == pp1) {
+ pprintf(p, "You and your opponent both chose the same partner!\n");
+ return COM_OK;
+ }
+ if (pp == p1 || pp1 == p) {
+ pprintf(p, "You and your opponent can't choose each other as partners!\n");
+ return COM_OK;
+ }
+ if (parray[pp].partner != p) {
+ pprintf(p, "Your partner hasn't chosen you as his partner!\n");
+ return COM_OK;
+ }
+ if (parray[pp1].partner != p1) {
+ pprintf(p, "Your opponent's partner hasn't chosen your opponent as his partner!\n");
+ return COM_OK;
+ }
+ if (!parray[pp].open || parray[pp].game >= 0) {
+ pprintf(p, "Your partner isn't open to play right now.\n");
+ return COM_OK;
+ }
+ if (!parray[pp1].open || parray[pp1].game >= 0) {
+ pprintf(p, "Your opponent's partner isn't open to play right now.\n");
+ return COM_OK;
+ }
+ /* Bypass NOPLAY lists, censored lists, ratedness, privacy, and formula for now */
+ /* Active challenger/ee will determine these. */
+ }
+ /* Ok match offer will be made */
+
+ } /* adjourned games shouldn't have to worry
+ about that junk? */
+ if (pendto >= 0) {
+ pprintf(p, "Updating offer already made to \"%s\".\n", parray[p1].name);
+ }
+ if (pendfrom >= 0) {
+ if (pendto >= 0) {
+ pprintf(p, "Internal error\n");
+ fprintf(stderr, "FICS: This shouldn't happen. You can't have a match pending from and to the same person.\n");
+ return COM_OK;
+ }
+ if (adjourned || ((wt == parray[p].p_from_list[pendfrom].param1) &&
+ (winc == parray[p].p_from_list[pendfrom].param2) &&
+ (bt == parray[p].p_from_list[pendfrom].param3) &&
+ (binc == parray[p].p_from_list[pendfrom].param4) &&
+ (rated == parray[p].p_from_list[pendfrom].param5) &&
+ ((white == -1) || (white + parray[p].p_from_list[pendfrom].param6 == 1)) &&
+ (!strcmp(category, parray[p].p_from_list[pendfrom].char1)) &&
+ (!strcmp(board, parray[p].p_from_list[pendfrom].char2)))) {
+ /* Identical match, should accept! */
+ accept_match(p, p1);
+ return COM_OK;
+ } else {
+ player_remove_pendfrom(p, p1, PEND_MATCH);
+ player_remove_pendto(p1, p, PEND_MATCH);
+ }
+ }
+ if (pendto < 0) {
+ ppend = player_new_pendto(p);
+ if (ppend < 0) {
+ pprintf(p, "Sorry you can't have any more pending matches.\n");
+ return COM_OK;
+ }
+ p1pend = player_new_pendfrom(p1);
+ if (p1pend < 0) {
+ pprintf(p, "Sorry %s can't have any more pending matches.\n", parray[p1].name);
+ parray[p].num_to = parray[p].num_to - 1;
+ return COM_OK;
+ }
+ } else {
+ ppend = pendto;
+ p1pend = player_find_pendfrom(p1, p, PEND_MATCH);
+ }
+ parray[p].p_to_list[ppend].param1 = wt;
+ parray[p].p_to_list[ppend].param2 = winc;
+ parray[p].p_to_list[ppend].param3 = bt;
+ parray[p].p_to_list[ppend].param4 = binc;
+ parray[p].p_to_list[ppend].param5 = rated;
+ parray[p].p_to_list[ppend].param6 = white;
+ strcpy(parray[p].p_to_list[ppend].char1, category);
+ strcpy(parray[p].p_to_list[ppend].char2, board);
+ parray[p].p_to_list[ppend].type = PEND_MATCH;
+ parray[p].p_to_list[ppend].whoto = p1;
+ parray[p].p_to_list[ppend].whofrom = p;
+
+ parray[p1].p_from_list[p1pend].param1 = wt;
+ parray[p1].p_from_list[p1pend].param2 = winc;
+ parray[p1].p_from_list[p1pend].param3 = bt;
+ parray[p1].p_from_list[p1pend].param4 = binc;
+ parray[p1].p_from_list[p1pend].param5 = rated;
+ parray[p1].p_from_list[p1pend].param6 = white;
+ strcpy(parray[p1].p_from_list[p1pend].char1, category);
+ strcpy(parray[p1].p_from_list[p1pend].char2, board);
+ parray[p1].p_from_list[p1pend].type = PEND_MATCH;
+ parray[p1].p_from_list[p1pend].whoto = p1;
+ parray[p1].p_from_list[p1pend].whofrom = p;
+
+ if (pendfrom >= 0) {
+ pprintf(p, "Declining offer from %s and offering new match parameters.\n", parray[p1].name);
+ pprintf(p1, "\n%s declines your match offer a match with these parameters:", parray[p].name);
+ }
+ if (pendto >= 0) {
+ pprintf(p, "Updating match request to: ");
+ pprintf(p1, "\n%s updates the match request.\n", parray[p].name);
+ } else {
+ pprintf(p, "Issuing: ");
+ pprintf(p1, "\n", parray[p].name);
+ }
+
+ pprintf(p, "%s (%s) %s", parray[p].name,
+ ratstrii(GetRating(&parray[p], type), parray[p].registered),
+ colorstr[white + 1]);
+ pprintf_highlight(p, "%s", parray[p1].name);
+ pprintf(p, " (%s) %s%s.\n",
+ ratstrii(GetRating(&parray[p1], type), parray[p1].registered),
+ game_str(rated, wt * 60, winc, bt * 60, binc, category, board),
+ adjustr[adjourned]);
+ pprintf(p1, "Challenge: ");
+ pprintf_highlight(p1, "%s", parray[p].name);
+ pprintf(p1, " (%s) %s", ratstrii(GetRating(&parray[p], type), parray[p].registered), colorstr[white + 1]);
+ pprintf(p1, "%s (%s) %s%s.\n", parray[p1].name,
+ ratstrii(GetRating(&parray[p1], type), parray[p1].registered),
+ game_str(rated, wt * 60, winc, bt * 60, binc, category, board),
+ adjustr[adjourned]);
+ if (parray[p1].bell == 1)
+ pprintf_noformat(p1, "\007");
+
+ if (bh) {
+ pprintf(pp, "\nYour bughouse partner issuing %s (%s) %s",
+ parray[p].name, ratstrii(GetRating(&parray[p], type),
+ parray[p].registered), colorstr[white + 1]);
+ pprintf_highlight(pp, "%s", parray[p1].name);
+ pprintf(pp, " (%s) %s.\n",
+ ratstrii(GetRating(&parray[p1], type), parray[p1].registered),
+ game_str(rated, wt * 60, winc, bt * 60, binc, category, board));
+ pprintf(pp, "Your game would be ");
+ pprintf_highlight(pp, "%s", parray[pp1].name);
+ pprintf_prompt(pp, " (%s) %s%s (%s) %s.\n",
+ ratstrii(GetRating(&parray[pp1], type), parray[pp1].registered),
+ colorstr[white + 1], parray[pp].name,
+ ratstrii(GetRating(&parray[pp], type), parray[pp].registered),
+ game_str(rated, wt * 60, winc, bt * 60, binc, category, board));
+ if (parray[pp].bell == 1)
+ pprintf_noformat(pp, "\007");
+
+ pprintf(pp1, "\nYour bughouse partner was challenged ");
+ pprintf_highlight(pp1, "%s", parray[p].name);
+ pprintf(pp1, " (%s) %s", ratstrii(GetRating(&parray[p], type), parray[p].registered), colorstr[white + 1]);
+ pprintf(pp1, "%s (%s) %s.\n", parray[p1].name,
+ ratstrii(GetRating(&parray[p1], type), parray[p1].registered),
+ game_str(rated, wt * 60, winc, bt * 60, binc, category, board));
+ pprintf(pp1, "Your game would be %s (%s) %s", parray[pp1].name,
+ ratstrii(GetRating(&parray[pp1], type), parray[pp1].registered),
+ colorstr[white + 1]);
+ pprintf_highlight(pp1, "%s", parray[pp].name);
+ pprintf_prompt(pp1, " (%s) %s.\n",
+ ratstrii(GetRating(&parray[pp], type), parray[pp].registered),
+ game_str(rated, wt * 60, winc, bt * 60, binc, category, board));
+ if (parray[pp1].bell == 1)
+ pprintf_noformat(pp1, "\007");
+ }
+
+ if (in_list(p, L_COMPUTER, parray[p].name)) {
+ pprintf(p1, "--** %s is a ", parray[p].name);
+ pprintf_highlight(p1, "computer");
+ pprintf(p1, " **--\n");
+ }
+ if (in_list(p, L_COMPUTER, parray[p1].name)) {
+ pprintf(p, "--** %s is a ", parray[p1].name);
+ pprintf_highlight(p, "computer");
+ pprintf(p, " **--\n");
+ }
+ if (in_list(p, L_ABUSER, parray[p].name)) {
+ pprintf(p1, "--** %s is in the ", parray[p].name);
+ pprintf_highlight(p1, "abuser");
+ pprintf(p1, " list **--\n");
+ }
+ if (in_list(p, L_ABUSER, parray[p1].name)) {
+ pprintf(p, "--** %s is in the ", parray[p1].name);
+ pprintf_highlight(p, "abuser");
+ pprintf(p, " list **--\n");
+ }
+ if (rated) {
+ int win, draw, loss;
+ double newsterr;
+
+ rating_sterr_delta(p1, p, type, time(0), RESULT_WIN, &win, &newsterr);
+ rating_sterr_delta(p1, p, type, time(0), RESULT_DRAW, &draw, &newsterr);
+ rating_sterr_delta(p1, p, type, time(0), RESULT_LOSS, &loss, &newsterr);
+ pprintf(p1, "Your %s rating will change: Win: %s%d, Draw: %s%d, Loss: %s%d\n",
+ bstr[type],
+ (win >= 0) ? "+" : "", win,
+ (draw >= 0) ? "+" : "", draw,
+ (loss >= 0) ? "+" : "", loss);
+ pprintf(p1, "Your new RD will be %5.1f\n", newsterr);
+
+ rating_sterr_delta(p, p1, type, time(0), RESULT_WIN, &win, &newsterr);
+ rating_sterr_delta(p, p1, type, time(0), RESULT_DRAW, &draw, &newsterr);
+ rating_sterr_delta(p, p1, type, time(0), RESULT_LOSS, &loss, &newsterr);
+ pprintf(p, "Your %s rating will change: Win: %s%d, Draw: %s%d, Loss: %s%d\n",
+ bstr[type],
+ (win >= 0) ? "+" : "", win,
+ (draw >= 0) ? "+" : "", draw,
+ (loss >= 0) ? "+" : "", loss);
+ pprintf(p, "Your new RD will be %5.1f\n", newsterr);
+ }
+ pprintf_prompt(p1, "You can \"accept\" or \"decline\", or propose different parameters.\n");
+ return COM_OK;
+}
+
+PUBLIC int com_accept(int p, param_list param)
+{
+ int acceptNum = -1;
+ int from;
+ int type = -1;
+ int p1;
+
+ if (parray[p].num_from == 0) {
+ pprintf(p, "You have no offers to accept.\n");
+ return COM_OK;
+ }
+ if (param[0].type == TYPE_NULL) {
+ if (parray[p].num_from != 1) {
+ pprintf(p, "You have more than one offer to accept.\nUse \"pending\" to see them and \"accept n\" to choose which one.\n");
+ return COM_OK;
+ }
+ acceptNum = 0;
+ } else if (param[0].type == TYPE_INT) {
+ if ((param[0].val.integer < 1) || (param[0].val.integer > parray[p].num_from)) {
+ pprintf(p, "Out of range. Use \"pending\" to see the list of offers.\n");
+ return COM_OK;
+ }
+ acceptNum = param[0].val.integer - 1;
+ } else if (param[0].type == TYPE_WORD) {
+ if (!strcmp(param[0].val.word, "draw")) {
+ type = PEND_DRAW;
+ } else if (!strcmp(param[0].val.word, "pause")) {
+ type = PEND_PAUSE;
+ } else if (!strcmp(param[0].val.word, "adjourn")) {
+ type = PEND_ADJOURN;
+ } else if (!strcmp(param[0].val.word, "abort")) {
+ type = PEND_ABORT;
+ } else if (!strcmp(param[0].val.word, "takeback")) {
+ type = PEND_TAKEBACK;
+ } else if (!strcmp(param[0].val.word, "simmatch")) {
+ type = PEND_SIMUL;
+ } else if (!strcmp(param[0].val.word, "switch")) {
+ type = PEND_SWITCH;
+ } else if (!strcmp(param[0].val.word, "partner")) {
+ type = PEND_PARTNER;
+ }
+
+#if 0 /* I don't think 'accept all' makes sense. -- hersco */
+ if (!strcmp(param[0].val.word, "all")) {
+ while (parray[p].num_from != 0) {
+ pcommand(p, "accept 1");
+ }
+ return COM_OK;
+ }
+#endif
+
+ if (type >= 0) {
+ if ((acceptNum = player_find_pendfrom(p, -1, type)) < 0) {
+ pprintf(p, "There are no pending %s offers.\n", param[0].val.word);
+ return COM_OK;
+ }
+ } else { /* Word must be a name */
+ p1 = player_find_part_login(param[0].val.word);
+ if (p1 < 0) {
+ pprintf(p, "No user named \"%s\" is logged in.\n", param[0].val.word);
+ return COM_OK;
+ }
+ if ((acceptNum = player_find_pendfrom(p, p1, -1)) < 0) {
+ pprintf(p, "There are no pending offers from %s.\n", parray[p1].name);
+ return COM_OK;
+ }
+ }
+ }
+ from = parray[p].p_from_list[acceptNum].whofrom;
+
+ switch (parray[p].p_from_list[acceptNum].type) {
+ case PEND_MATCH:
+ accept_match(p, from);
+ return (COM_OK);
+ break;
+ case PEND_DRAW:
+ pcommand(p, "draw");
+ break;
+ case PEND_PAUSE:
+ pcommand(p, "pause");
+ break;
+ case PEND_ABORT:
+ pcommand(p, "abort");
+ break;
+ case PEND_TAKEBACK:
+ pcommand(p, "takeback %d", parray[p].p_from_list[acceptNum].param1);
+ break;
+ case PEND_SIMUL:
+ pcommand(p, "simmatch %s", parray[from].name);
+ break;
+ case PEND_SWITCH:
+ pcommand(p, "switch");
+ break;
+ case PEND_ADJOURN:
+ pcommand(p, "adjourn");
+ break;
+ case PEND_PARTNER:
+ pcommand(p, "partner %s", parray[from].name);
+ break;
+ }
+ return COM_OK_NOPROMPT;
+}
+
+int WordToOffer (int p, char *Word, int *type, int *p1)
+{
+ /* Convert draw adjourn match takeback abort pause
+ simmatch switch partner or <name> to offer type. */
+ if (!strcmp(Word, "match")) {
+ *type = PEND_MATCH;
+ } else if (!strcmp(Word, "draw")) {
+ *type = PEND_DRAW;
+ } else if (!strcmp(Word, "pause")) {
+ *type = PEND_PAUSE;
+ } else if (!strcmp(Word, "abort")) {
+ *type = PEND_ABORT;
+ } else if (!strcmp(Word, "takeback")) {
+ *type = PEND_TAKEBACK;
+ } else if (!strcmp(Word, "adjourn")) {
+ *type = PEND_ADJOURN;
+ } else if (!strcmp(Word, "switch")) {
+ *type = PEND_SWITCH;
+ } else if (!strcmp(Word, "simul")) {
+ *type = PEND_SIMUL;
+ } else if (!strcmp(Word, "partner")) {
+ *type = PEND_PARTNER;
+ } else if (!strcmp(Word, "all")) {
+ } else {
+ *p1 = player_find_part_login(Word);
+ if (*p1 < 0) {
+ pprintf(p, "No user named \"%s\" is logged in.\n", Word);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+PUBLIC int com_decline(int p, param_list param)
+{
+ int declineNum;
+ int p1 = -1, type = -1;
+ int count;
+
+ if (parray[p].num_from == 0) {
+ pprintf(p, "You have no pending offers from other players.\n");
+ return COM_OK;
+ }
+ if (param[0].type == TYPE_NULL) {
+ if (parray[p].num_from == 1) {
+ p1 = parray[p].p_from_list[0].whofrom;
+ type = parray[p].p_from_list[0].type;
+ } else {
+ pprintf(p, "You have more than one pending offer. Please specify which one\nyou wish to decline.\n'Pending' will give you the list.\n");
+ return COM_OK;
+ }
+ } else {
+ if (param[0].type == TYPE_WORD) {
+ if (!WordToOffer (p, param[0].val.word, &type, &p1))
+ return COM_OK;
+ } else { /* Must be an integer */
+ declineNum = param[0].val.integer - 1;
+ if (declineNum >= parray[p].num_from || declineNum < 0) {
+ pprintf(p, "Invalid offer number. Must be between 1 and %d.\n", parray[p].num_from);
+ return COM_OK;
+ }
+ p1 = parray[p].p_from_list[declineNum].whofrom;
+ type = parray[p].p_from_list[declineNum].type;
+ }
+ }
+ count = player_decline_offers(p, p1, type);
+ if (count != 1)
+ pprintf(p, "%d offers declined\n", count);
+ return COM_OK;
+}
+
+PUBLIC int com_withdraw(int p, param_list param)
+{
+ int withdrawNum;
+ int p1 = -1, type = -1;
+ int count;
+
+ if (parray[p].num_to == 0) {
+ pprintf(p, "You have no pending offers to other players.\n");
+ return COM_OK;
+ }
+ if (param[0].type == TYPE_NULL) {
+ if (parray[p].num_to == 1) {
+ p1 = parray[p].p_to_list[0].whoto;
+ type = parray[p].p_to_list[0].type;
+ } else {
+ pprintf(p, "You have more than one pending offer. Please specify which one\nyou wish to withdraw.\n'Pending' will give you the list.\n");
+ return COM_OK;
+ }
+ } else {
+ if (param[0].type == TYPE_WORD) {
+ if (!WordToOffer (p, param[0].val.word, &type, &p1))
+ return COM_OK;
+ } else { /* Must be an integer */
+ withdrawNum = param[0].val.integer - 1;
+ if (withdrawNum >= parray[p].num_to || withdrawNum < 0) {
+ pprintf(p, "Invalid offer number. Must be between 1 and %d.\n", parray[p].num_to);
+ return COM_OK;
+ }
+ p1 = parray[p].p_to_list[withdrawNum].whoto;
+ type = parray[p].p_to_list[withdrawNum].type;
+ }
+ }
+ count = player_withdraw_offers(p, p1, type);
+ if (count != 1)
+ pprintf(p, "%d offers withdrawn\n", count);
+ return COM_OK;
+}
+
+PUBLIC int com_pending(int p, param_list param)
+{
+ int i;
+
+ if (!parray[p].num_to) {
+ pprintf(p, "There are no offers pending TO other players.\n");
+ } else {
+ pprintf(p, "Offers TO other players:\n");
+ for (i = 0; i < parray[p].num_to; i++) {
+ pprintf(p, " ");
+ player_pend_print(p, &parray[p].p_to_list[i]);
+ }
+ }
+ if (!parray[p].num_from) {
+ pprintf(p, "\nThere are no offers pending FROM other players.\n");
+ } else {
+ pprintf(p, "\nOffers FROM other players:\n");
+ for (i = 0; i < parray[p].num_from; i++) {
+ pprintf(p, " %d: ", i + 1);
+ player_pend_print(p, &parray[p].p_from_list[i]);
+ }
+ pprintf(p, "\nIf you wish to accept any of these offers type 'accept n'\nor just 'accept' if there is only one offer.\n");
+ }
+ return COM_OK;
+}
diff --git a/FICS/matchproc.h b/FICS/matchproc.h
new file mode 100644
index 0000000..8f616ae
--- /dev/null
+++ b/FICS/matchproc.h
@@ -0,0 +1,35 @@
+/* matchproc.h
+ *
+ */
+
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name yy/mm/dd Change
+ hersco 95/07/24 Created
+*/
+
+#ifndef _MATCHPROC_H
+#define _MATCHPROC_H
+
+extern int com_match();
+extern int com_decline();
+extern int com_withdraw();
+extern int com_pending();
+extern int com_accept();
+extern int create_new_match(int, int, int, int, int, int, int, char *, char *, int);
+
+#endif /* _MATCHPROC_H */
diff --git a/FICS/memmove.c b/FICS/memmove.c
new file mode 100644
index 0000000..7f2ee3a
--- /dev/null
+++ b/FICS/memmove.c
@@ -0,0 +1,120 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * 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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)bcopy.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+/* #include <sys/cdefs.h>
+*/
+#include <string.h>
+
+/*
+ * sizeof(word) MUST BE A POWER OF TWO
+ * SO THAT wmask BELOW IS ALL ONES
+ */
+typedef int word; /* "word" used for optimal copy speed */
+
+#define wsize sizeof(word)
+#define wmask (wsize - 1)
+
+/*
+ * Copy a block of memory, handling overlap.
+ * This is the routine that actually implements
+ * (the portable versions of) bcopy, memcpy, and memmove.
+ */
+void *
+ memmove(dst0, src0, length)
+void *dst0;
+const void *src0;
+register size_t length;
+{
+ register char *dst = dst0;
+ register const char *src = src0;
+ register size_t t;
+
+ if (length == 0 || dst == src)/* nothing to do */
+ goto done;
+
+ /* Macros: loop-t-times; and loop-t-times, t>0 */
+#define TLOOP(s) if (t) TLOOP1(s)
+#define TLOOP1(s) do { s; } while (--t)
+
+ if ((unsigned long) dst < (unsigned long) src) {
+ /* Copy forward. */
+ t = (int) src; /* only need low bits */
+ if ((t | (int) dst) & wmask) {
+ /* Try to align operands. This cannot be done unless the low bits
+ match. */
+ if ((t ^ (int) dst) & wmask || length < wsize)
+ t = length;
+ else
+ t = wsize - (t & wmask);
+ length -= t;
+ TLOOP1(*dst++ = *src++);
+ }
+ /* Copy whole words, then mop up any trailing bytes. */
+ t = length / wsize;
+ TLOOP(*(word *) dst = *(word *) src;
+ src += wsize;
+ dst += wsize);
+ t = length & wmask;
+ TLOOP(*dst++ = *src++);
+ } else {
+ /* Copy backwards. Otherwise essentially the same. Alignment works as
+ before, except that it takes (t&wmask) bytes to align, not
+ wsize-(t&wmask). */
+ src += length;
+ dst += length;
+ t = (int) src;
+ if ((t | (int) dst) & wmask) {
+ if ((t ^ (int) dst) & wmask || length <= wsize)
+ t = length;
+ else
+ t &= wmask;
+ length -= t;
+ TLOOP1(*--dst = *--src);
+ }
+ t = length / wsize;
+ TLOOP(src -= wsize;
+ dst -= wsize;
+ *(word *) dst = *(word *) src);
+ t = length & wmask;
+ TLOOP(*--dst = *--src);
+ }
+done:
+ return (dst0);
+}
diff --git a/FICS/movecheck.c b/FICS/movecheck.c
new file mode 100644
index 0000000..8d62486
--- /dev/null
+++ b/FICS/movecheck.c
@@ -0,0 +1,1297 @@
+/* movecheck.c
+ *
+ */
+
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name email yy/mm/dd Change
+ Richard Nash 93/10/22 Created
+*/
+
+#include "stdinclude.h"
+
+#include "common.h"
+#include "movecheck.h"
+#include "algcheck.h"
+#include "board.h"
+#include "gamedb.h"
+#include "utils.h"
+#include "network.h"
+#include "playerdb.h"
+
+/* Simply tests if the input string is a move or not. */
+/* If it matches patterns below */
+/* Add to this list as you improve the move parser */
+/* MS_COMP e2e4 */
+/* MS_COMPDASH e2-e4 */
+/* MS_CASTLE o-o, o-o-o */
+/* Not done yet */
+/* MS_ALG e4, Nd5 Ncd5 */
+PUBLIC int is_move(char *mstr)
+{
+ int len = strlen(mstr);
+ if ((len > 3) && (mstr[len - 2] == '='))
+ len -= 2;
+
+ if (len == 4) { /* Test for e2e4 */
+ if (isfile(mstr[0]) && isrank(mstr[1]) &&
+ isfile(mstr[2]) && isrank(mstr[3])) {
+ return MS_COMP;
+ }
+ }
+ if (len == 5) { /* Test for e2-e4 */
+ if (isfile(mstr[0]) && isrank(mstr[1]) &&
+ (mstr[2] == '-') &&
+ isfile(mstr[3]) && isrank(mstr[4])) {
+ return MS_COMPDASH;
+ }
+ }
+ if (len == 3) { /* Test for o-o */
+ if ((mstr[0] == 'o') && (mstr[1] == '-') && (mstr[2] == 'o')) {
+ return MS_KCASTLE;
+ }
+ if ((mstr[0] == 'O') && (mstr[1] == '-') && (mstr[2] == 'O')) {
+ return MS_KCASTLE;
+ }
+ if ((mstr[0] == '0') && (mstr[1] == '-') && (mstr[2] == '0')) {
+ return MS_KCASTLE;
+ }
+ }
+ if (len == 2) { /* Test for oo */
+ if ((mstr[0] == 'o') && (mstr[1] == 'o')) {
+ return MS_KCASTLE;
+ }
+ if ((mstr[0] == 'O') && (mstr[1] == 'O')) {
+ return MS_KCASTLE;
+ }
+ if ((mstr[0] == '0') && (mstr[1] == '0')) {
+ return MS_KCASTLE;
+ }
+ }
+ if (len == 5) { /* Test for o-o-o */
+ if ((mstr[0] == 'o') && (mstr[1] == '-') && (mstr[2] == 'o') && (mstr[3] == '-') && (mstr[4] == 'o')) {
+ return MS_QCASTLE;
+ }
+ if ((mstr[0] == 'O') && (mstr[1] == '-') && (mstr[2] == 'O') && (mstr[3] == '-') && (mstr[4] == 'O')) {
+ return MS_QCASTLE;
+ }
+ if ((mstr[0] == '0') && (mstr[1] == '-') && (mstr[2] == '0') && (mstr[3] == '-') && (mstr[4] == '0')) {
+ return MS_QCASTLE;
+ }
+ }
+ if (len == 3) { /* Test for ooo */
+ if ((mstr[0] == 'o') && (mstr[1] == 'o') && (mstr[2] == 'o')) {
+ return MS_QCASTLE;
+ }
+ if ((mstr[0] == 'O') && (mstr[1] == 'O') && (mstr[2] == 'O')) {
+ return MS_QCASTLE;
+ }
+ if ((mstr[0] == '0') && (mstr[1] == '0') && (mstr[2] == '0')) {
+ return MS_QCASTLE;
+ }
+ }
+ return alg_is_move(mstr);
+}
+
+
+PUBLIC int NextPieceLoop(board_t b, int *f, int *r, int color)
+{
+ while (1) {
+ (*r) = (*r) + 1;
+ if (*r > 7) {
+ *r = 0;
+ *f = *f + 1;
+ if (*f > 7)
+ break;
+ }
+ if ((b[*f][*r] != NOPIECE) && iscolor(b[*f][*r], color))
+ return 1;
+ }
+ return 0;
+}
+
+PUBLIC int InitPieceLoop(board_t b, int *f, int *r, int color)
+{
+ *f = 0;
+ *r = -1;
+ return 1;
+}
+
+/* All of the routines assume that the obvious problems have been checked */
+/* See legal_move() */
+PRIVATE int legal_pawn_move( game_state_t *gs, int ff, int fr, int tf, int tr )
+{
+ if (ff == tf) {
+ if (gs->board[tf][tr] != NOPIECE) return 0;
+ if (gs->onMove == WHITE) {
+ if (tr - fr == 1) return 1;
+ if ((fr == 1) && (tr - fr == 2) && gs->board[ff][2]==NOPIECE) return 1;
+ } else {
+ if (fr - tr == 1) return 1;
+ if ((fr == 6) && (fr - tr == 2) && gs->board[ff][5]==NOPIECE) return 1;
+ }
+ return 0;
+ }
+ if (ff != tf) { /* Capture ? */
+ if ((ff - tf != 1) && (tf - ff != 1)) return 0;
+ if ((fr - tr != 1) && (tr - fr != 1)) return 0;
+ if (gs->onMove == WHITE) {
+ if (fr > tr) return 0;
+ if ((gs->board[tf][tr] != NOPIECE) && iscolor(gs->board[tf][tr],BLACK))
+ return 1;
+ if (gs->ep_possible[0][ff] == 1) {
+ if ((tf==ff+1) && (gs->board[ff+1][fr] == B_PAWN)) return 1;
+ } else if (gs->ep_possible[0][ff] == -1) {
+ if ((tf==ff-1) && (gs->board[ff-1][fr] == B_PAWN)) return 1;
+ }
+ } else {
+ if (tr > fr) return 0;
+ if ((gs->board[tf][tr] != NOPIECE) && iscolor(gs->board[tf][tr],WHITE))
+ return 1;
+ if (gs->ep_possible[1][ff] == 1) {
+ if ((tf==ff+1) && (gs->board[ff+1][fr] == W_PAWN)) return 1;
+ } else if (gs->ep_possible[1][ff] == -1) {
+ if ((tf==ff-1) && (gs->board[ff-1][fr] == W_PAWN)) return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+PRIVATE int legal_knight_move(game_state_t * gs, int ff, int fr, int tf, int tr)
+{
+ int dx, dy;
+
+ dx = ff - tf;
+ dy = fr - tr;
+ if ((dx == 2) || (dx == -2)) {
+ if ((dy == -1) || (dy == 1))
+ return 1;
+ }
+ if ((dy == 2) || (dy == -2)) {
+ if ((dx == -1) || (dx == 1))
+ return 1;
+ }
+ return 0;
+}
+
+PRIVATE int legal_bishop_move(game_state_t * gs, int ff, int fr, int tf, int tr)
+{
+ int dx, dy, x, y;
+ int startx, starty;
+ int count;
+ int incx, incy;
+
+ if (ff > tf) {
+ dx = ff - tf;
+ incx = -1;
+ } else {
+ dx = tf - ff;
+ incx = 1;
+ }
+ startx = ff + incx;
+ if (fr > tr) {
+ dy = fr - tr;
+ incy = -1;
+ } else {
+ dy = tr - fr;
+ incy = 1;
+ }
+ starty = fr + incy;
+ if (dx != dy)
+ return 0; /* Not diagonal */
+ if (dx == 1)
+ return 1; /* One square, ok */
+ count = dx - 1;
+ for (x = startx, y = starty; count; x += incx, y += incy, count--) {
+ if (gs->board[x][y] != NOPIECE)
+ return 0;
+ }
+ return 1;
+}
+
+PRIVATE int legal_rook_move(game_state_t * gs, int ff, int fr, int tf, int tr)
+{
+ int i;
+ int start, stop;
+
+ if (ff == tf) {
+ if (((fr - tr) == 1) || ((tr - fr) == 1))
+ return 1;
+ if (fr < tr) {
+ start = fr + 1;
+ stop = tr - 1;
+ } else {
+ start = tr + 1;
+ stop = fr - 1;
+ }
+ for (i = start; i <= stop; i++) {
+ if (gs->board[ff][i] != NOPIECE)
+ return 0;
+ }
+ return 1;
+ } else if (fr == tr) {
+ if (((ff - tf) == 1) || ((tf - ff) == 1))
+ return 1;
+ if (ff < tf) {
+ start = ff + 1;
+ stop = tf - 1;
+ } else {
+ start = tf + 1;
+ stop = ff - 1;
+ }
+ for (i = start; i <= stop; i++) {
+ if (gs->board[i][fr] != NOPIECE)
+ return 0;
+ }
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+PRIVATE int legal_queen_move(game_state_t * gs, int ff, int fr, int tf, int tr)
+{
+ return legal_rook_move(gs, ff, fr, tf, tr) || legal_bishop_move(gs, ff, fr, tf, tr);
+}
+
+/* Ckeck, if square (kf,kr) is attacked by enemy piece.
+ * Used in castling from/through check testing.
+ */
+
+/* new one from soso: */
+PRIVATE int is_square_attacked (game_state_t *gs, int kf, int kr)
+{
+ game_state_t fakeMove;
+
+ fakeMove = *gs;
+ fakeMove.board[4][kr] = NOPIECE;
+ fakeMove.board[kf][kr] = KING | fakeMove.onMove;
+ fakeMove.onMove = CToggle (fakeMove.onMove);
+ if (in_check(&fakeMove)) return 1;
+ else return 0;
+}
+
+/* old one:
+PRIVATE int is_square_attacked(game_state_t * gs, int kf, int kr)
+{
+ int f, r;
+ gs->onMove = CToggle(gs->onMove);
+
+ for (InitPieceLoop(gs->board, &f, &r, gs->onMove);
+ NextPieceLoop(gs->board, &f, &r, gs->onMove);) {
+ if (legal_move(gs, f, r, kf, kr)) {
+ gs->onMove = CToggle(gs->onMove);
+ return 1;
+ }
+ }
+ gs->onMove = CToggle(gs->onMove);
+ return 0;
+}
+*/
+
+PRIVATE int legal_king_move(game_state_t * gs, int ff, int fr, int tf, int tr)
+{
+ if (gs->onMove == WHITE) {
+ /* King side castling */
+ if ((fr == 0) && (tr == 0) && (ff == 4) && (tf == 6) && !gs->wkmoved
+ && (!gs->wkrmoved) && (gs->board[5][0] == NOPIECE) &&
+ (gs->board[6][0] == NOPIECE) && (gs->board[7][0] == W_ROOK) &&
+ (!is_square_attacked(gs, 4, 0)) && (!is_square_attacked(gs, 5, 0))) {
+ return 1;
+ }
+ /* Queen side castling */
+ if ((fr == 0) && (tr == 0) && (ff == 4) && (tf == 2) && !gs->wkmoved
+ && (!gs->wqrmoved) && (gs->board[3][0] == NOPIECE) &&
+ (gs->board[2][0] == NOPIECE) && (gs->board[1][0] == NOPIECE) &&
+ (gs->board[0][0] == W_ROOK) &&
+ (!is_square_attacked(gs, 4, 0)) && (!is_square_attacked(gs, 3, 0))) {
+ return 1;
+ }
+ } else { /* Black */
+ /* King side castling */
+ if ((fr == 7) && (tr == 7) && (ff == 4) && (tf == 6) && !gs->bkmoved
+ && (!gs->bkrmoved) && (gs->board[5][7] == NOPIECE) &&
+ (gs->board[6][7] == NOPIECE) && (gs->board[7][7] == B_ROOK) &&
+ (!is_square_attacked(gs, 4, 7)) && (!is_square_attacked(gs, 5, 7))) {
+ return 1;
+ }
+ /* Queen side castling */
+ if ((fr == 7) && (tr == 7) && (ff == 4) && (tf == 2) && (!gs->bkmoved)
+ && (!gs->bqrmoved) && (gs->board[3][7] == NOPIECE) &&
+ (gs->board[2][7] == NOPIECE) && (gs->board[1][7] == NOPIECE) &&
+ (gs->board[0][7] == B_ROOK) &&
+ (!is_square_attacked(gs, 4, 7)) && (!is_square_attacked(gs, 3, 7))) {
+ return 1;
+ }
+ }
+ if (((ff - tf) > 1) || ((tf - ff) > 1))
+ return 0;
+ if (((fr - tr) > 1) || ((tr - fr) > 1))
+ return 0;
+ return 1;
+}
+
+PRIVATE void add_pos(int tof, int tor, int *posf, int *posr, int *numpos)
+{
+ posf[*numpos] = tof;
+ posr[*numpos] = tor;
+ (*numpos)++;
+}
+
+PRIVATE void possible_pawn_moves(game_state_t * gs,
+ int onf, int onr,
+ int *posf, int *posr, int *numpos)
+{
+ if (gs->onMove == WHITE) {
+ if (gs->board[onf][onr + 1] == NOPIECE) {
+ add_pos(onf, onr + 1, posf, posr, numpos);
+ if ((onr == 1) && (gs->board[onf][onr + 2] == NOPIECE))
+ add_pos(onf, onr + 2, posf, posr, numpos);
+ }
+ if ((onf > 0) && (gs->board[onf - 1][onr + 1] != NOPIECE) &&
+ (iscolor(gs->board[onf - 1][onr + 1], BLACK)))
+ add_pos(onf - 1, onr + 1, posf, posr, numpos);
+ if ((onf < 7) && (gs->board[onf + 1][onr + 1] != NOPIECE) &&
+ (iscolor(gs->board[onf + 1][onr + 1], BLACK)))
+ add_pos(onf + 1, onr + 1, posf, posr, numpos);
+ if (gs->ep_possible[0][onf] == -1)
+ add_pos(onf - 1, onr + 1, posf, posr, numpos);
+ if (gs->ep_possible[0][onf] == 1)
+ add_pos(onf + 1, onr + 1, posf, posr, numpos);
+ } else {
+ if (gs->board[onf][onr - 1] == NOPIECE) {
+ add_pos(onf, onr - 1, posf, posr, numpos);
+ if ((onr == 6) && (gs->board[onf][onr - 2] == NOPIECE))
+ add_pos(onf, onr - 2, posf, posr, numpos);
+ }
+ if ((onf > 0) && (gs->board[onf - 1][onr - 1] != NOPIECE) &&
+ (iscolor(gs->board[onf - 1][onr - 1], WHITE)))
+ add_pos(onf - 1, onr - 1, posf, posr, numpos);
+/* loon: changed what looks like a typo, here's the original line:
+ add_pos(onf - 1, onr + 1, posf, posr, numpos);
+*/
+ if ((onf < 7) && (gs->board[onf + 1][onr - 1] != NOPIECE) &&
+ (iscolor(gs->board[onf + 1][onr - 1], WHITE)))
+ add_pos(onf + 1, onr - 1, posf, posr, numpos);
+ if (gs->ep_possible[1][onf] == -1)
+ add_pos(onf - 1, onr - 1, posf, posr, numpos);
+ if (gs->ep_possible[1][onf] == 1)
+ add_pos(onf + 1, onr - 1, posf, posr, numpos);
+ }
+}
+
+PRIVATE void possible_knight_moves(game_state_t * gs,
+ int onf, int onr,
+ int *posf, int *posr, int *numpos)
+{
+ static int knightJumps[8][2] = {{-1, 2}, {1, 2}, {2, -1}, {2, 1},
+ {-1, -2}, {1, -2}, {-2, 1}, {-2, -1}};
+ int f, r;
+ int j;
+
+ for (j = 0; j < 8; j++) {
+ f = knightJumps[j][0] + onf;
+ r = knightJumps[j][1] + onr;
+ if ((f < 0) || (f > 7))
+ continue;
+ if ((r < 0) || (r > 7))
+ continue;
+ if ((gs->board[f][r] == NOPIECE) ||
+ (iscolor(gs->board[f][r], CToggle(gs->onMove))))
+ add_pos(f, r, posf, posr, numpos);
+ }
+}
+
+PRIVATE void possible_bishop_moves(game_state_t * gs,
+ int onf, int onr,
+ int *posf, int *posr, int *numpos)
+{
+ int f, r;
+
+ /* Up Left */
+ f = onf;
+ r = onr;
+ while (1) {
+ f--;
+ r++;
+ if ((f < 0) || (f > 7))
+ break;
+ if ((r < 0) || (r > 7))
+ break;
+ if ((gs->board[f][r] != NOPIECE) && (iscolor(gs->board[f][r], gs->onMove)))
+ break;
+ add_pos(f, r, posf, posr, numpos);
+ if (gs->board[f][r] != NOPIECE)
+ break;
+ }
+ /* Up Right */
+ f = onf;
+ r = onr;
+ while (1) {
+ f++;
+ r++;
+ if ((f < 0) || (f > 7))
+ break;
+ if ((r < 0) || (r > 7))
+ break;
+ if ((gs->board[f][r] != NOPIECE) && (iscolor(gs->board[f][r], gs->onMove)))
+ break;
+ add_pos(f, r, posf, posr, numpos);
+ if (gs->board[f][r] != NOPIECE)
+ break;
+ }
+ /* Down Left */
+ f = onf;
+ r = onr;
+ while (1) {
+ f--;
+ r--;
+ if ((f < 0) || (f > 7))
+ break;
+ if ((r < 0) || (r > 7))
+ break;
+ if ((gs->board[f][r] != NOPIECE) && (iscolor(gs->board[f][r], gs->onMove)))
+ break;
+ add_pos(f, r, posf, posr, numpos);
+ if (gs->board[f][r] != NOPIECE)
+ break;
+ }
+ /* Down Right */
+ f = onf;
+ r = onr;
+ while (1) {
+ f++;
+ r--;
+ if ((f < 0) || (f > 7))
+ break;
+ if ((r < 0) || (r > 7))
+ break;
+ if ((gs->board[f][r] != NOPIECE) && (iscolor(gs->board[f][r], gs->onMove)))
+ break;
+ add_pos(f, r, posf, posr, numpos);
+ if (gs->board[f][r] != NOPIECE)
+ break;
+ }
+}
+
+PRIVATE void possible_rook_moves(game_state_t * gs,
+ int onf, int onr,
+ int *posf, int *posr, int *numpos)
+{
+ int f, r;
+
+ /* Left */
+ f = onf;
+ r = onr;
+ while (1) {
+ f--;
+ if ((f < 0) || (f > 7))
+ break;
+ if ((r < 0) || (r > 7))
+ break;
+ if ((gs->board[f][r] != NOPIECE) && (iscolor(gs->board[f][r], gs->onMove)))
+ break;
+ add_pos(f, r, posf, posr, numpos);
+ if (gs->board[f][r] != NOPIECE)
+ break;
+ }
+ /* Right */
+ f = onf;
+ r = onr;
+ while (1) {
+ f++;
+ if ((f < 0) || (f > 7))
+ break;
+ if ((r < 0) || (r > 7))
+ break;
+ if ((gs->board[f][r] != NOPIECE) && (iscolor(gs->board[f][r], gs->onMove)))
+ break;
+ add_pos(f, r, posf, posr, numpos);
+ if (gs->board[f][r] != NOPIECE)
+ break;
+ }
+ /* Up */
+ f = onf;
+ r = onr;
+ while (1) {
+ r++;
+ if ((f < 0) || (f > 7))
+ break;
+ if ((r < 0) || (r > 7))
+ break;
+ if ((gs->board[f][r] != NOPIECE) && (iscolor(gs->board[f][r], gs->onMove)))
+ break;
+ add_pos(f, r, posf, posr, numpos);
+ if (gs->board[f][r] != NOPIECE)
+ break;
+ }
+ /* Down */
+ f = onf;
+ r = onr;
+ while (1) {
+ r--;
+ if ((f < 0) || (f > 7))
+ break;
+ if ((r < 0) || (r > 7))
+ break;
+ if ((gs->board[f][r] != NOPIECE) && (iscolor(gs->board[f][r], gs->onMove)))
+ break;
+ add_pos(f, r, posf, posr, numpos);
+ if (gs->board[f][r] != NOPIECE)
+ break;
+ }
+}
+
+PRIVATE void possible_queen_moves(game_state_t * gs,
+ int onf, int onr,
+ int *posf, int *posr, int *numpos)
+{
+ possible_rook_moves(gs, onf, onr, posf, posr, numpos);
+ possible_bishop_moves(gs, onf, onr, posf, posr, numpos);
+}
+
+PRIVATE void possible_king_moves(game_state_t * gs,
+ int onf, int onr,
+ int *posf, int *posr, int *numpos)
+{
+ static int kingJumps[8][2] = {{-1, -1}, {0, -1}, {1, -1}, {-1, 1},
+ {0, 1}, {1, 1}, {-1, 0}, {1, 0}};
+ int f, r;
+ int j;
+
+ for (j = 0; j < 8; j++) {
+ f = kingJumps[j][0] + onf;
+ r = kingJumps[j][1] + onr;
+ if ((f < 0) || (f > 7))
+ continue;
+ if ((r < 0) || (r > 7))
+ continue;
+ if ((gs->board[f][r] == NOPIECE) ||
+ (iscolor(gs->board[f][r], CToggle(gs->onMove))))
+ add_pos(f, r, posf, posr, numpos);
+ }
+}
+
+/* Doesn't check for check */
+PUBLIC int legal_move(game_state_t * gs,
+ int fFile, int fRank,
+ int tFile, int tRank)
+{
+ int move_piece;
+ int legal;
+
+ if (fFile == ALG_DROP) {
+ move_piece = fRank;
+ if (move_piece == KING)
+ return 0;
+ if (gs->holding[gs->onMove==WHITE ? 0 : 1][move_piece-1] == 0)
+ return 0;
+ if (gs->board[tFile][tRank] != NOPIECE)
+ return 0;
+ if (move_piece == PAWN && (tRank == 0 || tRank == 7))
+ return 0;
+ return 1;
+ } else {
+ move_piece = piecetype(gs->board[fFile][fRank]);
+ }
+ if (gs->board[fFile][fRank] == NOPIECE)
+ return 0;
+ if (!iscolor(gs->board[fFile][fRank], gs->onMove)) /* Wrong color */
+ return 0;
+ if ((gs->board[tFile][tRank] != NOPIECE) &&
+ iscolor(gs->board[tFile][tRank], gs->onMove)) /* Can't capture own */
+ return 0;
+ if ((fFile == tFile) && (fRank == tRank)) /* Same square */
+ return 0;
+ switch (move_piece) {
+ case PAWN:
+ legal = legal_pawn_move(gs, fFile, fRank, tFile, tRank);
+ break;
+ case KNIGHT:
+ legal = legal_knight_move(gs, fFile, fRank, tFile, tRank);
+ break;
+ case BISHOP:
+ legal = legal_bishop_move(gs, fFile, fRank, tFile, tRank);
+ break;
+ case ROOK:
+ legal = legal_rook_move(gs, fFile, fRank, tFile, tRank);
+ break;
+ case QUEEN:
+ legal = legal_queen_move(gs, fFile, fRank, tFile, tRank);
+ break;
+ case KING:
+ legal = legal_king_move(gs, fFile, fRank, tFile, tRank);
+ break;
+ default:
+ return 0;
+ break;
+ }
+ return legal;
+}
+
+
+/* This fills in the rest of the mt structure once it is determined that
+ * the move is legal. Returns MOVE_ILLEGAL if move leaves you in check */
+PRIVATE int move_calculate(game_state_t * gs, move_t * mt, int promote)
+{
+ game_state_t fakeMove;
+
+ mt->pieceCaptured = gs->board[mt->toFile][mt->toRank];
+ mt->enPassant = 0; /* Don't know yet, let execute move take care
+ of it */
+ if (mt->fromFile == ALG_DROP) {
+ mt->piecePromotionTo = NOPIECE;
+ sprintf(mt->moveString, "%s/%c%c-%c%d",
+ wpstring[mt->fromRank],
+ DROP_CHAR, DROP_CHAR,
+ mt->toFile + 'a', mt->toRank + 1);
+ } else {
+ if ((piecetype(gs->board[mt->fromFile][mt->fromRank]) == PAWN) &&
+ ((mt->toRank == 0) || (mt->toRank == 7))) {
+ mt->piecePromotionTo = promote |
+ (colorval(gs->board[mt->fromFile][mt->fromRank]));
+ } else {
+ mt->piecePromotionTo = NOPIECE;
+ }
+ if ((piecetype(gs->board[mt->fromFile][mt->fromRank]) == PAWN) &&
+ ((mt->fromRank - mt->toRank == 2) || (mt->toRank - mt->fromRank == 2))) {
+ mt->doublePawn = mt->fromFile;
+ } else {
+ mt->doublePawn = -1;
+ }
+ if ((piecetype(gs->board[mt->fromFile][mt->fromRank]) == KING) &&
+ (mt->fromFile == 4) && (mt->toFile == 2)) {
+ sprintf(mt->moveString, "o-o-o");
+ } else if ((piecetype(gs->board[mt->fromFile][mt->fromRank]) == KING) &&
+ (mt->fromFile == 4) && (mt->toFile == 6)) {
+ sprintf(mt->moveString, "o-o");
+ } else {
+ sprintf(mt->moveString, "%s/%c%d-%c%d",
+ wpstring[piecetype(gs->board[mt->fromFile][mt->fromRank])],
+ mt->fromFile + 'a', mt->fromRank + 1,
+ mt->toFile + 'a', mt->toRank + 1);
+ }
+ }
+ /* Replace this with an algabraic de-parser */
+
+ sprintf(mt->algString, alg_unparse(gs, mt));
+ fakeMove = *gs;
+ /* Calculates enPassant also */
+ execute_move(&fakeMove, mt, 0);
+
+ /* Does making this move leave ME in check? */
+ if (in_check(&fakeMove))
+ return MOVE_ILLEGAL;
+ /* IanO: bughouse variants: drop cannot be check/checkmate */
+
+ return MOVE_OK;
+}
+
+PUBLIC int legal_andcheck_move(game_state_t * gs,
+ int fFile, int fRank,
+ int tFile, int tRank)
+{
+ move_t mt;
+ if (!legal_move(gs, fFile, fRank, tFile, tRank))
+ return 0;
+ mt.color = gs->onMove;
+ mt.fromFile = fFile;
+ mt.fromRank = fRank;
+ mt.toFile = tFile;
+ mt.toRank = tRank;
+ /* This should take into account a pawn promoting to another piece */
+ if (move_calculate(gs, &mt, QUEEN) == MOVE_OK)
+ return 1;
+ else
+ return 0;
+}
+
+PUBLIC int in_check(game_state_t * gs)
+{
+ int f, r;
+ int kf = -1, kr = -1;
+
+ /* Find the king */
+ if (gs->onMove == WHITE) {
+ for (f = 0; f < 8 && kf < 0; f++)
+ for (r = 0; r < 8 && kf < 0; r++)
+ if (gs->board[f][r] == B_KING) {
+ kf = f;
+ kr = r;
+ }
+ } else {
+ for (f = 0; f < 8 && kf < 0; f++)
+ for (r = 0; r < 8 && kf < 0; r++)
+ if (gs->board[f][r] == W_KING) {
+ kf = f;
+ kr = r;
+ }
+ }
+ if (kf < 0) {
+ fprintf(stderr, "FICS: Error game with no king!\n");
+ return 0;
+ }
+ for (InitPieceLoop(gs->board, &f, &r, gs->onMove);
+ NextPieceLoop(gs->board, &f, &r, gs->onMove);) {
+ if (legal_move(gs, f, r, kf, kr)) { /* In Check? */
+ return 1;
+ }
+ }
+ return 0;
+}
+
+PRIVATE int has_legal_move(game_state_t * gs)
+{
+ int i;
+ int f, r;
+ int kf,kr;
+ int possiblef[500], possibler[500];
+ int numpossible = 0;
+
+ for (InitPieceLoop(gs->board, &f, &r, gs->onMove);
+ NextPieceLoop(gs->board, &f, &r, gs->onMove);) {
+ switch (piecetype(gs->board[f][r])) {
+ case PAWN:
+ possible_pawn_moves(gs, f, r, possiblef, possibler, &numpossible);
+ break;
+ case KNIGHT:
+ possible_knight_moves(gs, f, r, possiblef, possibler, &numpossible);
+ break;
+ case BISHOP:
+ possible_bishop_moves(gs, f, r, possiblef, possibler, &numpossible);
+ break;
+ case ROOK:
+ possible_rook_moves(gs, f, r, possiblef, possibler, &numpossible);
+ break;
+ case QUEEN:
+ possible_queen_moves(gs, f, r, possiblef, possibler, &numpossible);
+ break;
+ case KING:
+ kf = f;
+ kr = r;
+ possible_king_moves(gs, f, r, possiblef, possibler, &numpossible);
+ break;
+ }
+ if (numpossible >= 500) {
+ fprintf(stderr, "FICS: Possible move overrun\n");
+ }
+ for (i = 0; i < numpossible; i++)
+ if (legal_andcheck_move(gs, f, r, possiblef[i], possibler[i])) {
+ return 1;
+ }
+ }
+
+ /* IanO: if we got here, then kf and kr must be set */
+ if (gs->gameNum >=0 && garray[gs->gameNum].link >= 0) {
+ /* bughouse: potential drops as check interpositions */
+ gs->holding[gs->onMove==WHITE ? 0 : 1][QUEEN - 1]++;
+ for (f=kf-1; f<=kf+1; f++) for (r=kr-1; r<=kr+1; r++) {
+ if (f>=0 && f<8 && r>=0 && r<8 && gs->board[f][r] == NOPIECE) {
+ /* try a drop next to the king */
+ if (legal_andcheck_move(gs, ALG_DROP, QUEEN, f, r)) {
+ gs->holding[gs->onMove==WHITE ? 0 : 1][QUEEN - 1]--;
+ return 1;
+ }
+ }
+ }
+ gs->holding[gs->onMove==WHITE ? 0 : 1][QUEEN - 1]--;
+ }
+
+/* don't think this works right... 9.30.95
+ printf("NO LEGAL MOVE!\n");
+*/
+ fprintf(stderr, "FICS: NO LEGAL MOVE!\n");
+ return 0;
+}
+
+/* This will end up being a very complicated function */
+PUBLIC int parse_move(char *mstr, game_state_t * gs, move_t * mt, int promote)
+{
+ int type = is_move(mstr);
+ int result;
+
+ mt->color = gs->onMove;
+ switch (type) {
+ case MS_NOTMOVE:
+ return MOVE_ILLEGAL;
+ break;
+ case MS_COMP:
+ mt->fromFile = mstr[0] - 'a';
+ mt->fromRank = mstr[1] - '1';
+ mt->toFile = mstr[2] - 'a';
+ mt->toRank = mstr[3] - '1';
+ break;
+ case MS_COMPDASH:
+ mt->fromFile = mstr[0] - 'a';
+ mt->fromRank = mstr[1] - '1';
+ mt->toFile = mstr[3] - 'a';
+ mt->toRank = mstr[4] - '1';
+ break;
+ case MS_KCASTLE:
+ mt->fromFile = 4;
+ mt->toFile = 6;
+ if (gs->onMove == WHITE) {
+ mt->fromRank = 0;
+ mt->toRank = 0;
+ } else {
+ mt->fromRank = 7;
+ mt->toRank = 7;
+ }
+ break;
+ case MS_QCASTLE:
+ mt->fromFile = 4;
+ mt->toFile = 2;
+ if (gs->onMove == WHITE) {
+ mt->fromRank = 0;
+ mt->toRank = 0;
+ } else {
+ mt->fromRank = 7;
+ mt->toRank = 7;
+ }
+ break;
+ case MS_ALG:
+ /* Fills in the mt structure */
+ if ((result = alg_parse_move(mstr, gs, mt)) != MOVE_OK)
+ return result;
+ break;
+ default:
+ return MOVE_ILLEGAL;
+ break;
+ }
+ if (!legal_move(gs, mt->fromFile, mt->fromRank, mt->toFile, mt->toRank)) {
+ return MOVE_ILLEGAL;
+ }
+ return move_calculate(gs, mt, promote);
+}
+
+/* Returns MOVE_OK, MOVE_NOMATERIAL, MOVE_CHECKMATE, or MOVE_STALEMATE */
+/* check_game_status prevents recursion */
+PUBLIC int execute_move(game_state_t * gs, move_t * mt, int check_game_status)
+{
+ int movedPiece;
+ int tookPiece;
+ int i, j, foobar;
+
+ if (mt->fromFile == ALG_DROP) {
+ movedPiece = mt->fromRank;
+ tookPiece = NOPIECE;
+ gs->holding[gs->onMove==WHITE ? 0 : 1][movedPiece-1]--;
+ gs->board[mt->toFile][mt->toRank] = movedPiece | gs->onMove;
+ if (gs->gameNum >= 0)
+ gs->lastIrreversable = garray[gs->gameNum].numHalfMoves;
+ } else {
+ movedPiece = gs->board[mt->fromFile][mt->fromRank];
+ tookPiece = gs->board[mt->toFile][mt->toRank];
+ if (mt->piecePromotionTo == NOPIECE) {
+ gs->board[mt->toFile][mt->toRank] = gs->board[mt->fromFile][mt->fromRank];
+ } else {
+ gs->board[mt->toFile][mt->toRank] = mt->piecePromotionTo | gs->onMove;
+ }
+ gs->board[mt->fromFile][mt->fromRank] = NOPIECE;
+ /* Check if irreversable */
+ if ((piecetype(movedPiece) == PAWN) || (tookPiece != NOPIECE)) {
+ if (gs->gameNum >= 0)
+ gs->lastIrreversable = garray[gs->gameNum].numHalfMoves;
+ }
+ /* Check if this move is en-passant */
+ if ((piecetype(movedPiece) == PAWN) && (mt->fromFile != mt->toFile) &&
+ (tookPiece == NOPIECE)) {
+ if (gs->onMove == WHITE) {
+ mt->pieceCaptured = B_PAWN;
+ } else {
+ mt->pieceCaptured = W_PAWN;
+ }
+ if (mt->fromFile > mt->toFile) {
+ mt->enPassant = -1;
+ } else {
+ mt->enPassant = 1;
+ }
+ gs->board[mt->toFile][mt->fromRank] = NOPIECE;
+ }
+ /* Check en-passant flags for next moves */
+ for (i = 0; i < 8; i++) {
+ gs->ep_possible[0][i] = 0;
+ gs->ep_possible[1][i] = 0;
+ }
+/* Added by Sparky 3/16/95
+
+ From soso@Viktoria.drp.fmph.uniba.sk Thu Mar 16 13:08:51 1995
+ Subject: Re: To DAV: enpassant prob. again
+ To: chess@caissa.onenet.net (ICS)
+ Date: Thu, 16 Mar 1995 20:06:20 +0100 (MET)
+
+ Yeah !
+ There was bug in other part of code:
+
+ movecheck.c , line about 800:
+
+ if (gs->onMove == WHITE) {
+ if ((mt->toFile+1 < 7 ) .... should be : (mt->toFile < 7 ) }
+*/
+
+ if ((piecetype(movedPiece) == PAWN) &&
+ ((mt->fromRank == mt->toRank + 2) || (mt->fromRank + 2 == mt->toRank))) {
+ /* Should turn on enpassent flag if possible */
+ if (gs->onMove == WHITE) {
+ if ((mt->toFile < 7) && gs->board[mt->toFile + 1][3] == B_PAWN) {
+ gs->ep_possible[1][mt->toFile + 1] = -1;
+ }
+ if ((mt->toFile - 1 >= 0) && gs->board[mt->toFile - 1][3] == B_PAWN) {
+ gs->ep_possible[1][mt->toFile - 1] = 1;
+ }
+ } else {
+ if ((mt->toFile < 7) && gs->board[mt->toFile + 1][4] == W_PAWN) {
+ gs->ep_possible[0][mt->toFile + 1] = -1;
+ }
+ if ((mt->toFile - 1 >= 0) && gs->board[mt->toFile - 1][4] == W_PAWN) {
+ gs->ep_possible[0][mt->toFile - 1] = 1;
+ }
+ }
+ }
+ if ((piecetype(movedPiece) == ROOK) && (mt->fromFile == 0)) {
+ if ((mt->fromRank == 0) && (gs->onMove == WHITE))
+ gs->wqrmoved = 1;
+ if ((mt->fromRank == 7) && (gs->onMove == BLACK))
+ gs->bqrmoved = 1;
+ }
+ if ((piecetype(movedPiece) == ROOK) && (mt->fromFile == 7)) {
+ if ((mt->fromRank == 0) && (gs->onMove == WHITE))
+ gs->wkrmoved = 1;
+ if ((mt->fromRank == 7) && (gs->onMove == BLACK))
+ gs->bkrmoved = 1;
+ }
+ if (piecetype(movedPiece) == KING) {
+ if (gs->onMove == WHITE)
+ gs->wkmoved = 1;
+ else
+ gs->bkmoved = 1;
+ }
+ if ((piecetype(movedPiece) == KING) &&
+ ((mt->fromFile == 4) && ((mt->toFile == 6)))) { /* Check for KS castling */
+ gs->board[5][mt->toRank] = gs->board[7][mt->toRank];
+ gs->board[7][mt->toRank] = NOPIECE;
+ }
+ if ((piecetype(movedPiece) == KING) &&
+ ((mt->fromFile == 4) && ((mt->toFile == 2)))) { /* Check for QS castling */
+ gs->board[3][mt->toRank] = gs->board[0][mt->toRank];
+ gs->board[0][mt->toRank] = NOPIECE;
+ }
+ }
+ if (gs->onMove == BLACK)
+ gs->moveNum++;
+
+ if (check_game_status) {
+ /* Does this move result in check? */
+ if (in_check(gs)) {
+ /* Check for checkmate */
+ gs->onMove = CToggle(gs->onMove);
+ if (!has_legal_move(gs))
+ return MOVE_CHECKMATE;
+ } else {
+ /* Check for stalemate */
+ gs->onMove = CToggle(gs->onMove);
+ if (!has_legal_move(gs))
+ return MOVE_STALEMATE;
+/* loon: check for insufficient mating material, first try */
+ foobar = 0;
+ for (i=0; i<8; i++) {
+ for (j=0; j<8; j++) {
+ switch(piecetype(gs->board[i][j])) {
+ case KNIGHT:
+ case BISHOP:
+ foobar++;
+ break;
+ case KING:
+ case NOPIECE:
+ break;
+ default:
+ foobar = 2;
+ break;
+ }
+ }
+ }
+ if (foobar < 2)
+ return MOVE_NOMATERIAL;
+ }
+ } else {
+ gs->onMove = CToggle(gs->onMove);
+ }
+ return MOVE_OK;
+}
+
+PUBLIC int backup_move(int g, int mode)
+{
+ game_state_t *gs;
+ move_t *m, *m1;
+ int now, i;
+
+ if (garray[g].link >= 0) /*IanO: not implemented for bughouse yet */
+ return MOVE_ILLEGAL;
+ if (garray[g].numHalfMoves < 1)
+ return MOVE_ILLEGAL;
+ gs = &garray[g].game_state;
+ m = (mode==REL_GAME) ? &garray[g].moveList[garray[g].numHalfMoves - 1] :
+ &garray[g].examMoveList[garray[g].numHalfMoves - 1];
+ if (m->toFile < 0) {
+ return MOVE_ILLEGAL;
+ }
+ gs->board[m->fromFile][m->fromRank] = gs->board[m->toFile][m->toRank];
+ if (m->piecePromotionTo != NOPIECE) {
+ gs->board[m->fromFile][m->fromRank] = PAWN |
+ colorval(gs->board[m->fromFile][m->fromRank]);
+ }
+ /******************
+ When takeback a _first_ move of rook, the ??rmoved variable
+ must be cleared . To check, if the move is first, we should
+ scan moveList.
+ *******************/
+ if (piecetype(gs->board[m->fromFile][m->fromRank]) == ROOK) {
+ if (m->color == WHITE) {
+ if ((m->fromFile == 0) && (m->fromRank == 0)) {
+ for (i = 2; i < garray[g].numHalfMoves - 1; i += 2) {
+ m1 = (mode==REL_GAME) ? &garray[g].moveList[i] : &garray[g].examMoveList[i];
+ if ((m1->fromFile == 0) && (m1->fromRank == 0))
+ break;
+ }
+ if (i == garray[g].numHalfMoves - 1)
+ gs->wqrmoved = 0;
+ }
+ if ((m->fromFile == 7) && (m->fromRank == 0)) {
+ for (i = 2; i < garray[g].numHalfMoves - 1; i += 2) {
+ m1 = (mode==REL_GAME) ? &garray[g].moveList[i] : &garray[g].examMoveList[i];
+ if ((m1->fromFile == 7) && (m1->fromRank == 0))
+ break;
+ }
+ if (i == garray[g].numHalfMoves - 1)
+ gs->wkrmoved = 0;
+ }
+ } else {
+ if ((m->fromFile == 0) && (m->fromRank == 7)) {
+ for (i = 3; i < garray[g].numHalfMoves - 1; i += 2) {
+ m1 = (mode==REL_GAME) ? &garray[g].moveList[i] : &garray[g].examMoveList[i];
+ if ((m1->fromFile == 0) && (m1->fromRank == 0))
+ break;
+ }
+ if (i == garray[g].numHalfMoves - 1)
+ gs->bqrmoved = 0;
+ }
+ if ((m->fromFile == 7) && (m->fromRank == 7)) {
+ for (i = 3; i < garray[g].numHalfMoves - 1; i += 2) {
+ m1 = (mode==REL_GAME) ? &garray[g].moveList[i] : &garray[g].examMoveList[i];
+ if ((m1->fromFile == 7) && (m1->fromRank == 0))
+ break;
+ }
+ if (i == garray[g].numHalfMoves - 1)
+ gs->bkrmoved = 0;
+ }
+ }
+ }
+ if (piecetype(gs->board[m->fromFile][m->fromRank]) == KING) {
+ gs->board[m->toFile][m->toRank] = m->pieceCaptured;
+
+ if (m->toFile - m->fromFile == 2) {
+ gs->board[7][m->fromRank] = ROOK |
+ colorval(gs->board[m->fromFile][m->fromRank]);
+ gs->board[5][m->fromRank] = NOPIECE;
+
+ /********
+ If takeback a castling, the appropriates ??moved variables
+ must be cleared
+ ********/
+ if (m->color == WHITE) {
+ gs->wkmoved = 0;
+ gs->wkrmoved = 0;
+ } else {
+ gs->bkmoved = 0;
+ gs->bkrmoved = 0;
+ }
+ goto cleanupMove;
+ }
+ if (m->fromFile - m->toFile == 2) {
+ gs->board[0][m->fromRank] = ROOK |
+ colorval(gs->board[m->fromFile][m->fromRank]);
+ gs->board[3][m->fromRank] = NOPIECE;
+
+ /**********
+ If takeback a castling, the appropriate ??moved variables
+ must be cleared
+ ***********/
+ if (m->color == WHITE) {
+ gs->wkmoved = 0;
+ gs->wqrmoved = 0;
+ } else {
+ gs->bkmoved = 0;
+ gs->bqrmoved = 0;
+ }
+ goto cleanupMove;
+ }
+ /******************
+ When takeback a _first_ move of king (not the castling),
+ the ?kmoved variable must be cleared . To check, if the move is first,
+ we should scan moveList.
+ *******************/
+
+ if (m->color == WHITE) {
+
+ if ((m->fromFile == 4) && (m->fromRank == 0)) {
+ for (i = 2; i < garray[g].numHalfMoves - 1; i += 2) {
+ m1 = (mode==REL_GAME) ? &garray[g].moveList[i] : &garray[g].examMoveList[i];
+ if ((m1->fromFile == 4) && (m1->fromRank == 0))
+ break;
+ }
+ if (i == garray[g].numHalfMoves - 1)
+ gs->wkmoved = 0;
+ }
+ } else {
+ if ((m->fromFile == 4) && (m->fromRank == 7)) {
+ for (i = 3; i < garray[g].numHalfMoves - 1; i += 2) {
+ m1 = (mode==REL_GAME) ? &garray[g].moveList[i] : &garray[g].examMoveList[i];
+ if ((m1->fromFile == 4) && (m1->fromRank == 7))
+ break;
+ }
+ if (i == garray[g].numHalfMoves - 1)
+ gs->bkmoved = 0;
+ }
+ }
+ }
+ if (m->enPassant) { /* Do enPassant */
+ gs->board[m->toFile][m->fromRank] = PAWN |
+ (colorval(gs->board[m->fromFile][m->fromRank]) == WHITE ? BLACK : WHITE);
+ gs->board[m->toFile][m->toRank] = NOPIECE;
+ /* Should set the enpassant array, but I don't care right now */
+ goto cleanupMove;
+ }
+ gs->board[m->toFile][m->toRank] = m->pieceCaptured;
+cleanupMove:
+ if (garray[g].status != GAME_EXAMINE) {
+ game_update_time(g);
+ }
+ garray[g].numHalfMoves--;
+ if (garray[g].status != GAME_EXAMINE) {
+ if (garray[g].wInitTime) { /* Don't update times in untimed games */
+ now = tenth_secs();
+
+#ifdef TIMESEAL
+
+ if (m->color == WHITE) {
+ if (con[parray[garray[g].white].socket].timeseal) { /* white uses timeseal? */
+ garray[g].wRealTime += (m->tookTime * 100);
+ garray[g].wRealTime -= (garray[g].wIncrement * 100);
+ garray[g].wTime = garray[g].wRealTime / 100;
+ if (con[parray[garray[g].black].socket].timeseal) { /* opp uses timeseal? */
+ garray[g].bTime = garray[g].bRealTime / 100;
+ } else { /* opp has no timeseal */
+ garray[g].bTime += (garray[g].lastDecTime - garray[g].lastMoveTime);
+ }
+ } else { /* white has no timeseal */
+ garray[g].wTime += m->tookTime;
+ garray[g].wTime -= garray[g].wIncrement;
+ if (con[parray[garray[g].black].socket].timeseal) { /* opp uses timeseal? */
+ garray[g].bTime = garray[g].bRealTime / 100;
+ } else { /* opp has no timeseal */
+ garray[g].bTime += (garray[g].lastDecTime - garray[g].lastMoveTime);
+ }
+ }
+ } else {
+ if (con[parray[garray[g].black].socket].timeseal) { /* black uses timeseal? */
+ garray[g].bRealTime += (m->tookTime * 100);
+ garray[g].bRealTime -= (garray[g].wIncrement * 100);
+ garray[g].bTime = garray[g].bRealTime / 100;
+ if (con[parray[garray[g].white].socket].timeseal) { /* opp uses timeseal? */
+ garray[g].wTime = garray[g].wRealTime / 100;
+ } else { /* opp has no timeseal */
+ garray[g].wTime += (garray[g].lastDecTime - garray[g].lastMoveTime);
+ }
+ } else { /* black has no timeseal */
+ garray[g].bTime += m->tookTime;
+ if (!garray[g].bIncrement)
+ garray[g].bTime -= garray[g].wIncrement;
+ else
+ garray[g].bTime -= garray[g].bIncrement;
+ if (con[parray[garray[g].white].socket].timeseal) { /* opp uses timeseal? */
+ garray[g].wTime = garray[g].wRealTime / 100;
+ } else { /* opp has no timeseal */
+ garray[g].wTime += (garray[g].lastDecTime - garray[g].lastMoveTime);
+ }
+ }
+ }
+
+#else
+
+ if (m->color == WHITE) {
+ garray[g].wTime += m->tookTime;
+ garray[g].wTime = garray[g].wTime - garray[g].wIncrement;
+ garray[g].bTime += (garray[g].lastDecTime - garray[g].lastMoveTime);
+ } else {
+ garray[g].bTime += m->tookTime;
+ if (!garray[g].bIncrement)
+ garray[g].bTime = garray[g].bTime - garray[g].wIncrement;
+ else
+ garray[g].bTime = garray[g].bTime - garray[g].bIncrement;
+ garray[g].wTime += (garray[g].lastDecTime - garray[g].lastMoveTime);
+ }
+
+#endif
+
+ if (garray[g].numHalfMoves == 0)
+ garray[g].timeOfStart = now;
+ garray[g].lastMoveTime = now;
+ garray[g].lastDecTime = now;
+ }
+ }
+ if (gs->onMove == BLACK)
+ gs->onMove = WHITE;
+ else {
+ gs->onMove = BLACK;
+ gs->moveNum--;
+ }
+
+ /******* Here begins the patch : ********************************
+ Takeback of last move is done already, it's time to update enpassant
+ array. (patch from Soso, added by Sparky 3/17/95)
+ ********/
+
+ if (garray[g].numHalfMoves > 0) {
+ m1 = (mode==REL_GAME) ? &garray[g].moveList[garray[g].numHalfMoves - 1] :
+ &garray[g].examMoveList[garray[g].numHalfMoves - 1];
+ if (piecetype(gs->board[m1->toFile][m1->toRank]) == PAWN) {
+ if ((m1->toRank - m1->fromRank) == 2) {
+ if ((m1->toFile < 7) && gs->board[m1->toFile + 1][3] == B_PAWN) {
+ gs->ep_possible[1][m1->toFile + 1] = -1;
+ }
+ if ((m1->toFile - 1 >= 0) && gs->board[m1->toFile - 1][3] == B_PAWN) {
+ gs->ep_possible[1][m1->toFile - 1] = 1;
+ }
+ }
+ if ((m1->toRank - m1->fromRank) == -2) {
+ if ((m1->toFile < 7) && gs->board[m1->toFile + 1][4] == W_PAWN) {
+ gs->ep_possible[0][m1->toFile + 1] = -1;
+ }
+ if ((m1->toFile - 1 >= 0) && gs->board[m1->toFile - 1][4] == W_PAWN) {
+ gs->ep_possible[0][m1->toFile - 1] = 1;
+ }
+ }
+ }
+ }
+ /************** and here's the end **************/
+ return MOVE_OK;
+}
+
+
+
+
diff --git a/FICS/movecheck.h b/FICS/movecheck.h
new file mode 100644
index 0000000..53a1712
--- /dev/null
+++ b/FICS/movecheck.h
@@ -0,0 +1,61 @@
+/* movecheck.h
+ *
+ */
+
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name email yy/mm/dd Change
+ Richard Nash 93/10/22 Created
+*/
+
+#ifndef _MOVECHECK_H
+#define _MOVECHECK_H
+
+#define MOVE_OK 0
+#define MOVE_ILLEGAL 1
+#define MOVE_STALEMATE 2
+#define MOVE_CHECKMATE 3
+#define MOVE_AMBIGUOUS 4
+#define MOVE_NOMATERIAL 5
+
+#define MS_NOTMOVE 0
+#define MS_COMP 1
+#define MS_COMPDASH 2
+#define MS_ALG 3
+#define MS_KCASTLE 4
+#define MS_QCASTLE 5
+
+#define isrank(c) (((c) <= '8') && ((c) >= '1'))
+#define isfile(c) (((c) >= 'a') && ((c) <= 'h'))
+
+#if !defined(_BOARD_H)
+#include "board.h"
+#endif
+
+extern int is_move(char *);
+extern int parse_move(char *, game_state_t *, move_t *, int);
+extern int execute_move(game_state_t *, move_t *, int);
+extern int backup_move(int, int);
+
+/* Some useful chess utilities */
+extern int NextPieceLoop(board_t, int *, int *, int);
+extern int InitPieceLoop(board_t, int *, int *, int);
+extern int legal_move(game_state_t *, int, int, int, int);
+extern int legal_andcheck_move(game_state_t *, int, int, int, int);
+extern int in_check(game_state_t *);
+
+#endif /* _MOVECHECK_H */
diff --git a/FICS/multicol.c b/FICS/multicol.c
new file mode 100644
index 0000000..8d52b04
--- /dev/null
+++ b/FICS/multicol.c
@@ -0,0 +1,153 @@
+/* multicol.c
+ *
+ */
+
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name email yy/mm/dd Change
+ Richard Nash 93/10/22 Created
+*/
+
+#include "stdinclude.h"
+
+#include "common.h"
+#include "multicol.h"
+#include "utils.h"
+#include "rmalloc.h"
+
+/* grimm */
+#if defined(SGI)
+#else
+/* char *strchr( char *s, int c); */
+#endif
+/* added for warning */
+
+PUBLIC multicol *multicol_start(int maxArray)
+{
+ int i;
+ multicol *m;
+
+ m = (multicol *) rmalloc(sizeof(multicol));
+ m->arraySize = maxArray;
+ m->num = 0;
+ m->strArray = (char **) rmalloc(sizeof(char *) * m->arraySize);
+ for (i = 0; i < m->arraySize; i++)
+ m->strArray[i] = NULL;
+ return m;
+}
+
+PUBLIC int multicol_store(multicol * m, char *str)
+{
+ if (m->num >= m->arraySize)
+ return -1;
+ if (!str)
+ return -1;
+ m->strArray[m->num] = strdup(str);
+ m->num++;
+ return 0;
+}
+
+PUBLIC int multicol_store_sorted(multicol * m, char *str)
+/* use this instead of milticol_store to print a list sorted */
+{
+ int i;
+ int found = 0;
+ if (m->num >= m->arraySize)
+ return -1;
+ if (!str)
+ return -1;
+ for (i = m->num; (i > 0) && (!found); i--) {
+ if (strcasecmp(str, m->strArray[i - 1]) >= 0) {
+ found = 1;
+ m->strArray[i] = strdup(str);
+ } else {
+ m->strArray[i] = m->strArray[i - 1];
+ }
+ }
+ if (!found)
+ m->strArray[0] = strdup(str);
+ m->num++;
+ return 0;
+}
+
+PUBLIC int multicol_pprint(multicol * m, int player, int cols, int space)
+{
+ int i;
+ int maxWidth = 0;
+ int numPerLine;
+ int numLines;
+ int on, theone, len;
+ int done;
+ int temp;
+ char *tempptr;
+
+ pprintf(player, "\n");
+ for (i = 0; i < m->num; i++) {
+ tempptr = m->strArray[i];
+ temp = strlen(tempptr); /* loon: yes, this is pathetic */
+ for (; *tempptr; tempptr++) {
+ if (*tempptr == '\033')
+ temp -= 4;
+ }
+ if (temp > maxWidth)
+ maxWidth = temp;
+ }
+ maxWidth += space;
+ numPerLine = cols / maxWidth;
+ numLines = m->num / numPerLine;
+ if (numLines * numPerLine < m->num)
+ numLines++;
+ on = 0;
+ done = 0;
+ while (!done) {
+ for (i = 0; i < numPerLine; i++) {
+ theone = on + numLines * i;
+ if (theone >= m->num) {
+ break;
+ }
+ tempptr = m->strArray[theone];
+ temp = strlen(tempptr); /* loon: yes, still pathetic */
+ for (; *tempptr; tempptr++) {
+ if (*tempptr == '\033')
+ temp -= 4;
+ }
+ len = maxWidth - temp;
+ if (i == numPerLine - 1)
+ len -= space;
+ pprintf(player, "%s", m->strArray[theone]);
+ while (len) {
+ pprintf(player, " ");
+ len--;
+ }
+ }
+ pprintf(player, "\n");
+ on += 1;
+ if (on >= numLines)
+ break;
+ }
+ return 0;
+}
+
+PUBLIC int multicol_end(multicol * m)
+{
+ int i;
+ for (i = 0; i < m->num; i++)
+ rfree(m->strArray[i]);
+ rfree(m->strArray);
+ rfree(m);
+ return 0;
+}
diff --git a/FICS/multicol.c.orig b/FICS/multicol.c.orig
new file mode 100644
index 0000000..8d52b04
--- /dev/null
+++ b/FICS/multicol.c.orig
@@ -0,0 +1,153 @@
+/* multicol.c
+ *
+ */
+
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name email yy/mm/dd Change
+ Richard Nash 93/10/22 Created
+*/
+
+#include "stdinclude.h"
+
+#include "common.h"
+#include "multicol.h"
+#include "utils.h"
+#include "rmalloc.h"
+
+/* grimm */
+#if defined(SGI)
+#else
+/* char *strchr( char *s, int c); */
+#endif
+/* added for warning */
+
+PUBLIC multicol *multicol_start(int maxArray)
+{
+ int i;
+ multicol *m;
+
+ m = (multicol *) rmalloc(sizeof(multicol));
+ m->arraySize = maxArray;
+ m->num = 0;
+ m->strArray = (char **) rmalloc(sizeof(char *) * m->arraySize);
+ for (i = 0; i < m->arraySize; i++)
+ m->strArray[i] = NULL;
+ return m;
+}
+
+PUBLIC int multicol_store(multicol * m, char *str)
+{
+ if (m->num >= m->arraySize)
+ return -1;
+ if (!str)
+ return -1;
+ m->strArray[m->num] = strdup(str);
+ m->num++;
+ return 0;
+}
+
+PUBLIC int multicol_store_sorted(multicol * m, char *str)
+/* use this instead of milticol_store to print a list sorted */
+{
+ int i;
+ int found = 0;
+ if (m->num >= m->arraySize)
+ return -1;
+ if (!str)
+ return -1;
+ for (i = m->num; (i > 0) && (!found); i--) {
+ if (strcasecmp(str, m->strArray[i - 1]) >= 0) {
+ found = 1;
+ m->strArray[i] = strdup(str);
+ } else {
+ m->strArray[i] = m->strArray[i - 1];
+ }
+ }
+ if (!found)
+ m->strArray[0] = strdup(str);
+ m->num++;
+ return 0;
+}
+
+PUBLIC int multicol_pprint(multicol * m, int player, int cols, int space)
+{
+ int i;
+ int maxWidth = 0;
+ int numPerLine;
+ int numLines;
+ int on, theone, len;
+ int done;
+ int temp;
+ char *tempptr;
+
+ pprintf(player, "\n");
+ for (i = 0; i < m->num; i++) {
+ tempptr = m->strArray[i];
+ temp = strlen(tempptr); /* loon: yes, this is pathetic */
+ for (; *tempptr; tempptr++) {
+ if (*tempptr == '\033')
+ temp -= 4;
+ }
+ if (temp > maxWidth)
+ maxWidth = temp;
+ }
+ maxWidth += space;
+ numPerLine = cols / maxWidth;
+ numLines = m->num / numPerLine;
+ if (numLines * numPerLine < m->num)
+ numLines++;
+ on = 0;
+ done = 0;
+ while (!done) {
+ for (i = 0; i < numPerLine; i++) {
+ theone = on + numLines * i;
+ if (theone >= m->num) {
+ break;
+ }
+ tempptr = m->strArray[theone];
+ temp = strlen(tempptr); /* loon: yes, still pathetic */
+ for (; *tempptr; tempptr++) {
+ if (*tempptr == '\033')
+ temp -= 4;
+ }
+ len = maxWidth - temp;
+ if (i == numPerLine - 1)
+ len -= space;
+ pprintf(player, "%s", m->strArray[theone]);
+ while (len) {
+ pprintf(player, " ");
+ len--;
+ }
+ }
+ pprintf(player, "\n");
+ on += 1;
+ if (on >= numLines)
+ break;
+ }
+ return 0;
+}
+
+PUBLIC int multicol_end(multicol * m)
+{
+ int i;
+ for (i = 0; i < m->num; i++)
+ rfree(m->strArray[i]);
+ rfree(m->strArray);
+ rfree(m);
+ return 0;
+}
diff --git a/FICS/multicol.h b/FICS/multicol.h
new file mode 100644
index 0000000..5acb850
--- /dev/null
+++ b/FICS/multicol.h
@@ -0,0 +1,41 @@
+/* multicol.h
+ *
+ */
+
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name email yy/mm/dd Change
+ Richard Nash 93/10/22 Created
+*/
+
+#ifndef _MULTICOL_H
+#define _MULTICOL_H
+
+typedef struct _multicol
+{
+ int arraySize;
+ int num;
+ char **strArray;
+} multicol;
+
+extern multicol *multicol_start(int);
+extern int multicol_store(multicol *, char *);
+extern int multicol_store_sorted(multicol *, char *);
+extern int multicol_pprint(multicol *, int, int, int);
+extern int multicol_end(multicol *);
+
+#endif /* _MULTICOL_H */
diff --git a/FICS/network.c b/FICS/network.c
new file mode 100644
index 0000000..2480a69
--- /dev/null
+++ b/FICS/network.c
@@ -0,0 +1,622 @@
+/* network.c
+ *
+ */
+
+#include "stdinclude.h"
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/telnet.h>
+
+#include "ficsmain.h"
+#include "common.h"
+#include "utils.h"
+#include "playerdb.h"
+#include "network.h"
+#include "rmalloc.h"
+#include "config.h"
+#ifdef TIMESEAL
+#include "timeseal.h"
+#endif
+
+extern int errno;
+
+PRIVATE int sockfd = 0; /* The socket */
+PRIVATE int numConnections = 0;
+/* Sparse array */
+PUBLIC connection con[512];
+
+PUBLIC int no_file;
+PUBLIC int max_connections;
+
+/* Index == fd, for sparse array, quick lookups! wasted memory :( */
+PUBLIC int findConnection(int fd)
+{
+ if (con[fd].status == NETSTAT_EMPTY)
+ return -1;
+ else
+ return fd;
+}
+
+PUBLIC int net_addConnection(int fd, unsigned int fromHost)
+{
+ int noblock = 1;
+
+ if (findConnection(fd) >= 0) {
+ fprintf(stderr, "FICS: FD already in connection table!\n");
+ return -1;
+ }
+ if (numConnections >= max_connections)
+ return -1;
+ if (ioctl(fd, FIONBIO, &noblock) == -1) {
+ fprintf(stderr, "Error setting nonblocking mode errno=%d\n", errno);
+ }
+ con[fd].fd = fd;
+ if (fd != 0)
+ con[fd].outFd = fd;
+ else
+ con[fd].outFd = 1;
+ con[fd].fromHost = fromHost;
+ con[fd].status = NETSTAT_CONNECTED;
+#ifdef TIMESEAL
+ con[fd].user[0]='\0';
+ con[fd].sys[0]='\0';
+ con[fd].timeseal = 0;
+ con[fd].time = 0;
+#endif
+ con[fd].numPending = 0;
+ con[fd].processed = 0;
+ con[fd].outPos = 0;
+ if (con[fd].sndbuf == NULL) {
+#ifdef DEBUG
+ fprintf(stderr, "FICS: nac(%d) allocating sndbuf.\n", fd);
+#endif
+ con[fd].sndbufpos = 0;
+ con[fd].sndbufsize = MAX_STRING_LENGTH;
+ con[fd].sndbuf = rmalloc(MAX_STRING_LENGTH);
+ } else {
+#ifdef DEBUG
+ fprintf(stderr, "FICS: nac(%d) reusing old sndbuf size %d pos %d.\n", fd, con[fd].sndbufsize, con[fd].sndbufpos);
+#endif
+ }
+ con[fd].state = 0;
+ numConnections++;
+
+#ifdef DEBUG
+ fprintf(stderr, "FICS: fd: %d connections: %d descriptors: %d \n", fd, numConnections, getdtablesize()); /* sparky 3/13/95 */
+#endif
+
+ return 0;
+}
+
+PRIVATE int remConnection(int fd)
+{
+ int which;
+ if ((which = findConnection(fd)) < 0) {
+ return -1;
+ }
+ numConnections--;
+ con[fd].status = NETSTAT_EMPTY;
+ if (con[fd].sndbuf == NULL) {
+ fprintf(stderr, "FICS: remcon(%d) SNAFU, this shouldn't happen.\n", fd);
+ } else {
+ if (con[fd].sndbufsize > MAX_STRING_LENGTH) {
+ con[fd].sndbufsize = MAX_STRING_LENGTH;
+ con[fd].sndbuf = rrealloc(con[fd].sndbuf, MAX_STRING_LENGTH);
+ }
+ if (con[fd].sndbufpos) { /* didn't send everything, bummer */
+ con[fd].sndbufpos = 0;
+ }
+ }
+ return 0;
+}
+
+PRIVATE void net_flushme(int which)
+{
+ int sent;
+
+ sent = send(con[which].outFd, con[which].sndbuf, con[which].sndbufpos, 0);
+ if (sent == -1) {
+ if (errno != EPIPE) /* EPIPE = they've disconnected */
+ fprintf(stderr, "FICS: net_flushme(%d) couldn't send, errno=%d.\n", which, errno);
+ con[which].sndbufpos = 0;
+ } else {
+ con[which].sndbufpos -= sent;
+ if (con[which].sndbufpos)
+ memmove(con[which].sndbuf, con[which].sndbuf + sent, con[which].sndbufpos);
+ }
+ if (con[which].sndbufsize > MAX_STRING_LENGTH && con[which].sndbufpos < MAX_STRING_LENGTH) {
+ /* time to shrink the buffer */
+ con[which].sndbuf = rrealloc(con[which].sndbuf, MAX_STRING_LENGTH);
+ con[which].sndbufsize = MAX_STRING_LENGTH;
+ }
+}
+
+PRIVATE void net_flush_all_connections(void)
+{
+ int which;
+ fd_set writefds;
+ struct timeval to;
+
+ FD_ZERO(&writefds);
+ for (which = 0; which < MAX_PLAYER; which++)
+ if (con[which].status == NETSTAT_CONNECTED && con[which].sndbufpos)
+ FD_SET(con[which].outFd, &writefds);
+
+ to.tv_usec = 0;
+ to.tv_sec = 0;
+ select(no_file, NULL, &writefds, NULL, &to);
+ for (which = 0; which < MAX_PLAYER; which++) {
+ if (FD_ISSET(con[which].outFd, &writefds)) {
+ net_flushme(which);
+ }
+ }
+}
+
+PRIVATE void net_flush_connection(int fd)
+{
+ int which;
+ fd_set writefds;
+ struct timeval to;
+
+ if (((which = findConnection(fd)) >= 0) && (con[which].sndbufpos)) {
+ FD_ZERO(&writefds);
+ FD_SET(con[which].outFd, &writefds);
+ to.tv_usec = 0;
+ to.tv_sec = 0;
+ select(no_file, NULL, &writefds, NULL, &to);
+ if (FD_ISSET(con[which].outFd, &writefds)) {
+ net_flushme(which);
+ }
+ }
+ return;
+}
+
+PRIVATE int sendme(int which, char *str, int len)
+{
+ int i, count;
+ fd_set writefds;
+ struct timeval to;
+ count = len;
+
+ while ((i = ((con[which].sndbufsize - con[which].sndbufpos) < len) ? (con[which].sndbufsize - con[which].sndbufpos) : len) > 0) {
+ memmove(con[which].sndbuf + con[which].sndbufpos, str, i);
+ con[which].sndbufpos += i;
+ if (con[which].sndbufpos == con[which].sndbufsize) {
+
+ FD_ZERO(&writefds);
+ FD_SET(con[which].outFd, &writefds);
+ to.tv_usec = 0;
+ to.tv_sec = 0;
+ select(no_file, NULL, &writefds, NULL, &to);
+ if (FD_ISSET(con[which].outFd, &writefds)) {
+ net_flushme(which);
+ } else {
+ /* time to grow the buffer */
+ con[which].sndbufsize += MAX_STRING_LENGTH;
+ con[which].sndbuf = rrealloc(con[which].sndbuf, con[which].sndbufsize);
+ }
+ }
+ str += i;
+ len -= i;
+ }
+ return count;
+}
+
+/*
+ * -1 for an error other than EWOULDBLOCK.
+ * Put <lf> after every <cr> and put \ at the end of overlength lines.
+ * Doesn't send anything unless the buffer fills, output waits until
+ * flushed
+*/
+PUBLIC int net_send_string(int fd, char *str, int format)
+{
+ int which, i, j;
+
+ if ((which = findConnection(fd)) < 0) {
+ return -1;
+ }
+ while (*str) {
+ for (i = 0; str[i] >= ' '; i++);
+ if (i) {
+ if (format && (i >= (j = LINE_WIDTH - con[which].outPos))) { /* word wrap */
+ i = j;
+ while (i > 0 && str[i - 1] != ' ')
+ i--;
+ while (i > 0 && str[i - 1] == ' ')
+ i--;
+ if (i == 0)
+ i = j - 1;
+ sendme(which, str, i);
+ sendme(which, "\n\r\\ ", 6);
+ con[which].outPos = 4;
+ while (str[i] == ' ') /* eat the leading spaces after we wrap */
+ i++;
+ } else {
+ sendme(which, str, i);
+ con[which].outPos += i;
+ }
+ str += i;
+ } else { /* non-printable stuff handled here */
+ switch (*str) {
+ case '\t':
+ sendme(which, " ", 8 - (con[which].outPos & 7));
+ con[which].outPos &= ~7;
+ if (con[which].outPos += 8 >= LINE_WIDTH)
+ con[which].outPos = 0;
+ break;
+ case '\n':
+ sendme(which, "\n\r", 2);
+ con[which].outPos = 0;
+ break;
+ case '\033':
+ con[which].outPos -= 3;
+ default:
+ sendme(which, str, 1);
+ }
+ str++;
+ }
+ }
+ return 0;
+}
+
+/* if we get a complete line (something terminated by \n), copy it to com
+ and return 1.
+ if we don't get a complete line, but there is no error, return 0.
+ if some error, return -1.
+ */
+PUBLIC int readline2(char *com, int who)
+{
+ unsigned char *start, *s, *d;
+ int howmany, state, fd, pending;
+
+ static unsigned char will_tm[] = {IAC, WILL, TELOPT_TM, '\0'};
+ static unsigned char will_sga[] = {IAC, WILL, TELOPT_SGA, '\0'};
+ static unsigned char ayt[] = "[Responding to AYT: Yes, I'm here.]\n";
+
+ state = con[who].state;
+ if ((state == 2) || (state > 4)) {
+ fprintf(stderr, "FICS: state screwed for con[%d], this is a bug.\n", who);
+ state = 0;
+ }
+ s = start = con[who].inBuf;
+ pending = con[who].numPending;
+ fd = con[who].fd;
+
+ howmany = recv(fd, start + pending, MAX_STRING_LENGTH - 1 - pending, 0);
+ if (howmany == 0) /* error: they've disconnected */
+ return (-1);
+ else if (howmany == -1) {
+ if (errno != EWOULDBLOCK) { /* some other error */
+ return (-1);
+ } else if (con[who].processed) { /* nothing new and nothing old */
+ return (0);
+ } else { /* nothing new, but some unprocessed old */
+ howmany = 0;
+ }
+ }
+ if (con[who].processed)
+ s += pending;
+ else
+ howmany += pending;
+ d = s;
+
+ for (; howmany-- > 0; s++) {
+ switch (state) {
+ case 0: /* Haven't skipped over any control chars or
+ telnet commands */
+ if (*s == IAC) {
+ d = s;
+ state = 1;
+ } else if (*s == '\n') {
+ *s = '\0';
+ strcpy(com, start);
+ if (howmany)
+ bcopy(s + 1, start, howmany);
+ con[who].state = 0;
+ con[who].numPending = howmany;
+ con[who].processed = 0;
+ con[who].outPos = 0;
+ return (1);
+ } else if ((*s > (0xff - 0x20)) || (*s < 0x20)) {
+ d = s;
+ state = 2;
+ }
+ break;
+ case 1: /* got telnet IAC */
+ if (*s == IP)
+ return (-1); /* ^C = logout */
+ else if (*s == DO)
+ state = 4;
+ else if ((*s == WILL) || (*s == DONT) || (*s == WONT))
+ state = 3; /* this is cheesy, but we aren't using em */
+ else if (*s == AYT) {
+ send(fd, (char *) ayt, strlen((char *) ayt), 0);
+ state = 2;
+ } else if (*s == EL) { /* erase line */
+ d = start;
+ state = 2;
+ } else /* dunno what it is, so ignore it */
+ state = 2;
+ break;
+ case 2: /* we've skipped over something, need to
+ shuffle processed chars down */
+ if (*s == IAC)
+ state = 1;
+ else if (*s == '\n') {
+ *d = '\0';
+ strcpy(com, start);
+ if (howmany)
+ memmove(start, s + 1, howmany);
+ con[who].state = 0;
+ con[who].numPending = howmany;
+ con[who].processed = 0;
+ con[who].outPos = 0;
+ return (1);
+ } else if (*s >= ' ')
+ *(d++) = *s;
+ break;
+ case 3: /* some telnet junk we're ignoring */
+ state = 2;
+ break;
+ case 4: /* got IAC DO */
+ if (*s == TELOPT_TM)
+ send(fd, (char *) will_tm, strlen((char *) will_tm), 0);
+ else if (*s == TELOPT_SGA)
+ send(fd, (char *) will_sga, strlen((char *) will_sga), 0);
+ state = 2;
+ break;
+ }
+ }
+ if (state == 0)
+ d = s;
+ else if (state == 2)
+ state = 0;
+ con[who].state = state;
+ con[who].numPending = d - start;
+ con[who].processed = 1;
+ if (con[who].numPending == MAX_STRING_LENGTH - 1) { /* buffer full */
+ *d = '\0';
+ strcpy(com, start);
+ con[who].state = 0;
+ con[who].numPending = 0;
+ con[who].processed = 0;
+ return (1);
+ }
+ return (0);
+}
+
+/*
+PRIVATE int readline(int who)
+{
+ int e, fd = con[who].fd;
+ char *t = con[who].inBuf;
+ int recvCount;
+ int totalCount = 0;
+ unsigned char c;
+ static unsigned char will_tm[] = {IAC, WILL, TELOPT_TM, '\0'};
+ static unsigned char will_sga[] = {IAC, WILL, TELOPT_SGA, '\0'};
+
+ t += con[who].numPending;
+
+ while ((recvCount = recv(fd, (char *) &c, 1, 0)) == 1) {
+ totalCount += recvCount;
+ if (c == IAC) {
+ recvCount = recv(fd, (char *) &c, 1, 0);
+ if (recvCount == 1) {
+ totalCount += recvCount;
+ switch (c) {
+ case IP:
+ c = '\3';
+ break;
+ case DO:
+ recvCount = recv(fd, (char *) &c, 1, 0);
+ if (recvCount == 1) {
+ totalCount += recvCount;
+ if (c == TELOPT_TM) {
+ send(fd, (char *) will_tm, strlen((char *) will_tm), 0);
+ } else if (c == TELOPT_SGA) {
+ send(fd, (char *) will_sga, strlen((char *) will_sga), 0);
+ }
+ }
+ c = '\0';
+ break;
+ case DONT:
+ recvCount = recv(fd, (char *) &c, 1, 0);
+ break;
+ c = '\0';
+ default:
+ recvCount = recv(fd, (char *) &c, 1, 0);
+ c = '\0';
+ break;
+ }
+ }
+ }
+ if (c != '\r' && c > 2) {
+ if (isprint(c) || (c == '\n')) {
+ *t++ = c;
+ con[who].numPending++;
+ }
+ }
+ if (c == '\n' || con[who].numPending >= MAX_STRING_LENGTH - 1) {
+ *--t = '\0';
+ con[who].numPending = 0;
+ con[fd].outPos = 0;
+ return 1;
+ }
+ }
+
+ *t = '\0';
+ e = ((totalCount == 0) || (errno != EWOULDBLOCK)) ? -1 : 0;
+ return (e);
+}
+*/
+
+PUBLIC int net_init(int port)
+{
+ int i;
+ int opt;
+ struct sockaddr_in serv_addr;
+ struct linger lingeropt;
+
+/* Although we have 256 descriptors to work with for opening files,
+ * we can only use 126 for sockets under SunOS 4.x.x socket libs. Using
+ * glibc can get you up to 256 again. Many OS's can do more than that!
+ * Sparky 9/20/95
+ */
+ no_file = getdtablesize();
+ if (no_file > MAX_PLAYER + 6)
+ no_file = MAX_PLAYER + 6;
+ max_connections = no_file - 6;
+ for (i = 0; i < no_file; i++) {
+ con[i].status = NETSTAT_EMPTY;
+ con[i].sndbuf = NULL;
+ con[i].sndbufsize = con[i].sndbufpos = 0;
+ }
+ /* Open a TCP socket (an Internet stream socket). */
+ if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ fprintf(stderr, "FICS: can't open stream socket\n");
+ return -1;
+ }
+ /* Bind our local address so that the client can send to us */
+ memset((char *) &serv_addr, 0, sizeof(serv_addr));
+ serv_addr.sin_family = AF_INET;
+ serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ serv_addr.sin_port = htons(port);
+
+ /** added in an attempt to allow rebinding to the port **/
+
+ opt = 1;
+ setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *) &opt, sizeof(opt));
+ opt = 1;
+ setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, (char *) &opt, sizeof(opt));
+ lingeropt.l_onoff = 0;
+ lingeropt.l_linger = 0;
+ setsockopt(sockfd, SOL_SOCKET, SO_LINGER, (char *) &lingeropt, sizeof(lingeropt));
+
+/*
+#ifdef DEBUG
+ opt = 1;
+ setsockopt(sockfd, SOL_SOCKET, SO_DEBUG, (char *)&opt, sizeof(opt));
+#endif
+*/
+
+ if (bind(sockfd, (struct sockaddr *) & serv_addr, sizeof(serv_addr)) < 0) {
+ fprintf(stderr, "FICS: can't bind local address. errno=%d\n", errno);
+ return -1;
+ }
+ opt = 1;
+ ioctl(sockfd, FIONBIO, &opt);
+ listen(sockfd, 5);
+ return 0;
+}
+
+PUBLIC void net_close(void)
+{
+ int i;
+ for (i = 0; i < no_file; i++) {
+ if (con[i].status != NETSTAT_EMPTY)
+ net_close_connection(con[i].fd);
+ }
+}
+
+PUBLIC void net_close_connection(int fd)
+{
+ if (con[fd].status == NETSTAT_CONNECTED)
+ net_flush_connection(fd);
+ if (!remConnection(fd)) {
+ if (fd > 2)
+ close(fd);
+ }
+}
+
+PUBLIC void turn_echo_on(int fd)
+{
+ static unsigned char wont_echo[] = {IAC, WONT, TELOPT_ECHO, '\0'};
+
+ send(fd, (char *) wont_echo, strlen((char *) wont_echo), 0);
+}
+
+PUBLIC void turn_echo_off(int fd)
+{
+ static unsigned char will_echo[] = {IAC, WILL, TELOPT_ECHO, '\0'};
+
+ send(fd, (char *) will_echo, strlen((char *) will_echo), 0);
+}
+
+PUBLIC unsigned int net_connected_host(int fd)
+{
+ int which;
+
+ if ((which = findConnection(fd)) < 0) {
+ fprintf(stderr, "FICS: FD not in connection table!\n");
+ return -1;
+ }
+ return con[which].fromHost;
+}
+
+PUBLIC void ngc2(char *com, int timeout)
+{
+ struct sockaddr_in cli_addr;
+ int cli_len = (int) sizeof(struct sockaddr_in);
+
+ int fd, loop, nfound, lineComplete;
+ fd_set readfds;
+ struct timeval to;
+
+ while ((fd = accept(sockfd, (struct sockaddr *) & cli_addr, &cli_len)) != -1)
+ {
+ if (net_addConnection(fd, cli_addr.sin_addr.s_addr)) {
+ fprintf(stderr, "FICS is full. fd = %d.\n", fd);
+ psend_raw_file(fd, mess_dir, MESS_FULL);
+ close(fd);
+ } else
+ process_new_connection(fd, net_connected_host(fd));
+ }
+
+ if (errno != EWOULDBLOCK)
+ fprintf(stderr, "FICS: Problem with accept(). errno=%d\n", errno);
+
+ net_flush_all_connections();
+
+ FD_ZERO(&readfds);
+ for (loop = 0; loop < no_file; loop++)
+ if (con[loop].status != NETSTAT_EMPTY)
+ FD_SET(con[loop].fd, &readfds);
+
+ to.tv_usec = 0;
+ to.tv_sec = timeout;
+ nfound = select(no_file, &readfds, NULL, NULL, &to);
+ for (loop = 0; loop < no_file; loop++) {
+ if (con[loop].status != NETSTAT_EMPTY) {
+ fd = con[loop].fd;
+ lineComplete = readline2(com, fd);
+ if (lineComplete == 0) /* partial line: do nothing with it */
+ continue;
+ if (lineComplete > 0) { /* complete line: process it */
+#ifdef TIMESEAL
+ if (!(parseInput(com, &con[loop]))) continue;
+#endif
+ if (process_input(fd, com) != COM_LOGOUT) {
+ net_flush_connection(fd);
+ continue;
+ }
+ }
+ /* Disconnect anyone who gets here */
+ process_disconnection(fd);
+ net_close_connection(fd);
+ }
+ }
+}
+
+PUBLIC int net_consize(void)
+{
+ int i, total;
+
+ total = sizeof(con);
+ for (i=0; i < no_file; i++)
+ total += con[i].sndbufsize;
+
+ return(total);
+}
diff --git a/FICS/network.c.orig b/FICS/network.c.orig
new file mode 100644
index 0000000..eed60c8
--- /dev/null
+++ b/FICS/network.c.orig
@@ -0,0 +1,657 @@
+/* network.c
+ *
+ */
+
+#include "stdinclude.h"
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/telnet.h>
+
+#include "common.h"
+#include "utils.h"
+#include "playerdb.h"
+#include "network.h"
+#include "rmalloc.h"
+#ifdef TIMESEAL
+#include "timeseal.h"
+#endif
+
+extern int errno;
+
+/* grimm */
+#if defined(SGI)
+#else
+/*
+int send(int s, char *msg, int len, int flags);
+int recv(int s, char *buf, int len, int flags);
+char *memset(char *s, int c, int n);
+int setsockopt( int s, int level, int optname, char *optval, int optlen);
+int accept(int s, struct sockaddr *addr, size_t *addrlen);
+void *memmove(void *dst0, const void *src0, register size_t length);
+int bind(int s, struct sockaddr *name, int namelen);
+int select(int width, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
+ struct timeval *timeout);
+int listen(int s, int level);
+*/
+#endif
+/* added for warning */
+
+PRIVATE int sockfd = 0; /* The socket */
+PRIVATE int numConnections = 0;
+/* Sparse array */
+PUBLIC connection con[256];
+
+PUBLIC int no_file;
+PUBLIC int max_connections;
+
+/* Index == fd, for sparse array, quick lookups! wasted memory :( */
+PUBLIC int findConnection(int fd)
+{
+ if (con[fd].status == NETSTAT_EMPTY)
+ return -1;
+ else
+ return fd;
+}
+
+PUBLIC int net_addConnection(int fd, unsigned int fromHost)
+{
+ int noblock = 1;
+
+ if (findConnection(fd) >= 0) {
+ fprintf(stderr, "FICS: FD already in connection table!\n");
+ return -1;
+ }
+ if (numConnections == max_connections)
+ return -1;
+ if (ioctl(fd, FIONBIO, &noblock) == -1) {
+ fprintf(stderr, "Error setting nonblocking mode errno=%d\n", errno);
+ }
+ con[fd].fd = fd;
+ if (fd != 0)
+ con[fd].outFd = fd;
+ else
+ con[fd].outFd = 1;
+ con[fd].fromHost = fromHost;
+ con[fd].status = NETSTAT_CONNECTED;
+#ifdef TIMESEAL
+ con[fd].user[0]='\0';
+ con[fd].sys[0]='\0';
+ con[fd].timeseal = 0;
+ con[fd].time = 0;
+#endif
+ con[fd].numPending = 0;
+ con[fd].processed = 0;
+ con[fd].outPos = 0;
+ if (con[fd].sndbuf == NULL) {
+#ifdef DEBUG
+ fprintf(stderr, "FICS: nac(%d) allocating sndbuf.\n", fd);
+#endif
+ con[fd].sndbufpos = 0;
+ con[fd].sndbufsize = MAX_STRING_LENGTH;
+ con[fd].sndbuf = rmalloc(MAX_STRING_LENGTH);
+ } else {
+#ifdef DEBUG
+ fprintf(stderr, "FICS: nac(%d) reusing old sndbuf size %d pos %d.\n", fd, con[fd].sndbufsize, con[fd].sndbufpos);
+#endif
+ }
+ con[fd].state = 0;
+ numConnections++;
+
+#ifdef DEBUG
+ fprintf(stderr, "FICS: fd: %d connections: %d descriptors: %d \n", fd, numConnections, getdtablesize()); /* sparky 3/13/95 */
+#endif
+
+ return 0;
+}
+
+PRIVATE int remConnection(int fd)
+{
+ int which;
+ if ((which = findConnection(fd)) < 0) {
+ return -1;
+ }
+ numConnections--;
+ con[fd].status = NETSTAT_EMPTY;
+ if (con[fd].sndbuf == NULL) {
+ fprintf(stderr, "FICS: remcon(%d) totally screwed, this shouldn't happen.\n", fd);
+ } else {
+ if (con[fd].sndbufsize > MAX_STRING_LENGTH) {
+ con[fd].sndbufsize = MAX_STRING_LENGTH;
+ con[fd].sndbuf = rrealloc(con[fd].sndbuf, MAX_STRING_LENGTH);
+ }
+ if (con[fd].sndbufpos) { /* didn't send everything, bummer */
+ con[fd].sndbufpos = 0;
+ }
+ }
+ return 0;
+}
+
+PRIVATE void net_flushme(int which)
+{
+ int sent;
+
+ sent = send(con[which].outFd, con[which].sndbuf, con[which].sndbufpos, 0);
+ if (sent == -1) {
+ if (errno != EPIPE) /* EPIPE = they've disconnected */
+ fprintf(stderr, "FICS: net_flushme(%d) couldn't send, errno=%d.\n", which, errno);
+ con[which].sndbufpos = 0;
+ } else {
+ con[which].sndbufpos -= sent;
+ if (con[which].sndbufpos)
+ memmove(con[which].sndbuf, con[which].sndbuf + sent, con[which].sndbufpos);
+ }
+ if (con[which].sndbufsize > MAX_STRING_LENGTH && con[which].sndbufpos < MAX_STRING_LENGTH) {
+ /* time to shrink the buffer */
+ con[which].sndbuf = rrealloc(con[which].sndbuf, MAX_STRING_LENGTH);
+ con[which].sndbufsize = MAX_STRING_LENGTH;
+ }
+}
+
+PRIVATE void net_flush_all_connections(void)
+{
+ int which;
+ fd_set writefds;
+ struct timeval to;
+
+ FD_ZERO(&writefds);
+ for (which = 0; which < max_connections; which++)
+ if (con[which].status == NETSTAT_CONNECTED && con[which].sndbufpos)
+ FD_SET(con[which].outFd, &writefds);
+
+ to.tv_usec = 0;
+ to.tv_sec = 0;
+ select(no_file, NULL, &writefds, NULL, &to);
+ for (which = 0; which < max_connections; which++) {
+ if (FD_ISSET(con[which].outFd, &writefds)) {
+ net_flushme(which);
+ }
+ }
+}
+
+PRIVATE void net_flush_connection(int fd)
+{
+ int which;
+ fd_set writefds;
+ struct timeval to;
+
+ if (((which = findConnection(fd)) >= 0) && (con[which].sndbufpos)) {
+ FD_ZERO(&writefds);
+ FD_SET(con[which].outFd, &writefds);
+ to.tv_usec = 0;
+ to.tv_sec = 0;
+ select(no_file, NULL, &writefds, NULL, &to);
+ if (FD_ISSET(con[which].outFd, &writefds)) {
+ net_flushme(which);
+ }
+ }
+ return;
+}
+
+PRIVATE int sendme(int which, char *str, int len)
+{
+ int i, count;
+ fd_set writefds;
+ struct timeval to;
+ count = len;
+
+ while ((i = ((con[which].sndbufsize - con[which].sndbufpos) < len) ? (con[which].sndbufsize - con[which].sndbufpos) : len) > 0) {
+ memmove(con[which].sndbuf + con[which].sndbufpos, str, i);
+ con[which].sndbufpos += i;
+ if (con[which].sndbufpos == con[which].sndbufsize) {
+
+ FD_ZERO(&writefds);
+ FD_SET(con[which].outFd, &writefds);
+ to.tv_usec = 0;
+ to.tv_sec = 0;
+ select(no_file, NULL, &writefds, NULL, &to);
+ if (FD_ISSET(con[which].outFd, &writefds)) {
+ net_flushme(which);
+ } else {
+ /* time to grow the buffer */
+ con[which].sndbufsize += MAX_STRING_LENGTH;
+ con[which].sndbuf = rrealloc(con[which].sndbuf, con[which].sndbufsize);
+ }
+ }
+ str += i;
+ len -= i;
+ }
+ return count;
+}
+
+/*
+ * -1 for an error other than EWOULDBLOCK.
+ * Put <lf> after every <cr> and put \ at the end of overlength lines.
+ * Doesn't send anything unless the buffer fills, output waits until
+ * flushed
+*/
+PUBLIC int net_send_string(int fd, char *str, int format)
+{
+ int which, i, j;
+
+ if ((which = findConnection(fd)) < 0) {
+ return -1;
+ }
+ while (*str) {
+ for (i = 0; str[i] >= ' '; i++);
+ if (i) {
+ if (format && (i >= (j = LINE_WIDTH - con[which].outPos))) { /* word wrap */
+ i = j;
+ while (i > 0 && str[i - 1] != ' ')
+ i--;
+ while (i > 0 && str[i - 1] == ' ')
+ i--;
+ if (i == 0)
+ i = j - 1;
+ sendme(which, str, i);
+ sendme(which, "\n\r\\ ", 6);
+ con[which].outPos = 4;
+ while (str[i] == ' ') /* eat the leading spaces after we wrap */
+ i++;
+ } else {
+ sendme(which, str, i);
+ con[which].outPos += i;
+ }
+ str += i;
+ } else { /* non-printable stuff handled here */
+ switch (*str) {
+ case '\t':
+ sendme(which, " ", 8 - (con[which].outPos & 7));
+ con[which].outPos &= ~7;
+ if (con[which].outPos += 8 >= LINE_WIDTH)
+ con[which].outPos = 0;
+ break;
+ case '\n':
+ sendme(which, "\n\r", 2);
+ con[which].outPos = 0;
+ break;
+ case '\033':
+ con[which].outPos -= 3;
+ default:
+ sendme(which, str, 1);
+ }
+ str++;
+ }
+ }
+ return 0;
+}
+
+/* if we get a complete line (something terminated by \n), copy it to com
+ and return 1.
+ if we don't get a complete line, but there is no error, return 0.
+ if some error, return -1.
+ */
+PUBLIC int readline2(char *com, int who)
+{
+ unsigned char *start, *s, *d;
+ int howmany, state, fd, pending;
+
+ static unsigned char will_tm[] = {IAC, WILL, TELOPT_TM, '\0'};
+ static unsigned char will_sga[] = {IAC, WILL, TELOPT_SGA, '\0'};
+ static unsigned char ayt[] = "[Responding to AYT: Yes, I'm here.]\n";
+
+ state = con[who].state;
+ if ((state == 2) || (state > 4)) {
+ fprintf(stderr, "FICS: state screwed for con[%d], this is a bug.\n", who);
+ state = 0;
+ }
+ s = start = con[who].inBuf;
+ pending = con[who].numPending;
+ fd = con[who].fd;
+
+ howmany = recv(fd, start + pending, MAX_STRING_LENGTH - 1 - pending, 0);
+ if (howmany == 0) /* error: they've disconnected */
+ return (-1);
+ else if (howmany == -1) {
+ if (errno != EWOULDBLOCK) { /* some other error */
+ return (-1);
+ } else if (con[who].processed) { /* nothing new and nothing old */
+ return (0);
+ } else { /* nothing new, but some unprocessed old */
+ howmany = 0;
+ }
+ }
+ if (con[who].processed)
+ s += pending;
+ else
+ howmany += pending;
+ d = s;
+
+ for (; howmany-- > 0; s++) {
+ switch (state) {
+ case 0: /* Haven't skipped over any control chars or
+ telnet commands */
+ if (*s == IAC) {
+ d = s;
+ state = 1;
+ } else if (*s == '\n') {
+ *s = '\0';
+ strcpy(com, start);
+ if (howmany)
+ bcopy(s + 1, start, howmany);
+ con[who].state = 0;
+ con[who].numPending = howmany;
+ con[who].processed = 0;
+ con[who].outPos = 0;
+ return (1);
+ } else if ((*s > (0xff - 0x20)) || (*s < 0x20)) {
+ d = s;
+ state = 2;
+ }
+ break;
+ case 1: /* got telnet IAC */
+ if (*s == IP)
+ return (-1); /* ^C = logout */
+ else if (*s == DO)
+ state = 4;
+ else if ((*s == WILL) || (*s == DONT) || (*s == WONT))
+ state = 3; /* this is cheesy, but we aren't using em */
+ else if (*s == AYT) {
+ send(fd, (char *) ayt, strlen((char *) ayt), 0);
+ state = 2;
+ } else if (*s == EL) { /* erase line */
+ d = start;
+ state = 2;
+ } else /* dunno what it is, so ignore it */
+ state = 2;
+ break;
+ case 2: /* we've skipped over something, need to
+ shuffle processed chars down */
+ if (*s == IAC)
+ state = 1;
+ else if (*s == '\n') {
+ *d = '\0';
+ strcpy(com, start);
+ if (howmany)
+ memmove(start, s + 1, howmany);
+ con[who].state = 0;
+ con[who].numPending = howmany;
+ con[who].processed = 0;
+ con[who].outPos = 0;
+ return (1);
+ } else if (*s >= ' ')
+ *(d++) = *s;
+ break;
+ case 3: /* some telnet junk we're ignoring */
+ state = 2;
+ break;
+ case 4: /* got IAC DO */
+ if (*s == TELOPT_TM)
+ send(fd, (char *) will_tm, strlen((char *) will_tm), 0);
+ else if (*s == TELOPT_SGA)
+ send(fd, (char *) will_sga, strlen((char *) will_sga), 0);
+ state = 2;
+ break;
+ }
+ }
+ if (state == 0)
+ d = s;
+ else if (state == 2)
+ state = 0;
+ con[who].state = state;
+ con[who].numPending = d - start;
+ con[who].processed = 1;
+ if (con[who].numPending == MAX_STRING_LENGTH - 1) { /* buffer full */
+ *d = '\0';
+ strcpy(com, start);
+ con[who].state = 0;
+ con[who].numPending = 0;
+ con[who].processed = 0;
+ return (1);
+ }
+ return (0);
+}
+
+/*
+PRIVATE int readline(int who)
+{
+ int e, fd = con[who].fd;
+ char *t = con[who].inBuf;
+ int recvCount;
+ int totalCount = 0;
+ unsigned char c;
+ static unsigned char will_tm[] = {IAC, WILL, TELOPT_TM, '\0'};
+ static unsigned char will_sga[] = {IAC, WILL, TELOPT_SGA, '\0'};
+
+ t += con[who].numPending;
+
+ while ((recvCount = recv(fd, (char *) &c, 1, 0)) == 1) {
+ totalCount += recvCount;
+ if (c == IAC) {
+ recvCount = recv(fd, (char *) &c, 1, 0);
+ if (recvCount == 1) {
+ totalCount += recvCount;
+ switch (c) {
+ case IP:
+ c = '\3';
+ break;
+ case DO:
+ recvCount = recv(fd, (char *) &c, 1, 0);
+ if (recvCount == 1) {
+ totalCount += recvCount;
+ if (c == TELOPT_TM) {
+ send(fd, (char *) will_tm, strlen((char *) will_tm), 0);
+ } else if (c == TELOPT_SGA) {
+ send(fd, (char *) will_sga, strlen((char *) will_sga), 0);
+ }
+ }
+ c = '\0';
+ break;
+ case DONT:
+ recvCount = recv(fd, (char *) &c, 1, 0);
+ break;
+ c = '\0';
+ default:
+ recvCount = recv(fd, (char *) &c, 1, 0);
+ c = '\0';
+ break;
+ }
+ }
+ }
+ if (c != '\r' && c > 2) {
+ if (isprint(c) || (c == '\n')) {
+ *t++ = c;
+ con[who].numPending++;
+ }
+ }
+ if (c == '\n' || con[who].numPending >= MAX_STRING_LENGTH - 1) {
+ *--t = '\0';
+ con[who].numPending = 0;
+ con[fd].outPos = 0;
+ return 1;
+ }
+ }
+
+ *t = '\0';
+ e = ((totalCount == 0) || (errno != EWOULDBLOCK)) ? -1 : 0;
+ return (e);
+}
+*/
+
+PUBLIC int net_init(int port)
+{
+ int i;
+ int opt;
+ struct sockaddr_in serv_addr;
+ struct linger lingeropt;
+
+ no_file = getdtablesize();
+/* Although we have 256 descriptors to work with for opening sockets,
+ * we can only use 126 maximum of them for some reason... built in
+ * limitation of the socket libs? Sparky 3/13/95
+ */
+ if (no_file > MAX_PLAYER + 10)
+ no_file = MAX_PLAYER + 10;
+ max_connections = no_file - 10;
+ for (i = 0; i < no_file; i++) {
+ con[i].status = NETSTAT_EMPTY;
+ con[i].sndbuf = NULL;
+ con[i].sndbufsize = con[i].sndbufpos = 0;
+ }
+ /* Open a TCP socket (an Internet stream socket). */
+ if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ fprintf(stderr, "FICS: can't open stream socket\n");
+ return -1;
+ }
+ /* Bind our local address so that the client can send to us */
+ memset((char *) &serv_addr, 0, sizeof(serv_addr));
+ serv_addr.sin_family = AF_INET;
+ serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ serv_addr.sin_port = htons(port);
+
+ /** added in an attempt to allow rebinding to the port **/
+
+ opt = 1;
+ setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *) &opt, sizeof(opt));
+ opt = 1;
+ setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, (char *) &opt, sizeof(opt));
+ lingeropt.l_onoff = 0;
+ lingeropt.l_linger = 0;
+ setsockopt(sockfd, SOL_SOCKET, SO_LINGER, (char *) &lingeropt, sizeof(lingeropt));
+
+/*
+#ifdef DEBUG
+ opt = 1;
+ setsockopt(sockfd, SOL_SOCKET, SO_DEBUG, (char *)&opt, sizeof(opt));
+#endif
+*/
+
+ if (bind(sockfd, (struct sockaddr *) & serv_addr, sizeof(serv_addr)) < 0) {
+ fprintf(stderr, "FICS: can't bind local address. errno=%d\n", errno);
+ return -1;
+ }
+ opt = 1;
+ ioctl(sockfd, FIONBIO, &opt);
+ listen(sockfd, 5);
+ return 0;
+}
+
+PUBLIC void net_close(void)
+{
+ int i;
+ for (i = 0; i < no_file; i++) {
+ if (con[i].status != NETSTAT_EMPTY)
+ net_close_connection(con[i].fd);
+ }
+}
+
+PUBLIC void net_close_connection(int fd)
+{
+ if (con[fd].status == NETSTAT_CONNECTED)
+ net_flush_connection(fd);
+ if (!remConnection(fd)) {
+ if (fd > 2)
+ close(fd);
+ }
+}
+
+PUBLIC void turn_echo_on(int fd)
+{
+ static unsigned char wont_echo[] = {IAC, WONT, TELOPT_ECHO, '\0'};
+
+ send(fd, (char *) wont_echo, strlen((char *) wont_echo), 0);
+}
+
+PUBLIC void turn_echo_off(int fd)
+{
+ static unsigned char will_echo[] = {IAC, WILL, TELOPT_ECHO, '\0'};
+
+ send(fd, (char *) will_echo, strlen((char *) will_echo), 0);
+}
+
+PUBLIC unsigned int net_connected_host(int fd)
+{
+ int which;
+
+ if ((which = findConnection(fd)) < 0) {
+ fprintf(stderr, "FICS: FD not in connection table!\n");
+ return -1;
+ }
+ return con[which].fromHost;
+}
+
+PUBLIC void ngc2(char *com, int timeout)
+{
+ struct sockaddr_in cli_addr;
+ size_t cli_len = sizeof(struct sockaddr_in);
+
+ int fd, loop, nfound, lineComplete;
+ fd_set readfds;
+ struct timeval to;
+
+ while ((fd = accept(sockfd, (struct sockaddr *) & cli_addr, &cli_len)) != -1) {
+ if (net_addConnection(fd, cli_addr.sin_addr.s_addr)) {
+ char *woo =
+"FICS is currently full. Please try one of the following sites:\n\r\n\r"
+"E-FICS: krypton.daimi.aau.dk 5000 (130.225.18.157 5000) Main EURO server\n\r"
+"D-FICS: dds.hacktic.nl 5000 (193.78.33.69 5000) Dutch server\n\r"
+"B-FICS: holly.csv.warwick.ac.uk 5000 (137.205.192.12 5000) British server\n\r"
+"?-FICS: chess.unix-ag.uni-kl.de 5000 (131.246.89.3 5000) German server\n\r"
+"M-FICS: wisdom.weizmann.ac.il 5000 (132.76.80.77 5000) Mid-east server\n\r\n\r";
+ fprintf(stderr, "FICS is full. fd = %d\n");
+ write (fd, woo, strlen(woo));
+ sleep(1);
+ close(fd);
+ } else {
+/* int opt, optlen;
+
+ opt = 1024;
+ setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *)&opt, sizeof(opt));
+ opt = 8192;
+ setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *) &opt, sizeof(opt));
+ optlen = sizeof(opt);
+ getsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *)&opt, &optlen);
+ fprintf(stderr, "%d sndbuf: %d", fd, opt);
+ optlen = sizeof(opt);
+ getsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *)&opt, &optlen);
+ fprintf(stderr, " rcvbuf: %d\n", opt);
+*/
+ process_new_connection(fd, net_connected_host(fd));
+ }
+ }
+ if (errno != EWOULDBLOCK)
+ fprintf(stderr, "FICS: Problem with accept(). errno=%d\n", errno);
+
+ net_flush_all_connections();
+
+ FD_ZERO(&readfds);
+ for (loop = 0; loop < no_file; loop++)
+ if (con[loop].status != NETSTAT_EMPTY)
+ FD_SET(con[loop].fd, &readfds);
+
+ to.tv_usec = 0;
+ to.tv_sec = timeout;
+ nfound = select(no_file, &readfds, NULL, NULL, &to);
+ for (loop = 0; loop < no_file; loop++) {
+ if (con[loop].status != NETSTAT_EMPTY) {
+ fd = con[loop].fd;
+ lineComplete = readline2(com, fd);
+ if (lineComplete == 0) /* partial line: do nothing with it */
+ continue;
+ if (lineComplete > 0) { /* complete line: process it */
+#ifdef TIMESEAL
+ if (!(parseInput(com, &con[loop]))) continue;
+#endif
+ if (process_input(fd, com) != COM_LOGOUT) {
+ net_flush_connection(fd);
+ continue;
+ }
+ }
+ /* Disconnect anyone who gets here */
+ process_disconnection(fd);
+ net_close_connection(fd);
+ }
+ }
+}
+
+PUBLIC int net_consize(void)
+{
+ int i, total;
+
+ total = sizeof(con);
+ for (i=0; i < no_file; i++)
+ total += con[i].sndbufsize;
+
+ return(total);
+}
diff --git a/FICS/network.h b/FICS/network.h
new file mode 100644
index 0000000..7ff2a22
--- /dev/null
+++ b/FICS/network.h
@@ -0,0 +1,89 @@
+/* network.h
+ *
+ */
+
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name email yy/mm/dd Change
+ Richard Nash 93/10/22 Created
+*/
+
+#ifndef _NETWORK_H
+#define _NETWORK_H
+
+#include "command.h" /* For MAX_STRING_LENGTH */
+
+#define NET_NETERROR 0
+#define NET_NEW 1
+#define NET_DISCONNECT 2
+#define NET_READLINE 3
+#define NET_TIMEOUT 4
+#define NET_NOTCOMPLETE 5
+
+#define LINE_WIDTH 80
+
+#ifndef O_NONBLOCK
+#define O_NONBLOCK 00004
+#endif
+
+#define NETSTAT_EMPTY 0
+#define NETSTAT_CONNECTED 1
+#define NETSTAT_IDENT 2
+
+typedef struct _connection {
+ int fd;
+ int outFd;
+ unsigned int fromHost;
+ int status;
+#ifdef TIMESEAL
+ char user[512];
+ char sys[512];
+ int timeseal;
+ int time;
+#endif
+/* Input buffering */
+ int numPending;
+ int processed;
+ unsigned char inBuf[MAX_STRING_LENGTH];
+/* Output buffering */
+ int sndbufsize; /* size of send buffer (this changes) */
+ int sndbufpos; /* position in send buffer */
+ char *sndbuf; /* our send buffer, or NULL if none yet */
+ int outPos; /* column count */
+ int state; /* 'telnet state' */
+/* identd stuff */
+ char ident[20];
+ int mypal;
+} connection;
+
+extern int no_file;
+extern int max_connections;
+extern connection con[512];
+
+extern int findConnection();
+extern int net_init(int);
+extern void net_close(void);
+extern void ngc2(char *, int);
+extern void net_close_connection(int);
+extern int net_send_string(int, char *, int);
+extern void turn_echo_on(int);
+extern void turn_echo_off(int);
+extern unsigned int net_connected_host(int);
+extern int net_addConnection(int, unsigned int);
+extern int net_consize(void);
+extern int readline2(char *, int);
+#endif /* _NETWORK_H */
diff --git a/FICS/obsproc.c b/FICS/obsproc.c
new file mode 100644
index 0000000..e24a116
--- /dev/null
+++ b/FICS/obsproc.c
@@ -0,0 +1,1528 @@
+/* obsproc.c
+ *
+ */
+
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name email yy/mm/dd Change
+ Dave Herscovici 95/11/26 Created
+*/
+
+#include "stdinclude.h"
+
+#include "common.h"
+#include "command.h"
+#include "ficsmain.h"
+#include "config.h"
+#include "playerdb.h"
+#include "gamedb.h"
+#include "gameproc.h"
+#include "obsproc.h"
+#include "movecheck.h"
+#include "utils.h"
+#include "ratings.h"
+#include "rmalloc.h"
+#include "comproc.h"
+#include "matchproc.h"
+#include "formula.h"
+#include "eco.h"
+#include "network.h"
+
+PUBLIC int GameNumFromParam(int p, int *p1, parameter *param)
+{
+ if (param->type == TYPE_WORD) {
+ *p1 = player_find_part_login(param->val.word);
+ if (*p1 < 0) {
+ pprintf(p, "No user named \"%s\" is logged in.\n", param->val.word);
+ return -1;
+ }
+ if (parray[*p1].game < 0)
+ pprintf(p, "%s is not playing a game.\n", parray[*p1].name);
+ return parray[*p1].game;
+ } else { /* Must be an integer */
+ *p1 = -1;
+ if (param->val.integer <= 0)
+ pprintf(p, "%d is not a valid game number.\n", param->val.integer);
+ return param->val.integer - 1;
+ }
+}
+
+PRIVATE int gamesortfunc(const void *i, const void *j)
+{
+/* examine mode games moved to top of "games" output */
+ return (GetRating(&parray[garray[*(int *) i].white], garray[*(int *) i].type) +
+ GetRating(&parray[garray[*(int *) i].black], garray[*(int *) i].type) -
+ ((garray[*(int *) i].status == GAME_EXAMINE) ? 10000 : 0) -
+ GetRating(&parray[garray[*(int *) j].white], garray[*(int *) j].type) -
+ GetRating(&parray[garray[*(int *) j].black], garray[*(int *) j].type) +
+ ((garray[*(int *) j].status == GAME_EXAMINE) ? 10000 : 0));
+}
+
+
+PUBLIC int com_games(int p, param_list param)
+{
+ int i, j;
+ int wp, bp;
+ int ws, bs;
+ int selected = 0;
+ int count = 0;
+ int totalcount;
+ char *s = NULL;
+ int slen = 0;
+ int *sortedgames; /* for qsort */
+
+ totalcount = game_count();
+ if (totalcount == 0) {
+ pprintf(p, "There are no games in progress.\n");
+ } else {
+ sortedgames = rmalloc(totalcount * sizeof(int)); /* for qsort */
+
+ if (param[0].type == TYPE_WORD) {
+ s = param[0].val.word;
+ slen = strlen(s);
+ selected = atoi(s);
+ if (selected < 0)
+ selected = 0;
+ }
+ for (i = 0; i < g_num; i++) {
+ if ((garray[i].status != GAME_ACTIVE) && (garray[i].status != GAME_EXAMINE))
+ continue;
+ if ((selected) && (selected != i + 1))
+ continue; /* not selected game number */
+ wp = garray[i].white;
+ bp = garray[i].black;
+ if ((!selected) && s && strncasecmp(s, garray[i].white_name, slen) &&
+ strncasecmp(s, garray[i].black_name, slen))
+ continue; /* player names did not match */
+ sortedgames[count++] = i;
+ }
+ if (!count)
+ pprintf(p, "No matching games were found (of %d in progress).\n", totalcount);
+ else {
+ qsort(sortedgames, count, sizeof(int), gamesortfunc);
+ pprintf(p, "\n");
+ for (j = 0; j < count; j++) {
+ i = sortedgames[j];
+ wp = garray[i].white;
+ bp = garray[i].black;
+ board_calc_strength(&garray[i].game_state, &ws, &bs);
+ if (garray[i].status != GAME_EXAMINE) {
+ pprintf_noformat(p, "%2d %4s %-11.11s %4s %-10.10s [%c%c%c%3d %3d] ",
+ i + 1,
+ ratstrii(GetRating(&parray[wp],
+ garray[i].type),
+ parray[wp].registered),
+ parray[wp].name,
+ ratstrii(GetRating(&parray[bp],
+ garray[i].type),
+ parray[bp].registered),
+ parray[bp].name,
+ (garray[i].private) ? 'p' : ' ',
+ *bstr[garray[i].type],
+ *rstr[garray[i].rated],
+ garray[i].wInitTime / 600,
+ garray[i].wIncrement / 10);
+ game_update_time(i);
+ pprintf_noformat(p, "%6s -",
+ tenth_str((garray[i].wTime > 0 ? garray[i].wTime : 0), 0));
+ pprintf_noformat(p, "%6s (%2d-%2d) %c: %2d\n",
+ tenth_str((garray[i].bTime > 0 ? garray[i].bTime : 0), 0),
+ ws, bs,
+ (garray[i].game_state.onMove == WHITE) ? 'W' : 'B',
+ garray[i].game_state.moveNum);
+ } else {
+ pprintf_noformat(p, "%2d (Exam. %4d %-11.11s %4d %-10.10s) [%c%c%c%3d %3d] ",
+ i + 1,
+ garray[i].white_rating,
+ garray[i].white_name,
+ garray[i].black_rating,
+ garray[i].black_name,
+ (garray[i].private) ? 'p' : ' ',
+ *bstr[garray[i].type],
+ *rstr[garray[i].rated],
+ garray[i].wInitTime / 600,
+ garray[i].wIncrement / 10);
+ pprintf_noformat(p, "%c: %2d\n",
+ (garray[i].game_state.onMove == WHITE) ? 'W' : 'B',
+ garray[i].game_state.moveNum);
+ }
+ }
+ if (count < totalcount)
+ pprintf(p, "\n %d game%s displayed (of %d in progress).\n", count,
+ (count == 1) ? "" : "s", totalcount);
+ else
+ pprintf(p, "\n %d game%s displayed.\n", totalcount, (totalcount == 1) ? "" : "s");
+ }
+ rfree(sortedgames);
+ }
+ return COM_OK;
+}
+
+PRIVATE int do_observe(int p, int obgame)
+{
+ if ((garray[obgame].private) && (parray[p].adminLevel < ADMIN_ADMIN)) {
+ pprintf(p, "Sorry, game %d is a private game.\n", obgame + 1);
+ return COM_OK;
+ }
+ if ((garray[obgame].white == p) || (garray[obgame].black == p)) {
+ if (garray[obgame].status != GAME_EXAMINE) {
+ pprintf(p, "You cannot observe a game that you are playing.\n");
+ return COM_OK;
+ }
+ }
+ if (player_is_observe(p, obgame)) {
+ pprintf(p, "Removing game %d from observation list.\n", obgame + 1);
+ player_remove_observe(p, obgame);
+ } else {
+ if (!player_add_observe(p, obgame)) {
+ pprintf(p, "You are now observing game %d.\n", obgame + 1);
+ send_board_to(obgame, p);
+ } else {
+ pprintf(p, "You are already observing the maximum number of games.\n");
+ }
+ }
+ return COM_OK;
+}
+
+PUBLIC void unobserveAll(int p)
+{
+ int i;
+
+ for (i = 0; i < parray[p].num_observe; i++) {
+ pprintf(p, "Removing game %d from observation list.\n", parray[p].observe_list[i] + 1);
+ }
+ parray[p].num_observe = 0;
+ return;
+}
+
+PUBLIC int com_unobserve(int p, param_list param)
+{
+ int gNum, p1;
+
+ if (param[0].type == TYPE_NULL) {
+ unobserveAll(p);
+ return COM_OK;
+ }
+ gNum = GameNumFromParam(p, &p1, &param[0]);
+ if (gNum < 0)
+ return COM_OK;
+ if (!player_is_observe(p, gNum)) {
+ pprintf(p, "You are not observing game %d.\n", gNum);
+ } else {
+ player_remove_observe(p, gNum);
+ pprintf(p, "Removing game %d from observation list.\n", gNum + 1);
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_observe(int p, param_list param)
+{
+ int i;
+ int p1, obgame;
+
+ if ((parray[p].game >=0) &&(garray[parray[p].game].status == GAME_EXAMINE)) {
+ pprintf(p, "You are still examining a game.\n");
+ return COM_OK;
+ }
+ if (param[0].type == TYPE_NULL) {
+ unobserveAll(p);
+ return COM_OK;
+ }
+ obgame = GameNumFromParam(p, &p1, &param[0]);
+ if (obgame < 0)
+ return COM_OK;
+
+ if ((obgame >= g_num) || ((garray[obgame].status != GAME_ACTIVE) &&
+ (garray[obgame].status != GAME_EXAMINE))) {
+ pprintf(p, "There is no such game.\n");
+ return COM_OK;
+ }
+ if ((p1 >= 0) && parray[p1].simul_info.numBoards) {
+ for (i = 0; i < parray[p1].simul_info.numBoards; i++)
+ if (parray[p1].simul_info.boards[i] >= 0)
+ do_observe(p, parray[p1].simul_info.boards[i]);
+ } else {
+ do_observe(p, obgame);
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_allobservers(int p, param_list param)
+{
+ int obgame;
+ int p1;
+ int start, end;
+ int g;
+ int first;
+
+ if (param[0].type == TYPE_NULL) {
+ obgame = -1;
+ } else {
+ obgame = GameNumFromParam(p, &p1, &param[0]);
+ if (obgame < 0)
+ return COM_OK;
+ }
+ if (obgame == -1) {
+ start = 0;
+ end = g_num;
+ } else if ((obgame >= g_num) || ((obgame < g_num)
+ && ((garray[obgame].status != GAME_ACTIVE)
+ && (garray[obgame].status != GAME_EXAMINE)))) {
+ pprintf(p, "There is no such game.\n");
+ return COM_OK;
+ } else {
+ start = obgame;
+ end = obgame + 1;
+ }
+
+ /* list games being played */
+
+ for (g = start; g < end; g++) {
+ if ((garray[g].status == GAME_ACTIVE) &&
+ ((parray[p].adminLevel > 0) || (garray[g].private == 0))) {
+ for (first = 1, p1 = 0; p1 < p_num; p1++) {
+ if ((parray[p1].status != PLAYER_EMPTY) && (player_is_observe(p1, g))) {
+ if (first) {
+ pprintf(p, "Observing %2d [%s vs. %s]:",
+ g + 1,
+ parray[garray[g].white].name,
+ parray[garray[g].black].name);
+ first = 0;
+ }
+ pprintf(p, " %s%s", (parray[p1].game >=0) ? "#" : "", parray[p1].name);
+ }
+ }
+ if (!first)
+ pprintf(p, "\n");
+ }
+ }
+
+ /* list games being examined last */
+
+ for (g = start; g < end; g++) {
+ if ((garray[g].status == GAME_EXAMINE) &&
+ ((parray[p].adminLevel > 0) || (garray[g].private == 0))) {
+ for (first = 1, p1 = 0; p1 < p_num; p1++) {
+ if ((parray[p1].status != PLAYER_EMPTY) && (player_is_observe(p1, g) ||
+ (parray[p1].game == g))) {
+ if (first) {
+ if (strcmp(garray[g].white_name, garray[g].black_name)) {
+ pprintf(p, "Examining %2d [%s vs %s]:", g + 1,
+ garray[g].white_name, garray[g].black_name);
+ } else {
+ pprintf(p, "Examining %2d (scratch):", g + 1);
+ }
+ first = 0;
+ }
+ pprintf(p, " %s%s", (parray[p1].game == g) ? "#" : "", parray[p1].name);
+ }
+ }
+ if (!first)
+ pprintf(p, "\n");
+ }
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_unexamine(int p, param_list param)
+{
+ int g, p1, flag = 0;
+
+ if ((parray[p].game <0) ||(garray[parray[p].game].status != GAME_EXAMINE)) {
+ pprintf(p, "You are not examining any games.\n");
+ return COM_OK;
+ }
+ g = parray[p].game;
+ parray[p].game = -1;
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (parray[p1].status != PLAYER_PROMPT)
+ continue;
+ if ((parray[p1].game == g) &&(p != p1)) {
+ /* ok - there are other examiners to take over the game */
+ flag = 1;
+ }
+ if ((player_is_observe(p1, g)) || (parray[p1].game == g)) {
+ pprintf(p1, "%s stopped examining game %d.\n", parray[p].name, g + 1);
+ }
+ }
+ if (!flag) {
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (parray[p1].status != PLAYER_PROMPT)
+ continue;
+ if (player_is_observe(p1, g)) {
+ pprintf(p1, "There are no examiners.\n");
+ pcommand(p1, "unobserve %d", g + 1);
+ }
+ }
+ game_remove(g);
+ }
+ pprintf(p, "You are no longer examining game %d.\n", g + 1);
+ return COM_OK;
+}
+
+PUBLIC int com_mexamine(int p, param_list param)
+{
+ int g, p1, p2;
+
+ if ((parray[p].game <0) ||(garray[parray[p].game].status != GAME_EXAMINE)) {
+ pprintf(p, "You are not examining any games.\n");
+ return COM_OK;
+ }
+ p1 = player_find_part_login(param[0].val.word);
+ if (p1 < 0) {
+ pprintf(p, "No user named \"%s\" is logged in.\n", param[0].val.word);
+ return COM_OK;
+ }
+ g = parray[p].game;
+ if (!player_is_observe(p1, g)) {
+ pprintf(p, "%s must observe the game you are analysing.\n", parray[p1].name);
+ return COM_OK;
+ } else {
+ if (parray[p1].game >=0) {
+ pprintf(p, "%s is already analysing the game.\n", parray[p1].name);
+ return COM_OK;
+ }
+ /* if we get here - let's make him examiner of the game */
+ unobserveAll(p1); /* fix for Xboard */
+ player_decline_offers(p1, -1, PEND_MATCH);
+ player_withdraw_offers(p1, -1, PEND_MATCH);
+ player_withdraw_offers(p1, -1, PEND_SIMUL);
+
+ parray[p1].game = g; /* yep - it really is that easy :-) */
+ pprintf(p1, "You are now examiner of game %d.\n", g + 1);
+ send_board_to(g, p1); /* pos not changed - but fixes Xboard */
+ for (p2 = 0; p2 < p_num; p2++) {
+ if (parray[p2].status != PLAYER_PROMPT)
+ continue;
+ if (p2 == p1)
+ continue;
+ if ((player_is_observe(p2, g)) || (parray[p2].game == g)) {
+ pprintf_prompt(p2, "%s is now examiner of game %d.\n", parray[p1].name, g + 1);
+ }
+ }
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_moves(int p, param_list param)
+{
+ int g;
+ int p1;
+
+ if (param[0].type == TYPE_NULL) {
+ if (parray[p].game >=0) {
+ g = parray[p].game;
+ } else if (parray[p].num_observe) {
+ for (g = 0; g < parray[p].num_observe; g++) {
+ pprintf(p, "%s\n", movesToString(parray[p].observe_list[g], 0));
+ }
+ return COM_OK;
+ } else {
+ pprintf(p, "You are neither playing, observing nor examining a game.\n");
+ return COM_OK;
+ }
+ } else {
+ g = GameNumFromParam(p, &p1, &param[0]);
+ if (g < 0)
+ return COM_OK;
+ }
+ if ((g < 0) || (g >= g_num) || ((garray[g].status != GAME_ACTIVE) &&
+ (garray[g].status != GAME_EXAMINE))) {
+ pprintf(p, "There is no such game.\n");
+ return COM_OK;
+ }
+ if ((garray[g].white != p) && (garray[g].black != p) && (garray[g].private) && (parray[p].adminLevel < ADMIN_ADMIN)) {
+ pprintf(p, "Sorry, that is a private game.\n");
+ return COM_OK;
+ }
+ pprintf(p, "%s\n", movesToString(g, 0)); /* pgn may break interfaces? */
+ return COM_OK;
+}
+
+PUBLIC int com_mailmoves(int p, param_list param)
+{
+ int g;
+ int p1;
+ char subj[81];
+
+ if (!parray[p].registered) {
+ pprintf (p,"Unregistered players cannot use mailmoves.\n");
+ return COM_OK;
+ }
+
+ if (param[0].type == TYPE_NULL) {
+ if (parray[p].game >=0) {
+ g = parray[p].game;
+ } else {
+ pprintf(p, "You are neither playing, observing nor examining a game.\n");
+ return COM_OK;
+ }
+ } else {
+ g = GameNumFromParam(p, &p1, &param[0]);
+ if (g < 0)
+ return COM_OK;
+ }
+ if ((g < 0) || (g >= g_num) || ((garray[g].status != GAME_ACTIVE) && (garray[g].status != GAME_EXAMINE))) {
+ pprintf(p, "There is no such game.\n");
+ return COM_OK;
+ }
+ if ((garray[g].white != p) && (garray[g].black != p) && (garray[g].private) && (parray[p].adminLevel < ADMIN_ADMIN)) {
+ pprintf(p, "Sorry, that is a private game.\n");
+ return COM_OK;
+ }
+ sprintf(subj, "FICS game report %s vs %s", garray[g].white_name, garray[g].black_name);
+ if (mail_string_to_user(p, subj, movesToString(g, parray[p].pgn))) {
+ pprintf(p, "Moves NOT mailed, perhaps your address is incorrect.\n");
+ } else {
+ pprintf(p, "Moves mailed.\n");
+ }
+ return COM_OK;
+}
+
+PRIVATE int old_mail_moves(int p,int mail, param_list param)
+{
+ int p1, connected;
+ int count;
+ FILE *fp;
+ char fname[MAX_FILENAME_SIZE];
+ char tmp[2048];
+ char *ptmp = tmp;
+
+ if (mail && (!parray[p].registered)) {
+ pprintf (p,"Unregistered players cannot use mailoldmoves.\n");
+ return COM_OK;
+ }
+
+ if (param[0].type == TYPE_WORD) {
+ if (!FindPlayer(p, param[0].val.word, &p1, &connected))
+ return COM_OK;
+ } else {
+ p1 = p;
+ connected = 1;
+ }
+
+ sprintf(fname, "%s/player_data/%c/%s.%s", stats_dir,
+ parray[p1].login[0], parray[p1].login, STATS_GAMES);
+ fp = fopen(fname, "r"); /* old moves now looks in history to save mem - DAV */
+
+ if (!fp) {
+ pprintf (p,"There is no old game for %s.\n", parray[p1].name);
+ if (!connected)
+ player_remove(p1);
+ return COM_OK;
+ }
+
+ while (!feof(fp))
+ fgets(tmp, 1024, fp);
+ sscanf(ptmp, "%d", &count);
+ fclose(fp); /* find the last game played in history */
+
+ pprintf (p,"Last game for %s was history game %d.\n",parray[p1].name,count);
+
+ if (mail)
+ pcommand (p,"mailstored %s %d",parray[p1].name,count);
+ else
+ pcommand (p,"smoves %s %d",parray[p1].name,count);
+
+ if (!connected)
+ player_remove(p1);
+
+ return COM_OK;
+}
+
+PUBLIC int com_oldmoves(int p, param_list param)
+{
+ return old_mail_moves(p , 0, param);
+}
+
+PUBLIC int com_mailoldmoves(int p, param_list param)
+{
+ return old_mail_moves(p , 1, param);
+}
+
+PUBLIC void ExamineScratch(int p, param_list param)
+{
+ char category[100], board[100], parsebuf[100];
+ char *val;
+ int confused = 0;
+ int g = game_new();
+
+ unobserveAll(p);
+
+ player_decline_offers(p, -1, PEND_MATCH);
+ player_withdraw_offers(p, -1, PEND_MATCH);
+ player_withdraw_offers(p, -1, PEND_SIMUL);
+
+ garray[g].wInitTime = garray[g].wIncrement = 0;
+ garray[g].bInitTime = garray[g].bIncrement = 0;
+ garray[g].timeOfStart = tenth_secs();
+ garray[g].wTime = garray[g].bTime = 0;
+ garray[g].rated = 0;
+ garray[g].clockStopped = 0;
+ garray[g].type = TYPE_UNTIMED;
+ garray[g].white = garray[g].black = p;
+ garray[g].status = GAME_EXAMINE;
+ garray[g].startTime = tenth_secs();
+ garray[g].lastMoveTime = garray[g].startTime;
+ garray[g].lastDecTime = garray[g].startTime;
+ garray[g].totalHalfMoves = 0;
+
+ parray[p].side = WHITE; /* oh well... */
+ parray[p].game = g;
+
+ category[0] = '\0';
+ board[0] = '\0';
+
+ if ((param[0].val.string != parray[p].name) &&
+ (param[1].type == TYPE_WORD)) {
+ strcpy(category, param[0].val.string);
+ strcpy(board, param[1].val.string);
+ } else if (param[1].type != TYPE_NULL) {
+
+ val = param[1].val.string;
+
+ while (!confused && (sscanf(val, " %99s", parsebuf) == 1)) {
+ val = eatword(eatwhite(val));
+ if ((category[0] != '\0') && (board[0] == '\0'))
+ strcpy(board, parsebuf);
+ else if (isdigit(*parsebuf)) {
+ pprintf(p, "You can't specify time controls.\n");
+ return;
+ } else if (category[0] == '\0')
+ strcpy(category, parsebuf);
+ else
+ confused = 1;
+ }
+ if (confused) {
+ pprintf(p, "Can't interpret %s in match command.\n", parsebuf);
+ return;
+ }
+ }
+
+
+ if (category[0] && !board[0]) {
+ pprintf(p, "You must specify a board and a category.\n");
+ return;
+ }
+
+ pprintf(p, "Starting a game in examine (scratch) mode.\n");
+
+ if (category[0]) {
+ pprintf(p, "Loading from catagory: %s, board: %s.\n", category, board);
+ }
+
+ if (board_init(&garray[g].game_state, category, board)) {
+ pprintf(p, "PROBLEM LOADING BOARD. Game Aborted.\n");
+ fprintf(stderr, "FICS: PROBLEM LOADING BOARD. Game Aborted.\n");
+ return;
+ }
+
+ garray[g].game_state.gameNum = g;
+ strcpy(garray[g].white_name, parray[p].name);
+ strcpy(garray[g].black_name, parray[p].name);
+ garray[g].white_rating = garray[g].black_rating = parray[p].s_stats.rating;
+
+ send_boards(g);
+ MakeFENpos(g, garray[g].FENstartPos);
+}
+
+PRIVATE int ExamineStored(FILE * fp, int p, char *filename)
+{
+ int g;
+ char category[100], board[100];
+ game *gg;
+
+ unobserveAll(p);
+
+ player_decline_offers(p, -1, PEND_MATCH);
+ player_withdraw_offers(p, -1, PEND_MATCH);
+ player_withdraw_offers(p, -1, PEND_SIMUL);
+
+ g = game_new();
+ gg = &garray[g];
+ category[0] = '\0';
+ board[0] = '\0';
+ if (board_init(&gg->game_state, category, board)) {
+ pprintf(p, "PROBLEM LOADING BOARD. Game Aborted.\n");
+ fprintf(stderr, "FICS: PROBLEM LOADING BOARD %s %s. Game Aborted.\n",
+ category, board);
+ return -1;
+ }
+ gg->status = GAME_EXAMINE;
+ if (ReadGameAttrs(fp, filename, g) < 0) {
+ pprintf(p, "Gamefile is corrupt; please notify an admin.\n");
+ return -1;
+ }
+ gg->totalHalfMoves = gg->numHalfMoves;
+ gg->numHalfMoves = 0;
+ gg->revertHalfMove = 0;
+ gg->white = p;
+ gg->black = p;
+ gg->game_state.gameNum = g;
+
+ gg->startTime = tenth_secs();
+ gg->lastMoveTime = gg->startTime;
+ gg->lastDecTime = gg->startTime;
+
+ parray[p].side = WHITE; /* oh well... */
+ parray[p].game = g;
+
+ send_boards(g);
+ MakeFENpos(g, garray[g].FENstartPos);
+
+ return g;
+}
+
+PRIVATE void ExamineAdjourned(int p, int p1, int p2)
+{
+ FILE *fp;
+ char filename[1024];
+ char *p1Login, *p2Login;
+ int g;
+
+ p1Login = parray[p1].login;
+ p2Login = parray[p2].login;
+
+ sprintf(filename, "%s/%c/%s-%s", adj_dir, *p1Login, p1Login, p2Login);
+ fp = fopen(filename, "r");
+ if (!fp) {
+ sprintf(filename, "%s/%c/%s-%s", adj_dir, *p2Login, p1Login, p2Login);
+ fp = fopen(filename, "r");
+ if (!fp) {
+ sprintf(filename, "%s/%c/%s-%s", adj_dir, *p2Login, p2Login, p1Login);
+ fp = fopen(filename, "r");
+ if (!fp) {
+ sprintf(filename, "%s/%c/%s-%s", adj_dir, *p1Login, p2Login, p1Login);
+ fp = fopen(filename, "r");
+ if (!fp) {
+ pprintf(p, "No stored game between \"%s\" and \"%s\".\n",
+ parray[p1].name, parray[p2].name);
+ return;
+ }
+ }
+ }
+ }
+ g = ExamineStored(fp, p, filename);
+ fclose(fp);
+
+ if (g >= 0) {
+ if (garray[g].white_name[0] == '\0')
+ strcpy(garray[g].white_name, p1Login);
+ if (garray[g].black_name[0] == '\0')
+ strcpy(garray[g].black_name, p2Login);
+ }
+ return;
+}
+
+PRIVATE char *FindHistory(int p, int p1, int game)
+{
+ FILE *fpHist;
+ static char fileName[MAX_FILENAME_SIZE];
+ int index;
+ long when;
+
+ sprintf(fileName, "%s/player_data/%c/%s.%s", stats_dir,
+ parray[p1].login[0], parray[p1].login, STATS_GAMES);
+ fpHist = fopen(fileName, "r");
+ if (fpHist == NULL) {
+ pprintf(p, "No games in history for %s.\n", parray[p1].name);
+ return(NULL);
+ }
+ do {
+ fscanf(fpHist, "%d %*c %*d %*c %*d %*s %*s %*d %*d %*d %*d %*s %*s %ld",
+ &index, &when);
+ } while (!feof(fpHist) && index != game);
+
+ if (feof(fpHist)) {
+ pprintf(p, "There is no history game %d for %s.\n", game, parray[p1].name);
+ fclose(fpHist);
+ return(NULL);
+ }
+ fclose(fpHist);
+
+ sprintf(fileName, "%s/%ld/%ld", hist_dir, when % 100, when);
+ return(fileName);
+}
+
+/* I want to know how game ended */
+
+PRIVATE char *FindHistory2(int p, int p1,int game,char* End)
+{
+ FILE *fpHist;
+ static char fileName[MAX_FILENAME_SIZE];
+ int index;
+ long when;
+
+ sprintf(fileName, "%s/player_data/%c/%s.%s", stats_dir,
+ parray[p1].login[0], parray[p1].login, STATS_GAMES);
+ fpHist = fopen(fileName, "r");
+ if (fpHist == NULL) {
+ pprintf(p, "No games in history for %s.\n", parray[p1].name);
+ return(NULL);
+ }
+ do {
+ fscanf(fpHist, "%d %*c %*d %*c %*d %*s %*s %*d %*d %*d %*d %*s %s %ld",
+ &index, End, &when);
+ } while (!feof(fpHist) && index != game);
+
+ if (feof(fpHist)) {
+ pprintf(p, "There is no history game %d for %s.\n", game, parray[p1].name);
+ fclose(fpHist);
+ return(NULL);
+ }
+ fclose(fpHist);
+
+ sprintf(fileName, "%s/%ld/%ld", hist_dir, when % 100, when);
+ return(fileName);
+}
+
+PRIVATE void ExamineHistory(int p, int p1, int game)
+{
+ char *fileName;
+
+ fileName = FindHistory(p, p1, game);
+ if (fileName != NULL) {
+ FILE *fpGame = fopen(fileName, "r");
+ if (fpGame == NULL) {
+ pprintf(p, "History game %d not available for %s.\n", game, parray[p1].name);
+ } else {
+ ExamineStored(fpGame, p, fileName);
+ fclose(fpGame);
+ }
+ }
+ return;
+}
+
+PRIVATE void ExamineJournal(int p,int p1,char slot)
+{
+ char fname[MAX_FILENAME_SIZE];
+ char* name_from = parray[p1].login;
+ FILE *fpGame;
+
+ if ((parray[p1].jprivate) && (p != p1) && (parray[p].adminLevel < ADMIN_ADMIN)) {
+ pprintf (p,"Sorry, this journal is private.\n");
+ return;
+ }
+ if (((slot - 'a' - 1) > MAX_JOURNAL) && (parray[p1].adminLevel < ADMIN_ADMIN)
+&& (!titled_player(p,parray[p1].login))) {
+ pprintf (p,"%s's maximum journal entry is %c\n",parray[p1].name,toupper((char)(MAX_JOURNAL + 'A' - 1)));
+ return;
+ }
+
+ sprintf(fname, "%s/%c/%s.%c", journal_dir, name_from[0],name_from,slot);
+ fpGame = fopen(fname, "r");
+ if (fpGame == NULL) {
+ pprintf(p, "Journal entry %c is not available for %s.\n", toupper (slot), parray[p1].name);
+ } else {
+ ExamineStored(fpGame, p, fname);
+ fclose(fpGame);
+ }
+ return;
+}
+
+PUBLIC int com_examine(int p, param_list param)
+{
+ int p1, p2 = p, p1conn, p2conn = 1;
+ char* param2string;
+ char fname[MAX_FILENAME_SIZE];
+
+ if ((parray[p].game >=0) &&(garray[parray[p].game].status == GAME_EXAMINE)) {
+ pprintf(p, "You are already examining a game.\n");
+ } else if (parray[p].game >=0) {
+ pprintf(p, "You are playing a game.\n");
+ } else if (param[0].type == TYPE_NULL) {
+ ExamineScratch(p, param);
+ } else if (param[0].type == TYPE_WORD) {
+ if (param[1].type == TYPE_WORD) {
+ sprintf(fname, "%s/%s/%s", board_dir, param[0].val.word, param[1].val.word);
+ if (file_exists(fname)) {
+ ExamineScratch(p, param);
+ return COM_OK;
+ }
+ }
+ if (!FindPlayer(p, param[0].val.word, &p1, &p1conn))
+ return COM_OK;
+
+ if (param[1].type == TYPE_INT)
+ ExamineHistory(p, p1, param[1].val.integer);
+ else {
+ if (param[1].type == TYPE_WORD) {
+
+ /* Lets check the journal */
+ param2string = param[1].val.word;
+ if ((strlen(param2string) == 1) && (isalpha(param2string[0]))) {
+ ExamineJournal(p,p1,param2string[0]);
+ if (!p1conn)
+ player_remove(p1);
+ return COM_OK;
+ } else {
+ if (!FindPlayer(p, param[1].val.word, &p2, &p2conn)) {
+ if (!p1conn)
+ player_remove(p1);
+ return COM_OK;
+ }
+ }
+ }
+ ExamineAdjourned(p, p1, p2);
+ if (!p2conn)
+ player_remove(p2);
+ }
+ if (!p1conn)
+ player_remove(p1);
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_stored(int p, param_list param)
+{
+ DIR *dirp;
+#ifdef USE_DIRENT
+ struct dirent *dp;
+#else
+ struct direct *dp;
+#endif
+ int p1, connected;
+ char dname[MAX_FILENAME_SIZE];
+
+ if (param[0].type == TYPE_WORD) {
+ if (!FindPlayer(p, param[0].val.word, &p1, &connected))
+ return COM_OK;
+ } else {
+ p1 = p;
+ connected = 1;
+ }
+
+ sprintf(dname, "%s/%c", adj_dir, parray[p1].login[0]);
+ dirp = opendir(dname);
+ if (!dirp) {
+ pprintf(p, "Player %s has no games stored.\n", parray[p1].name);
+ if (!connected)
+ player_remove(p1);
+ return COM_OK;
+ }
+ pprintf(p, "Stored games for %s:\n", parray[p1].name);
+ for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
+ if (file_has_pname(dp->d_name, parray[p1].login)) {
+ pprintf(p, " %s vs. %s\n", file_wplayer(dp->d_name), file_bplayer(dp->d_name));
+ }
+ }
+
+ closedir(dirp);
+ pprintf(p, "\n");
+ if (!connected)
+ player_remove(p1);
+ return COM_OK;
+}
+
+PRIVATE void stored_mail_moves(int p, int mail, param_list param)
+{
+ int wp, wconnected, bp, bconnected, gotit = 0;
+ int g = -1;
+ char* param2string;
+ char* name_from;
+ char* fileName;
+ char fileName2[MAX_FILENAME_SIZE];
+ FILE* fpGame;
+
+ if (mail && (!parray[p].registered)) {
+ pprintf (p,"Unregistered players cannot use mailstored.\n");
+ return;
+ }
+
+ if (!FindPlayer(p, param[0].val.word, &wp, &wconnected))
+ return;
+
+ if (param[1].type == TYPE_INT) { /* look for a game from history */
+ fileName = FindHistory(p, wp, param[1].val.integer);
+ if (fileName != NULL) {
+ fpGame = fopen(fileName, "r");
+ if (fpGame == NULL) {
+ pprintf(p, "History game %d not available for %s.\n", param[1].val.integer, parray[wp].name);
+ } else {
+ g = game_new();
+ if (ReadGameAttrs(fpGame, fileName, g) < 0)
+ pprintf(p, "Gamefile is corrupt; please notify an admin.\n");
+ else
+ gotit = 1;
+ fclose(fpGame);
+ }
+ }
+ } else { /* Let's test for journal */
+ name_from = param[0].val.word;
+ param2string = param[1].val.word;
+ if ((strlen(param2string) == 1) && (isalpha(param2string[0]))) {
+ if ((parray[wp].jprivate) && (parray[p].adminLevel < ADMIN_ADMIN) && (p != wp)) {
+ pprintf (p,"Sorry, the journal from which you are trying to fetch is private.\n");
+ } else {
+ if (((param2string[0] - 'a' - 1) > MAX_JOURNAL) && (parray[wp].adminLevel < ADMIN_ADMIN) && (!titled_player(p,parray[wp].login))) {
+ pprintf (p,"%s's maximum journal entry is %c\n",parray[wp].name,toupper((char)(MAX_JOURNAL + 'A' - 1)));
+ } else {
+ sprintf(fileName2, "%s/%c/%s.%c", journal_dir, name_from[0],name_from,param2string[0]);
+ fpGame = fopen(fileName2, "r");
+ if (fpGame == NULL) {
+ pprintf(p, "Journal entry %c is not available for %s.\n", toupper(param2string[0]),
+ parray[wp].name);
+ } else {
+ g = game_new();
+ if (ReadGameAttrs(fpGame, fileName, g) < 0)
+ pprintf(p, "Journal entry is corrupt; please notify an admin.\n");
+ else
+ gotit = 1;
+ fclose(fpGame);
+ }
+ }
+ }
+ } else {
+
+ /* look for a stored game between the players */
+
+ if (FindPlayer(p, param[1].val.word, &bp, &bconnected)) {
+ g = game_new();
+ if (game_read(g, wp, bp) >= 0) { /* look for a game white-black, */
+ gotit = 1;
+ } else if (game_read(g, bp, wp) >= 0) { /* or black-white */
+ gotit = 1;
+ } else {
+ pprintf(p, "There is no stored game %s vs. %s\n", parray[wp].name, parray[bp].name);
+ }
+ if (!bconnected)
+ player_remove(bp);
+ }
+ }
+ }
+ if (gotit) {
+ if (strcasecmp(parray[p].name, garray[g].white_name) && strcasecmp(parray[p]
+.name, garray[g].black_name) && garray[g].private && (parray[p].adminLevel < ADMIN_ADMIN)) {
+ pprintf(p, "Sorry, that is a private game.\n");
+ } else {
+ if (mail == 1) { /*Do mailstored */
+ char subj[81];
+ if (param[1].type == TYPE_INT)
+ sprintf(subj, "FICS history game: %s %d", parray[wp].name, param[1].val.integer);
+ else
+ if ((strlen(param2string) == 1) && (isalpha(param2string[0]))) {
+ sprintf(subj, "FICS journal game %s vs %s", garray[g].white_name, garray[g].black_name);
+ } else {
+ sprintf(subj, "FICS adjourned game %s vs %s", garray[g].white_name, garray[g].black_name);
+ }
+ if (mail_string_to_user(p, subj, movesToString(g, parray[p].pgn)))
+ pprintf(p, "Moves NOT mailed, perhaps your address is incorrect.\n");
+ else
+ pprintf(p, "Moves mailed.\n");
+ } else {
+ pprintf(p, "%s\n", movesToString(g, 0));
+ } /* Do smoves */
+ }
+ }
+ if (!wconnected)
+ player_remove(wp);
+ if (g != -1)
+ game_remove(g);
+}
+
+/* Tidied up a bit but still messy */
+
+PUBLIC int com_mailstored(int p, param_list param)
+{
+ stored_mail_moves(p, 1, param);
+ return COM_OK;
+}
+
+PUBLIC int com_smoves(int p, param_list param)
+{
+ stored_mail_moves(p, 0, param);
+ return COM_OK;
+}
+
+PUBLIC int com_sposition(int p, param_list param)
+{
+ int wp, wconnected, bp, bconnected, confused = 0;
+ int g;
+
+ if (!FindPlayer(p, param[0].val.word, &wp, &wconnected))
+ return (COM_OK);
+ if (!FindPlayer(p, param[1].val.word, &bp, &bconnected)) {
+ if (!wconnected)
+ player_remove(wp);
+ return (COM_OK);
+ }
+
+ g = game_new();
+ if (game_read(g, wp, bp) < 0) { /* if no game white-black, */
+ if (game_read(g, bp, wp) < 0) { /* look for black-white */
+ confused = 1;
+ pprintf(p, "There is no stored game %s vs. %s\n", parray[wp].name, parray[bp].name);
+ } else {
+ int tmp;
+ tmp = wp;
+ wp = bp;
+ bp = tmp;
+ tmp = wconnected;
+ wconnected = bconnected;
+ bconnected = tmp;
+ }
+ }
+ if (!confused) {
+ if ((wp != p) && (bp != p) && (garray[g].private) && (parray[p].adminLevel < ADMIN_ADMIN)) {
+ pprintf(p, "Sorry, that is a private game.\n");
+ } else {
+ garray[g].white = wp;
+ garray[g].black = bp;
+ garray[g].startTime = tenth_secs();
+ garray[g].lastMoveTime = garray[g].startTime;
+ garray[g].lastDecTime = garray[g].startTime;
+ pprintf(p, "Position of stored game %s vs. %s\n", parray[wp].name, parray[bp].name);
+ send_board_to(g, p);
+ }
+ }
+ game_remove(g);
+ if (!wconnected)
+ player_remove(wp);
+ if (!bconnected)
+ player_remove(bp);
+ return COM_OK;
+}
+
+PUBLIC int com_forward(int p, param_list param)
+{
+ int nHalfMoves = 1;
+ int g, i;
+ int p1;
+ unsigned now;
+
+ if (!((parray[p].game >=0) &&(garray[parray[p].game].status == GAME_EXAMINE))) {
+ pprintf(p, "You are not examining any games.\n");
+ return COM_OK;
+ }
+ g = parray[p].game;
+ if (!strcmp(garray[g].white_name, garray[g].black_name)) {
+ pprintf(p, "You cannot go forward; no moves are stored.\n");
+ return COM_OK;
+ }
+ if (param[0].type == TYPE_INT) {
+ nHalfMoves = param[0].val.integer;
+ }
+ if (garray[g].numHalfMoves > garray[g].revertHalfMove) {
+ pprintf(p, "No more moves.\n");
+ return COM_OK;
+ }
+ if (garray[g].numHalfMoves < garray[g].totalHalfMoves) {
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (parray[p1].status != PLAYER_PROMPT)
+ continue;
+ if (player_is_observe(p1, g) || parray[p1].game == g) {
+ pprintf(p1, "%s goes forward %d move%s.\n",
+ parray[p].name, nHalfMoves, (nHalfMoves == 1) ? "" : "s");
+ }
+ }
+ }
+ for (i = 0; i < nHalfMoves; i++) {
+ if (garray[g].numHalfMoves < garray[g].totalHalfMoves) {
+ execute_move(&garray[g].game_state, &garray[g].moveList[garray[g].numHalfMoves], 1);
+ if (garray[g].numHalfMoves + 1 > garray[g].examMoveListSize) {
+ garray[g].examMoveListSize += 20; /* Allocate 20 moves at a
+ time */
+ if (!garray[g].examMoveList) {
+ garray[g].examMoveList = (move_t *) rmalloc(sizeof(move_t) * garray[g].examMoveListSize);
+ } else {
+ garray[g].examMoveList = (move_t *) rrealloc(garray[g].examMoveList, sizeof(move_t) * garray[g].examMoveListSize);
+ }
+ }
+ garray[g].examMoveList[garray[g].numHalfMoves] = garray[g].moveList[garray[g].numHalfMoves];
+ garray[g].revertHalfMove++;
+ garray[g].numHalfMoves++;
+ } else {
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (parray[p1].status != PLAYER_PROMPT)
+ continue;
+ if (player_is_observe(p1, g) || parray[p1].game == g) {
+ pprintf(p1, "End of game.\n");
+ }
+ }
+ break;
+ }
+ }
+ /* roll back time */
+ if (garray[g].game_state.onMove == WHITE) {
+ garray[g].wTime += (garray[g].lastDecTime - garray[g].lastMoveTime);
+ } else {
+ garray[g].bTime += (garray[g].lastDecTime - garray[g].lastMoveTime);
+ }
+ now = tenth_secs();
+ if (garray[g].numHalfMoves == 0)
+ garray[g].timeOfStart = now;
+ garray[g].lastMoveTime = now;
+ garray[g].lastDecTime = now;
+ send_boards(g);
+ return COM_OK;
+}
+
+PUBLIC int com_backward(int p, param_list param)
+{
+ int nHalfMoves = 1;
+ int g, i;
+ int p1;
+ unsigned now;
+
+ if (!((parray[p].game >=0) &&(garray[parray[p].game].status == GAME_EXAMINE))) {
+ pprintf(p, "You are not examining any games.\n");
+ return COM_OK;
+ }
+ g = parray[p].game;
+ if (param[0].type == TYPE_INT) {
+ nHalfMoves = param[0].val.integer;
+ }
+ if (garray[g].numHalfMoves != 0) {
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (parray[p1].status != PLAYER_PROMPT)
+ continue;
+ if (player_is_observe(p1, g) || parray[p1].game == g) {
+ pprintf(p1, "%s backs up %d move%s.\n",
+ parray[p].name, nHalfMoves, (nHalfMoves == 1) ? "" : "s");
+ }
+ }
+ }
+ for (i = 0; i < nHalfMoves; i++) {
+ if (backup_move(g, REL_EXAMINE) != MOVE_OK) {
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (parray[p1].status != PLAYER_PROMPT)
+ continue;
+ if (player_is_observe(p1, g) || parray[p1].game == g) {
+ pprintf(p1, "Beginning of game.\n");
+ }
+ }
+
+ break;
+ }
+ }
+ if (garray[g].numHalfMoves < garray[g].revertHalfMove) {
+ garray[g].revertHalfMove = garray[g].numHalfMoves;
+ }
+ /* roll back time */
+ if (garray[g].game_state.onMove == WHITE) {
+ garray[g].wTime += (garray[g].lastDecTime - garray[g].lastMoveTime);
+ } else {
+ garray[g].bTime += (garray[g].lastDecTime - garray[g].lastMoveTime);
+ }
+ now = tenth_secs();
+ if (garray[g].numHalfMoves == 0)
+ garray[g].timeOfStart = now;
+ garray[g].lastMoveTime = now;
+ garray[g].lastDecTime = now;
+ send_boards(g);
+ return COM_OK;
+}
+
+PUBLIC int com_revert(int p, param_list param)
+{
+ int nHalfMoves = 1;
+ int g, i;
+ int p1;
+ unsigned now;
+
+ if (!((parray[p].game >=0) &&(garray[parray[p].game].status == GAME_EXAMINE))) {
+ pprintf(p, "You are not examining any games.\n");
+ return COM_OK;
+ }
+ g = parray[p].game;
+ nHalfMoves = garray[g].numHalfMoves - garray[g].revertHalfMove;
+ if (nHalfMoves == 0) {
+ pprintf(p, "Already at mainline.\n");
+ return COM_OK;
+ }
+ if (nHalfMoves < 0) { /* eek - should NEVER happen! */
+ fprintf(stderr, "OUCH! in com_revert: nHalfMoves < 0\n");
+ return COM_OK;
+ }
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (parray[p1].status != PLAYER_PROMPT)
+ continue;
+ if (player_is_observe(p1, g) || parray[p1].game == g) {
+ pprintf(p1, "%s reverts to mainline.\n", parray[p].name);
+ }
+ }
+ for (i = 0; i < nHalfMoves; i++) {
+ backup_move(g, REL_EXAMINE);/* should never return error */
+ }
+ /* roll back time */
+ if (garray[g].game_state.onMove == WHITE) {
+ garray[g].wTime += (garray[g].lastDecTime - garray[g].lastMoveTime);
+ } else {
+ garray[g].bTime += (garray[g].lastDecTime - garray[g].lastMoveTime);
+ }
+ now = tenth_secs();
+ if (garray[g].numHalfMoves == 0)
+ garray[g].timeOfStart = now;
+ garray[g].lastMoveTime = now;
+ garray[g].lastDecTime = now;
+ send_boards(g);
+ return COM_OK;
+}
+
+PUBLIC int com_history(int p, param_list param)
+{
+ int p1, connected;
+ char fname[MAX_FILENAME_SIZE];
+
+ if (param[0].type == TYPE_WORD) {
+ if (!FindPlayer(p, param[0].val.word, &p1, &connected))
+ return COM_OK;
+ } else {
+ p1 = p;
+ connected = 1;
+ }
+
+ sprintf(fname, "%s/player_data/%c/%s.%s", stats_dir, parray[p1].login[0],
+ parray[p1].login, STATS_GAMES);
+ pgames(p, p1, fname);
+ if (!connected)
+ player_remove(p1);
+ return COM_OK;
+}
+
+PUBLIC int com_journal(int p, param_list param)
+{
+ int p1, connected;
+ char fname[MAX_FILENAME_SIZE];
+
+ if (param[0].type == TYPE_WORD) {
+ if (!FindPlayer(p, param[0].val.word, &p1, &connected))
+ return COM_OK;
+ } else {
+ p1 = p;
+ connected = 1;
+ }
+
+if (!parray[p1].registered) {
+ pprintf (p,"Only registered players may keep a journal.\n");
+ if (!connected)
+ player_remove(p1);
+ return COM_OK;
+ }
+ if ((parray[p1].jprivate) && (p != p1) && (parray[p].adminLevel < ADMIN_ADMIN)) {
+ pprintf (p,"Sorry, this journal is private.\n");
+ if (!connected)
+ player_remove(p1);
+ return COM_OK;
+ }
+ sprintf(fname, "%s/player_data/%c/%s.%s", stats_dir, parray[p1].login[0],
+ parray[p1].login, STATS_JOURNAL);
+ pjournal(p, p1, fname);
+ if (!connected)
+ player_remove(p1);
+ return COM_OK;
+}
+
+PRIVATE void jsave_journalentry(int p,char save_spot,int p1,char from_spot,char* to_file)
+
+{
+ FILE *Game;
+
+ char fname[MAX_FILENAME_SIZE], fname2[MAX_FILENAME_SIZE];
+ char command[MAX_FILENAME_SIZE*2+3];
+ char* name_from = parray[p1].login;
+ char* name_to = parray[p].login;
+ char WhiteName[MAX_LOGIN_NAME + 1];
+ char BlackName[MAX_LOGIN_NAME + 1];
+ int WhiteRating;
+ int BlackRating;
+ int i,t;
+ char type[100];
+ char eco[100];
+ char ending[100];
+ char result[100];
+
+ sprintf(fname, "%s/%c/%s.%c", journal_dir, name_from[0],name_from,from_spot);
+ Game = fopen(fname, "r");
+ if (Game == NULL) {
+ pprintf(p, "Journal entry %c not available for %s.\n", toupper(from_spot), parray[p1].name);
+ return;
+ }
+ fclose (Game);
+
+ sprintf(fname2, "%s/%c/%s.%c", journal_dir, name_to[0],name_to,save_spot);
+ unlink (fname2); /* necessarity if cp is hard aliased to cp -i */
+ sprintf(command, "cp %s %s",fname,fname2);
+
+ if (system(command)) { /* A little messy, but works */
+ pprintf (p,"System command in jsave_journalentry failed!\n");
+ pprintf (p,"Please report this to an admin.\n");
+ fprintf (stderr, "FICS: System command failed in jsave_journalentry\n");
+ return;
+ }
+
+ sprintf(fname, "%s/player_data/%c/%s.%s", stats_dir, name_to[0],
+ name_to, STATS_JOURNAL);
+
+ if (!journal_get_info(p,from_spot,WhiteName,&WhiteRating,
+ BlackName,&BlackRating,type,&t,&i,eco,
+ ending,result,fname)) {
+ return;
+ }
+
+ addjournalitem(p, toupper(save_spot), WhiteName, WhiteRating,
+ BlackName,BlackRating,type,t,i,eco,
+ ending,result, to_file);
+
+ pprintf(p,"Journal entry %s %c saved in slot %c in journal.\n",parray[p1].name, toupper(from_spot), toupper(save_spot));
+}
+
+PUBLIC void jsave_history(int p,char save_spot,int p1,int from,char* to_file)
+{
+
+
+ char End[100];
+ char jfname[MAX_FILENAME_SIZE];
+ char* HistoryFname = FindHistory2(p, p1, from, End);
+ /* End symbol Mat Res, etc is the only thing we can't find out */
+ char command[MAX_FILENAME_SIZE*2+3];
+ char* name_to = parray[p].login;
+ char* EndSymbol;
+ FILE *Game;
+ char type[4];
+ int g;
+ char* filename[MAX_FILENAME_SIZE+1];
+
+ if (HistoryFname != NULL) {
+ Game = fopen(HistoryFname, "r");
+ if (Game == NULL) {
+ pprintf(p, "History game %d not available for %s.\n", from, parray[p1].name);
+ } else {
+ sprintf(jfname, "%s/%c/%s.%c", journal_dir, name_to[0],name_to,save_spot);
+ unlink(jfname); /* necessary if cp is hard aliased to cp -i */
+ sprintf(command, "cp %s %s",HistoryFname,jfname);
+ if (system(command)) { /* A little messy, but works */
+ pprintf (p,"System command in jsave_history failed!\n");
+ pprintf (p,"Please report this to an admin.\n");
+ fprintf (stderr, "FICS: System command failed in jsave_journalentry\n");
+ return;
+ }
+ g = game_new(); /* Open a dummy game */
+
+ if (ReadGameAttrs(Game, filename, g) < 0) {
+ pprintf (p,"Gamefile is corrupt. Please tell an admin.\n");
+ game_free(g);
+ fclose (Game);
+ return;
+ }
+ fclose (Game);
+ if (garray[g].private) {
+ type[0] = 'p';
+ } else {
+ type[0] = ' ';
+ }
+ if (garray[g].type == TYPE_BLITZ) {
+ type[1] = 'b';
+ } else if (garray[g].type == TYPE_WILD) {
+ type[1] = 'w';
+ } else if (garray[g].type == TYPE_STAND) {
+ type[1] = 's';
+ } else {
+ if (garray[g].type == TYPE_NONSTANDARD)
+ type[1] = 'n';
+ else
+ type[1] = 'u';
+ }
+ if (garray[g].rated) {
+ type[2] = 'r';
+ } else {
+ type[2] = 'u';
+ }
+ type[3] = '\0';
+
+ EndSymbol = EndSym(g);
+ addjournalitem(p, toupper(save_spot), garray[g].white_name, garray[g].white_rating,
+ garray[g].black_name,garray[g].black_rating,type,garray[g].wInitTime,garray[g].wIncrement,getECO(g),
+ End,EndSymbol, to_file);
+ game_free(g);
+ pprintf(p,"Game %s %d saved in slot %c in journal.\n",parray[p1].name, from, toupper(save_spot));
+ }
+ }
+}
+
+PUBLIC int com_jsave(int p, param_list param)
+{
+ int p1, p1conn;
+ char* to = param[0].val.word;
+ char* from;
+ char fname[MAX_FILENAME_SIZE];
+
+ if (!parray[p].registered) {
+ pprintf (p,"Only registered players may keep a journal.\n");
+ return COM_OK;
+ }
+
+ if ((strlen(to) != 1) || (!(isalpha(to[0])))) {
+ pprintf (p,"Journal entries are referenced by single letters.\n");
+ return COM_OK;
+ }
+
+ if (((to[0] - 'a' - 1) > MAX_JOURNAL) && (parray[p].adminLevel < ADMIN_ADMIN) && (!titled_player(p,parray[p].login))) {
+ pprintf (p,"Your maximum journal entry is %c\n",toupper ((char)(MAX_JOURNAL + 'A' - 1)));
+ return COM_OK;
+ }
+
+ if (!FindPlayer(p, param[1].val.word, &p1, &p1conn))
+ return COM_OK;
+
+ if (param[2].type == TYPE_INT) {
+
+ /* grab from a history */
+ sprintf (fname,"%s/player_data/%c/%s.%s",stats_dir,parray[p].login[0],parray[p].login, STATS_JOURNAL);
+ jsave_history(p, to[0], p1, param[2].val.integer,fname);
+
+ } else {
+
+ from = param[2].val.word;
+
+ if ((strlen(from) != 1) || (!(isalpha(from[0])))) {
+ pprintf (p,"Journal entries are referenced by single letters.\n");
+ if (!p1conn)
+ player_remove(p1);
+ return COM_OK;
+ }
+
+ if ((parray[p1].jprivate) && (parray[p].adminLevel < ADMIN_ADMIN) && (p != p1)) {
+ pprintf (p,"Sorry, the journal from which you are trying to fetch is private.\n");
+
+ if (!p1conn)
+ player_remove(p1);
+ return COM_OK;
+ }
+
+ if (((to[0] - 'a' - 1) > MAX_JOURNAL) && (parray[p1].adminLevel < ADMIN_ADMIN) && (!titled_player(p,parray[p1].login))) {
+ pprintf (p,"%s's maximum journal entry is %c\n",parray[p1].name,toupper((char)(MAX_JOURNAL + 'A' - 1)));
+ if (!p1conn)
+ player_remove(p1);
+ return COM_OK;
+ }
+ if (( p == p1) && (to[0] == from [0])) {
+ pprintf (p,"Source and destination entries are the same.\n");
+ return COM_OK;
+ }
+
+ /* grab from a journal */
+
+ sprintf(fname, "%s/player_data/%c/%s.%s", stats_dir, parray[p].login[0],
+ parray[p].login, STATS_JOURNAL);
+ jsave_journalentry(p,to[0],p1, from[0], fname);
+
+ }
+ if (!p1conn)
+ player_remove(p1);
+ return COM_OK;
+}
diff --git a/FICS/obsproc.h b/FICS/obsproc.h
new file mode 100644
index 0000000..0d85e9a
--- /dev/null
+++ b/FICS/obsproc.h
@@ -0,0 +1,57 @@
+/* obsproc.h
+ *
+ */
+
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name email yy/mm/dd Change
+ Dave Herscovici 95/11/26 Created
+*/
+
+#ifndef _OBSPROC_H
+#define _OBSPROC_H
+
+#define MAX_JOURNAL 10
+
+extern int GameNumFromParam();
+extern int com_games();
+extern int com_observe();
+extern void unobserveAll(int);
+extern int com_unobserve();
+extern int com_allobservers();
+extern int com_moves();
+extern int com_mailmoves();
+extern int com_oldmoves();
+extern int com_mailoldmoves();
+extern int com_mailstored();
+extern int com_stored();
+extern int com_smoves();
+extern int com_sposition();
+extern int com_history();
+extern int com_journal();
+extern int com_jsave();
+
+extern int com_examine();
+extern int com_mexamine();
+extern int com_unexamine();
+extern int com_forward();
+extern int com_backward();
+extern int com_revert();
+
+extern void ExamineScratch ();
+
+#endif /* _OBSPROC_H */
diff --git a/FICS/playerdb.c b/FICS/playerdb.c
new file mode 100644
index 0000000..2cb30c5
--- /dev/null
+++ b/FICS/playerdb.c
@@ -0,0 +1,2542 @@
+/* playerdb.c
+ *
+ */
+
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name email yy/mm/dd Change
+ Richard Nash 93/10/22 Created
+*/
+
+#include "stdinclude.h"
+#include "common.h"
+#include "command.h"
+#include "comproc.h"
+#include "playerdb.h"
+#include "rmalloc.h"
+#include "utils.h"
+#include "network.h"
+#include "ficsmain.h"
+#include "config.h"
+#include "talkproc.h"
+#include "gamedb.h"
+#include "lists.h"
+/*#include "hostinfo.h" */
+#include "ratings.h"
+
+PUBLIC player parray[PARRAY_SIZE];
+PUBLIC int p_num = 0;
+
+#if 0 /* This never gets used! -- hersco */
+PUBLIC char *pend_strings[7] = {"match", "draw", "abort", "take back", "adjourn", "switch", "simul"};
+#endif
+
+PRIVATE int get_empty_slot(void)
+{
+ int i;
+
+ for (i = 0; i < p_num; i++) {
+ if (parray[i].status == PLAYER_EMPTY) {
+/*** fprintf(stderr,"New player put in parray[%d/%d]\n", i, p_num-1);*/
+ return i;
+ }
+ }
+
+ p_num++;
+
+ if (p_num+1 >= PARRAY_SIZE) {
+ fprintf(stderr, "*** Bogus attempt to get_empty_slot() past end of parray ***\n");
+ }
+
+/*** fprintf(stderr,"New player added in parray[%d]\n", p_num-1); */
+ parray[p_num - 1].status = PLAYER_EMPTY;
+ return p_num - 1;
+}
+
+PUBLIC void player_array_init()
+{
+ int i;
+
+ for (i = 0; i < PARRAY_SIZE; i++)
+ parray[i].status = PLAYER_EMPTY;
+}
+
+PUBLIC void player_init(int startConsole)
+{
+ int p;
+
+ if (startConsole) {
+ net_addConnection(0, 0);
+ p = player_new();
+ parray[p].login = strdup("console");
+ parray[p].name = strdup("console");
+ parray[p].passwd = strdup("*");
+ parray[p].fullName = strdup("The Operator");
+ parray[p].emailAddress = NULL;
+ parray[p].prompt = strdup("fics%");
+ parray[p].adminLevel = ADMIN_GOD;
+ parray[p].socket = 0;
+ parray[p].busy[0] = '\0';
+ pprintf_prompt(p, "\nLogged in on console.\n");
+ }
+}
+
+PUBLIC int player_new()
+{
+ int new;
+
+ new = get_empty_slot();
+ player_zero(new);
+ return new;
+}
+
+#define INVALID ((char *) -42)
+
+PUBLIC int player_zero(int p)
+{
+ int i;
+
+ parray[p].name = NULL;
+ parray[p].login = NULL;
+ parray[p].fullName = NULL;
+ parray[p].emailAddress = NULL;
+ parray[p].prompt = def_prompt;
+ parray[p].partner = -1;
+ parray[p].passwd = NULL;
+ parray[p].socket = -1;
+ parray[p].registered = 0;
+ parray[p].status = PLAYER_NEW;
+ parray[p].s_stats.num = 0;
+ parray[p].s_stats.win = 0;
+ parray[p].s_stats.los = 0;
+ parray[p].s_stats.dra = 0;
+ parray[p].s_stats.rating = 0;
+ parray[p].s_stats.sterr = 350.0;
+ parray[p].s_stats.ltime = 0;
+ parray[p].s_stats.best = 0;
+ parray[p].s_stats.whenbest = 0;
+ parray[p].b_stats.num = 0;
+ parray[p].b_stats.win = 0;
+ parray[p].b_stats.los = 0;
+ parray[p].b_stats.dra = 0;
+ parray[p].b_stats.rating = 0;
+ parray[p].b_stats.sterr = 350.0;
+ parray[p].b_stats.ltime = 0;
+ parray[p].b_stats.best = 0;
+ parray[p].b_stats.whenbest = 0;
+ parray[p].w_stats.num = 0;
+ parray[p].w_stats.win = 0;
+ parray[p].w_stats.los = 0;
+ parray[p].w_stats.dra = 0;
+ parray[p].w_stats.rating = 0;
+ parray[p].w_stats.sterr = 350.0;
+ parray[p].w_stats.ltime = 0;
+ parray[p].w_stats.best = 0;
+ parray[p].w_stats.whenbest = 0;
+ parray[p].l_stats.num = 0;
+ parray[p].l_stats.win = 0;
+ parray[p].l_stats.los = 0;
+ parray[p].l_stats.dra = 0;
+ parray[p].l_stats.rating = 0;
+ parray[p].l_stats.sterr = 350.0;
+ parray[p].l_stats.ltime = 0;
+ parray[p].l_stats.best = 0;
+ parray[p].l_stats.whenbest = 0;
+ parray[p].bug_stats.num = 0;
+ parray[p].bug_stats.win = 0;
+ parray[p].bug_stats.los = 0;
+ parray[p].bug_stats.dra = 0;
+ parray[p].bug_stats.rating = 0;
+ parray[p].bug_stats.sterr = 350.0;
+ parray[p].bug_stats.ltime = 0;
+ parray[p].bug_stats.best = 0;
+ parray[p].bug_stats.whenbest = 0;
+ parray[p].d_time = 2;
+ parray[p].d_inc = 12;
+ parray[p].d_height = 24;
+ parray[p].d_width = 79;
+ parray[p].language = LANG_DEFAULT;
+ parray[p].last_file = NULL;
+ parray[p].last_file_byte = 0L;
+ parray[p].open = 1;
+ parray[p].rated = 0;
+ parray[p].ropen = 1;
+ parray[p].bell = 0;
+ parray[p].timeOfReg = 0;
+ parray[p].totalTime = 0;
+ parray[p].pgn = 0;
+ parray[p].notifiedby = 0;
+ parray[p].i_login = 0;
+ parray[p].i_game = 0;
+ parray[p].i_shout = 1;
+ parray[p].i_cshout = 1;
+ parray[p].i_tell = 1;
+ parray[p].i_kibitz = 1;
+ parray[p].kiblevel = 0;
+ parray[p].private = 0;
+ parray[p].jprivate = 0;
+ parray[p].automail = 0;
+ parray[p].i_mailmess = 0;
+ parray[p].style = 0;
+ parray[p].promote = QUEEN;
+ parray[p].game = -1;
+ parray[p].last_tell = -1;
+ parray[p].last_channel = -1;
+ parray[p].logon_time = 0;
+ parray[p].last_command_time = 0;
+ parray[p].num_from = 0;
+ parray[p].num_to = 0;
+ parray[p].adminLevel = 0;
+ parray[p].i_admin = 1;
+/* parray[p].computer = 0; */
+ parray[p].num_plan = 0;
+ for (i = 0; i < MAX_PLAN; i++)
+ parray[p].planLines[i] = INVALID;
+ parray[p].num_formula = 0;
+ for (i = 0; i < MAX_FORMULA; i++)
+ parray[p].formulaLines[i] = NULL;
+ parray[p].formula = NULL;
+/* parray[p].nochannels = 0; */
+ parray[p].num_white = 0;
+ parray[p].num_black = 0;
+ parray[p].num_observe = 0;
+/* parray[p].uscfRating = 0; */
+/* parray[p].network_player = 0; */
+ parray[p].thisHost = 0;
+ parray[p].lastHost = 0;
+ parray[p].lastColor = WHITE;
+ parray[p].numAlias = 0;
+ for (i = 0; i < MAX_ALIASES; i++) {
+ parray[p].alias_list[i].comm_name = INVALID;
+ parray[p].alias_list[i].alias = INVALID;
+ }
+ parray[p].opponent = -1;
+ parray[p].last_opponent = -1;
+ parray[p].highlight = 0;
+/* parray[p].query_log = NULL; */
+ parray[p].lastshout_a = 0;
+ parray[p].lastshout_b = 0;
+ parray[p].sopen = 0;
+ parray[p].simul_info.numBoards = 0;
+ parray[p].num_comments = 0;
+ parray[p].flip = 0;
+ parray[p].lists = NULL;
+ return 0;
+}
+
+PUBLIC int player_free(int p)
+{
+ int i;
+
+ strfree(parray[p].login);
+ strfree(parray[p].name);
+ strfree(parray[p].passwd);
+ strfree(parray[p].fullName);
+ strfree(parray[p].emailAddress);
+ if (parray[p].prompt != def_prompt)
+ strfree(parray[p].prompt);
+/* strfree(parray[p].partner); */
+ for (i = 0; i < parray[p].num_plan; i++)
+ strfree(parray[p].planLines[i]);
+ for (i = 0; i < parray[p].num_formula; i++)
+ strfree(parray[p].formulaLines[i]);
+ strfree(parray[p].formula);
+ list_free(parray[p].lists);
+ for (i = 0; i < parray[p].numAlias; i++) {
+ strfree(parray[p].alias_list[i].comm_name);
+ strfree(parray[p].alias_list[i].alias);
+ }
+/*
+ if (parray[p].query_log != NULL)
+ tl_free(parray[p].query_log);
+*/
+ return 0;
+}
+
+PUBLIC int player_clear(int p)
+{
+ player_free(p);
+ player_zero(p);
+ return 0;
+}
+
+PUBLIC int player_remove(int p)
+{
+ int i;
+
+ player_decline_offers(p, -1, -1);
+ player_withdraw_offers(p, -1, -1);
+ if (parray[p].simul_info.numBoards) { /* Player disconnected in middle of
+ simul */
+ for (i = 0; i < parray[p].simul_info.numBoards; i++) {
+ if (parray[p].simul_info.boards[i] >= 0) {
+ game_disconnect(parray[p].simul_info.boards[i], p);
+ }
+ }
+ }
+#if 0 /* was finished elsewhere */
+ if ((parray[p].game >=0) && (garray[parray[p].game].status == GAME_EXAMINE)) {
+ if (garray[parray[p].game].white == p) { /* owner of exam game */
+
+ /* not yet done */
+
+ }
+#endif
+
+ if (parray[p].game >=0) { /* Player disconnected in the middle of a
+ game! */
+ pprintf(parray[p].opponent, "Your opponent has lost contact or quit.");
+ game_disconnect(parray[p].game, p);
+ }
+/* ReallyRemoveOldGamesForPlayer(p); */
+ for (i = 0; i < p_num; i++) {
+ if (parray[i].status == PLAYER_EMPTY)
+ continue;
+ if (parray[i].last_tell == p)
+ parray[i].last_tell = -1;
+ if (parray[i].last_opponent == p)
+ parray[i].last_opponent = -1;
+ if (parray[i].partner == p) {
+ pprintf_prompt (i, "Your partner has disconnected.\n");
+ player_withdraw_offers(i, -1, PEND_BUGHOUSE);
+ player_decline_offers(i, -1, PEND_BUGHOUSE);
+ parray[i].partner = -1;
+ }
+ }
+ player_clear(p);
+ parray[p].status = PLAYER_EMPTY;
+/*** fprintf(stderr, "Removed parray[%d/%d]\n", p, p_num-1);*/
+ return 0;
+}
+
+void ReadV1PlayerFmt(int p,player *pp, FILE * fp, char *file, int version)
+{
+
+ int i,size_cens, size_noplay, size_not, size_gnot, size_chan, len;
+ int bs,ss,ws,ls,bugs;
+
+ char* tmp;
+ char tmp2[MAX_STRING_LENGTH];
+
+ fgets(tmp2, MAX_STRING_LENGTH, fp);
+ if (strcmp(tmp2,"NONE\n")) {
+ tmp2[strlen(tmp2)-1] = '\0';
+ pp->name = strdup (tmp2);
+ } else
+ pp->name = NULL;
+ fgets(tmp2, MAX_STRING_LENGTH, fp);
+ if (strcmp(tmp2,"NONE\n")) {
+ tmp2[strlen(tmp2)-1] = '\0';
+ pp->fullName = strdup (tmp2);
+ } else
+ pp->fullName = NULL;
+ fgets(tmp2, MAX_STRING_LENGTH, fp);
+ if (strcmp(tmp2,"NONE\n")) {
+ tmp2[strlen(tmp2)-1] = '\0';
+ pp->passwd = strdup (tmp2);
+ } else
+ pp->passwd = NULL;
+ fgets(tmp2, MAX_STRING_LENGTH, fp);
+ if (strcmp(tmp2,"NONE\n")) {
+ tmp2[strlen(tmp2)-1] = '\0';
+ pp->emailAddress = strdup (tmp2);
+ } else
+ pp->emailAddress = NULL;
+ if (fscanf(fp, "%u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %d\n",
+
+ &pp->s_stats.num, &pp->s_stats.win, &pp->s_stats.los,
+ &pp->s_stats.dra, &pp->s_stats.rating, &ss,
+ &pp->s_stats.ltime, &pp->s_stats.best, &pp->s_stats.whenbest,
+
+ &pp->b_stats.num, &pp->b_stats.win, &pp->b_stats.los,
+ &pp->b_stats.dra, &pp->b_stats.rating, &bs,
+ &pp->b_stats.ltime, &pp->b_stats.best, &pp->b_stats.whenbest,
+
+ &pp->w_stats.num, &pp->w_stats.win, &pp->w_stats.los,
+ &pp->w_stats.dra, &pp->w_stats.rating, &ws,
+ &pp->w_stats.ltime, &pp->w_stats.best, &pp->w_stats.whenbest,
+
+ &pp->l_stats.num, &pp->l_stats.win, &pp->l_stats.los,
+ &pp->l_stats.dra, &pp->l_stats.rating, &ls,
+ &pp->l_stats.ltime, &pp->l_stats.best, &pp->l_stats.whenbest,
+
+ &pp->bug_stats.num, &pp->bug_stats.win, &pp->bug_stats.los,
+ &pp->bug_stats.dra, &pp->bug_stats.rating, &bugs,
+ &pp->bug_stats.ltime, &pp->bug_stats.best, &pp->bug_stats.whenbest,
+ &pp->lastHost) != 46) {
+ fprintf(stderr,"Player %s is corrupt\n",parray[p].name);
+ return;
+ }
+
+ pp->b_stats.sterr = bs / 10.0;
+ pp->s_stats.sterr = ss / 10.0;
+ pp->w_stats.sterr = ws / 10.0;
+ pp->l_stats.sterr = ls / 10.0;
+ pp->bug_stats.sterr = bugs / 10.0;
+
+ fgets (tmp2, MAX_STRING_LENGTH, fp);
+ tmp2[strlen(tmp2)-1] = '\0';
+ pp->prompt = strdup(tmp2);
+ if (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\n",
+ &pp->open, &pp->rated, &pp->ropen, &pp->timeOfReg,
+ &pp->totalTime, &pp->bell, &pp->pgn, &pp->notifiedby,
+ &pp->i_login, &pp->i_game, &pp->i_shout, &pp->i_cshout,
+ &pp->i_tell, &pp->i_kibitz, &pp->private, &pp->jprivate,
+ &pp->automail, &pp->i_mailmess, &pp->style, &pp->d_time,
+ &pp->d_inc, &pp->d_height, &pp->d_width, &pp->language,
+ &pp->adminLevel, &pp->num_white, &pp->num_black, &pp->highlight,
+ &pp->num_comments,
+ &pp->num_plan, &pp->num_formula,&size_cens,
+ &size_not, &size_noplay,
+ &size_gnot, &pp->numAlias, &size_chan) != 37) {
+ fprintf(stderr,"Player %s is corrupt\n",parray[p].name);
+ return;
+ }
+
+ if (pp->num_plan > 0) {
+ for (i = 0; i < pp->num_plan; i++) {
+ fgets(tmp2, MAX_LINE_SIZE, fp);
+ if (!(len = strlen(tmp2))) {
+ fprintf(stderr, "FICS: Error bad plan in file %s\n", file);
+ i--;
+ pp->num_plan--;
+ } else {
+ tmp2[len - 1] = '\0'; /* Get rid of '\n' */
+ pp->planLines[i] = (len > 1) ? strdup(tmp2) : NULL;
+ }
+ }
+ }
+ if (pp->num_formula > 0) {
+ for (i = 0; i < pp->num_formula; i++) {
+ fgets(tmp2, MAX_LINE_SIZE, fp);
+ if (!(len = strlen(tmp2))) {
+ fprintf(stderr, "FICS: Error bad formula in file %s\n", file);
+ i--;
+ pp->num_formula--;
+ } else {
+ tmp2[len - 1] = '\0'; /* Get rid of '\n' */
+ pp->formulaLines[i] = (len > 1) ? strdup(tmp2) : NULL;
+ }
+ }
+ }
+ fgets(tmp2, MAX_LINE_SIZE, fp);
+ tmp2[strlen(tmp2) - 1] = '\0';
+ if (!strcmp (tmp2,"NONE"))
+ pp->formula = NULL;
+ else
+ pp->formula = strdup(tmp2);
+
+ if (pp->numAlias > 0) {
+ for (i = 0; i < pp->numAlias; i++) {
+ fgets(tmp2, MAX_LINE_SIZE, fp);
+ if (!(len = strlen(tmp2))) {
+ fprintf(stderr, "FICS: Error bad alias in file %s\n", file);
+ i--;
+ pp->numAlias--;
+ } else {
+ tmp2[len - 1] = '\0'; /* Get rid of '\n' */
+ tmp = tmp2;
+ tmp = eatword(tmp2);
+ *tmp = '\0';
+ tmp++;
+ tmp = eatwhite(tmp);
+ pp->alias_list[i].comm_name = strdup(tmp2);
+ pp->alias_list[i].alias = strdup(tmp);
+ }
+ }
+ }
+
+ while (size_cens--) {
+ fscanf(fp,"%s",tmp2);
+ list_add(p, L_CENSOR, tmp2);
+ }
+
+ while(size_not--) {
+ fscanf(fp,"%s",tmp2);
+ list_add(p, L_NOTIFY, tmp2);
+ }
+
+ while(size_noplay--) {
+ fscanf(fp,"%s",tmp2);
+ list_add(p, L_NOPLAY, tmp2);
+ }
+
+ while(size_gnot--) {
+ fscanf(fp,"%s",tmp2);
+ list_add(p, L_GNOTIFY, tmp2);
+ }
+
+ while(size_chan--) {
+ fscanf(fp,"%s",tmp2);
+ list_add(p, L_CHANNEL, tmp2);
+ }
+}
+
+PRIVATE int got_attr_value_player(int p, char *attr, char *value, FILE * fp, char *file)
+{
+ int i, len;
+ char tmp[MAX_LINE_SIZE], *tmp1;
+
+ if (!strcmp(attr, "name:")) {
+ parray[p].name = strdup(value);
+ } else if (!strcmp(attr, "password:")) {
+ parray[p].passwd = strdup(value);
+ } else if (!strcmp(attr, "fullname:")) {
+ parray[p].fullName = strdup(value);
+ } else if (!strcmp(attr, "email:")) {
+ parray[p].emailAddress = strdup(value);
+ } else if (!strcmp(attr, "prompt:")) {
+ parray[p].prompt = strdup(value);
+ } else if (!strcmp(attr, "s_num:")) {
+ parray[p].s_stats.num = atoi(value);
+ } else if (!strcmp(attr, "s_win:")) {
+ parray[p].s_stats.win = atoi(value);
+ } else if (!strcmp(attr, "s_loss:")) {
+ parray[p].s_stats.los = atoi(value);
+ } else if (!strcmp(attr, "s_draw:")) {
+ parray[p].s_stats.dra = atoi(value);
+ } else if (!strcmp(attr, "s_rating:")) {
+ parray[p].s_stats.rating = atoi(value);
+ } else if (!strcmp(attr, "s_sterr:")) {
+ parray[p].s_stats.sterr = (atoi(value) / 10.0);
+ } else if (!strcmp(attr, "s_ltime:")) {
+ parray[p].s_stats.ltime = atoi(value);
+ } else if (!strcmp(attr, "s_best:")) {
+ parray[p].s_stats.best = atoi(value);
+ } else if (!strcmp(attr, "s_wbest:")) {
+ parray[p].s_stats.whenbest = atoi(value);
+ } else if (!strcmp(attr, "b_num:")) {
+ parray[p].b_stats.num = atoi(value);
+ } else if (!strcmp(attr, "b_win:")) {
+ parray[p].b_stats.win = atoi(value);
+ } else if (!strcmp(attr, "b_loss:")) {
+ parray[p].b_stats.los = atoi(value);
+ } else if (!strcmp(attr, "b_draw:")) {
+ parray[p].b_stats.dra = atoi(value);
+ } else if (!strcmp(attr, "b_rating:")) {
+ parray[p].b_stats.rating = atoi(value);
+ } else if (!strcmp(attr, "b_sterr:")) {
+ parray[p].b_stats.sterr = (atoi(value) / 10.0);
+ } else if (!strcmp(attr, "b_ltime:")) {
+ parray[p].b_stats.ltime = atoi(value);
+ } else if (!strcmp(attr, "b_best:")) {
+ parray[p].b_stats.best = atoi(value);
+ } else if (!strcmp(attr, "b_wbest:")) {
+ parray[p].b_stats.whenbest = atoi(value);
+ } else if (!strcmp(attr, "w_num:")) {
+ parray[p].w_stats.num = atoi(value);
+ } else if (!strcmp(attr, "w_win:")) {
+ parray[p].w_stats.win = atoi(value);
+ } else if (!strcmp(attr, "w_loss:")) {
+ parray[p].w_stats.los = atoi(value);
+ } else if (!strcmp(attr, "w_draw:")) {
+ parray[p].w_stats.dra = atoi(value);
+ } else if (!strcmp(attr, "w_rating:")) {
+ parray[p].w_stats.rating = atoi(value);
+ } else if (!strcmp(attr, "w_sterr:")) {
+ parray[p].w_stats.sterr = (atoi(value) / 10.0);
+ } else if (!strcmp(attr, "w_ltime:")) {
+ parray[p].w_stats.ltime = atoi(value);
+ } else if (!strcmp(attr, "w_best:")) {
+ parray[p].w_stats.best = atoi(value);
+ } else if (!strcmp(attr, "w_wbest:")) {
+ parray[p].w_stats.whenbest = atoi(value);
+ } else if (!strcmp(attr, "open:")) {
+ parray[p].open = atoi(value);
+ } else if (!strcmp(attr, "rated:")) {
+ parray[p].rated = atoi(value);
+ } else if (!strcmp(attr, "ropen:")) {
+ parray[p].ropen = atoi(value);
+ } else if (!strcmp(attr, "bell:")) {
+ parray[p].bell = atoi(value);
+ } else if (!strcmp(attr, "pgn:")) {
+ parray[p].pgn = atoi(value);
+ } else if (!strcmp(attr, "timeofreg:")) {
+ parray[p].timeOfReg = atoi(value);
+ } else if (!strcmp(attr, "totaltime:")) {
+ parray[p].totalTime = atoi(value);
+ } else if (!strcmp(attr, "notifiedby:")) {
+ parray[p].notifiedby = atoi(value);
+ } else if (!strcmp(attr, "i_login:")) {
+ parray[p].i_login = atoi(value);
+ } else if (!strcmp(attr, "i_game:")) {
+ parray[p].i_game = atoi(value);
+ } else if (!strcmp(attr, "i_shout:")) {
+ parray[p].i_shout = atoi(value);
+ } else if (!strcmp(attr, "i_cshout:")) {
+ parray[p].i_cshout = atoi(value);
+ } else if (!strcmp(attr, "i_tell:")) {
+ parray[p].i_tell = atoi(value);
+ } else if (!strcmp(attr, "i_kibitz:")) {
+ parray[p].i_kibitz = atoi(value);
+ } else if (!strcmp(attr, "kiblevel:")) {
+ parray[p].kiblevel = atoi(value);
+ } else if (!strcmp(attr, "private:")) {
+ parray[p].private = atoi(value);
+ } else if (!strcmp(attr, "jprivate:")) {
+ parray[p].jprivate = atoi(value);
+ } else if (!strcmp(attr, "automail:")) {
+ parray[p].automail = atoi(value);
+ } else if (!strcmp(attr, "i_mailmess:")) {
+ parray[p].i_mailmess = atoi(value);
+ } else if (!strcmp(attr, "style:")) {
+ parray[p].style = atoi(value);
+ } else if (!strcmp(attr, "d_time:")) {
+ parray[p].d_time = atoi(value);
+ } else if (!strcmp(attr, "d_inc:")) {
+ parray[p].d_inc = atoi(value);
+ } else if (!strcmp(attr, "d_height:")) {
+ parray[p].d_height = atoi(value);
+ } else if (!strcmp(attr, "d_width:")) {
+ parray[p].d_width = atoi(value);
+ } else if (!strcmp(attr, "language:")) {
+ parray[p].language = atoi(value);
+ } else if (!strcmp(attr, "admin_level:")) {
+ parray[p].adminLevel = atoi(value);
+ if (parray[p].adminLevel >= ADMIN_ADMIN)
+ parray[p].i_admin = 1;
+ } else if (!strcmp(attr, "i_admin:")) {
+/* parray[p].i_admin = atoi(value); */
+ } else if (!strcmp(attr, "computer:")) {
+/* parray[p].computer = atoi(value); */
+ } else if (!strcmp(attr, "black_games:")) {
+ parray[p].num_black = atoi(value);
+ } else if (!strcmp(attr, "white_games:")) {
+ parray[p].num_white = atoi(value);
+ } else if (!strcmp(attr, "uscf:")) {
+/* parray[p].uscfRating = atoi(value); */
+ } else if (!strcmp(attr, "muzzled:")) { /* ignore these: obsolete */
+ } else if (!strcmp(attr, "cmuzzled:")) { /* ignore these: obsolete */
+ } else if (!strcmp(attr, "highlight:")) {
+ parray[p].highlight = atoi(value);
+ } else if (!strcmp(attr, "network:")) {
+/* parray[p].network_player = atoi(value); */
+ } else if (!strcmp(attr, "lasthost:")) {
+ parray[p].lastHost = atoi(value);
+ } else if (!strcmp(attr, "channel:")) {
+ list_addsub(p,"channel",value, 1);
+ } else if (!strcmp(attr, "num_comments:")) {
+ parray[p].num_comments = atoi(value);
+ } else if (!strcmp(attr, "num_plan:")) {
+ parray[p].num_plan = atoi(value);
+ if (parray[p].num_plan > 0) {
+ for (i = 0; i < parray[p].num_plan; i++) {
+ fgets(tmp, MAX_LINE_SIZE, fp);
+ if (!(len = strlen(tmp))) {
+ fprintf(stderr, "FICS: Error bad plan in file %s\n", file);
+ i--;
+ parray[p].num_plan--;
+ } else {
+ tmp[len - 1] = '\0'; /* Get rid of '\n' */
+ parray[p].planLines[i] = (len > 1) ? strdup(tmp) : NULL;
+ }
+ }
+ }
+ } else if (!strcmp(attr, "num_formula:")) {
+ parray[p].num_formula = atoi(value);
+ if (parray[p].num_formula > 0) {
+ for (i = 0; i < parray[p].num_formula; i++) {
+ fgets(tmp, MAX_LINE_SIZE, fp);
+ if (!(len = strlen(tmp))) {
+ fprintf(stderr, "FICS: Error bad formula in file %s\n", file);
+ i--;
+ parray[p].num_formula--;
+ } else {
+ tmp[len - 1] = '\0'; /* Get rid of '\n' */
+ parray[p].formulaLines[i] = (len > 1) ? strdup(tmp) : NULL;
+ }
+ }
+ }
+ } else if (!strcmp(attr, "formula:")) {
+ parray[p].formula = strdup(value);
+ } else if (!strcmp(attr, "num_alias:")) {
+ parray[p].numAlias = atoi(value);
+ if (parray[p].numAlias > 0) {
+ for (i = 0; i < parray[p].numAlias; i++) {
+ fgets(tmp, MAX_LINE_SIZE, fp);
+ if (!(len = strlen(tmp))) {
+ fprintf(stderr, "FICS: Error bad alias in file %s\n", file);
+ i--;
+ parray[p].numAlias--;
+ } else {
+ tmp[len - 1] = '\0'; /* Get rid of '\n' */
+ tmp1 = tmp;
+ tmp1 = eatword(tmp1);
+ *tmp1 = '\0';
+ tmp1++;
+ tmp1 = eatwhite(tmp1);
+ parray[p].alias_list[i].comm_name = strdup(tmp);
+ parray[p].alias_list[i].alias = strdup(tmp1);
+ }
+ }
+ }
+ } else if (!strcmp(attr, "num_censor:")) {
+ i = atoi(value);
+ while (i--) {
+ fgets(tmp, MAX_LINE_SIZE, fp);
+ if ((!(len = strlen(tmp))) || (len == 1)) { /* blank lines do occur!! */
+ fprintf(stderr, "FICS: Error bad censor in file %s\n", file);
+ } else {
+ tmp[len - 1] = '\0'; /* Get rid of '\n' */
+ list_add(p, L_CENSOR, tmp);
+ }
+ }
+ } else if (!strcmp(attr, "num_notify:")) {
+ i = atoi(value);
+ while(i--) {
+ fgets(tmp, MAX_LINE_SIZE, fp);
+ if ((!(len = strlen(tmp))) || (len == 1)) { /* blank lines do occur!! */
+ fprintf(stderr, "FICS: Error bad notify in file %s\n", file);
+ } else {
+ tmp[len - 1] = '\0'; /* Get rid of '\n' */
+ list_add(p, L_NOTIFY, tmp);
+ }
+ }
+ } else if (!strcmp(attr, "num_noplay:")) {
+ i = atoi(value);
+ while(i--) {
+ fgets(tmp, MAX_LINE_SIZE, fp);
+ if ((!(len = strlen(tmp))) || (len == 1)) { /* blank lines do occur!! */
+ fprintf(stderr, "FICS: Error bad noplay in file %s\n", file);
+ } else {
+ tmp[len - 1] = '\0'; /* Get rid of '\n' */
+ list_add(p, L_NOPLAY, tmp);
+ }
+ }
+ } else if (!strcmp(attr, "num_gnotify:")) {
+ i = atoi(value);
+ while(i--) {
+ fgets(tmp, MAX_LINE_SIZE, fp);
+ if ((!(len = strlen(tmp))) || (len == 1)) { /* blank lines do occur!! */
+ fprintf(stderr, "FICS: Error bad gnotify in file %s\n", file);
+ } else {
+ tmp[len - 1] = '\0'; /* Get rid of '\n' */
+ list_add(p, L_GNOTIFY, tmp);
+ }
+ }
+ } else {
+ fprintf(stderr, "FICS: Error bad attribute >%s< from file %s\n", attr, file);
+ }
+ return 0;
+}
+
+PUBLIC int player_read(int p, char *name)
+{
+ char fname[MAX_FILENAME_SIZE];
+ char line[MAX_LINE_SIZE];
+ char *attr, *value;
+ FILE *fp;
+ int len;
+ int version = 0;
+
+ parray[p].login = stolower(strdup(name));
+
+ sprintf(fname, "%s/%c/%s", player_dir, parray[p].login[0], parray[p].login);
+ fp = fopen(fname, "r");
+
+ if (!fp) { /* unregistered player */
+ parray[p].name = strdup(name);
+ parray[p].registered = 0;
+ return -1;
+ }
+
+ parray[p].registered = 1; /* lets load the file */
+
+ fgets(line, MAX_LINE_SIZE, fp); /* ok so which version file? */
+
+ if (line[0] == 'v') {
+ sscanf(line, "%*c %d", &version);
+ }
+
+ if (version > 0) {
+ ReadV1PlayerFmt(p,&parray[p], fp, fname, version); /* Quick method */
+ }
+ else /* do it the old SLOW way */
+ do {
+ if (feof(fp))
+ break;
+ if ((len = strlen(line)) <= 1)
+ continue;
+ line[len - 1] = '\0';
+ attr = eatwhite(line);
+ if (attr[0] == '#')
+ continue; /* Comment */
+ value = eatword(attr);
+ if (!*value) {
+ fprintf(stderr, "FICS: Error reading file %s\n", fname);
+ continue;
+ }
+ *value = '\0';
+ value++;
+ value = eatwhite(value);
+ stolower(attr);
+ got_attr_value_player(p, attr, value, fp, fname);
+ fgets(line, MAX_LINE_SIZE, fp);
+ } while (!feof(fp));
+
+ fclose(fp);
+
+ if (version == 0)
+ player_save (p); /* ensure old files are quickly converted eg when someone
+ fingers */
+ if (!parray[p].name) {
+ parray[p].name = strdup(name);
+ pprintf(p, "\n*** WARNING: Your Data file is corrupt. Please tell an admin ***\n");
+ }
+ return 0;
+}
+
+PUBLIC int player_delete(int p)
+{
+ char fname[MAX_FILENAME_SIZE];
+
+ if (!parray[p].registered) { /* Player must not be registered */
+ return -1;
+ }
+/* if (iamserver)
+ sprintf(fname, "%s.server/%c/%s", player_dir, parray[p].login[0], parray[p].login);
+ else */
+ sprintf(fname, "%s/%c/%s", player_dir, parray[p].login[0], parray[p].login);
+ unlink(fname);
+ return 0;
+}
+
+PUBLIC int player_markdeleted(int p)
+{
+ FILE *fp;
+ char fname[MAX_FILENAME_SIZE], fname2[MAX_FILENAME_SIZE];
+
+ if (!parray[p].registered) { /* Player must not be registered */
+ return -1;
+ }
+/* if (iamserver) {
+ sprintf(fname, "%s.server/%c/%s", player_dir, parray[p].login[0], parray[p].login);
+ sprintf(fname2, "%s.server/%c/%s.delete", player_dir, parray[p].login[0], parray[p].login);
+ } else {*/
+ sprintf(fname, "%s/%c/%s", player_dir, parray[p].login[0], parray[p].login);
+ sprintf(fname2, "%s/%c/%s.delete", player_dir, parray[p].login[0], parray[p].login);
+/* } */
+ rename(fname, fname2);
+ fp = fopen(fname2, "a"); /* Touch the file */
+ if (fp) {
+ fprintf(fp, "\n");
+ fclose(fp);
+ }
+ return 0;
+}
+
+void WritePlayerFile(FILE* fp, int p)
+{
+ int i;
+ player *pp = &parray[p];
+
+ fprintf (fp, "v %d\n", PLAYER_VERSION);
+ if (pp->name == NULL) /* This should never happen! */
+ fprintf (fp,"NONE\n");
+ else
+ fprintf(fp, "%s\n", pp->name);
+ if (pp->fullName == NULL)
+ fprintf (fp,"NONE\n");
+ else
+ fprintf(fp, "%s\n", pp->fullName);
+ if (pp->passwd == NULL)
+ fprintf (fp,"NONE\n");
+ else
+ fprintf(fp, "%s\n", pp->passwd);
+ if (pp->emailAddress == NULL)
+ fprintf (fp,"NONE\n");
+ else
+ fprintf(fp, "%s\n", pp->emailAddress);
+ fprintf(fp, "%u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %d\n",
+
+ pp->s_stats.num, pp->s_stats.win, pp->s_stats.los,
+ pp->s_stats.dra, pp->s_stats.rating, (int) (pp->s_stats.sterr * 10.0),
+ pp->s_stats.ltime, pp->s_stats.best, pp->s_stats.whenbest,
+
+ pp->b_stats.num, pp->b_stats.win, pp->b_stats.los,
+ pp->b_stats.dra, pp->b_stats.rating, (int) (pp->b_stats.sterr * 10.0),
+ pp->b_stats.ltime, pp->b_stats.best, pp->b_stats.whenbest,
+
+ pp->w_stats.num, pp->w_stats.win, pp->w_stats.los,
+ pp->w_stats.dra, pp->w_stats.rating, (int) (pp->w_stats.sterr * 10.0),
+ pp->w_stats.ltime, pp->w_stats.best, pp->w_stats.whenbest,
+
+ pp->l_stats.num, pp->l_stats.win, pp->l_stats.los,
+ pp->l_stats.dra, pp->l_stats.rating, (int) (pp->l_stats.sterr * 10.0),
+ pp->l_stats.ltime, pp->l_stats.best, pp->l_stats.whenbest,
+
+ pp->bug_stats.num, pp->bug_stats.win, pp->bug_stats.los,
+ pp->bug_stats.dra, pp->bug_stats.rating, (int) (pp->bug_stats.sterr * 10.0),
+ pp->bug_stats.ltime, pp->bug_stats.best, pp->bug_stats.whenbest,
+ pp->lastHost);
+
+ fprintf (fp, "%s\n", pp->prompt);
+ fprintf (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\n",
+ pp->open, pp->rated, pp->ropen, pp->timeOfReg,
+ pp->totalTime, pp->bell, pp->pgn, pp->notifiedby,
+ pp->i_login, pp->i_game, pp->i_shout, pp->i_cshout,
+ pp->i_tell, pp->i_kibitz, pp->private, pp->jprivate,
+ pp->automail, pp->i_mailmess, pp->style, pp->d_time,
+ pp->d_inc, pp->d_height, pp->d_width, pp->language,
+ pp->adminLevel, pp->num_white, pp->num_black, pp->highlight,
+ pp->num_comments,
+ pp->num_plan, pp->num_formula,list_size(p, L_CENSOR),
+ list_size(p, L_NOTIFY), list_size(p, L_NOPLAY),
+ list_size(p, L_GNOTIFY), pp->numAlias, list_size(p, L_CHANNEL ));
+ for (i = 0; i < pp->num_plan; i++)
+ fprintf(fp, "%s\n", (pp->planLines[i] ? pp->planLines[i] : ""));
+ for (i = 0; i < pp->num_formula; i++)
+ fprintf(fp, "%s\n", (pp->formulaLines[i] ? pp->formulaLines[i] : ""));
+ if (parray[p].formula != NULL)
+ fprintf(fp, "%s\n", pp->formula);
+ else
+ fprintf(fp, "NONE\n");
+ for (i = 0; i < pp->numAlias; i++)
+ fprintf(fp, "%s %s\n", pp->alias_list[i].comm_name,
+ pp->alias_list[i].alias);
+
+ list_print(fp, p, L_CENSOR);
+ list_print(fp, p, L_NOTIFY);
+ list_print(fp, p, L_NOPLAY);
+ list_print(fp, p, L_GNOTIFY);
+ list_print(fp, p, L_CHANNEL);
+}
+
+ #if 0 /* the old way */
+ if (parray[p].name)
+ fprintf(fp, "Name: %s\n", parray[p].name);
+ if (parray[p].fullName)
+ fprintf(fp, "Fullname: %s\n", parray[p].fullName);
+ if (parray[p].passwd)
+ fprintf(fp, "Password: %s\n", parray[p].passwd);
+ if (parray[p].emailAddress)
+ fprintf(fp, "Email: %s\n", parray[p].emailAddress);
+ fprintf(fp, "S_NUM: %u\n", parray[p].s_stats.num);
+ fprintf(fp, "S_WIN: %u\n", parray[p].s_stats.win);
+ fprintf(fp, "S_LOSS: %u\n", parray[p].s_stats.los);
+ fprintf(fp, "S_DRAW: %u\n", parray[p].s_stats.dra);
+ fprintf(fp, "S_RATING: %u\n", parray[p].s_stats.rating);
+ fprintf(fp, "S_STERR: %u\n", (int) (parray[p].s_stats.sterr * 10.0));
+ fprintf(fp, "S_LTIME: %u\n", parray[p].s_stats.ltime);
+ fprintf(fp, "S_BEST: %u\n", parray[p].s_stats.best);
+ fprintf(fp, "S_WBEST: %u\n", parray[p].s_stats.whenbest);
+ fprintf(fp, "B_NUM: %u\n", parray[p].b_stats.num);
+ fprintf(fp, "B_WIN: %u\n", parray[p].b_stats.win);
+ fprintf(fp, "B_LOSS: %u\n", parray[p].b_stats.los);
+ fprintf(fp, "B_DRAW: %u\n", parray[p].b_stats.dra);
+ fprintf(fp, "B_RATING: %u\n", parray[p].b_stats.rating);
+ fprintf(fp, "B_STERR: %u\n", (int) (parray[p].b_stats.sterr * 10.0));
+ fprintf(fp, "B_LTIME: %u\n", parray[p].b_stats.ltime);
+ fprintf(fp, "B_BEST: %u\n", parray[p].b_stats.best);
+ fprintf(fp, "B_WBEST: %u\n", parray[p].b_stats.whenbest);
+ fprintf(fp, "W_NUM: %u\n", parray[p].w_stats.num);
+ fprintf(fp, "W_WIN: %u\n", parray[p].w_stats.win);
+ fprintf(fp, "W_LOSS: %u\n", parray[p].w_stats.los);
+ fprintf(fp, "W_DRAW: %u\n", parray[p].w_stats.dra);
+ fprintf(fp, "W_RATING: %u\n", parray[p].w_stats.rating);
+ fprintf(fp, "W_STERR: %u\n", (int) (parray[p].w_stats.sterr * 10.0));
+ fprintf(fp, "W_LTIME: %u\n", parray[p].w_stats.ltime);
+ fprintf(fp, "W_BEST: %u\n", parray[p].w_stats.best);
+ fprintf(fp, "W_WBEST: %u\n", parray[p].w_stats.whenbest);
+ /* fprintf(fp, "Network: %d\n", parray[p].network_player); */
+ /* if (iamserver)
+ return 0; *//* No need to write any more */
+
+ fprintf(fp, "LastHost: %d\n", parray[p].lastHost);
+ if (parray[p].prompt != def_prompt)
+ fprintf(fp, "Prompt: %s\n", parray[p].prompt);
+ fprintf(fp, "Open: %d\n", parray[p].open);
+ fprintf(fp, "Rated: %d\n", parray[p].rated);
+ fprintf(fp, "Ropen: %d\n", parray[p].ropen);
+ fprintf(fp, "timeofreg: %d\n", parray[p].timeOfReg);
+ fprintf(fp, "totaltime: %d\n", parray[p].totalTime);
+ fprintf(fp, "Bell: %d\n", parray[p].bell);
+ fprintf(fp, "PGN: %d\n", parray[p].pgn);
+ fprintf(fp, "Notifiedby: %d\n", parray[p].notifiedby);
+ fprintf(fp, "I_Login: %d\n", parray[p].i_login);
+ fprintf(fp, "I_Game: %d\n", parray[p].i_game);
+ fprintf(fp, "I_Shout: %d\n", parray[p].i_shout);
+ fprintf(fp, "I_CShout: %d\n", parray[p].i_cshout);
+ fprintf(fp, "I_Tell: %d\n", parray[p].i_tell);
+ fprintf(fp, "I_Kibitz: %d\n", parray[p].i_kibitz);
+ fprintf(fp, "Private: %d\n", parray[p].private);
+ fprintf(fp, "JPrivate: %d\n", parray[p].jprivate);
+ fprintf(fp, "Automail: %d\n", parray[p].automail);
+ fprintf(fp, "I_Mailmess: %d\n", parray[p].i_mailmess);
+ fprintf(fp, "Style: %d\n", parray[p].style);
+ fprintf(fp, "D_Time: %d\n", parray[p].d_time);
+ fprintf(fp, "D_Inc: %d\n", parray[p].d_inc);
+ fprintf(fp, "D_Height: %d\n", parray[p].d_height);
+ fprintf(fp, "D_Width: %d\n", parray[p].d_width);
+ fprintf(fp, "Language: %d\n", parray[p].language);
+ fprintf(fp, "Admin_level: %d\n", parray[p].adminLevel);
+ /* fprintf(fp, "I_Admin: %d\n", parray[p].i_admin); */
+ /* if (parray[p].computer) {
+ fprintf(fp, "Computer: %d\n", parray[p].computer);
+ } */
+ fprintf(fp, "White_Games: %d\n", parray[p].num_white);
+ fprintf(fp, "Black_Games: %d\n", parray[p].num_black);
+ /* fprintf(fp, "USCF: %d\n", parray[p].uscfRating); */
+ fprintf(fp, "Highlight: %d\n", parray[p].highlight);
+ fprintf(fp, "Num_Comments: %d\n", parray[p].num_comments);
+ for (i = 0; i < MAX_CHANNELS; i++) {
+ if (on_channel(i, p))
+ fprintf(fp, "Channel: %d\n", i);
+ }
+ fprintf(fp, "Num_plan: %d\n", parray[p].num_plan);
+ for (i = 0; i < parray[p].num_plan; i++)
+ fprintf(fp, "%s\n", (parray[p].planLines[i] ? parray[p].planLines[i] : ""));
+ fprintf(fp, "Num_formula: %d\n", parray[p].num_formula);
+ for (i = 0; i < parray[p].num_formula; i++)
+ fprintf(fp, "%s\n", (parray[p].formulaLines[i] ? parray[p].formulaLines[i] : ""));
+ if (parray[p].formula != NULL)
+ fprintf(fp, "Formula: %s\n", parray[p].formula);
+ fprintf(fp, "Num_censor: %d\n", list_size(p, L_CENSOR));
+ list_print(fp, p, L_CENSOR);
+ fprintf(fp, "Num_notify: %d\n", list_size(p, L_NOTIFY));
+ list_print(fp, p, L_NOTIFY);
+ fprintf(fp, "Num_noplay: %d\n", list_size(p, L_NOPLAY));
+ list_print(fp, p, L_NOPLAY);
+ fprintf(fp, "Num_gnotify: %d\n", list_size(p, L_GNOTIFY));
+ list_print(fp, p, L_GNOTIFY);
+ fprintf(fp, "Num_alias: %d\n", parray[p].numAlias);
+ for (i = 0; i < parray[p].numAlias; i++)
+ fprintf(fp, "%s %s\n", parray[p].alias_list[i].comm_name,
+ parray[p].alias_list[i].alias);
+ }
+ #endif
+
+PUBLIC int player_save(int p)
+{
+ FILE *fp;
+ char fname[MAX_FILENAME_SIZE];
+
+ if (!parray[p].registered) { /* Player must not be registered */
+ return -1;
+ }
+ if (parray[p].name == NULL) { /* fixes a bug if name is null */
+ pprintf(p, "WARNING: Your player file could not be updated, due to corrupt data.\n");
+ return -1;
+ }
+ if (strcasecmp(parray[p].login, parray[p].name)) {
+ pprintf(p, "WARNING: Your player file could not be updated, due to corrupt data.\n");
+ return -1;
+ }
+
+ sprintf(fname, "%s/%c/%s", player_dir, parray[p].login[0], parray[p].login);
+ fp = fopen(fname, "w");
+ if (!fp) {
+ fprintf(stderr, "FICS: Problem opening file %s for write\n", fname);
+ return -1;
+ }
+
+ WritePlayerFile(fp,p);
+ fclose(fp);
+ return 0;
+}
+
+PUBLIC int player_find(int fd)
+{
+ int i;
+
+ for (i = 0; i < p_num; i++) {
+ if (parray[i].status == PLAYER_EMPTY)
+ continue;
+ if (parray[i].socket == fd)
+ return i;
+ }
+ return -1;
+}
+
+PUBLIC int player_find_bylogin(char *name)
+{
+ int i;
+
+ for (i = 0; i < p_num; i++) {
+ if ((parray[i].status == PLAYER_EMPTY) ||
+ (parray[i].status == PLAYER_LOGIN) ||
+ (parray[i].status == PLAYER_PASSWORD))
+ continue;
+ if (!parray[i].login)
+ continue;
+ if (!strcmp(parray[i].login, name))
+ return i;
+ }
+ return -1;
+}
+
+PUBLIC int player_find_part_login(char *name)
+{
+ int i;
+ int found = -1;
+
+ i = player_find_bylogin(name);
+ if (i >= 0)
+ return i;
+ for (i = 0; i < p_num; i++) {
+ if ((parray[i].status == PLAYER_EMPTY) ||
+ (parray[i].status == PLAYER_LOGIN) ||
+ (parray[i].status == PLAYER_PASSWORD))
+ continue;
+ if (!parray[i].login)
+ continue;
+ if (!strncmp(parray[i].login, name, strlen(name))) {
+ if (found >= 0) { /* Ambiguous */
+ return -2;
+ }
+ found = i;
+ }
+ }
+ return found;
+}
+
+PUBLIC int player_censored(int p, int p1)
+{
+ if (in_list(p, L_CENSOR, parray[p1].login))
+ return 1;
+ else
+ return 0;
+}
+
+/* is p1 on p's notify list? */
+PUBLIC int player_notified(int p, int p1)
+{
+ if (!parray[p1].registered)
+ return 0;
+
+ /* possible bug: p has just arrived! */
+ if (!parray[p].name)
+ return 0;
+
+ return (in_list(p, L_NOTIFY, parray[p1].login));
+}
+
+PUBLIC void player_notify_departure(int p)
+/* Notify those with notifiedby set on a departure */
+{
+ int p1;
+
+ if (!parray[p].registered)
+ return;
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (parray[p1].notifiedby && !player_notified(p1, p) && player_notified(p, p1)
+ && (parray[p1].status == PLAYER_PROMPT)) {
+ if (parray[p1].bell)
+ pprintf_noformat(p1, "\007");
+ pprintf(p1, "\nNotification: ");
+ pprintf_highlight(p1, "%s", parray[p].name);
+ pprintf_prompt(p1, " has departed and isn't on your notify list.\n");
+ }
+ }
+}
+
+PUBLIC int player_notify_present(int p)
+/* output Your arrival was notified by..... */
+/* also notify those with notifiedby set if necessary */
+{
+ int p1;
+ int count = 0;
+
+ if (!parray[p].registered)
+ return count;
+ for (p1 = 0; p1 < p_num; p1++) {
+ if ((player_notified(p, p1)) && (parray[p1].status == PLAYER_PROMPT)) {
+ if (!count) {
+ pprintf(p, "Present company includes:");
+ }
+ count++;
+ pprintf(p, " %s", parray[p1].name);
+ if ((parray[p1].notifiedby) && (!player_notified(p1, p))
+ && (parray[p1].status == PLAYER_PROMPT)) {
+ if (parray[p1].bell)
+ pprintf_noformat(p1, "\007");
+ pprintf(p1, "\nNotification: ");
+ pprintf_highlight(p1, "%s", parray[p].name);
+ pprintf_prompt(p1, " has arrived and isn't on your notify list.\n");
+ }
+ }
+ }
+ if (count)
+ pprintf(p, ".\n");
+ return count;
+}
+
+PUBLIC int player_notify(int p, char *note1, char *note2)
+/* notify those interested that p has arrived/departed */
+{
+ int p1;
+ int count = 0;
+
+ if (!parray[p].registered)
+ return count;
+ for (p1 = 0; p1 < p_num; p1++) {
+ if ((player_notified(p1, p)) && (parray[p1].status == PLAYER_PROMPT)) {
+ if (parray[p1].bell)
+ pprintf_noformat(p1, "\007");
+ pprintf(p1, "\nNotification: ");
+ pprintf_highlight(p1, "%s", parray[p].name);
+ pprintf_prompt(p1, " has %s.\n", note1);
+ if (!count) {
+ pprintf(p, "Your %s was noted by:", note2);
+ }
+ count++;
+ pprintf(p, " %s", parray[p1].name);
+ }
+ }
+ if (count)
+ pprintf(p, ".\n");
+ return count;
+}
+
+/* Show adjourned games upon logon. connex/sous@ipp.tu-clausthal.de
+24.10.1995 */
+PUBLIC int showstored(int p)
+{
+ DIR *dirp;
+#ifdef USE_DIRENT
+ struct dirent *dp;
+#else
+ struct direct *dp;
+#endif
+ int c=0,p1;
+ char dname[MAX_FILENAME_SIZE];
+ multicol *m = multicol_start(50); /* Limit to 50, should be enough*/
+
+ sprintf(dname, "%s/%c", adj_dir, parray[p].login[0]);
+ dirp = opendir(dname);
+ if (!dirp) {
+ multicol_end(m);
+ return COM_OK;
+ }
+ for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
+ if (file_has_pname(dp->d_name, parray[p].login)) {
+ if (strcmp(file_wplayer(dp->d_name),parray[p].login) != 0) {
+ p1=player_find_bylogin(file_wplayer(dp->d_name));
+ } else {
+ p1=player_find_bylogin(file_bplayer(dp->d_name));
+ }
+ if (p1>=0) {
+ if (c<50)
+ multicol_store(m,parray[p1].name);
+ pprintf(p1,"\nNotification: ");
+ pprintf_highlight(p1,"%s",parray[p].name);
+ pprintf_prompt(p1,", who has an adjourned game with you, has arrived.\n");
+ c++;
+ }
+ }
+ }
+ closedir(dirp);
+ if (c == 1) {
+ pprintf(p, "1 player, who has an adjourned game with you, is online:\007");
+ } else if (c > 1) {
+ pprintf(p, "\n%d players, who have an adjourned game with you, are online:\007",c);
+ }
+ if (c != 0)
+ multicol_pprint(m,p,parray[p].d_width,2);
+ multicol_end(m);
+/* pprintf(p,"\n"); */
+ return COM_OK;
+}
+
+
+PUBLIC int player_count(int CountAdmins)
+{
+ int count;
+ int i;
+
+ for (count = 0, i = 0; i < p_num; i++) {
+ if ((parray[i].status == PLAYER_PROMPT) &&
+ (CountAdmins || !in_list(i, L_ADMIN, parray[i].name)))
+ count++;
+ }
+ if (count > player_high)
+ player_high = count;
+
+ return count;
+}
+
+PUBLIC int player_idle(int p)
+{
+ if (parray[p].status != PLAYER_PROMPT)
+ return time(0) - parray[p].logon_time;
+ else
+ return time(0) - parray[p].last_command_time;
+}
+
+PUBLIC int player_ontime(int p)
+{
+ return time(0) - parray[p].logon_time;
+}
+
+PRIVATE void write_p_inout(int inout, int p, char *file, int maxlines)
+{
+ FILE *fp;
+
+ fp = fopen(file, "a");
+ if (!fp)
+ return;
+ fprintf(fp, "%d %s %d %d %s\n", inout, parray[p].name, (int) time(0),
+ parray[p].registered, dotQuad(parray[p].thisHost));
+ fclose(fp);
+ if (maxlines)
+ truncate_file(file, maxlines);
+}
+
+PUBLIC void player_write_login(int p)
+{
+ char fname[MAX_FILENAME_SIZE];
+
+ if (parray[p].registered) {
+ sprintf(fname, "%s/player_data/%c/%s.%s", stats_dir, parray[p].login[0], parray[p].login, STATS_LOGONS);
+ write_p_inout(P_LOGIN, p, fname, 8);
+ }
+ sprintf(fname, "%s/%s", stats_dir, STATS_LOGONS);
+ write_p_inout(P_LOGIN, p, fname, 30);
+ /* added complete login/logout log to "logons.log" file */
+ sprintf(fname, "%s/%s", stats_dir, "logons.log");
+ write_p_inout(P_LOGIN, p, fname, 0);
+}
+
+PUBLIC void player_write_logout(int p)
+{
+ char fname[MAX_FILENAME_SIZE];
+
+ if (parray[p].registered) {
+ sprintf(fname, "%s/player_data/%c/%s.%s", stats_dir, parray[p].login[0], parray[p].login, STATS_LOGONS);
+ write_p_inout(P_LOGOUT, p, fname, 8);
+ }
+ sprintf(fname, "%s/%s", stats_dir, STATS_LOGONS);
+ write_p_inout(P_LOGOUT, p, fname, 30);
+ /* added complete login/logout log to "logons.log" file */
+ sprintf(fname, "%s/%s", stats_dir, "logons.log");
+ write_p_inout(P_LOGOUT, p, fname, 0);
+}
+
+PUBLIC int player_lastconnect(int p)
+{
+ char fname[MAX_FILENAME_SIZE];
+ FILE *fp;
+ int inout, thetime, registered;
+ int last = 0;
+ char loginName[MAX_LOGIN_NAME];
+ char ipstr[20];
+
+ sprintf(fname, "%s/player_data/%c/%s.%s", stats_dir,
+ parray[p].login[0], parray[p].login, STATS_LOGONS);
+ fp = fopen(fname, "r");
+ if (!fp)
+ return 0;
+ inout = 1;
+ while (!feof(fp)) {
+ if (inout == P_LOGIN)
+ last = thetime;
+ if (fscanf(fp, "%d %s %d %d %s\n", &inout, loginName, &thetime,
+ &registered, ipstr) != 5) {
+ fprintf(stderr, "FICS: Error in login info format. %s\n", fname);
+ fclose(fp);
+ return 0;
+ }
+ }
+ fclose(fp);
+ return last;
+}
+
+PUBLIC int player_lastdisconnect(int p)
+{
+ char fname[MAX_FILENAME_SIZE];
+ FILE *fp;
+ int inout, thetime, registered;
+ int last = 0;
+ char ipstr[20];
+ char loginName[MAX_LOGIN_NAME];
+
+ sprintf(fname, "%s/player_data/%c/%s.%s", stats_dir, parray[p].login[0], parray[p].login, STATS_LOGONS);
+ fp = fopen(fname, "r");
+ if (!fp)
+ return 0;
+ while (!feof(fp)) {
+ if (fscanf(fp, "%d %s %d %d %s\n", &inout, loginName, &thetime, &registered, ipstr) != 5) {
+ fprintf(stderr, "FICS: Error in login info format. %s\n", fname);
+ fclose(fp);
+ return 0;
+ }
+ if (inout == P_LOGOUT)
+ last = thetime;
+ }
+ fclose(fp);
+ return last;
+}
+
+PUBLIC void player_pend_print(int p, pending *pend)
+{
+ char outstr[200];
+ char tmp[200];
+
+ if (p == pend->whofrom) {
+ sprintf(outstr, "You are offering ");
+ } else {
+ sprintf(outstr, "%s is offering ", parray[pend->whofrom].name);
+ }
+
+ if (p == pend->whoto) {
+ strcpy(tmp, "");
+ } else {
+ sprintf(tmp, "%s ", parray[pend->whoto].name);
+ }
+
+ strcat(outstr, tmp);
+ switch (pend->type) {
+ case PEND_MATCH:
+ sprintf(tmp, "%s.", game_str(pend->param5, pend->param1 * 60, pend->param2, pend->param3 * 60, pend->param4, pend->char1, pend->char2));
+ break;
+ case PEND_DRAW:
+ sprintf(tmp, "a draw.\n");
+ break;
+ case PEND_PAUSE:
+ sprintf(tmp, "to pause the clock.\n");
+ break;
+ case PEND_ABORT:
+ sprintf(tmp, "to abort the game.\n");
+ break;
+ case PEND_TAKEBACK:
+ sprintf(tmp, "to takeback the last %d half moves.\n", pend->param1);
+ break;
+ case PEND_SIMUL:
+ sprintf(tmp, "to play a simul match.\n");
+ break;
+ case PEND_SWITCH:
+ sprintf(tmp, "to switch sides.\n");
+ break;
+ case PEND_ADJOURN:
+ sprintf(tmp, "an adjournment.\n");
+ break;
+ case PEND_PARTNER:
+ sprintf(tmp, "to be bughouse partners.\n");
+ break;
+ }
+ strcat(outstr, tmp);
+ pprintf(p, "%s\n", outstr);
+}
+
+PUBLIC int player_find_pendto(int p, int p1, int type)
+{
+ int i;
+
+ for (i = 0; i < parray[p].num_to; i++) {
+ if (parray[p].p_to_list[i].whoto != p1 && p1 != -1)
+ continue;
+ if (type < 0 || parray[p].p_to_list[i].type == type)
+ return i;
+ if (type == PEND_BUGHOUSE
+ && parray[p].p_to_list[i].type == PEND_MATCH
+ && !strcmp(parray[p].p_to_list[i].char2, "bughouse"))
+ return i;
+ }
+ return -1;
+}
+
+PUBLIC int player_new_pendto(int p)
+{
+ if (parray[p].num_to >= MAX_PENDING)
+ return -1;
+ parray[p].num_to++;
+ return parray[p].num_to - 1;
+}
+
+PUBLIC int player_remove_pendto(int p, int p1, int type)
+{
+ int w;
+ if ((w = player_find_pendto(p, p1, type)) < 0)
+ return -1;
+ for (; w < parray[p].num_to - 1; w++)
+ parray[p].p_to_list[w] = parray[p].p_to_list[w + 1];
+ parray[p].num_to = parray[p].num_to - 1;
+ return 0;
+}
+
+PUBLIC int player_find_pendfrom(int p, int p1, int type)
+{
+ int i;
+
+ for (i = 0; i < parray[p].num_from; i++) {
+ if (parray[p].p_from_list[i].whofrom != p1 && p1 != -1)
+ continue;
+ if (type == PEND_ALL || parray[p].p_from_list[i].type == type)
+ return i;
+ if (type < 0 && parray[p].p_from_list[i].type != -type)
+ return i;
+ /* The above "if" allows a type of -PEND_SIMUL to match every request
+ EXCEPT simuls, for example. I'm doing this because Heringer does
+ not want to decline simul requests when he makes a move in a sumul.
+ -- hersco. */
+ if (type == PEND_BUGHOUSE
+ && parray[p].p_from_list[i].type == PEND_MATCH
+ && !strcmp(parray[p].p_from_list[i].char2, "bughouse"))
+ return i;
+ }
+ return -1;
+}
+
+PUBLIC int player_new_pendfrom(int p)
+{
+ if (parray[p].num_from >= MAX_PENDING)
+ return -1;
+ parray[p].num_from++;
+ return parray[p].num_from - 1;
+}
+
+PUBLIC int player_remove_pendfrom(int p, int p1, int type)
+{
+ int w;
+ if ((w = player_find_pendfrom(p, p1, type)) < 0)
+ return -1;
+ for (; w < parray[p].num_from - 1; w++)
+ parray[p].p_from_list[w] = parray[p].p_from_list[w + 1];
+ parray[p].num_from = parray[p].num_from - 1;
+ return 0;
+}
+
+PUBLIC int player_add_request(int p, int p1, int type, int param)
+{
+ int pendt;
+ int pendf;
+
+ if (player_find_pendto(p, p1, type) >= 0)
+ return -1; /* Already exists */
+ pendt = player_new_pendto(p);
+ if (pendt == -1) {
+ return -1;
+ }
+ pendf = player_new_pendfrom(p1);
+ if (pendf == -1) {
+ parray[p].num_to--; /* Remove the pendto we allocated */
+ return -1;
+ }
+ parray[p].p_to_list[pendt].type = type;
+ parray[p].p_to_list[pendt].whoto = p1;
+ parray[p].p_to_list[pendt].whofrom = p;
+ parray[p].p_to_list[pendt].param1 = param;
+ parray[p1].p_from_list[pendf].type = type;
+ parray[p1].p_from_list[pendf].whoto = p1;
+ parray[p1].p_from_list[pendf].whofrom = p;
+ parray[p1].p_from_list[pendf].param1 = param;
+ return 0;
+}
+
+PUBLIC int player_remove_request(int p, int p1, int type)
+{
+ int to = 0, from = 0;
+
+ while (to != -1) {
+ to = player_find_pendto(p, p1, type);
+ if (to != -1) {
+ for (; to < parray[p].num_to - 1; to++)
+ parray[p].p_to_list[to] = parray[p].p_to_list[to + 1];
+ parray[p].num_to = parray[p].num_to - 1;
+ }
+ }
+ while (from != -1) {
+ from = player_find_pendfrom(p1, p, type);
+ if (from != -1) {
+ for (; from < parray[p1].num_from - 1; from++)
+ parray[p1].p_from_list[from] = parray[p1].p_from_list[from + 1];
+ parray[p1].num_from = parray[p1].num_from - 1;
+ }
+ }
+ if ((type == PEND_ALL || type == PEND_MATCH)
+ && parray[p].partner >= 0)
+ player_remove_request (parray[p].partner, p1, PEND_BUGHOUSE);
+ return 0;
+}
+
+
+PUBLIC int player_decline_offers(int p, int p1, int offerType)
+{
+ int offer;
+ int type, p2;
+ int count = 0;
+ int part, p2part;
+ char *pName = parray[p].name, *p2Name;
+
+ /* First get rid of bughouse offers from partner. */
+ if ((offerType == PEND_MATCH || offerType == PEND_ALL)
+ && parray[p].partner >= 0 && parray[parray[p].partner].partner == p)
+ count += player_decline_offers(parray[p].partner, p1, PEND_BUGHOUSE);
+
+ while ((offer = player_find_pendfrom(p, p1, offerType)) >= 0) {
+ type = parray[p].p_from_list[offer].type;
+ p2 = parray[p].p_from_list[offer].whofrom;
+ p2Name = parray[p2].name;
+
+ part = parray[p].partner;
+ if (part >= 0 && parray[part].partner != p)
+ part = -1;
+ p2part = parray[p2].partner;
+ if (p2part >= 0 && parray[p2part].partner != p2)
+ p2part = -1;
+
+ switch (type) {
+ case PEND_MATCH:
+ pprintf_prompt(p2, "\n%s declines the match offer.\n", pName);
+ pprintf(p, "You decline the match offer from %s.\n", p2Name);
+ if (!strcmp(parray[p].p_from_list[offer].char2, "bughouse")) {
+ if (part >= 0)
+ pprintf_prompt(part,
+ "Your partner declines the bughouse offer from %s.\n",
+ parray[p2].name);
+ if (p2part >= 0)
+ pprintf_prompt(p2part,
+ "%s declines the bughouse offer from your partner.\n",
+ parray[p].name);
+ }
+ break;
+ case PEND_DRAW:
+ pprintf_prompt(p2, "\n%s declines draw request.\n", pName);
+ pprintf(p, "You decline the draw request from %s.\n", p2Name);
+ break;
+ case PEND_PAUSE:
+ pprintf_prompt(p2, "\n%s declines pause request.\n", pName);
+ pprintf(p, "You decline the pause request from %s.\n", p2Name);
+ break;
+ case PEND_ABORT:
+ pprintf_prompt(p2, "\n%s declines abort request.\n", pName);
+ pprintf(p, "You decline the abort request from %s.\n", p2Name);
+ break;
+ case PEND_TAKEBACK:
+ pprintf_prompt(p2, "\n%s declines the takeback request.\n", pName);
+ pprintf(p, "You decline the takeback request from %s.\n", p2Name);
+ break;
+ case PEND_ADJOURN:
+ pprintf_prompt(p2, "\n%s declines the adjourn request.\n", pName);
+ pprintf(p, "You decline the adjourn request from %s.\n", p2Name);
+ break;
+ case PEND_SWITCH:
+ pprintf_prompt(p2, "\n%s declines the switch sides request.\n", pName);
+ pprintf(p, "You decline the switch sides request from %s.\n", p2Name);
+ break;
+ case PEND_SIMUL:
+ pprintf_prompt(p2, "\n%s declines the simul offer.\n", pName);
+ pprintf(p, "You decline the simul offer from %s.\n", p2Name);
+ break;
+ case PEND_PARTNER:
+ pprintf_prompt(p2, "\n%s declines your partnership request.\n", pName);
+ pprintf(p, "You decline the partnership request from %s.\n", p2Name);
+ break;
+ }
+ player_remove_request(p2, p, type);
+ count++;
+ }
+ return count;
+}
+
+
+PUBLIC int player_withdraw_offers(int p, int p1, int offerType)
+{
+ int offer;
+ int type, p2;
+ int count = 0;
+ int part, p2part;
+ char *pName = parray[p].name, *p2Name;
+
+ /* First get rid of bughouse offers from partner. */
+ if ((offerType == PEND_MATCH || offerType == PEND_ALL)
+ && parray[p].partner >= 0 && parray[parray[p].partner].partner == p)
+ count += player_withdraw_offers(parray[p].partner, p1, PEND_BUGHOUSE);
+
+ while ((offer = player_find_pendto(p, p1, offerType)) >= 0) {
+ type = parray[p].p_to_list[offer].type;
+ p2 = parray[p].p_to_list[offer].whoto;
+ p2Name = parray[p2].name;
+
+ part = parray[p].partner;
+ if (part >= 0 && parray[part].partner != p)
+ part = -1;
+ p2part = parray[p2].partner;
+ if (p2part >= 0 && parray[p2part].partner != p2)
+ p2part = -1;
+
+ switch (type) {
+ case PEND_MATCH:
+ pprintf_prompt(p2, "\n%s withdraws the match offer.\n", pName);
+ pprintf(p, "You withdraw the match offer to %s.\n", p2Name);
+ if (!strcmp(parray[p].p_to_list[offer].char2, "bughouse")) {
+ if (part >= 0)
+ pprintf_prompt(part,
+ "Your partner withdraws the bughouse offer to %s.\n",
+ parray[p2].name);
+ if (p2part >= 0)
+ pprintf_prompt(p2part,
+ "%s withdraws the bughouse offer to your partner.\n",
+ parray[p].name);
+ }
+ break;
+ case PEND_DRAW:
+ pprintf_prompt(p2, "\n%s withdraws draw request.\n", pName);
+ pprintf(p, "You withdraw the draw request to %s.\n", p2Name);
+ break;
+ case PEND_PAUSE:
+ pprintf_prompt(p2, "\n%s withdraws pause request.\n", pName);
+ pprintf(p, "You withdraw the pause request to %s.\n", p2Name);
+ break;
+ case PEND_ABORT:
+ pprintf_prompt(p2, "\n%s withdraws abort request.\n", pName);
+ pprintf(p, "You withdraw the abort request to %s.\n", p2Name);
+ break;
+ case PEND_TAKEBACK:
+ pprintf_prompt(p2, "\n%s withdraws the takeback request.\n", pName);
+ pprintf(p, "You withdraw the takeback request to %s.\n", p2Name);
+ break;
+ case PEND_ADJOURN:
+ pprintf_prompt(p2, "\n%s withdraws the adjourn request.\n", pName);
+ pprintf(p, "You withdraw the adjourn request to %s.\n", p2Name);
+ break;
+ case PEND_SWITCH:
+ pprintf_prompt(p2, "\n%s withdraws the switch sides request.\n", pName);
+ pprintf(p, "You withdraw the switch sides request to %s.\n", p2Name);
+ break;
+ case PEND_SIMUL:
+ pprintf_prompt(p2, "\n%s withdraws the simul offer.\n", pName);
+ pprintf(p, "You withdraw the simul offer to %s.\n", p2Name);
+ break;
+ case PEND_PARTNER:
+ pprintf_prompt(p2, "\n%s withdraws partnership request.\n", pName);
+ pprintf(p, "You withdraw the partnership request to %s.\n", p2Name);
+ break;
+ }
+ player_remove_request(p, p2, type);
+ count++;
+ }
+ return count;
+}
+
+
+PUBLIC int player_is_observe(int p, int g)
+{
+ int i;
+
+ for (i = 0; i < parray[p].num_observe; i++) {
+ if (parray[p].observe_list[i] == g)
+ break;
+ }
+ if (i == parray[p].num_observe)
+ return 0;
+ else
+ return 1;
+}
+
+PUBLIC int player_add_observe(int p, int g)
+{
+ if (parray[p].num_observe == MAX_OBSERVE)
+ return -1;
+ parray[p].observe_list[parray[p].num_observe] = g;
+ parray[p].num_observe++;
+ return 0;
+}
+
+PUBLIC int player_remove_observe(int p, int g)
+{
+ int i;
+
+ for (i = 0; i < parray[p].num_observe; i++) {
+ if (parray[p].observe_list[i] == g)
+ break;
+ }
+ if (i == parray[p].num_observe)
+ return -1; /* Not found! */
+ for (; i < parray[p].num_observe - 1; i++) {
+ parray[p].observe_list[i] = parray[p].observe_list[i + 1];
+ }
+ parray[p].num_observe--;
+ return 0;
+}
+
+PUBLIC int player_game_ended(int g)
+{
+ int p;
+
+ for (p = 0; p < p_num; p++) {
+ if (parray[p].status == PLAYER_EMPTY)
+ continue;
+ player_remove_observe(p, g);
+ }
+ player_remove_request(garray[g].white, garray[g].black, -1);
+ player_remove_request(garray[g].black, garray[g].white, -1);
+ player_save(garray[g].white); /* Hawk: Added to save finger-info after each
+ game */
+ player_save(garray[g].black);
+ return 0;
+}
+
+PUBLIC int player_goto_board(int p, int board_num)
+{
+ int start, count = 0, on, g;
+
+ if (board_num < 0 || board_num >= parray[p].simul_info.numBoards)
+ return -1;
+ if (parray[p].simul_info.boards[board_num] < 0)
+ return -1;
+ parray[p].simul_info.onBoard = board_num;
+ parray[p].game = parray[p].simul_info.boards[board_num];
+ parray[p].opponent = garray[parray[p].game].black;
+ if (parray[p].simul_info.numBoards == 1)
+ return 0;
+ send_board_to(parray[p].game, p);
+ start = parray[p].game;
+ on = parray[p].simul_info.onBoard;
+ do {
+ g = parray[p].simul_info.boards[on];
+ if (g >= 0) {
+ if (count == 0) {
+ if (parray[garray[g].black].bell) {
+ pprintf(garray[g].black, "\007");
+ }
+ pprintf(garray[g].black, "\n");
+ pprintf_highlight(garray[g].black, "%s", parray[p].name);
+ pprintf_prompt(garray[g].black, " is at your board!\n");
+ } else if (count == 1) {
+ if (parray[garray[g].black].bell) {
+ pprintf(garray[g].black, "\007");
+ }
+ pprintf(garray[g].black, "\n");
+ pprintf_highlight(garray[g].black, "%s", parray[p].name);
+ pprintf_prompt(garray[g].black, " will be at your board NEXT!\n");
+ } else {
+ pprintf(garray[g].black, "\n");
+ pprintf_highlight(garray[g].black, "%s", parray[p].name);
+ pprintf_prompt(garray[g].black, " is %d boards away.\n", count);
+ }
+ count++;
+ }
+ on++;
+ if (on >= parray[p].simul_info.numBoards)
+ on = 0;
+ } while (start != parray[p].simul_info.boards[on]);
+ return 0;
+}
+
+PUBLIC int player_goto_next_board(int p)
+{
+ int on;
+ int start;
+ int g;
+
+ on = parray[p].simul_info.onBoard;
+ start = on;
+ g = -1;
+ do {
+ on++;
+ if (on >= parray[p].simul_info.numBoards)
+ on = 0;
+ g = parray[p].simul_info.boards[on];
+ if (g >= 0)
+ break;
+ } while (start != on);
+ if (g == -1) {
+ pprintf(p, "\nMajor Problem! Can't find your next board.\n");
+ return -1;
+ }
+ return player_goto_board(p, on);
+}
+
+PUBLIC int player_goto_prev_board(int p)
+{
+ int on;
+ int start;
+ int g;
+
+ on = parray[p].simul_info.onBoard;
+ start = on;
+ g = -1;
+ do {
+ --on;
+ if (on < 0)
+ on = (parray[p].simul_info.numBoards) - 1;
+ g = parray[p].simul_info.boards[on];
+ if (g >= 0)
+ break;
+ } while (start != on);
+ if (g == -1) {
+ pprintf(p, "\nMajor Problem! Can't find your previous board.\n");
+ return -1;
+ }
+ return player_goto_board(p, on);
+}
+
+PUBLIC int player_goto_simulgame_bynum(int p, int num)
+{
+ int on;
+ int start;
+ int g;
+
+ on = parray[p].simul_info.onBoard;
+ start = on;
+ do {
+ on++;
+ if (on >= parray[p].simul_info.numBoards)
+ on = 0;
+ g = parray[p].simul_info.boards[on];
+ if (g == num)
+ break;
+ } while (start != on);
+ if (g != num) {
+ pprintf(p, "\nYou aren't playing that game!!\n");
+ return -1;
+ }
+ return player_goto_board(p, on);
+}
+
+PUBLIC int player_num_active_boards(int p)
+{
+ int count = 0, i;
+
+ if (!parray[p].simul_info.numBoards)
+ return 0;
+ for (i = 0; i < parray[p].simul_info.numBoards; i++)
+ if (parray[p].simul_info.boards[i] >= 0)
+ count++;
+ return count;
+}
+
+PUBLIC int player_num_results(int p, int result)
+{
+ int count = 0, i;
+
+ if (!parray[p].simul_info.numBoards)
+ return 0;
+ for (i = 0; i < parray[p].simul_info.numBoards; i++)
+ if (parray[p].simul_info.results[i] == result)
+ count++;
+ return count;
+}
+
+PUBLIC int player_simul_over(int p, int g, int result)
+{
+ int on, ong, p1, which = -1, won;
+ char tmp[1024];
+
+ for (won = 0; won < parray[p].simul_info.numBoards; won++) {
+ if (parray[p].simul_info.boards[won] == g) {
+ which = won;
+ break;
+ }
+ }
+ if (which == -1) {
+ pprintf(p, "I can't find that game!\n");
+ return -1;
+ }
+ pprintf(p, "\nBoard %d has completed.\n", won + 1);
+ on = parray[p].simul_info.onBoard;
+ ong = parray[p].simul_info.boards[on];
+ parray[p].simul_info.boards[won] = -1;
+ parray[p].simul_info.results[won] = result;
+ if (player_num_active_boards(p) == 0) {
+ sprintf(tmp, "\n{Simul (%s vs. %d) is over.}\nResults: %d Wins, %d Losses, %d Draws, %d Aborts\n",
+ parray[p].name,
+ parray[p].simul_info.numBoards,
+ player_num_results(p, RESULT_WIN),
+ player_num_results(p, RESULT_LOSS),
+ player_num_results(p, RESULT_DRAW),
+ player_num_results(p, RESULT_ABORT));
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (parray[p].status != PLAYER_PROMPT)
+ continue;
+ if (!parray[p1].i_game && !player_is_observe(p1, g) && (p1 != p))
+ continue;
+ pprintf_prompt(p1, "%s", tmp);
+ }
+ parray[p].simul_info.numBoards = 0;
+ pprintf_prompt(p, "\nThat was the last board, thanks for playing.\n");
+ return 0;
+ }
+ if (ong == g) { /* This game is over */
+ player_goto_next_board(p);
+ } else {
+ player_goto_board(p, parray[p].simul_info.onBoard);
+ }
+ pprintf_prompt(p, "\nThere are %d boards left.\n",
+ player_num_active_boards(p));
+ return 0;
+}
+
+PRIVATE void GetMsgFile (int p, char *fName)
+{
+ sprintf(fName, "%s/player_data/%c/%s.%s", stats_dir, parray[p].login[0],
+ parray[p].login, STATS_MESSAGES);
+ }
+
+PUBLIC int player_num_messages(int p)
+{
+ char fname[MAX_FILENAME_SIZE];
+
+ if (!parray[p].registered)
+ return 0;
+ GetMsgFile (p, fname);
+ return lines_file(fname);
+}
+
+PUBLIC int player_add_message(int top, int fromp, char *message)
+{
+/* char command[MAX_FILENAME_SIZE]; */
+ char fname[MAX_FILENAME_SIZE];
+ FILE *fp;
+ char subj[256];
+ char messbody[1024];
+ int t = time(0);
+
+ if (!parray[top].registered)
+ return -1;
+ if (!parray[fromp].registered)
+ return -1;
+ GetMsgFile (top, fname);
+ if ((lines_file(fname) >= MAX_MESSAGES) && (parray[top].adminLevel == 0))
+ return -1;
+ fp = fopen(fname, "a");
+ if (!fp)
+ return -1;
+ fprintf(fp, "%s at %s: %s\n", parray[fromp].name, strltime(&t), message);
+ fclose(fp);
+ pprintf(fromp, "\nThe following message was sent ");
+ if (parray[top].i_mailmess) {
+ sprintf(subj, "FICS message from %s at FICS %s (Do not reply by mail)", parray[fromp].name, fics_hostname);
+ sprintf(messbody, "%s at %s: %s\n", parray[fromp].name, strltime(&t), message);
+ mail_string_to_user(top, subj, messbody);
+ pprintf(fromp, "(and emailed) ");
+ }
+ pprintf(fromp, "to %s: \n %s\n", parray[top].name, message);
+ return 0;
+}
+
+#if 0
+/* Function changed and moved lower to have messages numbered. */
+PUBLIC int player_show_messages(int p)
+{
+ char fname[MAX_FILENAME_SIZE];
+
+ if (!parray[p].registered)
+ return -1;
+ GetMsgFile (p, fname);
+ if (lines_file(fname) <= 0)
+ return -1;
+ psend_file(p, NULL, fname);
+ return 0;
+}
+#endif
+
+PUBLIC void SaveTextListEntry(textlist **Entry, char *string, int n)
+{
+ *Entry = (textlist *) rmalloc(sizeof(textlist));
+ (*Entry)->text = strdup(string);
+ (*Entry)->index = n;
+ (*Entry)->next = NULL;
+}
+
+PUBLIC textlist *ClearTextListEntry(textlist *entry)
+{
+ textlist *ret = entry->next;
+ strfree(entry->text);
+ rfree(entry);
+ return ret;
+}
+
+PUBLIC void ClearTextList(textlist *head)
+{
+ textlist *cur;
+
+ for (cur = head; cur != NULL; cur = ClearTextListEntry(cur));
+}
+
+/* if which=0 load all messages; if it's (p1+1) load messages only from
+ p1; if it's -(p1+1) load all messages EXCEPT those from p1. */
+PRIVATE int SaveThisMsg (int which, char *line)
+{
+ char Sender[MAX_LOGIN_NAME];
+ int p1;
+
+ if (which == 0) return 1;
+
+ sscanf (line, "%s", Sender);
+ if (which < 0) {
+ p1 = -which - 1;
+ return strcmp(Sender, parray[p1].name);
+ }
+ else {
+ p1 = which - 1;
+ return !strcmp(Sender, parray[p1].name);
+ }
+}
+
+PRIVATE int LoadMsgs(int p, int which, textlist **Head)
+ /* which=0 to load all messages; it's (p1+1) to load messages only from
+ p1, and it's -(p1+1) to load all messages EXCEPT those from p1. */
+{
+ FILE *fp;
+ textlist **Cur = Head;
+ char fName[MAX_FILENAME_SIZE];
+ char line[MAX_LINE_SIZE];
+ int n=0, nSave=0;
+
+ *Head = NULL;
+ GetMsgFile (p, fName);
+ fp = fopen(fName, "r");
+ if (fp == NULL) {
+ return -1;
+ }
+ while (!feof(fp)) {
+ fgets(line, MAX_LINE_SIZE, fp);
+ if (feof(fp))
+ break;
+ if (SaveThisMsg(which, line)) {
+ SaveTextListEntry(Cur, line, ++n);
+ Cur = &(*Cur)->next;
+ nSave++;
+ }
+ else n++;
+ }
+ fclose (fp);
+ return nSave;
+}
+
+/* start > 0 and end > start (or end = 0) to save messages in range;
+ start < 0 and end < start (or end = 0) to clear messages in range;
+ if end = 0, range goes to end of file (not tested yet). */
+PRIVATE int LoadMsgRange(int p, int start, int end, textlist **Head)
+{
+ FILE *fp;
+ char fName[MAX_FILENAME_SIZE];
+ char line[MAX_LINE_SIZE];
+ textlist **Cur = Head;
+ int n=1, nSave=0, nKill=0;
+
+ *Head = NULL;
+ GetMsgFile (p, fName);
+ fp = fopen (fName, "r");
+ if (fp == NULL) {
+ pprintf (p, "You have no messages.\n");
+ return -1;
+ }
+ for (n=1; n <= end || end <= 0; n++) {
+ fgets (line, MAX_LINE_SIZE, fp);
+ if (feof(fp))
+ break;
+ if ((start < 0 && (n < -start || n > -end))
+ || (start >= 0 && n >= start)) {
+ SaveTextListEntry (Cur, line, n);
+ Cur = &(*Cur)->next;
+ nSave++;
+ }
+ else nKill++;
+ }
+ fclose (fp);
+ if (start < 0) {
+ if (n <= -start)
+ pprintf (p, "You do not have a message %d.\n", -start);
+ return nKill;
+ } else {
+ if (n <= start)
+ pprintf (p, "You do not have a message %d.\n", start);
+ return nSave;
+ }
+}
+
+#if 0
+PRIVATE int LoadMsgsFromP1(int p, int p1, int saveP1, textlist **Head)
+{
+ textlist **Cur=Head;
+ char Sender[MAX_LOGIN_NAME];
+ int nKeep = 0;
+ int nKill = 0;
+
+ /* First load all messages; then get rid of the ones we don't want. */
+ LoadMsgs(p, 0, Head);
+ while (Cur != NULL && *Cur != NULL) {
+ sscanf ((*Cur)->text, "%s", Sender);
+ if ((saveP1 && strcmp(Sender, parray[p1].name))
+ || (!saveP1 && !strcmp(Sender, parray[p1].name)))
+ /* either the message is from p1 and we're not saving his message
+ or it's not from p1, and we're only saving his messages. In
+ either case, we want to get rid of this one. */
+ *Cur = ClearTextListEntry (*Cur);
+ nKill++;
+ }
+ else {
+ /* Save this message; move on to the next one. */
+ if ((*Cur)->next != NULL)
+ Cur = &(*Cur)->next;
+ else Cur = NULL;
+ nKeep++;
+ }
+ }
+ return (saveP1 ? nKeep : nKill);
+}
+#endif
+
+PRIVATE int WriteMsgFile (int p, textlist *Head)
+{
+ char fName[MAX_FILENAME_SIZE];
+ FILE *fp;
+ textlist *Cur;
+
+ GetMsgFile (p, fName);
+ fp = fopen(fName, "w");
+ if (fp == NULL)
+ return 0;
+ for (Cur = Head; Cur != NULL; Cur = Cur->next)
+ fprintf(fp, "%s", Cur->text);
+ fclose(fp);
+ return 1;
+}
+
+PUBLIC int ClearMsgsBySender(int p, param_list param)
+{
+ textlist *Head;
+ int p1, connected;
+ int nFound;
+
+ if (!FindPlayer(p, param[0].val.word, &p1, &connected))
+ return -1;
+
+ nFound = LoadMsgs(p, -(p1+1), &Head);
+ if (nFound < 0) {
+ pprintf(p, "You have no messages.\n");
+ } else if (nFound == 0) {
+ pprintf(p, "You have no messages from %s.\n", parray[p1].name);
+ } else {
+ if (WriteMsgFile (p, Head))
+ pprintf(p, "Messages from %s cleared.\n", parray[p1].name);
+ else {
+ pprintf(p, "Problem writing message file; please contact an admin.\n");
+ fprintf(stderr, "Problem writing message file for %s.\n", parray[p].name);
+ }
+ ClearTextList(Head);
+ }
+ if (!connected)
+ player_remove(p1);
+ return nFound;
+}
+
+PRIVATE void ShowTextList (int p, textlist *Head, int ShowIndex)
+{
+ textlist *CurMsg;
+
+ if (ShowIndex) {
+ for (CurMsg = Head; CurMsg != NULL; CurMsg = CurMsg->next)
+ pprintf(p, "%2d. %s", CurMsg->index, CurMsg->text);
+ }
+ else {
+ for (CurMsg = Head; CurMsg != NULL; CurMsg = CurMsg->next)
+ pprintf(p, "%s", CurMsg->text);
+ }
+}
+
+PUBLIC int player_show_messages(int p)
+{
+ textlist *Head;
+ int n;
+
+ n = LoadMsgs (p, 0, &Head);
+ if (n <= 0) {
+ pprintf (p, "You have no messages.\n");
+ return -1;
+ } else {
+ pprintf (p, "Messages:\n");
+ ShowTextList (p, Head, 1);
+ ClearTextList (Head);
+ return 0;
+ }
+}
+
+PUBLIC int ShowMsgsBySender(int p, param_list param)
+{
+ textlist *Head;
+ int p1, connected;
+ int nFrom, nTo;
+
+ if (!FindPlayer(p, param[0].val.word, &p1, &connected))
+ return -1;
+
+ if (!parray[p1].registered) {
+ pprintf(p, "Player \"%s\" is unregistered and cannot send or receive messages.\n",
+ parray[p1].name);
+ return -1; /* no need to disconnect */
+ }
+
+ if (p != p1) {
+ nTo = LoadMsgs(p1, p+1, &Head);
+ if (nTo <= 0) {
+ pprintf(p, "%s has no messages from you.\n", parray[p1].name);
+ } else {
+ pprintf(p, "Messages to %s:\n", parray[p1].name);
+ ShowTextList (p, Head, 0);
+ ClearTextList(Head);
+ }
+ }
+ nFrom = LoadMsgs(p, p1+1, &Head);
+ if (nFrom <= 0) {
+ pprintf(p, "\nYou have no messages from %s.\n", parray[p1].name);
+ } else {
+ pprintf(p, "Messages from %s:\n", parray[p1].name);
+ ShowTextList (p, Head, 1);
+ ClearTextList(Head);
+ }
+ if (!connected)
+ player_remove(p1);
+ return (nFrom > 0 || nTo > 0);
+}
+
+PUBLIC int ShowMsgRange (int p, int start, int end)
+{
+ textlist *Head;
+ int n;
+
+ n = LoadMsgRange (p, start, end, &Head);
+ if (n > 0) {
+ ShowTextList (p, Head, 1);
+ ClearTextList (Head);
+ }
+ return n;
+}
+
+PUBLIC int ClrMsgRange (int p, int start, int end)
+{
+ textlist *Head;
+ int n;
+
+ n = LoadMsgRange (p, -start, -end, &Head);
+ if (n > 0) {
+/* You can use ShowTextList to see what's left after the real code. */
+/* ShowTextList (p, Head, 1); */
+ if (WriteMsgFile (p, Head))
+ pprintf (p, "Message %d cleared.\n", start);
+ }
+ if (n >= 0)
+ ClearTextList (Head);
+ return n;
+}
+
+PUBLIC int player_clear_messages(int p)
+{
+ char fname[MAX_FILENAME_SIZE];
+
+ if (!parray[p].registered)
+ return -1;
+ GetMsgFile (p, fname);
+ unlink(fname);
+ return 0;
+}
+
+PUBLIC int player_search(int p, char *name)
+/*
+ * Find player matching the given string. First looks for exact match
+ * with a logged in player, then an exact match with a registered player,
+ * then a partial unique match with a logged in player, then a partial
+ * match with a registered player.
+ * Returns player number if the player is connected, negative (player number)
+ * if the player had to be connected, and 0 if no player was found
+ */
+{
+ int p1, count;
+ char *buffer[1000];
+ char pdir[MAX_FILENAME_SIZE];
+
+ /* exact match with connected player? */
+ if ((p1 = player_find_bylogin(name)) >= 0) {
+ return p1 + 1;
+ }
+ /* exact match with registered player? */
+ sprintf(pdir, "%s/%c", player_dir, name[0]);
+ count = search_directory(pdir, name, buffer, 1000);
+ if (count > 0 && !strcmp(name, *buffer)) {
+ goto ReadPlayerFromFile; /* found an unconnected registered player */
+ }
+ /* partial match with connected player? */
+ if ((p1 = player_find_part_login(name)) >= 0) {
+ return p1 + 1;
+ } else if (p1 == -2) {
+ /* ambiguous; matches too many connected players. */
+ pprintf (p, "Ambiguous name '%s'; matches more than one player.\n", name);
+ return 0;
+ }
+ /* partial match with registered player? */
+ if (count < 1) {
+ pprintf(p, "There is no player matching that name.\n");
+ return 0;
+ }
+ if (count > 1) {
+ pprintf(p, "-- Matches: %d names --", count);
+ display_directory(p, buffer, count);
+ return(0);
+/*
+ char *s = buffer;
+ multicol *m = multicol_start(2000);
+ for (i = 0; i < count; i++) {
+ multicol_store(m, s);
+ s += strlen(s) + 1;
+ }
+ multicol_pprint(m, p, 78, 1);
+ multicol_end(m);
+ return 0;
+*/
+ }
+ReadPlayerFromFile:
+ p1 = player_new();
+ if (player_read(p1, *buffer)) {
+ player_remove(p1);
+ pprintf(p, "ERROR: a player named %s was expected but not found!\n",
+ *buffer);
+ pprintf(p, "Please tell an admin about this incident. Thank you.\n");
+ return 0;
+ }
+ return (-p1) - 1; /* negative to indicate player was not
+ connected */
+}
+
+
+PUBLIC int player_kill(char *name)
+{
+ char fname[MAX_FILENAME_SIZE], fname2[MAX_FILENAME_SIZE];
+
+ sprintf(fname, "%s/%c/%s", player_dir, name[0], name);
+ sprintf(fname2, "%s/%c/.rem.%s", player_dir, name[0], name);
+ rename(fname, fname2);
+ RemHist (name);
+ sprintf(fname, "%s/player_data/%c/%s.games", stats_dir, name[0], name);
+ sprintf(fname2, "%s/player_data/%c/.rem.%s.games", stats_dir, name[0], name);
+ rename(fname, fname2);
+ sprintf(fname, "%s/player_data/%c/%s.comments", stats_dir, name[0], name);
+ sprintf(fname2, "%s/player_data/%c/.rem.%s.comments", stats_dir, name[0], name);
+ rename(fname, fname2);
+
+ sprintf(fname, "%s/player_data/%c/%s.logons", stats_dir, name[0], name);
+ sprintf(fname2, "%s/player_data/%c/.rem.%s.logons", stats_dir, name[0], name);
+ rename(fname, fname2);
+ sprintf(fname, "%s/player_data/%c/%s.messages", stats_dir, name[0], name);
+ sprintf(fname2, "%s/player_data/%c/.rem.%s.messages", stats_dir, name[0], name);
+ rename(fname, fname2);
+ return 0;
+}
+
+PUBLIC int player_rename(char *name, char *newname)
+{
+ char fname[MAX_FILENAME_SIZE], fname2[MAX_FILENAME_SIZE];
+
+ sprintf(fname, "%s/%c/%s", player_dir, name[0], name);
+ sprintf(fname2, "%s/%c/%s", player_dir, newname[0], newname);
+ rename(fname, fname2);
+ sprintf(fname, "%s/player_data/%c/%s.games", stats_dir, name[0], name);
+ sprintf(fname2, "%s/player_data/%c/%s.games", stats_dir, newname[0], newname);
+ rename(fname, fname2);
+ sprintf(fname, "%s/player_data/%c/%s.comments", stats_dir, name[0], name);
+ sprintf(fname2, "%s/player_data/%c/%s.comments", stats_dir, newname[0], newname);
+ rename(fname, fname2);
+ sprintf(fname, "%s/player_data/%c/%s.logons", stats_dir, name[0], name);
+ sprintf(fname2, "%s/player_data/%c/%s.logons", stats_dir, newname[0], newname);
+ rename(fname, fname2);
+ sprintf(fname, "%s/player_data/%c/%s.messages", stats_dir, name[0], name);
+ sprintf(fname2, "%s/player_data/%c/%s.messages", stats_dir, newname[0], newname);
+ rename(fname, fname2);
+ return 0;
+}
+
+PUBLIC int player_raise(char *name)
+{
+ char fname[MAX_FILENAME_SIZE], fname2[MAX_FILENAME_SIZE];
+
+ sprintf(fname, "%s/%c/%s", player_dir, name[0], name);
+ sprintf(fname2, "%s/%c/.rem.%s", player_dir, name[0], name);
+ rename(fname2, fname);
+ sprintf(fname, "%s/player_data/%c/%s.games", stats_dir, name[0], name);
+ sprintf(fname2, "%s/player_data/%c/.rem.%s.games", stats_dir, name[0], name);
+ rename(fname2, fname);
+ sprintf(fname, "%s/player_data/%c/%s.comments", stats_dir, name[0], name);
+ sprintf(fname2, "%s/player_data/%c/.rem.%s.comments", stats_dir, name[0], name);
+ rename(fname2, fname);
+ sprintf(fname, "%s/player_data/%c/%s.logons", stats_dir, name[0], name);
+ sprintf(fname2, "%s/player_data/%c/.rem.%s.logons", stats_dir, name[0], name);
+ rename(fname2, fname);
+ sprintf(fname, "%s/player_data/%c/%s.messages", stats_dir, name[0], name);
+ sprintf(fname2, "%s/player_data/%c/.rem.%s.messages", stats_dir, name[0], name);
+ rename(fname2, fname);
+ return 0;
+}
+
+PUBLIC int player_reincarn(char *name, char *newname)
+{
+ char fname[MAX_FILENAME_SIZE], fname2[MAX_FILENAME_SIZE];
+
+ sprintf(fname, "%s/%c/%s", player_dir, newname[0], newname);
+ sprintf(fname2, "%s/%c/.rem.%s", player_dir, name[0], name);
+ rename(fname2, fname);
+ sprintf(fname, "%s/player_data/%c/%s.games", stats_dir, newname[0], newname);
+ sprintf(fname2, "%s/player_data/%c/.rem.%s.games", stats_dir, name[0], name);
+ rename(fname2, fname);
+ sprintf(fname, "%s/player_data/%c/%s.comments", stats_dir, newname[0], newname);
+ sprintf(fname2, "%s/player_data/%c/.rem.%s.comments", stats_dir, name[0], name);
+ rename(fname2, fname);
+ sprintf(fname, "%s/player_data/%c/%s.logons", stats_dir, newname[0], newname);
+ sprintf(fname2, "%s/player_data/%c/.rem.%s.logons", stats_dir, name[0], name);
+ rename(fname2, fname);
+ sprintf(fname, "%s/player_data/%c/%s.messages", stats_dir, newname[0], newname);
+ sprintf(fname2, "%s/player_data/%c/.rem.%s.messages", stats_dir, name[0], name);
+ rename(fname2, fname);
+ return 0;
+}
+
+PUBLIC int player_num_comments(int p)
+{
+ char fname[MAX_FILENAME_SIZE];
+
+ if (!parray[p].registered)
+ return 0;
+ sprintf(fname, "%s/player_data/%c/%s.%s", stats_dir, parray[p].login[0],
+ parray[p].login, "comments");
+ return lines_file(fname);
+}
+
+PUBLIC int player_add_comment(int p_by, int p_to, char *comment)
+{
+ char fname[MAX_FILENAME_SIZE];
+ FILE *fp;
+ int t = time(0);
+
+ if (!parray[p_to].registered)
+ return -1;
+ sprintf(fname, "%s/player_data/%c/%s.%s", stats_dir, parray[p_to].login[0],
+ parray[p_to].login, "comments");
+ fp = fopen(fname, "a");
+ if (!fp)
+ return -1;
+ fprintf(fp, "%s at %s: %s\n", parray[p_by].name, strltime(&t), comment);
+ fclose(fp);
+ parray[p_to].num_comments = player_num_comments(p_to);
+ return 0;
+}
+
+PUBLIC int player_show_comments(int p, int p1)
+{
+ char fname[MAX_FILENAME_SIZE];
+
+ sprintf(fname, "%s/player_data/%c/%s.%s", stats_dir, parray[p1].login[0],
+ parray[p1].login, "comments");
+ psend_file(p, NULL, fname);
+ return 0;
+}
+
+/* returns 1 if player is head admin, 0 otherwise */
+PUBLIC int player_ishead(int p)
+{
+ return (!strcasecmp(parray[p].name, hadmin_handle));
+}
diff --git a/FICS/playerdb.h b/FICS/playerdb.h
new file mode 100644
index 0000000..4a40ef6
--- /dev/null
+++ b/FICS/playerdb.h
@@ -0,0 +1,289 @@
+/* playerdb.h
+ *
+ */
+
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name email yy/mm/dd Change
+ Richard Nash 93/10/22 Created
+*/
+
+#include "command.h"
+#include "lists.h"
+
+#ifndef _PLAYERDB_H
+#define _PLAYERDB_H
+
+#define PLAYER_VERSION 1
+
+#define MAX_PLAYER 500
+#define MAX_PENDING 10
+#define MAX_OBSERVE 30 /* max # of games one person can observe */
+#define MAX_PLAN 10
+#define MAX_FORMULA 9
+#define MAX_CENSOR 50
+#define MAX_NOTIFY 80
+#define MAX_ALIASES 30
+#define MAX_SIMUL 30
+#define MAX_MESSAGES 40
+#define MAX_INCHANNELS 16
+
+#define PLAYER_EMPTY 0
+#define PLAYER_NEW 1
+#define PLAYER_INQUEUE 2
+#define PLAYER_LOGIN 3
+#define PLAYER_PASSWORD 4
+#define PLAYER_PROMPT 5
+
+#define P_LOGIN 0
+#define P_LOGOUT 1
+
+#define SORT_BLITZ 0
+#define SORT_STAND 1
+#define SORT_ALPHA 2
+#define SORT_WILD 3
+
+typedef struct _statistics {
+ int num, win, los, dra, rating, ltime, best, whenbest;
+ double sterr;
+} statistics;
+
+#define PEND_MATCH 0 /* Params 1=wt 2=winc 3=bt 4=binc 5=registered */
+#define PEND_DRAW 1
+#define PEND_ABORT 2
+#define PEND_TAKEBACK 3
+#define PEND_ADJOURN 4
+#define PEND_SWITCH 5
+#define PEND_SIMUL 6
+#define PEND_PAUSE 7
+#define PEND_PARTNER 8
+#define PEND_BUGHOUSE 9
+#define PEND_ALL -1
+
+#if 0 /* We never use this. -- hersco */
+extern char *pend_strings[7];
+#endif
+
+#define PEND_TO 0
+#define PEND_FROM 1
+typedef struct _pending {
+ int type;
+ int whoto;
+ int whofrom;
+ int param1, param2, param3, param4, param5, param6;
+ char char1[50], char2[50];
+} pending;
+
+typedef struct _simul_info_t {
+ int numBoards;
+ int onBoard;
+ int results[MAX_SIMUL];
+ int boards[MAX_SIMUL];
+} simul_info_t;
+
+typedef struct _player {
+/* This first block is not saved between logins */
+ char *login;
+ int registered;
+ int socket;
+ int status;
+ int game;
+ int opponent; /* Only valid if game is >= 0 */
+ int side; /* Only valid if game is >= 0 */
+ int timeOfReg;
+ int totalTime;
+ int last_tell;
+ int last_channel;
+ int last_opponent;
+ int logon_time;
+ int last_command_time;
+ int num_from;
+ pending p_from_list[MAX_PENDING];
+ int num_to;
+ pending p_to_list[MAX_PENDING];
+ int num_observe;
+ int observe_list[MAX_OBSERVE];
+ int lastColor;
+ unsigned int thisHost;
+/* timelog *query_log; */
+ int lastshout_a;
+ int lastshout_b;
+ int sopen;
+ simul_info_t simul_info;
+ char busy[100]; /* more than enough */
+ char *identptr; /* identd stuff */
+ int num_comments; /* number of lines in comments file */
+ int flip;
+ List *lists;
+ int partner;
+ long last_file_byte;
+ char *last_file;
+ int i_admin;
+ int kiblevel;
+
+/* All of this is saved between logins */
+ char *name;
+ char *passwd;
+ char *fullName;
+ char *emailAddress;
+ char *prompt;
+ statistics s_stats;
+ statistics b_stats;
+ statistics w_stats;
+ statistics l_stats;
+ statistics bug_stats;
+ int d_time;
+ int d_inc;
+ int d_height;
+ int d_width;
+ int language;
+ int open;
+ int rated;
+ int ropen;
+ int notifiedby;
+ int bell;
+ int pgn;
+ int i_login;
+ int i_game;
+ int i_shout;
+ int i_cshout;
+ int i_tell;
+ int i_kibitz;
+ int private;
+ int jprivate;
+ int automail;
+ int i_mailmess;
+ int style;
+ int promote;
+ int adminLevel;
+/* int computer; */ /* 1 if the player is a computer */
+ int num_plan;
+ char *planLines[MAX_PLAN];
+ int num_formula;
+ char *formulaLines[MAX_FORMULA];
+ char *formula;
+ int nochannels;
+ int num_white;
+ int num_black;
+/* int uscfRating; */
+/* int network_player; */
+ unsigned int lastHost;
+ int numAlias;
+ alias_type alias_list[MAX_ALIASES];
+ int highlight;
+} player;
+
+typedef struct _textlist {
+ char *text;
+ int index;
+ struct _textlist *next;
+} textlist;
+
+#define PARRAY_SIZE (MAX_PLAYER + 50)
+extern player parray[PARRAY_SIZE];
+extern int p_num;
+
+extern void player_init(int);
+
+extern int player_new(void);
+extern int player_remove(int);
+extern int player_read(int, char *);
+extern int player_delete(int);
+extern int player_markdeleted(int);
+extern int player_save(int);
+extern int player_find(int);
+extern int player_find_bylogin(char *);
+extern int player_find_part_login(char *);
+extern int player_censored(int, int);
+extern int player_notified(int, int);
+extern int player_notified_departure(int);
+extern int player_notify_present (int);
+extern int player_notify(int, char *, char *);
+extern int showstored(int);
+
+extern int player_count(int);
+extern int player_idle(int);
+extern int player_ontime(int);
+
+extern int player_zero(int);
+extern int player_free(int);
+extern int player_clear(int);
+
+extern void player_write_login(int);
+extern void player_write_logout(int);
+extern int player_lastconnect(int);
+extern int player_lastdisconnect(int);
+
+extern void player_pend_print(int, pending *);
+
+extern int player_find_pendto(int, int, int);
+extern int player_new_pendto(int);
+extern int player_remove_pendto(int, int, int);
+
+extern int player_find_pendfrom(int, int, int);
+extern int player_new_pendfrom(int);
+extern int player_remove_pendfrom(int, int, int);
+extern int player_add_request(int, int, int, int);
+extern int player_remove_request(int, int, int);
+extern int player_decline_offers(int, int, int);
+extern int player_withdraw_offers(int, int, int);
+
+extern int player_is_observe(int, int);
+extern int player_add_observe(int, int);
+extern int player_remove_observe(int, int);
+extern int player_game_ended(int);
+
+extern int player_goto_board(int, int);
+extern int player_goto_next_board(int);
+extern int player_goto_prev_board(int);
+extern int player_goto_simulgame_bynum(int,int);
+extern int player_num_active_boards(int);
+extern int player_num_results(int, int);
+extern int player_simul_over(int, int, int);
+
+extern int player_num_messages(int);
+extern int player_add_message(int, int, char *);
+extern int player_show_messages(int);
+extern int player_clear_messages(int);
+extern void ClearTextList(textlist *);
+extern void SaveTextListEntry(textlist **, char *, int);
+extern int ShowMsgsBySender(int, param_list);
+extern int ClrMsgRange(int, int, int);
+extern int ShowMsgRange(int, int, int);
+extern int ClearMsgsBySender(int, param_list);
+#if 0
+extern int MessagesBySender(int, param_list, int);
+#endif
+extern int player_search(int, char *);
+
+extern int sort_blitz[];
+extern int sort_stand[];
+extern int sort_alpha[];
+extern int sort_wild[];
+
+extern int player_kill(char *);
+extern int player_rename(char *, char *);
+extern int player_raise(char *);
+extern int player_reincarn(char *,char *);
+
+extern int player_num_comments(int);
+extern int player_add_comment(int, int, char *);
+extern int player_show_comments(int, int);
+extern int player_ishead(int);
+
+#endif /* _PLAYERDB_H */
+
diff --git a/FICS/rating_conv.c b/FICS/rating_conv.c
new file mode 100644
index 0000000..553b4b0
--- /dev/null
+++ b/FICS/rating_conv.c
@@ -0,0 +1,73 @@
+/* Ratings conversions by DAV */
+/* GNU licensing applies */
+
+#include "rating_conv.h"
+#include "common.h"
+#include "stdinclude.h"
+#include "command.h"
+#include "utils.h"
+
+PRIVATE int elo_to_uscf(int elo)
+{
+ return elo + 100;
+}
+
+PRIVATE int uscf_to_elo(int uscf)
+{
+ return uscf - 100;
+}
+
+PRIVATE int bcf_to_elo(int bcf)
+{
+ return bcf * 8 + 600;
+}
+
+PRIVATE int elo_to_bcf(int elo)
+{
+ return (elo - 600) / 8;
+}
+
+PRIVATE int uscf_to_bcf(int uscf)
+{
+ return (uscf - 700) / 8;
+}
+
+PRIVATE int bcf_to_uscf(int bcf)
+{
+ return bcf * 8 + 700;
+}
+
+PRIVATE void printgrades(int p, int elo, int uscf, int bcf)
+{
+ pprintf(p, "Grading conversion:\n");
+ pprintf(p, " ELO = %d, USCF = %d, BCF = %d\n", elo, uscf, bcf);
+}
+
+PUBLIC int com_CONVERT_BCF(int p, param_list param)
+{
+ int elo = bcf_to_elo(param[0].val.integer);
+ int uscf = elo_to_uscf(elo);
+
+ printgrades(p, elo, uscf, param[0].val.integer);
+ return COM_OK;
+}
+
+PUBLIC int com_CONVERT_ELO(int p, param_list param)
+{
+ int bcf = elo_to_bcf(param[0].val.integer);
+ int uscf = elo_to_uscf(param[0].val.integer);
+
+ printgrades(p, param[0].val.integer, uscf, bcf);
+ return COM_OK;
+}
+
+
+PUBLIC int com_CONVERT_USCF(int p, param_list param)
+{
+ int elo = uscf_to_elo(param[0].val.integer);
+ int bcf = elo_to_bcf(elo);
+
+ printgrades(p, elo, param[0].val.integer, bcf);
+ return COM_OK;
+}
+
diff --git a/FICS/rating_conv.h b/FICS/rating_conv.h
new file mode 100644
index 0000000..cf12b27
--- /dev/null
+++ b/FICS/rating_conv.h
@@ -0,0 +1,11 @@
+/* rating_conv.h */
+
+
+#ifndef _RATINGCONV_H
+#define _RATINGCONV_H
+
+extern int com_CONVERT_BCF();
+extern int com_CONVERT_ELO();
+extern int com_CONVERT_USCF();
+
+#endif /* _RATINGCONV_H */
diff --git a/FICS/ratings.c b/FICS/ratings.c
new file mode 100644
index 0000000..007e613
--- /dev/null
+++ b/FICS/ratings.c
@@ -0,0 +1,1512 @@
+/*
+ ratings.c
+
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name email yy/mm/dd Change
+ Richard Nash 93/10/22 Created
+ vek leeds@math.gatech.edu 95/04/05 Glicko system, with sterr
+*/
+
+#include "stdinclude.h"
+
+#include "common.h"
+#include "playerdb.h"
+#include "ratings.h"
+#include "gamedb.h"
+#include "command.h"
+#include "comproc.h"
+#include "lists.h"
+#include "ficsmain.h"
+#include "config.h"
+#include "utils.h"
+
+/* grimm */
+#if defined(SGI)
+#else
+/* int system(char *arg); */
+/* int rewind(FILE *stream); */
+#endif
+
+
+PUBLIC double Ratings_B_Average;
+PUBLIC double Ratings_B_StdDev;
+
+PUBLIC double Ratings_S_Average;
+PUBLIC double Ratings_S_StdDev;
+
+PUBLIC double Ratings_L_Average;
+PUBLIC double Ratings_L_StdDev;
+
+PUBLIC double Ratings_W_Average;
+PUBLIC double Ratings_W_StdDev;
+
+
+PRIVATE double Rb_M = 0.0, Rb_S = 0.0, Rb_total = 0.0;
+PRIVATE int Rb_count = 0;
+
+PRIVATE double Rs_M = 0.0, Rs_S = 0.0, Rs_total = 0.0;
+PRIVATE int Rs_count = 0;
+
+PRIVATE double Rl_M = 0.0, Rl_S = 0.0, Rl_total = 0.0;
+PRIVATE int Rl_count = 0;
+
+PRIVATE double Rw_M = 0.0, Rw_S = 0.0, Rw_total = 0.0;
+PRIVATE int Rw_count = 0;
+
+/*
+PUBLIC rateStruct bestS[MAX_BEST];
+PUBLIC int numS = 0;
+PUBLIC rateStruct bestB[MAX_BEST];
+PUBLIC int numB = 0;
+PUBLIC rateStruct bestW[MAX_BEST];
+PUBLIC int numW = 0;
+*/
+
+#define MAXHIST 30
+#define LOWESTHIST 800
+PUBLIC int sHist[MAXHIST];
+PUBLIC int bHist[MAXHIST];
+PUBLIC int wHist[MAXHIST];
+PUBLIC int lHist[MAXHIST];
+
+char sdir[] = DEFAULT_STATS;
+
+PUBLIC int is_active(int Games)
+{
+ return (Games >= PROVISIONAL);
+}
+
+
+PUBLIC void rating_add(int rating, int type)
+{
+ int which;
+
+ which = (rating - LOWESTHIST) / 100;
+ if (which < 0)
+ which = 0;
+ if (which >= MAXHIST)
+ which = MAXHIST - 1;
+ if (type == TYPE_BLITZ) {
+ bHist[which] += 1;
+ Rb_count++;
+ Rb_total += rating;
+ if (Rb_count == 1) {
+ Rb_M = rating;
+ } else {
+ Rb_S = Rb_S + (rating - Rb_M) * (rating - Rb_M);
+ Rb_M = Rb_M + (rating - Rb_M) / (Rb_count);
+ }
+ Ratings_B_StdDev = sqrt(Rb_S / Rb_count);
+ Ratings_B_Average = Rb_total / (double) Rb_count;
+ } else if (type == TYPE_WILD) { /* TYPE_WILD */
+ wHist[which] += 1;
+ Rw_count++;
+ Rw_total += rating;
+ if (Rw_count == 1) {
+ Rw_M = rating;
+ } else {
+ Rw_S = Rw_S + (rating - Rw_M) * (rating - Rw_M);
+ Rw_M = Rw_M + (rating - Rw_M) / (Rw_count);
+ }
+ Ratings_W_StdDev = sqrt(Rw_S / Rw_count);
+ Ratings_W_Average = Rw_total / (double) Rw_count;
+ } else if (type == TYPE_LIGHT) { /* TYPE_LIGHT */
+ lHist[which] += 1;
+ Rl_count++;
+ Rl_total += rating;
+ if (Rl_count == 1) {
+ Rl_M = rating;
+ } else {
+ Rl_S = Rl_S + (rating - Rl_M) * (rating - Rl_M);
+ Rl_M = Rl_M + (rating - Rl_M) / (Rl_count);
+ }
+ Ratings_L_StdDev = sqrt(Rl_S / Rl_count);
+ Ratings_L_Average = Rl_total / (double) Rl_count;
+
+/* Insert bughouse stuff */
+ } else { /* TYPE_STAND */
+ sHist[which] += 1;
+ Rs_count++;
+ Rs_total += rating;
+ if (Rs_count == 1) {
+ Rs_M = rating;
+ } else {
+ Rs_S = Rs_S + (rating - Rs_M) * (rating - Rs_M);
+ Rs_M = Rs_M + (rating - Rs_M) / (Rs_count);
+ }
+ Ratings_S_StdDev = sqrt(Rs_S / Rs_count);
+ Ratings_S_Average = Rs_total / (double) Rs_count;
+ }
+}
+
+PUBLIC void rating_remove(int rating, int type)
+{
+ int which;
+
+ which = (rating - LOWESTHIST) / 100;
+ if (which < 0)
+ which = 0;
+ if (which >= MAXHIST)
+ which = MAXHIST - 1;
+ if (type == TYPE_BLITZ) {
+ bHist[which] = bHist[which] - 1;
+ if (bHist[which] < 0)
+ bHist[which] = 0;
+ if (Rb_count == 0)
+ return;
+ Rb_count--;
+ Rb_total -= rating;
+ if (Rb_count == 0) {
+ Rb_M = 0;
+ Rb_S = 0;
+ } else {
+ Rb_M = Rb_M - (rating - Rb_M) / (Rb_count);
+ Rb_S = Rb_S - (rating - Rb_M) * (rating - Rb_M);
+ /* added this 3.11.95 foxbat */ if (Rb_S < 0)
+ Rb_S = 0;
+ }
+ if (Rb_count) {
+ Ratings_B_StdDev = sqrt(Rb_S / Rb_count);
+ Ratings_B_Average = Rb_total / (double) Rb_count;
+ } else {
+ Ratings_B_StdDev = 0;
+ Ratings_B_Average = 0;
+ }
+ } else if (type == TYPE_WILD) { /* TYPE_WILD */
+ wHist[which] = wHist[which] - 1;
+ if (wHist[which] < 0)
+ wHist[which] = 0;
+ if (Rw_count == 0)
+ return;
+ Rw_count--;
+ Rw_total -= rating;
+ if (Rw_count == 0) {
+ Rw_M = 0;
+ Rw_S = 0;
+ } else {
+ Rw_M = Rw_M - (rating - Rw_M) / (Rw_count);
+ Rw_S = Rw_S - (rating - Rw_M) * (rating - Rw_M);
+ /* added this 3.10.95 foxbat */ if (Rw_S < 0)
+ Rw_S = 0;
+ }
+ if (Rw_count) {
+ Ratings_W_StdDev = sqrt(Rw_S / Rw_count);
+ Ratings_W_Average = Rw_total / (double) Rw_count;
+ } else {
+ Ratings_W_StdDev = 0;
+ Ratings_W_Average = 0;
+ }
+ } else if (type == TYPE_LIGHT) { /* TYPE_LIGHT */
+ lHist[which] = lHist[which] - 1;
+ if (lHist[which] < 0)
+ lHist[which] = 0;
+ if (Rl_count == 0)
+ return;
+ Rl_count--;
+ Rl_total -= rating;
+ if (Rl_count == 0) {
+ Rl_M = 0;
+ Rl_S = 0;
+ } else {
+ Rl_M = Rl_M - (rating - Rl_M) / (Rl_count);
+ Rl_S = Rl_S - (rating - Rl_M) * (rating - Rl_M);
+ /* added this 3.10.95 foxbat */ if (Rl_S < 0)
+ Rl_S = 0;
+ }
+ if (Rl_count) {
+ Ratings_L_StdDev = sqrt(Rl_S / Rl_count);
+ Ratings_L_Average = Rl_total / (double) Rl_count;
+ } else {
+ Ratings_L_StdDev = 0;
+ Ratings_L_Average = 0;
+ }
+/* insert bughouse stuff here */
+ } else { /* TYPE_STAND */
+ sHist[which] = sHist[which] - 1;
+ if (sHist[which] < 0)
+ sHist[which] = 0;
+ if (Rs_count == 0)
+ return;
+ Rs_count--;
+ Rs_total -= rating;
+ if (Rs_count == 0) {
+ Rs_M = 0;
+ Rs_S = 0;
+ } else {
+ Rs_M = Rs_M - (rating - Rs_M) / (Rs_count);
+ Rs_S = Rs_S - (rating - Rs_M) * (rating - Rs_M);
+ /* added this 3.10.95 foxbat */ if (Rs_S < 0)
+ Rs_S = 0;
+ }
+ if (Rs_count) {
+ Ratings_S_StdDev = sqrt(Rs_S / Rs_count);
+ Ratings_S_Average = Rs_total / (double) Rs_count;
+ } else {
+ Ratings_S_StdDev = 0;
+ Ratings_S_Average = 0;
+ }
+ }
+}
+
+PRIVATE void load_ratings(void)
+{
+ FILE *fp;
+ char fname[MAX_FILENAME_SIZE];
+ int i;
+
+ sprintf(fname, "%s/newratingsV%d_data", stats_dir,STATS_VERSION);
+ fp = fopen(fname, "r");
+ if (!fp) {
+ fprintf(stderr, "FICS: Can't read ratings data!\n");
+ return;
+ }
+ fscanf(fp, "%lf %lf %lf %d", &Rb_M, &Rb_S, &Rb_total, &Rb_count);
+ fscanf(fp, "%lf %lf %lf %d", &Rs_M, &Rs_S, &Rs_total, &Rs_count);
+ fscanf(fp, "%lf %lf %lf %d", &Rw_M, &Rw_S, &Rw_total, &Rw_count);
+ fscanf(fp, "%lf %lf %lf %d", &Rl_M, &Rl_S, &Rl_total, &Rl_count);
+
+ for (i = 0; i < MAXHIST; i++) {
+ fscanf(fp, "%d %d %d %d", &sHist[i], &bHist[i], &wHist[i], &lHist[i]);
+ }
+ fclose(fp);
+ if (Rs_count) {
+ Ratings_S_StdDev = sqrt(Rs_S / Rs_count);
+ Ratings_S_Average = Rs_total / (double) Rs_count;
+ } else {
+ Ratings_S_StdDev = 0;
+ Ratings_S_Average = 0;
+ }
+ if (Rb_count) {
+ Ratings_B_StdDev = sqrt(Rb_S / Rb_count);
+ Ratings_B_Average = Rb_total / (double) Rb_count;
+ } else {
+ Ratings_B_StdDev = 0;
+ Ratings_B_Average = 0;
+ }
+ if (Rw_count) {
+ Ratings_W_StdDev = sqrt(Rw_S / Rw_count);
+ Ratings_W_Average = Rw_total / (double) Rw_count;
+ } else {
+ Ratings_W_StdDev = 0;
+ Ratings_W_Average = 0;
+ }
+ if (Rl_count) {
+ Ratings_L_StdDev = sqrt(Rl_S / Rl_count);
+ Ratings_L_Average = Rl_total / (double) Rl_count;
+ } else {
+ Ratings_L_StdDev = 0;
+ Ratings_L_Average = 0;
+ }
+}
+
+PUBLIC void save_ratings(void)
+{
+ FILE *fp;
+ char fname[MAX_FILENAME_SIZE];
+ int i;
+
+ sprintf(fname, "%s/newratingsV%d_data", stats_dir,STATS_VERSION);
+ fp = fopen(fname, "w");
+ if (!fp) {
+ fprintf(stderr, "FICS: Can't write ratings data!\n");
+ return;
+ }
+ fprintf(fp, "%10f %10f %10f %d\n", Rb_M, Rb_S, Rb_total, Rb_count);
+ fprintf(fp, "%10f %10f %10f %d\n", Rs_M, Rs_S, Rs_total, Rs_count);
+ fprintf(fp, "%10f %10f %10f %d\n", Rw_M, Rw_S, Rw_total, Rw_count);
+ fprintf(fp, "%10f %10f %10f %d\n", Rl_M, Rl_S, Rl_total, Rl_count);
+
+ for (i = 0; i < MAXHIST; i++) {
+ fprintf(fp, "%d %d %d %d\n", sHist[i], bHist[i], wHist[i], lHist[i]);
+ }
+ fclose(fp);
+}
+
+/*
+PRIVATE void BestRemove(int p)
+{
+ int i;
+
+ for (i = 0; i < numB; i++) {
+ if (!strcmp(bestB[i].name, parray[p].name))
+ break;
+ }
+ if (i < numB) {
+ for (; i < numB - 1; i++) {
+ strcpy(bestB[i].name, bestB[i + 1].name);
+ bestB[i].rating = bestB[i + 1].rating;
+ }
+ numB--;
+ }
+ for (i = 0; i < numS; i++) {
+ if (!strcmp(bestS[i].name, parray[p].name))
+ break;
+ }
+ if (i < numS) {
+ for (; i < numS - 1; i++) {
+ strcpy(bestS[i].name, bestS[i + 1].name);
+ bestS[i].rating = bestS[i + 1].rating;
+ }
+ numS--;
+ }
+ for (i = 0; i < numW; i++) {
+ if (!strcmp(bestW[i].name, parray[p].name))
+ break;
+ }
+ if (i < numW) {
+ for (; i < numW - 1; i++) {
+ strcpy(bestW[i].name, bestW[i + 1].name);
+ bestW[i].rating = bestW[i + 1].rating;
+ }
+ numW--;
+ }
+}
+
+PRIVATE void BestAdd(int p)
+{
+ int where, j;
+
+ if ((parray[p].b_stats.rating > 0) && (parray[p].b_stats.num > 19)) {
+ for (where = 0; where < numB; where++) {
+ if (parray[p].b_stats.rating > bestB[where].rating)
+ break;
+ }
+ if (where < MAX_BEST) {
+ for (j = numB; j > where; j--) {
+ if (j == MAX_BEST)
+ continue;
+ strcpy(bestB[j].name, bestB[j - 1].name);
+ bestB[j].rating = bestB[j - 1].rating;
+ }
+ strcpy(bestB[where].name, parray[p].name);
+ bestB[where].rating = parray[p].b_stats.rating;
+ if (numB < MAX_BEST)
+ numB++;
+ }
+ }
+ if ((parray[p].s_stats.rating > 0) && (parray[p].s_stats.num > 19)) {
+ for (where = 0; where < numS; where++) {
+ if (parray[p].s_stats.rating > bestS[where].rating)
+ break;
+ }
+ if (where < MAX_BEST) {
+ for (j = numS; j > where; j--) {
+ if (j == MAX_BEST)
+ continue;
+ strcpy(bestS[j].name, bestS[j - 1].name);
+ bestS[j].rating = bestS[j - 1].rating;
+ }
+ strcpy(bestS[where].name, parray[p].name);
+ bestS[where].rating = parray[p].s_stats.rating;
+ if (numS < MAX_BEST)
+ numS++;
+ }
+ }
+ if ((parray[p].w_stats.rating > 0) && (parray[p].w_stats.num > 19)) {
+ for (where = 0; where < numW; where++) {
+ if (parray[p].w_stats.rating > bestW[where].rating)
+ break;
+ }
+ if (where < MAX_BEST) {
+ for (j = numW; j > where; j--) {
+ if (j == MAX_BEST)
+ continue;
+ strcpy(bestW[j].name, bestW[j - 1].name);
+ bestW[j].rating = bestW[j - 1].rating;
+ }
+ strcpy(bestW[where].name, parray[p].name);
+ bestW[where].rating = parray[p].w_stats.rating;
+ if (numW < MAX_BEST)
+ numW++;
+ }
+ }
+}
+
+PUBLIC void BestUpdate(int p)
+{
+ BestRemove(p);
+ BestAdd(p);
+}
+
+*/
+
+PUBLIC void zero_stats(void)
+{
+ int i;
+ for (i = 0; i < MAXHIST; i++) {
+ sHist[i] = 0;
+ bHist[i] = 0;
+ wHist[i] = 0;
+ lHist[i] = 0;
+ }
+ Rb_M = 0.0, Rb_S = 0.0, Rb_total = 0.0;
+ Rb_count = 0;
+
+ Rs_M = 0.0, Rs_S = 0.0, Rs_total = 0.0;
+ Rs_count = 0;
+
+ Rw_M = 0.0, Rw_S = 0.0, Rw_total = 0.0;
+ Rw_count = 0;
+
+ Rl_M = 0.0, Rl_S = 0.0, Rl_total = 0.0;
+ Rl_count = 0;
+
+/*
+ numS = 0;
+ numB = 0;
+ numW = 0;
+*/
+}
+
+PUBLIC void rating_init(void)
+{
+ zero_stats();
+ load_ratings();
+}
+
+/* This recalculates the rating info from the player data, it can take
+ a long time! */
+PUBLIC void rating_recalc(void)
+{
+ char dname[MAX_FILENAME_SIZE];
+ int p1;
+ int c;
+ int t = time(0);
+ DIR *dirp;
+#if USE_DIRENT
+ struct dirent *dp;
+#else
+ struct direct *dp;
+#endif
+
+ fprintf(stderr, "FICS: Recalculating ratings at %s\n", strltime(&t));
+ zero_stats();
+ for (c = 'a'; c <= 'z'; c++) {
+ /* Never done as ratings server */
+ sprintf(dname, "%s/%c", player_dir, c);
+ dirp = opendir(dname);
+ if (!dirp)
+ continue;
+ for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
+ if (dp->d_name[0] != '.') {
+ p1 = player_new();
+ if (player_read(p1, dp->d_name)) {
+ player_remove(p1);
+ fprintf(stderr, "FICS: Problem reading player %s.\n", dp->d_name);
+ continue;
+ }
+ if (parray[p1].b_stats.rating > 0) {
+ rating_add(parray[p1].b_stats.rating, TYPE_BLITZ);
+ }
+ if (parray[p1].s_stats.rating > 0) {
+ rating_add(parray[p1].s_stats.rating, TYPE_STAND);
+ }
+ if (parray[p1].l_stats.rating > 0) {
+ rating_add(parray[p1].l_stats.rating, TYPE_LIGHT);
+ }
+/* insert bughouse stuff here */
+ if (parray[p1].w_stats.rating > 0) {
+ rating_add(parray[p1].w_stats.rating, TYPE_WILD);
+ }
+ player_remove(p1);
+ }
+ }
+ closedir(dirp);
+ }
+ if (Rs_count) {
+ Ratings_S_StdDev = sqrt(Rs_S / Rs_count);
+ Ratings_S_Average = Rs_total / (double) Rs_count;
+ } else {
+ Ratings_S_StdDev = 0;
+ Ratings_S_Average = 0;
+ }
+ if (Rb_count) {
+ Ratings_B_StdDev = sqrt(Rb_S / Rb_count);
+ Ratings_B_Average = Rb_total / (double) Rb_count;
+ } else {
+ Ratings_B_StdDev = 0;
+ Ratings_B_Average = 0;
+ }
+ if (Rl_count) {
+ Ratings_L_StdDev = sqrt(Rl_S / Rl_count);
+ Ratings_L_Average = Rl_total / (double) Rl_count;
+ } else {
+ Ratings_L_StdDev = 0;
+ Ratings_L_Average = 0;
+ }
+ if (Rw_count) {
+ Ratings_W_StdDev = sqrt(Rw_S / Rw_count);
+ Ratings_W_Average = Rw_total / (double) Rw_count;
+ } else {
+ Ratings_W_StdDev = 0;
+ Ratings_W_Average = 0;
+ }
+ save_ratings();
+ t = time(0);
+ fprintf(stderr, "FICS: Finished at %s\n", strltime(&t));
+}
+
+PRIVATE int Round (double x)
+{
+ return (x < 0 ? (int) (x - 0.5) : (int) (x + 0.5));
+}
+
+/* Constants for Glicko system */
+#define Gd 3.25
+#define Gr0 1720
+#define Gs0 350
+#define Gq 0.00575646273249
+#define Gp 0.000010072398601964
+
+/* End of Glicko system variables */
+
+PRIVATE double Gf(double ss)
+{
+ return (1.0 / sqrt(1.0 + Gp * ss * ss));
+}
+
+/* Confusing but economical: calculate error and attenuation function together */
+PRIVATE double GE(int r, int rr, double ss, double *fss)
+{
+ *fss = Gf(ss);
+ return (1.0 / (1.0 + pow(10.0, (rr - r) * (*fss) / 400.0)));
+}
+
+PUBLIC double current_sterr(double s, int t)
+{
+ if (t < 0)
+ t = 0; /* this shouldn't happen */
+ return (sqrt(s * s + Gd * Gd * log(1.0 + t / 60.0)));
+}
+
+/* Calculates new rating and standard error. By vek. The person */
+/* who invented the ratings system is Mark E. Glickman, Ph.D. */
+/* His e-mail address is glickman@hustat.harvard.edu as of April '95. */
+/* Postscript copy of the note I coded this from should be available */
+/* for ftp from ics.onenet.net, if not elsewhere. */
+
+PUBLIC void rating_sterr_delta(int p1, int p2, int type, int gtime, int result,
+ int *deltarating, double *newsterr)
+{
+ statistics *p1_stats;
+ statistics *p2_stats;
+
+ double s1, s2;
+ int t1, r1, t2, r2; /* Initial sterrs and ratings */
+ double E, fs2, denominator, GK, w; /* Parts of fancy formulas */
+ double delta, sigma; /* Results to return */
+
+ if (type == TYPE_BLITZ) {
+ p1_stats = &parray[p1].b_stats;
+ p2_stats = &parray[p2].b_stats;
+ } else if (type == TYPE_WILD) {
+ p1_stats = &parray[p1].w_stats;
+ p2_stats = &parray[p2].w_stats;
+ } else if (type == TYPE_LIGHT) {
+ p1_stats = &parray[p1].l_stats;
+ p2_stats = &parray[p2].l_stats;
+/* insert bughouse stuff here */
+ } else {
+ p1_stats = &parray[p1].s_stats;
+ p2_stats = &parray[p2].s_stats;
+ }
+
+ /* Calculate effective pre-game sterrs. ltime==0 implies never had sterr. */
+
+ if (p1_stats->ltime == 0)
+ s1 = Gs0;
+ else {
+ t1 = gtime - p1_stats->ltime;
+ s1 = current_sterr(p1_stats->sterr, t1);
+ if (s1 > Gs0)
+ s1 = Gs0;
+ }
+
+ if (p2_stats->ltime == 0)
+ s2 = Gs0;
+ else {
+ t2 = gtime - p2_stats->ltime;
+ s2 = current_sterr(p2_stats->sterr, t2);
+ if (s2 > Gs0)
+ s2 = Gs0;
+ }
+
+ /* pre-game ratings */
+ if (p1_stats->rating == 0 && p1_stats->num == 0)
+ r1 = Gr0;
+ else
+ r1 = p1_stats->rating;
+
+ if (p2_stats->rating == 0 && p2_stats->num == 0)
+ r2 = Gr0;
+ else
+ r2 = p2_stats->rating;
+
+ /* now crunch */
+
+ if (result == RESULT_WIN) {
+ w = 1.0;
+ } else if (result == RESULT_DRAW) {
+ w = 0.5;
+ } else {
+ w = 0.0;
+ }
+
+ E = GE(r1, r2, s2, &fs2); /* side effect: calculate fs2 */
+
+ denominator = 1.0 / (s1 * s1) + Gq * Gq * fs2 * fs2 * E * (1.0 - E);
+ GK = Gq * fs2 / denominator;
+
+ delta = GK * (w - E);
+ if (p1_stats->rating == 0 && p1_stats->num == 0)
+ *deltarating = Round(Gr0 + delta);
+ else
+ *deltarating = Round(delta); /* Returned values: deltarating,
+ newsterr */
+ sigma = 1.0 / sqrt(denominator);
+ *newsterr = (double) sigma;
+}
+
+/* vek: Next is for when we want just the delta, and not the sigma. */
+
+PUBLIC int rating_delta(int p1, int p2, int type, int result, int gtime)
+{
+ int delta;
+ double sigma;
+
+ rating_sterr_delta(p1, p2, type, gtime, result, &delta, &sigma);
+ return (delta);
+
+}
+
+PUBLIC int rating_update(int g)
+{
+ int wDelta, bDelta;
+ double wSigma, bSigma; /* vek */
+
+ int wRes, bRes;
+ statistics *w_stats;
+ statistics *b_stats;
+
+ int gtime;
+
+ int inprogress = (g == parray[garray[g].black].game);
+ /* if this is adjudication of stored game, be quiet about ratings change */
+
+ if (garray[g].type == TYPE_BLITZ) {
+ w_stats = &parray[garray[g].white].b_stats;
+ b_stats = &parray[garray[g].black].b_stats;
+ } else if (garray[g].type == TYPE_STAND) {
+ w_stats = &parray[garray[g].white].s_stats;
+ b_stats = &parray[garray[g].black].s_stats;
+ } else if (garray[g].type == TYPE_WILD) {
+ w_stats = &parray[garray[g].white].w_stats;
+ b_stats = &parray[garray[g].black].w_stats;
+ } else if (garray[g].type == TYPE_LIGHT) {
+ w_stats = &parray[garray[g].white].l_stats;
+ b_stats = &parray[garray[g].black].l_stats;
+ } else {
+ fprintf(stderr, "FICS: Can't update untimed ratings!\n");
+ return -1;
+ }
+
+ switch (garray[g].result) {
+ case END_CHECKMATE:
+ case END_RESIGN:
+ case END_FLAG:
+ case END_ADJWIN:
+ if (garray[g].winner == WHITE) {
+ wRes = RESULT_WIN;
+ bRes = RESULT_LOSS;
+ } else {
+ bRes = RESULT_WIN;
+ wRes = RESULT_LOSS;
+ }
+ break;
+ case END_AGREEDDRAW:
+ case END_REPETITION:
+ case END_50MOVERULE:
+ case END_STALEMATE:
+ case END_NOMATERIAL:
+ case END_BOTHFLAG:
+ case END_ADJDRAW:
+ case END_FLAGNOMATERIAL:
+ wRes = bRes = RESULT_DRAW;
+ break;
+ default:
+ fprintf(stderr, "FICS: Update undecided game %d?\n", garray[g].result);
+ return -1;
+ }
+ gtime = untenths(garray[g].timeOfStart);
+ rating_sterr_delta(garray[g].white, garray[g].black,
+ garray[g].type, gtime, wRes,
+ &wDelta, &wSigma);
+
+ rating_sterr_delta(garray[g].black, garray[g].white,
+ garray[g].type, gtime, bRes,
+ &bDelta, &bSigma);
+
+ /* vek: Update time of last rated game played, for future ratings calcs. */
+ /* Kept independently for blitz and standard. */
+ w_stats->ltime = gtime;
+ b_stats->ltime = gtime;
+ /* end vek add 4/5/95 */
+
+ if (wRes == RESULT_WIN) {
+ w_stats->win++;
+ } else if (wRes == RESULT_LOSS) {
+ w_stats->los++;
+ } else {
+ w_stats->dra++;
+ }
+ w_stats->num++;
+ if (bRes == RESULT_WIN) {
+ b_stats->win++;
+ } else if (bRes == RESULT_LOSS) {
+ b_stats->los++;
+ } else {
+ b_stats->dra++;
+ }
+ b_stats->num++;
+ rating_remove(w_stats->rating, garray[g].type);
+ rating_remove(b_stats->rating, garray[g].type);
+
+ if (inprogress) {
+ pprintf(garray[g].white, "%s rating adjustment: %d ",
+ ((garray[g].type == TYPE_BLITZ) ? "Blitz" : ((garray[g].type == TYPE_WILD) ? "Wild" : ((garray[g].type == TYPE_LIGHT) ? "Lightning" : "Standard"))), w_stats->rating);
+ pprintf(garray[g].black, "%s rating adjustment: %d ",
+ ((garray[g].type == TYPE_BLITZ) ? "Blitz" : ((garray[g].type == TYPE_WILD) ? "Wild" : ((garray[g].type == TYPE_LIGHT) ? "Lightning" : "Standard"))), b_stats->rating);
+ }
+ if (wDelta < -1000) {
+ pprintf(garray[g].white, "not changed due to bug (way too small)! sorry!\n");
+ fprintf(stderr, "FICS: Got too small ratings bug for %s (w) vs. %s\n",
+ parray[garray[g].white].login, parray[garray[g].black].login);
+ } else if (wDelta > 3000) {
+ pprintf(garray[g].white, "not changed due to bug (way too big)! sorry!\n");
+ fprintf(stderr, "FICS: Got too big ratings bug for %s (w) vs. %s\n",
+ parray[garray[g].white].login, parray[garray[g].black].login);
+ } else {
+ w_stats->rating += wDelta;
+ w_stats->sterr = wSigma;
+ }
+
+ if (bDelta < -1000) {
+ pprintf(garray[g].black, "not changed due to bug (way too small)! sorry! ");
+ fprintf(stderr, "FICS: Got too small ratings bug for %s (b) vs. %s\n",
+ parray[garray[g].black].login, parray[garray[g].white].login);
+ } else if (bDelta > 3000) {
+ pprintf(garray[g].black, "not changed due to bug (way too big)! sorry! ");
+ fprintf(stderr, "FICS: Got too big ratings bug for %s (b) vs. %s\n",
+ parray[garray[g].black].login, parray[garray[g].white].login);
+ } else {
+ b_stats->rating += bDelta;
+ b_stats->sterr = bSigma;
+ } /* error messages down to vek */
+
+ rating_add(w_stats->rating, garray[g].type);
+ rating_add(b_stats->rating, garray[g].type);
+
+ if ((w_stats->rating > w_stats->best) && (is_active(w_stats->num))) {
+ w_stats->best = w_stats->rating;
+ w_stats->whenbest = time(NULL);
+ }
+ if ((b_stats->rating > b_stats->best) && (is_active(b_stats->num))) {
+ b_stats->best = b_stats->rating;
+ b_stats->whenbest = time(NULL);
+ }
+/* Ratings are now saved to disk after each game */
+ player_save(garray[g].white);
+ player_save(garray[g].black);
+
+/* foxbat 3.11.95 */
+ if (garray[g].type == TYPE_BLITZ) {
+ Rb_count++;
+ Rb_total += (w_stats->rating + b_stats->rating) / 2.0;
+ } else if (garray[g].type == TYPE_STAND) {
+ Rs_count++;
+ Rs_total += (w_stats->rating + b_stats->rating) / 2.0;
+ } else if (garray[g].type == TYPE_LIGHT) {
+ Rl_count++;
+ Rl_total += (w_stats->rating + b_stats->rating) / 2.0;
+ } else if (garray[g].type == TYPE_WILD) {
+ Rw_count++;
+ Rw_total += (w_stats->rating + b_stats->rating) / 2.0;
+ }
+/* end add */
+ if (inprogress) {
+ pprintf(garray[g].white, "--> %d\n", w_stats->rating);
+ pprintf(garray[g].black, "--> %d\n", b_stats->rating);
+ }
+ save_ratings();
+ UpdateRank(garray[g].type, parray[garray[g].white].name,
+ w_stats, parray[garray[g].white].name);
+ UpdateRank(garray[g].type, parray[garray[g].black].name,
+ b_stats, parray[garray[g].black].name);
+ return 0;
+}
+
+PUBLIC int com_assess(int p, param_list param)
+{
+ int p1 = p, p2, nowtime;
+ int p1_connected = 1, p2_connected = 1;
+ int win1, draw1, loss1;
+ double newsterr1;
+ int win2, draw2, loss2;
+ double newsterr2;
+
+ nowtime = time(0);
+
+/* Hawk: Now assess can be used with players not */
+/* logged on -- I wonder if anyone doesn't */
+/* get just a bit confused here :) */
+
+ if (param[0].type == TYPE_NULL) {
+ if (parray[p].game < 0) {
+ pprintf(p, "You are not playing a game.\n");
+ return COM_OK;
+ } else if (garray[parray[p].game].status == GAME_EXAMINE) {
+ if (!strcmp(garray[parray[p].game].black_name, parray[p].name)) {
+ pcommand(p, "assess %s\n", garray[parray[p].game].white_name);
+ } else {
+ pcommand(p, "assess %s %s\n",
+ garray[parray[p].game].white_name,
+ garray[parray[p].game].black_name);
+ }
+ return COM_OK;
+ } else {
+ p2 = parray[p].opponent;
+ }
+ } else {
+ if (!FindPlayer(p, param[0].val.word, &p2, &p2_connected)) {
+ pprintf(p, "No user named \"%s\" was found.\n", param[0].val.word);
+ return COM_OK;
+ }
+ if (param[1].type != TYPE_NULL) {
+ p1 = p2;
+ p1_connected = p2_connected;
+ if (!FindPlayer(p, param[1].val.word, &p2, &p2_connected)) {
+ pprintf(p, "No user named \"%s\" was found.\n", param[1].val.word);
+ if (!p1_connected)
+ player_remove(p1);
+ return COM_OK;
+ }
+ }
+ }
+ if (p1 == p2) {
+ pprintf(p, "You can't assess the same players.\n");
+ if (!p1_connected)
+ player_remove(p1);
+ if (!p2_connected)
+ player_remove(p2);
+ return COM_OK;
+ }
+ rating_sterr_delta(p1, p2, TYPE_BLITZ, nowtime, RESULT_WIN, &win1, &newsterr1);
+ rating_sterr_delta(p1, p2, TYPE_BLITZ, nowtime, RESULT_DRAW, &draw1, &newsterr1);
+ rating_sterr_delta(p1, p2, TYPE_BLITZ, nowtime, RESULT_LOSS, &loss1, &newsterr1);
+ rating_sterr_delta(p2, p1, TYPE_BLITZ, nowtime, RESULT_WIN, &win2, &newsterr2);
+ rating_sterr_delta(p2, p1, TYPE_BLITZ, nowtime, RESULT_DRAW, &draw2, &newsterr2);
+ rating_sterr_delta(p2, p1, TYPE_BLITZ, nowtime, RESULT_LOSS, &loss2, &newsterr2);
+
+ pprintf(p, "\nBlitz\n %10s (%4s, RD: %5.1f) %10s (%4s, RD: %5.1f)\n",
+ parray[p1].name, ratstrii(parray[p1].b_stats.rating, parray[p1].registered), parray[p1].b_stats.sterr,
+ parray[p2].name, ratstrii(parray[p2].b_stats.rating, parray[p2].registered), parray[p2].b_stats.sterr);
+ pprintf(p, "Win : %4d %4d\n", win1, loss2);
+ pprintf(p, "Draw: %4d %4d\n", draw1, draw2);
+ pprintf(p, "Loss: %4d %4d\n", loss1, win2);
+ pprintf(p, "New RD: %5.1f %5.1f\n", newsterr1, newsterr2);
+
+ rating_sterr_delta(p1, p2, TYPE_STAND, nowtime, RESULT_WIN, &win1, &newsterr1);
+ rating_sterr_delta(p1, p2, TYPE_STAND, nowtime, RESULT_DRAW, &draw1, &newsterr1);
+ rating_sterr_delta(p1, p2, TYPE_STAND, nowtime, RESULT_LOSS, &loss1, &newsterr1);
+ rating_sterr_delta(p2, p1, TYPE_STAND, nowtime, RESULT_WIN, &win2, &newsterr2);
+ rating_sterr_delta(p2, p1, TYPE_STAND, nowtime, RESULT_DRAW, &draw2, &newsterr2);
+ rating_sterr_delta(p2, p1, TYPE_STAND, nowtime, RESULT_LOSS, &loss2, &newsterr2);
+
+ pprintf(p, "\nStandard\n %10s (%4s, RD: %5.1f) %10s (%4s, RD: %5.1f)\n",
+ parray[p1].name, ratstrii(parray[p1].s_stats.rating, parray[p1].registered), parray[p1].s_stats.sterr,
+ parray[p2].name, ratstrii(parray[p2].s_stats.rating, parray[p2].registered), parray[p2].s_stats.sterr);
+ pprintf(p, "Win : %4d %4d\n", win1, loss2);
+ pprintf(p, "Draw: %4d %4d\n", draw1, draw2);
+ pprintf(p, "Loss: %4d %4d\n", loss1, win2);
+ pprintf(p, "New RD: %5.1f %5.1f\n", newsterr1, newsterr2);
+
+ rating_sterr_delta(p1, p2, TYPE_LIGHT, nowtime, RESULT_WIN, &win1, &newsterr1); rating_sterr_delta(p1, p2, TYPE_LIGHT, nowtime, RESULT_DRAW, &draw1, &newsterr1);
+ rating_sterr_delta(p1, p2, TYPE_LIGHT, nowtime, RESULT_LOSS, &loss1, &newsterr1);
+ rating_sterr_delta(p2, p1, TYPE_LIGHT, nowtime, RESULT_WIN, &win2, &newsterr2); rating_sterr_delta(p2, p1, TYPE_LIGHT, nowtime, RESULT_DRAW, &draw2, &newsterr2);
+ rating_sterr_delta(p2, p1, TYPE_LIGHT, nowtime, RESULT_LOSS, &loss2, &newsterr2);
+
+ pprintf(p, "\nLightning\n %10s (%4s, RD: %5.1f) %10s (%4s, RD: %5.1f)\n",
+ parray[p1].name, ratstrii(parray[p1].l_stats.rating, parray[p1].registered), parray[p1].l_stats.sterr,
+ parray[p2].name, ratstrii(parray[p2].l_stats.rating, parray[p2].registered), parray[p2].l_stats.sterr);
+ pprintf(p, "Win : %4d %4d\n", win1, loss2);
+ pprintf(p, "Draw: %4d %4d\n", draw1, draw2);
+ pprintf(p, "Loss: %4d %4d\n", loss1, win2);
+ pprintf(p, "New RD: %5.1f %5.1f\n", newsterr1, newsterr2);
+
+ rating_sterr_delta(p1, p2, TYPE_WILD, nowtime, RESULT_WIN, &win1, &newsterr1);
+ rating_sterr_delta(p1, p2, TYPE_WILD, nowtime, RESULT_DRAW, &draw1, &newsterr1);
+ rating_sterr_delta(p1, p2, TYPE_WILD, nowtime, RESULT_LOSS, &loss1, &newsterr1);
+ rating_sterr_delta(p2, p1, TYPE_WILD, nowtime, RESULT_WIN, &win2, &newsterr2);
+ rating_sterr_delta(p2, p1, TYPE_WILD, nowtime, RESULT_DRAW, &draw2, &newsterr2);
+ rating_sterr_delta(p2, p1, TYPE_WILD, nowtime, RESULT_LOSS, &loss2, &newsterr2);
+
+ pprintf(p, "\nWild\n %10s (%4s, RD: %5.1f) %10s (%4s, RD: %5.1f)\n",
+ parray[p1].name, ratstrii(parray[p1].w_stats.rating, parray[p1].registered), parray[p1].w_stats.sterr,
+ parray[p2].name, ratstrii(parray[p2].w_stats.rating, parray[p2].registered), parray[p2].w_stats.sterr);
+ pprintf(p, "Win : %4d %4d\n", win1, loss2);
+ pprintf(p, "Draw: %4d %4d\n", draw1, draw2);
+ pprintf(p, "Loss: %4d %4d\n", loss1, win2);
+ pprintf(p, "New RD: %5.1f %5.1f\n", newsterr1, newsterr2);
+
+ if (!p1_connected)
+ player_remove(p1);
+ if (!p2_connected)
+ player_remove(p2);
+ return COM_OK;
+}
+
+
+PUBLIC int com_best(int p, param_list param)
+{
+ return Best(p, param, 1);
+}
+
+PUBLIC int com_hbest(int p, param_list param)
+{
+ return Best(p, param, 0);
+}
+
+#if 0
+PUBLIC int com_best(int p, param_list param)
+{
+ int i;
+
+ pprintf(p, "Standard Blitz Wild\n");
+ for (i = 0; i < MAX_BEST; i++) {
+ if ((i >= numS) && (i >= numB))
+ break;
+ if (i < numS) {
+ pprintf(p, "%4d %-17s ", bestS[i].rating, bestS[i].name);
+ } else {
+ pprintf(p, " ");
+ }
+ if (i < numB) {
+ pprintf(p, "%4d %-17s ", bestB[i].rating, bestB[i].name);
+ } else {
+ pprintf(p, " ");
+ }
+ if (i < numW) {
+ pprintf(p, "%4d %-17s\n", bestW[i].rating, bestW[i].name);
+ } else {
+ pprintf(p, "\n");
+ }
+ }
+ return COM_OK;
+}
+#endif
+
+PUBLIC int com_statistics(int p, param_list param)
+{
+ pprintf(p, " Standard Blitz Lightning Wild\n");
+ pprintf(p, "average: %7.2f %7.2f %7.2f %7.2f\n", Ratings_S_Average, Ratings_B_Average, Ratings_L_Average, Ratings_W_Average);
+ pprintf(p, "std dev: %7.2f %7.2f %7.2f %7.2f\n", Ratings_S_StdDev, Ratings_B_StdDev, Ratings_L_StdDev, Ratings_W_StdDev);
+ pprintf(p, "number : %7d %7d %7d %7d\n", Rs_count, Rb_count, Rl_count, Rw_count);
+ return COM_OK;
+}
+
+PUBLIC int com_fixrank(int p, param_list param)
+{
+ int p1, connected;
+
+ if (!FindPlayer(p, param[0].val.word, &p1, &connected))
+ return COM_OK;
+ UpdateRank(TYPE_BLITZ, parray[p1].name, &parray[p1].b_stats,
+ parray[p1].name);
+ UpdateRank(TYPE_STAND, parray[p1].name, &parray[p1].s_stats,
+ parray[p1].name);
+ UpdateRank(TYPE_WILD, parray[p1].name, &parray[p1].w_stats,
+ parray[p1].name);
+ if (!connected)
+ player_remove(p1);
+ return COM_OK;
+}
+
+PUBLIC int com_rank(int p, param_list param)
+{
+ return DisplayRank(p, param, 1);
+}
+
+PUBLIC int com_hrank(int p, param_list param)
+{
+ return DisplayRank(p, param, 0);
+}
+
+PUBLIC int DisplayRank(int p, param_list param, int showComputers)
+{
+ int start, end, target, connected;
+ int show = SHOW_BLITZ | SHOW_STANDARD | SHOW_WILD;
+
+ if (param[0].type == TYPE_NULL) {
+ DisplayTargetRank(p, parray[p].name, show, showComputers);
+ return COM_OK;
+ } else if (isdigit(param[0].val.word[0])) {
+ end = -1;
+ sscanf(param[0].val.word, "%d-%d", &start, &end);
+ if (end > 0 && (param[1].type != TYPE_NULL))
+ show = ShowFromString(param[1].val.word);
+ DisplayRankedPlayers(p, start, end, show, showComputers);
+ return COM_OK;
+ } else {
+ target = player_search(p, param[0].val.word);
+ if (target == 0) {
+ pprintf(p, "Target %s not found.\n", param[0].val.word);
+ return COM_OK;
+ }
+ connected = (target > 0);
+ if (!connected)
+ target = -target - 1;
+ else
+ target--;
+
+ if (param[1].type != TYPE_NULL)
+ show = ShowFromString(param[1].val.word);
+ DisplayTargetRank(p, parray[target].name, show, showComputers);
+ if (!connected)
+ player_remove(target);
+ return COM_OK;
+ }
+}
+
+/* CompareStats returns 1 if s1 comes first, -1 if s2 comes first, and 0
+ if neither takes precedence. */
+#if 0
+PRIVATE int CompareStats(char *name1, statistics *s1,
+ char *name2, statistics *s2)
+{
+ int i, l1;
+
+ if (s1 == NULL)
+ if (s2 == NULL)
+ return 0;
+ else
+ return -1;
+ else if (s2 == NULL)
+ return 1;
+
+ if (s1->rating > s2->rating)
+ return 1;
+ if (s1->rating < s2->rating)
+ return -1;
+ l1 = strlen(name1);
+ for (i = 0; i < l1; i++) {
+ if (name2[i] == '\0')
+ return -1;
+ if (tolower(name1[i]) < tolower(name2[i]))
+ return 1;
+ if (tolower(name1[i]) > tolower(name2[i]))
+ return -1;
+ }
+ if (name2[i] != '\0')
+ return 1;
+/* if (s1->sterr < s2->sterr) return 1;
+ if (s1->sterr > s2->sterr) return -1;
+ if (s1->num > s2->num) return 1;
+ if (s1->num < s2->num) return -1;
+*/
+ fprintf(stderr, "Duplicate entries found: %s.\n", name1);
+ return 0;
+}
+#endif
+PRIVATE int GetRankFileName(char *out, int type)
+{
+ switch (type) {
+ case TYPE_BLITZ:
+ sprintf(out, "%s/rank.blitz", sdir);
+ return type;
+ case TYPE_STAND:
+ sprintf(out, "%s/rank.std", sdir);
+ return type;
+ case TYPE_WILD:
+ sprintf(out, "%s/rank.wild", sdir);
+ return type;
+ default:
+ return -1;
+ }
+}
+
+/* loon: Turning this off 28 Oct 1995 (temporary:)) since we're lagged
+ into outer space */
+PUBLIC void UpdateRank(int type, char *addName,
+ statistics *sNew, char *delName)
+{}
+
+#if 0
+PUBLIC void UpdateRank(int type, char *addName,
+ statistics *sNew, char *delName)
+{
+ char RankFile[MAX_FILENAME_SIZE];
+ char TmpRankFile[MAX_FILENAME_SIZE];
+ char line[MAX_RANK_LINE];
+ char login[MAX_LOGIN_NAME];
+ char command[MAX_STRING_LENGTH];
+ int comp;
+ statistics sCur;
+ FILE *fp;
+ FILE *fptemp;
+
+ if (GetRankFileName(RankFile, type) < 0)
+ return;
+ fp = fopen(RankFile, "r");
+ if (fp == NULL) {
+ fprintf(stderr, "Can't open rank file to update.\n");
+ return;
+ }
+ sprintf(TmpRankFile, "%s/tmpRank", sdir);
+ fptemp = fopen(TmpRankFile, "w");
+ if (fptemp == NULL) {
+ fprintf (stderr, "Unable to open rank file for updating.\n");
+ return;
+ }
+ while (fgets(line, MAX_RANK_LINE - 1, fp)) {
+ sscanf(line, "%s %d %d %d", login, &sCur.rating,
+ &sCur.num, &comp);
+ if (delName != NULL && !strcasecmp(delName, login)) { /* Kill name. */
+ delName = NULL;
+ continue;
+ }
+ if (addName != NULL && CompareStats(addName, sNew, login, &sCur) > 0) {
+ int computer = in_list(-1, L_COMPUTER, addName);
+ fprintf(fptemp, "%s %d %d %d\n", addName, sNew->rating,
+ sNew->num, computer);
+ addName = NULL;
+ }
+ fprintf(fptemp, "%s %d %d %d\n", login, sCur.rating, sCur.num, comp);
+ }
+ fclose(fptemp);
+ fclose(fp);
+
+ sprintf(command, "mv %s %s", TmpRankFile, RankFile);
+ system(command);
+}
+#endif
+
+PRIVATE void DisplayRankHead(int p, int show)
+{
+ char Line[MAX_STRING_LENGTH];
+
+ Line[0] = '\0';
+ if (CheckFlag(show, SHOW_BLITZ))
+ strcat(Line, " Blitz ");
+ if (CheckFlag(show, SHOW_STANDARD))
+ strcat(Line, " Standard ");
+ if (CheckFlag(show, SHOW_WILD))
+ strcat(Line, " Wild");
+ pprintf(p, "%s\n\n", Line);
+}
+
+PRIVATE int CountRankLine(int countComp, char *loginName,
+ int num, int is_computer)
+{
+ if (loginName == NULL || loginName[0] == '\0')
+ return 0;
+ return (countComp || !is_computer) && (is_active(num));
+}
+
+PRIVATE int GetRank(FILE * fp, char *target, int countComp)
+{
+ int count = 0;
+ int nGames, is_computer;
+ int playerFound = 0;
+ char line[MAX_RANK_LINE];
+ char login[MAX_LOGIN_NAME];
+
+ while (fgets(line, MAX_RANK_LINE - 1, fp) && !playerFound) {
+ sscanf(line, "%s %*d %d %d", login, &nGames, &is_computer);
+ if ((playerFound = !strcasecmp(login, target))
+ || CountRankLine(countComp, login, nGames, is_computer))
+ count++;
+ }
+ return (playerFound ? count : -1);
+}
+
+PRIVATE void PositionFilePtr(FILE * fp, int count, int *last,
+ int *nTied, int showComp)
+{
+ int i, rating, nGames, is_computer;
+ char login[MAX_LOGIN_NAME];
+ char line[MAX_RANK_LINE];
+
+ if (fp == NULL)
+ return;
+ rewind(fp);
+ for (i = 1; i < count; i++) {
+ do {
+ fgets(line, MAX_RANK_LINE - 1, fp);
+ if (feof(fp))
+ break;
+ sscanf(line, "%s %d %d %d", login, &rating, &nGames, &is_computer);
+ } while (!CountRankLine(showComp, login, nGames, is_computer));
+ if (rating != *last) {
+ *nTied = 1;
+ *last = rating;
+ } else
+ (*nTied)++;
+ }
+}
+
+PRIVATE int ShowRankEntry(int p, FILE * fp, int count, int comp,
+ char *target, int *lastRating, int *nTied)
+{
+ char newLine[MAX_RANK_LINE];
+ char login[MAX_LOGIN_NAME];
+ int rating, findable, nGames, is_comp;
+
+ findable = (count > 0) && !feof(fp);
+ if (findable) {
+ do {
+ fgets(newLine, MAX_RANK_LINE - 1, fp);
+ if (feof(fp))
+ findable = 0;
+ else if (newLine[0] != '\0')
+ sscanf(newLine, "%s %d %d %d",
+ login, &rating, &nGames, &is_comp);
+ else
+ login[0] = '\0';
+ } while (!CountRankLine(comp, login, nGames, is_comp) && findable
+ && strcasecmp(login, target));
+ }
+ if (findable) {
+ if (!strcasecmp(login, target)
+ && !CountRankLine(comp, login, nGames, is_comp)) {
+ pprintf_highlight(p, "---- %-12.12s %4s", login, ratstr(rating));
+ pprintf(p, " ");
+ return 0;
+ } else if (*lastRating == rating && *nTied < 1) {
+ pprintf(p, " ");
+ if (!strcasecmp(login, target))
+ pprintf_highlight(p, "%-12.12s %4s", login, ratstr(rating));
+ else
+ pprintf(p, "%-12.12s %4s", login, ratstr(rating));
+ pprintf(p, " ");
+ return 1;
+ } else {
+ if (*nTied >= 1) {
+ if (*lastRating == rating)
+ count -= *nTied;
+ *nTied = -1;
+ }
+ if (!strcasecmp(login, target))
+ pprintf_highlight(p, "%4d. %-12.12s %4s",
+ count, login, ratstr(rating));
+ else
+ pprintf(p, "%4d. %-12.12s %4s",
+ count, login, ratstr(rating));
+ pprintf(p, " ");
+ *lastRating = rating;
+ return 1;
+ }
+ } else {
+ pprintf(p, "%25s", "");
+ return 1;
+ }
+}
+
+PRIVATE int CountAbove(int num, int blitz, int std, int wild, int which)
+{
+ int max = blitz;
+
+ if (max < std)
+ max = std;
+ if (max < wild)
+ max = wild;
+ return (max <= (num + 1) / 2 ? max - 1 : (num + 1) / 2);
+}
+
+PRIVATE int ShowRankLines(int p, FILE * fb, FILE * fs, FILE * fw, int bCount,
+ int sCount, int wCount, int n, int showComp, int show, char *target)
+{
+ int lastBlitz = 9999, nTiedBlitz = 0;
+ int lastStd = 9999, nTiedStd = 0;
+ int lastWild = 9999, nTiedWild = 0;
+ int i;
+
+ if (n <= 0)
+ return 0;
+ if (CheckFlag(show, SHOW_BLITZ)) {
+ PositionFilePtr(fb, bCount, &lastBlitz, &nTiedBlitz, showComp);
+ if (feof(fb))
+ ClearFlag(show, SHOW_BLITZ);
+ }
+ if (CheckFlag(show, SHOW_STANDARD)) {
+ PositionFilePtr(fs, sCount, &lastStd, &nTiedStd, showComp);
+ if (feof(fs))
+ ClearFlag(show, SHOW_STANDARD);
+ }
+ if (CheckFlag(show, SHOW_WILD)) {
+ PositionFilePtr(fw, wCount, &lastWild, &nTiedWild, showComp);
+ if (feof(fw))
+ ClearFlag(show, SHOW_WILD);
+ }
+ if (!CheckFlag(show, SHOW_BLITZ | SHOW_STANDARD | SHOW_WILD))
+ return 0;
+ DisplayRankHead(p, show);
+
+ for (i = 0; i < n && show; i++) {
+ if (CheckFlag(show, SHOW_BLITZ))
+ bCount += ShowRankEntry(p, fb, bCount, showComp, target,
+ &lastBlitz, &nTiedBlitz);
+ if (CheckFlag(show, SHOW_STANDARD))
+ sCount += ShowRankEntry(p, fs, sCount, showComp, target,
+ &lastStd, &nTiedStd);
+ if (CheckFlag(show, SHOW_WILD))
+ wCount += ShowRankEntry(p, fw, wCount, showComp, target,
+ &lastWild, &nTiedWild);
+ pprintf(p, "\n");
+ }
+ return 1;
+}
+
+PUBLIC int DisplayTargetRank(int p, char *target, int show, int showComp)
+{
+ int numToShow = 20;
+ int blitzRank = -1, blitzCount;
+ int stdRank = -1, stdCount;
+ int wildRank = -1, wildCount;
+ int numAbove;
+ char Path[MAX_FILENAME_SIZE];
+ FILE *fb = NULL, *fs = NULL, *fw = NULL;
+
+ if (CheckFlag(show, SHOW_BLITZ)) {
+ GetRankFileName(Path, TYPE_BLITZ);
+ fb = (FILE *) fopen(Path, "r");
+ if (fb != NULL)
+ blitzRank = GetRank(fb, target, showComp);
+ if (blitzRank < 0)
+ ClearFlag(show, SHOW_BLITZ);
+ }
+ if (CheckFlag(show, SHOW_STANDARD)) {
+ GetRankFileName(Path, TYPE_STAND);
+ fs = (FILE *) fopen(Path, "r");
+ if (fs != NULL)
+ stdRank = GetRank(fs, target, showComp);
+ if (stdRank < 0)
+ ClearFlag(show, SHOW_STANDARD);
+ }
+ if (CheckFlag(show, SHOW_WILD)) {
+ GetRankFileName(Path, TYPE_WILD);
+ if (CheckFlag(show, SHOW_WILD))
+ fw = (FILE *) fopen(Path, "r");
+ if (fw != NULL)
+ wildRank = GetRank(fw, target, showComp);
+ if (wildRank < 0)
+ ClearFlag(show, SHOW_WILD);
+ }
+ if (!CheckFlag(show, SHOW_BLITZ | SHOW_STANDARD | SHOW_WILD)) {
+ pprintf(p, "No ratings to show.\n");
+ if (fb != NULL) fclose(fb);
+ if (fs != NULL) fclose(fs);
+ if (fw != NULL) fclose(fw);
+ return (0);
+ }
+ numAbove = CountAbove(numToShow, blitzRank, stdRank, wildRank, show);
+ blitzCount = blitzRank - numAbove;
+ stdCount = stdRank - numAbove;
+ wildCount = wildRank - numAbove;
+
+ ShowRankLines(p, fb, fs, fw, blitzCount, stdCount, wildCount,
+ numToShow, showComp, show, target);
+ if (fb != NULL) fclose(fb);
+ if (fs != NULL) fclose(fs);
+ if (fw != NULL) fclose(fw);
+ return (1);
+}
+
+PUBLIC int DisplayRankedPlayers(int p, int start, int end,
+ int show, int showComp)
+{
+ int num = end - start + 1;
+ FILE *fb = NULL, *fs = NULL, *fw = NULL;
+ char Path[MAX_FILENAME_SIZE];
+
+ if (start <= 0)
+ start = 1;
+ if (num <= 0)
+ return 0;
+ if (num > 100)
+ num = 100;
+ if (CheckFlag(show, SHOW_BLITZ)) {
+ GetRankFileName(Path, TYPE_BLITZ);
+ fb = (FILE *) fopen(Path, "r");
+ if (fb == NULL)
+ ClearFlag(show, SHOW_BLITZ);
+ }
+ if (CheckFlag(show, SHOW_STANDARD)) {
+ GetRankFileName(Path, TYPE_STAND);
+ fs = (FILE *) fopen(Path, "r");
+ if (fs == NULL)
+ ClearFlag(show, SHOW_STANDARD);
+ }
+ if (CheckFlag(show, SHOW_WILD)) {
+ GetRankFileName(Path, TYPE_WILD);
+ fw = (FILE *) fopen(Path, "r");
+ if (fw == NULL)
+ ClearFlag(show, SHOW_WILD);
+ }
+ ShowRankLines(p, fb, fs, fw, start, start, start,
+ num, showComp, show, "");
+ if (fb)
+ fclose(fb);
+ if (fs)
+ fclose(fs);
+ if (fw)
+ fclose(fw);
+ return 1;
+}
+
+PUBLIC int ShowFromString(char *s)
+{
+ int i, len = strlen(s);
+ int show = 0;
+
+ if (s == NULL || s[0] == '\0')
+ return SHOW_BLITZ | SHOW_STANDARD | SHOW_WILD;
+ for (i = 0; i < len; i++) {
+ switch (s[i]) {
+ case 'b':
+ SetFlag(show, SHOW_BLITZ);
+ break;
+ case 's':
+ SetFlag(show, SHOW_STANDARD);
+ break;
+ case 'w':
+ SetFlag(show, SHOW_WILD);
+ break;
+ }
+ }
+ return (show);
+}
+
+PUBLIC int Best(int p, param_list param, int ShowComp)
+{
+ int show = SHOW_BLITZ | SHOW_STANDARD | SHOW_WILD;
+
+ if (param[0].type != TYPE_NULL)
+ show = ShowFromString(param[0].val.word);
+
+ DisplayRankedPlayers(p, 1, 20, show, ShowComp);
+ return COM_OK;
+}
diff --git a/FICS/ratings.c.orig b/FICS/ratings.c.orig
new file mode 100644
index 0000000..37e26f9
--- /dev/null
+++ b/FICS/ratings.c.orig
@@ -0,0 +1,1432 @@
+/*
+ ratings.c
+
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name email yy/mm/dd Change
+ Richard Nash 93/10/22 Created
+ vek leeds@math.gatech.edu 95/04/05 Glicko system, with sterr
+*/
+
+#include "stdinclude.h"
+
+#include "common.h"
+#include "playerdb.h"
+#include "ratings.h"
+#include "gamedb.h"
+#include "command.h"
+#include "comproc.h"
+#include "lists.h"
+#include "ficsmain.h"
+#include "config.h"
+#include "utils.h"
+
+/* grimm */
+#if defined(SGI)
+#else
+/* int system(char *arg); */
+/* int rewind(FILE *stream); */
+#endif
+
+
+PUBLIC double Ratings_B_Average;
+PUBLIC double Ratings_B_StdDev;
+
+PUBLIC double Ratings_S_Average;
+PUBLIC double Ratings_S_StdDev;
+
+PUBLIC double Ratings_W_Average;
+PUBLIC double Ratings_W_StdDev;
+
+PRIVATE double Rb_M = 0.0, Rb_S = 0.0, Rb_total = 0.0;
+PRIVATE int Rb_count = 0;
+
+PRIVATE double Rs_M = 0.0, Rs_S = 0.0, Rs_total = 0.0;
+PRIVATE int Rs_count = 0;
+
+PRIVATE double Rw_M = 0.0, Rw_S = 0.0, Rw_total = 0.0;
+PRIVATE int Rw_count = 0;
+
+/*
+PUBLIC rateStruct bestS[MAX_BEST];
+PUBLIC int numS = 0;
+PUBLIC rateStruct bestB[MAX_BEST];
+PUBLIC int numB = 0;
+PUBLIC rateStruct bestW[MAX_BEST];
+PUBLIC int numW = 0;
+*/
+
+#define MAXHIST 30
+#define LOWESTHIST 800
+PUBLIC int sHist[MAXHIST];
+PUBLIC int bHist[MAXHIST];
+PUBLIC int wHist[MAXHIST];
+
+char sdir[] = DEFAULT_STATS;
+
+PUBLIC int is_active(int Games)
+{
+ return (Games >= PROVISIONAL);
+}
+
+
+PUBLIC void rating_add(int rating, int type)
+{
+ int which;
+
+ which = (rating - LOWESTHIST) / 100;
+ if (which < 0)
+ which = 0;
+ if (which >= MAXHIST)
+ which = MAXHIST - 1;
+ if (type == TYPE_BLITZ) {
+ bHist[which] += 1;
+ Rb_count++;
+ Rb_total += rating;
+ if (Rb_count == 1) {
+ Rb_M = rating;
+ } else {
+ Rb_S = Rb_S + (rating - Rb_M) * (rating - Rb_M);
+ Rb_M = Rb_M + (rating - Rb_M) / (Rb_count);
+ }
+ Ratings_B_StdDev = sqrt(Rb_S / Rb_count);
+ Ratings_B_Average = Rb_total / (double) Rb_count;
+ } else if (type == TYPE_WILD) { /* TYPE_WILD */
+ wHist[which] += 1;
+ Rw_count++;
+ Rw_total += rating;
+ if (Rw_count == 1) {
+ Rw_M = rating;
+ } else {
+ Rw_S = Rw_S + (rating - Rw_M) * (rating - Rw_M);
+ Rw_M = Rw_M + (rating - Rw_M) / (Rw_count);
+ }
+ Ratings_W_StdDev = sqrt(Rw_S / Rw_count);
+ Ratings_W_Average = Rw_total / (double) Rw_count;
+ } else { /* TYPE_STAND */
+ sHist[which] += 1;
+ Rs_count++;
+ Rs_total += rating;
+ if (Rs_count == 1) {
+ Rs_M = rating;
+ } else {
+ Rs_S = Rs_S + (rating - Rs_M) * (rating - Rs_M);
+ Rs_M = Rs_M + (rating - Rs_M) / (Rs_count);
+ }
+ Ratings_S_StdDev = sqrt(Rs_S / Rs_count);
+ Ratings_S_Average = Rs_total / (double) Rs_count;
+ }
+}
+
+PUBLIC void rating_remove(int rating, int type)
+{
+ int which;
+
+ which = (rating - LOWESTHIST) / 100;
+ if (which < 0)
+ which = 0;
+ if (which >= MAXHIST)
+ which = MAXHIST - 1;
+ if (type == TYPE_BLITZ) {
+ bHist[which] = bHist[which] - 1;
+ if (bHist[which] < 0)
+ bHist[which] = 0;
+ if (Rb_count == 0)
+ return;
+ Rb_count--;
+ Rb_total -= rating;
+ if (Rb_count == 0) {
+ Rb_M = 0;
+ Rb_S = 0;
+ } else {
+ Rb_M = Rb_M - (rating - Rb_M) / (Rb_count);
+ Rb_S = Rb_S - (rating - Rb_M) * (rating - Rb_M);
+ /* added this 3.11.95 foxbat */ if (Rb_S < 0)
+ Rb_S = 0;
+ }
+ if (Rb_count) {
+ Ratings_B_StdDev = sqrt(Rb_S / Rb_count);
+ Ratings_B_Average = Rb_total / (double) Rb_count;
+ } else {
+ Ratings_B_StdDev = 0;
+ Ratings_B_Average = 0;
+ }
+ } else if (type == TYPE_WILD) { /* TYPE_WILD */
+ wHist[which] = wHist[which] - 1;
+ if (wHist[which] < 0)
+ wHist[which] = 0;
+ if (Rw_count == 0)
+ return;
+ Rw_count--;
+ Rw_total -= rating;
+ if (Rw_count == 0) {
+ Rw_M = 0;
+ Rw_S = 0;
+ } else {
+ Rw_M = Rw_M - (rating - Rw_M) / (Rw_count);
+ Rw_S = Rw_S - (rating - Rw_M) * (rating - Rw_M);
+ /* added this 3.10.95 foxbat */ if (Rw_S < 0)
+ Rw_S = 0;
+ }
+ if (Rw_count) {
+ Ratings_W_StdDev = sqrt(Rw_S / Rw_count);
+ Ratings_W_Average = Rw_total / (double) Rw_count;
+ } else {
+ Ratings_W_StdDev = 0;
+ Ratings_W_Average = 0;
+ }
+ } else { /* TYPE_STAND */
+ sHist[which] = sHist[which] - 1;
+ if (sHist[which] < 0)
+ sHist[which] = 0;
+ if (Rs_count == 0)
+ return;
+ Rs_count--;
+ Rs_total -= rating;
+ if (Rs_count == 0) {
+ Rs_M = 0;
+ Rs_S = 0;
+ } else {
+ Rs_M = Rs_M - (rating - Rs_M) / (Rs_count);
+ Rs_S = Rs_S - (rating - Rs_M) * (rating - Rs_M);
+ /* added this 3.10.95 foxbat */ if (Rs_S < 0)
+ Rs_S = 0;
+ }
+ if (Rs_count) {
+ Ratings_S_StdDev = sqrt(Rs_S / Rs_count);
+ Ratings_S_Average = Rs_total / (double) Rs_count;
+ } else {
+ Ratings_S_StdDev = 0;
+ Ratings_S_Average = 0;
+ }
+ }
+}
+
+PRIVATE void load_ratings(void)
+{
+ FILE *fp;
+ char fname[MAX_FILENAME_SIZE];
+ int i;
+
+ sprintf(fname, "%s/newratings_data", stats_dir);
+ fp = fopen(fname, "r");
+ if (!fp) {
+ fprintf(stderr, "FICS: Can't read ratings data!\n");
+ return;
+ }
+ fscanf(fp, "%lf %lf %lf %d", &Rb_M, &Rb_S, &Rb_total, &Rb_count);
+ fscanf(fp, "%lf %lf %lf %d", &Rs_M, &Rs_S, &Rs_total, &Rs_count);
+ fscanf(fp, "%lf %lf %lf %d", &Rw_M, &Rw_S, &Rw_total, &Rw_count);
+/* loon testing
+ fscanf(fp, "%d", &numB);
+ for (i = 0; i < numB; i++) {
+ fscanf(fp, "%s %d", bestB[i].name, &(bestB[i].rating));
+ }
+ fscanf(fp, "%d", &numS);
+ for (i = 0; i < numS; i++) {
+ fscanf(fp, "%s %d", bestS[i].name, &bestS[i].rating);
+ }
+ fscanf(fp, "%d", &numW);
+ for (i = 0; i < numW; i++) {
+ fscanf(fp, "%s %d", bestW[i].name, &bestW[i].rating);
+ }
+*/
+ for (i = 0; i < MAXHIST; i++) {
+ fscanf(fp, "%d %d %d", &sHist[i], &bHist[i], &wHist[i]);
+ }
+ fclose(fp);
+ if (Rs_count) {
+ Ratings_S_StdDev = sqrt(Rs_S / Rs_count);
+ Ratings_S_Average = Rs_total / (double) Rs_count;
+ } else {
+ Ratings_S_StdDev = 0;
+ Ratings_S_Average = 0;
+ }
+ if (Rb_count) {
+ Ratings_B_StdDev = sqrt(Rb_S / Rb_count);
+ Ratings_B_Average = Rb_total / (double) Rb_count;
+ } else {
+ Ratings_B_StdDev = 0;
+ Ratings_B_Average = 0;
+ }
+ if (Rw_count) {
+ Ratings_W_StdDev = sqrt(Rw_S / Rw_count);
+ Ratings_W_Average = Rw_total / (double) Rw_count;
+ } else {
+ Ratings_W_StdDev = 0;
+ Ratings_W_Average = 0;
+ }
+}
+
+PUBLIC void save_ratings(void)
+{
+ FILE *fp;
+ char fname[MAX_FILENAME_SIZE];
+ int i;
+
+ sprintf(fname, "%s/newratings_data", stats_dir);
+ fp = fopen(fname, "w");
+ if (!fp) {
+ fprintf(stderr, "FICS: Can't write ratings data!\n");
+ return;
+ }
+ fprintf(fp, "%10f %10f %10f %d\n", Rb_M, Rb_S, Rb_total, Rb_count);
+ fprintf(fp, "%10f %10f %10f %d\n", Rs_M, Rs_S, Rs_total, Rs_count);
+ fprintf(fp, "%10f %10f %10f %d\n", Rw_M, Rw_S, Rw_total, Rw_count);
+/* loon testing
+ fprintf(fp, "%d\n", numB);
+ for (i = 0; i < numB; i++) {
+ fprintf(fp, "%s %d\n", bestB[i].name, bestB[i].rating);
+ }
+ fprintf(fp, "%d\n", numS);
+ for (i = 0; i < numS; i++) {
+ fprintf(fp, "%s %d\n", bestS[i].name, bestS[i].rating);
+ }
+ fprintf(fp, "%d\n", numW);
+ for (i = 0; i < numW; i++) {
+ fprintf(fp, "%s %d\n", bestW[i].name, bestW[i].rating);
+ }
+*/
+ for (i = 0; i < MAXHIST; i++) {
+ fprintf(fp, "%d %d %d\n", sHist[i], bHist[i], wHist[i]);
+ }
+ fclose(fp);
+}
+
+/*
+PRIVATE void BestRemove(int p)
+{
+ int i;
+
+ for (i = 0; i < numB; i++) {
+ if (!strcmp(bestB[i].name, parray[p].name))
+ break;
+ }
+ if (i < numB) {
+ for (; i < numB - 1; i++) {
+ strcpy(bestB[i].name, bestB[i + 1].name);
+ bestB[i].rating = bestB[i + 1].rating;
+ }
+ numB--;
+ }
+ for (i = 0; i < numS; i++) {
+ if (!strcmp(bestS[i].name, parray[p].name))
+ break;
+ }
+ if (i < numS) {
+ for (; i < numS - 1; i++) {
+ strcpy(bestS[i].name, bestS[i + 1].name);
+ bestS[i].rating = bestS[i + 1].rating;
+ }
+ numS--;
+ }
+ for (i = 0; i < numW; i++) {
+ if (!strcmp(bestW[i].name, parray[p].name))
+ break;
+ }
+ if (i < numW) {
+ for (; i < numW - 1; i++) {
+ strcpy(bestW[i].name, bestW[i + 1].name);
+ bestW[i].rating = bestW[i + 1].rating;
+ }
+ numW--;
+ }
+}
+
+PRIVATE void BestAdd(int p)
+{
+ int where, j;
+
+ if ((parray[p].b_stats.rating > 0) && (parray[p].b_stats.num > 19)) {
+ for (where = 0; where < numB; where++) {
+ if (parray[p].b_stats.rating > bestB[where].rating)
+ break;
+ }
+ if (where < MAX_BEST) {
+ for (j = numB; j > where; j--) {
+ if (j == MAX_BEST)
+ continue;
+ strcpy(bestB[j].name, bestB[j - 1].name);
+ bestB[j].rating = bestB[j - 1].rating;
+ }
+ strcpy(bestB[where].name, parray[p].name);
+ bestB[where].rating = parray[p].b_stats.rating;
+ if (numB < MAX_BEST)
+ numB++;
+ }
+ }
+ if ((parray[p].s_stats.rating > 0) && (parray[p].s_stats.num > 19)) {
+ for (where = 0; where < numS; where++) {
+ if (parray[p].s_stats.rating > bestS[where].rating)
+ break;
+ }
+ if (where < MAX_BEST) {
+ for (j = numS; j > where; j--) {
+ if (j == MAX_BEST)
+ continue;
+ strcpy(bestS[j].name, bestS[j - 1].name);
+ bestS[j].rating = bestS[j - 1].rating;
+ }
+ strcpy(bestS[where].name, parray[p].name);
+ bestS[where].rating = parray[p].s_stats.rating;
+ if (numS < MAX_BEST)
+ numS++;
+ }
+ }
+ if ((parray[p].w_stats.rating > 0) && (parray[p].w_stats.num > 19)) {
+ for (where = 0; where < numW; where++) {
+ if (parray[p].w_stats.rating > bestW[where].rating)
+ break;
+ }
+ if (where < MAX_BEST) {
+ for (j = numW; j > where; j--) {
+ if (j == MAX_BEST)
+ continue;
+ strcpy(bestW[j].name, bestW[j - 1].name);
+ bestW[j].rating = bestW[j - 1].rating;
+ }
+ strcpy(bestW[where].name, parray[p].name);
+ bestW[where].rating = parray[p].w_stats.rating;
+ if (numW < MAX_BEST)
+ numW++;
+ }
+ }
+}
+
+PUBLIC void BestUpdate(int p)
+{
+ BestRemove(p);
+ BestAdd(p);
+}
+
+*/
+
+PUBLIC void zero_stats(void)
+{
+ int i;
+ for (i = 0; i < MAXHIST; i++) {
+ sHist[i] = 0;
+ bHist[i] = 0;
+ wHist[i] = 0;
+ }
+ Rb_M = 0.0, Rb_S = 0.0, Rb_total = 0.0;
+ Rb_count = 0;
+
+ Rs_M = 0.0, Rs_S = 0.0, Rs_total = 0.0;
+ Rs_count = 0;
+
+ Rw_M = 0.0, Rw_S = 0.0, Rw_total = 0.0;
+ Rw_count = 0;
+
+/*
+ numS = 0;
+ numB = 0;
+ numW = 0;
+*/
+}
+
+PUBLIC void rating_init(void)
+{
+ zero_stats();
+ load_ratings();
+}
+
+/* This recalculates the rating info from the player data, it can take
+ a long time! */
+PUBLIC void rating_recalc(void)
+{
+ char dname[MAX_FILENAME_SIZE];
+ int p1;
+ int c;
+ int t = time(0);
+ DIR *dirp;
+#if USE_DIRENT
+ struct dirent *dp;
+#else
+ struct direct *dp;
+#endif
+
+ fprintf(stderr, "FICS: Recalculating ratings at %s\n", strltime(&t));
+ zero_stats();
+ for (c = 'a'; c <= 'z'; c++) {
+ /* Never done as ratings server */
+ sprintf(dname, "%s/%c", player_dir, c);
+ dirp = opendir(dname);
+ if (!dirp)
+ continue;
+ for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
+ if (dp->d_name[0] != '.') {
+ p1 = player_new();
+ if (player_read(p1, dp->d_name)) {
+ player_remove(p1);
+ fprintf(stderr, "FICS: Problem reading player %s.\n", dp->d_name);
+ continue;
+ }
+ if (parray[p1].b_stats.rating > 0) {
+ rating_add(parray[p1].b_stats.rating, TYPE_BLITZ);
+ }
+ if (parray[p1].s_stats.rating > 0) {
+ rating_add(parray[p1].s_stats.rating, TYPE_STAND);
+ }
+ if (parray[p1].w_stats.rating > 0) {
+ rating_add(parray[p1].w_stats.rating, TYPE_WILD);
+ }
+ player_remove(p1);
+ }
+ }
+ closedir(dirp);
+ }
+ if (Rs_count) {
+ Ratings_S_StdDev = sqrt(Rs_S / Rs_count);
+ Ratings_S_Average = Rs_total / (double) Rs_count;
+ } else {
+ Ratings_S_StdDev = 0;
+ Ratings_S_Average = 0;
+ }
+ if (Rb_count) {
+ Ratings_B_StdDev = sqrt(Rb_S / Rb_count);
+ Ratings_B_Average = Rb_total / (double) Rb_count;
+ } else {
+ Ratings_B_StdDev = 0;
+ Ratings_B_Average = 0;
+ }
+ if (Rw_count) {
+ Ratings_W_StdDev = sqrt(Rw_S / Rw_count);
+ Ratings_W_Average = Rw_total / (double) Rw_count;
+ } else {
+ Ratings_W_StdDev = 0;
+ Ratings_W_Average = 0;
+ }
+ save_ratings();
+ t = time(0);
+ fprintf(stderr, "FICS: Finished at %s\n", strltime(&t));
+}
+
+/* Constants for Glicko system */
+#define Gd 3.25
+#define Gr0 1720
+#define Gs0 350
+#define Gq 0.00575646273249
+#define Gp 0.000010072398601964
+
+/* End of Glicko system variables */
+
+PRIVATE double Gf(double ss)
+{
+ return (1.0 / sqrt(1.0 + Gp * ss * ss));
+}
+
+/* Confusing but economical: calculate error and attenuation function together */
+PRIVATE double GE(int r, int rr, double ss, double *fss)
+{
+ *fss = Gf(ss);
+ return (1.0 / (1.0 + pow(10.0, (rr - r) * (*fss) / 400.0)));
+}
+
+PUBLIC double current_sterr(double s, int t)
+{
+ if (t < 0)
+ t = 0; /* this shouldn't happen */
+ return (sqrt(s * s + Gd * Gd * log(1.0 + t / 60.0)));
+}
+
+/* Calculates new rating and standard error. By vek. The person */
+/* who invented the ratings system is Mark E. Glickman, Ph.D. */
+/* His e-mail address is glickman@hustat.harvard.edu as of April '95. */
+/* Postscript copy of the note I coded this from should be available */
+/* for ftp from ics.onenet.net, if not elsewhere. */
+
+PUBLIC void rating_sterr_delta(int p1, int p2, int type, int gtime, int result,
+ int *deltarating, double *newsterr)
+{
+ statistics *p1_stats;
+ statistics *p2_stats;
+
+ double s1, s2;
+ int t1, r1, t2, r2; /* Initial sterrs and ratings */
+ double E, fs2, denominator, GK, w; /* Parts of fancy formulas */
+ double delta, sigma; /* Results to return */
+
+ if (type == TYPE_BLITZ) {
+ p1_stats = &parray[p1].b_stats;
+ p2_stats = &parray[p2].b_stats;
+ } else if (type == TYPE_WILD) {
+ p1_stats = &parray[p1].w_stats;
+ p2_stats = &parray[p2].w_stats;
+ } else {
+ p1_stats = &parray[p1].s_stats;
+ p2_stats = &parray[p2].s_stats;
+ }
+
+ /* Calculate effective pre-game sterrs. ltime==0 implies never had sterr. */
+
+ if (p1_stats->ltime == 0)
+ s1 = Gs0;
+ else {
+ t1 = gtime - p1_stats->ltime;
+ s1 = current_sterr(p1_stats->sterr, t1);
+ if (s1 > Gs0)
+ s1 = Gs0;
+ }
+
+ if (p2_stats->ltime == 0)
+ s2 = Gs0;
+ else {
+ t2 = gtime - p2_stats->ltime;
+ s2 = current_sterr(p2_stats->sterr, t2);
+ if (s2 > Gs0)
+ s2 = Gs0;
+ }
+
+ /* pre-game ratings */
+ if (p1_stats->rating == 0 && p1_stats->num == 0)
+ r1 = Gr0;
+ else
+ r1 = p1_stats->rating;
+
+ if (p2_stats->rating == 0 && p2_stats->num == 0)
+ r2 = Gr0;
+ else
+ r2 = p2_stats->rating;
+
+ /* now crunch */
+
+ if (result == RESULT_WIN) {
+ w = 1.0;
+ } else if (result == RESULT_DRAW) {
+ w = 0.5;
+ } else {
+ w = 0.0;
+ }
+
+ E = GE(r1, r2, s2, &fs2); /* side effect: calculate fs2 */
+
+ denominator = 1.0 / (s1 * s1) + Gq * Gq * fs2 * fs2 * E * (1.0 - E);
+ GK = Gq * fs2 / denominator;
+
+ delta = GK * (w - E);
+ if (p1_stats->rating == 0 && p1_stats->num == 0)
+ *deltarating = (int) (Gr0 + delta + 0.5);
+ else
+ *deltarating = (int) delta + 0.5; /* Returned values: deltarating,
+ newsterr */
+
+ sigma = 1.0 / sqrt(denominator);
+ *newsterr = (double) sigma;
+
+}
+
+/* vek: Next is for when we want just the delta, and not the sigma. */
+
+PUBLIC int rating_delta(int p1, int p2, int type, int result, int gtime)
+{
+ int delta;
+ double sigma;
+
+ rating_sterr_delta(p1, p2, type, gtime, result, &delta, &sigma);
+ return (delta);
+
+}
+
+PUBLIC int rating_update(int g)
+{
+ int wDelta, bDelta;
+ double wSigma, bSigma; /* vek */
+
+ int wRes, bRes;
+ statistics *w_stats;
+ statistics *b_stats;
+
+ int gtime;
+
+ int inprogress = (g == parray[garray[g].black].game);
+ /* if this is adjudication of stored game, be quiet about ratings change */
+
+ if (garray[g].type == TYPE_BLITZ) {
+ w_stats = &parray[garray[g].white].b_stats;
+ b_stats = &parray[garray[g].black].b_stats;
+ } else if (garray[g].type == TYPE_STAND) {
+ w_stats = &parray[garray[g].white].s_stats;
+ b_stats = &parray[garray[g].black].s_stats;
+ } else if (garray[g].type == TYPE_WILD) {
+ w_stats = &parray[garray[g].white].w_stats;
+ b_stats = &parray[garray[g].black].w_stats;
+ } else {
+ fprintf(stderr, "FICS: Can't update untimed ratings!\n");
+ return -1;
+ }
+
+ switch (garray[g].result) {
+ case END_CHECKMATE:
+ case END_RESIGN:
+ case END_FLAG:
+ case END_ADJWIN:
+ if (garray[g].winner == WHITE) {
+ wRes = RESULT_WIN;
+ bRes = RESULT_LOSS;
+ } else {
+ bRes = RESULT_WIN;
+ wRes = RESULT_LOSS;
+ }
+ break;
+ case END_AGREEDDRAW:
+ case END_REPETITION:
+ case END_50MOVERULE:
+ case END_STALEMATE:
+ case END_NOMATERIAL:
+ case END_BOTHFLAG:
+ case END_ADJDRAW:
+ case END_FLAGNOMATERIAL:
+ wRes = bRes = RESULT_DRAW;
+ break;
+ default:
+ fprintf(stderr, "FICS: Update undecided game %d?\n", garray[g].result);
+ return -1;
+ }
+ gtime = untenths(garray[g].timeOfStart);
+ rating_sterr_delta(garray[g].white, garray[g].black,
+ garray[g].type, gtime, wRes,
+ &wDelta, &wSigma);
+
+ rating_sterr_delta(garray[g].black, garray[g].white,
+ garray[g].type, gtime, bRes,
+ &bDelta, &bSigma);
+
+ /* vek: Update time of last rated game played, for future ratings calcs. */
+ /* Kept independently for blitz and standard. */
+ w_stats->ltime = gtime;
+ b_stats->ltime = gtime;
+ /* end vek add 4/5/95 */
+
+ if (wRes == RESULT_WIN) {
+ w_stats->win++;
+ } else if (wRes == RESULT_LOSS) {
+ w_stats->los++;
+ } else {
+ w_stats->dra++;
+ }
+ w_stats->num++;
+ if (bRes == RESULT_WIN) {
+ b_stats->win++;
+ } else if (bRes == RESULT_LOSS) {
+ b_stats->los++;
+ } else {
+ b_stats->dra++;
+ }
+ b_stats->num++;
+ rating_remove(w_stats->rating, garray[g].type);
+ rating_remove(b_stats->rating, garray[g].type);
+
+ if (inprogress) {
+ pprintf(garray[g].white, "%s rating adjustment: %d ",
+ ((garray[g].type == TYPE_BLITZ) ? "Blitz" : ((garray[g].type == TYPE_WILD) ? "Wild" : "Standard")), w_stats->rating);
+ pprintf(garray[g].black, "%s rating adjustment: %d ",
+ ((garray[g].type == TYPE_BLITZ) ? "Blitz" : ((garray[g].type == TYPE_WILD) ? "Wild" : "Standard")), b_stats->rating);
+ }
+ if (wDelta < -1000) {
+ pprintf(garray[g].white, "not changed due to bug (way too small)! sorry!\n");
+ fprintf(stderr, "FICS: Got too small ratings bug for %s (w) vs. %s\n",
+ parray[garray[g].white].login, parray[garray[g].black].login);
+ } else if (wDelta > 3000) {
+ pprintf(garray[g].white, "not changed due to bug (way too big)! sorry!\n");
+ fprintf(stderr, "FICS: Got too big ratings bug for %s (w) vs. %s\n",
+ parray[garray[g].white].login, parray[garray[g].black].login);
+ } else {
+ w_stats->rating += wDelta;
+ w_stats->sterr = wSigma;
+ }
+
+ if (bDelta < -1000) {
+ pprintf(garray[g].black, "not changed due to bug (way too small)! sorry! ");
+ fprintf(stderr, "FICS: Got too small ratings bug for %s (b) vs. %s\n",
+ parray[garray[g].black].login, parray[garray[g].white].login);
+ } else if (bDelta > 3000) {
+ pprintf(garray[g].black, "not changed due to bug (way too big)! sorry! ");
+ fprintf(stderr, "FICS: Got too big ratings bug for %s (b) vs. %s\n",
+ parray[garray[g].black].login, parray[garray[g].white].login);
+ } else {
+ b_stats->rating += bDelta;
+ b_stats->sterr = bSigma;
+ } /* error messages down to vek */
+
+ rating_add(w_stats->rating, garray[g].type);
+ rating_add(b_stats->rating, garray[g].type);
+
+ if ((w_stats->rating > w_stats->best) && (is_active(w_stats->num))) {
+ w_stats->best = w_stats->rating;
+ w_stats->whenbest = time(NULL);
+ }
+ if ((b_stats->rating > b_stats->best) && (is_active(b_stats->num))) {
+ b_stats->best = b_stats->rating;
+ b_stats->whenbest = time(NULL);
+ }
+/* Ratings are now saved to disk after each game */
+ player_save(garray[g].white);
+ player_save(garray[g].black);
+
+/* foxbat 3.11.95 */
+ if (garray[g].type == TYPE_BLITZ) {
+ Rb_count++;
+ Rb_total += (w_stats->rating + b_stats->rating) / 2.0;
+ } else if (garray[g].type == TYPE_STAND) {
+ Rs_count++;
+ Rs_total += (w_stats->rating + b_stats->rating) / 2.0;
+ } else if (garray[g].type == TYPE_WILD) {
+ Rw_count++;
+ Rw_total += (w_stats->rating + b_stats->rating) / 2.0;
+ }
+/* end add */
+/* loon testing
+ BestUpdate(garray[g].white);
+ BestUpdate(garray[g].black);
+*/
+ if (inprogress) {
+ pprintf(garray[g].white, "--> %d\n", w_stats->rating);
+ pprintf(garray[g].black, "--> %d\n", b_stats->rating);
+ }
+ save_ratings();
+ UpdateRank(garray[g].type, parray[garray[g].white].name,
+ w_stats, parray[garray[g].white].name);
+ UpdateRank(garray[g].type, parray[garray[g].black].name,
+ b_stats, parray[garray[g].black].name);
+ return 0;
+}
+
+PUBLIC int com_assess(int p, param_list param)
+{
+ int p1 = p, p2, nowtime;
+ int p1_connected = 1, p2_connected = 1;
+ int win1, draw1, loss1;
+ double newsterr1;
+ int win2, draw2, loss2;
+ double newsterr2;
+
+ nowtime = time(0);
+
+/* Hawk: Now assess can be used with players not */
+/* logged on -- I wonder if anyone doesn't */
+/* get just a bit confused here :) */
+
+ if (param[0].type == TYPE_NULL) {
+ if (parray[p].game <0) {
+ pprintf(p, "You are not playing a game.\n");
+ return COM_OK;
+ } else if (garray[parray[p].game].status == GAME_EXAMINE) {
+ if (!strcmp(garray[parray[p].game].black_name, parray[p].name)) {
+ pcommand(p, "assess %s\n", garray[parray[p].game].white_name);
+ } else {
+ pcommand(p, "assess %s %s\n",
+ garray[parray[p].game].white_name,
+ garray[parray[p].game].black_name);
+ }
+ return COM_OK;
+ } else {
+ p2 = parray[p].opponent;
+ }
+ } else {
+ if (!FindPlayer(p, &param[0], &p2, &p2_connected)) {
+ pprintf(p, "No user named \"%s\" was found.\n", param[0].val.word);
+ return COM_OK;
+ }
+ if (param[1].type != TYPE_NULL) {
+ p1 = p2;
+ p1_connected = p2_connected;
+ if (!FindPlayer(p, &param[1], &p2, &p2_connected)) {
+ pprintf(p, "No user named \"%s\" was found.\n", param[1].val.word);
+ if (!p1_connected)
+ player_remove(p1);
+ return COM_OK;
+ }
+ }
+ }
+ if (p1 == p2) {
+ pprintf(p, "You can't assess the same players.\n");
+ if (!p1_connected)
+ player_remove(p1);
+ if (!p2_connected)
+ player_remove(p2);
+ return COM_OK;
+ }
+ rating_sterr_delta(p1, p2, TYPE_BLITZ, nowtime, RESULT_WIN, &win1, &newsterr1);
+ rating_sterr_delta(p1, p2, TYPE_BLITZ, nowtime, RESULT_DRAW, &draw1, &newsterr1);
+ rating_sterr_delta(p1, p2, TYPE_BLITZ, nowtime, RESULT_LOSS, &loss1, &newsterr1);
+ rating_sterr_delta(p2, p1, TYPE_BLITZ, nowtime, RESULT_WIN, &win2, &newsterr2);
+ rating_sterr_delta(p2, p1, TYPE_BLITZ, nowtime, RESULT_DRAW, &draw2, &newsterr2);
+ rating_sterr_delta(p2, p1, TYPE_BLITZ, nowtime, RESULT_LOSS, &loss2, &newsterr2);
+
+ pprintf(p, "\nBlitz\n %10s (%4s, RD: %5.1f) %10s (%4s, RD: %5.1f)\n",
+ parray[p1].name, ratstrii(parray[p1].b_stats.rating, parray[p1].registered), parray[p1].b_stats.sterr,
+ parray[p2].name, ratstrii(parray[p2].b_stats.rating, parray[p2].registered), parray[p2].b_stats.sterr);
+ pprintf(p, "Win : %4d %4d\n", win1, loss2);
+ pprintf(p, "Draw: %4d %4d\n", draw1, draw2);
+ pprintf(p, "Loss: %4d %4d\n", loss1, win2);
+ pprintf(p, "New RD: %5.1f %5.1f\n", newsterr1, newsterr2);
+
+ rating_sterr_delta(p1, p2, TYPE_STAND, nowtime, RESULT_WIN, &win1, &newsterr1);
+ rating_sterr_delta(p1, p2, TYPE_STAND, nowtime, RESULT_DRAW, &draw1, &newsterr1);
+ rating_sterr_delta(p1, p2, TYPE_STAND, nowtime, RESULT_LOSS, &loss1, &newsterr1);
+ rating_sterr_delta(p2, p1, TYPE_STAND, nowtime, RESULT_WIN, &win2, &newsterr2);
+ rating_sterr_delta(p2, p1, TYPE_STAND, nowtime, RESULT_DRAW, &draw2, &newsterr2);
+ rating_sterr_delta(p2, p1, TYPE_STAND, nowtime, RESULT_LOSS, &loss2, &newsterr2);
+
+ pprintf(p, "\nStandard\n %10s (%4s, RD: %5.1f) %10s (%4s, RD: %5.1f)\n",
+ parray[p1].name, ratstrii(parray[p1].s_stats.rating, parray[p1].registered), parray[p1].s_stats.sterr,
+ parray[p2].name, ratstrii(parray[p2].s_stats.rating, parray[p2].registered), parray[p2].s_stats.sterr);
+ pprintf(p, "Win : %4d %4d\n", win1, loss2);
+ pprintf(p, "Draw: %4d %4d\n", draw1, draw2);
+ pprintf(p, "Loss: %4d %4d\n", loss1, win2);
+ pprintf(p, "New RD: %5.1f %5.1f\n", newsterr1, newsterr2);
+
+ rating_sterr_delta(p1, p2, TYPE_WILD, nowtime, RESULT_WIN, &win1, &newsterr1);
+ rating_sterr_delta(p1, p2, TYPE_WILD, nowtime, RESULT_DRAW, &draw1, &newsterr1);
+ rating_sterr_delta(p1, p2, TYPE_WILD, nowtime, RESULT_LOSS, &loss1, &newsterr1);
+ rating_sterr_delta(p2, p1, TYPE_WILD, nowtime, RESULT_WIN, &win2, &newsterr2);
+ rating_sterr_delta(p2, p1, TYPE_WILD, nowtime, RESULT_DRAW, &draw2, &newsterr2);
+ rating_sterr_delta(p2, p1, TYPE_WILD, nowtime, RESULT_LOSS, &loss2, &newsterr2);
+
+ pprintf(p, "\nWild\n %10s (%4s, RD: %5.1f) %10s (%4s, RD: %5.1f)\n",
+ parray[p1].name, ratstrii(parray[p1].w_stats.rating, parray[p1].registered), parray[p1].w_stats.sterr,
+ parray[p2].name, ratstrii(parray[p2].w_stats.rating, parray[p2].registered), parray[p2].w_stats.sterr);
+ pprintf(p, "Win : %4d %4d\n", win1, loss2);
+ pprintf(p, "Draw: %4d %4d\n", draw1, draw2);
+ pprintf(p, "Loss: %4d %4d\n", loss1, win2);
+ pprintf(p, "New RD: %5.1f %5.1f\n", newsterr1, newsterr2);
+
+ if (!p1_connected)
+ player_remove(p1);
+ if (!p2_connected)
+ player_remove(p2);
+ return COM_OK;
+}
+
+
+PUBLIC int com_best(int p, param_list param)
+{
+ return Best(p, param, 1);
+}
+
+PUBLIC int com_hbest(int p, param_list param)
+{
+ return Best(p, param, 0);
+}
+
+#if 0
+PUBLIC int com_best(int p, param_list param)
+{
+ int i;
+
+ pprintf(p, "Standard Blitz Wild\n");
+ for (i = 0; i < MAX_BEST; i++) {
+ if ((i >= numS) && (i >= numB))
+ break;
+ if (i < numS) {
+ pprintf(p, "%4d %-17s ", bestS[i].rating, bestS[i].name);
+ } else {
+ pprintf(p, " ");
+ }
+ if (i < numB) {
+ pprintf(p, "%4d %-17s ", bestB[i].rating, bestB[i].name);
+ } else {
+ pprintf(p, " ");
+ }
+ if (i < numW) {
+ pprintf(p, "%4d %-17s\n", bestW[i].rating, bestW[i].name);
+ } else {
+ pprintf(p, "\n");
+ }
+ }
+ return COM_OK;
+}
+#endif
+
+PUBLIC int com_statistics(int p, param_list param)
+{
+ pprintf(p, " Standard Blitz Wild\n");
+ pprintf(p, "average: %7.2f %7.2f %7.2f\n", Ratings_S_Average, Ratings_B_Average, Ratings_W_Average);
+ pprintf(p, "std dev: %7.2f %7.2f %7.2f\n", Ratings_S_StdDev, Ratings_B_StdDev, Ratings_W_StdDev);
+ pprintf(p, "number : %7d %7d %7d\n", Rs_count, Rb_count, Rw_count);
+ return COM_OK;
+}
+
+PUBLIC int com_fixrank(int p, param_list param)
+{
+ int p1, connected;
+
+ if (!FindPlayer(p, &param[0], &p1, &connected))
+ return COM_OK;
+ UpdateRank(TYPE_BLITZ, parray[p1].name, &parray[p1].b_stats,
+ parray[p1].name);
+ UpdateRank(TYPE_STAND, parray[p1].name, &parray[p1].s_stats,
+ parray[p1].name);
+ UpdateRank(TYPE_WILD, parray[p1].name, &parray[p1].w_stats,
+ parray[p1].name);
+ if (!connected)
+ player_remove(p1);
+ return COM_OK;
+}
+
+PUBLIC int com_rank(int p, param_list param)
+{
+ return DisplayRank(p, param, 1);
+}
+
+PUBLIC int com_hrank(int p, param_list param)
+{
+ return DisplayRank(p, param, 0);
+}
+
+PUBLIC int DisplayRank(int p, param_list param, int showComputers)
+{
+ int start, end, target, connected;
+ int show = SHOW_BLITZ | SHOW_STANDARD | SHOW_WILD;
+
+ if (param[0].type == TYPE_NULL) {
+ DisplayTargetRank(p, parray[p].name, show, showComputers);
+ return COM_OK;
+ } else if (isdigit(param[0].val.word[0])) {
+ end = -1;
+ sscanf(param[0].val.word, "%d-%d", &start, &end);
+ if (end > 0 && (param[1].type != TYPE_NULL))
+ show = ShowFromString(param[1].val.word);
+ DisplayRankedPlayers(p, start, end, show, showComputers);
+ return COM_OK;
+ } else {
+ target = player_search(p, param[0].val.word);
+ if (target == 0) {
+ pprintf(p, "Target %s not found.\n", param[0].val.word);
+ return COM_OK;
+ }
+ connected = (target > 0);
+ if (!connected)
+ target = -target - 1;
+ else
+ target--;
+
+ if (param[1].type != TYPE_NULL)
+ show = ShowFromString(param[1].val.word);
+ DisplayTargetRank(p, parray[target].name, show, showComputers);
+ if (!connected)
+ player_remove(target);
+ return COM_OK;
+ }
+}
+
+/* CompareStats returns 1 if s1 comes first, -1 if s2 comes first, and 0
+ if neither takes precedence. */
+PRIVATE int CompareStats(char *name1, statistics *s1,
+ char *name2, statistics *s2)
+{
+ int i, l1;
+
+ if (s1 == NULL)
+ if (s2 == NULL)
+ return 0;
+ else
+ return -1;
+ else if (s2 == NULL)
+ return 1;
+
+ if (s1->rating > s2->rating)
+ return 1;
+ if (s1->rating < s2->rating)
+ return -1;
+ l1 = strlen(name1);
+ for (i = 0; i < l1; i++) {
+ if (name2[i] == '\0')
+ return -1;
+ if (tolower(name1[i]) < tolower(name2[i]))
+ return 1;
+ if (tolower(name1[i]) > tolower(name2[i]))
+ return -1;
+ }
+ if (name2[i] != '\0')
+ return 1;
+/* if (s1->sterr < s2->sterr) return 1;
+ if (s1->sterr > s2->sterr) return -1;
+ if (s1->num > s2->num) return 1;
+ if (s1->num < s2->num) return -1;
+*/
+ fprintf(stderr, "Duplicate entries found: %s.\n", name1);
+ return 0;
+}
+
+PRIVATE int GetRankFileName(char *out, int type)
+{
+ switch (type) {
+ case TYPE_BLITZ:sprintf(out, "%s/rank.blitz", sdir);
+ return type;
+ case TYPE_STAND:
+ sprintf(out, "%s/rank.std", sdir);
+ return type;
+ case TYPE_WILD:
+ sprintf(out, "%s/rank.wild", sdir);
+ return type;
+ default:
+ return -1;
+ }
+}
+
+PUBLIC void UpdateRank(int type, char *addName,
+ statistics *sNew, char *delName)
+{
+ char RankFile[MAX_FILENAME_SIZE];
+ char TmpRankFile[MAX_FILENAME_SIZE];
+ char line[MAX_RANK_LINE];
+ char login[MAX_LOGIN_NAME];
+ char command[MAX_STRING_LENGTH];
+ int comp;
+ statistics sCur;
+ FILE *fp;
+ FILE *fptemp;
+
+ if (GetRankFileName(RankFile, type) < 0)
+ return;
+ fp = fopen(RankFile, "r");
+ if (fp == NULL) {
+ fprintf(stderr, "Can't open rank file to update.\n");
+ return;
+ }
+ sprintf(TmpRankFile, "%s/tmpRank", sdir);
+ fptemp = fopen(TmpRankFile, "w");
+
+ while (fgets(line, MAX_RANK_LINE - 1, fp)) {
+ sscanf(line, "%s %d %d %d", login, &sCur.rating,
+ &sCur.num, &comp);
+ if (delName != NULL && !strcasecmp(delName, login)) { /* Kill name. */
+ delName = NULL;
+ continue;
+ }
+ if (addName != NULL && CompareStats(addName, sNew, login, &sCur) > 0) {
+ int computer = in_list("computer", addName);
+ fprintf(fptemp, "%s %d %d %d\n", addName, sNew->rating,
+ sNew->num, computer);
+ addName = NULL;
+ }
+ fprintf(fptemp, "%s %d %d %d\n", login, sCur.rating, sCur.num, comp);
+ }
+ fclose(fptemp);
+ fclose(fp);
+
+ sprintf(command, "mv %s %s", TmpRankFile, RankFile);
+ system(command);
+}
+
+PRIVATE void DisplayRankHead(int p, int show)
+{
+ char Line[MAX_STRING_LENGTH];
+
+ Line[0] = '\0';
+ if (CheckFlag(show, SHOW_BLITZ))
+ strcat(Line, " Blitz ");
+ if (CheckFlag(show, SHOW_STANDARD))
+ strcat(Line, " Standard ");
+ if (CheckFlag(show, SHOW_WILD))
+ strcat(Line, " Wild");
+ pprintf(p, "%s\n\n", Line);
+}
+
+PRIVATE int CountRankLine(int countComp, char *loginName,
+ int num, int is_computer)
+{
+ if (loginName == NULL || loginName[0] == '\0')
+ return 0;
+ return (countComp || !is_computer) && (is_active(num));
+}
+
+PRIVATE int GetRank(FILE * fp, char *target, int countComp)
+{
+ int count = 0;
+ int nGames, is_computer;
+ int playerFound = 0;
+ char line[MAX_RANK_LINE];
+ char login[MAX_LOGIN_NAME];
+
+ while (fgets(line, MAX_RANK_LINE - 1, fp) && !playerFound) {
+ sscanf(line, "%s %*d %d %d", login, &nGames, &is_computer);
+ if ((playerFound = !strcasecmp(login, target))
+ || CountRankLine(countComp, login, nGames, is_computer))
+ count++;
+ }
+ return (playerFound ? count : -1);
+}
+
+PRIVATE void PositionFilePtr(FILE * fp, int count, int *last,
+ int *nTied, int showComp)
+{
+ int i, rating, nGames, is_computer;
+ char login[MAX_LOGIN_NAME];
+ char line[MAX_RANK_LINE];
+
+ if (fp == NULL)
+ return;
+ rewind(fp);
+ for (i = 1; i < count; i++) {
+ do {
+ fgets(line, MAX_RANK_LINE - 1, fp);
+ if (feof(fp))
+ break;
+ sscanf(line, "%s %d %d %d", login, &rating, &nGames, &is_computer);
+ } while (!CountRankLine(showComp, login, nGames, is_computer));
+ if (rating != *last) {
+ *nTied = 1;
+ *last = rating;
+ } else
+ (*nTied)++;
+ }
+}
+
+PRIVATE int ShowRankEntry(int p, FILE * fp, int count, int comp,
+ char *target, int *lastRating, int *nTied)
+{
+ char newLine[MAX_RANK_LINE];
+ char login[MAX_LOGIN_NAME];
+ int rating, findable, nGames, is_comp;
+
+ findable = (count > 0) && !feof(fp);
+ if (findable) {
+ do {
+ fgets(newLine, MAX_RANK_LINE - 1, fp);
+ if (feof(fp))
+ findable = 0;
+ else if (newLine[0] != '\0')
+ sscanf(newLine, "%s %d %d %d",
+ login, &rating, &nGames, &is_comp);
+ else
+ login[0] = '\0';
+ } while (!CountRankLine(comp, login, nGames, is_comp) && findable
+ && strcasecmp(login, target));
+ }
+ if (findable) {
+ if (!strcasecmp(login, target)
+ && !CountRankLine(comp, login, nGames, is_comp)) {
+ pprintf_highlight(p, "---- %-12.12s %4s", login, ratstr(rating));
+ pprintf(p, " ");
+ return 0;
+ } else if (*lastRating == rating && *nTied < 1) {
+ pprintf(p, " ");
+ if (!strcasecmp(login, target))
+ pprintf_highlight(p, "%-12.12s %4s", login, ratstr(rating));
+ else
+ pprintf(p, "%-12.12s %4s", login, ratstr(rating));
+ pprintf(p, " ");
+ return 1;
+ } else {
+ if (*nTied >= 1) {
+ if (*lastRating == rating)
+ count -= *nTied;
+ *nTied = -1;
+ }
+ if (!strcasecmp(login, target))
+ pprintf_highlight(p, "%4d. %-12.12s %4s",
+ count, login, ratstr(rating));
+ else
+ pprintf(p, "%4d. %-12.12s %4s",
+ count, login, ratstr(rating));
+ pprintf(p, " ");
+ *lastRating = rating;
+ return 1;
+ }
+ } else {
+ pprintf(p, "%25s", "");
+ return 1;
+ }
+}
+
+PRIVATE int CountAbove(int num, int blitz, int std, int wild, int which)
+{
+ int max = blitz;
+
+ if (max < std)
+ max = std;
+ if (max < wild)
+ max = wild;
+ return (max <= (num + 1) / 2 ? max - 1 : (num + 1) / 2);
+}
+
+PRIVATE int ShowRankLines(int p, FILE * fb, FILE * fs, FILE * fw, int bCount,
+ int sCount, int wCount, int n, int showComp, int show, char *target)
+{
+ int lastBlitz = 9999, nTiedBlitz = 0;
+ int lastStd = 9999, nTiedStd = 0;
+ int lastWild = 9999, nTiedWild = 0;
+ int i;
+
+ if (n <= 0)
+ return 0;
+ if (CheckFlag(show, SHOW_BLITZ)) {
+ PositionFilePtr(fb, bCount, &lastBlitz, &nTiedBlitz, showComp);
+ if (feof(fb))
+ ClearFlag(show, SHOW_BLITZ);
+ }
+ if (CheckFlag(show, SHOW_STANDARD)) {
+ PositionFilePtr(fs, sCount, &lastStd, &nTiedStd, showComp);
+ if (feof(fs))
+ ClearFlag(show, SHOW_STANDARD);
+ }
+ if (CheckFlag(show, SHOW_WILD)) {
+ PositionFilePtr(fw, wCount, &lastWild, &nTiedWild, showComp);
+ if (feof(fw))
+ ClearFlag(show, SHOW_WILD);
+ }
+ if (!CheckFlag(show, SHOW_BLITZ | SHOW_STANDARD | SHOW_WILD))
+ return 0;
+ DisplayRankHead(p, show);
+
+ for (i = 0; i < n && show; i++) {
+ if (CheckFlag(show, SHOW_BLITZ))
+ bCount += ShowRankEntry(p, fb, bCount, showComp, target,
+ &lastBlitz, &nTiedBlitz);
+ if (CheckFlag(show, SHOW_STANDARD))
+ sCount += ShowRankEntry(p, fs, sCount, showComp, target,
+ &lastStd, &nTiedStd);
+ if (CheckFlag(show, SHOW_WILD))
+ wCount += ShowRankEntry(p, fw, wCount, showComp, target,
+ &lastWild, &nTiedWild);
+ pprintf(p, "\n");
+ }
+ return 1;
+}
+
+PUBLIC int DisplayTargetRank(int p, char *target, int show, int showComp)
+{
+ int numToShow = 20;
+ int blitzRank = -1, blitzCount;
+ int stdRank = -1, stdCount;
+ int wildRank = -1, wildCount;
+ int numAbove;
+ char Path[MAX_FILENAME_SIZE];
+ FILE *fb = NULL, *fs = NULL, *fw = NULL;
+
+ if (CheckFlag(show, SHOW_BLITZ)) {
+ GetRankFileName(Path, TYPE_BLITZ);
+ fb = (FILE *) fopen(Path, "r");
+ if (fb != NULL)
+ blitzRank = GetRank(fb, target, showComp);
+ if (blitzRank < 0)
+ ClearFlag(show, SHOW_BLITZ);
+ }
+ if (CheckFlag(show, SHOW_STANDARD)) {
+ GetRankFileName(Path, TYPE_STAND);
+ fs = (FILE *) fopen(Path, "r");
+ if (fs != NULL)
+ stdRank = GetRank(fs, target, showComp);
+ if (stdRank < 0)
+ ClearFlag(show, SHOW_STANDARD);
+ }
+ if (CheckFlag(show, SHOW_WILD)) {
+ GetRankFileName(Path, TYPE_WILD);
+ if (CheckFlag(show, SHOW_WILD))
+ fw = (FILE *) fopen(Path, "r");
+ if (fw != NULL)
+ wildRank = GetRank(fw, target, showComp);
+ if (wildRank < 0)
+ ClearFlag(show, SHOW_WILD);
+ }
+ if (!CheckFlag(show, SHOW_BLITZ | SHOW_STANDARD | SHOW_WILD)) {
+ pprintf(p, "No ratings to show.\n");
+ return (0);
+ }
+ numAbove = CountAbove(numToShow, blitzRank, stdRank, wildRank, show);
+ blitzCount = blitzRank - numAbove;
+ stdCount = stdRank - numAbove;
+ wildCount = wildRank - numAbove;
+
+ ShowRankLines(p, fb, fs, fw, blitzCount, stdCount, wildCount,
+ numToShow, showComp, show, target);
+ if (fb != NULL)
+ fclose(fb);
+ if (fs != NULL)
+ fclose(fs);
+ if (fw != NULL)
+ fclose(fw);
+ return (1);
+}
+
+PUBLIC int DisplayRankedPlayers(int p, int start, int end,
+ int show, int showComp)
+{
+ int num = end - start + 1;
+ FILE *fb = NULL, *fs = NULL, *fw = NULL;
+ char Path[MAX_FILENAME_SIZE];
+
+ if (start <= 0)
+ start = 1;
+ if (num <= 0)
+ return 0;
+ if (num > 100)
+ num = 100;
+ if (CheckFlag(show, SHOW_BLITZ)) {
+ GetRankFileName(Path, TYPE_BLITZ);
+ fb = (FILE *) fopen(Path, "r");
+ if (fb == NULL)
+ ClearFlag(show, SHOW_BLITZ);
+ }
+ if (CheckFlag(show, SHOW_STANDARD)) {
+ GetRankFileName(Path, TYPE_STAND);
+ fs = (FILE *) fopen(Path, "r");
+ if (fs == NULL)
+ ClearFlag(show, SHOW_STANDARD);
+ }
+ if (CheckFlag(show, SHOW_WILD)) {
+ GetRankFileName(Path, TYPE_WILD);
+ fw = (FILE *) fopen(Path, "r");
+ if (fw == NULL)
+ ClearFlag(show, SHOW_WILD);
+ }
+ ShowRankLines(p, fb, fs, fw, start, start, start,
+ num, showComp, show, "");
+ if (fb)
+ fclose(fb);
+ if (fs)
+ fclose(fs);
+ if (fw)
+ fclose(fw);
+ return 1;
+}
+
+PUBLIC int ShowFromString(char *s)
+{
+ int i, len = strlen(s);
+ int show = 0;
+
+ if (s == NULL || s[0] == '\0')
+ return SHOW_BLITZ | SHOW_STANDARD | SHOW_WILD;
+ for (i = 0; i < len; i++) {
+ switch (s[i]) {
+ case 'b':
+ SetFlag(show, SHOW_BLITZ);
+ break;
+ case 's':
+ SetFlag(show, SHOW_STANDARD);
+ break;
+ case 'w':
+ SetFlag(show, SHOW_WILD);
+ break;
+ }
+ }
+ return (show);
+}
+
+PUBLIC int Best(int p, param_list param, int ShowComp)
+{
+ int show = SHOW_BLITZ | SHOW_STANDARD | SHOW_WILD;
+
+ if (param[0].type != TYPE_NULL)
+ show = ShowFromString(param[0].val.word);
+
+ DisplayRankedPlayers(p, 1, 20, show, ShowComp);
+ return COM_OK;
+}
diff --git a/FICS/ratings.h b/FICS/ratings.h
new file mode 100644
index 0000000..a9823da
--- /dev/null
+++ b/FICS/ratings.h
@@ -0,0 +1,80 @@
+/* ratings.h
+ *
+ */
+
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name email yy/mm/dd Change
+ Richard Nash 93/10/22 Created
+*/
+
+#ifndef _RATINGS_H
+#define _RATINGS_H
+
+#define STATS_VERSION 2
+
+#define RESULT_WIN 0
+#define RESULT_DRAW 1
+#define RESULT_LOSS 2
+#define RESULT_ABORT 3
+
+#define PROVISIONAL 20
+
+#define MAX_RANK_LINE 50
+#define MAX_BEST 20
+
+#define SHOW_BLITZ 0x1
+#define SHOW_STANDARD 0x2
+#define SHOW_WILD 0x4
+
+#include "command.h"
+
+typedef struct _rateStruct {
+ char name[MAX_LOGIN_NAME];
+ int rating;
+} rateStruct;
+
+extern int is_active(int);
+extern void rating_init(void);
+extern void rating_recalc(void);
+extern void rating_sterr_delta(int, int, int, int, int, int *, double *);
+extern int rating_delta(int, int, int, int, int);
+extern int rating_update(int);
+
+extern int com_assess(int, param_list);
+extern int com_best(int, param_list);
+extern int com_hbest(int, param_list);
+extern int com_statistics(int, param_list);
+extern int com_fixrank(int, param_list);
+extern int com_rank(int, param_list);
+extern int com_hrank(int, param_list);
+
+extern void save_ratings(void);
+/*extern void BestUpdate(int); */
+extern void rating_remove(int, int);
+extern void rating_add(int, int);
+
+extern int DisplayRank(int, param_list, int);
+extern void UpdateRank(int, char *, statistics *, char *);
+extern int DisplayTargetRank(int, char *, int, int);
+extern int DisplayRankedPlayers(int, int, int, int, int);
+extern int ShowFromString(char *);
+/* extern time_t time(); */
+extern int Best(int, param_list, int);
+
+#endif /* _RATINGS_H */
+
diff --git a/FICS/rmalloc.c b/FICS/rmalloc.c
new file mode 100644
index 0000000..9eb0dbc
--- /dev/null
+++ b/FICS/rmalloc.c
@@ -0,0 +1,87 @@
+/* rmalloc.c
+ *
+ */
+
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name email yy/mm/dd Change
+ Richard Nash 93/10/22 Created
+*/
+
+
+#include "stdinclude.h"
+
+#include "common.h"
+
+PUBLIC unsigned int allocated_size = 0;
+PUBLIC unsigned int malloc_count = 0;
+PUBLIC unsigned int free_count = 0;
+
+PUBLIC void *rmalloc(int byteSize)
+{
+ void *newptr;
+#ifdef HASMALLOCSIZE
+ allocated_size += byteSize;
+#endif
+ malloc_count++;
+ newptr = malloc(byteSize);
+ if (newptr == NULL) {
+ fprintf(stderr, "Out of memory in malloc!\n");
+ abort();
+ }
+ return newptr;
+}
+
+PUBLIC void *rrealloc(void *ptr, int byteSize)
+{
+#ifdef HASMALLOCSIZE
+ allocated_size += (byteSize - malloc_size(ptr));
+#endif
+ if (ptr == NULL) { /* Sparky 3/16/95 */
+ fprintf(stderr, "Hoser! Null ptr passed to rrealloc!!\n");
+ return NULL;
+ } else {
+ void *newptr = realloc(ptr, byteSize);
+ if (newptr == NULL) {
+ fprintf(stderr, "Out of memory in rrealloc!\n");
+ abort();
+ }
+ return newptr;
+ }
+}
+
+PUBLIC void rfree(void *ptr)
+{
+#ifdef HASMALLOCSIZE
+ allocated_size = allocated_size - malloc_size(ptr);
+#endif
+ if (ptr == NULL) { /* Sparky 3/16/95 */
+ fprintf(stderr, "Hoser! Null ptr passed to rfree!!\n");
+ } else {
+ free_count++;
+ free(ptr);
+ }
+}
+
+PUBLIC void strfree (char *string)
+{
+ /* This routine is often called on strings that are either
+ a rmalloc'd value or NULL. So it's not an error to see
+ a NULL here. --mann
+ */
+ if (string != NULL) rfree (string);
+}
diff --git a/FICS/rmalloc.h b/FICS/rmalloc.h
new file mode 100644
index 0000000..cde1d06
--- /dev/null
+++ b/FICS/rmalloc.h
@@ -0,0 +1,38 @@
+/* rmalloc.h
+ *
+ */
+
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name email yy/mm/dd Change
+ Richard Nash 93/10/22 Created
+*/
+
+#ifndef _RMALLOC_H
+#define _RMALLOC_H
+
+extern unsigned int allocated_size;
+extern unsigned int malloc_count;
+extern unsigned int free_count;
+
+extern void *rmalloc(int);
+extern void *rrealloc(void *, int);
+extern void rfree(void *);
+extern void strfree(char *);
+
+#endif /* _RMALLOC_H */
+
diff --git a/FICS/shutdown.c b/FICS/shutdown.c
new file mode 100644
index 0000000..4f6f91c
--- /dev/null
+++ b/FICS/shutdown.c
@@ -0,0 +1,262 @@
+/* shutdown.c
+
+ Contains stuff related to shutdowns
+
+ */
+ /* 12/9/95 - added reason to shutdown - DAV */
+
+#include "stdinclude.h"
+#include "common.h"
+#include "shutdown.h"
+#include "command.h"
+#include "ficsmain.h"
+#include "network.h"
+#include "playerdb.h"
+#include "utils.h"
+
+PRIVATE int shutdownTime = 0;
+PRIVATE int lastTimeLeft;
+PRIVATE int shutdownStartTime;
+PRIVATE char downer[1024];
+PRIVATE char reason[1024];
+
+PUBLIC void output_shut_mess()
+{
+ int shuttime = time(0);
+ fprintf(stderr, "FICS: Shutting down at %s\n", strltime(&shuttime));
+}
+
+PUBLIC void ShutDown(void)
+{
+ int p1;
+ int shuttime = time(0);
+
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (parray[p1].status != PLAYER_PROMPT)
+ continue;
+ pprintf(p1, "\n\n **** Server shutdown ordered by %s. ****\n", downer);
+ if (reason[0] != '\0')
+ pprintf(p1, "\n **** We are going down because: %s. ****\n", reason);
+ }
+ TerminateCleanup();
+ fprintf(stderr, "FICS: Shut down ordered at %s by %s.\n", strltime(&shuttime), downer);
+ output_shut_mess();
+ net_close();
+ exit(0);
+}
+
+PUBLIC void ShutHeartBeat(void)
+{
+ int t = time(0);
+ int p1;
+ int timeLeft;
+ int crossing = 0;
+
+ if (!shutdownTime)
+ return;
+ if (!lastTimeLeft)
+ lastTimeLeft = shutdownTime;
+ timeLeft = shutdownTime - (t - shutdownStartTime);
+ if ((lastTimeLeft > 3600) && (timeLeft <= 3600))
+ crossing = 1;
+ if ((lastTimeLeft > 2400) && (timeLeft <= 2400))
+ crossing = 1;
+ if ((lastTimeLeft > 1200) && (timeLeft <= 1200))
+ crossing = 1;
+ if ((lastTimeLeft > 600) && (timeLeft <= 600))
+ crossing = 1;
+ if ((lastTimeLeft > 300) && (timeLeft <= 300))
+ crossing = 1;
+ if ((lastTimeLeft > 120) && (timeLeft <= 120))
+ crossing = 1;
+ if ((lastTimeLeft > 60) && (timeLeft <= 60))
+ crossing = 1;
+ if ((lastTimeLeft > 10) && (timeLeft <= 10))
+ crossing = 1;
+ if (crossing) {
+ fprintf(stderr,
+ "FICS: **** Server going down in %d minutes and %d seconds. ****\n\n",
+ timeLeft / 60,
+ timeLeft - ((timeLeft / 60) * 60));
+ if (reason[0] != '\0')
+ fprintf(stderr,"FICS: We are going down because: %s.\n",reason);
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (parray[p1].status != PLAYER_PROMPT)
+ continue;
+ pprintf(p1,
+ "\n\n **** Server going down in %d minutes and %d seconds. ****\n",
+ timeLeft / 60,
+ timeLeft - ((timeLeft / 60) * 60));
+ if (reason[0] != '\0')
+ pprintf_prompt(p1,"\n **** We are going down because: %s. ****\n",reason);
+ else
+ pprintf_prompt(p1,"\n");
+ }
+ }
+ lastTimeLeft = timeLeft;
+ if (timeLeft <= 0) {
+ ShutDown();
+ }
+}
+
+PUBLIC int check_and_print_shutdown(int p)
+
+ /* Tells a user if they is to be a shutdown */
+ /* returns 0 if there is not to be one, 1 if there is to be one */
+ /* for whenshut command */
+
+{
+
+
+ int timeLeft;
+ int t = time(0);
+
+ if (!shutdownTime)
+ return 0; /* no shut down */
+
+ timeLeft = shutdownTime - (t - shutdownStartTime);
+
+ pprintf(p,
+ "\n **** Server going down in %d minutes and %d seconds. ****\n",
+ timeLeft / 60,
+ timeLeft - ((timeLeft / 60) * 60));
+ if (reason[0] != '\0')
+ pprintf(p, "\n **** We are going down because: %s. ****\n", reason);
+ return 1;
+}
+
+
+/*
+ * shutdown
+ *
+ * Usage: shutdown [now,cancel,time]
+ *
+ * This command shutsdown the server. If the parameter is omitted or
+ * is 'now' then the server is immediately halted cleanly. If a time is
+ * given then a countdown commences and the server is halted when time is
+ * up. If 'cancel' is given then the countdown is stopped.
+ */
+PUBLIC int com_shutdown(int p, param_list param)
+{
+ char *ptr;
+ int p1, secs;
+
+ ASSERT(parray[p].adminLevel >= ADMIN_ADMIN);
+ strcpy(downer, parray[p].name);
+ shutdownStartTime = time(0);
+ if (shutdownTime) { /* Cancel any pending shutdowns */
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (parray[p1].status != PLAYER_PROMPT)
+ continue;
+ pprintf(p1, "\n\n **** Server shutdown canceled by %s. ****\n", downer);
+ }
+ shutdownTime = 0;
+ if (param[0].type == TYPE_NULL)
+ return COM_OK;
+ }
+ /* Work out how soon to shut down */
+ if (param[0].type == TYPE_NULL)
+ shutdownTime = 300;
+ else {
+ if (!strcmp(param[0].val.word, "now"))
+ shutdownTime = 0;
+ else if (!strcmp(param[0].val.word, "die")) {
+ fprintf (stderr,"%s salutes FICS and presses the self-destruct button.\n",
+ parray[p].name);
+ output_shut_mess();
+ abort();
+ } else if (!strcmp(param[0].val.word, "cancel"))
+ return COM_OK;
+ else {
+ ptr = param[0].val.word;
+ shutdownTime = secs = 0;
+ p1 = 2;
+ while (*ptr) {
+ switch (*ptr) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ secs = secs * 10 + *ptr - '0';
+ break;
+ case ':':
+ if (p1--) {
+ shutdownTime = shutdownTime * 60 + secs;
+ secs = 0;
+ break;
+ }
+ default:
+ shutdownTime = 0;
+ pprintf(p, "I don't know what you mean by %s\n", param[0].val.word);
+ return COM_OK;
+ }
+ ptr++;
+ }
+ shutdownTime = shutdownTime * 60 + secs;
+ }
+ }
+ if (shutdownTime <= 0)
+ ShutDown();
+ if (param[1].type == TYPE_STRING)
+ strcpy (reason,param[1].val.string);
+ else
+ reason[0] = '\0'; /* No reason - perhaps admin is in a bad mood? :) */
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (parray[p1].status != PLAYER_PROMPT)
+ continue;
+ pprintf(p1, "\n\n **** Server shutdown ordered by %s. ****\n", downer);
+ if (reason[0] != '\0')
+ pprintf(p1, " **** We are going down because: %s. ****\n",reason);
+ pprintf(p1,
+ " **** Server going down in %d minutes and %d seconds. ****\n",
+ shutdownTime / 60, shutdownTime % 60);
+ if (p != p1) /* fix double prompt - DAV */
+ pprintf_prompt (p1,"\n");
+ else
+ pprintf (p1,"\n");
+ }
+ lastTimeLeft = 0;
+ return COM_OK;
+}
+
+PUBLIC int server_shutdown(int secs, char *why)
+{
+ int p1;
+
+ if (shutdownTime && (shutdownTime <= secs)) {
+ /* Server is already shutting down, I'll let it go */
+ return 0;
+ }
+ strcpy(downer, "Automatic");
+ shutdownTime = secs;
+ shutdownStartTime = time(0);
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (parray[p1].status != PLAYER_PROMPT)
+ continue;
+ pprintf(p1, "\n\n **** Automatic Server shutdown. ****\n");
+ pprintf(p1, "%s\n", why);
+ pprintf_prompt(p1,
+ " **** Server going down in %d minutes and %d seconds. ****\n\n",
+ shutdownTime / 60,
+ shutdownTime - ((shutdownTime / 60) * 60));
+ }
+ fprintf(stderr, "FICS: **** Automatic Server shutdown. ****\n");
+ fprintf(stderr, "FICS: %s\n", why);
+ return 0;
+}
+
+PUBLIC int com_whenshut(int p, param_list param)
+
+{
+ int result = check_and_print_shutdown(p);
+ if (result == 0) {
+ pprintf (p,"No shutdown currently in progress\n");
+ }
+ return COM_OK;
+}
diff --git a/FICS/shutdown.h b/FICS/shutdown.h
new file mode 100644
index 0000000..5bbf77b
--- /dev/null
+++ b/FICS/shutdown.h
@@ -0,0 +1,14 @@
+/* shutdown.h */
+
+#ifndef _SHUTDOWN_H
+#define _SHUTDOWN_H
+
+extern void output_shut_mess();
+extern int com_shutdown();
+extern void ShutHeartBeat();
+extern void ShutDown();
+extern int server_shutdown();
+extern int check_and_print_shutdown(int);
+extern int com_whenshut();
+
+#endif /* _SHUTDOWN_H */
diff --git a/FICS/stdinclude.h b/FICS/stdinclude.h
new file mode 100644
index 0000000..295774e
--- /dev/null
+++ b/FICS/stdinclude.h
@@ -0,0 +1,174 @@
+/* stdinclude.h
+ *
+ */
+
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name email yy/mm/dd Change
+ Richard Nash 93/10/22 Created
+*/
+
+#ifndef _STDINCLUDE_H
+#define _STDINCLUDE_H
+
+#include <sys/types.h>
+
+/* Set up system specific defines */
+#if defined(SYSTEM_NEXT)
+
+#define HASMALLOCSIZE
+#include <sys/vfs.h>
+
+#elif defined(SYSTEM_ULTRIX)
+
+#include <sys/param.h>
+#include <sys/mount.h>
+
+#endif
+
+#ifdef SYSTEM_USL
+# define NO_TM_ZONE
+#endif
+
+#ifdef SYSTEM_SUN4
+# define USE_VARARGS
+#endif
+
+#ifdef SGI
+#define _BSD_SIGNALS
+#include <fcntl.h>
+#include <bstring.h>
+#endif
+
+#if defined(SYSTEM_RS6K)
+#include <sys/select.h>
+#include <dirent.h>
+#define USE_DIRENT
+#endif
+
+/* These are included into every .c file */
+#if defined(SYSTEM_SUN5)
+#define USE_RLIMIT
+#define USE_TIMES
+#define USE_WAITPID
+#define GOOD_STDIO
+#define NO_TM_ZONE
+#include <string.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/filio.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#define direct dirent
+
+#else
+#include <strings.h>
+#include <sys/dir.h>
+#endif
+
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <sys/errno.h>
+#include <signal.h>
+#include <time.h>
+#include <math.h>
+#include <sys/time.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+
+/* Forward declare the functions that aren't defined above */
+
+#if defined(SGI)
+#include <errno.h>
+#define GOOD_STDIO
+#define NO_TM_ZONE
+#else
+#define GOOD_STDIO
+/*
+extern FILE *popen(char *, char *);
+extern int pclose(FILE *);
+extern int fputs(char *, FILE *);
+extern int fseek(FILE *, long, int);
+extern int fread(char *, int, int, FILE *);
+extern int fwrite(char *, int, int, FILE *);
+extern int socket(int, int, int);
+extern int atoi (char *nptr);
+extern long atol (char *nptr);
+extern int truncate(char *, off_t), ftruncate(int, off_t);
+*/
+#endif
+
+#ifndef GOOD_STDIO
+extern int fclose(FILE *);
+extern int fscanf(FILE *, char *, ...);
+extern int fprintf(FILE *, char *, ...);
+extern int printf(char *, ...);
+#endif
+
+
+#ifndef NETBSD
+extern char *crypt (char *key, char *salt);
+#endif
+
+extern time_t time(time_t *);
+extern int rand(void);
+extern int close(int);
+extern size_t malloc_size(void *ptr);
+
+#ifdef __STDC__
+/* extern fcntl(int fildes, int cmd, ...); */
+/* extern open(char *path, int oflag, ...); */
+/* extern int ioctl(int, long, ...); */
+
+#else
+ extern fcntl();
+ extern open();
+ extern int ioctl();
+
+#endif /* __STDC__ */
+/* extern void *malloc(unsigned int size); */
+/* extern void *calloc(unsigned int num, unsigned int size); */
+/* extern void *realloc(void *addr, unsigned int size); */
+/* extern void free(void *data); */
+/* extern void malloc_good_size(unsigned int size); */
+/* extern int link(); */
+/* extern int unlink(); */
+/* extern int rename(); */
+/* extern int getpid(); */
+/* extern int kill(); */
+/* extern int fork(); */
+/* extern int access(); */
+/* extern int getdtablesize(); */
+/* extern int write(); */
+#if !defined(SYSTEM_ULTRIX)
+/* extern int sleep(); */
+/* extern int getuid(); */
+extern int statfs();
+#endif
+
+#endif /* _STDINCLUDE_H */
+
diff --git a/FICS/stdinclude.h.orig b/FICS/stdinclude.h.orig
new file mode 100644
index 0000000..394b007
--- /dev/null
+++ b/FICS/stdinclude.h.orig
@@ -0,0 +1,164 @@
+/* stdinclude.h
+ *
+ */
+
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name email yy/mm/dd Change
+ Richard Nash 93/10/22 Created
+*/
+#include <sys/types.h>
+
+/* Set up system specific defines */
+#if defined(SYSTEM_NEXT)
+
+#define HASMALLOCSIZE
+#include <sys/vfs.h>
+
+#elif defined(SYSTEM_ULTRIX)
+
+#include <sys/param.h>
+#include <sys/mount.h>
+
+#endif
+
+#ifdef SYSTEM_USL
+# define NO_TM_ZONE
+#endif
+
+#ifdef SYSTEM_SUN4
+# define USE_VARARGS
+#endif
+
+#ifdef SGI
+#define _BSD_SIGNALS
+#include <fcntl.h>
+#include <bstring.h>
+#endif
+
+#if defined(SYSTEM_RS6K)
+#include <sys/select.h>
+#include <dirent.h>
+#define USE_DIRENT
+#endif
+
+/* These are included into every .c file */
+#if defined(SYSTEM_SUN5)
+#define USE_RLIMIT
+#define USE_TIMES
+#define USE_WAITPID
+#define GOOD_STDIO
+#define NO_TM_ZONE
+#include <string.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/filio.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#define direct dirent
+
+#else
+#include <strings.h>
+#include <sys/dir.h>
+#endif
+
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <sys/errno.h>
+#include <signal.h>
+#include <time.h>
+#include <math.h>
+#include <sys/time.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+
+/* Forward declare the functions that aren't defined above */
+
+#if defined(SGI)
+#include <errno.h>
+#define GOOD_STDIO
+#define NO_TM_ZONE
+#else
+#define GOOD_STDIO
+/*
+extern FILE *popen(char *, char *);
+extern int pclose(FILE *);
+extern int fputs(char *, FILE *);
+extern int fseek(FILE *, long, int);
+extern int fread(char *, int, int, FILE *);
+extern int fwrite(char *, int, int, FILE *);
+extern int socket(int, int, int);
+extern int atoi (char *nptr);
+extern long atol (char *nptr);
+extern int truncate(char *, off_t), ftruncate(int, off_t);
+*/
+#endif
+
+#ifndef GOOD_STDIO
+extern int fclose(FILE *);
+extern int fscanf(FILE *, char *, ...);
+extern int fprintf(FILE *, char *, ...);
+extern int printf(char *, ...);
+#endif
+
+extern time_t time(time_t *);
+extern int rand(void);
+extern int close(int);
+extern char *crypt (char *key, char *salt);
+extern size_t malloc_size(void *ptr);
+
+#ifdef __STDC__
+/* extern fcntl(int fildes, int cmd, ...); */
+/* extern open(char *path, int oflag, ...); */
+/* extern int ioctl(int, long, ...); */
+
+#else
+ extern fcntl();
+ extern open();
+ extern int ioctl();
+
+#endif /* __STDC__ */
+/* extern void *malloc(unsigned int size); */
+/* extern void *calloc(unsigned int num, unsigned int size); */
+/* extern void *realloc(void *addr, unsigned int size); */
+/* extern void free(void *data); */
+/* extern void malloc_good_size(unsigned int size); */
+/* extern int link(); */
+/* extern int unlink(); */
+/* extern int rename(); */
+/* extern int getpid(); */
+/* extern int kill(); */
+/* extern int fork(); */
+/* extern int access(); */
+/* extern int getdtablesize(); */
+/* extern int write(); */
+#if !defined(SYSTEM_ULTRIX)
+/* extern int sleep(); */
+/* extern int getuid(); */
+extern int statfs();
+#endif
+
diff --git a/FICS/talkproc.c b/FICS/talkproc.c
new file mode 100644
index 0000000..cfd35b6
--- /dev/null
+++ b/FICS/talkproc.c
@@ -0,0 +1,1247 @@
+/* talkproc.c
+ *
+ */
+
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name yy/mm/dd Change
+ hersco and Marsalis 95/07/24 Created
+ Sparky 95/10/05
+ Modifed tell function for the following
+ changes: no longer informs you that someone
+ is censoring in tells to channels, whisper or
+ kibitz. Titles now shown instead of ratings
+ in kibitz and whisper, admins on duty now are
+ shown as (*), and computers marked as (C) as
+ well as with rating.
+
+*/
+
+#include "stdinclude.h"
+
+#include "common.h"
+#include "talkproc.h"
+#include "comproc.h"
+#include "command.h"
+#include "utils.h"
+#include "ficsmain.h"
+#include "config.h"
+#include "playerdb.h"
+#include "network.h"
+#include "rmalloc.h"
+#include "variable.h"
+#include "gamedb.h"
+#include "gameproc.h"
+#include "obsproc.h"
+/* #include "hostinfo.h" */
+#include "multicol.h"
+#include "lists.h"
+#include "formula.h"
+#include <string.h>
+
+#include <sys/resource.h>
+
+/* grimm */
+#if defined(SGI)
+#else
+/* int system(char *arg); */
+#endif
+
+int quota_time;
+
+#if 0
+PUBLIC int com_query(int p, param_list param)
+{
+ int p1;
+ int count = 0;
+
+ if (!parray[p].registered) {
+ pprintf(p, "Only registered players can use the query command.\n");
+ return COM_OK;
+ }
+ if (in_list(p, L_MUZZLE, parray[p].login)) {
+ pprintf(p, "You are muzzled.\n");
+ return COM_OK;
+ }
+ if (!printablestring(param[0].val.string)) {
+ pprintf(p, "Your message contains some unprintable character(s).\n");
+ return COM_OK;
+ }
+ if (!parray[p].query_log) {
+ parray[p].query_log = tl_new(5);
+ } else {
+ if (tl_numinlast(parray[p].query_log, 60 * 60) >= 2) {
+ pprintf(p, "Your can only query twice per hour.\n");
+ return COM_OK;
+ }
+ }
+ in_push(IN_SHOUT);
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (p1 == p)
+ continue;
+ if (parray[p1].status != PLAYER_PROMPT)
+ continue;
+ if (player_censored(p1, p))
+ continue;
+ count++;
+ if (parray[p1].highlight) {
+ pprintf_prompt(p1, "\n\033[7m%s queries:\033[0m %s\n", parray[p].name,
+ param[0].val.string);
+ } else {
+ pprintf_prompt(p1, "\n%s queries: %s\n", parray[p].name,
+ param[0].val.string);
+ }
+ }
+ pprintf(p, "Query heard by %d player(s).\n", count);
+ tl_logevent(parray[p].query_log, 1);
+ in_pop();
+ return COM_OK;
+}
+#endif
+
+/* hawk: hacked it to fit ALL persons - quota list is not needed anymore */
+int CheckShoutQuota(int p)
+{
+ int timenow = time(0);
+ int timeleft = 0;
+
+ if (((timeleft = timenow - parray[p].lastshout_a) < quota_time) &&
+ (parray[p].adminLevel == 0)) {
+ return (quota_time - timeleft);
+ } else {
+ return 0;
+ }
+}
+
+PUBLIC int com_shout(int p, param_list param)
+{
+ int p1;
+ int count = 0;
+ int timeleft; /* time left for quota if applicable */
+
+ if (!parray[p].registered) {
+ pprintf(p, "Only registered players can use the shout command.\n");
+ return COM_OK;
+ }
+ if (in_list(p, L_MUZZLE, parray[p].login)) {
+ pprintf(p, "You are muzzled.\n");
+ return COM_OK;
+ }
+ if (param[0].type == TYPE_NULL) {
+ if ((timeleft = CheckShoutQuota(p))) {
+ pprintf(p, "Next shout available in %d seconds.\n", timeleft);
+ } else {
+ pprintf(p, "Your next shout is ready for use.\n");
+ }
+ return COM_OK;
+ }
+ if ((timeleft = CheckShoutQuota(p))) {
+ pprintf(p, "Shout not sent. Next shout in %d seconds.\n", timeleft);
+ return COM_OK;
+ }
+ parray[p].lastshout_a = parray[p].lastshout_b;
+ parray[p].lastshout_b = time(0);
+ if (!printablestring(param[0].val.string)) {
+ pprintf(p, "Your message contains some unprintable character(s).\n");
+ return COM_OK;
+ }
+/* in_push(IN_SHOUT); */
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (p1 == p)
+ continue;
+ if (parray[p1].status != PLAYER_PROMPT)
+ continue;
+ if (!parray[p1].i_shout)
+ continue;
+ if (player_censored(p1, p))
+ continue;
+ count++;
+ pprintf_prompt(p1, "\n%s shouts: %s\n", parray[p].name,
+ param[0].val.string);
+ }
+ pprintf(p, "(%d) %s shouts: %s\n", count, parray[p].name,
+ param[0].val.string);
+/* in_pop(); */
+ if ((timeleft = CheckShoutQuota(p))) {
+ pprintf(p, "Next shout in %d seconds.\n", timeleft);
+ return COM_OK;
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_cshout(int p, param_list param)
+{
+ int p1;
+ int count = 0;
+
+ if (!parray[p].registered) {
+ pprintf(p, "Only registered players can use the cshout command.\n");
+ return COM_OK;
+ }
+ if (in_list(p, L_CMUZZLE, parray[p].login)) {
+ pprintf(p, "You are c-muzzled.\n");
+ return COM_OK;
+ }
+ if (!printablestring(param[0].val.string)) {
+ pprintf(p, "Your message contains some unprintable character(s).\n");
+ return COM_OK;
+ }
+/* in_push(IN_SHOUT); */
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (p1 == p)
+ continue;
+ if (parray[p1].status != PLAYER_PROMPT)
+ continue;
+ if (!parray[p1].i_cshout)
+ continue;
+ if (player_censored(p1, p))
+ continue;
+ count++;
+ pprintf_prompt(p1, "\n%s c-shouts: %s\n", parray[p].name,
+ param[0].val.string);
+ }
+ pprintf(p, "(%d) %s c-shouts: %s\n", count, parray[p].name,
+ param[0].val.string);
+/* in_pop(); */
+ return COM_OK;
+}
+
+PUBLIC int com_it(int p, param_list param)
+{
+ int p1;
+ int count = 0;
+ int timeleft;
+
+ if (!parray[p].registered) {
+ pprintf(p, "Only registered players can use the it command.\n");
+ return COM_OK;
+ }
+ if (in_list(p, L_MUZZLE, parray[p].login)) {
+ pprintf(p, "You are muzzled.\n");
+ return COM_OK;
+ }
+ if (param[0].type == TYPE_NULL) {
+ if ((timeleft = CheckShoutQuota(p))) {
+ pprintf(p, "Next shout available in %d seconds.\n", timeleft);
+ } else {
+ pprintf(p, "Your next shout is ready for use.\n");
+ }
+ return COM_OK;
+ }
+ if ((timeleft = CheckShoutQuota(p))) {
+ pprintf(p, "Shout not sent. Next shout in %d seconds.\n", timeleft);
+ return COM_OK;
+ }
+ parray[p].lastshout_a = parray[p].lastshout_b;
+ parray[p].lastshout_b = time(0);
+
+ if (!printablestring(param[0].val.string)) {
+ pprintf(p, "Your message contains some unprintable character(s).\n");
+ return COM_OK;
+ }
+/* in_push(IN_SHOUT); */
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (p1 == p)
+ continue;
+ if (parray[p1].status != PLAYER_PROMPT)
+ continue;
+ if (!parray[p1].i_shout)
+ continue;
+ if (player_censored(p1, p))
+ continue;
+ count++;
+ if ((!strncmp(param[0].val.string, "\'", 1)) ||
+ (!strncmp(param[0].val.string, ",", 1)) ||
+ (!strncmp(param[0].val.string, ".", 1))) {
+ pprintf_prompt(p1, "\n--> %s%s\n", parray[p].name,
+ param[0].val.string);
+ } else {
+ pprintf_prompt(p1, "\n--> %s %s\n", parray[p].name,
+ param[0].val.string);
+ }
+ }
+ if ((!strncmp(param[0].val.string, "\'", 1)) ||
+ (!strncmp(param[0].val.string, ",", 1)) ||
+ (!strncmp(param[0].val.string, ".", 1))) {
+ pprintf(p, "(%d) --> %s%s\n", count, parray[p].name, param[0].val.string);
+ } else {
+ pprintf(p, "(%d) --> %s %s\n", count, parray[p].name, param[0].val.string);
+ }
+/* in_pop(); */
+ if ((timeleft = CheckShoutQuota(p))) {
+ pprintf(p, "Next shout in %d seconds.\n", timeleft);
+ return COM_OK;
+ }
+ return COM_OK;
+}
+
+#define TELL_TELL 0
+#define TELL_SAY 1
+#define TELL_WHISPER 2
+#define TELL_KIBITZ 3
+#define TELL_CHANNEL 4
+PRIVATE int tell(int p, int p1, char *msg, int why, int ch)
+{
+ char tmp[MAX_LINE_SIZE];
+ int rating;
+ int rating1;
+
+ if (!printablestring(msg)) {
+ pprintf(p, "Your message contains some unprintable character(s).\n");
+ return COM_OK;
+ }
+/* if (p1 == p) {
+ * pprintf(p, "Quit talking to yourself! It's embarrassing.\n");
+ * return COM_OK;
+ * }
+ */
+ if ((!parray[p1].i_tell) && (!parray[p].registered)) {
+ pprintf(p, "Player \"%s\" isn't listening to unregistered tells.\n",
+ parray[p1].name);
+ return COM_OK;
+ }
+ if ((player_censored(p1, p)) && (parray[p].adminLevel == 0)) {
+ if ((why != TELL_KIBITZ) || (why != TELL_WHISPER) || (why != TELL_CHANNEL))
+ {
+ pprintf(p, "Player \"%s\" is censoring you.\n", parray[p1].name);
+ }
+ return COM_OK;
+ }
+/* in_push(IN_TELL); */
+ switch (why) {
+ case TELL_SAY:
+ pprintf_highlight(p1, "\n%s", parray[p].name);
+ pprintf_prompt(p1, " says: %s\n", msg);
+ break;
+ case TELL_WHISPER:
+ case TELL_KIBITZ:
+ rating = GetRating(&parray[p], TYPE_BLITZ);
+ if (rating < ( rating1 = ( GetRating(&parray[p], TYPE_STAND ) ) ) )
+ rating = rating1;
+ if (in_list(p, L_FM, parray[p].name))
+ pprintf(p1, "\n%s(FM)", parray[p].name);
+ else if (in_list(p, L_IM, parray[p].name))
+ pprintf(p1, "\n%s(IM)", parray[p].name);
+ else if (in_list(p, L_GM, parray[p].name))
+ pprintf(p1, "\n%s(GM)", parray[p].name);
+ else if ((parray[p].adminLevel >= 10) && (parray[p].i_admin))
+ pprintf(p1, "\n%s(*)", parray[p].name);
+ else if ((rating >= parray[p1].kiblevel) ||
+ ((parray[p].adminLevel >= 10) && (parray[p].i_admin)))
+ if (!parray[p].registered)
+ pprintf(p1, "\n%s(++++)", parray[p].name);
+
+ else if (rating != 0)
+ if (in_list(p, L_COMPUTER, parray[p].name))
+ pprintf(p1, "\n%s(%d)(C)", parray[p].name, rating);
+ else
+ pprintf(p1, "\n%s(%d)", parray[p].name, rating);
+ else
+ pprintf(p1, "\n%s(----)", parray[p].name, rating);
+ else break;
+
+ if (why == TELL_WHISPER)
+ pprintf_prompt(p1, " whispers: %s\n", msg);
+ else
+ pprintf_prompt(p1, " kibitzes: %s\n", msg);
+
+ break;
+ case TELL_CHANNEL:
+ pprintf(p1, "\n%s", parray[p].name);
+ pprintf_prompt(p1, "(%d): %s\n", ch, msg);
+ break;
+ case TELL_TELL:
+ default:
+ if (parray[p1].highlight) {
+ pprintf_highlight(p1, "\n%s", parray[p].name);
+ } else {
+ pprintf(p1, "\n%s", parray[p].name);
+ }
+ pprintf_prompt(p1, " tells you: %s\n", msg);
+ break;
+ }
+ tmp[0] = '\0';
+ if (!(parray[p1].busy[0] == '\0')) {
+ sprintf(tmp, ", who %s (idle: %s)", parray[p1].busy,
+ hms_desc(player_idle(p1)));
+ } else {
+ if (((player_idle(p1) % 3600) / 60) > 2) {
+ sprintf(tmp, ", who has been idle %s", hms_desc(player_idle(p1)));
+ }
+ /* else sprintf(tmp," "); */
+ }
+ if ((why == TELL_SAY) || (why == TELL_TELL)) {
+ pprintf(p, "(told %s%s)\n", parray[p1].name,
+ (((parray[p1].game>=0) && (garray[parray[p1].game].status == GAME_EXAMINE))
+ ? ", who is examining a game" :
+ (parray[p1].game >= 0 && (parray[p1].game != parray[p].game))
+ ? ", who is playing" : tmp));
+ parray[p].last_tell = p1;
+ }
+/* in_pop(); */
+ return COM_OK;
+}
+
+PUBLIC int com_ptell(int p, param_list param) /*tells partner - doesn't change last tell */
+
+{
+ char tmp[MAX_LINE_SIZE];
+ int p1;
+
+ if (parray[p].partner < 0) {
+ pprintf (p, "You do not have a partner at present.\n");
+ return COM_OK;
+ }
+
+ p1 = parray[p].partner;
+ if ((p1 < 0) || (parray[p1].status == PLAYER_PASSWORD)
+ || (parray[p1].status == PLAYER_LOGIN)) {
+ pprintf(p, "Your partner is not logged in.\n");
+ return COM_OK;
+ }
+ if (parray[p1].highlight) {
+ pprintf_highlight(p1, "\n%s", parray[p].name);
+ } else {
+ pprintf(p1, "\n%s", parray[p].name);
+ }
+ pprintf_prompt(p1, " (your partner) tells you: %s\n", param[0].val.string);
+ tmp[0] = '\0';
+ if (!(parray[p1].busy[0] == '\0')) {
+ sprintf(tmp, ", who %s (idle: %s)", parray[p1].busy,
+ hms_desc(player_idle(p1)));
+ } else {
+ if (((player_idle(p1) % 3600) / 60) > 2) {
+ sprintf(tmp, ", who has been idle %s", hms_desc(player_idle(p1)));
+ }
+ }
+ /* else sprintf(tmp," "); */
+ pprintf(p, "(told %s%s)\n", parray[p1].name,
+ (((parray[p1].game>=0) && (garray[parray[p1].game].status == GAME_EXAMINE))
+ ? ", who is examining a game" :
+ (parray[p1].game >= 0 && (parray[p1].game != parray[p].game))
+ ? ", who is playing" : tmp));
+ return COM_OK;
+}
+
+PRIVATE int chtell(int p, int ch, char *msg)
+{
+ int p1, count = 0;
+
+ if ((ch == 0) && (parray[p].adminLevel == 0)) {
+ pprintf(p, "Only admins may send tells to channel 0.\n");
+ return COM_OK;
+ }
+
+ if (ch < 0) {
+ pprintf(p, "The lowest channel number is 0.\n");
+ return COM_OK;
+ }
+
+ if (ch >= MAX_CHANNELS) {
+ pprintf(p, "The maximum channel number is %d.\n", MAX_CHANNELS - 1);
+ return COM_OK;
+ }
+
+ for (p1 = 0; p1 < p_num; p1++) {
+ if ((p1 == p) || (parray[p1].status != PLAYER_PROMPT))
+ continue;
+ if ((on_channel(ch, p1)) && (!player_censored(p1, p))) {
+ tell(p, p1, msg, TELL_CHANNEL, ch);
+ count++;
+ }
+ }
+
+ if (count)
+ parray[p].last_channel = ch;
+
+ pprintf(p, "(%d->(%d))\n", ch, count);
+ if (!on_channel(ch, p))
+ pprintf(p, " (You're not listening to channel %d.)\n", ch);
+
+ return COM_OK;
+}
+
+PUBLIC int com_whisper(int p, param_list param)
+{
+ int g;
+ int p1;
+ int count = 0;
+
+ if (!parray[p].num_observe && parray[p].game < 0) {
+ pprintf(p, "You are not playing or observing a game.\n");
+ return COM_OK;
+ }
+ if (!parray[p].registered && (parray[p].game == -1)) {
+ pprintf(p, "You must be registered to whisper other people's games.\n");
+ return COM_OK;
+ }
+ if (parray[p].game >= 0)
+ g = parray[p].game;
+ else
+ g = parray[p].observe_list[0];
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (p1 == p)
+ continue;
+ if (parray[p1].status != PLAYER_PROMPT)
+ continue;
+ if (player_is_observe(p1, g) ||
+ (garray[g].link >= 0 && player_is_observe(p1, garray[g].link))) {
+ tell(p, p1, param[0].val.string, TELL_WHISPER, 0);
+ if ((parray[p].adminLevel >= ADMIN_ADMIN) || !garray[g].private)
+ count++;
+ }
+ }
+ pprintf(p, "whispered to %d.\n", count);
+ return COM_OK;
+}
+
+PUBLIC int com_kibitz(int p, param_list param)
+{
+ int g;
+ int p1;
+ int count = 0;
+
+ if (!parray[p].num_observe && parray[p].game < 0) {
+ pprintf(p, "You are not playing or observing a game.\n");
+ return COM_OK;
+ }
+ if (!parray[p].registered && (parray[p].game == -1)) {
+ pprintf(p, "You must be registered to kibitz other people's games.\n");
+ return COM_OK;
+ }
+ if (parray[p].game >= 0)
+ g = parray[p].game;
+ else
+ g = parray[p].observe_list[0];
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (p1 == p)
+ continue;
+ if (parray[p1].status != PLAYER_PROMPT)
+ continue;
+ if ((player_is_observe(p1, g) || parray[p1].game == g ||
+ (garray[g].link >= 0 &&
+ (player_is_observe(p1, garray[g].link) || parray[p1].game == garray[g].link)
+ )
+ ) && parray[p1].i_kibitz) {
+ tell(p, p1, param[0].val.string, TELL_KIBITZ, 0);
+ if ((parray[p].adminLevel >= ADMIN_ADMIN) || !garray[g].private || (parray[p1].game == g))
+ count++;
+ }
+ }
+ pprintf(p, "kibitzed to %d.\n", count);
+ return COM_OK;
+}
+
+PUBLIC int com_tell(int p, param_list param)
+{
+ int p1;
+
+ if (param[0].type == TYPE_NULL)
+ return COM_BADPARAMETERS;
+ if (param[0].type == TYPE_WORD) {
+ stolower(param[0].val.word);
+ if (!strcmp(param[0].val.word, ".")) {
+ if (parray[p].last_tell < 0) {
+ pprintf(p, "No one to tell anything to.\n");
+ return COM_OK;
+ } else {
+ return tell(p, parray[p].last_tell, param[1].val.string, TELL_TELL, 0);
+ }
+ }
+ if (!strcmp(param[0].val.word, ",")) {
+ if (parray[p].last_channel < 0) {
+ pprintf(p, "No previous channel.\n");
+ return COM_OK;
+ } else {
+ return chtell(p, parray[p].last_channel, param[1].val.string);
+ }
+ }
+ p1 = player_find_part_login(param[0].val.word);
+ if ((p1 < 0) || (parray[p1].status == PLAYER_PASSWORD)
+ || (parray[p1].status == PLAYER_LOGIN)) {
+ pprintf(p, "No user named \"%s\" is logged in.\n", param[0].val.word);
+ return COM_OK;
+ }
+ return tell(p, p1, param[1].val.string, TELL_TELL, 0);
+ } else { /* Channel */
+ return chtell(p, param[0].val.integer, param[1].val.string);
+ }
+}
+
+PUBLIC int com_xtell(int p, param_list param)
+{
+ int p1;
+ char *msg;
+ char tmp[2048];
+
+ msg = param[1].val.string;
+ p1 = player_find_part_login(param[0].val.word);
+ if ((p1 < 0) || (parray[p1].status == PLAYER_PASSWORD)
+ || (parray[p1].status == PLAYER_LOGIN)) {
+ pprintf(p, "No user named \"%s\" is logged in.\n", param[0].val.word);
+ return COM_OK;
+ }
+ if (!printablestring(msg)) {
+ pprintf(p, "Your message contains some unprintable character(s).\n");
+ return COM_OK;
+ }
+ if ((!parray[p1].i_tell) && (!parray[p].registered)) {
+ pprintf(p, "Player \"%s\" isn't listening to unregistered tells.\n",
+ parray[p1].name);
+ return COM_OK;
+ }
+ if ((player_censored(p1, p)) && (parray[p].adminLevel == 0)) {
+ pprintf(p, "Player \"%s\" is censoring you.\n", parray[p1].name);
+ return COM_OK;
+ }
+ if (parray[p1].highlight) {
+ pprintf_highlight(p1, "\n%s", parray[p].name);
+ } else {
+ pprintf(p1, "\n%s", parray[p].name);
+ }
+ pprintf_prompt(p1, " tells you: %s\n", msg);
+
+ tmp[0] = '\0';
+ if (!(parray[p1].busy[0] == '\0')) {
+ sprintf(tmp, ", who %s (idle: %s)", parray[p1].busy,
+ hms_desc(player_idle(p1)));
+ } else {
+ if (((player_idle(p1) % 3600) / 60) > 2) {
+ sprintf(tmp, ", who has been idle %s", hms_desc(player_idle(p1)));
+ }
+ }
+ pprintf(p, "(told %s%s)\n", parray[p1].name,
+ (((parray[p1].game>=0) && (garray[parray[p1].game].status == GAME_EXAMINE))
+ ? ", who is examining a game" :
+ (parray[p1].game >= 0 && (parray[p1].game != parray[p].game))
+ ? ", who is playing" : tmp));
+ return COM_OK;
+}
+
+PUBLIC int com_say(int p, param_list param)
+{
+ if (parray[p].opponent < 0) {
+ if (parray[p].last_opponent < 0) {
+ pprintf(p, "No one to say anything to, try tell.\n");
+ return COM_OK;
+ } else {
+ return tell(p, parray[p].last_opponent, param[0].val.string, TELL_SAY, 0);
+ }
+ }
+ return tell(p, parray[p].opponent, param[0].val.string, TELL_SAY, 0);
+}
+
+#if 0
+PRIVATE int notorcen(int p, param_list param, int *num, int max,
+ char **list, char *listname)
+{
+ int i, p1, connected;
+
+ if (param[0].type != TYPE_WORD) {
+ if (!*num) {
+ pprintf(p, "Your %s list is empty.\n", listname);
+ return COM_OK;
+ } else
+ pprintf(p, "-- Your %s list contains %d names: --", listname, *num);
+ /* New code to print names in columns */
+ {
+ multicol *m = multicol_start(MAX_NOTIFY + 1);
+ for (i = 0; i < *num; i++)
+ multicol_store_sorted(m, list[i]);
+ multicol_pprint(m, p, 78, 2);
+ multicol_end(m);
+ }
+ return COM_OK;
+ }
+ if (*num >= max) {
+ pprintf(p, "Sorry, your %s list is already full.\n", listname);
+ return COM_OK;
+ }
+ if (!FindPlayer(p, param[0].val.word, &p1, &connected))
+ return COM_OK;
+
+ for (i = 0; i < *num; i++) {
+ if (!strcasecmp(list[i], param[0].val.word)) {
+ pprintf(p, "Your %s list already includes %s.\n",
+ listname, parray[p1].name);
+ if (!connected)
+ player_remove(p1);
+ return COM_OK;
+ }
+ }
+ if (p1 == p) {
+ pprintf(p, "You can't %s yourself.\n", listname);
+ return COM_OK;
+ }
+ list[*num] = strdup(parray[p1].name);
+ ++(*num);
+ pprintf(p, "%s is now on your %s list.\n", parray[p1].name, listname);
+ if (!connected)
+ player_remove(p1);
+ return COM_OK;
+}
+
+PRIVATE int unnotorcen(int p, param_list param, int *num, int max,
+ char **list, char *listname)
+{
+ char *pname = NULL;
+ int i, j;
+ int unc = 0;
+
+ if (param[0].type == TYPE_WORD) {
+ pname = param[0].val.word;
+ }
+ for (i = 0; i < *num; i++) {
+ if (!pname || !strcasecmp(pname, list[i])) {
+ pprintf(p, "%s is removed from your %s list.\n",
+ list[i], listname);
+ rfree(list[i]);
+ list[i] = NULL;
+ unc++;
+ }
+ }
+ if (unc) {
+ i = 0;
+ j = 0;
+ while (j < *num) {
+ if (list[j] != NULL) {
+ list[i++] = list[j];
+ }
+ j++;
+ }
+ while (i < j) {
+ list[i++] = NULL;
+ }
+ (*num) -= unc;
+ } else {
+ pprintf(p, "No one was removed from your %s list.\n", listname);
+ }
+ return COM_OK;
+}
+
+
+PUBLIC int com_notify(int p, param_list param)
+{
+ return notorcen(p, param, &parray[p].num_notify, MAX_NOTIFY,
+ parray[p].notifyList, "notify");
+}
+
+PUBLIC int com_censor(int p, param_list param)
+{
+ return notorcen(p, param, &parray[p].num_censor, MAX_CENSOR,
+ parray[p].censorList, "censor");
+}
+
+PUBLIC int com_unnotify(int p, param_list param)
+{
+ return unnotorcen(p, param, &parray[p].num_notify, MAX_NOTIFY,
+ parray[p].notifyList, "notify");
+}
+
+PUBLIC int com_uncensor(int p, param_list param)
+{
+ return unnotorcen(p, param, &parray[p].num_censor, MAX_CENSOR,
+ parray[p].censorList, "censor");
+}
+
+#endif
+
+#if 0 /* now in lists.c */
+PUBLIC int com_channel(int p, param_list param)
+{
+ int i, err;
+
+ if (param[0].type == TYPE_NULL) { /* Turn off all channels */
+ for (i = 0; i < MAX_CHANNELS; i++) {
+ if (!channel_remove(i, p))
+ pprintf(p, "Channel %d turned off.\n", i);
+ }
+ } else {
+ i = param[0].val.integer;
+ if ((i == 0) && (!on_channel(i, p)) && (parray[p].adminLevel == 0)) {
+ pprintf(p, "Only admins may join channel 0.\n");
+ return COM_OK;
+ }
+ if (i < 0) {
+ pprintf(p, "The lowest channel number is 0.\n");
+ return COM_OK;
+ }
+ if (i >= MAX_CHANNELS) {
+ pprintf(p, "The maximum channel number is %d.\n", MAX_CHANNELS - 1);
+ return COM_OK;
+ }
+ if (on_channel(i, p)) {
+ if (!channel_remove(i, p))
+ pprintf(p, "Channel %d turned off.\n", i);
+ } else {
+ if (!(err = channel_add(i, p)))
+ pprintf(p, "Channel %d turned on.\n", i);
+ else {
+ if (err == 1)
+ pprintf(p, "Channel %d is already full.\n", i);
+ if (err == 3)
+ pprintf(p, "Maximum channel number exceeded.\n");
+ }
+ }
+ }
+ return COM_OK;
+}
+#endif
+
+PUBLIC int com_inchannel(int p, param_list param)
+{
+ int p1,count = 0;
+ char tmp[18];
+
+
+ if (param[0].type == NULL) {
+ pprintf (p,"inchannel [no params] has been removed\n");
+ pprintf (p,"Please use inchannel [name] or inchannel [number]\n");
+ return COM_OK;
+ }
+
+ if (param[0].type == TYPE_WORD) {
+ p1 = player_find_part_login(param[0].val.word);
+ if ((p1 < 0) || (parray[p1].status != PLAYER_PROMPT)) {
+ pprintf(p, "No user named \"%s\" is logged in.\n", param[0].val.word);
+ return COM_OK;
+ }
+ pprintf (p,"%s is in the following channels:",parray[p1].name); /* no \n */
+ if (list_channels (p,p1))
+ pprintf (p," No channels found.\n",parray[p1].name);
+ return COM_OK;
+ } else {
+ sprintf (tmp,"%d",param[0].val.integer);
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (parray[p1].status != PLAYER_PROMPT)
+ continue;
+ if (in_list(p1,L_CHANNEL,tmp)) {
+ if (!count)
+ pprintf(p,"Channel %d:",param[0].val.integer);
+ pprintf (p," %s%s",parray[p1].name,(((parray[p1].adminLevel >= 10) && (parray[p1].i_admin) && (param[0].val.integer < 2)) ? "(*)" : ""));
+ count++;
+ }
+ }
+ if (!count)
+ pprintf(p,"Channel %d is empty.\n",param[0].val.integer);
+ else
+ pprintf (p,"\n%d %s in channel %d.\n",count,(count == 1 ? "person is" : "people are"),param[0].val.integer);
+ return COM_OK;
+ }
+}
+
+#if 0 /* if anyone can do inchannel NULL without n^3 computation
+ please do so*/
+PUBLIC int com_inchannel(int p, param_list param)
+{
+ int c1, c2;
+ int i, j, count = 0;
+
+ if (param[0].type == TYPE_NULL) { /* List everyone on every channel */
+ c1 = -1;
+ c2 = -1;
+ } else if (param[1].type == TYPE_NULL) { /* One parameter */
+ c1 = param[0].val.integer;
+ if (c1 < 0) {
+ pprintf(p, "The lowest channel number is 0.\n");
+ return COM_OK;
+ }
+ c2 = -1;
+ } else { /* Two parameters */
+ c1 = param[0].val.integer;
+ c2 = param[1].val.integer;
+ if ((c1 < 0) || (c2 < 0)) {
+ pprintf(p, "The lowest channel number is 0.\n");
+ return COM_OK;
+ }
+ pprintf(p, "Two parameter inchannel is not implemented.\n");
+ return COM_OK;
+ }
+ if ((c1 >= MAX_CHANNELS) || (c2 >= MAX_CHANNELS)) {
+ pprintf(p, "The maximum channel number is %d.\n", MAX_CHANNELS - 1);
+ return COM_OK;
+ }
+ for (i = 0; i < MAX_CHANNELS; i++) {
+ if (numOn[i] && ((c1 < 0) || (i == c1))) {
+ /* First get rid of ghosts. */
+ for (j=0; j < numOn[i]; j++) {
+ if (parray[channels[i][j]].status == PLAYER_PROMPT)
+ continue;
+ channels[i][j] = channels[i][--numOn[i]];
+ j--;
+ }
+ pprintf(p, "Channel %d:", i);
+ for (j = 0; j < numOn[i]; j++) {
+ pprintf(p, " %s", parray[channels[i][j]].name);
+ if ((i==0 || i==1) && parray[channels[i][j]].adminLevel >= 10
+ && parray[channels[i][j]].i_admin)
+ pprintf(p, "(*)");
+ }
+ count++;
+ pprintf(p, "\n");
+ }
+ }
+ if (!count) {
+ if (c1 < 0)
+ pprintf(p, "No channels in use.\n");
+ else
+ pprintf(p, "Channel not in use.\n");
+ }
+ return COM_OK;
+}
+#endif
+
+PUBLIC int com_sendmessage(int p, param_list param)
+{
+ int p1, connected = 1;
+
+ if (!parray[p].registered) {
+ pprintf(p, "You are not registered and cannot send messages.\n");
+ return COM_OK;
+ }
+ if ((param[0].type == TYPE_NULL) || (param[1].type == TYPE_NULL)) {
+ pprintf(p, "No message sent.\n");
+ return COM_OK;
+ }
+ if (!FindPlayer(p, param[0].val.word, &p1, &connected))
+ return COM_OK;
+
+ if (!parray[p1].registered) {
+ pprintf(p, "Player \"%s\" is unregistered and cannot receive messages.\n",
+ parray[p1].name);
+ return COM_OK; /* no need to removed */
+ }
+
+ if ((player_censored(p1, p)) && (parray[p].adminLevel == 0)) {
+ pprintf(p, "Player \"%s\" is censoring you.\n", parray[p1].name);
+ if (!connected)
+ player_remove(p1);
+ return COM_OK;
+ }
+ if (player_add_message(p1, p, param[1].val.string)) {
+ pprintf(p, "Couldn't send message to %s. Message buffer full.\n",
+ parray[p1].name);
+ } else {
+ if (connected) {
+ pprintf(p1, "\n%s just sent you a message:\n", parray[p].name);
+ pprintf_prompt(p1, " %s\n", param[1].val.string);
+ }
+ }
+ if (!connected)
+ player_remove(p1);
+ return COM_OK;
+}
+
+PUBLIC int com_messages(int p, param_list param)
+{
+ int start;
+ /* int end = -1; */
+
+ if (!parray[p].registered) {
+ pprintf (p, "Unregistered players may not send or receive messages.\n");
+ return COM_OK;
+ }
+ if (param[0].type == TYPE_NULL) {
+ player_show_messages (p);
+ } else if (param[0].type == TYPE_WORD) {
+ if (param[1].type != TYPE_NULL)
+ return com_sendmessage(p, param);
+ else ShowMsgsBySender(p, param);
+ } else {
+ start = param[0].val.integer;
+ ShowMsgRange (p, start, start);
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_clearmessages(int p, param_list param)
+{
+ int start;
+
+ if (player_num_messages(p) <= 0) {
+ pprintf(p, "You have no messages.\n");
+ return COM_OK;
+ }
+ if (param[0].type == TYPE_NULL) {
+ pprintf(p, "Messages cleared.\n");
+ player_clear_messages(p);
+ } else if (param[0].type == TYPE_WORD) {
+ ClearMsgsBySender(p, param);
+ } else if (param[0].type == TYPE_INT) {
+ start = param[0].val.integer;
+ ClrMsgRange (p, start, start);
+ }
+ return COM_OK;
+}
+
+PUBLIC int com_mailmess(int p, param_list param)
+{
+ char *buffer[1000];
+ char mdir[MAX_FILENAME_SIZE];
+ char filename[MAX_FILENAME_SIZE];
+ char subj[81], fname[MAX_FILENAME_SIZE];
+
+
+ if (!parray[p].registered) {
+ pprintf(p, "Only registered people can use the mailmess command.\n");
+ return COM_OK;
+ }
+ sprintf(filename, "%s.messages", parray[p].login);
+ sprintf(mdir, "%s/player_data/%c/", stats_dir, parray[p].login[0]);
+
+ if (search_directory(mdir, filename, buffer, 1000)) {
+ sprintf(subj, "Your FICS messages from server %s", fics_hostname);
+ sprintf(fname, "%s/%s", mdir, filename);
+ mail_file_to_user (p, subj, fname);
+ pprintf(p, "Messages sent to %s\n", parray[p].emailAddress);
+ } else {
+ pprintf(p, "You have no messages.\n");
+ }
+ return COM_OK;
+
+}
+
+PUBLIC int com_znotify(int p, param_list param)
+{
+ int p1, count = 0;
+
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (player_notified(p, p1)) {
+ if (!count)
+ pprintf(p, "Present company on your notify list:\n ");
+ pprintf(p, " %s", parray[p1].name);
+ count++;
+ }
+ }
+ if (count)
+ pprintf(p, ".\n");
+ else
+ pprintf(p, "No one from your notify list is logged on.\n");
+
+ count = 0;
+ for (p1 = 0; p1 < p_num; p1++) {
+ if (player_notified(p1, p) && parray[p1].status == PLAYER_PROMPT) {
+ if (!count)
+ pprintf(p,
+ "The following players have you on their notify list:\n ");
+ pprintf(p, " %s", parray[p1].name);
+ count++;
+ }
+ }
+ if (count)
+ pprintf(p, ".\n");
+ else
+ pprintf(p, "No one logged in has you on their notify list.\n");
+ return COM_OK;
+}
+
+PUBLIC int com_qtell(int p, param_list param)
+{
+ int p1;
+ char tmp[MAX_STRING_LENGTH];
+ char dummy[2];
+ char buffer1[MAX_STRING_LENGTH]; /* no highlight and no bell */
+ char buffer2[MAX_STRING_LENGTH]; /* no highlight and bell */
+ char buffer3[MAX_STRING_LENGTH]; /* highlight and no bell */
+ char buffer4[MAX_STRING_LENGTH]; /* highlight and and bell */
+ int i,count;
+/* FILE *fp; */
+
+ if (!in_list(p, L_TD, parray[p].name)) {
+ pprintf(p, "Only TD programs are allowed to use this command.\n");
+ return COM_OK;
+ }
+ strcpy(buffer1, ":\0");
+ strcpy(buffer2, ":\0");
+ strcpy(buffer3, ":\0");
+ strcpy(buffer4, ":\0");
+
+ sprintf(tmp, "%s", param[1].val.string);
+ for (i = 0, count = 0; ((tmp[i] != '\0') && (count < 1029));) {
+ if ((tmp[i] == '\\') && (tmp[i + 1] == 'n')) {
+ strcat(buffer1, "\n:");
+ strcat(buffer2, "\n:");
+ strcat(buffer3, "\n:");
+ strcat(buffer4, "\n:");
+ count += 2;
+ i += 2;
+ } else if ((tmp[i] == '\\') && (tmp[i + 1] == 'b')) {
+ strcat(buffer2, "\007");
+ strcat(buffer4, "\007");
+ count++;
+ i += 2;
+ } else if ((tmp[i] == '\\') && (tmp[i + 1] == 'H')) {
+ strcat(buffer3, "\033[7m");
+ strcat(buffer4, "\033[7m");
+ count += 4;
+ i += 2;
+ } else if ((tmp[i] == '\\') && (tmp[i + 1] == 'h')) {
+ strcat(buffer3, "\033[0m");
+ strcat(buffer4, "\033[0m");
+ count += 4;
+ i += 2;
+ } else {
+ dummy[0] = tmp[i];
+ dummy[1] = '\0';
+ strcat(buffer1, dummy);
+ strcat(buffer2, dummy);
+ strcat(buffer3, dummy);
+ strcat(buffer4, dummy);
+ count++;
+ i++;
+ }
+ }
+
+ if (param[0].type == TYPE_WORD) {
+/*
+ fp = fopen("/tmp/fics-log", "a");
+ fprintf(fp, "PLAYER \"%s\" - MESSAGE \"%s\"\n", param[0].val.word, param[1].val.string);
+ fclose(fp);
+*/
+ if ((p1 = player_find_bylogin(param[0].val.word)) < 0) {
+ pprintf(p, "*qtell %s 1*\n", param[0].val.word);
+ return COM_OK;
+ }
+ pprintf_prompt(p1, "\n%s\n", (parray[p1].highlight && parray[p1].bell) ? buffer4 :
+ (parray[p1].highlight && !parray[p1].bell) ? buffer3 :
+ (!parray[p1].highlight && parray[p1].bell) ? buffer2 :
+ buffer1);
+ pprintf(p, "*qtell %s 0*\n", parray[p1].name);
+
+ } else {
+ int p1;
+ int ch = param[0].val.integer;
+
+ if (ch == 0) {
+ pprintf(p, "*qtell %d 1*\n", param[0].val.integer);
+ return COM_OK;
+ }
+ if (ch < 0) {
+ pprintf(p, "*qtell %d 1*\n", param[0].val.integer);
+ return COM_OK;
+ }
+ if (ch >= MAX_CHANNELS) {
+ pprintf(p, "*qtell %d 1*\n", param[0].val.integer);
+ return COM_OK;
+ }
+ sprintf (tmp,"%d",param[0].val.integer);
+ for (p1 = 0; p1 < p_num ; p1++) {
+ if ((p1 == p) || (player_censored(p1, p)) || (parray[p1].status != PLAYER_PROMPT))
+ continue;
+ if (in_list(p1,L_CHANNEL,tmp))
+ pprintf_prompt(p1, "\n%s\n", (parray[p1].highlight && parray[p1].bell) ? buffer4 :
+ (parray[p1].highlight && !parray[p1].bell) ? buffer3 :
+ (!parray[p1].highlight && parray[p1].bell) ? buffer2 :
+ buffer1);
+ }
+ pprintf(p, "*qtell %d 0*\n", param[0].val.integer);
+ }
+ return COM_OK;
+}
+
+PUBLIC int on_channel(int ch, int p)
+{
+ char tmp[10]; /* 9 digits ought to be enough :) */
+
+ sprintf (tmp,"%d",ch);
+ return in_list(p, L_CHANNEL,tmp ); /* since needs ch converted to a string keep
+ hidden from view */
+}
+
+#if 0
+
+void WhichList(int p, char *partial, char **Full)
+{
+ int i, gotit = -1;
+ int slen = strlen(partial);
+ static char *MyList[] = {"notify", "censor", "channel", ""};
+
+ for (i=0; MyList[i][0] != '\0'; i++) {
+ if (strncmp(partial, MyList[i], slen))
+ continue;
+ if (gotit >= 0) {
+ pprintf(p, "Ambiguous list name.\n");
+ *Full = NULL;
+ return;
+ }
+ gotit = i;
+ }
+ if (gotit >= 0)
+ *Full = MyList[gotit];
+ else **Full = '\0';
+ return;
+}
+
+PUBLIC int com_plus(int p, param_list param)
+{
+ char *list;
+ int chan;
+ parameter *Param1 = &param[1];
+
+ WhichList (p, param[0].val.word, &list);
+
+ if (list == NULL) return COM_OK;
+
+ if (!strcmp(list, "notify"))
+ return notorcen (p, Param1, &parray[p].num_notify, MAX_NOTIFY,
+ parray[p].notifyList, "notify");
+ if (!strcmp(list, "censor"))
+ return notorcen (p, Param1, &parray[p].num_censor, MAX_NOTIFY,
+ parray[p].censorList, "censor");
+ if (!strcmp(list, "channel")) {
+ chan = atoi(param[1].val.word);
+ if (chan < 0) {
+ }
+
+ switch (channel_add(chan, p)) {
+ case 0: pprintf(p, "Channel %d turned on.\n", chan); break;
+ case 1: pprintf(p, "Channel %d is already full.\n", chan); break;
+ case 2: pprintf(p, "You are already on channel %d.\n", chan); break;
+ case 3: pprintf(p, "Maximum number of channels exceeded.\n"); break;
+ }
+ return COM_OK;
+ }
+ return com_addlist(p, param);
+}
+
+PUBLIC int com_minus(int p, param_list param)
+{
+ char *list;
+ int chan;
+ parameter *Param1 = &param[1];
+
+ WhichList (p, param[0].val.word, &list);
+
+ if (list == NULL) return COM_OK;
+
+ if (!strcmp(list, "notify"))
+ return unnotorcen (p, Param1, &parray[p].num_notify, MAX_NOTIFY,
+ parray[p].notifyList, "notify");
+ if (!strcmp(list, "censor"))
+ return unnotorcen (p, Param1, &parray[p].num_censor, MAX_CENSOR,
+ parray[p].censorList, "censor");
+ if (!strcmp(list, "channel")) {
+ chan = atoi(param[1].val.word);
+ if (on_channel(chan, p))
+ channel_remove(chan, p);
+ else
+ pprintf(p, "You are not on channel %d", chan);
+ return COM_OK;
+ }
+ return com_sublist(p, param);
+}
+#endif
diff --git a/FICS/talkproc.h b/FICS/talkproc.h
new file mode 100644
index 0000000..c2c9ee8
--- /dev/null
+++ b/FICS/talkproc.h
@@ -0,0 +1,56 @@
+/* talkproc.h
+ *
+ */
+
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name yy/mm/dd Change
+ hersco and Marsalis 95/07/24 Created
+*/
+
+#ifndef _TALKPROC_H
+#define _TALKPROC_H
+
+#define MAX_CHANNELS 256
+
+extern int quota_time;
+
+extern int com_xtell();
+extern int com_shout();
+extern int com_cshout();
+/* extern int com_query(); */
+extern int com_it();
+extern int com_tell();
+extern int com_ptell();
+extern int com_say();
+extern int com_whisper();
+extern int com_kibitz();
+extern int com_censor();
+extern int com_uncensor();
+extern int com_notify();
+extern int com_unnotify();
+extern int com_channel();
+extern int com_inchannel();
+extern int com_sendmessage();
+extern int com_messages();
+extern int com_clearmessages();
+extern int com_mailmess();
+extern int com_znotify();
+extern int com_qtell();
+extern int on_channel(int, int);
+
+#endif /* _TALKPROC_H */
diff --git a/FICS/utils.c b/FICS/utils.c
new file mode 100644
index 0000000..3cf08c0
--- /dev/null
+++ b/FICS/utils.c
@@ -0,0 +1,1049 @@
+/* utils.c
+ *
+ */
+
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name email yy/mm/dd Change
+ Richard Nash 93/10/22 Created
+*/
+
+#include "stdinclude.h"
+
+#include "common.h"
+#include "utils.h"
+#include "playerdb.h"
+#include "network.h"
+#include "rmalloc.h"
+#include "config.h"
+#if defined(SYSTEM_SUN4)
+/* #include <sys/vfs.h> */
+#endif
+
+/*
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+*/
+
+#if defined(SGI)
+#else
+int gettimeofday(struct timeval * tp, struct timezone * tzp);
+#endif
+
+PUBLIC int count_lines(FILE *fp)
+{
+ int c, nl = 0;
+
+ while ((c = fgetc(fp)) != EOF)
+ if (c == '\n')
+ ++nl;
+
+ return nl;
+}
+
+PUBLIC int iswhitespace(int c)
+{
+ if ((c < ' ') || (c == '\b') || (c == '\n') ||
+ (c == '\t') || (c == ' ')) { /* white */
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+PUBLIC char *getword(char *str)
+{
+ int i;
+ static char word[MAX_WORD_SIZE];
+
+ i = 0;
+ while (*str && !iswhitespace(*str)) {
+ word[i] = *str;
+ str++;
+ i++;
+ if (i == MAX_WORD_SIZE) {
+ i = i - 1;
+ break;
+ }
+ }
+ word[i] = '\0';
+ return word;
+}
+
+PUBLIC char *eatword(char *str)
+{
+ while (*str && !iswhitespace(*str))
+ str++;
+ return str;
+}
+
+PUBLIC char *eatwhite(char *str)
+{
+ while (*str && iswhitespace(*str))
+ str++;
+ return str;
+}
+
+PUBLIC char *eattailwhite(char *str)
+{
+ int len;
+ if (str == NULL)
+ return NULL;
+
+ len = strlen(str);
+ while (len > 0 && iswhitespace(str[len - 1]))
+ len--;
+ str[len] = '\0';
+ return (str);
+}
+
+PUBLIC char *nextword(char *str)
+{
+ return eatwhite(eatword(str));
+}
+
+/* check_the_email_address function: */
+/*
+PUBLIC int check_emailaddr(char *email)
+{
+ int qbuflen;
+ char qabuf[5120];
+
+ if ((email = strchr(email, '@')) == NULL)
+ return 2;
+ email++;
+
+ res_init();
+ _res.options = (RES_INIT | RES_RECURSE);
+ if ((qbuflen = res_mkquery(QUERY, email, C_IN, T_ANY, NULL, 0, NULL, qabuf, sizeof(qabuf))) == -1)
+ return 1;
+
+ if (res_send(qabuf, qbuflen, qabuf, sizeof(qabuf)) == -1)
+ return 1;
+
+ return (_getshort(qabuf + 6) ? 0 : 2);
+}
+*/
+
+PUBLIC int mail_string_to_address(char *addr, char *subj, char *str)
+{
+ char com[1000];
+ FILE *fp;
+
+#ifdef SENDMAILPROG
+ sprintf(com, "%s\n", SENDMAILPROG);
+#else
+ sprintf(com, "%s -s \"%s\" %s", MAILPROGRAM, subj, addr);
+#endif
+ fp = popen(com, "w");
+ if (!fp)
+ return -1;
+#ifdef SENDMAILPROG
+ fprintf(fp, "To: %s\nSubject: %s\n%s", addr, subj, str);
+#else
+ fprintf(fp, "%s", str);
+#endif
+ pclose(fp);
+ return 0;
+}
+
+PUBLIC int mail_string_to_user(int p, char *subj, char *str)
+{
+
+ if (parray[p].emailAddress &&
+ parray[p].emailAddress[0] &&
+ safestring(parray[p].emailAddress)) {
+ return mail_string_to_address(parray[p].emailAddress, subj, str);
+ } else {
+ return -1;
+ }
+}
+
+PUBLIC int mail_file_to_address(char *addr, char *subj, char *fname)
+{
+ char com[1000];
+ char tmp[MAX_LINE_SIZE];
+ FILE *fp;
+ FILE *file_fp;
+
+#ifdef SENDMAILPROG
+ sprintf(com, "%s\n", SENDMAILPROG);
+#else
+ sprintf(com, "%s -s \"%s\" %s < %s&", MAILPROGRAM, subj, addr, fname);
+#endif
+ fp = popen(com, "w");
+ if (!fp)
+ return -1;
+#ifdef SENDMAILPROG
+ fprintf(fp, "To: %s\nSubject: %s\n", addr, subj);
+ file_fp = fopen(fname, "r");
+ if (!file_fp)
+ return -1;
+ while (!feof(file_fp)) {
+ fgets(tmp, MAX_LINE_SIZE - 1, file_fp);
+ if (!feof(file_fp)) {
+ fputs(tmp, fp);
+ }
+ }
+ fclose(file_fp);
+
+#endif
+ pclose(fp);
+ return 0;
+}
+
+PUBLIC int mail_file_to_user(int p, char *subj, char *fname)
+{
+
+ if (parray[p].emailAddress &&
+ parray[p].emailAddress[0] &&
+ safestring(parray[p].emailAddress)) {
+ return mail_file_to_address(parray[p].emailAddress, subj, fname);
+ } else {
+ return -1;
+ }
+}
+
+
+/* Process a command for a user */
+PUBLIC int pcommand(int p, char *comstr,...)
+{
+ char tmp[MAX_LINE_SIZE];
+ int retval;
+ int current_socket = parray[p].socket;
+ va_list ap;
+ va_start(ap, comstr);
+
+ vsprintf(tmp, comstr, ap);
+ retval = process_input(current_socket, tmp);
+ if (retval == COM_LOGOUT) {
+ process_disconnection(current_socket);
+ net_close_connection(current_socket);
+ }
+ va_end(ap);
+ return retval;
+}
+
+PUBLIC int pprintf(int p, char *format,...)
+{
+ char tmp[10 * MAX_LINE_SIZE]; /* Make sure you can handle 10 lines worth of
+ stuff */
+ int retval;
+ va_list ap;
+ va_start(ap, format);
+
+ retval = vsprintf(tmp, format, ap);
+ if (strlen(tmp) > 10 * MAX_LINE_SIZE) {
+ fprintf(stderr, "FICS: pprintf buffer overflow\n");
+ }
+ net_send_string(parray[p].socket, tmp, 1);
+ va_end(ap);
+ return retval;
+}
+
+PUBLIC void pprintf_dohightlight(int p)
+{
+ if (parray[p].highlight & 0x01)
+ pprintf(p, "\033[7m");
+ if (parray[p].highlight & 0x02)
+ pprintf(p, "\033[1m");
+ if (parray[p].highlight & 0x04)
+ pprintf(p, "\033[4m");
+ if (parray[p].highlight & 0x08)
+ pprintf(p, "\033[2m");
+}
+
+PUBLIC int pprintf_highlight(int p, char *format,...)
+{
+ char tmp[10 * MAX_LINE_SIZE];
+ int retval;
+ va_list ap;
+
+ va_start(ap, format);
+ pprintf_dohightlight(p);
+ retval = vsprintf(tmp, format, ap);
+ if (strlen(tmp) > 10 * MAX_LINE_SIZE) {
+ fprintf(stderr, "FICS: pprintf buffer overflow\n");
+ }
+ net_send_string(parray[p].socket, tmp, 1);
+ va_end(ap);
+ if (parray[p].highlight)
+ pprintf(p, "\033[0m");
+ return retval;
+}
+
+PUBLIC void sprintf_dohightlight(int p, char *s)
+{
+
+ if (parray[p].highlight & 0x01)
+ strcat(s, "\033[7m");
+ if (parray[p].highlight & 0x02)
+ strcat(s, "\033[1m");
+ if (parray[p].highlight & 0x04)
+ strcat(s, "\033[4m");
+ if (parray[p].highlight & 0x08)
+ strcat(s, "\033[2m");
+}
+
+PUBLIC int psprintf_highlight(int p, char *s, char *format,...)
+{
+ int retval;
+ va_list ap;
+
+ va_start(ap, format);
+ if (parray[p].highlight) {
+ sprintf_dohightlight(p, s);
+ retval = vsprintf(s + strlen(s), format, ap);
+ strcat(s, "\033[0m");
+ } else {
+ retval = vsprintf(s, format, ap);
+ }
+ va_end(ap);
+ return retval;
+}
+
+PUBLIC int pprintf_prompt(int p, char *format,...)
+{
+ char tmp[10 * MAX_LINE_SIZE]; /* Make sure you can handle 10 lines worth of
+ stuff */
+ int retval;
+ va_list ap;
+
+ va_start(ap, format);
+
+ retval = vsprintf(tmp, format, ap);
+ if (strlen(tmp) > 10 * MAX_LINE_SIZE) {
+ fprintf(stderr, "FICS: pprintf_prompt buffer overflow\n");
+ }
+ net_send_string(parray[p].socket, tmp, 1);
+ net_send_string(parray[p].socket, parray[p].prompt, 1);
+ va_end(ap);
+ return retval;
+}
+
+PUBLIC int pprintf_noformat(int p, char *format,...)
+{
+ char tmp[10 * MAX_LINE_SIZE]; /* Make sure you can handle 10 lines worth of
+ stuff */
+ int retval;
+ va_list ap;
+
+ va_start(ap, format);
+
+ retval = vsprintf(tmp, format, ap);
+ if (strlen(tmp) > 10 * MAX_LINE_SIZE) {
+ fprintf(stderr, "FICS: pprintf_noformat buffer overflow\n");
+ }
+ net_send_string(parray[p].socket, tmp, 0);
+ va_end(ap);
+ return retval;
+}
+
+
+/******************* grimm *************/
+/******************* added below *************/
+PUBLIC int psend_raw_file(int p, char *dir, char *file)
+{
+ FILE *fp;
+ char tmp[MAX_LINE_SIZE];
+ char fname[MAX_FILENAME_SIZE];
+ int num;
+
+ if (dir)
+ sprintf(fname, "%s/%s", dir, file);
+ else
+ strcpy(fname, file);
+ fp = fopen(fname, "r");
+ if (!fp)
+ return -1;
+ while ((num = fread(tmp, sizeof(char), MAX_LINE_SIZE - 1, fp)) > 0) {
+ tmp[num] = '\0';
+ net_send_string(parray[p].socket, tmp, 1);
+ }
+ fclose(fp);
+ return 0;
+}
+
+PUBLIC int psend_file(int p, char *dir, char *file)
+{
+ FILE *fp;
+ char tmp[MAX_LINE_SIZE];
+ char fname[MAX_FILENAME_SIZE];
+ int lcount = parray[p].d_height - 1;
+
+ if (parray[p].last_file)
+ rfree(parray[p].last_file);
+ parray[p].last_file = NULL;
+ parray[p].last_file_byte = 0L;
+
+ if (dir)
+ sprintf(fname, "%s/%s", dir, file);
+ else
+ strcpy(fname, file);
+ fp = fopen(fname, "r");
+ if (!fp)
+ return -1;
+ while (!feof(fp) && (--lcount > 0)) {
+ fgets(tmp, MAX_LINE_SIZE - 1, fp);
+ if (!feof(fp)) {
+ net_send_string(parray[p].socket, tmp, 1);
+ }
+ }
+ if (!feof(fp)) {
+ parray[p].last_file = strdup(fname);
+ parray[p].last_file_byte = ftell(fp);
+ pprintf(p, "Type [next] to see next page.\n");
+ }
+ fclose(fp);
+ return 0;
+}
+
+/*
+ * Marsalis added on 8/27/95 so that [next] does not
+ * appear in the logout process for those that have
+ * a short screen height. (Fixed due to complaint
+ * in Bug's messages).
+ */
+PUBLIC int psend_logoutfile(int p, char *dir, char *file)
+{
+ FILE *fp;
+ char tmp[MAX_LINE_SIZE];
+ char fname[MAX_FILENAME_SIZE];
+
+ if (parray[p].last_file)
+ rfree(parray[p].last_file);
+ parray[p].last_file = NULL;
+ parray[p].last_file_byte = 0L;
+
+ if (dir)
+ sprintf(fname, "%s/%s", dir, file);
+ else
+ strcpy(fname, file);
+ fp = fopen(fname, "r");
+ if (!fp)
+ return -1;
+ while (!feof(fp)) {
+ fgets(tmp, MAX_LINE_SIZE - 1, fp);
+ if (!feof(fp)) {
+ net_send_string(parray[p].socket, tmp, 1);
+ }
+ }
+
+ fclose(fp);
+ return 0;
+}
+
+PUBLIC int pmore_file(int p)
+{
+ FILE *fp;
+ char tmp[MAX_LINE_SIZE];
+ int lcount = parray[p].d_height - 1;
+
+ if (!parray[p].last_file) {
+ pprintf(p, "There is no more.\n");
+ return -1;
+ }
+ fp = fopen(parray[p].last_file, "r");
+ if (!fp) {
+ pprintf(p, "File not found!\n");
+ return -1;
+ }
+ fseek(fp, parray[p].last_file_byte, SEEK_SET);
+
+ while (!feof(fp) && (--lcount > 0)) {
+ fgets(tmp, MAX_LINE_SIZE, fp);
+ if (!feof(fp)) {
+ net_send_string(parray[p].socket, tmp, 1);
+ }
+ }
+ if (!feof(fp)) {
+ parray[p].last_file_byte = ftell(fp);
+ pprintf(p, "Type [next] to see next page.\n");
+ } else {
+ rfree(parray[p].last_file);
+ parray[p].last_file = NULL;
+ parray[p].last_file_byte = 0L;
+ }
+ fclose(fp);
+ return 0;
+}
+
+PUBLIC int psend_command(int p, char *command, char *input)
+{
+ FILE *fp;
+ char tmp[MAX_LINE_SIZE];
+ int num;
+
+ if (input)
+ fp = popen(command, "w");
+ else
+ fp = popen(command, "r");
+ if (!fp)
+ return -1;
+ if (input) {
+ fwrite(input, sizeof(char), strlen(input), fp);
+ } else {
+ while (!feof(fp)) {
+ num = fread(tmp, sizeof(char), MAX_LINE_SIZE - 1, fp);
+ tmp[num] = '\0';
+ net_send_string(parray[p].socket, tmp, 1);
+ }
+ }
+ pclose(fp);
+ return 0;
+}
+
+PUBLIC char *stolower(char *str)
+{
+ int i;
+
+ if (!str)
+ return NULL;
+ for (i = 0; str[i]; i++) {
+ if (isupper(str[i])) {
+ str[i] = tolower(str[i]);
+ }
+ }
+ return str;
+}
+
+PUBLIC int safechar(int c)
+{
+ if ((c == '>') || (c == '!') || (c == '&') || (c == '*') || (c == '?') ||
+ (c == '/') || (c == '<') || (c == '|') || (c == '`') || (c == '$'))
+ return 0;
+ return 1;
+}
+
+PUBLIC int safestring(char *str)
+{
+ int i;
+
+ if (!str)
+ return 1;
+ for (i = 0; str[i]; i++) {
+ if (!safechar(str[i]))
+ return 0;
+ }
+ return 1;
+}
+
+PUBLIC int alphastring(char *str)
+{
+ int i;
+
+ if (!str)
+ return 1;
+ for (i = 0; str[i]; i++) {
+ if (!isalpha(str[i])) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+PUBLIC int printablestring(char *str)
+{
+ int i;
+
+ if (!str)
+ return 1;
+ for (i = 0; str[i]; i++) {
+ if ((!isprint(str[i])) && (str[i] != '\t') && (str[i] != '\n'))
+ return 0;
+ }
+ return 1;
+}
+
+
+/*#if defined(SGI)
+#else*/
+/* This version of strdup must be used, or the count of allocs
+ in "uptime" will omit them. Maybe we change the name to
+ rstrdup and replace all calls, if the name is causing a conflict
+ with anyone's libraries. --mann
+*/
+PUBLIC char *strdup(const char *str)
+{
+ char *tmp;
+
+ if (!str)
+ return NULL;
+ tmp = rmalloc(strlen(str) + 1);
+ strcpy(tmp, str);
+ return tmp;
+}
+/*#endif*/
+
+PUBLIC char *hms_desc(int t)
+{
+static char tstr[80];
+int days, hours, mins, secs;
+
+ days = (t / (60*60*24));
+ hours = ((t % (60*60*24)) / (60*60));
+ mins = (((t % (60*60*24)) % (60*60)) / 60);
+ secs = (((t % (60*60*24)) % (60*60)) % 60);
+ if ((days==0) && (hours==0) && (mins==0)) {
+ sprintf(tstr, "%d sec%s",
+ secs, (secs==1) ? "" : "s");
+ } else if ((days==0) && (hours==0)) {
+/* sprintf(tstr, "%d min%s, %d sec%s",
+ mins, (mins==1) ? "" : "s",
+ secs, (secs==1) ? "" : "s"); */
+ sprintf(tstr, "%d min%s",
+ mins, (mins==1) ? "" : "s");
+ } else if (days==0) {
+ sprintf(tstr, "%d hr%s, %d min%s, %d "
+ "sec%s",
+ hours, (hours==1) ? "" : "s",
+ mins, (mins==1) ? "" : "s",
+ secs, (secs==1) ? "" : "s");
+ } else {
+ sprintf(tstr, "%d day%s, %d hour%s, %d minute%s "
+ "and %d second%s",
+ days, (days==1) ? "" : "s",
+ hours, (hours==1) ? "" : "s",
+ mins, (mins==1) ? "" : "s",
+ secs, (secs==1) ? "" : "s");
+ }
+ return tstr;
+}
+
+PUBLIC char *hms(int t, int showhour, int showseconds, int spaces)
+{
+ static char tstr[20];
+ char tmp[10];
+ int h, m, s;
+
+ h = t / 3600;
+ t = t % 3600;
+ m = t / 60;
+ s = t % 60;
+ if (h || showhour) {
+ if (spaces)
+ sprintf(tstr, "%d : %02d", h, m);
+ else
+ sprintf(tstr, "%d:%02d", h, m);
+ } else {
+ sprintf(tstr, "%d", m);
+ }
+ if (showseconds) {
+ if (spaces)
+ sprintf(tmp, " : %02d", s);
+ else
+ sprintf(tmp, ":%02d", s);
+ strcat(tstr, tmp);
+ }
+ return tstr;
+}
+
+PRIVATE char *strtime(struct tm * stm)
+{
+ static char tstr[100];
+
+#if defined (SGI)
+ strftime(tstr, sizeof(tstr), "%a %b %e, %H:%M %Z", stm);
+#else
+ strftime(tstr, sizeof(tstr), "%a %b %e, %k:%M %Z", stm);
+#endif
+ return (tstr);
+}
+
+PUBLIC char *fix_time(char *old_time) {
+
+ char day[5];
+ char month[5];
+ char date[5];
+ char new_time[20];
+ char i;
+
+ sscanf(old_time, "%s %s %s", day, month, date);
+
+ if (date[2] != ',') {
+ i = date[0];
+ date[0] = '0';
+ date[1] = i;
+ }
+ date[2] = '\0';
+
+ sprintf (new_time, "%s, %s %s", day, month, date);
+
+ return new_time;
+}
+
+PUBLIC char *strltime(time_t *clock)
+{
+ struct tm *stm = localtime(clock);
+ return strtime(stm);
+}
+
+PUBLIC char *strgtime(time_t *clock)
+{
+ struct tm *stm = gmtime(clock);
+ return strtime(stm);
+}
+
+/* This is used only for relative timeing since it reports seconds since
+ * about 5:00pm on Feb 16, 1994
+ */
+PUBLIC unsigned tenth_secs(void)
+{
+ struct timeval tp;
+ struct timezone tzp;
+
+ gettimeofday(&tp, &tzp);
+/* .1 seconds since 1970 almost fills a 32 bit int! So lets subtract off
+ * the time right now */
+ return ((tp.tv_sec - 331939277) * 10L) + (tp.tv_usec / 100000);
+}
+
+/* This is to translate tenths-secs time back into 1/1/70 time in full
+ * seconds, because vek didn't read utils.c when he programmed new ratings.
+ 1 sec since 1970 fits into a 32 bit int OK.
+*/
+PUBLIC int untenths(unsigned tenths)
+{
+ return (tenths / 10 + 331939277 + 0xffffffff / 10 + 1);
+}
+
+PUBLIC char *tenth_str(unsigned t, int spaces)
+{
+ return hms((t + 5) / 10, 0, 1, spaces); /* Round it */
+}
+
+#define MAX_TRUNC_SIZE 100
+
+/* Warning, if lines in the file are greater than 1024 bytes in length, this
+ won't work! */
+/* nasty bug fixed by mann, 3-12-95 */
+PUBLIC int truncate_file(char *file, int lines)
+{
+ FILE *fp;
+ int bptr = 0, trunc = 0, i;
+ char tBuf[MAX_TRUNC_SIZE][MAX_LINE_SIZE];
+
+ if (lines > MAX_TRUNC_SIZE)
+ lines = MAX_TRUNC_SIZE;
+ fp = fopen(file, "r");
+ if (!fp)
+ return 1;
+ while (!feof(fp)) {
+ fgets(tBuf[bptr], MAX_LINE_SIZE, fp);
+ if (feof(fp))
+ break;
+ if (tBuf[bptr][strlen(tBuf[bptr]) - 1] != '\n') { /* Line too long */
+ fclose(fp);
+ return -1;
+ }
+ bptr++;
+ if (bptr == lines) {
+ trunc = 1;
+ bptr = 0;
+ }
+ }
+ fclose(fp);
+ if (trunc) {
+ fp = fopen(file, "w");
+ for (i = 0; i < lines; i++) {
+ fputs(tBuf[bptr], fp);
+ bptr++;
+ if (bptr == lines) {
+ bptr = 0;
+ }
+ }
+ fclose(fp);
+ }
+ return 0;
+}
+
+/* Warning, if lines in the file are greater than 1024 bytes in length, this
+ won't work! */
+PUBLIC int lines_file(char *file)
+{
+ FILE *fp;
+ int lcount = 0;
+ char tmp[MAX_LINE_SIZE];
+
+ fp = fopen(file, "r");
+ if (!fp)
+ return 0;
+ while (!feof(fp)) {
+ if (fgets(tmp, MAX_LINE_SIZE, fp))
+ lcount++;
+ }
+ fclose(fp);
+ return lcount;
+}
+
+PUBLIC int file_has_pname(char *fname, char *plogin)
+{
+ if (!strcmp(file_wplayer(fname), plogin))
+ return 1;
+ if (!strcmp(file_bplayer(fname), plogin))
+ return 1;
+ return 0;
+}
+
+PUBLIC char *file_wplayer(char *fname)
+{
+ static char tmp[MAX_FILENAME_SIZE];
+ char *ptr;
+ strcpy(tmp, fname);
+ ptr = rindex(tmp, '-');
+ if (!ptr)
+ return "";
+ *ptr = '\0';
+ return tmp;
+}
+
+PUBLIC char *file_bplayer(char *fname)
+{
+ char *ptr;
+
+ ptr = rindex(fname, '-');
+ if (!ptr)
+ return "";
+ return ptr + 1;
+}
+
+
+/* Hey, leave this code alone. "a" is always in network byte order,
+ which is big-endian, even on a little-endian machine. I fixed this
+ code to handle that correctly a while ago, and someone
+ gratuitiously changed it so that it would work correctly only on a
+ big-endian machine (like a Sun). I have now changed it back. --mann
+*/
+PUBLIC char *dotQuad(unsigned int a)
+{
+ static char tmp[20];
+ unsigned char *aa = (unsigned char *) &a;
+
+ sprintf(tmp, "%d.%d.%d.%d", aa[0], aa[1], aa[2], aa[3]);
+ return tmp;
+}
+
+PUBLIC int available_space(void)
+{
+#if defined(SYSTEM_NEXT)
+ struct statfs buf;
+
+ statfs(player_dir, &buf);
+ return ((buf.f_bsize * buf.f_bavail));
+#elif defined(SYSTEM_ULTRIX)
+ struct fs_data buf;
+
+ statfs(player_dir, &buf);
+ return ((1024 * buf.bfreen));
+#elif defined(SYSTEM_SUN4)
+/*
+ struct statfs buf;
+
+ statfs(player_dir, buf);
+ return (1024 * buf.f_bfree); */
+ return 100000000; /* Infinite space */
+#else
+ return 100000000; /* Infinite space */
+#endif
+}
+
+PUBLIC int file_exists(char *fname)
+{
+ FILE *fp;
+
+ fp = fopen(fname, "r");
+ if (!fp)
+ return 0;
+ fclose(fp);
+ return 1;
+}
+
+PUBLIC char *ratstr(int rat)
+{
+ static int on = 0;
+ static char tmp[20][10];
+
+ if (on == 20)
+ on = 0;
+ if (rat) {
+ sprintf(tmp[on], "%4d", rat);
+ } else {
+ sprintf(tmp[on], "----");
+
+ }
+ on++;
+ return tmp[on - 1];
+}
+
+PUBLIC char *ratstrii(int rat, int reg)
+{
+ static int on = 0;
+ static char tmp[20][10];
+
+ if (on == 20)
+ on = 0;
+ if (rat) {
+ sprintf(tmp[on], "%4d", rat);
+ } else {
+ if (reg) {
+ sprintf(tmp[on], "----");
+ } else {
+ sprintf(tmp[on], "++++");
+ }
+ }
+ on++;
+ return tmp[on - 1];
+}
+
+struct t_tree {
+ struct t_tree *left, *right;
+ char name; /* Not just 1 char - space for whole name */
+}; /* is allocated. Maybe a little cheesy? */
+
+struct t_dirs {
+ struct t_dirs *left, *right;
+ time_t mtime; /* dir's modification time */
+ struct t_tree *files;
+ char name; /* ditto */
+};
+
+PRIVATE char **t_buffer = NULL; /* pointer to next space in return buffer */
+PRIVATE int t_buffersize = 0; /* size of return buffer */
+
+/* fill t_buffer with anything matching "want*" in file tree t_tree */
+PRIVATE void t_sft(char *want, struct t_tree *t)
+{
+ if (t) {
+ int cmp = strncmp(want, &t->name, strlen(want));
+ if (cmp <= 0) /* if want <= this one, look left */
+ t_sft(want, t->left);
+ if (t_buffersize && (cmp == 0)) { /* if a match, add it to buffer */
+ t_buffersize--;
+ *t_buffer++ = &(t->name);
+ }
+ if (cmp >= 0) /* if want >= this one, look right */
+ t_sft(want, t->right);
+ }
+}
+
+/* delete file tree t_tree */
+PRIVATE void t_cft(struct t_tree **t)
+{
+ if (*t) {
+ t_cft(&(*t)->left);
+ t_cft(&(*t)->right);
+ rfree(*t);
+ *t = NULL;
+ }
+}
+
+/* make file tree for dir d */
+PRIVATE void t_mft(struct t_dirs *d)
+{
+ DIR *dirp;
+#ifdef USE_DIRENT
+ struct dirent *dp;
+#else
+ struct direct *dp;
+#endif
+ struct t_tree **t;
+
+ if ((dirp = opendir(&(d->name))) == NULL) {
+ fprintf(stderr, "FICS:mft() couldn't opendir.\n");
+ } else {
+ while ((dp = readdir(dirp))) {
+ t = &d->files;
+ if (dp->d_name[0] != '.') { /* skip anything starting with . */
+ while (*t) {
+ if (strcmp(dp->d_name, &(*t)->name) < 0) {
+ t = &(*t)->left;
+ } else {
+ t = &(*t)->right;
+ }
+ }
+ *t = rmalloc(sizeof(struct t_tree) + strlen(dp->d_name));
+ (*t)->right = (*t)->left = NULL;
+ strcpy(&(*t)->name, dp->d_name);
+ }
+ }
+ closedir(dirp);
+ }
+}
+
+PUBLIC int search_directory(char *dir, char *filter, char **buffer, int buffersize)
+{
+/* dir = directory to search
+ filter = what to search for
+ buffer = where to store pointers to matches
+ buffersize = how many pointers will fit inside buffer */
+
+ static struct t_dirs *ramdirs = NULL;
+ struct t_dirs **i;
+ int cmp;
+ static char nullify = '\0';
+ struct stat statbuf;
+
+ t_buffer = buffer;
+ t_buffersize = buffersize;
+
+ if (!stat(dir, &statbuf)) {
+ if (filter == NULL) /* NULL becomes pointer to null string */
+ filter = &nullify;
+ i = &ramdirs;
+ while (*i) { /* find dir in dir tree */
+ cmp = strcmp(dir, &(*i)->name);
+ if (cmp == 0)
+ break;
+ else if (cmp < 0)
+ i = &(*i)->left;
+ else
+ i = &(*i)->right;
+ }
+ if (!*i) { /* if dir isn't in dir tree, add him */
+ *i = rmalloc(sizeof(struct t_dirs) + strlen(dir));
+ (*i)->left = (*i)->right = NULL;
+ (*i)->files = NULL;
+ strcpy(&(*i)->name, dir);
+ }
+ if ((*i)->files) { /* delete any obsolete file tree */
+ if ((*i)->mtime != statbuf.st_mtime) {
+ t_cft(&(*i)->files);
+ }
+ }
+ if ((*i)->files == NULL) { /* if no file tree for him, make one */
+ (*i)->mtime = statbuf.st_mtime;
+ t_mft(*i);
+ }
+ t_sft(filter, (*i)->files); /* finally, search for matches */
+ }
+ return (buffersize - t_buffersize);
+}
+
+PUBLIC int display_directory(int p, char **buffer, int count)
+/* buffer contains 'count' string pointers */
+{
+ int i;
+ multicol *m = multicol_start(count);
+
+ for (i = 0; (i < count); i++)
+ multicol_store(m, *buffer++);
+ multicol_pprint(m, p, 78, 1);
+ multicol_end(m);
+ return (i);
+}
diff --git a/FICS/utils.c.orig b/FICS/utils.c.orig
new file mode 100644
index 0000000..1d4fdaa
--- /dev/null
+++ b/FICS/utils.c.orig
@@ -0,0 +1,991 @@
+/* utils.c
+ *
+ */
+
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name email yy/mm/dd Change
+ Richard Nash 93/10/22 Created
+*/
+
+#include "stdinclude.h"
+
+#include "common.h"
+#include "utils.h"
+#include "playerdb.h"
+#include "network.h"
+#include "rmalloc.h"
+#include "config.h"
+#if defined(SYSTEM_SUN4)
+/* #include <sys/vfs.h> */
+#endif
+
+/*
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+*/
+
+/* grimm */
+#if defined(SGI)
+#else
+/* char vsprintf(char *tmp, va_list format, va_list ap); */
+int gettimeofday(struct timeval * tp, struct timezone * tzp);
+#endif
+/* added for warnings */
+
+PUBLIC int count_lines(FILE *fp)
+{
+ int c, nl = 0;
+
+ while ((c = fgetc(fp)) != EOF)
+ if (c == '\n')
+ ++nl;
+
+ return nl;
+}
+
+PUBLIC int iswhitespace(int c)
+{
+ if ((c < ' ') || (c == '\b') || (c == '\n') ||
+ (c == '\t') || (c == ' ')) { /* white */
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+PUBLIC char *getword(char *str)
+{
+ int i;
+ static char word[MAX_WORD_SIZE];
+
+ i = 0;
+ while (*str && !iswhitespace(*str)) {
+ word[i] = *str;
+ str++;
+ i++;
+ if (i == MAX_WORD_SIZE) {
+ i = i - 1;
+ break;
+ }
+ }
+ word[i] = '\0';
+ return word;
+}
+
+PUBLIC char *eatword(char *str)
+{
+ while (*str && !iswhitespace(*str))
+ str++;
+ return str;
+}
+
+PUBLIC char *eatwhite(char *str)
+{
+ while (*str && iswhitespace(*str))
+ str++;
+ return str;
+}
+
+PUBLIC char *eattailwhite(char *str)
+{
+ int len;
+ if (str == NULL)
+ return NULL;
+
+ len = strlen(str);
+ while (len > 0 && iswhitespace(str[len - 1]))
+ len--;
+ str[len] = '\0';
+ return (str);
+}
+
+PUBLIC char *nextword(char *str)
+{
+ return eatwhite(eatword(str));
+}
+
+/* check_the_email_address function: */
+/*
+PUBLIC int check_emailaddr(char *email)
+{
+ int qbuflen;
+ char qabuf[5120];
+
+ if ((email = strchr(email, '@')) == NULL)
+ return 2;
+ email++;
+
+ res_init();
+ _res.options = (RES_INIT | RES_RECURSE);
+ if ((qbuflen = res_mkquery(QUERY, email, C_IN, T_ANY, NULL, 0, NULL, qabuf, sizeof(qabuf))) == -1)
+ return 1;
+
+ if (res_send(qabuf, qbuflen, qabuf, sizeof(qabuf)) == -1)
+ return 1;
+
+ return (_getshort(qabuf + 6) ? 0 : 2);
+}
+*/
+
+PUBLIC int mail_string_to_address(char *addr, char *subj, char *str)
+{
+ char com[1000];
+ FILE *fp;
+
+#ifdef SENDMAILPROG
+ sprintf(com, "%s\n", SENDMAILPROG);
+#else
+ sprintf(com, "%s -s \"%s\" %s", MAILPROGRAM, subj, addr);
+#endif
+ fp = popen(com, "w");
+ if (!fp)
+ return -1;
+#ifdef SENDMAILPROG
+ fprintf(fp, "To: %s\nSubject: %s\n%s", addr, subj, str);
+#else
+ fprintf(fp, "%s", str);
+#endif
+ pclose(fp);
+ return 0;
+}
+
+PUBLIC int mail_string_to_user(int p, char *subj, char *str)
+{
+
+ if (parray[p].emailAddress &&
+ parray[p].emailAddress[0] &&
+ safestring(parray[p].emailAddress)) {
+ return mail_string_to_address(parray[p].emailAddress, subj, str);
+ } else {
+ return -1;
+ }
+}
+
+PUBLIC int mail_file_to_address(char *addr, char *subj, char *fname)
+{
+ char com[1000];
+ char tmp[MAX_LINE_SIZE];
+ FILE *fp;
+ FILE *file_fp;
+
+#ifdef SENDMAILPROG
+ sprintf(com, "%s\n", SENDMAILPROG);
+#else
+ sprintf(com, "%s -s \"%s\" %s < %s&", MAILPROGRAM, subj, addr, fname);
+#endif
+ fp = popen(com, "w");
+ if (!fp)
+ return -1;
+#ifdef SENDMAILPROG
+ fprintf(fp, "To: %s\nSubject: %s\n", addr, subj);
+ file_fp = fopen(fname, "r");
+ if (!file_fp)
+ return -1;
+ while (!feof(file_fp)) {
+ fgets(tmp, MAX_LINE_SIZE - 1, file_fp);
+ if (!feof(file_fp)) {
+ fputs(tmp, fp);
+ }
+ }
+ fclose(file_fp);
+
+#endif
+ pclose(fp);
+ return 0;
+}
+
+PUBLIC int mail_file_to_user(int p, char *subj, char *fname)
+{
+
+ if (parray[p].emailAddress &&
+ parray[p].emailAddress[0] &&
+ safestring(parray[p].emailAddress)) {
+ return mail_file_to_address(parray[p].emailAddress, subj, fname);
+ } else {
+ return -1;
+ }
+}
+
+
+/* Process a command for a user */
+PUBLIC int pcommand(int p, char *comstr,...)
+{
+ char tmp[MAX_LINE_SIZE];
+ int retval;
+ int current_socket = parray[p].socket;
+ va_list ap;
+ va_start(ap, comstr);
+
+ vsprintf(tmp, comstr, ap);
+ retval = process_input(current_socket, tmp);
+ if (retval == COM_LOGOUT) {
+ process_disconnection(current_socket);
+ net_close_connection(current_socket);
+ }
+ va_end(ap);
+ return retval;
+}
+
+PUBLIC int pprintf(int p, char *format,...)
+{
+ char tmp[10 * MAX_LINE_SIZE]; /* Make sure you can handle 10 lines worth of
+ stuff */
+ int retval;
+ va_list ap;
+ va_start(ap, format);
+
+ retval = vsprintf(tmp, format, ap);
+ if (strlen(tmp) > 10 * MAX_LINE_SIZE) {
+ fprintf(stderr, "FICS: pprintf buffer overflow\n");
+ }
+ net_send_string(parray[p].socket, tmp, 1);
+ va_end(ap);
+ return retval;
+}
+
+PUBLIC void pprintf_dohightlight(int p)
+{
+ if (parray[p].highlight & 0x01)
+ pprintf(p, "\033[7m");
+ if (parray[p].highlight & 0x02)
+ pprintf(p, "\033[1m");
+ if (parray[p].highlight & 0x04)
+ pprintf(p, "\033[4m");
+ if (parray[p].highlight & 0x08)
+ pprintf(p, "\033[2m");
+}
+
+PUBLIC int pprintf_highlight(int p, char *format,...)
+{
+ char tmp[10 * MAX_LINE_SIZE];
+ int retval;
+ va_list ap;
+
+ va_start(ap, format);
+ pprintf_dohightlight(p);
+ retval = vsprintf(tmp, format, ap);
+ if (strlen(tmp) > 10 * MAX_LINE_SIZE) {
+ fprintf(stderr, "FICS: pprintf buffer overflow\n");
+ }
+ net_send_string(parray[p].socket, tmp, 1);
+ va_end(ap);
+ if (parray[p].highlight)
+ pprintf(p, "\033[0m");
+ return retval;
+}
+
+PUBLIC void sprintf_dohightlight(int p, char *s)
+{
+
+ if (parray[p].highlight & 0x01)
+ strcat(s, "\033[7m");
+ if (parray[p].highlight & 0x02)
+ strcat(s, "\033[1m");
+ if (parray[p].highlight & 0x04)
+ strcat(s, "\033[4m");
+ if (parray[p].highlight & 0x08)
+ strcat(s, "\033[2m");
+}
+
+PUBLIC int psprintf_highlight(int p, char *s, char *format,...)
+{
+ int retval;
+ va_list ap;
+
+ va_start(ap, format);
+ if (parray[p].highlight) {
+ sprintf_dohightlight(p, s);
+ retval = vsprintf(s + strlen(s), format, ap);
+ strcat(s, "\033[0m");
+ } else {
+ retval = vsprintf(s, format, ap);
+ }
+ va_end(ap);
+ return retval;
+}
+
+PUBLIC int pprintf_prompt(int p, char *format,...)
+{
+ char tmp[10 * MAX_LINE_SIZE]; /* Make sure you can handle 10 lines worth of
+ stuff */
+ int retval;
+ va_list ap;
+
+ va_start(ap, format);
+
+ retval = vsprintf(tmp, format, ap);
+ if (strlen(tmp) > 10 * MAX_LINE_SIZE) {
+ fprintf(stderr, "FICS: pprintf_prompt buffer overflow\n");
+ }
+ net_send_string(parray[p].socket, tmp, 1);
+ net_send_string(parray[p].socket, parray[p].prompt, 1);
+ va_end(ap);
+ return retval;
+}
+
+PUBLIC int pprintf_noformat(int p, char *format,...)
+{
+ char tmp[10 * MAX_LINE_SIZE]; /* Make sure you can handle 10 lines worth of
+ stuff */
+ int retval;
+ va_list ap;
+
+ va_start(ap, format);
+
+ retval = vsprintf(tmp, format, ap);
+ if (strlen(tmp) > 10 * MAX_LINE_SIZE) {
+ fprintf(stderr, "FICS: pprintf_noformat buffer overflow\n");
+ }
+ net_send_string(parray[p].socket, tmp, 0);
+ va_end(ap);
+ return retval;
+}
+
+
+/******************* grimm *************/
+/******************* added below *************/
+PUBLIC int psend_raw_file(int p, char *dir, char *file)
+{
+ FILE *fp;
+ char tmp[MAX_LINE_SIZE];
+ char fname[MAX_FILENAME_SIZE];
+ int num;
+
+ if (dir)
+ sprintf(fname, "%s/%s", dir, file);
+ else
+ strcpy(fname, file);
+ fp = fopen(fname, "r");
+ if (!fp)
+ return -1;
+ while ((num = fread(tmp, sizeof(char), MAX_LINE_SIZE - 1, fp)) > 0) {
+ tmp[num] = '\0';
+ net_send_string(parray[p].socket, tmp, 1);
+ }
+ fclose(fp);
+ return 0;
+}
+
+PUBLIC int psend_file(int p, char *dir, char *file)
+{
+ FILE *fp;
+ char tmp[MAX_LINE_SIZE];
+ char fname[MAX_FILENAME_SIZE];
+ int lcount = parray[p].d_height - 1;
+
+ if (parray[p].last_file)
+ rfree(parray[p].last_file);
+ parray[p].last_file = NULL;
+ parray[p].last_file_byte = 0L;
+
+ if (dir)
+ sprintf(fname, "%s/%s", dir, file);
+ else
+ strcpy(fname, file);
+ fp = fopen(fname, "r");
+ if (!fp)
+ return -1;
+ while (!feof(fp) && (--lcount > 0)) {
+ fgets(tmp, MAX_LINE_SIZE - 1, fp);
+ if (!feof(fp)) {
+ net_send_string(parray[p].socket, tmp, 1);
+ }
+ }
+ if (!feof(fp)) {
+ parray[p].last_file = strdup(fname);
+ parray[p].last_file_byte = ftell(fp);
+ pprintf(p, "Type [next] to see next page.\n");
+ }
+ fclose(fp);
+ return 0;
+}
+
+/*
+ * Marsalis added on 8/27/95 so that [next] does not
+ * appear in the logout process for those that have
+ * a short screen height. (Fixed due to complaint
+ * in Bug's messages).
+ */
+PUBLIC int psend_logoutfile(int p, char *dir, char *file)
+{
+ FILE *fp;
+ char tmp[MAX_LINE_SIZE];
+ char fname[MAX_FILENAME_SIZE];
+
+ if (parray[p].last_file)
+ rfree(parray[p].last_file);
+ parray[p].last_file = NULL;
+ parray[p].last_file_byte = 0L;
+
+ if (dir)
+ sprintf(fname, "%s/%s", dir, file);
+ else
+ strcpy(fname, file);
+ fp = fopen(fname, "r");
+ if (!fp)
+ return -1;
+ while (!feof(fp)) {
+ fgets(tmp, MAX_LINE_SIZE - 1, fp);
+ if (!feof(fp)) {
+ net_send_string(parray[p].socket, tmp, 1);
+ }
+ }
+
+ fclose(fp);
+ return 0;
+}
+
+PUBLIC int pmore_file(int p)
+{
+ FILE *fp;
+ char tmp[MAX_LINE_SIZE];
+ int lcount = parray[p].d_height - 1;
+
+ if (!parray[p].last_file) {
+ pprintf(p, "There is no more.\n");
+ return -1;
+ }
+ fp = fopen(parray[p].last_file, "r");
+ if (!fp) {
+ pprintf(p, "File not found!\n");
+ return -1;
+ }
+ fseek(fp, parray[p].last_file_byte, SEEK_SET);
+
+ while (!feof(fp) && (--lcount > 0)) {
+ fgets(tmp, MAX_LINE_SIZE - 1, fp);
+ if (!feof(fp)) {
+ net_send_string(parray[p].socket, tmp, 1);
+ }
+ }
+ if (!feof(fp)) {
+ parray[p].last_file_byte = ftell(fp);
+ pprintf(p, "Type [next] to see next page.\n");
+ } else {
+ rfree(parray[p].last_file);
+ parray[p].last_file = NULL;
+ parray[p].last_file_byte = 0L;
+ }
+ fclose(fp);
+ return 0;
+}
+
+PUBLIC int psend_command(int p, char *command, char *input)
+{
+ FILE *fp;
+ char tmp[MAX_LINE_SIZE];
+ int num;
+
+ if (input)
+ fp = popen(command, "w");
+ else
+ fp = popen(command, "r");
+ if (!fp)
+ return -1;
+ if (input) {
+ fwrite(input, sizeof(char), strlen(input), fp);
+ } else {
+ while (!feof(fp)) {
+ num = fread(tmp, sizeof(char), MAX_LINE_SIZE - 1, fp);
+ tmp[num] = '\0';
+ net_send_string(parray[p].socket, tmp, 1);
+ }
+ }
+ pclose(fp);
+ return 0;
+}
+
+PUBLIC char *stolower(char *str)
+{
+ int i;
+
+ if (!str)
+ return NULL;
+ for (i = 0; str[i]; i++) {
+ if (isupper(str[i])) {
+ str[i] = tolower(str[i]);
+ }
+ }
+ return str;
+}
+
+PUBLIC int safechar(int c)
+{
+ if ((c == '>') || (c == '!') || (c == '&') || (c == '*') || (c == '?') ||
+ (c == '/') || (c == '<') || (c == '|') || (c == '`') || (c == '$'))
+ return 0;
+ return 1;
+}
+
+PUBLIC int safestring(char *str)
+{
+ int i;
+
+ if (!str)
+ return 1;
+ for (i = 0; str[i]; i++) {
+ if (!safechar(str[i]))
+ return 0;
+ }
+ return 1;
+}
+
+PUBLIC int alphastring(char *str)
+{
+ int i;
+
+ if (!str)
+ return 1;
+ for (i = 0; str[i]; i++) {
+ if (!isalpha(str[i])) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+PUBLIC int printablestring(char *str)
+{
+ int i;
+
+ if (!str)
+ return 1;
+ for (i = 0; str[i]; i++) {
+ if ((!isprint(str[i])) && (str[i] != '\t') && (str[i] != '\n'))
+ return 0;
+ }
+ return 1;
+}
+
+
+/*#if defined(SGI)
+#else*/
+/* This version of strdup must be used, or the count of allocs
+ in "uptime" will omit them. Maybe we change the name to
+ rstrdup and replace all calls, if the name is causing a conflict
+ with anyone's libraries. --mann
+*/
+PUBLIC char *strdup(const char *str)
+{
+ char *tmp;
+
+ if (!str)
+ return NULL;
+ tmp = rmalloc(strlen(str) + 1);
+ strcpy(tmp, str);
+ return tmp;
+}
+/*#endif*/
+
+PUBLIC char *hms(int t, int showhour, int showseconds, int spaces)
+{
+ static char tstr[20];
+ char tmp[10];
+ int h, m, s;
+
+ h = t / 3600;
+ t = t % 3600;
+ m = t / 60;
+ s = t % 60;
+ if (h || showhour) {
+ if (spaces)
+ sprintf(tstr, "%d : %02d", h, m);
+ else
+ sprintf(tstr, "%d:%02d", h, m);
+ } else {
+ sprintf(tstr, "%d", m);
+ }
+ if (showseconds) {
+ if (spaces)
+ sprintf(tmp, " : %02d", s);
+ else
+ sprintf(tmp, ":%02d", s);
+ strcat(tstr, tmp);
+ }
+ return tstr;
+}
+
+PRIVATE char *strtime(struct tm * stm)
+{
+ static char tstr[100];
+
+#if defined (SGI)
+ strftime(tstr, sizeof(tstr), "%a %b %e, %H:%M %Z", stm);
+#else
+ strftime(tstr, sizeof(tstr), "%a %b %e, %k:%M %Z", stm);
+#endif
+ return (tstr);
+}
+
+PUBLIC char *strltime(time_t *clock)
+{
+ struct tm *stm = localtime(clock);
+ return strtime(stm);
+}
+
+PUBLIC char *strgtime(time_t *clock)
+{
+ struct tm *stm = gmtime(clock);
+ return strtime(stm);
+}
+
+/* This is used only for relative timeing since it reports seconds since
+ * about 5:00pm on Feb 16, 1994
+ */
+PUBLIC unsigned tenth_secs(void)
+{
+ struct timeval tp;
+ struct timezone tzp;
+
+ gettimeofday(&tp, &tzp);
+/* .1 seconds since 1970 almost fills a 32 bit int! So lets subtract off
+ * the time right now */
+ return ((tp.tv_sec - 331939277) * 10L) + (tp.tv_usec / 100000);
+}
+
+/* This is to translate tenths-secs time back into 1/1/70 time in full
+ * seconds, because vek didn't read utils.c when he programmed new ratings.
+ 1 sec since 1970 fits into a 32 bit int OK.
+*/
+PUBLIC int untenths(unsigned tenths)
+{
+ return (tenths / 10 + 331939277 + 0xffffffff / 10 + 1);
+}
+
+PUBLIC char *tenth_str(unsigned t, int spaces)
+{
+ return hms((t + 5) / 10, 0, 1, spaces); /* Round it */
+}
+
+#define MAX_TRUNC_SIZE 100
+
+/* Warning, if lines in the file are greater than 1024 bytes in length, this
+ won't work! */
+/* nasty bug fixed by mann, 3-12-95 */
+PUBLIC int truncate_file(char *file, int lines)
+{
+ FILE *fp;
+ int bptr = 0, trunc = 0, i;
+ char tBuf[MAX_TRUNC_SIZE][MAX_LINE_SIZE];
+
+ if (lines > MAX_TRUNC_SIZE)
+ lines = MAX_TRUNC_SIZE;
+ fp = fopen(file, "r");
+ if (!fp)
+ return 1;
+ while (!feof(fp)) {
+ fgets(tBuf[bptr], MAX_LINE_SIZE, fp);
+ if (feof(fp))
+ break;
+ if (tBuf[bptr][strlen(tBuf[bptr]) - 1] != '\n') { /* Line too long */
+ fclose(fp);
+ return -1;
+ }
+ bptr++;
+ if (bptr == lines) {
+ trunc = 1;
+ bptr = 0;
+ }
+ }
+ fclose(fp);
+ if (trunc) {
+ fp = fopen(file, "w");
+ for (i = 0; i < lines; i++) {
+ fputs(tBuf[bptr], fp);
+ bptr++;
+ if (bptr == lines) {
+ bptr = 0;
+ }
+ }
+ fclose(fp);
+ }
+ return 0;
+}
+
+/* Warning, if lines in the file are greater than 1024 bytes in length, this
+ won't work! */
+PUBLIC int lines_file(char *file)
+{
+ FILE *fp;
+ int lcount = 0;
+ char tmp[MAX_LINE_SIZE];
+
+ fp = fopen(file, "r");
+ if (!fp)
+ return 0;
+ while (!feof(fp)) {
+ if (fgets(tmp, MAX_LINE_SIZE, fp))
+ lcount++;
+ }
+ fclose(fp);
+ return lcount;
+}
+
+PUBLIC int file_has_pname(char *fname, char *plogin)
+{
+ if (!strcmp(file_wplayer(fname), plogin))
+ return 1;
+ if (!strcmp(file_bplayer(fname), plogin))
+ return 1;
+ return 0;
+}
+
+PUBLIC char *file_wplayer(char *fname)
+{
+ static char tmp[MAX_FILENAME_SIZE];
+ char *ptr;
+ strcpy(tmp, fname);
+ ptr = rindex(tmp, '-');
+ if (!ptr)
+ return "";
+ *ptr = '\0';
+ return tmp;
+}
+
+PUBLIC char *file_bplayer(char *fname)
+{
+ char *ptr;
+
+ ptr = rindex(fname, '-');
+ if (!ptr)
+ return "";
+ return ptr + 1;
+}
+
+
+/* Hey, leave this code alone. "a" is always in network byte order,
+ which is big-endian, even on a little-endian machine. I fixed this
+ code to handle that correctly a while ago, and someone
+ gratuitiously changed it so that it would work correctly only on a
+ big-endian machine (like a Sun). I have now changed it back. --mann
+*/
+PUBLIC char *dotQuad(unsigned int a)
+{
+ static char tmp[20];
+ unsigned char *aa = (unsigned char *) &a;
+
+ sprintf(tmp, "%d.%d.%d.%d", aa[0], aa[1], aa[2], aa[3]);
+ return tmp;
+}
+
+PUBLIC int available_space(void)
+{
+#if defined(SYSTEM_NEXT)
+ struct statfs buf;
+
+ statfs(player_dir, &buf);
+ return ((buf.f_bsize * buf.f_bavail));
+#elif defined(SYSTEM_ULTRIX)
+ struct fs_data buf;
+
+ statfs(player_dir, &buf);
+ return ((1024 * buf.bfreen));
+#elif defined(SYSTEM_SUN4)
+/*
+ struct statfs buf;
+
+ statfs(player_dir, buf);
+ return (1024 * buf.f_bfree); */
+ return 100000000; /* Infinite space */
+#else
+ return 100000000; /* Infinite space */
+#endif
+}
+
+PUBLIC int file_exists(char *fname)
+{
+ FILE *fp;
+
+ fp = fopen(fname, "r");
+ if (!fp)
+ return 0;
+ fclose(fp);
+ return 1;
+}
+
+PUBLIC char *ratstr(int rat)
+{
+ static int on = 0;
+ static char tmp[20][10];
+
+ if (on == 20)
+ on = 0;
+ if (rat) {
+ sprintf(tmp[on], "%4d", rat);
+ } else {
+ sprintf(tmp[on], "----");
+
+ }
+ on++;
+ return tmp[on - 1];
+}
+
+PUBLIC char *ratstrii(int rat, int reg)
+{
+ static int on = 0;
+ static char tmp[20][10];
+
+ if (on == 20)
+ on = 0;
+ if (rat) {
+ sprintf(tmp[on], "%4d", rat);
+ } else {
+ if (reg) {
+ sprintf(tmp[on], "----");
+ } else {
+ sprintf(tmp[on], "++++");
+ }
+ }
+ on++;
+ return tmp[on - 1];
+}
+
+struct t_tree {
+ struct t_tree *left, *right;
+ char name; /* Not just 1 char - space for whole name */
+}; /* is allocated. Maybe a little cheesy? */
+
+struct t_dirs {
+ struct t_dirs *left, *right;
+ time_t mtime; /* dir's modification time */
+ struct t_tree *files;
+ char name; /* ditto */
+};
+
+PRIVATE char **t_buffer = NULL; /* pointer to next space in return buffer */
+PRIVATE int t_buffersize = 0; /* size of return buffer */
+
+/* fill t_buffer with anything matching "want*" in file tree t_tree */
+PRIVATE void t_sft(char *want, struct t_tree *t)
+{
+ if (t) {
+ int cmp = strncmp(want, &t->name, strlen(want));
+ if (cmp <= 0) /* if want <= this one, look left */
+ t_sft(want, t->left);
+ if (t_buffersize && (cmp == 0)) { /* if a match, add it to buffer */
+ t_buffersize--;
+ *t_buffer++ = &(t->name);
+ }
+ if (cmp >= 0) /* if want >= this one, look right */
+ t_sft(want, t->right);
+ }
+}
+
+/* delete file tree t_tree */
+PRIVATE void t_cft(struct t_tree **t)
+{
+ if (*t) {
+ t_cft(&(*t)->left);
+ t_cft(&(*t)->right);
+ rfree(*t);
+ *t = NULL;
+ }
+}
+
+/* make file tree for dir d */
+PRIVATE void t_mft(struct t_dirs *d)
+{
+ DIR *dirp;
+ struct dirent *dp;
+ struct t_tree **t;
+
+ if ((dirp = opendir(&(d->name))) == NULL) {
+ fprintf(stderr, "FICS:mft() couldn't opendir.\n");
+ } else {
+ while ((dp = readdir(dirp))) {
+ t = &d->files;
+ if (dp->d_name[0] != '.') { /* skip anything starting with . */
+ while (*t) {
+ if (strcmp(dp->d_name, &(*t)->name) < 0) {
+ t = &(*t)->left;
+ } else {
+ t = &(*t)->right;
+ }
+ }
+ *t = rmalloc(sizeof(struct t_tree) + strlen(dp->d_name));
+ (*t)->right = (*t)->left = NULL;
+ strcpy(&(*t)->name, dp->d_name);
+ }
+ }
+ closedir(dirp);
+ }
+}
+
+PUBLIC int search_directory(char *dir, char *filter, char **buffer, int buffersize)
+{
+/* dir = directory to search
+ filter = what to search for
+ buffer = where to store pointers to matches
+ buffersize = how many pointers will fit inside buffer */
+
+ static struct t_dirs *ramdirs = NULL;
+ struct t_dirs **i;
+ int cmp;
+ static char nullify = '\0';
+ struct stat statbuf;
+
+ t_buffer = buffer;
+ t_buffersize = buffersize;
+
+ if (!stat(dir, &statbuf)) {
+ if (filter == NULL) /* NULL becomes pointer to null string */
+ filter = &nullify;
+ i = &ramdirs;
+ while (*i) { /* find dir in dir tree */
+ cmp = strcmp(dir, &(*i)->name);
+ if (cmp == 0)
+ break;
+ else if (cmp < 0)
+ i = &(*i)->left;
+ else
+ i = &(*i)->right;
+ }
+ if (!*i) { /* if dir isn't in dir tree, add him */
+ *i = rmalloc(sizeof(struct t_dirs) + strlen(dir));
+ (*i)->left = (*i)->right = NULL;
+ (*i)->files = NULL;
+ strcpy(&(*i)->name, dir);
+ }
+ if ((*i)->files) { /* delete any obsolete file tree */
+ if ((*i)->mtime != statbuf.st_mtime) {
+ t_cft(&(*i)->files);
+ }
+ }
+ if ((*i)->files == NULL) { /* if no file tree for him, make one */
+ (*i)->mtime = statbuf.st_mtime;
+ t_mft(*i);
+ }
+ t_sft(filter, (*i)->files); /* finally, search for matches */
+ }
+ return (buffersize - t_buffersize);
+}
+
+PUBLIC int display_directory(int p, char **buffer, int count)
+/* buffer contains 'count' string pointers */
+{
+ int i;
+ multicol *m = multicol_start(count);
+
+ for (i = 0; (i < count); i++)
+ multicol_store(m, *buffer++);
+ multicol_pprint(m, p, 78, 1);
+ multicol_end(m);
+ return (i);
+}
diff --git a/FICS/utils.h b/FICS/utils.h
new file mode 100644
index 0000000..901da93
--- /dev/null
+++ b/FICS/utils.h
@@ -0,0 +1,116 @@
+/* utils.h
+ *
+ */
+
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name email yy/mm/dd Change
+ Richard Nash 93/10/22 Created
+*/
+
+#ifndef _UTILS_H
+#define _UTILS_H
+
+#include <stdio.h>
+#include "multicol.h"
+
+#define MAX_WORD_SIZE 1024
+
+/* Maximum length of an output line */
+#define MAX_LINE_SIZE 1024
+
+/* Maximum size of a filename */
+#ifdef FILENAME_MAX
+# define MAX_FILENAME_SIZE FILENAME_MAX
+#else
+# define MAX_FILENAME_SIZE 1024
+#endif
+
+#define SetFlag(VAR, FLAG) (VAR |= (FLAG))
+#define ClearFlag(VAR, FLAG) (VAR &= ~(FLAG))
+#define CheckFlag(VAR, FLAG) (VAR & (FLAG))
+
+extern int count_lines(FILE *);
+extern int iswhitespace( );
+extern char *getword( );
+/* Returns a pointer to the first whitespace in the argument */
+extern char *eatword( );
+/* Returns a pointer to the first non-whitespace char in the argument */
+extern char *eatwhite( );
+/* Returns a pointer to the same string with trailing spaces removed */
+extern char *eattailwhite( );
+/* Returns the next word in a given string >eatwhite(eatword(foo))< */
+extern char *nextword( );
+
+extern int check_emailaddr(char *);
+extern int mail_string_to_address();
+extern int mail_string_to_user();
+extern int mail_file_to_address();
+extern int mail_file_to_user();
+extern int pcommand(int, char *, ...);
+extern int pprintf(int, char *, ...);
+extern void pprintf_dohightlight(int);
+extern void sprintf_dohightlight(int,char *);
+extern int pprintf_highlight(int, char *, ...);
+extern int psprintf_highlight(int, char *, char *, ...);
+extern int pprintf_prompt(int, char *, ...);
+extern int pprintf_noformat(int, char *, ...);
+extern int psend_raw_file( );
+extern int psend_file( );
+extern int psend_logoutfile( );
+extern int pmore_file( );
+extern int pmail_file( );
+extern int psend_command( );
+extern char *fix_time(char *);
+extern char *stolower( );
+
+extern int safechar( );
+extern int safestring( );
+extern int alphastring( );
+extern int printablestring( );
+#if defined(SGI)
+#else
+/* extern char *strdup( ); */
+#endif
+
+extern char *hms_desc();
+extern char *hms();
+extern char *strltime();
+extern char *strgtime();
+extern unsigned tenth_secs();
+extern char *tenth_str();
+extern int untenths();
+
+extern int truncate_file();
+extern int lines_file();
+
+extern int file_has_pname();
+extern char *file_wplayer();
+extern char *file_bplayer();
+
+extern char *dotQuad();
+
+extern int available_space();
+extern int file_exists();
+extern char *ratstr();
+extern char *ratstrii();
+
+extern int search_directory(char *, char *, char **, int);
+extern int display_directory(int, char **, int);
+
+#endif /* _UTILS_H */
+
diff --git a/FICS/utils.h.orig b/FICS/utils.h.orig
new file mode 100644
index 0000000..68f042f
--- /dev/null
+++ b/FICS/utils.h.orig
@@ -0,0 +1,115 @@
+/* utils.h
+ *
+ */
+
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name email yy/mm/dd Change
+ Richard Nash 93/10/22 Created
+*/
+
+#ifndef _UTILS_H
+#define _UTILS_H
+
+#include <stdio.h>
+#include "multicol.h"
+
+#define MAX_WORD_SIZE 1024
+
+/* Maximum length of an output line */
+#define MAX_LINE_SIZE 1024
+
+/* Maximum size of a filename */
+#ifdef FILENAME_MAX
+# define MAX_FILENAME_SIZE FILENAME_MAX
+#else
+# define MAX_FILENAME_SIZE 1024
+#endif
+
+#define SetFlag(VAR, FLAG) (VAR |= (FLAG))
+#define ClearFlag(VAR, FLAG) (VAR &= ~(FLAG))
+#define CheckFlag(VAR, FLAG) (VAR & (FLAG))
+
+extern int count_lines(FILE *);
+extern int iswhitespace( );
+extern char *getword( );
+/* Returns a pointer to the first whitespace in the argument */
+extern char *eatword( );
+/* Returns a pointer to the first non-whitespace char in the argument */
+extern char *eatwhite( );
+/* Returns a pointer to the same string with trailing spaces removed */
+extern char *eattailwhite( );
+/* Returns the next word in a given string >eatwhite(eatword(foo))< */
+extern char *nextword( );
+
+extern int check_emailaddr(char *);
+extern int mail_string_to_address();
+extern int mail_string_to_user();
+extern int mail_file_to_address();
+extern int mail_file_to_user();
+extern int pcommand(int, char *, ...);
+extern int pprintf(int, char *, ...);
+extern void pprintf_dohightlight(int);
+extern void sprintf_dohightlight(int,char *);
+extern int pprintf_highlight(int, char *, ...);
+extern int psprintf_highlight(int, char *, char *, ...);
+extern int pprintf_prompt(int, char *, ...);
+extern int pprintf_noformat(int, char *, ...);
+extern int psend_raw_file( );
+extern int psend_file( );
+extern int psend_logoutfile( );
+extern int pmore_file( );
+extern int pmail_file( );
+extern int psend_command( );
+
+extern char *stolower( );
+
+extern int safechar( );
+extern int safestring( );
+extern int alphastring( );
+extern int printablestring( );
+#if defined(SGI)
+#else
+/* extern char *strdup( ); */
+#endif
+
+extern char *hms();
+extern char *strltime();
+extern char *strgtime();
+extern unsigned tenth_secs();
+extern char *tenth_str();
+extern int untenths();
+
+extern int truncate_file();
+extern int lines_file();
+
+extern int file_has_pname();
+extern char *file_wplayer();
+extern char *file_bplayer();
+
+extern char *dotQuad();
+
+extern int available_space();
+extern int file_exists();
+extern char *ratstr();
+extern char *ratstrii();
+
+extern int search_directory(char *, char *, char **, int);
+extern int display_directory(int, char **, int);
+
+#endif /* _UTILS_H */
+
diff --git a/FICS/variable.c b/FICS/variable.c
new file mode 100644
index 0000000..b53664d
--- /dev/null
+++ b/FICS/variable.c
@@ -0,0 +1,899 @@
+/* variable.c
+ * This is cludgy
+ */
+
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name email yy/mm/dd Change
+ Richard Nash 93/10/22 Created
+ loon 95/03/17 added figure_boolean, use it :)
+ hersco 95/04/10 replaced figure_boolean with
+ set_boolean_var
+ DAV 95/19/11 moved variable command to here
+ added jprivate
+*/
+
+#include "stdinclude.h"
+
+#include "common.h"
+#include "variable.h"
+#include "playerdb.h"
+#include "utils.h"
+#include "ficsmain.h"
+#include "config.h"
+#include "rmalloc.h"
+#include "board.h"
+#include "command.h"
+#include "talkproc.h"
+#include "comproc.h"
+
+/* grimm */
+int SetValidFormula(int p, int which, char *val);
+/* added for warning */
+PRIVATE int set_boolean_var(int *var, char *val)
+{
+ int v = -1;
+
+ if (val == NULL)
+ return (*var = !*var);
+
+ if (sscanf(val, "%d", &v) != 1) {
+ stolower(val);
+ if (!strcmp(val, "off"))
+ v = 0;
+ if (!strcmp(val, "false"))
+ v = 0;
+ if (!strcmp(val, "on"))
+ v = 1;
+ if (!strcmp(val, "true"))
+ v = 1;
+ }
+ if ((v == 0) || (v == 1))
+ return (*var = v);
+ else
+ return (-1);
+}
+
+PRIVATE int set_open(int p, char *var, char *val)
+{
+ int v = set_boolean_var(&parray[p].open, val);
+
+ if (v < 0)
+ return VAR_BADVAL;
+ if (v > 0)
+ pprintf(p, "You are now open to receive match requests.\n");
+ else {
+ player_decline_offers(p, -1, PEND_MATCH);
+ player_withdraw_offers(p, -1, PEND_MATCH);
+ pprintf(p, "You are no longer receiving match requests.\n");
+ }
+ return VAR_OK;
+}
+
+PRIVATE int set_sopen(int p, char *var, char *val)
+{
+ int v = set_boolean_var(&parray[p].sopen, val);
+
+ if (v < 0)
+ return VAR_BADVAL;
+ pprintf(p, "sopen set to %d.\n", parray[p].sopen);
+
+ if (v > 0)
+ pprintf(p, "You are now open to receive simul requests.\n");
+ else
+ pprintf(p, "You are no longer receiving simul requests.\n");
+ player_decline_offers(p, -1, PEND_SIMUL);
+ return VAR_OK;
+}
+
+PRIVATE int set_ropen(int p, char *var, char *val)
+{
+ if (set_boolean_var(&parray[p].ropen, val) < 0)
+ return VAR_BADVAL;
+ pprintf(p, "ropen set to %d.\n", parray[p].ropen);
+ return VAR_OK;
+}
+
+PRIVATE int set_rated(int p, char *var, char *val)
+{
+ if (!parray[p].registered) {
+ pprintf(p, "You cannot change your rated status.\n");
+ return VAR_OK;
+ }
+ if (set_boolean_var(&parray[p].rated, val) < 0)
+ return VAR_BADVAL;
+ pprintf(p, "rated set to %d.\n", parray[p].rated);
+ return VAR_OK;
+}
+
+PRIVATE int set_shout(int p, char *var, char *val)
+{
+ if (set_boolean_var(&parray[p].i_shout, val) < 0)
+ return VAR_BADVAL;
+ if (parray[p].i_shout)
+ pprintf(p, "You will now hear shouts.\n");
+ else
+ pprintf(p, "You will not hear shouts.\n");
+ return VAR_OK;
+}
+
+PRIVATE int set_cshout(int p, char *var, char *val)
+{
+ if (set_boolean_var(&parray[p].i_cshout, val) < 0)
+ return VAR_BADVAL;
+ if (parray[p].i_cshout)
+ pprintf(p, "You will now hear cshouts.\n");
+ else
+ pprintf(p, "You will not hear cshouts.\n");
+ return VAR_OK;
+}
+
+PRIVATE int set_kibitz(int p, char *var, char *val)
+{
+ if (set_boolean_var(&parray[p].i_kibitz, val) < 0)
+ return VAR_BADVAL;
+ if (parray[p].i_kibitz)
+ pprintf(p, "You will now hear kibitzes.\n");
+ else
+ pprintf(p, "You will not hear kibitzes.\n");
+ return VAR_OK;
+}
+PRIVATE int set_kiblevel(int p, char *var, char *val)
+{
+ int v = -1;
+
+ if (!val)
+ return VAR_BADVAL;
+ if (sscanf(val, "%d", &v) != 1)
+ return VAR_BADVAL;
+ if ((v < 0) || (v > 9999))
+ return VAR_BADVAL;
+ parray[p].kiblevel = v;
+ pprintf(p, "Kibitz level now set to: %d.\n", v);
+ return VAR_OK;
+}
+
+PRIVATE int set_tell(int p, char *var, char *val)
+{
+ if (set_boolean_var(&parray[p].i_tell, val) < 0)
+ return VAR_BADVAL;
+ if (parray[p].i_tell)
+ pprintf(p, "You will now hear tells.\n");
+ else
+ pprintf(p, "You will not hear tells.\n");
+ return VAR_OK;
+}
+
+PRIVATE int set_notifiedby(int p, char *var, char *val)
+{
+ if (set_boolean_var(&parray[p].notifiedby, val) < 0)
+ return VAR_BADVAL;
+ if (parray[p].notifiedby)
+ pprintf(p, "You will now hear if people notify you, but you don't notify them.\n");
+ else
+ pprintf(p, "You will not hear if people notify you, but you don't notify them.\n");
+ return VAR_OK;
+}
+
+PRIVATE int set_pinform(int p, char *var, char *val)
+{
+ if (set_boolean_var(&parray[p].i_login, val) < 0)
+ return VAR_BADVAL;
+ if (parray[p].i_login)
+ pprintf(p, "You will now hear logins/logouts.\n");
+ else
+ pprintf(p, "You will not hear logins/logouts.\n");
+ return VAR_OK;
+}
+
+PRIVATE int set_ginform(int p, char *var, char *val)
+{
+ if (set_boolean_var(&parray[p].i_game, val) < 0)
+ return VAR_BADVAL;
+ if (parray[p].i_game)
+ pprintf(p, "You will now hear game results.\n");
+ else
+ pprintf(p, "You will not hear game results.\n");
+ return VAR_OK;
+}
+
+PRIVATE int set_private(int p, char *var, char *val)
+{
+ if (set_boolean_var(&parray[p].private, val) < 0)
+ return VAR_BADVAL;
+ if (parray[p].private)
+ pprintf(p, "Your games will be private.\n");
+ else
+ pprintf(p, "Your games may not be private.\n");
+ return VAR_OK;
+}
+
+PRIVATE int set_jprivate(int p, char *var, char *val)
+{
+ if (!parray[p].registered) {
+ pprintf(p, "Unregistered players may not keep a journal.\n");
+ return VAR_OK;
+ }
+
+ if (set_boolean_var(&parray[p].jprivate, val) < 0)
+ return VAR_BADVAL;
+ if (parray[p].jprivate)
+ pprintf(p, "Your journal will be private.\n");
+ else
+ pprintf(p, "Your journal will not be private.\n");
+ return VAR_OK;
+}
+
+PRIVATE int set_automail(int p, char *var, char *val)
+{
+ if (set_boolean_var(&parray[p].automail, val) < 0)
+ return VAR_BADVAL;
+ if (parray[p].automail)
+ pprintf(p, "Your games will be mailed to you.\n");
+ else
+ pprintf(p, "Your games will not be mailed to you.\n");
+ return VAR_OK;
+}
+
+PRIVATE int set_mailmess(int p, char *var, char *val)
+{
+ if (!parray[p].registered) {
+ pprintf(p, "Unregistered players may not receive messages.\n");
+ return VAR_OK;
+ }
+ if (set_boolean_var(&parray[p].i_mailmess, val) < 0)
+ return VAR_BADVAL;
+ if (parray[p].i_mailmess)
+ pprintf(p, "Your messages will be mailed to you.\n");
+ else
+ pprintf(p, "Your messages will not be mailed to you.\n");
+ return VAR_OK;
+}
+
+PRIVATE int set_pgn(int p, char *var, char *val)
+{
+ if (set_boolean_var(&parray[p].pgn, val) < 0)
+ return VAR_BADVAL;
+ if (parray[p].pgn)
+ pprintf(p, "Games will now be mailed to you in PGN.\n");
+ else
+ pprintf(p, "Games will now be mailed to you in FICS format.\n");
+ return VAR_OK;
+}
+
+PRIVATE int set_bell(int p, char *var, char *val)
+{
+ if (set_boolean_var(&parray[p].bell, val) < 0)
+ return VAR_BADVAL;
+ if (parray[p].bell)
+ pprintf(p, "Bell on.\n");
+ else
+ pprintf(p, "Bell off.\n");
+ return VAR_OK;
+}
+
+PRIVATE int set_highlight(int p, char *var, char *val)
+{
+/* if (set_boolean_var (&parray[p].highlight, val) < 0) return VAR_BADVAL;
+ */
+ int v = -1;
+
+ if (!val)
+ return VAR_BADVAL;
+ if (sscanf(val, "%d", &v) != 1)
+ return VAR_BADVAL;
+ if ((v < 0) || (v > 15))
+ return VAR_BADVAL;
+
+ if ((parray[p].highlight = v)) {
+ pprintf(p, "Highlight is now style ");
+ pprintf_highlight(p, "%d", v);
+ pprintf(p, ".\n");
+ } else
+ pprintf(p, "Highlight is off.\n");
+ return VAR_OK;
+}
+
+PRIVATE int set_style(int p, char *var, char *val)
+{
+ int v = -1;
+
+ if (!val)
+ return VAR_BADVAL;
+ if (sscanf(val, "%d", &v) != 1)
+ return VAR_BADVAL;
+ if ((v < 1) || (v > MAX_STYLES))
+ return VAR_BADVAL;
+ parray[p].style = v - 1;
+ pprintf(p, "Style %d set.\n", v);
+ return VAR_OK;
+}
+
+PRIVATE int set_flip(int p, char *var, char *val)
+{
+ if (set_boolean_var(&parray[p].flip, val) < 0)
+ return VAR_BADVAL;
+ if (parray[p].flip)
+ pprintf(p, "Flip on.\n");
+ else
+ pprintf(p, "Flip off.\n");
+ return VAR_OK;
+}
+/*
+PRIVATE int set_uscf(int p, char *var, char *val)
+{
+ int v = -1;
+
+ if (!val)
+ return VAR_BADVAL;
+ if (sscanf(val, "%d", &v) != 1)
+ return VAR_BADVAL;
+ if ((v < 0) || (v > 3000))
+ return VAR_BADVAL;
+ parray[p].uscfRating = v;
+ pprintf(p, "USCF Rating set to %d.\n", v);
+ return VAR_OK;
+}
+*/
+PRIVATE int set_time(int p, char *var, char *val)
+{
+ int v = -1;
+
+ if (!val)
+ return VAR_BADVAL;
+ if (sscanf(val, "%d", &v) != 1)
+ return VAR_BADVAL;
+ if ((v < 0) || (v > 240))
+ return VAR_BADVAL;
+ parray[p].d_time = v;
+ pprintf(p, "Default time set to %d.\n", v);
+ return VAR_OK;
+}
+
+PRIVATE int set_inc(int p, char *var, char *val)
+{
+ int v = -1;
+
+ if (!val)
+ return VAR_BADVAL;
+ if (sscanf(val, "%d", &v) != 1)
+ return VAR_BADVAL;
+ if ((v < 0) || (v > 300))
+ return VAR_BADVAL;
+ parray[p].d_inc = v;
+ pprintf(p, "Default increment set to %d.\n", v);
+ return VAR_OK;
+}
+
+PRIVATE int set_height(int p, char *var, char *val)
+{
+ int v = -1;
+
+ if (!val)
+ return VAR_BADVAL;
+ if (sscanf(val, "%d", &v) != 1)
+ return VAR_BADVAL;
+ if ((v < 5) || (v > 240))
+ return VAR_BADVAL;
+ parray[p].d_height = v;
+ pprintf(p, "Height set to %d.\n", v);
+ return VAR_OK;
+}
+
+PRIVATE int set_width(int p, char *var, char *val)
+{
+ int v = -1;
+
+ if (!val)
+ return VAR_BADVAL;
+ if (sscanf(val, "%d", &v) != 1)
+ return VAR_BADVAL;
+ if ((v < 32) || (v > 240))
+ return VAR_BADVAL;
+ parray[p].d_width = v;
+ pprintf(p, "Width set to %d.\n", v);
+ return VAR_OK;
+}
+
+PUBLIC char *Language(int i)
+{
+ static char *Lang[NUM_LANGS] = {"English", "Spanish", "French", "Danish"};
+ return Lang[i];
+}
+
+PRIVATE int set_language (int p, char *var, char *val)
+{
+ int i, len, gotIt = -1;
+
+ if (!val)
+ return VAR_BADVAL;
+ len = strlen(val);
+ for (i=0; i < NUM_LANGS; i++) {
+ if (strncasecmp(val, Language(i), len))
+ continue;
+ if (gotIt >= 0)
+ return VAR_BADVAL;
+ else gotIt = i;
+ }
+ if (gotIt < 0)
+ return VAR_BADVAL;
+ parray[p].language = gotIt;
+ pprintf(p, "Language set to %s.\n", Language(gotIt));
+ return VAR_OK;
+}
+
+PRIVATE int set_promote(int p, char *var, char *val)
+{
+ if (!val)
+ return VAR_BADVAL;
+ stolower(val);
+ switch (val[0]) {
+ case 'q':
+ parray[p].promote = QUEEN;
+ pprintf(p, "Promotion piece set to QUEEN.\n");
+ break;
+ case 'r':
+ parray[p].promote = ROOK;
+ pprintf(p, "Promotion piece set to ROOK.\n");
+ break;
+ case 'b':
+ parray[p].promote = BISHOP;
+ pprintf(p, "Promotion piece set to BISHOP.\n");
+ break;
+ case 'n':
+ case 'k':
+ parray[p].promote = KNIGHT;
+ pprintf(p, "Promotion piece set to KNIGHT.\n");
+ break;
+ default:
+ return VAR_BADVAL;
+ }
+ return VAR_OK;
+}
+
+PRIVATE int set_prompt(int p, char *var, char *val)
+{
+ if (!val) {
+ if (parray[p].prompt && (parray[p].prompt != def_prompt))
+ rfree(parray[p].prompt);
+ parray[p].prompt = def_prompt;
+ return VAR_OK;
+ }
+ if (!printablestring(val))
+ return VAR_BADVAL;
+ if (parray[p].prompt != def_prompt)
+ rfree(parray[p].prompt);
+ parray[p].prompt = (char *) rmalloc(strlen(val) + 2);
+ strcpy(parray[p].prompt, val);
+ strcat(parray[p].prompt, " ");
+ return VAR_OK;
+}
+
+int RePartner (int p, int new)
+{
+ int pOld;
+
+ if (p < 0)
+ return -1;
+ pOld = parray[p].partner;
+ if (parray[pOld].partner == p) {
+ if (new >= 0)
+ pprintf_prompt (pOld, "Your partner has just chosen a new partner.\n");
+ else
+ pprintf_prompt (pOld, "Your partner has just unset his/her partner.\n");
+ player_withdraw_offers (pOld, -1, PEND_BUGHOUSE);
+ player_decline_offers (pOld, -1, PEND_BUGHOUSE);
+ player_withdraw_offers (p, -1, PEND_BUGHOUSE);
+ player_decline_offers (p, -1, PEND_BUGHOUSE);
+ }
+ player_withdraw_offers(p, -1, PEND_PARTNER);
+ player_decline_offers(p, -1, PEND_PARTNER);
+ parray[pOld].partner = -1;
+ parray[p].partner = new;
+ return new;
+}
+
+PUBLIC int com_partner(int p, param_list param)
+{
+ int pNew;
+
+ if (param[0].type == TYPE_NULL) {
+ RePartner(p, -1);
+ return COM_OK;
+ }
+ /* OK, we're trying to set a new partner. */
+ pNew = player_find_part_login(param[0].val.word);
+ if (pNew < 0 || parray[pNew].status == PLAYER_PASSWORD
+ || parray[pNew].status == PLAYER_LOGIN) {
+ pprintf(p, "No user named \"%s\" is logged in.\n", param[0].val.word);
+ return COM_OK;
+ }
+ if (pNew == p) {
+ pprintf(p, "You can't be your own bughouse partner.\n");
+ return COM_OK;
+ }
+ /* Now we know a legit partner has been chosen. Is an offer pending? */
+ if (player_find_pendfrom(p, pNew, PEND_PARTNER) >= 0) {
+ pprintf (p, "You agree to be %s's partner.\n", parray[pNew].name);
+ pprintf_prompt (pNew, "%s agrees to be your partner.\n", parray[p].name);
+ player_remove_request(pNew, p, PEND_PARTNER);
+
+ /* Make the switch. */
+ RePartner (p, pNew);
+ RePartner (pNew, p);
+ return COM_OK;
+ }
+ /* This is just an offer. Make sure a new partner is needed. */
+ if (parray[pNew].partner >= 0) {
+ pprintf(p, "%s already has a partner.\n", parray[pNew].name);
+ return COM_OK;
+ }
+ pprintf(pNew, "\n");
+ pprintf_highlight(pNew, "%s", parray[p].name);
+ pprintf(pNew, " offers to be your bughouse partner; ");
+ pprintf_prompt(pNew, "type \"partner %s\" to accept.\n", parray[p].name);
+ pprintf(p, "Making a partnership offer to %s.\n", parray[pNew].name);
+ player_add_request(p, pNew, PEND_PARTNER, 0);
+
+/* pprintf (p, "Command not implemented yet.\n"); */
+ return COM_OK;
+}
+
+PRIVATE int set_partner(int p, char *var, char *val)
+{
+#if 0
+ int p1 = parray[p].partner;
+
+ if (!val) {
+ RePartner (p, -1);
+ return VAR_OK;
+ }
+ if (!printablestring(val))
+ return VAR_BADVAL;
+ stolower(val);
+ p1 = player_find_part_login(val);
+ if (p1 < 0 || parray[p1].status == PLAYER_PASSWORD
+ || parray[p1].status == PLAYER_LOGIN) {
+ pprintf(p, "No user named \"%s\" is logged in.\n", val);
+ return VAR_OK;
+ }
+ if (p1 == p) {
+ pprintf(p, "You can't be your own bughouse partner!\n");
+ return VAR_OK;
+ }
+ parray[p].partner = p1;
+ pprintf(p, "Your bughouse partner is now %s.\n", parray[p1].name);
+ pprintf_prompt(p1, "%s just chose you for a bughouse partner.\n", parray[p].name);
+#endif
+ if (!val)
+ pprintf(p, "Command is obsolete; type \"partner\" to clear your partner\n");
+ else
+ pprintf(p, "Command is obsolete; type \"partner %s\" to change your partner\n", val);
+ return VAR_OK;
+}
+
+PRIVATE int set_busy(int p, char *var, char *val)
+{
+ if (!val) {
+ parray[p].busy[0] = '\0';
+ pprintf(p, "Your \"busy\" string was cleared.\n");
+ return VAR_OK;
+ }
+ if ((val) && (!printablestring(val)))
+ return VAR_BADVAL;
+ if (strlen(val) > 50) {
+ pprintf(p, "That string is too long.\n");
+ return VAR_BADVAL;
+ }
+ sprintf(parray[p].busy, "%s", val);
+ pprintf(p, "Your \"busy\" string was set to \" %s\"\n", parray[p].busy);
+ return VAR_OK;
+}
+
+/* this is really ugly */
+PRIVATE int set_plan(int p, char *var, char *val)
+{
+ int which;
+ int i;
+
+ if (val && !printablestring(val))
+ return VAR_BADVAL;
+ which = atoi(var); /* Must be an integer, no test needed */
+
+ if (which > MAX_PLAN)
+ return VAR_BADVAL;
+
+ if (which > parray[p].num_plan)
+ which = parray[p].num_plan + 1;
+
+ if (which == 0) { /* shove from top */
+ if (parray[p].num_plan >= MAX_PLAN) { /* free the bottom string */
+ if (parray[p].planLines[parray[p].num_plan - 1] != NULL)
+ rfree(parray[p].planLines[parray[p].num_plan - 1]);
+ }
+ if (parray[p].num_plan) {
+ for (i = (parray[p].num_plan >= MAX_PLAN) ? MAX_PLAN - 1 : parray[p].num_plan; i > 0; i--)
+ parray[p].planLines[i] = parray[p].planLines[i - 1];
+ }
+ if (parray[p].num_plan < MAX_PLAN)
+ parray[p].num_plan++;
+ parray[p].planLines[0] = ((val == NULL) ? NULL : strdup(val));
+ pprintf(p, "\nPlan variable %d changed to '%s'.\n", which+1, parray[p].planLines[which]);
+ pprintf(p, "All other variables moved down.\n");
+ return VAR_OK;
+ }
+ if (which > parray[p].num_plan) { /* new line at bottom */
+ if (parray[p].num_plan >= MAX_PLAN) { /* shove the old lines up */
+ if (parray[p].planLines[0] != NULL)
+ rfree(parray[p].planLines[0]);
+ for (i = 0; i < parray[p].num_plan; i++)
+ parray[p].planLines[i] = parray[p].planLines[i + 1];
+ } else {
+ parray[p].num_plan++;
+ }
+ parray[p].planLines[which - 1] = ((val == NULL) ? NULL : strdup(val));
+ pprintf(p, "\nPlan variable %d changed to '%s'.\n", which, parray[p].planLines[which-1]);
+ return VAR_OK;
+ }
+ which--;
+ if (parray[p].planLines[which] != NULL) {
+ rfree(parray[p].planLines[which]);
+ }
+ if (val != NULL) {
+ parray[p].planLines[which] = strdup(val);
+ pprintf(p, "\nPlan variable %d changed to '%s'.\n", which+1, parray[p].planLines[which]);
+ } else {
+ parray[p].planLines[which] = NULL;
+ if (which == parray[p].num_plan - 1) { /* clear nulls from bottom */
+ while ((parray[p].num_plan > 0) && (parray[p].planLines[parray[p].num_plan - 1] == NULL)) {
+ parray[p].num_plan--;
+ pprintf(p, "\nPlan variable %d cleared.\n", which+1);
+ }
+ } else if (which == 0) { /* clear nulls from top */
+ while ((which < parray[p].num_plan) && (parray[p].planLines[which] == NULL)) {
+ which++;
+ }
+ if (which != parray[p].num_plan) {
+ for (i = which; i < parray[p].num_plan; i++)
+ parray[p].planLines[i - which] = parray[p].planLines[i];
+ }
+ parray[p].num_plan -= which;
+ }
+ }
+ return VAR_OK;
+}
+
+PRIVATE int set_formula(int p, char *var, char *val)
+{
+ int which;
+ player *me = &parray[p];
+
+#ifdef NO_FORMULAS
+ pprintf(p, "Sorry -- not available because of a bug\n");
+ return COM_OK;
+#else
+ if (isdigit(var[1]))
+ which = var[1] - '1';
+ else
+ which = MAX_FORMULA;
+
+ if (val != NULL) {
+ val = eatwhite(val);
+ if (val[0] == '\0')
+ val = NULL;
+ }
+ if (!SetValidFormula(p, which, val))
+ return VAR_BADVAL;
+
+ if (which < MAX_FORMULA) {
+ if (val != NULL) {
+ while (me->num_formula < which) {
+ me->formulaLines[me->num_formula] = NULL;
+ (me->num_formula)++;
+ }
+ if (me->num_formula <= which)
+ me->num_formula = which + 1;
+ pprintf(p, "Formula variable f%d set to %s.\n",
+ which + 1, me->formulaLines[which]);
+ return VAR_OK;
+ }
+ pprintf(p, "Formula variable f%d unset.\n", which + 1);
+ if (which + 1 >= me->num_formula) {
+ while (which >= 0 && me->formulaLines[which] == NULL)
+ which--;
+ me->num_formula = which + 1;
+ }
+ } else {
+ if (me->formula != NULL)
+ pprintf(p, "Formula set to %s.\n", me->formula);
+ else
+ pprintf(p, "Formula unset.\n");
+ }
+ return VAR_OK;
+#endif
+}
+
+PUBLIC var_list variables[] = {
+ {"open", set_open},
+ {"simopen", set_sopen},
+ {"rated", set_rated},
+ {"ropen", set_ropen},
+ {"cshout", set_cshout},
+ {"shout", set_shout},
+ {"kibitz", set_kibitz},
+ {"kiblevel", set_kiblevel},
+ {"tell", set_tell},
+ {"notifiedby", set_notifiedby},
+ {"pinform", set_pinform},
+ {"i_login", set_pinform},
+ {"ginform", set_ginform},
+ {"i_game", set_ginform},
+ {"automail", set_automail},
+ {"mailmess", set_mailmess},
+ {"bell", set_bell},
+ {"pgn", set_pgn},
+ {"private", set_private},
+ {"jprivate", set_jprivate},
+ {"highlight", set_highlight},
+ {"style", set_style},
+ {"flip", set_flip},
+/* {"uscf", set_uscf},*/
+ {"prompt", set_prompt},
+ {"partner", set_partner},
+ {"promote", set_promote},
+ {"time", set_time},
+ {"inc", set_inc},
+ {"height", set_height},
+ {"width", set_width},
+ {"language", set_language},
+ {"busy", set_busy},
+ {"0", set_plan},
+ {"1", set_plan},
+ {"2", set_plan},
+ {"3", set_plan},
+ {"4", set_plan},
+ {"5", set_plan},
+ {"6", set_plan},
+ {"7", set_plan},
+ {"8", set_plan},
+ {"9", set_plan},
+ {"10", set_plan},
+ {"f1", set_formula},
+ {"f2", set_formula},
+ {"f3", set_formula},
+ {"f4", set_formula},
+ {"f5", set_formula},
+ {"f6", set_formula},
+ {"f7", set_formula},
+ {"f8", set_formula},
+ {"f9", set_formula},
+ {"formula", set_formula},
+ {NULL, NULL}
+};
+
+PRIVATE int set_find(char *var)
+{
+ int i = 0;
+ int gotIt = -1;
+ int len = strlen(var);
+
+ while (variables[i].name) {
+ if (!strncmp(variables[i].name, var, len)) {
+ if (len == strlen(variables[i].name)) {
+ return i;
+ } else if (gotIt >= 0) {
+ return -VAR_AMBIGUOUS;
+ }
+ gotIt = i;
+ }
+ i++;
+ }
+ if (gotIt >= 0) {
+ return gotIt;
+ }
+ return -VAR_NOSUCH;
+}
+
+PUBLIC int var_set(int p, char *var, char *val, int *wh)
+{
+ int which;
+
+ if (!var)
+ return VAR_NOSUCH;
+ if ((which = set_find(var)) < 0) {
+ return -which;
+ }
+ *wh = which;
+ return variables[which].var_func(p, (isdigit(*variables[which].name) ? var : variables[which].name), val);
+}
+
+PUBLIC int com_variables(int p, param_list param)
+{
+ int p1, connected;
+ int i;
+
+ if (param[0].type == TYPE_WORD) {
+ if (!FindPlayer(p, param[0].val.word, &p1, &connected))
+ return COM_OK;
+ } else {
+ p1 = p;
+ connected = 1;
+ }
+
+ pprintf(p, "Variable settings of %s:\n", parray[p1].name);
+/* if (parray[p1].fullName)
+ pprintf(p, " Realname: %s\n", parray[p1].fullName);
+ if (parray[p1].uscfRating)
+ pprintf(p, " USCF: %d\n", parray[p1].uscfRating);
+*/
+ pprintf(p, " time=%-3d inc=%-3d private=%d jprivate=%d Lang=%s\n",
+ parray[p1].d_time, parray[p1].d_inc, parray[p1].private,
+ parray[p1].jprivate, Language(parray[p1].language));
+ pprintf(p, " rated=%d ropen=%d open=%d simopen=%d\n",
+ parray[p1].rated, parray[p1].ropen, parray[p1].open, parray[p1].sopen);
+ pprintf(p, " shout=%d cshout=%d kib=%d tell=%d notifiedby=%d\n",
+ parray[p1].i_shout, parray[p1].i_cshout, parray[p1].i_kibitz, parray[p1].i_tell, parray[p1].notifiedby);
+ pprintf(p, " pin=%d gin=%d style=%-3d flip=%d kiblevel=%d\n",
+ parray[p1].i_login, parray[p1].i_game, parray[p1].style + 1, parray[p1].flip, parray[p1].kiblevel);
+ pprintf(p, " highlight=%d bell=%d auto=%d mailmess=%d pgn=%d\n",
+ parray[p1].highlight, parray[p1].bell, parray[p1].automail, parray[p1].i_mailmess, parray[p1].pgn);
+ pprintf(p, " width=%-3d height=%-3d\n",
+ parray[p1].d_width, parray[p1].d_height);
+ if (parray[p1].prompt && parray[p1].prompt != def_prompt)
+ pprintf(p, " Prompt: %s\n", parray[p1].prompt);
+ if (parray[p1].partner >= 0)
+ pprintf(p, " Bughouse partner: %s\n", parray[parray[p1].partner].name);
+
+#if 0
+ { /* added code to print channels */
+ int count = 0;
+ for (i = 0; i < MAX_CHANNELS; i++) {
+ if (on_channel(i, p1)) {
+ if (!count)
+ pprintf(p, "\n Channels:");
+ pprintf(p, " %d", i);
+ count++;
+ }
+ }
+ if (count)
+ pprintf(p, "\n");
+ }
+#endif /* Now use in [name] */
+/* if (parray[p1].numAlias && (p == p1)) {
+ pprintf(p, "\n Aliases:\n");
+ for (i = 0; i < parray[p1].numAlias; i++) {
+ pprintf(p, " %s %s\n", parray[p1].alias_list[i].comm_name,
+ parray[p1].alias_list[i].alias);
+ }
+ }
+*/
+ if (parray[p1].num_formula) {
+ pprintf(p, "\n");
+ for (i = 0; i < parray[p1].num_formula; i++) {
+ if (parray[p1].formulaLines[i] != NULL)
+ pprintf(p, " f%d: %s\n", i + 1, parray[p1].formulaLines[i]);
+ else
+ pprintf(p, " f%d:\n", i + 1);
+ }
+ }
+ if (parray[p1].formula != NULL)
+ pprintf(p, "\nFormula: %s\n", parray[p1].formula);
+
+ if (!connected)
+ player_remove(p1);
+ return COM_OK;
+}
diff --git a/FICS/variable.h b/FICS/variable.h
new file mode 100644
index 0000000..41e5a67
--- /dev/null
+++ b/FICS/variable.h
@@ -0,0 +1,56 @@
+/* variable.h
+ *
+ */
+
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name email yy/mm/dd Change
+ Richard Nash 93/10/22 Created
+*/
+
+#ifndef _VARIABLE_H
+#define _VARIABLE_H
+
+typedef struct _var_list {
+ char *name;
+ int (*var_func)();
+} var_list;
+
+#define VAR_OK 0
+#define VAR_NOSUCH 1
+#define VAR_BADVAL 2
+#define VAR_AMBIGUOUS 3
+
+#define LANG_ENGLISH 0
+#define LANG_SPANISH 1
+#define LANG_FRENCH 2
+#define LANG_DANISH 3
+#define NUM_LANGS 4
+
+extern int var_set(int, char *, char *, int *);
+extern int com_time();
+extern int com_inc();
+extern int com_height();
+extern int com_width();
+extern int com_partner();
+extern char *Language();
+
+extern var_list variables[];
+
+extern int com_variables();
+
+#endif /* _VARIABLE_H */
diff --git a/FICS/vers.h b/FICS/vers.h
new file mode 100644
index 0000000..df005ac
--- /dev/null
+++ b/FICS/vers.h
@@ -0,0 +1,31 @@
+/* vers.h
+ */
+
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name email yy/mm/dd Change
+ Richard Nash 93/10/22 Created
+*/
+
+#ifndef _VERS_H
+#define _VERS_H
+
+extern char SGS_VERS[];
+extern char VERS_NUM[];
+extern char COMP_DATE[];
+
+#endif /* _VERS_H */
diff --git a/FICS/vers.h.orig b/FICS/vers.h.orig
new file mode 100644
index 0000000..df005ac
--- /dev/null
+++ b/FICS/vers.h.orig
@@ -0,0 +1,31 @@
+/* vers.h
+ */
+
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name email yy/mm/dd Change
+ Richard Nash 93/10/22 Created
+*/
+
+#ifndef _VERS_H
+#define _VERS_H
+
+extern char SGS_VERS[];
+extern char VERS_NUM[];
+extern char COMP_DATE[];
+
+#endif /* _VERS_H */