Fix bug in setting up w3 and w4 games
[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;
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   fpHist = fopen_s(fileName, "r");
780   if (fpHist == NULL) {
781     pprintf(p, "No games in history for %s.\n", player_globals.parray[p1].name);
782     return(NULL);
783   }
784   do {
785     fscanf(fpHist, "%d %*c %*d %*c %*d %*s %s %*d %*d %*d %*d %*s %*s %ld",
786            &index, typestr, &when);
787   } while (!feof(fpHist) && index != game);
788
789   if (feof(fpHist)) {
790     pprintf(p, "There is no history game %d for %s.\n", game, player_globals.parray[p1].name);
791     fclose(fpHist);
792     return(NULL);
793   }
794   fclose(fpHist);
795
796   if (typestr[0] != 'p')
797     *type = typestr[0];
798   else
799     *type = typestr[1];
800
801   sprintf(fileName, "%s/%ld/%ld", HISTORY_DIR, when % 100, when);
802   return(fileName);
803 }
804
805 /* I want to know how game ended/ECO code */
806
807 static char *FindHistory2(int p, int p1,int game, char* Eco,char* End)
808 {
809   FILE *fpHist;
810   static char fileName[MAX_FILENAME_SIZE];
811   int index;
812   long when;
813   
814   sprintf(fileName, "%s/player_data/%c/%s.%s", STATS_DIR,
815           player_globals.parray[p1].login[0], player_globals.parray[p1].login, STATS_GAMES);
816   fpHist = fopen_s(fileName, "r");
817   if (fpHist == NULL) {
818     pprintf(p, "No games in history for %s.\n", player_globals.parray[p1].name);
819     return(NULL);
820   }
821   do {
822     fscanf(fpHist, "%d %*c %*d %*c %*d %*s %*s %*d %*d %*d %*d %s %s %ld",
823            &index, Eco, End, &when);
824   } while (!feof(fpHist) && index != game);
825
826   if (feof(fpHist)) {
827     pprintf(p, "There is no history game %d for %s.\n", game, player_globals.parray[p1].name);
828     fclose(fpHist);
829     return(NULL);
830   }
831   fclose(fpHist);
832
833   sprintf(fileName, "%s/%ld/%ld", HISTORY_DIR, when % 100, when);
834   return(fileName);
835 }
836
837 int com_wname(int p, param_list param)
838 {
839   struct player *pp = &player_globals.parray[p];
840
841   int g = pp->game;
842
843   if ((g < 0) ||
844      ((g >= 0) &&
845         !((game_globals.garray[g].status != GAME_EXAMINE) || (game_globals.garray[g].status != GAME_SETUP)))) {
846     pprintf (p, "You are not examining or setting up a game.\n");
847     return COM_OK;
848   }
849
850   if (param[0].type == TYPE_NULL)
851     strcpy (game_globals.garray[g].white_name,pp->name);
852   else {
853
854     if (strlen (param[0].val.word) > MAX_LOGIN_NAME - 1) {
855       pprintf (p,"The maximum length of a name is %d characters.\n",MAX_LOGIN_NAME - 1);
856       return COM_OK;
857     } else
858       strcpy (game_globals.garray[g].white_name,param[0].val.word);
859   }
860
861   send_boards(g);
862   return COM_OK;
863 }
864
865 int com_bname(int p, param_list param)
866 {
867   struct player *pp = &player_globals.parray[p];
868
869   int g = pp->game;
870
871   if ((g < 0) ||
872      ((g >= 0) &&
873         !((game_globals.garray[g].status != GAME_EXAMINE) || (game_globals.garray[g].status != GAME_SETUP)))) {
874     pprintf (p, "You are not examining or setting up a game.\n");
875     return COM_OK;
876   }
877
878   if (param[0].type == TYPE_NULL)
879     strcpy (game_globals.garray[g].black_name,pp->name);
880   else {
881
882     if (strlen (param[0].val.word) > MAX_LOGIN_NAME - 1) {
883       pprintf (p,"The maximum length of a name is %d characters.\n",MAX_LOGIN_NAME - 1);
884       return COM_OK;
885     } else
886       strcpy (game_globals.garray[g].black_name,param[0].val.word);
887   }
888
889   send_boards(g);
890   return COM_OK;
891 }
892
893 static void ExamineHistory(int p, int p1, int game)
894 {
895         char *fileName;
896         char type;
897
898         fileName = FindHistory(p, p1, game,&type);
899         if (fileName) {
900                 FILE *fpGame = fopen_p("%s", "r", fileName);
901                 if (fpGame == NULL) {
902                         pprintf(p, "History game %d not available for %s.\n", 
903                                 game, player_globals.parray[p1].name);
904                 } else {
905                         ExamineStored(fpGame, p,type);
906                         fclose(fpGame);
907                 }
908         }
909 }
910
911 static void ExamineJournal(int p,int p1,char slot)
912 {
913         struct player *pp = &player_globals.parray[p];
914         char* name_from = player_globals.parray[p1].login;
915         char type;
916         FILE *fpGame;
917
918         if (CheckPFlag(p1, PFLAG_JPRIVATE) && (p != p1)
919             && (pp->adminLevel < ADMIN_ADMIN)) {
920                 pprintf (p,"Sorry, this journal is private.\n");
921                 return;
922         }
923
924         if ((slot - 'a' + 1) > MAX_JOURNAL && 
925             !check_admin(p1, ADMIN_ADMIN) && 
926             !titled_player(p,player_globals.parray[p1].login)) {
927                 pprintf(p,"%s's maximum journal entry is %c\n",
928                         player_globals.parray[p1].name,
929                         toupper((char)(MAX_JOURNAL + 'A' - 1)));
930                 return;
931         }
932
933         fpGame = fopen_p("%s/%c/%s.%c", "r", JOURNAL_DIR, name_from[0],name_from,slot);
934         if (fpGame == NULL) {
935                 pprintf(p, "Journal entry %c is not available for %s.\n", toupper (slot),
936                         player_globals.parray[p1].name);
937         } else {
938                 char *fname;
939                 asprintf(&fname, "%s/player_data/%c/%s.journal",
940                          STATS_DIR, name_from[0],name_from);
941                 slot = toupper(slot);
942
943                 if ((type = get_journalgame_type (p,fname,slot)) == '\0') {
944                         pprintf(p, "Journal entry %c is not available for %s or is corrupt.\n",
945                                 slot, player_globals.parray[p1].name);
946                 } else {
947                         ExamineStored(fpGame, p,type);
948                 }
949
950                 free(fname);
951                 fclose (fpGame);
952         }
953 }
954
955 int com_examine(int p, param_list param)
956 {
957   struct player *pp = &player_globals.parray[p];
958   int p1, p2 = p, p1conn, p2conn = 1;
959   char* wincstring;
960   char fname[MAX_FILENAME_SIZE];
961
962   if (pp->game >=0) {
963      if ((game_globals.garray[pp->game].status == GAME_EXAMINE) || 
964         (game_globals.garray[pp->game].status == GAME_SETUP))
965        pprintf(p, "You are already examining a game.\n");
966      else
967        pprintf(p, "You are playing a game.\n");
968   } else if (param[0].type == TYPE_NULL) {
969     ExamineScratch(p, param, 0);
970   } else if (param[0].type != TYPE_NULL) {
971     if ((param[1].type == TYPE_NULL) && (!strcmp(param[0].val.word,"setup"))) {
972         ExamineScratch(p, param, 1);
973         return COM_OK;
974     } else if (param[1].type == TYPE_WORD) {
975       sprintf(fname, "%s/%s/%s", BOARD_DIR, param[0].val.word, param[1].val.word);
976       if (file_exists(fname)) {
977         ExamineScratch(p, param, 0);
978         return COM_OK;
979       }
980     }
981     if (!FindPlayer(p, param[0].val.word, &p1, &p1conn))
982       return COM_OK;
983
984     if (param[1].type == TYPE_INT)
985       ExamineHistory(p, p1, param[1].val.integer); 
986     else {
987       if (param[1].type == TYPE_WORD) {
988
989         /* Lets check the journal */
990         wincstring = param[1].val.word;
991         if ((strlen(wincstring) == 1) && (isalpha(wincstring[0]))) {
992           ExamineJournal(p,p1,wincstring[0]);
993           if (!p1conn)
994             player_remove(p1);
995           return COM_OK;
996         } else {
997           if (!FindPlayer(p, param[1].val.word, &p2, &p2conn)) {
998             if (!p1conn)
999               player_remove(p1);
1000             return COM_OK;
1001           }
1002         }
1003       } 
1004       ExamineAdjourned(p, p1, p2);
1005       if (!p2conn)
1006        player_remove(p2);
1007     }
1008     if (!p1conn)
1009      player_remove(p1);
1010   }
1011   return COM_OK;
1012 }
1013
1014 int com_stored(int p, param_list param)
1015 {
1016   int count = 0;
1017   DIR *dirp;
1018   struct dirent *dp;
1019
1020   int p1, connected;
1021   char dname[MAX_FILENAME_SIZE];
1022
1023   if (param[0].type == TYPE_WORD) {
1024     if (!FindPlayer(p, param[0].val.word, &p1, &connected))
1025       return COM_OK;
1026   } else {
1027       p1 = p;
1028       connected = 1;
1029   }
1030
1031   sprintf(dname, "%s/%c", ADJOURNED_DIR, player_globals.parray[p1].login[0]);
1032   dirp = opendir(dname);
1033   if (!dirp) {
1034     pprintf(p, "Player %s has no games stored.\n", player_globals.parray[p1].name);
1035     if (!connected)
1036       player_remove(p1);
1037     return COM_OK;
1038   }
1039   pprintf(p, "Stored games for %s:\n", player_globals.parray[p1].name);
1040   for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
1041     if (file_has_pname(dp->d_name, player_globals.parray[p1].login)) {
1042       pprintf(p, "   %s vs. %s\n", file_wplayer(dp->d_name), file_bplayer(dp->d_name));
1043       count++;
1044     }
1045   }
1046
1047   closedir(dirp);
1048   pprintf (p,"%d stored game%sfound.\n",count,(count == 1 ? " " : "s "));
1049   if (!connected)
1050     player_remove(p1);
1051   return COM_OK;
1052 }
1053
1054 static void stored_mail_moves(int p, int mail, param_list param)
1055 {
1056   struct player *pp = &player_globals.parray[p];
1057   int wp, wconnected, bp, bconnected, gotit = 0;
1058   int g = -1;
1059   char type; /* dummy */
1060   char* wincstring = NULL;
1061   char* name_from = NULL;
1062   char* fileName = NULL;
1063   char fileName2[MAX_FILENAME_SIZE];
1064   FILE* fpGame;
1065   
1066   if (param[2].type == TYPE_NULL) {
1067     if (!CheckPFlag(p, PFLAG_REG)) {
1068       pprintf (p,"Unregistered players must specify their e-mail address.\n");
1069       return COM_OK;
1070     }
1071   } else if(!safestring(param[2].val.string)) {
1072     pprintf (p,"Bad e-mail address.\n");
1073     return COM_OK;
1074   }
1075
1076   if (!FindPlayer(p, param[0].val.word, &wp, &wconnected))
1077     return;
1078
1079   if (param[1].type == TYPE_INT) { /* look for a game from history */
1080     fileName = FindHistory(p, wp, param[1].val.integer,&type);
1081     if (fileName != NULL) {
1082       fpGame = fopen_s(fileName, "r");
1083       if (fpGame == NULL) {
1084         pprintf(p, "History game %d not available for %s.\n", param[1].val.integer, player_globals.parray[wp].name);
1085       } else {
1086         g = game_new();
1087         if (ReadGameAttrs(fpGame, g) < 0)
1088           pprintf(p, "Gamefile is corrupt; please notify an admin.\n");
1089         else
1090           gotit = 1;
1091         fclose(fpGame);
1092       }
1093     }
1094   } else { /* Let's test for journal */
1095     name_from = param[0].val.word;
1096     wincstring = param[1].val.word;
1097     if ((strlen(wincstring) == 1) && (isalpha(wincstring[0]))) {
1098       if (CheckPFlag(wp, PFLAG_JPRIVATE)
1099           && (pp->adminLevel < ADMIN_ADMIN) && (p != wp)) {
1100         pprintf (p,"Sorry, the journal from which you are trying to fetch is private.\n");
1101       } else {
1102         if (((wincstring[0] - 'a' + 1) > MAX_JOURNAL) && (player_globals.parray[wp].adminLevel < ADMIN_ADMIN) && (!titled_player(p,player_globals.parray[wp].login))) {
1103           pprintf (p,"%s's maximum journal entry is %c\n",player_globals.parray[wp].name,toupper((char)(MAX_JOURNAL + 'A' - 1)));
1104         } else {
1105           sprintf(fileName2, "%s/%c/%s.%c", JOURNAL_DIR, name_from[0],name_from,wincstring[0]);
1106           fpGame = fopen_s(fileName2, "r");
1107           if (fpGame == NULL) {
1108             pprintf(p, "Journal entry %c is not available for %s.\n", toupper(wincstring[0]),
1109             player_globals.parray[wp].name);
1110           } else {
1111             g = game_new();
1112             if (ReadGameAttrs(fpGame, g) < 0)
1113               pprintf(p, "Journal entry is corrupt; please notify an admin.\n");
1114             else
1115               gotit = 1;
1116             fclose(fpGame);
1117           }
1118         }
1119       }
1120     } else {
1121   
1122        /* look for a stored game between the players */
1123
1124       if (FindPlayer(p, param[1].val.word, &bp, &bconnected)) {
1125         g = game_new();
1126         if (game_read(g, wp, bp) >= 0) {  /* look for a game white-black, */
1127           gotit = 1;
1128         } else if (game_read(g, bp, wp) >= 0) {   /* or black-white */
1129           gotit = 1;
1130         } else {
1131           pprintf(p, "There is no stored game %s vs. %s\n", player_globals.parray[wp].name, player_globals.parray[bp].name);
1132         }
1133         if (!bconnected)
1134           player_remove(bp);
1135       }
1136     }
1137   }
1138   if (gotit) {
1139     if (strcasecmp(pp->name, game_globals.garray[g].white_name) && strcasecmp(player_globals.parray[p]
1140 .name, game_globals.garray[g].black_name) && game_globals.garray[g].private && (pp->adminLevel < ADMIN_ADMIN)) {
1141       pprintf(p, "Sorry, that is a private game.\n");
1142     } else {
1143       if (mail == 1) { /*Do mailstored */
1144         char subj[81];
1145         if (param[1].type == TYPE_INT)
1146           sprintf(subj, "FICS history game: %s %d", player_globals.parray[wp].name, param[1].val.integer);
1147         else
1148           if ((strlen(wincstring) == 1) && (isalpha(wincstring[0]))) {
1149             sprintf(subj, "FICS journal game %s vs %s", game_globals.garray[g].white_name, game_globals.garray[g].black_name); 
1150           } else {
1151             sprintf(subj, "FICS adjourned game %s vs %s", game_globals.garray[g].white_name, game_globals.garray[g].black_name);
1152           }
1153         if (param[2].type == TYPE_NULL ? mail_string_to_user(p, subj,
1154                                 movesToString(g, CheckPFlag(p, PFLAG_PGN))) :
1155                         mail_string_to_address(param[2].val.string, subj,
1156                                 movesToString(g, CheckPFlag(p, PFLAG_PGN))))
1157           pprintf(p, "Moves NOT mailed, perhaps your address is incorrect.\n");
1158         else
1159           pprintf(p, "Moves mailed.\n");
1160       } else {
1161         pprintf_noformat(p, "%s\n", movesToString(g, 0));
1162       } /* Do smoves */
1163     }
1164   }
1165   if (!wconnected)
1166     player_remove(wp);
1167   if (g != -1)
1168     game_remove(g);
1169 }
1170
1171 /* Tidied up a bit but still messy */
1172
1173 int com_mailstored(int p, param_list param)
1174 {
1175   stored_mail_moves(p, 1, param);
1176   return COM_OK;
1177 }
1178
1179 int com_smoves(int p, param_list param)
1180 {
1181   stored_mail_moves(p, 0, param);
1182   return COM_OK;
1183 }
1184
1185 int com_sposition(int p, param_list param)
1186 {
1187   struct player *pp = &player_globals.parray[p];
1188   int wp, wconnected, bp, bconnected, confused = 0;
1189   int g;
1190
1191   if (!FindPlayer(p, param[0].val.word, &wp, &wconnected))
1192     return (COM_OK);
1193   if (!FindPlayer(p, param[1].val.word, &bp, &bconnected)) {
1194     if (!wconnected)
1195       player_remove(wp);
1196     return (COM_OK);
1197   }
1198
1199   g = game_new();
1200   if (game_read(g, wp, bp) < 0) {       /* if no game white-black, */
1201     if (game_read(g, bp, wp) < 0) {     /* look for black-white */
1202       confused = 1;
1203       pprintf(p, "There is no stored game %s vs. %s\n", player_globals.parray[wp].name, player_globals.parray[bp].name);
1204     } else {
1205       int tmp;
1206       tmp = wp;
1207       wp = bp;
1208       bp = tmp;
1209       tmp = wconnected;
1210       wconnected = bconnected;
1211       bconnected = tmp;
1212     }
1213   }
1214   if (!confused) {
1215     if ((wp != p) && (bp != p) && (game_globals.garray[g].private) && (pp->adminLevel < ADMIN_ADMIN)) {
1216       pprintf(p, "Sorry, that is a private game.\n");
1217     } else {
1218       game_globals.garray[g].white = wp;
1219       game_globals.garray[g].black = bp;
1220       game_globals.garray[g].startTime = tenth_secs();
1221       game_globals.garray[g].lastMoveTime = game_globals.garray[g].startTime;
1222       game_globals.garray[g].lastDecTime = game_globals.garray[g].startTime;
1223       pprintf(p, "Position of stored game %s vs. %s\n", player_globals.parray[wp].name, player_globals.parray[bp].name);
1224       send_board_to(g, p);
1225     }
1226   }
1227   game_remove(g);
1228   if (!wconnected)
1229     player_remove(wp);
1230   if (!bconnected)
1231     player_remove(bp);
1232   return COM_OK;
1233 }
1234
1235 int com_forward(int p, param_list param)
1236 {
1237   struct player *pp = &player_globals.parray[p];
1238   int nHalfMoves = 1;
1239   int g, i;
1240   int p1;
1241   unsigned now;
1242
1243   if (!((pp->game >=0) && ((game_globals.garray[pp->game].status == GAME_EXAMINE) || (game_globals.garray[pp->game].status == GAME_SETUP)))) {
1244     pprintf(p, "You are not examining any games.\n");
1245     return COM_OK;
1246   }
1247   if (game_globals.garray[pp->game].status == GAME_SETUP) {
1248     pprintf (p,"You can't move forward yet, the position is still being set up.\n");
1249     return COM_OK;
1250   }
1251   g = pp->game;
1252   if (!strcmp(game_globals.garray[g].white_name, game_globals.garray[g].black_name)) {
1253     pprintf(p, "You cannot go forward; no moves are stored.\n");
1254     return COM_OK;
1255   }
1256   if (param[0].type == TYPE_INT) {
1257     nHalfMoves = param[0].val.integer;
1258   }
1259   if (game_globals.garray[g].numHalfMoves > game_globals.garray[g].revertHalfMove) {
1260     pprintf(p, "Game %u: No more moves.\n", g);
1261     return COM_OK;
1262   }
1263   if (game_globals.garray[g].numHalfMoves < game_globals.garray[g].totalHalfMoves) {
1264     for (p1 = 0; p1 < player_globals.p_num; p1++) {
1265       if (player_globals.parray[p1].status != PLAYER_PROMPT)
1266         continue;
1267       if (player_is_observe(p1, g) || player_globals.parray[p1].game == g) {
1268         pprintf(p1, "Game %u: %s goes forward %d move%s.\n",
1269                 g,
1270                 pp->name, nHalfMoves, (nHalfMoves == 1) ? "" : "s");
1271       }
1272     }
1273   }
1274   for (i = 0; i < nHalfMoves; i++) {
1275     if (game_globals.garray[g].numHalfMoves < game_globals.garray[g].totalHalfMoves) {
1276       execute_move(&game_globals.garray[g].game_state, &game_globals.garray[g].moveList[game_globals.garray[g].numHalfMoves], 1);
1277       if (game_globals.garray[g].numHalfMoves + 1 > game_globals.garray[g].examMoveListSize) {
1278         game_globals.garray[g].examMoveListSize += 20;  /* Allocate 20 moves at a
1279                                                    time */
1280         game_globals.garray[g].examMoveList = (struct move_t *) realloc(game_globals.garray[g].examMoveList, sizeof(struct move_t) * game_globals.garray[g].examMoveListSize);
1281       }
1282       game_globals.garray[g].examMoveList[game_globals.garray[g].numHalfMoves] = game_globals.garray[g].moveList[game_globals.garray[g].numHalfMoves];
1283       game_globals.garray[g].revertHalfMove++;
1284       game_globals.garray[g].numHalfMoves++;
1285     } else {
1286       for (p1 = 0; p1 < player_globals.p_num; p1++) {
1287         if (player_globals.parray[p1].status != PLAYER_PROMPT)
1288           continue;
1289         if (player_is_observe(p1, g) || player_globals.parray[p1].game == g) {
1290           pprintf(p1, "Game %u: End of game.\n", g);
1291         }
1292       }
1293       break;
1294     }
1295   }
1296   /* roll back time */
1297   if (game_globals.garray[g].game_state.onMove == WHITE) {
1298     game_globals.garray[g].wTime += (game_globals.garray[g].lastDecTime - game_globals.garray[g].lastMoveTime);
1299   } else {
1300     game_globals.garray[g].bTime += (game_globals.garray[g].lastDecTime - game_globals.garray[g].lastMoveTime);
1301   }
1302   now = tenth_secs();
1303   if (game_globals.garray[g].numHalfMoves == 0)
1304     game_globals.garray[g].timeOfStart = now;
1305   game_globals.garray[g].lastMoveTime = now;
1306   game_globals.garray[g].lastDecTime = now;
1307   send_boards(g);
1308
1309   if (game_globals.garray[g].revertHalfMove == game_globals.garray[g].totalHalfMoves) {
1310           for (p1 = 0; p1 < player_globals.p_num; p1++) {
1311                   if (player_globals.parray[p1].status != PLAYER_PROMPT) continue;
1312                   if (player_is_observe(p1, g) || player_globals.parray[p1].game == g) {
1313                           pprintf(p1, "Game %u: %s %s\n", g, EndString(g,0), EndSym(g));
1314                   }
1315           }
1316   }
1317
1318   return COM_OK;
1319 }
1320
1321 int com_backward(int p, param_list param)
1322 {
1323   struct player *pp = &player_globals.parray[p];
1324   int nHalfMoves = 1;
1325   int g, i;
1326   int p1;
1327   unsigned now;
1328
1329   if (!((pp->game >=0) && ((game_globals.garray[pp->game].status == GAME_EXAMINE) || (game_globals.garray[pp->game].status == GAME_SETUP)))) {
1330     pprintf(p, "You are not examining any games.\n");
1331     return COM_OK;
1332   }
1333   if (game_globals.garray[pp->game].status == GAME_SETUP) {
1334     pprintf (p,"You can't move backward yet, the postion is still being set up.\n");
1335     return COM_OK;
1336   }
1337
1338   g = pp->game;
1339   if (param[0].type == TYPE_INT) {
1340     nHalfMoves = param[0].val.integer;
1341   }
1342   if (game_globals.garray[g].numHalfMoves != 0) {
1343     for (p1 = 0; p1 < player_globals.p_num; p1++) {
1344       if (player_globals.parray[p1].status != PLAYER_PROMPT)
1345         continue;
1346       if (player_is_observe(p1, g) || player_globals.parray[p1].game == g) {
1347         pprintf(p1, "Game %u: %s backs up %d move%s.\n",
1348                 g,
1349                 pp->name, nHalfMoves, (nHalfMoves == 1) ? "" : "s");
1350       }
1351     }
1352   }
1353   for (i = 0; i < nHalfMoves; i++) {
1354     if (backup_move(g, REL_EXAMINE) != MOVE_OK) {
1355       for (p1 = 0; p1 < player_globals.p_num; p1++) {
1356         if (player_globals.parray[p1].status != PLAYER_PROMPT)
1357           continue;
1358         if (player_is_observe(p1, g) || player_globals.parray[p1].game == g) {
1359           pprintf(p1, "Game %u: Beginning of game.\n", g);
1360         }
1361       }
1362
1363       break;
1364     }
1365   }
1366   if (game_globals.garray[g].numHalfMoves < game_globals.garray[g].revertHalfMove) {
1367     game_globals.garray[g].revertHalfMove = game_globals.garray[g].numHalfMoves;
1368   }
1369   /* roll back time */
1370   if (game_globals.garray[g].game_state.onMove == WHITE) {
1371     game_globals.garray[g].wTime += (game_globals.garray[g].lastDecTime - game_globals.garray[g].lastMoveTime);
1372   } else {
1373     game_globals.garray[g].bTime += (game_globals.garray[g].lastDecTime - game_globals.garray[g].lastMoveTime);
1374   }
1375   now = tenth_secs();
1376   if (game_globals.garray[g].numHalfMoves == 0)
1377     game_globals.garray[g].timeOfStart = now;
1378   game_globals.garray[g].lastMoveTime = now;
1379   game_globals.garray[g].lastDecTime = now;
1380   send_boards(g);
1381   return COM_OK;
1382 }
1383
1384 int com_revert(int p, param_list param)
1385 {
1386   struct player *pp = &player_globals.parray[p];
1387   int nHalfMoves = 1;
1388   int g, i;
1389   int p1;
1390   unsigned now;
1391
1392   if (!((pp->game >=0) && ((game_globals.garray[pp->game].status == GAME_EXAMINE)|| (game_globals.garray[pp->game].status == GAME_SETUP)))) {
1393     pprintf(p, "You are not examining any games.\n");
1394     return COM_OK;
1395   }
1396   if (game_globals.garray[pp->game].status == GAME_SETUP) {
1397     pprintf (p,"You can't move revert yet, the position is still being set up.\n");
1398     return COM_OK;
1399   }
1400   g = pp->game;
1401   nHalfMoves = game_globals.garray[g].numHalfMoves - game_globals.garray[g].revertHalfMove;
1402   if (nHalfMoves == 0) {
1403     pprintf(p, "Game %u: Already at mainline.\n", g);
1404     return COM_OK;
1405   }
1406   if (nHalfMoves < 0) {         /* eek - should NEVER happen! */
1407     d_printf( "OUCH! in com_revert: nHalfMoves < 0\n");
1408     return COM_OK;
1409   }
1410   for (p1 = 0; p1 < player_globals.p_num; p1++) {
1411     if (player_globals.parray[p1].status != PLAYER_PROMPT)
1412       continue;
1413     if (player_is_observe(p1, g) || player_globals.parray[p1].game == g) {
1414       pprintf(p1, "Game %u: %s reverts to mainline.\n", 
1415               g, pp->name);
1416     }
1417   }
1418   for (i = 0; i < nHalfMoves; i++) {
1419     backup_move(g, REL_EXAMINE);/* should never return error */
1420   }
1421   /* roll back time */
1422   if (game_globals.garray[g].game_state.onMove == WHITE) {
1423     game_globals.garray[g].wTime += (game_globals.garray[g].lastDecTime - game_globals.garray[g].lastMoveTime);
1424   } else {
1425     game_globals.garray[g].bTime += (game_globals.garray[g].lastDecTime - game_globals.garray[g].lastMoveTime);
1426   }
1427   now = tenth_secs();
1428   if (game_globals.garray[g].numHalfMoves == 0)
1429     game_globals.garray[g].timeOfStart = now;
1430   game_globals.garray[g].lastMoveTime = now;
1431   game_globals.garray[g].lastDecTime = now;
1432   send_boards(g);
1433   return COM_OK;
1434 }
1435
1436 int com_history(int p, param_list param)
1437 {
1438   int p1, connected;
1439   char fname[MAX_FILENAME_SIZE];
1440
1441   if (param[0].type == TYPE_WORD) {
1442     if (!FindPlayer(p, param[0].val.word, &p1, &connected))
1443       return COM_OK;
1444   } else {
1445       p1 = p;
1446       connected = 1;
1447   }
1448
1449   sprintf(fname, "%s/player_data/%c/%s.%s", STATS_DIR, player_globals.parray[p1].login[0],
1450           player_globals.parray[p1].login, STATS_GAMES);
1451   pgames(p, p1, fname);
1452   if (!connected)
1453     player_remove(p1);
1454   return COM_OK;
1455 }
1456
1457 int com_journal(int p, param_list param)
1458 {
1459   struct player *pp = &player_globals.parray[p];
1460   int p1, connected;
1461   char fname[MAX_FILENAME_SIZE];
1462
1463     if (param[0].type == TYPE_WORD) {
1464     if (!FindPlayer(p, param[0].val.word, &p1, &connected))
1465       return COM_OK;
1466   } else {
1467       p1 = p;
1468       connected = 1;
1469   }
1470
1471   if (!CheckPFlag(p1, PFLAG_REG)) {
1472     pprintf (p,"Only registered players may keep a journal.\n");
1473     if (!connected)
1474       player_remove(p1);
1475     return COM_OK;
1476   }
1477   if (CheckPFlag(p1, PFLAG_JPRIVATE) && (p != p1)
1478       && (pp->adminLevel < ADMIN_ADMIN)) {
1479     pprintf (p,"Sorry, this journal is private.\n");
1480     if (!connected)
1481       player_remove(p1);
1482     return COM_OK;
1483   }
1484   sprintf(fname, "%s/player_data/%c/%s.%s", STATS_DIR, player_globals.parray[p1].login[0],
1485           player_globals.parray[p1].login, STATS_JOURNAL);
1486   pjournal(p, p1, fname);
1487   if (!connected)
1488     player_remove(p1);
1489   return COM_OK;
1490 }
1491
1492 /* Remove a journal item */
1493
1494 int com_jkill(int p, param_list param)
1495 {
1496   struct player *pp = &player_globals.parray[p];
1497   FILE* Journal;
1498   char* kill = param[0].val.word;
1499   char fname[MAX_FILENAME_SIZE];
1500   char fname_new[MAX_FILENAME_SIZE];
1501   int empty;
1502
1503   if (!CheckPFlag(p, PFLAG_REG)) {
1504      pprintf (p,"Only registered players may keep a journal.\n");
1505      return COM_OK;
1506   }
1507
1508   if ((strlen(kill) != 1) || (!(isalpha(kill[0])))) {
1509     pprintf (p,"Journal entries are referenced by single letters.\n");
1510     return COM_OK;
1511   }
1512
1513   if (((kill[0] - 'a' + 1) > MAX_JOURNAL) && (pp->adminLevel < ADMIN_ADMIN)
1514   && (!titled_player(p,pp->login))) {
1515     pprintf (p,"Your maximum journal entry is %c\n",toupper ((char)(MAX_JOURNAL
1516 + 'A' - 1)));
1517     return COM_OK;
1518   }
1519
1520   sprintf(fname, "%s/player_data/%c/%s.journal",
1521       STATS_DIR, pp->login[0],pp->login);
1522   sprintf (fname_new,"%s.w",fname);
1523
1524   Journal = fopen_s(fname, "r");
1525   if (Journal == NULL) {
1526      pprintf(p, "You don't have a journal.\n");
1527      return COM_OK;
1528      }
1529
1530   kill[0] = toupper(kill[0]);
1531  
1532   if (removejournalitem(p, kill[0], Journal,fname_new,&empty)) {
1533     fclose (Journal);
1534     rename (fname_new,fname);
1535     if (empty)
1536        unlink (fname);
1537     sprintf(fname, "%s/%c/%s.%c",
1538          JOURNAL_DIR, pp->login[0],pp->login,tolower(kill[0]));
1539     unlink(fname);
1540     pprintf (p,"Journal entry %c deleted.\n",kill[0]);
1541   } else { 
1542     pprintf (p,"You have no journal entry %c.\n",kill[0]);
1543     fclose (Journal);
1544     unlink (fname_new);
1545   }
1546   return COM_OK;
1547 }
1548
1549 static void jsave_journalentry(int p,char save_spot,int p1,char from_spot,char* to_file)
1550 {
1551   struct player *pp = &player_globals.parray[p];
1552   FILE *Game;
1553   char fname[MAX_FILENAME_SIZE], fname2[MAX_FILENAME_SIZE];
1554   char* name_from = player_globals.parray[p1].login;
1555   char* name_to = pp->login;
1556   struct journal* j;
1557
1558   sprintf(fname, "%s/%c/%s.%c", JOURNAL_DIR, name_from[0],name_from,from_spot);
1559   Game = fopen_s(fname, "r");
1560   if (Game == NULL) {
1561      pprintf(p, "Journal entry %c not available for %s.\n", toupper(from_spot), player_globals.parray[p1].name);
1562      return;
1563      }
1564   fclose (Game);
1565   
1566   sprintf(fname2, "%s/%c/%s.%c", JOURNAL_DIR, name_to[0],name_to,save_spot);
1567
1568   if (file_copy(fname, fname2) != 0) {
1569     pprintf (p,"Copy in jsave_journalentry failed!\n");
1570     pprintf (p,"Please report this to an admin.\n");
1571     d_printf("CHESSD: Copy failed in jsave_journalentry\n");
1572     return;
1573   }
1574
1575  sprintf(fname, "%s/player_data/%c/%s.%s", STATS_DIR, name_from[0],
1576           name_from, STATS_JOURNAL);
1577
1578  j = (struct journal*) malloc (sizeof(struct journal));
1579  if (!journal_get_info(p,tolower(from_spot),j,fname)) {
1580    free (j);
1581    return;
1582  }
1583
1584  j->slot = toupper(save_spot);
1585  addjournalitem(p, j, to_file);
1586
1587  pprintf(p,"Journal entry %s %c saved in slot %c in journal.\n",player_globals.parray[p1].name, toupper(from_spot), toupper(save_spot));
1588  free (j);
1589 }
1590
1591 static void jsave_history(int p,char save_spot,int p1,int from,char* to_file)
1592 {
1593         struct player *pp = &player_globals.parray[p];
1594         char Eco[100];
1595         char End[100];
1596         char jfname[MAX_FILENAME_SIZE];
1597         char* HistoryFname = FindHistory2(p, p1, from, Eco, End);
1598         /* End symbol Mat Res, etc is the only thing we can't find out */
1599         char command[MAX_FILENAME_SIZE*2+3];
1600         char* name_to = pp->login;
1601         FILE *Game;
1602         int g;
1603         struct journal* j;
1604         
1605         if (HistoryFname == NULL) {
1606                 return;
1607         }
1608         Game = fopen_s(HistoryFname, "r");
1609         if (Game == NULL) {
1610                 pprintf(p, "History game %d not available for %s.\n", 
1611                         from, player_globals.parray[p1].name);
1612                 return;
1613         } 
1614
1615         sprintf(jfname, "%s/%c/%s.%c", JOURNAL_DIR, name_to[0],name_to,save_spot);
1616         unlink(jfname); /* necessary if cp is hard aliased to cp -i */
1617         sprintf(command, "cp %s %s",HistoryFname,jfname);
1618         if (file_copy(HistoryFname, jfname) != 0) {
1619                 pprintf (p,"Copy in jsave_history failed!\n");
1620                 pprintf (p,"Please report this to an admin.\n");
1621                 d_printf("CHESSD: Copy failed in jsave_journalentry\n");
1622                 return;
1623         }
1624         g = game_new(); /* Open a dummy game */
1625         
1626         if (ReadGameAttrs(Game, g) < 0) {
1627                 pprintf (p,"Gamefile is corrupt. Please tell an admin.\n");
1628                 game_remove(g);
1629                 fclose (Game);
1630                 return;
1631         }
1632         fclose (Game);
1633         
1634         j = (struct journal*) malloc (sizeof(struct journal));
1635         
1636         if (game_globals.garray[g].private) {
1637                 j->type[0] = 'p';
1638         } else {
1639                 j->type[0] = ' ';
1640         }
1641         if (game_globals.garray[g].type == TYPE_BLITZ) {
1642                 j->type[1] = 'b';
1643         } else if (game_globals.garray[g].type == TYPE_WILD) {
1644                 j->type[1] = 'w';
1645         } else if (game_globals.garray[g].type == TYPE_STAND) {
1646                 j->type[1] = 's';
1647         } else if (game_globals.garray[g].type == TYPE_LIGHT) {
1648                 j->type[1] = 'l';
1649         } else if (game_globals.garray[g].type == TYPE_BUGHOUSE) {
1650                 j->type[1] = 'B';
1651         } else {
1652                 if (game_globals.garray[g].type == TYPE_NONSTANDARD)
1653                         j->type[1] = 'n';
1654                 else
1655                         j->type[1] = 'u';
1656         }
1657         if (game_globals.garray[g].rated) {
1658                 j->type[2] = 'r';
1659         } else {
1660                 j->type[2] = 'u';
1661         }
1662         j->type[3] = '\0';
1663         
1664         j->slot = toupper(save_spot);
1665         strcpy (j->WhiteName, game_globals.garray[g].white_name);
1666         j->WhiteRating = game_globals.garray[g].white_rating;
1667         strcpy (j->BlackName, game_globals.garray[g].black_name);
1668         j->BlackRating = game_globals.garray[g].black_rating;
1669         j->t = game_globals.garray[g].wInitTime;
1670         j->i = game_globals.garray[g].wIncrement;
1671         strcpy (j->eco, Eco);
1672         strcpy (j->ending, End);
1673         strcpy (j->result, EndSym(g));
1674         
1675         addjournalitem(p, j, to_file);
1676         game_remove(g);
1677         pprintf(p,"Game %s %d saved in slot %c in journal.\n",
1678                 player_globals.parray[p1].name, from, toupper(save_spot));
1679         free(j);
1680 }
1681
1682 int com_jsave(int p, param_list param)
1683 {
1684   struct player *pp = &player_globals.parray[p];
1685   int p1, p1conn;
1686   char* to = param[0].val.word;
1687   char* from;
1688   char fname[MAX_FILENAME_SIZE];
1689
1690   if (!CheckPFlag(p, PFLAG_REG)) {
1691      pprintf (p,"Only registered players may keep a journal.\n");
1692      return COM_OK;
1693   }
1694
1695   if ((strlen(to) != 1) || (!(isalpha(to[0])))) {
1696     pprintf (p,"Journal entries are referenced by single letters.\n");
1697     return COM_OK;
1698   }
1699
1700   if (((to[0] - 'a' + 1) > MAX_JOURNAL) && (pp->adminLevel < ADMIN_ADMIN) && (!titled_player(p,pp->login))) {
1701     pprintf (p,"Your maximum journal entry is %c\n",toupper ((char)(MAX_JOURNAL + 'A' - 1)));
1702     return COM_OK;
1703   }
1704
1705   if (!FindPlayer(p, param[1].val.word, &p1, &p1conn))
1706     return COM_OK;
1707
1708   if (param[2].type == TYPE_INT) {
1709
1710   /* grab from a history */
1711     sprintf (fname,"%s/player_data/%c/%s.%s",STATS_DIR,pp->login[0],pp->login, STATS_JOURNAL);
1712     jsave_history(p, to[0], p1, param[2].val.integer,fname); 
1713
1714   } else {
1715
1716   from = param[2].val.word;
1717
1718   if ((strlen(from) != 1) || (!(isalpha(from[0])))) {
1719     pprintf (p,"Journal entries are referenced by single letters.\n");
1720     if (!p1conn)
1721       player_remove(p1);
1722     return COM_OK;
1723     }
1724
1725   if (CheckPFlag(p1, PFLAG_JPRIVATE)
1726       && (pp->adminLevel < ADMIN_ADMIN) && (p != p1)) {
1727     pprintf (p,"Sorry, the journal from which you are trying to fetch is private.\n");
1728
1729     if (!p1conn)
1730        player_remove(p1);
1731     return COM_OK;
1732     }
1733
1734   if (((to[0] - 'a' + 1) > MAX_JOURNAL) && (player_globals.parray[p1].adminLevel < ADMIN_ADMIN) && (!titled_player(p,player_globals.parray[p1].login))) {
1735     pprintf (p,"%s's maximum journal entry is %c\n",player_globals.parray[p1].name,toupper((char)(MAX_JOURNAL + 'A' - 1)));
1736     if (!p1conn)
1737        player_remove(p1);
1738     return COM_OK;
1739   }
1740   if (( p == p1) && (to[0] == from [0])) {
1741     pprintf (p,"Source and destination entries are the same.\n");
1742     return COM_OK;
1743   }
1744   
1745   /* grab from a journal */
1746
1747   sprintf(fname, "%s/player_data/%c/%s.%s", STATS_DIR, pp->login[0],
1748           pp->login, STATS_JOURNAL);
1749   jsave_journalentry(p,to[0],p1, from[0], fname);
1750
1751   }
1752   if (!p1conn)
1753      player_remove(p1);
1754   return COM_OK;
1755 }
1756
1757 int com_refresh(int p, param_list param)
1758 {
1759   struct player *pp = &player_globals.parray[p];
1760   int g, p1;
1761
1762   if (param[0].type == TYPE_NULL) {
1763     if (pp->game >= 0) {
1764       send_board_to(pp->game, p);
1765     } else {                    /* Do observing in here */
1766       if (pp->num_observe) {
1767         for (g = 0; g < pp->num_observe; g++) {
1768           send_board_to(pp->observe_list[g], p);
1769         }
1770       } else {
1771         pprintf(p, "You are neither playing, observing nor examining a game.\n");
1772         return COM_OK;
1773       }
1774     }
1775   } else {
1776     g = GameNumFromParam (p, &p1, &param[0]);
1777     if (g < 0)
1778       return COM_OK;
1779     if ((g >= game_globals.g_num) || ((game_globals.garray[g].status != GAME_ACTIVE)
1780                         && ((game_globals.garray[g].status != GAME_EXAMINE)
1781                         || (game_globals.garray[g].status != GAME_SETUP)))) {
1782       pprintf(p, "No such game.\n");
1783     } else {
1784
1785       int link = game_globals.garray[g].link;
1786
1787       if ((game_globals.garray[g].private && pp->adminLevel==ADMIN_USER) &&
1788         (game_globals.garray[g].white != p) && (game_globals.garray[g].white != p1)) {
1789         if (link != -1) {
1790           if ((game_globals.garray[link].white != p) && (game_globals.garray[link].black != p)) {
1791             pprintf (p, "Sorry, game %d is a private game.\n", g+1);
1792             return COM_OK;
1793           }
1794         }
1795       }
1796
1797       if (game_globals.garray[g].private)
1798         pprintf(p, "Refreshing static game %d\n", g+1);
1799       send_board_to(g, p);
1800     }
1801   }
1802   return COM_OK;
1803 }
1804
1805 int com_prefresh(int p, param_list param)
1806 {
1807   struct player *pp = &player_globals.parray[p];
1808   int retval, part = pp->partner;
1809
1810   if (part < 0) {
1811     pprintf(p, "You do not have a partner.\n");
1812     return COM_OK;
1813   }
1814   retval = pcommand (p, "refresh %s", player_globals.parray[part].name);
1815   if (retval == COM_OK)
1816     return COM_OK_NOPROMPT;
1817   else
1818     return retval;
1819 }