From b9bbf0205bfa7cba83cb6f0b252582afc4e31741 Mon Sep 17 00:00:00 2001 From: PatR Date: Fri, 27 Jan 2023 16:35:35 -0800 Subject: [PATCH] Pending 3.7 edition of the naming overflow patch. Like the 3.6.7 one, the original pieces have been combined into one commit. But it is separate from the one added to that version. --- doc/fixes3-7-0.txt | 4 ++- src/do_name.c | 66 +++++++++++++++++++++----------------- src/invent.c | 51 ++++++++++++++++++++---------- src/o_init.c | 31 ++++++++++++++++-- src/objnam.c | 79 ++++++++++++++++++++++++++++------------------ 5 files changed, 151 insertions(+), 80 deletions(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 1915cd325..73aa30191 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1,4 +1,4 @@ -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 ----------------------------------- @@ -1101,6 +1101,8 @@ eating garlic makes nearby monsters flee 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 diff --git a/src/do_name.c b/src/do_name.c index ba1b35257..670943d93 100644 --- a/src/do_name.c +++ b/src/do_name.c @@ -1,4 +1,4 @@ -/* 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. */ @@ -19,6 +19,7 @@ static void gloc_filter_done(void); 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 *); @@ -1180,6 +1181,34 @@ safe_oname(struct obj *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 * @@ -1290,17 +1319,10 @@ do_mgivenname(void) /* 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 @@ -1348,17 +1370,9 @@ do_oname(struct obj *obj) 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 @@ -1701,14 +1715,8 @@ docall(struct obj *obj) 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 */ diff --git a/src/invent.c b/src/invent.c index 6682d75f5..e144911c8 100644 --- a/src/invent.c +++ b/src/invent.c @@ -1,4 +1,4 @@ -/* 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. */ @@ -2567,17 +2567,21 @@ prinv(const char *prefix, struct obj *obj, long quan) 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; @@ -2589,18 +2593,33 @@ xprname(struct obj *obj, * * 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; diff --git a/src/o_init.c b/src/o_init.c index 50526b837..f3da16d9c 100644 --- a/src/o_init.c +++ b/src/o_init.c @@ -1,4 +1,4 @@ -/* 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. */ @@ -11,6 +11,7 @@ static void shuffle_all(void); 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 @@ -651,6 +652,30 @@ disco_typename(int otyp) 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 */ @@ -734,7 +759,7 @@ 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); @@ -966,7 +991,7 @@ doclassdisco(void) 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); diff --git a/src/objnam.c b/src/objnam.c index 960247ac3..7385b65c1 100644 --- a/src/objnam.c +++ b/src/objnam.c @@ -1,4 +1,4 @@ -/* 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. */ @@ -35,6 +35,7 @@ struct _readobjnam_data { 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 *); @@ -187,6 +188,7 @@ obj_typename(int otyp) if (!actualn) actualn = (otyp > 0 && otyp < MAXOCLASSES) ? "generic" : "object?"; + buf[0] = '\0'; /* redundant */ switch (ocl->oc_class) { case COIN_CLASS: Strcpy(buf, "coin"); @@ -217,7 +219,7 @@ obj_typename(int otyp) 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; @@ -226,8 +228,8 @@ obj_typename(int otyp) 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 { @@ -236,7 +238,7 @@ obj_typename(int otyp) Strcat(buf, (ocl->oc_material == MINERAL) ? " stone" : " gem"); if (un) - Sprintf(eos(buf), " called %s", un); + xcalled(buf, BUFSZ, "", un); } return buf; } @@ -247,8 +249,8 @@ obj_typename(int otyp) 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; @@ -498,6 +500,24 @@ reorder_fruit(boolean forward) } } +/* add " called " 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) { @@ -530,7 +550,7 @@ xname_flags( 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; @@ -592,7 +612,7 @@ xname_flags( 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; @@ -611,11 +631,9 @@ xname_flags( 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) { @@ -650,7 +668,7 @@ xname_flags( 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; @@ -750,8 +768,7 @@ xname_flags( } Strcat(buf, actualn); } else { - Strcat(buf, " called "); - Strcat(buf, un); + xcalled(buf, BUFSZ - PREFIX, "", un); } } else { Strcat(buf, dn); @@ -766,8 +783,7 @@ xname_flags( 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); @@ -782,7 +798,7 @@ xname_flags( 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; @@ -793,7 +809,7 @@ xname_flags( 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; @@ -805,7 +821,7 @@ xname_flags( 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; @@ -815,7 +831,7 @@ xname_flags( 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; @@ -826,7 +842,7 @@ xname_flags( 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 { @@ -879,7 +895,8 @@ xname_flags( 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 " named The ..." */ if (obj->oartifact && !strncmp(obufp, "The ", 4)) *obufp = lowc(*obufp); /* = 't'; */ @@ -1866,7 +1883,7 @@ just_an(char *outbuf, const char *str) } char * -an(const char* str) +an(const char *str) { char *buf = nextobuf(); @@ -1875,11 +1892,11 @@ an(const char* str) 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); @@ -1950,9 +1967,7 @@ the(const char* str) Strcpy(buf, "the "); else buf[0] = '\0'; - Strcat(buf, str); - - return buf; + return strncat(buf, str, BUFSZ - 1 - Strlen(buf)); } char * @@ -3846,10 +3861,12 @@ readobjnam_postparse1(struct _readobjnam_data *d) */ 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)