mbswidth.c 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. /* Determine the number of screen columns needed for a string.
  2. Copyright (C) 2000-2013 Free Software Foundation, Inc.
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation; either version 3 of the License, or
  6. (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>. */
  13. /* Written by Bruno Haible <haible@clisp.cons.org>. */
  14. #include <config.h>
  15. /* Specification. */
  16. #include "mbswidth.h"
  17. /* Get MB_CUR_MAX. */
  18. #include <stdlib.h>
  19. #include <string.h>
  20. /* Get isprint(). */
  21. #include <ctype.h>
  22. /* Get mbstate_t, mbrtowc(), mbsinit(), wcwidth(). */
  23. #include "wchar--.h"
  24. /* Get iswcntrl(). */
  25. #include <wctype.h>
  26. /* Get INT_MAX. */
  27. #include <limits.h>
  28. /* Returns the number of columns needed to represent the multibyte
  29. character string pointed to by STRING. If a non-printable character
  30. occurs, and MBSW_REJECT_UNPRINTABLE is specified, -1 is returned.
  31. With flags = MBSW_REJECT_INVALID | MBSW_REJECT_UNPRINTABLE, this is
  32. the multibyte analogue of the wcswidth function. */
  33. int
  34. mbswidth (const char *string, int flags)
  35. {
  36. return mbsnwidth (string, strlen (string), flags);
  37. }
  38. /* Returns the number of columns needed to represent the multibyte
  39. character string pointed to by STRING of length NBYTES. If a
  40. non-printable character occurs, and MBSW_REJECT_UNPRINTABLE is
  41. specified, -1 is returned. */
  42. int
  43. mbsnwidth (const char *string, size_t nbytes, int flags)
  44. {
  45. const char *p = string;
  46. const char *plimit = p + nbytes;
  47. int width;
  48. width = 0;
  49. if (MB_CUR_MAX > 1)
  50. {
  51. while (p < plimit)
  52. switch (*p)
  53. {
  54. case ' ': case '!': case '"': case '#': case '%':
  55. case '&': case '\'': case '(': case ')': case '*':
  56. case '+': case ',': case '-': case '.': case '/':
  57. case '0': case '1': case '2': case '3': case '4':
  58. case '5': case '6': case '7': case '8': case '9':
  59. case ':': case ';': case '<': case '=': case '>':
  60. case '?':
  61. case 'A': case 'B': case 'C': case 'D': case 'E':
  62. case 'F': case 'G': case 'H': case 'I': case 'J':
  63. case 'K': case 'L': case 'M': case 'N': case 'O':
  64. case 'P': case 'Q': case 'R': case 'S': case 'T':
  65. case 'U': case 'V': case 'W': case 'X': case 'Y':
  66. case 'Z':
  67. case '[': case '\\': case ']': case '^': case '_':
  68. case 'a': case 'b': case 'c': case 'd': case 'e':
  69. case 'f': case 'g': case 'h': case 'i': case 'j':
  70. case 'k': case 'l': case 'm': case 'n': case 'o':
  71. case 'p': case 'q': case 'r': case 's': case 't':
  72. case 'u': case 'v': case 'w': case 'x': case 'y':
  73. case 'z': case '{': case '|': case '}': case '~':
  74. /* These characters are printable ASCII characters. */
  75. p++;
  76. width++;
  77. break;
  78. default:
  79. /* If we have a multibyte sequence, scan it up to its end. */
  80. {
  81. mbstate_t mbstate;
  82. memset (&mbstate, 0, sizeof mbstate);
  83. do
  84. {
  85. wchar_t wc;
  86. size_t bytes;
  87. int w;
  88. bytes = mbrtowc (&wc, p, plimit - p, &mbstate);
  89. if (bytes == (size_t) -1)
  90. /* An invalid multibyte sequence was encountered. */
  91. {
  92. if (!(flags & MBSW_REJECT_INVALID))
  93. {
  94. p++;
  95. width++;
  96. break;
  97. }
  98. else
  99. return -1;
  100. }
  101. if (bytes == (size_t) -2)
  102. /* An incomplete multibyte character at the end. */
  103. {
  104. if (!(flags & MBSW_REJECT_INVALID))
  105. {
  106. p = plimit;
  107. width++;
  108. break;
  109. }
  110. else
  111. return -1;
  112. }
  113. if (bytes == 0)
  114. /* A null wide character was encountered. */
  115. bytes = 1;
  116. w = wcwidth (wc);
  117. if (w >= 0)
  118. /* A printable multibyte character. */
  119. {
  120. if (w > INT_MAX - width)
  121. goto overflow;
  122. width += w;
  123. }
  124. else
  125. /* An unprintable multibyte character. */
  126. if (!(flags & MBSW_REJECT_UNPRINTABLE))
  127. {
  128. if (!iswcntrl (wc))
  129. {
  130. if (width == INT_MAX)
  131. goto overflow;
  132. width++;
  133. }
  134. }
  135. else
  136. return -1;
  137. p += bytes;
  138. }
  139. while (! mbsinit (&mbstate));
  140. }
  141. break;
  142. }
  143. return width;
  144. }
  145. while (p < plimit)
  146. {
  147. unsigned char c = (unsigned char) *p++;
  148. if (isprint (c))
  149. {
  150. if (width == INT_MAX)
  151. goto overflow;
  152. width++;
  153. }
  154. else if (!(flags & MBSW_REJECT_UNPRINTABLE))
  155. {
  156. if (!iscntrl (c))
  157. {
  158. if (width == INT_MAX)
  159. goto overflow;
  160. width++;
  161. }
  162. }
  163. else
  164. return -1;
  165. }
  166. return width;
  167. overflow:
  168. return INT_MAX;
  169. }