1*a0c9b602Sfox /* $NetBSD: hack.end.c,v 1.19 2020/02/07 22:04:02 fox Exp $ */
23ea4a95cSchristos
302ded532Smycroft /*
41c7f94e5Sjsm * Copyright (c) 1985, Stichting Centrum voor Wiskunde en Informatica,
51c7f94e5Sjsm * Amsterdam
61c7f94e5Sjsm * All rights reserved.
71c7f94e5Sjsm *
81c7f94e5Sjsm * Redistribution and use in source and binary forms, with or without
91c7f94e5Sjsm * modification, are permitted provided that the following conditions are
101c7f94e5Sjsm * met:
111c7f94e5Sjsm *
121c7f94e5Sjsm * - Redistributions of source code must retain the above copyright notice,
131c7f94e5Sjsm * this list of conditions and the following disclaimer.
141c7f94e5Sjsm *
151c7f94e5Sjsm * - Redistributions in binary form must reproduce the above copyright
161c7f94e5Sjsm * notice, this list of conditions and the following disclaimer in the
171c7f94e5Sjsm * documentation and/or other materials provided with the distribution.
181c7f94e5Sjsm *
191c7f94e5Sjsm * - Neither the name of the Stichting Centrum voor Wiskunde en
201c7f94e5Sjsm * Informatica, nor the names of its contributors may be used to endorse or
211c7f94e5Sjsm * promote products derived from this software without specific prior
221c7f94e5Sjsm * written permission.
231c7f94e5Sjsm *
241c7f94e5Sjsm * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
251c7f94e5Sjsm * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
261c7f94e5Sjsm * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
271c7f94e5Sjsm * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
281c7f94e5Sjsm * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
291c7f94e5Sjsm * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
301c7f94e5Sjsm * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
311c7f94e5Sjsm * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
321c7f94e5Sjsm * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
331c7f94e5Sjsm * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
341c7f94e5Sjsm * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
351c7f94e5Sjsm */
361c7f94e5Sjsm
371c7f94e5Sjsm /*
381c7f94e5Sjsm * Copyright (c) 1982 Jay Fenlason <hack@gnu.org>
391c7f94e5Sjsm * All rights reserved.
401c7f94e5Sjsm *
411c7f94e5Sjsm * Redistribution and use in source and binary forms, with or without
421c7f94e5Sjsm * modification, are permitted provided that the following conditions
431c7f94e5Sjsm * are met:
441c7f94e5Sjsm * 1. Redistributions of source code must retain the above copyright
451c7f94e5Sjsm * notice, this list of conditions and the following disclaimer.
461c7f94e5Sjsm * 2. Redistributions in binary form must reproduce the above copyright
471c7f94e5Sjsm * notice, this list of conditions and the following disclaimer in the
481c7f94e5Sjsm * documentation and/or other materials provided with the distribution.
491c7f94e5Sjsm * 3. The name of the author may not be used to endorse or promote products
501c7f94e5Sjsm * derived from this software without specific prior written permission.
511c7f94e5Sjsm *
521c7f94e5Sjsm * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
531c7f94e5Sjsm * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
541c7f94e5Sjsm * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
551c7f94e5Sjsm * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
561c7f94e5Sjsm * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
571c7f94e5Sjsm * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
581c7f94e5Sjsm * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
591c7f94e5Sjsm * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
601c7f94e5Sjsm * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
611c7f94e5Sjsm * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
6202ded532Smycroft */
6302ded532Smycroft
643ea4a95cSchristos #include <sys/cdefs.h>
6502ded532Smycroft #ifndef lint
66*a0c9b602Sfox __RCSID("$NetBSD: hack.end.c,v 1.19 2020/02/07 22:04:02 fox Exp $");
6702ded532Smycroft #endif /* not lint */
6861f28255Scgd
6961f28255Scgd #include <signal.h>
703ea4a95cSchristos #include <unistd.h>
713ea4a95cSchristos #include <stdlib.h>
723ea4a95cSchristos #include "hack.h"
733ea4a95cSchristos #include "extern.h"
74907fca1bSdholland #define Snprintf (void) snprintf
7561f28255Scgd
7661f28255Scgd xchar maxdlevel = 1;
7761f28255Scgd
789b92b189Sdholland struct toptenentry;
799b92b189Sdholland
809b92b189Sdholland static void topten(void);
819b92b189Sdholland static void outheader(void);
829b92b189Sdholland static int outentry(int, struct toptenentry *, int);
839b92b189Sdholland static char *itoa(int);
849b92b189Sdholland static const char *ordin(int);
859b92b189Sdholland
863ea4a95cSchristos int
dodone(void)871fa8a9a6Sdholland dodone(void)
883ea4a95cSchristos {
893ea4a95cSchristos done1(0);
903ea4a95cSchristos return 0;
913ea4a95cSchristos }
923ea4a95cSchristos
933ea4a95cSchristos
943ea4a95cSchristos /*ARGSUSED*/
9561f28255Scgd void
done1(int n __unused)961fa8a9a6Sdholland done1(int n __unused)
9761f28255Scgd {
9861f28255Scgd (void) signal(SIGINT, SIG_IGN);
9961f28255Scgd pline("Really quit?");
10061f28255Scgd if (readchar() != 'y') {
10161f28255Scgd (void) signal(SIGINT, done1);
10261f28255Scgd clrlin();
10361f28255Scgd (void) fflush(stdout);
1043ea4a95cSchristos if (multi > 0)
1053ea4a95cSchristos nomul(0);
10661f28255Scgd return;
10761f28255Scgd }
10861f28255Scgd done("quit");
10961f28255Scgd /* NOTREACHED */
11061f28255Scgd }
11161f28255Scgd
1129b92b189Sdholland static int done_stopprint;
1139b92b189Sdholland static int done_hup;
11461f28255Scgd
1153ea4a95cSchristos /*ARGSUSED*/
1169b92b189Sdholland static void
done_intr(int n __unused)1171fa8a9a6Sdholland done_intr(int n __unused)
1183ea4a95cSchristos {
11961f28255Scgd done_stopprint++;
12061f28255Scgd (void) signal(SIGINT, SIG_IGN);
12161f28255Scgd (void) signal(SIGQUIT, SIG_IGN);
12261f28255Scgd }
12361f28255Scgd
1249b92b189Sdholland static void
done_hangup(int n)1251fa8a9a6Sdholland done_hangup(int n)
1263ea4a95cSchristos {
12761f28255Scgd done_hup++;
12861f28255Scgd (void) signal(SIGHUP, SIG_IGN);
1293ea4a95cSchristos done_intr(n);
13061f28255Scgd }
13161f28255Scgd
1323ea4a95cSchristos void
done_in_by(struct monst * mtmp)1331fa8a9a6Sdholland done_in_by(struct monst *mtmp)
1343ea4a95cSchristos {
13561f28255Scgd static char buf[BUFSZ];
13661f28255Scgd pline("You die ...");
13761f28255Scgd if (mtmp->data->mlet == ' ') {
138907fca1bSdholland Snprintf(buf, sizeof(buf),
139907fca1bSdholland "the ghost of %s", (char *) mtmp->mextra);
14061f28255Scgd killer = buf;
14161f28255Scgd } else if (mtmp->mnamelth) {
142907fca1bSdholland Snprintf(buf, sizeof(buf), "%s called %s",
14361f28255Scgd mtmp->data->mname, NAME(mtmp));
14461f28255Scgd killer = buf;
14561f28255Scgd } else if (mtmp->minvis) {
146907fca1bSdholland Snprintf(buf, sizeof(buf), "invisible %s", mtmp->data->mname);
14761f28255Scgd killer = buf;
1483ea4a95cSchristos } else
1493ea4a95cSchristos killer = mtmp->data->mname;
15061f28255Scgd done("died");
15161f28255Scgd }
15261f28255Scgd
1533ea4a95cSchristos /*
1543ea4a95cSchristos * called with arg "died", "drowned", "escaped", "quit", "choked",
1553ea4a95cSchristos * "panicked", "burned", "starved" or "tricked"
1563ea4a95cSchristos */
15761f28255Scgd /* Be careful not to call panic from here! */
1583ea4a95cSchristos void
done(const char * st1)1591fa8a9a6Sdholland done(const char *st1)
16061f28255Scgd {
16161f28255Scgd
16261f28255Scgd #ifdef WIZARD
16361f28255Scgd if (wizard && *st1 == 'd') {
16461f28255Scgd u.uswldtim = 0;
1653ea4a95cSchristos if (u.uhpmax < 0)
1663ea4a95cSchristos u.uhpmax = 100; /* arbitrary */
16761f28255Scgd u.uhp = u.uhpmax;
16861f28255Scgd pline("For some reason you are still alive.");
16961f28255Scgd flags.move = 0;
1703ea4a95cSchristos if (multi > 0)
1713ea4a95cSchristos multi = 0;
1723ea4a95cSchristos else
1733ea4a95cSchristos multi = -1;
17461f28255Scgd flags.botl = 1;
17561f28255Scgd return;
17661f28255Scgd }
1773ea4a95cSchristos #endif /* WIZARD */
17861f28255Scgd (void) signal(SIGINT, done_intr);
17961f28255Scgd (void) signal(SIGQUIT, done_intr);
18061f28255Scgd (void) signal(SIGHUP, done_hangup);
18161f28255Scgd if (*st1 == 'q' && u.uhp < 1) {
18261f28255Scgd st1 = "died";
18361f28255Scgd killer = "quit while already on Charon's boat";
18461f28255Scgd }
1853ea4a95cSchristos if (*st1 == 's')
1863ea4a95cSchristos killer = "starvation";
1873ea4a95cSchristos else if (*st1 == 'd' && st1[1] == 'r')
1883ea4a95cSchristos killer = "drowning";
1893ea4a95cSchristos else if (*st1 == 'p')
1903ea4a95cSchristos killer = "panic";
1913ea4a95cSchristos else if (*st1 == 't')
1923ea4a95cSchristos killer = "trickery";
1933ea4a95cSchristos else if (!strchr("bcd", *st1))
1943ea4a95cSchristos killer = st1;
19561f28255Scgd paybill();
19661f28255Scgd clearlocks();
1973ea4a95cSchristos if (flags.toplin == 1)
1983ea4a95cSchristos more();
1993ea4a95cSchristos if (strchr("bcds", *st1)) {
20061f28255Scgd #ifdef WIZARD
20161f28255Scgd if (!wizard)
2023ea4a95cSchristos #endif /* WIZARD */
20361f28255Scgd savebones();
20461f28255Scgd if (!flags.notombstone)
20561f28255Scgd outrip();
20661f28255Scgd }
2073ea4a95cSchristos if (*st1 == 'c')
2083ea4a95cSchristos killer = st1; /* after outrip() */
2092c0ecb1aSdholland settty(NULL); /* does a clear_screen() */
21061f28255Scgd if (!done_stopprint)
21161f28255Scgd printf("Goodbye %s %s...\n\n", pl_character, plname);
2123ea4a95cSchristos {
2133ea4a95cSchristos long int tmp;
21461f28255Scgd tmp = u.ugold - u.ugold0;
21561f28255Scgd if (tmp < 0)
21661f28255Scgd tmp = 0;
21761f28255Scgd if (*st1 == 'd' || *st1 == 'b')
21861f28255Scgd tmp -= tmp / 10;
21961f28255Scgd u.urexp += tmp;
22061f28255Scgd u.urexp += 50 * maxdlevel;
22161f28255Scgd if (maxdlevel > 20)
22261f28255Scgd u.urexp += 1000 * ((maxdlevel > 30) ? 10 : maxdlevel - 20);
22361f28255Scgd }
22461f28255Scgd if (*st1 == 'e') {
2253ea4a95cSchristos struct monst *mtmp;
2263ea4a95cSchristos struct obj *otmp;
2273ea4a95cSchristos int i;
2283ea4a95cSchristos unsigned worthlessct = 0;
22961f28255Scgd boolean has_amulet = FALSE;
23061f28255Scgd
23161f28255Scgd killer = st1;
23261f28255Scgd keepdogs();
23361f28255Scgd mtmp = mydogs;
23461f28255Scgd if (mtmp) {
2353ea4a95cSchristos if (!done_stopprint)
2363ea4a95cSchristos printf("You");
23761f28255Scgd while (mtmp) {
23861f28255Scgd if (!done_stopprint)
23961f28255Scgd printf(" and %s", monnam(mtmp));
24061f28255Scgd if (mtmp->mtame)
24161f28255Scgd u.urexp += mtmp->mhp;
24261f28255Scgd mtmp = mtmp->nmon;
24361f28255Scgd }
24461f28255Scgd if (!done_stopprint)
24561f28255Scgd printf("\nescaped from the dungeon with %ld points,\n",
24661f28255Scgd u.urexp);
2473ea4a95cSchristos } else if (!done_stopprint)
24861f28255Scgd printf("You escaped from the dungeon with %ld points,\n",
24961f28255Scgd u.urexp);
25061f28255Scgd for (otmp = invent; otmp; otmp = otmp->nobj) {
25161f28255Scgd if (otmp->olet == GEM_SYM) {
25261f28255Scgd objects[otmp->otyp].oc_name_known = 1;
25361f28255Scgd i = otmp->quan * objects[otmp->otyp].g_val;
25461f28255Scgd if (i == 0) {
25561f28255Scgd worthlessct += otmp->quan;
25661f28255Scgd continue;
25761f28255Scgd }
25861f28255Scgd u.urexp += i;
25961f28255Scgd if (!done_stopprint)
26061f28255Scgd printf("\t%s (worth %d Zorkmids),\n",
26161f28255Scgd doname(otmp), i);
26261f28255Scgd } else if (otmp->olet == AMULET_SYM) {
26361f28255Scgd otmp->known = 1;
26461f28255Scgd i = (otmp->spe < 0) ? 2 : 5000;
26561f28255Scgd u.urexp += i;
26661f28255Scgd if (!done_stopprint)
26761f28255Scgd printf("\t%s (worth %d Zorkmids),\n",
26861f28255Scgd doname(otmp), i);
26961f28255Scgd if (otmp->spe >= 0) {
27061f28255Scgd has_amulet = TRUE;
27161f28255Scgd killer = "escaped (with amulet)";
27261f28255Scgd }
27361f28255Scgd }
27461f28255Scgd }
2753ea4a95cSchristos if (worthlessct)
2763ea4a95cSchristos if (!done_stopprint)
27761f28255Scgd printf("\t%u worthless piece%s of coloured glass,\n",
27861f28255Scgd worthlessct, plur(worthlessct));
2793ea4a95cSchristos if (has_amulet)
2803ea4a95cSchristos u.urexp *= 2;
2813ea4a95cSchristos } else if (!done_stopprint)
28261f28255Scgd printf("You %s on dungeon level %d with %ld points,\n",
28361f28255Scgd st1, dlevel, u.urexp);
28461f28255Scgd if (!done_stopprint)
28561f28255Scgd printf("and %ld piece%s of gold, after %ld move%s.\n",
28661f28255Scgd u.ugold, plur(u.ugold), moves, plur(moves));
28761f28255Scgd if (!done_stopprint)
28861f28255Scgd printf("You were level %u with a maximum of %d hit points when you %s.\n",
28961f28255Scgd u.ulevel, u.uhpmax, st1);
29061f28255Scgd if (*st1 == 'e' && !done_stopprint) {
29161f28255Scgd getret(); /* all those pieces of coloured glass ... */
29261f28255Scgd cls();
29361f28255Scgd }
29461f28255Scgd #ifdef WIZARD
29561f28255Scgd if (!wizard)
2963ea4a95cSchristos #endif /* WIZARD */
29761f28255Scgd topten();
2983ea4a95cSchristos if (done_stopprint)
2993ea4a95cSchristos printf("\n\n");
30061f28255Scgd exit(0);
30161f28255Scgd }
30261f28255Scgd
303434d266eSdholland #define newttentry() ((struct toptenentry *) alloc(sizeof(struct toptenentry)))
30461f28255Scgd #define NAMSZ 8
30561f28255Scgd #define DTHSZ 40
30661f28255Scgd #define PERSMAX 1
30761f28255Scgd #define POINTSMIN 1 /* must be > 0 */
30861f28255Scgd #define ENTRYMAX 100 /* must be >= 10 */
30961f28255Scgd #define PERS_IS_UID /* delete for PERSMAX per name; now per uid */
31061f28255Scgd struct toptenentry {
31161f28255Scgd struct toptenentry *tt_next;
31261f28255Scgd long int points;
31361f28255Scgd int level, maxlvl, hp, maxhp;
31461f28255Scgd int uid;
31561f28255Scgd char plchar;
31661f28255Scgd char sex;
31761f28255Scgd char name[NAMSZ + 1];
31861f28255Scgd char death[DTHSZ + 1];
31961f28255Scgd char date[7];/* yymmdd */
3209b92b189Sdholland };
32161f28255Scgd
3229b92b189Sdholland static struct toptenentry *tt_head;
3239b92b189Sdholland
3249b92b189Sdholland static void
topten(void)3251fa8a9a6Sdholland topten(void)
3263ea4a95cSchristos {
32761f28255Scgd int uid = getuid();
32861f28255Scgd int rank, rank0 = -1, rank1 = 0;
32961f28255Scgd int occ_cnt = PERSMAX;
3303ea4a95cSchristos struct toptenentry *t0, *t1, *tprev;
331ab8b6343Sjsm const char *recfile = RECORD;
332ab8b6343Sjsm const char *reclock = "record_lock";
33361f28255Scgd int sleepct = 300;
33461f28255Scgd FILE *rfile;
3353ea4a95cSchristos int flg = 0;
33661f28255Scgd #define HUP if(!done_hup)
33761f28255Scgd while (link(recfile, reclock) == -1) {
33861f28255Scgd HUP perror(reclock);
33961f28255Scgd if (!sleepct--) {
34061f28255Scgd HUP puts("I give up. Sorry.");
34161f28255Scgd HUP puts("Perhaps there is an old record_lock around?");
34261f28255Scgd return;
34361f28255Scgd }
34461f28255Scgd HUP printf("Waiting for access to record file. (%d)\n",
34561f28255Scgd sleepct);
34661f28255Scgd HUP(void) fflush(stdout);
34761f28255Scgd sleep(1);
34861f28255Scgd }
34961f28255Scgd if (!(rfile = fopen(recfile, "r"))) {
35061f28255Scgd HUP puts("Cannot open record file!");
35161f28255Scgd goto unlock;
35261f28255Scgd }
35361f28255Scgd HUP(void) putchar('\n');
35461f28255Scgd
35561f28255Scgd /* create a new 'topten' entry */
35661f28255Scgd t0 = newttentry();
35761f28255Scgd t0->level = dlevel;
35861f28255Scgd t0->maxlvl = maxdlevel;
35961f28255Scgd t0->hp = u.uhp;
36061f28255Scgd t0->maxhp = u.uhpmax;
36161f28255Scgd t0->points = u.urexp;
36261f28255Scgd t0->plchar = pl_character[0];
36361f28255Scgd t0->sex = (flags.female ? 'F' : 'M');
36461f28255Scgd t0->uid = uid;
365*a0c9b602Sfox (void) strncpy(t0->name, plname, NAMSZ);
36661f28255Scgd (t0->name)[NAMSZ] = 0;
36761f28255Scgd (void) strncpy(t0->death, killer, DTHSZ);
36861f28255Scgd (t0->death)[DTHSZ] = 0;
36937a52c5fSginsbach (void) strcpy(t0->date, getdatestr());
37061f28255Scgd
37161f28255Scgd /* assure minimum number of points */
37261f28255Scgd if (t0->points < POINTSMIN)
37361f28255Scgd t0->points = 0;
37461f28255Scgd
37561f28255Scgd t1 = tt_head = newttentry();
37661f28255Scgd tprev = 0;
37761f28255Scgd /* rank0: -1 undefined, 0 not_on_list, n n_th on list */
37861f28255Scgd for (rank = 1;;) {
37961f28255Scgd if (fscanf(rfile, "%6s %d %d %d %d %d %ld %c%c %[^,],%[^\n]",
38061f28255Scgd t1->date, &t1->uid,
38161f28255Scgd &t1->level, &t1->maxlvl,
38261f28255Scgd &t1->hp, &t1->maxhp, &t1->points,
38361f28255Scgd &t1->plchar, &t1->sex, t1->name, t1->death) != 11
38461f28255Scgd || t1->points < POINTSMIN)
38561f28255Scgd t1->points = 0;
38661f28255Scgd if (rank0 < 0 && t1->points < t0->points) {
38761f28255Scgd rank0 = rank++;
38861f28255Scgd if (tprev == 0)
38961f28255Scgd tt_head = t0;
39061f28255Scgd else
39161f28255Scgd tprev->tt_next = t0;
39261f28255Scgd t0->tt_next = t1;
39361f28255Scgd occ_cnt--;
39461f28255Scgd flg++; /* ask for a rewrite */
39561f28255Scgd } else
39661f28255Scgd tprev = t1;
3973ea4a95cSchristos if (t1->points == 0)
3983ea4a95cSchristos break;
39961f28255Scgd if (
40061f28255Scgd #ifdef PERS_IS_UID
40161f28255Scgd t1->uid == t0->uid &&
40261f28255Scgd #else
40361f28255Scgd strncmp(t1->name, t0->name, NAMSZ) == 0 &&
4043ea4a95cSchristos #endif /* PERS_IS_UID */
40561f28255Scgd t1->plchar == t0->plchar && --occ_cnt <= 0) {
40661f28255Scgd if (rank0 < 0) {
40761f28255Scgd rank0 = 0;
40861f28255Scgd rank1 = rank;
40961f28255Scgd HUP printf("You didn't beat your previous score of %ld points.\n\n",
41061f28255Scgd t1->points);
41161f28255Scgd }
41261f28255Scgd if (occ_cnt < 0) {
41361f28255Scgd flg++;
41461f28255Scgd continue;
41561f28255Scgd }
41661f28255Scgd }
41761f28255Scgd if (rank <= ENTRYMAX) {
41861f28255Scgd t1 = t1->tt_next = newttentry();
41961f28255Scgd rank++;
42061f28255Scgd }
42161f28255Scgd if (rank > ENTRYMAX) {
42261f28255Scgd t1->points = 0;
42361f28255Scgd break;
42461f28255Scgd }
42561f28255Scgd }
42661f28255Scgd if (flg) { /* rewrite record file */
42761f28255Scgd (void) fclose(rfile);
42861f28255Scgd if (!(rfile = fopen(recfile, "w"))) {
42961f28255Scgd HUP puts("Cannot write record file\n");
43061f28255Scgd goto unlock;
43161f28255Scgd }
4323ea4a95cSchristos if (!done_stopprint)
4333ea4a95cSchristos if (rank0 > 0) {
43461f28255Scgd if (rank0 <= 10)
43561f28255Scgd puts("You made the top ten list!\n");
43661f28255Scgd else
43761f28255Scgd printf("You reached the %d%s place on the top %d list.\n\n",
43861f28255Scgd rank0, ordin(rank0), ENTRYMAX);
43961f28255Scgd }
44061f28255Scgd }
4413ea4a95cSchristos if (rank0 == 0)
4423ea4a95cSchristos rank0 = rank1;
4433ea4a95cSchristos if (rank0 <= 0)
4443ea4a95cSchristos rank0 = rank;
4453ea4a95cSchristos if (!done_stopprint)
4463ea4a95cSchristos outheader();
44761f28255Scgd t1 = tt_head;
44861f28255Scgd for (rank = 1; t1->points != 0; rank++, t1 = t1->tt_next) {
4493ea4a95cSchristos if (flg)
4503ea4a95cSchristos fprintf(rfile, "%6s %d %d %d %d %d %ld %c%c %s,%s\n",
45161f28255Scgd t1->date, t1->uid,
45261f28255Scgd t1->level, t1->maxlvl,
45361f28255Scgd t1->hp, t1->maxhp, t1->points,
45461f28255Scgd t1->plchar, t1->sex, t1->name, t1->death);
4553ea4a95cSchristos if (done_stopprint)
4563ea4a95cSchristos continue;
4573c439f43Sdholland if (rank > (int)flags.end_top &&
4583c439f43Sdholland (rank < rank0 - (int)flags.end_around || rank > rank0 + (int)flags.end_around)
45961f28255Scgd && (!flags.end_own ||
46061f28255Scgd #ifdef PERS_IS_UID
46161f28255Scgd t1->uid != t0->uid))
46261f28255Scgd #else
46361f28255Scgd strncmp(t1->name, t0->name, NAMSZ)))
4643ea4a95cSchristos #endif /* PERS_IS_UID */
46561f28255Scgd continue;
4663c439f43Sdholland if (rank == rank0 - (int)flags.end_around &&
4673c439f43Sdholland rank0 > (int)flags.end_top + (int)flags.end_around + 1 &&
46861f28255Scgd !flags.end_own)
46961f28255Scgd (void) putchar('\n');
47061f28255Scgd if (rank != rank0)
47161f28255Scgd (void) outentry(rank, t1, 0);
47261f28255Scgd else if (!rank1)
47361f28255Scgd (void) outentry(rank, t1, 1);
47461f28255Scgd else {
47561f28255Scgd int t0lth = outentry(0, t0, -1);
47661f28255Scgd int t1lth = outentry(rank, t1, t0lth);
4773ea4a95cSchristos if (t1lth > t0lth)
4783ea4a95cSchristos t0lth = t1lth;
47961f28255Scgd (void) outentry(0, t0, t0lth);
48061f28255Scgd }
48161f28255Scgd }
4823ea4a95cSchristos if (rank0 >= rank)
4833ea4a95cSchristos if (!done_stopprint)
48461f28255Scgd (void) outentry(0, t0, 1);
48561f28255Scgd (void) fclose(rfile);
486a108a3f4Schristos free(t0);
48761f28255Scgd unlock:
48861f28255Scgd (void) unlink(reclock);
48961f28255Scgd }
49061f28255Scgd
4919b92b189Sdholland static void
outheader(void)4921fa8a9a6Sdholland outheader(void)
4933ea4a95cSchristos {
49461f28255Scgd char linebuf[BUFSZ];
4953ea4a95cSchristos char *bp;
49661f28255Scgd (void) strcpy(linebuf, "Number Points Name");
49761f28255Scgd bp = eos(linebuf);
4983ea4a95cSchristos while (bp < linebuf + COLNO - 9)
4993ea4a95cSchristos *bp++ = ' ';
50061f28255Scgd (void) strcpy(bp, "Hp [max]");
50161f28255Scgd puts(linebuf);
50261f28255Scgd }
50361f28255Scgd
504907fca1bSdholland /* so>0: standout line; so=0: ordinary line; so<0: no output, return length */
5059b92b189Sdholland static int
outentry(int rank,struct toptenentry * t1,int so)506ab8b6343Sjsm outentry(int rank, struct toptenentry *t1, int so)
5073ea4a95cSchristos {
5083c439f43Sdholland boolean quit = FALSE, gotkilled = FALSE, starv = FALSE;
50961f28255Scgd char linebuf[BUFSZ];
510907fca1bSdholland size_t pos;
5113c439f43Sdholland
512907fca1bSdholland linebuf[0] = '\0';
513907fca1bSdholland pos = 0;
514907fca1bSdholland
5153ea4a95cSchristos if (rank)
516907fca1bSdholland Snprintf(linebuf+pos, sizeof(linebuf)-pos, "%3d", rank);
5173ea4a95cSchristos else
518907fca1bSdholland Snprintf(linebuf+pos, sizeof(linebuf)-pos, " ");
519907fca1bSdholland pos = strlen(linebuf);
520907fca1bSdholland
521907fca1bSdholland Snprintf(linebuf+pos, sizeof(linebuf)-pos, " %6ld %8s",
522907fca1bSdholland t1->points, t1->name);
523907fca1bSdholland pos = strlen(linebuf);
524907fca1bSdholland
5253ea4a95cSchristos if (t1->plchar == 'X')
526907fca1bSdholland Snprintf(linebuf+pos, sizeof(linebuf)-pos, " ");
5273ea4a95cSchristos else
528907fca1bSdholland Snprintf(linebuf+pos, sizeof(linebuf)-pos, "-%c ", t1->plchar);
529907fca1bSdholland pos = strlen(linebuf);
530907fca1bSdholland
53161f28255Scgd if (!strncmp("escaped", t1->death, 7)) {
53261f28255Scgd if (!strcmp(" (with amulet)", t1->death + 7))
533907fca1bSdholland Snprintf(linebuf+pos, sizeof(linebuf)-pos,
534907fca1bSdholland "escaped the dungeon with amulet");
53561f28255Scgd else
536907fca1bSdholland Snprintf(linebuf+pos, sizeof(linebuf)-pos,
537907fca1bSdholland "escaped the dungeon [max level %d]",
53861f28255Scgd t1->maxlvl);
539907fca1bSdholland pos = strlen(linebuf);
54061f28255Scgd } else {
54161f28255Scgd if (!strncmp(t1->death, "quit", 4)) {
54261f28255Scgd quit = TRUE;
54361f28255Scgd if (t1->maxhp < 3 * t1->hp && t1->maxlvl < 4)
544907fca1bSdholland Snprintf(linebuf+pos, sizeof(linebuf)-pos,
545907fca1bSdholland "cravenly gave up");
54661f28255Scgd else
547907fca1bSdholland Snprintf(linebuf+pos, sizeof(linebuf)-pos,
548907fca1bSdholland "quit");
549907fca1bSdholland } else if (!strcmp(t1->death, "choked")) {
550907fca1bSdholland Snprintf(linebuf+pos, sizeof(linebuf)-pos,
551907fca1bSdholland "choked on %s food",
55261f28255Scgd (t1->sex == 'F') ? "her" : "his");
553907fca1bSdholland } else if (!strncmp(t1->death, "starv", 5)) {
554907fca1bSdholland Snprintf(linebuf+pos, sizeof(linebuf)-pos,
555907fca1bSdholland "starved to death");
556907fca1bSdholland starv = TRUE;
557907fca1bSdholland } else {
558907fca1bSdholland Snprintf(linebuf+pos, sizeof(linebuf)-pos,
559907fca1bSdholland "was killed");
560907fca1bSdholland gotkilled = TRUE;
56161f28255Scgd }
562907fca1bSdholland pos = strlen(linebuf);
563907fca1bSdholland
564907fca1bSdholland Snprintf(linebuf+pos, sizeof(linebuf)-pos, " on%s level %d",
565907fca1bSdholland (gotkilled || starv) ? "" : " dungeon", t1->level);
566907fca1bSdholland pos = strlen(linebuf);
567907fca1bSdholland
568907fca1bSdholland if (t1->maxlvl != t1->level)
569907fca1bSdholland Snprintf(linebuf+pos, sizeof(linebuf)-pos,
570907fca1bSdholland " [max %d]", t1->maxlvl);
571907fca1bSdholland pos = strlen(linebuf);
572907fca1bSdholland
573907fca1bSdholland if (quit && t1->death[4])
574907fca1bSdholland Snprintf(linebuf+pos, sizeof(linebuf)-pos,
575907fca1bSdholland "%s", t1->death + 4);
576907fca1bSdholland pos = strlen(linebuf);
577907fca1bSdholland }
578907fca1bSdholland if (gotkilled) {
579907fca1bSdholland Snprintf(linebuf+pos, sizeof(linebuf)-pos, " by %s%s",
58061f28255Scgd (!strncmp(t1->death, "trick", 5) || !strncmp(t1->death, "the ", 4))
58161f28255Scgd ? "" :
5823ea4a95cSchristos strchr(vowels, *t1->death) ? "an " : "a ",
58361f28255Scgd t1->death);
584907fca1bSdholland pos = strlen(linebuf);
585907fca1bSdholland }
586907fca1bSdholland strlcat(linebuf, ".", sizeof(linebuf));
587907fca1bSdholland pos = strlen(linebuf);
58861f28255Scgd if (t1->maxhp) {
58961f28255Scgd char hpbuf[10];
590907fca1bSdholland unsigned hppos;
591907fca1bSdholland
592907fca1bSdholland strlcpy(hpbuf, (t1->hp > 0) ? itoa(t1->hp) : "-", sizeof(hpbuf));
59361f28255Scgd hppos = COLNO - 7 - strlen(hpbuf);
594907fca1bSdholland if (pos <= hppos) {
595907fca1bSdholland while (pos < hppos)
596907fca1bSdholland linebuf[pos++] = ' ';
597907fca1bSdholland (void) strlcpy(linebuf+pos, hpbuf, sizeof(linebuf)-pos);
598907fca1bSdholland pos = strlen(linebuf);
599907fca1bSdholland Snprintf(linebuf+pos, sizeof(linebuf)-pos,
600907fca1bSdholland " [%d]", t1->maxhp);
601907fca1bSdholland pos = strlen(linebuf);
60261f28255Scgd }
60361f28255Scgd }
6043ea4a95cSchristos if (so == 0)
6053ea4a95cSchristos puts(linebuf);
60661f28255Scgd else if (so > 0) {
6073ea4a95cSchristos if (so >= COLNO)
6083ea4a95cSchristos so = COLNO - 1;
609907fca1bSdholland while (pos < (unsigned)so)
610907fca1bSdholland linebuf[pos++] = ' ';
611907fca1bSdholland linebuf[pos] = '\0';
61261f28255Scgd standoutbeg();
61361f28255Scgd fputs(linebuf, stdout);
61461f28255Scgd standoutend();
61561f28255Scgd (void) putchar('\n');
61661f28255Scgd }
617907fca1bSdholland return /*(strlen(linebuf))*/ pos;
61861f28255Scgd }
61961f28255Scgd
6209b92b189Sdholland static char *
itoa(int a)6211fa8a9a6Sdholland itoa(int a)
6223ea4a95cSchristos {
62361f28255Scgd static char buf[12];
624907fca1bSdholland Snprintf(buf, sizeof(buf), "%d", a);
62561f28255Scgd return (buf);
62661f28255Scgd }
62761f28255Scgd
6289b92b189Sdholland static const char *
ordin(int n)6291fa8a9a6Sdholland ordin(int n)
6303ea4a95cSchristos {
6313c439f43Sdholland int dg = n % 10;
6323c439f43Sdholland
6333c439f43Sdholland return ((dg == 0 || dg > 3 || n / 10 == 1) ? "th" : (dg == 1) ? "st" :
6343c439f43Sdholland (dg == 2) ? "nd" : "rd");
63561f28255Scgd }
63661f28255Scgd
6373ea4a95cSchristos void
clearlocks(void)6381fa8a9a6Sdholland clearlocks(void)
6393ea4a95cSchristos {
6403ea4a95cSchristos int x;
64161f28255Scgd (void) signal(SIGHUP, SIG_IGN);
64261f28255Scgd for (x = maxdlevel; x >= 0; x--) {
64361f28255Scgd glo(x);
64461f28255Scgd (void) unlink(lock); /* not all levels need be present */
64561f28255Scgd }
64661f28255Scgd }
64761f28255Scgd
64861f28255Scgd #ifdef NOSAVEONHANGUP
6493ea4a95cSchristos /*ARGSUSED*/
6503ea4a95cSchristos void
hang_up(int n __unused)65198eb8895Sroy hang_up(int n __unused)
65261f28255Scgd {
65361f28255Scgd (void) signal(SIGINT, SIG_IGN);
65461f28255Scgd clearlocks();
65561f28255Scgd exit(1);
65661f28255Scgd }
6573ea4a95cSchristos #endif /* NOSAVEONHANGUP */
65861f28255Scgd
65961f28255Scgd char *
eos(char * s)6601fa8a9a6Sdholland eos(char *s)
66161f28255Scgd {
6623ea4a95cSchristos while (*s)
6633ea4a95cSchristos s++;
66461f28255Scgd return (s);
66561f28255Scgd }
66661f28255Scgd
66761f28255Scgd /* it is the callers responsibility to check that there is room for c */
6683ea4a95cSchristos void
charcat(char * s,int c)6691fa8a9a6Sdholland charcat(char *s, int c)
6703ea4a95cSchristos {
6713ea4a95cSchristos while (*s)
6723ea4a95cSchristos s++;
67361f28255Scgd *s++ = c;
67461f28255Scgd *s = 0;
67561f28255Scgd }
67661f28255Scgd
67761f28255Scgd /*
67861f28255Scgd * Called with args from main if argc >= 0. In this case, list scores as
67961f28255Scgd * requested. Otherwise, find scores for the current player (and list them
68061f28255Scgd * if argc == -1).
68161f28255Scgd */
6823ea4a95cSchristos void
prscore(int argc,char ** argv)6831fa8a9a6Sdholland prscore(int argc, char **argv)
6843ea4a95cSchristos {
6853ea4a95cSchristos char **players = NULL;
68661f28255Scgd int playerct;
68761f28255Scgd int rank;
6883ea4a95cSchristos struct toptenentry *t1, *t2;
689ab8b6343Sjsm const char *recfile = RECORD;
69061f28255Scgd FILE *rfile;
6913ea4a95cSchristos int flg = 0;
6923ea4a95cSchristos int i;
69361f28255Scgd #ifdef nonsense
69461f28255Scgd long total_score = 0L;
69561f28255Scgd char totchars[10];
69661f28255Scgd int totcharct = 0;
6973ea4a95cSchristos #endif /* nonsense */
69861f28255Scgd int outflg = (argc >= -1);
69961f28255Scgd #ifdef PERS_IS_UID
70061f28255Scgd int uid = -1;
70161f28255Scgd #else
70261f28255Scgd char *player0;
7033ea4a95cSchristos #endif /* PERS_IS_UID */
70461f28255Scgd
70561f28255Scgd if (!(rfile = fopen(recfile, "r"))) {
70661f28255Scgd puts("Cannot open record file!");
70761f28255Scgd return;
70861f28255Scgd }
70961f28255Scgd if (argc > 1 && !strncmp(argv[1], "-s", 2)) {
71061f28255Scgd if (!argv[1][2]) {
71161f28255Scgd argc--;
71261f28255Scgd argv++;
7133ea4a95cSchristos } else if (!argv[1][3] && strchr("CFKSTWX", argv[1][2])) {
71461f28255Scgd argv[1]++;
71561f28255Scgd argv[1][0] = '-';
7163ea4a95cSchristos } else
7173ea4a95cSchristos argv[1] += 2;
71861f28255Scgd }
71961f28255Scgd if (argc <= 1) {
72061f28255Scgd #ifdef PERS_IS_UID
72161f28255Scgd uid = getuid();
72261f28255Scgd playerct = 0;
72361f28255Scgd #else
72461f28255Scgd player0 = plname;
72561f28255Scgd if (!*player0)
72661f28255Scgd player0 = "hackplayer";
72761f28255Scgd playerct = 1;
72861f28255Scgd players = &player0;
7293ea4a95cSchristos #endif /* PERS_IS_UID */
73061f28255Scgd } else {
73161f28255Scgd playerct = --argc;
73261f28255Scgd players = ++argv;
73361f28255Scgd }
7343ea4a95cSchristos if (outflg)
7353ea4a95cSchristos putchar('\n');
73661f28255Scgd
73761f28255Scgd t1 = tt_head = newttentry();
73861f28255Scgd for (rank = 1;; rank++) {
73961f28255Scgd if (fscanf(rfile, "%6s %d %d %d %d %d %ld %c%c %[^,],%[^\n]",
74061f28255Scgd t1->date, &t1->uid,
74161f28255Scgd &t1->level, &t1->maxlvl,
74261f28255Scgd &t1->hp, &t1->maxhp, &t1->points,
74361f28255Scgd &t1->plchar, &t1->sex, t1->name, t1->death) != 11)
74461f28255Scgd t1->points = 0;
7453ea4a95cSchristos if (t1->points == 0)
7463ea4a95cSchristos break;
74761f28255Scgd #ifdef PERS_IS_UID
74861f28255Scgd if (!playerct && t1->uid == uid)
74961f28255Scgd flg++;
75061f28255Scgd else
7513ea4a95cSchristos #endif /* PERS_IS_UID */
75261f28255Scgd for (i = 0; i < playerct; i++) {
75361f28255Scgd if (strcmp(players[i], "all") == 0 ||
75461f28255Scgd strncmp(t1->name, players[i], NAMSZ) == 0 ||
75561f28255Scgd (players[i][0] == '-' &&
75661f28255Scgd players[i][1] == t1->plchar &&
75761f28255Scgd players[i][2] == 0) ||
75861f28255Scgd (digit(players[i][0]) && rank <= atoi(players[i])))
75961f28255Scgd flg++;
76061f28255Scgd }
76161f28255Scgd t1 = t1->tt_next = newttentry();
76261f28255Scgd }
76361f28255Scgd (void) fclose(rfile);
76461f28255Scgd if (!flg) {
76561f28255Scgd if (outflg) {
76661f28255Scgd printf("Cannot find any entries for ");
7673ea4a95cSchristos if (playerct < 1)
7683ea4a95cSchristos printf("you.\n");
76961f28255Scgd else {
7703ea4a95cSchristos if (playerct > 1)
7713ea4a95cSchristos printf("any of ");
77261f28255Scgd for (i = 0; i < playerct; i++)
77361f28255Scgd printf("%s%s", players[i], (i < playerct - 1) ? ", " : ".\n");
77461f28255Scgd printf("Call is: %s -s [playernames]\n", hname);
77561f28255Scgd }
77661f28255Scgd }
77761f28255Scgd return;
77861f28255Scgd }
7793ea4a95cSchristos if (outflg)
7803ea4a95cSchristos outheader();
78161f28255Scgd t1 = tt_head;
78261f28255Scgd for (rank = 1; t1->points != 0; rank++, t1 = t2) {
78361f28255Scgd t2 = t1->tt_next;
78461f28255Scgd #ifdef PERS_IS_UID
78561f28255Scgd if (!playerct && t1->uid == uid)
78661f28255Scgd goto outwithit;
78761f28255Scgd else
7883ea4a95cSchristos #endif /* PERS_IS_UID */
78961f28255Scgd for (i = 0; i < playerct; i++) {
79061f28255Scgd if (strcmp(players[i], "all") == 0 ||
79161f28255Scgd strncmp(t1->name, players[i], NAMSZ) == 0 ||
79261f28255Scgd (players[i][0] == '-' &&
79361f28255Scgd players[i][1] == t1->plchar &&
79461f28255Scgd players[i][2] == 0) ||
79561f28255Scgd (digit(players[i][0]) && rank <= atoi(players[i]))) {
79661f28255Scgd outwithit:
79761f28255Scgd if (outflg)
79861f28255Scgd (void) outentry(rank, t1, 0);
79961f28255Scgd #ifdef nonsense
80061f28255Scgd total_score += t1->points;
80161f28255Scgd if (totcharct < sizeof(totchars) - 1)
80261f28255Scgd totchars[totcharct++] = t1->plchar;
8033ea4a95cSchristos #endif /* nonsense */
80461f28255Scgd break;
80561f28255Scgd }
80661f28255Scgd }
8078e73b3adSdholland free(t1);
80861f28255Scgd }
80961f28255Scgd #ifdef nonsense
81061f28255Scgd totchars[totcharct] = 0;
81161f28255Scgd
8123ea4a95cSchristos /*
8133ea4a95cSchristos * We would like to determine whether he is experienced. However, the
8143ea4a95cSchristos * information collected here only tells about the scores/roles that
8153ea4a95cSchristos * got into the topten (top 100?). We should maintain a .hacklog or
8163ea4a95cSchristos * something in his home directory.
8173ea4a95cSchristos */
81861f28255Scgd flags.beginner = (total_score < 6000);
81961f28255Scgd for (i = 0; i < 6; i++)
8203ea4a95cSchristos if (!strchr(totchars, "CFKSTWX"[i])) {
82161f28255Scgd flags.beginner = 1;
8223ea4a95cSchristos if (!pl_character[0])
8233ea4a95cSchristos pl_character[0] = "CFKSTWX"[i];
82461f28255Scgd break;
82561f28255Scgd }
8263ea4a95cSchristos #endif /* nonsense */
82761f28255Scgd }
828