potential ctype.h issues
authorPatR <rankin@nethack.org>
Mon, 23 May 2016 00:29:59 +0000 (17:29 -0700)
committerPatR <rankin@nethack.org>
Mon, 23 May 2016 00:29:59 +0000 (17:29 -0700)
A few things which might conceivably pass negative values to ctype
routines.  Some are post-3.6.0.  None of them explain the sporadic
Windows assertion failure.

Using tolower() without verifying the argument isupper() should be
completely safe when tolower() is a function but might not be when
it's a macro.  (Likewise for toupper() without islower().)  NetHack's
lowc() function is always safe, at least for ASCII.

src/makemon.c
src/options.c

index e9b56f9c90a1ef591c9abc2334cf8bd0022e3e83..b38f64fa49209e654702e7743f3611f16dc608e4 100644 (file)
@@ -1468,7 +1468,7 @@ rndmonst()
             rndmonst_state.mchoices[mndx] = 0;
             if (tooweak(mndx, minmlev) || toostrong(mndx, maxmlev))
                 continue;
-            if (upper && !isupper(def_monsyms[(int) (ptr->mlet)].sym))
+            if (upper && !isupper((uchar) def_monsyms[(int) ptr->mlet].sym))
                 continue;
             if (elemlevel && wrong_elem_type(ptr))
                 continue;
index 3f701edec3a9dcfc24d6c75e2ab72952c7dbe546..2a1eecadeedcf22576793c05754edb4c9c4c7456 100644 (file)
@@ -587,10 +587,13 @@ boolean val_allowed;
 
         if (!p || (q && q < p))
             p = q;
-        while (p && p > user_string && isspace((uchar) * (p - 1)))
-            p--;
-        if (p)
+        if (p) {
+            /* 'user_string' hasn't necessarily been through mungspaces()
+               so might have tabs or consecutive spaces */
+            while (p > user_string && isspace((uchar) *(p - 1)))
+                p--;
             len = (int) (p - user_string);
+        }
     }
 
     return (boolean) (len >= min_length
@@ -2110,7 +2113,7 @@ boolean tinitial, tfrom_file;
                 bad_negation(fullname, TRUE);
                 return;
             }
-            tmp = tolower(*op);
+            tmp = lowc(*op);
         }
         switch (tmp) {
         case 's': /* single message history cycle (default if negated) */
@@ -2576,30 +2579,24 @@ boolean tinitial, tfrom_file;
             bad_negation(fullname, FALSE);
             return;
         } else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0) {
-            switch (tolower(*op)) {
-            /* Unencumbered */
-            case 'u':
+            switch (lowc(*op)) {
+            case 'u': /* Unencumbered */
                 flags.pickup_burden = UNENCUMBERED;
                 break;
-            /* Burdened (slight encumbrance) */
-            case 'b':
+            case 'b': /* Burdened (slight encumbrance) */
                 flags.pickup_burden = SLT_ENCUMBER;
                 break;
-            /* streSsed (moderate encumbrance) */
-            case 's':
+            case 's': /* streSsed (moderate encumbrance) */
                 flags.pickup_burden = MOD_ENCUMBER;
                 break;
-            /* straiNed (heavy encumbrance) */
-            case 'n':
+            case 'n': /* straiNed (heavy encumbrance) */
                 flags.pickup_burden = HVY_ENCUMBER;
                 break;
-            /* OverTaxed (extreme encumbrance) */
-            case 'o':
+            case 'o': /* OverTaxed (extreme encumbrance) */
             case 't':
                 flags.pickup_burden = EXT_ENCUMBER;
                 break;
-            /* overLoaded */
-            case 'l':
+            case 'l': /* overLoaded */
                 flags.pickup_burden = OVERLOADED;
                 break;
             default:
@@ -2881,11 +2878,13 @@ boolean tinitial, tfrom_file;
     if (match_optname(opts, fullname, 4, TRUE)) {
         op = string_for_env_opt(fullname, opts, FALSE);
         if (op) {
-            switch (tolower(*op)) {
-            case 'n':
-            case 'l':
-            case 'f':
-                flags.sortloot = tolower(*op);
+            char c = lowc(*op);
+
+            switch (c) {
+            case 'n': /* none */
+            case 'l': /* loot (pickup) */
+            case 'f': /* full (pickup + invent) */
+                flags.sortloot = c;
                 break;
             default:
                 badoption(opts);
@@ -3163,22 +3162,26 @@ boolean tinitial, tfrom_file;
                 return; /* string_for_opt gave feedback */
             tmp = negated ? 'n' : 'f';
         } else {
-            tmp = tolower(*op);
+            tmp = lowc(*op);
         }
         switch (tmp) {
         case 'n': /* none */
-        case 't': /* traditional */
+        case 't': /* traditional: prompt for class(es) by symbol,
+                     prompt for each item within class(es) one at a time */
             flags.menu_style = MENU_TRADITIONAL;
             break;
-        case 'c': /* combo: trad.class sel+menu */
+        case 'c': /* combination: prompt for class(es) by symbol,
+                     choose items within selected class(es) by menu */
             flags.menu_style = MENU_COMBINATION;
             break;
-        case 'p': /* partial: no class menu */
-            flags.menu_style = MENU_PARTIAL;
-            break;
-        case 'f': /* full: class menu + menu */
+        case 'f': /* full: choose class(es) by first menu,
+                     choose items within selected class(es) by second menu */
             flags.menu_style = MENU_FULL;
             break;
+        case 'p': /* partial: skip class filtering,
+                     choose items among all classes by menu */
+            flags.menu_style = MENU_PARTIAL;
+            break;
         default:
             badoption(opts);
         }
@@ -5254,7 +5257,7 @@ const char *strval;
     buf[0] = '\0';
     if (!strval[0] || !strval[1]) { /* empty, or single character */
         /* if single char is space or tab, leave buf[0]=='\0' */
-        if (!isspace(strval[0]))
+        if (!isspace((uchar) strval[0]))
             buf[0] = strval[0];
     } else if (strval[0] == '\'') { /* single quote */
         /* simple matching single quote; we know strval[1] isn't '\0' */