+#define IN_UTF8_CTYPE_LOCALE PL_in_utf8_CTYPE_locale
+
+/* Use foo_LC_uvchr() instead of these for beyond the Latin1 range */
+
+/* For internal core Perl use only: the base macro for defining macros like
+ * isALPHA_LC, which uses the current LC_CTYPE locale. 'c' is the code point
+ * (0-255) to check. In a UTF-8 locale, the result is the same as calling
+ * isFOO_L1(); the 'utf8_locale_classnum' parameter is something like
+ * _CC_UPPER, which gives the class number for doing this. For non-UTF-8
+ * locales, the code to actually do the test this is passed in 'non_utf8'. If
+ * 'c' is above 255, 0 is returned. For accessing the full range of possible
+ * code points under locale rules, use the macros based on _generic_LC_uvchr
+ * instead of this. */
+#define _generic_LC_base(c, utf8_locale_classnum, non_utf8) \
+ (! FITS_IN_8_BITS(c) \
+ ? 0 \
+ : IN_UTF8_CTYPE_LOCALE \
+ ? cBOOL(PL_charclass[(U8) (c)] & _CC_mask(utf8_locale_classnum)) \
+ : cBOOL(non_utf8))
+
+/* For internal core Perl use only: a helper macro for defining macros like
+ * isALPHA_LC. 'c' is the code point (0-255) to check. The function name to
+ * actually do this test is passed in 'non_utf8_func', which is called on 'c',
+ * casting 'c' to the macro _LC_CAST, which should not be parenthesized. See
+ * _generic_LC_base for more info */
+#define _generic_LC(c, utf8_locale_classnum, non_utf8_func) \
+ _generic_LC_base(c,utf8_locale_classnum, \
+ non_utf8_func( (_LC_CAST) (c)))
+
+/* For internal core Perl use only: like _generic_LC, but also returns TRUE if
+ * 'c' is the platform's native underscore character */
+#define _generic_LC_underscore(c,utf8_locale_classnum,non_utf8_func) \
+ _generic_LC_base(c, utf8_locale_classnum, \
+ (non_utf8_func( (_LC_CAST) (c)) \
+ || (char)(c) == '_'))
+
+/* These next three are also for internal core Perl use only: case-change
+ * helper macros */
+#define _generic_toLOWER_LC(c, function, cast) (! FITS_IN_8_BITS(c) \
+ ? (c) \
+ : (IN_UTF8_CTYPE_LOCALE) \
+ ? PL_latin1_lc[ (U8) (c) ] \
+ : (cast)function((cast)(c)))
+
+/* Note that the result can be larger than a byte in a UTF-8 locale. It
+ * returns a single value, so can't adequately return the upper case of LATIN
+ * SMALL LETTER SHARP S in a UTF-8 locale (which should be a string of two
+ * values "SS"); instead it asserts against that under DEBUGGING, and
+ * otherwise returns its input */
+#define _generic_toUPPER_LC(c, function, cast) \
+ (! FITS_IN_8_BITS(c) \
+ ? (c) \
+ : ((! IN_UTF8_CTYPE_LOCALE) \
+ ? (cast)function((cast)(c)) \
+ : ((((U8)(c)) == MICRO_SIGN) \
+ ? GREEK_CAPITAL_LETTER_MU \
+ : ((((U8)(c)) == LATIN_SMALL_LETTER_Y_WITH_DIAERESIS) \
+ ? LATIN_CAPITAL_LETTER_Y_WITH_DIAERESIS \
+ : ((((U8)(c)) == LATIN_SMALL_LETTER_SHARP_S) \
+ ? (__ASSERT_(0) (c)) \
+ : PL_mod_latin1_uc[ (U8) (c) ])))))
+
+/* Note that the result can be larger than a byte in a UTF-8 locale. It
+ * returns a single value, so can't adequately return the fold case of LATIN
+ * SMALL LETTER SHARP S in a UTF-8 locale (which should be a string of two
+ * values "ss"); instead it asserts against that under DEBUGGING, and
+ * otherwise returns its input */
+#define _generic_toFOLD_LC(c, function, cast) \
+ ((UNLIKELY((c) == MICRO_SIGN) && IN_UTF8_CTYPE_LOCALE) \
+ ? GREEK_SMALL_LETTER_MU \
+ : (__ASSERT_(! IN_UTF8_CTYPE_LOCALE \
+ || (c) != LATIN_SMALL_LETTER_SHARP_S) \
+ _generic_toLOWER_LC(c, function, cast)))