1*42ceebb3Sderaadt /* $OpenBSD: scores.c,v 1.8 2003/04/06 18:50:38 deraadt Exp $ */ 2df930be7Sderaadt /* $NetBSD: scores.c,v 1.2 1995/04/22 07:42:38 cgd Exp $ */ 3df930be7Sderaadt 4df930be7Sderaadt /*- 5df930be7Sderaadt * Copyright (c) 1992, 1993 6df930be7Sderaadt * The Regents of the University of California. All rights reserved. 7df930be7Sderaadt * 8df930be7Sderaadt * This code is derived from software contributed to Berkeley by 9df930be7Sderaadt * Chris Torek and Darren F. Provine. 10df930be7Sderaadt * 11df930be7Sderaadt * Redistribution and use in source and binary forms, with or without 12df930be7Sderaadt * modification, are permitted provided that the following conditions 13df930be7Sderaadt * are met: 14df930be7Sderaadt * 1. Redistributions of source code must retain the above copyright 15df930be7Sderaadt * notice, this list of conditions and the following disclaimer. 16df930be7Sderaadt * 2. Redistributions in binary form must reproduce the above copyright 17df930be7Sderaadt * notice, this list of conditions and the following disclaimer in the 18df930be7Sderaadt * documentation and/or other materials provided with the distribution. 19df930be7Sderaadt * 3. All advertising materials mentioning features or use of this software 20df930be7Sderaadt * must display the following acknowledgement: 21df930be7Sderaadt * This product includes software developed by the University of 22df930be7Sderaadt * California, Berkeley and its contributors. 23df930be7Sderaadt * 4. Neither the name of the University nor the names of its contributors 24df930be7Sderaadt * may be used to endorse or promote products derived from this software 25df930be7Sderaadt * without specific prior written permission. 26df930be7Sderaadt * 27df930be7Sderaadt * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 28df930be7Sderaadt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29df930be7Sderaadt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30df930be7Sderaadt * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 31df930be7Sderaadt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 32df930be7Sderaadt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 33df930be7Sderaadt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 34df930be7Sderaadt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 35df930be7Sderaadt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 36df930be7Sderaadt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 37df930be7Sderaadt * SUCH DAMAGE. 38df930be7Sderaadt * 39df930be7Sderaadt * @(#)scores.c 8.1 (Berkeley) 5/31/93 40df930be7Sderaadt */ 41df930be7Sderaadt 42df930be7Sderaadt /* 43df930be7Sderaadt * Score code for Tetris, by Darren Provine (kilroy@gboro.glassboro.edu) 44df930be7Sderaadt * modified 22 January 1992, to limit the number of entries any one 45df930be7Sderaadt * person has. 46df930be7Sderaadt * 47df930be7Sderaadt * Major whacks since then. 48df930be7Sderaadt */ 49df930be7Sderaadt #include <errno.h> 50dcf934a0Spjanzen #include <err.h> 51df930be7Sderaadt #include <fcntl.h> 52df930be7Sderaadt #include <pwd.h> 53df930be7Sderaadt #include <stdio.h> 54df930be7Sderaadt #include <stdlib.h> 55df930be7Sderaadt #include <string.h> 56df930be7Sderaadt #include <time.h> 57dcf934a0Spjanzen #include <term.h> 58df930be7Sderaadt #include <unistd.h> 591804aebcSpjanzen #include <sys/param.h> 6075b7a267Stholo #include <sys/stat.h> 61dcf934a0Spjanzen #include <sys/types.h> 62df930be7Sderaadt 63df930be7Sderaadt #include "pathnames.h" 64df930be7Sderaadt #include "screen.h" 65df930be7Sderaadt #include "scores.h" 66df930be7Sderaadt #include "tetris.h" 67df930be7Sderaadt 68df930be7Sderaadt /* 69df930be7Sderaadt * Within this code, we can hang onto one extra "high score", leaving 70df930be7Sderaadt * room for our current score (whether or not it is high). 71df930be7Sderaadt * 72df930be7Sderaadt * We also sometimes keep tabs on the "highest" score on each level. 73df930be7Sderaadt * As long as the scores are kept sorted, this is simply the first one at 74df930be7Sderaadt * that level. 75df930be7Sderaadt */ 76df930be7Sderaadt #define NUMSPOTS (MAXHISCORES + 1) 77df930be7Sderaadt #define NLEVELS (MAXLEVEL + 1) 78df930be7Sderaadt 79df930be7Sderaadt static time_t now; 80df930be7Sderaadt static int nscores; 81df930be7Sderaadt static int gotscores; 82df930be7Sderaadt static struct highscore scores[NUMSPOTS]; 83df930be7Sderaadt 84c72b5b24Smillert static int checkscores(struct highscore *, int); 85c72b5b24Smillert static int cmpscores(const void *, const void *); 86c72b5b24Smillert static void getscores(FILE **); 87c72b5b24Smillert static void printem(int, int, struct highscore *, int, const char *); 88c72b5b24Smillert static char *thisuser(void); 89df930be7Sderaadt 90df930be7Sderaadt /* 91df930be7Sderaadt * Read the score file. Can be called from savescore (before showscores) 92df930be7Sderaadt * or showscores (if savescore will not be called). If the given pointer 93df930be7Sderaadt * is not NULL, sets *fpp to an open file pointer that corresponds to a 94df930be7Sderaadt * read/write score file that is locked with LOCK_EX. Otherwise, the 95df930be7Sderaadt * file is locked with LOCK_SH for the read and closed before return. 96df930be7Sderaadt * 97df930be7Sderaadt * Note, we assume closing the stdio file releases the lock. 98df930be7Sderaadt */ 99df930be7Sderaadt static void 100df930be7Sderaadt getscores(fpp) 101df930be7Sderaadt FILE **fpp; 102df930be7Sderaadt { 103f9f452a4Spjanzen int sd, mint, lck, mask, i; 104df930be7Sderaadt char *mstr, *human; 105df930be7Sderaadt FILE *sf; 106df930be7Sderaadt 107df930be7Sderaadt if (fpp != NULL) { 108df930be7Sderaadt mint = O_RDWR | O_CREAT; 109df930be7Sderaadt mstr = "r+"; 110df930be7Sderaadt human = "read/write"; 111df930be7Sderaadt lck = LOCK_EX; 112df930be7Sderaadt } else { 113df930be7Sderaadt mint = O_RDONLY; 114df930be7Sderaadt mstr = "r"; 115df930be7Sderaadt human = "reading"; 116df930be7Sderaadt lck = LOCK_SH; 117df930be7Sderaadt } 11875b7a267Stholo setegid(egid); 11975b7a267Stholo mask = umask(S_IWOTH); 120df930be7Sderaadt sd = open(_PATH_SCOREFILE, mint, 0666); 12175b7a267Stholo (void)umask(mask); 122dcf934a0Spjanzen setegid(gid); 123df930be7Sderaadt if (sd < 0) { 124df930be7Sderaadt if (fpp == NULL) { 125df930be7Sderaadt nscores = 0; 126df930be7Sderaadt return; 127df930be7Sderaadt } 128dcf934a0Spjanzen err(1, "cannot open %s for %s", _PATH_SCOREFILE, human); 129df930be7Sderaadt } 130dcf934a0Spjanzen setegid(egid); 131dcf934a0Spjanzen if ((sf = fdopen(sd, mstr)) == NULL) 132dcf934a0Spjanzen err(1, "cannot fdopen %s for %s", _PATH_SCOREFILE, human); 13375b7a267Stholo setegid(gid); 134df930be7Sderaadt 135df930be7Sderaadt /* 136df930be7Sderaadt * Grab a lock. 137df930be7Sderaadt */ 138df930be7Sderaadt if (flock(sd, lck)) 139dcf934a0Spjanzen warn("warning: score file %s cannot be locked", 140dcf934a0Spjanzen _PATH_SCOREFILE); 141df930be7Sderaadt 142df930be7Sderaadt nscores = fread(scores, sizeof(scores[0]), MAXHISCORES, sf); 143dcf934a0Spjanzen if (ferror(sf)) 144dcf934a0Spjanzen err(1, "error reading %s", _PATH_SCOREFILE); 145f9f452a4Spjanzen for (i = 0; i < nscores; i++) 146f9f452a4Spjanzen if (scores[i].hs_level < MINLEVEL || 147f9f452a4Spjanzen scores[i].hs_level > MAXLEVEL) 148f9f452a4Spjanzen errx(1, "scorefile %s corrupt", _PATH_SCOREFILE); 149df930be7Sderaadt 150df930be7Sderaadt if (fpp) 151df930be7Sderaadt *fpp = sf; 152df930be7Sderaadt else 153df930be7Sderaadt (void)fclose(sf); 154df930be7Sderaadt } 155df930be7Sderaadt 156df930be7Sderaadt void 157df930be7Sderaadt savescore(level) 158df930be7Sderaadt int level; 159df930be7Sderaadt { 16097419aa0Spjanzen struct highscore *sp; 16197419aa0Spjanzen int i; 162df930be7Sderaadt int change; 163df930be7Sderaadt FILE *sf; 164df930be7Sderaadt const char *me; 165df930be7Sderaadt 166df930be7Sderaadt getscores(&sf); 167df930be7Sderaadt gotscores = 1; 168df930be7Sderaadt (void)time(&now); 169df930be7Sderaadt 170df930be7Sderaadt /* 171df930be7Sderaadt * Allow at most one score per person per level -- see if we 172df930be7Sderaadt * can replace an existing score, or (easiest) do nothing. 173df930be7Sderaadt * Otherwise add new score at end (there is always room). 174df930be7Sderaadt */ 175df930be7Sderaadt change = 0; 176df930be7Sderaadt me = thisuser(); 177df930be7Sderaadt for (i = 0, sp = &scores[0]; i < nscores; i++, sp++) { 178df930be7Sderaadt if (sp->hs_level != level || strcmp(sp->hs_name, me) != 0) 179df930be7Sderaadt continue; 180df930be7Sderaadt if (score > sp->hs_score) { 181df930be7Sderaadt (void)printf("%s bettered %s %d score of %d!\n", 182df930be7Sderaadt "\nYou", "your old level", level, 183df930be7Sderaadt sp->hs_score * sp->hs_level); 184df930be7Sderaadt sp->hs_score = score; /* new score */ 185df930be7Sderaadt sp->hs_time = now; /* and time */ 186df930be7Sderaadt change = 1; 187df930be7Sderaadt } else if (score == sp->hs_score) { 188df930be7Sderaadt (void)printf("%s tied %s %d high score.\n", 189df930be7Sderaadt "\nYou", "your old level", level); 190df930be7Sderaadt sp->hs_time = now; /* renew it */ 191df930be7Sderaadt change = 1; /* gotta rewrite, sigh */ 192df930be7Sderaadt } /* else new score < old score: do nothing */ 193df930be7Sderaadt break; 194df930be7Sderaadt } 195df930be7Sderaadt if (i >= nscores) { 196*42ceebb3Sderaadt strlcpy(sp->hs_name, me, sizeof sp->hs_name); 197df930be7Sderaadt sp->hs_level = level; 198df930be7Sderaadt sp->hs_score = score; 199df930be7Sderaadt sp->hs_time = now; 200df930be7Sderaadt nscores++; 201df930be7Sderaadt change = 1; 202df930be7Sderaadt } 203df930be7Sderaadt 204df930be7Sderaadt if (change) { 205df930be7Sderaadt /* 206df930be7Sderaadt * Sort & clean the scores, then rewrite. 207df930be7Sderaadt */ 208df930be7Sderaadt nscores = checkscores(scores, nscores); 209df930be7Sderaadt rewind(sf); 210df930be7Sderaadt if (fwrite(scores, sizeof(*sp), nscores, sf) != nscores || 211df930be7Sderaadt fflush(sf) == EOF) 212f9f452a4Spjanzen warnx("error writing %s: %s\n\t-- %s", 213df930be7Sderaadt _PATH_SCOREFILE, strerror(errno), 214df930be7Sderaadt "high scores may be damaged"); 215df930be7Sderaadt } 216df930be7Sderaadt (void)fclose(sf); /* releases lock */ 217df930be7Sderaadt } 218df930be7Sderaadt 219df930be7Sderaadt /* 220df930be7Sderaadt * Get login name, or if that fails, get something suitable. 221df930be7Sderaadt * The result is always trimmed to fit in a score. 222df930be7Sderaadt */ 223df930be7Sderaadt static char * 224df930be7Sderaadt thisuser() 225df930be7Sderaadt { 2261804aebcSpjanzen const char *p; 2271804aebcSpjanzen struct passwd *pw; 228df930be7Sderaadt static char u[sizeof(scores[0].hs_name)]; 229df930be7Sderaadt 230df930be7Sderaadt if (u[0]) 231df930be7Sderaadt return (u); 232df930be7Sderaadt p = getlogin(); 233df930be7Sderaadt if (p == NULL || *p == '\0') { 234df930be7Sderaadt pw = getpwuid(getuid()); 235df930be7Sderaadt if (pw != NULL) 236df930be7Sderaadt p = pw->pw_name; 237df930be7Sderaadt else 238df930be7Sderaadt p = " ???"; 239df930be7Sderaadt } 2401804aebcSpjanzen strlcpy(u, p, sizeof(u)); 241df930be7Sderaadt return (u); 242df930be7Sderaadt } 243df930be7Sderaadt 244df930be7Sderaadt /* 245df930be7Sderaadt * Score comparison function for qsort. 246df930be7Sderaadt * 247df930be7Sderaadt * If two scores are equal, the person who had the score first is 248df930be7Sderaadt * listed first in the highscore file. 249df930be7Sderaadt */ 250df930be7Sderaadt static int 251df930be7Sderaadt cmpscores(x, y) 252df930be7Sderaadt const void *x, *y; 253df930be7Sderaadt { 25497419aa0Spjanzen const struct highscore *a, *b; 25597419aa0Spjanzen long l; 256df930be7Sderaadt 257df930be7Sderaadt a = x; 258df930be7Sderaadt b = y; 259df930be7Sderaadt l = (long)b->hs_level * b->hs_score - (long)a->hs_level * a->hs_score; 260df930be7Sderaadt if (l < 0) 261df930be7Sderaadt return (-1); 262df930be7Sderaadt if (l > 0) 263df930be7Sderaadt return (1); 264df930be7Sderaadt if (a->hs_time < b->hs_time) 265df930be7Sderaadt return (-1); 266df930be7Sderaadt if (a->hs_time > b->hs_time) 267df930be7Sderaadt return (1); 268df930be7Sderaadt return (0); 269df930be7Sderaadt } 270df930be7Sderaadt 271df930be7Sderaadt /* 272df930be7Sderaadt * If we've added a score to the file, we need to check the file and ensure 273df930be7Sderaadt * that this player has only a few entries. The number of entries is 274df930be7Sderaadt * controlled by MAXSCORES, and is to ensure that the highscore file is not 275df930be7Sderaadt * monopolised by just a few people. People who no longer have accounts are 276df930be7Sderaadt * only allowed the highest score. Scores older than EXPIRATION seconds are 277df930be7Sderaadt * removed, unless they are someone's personal best. 278df930be7Sderaadt * Caveat: the highest score on each level is always kept. 279df930be7Sderaadt */ 280df930be7Sderaadt static int 281df930be7Sderaadt checkscores(hs, num) 28297419aa0Spjanzen struct highscore *hs; 283df930be7Sderaadt int num; 284df930be7Sderaadt { 28597419aa0Spjanzen struct highscore *sp; 28697419aa0Spjanzen int i, j, k, numnames; 287df930be7Sderaadt int levelfound[NLEVELS]; 288df930be7Sderaadt struct peruser { 289df930be7Sderaadt char *name; 290df930be7Sderaadt int times; 291df930be7Sderaadt } count[NUMSPOTS]; 29297419aa0Spjanzen struct peruser *pu; 293df930be7Sderaadt 294df930be7Sderaadt /* 295df930be7Sderaadt * Sort so that highest totals come first. 296df930be7Sderaadt * 297df930be7Sderaadt * levelfound[i] becomes set when the first high score for that 298df930be7Sderaadt * level is encountered. By definition this is the highest score. 299df930be7Sderaadt */ 300df930be7Sderaadt qsort((void *)hs, nscores, sizeof(*hs), cmpscores); 301df930be7Sderaadt for (i = MINLEVEL; i < NLEVELS; i++) 302df930be7Sderaadt levelfound[i] = 0; 303df930be7Sderaadt numnames = 0; 304df930be7Sderaadt for (i = 0, sp = hs; i < num;) { 305df930be7Sderaadt /* 306df930be7Sderaadt * This is O(n^2), but do you think we care? 307df930be7Sderaadt */ 308df930be7Sderaadt for (j = 0, pu = count; j < numnames; j++, pu++) 309df930be7Sderaadt if (strcmp(sp->hs_name, pu->name) == 0) 310df930be7Sderaadt break; 311df930be7Sderaadt if (j == numnames) { 312df930be7Sderaadt /* 313df930be7Sderaadt * Add new user, set per-user count to 1. 314df930be7Sderaadt */ 315df930be7Sderaadt pu->name = sp->hs_name; 316df930be7Sderaadt pu->times = 1; 317df930be7Sderaadt numnames++; 318df930be7Sderaadt } else { 319df930be7Sderaadt /* 320df930be7Sderaadt * Two ways to keep this score: 321df930be7Sderaadt * - Not too many (per user), still has acct, & 322df930be7Sderaadt * score not dated; or 323df930be7Sderaadt * - High score on this level. 324df930be7Sderaadt */ 325df930be7Sderaadt if ((pu->times < MAXSCORES && 326df930be7Sderaadt getpwnam(sp->hs_name) != NULL && 327df930be7Sderaadt sp->hs_time + EXPIRATION >= now) || 328df930be7Sderaadt levelfound[sp->hs_level] == 0) 329df930be7Sderaadt pu->times++; 330df930be7Sderaadt else { 331df930be7Sderaadt /* 332df930be7Sderaadt * Delete this score, do not count it, 333df930be7Sderaadt * do not pass go, do not collect $200. 334df930be7Sderaadt */ 335df930be7Sderaadt num--; 336df930be7Sderaadt for (k = i; k < num; k++) 337df930be7Sderaadt hs[k] = hs[k + 1]; 338df930be7Sderaadt continue; 339df930be7Sderaadt } 340df930be7Sderaadt } 341df930be7Sderaadt levelfound[sp->hs_level] = 1; 342df930be7Sderaadt i++, sp++; 343df930be7Sderaadt } 344df930be7Sderaadt return (num > MAXHISCORES ? MAXHISCORES : num); 345df930be7Sderaadt } 346df930be7Sderaadt 347df930be7Sderaadt /* 348df930be7Sderaadt * Show current scores. This must be called after savescore, if 349df930be7Sderaadt * savescore is called at all, for two reasons: 350df930be7Sderaadt * - Showscores munches the time field. 351df930be7Sderaadt * - Even if that were not the case, a new score must be recorded 352df930be7Sderaadt * before it can be shown anyway. 353df930be7Sderaadt */ 354df930be7Sderaadt void 355df930be7Sderaadt showscores(level) 356df930be7Sderaadt int level; 357df930be7Sderaadt { 35897419aa0Spjanzen struct highscore *sp; 35997419aa0Spjanzen int i, n, c; 360df930be7Sderaadt const char *me; 361df930be7Sderaadt int levelfound[NLEVELS]; 362df930be7Sderaadt 363df930be7Sderaadt if (!gotscores) 364df930be7Sderaadt getscores((FILE **)NULL); 3651804aebcSpjanzen (void)printf("\n\t\t Tetris High Scores\n"); 366df930be7Sderaadt 367df930be7Sderaadt /* 368df930be7Sderaadt * If level == 0, the person has not played a game but just asked for 369df930be7Sderaadt * the high scores; we do not need to check for printing in highlight 370df930be7Sderaadt * mode. If SOstr is null, we can't do highlighting anyway. 371df930be7Sderaadt */ 372df930be7Sderaadt me = level && SOstr ? thisuser() : NULL; 373df930be7Sderaadt 374df930be7Sderaadt /* 375df930be7Sderaadt * Set times to 0 except for high score on each level. 376df930be7Sderaadt */ 377df930be7Sderaadt for (i = MINLEVEL; i < NLEVELS; i++) 378df930be7Sderaadt levelfound[i] = 0; 379df930be7Sderaadt for (i = 0, sp = scores; i < nscores; i++, sp++) { 380df930be7Sderaadt if (levelfound[sp->hs_level]) 381df930be7Sderaadt sp->hs_time = 0; 382df930be7Sderaadt else { 383df930be7Sderaadt sp->hs_time = 1; 384df930be7Sderaadt levelfound[sp->hs_level] = 1; 385df930be7Sderaadt } 386df930be7Sderaadt } 387df930be7Sderaadt 388df930be7Sderaadt /* 389df930be7Sderaadt * Page each screenful of scores. 390df930be7Sderaadt */ 391df930be7Sderaadt for (i = 0, sp = scores; i < nscores; sp += n) { 3921804aebcSpjanzen n = 20; 393df930be7Sderaadt if (i + n > nscores) 394df930be7Sderaadt n = nscores - i; 395df930be7Sderaadt printem(level, i + 1, sp, n, me); 396df930be7Sderaadt if ((i += n) < nscores) { 397df930be7Sderaadt (void)printf("\nHit RETURN to continue."); 398df930be7Sderaadt (void)fflush(stdout); 399df930be7Sderaadt while ((c = getchar()) != '\n') 400df930be7Sderaadt if (c == EOF) 401df930be7Sderaadt break; 402df930be7Sderaadt (void)printf("\n"); 403df930be7Sderaadt } 404df930be7Sderaadt } 405f9f452a4Spjanzen 406f9f452a4Spjanzen if (nscores == 0) 407f9f452a4Spjanzen printf("\t\t\t - none to date.\n"); 408df930be7Sderaadt } 409df930be7Sderaadt 410df930be7Sderaadt static void 411df930be7Sderaadt printem(level, offset, hs, n, me) 412df930be7Sderaadt int level, offset; 4131804aebcSpjanzen struct highscore *hs; 4141804aebcSpjanzen int n; 415df930be7Sderaadt const char *me; 416df930be7Sderaadt { 4171804aebcSpjanzen struct highscore *sp; 4181804aebcSpjanzen int row, highlight, i; 419df930be7Sderaadt char buf[100]; 420df930be7Sderaadt #define TITLE "Rank Score Name (points/level)" 4211804aebcSpjanzen #define TITL2 "==========================================================" 422df930be7Sderaadt 4231804aebcSpjanzen printf("%s\n%s\n", TITLE, TITL2); 424df930be7Sderaadt 425df930be7Sderaadt highlight = 0; 426df930be7Sderaadt 4271804aebcSpjanzen for (row = 0; row < n; row++) { 4281804aebcSpjanzen sp = &hs[row]; 4291804aebcSpjanzen (void)snprintf(buf, 100, 4301804aebcSpjanzen "%3d%c %6d %-31s (%6d on %d)\n", 4311804aebcSpjanzen row + offset, sp->hs_time ? '*' : ' ', 432df930be7Sderaadt sp->hs_score * sp->hs_level, 433df930be7Sderaadt sp->hs_name, sp->hs_score, sp->hs_level); 4341804aebcSpjanzen /* Print leaders every three lines */ 4351804aebcSpjanzen if ((row + 1) % 3 == 0) { 4361804aebcSpjanzen for (i = 0; i < 100; i++) 4371804aebcSpjanzen if (buf[i] == ' ') 4381804aebcSpjanzen buf[i] = '_'; 4391804aebcSpjanzen } 440df930be7Sderaadt /* 441df930be7Sderaadt * Highlight if appropriate. This works because 442df930be7Sderaadt * we only get one score per level. 443df930be7Sderaadt */ 444df930be7Sderaadt if (me != NULL && 445df930be7Sderaadt sp->hs_level == level && 446df930be7Sderaadt sp->hs_score == score && 447df930be7Sderaadt strcmp(sp->hs_name, me) == 0) { 448df930be7Sderaadt putpad(SOstr); 449df930be7Sderaadt highlight = 1; 450df930be7Sderaadt } 451df930be7Sderaadt (void)printf("%s", buf); 452df930be7Sderaadt if (highlight) { 453df930be7Sderaadt putpad(SEstr); 454df930be7Sderaadt highlight = 0; 455df930be7Sderaadt } 456df930be7Sderaadt } 457df930be7Sderaadt } 458