#ifndef RBIMPL_CTYPE_H                               /*-*-C++-*-vi:se ft=cpp:*/
#define RBIMPL_CTYPE_H
/**
 * @file
 * @author     Ruby developers <ruby-core@ruby-lang.org>
 * @copyright  This  file  is   a  part  of  the   programming  language  Ruby.
 *             Permission  is hereby  granted,  to  either redistribute  and/or
 *             modify this file, provided that  the conditions mentioned in the
 *             file COPYING are met.  Consult the file for details.
 * @warning    Symbols   prefixed  with   either  `RBIMPL`   or  `rbimpl`   are
 *             implementation details.   Don't take  them as canon.  They could
 *             rapidly appear then vanish.  The name (path) of this header file
 *             is also an  implementation detail.  Do not expect  it to persist
 *             at the place it is now.  Developers are free to move it anywhere
 *             anytime at will.
 * @note       To  ruby-core:  remember  that   this  header  can  be  possibly
 *             recursively included  from extension  libraries written  in C++.
 *             Do not  expect for  instance `__VA_ARGS__` is  always available.
 *             We assume C99  for ruby itself but we don't  assume languages of
 *             extension libraries.  They could be written in C++98.
 * @brief      Our own, locale independent, character handling routines.
 */
#include "ruby/internal/config.h"

#ifdef STDC_HEADERS
# include <ctype.h>
#endif

#include "ruby/internal/attr/artificial.h"
#include "ruby/internal/attr/const.h"
#include "ruby/internal/attr/constexpr.h"
#include "ruby/internal/attr/nonnull.h"
#include "ruby/internal/dllexport.h"

/**
 * @name Old character classification macros
 *
 * What  is this  #ISPRINT  business?   Well, according  to  our  VCS and  some
 * internet surfing, it appears that the initial intent of these macros were to
 * mimic codes appear  in common in several GNU projects.   As far as @shyouhei
 * detects they  seem to originate GNU  regex (that standalone one  rather than
 * Gnulib or Glibc), and at least date back to 1995.
 *
 * Let me lawfully quote from a GNU coreutils commit
 * https://git.savannah.gnu.org/cgit/coreutils.git/commit/?id=49803907f5dbd7646184a8912c9db9b09dcd0f22
 *
 *   > Jim Meyering writes:
 *   >
 *   > "... Some ctype macros are valid only for character codes that
 *   > isascii says are ASCII (SGI's IRIX-4.0.5 is one such system --when
 *   > using /bin/cc or gcc but without giving an ansi option).  So, all
 *   > ctype uses should be through macros like ISPRINT...  If
 *   > STDC_HEADERS is defined, then autoconf has verified that the ctype
 *   > macros don't need to be guarded with references to isascii. ...
 *   > Defining isascii to 1 should let any compiler worth its salt
 *   > eliminate the && through constant folding."
 *   >
 *   > Bruno Haible adds:
 *   >
 *   > "... Furthermore, isupper(c) etc. have an undefined result if c is
 *   > outside the range -1 <= c <= 255. One is tempted to write isupper(c)
 *   > with c being of type `char', but this is wrong if c is an 8-bit
 *   > character >= 128 which gets sign-extended to a negative value.
 *   > The macro ISUPPER protects against this as well."
 *
 * So the intent  was to reroute old problematic systems  that no longer exist.
 * At the same time the problems described  above no longer hurt us, because we
 * decided to completely  avoid using system-provided isupper  etc. to reinvent
 * the wheel.  These macros are entirely legacy; please ignore them.
 *
 * But let me also  put stress that GNU people are wise;  they use those macros
 * only inside of  their own implementations and never let  them be public.  On
 * the other hand ruby has thoughtlessly publicised them to 3rd party libraries
 * since its beginning, which is a very bad idea.  These macros are too easy to
 * get conflicted with definitions elsewhere.
 *
 * New programs should stick to the `rb_` prefixed names.
 *
 * @note  It seems we just mimic the API.  We do not share their implementation
 *        with GPL-ed programs.
 *
 * @{
 */
#ifndef ISPRINT
# define ISASCII  rb_isascii    /**< @old{rb_isascii}*/
# define ISPRINT  rb_isprint    /**< @old{rb_isprint}*/
# define ISGRAPH  rb_isgraph    /**< @old{rb_isgraph}*/
# define ISSPACE  rb_isspace    /**< @old{rb_isspace}*/
# define ISUPPER  rb_isupper    /**< @old{rb_isupper}*/
# define ISLOWER  rb_islower    /**< @old{rb_islower}*/
# define ISALNUM  rb_isalnum    /**< @old{rb_isalnum}*/
# define ISALPHA  rb_isalpha    /**< @old{rb_isalpha}*/
# define ISDIGIT  rb_isdigit    /**< @old{rb_isdigit}*/
# define ISXDIGIT rb_isxdigit   /**< @old{rb_isxdigit}*/
# define ISBLANK  rb_isblank    /**< @old{rb_isblank}*/
# define ISCNTRL  rb_iscntrl    /**< @old{rb_iscntrl}*/
# define ISPUNCT  rb_ispunct    /**< @old{rb_ispunct}*/
#endif

#define TOUPPER     rb_toupper    /**< @old{rb_toupper}*/
#define TOLOWER     rb_tolower    /**< @old{rb_tolower}*/
#define STRCASECMP  st_locale_insensitive_strcasecmp  /**< @old{st_locale_insensitive_strcasecmp}*/
#define STRNCASECMP st_locale_insensitive_strncasecmp /**< @old{st_locale_insensitive_strncasecmp}*/
#define STRTOUL     ruby_strtoul  /**< @old{ruby_strtoul}*/

/** @} */

RBIMPL_SYMBOL_EXPORT_BEGIN()
/** @name locale insensitive functions
 *  @{
 */

/* In descriptions below, `the POSIX Locale` and `the "C" locale` are tactfully
 * used as to whether the described function mimics POSIX or C99. */

RBIMPL_ATTR_NONNULL(())
/**
 * Our  own locale-insensitive  version  of `strcasecmp(3)`.   The "case"  here
 * always means that of the POSIX  Locale.  It doesn't depend on runtime locale
 * settings.
 *
 * @param[in]  s1  Comparison LHS.
 * @param[in]  s2  Comparison RHS.
 * @retval     -1  `s1` is "less" than `s2`.
 * @retval      0  Both strings converted into lowercase would be identical.
 * @retval      1  `s1` is "greater" than `s2`.
 * @note       Not only  does this function  works under the POSIX  Locale, but
 *             also assumes its  execution character set be what  ruby calls an
 *             ASCII-compatible  character  set;  which does  not  include  for
 *             instance EBCDIC or UTF-16LE.
 */
int st_locale_insensitive_strcasecmp(const char *s1, const char *s2);

RBIMPL_ATTR_NONNULL(())
/**
 * Our  own locale-insensitive  version of  `strcnasecmp(3)`.  The  "case" here
 * always means that of the POSIX  Locale.  It doesn't depend on runtime locale
 * settings.
 *
 * @param[in]  s1  Comparison LHS.
 * @param[in]  s2  Comparison RHS.
 * @param[in]  n   Comparison shall stop after first `n` bytes are scanned.
 * @retval     -1  `s1` is "less" than `s2`.
 * @retval      0  Both strings converted into lowercase would be identical.
 * @retval      1  `s1` is "greater" than `s2`.
 * @note       Not only  does this function  works under the POSIX  Locale, but
 *             also assumes its  execution character set be what  ruby calls an
 *             ASCII-compatible  character  set;  which does  not  include  for
 *             instance EBCDIC or UTF-16LE.
 * @warning    This function is _not_ timing safe.
 */
int st_locale_insensitive_strncasecmp(const char *s1, const char *s2, size_t n);

RBIMPL_ATTR_NONNULL((1))
/**
 * Our own locale-insensitive version of  `strtoul(3)`.  The conversion is done
 * as if the current locale is set  to the "C" locale, no matter actual runtime
 * locale settings.
 *
 * @note        This is needed because  `strtoul("i", 0, 36)` would return zero
 *              if it is locale sensitive and the current locale is `tr_TR`.
 * @param[in]   str     String of digits,  optionally preceded with whitespaces
 *                      (ignored) and optionally `+` or `-` sign.
 * @param[out]  endptr  NULL, or an arbitrary pointer (overwritten on return).
 * @param[in]   base    `2` to  `36` inclusive for  each base, or  special case
 *                      `0` to detect the base from the contents of the string.
 * @return      Converted integer, casted to unsigned long.
 * @post        If `endptr` is not NULL, it  is updated to point the first such
 *              byte where conversion failed.
 * @note        This function sets `errno` on failure.
 *                - `EINVAL`: Passed `base` is out of range.
 *                - `ERANGE`: Converted integer is out of range of `long`.
 * @warning     As far as @shyouhei reads ISO/IEC 9899:2018 section 7.22.1.4, a
 *              conforming  `strtoul`  implementation   shall  render  `ERANGE`
 *              whenever  it  finds  the  input string  represents  a  negative
 *              integer.  Such thing can never be representable using `unsigned
 *              long`.   However  this  implementation  does  not  honour  that
 *              language.   It just  casts such  negative value  to the  return
 *              type, resulting a very big  return value.  This behaviour is at
 *              least questionable.  But  we can no longer change  that at this
 *              point.
 * @note        Not only  does this  function works under  the "C"  locale, but
 *              also assumes its execution character  set be what ruby calls an
 *              ASCII-compatible  character set;  which  does  not include  for
 *              instance EBCDIC or UTF-16LE.
 */
unsigned long ruby_strtoul(const char *str, char **endptr, int base);
RBIMPL_SYMBOL_EXPORT_END()

/*
 * We are making  the functions below to return `int`  instead of `bool`.  They
 * have been as such since their birth at 5f237d79033b2109afb768bc889611fa9630.
 */

RBIMPL_ATTR_CONST()
RBIMPL_ATTR_CONSTEXPR(CXX11)
RBIMPL_ATTR_ARTIFICIAL()
/**
 * Our own locale-insensitive version of `isascii(3)`.
 *
 * @param[in]  c      Byte in question to query.
 * @retval     false  `c` is out of range of ASCII character set.
 * @retval     true   Yes it is.
 * @warning    `c` is  an int.  This  means that when  you pass a  `char` value
 *             here, it  experiences "integer promotion" as  defined in ISO/IEC
 *             9899:2018 section 6.3.1.1 paragraph 1.
 */
static inline int
rb_isascii(int c)
{
    return '\0' <= c && c <= '\x7f';
}

RBIMPL_ATTR_CONST()
RBIMPL_ATTR_CONSTEXPR(CXX11)
RBIMPL_ATTR_ARTIFICIAL()
/**
 * Our own locale-insensitive version of `isupper(3)`.
 *
 * @param[in]  c      Byte in question to query.
 * @retval     true   `c`  is  listed  in IEEE 1003.1 section 7.3.1.1 "upper".
 * @retval     false  Anything else.
 * @note       Not only  does this function  works under the POSIX  Locale, but
 *             also assumes its  execution character set be what  ruby calls an
 *             ASCII-compatible  character  set;  which does  not  include  for
 *             instance EBCDIC or UTF-16LE.
 * @warning    `c` is  an int.  This  means that when  you pass a  `char` value
 *             here, it  experiences "integer promotion" as  defined in ISO/IEC
 *             9899:2018 section 6.3.1.1 paragraph 1.
 */
static inline int
rb_isupper(int c)
{
    return 'A' <= c && c <= 'Z';
}

RBIMPL_ATTR_CONST()
RBIMPL_ATTR_CONSTEXPR(CXX11)
RBIMPL_ATTR_ARTIFICIAL()
/**
 * Our own locale-insensitive version of `islower(3)`.
 *
 * @param[in]  c      Byte in question to query.
 * @retval     true   `c`  is  listed  in IEEE 1003.1 section 7.3.1.1 "lower".
 * @retval     false  Anything else.
 * @note       Not only  does this function  works under the POSIX  Locale, but
 *             also assumes its  execution character set be what  ruby calls an
 *             ASCII-compatible  character  set;  which does  not  include  for
 *             instance EBCDIC or UTF-16LE.
 * @warning    `c` is  an int.  This  means that when  you pass a  `char` value
 *             here, it  experiences "integer promotion" as  defined in ISO/IEC
 *             9899:2018 section 6.3.1.1 paragraph 1.
 */
static inline int
rb_islower(int c)
{
    return 'a' <= c && c <= 'z';
}

RBIMPL_ATTR_CONST()
RBIMPL_ATTR_CONSTEXPR(CXX11)
RBIMPL_ATTR_ARTIFICIAL()
/**
 * Our own locale-insensitive version of `isalpha(3)`.
 *
 * @param[in]  c      Byte in question to query.
 * @retval     true   `c`  is  listed in  either  IEEE  1003.1 section  7.3.1.1
 *                    "upper" or "lower".
 * @retval     false  Anything else.
 * @note       Not only  does this function  works under the POSIX  Locale, but
 *             also assumes its  execution character set be what  ruby calls an
 *             ASCII-compatible  character  set;  which does  not  include  for
 *             instance EBCDIC or UTF-16LE.
 * @warning    `c` is  an int.  This  means that when  you pass a  `char` value
 *             here, it  experiences "integer promotion" as  defined in ISO/IEC
 *             9899:2018 section 6.3.1.1 paragraph 1.
 */
static inline int
rb_isalpha(int c)
{
    return rb_isupper(c) || rb_islower(c);
}

RBIMPL_ATTR_CONST()
RBIMPL_ATTR_CONSTEXPR(CXX11)
RBIMPL_ATTR_ARTIFICIAL()
/**
 * Our own locale-insensitive version of `isdigit(3)`.
 *
 * @param[in]  c      Byte in question to query.
 * @retval     true   `c`  is  listed  in IEEE 1003.1 section 7.3.1.1 "digit".
 * @retval     false  Anything else.
 * @note       Not only  does this function  works under the POSIX  Locale, but
 *             also assumes its  execution character set be what  ruby calls an
 *             ASCII-compatible  character  set;  which does  not  include  for
 *             instance EBCDIC or UTF-16LE.
 * @warning    `c` is  an int.  This  means that when  you pass a  `char` value
 *             here, it  experiences "integer promotion" as  defined in ISO/IEC
 *             9899:2018 section 6.3.1.1 paragraph 1.
 */
static inline int
rb_isdigit(int c)
{
    return '0' <= c && c <= '9';
}

RBIMPL_ATTR_CONST()
RBIMPL_ATTR_CONSTEXPR(CXX11)
RBIMPL_ATTR_ARTIFICIAL()
/**
 * Our own locale-insensitive version of `isalnum(3)`.
 *
 * @param[in]  c      Byte in question to query.
 * @retval     true   `c`  is  listed in  either  IEEE  1003.1 section  7.3.1.1
 *                    "upper", "lower", or "digit".
 * @retval     false  Anything else.
 * @note       Not only  does this function  works under the POSIX  Locale, but
 *             also assumes its  execution character set be what  ruby calls an
 *             ASCII-compatible  character  set;  which does  not  include  for
 *             instance EBCDIC or UTF-16LE.
 * @warning    `c` is  an int.  This  means that when  you pass a  `char` value
 *             here, it  experiences "integer promotion" as  defined in ISO/IEC
 *             9899:2018 section 6.3.1.1 paragraph 1.
 */
static inline int
rb_isalnum(int c)
{
    return rb_isalpha(c) || rb_isdigit(c);
}

RBIMPL_ATTR_CONST()
RBIMPL_ATTR_CONSTEXPR(CXX11)
RBIMPL_ATTR_ARTIFICIAL()
/**
 * Our own locale-insensitive version of `isxdigit(3)`.
 *
 * @param[in]  c      Byte in question to query.
 * @retval     true   `c`  is  listed  in IEEE 1003.1 section 7.3.1.1 "xdigit".
 * @retval     false  Anything else.
 * @note       Not only  does this function  works under the POSIX  Locale, but
 *             also assumes its  execution character set be what  ruby calls an
 *             ASCII-compatible  character  set;  which does  not  include  for
 *             instance EBCDIC or UTF-16LE.
 * @warning    `c` is  an int.  This  means that when  you pass a  `char` value
 *             here, it  experiences "integer promotion" as  defined in ISO/IEC
 *             9899:2018 section 6.3.1.1 paragraph 1.
 */
static inline int
rb_isxdigit(int c)
{
    return rb_isdigit(c) || ('A' <= c && c <= 'F') || ('a' <= c && c <= 'f');
}

RBIMPL_ATTR_CONST()
RBIMPL_ATTR_CONSTEXPR(CXX11)
RBIMPL_ATTR_ARTIFICIAL()
/**
 * Our own locale-insensitive version of `isblank(3)`.
 *
 * @param[in]  c      Byte in question to query.
 * @retval     true   `c`  is  listed  in IEEE 1003.1 section 7.3.1.1 "blank".
 * @retval     false  Anything else.
 * @note       Not only  does this function  works under the POSIX  Locale, but
 *             also assumes its  execution character set be what  ruby calls an
 *             ASCII-compatible  character  set;  which does  not  include  for
 *             instance EBCDIC or UTF-16LE.
 * @warning    `c` is  an int.  This  means that when  you pass a  `char` value
 *             here, it  experiences "integer promotion" as  defined in ISO/IEC
 *             9899:2018 section 6.3.1.1 paragraph 1.
 */
static inline int
rb_isblank(int c)
{
    return c == ' ' || c == '\t';
}

RBIMPL_ATTR_CONST()
RBIMPL_ATTR_CONSTEXPR(CXX11)
RBIMPL_ATTR_ARTIFICIAL()
/**
 * Our own locale-insensitive version of `isspace(3)`.
 *
 * @param[in]  c      Byte in question to query.
 * @retval     true   `c`  is  listed  in IEEE 1003.1 section 7.3.1.1 "space".
 * @retval     false  Anything else.
 * @note       Not only  does this function  works under the POSIX  Locale, but
 *             also assumes its  execution character set be what  ruby calls an
 *             ASCII-compatible  character  set;  which does  not  include  for
 *             instance EBCDIC or UTF-16LE.
 * @warning    `c` is  an int.  This  means that when  you pass a  `char` value
 *             here, it  experiences "integer promotion" as  defined in ISO/IEC
 *             9899:2018 section 6.3.1.1 paragraph 1.
 */
static inline int
rb_isspace(int c)
{
    return c == ' ' || ('\t' <= c && c <= '\r');
}

RBIMPL_ATTR_CONST()
RBIMPL_ATTR_CONSTEXPR(CXX11)
RBIMPL_ATTR_ARTIFICIAL()
/**
 * Our own locale-insensitive version of `iscntrl(3)`.
 *
 * @param[in]  c      Byte in question to query.
 * @retval     true   `c`  is  listed  in IEEE 1003.1 section 7.3.1.1 "cntrl".
 * @retval     false  Anything else.
 * @note       Not only  does this function  works under the POSIX  Locale, but
 *             also assumes its  execution character set be what  ruby calls an
 *             ASCII-compatible  character  set;  which does  not  include  for
 *             instance EBCDIC or UTF-16LE.
 * @warning    `c` is  an int.  This  means that when  you pass a  `char` value
 *             here, it  experiences "integer promotion" as  defined in ISO/IEC
 *             9899:2018 section 6.3.1.1 paragraph 1.
 */
static inline int
rb_iscntrl(int c)
{
    return ('\0' <= c && c < ' ') || c == '\x7f';
}

RBIMPL_ATTR_CONST()
RBIMPL_ATTR_CONSTEXPR(CXX11)
RBIMPL_ATTR_ARTIFICIAL()
/**
 * Identical to rb_isgraph(), except it also returns true for `' '`.
 *
 * @param[in]  c      Byte in question to query.
 * @retval     true   `c`  is  listed in  either  IEEE  1003.1 section  7.3.1.1
 *                    "upper", "lower", "digit", "punct", or a `' '`.
 * @retval     false  Anything else.
 * @note       Not only  does this function  works under the POSIX  Locale, but
 *             also assumes its  execution character set be what  ruby calls an
 *             ASCII-compatible  character  set;  which does  not  include  for
 *             instance EBCDIC or UTF-16LE.
 * @warning    `c` is  an int.  This  means that when  you pass a  `char` value
 *             here, it  experiences "integer promotion" as  defined in ISO/IEC
 *             9899:2018 section 6.3.1.1 paragraph 1.
 */
static inline int
rb_isprint(int c)
{
    return ' ' <= c && c <= '\x7e';
}

RBIMPL_ATTR_CONST()
RBIMPL_ATTR_CONSTEXPR(CXX11)
RBIMPL_ATTR_ARTIFICIAL()
/**
 * Our own locale-insensitive version of `ispunct(3)`.
 *
 * @param[in]  c      Byte in question to query.
 * @retval     true   `c`  is  listed  in IEEE 1003.1 section 7.3.1.1 "punct".
 * @retval     false  Anything else.
 * @note       Not only  does this function  works under the POSIX  Locale, but
 *             also assumes its  execution character set be what  ruby calls an
 *             ASCII-compatible  character  set;  which does  not  include  for
 *             instance EBCDIC or UTF-16LE.
 * @warning    `c` is  an int.  This  means that when  you pass a  `char` value
 *             here, it  experiences "integer promotion" as  defined in ISO/IEC
 *             9899:2018 section 6.3.1.1 paragraph 1.
 */
static inline int
rb_ispunct(int c)
{
    return !rb_isalnum(c);
}

RBIMPL_ATTR_CONST()
RBIMPL_ATTR_CONSTEXPR(CXX11)
RBIMPL_ATTR_ARTIFICIAL()
/**
 * Our own locale-insensitive version of `isgraph(3)`.
 *
 * @param[in]  c      Byte in question to query.
 * @retval     true   `c`  is  listed in  either  IEEE  1003.1 section  7.3.1.1
 *                    "upper", "lower", "digit", or "punct".
 * @retval     false  Anything else.
 * @note       Not only  does this function  works under the POSIX  Locale, but
 *             also assumes its  execution character set be what  ruby calls an
 *             ASCII-compatible  character  set;  which does  not  include  for
 *             instance EBCDIC or UTF-16LE.
 * @warning    `c` is  an int.  This  means that when  you pass a  `char` value
 *             here, it  experiences "integer promotion" as  defined in ISO/IEC
 *             9899:2018 section 6.3.1.1 paragraph 1.
 */
static inline int
rb_isgraph(int c)
{
    return '!' <= c && c <= '\x7e';
}

RBIMPL_ATTR_CONST()
RBIMPL_ATTR_CONSTEXPR(CXX11)
RBIMPL_ATTR_ARTIFICIAL()
/**
 * Our own locale-insensitive version of `tolower(3)`.
 *
 * @param[in]  c          Byte in question to convert.
 * @retval     c          The byte is not listed  in IEEE 1003.1 section 7.3.1.1
 *                        "upper".
 * @retval     otherwise  Byte converted  using the map defined  in IEEE 1003.1
 *                        section 7.3.1 "tolower".
 * @note       Not only  does this function  works under the POSIX  Locale, but
 *             also assumes its  execution character set be what  ruby calls an
 *             ASCII-compatible  character  set;  which does  not  include  for
 *             instance EBCDIC or UTF-16LE.
 * @warning    `c` is  an int.  This  means that when  you pass a  `char` value
 *             here, it  experiences "integer promotion" as  defined in ISO/IEC
 *             9899:2018 section 6.3.1.1 paragraph 1.
 */
static inline int
rb_tolower(int c)
{
    return rb_isupper(c) ? (c|0x20) : c;
}

RBIMPL_ATTR_CONST()
RBIMPL_ATTR_CONSTEXPR(CXX11)
RBIMPL_ATTR_ARTIFICIAL()
/**
 * Our own locale-insensitive version of `toupper(3)`.
 *
 * @param[in]  c          Byte in question to convert.
 * @retval     c          The  byte is  not listed  in in  IEEE 1003.1  section
 *                        7.3.1.1 "lower".
 * @retval     otherwise  Byte converted  using the map defined  in IEEE 1003.1
 *                        section 7.3.1 "toupper".
 * @note       Not only  does this function  works under the POSIX  Locale, but
 *             also assumes its  execution character set be what  ruby calls an
 *             ASCII-compatible  character  set;  which does  not  include  for
 *             instance EBCDIC or UTF-16LE.
 * @warning    `c` is  an int.  This  means that when  you pass a  `char` value
 *             here, it  experiences "integer promotion" as  defined in ISO/IEC
 *             9899:2018 section 6.3.1.1 paragraph 1.
 */
static inline int
rb_toupper(int c)
{
    return rb_islower(c) ? (c&0x5f) : c;
}

/** @} */
#endif /* RBIMPL_CTYPE_H */
