From 9315307df266d7b1442410376f123c208c01a3c7 Mon Sep 17 00:00:00 2001 From: "nethack.rankin" Date: Tue, 24 Oct 2006 05:18:00 +0000 Subject: [PATCH] safe_qbuf, short_oname (trunk only) Change safe_qbuf() so that instead of picking one of three strings for sprintf() to plug into a prompt string, it actually constructs the full prompt string itself. Also pass in the unformatted object and a pair of formatting functions instead of performing dual formatting in advance. The actual formatting is done via new routine short_oname() which also takes an object and a pair of formatting routines plus a target length. It uses the first routine, typically xname() or doname(), and formats the object, then if the result is too long it makes some transformations, and tries again. If truncating "called foo" and "named bar" down to 12 chars and omitting "uncursed, rustproof, thoroughly corroded" attributes still result in a string that's too long, it uses the other formatting routine. The latter calls one of several jacket routines around simple_typename() to produce a short result. This has been through about four incarnations now and has gotten a bit less testing each time, but I need to get it in place before I end up running out of gas and abandoning it. I've got some changes to shk.c (where safe_qbuf is needed but not currently used) that now need to be redone and will come eventually. --- include/extern.h | 9 +- src/apply.c | 35 ++++---- src/do_name.c | 4 +- src/eat.c | 14 +-- src/invent.c | 53 ++++++++---- src/lock.c | 19 ++-- src/mhitu.c | 13 ++- src/objnam.c | 220 +++++++++++++++++++++++++++++++++++++++++++++-- src/pickup.c | 52 +++-------- src/trap.c | 6 +- 10 files changed, 318 insertions(+), 107 deletions(-) diff --git a/include/extern.h b/include/extern.h index ea4cd3193..0ac63d8c6 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1500,6 +1500,8 @@ E boolean FDECL(not_fully_identified, (struct obj *)); E char *FDECL(corpse_xname, (struct obj *,const char *,unsigned)); E char *FDECL(cxname, (struct obj *)); E char *FDECL(killer_xname, (struct obj *)); +E char *FDECL(short_oname, (struct obj *,char *(*)(OBJ_P),char *(*)(OBJ_P), + unsigned)); E const char *FDECL(singular, (struct obj *,char *(*)(OBJ_P))); E char *FDECL(an, (const char *)); E char *FDECL(An, (const char *)); @@ -1516,6 +1518,9 @@ E char *FDECL(yname, (struct obj *)); E char *FDECL(Yname2, (struct obj *)); E char *FDECL(ysimple_name, (struct obj *)); E char *FDECL(Ysimple_name2, (struct obj *)); +E char *FDECL(simpleonames, (struct obj *)); +E char *FDECL(ansimpleoname, (struct obj *)); +E char *FDECL(thesimpleoname, (struct obj *)); E char *FDECL(bare_artifactname, (struct obj *)); E char *FDECL(makeplural, (const char *)); E char *FDECL(makesingular, (const char *)); @@ -1524,6 +1529,8 @@ E int FDECL(rnd_class, (int,int)); E const char *FDECL(cloak_simple_name, (struct obj *)); E const char *FDECL(helm_simple_name, (struct obj *)); E const char *FDECL(mimic_obj_name, (struct monst *)); +E char *FDECL(safe_qbuf, (char *,const char *,const char *,struct obj *, + char *(*)(OBJ_P),char *(*)(OBJ_P),const char *)); /* ### options.c ### */ @@ -1645,8 +1652,6 @@ E boolean FDECL(container_gone, (int (*)(OBJ_P))); E int FDECL(use_container, (struct obj **,int)); E int FDECL(loot_mon, (struct monst *,int *,boolean *)); E int NDECL(dotip); -E const char *FDECL(safe_qbuf, (const char *,unsigned, - const char *,const char *,const char *)); #ifdef AUTOPICKUP_EXCEPTIONS E boolean FDECL(is_autopickup_exception, (struct obj *, BOOLEAN_P)); #endif /* AUTOPICKUP_EXCEPTIONS */ diff --git a/src/apply.c b/src/apply.c index 416c65f87..4010540c4 100644 --- a/src/apply.c +++ b/src/apply.c @@ -1014,7 +1014,7 @@ struct obj **optr; register struct obj *obj = *optr; register struct obj *otmp; const char *s = (obj->quan != 1) ? "candles" : "candle"; - char qbuf[QBUFSZ]; + char qbuf[QBUFSZ], qsfx[QBUFSZ], *q; if(u.uswallow) { You(no_elbow_room); @@ -1031,13 +1031,16 @@ struct obj **optr; return; } - Sprintf(qbuf, "Attach %s", the(xname(obj))); - Sprintf(eos(qbuf), " to %s?", - safe_qbuf(qbuf, sizeof(" to ?"), the(xname(otmp)), - the(simple_typename(otmp->otyp)), "it")); - if(yn(qbuf) == 'n') { - if (!obj->lamplit) - You("try to light %s...", the(xname(obj))); + /* first, minimal candelabrum suffix for formatting candles */ + Sprintf(qsfx, " to\033%s?", thesimpleoname(otmp)); + /* next, format the candles as a prefix for the candelabrum */ + (void)safe_qbuf(qbuf, "Attach ", qsfx, + obj, yname, thesimpleoname, s); + /* strip temporary candelabrum suffix */ + if ((q = strstri(qbuf, " to\033")) != 0) Strcpy(q, " to "); + /* last, format final "attach candles to candelabrum?" query */ + if (yn(safe_qbuf(qbuf, qbuf, "?", + otmp, yname, thesimpleoname, "it")) == 'n') { use_lamp(obj); return; } else { @@ -2755,23 +2758,21 @@ do_break_wand(obj) boolean shop_damage = FALSE; boolean fillmsg = FALSE; int expltype = EXPL_MAGICAL; - char confirm[QBUFSZ], the_wand[BUFSZ], buf[BUFSZ]; + char confirm[QBUFSZ], buf[BUFSZ]; - Strcpy(the_wand, yname(obj)); - Sprintf(confirm, "Are you really sure you want to break %s?", - safe_qbuf("", sizeof("Are you really sure you want to break ?"), - the_wand, ysimple_name(obj), "the wand")); - if (yn(confirm) == 'n' ) return 0; + if (yn(safe_qbuf(confirm, "Are you really sure you want to break ", "?", + obj, yname, ysimple_name, "the wand")) == 'n') + return 0; if (nohands(youmonst.data)) { - You_cant("break %s without hands!", the_wand); + You_cant("break %s without hands!", yname(obj)); return 0; } else if (ACURR(A_STR) < 10) { - You("don't have the strength to break %s!", the_wand); + You("don't have the strength to break %s!", yname(obj)); return 0; } pline("Raising %s high above your %s, you break it in two!", - the_wand, body_part(HEAD)); + yname(obj), body_part(HEAD)); /* [ALI] Do this first so that wand is removed from bill. Otherwise, * the freeinv() below also hides it from setpaid() which causes problems. diff --git a/src/do_name.c b/src/do_name.c index da473b964..861cca8f4 100644 --- a/src/do_name.c +++ b/src/do_name.c @@ -391,9 +391,7 @@ register struct obj *obj; Sprintf(qbuf, "What do you want to name %s ", is_plural(obj) ? "these" : "this"); - Sprintf(eos(qbuf), "%s?", - safe_qbuf(qbuf, sizeof("?"), - xname(obj), simple_typename(obj->otyp), "")); + (void)safe_qbuf(qbuf, qbuf, "?", obj, xname, simpleonames, "item"); getlin(qbuf, buf); if(!*buf || *buf == '\033') return; /* strip leading and trailing spaces; unnames item if all spaces */ diff --git a/src/eat.c b/src/eat.c index e048486ad..8ccf7b6b2 100644 --- a/src/eat.c +++ b/src/eat.c @@ -2813,12 +2813,16 @@ floorfood(verb,corpsecheck) /* get food from floor or pack */ (otmp->otyp==CORPSE && (corpsecheck == 1 || tinnable(otmp))) : feeding ? (otmp->oclass != COIN_CLASS && is_edible(otmp)) : otmp->oclass==FOOD_CLASS) { + char qsfx[QBUFSZ]; + boolean one = (otmp->quan == 1L); + + /* "There is here; it?" or + "There are here; one?" */ Sprintf(qbuf, "There %s ", otense(otmp, "are")); - Sprintf(eos(qbuf), "%s here; %s %s?", - safe_qbuf(qbuf, sizeof(" here; ...?") + strlen(verb), - doname(otmp), simple_typename(otmp->otyp), - "something"), - verb, (otmp->quan == 1L) ? "it" : "one"); + Sprintf(qsfx, " here; %s %s?", verb, one ? "it" : "one"); + (void)safe_qbuf(qbuf, qbuf, qsfx, + otmp, doname, ansimpleoname, + one ? something : (const char *)"things"); if ((c = yn_function(qbuf,ynqchars,'n')) == 'y') return(otmp); else if (c == 'q') diff --git a/src/invent.c b/src/invent.c index f7aed474d..02af4b4af 100644 --- a/src/invent.c +++ b/src/invent.c @@ -17,6 +17,8 @@ STATIC_DCL boolean FDECL(taking_off, (const char *)); STATIC_DCL boolean FDECL(putting_on, (const char *)); STATIC_PTR int FDECL(ckunpaid,(struct obj *)); STATIC_PTR int FDECL(ckvalidcat,(struct obj *)); +STATIC_PTR char *FDECL(safeq_xprname, (struct obj *)); +STATIC_PTR char *FDECL(safeq_shortxprname, (struct obj *)); STATIC_DCL char FDECL(display_pickinv, (const char *,BOOLEAN_P, long *)); STATIC_DCL char FDECL(display_used_invlets, (CHAR_P)); STATIC_DCL boolean FDECL(this_type_only, (struct obj *)); @@ -1162,6 +1164,32 @@ register struct obj *otmp; W_WEP | W_SWAPWEP | W_QUIVER)))); } +/* extra xprname() input that askchain() can't pass through safe_qbuf() */ +STATIC_VAR struct xprnctx { + char let; + boolean dot; +} safeq_xprn_ctx; + +/* safe_qbuf() -> short_oname() callback */ +STATIC_PTR char * +safeq_xprname(obj) +struct obj *obj; +{ + return xprname(obj, (char *)0, + safeq_xprn_ctx.let, safeq_xprn_ctx.dot, + 0L, 0L); +} + +/* alternate safe_qbuf() -> short_oname() callback */ +STATIC_PTR char * +safeq_shortxprname(obj) +struct obj *obj; +{ + return xprname(obj, ansimpleoname(obj), + safeq_xprn_ctx.let, safeq_xprn_ctx.dot, + 0L, 0L); +} + static NEARDATA const char removeables[] = { ARMOR_CLASS, WEAPON_CLASS, RING_CLASS, AMULET_CLASS, TOOL_CLASS, 0 }; @@ -1396,20 +1424,16 @@ nextclass: if (ident && !not_fully_identified(otmp)) continue; if (ckfn && !(*ckfn)(otmp)) continue; if (!allflag) { - Strcpy(qbuf, !ininv ? doname(otmp) : - xprname(otmp, (char *)0, ilet, !nodot, 0L, 0L)); - /* this code seemed too complex to use safe_qbuf */ - if (strlen(qbuf) > QBUFSZ - 20) { - Strcpy(qbuf, - !ininv ? an(simple_typename(otmp->otyp)) : - xprname(otmp, simple_typename(otmp->otyp), - ilet, !nodot, 0L, 0L)); - } - Strcat(qbuf, "?"); + safeq_xprn_ctx.let = ilet; + safeq_xprn_ctx.dot = !nodot; + (void)safe_qbuf(qbuf, (char *)0, "?", + otmp, ininv ? safeq_xprname : doname, + ininv ? safeq_shortxprname : ansimpleoname, + "item"); sym = (takeoff || ident || otmp->quan < 2L) ? nyaq(qbuf) : nyNaq(qbuf); - } - else sym = 'y'; + } else + sym = 'y'; otmpo = otmp; if (sym == '#') { @@ -3045,9 +3069,8 @@ register struct obj *obj; int n; menu_item *selected = 0; - Sprintf(qbuf,"Contents of %s:", - safe_qbuf("", sizeof("Contents of :"), - doname(obj), simple_typename(obj->otyp), "")); + (void)safe_qbuf(qbuf, "Contents of ", ":", + obj, doname, ansimpleoname, "that"); if (obj->cobj) { n = query_objlist(qbuf, obj->cobj, INVORDER_SORT, &selected, diff --git a/src/lock.c b/src/lock.c index 88844a060..1fcd4c228 100644 --- a/src/lock.c +++ b/src/lock.c @@ -271,6 +271,7 @@ pick_lock(pick) /* pick a lock with a given object */ if(!get_adjacent_loc((char *)0, "Invalid location!", u.ux, u.uy, &cc)) return 0; if (cc.x == u.ux && cc.y == u.uy) { /* pick lock on a container */ const char *verb; + char qsfx[QBUFSZ]; boolean it; int count; @@ -300,10 +301,12 @@ pick_lock(pick) /* pick a lock with a given object */ else if (!otmp->olocked) verb = "lock", it = 1; else if (picktyp != LOCK_PICK) verb = "unlock", it = 1; else verb = "pick"; - Sprintf(qbuf, "There is %s here, %s %s?", - safe_qbuf("", sizeof("There is here, unlock its lock?"), - doname(otmp), an(simple_typename(otmp->otyp)), "a box"), + + /* "There is here; ?" */ + Sprintf(qsfx, " here; %s %s?", verb, it ? "it" : "its lock"); + (void)safe_qbuf(qbuf, "There is ", qsfx, + otmp, doname, ansimpleoname, "a box"); otmp->lknown = 1; c = ynq(qbuf); @@ -397,8 +400,8 @@ pick_lock(pick) /* pick a lock with a given object */ } #endif - Sprintf(qbuf,"%sock it?", - (door->doormask & D_LOCKED) ? "Unl" : "L" ); + Sprintf(qbuf,"%s it?", + (door->doormask & D_LOCKED) ? "Unlock" : "Lock"); c = yn(qbuf); if(c == 'n') return(0); @@ -466,10 +469,8 @@ doforce() /* try to force a chest with your weapon */ otmp->lknown = 1; continue; } - Sprintf(qbuf,"There is %s here, force its lock?", - safe_qbuf("", sizeof("There is here, force its lock?"), - doname(otmp), an(simple_typename(otmp->otyp)), - "a box")); + (void)safe_qbuf(qbuf, "There is ", " here; force its lock?", + otmp, doname, ansimpleoname, "a box"); otmp->lknown = 1; c = ynq(qbuf); diff --git a/src/mhitu.c b/src/mhitu.c index c54a98efc..ba943f75d 100644 --- a/src/mhitu.c +++ b/src/mhitu.c @@ -2170,9 +2170,9 @@ register struct monst *mon; if (uarmg) continue; /* next ring might not be worn */ } if (rn2(20) < ACURR(A_CHA)) { - Sprintf(qbuf, "\"That %s looks pretty. May I have it?\"", - safe_qbuf("",sizeof("\"That looks pretty. May I have it?\""), - xname(ring), simple_typename(ring->otyp), "ring")); + (void)safe_qbuf(qbuf, "\"That ", + " looks pretty. May I have it?\"", + ring, xname, simpleonames, "ring"); makeknown(RIN_ADORNMENT); if (yn(qbuf) == 'n') continue; } else pline("%s decides she'd like %s, and takes it.", @@ -2195,10 +2195,9 @@ register struct monst *mon; if (uarmg) break; /* no point trying further rings */ } if (rn2(20) < ACURR(A_CHA)) { - Sprintf(qbuf,"\"That %s looks pretty. Would you wear it for me?\"", - safe_qbuf("", - sizeof("\"That looks pretty. Would you wear it for me?\""), - xname(ring), simple_typename(ring->otyp), "ring")); + (void)safe_qbuf(qbuf, "\"That ", + " looks pretty. Would you wear it for me?\"", + ring, xname, simpleonames, "ring"); makeknown(RIN_ADORNMENT); if (yn(qbuf) == 'n') continue; } else { diff --git a/src/objnam.c b/src/objnam.c index 623057ad2..cdf3a9a76 100644 --- a/src/objnam.c +++ b/src/objnam.c @@ -1,4 +1,4 @@ -/* SCCS Id: @(#)objnam.c 3.5 2006/05/08 */ +/* SCCS Id: @(#)objnam.c 3.5 2006/10/16 */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -12,6 +12,7 @@ STATIC_DCL char *FDECL(strprepend,(char *,const char *)); STATIC_DCL boolean FDECL(wishymatch, (const char *,const char *,BOOLEAN_P)); STATIC_DCL char *NDECL(nextobuf); +STATIC_DCL void FDECL(releaseobuf, (char *)); STATIC_DCL void FDECL(add_erosion_words, (struct obj *, char *)); struct Jitem { @@ -62,14 +63,25 @@ register const char *pref; } /* manage a pool of BUFSZ buffers, so callers don't have to */ +static char NEARDATA obufs[NUMOBUF][BUFSZ]; +static int obufidx = 0; + STATIC_OVL char * nextobuf() { - static char NEARDATA bufs[NUMOBUF][BUFSZ]; - static int bufidx = 0; + obufidx = (obufidx + 1) % NUMOBUF; + return obufs[obufidx]; +} - bufidx = (bufidx + 1) % NUMOBUF; - return bufs[bufidx]; +/* put the most recently allocated buffer back if possible */ +STATIC_OVL void +releaseobuf(bufp) +char *bufp; +{ + /* caller may not know whether bufp is the most recently allocated + buffer; if it isn't, do nothing */ + if (bufp == obufs[obufidx]) + obufidx = (obufidx - 1 + NUMOBUF) % NUMOBUF; } char * @@ -1030,6 +1042,82 @@ struct obj *obj; return buf; } +/* xname,doname,&c with long results reformatted to omit some stuff */ +char * +short_oname(obj, func, altfunc, lenlimit) +struct obj *obj; +char *FDECL((*func), (OBJ_P)), /* main formatting routine */ + *FDECL((*altfunc), (OBJ_P)); /* alternate for shortest result */ +unsigned lenlimit; +{ + struct obj save_obj; + char unamebuf[12], onamebuf[12], + *save_oname, *save_uname, *outbuf; + + outbuf = (*func)(obj); + if ((unsigned)strlen(outbuf) <= lenlimit) return outbuf; + + /* shorten called string to fairly small amount */ + save_uname = objects[obj->otyp].oc_uname; + if (save_uname && strlen(save_uname) >= sizeof unamebuf) { + (void) strncpy(unamebuf, save_uname, sizeof unamebuf - 4); + Strcpy(unamebuf + sizeof unamebuf - 4, "..."); + objects[obj->otyp].oc_uname = unamebuf; + releaseobuf(outbuf); + outbuf = (*func)(obj); + objects[obj->otyp].oc_uname = save_uname; /* restore called string */ + if ((unsigned)strlen(outbuf) <= lenlimit) return outbuf; + } + + /* shorten named string to fairly small amount */ + save_oname = has_oname(obj) ? ONAME(obj) : 0; + if (save_oname && strlen(save_oname) >= sizeof onamebuf) { + (void) strncpy(onamebuf, save_oname, sizeof onamebuf - 4); + Strcpy(onamebuf + sizeof onamebuf - 4, "..."); + ONAME(obj) = onamebuf; + releaseobuf(outbuf); + outbuf = (*func)(obj); + ONAME(obj) = save_oname; /* restore named string */ + if ((unsigned)strlen(outbuf) <= lenlimit) return outbuf; + } + + /* shorten both called and named strings; + unamebuf and onamebuf have both already been populated */ + if (save_uname && strlen(save_uname) >= sizeof unamebuf && + save_oname && strlen(save_oname) >= sizeof onamebuf) { + objects[obj->otyp].oc_uname = unamebuf; + ONAME(obj) = onamebuf; + releaseobuf(outbuf); + outbuf = (*func)(obj); + if ((unsigned)strlen(outbuf) <= lenlimit) { + objects[obj->otyp].oc_uname = save_uname; + ONAME(obj) = save_oname; + return outbuf; + } + } + + /* still long; strip several name-lengthening attributes; + called and named strings are still in truncated form */ + save_obj = *obj; + obj->bknown = obj->rknown = obj->greased = 0; + obj->oeroded = obj->oeroded2 = 0; + releaseobuf(outbuf); + outbuf = (*func)(obj); + if (altfunc && (unsigned)strlen(outbuf) > lenlimit) { + /* still long; use the alternate function (usually one of + the jackets around simple_typename()) */ + releaseobuf(outbuf); + outbuf = (*altfunc)(obj); + } + /* restore the object */ + *obj = save_obj; + if (save_oname) ONAME(obj) = save_oname; + if (save_uname) objects[obj->otyp].oc_uname = save_uname; + + /* use whatever we've got, whether it's too long or not */ + return outbuf; +} + /* * Used if only one of a collection of objects is named (e.g. in eat.c). */ @@ -1413,6 +1501,48 @@ struct obj *obj; return s; } +/* "scroll" or "scrolls" */ +char * +simpleonames(obj) +struct obj *obj; +{ + char *simpleoname = simple_typename(obj->otyp); + + if (obj->quan != 1L) simpleoname = makeplural(simpleoname); + return simpleoname; +} + +/* "a scroll" or "scrolls"; "a silver bell" or "the Bell of Opening" */ +char * +ansimpleoname(obj) +struct obj *obj; +{ + char *simpleoname = simpleonames(obj); + int otyp = obj->otyp; + + /* prefix with "the" if a unique item, or a fake one imitating same, + has been formatted with its actual name (we let typename() handle + any `known' and `dknown' checking necessary) */ + if (otyp == FAKE_AMULET_OF_YENDOR) otyp = AMULET_OF_YENDOR; + if (objects[otyp].oc_unique && + !strcmp(simpleoname, OBJ_NAME(objects[otyp]))) + return the(simpleoname); + + /* simpleoname is singular if quan==1, plural otherwise */ + if (obj->quan == 1L) simpleoname = an(simpleoname); + return simpleoname; +} + +/* "the scroll" or "the scrolls" */ +char * +thesimpleoname(obj) +struct obj *obj; +{ + char *simpleoname = simpleonames(obj); + + return the(simpleoname); +} + char * bare_artifactname(obj) struct obj *obj; @@ -3038,4 +3168,84 @@ struct monst *mtmp; return "whatcha-may-callit"; } +/* + * Construct a query prompt string, based around an object name, which is + * guaranteed to fit within [QBUFSZ]. Takes an optional prefix, three + * choices for filling in the middle (two object formatting functions and a + * last resort literal which should be very short), and an optional suffix. + */ +char * +safe_qbuf(qbuf, qprefix, qsuffix, obj, func, altfunc, lastR) +char *qbuf; /* output buffer */ +const char *qprefix, *qsuffix; +struct obj *obj; +char *FDECL((*func), (OBJ_P)), *FDECL((*altfunc), (OBJ_P)); +const char *lastR; +{ + char *bufp, *endp; + /* convert size_t (or int for ancient systems) to ordinary unsigned */ + unsigned len, lenlimit, + len_qpfx = (unsigned)(qprefix ? strlen(qprefix) : 0), + len_qsfx = (unsigned)(qsuffix ? strlen(qsuffix) : 0), + len_lastR = (unsigned)strlen(lastR); + + lenlimit = QBUFSZ - 1; + endp = qbuf + lenlimit; + /* sanity check, aimed mainly at paniclog (it's conceivable for + the result of short_oname() to be shorter than the length of + the last resort string, but we ignore that possibility here) */ + if (len_qpfx > lenlimit) + impossible("safe_qbuf: prefix too long (%u characters).", + len_qpfx); + else if (len_qpfx + len_qsfx > lenlimit) + impossible("safe_qbuf: suffix too long (%u + %u characters).", + len_qpfx, len_qsfx); + else if (len_qpfx + len_lastR + len_qsfx > lenlimit) + impossible("safe_qbuf: filler too long (%u + %u + %u characters).", + len_qpfx, len_lastR, len_qsfx); + + /* the output buffer might be the same as the prefix if caller + has already partially filled it */ + if (qbuf == qprefix) { + /* prefix is already in the buffer */ + *endp = '\0'; + } else if (qprefix) { + /* put prefix into the buffer */ + (void)strncpy(qbuf, qprefix, lenlimit); + *endp = '\0'; + } else { + /* no prefix; output buffer starts out empty */ + qbuf[0] = '\0'; + } + len = (unsigned)strlen(qbuf); + + if (len + len_lastR + len_qsfx > lenlimit) { + /* too long; skip formatting, last resort output is truncated */ + if (len < lenlimit) { + (void)strncpy(&qbuf[len], lastR, lenlimit - len); + *endp = '\0'; + len = (unsigned)strlen(qbuf); + if (qsuffix && len < lenlimit) { + (void)strncpy(&qbuf[len], qsuffix, lenlimit - len); + *endp = '\0'; + /* len = (unsigned)strlen(qbuf); */ + } + } + } else { + /* suffix and last resort are guaranteed to fit */ + len += len_qsfx; /* include the pending suffix */ + /* format the object */ + bufp = short_oname(obj, func, altfunc, lenlimit - len); + if (len + strlen(bufp) <= lenlimit) + Strcat(qbuf, bufp); /* formatted name fits */ + else + Strcat(qbuf, lastR); /* use last resort */ + releaseobuf(bufp); + + if (qsuffix) Strcat(qbuf, qsuffix); + } + /* assert( strlen(qbuf) < QBUFSZ ); */ + return qbuf; +} + /*objnam.c*/ diff --git a/src/pickup.c b/src/pickup.c index 678c5ffb2..ae989a2ee 100644 --- a/src/pickup.c +++ b/src/pickup.c @@ -1,4 +1,4 @@ -/* SCCS Id: @(#)pickup.c 3.5 2006/07/08 */ +/* SCCS Id: @(#)pickup.c 3.5 2006/10/16 */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -580,9 +580,9 @@ menu_pickup: if (!all_of_a_type) { char qbuf[BUFSZ]; - Sprintf(qbuf, "Pick up %s?", - safe_qbuf("", sizeof("Pick up ?"), doname(obj), - an(simple_typename(obj->otyp)), "something")); + + (void)safe_qbuf(qbuf, "Pick up ", "?", + obj, doname, ansimpleoname, something); switch ((obj->quan < 2L) ? ynaq(qbuf) : ynNaq(qbuf)) { case 'q': goto end_query; /* out 2 levels */ case 'n': continue; @@ -1219,9 +1219,9 @@ boolean telekinesis; (next_encumbr > MOD_ENCUMBER) ? nearloadmsg : moderateloadmsg); if (container) (void) strsubst(qbuf,"lifting","removing"); - Sprintf(eos(qbuf), " %s. Continue?", - safe_qbuf(qbuf, sizeof(" . Continue?"), - doname(obj), an(simple_typename(obj->otyp)), "something")); + Strcat(qbuf, " "); + (void)safe_qbuf(qbuf, qbuf, ". Continue?", + obj, doname, ansimpleoname, something); obj->quan = savequan; switch (ynq(qbuf)) { case 'q': result = -1; break; @@ -1238,32 +1238,6 @@ boolean telekinesis; return result; } -/* To prevent qbuf overflow in prompts use planA only - * if it fits, or planB if PlanA doesn't fit, - * finally using the fallback as a last resort. - * last_restort is expected to be very short. - */ -const char * -safe_qbuf(qbuf, padlength, planA, planB, last_resort) -const char *qbuf, *planA, *planB, *last_resort; -unsigned padlength; -{ - /* convert size_t (or int for ancient systems) to ordinary unsigned */ - unsigned len_qbuf = (unsigned)strlen(qbuf), - len_planA = (unsigned)strlen(planA), - len_planB = (unsigned)strlen(planB), - len_lastR = (unsigned)strlen(last_resort); - unsigned textleft = QBUFSZ - (len_qbuf + padlength); - - if (len_lastR >= textleft) { - impossible("safe_qbuf: last_resort too large at %u characters.", - len_lastR); - return ""; - } - return (len_planA < textleft) ? planA : - (len_planB < textleft) ? planB : last_resort; -} - /* * Pick up of obj from the ground and add it to the hero's inventory. * Returns -1 if caller should break out of its loop, 0 if nothing picked @@ -1566,11 +1540,8 @@ lootcont: nobj = cobj->nexthere; if (Is_container(cobj)) { - Sprintf(qbuf, "There is %s here, loot it?", - safe_qbuf("", sizeof("There is here, loot it?"), - doname(cobj), an(simple_typename(cobj->otyp)), - "a container")); - c = ynq(qbuf); + c = ynq(safe_qbuf(qbuf, "There is ", " here, loot it?", + cobj, doname, ansimpleoname, "a container")); if (c == 'q') return (timepassed); if (c == 'n') continue; any = TRUE; @@ -2155,9 +2126,8 @@ int held; } if (cnt || flags.menu_style == MENU_FULL) { - Strcpy(qbuf, "Do you want to take something out of "); - Sprintf(eos(qbuf), "%s?", - safe_qbuf(qbuf, 1, yname(obj), ysimple_name(obj), "it")); + (void)safe_qbuf(qbuf, "Do you want to take something out of ", "?", + obj, yname, ysimple_name, "it"); if (flags.menu_style != MENU_TRADITIONAL) { if (flags.menu_style == MENU_FULL) { int t; diff --git a/src/trap.c b/src/trap.c index f339b4520..87ebfc444 100644 --- a/src/trap.c +++ b/src/trap.c @@ -3818,9 +3818,9 @@ boolean force; if(!u.dx && !u.dy) { for(otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere) if(Is_box(otmp)) { - Sprintf(qbuf, "There is %s here. Check it for traps?", - safe_qbuf("", sizeof("There is here. Check it for traps?"), - doname(otmp), an(simple_typename(otmp->otyp)), "a box")); + (void)safe_qbuf(qbuf, "There is ", + " here. Check it for traps?", + otmp, doname, ansimpleoname, "a box"); switch (ynq(qbuf)) { case 'q': return(0); case 'n': continue;