1#! @PERLPATH@ 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, 2014, 2015, 2016 Free Software Foundation, Inc. 6## 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. 11## 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. 16## 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/. * 19## 20## Email: evan@quadstone.co.uk 21## Snailmail: Evan Welsh 22## Quadstone Ltd 23## 16 Chester Street 24## Edinburgh EH3 7RA 25## Scotland 26#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 27 28 29#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 30## Print verbose diagnostics for debugging 31sub debug { 32 if ($DEBUG) { 33 local ($old) = select ; ## Remember selected output 34 select (logfile) ; 35 $| = 1 ; ## Keep it flushed 36 print @_ ; ## Print arguments 37 select ($old) ; 38 } 39} 40#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 41 42 43#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 44## Create a directory for storing games in if it doesn't already exist 45sub need_chess_dir { 46 local ($old) ; 47 48 ## ################################################################# ## 49 ## Check for existence of the named chess directory 50 ## ################################################################# ## 51 52 if (! (-d "$CMAILDIR")) { 53 54 ## ############################################################# ## 55 ## Ask user for confirmation if attached to tty 56 ## ############################################################# ## 57 58 if (-t) { 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]: ") ; 64 65 $_ = <tty> ; ## Read response from tty 66 die "Bye!\n" if (/^[qQ].*/) ; ## Quit if q selected 67 68 select ($old) ; ## Re-select the old output 69 } 70 71 ## ############################################################# ## 72 ## Create a cmail directory or die 73 ## ############################################################# ## 74 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") ; 80 } 81 82 ## ################################################################# ## 83 ## Change to the $CMAILDIR directory whether newly created or not 84 ## ################################################################# ## 85 86 die "Couldn't changed directory to \"$CMAILDIR\"\n" 87 unless (chdir "$CMAILDIR") ; 88 89 ## ################################################################# ## 90 ## Check for existence of the named chess directory 91 ## ################################################################# ## 92 93 if (! (-d "$ARCDIR")) { 94 95 ## ############################################################# ## 96 ## Ask user for confirmation if attached to tty 97 ## ############################################################# ## 98 99 if (-t) { 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]: ") ; 105 106 $_ = <tty> ; ## Read response from tty 107 die "Bye!\n" if (/^[qQ].*/) ; ## Quit if q selected 108 109 select ($old) ; ## Re-select the old output 110 } 111 112 ## ############################################################# ## 113 ## Create a chess directory or die 114 ## ############################################################# ## 115 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") ; 121 } 122} 123#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 124 125 126#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 127## Parse command-line arguments 128sub parse_flags { 129 ## ################################################################# ## 130 ## Set up defaults from the environment or from hard-wired constants 131 ## ################################################################# ## 132 133 $SHOWC = 0 ; 134 $SHOWW = 0 ; 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") 139 && (! $MAILPROG)) ; 140 $MAILPROG = "/usr/lib/sendmail" if ( (-x "/usr/lib/sendmail") 141 && (! $MAILPROG)) ; 142 $MAILPROG = "/etc/sendmail" if ( (-x "/usr/lib/sendmail") 143 && (! $MAILPROG)) ; 144 $MAILPROG = "/usr/ucb/Mail" if ( (-x "/usr/ucb/Mail") 145 && (! $MAILPROG)) ; 146 $MAILPROG = "/usr/ucb/mail" if ( (-x "/usr/ucb/mail") 147 && (! $MAILPROG)) ; 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) ; 154 $NUM_GAMES = "?" ; 155 $NUM_WGAMES = "?" ; 156 $NUM_BGAMES = "?" ; 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" ; 165 $PGN_SITE = "NET"; 166 $PGN_ROUND = "-"; 167 $PGN_MODE = "EM"; 168 $SEND_MAIL = 1 ; 169 $REMAIL = 0 ; 170 $LOAD_XBOARD = 1 unless $ENV{'CMAIL_NO_XBOARD'} ; 171 $REUSE = 1 ; 172 @TD_FLAGS = ("-td", $TIME_DELAY) ; 173 @NCP_FLAGS = ("-ncp") ; 174 175 ## ################################################################# ## 176 ## Define the usage string 177 ## ################################################################# ## 178 179 $USAGE = ("cmail 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>] 185 [-mode <mode>]") ; 186 187 ## ################################################################# ## 188 ## Overwrite defaults if specified on the command-line 189 ## ################################################################# ## 190 191 @UNREC_ARGS = () ; 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 ; 200 $QUIET = 1 ;} 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 ; 208 $SEND_MAIL = 1 ; 209 $REMAIL = 1 ;} 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)$/) 229 {@TD_FLAGS = ($ARGV, 230 shift) ;} 231 elsif ("$ARGV" =~ /^-noChessComputer$/) 232 {@NCP_FLAGS = ($ARGV, 233 shift) ;} 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) ; 241 $UNREC = 1 ; 242 } else { 243 die("cmail: Unrecognised flag \"$ARGV\"\nUsage: $USAGE\n") ; 244 } 245 } 246 247 ## ################################################################# ## 248 ## Assign a value to $ARCDIR if not specified on the command line 249 ## ################################################################# ## 250 251 $ARCDIR = $ENV{'CMAIL_ARCDIR'} unless ($ARCDIR) ; 252 $ARCDIR = $CMAILDIR unless ($ARCDIR) ; 253 $ENV{'CMAIL_ARCDIR'} = $ARCDIR ; ## Make sure this is set for xboard 254 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 ## ################################################################# ## 259 260 $ENV{'CMAIL_MAILPROG'} = $MAILPROG ; 261 $ENV{'CMAIL_DIR'} = $CMAILDIR ; 262 $ENV{'CHESSDIR'} = $CMAILDIR ; ## Make xboard use $CMAILDIR 263 $ENV{'CMAIL_ARCDIR'} = $ARCDIR ; 264 if ($LOGFILE) { 265 $ENV{'CMAIL_LOGFILE'} = $LOGFILE ; 266 } else { 267 $LOGFILE = "&STDERR" ; 268 } 269 270 ## ################################################################# ## 271 ## Work out how many games of each colour will be played 272 ## ################################################################# ## 273 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) ; 284 } 285 $NUM_WGAMES = $NUM_GAMES - $NUM_BGAMES ; 286 } elsif ("$NUM_BGAMES" eq "?") { 287 $NUM_BGAMES = $NUM_GAMES - $NUM_WGAMES ; 288 } 289 } elsif ("$NUM_WGAMES" eq "?") { 290 if ("$NUM_BGAMES" eq "?") { 291 $NUM_GAMES = 1 ; 292 $NUM_WGAMES = 1 ; 293 $NUM_BGAMES = 0 ; 294 } else { 295 $NUM_GAMES = $NUM_BGAMES ; 296 $NUM_WGAMES = 0 ; 297 } 298 } else { 299 if ("$NUM_BGAMES" eq "?") { 300 $NUM_GAMES = $NUM_WGAMES ; 301 $NUM_BGAMES = 0 ; 302 } else { 303 $NUM_GAMES = $NUM_WGAMES + $NUM_BGAMES ; 304 } 305 } 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) ; 311} 312#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 313 314 315#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 316## Initialisation of variables and environment 317sub showGPL { 318 ## ################################################################# ## 319 ## Show copyright notice 320 ## ################################################################# ## 321 322 while (<DATA>) { 323 last if (/^{END OF GPL COPYRIGHT}$/) ; 324 s/\$Revision[:] (.*) \$/$1/ ; 325 print ; 326 } 327 328 ## ################################################################# ## 329 ## Show conditions if requested 330 ## ################################################################# ## 331 332 while (<DATA>) { 333 last if (/^{END OF GPL CONDITIONS}$/) ; 334 print if ($SHOWW) ; 335 } 336 337 ## ################################################################# ## 338 ## Show warranty if requested 339 ## ################################################################# ## 340 341 if ($SHOWC) { 342 print "\n" if ($SHOWW) ; 343 print while (<DATA>) ; 344 } 345 346 347 exit 0 if ($SHOWC || $SHOWW) ; ## Abort if showed conditions or warranty 348} 349#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 350 351 352#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 353## Initialisation of variables and environment 354sub initialise { 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+" ; 360 361 &parse_flags (@ARGV) ; ## Parse command-line arguments 362 363 &showGPL unless $QUIET ; 364 365 open (tty, "< /dev/tty") ; ## Open tty for reading 366 367 &need_chess_dir () ; ## Check for the existence of CMAILDIR 368 369 open (logfile, ">$LOGFILE") if ($DEBUG) ; ## Default is STDERR 370 371 &debug ("Called <initialise>\n") ; 372} 373#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 374 375 376#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 377## Prompt for a game name, if <cr> use a default 378sub 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 383 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 388 389 select ($old) ; ## Re-select the old output 390} 391#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 392 393 394#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 395## Prompt for opponent's address 396sub 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 401 402 ## ################################################################# ## 403 ## Prompt for opponent's email address 404 ## ################################################################# ## 405 406 print "Opponent's email address: " ; 407 die "cmail: tty not open\n" unless (-t) ; 408 <tty> =~ /(.*)/ ; 409 $OPP_ADDRESS = $1 ; 410 411 ## ################################################################# ## 412 ## Use name as default if still blank 413 ## ################################################################# ## 414 415 $OPP_ADDRESS = $OPP_NNAME if ("" eq $OPP_ADDRESS) ; 416 417 select ($old) ; ## Re-select the old output 418} 419#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 420 421 422#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 423## Prompt for opponent's name 424sub 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: " ; 430 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) ; 436 437 select ($old) ; ## Re-select the old output 438} 439#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 440 441 442#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 443## Prompt for move 444sub prompt_for_move { 445 local ($prompt) = shift ; 446 local ($pattern) = shift ; 447 &debug ("Called <prompt_for_move>\n") ; 448 449 local ($move) = "" ; 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 454 455 do { 456 print $prompt ; 457 <STDIN> =~ /(.*)/ ; ## Read line from tty 458 $move = $1 ; ## Match! 459 } until ($move =~ /^$pattern$/) ; 460 461 select ($old) ; ## Re-select the old output 462 return ($move) ; 463} 464#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 465 466 467#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 468## Load the game 469sub play_game { 470 &debug ("Called <play_game>\n") ; 471 472 $| = 1 ; ## Start flushing output buffer 473 474 ## ################################################################# ## 475 ## Load xboard unless inhibited by command-line arguments 476 ## ################################################################# ## 477 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 ## ############################################################# ## 486 487 unlink "$PGN_GAME.out" ; 488 489 ## ############################################################# ## 490 ## Invoke xboard with loads of flags 491 ## ############################################################# ## 492 493 if ($PGN_GAME) { 494 if (@ARCHIVE) { 495 local ($date) = &get_date_from_games (@ARCHIVE) ; 496 $XBOARD_ARGS = join (' ', (("-lgf", 497 "'$ARCDIR/$PGN_GAME.$date.archive'"), 498 @NCP_FLAGS, 499 "-xics", 500 @TD_FLAGS, 501 @DEBUG_FLAGS, 502 @UNREC_ARGS)) ; 503 } else { 504 $XBOARD_ARGS = join (' ', (("-cmail", "'$PGN_GAME'"), 505 @TD_FLAGS, 506 @NCP_FLAGS, 507 "-xics", 508 @DEBUG_FLAGS, 509 @UNREC_ARGS)) ; 510 } 511 } else { 512 $PGN_GAME = "unknown.cmail" ; 513 $XBOARD_ARGS = join (' ', (("-lgf", "'$PGN_GAME'"), 514 @NCP_FLAGS, 515 "-xics", 516 @TD_FLAGS, 517 @DEBUG_FLAGS, 518 @UNREC_ARGS)) ; 519 $REUSE = 0 ; 520 } 521 522 $LOG_FILE = "$PGN_GAME.log" ; 523 &debug ("Invoking xboard with args: $XBOARD_ARGS\n") ; 524 $PID_FILE = "$PGN_GAME.pid" ; 525 if ( (! $REUSE) 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' ;" 534 . " wait ;" 535 . " rm '$PID_FILE') & } >'$LOG_FILE' 2>&1") ; 536 print ( "done.\n" 537 . "If nothing happens look for an error message in\n" 538 . "$CMAILDIR/$LOG_FILE\n") ; 539 } else { 540 print ("Revived existing xboard for game \"$PGN_GAME\".\n" 541 . "If nothing happens" 542 . " remove $CMAILDIR/$PID_FILE and try again.\n") ; 543 } 544 545 return 1 ; 546 } 547 return 0 ; 548} 549#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 550 551 552#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 553## Enter moves on tty instead of xboard 554sub play_on_tty { 555 &debug ("Called <play_on_tty>\n") ; 556 557 local (@results) = @_ ; 558 559 local (@games, $game, $to_play) ; 560 561 ## ################################################################# ## 562 ## Check we have access to tty 563 ## ################################################################# ## 564 565 if (open(STDIN, '/dev/tty')) { 566 ## ############################################################# ## 567 ## Read in games 568 ## ############################################################# ## 569 570 local ($infile) = "$PGN_GAME.game.in" ; 571 if (-f $infile) { 572 @games = &get_games_from_file($infile) ; 573 &debug ("Read in games from \"$infile\"\n") ; 574 575 if (@results) { 576 foreach $game (@games) { 577 $result = shift (@results) ; 578 if ($result && ($game =~ /\[Result\s+"[^*]+"\]/)) { 579 $game = "" ; 580 } 581 } 582 } 583 584 ## ################################################################# ## 585 ## Accept move on tty for each game in turn 586 ## ################################################################# ## 587 588 local ($comment_orig, $comment, $comment_line, 589 $pos, $move, $normal_move, $action, $result) ; 590 591 foreach $game (@games) { 592 next unless $game ; 593 594 $game =~ s/[\s\n]*[*]?[\s\n]*$// ; 595 $pos = "" ; 596 $pos = $1 if ($game =~ s/($posdiag)//) ; 597 print $game, "\n\n", $pos ; 598 599 ($number, $to_play) = &get_to_play ($game) ; 600 601 $comment_orig = "" ; 602 $game_nocomment = $game ; 603 if ($game_nocomment =~ s/\n?{\n?([^{]*)}$//) { 604 $comment_orig = $1 ; 605 $comment_orig =~ s/([^\n])$/$1\n/ ; 606# &debug ("Comment is:\n{\n$comment_orig}\n") ; 607 } 608 if ($game =~ /\[Result\s+"[^*]+"\]/) { 609 &prompt_for_move ("Game finished, press \"Return\" to continue: ", "") ; 610 next ; 611 } 612 613 $tmpgame = $game_nocomment ; 614 $comment = $comment_orig ; 615 616 outer: 617 while (1) { 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])") ; 621 } else { 622 $move = &prompt_for_move ("Enter move [MOVE/(r)esign/(c)omment/re(t)ry]: ", 623 "([-a-h0-9PRNBQK][-a-h0-9PRNBQK]+|[rct])") ; 624 } 625 $normal_move = 0 ; 626 $result = "" ; 627 if ($move =~ /^\s*r\s*$/i) { 628 if ($to_play eq "White") { 629 $result = "0-1" ; 630 } else { 631 $result = "1-0" ; 632 } 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" ; 639 } 640 next ; 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" ; 646 next ; 647 } else { 648 $normal_move = 1 ; 649 } 650 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" ; 658 } else { 659 $tmpgame .= " $move" ; 660 } 661 662 $tmpgame =~ s/\[Result\s+"(.*)"\]/[Result "$result"]/ if ($result) ; 663 $comment = "" ; 664 middle: 665 while (1) { 666 if ($normal_move) { 667 $action = &prompt_for_move ("Enter action [(d)raw/(c)omment/(s)end/re(t)ry]: ", 668 "[dcst]") ; 669 } elsif ($result) { 670 $action = &prompt_for_move ("Enter action [(s)end/re(t)ry]: ", 671 "[st]") ; 672 } else { 673 $action = &prompt_for_move ("Enter action [(c)omment/(s)end/re(t)ry]: ", 674 "[cst]") ; 675 } 676 if ($action =~ /^\s*d\s*$/i) { 677 if ($normal_move) { 678 $comment .= "$to_play offers a draw\n" ; 679 680 while (1) { 681 $action = &prompt_for_move ("Enter action [(c)omment/(s)end/re(t)ry]: ", 682 "[cst]") ; 683 if ($action =~ /^\s*c\s*$/i) { 684 while ($comment_line = &prompt_for_move ("Enter comment: ", ".*")) { 685 $comment .= $comment_line . "\n" ; 686 } 687 next ; 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" ; 693 next outer; 694 } elsif ($action =~ /^\s*s\s*$/i) { 695 $tmpgame .= "\n{\n" . $comment . "}" ; 696 last middle ; 697 } 698 } 699 } else { 700 print "You can't offer a draw at this point.\n" ; 701 next ; 702 } 703 } elsif ($action =~ /^\s*c\s*$/i) { 704 if ($result) { 705 print "You can't enter a comment after the game is finished.\n" ; 706 } else { 707 while ($comment_line = &prompt_for_move ("Enter comment: ", ".*")) { 708 $comment .= $comment_line . "\n" ; 709 } 710 } 711 next ; 712 } elsif ($action =~ /^\s*t\s*$/i) { 713 print $game, $pos ; 714 $tmpgame = $game_nocomment ; 715 $comment = $comment_orig ; 716 print "Try again.\n" ; 717 next outer; 718 } elsif ($action =~ /^\s*s\s*$/i) { 719 $tmpgame .= "\n{\n" . $comment . "}\n" if ($comment) ; 720 last ; 721 } 722 } 723 724 last ; 725 } 726 727 $tmpgame .= "\n*" if ($normal_move) ; 728 $tmpgame .= "\n\n" ; 729 $game = $tmpgame ; 730 } 731 } else { 732 die "cmail: No games to be read\n" ; 733 } 734 } else { 735 die "cmail: Can't open tty" ; 736 } 737 738 return (@games) ; 739} 740#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 741 742 743#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 744## Find the game the user wants by any means possible 745sub find_game { 746 &debug ("Called <find_game>\n") ; 747 748 ## ################################################################# ## 749 ## Ask user for a game name if not already known 750 ## ################################################################# ## 751 752 &prompt_for_game_name () if ("" eq "$PGN_GAME") ; 753 754 ## ################################################################# ## 755 ## Find out opponent's details 756 ## ################################################################# ## 757 758 if ("" eq "$PGN_GAME") { 759 ## ############################################################# ## 760 ## Failed to find the game name so construct a default from players 761 ## ############################################################# ## 762 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 770 } else { 771 $PGN_GAME = "$OPP_NNAME-vs-$MY_NNAME" ; ## Construct default 772 } 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") ; 777 } 778 779 ## ################################################################# ## 780 ## If no $PGN_GAME.game.in file, assume we're starting a new game 781 ## ################################################################# ## 782 783 &start_new_game () unless (-f "$PGN_GAME.game.in") ; 784} 785#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 786 787 788#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 789## Get the date 790sub get_date { 791 local ($the_time) = time ; 792 local ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = 793 localtime ($the_time) ; 794 $mon ++ ; 795 $year += 1900 ; 796 if ($mon < 10) { 797 $mon = "0$mon" ; 798 } 799 if ($mday < 10) { 800 $mday = "0$mday" ; 801 } 802 "$year.$mon.$mday" ; 803} 804#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 805 806 807#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 808## Start a new game 809sub start_new_game { 810 print ( "Starting new game" 811 . " -- $NUM_WGAMES as white, $NUM_BGAMES as black.\n") ; 812 local ($to_play) = "white" ; 813 $move_num = 0 ; 814 $STARTING_NEW_GAME = 1 ; 815 816 local (@game) ; 817 818 ## ################################################################# ## 819 ## Ask user for opponent's email address if unknown 820 ## ################################################################# ## 821 822 &prompt_for_opp_address () if ("" eq "$OPP_ADDRESS") ; 823 824 ## ################################################################# ## 825 ## Give up if we haven't got anywhere to send a move to 826 ## ################################################################# ## 827 828 die "cmail: Can't proceed without your opponent's email address.\n" 829 if ("" eq "$OPP_ADDRESS") ; 830 831 ## ################################################################# ## 832 ## Create an empty game file 833 ## ################################################################# ## 834 835 open (GAMEFILE, "> $PGN_GAME.game.in") ; 836 for ($j = 1; $j <= $NUM_GAMES; $j ++) { 837 $PW_GCOS = &get_pw_gcos () ; 838 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) ; 844 845 $PGN_OPPCOL = $OPP_FNAME ; 846 $PGN_OPPCOL = "?" unless ($PGN_OPPCOL) ; 847 $PGN_OPPCOLNA = $OPP_ADDRESS ; 848 $PGN_OPPCOLNA = "?" unless ($PGN_OPPCOLNA) ; 849 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 ; 855 } else { 856 $PGN_WHITE = $PGN_MYCOL ; 857 $PGN_BLACK = $PGN_OPPCOL ; 858 $PGN_WHITENA = $PGN_MYCOLNA ; 859 $PGN_BLACKNA = $PGN_OPPCOLNA ; 860 } 861 862 ## ######################################################### ## 863 ## If we only have one colour of game then allow command-line 864 ## colour specs to override 865 ## ######################################################### ## 866 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) ; 872 } 873 874 $PGN_DATE = &get_date () ; 875 $PGN_DATE = "?" unless ($PGN_DATE) ; 876 877 if ($NUM_GAMES > 1) { 878 $SUFFIX = ".$j" ; 879 } else { 880 $SUFFIX = "" ; 881 } 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", 888 "[Result \"*\"]\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) ; 897 } else { 898 print GAMEFILE @game ; 899 } 900 } 901 close (GAMEFILE) ; 902} 903#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 904 905 906#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 907## Get the password file gcos (full name) entry 908sub get_pw_entry { 909 local ($name, $passwd, $uid, $gid, $quota, $comment, $gcos, $dir, $shell) = 910 getpwuid ($<); 911 ($name, $gcos) ; 912} 913#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 914 915 916#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 917## Get password file gcos (full name) entry 918sub get_pw_gcos { 919 local ($PW_GCOS) ; 920 if (! $PW_GCOS) { 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 926 $PW_GCOS = "" ; 927 } ## Else leave it as one word 928 &debug ("PW full name is \"$PW_GCOS\"\n"); 929 } 930 return $PW_GCOS ; 931} 932#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 933 934 935#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 936## Get password file user name entry 937sub get_pw_name { 938 local ($PW_NAME) ; 939 ($PW_NAME, $dummy) = &get_pw_entry () ; 940 &debug ("PW name is $PW_NAME\n"); 941 942 return $PW_NAME ; 943} 944#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 945 946 947#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 948## Analyse the email message 949sub analyse_email_message { 950 local ($new_result, $delete_result, $unfinished, $finished) 951 = (0, 0, 0, 0) ; 952 local ($gamefile, @games) ; 953 954 ## ################################################################# ## 955 ## Slurp the mail message 956 ## ################################################################# ## 957 958 die "cmail: No games on standard input" 959 unless (@games = &get_games_from_file ("STDIN")) ; 960 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 ## ################################################################# ## 965 966 print "Processing game message..." ; 967 968 if ($PGN_GAME) { 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 ## ############################################################# ## 974 975 if (! -f "$PGN_GAME.res") { 976 ## Find what date would have been used to create the archive 977 foreach (@games) { 978 if (/\[Date\s"(.*)"\]/) { 979 $date = $1 ; 980 last ; ## Assume all dates are the same 981 } 982 } 983 ## Restore results file from archive directory if it exists 984 if ($date && (-f "$ARCDIR/$PGN_GAME.$date.archive")) { 985 if (system ("cp", 986 "$ARCDIR/$PGN_GAME.$date.archive", 987 "$PGN_GAME.res")) { 988 print stderr ( "\nWarning: couldn't restore results file" 989 . " from archive\n") ; 990 } else { 991 print "restored results file from archive..." ; 992 } 993 } 994 } 995 996 ## ############################################################# ## 997 ## Find existing results, if any. 998 ## ############################################################# ## 999 1000 local (@results) = &get_games_from_file ("$PGN_GAME.res") ; 1001 1002 ## ############################################################# ## 1003 ## Parse each game 1004 ## ############################################################# ## 1005 1006 foreach $game (@games) { 1007 next unless ($game) ; 1008 1009 ($game_name, $game_num) = &get_game_name_and_number ($game) ; 1010 1011 $result = 0 ; 1012 @game = split("\n", $game) ; 1013 foreach (@game) { 1014 if (/^\[(Black|White)\s*"[?]"\]$/) { 1015 $colour = $1; 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*"(.*)"\]$/) { 1021 $NA = $3 ; 1022 if ($NA eq "?") { 1023 if ($RETURN_ADDRESS) { 1024 $NA = $RETURN_ADDRESS ; 1025 } else { 1026 $NA = "??" ; 1027 } 1028 $_ = "[$1 \"$NA\"]" ; 1029 &debug ("Changed $1 tag.\n") ; 1030 } 1031 if ($2 eq "White") { 1032 $PGN_WHITENA = $NA ; 1033 &debug ("WhiteNA tag is \"$PGN_WHITENA\"\n") ; 1034 } else { 1035 $PGN_BLACKNA = $NA ; 1036 &debug ("BlackNA tag is \"$PGN_BLACKNA\"\n") ; 1037 } 1038 } elsif (/\[Result\s*"(.*)"\]$/) { 1039 if ($1 ne "*") { 1040 $result = 1 ; 1041 $finished ++ ; 1042 } else { 1043 $unfinished ++ ; 1044 } 1045 } elsif (/^(.*[^\d]+)?\d+[.]\s*([^\s*]*\s+)?[^\s.*]+(\s*\d+[.]\s*)?[\s*]*$/) { 1046 if ($2) { 1047 $to_play = "white" ; 1048 } else { 1049 $to_play = "black" ; 1050 } 1051 &debug ("$to_play to play\n") ; 1052 } 1053 } 1054 1055 ## ######################################################### ## 1056 ## Reconstruct possibly edited game 1057 ## ######################################################### ## 1058 1059 $game = join ("\n", @game) . "\n\n" ; 1060 1061 ## ######################################################### ## 1062 ## Build up results array 1063 ## ######################################################### ## 1064 1065 if ($result) { 1066 $results[$game_num] = $games[$game_num] ; 1067 $new_result = 1 ; 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 ; 1073 } 1074 1075 ## ######################################################### ## 1076 ## Remove old .out files 1077 ## ######################################################### ## 1078 1079 unlink <$PGN_GAME.game.out.*> ; 1080 1081 ## ######################################################### ## 1082 ## Write ongoing games to game file and append new results 1083 ## ######################################################### ## 1084 1085 die "cmail: Can't open file for writing:" 1086 . " \"$CMAILDIR/$PGN_GAME.game.in\"\n" 1087 unless open (gamefile, ">$PGN_GAME.game.in") ; 1088 &debug (@games) ; 1089 print gamefile @games ; 1090 close (gamefile) ; 1091 } 1092 1093 ## ############################################################# ## 1094 ## Print how many finished/unfinished games were found 1095 ## ############################################################# ## 1096 1097 printf ("%d unfinished %s and %d finished %s...", 1098 $unfinished, ($unfinished == 1) ? "game" : "games", 1099 $finished, ($finished == 1) ? "game" : "games") ; 1100 1101 ## ############################################################# ## 1102 ## Write results back to results file if there were any results 1103 ## in the input 1104 ## ############################################################# ## 1105 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 ; 1111 close (resfile) ; 1112 } 1113 1114 ## ############################################################# ## 1115 ## Archive results if there are no unfinished games 1116 ## ############################################################# ## 1117 1118 @ARCHIVE = @results unless ($unfinished) ; 1119 1120 ## ############################################################# ## 1121 ## Figure out return address if not known 1122 ## ############################################################# ## 1123 1124 if (! $RETURN_ADDRESS) { 1125 if ($to_play eq "black") { 1126 $RETURN_ADDRESS = $PGN_WHITENA unless ("$PGN_WHITENA" eq "?") ; 1127 } else { 1128 $RETURN_ADDRESS = $PGN_BLACKNA unless ("$PGN_BLACKNA" eq "?") ; 1129 } 1130 } 1131 1132 ## ############################################################# ## 1133 ## Decide to include position diagrams in output if not already 1134 ## decided and a position diagram was found in the input 1135 ## ############################################################# ## 1136 1137 if ("$OUTPUT_POS" eq "") { 1138 if (grep (/$posdiag/, @games)) { 1139 $OUTPUT_POS = "y" ; # Output position only if it was input 1140 } else { 1141 $OUTPUT_POS = "n" ; 1142 } 1143 } 1144 $ENV{'CMAIL_OUTPUT_POS'} = $OUTPUT_POS ; 1145 1146 ## ############################################################# ## 1147 ## Check that we have enough info about the players to continue 1148 ## ############################################################# ## 1149 1150 &find_game () ; 1151 } else { 1152 ## ############################################################# ## 1153 ## Set up xboard for viewing non-cmail PGN file 1154 ## ############################################################# ## 1155 1156 local ($file) = "unknown.cmail" ; 1157 print "done.\nDumping non-cmail file into $CMAILDIR/$file..." ; 1158 1159 die "cmail: Can't open file for writing: \"$CMAILDIR/$file\"\n" 1160 unless open (gamefile, ">$file") ; 1161 print gamefile @games ; 1162 close (gamefile) ; 1163 } 1164 1165 print "done.\n" ; 1166} 1167#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 1168 1169 1170#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 1171## Sort two filenames by the numeric suffix 1172sub sort_by_numeric_suffix { 1173 $a =~ /[.](\d+)$/ ; local ($na) = $1 ; 1174 $b =~ /[.](\d+)$/ ; local ($nb) = $1 ; 1175 1176 return ($na <=> $nb) ; 1177} 1178#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 1179 1180 1181#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 1182sub get_game_name_and_number { 1183 local ($game) = join ("\n", @_) ; 1184 1185 local ($game_name, $game_num) ; 1186 1187 die "CMailGameName tag missing\n" 1188 unless ($game =~ /\[C[Mm]ailGameName\s+"(.*)"\]/) ; 1189 1190 ## ################################################################# ## 1191 ## Set game name and number 1192 ## ################################################################# ## 1193 1194 $game_name = $1 ; 1195 if ($game_name =~ s/^(.*)[.](\d+)$/$1/) { 1196 $game_num = $2 ; 1197 } else { 1198 $game_num = 1 ; 1199 } 1200 1201 ## ################################################################# ## 1202 ## Set $PGN_GAME as a side-effect or check validity 1203 ## ################################################################# ## 1204 1205 if ($PGN_GAME) { 1206 die ( "cmail: Mismatched game names in input message:\n" 1207 . "\"$PGN_GAME\", \"$game_name\"\n") 1208 if ("$PGN_GAME" ne "$game_name") ; 1209 } else { 1210 $PGN_GAME = $game_name ; 1211 &debug ("PGN_GAME set to \"$PGN_GAME\"\n") ; 1212 } 1213 1214 return ($game_name, $game_num) ; 1215} 1216#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 1217 1218 1219#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 1220## Read in a file of games and split into separate games 1221sub get_games_from_file { 1222 local ($file) = shift ; 1223 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) ; 1229 1230 foreach (@file) { 1231 ## Strip off leading quotation characters 1232 s/^[^\s]*>// ; 1233 s/^[ \t]+// ; 1234 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+"(.*)"\]/) { 1242 $PGN_GAME = $1 ; 1243 $PGN_GAME =~ s/[.]\d+$// ; 1244 } 1245 } 1246 1247 return (@file) unless ($PGN_GAME) ; 1248 if (grep (/\{--------------|\[Event/, @file)) { 1249 shift (@file) while ($file[0] !~ /\{--------------|\[Event/) ; 1250 } 1251 } else { 1252 return () ; 1253 } 1254 } else { 1255 return () unless (open (file, "<$file")) ; 1256 1257 @file = <file> ; ## Slurp file 1258 close (file) ; 1259 } 1260 1261 local (@games, $game_name, $game_num, $game, $tag) ; 1262 1263 ## ################################################################# ## 1264 ## Remove headers and leading blanks 1265 ## ################################################################# ## 1266 1267 local (@tgames) = split (/($posdiag\[Event|\[Event)/, join ('', @file)) ; 1268 shift (@tgames) while (!$tgames[0]) ; 1269 1270 ## ################################################################# ## 1271 ## Set up @games array with proper game numbers 1272 ## ################################################################# ## 1273 1274 while (@tgames) { 1275 $game = shift (@tgames) . shift (@tgames) ; 1276 1277 ($game_name, $game_num) = &get_game_name_and_number ($game) ; 1278 $games[$game_num] = $game ; 1279 } 1280 1281 return (@games) ; 1282} 1283#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 1284 1285 1286#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 1287## Analyse output files and send the move 1288sub send_move { 1289 &debug ("Called <send_move>\n") ; 1290 1291 ## ################################################################# ## 1292 ## Cat the pos (if it exists), game and result (if it exists) files 1293 ## into the .out file. 1294 ## ################################################################# ## 1295 1296 local ($unfinished, $finished) = (0, 0) ; 1297 local (@results, $move, $move_msg, $to_play, $number) ; 1298 1299 ## ################################################################# ## 1300 ## Find any .game.out.* files 1301 ## ################################################################# ## 1302 1303 local (@outfiles) = (<$PGN_GAME.game.out.*>) ; 1304 @outfiles = grep (/[.]\d+$/, @outfiles) ; ## Ignore autosave files 1305 @outfiles = sort sort_by_numeric_suffix @outfiles ; ## Sort 1306 1307 ## ################################################################# ## 1308 ## Find .res file if it exists 1309 ## ################################################################# ## 1310 1311 local ($resfile) = "$PGN_GAME.res" ; 1312 if (-f $resfile) { 1313 @results = &get_games_from_file($resfile) ; 1314 &debug ("Read in results\n") ; 1315 } else { 1316 @results = () ; 1317 &debug ("No results to read\n") ; 1318 } 1319 1320 ## ################################################################# ## 1321 ## Find .out file if it exists 1322 ## ################################################################# ## 1323 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>)) ; 1333 close (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 ; 1339 } 1340 &debug ("Read in games from output files\n") ; 1341 $games[0] = "" ; 1342 } else { 1343 &debug ("No games to read from STDIN\n") ; 1344 if (-f $outfile) { 1345 @games = &get_games_from_file($outfile) ; 1346 } else { 1347 die "Can't find any game files\n" unless (@results) ; 1348 } 1349 } 1350 1351 ## ################################################################# ## 1352 ## Process games 1353 ## ################################################################# ## 1354 1355 if (@games) { 1356 ## ############################################################# ## 1357 ## Find opponent's email address in games 1358 ## ############################################################# ## 1359 1360 $OPP_ADDRESS = &get_opp_address_from_games (@games) 1361 unless ($OPP_ADDRESS) ; 1362 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 ## ############################################################# ## 1367 1368 $unfinished = 0 ; 1369 $move_num = 0 ; 1370 $move = blank ; 1371 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 ; 1376 close (outfile) ; 1377 1378 $game_num = -1 ; 1379 $num_games = 0 ; 1380 foreach $game (@games) { 1381 $game_num ++ ; 1382 next unless ($game) ; 1383 1384 $num_games ++ ; 1385 ## Determine last move and whether result or not 1386 $result = 0 ; 1387 foreach (split(/\n/, $game)) { 1388 if (/^(.*[^\d]+|)(\d+)[.]+\s*[^\s.]*\s+(\S+)\s*$/) { 1389 $move_num = $2 ; 1390 $move = $3 ; 1391 } elsif (/^\[Result\s*"(.*)"\]$/) { 1392 if ($1 ne "*") { 1393 $result = 1 ; 1394 $finished ++ ; 1395 } else { 1396 $unfinished ++ ; 1397 } 1398 } 1399 } 1400 1401 $results[$game_num] = $game if ($result) ; 1402 } 1403 1404 ## Write result files back to $PGN_GAME.res 1405 if (@results) { 1406 die "Can't open results file $PGN_GAME.res for writing\n" 1407 unless open (resfile, ">$PGN_GAME.res") ; 1408 print resfile @results ; 1409 close (results) ; 1410 } 1411 1412 unlink <$PGN_GAME.game.out.*> ; 1413 1414 ## ############################################################# ## 1415 ## Just say how many games are in the message 1416 ## ############################################################# ## 1417 1418 if ($num_games > 1) { 1419 $move_msg = "$num_games games" ; 1420 } else { 1421 $move_msg = "1 game" ; 1422 } 1423 1424 ## ############################################################# ## 1425 ## Print how many finished/unfinished games were found 1426 ## ############################################################# ## 1427 1428 printf ("Sending %d unfinished %s and %d finished %s.\n", 1429 $unfinished, ($unfinished == 1) ? "game" : "games", 1430 $finished, ($finished == 1) ? "game" : "games") ; 1431 1432 ## ############################################################# ## 1433 ## Send the mail message to opponent's address unless bypassed 1434 ## ############################################################# ## 1435 1436 if ($SEND_MAIL) { 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"; 1444 } else { 1445 $opened = open (mail, "|$MAILPROG -s \"$subject\" $OPP_ADDRESS") ; 1446 } 1447 if ($opened) { 1448 print mail @games ; 1449 close (mail) ; 1450 print ( "Mailed cmail message to \"$OPP_ADDRESS\":\n" 1451 . "$move_msg <$PGN_GAME>\n") ; 1452 @ARCHIVE = @results unless ($unfinished) ; 1453 } else { 1454 die "Failed to mail cmail message.\n" ; 1455 } 1456 } else { 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") ; 1460 } 1461 } else { 1462 if (@results) { 1463 print "Email not sent (the game is over).\n" ; 1464 } else { 1465 die "No games found\n" ; 1466 } 1467 } 1468} 1469#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 1470 1471 1472#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 1473## Get date from array of games 1474sub get_date_from_games { 1475 local (@games) = @_ ; 1476 1477 local ($date) = "nodate" ; 1478 1479 foreach (@games) { 1480 if (/\[Date\s"(.*)"\]/) { 1481 $date = $1 ; 1482 last ; ## Assume all dates are the same 1483 } 1484 } 1485 1486 return ($date) ; 1487} 1488#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 1489 1490 1491#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 1492## Determine which colour is to play and the move number 1493sub get_to_play { 1494 &debug ("Called <get_to_play>\n") ; 1495 local ($game) = shift ; 1496 1497 local ($number, $to_play) = (1, "White") ; 1498 1499 $game =~ s/{[^}]*}//g ; 1500 $game =~ s/[\s\n]*[*][\s\n]*$// ; 1501 1502 if ($game =~ /(\d+)[.][\n ]*([.]*)[\n ]*([^\n.]*)[\n\s]*[10-]*[\n\s]*$/) { 1503 $number = $1 ; 1504 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" ; 1511 $number ++ ; 1512 } else { 1513 $to_play = "Black" ; 1514 } 1515 } 1516 1517 &debug ("$to_play to play on move $number\n") ; 1518 1519 return ($number, $to_play) ; 1520} 1521#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 1522 1523 1524#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 1525## Get opp address from array of games 1526sub get_opp_address_from_games { 1527 &debug ("Called <get_opp_address_from_games>\n") ; 1528 local (@games) = @_ ; 1529 1530 local ($opp_address) = "" ; 1531 local ($number, $to_play, $tag) ; 1532 1533 foreach (@games) { 1534 next unless $_ ; 1535 1536 ($number, $to_play) = &get_to_play ($_) ; 1537 $tag = ( ($to_play eq "White") 1538 ? "WhiteNA" 1539 : "BlackNA") ; 1540 1541 if (/\[$tag\s"(.*)"\]/) { 1542 $opp_address = $1 ; 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 1546 } else { 1547 die "cmail: Can't find \"$tag\" tag\n" ; 1548 } 1549 } 1550 1551 die "cmail: Can't find opponent's email address\n" unless ($opp_address) ; 1552 1553 return ($opp_address) ; 1554} 1555#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 1556 1557 1558#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 1559## Archive @ARCHIVE in the $ARCDIR directory 1560sub archive { 1561 return () unless (@ARCHIVE) ; 1562 1563 local ($date) = &get_date_from_games (@ARCHIVE) ; 1564 1565 local ($file) = "$ARCDIR/$PGN_GAME.$date.archive" ; 1566 if (open (archive, ">$file")) { 1567 print archive @ARCHIVE ; 1568 close (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 1573 unlink (@remove) ; 1574 } else { 1575 print "Couldn't open \"$file\" to archive game\n" ; 1576 } 1577} 1578#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 1579 1580 1581#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 1582## Main body 1583sub main { 1584 local (@games) = () ; 1585 1586 &initialise () ; ## Initialise variables etc. 1587 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 1591 } else { 1592 &debug ("Piping!\n") ; 1593 &analyse_email_message () ; ## Analyse the mail message 1594 } 1595 1596 if (! &play_game ()) { ## Load the game 1597 &send_move () ; ## Analyse output and send moves 1598 } 1599 1600 &archive () ; ## Archive games if all finished 1601 1602 close (tty) ; ## Tidy up 1603 close (logfile) if ($DEBUG) ; ## Tidy up 1604} 1605#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 1606&main () ; 1607#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 1608__END__ 1609cmail (From XBoard version @PACKAGE_VERSION@), Copyright (C) 1993 Free Software Foundation, Inc. 1610cmail comes with ABSOLUTELY NO WARRANTY; for details type `cmail -w'. 1611cmail is free software, and you are welcome to redistribute it 1612under certain conditions; type `cmail -c' for details. 1613 1614{END OF GPL COPYRIGHT} 1615 GNU GENERAL PUBLIC LICENSE 1616 TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 1617 1618 0. This License applies to any program or other work which contains 1619a notice placed by the copyright holder saying it may be distributed 1620under the terms of this General Public License. The "Program", below, 1621refers to any such program or work, and a "work based on the Program" 1622means either the Program or any derivative work under copyright law: 1623that is to say, a work containing the Program or a portion of it, 1624either verbatim or with modifications and/or translated into another 1625language. (Hereinafter, translation is included without limitation in 1626the term "modification".) Each licensee is addressed as "you". 1627 1628Activities other than copying, distribution and modification are not 1629covered by this License; they are outside its scope. The act of 1630running the Program is not restricted, and the output from the Program 1631is covered only if its contents constitute a work based on the 1632Program (independent of having been made by running the Program). 1633Whether that is true depends on what the Program does. 1634 1635 1. You may copy and distribute verbatim copies of the Program's 1636source code as you receive it, in any medium, provided that you 1637conspicuously and appropriately publish on each copy an appropriate 1638copyright notice and disclaimer of warranty; keep intact all the 1639notices that refer to this License and to the absence of any warranty; 1640and give any other recipients of the Program a copy of this License 1641along with the Program. 1642 1643You may charge a fee for the physical act of transferring a copy, and 1644you may at your option offer warranty protection in exchange for a fee. 1645 1646 2. You may modify your copy or copies of the Program or any portion 1647of it, thus forming a work based on the Program, and copy and 1648distribute such modifications or work under the terms of Section 1 1649above, provided that you also meet all of these conditions: 1650 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. 1653 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. 1658 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.) 1669 1670These requirements apply to the modified work as a whole. If 1671identifiable sections of that work are not derived from the Program, 1672and can be reasonably considered independent and separate works in 1673themselves, then this License, and its terms, do not apply to those 1674sections when you distribute them as separate works. But when you 1675distribute the same sections as part of a whole which is a work based 1676on the Program, the distribution of the whole must be on the terms of 1677this License, whose permissions for other licensees extend to the 1678entire whole, and thus to each and every part regardless of who wrote it. 1679 1680Thus, it is not the intent of this section to claim rights or contest 1681your rights to work written entirely by you; rather, the intent is to 1682exercise the right to control the distribution of derivative or 1683collective works based on the Program. 1684 1685In addition, mere aggregation of another work not based on the Program 1686with the Program (or with a work based on the Program) on a volume of 1687a storage or distribution medium does not bring the other work under 1688the scope of this License. 1689 1690 3. You may copy and distribute the Program (or a work based on it, 1691under Section 2) in object code or executable form under the terms of 1692Sections 1 and 2 above provided that you also do one of the following: 1693 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, 1697 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, 1704 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.) 1710 1711The source code for a work means the preferred form of the work for 1712making modifications to it. For an executable work, complete source 1713code means all the source code for all modules it contains, plus any 1714associated interface definition files, plus the scripts used to 1715control compilation and installation of the executable. However, as a 1716special exception, the source code distributed need not include 1717anything that is normally distributed (in either source or binary 1718form) with the major components (compiler, kernel, and so on) of the 1719operating system on which the executable runs, unless that component 1720itself accompanies the executable. 1721 1722If distribution of executable or object code is made by offering 1723access to copy from a designated place, then offering equivalent 1724access to copy the source code from the same place counts as 1725distribution of the source code, even though third parties are not 1726compelled to copy the source along with the object code. 1727 1728 4. You may not copy, modify, sublicense, or distribute the Program 1729except as expressly provided under this License. Any attempt 1730otherwise to copy, modify, sublicense or distribute the Program is 1731void, and will automatically terminate your rights under this License. 1732However, parties who have received copies, or rights, from you under 1733this License will not have their licenses terminated so long as such 1734parties remain in full compliance. 1735 1736 5. You are not required to accept this License, since you have not 1737signed it. However, nothing else grants you permission to modify or 1738distribute the Program or its derivative works. These actions are 1739prohibited by law if you do not accept this License. Therefore, by 1740modifying or distributing the Program (or any work based on the 1741Program), you indicate your acceptance of this License to do so, and 1742all its terms and conditions for copying, distributing or modifying 1743the Program or works based on it. 1744 1745 6. Each time you redistribute the Program (or any work based on the 1746Program), the recipient automatically receives a license from the 1747original licensor to copy, distribute or modify the Program subject to 1748these terms and conditions. You may not impose any further 1749restrictions on the recipients' exercise of the rights granted herein. 1750You are not responsible for enforcing compliance by third parties to 1751this License. 1752 1753 7. If, as a consequence of a court judgment or allegation of patent 1754infringement or for any other reason (not limited to patent issues), 1755conditions are imposed on you (whether by court order, agreement or 1756otherwise) that contradict the conditions of this License, they do not 1757excuse you from the conditions of this License. If you cannot 1758distribute so as to satisfy simultaneously your obligations under this 1759License and any other pertinent obligations, then as a consequence you 1760may not distribute the Program at all. For example, if a patent 1761license would not permit royalty-free redistribution of the Program by 1762all those who receive copies directly or indirectly through you, then 1763the only way you could satisfy both it and this License would be to 1764refrain entirely from distribution of the Program. 1765 1766If any portion of this section is held invalid or unenforceable under 1767any particular circumstance, the balance of the section is intended to 1768apply and the section as a whole is intended to apply in other 1769circumstances. 1770 1771It is not the purpose of this section to induce you to infringe any 1772patents or other property right claims or to contest validity of any 1773such claims; this section has the sole purpose of protecting the 1774integrity of the free software distribution system, which is 1775implemented by public license practices. Many people have made 1776generous contributions to the wide range of software distributed 1777through that system in reliance on consistent application of that 1778system; it is up to the author/donor to decide if he or she is willing 1779to distribute software through any other system and a licensee cannot 1780impose that choice. 1781 1782This section is intended to make thoroughly clear what is believed to 1783be a consequence of the rest of this License. 1784 1785 8. If the distribution and/or use of the Program is restricted in 1786certain countries either by patents or by copyrighted interfaces, the 1787original copyright holder who places the Program under this License 1788may add an explicit geographical distribution limitation excluding 1789those countries, so that distribution is permitted only in or among 1790countries not thus excluded. In such case, this License incorporates 1791the limitation as if written in the body of this License. 1792 1793 9. The Free Software Foundation may publish revised and/or new versions 1794of the General Public License from time to time. Such new versions will 1795be similar in spirit to the present version, but may differ in detail to 1796address new problems or concerns. 1797 1798Each version is given a distinguishing version number. If the Program 1799specifies a version number of this License which applies to it and "any 1800later version", you have the option of following the terms and conditions 1801either of that version or of any later version published by the Free 1802Software Foundation. If the Program does not specify a version number of 1803this License, you may choose any version ever published by the Free Software 1804Foundation. 1805 1806 10. If you wish to incorporate parts of the Program into other free 1807programs whose distribution conditions are different, write to the author 1808to ask for permission. For software which is copyrighted by the Free 1809Software Foundation, write to the Free Software Foundation; we sometimes 1810make exceptions for this. Our decision will be guided by the two goals 1811of preserving the free status of all derivatives of our free software and 1812of promoting the sharing and reuse of software generally. 1813{END OF GPL CONDITIONS} 1814 GNU GENERAL PUBLIC LICENSE 1815 NO WARRANTY 1816 1817 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 1818FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 1819OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 1820PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 1821OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 1822MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 1823TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 1824PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 1825REPAIR OR CORRECTION. 1826 1827 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 1828WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 1829REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 1830INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 1831OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 1832TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 1833YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 1834PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 1835POSSIBILITY OF SUCH DAMAGES. 1836 1837