OSDN Git Service

update year to 2020
[jnethack/source.git] / src / topten.c
1 /* NetHack 3.6  topten.c        $NHDT-Date: 1450451497 2015/12/18 15:11:37 $  $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.44 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /*-Copyright (c) Robert Patrick Rankin, 2012. */
4 /* NetHack may be freely redistributed.  See license for details. */
5
6 /* JNetHack Copyright */
7 /* (c) Issei Numata, Naoki Hamada, Shigehiro Miyashita, 1994-2000  */
8 /* For 3.4-, Copyright (c) SHIRAKATA Kentaro, 2002-2020            */
9 /* JNetHack may be freely redistributed.  See license for details. */
10
11 #include "hack.h"
12 #include "dlb.h"
13 #ifdef SHORT_FILENAMES
14 #include "patchlev.h"
15 #else
16 #include "patchlevel.h"
17 #endif
18
19 #ifdef VMS
20 /* We don't want to rewrite the whole file, because that entails
21    creating a new version which requires that the old one be deletable. */
22 #define UPDATE_RECORD_IN_PLACE
23 #endif
24
25 /*
26  * Updating in place can leave junk at the end of the file in some
27  * circumstances (if it shrinks and the O.S. doesn't have a straightforward
28  * way to truncate it).  The trailing junk is harmless and the code
29  * which reads the scores will ignore it.
30  */
31 #ifdef UPDATE_RECORD_IN_PLACE
32 static long final_fpos;
33 #endif
34
35 #define done_stopprint program_state.stopprint
36
37 #define newttentry() (struct toptenentry *) alloc(sizeof (struct toptenentry))
38 #define dealloc_ttentry(ttent) free((genericptr_t) (ttent))
39 #ifndef NAMSZ
40 /* Changing NAMSZ can break your existing record/logfile */
41 #define NAMSZ 10
42 #endif
43 #define DTHSZ 100
44 #define ROLESZ 3
45
46 struct toptenentry {
47     struct toptenentry *tt_next;
48 #ifdef UPDATE_RECORD_IN_PLACE
49     long fpos;
50 #endif
51     long points;
52     int deathdnum, deathlev;
53     int maxlvl, hp, maxhp, deaths;
54     int ver_major, ver_minor, patchlevel;
55     long deathdate, birthdate;
56     int uid;
57     char plrole[ROLESZ + 1];
58     char plrace[ROLESZ + 1];
59     char plgend[ROLESZ + 1];
60     char plalign[ROLESZ + 1];
61     char name[NAMSZ + 1];
62     char death[DTHSZ + 1];
63 } * tt_head;
64 /* size big enough to read in all the string fields at once; includes
65    room for separating space or trailing newline plus string terminator */
66 #define SCANBUFSZ (4 * (ROLESZ + 1) + (NAMSZ + 1) + (DTHSZ + 1) + 1)
67
68 STATIC_DCL void FDECL(topten_print, (const char *));
69 STATIC_DCL void FDECL(topten_print_bold, (const char *));
70 STATIC_DCL void NDECL(outheader);
71 STATIC_DCL void FDECL(outentry, (int, struct toptenentry *, BOOLEAN_P));
72 STATIC_DCL void FDECL(discardexcess, (FILE *));
73 STATIC_DCL void FDECL(readentry, (FILE *, struct toptenentry *));
74 STATIC_DCL void FDECL(writeentry, (FILE *, struct toptenentry *));
75 #ifdef XLOGFILE
76 STATIC_DCL void FDECL(writexlentry, (FILE *, struct toptenentry *, int));
77 STATIC_DCL long NDECL(encodexlogflags);
78 STATIC_DCL long NDECL(encodeconduct);
79 STATIC_DCL long NDECL(encodeachieve);
80 #endif
81 STATIC_DCL void FDECL(free_ttlist, (struct toptenentry *));
82 STATIC_DCL int FDECL(classmon, (char *, BOOLEAN_P));
83 STATIC_DCL int FDECL(score_wanted, (BOOLEAN_P, int, struct toptenentry *, int,
84                                     const char **, int));
85 #ifdef NO_SCAN_BRACK
86 STATIC_DCL void FDECL(nsb_mung_line, (char *));
87 STATIC_DCL void FDECL(nsb_unmung_line, (char *));
88 #endif
89
90 static winid toptenwin = WIN_ERR;
91
92 /* "killed by",&c ["an"] 'killer.name' */
93 void
94 formatkiller(buf, siz, how, incl_helpless)
95 char *buf;
96 unsigned siz;
97 int how;
98 boolean incl_helpless;
99 {
100     static NEARDATA const char *const killed_by_prefix[] = {
101         /* DIED, CHOKING, POISONING, STARVING, */
102 /*JP
103         "killed by ", "choked on ", "poisoned by ", "died of ",
104 */
105         "\8e\80\82ñ\82¾", "\82Å\92\82\91§\82µ\82½", "\82Ì\93Å\82Å\8e\80\82ñ\82¾", "",
106         /* DROWNING, BURNING, DISSOLVED, CRUSHING, */
107 /*JP
108         "drowned in ", "burned by ", "dissolved in ", "crushed to death by ",
109 */
110         "\93M\8e\80\82µ\82½","\8fÄ\8e\80\82µ\82½", "\97n\8aâ\82É\97n\82¯\82½", "\89\9f\82µ\92×\82³\82ê\82½",
111         /* STONING, TURNED_SLIME, GENOCIDED, */
112 /*JP
113         "petrified by ", "turned to slime by ", "killed by ",
114 */
115         "\90Î\82É\82È\82Á\82½", "\82É\83X\83\89\83C\83\80\82É\82³\82ê\82½", "\8bs\8eE\82³\82ê\82½",
116         /* PANICKED, TRICKED, QUIT, ESCAPED, ASCENDED */
117         "", "", "", "", ""
118     };
119 #if 0 /*JP*/
120     unsigned l;
121     char c, *kname = killer.name;
122 #else
123     char *kname = killer.name;
124 #endif
125
126     buf[0] = '\0'; /* lint suppression */
127 #if 1 /*JP*//*\90æ\82É\91Î\8fÛ\82ð\83R\83s\81[*/
128     strncat(buf, kname, siz - 1);
129     siz -= strlen(buf);
130 #endif
131     switch (killer.format) {
132     default:
133         impossible("bad killer format? (%d)", killer.format);
134         /*FALLTHRU*/
135     case NO_KILLER_PREFIX:
136         break;
137     case KILLED_BY_AN:
138 #if 0 /*JP*//*\93ú\96{\8cê\82Å\82Í\95s\97v*/
139         kname = an(kname);
140 #endif
141         /*FALLTHRU*/
142     case KILLED_BY:
143 #if 0 /*JP*/
144         (void) strncat(buf, killed_by_prefix[how], siz - 1);
145         l = strlen(buf);
146         buf += l, siz -= l;
147 #else /*JP:\8aù\82É\91Î\8fÛ\82ð\83R\83s\81[\82µ\82Ä\82¢\82é\82Ì\82Å\92P\82É\92Ç\89Á*/
148         (void) strncat(buf, killed_by_prefix[how], siz - 1);
149 #endif
150         break;
151 #if 1 /*JP*/
152       case KILLED_SUFFIX:
153         (void) strncat(buf, "\82É\8eE\82³\82ê\82½", siz - 1);
154 #endif
155     }
156 #if 0 /*JP*//*\8aù\82É\83R\83s\81[\8dÏ\82Ý*//*JP:TODO:\83T\83j\83^\83C\83Y\82ª\95K\97v*/
157     /* Copy kname into buf[].
158      * Object names and named fruit have already been sanitized, but
159      * monsters can have "called 'arbitrary text'" attached to them,
160      * so make sure that that text can't confuse field splitting when
161      * record, logfile, or xlogfile is re-read at some later point.
162      */
163     while (--siz > 0) {
164         c = *kname++;
165         if (!c)
166             break;
167         else if (c == ',')
168             c = ';';
169         /* 'xlogfile' doesn't really need protection for '=', but
170            fixrecord.awk for corrupted 3.6.0 'record' does (only
171            if using xlogfile rather than logfile to repair record) */
172         else if (c == '=')
173             c = '_';
174         /* tab is not possible due to use of mungspaces() when naming;
175            it would disrupt xlogfile parsing if it were present */
176         else if (c == '\t')
177             c = ' ';
178         *buf++ = c;
179     }
180     *buf = '\0';
181 #endif
182
183 #if 0 /*JP*//*JP:TODO:\8e\80\88ö\82Ì\91O\82É\8e\9d\82Á\82Ä\8ds\82©\82È\82¯\82ê\82Î\82È\82ç\82È\82¢*/
184     if (incl_helpless && multi) {
185         /* X <= siz: 'sizeof "string"' includes 1 for '\0' terminator */
186         if (multi_reason && strlen(multi_reason) + sizeof ", while " <= siz)
187             Sprintf(buf, ", while %s", multi_reason);
188         /* either multi_reason wasn't specified or wouldn't fit */
189         else if (sizeof ", while helpless" <= siz)
190             Strcpy(buf, ", while helpless");
191         /* else extra death info won't fit, so leave it out */
192     }
193 #endif
194 }
195
196 STATIC_OVL void
197 topten_print(x)
198 const char *x;
199 {
200     if (toptenwin == WIN_ERR)
201         raw_print(x);
202     else
203         putstr(toptenwin, ATR_NONE, x);
204 }
205
206 STATIC_OVL void
207 topten_print_bold(x)
208 const char *x;
209 {
210     if (toptenwin == WIN_ERR)
211         raw_print_bold(x);
212     else
213         putstr(toptenwin, ATR_BOLD, x);
214 }
215
216 int
217 observable_depth(lev)
218 d_level *lev;
219 {
220 #if 0
221     /* if we ever randomize the order of the elemental planes, we
222        must use a constant external representation in the record file */
223     if (In_endgame(lev)) {
224         if (Is_astralevel(lev))
225             return -5;
226         else if (Is_waterlevel(lev))
227             return -4;
228         else if (Is_firelevel(lev))
229             return -3;
230         else if (Is_airlevel(lev))
231             return -2;
232         else if (Is_earthlevel(lev))
233             return -1;
234         else
235             return 0; /* ? */
236     } else
237 #endif
238     return depth(lev);
239 }
240
241 /* throw away characters until current record has been entirely consumed */
242 STATIC_OVL void
243 discardexcess(rfile)
244 FILE *rfile;
245 {
246     int c;
247
248     do {
249         c = fgetc(rfile);
250     } while (c != '\n' && c != EOF);
251 }
252
253 STATIC_OVL void
254 readentry(rfile, tt)
255 FILE *rfile;
256 struct toptenentry *tt;
257 {
258     char inbuf[SCANBUFSZ], s1[SCANBUFSZ], s2[SCANBUFSZ], s3[SCANBUFSZ],
259         s4[SCANBUFSZ], s5[SCANBUFSZ], s6[SCANBUFSZ];
260
261 #ifdef NO_SCAN_BRACK /* Version_ Pts DgnLevs_ Hp___ Died__Born id */
262     static const char fmt[] = "%d %d %d %ld %d %d %d %d %d %d %ld %ld %d%*c";
263     static const char fmt32[] = "%c%c %s %s%*c";
264     static const char fmt33[] = "%s %s %s %s %s %s%*c";
265 #else
266     static const char fmt[] = "%d.%d.%d %ld %d %d %d %d %d %d %ld %ld %d ";
267     static const char fmt32[] = "%c%c %[^,],%[^\n]%*c";
268     static const char fmt33[] = "%s %s %s %s %[^,],%[^\n]%*c";
269 #endif
270
271 #ifdef UPDATE_RECORD_IN_PLACE
272     /* note: input below must read the record's terminating newline */
273     final_fpos = tt->fpos = ftell(rfile);
274 #endif
275 #define TTFIELDS 13
276     if (fscanf(rfile, fmt, &tt->ver_major, &tt->ver_minor, &tt->patchlevel,
277                &tt->points, &tt->deathdnum, &tt->deathlev, &tt->maxlvl,
278                &tt->hp, &tt->maxhp, &tt->deaths, &tt->deathdate,
279                &tt->birthdate, &tt->uid) != TTFIELDS) {
280 #undef TTFIELDS
281         tt->points = 0;
282         discardexcess(rfile);
283     } else {
284         /* load remainder of record into a local buffer;
285            this imposes an implicit length limit of SCANBUFSZ
286            on every string field extracted from the buffer */
287         if (!fgets(inbuf, sizeof inbuf, rfile)) {
288             /* sscanf will fail and tt->points will be set to 0 */
289             *inbuf = '\0';
290         } else if (!index(inbuf, '\n')) {
291             Strcpy(&inbuf[sizeof inbuf - 2], "\n");
292             discardexcess(rfile);
293         }
294         /* Check for backwards compatibility */
295         if (tt->ver_major < 3 || (tt->ver_major == 3 && tt->ver_minor < 3)) {
296             int i;
297
298             if (sscanf(inbuf, fmt32, tt->plrole, tt->plgend, s1, s2) == 4) {
299                 tt->plrole[1] = tt->plgend[1] = '\0'; /* read via %c */
300                 copynchars(tt->name, s1, (int) (sizeof tt->name) - 1);
301                 copynchars(tt->death, s2, (int) (sizeof tt->death) - 1);
302             } else
303                 tt->points = 0;
304             tt->plrole[1] = '\0';
305             if ((i = str2role(tt->plrole)) >= 0)
306                 Strcpy(tt->plrole, roles[i].filecode);
307             Strcpy(tt->plrace, "?");
308             Strcpy(tt->plgend, (tt->plgend[0] == 'M') ? "Mal" : "Fem");
309             Strcpy(tt->plalign, "?");
310         } else if (sscanf(inbuf, fmt33, s1, s2, s3, s4, s5, s6) == 6) {
311             copynchars(tt->plrole, s1, (int) (sizeof tt->plrole) - 1);
312             copynchars(tt->plrace, s2, (int) (sizeof tt->plrace) - 1);
313             copynchars(tt->plgend, s3, (int) (sizeof tt->plgend) - 1);
314             copynchars(tt->plalign, s4, (int) (sizeof tt->plalign) - 1);
315             copynchars(tt->name, s5, (int) (sizeof tt->name) - 1);
316             copynchars(tt->death, s6, (int) (sizeof tt->death) - 1);
317         } else
318             tt->points = 0;
319 #ifdef NO_SCAN_BRACK
320         if (tt->points > 0) {
321             nsb_unmung_line(tt->name);
322             nsb_unmung_line(tt->death);
323         }
324 #endif
325     }
326
327     /* check old score entries for Y2K problem and fix whenever found */
328     if (tt->points > 0) {
329         if (tt->birthdate < 19000000L)
330             tt->birthdate += 19000000L;
331         if (tt->deathdate < 19000000L)
332             tt->deathdate += 19000000L;
333     }
334 }
335
336 STATIC_OVL void
337 writeentry(rfile, tt)
338 FILE *rfile;
339 struct toptenentry *tt;
340 {
341     static const char fmt32[] = "%c%c ";        /* role,gender */
342     static const char fmt33[] = "%s %s %s %s "; /* role,race,gndr,algn */
343 #ifndef NO_SCAN_BRACK
344     static const char fmt0[] = "%d.%d.%d %ld %d %d %d %d %d %d %ld %ld %d ";
345     static const char fmtX[] = "%s,%s\n";
346 #else /* NO_SCAN_BRACK */
347     static const char fmt0[] = "%d %d %d %ld %d %d %d %d %d %d %ld %ld %d ";
348     static const char fmtX[] = "%s %s\n";
349
350     nsb_mung_line(tt->name);
351     nsb_mung_line(tt->death);
352 #endif
353
354     (void) fprintf(rfile, fmt0, tt->ver_major, tt->ver_minor, tt->patchlevel,
355                    tt->points, tt->deathdnum, tt->deathlev, tt->maxlvl,
356                    tt->hp, tt->maxhp, tt->deaths, tt->deathdate,
357                    tt->birthdate, tt->uid);
358     if (tt->ver_major < 3 || (tt->ver_major == 3 && tt->ver_minor < 3))
359         (void) fprintf(rfile, fmt32, tt->plrole[0], tt->plgend[0]);
360     else
361         (void) fprintf(rfile, fmt33, tt->plrole, tt->plrace, tt->plgend,
362                        tt->plalign);
363     (void) fprintf(rfile, fmtX, onlyspace(tt->name) ? "_" : tt->name,
364                    tt->death);
365
366 #ifdef NO_SCAN_BRACK
367     nsb_unmung_line(tt->name);
368     nsb_unmung_line(tt->death);
369 #endif
370 }
371
372 #ifdef XLOGFILE
373
374 /* as tab is never used in eg. plname or death, no need to mangle those. */
375 STATIC_OVL void
376 writexlentry(rfile, tt, how)
377 FILE *rfile;
378 struct toptenentry *tt;
379 int how;
380 {
381 #define Fprintf (void) fprintf
382 #define XLOG_SEP '\t' /* xlogfile field separator. */
383     char buf[BUFSZ], tmpbuf[DTHSZ + 1];
384
385     Sprintf(buf, "version=%d.%d.%d", tt->ver_major, tt->ver_minor,
386             tt->patchlevel);
387     Sprintf(eos(buf), "%cpoints=%ld%cdeathdnum=%d%cdeathlev=%d", XLOG_SEP,
388             tt->points, XLOG_SEP, tt->deathdnum, XLOG_SEP, tt->deathlev);
389     Sprintf(eos(buf), "%cmaxlvl=%d%chp=%d%cmaxhp=%d", XLOG_SEP, tt->maxlvl,
390             XLOG_SEP, tt->hp, XLOG_SEP, tt->maxhp);
391     Sprintf(eos(buf), "%cdeaths=%d%cdeathdate=%ld%cbirthdate=%ld%cuid=%d",
392             XLOG_SEP, tt->deaths, XLOG_SEP, tt->deathdate, XLOG_SEP,
393             tt->birthdate, XLOG_SEP, tt->uid);
394     Fprintf(rfile, "%s", buf);
395     Sprintf(buf, "%crole=%s%crace=%s%cgender=%s%calign=%s", XLOG_SEP,
396             tt->plrole, XLOG_SEP, tt->plrace, XLOG_SEP, tt->plgend, XLOG_SEP,
397             tt->plalign);
398     /* make a copy of death reason that doesn't include ", while helpless" */
399     formatkiller(tmpbuf, sizeof tmpbuf, how, FALSE);
400     Fprintf(rfile, "%s%cname=%s%cdeath=%s",
401             buf, /* (already includes separator) */
402             XLOG_SEP, plname, XLOG_SEP, tmpbuf);
403     if (multi)
404 #if 0 /*JP:T*/
405         Fprintf(rfile, "%cwhile=%s", XLOG_SEP,
406                 multi_reason ? multi_reason : "helpless");
407 #else
408         Fprintf(rfile, "%cwhile=%s", XLOG_SEP,
409                 multi_reason ? multi_reason : "\96³\97Í\82È\8aÔ\82É");
410 #endif
411     Fprintf(rfile, "%cconduct=0x%lx%cturns=%ld%cachieve=0x%lx", XLOG_SEP,
412             encodeconduct(), XLOG_SEP, moves, XLOG_SEP, encodeachieve());
413     Fprintf(rfile, "%crealtime=%ld%cstarttime=%ld%cendtime=%ld", XLOG_SEP,
414             (long) urealtime.realtime, XLOG_SEP,
415             (long) ubirthday, XLOG_SEP, (long) urealtime.finish_time);
416     Fprintf(rfile, "%cgender0=%s%calign0=%s", XLOG_SEP,
417             genders[flags.initgend].filecode, XLOG_SEP,
418             aligns[1 - u.ualignbase[A_ORIGINAL]].filecode);
419     Fprintf(rfile, "%cflags=0x%lx", XLOG_SEP, encodexlogflags());
420     Fprintf(rfile, "\n");
421 #undef XLOG_SEP
422 }
423
424 STATIC_OVL long
425 encodexlogflags()
426 {
427     long e = 0L;
428
429     if (wizard)
430         e |= 1L << 0;
431     if (discover)
432         e |= 1L << 1;
433     if (!u.uroleplay.numbones)
434         e |= 1L << 2;
435
436     return e;
437 }
438
439 STATIC_OVL long
440 encodeconduct()
441 {
442     long e = 0L;
443
444     if (!u.uconduct.food)
445         e |= 1L << 0;
446     if (!u.uconduct.unvegan)
447         e |= 1L << 1;
448     if (!u.uconduct.unvegetarian)
449         e |= 1L << 2;
450     if (!u.uconduct.gnostic)
451         e |= 1L << 3;
452     if (!u.uconduct.weaphit)
453         e |= 1L << 4;
454     if (!u.uconduct.killer)
455         e |= 1L << 5;
456     if (!u.uconduct.literate)
457         e |= 1L << 6;
458     if (!u.uconduct.polypiles)
459         e |= 1L << 7;
460     if (!u.uconduct.polyselfs)
461         e |= 1L << 8;
462     if (!u.uconduct.wishes)
463         e |= 1L << 9;
464     if (!u.uconduct.wisharti)
465         e |= 1L << 10;
466     if (!num_genocides())
467         e |= 1L << 11;
468
469     return e;
470 }
471
472 STATIC_OVL long
473 encodeachieve()
474 {
475     long r = 0L;
476
477     if (u.uachieve.bell)
478         r |= 1L << 0;
479     if (u.uachieve.enter_gehennom)
480         r |= 1L << 1;
481     if (u.uachieve.menorah)
482         r |= 1L << 2;
483     if (u.uachieve.book)
484         r |= 1L << 3;
485     if (u.uevent.invoked)
486         r |= 1L << 4;
487     if (u.uachieve.amulet)
488         r |= 1L << 5;
489     if (In_endgame(&u.uz))
490         r |= 1L << 6;
491     if (Is_astralevel(&u.uz))
492         r |= 1L << 7;
493     if (u.uachieve.ascended)
494         r |= 1L << 8;
495     if (u.uachieve.mines_luckstone)
496         r |= 1L << 9;
497     if (u.uachieve.finish_sokoban)
498         r |= 1L << 10;
499     if (u.uachieve.killed_medusa)
500         r |= 1L << 11;
501     if (u.uroleplay.blind)
502         r |= 1L << 12;
503     if (u.uroleplay.nudist)
504         r |= 1L << 13;
505
506     return r;
507 }
508
509 #endif /* XLOGFILE */
510
511 STATIC_OVL void
512 free_ttlist(tt)
513 struct toptenentry *tt;
514 {
515     struct toptenentry *ttnext;
516
517     while (tt->points > 0) {
518         ttnext = tt->tt_next;
519         dealloc_ttentry(tt);
520         tt = ttnext;
521     }
522     dealloc_ttentry(tt);
523 }
524
525 void
526 topten(how, when)
527 int how;
528 time_t when;
529 {
530     int uid = getuid();
531     int rank, rank0 = -1, rank1 = 0;
532     int occ_cnt = sysopt.persmax;
533     register struct toptenentry *t0, *tprev;
534     struct toptenentry *t1;
535     FILE *rfile;
536     register int flg = 0;
537     boolean t0_used;
538 #ifdef LOGFILE
539     FILE *lfile;
540 #endif /* LOGFILE */
541 #ifdef XLOGFILE
542     FILE *xlfile;
543 #endif /* XLOGFILE */
544
545 #ifdef _DCC
546     /* Under DICE 3.0, this crashes the system consistently, apparently due to
547      * corruption of *rfile somewhere.  Until I figure this out, just cut out
548      * topten support entirely - at least then the game exits cleanly.  --AC
549      */
550     return;
551 #endif
552
553     /* If we are in the midst of a panic, cut out topten entirely.
554      * topten uses alloc() several times, which will lead to
555      * problems if the panic was the result of an alloc() failure.
556      */
557     if (program_state.panicking)
558         return;
559
560     if (iflags.toptenwin) {
561         toptenwin = create_nhwindow(NHW_TEXT);
562     }
563
564 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
565 #define HUP if (!program_state.done_hup)
566 #else
567 #define HUP
568 #endif
569
570 #ifdef TOS
571     restore_colors(); /* make sure the screen is black on white */
572 #endif
573     /* create a new 'topten' entry */
574     t0_used = FALSE;
575     t0 = newttentry();
576     t0->ver_major = VERSION_MAJOR;
577     t0->ver_minor = VERSION_MINOR;
578     t0->patchlevel = PATCHLEVEL;
579     t0->points = u.urexp;
580     t0->deathdnum = u.uz.dnum;
581     /* deepest_lev_reached() is in terms of depth(), and reporting the
582      * deepest level reached in the dungeon death occurred in doesn't
583      * seem right, so we have to report the death level in depth() terms
584      * as well (which also seems reasonable since that's all the player
585      * sees on the screen anyway)
586      */
587     t0->deathlev = observable_depth(&u.uz);
588     t0->maxlvl = deepest_lev_reached(TRUE);
589     t0->hp = u.uhp;
590     t0->maxhp = u.uhpmax;
591     t0->deaths = u.umortality;
592     t0->uid = uid;
593     copynchars(t0->plrole, urole.filecode, ROLESZ);
594     copynchars(t0->plrace, urace.filecode, ROLESZ);
595     copynchars(t0->plgend, genders[flags.female].filecode, ROLESZ);
596     copynchars(t0->plalign, aligns[1 - u.ualign.type].filecode, ROLESZ);
597     copynchars(t0->name, plname, NAMSZ);
598     formatkiller(t0->death, sizeof t0->death, how, TRUE);
599     t0->birthdate = yyyymmdd(ubirthday);
600     t0->deathdate = yyyymmdd(when);
601     t0->tt_next = 0;
602 #ifdef UPDATE_RECORD_IN_PLACE
603     t0->fpos = -1L;
604 #endif
605
606 #ifdef LOGFILE /* used for debugging (who dies of what, where) */
607     if (lock_file(LOGFILE, SCOREPREFIX, 10)) {
608         if (!(lfile = fopen_datafile(LOGFILE, "a", SCOREPREFIX))) {
609             HUP raw_print("Cannot open log file!");
610         } else {
611             writeentry(lfile, t0);
612             (void) fclose(lfile);
613         }
614         unlock_file(LOGFILE);
615     }
616 #endif /* LOGFILE */
617 #ifdef XLOGFILE
618     if (lock_file(XLOGFILE, SCOREPREFIX, 10)) {
619         if (!(xlfile = fopen_datafile(XLOGFILE, "a", SCOREPREFIX))) {
620             HUP raw_print("Cannot open extended log file!");
621         } else {
622             writexlentry(xlfile, t0, how);
623             (void) fclose(xlfile);
624         }
625         unlock_file(XLOGFILE);
626     }
627 #endif /* XLOGFILE */
628
629     if (wizard || discover) {
630         if (how != PANICKED)
631             HUP {
632                 char pbuf[BUFSZ];
633
634                 topten_print("");
635 #if 0 /*JP:T*/
636                 Sprintf(pbuf,
637              "Since you were in %s mode, the score list will not be checked.",
638                         wizard ? "wizard" : "discover");
639 #else
640                 Sprintf(pbuf,
641              "%s\83\82\81[\83h\82Å\83v\83\8c\83C\82µ\82½\82Ì\82Å\83X\83R\83A\83\8a\83X\83g\82É\82Í\8dÚ\82ç\82È\82¢\81D",
642                         wizard ? "\83E\83B\83U\81[\83h" : "\94­\8c©");
643 #endif
644                 topten_print(pbuf);
645             }
646         goto showwin;
647     }
648
649     if (!lock_file(RECORD, SCOREPREFIX, 60))
650         goto destroywin;
651
652 #ifdef UPDATE_RECORD_IN_PLACE
653     rfile = fopen_datafile(RECORD, "r+", SCOREPREFIX);
654 #else
655     rfile = fopen_datafile(RECORD, "r", SCOREPREFIX);
656 #endif
657
658     if (!rfile) {
659         HUP raw_print("Cannot open record file!");
660         unlock_file(RECORD);
661         goto destroywin;
662     }
663
664     HUP topten_print("");
665
666     /* assure minimum number of points */
667     if (t0->points < sysopt.pointsmin)
668         t0->points = 0;
669
670     t1 = tt_head = newttentry();
671     tprev = 0;
672     /* rank0: -1 undefined, 0 not_on_list, n n_th on list */
673     for (rank = 1;;) {
674         readentry(rfile, t1);
675         if (t1->points < sysopt.pointsmin)
676             t1->points = 0;
677         if (rank0 < 0 && t1->points < t0->points) {
678             rank0 = rank++;
679             if (tprev == 0)
680                 tt_head = t0;
681             else
682                 tprev->tt_next = t0;
683             t0->tt_next = t1;
684 #ifdef UPDATE_RECORD_IN_PLACE
685             t0->fpos = t1->fpos; /* insert here */
686 #endif
687             t0_used = TRUE;
688             occ_cnt--;
689             flg++; /* ask for a rewrite */
690         } else
691             tprev = t1;
692
693         if (t1->points == 0)
694             break;
695         if ((sysopt.pers_is_uid ? t1->uid == t0->uid
696                                 : strncmp(t1->name, t0->name, NAMSZ) == 0)
697             && !strncmp(t1->plrole, t0->plrole, ROLESZ) && --occ_cnt <= 0) {
698             if (rank0 < 0) {
699                 rank0 = 0;
700                 rank1 = rank;
701                 HUP {
702                     char pbuf[BUFSZ];
703
704                     Sprintf(pbuf,
705 /*JP
706                         "You didn't beat your previous score of %ld points.",
707 */
708                         "\82 \82È\82½\82Í\88È\91O\82Ì%ld\83|\83C\83\93\83g\82Ì\83X\83R\83A\82É\93Í\82©\82È\82©\82Á\82½\81D",
709                             t1->points);
710                     topten_print(pbuf);
711                     topten_print("");
712                 }
713             }
714             if (occ_cnt < 0) {
715                 flg++;
716                 continue;
717             }
718         }
719         if (rank <= sysopt.entrymax) {
720             t1->tt_next = newttentry();
721             t1 = t1->tt_next;
722             rank++;
723         }
724         if (rank > sysopt.entrymax) {
725             t1->points = 0;
726             break;
727         }
728     }
729     if (flg) { /* rewrite record file */
730 #ifdef UPDATE_RECORD_IN_PLACE
731         (void) fseek(rfile, (t0->fpos >= 0 ? t0->fpos : final_fpos),
732                      SEEK_SET);
733 #else
734         (void) fclose(rfile);
735         if (!(rfile = fopen_datafile(RECORD, "w", SCOREPREFIX))) {
736             HUP raw_print("Cannot write record file");
737             unlock_file(RECORD);
738             free_ttlist(tt_head);
739             goto destroywin;
740         }
741 #endif /* UPDATE_RECORD_IN_PLACE */
742         if (!done_stopprint)
743             if (rank0 > 0) {
744                 if (rank0 <= 10) {
745 /*JP
746                     topten_print("You made the top ten list!");
747 */
748                     topten_print("\82 \82È\82½\82Í\83g\83b\83v10\83\8a\83X\83g\82É\8dÚ\82Á\82½\81I");
749                 } else {
750                     char pbuf[BUFSZ];
751
752 #if 0 /*JP:T*/
753                     Sprintf(pbuf,
754                             "You reached the %d%s place on the top %d list.",
755                             rank0, ordin(rank0), sysopt.entrymax);
756 #else
757                     Sprintf(pbuf,
758                             "\82 \82È\82½\82Í\81C\83g\83b\83v%d\83\8a\83X\83g\82Ì%d\88Ê\82É\8dÚ\82Á\82½\81D",
759                             sysopt.entrymax, rank0);
760 #endif
761                     topten_print(pbuf);
762                 }
763                 topten_print("");
764             }
765     }
766     if (rank0 == 0)
767         rank0 = rank1;
768     if (rank0 <= 0)
769         rank0 = rank;
770     if (!done_stopprint)
771         outheader();
772     t1 = tt_head;
773     for (rank = 1; t1->points != 0; rank++, t1 = t1->tt_next) {
774         if (flg
775 #ifdef UPDATE_RECORD_IN_PLACE
776             && rank >= rank0
777 #endif
778             )
779             writeentry(rfile, t1);
780         if (done_stopprint)
781             continue;
782         if (rank > flags.end_top && (rank < rank0 - flags.end_around
783                                      || rank > rank0 + flags.end_around)
784             && (!flags.end_own
785                 || (sysopt.pers_is_uid
786                         ? t1->uid == t0->uid
787                         : strncmp(t1->name, t0->name, NAMSZ) == 0)))
788             continue;
789         if (rank == rank0 - flags.end_around
790             && rank0 > flags.end_top + flags.end_around + 1 && !flags.end_own)
791             topten_print("");
792         if (rank != rank0)
793             outentry(rank, t1, FALSE);
794         else if (!rank1)
795             outentry(rank, t1, TRUE);
796         else {
797             outentry(rank, t1, TRUE);
798             outentry(0, t0, TRUE);
799         }
800     }
801     if (rank0 >= rank)
802         if (!done_stopprint)
803             outentry(0, t0, TRUE);
804 #ifdef UPDATE_RECORD_IN_PLACE
805     if (flg) {
806 #ifdef TRUNCATE_FILE
807         /* if a reasonable way to truncate a file exists, use it */
808         truncate_file(rfile);
809 #else
810         /* use sentinel record rather than relying on truncation */
811         t1->points = 0L; /* terminates file when read back in */
812         t1->ver_major = t1->ver_minor = t1->patchlevel = 0;
813         t1->uid = t1->deathdnum = t1->deathlev = 0;
814         t1->maxlvl = t1->hp = t1->maxhp = t1->deaths = 0;
815         t1->plrole[0] = t1->plrace[0] = t1->plgend[0] = t1->plalign[0] = '-';
816         t1->plrole[1] = t1->plrace[1] = t1->plgend[1] = t1->plalign[1] = 0;
817         t1->birthdate = t1->deathdate = yyyymmdd((time_t) 0L);
818         Strcpy(t1->name, "@");
819         Strcpy(t1->death, "<eod>\n");
820         writeentry(rfile, t1);
821         (void) fflush(rfile);
822 #endif /* TRUNCATE_FILE */
823     }
824 #endif /* UPDATE_RECORD_IN_PLACE */
825     (void) fclose(rfile);
826     unlock_file(RECORD);
827     free_ttlist(tt_head);
828
829 showwin:
830     if (iflags.toptenwin && !done_stopprint)
831         display_nhwindow(toptenwin, 1);
832 destroywin:
833     if (!t0_used)
834         dealloc_ttentry(t0);
835     if (iflags.toptenwin) {
836         destroy_nhwindow(toptenwin);
837         toptenwin = WIN_ERR;
838     }
839 }
840
841 STATIC_OVL void
842 outheader()
843 {
844     char linebuf[BUFSZ];
845     register char *bp;
846
847     Strcpy(linebuf, " No  Points     Name");
848     bp = eos(linebuf);
849     while (bp < linebuf + COLNO - 9)
850         *bp++ = ' ';
851     Strcpy(bp, "Hp [max]");
852     topten_print(linebuf);
853 }
854
855 /* so>0: standout line; so=0: ordinary line */
856 STATIC_OVL void
857 outentry(rank, t1, so)
858 struct toptenentry *t1;
859 int rank;
860 boolean so;
861 {
862     boolean second_line = TRUE;
863     char linebuf[BUFSZ];
864 #if 0 /*JP*/
865     char *bp, hpbuf[24], linebuf3[BUFSZ];
866 #else
867     char *bp, hpbuf[24];
868 #endif
869     int hppos, lngr;
870 #if 1 /*JP*/
871     char who[BUFSZ];
872     char where[BUFSZ];
873     char action[BUFSZ];
874     char car[BUFSZ];
875     char cdr[BUFSZ];
876     const char *jdeath;
877 #endif
878
879 #if 1 /*JP*/
880     who[0] = '\0';
881     where[0] = '\0';
882     action[0] = '\0';
883 #endif
884     linebuf[0] = '\0';
885     if (rank)
886         Sprintf(eos(linebuf), "%3d", rank);
887     else
888         Strcat(linebuf, "   ");
889
890     Sprintf(eos(linebuf), " %10ld  %.10s", t1->points ? t1->points : u.urexp,
891             t1->name);
892     Sprintf(eos(linebuf), "-%s", t1->plrole);
893     if (t1->plrace[0] != '?')
894         Sprintf(eos(linebuf), "-%s", t1->plrace);
895     /* Printing of gender and alignment is intentional.  It has been
896      * part of the NetHack Geek Code, and illustrates a proper way to
897      * specify a character from the command line.
898      */
899     Sprintf(eos(linebuf), "-%s", t1->plgend);
900     if (t1->plalign[0] != '?')
901 /*JP
902         Sprintf(eos(linebuf), "-%s ", t1->plalign);
903 */
904         Sprintf(eos(linebuf), "-%s", t1->plalign);
905     else
906 /*JP
907         Strcat(linebuf, " ");
908 */
909         Strcat(linebuf, "");
910 #if 1 /*JP*/
911         Strcat(linebuf, "\82Í");
912 /*JP: \93ú\96{\8cê\82Å\82Í\81u\81\9b\81\9b\82ð\8eè\82É\81v\82ð\90æ\82É\92Ç\89Á\82µ\82È\82¢\82Æ\95s\8e©\91R */
913         jdeath = t1->death;
914         if (!strncmp(jdeath, "\96\82\8f\9c\82¯\82ð\8eè\82É", 12))
915             jdeath += 12;
916         else if (!strncmp(jdeath, "\93V\8fã\82Å\92p\90J\82ð\8eó\82¯", 16))
917             jdeath += 16;
918         else if (!strncmp(jdeath, "\8bU\95¨\82Ì\96\82\8f\9c\82¯\82ð\92Í\82Ü\82³\82ê", 24))
919             jdeath += 24;
920 #endif
921 #if 0 /*JP*/
922     if (!strncmp("escaped", t1->death, 7)) {
923 #else
924     if (!strncmp("\92E\8fo\82µ\82½", jdeath, 8)
925         || !strncmp("escaped", jdeath, 7)) {
926 #endif
927 #if 0 /*JP*/
928         Sprintf(eos(linebuf), "escaped the dungeon %s[max level %d]",
929                 !strncmp(" (", t1->death + 7, 2) ? t1->death + 7 + 2 : "",
930                 t1->maxlvl);
931         /* fixup for closing paren in "escaped... with...Amulet)[max..." */
932         if ((bp = index(linebuf, ')')) != 0)
933             *bp = (t1->deathdnum == astral_level.dnum) ? '\0' : ' ';
934 #else
935         char jbuf[BUFSZ];
936         strncpy(jbuf, t1->death, jdeath - t1->death);
937         jbuf[jdeath - t1->death] = '\0';
938         Sprintf(action, "%s\96À\8b{\82©\82ç\92E\8fo\82µ\82½[\8dÅ\91å\92n\89º%d\8aK]",
939                 jbuf, t1->maxlvl);
940 #endif
941         second_line = FALSE;
942 #if 0 /*JP*/
943     } else if (!strncmp("ascended", t1->death, 8)) {
944 #else
945     } else if (!strncmp("\8f¸\93V\82µ\82½", jdeath, 8)
946                || !strncmp("ascended", jdeath, 8)) {
947 #endif
948 #if 0 /*JP:T*/
949         Sprintf(eos(linebuf), "ascended to demigod%s-hood",
950                 (t1->plgend[0] == 'F') ? "dess" : "");
951 #else
952         Sprintf(action, "\8f¸\93V\82µ%s\90_\82Æ\82È\82Á\82½",
953                 (t1->plgend[0] == 'F') ? "\8f\97" : "");
954 #endif
955         second_line = FALSE;
956     } else {
957 /*JP
958         if (!strncmp(t1->death, "quit", 4)) {
959 */
960         if (!strncmp(jdeath, "\94²\82¯\82½", 4)) {
961 #if 0 /*JP*/
962             Strcat(linebuf, "quit");
963 #else
964             Strcat(action, t1->death);
965 #endif
966             second_line = FALSE;
967 #if 0 /*JP*/
968         } else if (!strncmp(t1->death, "died of st", 10)) {
969             Strcat(linebuf, "starved to death");
970             second_line = FALSE;
971         } else if (!strncmp(t1->death, "choked", 6)) {
972             Sprintf(eos(linebuf), "choked on h%s food",
973                     (t1->plgend[0] == 'F') ? "er" : "is");
974         } else if (!strncmp(t1->death, "poisoned", 8)) {
975             Strcat(linebuf, "was poisoned");
976         } else if (!strncmp(t1->death, "crushed", 7)) {
977             Strcat(linebuf, "was crushed to death");
978         } else if (!strncmp(t1->death, "petrified by ", 13)) {
979             Strcat(linebuf, "turned to stone");
980         } else
981             Strcat(linebuf, "died");
982 #else
983         }
984 #endif /*JP*/
985
986         if (t1->deathdnum == astral_level.dnum) {
987 #if 0 /*JP*/
988             const char *arg, *fmt = " on the Plane of %s";
989 #else
990             const char *arg;
991 #endif
992
993             switch (t1->deathlev) {
994             case -5:
995 #if 0 /*JP*/
996                 fmt = " on the %s Plane";
997 #endif
998 /*JP
999                 arg = "Astral";
1000 */
1001                 arg = "\93V\8fã\8aE";
1002                 break;
1003             case -4:
1004 /*JP
1005                 arg = "Water";
1006 */
1007                 arg = "\90\85\82Ì\90¸\97ì\8aE";
1008                 break;
1009             case -3:
1010 /*JP
1011                 arg = "Fire";
1012 */
1013                 arg = "\89Î\82Ì\90¸\97ì\8aE";
1014                 break;
1015             case -2:
1016 /*JP
1017                 arg = "Air";
1018 */
1019                 arg = "\95\97\82Ì\90¸\97ì\8aE";
1020                 break;
1021             case -1:
1022 /*JP
1023                 arg = "Earth";
1024 */
1025                 arg = "\92n\82Ì\90¸\97ì\8aE";
1026                 break;
1027             default:
1028                 arg = "Void";
1029                 break;
1030             }
1031 #if 0 /*JP*/
1032             Sprintf(eos(linebuf), fmt, arg);
1033 #else
1034             Sprintf(where, "%s\82É\82Ä", arg);
1035 #endif
1036         } else {
1037 /*JP
1038             Sprintf(eos(linebuf), " in %s", dungeons[t1->deathdnum].dname);
1039 */
1040             Sprintf(eos(linebuf), "%s", dungeons[t1->deathdnum].dname);
1041             if (t1->deathdnum != knox_level.dnum)
1042 /*JP
1043                 Sprintf(eos(linebuf), " on level %d", t1->deathlev);
1044 */
1045                 Sprintf(eos(linebuf), "\82Ì\92n\89º%d\8aK\82É\82Ä", t1->deathlev);
1046             if (t1->deathlev != t1->maxlvl)
1047 /*JP
1048                 Sprintf(eos(linebuf), " [max %d]", t1->maxlvl);
1049 */
1050                 Sprintf(eos(where), "[\8dÅ\91å\92n\89º%d\8aK]", t1->maxlvl);
1051         }
1052
1053 #if 0 /*JP*//* \93ú\96{\8cê\82Å\82Í\8d×\8dH\95s\97v */
1054         /* kludge for "quit while already on Charon's boat" */
1055         if (!strncmp(t1->death, "quit ", 5))
1056             Strcat(linebuf, t1->death + 4);
1057 #endif
1058     }
1059 #if 0 /*JP*/
1060     Strcat(linebuf, ".");
1061 #endif
1062
1063     /* Quit, starved, ascended, and escaped contain no second line */
1064     if (second_line)
1065 /*JP
1066         Sprintf(eos(linebuf), "  %c%s.", highc(*(t1->death)), t1->death + 1);
1067 */
1068         Sprintf(action, "%s", t1->death);
1069
1070 #if 1 /*JP*/
1071     Sprintf(eos(linebuf), "%s%s%s\81D", who, where, action);
1072 #endif
1073     lngr = (int) strlen(linebuf);
1074     if (t1->hp <= 0)
1075         hpbuf[0] = '-', hpbuf[1] = '\0';
1076     else
1077         Sprintf(hpbuf, "%d", t1->hp);
1078     /* beginning of hp column after padding (not actually padded yet) */
1079     hppos = COLNO - (sizeof("  Hp [max]") - 1); /* sizeof(str) includes \0 */
1080 #if 1 /*JP*/
1081     while (lngr >= hppos) {
1082 /*JP hppos\82æ\82è\91O\82Ì\93K\93\96\82È\88Ê\92u\82Å\95ª\8a\84\82·\82é\81D*/
1083         car[0] = '\0';
1084         cdr[0] = '\0';
1085         split_japanese(linebuf, car, cdr, hppos);
1086         
1087         bp = eos(car);
1088         if (so) {
1089             while (bp < car + (COLNO-1)) *bp++ = ' ';
1090             *bp = 0;
1091             topten_print_bold(car);
1092         } else
1093             topten_print(car);
1094         
1095             Sprintf(linebuf, "%15s %s", "", cdr);
1096         lngr = (int)strlen(linebuf);
1097     }
1098 /*JP: \93ú\96{\8cê\82ª\93ü\82é\82Æ\95\8e\9a\97ñ\82ð\8cã\82©\82ç\8c©\82Ä\82¢\82­\82±\82Æ\82Í\82Å\82«\82È\82¢\82½\82ß\83R\83\81\83\93\83g\83A\83E\83g*/
1099 #else
1100     while (lngr >= hppos) {
1101         for (bp = eos(linebuf); !(*bp == ' ' && (bp - linebuf < hppos)); bp--)
1102             ;
1103         /* special case: word is too long, wrap in the middle */
1104         if (linebuf + 15 >= bp)
1105             bp = linebuf + hppos - 1;
1106         /* special case: if about to wrap in the middle of maximum
1107            dungeon depth reached, wrap in front of it instead */
1108         if (bp > linebuf + 5 && !strncmp(bp - 5, " [max", 5))
1109             bp -= 5;
1110         if (*bp != ' ')
1111             Strcpy(linebuf3, bp);
1112         else
1113             Strcpy(linebuf3, bp + 1);
1114         *bp = 0;
1115         if (so) {
1116             while (bp < linebuf + (COLNO - 1))
1117                 *bp++ = ' ';
1118             *bp = 0;
1119             topten_print_bold(linebuf);
1120         } else
1121             topten_print(linebuf);
1122         Sprintf(linebuf, "%15s %s", "", linebuf3);
1123         lngr = strlen(linebuf);
1124     }
1125 #endif /*JP*/
1126     /* beginning of hp column not including padding */
1127     hppos = COLNO - 7 - (int) strlen(hpbuf);
1128     bp = eos(linebuf);
1129
1130     if (bp <= linebuf + hppos) {
1131         /* pad any necessary blanks to the hit point entry */
1132         while (bp < linebuf + hppos)
1133             *bp++ = ' ';
1134         Strcpy(bp, hpbuf);
1135         Sprintf(eos(bp), " %s[%d]",
1136                 (t1->maxhp < 10) ? "  " : (t1->maxhp < 100) ? " " : "",
1137                 t1->maxhp);
1138     }
1139
1140     if (so) {
1141         bp = eos(linebuf);
1142         if (so >= COLNO)
1143             so = COLNO - 1;
1144         while (bp < linebuf + so)
1145             *bp++ = ' ';
1146         *bp = 0;
1147         topten_print_bold(linebuf);
1148     } else
1149         topten_print(linebuf);
1150 }
1151
1152 STATIC_OVL int
1153 score_wanted(current_ver, rank, t1, playerct, players, uid)
1154 boolean current_ver;
1155 int rank;
1156 struct toptenentry *t1;
1157 int playerct;
1158 const char **players;
1159 int uid;
1160 {
1161     int i;
1162
1163     if (current_ver
1164         && (t1->ver_major != VERSION_MAJOR || t1->ver_minor != VERSION_MINOR
1165             || t1->patchlevel != PATCHLEVEL))
1166         return 0;
1167
1168     if (sysopt.pers_is_uid && !playerct && t1->uid == uid)
1169         return 1;
1170
1171     for (i = 0; i < playerct; i++) {
1172         if (players[i][0] == '-' && index("pr", players[i][1])
1173             && players[i][2] == 0 && i + 1 < playerct) {
1174             const char *arg = players[i + 1];
1175             if ((players[i][1] == 'p'
1176                  && str2role(arg) == str2role(t1->plrole))
1177                 || (players[i][1] == 'r'
1178                     && str2race(arg) == str2race(t1->plrace)))
1179                 return 1;
1180             i++;
1181         } else if (strcmp(players[i], "all") == 0
1182                    || strncmp(t1->name, players[i], NAMSZ) == 0
1183                    || (players[i][0] == '-' && players[i][1] == t1->plrole[0]
1184                        && players[i][2] == 0)
1185                    || (digit(players[i][0]) && rank <= atoi(players[i])))
1186             return 1;
1187     }
1188     return 0;
1189 }
1190
1191 /*
1192  * print selected parts of score list.
1193  * argc >= 2, with argv[0] untrustworthy (directory names, et al.),
1194  * and argv[1] starting with "-s".
1195  * caveat: some shells might allow argv elements to be arbitrarily long.
1196  */
1197 void
1198 prscore(argc, argv)
1199 int argc;
1200 char **argv;
1201 {
1202     const char **players;
1203     int playerct, rank;
1204     boolean current_ver = TRUE, init_done = FALSE;
1205     register struct toptenentry *t1;
1206     FILE *rfile;
1207     boolean match_found = FALSE;
1208     register int i;
1209     char pbuf[BUFSZ];
1210     int uid = -1;
1211     const char *player0;
1212
1213     if (argc < 2 || strncmp(argv[1], "-s", 2)) {
1214         raw_printf("prscore: bad arguments (%d)", argc);
1215         return;
1216     }
1217
1218     rfile = fopen_datafile(RECORD, "r", SCOREPREFIX);
1219     if (!rfile) {
1220         raw_print("Cannot open record file!");
1221         return;
1222     }
1223
1224 #ifdef AMIGA
1225     {
1226         extern winid amii_rawprwin;
1227
1228         init_nhwindows(&argc, argv);
1229         amii_rawprwin = create_nhwindow(NHW_TEXT);
1230     }
1231 #endif
1232
1233     /* If the score list isn't after a game, we never went through
1234      * initialization. */
1235     if (wiz1_level.dlevel == 0) {
1236         dlb_init();
1237         init_dungeons();
1238         init_done = TRUE;
1239     }
1240
1241     if (!argv[1][2]) { /* plain "-s" */
1242         argc--;
1243         argv++;
1244     } else
1245         argv[1] += 2;
1246
1247     if (argc > 1 && !strcmp(argv[1], "-v")) {
1248         current_ver = FALSE;
1249         argc--;
1250         argv++;
1251     }
1252
1253     if (argc <= 1) {
1254         if (sysopt.pers_is_uid) {
1255             uid = getuid();
1256             playerct = 0;
1257             players = (const char **) 0;
1258         } else {
1259             player0 = plname;
1260             if (!*player0)
1261 #ifdef AMIGA
1262                 player0 = "all"; /* single user system */
1263 #else
1264                 player0 = "hackplayer";
1265 #endif
1266             playerct = 1;
1267             players = &player0;
1268         }
1269     } else {
1270 #if 0 /*JP*/
1271         playerct = --argc;
1272         players = (const char **) ++argv;
1273 #else
1274         int j;
1275         playerct = --argc;
1276         ++argv;
1277         players = (const char **)alloc(sizeof(char *) * argc + 1);
1278         for (j = 0; j < argc; j++) {
1279             char *p = (char *)str2ic(argv[j]);
1280             players[j] = (char *)alloc(strlen(p) + 1);
1281             strcpy((void *)players[j], p);
1282         }
1283         players[j] = NULL;
1284 #endif
1285     }
1286     raw_print("");
1287
1288     t1 = tt_head = newttentry();
1289     for (rank = 1;; rank++) {
1290         readentry(rfile, t1);
1291         if (t1->points == 0)
1292             break;
1293         if (!match_found
1294             && score_wanted(current_ver, rank, t1, playerct, players, uid))
1295             match_found = TRUE;
1296         t1->tt_next = newttentry();
1297         t1 = t1->tt_next;
1298     }
1299
1300     (void) fclose(rfile);
1301     if (init_done) {
1302         free_dungeons();
1303         dlb_cleanup();
1304     }
1305
1306     if (match_found) {
1307         outheader();
1308         t1 = tt_head;
1309         for (rank = 1; t1->points != 0; rank++, t1 = t1->tt_next) {
1310             if (score_wanted(current_ver, rank, t1, playerct, players, uid))
1311                 (void) outentry(rank, t1, FALSE);
1312         }
1313     } else {
1314         Sprintf(pbuf, "Cannot find any %sentries for ",
1315                 current_ver ? "current " : "");
1316         if (playerct < 1)
1317             Strcat(pbuf, "you.");
1318         else {
1319             if (playerct > 1)
1320                 Strcat(pbuf, "any of ");
1321             for (i = 0; i < playerct; i++) {
1322                 /* stop printing players if there are too many to fit */
1323                 if (strlen(pbuf) + strlen(players[i]) + 2 >= BUFSZ) {
1324                     if (strlen(pbuf) < BUFSZ - 4)
1325                         Strcat(pbuf, "...");
1326                     else
1327                         Strcpy(pbuf + strlen(pbuf) - 4, "...");
1328                     break;
1329                 }
1330                 Strcat(pbuf, players[i]);
1331                 if (i < playerct - 1) {
1332                     if (players[i][0] == '-' && index("pr", players[i][1])
1333                         && players[i][2] == 0)
1334                         Strcat(pbuf, " ");
1335                     else
1336                         Strcat(pbuf, ":");
1337                 }
1338             }
1339         }
1340         raw_print(pbuf);
1341         raw_printf("Usage: %s -s [-v] <playertypes> [maxrank] [playernames]",
1342
1343                    hname);
1344         raw_printf("Player types are: [-p role] [-r race]");
1345     }
1346     free_ttlist(tt_head);
1347 #ifdef AMIGA
1348     {
1349         extern winid amii_rawprwin;
1350
1351         display_nhwindow(amii_rawprwin, 1);
1352         destroy_nhwindow(amii_rawprwin);
1353         amii_rawprwin = WIN_ERR;
1354     }
1355 #endif
1356 }
1357
1358 STATIC_OVL int
1359 classmon(plch, fem)
1360 char *plch;
1361 boolean fem;
1362 {
1363     int i;
1364
1365     /* Look for this role in the role table */
1366     for (i = 0; roles[i].name.m; i++)
1367         if (!strncmp(plch, roles[i].filecode, ROLESZ)) {
1368             if (fem && roles[i].femalenum != NON_PM)
1369                 return roles[i].femalenum;
1370             else if (roles[i].malenum != NON_PM)
1371                 return roles[i].malenum;
1372             else
1373                 return PM_HUMAN;
1374         }
1375     /* this might be from a 3.2.x score for former Elf class */
1376     if (!strcmp(plch, "E"))
1377         return PM_RANGER;
1378
1379     impossible("What weird role is this? (%s)", plch);
1380     return  PM_HUMAN_MUMMY;
1381 }
1382
1383 /*
1384  * Get a random player name and class from the high score list,
1385  */
1386 struct toptenentry *
1387 get_rnd_toptenentry()
1388 {
1389     int rank, i;
1390     FILE *rfile;
1391     register struct toptenentry *tt;
1392     static struct toptenentry tt_buf;
1393
1394     rfile = fopen_datafile(RECORD, "r", SCOREPREFIX);
1395     if (!rfile) {
1396         impossible("Cannot open record file!");
1397         return NULL;
1398     }
1399
1400     tt = &tt_buf;
1401     rank = rnd(sysopt.tt_oname_maxrank);
1402 pickentry:
1403     for (i = rank; i; i--) {
1404         readentry(rfile, tt);
1405         if (tt->points == 0)
1406             break;
1407     }
1408
1409     if (tt->points == 0) {
1410         if (rank > 1) {
1411             rank = 1;
1412             rewind(rfile);
1413             goto pickentry;
1414         }
1415         tt = NULL;
1416     }
1417
1418     (void) fclose(rfile);
1419     return tt;
1420 }
1421
1422
1423 /*
1424  * Attach random player name and class from high score list
1425  * to an object (for statues or morgue corpses).
1426  */
1427 struct obj *
1428 tt_oname(otmp)
1429 struct obj *otmp;
1430 {
1431     struct toptenentry *tt;
1432     if (!otmp)
1433         return (struct obj *) 0;
1434
1435     tt = get_rnd_toptenentry();
1436
1437     if (!tt)
1438         return (struct obj *) 0;
1439
1440     set_corpsenm(otmp, classmon(tt->plrole, (tt->plgend[0] == 'F')));
1441     otmp = oname(otmp, tt->name);
1442
1443     return otmp;
1444 }
1445
1446 #ifdef NO_SCAN_BRACK
1447 /* Lattice scanf isn't up to reading the scorefile.  What */
1448 /* follows deals with that; I admit it's ugly. (KL) */
1449 /* Now generally available (KL) */
1450 STATIC_OVL void
1451 nsb_mung_line(p)
1452 char *p;
1453 {
1454     while ((p = index(p, ' ')) != 0)
1455         *p = '|';
1456 }
1457
1458 STATIC_OVL void
1459 nsb_unmung_line(p)
1460 char *p;
1461 {
1462     while ((p = index(p, '|')) != 0)
1463         *p = ' ';
1464 }
1465 #endif /* NO_SCAN_BRACK */
1466
1467 /*topten.c*/