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