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