]> granicus.if.org Git - nethack/commitdiff
more plural/singular (trunk only)
authornethack.rankin <nethack.rankin>
Thu, 3 May 2007 01:17:46 +0000 (01:17 +0000)
committernethack.rankin <nethack.rankin>
Thu, 3 May 2007 01:17:46 +0000 (01:17 +0000)
     Extend makeplural/makesingular case-insensitivity to vtense() and to
wizard mode wishing for dungeon features.  And the previous set of fixes
missed one:  makesingular("zombies") was producing "zomby".  Plus a bit of
groundwork for a likely second overhaul of makeplural/makesingular.

include/extern.h
src/hacklib.c
src/objnam.c

index 67df9d746ae64da5fde463556f642da6e319c609..28d4acc978fe396d14e453344c8310d790cd6dd4 100644 (file)
@@ -812,7 +812,8 @@ E char *FDECL(mungspaces, (char *));
 E char *FDECL(eos, (char *));
 E char *FDECL(strkitten, (char *,CHAR_P));
 E void FDECL(copynchars, (char *,const char *,int));
-E void FDECL(Strcasecpy, (char *,const char *));
+E char FDECL(chrcasecpy, (int,int));
+E char *FDECL(strcasecpy, (char *,const char *));
 E char *FDECL(s_suffix, (const char *));
 E char *FDECL(xcrypt, (const char *,char *));
 E boolean FDECL(onlyspace, (const char *));
index 8c5234152ec14a7957ed9144e92c0ace0842a227..7a0128fc9f60412301e54d64464bad810c1285f2 100644 (file)
@@ -20,7 +20,8 @@ NetHack, except that rounddiv may call panic().
        char *          eos             (char *)
        char *          strkitten       (char *,char)
        void            copynchars      (char *,const char *,int)
-       void            Strcasecpy      (char *,const char *)
+       char            chrcasecpy      (int,int)
+       char *          strcasecpy      (char *,const char *)
        char *          s_suffix        (const char *)
        char *          xcrypt          (const char *, char *)
        boolean         onlyspace       (const char *)
@@ -157,19 +158,33 @@ copynchars(dst, src, n)           /* truncating string copy */
     *dst = '\0';
 }
 
-/* this assumes ASCII; someday we'll have to switch over to <ctype.h> */
-#define nh_islower(c) ((c) >= 'a' && (c) <= 'z')
-#define nh_isupper(c) ((c) >= 'A' && (c) <= 'Z')
-#define nh_tolower(c) lowc(c)
-#define nh_toupper(c) highc(c)
+/* mostly used by strcasecpy */
+char
+chrcasecpy(oc, nc)     /* convert char nc into oc's case */
+    int oc, nc;
+{
+#if 0  /* this will be necessary if we switch to <ctype.h> */
+    oc = (int)(unsigned char)oc;
+    nc = (int)(unsigned char)nc;
+#endif
+    if ('a' <= oc && oc <= 'z') {
+       /* old char is lower case; if new char is upper case, downcase it */
+       if ('A' <= nc && nc <= 'Z') nc += 'a' - 'A';    /* lowc(nc) */
+    } else if ('A' <= oc && oc <= 'Z') {
+       /* old char is upper case; if new char is lower case, upcase it */
+       if ('a' <= nc && nc <= 'z') nc += 'A' - 'a';    /* highc(nc) */
+    }
+    return (char)nc;
+}
 
 /* for case-insensitive editions of makeplural() and makesingular();
    src might be shorter, same length, or longer than dst */
-void
-Strcasecpy(dst, src)   /* overwrite string, preserving old chars' case */
+char *
+strcasecpy(dst, src)   /* overwrite string, preserving old chars' case */
     char *dst;
     const char *src;
 {
+    char *result = dst;
     int ic, oc, dst_exhausted = 0;
 
     /* while dst has characters, replace each one with corresponding
@@ -177,27 +192,15 @@ Strcasecpy(dst, src)      /* overwrite string, preserving old chars' case */
        once dst runs out, propagate the case of its last character to any
        remaining src; if dst starts empty, it must be a pointer to the
        tail of some other string because we examine the char at dst[-1] */
-    while ((ic = (int)(unsigned char)*src++) != '\0') {
+    while ((ic = (int)*src++) != '\0') {
        if (!dst_exhausted && !*dst) dst_exhausted = 1;
-       /* fetch old character */
-       oc = (int)(unsigned char)*(dst - dst_exhausted);
-       /* possibly convert new character */
-       if (nh_islower(oc)) {   /* the usual case... */
-           if (nh_isupper(ic)) ic = nh_tolower(ic);
-       } else if (nh_isupper(oc)) {
-           if (nh_islower(ic)) ic = nh_toupper(ic);
-       }
-       /* store new character */
-       *dst++ = (char)ic;
+       oc = (int)*(dst - dst_exhausted);
+       *dst++ = chrcasecpy(oc, ic);
     }
     *dst = '\0';
+    return result;
 }
 
-#undef nh_islower
-#undef nh_isupper
-#undef nh_tolower
-#undef nh_toupper
-
 char *
 s_suffix(s)            /* return a name converted to possessive */
     const char *s;
index 6260885590086aa2c42449aadaa136140682ea01..4237ea169d96d47803c79535534643a97615a3e6 100644 (file)
@@ -21,6 +21,10 @@ struct Jitem {
        const char *name;
 };
 
+#define BSTRCMPI(base,ptr,str)     ((ptr) < base || strcmpi((ptr),str))
+#define BSTRNCMPI(base,ptr,str,num) ((ptr) < base || strncmpi((ptr),str,num))
+#define Strcasecpy(dst,src)        (void)strcasecpy(dst,src)
+
 /* true for gems/rocks that should have " stone" appended to their names */
 #define GemStone(typ)  (typ == FLINT ||                                \
                         (objects[typ].oc_material == GEMSTONE &&       \
@@ -1386,7 +1390,7 @@ vtense(subj, verb)
 register const char *subj;
 register const char *verb;
 {
-       char *buf = nextobuf();
+       char *buf = nextobuf(), *bspot;
        int len, ltmp;
        const char *sp, *spot;
        const char * const *spec;
@@ -1406,11 +1410,11 @@ register const char *verb;
                goto sing;
            spot = (const char *)0;
            for (sp = subj; (sp = index(sp, ' ')) != 0; ++sp) {
-               if (!strncmp(sp, " of ", 4) ||
-                   !strncmp(sp, " from ", 6) ||
-                   !strncmp(sp, " called ", 8) ||
-                   !strncmp(sp, " named ", 7) ||
-                   !strncmp(sp, " labeled ", 9)) {
+               if (!strncmpi(sp, " of ", 4) ||
+                   !strncmpi(sp, " from ", 6) ||
+                   !strncmpi(sp, " called ", 8) ||
+                   !strncmpi(sp, " named ", 7) ||
+                   !strncmpi(sp, " labeled ", 9)) {
                    if (sp != subj) spot = sp - 1;
                    break;
                }
@@ -1422,12 +1426,12 @@ register const char *verb;
             * plural: anything that ends in 's', but not '*us' or '*ss'.
             * Guess at a few other special cases that makeplural creates.
             */
-           if ((*spot == 's' && spot != subj &&
-                       (*(spot-1) != 'u' && *(spot-1) != 's')) ||
-               ((spot - subj) >= 4 && !strncmp(spot-3, "eeth", 4)) ||
-               ((spot - subj) >= 3 && !strncmp(spot-3, "feet", 4)) ||
-               ((spot - subj) >= 2 && !strncmp(spot-1, "ia", 2)) ||
-               ((spot - subj) >= 2 && !strncmp(spot-1, "ae", 2))) {
+           if ((lowc(*spot) == 's' && spot != subj &&
+                       !index("us", lowc(*(spot-1)))) ||
+               BSTRNCMPI(subj, spot-3, "eeth", 4) ||
+               BSTRNCMPI(subj, spot-3, "feet", 4) ||
+               BSTRNCMPI(subj, spot-1, "ia", 2) ||
+               BSTRNCMPI(subj, spot-1, "ae", 2)) {
                /* check for special cases to avoid false matches */
                len = (int)(spot - subj) + 1;
                for (spec = special_subjs; *spec; spec++) {
@@ -1450,26 +1454,25 @@ register const char *verb;
        }
 
  sing:
-       len = strlen(verb);
-       spot = verb + len - 1;
-
-       if (!strcmp(verb, "are"))
-           Strcpy(buf, "is");
-       else if (!strcmp(verb, "have"))
-           Strcpy(buf, "has");
-       else if (index("zxs", *spot) ||
-                (len >= 2 && *spot=='h' && index("cs", *(spot-1))) ||
-                (len == 2 && *spot == 'o')) {
+       Strcpy(buf, verb);
+       len = (int)strlen(buf);
+       bspot = buf + len - 1;
+
+       if (!strcmpi(buf, "are")) {
+           Strcasecpy(buf, "is");
+       } else if (!strcmpi(buf, "have")) {
+           Strcasecpy(bspot-1, "s");
+       } else if (index("zxs", lowc(*bspot)) ||
+                (len >= 2 && lowc(*bspot) == 'h' &&
+                       index("cs", lowc(*(bspot-1)))) ||
+                (len == 2 && lowc(*bspot) == 'o')) {
            /* Ends in z, x, s, ch, sh; add an "es" */
-           Strcpy(buf, verb);
-           Strcat(buf, "es");
-       } else if (*spot == 'y' && (!index(vowels, *(spot-1)))) {
+           Strcasecpy(bspot+1, "es");
+       } else if (lowc(*bspot) == 'y' && !index(vowels, lowc(*(bspot-1)))) {
            /* like "y" case in makeplural */
-           Strcpy(buf, verb);
-           Strcpy(buf + len - 1, "ies");
+           Strcasecpy(bspot, "ies");
        } else {
-           Strcpy(buf, verb);
-           Strcat(buf, "s");
+           Strcasecpy(bspot+1, "s");
        }
 
        return buf;
@@ -1895,11 +1898,6 @@ STATIC_OVL NEARDATA const struct o_range o_ranges[] = {
        { "grey stone", GEM_CLASS,    LUCKSTONE,      FLINT },
 };
 
-#define BSTRCMP(base,ptr,string) ((ptr) < base || strcmp((ptr),string))
-#define BSTRCMPI(base,ptr,string) ((ptr) < base || strcmpi((ptr),string))
-#define BSTRNCMP(base,ptr,string,num) ((ptr)<base || strncmp((ptr),string,num))
-#define BSTRNCMPI(base,ptr,string,num) ((ptr)<base||strncmpi((ptr),string,num))
-
 /*
  * Singularize a string the user typed in; this helps reduce the complexity
  * of readobjnam, and is also used in pager.c to singularize the string
@@ -1936,11 +1934,7 @@ const char *oldstr;
                (p = strstri(bp, "-in-")) != 0 ||
                (p = strstri(bp, "-at-")) != 0) {
            /* [wo]men-at-arms -> [wo]man-at-arms; takes "not end in s" exit */
-           if (!BSTRNCMPI(bp, p-3, "men", 3)) {
-               char c = *p;    /* Strcasecpy will clobber *p with '\0' */
-               Strcasecpy(p-2, "an");
-               *p = c;
-           }
+           if (!BSTRNCMPI(bp, p-3, "men", 3)) p[-2] = chrcasecpy(p[-2], 'a');
            if (BSTRNCMPI(bp, p-1, "s", 1)) return bp;  /* wasn't plural */
 
            --p;                /* back up to the 's' */
@@ -1959,6 +1953,7 @@ const char *oldstr;
                        if (p >= bp+3 && lowc(p[-3]) == 'i') {  /* "ies" */
                                if (!BSTRCMPI(bp, p-7, "cookies") ||
                                    !BSTRCMPI(bp, p-4, "pies") ||
+                                   !BSTRCMPI(bp, p-5, "mbies") || /* zombie */
                                    !BSTRCMPI(bp, p-5, "yries")) /* valkyrie */
                                        goto mins;
                                Strcasecpy(p-3, "y");   /* ies -> y */
@@ -2826,7 +2821,7 @@ srch:
                /* furniture and terrain */
                lev = &levl[x][y];
                p = eos(bp);
-               if(!BSTRCMP(bp, p-8, "fountain")) {
+               if (!BSTRCMPI(bp, p-8, "fountain")) {
                        lev->typ = FOUNTAIN;
                        level.flags.nfountains++;
                        if(!strncmpi(bp, "magic ", 6)) lev->blessedftn = 1;
@@ -2834,14 +2829,14 @@ srch:
                        newsym(x, y);
                        return(&zeroobj);
                }
-               if(!BSTRCMP(bp, p-6, "throne")) {
+               if (!BSTRCMPI(bp, p-6, "throne")) {
                        lev->typ = THRONE;
                        pline("A throne.");
                        newsym(x, y);
                        return(&zeroobj);
                }
 # ifdef SINKS
-               if(!BSTRCMP(bp, p-4, "sink")) {
+               if (!BSTRCMPI(bp, p-4, "sink")) {
                        lev->typ = SINK;
                        level.flags.nsinks++;
                        pline("A sink.");
@@ -2850,8 +2845,8 @@ srch:
                }
 # endif
                /* ("water" matches "potion of water" rather than terrain) */
-               if (!BSTRCMP(bp, p-4, "pool") || !BSTRCMP(bp, p-4, "moat")) {
-                       lev->typ = !BSTRCMP(bp, p-4, "pool") ? POOL : MOAT;
+               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! */
@@ -2859,7 +2854,7 @@ srch:
                        newsym(x, y);
                        return &zeroobj;
                }
-               if (!BSTRCMP(bp, p-4, "lava")) {  /* also matches "molten lava" */
+               if (!BSTRCMPI(bp, p-4, "lava")) { /* also matches "molten lava" */
                        lev->typ = LAVAPOOL;
                        del_engr_at(x, y);
                        pline("A pool of molten lava.");
@@ -2868,7 +2863,7 @@ srch:
                        return &zeroobj;
                }
 
-               if(!BSTRCMP(bp, p-5, "altar")) {
+               if (!BSTRCMPI(bp, p-5, "altar")) {
                    aligntyp al;
 
                    lev->typ = ALTAR;
@@ -2888,7 +2883,8 @@ srch:
                    return(&zeroobj);
                }
 
-               if(!BSTRCMP(bp, p-5, "grave") || !BSTRCMP(bp, p-9, "headstone")) {
+               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");
@@ -2896,7 +2892,7 @@ srch:
                    return(&zeroobj);
                }
 
-               if(!BSTRCMP(bp, p-4, "tree")) {
+               if (!BSTRCMPI(bp, p-4, "tree")) {
                    lev->typ = TREE;
                    pline("A tree.");
                    newsym(x, y);
@@ -2904,7 +2900,7 @@ srch:
                    return &zeroobj;
                }
 
-               if(!BSTRCMP(bp, p-4, "bars")) {
+               if (!BSTRCMPI(bp, p-4, "bars")) {
                    lev->typ = IRONBARS;
                    pline("Iron bars.");
                    newsym(x, y);