OSDN Git Service

fix #48255
[jnethack/source.git] / src / objnam.c
index 4522da5..d32c5b8 100644 (file)
@@ -1,11 +1,11 @@
-/* NetHack 3.6 objnam.c        $NHDT-Date: 1521507553 2018/03/20 00:59:13 $  $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.199 $ */
+/* NetHack 3.6 objnam.c        $NHDT-Date: 1674864732 2023/01/28 00:12:12 $  $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.259 $ */
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
 /*-Copyright (c) Robert Patrick Rankin, 2011. */
 /* NetHack may be freely redistributed.  See license for details. */
 
 /* JNetHack Copyright */
 /* (c) Issei Numata, Naoki Hamada, Shigehiro Miyashita, 1994-2000  */
-/* For 3.4-, Copyright (c) SHIRAKATA Kentaro, 2002-2018            */
+/* For 3.4-, Copyright (c) SHIRAKATA Kentaro, 2002-2023            */
 /* JNetHack may be freely redistributed.  See license for details. */
 
 #include "hack.h"
 #define NUMOBUF 12
 
 STATIC_DCL char *FDECL(strprepend, (char *, const char *));
+#if 0 /*JP*/
 STATIC_DCL short FDECL(rnd_otyp_by_wpnskill, (SCHAR_P));
-STATIC_DCL short FDECL(rnd_otyp_by_namedesc, (char *, CHAR_P));
+#endif
+STATIC_DCL short FDECL(rnd_otyp_by_namedesc, (const char *, CHAR_P, int));
 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(xcalled, (char *, int, const char *, const char *));
 STATIC_DCL char *FDECL(minimal_xname, (struct obj *));
 STATIC_DCL void FDECL(add_erosion_words, (struct obj *, char *));
 STATIC_DCL char *FDECL(doname_base, (struct obj *obj, unsigned));
+STATIC_DCL char *FDECL(just_an, (char *str, const char *));
 #if 0 /*JP*/
 STATIC_DCL boolean FDECL(singplur_lookup, (char *, char *, BOOLEAN_P,
                                            const char *const *));
@@ -38,6 +42,7 @@ STATIC_DCL char *FDECL(xname_flags, (struct obj *, unsigned));
 #if 0 /*JP*/
 STATIC_DCL boolean FDECL(badman, (const char *, BOOLEAN_P));
 #endif
+STATIC_DCL char *FDECL(globwt, (struct obj *, char *, boolean *));
 
 struct Jitem {
     int item;
@@ -48,6 +53,9 @@ struct Jitem {
 #define BSTRNCMPI(base, ptr, str, num) \
     ((ptr) < base || strncmpi((ptr), str, num))
 #define Strcasecpy(dst, src) (void) strcasecpy(dst, src)
+#if 1 /*JP*/
+#define STRNCMPEX(x, y) strncmp(x, y, l = strlen(y))
+#endif
 
 /* true for gems/rocks that should have " stone" appended to their names */
 #define GemStone(typ)                                                  \
@@ -57,7 +65,7 @@ struct Jitem {
              && typ != SAPPHIRE && typ != BLACK_OPAL && typ != EMERALD \
              && typ != OPAL)))
 
-#if 0 /*JP*/
+#if 0 /*JP:T*/
 STATIC_OVL struct Jitem Japanese_items[] = { { SHORT_SWORD, "wakizashi" },
                                              { BROADSWORD, "ninja-to" },
                                              { FLAIL, "nunchaku" },
@@ -144,9 +152,14 @@ register int otyp;
 
     if (Role_if(PM_SAMURAI) && Japanese_item_name(otyp))
         actualn = Japanese_item_name(otyp);
+    buf[0] = '\0';
 #if 1 /*JP*/
-    if(un)
-        Sprintf(buf, "%s\82Æ\8cÄ\82Î\82ê\82é", un);
+    if (un) {
+        /* \89p\8cê\82Å\82Í\8cµ\96§\82É\83T\83C\83Y\82ð\8eZ\8fo\82µ\82Ä\82¢\82é\82ª\93ú\96{\8cê\82Í\8cê\8f\87\82Ì\93s\8d\87\82Å
+           \90³\8am\82É\8eZ\8fo\82·\82é\82Ì\82ª\91å\95Ï\82È\82Ì\82Å\97]\97T\82ð\8e\9d\82Á\82½\92l\82É\82·\82é */
+        xcalled(buf,
+                BUFSZ - PREFIX - (dn ? (int) strlen(dn) + 3 : 0), "", un);
+    }
 #endif
     switch (ocl->oc_class) {
     case COIN_CLASS:
@@ -176,9 +189,9 @@ register int otyp;
     case SPBOOK_CLASS:
         if (otyp != SPE_NOVEL) {
 /*JP
-        Strcpy(buf, "spellbook");
+            Strcpy(buf, "spellbook");
 */
-        Strcat(buf, "\96\82\96@\8f\91");
+            Strcat(buf, "\96\82\96@\8f\91");
         } else {
 /*JP
             Strcpy(buf, !nn ? "book" : "novel");
@@ -200,7 +213,7 @@ register 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;
@@ -225,8 +238,8 @@ register 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
@@ -239,7 +252,7 @@ register int otyp;
                 Strcat(buf,
                        (ocl->oc_material == MINERAL) ? " stone" : " gem");
             if (un)
-                Sprintf(eos(buf), " called %s", un);
+                xcalled(buf, BUFSZ, "", un);
 #else
             Strcat(buf, dn ? dn : actualn);
 #endif
@@ -262,8 +275,8 @@ register int otyp;
 #endif
     }
 #if 0 /*JP*/
-    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);
 #endif
     if (dn)
 #if 0 /*JP*/
@@ -290,6 +303,28 @@ int otyp;
     return bufp;
 }
 
+/* typename for debugging feedback where data involved might be suspect */
+char *
+safe_typename(otyp)
+int otyp;
+{
+    unsigned save_nameknown;
+    char *res = 0;
+
+    if (otyp < STRANGE_OBJECT || otyp >= NUM_OBJECTS
+        || !OBJ_NAME(objects[otyp])) {
+        res = nextobuf();
+        Sprintf(res, "glorkum[%d]", otyp);
+    } else {
+        /* force it to be treated as fully discovered */
+        save_nameknown = objects[otyp].oc_name_known;
+        objects[otyp].oc_name_known = 1;
+        res = simple_typename(otyp);
+        objects[otyp].oc_name_known = save_nameknown;
+    }
+    return res;
+}
+
 boolean
 obj_is_pname(obj)
 struct obj *obj;
@@ -338,11 +373,7 @@ char *
 fruitname(juice)
 boolean juice; /* whether or not to append " juice" to the name */
 {
-#if 1 /*JP*//*\93ú\96{\8cê\82Å\82Í\82»\82±\82Ü\82Å\82µ\82È\82¢*/
-    char *buf = nextobuf();
-    Sprintf(buf, "%s%s", pl_fruit, juice ? "\83W\83\85\81[\83X" : "");
-    return buf;
-#else
+#if 0 /*JP*/
     char *buf = nextobuf();
     const char *fruit_nam = strstri(pl_fruit, " of ");
 
@@ -353,6 +384,11 @@ boolean juice; /* whether or not to append " juice" to the name */
 
     Sprintf(buf, "%s%s", makesingular(fruit_nam), juice ? " juice" : "");
     return buf;
+#else
+    /*\93ú\96{\8cê\82Å\82Í\82»\82±\82Ü\82Å\82µ\82È\82¢*/
+    char *buf = nextobuf();
+    Sprintf(buf, "%s%s", pl_fruit, juice ? "\83W\83\85\81[\83X" : "");
+    return buf;
 #endif
 }
 
@@ -485,6 +521,37 @@ boolean forward;
     }
 }
 
+/* add "<pfx> called <sfx>" to end of buf, truncating if necessary */
+STATIC_OVL void
+xcalled(buf, siz, pfx, sfx)
+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),
+/*JP
+        pfxlen = (int) (strlen(pfx) + sizeof " called " - sizeof "");
+*/
+        pfxlen = (int) (strlen(pfx) + sizeof "\82Æ\8cÄ\82Î\82ê\82é" - sizeof "");
+
+    if (pfxlen > bufsiz)
+        panic("xcalled: not enough room for prefix (%d > %d)",
+              pfxlen, bufsiz);
+
+#if 0 /*JP*/
+    Sprintf(eos(buf), "%s called %.*s", pfx, bufsiz - pfxlen, sfx);
+#else
+    {
+        int sfxlen = bufsiz - pfxlen;
+        /* \91S\8ap\82Ì\93r\92\86\82Å\90Ø\82ê\82»\82¤\82È\82Æ\82«\82É\82Í\82»\82Ì\8e\9a\82Ì\90æ\93ª\82Ü\82Å\96ß\82é */
+        sfxlen = sfxlen - offset_in_kanji(sfx, sfxlen);
+        /* \95Ï\90\94\96¼\82ð\95Ï\82¦\82é\82±\82Æ\82Í\82µ\82È\82¢\82ª\81Asfx\82ª\91O\81Apfx\82ª\8cã\82ë\82É\82È\82é */
+        Sprintf(eos(buf), "%.*s\82Æ\8cÄ\82Î\82ê\82é%s", sfxlen, sfx, pfx);
+    }
+#endif
+}
+
 char *
 xname(obj)
 struct obj *obj;
@@ -492,7 +559,7 @@ struct obj *obj;
     return xname_flags(obj, CXN_NORMAL);
 }
 
-char *
+STATIC_OVL char *
 xname_flags(obj, cxn_flags)
 register struct obj *obj;
 unsigned cxn_flags; /* bitmask of CXN_xxx values */
@@ -502,7 +569,7 @@ unsigned cxn_flags; /* bitmask of CXN_xxx values */
     register struct objclass *ocl = &objects[typ];
     int nn = ocl->oc_name_known, omndx = obj->corpsenm;
     const char *actualn = OBJ_NAME(*ocl);
-    const char *dn = OBJ_DESCR(*ocl) ? OBJ_DESCR(*ocl) : actualn;
+    const char *dn = OBJ_DESCR(*ocl);
     const char *un = ocl->oc_uname;
     boolean pluralize = (obj->quan != 1L) && !(cxn_flags & CXN_SINGULAR);
     boolean known, dknown, bknown;
@@ -510,6 +577,10 @@ unsigned cxn_flags; /* bitmask of CXN_xxx values */
     buf = nextobuf() + PREFIX; /* leave room for "17 -3 " */
     if (Role_if(PM_SAMURAI) && Japanese_item_name(typ))
         actualn = Japanese_item_name(typ);
+    /* 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;
 
     buf[0] = '\0';
     /*
@@ -521,9 +592,12 @@ unsigned cxn_flags; /* bitmask of CXN_xxx values */
     if (!nn && ocl->oc_uses_known && ocl->oc_unique)
         obj->known = 0;
     if (!Blind && !distantname)
-        obj->dknown = TRUE;
+        obj->dknown = 1;
     if (Role_if(PM_PRIEST))
-        obj->bknown = TRUE;
+        obj->bknown = 1; /* actively avoid set_bknown();
+                          * we mustn't call update_inventory() now because
+                          * it would call xname() (via doname()) recursively
+                          * and could end up clobbering all the obufs... */
 
     if (iflags.override_ID) {
         known = dknown = bknown = TRUE;
@@ -563,9 +637,9 @@ unsigned cxn_flags; /* bitmask of CXN_xxx values */
             Strcpy(buf, actualn);
         else if (un)
 /*JP
-            Sprintf(buf, "amulet called %s", un);
+            xcalled(buf, BUFSZ - PREFIX, "amulet", un);
 */
-            Sprintf(eos(buf), "%s\82Æ\8cÄ\82Î\82ê\82é\96\82\8f\9c\82¯", un);
+            xcalled(buf, BUFSZ - PREFIX, "\96\82\8f\9c\82¯", un);
         else
 /*JP
             Sprintf(buf, "%s amulet", dn);
@@ -600,24 +674,17 @@ unsigned cxn_flags; /* bitmask of CXN_xxx values */
             Strcat(buf, dn);
         else if (nn)
             Strcat(buf, actualn);
-        else if (un) {
-#if 0 /*JP*/
-            Strcat(buf, dn);
-            Strcat(buf, " called ");
-            Strcat(buf, un);
-#else
-            Strcat(buf, un);
-            Strcat(buf, "\82Æ\8cÄ\82Î\82ê\82é");
-            Strcat(buf, dn);
-#endif
-        } else
+        else if (un)
+            xcalled(buf, BUFSZ - PREFIX, dn, un);
+        else
             Strcat(buf, dn);
+
 #if 0 /*JP*/ /*\82±\82ê\82Í\8cê\8f\87\82Ì\8aÖ\8cW\82©\82ç\8fã\82Ì\95û\82Å\92è\8b`*/
-        /* If we use an() here we'd have to remember never to use */
-        /* it whenever calling doname() or xname(). */
         if (typ == FIGURINE && omndx != NON_PM) {
-            Sprintf(eos(buf), " of a%s %s",
-                    index(vowels, *mons[omndx].mname) ? "n" : "",
+            char anbuf[10]; /* [4] would be enough: 'a','n',' ','\0' */
+
+            Sprintf(eos(buf), " of %s%s",
+                    just_an(anbuf, mons[omndx].mname),
                     mons[omndx].mname);
         } else if (is_wet_towel(obj)) {
 #else
@@ -658,9 +725,9 @@ unsigned cxn_flags; /* bitmask of CXN_xxx values */
             break;
         }
 
-        if (nn)
+        if (nn) {
             Strcat(buf, actualn);
-        else if (un) {
+        else if (un) {
 #if 0 /*JP*/
             if (is_boots(obj))
                 Strcat(buf, "boots");
@@ -756,8 +823,10 @@ unsigned cxn_flags; /* bitmask of CXN_xxx values */
 #endif
         break;
     case ROCK_CLASS:
-        if (typ == STATUE && omndx != NON_PM)
+        if (typ == STATUE && omndx != NON_PM) {
 #if 0 /*JP*/
+            char anbuf[10];
+
             Sprintf(buf, "%s%s of %s%s",
                     (Role_if(PM_ARCHEOLOGIST) && (obj->spe & STATUE_HISTORIC))
                        ? "historic "
@@ -767,9 +836,7 @@ unsigned cxn_flags; /* bitmask of CXN_xxx values */
                        ? ""
                        : the_unique_pm(&mons[omndx])
                           ? "the "
-                          : index(vowels, *mons[omndx].mname)
-                             ? "an "
-                             : "a ",
+                          : just_an(anbuf, mons[omndx].mname),
                     mons[omndx].mname);
 #else
             Sprintf(eos(buf), "%s%s\82Ì%s", 
@@ -778,7 +845,7 @@ unsigned cxn_flags; /* bitmask of CXN_xxx values */
                     : "",
                     mons[obj->corpsenm].mname, actualn);
 #endif
-        else
+        else
 #if 0 /*JP*/
             Strcpy(buf, actualn);
 #else
@@ -825,11 +892,9 @@ unsigned cxn_flags; /* bitmask of CXN_xxx values */
                 Strcat(buf, actualn);
             } else {
 #if 0 /*JP*/
-                Strcat(buf, " called ");
-                Strcat(buf, un);
+                xcalled(buf, BUFSZ - PREFIX, "", un);
 #else
-                Strcat(buf, un);
-                Strcat(buf, "\82Æ\8cÄ\82Î\82ê\82é\96ò");
+                xcalled(buf, BUFSZ - PREFIX, "\96ò", un);
 #endif
             }
         } else {
@@ -857,11 +922,9 @@ unsigned cxn_flags; /* bitmask of CXN_xxx values */
             Strcat(buf, actualn);
         } else if (un) {
 #if 0 /*JP*/
-            Strcat(buf, " called ");
-            Strcat(buf, un);
+            xcalled(buf, BUFSZ - PREFIX, "", un);
 #else
-            Strcat(buf, un);
-            Strcat(buf, "\82Æ\8cÄ\82Î\82ê\82é\8aª\95¨");
+            xcalled(buf, BUFSZ - PREFIX, "\8aª\95¨", un);
 #endif
         } else if (ocl->oc_magic) {
 #if 0 /*JP*/
@@ -890,9 +953,9 @@ unsigned cxn_flags; /* bitmask of CXN_xxx values */
             Strcat(buf, actualn);
         else if (un)
 /*JP
-            Sprintf(buf, "wand called %s", un);
+            xcalled(buf, BUFSZ - PREFIX, "wand", un);
 */
-            Sprintf(eos(buf), "%s\82Æ\8cÄ\82Î\82ê\82é\8fñ", un);
+            xcalled(buf, BUFSZ - PREFIX, "\8fñ", un);
         else
 /*JP
             Sprintf(buf, "%s wand", dn);
@@ -910,9 +973,9 @@ unsigned cxn_flags; /* bitmask of CXN_xxx values */
                 Strcpy(buf, actualn);
             else if (un)
 /*JP
-                Sprintf(buf, "novel called %s", un);
+                xcalled(buf, BUFSZ - PREFIX, "novel", un);
 */
-                Sprintf(buf, "%s\82Æ\82¢\82¤\8f¬\90à", un);
+                xcalled(buf, BUFSZ - PREFIX, "\8f¬\90à", un);
             else
 /*JP
                 Sprintf(buf, "%s book", dn);
@@ -933,9 +996,9 @@ unsigned cxn_flags; /* bitmask of CXN_xxx values */
             Strcat(buf, actualn);
         } else if (un) {
 /*JP
-            Sprintf(buf, "spellbook called %s", un);
+            xcalled(buf, BUFSZ - PREFIX, "spellbook", un);
 */
-            Sprintf(eos(buf), "%s\82Æ\8cÄ\82Î\82ê\82é\96\82\96@\8f\91", un);
+            xcalled(buf, BUFSZ - PREFIX, "\96\82\96@\8f\91", un);
         } else
 /*JP
             Sprintf(buf, "%s spellbook", dn);
@@ -955,9 +1018,9 @@ unsigned cxn_flags; /* bitmask of CXN_xxx values */
             Strcat(buf, actualn);
         else if (un)
 /*JP
-            Sprintf(buf, "ring called %s", un);
+            xcalled(buf, BUFSZ - PREFIX, "ring", un);
 */
-            Sprintf(eos(buf), "%s\82Æ\8cÄ\82Î\82ê\82é\8ew\97Ö", un);
+            xcalled(buf, BUFSZ - PREFIX, "\8ew\97Ö", un);
         else
 /*JP
             Sprintf(buf, "%s ring", dn);
@@ -978,10 +1041,7 @@ unsigned cxn_flags; /* bitmask of CXN_xxx values */
 #endif
         } else if (!nn) {
             if (un)
-/*JP
-                Sprintf(buf, "%s called %s", rock, un);
-*/
-                Sprintf(eos(buf), "%s\82Æ\8cÄ\82Î\82ê\82é%s", un, rock);
+                xcalled(buf, BUFSZ - PREFIX, rock, un);
             else
 /*JP
                 Sprintf(buf, "%s %s", dn, rock);
@@ -1016,8 +1076,9 @@ unsigned cxn_flags; /* bitmask of CXN_xxx values */
 #if 0 /*JP*/
     if (has_oname(obj) && dknown) {
         Strcat(buf, " named ");
-    nameit:
-        Strcat(buf, ONAME(obj));
+ nameit:
+        (void) strncat(buf, ONAME(obj),
+                       BUFSZ - 1 - PREFIX - (unsigned) strlen(buf));
     }
 
     if (!strncmpi(buf, "the ", 4))
@@ -1042,6 +1103,9 @@ struct obj *obj;
     struct obj bareobj;
     struct objclass saveobcls;
     int otyp = obj->otyp;
+#if 1 /*JP*/
+    int l = 0;
+#endif
 
     /* suppress user-supplied name */
     saveobcls.oc_uname = objects[otyp].oc_uname;
@@ -1074,8 +1138,8 @@ struct obj *obj;
     if (!strncmp(bufp, "uncursed ", 9))
         bufp += 9; /* Role_if(PM_PRIEST) */
 #else
-    if (!strncmp(bufp, "\8eô\82í\82ê\82Ä\82¢\82È\82¢", 14))
-        bufp += 14; /* Role_if(PM_PRIEST) */
+    if (!STRNCMPEX(bufp, "\8eô\82í\82ê\82Ä\82¢\82È\82¢"))
+        bufp += l; /* Role_if(PM_PRIEST) */
 #endif
 
     objects[otyp].oc_uname = saveobcls.oc_uname;
@@ -1204,13 +1268,13 @@ char *prefix;
         Strcat(prefix, is_corrodeable(obj) ? "\95\85\90H\82µ\82½" : "\95\85\82Á\82½");
     }
     if (rknown && obj->oerodeproof)
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         Strcat(prefix, iscrys
                           ? "fixed "
                           : is_rustprone(obj)
                              ? "rustproof "
                              : is_corrodeable(obj)
-                                ? "\95\85\90H\82µ\82È\82¢" /* "stainless"? */
+                                ? "corrodeproof " /* "stainless"? */
                                 : is_flammable(obj)
                                    ? "fireproof "
                                    : "");
@@ -1262,18 +1326,20 @@ unsigned doname_flags;
 {
     boolean ispoisoned = FALSE,
             with_price = (doname_flags & DONAME_WITH_PRICE) != 0,
-            vague_quan = (doname_flags & DONAME_VAGUE_QUAN) != 0;
+            vague_quan = (doname_flags & DONAME_VAGUE_QUAN) != 0,
+            weightshown = FALSE;
     boolean known, dknown, cknown, bknown, lknown;
     int omndx = obj->corpsenm;
-    char prefix[PREFIX];
+    char prefix[PREFIX], globbuf[QBUFSZ];
 #if 0 /*JP*/
     char tmpbuf[PREFIX + 1]; /* for when we have to add something at
                                 the start of prefix instead of the
                                 end (Strcat is used on the end) */
 #endif
     register char *bp = xname(obj);
-#if 1 /*JP*//*\8f\87\8f\98\93ü\82ê\91Ö\82¦\82É\8eg\82¤*/
-    char preprefix[PREFIX];
+#if 1 /*JP*/
+    char preprefix[PREFIX]; /*\8f\87\8f\98\93ü\82ê\91Ö\82¦\82É\8eg\82¤*/
+    int l = 0;
 #endif
 
     if (iflags.override_ID) {
@@ -1292,14 +1358,14 @@ unsigned doname_flags;
      * combining both into one function taking a parameter.
      */
     /* must check opoisoned--someone can have a weirdly-named fruit */
-#if 0 /*JP*/
+#if 0 /*JP:T*/
     if (!strncmp(bp, "poisoned ", 9) && obj->opoisoned) {
         bp += 9;
         ispoisoned = TRUE;
     }
 #else
-    if (!strncmp(bp, "\93Å\82Ì\93h\82ç\82ê\82½", 12) && obj->opoisoned) {
-        bp += 12;
+    if (!STRNCMPEX(bp, "\93Å\82Ì\93h\82ç\82ê\82½") && obj->opoisoned) {
+        bp += l;
         ispoisoned = TRUE;
     }
 #endif
@@ -1441,7 +1507,7 @@ unsigned doname_flags;
         /* we count the number of separate stacks, which corresponds
            to the number of inventory slots needed to be able to take
            everything out if no merges occur */
-        long itemcount = count_contents(obj, FALSE, FALSE, TRUE);
+        long itemcount = count_contents(obj, FALSE, FALSE, TRUE, FALSE);
 
 #if 0 /*JP*/
         Sprintf(eos(bp), " containing %ld item%s", itemcount,
@@ -1460,15 +1526,36 @@ unsigned doname_flags;
             Strcat(bp, "(\90g\82É\82Â\82¯\82Ä\82¢\82é)");
         break;
     case ARMOR_CLASS:
-        if (obj->owornmask & W_ARMOR)
+        if (obj->owornmask & W_ARMOR) {
 /*JP
             Strcat(bp, (obj == uskin) ? " (embedded in your skin)"
 */
             Strcat(bp, (obj == uskin) ? "(\94§\82É\96\84\82ß\82±\82Ü\82ê\82Ä\82¢\82é)"
+                       /* in case of perm_invent update while Wear/Takeoff
+                          is in progress; check doffing() before donning()
+                          because donning() returns True for both cases */
 /*JP
-                                      : " (being worn)");
+                       : doffing(obj) ? " (being doffed)"
 */
-                                      : "(\90g\82É\82Â\82¯\82Ä\82¢\82é)");
+                       : doffing(obj) ? " (\90g\82É\82Â\82¯\82Ä\82¢\82é\93r\92\86)"
+/*JP
+                         : donning(obj) ? " (being donned)"
+*/
+                         : donning(obj) ? " (\92E\82¢\82Å\82¢\82é\93r\92\86)"
+/*JP
+                           : " (being worn)");
+*/
+                           : "(\90g\82É\82Â\82¯\82Ä\82¢\82é)");
+            /* slippery fingers is an intrinsic condition of the hero
+               rather than extrinsic condition of objects, but gloves
+               are described as slippery when hero has slippery fingers */
+            if (obj == uarmg && Glib) /* just appended "(something)",
+                                       * change to "(something; slippery)" */
+/*JP
+                Strcpy(rindex(bp, ')'), "; slippery)");
+*/
+                Strcpy(rindex(bp, ')'), "; \82Ê\82é\82Ê\82é)");
+        }
         /*FALLTHRU*/
     case WEAPON_CLASS:
         if (ispoisoned)
@@ -1497,12 +1584,12 @@ unsigned doname_flags;
                 impossible("leashed monster not on this level");
                 obj->leashmon = 0;
             } else {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                 Sprintf(eos(bp), " (attached to %s)",
-                        a_monnam(mlsh));
+                        noit_mon_nam(mlsh));
 #else
                 Sprintf(eos(bp), " (%s\82É\8c\8b\82Ñ\82Â\82¯\82ç\82ê\82Ä\82¢\82é)",
-                        a_monnam(mlsh));
+                        noit_mon_nam(mlsh));
 #endif
             }
             break;
@@ -1545,7 +1632,7 @@ unsigned doname_flags;
             goto charges;
         break;
     case WAND_CLASS:
   charges:
+ charges:
         if (known)
 /*JP
             Sprintf(eos(bp), " (%d:%d)", (int) obj->recharged, obj->spe);
@@ -1560,7 +1647,7 @@ unsigned doname_flags;
             Strcat(bp, "(\8cõ\82Á\82Ä\82¢\82é)");
         break;
     case RING_CLASS:
   ring:
+ ring:
         if (obj->owornmask & W_RINGR)
 /*JP
             Strcat(bp, " (on right ");
@@ -1611,7 +1698,7 @@ unsigned doname_flags;
 #endif
             if (omndx >= LOW_PM
                 && (known || (mvitals[omndx].mvflags & MV_KNOWS_EGG))) {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                 Strcat(prefix, mons[omndx].mname);
                 Strcat(prefix, " ");
 #else
@@ -1650,24 +1737,25 @@ unsigned doname_flags;
 
             if (bimanual(obj))
                 hand_s = makeplural(hand_s);
-/*JP
-            Sprintf(eos(bp), " (weapon in %s)", hand_s);
-*/
+            /* note: Sting's glow message, if added, will insert text
+               in front of "(weapon in hand)"'s closing paren */
+#if 0 /*JP*/
+            Sprintf(eos(bp), " (%sweapon in %s)",
+                    (obj->otyp == AKLYS) ? "tethered " : "", hand_s);
+#else /*\83A\83L\83\8a\83X\82ð\93Á\95Ê\88µ\82¢\82µ\82È\82¢*/
             Sprintf(eos(bp), "(%s\82É\82µ\82Ä\82¢\82é)", hand_s);
+#endif
 
             if (warn_obj_cnt && obj == uwep && (EWarn_of_mon & W_WEP) != 0L) {
-                /* presumably can be felt when blind */
+                if (!Blind) /* we know bp[] ends with ')'; overwrite that */
 #if 0 /*JP*/
-                Strcat(bp, " (glowing");
-                if (!Blind)
-                    Sprintf(eos(bp), " %s", glow_color(obj->oartifact));
-                Strcat(bp, ")");
-#else
-                if (Blind)
-                    Strcat(bp, " (\94M\82ð\8e\9d\82Á\82Ä\82¢\82é)");
-                else
-                    Sprintf(eos(bp), " (%s\8bP\82¢\82Ä\82¢\82é)",
+                    Sprintf(eos(bp) - 1, ", %s %s)",
+                            glow_verb(warn_obj_cnt, TRUE),
                             glow_color(obj->oartifact));
+#else
+                    Sprintf(eos(bp) - 1, ", %s%s\82¢\82é)",
+                            glow_color(obj->oartifact),
+                            jconj(glow_verb(warn_obj_cnt, TRUE), "\82Ä"));
 #endif
             }
         }
@@ -1729,40 +1817,69 @@ unsigned doname_flags;
             Strcat(bp, "(\8f\80\94õ\82µ\82Ä\82¢\82é)");
         }
     }
-    if (!iflags.suppress_price && is_unpaid(obj)) {
+    /* treat 'restoring' like suppress_price because shopkeeper and
+       bill might not be available yet while restore is in progress
+       (objects won't normally be formatted during that time, but if
+       'perm_invent' is enabled then they might be) */
+    if (iflags.suppress_price || restoring) {
+        ; /* don't attempt to obtain any stop pricing, even if 'with_price' */
+    } else if (is_unpaid(obj)) { /* in inventory or in container in invent */
         long quotedprice = unpaid_cost(obj, TRUE);
 
-#if 0 /*JP*/
-        Sprintf(eos(bp), " (%s, %ld %s)",
+#if 0 /*JP:T*/
+        Sprintf(eos(bp), " (%s, %s%ld %s)",
                 obj->unpaid ? "unpaid" : "contents",
+                globwt(obj, globbuf, &weightshown),
                 quotedprice, currency(quotedprice));
 #else
-        Sprintf(eos(bp), " (%s, %ld%s)",
+        Sprintf(eos(bp), " (%s, %s%ld%s)",
                 obj->unpaid ? "\96¢\95¥\82¢" : "\92\86\90g",
+                globwt(obj, globbuf, &weightshown),
                 quotedprice, currency(quotedprice));
 #endif
-    } else if (with_price) {
-        long price = get_cost_of_shop_item(obj);
+    } else if (with_price) { /* on floor or in container on floor */
+        int nochrg = 0;
+        long price = get_cost_of_shop_item(obj, &nochrg);
 
-        if (price > 0)
-            Sprintf(eos(bp), " (%ld %s)", price, currency(price));
+        if (price > 0L)
+#if 0 /*JP:T*/
+            Sprintf(eos(bp), " (%s, %s%ld %s)",
+                    nochrg ? "contents" : "for sale",
+                    globwt(obj, globbuf, &weightshown),
+                    price, currency(price));
+#else
+            Sprintf(eos(bp), " (%s, %s%ld%s)",
+                    nochrg ? "\92\86\90g" : "\8f¤\95i",
+                    globwt(obj, globbuf, &weightshown),
+                    price, currency(price));
+#endif
+        else if (nochrg > 0)
+#if 0 /*JP:T*/
+            Sprintf(eos(bp), " (%sno charge)",
+                    globwt(obj, globbuf, &weightshown));
+#else
+            Sprintf(eos(bp), " (%s\96³\97¿)",
+                    globwt(obj, globbuf, &weightshown));
+#endif
     }
 #if 0 /*JP*//*\93ú\96{\8cê\82Å\82Í\95s\97v*/
-    if (!strncmp(prefix, "a ", 2)
-        && index(vowels, *(prefix + 2) ? *(prefix + 2) : *bp)
-        && (*(prefix + 2)
-            || (strncmp(bp, "uranium", 7) && strncmp(bp, "unicorn", 7)
-                && strncmp(bp, "eucalyptus", 10)))) {
-        Strcpy(tmpbuf, prefix);
-        Strcpy(prefix, "an ");
-        Strcpy(prefix + 3, tmpbuf + 2);
+    if (!strncmp(prefix, "a ", 2)) {
+        /* save current prefix, without "a "; might be empty */
+        Strcpy(tmpbuf, prefix + 2);
+        /* set prefix[] to "", "a ", or "an " */
+        (void) just_an(prefix, *tmpbuf ? tmpbuf : bp);
+        /* append remainder of original prefix */
+        Strcat(prefix, tmpbuf);
     }
 #endif
 
-    /* show weight for items (debug tourist info)
-     * aum is stolen from Crawl's "Arbitrary Unit of Measure" */
+    /* show weight for items (debug tourist info);
+       "aum" is stolen from Crawl's "Arbitrary Unit of Measure" */
     if (wizard && iflags.wizweight) {
-        Sprintf(eos(bp), " (%d aum)", obj->owt);
+        /* wizard mode user has asked to see object weights;
+           globs with shop pricing attached already include it */
+        if (!weightshown)
+            Sprintf(eos(bp), " (%u aum)", obj->owt);
     }
 #if 0 /*JP*/
     bp = strprepend(bp, prefix);
@@ -1854,7 +1971,8 @@ struct obj *otmp;
 const char *adjective;
 unsigned cxn_flags; /* bitmask of CXN_xxx values */
 {
-    char *nambuf = nextobuf();
+    /* some callers [aobjnam()] rely on prefix area that xname() sets aside */
+    char *nambuf = nextobuf() + PREFIX;
     int omndx = otmp->corpsenm;
 #if 0 /*JP*/
     boolean ignore_quan = (cxn_flags & CXN_SINGULAR) != 0,
@@ -2027,8 +2145,7 @@ struct obj *obj;
 
     /* format the object */
     if (obj->otyp == CORPSE) {
-        buf = nextobuf();
-        Strcpy(buf, corpse_xname(obj, (const char *) 0, CXN_NORMAL));
+        buf = corpse_xname(obj, (const char *) 0, CXN_NORMAL);
     } else if (obj->otyp == SLIME_MOLD) {
         /* concession to "most unique deaths competition" in the annual
            devnull tournament, suppress player supplied fruit names because
@@ -2158,30 +2275,50 @@ char *FDECL((*func), (OBJ_P));
     return nam;
 }
 
+/* pick "", "a ", or "an " as article for 'str'; used by an() and doname() */
+STATIC_OVL char *
+just_an(outbuf, str)
+char *outbuf;
+const char *str;
+{
+#if 0 /*JP*//*\95s\92è\8a¥\8e\8c\82Í\95s\97v*/
+    char c0;
+
+    *outbuf = '\0';
+    c0 = lowc(*str);
+    if (!str[1]) {
+        /* single letter; might be used for named fruit */
+        Strcpy(outbuf, index("aefhilmnosx", c0) ? "an " : "a ");
+    } else if (!strncmpi(str, "the ", 4) || !strcmpi(str, "molten lava")
+               || !strcmpi(str, "iron bars") || !strcmpi(str, "ice")) {
+        ; /* no article */
+    } else {
+        if ((index(vowels, c0) && strncmpi(str, "one-", 4)
+             && strncmpi(str, "eucalyptus", 10) && strncmpi(str, "unicorn", 7)
+             && strncmpi(str, "uranium", 7) && strncmpi(str, "useful", 6))
+            || (index("x", c0) && !index(vowels, lowc(str[1]))))
+            Strcpy(outbuf, "an ");
+        else
+            Strcpy(outbuf, "a ");
+    }
+#else
+    *outbuf = '\0';
+#endif
+    return outbuf;
+}
+
 char *
 an(str)
-register const char *str;
+const char *str;
 {
     char *buf = nextobuf();
 
-#if 0 /*JP*//*\95s\92è\8a¥\8e\8c\82Í\95s\97v*/
-    buf[0] = '\0';
-
-    if (strncmpi(str, "the ", 4) && strcmp(str, "molten lava")
-        && strcmp(str, "iron bars") && strcmp(str, "ice")) {
-        if (index(vowels, *str) && strncmp(str, "one-", 4)
-            && strncmp(str, "useful", 6) && strncmp(str, "unicorn", 7)
-            && strncmp(str, "uranium", 7) && strncmp(str, "eucalyptus", 10))
-            Strcpy(buf, "an ");
-        else
-            Strcpy(buf, "a ");
+    if (!str || !*str) {
+        impossible("Alphabet soup: 'an(%s)'.", str ? "\"\"" : "<null>");
+        return strcpy(buf, "an []");
     }
-
-    Strcat(buf, str);
-#else /*\92P\82É\83R\83s\81[*/
-    Strcpy(buf, str);
-#endif
-    return buf;
+    (void) just_an(buf, str);
+    return strncat(buf, str, BUFSZ - 1 - (unsigned) strlen(buf));
 }
 
 char *
@@ -2208,6 +2345,10 @@ const char *str;
 #if 0 /*JP*//*\92è\8a¥\8e\8c\82Í\95s\97v*/
     boolean insert_the = FALSE;
 
+    if (!str || !*str) {
+        impossible("Alphabet soup: 'the(%s)'.", str ? "\"\"" : "<null>");
+        return strcpy(buf, "the []");
+    }
     if (!strncmpi(str, "the ", 4)) {
         buf[0] = lowc(*str);
         Strcpy(&buf[1], str + 1);
@@ -2248,12 +2389,11 @@ const char *str;
         Strcpy(buf, "the ");
     else
         buf[0] = '\0';
-    Strcat(buf, str);
-
+    return strncat(buf, str, BUFSZ - 1 - (unsigned) strlen(buf));
 #else /*\92P\82É\83R\83s\81[*/
     Strcpy(buf, str);
-#endif /*JP*/
     return buf;
+#endif /*JP*/
 }
 
 char *
@@ -2361,6 +2501,30 @@ struct obj *obj;
     return s;
 }
 
+#if 0 /* stalled-out work in progress */
+/* Doname2() for itemized buying of 'obj' from a shop */
+char *
+payDoname(obj)
+struct obj *obj;
+{
+    static const char and_contents[] = " and its contents";
+    char *p = doname(obj);
+
+    if (Is_container(obj) && !obj->cknown) {
+        if (obj->unpaid) {
+            if ((int) strlen(p) + sizeof and_contents - 1 < BUFSZ - PREFIX)
+                Strcat(p, and_contents);
+            *p = highc(*p);
+        } else {
+            p = strprepend(p, "Contents of ");
+        }
+    } else {
+        *p = highc(*p);
+    }
+    return p;
+}
+#endif /*0*/
+
 /* returns "[your ]xname(obj)" or "Foobar's xname(obj)" or "the xname(obj)" */
 char *
 yname(obj)
@@ -2509,6 +2673,7 @@ static const char wrpsym[] = { WAND_CLASS,   RING_CLASS,   POTION_CLASS,
                                ARMOR_CLASS,  TOOL_CLASS,   FOOD_CLASS,
                                FOOD_CLASS };
 
+#if 0 /*JP*//*\93ú\96{\8cê\82É\82Í\8eO\92P\8c»\82Ìs\82Í\82È\82¢*/
 /* return form of the verb (input plural) if xname(otmp) were the subject */
 char *
 otense(otmp, verb)
@@ -2522,10 +2687,8 @@ const char *verb;
      * if the result of xname(otmp) would be plural.  Don't bother
      * recomputing xname(otmp) at this time.
      */
-#if 0 /*JP*//*\93ú\96{\8cê\82É\82Í\8eO\92P\8c»\82Ìs\82Í\82È\82¢*/
     if (!is_plural(otmp))
         return vtense((char *) 0, verb);
-#endif /*JP*/
 
     buf = nextobuf();
     Strcpy(buf, verb);
@@ -2551,7 +2714,6 @@ vtense(subj, verb)
 register const char *subj;
 register const char *verb;
 {
-#if 0 /*JP*//*\93ú\96{\8cê\82É\82Í\8eO\92P\8c»\82Ìs\82Í\82È\82¢*/
     char *buf = nextobuf(), *bspot;
     int len, ltmp;
     const char *sp, *spot;
@@ -2617,7 +2779,7 @@ register const char *verb;
             return strcpy(buf, verb);
     }
 
-sing:
+ sing:
     Strcpy(buf, verb);
     len = (int) strlen(buf);
     bspot = buf + len - 1;
@@ -2639,14 +2801,9 @@ sing:
         Strcasecpy(bspot + 1, "s");
     }
 
-#else /*\90V\82µ\82¢\83o\83b\83t\83@\82Í\95K\97v*/
-    char *buf;
-
-    buf = nextobuf();
-    Strcpy(buf, verb);
-#endif /*JP*/
     return buf;
 }
+#endif
 
 #if 0 /*JP*/
 struct sing_plur {
@@ -2708,6 +2865,7 @@ const char *const *alt_as_is; /* another set like as_is[] */
     const struct sing_plur *sp;
     const char *same, *other, *const *as;
     int al;
+    int baselen = strlen(basestr);
 
     for (as = as_is; *as; ++as) {
         al = (int) strlen(*as);
@@ -2722,7 +2880,11 @@ const char *const *alt_as_is; /* another set like as_is[] */
         }
     }
 
-    /* avoid false hit on one_off[].plur == "lice" or .sing == "goose";
+   /* Leave "craft" as a suffix as-is (aircraft, hovercraft);
+      "craft" itself is (arguably) not included in our likely context */
+   if ((baselen > 5) && (!BSTRCMPI(basestr, endstring - 5, "craft")))
+       return TRUE;
+   /* avoid false hit on one_off[].plur == "lice" or .sing == "goose";
        if more of these turn up, one_off[] entries will need to flagged
        as to which are whole words and which are matchable as suffices
        then matching in the loop below will end up becoming more complex */
@@ -2734,20 +2896,20 @@ const char *const *alt_as_is; /* another set like as_is[] */
     }
     /* skip "ox" -> "oxen" entry when pluralizing "<something>ox"
        unless it is muskox */
-    if (to_plural && strlen(basestr) > 2 && !strcmpi(endstring - 2, "ox")
-        && strcmpi(endstring - 6, "muskox")) {
+    if (to_plural && baselen > 2 && !strcmpi(endstring - 2, "ox")
+        && !(baselen > 5 && !strcmpi(endstring - 6, "muskox"))) {
         /* "fox" -> "foxes" */
         Strcasecpy(endstring, "es");
         return TRUE;
     }
     if (to_plural) {
-        if (!strcmpi(endstring - 3, "man")
+        if (baselen > 2 && !strcmpi(endstring - 3, "man")
             && badman(basestr, to_plural)) {
             Strcasecpy(endstring, "s");
             return TRUE;
         }
     } else {
-        if (!strcmpi(endstring - 3, "men")
+        if (baselen > 2 && !strcmpi(endstring - 3, "men")
             && badman(basestr, to_plural))
             return TRUE;
     }
@@ -2952,9 +3114,25 @@ const char *oldstr;
 
     lo_c = lowc(*spot);
 
+    /* codex/spadix/neocortex and the like */
+    if (len >= 5
+        && (!strcmpi(spot - 2, "dex")
+            ||!strcmpi(spot - 2, "dix")
+            ||!strcmpi(spot - 2, "tex"))
+           /* indices would have been ok too, but stick with indexes */
+        && (strcmpi(spot - 4,"index") != 0)) {
+        Strcasecpy(spot - 1, "ices"); /* ex|ix -> ices */
+        goto bottom;
+    }
     /* Ends in z, x, s, ch, sh; add an "es" */
     if (index("zxs", lo_c)
-        || (len >= 2 && lo_c == 'h' && index("cs", lowc(*(spot - 1))))
+        || (len >= 2 && lo_c == 'h' && index("cs", lowc(*(spot - 1)))
+            /* 21st century k-sound */
+            && !(len >= 4 &&
+                 ((lowc(*(spot - 2)) == 'e'
+                    && index("mt", lowc(*(spot - 3)))) ||
+                  (lowc(*(spot - 2)) == 'o'
+                    && index("lp", lowc(*(spot - 3)))))))
         /* Kludge to get "tomatoes" and "potatoes" right */
         || (len >= 4 && !strcmpi(spot - 2, "ato"))
         || (len >= 5 && !strcmpi(spot - 4, "dingo"))) {
@@ -2969,7 +3147,7 @@ const char *oldstr;
     /* Default: append an 's' */
     Strcasecpy(spot + 1, "s");
 
-bottom:
+ bottom:
     if (excess)
         Strcat(str, excess);
 #else /*JP*//*\90V\82µ\82¢\83o\83b\83t\83@\82Í\95K\97v*/
@@ -3027,7 +3205,13 @@ const char *oldstr;
         if (p >= bp + 2 && lowc(p[-2]) == 'e') {
             if (p >= bp + 3 && lowc(p[-3]) == 'i') { /* "ies" */
                 if (!BSTRCMPI(bp, p - 7, "cookies")
-                    || !BSTRCMPI(bp, p - 4, "pies")
+                    || (!BSTRCMPI(bp, p - 4, "pies")
+                        /* avoid false match for "harpies" */
+                        && (p - 4 == bp || p[-5] == ' '))
+                    /* alternate djinni/djinn spelling; not really needed */
+                    || (!BSTRCMPI(bp, p - 6, "genies")
+                        /* avoid false match for "progenies" */
+                        && (p - 6 == bp || p[-7] == ' '))
                     || !BSTRCMPI(bp, p - 5, "mbies") /* zombie */
                     || !BSTRCMPI(bp, p - 5, "yries")) /* valkyrie */
                     goto mins;
@@ -3068,7 +3252,7 @@ const char *oldstr;
                    || (p - 4 == bp && !strcmpi(p - 4, "lens"))) {
             goto bottom;
         }
   mins:
+ mins:
         *(p - 1) = '\0'; /* drop s */
 
     } else { /* input doesn't end in 's' */
@@ -3092,7 +3276,7 @@ const char *oldstr;
         /* here we cannot find the plural suffix */
     }
 
-bottom:
+ bottom:
     /* if we stripped off a suffix (" of bar" from "foo of bar"),
        put it back now [strcat() isn't actually 100% safe here...] */
     if (excess)
@@ -3107,15 +3291,13 @@ bottom:
 }
 
 #if 0 /*JP*/
-boolean
+STATIC_OVL boolean
 badman(basestr, to_plural)
 const char *basestr;
 boolean to_plural;            /* true => makeplural, false => makesingular */
 {
-    int i, al;
-    char *endstr, *spot;
     /* these are all the prefixes for *man that don't have a *men plural */
-    const char *no_men[] = {
+    static const char *no_men[] = {
         "albu", "antihu", "anti", "ata", "auto", "bildungsro", "cai", "cay",
         "ceru", "corner", "decu", "des", "dura", "fir", "hanu", "het",
         "infrahu", "inhu", "nonhu", "otto", "out", "prehu", "protohu",
@@ -3123,17 +3305,19 @@ boolean to_plural;            /* true => makeplural, false => makesingular */
         "hu", "un", "le", "re", "so", "to", "at", "a",
     };
     /* these are all the prefixes for *men that don't have a *man singular */
-    const char *no_man[] = {
+    static const char *no_man[] = {
         "abdo", "acu", "agno", "ceru", "cogno", "cycla", "fleh", "grava",
         "hegu", "preno", "sonar", "speci", "dai", "exa", "fla", "sta", "teg",
-        "tegu", "vela", "da", "hy", "lu", "no", "nu", "ra", "ru", "se", "vi", "ya",
-        "o", "a",
+        "tegu", "vela", "da", "hy", "lu", "no", "nu", "ra", "ru", "se", "vi",
+        "ya", "o", "a",
     };
+    int i, al;
+    const char *endstr, *spot;
 
     if (!basestr || strlen(basestr) < 4)
         return FALSE;
 
-    endstr = eos((char *)basestr);
+    endstr = eos((char *) basestr);
 
     if (to_plural) {
         for (i = 0; i < SIZE(no_men); i++) {
@@ -3288,7 +3472,7 @@ STATIC_OVL NEARDATA const struct o_range o_ranges[] = {
    absence of spaces and/or hyphens (such as "pickaxe" vs "pick axe"
    vs "pick-axe") then there is no need for inclusion in this list;
    likewise for ``"of" inversions'' ("boots of speed" vs "speed boots") */
-struct alt_spellings {
+static const struct alt_spellings {
     const char *sp;
     int ob;
 } spellings[] = {
@@ -3313,6 +3497,8 @@ struct alt_spellings {
     { "eucalyptus", EUCALYPTUS_LEAF },
     { "royal jelly", LUMP_OF_ROYAL_JELLY },
     { "lembas", LEMBAS_WAFER },
+    { "cookie", FORTUNE_COOKIE },
+    { "pie", CREAM_PIE },
     { "marker", MAGIC_MARKER },
     { "hook", GRAPPLING_HOOK },
     { "grappling iron", GRAPPLING_HOOK },
@@ -3330,7 +3516,6 @@ struct alt_spellings {
     { "flintstone", FLINT },
     { (const char *) 0, 0 },
 };
-#endif
 
 STATIC_OVL short
 rnd_otyp_by_wpnskill(skill)
@@ -3338,6 +3523,7 @@ schar skill;
 {
     int i, n = 0;
     short otyp = STRANGE_OBJECT;
+
     for (i = bases[WEAPON_CLASS];
          i < NUM_OBJECTS && objects[i].oc_class == WEAPON_CLASS; i++)
         if (objects[i].oc_skill == skill) {
@@ -3354,23 +3540,33 @@ schar skill;
     }
     return otyp;
 }
+#endif
 
 STATIC_OVL short
-rnd_otyp_by_namedesc(name, oclass)
-char *name;
+rnd_otyp_by_namedesc(name, oclass, xtra_prob)
+const char *name;
 char oclass;
+int xtra_prob; /* to force 0% random generation items to also be considered */
 {
     int i, n = 0;
     short validobjs[NUM_OBJECTS];
     register const char *zn;
-    long maxprob = 0;
+    int prob, maxprob = 0;
 
-    if (!name)
+    if (!name || !*name)
         return STRANGE_OBJECT;
 
-    memset((genericptr_t) validobjs, 0, sizeof(validobjs));
+    memset((genericptr_t) validobjs, 0, sizeof validobjs);
 
-    for (i = oclass ? bases[(int)oclass] : STRANGE_OBJECT + 1;
+    /* FIXME:
+     * When this spans classes (the !oclass case), the item
+     * probabilities are not very useful because they don't take
+     * the class generation probability into account.  [If 10%
+     * of spellbooks were blank and 1% of scrolls were blank,
+     * "blank" would have 10/11 chance to yield a book even though
+     * scrolls are supposed to be much more common than books.]
+     */
+    for (i = oclass ? bases[(int) oclass] : STRANGE_OBJECT + 1;
          i < NUM_OBJECTS && (!oclass || objects[i].oc_class == oclass);
          ++i) {
         /* don't match extra descriptions (w/o real name) */
@@ -3391,22 +3587,27 @@ char oclass;
             || ((zn = objects[i].oc_uname) != 0
                 && wishymatch(name, zn, FALSE))) {
             validobjs[n++] = (short) i;
-            maxprob += (objects[i].oc_prob + 1);
+            maxprob += (objects[i].oc_prob + xtra_prob);
         }
     }
 
     if (n > 0 && maxprob) {
-        long prob = rn2(maxprob);
-
-        i = 0;
-        while (i < n - 1
-               && (prob -= (objects[validobjs[i]].oc_prob + 1)) >= 0)
-            i++;
+        prob = rn2(maxprob);
+        for (i = 0; i < n - 1; i++)
+            if ((prob -= (objects[validobjs[i]].oc_prob + xtra_prob)) < 0)
+                break;
         return validobjs[i];
     }
     return STRANGE_OBJECT;
 }
 
+int
+shiny_obj(oclass)
+char oclass;
+{
+    return (int) rnd_otyp_by_namedesc("shiny", oclass, 0);
+}
+
 /*
  * Return something wished for.  Specifying a null pointer for
  * the user request string results in a random object.  Otherwise,
@@ -3436,7 +3637,11 @@ struct obj *no_wish;
     int wetness, gsize = 0;
     struct fruit *f;
     int ftype = context.current_fruit;
+#if 0 /*JP*/
+    char fruitbuf[BUFSZ], globbuf[BUFSZ];
+#else
     char fruitbuf[BUFSZ];
+#endif
     /* Fruits may not mess up the ability to wish for real objects (since
      * you can leave a fruit in a bones file and it will be added to
      * another person's game), so they must be checked for last, after
@@ -3503,13 +3708,13 @@ struct obj *no_wish;
                 bp++;
             l = 0;
 #if 1 /*JP*//* \8cã\82É\90\94\8e\8c\82ª\82 \82é\82Æ\82«\82Í\8dí\8f\9c */
-            if(!strncmp(bp, "\8dû\82Ì", l = 4) ||
-               !strncmp(bp, "\96{\82Ì", l = 4) ||
-               !strncmp(bp, "\92\85\82Ì", l = 4) ||
-               !strncmp(bp, "\8cÂ\82Ì", l = 4) ||
-               !strncmp(bp, "\96\87\82Ì", l = 4) ||
-               !strncmp(bp, "\82Â\82Ì", l = 4) ||
-               !strncmp(bp, "\82Ì", l = 2))
+            if(!STRNCMPEX(bp, "\8dû\82Ì") ||
+               !STRNCMPEX(bp, "\96{\82Ì") ||
+               !STRNCMPEX(bp, "\92\85\82Ì") ||
+               !STRNCMPEX(bp, "\8cÂ\82Ì") ||
+               !STRNCMPEX(bp, "\96\87\82Ì") ||
+               !STRNCMPEX(bp, "\82Â\82Ì") ||
+               !STRNCMPEX(bp, "\82Ì"))
               ;
             else
               l = 0;
@@ -3522,42 +3727,42 @@ struct obj *no_wish;
             while (*bp == ' ')
                 bp++;
             l = 0;
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         } else if (!strncmpi(bp, "blessed ", l = 8)
                    || !strncmpi(bp, "holy ", l = 5)) {
 #else
-        } else if (!strncmpi(bp, "\8fj\95\9f\82³\82ê\82½", l = 10)) {
+        } else if (!STRNCMPEX(bp, "\8fj\95\9f\82³\82ê\82½")) {
 #endif
             blessed = 1;
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         } else if (!strncmpi(bp, "moist ", l = 6)
                    || !strncmpi(bp, "wet ", l = 4)) {
 #else
-        } else if (!strncmpi(bp, "\8e¼\82Á\82½", l = 6)
-                   || !strncmpi(bp, "\94G\82ê\82½", l = 6)) {
+        } else if (!STRNCMPEX(bp, "\8e¼\82Á\82½")
+                   || !STRNCMPEX(bp, "\94G\82ê\82½")) {
 #endif
-#if 0 /*JP*/
+#if 0 /*JP:T*/
             if (!strncmpi(bp, "wet ", 4))
 #else
-            if (!strncmpi(bp, "\94G\82ê\82½", 6))
+            if (!STRNCMP2(bp, "\94G\82ê\82½"))
 #endif
                 wetness = rn2(3) + 3;
             else
                 wetness = rnd(2);
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         } else if (!strncmpi(bp, "cursed ", l = 7)
                    || !strncmpi(bp, "unholy ", l = 7)) {
 #else
-        } else if (!strncmpi(bp, "\8eô\82í\82ê\82½", l = 8)) {
+        } else if (!STRNCMPEX(bp, "\8eô\82í\82ê\82½")) {
 #endif
             iscursed = 1;
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         } else if (!strncmpi(bp, "uncursed ", l = 9)) {
 #else
-        } else if (!strncmpi(bp, "\8eô\82í\82ê\82Ä\82¢\82È\82¢", l = 14)) {
+        } else if (!STRNCMPEX(bp, "\8eô\82í\82ê\82Ä\82¢\82È\82¢")) {
 #endif
             uncursed = 1;
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         } else if (!strncmpi(bp, "rustproof ", l = 10)
                    || !strncmpi(bp, "erodeproof ", l = 11)
                    || !strncmpi(bp, "corrodeproof ", l = 13)
@@ -3565,41 +3770,41 @@ struct obj *no_wish;
                    || !strncmpi(bp, "fireproof ", l = 10)
                    || !strncmpi(bp, "rotproof ", l = 9)) {
 #else
-        } else if (!strncmpi(bp, "\8eK\82Ñ\82È\82¢", l = 8)
-                   || !strncmpi(bp, "\95\85\90H\82µ\82È\82¢", l = 10)
-                   || !strncmpi(bp, "\88À\92è\82µ\82½", l = 8)
-                   || !strncmpi(bp, "\94R\82¦\82È\82¢", l = 8)) {
+        } else if (!STRNCMPEX(bp, "\8eK\82Ñ\82È\82¢")
+                   || !STRNCMPEX(bp, "\95\85\90H\82µ\82È\82¢")
+                   || !STRNCMPEX(bp, "\88À\92è\82µ\82½")
+                   || !STRNCMPEX(bp, "\94R\82¦\82È\82¢")) {
 #endif
             erodeproof = 1;
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         } else if (!strncmpi(bp, "lit ", l = 4)
                    || !strncmpi(bp, "burning ", l = 8)) {
 #else
-        } else if (!strncmpi(bp, "\8cõ\82Á\82Ä\82¢\82é", l = 10)
-                   || !strncmpi(bp, "\94R\82¦\82Ä\82¢\82é", l = 10)) {
+        } else if (!STRNCMPEX(bp, "\8cõ\82Á\82Ä\82¢\82é")
+                   || !STRNCMPEX(bp, "\94R\82¦\82Ä\82¢\82é")) {
 #endif
             islit = 1;
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         } else if (!strncmpi(bp, "unlit ", l = 6)
                    || !strncmpi(bp, "extinguished ", l = 13)) {
 #else
-        } else if (!strncmpi(bp, "\8fÁ\82¦\82Ä\82¢\82é", l = 10)) {
+        } else if (!STRNCMPEX(bp, "\8fÁ\82¦\82Ä\82¢\82é")) {
 #endif
             islit = 0;
             /* "unlabeled" and "blank" are synonymous */
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         } else if (!strncmpi(bp, "unlabeled ", l = 10)
                    || !strncmpi(bp, "unlabelled ", l = 11)
                    || !strncmpi(bp, "blank ", l = 6)) {
 #else
-        } else if (!strncmpi(bp, "\83\89\83x\83\8b\82Ì\82È\82¢", l = 12)
-                   || !strncmpi(bp, "\90^\82Á\94\92\82È", l = 8)) {
+        } else if (!STRNCMPEX(bp, "\83\89\83x\83\8b\82Ì\82È\82¢")
+                   || !STRNCMPEX(bp, "\90^\82Á\94\92\82È")) {
 #endif
             unlabeled = 1;
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         } else if (!strncmpi(bp, "poisoned ", l = 9)) {
 #else
-        } else if (!strncmpi(bp, "\93Å\82Ì\93h\82ç\82ê\82½", l = 12)) {
+        } else if (!STRNCMPEX(bp, "\93Å\82Ì\93h\82ç\82ê\82½")) {
 #endif
             ispoisoned = 1;
             /* "trapped" recognized but not honored outside wizard mode */
@@ -3610,108 +3815,121 @@ struct obj *no_wish;
         } else if (!strncmpi(bp, "untrapped ", l = 10)) {
             trapped = 2; /* not trapped */
         /* locked, unlocked, broken: box/chest lock states */
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         } else if (!strncmpi(bp, "locked ", l = 7)) {
 #else
-        } else if (!strncmpi(bp, "\8c®\82Ì\8a|\82©\82Á\82½", l = 12)) {
+        } else if (!STRNCMPEX(bp, "\8c®\82Ì\8a|\82©\82Á\82½")) {
 #endif
             locked = 1, unlocked = broken = 0;
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         } else if (!strncmpi(bp, "unlocked ", l = 9)) {
 #else
-        } else if (!strncmpi(bp, "\8c®\82Ì\8a|\82©\82Á\82Ä\82¢\82È\82¢", l = 18)) {
+        } else if (!STRNCMPEX(bp, "\8c®\82Ì\8a|\82©\82Á\82Ä\82¢\82È\82¢")) {
 #endif
             unlocked = 1, locked = broken = 0;
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         } else if (!strncmpi(bp, "broken ", l = 7)) {
 #else
-        } else if (!strncmpi(bp, "\8c®\82Ì\89ó\82ê\82½", l = 10)) {
+        } else if (!STRNCMPEX(bp, "\8c®\82Ì\89ó\82ê\82½")) {
 #endif
             broken = 1, locked = unlocked = 0;
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         } else if (!strncmpi(bp, "greased ", l = 8)) {
 #else
-        } else if (!strncmpi(bp, "\96û\82Ì\93h\82ç\82ê\82½", l = 12)
-                   || !strncmpi(bp, "\8e\89\82Ì\93h\82ç\82ê\82½", l = 12)) {
+        } else if (!STRNCMPEX(bp, "\96û\82Ì\93h\82ç\82ê\82½")
+                   || !STRNCMPEX(bp, "\8e\89\82Ì\93h\82ç\82ê\82½")) {
 #endif
             isgreased = 1;
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         } else if (!strncmpi(bp, "very ", l = 5)) {
 #else
-        } else if (!strncmpi(bp, "\82Æ\82Ä\82à", l = 6)) {
+        } else if (!STRNCMPEX(bp, "\82Æ\82Ä\82à")) {
 #endif
             /* very rusted very heavy iron ball */
             very = 1;
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         } else if (!strncmpi(bp, "thoroughly ", l = 11)) {
 #else
-        } else if (!strncmpi(bp, "\82©\82È\82è", l = 6)) {
+        } else if (!STRNCMPEX(bp, "\82©\82È\82è")) {
 #endif
             very = 2;
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         } else if (!strncmpi(bp, "rusty ", l = 6)
                    || !strncmpi(bp, "rusted ", l = 7)
                    || !strncmpi(bp, "burnt ", l = 6)
                    || !strncmpi(bp, "burned ", l = 7)) {
 #else
-        } else if (!strncmpi(bp, "\8eK\82Ñ\82½", l = 6)
-                   || !strncmpi(bp, "\94R\82¦\82½", l = 6)) {
+        } else if (!STRNCMPEX(bp, "\8eK\82Ñ\82½")
+                   || !STRNCMPEX(bp, "\94R\82¦\82½")) {
 #endif
             eroded = 1 + very;
             very = 0;
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         } else if (!strncmpi(bp, "corroded ", l = 9)
                    || !strncmpi(bp, "rotted ", l = 7)) {
 #else
-        } else if (!strncmpi(bp, "\95\85\90H\82µ\82½", l = 8)
-                   || !strncmpi(bp, "\95\85\82Á\82½", l = 6)) {
+        } else if (!STRNCMPEX(bp, "\95\85\90H\82µ\82½")
+                   || !STRNCMPEX(bp, "\95\85\82Á\82½")) {
 #endif
             eroded2 = 1 + very;
             very = 0;
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         } else if (!strncmpi(bp, "partly eaten ", l = 13)
                    || !strncmpi(bp, "partially eaten ", l = 16)) {
 #else
-        } else if (!strncmpi(bp, "\90H\82×\82©\82¯\82Ì", l = 10)) {
+        } else if (!STRNCMPEX(bp, "\90H\82×\82©\82¯\82Ì")) {
 #endif
             halfeaten = 1;
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         } else if (!strncmpi(bp, "historic ", l = 9)) {
 #else
-        } else if (!strncmpi(bp, "\97ð\8ej\93I\82È", l = 8)) {
+        } else if (!STRNCMPEX(bp, "\97ð\8ej\93I\82È")) {
 #endif
             ishistoric = 1;
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         } else if (!strncmpi(bp, "diluted ", l = 8)) {
 #else
-        } else if (!strncmpi(bp, "\94\96\82Ü\82Á\82½", l = 8)) {
+        } else if (!STRNCMPEX(bp, "\94\96\82Ü\82Á\82½")) {
 #endif
             isdiluted = 1;
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         } else if (!strncmpi(bp, "empty ", l = 6)) {
 #else
-        } else if (!strncmpi(bp, "\8bó\82Á\82Û\82Ì", l = 8)) {
+        } else if (!STRNCMPEX(bp, "\8bó\82Á\82Û\82Ì")) {
 #endif
             contents = EMPTY;
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         } else if (!strncmpi(bp, "small ", l = 6)) { /* glob sizes */
 #else
-        } else if (!strncmpi(bp, "\8f¬\82³\82¢", l = 6)) { /* glob sizes */
-#endif
+        } else if (!STRNCMPEX(bp, "\8f¬\82³\82¢")) { /* glob sizes */
+#endif
+            /* "small" might be part of monster name (mimic, if wishing
+               for its corpse) rather than prefix for glob size; when
+               used for globs, it might be either "small glob of <foo>" or
+               "small <foo> glob" and user might add 's' even though plural
+               doesn't accomplish anything because globs don't stack */
+            if (strncmpi(bp + l, "glob", 4) && !strstri(bp + l, " glob"))
+                break;
             gsize = 1;
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         } else if (!strncmpi(bp, "medium ", l = 7)) {
 #else
-        } else if (!strncmpi(bp, "\92\86\82­\82ç\82¢\82Ì", l = 10)) {
+        } else if (!STRNCMPEX(bp, "\92\86\82­\82ç\82¢\82Ì")) {
 #endif
             /* xname() doesn't display "medium" but without this
-               there'd be no way to ask for the intermediate size */
+               there'd be no way to ask for the intermediate size
+               ("glob" without size prefix yields smallest one) */
             gsize = 2;
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         } else if (!strncmpi(bp, "large ", l = 6)) {
 #else
-        } else if (!strncmpi(bp, "\91å\82«\82¢", l = 6)) {
+        } else if (!STRNCMPEX(bp, "\91å\82«\82¢")) {
 #endif
+            /* "large" might be part of monster name (dog, cat, koboold,
+               mimic) or object name (box, round shield) rather than
+               prefix for glob size */
+            if (strncmpi(bp + l, "glob", 4) && !strstri(bp + l, " glob"))
+                break;
             /* "very large " had "very " peeled off on previous iteration */
             gsize = (very != 1) ? 3 : 4;
         } else
@@ -3719,7 +3937,7 @@ struct obj *no_wish;
         bp += l;
     }
     if (!cnt)
-        cnt = 1; /* %% what with "gems" etc. ? */
+        cnt = 1; /* will be changed to 2 if makesingular() changes string */
     if (strlen(bp) > 1 && (p = rindex(bp, '(')) != 0) {
         boolean keeptrailingchars = TRUE;
 
@@ -3785,10 +4003,12 @@ struct obj *no_wish;
      */
     if ((p = strstri(bp, " named ")) != 0) {
         *p = 0;
+        /* note: if 'name' is too long, oname() will truncate it */
         name = p + 7;
     }
     if ((p = strstri(bp, " called ")) != 0) {
         *p = 0;
+        /* note: if 'un' is too long, obj lookup just won't match anything */
         un = p + 8;
 #if 0 /*JP*//*\83^\83C\83v\95Ê\82Í\82Æ\82è\82 \82¦\82¸\82µ\82È\82¢*/
         /* "helmet called telepathy" is not "helmet" (a specific type)
@@ -3847,15 +4067,28 @@ struct obj *no_wish;
      *
      * also don't let player wish for multiple globs.
      */
-    if ((p = strstri(bp, "glob of ")) != 0
+    i = (int) strlen(bp);
+    p = (char *) 0;
+    /* check for "glob", "<foo> glob", and "glob of <foo>" */
+    if (!strcmpi(bp, "glob") || !BSTRCMPI(bp, bp + i - 5, " glob")
+        || !strcmpi(bp, "globs") || !BSTRCMPI(bp, bp + i - 6, " globs")
+        || (p = strstri(bp, "glob of ")) != 0
         || (p = strstri(bp, "globs of ")) != 0) {
-        int globoffset = (*(p + 4) == 's') ? 9 : 8;
-
-        if ((mntmp = name_to_mon(p + globoffset)) >= PM_GRAY_OOZE
-            && mntmp <= PM_BLACK_PUDDING) {
-            mntmp = NON_PM; /* lie to ourselves */
-            cnt = 0;        /* force only one */
-        }
+        mntmp = name_to_mon(!p ? bp : (strstri(p, " of ") + 4));
+        /* if we didn't recognize monster type, pick a valid one at random */
+        if (mntmp == NON_PM)
+            mntmp = rn1(PM_BLACK_PUDDING - PM_GRAY_OOZE, PM_GRAY_OOZE);
+        /* construct canonical spelling in case name_to_mon() recognized a
+           variant (grey ooze) or player used inverted syntax (<foo> glob);
+           if player has given a valid monster type but not valid glob type,
+           object name lookup won't find it and wish attempt will fail */
+        Sprintf(globbuf, "glob of %s", mons[mntmp].mname);
+        bp = globbuf;
+        mntmp = NON_PM; /* not useful for "glob of <foo>" object lookup */
+        cnt = 0; /* globs don't stack */
+        oclass = FOOD_CLASS;
+        actualn = bp, dn = 0;
+        goto srch;
     } else {
         /*
          * Find corpse type using "of" (figurine of an orc, tin of orc meat)
@@ -3911,7 +4144,8 @@ struct obj *no_wish;
     {
         /*JP \81u(\89ö\95¨\96¼)\82Ì\89ò\81v\82Í\8cÂ\81X\82ÉID\82ª\82 \82é\82Ì\82Å\95Ê\88µ\82¢ */
         int l = strlen(bp);
-        if (l > 4 && strncmp(bp + l - 4, "\82Ì\89ò", 4) == 0) {
+        int l2 = strlen("\82Ì\89ò");
+        if (l > 4 && strncmp(bp + l - l2, "\82Ì\89ò", l2) == 0) {
             if ((mntmp = name_to_mon(bp)) >= PM_GRAY_OOZE
                 && mntmp <= PM_BLACK_PUDDING) {
                 mntmp = NON_PM; /* lie to ourselves */
@@ -3931,6 +4165,7 @@ struct obj *no_wish;
     /* first change to singular if necessary */
     if (*bp) {
         char *sng = makesingular(bp);
+
         if (strcmp(bp, sng)) {
             if (cnt == 1)
                 cnt = 2;
@@ -3942,7 +4177,7 @@ struct obj *no_wish;
 #if 0 /*JP*//*\83X\83y\83\8b\97h\82ê\8f\88\97\9d\82Í\82µ\82È\82¢*/
     /* Alternate spellings (pick-ax, silver sabre, &c) */
     {
-        struct alt_spellings *as = spellings;
+        const struct alt_spellings *as = spellings;
 
         while (as->sp) {
             if (fuzzymatch(bp, as->sp, " -", TRUE)) {
@@ -4027,6 +4262,11 @@ struct obj *no_wish;
         typ = SPE_BLANK_PAPER;
         goto typfnd;
     }
+    /* specific food rather than color of gem/potion/spellbook[/scales] */
+    if (!BSTRCMPI(bp, p - 6, "orange") && mntmp == NON_PM) {
+        typ = ORANGE;
+        goto typfnd;
+    }
     /*
      * NOTE: Gold pieces are handled as objects nowadays, and therefore
      * this section should probably be reconsidered as well as the entire
@@ -4080,6 +4320,7 @@ struct obj *no_wish;
         for (i = 0; i < (int) (sizeof wrpsym); i++) {
             register int j = strlen(wrp[i]);
 
+            /* check for "<class> [ of ] something" */
             if (!strncmpi(bp, wrp[i], j)) {
                 oclass = wrpsym[i];
                 if (oclass != AMULET_CLASS) {
@@ -4091,12 +4332,27 @@ struct obj *no_wish;
                     actualn = bp;
                 goto srch;
             }
+            /* check for "something <class>" */
             if (!BSTRCMPI(bp, p - j, wrp[i])) {
                 oclass = wrpsym[i];
-                p -= j;
-                *p = 0;
-                if (p > bp && p[-1] == ' ')
-                    p[-1] = 0;
+                /* for "foo amulet", leave the class name so that
+                   wishymatch() can do "of inversion" to try matching
+                   "amulet of foo"; other classes don't include their
+                   class name in their full object names (where
+                   "potion of healing" is just "healing", for instance) */
+                if (oclass != AMULET_CLASS) {
+                    p -= j;
+                    *p = '\0';
+                    if (p > bp && p[-1] == ' ')
+                        p[-1] = '\0';
+                } else {
+                    /* amulet without "of"; convoluted wording but better a
+                       special case that's handled than one that's missing */
+                    if (!strncmpi(bp, "versus poison ", 14)) {
+                        typ = AMULET_VERSUS_POISON;
+                        goto typfnd;
+                    }
+                }
                 actualn = dn = bp;
                 goto srch;
             }
@@ -4145,7 +4401,7 @@ struct obj *no_wish;
     }
 
 #if 0 /*JP*//* mail/armor\8aÖ\98A\82Å\82Ì\82Ý\8eg\82¤\83\89\83x\83\8b */
-retry:
+ retry:
 #endif
 #if 0 /*JP*//* \83^\83C\83v\95Ê\82Í\82Æ\82è\82 \82¦\82¸\82µ\82È\82¢ */
     /* "grey stone" check must be before general "stone" */
@@ -4200,7 +4456,7 @@ retry:
     if (!dn)
         dn = actualn; /* ex. "skull cap" */
 #if 0 /*JP*/
-srch:
+ srch:
 #endif
     /* check real names of gems first */
     if (!oclass && actualn) {
@@ -4222,10 +4478,10 @@ srch:
 #endif
     }
 
-    if (((typ = rnd_otyp_by_namedesc(actualn, oclass)) != STRANGE_OBJECT)
-        || ((typ = rnd_otyp_by_namedesc(dn, oclass)) != STRANGE_OBJECT)
-        || ((typ = rnd_otyp_by_namedesc(un, oclass)) != STRANGE_OBJECT)
-        || ((typ = rnd_otyp_by_namedesc(origbp, oclass)) != STRANGE_OBJECT))
+    if (((typ = rnd_otyp_by_namedesc(actualn, oclass, 1)) != STRANGE_OBJECT)
+        || ((typ = rnd_otyp_by_namedesc(dn, oclass, 1)) != STRANGE_OBJECT)
+        || ((typ = rnd_otyp_by_namedesc(un, oclass, 1)) != STRANGE_OBJECT)
+        || ((typ = rnd_otyp_by_namedesc(origbp, oclass, 1)) != STRANGE_OBJECT))
         goto typfnd;
     typ = 0;
 
@@ -4340,14 +4596,17 @@ srch:
             goto typfnd;
         }
     }
-/* Let wizards wish for traps and furniture.
- * Must come after objects check so wizards can still wish for
- * trap objects like beartraps.
- * Disallow such topology tweaks for WIZKIT startup wishes.
- */
-wiztrap:
+
+    /*
+     * Let wizards wish for traps and furniture.
+     * Must come after objects check so wizards can still wish for
+     * trap objects like beartraps.
+     * Disallow such topology tweaks for WIZKIT startup wishes.
+     */
+ wiztrap:
     if (wizard && !program_state.wizkit_wishing) {
         struct rm *lev;
+        boolean madeterrain = FALSE;
         int trap, x = u.ux, y = u.uy;
 
         for (trap = NO_TRAP + 1; trap < TRAPNUM; trap++) {
@@ -4358,7 +4617,7 @@ wiztrap:
             if (strncmpi(tname, bp, strlen(tname)))
                 continue;
             /* found it; avoid stupid mistakes */
-            if ((trap == TRAPDOOR || trap == HOLE) && !Can_fall_thru(&u.uz))
+            if (is_hole(trap) && !Can_fall_thru(&u.uz))
                 trap = ROCKTRAP;
             if ((t = maketrap(x, y, trap)) != 0) {
                 trap = t->ttyp;
@@ -4367,10 +4626,11 @@ wiztrap:
                       (trap != MAGIC_PORTAL) ? "" : " to nowhere");
             } else
                 pline("Creation of %s failed.", an(tname));
-            return &zeroobj;
+            return (struct obj *) &zeroobj;
         }
 
-        /* furniture and terrain */
+        /* furniture and terrain (use at your own risk; can clobber stairs
+           or place furniture on existing traps which shouldn't be allowed) */
         lev = &levl[x][y];
         p = eos(bp);
         if (!BSTRCMPI(bp, p - 8, "fountain")) {
@@ -4379,43 +4639,36 @@ wiztrap:
             if (!strncmpi(bp, "magic ", 6))
                 lev->blessedftn = 1;
             pline("A %sfountain.", lev->blessedftn ? "magic " : "");
-            newsym(x, y);
-            return &zeroobj;
-        }
-        if (!BSTRCMPI(bp, p - 6, "throne")) {
+            madeterrain = TRUE;
+        } else if (!BSTRCMPI(bp, p - 6, "throne")) {
             lev->typ = THRONE;
             pline("A throne.");
-            newsym(x, y);
-            return &zeroobj;
-        }
-        if (!BSTRCMPI(bp, p - 4, "sink")) {
+            madeterrain = TRUE;
+        } else if (!BSTRCMPI(bp, p - 4, "sink")) {
             lev->typ = SINK;
             level.flags.nsinks++;
             pline("A sink.");
-            newsym(x, y);
-            return &zeroobj;
-        }
+            madeterrain = TRUE;
+
         /* ("water" matches "potion of water" rather than terrain) */
-        if (!BSTRCMPI(bp, p - 4, "pool") || !BSTRCMPI(bp, p - 4, "moat")) {
+        } else if (!BSTRCMPI(bp, p - 4, "pool")
+                   || !BSTRCMPI(bp, p - 4, "moat")) {
             lev->typ = !BSTRCMPI(bp, p - 4, "pool") ? POOL : MOAT;
             del_engr_at(x, y);
             pline("A %s.", (lev->typ == POOL) ? "pool" : "moat");
             /* Must manually make kelp! */
             water_damage_chain(level.objects[x][y], TRUE);
-            newsym(x, y);
-            return &zeroobj;
-        }
-        if (!BSTRCMPI(bp, p - 4, "lava")) { /* also matches "molten lava" */
+            madeterrain = TRUE;
+
+        /* also matches "molten lava" */
+        } else if (!BSTRCMPI(bp, p - 4, "lava")) {
             lev->typ = LAVAPOOL;
             del_engr_at(x, y);
             pline("A pool of molten lava.");
             if (!(Levitation || Flying))
-                (void) lava_effects();
-            newsym(x, y);
-            return &zeroobj;
-        }
-
-        if (!BSTRCMPI(bp, p - 5, "altar")) {
+                pooleffects(FALSE);
+            madeterrain = TRUE;
+        } else if (!BSTRCMPI(bp, p - 5, "altar")) {
             aligntyp al;
 
             lev->typ = ALTAR;
@@ -4428,37 +4681,43 @@ wiztrap:
             else if (!strncmpi(bp, "unaligned ", 10))
                 al = A_NONE;
             else /* -1 - A_CHAOTIC, 0 - A_NEUTRAL, 1 - A_LAWFUL */
-                al = (!rn2(6)) ? A_NONE : rn2((int) A_LAWFUL + 2) - 1;
+                al = !rn2(6) ? A_NONE : (rn2((int) A_LAWFUL + 2) - 1);
             lev->altarmask = Align2amask(al);
             pline("%s altar.", An(align_str(al)));
-            newsym(x, y);
-            return &zeroobj;
-        }
-
-        if (!BSTRCMPI(bp, p - 5, "grave")
-            || !BSTRCMPI(bp, p - 9, "headstone")) {
+            madeterrain = TRUE;
+        } else if (!BSTRCMPI(bp, p - 5, "grave")
+                   || !BSTRCMPI(bp, p - 9, "headstone")) {
             make_grave(x, y, (char *) 0);
             pline("%s.", IS_GRAVE(lev->typ) ? "A grave"
                                             : "Can't place a grave here");
-            newsym(x, y);
-            return &zeroobj;
-        }
-
-        if (!BSTRCMPI(bp, p - 4, "tree")) {
+            madeterrain = TRUE;
+        } else if (!BSTRCMPI(bp, p - 4, "tree")) {
             lev->typ = TREE;
             pline("A tree.");
-            newsym(x, y);
             block_point(x, y);
-            return &zeroobj;
-        }
-
-        if (!BSTRCMPI(bp, p - 4, "bars")) {
+            madeterrain = TRUE;
+        } else if (!BSTRCMPI(bp, p - 4, "bars")) {
             lev->typ = IRONBARS;
             pline("Iron bars.");
-            newsym(x, y);
-            return &zeroobj;
+            madeterrain = TRUE;
         }
-    }
+
+        if (madeterrain) {
+            feel_newsym(x, y); /* map the spot where the wish occurred */
+            /* hero started at <x,y> but might not be there anymore (create
+               lava, decline to die, and get teleported away to safety) */
+            if (u.uinwater && !is_pool(u.ux, u.uy)) {
+                u.uinwater = 0; /* leave the water */
+                docrt();
+                vision_full_recalc = 1;
+            } else if (u.utrap && u.utraptype == TT_LAVA
+                       && !is_lava(u.ux, u.uy)) {
+                reset_utrap(FALSE);
+            }
+            /* cast 'const' away; caller won't modify this */
+            return (struct obj *) &zeroobj;
+        }
+    } /* end of wizard mode traps and terrain */
 
 #if 0 /*JP*//* \83^\83C\83v\95Ê\82Í\82Æ\82è\82 \82¦\82¸\82µ\82È\82¢ */
     if (!oclass && !typ) {
@@ -4474,10 +4733,10 @@ wiztrap:
 
     if (!oclass)
         return ((struct obj *) 0);
-any:
+ any:
     if (!oclass)
-        oclass = wrpsym[rn2((int) sizeof(wrpsym))];
-typfnd:
+        oclass = wrpsym[rn2((int) sizeof wrpsym)];
+ typfnd:
     if (typ)
         oclass = objects[typ].oc_class;
 
@@ -4767,12 +5026,13 @@ typfnd:
          || (otmp->oartifact && rn2(nartifact_exist()) > 1)) && !wizard) {
         artifact_exists(otmp, safe_oname(otmp), FALSE);
         obfree(otmp, (struct obj *) 0);
-        otmp = &zeroobj;
+        otmp = (struct obj *) &zeroobj;
 /*JP
         pline("For a moment, you feel %s in your %s, but it disappears!",
 */
         pline("\88ê\8fu%s\82ª%s\82Ì\92\86\82É\82 \82é\82æ\82¤\82È\8a´\82\82ª\82µ\82½\82ª\81C\82·\82®\82É\8fÁ\82¦\82³\82Á\82½\81I",
               something, makeplural(body_part(HAND)));
+        return otmp;
     }
 
     if (halfeaten && otmp->oclass == FOOD_CLASS) {
@@ -4833,31 +5093,33 @@ struct obj *suit;
 {
     const char *suitnm, *esuitp;
 
-    if (Is_dragon_mail(suit))
-#if 0 /*JP*/
-        return "dragon mail"; /* <color> dragon scale mail */
+    if (suit) {
+        if (Is_dragon_mail(suit))
+#if 0 /*JP:T*/
+            return "dragon mail"; /* <color> dragon scale mail */
 #else
-        return "\97Ø\8aZ"; /* <color> dragon scale mail */
+            return "\97Ø\8aZ"; /* <color> dragon scale mail */
 #endif
-    else if (Is_dragon_scales(suit))
+        else if (Is_dragon_scales(suit))
 /*JP
-        return "dragon scales";
+            return "dragon scales";
 */
-        return "\97Ø";
-    suitnm = OBJ_NAME(objects[suit->otyp]);
-    esuitp = eos((char *) suitnm);
-#if 0 /*JP*/
-    if (strlen(suitnm) > 5 && !strcmp(esuitp - 5, " mail"))
-        return "mail"; /* most suits fall into this category */
+            return "\97Ø";
+        suitnm = OBJ_NAME(objects[suit->otyp]);
+        esuitp = eos((char *) suitnm);
+#if 0 /*JP:T*/
+        if (strlen(suitnm) > 5 && !strcmp(esuitp - 5, " mail"))
+            return "mail"; /* most suits fall into this category */
 #else
-    if (strlen(suitnm) > 2 && !strcmp(esuitp - 2, "\8aZ"))
-        return "\8aZ"; /* most suits fall into this category */
+        if (strlen(suitnm) > 2 && !strcmp(esuitp - 2, "\8aZ"))
+            return "\8aZ"; /* most suits fall into this category */
 #endif
 #if 0 /*JP*/
-    else if (strlen(suitnm) > 7 && !strcmp(esuitp - 7, " jacket"))
-        return "jacket"; /* leather jacket */
+        else if (strlen(suitnm) > 7 && !strcmp(esuitp - 7, " jacket"))
+            return "jacket"; /* leather jacket */
 #endif
-    /* suit is lame but armor is ambiguous and body armor is absurd */
+    }
+    /* "suit" is lame but "armor" is ambiguous and "body armor" is absurd */
 /*JP
     return "suit";
 */
@@ -4923,11 +5185,37 @@ struct obj *helmet;
     return (helmet && !is_metallic(helmet)) ? "\96X\8eq" : "\8a\95";
 }
 
+/* gloves vs gauntlets; depends upon discovery state */
+const char *
+gloves_simple_name(gloves)
+struct obj *gloves;
+{
+/*JP
+    static const char gauntlets[] = "gauntlets";
+*/
+    static const char gauntlets[] = "\8f¬\8eè";
+
+    if (gloves && gloves->dknown) {
+        int otyp = gloves->otyp;
+        struct objclass *ocl = &objects[otyp];
+        const char *actualn = OBJ_NAME(*ocl),
+                   *descrpn = OBJ_DESCR(*ocl);
+
+        if (strstri(objects[otyp].oc_name_known ? actualn : descrpn,
+                    gauntlets))
+            return gauntlets;
+    }
+/*JP
+    return "gloves";
+*/
+    return "\8eè\91Ü";
+}
+
 const char *
 mimic_obj_name(mtmp)
 struct monst *mtmp;
 {
-    if (mtmp->m_ap_type == M_AP_OBJECT) {
+    if (M_AP_TYPE(mtmp) == M_AP_OBJECT) {
         if (mtmp->mappearance == GOLD_PIECE)
 /*JP
             return "gold";
@@ -5022,4 +5310,20 @@ const char *lastR;
     return qbuf;
 }
 
+STATIC_OVL char *
+globwt(otmp, buf, weightformatted_p)
+struct obj *otmp;
+char *buf;
+boolean *weightformatted_p;
+{
+    *buf = '\0';
+    if (otmp->globby) {
+        Sprintf(buf, "%u aum, ", otmp->owt);
+        *weightformatted_p = TRUE;
+    } else {
+        *weightformatted_p = FALSE;
+    }
+    return buf;
+}
+
 /*objnam.c*/