1 | /*
2 | * $Log: history.c,v $
3 | * Revision 1.9 2002/08/27 22:27:50 millis
4 | * Updated for automake/autoconf functionality
5 | *
6 | * Revision 1.8 2002/05/11 09:15:32 greg
7 | * Minor bug fixes
8 | * - fixed subjectline for absence requests
9 | * - fixed phase length, so it's no longer hard coded for responses
10 | * - partial fix for unusable builds, players with only unusable builds
11 | * will no longer be flagged as having orders due, however players
12 | * with some usable builds will need to waive any unusable builds,
13 | * also, if one or more players have unusable builds, but no
14 | * player has usable builds, the build phase will process after
15 | * a short delay
16 | *
17 | * Revision 1.7 2001/08/30 03:40:12 greg
18 | * fix "history exclstart" to work with both old and new subject lines
19 | *
20 | * Revision 1.6 2001/07/15 09:15:19 greg
21 | * added support for game directories in a sub directory
22 | *
23 | * Revision 1.5 2001/07/14 07:25:41 greg
24 | * fix "history exclstart" command with new subjectlines
25 | *
26 | * Revision 1.4 2001/02/23 00:19:33 miller
27 | * un-DOSify
28 | *
29 | * Revision 1.3 2001/02/03 10:36:16 miller
30 | * properly hide history in blind game
31 | *
32 | * Revision 1.2 2000/11/14 14:27:37 miller
33 | * Restrict history for blind games (so that players cannot find out more than they should!)
34 | *
35 | * Revision 1.1 1998/02/28 17:49:42 david
36 | * Initial revision
37 | *
38 | * Revision 1.3 1996/11/10 14:22:47 rpaar
39 | * changed the "broad#" to "broad" in the keys[] array.
40 | * modified the return text to include newlines.
41 | *
42 | * Revision 1.2 1996/11/05 23:06:12 rpaar
43 | * Added function to process history as turns not dates
44 | */
45 |
46 | /* history.c -- Extract selected portions of the history archives.
47 | * Copyright 1987, Lowe.
48 | *
49 | * Diplomacy is a trademark of the Avalon Hill Game Company, Baltimore,
50 | * Maryland, all rights reserved; used with permission.
51 | *
52 | * Redistribution and use in source and binary forms are permitted
53 | * provided that it is for non-profit purposes, that this and the
54 | * above notices are preserved and that due credit is given to Mr.
55 | * Lowe.
56 | */
57 |
58 | #include <stdlib.h>
59 | #include <string.h>
60 | #include <sys/stat.h>
61 | #include <sys/types.h>
62 | #include <time.h>
63 |
64 | #include "config.h"
65 | #include "dip.h"
66 | #include "functions.h"
67 | #include "mail.h"
68 |
69 | static FILE *fp;
70 | static int lcnt;
71 | static struct stat sbuf;
72 |
73 | /***************************************************************************/
74 |
75 | /* Moded by R.Paar 27 May 1996
76 | */
77 |
78 | /* Function: doturns
79 |
80 | * Input params:
81 | * int nbflag - flag whether notices should be included (1), or Not (0)
82 | * char * against - the starting turn for exclusion zone
83 | * char * lastturn - ending turn for exlusion zone
84 | * char * GameName - name of the game we are currently processing.
85 | *
86 | * Output: 1 on success
87 | *
88 | * Error Checking: None.
89 | * To keep it simple:
90 | * nbflag cannot be incorrectly passed. (Should be a BOOL)
91 | * on incorrect 'against' parameter, copies whole archive (beware!)
92 | * on incorrect 'lastturn' parameter, copies only to 'against' turn.
93 | *
94 | * Objective: Copy a history archive such that starting on turn 'against'
95 | * the archive is not copied. Ends on turn 'lastturn'.
96 | * If nbflag set, exclude _ALL_ notices. (N.B. this includes draw notices)
97 | *
98 | * N.B: I've hacked it so it will include draws in the returned history.
99 | *
100 | * Q: Shouldn't Draws be given a "Diplomacy results" subject line?
101 | */
102 |
103 | static int doturns(nbflag, against, lastturn, GameName)
104 | int nbflag;
105 | char *against;
106 | char *lastturn;
107 | char *GameName;
108 | /* Should probably extract the const char *'s to somewhere more useful.
109 | * flag variable may be overkill, however, better than executing a strstr several
110 | * times per line. Def of flag values:
111 | * 0 - ok to print to output file.
112 | * 1 - Do not print to output file.
113 | * 2 - Found the exclusion zone start, used to determine looking for exclusion
114 | * end.
115 | */
116 | {
117 | FILE *out = rfp;
118 | int flag = 0;
119 | static char Input[150];
120 |
121 | while (fgets(Input, sizeof(Input), fp) != NULL) {
122 | if (flag == 0) {
123 | if (strstr(Input, "Subject:") != NULL) {
124 | if ((!nbflag) && (strstr(Input, "Results") == NULL)
125 | && (strstr(Input, "Game Starting") == NULL)
126 | && (strstr(Input, "starting") == NULL)
127 | && (strstr(Input, "results") == NULL)) {
128 | flag = 1;
129 | } else if (strstr(Input, against) != NULL) {
130 | flag = 2;
131 | }
132 | }
133 | }
134 | if ((flag == 2) && strstr(Input, lastturn) != NULL) {
135 | flag = 0;
136 | }
137 | if ((flag == 1) && (strstr(Input, "declared a draw between") != NULL)) {
138 | fprintf(out, "Subject: Diplomacy notice: %s\n\n", GameName);
139 | flag = 0;
140 | }
141 | if (flag == 0) {
142 | fprintf(out, "%s", Input);
143 | }
144 | if ((flag < 2) && (strstr(Input, "Date:") != NULL) &&
145 | (strstr(Input, "\(") != NULL)) {
146 | flag = 0;
147 | }
148 | }
149 |
150 | return 1;
151 | }
152 |
153 | /***************************************************************************/
154 |
155 | static void hist_date(long *date, long *pos, int print)
156 | {
157 |
158 | static char line[150];
159 | char *s;
160 |
161 | for (;;) {
162 |
163 | if (print) {
164 | fputs(line, rfp);
165 | if (!--lcnt) {
166 | return;
167 | }
168 | }
169 | *pos = ftell(fp);
170 | if (!fgets(line, sizeof(line), fp)) {
171 | if (print)
172 | lcnt = -1;
173 | *date = sbuf.st_mtime;
174 | return;
175 | }
176 | if (!strncmp(line, "Date: ", 6) && (s = strchr(line, '('))) {
177 | *date = atol(s + 1);
178 | return;
179 | }
180 | }
181 | }
182 |
183 | /***************************************************************************/
184 |
185 | int history(char *line, int power_type)
186 | {
187 |
188 | /*
189 | * power_type is the type of the plyer requesting history
190 | * Set to OBSERVER if power type is unknown
191 | */
192 |
193 | /*
194 | * The input line consists of [name] [from date] [to date] [lines n].
195 | * or: [name] exclstart turn [exclend turn] [broad].
196 | *
197 | * 'broad' option is default for exstart parameter to
198 | * eliminate potentially long histories from being
199 | * returned. Users must specifically ask for notices to
200 | * to be included if they so desire.
201 | */
202 |
203 | /* Rp - added int flagb to flag incl/excl Broadcasts. flagt = time/turns */
204 | int i, not_eof, flagb, flagt;
205 | char *s, name[sizeof(dipent.name)], file[sizeof(name) + 31];
206 | char exstart[10], exend[10]; /* 10 characters should be plenty */
207 | long s_date, s_pos, e_date, e_pos, n_date, n_pos, from, pos,
208 | to;
209 |
210 | /* Rp - added exclstart#, exclend#, nb# */
211 | static char *keys[] =
212 | {"", "from#", "to#", "until#", "lines#", "for#",
213 | "exclstart#", "exclend#", "broad"};
214 | static char value[] =
215 | {'x', 'f', 't', 't', 'l', 'l', 's', 'e', 'n'};
216 |
217 | *name = '\0';
218 | /* Rp - init char *s */
219 | *exstart = '\0';
220 | *exend = '\0';
221 |
222 | s = line;
223 |
224 | time(&to);
225 | from = to - 168 * 60 * 60;
226 | lcnt = 1000;
227 | flagb = 0; /* Rp - define flagb = 1 include broadcast, 0 excluded */
228 | flagt = 1; /* Rp - define flagt = 1 do time-wise, define flagt = 0 as exclude */
229 |
230 |
231 | while (isspace(*s))
232 | s++;
233 |
234 | while (*s) {
235 | s = lookfor(s, keys, nentry(keys), &i);
236 |
237 | switch (value[i]) {
238 | case 'x':
239 | if (*name) {
240 | if ((!msg_header_done) && (!signedon))
241 | msg_header(rfp);
242 | fprintf(rfp, "history %s", line);
243 | fprintf(rfp, "Unrecognized keyword: %s", s);
244 | return 1;
245 | }
246 | while (isspace(*s))
247 | s++;
248 | for (i = 0; *s && !isspace(*s) && i < sizeof(name) - 1; i++) {
249 | /* TODO this should allow case insensitive history commands */
250 | /* double check that the tolower has that effect */
251 | name[i] = tolower(*s);
252 | s++;
253 | }
254 | name[i] = '\0';
255 | break;
256 |
257 | case 'f':
258 | if (mail_date(&s, &from, 1, rfp)) {
259 | if ((!msg_header_done) && (!signedon))
260 | msg_header(rfp);
261 | fprintf(rfp, "history %s", line);
262 | fprintf(rfp, "Unparsable from date: %s", s);
263 | return 1;
264 | }
265 | break;
266 |
267 | case 't':
268 | if (mail_date(&s, &to, 1, rfp)) {
269 | if ((!msg_header_done) && (!signedon))
270 | msg_header(rfp);
271 | fprintf(rfp, "history %s", line);
272 | fprintf(rfp, "Unparsable to date: %s", s);
273 | return 1;
274 | }
275 | break;
276 |
277 | case 'l':
278 | while (isspace(*s))
279 | s++;
280 | for (lcnt = 0; isdigit(*s); lcnt = lcnt * 10 + *s++ - '0');
281 | if (lcnt == 0) {
282 | if ((!msg_header_done) && (!signedon))
283 | msg_header(rfp);
284 | fprintf(rfp, "history %s", line);
285 | fprintf(rfp, "Unparsable line count: %s", s);
286 | return 1;
287 | }
288 | break;
289 | /* Rp - init vars if found on History line */
290 | case 's':{
291 | flagt = 0;
292 | if ((!msg_header_done) && (!signedon))
293 | msg_header(rfp);
294 | while (isspace(*s))
295 | s++;
296 | /* if (copycheck( s, exstart)) {} */
297 | for (i = 0; *s && !isspace(*s) && i < sizeof(exstart) - 1; i++) {
298 | exstart[i] = *s++;
299 | }
300 | exstart[i] = '\0';
301 | }
302 | break;
303 |
304 | case 'e':{
305 | if ((!msg_header_done) && (!signedon))
306 | msg_header(rfp);
307 | while (isspace(*s))
308 | s++;
309 | /* if (copycheck( s, exend)) {} */
310 | for (i = 0; *s && !isspace(*s) && i < sizeof(exend) - 1; i++) {
311 | exend[i] = *s++;
312 | }
313 | exend[i] = '\0';
314 | }
315 | break;
316 |
317 | case 'n':
318 | if ((!msg_header_done) && (!signedon))
319 | msg_header(rfp);
320 | flagt = 0;
321 | flagb = 1;
322 | break;
323 | }
324 |
325 | while (isspace(*s))
326 | s++;
327 |
328 | }
329 |
330 | /*
331 | * Now read that archive file.
332 | */
333 |
334 | if (!*name) {
335 | if (!signedon) {
336 | if (!msg_header_done)
337 | msg_header(rfp);
338 | fprintf(rfp, "history %s", line);
339 | fprintf(rfp, "Game name not specified.\n");
340 | return 1;
341 | }
342 | strcpy(name, dipent.name);
343 | } else {
344 | if (!signedon) {
345 | if ((mfp = fopen(MASTER_FILE, "r")) == NULL) {
346 | if (!msg_header_done)
347 | msg_header(rfp);
348 | fprintf(rfp, "Error opening master file %s.\n", MASTER_FILE);
349 | return 1;
350 | }
351 | while ((not_eof = getdipent(mfp))) {
352 | if (!strcmp(dipent.name, name)) {
353 | break;
354 | }
355 | }
356 | fclose(mfp);
357 | }
358 | }
359 |
360 | sprintf(file, "%s%s/archive", GAME_DIR, name);
361 | if (stat(file, &sbuf)) {
362 | if ((!msg_header_done) && (!signedon))
363 | msg_header(rfp);
364 | fprintf(rfp, "history %s", line);
365 | fprintf(rfp, "No history available for game '%s'.\n", name);
366 | return 1;
367 | }
368 |
369 | /* Check if a blind game - if so, only the master can do this */
370 | if (dipent.flags & F_BLIND ) {
371 | if (dipent.phase[6] != 'X' ) {
372 | /* Non-finished blind games have restrictions on history */
373 | switch (power_type)
374 | {
375 | case 0: /* not signed on */
376 | case OBSERVER:
377 | fprintf(rfp,"Observers are not allowed history command in blind games.\n");
378 | return 0;
379 | case MASTER:
380 | break; /* No problem for the master! */
381 |
382 | default:
383 | fprintf(rfp, "Players are not allowed history command in blind games.\nContact the master for help in this.\n");
384 | return 0;
385 | }
386 | }
387 | }
388 |
389 | if (!msg_header_done)
390 | msg_header(rfp);
391 | fprintf(rfp, "History information for game '%s' ", name);
392 | if (flagt) {
393 | fprintf(rfp, "from %s\n", ptime(&from));
394 | fprintf(rfp, "through %s:\n", ptime(&to));
395 | } else {
396 | fprintf(rfp, "excluding turn '%s'\n", exstart);
397 | fprintf(rfp, "to turn '%s'.", ((*exend != '\0') ? exend : "end of game"));
398 | }
399 | fprintf(rfp, "\n");
400 |
401 | /*
402 | * Open the archive file and determine its start time.
403 | */
404 |
405 | if (!(fp = fopen(file, "r"))) {
406 | fprintf(rfp, "Unable to open history file for '%s'.\n", name);
407 | return 1;
408 | }
409 | /* Rp - if (flagt) do search by date */
410 | if (flagt) {
411 | hist_date(&s_date, &s_pos, 0);
412 |
413 | e_date = sbuf.st_mtime;
414 | e_pos = sbuf.st_size;
415 |
416 | /*
417 | * Now do a binary search to find the intended start time.
418 | */
419 |
420 | while (s_date < from && from < e_date && e_pos - s_pos > 2048) {
421 | if (Dflg)
422 | printf("s = %24.24s / %ld.\n", ctime(&s_date), s_pos);
423 | if (Dflg)
424 | printf("e = %24.24s / %ld.\n", ctime(&e_date), e_pos);
425 | pos = s_pos + (long) (((float) (e_pos - s_pos) *
426 | (float) (from - s_date)) /
427 | (float) (e_date - s_date));
428 | fseek(fp, pos, 0);
429 | hist_date(&n_date, &n_pos, 0);
430 | if (Dflg)
431 | printf("n = %24.24s / %ld from %ld.\n", ctime(&n_date), n_pos, pos);
432 | if (n_date >= from) {
433 | e_date = n_date;
434 | e_pos = pos;
435 | } else {
436 | s_date = n_date;
437 | s_pos = n_pos;
438 | }
439 | }
440 |
441 | /*
442 | * Okay, close enuf.
443 | */
444 |
445 | if (from > e_date) {
446 | fprintf(rfp, "No history for specified period.\n");
447 | } else {
448 |
449 | fseek(fp, s_pos, 0);
450 | do {
451 | hist_date(&s_date, &s_pos, 0);
452 | if (Dflg)
453 | printf("Skipped to %24.24s at %ld.\n", ctime(&s_date), s_pos);
454 | } while (s_date < from);
455 |
456 | do {
457 | hist_date(&s_date, &s_pos, 1);
458 | if (Dflg)
459 | printf("Printed to %24.24s at %ld.\n", ctime(&s_date), s_pos);
460 | } while (s_date < to && lcnt > 0);
461 |
462 | fprintf(rfp, "\n---- History terminated due to %s.\n",
463 | lcnt == 0 ? "line limit exceeded" :
464 | lcnt == -1 ? "end of information" :
465 | "time range exhausted");
466 | }
467 | }
468 | /* end if (flagt) */
469 | else {
470 | if (doturns(flagb, exstart, exend, name)) {
471 | fprintf(rfp, "\n---- History terminated due to end of infomation.\n");
472 | }
473 | }
474 |
475 | fclose(fp);
476 | return 0;
477 | }