Subject: bin/8085: [PATCH] Battlestar save file handling 2
To: None <gnats-bugs@gnats.netbsd.org>
From: Joseph Myers <jsm28@cam.ac.uk>
List: netbsd-bugs
Date: 07/26/1999 15:36:02
>Number:         8085
>Category:       bin
>Synopsis:       [PATCH] Battlestar save file handling 2
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    bin-bug-people (Utility Bug People)
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Mon Jul 26 15:35:00 1999
>Last-Modified:
>Originator:     Joseph S. Myers
>Organization:
Trinity College, University of Cambridge, UK
>Release:        NetBSD anoncvs of 1999-07-26
>Environment:
[
System: Linux decomino 2.2.10 #1 Mon Jun 14 07:48:53 UTC 1999 i686 unknown
Architecture: i686
]
>Description:

This patch improves the handling of save files in battlestar(6), by
allowing the user to choose the name of the save file and specify it
on the command line when restoring.  It also eliminates a buffer
overrun in determining the path to the save file, and any particular
arbitrary limit on the name length.  In the name of a tidier home
directory, the default name is changed from "Bstar" to ".Bstar".

>How-To-Repeat:

>Fix:

diff -ruN battlestar/battlestar.6 battlestar+/battlestar.6
--- battlestar/battlestar.6	Thu Sep 10 21:50:35 1998
+++ battlestar+/battlestar.6	Mon Jul 26 22:11:31 1999
@@ -41,7 +41,8 @@
 .Nd a tropical adventure game
 .Sh SYNOPSIS
 .Nm
-.Op Fl r Em Pq recover a saved game
+.Op Fl r
+.Op Ar saved-file
 .Sh DESCRIPTION
 .Nm
 is an adventure game in the classic style.  However, it's slightly less
@@ -122,9 +123,12 @@
 The two commands "score" and "inven" will print out your current status
 in the game.
 .Sh SAVING A GAME
-The command "save" will save your game in a file called "Bstar."  You
-can recover a saved game by using the "-r" option when you start up the
-game.
+The command "save" will save your game in a file, by default called
+".Bstar" in your home directory.  You
+can recover a saved game by using the 
+.Fl r
+option when you start up the
+game, or by giving the name of the saved file as an argument.
 .Sh DIRECTIONS
 The compass directions N, S, E, and W can be used if you have a compass.
 If you don't have a compass, you'll have to say R, L, A, or B, which
diff -ruN battlestar/battlestar.c battlestar+/battlestar.c
--- battlestar/battlestar.c	Wed Jul 21 03:56:53 1999
+++ battlestar+/battlestar.c	Fri Jul 23 10:38:01 1999
@@ -69,7 +69,10 @@
 	/* Open the score file then revoke setgid privileges */
 	open_score_file();
 	setregid(getgid(), getgid());
-	initialize(argc < 2 || strcmp(argv[1], "-r"));
+
+	initialize((argc < 2) ? NULL : (strcmp(argv[1], "-r") ? argv[1]
+					: (argv[2] ? argv[2]
+					   : DEFAULT_SAVE_FILE)));
 start:
 	news();
 	beenthere[position]++;
diff -ruN battlestar/cypher.c battlestar+/cypher.c
--- battlestar/cypher.c	Thu Mar 25 16:46:08 1999
+++ battlestar+/cypher.c	Mon Jul 26 22:02:01 1999
@@ -51,6 +51,8 @@
 	int     junk;
 	int     lflag = -1;
 	char    buffer[10];
+	char   *filename, *rfilename;
+	size_t	filename_len;
 
 	while (wordtype[wordnumber] == ADJS)
 		wordnumber++;
@@ -367,7 +369,21 @@
 			break;
 
 		case SAVE:
-			save();
+			printf("\nSave file name (default %s) ",
+			       DEFAULT_SAVE_FILE);
+			filename = fgetln(stdin, &filename_len);
+			if (filename_len == 0
+			    || (filename_len == 1 && filename[0] == '\n'))
+				rfilename = save_file_name(DEFAULT_SAVE_FILE,
+				    strlen(DEFAULT_SAVE_FILE));
+			else {
+				if (filename[filename_len - 1] == '\n')
+					filename_len--;
+				rfilename = save_file_name(filename,
+							   filename_len);
+			}
+			save(rfilename);
+			free(rfilename);
 			break;
 
 		case FOLLOW:
diff -ruN battlestar/extern.h battlestar+/extern.h
--- battlestar/extern.h	Wed Jul 21 03:56:53 1999
+++ battlestar+/extern.h	Mon Jul 26 22:03:34 1999
@@ -303,6 +303,8 @@
 extern const struct objs dayobjs[];
 extern const struct objs nightobjs[];
 
+#define DEFAULT_SAVE_FILE	".Bstar"
+
 void blast __P((void));
 void bury __P((void));
 int card __P((const char *, int));
@@ -325,7 +327,7 @@
 void getutmp __P((char *));
 int give __P((void));
 int hash __P((const char *));
-void initialize __P((char));
+void initialize __P((const char *));
 void install __P((struct wlist *));
 int jump __P((void));
 void kiss __P((void));
@@ -347,9 +349,10 @@
 int put __P((void));
 int puton __P((void));
 void ravage __P((void));
-void restore __P((void));
+void restore __P((const char *));
 int ride __P((void));
-void save __P((void));
+void save __P((const char *));
+char *save_file_name __P((const char *, size_t));
 void screen __P((void));
 int shoot __P((void));
 void succumb __P((int));
diff -ruN battlestar/init.c battlestar+/init.c
--- battlestar/init.c	Wed Feb 10 01:36:50 1999
+++ battlestar+/init.c	Mon Jul 26 22:07:41 1999
@@ -46,9 +46,10 @@
 
 void
 initialize(startup)
-	char    startup;
+	const char   *startup;
 {
 	const struct objs *p;
+	char *savefile;
 
 	puts("Version 4.2, fall 1984.");
 	puts("First Adventure game written by His Lordship, the honorable");
@@ -57,7 +58,7 @@
 	srand(getpid());
 	getutmp(uname);
 	wordinit();
-	if (startup) {
+	if (startup == NULL) {
 		direction = NORTH;
 		ourtime = 0;
 		snooze = CYCLE * 1.5;
@@ -67,8 +68,11 @@
 		torps = TORPEDOES;
 		for (p = dayobjs; p->room != 0; p++)
 			setbit(location[p->room].objects, p->obj);
-	} else
-		restore();
+	} else {
+		savefile = save_file_name(startup, strlen(startup));
+		restore(savefile);
+		free(savefile);
+	}
 	wiz = wizard(uname);
 	signal(SIGINT, diesig);
 }
diff -ruN battlestar/save.c battlestar+/save.c
--- battlestar/save.c	Sun Sep 13 15:24:41 1998
+++ battlestar+/save.c	Sun Jul 18 08:19:58 1999
@@ -45,20 +45,17 @@
 #include "extern.h"
 
 void
-restore()
+restore(filename)
+	const char *filename;
 {
-	char   *home;
-	char    home1[100];
 	int     n;
 	int     tmp;
 	FILE   *fp;
 
-	home = getenv("HOME");
-	strcpy(home1, home);
-	strcat(home1, "/Bstar");
-	if ((fp = fopen(home1, "r")) == 0) {
-		err(1, "fopen %s", home1);
-		return;
+	if (filename == NULL)
+		exit(1); /* Error determining save file name.  */
+	if ((fp = fopen(filename, "r")) == 0) {
+		err(1, "fopen %s", filename);
 	}
 	fread(&WEIGHT, sizeof WEIGHT, 1, fp);
 	fread(&CUMBER, sizeof CUMBER, 1, fp);
@@ -94,28 +91,27 @@
 	fread(&loved, sizeof loved, 1, fp);
 	fread(&pleasure, sizeof pleasure, 1, fp);
 	fread(&power, sizeof power, 1, fp);
+	/* We must check the last read, to catch truncated save files */
 	if (fread(&ego, sizeof ego, 1, fp) < 1)
-		errx(1, "save file %s too short", home1);
+		errx(1, "save file %s too short", filename);
 	fclose(fp);
 }
 
 void
-save()
+save(filename)
+	const char *filename;
 {
-	char   *home;
-	char    home1[100];
 	int     n;
 	int     tmp;
 	FILE   *fp;
 
-	home = getenv("HOME");
-	strcpy(home1, home);
-	strcat(home1, "/Bstar");
-	if ((fp = fopen(home1, "w")) == 0) {
-		warn("fopen %s", home1);
+	if (filename == NULL)
+		return; /* Error determining save file name.  */
+	if ((fp = fopen(filename, "w")) == NULL) {
+		warn("fopen %s", filename);
 		return;
 	}
-	printf("Saved in %s.\n", home1);
+	printf("Saved in %s.\n", filename);
 	fwrite(&WEIGHT, sizeof WEIGHT, 1, fp);
 	fwrite(&CUMBER, sizeof CUMBER, 1, fp);
 	fwrite(&ourclock, sizeof ourclock, 1, fp);
@@ -153,6 +149,55 @@
 	fwrite(&ego, sizeof ego, 1, fp);
 	fflush(fp);
 	if (ferror(fp))
-		warn("fwrite %s", home1);
+		warn("fwrite %s", filename);
 	fclose(fp);
+}
+
+/*
+ * Given a save file name (possibly from fgetln, so without terminating NUL),
+ * determine the name of the file to be saved to by adding the HOME
+ * directory if the name does not contain a slash.  Name will be allocated
+ * with malloc(3).
+ */
+char *
+save_file_name(filename, len)
+	const char *filename;
+	size_t len;
+{
+	char   *home;
+	char   *newname;
+	size_t	tmpl;
+
+	if (memchr(filename, '/', len)) {
+		newname = malloc(len + 1);
+		if (newname == NULL) {
+			warnx("out of memory");
+			return NULL;
+		}
+		memcpy(newname, filename, len);
+		newname[len] = 0;
+	} else {
+		home = getenv("HOME");
+		if (home != NULL) {
+			tmpl = strlen(home);
+			newname = malloc(tmpl + len + 2);
+			if (newname == NULL) {
+				warnx("out of memory");
+				return NULL;
+			}
+			memcpy(newname, home, tmpl);
+			newname[tmpl] = '/';
+			memcpy(newname + tmpl + 1, filename, len);
+			newname[tmpl + len + 1] = 0;
+		} else {
+			newname = malloc(len + 1);
+			if (newname == NULL) {
+				warnx("out of memory");
+				return NULL;
+			}
+			memcpy(newname, filename, len);
+			newname[len] = 0;
+		}
+	}
+	return newname;
 }
>Audit-Trail:
>Unformatted: