1    | /*
2    |  * $Log: dipent.c,v $
3    |  * Revision 1.20  2003/07/17 22:59:29  millis
4    |  * Bug 185
5    |  *
6    |  * Revision 1.19  2003/07/16 14:50:56  millis
7    |  * Used D_X2FLAGS to allow default setting for X2FLAGS for games (if desired)
8    |  *
9    |  * Revision 1.18  2003/06/20 00:21:02  millis
10   |  * Tried to fix it correctly!
11   |  *
12   |  * Revision 1.17  2003/06/19 23:43:18  millis
13   |  * Added a repeat test flag.
14   |  *
15   |  * Revision 1.16  2003/06/19 23:27:55  millis
16   |  * Bug 181, bailout recovery only for control game, and adjust deadlines
17   |  * for non-control games.
18   |  *
19   |  * Revision 1.15  2003/05/14 19:01:22  millis
20   |  * Don't adjust mach files in 'A' season on bailout
21   |  *
22   |  * Revision 1.14  2003/05/13 00:07:26  millis
23   |  * Bug 110, move on process deadline by 24 hours on bailout recovery
24   |  *
25   |  * Revision 1.13  2003/05/12 23:47:59  millis
26   |  * Fix bug 110, shift process time out on a timewarp.
27   |  *
28   |  * Revision 1.12  2003/04/16 04:31:32  millis
29   |  * Fixed a bug that zapped the x/x2/flags settings on getdipent calls
30   |  *
31   |  * Revision 1.11  2003/01/13 22:38:51  millis
32   |  * merged in from ustv
33   |  *
34   |  * Revision 1.10  2002/08/27 22:27:50  millis
35   |  * Updated for automake/autoconf functionality
36   |  *
37   |  * Revision 1.9  2001/10/20 12:11:11  miller
38   |  * Merged in changes from DEMA and USTV
39   |  *
40   |  * Revision 1.8.2.2  2001/10/19 23:29:08  dema
41   |  * Allow powers with spaces in their names to be used
42   |  *
43   |  * Revision 1.8.2.1  2001/10/15 00:00:24  ustv
44   |  * Added reading/writing of dipent.x2flags
45   |  *
46   |  * Revision 1.8  2001/07/15 09:14:18  greg
47   |  * added support for game directories in a sub directory
48   |  *
49   |  * Revision 1.7  2001/07/08 22:54:49  miller
50   |  * Set default correctly for rrded, and add TIME_TOLERANCE usage from dip.conf
51   |  *
52   |  * Revision 1.6  2001/07/01 23:19:29  miller
53   |  * Default for XF_COASTAL
54   |  *
55   |  * Revision 1.5  2001/06/24 05:30:29  nzmb
56   |  * Added read/write capability for new dipent variables (dedapplied, orded,
57   |  * rrded) used in the plyrdata and new deadline systems.
58   |  *
59   |  * Revision 1.4  2001/05/26 11:20:34  miller
60   |  * Do not notify time-warp for shifts < 1 minute
61   |  *
62   |  * Revision 1.3  2001/01/05 22:27:53  miller
63   |  * Fix to test using '==' not '='
64   |  * (made all games erroneously of Chaos variant when upgrading from older dip.master format.
65   |  *
66   |  * Revision 1.2  2000/11/14 14:27:37  miller
67   |  * Added handling of new XF_:FLAGS , and absence data elements in master.dip
68   |  * Used gerenric flags to handle variants (not specificif tests)
69   |  *
70   |  * Revision 1.1  1998/02/28 17:49:42  david
71   |  * Initial revision
72   |  *
73   |  * Revision 1.2  1997/02/16 20:43:18  davidn
74   |  * Additions to dipent structure and associated code, to allow duplex variants.
75   |  * Command is "set players n".
76   |  *
77   |  * Revision 1.1  1996/10/20 12:29:45  rpaar
78   |  * Morrolan v9.0
79   |  */
80   | 
81   | /*
82   |  * dipent.c *  Copyright 1987, Lowe. * *  Diplomacy is a trademark of the
83   |  * Avalon Hill Game Company, Baltimore, *  Maryland, all rights reserved;
84   |  * used with permission. * *  Redistribution and use in source and binary
85   |  * forms are permitted *  provided that it is for non-profit purposes,
86   |  * that this and the  *  above notices are preserved and that due credit
87   |  * is given to Mr. *  Lowe. * *  DATE        NAME         REASON *
88   |  * ----------- ------------ ----------------------------------------- *
89   |  * ?? ??? 1987 Ken Lowe     He wrote it *  29 Dec 1996 David Norman
90   |  * Addition of dipent.no_of_players *  29 Dec 1996 David Norman Protection 
91   |  * against killing dip.master added 
92   |  * 26 Nov 1999 Millis Miller Added player late_count to payer structure
93   |  *                           Also added use of xflags structure.
94   |  * 26 May 2001 Mario Becroft Added code for dipent.orded,dipent.rrded, 
95   |  *	       Tim Miller    and dipent.dedapplied, all part of the new
96   |  *			     dedication systems.
97   |  */
98   | 
99   | #include <fcntl.h>
100  | #include <stdlib.h>
101  | #include <string.h>
102  | #include <sys/stat.h>
103  | #include <sys/types.h>
104  | #include <time.h>
105  | #include <unistd.h>
106  | 
107  | #include "config.h"
108  | #include "dip.h"
109  | #include "defaults.h"
110  | #include "functions.h"
111  | #include "diplog.h"
112  | 
113  | extern int Dflg;
114  | 
115  | char *ctime();
116  | char *lookfor();
117  | 
118  | void putseq(FILE * fp, char *s, sequence * seq);
119  | void gettime(char *line, long *time);
120  | void getplay(char *line, Player * p);
121  | void putplay(FILE * fp, Player * p, int dopw);
122  | 
123  | int time_warp = 0; /* Set to 1 when a time warp was detected */
124  | /************************************************************************/
125  | 
126  | int getdipent(FILE * fp)
127  | {
128  | 
129  | /*
130  |  *  Read the next game entry from the master file.
131  |  */
132  | 
133  | 	int i, j, tempvp, tempplayers;
134  | 	int old_flags, old_xflags, old_x2flags;  /* Remember flags settings! */
135  | 	time_t now;
136  | 	unsigned char line[1000];
137  | 	char *s; 
138  | 	char *malloc();
139  | 	static int recover_print = 0;
140  | 
141  | 	memset(&dipent, 0, sizeof(dipent));
142  | 	if (!fgets(line, sizeof(line), fp))
143  | 		return 0;
144  | 	i = sscanf(line, "%s%s%s%d%d%d%d%x%d%d%x%d%x%d", dipent.name, dipent.seq, dipent.phase,
145  | 		   &dipent.access, &dipent.variant,
146  | 		   &dipent.level, &dipent.dedicate,
147  | 		   &dipent.flags, &tempvp, &tempplayers,
148  | 		   &dipent.xflags, &dipent.max_absence_delay,
149  | 		   &dipent.x2flags, &dipent.num_homes);
150  | 
151  | 	if (i == 7) {
152  | 		dipent.flags = F_NONMR;
153  | 		i = 8;
154  | 	}
155  | 	if (i == 8) {
156  | 		tempvp = 0;
157  | 		i = 9;
158  | 	}
159  | 	if (i == 9) {
160  | 		tempplayers = 0;
161  | 		i = 10;
162  | 	}
163  | 	if (i == 10) {
164  | 		dipent.xflags = 0;
165  | 		if ((dipent.variant == V_h31)
166  |                     || (dipent.variant == V_h32)
167  |                     || (dipent.variant == V_classical)
168  |                     || (dipent.variant == V_chaos)) {
169  | 			dipent.xflags |= XF_BUILD_ANYCENTRES;
170  | 		}
171  | 		if (dipent.variant == V_aberration) dipent.xflags |= XF_BUILD_ONECENTRE;
172  | 		i = 11;
173  | 	}
174  | 	if (i == 11) {
175  | 		dipent.max_absence_delay = 0;
176  | 		dipent.rrded = 1.000;
177  | 		dipent.orded = 0.000; /* Set ded settings for migrated games */
178  | 		i = 12;
179  | 	}
180  | 
181  |         if (i == 12) {
182  |                 if (dipent.variant == V_machiavelli)
183  |                     dipent.xflags |= XF_COASTAL_CONVOYS;
184  |                 i = 13;
185  | 		dipent.x2flags = 0;
186  |         }
187  | 	if (i == 13) {
188  | 	    dipent.num_homes = 0;
189  | 	    i = 14;
190  | 	}
191  | 
192  | 	if (i != 14) {
193  | 		fprintf(stderr, "Bad header in master file (returned %d).\n%s\n", i ,line);
194  | 		bailout(E_FATAL);
195  | 	}
196  | 	/* tempcentres will remember centres setting */
197  | 	old_flags = dipent.flags;
198  | 	old_xflags = dipent.xflags;
199  | 	old_x2flags = dipent.x2flags;
200  | 	SETNP(dipent.variant);
201  | 	dipent.xflags = old_xflags;
202  | 	dipent.x2flags = old_x2flags;
203  | 	dipent.flags = old_flags;
204  | 	dipent.has_natives = GetNativeIndex();
205  | 
206  | 	if (tempvp != 0)
207  | 		dipent.vp = tempvp;
208  | 
209  | 	if (tempplayers != 0) {
210  | 		dipent.no_of_players = tempplayers;
211  | 	} else {
212  | 		dipent.no_of_players = dipent.np;
213  | 	}
214  | 
215  | 	while (fgets(line, sizeof(line), fp) && *line != '-') {
216  | 		switch (*line) {
217  | 
218  | 		case 'A':
219  | 		case 'B':
220  | 			memset(&dipent.builds, 0, sizeof(sequence));
221  | 			if (getseq(stderr, line, &dipent.builds))
222  | 				bailout(E_FATAL);
223  | 			break;
224  | 
225  | 		case 'C':
226  | 			line[strlen(line) - 1] = '\0';
227  | 			for (s = line; !isspace(*s); s++);
228  | 			while (isspace(*s))
229  | 				s++;
230  | 			strncpy(dipent.comment, s, sizeof(dipent.comment) - 1);
231  | 			break;
232  | 
233  | 		case 'D':
234  | 			gettime(line, &dipent.deadline);
235  | 			break;
236  | 
237  | 		case 'E':
238  | 			line[strlen(line) - 1] = '\0';
239  | 			for (s = line; !isspace(*s); s++);
240  | 			while (isspace(*s))
241  | 				s++;
242  | 			strncpy(dipent.epnum, s, sizeof(dipent.epnum) - 1);
243  | 			break;
244  | 
245  | 		case 'G':
246  | 			gettime(line, &dipent.grace);
247  | 			break;
248  | 
249  | 		case 'M':
250  | 			memset(&dipent.movement, 0, sizeof(sequence));
251  | 			if (getseq(stderr, line, &dipent.movement))
252  | 				bailout(E_FATAL);
253  | 			break;
254  | 
255  | 		case 'N':
256  | 			line[strlen(line) - 1] = '\0';
257  | 			for (s = line; !isspace(*s); s++);
258  | 			while (isspace(*s))
259  | 				s++;
260  | 			strncpy(dipent.bn_mnnum, s, sizeof(dipent.bn_mnnum) - 1);
261  | 			break;
262  | 
263  | 		case 'P':
264  | 			gettime(line, &dipent.process);
265  | 			break;
266  | 
267  | 		case 'R':
268  | 			memset(&dipent.retreat, 0, sizeof(sequence));
269  | 			if (getseq(stderr, line, &dipent.retreat))
270  | 				bailout(E_FATAL);
271  | 			break;
272  | 
273  | 		case 'S':
274  | 			gettime(line, &dipent.start);
275  | 			break;
276  | 
277  | 		case 'Y':
278  | 			dipent.dedapplied = strtol( &(line[22]), NULL, 10);
279  | 			break;
280  | 		case 'O':
281  | 			sscanf( &(line[16]),"%f", &dipent.orded);
282  | 			break;
283  | 
284  | 		case 'T':
285  | 			sscanf( &(line[26]),"%f", &dipent.rrded);
286  | 			break;
287  | 
288  | 		case '_':
289  | 			if (dipent.n > 0) {
290  | 				line[strlen(line) - 1] = '\0';
291  | 				for (s = line; !isspace(*s); s++);
292  | 				while (isspace(*s))
293  | 					s++;
294  | 				strcpy(dipent.players[dipent.n - 1].pref, s);
295  | 			}
296  | 			break;
297  | 
298  | 		default:
299  | 			if (dipent.n >= MAXPLAYERS) {
300  | 				fprintf(stderr, "Too many players for game '%s'.\n", dipent.name);
301  | 				bailout(E_FATAL);
302  | 			}
303  | 			*dipent.players[dipent.n].pref = '\0';
304  | 			getplay(line, &dipent.players[dipent.n++]);
305  | 		}
306  | 	}
307  | 	if (Dflg > 1)
308  | 		fprintf(log_fp, "Getdipent returns: '%s'.\n", dipent.name);
309  | 
310  | 	if (!strcmp(dipent.name, "control")) {
311  | 		time(&now);
312  | 		/* MLM 26/5/2001 only notify warp on shift more than TIME_TOLERANCE */
313  | 		if (dipent.process && (dipent.start > (now +TIME_TOLERANCE) || 
314  | 		    now > (TIME_TOLERANCE + dipent.process))) {
315  | 			fprintf(stderr, "Current date %24.24s should be between...\n", ctime(&now));
316  | 			fprintf(stderr, "Control dates %24.24s ", ctime(&dipent.start));
317  | 			fprintf(stderr, "< %24.24s.\n", ctime(&dipent.process));
318  | 			fprintf(stderr, "Time warp indicated.  GM notified.\n");
319  | 			sprintf(line, "/dev/null 'Diplomacy time warp'");
320  | 			MailOut(line, GAMES_MASTER);
321  | 			/* bailout(E_FATAL); */
322  | 			/* Try to fix time warp by advancing deadline */
323  | 			deadline(NULL, 1);
324  | 			time_warp = 1;
325  | 			DIPNOTICE("TimeWarp detected.");
326  | 
327  | 			/* If recovering from bailout, notify the GAMES_MASTER */
328  |            		if (bailout_recovery && !recover_print) {
329  |                     	    sprintf(line, "/dev/null 'Bailout recovery initiated'");
330  | 			    MailOut(line, GAMES_MASTER);
331  |                             recover_print = 1;
332  | 			}
333  | 			
334  | 		} else {
335  | 		    time_warp = 0;
336  | 		}
337  | 		dipent.start = now - 1;
338  | 		dipent.process = now + 168 * HRS2SECS;
339  | 		for (j = i = 0; i < dipent.n; i++)
340  | 			j += strlen(dipent.players[i].address) + 2;
341  | 		if ((s = notifies = malloc(j + 1))) {
342  | 			for (i = 0; i < dipent.n; i++) {
343  | 				if (dipent.players[i].power == MASTER)
344  | 					*s++ = '+';
345  | 				strcpy(s, dipent.players[i].address);
346  | 				s += strlen(s) + 1;
347  | 			}
348  | 			*s = '\0';
349  | 		} else {
350  | 			notifies = "*";
351  | 		}
352  | 
353  | 	} else {
354  | 	    /* Non control game, check for a time-warp or bailout recovery set */
355  |                 if ((time_warp || bailout_recovery) && dipent.phase[5] != 'A') {
356  |                 /* Try to fix the warp/recovery by adjusting deadline */
357  |                 /* Rather simplistic, but will do for now */
358  |                     deadline(NULL,1);
359  | 
360  |                  /* Bug 110: Also, shift out the process date by 24 hours if sooner */
361  | #define PROCESS_SHIFT (24*60*60)
362  |                     if (dipent.process && (dipent.process < now + PROCESS_SHIFT))
363  |                         dipent.process += PROCESS_SHIFT;
364  |                }
365  | 
366  |         }
367  | 	return 1;
368  | }
369  | 
370  | /***********************************************************************/
371  | 
372  | void putdipent(FILE * fp, int dopw)
373  | {
374  | 
375  | /*
376  |  * Write the current entry out to the master file. 
377  |  */
378  | 
379  | 	int i;
380  | 	char line[1000];
381  | 
382  | 	fprintf(fp, "%-8.8s  %-8.8s  %-8.8s  %d %d %d %d %x %d %d %x %d %x %d\n", 
383  | 		dipent.name, dipent.seq,
384  | 		dipent.phase, dipent.access, dipent.variant,
385  | 		dipent.level, dipent.dedicate, dipent.flags, dipent.vp,
386  | 		dipent.no_of_players, dipent.xflags,
387  | 		dipent.max_absence_delay,
388  |                 dipent.x2flags, /* This indicates version 0.8.9 onwards */
389  | 		dipent.num_homes /* This indicates version 1.1.1 onwards */
390  | 	);
391  | 
392  | 	if (dipent.process)
393  | 		fprintf(fp, "Process   %24.24s (%ld)\n",
394  | 			ctime(&dipent.process), dipent.process);
395  | 	if (dipent.deadline)
396  | 		fprintf(fp, "Deadline  %24.24s (%ld)\n",
397  | 			ctime(&dipent.deadline), dipent.deadline);
398  | 	if (dipent.start)
399  | 		fprintf(fp, "Start     %24.24s (%ld)\n",
400  | 			ctime(&dipent.start), dipent.start);
401  | 	if (dipent.grace)
402  | 		fprintf(fp, "Grace     %24.24s (%ld)\n",
403  | 			ctime(&dipent.grace), dipent.grace);
404  | 	fprintf(fp, "Ontime rat. min %.3f\n",dipent.orded);
405  |         fprintf(fp, "T = resignation ratio max %.3f\n",dipent.rrded);
406  |         fprintf (fp,"Yet_Applied_deadline? %d\n",
407  |                                                 dipent.dedapplied);
408  | 	if (*dipent.comment)
409  | 		fprintf(fp, "Comment   %s\n", dipent.comment);
410  | 	if (*dipent.epnum)
411  | 		fprintf(fp, "EP_number %s\n", dipent.epnum);
412  | 	if (*dipent.bn_mnnum)
413  | 		fprintf(fp, "Number_BM %s\n", dipent.bn_mnnum);
414  | 
415  | 	putseq(fp, "Moves  ", &dipent.movement);
416  | 	putseq(fp, "Retreat", &dipent.retreat);
417  | 	putseq(fp, "Adjust ", &dipent.builds);
418  | 
419  | 	for (i = 0; i < dipent.n; i++) {
420  | 		putplay(fp, &dipent.players[i], dopw);
421  | 	}
422  | 	if (fprintf(fp, "-\n") == 0) {
423  | 		fprintf(stderr, "Error writing to dip.master. Disk error suspected. Bailing out\n");
424  | 		sprintf(line, "/dev/null 'File error writing dip.master'");
425  | 		MailOut(line, GAMES_MASTER);
426  | 		bailout(E_FATAL);
427  | 	}
428  | }
429  | 
430  | /***********************************************************************/
431  | 
432  | void newdipent(char *name, int variant)
433  | {
434  | 
435  | /*
436  |  * Build a new diplomacy master file entry. 
437  |  */
438  | 
439  | 	char dir[50];
440  | 
441  | 	/* TODO allow an arbitrary prefix.  E.g. "games/" */
442  | 	/*   done - greg  :-)  */
443  | 	sprintf(dir, "%s%s", GAME_DIR, name);
444  | 	mkdir(dir, 0777);
445  | 	strncpy(dipent.name, name, sizeof(dipent.name));
446  | 	strcpy(dipent.seq, "x0");
447  | 	strcpy(dipent.phase, sphase[variant]);
448  | 	dipent.access = D_ACCESS;
449  | 	dipent.level = D_LEVEL;
450  | 	dipent.flags = D_FLAGS;
451  | 	dipent.xflags = D_XFLAGS;
452  | 	dipent.x2flags = D_X2FLAGS;
453  | 	dipent.dedicate = D_DEDICATE;
454  | 	dipent.variant = variant;
455  | 	SETNP(variant);
456  | 	dipent.process = 0;
457  | 	dipent.deadline = 0;
458  | 	dipent.start = 0;
459  | 	dipent.grace = 0;
460  | 	dipent.movement.clock = D_MOVE_CLOCK;
461  | 	dipent.movement.mint = D_MOVE_MINT;
462  | 	dipent.movement.next = D_MOVE_NEXT;
463  | 	dipent.movement.grace = D_MOVE_GRACE;
464  | 	dipent.movement.delay = D_MOVE_DELAY;
465  | 	strcpy(dipent.movement.days, D_MOVE_DAYS);
466  | 	dipent.retreat.clock = D_RETREAT_CLOCK;
467  | 	dipent.retreat.mint = D_RETREAT_MINT;
468  | 	dipent.retreat.next = D_RETREAT_NEXT;
469  | 	dipent.retreat.grace = D_RETREAT_GRACE;
470  | 	dipent.retreat.delay = D_RETREAT_DELAY;
471  | 	strcpy(dipent.retreat.days, D_RETREAT_DAYS);
472  | 	dipent.builds.clock = D_BUILDS_CLOCK;
473  | 	dipent.builds.mint = D_BUILDS_MINT;
474  | 	dipent.builds.next = D_BUILDS_NEXT;
475  | 	dipent.builds.grace = D_BUILDS_GRACE;
476  | 	dipent.builds.delay = D_BUILDS_DELAY;
477  | 	strcpy(dipent.builds.days, D_BUILDS_DAYS);
478  | 	dipent.n = 0;
479  | 	dipent.orded = 0.000;
480  | 	dipent.rrded = 1.000;
481  | 	dipent.dedapplied = 0;
482  | 	dipent.no_of_players = dipent.np;
483  | 	dipent.max_absence_delay = D_MAX_ABSENCE_DELAY;
484  |         dipent.has_natives = GetNativeIndex();
485  | }
486  | 
487  | /***********************************************************************/
488  | 
489  | void testdipent(int seq, int variant)
490  | {
491  | 
492  | /*
493  |  * Initialize the dipent structure for the test case. 
494  |  */
495  | 
496  | 	int i;
497  | 	long now;
498  | 
499  | 	time(&now);
500  | 
501  | 	newdipent("test", variant);
502  | 	sprintf(dipent.seq, "%03d", seq);
503  | 	strcpy(dipent.phase, "?1901?");
504  | 
505  | 	dipent.process = now - 1;
506  | 	dipent.deadline = now - 1;
507  | 	dipent.start = now - 1;
508  | 	dipent.grace = now - 1;
509  | 	dipent.n = WILD_PLAYER;
510  | 	for (i = 0; i < dipent.n; i++) {
511  | 		dipent.players[i].power = i;
512  | 		dipent.players[i].status = 0;
513  | 		dipent.players[i].siteid = 0;
514  | 		dipent.players[i].userid = 0;
515  | 		dipent.players[i].late_count = 0;
516  | 		dipent.players[i].centres_blockaded = 0;
517  | 		strcpy(dipent.players[i].password, "spud");
518  | 		strcpy(dipent.players[i].address, "*");
519  | 	}
520  | 	dipent.players[0].power = power('o');
521  | 	strcpy(dipent.players[0].password, "spud");
522  | 	strcpy(dipent.players[0].address, GAMES_MASTER);
523  | }
524  | 
525  | /***********************************************************************/
526  | 
527  | int getseq(FILE * fp, char *line, sequence * seq)
528  | {
529  | 
530  | 	static char *keys[] =
531  | 	{"", "clock", "c", "min", "m", "next", "n",
532  | 	 "grace", "g", "delay", "de", "days", "day", "da"};
533  | 	static char action[] =
534  | 	{'x', 'c', 'c', 'm', 'm', 'n', 'n',
535  | 	 'g', 'g', 'd', 'd', 'D', 'D', 'D'};
536  | 	int i;
537  | 	char *p, *s, *t, word[30];
538  | 	float f;
539  | 
540  | 	for (s = line; !isspace(*s); s++);
541  | 	while (isspace(*s))
542  | 		s++;
543  | 
544  | 	while (*s) {
545  | 		s = lookfor(s, keys, nentry(keys), &i);
546  | 		switch (action[i]) {
547  | 		case 'c':
548  | 			if (sscanf(s, "%d", &i) != 1 || i > 1440) {
549  | 				fprintf(fp, "%sBad clock specification.\n", line);
550  | 				return 1;
551  | 			}
552  | 			seq->clock = i;
553  | 			break;
554  | 
555  | 		case 'm':
556  | 			if (sscanf(s, "%f", &f) != 1 || f < 0) {
557  | 				fprintf(fp, "%sBad minimum time specification.\n", line);
558  | 				return 1;
559  | 			}
560  | 			seq->mint = f;
561  | 			break;
562  | 
563  | 		case 'n':
564  | 			if (sscanf(s, "%f", &f) != 1 || f < 0) {
565  | 				fprintf(fp, "%sBad next time specification.\n", line);
566  | 				return 1;
567  | 			}
568  | 			seq->next = f;
569  | 			break;
570  | 
571  | 		case 'g':
572  | 			if (sscanf(s, "%f", &f) != 1 || f < 0) {
573  | 				fprintf(fp, "%sBad grace time specification.\n", line);
574  | 				return 1;
575  | 			}
576  | 			seq->grace = f;
577  | 			break;
578  | 
579  | 		case 'd':
580  | 			if (sscanf(s, "%f", &f) != 1 || f < 0) {
581  | 				fprintf(fp, "%sBad delay time specification.\n", line);
582  | 				return 1;
583  | 			}
584  | 			seq->delay = f;
585  | 			break;
586  | 
587  | 		case 'D':
588  | 			if (sscanf(s, "%10s", word) != 1) {
589  | 				fprintf(fp, "%s,Bad list of days specification.\n", line);
590  | 				return 1;
591  | 			}
592  | 			for (p = word, t = "SMTWTFS"; *t; t++, p++) {
593  | 				if (*p != *t && *p != tolower(*t) && *p != '-') {
594  | 					fprintf(fp, "%sBad list of days specified.\n", line);
595  | 					return 1;
596  | 				}
597  | 			}
598  | 			*p = '\0';
599  | 			strcpy(seq->days, word);
600  | 			if (!strcmp(seq->days, "-------")) {
601  | 				fprintf(fp, "%sAt least one day of the week must be allowed.\n", line);
602  | 				return 1;
603  | 			}
604  | 			break;
605  | 
606  | 		default:
607  | 			sscanf(s, "%s", word);
608  | 			fprintf(fp, "Invalid keyword %s.\n", word);
609  | 			return 1;
610  | 			break;
611  | 		}
612  | 
613  | 		while (*s && !isspace(*s))
614  | 			s++;
615  | 		while (isspace(*s))
616  | 			s++;
617  | 	}
618  | 	return 0;
619  | }
620  | 
621  | /***********************************************************************/
622  | 
623  | void putseq(FILE * fp, char *s, sequence * seq)
624  | {
625  | 	if (*seq->days)
626  | 		fprintf(fp,
627  | 			"%-9.9s clock %4d min %5.2f next %6.2f grace %6.2f delay %.2f days %s\n",
628  | 			s, seq->clock, seq->mint, seq->next, seq->grace, seq->delay, seq->days);
629  | }
630  | 
631  | /***********************************************************************/
632  | 
633  | void gettime(char *line, long *time)
634  | {
635  | 
636  | 	char *s, *t;
637  | 
638  | 	for (s = line; !isspace(*s); s++);
639  | 	while (isspace(*s))
640  | 		s++;
641  | 
642  | 	for (t = s; *t && *t != '('; t++);
643  | 	if (*t == '(' && (*time = atol(t + 1)))
644  | 		return;
645  | 
646  | 	if (jm(s, time)) {
647  | 		fprintf(stderr, "Error processing '%s' %s", dipent.name, line);
648  | 		bailout(E_FATAL);
649  | 	}
650  | }
651  | 
652  | /***********************************************************************/
653  | 
654  | void getplay(char *line, Player * p)
655  | {
656  | 
657  | 	int i;
658  | 	char c;
659  | 
660  | 	*p->pref = '\0';
661  | 	/* You'd better be really sure you've not messed with absence array limit! */
662  | 	i = sscanf(line, "%c%*s %x %d %d %d %d %s %s %d %d %d %ld %ld %ld %ld %ld %ldi %ld", &c, &p->status,
663  | 		   &p->units, &p->centers, &p->userid, &p->siteid,
664  | 		   p->password, p->address, &p->late_count, &p->centres_blockaded,
665  | 		   &p->absence_count, 
666  | 		   &p->absence_start[0], &p->absence_end[0],
667  | 		   &p->absence_start[1], &p->absence_end[1],
668  | 		   &p->absence_start[2], &p->absence_end[2],
669  | 		   &p->absence_total);
670  | 	switch (i)
671  | 	{
672  | 		case 8: {
673  | 		     /* versions prior to 0.8.7 which had no late count */
674  | 		     p->late_count = 0;  /* initialise it to something! */
675  | 		    }
676  | 		case 9: {
677  | 		     p->centres_blockaded = 0;
678  | 		}
679  | 		case 10: {
680  | 		     /* versions 0.8.7 and up are fine here! */
681  | 			p->absence_count = 0; 
682  |                    	p->absence_start[0] = p->absence_end[0] = 0;
683  | 			p->absence_start[1] = p->absence_end[1] = 0; 
684  |                         p->absence_start[2] = p->absence_end[2] = 0;
685  | 		    }
686  | 		case 17: {
687  | 			/* versions 0.8.7 and up are fine here! */
688  | 			p->absence_total = 0;
689  |                      break;
690  |                     }
691  | 		case 18: {
692  | 			/* OK, this is the latest one! */
693  | 		    }
694  | 
695  | 		
696  | 		default: {
697  | 		    /* OK, we've got a problem: bailout */	
698  | 		fprintf(stderr, "Bad player entry for '%s'.  Only found %d items.\n%s\n",
699  | 			dipent.name, i, line);
700  | 		bailout(E_FATAL);
701  | 		}
702  | 	}
703  | 	/*
704  | 	 * The following 2 lines are to intended to cover the removal of the
705  | 	 * alternate from the judge code.  This code will automatically
706  | 	 * replace alternates that might still be in the dip.master file and
707  | 	 * make them observers. 
708  | 	 */
709  | 	if (c == '%')
710  | 		c = 'o';
711  | 	if (!(p->power = power(c))) {
712  | 		fprintf(stderr, "Invalid power character: %s\n", line);
713  | 		bailout(E_FATAL);
714  | 	}
715  | }
716  | 
717  | void putplay(FILE * fp, Player * p, int dopw)
718  | {
719  | #define MAX_OUTPOWER 9
720  | 	char c;
721  | /* Outpower will allow powers with spaces in names to be used */
722  | 	char out_power[MAX_OUTPOWER];
723  | 	int i;
724  | 
725  | 	if (p->power >= 0) {
726  | 		strncpy(out_power, powers[p->power], MAX_OUTPOWER - 1);
727  | 		out_power[MAX_OUTPOWER-1] = '\0';
728  | 		for (i=0; out_power[i] != '\0' && i < MAX_OUTPOWER; i++) {
729  | 		    /* Substitute space with '_' to prevent crash on fscanf */
730  | 		    if (out_power[i] == ' ') out_power[i] = '_';
731  | 		}
732  | 		    
733  | 		if (isupper(c = dipent.pl[p->power]))
734  | 			c = tolower(c);
735  | 		fprintf(fp, "%c%-8s %4x %2d %2d %3d %5d %-12s %s %4d %2d %d %ld %ld %ld %ld %ld %ld %ld\n",
736  | 			c, &out_power[1],
737  | 		   p->status, p->units, p->centers, p->userid, p->siteid,
738  | 			dopw ? p->password : "xxx", p->address,p->late_count, p->centres_blockaded,
739  | 		   p->absence_count,
740  |                    p->absence_start[0], p->absence_end[0],
741  |                    p->absence_start[1], p->absence_end[1],
742  |                    p->absence_start[2], p->absence_end[2],
743  | 		   p->absence_total);
744  | 		if (*(p->pref))
745  | 			fprintf(fp, "_pref: %s\n", p->pref);
746  | 	}
747  | }
748  | 
749  | int countgames(void)
750  | {
751  | 	FILE *mf;
752  | 	char line[1024];
753  | 	int gamecount = 0;
754  | 	int len;
755  | 
756  | 	if (!(mf = fopen(MASTER_FILE, "r"))) {
757  | 		perror(MASTER_FILE);
758  | 	}
759  | 	do {
760  | 		fgets(line, sizeof(line), mf);
761  | 		if (feof(mf))
762  | 			break;
763  | 		/* TODO check for an error */
764  | 		len = strlen(line);
765  | 		if (len && line[len - 1] == '\n') {
766  | 			line[len - 1] = '\0';
767  | 		}
768  | 		if (!strcmp("-", line)) {
769  | 			gamecount++;
770  | 		}
771  | 	} while (!feof(mf));
772  | 
773  | 	fclose(mf);
774  | 
775  | 	/* the control game will be counted by above */
776  | 	return gamecount - 1;
777  | }