xref: /netbsd-src/games/atc/log.c (revision 1182a44c59cae4d586117d55eca24b4b8b173211)
1*1182a44cSrillig /*	$NetBSD: log.c,v 1.25 2021/05/02 12:50:43 rillig Exp $	*/
2101657d1Scgd 
361f28255Scgd /*-
4101657d1Scgd  * Copyright (c) 1990, 1993
5101657d1Scgd  *	The Regents of the University of California.  All rights reserved.
661f28255Scgd  *
761f28255Scgd  * This code is derived from software contributed to Berkeley by
861f28255Scgd  * Ed James.
961f28255Scgd  *
1061f28255Scgd  * Redistribution and use in source and binary forms, with or without
1161f28255Scgd  * modification, are permitted provided that the following conditions
1261f28255Scgd  * are met:
1361f28255Scgd  * 1. Redistributions of source code must retain the above copyright
1461f28255Scgd  *    notice, this list of conditions and the following disclaimer.
1561f28255Scgd  * 2. Redistributions in binary form must reproduce the above copyright
1661f28255Scgd  *    notice, this list of conditions and the following disclaimer in the
1761f28255Scgd  *    documentation and/or other materials provided with the distribution.
18e5aeb4eaSagc  * 3. Neither the name of the University nor the names of its contributors
1961f28255Scgd  *    may be used to endorse or promote products derived from this software
2061f28255Scgd  *    without specific prior written permission.
2161f28255Scgd  *
2261f28255Scgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2361f28255Scgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2461f28255Scgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2561f28255Scgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2661f28255Scgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2761f28255Scgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2861f28255Scgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2961f28255Scgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3061f28255Scgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3161f28255Scgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3261f28255Scgd  * SUCH DAMAGE.
3361f28255Scgd  */
3461f28255Scgd 
3561f28255Scgd /*
3661f28255Scgd  * Copyright (c) 1987 by Ed James, UC Berkeley.  All rights reserved.
3761f28255Scgd  *
3861f28255Scgd  * Copy permission is hereby granted provided that this notice is
3961f28255Scgd  * retained on all partial or complete copies.
4061f28255Scgd  *
4161f28255Scgd  * For more info on this and all of my stuff, mail edjames@berkeley.edu.
4261f28255Scgd  */
4361f28255Scgd 
44ca57cf90Slukem #include <sys/cdefs.h>
4561f28255Scgd #ifndef lint
46101657d1Scgd #if 0
47101657d1Scgd static char sccsid[] = "@(#)log.c	8.1 (Berkeley) 5/31/93";
48101657d1Scgd #else
49*1182a44cSrillig __RCSID("$NetBSD: log.c,v 1.25 2021/05/02 12:50:43 rillig Exp $");
50101657d1Scgd #endif
51d594ce93Scgd #endif /* not lint */
5261f28255Scgd 
5301dcf47eSdholland #include <sys/types.h>
5401dcf47eSdholland #include <sys/utsname.h>
55fa60103eSchristos #include <sys/stat.h>	/* for umask(2) */
5601dcf47eSdholland #include <stdio.h>
5701dcf47eSdholland #include <stdlib.h>
5801dcf47eSdholland #include <string.h>
5901dcf47eSdholland #include <unistd.h>
6001dcf47eSdholland #include <fcntl.h>
6101dcf47eSdholland #include <pwd.h>
6201dcf47eSdholland #include <err.h>
6301dcf47eSdholland 
6461f28255Scgd #include "pathnames.h"
6501dcf47eSdholland #include "def.h"
6601dcf47eSdholland #include "struct.h"
6701dcf47eSdholland #include "extern.h"
6801dcf47eSdholland #include "tunable.h"
6961f28255Scgd 
70d5f85ed9Shubertf static FILE *score_fp;
71d5f85ed9Shubertf 
727a2ed764Sdholland static int
compar(const void * va,const void * vb)734931378aSjmc compar(const void *va, const void *vb)
7461f28255Scgd {
75a9d35e83Shubertf 	const SCORE	*a, *b;
76ca57cf90Slukem 
77a9d35e83Shubertf 	a = (const SCORE *)va;
78a9d35e83Shubertf 	b = (const SCORE *)vb;
7961f28255Scgd 	if (b->planes == a->planes)
8061f28255Scgd 		return (b->time - a->time);
8161f28255Scgd 	else
8261f28255Scgd 		return (b->planes - a->planes);
8361f28255Scgd }
8461f28255Scgd 
8561f28255Scgd #define SECAMIN		60
8661f28255Scgd #define MINAHOUR	60
8761f28255Scgd #define HOURADAY	24
8861f28255Scgd #define SECAHOUR	(SECAMIN * MINAHOUR)
8961f28255Scgd #define SECADAY		(SECAHOUR * HOURADAY)
9061f28255Scgd #define DAY(t)		((t) / SECADAY)
9161f28255Scgd #define HOUR(t)		(((t) % SECADAY) / SECAHOUR)
9261f28255Scgd #define MIN(t)		(((t) % SECAHOUR) / SECAMIN)
9361f28255Scgd #define SEC(t)		((t) % SECAMIN)
9461f28255Scgd 
957a2ed764Sdholland static const char *
timestr(int t)964931378aSjmc timestr(int t)
9761f28255Scgd {
9861f28255Scgd 	static char	s[80];
9961f28255Scgd 
10061f28255Scgd 	if (DAY(t) > 0)
1012e221153Sdholland 		(void)snprintf(s, sizeof(s), "%dd+%02dhrs", DAY(t), HOUR(t));
10261f28255Scgd 	else if (HOUR(t) > 0)
1032e221153Sdholland 		(void)snprintf(s, sizeof(s), "%d:%02d:%02d", HOUR(t), MIN(t),
1042e221153Sdholland 			SEC(t));
10561f28255Scgd 	else if (MIN(t) > 0)
1062e221153Sdholland 		(void)snprintf(s, sizeof(s), "%d:%02d", MIN(t), SEC(t));
10761f28255Scgd 	else if (SEC(t) > 0)
1082e221153Sdholland 		(void)snprintf(s, sizeof(s), ":%02d", SEC(t));
10961f28255Scgd 	else
11061f28255Scgd 		*s = '\0';
11161f28255Scgd 
11261f28255Scgd 	return (s);
11361f28255Scgd }
11461f28255Scgd 
115d5f85ed9Shubertf void
open_score_file(void)1164931378aSjmc open_score_file(void)
11761f28255Scgd {
118d5f85ed9Shubertf 	mode_t old_mask;
119d5f85ed9Shubertf 	int score_fd;
120d5f85ed9Shubertf 	int flags;
12161f28255Scgd 
122d5f85ed9Shubertf 	old_mask = umask(0);
123c8e9ec4eSjnemeth #if defined(O_NOFOLLOW)
124c8e9ec4eSjnemeth 	score_fd = open(_PATH_SCORE, O_CREAT|O_RDWR|O_NOFOLLOW, 0664);
125c8e9ec4eSjnemeth #else
126d5f85ed9Shubertf 	score_fd = open(_PATH_SCORE, O_CREAT|O_RDWR, 0664);
127c8e9ec4eSjnemeth #endif
128b0282a1eSrpaulo 	(void)umask(old_mask);
129d5f85ed9Shubertf 	if (score_fd < 0) {
13036b47bd3Slukem 		warn("open %s", _PATH_SCORE);
131d5f85ed9Shubertf 		return;
13261f28255Scgd 	}
133d5f85ed9Shubertf 	if (score_fd < 3)
134d5f85ed9Shubertf 		exit(1);
135d5f85ed9Shubertf 	/* Set the close-on-exec flag.  If this fails for any reason, quit
136d5f85ed9Shubertf 	 * rather than leave the score file open to tampering.  */
137d5f85ed9Shubertf 	flags = fcntl(score_fd, F_GETFD);
138d5f85ed9Shubertf 	if (flags < 0)
139d5f85ed9Shubertf 		err(1, "fcntl F_GETFD");
140d5f85ed9Shubertf 	flags |= FD_CLOEXEC;
141d5f85ed9Shubertf 	if (fcntl(score_fd, F_SETFD, flags) == -1)
142d5f85ed9Shubertf 		err(1, "fcntl F_SETFD");
14361f28255Scgd 	/*
14461f28255Scgd 	 * This is done to take advantage of stdio, while still
14561f28255Scgd 	 * allowing a O_CREAT during the open(2) of the log file.
14661f28255Scgd 	 */
147d5f85ed9Shubertf 	score_fp = fdopen(score_fd, "r+");
148d5f85ed9Shubertf 	if (score_fp == NULL) {
14936b47bd3Slukem 		warn("fdopen %s", _PATH_SCORE);
150d5f85ed9Shubertf 		return;
151d5f85ed9Shubertf 	}
152d5f85ed9Shubertf }
153d5f85ed9Shubertf 
154d5f85ed9Shubertf int
log_score(int list_em)1554931378aSjmc log_score(int list_em)
156d5f85ed9Shubertf {
157d5f85ed9Shubertf 	int		i, num_scores = 0, good, changed = 0, found = 0;
158d5f85ed9Shubertf 	struct passwd	*pw;
159d5f85ed9Shubertf 	char		*cp;
160d5f85ed9Shubertf 	SCORE		score[100], thisscore;
1614931378aSjmc 	struct utsname	lname;
162d5f85ed9Shubertf 	long		offset;
163d5f85ed9Shubertf 
164a9c53af5Spgoyette 	if (safe_planes == 1)
165a9c53af5Spgoyette 		printf("You directed 1 plane safely to its destination.\n\n");
166a9c53af5Spgoyette 	else
167a9c53af5Spgoyette 		printf("You directed %d planes safely to their destinations.\n\n",
168a9c53af5Spgoyette 		    safe_planes);
169a9c53af5Spgoyette 
170d5f85ed9Shubertf 	if (score_fp == NULL) {
171d5f85ed9Shubertf 		warnx("no score file available");
17261f28255Scgd 		return (-1);
17361f28255Scgd 	}
174d5f85ed9Shubertf 
17561f28255Scgd #ifdef BSD
176d5f85ed9Shubertf 	if (flock(fileno(score_fp), LOCK_EX) < 0)
17761f28255Scgd #endif
17861f28255Scgd #ifdef SYSV
179c8e9ec4eSjnemeth 	if (lockf(fileno(score_fp), F_LOCK, 1) < 0)
18061f28255Scgd #endif
18161f28255Scgd 	{
18236b47bd3Slukem 		warn("flock %s", _PATH_SCORE);
18361f28255Scgd 		return (-1);
18461f28255Scgd 	}
18561f28255Scgd 	for (;;) {
186d5f85ed9Shubertf 		good = fscanf(score_fp, SCORE_SCANF_FMT,
18761f28255Scgd 			   score[num_scores].name,
18861f28255Scgd 			   score[num_scores].host,
18961f28255Scgd 			   score[num_scores].game,
19061f28255Scgd 			   &score[num_scores].planes,
19161f28255Scgd 			   &score[num_scores].time,
19261f28255Scgd 			   &score[num_scores].real_time);
19361f28255Scgd 		if (good != 6 || ++num_scores >= NUM_SCORES)
19461f28255Scgd 			break;
19561f28255Scgd 	}
19661f28255Scgd 	if (!test_mode && !list_em) {
19761f28255Scgd 		if ((pw = (struct passwd *) getpwuid(getuid())) == NULL) {
198b0282a1eSrpaulo 			(void)fprintf(stderr,
19961f28255Scgd 				"getpwuid failed for uid %d.  Who are you?\n",
200d5f85ed9Shubertf 				(int)getuid());
20161f28255Scgd 			return (-1);
20261f28255Scgd 		}
203c8e9ec4eSjnemeth 		(void)strlcpy(thisscore.name, pw->pw_name, SCORE_NAME_LEN);
204b0282a1eSrpaulo 		(void)uname(&lname);
205b0282a1eSrpaulo 		(void)strlcpy(thisscore.host, lname.nodename,
206b0282a1eSrpaulo 		    sizeof(thisscore.host));
20761f28255Scgd 
2084931378aSjmc 		cp = strrchr(filename, '/');
20961f28255Scgd 		if (cp == NULL) {
210b0282a1eSrpaulo 			(void)fprintf(stderr, "log: where's the '/' in %s?\n",
2114931378aSjmc 			    filename);
21261f28255Scgd 			return (-1);
21361f28255Scgd 		}
21461f28255Scgd 		cp++;
215c8e9ec4eSjnemeth 		(void)strlcpy(thisscore.game, cp, SCORE_GAME_LEN);
21661f28255Scgd 
21761f28255Scgd 		thisscore.time = clck;
21861f28255Scgd 		thisscore.planes = safe_planes;
21961f28255Scgd 		thisscore.real_time = time(0) - start_time;
22061f28255Scgd 
22161f28255Scgd 		for (i = 0; i < num_scores; i++) {
22261f28255Scgd 			if (strcmp(thisscore.name, score[i].name) == 0 &&
22361f28255Scgd 			    strcmp(thisscore.host, score[i].host) == 0 &&
22461f28255Scgd 			    strcmp(thisscore.game, score[i].game) == 0) {
22561f28255Scgd 				if (thisscore.time > score[i].time) {
22661f28255Scgd 					score[i].time = thisscore.time;
22761f28255Scgd 					score[i].planes = thisscore.planes;
22861f28255Scgd 					score[i].real_time =
22961f28255Scgd 					    thisscore.real_time;
23061f28255Scgd 					changed++;
23161f28255Scgd 				}
23261f28255Scgd 				found++;
23361f28255Scgd 				break;
23461f28255Scgd 			}
23561f28255Scgd 		}
23661f28255Scgd 		if (!found) {
23761f28255Scgd 			for (i = 0; i < num_scores; i++) {
23861f28255Scgd 				if (thisscore.time > score[i].time) {
23961f28255Scgd 					if (num_scores < NUM_SCORES)
24061f28255Scgd 						num_scores++;
241b0282a1eSrpaulo 					(void)memcpy(&score[num_scores - 1],
242b0282a1eSrpaulo 					    &score[i], sizeof (score[i]));
243b0282a1eSrpaulo 					(void)memcpy(&score[i], &thisscore,
24461f28255Scgd 					    sizeof (score[i]));
24561f28255Scgd 					changed++;
24661f28255Scgd 					break;
24761f28255Scgd 				}
24861f28255Scgd 			}
24961f28255Scgd 		}
25061f28255Scgd 		if (!found && !changed && num_scores < NUM_SCORES) {
251b0282a1eSrpaulo 			(void)memcpy(&score[num_scores], &thisscore,
25261f28255Scgd 			    sizeof (score[num_scores]));
25361f28255Scgd 			num_scores++;
25461f28255Scgd 			changed++;
25561f28255Scgd 		}
25661f28255Scgd 
25761f28255Scgd 		if (changed) {
25861f28255Scgd 			if (found)
259b0282a1eSrpaulo 				(void)puts("You beat your previous score!");
26061f28255Scgd 			else
261b0282a1eSrpaulo 				(void)puts("You made the top players list!");
262b0282a1eSrpaulo 			qsort(score, (size_t)num_scores, sizeof (*score),
263b0282a1eSrpaulo 			    compar);
264d5f85ed9Shubertf 			rewind(score_fp);
26561f28255Scgd 			for (i = 0; i < num_scores; i++)
266b0282a1eSrpaulo 				(void)fprintf(score_fp, "%s %s %s %d %d %d\n",
26761f28255Scgd 				    score[i].name, score[i].host,
26861f28255Scgd 				    score[i].game, score[i].planes,
26961f28255Scgd 				    score[i].time, score[i].real_time);
270b0282a1eSrpaulo 			(void)fflush(score_fp);
271d5f85ed9Shubertf 			if (ferror(score_fp))
272d5f85ed9Shubertf 				warn("error writing %s", _PATH_SCORE);
273d5f85ed9Shubertf 			/* It is just possible that updating an entry could
274d5f85ed9Shubertf 			 * have reduced the length of the file, so we
275d5f85ed9Shubertf 			 * truncate it.  The seeks are required for stream/fd
276d5f85ed9Shubertf 			 * synchronisation by POSIX.1.  */
277d5f85ed9Shubertf 			offset = ftell(score_fp);
278b0282a1eSrpaulo 			(void)lseek(fileno(score_fp), (off_t)0, SEEK_SET);
279b0282a1eSrpaulo 			(void)ftruncate(fileno(score_fp), (off_t)offset);
280d5f85ed9Shubertf 			rewind(score_fp);
28161f28255Scgd 		} else {
28261f28255Scgd 			if (found)
283b0282a1eSrpaulo 				(void)puts(
284b0282a1eSrpaulo 				    "You didn't beat your previous score.");
28561f28255Scgd 			else
286b0282a1eSrpaulo 				(void)puts(
287b0282a1eSrpaulo 				    "You didn't make the top players list.");
28861f28255Scgd 		}
289b0282a1eSrpaulo 		(void)putchar('\n');
29061f28255Scgd 	}
29161f28255Scgd #ifdef BSD
292b0282a1eSrpaulo 	(void)flock(fileno(score_fp), LOCK_UN);
29361f28255Scgd #endif
29461f28255Scgd #ifdef SYSV
29561f28255Scgd 	/* lock will evaporate upon close */
29661f28255Scgd #endif
297b0282a1eSrpaulo 	(void)fclose(score_fp);
298b0282a1eSrpaulo 	(void)printf("%2s:  %-8s  %-8s  %-18s  %4s  %9s  %4s\n", "#", "name",
299b0282a1eSrpaulo 	    "host", "game", "time", "real time", "planes safe");
300279b6fbaSrpaulo 	(void)printf("-------------------------------------------------------");
30107b524b4Srpaulo 	(void)printf("-------------------------\n");
30261f28255Scgd 	for (i = 0; i < num_scores; i++) {
303ca57cf90Slukem 		cp = strchr(score[i].host, '.');
30461f28255Scgd 		if (cp != NULL)
30561f28255Scgd 			*cp = '\0';
306b0282a1eSrpaulo 		(void)printf("%2d:  %-8s  %-8s  %-18s  %4d  %9s  %4d\n", i + 1,
30761f28255Scgd 		    score[i].name, score[i].host, score[i].game,
30861f28255Scgd 		    score[i].time, timestr(score[i].real_time),
30961f28255Scgd 		    score[i].planes);
31061f28255Scgd 	}
311b0282a1eSrpaulo 	(void)putchar('\n');
31261f28255Scgd 	return (0);
31361f28255Scgd }
314ca57cf90Slukem 
315b0282a1eSrpaulo /* ARGSUSED */
316ca57cf90Slukem void
log_score_quit(int dummy __unused)3178b0f9554Sperry log_score_quit(int dummy __unused)
318ca57cf90Slukem {
319ca57cf90Slukem 	(void)log_score(0);
320ca57cf90Slukem 	exit(0);
321ca57cf90Slukem }
322