Home | History | Annotate | Line # | Download | only in hack
hack.end.c revision 1.6
      1  1.6       jsm /*	$NetBSD: hack.end.c,v 1.6 2003/04/02 18:36:36 jsm Exp $	*/
      2  1.4  christos 
      3  1.2   mycroft /*
      4  1.6       jsm  * Copyright (c) 1985, Stichting Centrum voor Wiskunde en Informatica,
      5  1.6       jsm  * Amsterdam
      6  1.6       jsm  * All rights reserved.
      7  1.6       jsm  *
      8  1.6       jsm  * Redistribution and use in source and binary forms, with or without
      9  1.6       jsm  * modification, are permitted provided that the following conditions are
     10  1.6       jsm  * met:
     11  1.6       jsm  *
     12  1.6       jsm  * - Redistributions of source code must retain the above copyright notice,
     13  1.6       jsm  * this list of conditions and the following disclaimer.
     14  1.6       jsm  *
     15  1.6       jsm  * - Redistributions in binary form must reproduce the above copyright
     16  1.6       jsm  * notice, this list of conditions and the following disclaimer in the
     17  1.6       jsm  * documentation and/or other materials provided with the distribution.
     18  1.6       jsm  *
     19  1.6       jsm  * - Neither the name of the Stichting Centrum voor Wiskunde en
     20  1.6       jsm  * Informatica, nor the names of its contributors may be used to endorse or
     21  1.6       jsm  * promote products derived from this software without specific prior
     22  1.6       jsm  * written permission.
     23  1.6       jsm  *
     24  1.6       jsm  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
     25  1.6       jsm  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     26  1.6       jsm  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
     27  1.6       jsm  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
     28  1.6       jsm  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     29  1.6       jsm  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     30  1.6       jsm  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     31  1.6       jsm  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
     32  1.6       jsm  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
     33  1.6       jsm  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     34  1.6       jsm  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     35  1.6       jsm  */
     36  1.6       jsm 
     37  1.6       jsm /*
     38  1.6       jsm  * Copyright (c) 1982 Jay Fenlason <hack (at) gnu.org>
     39  1.6       jsm  * All rights reserved.
     40  1.6       jsm  *
     41  1.6       jsm  * Redistribution and use in source and binary forms, with or without
     42  1.6       jsm  * modification, are permitted provided that the following conditions
     43  1.6       jsm  * are met:
     44  1.6       jsm  * 1. Redistributions of source code must retain the above copyright
     45  1.6       jsm  *    notice, this list of conditions and the following disclaimer.
     46  1.6       jsm  * 2. Redistributions in binary form must reproduce the above copyright
     47  1.6       jsm  *    notice, this list of conditions and the following disclaimer in the
     48  1.6       jsm  *    documentation and/or other materials provided with the distribution.
     49  1.6       jsm  * 3. The name of the author may not be used to endorse or promote products
     50  1.6       jsm  *    derived from this software without specific prior written permission.
     51  1.6       jsm  *
     52  1.6       jsm  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
     53  1.6       jsm  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
     54  1.6       jsm  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
     55  1.6       jsm  * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     56  1.6       jsm  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     57  1.6       jsm  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     58  1.6       jsm  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     59  1.6       jsm  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     60  1.6       jsm  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
     61  1.6       jsm  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     62  1.2   mycroft  */
     63  1.2   mycroft 
     64  1.4  christos #include <sys/cdefs.h>
     65  1.2   mycroft #ifndef lint
     66  1.6       jsm __RCSID("$NetBSD: hack.end.c,v 1.6 2003/04/02 18:36:36 jsm Exp $");
     67  1.4  christos #endif				/* not lint */
     68  1.1       cgd 
     69  1.4  christos #include <signal.h>
     70  1.4  christos #include <unistd.h>
     71  1.4  christos #include <stdlib.h>
     72  1.1       cgd #include "hack.h"
     73  1.4  christos #include "extern.h"
     74  1.1       cgd #define	Sprintf	(void) sprintf
     75  1.1       cgd 
     76  1.4  christos xchar           maxdlevel = 1;
     77  1.4  christos 
     78  1.4  christos int
     79  1.4  christos dodone()
     80  1.4  christos {
     81  1.4  christos 	done1(0);
     82  1.4  christos 	return 0;
     83  1.4  christos }
     84  1.4  christos 
     85  1.1       cgd 
     86  1.4  christos /*ARGSUSED*/
     87  1.1       cgd void
     88  1.4  christos done1(n)
     89  1.5       jsm 	int n __attribute__((__unused__));
     90  1.1       cgd {
     91  1.4  christos 	(void) signal(SIGINT, SIG_IGN);
     92  1.1       cgd 	pline("Really quit?");
     93  1.4  christos 	if (readchar() != 'y') {
     94  1.4  christos 		(void) signal(SIGINT, done1);
     95  1.1       cgd 		clrlin();
     96  1.1       cgd 		(void) fflush(stdout);
     97  1.4  christos 		if (multi > 0)
     98  1.4  christos 			nomul(0);
     99  1.1       cgd 		return;
    100  1.1       cgd 	}
    101  1.1       cgd 	done("quit");
    102  1.1       cgd 	/* NOTREACHED */
    103  1.1       cgd }
    104  1.1       cgd 
    105  1.4  christos int             done_stopprint;
    106  1.4  christos int             done_hup;
    107  1.1       cgd 
    108  1.4  christos /*ARGSUSED*/
    109  1.1       cgd void
    110  1.4  christos done_intr(n)
    111  1.5       jsm 	int n __attribute__((__unused__));
    112  1.4  christos {
    113  1.1       cgd 	done_stopprint++;
    114  1.1       cgd 	(void) signal(SIGINT, SIG_IGN);
    115  1.1       cgd 	(void) signal(SIGQUIT, SIG_IGN);
    116  1.1       cgd }
    117  1.1       cgd 
    118  1.1       cgd void
    119  1.4  christos done_hangup(n)
    120  1.4  christos 	int n;
    121  1.4  christos {
    122  1.1       cgd 	done_hup++;
    123  1.1       cgd 	(void) signal(SIGHUP, SIG_IGN);
    124  1.4  christos 	done_intr(n);
    125  1.1       cgd }
    126  1.1       cgd 
    127  1.4  christos void
    128  1.4  christos done_in_by(mtmp)
    129  1.4  christos 	struct monst   *mtmp;
    130  1.4  christos {
    131  1.4  christos 	static char     buf[BUFSZ];
    132  1.1       cgd 	pline("You die ...");
    133  1.4  christos 	if (mtmp->data->mlet == ' ') {
    134  1.1       cgd 		Sprintf(buf, "the ghost of %s", (char *) mtmp->mextra);
    135  1.1       cgd 		killer = buf;
    136  1.4  christos 	} else if (mtmp->mnamelth) {
    137  1.1       cgd 		Sprintf(buf, "%s called %s",
    138  1.1       cgd 			mtmp->data->mname, NAME(mtmp));
    139  1.1       cgd 		killer = buf;
    140  1.4  christos 	} else if (mtmp->minvis) {
    141  1.1       cgd 		Sprintf(buf, "invisible %s", mtmp->data->mname);
    142  1.1       cgd 		killer = buf;
    143  1.4  christos 	} else
    144  1.4  christos 		killer = mtmp->data->mname;
    145  1.1       cgd 	done("died");
    146  1.1       cgd }
    147  1.1       cgd 
    148  1.4  christos /*
    149  1.4  christos  * called with arg "died", "drowned", "escaped", "quit", "choked",
    150  1.4  christos  * "panicked", "burned", "starved" or "tricked"
    151  1.4  christos  */
    152  1.1       cgd /* Be careful not to call panic from here! */
    153  1.4  christos void
    154  1.1       cgd done(st1)
    155  1.5       jsm 	const char           *st1;
    156  1.1       cgd {
    157  1.1       cgd 
    158  1.1       cgd #ifdef WIZARD
    159  1.4  christos 	if (wizard && *st1 == 'd') {
    160  1.1       cgd 		u.uswldtim = 0;
    161  1.4  christos 		if (u.uhpmax < 0)
    162  1.4  christos 			u.uhpmax = 100;	/* arbitrary */
    163  1.1       cgd 		u.uhp = u.uhpmax;
    164  1.1       cgd 		pline("For some reason you are still alive.");
    165  1.1       cgd 		flags.move = 0;
    166  1.4  christos 		if (multi > 0)
    167  1.4  christos 			multi = 0;
    168  1.4  christos 		else
    169  1.4  christos 			multi = -1;
    170  1.1       cgd 		flags.botl = 1;
    171  1.1       cgd 		return;
    172  1.1       cgd 	}
    173  1.4  christos #endif	/* WIZARD */
    174  1.1       cgd 	(void) signal(SIGINT, done_intr);
    175  1.1       cgd 	(void) signal(SIGQUIT, done_intr);
    176  1.1       cgd 	(void) signal(SIGHUP, done_hangup);
    177  1.4  christos 	if (*st1 == 'q' && u.uhp < 1) {
    178  1.1       cgd 		st1 = "died";
    179  1.1       cgd 		killer = "quit while already on Charon's boat";
    180  1.1       cgd 	}
    181  1.4  christos 	if (*st1 == 's')
    182  1.4  christos 		killer = "starvation";
    183  1.4  christos 	else if (*st1 == 'd' && st1[1] == 'r')
    184  1.4  christos 		killer = "drowning";
    185  1.4  christos 	else if (*st1 == 'p')
    186  1.4  christos 		killer = "panic";
    187  1.4  christos 	else if (*st1 == 't')
    188  1.4  christos 		killer = "trickery";
    189  1.4  christos 	else if (!strchr("bcd", *st1))
    190  1.4  christos 		killer = st1;
    191  1.1       cgd 	paybill();
    192  1.1       cgd 	clearlocks();
    193  1.4  christos 	if (flags.toplin == 1)
    194  1.4  christos 		more();
    195  1.4  christos 	if (strchr("bcds", *st1)) {
    196  1.1       cgd #ifdef WIZARD
    197  1.4  christos 		if (!wizard)
    198  1.4  christos #endif	/* WIZARD */
    199  1.4  christos 			savebones();
    200  1.4  christos 		if (!flags.notombstone)
    201  1.1       cgd 			outrip();
    202  1.1       cgd 	}
    203  1.4  christos 	if (*st1 == 'c')
    204  1.4  christos 		killer = st1;	/* after outrip() */
    205  1.1       cgd 	settty((char *) 0);	/* does a clear_screen() */
    206  1.4  christos 	if (!done_stopprint)
    207  1.1       cgd 		printf("Goodbye %s %s...\n\n", pl_character, plname);
    208  1.4  christos 	{
    209  1.4  christos 		long int        tmp;
    210  1.4  christos 		tmp = u.ugold - u.ugold0;
    211  1.4  christos 		if (tmp < 0)
    212  1.4  christos 			tmp = 0;
    213  1.4  christos 		if (*st1 == 'd' || *st1 == 'b')
    214  1.4  christos 			tmp -= tmp / 10;
    215  1.4  christos 		u.urexp += tmp;
    216  1.4  christos 		u.urexp += 50 * maxdlevel;
    217  1.4  christos 		if (maxdlevel > 20)
    218  1.4  christos 			u.urexp += 1000 * ((maxdlevel > 30) ? 10 : maxdlevel - 20);
    219  1.4  christos 	}
    220  1.4  christos 	if (*st1 == 'e') {
    221  1.4  christos 		struct monst   *mtmp;
    222  1.4  christos 		struct obj     *otmp;
    223  1.4  christos 		int             i;
    224  1.4  christos 		unsigned        worthlessct = 0;
    225  1.4  christos 		boolean         has_amulet = FALSE;
    226  1.1       cgd 
    227  1.1       cgd 		killer = st1;
    228  1.1       cgd 		keepdogs();
    229  1.1       cgd 		mtmp = mydogs;
    230  1.4  christos 		if (mtmp) {
    231  1.4  christos 			if (!done_stopprint)
    232  1.4  christos 				printf("You");
    233  1.4  christos 			while (mtmp) {
    234  1.4  christos 				if (!done_stopprint)
    235  1.1       cgd 					printf(" and %s", monnam(mtmp));
    236  1.4  christos 				if (mtmp->mtame)
    237  1.1       cgd 					u.urexp += mtmp->mhp;
    238  1.1       cgd 				mtmp = mtmp->nmon;
    239  1.1       cgd 			}
    240  1.4  christos 			if (!done_stopprint)
    241  1.4  christos 				printf("\nescaped from the dungeon with %ld points,\n",
    242  1.4  christos 				       u.urexp);
    243  1.4  christos 		} else if (!done_stopprint)
    244  1.4  christos 			printf("You escaped from the dungeon with %ld points,\n",
    245  1.4  christos 			       u.urexp);
    246  1.4  christos 		for (otmp = invent; otmp; otmp = otmp->nobj) {
    247  1.4  christos 			if (otmp->olet == GEM_SYM) {
    248  1.1       cgd 				objects[otmp->otyp].oc_name_known = 1;
    249  1.4  christos 				i = otmp->quan * objects[otmp->otyp].g_val;
    250  1.4  christos 				if (i == 0) {
    251  1.1       cgd 					worthlessct += otmp->quan;
    252  1.1       cgd 					continue;
    253  1.1       cgd 				}
    254  1.1       cgd 				u.urexp += i;
    255  1.4  christos 				if (!done_stopprint)
    256  1.4  christos 					printf("\t%s (worth %d Zorkmids),\n",
    257  1.4  christos 					       doname(otmp), i);
    258  1.4  christos 			} else if (otmp->olet == AMULET_SYM) {
    259  1.1       cgd 				otmp->known = 1;
    260  1.1       cgd 				i = (otmp->spe < 0) ? 2 : 5000;
    261  1.1       cgd 				u.urexp += i;
    262  1.4  christos 				if (!done_stopprint)
    263  1.4  christos 					printf("\t%s (worth %d Zorkmids),\n",
    264  1.4  christos 					       doname(otmp), i);
    265  1.4  christos 				if (otmp->spe >= 0) {
    266  1.1       cgd 					has_amulet = TRUE;
    267  1.1       cgd 					killer = "escaped (with amulet)";
    268  1.1       cgd 				}
    269  1.1       cgd 			}
    270  1.1       cgd 		}
    271  1.4  christos 		if (worthlessct)
    272  1.4  christos 			if (!done_stopprint)
    273  1.4  christos 				printf("\t%u worthless piece%s of coloured glass,\n",
    274  1.4  christos 				       worthlessct, plur(worthlessct));
    275  1.4  christos 		if (has_amulet)
    276  1.4  christos 			u.urexp *= 2;
    277  1.4  christos 	} else if (!done_stopprint)
    278  1.4  christos 		printf("You %s on dungeon level %d with %ld points,\n",
    279  1.4  christos 		       st1, dlevel, u.urexp);
    280  1.4  christos 	if (!done_stopprint)
    281  1.4  christos 		printf("and %ld piece%s of gold, after %ld move%s.\n",
    282  1.4  christos 		       u.ugold, plur(u.ugold), moves, plur(moves));
    283  1.4  christos 	if (!done_stopprint)
    284  1.4  christos 		printf("You were level %u with a maximum of %d hit points when you %s.\n",
    285  1.4  christos 		       u.ulevel, u.uhpmax, st1);
    286  1.4  christos 	if (*st1 == 'e' && !done_stopprint) {
    287  1.1       cgd 		getret();	/* all those pieces of coloured glass ... */
    288  1.1       cgd 		cls();
    289  1.1       cgd 	}
    290  1.1       cgd #ifdef WIZARD
    291  1.4  christos 	if (!wizard)
    292  1.4  christos #endif	/* WIZARD */
    293  1.1       cgd 		topten();
    294  1.4  christos 	if (done_stopprint)
    295  1.4  christos 		printf("\n\n");
    296  1.1       cgd 	exit(0);
    297  1.1       cgd }
    298  1.1       cgd 
    299  1.1       cgd #define newttentry() (struct toptenentry *) alloc(sizeof(struct toptenentry))
    300  1.1       cgd #define	NAMSZ	8
    301  1.1       cgd #define	DTHSZ	40
    302  1.1       cgd #define	PERSMAX	1
    303  1.1       cgd #define	POINTSMIN	1	/* must be > 0 */
    304  1.1       cgd #define	ENTRYMAX	100	/* must be >= 10 */
    305  1.1       cgd #define	PERS_IS_UID		/* delete for PERSMAX per name; now per uid */
    306  1.1       cgd struct toptenentry {
    307  1.1       cgd 	struct toptenentry *tt_next;
    308  1.4  christos 	long int        points;
    309  1.4  christos 	int             level, maxlvl, hp, maxhp;
    310  1.4  christos 	int             uid;
    311  1.4  christos 	char            plchar;
    312  1.4  christos 	char            sex;
    313  1.4  christos 	char            name[NAMSZ + 1];
    314  1.4  christos 	char            death[DTHSZ + 1];
    315  1.4  christos 	char            date[7];/* yymmdd */
    316  1.4  christos }              *tt_head;
    317  1.4  christos 
    318  1.4  christos void
    319  1.4  christos topten()
    320  1.4  christos {
    321  1.4  christos 	int             uid = getuid();
    322  1.4  christos 	int             rank, rank0 = -1, rank1 = 0;
    323  1.4  christos 	int             occ_cnt = PERSMAX;
    324  1.4  christos 	struct toptenentry *t0, *t1, *tprev;
    325  1.5       jsm 	const char     *recfile = RECORD;
    326  1.5       jsm 	const char     *reclock = "record_lock";
    327  1.4  christos 	int             sleepct = 300;
    328  1.4  christos 	FILE           *rfile;
    329  1.4  christos 	int 		flg = 0;
    330  1.1       cgd #define	HUP	if(!done_hup)
    331  1.4  christos 	while (link(recfile, reclock) == -1) {
    332  1.4  christos 		HUP             perror(reclock);
    333  1.4  christos 		if (!sleepct--) {
    334  1.4  christos 			HUP             puts("I give up. Sorry.");
    335  1.4  christos 			HUP             puts("Perhaps there is an old record_lock around?");
    336  1.1       cgd 			return;
    337  1.1       cgd 		}
    338  1.4  christos 		HUP             printf("Waiting for access to record file. (%d)\n",
    339  1.4  christos 				                       sleepct);
    340  1.4  christos 		HUP(void) fflush(stdout);
    341  1.1       cgd 		sleep(1);
    342  1.1       cgd 	}
    343  1.4  christos 	if (!(rfile = fopen(recfile, "r"))) {
    344  1.4  christos 		HUP             puts("Cannot open record file!");
    345  1.1       cgd 		goto unlock;
    346  1.1       cgd 	}
    347  1.4  christos 	HUP(void) putchar('\n');
    348  1.1       cgd 
    349  1.1       cgd 	/* create a new 'topten' entry */
    350  1.1       cgd 	t0 = newttentry();
    351  1.1       cgd 	t0->level = dlevel;
    352  1.1       cgd 	t0->maxlvl = maxdlevel;
    353  1.1       cgd 	t0->hp = u.uhp;
    354  1.1       cgd 	t0->maxhp = u.uhpmax;
    355  1.1       cgd 	t0->points = u.urexp;
    356  1.1       cgd 	t0->plchar = pl_character[0];
    357  1.1       cgd 	t0->sex = (flags.female ? 'F' : 'M');
    358  1.1       cgd 	t0->uid = uid;
    359  1.1       cgd 	(void) strncpy(t0->name, plname, NAMSZ);
    360  1.1       cgd 	(t0->name)[NAMSZ] = 0;
    361  1.1       cgd 	(void) strncpy(t0->death, killer, DTHSZ);
    362  1.1       cgd 	(t0->death)[DTHSZ] = 0;
    363  1.1       cgd 	(void) strcpy(t0->date, getdate());
    364  1.1       cgd 
    365  1.1       cgd 	/* assure minimum number of points */
    366  1.4  christos 	if (t0->points < POINTSMIN)
    367  1.1       cgd 		t0->points = 0;
    368  1.1       cgd 
    369  1.1       cgd 	t1 = tt_head = newttentry();
    370  1.1       cgd 	tprev = 0;
    371  1.1       cgd 	/* rank0: -1 undefined, 0 not_on_list, n n_th on list */
    372  1.4  christos 	for (rank = 1;;) {
    373  1.4  christos 		if (fscanf(rfile, "%6s %d %d %d %d %d %ld %c%c %[^,],%[^\n]",
    374  1.4  christos 			   t1->date, &t1->uid,
    375  1.4  christos 			   &t1->level, &t1->maxlvl,
    376  1.4  christos 			   &t1->hp, &t1->maxhp, &t1->points,
    377  1.4  christos 			   &t1->plchar, &t1->sex, t1->name, t1->death) != 11
    378  1.4  christos 		    || t1->points < POINTSMIN)
    379  1.1       cgd 			t1->points = 0;
    380  1.4  christos 		if (rank0 < 0 && t1->points < t0->points) {
    381  1.4  christos 			rank0 = rank++;
    382  1.4  christos 			if (tprev == 0)
    383  1.4  christos 				tt_head = t0;
    384  1.4  christos 			else
    385  1.4  christos 				tprev->tt_next = t0;
    386  1.4  christos 			t0->tt_next = t1;
    387  1.4  christos 			occ_cnt--;
    388  1.4  christos 			flg++;	/* ask for a rewrite */
    389  1.4  christos 		} else
    390  1.4  christos 			tprev = t1;
    391  1.4  christos 		if (t1->points == 0)
    392  1.4  christos 			break;
    393  1.4  christos 		if (
    394  1.1       cgd #ifdef PERS_IS_UID
    395  1.4  christos 		    t1->uid == t0->uid &&
    396  1.1       cgd #else
    397  1.4  christos 		    strncmp(t1->name, t0->name, NAMSZ) == 0 &&
    398  1.4  christos #endif	/* PERS_IS_UID */
    399  1.4  christos 		    t1->plchar == t0->plchar && --occ_cnt <= 0) {
    400  1.4  christos 			if (rank0 < 0) {
    401  1.4  christos 				rank0 = 0;
    402  1.4  christos 				rank1 = rank;
    403  1.4  christos 				HUP             printf("You didn't beat your previous score of %ld points.\n\n",
    404  1.4  christos 						                t1->points);
    405  1.4  christos 			}
    406  1.4  christos 			if (occ_cnt < 0) {
    407  1.4  christos 				flg++;
    408  1.4  christos 				continue;
    409  1.4  christos 			}
    410  1.1       cgd 		}
    411  1.4  christos 		if (rank <= ENTRYMAX) {
    412  1.4  christos 			t1 = t1->tt_next = newttentry();
    413  1.4  christos 			rank++;
    414  1.4  christos 		}
    415  1.4  christos 		if (rank > ENTRYMAX) {
    416  1.4  christos 			t1->points = 0;
    417  1.4  christos 			break;
    418  1.1       cgd 		}
    419  1.1       cgd 	}
    420  1.4  christos 	if (flg) {		/* rewrite record file */
    421  1.1       cgd 		(void) fclose(rfile);
    422  1.4  christos 		if (!(rfile = fopen(recfile, "w"))) {
    423  1.4  christos 			HUP             puts("Cannot write record file\n");
    424  1.1       cgd 			goto unlock;
    425  1.1       cgd 		}
    426  1.4  christos 		if (!done_stopprint)
    427  1.4  christos 			if (rank0 > 0) {
    428  1.4  christos 				if (rank0 <= 10)
    429  1.4  christos 					puts("You made the top ten list!\n");
    430  1.4  christos 				else
    431  1.4  christos 					printf("You reached the %d%s place on the top %d list.\n\n",
    432  1.4  christos 					     rank0, ordin(rank0), ENTRYMAX);
    433  1.4  christos 			}
    434  1.1       cgd 	}
    435  1.4  christos 	if (rank0 == 0)
    436  1.4  christos 		rank0 = rank1;
    437  1.4  christos 	if (rank0 <= 0)
    438  1.4  christos 		rank0 = rank;
    439  1.4  christos 	if (!done_stopprint)
    440  1.4  christos 		outheader();
    441  1.1       cgd 	t1 = tt_head;
    442  1.4  christos 	for (rank = 1; t1->points != 0; rank++, t1 = t1->tt_next) {
    443  1.4  christos 		if (flg)
    444  1.4  christos 			fprintf(rfile, "%6s %d %d %d %d %d %ld %c%c %s,%s\n",
    445  1.4  christos 				t1->date, t1->uid,
    446  1.4  christos 				t1->level, t1->maxlvl,
    447  1.4  christos 				t1->hp, t1->maxhp, t1->points,
    448  1.4  christos 				t1->plchar, t1->sex, t1->name, t1->death);
    449  1.4  christos 		if (done_stopprint)
    450  1.4  christos 			continue;
    451  1.4  christos 		if (rank > flags.end_top &&
    452  1.4  christos 		    (rank < rank0 - flags.end_around || rank > rank0 + flags.end_around)
    453  1.4  christos 		    && (!flags.end_own ||
    454  1.1       cgd #ifdef PERS_IS_UID
    455  1.4  christos 			t1->uid != t0->uid))
    456  1.1       cgd #else
    457  1.4  christos 			strncmp(t1->name, t0->name, NAMSZ)))
    458  1.4  christos #endif	/* PERS_IS_UID */
    459  1.4  christos 			continue;
    460  1.4  christos 		if (rank == rank0 - flags.end_around &&
    461  1.4  christos 		    rank0 > flags.end_top + flags.end_around + 1 &&
    462  1.4  christos 		    !flags.end_own)
    463  1.4  christos 			(void) putchar('\n');
    464  1.4  christos 		if (rank != rank0)
    465  1.4  christos 			(void) outentry(rank, t1, 0);
    466  1.4  christos 		else if (!rank1)
    467  1.4  christos 			(void) outentry(rank, t1, 1);
    468  1.4  christos 		else {
    469  1.4  christos 			int             t0lth = outentry(0, t0, -1);
    470  1.4  christos 			int             t1lth = outentry(rank, t1, t0lth);
    471  1.4  christos 			if (t1lth > t0lth)
    472  1.4  christos 				t0lth = t1lth;
    473  1.4  christos 			(void) outentry(0, t0, t0lth);
    474  1.4  christos 		}
    475  1.1       cgd 	}
    476  1.4  christos 	if (rank0 >= rank)
    477  1.4  christos 		if (!done_stopprint)
    478  1.4  christos 			(void) outentry(0, t0, 1);
    479  1.1       cgd 	(void) fclose(rfile);
    480  1.1       cgd unlock:
    481  1.1       cgd 	(void) unlink(reclock);
    482  1.1       cgd }
    483  1.1       cgd 
    484  1.4  christos void
    485  1.4  christos outheader()
    486  1.4  christos {
    487  1.4  christos 	char            linebuf[BUFSZ];
    488  1.4  christos 	char           *bp;
    489  1.1       cgd 	(void) strcpy(linebuf, "Number Points  Name");
    490  1.1       cgd 	bp = eos(linebuf);
    491  1.4  christos 	while (bp < linebuf + COLNO - 9)
    492  1.4  christos 		*bp++ = ' ';
    493  1.1       cgd 	(void) strcpy(bp, "Hp [max]");
    494  1.1       cgd 	puts(linebuf);
    495  1.1       cgd }
    496  1.1       cgd 
    497  1.1       cgd /* so>0: standout line; so=0: ordinary line; so<0: no output, return lth */
    498  1.1       cgd int
    499  1.5       jsm outentry(int rank, struct toptenentry *t1, int so)
    500  1.4  christos {
    501  1.4  christos 	boolean         quit = FALSE, killed = FALSE, starv = FALSE;
    502  1.4  christos 	char            linebuf[BUFSZ];
    503  1.1       cgd 	linebuf[0] = 0;
    504  1.4  christos 	if (rank)
    505  1.4  christos 		Sprintf(eos(linebuf), "%3d", rank);
    506  1.4  christos 	else
    507  1.4  christos 		Sprintf(eos(linebuf), "   ");
    508  1.1       cgd 	Sprintf(eos(linebuf), " %6ld %8s", t1->points, t1->name);
    509  1.4  christos 	if (t1->plchar == 'X')
    510  1.4  christos 		Sprintf(eos(linebuf), " ");
    511  1.4  christos 	else
    512  1.4  christos 		Sprintf(eos(linebuf), "-%c ", t1->plchar);
    513  1.4  christos 	if (!strncmp("escaped", t1->death, 7)) {
    514  1.4  christos 		if (!strcmp(" (with amulet)", t1->death + 7))
    515  1.4  christos 			Sprintf(eos(linebuf), "escaped the dungeon with amulet");
    516  1.4  christos 		else
    517  1.4  christos 			Sprintf(eos(linebuf), "escaped the dungeon [max level %d]",
    518  1.4  christos 				t1->maxlvl);
    519  1.1       cgd 	} else {
    520  1.4  christos 		if (!strncmp(t1->death, "quit", 4)) {
    521  1.4  christos 			quit = TRUE;
    522  1.4  christos 			if (t1->maxhp < 3 * t1->hp && t1->maxlvl < 4)
    523  1.4  christos 				Sprintf(eos(linebuf), "cravenly gave up");
    524  1.4  christos 			else
    525  1.4  christos 				Sprintf(eos(linebuf), "quit");
    526  1.4  christos 		} else if (!strcmp(t1->death, "choked"))
    527  1.4  christos 			Sprintf(eos(linebuf), "choked on %s food",
    528  1.4  christos 				(t1->sex == 'F') ? "her" : "his");
    529  1.4  christos 		else if (!strncmp(t1->death, "starv", 5))
    530  1.4  christos 			Sprintf(eos(linebuf), "starved to death"), starv = TRUE;
    531  1.4  christos 		else
    532  1.4  christos 			Sprintf(eos(linebuf), "was killed"), killed = TRUE;
    533  1.4  christos 		Sprintf(eos(linebuf), " on%s level %d",
    534  1.4  christos 			(killed || starv) ? "" : " dungeon", t1->level);
    535  1.4  christos 		if (t1->maxlvl != t1->level)
    536  1.4  christos 			Sprintf(eos(linebuf), " [max %d]", t1->maxlvl);
    537  1.4  christos 		if (quit && t1->death[4])
    538  1.4  christos 			Sprintf(eos(linebuf), t1->death + 4);
    539  1.4  christos 	}
    540  1.4  christos 	if (killed)
    541  1.4  christos 		Sprintf(eos(linebuf), " by %s%s",
    542  1.4  christos 			(!strncmp(t1->death, "trick", 5) || !strncmp(t1->death, "the ", 4))
    543  1.4  christos 			? "" :
    544  1.4  christos 			strchr(vowels, *t1->death) ? "an " : "a ",
    545  1.4  christos 			t1->death);
    546  1.1       cgd 	Sprintf(eos(linebuf), ".");
    547  1.4  christos 	if (t1->maxhp) {
    548  1.4  christos 		char           *bp = eos(linebuf);
    549  1.4  christos 		char            hpbuf[10];
    550  1.4  christos 		int             hppos;
    551  1.4  christos 		Sprintf(hpbuf, (t1->hp > 0) ? itoa(t1->hp) : "-");
    552  1.4  christos 		hppos = COLNO - 7 - strlen(hpbuf);
    553  1.4  christos 		if (bp <= linebuf + hppos) {
    554  1.4  christos 			while (bp < linebuf + hppos)
    555  1.4  christos 				*bp++ = ' ';
    556  1.4  christos 			(void) strcpy(bp, hpbuf);
    557  1.4  christos 			Sprintf(eos(bp), " [%d]", t1->maxhp);
    558  1.4  christos 		}
    559  1.4  christos 	}
    560  1.4  christos 	if (so == 0)
    561  1.4  christos 		puts(linebuf);
    562  1.4  christos 	else if (so > 0) {
    563  1.4  christos 		char           *bp = eos(linebuf);
    564  1.4  christos 		if (so >= COLNO)
    565  1.4  christos 			so = COLNO - 1;
    566  1.4  christos 		while (bp < linebuf + so)
    567  1.4  christos 			*bp++ = ' ';
    568  1.4  christos 		*bp = 0;
    569  1.4  christos 		standoutbeg();
    570  1.4  christos 		fputs(linebuf, stdout);
    571  1.4  christos 		standoutend();
    572  1.4  christos 		(void) putchar('\n');
    573  1.4  christos 	}
    574  1.4  christos 	return (strlen(linebuf));
    575  1.4  christos }
    576  1.4  christos 
    577  1.4  christos char           *
    578  1.4  christos itoa(a)
    579  1.4  christos 	int             a;
    580  1.4  christos {
    581  1.4  christos 	static char     buf[12];
    582  1.4  christos 	Sprintf(buf, "%d", a);
    583  1.4  christos 	return (buf);
    584  1.4  christos }
    585  1.4  christos 
    586  1.5       jsm const char           *
    587  1.4  christos ordin(n)
    588  1.4  christos 	int             n;
    589  1.4  christos {
    590  1.4  christos 	int             d = n % 10;
    591  1.4  christos 	return ((d == 0 || d > 3 || n / 10 == 1) ? "th" : (d == 1) ? "st" :
    592  1.4  christos 		(d == 2) ? "nd" : "rd");
    593  1.4  christos }
    594  1.4  christos 
    595  1.4  christos void
    596  1.4  christos clearlocks()
    597  1.4  christos {
    598  1.4  christos 	int x;
    599  1.4  christos 	(void) signal(SIGHUP, SIG_IGN);
    600  1.4  christos 	for (x = maxdlevel; x >= 0; x--) {
    601  1.1       cgd 		glo(x);
    602  1.1       cgd 		(void) unlink(lock);	/* not all levels need be present */
    603  1.1       cgd 	}
    604  1.1       cgd }
    605  1.1       cgd 
    606  1.1       cgd #ifdef NOSAVEONHANGUP
    607  1.4  christos /*ARGSUSED*/
    608  1.4  christos void
    609  1.4  christos hangup(n)
    610  1.4  christos 	int n;
    611  1.1       cgd {
    612  1.1       cgd 	(void) signal(SIGINT, SIG_IGN);
    613  1.1       cgd 	clearlocks();
    614  1.1       cgd 	exit(1);
    615  1.1       cgd }
    616  1.4  christos #endif	/* NOSAVEONHANGUP */
    617  1.1       cgd 
    618  1.4  christos char           *
    619  1.1       cgd eos(s)
    620  1.4  christos 	char           *s;
    621  1.1       cgd {
    622  1.4  christos 	while (*s)
    623  1.4  christos 		s++;
    624  1.4  christos 	return (s);
    625  1.1       cgd }
    626  1.1       cgd 
    627  1.1       cgd /* it is the callers responsibility to check that there is room for c */
    628  1.4  christos void
    629  1.4  christos charcat(s, c)
    630  1.4  christos 	char           *s, c;
    631  1.4  christos {
    632  1.4  christos 	while (*s)
    633  1.4  christos 		s++;
    634  1.1       cgd 	*s++ = c;
    635  1.1       cgd 	*s = 0;
    636  1.1       cgd }
    637  1.1       cgd 
    638  1.1       cgd /*
    639  1.1       cgd  * Called with args from main if argc >= 0. In this case, list scores as
    640  1.1       cgd  * requested. Otherwise, find scores for the current player (and list them
    641  1.1       cgd  * if argc == -1).
    642  1.1       cgd  */
    643  1.4  christos void
    644  1.4  christos prscore(argc, argv)
    645  1.4  christos 	int             argc;
    646  1.4  christos 	char          **argv;
    647  1.4  christos {
    648  1.4  christos 	char          **players = NULL;
    649  1.4  christos 	int             playerct;
    650  1.4  christos 	int             rank;
    651  1.4  christos 	struct toptenentry *t1, *t2;
    652  1.5       jsm 	const char           *recfile = RECORD;
    653  1.4  christos 	FILE           *rfile;
    654  1.4  christos 	int		flg = 0;
    655  1.4  christos 	int             i;
    656  1.1       cgd #ifdef nonsense
    657  1.4  christos 	long            total_score = 0L;
    658  1.4  christos 	char            totchars[10];
    659  1.4  christos 	int             totcharct = 0;
    660  1.4  christos #endif	/* nonsense */
    661  1.4  christos 	int             outflg = (argc >= -1);
    662  1.1       cgd #ifdef PERS_IS_UID
    663  1.4  christos 	int             uid = -1;
    664  1.1       cgd #else
    665  1.4  christos 	char           *player0;
    666  1.4  christos #endif	/* PERS_IS_UID */
    667  1.1       cgd 
    668  1.4  christos 	if (!(rfile = fopen(recfile, "r"))) {
    669  1.1       cgd 		puts("Cannot open record file!");
    670  1.1       cgd 		return;
    671  1.1       cgd 	}
    672  1.4  christos 	if (argc > 1 && !strncmp(argv[1], "-s", 2)) {
    673  1.4  christos 		if (!argv[1][2]) {
    674  1.1       cgd 			argc--;
    675  1.1       cgd 			argv++;
    676  1.4  christos 		} else if (!argv[1][3] && strchr("CFKSTWX", argv[1][2])) {
    677  1.1       cgd 			argv[1]++;
    678  1.1       cgd 			argv[1][0] = '-';
    679  1.4  christos 		} else
    680  1.4  christos 			argv[1] += 2;
    681  1.1       cgd 	}
    682  1.4  christos 	if (argc <= 1) {
    683  1.1       cgd #ifdef PERS_IS_UID
    684  1.1       cgd 		uid = getuid();
    685  1.1       cgd 		playerct = 0;
    686  1.1       cgd #else
    687  1.1       cgd 		player0 = plname;
    688  1.4  christos 		if (!*player0)
    689  1.1       cgd 			player0 = "hackplayer";
    690  1.1       cgd 		playerct = 1;
    691  1.1       cgd 		players = &player0;
    692  1.4  christos #endif	/* PERS_IS_UID */
    693  1.1       cgd 	} else {
    694  1.1       cgd 		playerct = --argc;
    695  1.1       cgd 		players = ++argv;
    696  1.1       cgd 	}
    697  1.4  christos 	if (outflg)
    698  1.4  christos 		putchar('\n');
    699  1.1       cgd 
    700  1.1       cgd 	t1 = tt_head = newttentry();
    701  1.4  christos 	for (rank = 1;; rank++) {
    702  1.4  christos 		if (fscanf(rfile, "%6s %d %d %d %d %d %ld %c%c %[^,],%[^\n]",
    703  1.4  christos 			   t1->date, &t1->uid,
    704  1.4  christos 			   &t1->level, &t1->maxlvl,
    705  1.4  christos 			   &t1->hp, &t1->maxhp, &t1->points,
    706  1.4  christos 			   &t1->plchar, &t1->sex, t1->name, t1->death) != 11)
    707  1.1       cgd 			t1->points = 0;
    708  1.4  christos 		if (t1->points == 0)
    709  1.4  christos 			break;
    710  1.1       cgd #ifdef PERS_IS_UID
    711  1.4  christos 		if (!playerct && t1->uid == uid)
    712  1.1       cgd 			flg++;
    713  1.4  christos 		else
    714  1.4  christos #endif	/* PERS_IS_UID */
    715  1.4  christos 			for (i = 0; i < playerct; i++) {
    716  1.4  christos 				if (strcmp(players[i], "all") == 0 ||
    717  1.4  christos 				strncmp(t1->name, players[i], NAMSZ) == 0 ||
    718  1.4  christos 				    (players[i][0] == '-' &&
    719  1.4  christos 				     players[i][1] == t1->plchar &&
    720  1.4  christos 				     players[i][2] == 0) ||
    721  1.4  christos 				    (digit(players[i][0]) && rank <= atoi(players[i])))
    722  1.4  christos 					flg++;
    723  1.4  christos 			}
    724  1.4  christos 		t1 = t1->tt_next = newttentry();
    725  1.1       cgd 	}
    726  1.1       cgd 	(void) fclose(rfile);
    727  1.4  christos 	if (!flg) {
    728  1.4  christos 		if (outflg) {
    729  1.4  christos 			printf("Cannot find any entries for ");
    730  1.4  christos 			if (playerct < 1)
    731  1.4  christos 				printf("you.\n");
    732  1.4  christos 			else {
    733  1.4  christos 				if (playerct > 1)
    734  1.4  christos 					printf("any of ");
    735  1.4  christos 				for (i = 0; i < playerct; i++)
    736  1.4  christos 					printf("%s%s", players[i], (i < playerct - 1) ? ", " : ".\n");
    737  1.4  christos 				printf("Call is: %s -s [playernames]\n", hname);
    738  1.4  christos 			}
    739  1.1       cgd 		}
    740  1.4  christos 		return;
    741  1.1       cgd 	}
    742  1.4  christos 	if (outflg)
    743  1.4  christos 		outheader();
    744  1.1       cgd 	t1 = tt_head;
    745  1.4  christos 	for (rank = 1; t1->points != 0; rank++, t1 = t2) {
    746  1.1       cgd 		t2 = t1->tt_next;
    747  1.1       cgd #ifdef PERS_IS_UID
    748  1.4  christos 		if (!playerct && t1->uid == uid)
    749  1.1       cgd 			goto outwithit;
    750  1.1       cgd 		else
    751  1.4  christos #endif	/* PERS_IS_UID */
    752  1.4  christos 			for (i = 0; i < playerct; i++) {
    753  1.4  christos 				if (strcmp(players[i], "all") == 0 ||
    754  1.4  christos 				strncmp(t1->name, players[i], NAMSZ) == 0 ||
    755  1.4  christos 				    (players[i][0] == '-' &&
    756  1.4  christos 				     players[i][1] == t1->plchar &&
    757  1.4  christos 				     players[i][2] == 0) ||
    758  1.4  christos 				    (digit(players[i][0]) && rank <= atoi(players[i]))) {
    759  1.1       cgd 			outwithit:
    760  1.4  christos 					if (outflg)
    761  1.4  christos 						(void) outentry(rank, t1, 0);
    762  1.1       cgd #ifdef nonsense
    763  1.4  christos 					total_score += t1->points;
    764  1.4  christos 					if (totcharct < sizeof(totchars) - 1)
    765  1.4  christos 						totchars[totcharct++] = t1->plchar;
    766  1.4  christos #endif	/* nonsense */
    767  1.4  christos 					break;
    768  1.4  christos 				}
    769  1.1       cgd 			}
    770  1.1       cgd 		free((char *) t1);
    771  1.1       cgd 	}
    772  1.1       cgd #ifdef nonsense
    773  1.1       cgd 	totchars[totcharct] = 0;
    774  1.1       cgd 
    775  1.4  christos 	/*
    776  1.4  christos 	 * We would like to determine whether he is experienced. However, the
    777  1.4  christos 	 * information collected here only tells about the scores/roles that
    778  1.4  christos 	 * got into the topten (top 100?). We should maintain a .hacklog or
    779  1.4  christos 	 * something in his home directory.
    780  1.4  christos 	 */
    781  1.1       cgd 	flags.beginner = (total_score < 6000);
    782  1.4  christos 	for (i = 0; i < 6; i++)
    783  1.4  christos 		if (!strchr(totchars, "CFKSTWX"[i])) {
    784  1.4  christos 			flags.beginner = 1;
    785  1.4  christos 			if (!pl_character[0])
    786  1.4  christos 				pl_character[0] = "CFKSTWX"[i];
    787  1.4  christos 			break;
    788  1.4  christos 		}
    789  1.4  christos #endif	/* nonsense */
    790  1.1       cgd }
    791