Check-in Lasker-2.2.3 tar ball from samba.org
[capablanca.git] / lasker-2.2.3 / src / obsproc.c
1 /*
2    Copyright (c) 1993 Richard V. Nash.
3    Copyright (c) 2000 Dan Papasian
4    Copyright (C) Andrew Tridgell 2002
5    
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #include "includes.h"
22
23 int GameNumFromParam(int p, int *p1, parameter *param)
24 {
25   if (param->type == TYPE_WORD) {
26     *p1 = player_find_part_login(param->val.word);
27     if (*p1 < 0) {
28       pprintf(p, "No user named \"%s\" is logged in.\n", param->val.word);
29       return -1;
30     }
31     if (player_globals.parray[*p1].game < 0)
32       pprintf(p, "%s is not playing a game.\n", player_globals.parray[*p1].name);
33     return player_globals.parray[*p1].game;
34   } else {                      /* Must be an integer */
35     *p1 = -1;
36     if (param->val.integer <= 0)
37       pprintf(p, "%d is not a valid game number.\n", param->val.integer);
38     return param->val.integer - 1;
39   }
40 }
41
42 static int gamesortfunc(const void *i, const void *j)
43 {
44 /* examine mode games moved to top of "games" output */
45   return (GetRating(&player_globals.parray[game_globals.garray[*(int *) i].white], game_globals.garray[*(int *) i].type) +
46      GetRating(&player_globals.parray[game_globals.garray[*(int *) i].black], game_globals.garray[*(int *) i].type) -
47           (((game_globals.garray[*(int *) i].status == GAME_EXAMINE) || (game_globals.garray[*(int *) i].status == GAME_SETUP)) ? 10000 : 0) -
48      GetRating(&player_globals.parray[game_globals.garray[*(int *) j].white], game_globals.garray[*(int *) j].type) -
49      GetRating(&player_globals.parray[game_globals.garray[*(int *) j].black], game_globals.garray[*(int *) j].type) +
50           (((game_globals.garray[*(int *) j].status == GAME_EXAMINE) || (game_globals.garray[*(int *) j].status == GAME_SETUP)) ? 10000 : 0));
51 }
52
53
54 int com_games(int p, param_list param)
55 {
56   int i, j;
57   int wp, bp;
58   int ws, bs;
59   int selected = 0;
60   int count = 0;
61   int totalcount;
62   char *s = NULL;
63   int slen = 0;
64   int *sortedgames;             /* for qsort */
65
66   totalcount = game_count();
67   if (totalcount == 0) {
68     pprintf(p, "There are no games in progress.\n");
69   } else {
70     sortedgames = malloc(totalcount * sizeof(int));     /* for qsort */
71
72     if (param[0].type == TYPE_WORD) {
73       s = param[0].val.word;
74       slen = strlen(s);
75       selected = atoi(s);
76       if (selected < 0)
77         selected = 0;
78     }
79     for (i = 0; i < game_globals.g_num; i++) {
80       if ((game_globals.garray[i].status != GAME_ACTIVE) && (game_globals.garray[i].status != GAME_EXAMINE) && (game_globals.garray[i].status != GAME_SETUP))
81         continue;
82       if ((selected) && (selected != i + 1))
83         continue;               /* not selected game number */
84       wp = game_globals.garray[i].white;
85       bp = game_globals.garray[i].black;
86       if ((!selected) && s && strncasecmp(s, game_globals.garray[i].white_name, slen) &&
87           strncasecmp(s, game_globals.garray[i].black_name, slen))
88         continue;               /* player names did not match */
89       sortedgames[count++] = i;
90     }
91     if (!count)
92       pprintf(p, "No matching games were found (of %d in progress).\n", totalcount);
93     else {
94       qsort(sortedgames, count, sizeof(int), gamesortfunc);
95       pprintf(p, "\n");
96       for (j = 0; j < count; j++) {
97         i = sortedgames[j];
98         wp = game_globals.garray[i].white;
99         bp = game_globals.garray[i].black;
100         board_calc_strength(&game_globals.garray[i].game_state, &ws, &bs);
101         if ((game_globals.garray[i].status != GAME_EXAMINE) && (game_globals.garray[i].status != GAME_SETUP)) {
102           pprintf_noformat(p, "%3d %4s %-11.11s %4s %-10.10s [%c%c%c%3d %3d] ",
103                            i + 1,
104                            ratstrii(GetRating(&player_globals.parray[wp],game_globals.garray[i].type), wp),
105                            player_globals.parray[wp].name,
106                            ratstrii(GetRating(&player_globals.parray[bp],game_globals.garray[i].type), bp),
107                            player_globals.parray[bp].name,
108                            (game_globals.garray[i].private) ? 'p' : ' ',
109                            *bstr[game_globals.garray[i].type],
110                            *rstr[game_globals.garray[i].rated],
111                            game_globals.garray[i].wInitTime / 600,
112                            game_globals.garray[i].wIncrement / 10);
113           game_update_time(i);
114           pprintf_noformat(p, "%6s -",
115                  tenth_str((game_globals.garray[i].wTime > 0 ? game_globals.garray[i].wTime : 0), 0));
116           pprintf_noformat(p, "%6s (%2d-%2d) %c: %2d\n",
117                   tenth_str((game_globals.garray[i].bTime > 0 ? game_globals.garray[i].bTime : 0), 0),
118                            ws, bs,
119                          (game_globals.garray[i].game_state.onMove == WHITE) ? 'W' : 'B',
120                            game_globals.garray[i].game_state.moveNum);
121         } else {
122           pprintf_noformat(p, "%3d (%s %4d %-11.11s %4d %-10.10s) [%c%c%c%3d %3d] ",
123                            i + 1,
124                            (game_globals.garray[i].status == GAME_EXAMINE) ? "Exam." : "Setup",
125                            game_globals.garray[i].white_rating,
126                            game_globals.garray[i].white_name,
127                            game_globals.garray[i].black_rating,
128                            game_globals.garray[i].black_name,
129                            (game_globals.garray[i].private) ? 'p' : ' ',
130                            *bstr[game_globals.garray[i].type],
131                            *rstr[game_globals.garray[i].rated],
132                            game_globals.garray[i].wInitTime / 600,
133                            game_globals.garray[i].wIncrement / 10);
134           pprintf_noformat(p, "%c: %2d\n",
135                          (game_globals.garray[i].game_state.onMove == WHITE) ? 'W' : 'B',
136                            game_globals.garray[i].game_state.moveNum);
137         }
138       }
139       if (count < totalcount)
140         pprintf(p, "\n  %d game%s displayed (of %d in progress).\n", count,
141                 (count == 1) ? "" : "s", totalcount);
142       else
143         pprintf(p, "\n  %d game%s displayed.\n", totalcount, (totalcount == 1) ? "" : "s");
144     }
145     free(sortedgames);
146   }
147   return COM_OK;
148 }
149
150 static int do_observe(int p, int obgame)
151 {
152   struct player *pp = &player_globals.parray[p];
153   if ((game_globals.garray[obgame].private) && (pp->adminLevel < ADMIN_ADMIN)) {
154     pprintf(p, "Sorry, game %d is a private game.\n", obgame + 1);
155     return COM_OK;
156   }
157   if ((game_globals.garray[obgame].white == p) || (game_globals.garray[obgame].black == p)) {
158     if ((game_globals.garray[obgame].status != GAME_EXAMINE) || (game_globals.garray[obgame].status != GAME_SETUP)) {
159       pprintf(p, "You cannot observe a game that you are playing.\n");
160       return COM_OK;
161     }
162   }
163   if (player_is_observe(p, obgame)) {
164     pprintf(p, "Removing game %d from observation list.\n", obgame + 1);
165     player_remove_observe(p, obgame);
166   } else {
167     if (!player_add_observe(p, obgame)) {
168       pprintf(p, "You are now observing game %d.\n", obgame + 1);
169       send_board_to(obgame, p);
170     } else {
171       pprintf(p, "You are already observing the maximum number of games.\n");
172     }
173   }
174   return COM_OK;
175 }
176
177 void unobserveAll(int p)
178 {
179         struct player *pp = &player_globals.parray[p];
180         int i;
181         
182         for (i = 0; i < pp->num_observe; i++) {
183                 pprintf(p, "Removing game %d from observation list.\n", 
184                         pp->observe_list[i] + 1);
185         }
186         pp->num_observe = 0;
187         return;
188 }
189
190 int com_unobserve(int p, param_list param)
191 {
192   int gNum, p1;
193
194   if (param[0].type == TYPE_NULL) {
195     unobserveAll(p);
196     return COM_OK;
197   }
198   gNum = GameNumFromParam(p, &p1, &param[0]);
199   if (gNum < 0)
200     return COM_OK;
201   if (!player_is_observe(p, gNum)) {
202     pprintf(p, "You are not observing game %d.\n", gNum);
203   } else {
204     player_remove_observe(p, gNum);
205     pprintf(p, "Removing game %d from observation list.\n", gNum + 1);
206   }
207   return COM_OK;
208 }
209
210 int com_observe(int p, param_list param)
211 {
212   struct player *pp = &player_globals.parray[p];
213   int i;
214   int p1, obgame;
215   
216   if (param[0].type == TYPE_NULL)
217     return COM_BADPARAMETERS;
218   if ((pp->game >=0) &&(game_globals.garray[pp->game].status == GAME_EXAMINE)) {
219     pprintf(p, "You are still examining a game.\n");
220     return COM_OK;
221   }
222   if ((pp->game >=0) &&(game_globals.garray[pp->game].status == GAME_SETUP)) {
223     pprintf(p, "You are still setting up a position.\n");
224     return COM_OK;
225   }
226   if (param[0].type == TYPE_NULL) {
227     unobserveAll(p);
228     return COM_OK;
229   }
230   obgame = GameNumFromParam(p, &p1, &param[0]);
231   if (obgame < 0)
232     return COM_OK;
233
234   if ((obgame >= game_globals.g_num) || ((game_globals.garray[obgame].status != GAME_ACTIVE) &&
235                             (game_globals.garray[obgame].status != GAME_EXAMINE) &&
236                             (game_globals.garray[obgame].status != GAME_SETUP))) {
237     pprintf(p, "There is no such game.\n");
238     return COM_OK;
239   }
240   if ((p1 >= 0) && (player_globals.parray[p1].simul_info != NULL) && (player_globals.parray[p1].simul_info->numBoards)) {
241     for (i = 0; i < player_globals.parray[p1].simul_info->numBoards; i++)
242       if (player_globals.parray[p1].simul_info->boards[i] >= 0)
243         do_observe(p, player_globals.parray[p1].simul_info->boards[i]);
244     } else
245       do_observe(p, obgame);
246   return COM_OK;
247 }
248
249 int com_allobservers(int p, param_list param)
250 {
251   struct player *pp = &player_globals.parray[p];
252   int obgame;
253   int p1;
254   int start, end;
255   int g;
256   int first;
257
258   if (param[0].type == TYPE_NULL) {
259     obgame = -1;
260   } else {
261     obgame = GameNumFromParam(p, &p1, &param[0]);
262     if (obgame < 0)
263       return COM_OK;
264   }
265   if (obgame == -1) {
266     start = 0;
267     end = game_globals.g_num;
268   } else if ((obgame >= game_globals.g_num) || ((obgame < game_globals.g_num)
269                                    && ((game_globals.garray[obgame].status != GAME_ACTIVE)
270                              && (game_globals.garray[obgame].status != GAME_EXAMINE)
271                              && (game_globals.garray[obgame].status != GAME_SETUP)))) {
272     pprintf(p, "There is no such game.\n");
273     return COM_OK;
274   } else {
275     start = obgame;
276     end = obgame + 1;
277   }
278
279   /* list games being played */
280
281   for (g = start; g < end; g++) {
282     if ((game_globals.garray[g].status == GAME_ACTIVE) &&
283         ((pp->adminLevel > 0) || (game_globals.garray[g].private == 0))) {
284       for (first = 1, p1 = 0; p1 < player_globals.p_num; p1++) {
285         if ((player_globals.parray[p1].status != PLAYER_EMPTY) && (player_is_observe(p1, g))) {
286           if (first) {
287             pprintf(p, "Observing %2d [%s vs. %s]:",
288                     g + 1,
289                     player_globals.parray[game_globals.garray[g].white].name,
290                     player_globals.parray[game_globals.garray[g].black].name);
291             first = 0;
292           }
293           pprintf(p, " %s%s", (player_globals.parray[p1].game >=0) ? "#" : "", player_globals.parray[p1].name);
294         }
295       }
296       if (!first)
297         pprintf(p, "\n");
298     }
299   }
300
301   /* list games being examined last */
302
303   for (g = start; g < end; g++) {
304     if (((game_globals.garray[g].status == GAME_EXAMINE) || (game_globals.garray[g].status == GAME_SETUP)) &&
305         ((pp->adminLevel > 0) || (game_globals.garray[g].private == 0))) {
306       for (first = 1, p1 = 0; p1 < player_globals.p_num; p1++) {
307         if ((player_globals.parray[p1].status != PLAYER_EMPTY) && (player_is_observe(p1, g) ||
308                                                   (player_globals.parray[p1].game == g))) {
309           if (first) {
310             if (strcmp(game_globals.garray[g].white_name, game_globals.garray[g].black_name)) {
311               pprintf(p, "Examining %2d [%s vs %s]:", g + 1,
312                       game_globals.garray[g].white_name, game_globals.garray[g].black_name);
313             } else {
314               pprintf(p, "Examining %2d (scratch):", g + 1);
315             }
316             first = 0;
317           }
318           pprintf(p, " %s%s", (player_globals.parray[p1].game == g) ? "#" : "", player_globals.parray[p1].name);
319         }
320       }
321       if (!first)
322         pprintf(p, "\n");
323     }
324   }
325   return COM_OK;
326 }
327
328 int com_unexamine(int p, param_list param)
329 {
330   struct player *pp = &player_globals.parray[p];
331   int g, p1, flag = 0;
332
333   if ((pp->game <0) || ((game_globals.garray[pp->game].status != GAME_EXAMINE) && (game_globals.garray[pp->game].status != GAME_SETUP))) {
334     pprintf(p, "You are not examining any games.\n");
335     return COM_OK;
336   }
337   g = pp->game;
338   pp->game = -1;
339   for (p1 = 0; p1 < player_globals.p_num; p1++) {
340     if (player_globals.parray[p1].status != PLAYER_PROMPT)
341       continue;
342     if ((player_globals.parray[p1].game == g) &&(p != p1)) {
343       /* ok - there are other examiners to take over the game */
344       flag = 1;
345     }
346     if ((player_is_observe(p1, g)) || (player_globals.parray[p1].game == g)) {
347       pprintf(p1, "%s stopped examining game %d.\n", pp->name, g + 1);
348     }
349   }
350   if (!flag) {
351     for (p1 = 0; p1 < player_globals.p_num; p1++) {
352       if (player_globals.parray[p1].status != PLAYER_PROMPT)
353         continue;
354       if (player_is_observe(p1, g)) {
355         pprintf(p1, "There are no examiners.\n");
356         pcommand(p1, "unobserve %d", g + 1);
357       }
358     }
359     game_remove(g);
360   }
361   pprintf(p, "You are no longer examining game %d.\n", g + 1);
362   announce_avail(p);
363   return COM_OK;
364 }
365
366 int com_mexamine(int p, param_list param)
367 {
368   struct player *pp = &player_globals.parray[p];
369   int g, p1, p2;
370
371   if ((pp->game <0) ||
372       ((game_globals.garray[pp->game].status != GAME_EXAMINE) &&
373       (game_globals.garray[pp->game].status != GAME_SETUP))) {
374     pprintf(p, "You are not examining any games.\n");
375     return COM_OK;
376   }
377   p1 = player_find_part_login(param[0].val.word);
378   if (p1 < 0) {
379     pprintf(p, "No user named \"%s\" is logged in.\n", param[0].val.word);
380     return COM_OK;
381   }
382   g = pp->game;
383   if (!player_is_observe(p1, g)) {
384     pprintf(p, "%s must observe the game you are analysing.\n", player_globals.parray[p1].name);
385     return COM_OK;
386   } else {
387     if (player_globals.parray[p1].game >=0) {
388       pprintf(p, "%s is already analysing the game.\n", player_globals.parray[p1].name);
389       return COM_OK;
390     }
391     /* if we get here - let's make him examiner of the game */
392     unobserveAll(p1);           /* fix for Xboard */
393     decline_withdraw_offers(p1, -1, PEND_MATCH,DO_DECLINE | DO_WITHDRAW);
394     decline_withdraw_offers(p1, -1, PEND_SIMUL,DO_WITHDRAW);
395
396     player_globals.parray[p1].game = g; /* yep - it really is that easy :-) */
397     pprintf(p1, "You are now examiner of game %d.\n", g + 1);
398     send_board_to(g, p1);       /* pos not changed - but fixes Xboard */
399     for (p2 = 0; p2 < player_globals.p_num; p2++) {
400       if (player_globals.parray[p2].status != PLAYER_PROMPT)
401         continue;
402       if (p2 == p1)
403         continue;
404       if ((player_is_observe(p2, g)) || (player_globals.parray[p2].game == g)) {
405         pprintf_prompt(p2, "%s is now examiner of game %d.\n", player_globals.parray[p1].name, g + 1);
406       }
407     }
408   }
409   if (CheckPFlag(p2, PFLAG_OPEN)) /* was open */
410     announce_notavail(p2);
411   return COM_OK;
412 }
413
414 int com_moves(int p, param_list param)
415 {
416   struct player *pp = &player_globals.parray[p];
417   int g;
418   int p1;
419
420   if (param[0].type == TYPE_NULL) {
421     if (pp->game >=0) {
422       g = pp->game;
423     } else if (pp->num_observe) {
424       for (g = 0; g < pp->num_observe; g++) {
425         pprintf(p, "%s\n", movesToString(pp->observe_list[g], 0));
426       }
427       return COM_OK;
428     } else {
429       pprintf(p, "You are neither playing, observing nor examining a game.\n");
430       return COM_OK;
431     }
432   } else {
433     g = GameNumFromParam(p, &p1, &param[0]);
434     if (g < 0)
435       return COM_OK;
436   }
437   if ((g < 0) || (g >= game_globals.g_num) || ((game_globals.garray[g].status != GAME_ACTIVE) &&
438                                   (game_globals.garray[g].status != GAME_EXAMINE) &&
439                                   (game_globals.garray[g].status != GAME_SETUP))) {
440     pprintf(p, "There is no such game.\n");
441     return COM_OK;
442   }
443   if ((game_globals.garray[g].white != p) && (game_globals.garray[g].black != p) && (game_globals.garray[g].private) && (pp->adminLevel < ADMIN_ADMIN)) {
444     pprintf(p, "Sorry, that is a private game.\n");
445     return COM_OK;
446   }
447   pprintf(p, "%s\n", movesToString(g, 0));      /* pgn may break interfaces? */
448   return COM_OK;
449 }
450
451 int com_mailmoves(int p, param_list param)
452 {
453   struct player *pp = &player_globals.parray[p];
454   int g;
455   int p1;
456   char subj[81];
457
458   if (!CheckPFlag(p, PFLAG_REG)) {
459     pprintf (p,"Unregistered players cannot use mailmoves.\n");
460     return COM_OK;
461   }
462
463   if (param[0].type == TYPE_NULL) {
464     if (pp->game >=0) {
465       g = pp->game;
466     } else {
467       pprintf(p, "You are neither playing, observing nor examining a game.\n");
468       return COM_OK;
469     }
470   } else {
471     g = GameNumFromParam(p, &p1, &param[0]);
472     if (g < 0)
473       return COM_OK;
474   }
475   if ((g < 0) || (g >= game_globals.g_num) || ((game_globals.garray[g].status != GAME_ACTIVE) && (game_globals.garray[g].status != GAME_EXAMINE))) {
476     pprintf(p, "There is no such game.\n");
477     return COM_OK;
478   }
479   if ((game_globals.garray[g].white != p) && (game_globals.garray[g].black != p) && (game_globals.garray[g].private) && (pp->adminLevel < ADMIN_ADMIN)) {
480     pprintf(p, "Sorry, that is a private game.\n");
481     return COM_OK;
482   }
483   sprintf(subj, "FICS game report %s vs %s", game_globals.garray[g].white_name, game_globals.garray[g].black_name);
484   if (mail_string_to_user(p, subj,
485                           movesToString(g, CheckPFlag(p, PFLAG_PGN)))) {
486     pprintf(p, "Moves NOT mailed, perhaps your address is incorrect.\n");
487   } else {
488     pprintf(p, "Moves mailed.\n");
489   }
490   return COM_OK;
491 }
492
493 static int old_mail_moves(int p,int mail, param_list param)
494 {
495   int p1, connected;
496   int count;
497   FILE *fp;
498   char fname[MAX_FILENAME_SIZE];
499   char tmp[2048];
500   char *ptmp = tmp;
501
502  if (mail && !CheckPFlag(p, PFLAG_REG)) {
503     pprintf (p,"Unregistered players cannot use mailoldmoves.\n");
504     return COM_OK;
505   }
506
507    if (param[0].type == TYPE_WORD) {
508     if (!FindPlayer(p, param[0].val.word, &p1, &connected))
509       return COM_OK;
510   } else {
511       p1 = p;
512       connected = 1;
513   }
514  
515   sprintf(fname, "%s/player_data/%c/%s.%s", STATS_DIR,
516           player_globals.parray[p1].login[0], player_globals.parray[p1].login, STATS_GAMES);
517   fp = fopen_s(fname, "r"); /* old moves now looks in history to save mem - DAV */
518
519   if (!fp) {
520     pprintf (p,"There is no old game for %s.\n", player_globals.parray[p1].name);
521     if (!connected)
522       player_remove(p1);
523     return COM_OK;
524   } 
525
526   while (!feof(fp))
527       fgets(tmp, 1024, fp);
528   sscanf(ptmp, "%d", &count);
529   fclose(fp); /* find the last game played in history */
530
531   pprintf (p,"Last game for %s was history game %d.\n",player_globals.parray[p1].name,count);
532
533   if (mail)
534    pcommand (p,"mailstored %s %d",player_globals.parray[p1].name,count);
535   else
536    pcommand (p,"smoves %s %d",player_globals.parray[p1].name,count);
537
538   if (!connected)
539     player_remove(p1); 
540   
541   return COM_OK;
542 }
543
544 int com_oldmoves(int p, param_list param)
545 {
546   return old_mail_moves(p , 0, param);
547 }
548
549 int com_mailoldmoves(int p, param_list param)
550 {
551   return old_mail_moves(p , 1, param);
552 }
553
554 void ExamineScratch(int p,  param_list param,int setup)
555 {
556   struct player *pp = &player_globals.parray[p];
557   char category[100], board[100], parsebuf[100];
558   char *val;
559   int confused = 0;
560   int g;
561
562   category[0] = '\0';
563   board[0] = '\0';
564
565   if ((param[0].val.string != pp->name) &&
566       (param[1].type == TYPE_WORD)) {
567         strcpy(category, param[0].val.string);
568         strcpy(board, param[1].val.string);
569   } else if (param[1].type != TYPE_NULL) {
570
571       val = param[1].val.string;
572
573       while (!confused && (sscanf(val, " %99s", parsebuf) == 1)) {
574         val = eatword(eatwhite(val));
575         if ((category[0] != '\0') && (board[0] == '\0'))
576           strcpy(board, parsebuf);
577         else if (isdigit(*parsebuf)) {
578           pprintf(p, "You can't specify time controls.\n");
579           return;
580         } else if (category[0] == '\0')
581           strcpy(category, parsebuf);
582         else
583           confused = 1;
584       }
585       if (confused) {
586         pprintf(p, "Can't interpret %s in match command.\n", parsebuf);
587         return;
588       }
589   }
590
591
592   if (category[0] && !board[0]) {
593     pprintf(p, "You must specify a board and a category.\n");
594     return;
595   }
596
597   g = game_new();
598
599   unobserveAll(p);
600
601   decline_withdraw_offers(p, -1, PEND_MATCH,DO_DECLINE | DO_WITHDRAW);
602   decline_withdraw_offers(p, -1, PEND_SIMUL,DO_WITHDRAW);
603
604   game_globals.garray[g].wInitTime = game_globals.garray[g].wIncrement = 0;
605   game_globals.garray[g].bInitTime = game_globals.garray[g].bIncrement = 0;
606   game_globals.garray[g].timeOfStart = tenth_secs();
607   game_globals.garray[g].wTime = game_globals.garray[g].bTime = 0;
608   game_globals.garray[g].rated = 0;
609   game_globals.garray[g].clockStopped = 0;
610   game_globals.garray[g].type = TYPE_UNTIMED;
611   game_globals.garray[g].white = game_globals.garray[g].black = p;
612   game_globals.garray[g].startTime = tenth_secs();
613   game_globals.garray[g].lastMoveTime = game_globals.garray[g].startTime;
614   game_globals.garray[g].lastDecTime = game_globals.garray[g].startTime;
615   game_globals.garray[g].totalHalfMoves = 0;
616
617   pp->side = WHITE;       /* oh well... */
618   pp->game = g;
619
620
621   if (!setup)
622     pprintf(p, "Starting a game in examine (scratch) mode.\n");
623   else
624     pprintf(p, "Starting a game in examine (setup) mode.\n");
625
626   if (category[0]) {
627     pprintf(p, "Loading from catagory: %s, board: %s.\n", category, board);
628   }
629
630   if (setup) {
631     board_clear(&game_globals.garray[g].game_state);
632     game_globals.garray[g].status = GAME_SETUP;
633   } else {
634     game_globals.garray[g].status = GAME_EXAMINE;
635     if (board_init(g,&game_globals.garray[g].game_state, category, board)) {
636       pprintf(p, "PROBLEM LOADING BOARD. Examine Aborted.\n");
637       d_printf( "CHESSD: PROBLEM LOADING BOARD. Examine Aborted.\n");
638       pp->game = -1;
639       game_remove(g);
640       return;
641     }
642   }
643
644   game_globals.garray[g].game_state.gameNum = g;
645   strcpy(game_globals.garray[g].white_name, pp->name);
646   strcpy(game_globals.garray[g].black_name, pp->name);
647   game_globals.garray[g].white_rating = game_globals.garray[g].black_rating = pp->s_stats.rating;
648
649   send_boards(g);
650   if (CheckPFlag(p, PFLAG_OPEN)) /*was open */
651     announce_notavail(p);
652 }
653
654 static int ExamineStored(FILE * fp, int p, char type)
655 {
656   struct player *pp = &player_globals.parray[p];
657   int g;
658   char category[100], board[100];
659   struct game *gg;
660
661   unobserveAll(p);
662
663   decline_withdraw_offers(p, -1, PEND_MATCH,DO_DECLINE | DO_WITHDRAW);
664   decline_withdraw_offers(p, -1, PEND_SIMUL,DO_WITHDRAW);
665
666   g = game_new();
667   gg = &game_globals.garray[g];
668   category[0] = '\0';
669   board[0] = '\0';
670   if (board_init(g,&gg->game_state, category, board)) {
671     pprintf(p, "PROBLEM LOADING BOARD. Examine Aborted.\n");
672     d_printf( "CHESSD: PROBLEM LOADING BOARD %s %s. Examine Aborted.\n",
673             category, board);
674     game_remove(g);
675     return -1;
676   }
677
678   gg->status = GAME_EXAMINE;
679
680   if (type == 'w' || type == 'n') {
681     if (ReadGameAttrs_exam(fp, g) < 0) {
682       pprintf(p, "Either this is an old wild/nonstandard game or the gamefile is corrupt.\n");
683       game_remove(g);
684       return -1;
685     }
686   } else if (ReadGameAttrs(fp, g) < 0) {
687     pprintf(p, "Gamefile is corrupt; please notify an admin.\n");
688     game_remove(g);
689     return -1;
690   }
691
692   gg->totalHalfMoves = gg->numHalfMoves;
693   gg->numHalfMoves = 0;
694   gg->revertHalfMove = 0;
695   gg->white = p;
696   gg->black = p;
697   gg->game_state.gameNum = g;
698
699   gg->startTime = tenth_secs();
700   gg->lastMoveTime = gg->startTime;
701   gg->lastDecTime = gg->startTime;
702
703   pp->side = WHITE;     /* oh well... */
704   pp->game = g;
705
706   send_boards(g);
707   if (CheckPFlag(p, PFLAG_OPEN)) /* was open */
708     announce_notavail(p);
709
710   return g;
711 }
712
713 static void ExamineAdjourned(int p, int p1, int p2)
714 {
715         FILE *fp;
716         char *p1Login, *p2Login;
717         int g;
718         
719         p1Login = player_globals.parray[p1].login;
720         p2Login = player_globals.parray[p2].login;
721
722         fp = fopen_p("%s/%c/%s-%s", "r", ADJOURNED_DIR, *p1Login, p1Login, p2Login);
723         if (!fp) fp = fopen_p("%s/%c/%s-%s", "r", ADJOURNED_DIR, *p2Login, p1Login, p2Login);
724         if (!fp) fp = fopen_p("%s/%c/%s-%s", "r", ADJOURNED_DIR, *p2Login, p2Login, p1Login);
725         if (!fp) fp = fopen_p("%s/%c/%s-%s", "r", ADJOURNED_DIR, *p1Login, p2Login, p1Login);
726         if (!fp) {
727                 pprintf(p, "No stored game between \"%s\" and \"%s\".\n",
728                         player_globals.parray[p1].name, player_globals.parray[p2].name);
729                 return;
730         }
731         /* Assume old wild games are of type blitz - adjudicators should be
732            careful */
733         g = ExamineStored(fp, p,'b');
734         fclose(fp);
735         
736         if (g >= 0) {
737                 if (game_globals.garray[g].white_name[0] == '\0')
738                         strcpy(game_globals.garray[g].white_name, p1Login);
739                 if (game_globals.garray[g].black_name[0] == '\0')
740                         strcpy(game_globals.garray[g].black_name, p2Login);
741         }
742 }
743
744 /* type is now returned because prior to V1.7.1 loading a wild game for 
745   examining was impossible since the initial position was not saved.
746   Only types blitz,standard,lightning and untimed may be loaded for
747   examining if the gamefile version is less than 4 */ 
748
749 static char *FindHistory(int p, int p1, int game,char* type)
750 {
751   FILE *fpHist;
752   static char fileName[MAX_FILENAME_SIZE];
753   int index;
754   long when;
755   char typestr[4];
756
757   sprintf(fileName, "%s/player_data/%c/%s.%s", STATS_DIR,
758           player_globals.parray[p1].login[0], player_globals.parray[p1].login, STATS_GAMES);
759   fpHist = fopen_s(fileName, "r");
760   if (fpHist == NULL) {
761     pprintf(p, "No games in history for %s.\n", player_globals.parray[p1].name);
762     return(NULL);
763   }
764   do {
765     fscanf(fpHist, "%d %*c %*d %*c %*d %*s %s %*d %*d %*d %*d %*s %*s %ld",
766            &index, typestr, &when);
767   } while (!feof(fpHist) && index != game);
768
769   if (feof(fpHist)) {
770     pprintf(p, "There is no history game %d for %s.\n", game, player_globals.parray[p1].name);
771     fclose(fpHist);
772     return(NULL);
773   }
774   fclose(fpHist);
775
776   if (typestr[0] != 'p')
777     *type = typestr[0];
778   else
779     *type = typestr[1];
780
781   sprintf(fileName, "%s/%ld/%ld", HISTORY_DIR, when % 100, when);
782   return(fileName);
783 }
784
785 /* I want to know how game ended/ECO code */
786
787 static char *FindHistory2(int p, int p1,int game, char* Eco,char* End)
788 {
789   FILE *fpHist;
790   static char fileName[MAX_FILENAME_SIZE];
791   int index;
792   long when;
793   
794   sprintf(fileName, "%s/player_data/%c/%s.%s", STATS_DIR,
795           player_globals.parray[p1].login[0], player_globals.parray[p1].login, STATS_GAMES);
796   fpHist = fopen_s(fileName, "r");
797   if (fpHist == NULL) {
798     pprintf(p, "No games in history for %s.\n", player_globals.parray[p1].name);
799     return(NULL);
800   }
801   do {
802     fscanf(fpHist, "%d %*c %*d %*c %*d %*s %*s %*d %*d %*d %*d %s %s %ld",
803            &index, Eco, End, &when);
804   } while (!feof(fpHist) && index != game);
805
806   if (feof(fpHist)) {
807     pprintf(p, "There is no history game %d for %s.\n", game, player_globals.parray[p1].name);
808     fclose(fpHist);
809     return(NULL);
810   }
811   fclose(fpHist);
812
813   sprintf(fileName, "%s/%ld/%ld", HISTORY_DIR, when % 100, when);
814   return(fileName);
815 }
816
817 int com_wname(int p, param_list param)
818 {
819   struct player *pp = &player_globals.parray[p];
820
821   int g = pp->game;
822
823   if ((g < 0) ||
824      ((g >= 0) &&
825         !((game_globals.garray[g].status != GAME_EXAMINE) || (game_globals.garray[g].status != GAME_SETUP)))) {
826     pprintf (p, "You are not examining or setting up a game.\n");
827     return COM_OK;
828   }
829
830   if (param[0].type == TYPE_NULL)
831     strcpy (game_globals.garray[g].white_name,pp->name);
832   else {
833
834     if (strlen (param[0].val.word) > MAX_LOGIN_NAME - 1) {
835       pprintf (p,"The maximum length of a name is %d characters.\n",MAX_LOGIN_NAME - 1);
836       return COM_OK;
837     } else
838       strcpy (game_globals.garray[g].white_name,param[0].val.word);
839   }
840
841   send_boards(g);
842   return COM_OK;
843 }
844
845 int com_bname(int p, param_list param)
846 {
847   struct player *pp = &player_globals.parray[p];
848
849   int g = pp->game;
850
851   if ((g < 0) ||
852      ((g >= 0) &&
853         !((game_globals.garray[g].status != GAME_EXAMINE) || (game_globals.garray[g].status != GAME_SETUP)))) {
854     pprintf (p, "You are not examining or setting up a game.\n");
855     return COM_OK;
856   }
857
858   if (param[0].type == TYPE_NULL)
859     strcpy (game_globals.garray[g].black_name,pp->name);
860   else {
861
862     if (strlen (param[0].val.word) > MAX_LOGIN_NAME - 1) {
863       pprintf (p,"The maximum length of a name is %d characters.\n",MAX_LOGIN_NAME - 1);
864       return COM_OK;
865     } else
866       strcpy (game_globals.garray[g].black_name,param[0].val.word);
867   }
868
869   send_boards(g);
870   return COM_OK;
871 }
872
873 static void ExamineHistory(int p, int p1, int game)
874 {
875         char *fileName;
876         char type;
877
878         fileName = FindHistory(p, p1, game,&type);
879         if (fileName) {
880                 FILE *fpGame = fopen_p("%s", "r", fileName);
881                 if (fpGame == NULL) {
882                         pprintf(p, "History game %d not available for %s.\n", 
883                                 game, player_globals.parray[p1].name);
884                 } else {
885                         ExamineStored(fpGame, p,type);
886                         fclose(fpGame);
887                 }
888         }
889 }
890
891 static void ExamineJournal(int p,int p1,char slot)
892 {
893         struct player *pp = &player_globals.parray[p];
894         char* name_from = player_globals.parray[p1].login;
895         char type;
896         FILE *fpGame;
897
898         if (CheckPFlag(p1, PFLAG_JPRIVATE) && (p != p1)
899             && (pp->adminLevel < ADMIN_ADMIN)) {
900                 pprintf (p,"Sorry, this journal is private.\n");
901                 return;
902         }
903
904         if ((slot - 'a' + 1) > MAX_JOURNAL && 
905             !check_admin(p1, ADMIN_ADMIN) && 
906             !titled_player(p,player_globals.parray[p1].login)) {
907                 pprintf(p,"%s's maximum journal entry is %c\n",
908                         player_globals.parray[p1].name,
909                         toupper((char)(MAX_JOURNAL + 'A' - 1)));
910                 return;
911         }
912
913         fpGame = fopen_p("%s/%c/%s.%c", "r", JOURNAL_DIR, name_from[0],name_from,slot);
914         if (fpGame == NULL) {
915                 pprintf(p, "Journal entry %c is not available for %s.\n", toupper (slot),
916                         player_globals.parray[p1].name);
917         } else {
918                 char *fname;
919                 asprintf(&fname, "%s/player_data/%c/%s.journal",
920                          STATS_DIR, name_from[0],name_from);
921                 slot = toupper(slot);
922
923                 if ((type = get_journalgame_type (p,fname,slot)) == '\0') {
924                         pprintf(p, "Journal entry %c is not available for %s or is corrupt.\n",
925                                 slot, player_globals.parray[p1].name);
926                 } else {
927                         ExamineStored(fpGame, p,type);
928                 }
929
930                 free(fname);
931                 fclose (fpGame);
932         }
933 }
934
935 int com_examine(int p, param_list param)
936 {
937   struct player *pp = &player_globals.parray[p];
938   int p1, p2 = p, p1conn, p2conn = 1;
939   char* wincstring;
940   char fname[MAX_FILENAME_SIZE];
941
942   if (pp->game >=0) {
943      if ((game_globals.garray[pp->game].status == GAME_EXAMINE) || 
944         (game_globals.garray[pp->game].status == GAME_SETUP))
945        pprintf(p, "You are already examining a game.\n");
946      else
947        pprintf(p, "You are playing a game.\n");
948   } else if (param[0].type == TYPE_NULL) {
949     ExamineScratch(p, param, 0);
950   } else if (param[0].type != TYPE_NULL) {
951     if ((param[1].type == TYPE_NULL) && (!strcmp(param[0].val.word,"setup"))) {
952         ExamineScratch(p, param, 1);
953         return COM_OK;
954     } else if (param[1].type == TYPE_WORD) {
955       sprintf(fname, "%s/%s/%s", BOARD_DIR, param[0].val.word, param[1].val.word);
956       if (file_exists(fname)) {
957         ExamineScratch(p, param, 0);
958         return COM_OK;
959       }
960     }
961     if (!FindPlayer(p, param[0].val.word, &p1, &p1conn))
962       return COM_OK;
963
964     if (param[1].type == TYPE_INT)
965       ExamineHistory(p, p1, param[1].val.integer); 
966     else {
967       if (param[1].type == TYPE_WORD) {
968
969         /* Lets check the journal */
970         wincstring = param[1].val.word;
971         if ((strlen(wincstring) == 1) && (isalpha(wincstring[0]))) {
972           ExamineJournal(p,p1,wincstring[0]);
973           if (!p1conn)
974             player_remove(p1);
975           return COM_OK;
976         } else {
977           if (!FindPlayer(p, param[1].val.word, &p2, &p2conn)) {
978             if (!p1conn)
979               player_remove(p1);
980             return COM_OK;
981           }
982         }
983       } 
984       ExamineAdjourned(p, p1, p2);
985       if (!p2conn)
986        player_remove(p2);
987     }
988     if (!p1conn)
989      player_remove(p1);
990   }
991   return COM_OK;
992 }
993
994 int com_stored(int p, param_list param)
995 {
996   int count = 0;
997   DIR *dirp;
998   struct dirent *dp;
999
1000   int p1, connected;
1001   char dname[MAX_FILENAME_SIZE];
1002
1003   if (param[0].type == TYPE_WORD) {
1004     if (!FindPlayer(p, param[0].val.word, &p1, &connected))
1005       return COM_OK;
1006   } else {
1007       p1 = p;
1008       connected = 1;
1009   }
1010
1011   sprintf(dname, "%s/%c", ADJOURNED_DIR, player_globals.parray[p1].login[0]);
1012   dirp = opendir(dname);
1013   if (!dirp) {
1014     pprintf(p, "Player %s has no games stored.\n", player_globals.parray[p1].name);
1015     if (!connected)
1016       player_remove(p1);
1017     return COM_OK;
1018   }
1019   pprintf(p, "Stored games for %s:\n", player_globals.parray[p1].name);
1020   for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
1021     if (file_has_pname(dp->d_name, player_globals.parray[p1].login)) {
1022       pprintf(p, "   %s vs. %s\n", file_wplayer(dp->d_name), file_bplayer(dp->d_name));
1023       count++;
1024     }
1025   }
1026
1027   closedir(dirp);
1028   pprintf (p,"%d stored game%sfound.\n",count,(count == 1 ? " " : "s "));
1029   if (!connected)
1030     player_remove(p1);
1031   return COM_OK;
1032 }
1033
1034 static void stored_mail_moves(int p, int mail, param_list param)
1035 {
1036   struct player *pp = &player_globals.parray[p];
1037   int wp, wconnected, bp, bconnected, gotit = 0;
1038   int g = -1;
1039   char type; /* dummy */
1040   char* wincstring = NULL;
1041   char* name_from = NULL;
1042   char* fileName = NULL;
1043   char fileName2[MAX_FILENAME_SIZE];
1044   FILE* fpGame;
1045   
1046  if (mail && !CheckPFlag(p, PFLAG_REG)) {
1047     pprintf (p,"Unregistered players cannot use mailstored.\n");
1048     return;
1049   }
1050
1051   if (!FindPlayer(p, param[0].val.word, &wp, &wconnected))
1052     return;
1053
1054   if (param[1].type == TYPE_INT) { /* look for a game from history */
1055     fileName = FindHistory(p, wp, param[1].val.integer,&type);
1056     if (fileName != NULL) {
1057       fpGame = fopen_s(fileName, "r");
1058       if (fpGame == NULL) {
1059         pprintf(p, "History game %d not available for %s.\n", param[1].val.integer, player_globals.parray[wp].name);
1060       } else {
1061         g = game_new();
1062         if (ReadGameAttrs(fpGame, g) < 0)
1063           pprintf(p, "Gamefile is corrupt; please notify an admin.\n");
1064         else
1065           gotit = 1;
1066         fclose(fpGame);
1067       }
1068     }
1069   } else { /* Let's test for journal */
1070     name_from = param[0].val.word;
1071     wincstring = param[1].val.word;
1072     if ((strlen(wincstring) == 1) && (isalpha(wincstring[0]))) {
1073       if (CheckPFlag(wp, PFLAG_JPRIVATE)
1074           && (pp->adminLevel < ADMIN_ADMIN) && (p != wp)) {
1075         pprintf (p,"Sorry, the journal from which you are trying to fetch is private.\n");
1076       } else {
1077         if (((wincstring[0] - 'a' + 1) > MAX_JOURNAL) && (player_globals.parray[wp].adminLevel < ADMIN_ADMIN) && (!titled_player(p,player_globals.parray[wp].login))) {
1078           pprintf (p,"%s's maximum journal entry is %c\n",player_globals.parray[wp].name,toupper((char)(MAX_JOURNAL + 'A' - 1)));
1079         } else {
1080           sprintf(fileName2, "%s/%c/%s.%c", JOURNAL_DIR, name_from[0],name_from,wincstring[0]);
1081           fpGame = fopen_s(fileName2, "r");
1082           if (fpGame == NULL) {
1083             pprintf(p, "Journal entry %c is not available for %s.\n", toupper(wincstring[0]),
1084             player_globals.parray[wp].name);
1085           } else {
1086             g = game_new();
1087             if (ReadGameAttrs(fpGame, g) < 0)
1088               pprintf(p, "Journal entry is corrupt; please notify an admin.\n");
1089             else
1090               gotit = 1;
1091             fclose(fpGame);
1092           }
1093         }
1094       }
1095     } else {
1096   
1097        /* look for a stored game between the players */
1098
1099       if (FindPlayer(p, param[1].val.word, &bp, &bconnected)) {
1100         g = game_new();
1101         if (game_read(g, wp, bp) >= 0) {  /* look for a game white-black, */
1102           gotit = 1;
1103         } else if (game_read(g, bp, wp) >= 0) {   /* or black-white */
1104           gotit = 1;
1105         } else {
1106           pprintf(p, "There is no stored game %s vs. %s\n", player_globals.parray[wp].name, player_globals.parray[bp].name);
1107         }
1108         if (!bconnected)
1109           player_remove(bp);
1110       }
1111     }
1112   }
1113   if (gotit) {
1114     if (strcasecmp(pp->name, game_globals.garray[g].white_name) && strcasecmp(player_globals.parray[p]
1115 .name, game_globals.garray[g].black_name) && game_globals.garray[g].private && (pp->adminLevel < ADMIN_ADMIN)) {
1116       pprintf(p, "Sorry, that is a private game.\n");
1117     } else {
1118       if (mail == 1) { /*Do mailstored */
1119         char subj[81];
1120         if (param[1].type == TYPE_INT)
1121           sprintf(subj, "FICS history game: %s %d", player_globals.parray[wp].name, param[1].val.integer);
1122         else
1123           if ((strlen(wincstring) == 1) && (isalpha(wincstring[0]))) {
1124             sprintf(subj, "FICS journal game %s vs %s", game_globals.garray[g].white_name, game_globals.garray[g].black_name); 
1125           } else {
1126             sprintf(subj, "FICS adjourned game %s vs %s", game_globals.garray[g].white_name, game_globals.garray[g].black_name);
1127           }
1128         if (mail_string_to_user(p, subj,
1129                                 movesToString(g, CheckPFlag(p, PFLAG_PGN))))
1130           pprintf(p, "Moves NOT mailed, perhaps your address is incorrect.\n");
1131         else
1132           pprintf(p, "Moves mailed.\n");
1133       } else {
1134         pprintf(p, "%s\n", movesToString(g, 0));
1135       } /* Do smoves */
1136     }
1137   }
1138   if (!wconnected)
1139     player_remove(wp);
1140   if (g != -1)
1141     game_remove(g);
1142 }
1143
1144 /* Tidied up a bit but still messy */
1145
1146 int com_mailstored(int p, param_list param)
1147 {
1148   stored_mail_moves(p, 1, param);
1149   return COM_OK;
1150 }
1151
1152 int com_smoves(int p, param_list param)
1153 {
1154   stored_mail_moves(p, 0, param);
1155   return COM_OK;
1156 }
1157
1158 int com_sposition(int p, param_list param)
1159 {
1160   struct player *pp = &player_globals.parray[p];
1161   int wp, wconnected, bp, bconnected, confused = 0;
1162   int g;
1163
1164   if (!FindPlayer(p, param[0].val.word, &wp, &wconnected))
1165     return (COM_OK);
1166   if (!FindPlayer(p, param[1].val.word, &bp, &bconnected)) {
1167     if (!wconnected)
1168       player_remove(wp);
1169     return (COM_OK);
1170   }
1171
1172   g = game_new();
1173   if (game_read(g, wp, bp) < 0) {       /* if no game white-black, */
1174     if (game_read(g, bp, wp) < 0) {     /* look for black-white */
1175       confused = 1;
1176       pprintf(p, "There is no stored game %s vs. %s\n", player_globals.parray[wp].name, player_globals.parray[bp].name);
1177     } else {
1178       int tmp;
1179       tmp = wp;
1180       wp = bp;
1181       bp = tmp;
1182       tmp = wconnected;
1183       wconnected = bconnected;
1184       bconnected = tmp;
1185     }
1186   }
1187   if (!confused) {
1188     if ((wp != p) && (bp != p) && (game_globals.garray[g].private) && (pp->adminLevel < ADMIN_ADMIN)) {
1189       pprintf(p, "Sorry, that is a private game.\n");
1190     } else {
1191       game_globals.garray[g].white = wp;
1192       game_globals.garray[g].black = bp;
1193       game_globals.garray[g].startTime = tenth_secs();
1194       game_globals.garray[g].lastMoveTime = game_globals.garray[g].startTime;
1195       game_globals.garray[g].lastDecTime = game_globals.garray[g].startTime;
1196       pprintf(p, "Position of stored game %s vs. %s\n", player_globals.parray[wp].name, player_globals.parray[bp].name);
1197       send_board_to(g, p);
1198     }
1199   }
1200   game_remove(g);
1201   if (!wconnected)
1202     player_remove(wp);
1203   if (!bconnected)
1204     player_remove(bp);
1205   return COM_OK;
1206 }
1207
1208 int com_forward(int p, param_list param)
1209 {
1210   struct player *pp = &player_globals.parray[p];
1211   int nHalfMoves = 1;
1212   int g, i;
1213   int p1;
1214   unsigned now;
1215
1216   if (!((pp->game >=0) && ((game_globals.garray[pp->game].status == GAME_EXAMINE) || (game_globals.garray[pp->game].status == GAME_SETUP)))) {
1217     pprintf(p, "You are not examining any games.\n");
1218     return COM_OK;
1219   }
1220   if (game_globals.garray[pp->game].status == GAME_SETUP) {
1221     pprintf (p,"You can't move forward yet, the position is still being set up.\n");
1222     return COM_OK;
1223   }
1224   g = pp->game;
1225   if (!strcmp(game_globals.garray[g].white_name, game_globals.garray[g].black_name)) {
1226     pprintf(p, "You cannot go forward; no moves are stored.\n");
1227     return COM_OK;
1228   }
1229   if (param[0].type == TYPE_INT) {
1230     nHalfMoves = param[0].val.integer;
1231   }
1232   if (game_globals.garray[g].numHalfMoves > game_globals.garray[g].revertHalfMove) {
1233     pprintf(p, "Game %u: No more moves.\n", g);
1234     return COM_OK;
1235   }
1236   if (game_globals.garray[g].numHalfMoves < game_globals.garray[g].totalHalfMoves) {
1237     for (p1 = 0; p1 < player_globals.p_num; p1++) {
1238       if (player_globals.parray[p1].status != PLAYER_PROMPT)
1239         continue;
1240       if (player_is_observe(p1, g) || player_globals.parray[p1].game == g) {
1241         pprintf(p1, "Game %u: %s goes forward %d move%s.\n",
1242                 g,
1243                 pp->name, nHalfMoves, (nHalfMoves == 1) ? "" : "s");
1244       }
1245     }
1246   }
1247   for (i = 0; i < nHalfMoves; i++) {
1248     if (game_globals.garray[g].numHalfMoves < game_globals.garray[g].totalHalfMoves) {
1249       execute_move(&game_globals.garray[g].game_state, &game_globals.garray[g].moveList[game_globals.garray[g].numHalfMoves], 1);
1250       if (game_globals.garray[g].numHalfMoves + 1 > game_globals.garray[g].examMoveListSize) {
1251         game_globals.garray[g].examMoveListSize += 20;  /* Allocate 20 moves at a
1252                                                    time */
1253         game_globals.garray[g].examMoveList = (struct move_t *) realloc(game_globals.garray[g].examMoveList, sizeof(struct move_t) * game_globals.garray[g].examMoveListSize);
1254       }
1255       game_globals.garray[g].examMoveList[game_globals.garray[g].numHalfMoves] = game_globals.garray[g].moveList[game_globals.garray[g].numHalfMoves];
1256       game_globals.garray[g].revertHalfMove++;
1257       game_globals.garray[g].numHalfMoves++;
1258     } else {
1259       for (p1 = 0; p1 < player_globals.p_num; p1++) {
1260         if (player_globals.parray[p1].status != PLAYER_PROMPT)
1261           continue;
1262         if (player_is_observe(p1, g) || player_globals.parray[p1].game == g) {
1263           pprintf(p1, "Game %u: End of game.\n", g);
1264         }
1265       }
1266       break;
1267     }
1268   }
1269   /* roll back time */
1270   if (game_globals.garray[g].game_state.onMove == WHITE) {
1271     game_globals.garray[g].wTime += (game_globals.garray[g].lastDecTime - game_globals.garray[g].lastMoveTime);
1272   } else {
1273     game_globals.garray[g].bTime += (game_globals.garray[g].lastDecTime - game_globals.garray[g].lastMoveTime);
1274   }
1275   now = tenth_secs();
1276   if (game_globals.garray[g].numHalfMoves == 0)
1277     game_globals.garray[g].timeOfStart = now;
1278   game_globals.garray[g].lastMoveTime = now;
1279   game_globals.garray[g].lastDecTime = now;
1280   send_boards(g);
1281
1282   if (game_globals.garray[g].revertHalfMove == game_globals.garray[g].totalHalfMoves) {
1283           for (p1 = 0; p1 < player_globals.p_num; p1++) {
1284                   if (player_globals.parray[p1].status != PLAYER_PROMPT) continue;
1285                   if (player_is_observe(p1, g) || player_globals.parray[p1].game == g) {
1286                           pprintf(p1, "Game %u: %s %s\n", g, EndString(g,0), EndSym(g));
1287                   }
1288           }
1289   }
1290
1291   return COM_OK;
1292 }
1293
1294 int com_backward(int p, param_list param)
1295 {
1296   struct player *pp = &player_globals.parray[p];
1297   int nHalfMoves = 1;
1298   int g, i;
1299   int p1;
1300   unsigned now;
1301
1302   if (!((pp->game >=0) && ((game_globals.garray[pp->game].status == GAME_EXAMINE) || (game_globals.garray[pp->game].status == GAME_SETUP)))) {
1303     pprintf(p, "You are not examining any games.\n");
1304     return COM_OK;
1305   }
1306   if (game_globals.garray[pp->game].status == GAME_SETUP) {
1307     pprintf (p,"You can't move backward yet, the postion is still being set up.\n");
1308     return COM_OK;
1309   }
1310
1311   g = pp->game;
1312   if (param[0].type == TYPE_INT) {
1313     nHalfMoves = param[0].val.integer;
1314   }
1315   if (game_globals.garray[g].numHalfMoves != 0) {
1316     for (p1 = 0; p1 < player_globals.p_num; p1++) {
1317       if (player_globals.parray[p1].status != PLAYER_PROMPT)
1318         continue;
1319       if (player_is_observe(p1, g) || player_globals.parray[p1].game == g) {
1320         pprintf(p1, "Game %u: %s backs up %d move%s.\n",
1321                 g,
1322                 pp->name, nHalfMoves, (nHalfMoves == 1) ? "" : "s");
1323       }
1324     }
1325   }
1326   for (i = 0; i < nHalfMoves; i++) {
1327     if (backup_move(g, REL_EXAMINE) != MOVE_OK) {
1328       for (p1 = 0; p1 < player_globals.p_num; p1++) {
1329         if (player_globals.parray[p1].status != PLAYER_PROMPT)
1330           continue;
1331         if (player_is_observe(p1, g) || player_globals.parray[p1].game == g) {
1332           pprintf(p1, "Game %u: Beginning of game.\n", g);
1333         }
1334       }
1335
1336       break;
1337     }
1338   }
1339   if (game_globals.garray[g].numHalfMoves < game_globals.garray[g].revertHalfMove) {
1340     game_globals.garray[g].revertHalfMove = game_globals.garray[g].numHalfMoves;
1341   }
1342   /* roll back time */
1343   if (game_globals.garray[g].game_state.onMove == WHITE) {
1344     game_globals.garray[g].wTime += (game_globals.garray[g].lastDecTime - game_globals.garray[g].lastMoveTime);
1345   } else {
1346     game_globals.garray[g].bTime += (game_globals.garray[g].lastDecTime - game_globals.garray[g].lastMoveTime);
1347   }
1348   now = tenth_secs();
1349   if (game_globals.garray[g].numHalfMoves == 0)
1350     game_globals.garray[g].timeOfStart = now;
1351   game_globals.garray[g].lastMoveTime = now;
1352   game_globals.garray[g].lastDecTime = now;
1353   send_boards(g);
1354   return COM_OK;
1355 }
1356
1357 int com_revert(int p, param_list param)
1358 {
1359   struct player *pp = &player_globals.parray[p];
1360   int nHalfMoves = 1;
1361   int g, i;
1362   int p1;
1363   unsigned now;
1364
1365   if (!((pp->game >=0) && ((game_globals.garray[pp->game].status == GAME_EXAMINE)|| (game_globals.garray[pp->game].status == GAME_SETUP)))) {
1366     pprintf(p, "You are not examining any games.\n");
1367     return COM_OK;
1368   }
1369   if (game_globals.garray[pp->game].status == GAME_SETUP) {
1370     pprintf (p,"You can't move revert yet, the position is still being set up.\n");
1371     return COM_OK;
1372   }
1373   g = pp->game;
1374   nHalfMoves = game_globals.garray[g].numHalfMoves - game_globals.garray[g].revertHalfMove;
1375   if (nHalfMoves == 0) {
1376     pprintf(p, "Game %u: Already at mainline.\n", g);
1377     return COM_OK;
1378   }
1379   if (nHalfMoves < 0) {         /* eek - should NEVER happen! */
1380     d_printf( "OUCH! in com_revert: nHalfMoves < 0\n");
1381     return COM_OK;
1382   }
1383   for (p1 = 0; p1 < player_globals.p_num; p1++) {
1384     if (player_globals.parray[p1].status != PLAYER_PROMPT)
1385       continue;
1386     if (player_is_observe(p1, g) || player_globals.parray[p1].game == g) {
1387       pprintf(p1, "Game %u: %s reverts to mainline.\n", 
1388               g, pp->name);
1389     }
1390   }
1391   for (i = 0; i < nHalfMoves; i++) {
1392     backup_move(g, REL_EXAMINE);/* should never return error */
1393   }
1394   /* roll back time */
1395   if (game_globals.garray[g].game_state.onMove == WHITE) {
1396     game_globals.garray[g].wTime += (game_globals.garray[g].lastDecTime - game_globals.garray[g].lastMoveTime);
1397   } else {
1398     game_globals.garray[g].bTime += (game_globals.garray[g].lastDecTime - game_globals.garray[g].lastMoveTime);
1399   }
1400   now = tenth_secs();
1401   if (game_globals.garray[g].numHalfMoves == 0)
1402     game_globals.garray[g].timeOfStart = now;
1403   game_globals.garray[g].lastMoveTime = now;
1404   game_globals.garray[g].lastDecTime = now;
1405   send_boards(g);
1406   return COM_OK;
1407 }
1408
1409 int com_history(int p, param_list param)
1410 {
1411   int p1, connected;
1412   char fname[MAX_FILENAME_SIZE];
1413
1414   if (param[0].type == TYPE_WORD) {
1415     if (!FindPlayer(p, param[0].val.word, &p1, &connected))
1416       return COM_OK;
1417   } else {
1418       p1 = p;
1419       connected = 1;
1420   }
1421
1422   sprintf(fname, "%s/player_data/%c/%s.%s", STATS_DIR, player_globals.parray[p1].login[0],
1423           player_globals.parray[p1].login, STATS_GAMES);
1424   pgames(p, p1, fname);
1425   if (!connected)
1426     player_remove(p1);
1427   return COM_OK;
1428 }
1429
1430 int com_journal(int p, param_list param)
1431 {
1432   struct player *pp = &player_globals.parray[p];
1433   int p1, connected;
1434   char fname[MAX_FILENAME_SIZE];
1435
1436     if (param[0].type == TYPE_WORD) {
1437     if (!FindPlayer(p, param[0].val.word, &p1, &connected))
1438       return COM_OK;
1439   } else {
1440       p1 = p;
1441       connected = 1;
1442   }
1443
1444   if (!CheckPFlag(p1, PFLAG_REG)) {
1445     pprintf (p,"Only registered players may keep a journal.\n");
1446     if (!connected)
1447       player_remove(p1);
1448     return COM_OK;
1449   }
1450   if (CheckPFlag(p1, PFLAG_JPRIVATE) && (p != p1)
1451       && (pp->adminLevel < ADMIN_ADMIN)) {
1452     pprintf (p,"Sorry, this journal is private.\n");
1453     if (!connected)
1454       player_remove(p1);
1455     return COM_OK;
1456   }
1457   sprintf(fname, "%s/player_data/%c/%s.%s", STATS_DIR, player_globals.parray[p1].login[0],
1458           player_globals.parray[p1].login, STATS_JOURNAL);
1459   pjournal(p, p1, fname);
1460   if (!connected)
1461     player_remove(p1);
1462   return COM_OK;
1463 }
1464
1465 /* Remove a journal item */
1466
1467 int com_jkill(int p, param_list param)
1468 {
1469   struct player *pp = &player_globals.parray[p];
1470   FILE* Journal;
1471   char* kill = param[0].val.word;
1472   char fname[MAX_FILENAME_SIZE];
1473   char fname_new[MAX_FILENAME_SIZE];
1474   int empty;
1475
1476   if (!CheckPFlag(p, PFLAG_REG)) {
1477      pprintf (p,"Only registered players may keep a journal.\n");
1478      return COM_OK;
1479   }
1480
1481   if ((strlen(kill) != 1) || (!(isalpha(kill[0])))) {
1482     pprintf (p,"Journal entries are referenced by single letters.\n");
1483     return COM_OK;
1484   }
1485
1486   if (((kill[0] - 'a' + 1) > MAX_JOURNAL) && (pp->adminLevel < ADMIN_ADMIN)
1487   && (!titled_player(p,pp->login))) {
1488     pprintf (p,"Your maximum journal entry is %c\n",toupper ((char)(MAX_JOURNAL
1489 + 'A' - 1)));
1490     return COM_OK;
1491   }
1492
1493   sprintf(fname, "%s/player_data/%c/%s.journal",
1494       STATS_DIR, pp->login[0],pp->login);
1495   sprintf (fname_new,"%s.w",fname);
1496
1497   Journal = fopen_s(fname, "r");
1498   if (Journal == NULL) {
1499      pprintf(p, "You don't have a journal.\n");
1500      return COM_OK;
1501      }
1502
1503   kill[0] = toupper(kill[0]);
1504  
1505   if (removejournalitem(p, kill[0], Journal,fname_new,&empty)) {
1506     fclose (Journal);
1507     rename (fname_new,fname);
1508     if (empty)
1509        unlink (fname);
1510     sprintf(fname, "%s/%c/%s.%c",
1511          JOURNAL_DIR, pp->login[0],pp->login,tolower(kill[0]));
1512     unlink(fname);
1513     pprintf (p,"Journal entry %c deleted.\n",kill[0]);
1514   } else { 
1515     pprintf (p,"You have no journal entry %c.\n",kill[0]);
1516     fclose (Journal);
1517     unlink (fname_new);
1518   }
1519   return COM_OK;
1520 }
1521
1522 static void jsave_journalentry(int p,char save_spot,int p1,char from_spot,char* to_file)
1523 {
1524   struct player *pp = &player_globals.parray[p];
1525   FILE *Game;
1526   char fname[MAX_FILENAME_SIZE], fname2[MAX_FILENAME_SIZE];
1527   char* name_from = player_globals.parray[p1].login;
1528   char* name_to = pp->login;
1529   struct journal* j;
1530
1531   sprintf(fname, "%s/%c/%s.%c", JOURNAL_DIR, name_from[0],name_from,from_spot);
1532   Game = fopen_s(fname, "r");
1533   if (Game == NULL) {
1534      pprintf(p, "Journal entry %c not available for %s.\n", toupper(from_spot), player_globals.parray[p1].name);
1535      return;
1536      }
1537   fclose (Game);
1538   
1539   sprintf(fname2, "%s/%c/%s.%c", JOURNAL_DIR, name_to[0],name_to,save_spot);
1540
1541   if (file_copy(fname, fname2) != 0) {
1542     pprintf (p,"Copy in jsave_journalentry failed!\n");
1543     pprintf (p,"Please report this to an admin.\n");
1544     d_printf("CHESSD: Copy failed in jsave_journalentry\n");
1545     return;
1546   }
1547
1548  sprintf(fname, "%s/player_data/%c/%s.%s", STATS_DIR, name_from[0],
1549           name_from, STATS_JOURNAL);
1550
1551  j = (struct journal*) malloc (sizeof(struct journal));
1552  if (!journal_get_info(p,tolower(from_spot),j,fname)) {
1553    free (j);
1554    return;
1555  }
1556
1557  j->slot = toupper(save_spot);
1558  addjournalitem(p, j, to_file);
1559
1560  pprintf(p,"Journal entry %s %c saved in slot %c in journal.\n",player_globals.parray[p1].name, toupper(from_spot), toupper(save_spot));
1561  free (j);
1562 }
1563
1564 static void jsave_history(int p,char save_spot,int p1,int from,char* to_file)
1565 {
1566         struct player *pp = &player_globals.parray[p];
1567         char Eco[100];
1568         char End[100];
1569         char jfname[MAX_FILENAME_SIZE];
1570         char* HistoryFname = FindHistory2(p, p1, from, Eco, End);
1571         /* End symbol Mat Res, etc is the only thing we can't find out */
1572         char command[MAX_FILENAME_SIZE*2+3];
1573         char* name_to = pp->login;
1574         FILE *Game;
1575         int g;
1576         struct journal* j;
1577         
1578         if (HistoryFname == NULL) {
1579                 return;
1580         }
1581         Game = fopen_s(HistoryFname, "r");
1582         if (Game == NULL) {
1583                 pprintf(p, "History game %d not available for %s.\n", 
1584                         from, player_globals.parray[p1].name);
1585                 return;
1586         } 
1587
1588         sprintf(jfname, "%s/%c/%s.%c", JOURNAL_DIR, name_to[0],name_to,save_spot);
1589         unlink(jfname); /* necessary if cp is hard aliased to cp -i */
1590         sprintf(command, "cp %s %s",HistoryFname,jfname);
1591         if (file_copy(HistoryFname, jfname) != 0) {
1592                 pprintf (p,"Copy in jsave_history failed!\n");
1593                 pprintf (p,"Please report this to an admin.\n");
1594                 d_printf("CHESSD: Copy failed in jsave_journalentry\n");
1595                 return;
1596         }
1597         g = game_new(); /* Open a dummy game */
1598         
1599         if (ReadGameAttrs(Game, g) < 0) {
1600                 pprintf (p,"Gamefile is corrupt. Please tell an admin.\n");
1601                 game_remove(g);
1602                 fclose (Game);
1603                 return;
1604         }
1605         fclose (Game);
1606         
1607         j = (struct journal*) malloc (sizeof(struct journal));
1608         
1609         if (game_globals.garray[g].private) {
1610                 j->type[0] = 'p';
1611         } else {
1612                 j->type[0] = ' ';
1613         }
1614         if (game_globals.garray[g].type == TYPE_BLITZ) {
1615                 j->type[1] = 'b';
1616         } else if (game_globals.garray[g].type == TYPE_WILD) {
1617                 j->type[1] = 'w';
1618         } else if (game_globals.garray[g].type == TYPE_STAND) {
1619                 j->type[1] = 's';
1620         } else if (game_globals.garray[g].type == TYPE_LIGHT) {
1621                 j->type[1] = 'l';
1622         } else if (game_globals.garray[g].type == TYPE_BUGHOUSE) {
1623                 j->type[1] = 'B';
1624         } else {
1625                 if (game_globals.garray[g].type == TYPE_NONSTANDARD)
1626                         j->type[1] = 'n';
1627                 else
1628                         j->type[1] = 'u';
1629         }
1630         if (game_globals.garray[g].rated) {
1631                 j->type[2] = 'r';
1632         } else {
1633                 j->type[2] = 'u';
1634         }
1635         j->type[3] = '\0';
1636         
1637         j->slot = toupper(save_spot);
1638         strcpy (j->WhiteName, game_globals.garray[g].white_name);
1639         j->WhiteRating = game_globals.garray[g].white_rating;
1640         strcpy (j->BlackName, game_globals.garray[g].black_name);
1641         j->BlackRating = game_globals.garray[g].black_rating;
1642         j->t = game_globals.garray[g].wInitTime;
1643         j->i = game_globals.garray[g].wIncrement;
1644         strcpy (j->eco, Eco);
1645         strcpy (j->ending, End);
1646         strcpy (j->result, EndSym(g));
1647         
1648         addjournalitem(p, j, to_file);
1649         game_remove(g);
1650         pprintf(p,"Game %s %d saved in slot %c in journal.\n",
1651                 player_globals.parray[p1].name, from, toupper(save_spot));
1652         free(j);
1653 }
1654
1655 int com_jsave(int p, param_list param)
1656 {
1657   struct player *pp = &player_globals.parray[p];
1658   int p1, p1conn;
1659   char* to = param[0].val.word;
1660   char* from;
1661   char fname[MAX_FILENAME_SIZE];
1662
1663   if (!CheckPFlag(p, PFLAG_REG)) {
1664      pprintf (p,"Only registered players may keep a journal.\n");
1665      return COM_OK;
1666   }
1667
1668   if ((strlen(to) != 1) || (!(isalpha(to[0])))) {
1669     pprintf (p,"Journal entries are referenced by single letters.\n");
1670     return COM_OK;
1671   }
1672
1673   if (((to[0] - 'a' + 1) > MAX_JOURNAL) && (pp->adminLevel < ADMIN_ADMIN) && (!titled_player(p,pp->login))) {
1674     pprintf (p,"Your maximum journal entry is %c\n",toupper ((char)(MAX_JOURNAL + 'A' - 1)));
1675     return COM_OK;
1676   }
1677
1678   if (!FindPlayer(p, param[1].val.word, &p1, &p1conn))
1679     return COM_OK;
1680
1681   if (param[2].type == TYPE_INT) {
1682
1683   /* grab from a history */
1684     sprintf (fname,"%s/player_data/%c/%s.%s",STATS_DIR,pp->login[0],pp->login, STATS_JOURNAL);
1685     jsave_history(p, to[0], p1, param[2].val.integer,fname); 
1686
1687   } else {
1688
1689   from = param[2].val.word;
1690
1691   if ((strlen(from) != 1) || (!(isalpha(from[0])))) {
1692     pprintf (p,"Journal entries are referenced by single letters.\n");
1693     if (!p1conn)
1694       player_remove(p1);
1695     return COM_OK;
1696     }
1697
1698   if (CheckPFlag(p1, PFLAG_JPRIVATE)
1699       && (pp->adminLevel < ADMIN_ADMIN) && (p != p1)) {
1700     pprintf (p,"Sorry, the journal from which you are trying to fetch is private.\n");
1701
1702     if (!p1conn)
1703        player_remove(p1);
1704     return COM_OK;
1705     }
1706
1707   if (((to[0] - 'a' + 1) > MAX_JOURNAL) && (player_globals.parray[p1].adminLevel < ADMIN_ADMIN) && (!titled_player(p,player_globals.parray[p1].login))) {
1708     pprintf (p,"%s's maximum journal entry is %c\n",player_globals.parray[p1].name,toupper((char)(MAX_JOURNAL + 'A' - 1)));
1709     if (!p1conn)
1710        player_remove(p1);
1711     return COM_OK;
1712   }
1713   if (( p == p1) && (to[0] == from [0])) {
1714     pprintf (p,"Source and destination entries are the same.\n");
1715     return COM_OK;
1716   }
1717   
1718   /* grab from a journal */
1719
1720   sprintf(fname, "%s/player_data/%c/%s.%s", STATS_DIR, pp->login[0],
1721           pp->login, STATS_JOURNAL);
1722   jsave_journalentry(p,to[0],p1, from[0], fname);
1723
1724   }
1725   if (!p1conn)
1726      player_remove(p1);
1727   return COM_OK;
1728 }
1729
1730 int com_refresh(int p, param_list param)
1731 {
1732   struct player *pp = &player_globals.parray[p];
1733   int g, p1;
1734
1735   if (param[0].type == TYPE_NULL) {
1736     if (pp->game >= 0) {
1737       send_board_to(pp->game, p);
1738     } else {                    /* Do observing in here */
1739       if (pp->num_observe) {
1740         for (g = 0; g < pp->num_observe; g++) {
1741           send_board_to(pp->observe_list[g], p);
1742         }
1743       } else {
1744         pprintf(p, "You are neither playing, observing nor examining a game.\n");
1745         return COM_OK;
1746       }
1747     }
1748   } else {
1749     g = GameNumFromParam (p, &p1, &param[0]);
1750     if (g < 0)
1751       return COM_OK;
1752     if ((g >= game_globals.g_num) || ((game_globals.garray[g].status != GAME_ACTIVE)
1753                         && ((game_globals.garray[g].status != GAME_EXAMINE)
1754                         || (game_globals.garray[g].status != GAME_SETUP)))) {
1755       pprintf(p, "No such game.\n");
1756     } else {
1757
1758       int link = game_globals.garray[g].link;
1759
1760       if ((game_globals.garray[g].private && pp->adminLevel==ADMIN_USER) &&
1761         (game_globals.garray[g].white != p) && (game_globals.garray[g].white != p1)) {
1762         if (link != -1) {
1763           if ((game_globals.garray[link].white != p) && (game_globals.garray[link].black != p)) {
1764             pprintf (p, "Sorry, game %d is a private game.\n", g+1);
1765             return COM_OK;
1766           }
1767         }
1768       }
1769
1770       if (game_globals.garray[g].private)
1771         pprintf(p, "Refreshing static game %d\n", g+1);
1772       send_board_to(g, p);
1773     }
1774   }
1775   return COM_OK;
1776 }
1777
1778 int com_prefresh(int p, param_list param)
1779 {
1780   struct player *pp = &player_globals.parray[p];
1781   int retval, part = pp->partner;
1782
1783   if (part < 0) {
1784     pprintf(p, "You do not have a partner.\n");
1785     return COM_OK;
1786   }
1787   retval = pcommand (p, "refresh %s", player_globals.parray[part].name);
1788   if (retval == COM_OK)
1789     return COM_OK_NOPROMPT;
1790   else
1791     return retval;
1792 }