]> granicus.if.org Git - nethack/commitdiff
safe_qbuf, short_oname (trunk only)
authornethack.rankin <nethack.rankin>
Tue, 24 Oct 2006 05:18:00 +0000 (05:18 +0000)
committernethack.rankin <nethack.rankin>
Tue, 24 Oct 2006 05:18:00 +0000 (05:18 +0000)
     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
src/apply.c
src/do_name.c
src/eat.c
src/invent.c
src/lock.c
src/mhitu.c
src/objnam.c
src/pickup.c
src/trap.c

index ea4cd319322655a6bfa7241f4b339022b217bd31..0ac63d8c6568137b5b270b3f810116f01147ccdc 100644 (file)
@@ -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 */
index 416c65f87429cc672632a07b5f714ea0f6b1f893..4010540c46ace6dcf06b4e45da2dac4023f62c8d 100644 (file)
@@ -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.
index da473b9649824f1fbfbc6bc64320b846dcc5dfb2..861cca8f4ff0cc08c042f8fc75536edcfbeb88f1 100644 (file)
@@ -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 */
index e048486ad68ee23d7c3ebde9f4684759a1007a09..8ccf7b6b27aee2c41c30d68eab859cc865ab9354 100644 (file)
--- 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 <an object> here; <verb> it?" or
+                  "There are <N objects> here; <verb> 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')
index f7aed474d04f845f5f3e0b251cd85bf64010325c..02af4b4afab54792ef65d5b9fe10f505612a066a 100644 (file)
@@ -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,
index 88844a060f116a144160c54515469b12285b17e0..1fcd4c2281ee62698c2e4e3428de85e5f56bdd05 100644 (file)
@@ -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 <a box> here; <verb> <it|its lock>?" */
+                   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);
index c54a98efc5f9da799f2d51826223ce20635ccb1a..ba943f75dcea5a3e1837385346b5933536d5651f 100644 (file)
@@ -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 {
index 623057ad2f362f1777af134a58e9ad6cc23f4674..cdf3a9a7642e48927114fcbb477194d59e8855e5 100644 (file)
@@ -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*/
index 678c5ffb27a7391bcc559de8b1c43ddd7e992bfa..ae989a2ee14394a23ebe5f01a9bfaa6527422bd7 100644 (file)
@@ -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 <count> 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;
index f339b452099323ed9cd5c63cd1ad0074ed5fac46..87ebfc44431a4d0d96d39694642e17d9963dca1c 100644 (file)
@@ -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;