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