Blame lib/mbswidth.c

Packit 8f70b4
/* Determine the number of screen columns needed for a string.
Packit 8f70b4
   Copyright (C) 2000-2018 Free Software Foundation, Inc.
Packit 8f70b4
Packit 8f70b4
   This program is free software: you can redistribute it and/or modify
Packit 8f70b4
   it under the terms of the GNU General Public License as published by
Packit 8f70b4
   the Free Software Foundation; either version 3 of the License, or
Packit 8f70b4
   (at your option) any later version.
Packit 8f70b4
Packit 8f70b4
   This program is distributed in the hope that it will be useful,
Packit 8f70b4
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 8f70b4
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 8f70b4
   GNU General Public License for more details.
Packit 8f70b4
Packit 8f70b4
   You should have received a copy of the GNU General Public License
Packit 8f70b4
   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
Packit 8f70b4
Packit 8f70b4
/* Written by Bruno Haible <haible@clisp.cons.org>.  */
Packit 8f70b4
Packit 8f70b4
#include <config.h>
Packit 8f70b4
Packit 8f70b4
/* Specification.  */
Packit 8f70b4
#include "mbswidth.h"
Packit 8f70b4
Packit 8f70b4
/* Get MB_CUR_MAX.  */
Packit 8f70b4
#include <stdlib.h>
Packit 8f70b4
Packit 8f70b4
#include <string.h>
Packit 8f70b4
Packit 8f70b4
/* Get isprint().  */
Packit 8f70b4
#include <ctype.h>
Packit 8f70b4
Packit 8f70b4
/* Get mbstate_t, mbrtowc(), mbsinit(), wcwidth().  */
Packit 8f70b4
#include <wchar.h>
Packit 8f70b4
Packit 8f70b4
/* Get iswcntrl().  */
Packit 8f70b4
#include <wctype.h>
Packit 8f70b4
Packit 8f70b4
/* Get INT_MAX.  */
Packit 8f70b4
#include <limits.h>
Packit 8f70b4
Packit 8f70b4
/* Returns the number of columns needed to represent the multibyte
Packit 8f70b4
   character string pointed to by STRING.  If a non-printable character
Packit 8f70b4
   occurs, and MBSW_REJECT_UNPRINTABLE is specified, -1 is returned.
Packit 8f70b4
   With flags = MBSW_REJECT_INVALID | MBSW_REJECT_UNPRINTABLE, this is
Packit 8f70b4
   the multibyte analogue of the wcswidth function.  */
Packit 8f70b4
int
Packit 8f70b4
mbswidth (const char *string, int flags)
Packit 8f70b4
{
Packit 8f70b4
  return mbsnwidth (string, strlen (string), flags);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
/* Returns the number of columns needed to represent the multibyte
Packit 8f70b4
   character string pointed to by STRING of length NBYTES.  If a
Packit 8f70b4
   non-printable character occurs, and MBSW_REJECT_UNPRINTABLE is
Packit 8f70b4
   specified, -1 is returned.  */
Packit 8f70b4
int
Packit 8f70b4
mbsnwidth (const char *string, size_t nbytes, int flags)
Packit 8f70b4
{
Packit 8f70b4
  const char *p = string;
Packit 8f70b4
  const char *plimit = p + nbytes;
Packit 8f70b4
  int width;
Packit 8f70b4
Packit 8f70b4
  width = 0;
Packit 8f70b4
  if (MB_CUR_MAX > 1)
Packit 8f70b4
    {
Packit 8f70b4
      while (p < plimit)
Packit 8f70b4
        switch (*p)
Packit 8f70b4
          {
Packit 8f70b4
            case ' ': case '!': case '"': case '#': case '%':
Packit 8f70b4
            case '&': case '\'': case '(': case ')': case '*':
Packit 8f70b4
            case '+': case ',': case '-': case '.': case '/':
Packit 8f70b4
            case '0': case '1': case '2': case '3': case '4':
Packit 8f70b4
            case '5': case '6': case '7': case '8': case '9':
Packit 8f70b4
            case ':': case ';': case '<': case '=': case '>':
Packit 8f70b4
            case '?':
Packit 8f70b4
            case 'A': case 'B': case 'C': case 'D': case 'E':
Packit 8f70b4
            case 'F': case 'G': case 'H': case 'I': case 'J':
Packit 8f70b4
            case 'K': case 'L': case 'M': case 'N': case 'O':
Packit 8f70b4
            case 'P': case 'Q': case 'R': case 'S': case 'T':
Packit 8f70b4
            case 'U': case 'V': case 'W': case 'X': case 'Y':
Packit 8f70b4
            case 'Z':
Packit 8f70b4
            case '[': case '\\': case ']': case '^': case '_':
Packit 8f70b4
            case 'a': case 'b': case 'c': case 'd': case 'e':
Packit 8f70b4
            case 'f': case 'g': case 'h': case 'i': case 'j':
Packit 8f70b4
            case 'k': case 'l': case 'm': case 'n': case 'o':
Packit 8f70b4
            case 'p': case 'q': case 'r': case 's': case 't':
Packit 8f70b4
            case 'u': case 'v': case 'w': case 'x': case 'y':
Packit 8f70b4
            case 'z': case '{': case '|': case '}': case '~':
Packit 8f70b4
              /* These characters are printable ASCII characters.  */
Packit 8f70b4
              p++;
Packit 8f70b4
              width++;
Packit 8f70b4
              break;
Packit 8f70b4
            default:
Packit 8f70b4
              /* If we have a multibyte sequence, scan it up to its end.  */
Packit 8f70b4
              {
Packit 8f70b4
                mbstate_t mbstate;
Packit 8f70b4
                memset (&mbstate, 0, sizeof mbstate);
Packit 8f70b4
                do
Packit 8f70b4
                  {
Packit 8f70b4
                    wchar_t wc;
Packit 8f70b4
                    size_t bytes;
Packit 8f70b4
                    int w;
Packit 8f70b4
Packit 8f70b4
                    bytes = mbrtowc (&wc, p, plimit - p, &mbstate);
Packit 8f70b4
Packit 8f70b4
                    if (bytes == (size_t) -1)
Packit 8f70b4
                      /* An invalid multibyte sequence was encountered.  */
Packit 8f70b4
                      {
Packit 8f70b4
                        if (!(flags & MBSW_REJECT_INVALID))
Packit 8f70b4
                          {
Packit 8f70b4
                            p++;
Packit 8f70b4
                            width++;
Packit 8f70b4
                            break;
Packit 8f70b4
                          }
Packit 8f70b4
                        else
Packit 8f70b4
                          return -1;
Packit 8f70b4
                      }
Packit 8f70b4
Packit 8f70b4
                    if (bytes == (size_t) -2)
Packit 8f70b4
                      /* An incomplete multibyte character at the end.  */
Packit 8f70b4
                      {
Packit 8f70b4
                        if (!(flags & MBSW_REJECT_INVALID))
Packit 8f70b4
                          {
Packit 8f70b4
                            p = plimit;
Packit 8f70b4
                            width++;
Packit 8f70b4
                            break;
Packit 8f70b4
                          }
Packit 8f70b4
                        else
Packit 8f70b4
                          return -1;
Packit 8f70b4
                      }
Packit 8f70b4
Packit 8f70b4
                    if (bytes == 0)
Packit 8f70b4
                      /* A null wide character was encountered.  */
Packit 8f70b4
                      bytes = 1;
Packit 8f70b4
Packit 8f70b4
                    w = wcwidth (wc);
Packit 8f70b4
                    if (w >= 0)
Packit 8f70b4
                      /* A printable multibyte character.  */
Packit 8f70b4
                      {
Packit 8f70b4
                        if (w > INT_MAX - width)
Packit 8f70b4
                          goto overflow;
Packit 8f70b4
                        width += w;
Packit 8f70b4
                      }
Packit 8f70b4
                    else
Packit 8f70b4
                      /* An unprintable multibyte character.  */
Packit 8f70b4
                      if (!(flags & MBSW_REJECT_UNPRINTABLE))
Packit 8f70b4
                        {
Packit 8f70b4
                          if (!iswcntrl (wc))
Packit 8f70b4
                            {
Packit 8f70b4
                              if (width == INT_MAX)
Packit 8f70b4
                                goto overflow;
Packit 8f70b4
                              width++;
Packit 8f70b4
                            }
Packit 8f70b4
                        }
Packit 8f70b4
                      else
Packit 8f70b4
                        return -1;
Packit 8f70b4
Packit 8f70b4
                    p += bytes;
Packit 8f70b4
                  }
Packit 8f70b4
                while (! mbsinit (&mbstate));
Packit 8f70b4
              }
Packit 8f70b4
              break;
Packit 8f70b4
          }
Packit 8f70b4
      return width;
Packit 8f70b4
    }
Packit 8f70b4
Packit 8f70b4
  while (p < plimit)
Packit 8f70b4
    {
Packit 8f70b4
      unsigned char c = (unsigned char) *p++;
Packit 8f70b4
Packit 8f70b4
      if (isprint (c))
Packit 8f70b4
        {
Packit 8f70b4
          if (width == INT_MAX)
Packit 8f70b4
            goto overflow;
Packit 8f70b4
          width++;
Packit 8f70b4
        }
Packit 8f70b4
      else if (!(flags & MBSW_REJECT_UNPRINTABLE))
Packit 8f70b4
        {
Packit 8f70b4
          if (!iscntrl (c))
Packit 8f70b4
            {
Packit 8f70b4
              if (width == INT_MAX)
Packit 8f70b4
                goto overflow;
Packit 8f70b4
              width++;
Packit 8f70b4
            }
Packit 8f70b4
        }
Packit 8f70b4
      else
Packit 8f70b4
        return -1;
Packit 8f70b4
    }
Packit 8f70b4
  return width;
Packit 8f70b4
Packit 8f70b4
 overflow:
Packit 8f70b4
  return INT_MAX;
Packit 8f70b4
}