Use gcore for creating core dump on segfault
[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_noformat(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_noformat(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 (param[1].type == TYPE_NULL) {
459     if (!CheckPFlag(p, PFLAG_REG)) {
460       pprintf (p,"Unregistered players must specify their e-mail address.\n");
461       return COM_OK;
462     }
463   } else if(!safestring(param[1].val.string)) {
464     pprintf (p,"Bad e-mail address.\n");
465     return COM_OK;
466   }
467
468   if (param[0].type == TYPE_NULL) {
469     if (pp->game >=0) {
470       g = pp->game;
471     } else {
472       pprintf(p, "You are neither playing, observing nor examining a game.\n");
473       return COM_OK;
474     }
475   } else {
476     g = GameNumFromParam(p, &p1, &param[0]);
477     if (g < 0)
478       return COM_OK;
479   }
480   if ((g < 0) || (g >= game_globals.g_num) || ((game_globals.garray[g].status != GAME_ACTIVE) && (game_globals.garray[g].status != GAME_EXAMINE))) {
481     pprintf(p, "There is no such game.\n");
482     return COM_OK;
483   }
484   if ((game_globals.garray[g].white != p) && (game_globals.garray[g].black != p) && (game_globals.garray[g].private) && (pp->adminLevel < ADMIN_ADMIN)) {
485     pprintf(p, "Sorry, that is a private game.\n");
486     return COM_OK;
487   }
488   sprintf(subj, "FICS game report %s vs %s", game_globals.garray[g].white_name, game_globals.garray[g].black_name);
489   if (param[1].type == TYPE_NULL ? mail_string_to_user(p, subj,
490                           movesToString(g, CheckPFlag(p, PFLAG_PGN))) :
491                mail_string_to_address(param[1].val.string, subj,
492                           movesToString(g, CheckPFlag(p, PFLAG_PGN)))) {
493     pprintf(p, "Moves NOT mailed, perhaps your address is incorrect.\n");
494   } else {
495     pprintf(p, "Moves mailed.\n");
496   }
497   return COM_OK;
498 }
499
500 static int old_mail_moves(int p,int mail, param_list param)
501 {
502   int p1, connected;
503   int count;
504   FILE *fp;
505   char fname[MAX_FILENAME_SIZE];
506   char tmp[2048];
507   char *ptmp = tmp;
508
509  if (mail && !CheckPFlag(p, PFLAG_REG)) {
510     pprintf (p,"Unregistered players cannot use mailoldmoves.\n");
511     return COM_OK;
512   }
513
514    if (param[0].type == TYPE_WORD) {
515     if (!FindPlayer(p, param[0].val.word, &p1, &connected))
516       return COM_OK;
517   } else {
518       p1 = p;
519       connected = 1;
520   }
521  
522   sprintf(fname, "%s/player_data/%c/%s.%s", STATS_DIR,
523           player_globals.parray[p1].login[0], player_globals.parray[p1].login, STATS_GAMES);
524   fp = fopen_s(fname, "r"); /* old moves now looks in history to save mem - DAV */
525
526   if (!fp) {
527     pprintf (p,"There is no old game for %s.\n", player_globals.parray[p1].name);
528     if (!connected)
529       player_remove(p1);
530     return COM_OK;
531   } 
532
533   while (!feof(fp))
534       fgets(tmp, 1024, fp);
535   sscanf(ptmp, "%d", &count);
536   fclose(fp); /* find the last game played in history */
537
538   pprintf (p,"Last game for %s was history game %d.\n",player_globals.parray[p1].name,count);
539
540   if (mail)
541    pcommand (p,"mailstored %s %d",player_globals.parray[p1].name,count);
542   else
543    pcommand (p,"smoves %s %d",player_globals.parray[p1].name,count);
544
545   if (!connected)
546     player_remove(p1); 
547   
548   return COM_OK;
549 }
550
551 int com_oldmoves(int p, param_list param)
552 {
553   return old_mail_moves(p , 0, param);
554 }
555
556 int com_mailoldmoves(int p, param_list param)
557 {
558   return old_mail_moves(p , 1, param);
559 }
560
561 void ExamineScratch(int p,  param_list param,int setup)
562 {
563   struct player *pp = &player_globals.parray[p];
564   char category[100], board[100], parsebuf[100];
565   char *val;
566   int confused = 0;
567   int g;
568
569   category[0] = '\0';
570   board[0] = '\0';
571
572   if ((param[0].val.string != pp->name) &&
573       (param[1].type == TYPE_WORD)) {
574         strcpy(category, param[0].val.string);
575         strcpy(board, param[1].val.string);
576   } else if (param[1].type != TYPE_NULL) {
577
578       val = param[1].val.string;
579
580       while (!confused && (sscanf(val, " %99s", parsebuf) == 1)) {
581         val = eatword(eatwhite(val));
582         if ((category[0] != '\0') && (board[0] == '\0'))
583           strcpy(board, parsebuf);
584         else if (isdigit(*parsebuf)) {
585           pprintf(p, "You can't specify time controls.\n");
586           return;
587         } else if (category[0] == '\0')
588           strcpy(category, parsebuf);
589         else
590           confused = 1;
591       }
592       if (confused) {
593         pprintf(p, "Can't interpret %s in match command.\n", parsebuf);
594         return;
595       }
596   }
597
598
599   if (category[0] && !board[0]) {
600     pprintf(p, "You must specify a board and a category.\n");
601     return;
602   }
603
604   g = game_new();
605
606   unobserveAll(p);
607
608   decline_withdraw_offers(p, -1, PEND_MATCH,DO_DECLINE | DO_WITHDRAW);
609   decline_withdraw_offers(p, -1, PEND_SIMUL,DO_WITHDRAW);
610
611   game_globals.garray[g].wInitTime = game_globals.garray[g].wIncrement = 0;
612   game_globals.garray[g].bInitTime = game_globals.garray[g].bIncrement = 0;
613   game_globals.garray[g].timeOfStart = tenth_secs();
614   game_globals.garray[g].wTime = game_globals.garray[g].bTime = 0;
615   game_globals.garray[g].rated = 0;
616   game_globals.garray[g].clockStopped = 0;
617   game_globals.garray[g].type = TYPE_UNTIMED;
618   game_globals.garray[g].white = game_globals.garray[g].black = p;
619   game_globals.garray[g].startTime = tenth_secs();
620   game_globals.garray[g].lastMoveTime = game_globals.garray[g].startTime;
621   game_globals.garray[g].lastDecTime = game_globals.garray[g].startTime;
622   game_globals.garray[g].totalHalfMoves = 0;
623
624   pp->side = WHITE;       /* oh well... */
625   pp->game = g;
626
627
628   if (!setup)
629     pprintf(p, "Starting a game in examine (scratch) mode.\n");
630   else
631     pprintf(p, "Starting a game in examine (setup) mode.\n");
632
633   if (category[0]) {
634     pprintf(p, "Loading from catagory: %s, board: %s.\n", category, board);
635   }
636
637   game_globals.garray[g].FENstartPos[0] = 0; // [HGM] new shuffle game
638   if (setup) {
639     board_clear(&game_globals.garray[g].game_state);
640     game_globals.garray[g].status = GAME_SETUP;
641   } else {
642     game_globals.garray[g].status = GAME_EXAMINE;
643     if (board_init(g,&game_globals.garray[g].game_state, category, board)) {
644       pprintf(p, "PROBLEM LOADING BOARD. Examine Aborted.\n");
645       d_printf( "CHESSD: PROBLEM LOADING BOARD. Examine Aborted.\n");
646       pp->game = -1;
647       game_remove(g);
648       return;
649     }
650   }
651
652   game_globals.garray[g].game_state.gameNum = g;
653   strcpy(game_globals.garray[g].white_name, pp->name);
654   strcpy(game_globals.garray[g].black_name, pp->name);
655   game_globals.garray[g].white_rating = game_globals.garray[g].black_rating = pp->s_stats.rating;
656
657   send_boards(g);
658   if (CheckPFlag(p, PFLAG_OPEN)) /*was open */
659     announce_notavail(p);
660 }
661
662 static int ExamineStored(FILE * fp, int p, char type)
663 {
664   struct player *pp = &player_globals.parray[p];
665   int g;
666   char category[100], board[100];
667   struct game *gg;
668
669   unobserveAll(p);
670
671   decline_withdraw_offers(p, -1, PEND_MATCH,DO_DECLINE | DO_WITHDRAW);
672   decline_withdraw_offers(p, -1, PEND_SIMUL,DO_WITHDRAW);
673
674   g = game_new();
675   gg = &game_globals.garray[g];
676   category[0] = '\0';
677   board[0] = '\0';
678   game_globals.garray[g].FENstartPos[0] = 0; // [HGM] make new shuffle for now
679   if (board_init(g,&gg->game_state, category, board)) {
680     pprintf(p, "PROBLEM LOADING BOARD. Examine Aborted.\n");
681     d_printf( "CHESSD: PROBLEM LOADING BOARD %s %s. Examine Aborted.\n",
682             category, board);
683     game_remove(g);
684     return -1;
685   }
686
687   gg->status = GAME_EXAMINE;
688
689   if (type == 'w' || type == 'n') {
690     if (ReadGameAttrs_exam(fp, g) < 0) {
691       pprintf(p, "Either this is an old wild/nonstandard game or the gamefile is corrupt.\n");
692       game_remove(g);
693       return -1;
694     }
695
696     // [HGM] OK, we retreived the game info, which includes variant name as "category/board"
697     // So now we set up the board again, this time for the proper variant (and proper shuffle)
698     sscanf(gg->variant, "%s/%s", category, board);
699     if(category[0] && !board[0]) strcpy(board, "0");
700     if (board_init(g,&gg->game_state, category, board)) {
701       pprintf(p, "PROBLEM LOADING BOARD. Examine Aborted.\n");
702       d_printf( "CHESSD: PROBLEM LOADING BOARD %s %s. Examine Aborted.\n",
703               category, board);
704       game_remove(g);
705       return -1;
706     }
707   } else if (ReadGameAttrs(fp, g) < 0) {
708     pprintf(p, "Gamefile is corrupt; please notify an admin.\n");
709     game_remove(g);
710     return -1;
711   }
712
713   gg->totalHalfMoves = gg->numHalfMoves;
714   gg->numHalfMoves = 0;
715   gg->revertHalfMove = 0;
716   gg->white = p;
717   gg->black = p;
718   gg->game_state.gameNum = g;
719
720   gg->startTime = tenth_secs();
721   gg->lastMoveTime = gg->startTime;
722   gg->lastDecTime = gg->startTime;
723
724   pp->side = WHITE;     /* oh well... */
725   pp->game = g;
726   send_boards(g);
727   if (CheckPFlag(p, PFLAG_OPEN)) /* was open */
728     announce_notavail(p);
729
730   return g;
731 }
732
733 static void ExamineAdjourned(int p, int p1, int p2)
734 {
735         FILE *fp;
736         char *p1Login, *p2Login;
737         int g;
738         
739         p1Login = player_globals.parray[p1].login;
740         p2Login = player_globals.parray[p2].login;
741
742         fp = fopen_p("%s/%c/%s-%s", "r", ADJOURNED_DIR, *p1Login, p1Login, p2Login);
743         if (!fp) fp = fopen_p("%s/%c/%s-%s", "r", ADJOURNED_DIR, *p2Login, p1Login, p2Login);
744         if (!fp) fp = fopen_p("%s/%c/%s-%s", "r", ADJOURNED_DIR, *p2Login, p2Login, p1Login);
745         if (!fp) fp = fopen_p("%s/%c/%s-%s", "r", ADJOURNED_DIR, *p1Login, p2Login, p1Login);
746         if (!fp) {
747                 pprintf(p, "No stored game between \"%s\" and \"%s\".\n",
748                         player_globals.parray[p1].name, player_globals.parray[p2].name);
749                 return;
750         }
751         /* Assume old wild games are of type blitz - adjudicators should be
752            careful */
753         g = ExamineStored(fp, p,'n');
754         fclose(fp);
755         
756         if (g >= 0) {
757                 if (game_globals.garray[g].white_name[0] == '\0')
758                         strcpy(game_globals.garray[g].white_name, p1Login);
759                 if (game_globals.garray[g].black_name[0] == '\0')
760                         strcpy(game_globals.garray[g].black_name, p2Login);
761         }
762 }
763
764 /* type is now returned because prior to V1.7.1 loading a wild game for 
765   examining was impossible since the initial position was not saved.
766   Only types blitz,standard,lightning and untimed may be loaded for
767   examining if the gamefile version is less than 4 */ 
768
769 static char *FindHistory(int p, int p1, int game,char* type)
770 {
771   FILE *fpHist;
772   static char fileName[MAX_FILENAME_SIZE];
773   int index, last = -1;
774   long when;
775   char typestr[4];
776
777   sprintf(fileName, "%s/player_data/%c/%s.%s", STATS_DIR,
778           player_globals.parray[p1].login[0], player_globals.parray[p1].login, STATS_GAMES);
779  again:
780   fpHist = fopen_s(fileName, "r");
781   if (fpHist == NULL) {
782     pprintf(p, "No games in history for %s.\n", player_globals.parray[p1].name);
783     return(NULL);
784   }
785   do {
786     fscanf(fpHist, "%d %*c %*d %*c %*d %*s %s %*d %*d %*d %*d %*s %*s %ld",
787            &index, typestr, &when);
788     last = index; // [HGM] remember most recent game
789   } while (!feof(fpHist) && index != game);
790
791   if(game < 0) { // [HGM] requested game relative to end
792     game += last + 1; // calculate absolute game number
793     if(game < 0) game += 100; // wrap
794     if(game >= 0) { // try again with valid absolute number
795       fclose(fpHist);
796       goto again;
797     }
798   }
799
800   if (feof(fpHist)) {
801     pprintf(p, "There is no history game %d for %s.\n", game, player_globals.parray[p1].name);
802     fclose(fpHist);
803     return(NULL);
804   }
805   fclose(fpHist);
806
807   if (typestr[0] != 'p')
808     *type = typestr[0];
809   else
810     *type = typestr[1];
811
812   sprintf(fileName, "%s/%ld/%ld", HISTORY_DIR, when % 100, when);
813   return(fileName);
814 }
815
816 /* I want to know how game ended/ECO code */
817
818 static char *FindHistory2(int p, int p1,int game, char* Eco,char* End)
819 {
820   FILE *fpHist;
821   static char fileName[MAX_FILENAME_SIZE];
822   int index;
823   long when;
824   
825   sprintf(fileName, "%s/player_data/%c/%s.%s", STATS_DIR,
826           player_globals.parray[p1].login[0], player_globals.parray[p1].login, STATS_GAMES);
827   fpHist = fopen_s(fileName, "r");
828   if (fpHist == NULL) {
829     pprintf(p, "No games in history for %s.\n", player_globals.parray[p1].name);
830     return(NULL);
831   }
832   do {
833     fscanf(fpHist, "%d %*c %*d %*c %*d %*s %*s %*d %*d %*d %*d %s %s %ld",
834            &index, Eco, End, &when);
835   } while (!feof(fpHist) && index != game);
836
837   if (feof(fpHist)) {
838     pprintf(p, "There is no history game %d for %s.\n", game, player_globals.parray[p1].name);
839     fclose(fpHist);
840     return(NULL);
841   }
842   fclose(fpHist);
843
844   sprintf(fileName, "%s/%ld/%ld", HISTORY_DIR, when % 100, when);
845   return(fileName);
846 }
847
848 int com_wname(int p, param_list param)
849 {
850   struct player *pp = &player_globals.parray[p];
851
852   int g = pp->game;
853
854   if ((g < 0) ||
855      ((g >= 0) &&
856         !((game_globals.garray[g].status != GAME_EXAMINE) || (game_globals.garray[g].status != GAME_SETUP)))) {
857     pprintf (p, "You are not examining or setting up a game.\n");
858     return COM_OK;
859   }
860
861   if (param[0].type == TYPE_NULL)
862     strcpy (game_globals.garray[g].white_name,pp->name);
863   else {
864
865     if (strlen (param[0].val.word) > MAX_LOGIN_NAME - 1) {
866       pprintf (p,"The maximum length of a name is %d characters.\n",MAX_LOGIN_NAME - 1);
867       return COM_OK;
868     } else
869       strcpy (game_globals.garray[g].white_name,param[0].val.word);
870   }
871
872   send_boards(g);
873   return COM_OK;
874 }
875
876 int com_bname(int p, param_list param)
877 {
878   struct player *pp = &player_globals.parray[p];
879
880   int g = pp->game;
881
882   if ((g < 0) ||
883      ((g >= 0) &&
884         !((game_globals.garray[g].status != GAME_EXAMINE) || (game_globals.garray[g].status != GAME_SETUP)))) {
885     pprintf (p, "You are not examining or setting up a game.\n");
886     return COM_OK;
887   }
888
889   if (param[0].type == TYPE_NULL)
890     strcpy (game_globals.garray[g].black_name,pp->name);
891   else {
892
893     if (strlen (param[0].val.word) > MAX_LOGIN_NAME - 1) {
894       pprintf (p,"The maximum length of a name is %d characters.\n",MAX_LOGIN_NAME - 1);
895       return COM_OK;
896     } else
897       strcpy (game_globals.garray[g].black_name,param[0].val.word);
898   }
899
900   send_boards(g);
901   return COM_OK;
902 }
903
904 static void ExamineHistory(int p, int p1, int game)
905 {
906         char *fileName;
907         char type;
908
909         fileName = FindHistory(p, p1, game,&type);
910         if (fileName) {
911                 FILE *fpGame = fopen_p("%s", "r", fileName);
912                 if (fpGame == NULL) {
913                         pprintf(p, "History game %d not available for %s.\n", 
914                                 game, player_globals.parray[p1].name);
915                 } else {
916                         ExamineStored(fpGame, p,type);
917                         fclose(fpGame);
918                 }
919         }
920 }
921
922 static void ExamineJournal(int p,int p1,char slot)
923 {
924         struct player *pp = &player_globals.parray[p];
925         char* name_from = player_globals.parray[p1].login;
926         char type;
927         FILE *fpGame;
928
929         if (CheckPFlag(p1, PFLAG_JPRIVATE) && (p != p1)
930             && (pp->adminLevel < ADMIN_ADMIN)) {
931                 pprintf (p,"Sorry, this journal is private.\n");
932                 return;
933         }
934
935         if ((slot - 'a' + 1) > MAX_JOURNAL && 
936             !check_admin(p1, ADMIN_ADMIN) && 
937             !titled_player(p,player_globals.parray[p1].login)) {
938                 pprintf(p,"%s's maximum journal entry is %c\n",
939                         player_globals.parray[p1].name,
940                         toupper((char)(MAX_JOURNAL + 'A' - 1)));
941                 return;
942         }
943
944         fpGame = fopen_p("%s/%c/%s.%c", "r", JOURNAL_DIR, name_from[0],name_from,slot);
945         if (fpGame == NULL) {
946                 pprintf(p, "Journal entry %c is not available for %s.\n", toupper (slot),
947                         player_globals.parray[p1].name);
948         } else {
949                 char *fname;
950                 asprintf(&fname, "%s/player_data/%c/%s.journal",
951                          STATS_DIR, name_from[0],name_from);
952                 slot = toupper(slot);
953
954                 if ((type = get_journalgame_type (p,fname,slot)) == '\0') {
955                         pprintf(p, "Journal entry %c is not available for %s or is corrupt.\n",
956                                 slot, player_globals.parray[p1].name);
957                 } else {
958                         ExamineStored(fpGame, p,type);
959                 }
960
961                 free(fname);
962                 fclose (fpGame);
963         }
964 }
965
966 int com_examine(int p, param_list param)
967 {
968   struct player *pp = &player_globals.parray[p];
969   int p1, p2 = p, p1conn, p2conn = 1;
970   char* wincstring;
971   char fname[MAX_FILENAME_SIZE];
972
973   if (pp->game >=0) {
974      if ((game_globals.garray[pp->game].status == GAME_EXAMINE) || 
975         (game_globals.garray[pp->game].status == GAME_SETUP))
976        pprintf(p, "You are already examining a game.\n");
977      else
978        pprintf(p, "You are playing a game.\n");
979   } else if (param[0].type == TYPE_NULL) {
980     ExamineScratch(p, param, 0);
981   } else if (param[0].type != TYPE_NULL) {
982     if ((param[1].type == TYPE_NULL) && (!strcmp(param[0].val.word,"setup"))) {
983         ExamineScratch(p, param, 1);
984         return COM_OK;
985     } else if (param[1].type == TYPE_WORD) {
986       sprintf(fname, "%s/%s/%s", BOARD_DIR, param[0].val.word, param[1].val.word);
987       if (file_exists(fname)) {
988         ExamineScratch(p, param, 0);
989         return COM_OK;
990       }
991     }
992     if (!FindPlayer(p, param[0].val.word, &p1, &p1conn))
993       return COM_OK;
994
995     if (param[1].type == TYPE_INT)
996       ExamineHistory(p, p1, param[1].val.integer); 
997     else {
998       if (param[1].type == TYPE_WORD) {
999
1000         /* Lets check the journal */
1001         wincstring = param[1].val.word;
1002         if ((strlen(wincstring) == 1) && (isalpha(wincstring[0]))) {
1003           ExamineJournal(p,p1,wincstring[0]);
1004           if (!p1conn)
1005             player_remove(p1);
1006           return COM_OK;
1007         } else {
1008           if (!FindPlayer(p, param[1].val.word, &p2, &p2conn)) {
1009             if (!p1conn)
1010               player_remove(p1);
1011             return COM_OK;
1012           }
1013         }
1014       } 
1015       ExamineAdjourned(p, p1, p2);
1016       if (!p2conn)
1017        player_remove(p2);
1018     }
1019     if (!p1conn)
1020      player_remove(p1);
1021   }
1022   return COM_OK;
1023 }
1024
1025 int com_stored(int p, param_list param)
1026 {
1027   int count = 0;
1028   DIR *dirp;
1029   struct dirent *dp;
1030
1031   int p1, connected;
1032   char dname[MAX_FILENAME_SIZE];
1033
1034   if (param[0].type == TYPE_WORD) {
1035     if (!FindPlayer(p, param[0].val.word, &p1, &connected))
1036       return COM_OK;
1037   } else {
1038       p1 = p;
1039       connected = 1;
1040   }
1041
1042   sprintf(dname, "%s/%c", ADJOURNED_DIR, player_globals.parray[p1].login[0]);
1043   dirp = opendir(dname);
1044   if (!dirp) {
1045     pprintf(p, "Player %s has no games stored.\n", player_globals.parray[p1].name);
1046     if (!connected)
1047       player_remove(p1);
1048     return COM_OK;
1049   }
1050   pprintf(p, "Stored games for %s:\n", player_globals.parray[p1].name);
1051   for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
1052     if (file_has_pname(dp->d_name, player_globals.parray[p1].login)) {
1053       pprintf(p, "   %s vs. %s\n", file_wplayer(dp->d_name), file_bplayer(dp->d_name));
1054       count++;
1055     }
1056   }
1057
1058   closedir(dirp);
1059   pprintf (p,"%d stored game%sfound.\n",count,(count == 1 ? " " : "s "));
1060   if (!connected)
1061     player_remove(p1);
1062   return COM_OK;
1063 }
1064
1065 static void stored_mail_moves(int p, int mail, param_list param)
1066 {
1067   struct player *pp = &player_globals.parray[p];
1068   int wp, wconnected, bp, bconnected, gotit = 0;
1069   int g = -1;
1070   char type; /* dummy */
1071   char* wincstring = NULL;
1072   char* name_from = NULL;
1073   char* fileName = NULL;
1074   char fileName2[MAX_FILENAME_SIZE];
1075   FILE* fpGame;
1076   
1077   if (param[2].type == TYPE_NULL) {
1078     if (!CheckPFlag(p, PFLAG_REG)) {
1079       pprintf (p,"Unregistered players must specify their e-mail address.\n");
1080       return COM_OK;
1081     }
1082   } else if(!safestring(param[2].val.string)) {
1083     pprintf (p,"Bad e-mail address.\n");
1084     return COM_OK;
1085   }
1086
1087   if (!FindPlayer(p, param[0].val.word, &wp, &wconnected))
1088     return;
1089
1090   if (param[1].type == TYPE_WORD && sscanf(param[1].val.word, "%d", &(param[1].val.integer)) == 1 && param[1].val.integer < 0)
1091     param[1].type = TYPE_INT; /* [HGM] allow negative number */
1092
1093   if (param[1].type == TYPE_INT) { /* look for a game from history */
1094     fileName = FindHistory(p, wp, param[1].val.integer,&type);
1095     if (fileName != NULL) {
1096       fpGame = fopen_s(fileName, "r");
1097       if (fpGame == NULL) {
1098         pprintf(p, "History game %d not available for %s.\n", param[1].val.integer, player_globals.parray[wp].name);
1099       } else {
1100         g = game_new();
1101         if (ReadGameAttrs(fpGame, g) < 0)
1102           pprintf(p, "Gamefile is corrupt; please notify an admin.\n");
1103         else
1104           gotit = 1;
1105         fclose(fpGame);
1106       }
1107     }
1108   } else { /* Let's test for journal */
1109     name_from = param[0].val.word;
1110     wincstring = param[1].val.word;
1111     if ((strlen(wincstring) == 1) && (isalpha(wincstring[0]))) {
1112       if (CheckPFlag(wp, PFLAG_JPRIVATE)
1113           && (pp->adminLevel < ADMIN_ADMIN) && (p != wp)) {
1114         pprintf (p,"Sorry, the journal from which you are trying to fetch is private.\n");
1115       } else {
1116         if (((wincstring[0] - 'a' + 1) > MAX_JOURNAL) && (player_globals.parray[wp].adminLevel < ADMIN_ADMIN) && (!titled_player(p,player_globals.parray[wp].login))) {
1117           pprintf (p,"%s's maximum journal entry is %c\n",player_globals.parray[wp].name,toupper((char)(MAX_JOURNAL + 'A' - 1)));
1118         } else {
1119           sprintf(fileName2, "%s/%c/%s.%c", JOURNAL_DIR, name_from[0],name_from,wincstring[0]);
1120           fpGame = fopen_s(fileName2, "r");
1121           if (fpGame == NULL) {
1122             pprintf(p, "Journal entry %c is not available for %s.\n", toupper(wincstring[0]),
1123             player_globals.parray[wp].name);
1124           } else {
1125             g = game_new();
1126             if (ReadGameAttrs(fpGame, g) < 0)
1127               pprintf(p, "Journal entry is corrupt; please notify an admin.\n");
1128             else
1129               gotit = 1;
1130             fclose(fpGame);
1131           }
1132         }
1133       }
1134     } else {
1135   
1136        /* look for a stored game between the players */
1137
1138       if (FindPlayer(p, param[1].val.word, &bp, &bconnected)) {
1139         g = game_new();
1140         if (game_read(g, wp, bp) >= 0) {  /* look for a game white-black, */
1141           gotit = 1;
1142         } else if (game_read(g, bp, wp) >= 0) {   /* or black-white */
1143           gotit = 1;
1144         } else {
1145           pprintf(p, "There is no stored game %s vs. %s\n", player_globals.parray[wp].name, player_globals.parray[bp].name);
1146         }
1147         if (!bconnected)
1148           player_remove(bp);
1149       }
1150     }
1151   }
1152   if (gotit) {
1153     if (strcasecmp(pp->name, game_globals.garray[g].white_name) && strcasecmp(player_globals.parray[p]
1154 .name, game_globals.garray[g].black_name) && game_globals.garray[g].private && (pp->adminLevel < ADMIN_ADMIN)) {
1155       pprintf(p, "Sorry, that is a private game.\n");
1156     } else {
1157       if (mail == 1) { /*Do mailstored */
1158         char subj[81];
1159         if (param[1].type == TYPE_INT)
1160           sprintf(subj, "FICS history game: %s %d", player_globals.parray[wp].name, param[1].val.integer);
1161         else
1162           if ((strlen(wincstring) == 1) && (isalpha(wincstring[0]))) {
1163             sprintf(subj, "FICS journal game %s vs %s", game_globals.garray[g].white_name, game_globals.garray[g].black_name); 
1164           } else {
1165             sprintf(subj, "FICS adjourned game %s vs %s", game_globals.garray[g].white_name, game_globals.garray[g].black_name);
1166           }
1167         if (param[2].type == TYPE_NULL ? mail_string_to_user(p, subj,
1168                                 movesToString(g, CheckPFlag(p, PFLAG_PGN))) :
1169                         mail_string_to_address(param[2].val.string, subj,
1170                                 movesToString(g, CheckPFlag(p, PFLAG_PGN))))
1171           pprintf(p, "Moves NOT mailed, perhaps your address is incorrect.\n");
1172         else
1173           pprintf(p, "Moves mailed.\n");
1174       } else {
1175         pprintf_noformat(p, "%s\n", movesToString(g, 0));
1176       } /* Do smoves */
1177     }
1178   }
1179   if (!wconnected)
1180     player_remove(wp);
1181   if (g != -1)
1182     game_remove(g);
1183 }
1184
1185 /* Tidied up a bit but still messy */
1186
1187 int com_mailstored(int p, param_list param)
1188 {
1189   stored_mail_moves(p, 1, param);
1190   return COM_OK;
1191 }
1192
1193 int com_smoves(int p, param_list param)
1194 {
1195   stored_mail_moves(p, 0, param);
1196   return COM_OK;
1197 }
1198
1199 int com_sposition(int p, param_list param)
1200 {
1201   struct player *pp = &player_globals.parray[p];
1202   int wp, wconnected, bp, bconnected, confused = 0;
1203   int g;
1204
1205   if (!FindPlayer(p, param[0].val.word, &wp, &wconnected))
1206     return (COM_OK);
1207   if (!FindPlayer(p, param[1].val.word, &bp, &bconnected)) {
1208     if (!wconnected)
1209       player_remove(wp);
1210     return (COM_OK);
1211   }
1212
1213   g = game_new();
1214   if (game_read(g, wp, bp) < 0) {       /* if no game white-black, */
1215     if (game_read(g, bp, wp) < 0) {     /* look for black-white */
1216       confused = 1;
1217       pprintf(p, "There is no stored game %s vs. %s\n", player_globals.parray[wp].name, player_globals.parray[bp].name);
1218     } else {
1219       int tmp;
1220       tmp = wp;
1221       wp = bp;
1222       bp = tmp;
1223       tmp = wconnected;
1224       wconnected = bconnected;
1225       bconnected = tmp;
1226     }
1227   }
1228   if (!confused) {
1229     if ((wp != p) && (bp != p) && (game_globals.garray[g].private) && (pp->adminLevel < ADMIN_ADMIN)) {
1230       pprintf(p, "Sorry, that is a private game.\n");
1231     } else {
1232       game_globals.garray[g].white = wp;
1233       game_globals.garray[g].black = bp;
1234       game_globals.garray[g].startTime = tenth_secs();
1235       game_globals.garray[g].lastMoveTime = game_globals.garray[g].startTime;
1236       game_globals.garray[g].lastDecTime = game_globals.garray[g].startTime;
1237       pprintf(p, "Position of stored game %s vs. %s\n", player_globals.parray[wp].name, player_globals.parray[bp].name);
1238       send_board_to(g, p);
1239     }
1240   }
1241   game_remove(g);
1242   if (!wconnected)
1243     player_remove(wp);
1244   if (!bconnected)
1245     player_remove(bp);
1246   return COM_OK;
1247 }
1248
1249 int com_forward(int p, param_list param)
1250 {
1251   struct player *pp = &player_globals.parray[p];
1252   int nHalfMoves = 1;
1253   int g, i;
1254   int p1;
1255   unsigned now;
1256
1257   if (!((pp->game >=0) && ((game_globals.garray[pp->game].status == GAME_EXAMINE) || (game_globals.garray[pp->game].status == GAME_SETUP)))) {
1258     pprintf(p, "You are not examining any games.\n");
1259     return COM_OK;
1260   }
1261   if (game_globals.garray[pp->game].status == GAME_SETUP) {
1262     pprintf (p,"You can't move forward yet, the position is still being set up.\n");
1263     return COM_OK;
1264   }
1265   g = pp->game;
1266   if (!strcmp(game_globals.garray[g].white_name, game_globals.garray[g].black_name)) {
1267     pprintf(p, "You cannot go forward; no moves are stored.\n");
1268     return COM_OK;
1269   }
1270   if (param[0].type == TYPE_INT) {
1271     nHalfMoves = param[0].val.integer;
1272   }
1273   if (game_globals.garray[g].numHalfMoves > game_globals.garray[g].revertHalfMove) {
1274     pprintf(p, "Game %u: No more moves.\n", g);
1275     return COM_OK;
1276   }
1277   if (game_globals.garray[g].numHalfMoves < game_globals.garray[g].totalHalfMoves) {
1278     for (p1 = 0; p1 < player_globals.p_num; p1++) {
1279       if (player_globals.parray[p1].status != PLAYER_PROMPT)
1280         continue;
1281       if (player_is_observe(p1, g) || player_globals.parray[p1].game == g) {
1282         pprintf(p1, "Game %u: %s goes forward %d move%s.\n",
1283                 g,
1284                 pp->name, nHalfMoves, (nHalfMoves == 1) ? "" : "s");
1285       }
1286     }
1287   }
1288   for (i = 0; i < nHalfMoves; i++) {
1289     if (game_globals.garray[g].numHalfMoves < game_globals.garray[g].totalHalfMoves) {
1290       execute_move(&game_globals.garray[g].game_state, &game_globals.garray[g].moveList[game_globals.garray[g].numHalfMoves], 1);
1291       if (game_globals.garray[g].numHalfMoves + 1 > game_globals.garray[g].examMoveListSize) {
1292         game_globals.garray[g].examMoveListSize += 20;  /* Allocate 20 moves at a
1293                                                    time */
1294         game_globals.garray[g].examMoveList = (struct move_t *) realloc(game_globals.garray[g].examMoveList, sizeof(struct move_t) * game_globals.garray[g].examMoveListSize);
1295       }
1296       game_globals.garray[g].examMoveList[game_globals.garray[g].numHalfMoves] = game_globals.garray[g].moveList[game_globals.garray[g].numHalfMoves];
1297       game_globals.garray[g].revertHalfMove++;
1298       game_globals.garray[g].numHalfMoves++;
1299     } else {
1300       for (p1 = 0; p1 < player_globals.p_num; p1++) {
1301         if (player_globals.parray[p1].status != PLAYER_PROMPT)
1302           continue;
1303         if (player_is_observe(p1, g) || player_globals.parray[p1].game == g) {
1304           pprintf(p1, "Game %u: End of game.\n", g);
1305         }
1306       }
1307       break;
1308     }
1309   }
1310   /* roll back time */
1311   if (game_globals.garray[g].game_state.onMove == WHITE) {
1312     game_globals.garray[g].wTime += (game_globals.garray[g].lastDecTime - game_globals.garray[g].lastMoveTime);
1313   } else {
1314     game_globals.garray[g].bTime += (game_globals.garray[g].lastDecTime - game_globals.garray[g].lastMoveTime);
1315   }
1316   now = tenth_secs();
1317   if (game_globals.garray[g].numHalfMoves == 0)
1318     game_globals.garray[g].timeOfStart = now;
1319   game_globals.garray[g].lastMoveTime = now;
1320   game_globals.garray[g].lastDecTime = now;
1321   send_boards(g);
1322
1323   if (game_globals.garray[g].revertHalfMove == game_globals.garray[g].totalHalfMoves) {
1324           for (p1 = 0; p1 < player_globals.p_num; p1++) {
1325                   if (player_globals.parray[p1].status != PLAYER_PROMPT) continue;
1326                   if (player_is_observe(p1, g) || player_globals.parray[p1].game == g) {
1327                           pprintf(p1, "Game %u: %s %s\n", g, EndString(g,0), EndSym(g));
1328                   }
1329           }
1330   }
1331
1332   return COM_OK;
1333 }
1334
1335 int com_backward(int p, param_list param)
1336 {
1337   struct player *pp = &player_globals.parray[p];
1338   int nHalfMoves = 1;
1339   int g, i;
1340   int p1;
1341   unsigned now;
1342
1343   if (!((pp->game >=0) && ((game_globals.garray[pp->game].status == GAME_EXAMINE) || (game_globals.garray[pp->game].status == GAME_SETUP)))) {
1344     pprintf(p, "You are not examining any games.\n");
1345     return COM_OK;
1346   }
1347   if (game_globals.garray[pp->game].status == GAME_SETUP) {
1348     pprintf (p,"You can't move backward yet, the postion is still being set up.\n");
1349     return COM_OK;
1350   }
1351
1352   g = pp->game;
1353   if (param[0].type == TYPE_INT) {
1354     nHalfMoves = param[0].val.integer;
1355   }
1356   if (game_globals.garray[g].numHalfMoves != 0) {
1357     for (p1 = 0; p1 < player_globals.p_num; p1++) {
1358       if (player_globals.parray[p1].status != PLAYER_PROMPT)
1359         continue;
1360       if (player_is_observe(p1, g) || player_globals.parray[p1].game == g) {
1361         pprintf(p1, "Game %u: %s backs up %d move%s.\n",
1362                 g,
1363                 pp->name, nHalfMoves, (nHalfMoves == 1) ? "" : "s");
1364       }
1365     }
1366   }
1367   for (i = 0; i < nHalfMoves; i++) {
1368     if (backup_move(g, REL_EXAMINE) != MOVE_OK) {
1369       for (p1 = 0; p1 < player_globals.p_num; p1++) {
1370         if (player_globals.parray[p1].status != PLAYER_PROMPT)
1371           continue;
1372         if (player_is_observe(p1, g) || player_globals.parray[p1].game == g) {
1373           pprintf(p1, "Game %u: Beginning of game.\n", g);
1374         }
1375       }
1376
1377       break;
1378     }
1379   }
1380   if (game_globals.garray[g].numHalfMoves < game_globals.garray[g].revertHalfMove) {
1381     game_globals.garray[g].revertHalfMove = game_globals.garray[g].numHalfMoves;
1382   }
1383   /* roll back time */
1384   if (game_globals.garray[g].game_state.onMove == WHITE) {
1385     game_globals.garray[g].wTime += (game_globals.garray[g].lastDecTime - game_globals.garray[g].lastMoveTime);
1386   } else {
1387     game_globals.garray[g].bTime += (game_globals.garray[g].lastDecTime - game_globals.garray[g].lastMoveTime);
1388   }
1389   now = tenth_secs();
1390   if (game_globals.garray[g].numHalfMoves == 0)
1391     game_globals.garray[g].timeOfStart = now;
1392   game_globals.garray[g].lastMoveTime = now;
1393   game_globals.garray[g].lastDecTime = now;
1394   send_boards(g);
1395   return COM_OK;
1396 }
1397
1398 int com_revert(int p, param_list param)
1399 {
1400   struct player *pp = &player_globals.parray[p];
1401   int nHalfMoves = 1;
1402   int g, i;
1403   int p1;
1404   unsigned now;
1405
1406   if (!((pp->game >=0) && ((game_globals.garray[pp->game].status == GAME_EXAMINE)|| (game_globals.garray[pp->game].status == GAME_SETUP)))) {
1407     pprintf(p, "You are not examining any games.\n");
1408     return COM_OK;
1409   }
1410   if (game_globals.garray[pp->game].status == GAME_SETUP) {
1411     pprintf (p,"You can't move revert yet, the position is still being set up.\n");
1412     return COM_OK;
1413   }
1414   g = pp->game;
1415   nHalfMoves = game_globals.garray[g].numHalfMoves - game_globals.garray[g].revertHalfMove;
1416   if (nHalfMoves == 0) {
1417     pprintf(p, "Game %u: Already at mainline.\n", g);
1418     return COM_OK;
1419   }
1420   if (nHalfMoves < 0) {         /* eek - should NEVER happen! */
1421     d_printf( "OUCH! in com_revert: nHalfMoves < 0\n");
1422     return COM_OK;
1423   }
1424   for (p1 = 0; p1 < player_globals.p_num; p1++) {
1425     if (player_globals.parray[p1].status != PLAYER_PROMPT)
1426       continue;
1427     if (player_is_observe(p1, g) || player_globals.parray[p1].game == g) {
1428       pprintf(p1, "Game %u: %s reverts to mainline.\n", 
1429               g, pp->name);
1430     }
1431   }
1432   for (i = 0; i < nHalfMoves; i++) {
1433     backup_move(g, REL_EXAMINE);/* should never return error */
1434   }
1435   /* roll back time */
1436   if (game_globals.garray[g].game_state.onMove == WHITE) {
1437     game_globals.garray[g].wTime += (game_globals.garray[g].lastDecTime - game_globals.garray[g].lastMoveTime);
1438   } else {
1439     game_globals.garray[g].bTime += (game_globals.garray[g].lastDecTime - game_globals.garray[g].lastMoveTime);
1440   }
1441   now = tenth_secs();
1442   if (game_globals.garray[g].numHalfMoves == 0)
1443     game_globals.garray[g].timeOfStart = now;
1444   game_globals.garray[g].lastMoveTime = now;
1445   game_globals.garray[g].lastDecTime = now;
1446   send_boards(g);
1447   return COM_OK;
1448 }
1449
1450 int com_history(int p, param_list param)
1451 {
1452   int p1, connected;
1453   char fname[MAX_FILENAME_SIZE];
1454
1455   if (param[0].type == TYPE_WORD) {
1456     if (!FindPlayer(p, param[0].val.word, &p1, &connected))
1457       return COM_OK;
1458   } else {
1459       p1 = p;
1460       connected = 1;
1461   }
1462
1463   sprintf(fname, "%s/player_data/%c/%s.%s", STATS_DIR, player_globals.parray[p1].login[0],
1464           player_globals.parray[p1].login, STATS_GAMES);
1465   pgames(p, p1, fname);
1466   if (!connected)
1467     player_remove(p1);
1468   return COM_OK;
1469 }
1470
1471 int com_journal(int p, param_list param)
1472 {
1473   struct player *pp = &player_globals.parray[p];
1474   int p1, connected;
1475   char fname[MAX_FILENAME_SIZE];
1476
1477     if (param[0].type == TYPE_WORD) {
1478     if (!FindPlayer(p, param[0].val.word, &p1, &connected))
1479       return COM_OK;
1480   } else {
1481       p1 = p;
1482       connected = 1;
1483   }
1484
1485   if (!CheckPFlag(p1, PFLAG_REG)) {
1486     pprintf (p,"Only registered players may keep a journal.\n");
1487     if (!connected)
1488       player_remove(p1);
1489     return COM_OK;
1490   }
1491   if (CheckPFlag(p1, PFLAG_JPRIVATE) && (p != p1)
1492       && (pp->adminLevel < ADMIN_ADMIN)) {
1493     pprintf (p,"Sorry, this journal is private.\n");
1494     if (!connected)
1495       player_remove(p1);
1496     return COM_OK;
1497   }
1498   sprintf(fname, "%s/player_data/%c/%s.%s", STATS_DIR, player_globals.parray[p1].login[0],
1499           player_globals.parray[p1].login, STATS_JOURNAL);
1500   pjournal(p, p1, fname);
1501   if (!connected)
1502     player_remove(p1);
1503   return COM_OK;
1504 }
1505
1506 /* Remove a journal item */
1507
1508 int com_jkill(int p, param_list param)
1509 {
1510   struct player *pp = &player_globals.parray[p];
1511   FILE* Journal;
1512   char* kill = param[0].val.word;
1513   char fname[MAX_FILENAME_SIZE];
1514   char fname_new[MAX_FILENAME_SIZE];
1515   int empty;
1516
1517   if (!CheckPFlag(p, PFLAG_REG)) {
1518      pprintf (p,"Only registered players may keep a journal.\n");
1519      return COM_OK;
1520   }
1521
1522   if ((strlen(kill) != 1) || (!(isalpha(kill[0])))) {
1523     pprintf (p,"Journal entries are referenced by single letters.\n");
1524     return COM_OK;
1525   }
1526
1527   if (((kill[0] - 'a' + 1) > MAX_JOURNAL) && (pp->adminLevel < ADMIN_ADMIN)
1528   && (!titled_player(p,pp->login))) {
1529     pprintf (p,"Your maximum journal entry is %c\n",toupper ((char)(MAX_JOURNAL
1530 + 'A' - 1)));
1531     return COM_OK;
1532   }
1533
1534   sprintf(fname, "%s/player_data/%c/%s.journal",
1535       STATS_DIR, pp->login[0],pp->login);
1536   sprintf (fname_new,"%s.w",fname);
1537
1538   Journal = fopen_s(fname, "r");
1539   if (Journal == NULL) {
1540      pprintf(p, "You don't have a journal.\n");
1541      return COM_OK;
1542      }
1543
1544   kill[0] = toupper(kill[0]);
1545  
1546   if (removejournalitem(p, kill[0], Journal,fname_new,&empty)) {
1547     fclose (Journal);
1548     rename (fname_new,fname);
1549     if (empty)
1550        unlink (fname);
1551     sprintf(fname, "%s/%c/%s.%c",
1552          JOURNAL_DIR, pp->login[0],pp->login,tolower(kill[0]));
1553     unlink(fname);
1554     pprintf (p,"Journal entry %c deleted.\n",kill[0]);
1555   } else { 
1556     pprintf (p,"You have no journal entry %c.\n",kill[0]);
1557     fclose (Journal);
1558     unlink (fname_new);
1559   }
1560   return COM_OK;
1561 }
1562
1563 static void jsave_journalentry(int p,char save_spot,int p1,char from_spot,char* to_file)
1564 {
1565   struct player *pp = &player_globals.parray[p];
1566   FILE *Game;
1567   char fname[MAX_FILENAME_SIZE], fname2[MAX_FILENAME_SIZE];
1568   char* name_from = player_globals.parray[p1].login;
1569   char* name_to = pp->login;
1570   struct journal* j;
1571
1572   sprintf(fname, "%s/%c/%s.%c", JOURNAL_DIR, name_from[0],name_from,from_spot);
1573   Game = fopen_s(fname, "r");
1574   if (Game == NULL) {
1575      pprintf(p, "Journal entry %c not available for %s.\n", toupper(from_spot), player_globals.parray[p1].name);
1576      return;
1577      }
1578   fclose (Game);
1579   
1580   sprintf(fname2, "%s/%c/%s.%c", JOURNAL_DIR, name_to[0],name_to,save_spot);
1581
1582   if (file_copy(fname, fname2) != 0) {
1583     pprintf (p,"Copy in jsave_journalentry failed!\n");
1584     pprintf (p,"Please report this to an admin.\n");
1585     d_printf("CHESSD: Copy failed in jsave_journalentry\n");
1586     return;
1587   }
1588
1589  sprintf(fname, "%s/player_data/%c/%s.%s", STATS_DIR, name_from[0],
1590           name_from, STATS_JOURNAL);
1591
1592  j = (struct journal*) malloc (sizeof(struct journal));
1593  if (!journal_get_info(p,tolower(from_spot),j,fname)) {
1594    free (j);
1595    return;
1596  }
1597
1598  j->slot = toupper(save_spot);
1599  addjournalitem(p, j, to_file);
1600
1601  pprintf(p,"Journal entry %s %c saved in slot %c in journal.\n",player_globals.parray[p1].name, toupper(from_spot), toupper(save_spot));
1602  free (j);
1603 }
1604
1605 static void jsave_history(int p,char save_spot,int p1,int from,char* to_file)
1606 {
1607         struct player *pp = &player_globals.parray[p];
1608         char Eco[100];
1609         char End[100];
1610         char jfname[MAX_FILENAME_SIZE];
1611         char* HistoryFname = FindHistory2(p, p1, from, Eco, End);
1612         /* End symbol Mat Res, etc is the only thing we can't find out */
1613         char command[MAX_FILENAME_SIZE*2+3];
1614         char* name_to = pp->login;
1615         FILE *Game;
1616         int g;
1617         struct journal* j;
1618         
1619         if (HistoryFname == NULL) {
1620                 return;
1621         }
1622         Game = fopen_s(HistoryFname, "r");
1623         if (Game == NULL) {
1624                 pprintf(p, "History game %d not available for %s.\n", 
1625                         from, player_globals.parray[p1].name);
1626                 return;
1627         } 
1628
1629         sprintf(jfname, "%s/%c/%s.%c", JOURNAL_DIR, name_to[0],name_to,save_spot);
1630         unlink(jfname); /* necessary if cp is hard aliased to cp -i */
1631         sprintf(command, "cp %s %s",HistoryFname,jfname);
1632         if (file_copy(HistoryFname, jfname) != 0) {
1633                 pprintf (p,"Copy in jsave_history failed!\n");
1634                 pprintf (p,"Please report this to an admin.\n");
1635                 d_printf("CHESSD: Copy failed in jsave_journalentry\n");
1636                 return;
1637         }
1638         g = game_new(); /* Open a dummy game */
1639         
1640         if (ReadGameAttrs(Game, g) < 0) {
1641                 pprintf (p,"Gamefile is corrupt. Please tell an admin.\n");
1642                 game_remove(g);
1643                 fclose (Game);
1644                 return;
1645         }
1646         fclose (Game);
1647         
1648         j = (struct journal*) malloc (sizeof(struct journal));
1649         
1650         if (game_globals.garray[g].private) {
1651                 j->type[0] = 'p';
1652         } else {
1653                 j->type[0] = ' ';
1654         }
1655         if (game_globals.garray[g].type == TYPE_BLITZ) {
1656                 j->type[1] = 'b';
1657         } else if (game_globals.garray[g].type == TYPE_WILD) {
1658                 j->type[1] = 'w';
1659         } else if (game_globals.garray[g].type == TYPE_STAND) {
1660                 j->type[1] = 's';
1661         } else if (game_globals.garray[g].type == TYPE_LIGHT) {
1662                 j->type[1] = 'l';
1663         } else if (game_globals.garray[g].type == TYPE_BUGHOUSE) {
1664                 j->type[1] = 'B';
1665         } else {
1666                 if (game_globals.garray[g].type == TYPE_NONSTANDARD)
1667                         j->type[1] = 'n';
1668                 else
1669                         j->type[1] = 'u';
1670         }
1671         if (game_globals.garray[g].rated) {
1672                 j->type[2] = 'r';
1673         } else {
1674                 j->type[2] = 'u';
1675         }
1676         j->type[3] = '\0';
1677         
1678         j->slot = toupper(save_spot);
1679         strcpy (j->WhiteName, game_globals.garray[g].white_name);
1680         j->WhiteRating = game_globals.garray[g].white_rating;
1681         strcpy (j->BlackName, game_globals.garray[g].black_name);
1682         j->BlackRating = game_globals.garray[g].black_rating;
1683         j->t = game_globals.garray[g].wInitTime;
1684         j->i = game_globals.garray[g].wIncrement;
1685         strcpy (j->eco, Eco);
1686         strcpy (j->ending, End);
1687         strcpy (j->result, EndSym(g));
1688         
1689         addjournalitem(p, j, to_file);
1690         game_remove(g);
1691         pprintf(p,"Game %s %d saved in slot %c in journal.\n",
1692                 player_globals.parray[p1].name, from, toupper(save_spot));
1693         free(j);
1694 }
1695
1696 int com_jsave(int p, param_list param)
1697 {
1698   struct player *pp = &player_globals.parray[p];
1699   int p1, p1conn;
1700   char* to = param[0].val.word;
1701   char* from;
1702   char fname[MAX_FILENAME_SIZE];
1703
1704   if (!CheckPFlag(p, PFLAG_REG)) {
1705      pprintf (p,"Only registered players may keep a journal.\n");
1706      return COM_OK;
1707   }
1708
1709   if ((strlen(to) != 1) || (!(isalpha(to[0])))) {
1710     pprintf (p,"Journal entries are referenced by single letters.\n");
1711     return COM_OK;
1712   }
1713
1714   if (((to[0] - 'a' + 1) > MAX_JOURNAL) && (pp->adminLevel < ADMIN_ADMIN) && (!titled_player(p,pp->login))) {
1715     pprintf (p,"Your maximum journal entry is %c\n",toupper ((char)(MAX_JOURNAL + 'A' - 1)));
1716     return COM_OK;
1717   }
1718
1719   if (!FindPlayer(p, param[1].val.word, &p1, &p1conn))
1720     return COM_OK;
1721
1722   if (param[2].type == TYPE_INT) {
1723
1724   /* grab from a history */
1725     sprintf (fname,"%s/player_data/%c/%s.%s",STATS_DIR,pp->login[0],pp->login, STATS_JOURNAL);
1726     jsave_history(p, to[0], p1, param[2].val.integer,fname); 
1727
1728   } else {
1729
1730   from = param[2].val.word;
1731
1732   if ((strlen(from) != 1) || (!(isalpha(from[0])))) {
1733     pprintf (p,"Journal entries are referenced by single letters.\n");
1734     if (!p1conn)
1735       player_remove(p1);
1736     return COM_OK;
1737     }
1738
1739   if (CheckPFlag(p1, PFLAG_JPRIVATE)
1740       && (pp->adminLevel < ADMIN_ADMIN) && (p != p1)) {
1741     pprintf (p,"Sorry, the journal from which you are trying to fetch is private.\n");
1742
1743     if (!p1conn)
1744        player_remove(p1);
1745     return COM_OK;
1746     }
1747
1748   if (((to[0] - 'a' + 1) > MAX_JOURNAL) && (player_globals.parray[p1].adminLevel < ADMIN_ADMIN) && (!titled_player(p,player_globals.parray[p1].login))) {
1749     pprintf (p,"%s's maximum journal entry is %c\n",player_globals.parray[p1].name,toupper((char)(MAX_JOURNAL + 'A' - 1)));
1750     if (!p1conn)
1751        player_remove(p1);
1752     return COM_OK;
1753   }
1754   if (( p == p1) && (to[0] == from [0])) {
1755     pprintf (p,"Source and destination entries are the same.\n");
1756     return COM_OK;
1757   }
1758   
1759   /* grab from a journal */
1760
1761   sprintf(fname, "%s/player_data/%c/%s.%s", STATS_DIR, pp->login[0],
1762           pp->login, STATS_JOURNAL);
1763   jsave_journalentry(p,to[0],p1, from[0], fname);
1764
1765   }
1766   if (!p1conn)
1767      player_remove(p1);
1768   return COM_OK;
1769 }
1770
1771 int com_refresh(int p, param_list param)
1772 {
1773   struct player *pp = &player_globals.parray[p];
1774   int g, p1;
1775
1776   if (param[0].type == TYPE_NULL) {
1777     if (pp->game >= 0) {
1778       send_board_to(pp->game, p);
1779     } else {                    /* Do observing in here */
1780       if (pp->num_observe) {
1781         for (g = 0; g < pp->num_observe; g++) {
1782           send_board_to(pp->observe_list[g], p);
1783         }
1784       } else {
1785         pprintf(p, "You are neither playing, observing nor examining a game.\n");
1786         return COM_OK;
1787       }
1788     }
1789   } else {
1790     g = GameNumFromParam (p, &p1, &param[0]);
1791     if (g < 0)
1792       return COM_OK;
1793     if ((g >= game_globals.g_num) || ((game_globals.garray[g].status != GAME_ACTIVE)
1794                         && ((game_globals.garray[g].status != GAME_EXAMINE)
1795                         || (game_globals.garray[g].status != GAME_SETUP)))) {
1796       pprintf(p, "No such game.\n");
1797     } else {
1798
1799       int link = game_globals.garray[g].link;
1800
1801       if ((game_globals.garray[g].private && pp->adminLevel==ADMIN_USER) &&
1802         (game_globals.garray[g].white != p) && (game_globals.garray[g].white != p1)) {
1803         if (link != -1) {
1804           if ((game_globals.garray[link].white != p) && (game_globals.garray[link].black != p)) {
1805             pprintf (p, "Sorry, game %d is a private game.\n", g+1);
1806             return COM_OK;
1807           }
1808         }
1809       }
1810
1811       if (game_globals.garray[g].private)
1812         pprintf(p, "Refreshing static game %d\n", g+1);
1813       send_board_to(g, p);
1814     }
1815   }
1816   return COM_OK;
1817 }
1818
1819 int com_prefresh(int p, param_list param)
1820 {
1821   struct player *pp = &player_globals.parray[p];
1822   int retval, part = pp->partner;
1823
1824   if (part < 0) {
1825     pprintf(p, "You do not have a partner.\n");
1826     return COM_OK;
1827   }
1828   retval = pcommand (p, "refresh %s", player_globals.parray[part].name);
1829   if (retval == COM_OK)
1830     return COM_OK_NOPROMPT;
1831   else
1832     return retval;
1833 }