more plural/singular (trunk only)

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.
This commit is contained in:
nethack.rankin
2007-05-03 01:17:46 +00:00
parent 894e7f0267
commit 15e79e99b9
3 changed files with 73 additions and 73 deletions
+2 -1
View File
@@ -812,7 +812,8 @@ E char *FDECL(mungspaces, (char *));
E char *FDECL(eos, (char *)); E char *FDECL(eos, (char *));
E char *FDECL(strkitten, (char *,CHAR_P)); E char *FDECL(strkitten, (char *,CHAR_P));
E void FDECL(copynchars, (char *,const char *,int)); 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(s_suffix, (const char *));
E char *FDECL(xcrypt, (const char *,char *)); E char *FDECL(xcrypt, (const char *,char *));
E boolean FDECL(onlyspace, (const char *)); E boolean FDECL(onlyspace, (const char *));
+27 -24
View File
@@ -20,7 +20,8 @@ NetHack, except that rounddiv may call panic().
char * eos (char *) char * eos (char *)
char * strkitten (char *,char) char * strkitten (char *,char)
void copynchars (char *,const char *,int) 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 * s_suffix (const char *)
char * xcrypt (const char *, char *) char * xcrypt (const char *, char *)
boolean onlyspace (const char *) boolean onlyspace (const char *)
@@ -157,19 +158,33 @@ copynchars(dst, src, n) /* truncating string copy */
*dst = '\0'; *dst = '\0';
} }
/* this assumes ASCII; someday we'll have to switch over to <ctype.h> */ /* mostly used by strcasecpy */
#define nh_islower(c) ((c) >= 'a' && (c) <= 'z') char
#define nh_isupper(c) ((c) >= 'A' && (c) <= 'Z') chrcasecpy(oc, nc) /* convert char nc into oc's case */
#define nh_tolower(c) lowc(c) int oc, nc;
#define nh_toupper(c) highc(c) {
#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(); /* for case-insensitive editions of makeplural() and makesingular();
src might be shorter, same length, or longer than dst */ src might be shorter, same length, or longer than dst */
void char *
Strcasecpy(dst, src) /* overwrite string, preserving old chars' case */ strcasecpy(dst, src) /* overwrite string, preserving old chars' case */
char *dst; char *dst;
const char *src; const char *src;
{ {
char *result = dst;
int ic, oc, dst_exhausted = 0; int ic, oc, dst_exhausted = 0;
/* while dst has characters, replace each one with corresponding /* 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 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 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] */ 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; if (!dst_exhausted && !*dst) dst_exhausted = 1;
/* fetch old character */ oc = (int)*(dst - dst_exhausted);
oc = (int)(unsigned char)*(dst - dst_exhausted); *dst++ = chrcasecpy(oc, ic);
/* 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;
} }
*dst = '\0'; *dst = '\0';
return result;
} }
#undef nh_islower
#undef nh_isupper
#undef nh_tolower
#undef nh_toupper
char * char *
s_suffix(s) /* return a name converted to possessive */ s_suffix(s) /* return a name converted to possessive */
const char *s; const char *s;
+44 -48
View File
@@ -21,6 +21,10 @@ struct Jitem {
const char *name; 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 */ /* true for gems/rocks that should have " stone" appended to their names */
#define GemStone(typ) (typ == FLINT || \ #define GemStone(typ) (typ == FLINT || \
(objects[typ].oc_material == GEMSTONE && \ (objects[typ].oc_material == GEMSTONE && \
@@ -1386,7 +1390,7 @@ vtense(subj, verb)
register const char *subj; register const char *subj;
register const char *verb; register const char *verb;
{ {
char *buf = nextobuf(); char *buf = nextobuf(), *bspot;
int len, ltmp; int len, ltmp;
const char *sp, *spot; const char *sp, *spot;
const char * const *spec; const char * const *spec;
@@ -1406,11 +1410,11 @@ register const char *verb;
goto sing; goto sing;
spot = (const char *)0; spot = (const char *)0;
for (sp = subj; (sp = index(sp, ' ')) != 0; ++sp) { for (sp = subj; (sp = index(sp, ' ')) != 0; ++sp) {
if (!strncmp(sp, " of ", 4) || if (!strncmpi(sp, " of ", 4) ||
!strncmp(sp, " from ", 6) || !strncmpi(sp, " from ", 6) ||
!strncmp(sp, " called ", 8) || !strncmpi(sp, " called ", 8) ||
!strncmp(sp, " named ", 7) || !strncmpi(sp, " named ", 7) ||
!strncmp(sp, " labeled ", 9)) { !strncmpi(sp, " labeled ", 9)) {
if (sp != subj) spot = sp - 1; if (sp != subj) spot = sp - 1;
break; break;
} }
@@ -1422,12 +1426,12 @@ register const char *verb;
* plural: anything that ends in 's', but not '*us' or '*ss'. * plural: anything that ends in 's', but not '*us' or '*ss'.
* Guess at a few other special cases that makeplural creates. * Guess at a few other special cases that makeplural creates.
*/ */
if ((*spot == 's' && spot != subj && if ((lowc(*spot) == 's' && spot != subj &&
(*(spot-1) != 'u' && *(spot-1) != 's')) || !index("us", lowc(*(spot-1)))) ||
((spot - subj) >= 4 && !strncmp(spot-3, "eeth", 4)) || BSTRNCMPI(subj, spot-3, "eeth", 4) ||
((spot - subj) >= 3 && !strncmp(spot-3, "feet", 4)) || BSTRNCMPI(subj, spot-3, "feet", 4) ||
((spot - subj) >= 2 && !strncmp(spot-1, "ia", 2)) || BSTRNCMPI(subj, spot-1, "ia", 2) ||
((spot - subj) >= 2 && !strncmp(spot-1, "ae", 2))) { BSTRNCMPI(subj, spot-1, "ae", 2)) {
/* check for special cases to avoid false matches */ /* check for special cases to avoid false matches */
len = (int)(spot - subj) + 1; len = (int)(spot - subj) + 1;
for (spec = special_subjs; *spec; spec++) { for (spec = special_subjs; *spec; spec++) {
@@ -1450,26 +1454,25 @@ register const char *verb;
} }
sing: sing:
len = strlen(verb); Strcpy(buf, verb);
spot = verb + len - 1; len = (int)strlen(buf);
bspot = buf + len - 1;
if (!strcmp(verb, "are")) if (!strcmpi(buf, "are")) {
Strcpy(buf, "is"); Strcasecpy(buf, "is");
else if (!strcmp(verb, "have")) } else if (!strcmpi(buf, "have")) {
Strcpy(buf, "has"); Strcasecpy(bspot-1, "s");
else if (index("zxs", *spot) || } else if (index("zxs", lowc(*bspot)) ||
(len >= 2 && *spot=='h' && index("cs", *(spot-1))) || (len >= 2 && lowc(*bspot) == 'h' &&
(len == 2 && *spot == 'o')) { index("cs", lowc(*(bspot-1)))) ||
(len == 2 && lowc(*bspot) == 'o')) {
/* Ends in z, x, s, ch, sh; add an "es" */ /* Ends in z, x, s, ch, sh; add an "es" */
Strcpy(buf, verb); Strcasecpy(bspot+1, "es");
Strcat(buf, "es"); } else if (lowc(*bspot) == 'y' && !index(vowels, lowc(*(bspot-1)))) {
} else if (*spot == 'y' && (!index(vowels, *(spot-1)))) {
/* like "y" case in makeplural */ /* like "y" case in makeplural */
Strcpy(buf, verb); Strcasecpy(bspot, "ies");
Strcpy(buf + len - 1, "ies");
} else { } else {
Strcpy(buf, verb); Strcasecpy(bspot+1, "s");
Strcat(buf, "s");
} }
return buf; return buf;
@@ -1895,11 +1898,6 @@ STATIC_OVL NEARDATA const struct o_range o_ranges[] = {
{ "grey stone", GEM_CLASS, LUCKSTONE, FLINT }, { "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 * 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 * 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, "-in-")) != 0 ||
(p = strstri(bp, "-at-")) != 0) { (p = strstri(bp, "-at-")) != 0) {
/* [wo]men-at-arms -> [wo]man-at-arms; takes "not end in s" exit */ /* [wo]men-at-arms -> [wo]man-at-arms; takes "not end in s" exit */
if (!BSTRNCMPI(bp, p-3, "men", 3)) { if (!BSTRNCMPI(bp, p-3, "men", 3)) p[-2] = chrcasecpy(p[-2], 'a');
char c = *p; /* Strcasecpy will clobber *p with '\0' */
Strcasecpy(p-2, "an");
*p = c;
}
if (BSTRNCMPI(bp, p-1, "s", 1)) return bp; /* wasn't plural */ if (BSTRNCMPI(bp, p-1, "s", 1)) return bp; /* wasn't plural */
--p; /* back up to the 's' */ --p; /* back up to the 's' */
@@ -1959,6 +1953,7 @@ const char *oldstr;
if (p >= bp+3 && lowc(p[-3]) == 'i') { /* "ies" */ if (p >= bp+3 && lowc(p[-3]) == 'i') { /* "ies" */
if (!BSTRCMPI(bp, p-7, "cookies") || if (!BSTRCMPI(bp, p-7, "cookies") ||
!BSTRCMPI(bp, p-4, "pies") || !BSTRCMPI(bp, p-4, "pies") ||
!BSTRCMPI(bp, p-5, "mbies") || /* zombie */
!BSTRCMPI(bp, p-5, "yries")) /* valkyrie */ !BSTRCMPI(bp, p-5, "yries")) /* valkyrie */
goto mins; goto mins;
Strcasecpy(p-3, "y"); /* ies -> y */ Strcasecpy(p-3, "y"); /* ies -> y */
@@ -2826,7 +2821,7 @@ srch:
/* furniture and terrain */ /* furniture and terrain */
lev = &levl[x][y]; lev = &levl[x][y];
p = eos(bp); p = eos(bp);
if(!BSTRCMP(bp, p-8, "fountain")) { if (!BSTRCMPI(bp, p-8, "fountain")) {
lev->typ = FOUNTAIN; lev->typ = FOUNTAIN;
level.flags.nfountains++; level.flags.nfountains++;
if(!strncmpi(bp, "magic ", 6)) lev->blessedftn = 1; if(!strncmpi(bp, "magic ", 6)) lev->blessedftn = 1;
@@ -2834,14 +2829,14 @@ srch:
newsym(x, y); newsym(x, y);
return(&zeroobj); return(&zeroobj);
} }
if(!BSTRCMP(bp, p-6, "throne")) { if (!BSTRCMPI(bp, p-6, "throne")) {
lev->typ = THRONE; lev->typ = THRONE;
pline("A throne."); pline("A throne.");
newsym(x, y); newsym(x, y);
return(&zeroobj); return(&zeroobj);
} }
# ifdef SINKS # ifdef SINKS
if(!BSTRCMP(bp, p-4, "sink")) { if (!BSTRCMPI(bp, p-4, "sink")) {
lev->typ = SINK; lev->typ = SINK;
level.flags.nsinks++; level.flags.nsinks++;
pline("A sink."); pline("A sink.");
@@ -2850,8 +2845,8 @@ srch:
} }
# endif # endif
/* ("water" matches "potion of water" rather than terrain) */ /* ("water" matches "potion of water" rather than terrain) */
if (!BSTRCMP(bp, p-4, "pool") || !BSTRCMP(bp, p-4, "moat")) { if (!BSTRCMPI(bp, p-4, "pool") || !BSTRCMPI(bp, p-4, "moat")) {
lev->typ = !BSTRCMP(bp, p-4, "pool") ? POOL : MOAT; lev->typ = !BSTRCMPI(bp, p-4, "pool") ? POOL : MOAT;
del_engr_at(x, y); del_engr_at(x, y);
pline("A %s.", (lev->typ == POOL) ? "pool" : "moat"); pline("A %s.", (lev->typ == POOL) ? "pool" : "moat");
/* Must manually make kelp! */ /* Must manually make kelp! */
@@ -2859,7 +2854,7 @@ srch:
newsym(x, y); newsym(x, y);
return &zeroobj; 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; lev->typ = LAVAPOOL;
del_engr_at(x, y); del_engr_at(x, y);
pline("A pool of molten lava."); pline("A pool of molten lava.");
@@ -2868,7 +2863,7 @@ srch:
return &zeroobj; return &zeroobj;
} }
if(!BSTRCMP(bp, p-5, "altar")) { if (!BSTRCMPI(bp, p-5, "altar")) {
aligntyp al; aligntyp al;
lev->typ = ALTAR; lev->typ = ALTAR;
@@ -2888,7 +2883,8 @@ srch:
return(&zeroobj); 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); make_grave(x, y, (char *)0);
pline("%s.", IS_GRAVE(lev->typ) ? "A grave" : pline("%s.", IS_GRAVE(lev->typ) ? "A grave" :
"Can't place a grave here"); "Can't place a grave here");
@@ -2896,7 +2892,7 @@ srch:
return(&zeroobj); return(&zeroobj);
} }
if(!BSTRCMP(bp, p-4, "tree")) { if (!BSTRCMPI(bp, p-4, "tree")) {
lev->typ = TREE; lev->typ = TREE;
pline("A tree."); pline("A tree.");
newsym(x, y); newsym(x, y);
@@ -2904,7 +2900,7 @@ srch:
return &zeroobj; return &zeroobj;
} }
if(!BSTRCMP(bp, p-4, "bars")) { if (!BSTRCMPI(bp, p-4, "bars")) {
lev->typ = IRONBARS; lev->typ = IRONBARS;
pline("Iron bars."); pline("Iron bars.");
newsym(x, y); newsym(x, y);