2 ## (configure will change the top line to the location of perl on your system)
3 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
4 ## cmail: a tool to aid playing chess by email
5 ## Copyright (C) 1993,2009, 2010, 2011, 2012, 2013 Free Software Foundation, Inc.
7 ## cmail is free software: you can redistribute it and/or modify
8 ## it under the terms of the GNU General Public License as published by
9 ## the Free Software Foundation, either version 3 of the License, or (at
10 ## your option) any later version.
12 ## cmail is distributed in the hope that it will be useful, but
13 ## WITHOUT ANY WARRANTY; without even the implied warranty of
14 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 ## General Public License for more details.
17 ## You should have received a copy of the GNU General Public License
18 ## along with this program. If not, see http://www.gnu.org/licenses/. *
20 ## Email: evan@quadstone.co.uk
21 ## Snailmail: Evan Welsh
26 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
29 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
30 ## Print verbose diagnostics for debugging
33 local ($old) = select ; ## Remember selected output
35 $| = 1 ; ## Keep it flushed
36 print @_ ; ## Print arguments
40 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
43 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
44 ## Create a directory for storing games in if it doesn't already exist
48 ## ################################################################# ##
49 ## Check for existence of the named chess directory
50 ## ################################################################# ##
52 if (! (-d "$CMAILDIR")) {
54 ## ############################################################# ##
55 ## Ask user for confirmation if attached to tty
56 ## ############################################################# ##
59 $old = select ; ## Remember selected output
60 select (stdout) ; ## Write to standard output
61 $| = 1 ; ## Keep it flushed
62 print ( "CMail directory \"$CMAILDIR\" does not exist."
63 . " Create it? [y/q]: ") ;
65 $_ = <tty> ; ## Read response from tty
66 die "Bye!\n" if (/^[qQ].*/) ; ## Quit if q selected
68 select ($old) ; ## Re-select the old output
71 ## ############################################################# ##
72 ## Create a cmail directory or die
73 ## ############################################################# ##
75 die "cmail: Can't create CMail directory: \"$CMAILDIR\"\n"
76 unless mkdir ("$CMAILDIR", 511) ;
77 print ( "Created cmail directory \"$CMAILDIR\".\n"
78 . "You can move it but remember to set the CMAIL_DIR"
79 . " environment variable.\n") ;
82 ## ################################################################# ##
83 ## Change to the $CMAILDIR directory whether newly created or not
84 ## ################################################################# ##
86 die "Couldn't changed directory to \"$CMAILDIR\"\n"
87 unless (chdir "$CMAILDIR") ;
89 ## ################################################################# ##
90 ## Check for existence of the named chess directory
91 ## ################################################################# ##
93 if (! (-d "$ARCDIR")) {
95 ## ############################################################# ##
96 ## Ask user for confirmation if attached to tty
97 ## ############################################################# ##
100 $old = select ; ## Remember selected output
101 select (stdout) ; ## Write to standard output
102 $| = 1 ; ## Keep it flushed
103 print ( "Archive directory \"$ARCDIR\" does not exist."
104 . " Create it? [y/q]: ") ;
106 $_ = <tty> ; ## Read response from tty
107 die "Bye!\n" if (/^[qQ].*/) ; ## Quit if q selected
109 select ($old) ; ## Re-select the old output
112 ## ############################################################# ##
113 ## Create a chess directory or die
114 ## ############################################################# ##
116 die "cmail: Can't create archive directory: \"$ARCDIR\"\n"
117 unless mkdir ("$ARCDIR", 511) ;
118 print ( "Created archive directory \"$ARCDIR\".\n"
119 . "You can move it but remember to set the CMAIL_ARCDIR"
120 . " environment variable.\n") ;
123 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
126 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
127 ## Parse command-line arguments
129 ## ################################################################# ##
130 ## Set up defaults from the environment or from hard-wired constants
131 ## ################################################################# ##
135 $OUTPUT_POS = $ENV{'CMAIL_OUTPUT_POS'} ;
136 $LOGFILE = $ENV{'CMAIL_LOGFILE'} ;
137 $MAILPROG = $ENV{'CMAIL_MAILPROG'} ;
138 $MAILPROG = "/usr/sbin/sendmail" if ( (-x "/usr/sbin/sendmail")
140 $MAILPROG = "/usr/lib/sendmail" if ( (-x "/usr/lib/sendmail")
142 $MAILPROG = "/etc/sendmail" if ( (-x "/usr/lib/sendmail")
144 $MAILPROG = "/usr/ucb/Mail" if ( (-x "/usr/ucb/Mail")
146 $MAILPROG = "/usr/ucb/mail" if ( (-x "/usr/ucb/mail")
148 $MAILPROG = "Mail" unless ($MAILPROG) ;
149 $HOMEDIR = $ENV{'HOME'} ;
150 $CMAILDIR = $ENV{'CMAIL_DIR'} ;
151 $CMAILDIR = $ENV{'CHESSDIR'} unless ($CMAILDIR) ;
152 $CMAILDIR = "$HOMEDIR/Chess" unless ($CMAILDIR) ;
153 $CMAILDIR = "~/Chess" unless ($HOMEDIR) ;
157 $TIME_DELAY = $ENV{'CMAIL_TIME_DELAY'} ;
158 $TIME_DELAY = 0 unless ($TIME_DELAY) ;
159 $PW_NAME = &get_pw_name () ;
160 $MY_NNAME = $PW_NAME ;
161 $MY_NNAME = $ENV{'LOGNAME'} unless ($MY_NNAME) ;
162 $MY_NNAME = $ENV{'USER'} unless ($MY_NNAME) ;
163 $MY_NNAME = "?" unless ($MY_NNAME) ;
164 $PGN_EVENT = "Email correspondence game" ;
170 $LOAD_XBOARD = 1 unless $ENV{'CMAIL_NO_XBOARD'} ;
172 @TD_FLAGS = ("-td", $TIME_DELAY) ;
173 @NCP_FLAGS = ("-ncp") ;
175 ## ################################################################# ##
176 ## Define the usage string
177 ## ################################################################# ##
180 [-h] [-c] [-w] [-[x]v] [-[x]mail] [-[x]xboard] [-[x]reuse] [-remail]
181 [-game <name>] [-(w|b|)games <number>] [-(me|opp) <short name>]
182 [-(w|b|my|opp)name <full name>] [-(w|b|my|opp)na <email>]
183 [-dir <directory>] [-arcdir <directory>] [-mailprog <mail program>]
184 [-logFile <file>] [-event <event>] [-site <site>] [-round <round>]
187 ## ################################################################# ##
188 ## Overwrite defaults if specified on the command-line
189 ## ################################################################# ##
192 while ($ARGV = shift) {
193 $UNREC = 0 if ($ARGV =~ /^-/) ;
194 if ("$ARGV" eq "-h") {die ("Usage: $USAGE\n") ;}
195 elsif ("$ARGV" eq "-c") {$SHOWC = 1 ;}
196 elsif ("$ARGV" eq "-w") {$SHOWW = 1 ;}
197 elsif ("$ARGV" eq "-v") {$DEBUG = 1 ;
198 @DEBUG_FLAGS = ("-debug") ;}
199 elsif ("$ARGV" eq "-xv") {$DEBUG = 0 ;
201 elsif ("$ARGV" eq "-mail") {$SEND_MAIL = 1 ;}
202 elsif ("$ARGV" eq "-xmail") {$SEND_MAIL = 0 ;}
203 elsif ("$ARGV" eq "-xboard") {$LOAD_XBOARD = 1 ;}
204 elsif ("$ARGV" eq "-xxboard") {$LOAD_XBOARD = 0 ;}
205 elsif ("$ARGV" eq "-reuse") {$REUSE = 1 ;}
206 elsif ("$ARGV" eq "-xreuse") {$REUSE = 0 ;}
207 elsif ("$ARGV" eq "-remail") {$LOAD_XBOARD = 0 ;
210 elsif ("$ARGV" eq "-game") {$PGN_GAME = shift ;}
211 elsif ("$ARGV" eq "-games") {$NUM_GAMES = shift ;}
212 elsif ("$ARGV" eq "-wgames") {$NUM_WGAMES = shift ;}
213 elsif ("$ARGV" eq "-bgames") {$NUM_BGAMES = shift ;}
214 elsif ("$ARGV" eq "-me") {$MY_NNAME = shift ;}
215 elsif ("$ARGV" eq "-opp") {$OPP_NNAME = shift ;}
216 elsif ("$ARGV" eq "-myname") {$MY_FNAME = shift ;}
217 elsif ("$ARGV" eq "-oppname") {$OPP_FNAME = shift ;}
218 elsif ("$ARGV" eq "-wname") {$WHITE_FNAME = shift ;}
219 elsif ("$ARGV" eq "-bname") {$BLACK_FNAME = shift ;}
220 elsif ("$ARGV" eq "-myna") {$MY_ADDRESS = shift ;}
221 elsif ("$ARGV" eq "-oppna") {$OPP_ADDRESS = shift ;}
222 elsif ("$ARGV" eq "-wna") {$WHITENA = shift ;}
223 elsif ("$ARGV" eq "-bna") {$BLACKNA = shift ;}
224 elsif ("$ARGV" eq "-dir") {$CMAILDIR = shift ;}
225 elsif ("$ARGV" eq "-arcdir") {$ARCDIR = shift ;}
226 elsif ("$ARGV" eq "-mailprog") {$MAILPROG = shift ;}
227 elsif ("$ARGV" eq "-logFile") {$LOGFILE = shift ;}
228 elsif ("$ARGV" =~ /^-(td|timeDelay)$/)
231 elsif ("$ARGV" =~ /^-noChessComputer$/)
232 {@NCP_FLAGS = ($ARGV,
234 elsif ("$ARGV" =~ /^-[x]?ncp$/) {@NCP_FLAGS = ($ARGV) ;}
235 elsif ("$ARGV" eq "-event") {$PGN_EVENT = shift ;}
236 elsif ("$ARGV" eq "-site") {$PGN_SITE = shift ;}
237 elsif ("$ARGV" eq "-round") {$PGN_ROUND = shift ;}
238 elsif ("$ARGV" eq "-mode") {$PGN_MODE = shift ;}
239 elsif ("$ARGV" =~ /^-/ || $UNREC) {
240 push(@UNREC_ARGS, $ARGV) ;
243 die("cmail: Unrecognised flag \"$ARGV\"\nUsage: $USAGE\n") ;
247 ## ################################################################# ##
248 ## Assign a value to $ARCDIR if not specified on the command line
249 ## ################################################################# ##
251 $ARCDIR = $ENV{'CMAIL_ARCDIR'} unless ($ARCDIR) ;
252 $ARCDIR = $CMAILDIR unless ($ARCDIR) ;
253 $ENV{'CMAIL_ARCDIR'} = $ARCDIR ; ## Make sure this is set for xboard
255 ## ################################################################# ##
256 ## Propagate some CMAIL variables through xboard to the cmail
257 ## grandchild so that it uses the same important variables as this one
258 ## ################################################################# ##
260 $ENV{'CMAIL_MAILPROG'} = $MAILPROG ;
261 $ENV{'CMAIL_DIR'} = $CMAILDIR ;
262 $ENV{'CHESSDIR'} = $CMAILDIR ; ## Make xboard use $CMAILDIR
263 $ENV{'CMAIL_ARCDIR'} = $ARCDIR ;
265 $ENV{'CMAIL_LOGFILE'} = $LOGFILE ;
267 $LOGFILE = "&STDERR" ;
270 ## ################################################################# ##
271 ## Work out how many games of each colour will be played
272 ## ################################################################# ##
274 die "cmail: Illegal number of games: $NUM_GAMES\n"
275 if ($NUM_GAMES < 0) ;
276 die "cmail: Illegal number of white games: $NUM_WGAMES\n"
277 if ($NUM_WGAMES < 0) ;
278 die "cmail: Illegal number of black games: $NUM_BGAMES\n"
279 if ($NUM_BGAMES < 0) ;
280 if ("$NUM_GAMES" ne "?") {
281 if ("$NUM_WGAMES" eq "?") {
282 if ("$NUM_BGAMES" eq "?") {
283 $NUM_BGAMES = int($NUM_GAMES / 2) ;
285 $NUM_WGAMES = $NUM_GAMES - $NUM_BGAMES ;
286 } elsif ("$NUM_BGAMES" eq "?") {
287 $NUM_BGAMES = $NUM_GAMES - $NUM_WGAMES ;
289 } elsif ("$NUM_WGAMES" eq "?") {
290 if ("$NUM_BGAMES" eq "?") {
295 $NUM_GAMES = $NUM_BGAMES ;
299 if ("$NUM_BGAMES" eq "?") {
300 $NUM_GAMES = $NUM_WGAMES ;
303 $NUM_GAMES = $NUM_WGAMES + $NUM_BGAMES ;
306 die "cmail: Illegal number of games: $NUM_GAMES\n"
307 if ("$NUM_GAMES" eq "0") ;
308 die ( "cmail: Inconsistent numbers of games specified:"
309 . " $NUM_WGAMES + $NUM_BGAMES != $NUM_GAMES\n")
310 unless ($NUM_GAMES == $NUM_WGAMES + $NUM_BGAMES) ;
312 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
315 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
316 ## Initialisation of variables and environment
318 ## ################################################################# ##
319 ## Show copyright notice
320 ## ################################################################# ##
323 last if (/^{END OF GPL COPYRIGHT}$/) ;
324 s/\$Revision[:] (.*) \$/$1/ ;
328 ## ################################################################# ##
329 ## Show conditions if requested
330 ## ################################################################# ##
333 last if (/^{END OF GPL CONDITIONS}$/) ;
337 ## ################################################################# ##
338 ## Show warranty if requested
339 ## ################################################################# ##
342 print "\n" if ($SHOWW) ;
343 print while (<DATA>) ;
347 exit 0 if ($SHOWC || $SHOWW) ; ## Abort if showed conditions or warranty
349 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
352 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
353 ## Initialisation of variables and environment
355 local ($p) = "[.PRNBQKprnbqk]" ;
356 local ($l) = "$p $p $p $p $p $p $p $p\n" ;
357 local ($board) = "$l$l$l$l$l$l$l$l" ;
358 local ($tp) = ".* to play\n" ;
359 $posdiag = "\{--------------\n$board$tp--------------\}\n+" ;
361 &parse_flags (@ARGV) ; ## Parse command-line arguments
363 &showGPL unless $QUIET ;
365 open (tty, "< /dev/tty") ; ## Open tty for reading
367 &need_chess_dir () ; ## Check for the existence of CMAILDIR
369 open (logfile, ">$LOGFILE") if ($DEBUG) ; ## Default is STDERR
371 &debug ("Called <initialise>\n") ;
373 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
376 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
377 ## Prompt for a game name, if <cr> use a default
378 sub prompt_for_game_name {
379 &debug ("Called <prompt_for_game_name>\n") ;
380 local ($old) = select ; ## Remember the selected output
381 select (stdout); ## Prompt goes to stdout
382 $| = 1 ; ## Keep it flushed
384 print "Game name [<cr> to use default]: " ;
385 die "cmail: tty not open\n" unless (-t) ;
386 <tty> =~ /(.*)/ ; ## Read line from tty
387 $PGN_GAME = "$1" ; ## Assign to game name
389 select ($old) ; ## Re-select the old output
391 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
394 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
395 ## Prompt for opponent's address
396 sub prompt_for_opp_address {
397 &debug ("Called <prompt_for_opp_address>\n") ;
398 local ($old) = select ; ## Remember the selected output
399 select (stdout); ## Prompt goes to stdout
400 $| = 1 ; ## Keep it flushed
402 ## ################################################################# ##
403 ## Prompt for opponent's email address
404 ## ################################################################# ##
406 print "Opponent's email address: " ;
407 die "cmail: tty not open\n" unless (-t) ;
411 ## ################################################################# ##
412 ## Use name as default if still blank
413 ## ################################################################# ##
415 $OPP_ADDRESS = $OPP_NNAME if ("" eq $OPP_ADDRESS) ;
417 select ($old) ; ## Re-select the old output
419 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
422 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
423 ## Prompt for opponent's name
424 sub prompt_for_opp_name {
425 &debug ("Called <prompt_for_opp_name>\n") ;
426 local ($old) = select ; ## Remember the selected output
427 select (stdout) ; ## Prompt goes to stdout
428 $| = 1 ; ## Keep it flushed
429 print "Opponent's name: " ;
431 die "cmail: tty not open\n" unless (-t) ; ## Check tty is open
432 <tty> =~ /(.*)/ ; ## Read line from tty
433 $OPP_NNAME = $1 ; ## Match!
434 die "cmail: Can't proceed without the opponent's name.\n"
435 unless ($OPP_NNAME) ;
437 select ($old) ; ## Re-select the old output
439 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
442 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
444 sub prompt_for_move {
445 local ($prompt) = shift ;
446 local ($pattern) = shift ;
447 &debug ("Called <prompt_for_move>\n") ;
450 local ($old) = select ; ## Remember the selected output
451 select (stdout) ; ## Write to stdout
452 $| = 1 ; ## Keep it flushed
453 die "cmail: tty not open\n" unless (-t) ; ## Check tty is open
457 <STDIN> =~ /(.*)/ ; ## Read line from tty
458 $move = $1 ; ## Match!
459 } until ($move =~ /^$pattern$/) ;
461 select ($old) ; ## Re-select the old output
464 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
467 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
470 &debug ("Called <play_game>\n") ;
472 $| = 1 ; ## Start flushing output buffer
474 ## ################################################################# ##
475 ## Load xboard unless inhibited by command-line arguments
476 ## ################################################################# ##
478 if (($STARTING_NEW_GAME) && ($NUM_WGAMES == 0)) {
479 print ( "Bypassing xboard and mailing $NUM_BGAMES empty"
480 . " black games.\n") ;
481 } elsif ($LOAD_XBOARD) {
482 ## ############################################################# ##
483 ## Remove output file from previous run, but preserve
484 ## $PGN_GAME.game.out.* because they will be empty black games
485 ## ############################################################# ##
487 unlink "$PGN_GAME.out" ;
489 ## ############################################################# ##
490 ## Invoke xboard with loads of flags
491 ## ############################################################# ##
495 local ($date) = &get_date_from_games (@ARCHIVE) ;
496 $XBOARD_ARGS = join (' ', (("-lgf",
497 "'$ARCDIR/$PGN_GAME.$date.archive'"),
504 $XBOARD_ARGS = join (' ', (("-cmail", "'$PGN_GAME'"),
512 $PGN_GAME = "unknown.cmail" ;
513 $XBOARD_ARGS = join (' ', (("-lgf", "'$PGN_GAME'"),
522 $LOG_FILE = "$PGN_GAME.log" ;
523 &debug ("Invoking xboard with args: $XBOARD_ARGS\n") ;
524 $PID_FILE = "$PGN_GAME.pid" ;
526 || (! ( (-f $PID_FILE)
527 && ($XBOARD_PID = `cat '$PID_FILE'`)
528 && ("$XBOARD_PID" =~ /^\d+$/)
529 && (kill "SIGUSR1", $XBOARD_PID)))) {
530 print "Loading xboard for game \"$PGN_GAME\"..." ;
531 # system ("gdb xboard") ;
532 system ( "{ ({ xboard $XBOARD_ARGS & } ;"
533 . " echo \$! > '$PID_FILE' ;"
535 . " rm '$PID_FILE') & } >'$LOG_FILE' 2>&1") ;
537 . "If nothing happens look for an error message in\n"
538 . "$CMAILDIR/$LOG_FILE\n") ;
540 print ("Revived existing xboard for game \"$PGN_GAME\".\n"
541 . "If nothing happens"
542 . " remove $CMAILDIR/$PID_FILE and try again.\n") ;
549 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
552 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
553 ## Enter moves on tty instead of xboard
555 &debug ("Called <play_on_tty>\n") ;
557 local (@results) = @_ ;
559 local (@games, $game, $to_play) ;
561 ## ################################################################# ##
562 ## Check we have access to tty
563 ## ################################################################# ##
565 if (open(STDIN, '/dev/tty')) {
566 ## ############################################################# ##
568 ## ############################################################# ##
570 local ($infile) = "$PGN_GAME.game.in" ;
572 @games = &get_games_from_file($infile) ;
573 &debug ("Read in games from \"$infile\"\n") ;
576 foreach $game (@games) {
577 $result = shift (@results) ;
578 if ($result && ($game =~ /\[Result\s+"[^*]+"\]/)) {
584 ## ################################################################# ##
585 ## Accept move on tty for each game in turn
586 ## ################################################################# ##
588 local ($comment_orig, $comment, $comment_line,
589 $pos, $move, $normal_move, $action, $result) ;
591 foreach $game (@games) {
594 $game =~ s/[\s\n]*[*]?[\s\n]*$// ;
596 $pos = $1 if ($game =~ s/($posdiag)//) ;
597 print $game, "\n\n", $pos ;
599 ($number, $to_play) = &get_to_play ($game) ;
602 $game_nocomment = $game ;
603 if ($game_nocomment =~ s/\n?{\n?([^{]*)}$//) {
605 $comment_orig =~ s/([^\n])$/$1\n/ ;
606 # &debug ("Comment is:\n{\n$comment_orig}\n") ;
608 if ($game =~ /\[Result\s+"[^*]+"\]/) {
609 &prompt_for_move ("Game finished, press \"Return\" to continue: ", "") ;
613 $tmpgame = $game_nocomment ;
614 $comment = $comment_orig ;
618 if ($game =~ /{\n?.* offers a draw\n?}$/) {
619 $move = &prompt_for_move ("Enter move [MOVE/(r)esign/(a)ccept/(c)omment/re(t)ry]: ",
620 "([-a-h0-9PRNBQK][-a-h0-9PRNBQK]+|[ract])") ;
622 $move = &prompt_for_move ("Enter move [MOVE/(r)esign/(c)omment/re(t)ry]: ",
623 "([-a-h0-9PRNBQK][-a-h0-9PRNBQK]+|[rct])") ;
627 if ($move =~ /^\s*r\s*$/i) {
628 if ($to_play eq "White") {
633 $move = "\n{$to_play resigns} $result" ;
634 } elsif ($move =~ /^\s*a\s*$/i) {
635 $move = "\n{Draw agreed} $result" ;
636 } elsif ($move =~ /^\s*c\s*$/i) {
637 while ($comment_line = &prompt_for_move ("Enter comment: ", ".*")) {
638 $comment .= $comment_line . "\n" ;
641 } elsif ($move =~ /^\s*t\s*$/i) {
642 print $game, "\n\n", $pos ;
643 $tmpgame = $game_nocomment ;
644 $comment = $comment_orig ;
645 print "Try again.\n" ;
651 $tmpgame .= "\n{\n" . $comment . "}" if ($comment) ;
652 if (! $normal_move) {
653 $tmpgame .= "$move" ;
654 } elsif ($to_play eq "White") {
655 $tmpgame .= "\n$number. $move" ;
656 } elsif ($tmpgame =~ /}$/) {
657 $tmpgame .= "\n$number. ... $move" ;
659 $tmpgame .= " $move" ;
662 $tmpgame =~ s/\[Result\s+"(.*)"\]/[Result "$result"]/ if ($result) ;
667 $action = &prompt_for_move ("Enter action [(d)raw/(c)omment/(s)end/re(t)ry]: ",
670 $action = &prompt_for_move ("Enter action [(s)end/re(t)ry]: ",
673 $action = &prompt_for_move ("Enter action [(c)omment/(s)end/re(t)ry]: ",
676 if ($action =~ /^\s*d\s*$/i) {
678 $comment .= "$to_play offers a draw\n" ;
681 $action = &prompt_for_move ("Enter action [(c)omment/(s)end/re(t)ry]: ",
683 if ($action =~ /^\s*c\s*$/i) {
684 while ($comment_line = &prompt_for_move ("Enter comment: ", ".*")) {
685 $comment .= $comment_line . "\n" ;
688 } elsif ($action =~ /^\s*t\s*$/i) {
689 print $game, "\n\n", $pos ;
690 $tmpgame = $game_nocomment ;
691 $comment = $comment_orig ;
692 print "Try again.\n" ;
694 } elsif ($action =~ /^\s*s\s*$/i) {
695 $tmpgame .= "\n{\n" . $comment . "}" ;
700 print "You can't offer a draw at this point.\n" ;
703 } elsif ($action =~ /^\s*c\s*$/i) {
705 print "You can't enter a comment after the game is finished.\n" ;
707 while ($comment_line = &prompt_for_move ("Enter comment: ", ".*")) {
708 $comment .= $comment_line . "\n" ;
712 } elsif ($action =~ /^\s*t\s*$/i) {
714 $tmpgame = $game_nocomment ;
715 $comment = $comment_orig ;
716 print "Try again.\n" ;
718 } elsif ($action =~ /^\s*s\s*$/i) {
719 $tmpgame .= "\n{\n" . $comment . "}\n" if ($comment) ;
727 $tmpgame .= "\n*" if ($normal_move) ;
732 die "cmail: No games to be read\n" ;
735 die "cmail: Can't open tty" ;
740 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
743 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
744 ## Find the game the user wants by any means possible
746 &debug ("Called <find_game>\n") ;
748 ## ################################################################# ##
749 ## Ask user for a game name if not already known
750 ## ################################################################# ##
752 &prompt_for_game_name () if ("" eq "$PGN_GAME") ;
754 ## ################################################################# ##
755 ## Find out opponent's details
756 ## ################################################################# ##
758 if ("" eq "$PGN_GAME") {
759 ## ############################################################# ##
760 ## Failed to find the game name so construct a default from players
761 ## ############################################################# ##
763 &prompt_for_opp_name () if ("" eq "$OPP_NNAME") ; ## Ask user
764 die "cmail: Can't proceed without your opponent's short name (-opp)\n"
765 if ("" eq "$OPP_NNAME") ;
766 die "cmail: Can't proceed without your own short name (-me)\n"
767 if ("" eq "$MY_NNAME") ;
768 if ($NUM_WGAMES > 0) {
769 $PGN_GAME = "$MY_NNAME-vs-$OPP_NNAME" ; ## Construct default
771 $PGN_GAME = "$OPP_NNAME-vs-$MY_NNAME" ; ## Construct default
773 } elsif (("" eq "$OPP_ADDRESS") && ("" ne "$RETURN_ADDRESS")) {
774 $OPP_ADDRESS = $RETURN_ADDRESS ; ## Use return address instead
775 &debug ( "Using return address \"$OPP_ADDRESS\""
776 . " for opponent address\n") ;
779 ## ################################################################# ##
780 ## If no $PGN_GAME.game.in file, assume we're starting a new game
781 ## ################################################################# ##
783 &start_new_game () unless (-f "$PGN_GAME.game.in") ;
785 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
788 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
791 local ($the_time) = time ;
792 local ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) =
793 localtime ($the_time) ;
804 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
807 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
810 print ( "Starting new game"
811 . " -- $NUM_WGAMES as white, $NUM_BGAMES as black.\n") ;
812 local ($to_play) = "white" ;
814 $STARTING_NEW_GAME = 1 ;
818 ## ################################################################# ##
819 ## Ask user for opponent's email address if unknown
820 ## ################################################################# ##
822 &prompt_for_opp_address () if ("" eq "$OPP_ADDRESS") ;
824 ## ################################################################# ##
825 ## Give up if we haven't got anywhere to send a move to
826 ## ################################################################# ##
828 die "cmail: Can't proceed without your opponent's email address.\n"
829 if ("" eq "$OPP_ADDRESS") ;
831 ## ################################################################# ##
832 ## Create an empty game file
833 ## ################################################################# ##
835 open (GAMEFILE, "> $PGN_GAME.game.in") ;
836 for ($j = 1; $j <= $NUM_GAMES; $j ++) {
837 $PW_GCOS = &get_pw_gcos () ;
839 $PGN_MYCOL = $MY_FNAME ;
840 $PGN_MYCOL = $PW_GCOS unless $PGN_MYCOL ;
841 $PGN_MYCOL = $MY_NNAME unless $PGN_MYCOL ;
842 $PGN_MYCOLNA = $MY_ADDRESS ;
843 $PGN_MYCOLNA = "?" unless ($PGN_MYCOLNA) ;
845 $PGN_OPPCOL = $OPP_FNAME ;
846 $PGN_OPPCOL = "?" unless ($PGN_OPPCOL) ;
847 $PGN_OPPCOLNA = $OPP_ADDRESS ;
848 $PGN_OPPCOLNA = "?" unless ($PGN_OPPCOLNA) ;
850 if ($j > $NUM_WGAMES) {
851 $PGN_WHITE = $PGN_OPPCOL ;
852 $PGN_BLACK = $PGN_MYCOL ;
853 $PGN_WHITENA = $PGN_OPPCOLNA ;
854 $PGN_BLACKNA = $PGN_MYCOLNA ;
856 $PGN_WHITE = $PGN_MYCOL ;
857 $PGN_BLACK = $PGN_OPPCOL ;
858 $PGN_WHITENA = $PGN_MYCOLNA ;
859 $PGN_BLACKNA = $PGN_OPPCOLNA ;
862 ## ######################################################### ##
863 ## If we only have one colour of game then allow command-line
864 ## colour specs to override
865 ## ######################################################### ##
867 if (! ($NUM_WGAMES && $NUM_BGAMES)) {
868 $PGN_WHITE = $WHITE_FNAME if ($WHITE_FNAME) ;
869 $PGN_BLACK = $BLACK_FNAME if ($BLACK_FNAME) ;
870 $PGN_WHITENA = $WHITENA if ($WHITENA) ;
871 $PGN_BLACKNA = $BLACKNA if ($BLACKNA) ;
874 $PGN_DATE = &get_date () ;
875 $PGN_DATE = "?" unless ($PGN_DATE) ;
877 if ($NUM_GAMES > 1) {
882 @game = ("[Event \"$PGN_EVENT\"]\n",
883 "[Site \"$PGN_SITE\"]\n",
884 "[Date \"$PGN_DATE\"]\n",
885 "[Round \"$PGN_ROUND\"]\n",
886 "[White \"$PGN_WHITE\"]\n",
887 "[Black \"$PGN_BLACK\"]\n",
889 "[WhiteNA \"$PGN_WHITENA\"]\n",
890 "[BlackNA \"$PGN_BLACKNA\"]\n",
891 "[Mode \"$PGN_MODE\"]\n",
892 "[CmailGameName \"$PGN_GAME$SUFFIX\"]\n\n*\n") ;
893 if ($j > $NUM_WGAMES) {
894 open (GAMEOUTFILE, "> $PGN_GAME.game.out.$j") ;
895 print GAMEOUTFILE @game ;
896 close (GAMEOUTFILE) ;
898 print GAMEFILE @game ;
903 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
906 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
907 ## Get the password file gcos (full name) entry
909 local ($name, $passwd, $uid, $gid, $quota, $comment, $gcos, $dir, $shell) =
913 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
916 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
917 ## Get password file gcos (full name) entry
921 ($dummy, $PW_GCOS) = &get_pw_entry () ;
922 $PW_GCOS =~ s/^\s*([^,()]+[^ ,()])[ ]*[,()].*$/$1/;
923 if ($PW_GCOS =~ /^([^,()]+)\s+([^\s,()]+)$/) { ## Multi-word name
924 $PW_GCOS = $2 . ", " . $1 ;
925 } elsif ($PW_GCOS !~ /^([^\s,()]+)/) { ## No sensible gcos entry
927 } ## Else leave it as one word
928 &debug ("PW full name is \"$PW_GCOS\"\n");
932 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
935 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
936 ## Get password file user name entry
939 ($PW_NAME, $dummy) = &get_pw_entry () ;
940 &debug ("PW name is $PW_NAME\n");
944 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
947 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
948 ## Analyse the email message
949 sub analyse_email_message {
950 local ($new_result, $delete_result, $unfinished, $finished)
952 local ($gamefile, @games) ;
954 ## ################################################################# ##
955 ## Slurp the mail message
956 ## ################################################################# ##
958 die "cmail: No games on standard input"
959 unless (@games = &get_games_from_file ("STDIN")) ;
961 ## ################################################################# ##
962 ## Is the message a real cmail message or just a chess game? $PGN_GAME
963 ## will have been set by calling get_games_from_file if it's real
964 ## ################################################################# ##
966 print "Processing game message..." ;
969 ## ############################################################# ##
970 ## Restore the results file from the archive if
971 ## necessary. This is helpful if the user pipes in an old
972 ## message for take-back purposes or whatever.
973 ## ############################################################# ##
975 if (! -f "$PGN_GAME.res") {
976 ## Find what date would have been used to create the archive
978 if (/\[Date\s"(.*)"\]/) {
980 last ; ## Assume all dates are the same
983 ## Restore results file from archive directory if it exists
984 if ($date && (-f "$ARCDIR/$PGN_GAME.$date.archive")) {
986 "$ARCDIR/$PGN_GAME.$date.archive",
988 print stderr ( "\nWarning: couldn't restore results file"
989 . " from archive\n") ;
991 print "restored results file from archive..." ;
996 ## ############################################################# ##
997 ## Find existing results, if any.
998 ## ############################################################# ##
1000 local (@results) = &get_games_from_file ("$PGN_GAME.res") ;
1002 ## ############################################################# ##
1004 ## ############################################################# ##
1006 foreach $game (@games) {
1007 next unless ($game) ;
1009 ($game_name, $game_num) = &get_game_name_and_number ($game) ;
1012 @game = split("\n", $game) ;
1014 if (/^\[(Black|White)\s*"[?]"\]$/) {
1016 $PW_GCOS = &get_pw_gcos () unless ($PW_GCOS) ;
1017 $PW_GCOS = "$MY_NNAME" unless ($PW_GCOS) ;
1018 s/".*"/"$PW_GCOS"/ ;
1019 &debug ("Changed $colour tag to be $_") ;
1020 } elsif (/^\[((Black|White)NA)\s*"(.*)"\]$/) {
1023 if ($RETURN_ADDRESS) {
1024 $NA = $RETURN_ADDRESS ;
1028 $_ = "[$1 \"$NA\"]" ;
1029 &debug ("Changed $1 tag.\n") ;
1031 if ($2 eq "White") {
1032 $PGN_WHITENA = $NA ;
1033 &debug ("WhiteNA tag is \"$PGN_WHITENA\"\n") ;
1035 $PGN_BLACKNA = $NA ;
1036 &debug ("BlackNA tag is \"$PGN_BLACKNA\"\n") ;
1038 } elsif (/\[Result\s*"(.*)"\]$/) {
1045 } elsif (/^(.*[^\d]+)?\d+[.]\s*([^\s*]*\s+)?[^\s.*]+(\s*\d+[.]\s*)?[\s*]*$/) {
1047 $to_play = "white" ;
1049 $to_play = "black" ;
1051 &debug ("$to_play to play\n") ;
1055 ## ######################################################### ##
1056 ## Reconstruct possibly edited game
1057 ## ######################################################### ##
1059 $game = join ("\n", @game) . "\n\n" ;
1061 ## ######################################################### ##
1062 ## Build up results array
1063 ## ######################################################### ##
1066 $results[$game_num] = $games[$game_num] ;
1068 } elsif ($results[$game_num]) {
1069 ## Deleting a result does actually make sense if the user
1070 ## pipes in an old message for take-back purposes or whatever
1071 $results[$game_num] = "" ;
1072 $delete_result = 1 ;
1075 ## ######################################################### ##
1076 ## Remove old .out files
1077 ## ######################################################### ##
1079 unlink <$PGN_GAME.game.out.*> ;
1081 ## ######################################################### ##
1082 ## Write ongoing games to game file and append new results
1083 ## ######################################################### ##
1085 die "cmail: Can't open file for writing:"
1086 . " \"$CMAILDIR/$PGN_GAME.game.in\"\n"
1087 unless open (gamefile, ">$PGN_GAME.game.in") ;
1089 print gamefile @games ;
1093 ## ############################################################# ##
1094 ## Print how many finished/unfinished games were found
1095 ## ############################################################# ##
1097 printf ("%d unfinished %s and %d finished %s...",
1098 $unfinished, ($unfinished == 1) ? "game" : "games",
1099 $finished, ($finished == 1) ? "game" : "games") ;
1101 ## ############################################################# ##
1102 ## Write results back to results file if there were any results
1104 ## ############################################################# ##
1106 if ($new_result || $delete_result) {
1107 die ( "cmail: Can't open results file for writing:"
1108 . "\"$CMAILDIR/$PGN_GAME.res\"\n")
1109 unless open (resfile, ">$PGN_GAME.res") ;
1110 print resfile @results ;
1114 ## ############################################################# ##
1115 ## Archive results if there are no unfinished games
1116 ## ############################################################# ##
1118 @ARCHIVE = @results unless ($unfinished) ;
1120 ## ############################################################# ##
1121 ## Figure out return address if not known
1122 ## ############################################################# ##
1124 if (! $RETURN_ADDRESS) {
1125 if ($to_play eq "black") {
1126 $RETURN_ADDRESS = $PGN_WHITENA unless ("$PGN_WHITENA" eq "?") ;
1128 $RETURN_ADDRESS = $PGN_BLACKNA unless ("$PGN_BLACKNA" eq "?") ;
1132 ## ############################################################# ##
1133 ## Decide to include position diagrams in output if not already
1134 ## decided and a position diagram was found in the input
1135 ## ############################################################# ##
1137 if ("$OUTPUT_POS" eq "") {
1138 if (grep (/$posdiag/, @games)) {
1139 $OUTPUT_POS = "y" ; # Output position only if it was input
1144 $ENV{'CMAIL_OUTPUT_POS'} = $OUTPUT_POS ;
1146 ## ############################################################# ##
1147 ## Check that we have enough info about the players to continue
1148 ## ############################################################# ##
1152 ## ############################################################# ##
1153 ## Set up xboard for viewing non-cmail PGN file
1154 ## ############################################################# ##
1156 local ($file) = "unknown.cmail" ;
1157 print "done.\nDumping non-cmail file into $CMAILDIR/$file..." ;
1159 die "cmail: Can't open file for writing: \"$CMAILDIR/$file\"\n"
1160 unless open (gamefile, ">$file") ;
1161 print gamefile @games ;
1167 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
1170 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
1171 ## Sort two filenames by the numeric suffix
1172 sub sort_by_numeric_suffix {
1173 $a =~ /[.](\d+)$/ ; local ($na) = $1 ;
1174 $b =~ /[.](\d+)$/ ; local ($nb) = $1 ;
1176 return ($na <=> $nb) ;
1178 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
1181 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
1182 sub get_game_name_and_number {
1183 local ($game) = join ("\n", @_) ;
1185 local ($game_name, $game_num) ;
1187 die "CMailGameName tag missing\n"
1188 unless ($game =~ /\[C[Mm]ailGameName\s+"(.*)"\]/) ;
1190 ## ################################################################# ##
1191 ## Set game name and number
1192 ## ################################################################# ##
1195 if ($game_name =~ s/^(.*)[.](\d+)$/$1/) {
1201 ## ################################################################# ##
1202 ## Set $PGN_GAME as a side-effect or check validity
1203 ## ################################################################# ##
1206 die ( "cmail: Mismatched game names in input message:\n"
1207 . "\"$PGN_GAME\", \"$game_name\"\n")
1208 if ("$PGN_GAME" ne "$game_name") ;
1210 $PGN_GAME = $game_name ;
1211 &debug ("PGN_GAME set to \"$PGN_GAME\"\n") ;
1214 return ($game_name, $game_num) ;
1216 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
1219 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
1220 ## Read in a file of games and split into separate games
1221 sub get_games_from_file {
1222 local ($file) = shift ;
1224 local (@file, $first_line) ; ## Slurp stdin
1225 if ($file eq "STDIN") {
1226 if ($first_line = <STDIN>) { ## Necessary to handle no input case
1227 @file = <STDIN> ; ## Slurp stdin
1228 @file = ($first_line, @file) ;
1231 ## Strip off leading quotation characters
1235 ## Find return address and set it as a side-effect
1236 if ( /^From:?.*<([^>]+)>.*\n$/
1237 || /^From:? *([^ ]*).*\n$/) {
1238 $RETURN_ADDRESS = $1 ; ## Default for opp's email
1239 &debug ("Found opponent's email address",
1240 " \"$RETURN_ADDRESS\"\n") ;
1241 } elsif (/\[C[Mm]ailGameName\s+"(.*)"\]/) {
1243 $PGN_GAME =~ s/[.]\d+$// ;
1247 return (@file) unless ($PGN_GAME) ;
1248 if (grep (/\{--------------|\[Event/, @file)) {
1249 shift (@file) while ($file[0] !~ /\{--------------|\[Event/) ;
1255 return () unless (open (file, "<$file")) ;
1257 @file = <file> ; ## Slurp file
1261 local (@games, $game_name, $game_num, $game, $tag) ;
1263 ## ################################################################# ##
1264 ## Remove headers and leading blanks
1265 ## ################################################################# ##
1267 local (@tgames) = split (/($posdiag\[Event|\[Event)/, join ('', @file)) ;
1268 shift (@tgames) while (!$tgames[0]) ;
1270 ## ################################################################# ##
1271 ## Set up @games array with proper game numbers
1272 ## ################################################################# ##
1275 $game = shift (@tgames) . shift (@tgames) ;
1277 ($game_name, $game_num) = &get_game_name_and_number ($game) ;
1278 $games[$game_num] = $game ;
1283 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
1286 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
1287 ## Analyse output files and send the move
1289 &debug ("Called <send_move>\n") ;
1291 ## ################################################################# ##
1292 ## Cat the pos (if it exists), game and result (if it exists) files
1293 ## into the .out file.
1294 ## ################################################################# ##
1296 local ($unfinished, $finished) = (0, 0) ;
1297 local (@results, $move, $move_msg, $to_play, $number) ;
1299 ## ################################################################# ##
1300 ## Find any .game.out.* files
1301 ## ################################################################# ##
1303 local (@outfiles) = (<$PGN_GAME.game.out.*>) ;
1304 @outfiles = grep (/[.]\d+$/, @outfiles) ; ## Ignore autosave files
1305 @outfiles = sort sort_by_numeric_suffix @outfiles ; ## Sort
1307 ## ################################################################# ##
1308 ## Find .res file if it exists
1309 ## ################################################################# ##
1311 local ($resfile) = "$PGN_GAME.res" ;
1313 @results = &get_games_from_file($resfile) ;
1314 &debug ("Read in results\n") ;
1317 &debug ("No results to read\n") ;
1320 ## ################################################################# ##
1321 ## Find .out file if it exists
1322 ## ################################################################# ##
1324 local ($outfile) = "$PGN_GAME.out" ;
1325 if (! ($REMAIL || $LOAD_XBOARD)) {
1326 @games = &play_on_tty (@results) ;
1327 } elsif (@outfiles) {
1328 foreach (@outfiles) {
1329 die "Can't open game file \"$_\" for reading"
1330 unless (open (game, "<$_")) ;
1331 die "Empty game file \"$_\""
1332 unless ($game = join ('', <game>)) ;
1334 &debug ("Read in game file \"$_\"\n") ;
1335 ## Remove position diagram if it wasn't in the input msg
1336 $game =~ s/($posdiag)// if ("$OUTPUT_POS" ne "y") ;
1337 ($game_name, $game_num) = &get_game_name_and_number ($game) ;
1338 $games[$game_num] = $game ;
1340 &debug ("Read in games from output files\n") ;
1343 &debug ("No games to read from STDIN\n") ;
1345 @games = &get_games_from_file($outfile) ;
1347 die "Can't find any game files\n" unless (@results) ;
1351 ## ################################################################# ##
1353 ## ################################################################# ##
1356 ## ############################################################# ##
1357 ## Find opponent's email address in games
1358 ## ############################################################# ##
1360 $OPP_ADDRESS = &get_opp_address_from_games (@games)
1361 unless ($OPP_ADDRESS) ;
1363 ## ############################################################# ##
1364 ## Collect the .game.out.* files into the .out file, remembering
1365 ## the move number of the last line and whether result or not
1366 ## ############################################################# ##
1372 ## Write games to output file
1373 die "Can't open output file \"$PGN_GAME.out\" for writing\n"
1374 unless open (outfile, ">$PGN_GAME.out") ;
1375 print outfile @games ;
1380 foreach $game (@games) {
1382 next unless ($game) ;
1385 ## Determine last move and whether result or not
1387 foreach (split(/\n/, $game)) {
1388 if (/^(.*[^\d]+|)(\d+)[.]+\s*[^\s.]*\s+(\S+)\s*$/) {
1391 } elsif (/^\[Result\s*"(.*)"\]$/) {
1401 $results[$game_num] = $game if ($result) ;
1404 ## Write result files back to $PGN_GAME.res
1406 die "Can't open results file $PGN_GAME.res for writing\n"
1407 unless open (resfile, ">$PGN_GAME.res") ;
1408 print resfile @results ;
1412 unlink <$PGN_GAME.game.out.*> ;
1414 ## ############################################################# ##
1415 ## Just say how many games are in the message
1416 ## ############################################################# ##
1418 if ($num_games > 1) {
1419 $move_msg = "$num_games games" ;
1421 $move_msg = "1 game" ;
1424 ## ############################################################# ##
1425 ## Print how many finished/unfinished games were found
1426 ## ############################################################# ##
1428 printf ("Sending %d unfinished %s and %d finished %s.\n",
1429 $unfinished, ($unfinished == 1) ? "game" : "games",
1430 $finished, ($finished == 1) ? "game" : "games") ;
1432 ## ############################################################# ##
1433 ## Send the mail message to opponent's address unless bypassed
1434 ## ############################################################# ##
1437 local ($subject) = "cmail $move_msg <$PGN_GAME>" ;
1438 if ($MAILPROG =~ /sendmail/) {
1439 $opened = open (mail, "|$MAILPROG $OPP_ADDRESS") ;
1440 print mail "To: $OPP_ADDRESS\n";
1441 print mail "Subject: $subject\n";
1442 print mail "Mime-Version: 1.0\n";
1443 print mail "Content-Type: application/x-chess;name=$PGN_GAME.pgn\n\n";
1445 $opened = open (mail, "|$MAILPROG -s \"$subject\" $OPP_ADDRESS") ;
1450 print ( "Mailed cmail message to \"$OPP_ADDRESS\":\n"
1451 . "$move_msg <$PGN_GAME>\n") ;
1452 @ARCHIVE = @results unless ($unfinished) ;
1454 die "Failed to mail cmail message.\n" ;
1457 print ( "Email not sent (as requested).\n"
1458 . "Would have mailed cmail message to \"$OPP_ADDRESS\":\n"
1459 . "$NUM_GAMES games <$PGN_GAME>\n") ;
1463 print "Email not sent (the game is over).\n" ;
1465 die "No games found\n" ;
1469 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
1472 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
1473 ## Get date from array of games
1474 sub get_date_from_games {
1475 local (@games) = @_ ;
1477 local ($date) = "nodate" ;
1480 if (/\[Date\s"(.*)"\]/) {
1482 last ; ## Assume all dates are the same
1488 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
1491 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
1492 ## Determine which colour is to play and the move number
1494 &debug ("Called <get_to_play>\n") ;
1495 local ($game) = shift ;
1497 local ($number, $to_play) = (1, "White") ;
1499 $game =~ s/{[^}]*}//g ;
1500 $game =~ s/[\s\n]*[*][\s\n]*$// ;
1502 if ($game =~ /(\d+)[.][\n ]*([.]*)[\n ]*([^\n.]*)[\n\s]*[10-]*[\n\s]*$/) {
1505 if ($game =~ /\[Result "0-1"\]/) {
1506 $to_play = "Black" ;
1507 } elsif ($game =~ /\[Result "1-0"\]/) {
1508 $to_play = "White" ;
1509 } elsif (($2 ne "") || (($3 =~ / /) || ($3 eq ""))) {
1510 $to_play = "White" ;
1513 $to_play = "Black" ;
1517 &debug ("$to_play to play on move $number\n") ;
1519 return ($number, $to_play) ;
1521 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
1524 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
1525 ## Get opp address from array of games
1526 sub get_opp_address_from_games {
1527 &debug ("Called <get_opp_address_from_games>\n") ;
1528 local (@games) = @_ ;
1530 local ($opp_address) = "" ;
1531 local ($number, $to_play, $tag) ;
1536 ($number, $to_play) = &get_to_play ($_) ;
1537 $tag = ( ($to_play eq "White")
1541 if (/\[$tag\s"(.*)"\]/) {
1543 die "cmail: Empty \"$tag\" tag\n" unless ($opp_address) ;
1544 &debug ("Found opponent's address \"$opp_address\" from games.\n") ;
1545 last ; ## Assume all opp addresses are the same
1547 die "cmail: Can't find \"$tag\" tag\n" ;
1551 die "cmail: Can't find opponent's email address\n" unless ($opp_address) ;
1553 return ($opp_address) ;
1555 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
1558 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
1559 ## Archive @ARCHIVE in the $ARCDIR directory
1561 return () unless (@ARCHIVE) ;
1563 local ($date) = &get_date_from_games (@ARCHIVE) ;
1565 local ($file) = "$ARCDIR/$PGN_GAME.$date.archive" ;
1566 if (open (archive, ">$file")) {
1567 print archive @ARCHIVE ;
1569 print "Archived game in $file\n" ;
1570 local (@remove) = <$PGN_GAME*> ;
1571 @remove = grep ($_ ne "$PGN_GAME.$date.archive",
1572 @remove) ; ## Don't delete archive
1575 print "Couldn't open \"$file\" to archive game\n" ;
1578 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
1581 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
1584 local (@games) = () ;
1586 &initialise () ; ## Initialise variables etc.
1588 if (-t || $REMAIL) { ## No input directed (invoked from a shell rather than a mailer)
1589 &debug ("Interactive!\n") ;
1590 &find_game () ; ## Get the necessary info about the game
1592 &debug ("Piping!\n") ;
1593 &analyse_email_message () ; ## Analyse the mail message
1596 if (! &play_game ()) { ## Load the game
1597 &send_move () ; ## Analyse output and send moves
1600 &archive () ; ## Archive games if all finished
1602 close (tty) ; ## Tidy up
1603 close (logfile) if ($DEBUG) ; ## Tidy up
1605 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
1607 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
1609 cmail (From XBoard version @PACKAGE_VERSION@), Copyright (C) 1993 Free Software Foundation, Inc.
1610 cmail comes with ABSOLUTELY NO WARRANTY; for details type `cmail -w'.
1611 cmail is free software, and you are welcome to redistribute it
1612 under certain conditions; type `cmail -c' for details.
1614 {END OF GPL COPYRIGHT}
1615 GNU GENERAL PUBLIC LICENSE
1616 TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
1618 0. This License applies to any program or other work which contains
1619 a notice placed by the copyright holder saying it may be distributed
1620 under the terms of this General Public License. The "Program", below,
1621 refers to any such program or work, and a "work based on the Program"
1622 means either the Program or any derivative work under copyright law:
1623 that is to say, a work containing the Program or a portion of it,
1624 either verbatim or with modifications and/or translated into another
1625 language. (Hereinafter, translation is included without limitation in
1626 the term "modification".) Each licensee is addressed as "you".
1628 Activities other than copying, distribution and modification are not
1629 covered by this License; they are outside its scope. The act of
1630 running the Program is not restricted, and the output from the Program
1631 is covered only if its contents constitute a work based on the
1632 Program (independent of having been made by running the Program).
1633 Whether that is true depends on what the Program does.
1635 1. You may copy and distribute verbatim copies of the Program's
1636 source code as you receive it, in any medium, provided that you
1637 conspicuously and appropriately publish on each copy an appropriate
1638 copyright notice and disclaimer of warranty; keep intact all the
1639 notices that refer to this License and to the absence of any warranty;
1640 and give any other recipients of the Program a copy of this License
1641 along with the Program.
1643 You may charge a fee for the physical act of transferring a copy, and
1644 you may at your option offer warranty protection in exchange for a fee.
1646 2. You may modify your copy or copies of the Program or any portion
1647 of it, thus forming a work based on the Program, and copy and
1648 distribute such modifications or work under the terms of Section 1
1649 above, provided that you also meet all of these conditions:
1651 a) You must cause the modified files to carry prominent notices
1652 stating that you changed the files and the date of any change.
1654 b) You must cause any work that you distribute or publish, that in
1655 whole or in part contains or is derived from the Program or any
1656 part thereof, to be licensed as a whole at no charge to all third
1657 parties under the terms of this License.
1659 c) If the modified program normally reads commands interactively
1660 when run, you must cause it, when started running for such
1661 interactive use in the most ordinary way, to print or display an
1662 announcement including an appropriate copyright notice and a
1663 notice that there is no warranty (or else, saying that you provide
1664 a warranty) and that users may redistribute the program under
1665 these conditions, and telling the user how to view a copy of this
1666 License. (Exception: if the Program itself is interactive but
1667 does not normally print such an announcement, your work based on
1668 the Program is not required to print an announcement.)
1670 These requirements apply to the modified work as a whole. If
1671 identifiable sections of that work are not derived from the Program,
1672 and can be reasonably considered independent and separate works in
1673 themselves, then this License, and its terms, do not apply to those
1674 sections when you distribute them as separate works. But when you
1675 distribute the same sections as part of a whole which is a work based
1676 on the Program, the distribution of the whole must be on the terms of
1677 this License, whose permissions for other licensees extend to the
1678 entire whole, and thus to each and every part regardless of who wrote it.
1680 Thus, it is not the intent of this section to claim rights or contest
1681 your rights to work written entirely by you; rather, the intent is to
1682 exercise the right to control the distribution of derivative or
1683 collective works based on the Program.
1685 In addition, mere aggregation of another work not based on the Program
1686 with the Program (or with a work based on the Program) on a volume of
1687 a storage or distribution medium does not bring the other work under
1688 the scope of this License.
1690 3. You may copy and distribute the Program (or a work based on it,
1691 under Section 2) in object code or executable form under the terms of
1692 Sections 1 and 2 above provided that you also do one of the following:
1694 a) Accompany it with the complete corresponding machine-readable
1695 source code, which must be distributed under the terms of Sections
1696 1 and 2 above on a medium customarily used for software interchange; or,
1698 b) Accompany it with a written offer, valid for at least three
1699 years, to give any third party, for a charge no more than your
1700 cost of physically performing source distribution, a complete
1701 machine-readable copy of the corresponding source code, to be
1702 distributed under the terms of Sections 1 and 2 above on a medium
1703 customarily used for software interchange; or,
1705 c) Accompany it with the information you received as to the offer
1706 to distribute corresponding source code. (This alternative is
1707 allowed only for noncommercial distribution and only if you
1708 received the program in object code or executable form with such
1709 an offer, in accord with Subsection b above.)
1711 The source code for a work means the preferred form of the work for
1712 making modifications to it. For an executable work, complete source
1713 code means all the source code for all modules it contains, plus any
1714 associated interface definition files, plus the scripts used to
1715 control compilation and installation of the executable. However, as a
1716 special exception, the source code distributed need not include
1717 anything that is normally distributed (in either source or binary
1718 form) with the major components (compiler, kernel, and so on) of the
1719 operating system on which the executable runs, unless that component
1720 itself accompanies the executable.
1722 If distribution of executable or object code is made by offering
1723 access to copy from a designated place, then offering equivalent
1724 access to copy the source code from the same place counts as
1725 distribution of the source code, even though third parties are not
1726 compelled to copy the source along with the object code.
1728 4. You may not copy, modify, sublicense, or distribute the Program
1729 except as expressly provided under this License. Any attempt
1730 otherwise to copy, modify, sublicense or distribute the Program is
1731 void, and will automatically terminate your rights under this License.
1732 However, parties who have received copies, or rights, from you under
1733 this License will not have their licenses terminated so long as such
1734 parties remain in full compliance.
1736 5. You are not required to accept this License, since you have not
1737 signed it. However, nothing else grants you permission to modify or
1738 distribute the Program or its derivative works. These actions are
1739 prohibited by law if you do not accept this License. Therefore, by
1740 modifying or distributing the Program (or any work based on the
1741 Program), you indicate your acceptance of this License to do so, and
1742 all its terms and conditions for copying, distributing or modifying
1743 the Program or works based on it.
1745 6. Each time you redistribute the Program (or any work based on the
1746 Program), the recipient automatically receives a license from the
1747 original licensor to copy, distribute or modify the Program subject to
1748 these terms and conditions. You may not impose any further
1749 restrictions on the recipients' exercise of the rights granted herein.
1750 You are not responsible for enforcing compliance by third parties to
1753 7. If, as a consequence of a court judgment or allegation of patent
1754 infringement or for any other reason (not limited to patent issues),
1755 conditions are imposed on you (whether by court order, agreement or
1756 otherwise) that contradict the conditions of this License, they do not
1757 excuse you from the conditions of this License. If you cannot
1758 distribute so as to satisfy simultaneously your obligations under this
1759 License and any other pertinent obligations, then as a consequence you
1760 may not distribute the Program at all. For example, if a patent
1761 license would not permit royalty-free redistribution of the Program by
1762 all those who receive copies directly or indirectly through you, then
1763 the only way you could satisfy both it and this License would be to
1764 refrain entirely from distribution of the Program.
1766 If any portion of this section is held invalid or unenforceable under
1767 any particular circumstance, the balance of the section is intended to
1768 apply and the section as a whole is intended to apply in other
1771 It is not the purpose of this section to induce you to infringe any
1772 patents or other property right claims or to contest validity of any
1773 such claims; this section has the sole purpose of protecting the
1774 integrity of the free software distribution system, which is
1775 implemented by public license practices. Many people have made
1776 generous contributions to the wide range of software distributed
1777 through that system in reliance on consistent application of that
1778 system; it is up to the author/donor to decide if he or she is willing
1779 to distribute software through any other system and a licensee cannot
1782 This section is intended to make thoroughly clear what is believed to
1783 be a consequence of the rest of this License.
1785 8. If the distribution and/or use of the Program is restricted in
1786 certain countries either by patents or by copyrighted interfaces, the
1787 original copyright holder who places the Program under this License
1788 may add an explicit geographical distribution limitation excluding
1789 those countries, so that distribution is permitted only in or among
1790 countries not thus excluded. In such case, this License incorporates
1791 the limitation as if written in the body of this License.
1793 9. The Free Software Foundation may publish revised and/or new versions
1794 of the General Public License from time to time. Such new versions will
1795 be similar in spirit to the present version, but may differ in detail to
1796 address new problems or concerns.
1798 Each version is given a distinguishing version number. If the Program
1799 specifies a version number of this License which applies to it and "any
1800 later version", you have the option of following the terms and conditions
1801 either of that version or of any later version published by the Free
1802 Software Foundation. If the Program does not specify a version number of
1803 this License, you may choose any version ever published by the Free Software
1806 10. If you wish to incorporate parts of the Program into other free
1807 programs whose distribution conditions are different, write to the author
1808 to ask for permission. For software which is copyrighted by the Free
1809 Software Foundation, write to the Free Software Foundation; we sometimes
1810 make exceptions for this. Our decision will be guided by the two goals
1811 of preserving the free status of all derivatives of our free software and
1812 of promoting the sharing and reuse of software generally.
1813 {END OF GPL CONDITIONS}
1814 GNU GENERAL PUBLIC LICENSE
1817 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
1818 FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
1819 OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
1820 PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
1821 OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
1822 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
1823 TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
1824 PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
1825 REPAIR OR CORRECTION.
1827 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
1828 WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
1829 REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
1830 INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
1831 OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
1832 TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
1833 YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
1834 PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
1835 POSSIBILITY OF SUCH DAMAGES.