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