-HDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.1051 $ $NHDT-Date: 1665130022 2022/10/07 08:07:02 $
+HDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.1093 $ $NHDT-Date: 1672605786 2023/01/01 20:43:06 $
General Fixes and Modified Features
-----------------------------------
giants occasionally get a battle axe or a two-handed sword
give gremlin the property it stole, if possible
'F'orcefighting with a war hammer has a small chance of breaking iron bars
+player assigned name for monsters, specific objects, or object types could be
+ longer than what was intented to be allowed; for 'curses', much longer
Fixes to 3.7.0-x Problems that Were Exposed Via git Repository
-/* NetHack 3.7 do_name.c $NHDT-Date: 1655663780 2022/06/19 18:36:20 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.254 $ */
+/* NetHack 3.7 do_name.c $NHDT-Date: 1672605786 2023/01/01 20:43:06 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.280 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*-Copyright (c) Pasi Kallinen, 2018. */
/* NetHack may be freely redistributed. See license for details. */
static boolean gather_locs_interesting(coordxy, coordxy, int);
static void gather_locs(coord **, int *, int);
static void truncate_to_map(int *, int *, schar, schar);
+static char *name_from_player(char *, const char *, const char *);
static void do_mgivenname(void);
static boolean alreadynamed(struct monst *, char *, char *);
static void do_oname(struct obj *);
return "";
}
+/* get a name for a monster or an object from player;
+ truncate if longer than PL_PSIZ, then return it */
+static char *
+name_from_player(
+ char *outbuf, /* output buffer, assumed to be at least BUFSZ long;
+ * anything longer than PL_PSIZ will be truncated */
+ const char *prompt,
+ const char *defres) /* only used if EDIT_GETLIN is enabled; only useful
+ * if windowport xxx's xxx_getlin() supports that */
+{
+ outbuf[0] = '\0';
+#ifdef EDIT_GETLIN
+ if (defres && *defres)
+ Strcpy(outbuf, defres); /* default response from getlin() */
+#else
+ nhUse(defres);
+#endif
+ getlin(prompt, outbuf);
+ if (!*outbuf || *outbuf == '\033')
+ return NULL;
+
+ /* strip leading and trailing spaces, condense internal sequences */
+ (void) mungspaces(outbuf);
+ if (strlen(outbuf) >= PL_PSIZ)
+ outbuf[PL_PSIZ - 1] = '\0';
+ return outbuf;
+}
+
/* historical note: this returns a monster pointer because it used to
allocate a new bigger block of memory to hold the monster and its name */
struct monst *
/* special case similar to the one in lookat() */
Sprintf(qbuf, "What do you want to call %s?",
distant_monnam(mtmp, ARTICLE_THE, monnambuf));
- buf[0] = '\0';
-#ifdef EDIT_GETLIN
- /* if there's an existing name, make it be the default answer */
- if (has_mgivenname(mtmp))
- Strcpy(buf, MGIVENNAME(mtmp));
-#endif
- getlin(qbuf, buf);
- if (!*buf || *buf == '\033')
+ /* use getlin() to get a name string from the player */
+ if (!name_from_player(buf, qbuf,
+ has_mgivenname(mtmp) ? MGIVENNAME(mtmp) : NULL))
return;
- /* strip leading and trailing spaces; unnames monster if all spaces */
- (void) mungspaces(buf);
/* Unique monsters have their own specific names or titles.
* Shopkeepers, temple priests and other minions use alternate
Sprintf(qbuf, "What do you want to name %s ",
is_plural(obj) ? "these" : "this");
(void) safe_qbuf(qbuf, qbuf, "?", obj, xname, simpleonames, "item");
- buf[0] = '\0';
-#ifdef EDIT_GETLIN
- /* if there's an existing name, make it be the default answer */
- if (has_oname(obj))
- Strcpy(buf, ONAME(obj));
-#endif
- getlin(qbuf, buf);
- if (!*buf || *buf == '\033')
+ /* use getlin() to get a name string from the player */
+ if (!name_from_player(buf, qbuf, safe_oname(obj)))
return;
- /* strip leading and trailing spaces; unnames item if all spaces */
- (void) mungspaces(buf);
/*
* We don't violate illiteracy conduct here, although it is
docall_xname, simpleonames, "thing");
/* pointer to old name */
str1 = &(objects[obj->otyp].oc_uname);
- buf[0] = '\0';
-#ifdef EDIT_GETLIN
- /* if there's an existing name, make it be the default answer */
- if (*str1)
- Strcpy(buf, *str1);
-#endif
- getlin(qbuf, buf);
- if (!*buf || *buf == '\033')
+ /* use getlin() to get a name string from the player */
+ if (!name_from_player(buf, qbuf, *str1))
return;
/* clear old name */
-/* NetHack 3.7 invent.c $NHDT-Date: 1661240719 2022/08/23 07:45:19 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.424 $ */
+/* NetHack 3.7 invent.c $NHDT-Date: 1672827802 2023/01/04 10:23:22 $ $NHDT-Branch: naming-overflow-fix $:$NHDT-Revision: 1.439 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*-Copyright (c) Derek S. Ray, 2015. */
/* NetHack may be freely redistributed. See license for details. */
DISABLE_WARNING_FORMAT_NONLITERAL
char *
-xprname(struct obj *obj,
- const char *txt, /* text to print instead of obj */
- char let, /* inventory letter */
- boolean dot, /* append period; (dot && cost => Iu) */
- long cost, /* cost (for inventory of unpaid or expended items) */
- long quan) /* if non-0, print this quantity, not obj->quan */
+xprname(
+ struct obj *obj,
+ const char *txt, /* text to print instead of obj */
+ char let, /* inventory letter */
+ boolean dot, /* append period; (dot && cost => Iu) */
+ long cost, /* cost (for inventory of unpaid or expended items) */
+ long quan) /* if non-0, print this quantity, not obj->quan */
{
static char li[BUFSZ];
+ char suffix[80]; /* plenty of room for count and hallucinatory currency */
+ int sfxlen, txtlen; /* signed int for %*s formatting */
+ const char *fmt;
boolean use_invlet = (flags.invlet_constant
&& let != CONTAINED_SYM && let != HANDS_SYM);
- long savequan = 0;
+ long savequan = 0L;
if (quan && obj) {
savequan = obj->quan;
* * Then obj == null and we are printing a total amount.
* > Then the object is contained and doesn't have an inventory letter.
*/
- if (cost != 0 || let == '*') {
+ fmt = "%c - %.*s%s";
+ if (!txt)
+ txt = doname(obj);
+ txtlen = (int) strlen(txt);
+
+ if (cost != 0L || let == '*') {
/* if dot is true, we're doing Iu, otherwise Ix */
- Sprintf(li,
- iflags.menu_tab_sep ? "%c - %s\t%6ld %s"
- : "%c - %-45s %6ld %s",
- (dot && use_invlet ? obj->invlet : let),
- (txt ? txt : doname(obj)), cost, currency(cost));
+ if (dot && use_invlet)
+ let = obj->invlet;
+ Sprintf(suffix, "%c%6ld %.50s", iflags.menu_tab_sep ? '\t' : ' ',
+ cost, currency(cost));
+ if (!iflags.menu_tab_sep) {
+ fmt = "%c - %-45.*s%s";
+ if (txtlen < 45)
+ txtlen = 45;
+ }
} else {
/* ordinary inventory display or pickup message */
- Sprintf(li, "%c - %s%s", (use_invlet ? obj->invlet : let),
- (txt ? txt : doname(obj)), (dot ? "." : ""));
+ if (use_invlet)
+ let = obj->invlet;
+ Strcpy(suffix, dot ? "." : "");
}
+ sfxlen = (int) strlen(suffix);
+ if (txtlen > BUFSZ - 1 - (4 + sfxlen)) /* 4: "c - " prefix */
+ txtlen = BUFSZ - 1 - (4 + sfxlen);
+ Sprintf(li, fmt, let, txtlen, txt, suffix);
+
if (savequan)
obj->quan = savequan;
-/* NetHack 3.7 o_init.c $NHDT-Date: 1646950588 2022/03/10 22:16:28 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.56 $ */
+/* NetHack 3.7 o_init.c $NHDT-Date: 1672829455 2023/01/04 10:50:55 $ $NHDT-Branch: naming-overflow-fix $:$NHDT-Revision: 1.68 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*-Copyright (c) Robert Patrick Rankin, 2011. */
/* NetHack may be freely redistributed. See license for details. */
static int QSORTCALLBACK discovered_cmp(const genericptr, const genericptr);
static char *sortloot_descr(int, char *);
static char *disco_typename(int);
+static void disco_append_typename(char *, int);
static char *oclass_to_name(char, char *);
#ifdef TILES_IN_GLYPHMAP
return result;
}
+/* append typename(dis) to buf[], possibly truncating in the process */
+static void
+disco_append_typename(char *buf, int dis)
+{
+ unsigned len = (unsigned) strlen(buf);
+ char *p, *typnm = disco_typename(dis);
+
+ if (len + (unsigned) strlen(typnm) < BUFSZ) {
+ /* ordinary */
+ Strcat(buf, typnm);
+ } else if ((p = strrchr(typnm, '(')) != 0
+ && p > typnm && p[-1] == ' ' && strchr(p, ')') != 0) {
+ /* typename() returned "really long user-applied name (actual type)"
+ and we want to truncate from "really long user-applied name" while
+ keeping " (actual type)" intact */
+ --p; /* back up to space in front of open paren */
+ (void) strncat(buf, typnm, BUFSZ - 1 - (len + (unsigned) strlen(p)));
+ Strcat(buf, p);
+ } else {
+ /* unexpected; just truncate from end of typename */
+ (void) strncat(buf, typnm, BUFSZ - 1 - len);
+ }
+}
+
/* the #known command - show discovered object types */
int
dodiscovered(void) /* free after Robert Viduya */
Strcpy(buf, objects[dis].oc_pre_discovered ? "* " : " ");
if (lootsort)
(void) sortloot_descr(dis, &buf[2]);
- Strcat(buf, disco_typename(dis));
+ disco_append_typename(buf, dis);
if (!alphabetized && !lootsort)
putstr(tmpwin, 0, buf);
Strcpy(buf, objects[dis].oc_pre_discovered ? "* " : " ");
if (lootsort)
(void) sortloot_descr(dis, &buf[2]);
- Strcat(buf, disco_typename(dis));
+ disco_append_typename(buf, dis);
if (!alphabetized && !lootsort)
putstr(tmpwin, 0, buf);
-/* NetHack 3.7 objnam.c $NHDT-Date: 1654557302 2022/06/06 23:15:02 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.368 $ */
+/* NetHack 3.7 objnam.c $NHDT-Date: 1672829441 2023/01/04 10:50:41 $ $NHDT-Branch: naming-overflow-fix $:$NHDT-Revision: 1.386 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*-Copyright (c) Robert Patrick Rankin, 2011. */
/* NetHack may be freely redistributed. See license for details. */
static char *strprepend(char *, const char *);
static char *nextobuf(void);
static void releaseobuf(char *);
+static void xcalled(char *, int, const char *, const char *);
static char *xname_flags(struct obj *, unsigned);
static char *minimal_xname(struct obj *);
static void add_erosion_words(struct obj *, char *);
if (!actualn)
actualn = (otyp > 0 && otyp < MAXOCLASSES) ? "generic" : "object?";
+ buf[0] = '\0'; /* redundant */
switch (ocl->oc_class) {
case COIN_CLASS:
Strcpy(buf, "coin");
else
Strcpy(buf, "amulet");
if (un)
- Sprintf(eos(buf), " called %s", un);
+ xcalled(buf, BUFSZ - (dn ? (int) strlen(dn) + 3 : 0), "", un);
if (dn)
Sprintf(eos(buf), " (%s)", dn);
return buf;
Strcpy(buf, actualn);
if (GemStone(otyp))
Strcat(buf, " stone");
- if (un)
- Sprintf(eos(buf), " called %s", un);
+ if (un) /* 3: length of " (" + ")" which will enclose 'dn' */
+ xcalled(buf, BUFSZ - (dn ? (int) strlen(dn) + 3 : 0), "", un);
if (dn)
Sprintf(eos(buf), " (%s)", dn);
} else {
Strcat(buf,
(ocl->oc_material == MINERAL) ? " stone" : " gem");
if (un)
- Sprintf(eos(buf), " called %s", un);
+ xcalled(buf, BUFSZ, "", un);
}
return buf;
}
else
Sprintf(eos(buf), " of %s", actualn);
}
- if (un)
- Sprintf(eos(buf), " called %s", un);
+ if (un) /* 3: length of " (" + ")" which will enclose 'dn' */
+ xcalled(buf, BUFSZ - (dn ? (int) strlen(dn) + 3 : 0), "", un);
if (dn)
Sprintf(eos(buf), " (%s)", dn);
return buf;
}
}
+/* add "<pfx> called <sfx>" to end of buf, truncating if necessary */
+static void
+xcalled(
+ char *buf, /* eos(obuf) or eos(&obuf[PREFIX]) */
+ int siz, /* BUFSZ or BUFSZ-PREFIX */
+ const char *pfx, /* usually class string, sometimes more specific */
+ const char *sfx) /* user assigned type name */
+{
+ int bufsiz = siz - 1 - (int) strlen(buf),
+ pfxlen = (int) (strlen(pfx) + sizeof " called " - sizeof "");
+
+ if (pfxlen > bufsiz)
+ panic("xcalled: not enough room for prefix (%d > %d)",
+ pfxlen, bufsiz);
+
+ Sprintf(eos(buf), "%s called %.*s", pfx, bufsiz - pfxlen, sfx);
+}
+
char *
xname(struct obj* obj)
{
for those; pacify static analyzer without resorting to impossible() */
if (!actualn)
actualn = (typ > 0 && typ < MAXOCLASSES) ? "generic" : "object?";
- /* As of 3.6.2: this used to be part of 'dn's initialization, but it
+ /* 3.6.2: this used to be part of 'dn's initialization, but it
needs to come after possibly overriding 'actualn' */
if (!dn)
dn = actualn;
else if (nn)
Strcpy(buf, actualn);
else if (un)
- Sprintf(buf, "amulet called %s", un);
+ xcalled(buf, BUFSZ - PREFIX, "amulet", un);
else
Sprintf(buf, "%s amulet", dn);
break;
Strcat(buf, dn);
else if (nn)
Strcat(buf, actualn);
- else if (un) {
- Strcat(buf, dn);
- Strcat(buf, " called ");
- Strcat(buf, un);
- } else
+ else if (un)
+ xcalled(buf, BUFSZ - PREFIX, dn, un);
+ else
Strcat(buf, dn);
if (typ == FIGURINE && omndx != NON_PM) {
if (nn)
Strcat(buf, actualn);
else if (un)
- Sprintf(eos(buf), "%s called %s", armor_simple_name(obj), un);
+ xcalled(buf, BUFSZ - PREFIX, armor_simple_name(obj), un);
else
Strcat(buf, dn);
break;
}
Strcat(buf, actualn);
} else {
- Strcat(buf, " called ");
- Strcat(buf, un);
+ xcalled(buf, BUFSZ - PREFIX, "", un);
}
} else {
Strcat(buf, dn);
Strcat(buf, " of ");
Strcat(buf, actualn);
} else if (un) {
- Strcat(buf, " called ");
- Strcat(buf, un);
+ xcalled(buf, BUFSZ - PREFIX, "", un);
} else if (ocl->oc_magic) {
Strcat(buf, " labeled ");
Strcat(buf, dn);
else if (nn)
Sprintf(buf, "wand of %s", actualn);
else if (un)
- Sprintf(buf, "wand called %s", un);
+ xcalled(buf, BUFSZ - PREFIX, "wand", un);
else
Sprintf(buf, "%s wand", dn);
break;
else if (nn)
Strcpy(buf, actualn);
else if (un)
- Sprintf(buf, "novel called %s", un);
+ xcalled(buf, BUFSZ - PREFIX, "novel", un);
else
Sprintf(buf, "%s book", dn);
break;
Strcpy(buf, "spellbook of ");
Strcat(buf, actualn);
} else if (un) {
- Sprintf(buf, "spellbook called %s", un);
+ xcalled(buf, BUFSZ - PREFIX, "spellbook", un);
} else
Sprintf(buf, "%s spellbook", dn);
break;
else if (nn)
Sprintf(buf, "ring of %s", actualn);
else if (un)
- Sprintf(buf, "ring called %s", un);
+ xcalled(buf, BUFSZ - PREFIX, "ring", un);
else
Sprintf(buf, "%s ring", dn);
break;
Strcpy(buf, rock);
} else if (!nn) {
if (un)
- Sprintf(buf, "%s called %s", rock, un);
+ xcalled(buf, BUFSZ - PREFIX, rock, un);
else
Sprintf(buf, "%s %s", dn, rock);
} else {
Strcat(buf, " named ");
nameit:
obufp = eos(buf);
- Strcat(buf, ONAME(obj));
+ (void) strncat(buf, ONAME(obj),
+ BUFSZ - 1 - PREFIX - (unsigned) strlen(buf));
/* downcase "The" in "<quest-artifact-item> named The ..." */
if (obj->oartifact && !strncmp(obufp, "The ", 4))
*obufp = lowc(*obufp); /* = 't'; */
}
char *
-an(const char* str)
+an(const char *str)
{
char *buf = nextobuf();
return strcpy(buf, "an []");
}
(void) just_an(buf, str);
- return strcat(buf, str);
+ return strncat(buf, str, BUFSZ - 1 - Strlen(buf));
}
char *
-An(const char* str)
+An(const char *str)
{
char *tmp = an(str);
Strcpy(buf, "the ");
else
buf[0] = '\0';
- Strcat(buf, str);
-
- return buf;
+ return strncat(buf, str, BUFSZ - 1 - Strlen(buf));
}
char *
*/
if ((d->p = strstri(d->bp, " named ")) != 0) {
*d->p = 0;
+ /* note: if 'name' is too long, oname() will truncate it */
d->name = d->p + 7;
}
if ((d->p = strstri(d->bp, " called ")) != 0) {
*d->p = 0;
+ /* note: if 'un' is too long, obj lookup just won't match anything */
d->un = d->p + 8;
/* "helmet called telepathy" is not "helmet" (a specific type)
* "shield called reflection" is not "shield" (a general type)