OSDN Git Service

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