1/* $OpenBSD: log.c,v 1.25 2019/06/28 13:32:52 deraadt Exp $ */ 2/* $NetBSD: log.c,v 1.3 1995/03/21 15:04:21 cgd Exp $ */ 3 4/*- 5 * Copyright (c) 1990, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * This code is derived from software contributed to Berkeley by 9 * Ed James. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36/* 37 * Copyright (c) 1987 by Ed James, UC Berkeley. All rights reserved. 38 * 39 * Copy permission is hereby granted provided that this notice is 40 * retained on all partial or complete copies. 41 * 42 * For more info on this and all of my stuff, mail edjames@berkeley.edu. 43 */ 44 45#include <sys/stat.h> 46#include <sys/types.h> 47 48#include <err.h> 49#include <errno.h> 50#include <fcntl.h> 51#include <stdlib.h> 52#include <string.h> 53#include <time.h> 54#include <unistd.h> 55 56#include "extern.h" 57#include "pathnames.h" 58 59static FILE *score_fp; 60 61int 62compar(const void *va, const void *vb) 63{ 64 const SCORE *a, *b; 65 66 a = (const SCORE *)va; 67 b = (const SCORE *)vb; 68 if (b->planes == a->planes) 69 return (b->time - a->time); 70 else 71 return (b->planes - a->planes); 72} 73 74#define SECAMIN 60 75#define MINAHOUR 60 76#define HOURADAY 24 77#define SECAHOUR (SECAMIN * MINAHOUR) 78#define SECADAY (SECAHOUR * HOURADAY) 79#define DAY(t) ((t) / SECADAY) 80#define HOUR(t) (((t) % SECADAY) / SECAHOUR) 81#define MINUTES(t) (((t) % SECAHOUR) / SECAMIN) 82#define SEC(t) ((t) % SECAMIN) 83 84const char * 85timestr(int t) 86{ 87 static char s[80]; 88 89 if (DAY(t) > 0) 90 (void)snprintf(s, sizeof s, "%dd+%02dhrs", DAY(t), HOUR(t)); 91 else if (HOUR(t) > 0) 92 (void)snprintf(s, sizeof s, "%d:%02d:%02d", 93 HOUR(t), MINUTES(t), SEC(t)); 94 else if (MINUTES(t) > 0) 95 (void)snprintf(s, sizeof s, "%d:%02d", MINUTES(t), SEC(t)); 96 else if (SEC(t) > 0) 97 (void)snprintf(s, sizeof s, ":%02d", SEC(t)); 98 else 99 *s = '\0'; 100 101 return (s); 102} 103 104int 105open_score_file(void) 106{ 107 mode_t old_mode; 108 char *home; 109 char scorefile[PATH_MAX]; 110 int ret; 111 int score_fd; 112 113 home = getenv("HOME"); 114 if (home == NULL || *home == '\0') 115 err(1, "getenv"); 116 ret = snprintf(scorefile, sizeof(scorefile), "%s/%s", home, 117 ".atc.scores"); 118 if (ret < 0 || ret >= PATH_MAX) 119 errc(1, ENAMETOOLONG, "%s/%s", home, ".atc.scores"); 120 121 old_mode = umask(0); 122 score_fd = open(scorefile, O_CREAT|O_RDWR, 0644); 123 if (score_fd == -1) 124 err(1, "open"); 125 /* 126 * This is done to take advantage of stdio, while still 127 * allowing a O_CREAT during the open(2) of the log file. 128 */ 129 score_fp = fdopen(score_fd, "r+"); 130 if (score_fp == NULL) 131 err(1, "fdopen"); 132 umask(old_mode); 133 return (0); 134} 135 136int 137log_score(int list_em) 138{ 139 int i, num_scores = 0, good, changed = 0, found = 0; 140 const char *name; 141 char *cp; 142 char scanstr[50]; 143 SCORE score[NUM_SCORES], thisscore; 144 145 if (score_fp == NULL) 146 return (-1); 147 if (flock(fileno(score_fp), LOCK_EX) == -1) 148 err(1, "flock"); 149 snprintf(scanstr, 50, "%%%zus %%%zus %%d %%d %%d", sizeof(score[0].name)-1, 150 sizeof(score[0].game)-1); 151 for (;;) { 152 good = fscanf(score_fp, scanstr, 153 score[num_scores].name, 154 score[num_scores].game, 155 &score[num_scores].planes, 156 &score[num_scores].time, 157 &score[num_scores].real_time); 158 if (good != 5 || ++num_scores >= NUM_SCORES) 159 break; 160 } 161 if (!test_mode && !list_em) { 162 name = getenv("LOGNAME"); 163 if (name == NULL || *name == '\0') 164 name = getenv("USER"); 165 if (name == NULL || *name == '\0') 166 name = getlogin(); 167 if (name == NULL || *name == '\0') 168 name = " ???"; 169 strlcpy(thisscore.name, name, sizeof(thisscore.name)); 170 171 cp = strrchr(file, '/'); 172 if (cp == NULL) { 173 warnx("log: where's the '/' in %s?", file); 174 return (-1); 175 } 176 cp++; 177 strlcpy(thisscore.game, cp, sizeof(thisscore.game)); 178 179 thisscore.time = clck; 180 thisscore.planes = safe_planes; 181 thisscore.real_time = time(0) - start_time; 182 183 for (i = 0; i < num_scores; i++) { 184 if (strcmp(thisscore.name, score[i].name) == 0 && 185 strcmp(thisscore.game, score[i].game) == 0) { 186 if (thisscore.time > score[i].time) { 187 score[i].time = thisscore.time; 188 score[i].planes = thisscore.planes; 189 score[i].real_time = 190 thisscore.real_time; 191 changed++; 192 } 193 found++; 194 break; 195 } 196 } 197 if (!found) { 198 for (i = 0; i < num_scores; i++) { 199 if (thisscore.time > score[i].time) { 200 if (num_scores < NUM_SCORES) 201 num_scores++; 202 memcpy(&score[num_scores - 1], 203 &score[i], 204 sizeof (score[i])); 205 memcpy(&score[i], &thisscore, 206 sizeof (score[i])); 207 changed++; 208 break; 209 } 210 } 211 } 212 if (!found && !changed && num_scores < NUM_SCORES) { 213 memcpy(&score[num_scores], &thisscore, 214 sizeof (score[num_scores])); 215 num_scores++; 216 changed++; 217 } 218 219 if (seeded) { 220 puts("The high score list does not include '-r' seeded games."); 221 } else if (changed) { 222 if (found) 223 puts("You beat your previous score!"); 224 else 225 puts("You made the top players list!"); 226 qsort(score, num_scores, sizeof (*score), compar); 227 if (fseek(score_fp, 0L, SEEK_SET) == -1) 228 err(1, "fseek"); 229 for (i = 0; i < num_scores; i++) 230 fprintf(score_fp, "%s %s %d %d %d\n", 231 score[i].name, 232 score[i].game, score[i].planes, 233 score[i].time, score[i].real_time); 234 } else { 235 if (found) 236 puts("You didn't beat your previous score."); 237 else 238 puts("You didn't make the top players list."); 239 } 240 putchar('\n'); 241 } 242 flock(fileno(score_fp), LOCK_UN); 243 fflush(score_fp); 244 fsync(fileno(score_fp)); 245 if (fseek(score_fp, 0L, SEEK_SET) == -1) 246 err(1, "fseek"); 247 printf("%2s: %-31s %-18s %4s %9s %4s\n", "#", "name", 248 "game", "time", "real time", "safe"); 249 puts("-------------------------------------------------------------------------------"); 250 for (i = 0; i < num_scores; i++) { 251 printf("%2d: %-31s %-18s %4d %9s %4d\n", i + 1, 252 score[i].name, score[i].game, 253 score[i].time, timestr(score[i].real_time), 254 score[i].planes); 255 } 256 putchar('\n'); 257 return (0); 258} 259 260void 261log_score_quit(int dummy) 262{ 263 (void)log_score(0); 264 exit(0); 265} 266