1    | /*
2    |  * $Log: ml_press.c,v $
3    |  * Revision 1.12  2004/07/12 00:29:09  millis
4    |  * Don't show normal press warnings if game is PAUSED.
5    |  *
6    |  * Revision 1.11  2004/03/28 10:17:06  millis
7    |  * Fix Bug 281: generate error if press rejected by MustOrder flag.
8    |  *
9    |  * Revision 1.10  2004/02/18 00:11:06  millis
10   |  * Fix bug 275, not checking for touch press when game terminated.
11   |  *
12   |  * Revision 1.9  2003/09/13 22:54:51  millis
13   |  * fix bug 228. Also prohibit press to non-masters while game is paused.
14   |  *
15   |  * Revision 1.8  2003/08/10 15:27:51  millis
16   |  * Fix bug 25 (Add TouchPress)
17   |  *
18   |  * Revision 1.7  2003/06/20 23:25:37  millis
19   |  * Fix bug 180, missing press messages (due to too much stack variables)
20   |  *
21   |  * Revision 1.6  2002/06/11 16:26:19  nzmb
22   |  *
23   |  * Added set [no]mustorder to require players to submit avalid set of orders
24   |  * before they may send press (to be used in conjunction with set wait).
25   |  *
26   |  * Revision 1.5  2002/04/06 17:14:08  nzmb
27   |  *
28   |  * Changed ml_press.c so that press from late players isn't blocked when the game is over (again)!
29   |  *
30   |  * Revision 1.4  2002/03/14 03:12:26  nzmb
31   |  *
32   |  * Added fix to ml_press.c to prevent people from getting warning messages (of if nno late press is on being prohibited from) sending press when the game is over.
33   |  *
34   |  * Revision 1.3  2001/05/10 09:05:05  greg
35   |  * added subjectlines
36   |  *
37   |  * Revision 1.2  2000/11/14 14:27:37  miller
38   |  * Various changes including
39   |  *  - Partial press between master and only one power is NOT sent to others with WATCHALL set
40   |  *  - Rejct press in a monor phase when XF_NOMINORPRESS is set
41   |  *  - Change press handling to allow observers to press  in no-press games f allowed (i.e. BackSeat Driver variant)
42   |  *  - Add prevention of press from late players, if flag is set
43   |  *
44   |  * Revision 1.1  1998/02/28 17:49:42  david
45   |  * Initial revision
46   |  *
47   |  * Revision 1.1  1996/10/20 12:29:45  rpaar
48   |  * Morrolan v9.0
49   |  */
50   | 
51   | /*
52   |  *  ml_press.c -- Process 'Press' and 'Broadcast' Commands
53   |  *
54   |  *  Copyright (C) 1987, Ken Lowe
55   |  *
56   |  *  Diplomacy is a trademark of the Avalon Hill Game Company, Baltimore,
57   |  *  Maryland, all rights reserved; used with permission.
58   |  *
59   |  *  Redistribution and use in source and binary forms are permitted
60   |  *  provided that it is for non-profit purposes, that this and the 
61   |  *  above notices are preserved and that due credit is given to Mr.
62   |  *  Lowe.
63   |  *
64   |  *  REVISION HISTORY
65   |  *      DATE        NAME         REASON
66   |  *      ----------- ------------ -----------------------------------------------
67   |  *      ?? ??? 1987 K.Lowe       Original development.
68   |  *      10 Mar 1993 Positron     Tell Master the truth about the message.
69   |  *      31 Mar 1994 C.Marcus,Jr. Complete redesign/rewrite.
70   |  *      28 Apr 1994 C.Marcus,Jr. Always include sender's address to Master.
71   |  *      23 May 1994 C.Marcus,Jr. Correct parsing of press options.
72   |  *      27 Nov 1999 M. Miller	 Prohibit partial press from late players 
73   |  *	07 Dec 1999 M. Miller	 parse_power doesn't allow duplicates
74   |  *	01 Jun 2002 T. Miller    Support for must_order flag
75   |  */
76   | 
77   | #include <stdlib.h>
78   | #include <string.h>
79   | 
80   | #include "dip.h"
81   | #include "mail.h"
82   | #include "functions.h"
83   | #include "porder.h"
84   | extern int errorflag;
85   | 
86   | /*
87   |  *  Declarations & Definitions
88   |  */
89   | 
90   | enum {
91   | 	NONE = 0,		/* Unrecognized option */
92   | 	GREY,			/* Grey press */
93   | 	WHITE,			/* White press */
94   | 	PARTIAL,		/* Press to selected individuals */
95   | 	ALLBUT,			/* To all but selected individuals */
96   | 	FAKEB,			/* Faked as broadcast */
97   | 	FAKEP,			/* Faked as to selected individuals */
98   | 	FAKEA,			/* Faked as not to selected individuals */
99   | 	NOFAKEB,		/* Override default of faked broadcasts */
100  | };
101  | 
102  | #define is_observer(x)      (dipent.players[x].power == OBSERVER)
103  | 
104  | /*
105  |  *  Public Function Declarations
106  |  */
107  | 
108  | void mail_press(char *s, int need_opts);
109  | 
110  | /*
111  |  *  Static Function Declarations
112  |  */
113  | 
114  | static void print_line(FILE * outf, char *line);
115  | static int IsAdjacent(int asking_power, int other_power);
116  | 
117  | 
118  | /*
119  |  *  Static Data Object Definitions
120  |  */
121  | 
122  | static char *options[] =
123  | {"", "grey", "white", "anonymous", "unanonymous",
124  |  "anonymously", "unanonymously", "gray",
125  |  "-", "to all but", "+", "to",
126  |  "fake partial to all but", "fake parital -", "fake -",
127  |  "fake to all but", "fake partial to", "fake to",
128  |  "fake +", "fake broadcast", "no fake broadcast",
129  |  "fake", "no fake "};
130  | 
131  | static int values[] =
132  | {NONE, GREY, WHITE, GREY, WHITE,
133  |  GREY, WHITE, GREY,
134  |  ALLBUT, ALLBUT, PARTIAL, PARTIAL,
135  |  FAKEA, FAKEA, FAKEA,
136  |  FAKEA, FAKEP, FAKEP,
137  |  FAKEP, FAKEB, NOFAKEB,
138  |  FAKEB, NOFAKEB};
139  | 
140  | /*******************************************************************************
141  | NAME:  mail_press
142  | 
143  | DESCRIPTION
144  |     This procedure processes a 'press' or 'broadcast' command.  It parses the
145  |     rest of the command line (if any), checks the validity of the command, and
146  |     if valid then sets the appropriate flags so that the main command parser
147  |     will interpret the following lines as a press message (to be either sent or
148  |     ignored, as necessary).
149  | 
150  |     Press commands can have up to three types of options:  a colour (white or
151  |     grey), a partial delivery list (either players to send the message to, or
152  |     players not to send the message to), and a 'fake' option (fake the message
153  |     as a broadcast, fake it as going to certain players, fake it as not going
154  |     to certain players, or override a default fake broadcast).  None of these
155  |     options can be specified more than once.
156  | 
157  | REVISION HISTORY
158  |     DATE        NAME         REASON
159  |     ----------- ------------ ---------------------------------------------------
160  |     ?? ??? 1987 K.Lowe       Original development.
161  |     10 Mar 1993 Positron     Tell Master the truth about the source and desti-
162  |                              nation(s) of the press/broadcast message.
163  |     31 Mar 1994 C.Marcus,Jr. Complete redesign/rewrite, to allow 'press to m' to
164  |                              always work no matter what press settings are.
165  |     28 Apr 1994 C.Marcus,Jr. Always include the sender's address in press to
166  |                              Master.
167  |     23 May 1994 C.Marcus,Jr. Correct parsing of press options.
168  | 
169  | INTERFACE DEFINITION
170  |     Calling Sequence:
171  |         mail_press(s, need_opts);
172  | 
173  |     Inputs:
174  |         s         := Pointer to command line, following 'press' or 'broadcast'
175  |                      keyword
176  |         need_opts := TRUE if options must be specified, FALSE if options are
177  |                      optional (sorry about that wording!)
178  | 
179  |     Outputs:
180  |         broadcast (global)    := TRUE if command is good, else FALSE
181  |         broad_part (global)   := TRUE if press is only to some players, FALSE
182  |                                  if press is to everybody
183  |         broad_allbut (global) := TRUE if partial list is list of players to
184  |                                  *not* receive message, FALSE if partial list
185  |                                  is players to receive message
186  |         broad_read (global)   := TRUE if message is to be read and sent, else
187  |                                  FALSE
188  |         broad_skip (global)   := TRUE if message is to be read and discarded,
189  |                                  else FALSE
190  |         broad_list (global)   := List of power letters to which message is to
191  |                                  be sent (broad_allbut = FALSE) or not sent
192  |                                  (broad_allbut = TRUE)
193  | 
194  | ALGORITHM
195  |     [Note:  global return variables were initialized when broadcast files were
196  |         opened, and do not need to be initialized here.]
197  | 
198  |     Initialize local flags as necessary.
199  |     Parse the command line options, if any, setting local flags only.
200  |     Validate command options against game flags.
201  |     If command is valid, write press header to broadcast and/or master's broad-
202  |         cast file(s).
203  |     Set global return variables according to validity of local flags.
204  | *******************************************************************************/
205  | 
206  | void mail_press(char *s, int need_opts)
207  | {
208  | /*
209  |  *  Dynamic Data Object Definitions
210  |  */
211  | 	int bad_cmd = 0,	/* Is this a bad command? */
212  | 	 have_opts = 0;		/* Have we processed any options? */
213  | 	static char fake_list[sizeof(broad_list)],	/* List of fake destination powers */
214  | 	 game_name[32],		/* " in <name>:" string buffer */
215  | 	 line[1024],		/* Line to be sent to other players */
216  | 	 mline[1024],		/* Line to be sent to Master */
217  | 	 part_list[sizeof(broad_list)];
218  | 	/* List of destination powers */
219  | 	int color = NONE,	/* Is press grey, white, or default? */
220  | 	 count = 0,		/* Number of entries in part_list */
221  | 	 fake = NONE,		/* Is this message faked? */
222  | 	 fake_count = 0,	/* Number of entries in fake_list */
223  | 	 i,			/* Return value from lookfor() */
224  | 	 partial = NONE;	/* Is this message to some only? */
225  | 	char lpower;
226  | 	int xctr;
227  | 
228  | /*
229  |  *  End of Definitions
230  |  */
231  | 
232  | /*  Parse the command line options, if any, setting local flags only.  */
233  | 	sprintf(subjectline, "%s:%s - %s Diplomacy Notice ml_press", JUDGE_CODE, dipent.name, dipent.phase);
234  | 
235  | 	master_press = 0;
236  | 	master_only_press = 0; 
237  | 
238  | 	while (*s != '\0') {
239  | 		s = lookfor(s, options, nentry(options), &i);
240  | 		if (!i) {
241  | 			while (isspace(*s))
242  | 				++s;
243  | 			if (*s != '\0') {
244  | 				fprintf(rfp, "Unrecognized press/broadcast option '%s'.\n", s);
245  | 				bad_cmd = 1;
246  | 			}
247  | 			break;
248  | 		} else {
249  | 			have_opts = 1;
250  | 			switch (values[i]) {
251  | 
252  | 			case GREY:
253  | 				if (color) {
254  | 					fprintf(rfp,
255  | 						"Multiple 'colors' specified in press command.\n");
256  | 					bad_cmd = 1;
257  | 				} else {
258  | 					color = GREY;
259  | 				}
260  | 				break;
261  | 
262  | 			case WHITE:
263  | 				if (color) {
264  | 					fprintf(rfp,
265  | 						"Multiple 'colors' specified in press command.\n");
266  | 					bad_cmd = 1;
267  | 				} else {
268  | 					color = WHITE;
269  | 				}
270  | 				break;
271  | 
272  | 			case PARTIAL:
273  | 				if (partial) {
274  | 					fprintf(rfp,
275  | 						"Conflicting options specified in press command.\n");
276  | 					bad_cmd = 1;
277  | 				} else {
278  | 					partial = PARTIAL;
279  | 				}
280  | 				bad_cmd |= parse_powers(&s, part_list, sizeof(part_list),
281  | 							&count,1);
282  | 				break;
283  | 
284  | 			case ALLBUT:
285  | 				if (partial) {
286  | 					fprintf(rfp,
287  | 						"Conflicting options specified in press command.\n");
288  | 					bad_cmd = 1;
289  | 				} else {
290  | 					partial = ALLBUT;
291  | 				}
292  | 				bad_cmd |= parse_powers(&s, part_list, sizeof(part_list),
293  | 							&count,1);
294  | 				break;
295  | 
296  | 			case FAKEB:
297  | 				if (fake) {
298  | 					fprintf(rfp,
299  | 						"Conflicting options specified in press command.\n");
300  | 					bad_cmd = 1;
301  | 				} else {
302  | 					fake = FAKEB;
303  | 				}
304  | 				break;
305  | 
306  | 			case FAKEP:
307  | 				if (fake) {
308  | 					fprintf(rfp,
309  | 						"Conflicting options specified in press command.\n");
310  | 					bad_cmd = 1;
311  | 				} else {
312  | 					fake = FAKEP;
313  | 				}
314  | 				bad_cmd |= parse_powers(&s, fake_list, sizeof(fake_list),
315  | 							&fake_count,1);
316  | 				break;
317  | 
318  | 			case FAKEA:
319  | 				if (fake) {
320  | 					fprintf(rfp,
321  | 						"Conflicting options specified in press command.\n");
322  | 					bad_cmd = 1;
323  | 				} else {
324  | 					fake = FAKEA;
325  | 				}
326  | 				bad_cmd |= parse_powers(&s, fake_list, sizeof(fake_list),
327  | 							&fake_count,1);
328  | 				break;
329  | 
330  | 			case NOFAKEB:
331  | 				if (fake) {
332  | 					fprintf(rfp,
333  | 						"Conflicting options specified in press command.\n");
334  | 					bad_cmd = 1;
335  | 				} else {
336  | 					fake = NOFAKEB;
337  | 				}
338  | 				break;
339  | 			}
340  | 		}
341  | 	}
342  | 
343  | /*  Validate command options against game flags:  first, if options were
344  |    required, were they specified?  */
345  | 
346  | 	if (need_opts && !have_opts) {
347  | 		fprintf(rfp,
348  | 			"You have specified no options.  If you want to send a plain\n%s",
349  | 			"message to everyone, please use the BROADCAST command.\n");
350  | 		bad_cmd = 1;
351  | 
352  | /*  Master can send any kind of press he wants, no matter what the settings.  */
353  | 
354  | 	} else if (dipent.players[player].power == MASTER) {
355  | 	master_press = 1;
356  | 		/*  Good command by definition, so don't do any more checking.  */
357  | 	   if (partial == PARTIAL && count == 1) {
358  | 		/* Keep this between master and sender only */
359  | 		master_only_press = 1;
360  | 	   }
361  | 
362  | /*  Anyone can send to Master (only), no matter what the settings (but it won't
363  |    be grey press, and he better not try to fake it!).  */
364  | 
365  | 	} else if ((partial == PARTIAL) && (count == 1) &&
366  | 		   (power(part_list[0]) == MASTER)) {
367  | 		color = WHITE;
368  | 		master_press = 1;
369  | 		master_only_press = 1; /* Only for master and sender */
370  | 		if (fake) {
371  | 			fprintf(rfp,
372  | 				"Shame!  Attempting to fake out the Master is not allowed.\n");
373  | 			fake = NONE;
374  | 		}
375  | /*  Checking for normal (i.e., not from or to Master) press:  first, is press
376  |    allowed from non-observers (i.e. players)?  */
377  | 
378  | 	} else {
379  | 		if ((dipent.flags & F_NOWHITE) && !(dipent.flags & F_GREY) && !is_observer(player)) {
380  | 			fprintf(rfp, "Game '%s' does not allow press.\n", dipent.name);
381  | 			bad_cmd = 1;
382  | 
383  | /*  If this is from an Observer, is observer press allowed?  */
384  | 
385  | 		} else if ((dipent.flags & F_OBNONE) && is_observer(player)) {
386  | 			fprintf(rfp, "Game '%s' does not allow observer press.\n",
387  | 				dipent.name);
388  | 			bad_cmd = 1;
389  | 
390  | /* If a minor phase and no minor press allowed, reject it */
391  | 		} else if ((dipent.xflags & XF_NOMINORPRESS) && 
392  | 			    (dipent.phase[5] == 'B' || dipent.phase[5] == 'R')) {
393  | 			fprintf(rfp, "Game '%s' does not allow press in minor phases.\n",
394  | 				dipent.name);
395  | 			bad_cmd = 1;
396  | 
397  | /*  If this is grey press, is it allowed?  */
398  | 
399  | 		} else {
400  | 			if (color == GREY) {
401  | 				if (!(dipent.flags & F_GREY)) {
402  | 					fprintf(rfp, "Game '%s' does not allow grey press.\n",
403  | 						dipent.name);
404  | 					bad_cmd = 1;
405  | 				} else if ((dipent.flags & F_OBWHITE) && is_observer(player)) {
406  | 					fprintf(rfp,
407  | 						"Game '%s' does not allow grey press from observers.\n",
408  | 						dipent.name);
409  | 					bad_cmd = 1;
410  | 				}
411  | /*  If this is white press, is it allowed?  */
412  | 
413  | 			} else if (color == WHITE) {
414  | 				if (dipent.flags & F_NOWHITE) {
415  | 					fprintf(rfp, "Game '%s' does not allow white press.\n",
416  | 						dipent.name);
417  | 					bad_cmd = 1;
418  | 				}
419  | /*  Colour not specified; set default colour (Master defaults to white press;
420  |    observers default to white if ObWhite is set; otherwise, default
421  |    is grey if NoWhite is set, or if Grey is set and DefWhite is NOT set, or
422  |    white in any other case).  */
423  | 
424  | 			} else {
425  | 				if (((dipent.flags & F_OBWHITE) && is_observer(player)) ||
426  | 				    (dipent.players[player].power == MASTER)) {
427  | 					color = WHITE;
428  | 				} else {
429  | 					color = (((dipent.flags & F_GREY) &&
430  | 					 !(dipent.flags & F_DEFWHITE)) ||
431  | 						 (dipent.flags & F_NOWHITE)) ? GREY : WHITE;
432  | 				}
433  | 			}
434  | 
435  | /*  If this is partial press, is it allowed?  */
436  | 
437  | 			if (partial) {
438  | 			    if (is_observer(player)) {
439  | 				if (dipent.flags & (F_OBNONE | F_OBWHITE )) {
440  | 					fprintf(rfp,"Game '%s' does not allow observer partial press.\n",
441  | 						dipent.name);
442  | 					bad_cmd = 1;
443  | 				}
444  | 			    } else {
445  | 				if (dipent.flags & F_NOPARTIAL) {
446  | 					fprintf(rfp, "Game '%s' does not allow partial press.\n",
447  | 						dipent.name);
448  | 					bad_cmd = 1;
449  | 				}
450  | 			    }
451  | 			}
452  | 
453  | /*  If this is a no-fake-broadcast, is it allowed?  */
454  | 
455  | 			if (fake == NOFAKEB) {
456  | 				if (!(dipent.flags & F_FAKE) && (dipent.flags & F_DEFFAKE)) {
457  | 					fprintf(rfp,
458  | 						"Game '%s' only allows partial press to fake broadcast.\n",
459  | 						dipent.name);
460  | 					bad_cmd = 1;
461  | 				}
462  | /*  If this is a fake broadcast, is it allowed?  */
463  | 
464  | 			} else if (fake == FAKEB) {
465  | 				if (!(dipent.flags & F_FAKE) && !(dipent.flags & F_DEFFAKE)) {
466  | 					fprintf(rfp, "Game '%s' does not allow fake press.\n",
467  | 						dipent.name);
468  | 					bad_cmd = 1;
469  | 				}
470  | /*  If this is a fake partial, is it allowed?  */
471  | 
472  | 			} else if (fake) {
473  | 				if (dipent.flags & F_NOPARTIAL) {
474  | 					fprintf(rfp, "Game '%s' does not allow partial press.\n",
475  | 						dipent.name);
476  | 					bad_cmd = 1;
477  | 				} else if (!(dipent.flags & F_FAKE) &&
478  | 					   (dipent.flags & F_DEFFAKE)) {
479  | 					fprintf(rfp,
480  | 						"Game '%s' only allows partial press to fake broadcast.\n",
481  | 						dipent.name);
482  | 					bad_cmd = 1;
483  | 				} else if (!(dipent.flags & F_FAKE)) {
484  | 					fprintf(rfp, "Game '%s' does not allow fake press.\n",
485  | 						dipent.name);
486  | 					bad_cmd = 1;
487  | 				}
488  | /*  Fake option not specified; default to fake broadcast if message is partial
489  |    and partial fakes broadcast.  */
490  | 
491  | 			} else {
492  | 				if (partial && (dipent.flags & F_DEFFAKE)) {
493  | 					fake = FAKEB;
494  | 				}
495  | 			}
496  | 		}
497  | 	}
498  | 
499  | /* Have we submitted orders yet? */
500  | 	if (!(dipent.phase[6] == 'X') && !GAME_PAUSED)
501  | 	{
502  | 		if((!(dipent.players[player].status & SF_MOVED)) && (dipent.players[player].status & SF_MOVE) && (dipent.x2flags & X2F_MUSTORDER))
503  | 		{
504  | 			if(!((count == 1) && (power(part_list[0]) == MASTER)))
505  | 			{
506  | 				/* don't want to send this ... */
507  | 				fprintf(rfp,"Game %s does not allow press from players who have not sent complete orders.\n", dipent.name);
508  | 				bad_cmd = 1;
509  | 			}
510  | 		}
511  | 		/* OK, let's see if press from late powers is allowed */
512  | 
513  | 		if (WAITING(dipent.players[player].status)) {
514  | 			/* it is not, let us see if this power is late and not pressing to master */
515  | 		    if (!((count == 1) && (power(part_list[0]) == MASTER))) {
516  | 			/* We are waiting for him, see if messag is only to master */
517  | 			if (time(NULL) > dipent.deadline) { 
518  | 		          if (dipent.xflags & XF_NOLATEPRESS) {
519  | 			  	fprintf(rfp, "Game '%s' does not allow press from late players.\n",
520  |                                                 dipent.name);
521  | 				fprintf(rfp, "You must submit moves for ALL your units before pressing.\n");
522  |                                 bad_cmd = 1;
523  | 			  } else {
524  | 			    /* you can press when late, but it's not really good so... */
525  | 			        fprintf(rfp, 
526  | 				"Note: You are marked as late and should really have\n      corrected this before sending press.\n\n");
527  | 			  }
528  | 			}
529  | 		    }
530  | 		}
531  | 	}
532  | 
533  | 	if (partial && !GAME_PAUSED) {
534  | 		if (dipent.x2flags & X2F_TOUCHPRESS && !master_press && dipent.phase[6] != 'X') {
535  | 		    xctr =0;
536  |                     while ((part_list[xctr] = toupper(part_list[xctr])) != '\0') {
537  | 		        if (!IsAdjacent(dipent.players[player].power, power(part_list[xctr]))) {
538  | 		            fprintf(rfp, "You are not currently adjacent to %s.\n",
539  | 			    powers[power(part_list[xctr])]);
540  | 			    bad_cmd = 1;
541  | 		        }
542  | 		        xctr++;
543  | 		    }
544  | 		}
545  |         } else if (!GAME_PAUSED) {
546  |             if (dipent.x2flags & X2F_TOUCHPRESS && !master_press && dipent.phase[6] != 'X')  {
547  |                 if ( dipent.players[player].status & SF_BROAD_SENT) {
548  |                        fprintf(rfp,"Sorry, you have already sent one broadcast: wait until next turn!\n");
549  |                        bad_cmd = 1;
550  |                 } else {
551  |                        dipent.players[player].status |= SF_BROAD_SENT;
552  | 		}
553  |             }
554  |         }
555  | 
556  | 	if (GAME_PAUSED && !master_press && !master_only_press) {
557  | 		fprintf(rfp,"Sorry, you cannot press (except to the master alone) when the game is paused.\n");
558  |                        bad_cmd = 1;
559  | 	}
560  | 
561  | 			
562  | /*  If command is invalid, discard the following text.  */
563  | 
564  | 	if (bad_cmd) {
565  | 		fprintf(rfp, "\nNo press/broadcast message sent, discarding text:\n\n");
566  | 		broad_skip = 1;
567  | 		errorflag++; /* Bug 281, set error flag if press rejected */
568  | 
569  | /*  If command is valid, write press header to broadcast and/or master's
570  |    broadcast file(s):  first, tell the sender what we're doing.  */
571  | 
572  | 	} else {
573  | 		if (partial) {
574  | 			sprintf(line, "Message sent to %s", (partial == ALLBUT) ?
575  | 				"all but " : "");
576  | 			list_powers(line, part_list);
577  | 			strcat(line, ":\n");
578  | 		} else {
579  | 			strcpy(line, "Broadcast message sent:\n");
580  | 		}
581  | 		print_line(rfp, line);
582  | 
583  | /*  Tell the recipients what we want them to believe, and tell the Master the
584  |    truth:  if it's grey partial press (or we want them to think so), the other
585  |    players get "Message to ", while the Master gets "Message [from <address> as
586  |    <power>] to ".  */
587  | 
588  | 		if ((partial && (fake != FAKEB)) || (fake == FAKEP) || (fake == FAKEA)) {
589  | 			if (color == GREY) {
590  | 				sprintf(subjectline, "%s:%s - %s Press to ", JUDGE_CODE, dipent.name, dipent.phase);
591  | 
592  | 				strcpy(line, "Message to ");
593  | 				sprintf(mline, "Message [from %s as %s] to ",
594  | 					dipent.players[player].address,
595  | 				   powers[dipent.players[player].power]);
596  | 
597  | /*  If it's white partial press, either in a non-Gunboat game or from the
598  |    Master, the other players and the Master get "Message from <address> as
599  |    <power> to ".  */
600  | 
601  | 			} else {
602  | 				if (!(dipent.flags & F_GUNBOAT) ||
603  | 				    (dipent.players[player].power == MASTER)) {
604  | 					lpower = dipent.pl[dipent.players[player].power];
605  | 					sprintf(subjectline, "%s:%s - %s Press from %c to ", JUDGE_CODE, dipent.name, dipent.phase, lpower);
606  | 
607  | 					sprintf(line, "Message from %s as %s to ", raddr,
608  | 						powers[dipent.players[player].power]);
609  | 					sprintf(mline, "Message from %s as %s to ", raddr,
610  | 						powers[dipent.players[player].power]);
611  | 
612  | /*  If it's white partial press from an observer or player (Gunboat game), the
613  |    other players get "Message from <power> to ", while the Master gets "Message
614  |    from [<address> as] <power> to ".  */
615  | 
616  | 				} else {
617  | 					lpower = dipent.pl[dipent.players[player].power];
618  | 					sprintf(subjectline, "%s:%s - %s Press from %c to ", JUDGE_CODE, dipent.name, dipent.phase, lpower);
619  | 
620  | 					sprintf(line, "Message from %s to ",
621  | 						powers[dipent.players[player].power]);
622  | 					sprintf(mline, "Message from [%s as] %s to ",
623  | 					  dipent.players[player].address,
624  | 						powers[dipent.players[player].power]);
625  | 				}
626  | 			}
627  | 
628  | /*  If the partial press is to all but certain powers (or we wish them to think
629  |    so), add "all but ".  */
630  | 
631  | 			if ((fake == FAKEA) || (!fake && (partial == ALLBUT))) {
632  | 				strcat(subjectline, "all but ");
633  | 				strcat(line, "all but ");
634  | 				strcat(mline, "all but ");
635  | 			}
636  | /*  List the destination (or supposed destination) powers.  */
637  | 			xctr = 0;
638  | 			while ((fake_list[xctr] = toupper(fake_list[xctr])) != '\0') xctr++;
639  | 			xctr =0;
640  | 			while ((part_list[xctr] = toupper(part_list[xctr])) != '\0') xctr++;
641  | 
642  | 			strcat(subjectline, ((fake == FAKEP) || (fake == FAKEA)) ? fake_list :
643  | 				part_list);
644  | 
645  | 			list_powers(line, ((fake == FAKEP) || (fake == FAKEA)) ? fake_list :
646  | 				    part_list);
647  | 			list_powers(mline, ((fake == FAKEP) || (fake == FAKEA)) ? fake_list
648  | 				    : part_list);
649  | 
650  | /*  If it's grey broadcast press (or we want them to think so), the other
651  |    players get "Broadcast message", while the Master gets "Broadcast message
652  |    [from <address> as <power>]".  */
653  | 
654  | 		} else {
655  | 			if (color == GREY) {
656  | 				sprintf(subjectline, "%s:%s - %s Broadcast", JUDGE_CODE, dipent.name, dipent.phase);
657  | 
658  | 				strcpy(line, "Broadcast message");
659  | 				sprintf(mline, "Broadcast message [from %s as %s]",
660  | 					dipent.players[player].address,
661  | 				   powers[dipent.players[player].power]);
662  | 
663  | /*  If it's white broadcast press, either in a non-Gunboat game or from the
664  |    Master, the other players and the Master get "Broadcast message from
665  |    <address> as <power>".  */
666  | 
667  | 			} else {
668  | 				if (!(dipent.flags & F_GUNBOAT) ||
669  | 				    (dipent.players[player].power == MASTER)) {
670  | 					lpower = dipent.pl[dipent.players[player].power];
671  | 					sprintf(subjectline, "%s:%s - %s Broadcast from %c", JUDGE_CODE, dipent.name, dipent.phase, lpower);
672  | 
673  | 					sprintf(line, "Broadcast message from %s as %s", raddr,
674  | 						powers[dipent.players[player].power]);
675  | 					sprintf(mline, "Broadcast message from %s as %s", raddr,
676  | 						powers[dipent.players[player].power]);
677  | 
678  | /*  If it's white broadcast press from an observer or player (Gunboat game), the
679  |    other players get "Broadcast message from <power>", while the Master gets
680  |    "Broadcast message from [<address> as] <power>".  */
681  | 
682  | 				} else {
683  | 					lpower = dipent.pl[dipent.players[player].power];
684  | 					sprintf(subjectline, "%s:%s - %s Broadcast from %c", JUDGE_CODE, dipent.name, dipent.phase, lpower);
685  | 
686  | 					sprintf(line, "Broadcast message from %s",
687  | 						powers[dipent.players[player].power]);
688  | 					sprintf(mline, "Broadcast message from [%s as] %s",
689  | 					  dipent.players[player].address,
690  | 						powers[dipent.players[player].power]);
691  | 				}
692  | 			}
693  | 		}
694  | 
695  | 
696  | /*  Make sure the Master knows who it's really to.  */
697  | 
698  | 		if (fake && (fake != NOFAKEB)) {
699  | 			if (partial) {
700  | 				if (partial == ALLBUT) {
701  | 					strcat(mline, " [really to all but ");
702  | 				} else {
703  | 					strcat(mline, " [really to ");
704  | 				}
705  | 				list_powers(mline, part_list);
706  | 				strcat(mline, "]");
707  | 			} else {
708  | 				strcat(mline, " [really to everyone]");
709  | 			}
710  | 		}
711  | /*  Finish up with the game name, and print it.  */
712  | 
713  | 		sprintf(game_name, " in '%s':\n", dipent.name);
714  | 		strcat(line, game_name);
715  | 		strcat(mline, game_name);
716  | 		print_line(rfp, line);
717  | 		fprintf(bfp, "%s\n", line);	/* The previous call has put in the new-
718  | 						   lines except the extra at the end, so
719  | 						   we don't have to call print_line(). */
720  | 		print_line(mbfp, mline);
721  | 
722  | /*  Set global return variables according to validity of local flags.  */
723  | 
724  | 		broadcast = 1;
725  | 		broad_part = (partial != NONE);
726  | 		broad_allbut = (partial == ALLBUT);
727  | 		broad_read = 1;
728  | 		if (partial)
729  | 			strcpy(broad_list, part_list);
730  | 	}
731  | 	return;
732  | }
733  | /*******************************************************************************
734  | NAME:  parse_powers
735  | 
736  | DESCRIPTION
737  |     This procedure parses a list of power letters, checking that each letter is
738  |     valid, and that not too many are specified, and stores them in the specified
739  |     array.
740  | 
741  | REVISION HISTORY
742  |     DATE        NAME         REASON
743  |     ----------- ------------ ---------------------------------------------------
744  |     31 Mar 1994 C.Marcus,Jr. Original development (extracted from original
745  |                              mail_press() procedure).
746  | 
747  | INTERFACE DEFINITION
748  |     Calling Sequence:
749  |         error = parse_powers(&s, &list, size, &count);
750  | 
751  |     Inputs:
752  |         s    := Pointer to command line, at beginning of power letters list
753  |         size := Size of list array
754  | 	put_output: =1 if to go to rfp, else not.
755  | 
756  |     Outputs:
757  |         s     := Updated pointer, at next character after power letters list
758  |         error := TRUE if parsing error occurred, else FALSE
759  |         list  := Character array containing validated power letters
760  |         count := Number of valid power letters parsed
761  | 
762  | ALGORITHM
763  |     Initialize count and list array, and skip any spaces in command line.
764  |     While not at end of command line or at space:
765  |         If next character is valid power letter, and room remains in array,
766  |             store the power letter and count it, else issue error message.
767  |     Endwhile.
768  | *******************************************************************************/
769  | 
770  | int parse_powers(char **s, char *list, size_t size, int *count, int put_output)
771  | {
772  | /*
773  |  *  Dynamic Data Object Definitions
774  |  */
775  | 	int error = 0;		/* Parsing error occurred? */
776  | 	char *next = list;	/* Place to store next letter */
777  | 	int power_used[MASTER+1];
778  |         int i;
779  | /*
780  |  *  End of Definitions
781  |  */
782  | 
783  | /*  Initialize count and list array, and skip any spaces in command line.  */
784  | 
785  | 	for (i=0; i <= MASTER; i++) power_used[i] = 0;
786  | 	
787  | 	*count = 0;
788  | 	memset((void *) list, (int) '\0', size);
789  | 	while (isspace(**s))
790  | 		++(*s);
791  | 
792  | /*  While not at end of command line or at space:  If next character is valid
793  |    power letter, and room remains in array, store the power letter and count
794  |    it, else issue error message.  */
795  | 
796  | 	while (**s && !isspace(**s)) {
797  | 		power_used[power(**s)]++;
798  | 		if (!power(**s)) {
799  | 			if (put_output) fprintf(rfp, "Unknown power specification '%c'.\n", **s);
800  | 			error = 1;
801  | 		} else if (next >= list + size) {
802  | 			if (put_output) fprintf(rfp, "Too many power specifications.\n");
803  | 			error = 1;
804  | 		} else if (power_used[power(**s)] == 2 ) {
805  | 			if (put_output) fprintf(rfp, "Power '%c' repeated in list. \n", **s);
806  | 			error = 1;
807  | 		} else {
808  | 			*next++ = **s;
809  | 			++(*count);
810  | 		}
811  | 		++(*s);
812  | 	}
813  | 	return error;
814  | }
815  | /*******************************************************************************
816  | NAME:  list_powers
817  | 
818  | DESCRIPTION
819  |     This procedure takes a list of power letters, and adds the corresponding
820  |     names to the indicated line to be printed out.
821  | 
822  | REVISION HISTORY
823  |     DATE        NAME         REASON
824  |     ----------- ------------ ---------------------------------------------------
825  |     31 Mar 1994 C.Marcus,Jr. Original development (extracted from original
826  |                              mail_press() procedure).
827  | 
828  | INTERFACE DEFINITION
829  |     Calling Sequence:
830  |         list_powers(&line, &list);
831  | 
832  |     Inputs:
833  |         list := Character array containing valid power letters
834  | 
835  |     Outputs:
836  |         line := Character string to which power names are concatenated
837  | 
838  | ALGORITHM
839  |     For each letter in list:
840  |         Concatenate corresponding power name to output line.
841  |         If two or more letters remain, add a comma; else, if only one letter
842  |             remains, add "and".
843  |     Endloop.
844  | *******************************************************************************/
845  | 
846  | void list_powers(char *line, char *list)
847  | {
848  | /*
849  |  *  Dynamic Data Object Definitions
850  |  */
851  | 
852  | /*
853  |  *  End of Definitions
854  |  */
855  | 
856  | /*  For each letter in list:  Concatenate corresponding power name to output
857  |    line.  */
858  | 
859  | 	for ( /* list pointer already set */ ; *list; ++list) {
860  | 		strcat(line, powers[power(*list)]);
861  | 
862  | /*  If two or more letters remain, add a comma; else, if only one letter
863  |    remains, add "and".  */
864  | 
865  | 		if (*(list + 1)) {
866  | 			if (*(list + 2)) {
867  | 				strcat(line, ", ");
868  | 			} else {
869  | 				strcat(line, " and ");
870  | 			}
871  | 		}
872  | 	}
873  | 	return;
874  | }
875  | /*******************************************************************************
876  | NAME:  print_line
877  | 
878  | DESCRIPTION
879  |     This procedure takes a line of output text, breaks it into pieces of a
880  |     defined size or smaller (breaking only at spaces), and prints it to the
881  |     specified output file.  An additional newline is added at the end of the
882  |     line.  It is assumed that there are no imbedded newlines in the line before
883  |     processing starts (except possibly the last character, if a following blank
884  |     line is desired).
885  | 
886  |     Note that upon return, the newlines inserted to break up the line are still
887  |     present, so that the line may be printed again (perhaps to a different out-
888  |     put file) without calling print_line() a second time.  However, the termina-
889  |     ting newline is not added to the array.
890  | 
891  | REVISION HISTORY
892  |     DATE        NAME         REASON
893  |     ----------- ------------ ---------------------------------------------------
894  |     31 Mar 1994 C.Marcus,Jr. Original development (extracted from original
895  |                              mail_press() procedure).
896  | 
897  | INTERFACE DEFINITION
898  |     Calling Sequence:
899  |         print_line(outf, &line);
900  | 
901  |     Inputs:
902  |         outf := File pointer to output file
903  |         line := Character string to be printed
904  | 
905  |     Outputs:
906  |         line := Character string as printed, possibly with embedded newlines
907  |         Text written to specified output file.
908  | 
909  | ALGORITHM
910  |     Loop until length of last segment is less than cutoff point:
911  |         Starting from cutoff point, search backward for last space, and change
912  |             it to a newline.
913  |     Endloop.
914  |     Print the line (with terminating newline).
915  | *******************************************************************************/
916  | 
917  | static void print_line(FILE * outf, char *line)
918  | {
919  | /*
920  |  *  Dynamic Data Object Definitions
921  |  */
922  | 	char *head, *tail;	/* Ptrs. to ends of string segment */
923  | /*
924  |  *  End of Definitions
925  |  */
926  | 
927  | /*  Loop until length of last segment is less than cutoff point:  Starting
928  |    from cutoff point, search backward for last space, and change it to a new-
929  |    line.  */
930  | 
931  | 	for (head = line, tail = line + CUTOFF_LENGTH;
932  | 	     strlen(head) > CUTOFF_LENGTH;
933  | 	     head = tail + 1, tail = head + CUTOFF_LENGTH) {
934  | 		while (!isspace(*tail))
935  | 			--tail;
936  | 		*tail = '\n';
937  | 	}
938  | 
939  | /*  Print the line (with terminating newline).  */
940  | 
941  | 	fprintf(outf, "%s\n", line);
942  | 	return;
943  | }
944  | 
945  | /* Find out if a power is adjacent to another */
946  | 
947  | static int IsAdjacent(int asking_power, int other_power)
948  | {
949  |     int u, u1, c=0, b=0, adjacent = 0;
950  | 
951  |     if (asking_power == other_power)
952  |         return 1;  /* Always adjacent to self! */
953  | 
954  |     if (other_power == OBSERVER)
955  |         return 0;  /* Never close to observer */
956  | 
957  |     if (other_power == MASTER)
958  |         return 1;  /* Always close to master */
959  | 
960  |     if (!dipent.pr_valid) {
961  |                 /* pr[] must be filled to run this code */
962  |                 po_init();
963  |                 gamein();
964  |                 dipent.pr_valid++;
965  |     }
966  | 
967  | 
968  | /* OK, look though all my units, and see if anyone of them is next to other power's units */
969  | 
970  | /* Do this by pretending to be a wing, and seeing if can move there */
971  | 
972  |     for (u=1; u <= nunit && !adjacent; u++) {
973  |         if (unit[u].owner == asking_power) {
974  |             unit[++nunit].type = 'W';
975  |             unit[nunit].loc = unit[u].loc; /* Add dummy wing unit to test moveability */
976  |             for (u1 = 1; u1 < nunit && !adjacent; u1++) {
977  |                 if (unit[u1].owner == other_power)
978  |                     if (valid_move(nunit, unit[u1].loc, &c, &b))
979  |                         adjacent++;
980  |             }
981  |             nunit--;  /* Remove dummy unit */
982  | 	}
983  |     }
984  | 
985  |     return adjacent;
986  | }
987  |