Print this page
    
2964 need POSIX 2008 locale object support
Reviewed by: Robert Mustacchi <rm@joyent.com>
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
Approved by: TBD
    
      
        | Split | Close | 
      | Expand all | 
      | Collapse all | 
    
    
          --- old/usr/src/lib/libc/port/locale/utf8.c
          +++ new/usr/src/lib/libc/port/locale/utf8.c
   1    1  /*
        2 + * Copyright 2013 Garrett D'Amore <garrett@damore.org>
   2    3   * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
   3    4   * Copyright (c) 2002-2004 Tim J. Robbins
   4    5   * All rights reserved.
   5    6   *
   6    7   * Redistribution and use in source and binary forms, with or without
   7    8   * modification, are permitted provided that the following conditions
   8    9   * are met:
   9   10   * 1. Redistributions of source code must retain the above copyright
  10   11   *    notice, this list of conditions and the following disclaimer.
  11   12   * 2. Redistributions in binary form must reproduce the above copyright
  12   13   *    notice, this list of conditions and the following disclaimer in the
  13   14   *    documentation and/or other materials provided with the distribution.
  14   15   *
  15   16   * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  16   17   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  17   18   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  18   19   * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  19   20   * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  20   21   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  
    | ↓ open down ↓ | 9 lines elided | ↑ open up ↑ | 
  21   22   * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  22   23   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  23   24   * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  24   25   * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  25   26   * SUCH DAMAGE.
  26   27   */
  27   28  
  28   29  #include "lint.h"
  29   30  #include <errno.h>
  30   31  #include <limits.h>
  31      -#include "runetype.h"
  32   32  #include <stdlib.h>
  33   33  #include <string.h>
  34   34  #include <wchar.h>
  35   35  #include "mblocal.h"
       36 +#include "lctype.h"
  36   37  
  37   38  static size_t   _UTF8_mbrtowc(wchar_t *_RESTRICT_KYWD,
  38   39                      const char *_RESTRICT_KYWD,
  39   40                      size_t, mbstate_t *_RESTRICT_KYWD);
  40   41  static int      _UTF8_mbsinit(const mbstate_t *);
  41   42  static size_t   _UTF8_mbsnrtowcs(wchar_t *_RESTRICT_KYWD,
  42   43                      const char **_RESTRICT_KYWD, size_t, size_t,
  43   44                      mbstate_t *_RESTRICT_KYWD);
  44   45  static size_t   _UTF8_wcrtomb(char *_RESTRICT_KYWD, wchar_t,
  45   46                      mbstate_t *_RESTRICT_KYWD);
  46   47  static size_t   _UTF8_wcsnrtombs(char *_RESTRICT_KYWD,
  47   48                      const wchar_t **_RESTRICT_KYWD,
  48   49                      size_t, size_t, mbstate_t *_RESTRICT_KYWD);
  49   50  
  50   51  typedef struct {
  51   52          wchar_t ch;
  52   53          int     want;
  53   54          wchar_t lbound;
  54   55  } _UTF8State;
  55   56  
  56      -int
  57      -_UTF8_init(_RuneLocale *rl)
       57 +void
       58 +_UTF8_init(struct lc_ctype *lct)
  58   59  {
  59      -        __mbrtowc = _UTF8_mbrtowc;
  60      -        __wcrtomb = _UTF8_wcrtomb;
  61      -        __mbsinit = _UTF8_mbsinit;
  62      -        __mbsnrtowcs = _UTF8_mbsnrtowcs;
  63      -        __wcsnrtombs = _UTF8_wcsnrtombs;
  64      -        _CurrentRuneLocale = rl;
  65      -
  66      -        charset_is_ascii = 0;
  67      -
  68      -        /*
  69      -         * In theory up to 6 bytes can be used for the encoding,
  70      -         * but only encodings with more than 4 bytes are illegal.
  71      -         */
  72      -        __ctype[520] = 4;
  73      -        /*
  74      -         * Note that the other CSWIDTH members are nonsensical for this
  75      -         * this coding.  They only are valid with EUC codings.
  76      -         */
  77      -
  78      -        return (0);
       60 +        lct->lc_mbrtowc = _UTF8_mbrtowc;
       61 +        lct->lc_wcrtomb = _UTF8_wcrtomb;
       62 +        lct->lc_mbsinit = _UTF8_mbsinit;
       63 +        lct->lc_mbsnrtowcs = _UTF8_mbsnrtowcs;
       64 +        lct->lc_wcsnrtombs = _UTF8_wcsnrtombs;
       65 +        lct->lc_is_ascii = 0;
       66 +        lct->lc_max_mblen = 4;
  79   67  }
  80   68  
  81   69  static int
  82   70  _UTF8_mbsinit(const mbstate_t *ps)
  83   71  {
  84   72  
  85   73          return (ps == NULL || ((const _UTF8State *)ps)->want == 0);
  86   74  }
  87   75  
  88   76  static size_t
  89   77  _UTF8_mbrtowc(wchar_t *_RESTRICT_KYWD pwc, const char *_RESTRICT_KYWD s,
  90   78      size_t n, mbstate_t *_RESTRICT_KYWD ps)
  91   79  {
  92   80          _UTF8State *us;
  93   81          int ch, i, mask, want;
  94   82          wchar_t lbound, wch;
  95   83  
  96   84          us = (_UTF8State *)ps;
  97   85  
  98   86          if (us->want < 0 || us->want > 6) {
  99   87                  errno = EINVAL;
 100   88                  return ((size_t)-1);
 101   89          }
 102   90  
 103   91          if (s == NULL) {
 104   92                  s = "";
 105   93                  n = 1;
 106   94                  pwc = NULL;
 107   95          }
 108   96  
 109   97          if (n == 0)
 110   98                  /* Incomplete multibyte sequence */
 111   99                  return ((size_t)-2);
 112  100  
 113  101          if (us->want == 0) {
 114  102                  /*
 115  103                   * Determine the number of octets that make up this character
 116  104                   * from the first octet, and a mask that extracts the
 117  105                   * interesting bits of the first octet. We already know
 118  106                   * the character is at least two bytes long.
 119  107                   *
 120  108                   * We also specify a lower bound for the character code to
 121  109                   * detect redundant, non-"shortest form" encodings. For
 122  110                   * example, the sequence C0 80 is _not_ a legal representation
 123  111                   * of the null character. This enforces a 1-to-1 mapping
 124  112                   * between character codes and their multibyte representations.
 125  113                   */
 126  114                  ch = (unsigned char)*s;
 127  115                  if ((ch & 0x80) == 0) {
 128  116                          /* Fast path for plain ASCII characters. */
 129  117                          if (pwc != NULL)
 130  118                                  *pwc = ch;
 131  119                          return (ch != '\0' ? 1 : 0);
 132  120                  }
 133  121                  if ((ch & 0xe0) == 0xc0) {
 134  122                          mask = 0x1f;
 135  123                          want = 2;
 136  124                          lbound = 0x80;
 137  125                  } else if ((ch & 0xf0) == 0xe0) {
 138  126                          mask = 0x0f;
 139  127                          want = 3;
 140  128                          lbound = 0x800;
 141  129                  } else if ((ch & 0xf8) == 0xf0) {
 142  130                          mask = 0x07;
 143  131                          want = 4;
 144  132                          lbound = 0x10000;
 145  133  #if 0
 146  134                  /* These would be illegal in the UTF-8 space */
 147  135  
 148  136                  } else if ((ch & 0xfc) == 0xf8) {
 149  137                          mask = 0x03;
 150  138                          want = 5;
 151  139                          lbound = 0x200000;
 152  140                  } else if ((ch & 0xfe) == 0xfc) {
 153  141                          mask = 0x01;
 154  142                          want = 6;
 155  143                          lbound = 0x4000000;
 156  144  #endif
 157  145                  } else {
 158  146                          /*
 159  147                           * Malformed input; input is not UTF-8.
 160  148                           */
 161  149                          errno = EILSEQ;
 162  150                          return ((size_t)-1);
 163  151                  }
 164  152          } else {
 165  153                  want = us->want;
 166  154                  lbound = us->lbound;
 167  155          }
 168  156  
 169  157          /*
 170  158           * Decode the octet sequence representing the character in chunks
 171  159           * of 6 bits, most significant first.
 172  160           */
 173  161          if (us->want == 0)
 174  162                  wch = (unsigned char)*s++ & mask;
 175  163          else
 176  164                  wch = us->ch;
 177  165  
 178  166          for (i = (us->want == 0) ? 1 : 0; i < MIN(want, n); i++) {
 179  167                  if ((*s & 0xc0) != 0x80) {
 180  168                          /*
 181  169                           * Malformed input; bad characters in the middle
 182  170                           * of a character.
 183  171                           */
 184  172                          errno = EILSEQ;
 185  173                          return ((size_t)-1);
 186  174                  }
 187  175                  wch <<= 6;
 188  176                  wch |= *s++ & 0x3f;
 189  177          }
 190  178          if (i < want) {
 191  179                  /* Incomplete multibyte sequence. */
 192  180                  us->want = want - i;
 193  181                  us->lbound = lbound;
 194  182                  us->ch = wch;
 195  183                  return ((size_t)-2);
 196  184          }
 197  185          if (wch < lbound) {
 198  186                  /*
 199  187                   * Malformed input; redundant encoding.
 200  188                   */
 201  189                  errno = EILSEQ;
 202  190                  return ((size_t)-1);
 203  191          }
 204  192          if (pwc != NULL)
 205  193                  *pwc = wch;
 206  194          us->want = 0;
 207  195          return (wch == L'\0' ? 0 : want);
 208  196  }
 209  197  
 210  198  static size_t
 211  199  _UTF8_mbsnrtowcs(wchar_t *_RESTRICT_KYWD dst, const char **_RESTRICT_KYWD src,
 212  200      size_t nms, size_t len, mbstate_t *_RESTRICT_KYWD ps)
 213  201  {
 214  202          _UTF8State *us;
 215  203          const char *s;
 216  204          size_t nchr;
 217  205          wchar_t wc;
 218  206          size_t nb;
 219  207  
 220  208          us = (_UTF8State *)ps;
 221  209  
 222  210          s = *src;
 223  211          nchr = 0;
 224  212  
 225  213          if (dst == NULL) {
 226  214                  /*
 227  215                   * The fast path in the loop below is not safe if an ASCII
 228  216                   * character appears as anything but the first byte of a
 229  217                   * multibyte sequence. Check now to avoid doing it in the loop.
 230  218                   */
 231  219                  if (nms > 0 && us->want > 0 && (signed char)*s > 0) {
 232  220                          errno = EILSEQ;
 233  221                          return ((size_t)-1);
 234  222                  }
 235  223                  for (;;) {
 236  224                          if (nms > 0 && (signed char)*s > 0)
 237  225                                  /*
 238  226                                   * Fast path for plain ASCII characters
 239  227                                   * excluding NUL.
 240  228                                   */
 241  229                                  nb = 1;
 242  230                          else if ((nb = _UTF8_mbrtowc(&wc, s, nms, ps)) ==
 243  231                              (size_t)-1)
 244  232                                  /* Invalid sequence - mbrtowc() sets errno. */
 245  233                                  return ((size_t)-1);
 246  234                          else if (nb == 0 || nb == (size_t)-2)
 247  235                                  return (nchr);
 248  236                          s += nb;
 249  237                          nms -= nb;
 250  238                          nchr++;
 251  239                  }
 252  240                  /*NOTREACHED*/
 253  241          }
 254  242  
 255  243          /*
 256  244           * The fast path in the loop below is not safe if an ASCII
 257  245           * character appears as anything but the first byte of a
 258  246           * multibyte sequence. Check now to avoid doing it in the loop.
 259  247           */
 260  248          if (nms > 0 && len > 0 && us->want > 0 && (signed char)*s > 0) {
 261  249                  errno = EILSEQ;
 262  250                  return ((size_t)-1);
 263  251          }
 264  252          while (len-- > 0) {
 265  253                  if (nms > 0 && (signed char)*s > 0) {
 266  254                          /*
 267  255                           * Fast path for plain ASCII characters
 268  256                           * excluding NUL.
 269  257                           */
 270  258                          *dst = (wchar_t)*s;
 271  259                          nb = 1;
 272  260                  } else if ((nb = _UTF8_mbrtowc(dst, s, nms, ps)) ==
 273  261                      (size_t)-1) {
 274  262                          *src = s;
 275  263                          return ((size_t)-1);
 276  264                  } else if (nb == (size_t)-2) {
 277  265                          *src = s + nms;
 278  266                          return (nchr);
 279  267                  } else if (nb == 0) {
 280  268                          *src = NULL;
 281  269                          return (nchr);
 282  270                  }
 283  271                  s += nb;
 284  272                  nms -= nb;
 285  273                  nchr++;
 286  274                  dst++;
 287  275          }
 288  276          *src = s;
 289  277          return (nchr);
 290  278  }
 291  279  
 292  280  static size_t
 293  281  _UTF8_wcrtomb(char *_RESTRICT_KYWD s, wchar_t wc, mbstate_t *_RESTRICT_KYWD ps)
 294  282  {
 295  283          _UTF8State *us;
 296  284          unsigned char lead;
 297  285          int i, len;
 298  286  
 299  287          us = (_UTF8State *)ps;
 300  288  
 301  289          if (us->want != 0) {
 302  290                  errno = EINVAL;
 303  291                  return ((size_t)-1);
 304  292          }
 305  293  
 306  294          if (s == NULL)
 307  295                  /* Reset to initial shift state (no-op) */
 308  296                  return (1);
 309  297  
 310  298          /*
 311  299           * Determine the number of octets needed to represent this character.
 312  300           * We always output the shortest sequence possible. Also specify the
 313  301           * first few bits of the first octet, which contains the information
 314  302           * about the sequence length.
 315  303           */
 316  304          if ((wc & ~0x7f) == 0) {
 317  305                  /* Fast path for plain ASCII characters. */
 318  306                  *s = (char)wc;
 319  307                  return (1);
 320  308          } else if ((wc & ~0x7ff) == 0) {
 321  309                  lead = 0xc0;
 322  310                  len = 2;
 323  311          } else if ((wc & ~0xffff) == 0) {
 324  312                  lead = 0xe0;
 325  313                  len = 3;
 326  314          } else if ((wc & ~0x1fffff) == 0) {
 327  315                  lead = 0xf0;
 328  316                  len = 4;
 329  317  #if 0
 330  318          /* Again, 5 and 6 byte encodings are simply not permitted */
 331  319          } else if ((wc & ~0x3ffffff) == 0) {
 332  320                  lead = 0xf8;
 333  321                  len = 5;
 334  322          } else if ((wc & ~0x7fffffff) == 0) {
 335  323                  lead = 0xfc;
 336  324                  len = 6;
 337  325  #endif
 338  326          } else {
 339  327                  errno = EILSEQ;
 340  328                  return ((size_t)-1);
 341  329          }
 342  330  
 343  331          /*
 344  332           * Output the octets representing the character in chunks
 345  333           * of 6 bits, least significant last. The first octet is
 346  334           * a special case because it contains the sequence length
 347  335           * information.
 348  336           */
 349  337          for (i = len - 1; i > 0; i--) {
 350  338                  s[i] = (wc & 0x3f) | 0x80;
 351  339                  wc >>= 6;
 352  340          }
 353  341          *s = (wc & 0xff) | lead;
 354  342  
 355  343          return (len);
 356  344  }
 357  345  
 358  346  static size_t
 359  347  _UTF8_wcsnrtombs(char *_RESTRICT_KYWD dst, const wchar_t **_RESTRICT_KYWD src,
 360  348      size_t nwc, size_t len, mbstate_t *_RESTRICT_KYWD ps)
 361  349  {
 362  350          _UTF8State *us;
 363  351          char buf[MB_LEN_MAX];
 364  352          const wchar_t *s;
 365  353          size_t nbytes;
 366  354          size_t nb;
 367  355  
 368  356          us = (_UTF8State *)ps;
 369  357  
 370  358          if (us->want != 0) {
 371  359                  errno = EINVAL;
 372  360                  return ((size_t)-1);
 373  361          }
 374  362  
 375  363          s = *src;
 376  364          nbytes = 0;
 377  365  
 378  366          if (dst == NULL) {
 379  367                  while (nwc-- > 0) {
 380  368                          if (0 <= *s && *s < 0x80)
 381  369                                  /* Fast path for plain ASCII characters. */
 382  370                                  nb = 1;
 383  371                          else if ((nb = _UTF8_wcrtomb(buf, *s, ps)) ==
 384  372                              (size_t)-1)
 385  373                                  /* Invalid character - wcrtomb() sets errno. */
 386  374                                  return ((size_t)-1);
 387  375                          if (*s == L'\0')
 388  376                                  return (nbytes + nb - 1);
 389  377                          s++;
 390  378                          nbytes += nb;
 391  379                  }
 392  380                  return (nbytes);
 393  381          }
 394  382  
 395  383          while (len > 0 && nwc-- > 0) {
 396  384                  if (0 <= *s && *s < 0x80) {
 397  385                          /* Fast path for plain ASCII characters. */
 398  386                          nb = 1;
 399  387                          *dst = *s;
 400  388                  } else if (len > (size_t)MB_CUR_MAX) {
 401  389                          /* Enough space to translate in-place. */
 402  390                          if ((nb = _UTF8_wcrtomb(dst, *s, ps)) == (size_t)-1) {
 403  391                                  *src = s;
 404  392                                  return ((size_t)-1);
 405  393                          }
 406  394                  } else {
 407  395                          /*
 408  396                           * May not be enough space; use temp. buffer.
 409  397                           */
 410  398                          if ((nb = _UTF8_wcrtomb(buf, *s, ps)) == (size_t)-1) {
 411  399                                  *src = s;
 412  400                                  return ((size_t)-1);
 413  401                          }
 414  402                          if (nb > (int)len)
 415  403                                  /* MB sequence for character won't fit. */
 416  404                                  break;
 417  405                          (void) memcpy(dst, buf, nb);
 418  406                  }
 419  407                  if (*s == L'\0') {
 420  408                          *src = NULL;
 421  409                          return (nbytes + nb - 1);
 422  410                  }
 423  411                  s++;
 424  412                  dst += nb;
 425  413                  len -= nb;
 426  414                  nbytes += nb;
 427  415          }
 428  416          *src = s;
 429  417          return (nbytes);
 430  418  }
  
    | ↓ open down ↓ | 342 lines elided | ↑ open up ↑ | 
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX