1    | 
2    | 	/*
3    | 	 * $Log: lib.c,v $
4    | 	 * Revision 1.21  2003/09/14 08:25:13  millis
5    | 	 * Fix bug 225
6    | 	 *
7    | 	 * Revision 1.20  2003/08/25 14:39:36  millis
8    | 	 * Fixed bug 220
9    | 	 *
10   | 	 * Revision 1.19  2003/07/23 00:11:43  millis
11   | 	 * Bug 192
12   | 	 *
13   | 	 * Revision 1.18  2003/07/19 14:04:27  millis
14   | 	 * fixed bug in address handling
15   | 	 *
16   | 	 * Revision 1.17  2003/07/17 00:01:29  millis
17   | 	 * Use MailOut to send emails
18   | 	 *
19   | 	 * Revision 1.16  2003/07/15 22:47:06  millis
20   | 	 * Fix Bug 185 (call smail for each email individually)
21   | 	 *
22   | 	 * Revision 1.15  2003/06/11 15:48:52  millis
23   | 	 * Remove the 'unit' text
24   | 	 *
25   | 	 * Revision 1.14  2003/02/18 14:05:28  millis
26   | 	 * Added display of new Cavalry and Artillery units
27   | 	 *
28   | 	 * Revision 1.13  2003/01/15 14:12:04  millis
29   | 	 * Merged from ustv
30   | 	 *
31   | 	 * Revision 1.12  2002/12/28 00:02:54  millis
32   | 	 * Fixed bug 77, adding wrap_char() function
33   | 	 *
34   | 	 * Revision 1.11  2002/08/27 22:27:52  millis
35   | 	 * Updated for automake/autoconf functionality
36   | 	 *
37   | 	 * Revision 1.10  2002/04/15 12:55:43  miller
38   | 	 * Multiple changes for blind & Colonial & setup from USTV
39   | 	 *
40   | 	 * Revision 1.9  2001/07/15 09:15:46  greg
41   | 	 * added support for game directories in a sub directory
42   | 	 * /.
43   | 	 *
44   | 	 * Revision 1.8  2001/07/08 22:55:37  miller
45   | 	 * Use CUSTODIAN name in better order
46   | 	 *
47   | 	 * Revision 1.7  2001/07/01 23:19:29  miller
48   | 	 * Add InformCustodians func
49   | 	 *
50   | 	 * Revision 1.6  2001/06/24 05:35:03  nzmb
51   | 	 * Reset dipent.dedapplied to 0 when new deadline is calculated.
52   | 	 *
53   | 	 * Revision 1.2  2000/11/14 14:27:37  miller
54   | 	 * Lots of changes, including
55   | 	 *  - get_die_magic() Get DIE_MAGIC value from .magic.dat (or cerate if not found)
56   | 	 *
57   | 	 * Revision 1.1  1998/02/28 17:49:42  david
58   | 	 * Initial revision
59   | 	 *
60   | 	 * Revision 1.2  1996/11/05 23:09:52  rpaar
61   | 	 * USIT changes to fix minor bugs
62   | 	 */
63   | 
64   | 	/*  lib.c
65   | 	 *  Copyright 1987, Lowe.
66   | 	 *
67   | 	 *  Diplomacy is a trademark of the Avalon Hill Game Company, Baltimore,
68   | 	 *  Maryland, all rights reserved; used with permission.
69   | 	 *
70   | 	 *  Redistribution and use in source and binary forms are permitted
71   | 	 *  provided that it is for non-profit purposes, that this and the 
72   | 	 *  above notices are preserved and that due credit is given to Mr.
73   | 	 *  Lowe.
74   | 	 *
75   | 	 *  Version History
76   | 	 *
77   | 	 *  Version Author          Date      Comments
78   | 	 * ------------------------------------------------------------------------
79   | 	 *  1       Ken Lowe        1987      Original Code
80   | 	 *  2       David Norman    19/05/96  Unobfuscate get_prov
81   |  *                                    Fix get_prov to cope with a province 
82   |  *                                    "New York" and an abbreviation "New"
83   |  *                                    for a different province
84   |  *                                    Fix get_prov to differ between "Laos"
85   |  *                                    and "Lao s"
86   |  */
87   | 
88   | #include <stdlib.h>
89   | #include <string.h>
90   | #include <time.h>
91   | #include <unistd.h>
92   | 
93   | #include "config.h"
94   | #include "dip.h"
95   | #include "functions.h"
96   | #include "porder.h"
97   | /* .magic.h no longer used */
98   | /*#include ".magic.h"*/
99   | #include "mail.h"
100  | 
101  | /* TODO make this static to the function, rather than global */
102  | char nowString[20];
103  | static long DIE_MAGIC;
104  | /****************************************************************************/
105  | 
106  | void archive(char *file, char *subject)
107  | {
108  | 
109  | 	/*
110  | 	 *  Archive will copy the specified file to the archive file for this
111  | 	 *  particular game.
112  | 	 */
113  | 
114  | 	long now;
115  | 	char name[60];
116  | 	FILE *ofp, *ifp;
117  | 
118  | 	sprintf(name, "%s%s/archive", GAME_DIR, dipent.name);
119  | 	if (!(ofp = fopen(name, "a"))) {
120  | 		perror(name);
121  | 		fprintf(log_fp, "Unable to append to archive file for '%s'.\n", dipent.name);
122  | 		bailout(1);
123  | 	}
124  | 	if (!(ifp = fopen(file, "r"))) {
125  | 		perror(file);
126  | 		fprintf(log_fp, "Unable to archive %s to %s.\n", file, name);
127  | 		bailout(1);
128  | 	}
129  | 	time(&now);
130  | 	fprintf(ofp, "Date:    %24.24s (%ld)\n", ctime(&now), now);
131  | 	fprintf(ofp, "Subject: %s\n\n", subject);
132  | 
133  | 	while (fgets(line, sizeof(line), ifp)) {
134  | 		if (strncmp(line, "::", 2) != 0)
135  | 			fputs(line, ofp);
136  | 	}
137  | 
138  | 	fputc('\n', ofp);
139  | 
140  | 	fclose(ifp);
141  | 	fclose(ofp);
142  | 
143  | }
144  | 
145  | /***********************************************************************/
146  | 
147  | 
148  | int die(int n, int s)
149  | {
150  | 
151  | 	/*
152  | 	 *  Generate the results of "n" dice with "s" sides each.
153  | 	 */
154  | 
155  | 	register int r = 0;
156  | 
157  | 	while (n-- > 0)
158  | 		r += ((rand() >> 1) % s) + 1;
159  | 	return r;
160  | 
161  | }
162  | /****** */
163  | long get_die_magic(void)
164  | {
165  | 	/*
166  | 	 * This function will see if a file called .magic.dat exists
167  | 	 * (assumed previosuly created). If so, it will set DIE_MAGIC to
168  | 	 * the value inside. If not, it will write a randon six-digit value
169  | 	 * to this file 
170  |  	 */
171  | 
172  |     /* First, make sure value is generated only once */
173  |     static int first_pass = 0;
174  |     FILE *rand_ptr = NULL;
175  |     int n = 0;
176  | 
177  |     if (!first_pass) { 
178  | 
179  |     first_pass = 1;
180  | 
181  |     rand_ptr = fopen("./.magic.dat", "r");
182  |     if (rand_ptr) {
183  | 	n = fscanf(rand_ptr, "%ld", &DIE_MAGIC);
184  | 	fclose(rand_ptr);
185  |     }
186  | 
187  |     if (!rand_ptr || n != 1) { 
188  |     /* read errors, generate and write a random number */
189  |         rand_ptr = fopen("./.magic.dat", "w");
190  | 	if (rand_ptr) {
191  | 	    DIE_MAGIC = rand() % 1000000;
192  | 	    n = fprintf(rand_ptr, "%6.6ld\n", DIE_MAGIC);
193  | 	    fclose(rand_ptr);
194  | 	}
195  | 	if (!rand_ptr || n != 7) {
196  | 		fprintf(log_fp, "Unable to write magic data to ./.magic.dat.\n");
197  |                 bailout(1);
198  | 	}
199  |     }
200  |   }
201  |   return DIE_MAGIC;
202  | }
203  | 
204  | 
205  | 
206  | 	
207  | /***********************************************************************/
208  | 
209  | void die_rolls(int seed)
210  | {
211  | 
212  | 	/*
213  | 	 *  Establish a starting seed for random number generation.  The seed 
214  | 	 *  is set to a predictable value to allow deterministic die rolls for 
215  | 	 *  replayability.  The "DIE_MAGIC" value is defined outside and can be 
216  | 	 *  any number as long as it is a constant and kept secret.  The purpose 
217  | 	 *  of it is so the players can't predict (by writing a little program 
218  | 	 *  at home) what the die rolls are going to be.
219  | 	 */
220  | 
221  | 	char *s;
222  | 	extern char *rflg;
223  | 
224  | 	for (s = rflg ? rflg : dipent.name; *s; s++)
225  | 		seed += *s;
226  | 
227  | 	for (s = dipent.phase; *s; s++)
228  | 		seed += *s;
229  | 
230  | 	srand(seed + get_die_magic());
231  | 
232  | }
233  | 
234  | /****************************************************************************/
235  | 
236  | int execute(char *command)
237  | {
238  | 
239  | 	/*
240  | 	 *  Execute the command and verify that it really happened -- a bug
241  | 	 *  somewhere was causing "system()" to return without actually
242  | 	 *  executing anything so outgoing mail got lost.  The executed command
243  | 	 *  must remove the "dip.xfail" file so that we know it did its thing.
244  | 	 */
245  | 
246  | 	int i, status;
247  | 	FILE *fp;
248  | 	long now;
249  | 
250  | 	for (i = 0;; i++) {
251  | 		time(&now);
252  | 		fprintf(log_fp, "%12.12s: Xcute: %s\n", ctime(&now) + 4, command);
253  | 
254  | 		if (!(fp = fopen("dip.xfail", "w"))) {
255  | 			fprintf(log_fp, "Unable to create dip.xfail.\n");
256  | 			perror("dip.xfail");
257  | 			bailout(E_FATAL);
258  | 		}
259  | 		fclose(fp);
260  | 
261  | 		if ((status = system(command))) {
262  | 			fprintf(log_fp, "System error %x executing: %s\n", status, command);
263  | 			if (i < 3) {
264  | 				fprintf(log_fp, "Will try again...\n");
265  | 				sleep(30);
266  | 				continue;
267  | 			} else {
268  | 				bailout(E_FATAL);
269  | 			}
270  | 		}
271  | 		break;
272  | 	}
273  | 
274  | 	if ((fp = fopen("dip.xfail", "r"))) {
275  | 		fprintf(log_fp, "dip.xfail not deleted properly.\n");
276  | 		bailout(E_FATAL);
277  | 	}
278  | 	return 0;
279  | 
280  | }
281  | 
282  | /****************************************************************************/
283  | 
284  | void ferrck(FILE * fp, int n)
285  | {
286  | 
287  | 	/*
288  | 	 *  Check for an error status on an output file before renaming over
289  | 	 *  the top of the master copy of the file.  This prevents disk full
290  | 	 *  and such from destroying all our data.
291  | 	 */
292  | 
293  | 	char *fmt;
294  | 
295  | 	fmt = "Disk full error number %d.  Bailing out!\n";
296  | 
297  | 	if (ferror(fp)) {
298  | 		fprintf(log_fp, fmt, n);
299  | 		fprintf(stderr, fmt, n);
300  | 		bailout(E_FATAL);
301  | 	}
302  | }
303  | 
304  | /****************************************************************************/
305  | 
306  |        static char *words[] =
307  |         {"0", "(north coast)", "(nc)", "/north coast", "/nc",
308  |          "(east coast)", "(ec)", "/east coast", "/ec",
309  |          "(west coast)", "(wc)", "/west coast", "/wc",
310  |          "(south coast)", "(sc)", "/south coast", "/sc"};
311  | 
312  |         static int coasts[] =
313  |         {0, NC, NC, NC, NC, EC, EC, EC, EC,
314  |          WC, WC, WC, WC, SC, SC, SC, SC};
315  | 
316  | /* Get Coast called to return coast index for passed string */
317  | 
318  | char *get_coast(coast_string, coast)
319  | char *coast_string;
320  | int *coast;
321  | {
322  | 	int ret_coast = XC;  /* default coast */
323  | 	char c_string[256];
324  | 	char *t = coast_string;
325  | 	char *s = c_string;
326  | 
327  | 	while (isspace(*t) && *t) t++;  /* Strip out leading blanks */
328  | 
329  | 	while (isprint(*t) && *t) {
330  | 		*s = *t; t++; s++;
331  | 	}
332  | 	*s = '\0';
333  | 
334  | 	/* Todo, work backwards, consuming non-prints until first printable char */
335  | 
336  | 	lookfor(c_string, words, nentry(words), &ret_coast);
337  | 
338  |         /* If there is a coast, decode its coast code */
339  | 
340  |         if (ret_coast) 
341  |             ret_coast = coasts[ret_coast];
342  | 	else
343  | 	    ret_coast = XC;
344  | 
345  | 	*coast = ret_coast;
346  | 
347  |     return t;
348  | }
349  | 
350  | /****************************************************************************/
351  | 
352  | char *get_prov(search_province, province_number, coast)
353  | char *search_province;		/* The province to search for */
354  | int *province_number;		/* OUTPUT: The number of the province */
355  | int *coast;			/* OUTPUT: The specified coast */
356  | 
357  | {
358  | 
359  | 	char *heap_walker;	/* Pointer to walk the province name heap */
360  | 	char *search_province_walker;	/* Pointer to walk the search province string */
361  | 
362  | 	char *search_province_remains = search_province;
363  | 	/* The part of the search province which was not used */
364  | 	int heap_white_space;	/* Flag to indicate white space in the heap */
365  | 	int province_white_space;	/* Flag to indicate white space in the search province */
366  | 
367  | 
368  | 	/*
369  | 	 *  Scan the input line for a suitable province name.
370  | 	 *
371  | 	 *  Exit: province_number = 0 if invalid province, otherwise province ordinal
372  | 	 *        coast           = coast specification if present.
373  | 	 *        Return          = the part of the search province which was not used
374  | 	 */
375  | 
376  | 	/* Initialise the province number to 0 */
377  | 
378  | 	*province_number = 0;
379  | 
380  | 	/* Initialise the heap walker to the beginning of the heap */
381  | 
382  | 	heap_walker = (char *) heap;
383  | 
384  | 	/* While we have not found the end of the heap */
385  | 
386  | 	while (*heap_walker) {
387  | 		/* Initialise the search province walker to the start of the search province string */
388  | 
389  | 		search_province_walker = search_province;
390  | 
391  | 		/* Initialise the white space flags */
392  | 
393  | 		heap_white_space = 0;
394  | 		province_white_space = 0;
395  | 
396  | 		/* Skip any spaces at the start of the search province */
397  | 
398  | 		while (isspace(*search_province_walker))
399  | 			search_province_walker++;
400  | 
401  | 		/* 
402  | 		 * While the characters in the search province and the heap match,
403  | 		 * and any spaces in the search province and the heap match up in
404  | 		 * locn, if not number or type, and the end of the heap is not
405  | 		 * found
406  | 		 */
407  | 
408  | 		while (*heap_walker
409  | 		       && heap_white_space == province_white_space
410  | 		       && toupper(*search_province_walker) == toupper(*heap_walker)) {
411  | 			/* Reset the white space flags */
412  | 
413  | 			heap_white_space = 0;
414  | 			province_white_space = 0;
415  | 
416  | 			/* Skip any space characters or .'s in the search province */
417  | 
418  | 			while (*++search_province_walker == '.' || isspace(*search_province_walker)) {
419  | 				/* Set the province white space flag */
420  | 
421  | 				province_white_space = 1;
422  | 			}
423  | 
424  | 			/* Skip and space characters or .'s in the heap */
425  | 
426  | 			while (*++heap_walker == '.' || isspace(*heap_walker)) {
427  | 				/* Set the heap white space flag */
428  | 
429  | 				heap_white_space = 1;
430  | 			}
431  | 		}
432  | 
433  | 		/* If we stopped due to finding the end of the name in the heap, and at the time, the next
434  | 		   character in the search province was one of ( ) - / ; , \n or a space character */
435  | 
436  | 		if (!*heap_walker && (!*search_province_walker || strchr("(-/);,\n", *search_province_walker) || isspace(*(search_province_walker - 1)))) {
437  | 			/* We have a match */
438  | 
439  | 			/* If it is the best match so far (best = longest) */
440  | 			/*
441  | 			 * It does not matter that we have not yet considered coasts in
442  | 			 * search_province_walker, as if a coast was found in the best
443  | 			 * match so far, then it will always be a better match anyway
444  | 			 */
445  | 
446  | 			if (search_province_walker > search_province_remains) {
447  | 				/* Read the province number out of the heap */
448  | 
449  | 				*province_number = *((unsigned char *) ++heap_walker);
450  | 
451  | 				/* Check if the next part of the search province is a coast, and if so, pick up which one */
452  | 
453  | 				search_province_walker = lookfor(search_province_walker, words, nentry(words), coast);
454  | 
455  | 				/* If there is a coast, decode its coast code */
456  | 
457  | 				if (*coast)
458  | 					*coast = coasts[*coast];
459  | 
460  | 				/* Store a pointer to the unused part of the search province string */
461  | 
462  | 				search_province_remains = search_province_walker;
463  | 			}
464  | 		}
465  | 		/* Skip any NULs in the heap */
466  | 
467  | 		while (*heap_walker++);
468  | 
469  | 		/* Step on one more character */
470  | 
471  | 		heap_walker++;
472  | 	}
473  | 
474  | 	if (*province_number) {
475  | 	    /* province found: check if allowed gateway or railway */
476  | 	    if (pr[*province_number].type == 'r' && !(dipent.x2flags && X2F_RAILWAYS) && dipent.name[0]) {
477  | 		    *province_number = 0;
478  | 		    search_province_remains = search_province;
479  | 		}
480  | 	    if (pr[*province_number].type == 'g' && !(dipent.x2flags && X2F_GATEWAYS) && dipent.name[0]) {
481  | 		    *province_number = 0;
482  | 		    search_province_remains = search_province;
483  | 		}
484  | 	}
485  | 	/* Return the part of the search string which was not used */
486  | 
487  | 	return search_province_remains;
488  | }
489  | 
490  | char *lookfor(char *l, char *w[], int len, int *n)
491  | {
492  |     return lookforv(l, w, len, n, 0);
493  | }
494  | 
495  | char *lookforv(char *l, char *w[], int len, int *n, int exact_word)
496  | {
497  | 
498  | 	/*
499  | 	 *  Look for a phrase in a table of phrases. The phrases are lower case
500  | 	 *  with a single blank where blanks are allowed or an octothorp (#)
501  | 	 *  where blanks (or parenthesis) are required.
502  | 	 *
503  | 	 *  Entry: len = number of phrases
504  | 	 *  Exit:  n = index into phrases, or zero if not found.
505  | 	 */
506  | 
507  | 	int i;
508  | 	char *t, *s;
509  | 
510  | 	for (i = 1; i < len; i++) {
511  | 		t = w[i];
512  | 		s = l;
513  | 		while (isspace(*s))
514  | 			s++;
515  | 		while (*t && *t == tolower(*s)) {
516  | 			t++;
517  | 			s++;
518  | 			if (*t == ' ') {
519  | 				while (isspace(*s))
520  | 					s++;
521  | 				t++;
522  | 			} else if (*t == '#') {
523  | 				if (!isspace(*s) && !isdigit(*s) && !strchr("(;/,)", *s))
524  | 					break;
525  | 				while (isspace(*s))
526  | 					s++;
527  | 				t++;
528  | 			}
529  | 		}
530  | 
531  | 		if (!*t && (!exact_word || ((!*s || isspace(*s) || strchr("(;/,)",*s))))) 
532  | 		{
533  | 			*n = i;
534  | 			while (isspace(*s))
535  | 				s++;
536  | 			return s;
537  | 		}
538  | 	}
539  | 	*n = 0;
540  | 	return l;
541  | }
542  | 
543  | int power(char c)
544  | {
545  | 
546  | 	/*
547  | 	 *  Return the ordinal of the specified power character.
548  | 	 */
549  | 
550  | 	int i;
551  | 
552  | 	if (islower(c))
553  | 		c = toupper(c);
554  | 	for (i = 1; i < MASTER + 1; i++)
555  | 		if (c == dipent.pl[i])
556  | 			return (i);
557  | 
558  | 	return 0;
559  | }
560  | 
561  | char *astype(char c, char cu)
562  | {
563  | 	if (c == 'x')
564  | 		if (cu == 'A')
565  | 			return "an";
566  | 		else
567  | 			return "a";
568  | 	else if (c == 'c')
569  | 		return ("a citizen's militia");
570  | 	else if (c == 'm')
571  | 		return ("an elite mercenary");
572  | 	else if (c == 'p')
573  | 		return ("an elite professional");
574  | 	else
575  | 		return ("an unknown");
576  | }
577  | 
578  | char *autype(char c)
579  | {
580  | 	if (c == 'A')
581  | 		return ("an army");
582  | 	else if (c == 'F')
583  | 		return ("a fleet");
584  | 	else if (c == 'G')
585  | 		return ("a garrison");
586  | 	else if (c == 'W')
587  | 		return ("a wing");
588  | 	else if (c == 'T')
589  | 		return ("an army/fleet");
590  | 	else if (c == 'S')
591  | 		return ("a spy");
592  | 	else if (c == 'C')
593  | 		return ("a cavalry");
594  | 	else if (c == 'R')
595  | 		return ("an artillery");
596  | 	else if (c ==' ')
597  | 		return "";
598  | 	else
599  | 		return ("a unit");
600  | }
601  | 
602  | char *bstype(char c)
603  | {
604  | 	if (c == 'x')
605  | 		return ("");
606  | 	else if (c == 'c')
607  | 		return ("citizen's militia ");
608  | 	else if (c == 'm')
609  | 		return ("elite mercenary ");
610  | 	else if (c == 'p')
611  | 		return ("elite professional ");
612  | 	else
613  | 		return ("unknown ");
614  | }
615  | 
616  | char *Stype(char c)
617  | {
618  | 	if (c == 'x')
619  | 		return ("");
620  | 	else if (c == 'c')
621  | 		return ("Citizen's Militia ");
622  | 	else if (c == 'm')
623  | 		return ("Elite Mercenary ");
624  | 	else if (c == 'p')
625  | 		return ("Elite Professional ");
626  | 	else
627  | 		return ("Unknown ");
628  | }
629  | 
630  | char *utype(char c)
631  | {
632  | 	if (c == 'A')
633  | 		return ("army");
634  | 	else if (c == 'F')
635  | 		return ("fleet");
636  | 	else if (c == 'G')
637  | 		return ("garrison");
638  | 	else if (c == 'W')
639  |                 return ("wing");
640  | 	else if (c == 'T')
641  | 		return ("army/fleet");
642  | 	else if (c == 'S')
643  | 		return ("spy");
644  | 	else if (c == 'C')
645  | 		return ("cavalry");
646  | 	else if (c == 'R')
647  | 		return ("artillery");
648  | 	else if (c == ' ')
649  | 		return "";
650  | 	else
651  | 		return ("unit");
652  | }
653  | 
654  | /****************************************************************************/
655  | 
656  | char *Utype(c)
657  | char c;
658  | {
659  | 	if (c == 'A')
660  | 		return ("Army");
661  | 	else if (c == 'F')
662  | 		return ("Fleet");
663  | 	else if (c == 'G')
664  | 		return ("Garrison");
665  | 	else if (c == 'W')
666  |                 return ("Wing");
667  | 	else if (c == 'T')
668  | 		return ("Army/Fleet");
669  | 	else if (c == 'S')
670  | 		return ("Spy");
671  | 	else if (c == 'C')
672  | 		return ("Cavalry");
673  | 	else if (c == 'R')
674  | 		return ("Artillery");
675  | 	else if (c == ' ')
676  | 		return "";
677  | 	else
678  | 		return ("Unit");
679  | }
680  | 
681  | /*
682  |  ** mov_type - return a string proper for unit type/province type
683  |  ** i.e. A Wing is 'over Spain' or 'over the Baltic'
684  |  ** A fleet in 'in Spain' or 'in the Baltic'
685  |  ** an army is 'in Spain' only
686  |  */
687  | 
688  | char *mov_type( int prov_index, int unit_index)
689  | {
690  | 	static char line[20];
691  | 	char prov_type; 
692  | 	char unit_type;
693  | 
694  | 	line[0] = '\0';
695  | 	prov_type = pr[prov_index].type;
696  | 	unit_type = unit[unit_index].type;
697  | 
698  | 	if (unit_type == 'W' )  {
699  | 		strcpy(line, "over");
700  | 		if (water(prov_index) ) {
701  | 			strcat(line, " the");
702  | 		}
703  | 	} else {
704  | 		if (water(prov_index) ) {
705  | 			strcpy(line, "in the");
706  | 		} else {
707  | 			strcpy(line, "in");
708  | 		}
709  | 	}
710  | 	return line;
711  | }
712  | 	
713  | 	
714  | void wrap(FILE * fp, char *buf, int pos, int indent)
715  | {
716  | 
717  | 	char *s, *t;
718  | 
719  | 	for (t = s = buf; *s; s++, pos++) {
720  | 		if (pos > 78) {
721  | 			while (*--s != ' ');
722  | 			*s = '\0';
723  | 			fputs(t, fp);
724  | 			fprintf(fp, "\n%*s", indent, "");
725  | 			*s++ = ' ';
726  | 			t = s;
727  | 			pos = indent;
728  | 		}
729  | 	}
730  | 	fputs(t, fp);
731  | 
732  | }
733  | /* Do a wrap bu around the passed character */
734  | void wrap_char(FILE * fp, char *buf, int pos, int indent, char w_char)
735  | {
736  | 
737  |         char *s, *t;
738  | 
739  |         for (t = s = buf; *s; s++, pos++) {
740  |                 if (pos > 78) {
741  | 			while (*--s != w_char);
742  |                         /* Will wrap at the NEXT space character or line-end */
743  | 			while (*++s != ' ' && *s != '\0');
744  |                         *s = '\0';
745  |                         fputs(t, fp);
746  |                         fprintf(fp, "\n%*s", indent, "");
747  |                         *s++ = ' ';
748  |                         t = s;
749  |                         pos = indent;
750  |                 }
751  |         }
752  |         fputs(t, fp);
753  | 
754  | }
755  | 
756  | /*
757  |  *  Capitalize a string.
758  |  */
759  | 
760  | char *strcap(char *string)
761  | {
762  | 	char *copy;
763  | 	int i;
764  | 
765  | 	/*
766  | 	 * Allocate a string buffer.  It's the CALLER's responsibility to
767  | 	 * free() the string when he's finished with it.
768  | 	 */
769  | 
770  | 	copy = (char *) malloc(strlen(string) + 1);
771  | 
772  | 	/*
773  | 	 * Copy the original string to it, capitalizing all lower-case letters
774  | 	 * that (1) are the first character; (2) follow a whitespace character;
775  | 	 * or (3) follow punctuation.  DON'T use 'toupper()', as that changes
776  | 	 * the character in the original string.  
777  | 	 */
778  | 
779  | 	if (copy) {
780  | 		for (i = 0; i < strlen(string); ++i) {
781  | 			if (islower(string[i]) && ((i == 0) || isspace(string[i - 1]) ||
782  | 					       ispunct(string[i - 1]))) {
783  | 				copy[i] = string[i] - 'a' + 'A';
784  | 			} else {
785  | 				copy[i] = string[i];
786  | 			}
787  | 		}
788  | 		copy[i] = '\0';
789  | 	}
790  | 	return copy;
791  | }
792  | 
793  | char *NowString(void)
794  | {
795  | 	long now;
796  | 
797  | 	time(&now);
798  | 
799  | 	sprintf(nowString, "%12.12s: ", ctime(&now) + 4);
800  | 	return (nowString);
801  | }
802  | 
803  | int FindPower(int power_index)
804  | {
805  | 	int i;
806  | 	for (i = 0; i < dipent.n; i++)
807  | 	{
808  | 		if (dipent.players[i].power == power_index) return i;
809  | 	}
810  | 	return i;
811  | }
812  | 
813  | 
814  | int IsBlank(char *text)
815  | {
816  |         char *t = text;
817  |         int non_blank = 0;
818  |         while (*t != '\0' && !non_blank) {
819  |             if (0 == isspace(*t)) non_blank = 1;
820  |             t++;
821  |         }
822  |         return !non_blank;
823  | }
824  | 
825  | int GetOneWord(char *text)
826  | {
827  |         char *t = text;
828  |         int non_blank = 0;
829  |         while (*t != '\0') {
830  |             if (non_blank == 1 && 0 != isspace(*t)) *t = '\0'; /*terminate at 1st non-blank*/
831  |             if (0 == isspace(*t)) non_blank = 1;
832  |             t++;
833  |         }
834  |         return non_blank;
835  | }
836  | 
837  | int deadline_recursive( sequence *seq, int new, int *rec_count);
838  | 
839  | /****************************************************************************/
840  | /* Look and see if any requested absences */
841  | /* and adjust deadline parameter accordingly if so */
842  | 
843  | int absence_adjust(long *deadline)
844  | {
845  |         int ret = 0; /* Set to 1 if adjusted times */
846  |         int i,j;
847  | 
848  |         for (i =0; i < dipent.n; i++ )
849  |         {
850  |                 for ( j=0; j < MAX_ABSENCES; j++)
851  |                 {
852  |                         if (dipent.players[i].absence_start[j] < *deadline ) {
853  |                                 if (dipent.players[i].absence_end[j] > *deadline ) {
854  | 				    /* Check if a player has a pending move and (no press or strictwait)
855  | 				       or game is quiet
856  | 				       or is the master before adjusting the deadline */
857  | 				    if ((dipent.players[i].status & SF_MOVE &&
858  |  					(dipent.flags & F_STRWAIT || DIPENT_NO_PRESS)) || 
859  | 				        (!(dipent.flags & F_STRWAIT) && !DIPENT_NO_PRESS) ||
860  | 					dipent.flags & F_QUIET ||
861  | 					dipent.players[i].power == MASTER)
862  | 				    {
863  |                                         /* OK, we're in a requested deadline period */
864  |                                         /* so boot up the deadline accordingly      */
865  |                                         *deadline = dipent.players[i].absence_end[j];
866  |                                         ret = 1; /* Changed deadline */
867  | 				    }
868  |                                 }
869  |                         }
870  |                 }
871  |         }
872  |         /* OK, now delete passed deadlines */
873  |         for (i =0; i < dipent.n; i++ )
874  |         {
875  |                 for ( j=0; j < MAX_ABSENCES; j++)
876  |                 {
877  |                     if (dipent.players[i].absence_start[j] != 0L ) {
878  | 
879  |                         if (dipent.players[i].absence_start[j] <= *deadline ) {
880  |                                 if (dipent.players[i].absence_end[j] <= *deadline ) {
881  |                                         /* OK, This deadline is no longer needed */
882  | 					/* but bump up total anyway */
883  | 					dipent.players[i].absence_total += 
884  | 					    (dipent.players[i].absence_end[j] - 
885  | 					    dipent.players[i].absence_end[j]);
886  |                                         dipent.players[i].absence_start[j] = 0L;
887  |                                         dipent.players[i].absence_end[j] = 0L;
888  |                                         dipent.players[i].absence_count--;
889  |                                 }
890  |                         }
891  |                     }
892  |                 }
893  |         }
894  |         return ret;
895  | }
896  | 
897  | /*************************************************************************/
898  | 
899  | int deadline(sequence *seq, int new)
900  | {
901  |         int rec_count = 0;
902  |         return deadline_recursive(seq, new, &rec_count);
903  | }
904  | 
905  | 
906  | /****************************************************************************/
907  | 
908  | 
909  | int deadline_recursive(sequence * seq, int new, int *rec_count)
910  | {
911  | 
912  | /*
913  |  *  Compute a new deadline for this dip entry.
914  |  */
915  | 
916  |         int i, k;
917  |         long now, temp;
918  |         struct tm *tm, *localtime();
919  |         int ret = 0;
920  | 
921  |         (*rec_count)++;
922  |         if (*rec_count > 50) return 0; /* safety code */
923  | 
924  |         time(&now);
925  |         if (dipent.phase[6] == 'X') {
926  |                 dipent.process = now + 168 * HRS2SECS;
927  |                 return 0;
928  |         }
929  |         if (!seq) {
930  |                 if (!(seq = dipent.phase[5] == 'M' ? &dipent.movement :
931  |                       dipent.phase[5] == 'R' ? &dipent.retreat :
932  |                       dipent.phase[5] == 'B' ? &dipent.builds : NULL)) {
933  |                         fprintf(stderr, "Invalid phase [%s] in deadline for '%s'.\n",
934  |                                 dipent.phase, dipent.name);
935  |                         fprintf(log_fp, "Invalid phase [%s] in deadline for '%s'.\n",
936  |                                 dipent.phase, dipent.name);
937  |                         return E_FATAL;
938  |                 }
939  |         }
940  |         /*
941  |            **  If the new flag is indicated, we assume a move has just completed
942  |            **  and we establish the next deadline.  We don't want to set the new
943  |            **  deadline earlier than the old one though.
944  |          */
945  | 
946  |         if (new) {
947  |                 temp = now + (int) (seq->next * HRS2SECS);
948  | 
949  | 		dipent.dedapplied = 0; 
950  | 
951  |                 if (temp < dipent.deadline)
952  |                         temp = dipent.deadline;
953  |                 /* OK, adjust the time requested according to pending absences */
954  |                 ret = absence_adjust(&temp);
955  | 
956  |                 if (seq->clock >= 0) {
957  |                         tm = localtime(&temp);
958  |                         i = seq->clock * 60 - ((tm->tm_hour * 60 + tm->tm_min) * 60 + tm->tm_sec);
959  |                         if (i < 0)
960  |                                 i += 24 * 60 * 60;
961  |                         temp += i;
962  |                 }
963  |                 for (k = 0; k < 8; k++) {
964  |                         tm = localtime(&temp);
965  |                         if (seq->days[tm->tm_wday] == '-')
966  |                                 temp += 24 * 60 * 60;
967  |                         else if (islower(seq->days[tm->tm_wday]) && tm->tm_hour < 12)
968  |                                 temp += (12 - tm->tm_hour) * 60 * 60 - tm->tm_min * 60 - tm->tm_sec;
969  |                         else
970  |                                 break;
971  |                 }
972  | 
973  |                 /* If old deadline was huge, keep it */
974  | 		dipent.deadline = max(temp,  dipent.deadline) ;
975  |                 dipent.process = temp;
976  |                 temp += (int) (seq->grace * HRS2SECS);
977  |                 if (dipent.flags & F_GRACEDAYS) {
978  |                         for (k = 0; k < 8; k++) {
979  |                                 tm = localtime(&temp);
980  |                                 if (seq->days[tm->tm_wday] == '-')
981  |                                         temp += 24 * 60 * 60;
982  |                                 else if (islower(seq->days[tm->tm_wday]) && tm->tm_hour < 12)
983  |                                         temp += (12 - tm->tm_hour) * 60 * 60 - tm->tm_min * 60 - tm->tm_sec;
984  |                                 else
985  |                                         break;
986  |                         }
987  |                 }
988  | 		/* If old grace was huge, keep it */
989  |                 dipent.grace =  max(temp,  dipent.grace);
990  |                 dipent.start = now + (int) (seq->mint * HRS2SECS);
991  |         }
992  |         /*
993  |            **  Figure out if we can bump up the process time.
994  |          */
995  | 
996  |         temp = now + (int) (seq->delay * HRS2SECS);
997  |         for (i = 0; i < dipent.n; i++) {
998  |                 if (dipent.players[i].power < 0)
999  |                         continue;
1000 | 
1001 |                 if (dipent.players[i].status & SF_PROCESS) {
1002 |                         temp = now - 1 * HRS2SECS;
1003 |                         dipent.players[i].status &= ~SF_PROCESS;
1004 |                 }
1005 |                 if (dipent.players[i].status & SF_WAIT && now < dipent.deadline) {
1006 |                         temp = dipent.deadline;
1007 |                         break;
1008 |                 }
1009 |                 if (WAITING(dipent.players[i].status)) {
1010 |                         temp = now < dipent.deadline ? dipent.deadline :
1011 |                             now > dipent.grace && dipent.flags & F_NONMR ? dipent.process :
1012 |                             dipent.grace;
1013 |                         break;
1014 |                 }
1015 |         }
1016 | 
1017 |         /*
1018 |          *  We don't want to advance the process time beyond the deadline
1019 |          *  here.  That will be done in 'process' when a reminder is sent
1020 |          *  out to those who haven't gotten their orders in yet.
1021 |          */
1022 | 
1023 |         if ((dipent.process < dipent.deadline && temp < dipent.deadline) ||
1024 |             temp < dipent.process)
1025 |                 dipent.process = max(dipent.start, temp);
1026 | 
1027 |         if (ret == 1) {
1028 |                 /* We have adjusted the deadline due to an absence */
1029 |                 /* Recursively call to re-adjust if necessary */
1030 |                 deadline_recursive(seq, new, rec_count);
1031 |                 /* set the mailing flag so we can advise an absence adjust */
1032 |                 broadcast_absence_adjust = 1;
1033 |         }
1034 |         return 0;
1035 | 
1036 | }
1037 | 
1038 | 
1039 | /***** */
1040 | /* This following function will remove all leading 
1041 |  * spaces and convert double (or more) spaces to only one
1042 |  */
1043 | 
1044 | int despace(char *intext)
1045 | {
1046 | 	char temp[350];
1047 | 	int space_count = 0;
1048 | 	char *s = intext;
1049 | 	char *outtext = temp;
1050 | 
1051 | 	/* First take away leading spaces */
1052 | 	while (isspace(*s)) s++;
1053 | 
1054 | 	while (*s) {
1055 | 	    if (isspace(*s)) {
1056 | 		space_count++;
1057 | 		if (space_count == 1) *outtext++ = ' ';
1058 | 	    }
1059 | 	    else {
1060 | 		*outtext++ = *s;
1061 | 		space_count = 0;
1062 | 	    }
1063 | 	    s++;
1064 | 	}
1065 | 	*outtext = '\0'; /* terminate the string */
1066 | 	strcpy(intext,temp);
1067 | 	return strlen(temp);
1068 | }
1069 | 
1070 | 
1071 | /***** */
1072 | /* This function will display the absences for 
1073 |  * a particular power (passsed as a parameter)
1074 |  * Normal powers will see their absences only
1075 |  * Master will see all absences
1076 |  * Observer will see none
1077 |  */
1078 | 
1079 | void display_one_absences(int pindex, FILE *fptr)
1080 | {
1081 | 	int i = pindex;
1082 | 	int k;
1083 | 
1084 |         if (dipent.players[i].absence_count > 0) {
1085 |            for (k = 0; k < MAX_ABSENCES;k++) {
1086 |                 if (dipent.players[i].absence_start[k] != 0) {
1087 |                     fprintf(fptr, "%s: Absence requested from \n%s to ",
1088 |                          powers[dipent.players[i].power], 
1089 | 			 ptime(&dipent.players[i].absence_start[k]));
1090 |                     fprintf(fptr,"%s\n", ptime(&dipent.players[i].absence_end[k]));
1091 |                 }
1092 |             }
1093 |             fprintf(rfp,"\n");
1094 |         }
1095 |         return;
1096 | }
1097 | void display_absence(int pnum, FILE *fptr)
1098 | {
1099 | 
1100 | 	int i;
1101 | 
1102 | 	switch (pnum) {
1103 | 
1104 | 	    case OBSERVER:
1105 | 	    case WILD_PLAYER:
1106 | 		/* Do nothing */
1107 | 		break;   
1108 | 
1109 | 	    case MASTER:
1110 | 		for (i = 0; i < MAXPLAYERS; i++ ) {
1111 | 		    display_one_absences(i, fptr);
1112 | 		}
1113 | 		break;
1114 | 
1115 | 	    default:
1116 | 		    display_one_absences(FindPower(pnum), fptr);
1117 | 	}
1118 | 	return;
1119 | }
1120 | 
1121 | /***********************************************************************/
1122 | 
1123 | 
1124 | void AddOrderToOrder(char *text, int order)
1125 | {
1126 |     if (text == NULL) return;
1127 | 
1128 |     switch (order)
1129 |     {
1130 | 	case 'a':
1131 | 	    strcat(text,"AIRLIFT ");
1132 |             break;
1133 | 	case 'c':
1134 | 	    strcat(text,"CONVOY ");
1135 | 	    break;
1136 | 	case 'm':
1137 | 	    strcat(text, "-> ");
1138 | 	    break;
1139 | 	case 't':
1140 | 	    strcat(text,"TRANSFORM ");
1141 | 	    break;
1142 | 	case 'p':
1143 | 	    strcat(text, "PROXY TO ");
1144 | 	    break;
1145 | 	case 'd':
1146 | 	    strcat(text, "DISBANDS ");
1147 | 	    break;
1148 | 	case 'h':
1149 | 	    strcat(text, "HOLD ");
1150 | 	    break;
1151 | 	case 's':
1152 | 	    strcat(text, "SUPPORT ");
1153 | 	    break;
1154 | 	case 'r':
1155 | 	    strcat(text, "REMOVE ");
1156 | 	    break;
1157 | 	case 'b':
1158 | 	    strcat(text,"BUILD ");
1159 | 	    break;
1160 | 	case 'w':
1161 | 	    strcat(text, "WAIVE ");
1162 | 	    break;
1163 | 	case 'x':
1164 | 	    break;
1165 | 	default:
1166 | 	   strcat(text,"*unknown order*");
1167 |     }
1168 | }
1169 | 
1170 | void AddCoastToOrder(char *text, int c1)
1171 | {
1172 |     char temp[50];
1173 |     if (text == NULL) return;
1174 |     sprintf(temp, "(%s) ", mtype[c1]);
1175 |     strcat(text, temp);
1176 | }
1177 | 
1178 | void AddUnitToOrder(char *text, int unit)
1179 | {
1180 |     if (text == NULL) return;
1181 |     strcat(text, Utype(unit));
1182 |     strcat(text," ");
1183 | }
1184 | 
1185 | void AddPlaceToOrder(char *text, int place)
1186 | {
1187 |     if (text == NULL) return;
1188 |     if (place <= 0) return;
1189 |     strcat(text, pr[place].name);
1190 |     strcat(text," ");
1191 | }
1192 | void AddProvinceToOrder(char *text, int place)
1193 | {
1194 | 	return AddPlaceToOrder(text,place);
1195 | }
1196 | void AddUnitProvinceToOrder(char *text, int unit, int place)
1197 | {
1198 | 	AddUnitToOrder(text, unit);
1199 | 	AddPlaceToOrder(text,place);
1200 | }
1201 | void AddPowerToOrder(char *text, int power)
1202 | {
1203 | 	if (text == NULL) return;
1204 | 	strcat(text,powers[power]);
1205 | }
1206 | void InformCustodians( char *game_name, char *text, int variant, int is_gunboat)
1207 | {
1208 |     char *variant_guardian;
1209 |     char guardian_string[200];
1210 |     char *out_addr;
1211 | 
1212 |     if (variant != V_STANDARD || is_gunboat) {
1213 |            sprintf(line, text, "dip.temp", "MNC", game_name);
1214 | 	   out_addr = MN_CUSTODIAN;
1215 |     } else {
1216 |            sprintf(line, text, "dip.temp", "BNC", game_name);                   
1217 | 	   out_addr = BN_CUSTODIAN;
1218 |    }
1219 |    MailOut(line, out_addr);
1220 |    sprintf(guardian_string, "CUSTODIAN_%s", variants[variant]);
1221 |    variant_guardian = config(guardian_string);
1222 | 
1223 |    if (variant_guardian) {
1224 |         sprintf(line, text, "dip.temp", variants[variant], game_name);
1225 |         MailOut(line, variant_guardian);
1226 |     }
1227 | 
1228 | }
1229 | int ValidGatewayProvince(int igw,  int p)
1230 | {
1231 |     int i;
1232 | 
1233 |     if (!(dipent.x2flags & X2F_GATEWAYS)) return 0;
1234 | 
1235 |      for (i = 0; i < gw[igw].np; i++) 
1236 | 	if (gw[igw].pr[i] == p)
1237 | 	    return 1;
1238 | 
1239 |      return 0;  /* not found in valid province list */
1240 | }
1241 | int ValidRailwayProvince( int irw, int p, int *pr_index)
1242 | {
1243 |     int i;
1244 |     *pr_index = -1;
1245 | 
1246 |     if (!(dipent.x2flags & X2F_RAILWAYS)) return 0;
1247 | 
1248 |     for (i=0; i < rw[irw].np; i++)
1249 | 	if (rw[irw].pr[i] == p) {
1250 | 	    *pr_index = i;
1251 | 	    return 1;
1252 |     }
1253 | 
1254 |     return 0;  /* not found in valid provinces list */
1255 | }
1256 | 
1257 | int PermittedRailwayPower(int irw, char power_letter)
1258 | {
1259 |     int i;
1260 | 
1261 |     if (!(dipent.x2flags & X2F_RAILWAYS)) return 0;
1262 | 
1263 |     for (i=0; i < MAX_POWERS+1; i++) {
1264 |         if (rw[irw].power_letter[i] == power_letter)
1265 | 	    return 1;
1266 |     }    
1267 |     return 0;
1268 | }
1269 | ;
1270 | /* This function determines if the source-dest order uses a gateway */
1271 | int IsGatewayOrder(int u, int *igw) 
1272 | {
1273 | 	int source = unit[u].loc;
1274 | 	int dest = unit[u].dest;
1275 | 	int i,j,k; 
1276 | 
1277 | 	if (!(dipent.x2flags & X2F_GATEWAYS)) return 0;
1278 | 
1279 | 	/* If not moving or supporting, not using gateway! */
1280 |         if (unit[u].order != 'm' && unit[u].order != 's') return 0; 
1281 | 	for (i=0; i < ngw; i++) {
1282 | 	    for (j=0; j < MAX_GW_PROVINCES; j++) {
1283 | 	        if (gw[i].pr[j] == source) {
1284 | 		    for (k = 0; k < MAX_GW_PROVINCES; k++) {
1285 | 		        if (gw[i].pr[k] == dest) {
1286 | 			    *igw = i;
1287 | 			    return 1;
1288 | 			}
1289 | 		    }
1290 | 		} else if (gw[i].pr[j] == dest) {
1291 | 		    for (k = 0; k < MAX_GW_PROVINCES; k++) {
1292 |                         if (gw[i].pr[k] == source) {
1293 |                             *igw = i;
1294 |                             return 1;
1295 |                         }
1296 | 		    }
1297 | 		}
1298 | 	    }
1299 | 	}
1300 | 	return 0; 
1301 | };
1302 | 
1303 | /* Check that the passed gateway follows the unit's order */
1304 | int IsGatewayOrdered(int igw,int u, int result[])
1305 | {
1306 | 	int u1;
1307 | 
1308 |         if (!(dipent.x2flags & X2F_GATEWAYS)) return 0;
1309 | 
1310 | 	/* Sanity check */
1311 | 	if (igw < 0 || igw >= ngw) return 0;
1312 | 	
1313 | 	u1 = pr[gw[igw].prov_index].unit;
1314 | 
1315 | 	if (!u1) return 0;  /* No unit there */
1316 | 
1317 | 	if (unit[u1].order != 's') return 0; /* Not supporting anyone */
1318 | 
1319 | 	if (unit[u].order == 'm' &&
1320 | 	    unit[u1].dest == unit[u].dest &&
1321 | 	    unit[u1].unit == u && !result[u1])
1322 | 		return 1;
1323 | 
1324 | 	if (unit[u].order == 's' &&
1325 | 	    unit[u1].dest == unit[u].loc &&
1326 | 	    unit[u1].unit == u && !result[u1])
1327 | 		return 1;
1328 | 
1329 | 	return 0;	
1330 | };
1331 | 
1332 | char HasUnit(int province_index)
1333 | {
1334 | 	if (pr[province_index].unit >= 0)
1335 |             return unit[pr[province_index].unit].type;
1336 |        else
1337 | 	return '\0';
1338 | };
1339 | 
1340 | /* See if a gateway province, then only can retreat if owns gateway */
1341 | int AllowedGatewayRetreat( int u, int p)
1342 | {
1343 |     int i, j, k, u1;
1344 | 
1345 |     if (!(dipent.x2flags & X2F_GATEWAYS))
1346 | 	return 1;   /* No gateways, so always allowed */
1347 | 
1348 |     for (i=0; i < ngw; i++) {
1349 |         for (j=0; j < MAX_GW_PROVINCES; j++) {
1350 |             if (gw[i].pr[j] == unit[u].loc) {
1351 |                 for (k = 0; k < MAX_GW_PROVINCES; k++) {
1352 |                     if (gw[i].pr[k] == p) {
1353 | 			 for (u1 = 1; u1 <= nunit; u1++) {
1354 | 			     if (unit[u1].loc == gw[i].prov_index) {
1355 | 				if (unit[u1].status == ':' && 
1356 | 				    unit[u1].owner == unit[u].owner) {
1357 | 				    return 1;  
1358 | 				/* Found gatway unit of same owner, not dislodged */
1359 | 				}
1360 | 			     }
1361 | 			}
1362 | 			return 0; /* using gateway, but not allowed */
1363 | 		    }
1364 | 		}
1365 | 	    }
1366 | 	}
1367 |     }
1368 |     return 1;  /* not on a gateway, so ok to retreat */
1369 | }
1370 | 
1371 | /* See if natives in gaem, return their index if so, else 0 */
1372 | int GetNativeIndex()
1373 | {
1374 |    int i;
1375 | 
1376 |    for (i=1; i < AUTONOMOUS; i++)
1377 | 	if (pletter[dipent.variant][i] == NATIVE_POWER)
1378 | 	    return i;
1379 | 
1380 |   return 0;  /* No native power found */
1381 | 
1382 | }
1383 | 
1384 | /* See if game has multi-unit provinces */
1385 | int CheckForMultiUnitProvinces()
1386 | {
1387 |     int i;
1388 | 
1389 |     for (i = 1; i <= npr; i++) 
1390 | 	if (highsea(i)) 
1391 | 	     return !0;
1392 | 
1393 |     return 0;
1394 | }
1395 | 
1396 | /* See if province concerned is a multi-unit province */
1397 | int IsMultiProvince(int p)
1398 | {
1399 |     if (!dipent.has_multi_unit_provs) return 0;
1400 | 
1401 |     if (highsea(p))
1402 | 	return !0;
1403 | 
1404 |     return 0;
1405 | }
1406 | 
1407 | /* return the 'n'th unit in a multi-unit province */
1408 | int GetUnitIndex(int p, int power)
1409 | {
1410 |     int u;
1411 |     int index = 0; /* 'n'th unit found in this province */
1412 |     int *nordinal = &pr[p].order_index;
1413 |     int first_unit = pr[p].unit;
1414 | 	
1415 |     if (!IsMultiProvince(p))
1416 |         return pr[p].unit;  /* Normal province, return first unit */
1417 | 
1418 |     for (u=1; u <= nunit; u++) {
1419 |         if (unit[u].loc == p) {
1420 |             if (unit[u].owner == power || power == MASTER) {
1421 | 		first_unit = u; /* remember first unit for this person */
1422 | 	        if (index == *nordinal) {
1423 | 		    (*nordinal)++; /* Found requested unit, next time is n+1 */
1424 | 		    return u;
1425 | 		}
1426 | 		index++;  /* now looking for next unit */
1427 | 	    }
1428 | 	}
1429 |     }
1430 | 
1431 |     *nordinal = 0;  /* not found requested unit, so reset ordinal */
1432 |     return first_unit;  /* wrapped round to first unit in province */
1433 | }
1434 | 
1435 | /* return the nth address in a string 
1436 |    using ';' and ',' as separators
1437 |    returns a string with only the address if found
1438 |    return NULL if not found */
1439 | 
1440 | static char *GetAddressPart(int index, char *address)
1441 | {
1442 | 
1443 |     static char part_address[256];  /* Address part to find */
1444 |     int i, count = 0;
1445 |     char *cur_ptr, *ptr;
1446 | 
1447 |     strcpy(part_address, address);
1448 | 
1449 |     /* Convert all ';' to ',', so as to have only one separator character */
1450 | 
1451 |     for (i = 0; i < strlen(part_address); i++)
1452 | 	if (part_address[i] == ';')
1453 | 	    part_address[i] = ',';
1454 | 
1455 | 
1456 |     cur_ptr = part_address;
1457 |     do {
1458 |         ptr = strchr(cur_ptr, ',');
1459 |         if (ptr) {
1460 | 	    *ptr = '\0';
1461 |         }
1462 |         if (count != index ) {
1463 | 	    if (ptr) cur_ptr = ptr + 1;
1464 | 	    count++;
1465 |         }
1466 |     } while (count < index && ptr); 
1467 | 	
1468 |     if (count != index || (count > 0 && !ptr)) 
1469 | 	return NULL;  /* No next part found */
1470 |     else
1471 | 	return cur_ptr;  /* Return found part */
1472 | 
1473 | } 
1474 | 
1475 | /* Send mail out, using individual calls to SMAIL_CMD per email address */
1476 | 
1477 | void MailOut(char *out_line, char *address)
1478 | {
1479 | 
1480 |    int count = 0;
1481 |    char *ptr;
1482 |    static char lline[256];
1483 | 
1484 |    while ( ptr = GetAddressPart(count++, address)) {
1485 |         if (*ptr != '*' ) {
1486 |            sprintf(lline, "%s %s '%s'", SMAIL_CMD, out_line, ptr);
1487 |            execute(lline);
1488 | 	}
1489 |    }
1490 | 
1491 | }