1 /* $OpenBSD: log.c,v 1.26 2024/08/20 15:48:32 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
59 static FILE *score_fp;
60
61 int
compar(const void * va,const void * vb)62 compar(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
84 const char *
timestr(int t)85 timestr(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
104 int
open_score_file(void)105 open_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
136 int
log_score(int list_em)137 log_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
260 void
log_score_quit(int dummy)261 log_score_quit(int dummy)
262 {
263 (void)log_score(0); /* XXX signal race */
264 exit(0); /* XXX signal race */
265 }
266