diff options
author | Markus Uhlin <markus@nifty-networks.net> | 2023-12-07 21:31:49 +0100 |
---|---|---|
committer | Markus Uhlin <markus@nifty-networks.net> | 2023-12-07 21:31:49 +0100 |
commit | 79b59f9b30fb6a1fdf8c3efb446271f7cb00d434 (patch) | |
tree | f6ade4ccbc3af20d825edacfd12b5da8ded8d240 /FICS |
FICS 1.6.2
Diffstat (limited to 'FICS')
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, ¶meters))) + 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, + ®istered, 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, ¶m[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, ¶m[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, ¶m[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, + ®istered, 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, ¶m[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, ¶m[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, ¶m[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, + ®istered, 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, ¶m[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, ¶m[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, ¶m[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, ¶m[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(¤t_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(¤t_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, ¶m[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, ¶m[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, ¶m[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, ¶m[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, ¶m[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, ¶m[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, ¶m[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, ¶m[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, ¶m[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, ¶m[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, ¶m[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, ¶m[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, ¶m[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, ¶m[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, ¶m[0], &wp, &wconnected)) + return (COM_OK); + if (!FindPlayer(p, ¶m[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, ¶m[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, ¶m[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 Binary files differnew file mode 100644 index 0000000..2f2b945 --- /dev/null +++ b/FICS/gmon.out 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, ¶m[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, ¶m[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, ¶m[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, ¶m[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, ¶m[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, + ®istered, 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, ®istered, 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, ¶m[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, ¶m[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, ¶m[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 = ¶m[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 = ¶m[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 */ |