OSDN Git Service

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