Subject: Re: bin/34750: string handling cleanup for rogue(6)
To: None <gnats-bugs@NetBSD.org>
From: David Holland <dholland@eecs.harvard.edu>
List: netbsd-bugs
Date: 12/27/2007 18:16:40
 > [rogue patches from a long time ago]

Here's an updated patch against HEAD. I'm now in a position to commit
this if it's ok, so just give me the word...

Index: hit.c
===================================================================
RCS file: /cvsroot/src/games/rogue/hit.c,v
retrieving revision 1.7
diff -u -p -r1.7 hit.c
--- hit.c	7 Aug 2003 09:37:37 -0000	1.7
+++ hit.c	27 Dec 2007 22:51:55 -0000
@@ -56,7 +56,7 @@ __RCSID("$NetBSD: hit.c,v 1.7 2003/08/07
 #include "rogue.h"
 
 object *fight_monster = 0;
-char hit_message[80] = "";
+char hit_message[HIT_MESSAGE_SIZE] = "";
 
 void
 mon_hit(monster)
@@ -86,16 +86,13 @@ mon_hit(monster)
 
 	if (!rand_percent(hit_chance)) {
 		if (!fight_monster) {
-			sprintf(hit_message + strlen(hit_message),
-			    "the %s misses", mn);
-			message(hit_message, 1);
+			messagef(1, "%sthe %s misses", hit_message, mn);
 			hit_message[0] = 0;
 		}
 		return;
 	}
 	if (!fight_monster) {
-		sprintf(hit_message + strlen(hit_message), "the %s hit", mn);
-		message(hit_message, 1);
+		messagef(1, "%sthe %s hit", hit_message, mn);
 		hit_message[0] = 0;
 	}
 	if (!(monster->m_flags & STATIONARY)) {
@@ -139,7 +136,8 @@ rogue_hit(monster, force_hit)
 		}
 		if (!rand_percent(hit_chance)) {
 			if (!fight_monster) {
-				(void) strcpy(hit_message, "you miss  ");
+				(void) strlcpy(hit_message, "you miss  ",
+					       sizeof(hit_message));
 			}
 			goto RET;
 		}
@@ -152,7 +150,8 @@ rogue_hit(monster, force_hit)
 		}
 		if (mon_damage(monster, damage)) {	/* still alive? */
 			if (!fight_monster) {
-				(void) strcpy(hit_message, "you hit  ");
+				(void) strlcpy(hit_message, "you hit  ",
+					       sizeof(hit_message));
 			}
 		}
 RET:	check_gold_seeker(monster);
@@ -186,9 +185,20 @@ get_damage(ds, r)
 
 	while (ds[i]) {
 		n = get_number(ds+i);
-		while (ds[i++] != 'd') ;
+		while ((ds[i] != 'd') && ds[i]) {
+			i++;
+		}
+		if (ds[i] == 'd') {
+			i++;
+		}
+
 		d = get_number(ds+i);
-		while ((ds[i] != '/') && ds[i]) i++;
+		while ((ds[i] != '/') && ds[i]) {
+			i++;
+		}
+		if (ds[i] == '/') {
+			i++;
+		}
 
 		for (j = 0; j < n; j++) {
 			if (r) {
@@ -197,9 +207,6 @@ get_damage(ds, r)
 				total += d;
 			}
 		}
-		if (ds[i] == '/') {
-			i++;
-		}
 	}
 	return(total);
 }
@@ -208,7 +215,7 @@ int
 get_w_damage(obj)
 	const object *obj;
 {
-	char new_damage[12];
+	char new_damage[32];
 	int tmp_to_hit, tmp_damage;
 	int i = 0;
 
@@ -216,10 +223,16 @@ get_w_damage(obj)
 		return(-1);
 	}
 	tmp_to_hit = get_number(obj->damage) + obj->hit_enchant;
-	while (obj->damage[i++] != 'd') ;
+	while ((obj->damage[i] != 'd') && obj->damage[i]) {
+		i++;
+	}
+	if (obj->damage[i] == 'd') {
+		i++;
+	}
 	tmp_damage = get_number(obj->damage + i) + obj->d_enchant;
 
-	sprintf(new_damage, "%dd%d", tmp_to_hit, tmp_damage);
+	snprintf(new_damage, sizeof(new_damage), "%dd%d", 
+		tmp_to_hit, tmp_damage);
 
 	return(get_damage(new_damage, 1));
 }
@@ -312,8 +325,7 @@ mon_damage(monster, damage)
 		fight_monster = 0;
 		cough_up(monster);
 		mn = mon_name(monster);
-		sprintf(hit_message+strlen(hit_message), "defeated the %s", mn);
-		message(hit_message, 1);
+		messagef(1, "%sdefeated the %s", hit_message, mn);
 		hit_message[0] = 0;
 		add_exp(monster->kill_exp, 1);
 		take_from_pack(monster, &level_monsters);
@@ -341,7 +353,7 @@ fight(to_the_death)
 	while (!is_direction(ch = rgetchar(), &d)) {
 		sound_bell();
 		if (first_miss) {
-			message("direction?", 0);
+			messagef(0, "direction?");
 			first_miss = 0;
 		}
 	}
@@ -355,7 +367,7 @@ fight(to_the_death)
 	c = mvinch(row, col);
 	if (((c < 'A') || (c > 'Z')) ||
 		(!can_move(rogue.row, rogue.col, row, col))) {
-		message("I see no monster there", 0);
+		messagef(0, "I see no monster there");
 		return;
 	}
 	if (!(fight_monster = object_at(&level_monsters, row, col))) {
@@ -465,7 +477,7 @@ s_con_mon(monster)
 	if (con_mon) {
 		monster->m_flags |= CONFUSED;
 		monster->moves_confused += get_rand(12, 22);
-		message("the monster appears confused", 0);
+		messagef(0, "the monster appears confused");
 		con_mon = 0;
 	}
 }
Index: init.c
===================================================================
RCS file: /cvsroot/src/games/rogue/init.c,v
retrieving revision 1.14
diff -u -p -r1.14 init.c
--- init.c	15 Dec 2007 19:44:43 -0000	1.14
+++ init.c	27 Dec 2007 22:51:55 -0000
@@ -96,7 +96,8 @@ init(argc, argv)
 	if ((!pn) || (strlen(pn) >= MAX_OPT_LEN)) {
 		clean_up("Hey!  Who are you?");
 	}
-	(void) strcpy(login_name, pn);
+	/* LOGIN_NAME_SIZE == MAX_OPT_LEN now, but just in case... */
+	(void) strlcpy(login_name, pn, sizeof(login_name));
 
 	do_args(argc, argv);
 	do_opts();
@@ -238,7 +239,7 @@ onintr(dummy)
 		did_int = 1;
 	} else {
 		check_message();
-		message("interrupt", 1);
+		messagef(1, "interrupt");
 	}
 	md_heed_signals();
 }
@@ -341,6 +342,7 @@ env_get_value(s, e, add_blank)
 			break;
 		}
 	}
+	/* note: edit_opts() in room.c depends on this being the right size */
 	*s = md_malloc(MAX_OPT_LEN + 2);
 	if (*s == NULL)
 		clean_up("out of memory");
@@ -357,9 +359,10 @@ init_str(str, dflt)
 	const char *dflt;
 {
 	if (!(*str)) {
+		/* note: edit_opts() in room.c depends on this size */
 		*str = md_malloc(MAX_OPT_LEN + 2);
 		if (*str == NULL)
 			clean_up("out of memory");
-		(void) strcpy(*str, dflt);
+		(void) strlcpy(*str, dflt, MAX_OPT_LEN + 2);
 	}
 }
Index: inventory.c
===================================================================
RCS file: /cvsroot/src/games/rogue/inventory.c,v
retrieving revision 1.10
diff -u -p -r1.10 inventory.c
--- inventory.c	14 May 2006 03:15:50 -0000	1.10
+++ inventory.c	27 Dec 2007 22:51:56 -0000
@@ -53,6 +53,7 @@ __RCSID("$NetBSD: inventory.c,v 1.10 200
  *
  */
 
+#include <stdarg.h>
 #include "rogue.h"
 
 boolean is_wood[WANDS];
@@ -216,43 +217,56 @@ inventory(pack, mask)
 	unsigned short mask;
 {
 	object *obj;
-	short i = 0, j, maxlen = 0, n;
-	char descs[MAX_PACK_COUNT+1][DCOLS];
+	short i = 0, j;
+	size_t maxlen = 0, n;
 	short row, col;
 
+	struct {
+		short letter;
+		short sepchar;
+		char desc[DCOLS];
+		char savebuf[DCOLS+8];   
+	} descs[MAX_PACK_COUNT+1];
+
+
 	obj = pack->next_object;
 
 	if (!obj) {
-		message("your pack is empty", 0);
+		messagef(0, "your pack is empty");
 		return;
 	}
 	while (obj) {
 		if (obj->what_is & mask) {
-			descs[i][0] = ' ';
-			descs[i][1] = obj->ichar;
-			descs[i][2] = ((obj->what_is & ARMOR) && obj->is_protected)
+			descs[i].letter = obj->ichar;
+			descs[i].sepchar = ((obj->what_is & ARMOR) && obj->is_protected)
 				? '}' : ')';
-			descs[i][3] = ' ';
-			get_desc(obj, descs[i]+4);
-			if ((n = strlen(descs[i])) > maxlen) {
+			get_desc(obj, descs[i].desc, sizeof(descs[i].desc));
+			n = strlen(descs[i].desc) + 4;
+			if (n > maxlen) {
 				maxlen = n;
 			}
-		i++;
+			i++;
+			/*assert(i<=MAX_PACK_COUNT);*/
 		}
 		obj = obj->next_object;
 	}
-	(void) strcpy(descs[i++], press_space);
 	if (maxlen < 27) maxlen = 27;
+	if (maxlen > DCOLS-2) maxlen = DCOLS-2;
 	col = DCOLS - (maxlen + 2);
 
-	for (row = 0; ((row < i) && (row < DROWS)); row++) {
-		if (row > 0) {
-			for (j = col; j < DCOLS; j++) {
-				descs[row-1][j-col] = mvinch(row, j);
-			}
-			descs[row-1][j-col] = 0;
+	for (row = 0; ((row <= i) && (row < DROWS)); row++) {
+		for (j = col; j < DCOLS; j++) {
+			descs[row].savebuf[j-col] = mvinch(row, j);
+		}
+		descs[row].savebuf[j-col] = 0;
+		if (row < i) {
+			mvprintw(row, col, " %c%c %s", 
+				descs[row].letter, descs[row].sepchar, 
+				descs[row].desc);
+		}
+		else {
+			mvaddstr(row, col, press_space);
 		}
-		mvaddstr(row, col, descs[row]);
 		clrtoeol();
 	}
 	refresh();
@@ -261,8 +275,8 @@ inventory(pack, mask)
 	move(0, 0);
 	clrtoeol();
 
-	for (j = 1; ((j < i) && (j < DROWS)); j++) {
-		mvaddstr(j, col, descs[j-1]);
+	for (j = 1; ((j <= i) && (j < DROWS)); j++) {
+		mvaddstr(j, col, descs[j].savebuf);
 	}
 }
 
@@ -274,7 +288,7 @@ id_com()
 
 	while (ch != CANCEL) {
 		check_message();
-		message("Character you want help for (* for all):", 0);
+		messagef(0, "Character you want help for (* for all):");
 
 		refresh();
 		ch = getchar();
@@ -334,7 +348,7 @@ MORE:
 			if (!pr_com_id(ch)) {
 				if (!pr_motion_char(ch)) {
 					check_message();
-					message("unknown character", 0);
+					messagef(0, "unknown character");
 				}
 			}
 			ch = CANCEL;
@@ -353,7 +367,7 @@ pr_com_id(ch)
 		return(0);
 	}
 	check_message();
-	message(com_id_tab[i].com_desc, 0);
+	messagef(0, "%s", com_id_tab[i].com_desc);
 	return(1);
 }
 
@@ -393,20 +407,18 @@ pr_motion_char(ch)
 			(ch == '\031') ||
 			(ch == '\016') ||
 			(ch == '\002')) {
-		char until[18], buf[DCOLS];
+		const char *until;
 		int n = 0;	/* XXX: GCC */
-
 		if (ch <= '\031') {
 			ch += 96;
-			(void) strcpy(until, "until adjascent");
+			until = " until adjacent";
 		} else {
 			ch += 32;
-			until[0] = '\0';
+			until = "";
 		}
 		(void) get_com_id(&n, ch);
-		sprintf(buf, "run %s %s", com_id_tab[n].com_desc + 8, until);
 		check_message();
-		message(buf, 0);
+		messagef(0, "run %s%s", com_id_tab[n].com_desc + 8, until);
 		return(1);
 	} else {
 		return(0);
@@ -422,9 +434,10 @@ mix_colors()
 	for (i = 0; i <= 32; i++) {
 		j = get_rand(0, (POTIONS - 1));
 		k = get_rand(0, (POTIONS - 1));
-		memcpy(t, id_potions[j].title, MAX_ID_TITLE_LEN);
-		memcpy(id_potions[j].title, id_potions[k].title, MAX_ID_TITLE_LEN);
-		memcpy(id_potions[k].title, t, MAX_ID_TITLE_LEN);
+		strlcpy(t, id_potions[j].title, sizeof(t));
+		strlcpy(id_potions[j].title, id_potions[k].title,
+			sizeof(id_potions[j].title));
+		strlcpy(id_potions[k].title, t, sizeof(id_potions[k].title));
 	}
 }
 
@@ -433,181 +446,246 @@ make_scroll_titles()
 {
 	short i, j, n;
 	short sylls, s;
+	size_t maxlen = sizeof(id_scrolls[0].title);
 
 	for (i = 0; i < SCROLS; i++) {
 		sylls = get_rand(2, 5);
-		(void) strcpy(id_scrolls[i].title, "'");
+		(void) strlcpy(id_scrolls[i].title, "'", maxlen);
 
 		for (j = 0; j < sylls; j++) {
 			s = get_rand(1, (MAXSYLLABLES-1));
-			(void) strcat(id_scrolls[i].title, syllables[s]);
+			(void) strlcat(id_scrolls[i].title, syllables[s],
+					maxlen);
 		}
+		/* trim trailing space */
 		n = strlen(id_scrolls[i].title);
-		(void) strcpy(id_scrolls[i].title+(n-1), "' ");
+		id_scrolls[i].title[n-1] = 0;
+
+		(void) strlcat(id_scrolls[i].title, "' ", maxlen);
+	}
+}
+
+struct sbuf {
+	char *buf;
+	size_t maxlen;
+};
+
+static void sbuf_init __P((struct sbuf *s, char *buf, size_t maxlen));
+static void sbuf_addstr __P((struct sbuf *s, const char *str));
+static void sbuf_addf __P((struct sbuf *s, const char *fmt, ...));
+static void desc_count __P((struct sbuf *s, int n));
+static void desc_called __P((struct sbuf *s, const object *));
+
+static
+void
+sbuf_init(s, buf, maxlen)
+	struct sbuf *s;
+	char *buf;
+	size_t maxlen;
+{
+	s->buf = buf;
+	s->maxlen = maxlen;
+	/*assert(maxlen>0);*/
+	s->buf[0] = 0;
+}
+
+static
+void
+sbuf_addstr(s, str)
+	struct sbuf *s;
+	const char *str;
+{
+	strlcat(s->buf, str, s->maxlen);
+}
+
+static void sbuf_addf(struct sbuf *s, const char *fmt, ...)
+	__attribute__((__format__(__printf__, 2, 3)));
+
+static
+void
+sbuf_addf(struct sbuf *s, const char *fmt, ...)
+{
+	va_list ap;
+	size_t initlen;
+
+	initlen = strlen(s->buf);
+	va_start(ap, fmt);
+	vsnprintf(s->buf+initlen, s->maxlen-initlen, fmt, ap);
+	va_end(ap);
+}
+
+static
+void
+desc_count(s, n)
+	struct sbuf *s;
+	int n;
+{
+	if (n == 1) {
+		sbuf_addstr(s, "an ");
+	} else {
+		sbuf_addf(s, "%d ", n);
 	}
 }
 
+static
 void
-get_desc(obj, desc)
+desc_called(s, obj)
+	struct sbuf *s;
+	const object *obj;
+{
+	struct id *id_table;
+
+	id_table = get_id_table(obj);
+	sbuf_addstr(s, name_of(obj));
+	sbuf_addstr(s, "called ");
+	sbuf_addstr(s, id_table[obj->which_kind].title);
+}
+
+void
+get_desc(obj, desc, desclen)
 	const object *obj;
 	char *desc;
+	size_t desclen;
 {
 	const char *item_name;
 	struct id *id_table;
-	char more_info[32];
-	short i;
+	struct sbuf db;
+	unsigned short objtype_id_status;
 
 	if (obj->what_is == AMULET) {
-		(void) strcpy(desc, "the amulet of Yendor ");
+		(void) strlcpy(desc, "the amulet of Yendor ", desclen);
 		return;
 	}
-	item_name = name_of(obj);
 
 	if (obj->what_is == GOLD) {
-		sprintf(desc, "%d pieces of gold", obj->quantity);
+		snprintf(desc, desclen, "%d pieces of gold", obj->quantity);
 		return;
 	}
 
-	if (obj->what_is != ARMOR) {
-		if (obj->quantity == 1) {
-			(void) strcpy(desc, "a ");
-		} else {
-			sprintf(desc, "%d ", obj->quantity);
+	item_name = name_of(obj);
+	id_table = get_id_table(obj);
+	if (wizard || id_table == NULL) {
+		objtype_id_status = IDENTIFIED;
+	}
+	else {
+		objtype_id_status = id_table[obj->which_kind].id_status;
+	}
+	if (obj->what_is & (WEAPON | ARMOR | WAND | RING)) {
+		if (obj->identified) {
+			objtype_id_status = IDENTIFIED;
 		}
 	}
-	if (obj->what_is == FOOD) {
+
+	sbuf_init(&db, desc, desclen);
+
+	switch (obj->what_is) {
+	case FOOD:
 		if (obj->which_kind == RATION) {
 			if (obj->quantity > 1) {
-				sprintf(desc, "%d rations of ", obj->quantity);
+				sbuf_addf(&db,
+					 "%d rations of %s", obj->quantity,
+					 item_name);
 			} else {
-				(void) strcpy(desc, "some ");
+				sbuf_addf(&db, "some %s", item_name);
 			}
 		} else {
-			(void) strcpy(desc, "a ");
+			sbuf_addf(&db, "an %s", item_name);
 		}
-		(void) strcat(desc, item_name);
-		goto ANA;
-	}
-	id_table = get_id_table(obj);
-
-	if (wizard) {
-		goto ID;
-	}
-	if (obj->what_is & (WEAPON | ARMOR | WAND | RING)) {
-		goto CHECK;
-	}
-
-	switch(id_table[obj->which_kind].id_status) {
-	case UNIDENTIFIED:
-CHECK:
-		switch(obj->what_is) {
-		case SCROL:
-			(void) strcat(desc, item_name);
-			(void) strcat(desc, "entitled: ");
-			(void) strcat(desc, id_table[obj->which_kind].title);
-			break;
-		case POTION:
-			(void) strcat(desc, id_table[obj->which_kind].title);
-			(void) strcat(desc, item_name);
-			break;
-		case WAND:
-		case RING:
-			if (obj->identified ||
-			(id_table[obj->which_kind].id_status == IDENTIFIED)) {
-				goto ID;
-			}
-			if (id_table[obj->which_kind].id_status == CALLED) {
-				goto CALL;
-			}
-			(void) strcat(desc, id_table[obj->which_kind].title);
-			(void) strcat(desc, item_name);
-			break;
-		case ARMOR:
-			if (obj->identified) {
-				goto ID;
-			}
-			(void) strcpy(desc, id_table[obj->which_kind].title);
-			break;
-		case WEAPON:
-			if (obj->identified) {
-				goto ID;
-			}
-			(void) strcat(desc, name_of(obj));
-			break;
+		break;
+	case SCROL:
+		desc_count(&db, obj->quantity);
+		if (objtype_id_status==UNIDENTIFIED) {
+			sbuf_addstr(&db, item_name);
+			sbuf_addstr(&db, "entitled: ");
+			sbuf_addstr(&db, id_table[obj->which_kind].title);
+		} else if (objtype_id_status==CALLED) {
+			desc_called(&db, obj);
+		} else {
+			sbuf_addstr(&db, item_name);
+			sbuf_addstr(&db, id_table[obj->which_kind].real);
 		}
 		break;
-	case CALLED:
-CALL:	switch(obj->what_is) {
-		case SCROL:
-		case POTION:
-		case WAND:
-		case RING:
-			(void) strcat(desc, item_name);
-			(void) strcat(desc, "called ");
-			(void) strcat(desc, id_table[obj->which_kind].title);
-			break;
+	case POTION:
+		desc_count(&db, obj->quantity);
+		if (objtype_id_status==UNIDENTIFIED) {
+			sbuf_addstr(&db, id_table[obj->which_kind].title);
+			sbuf_addstr(&db, item_name);
+		} else if (objtype_id_status==CALLED) {
+			desc_called(&db, obj);
+		} else {
+			sbuf_addstr(&db, item_name);
+			sbuf_addstr(&db, id_table[obj->which_kind].real);
 		}
 		break;
-	case IDENTIFIED:
-ID:		switch(obj->what_is) {
-		case SCROL:
-		case POTION:
-			(void) strcat(desc, item_name);
-			(void) strcat(desc, id_table[obj->which_kind].real);
-			break;
-		case RING:
-			if (wizard || obj->identified) {
-				if ((obj->which_kind == DEXTERITY) ||
-					(obj->which_kind == ADD_STRENGTH)) {
-					sprintf(more_info, "%s%d ", ((obj->class > 0) ? "+" : ""),
-						obj->class);
-					(void) strcat(desc, more_info);
-				}
-			}
-			(void) strcat(desc, item_name);
-			(void) strcat(desc, id_table[obj->which_kind].real);
-			break;
-		case WAND:
-			(void) strcat(desc, item_name);
-			(void) strcat(desc, id_table[obj->which_kind].real);
+	case WAND:
+		desc_count(&db, obj->quantity);
+		if (objtype_id_status==UNIDENTIFIED) {
+			sbuf_addstr(&db, id_table[obj->which_kind].title);
+			sbuf_addstr(&db, item_name);
+		} else if (objtype_id_status==CALLED) {
+			desc_called(&db, obj);
+		} else {
+			sbuf_addstr(&db, item_name);
+			sbuf_addstr(&db, id_table[obj->which_kind].real);
 			if (wizard || obj->identified) {
-				sprintf(more_info, "[%d]", obj->class);
-				(void) strcat(desc, more_info);
+				sbuf_addf(&db, "[%d]", obj->class);
 			}
-			break;
-		case ARMOR:
-			sprintf(desc, "%s%d ", ((obj->d_enchant >= 0) ? "+" : ""),
-			obj->d_enchant);
-			(void) strcat(desc, id_table[obj->which_kind].title);
-			sprintf(more_info, "[%d] ", get_armor_class(obj));
-			(void) strcat(desc, more_info);
-			break;
-		case WEAPON:
-			sprintf(desc+strlen(desc), "%s%d,%s%d ",
-			((obj->hit_enchant >= 0) ? "+" : ""), obj->hit_enchant,
-			((obj->d_enchant >= 0) ? "+" : ""), obj->d_enchant);
-			(void) strcat(desc, name_of(obj));
-			break;
 		}
 		break;
-	}
-ANA:
-	if (!strncmp(desc, "a ", 2)) {
-		if (is_vowel(desc[2])) {
-			for (i = strlen(desc) + 1; i > 1; i--) {
-				desc[i] = desc[i-1];
+	case RING:
+		desc_count(&db, obj->quantity);
+		if (objtype_id_status==UNIDENTIFIED) {
+			sbuf_addstr(&db, id_table[obj->which_kind].title);
+			sbuf_addstr(&db, item_name);
+		} else if (objtype_id_status==CALLED) {
+			desc_called(&db, obj);
+		} else {
+			if ((wizard || obj->identified) &&
+			    (obj->which_kind == DEXTERITY ||
+			     obj->which_kind == ADD_STRENGTH)) {
+				sbuf_addf(&db, "%+d ", obj->class);
 			}
-			desc[1] = 'n';
+			sbuf_addstr(&db, item_name);
+			sbuf_addstr(&db, id_table[obj->which_kind].real);
+		}
+		break;
+	case ARMOR:
+		/* no desc_count() */
+		if (objtype_id_status==UNIDENTIFIED) {
+			sbuf_addstr(&db, id_table[obj->which_kind].title);
+		} else {
+			sbuf_addf(&db, "%+d %s[%d] ", obj->d_enchant,
+				id_table[obj->which_kind].title,
+				get_armor_class(obj));
+    		}
+		break;
+	case WEAPON:
+		desc_count(&db, obj->quantity);
+		if (objtype_id_status==UNIDENTIFIED) {
+			sbuf_addstr(&db, name_of(obj));
+		} else {
+			sbuf_addf(&db, "%+d,%+d %s",
+				obj->hit_enchant, obj->d_enchant,
+				name_of(obj));
 		}
+		break;
 	}
+
 	if (obj->in_use_flags & BEING_WIELDED) {
-		(void) strcat(desc, "in hand");
+		sbuf_addstr(&db, "in hand");
 	} else if (obj->in_use_flags & BEING_WORN) {
-		(void) strcat(desc, "being worn");
+		sbuf_addstr(&db, "being worn");
 	} else if (obj->in_use_flags & ON_LEFT_HAND) {
-		(void) strcat(desc, "on left hand");
+		sbuf_addstr(&db, "on left hand");
 	} else if (obj->in_use_flags & ON_RIGHT_HAND) {
-		(void) strcat(desc, "on right hand");
+		sbuf_addstr(&db, "on right hand");
+	}
+
+	if (!strncmp(db.buf, "an ", 3)) {
+		if (!is_vowel(db.buf[3])) {
+			memmove(db.buf+2, db.buf+3, strlen(db.buf+3)+1);
+			db.buf[1] = ' ';
+		}
 	}
 }
 
@@ -625,7 +703,8 @@ get_wand_and_ring_materials()
 			j = get_rand(0, WAND_MATERIALS-1);
 		} while (used[j]);
 		used[j] = 1;
-		(void) strcpy(id_wands[i].title, wand_materials[j]);
+		(void) strlcpy(id_wands[i].title, wand_materials[j],
+			       sizeof(id_wands[i].title));
 		is_wood[i] = (j > MAX_METAL);
 	}
 	for (i = 0; i < GEMS; i++) {
@@ -636,7 +715,8 @@ get_wand_and_ring_materials()
 			j = get_rand(0, GEMS-1);
 		} while (used[j]);
 		used[j] = 1;
-		(void) strcpy(id_rings[i].title, gems[j]);
+		(void) strlcpy(id_rings[i].title, gems[j],
+			       sizeof(id_rings[i].title));
 	}
 }
 
@@ -644,7 +724,7 @@ void
 single_inv(ichar)
 	short ichar;
 {
-	short ch;
+	short ch, ch2;
 	char desc[DCOLS];
 	object *obj;
 
@@ -654,15 +734,12 @@ single_inv(ichar)
 		return;
 	}
 	if (!(obj = get_letter_object(ch))) {
-		message("no such item.", 0);
+		messagef(0, "no such item.");
 		return;
 	}
-	desc[0] = ch;
-	desc[1] = ((obj->what_is & ARMOR) && obj->is_protected) ? '}' : ')';
-	desc[2] = ' ';
-	desc[3] = 0;
-	get_desc(obj, desc+3);
-	message(desc, 0);
+	ch2 = ((obj->what_is & ARMOR) && obj->is_protected) ? '}' : ')';
+	get_desc(obj, desc, sizeof(desc));
+	messagef(0, "%c%c %s", ch, ch2, desc);
 }
 
 struct id *
@@ -694,13 +771,13 @@ inv_armor_weapon(is_weapon)
 		if (rogue.weapon) {
 			single_inv(rogue.weapon->ichar);
 		} else {
-			message("not wielding anything", 0);
+			messagef(0, "not wielding anything");
 		}
 	} else {
 		if (rogue.armor) {
 			single_inv(rogue.armor->ichar);
 		} else {
-			message("not wearing anything", 0);
+			messagef(0, "not wearing anything");
 		}
 	}
 }
@@ -710,9 +787,8 @@ id_type()
 {
 	const char *id;
 	int ch;
-	char buf[DCOLS];
 
-	message("what do you want identified?", 0);
+	messagef(0, "what do you want identified?");
 
 	ch = rgetchar();
 
@@ -781,6 +857,5 @@ id_type()
 		}
 	}
 	check_message();
-	sprintf(buf, "'%c': %s", ch, id);
-	message(buf, 0);
+	messagef(0, "'%c': %s", ch, id);
 }
Index: level.c
===================================================================
RCS file: /cvsroot/src/games/rogue/level.c,v
retrieving revision 1.7
diff -u -p -r1.7 level.c
--- level.c	7 Aug 2003 09:37:38 -0000	1.7
+++ level.c	27 Dec 2007 22:51:56 -0000
@@ -769,8 +769,8 @@ put_player(nr)
 	rn = get_room_number(rogue.row, rogue.col);
 	wake_room(rn, 1, rogue.row, rogue.col);
 	if (new_level_message) {
-		message(new_level_message, 0);
-		new_level_message = 0;
+		messagef(0, "%s", new_level_message);
+		new_level_message = NULL;
 	}
 	mvaddch(rogue.row, rogue.col, rogue.fchar);
 }
@@ -783,12 +783,12 @@ drop_check()
 	}
 	if (dungeon[rogue.row][rogue.col] & STAIRS) {
 		if (levitate) {
-			message("you're floating in the air!", 0);
+			messagef(0, "you're floating in the air!");
 			return(0);
 		}
 		return(1);
 	}
-	message("I see no way down", 0);
+	messagef(0, "I see no way down");
 	return(0);
 }
 
@@ -797,11 +797,11 @@ check_up()
 {
 	if (!wizard) {
 		if (!(dungeon[rogue.row][rogue.col] & STAIRS)) {
-			message("I see no way up", 0);
+			messagef(0, "I see no way up");
 			return(0);
 		}
 		if (!has_amulet()) {
-			message("your way is magically blocked", 0);
+			messagef(0, "your way is magically blocked");
 			return(0);
 		}
 	}
@@ -820,7 +820,6 @@ add_exp(e, promotion)
 	int e;
 	boolean promotion;
 {
-	char mbuf[40];
 	short new_exp;
 	short i, hp;
 
@@ -832,8 +831,7 @@ add_exp(e, promotion)
 			rogue.exp_points = MAX_EXP + 1;
 		}
 		for (i = rogue.exp+1; i <= new_exp; i++) {
-			sprintf(mbuf, "welcome to level %d", i);
-			message(mbuf, 0);
+			messagef(0, "welcome to level %d", i);
 			if (promotion) {
 				hp = hp_raise();
 				rogue.hp_current += hp;
@@ -873,7 +871,6 @@ hp_raise()
 void
 show_average_hp()
 {
-	char mbuf[80];
 	float real_average;
 	float effective_average;
 
@@ -885,9 +882,8 @@ show_average_hp()
 		effective_average = (float) (rogue.hp_max - INIT_HP) / (rogue.exp - 1);
 
 	}
-	sprintf(mbuf, "R-Hp: %.2f, E-Hp: %.2f (!: %d, V: %d)", real_average,
+	messagef(0, "R-Hp: %.2f, E-Hp: %.2f (!: %d, V: %d)", real_average,
 		effective_average, extra_hp, less_hp);
-	message(mbuf, 0);
 }
 
 void
Index: machdep.c
===================================================================
RCS file: /cvsroot/src/games/rogue/machdep.c,v
retrieving revision 1.14
diff -u -p -r1.14 machdep.c
--- machdep.c	24 Apr 2006 19:00:30 -0000	1.14
+++ machdep.c	27 Dec 2007 22:51:56 -0000
@@ -456,7 +456,7 @@ md_lock(l)
 		setegid(egid);
 		if ((fd = open(_PATH_SCOREFILE, O_RDONLY)) < 1) {
 			setegid(gid);
-			message("cannot lock score file", 0);
+			messagef(0, "cannot lock score file");
 			return;
 		}
 		setegid(gid);
@@ -472,10 +472,13 @@ md_lock(l)
 /* md_shell():
  *
  * This function spawns a shell for the user to use.  When this shell is
- * terminated, the game continues.  Since this program may often be run
- * setuid to gain access to privileged files, care is taken that the shell
- * is run with the user's REAL user id, and not the effective user id.
- * The effective user id is restored after the shell completes.
+ * terminated, the game continues.
+ *
+ * It is important that the game not give the shell the privileges the
+ * game uses to access the scores file. This version of the game runs
+ * with privileges low by default; only the saved gid (if setgid) or uid
+ * (if setuid) will be privileged, but that privilege is discarded by
+ * exec().
  */
 
 void
@@ -483,11 +486,19 @@ md_shell(shell)
 	const char *shell;
 {
 	int w;
+	pid_t pid;
 
-	if (!fork()) {
+	pid = fork();
+	switch (pid) {
+	case -1:
+		break;
+	case 0:
 		execl(shell, shell, (char *) 0);
+		_exit(255);
+	default:
+		waitpid(pid, &w, 0);
+		break;
 	}
-	wait(&w);
 }
 
 #endif
Index: message.c
===================================================================
RCS file: /cvsroot/src/games/rogue/message.c,v
retrieving revision 1.10
diff -u -p -r1.10 message.c
--- message.c	7 Aug 2003 09:37:38 -0000	1.10
+++ message.c	27 Dec 2007 22:51:56 -0000
@@ -55,6 +55,7 @@ __RCSID("$NetBSD: message.c,v 1.10 2003/
 
 #include <signal.h>
 #include <termios.h>
+#include <stdarg.h>
 #include "rogue.h"
 
 char msgs[NMESSAGES][DCOLS] = {"", "", "", "", ""};
@@ -63,6 +64,9 @@ boolean msg_cleared = 1, rmsg = 0;
 char hunger_str[HUNGER_STR_LEN] = "";
 const char *more = "-more-";
 
+static void message __P((const char *, boolean));
+
+static
 void
 message(msg, intrpt)
 	const char *msg;
@@ -86,7 +90,7 @@ message(msg, intrpt)
 	}
 	if (!rmsg) {
 		imsg = (imsg + 1) % NMESSAGES;
-		(void) strcpy(msgs[imsg], msg);
+		(void) strlcpy(msgs[imsg], msg, sizeof(msgs[imsg]));
 	}
 	mvaddstr(MIN_ROW-1, 0, msg);
 	addch(' ');
@@ -103,6 +107,19 @@ message(msg, intrpt)
 }
 
 void
+messagef(boolean intrpt, const char *fmt, ...)
+{
+	va_list ap;
+	char buf[DCOLS];
+
+	va_start(ap, fmt);
+	vsnprintf(buf, sizeof(buf), fmt, ap);
+	va_end(ap);
+
+	message(buf, intrpt);
+}
+
+void
 remessage(c)
 	short c;
 {
@@ -132,9 +149,10 @@ check_message()
 }
 
 int
-get_input_line(prompt, insert, buf, if_cancelled, add_blank, do_echo)
+get_input_line(prompt, insert, buf, buflen, if_cancelled, add_blank, do_echo)
 	const char *prompt, *insert;
 	char *buf;
+	size_t buflen;
 	const char *if_cancelled;
 	boolean add_blank;
 	boolean do_echo;
@@ -147,14 +165,14 @@ get_input_line(prompt, insert, buf, if_c
 
 	if (insert[0]) {
 		mvaddstr(0, n + 1, insert);
-		(void) strcpy(buf, insert);
-		i = strlen(insert);
+		(void) strlcpy(buf, insert, buflen);
+		i = strlen(buf);
 		move(0, (n + i + 1));
 		refresh();
 	}
 
 	while (((ch = rgetchar()) != '\r') && (ch != '\n') && (ch != CANCEL)) {
-		if ((ch >= ' ') && (ch <= '~') && (i < MAX_TITLE_LENGTH-2)) {
+		if ((ch >= ' ') && (ch <= '~') && (i < buflen-2)) {
 			if ((ch != ' ') || (i > 0)) {
 				buf[i++] = ch;
 				if (do_echo) {
@@ -239,9 +257,7 @@ print_stats(stat_mask)
 			mvaddstr(row, 0, "Level: ");
 		}
 		/* max level taken care of in make_level() */
-		sprintf(buf, "%d", cur_level);
-		mvaddstr(row, 7, buf);
-		pad(buf, 2);
+		mvprintw(row, 7, "%-2d", cur_level);
 	}
 	if (stat_mask & STAT_GOLD) {
 		if (label) {
@@ -250,9 +266,7 @@ print_stats(stat_mask)
 		if (rogue.gold > MAX_GOLD) {
 			rogue.gold = MAX_GOLD;
 		}
-		sprintf(buf, "%ld", rogue.gold);
-		mvaddstr(row, 16, buf);
-		pad(buf, 6);
+		mvprintw(row, 16, "%-6ld", rogue.gold);
 	}
 	if (stat_mask & STAT_HP) {
 		if (label) {
@@ -262,9 +276,9 @@ print_stats(stat_mask)
 			rogue.hp_current -= (rogue.hp_max - MAX_HP);
 			rogue.hp_max = MAX_HP;
 		}
-		sprintf(buf, "%d(%d)", rogue.hp_current, rogue.hp_max);
-		mvaddstr(row, 27, buf);
-		pad(buf, 8);
+		snprintf(buf, sizeof(buf), "%d(%d)",
+			rogue.hp_current, rogue.hp_max);
+		mvprintw(row, 27, "%-8s", buf);
 	}
 	if (stat_mask & STAT_STRENGTH) {
 		if (label) {
@@ -274,10 +288,9 @@ print_stats(stat_mask)
 			rogue.str_current -= (rogue.str_max - MAX_STRENGTH);
 			rogue.str_max = MAX_STRENGTH;
 		}
-		sprintf(buf, "%d(%d)", (rogue.str_current + add_strength),
-			rogue.str_max);
-		mvaddstr(row, 41, buf);
-		pad(buf, 6);
+		snprintf(buf, sizeof(buf), "%d(%d)",
+			(rogue.str_current + add_strength), rogue.str_max);
+		mvprintw(row, 41, "%-6s", buf);
 	}
 	if (stat_mask & STAT_ARMOR) {
 		if (label) {
@@ -286,9 +299,7 @@ print_stats(stat_mask)
 		if (rogue.armor && (rogue.armor->d_enchant > MAX_ARMOR)) {
 			rogue.armor->d_enchant = MAX_ARMOR;
 		}
-		sprintf(buf, "%d", get_armor_class(rogue.armor));
-		mvaddstr(row, 53, buf);
-		pad(buf, 2);
+		mvprintw(row, 53, "%-2d", get_armor_class(rogue.armor));
 	}
 	if (stat_mask & STAT_EXP) {
 		if (label) {
@@ -300,9 +311,9 @@ print_stats(stat_mask)
 		if (rogue.exp > MAX_EXP_LEVEL) {
 			rogue.exp = MAX_EXP_LEVEL;
 		}
-		sprintf(buf, "%d/%ld", rogue.exp, rogue.exp_points);
-		mvaddstr(row, 61, buf);
-		pad(buf, 11);
+		snprintf(buf, sizeof(buf), "%d/%ld",
+			rogue.exp, rogue.exp_points);
+		mvprintw(row, 61, "%-11s", buf);
 	}
 	if (stat_mask & STAT_HUNGER) {
 		mvaddstr(row, 73, hunger_str);
@@ -312,37 +323,21 @@ print_stats(stat_mask)
 }
 
 void
-pad(s, n)
-	const char *s;
-	short n;
-{
-	short i;
-
-	for (i = strlen(s); i < n; i++) {
-		addch(' ');
-	}
-}
-
-void
 save_screen()
 {
 	FILE *fp;
 	short i, j;
 	char buf[DCOLS+2];
-	boolean found_non_blank;
 
 	if ((fp = fopen("rogue.screen", "w")) != NULL) {
 		for (i = 0; i < DROWS; i++) {
-			found_non_blank = 0;
-			for (j = (DCOLS - 1); j >= 0; j--) {
+			for (j=0; j<DCOLS; j++) {
 				buf[j] = mvinch(i, j);
-				if (!found_non_blank) {
-					if ((buf[j] != ' ') || (j == 0)) {
-						buf[j + ((j == 0) ? 0 : 1)] = 0;
-						found_non_blank = 1;
-					}
-				}
 			}
+			/*buf[DCOLS] = 0; -- redundant */
+			for (j=DCOLS; j>0 && buf[j-1]==' '; j--);
+			buf[j] = 0;
+
 			fputs(buf, fp);
 			putc('\n', fp);
 		}
Index: monster.c
===================================================================
RCS file: /cvsroot/src/games/rogue/monster.c,v
retrieving revision 1.11
diff -u -p -r1.11 monster.c
--- monster.c	30 Mar 2006 04:10:04 -0000	1.11
+++ monster.c	27 Dec 2007 22:51:57 -0000
@@ -709,7 +709,7 @@ create_monster()
 			wake_up(monster);
 		}
 	} else {
-		message("you hear a faint cry of anguish in the distance", 0);
+		messagef(0, "you hear a faint cry of anguish in the distance");
 	}
 }
 
@@ -851,7 +851,7 @@ aggravate()
 {
 	object *monster;
 
-	message("you hear a high pitched humming noise", 0);
+	messagef(0, "you hear a high pitched humming noise");
 
 	monster = level_monsters.next_monster;
 
Index: move.c
===================================================================
RCS file: /cvsroot/src/games/rogue/move.c,v
retrieving revision 1.8
diff -u -p -r1.8 move.c
--- move.c	14 May 2006 03:15:50 -0000	1.8
+++ move.c	27 Dec 2007 22:51:57 -0000
@@ -66,7 +66,7 @@ one_move_rogue(dirch, pickup)
 	short row, col;
 	object *obj;
 	char desc[DCOLS];
-	short n, status, d = 0;	/* XXX: GCC */
+	short status, d = 0;	/* XXX: GCC */
 
 	row = rogue.row;
 	col = rogue.col;
@@ -83,9 +83,9 @@ one_move_rogue(dirch, pickup)
 	if (being_held || bear_trap) {
 		if (!(dungeon[row][col] & MONSTER)) {
 			if (being_held) {
-				message("you are being held", 1);
+				messagef(1, "you are being held");
 			} else {
-				message("you are still stuck in the bear trap", 0);
+				messagef(0, "you are still stuck in the bear trap");
 				(void) reg_move();
 			}
 			return(MOVE_FAILED);
@@ -135,9 +135,10 @@ one_move_rogue(dirch, pickup)
 		}
 		if (pickup && !levitate) {
 			if ((obj = pick_up(row, col, &status)) != NULL) {
-				get_desc(obj, desc);
+				get_desc(obj, desc, sizeof(desc));
 				if (obj->what_is == GOLD) {
 					free_object(obj);
+					messagef(1, "%s", desc);
 					goto NOT_IN_PACK;
 				}
 			} else if (!status) {
@@ -148,17 +149,12 @@ one_move_rogue(dirch, pickup)
 		} else {
 MOVE_ON:
 			obj = object_at(&level_objects, row, col);
-			(void) strcpy(desc, "moved onto ");
-			get_desc(obj, desc+11);
+			get_desc(obj, desc, sizeof(desc));
+			messagef(1, "moved onto %s", desc);
 			goto NOT_IN_PACK;
 		}
-		n = strlen(desc);
-		desc[n] = '(';
-		desc[n+1] = obj->ichar;
-		desc[n+2] = ')';
-		desc[n+3] = 0;
+		messagef(1, "%s(%c)", desc, obj->ichar);
 NOT_IN_PACK:
-		message(desc, 1);
 		(void) reg_move();
 		return(STOPPED_ON_SOMETHING);
 	}
@@ -326,7 +322,7 @@ move_onto()
 	while (!is_direction(ch = rgetchar(), &d)) {
 		sound_bell();
 		if (first_miss) {
-			message("direction? ", 0);
+			messagef(0, "direction? ");
 			first_miss = 0;
 		}
 	}
@@ -382,19 +378,19 @@ check_hunger(msg_only)
 	boolean fainted = 0;
 
 	if (rogue.moves_left == HUNGRY) {
-		(void) strcpy(hunger_str, "hungry");
-		message(hunger_str, 0);
+		(void) strlcpy(hunger_str, "hungry", sizeof(hunger_str));
+		messagef(0, "%s", hunger_str);
 		print_stats(STAT_HUNGER);
 	}
 	if (rogue.moves_left == WEAK) {
-		(void) strcpy(hunger_str, "weak");
-		message(hunger_str, 1);
+		(void) strlcpy(hunger_str, "weak", sizeof(hunger_str));
+		messagef(1, "%s", hunger_str);
 		print_stats(STAT_HUNGER);
 	}
 	if (rogue.moves_left <= FAINT) {
 		if (rogue.moves_left == FAINT) {
-			(void) strcpy(hunger_str, "faint");
-			message(hunger_str, 1);
+			(void) strlcpy(hunger_str, "faint", sizeof(hunger_str));
+			messagef(1, "%s", hunger_str);
 			print_stats(STAT_HUNGER);
 		}
 		n = get_rand(0, (FAINT - rogue.moves_left));
@@ -403,13 +399,13 @@ check_hunger(msg_only)
 			if (rand_percent(40)) {
 				rogue.moves_left++;
 			}
-			message("you faint", 1);
+			messagef(1, "you faint");
 			for (i = 0; i < n; i++) {
 				if (coin_toss()) {
 					mv_mons();
 				}
 			}
-			message(you_can_move_again, 1);
+			messagef(1, you_can_move_again);
 		}
 	}
 	if (msg_only) {
@@ -482,7 +478,7 @@ reg_move()
 	}
 	if (levitate) {
 		if (!(--levitate)) {
-			message("you float gently to the ground", 1);
+			messagef(1, "you float gently to the ground");
 			if (dungeon[rogue.row][rogue.col] & TRAP) {
 				trap_player(rogue.row, rogue.col);
 			}
@@ -490,7 +486,7 @@ reg_move()
 	}
 	if (haste_self) {
 		if (!(--haste_self)) {
-			message("you feel yourself slowing down", 0);
+			messagef(0, "you feel yourself slowing down");
 		}
 	}
 	heal();
Index: object.c
===================================================================
RCS file: /cvsroot/src/games/rogue/object.c,v
retrieving revision 1.10
diff -u -p -r1.10 object.c
--- object.c	30 Mar 2006 04:27:24 -0000	1.10
+++ object.c	27 Dec 2007 22:51:58 -0000
@@ -80,36 +80,36 @@ fighter rogue = {
 };
 
 struct id id_potions[POTIONS] = {
-{100, "blue \0                           ", "of increase strength ", 0},
-{250, "red \0                            ", "of restore strength ", 0},
-{100, "green \0                          ", "of healing ", 0},
-{200, "grey \0                           ", "of extra healing ", 0},
- {10, "brown \0                          ", "of poison ", 0},
-{300, "clear \0                          ", "of raise level ", 0},
- {10, "pink \0                           ", "of blindness ", 0},
- {25, "white \0                          ", "of hallucination ", 0},
-{100, "purple \0                         ", "of detect monster ", 0},
-{100, "black \0                          ", "of detect things ", 0},
- {10, "yellow \0                         ", "of confusion ", 0},
- {80, "plaid \0                          ", "of levitation ", 0},
-{150, "burgundy \0                       ", "of haste self ", 0},
-{145, "beige \0                          ", "of see invisible ", 0}
+{100, "blue ",     "of increase strength ", 0},
+{250, "red ",      "of restore strength ", 0},
+{100, "green ",    "of healing ", 0},
+{200, "grey ",     "of extra healing ", 0},
+ {10, "brown ",    "of poison ", 0},
+{300, "clear ",    "of raise level ", 0},
+ {10, "pink ",     "of blindness ", 0},
+ {25, "white ",    "of hallucination ", 0},
+{100, "purple ",   "of detect monster ", 0},
+{100, "black ",    "of detect things ", 0},
+ {10, "yellow ",   "of confusion ", 0},
+ {80, "plaid ",    "of levitation ", 0},
+{150, "burgundy ", "of haste self ", 0},
+{145, "beige ",    "of see invisible ", 0}
 };
 
 struct id id_scrolls[SCROLS] = {
-{505, "                                   ", "of protect armor ", 0},
-{200, "                                   ", "of hold monster ", 0},
-{235, "                                   ", "of enchant weapon ", 0},
-{235, "                                   ", "of enchant armor ", 0},
-{175, "                                   ", "of identify ", 0},
-{190, "                                   ", "of teleportation ", 0},
- {25, "                                   ", "of sleep ", 0},
-{610, "                                   ", "of scare monster ", 0},
-{210, "                                   ", "of remove curse ", 0},
- {80, "                                   ", "of create monster ",0},
- {25, "                                   ", "of aggravate monster ",0},
-{180, "                                   ", "of magic mapping ", 0},
- {90, "                                   ", "of confuse monster ", 0}
+{505, "", "of protect armor ", 0},
+{200, "", "of hold monster ", 0},
+{235, "", "of enchant weapon ", 0},
+{235, "", "of enchant armor ", 0},
+{175, "", "of identify ", 0},
+{190, "", "of teleportation ", 0},
+ {25, "", "of sleep ", 0},
+{610, "", "of scare monster ", 0},
+{210, "", "of remove curse ", 0},
+ {80, "", "of create monster ",0},
+ {25, "", "of aggravate monster ",0},
+{180, "", "of magic mapping ", 0},
+ {90, "", "of confuse monster ", 0}
 };
 
 struct id id_weapons[WEAPONS] = {
@@ -134,31 +134,31 @@ struct id id_armors[ARMORS] = {
 };
 
 struct id id_wands[WANDS] = {
-	 {25, "                                 ", "of teleport away ",0},
-	 {50, "                                 ", "of slow monster ", 0},
-	  {8, "                                 ", "of invisibility ",0},
-	 {55, "                                 ", "of polymorph ",0},
-	  {2, "                                 ", "of haste monster ",0},
-	 {20, "                                 ", "of magic missile ",0},
-	 {20, "                                 ", "of cancellation ",0},
-	  {0, "                                 ", "of do nothing ",0},
-	 {35, "                                 ", "of drain life ",0},
-	 {20, "                                 ", "of cold ",0},
-	 {20, "                                 ", "of fire ",0}
+	 {25, "", "of teleport away ",0},
+	 {50, "", "of slow monster ", 0},
+	  {8, "", "of invisibility ",0},
+	 {55, "", "of polymorph ",0},
+	  {2, "", "of haste monster ",0},
+	 {20, "", "of magic missile ",0},
+	 {20, "", "of cancellation ",0},
+	  {0, "", "of do nothing ",0},
+	 {35, "", "of drain life ",0},
+	 {20, "", "of cold ",0},
+	 {20, "", "of fire ",0}
 };
 
 struct id id_rings[RINGS] = {
-	 {250, "                                 ", "of stealth ",0},
-	 {100, "                                 ", "of teleportation ", 0},
-	 {255, "                                 ", "of regeneration ",0},
-	 {295, "                                 ", "of slow digestion ",0},
-	 {200, "                                 ", "of add strength ",0},
-	 {250, "                                 ", "of sustain strength ",0},
-	 {250, "                                 ", "of dexterity ",0},
-	  {25, "                                 ", "of adornment ",0},
-	 {300, "                                 ", "of see invisible ",0},
-	 {290, "                                 ", "of maintain armor ",0},
-	 {270, "                                 ", "of searching ",0},
+	 {250, "", "of stealth ",0},
+	 {100, "", "of teleportation ", 0},
+	 {255, "", "of regeneration ",0},
+	 {295, "", "of slow digestion ",0},
+	 {200, "", "of add strength ",0},
+	 {250, "", "of sustain strength ",0},
+	 {250, "", "of dexterity ",0},
+	  {25, "", "of adornment ",0},
+	 {300, "", "of see invisible ",0},
+	 {290, "", "of maintain armor ",0},
+	 {270, "", "of searching ",0},
 };
 
 void
@@ -257,7 +257,7 @@ object_at(pack, row, col)
 			obj = obj->next_object;
 		}
 		if (!obj) {
-			message("object_at(): inconsistent", 1);
+			messagef(1, "object_at(): inconsistent");
 		}
 	}
 	return(obj);
@@ -637,7 +637,7 @@ alloc_object()
 		obj = free_list;
 		free_list = free_list->next_object;
 	} else if (!(obj = (object *) md_malloc(sizeof(object)))) {
-			message("cannot allocate object, saving game", 0);
+			messagef(0, "cannot allocate object, saving game");
 			save_into_file(error_file);
 			clean_up("alloc_object:  save failed");
 	}
@@ -739,10 +739,10 @@ c_object_for_wizard()
 
 	max = 0;
 	if (pack_count((object *) 0) >= MAX_PACK_COUNT) {
-		message("pack full", 0);
+		messagef(0, "pack full");
 		return;
 	}
-	message("type of object?", 0);
+	messagef(0, "type of object?");
 
 	while (r_index("!?:)]=/,\033", (ch = rgetchar()), 0) == -1) {
 		sound_bell();
@@ -788,7 +788,7 @@ c_object_for_wizard()
 	}
 	if ((ch != ',') && (ch != ':')) {
 GIL:
-		if (get_input_line("which kind?", "", buf, "", 0, 1)) {
+		if (get_input_line("which kind?", "", buf, sizeof(buf), "", 0, 1)) {
 			wk = get_number(buf);
 			if ((wk >= 0) && (wk <= max)) {
 				obj->which_kind = (unsigned short) wk;
@@ -804,7 +804,7 @@ GIL:
 			return;
 		}
 	}
-	get_desc(obj, buf);
-	message(buf, 0);
+	get_desc(obj, buf, sizeof(buf));
+	messagef(0, "%s", buf);
 	(void) add_to_pack(obj, &rogue.pack, 1);
 }
Index: pack.c
===================================================================
RCS file: /cvsroot/src/games/rogue/pack.c,v
retrieving revision 1.7
diff -u -p -r1.7 pack.c
--- pack.c	7 Aug 2003 09:37:39 -0000	1.7
+++ pack.c	27 Dec 2007 22:51:58 -0000
@@ -110,18 +110,18 @@ pick_up(row, col, status)
 	*status = 1;
 
 	if (levitate) {
-		message("you're floating in the air!", 0);
+		messagef(0, "you're floating in the air!");
 		return((object *) 0);
 	}
 	obj = object_at(&level_objects, row, col);
 	if (!obj) {
-		message("pick_up(): inconsistent", 1);
+		messagef(1, "pick_up(): inconsistent");
 		return(obj);
 	}
 	if (	(obj->what_is == SCROL) &&
 			(obj->which_kind == SCARE_MONSTER) &&
 			obj->picked_up) {
-		message("the scroll turns to dust as you pick it up", 0);
+		messagef(0, "the scroll turns to dust as you pick it up");
 		dungeon[row][col] &= (~OBJECT);
 		vanish(obj, 0, &level_objects);
 		*status = 0;
@@ -138,7 +138,7 @@ pick_up(row, col, status)
 		return(obj);	/* obj will be free_object()ed in caller */
 	}
 	if (pack_count(obj) >= MAX_PACK_COUNT) {
-		message("pack too full", 1);
+		messagef(1, "pack too full");
 		return((object *) 0);
 	}
 	dungeon[row][col] &= ~(OBJECT);
@@ -156,29 +156,29 @@ drop()
 	char desc[DCOLS];
 
 	if (dungeon[rogue.row][rogue.col] & (OBJECT | STAIRS | TRAP)) {
-		message("there's already something there", 0);
+		messagef(0, "there's already something there");
 		return;
 	}
 	if (!rogue.pack.next_object) {
-		message("you have nothing to drop", 0);
+		messagef(0, "you have nothing to drop");
 		return;
 	}
 	if ((ch = pack_letter("drop what?", ALL_OBJECTS)) == CANCEL) {
 		return;
 	}
 	if (!(obj = get_letter_object(ch))) {
-		message("no such item.", 0);
+		messagef(0, "no such item.");
 		return;
 	}
 	if (obj->in_use_flags & BEING_WIELDED) {
 		if (obj->is_cursed) {
-			message(curse_message, 0);
+			messagef(0, "%s", curse_message);
 			return;
 		}
 		unwield(rogue.weapon);
 	} else if (obj->in_use_flags & BEING_WORN) {
 		if (obj->is_cursed) {
-			message(curse_message, 0);
+			messagef(0, "%s", curse_message);
 			return;
 		}
 		mv_aquatars();
@@ -186,7 +186,7 @@ drop()
 		print_stats(STAT_ARMOR);
 	} else if (obj->in_use_flags & ON_EITHER_HAND) {
 		if (obj->is_cursed) {
-			message(curse_message, 0);
+			messagef(0, "%s", curse_message);
 			return;
 		}
 		un_put_on(obj);
@@ -205,9 +205,8 @@ drop()
 		take_from_pack(obj, &rogue.pack);
 	}
 	place_at(obj, rogue.row, rogue.col);
-	(void) strcpy(desc, "dropped ");
-	get_desc(obj, desc+8);
-	message(desc, 0);
+	get_desc(obj, desc, sizeof(desc));
+	messagef(0, "dropped %s", desc);
 	(void) reg_move();
 }
 
@@ -257,7 +256,9 @@ next_avail_ichar()
 	}
 	obj = rogue.pack.next_object;
 	while (obj) {
-		ichars[(obj->ichar - 'a')] = 1;
+		if (obj->ichar >= 'a' && obj->ichar <= 'z') {
+			ichars[(obj->ichar - 'a')] = 1;
+		}
 		obj = obj->next_object;
 	}
 	for (i = 0; i < 26; i++) {
@@ -283,12 +284,12 @@ pack_letter(prompt, mask)
 	unsigned short tmask = mask;
 
 	if (!mask_pack(&rogue.pack, mask)) {
-		message("nothing appropriate", 0);
+		messagef(0, "nothing appropriate");
 		return(CANCEL);
 	}
 	for (;;) {
 
-		message(prompt, 0);
+		messagef(0, "%s", prompt);
 
 		for (;;) {
 			ch = rgetchar();
@@ -320,19 +321,18 @@ take_off()
 
 	if (rogue.armor) {
 		if (rogue.armor->is_cursed) {
-			message(curse_message, 0);
+			messagef(0, "%s", curse_message);
 		} else {
 			mv_aquatars();
 			obj = rogue.armor;
 			unwear(rogue.armor);
-			(void) strcpy(desc, "was wearing ");
-			get_desc(obj, desc+12);
-			message(desc, 0);
+			get_desc(obj, desc, sizeof(desc));
+			messagef(0, "was wearing %s", desc);
 			print_stats(STAT_ARMOR);
 			(void) reg_move();
 		}
 	} else {
-		message("not wearing any", 0);
+		messagef(0, "not wearing any");
 	}
 }
 
@@ -344,7 +344,7 @@ wear()
 	char desc[DCOLS];
 
 	if (rogue.armor) {
-		message("your already wearing some", 0);
+		messagef(0, "you're already wearing some");
 		return;
 	}
 	ch = pack_letter("wear what?", ARMOR);
@@ -353,17 +353,16 @@ wear()
 		return;
 	}
 	if (!(obj = get_letter_object(ch))) {
-		message("no such item.", 0);
+		messagef(0, "no such item.");
 		return;
 	}
 	if (obj->what_is != ARMOR) {
-		message("you can't wear that", 0);
+		messagef(0, "you can't wear that");
 		return;
 	}
 	obj->identified = 1;
-	(void) strcpy(desc, "wearing ");
-	get_desc(obj, desc + 8);
-	message(desc, 0);
+	get_desc(obj, desc, sizeof(desc));
+	messagef(0, "wearing %s", desc);
 	do_wear(obj);
 	print_stats(STAT_ARMOR);
 	(void) reg_move();
@@ -396,7 +395,7 @@ wield()
 	char desc[DCOLS];
 
 	if (rogue.weapon && rogue.weapon->is_cursed) {
-		message(curse_message, 0);
+		messagef(0, "%s", curse_message);
 		return;
 	}
 	ch = pack_letter("wield what?", WEAPON);
@@ -405,22 +404,20 @@ wield()
 		return;
 	}
 	if (!(obj = get_letter_object(ch))) {
-		message("No such item.", 0);
+		messagef(0, "No such item.");
 		return;
 	}
 	if (obj->what_is & (ARMOR | RING)) {
-		sprintf(desc, "you can't wield %s",
+		messagef(0, "you can't wield %s",
 			((obj->what_is == ARMOR) ? "armor" : "rings"));
-		message(desc, 0);
 		return;
 	}
 	if (obj->in_use_flags & BEING_WIELDED) {
-		message("in use", 0);
+		messagef(0, "in use");
 	} else {
 		unwield(rogue.weapon);
-		(void) strcpy(desc, "wielding ");
-		get_desc(obj, desc + 9);
-		message(desc, 0);
+		get_desc(obj, desc, sizeof(desc));
+		messagef(0, "wielding %s", desc);
 		do_wield(obj);
 		(void) reg_move();
 	}
@@ -458,18 +455,20 @@ call_it()
 		return;
 	}
 	if (!(obj = get_letter_object(ch))) {
-		message("no such item.", 0);
+		messagef(0, "no such item.");
 		return;
 	}
 	if (!(obj->what_is & (SCROL | POTION | WAND | RING))) {
-		message("surely you already know what that's called", 0);
+		messagef(0, "surely you already know what that's called");
 		return;
 	}
 	id_table = get_id_table(obj);
 
-	if (get_input_line("call it:","",buf,id_table[obj->which_kind].title,1,1)) {
+	if (get_input_line("call it:", "", buf, sizeof(buf),
+			id_table[obj->which_kind].title, 1, 1)) {
 		id_table[obj->which_kind].id_status = CALLED;
-		(void) strcpy(id_table[obj->which_kind].title, buf);
+		(void) strlcpy(id_table[obj->which_kind].title, buf,
+				sizeof(id_table[obj->which_kind].title));
 	}
 }
 
@@ -565,23 +564,18 @@ kick_into_pack()
 {
 	object *obj;
 	char desc[DCOLS];
-	short n, stat;
+	short stat;
 
 	if (!(dungeon[rogue.row][rogue.col] & OBJECT)) {
-		message("nothing here", 0);
+		messagef(0, "nothing here");
 	} else {
 		if ((obj = pick_up(rogue.row, rogue.col, &stat)) != NULL) {
-			get_desc(obj, desc);
+			get_desc(obj, desc, sizeof(desc));
 			if (obj->what_is == GOLD) {
-				message(desc, 0);
+				messagef(0, "%s", desc);
 				free_object(obj);
 			} else {
-				n = strlen(desc);
-				desc[n] = '(';
-				desc[n+1] = obj->ichar;
-				desc[n+2] = ')';
-				desc[n+3] = 0;
-				message(desc, 0);
+				messagef(0, "%s(%c)", desc, obj->ichar);
 			}
 		}
 		if (obj || (!stat)) {
Index: play.c
===================================================================
RCS file: /cvsroot/src/games/rogue/play.c,v
retrieving revision 1.6
diff -u -p -r1.6 play.c
--- play.c	7 Aug 2003 09:37:39 -0000	1.6
+++ play.c	27 Dec 2007 22:51:58 -0000
@@ -67,7 +67,7 @@ play_level()
 	for (;;) {
 		interrupted = 0;
 		if (hit_message[0]) {
-			message(hit_message, 1);
+			messagef(1, "%s", hit_message);
 			hit_message[0] = 0;
 		}
 		if (trap_door) {
@@ -214,7 +214,7 @@ CH:
 			throw();
 			break;
 		case 'v':
-			message("rogue-clone: Version III. (Tim Stoehr was here), tektronix!zeus!tims", 0);
+			messagef(0, "rogue-clone: Version III. (Tim Stoehr was here), tektronix!zeus!tims");
 			break;
 		case 'Q':
 			quit(0);
@@ -246,28 +246,28 @@ CH:
 			if (wizard) {
 				inventory(&level_objects, ALL_OBJECTS);
 			} else {
-				message(unknown_command, 0);
+				messagef(0, "%s", unknown_command);
 			}
 			break;
 		case '\023':
 			if (wizard) {
 				draw_magic_map();
 			} else {
-				message(unknown_command, 0);
+				messagef(0, "%s", unknown_command);
 			}
 			break;
 		case '\024':
 			if (wizard) {
 				show_traps();
 			} else {
-				message(unknown_command, 0);
+				messagef(0, "%s", unknown_command);
 			}
 			break;
 		case '\017':
 			if (wizard) {
 				show_objects();
 			} else {
-				message(unknown_command, 0);
+				messagef(0, "%s", unknown_command);
 			}
 			break;
 		case '\001':
@@ -277,21 +277,21 @@ CH:
 			if (wizard) {
 				c_object_for_wizard();
 			} else {
-				message(unknown_command, 0);
+				messagef(0, "%s", unknown_command);
 			}
 			break;
 		case '\015':
 			if (wizard) {
 				show_monsters();
 			} else {
-				message(unknown_command, 0);
+				messagef(0, "%s", unknown_command);
 			}
 			break;
 		case 'S':
 			save_game();
 			break;
 		default:
-			message(unknown_command, 0);
+			messagef(0, "%s", unknown_command);
 			break;
 		}
 	}
Index: ring.c
===================================================================
RCS file: /cvsroot/src/games/rogue/ring.c,v
retrieving revision 1.6
diff -u -p -r1.6 ring.c
--- ring.c	7 Aug 2003 09:37:39 -0000	1.6
+++ ring.c	27 Dec 2007 22:51:58 -0000
@@ -77,28 +77,28 @@ put_on_ring()
 	object *ring;
 
 	if (r_rings == 2) {
-		message("wearing two rings already", 0);
+		messagef(0, "wearing two rings already");
 		return;
 	}
 	if ((ch = pack_letter("put on what?", RING)) == CANCEL) {
 		return;
 	}
 	if (!(ring = get_letter_object(ch))) {
-		message("no such item.", 0);
+		messagef(0, "no such item.");
 		return;
 	}
 	if (!(ring->what_is & RING)) {
-		message("that's not a ring", 0);
+		messagef(0, "that's not a ring");
 		return;
 	}
 	if (ring->in_use_flags & (ON_LEFT_HAND | ON_RIGHT_HAND)) {
-		message("that ring is already being worn", 0);
+		messagef(0, "that ring is already being worn");
 		return;
 	}
 	if (r_rings == 1) {
 		ch = (rogue.left_ring ? 'r' : 'l');
 	} else {
-		message(left_or_right, 0);
+		messagef(0, "%s", left_or_right);
 		do {
 			ch = rgetchar();
 		} while ((ch != CANCEL) && (ch != 'l') && (ch != 'r') && (ch != '\n') &&
@@ -110,7 +110,7 @@ put_on_ring()
 	}
 	if (((ch == 'l') && rogue.left_ring)||((ch == 'r') && rogue.right_ring)) {
 		check_message();
-		message("there's already a ring on that hand", 0);
+		messagef(0, "there's already a ring on that hand");
 		return;
 	}
 	if (ch == 'l') {
@@ -120,8 +120,8 @@ put_on_ring()
 	}
 	ring_stats(1);
 	check_message();
-	get_desc(ring, desc);
-	message(desc, 0);
+	get_desc(ring, desc, sizeof(desc));
+	messagef(0, "%s", desc);
 	(void) reg_move();
 }
 
@@ -160,7 +160,7 @@ remove_ring()
 	} else if (!rogue.left_ring && rogue.right_ring) {
 		right = 1;
 	} else {
-		message(left_or_right, 0);
+		messagef(0, "%s", left_or_right);
 		do {
 			ch = rgetchar();
 		} while ((ch != CANCEL) && (ch != 'l') && (ch != 'r') &&
@@ -174,22 +174,21 @@ remove_ring()
 			if (rogue.left_ring) {
 				ring = rogue.left_ring;
 			} else {
-				message(no_ring, 0);
+				messagef(0, "%s", no_ring);
 			}
 		} else {
 			if (rogue.right_ring) {
 				ring = rogue.right_ring;
 			} else {
-				message(no_ring, 0);
+				messagef(0, "%s", no_ring);
 			}
 		}
 		if (ring->is_cursed) {
-			message(curse_message, 0);
+			messagef(0, "%s", curse_message);
 		} else {
 			un_put_on(ring);
-			(void) strcpy(buf, "removed ");
-			get_desc(ring, buf + 8);
-			message(buf, 0);
+			get_desc(ring, buf, sizeof(buf));
+			messagef(0, "removed %s", buf);
 			(void) reg_move();
 		}
 	}
@@ -257,23 +256,22 @@ inv_rings()
 	char buf[DCOLS];
 
 	if (r_rings == 0) {
-		message("not wearing any rings", 0);
+		messagef(0, "not wearing any rings");
 	} else {
 		if (rogue.left_ring) {
-			get_desc(rogue.left_ring, buf);
-			message(buf, 0);
+			get_desc(rogue.left_ring, buf, sizeof(buf));
+			messagef(0, "%s", buf);
 		}
 		if (rogue.right_ring) {
-			get_desc(rogue.right_ring, buf);
-			message(buf, 0);
+			get_desc(rogue.right_ring, buf, sizeof(buf));
+			messagef(0, "%s", buf);
 		}
 	}
 	if (wizard) {
-		sprintf(buf, "ste %d, r_r %d, e_r %d, r_t %d, s_s %d, a_s %d, reg %d, r_e %d, s_i %d, m_a %d, aus %d",
+		messagef(0, "ste %d, r_r %d, e_r %d, r_t %d, s_s %d, a_s %d, reg %d, r_e %d, s_i %d, m_a %d, aus %d",
 			stealthy, r_rings, e_rings, r_teleport, sustain_strength,
 			add_strength, regeneration, ring_exp, r_see_invisible,
 			maintain_armor, auto_search);
-		message(buf, 0);
 	}
 }
 
Index: rogue.h
===================================================================
RCS file: /cvsroot/src/games/rogue/rogue.h,v
retrieving revision 1.17
diff -u -p -r1.17 rogue.h
--- rogue.h	15 Feb 2005 12:54:50 -0000	1.17
+++ rogue.h	27 Dec 2007 22:51:59 -0000
@@ -515,14 +515,14 @@ void	freeze(object *);
 int	get_armor_class(const object *);
 int	get_com_id(int *, short);
 int	get_damage(const char *, boolean);
-void	get_desc(const object *, char *);
+void	get_desc(const object *, char *, size_t);
 int	get_dir(short, short, short, short);
 void	get_dir_rc(short, short *, short *, short);
 char	get_dungeon_char(short, short);
 int	get_exp_level(long);
 void	get_food(object *, boolean);
 int	get_hit_chance(const object *);
-int	get_input_line(const char *, const char *, char *, const char *, boolean, boolean);
+int	get_input_line(const char *, const char *, char *, size_t, const char *, boolean, boolean);
 char	get_mask_char(unsigned short);
 int	get_number(const char *);
 boolean	get_oth_room(short, short *, short *);
@@ -597,7 +597,9 @@ void	md_lock(boolean);
 void	md_shell(const char *);
 void	md_sleep(int);
 void	md_slurp(void);
-void	message(const char *, boolean);
+/*void	message(const char *, boolean);*/
+void	messagef(boolean, const char *, ...)
+		__attribute__((__format__(__printf__, 2, 3)));
 void	mix_colors(void);
 void	mix_random_rooms(void);
 int	mon_can_go(const object *, int, int);
@@ -759,7 +761,8 @@ extern	boolean	see_invisible;
 extern	boolean	sustain_strength;
 extern	boolean	trap_door;
 extern	boolean	wizard;
-extern	char	hit_message[];
+#define HIT_MESSAGE_SIZE 80
+extern	char	hit_message[HIT_MESSAGE_SIZE];
 #define HUNGER_STR_LEN 8
 extern	char	hunger_str[HUNGER_STR_LEN];
 extern	char	login_name[MAX_OPT_LEN];
Index: room.c
===================================================================
RCS file: /cvsroot/src/games/rogue/room.c,v
retrieving revision 1.9
diff -u -p -r1.9 room.c
--- room.c	2 Apr 2006 00:13:29 -0000	1.9
+++ room.c	27 Dec 2007 22:51:59 -0000
@@ -60,7 +60,7 @@ boolean rooms_visited[MAXROOMS];
 
 #define NOPTS 7
 
-struct option {
+const struct option {
 	const char *prompt;
 	boolean is_bool;
 	char **strval;
@@ -84,15 +84,15 @@ struct option {
 	},
 	{
 		"Name (\"name\"): ",
-		0, &nick_name
+		0, &nick_name, (boolean *) 0
 	},
 	{
 		"Fruit (\"fruit\"): ",
-		0, &fruit
+		0, &fruit, (boolean *) 0
 	},
 	{
 		"Save file (\"file\"): ",
-		0, &save_file
+		0, &save_file, (boolean *) 0
 	}
 };
 
@@ -602,6 +602,11 @@ CH:
 					ch = rgetchar();
 				} while ((ch != '\012') && (ch != '\015') && (ch != '\033'));
 				if (j != 0) {
+					/*
+					 * We rely on the option string being
+					 * allocated to hold MAX_OPT_LEN+2 
+					 * bytes. This is arranged in init.c.
+					 */
 					(void) strcpy(*(options[i].strval), buf);
 				}
 				opt_show(i);
@@ -626,7 +631,7 @@ opt_show(i)
 	int i;
 {
 	const char *s;
-	struct option *opt = &options[i];
+	const struct option *opt = &options[i];
 
 	opt_erase(i);
 
@@ -642,7 +647,7 @@ void
 opt_erase(i)
 	int i;
 {
-	struct option *opt = &options[i];
+	const struct option *opt = &options[i];
 
 	mvaddstr(i, 0, opt->prompt);
 	clrtoeol();
Index: save.c
===================================================================
RCS file: /cvsroot/src/games/rogue/save.c,v
retrieving revision 1.10
diff -u -p -r1.10 save.c
--- save.c	17 Mar 2006 23:04:01 -0000	1.10
+++ save.c	27 Dec 2007 22:52:00 -0000
@@ -64,12 +64,12 @@ save_game()
 {
 	char fname[64];
 
-	if (!get_input_line("file name?", save_file, fname, "game not saved",
-			0, 1)) {
+	if (!get_input_line("file name?", save_file, fname, sizeof(fname),
+			"game not saved", 0, 1)) {
 		return;
 	}
 	check_message();
-	message(fname, 0);
+	messagef(0, "%s", fname);
 	save_into_file(fname);
 }
 
@@ -89,20 +89,25 @@ save_into_file(sfile)
 			len = strlen(hptr) + strlen(sfile);
 			name_buffer = md_malloc(len);
 			if (name_buffer == NULL) {
-				message("out of memory for save file name", 0);
+				messagef(0,
+					"out of memory for save file name");
 				sfile = error_file;
 			} else {
 				(void) strcpy(name_buffer, hptr);
 				(void) strcat(name_buffer, sfile+1);
 				sfile = name_buffer;
 			}
+			/* 
+			 * Note: name_buffer gets leaked. But it's small,
+			 * and in the common case we're about to exit.
+			 */
 		}
 	}
 	if (((fp = fopen(sfile, "w")) == NULL) ||
 	    ((file_id = md_get_file_id(sfile)) == -1)) {
 		if (fp)
 			fclose(fp);
-		message("problem accessing the save file", 0);
+		messagef(0, "problem accessing the save file");
 		return;
 	}
 	md_ignore_signals();
@@ -160,7 +165,7 @@ restore(fname)
 	FILE *fp;
 	struct rogue_time saved_time, mod_time;
 	char buf[4];
-	char tbuf[40];
+	char tbuf[MAX_OPT_LEN];
 	int new_file_id, saved_file_id;
 
 	fp = NULL;
@@ -351,10 +356,13 @@ read_string(s, fp, len)
 	short n;
 
 	r_read(fp, (char *) &n, sizeof(short));
-	if (n > len)
+	if (n<=0 || (size_t)(unsigned short)n > len) {
 		clean_up("read_string: corrupt game file");
+	}
 	r_read(fp, s, n);
 	xxxx(s, n);
+	/* ensure null termination */
+	s[n-1] = 0;
 }
 
 void
@@ -389,7 +397,7 @@ r_write(fp, buf, n)
 {
 	if (!write_failed) {
 		if (fwrite(buf, sizeof(char), n, fp) != (size_t)n) {
-			message("write() failed, don't know why", 0);
+			messagef(0, "write() failed, don't know why");
 			sound_bell();
 			write_failed = 1;
 		}
Index: score.c
===================================================================
RCS file: /cvsroot/src/games/rogue/score.c,v
retrieving revision 1.11
diff -u -p -r1.11 score.c
--- score.c	7 Aug 2003 09:37:40 -0000	1.11
+++ score.c	27 Dec 2007 22:52:00 -0000
@@ -62,7 +62,10 @@ killed_by(monster, other)
 	const object *monster;
 	short other;
 {
-	char buf[128];
+	const char *mechanism = "killed by something unknown (?)";
+	char mechanism_buf[128];
+	const char *article;
+	char message_buf[128];
 
 	md_ignore_signals();
 
@@ -73,32 +76,35 @@ killed_by(monster, other)
 	if (other) {
 		switch(other) {
 		case HYPOTHERMIA:
-			(void) strcpy(buf, "died of hypothermia");
+			mechanism = "died of hypothermia";
 			break;
 		case STARVATION:
-			(void) strcpy(buf, "died of starvation");
+			mechanism = "died of starvation";
 			break;
 		case POISON_DART:
-			(void) strcpy(buf, "killed by a dart");
+			mechanism = "killed by a dart";
 			break;
 		case QUIT:
-			(void) strcpy(buf, "quit");
+			mechanism = "quit";
 			break;
 		case KFIRE:
-			(void) strcpy(buf, "killed by fire");
+			mechanism = "killed by fire";
 			break;
 		}
 	} else {
-		(void) strcpy(buf, "Killed by ");
 		if (is_vowel(m_names[monster->m_char - 'A'][0])) {
-			(void) strcat(buf, "an ");
+			article = "an";
 		} else {
-			(void) strcat(buf, "a ");
+			article = "a";
 		}
-		(void) strcat(buf, m_names[monster->m_char - 'A']);
+		snprintf(mechanism_buf, sizeof(mechanism_buf),
+			"Killed by %s %s",
+			 article, m_names[monster->m_char - 'A']);
+		mechanism = mechanism_buf;
 	}
-	(void) strcat(buf, " with ");
-	sprintf(buf+strlen(buf), "%ld gold", rogue.gold);
+	snprintf(message_buf, sizeof(message_buf),
+		 "%s with %ld gold", mechanism, rogue.gold);
+
 	if ((!other) && (!no_skull)) {
 		clear();
 		mvaddstr(4, 32, "__---------__");
@@ -118,11 +124,11 @@ killed_by(monster, other)
 		mvaddstr(18, 31, "\\_           _/");
 		mvaddstr(19, 33, "~---------~");
 		center(21, nick_name);
-		center(22, buf);
+		center(22, message_buf);
 	} else {
-		message(buf, 0);
+		messagef(0, "%s", message_buf);
 	}
-	message("", 0);
+	messagef(0, "%s", "");		/* gcc objects to just "" */
 	put_scores(monster, other);
 }
 
@@ -143,8 +149,8 @@ win()
 	mvaddstr(17, 11, "Congratulations,  you have  been admitted  to  the");
 	mvaddstr(18, 11, "Fighters' Guild.   You return home,  sell all your");
 	mvaddstr(19, 11, "treasures at great profit and retire into comfort.");
-	message("", 0);
-	message("", 0);
+	messagef(0, "%s", "");		/* gcc objects to just "" */
+	messagef(0, "%s", "");		/* gcc objects to just "" */
 	id_all();
 	sell_pack();
 	put_scores((object *) 0, WIN);
@@ -154,7 +160,7 @@ void
 quit(from_intrpt)
 	boolean from_intrpt;
 {
-	char buf[128];
+	char buf[DCOLS];
 	short i, orow, ocol;
 	boolean mc;
 
@@ -173,7 +179,7 @@ quit(from_intrpt)
 		}
 	}
 	check_message();
-	message("really quit?", 1);
+	messagef(1, "really quit?");
 	if (rgetchar() != 'y') {
 		md_heed_signals();
 		check_message();
@@ -194,17 +200,157 @@ quit(from_intrpt)
 	killed_by((object *) 0, QUIT);
 }
 
+/*
+ * The score file on disk is up to ten entries of the form
+ *      score block [80 bytes]
+ *      nickname block [30 bytes]
+ *
+ * The score block is to be parsed as follows:
+ *      bytes 0-1	Rank (" 1" to "10")
+ *      bytes 2-4	space padding
+ *      bytes 5-15	Score/gold
+ *      byte 15 up to a ':'	Login name
+ *      past the ':'    Death mechanism
+ *
+ * The nickname block is an alternate name to be printed in place of the
+ * login name. Both blocks are supposed to contain a null-terminator.
+ */
+
+struct score_entry {
+	long gold;
+	char username[80];
+	char death[80];
+	char nickname[30];
+};
+
+#define NUM_SCORE_ENTRIES 10
+
+static void pad_spaces __P((char *, size_t));
+static void unpad_spaces(char *);
+static int read_score_entry __P((struct score_entry *, FILE *));
+static void write_score_entry __P((const struct score_entry *, int, FILE *));
+static void make_score __P((struct score_entry *, const object *, int));
+
+
+static
+void
+pad_spaces(str, len)
+	char *str;
+	size_t len;
+{
+	size_t x;
+	for (x=strlen(str); x<len-1; x++) {
+		str[x] = ' ';
+	}
+	str[len-1] = 0;
+}
+
+static
+void
+unpad_spaces(str)
+	char *str;
+{
+	size_t x;
+	for (x=strlen(str); x>0 && str[x-1]==' '; x--);
+	str[x] = 0;
+}
+
+static
+int
+read_score_entry(se, fp)
+	struct score_entry *se;
+	FILE *fp;
+{
+	char score_block[80];
+	char nickname_block[30];
+	size_t n, x;
+
+	n = fread(score_block, 1, sizeof(score_block), fp);
+	if (n==0) {
+		/* EOF */
+		return 0;
+	}
+	if (n != sizeof(score_block)) {
+		sf_error();
+	}
+
+	n = fread(nickname_block, 1, sizeof(nickname_block), fp);
+	if (n != sizeof(nickname_block)) {
+		sf_error();
+	}
+
+	xxxx(score_block, sizeof(score_block));
+	xxxx(nickname_block, sizeof(nickname_block));
+
+	/* Ensure null termination */
+	score_block[sizeof(score_block)-1] = 0;
+	nickname_block[sizeof(nickname_block)-1] = 0;
+
+	/* If there are other nulls in the score block, file is corrupt */
+	if (strlen(score_block)!=sizeof(score_block)-1) {
+		sf_error();
+	}
+	/* but this is NOT true of the nickname block */
+
+	/* quash trailing spaces */
+	unpad_spaces(score_block);
+	unpad_spaces(nickname_block);
+
+	for (x=5; score_block[x] == ' '; x++);
+	se->gold = lget_number(score_block+x);
+
+	for (x=15; score_block[x] != 0 && score_block[x] != ':'; x++);
+	if (score_block[x] == 0) {
+		sf_error();
+	}
+	score_block[x++] = 0;
+	strlcpy(se->username, score_block+15, sizeof(se->username));
+
+	strlcpy(se->death, score_block+x, sizeof(se->death));
+	strlcpy(se->nickname, nickname_block, sizeof(se->nickname));
+
+	return 1;
+}
+
+static
+void
+write_score_entry(se, rank, fp)
+	const struct score_entry *se;
+	int rank;
+	FILE *fp;
+{
+	char score_block[80];
+	char nickname_block[30];
+
+	/* avoid writing crap to score file */
+	memset(score_block, 0, sizeof(score_block));
+	memset(nickname_block, 0, sizeof(nickname_block));
+
+	snprintf(score_block, sizeof(score_block),
+		 "%2d    %6ld   %s: %s",
+		 rank+1, se->gold, se->username, se->death);
+	strlcpy(nickname_block, se->nickname, sizeof(nickname_block));
+
+	/* pad blocks out with spaces */
+	pad_spaces(score_block, sizeof(score_block));
+	/*pad_spaces(nickname_block, sizeof(nickname_block)); -- wrong! */
+
+	xxxx(score_block, sizeof(score_block));
+	xxxx(nickname_block, sizeof(nickname_block));
+
+	fwrite(score_block, 1, sizeof(score_block), fp);
+	fwrite(nickname_block, 1, sizeof(nickname_block), fp);
+}
+
 void
 put_scores(monster, other)
 	const object *monster;
 	short other;
 {
-	short i, n, rank = 10, x, ne = 0, found_player = -1;
-	char scores[10][82];
-	char n_names[10][30];
-	char buf[128];
+	short i, rank=-1, found_player = -1, numscores = 0;
+	struct score_entry scores[NUM_SCORE_ENTRIES];
+	const char *name;
 	FILE *fp;
-	long s;
 	boolean dopause = score_only;
 
 	md_lock(1);
@@ -213,180 +359,172 @@ put_scores(monster, other)
 	if ((fp = fopen(_PATH_SCOREFILE, "r+")) == NULL &&
 	    (fp = fopen(_PATH_SCOREFILE, "w+")) == NULL) {
 		setegid(gid);
-		message("cannot read/write/create score file", 0);
+		messagef(0, "cannot read/write/create score file");
 		sf_error();
 	}
 	setegid(gid);
 	rewind(fp);
 	(void) xxx(1);
 
-	for (i = 0; i < 10; i++) {
-		if (((n = fread(scores[i], sizeof(char), 80, fp)) < 80) && (n != 0)) {
-			sf_error();
-		} else if (n != 0) {
-			xxxx(scores[i], 80);
-			if ((n = fread(n_names[i], sizeof(char), 30, fp)) < 30) {
-				sf_error();
-			}
-			xxxx(n_names[i], 30);
-		} else {
+	for (numscores = 0; numscores < NUM_SCORE_ENTRIES; numscores++) {
+		if (read_score_entry(&scores[numscores], fp) == 0) {
 			break;
 		}
-		ne++;
-		if ((!score_only) && (found_player == -1)) {
-			if (!name_cmp(scores[i]+15, login_name)) {
-				x = 5;
-				while (scores[i][x] == ' ') {
-					x++;
-				}
-				s = lget_number(scores[i] + x);
-				if (rogue.gold < s) {
-					score_only = 1;
-				} else {
-					found_player = i;
-				}
+	}
+
+	/* Search the score list. */
+	for (i=0; i<numscores; i++) {
+		if (!strcmp(scores[i].username, login_name)) {
+			/* found our score */
+			if (rogue.gold < scores[i].gold) {
+				/* we didn't do as well as last time */
+				score_only = 1;
+			} else {
+				/* we did better; mark entry for removal */
+				found_player = i;
 			}
+			break;
 		}
 	}
+
+	/* Remove a superseded entry, if any. */
 	if (found_player != -1) {
-		ne--;
-		for (i = found_player; i < ne; i++) {
-			(void) strcpy(scores[i], scores[i+1]);
-			(void) strcpy(n_names[i], n_names[i+1]);
+		numscores--;
+		for (i = found_player; i < numscores; i++) {
+			scores[i] = scores[i+1];
 		}
 	}
+
+	/* If we're going to insert ourselves, do it now */
 	if (!score_only) {
-		for (i = 0; i < ne; i++) {
-			x = 5;
-			while (scores[i][x] == ' ') {
-				x++;
-			}
-			s = lget_number(scores[i] + x);
 
-			if (rogue.gold >= s) {
+		/* if we aren't better than anyone, add at end. */
+		rank = numscores;
+
+		/* Otherwise, find our slot. */
+		for (i = 0; i < numscores; i++) {
+			if (rogue.gold >= scores[i].gold) {
 				rank = i;
 				break;
 			}
 		}
-		if (ne == 0) {
-			rank = 0;
-		} else if ((ne < 10) && (rank == 10)) {
-			rank = ne;
-		}
-		if (rank < 10) {
-			insert_score(scores, n_names, nick_name, rank, ne,
-			    monster, other);
-			if (ne < 10) {
-				ne++;
+
+		if (rank < NUM_SCORE_ENTRIES) {
+			/* Open up a slot */
+			for (i = numscores; i > rank; i--) {
+				scores[i] = scores[i-1];
 			}
+			numscores++;
+
+			/* Put our info in the slot */
+			make_score(&scores[rank], monster, other);
 		}
+
+		/* Now rewrite the score file */
+
+		md_ignore_signals();
 		rewind(fp);
+		(void) xxx(1);
+
+		for (i = 0; i < numscores; i++) {
+			write_score_entry(&scores[i], i, fp);
+		}
 	}
+	md_lock(0);
+	fclose(fp);
+
+	/* Display the scores */
 
 	clear();
 	mvaddstr(3, 30, "Top  Ten  Rogueists");
 	mvaddstr(8, 0, "Rank   Score   Name");
 
-	md_ignore_signals();
-
-	(void) xxx(1);
-
-	for (i = 0; i < ne; i++) {
+	for (i = 0; i < numscores; i++) {
 		if (i == rank) {
 			standout();
 		}
-		if (i == 9) {
-			scores[i][0] = '1';
-			scores[i][1] = '0';
+
+		if (scores[i].nickname[0]) {
+			name = scores[i].nickname;
 		} else {
-			scores[i][0] = ' ';
-			scores[i][1] = i + '1';
-		}
-		nickize(buf, scores[i], n_names[i]);
-		mvaddstr(i+10, 0, buf);
-		if (rank < 10) {
-			xxxx(scores[i], 80);
-			fwrite(scores[i], sizeof(char), 80, fp);
-			xxxx(n_names[i], 30);
-			fwrite(n_names[i], sizeof(char), 30, fp);
+			name = scores[i].username;
 		}
+
+		mvprintw(i+10, 0, "%2d    %6ld   %s: %s",
+			 i+1, scores[i].gold, name, scores[i].death);
+
 		if (i == rank) {
 			standend();
 		}
 	}
-	md_lock(0);
 	refresh();
-	fclose(fp);
-	message("", 0);
+	messagef(0, "%s", "");		/* gcc objects to just "" */
 	if (dopause) {
-		message("", 0);
+		messagef(0, "%s", "");
 	}
 	clean_up("");
 }
 
+static
 void
-insert_score(scores, n_names, n_name, rank, n, monster, other)
-	char scores[][82];
-	char n_names[][30];
-	const char *n_name;
-	short rank, n;
+make_score(se, monster, other)
+	struct score_entry *se;
 	const object *monster;
 	int other;
 {
-	short i;
-	char buf[128];
+	const char *death = "bolts from the blue (?)";
+	const char *hasamulet;
+	char deathbuf[80];
 
-	if (n > 0) {
-		for (i = n; i > rank; i--) {
-			if ((i < 10) && (i > 0)) {
-				(void) strcpy(scores[i], scores[i-1]);
-				(void) strcpy(n_names[i], n_names[i-1]);
-			}
-		}
-	}
-	sprintf(buf, "%2d    %6ld   %s: ", rank+1, (long)rogue.gold,
-	    login_name);
+	se->gold = rogue.gold;
+	strlcpy(se->username, login_name, sizeof(se->username));
 
 	if (other) {
 		switch(other) {
 		case HYPOTHERMIA:
-			(void) strcat(buf, "died of hypothermia");
+			death = "died of hypothermia";
 			break;
 		case STARVATION:
-			(void) strcat(buf, "died of starvation");
+			death = "died of starvation";
 			break;
 		case POISON_DART:
-			(void) strcat(buf, "killed by a dart");
+			death = "killed by a dart";
 			break;
 		case QUIT:
-			(void) strcat(buf, "quit");
+			death = "quit";
 			break;
 		case WIN:
-			(void) strcat(buf, "a total winner");
+			death = "a total winner";
 			break;
 		case KFIRE:
-			(void) strcat(buf, "killed by fire");
+			death = "killed by fire";
 			break;
 		}
 	} else {
-		(void) strcat(buf, "killed by ");
-		if (is_vowel(m_names[monster->m_char - 'A'][0])) {
-			(void) strcat(buf, "an ");
+		const char *mn, *article;
+
+		mn = m_names[monster->m_char - 'A'];
+		if (is_vowel(mn[0])) {
+			article = "an";
 		} else {
-			(void) strcat(buf, "a ");
+			article = "a";
 		}
-		(void) strcat(buf, m_names[monster->m_char - 'A']);
-	}
-	sprintf(buf+strlen(buf), " on level %d ",  max_level);
-	if ((other != WIN) && has_amulet()) {
-		(void) strcat(buf, "with amulet");
+		
+		snprintf(deathbuf, sizeof(deathbuf),
+			 "killed by %s %s", article, mn);
+		death = deathbuf;
 	}
-	for (i = strlen(buf); i < 79; i++) {
-		buf[i] = ' ';
+
+	if (other != WIN && has_amulet()) {
+		hasamulet = " with amulet";
+	} else {
+		hasamulet = "";
 	}
-	buf[79] = 0;
-	(void) strcpy(scores[rank], buf);
-	(void) strcpy(n_names[rank], n_name);
+
+	snprintf(se->death, sizeof(se->death), "%s on level %d%s",
+		 death, max_level, hasamulet);
+
+	strlcpy(se->nickname, nick_name, sizeof(se->nickname));
 }
 
 boolean
@@ -419,9 +557,8 @@ sell_pack()
 			rogue.gold += val;
 
 			if (row < DROWS) {
-				sprintf(buf, "%5d      ", val);
-				get_desc(obj, buf+11);
-				mvaddstr(row++, 0, buf);
+				get_desc(obj, buf, sizeof(buf));
+				mvprintw(row++, 0, "%5d      %s", val, buf);
 			}
 		}
 		obj = obj->next_object;
@@ -430,7 +567,7 @@ sell_pack()
 	if (rogue.gold > MAX_GOLD) {
 		rogue.gold = MAX_GOLD;
 	}
-	message("", 0);
+	messagef(0, "%s", "");		/* gcc objects to just "" */
 }
 
 int
@@ -504,23 +641,6 @@ id_all()
 	}
 }
 
-int
-name_cmp(s1, s2)
-	char *s1;
-	const char *s2;
-{
-	short i = 0;
-	int r;
-
-	while(s1[i] != ':') {
-		i++;
-	}
-	s1[i] = 0;
-	r = strcmp(s1, s2);
-	s1[i] = ':';
-	return(r);
-}
-
 void
 xxxx(buf, n)
 	char *buf;
@@ -557,33 +677,6 @@ xxx(st)
 }
 
 void
-nickize(buf, score, n_name)
-	char *buf;
-	const char *score, *n_name;
-{
-	short i = 15, j;
-
-	if (!n_name[0]) {
-		(void) strcpy(buf, score);
-	} else {
-		(void) strncpy(buf, score, 16);
-
-		while (score[i] != ':') {
-			i++;
-		}
-
-		(void) strcpy(buf+15, n_name);
-		j = strlen(buf);
-
-		while (score[i]) {
-			buf[j++] = score[i++];
-		}
-		buf[j] = 0;
-		buf[79] = 0;
-	}
-}
-
-void
 center(row, buf)
 	short row;
 	const char *buf;
@@ -598,6 +691,6 @@ void
 sf_error()
 {
 	md_lock(0);
-	message("", 1);
+	messagef(1, "%s", "");		/* gcc objects to just "" */
 	clean_up("sorry, score file is out of order");
 }
Index: spec_hit.c
===================================================================
RCS file: /cvsroot/src/games/rogue/spec_hit.c,v
retrieving revision 1.5
diff -u -p -r1.5 spec_hit.c
--- spec_hit.c	7 Aug 2003 09:37:40 -0000	1.5
+++ spec_hit.c	27 Dec 2007 22:52:00 -0000
@@ -100,12 +100,12 @@ rust(monster)
 	}
 	if ((rogue.armor->is_protected) || maintain_armor) {
 		if (monster && (!(monster->m_flags & RUST_VANISHED))) {
-			message("the rust vanishes instantly", 0);
+			messagef(0, "the rust vanishes instantly");
 			monster->m_flags |= RUST_VANISHED;
 		}
 	} else {
 		rogue.armor->d_enchant--;
-		message("your armor weakens", 0);
+		messagef(0, "your armor weakens");
 		print_stats(STAT_ARMOR);
 	}
 }
@@ -127,7 +127,7 @@ freeze(monster)
 
 	if (freeze_percent > 10) {
 		monster->m_flags |= FREEZING_ROGUE;
-		message("you are frozen", 1);
+		messagef(1, "you are frozen");
 
 		n = get_rand(4, 8);
 		for (i = 0; i < n; i++) {
@@ -139,7 +139,7 @@ freeze(monster)
 			}
 			killed_by((object *)0, HYPOTHERMIA);
 		}
-		message(you_can_move_again, 1);
+		messagef(1, you_can_move_again);
 		monster->m_flags &= (~FREEZING_ROGUE);
 	}
 }
@@ -160,7 +160,7 @@ steal_gold(monster)
 		amount = rogue.gold;
 	}
 	rogue.gold -= amount;
-	message("your purse feels lighter", 0);
+	messagef(0, "your purse feels lighter");
 	print_stats(STAT_GOLD);
 	disappear(monster);
 }
@@ -205,13 +205,12 @@ steal_item(monster)
 			}
 		}
 	}
-	(void) strcpy(desc, "she stole ");
 	if (obj->what_is != WEAPON) {
 		t = obj->quantity;
 		obj->quantity = 1;
 	}
-	get_desc(obj, desc+10);
-	message(desc, 0);
+	get_desc(obj, desc, sizeof(desc));
+	messagef(0, "she stole %s", desc);
 
 	obj->quantity = ((obj->what_is != WEAPON) ? t : 1);
 
@@ -363,16 +362,13 @@ boolean
 check_imitator(monster)
 	object *monster;
 {
-	char msg[80];
-
 	if (monster->m_flags & IMITATES) {
 		wake_up(monster);
 		if (!blind) {
 			mvaddch(monster->row, monster->col,
 					get_dungeon_char(monster->row, monster->col));
 			check_message();
-			sprintf(msg, "wait, that's a %s!", mon_name(monster));
-			message(msg, 1);
+			messagef(1, "wait, that's a %s!", mon_name(monster));
 		}
 		return(1);
 	}
@@ -400,7 +396,6 @@ sting(monster)
 	object *monster;
 {
 	short sting_chance = 35;
-	char msg[80];
 
 	if ((rogue.str_current <= 3) || sustain_strength) {
 		return;
@@ -411,9 +406,8 @@ sting(monster)
 		sting_chance -= (6 * ((rogue.exp + ring_exp) - 8));
 	}
 	if (rand_percent(sting_chance)) {
-		sprintf(msg, "the %s's bite has weakened you",
-		mon_name(monster));
-		message(msg, 0);
+		messagef(0, "the %s's bite has weakened you",
+			mon_name(monster));
 		rogue.str_current--;
 		print_stats(STAT_STRENGTH);
 	}
@@ -450,7 +444,7 @@ drain_life()
 	n = get_rand(1, 3);		/* 1 Hp, 2 Str, 3 both */
 
 	if ((n != 2) || (!sustain_strength)) {
-		message("you feel weaker", 0);
+		messagef(0, "you feel weaker");
 	}
 	if (n != 2) {
 		rogue.hp_max--;
@@ -472,8 +466,6 @@ boolean
 m_confuse(monster)
 	object *monster;
 {
-	char msg[80];
-
 	if (!rogue_can_see(monster->row, monster->col)) {
 		return(0);
 	}
@@ -483,8 +475,8 @@ m_confuse(monster)
 	}
 	if (rand_percent(55)) {
 		monster->m_flags &= (~CONFUSES);
-		sprintf(msg, "the gaze of the %s has confused you", mon_name(monster));
-		message(msg, 1);
+		messagef(1, "the gaze of the %s has confused you", 
+			mon_name(monster));
 		cnfs();
 		return(1);
 	}
Index: throw.c
===================================================================
RCS file: /cvsroot/src/games/rogue/throw.c,v
retrieving revision 1.7
diff -u -p -r1.7 throw.c
--- throw.c	30 Mar 2006 05:04:22 -0000	1.7
+++ throw.c	27 Dec 2007 22:52:00 -0000
@@ -67,7 +67,7 @@ throw()
 	while (!is_direction(dir = rgetchar(), &d)) {
 		sound_bell();
 		if (first_miss) {
-			message("direction? ", 0);
+			messagef(0, "direction? ");
 			first_miss = 0;
 		}
 	}
@@ -81,11 +81,11 @@ throw()
 	check_message();
 
 	if (!(weapon = get_letter_object(wch))) {
-		message("no such item.", 0);
+		messagef(0, "no such item.");
 		return;
 	}
 	if ((weapon->in_use_flags & BEING_USED) && weapon->is_cursed) {
-		message(curse_message, 0);
+		messagef(0, curse_message);
 		return;
 	}
 	row = rogue.row; col = rogue.col;
@@ -142,15 +142,15 @@ throw_at_monster(monster, weapon)
 	}
 	t = weapon->quantity;
 	weapon->quantity = 1;
-	sprintf(hit_message, "the %s", name_of(weapon));
+	snprintf(hit_message, HIT_MESSAGE_SIZE, "the %s", name_of(weapon));
 	weapon->quantity = t;
 
 	if (!rand_percent(hit_chance)) {
-		(void) strcat(hit_message, "misses  ");
+		(void) strlcat(hit_message, "misses  ", HIT_MESSAGE_SIZE);
 		return(0);
 	}
 	s_con_mon(monster);
-	(void) strcat(hit_message, "hit  ");
+	(void) strlcat(hit_message, "hit  ", HIT_MESSAGE_SIZE);
 	(void) mon_damage(monster, damage);
 	return(1);
 }
@@ -207,7 +207,6 @@ flop_weapon(weapon, row, col)
 {
 	object *new_weapon, *monster;
 	short i = 0;
-	char msg[80];
 	boolean found = 0;
 	short mch, dch;
 	unsigned short mon;
@@ -257,10 +256,9 @@ flop_weapon(weapon, row, col)
 
 		t = weapon->quantity;
 		weapon->quantity = 1;
-		sprintf(msg, "the %svanishes as it hits the ground",
-		name_of(weapon));
+		messagef(0, "the %svanishes as it hits the ground",
+			name_of(weapon));
 		weapon->quantity = t;
-		message(msg, 0);
 	}
 }
 
Index: trap.c
===================================================================
RCS file: /cvsroot/src/games/rogue/trap.c,v
retrieving revision 1.6
diff -u -p -r1.6 trap.c
--- trap.c	7 Aug 2003 09:37:40 -0000	1.6
+++ trap.c	27 Dec 2007 22:52:00 -0000
@@ -99,7 +99,7 @@ trap_player(row, col)
 	}
 	dungeon[row][col] &= (~HIDDEN);
 	if (rand_percent(rogue.exp + ring_exp)) {
-		message("the trap failed", 1);
+		messagef(1, "the trap failed");
 		return;
 	}
 	switch(t) {
@@ -108,7 +108,7 @@ trap_player(row, col)
 		new_level_message = trap_strings[(t*2)+1];
 		break;
 	case BEAR_TRAP:
-		message(trap_strings[(t*2)+1], 1);
+		messagef(1, "%s", trap_strings[(t*2)+1]);
 		bear_trap = get_rand(4, 7);
 		break;
 	case TELE_TRAP:
@@ -116,7 +116,7 @@ trap_player(row, col)
 		tele();
 		break;
 	case DART_TRAP:
-		message(trap_strings[(t*2)+1], 1);
+		messagef(1, "%s", trap_strings[(t*2)+1]);
 		rogue.hp_current -= get_damage("1d6", 1);
 		if (rogue.hp_current <= 0) {
 			rogue.hp_current = 0;
@@ -131,11 +131,11 @@ trap_player(row, col)
 		}
 		break;
 	case SLEEPING_GAS_TRAP:
-		message(trap_strings[(t*2)+1], 1);
+		messagef(1, "%s", trap_strings[(t*2)+1]);
 		take_a_nap();
 		break;
 	case RUST_TRAP:
-		message(trap_strings[(t*2)+1], 1);
+		messagef(1, "%s", trap_strings[(t*2)+1]);
 		rust((object *) 0);
 		break;
 	}
@@ -191,7 +191,7 @@ id_trap()
 {
 	short dir, row, col, d, t;
 
-	message("direction? ", 0);
+	messagef(0, "direction? ");
 
 	while (!is_direction(dir = rgetchar(), &d)) {
 		sound_bell();
@@ -208,9 +208,9 @@ id_trap()
 
 	if ((dungeon[row][col] & TRAP) && (!(dungeon[row][col] & HIDDEN))) {
 		t = trap_at(row, col);
-		message(trap_strings[t*2], 0);
+		messagef(0, "%s", trap_strings[t*2]);
 	} else {
-		message("no trap there", 0);
+		messagef(0, "no trap there");
 	}
 }
 
@@ -269,7 +269,8 @@ search(n, is_auto)
 						shown++;
 						if (dungeon[row][col] & TRAP) {
 							t = trap_at(row, col);
-							message(trap_strings[t*2], 1);
+							messagef(1, "%s",
+								 trap_strings[t*2]);
 						}
 					}
 				}
Index: use.c
===================================================================
RCS file: /cvsroot/src/games/rogue/use.c,v
retrieving revision 1.6
diff -u -p -r1.6 use.c
--- use.c	7 Aug 2003 09:37:40 -0000	1.6
+++ use.c	27 Dec 2007 22:52:01 -0000
@@ -70,7 +70,6 @@ void
 quaff()
 {
 	short ch;
-	char buf[80];
 	object *obj;
 
 	ch = pack_letter("quaff what?", POTION);
@@ -79,17 +78,16 @@ quaff()
 		return;
 	}
 	if (!(obj = get_letter_object(ch))) {
-		message("no such item.", 0);
+		messagef(0, "no such item.");
 		return;
 	}
 	if (obj->what_is != POTION) {
-		message("you can't drink that", 0);
+		messagef(0, "you can't drink that");
 		return;
 	}
 	switch(obj->which_kind) {
 		case INCREASE_STRENGTH:
-			message("you feel stronger now, what bulging muscles!",
-			0);
+			messagef(0, "you feel stronger now, what bulging muscles!");
 			rogue.str_current++;
 			if (rogue.str_current > rogue.str_max) {
 				rogue.str_max = rogue.str_current;
@@ -97,14 +95,14 @@ quaff()
 			break;
 		case RESTORE_STRENGTH:
 			rogue.str_current = rogue.str_max;
-			message("this tastes great, you feel warm all over", 0);
+			messagef(0, "this tastes great, you feel warm all over");
 			break;
 		case HEALING:
-			message("you begin to feel better", 0);
+			messagef(0, "you begin to feel better");
 			potion_heal(0);
 			break;
 		case EXTRA_HEALING:
-			message("you begin to feel much better", 0);
+			messagef(0, "you begin to feel much better");
 			potion_heal(1);
 			break;
 		case POISON:
@@ -114,27 +112,27 @@ quaff()
 					rogue.str_current = 1;
 				}
 			}
-			message("you feel very sick now", 0);
+			messagef(0, "you feel very sick now");
 			if (halluc) {
 				unhallucinate();
 			}
 			break;
 		case RAISE_LEVEL:
 			rogue.exp_points = level_points[rogue.exp - 1];
-			message("you suddenly feel much more skillful", 0);
+			messagef(0, "you suddenly feel much more skillful");
 			add_exp(1, 1);
 			break;
 		case BLINDNESS:
 			go_blind();
 			break;
 		case HALLUCINATION:
-			message("oh wow, everything seems so cosmic", 0);
+			messagef(0, "oh wow, everything seems so cosmic");
 			halluc += get_rand(500, 800);
 			break;
 		case DETECT_MONSTER:
 			show_monsters();
 			if (!(level_monsters.next_monster)) {
-				message(strange_feeling, 0);
+				messagef(0, "%s", strange_feeling);
 			}
 			break;
 		case DETECT_OBJECTS:
@@ -143,29 +141,29 @@ quaff()
 					show_objects();
 				}
 			} else {
-				message(strange_feeling, 0);
+				messagef(0, "%s", strange_feeling);
 			}
 			break;
 		case CONFUSION:
-			message((halluc ? "what a trippy feeling" :
-			"you feel confused"), 0);
+			messagef(0, (halluc ? "what a trippy feeling" :
+			"you feel confused"));
 			cnfs();
 			break;
 		case LEVITATION:
-			message("you start to float in the air", 0);
+			messagef(0, "you start to float in the air");
 			levitate += get_rand(15, 30);
 			being_held = bear_trap = 0;
 			break;
 		case HASTE_SELF:
-			message("you feel yourself moving much faster", 0);
+			messagef(0, "you feel yourself moving much faster");
 			haste_self += get_rand(11, 21);
 			if (!(haste_self % 2)) {
 				haste_self++;
 			}
 			break;
 		case SEE_INVISIBLE:
-			sprintf(buf, "hmm, this potion tastes like %sjuice", fruit);
-			message(buf, 0);
+			messagef(0, "hmm, this potion tastes like %sjuice", 
+				 fruit);
 			if (blind) {
 				unblind();
 			}
@@ -185,7 +183,6 @@ read_scroll()
 {
 	short ch;
 	object *obj;
-	char msg[DCOLS];
 
 	ch = pack_letter("read what?", SCROL);
 
@@ -193,17 +190,16 @@ read_scroll()
 		return;
 	}
 	if (!(obj = get_letter_object(ch))) {
-		message("no such item.", 0);
+		messagef(0, "no such item.");
 		return;
 	}
 	if (obj->what_is != SCROL) {
-		message("you can't read that", 0);
+		messagef(0, "you can't read that");
 		return;
 	}
 	switch(obj->which_kind) {
 		case SCARE_MONSTER:
-			message("you hear a maniacal laughter in the distance",
-			0);
+			messagef(0, "you hear a maniacal laughter in the distance");
 			break;
 		case HOLD_MONSTER:
 			hold_monster();
@@ -211,11 +207,10 @@ read_scroll()
 		case ENCH_WEAPON:
 			if (rogue.weapon) {
 				if (rogue.weapon->what_is == WEAPON) {
-					sprintf(msg, "your %sglow%s %sfor a moment",
-					name_of(rogue.weapon),
-					((rogue.weapon->quantity <= 1) ? "s" : ""),
-					get_ench_color());
-					message(msg, 0);
+					messagef(0, "your %sglow%s %sfor a moment",
+						name_of(rogue.weapon),
+						((rogue.weapon->quantity <= 1) ? "s" : ""),
+						get_ench_color());
 					if (coin_toss()) {
 						rogue.weapon->hit_enchant++;
 					} else {
@@ -224,23 +219,22 @@ read_scroll()
 				}
 				rogue.weapon->is_cursed = 0;
 			} else {
-				message("your hands tingle", 0);
+				messagef(0, "your hands tingle");
 			}
 			break;
 		case ENCH_ARMOR:
 			if (rogue.armor) {
-				sprintf(msg, "your armor glows %sfor a moment",
-				get_ench_color());
-				message(msg, 0);
+				messagef(0, "your armor glows %sfor a moment",
+					get_ench_color());
 				rogue.armor->d_enchant++;
 				rogue.armor->is_cursed = 0;
 				print_stats(STAT_ARMOR);
 			} else {
-				message("your skin crawls", 0);
+				messagef(0, "your skin crawls");
 			}
 			break;
 		case IDENTIFY:
-			message("this is a scroll of identify", 0);
+			messagef(0, "this is a scroll of identify");
 			obj->identified = 1;
 			id_scrolls[obj->which_kind].id_status = IDENTIFIED;
 			idntfy();
@@ -249,22 +243,22 @@ read_scroll()
 			tele();
 			break;
 		case SLEEP:
-			message("you fall asleep", 0);
+			messagef(0, "you fall asleep");
 			take_a_nap();
 			break;
 		case PROTECT_ARMOR:
 			if (rogue.armor) {
-				message( "your armor is covered by a shimmering gold shield",0);
+				messagef(0, "your armor is covered by a shimmering gold shield");
 				rogue.armor->is_protected = 1;
 				rogue.armor->is_cursed = 0;
 			} else {
-				message("your acne seems to have disappeared", 0);
+				messagef(0, "your acne seems to have disappeared");
 			}
 			break;
 		case REMOVE_CURSE:
-				message((!halluc) ?
+				messagef(0, (!halluc) ?
 					"you feel as though someone is watching over you" :
-					"you feel in touch with the universal oneness", 0);
+					"you feel in touch with the universal oneness");
 			uncurse_all();
 			break;
 		case CREATE_MONSTER:
@@ -274,13 +268,13 @@ read_scroll()
 			aggravate();
 			break;
 		case MAGIC_MAPPING:
-			message("this scroll seems to have a map on it", 0);
+			messagef(0, "this scroll seems to have a map on it");
 			draw_magic_map();
 			break;
 		case CON_MON:
 			con_mon = 1;
-			sprintf(msg, "your hands glow %sfor a moment", get_ench_color());
-			message(msg, 0);
+			messagef(0, "your hands glow %sfor a moment", 
+				 get_ench_color());
 			break;
 	}
 	if (id_scrolls[obj->which_kind].id_status != CALLED) {
@@ -378,8 +372,8 @@ AGAIN:
 		return;
 	}
 	if (!(obj = get_letter_object(ch))) {
-		message("no such item, try again", 0);
-		message("", 0);
+		messagef(0, "no such item, try again");
+		messagef(0, "%s", "");	/* gcc objects to just "" */
 		check_message();
 		goto AGAIN;
 	}
@@ -388,8 +382,8 @@ AGAIN:
 		id_table = get_id_table(obj);
 		id_table[obj->which_kind].id_status = IDENTIFIED;
 	}
-	get_desc(obj, desc);
-	message(desc, 0);
+	get_desc(obj, desc, sizeof(desc));
+	messagef(0, "%s", desc);
 }
 
 void
@@ -398,7 +392,6 @@ eat()
 	short ch;
 	short moves;
 	object *obj;
-	char buf[70];
 
 	ch = pack_letter("eat what?", FOOD);
 
@@ -406,24 +399,23 @@ eat()
 		return;
 	}
 	if (!(obj = get_letter_object(ch))) {
-		message("no such item.", 0);
+		messagef(0, "no such item.");
 		return;
 	}
 	if (obj->what_is != FOOD) {
-		message("you can't eat that", 0);
+		messagef(0, "you can't eat that");
 		return;
 	}
 	if ((obj->which_kind == FRUIT) || rand_percent(60)) {
 		moves = get_rand(950, 1150);
 		if (obj->which_kind == RATION) {
-			message("yum, that tasted good", 0);
+			messagef(0, "yum, that tasted good");
 		} else {
-			sprintf(buf, "my, that was a yummy %s", fruit);
-			message(buf, 0);
+			messagef(0, "my, that was a yummy %s", fruit);
 		}
 	} else {
 		moves = get_rand(750, 950);
-		message("yuk, that food tasted awful", 0);
+		messagef(0, "yuk, that food tasted awful");
 		add_exp(2, 1);
 	}
 	rogue.moves_left /= 3;
@@ -459,11 +451,11 @@ hold_monster()
 		}
 	}
 	if (mcount == 0) {
-		message("you feel a strange sense of loss", 0);
+		messagef(0, "you feel a strange sense of loss");
 	} else if (mcount == 1) {
-		message("the monster freezes", 0);
+		messagef(0, "the monster freezes");
 	} else {
-		message("the monsters around you freeze", 0);
+		messagef(0, "the monsters around you freeze");
 	}
 }
 
@@ -515,14 +507,14 @@ unhallucinate()
 {
 	halluc = 0;
 	relight();
-	message("everything looks SO boring now", 1);
+	messagef(1, "everything looks SO boring now");
 }
 
 void
 unblind()
 {
 	blind = 0;
-	message("the veil of darkness lifts", 1);
+	messagef(1, "the veil of darkness lifts");
 	relight();
 	if (halluc) {
 		hallucinate();
@@ -555,7 +547,7 @@ take_a_nap()
 		mv_mons();
 	}
 	md_sleep(1);
-	message(you_can_move_again, 0);
+	messagef(0, "%s", you_can_move_again);
 }
 
 void
@@ -564,7 +556,7 @@ go_blind()
 	short i, j;
 
 	if (!blind) {
-		message("a cloak of darkness falls around you", 0);
+		messagef(0, "a cloak of darkness falls around you");
 	}
 	blind += get_rand(500, 800);
 
@@ -610,11 +602,8 @@ cnfs()
 void
 unconfuse()
 {
-	char msg[80];
-
 	confused = 0;
-	sprintf(msg, "you feel less %s now", (halluc ? "trippy" : "confused"));
-	message(msg, 1);
+	messagef(1, "you feel less %s now", (halluc ? "trippy" : "confused"));
 }
 
 void
Index: zap.c
===================================================================
RCS file: /cvsroot/src/games/rogue/zap.c,v
retrieving revision 1.6
diff -u -p -r1.6 zap.c
--- zap.c	7 Aug 2003 09:37:40 -0000	1.6
+++ zap.c	27 Dec 2007 22:52:01 -0000
@@ -69,7 +69,7 @@ zapp()
 	while (!is_direction(dir = rgetchar(), &d)) {
 		sound_bell();
 		if (first_miss) {
-			message("direction? ", 0);
+			messagef(0, "direction? ");
 			first_miss = 0;
 		}
 	}
@@ -83,15 +83,15 @@ zapp()
 	check_message();
 
 	if (!(wand = get_letter_object(wch))) {
-		message("no such item.", 0);
+		messagef(0, "no such item.");
 		return;
 	}
 	if (wand->what_is != WAND) {
-		message("you can't zap with that", 0);
+		messagef(0, "you can't zap with that");
 		return;
 	}
 	if (wand->class <= 0) {
-		message("nothing happens", 0);
+		messagef(0, "nothing happens");
 	} else {
 		wand->class--;
 		row = rogue.row; col = rogue.col;
@@ -198,7 +198,7 @@ zap_monster(monster, kind)
 			FLAMES | IMITATES | CONFUSES | SEEKS_GOLD | HOLDS));
 		break;
 	case DO_NOTHING:
-		message("nothing happens", 0);
+		messagef(0, "nothing happens");
 		break;
 	}
 }
@@ -230,17 +230,18 @@ wizardize()
 
 	if (wizard) {
 		wizard = 0;
-		message("not wizard anymore", 0);
+		messagef(0, "not wizard anymore");
 	} else {
-		if (get_input_line("wizard's password:", "", buf, "", 0, 0)) {
+		if (get_input_line("wizard's password:", "", buf, sizeof(buf),
+				"", 0, 0)) {
 			(void) xxx(1);
 			xxxx(buf, strlen(buf));
 			if (!strncmp(buf, "\247\104\126\272\115\243\027", 7)) {
 				wizard = 1;
 				score_only = 1;
-				message("Welcome, mighty wizard!", 0);
+				messagef(0, "Welcome, mighty wizard!");
 			} else {
-				message("sorry", 0);
+				messagef(0, "sorry");
 			}
 		}
 	}
@@ -281,7 +282,6 @@ bounce(ball, dir, row, col, r)
 	short ball, dir, row, col, r;
 {
 	short orow, ocol;
-	char buf[DCOLS];
 	const char *s;
 	short i, ch, new_dir = -1, damage;
 	static short btime;
@@ -298,8 +298,7 @@ bounce(ball, dir, row, col, r)
 		s = "ice";
 	}
 	if (r > 1) {
-		sprintf(buf, "the %s bounces", s);
-		message(buf, 0);
+		messagef(0, "the %s bounces", s);
 	}
 	orow = row;
 	ocol = col;
@@ -336,8 +335,8 @@ bounce(ball, dir, row, col, r)
 
 		wake_up(monster);
 		if (rand_percent(33)) {
-			sprintf(buf, "the %s misses the %s", s, mon_name(monster));
-			message(buf, 0);
+			messagef(0, "the %s misses the %s", s, 
+				mon_name(monster));
 			goto ND;
 		}
 		if (ball == FIRE) {
@@ -352,14 +351,14 @@ bounce(ball, dir, row, col, r)
 			} else {
 				damage = (monster->hp_to_kill / 2) + 1;
 			}
-			sprintf(buf, "the %s hits the %s", s, mon_name(monster));
-			message(buf, 0);
+			messagef(0, "the %s hits the %s", s,
+				mon_name(monster));
 			(void) mon_damage(monster, damage);
 		} else {
 			damage = -1;
 			if (!(monster->m_flags & FREEZES)) {
 				if (rand_percent(33)) {
-					message("the monster is frozen", 0);
+					messagef(0, "the monster is frozen");
 					monster->m_flags |= (ASLEEP | NAPPING);
 					monster->nap_length = get_rand(3, 6);
 				} else {
@@ -369,15 +368,14 @@ bounce(ball, dir, row, col, r)
 				damage = -2;
 			}
 			if (damage != -1) {
-				sprintf(buf, "the %s hits the %s", s, mon_name(monster));
-				message(buf, 0);
+				messagef(0, "the %s hits the %s", s, 
+					mon_name(monster));
 				(void) mon_damage(monster, damage);
 			}
 		}
 	} else if ((row == rogue.row) && (col == rogue.col)) {
 		if (rand_percent(10 + (3 * get_armor_class(rogue.armor)))) {
-			sprintf(buf, "the %s misses", s);
-			message(buf, 0);
+			messagef(0, "the %s misses", s);
 			goto ND;
 		} else {
 			damage = get_rand(3, (3 * rogue.exp));
@@ -385,10 +383,9 @@ bounce(ball, dir, row, col, r)
 				damage = (damage * 3) / 2;
 				damage -= get_armor_class(rogue.armor);
 			}
-			sprintf(buf, "the %s hits", s);
 			rogue_damage(damage, (object *) 0,
 					((ball == FIRE) ? KFIRE : HYPOTHERMIA));
-			message(buf, 0);
+			messagef(0, "the %s hits", s);
 		}
 	} else {
 		short nrow, ncol;


-- 
   - David A. Holland / dholland@eecs.harvard.edu