xref: /netbsd/games/atc/log.c (revision bf9ec67e)
1 /*	$NetBSD: log.c,v 1.11 2001/01/16 02:50:28 cgd Exp $	*/
2 
3 /*-
4  * Copyright (c) 1990, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * Ed James.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. 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  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *	This product includes software developed by the University of
21  *	California, Berkeley and its contributors.
22  * 4. Neither the name of the University nor the names of its contributors
23  *    may be used to endorse or promote products derived from this software
24  *    without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  */
38 
39 /*
40  * Copyright (c) 1987 by Ed James, UC Berkeley.  All rights reserved.
41  *
42  * Copy permission is hereby granted provided that this notice is
43  * retained on all partial or complete copies.
44  *
45  * For more info on this and all of my stuff, mail edjames@berkeley.edu.
46  */
47 
48 #include <sys/cdefs.h>
49 #ifndef lint
50 #if 0
51 static char sccsid[] = "@(#)log.c	8.1 (Berkeley) 5/31/93";
52 #else
53 __RCSID("$NetBSD: log.c,v 1.11 2001/01/16 02:50:28 cgd Exp $");
54 #endif
55 #endif /* not lint */
56 
57 #include "include.h"
58 #include "pathnames.h"
59 
60 static FILE *score_fp;
61 
62 int
63 compar(va, vb)
64 	const void *va, *vb;
65 {
66 	const SCORE	*a, *b;
67 
68 	a = (const SCORE *)va;
69 	b = (const SCORE *)vb;
70 	if (b->planes == a->planes)
71 		return (b->time - a->time);
72 	else
73 		return (b->planes - a->planes);
74 }
75 
76 #define SECAMIN		60
77 #define MINAHOUR	60
78 #define HOURADAY	24
79 #define SECAHOUR	(SECAMIN * MINAHOUR)
80 #define SECADAY		(SECAHOUR * HOURADAY)
81 #define DAY(t)		((t) / SECADAY)
82 #define HOUR(t)		(((t) % SECADAY) / SECAHOUR)
83 #define MIN(t)		(((t) % SECAHOUR) / SECAMIN)
84 #define SEC(t)		((t) % SECAMIN)
85 
86 const char	*
87 timestr(t)
88 	int t;
89 {
90 	static char	s[80];
91 
92 	if (DAY(t) > 0)
93 		(void)sprintf(s, "%dd+%02dhrs", DAY(t), HOUR(t));
94 	else if (HOUR(t) > 0)
95 		(void)sprintf(s, "%d:%02d:%02d", HOUR(t), MIN(t), SEC(t));
96 	else if (MIN(t) > 0)
97 		(void)sprintf(s, "%d:%02d", MIN(t), SEC(t));
98 	else if (SEC(t) > 0)
99 		(void)sprintf(s, ":%02d", SEC(t));
100 	else
101 		*s = '\0';
102 
103 	return (s);
104 }
105 
106 void
107 open_score_file()
108 {
109 	mode_t old_mask;
110 	int score_fd;
111 	int flags;
112 
113 	old_mask = umask(0);
114 	score_fd = open(_PATH_SCORE, O_CREAT|O_RDWR, 0664);
115 	umask(old_mask);
116 	if (score_fd < 0) {
117 		warn("open %s", _PATH_SCORE);
118 		return;
119 	}
120 	if (score_fd < 3)
121 		exit(1);
122 	/* Set the close-on-exec flag.  If this fails for any reason, quit
123 	 * rather than leave the score file open to tampering.  */
124 	flags = fcntl(score_fd, F_GETFD);
125 	if (flags < 0)
126 		err(1, "fcntl F_GETFD");
127 	flags |= FD_CLOEXEC;
128 	if (fcntl(score_fd, F_SETFD, flags) == -1)
129 		err(1, "fcntl F_SETFD");
130 	/*
131 	 * This is done to take advantage of stdio, while still
132 	 * allowing a O_CREAT during the open(2) of the log file.
133 	 */
134 	score_fp = fdopen(score_fd, "r+");
135 	if (score_fp == NULL) {
136 		warn("fdopen %s", _PATH_SCORE);
137 		return;
138 	}
139 }
140 
141 int
142 log_score(list_em)
143 	int list_em;
144 {
145 	int		i, num_scores = 0, good, changed = 0, found = 0;
146 	struct passwd	*pw;
147 	char		*cp;
148 	SCORE		score[100], thisscore;
149 	struct utsname	name;
150 	long		offset;
151 
152 	if (score_fp == NULL) {
153 		warnx("no score file available");
154 		return (-1);
155 	}
156 
157 #ifdef BSD
158 	if (flock(fileno(score_fp), LOCK_EX) < 0)
159 #endif
160 #ifdef SYSV
161 	while (lockf(fileno(score_fp), F_LOCK, 1) < 0)
162 #endif
163 	{
164 		warn("flock %s", _PATH_SCORE);
165 		return (-1);
166 	}
167 	for (;;) {
168 		good = fscanf(score_fp, SCORE_SCANF_FMT,
169 			score[num_scores].name,
170 			score[num_scores].host,
171 			score[num_scores].game,
172 			&score[num_scores].planes,
173 			&score[num_scores].time,
174 			&score[num_scores].real_time);
175 		if (good != 6 || ++num_scores >= NUM_SCORES)
176 			break;
177 	}
178 	if (!test_mode && !list_em) {
179 		if ((pw = (struct passwd *) getpwuid(getuid())) == NULL) {
180 			fprintf(stderr,
181 				"getpwuid failed for uid %d.  Who are you?\n",
182 				(int)getuid());
183 			return (-1);
184 		}
185 		strcpy(thisscore.name, pw->pw_name);
186 		uname(&name);
187 		strncpy(thisscore.host, name.nodename, sizeof(thisscore.host)-1);
188 		thisscore.host[sizeof(thisscore.host) - 1] = '\0';
189 
190 		cp = strrchr(file, '/');
191 		if (cp == NULL) {
192 			fprintf(stderr, "log: where's the '/' in %s?\n", file);
193 			return (-1);
194 		}
195 		cp++;
196 		strcpy(thisscore.game, cp);
197 
198 		thisscore.time = clck;
199 		thisscore.planes = safe_planes;
200 		thisscore.real_time = time(0) - start_time;
201 
202 		for (i = 0; i < num_scores; i++) {
203 			if (strcmp(thisscore.name, score[i].name) == 0 &&
204 			    strcmp(thisscore.host, score[i].host) == 0 &&
205 			    strcmp(thisscore.game, score[i].game) == 0) {
206 				if (thisscore.time > score[i].time) {
207 					score[i].time = thisscore.time;
208 					score[i].planes = thisscore.planes;
209 					score[i].real_time =
210 						thisscore.real_time;
211 					changed++;
212 				}
213 				found++;
214 				break;
215 			}
216 		}
217 		if (!found) {
218 			for (i = 0; i < num_scores; i++) {
219 				if (thisscore.time > score[i].time) {
220 					if (num_scores < NUM_SCORES)
221 						num_scores++;
222 					memcpy(&score[num_scores - 1],
223 					       &score[i],
224 					       sizeof (score[i]));
225 					memcpy(&score[i], &thisscore,
226 					       sizeof (score[i]));
227 					changed++;
228 					break;
229 				}
230 			}
231 		}
232 		if (!found && !changed && num_scores < NUM_SCORES) {
233 			memcpy(&score[num_scores], &thisscore,
234 			       sizeof (score[num_scores]));
235 			num_scores++;
236 			changed++;
237 		}
238 
239 		if (changed) {
240 			if (found)
241 				puts("You beat your previous score!");
242 			else
243 				puts("You made the top players list!");
244 			qsort(score, num_scores, sizeof (*score), compar);
245 			rewind(score_fp);
246 			for (i = 0; i < num_scores; i++)
247 				fprintf(score_fp, "%s %s %s %d %d %d\n",
248 					score[i].name, score[i].host,
249 					score[i].game, score[i].planes,
250 					score[i].time, score[i].real_time);
251 			fflush(score_fp);
252 			if (ferror(score_fp))
253 				warn("error writing %s", _PATH_SCORE);
254 			/* It is just possible that updating an entry could
255 			 * have reduced the length of the file, so we
256 			 * truncate it.  The seeks are required for stream/fd
257 			 * synchronisation by POSIX.1.  */
258 			offset = ftell(score_fp);
259 			lseek(fileno(score_fp), 0, SEEK_SET);
260 			ftruncate(fileno(score_fp), offset);
261 			rewind(score_fp);
262 		} else {
263 			if (found)
264 				puts("You didn't beat your previous score.");
265 			else
266 				puts("You didn't make the top players list.");
267 		}
268 		putchar('\n');
269 	}
270 #ifdef BSD
271 	flock(fileno(score_fp), LOCK_UN);
272 #endif
273 #ifdef SYSV
274 	/* lock will evaporate upon close */
275 #endif
276 	fclose(score_fp);
277 	printf("%2s:  %-8s  %-8s  %-18s  %4s  %9s  %4s\n", "#", "name", "host",
278 		"game", "time", "real time", "planes safe");
279 	puts("-------------------------------------------------------------------------------");
280 	for (i = 0; i < num_scores; i++) {
281 		cp = strchr(score[i].host, '.');
282 		if (cp != NULL)
283 			*cp = '\0';
284 		printf("%2d:  %-8s  %-8s  %-18s  %4d  %9s  %4d\n", i + 1,
285 			score[i].name, score[i].host, score[i].game,
286 			score[i].time, timestr(score[i].real_time),
287 			score[i].planes);
288 	}
289 	putchar('\n');
290 	return (0);
291 }
292 
293 void
294 log_score_quit(dummy)
295 	int dummy __attribute__((__unused__));
296 {
297 	(void)log_score(0);
298 	exit(0);
299 }
300