Hack to bypass timeseal decoder
[capablanca.git] / lasker-2.2.3 / src / playerdb.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 static int player_zero(int p);
24
25 static int get_empty_slot(void)
26 {
27         int i;
28
29         for (i = 0; i < player_globals.p_num; i++) {
30                 if (player_globals.parray[i].status == PLAYER_EMPTY) 
31                         return i;
32         }
33
34         if (i < player_globals.parray_size) {
35                 player_globals.p_num++;
36                 ZERO_STRUCT(player_globals.parray[i]);
37                 player_globals.parray[i].status = PLAYER_EMPTY;
38                 return i;
39         }
40
41         if (player_globals.parray_size >= config_get_int("MAX_PLAYERS", DEFAULT_MAX_PLAYER)) {
42                 d_printf("Too many players connected!\n");
43                 return -1;
44         }
45
46         player_globals.parray_size += 100;
47         player_globals.parray = (struct player *)realloc(player_globals.parray,
48                                                          player_globals.parray_size * sizeof(struct player));
49         
50         player_globals.p_num++;
51         ZERO_STRUCT(player_globals.parray[i]);
52         player_globals.parray[i].status = PLAYER_EMPTY;
53         return i;
54 }
55
56 int player_new(void)
57 {
58         int new = get_empty_slot();
59   
60         player_zero(new);
61         return new;
62 }
63
64 static void ResetStats(struct statistics *ss)
65 {
66         ss->num = 0;
67         ss->win = 0;
68         ss->los = 0;
69         ss->dra = 0;
70         ss->rating = config_get_int("DEFAULT_RATING", DEFAULT_RATING);
71         ss->sterr = config_get_int("DEFAULT_RD", DEFAULT_RD);
72         ss->ltime = 0;
73         ss->best = 0;
74         ss->whenbest = 0;
75 }
76
77 static int player_zero(int p)
78 {
79         struct player *pp = &player_globals.parray[p];
80
81         ZERO_STRUCTP(pp);
82
83         pp->prompt = config_get("DEFAULT_PROMPT");
84         pp->partner = -1;
85         pp->socket = -1;
86         pp->status = PLAYER_NEW;
87         
88         ResetStats(&pp->s_stats);
89         ResetStats(&pp->b_stats);
90         ResetStats(&pp->l_stats);
91         ResetStats(&pp->w_stats);
92         ResetStats(&pp->bug_stats);
93         
94         pp->d_time = config_get_int("DEFAULT_TIME", DEFAULT_TIME);
95         pp->d_inc = config_get_int("DEFAULT_INCREMENT", DEFAULT_INCREMENT);
96         pp->d_height = 24;
97         pp->d_width = 79;
98         pp->language = LANG_DEFAULT;
99         pp->promote = QUEEN;
100         pp->game = -1;
101         pp->last_channel = -1;
102         pp->ftell = -1;
103         pp->Flags = PFLAG_DEFAULT;
104         pp->opponent = -1;
105         return 0;
106 }
107
108 void player_free(struct player *pp)
109 {
110         int i;
111         
112         FREE(pp->login);
113         FREE(pp->more_text);
114         FREE(pp->name);
115         FREE(pp->passwd);
116         FREE(pp->fullName);
117         FREE(pp->emailAddress);
118         FREE(pp->prompt);
119         FREE(pp->formula);
120         FREE(pp->busy);
121         FREE(pp->last_tell);
122         FREE(pp->last_opponent);
123         FREE(pp->simul_info);
124         for (i = 0; i < pp->num_plan; i++)
125                 FREE(pp->planLines[i]);
126         for (i = 0; i < pp->num_formula; i++)
127                 FREE(pp->formulaLines[i]);
128         list_free(pp->lists);
129         for (i = 0; i < pp->numAlias; i++) {
130                 FREE(pp->alias_list[i].comm_name);
131                 FREE(pp->alias_list[i].alias);
132         }
133 }
134
135 int player_clear(int p)
136 {
137         player_free(&player_globals.parray[p]);
138         player_zero(p);
139         return 0;
140 }
141
142 int player_remove(int p)
143 {
144         struct player *pp = &player_globals.parray[p];
145         int i;
146
147         decline_withdraw_offers(p, -1, -1, DO_DECLINE | DO_WITHDRAW);
148         if (pp->simul_info != NULL)
149                 /* Player disconnected in middle of simul */
150                 for (i = 0; pp->simul_info != NULL
151                              && i < pp->simul_info->numBoards; i++)
152                         if (pp->simul_info->boards[i] >= 0) 
153                                 game_disconnect(pp->simul_info->boards[i], p);
154         
155         if ((pp->game >=0) && (pIsPlaying(p))) {  
156                 /* Player disconnected in the middle of a game! */
157                 pprintf(pp->opponent, "Your opponent has lost contact or quit.");
158                 game_disconnect(pp->game, p);
159         }
160         for (i = 0; i < player_globals.p_num; i++) {
161                 if (player_globals.parray[i].status == PLAYER_EMPTY)
162                         continue;
163                 if (player_globals.parray[i].partner == p) {
164                         pprintf_prompt (i, "Your partner has disconnected.\n");
165                         decline_withdraw_offers(i, -1, PEND_BUGHOUSE, DO_DECLINE | DO_WITHDRAW);
166                         player_globals.parray[i].partner = -1;
167                 }
168         }
169         player_clear(p);
170         pp->status = PLAYER_EMPTY;
171
172         return 0;
173 }
174
175 /*
176   write a player file using the new generic marshalling format
177 */
178 static int WritePlayerFile_v100(int p)
179 {
180         struct player pp = player_globals.parray[p];
181         const char *s;
182         FILE *fp;
183
184         /* zero any elements we don't want to save */
185         memset(&pp, 0, offsetof(struct player, not_saved_marker));
186
187         /* marshall it into a string */
188         s = marshall_player(&pp);
189         if (!s) {
190                 d_printf("Unable to marshall player structure for %s\n", 
191                          pp.name);
192                 return -1;
193         }
194
195         fp = fopen_p("%s/%c/%s", "w", 
196                      PLAYER_DIR, 
197                      player_globals.parray[p].login[0], 
198                      player_globals.parray[p].login);
199         if (!fp) {
200                 d_printf("CHESSD: Problem opening player file '%s' for write\n", 
201                          player_globals.parray[p].login);
202                 free(s);
203                 return -1;
204         }
205
206         /* and save it */
207         fprintf(fp, "v 100\n%s\n", s);
208         free(s);
209         fclose(fp);
210         return 0;
211 }
212
213 /*
214   read a player file using the new generic and extensible format 
215 */
216 static int ReadPlayerFile_v100(FILE *fp, int p)
217 {
218         char *s, *s2;       
219         struct player *pp = &player_globals.parray[p];
220
221         s = fd_load(fileno(fp), NULL);
222         if (!s) {
223                 d_printf("Error reading player file for '%s'\n", pp->login);
224                 return -1;
225         }
226
227         /* skip first line */
228         s2 = strchr(s, '\n');
229
230         /* the marshaller doesn't save zero elements, but some elements don't
231            default to zero. Make sure they get the right value */
232         memset(&pp->not_saved_marker, 0, 
233                sizeof(struct player) - offsetof(struct player, not_saved_marker));
234
235         if (!s2 || unmarshall_player(pp, s2) != 0) {
236                 d_printf("Error unmarshalling player data for '%s'!\n", pp->login);
237                 free(s);
238                 return -1;
239         }
240         free(s);
241
242         lists_validate(p);
243         
244         return 0;
245 }
246
247 int player_read(int p, char *name)
248 {
249         struct player *pp = &player_globals.parray[p];
250         char fname[MAX_FILENAME_SIZE];
251         char line[MAX_LINE_SIZE];
252         FILE *fp;
253         int version = 0;
254         int ret;
255
256         pp->login = stolower(strdup(name));
257
258         sprintf(fname, "%s/%c/%s", PLAYER_DIR, pp->login[0], pp->login);
259         fp = fopen_s(fname, "r");
260   
261         if (pp->name)
262                 free(pp->name);
263
264         if (!fp) { /* unregistered player */
265                 pp->name = stolower(strdup(name));
266                 PFlagOFF(p, PFLAG_REG);
267                 return -1;
268         }
269
270         PFlagON(p, PFLAG_REG); /* lets load the file */
271         fgets(line, MAX_LINE_SIZE, fp); /* ok so which version file? */
272
273         if (line[0] == 'v') {
274                 sscanf(line, "%*c %d", &version);
275         }
276
277         if (version == 9 || version == 100) {
278                 /* its the new style */
279                 ret = ReadPlayerFile_v100(fp, p);
280         } else {
281                 /* fall back to the old style */
282                 ret = player_read_old(p, fp, name, fname, version);
283                 player_save(p); /* update to the new generic format */
284         }
285
286         fclose(fp);
287
288         return ret;
289 }
290
291 int player_save(int p)
292 {
293         struct player *pp = &player_globals.parray[p];
294
295         if (!CheckPFlag(p, PFLAG_REG)) { /* Player not registered */
296                 return -1;
297         }
298         if (pp->name == NULL) { /* fixes a bug if name is null */
299                 pprintf(p, "WARNING: Your player file could not be updated, due to corrupt data.\n");
300                 return -1;
301         }
302         if (strcasecmp(pp->login, pp->name)) {
303                 pprintf(p, "WARNING: Your player file could not be updated, due to corrupt data.\n");
304                 return -1;
305         }
306
307         return WritePlayerFile_v100(p);
308 }
309
310 int player_find(int fd)
311 {
312         int i;
313         
314         for (i = 0; i < player_globals.p_num; i++) {
315                 if (player_globals.parray[i].status == PLAYER_EMPTY)
316                         continue;
317                 if (player_globals.parray[i].socket == fd)
318                         return i;
319         }
320         return -1;
321 }
322
323 /* incorrectly named */
324 int player_find_bylogin(const char *name)
325 {
326         int i;
327         
328         for (i = 0; i < player_globals.p_num; i++) {
329                 if ((player_globals.parray[i].status == PLAYER_EMPTY) ||
330                     (player_globals.parray[i].status == PLAYER_LOGIN) ||
331                     (player_globals.parray[i].status == PLAYER_PASSWORD))
332                         continue;
333                 if (!player_globals.parray[i].login)
334                         continue;
335                 if (!strcasecmp(player_globals.parray[i].name, name))
336                         return i;
337         }
338         return -1;
339 }
340
341 /* Now incorrectly named */
342 int player_find_part_login(const char *name)
343 {
344         int i;
345         int found = -1;
346         
347         i = player_find_bylogin(name);
348         if (i >= 0)
349                 return i;
350         for (i = 0; i < player_globals.p_num; i++) {
351                 if ((player_globals.parray[i].status == PLAYER_EMPTY) ||
352                     (player_globals.parray[i].status == PLAYER_LOGIN) ||
353                     (player_globals.parray[i].status == PLAYER_PASSWORD))
354                         continue;
355                 if (!player_globals.parray[i].name)
356                         continue;
357                 if (!strncasecmp(player_globals.parray[i].name, name, strlen(name))) {
358                         found = i;
359                         break;
360                 }
361         }
362
363         if(found < 0)
364                 return -1;
365         else
366                 return found;
367 }
368
369 int player_censored(int p, int p1)
370 {
371         if (in_list(p, L_CENSOR, player_globals.parray[p1].login))
372                 return 1;
373         else
374                 return 0;
375 }
376
377 /* is p1 on p's notify list? */
378 int player_notified(int p, int p1)
379 {
380         struct player *pp = &player_globals.parray[p];
381         if (!CheckPFlag(p1, PFLAG_REG))
382                 return 0;
383
384         /* possible bug: p has just arrived! */
385         if (!pp->name)
386                 return 0;
387         
388         return (in_list(p, L_NOTIFY, player_globals.parray[p1].login));
389 }
390
391 void player_notify_departure(int p)
392 /* Notify those with notifiedby set on a departure */
393 {
394   struct player *pp = &player_globals.parray[p];
395   int p1;
396
397   if (!CheckPFlag(p, PFLAG_REG))
398     return;
399   for (p1 = 0; p1 < player_globals.p_num; p1++) {
400     if (CheckPFlag(p1, PFLAG_NOTIFYBY) && !player_notified(p1, p)
401         && player_notified(p, p1) && (player_globals.parray[p1].status == PLAYER_PROMPT)) {
402       Bell (p1);
403       pprintf(p1, "\nNotification: ");
404       pprintf_highlight(p1, "%s", pp->name);
405       pprintf_prompt(p1, " has departed and isn't on your notify list.\n");
406     }
407   }
408 }
409
410 int player_notify_present(int p)
411 /* output Your arrival was notified by..... */
412 /* also notify those with notifiedby set if necessary */
413 {
414   struct player *pp = &player_globals.parray[p];
415   int p1;
416   int count = 0;
417
418   if (!CheckPFlag(p, PFLAG_REG))
419     return count;
420   for (p1 = 0; p1 < player_globals.p_num; p1++) {
421     if ((player_notified(p, p1)) && (player_globals.parray[p1].status == PLAYER_PROMPT)) {
422       if (!count) {
423         pprintf(p, "Present company includes:");
424       }
425       count++;
426       pprintf(p, " %s", player_globals.parray[p1].name);
427       if (CheckPFlag(p1, PFLAG_NOTIFYBY) && (!player_notified(p1, p))
428                          && (player_globals.parray[p1].status == PLAYER_PROMPT)) {
429         Bell (p1);
430         pprintf(p1, "\nNotification: ");
431         pprintf_highlight(p1, "%s", pp->name);
432         pprintf_prompt(p1, " has arrived and isn't on your notify list.\n");
433       }
434     }
435   }
436   if (count)
437     pprintf(p, ".\n");
438   return count;
439 }
440
441 int player_notify(int p, char *note1, char *note2)
442 /* notify those interested that p has arrived/departed */
443 {
444   struct player *pp = &player_globals.parray[p];
445   int p1, count = 0;
446
447   if (!CheckPFlag(p, PFLAG_REG))
448     return count;
449   for (p1 = 0; p1 < player_globals.p_num; p1++) {
450     if ((player_notified(p1, p)) && (player_globals.parray[p1].status == PLAYER_PROMPT)) {
451       Bell (p1);
452       pprintf(p1, "\nNotification: ");
453       pprintf_highlight(p1, "%s", pp->name);
454       pprintf_prompt(p1, " has %s.\n", note1);
455       if (!count) {
456         pprintf(p, "Your %s was noted by:", note2);
457       }
458       count++;
459       pprintf(p, " %s", player_globals.parray[p1].name);
460     }
461   }
462   if (count)
463     pprintf(p, ".\n");
464   return count;
465 }
466
467 int showstored(int p)
468 {
469   struct player *pp = &player_globals.parray[p];
470   DIR *dirp;
471   struct dirent *dp;
472
473   int c=0,p1;
474   char dname[MAX_FILENAME_SIZE];
475   multicol *m = multicol_start(50); /* Limit to 50, should be enough*/
476   
477   sprintf(dname, "%s/%c", ADJOURNED_DIR, pp->login[0]);
478   dirp = opendir(dname);
479   if (!dirp) {
480     multicol_end(m);
481     return COM_OK;
482   }
483   for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
484     if (file_has_pname(dp->d_name, pp->login)) {
485       if (strcmp(file_wplayer(dp->d_name),pp->login) != 0) {
486         p1=player_find_bylogin(file_wplayer(dp->d_name));
487       } else {
488         p1=player_find_bylogin(file_bplayer(dp->d_name));
489       }
490       if (p1>=0) {
491         if (c<50)
492                 multicol_store(m,player_globals.parray[p1].name);
493         pprintf(p1,"\nNotification: ");
494         pprintf_highlight(p1,"%s",pp->name);
495         pprintf_prompt(p1,", who has an adjourned game with you, has arrived.\n");
496         c++;
497       } 
498     }
499   }
500   closedir(dirp);
501   if (c == 1) {
502         pprintf(p, "1 player, who has an adjourned game with you, is online:\007");
503   } else if (c > 1) {
504         pprintf(p, "\n%d players, who have an adjourned game with you, are online:\007",c);
505   }
506   if (c != 0)
507         multicol_pprint(m,p,pp->d_width,2);
508   multicol_end(m);
509
510   return COM_OK;
511 }
512
513 int player_count(int CountAdmins)
514 {
515   int count;
516   int i;
517
518   for (count = 0, i = 0; i < player_globals.p_num; i++) {
519     if ((player_globals.parray[i].status == PLAYER_PROMPT) &&
520         (CountAdmins || !in_list(i, L_ADMIN, player_globals.parray[i].name)))
521       count++;
522   }
523   if (count > command_globals.player_high)
524     command_globals.player_high = count;
525
526   return count;
527 }
528
529 int player_idle(int p)
530 {
531         struct player *pp = &player_globals.parray[p];
532         if (pp->status != PLAYER_PROMPT)
533                 return time(0) - pp->logon_time;
534         else
535                 return time(0) - pp->last_command_time;
536 }
537
538 int player_ontime(int p)
539 {
540         struct player *pp = &player_globals.parray[p];
541         return time(0) - pp->logon_time;
542 }
543
544 static void write_p_inout(int inout, int p, char *file, int maxlines)
545 {
546         struct player *pp = &player_globals.parray[p];
547         FILE *fp;
548
549         fp = fopen_s(file, "a");
550         if (!fp)
551                 return;
552         fprintf(fp, "%d %s %d %d %s\n", inout, pp->name, (int) time(0),
553                 BoolCheckPFlag(p, PFLAG_REG),
554                 dotQuad(pp->thisHost));
555         fclose(fp);
556         if (maxlines)
557                 truncate_file(file, maxlines);
558 }
559
560 void player_write_login(int p)
561 {
562         struct player *pp = &player_globals.parray[p];
563         char fname[MAX_FILENAME_SIZE];
564         
565         if (CheckPFlag(p, PFLAG_REG)) {
566                 sprintf(fname, "%s/player_data/%c/%s.%s", STATS_DIR, pp->login[0], pp->login, STATS_LOGONS);
567                 write_p_inout(P_LOGIN, p, fname, 8);
568         }
569         sprintf(fname, "%s/%s", STATS_DIR, STATS_LOGONS);
570         write_p_inout(P_LOGIN, p, fname, 30);
571         /* added complete login/logout log to "logons.log" file */
572         sprintf(fname, "%s/%s", STATS_DIR, "logons.log");
573         write_p_inout(P_LOGIN, p, fname, 0);
574 }
575
576 void player_write_logout(int p)
577 {
578         struct player *pp = &player_globals.parray[p];
579         char fname[MAX_FILENAME_SIZE];
580         
581         if (CheckPFlag(p, PFLAG_REG)) {
582                 sprintf(fname, "%s/player_data/%c/%s.%s", STATS_DIR, pp->login[0], pp->login, STATS_LOGONS);
583                 write_p_inout(P_LOGOUT, p, fname, 8);
584         }
585         sprintf(fname, "%s/%s", STATS_DIR, STATS_LOGONS);
586         write_p_inout(P_LOGOUT, p, fname, 30);
587         /* added complete login/logout log to "logons.log" file */
588         sprintf(fname, "%s/%s", STATS_DIR, "logons.log");
589         write_p_inout(P_LOGOUT, p, fname, 0);
590 }
591
592 int player_lastdisconnect(int p)
593 {
594   struct player *pp = &player_globals.parray[p];
595   char fname[MAX_FILENAME_SIZE];
596   FILE *fp;
597   int inout, thetime, registered;
598   int last = 0;
599   char ipstr[20];
600   char loginName[MAX_LOGIN_NAME];
601
602   sprintf(fname, "%s/player_data/%c/%s.%s", STATS_DIR, pp->login[0], pp->login, STATS_LOGONS);
603   fp = fopen_s(fname, "r");
604   if (!fp)
605     return 0;
606   while (!feof(fp)) {
607     if (fscanf(fp, "%d %s %d %d %s\n", &inout, loginName, &thetime, &registered, ipstr) != 5) {
608       d_printf( "CHESSD: Error in login info format. %s\n", fname);
609       fclose(fp);
610       return 0;
611     }
612     if (inout == P_LOGOUT)
613       last = thetime;
614   }
615   fclose(fp);
616   return last;
617 }
618
619 int player_is_observe(int p, int g)
620 {
621   struct player *pp = &player_globals.parray[p];
622   int i;
623
624   for (i = 0; i < pp->num_observe; i++) {
625     if (pp->observe_list[i] == g)
626       break;
627   }
628   if (i == pp->num_observe)
629     return 0;
630   else
631     return 1;
632 }
633
634 int player_add_observe(int p, int g)
635 {
636   struct player *pp = &player_globals.parray[p];
637   if (pp->num_observe == MAX_OBSERVE)
638     return -1;
639   pp->observe_list[pp->num_observe] = g;
640   pp->num_observe++;
641   return 0;
642 }
643
644 int player_remove_observe(int p, int g)
645 {
646   struct player *pp = &player_globals.parray[p];
647   int i;
648
649   for (i = 0; i < pp->num_observe; i++) {
650     if (pp->observe_list[i] == g)
651       break;
652   }
653   if (i == pp->num_observe)
654     return -1;                  /* Not found! */
655   for (; i < pp->num_observe - 1; i++) {
656     pp->observe_list[i] = pp->observe_list[i + 1];
657   }
658   pp->num_observe--;
659   return 0;
660 }
661
662 int player_game_ended(int g)
663 {
664         int p;
665         
666         for (p = 0; p < player_globals.p_num; p++) {
667                 struct player *pp = &player_globals.parray[p];
668                 if (pp->status == PLAYER_EMPTY)
669                         continue;
670                 player_remove_observe(p, g);
671         }
672         remove_request(game_globals.garray[g].white, game_globals.garray[g].black, -1);
673         remove_request(game_globals.garray[g].black, game_globals.garray[g].white, -1);
674         player_save(game_globals.garray[g].white);      /* Hawk: Added to save finger-info after each
675                                                            game */
676         player_save(game_globals.garray[g].black);
677         return 0;
678 }
679
680 int player_goto_board(int p, int board_num)
681 {
682   struct player *pp = &player_globals.parray[p];
683   int start, count = 0, on, g;
684
685   if (pp->simul_info == NULL)
686     return -1;
687
688   if (board_num < 0 || board_num >= pp->simul_info->numBoards)
689     return -1;
690   if (pp->simul_info->boards[board_num] < 0)
691     return -1;
692   pp->simul_info->onBoard = board_num;
693   pp->game = pp->simul_info->boards[board_num];
694   pp->opponent = game_globals.garray[pp->game].black;
695   if (pp->simul_info->numBoards == 1)
696     return 0;
697   send_board_to(pp->game, p);
698   start = pp->game;
699   on = pp->simul_info->onBoard;
700   do {
701     g = pp->simul_info->boards[on];
702     if (g >= 0) {
703       if (count == 0) {
704         Bell (game_globals.garray[g].black);
705         pprintf(game_globals.garray[g].black, "\n");
706         pprintf_highlight(game_globals.garray[g].black, "%s", pp->name);
707         pprintf_prompt(game_globals.garray[g].black, " is at your board!\n");
708       } else if (count == 1) {
709         Bell (game_globals.garray[g].black);
710         pprintf(game_globals.garray[g].black, "\n");
711         pprintf_highlight(game_globals.garray[g].black, "%s", pp->name);
712         pprintf_prompt(game_globals.garray[g].black, " will be at your board NEXT!\n");
713       } else {
714         pprintf(game_globals.garray[g].black, "\n");
715         pprintf_highlight(game_globals.garray[g].black, "%s", pp->name);
716         pprintf_prompt(game_globals.garray[g].black, " is %d boards away.\n", count);
717       }
718       count++;
719     }
720     on++;
721     if (on >= pp->simul_info->numBoards)
722       on = 0;
723   } while (start != pp->simul_info->boards[on]);
724   return 0;
725 }
726
727 int player_goto_next_board(int p)
728 {
729   struct player *pp = &player_globals.parray[p];
730   int on;
731   int start;
732   int g;
733
734   if (pp->simul_info == NULL)
735     return -1;
736
737   on = pp->simul_info->onBoard;
738   start = on;
739   g = -1;
740   do {
741     on++;
742     if (on >= pp->simul_info->numBoards)
743       on = 0;
744     g = pp->simul_info->boards[on];
745     if (g >= 0)
746       break;
747   } while (start != on);
748   if (g == -1) {
749     pprintf(p, "\nMajor Problem! Can't find your next board.\n");
750     return -1;
751   }
752   return player_goto_board(p, on);
753 }
754
755 int player_goto_prev_board(int p)
756 {
757   struct player *pp = &player_globals.parray[p];
758   int on;
759   int start;
760   int g;
761
762   if (pp->simul_info == NULL)
763     return -1;
764
765   on = pp->simul_info->onBoard;
766   start = on;
767   g = -1;
768   do {
769     --on;
770     if (on < 0)
771       on = (pp->simul_info->numBoards) - 1;
772     g = pp->simul_info->boards[on];
773     if (g >= 0)
774       break;
775   } while (start != on);
776   if (g == -1) {
777     pprintf(p, "\nMajor Problem! Can't find your previous board.\n");
778     return -1;
779   }
780   return player_goto_board(p, on);
781 }
782
783 int player_goto_simulgame_bynum(int p, int num)
784 {
785   struct player *pp = &player_globals.parray[p];
786   int on;
787   int start;
788   int g;
789
790   if (pp->simul_info == NULL)
791     return -1;
792
793   on = pp->simul_info->onBoard;
794   start = on;
795   do {
796     on++;
797     if (on >= pp->simul_info->numBoards)
798       on = 0;
799     g = pp->simul_info->boards[on];
800     if (g == num)
801       break;
802   } while (start != on);
803   if (g != num) {
804     pprintf(p, "\nYou aren't playing that game!!\n");
805     return -1;
806   }
807   return player_goto_board(p, on);
808 }
809
810 int player_num_active_boards(int p)
811 {
812   struct player *pp = &player_globals.parray[p];
813   int count = 0, i;
814
815   if (pp->simul_info == NULL)
816     return 0;
817
818   if (!pp->simul_info->numBoards)
819     return 0;
820   for (i = 0; i < pp->simul_info->numBoards; i++)
821     if (pp->simul_info->boards[i] >= 0)
822       count++;
823   return count;
824 }
825
826 static int player_num_results(int p, int result)
827 {
828   struct player *pp = &player_globals.parray[p];
829   switch (result) {
830   case RESULT_WIN:
831     return pp->simul_info->num_wins;
832   case RESULT_DRAW:
833     return pp->simul_info->num_draws;
834   case RESULT_LOSS:
835     return pp->simul_info->num_losses;
836   default:
837     return -1;
838   }
839 }
840
841 static void new_simul_result(struct simul_info_t *sim, int result)
842 {
843   switch (result) {
844   case RESULT_WIN:
845     sim->num_wins++;
846     return;
847   case RESULT_DRAW:
848     sim->num_draws++;
849     return;
850   case RESULT_LOSS:
851     sim->num_losses++;
852     return;
853   }
854 }
855
856 int player_simul_over(int p, int g, int result)
857 {
858   struct player *pp = &player_globals.parray[p];
859   int on, ong, p1, which;
860   char tmp[1024];
861
862   if (pp->simul_info == NULL)
863     return -1;
864
865   for (which = 0; which < pp->simul_info->numBoards; which++) {
866     if (pp->simul_info->boards[which] == g) {
867       break;
868     }
869   }
870   if (which == pp->simul_info->numBoards) {
871     pprintf(p, "I can't find that game!\n");
872     return -1;
873   }
874   pprintf(p, "\nBoard %d has completed.\n", which + 1);
875   on = pp->simul_info->onBoard;
876   ong = pp->simul_info->boards[on];
877   pp->simul_info->boards[which] = -1;
878   new_simul_result(pp->simul_info, result);
879   if (player_num_active_boards(p) == 0) {
880     sprintf(tmp, "\n{Simul (%s vs. %d) is over.}\nResults: %d Wins, %d Losses, %d Draws, %d Aborts\n",
881             pp->name,
882             pp->simul_info->numBoards,
883             player_num_results(p, RESULT_WIN),
884             player_num_results(p, RESULT_LOSS),
885             player_num_results(p, RESULT_DRAW),
886             player_num_results(p, RESULT_ABORT));
887     for (p1 = 0; p1 < player_globals.p_num; p1++) {
888       if (pp->status != PLAYER_PROMPT)
889         continue;
890       if (!CheckPFlag(p1, PFLAG_GIN) && !player_is_observe(p1, g) && (p1 != p))
891         continue;
892       pprintf_prompt(p1, "%s", tmp);
893     }
894     pp->simul_info->numBoards = 0;
895     pprintf_prompt(p, "\nThat was the last board, thanks for playing.\n");
896     free(pp->simul_info);
897     pp->simul_info = NULL;
898     return 0;
899   }
900   if (ong == g) {               /* This game is over */
901     player_goto_next_board(p);
902   } else {
903     player_goto_board(p, pp->simul_info->onBoard);
904   }
905   pprintf_prompt(p, "\nThere are %d boards left.\n",
906                  player_num_active_boards(p));
907   return 0;
908 }
909
910 static void GetMsgFile (int p, char *fName)
911 {
912         struct player *pp = &player_globals.parray[p];
913         sprintf(fName, "%s/player_data/%c/%s.%s", STATS_DIR, pp->login[0],
914                 pp->login, STATS_MESSAGES);
915 }
916
917 int player_num_messages(int p)
918 {
919   char fname[MAX_FILENAME_SIZE];
920
921   if (!CheckPFlag(p, PFLAG_REG))
922     return 0;
923   GetMsgFile (p, fname);
924   return lines_file(fname);
925 }
926
927 int player_add_message(int top, int fromp, char *message)
928 {
929   char fname[MAX_FILENAME_SIZE];
930   FILE *fp;
931   char subj[256];
932   char messbody[1024];
933   time_t t = time(0);
934
935   if (!CheckPFlag(top, PFLAG_REG))
936     return -1;
937   if (!CheckPFlag(fromp, PFLAG_REG))
938     return -1;
939   GetMsgFile (top, fname);
940   if ((lines_file(fname) >= MAX_MESSAGES) && (player_globals.parray[top].adminLevel == 0))
941     return -1;
942   fp = fopen_s(fname, "a");
943   if (!fp)
944     return -1;
945   fprintf(fp, "%s at %s: %s\n", player_globals.parray[fromp].name, strltime(&t), message);
946   fclose(fp);
947   pprintf(fromp, "\nThe following message was sent ");
948   if (CheckPFlag(top, PFLAG_MAILMESS)) {
949       sprintf(subj, "FICS message from %s at FICS %s (Do not reply by mail)", 
950               player_globals.parray[fromp].name, 
951               config_get_tmp("SERVER_HOSTNAME"));
952       sprintf(messbody, "%s at %s: %s\n", player_globals.parray[fromp].name, strltime(&t), message);
953       mail_string_to_user(top, subj, messbody);
954       pprintf(fromp, "(and emailed) ");
955   }
956   pprintf(fromp, "to %s: \n   %s\n", player_globals.parray[top].name, message);
957   return 0;
958 }
959
960 static int player_forward_message(int top, int fromp, char *message)
961 {
962   char fname[MAX_FILENAME_SIZE];
963   FILE *fp;
964   char subj[256];
965   char messbody[1024];
966
967   if (!CheckPFlag(top, PFLAG_REG))
968     return -1;
969   if (!CheckPFlag(fromp, PFLAG_REG))
970     return -1;
971   GetMsgFile (top, fname);
972   if ((lines_file(fname) >= MAX_MESSAGES) && (player_globals.parray[top].adminLevel == 0))
973     return -1;
974   fp = fopen_s(fname, "a");
975   if (!fp)
976     return -1;
977   fprintf(fp, "FORWARDED MSG FROM %s : %s", player_globals.parray[fromp].name, message);
978   fclose(fp);
979   pprintf(fromp, "\nThe following message was forwarded ");
980   if (CheckPFlag(top, PFLAG_MAILMESS)) {
981       sprintf(subj, "FICS message from %s at FICS %s (Do not reply by mail)", 
982               player_globals.parray[fromp].name, 
983               config_get_tmp("SERVER_HOSTNAME"));
984       sprintf(messbody, "FORWARDED MSG FROM %s: %s\n", player_globals.parray[fromp].name, message);
985       mail_string_to_user(top, subj, messbody);
986       pprintf(fromp, "(and emailed) ");
987   }
988   pprintf(fromp, "to %s: \n   %s\n", player_globals.parray[top].name, message);
989   return 0;
990 }
991 void SaveTextListEntry(textlist **Entry, char *string, int n)
992 {
993   *Entry = (textlist *) malloc(sizeof(textlist));
994   (*Entry)->text = strdup(string);
995   (*Entry)->index = n;
996   (*Entry)->next = NULL;
997 }
998
999 static textlist *ClearTextListEntry(textlist *entry)
1000 {
1001   textlist *ret = entry->next;
1002   free(entry->text);
1003   free(entry);
1004   return ret;
1005 }
1006
1007 void ClearTextList(textlist *head)
1008 {
1009   textlist *cur;
1010
1011   for (cur = head; cur != NULL; cur = ClearTextListEntry(cur));
1012 }
1013
1014 static int SaveThisMsg (int which, char *line)
1015 {
1016   char Sender[MAX_LOGIN_NAME];
1017   int p1;
1018
1019   if (which == 0) return 1;
1020
1021   sscanf (line, "%s", Sender);
1022   if (which < 0) {
1023     p1 = -which - 1;
1024     return strcmp(Sender, player_globals.parray[p1].name);
1025   }
1026   else {
1027     p1 = which - 1;
1028     return !strcmp(Sender, player_globals.parray[p1].name);
1029   }
1030 }
1031
1032 static int LoadMsgs(int p, int which, textlist **Head)
1033 {
1034   FILE *fp;
1035   textlist **Cur = Head;
1036   char fName[MAX_FILENAME_SIZE];
1037   char line[MAX_LINE_SIZE];
1038   int n=0, nSave=0;
1039
1040   *Head = NULL;
1041   GetMsgFile (p, fName);
1042   fp = fopen_s(fName, "r");
1043   if (fp == NULL) {
1044     return -1;
1045   }
1046   while (!feof(fp)) {
1047     fgets(line, MAX_LINE_SIZE, fp);
1048     if (feof(fp))
1049       break;
1050     if (SaveThisMsg(which, line)) {
1051       SaveTextListEntry(Cur, line, ++n);
1052       Cur = &(*Cur)->next;
1053       nSave++;
1054     }
1055     else n++;
1056   }
1057   fclose (fp);
1058   return nSave;
1059 }
1060
1061 /* start > 0 and end > start (or end = 0) to save messages in range;
1062    start < 0 and end < start (or end = 0) to clear messages in range;
1063    if end = 0, range goes to end of file (not tested yet). */
1064 static int LoadMsgRange(int p, int start, int end, textlist **Head)
1065 {
1066   FILE *fp;
1067   char fName[MAX_FILENAME_SIZE];
1068   char line[MAX_LINE_SIZE];
1069   textlist **Cur = Head;
1070   int n=1, nSave=0, nKill=0;
1071
1072   *Head = NULL;
1073   GetMsgFile (p, fName);
1074   fp = fopen  (fName, "r");
1075   if (fp == NULL) {
1076     pprintf (p, "You have no messages.\n");
1077     return -1;
1078   }
1079   for (n=1; n <= end || end <= 0; n++) {
1080     fgets (line, MAX_LINE_SIZE, fp);
1081     if (feof(fp))
1082       break;
1083     if ((start < 0 && (n < -start || n > -end)) || (start >= 0 && n >= start)) {
1084       SaveTextListEntry (Cur, line, n);
1085       Cur = &(*Cur)->next;
1086       nSave++;
1087     }
1088     else nKill++;
1089   }
1090   fclose (fp);
1091   if (start < 0) {
1092     if (n <= -start)
1093       pprintf (p, "You do not have a message %d.\n", -start);
1094     return nKill;
1095   } else {
1096     if (n <= start)
1097       pprintf (p, "You do not have a message %d.\n", start);
1098     return nSave;
1099   }
1100 }
1101
1102 int ForwardMsgRange(char *p1, int p, int start, int end)
1103 {
1104   /* p1 is player to   and p is player from */
1105   FILE *fp;
1106   char fName[MAX_FILENAME_SIZE];
1107   char line[MAX_LINE_SIZE];
1108   int n=1, top, connected;
1109
1110   if (!FindPlayer(p, p1, &top, &connected))
1111     return -1;
1112   GetMsgFile (p, fName);
1113   fp = fopen  (fName, "r");
1114   if (fp == NULL) {
1115     pprintf (p, "You have no messages.\n");
1116     return -1;
1117   }
1118   for (n=1; n <= end || end <= 0; n++) {
1119     fgets (line, MAX_LINE_SIZE, fp);
1120     if (feof(fp))
1121       break;
1122     if ((start < 0 && (n < -start || n > -end)) || (start >= 0 && n >= start)) {
1123       player_forward_message(top, p, line);
1124     }
1125   }
1126   fclose (fp);
1127   if (start < 0) {
1128     if (n <= -start)
1129       pprintf (p, "You do not have a message %d.\n", -start);
1130     return 0;
1131   } else {
1132     if (n <= start)
1133       pprintf (p, "You do not have a message %d.\n", start);
1134     return 0;
1135   }
1136 }
1137
1138 static int WriteMsgFile (int p, textlist *Head)
1139 {
1140   char fName[MAX_FILENAME_SIZE];
1141   FILE *fp;
1142   textlist *Cur;
1143
1144   GetMsgFile (p, fName);
1145   fp = fopen_s(fName, "w");
1146   if (fp == NULL)
1147     return 0;
1148   for (Cur = Head; Cur != NULL; Cur = Cur->next)
1149     fprintf(fp, "%s", Cur->text);
1150   fclose(fp);
1151   return 1;
1152 }
1153
1154 int ClearMsgsBySender(int p, param_list param)
1155 {
1156         struct player *pp = &player_globals.parray[p];
1157         textlist *Head;
1158         int p1, connected;
1159         int nFound;
1160         
1161         if (!FindPlayer(p, param[0].val.word, &p1, &connected))
1162                 return -1;
1163         
1164         nFound = LoadMsgs(p, -(p1+1), &Head);
1165         if (nFound < 0) {
1166                 pprintf(p, "You have no messages.\n");
1167         } else if (nFound == 0) {
1168                 pprintf(p, "You have no messages from %s.\n", player_globals.parray[p1].name);
1169         } else {
1170                 if (WriteMsgFile (p, Head))
1171                         pprintf(p, "Messages from %s cleared.\n", player_globals.parray[p1].name); 
1172                 else {
1173                         pprintf(p, "Problem writing message file; please contact an admin.\n");
1174                         d_printf( "Problem writing message file for %s.\n", pp->name);
1175                 }
1176                 ClearTextList(Head);
1177         }
1178         if (!connected)
1179                 player_remove(p1);
1180         return nFound;
1181 }
1182
1183 static void ShowTextList (int p, textlist *Head, int ShowIndex)
1184 {
1185   textlist *CurMsg;
1186
1187   if (ShowIndex) {
1188     for (CurMsg = Head; CurMsg != NULL; CurMsg = CurMsg->next)
1189       pprintf(p, "%2d. %s", CurMsg->index, CurMsg->text);
1190   }
1191   else {
1192     for (CurMsg = Head; CurMsg != NULL; CurMsg = CurMsg->next)
1193       pprintf(p, "%s", CurMsg->text);
1194   }
1195 }
1196
1197 int player_show_messages(int p)
1198 {
1199   textlist *Head;
1200   int n;
1201
1202   n = LoadMsgs (p, 0, &Head);
1203   if (n <= 0) {
1204     pprintf (p, "You have no messages.\n");
1205     return -1;
1206   } else {
1207     pprintf (p, "Messages:\n");
1208     ShowTextList (p, Head, 1);
1209     ClearTextList (Head);
1210     return 0;
1211   }
1212 }
1213
1214 int ShowMsgsBySender(int p, param_list param)
1215 {
1216   textlist *Head;
1217   int p1, connected;
1218   int nFrom;
1219   int nTo = 0;
1220
1221   if (!FindPlayer(p, param[0].val.word, &p1, &connected))
1222     return -1;
1223
1224   if (!CheckPFlag(p1, PFLAG_REG)) {
1225     pprintf(p, "Player \"%s\" is unregistered and cannot send or receive messages.\n",
1226        player_globals.parray[p1].name);
1227     return -1; /* no need to disconnect */
1228   }
1229  
1230   if (p != p1) {
1231     nTo = LoadMsgs(p1, p+1, &Head);
1232     if (nTo <= 0) {
1233       pprintf(p, "%s has no messages from you.\n", player_globals.parray[p1].name);
1234     } else {
1235       pprintf(p, "Messages to %s:\n", player_globals.parray[p1].name);
1236       ShowTextList (p, Head, 0);
1237       ClearTextList(Head);
1238     }
1239   }
1240   nFrom = LoadMsgs(p, p1+1, &Head);
1241   if (nFrom <= 0) {
1242     pprintf(p, "\nYou have no messages from %s.\n", player_globals.parray[p1].name);
1243   } else {
1244     pprintf(p, "Messages from %s:\n", player_globals.parray[p1].name);
1245     ShowTextList (p, Head, 1);
1246     ClearTextList(Head);
1247   }
1248   if (!connected)
1249     player_remove(p1);
1250   return (nFrom > 0 || nTo > 0);
1251 }
1252
1253 int ShowMsgRange (int p, int start, int end)
1254 {
1255   textlist *Head;
1256   int n;
1257
1258   n = LoadMsgRange (p, start, end, &Head);
1259   if (n > 0) {
1260     ShowTextList (p, Head, 1);
1261     ClearTextList (Head);
1262   }
1263   return n;
1264 }
1265
1266 int ClrMsgRange (int p, int start, int end)
1267 {
1268   textlist *Head;
1269   int n;
1270
1271   n = LoadMsgRange (p, -start, -end, &Head);
1272   if (n > 0)
1273     if (WriteMsgFile (p, Head))
1274       pprintf (p, "Message %d cleared.\n", start);
1275   if (n >= 0)
1276     ClearTextList (Head);
1277
1278   return n;
1279 }
1280
1281 int player_clear_messages(int p)
1282 {
1283   char fname[MAX_FILENAME_SIZE];
1284
1285   if (!CheckPFlag(p, PFLAG_REG))
1286     return -1;
1287   GetMsgFile (p, fname);
1288   unlink(fname);
1289   return 0;
1290 }
1291
1292 int player_search(int p, char *name)
1293 /*
1294  * Find player matching the given string. First looks for exact match
1295  *  with a logged in player, then an exact match with a registered player,
1296  *  then a partial unique match with a logged in player, then a partial
1297  *  match with a registered player.
1298  *  Returns player number if the player is connected, negative (player number)
1299  *  if the player had to be connected, and 0 if no player was found
1300  */
1301 {
1302   int p1, count;
1303   char *buffer[1000];
1304   char pdir[MAX_FILENAME_SIZE];
1305
1306   /* exact match with connected player? */
1307   if ((p1 = player_find_bylogin(name)) >= 0) {
1308     return p1 + 1;
1309   }
1310   /* exact match with registered player? */
1311   sprintf(pdir, "%s/%c", PLAYER_DIR, name[0]);
1312   count = search_directory(pdir, name, buffer, 1000);
1313   if (count > 0 && !strcmp(name, *buffer)) {
1314     goto ReadPlayerFromFile;    /* found an unconnected registered player */
1315   }
1316   /* partial match with connected player? */
1317   if ((p1 = player_find_part_login(name)) >= 0) {
1318     return p1 + 1;
1319   } else if (p1 == -2) {
1320     /* ambiguous; matches too many connected players. */
1321     pprintf (p, "Ambiguous name '%s'; matches more than one player.\n", name);
1322     return 0;
1323   }
1324   /* partial match with registered player? */
1325   if (count < 1) {
1326     pprintf(p, "There is no player matching that name.\n");
1327     return 0;
1328   }
1329   if (count > 1) {
1330     pprintf(p, "-- Matches: %d names --", count);
1331     display_directory(p, buffer, count);
1332     return(0);
1333   }
1334 ReadPlayerFromFile:
1335   p1 = player_new();
1336   if (player_read(p1, *buffer)) {
1337     player_remove(p1);
1338     pprintf(p, "ERROR: a player named %s was expected but not found!\n",
1339             *buffer);
1340     pprintf(p, "Please tell an admin about this incident. Thank you.\n");
1341     return 0;
1342   }
1343   return (-p1) - 1;             /* negative to indicate player was not
1344                                    connected */
1345 }
1346
1347
1348 int player_kill(char *name)
1349 {
1350   char fname[MAX_FILENAME_SIZE], fname2[MAX_FILENAME_SIZE];
1351
1352   sprintf(fname, "%s/%c/%s", PLAYER_DIR, name[0], name);
1353   sprintf(fname2, "%s/%c/.rem.%s", PLAYER_DIR, name[0], name);
1354   rename(fname, fname2);
1355   RemHist (name);
1356   sprintf(fname, "%s/player_data/%c/%s.games", STATS_DIR, name[0], name);
1357   sprintf(fname2, "%s/player_data/%c/.rem.%s.games", STATS_DIR, name[0], name);
1358   rename(fname, fname2);
1359   sprintf(fname, "%s/player_data/%c/%s.comments", STATS_DIR, name[0], name);
1360   sprintf(fname2, "%s/player_data/%c/.rem.%s.comments", STATS_DIR, name[0], name);
1361   rename(fname, fname2);
1362
1363   sprintf(fname, "%s/player_data/%c/%s.logons", STATS_DIR, name[0], name);
1364   sprintf(fname2, "%s/player_data/%c/.rem.%s.logons", STATS_DIR, name[0], name);
1365   rename(fname, fname2);
1366   sprintf(fname, "%s/player_data/%c/%s.messages", STATS_DIR, name[0], name);
1367   sprintf(fname2, "%s/player_data/%c/.rem.%s.messages", STATS_DIR, name[0], name);
1368   rename(fname, fname2);
1369   return 0;
1370 }
1371
1372 int player_rename(char *name, char *newname)
1373 {
1374   char fname[MAX_FILENAME_SIZE], fname2[MAX_FILENAME_SIZE];
1375
1376   sprintf(fname, "%s/%c/%s", PLAYER_DIR, name[0], name);
1377   sprintf(fname2, "%s/%c/%s", PLAYER_DIR, newname[0], newname);
1378   rename(fname, fname2);
1379   sprintf(fname, "%s/player_data/%c/%s.games", STATS_DIR, name[0], name);
1380   sprintf(fname2, "%s/player_data/%c/%s.games", STATS_DIR, newname[0], newname);
1381   rename(fname, fname2);
1382   sprintf(fname, "%s/player_data/%c/%s.comments", STATS_DIR, name[0], name);
1383   sprintf(fname2, "%s/player_data/%c/%s.comments", STATS_DIR, newname[0], newname);
1384   rename(fname, fname2);
1385   sprintf(fname, "%s/player_data/%c/%s.logons", STATS_DIR, name[0], name);
1386   sprintf(fname2, "%s/player_data/%c/%s.logons", STATS_DIR, newname[0], newname);
1387   rename(fname, fname2);
1388   sprintf(fname, "%s/player_data/%c/%s.messages", STATS_DIR, name[0], name);
1389   sprintf(fname2, "%s/player_data/%c/%s.messages", STATS_DIR, newname[0], newname);
1390   rename(fname, fname2);
1391   return 0;
1392 }
1393
1394 int player_reincarn(char *name, char *newname)
1395 {
1396   char fname[MAX_FILENAME_SIZE], fname2[MAX_FILENAME_SIZE];
1397
1398   sprintf(fname, "%s/%c/%s", PLAYER_DIR, newname[0], newname);
1399   sprintf(fname2, "%s/%c/.rem.%s", PLAYER_DIR, name[0], name);
1400   rename(fname2, fname);
1401   sprintf(fname, "%s/player_data/%c/%s.games", STATS_DIR, newname[0], newname);
1402   sprintf(fname2, "%s/player_data/%c/.rem.%s.games", STATS_DIR, name[0], name);
1403   rename(fname2, fname);
1404   sprintf(fname, "%s/player_data/%c/%s.comments", STATS_DIR, newname[0], newname);
1405   sprintf(fname2, "%s/player_data/%c/.rem.%s.comments", STATS_DIR, name[0], name);
1406   rename(fname2, fname);
1407   sprintf(fname, "%s/player_data/%c/%s.logons", STATS_DIR, newname[0], newname);
1408   sprintf(fname2, "%s/player_data/%c/.rem.%s.logons", STATS_DIR, name[0], name);
1409   rename(fname2, fname);
1410   sprintf(fname, "%s/player_data/%c/%s.messages", STATS_DIR, newname[0], newname);
1411   sprintf(fname2, "%s/player_data/%c/.rem.%s.messages", STATS_DIR, name[0], name);
1412   rename(fname2, fname);
1413   return 0;
1414 }
1415
1416 int player_num_comments(int p)
1417 {
1418         struct player *pp = &player_globals.parray[p];
1419         char fname[MAX_FILENAME_SIZE];
1420
1421         if (!CheckPFlag(p, PFLAG_REG))
1422                 return 0;
1423         sprintf(fname, "%s/player_data/%c/%s.%s", STATS_DIR, pp->login[0],
1424                 pp->login, "comments");
1425         return lines_file(fname);
1426 }
1427
1428 int player_add_comment(int p_by, int p_to, char *comment)
1429 {
1430   char fname[MAX_FILENAME_SIZE];
1431   FILE *fp;
1432   time_t t = time(0);
1433
1434   if (!CheckPFlag(p_to, PFLAG_REG))
1435     return -1;
1436   sprintf(fname, "%s/player_data/%c/%s.%s", STATS_DIR, player_globals.parray[p_to].login[0],
1437           player_globals.parray[p_to].login, "comments");
1438   fp = fopen_s(fname, "a");
1439   if (!fp)
1440     return -1;
1441   fprintf(fp, "%s at %s: %s\n", player_globals.parray[p_by].name, strltime(&t), comment);
1442   fclose(fp);
1443   player_globals.parray[p_to].num_comments = player_num_comments(p_to);
1444   return 0;
1445 }
1446
1447 int player_show_comments(int p, int p1)
1448 {
1449   char fname[MAX_FILENAME_SIZE];
1450
1451   if (CheckPFlag(p1, PFLAG_REG)) {
1452     sprintf(fname, "%s/player_data/%c/%s.%s", STATS_DIR, player_globals.parray[p1].login[0],
1453           player_globals.parray[p1].login, "comments");
1454     if (psend_file(p, NULL, fname))
1455       pprintf(p, "There are no comments to show for %s.\n", player_globals.parray[p1].name);
1456     if (player_globals.parray[p1].passwd[0] == '*')
1457       pprintf(p, "%s's account is LOCKED.\n", player_globals.parray[p1].name);
1458     if (in_list(p1, L_BAN, player_globals.parray[p1].name))
1459       pprintf(p, "%s has been BANNED.\n", player_globals.parray[p1].name);
1460   } else
1461     pprintf(p, "Player \"%s\" is unregistered and cannot have comments.\n",
1462                player_globals.parray[p1].name);
1463   return 0;
1464 }
1465
1466 /* returns 1 if player is head admin, 0 otherwise */
1467 int player_ishead(int p)
1468 {
1469         struct player *pp = &player_globals.parray[p];
1470         return (strcasecmp(pp->name, config_get_tmp("HEAD_ADMIN")) == 0);
1471 }
1472
1473 /* GetRating chooses between blitz, standard and other ratings. */
1474 int GetRating(struct player *p, int gametype)
1475 {
1476     if (gametype == TYPE_BLITZ) return (p->b_stats.rating);
1477     else if (gametype == TYPE_STAND) return (p->s_stats.rating);
1478     else if (gametype == TYPE_WILD) return (p->w_stats.rating);
1479     else if (gametype == TYPE_LIGHT) return (p->l_stats.rating);
1480     else if (gametype == TYPE_BUGHOUSE) return (p->bug_stats.rating);
1481     else return 0;
1482 }    /* end of function GetRating. */
1483
1484 /* GetRD chooses between blitz, standard and other RD's. */
1485 double GetRD(struct player *p, int gametype)
1486 {
1487   struct statistics *s;
1488
1489   switch(gametype) {
1490     case TYPE_BLITZ:   s = &p->b_stats;   break;
1491     case TYPE_STAND:   s = &p->s_stats;   break;
1492     case TYPE_WILD:   s = &p->w_stats;   break;
1493     case TYPE_LIGHT:   s = &p->l_stats;   break;
1494     case TYPE_BUGHOUSE:   s = &p->bug_stats;   break;
1495     default:   return 0.0;
1496   }
1497   return (current_sterr(s->sterr, time(NULL)-(s->ltime)));
1498 }    /* end of function GetRD. */
1499