aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBenjamin Linskey2026-06-08 04:05:03 -0400
committerBenjamin Linskey2026-06-08 17:01:22 -0400
commitf9a393c59f95f387b7a0c6c774b970b3b1d49e20 (patch)
treeb4981b0af79391db11359c07d348ba5b32f91030
parent197ecfff9e99d57fe9539685316d3acf23f6cdfc (diff)
downloadrogue-f9a393c59f95f387b7a0c6c774b970b3b1d49e20.tar.gz

Store score file in home directory

BSD games are traditionally designed for multiuser systems and thus write their data to /var/games, using setgid to prevent tampering. Switch to a single-user design, writing the score file to the user’s home directory. $XDGDATAHOME/rogue will will be used if $XDGDATAHOME is defined. Otherwise, data will be written to a $HOME/.rogue directory.

This also improves the makefile by making header files a dependency of objects.

-rw-r--r--Makefile9
-rw-r--r--machdep.c34
-rw-r--r--pathnames.c142
-rw-r--r--pathnames.h10
-rw-r--r--rogue.h1
-rw-r--r--score.c14
6 files changed, 163 insertions, 47 deletions
diff --git a/Makefile b/Makefile
index 5379b21..faaf561 100644
--- a/Makefile
+++ b/Makefile
@@ -7,10 +7,13 @@ BIN_DIR:=$(PREFIX)/bin
CPPFLAGS+=-DUNIX -DUNIX_SYSV
SRCS= hit.c init.c inventory.c level.c machdep.c main.c \
message.c monster.c move.c object.c pack.c play.c random.c ring.c \
- room.c save.c score.c spec_hit.c throw.c trap.c use.c zap.c
+ room.c save.c score.c spec_hit.c throw.c trap.c use.c zap.c \
+ pathnames.c
OBJS= hit.o init.o inventory.o level.o machdep.o main.o \
message.o monster.o move.o object.o pack.o play.o random.o ring.o \
- room.o save.o score.o spec_hit.o throw.o trap.o use.o zap.o
+ room.o save.o score.o spec_hit.o throw.o trap.o use.o zap.o \
+ pathnames.o
+HEADERS=rogue.h pathnames.h
DPADD= ${LIBCURSES} ${LIBTERMINFO}
LDADD= -lcurses -lterminfo
HIDEGAME=hidegame
@@ -24,6 +27,8 @@ COPTS.score.c+= ${CC_WNO_FORMAT_TRUNCATION}
$(PROG): $(OBJS)
cc -o $@ -lcurses $(OBJS)
+$(OBJS): $(HEADERS)
+
.PHONY: install
install: $(PROG)
chmod 755 $(PROG)
diff --git a/machdep.c b/machdep.c
index e6d6292..4d5d334 100644
--- a/machdep.c
+++ b/machdep.c
@@ -424,40 +424,6 @@ md_exit(int status)
exit(status);
}
-/* md_lock():
- *
- * This function is intended to give the user exclusive access to the score
- * file. It does so by flock'ing the score file. The full path name of the
- * score file should be defined for any particular site in rogue.h. The
- * constants _PATH_SCOREFILE defines this file name.
- *
- * When the parameter 'l' is non-zero (true), a lock is requested. Otherwise
- * the lock is released.
- */
-
-void
-md_lock(boolean l)
-{
- static int fd = -1;
- short tries;
-
- if (l) {
- setegid(egid);
- if ((fd = open(_PATH_SCOREFILE, O_RDONLY)) < 1) {
- setegid(gid);
- messagef(0, "cannot lock score file");
- return;
- }
- setegid(gid);
- for (tries = 0; tries < 5; tries++)
- if (!flock(fd, LOCK_EX|LOCK_NB))
- return;
- } else {
- (void)flock(fd, LOCK_UN|LOCK_NB);
- (void)close(fd);
- }
-}
-
/* md_shell():
*
* This function spawns a shell for the user to use. When this shell is
diff --git a/pathnames.c b/pathnames.c
new file mode 100644
index 0000000..e6d9117
--- /dev/null
+++ b/pathnames.c
@@ -0,0 +1,142 @@
+#include <stdlib.h>
+#include <pwd.h>
+#include <stdbool.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <stdio.h>
+#include "pathnames.h"
+#include "rogue.h"
+
+#define XDG_DIR "rogue"
+#define DOTFILE_DIR ".rogue"
+#define SCORE_FILE "scores"
+
+static char *get_prog_dir_path(void);
+static char *get_score_path(void);
+static int ensure_prog_dir(void);
+
+/*
+ * Opens the score file for reading and writing and returns a file pointer.
+ * Returns NULL on error.
+ */
+FILE *
+open_score_file(void) {
+ if (ensure_prog_dir() == -1) {
+ printf("Failed to ensure directory\n");
+ return NULL;
+ }
+
+ char *path = get_score_path();
+ FILE *fp = fopen(path, "r+");
+ if (!fp && errno == ENOENT) {
+ fp = fopen(path, "w+");
+ }
+ free(path);
+
+ if (!fp) {
+ printf("Failed to open file %s\n", path);
+ }
+ return fp;
+}
+
+/*
+ * Returns the path to the program data directory. The caller is responsible
+ * for freeing the returned memory.
+ */
+static
+char *
+get_prog_dir_path(void) {
+ /*
+ * Use $XDG_DATA_HOME if defined, or the user's home directory
+ * otherwise.
+ */
+ char *data_dir = getenv("XDG_DATA_HOME");
+ char *dir;
+ if (data_dir) {
+ dir = XDG_DIR;
+ } else {
+ dir = DOTFILE_DIR;
+ data_dir = getenv("HOME");
+ if (!data_dir) {
+ data_dir = getpwuid(getuid())->pw_dir;
+ }
+ }
+
+ size_t len = strlen(data_dir) + strlen(dir) + 1;
+ bool add_slash = false;
+ if (data_dir[strlen(data_dir) - 1] != '/') {
+ len += 1;
+ add_slash = true;
+ }
+
+ char *buf = malloc(len);
+ strlcpy(buf, data_dir, len);
+ if (add_slash) {
+ strlcat(buf, "/", len);
+ }
+ strlcat(buf, dir, len);
+
+ return buf;
+}
+
+/*
+ * Returns the path to the score file. The caller is repsonsible for freeing
+ * the returned memory.
+ */
+static
+char *
+get_score_path(void)
+{
+ char *dir = get_prog_dir_path();
+
+ // Account for slash after directory and trailing NULL.
+ size_t len = strlen(dir) + strlen(SCORE_FILE) + 2;
+
+ char *buf = malloc(len);
+ strlcpy(buf, dir, len);
+ strlcat(buf, "/", len);
+ strlcat(buf, SCORE_FILE, len);
+
+ free(dir);
+
+ return buf;
+}
+
+/*
+ * Ensures that the program data directory exists. Returns 0 on success or -1
+ * if an error occurs.
+ */
+static
+int
+ensure_prog_dir(void) {
+ int ret;
+ char *path = get_prog_dir_path();
+ struct stat s = {0};
+ int err = stat(path, &s);
+
+ // If file does not exist, create the directory.
+ if (err && errno == ENOENT) {
+ ret = mkdir(path, 0755);
+ goto cleanup;
+ }
+
+ // If file does exist and stat() returned an error, bail out.
+ if (err) {
+ ret = err;
+ goto cleanup;
+ }
+
+ // If file is a directory, we don't need to do anything.
+ if (s.st_mode & S_IFDIR) {
+ ret = 0;
+ goto cleanup;
+ }
+
+ // File exists and isn't a directory, so return an error.
+ ret = -1;
+ goto cleanup;
+
+cleanup:
+ free(path);
+ return ret;
+}
diff --git a/pathnames.h b/pathnames.h
index d6e91a5..f24ba08 100644
--- a/pathnames.h
+++ b/pathnames.h
@@ -31,5 +31,13 @@
* @(#)pathnames.h 8.1 (Berkeley) 5/31/93
*/
-#define _PATH_SCOREFILE "/var/games/rogue.scores"
+#ifndef _PATHNAMES_H
+#define _PATHNAMES_H
+
+#include <stdio.h>
+
#define _PATH_SCREENDUMP "rogue.screen"
+
+FILE *open_score_file(void);
+
+#endif // _PATHNAMES_H
diff --git a/rogue.h b/rogue.h
index 75d03ef..e3ae604 100644
--- a/rogue.h
+++ b/rogue.h
@@ -542,7 +542,6 @@ int md_gseed(void);
void md_heed_signals(void);
void md_ignore_signals(void);
int md_link_count(const char *);
-void md_lock(boolean);
void md_shell(const char *);
void md_sleep(int);
void md_slurp(void);
diff --git a/score.c b/score.c
index 4bcf4e3..e8815ec 100644
--- a/score.c
+++ b/score.c
@@ -32,6 +32,7 @@
* SUCH DAMAGE.
*/
+#include <stdlib.h>
#include <sys/cdefs.h>
#ifndef lint
#if 0
@@ -341,16 +342,12 @@ put_scores(const object *monster, short other)
FILE *fp;
boolean dopause = score_only;
- md_lock(1);
-
- setegid(egid);
- if ((fp = fopen(_PATH_SCOREFILE, "r+")) == NULL &&
- (fp = fopen(_PATH_SCOREFILE, "w+")) == NULL) {
- setegid(gid);
+ fp = open_score_file();
+ if (fp== NULL) {
messagef(0, "cannot read/write/create score file");
sf_error();
}
- setegid(gid);
+
rewind(fp);
(void)xxx(1);
@@ -418,7 +415,7 @@ put_scores(const object *monster, short other)
write_score_entry(&scores[i], i, fp);
}
}
- md_lock(0);
+
fclose(fp);
/* Display the scores */
@@ -668,7 +665,6 @@ center(short row, const char *buf)
static void
sf_error(void)
{
- md_lock(0);
messagef(1, "%s", ""); /* gcc objects to just "" */
clean_up("sorry, score file is out of order");
}