xref: /openbsd/games/atc/log.c (revision 264ca280)
1 /*	$OpenBSD: log.c,v 1.22 2016/03/16 15:00:35 mestre 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 <unistd.h>
54 
55 #include "extern.h"
56 #include "pathnames.h"
57 
58 static FILE *score_fp;
59 
60 int
61 compar(const void *va, const void *vb)
62 {
63 	const SCORE *a, *b;
64 
65 	a = (const SCORE *)va;
66 	b = (const SCORE *)vb;
67 	if (b->planes == a->planes)
68 		return (b->time - a->time);
69 	else
70 		return (b->planes - a->planes);
71 }
72 
73 #define SECAMIN		60
74 #define MINAHOUR	60
75 #define HOURADAY	24
76 #define SECAHOUR	(SECAMIN * MINAHOUR)
77 #define SECADAY		(SECAHOUR * HOURADAY)
78 #define DAY(t)		((t) / SECADAY)
79 #define HOUR(t)		(((t) % SECADAY) / SECAHOUR)
80 #define MINUTES(t)		(((t) % SECAHOUR) / SECAMIN)
81 #define SEC(t)		((t) % SECAMIN)
82 
83 const char	*
84 timestr(int t)
85 {
86 	static char	s[80];
87 
88 	if (DAY(t) > 0)
89 		(void)snprintf(s, sizeof s, "%dd+%02dhrs", DAY(t), HOUR(t));
90 	else if (HOUR(t) > 0)
91 		(void)snprintf(s, sizeof s, "%d:%02d:%02d",
92 		    HOUR(t), MINUTES(t), SEC(t));
93 	else if (MINUTES(t) > 0)
94 		(void)snprintf(s, sizeof s, "%d:%02d", MINUTES(t), SEC(t));
95 	else if (SEC(t) > 0)
96 		(void)snprintf(s, sizeof s, ":%02d", SEC(t));
97 	else
98 		*s = '\0';
99 
100 	return (s);
101 }
102 
103 int
104 open_score_file(void)
105 {
106 	mode_t old_mode;
107 	char *home;
108 	char scorefile[PATH_MAX];
109 	int ret;
110 	int score_fd;
111 
112 	home = getenv("HOME");
113 	if (home == NULL || *home == '\0')
114 		err(1, "getenv");
115 	ret = snprintf(scorefile, sizeof(scorefile), "%s/%s", home,
116 	    ".atc.scores");
117 	if (ret < 0 || ret >= PATH_MAX)
118 		errc(1, ENAMETOOLONG, "%s/%s", home, ".atc.scores");
119 
120 	old_mode = umask(0);
121 	score_fd = open(scorefile, O_CREAT|O_RDWR, 0644);
122 	if (score_fd < 0)
123 		err(1, "open");
124 	/*
125 	 * This is done to take advantage of stdio, while still
126 	 * allowing a O_CREAT during the open(2) of the log file.
127 	 */
128 	score_fp = fdopen(score_fd, "r+");
129 	if (score_fp == NULL)
130 		err(1, "fdopen");
131 	umask(old_mode);
132 	return (0);
133 }
134 
135 int
136 log_score(int list_em)
137 {
138 	int		i, num_scores = 0, good, changed = 0, found = 0;
139 	const char	*name;
140 	char		*cp;
141 	char		scanstr[50];
142 	SCORE		score[NUM_SCORES], thisscore;
143 
144 	if (score_fp == NULL)
145 		return (-1);
146 	if (flock(fileno(score_fp), LOCK_EX) < 0)
147 		err(1, "flock");
148 	snprintf(scanstr, 50, "%%%zus %%%zus %%d %%d %%d", sizeof(score[0].name)-1,
149 	    sizeof(score[0].game)-1);
150 	for (;;) {
151 		good = fscanf(score_fp, scanstr,
152 			score[num_scores].name,
153 			score[num_scores].game,
154 			&score[num_scores].planes,
155 			&score[num_scores].time,
156 			&score[num_scores].real_time);
157 		if (good != 5 || ++num_scores >= NUM_SCORES)
158 			break;
159 	}
160 	if (!test_mode && !list_em) {
161 		name = getenv("LOGNAME");
162 		if (name == NULL || *name == '\0')
163 			name = getenv("USER");
164 		if (name == NULL || *name == '\0')
165 			name = getlogin();
166 		if (name == NULL || *name == '\0')
167 			name = "  ???";
168 		strlcpy(thisscore.name, name, sizeof(thisscore.name));
169 
170 		cp = strrchr(file, '/');
171 		if (cp == NULL) {
172 			warnx("log: where's the '/' in %s?", file);
173 			return (-1);
174 		}
175 		cp++;
176 		strlcpy(thisscore.game, cp, sizeof(thisscore.game));
177 
178 		thisscore.time = clck;
179 		thisscore.planes = safe_planes;
180 		thisscore.real_time = time(0) - start_time;
181 
182 		for (i = 0; i < num_scores; i++) {
183 			if (strcmp(thisscore.name, score[i].name) == 0 &&
184 			    strcmp(thisscore.game, score[i].game) == 0) {
185 				if (thisscore.time > score[i].time) {
186 					score[i].time = thisscore.time;
187 					score[i].planes = thisscore.planes;
188 					score[i].real_time =
189 						thisscore.real_time;
190 					changed++;
191 				}
192 				found++;
193 				break;
194 			}
195 		}
196 		if (!found) {
197 			for (i = 0; i < num_scores; i++) {
198 				if (thisscore.time > score[i].time) {
199 					if (num_scores < NUM_SCORES)
200 						num_scores++;
201 					memcpy(&score[num_scores - 1],
202 						&score[i],
203 						sizeof (score[i]));
204 					memcpy(&score[i], &thisscore,
205 						sizeof (score[i]));
206 					changed++;
207 					break;
208 				}
209 			}
210 		}
211 		if (!found && !changed && num_scores < NUM_SCORES) {
212 			memcpy(&score[num_scores], &thisscore,
213 				sizeof (score[num_scores]));
214 			num_scores++;
215 			changed++;
216 		}
217 
218 		if (seeded) {
219 			puts("The high score list does not include '-r' seeded games.");
220 		} else if (changed) {
221 			if (found)
222 				puts("You beat your previous score!");
223 			else
224 				puts("You made the top players list!");
225 			qsort(score, num_scores, sizeof (*score), compar);
226 			if (fseek(score_fp, 0L, SEEK_SET) == -1)
227 				err(1, "fseek");
228 			for (i = 0; i < num_scores; i++)
229 				fprintf(score_fp, "%s %s %d %d %d\n",
230 					score[i].name,
231 					score[i].game, score[i].planes,
232 					score[i].time, score[i].real_time);
233 		} else {
234 			if (found)
235 				puts("You didn't beat your previous score.");
236 			else
237 				puts("You didn't make the top players list.");
238 		}
239 		putchar('\n');
240 	}
241 	flock(fileno(score_fp), LOCK_UN);
242 	fflush(score_fp);
243 	fsync(fileno(score_fp));
244 	if (fseek(score_fp, 0L, SEEK_SET) == -1)
245 		err(1, "fseek");
246 	printf("%2s:  %-31s  %-18s  %4s  %9s  %4s\n", "#", "name",
247 		"game", "time", "real time", "safe");
248 	puts("-------------------------------------------------------------------------------");
249 	for (i = 0; i < num_scores; i++) {
250 		printf("%2d:  %-31s  %-18s  %4d  %9s  %4d\n", i + 1,
251 			score[i].name, score[i].game,
252 			score[i].time, timestr(score[i].real_time),
253 			score[i].planes);
254 	}
255 	putchar('\n');
256 	return (0);
257 }
258 
259 void
260 log_score_quit(int dummy)
261 {
262 	(void)log_score(0);
263 	exit(0);
264 }
265