fix-width.patch 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. diff --git a/lib/argp-fmtstream.c b/lib/argp-fmtstream.c
  2. index ba6a407f7..d0685b3d4 100644
  3. --- a/lib/argp-fmtstream.c
  4. +++ b/lib/argp-fmtstream.c
  5. @@ -28,9 +28,11 @@
  6. #include <errno.h>
  7. #include <stdarg.h>
  8. #include <ctype.h>
  9. +#include <wchar.h>
  10. #include "argp-fmtstream.h"
  11. #include "argp-namefrob.h"
  12. +#include "mbswidth.h"
  13. #ifndef ARGP_FMTSTREAM_USE_LINEWRAP
  14. @@ -115,6 +117,51 @@ weak_alias (__argp_fmtstream_free, argp_fmtstream_free)
  15. #endif
  16. #endif
  17. +
  18. +/* Return the pointer to the first character that doesn't fit in l columns. */
  19. +static inline const ptrdiff_t
  20. +add_width (const char *ptr, const char *end, size_t l)
  21. +{
  22. + mbstate_t ps;
  23. + const char *ptr0 = ptr;
  24. +
  25. + memset (&ps, 0, sizeof (ps));
  26. +
  27. + while (ptr < end)
  28. + {
  29. + wchar_t wc;
  30. + size_t s, k;
  31. +
  32. + s = mbrtowc (&wc, ptr, end - ptr, &ps);
  33. + if (s == (size_t) -1)
  34. + break;
  35. + if (s == (size_t) -2)
  36. + {
  37. + if (1 >= l)
  38. + break;
  39. + l--;
  40. + ptr++;
  41. + continue;
  42. + }
  43. +
  44. + if (wc == '\e' && ptr + 3 < end
  45. + && ptr[1] == '[' && (ptr[2] == '0' || ptr[2] == '1')
  46. + && ptr[3] == 'm')
  47. + {
  48. + ptr += 4;
  49. + continue;
  50. + }
  51. +
  52. + k = wcwidth (wc);
  53. +
  54. + if (k >= l)
  55. + break;
  56. + l -= k;
  57. + ptr += s;
  58. + }
  59. + return ptr - ptr0;
  60. +}
  61. +
  62. /* Process FS's buffer so that line wrapping is done from POINT_OFFS to the
  63. end of its buffer. This code is mostly from glibc stdio/linewrap.c. */
  64. void
  65. @@ -168,13 +215,15 @@ __argp_fmtstream_update (argp_fmtstream_t fs)
  66. if (!nl)
  67. {
  68. /* The buffer ends in a partial line. */
  69. + size_t display_width = mbsnwidth (buf, fs->p - buf,
  70. + MBSW_STOP_AT_NUL);
  71. - if (fs->point_col + len < fs->rmargin)
  72. + if (fs->point_col + display_width < fs->rmargin)
  73. {
  74. /* The remaining buffer text is a partial line and fits
  75. within the maximum line width. Advance point for the
  76. characters to be written and stop scanning. */
  77. - fs->point_col += len;
  78. + fs->point_col += display_width;
  79. break;
  80. }
  81. else
  82. @@ -182,14 +231,18 @@ __argp_fmtstream_update (argp_fmtstream_t fs)
  83. the end of the buffer. */
  84. nl = fs->p;
  85. }
  86. - else if (fs->point_col + (nl - buf) < (ssize_t) fs->rmargin)
  87. - {
  88. - /* The buffer contains a full line that fits within the maximum
  89. - line width. Reset point and scan the next line. */
  90. - fs->point_col = 0;
  91. - buf = nl + 1;
  92. - continue;
  93. - }
  94. + else
  95. + {
  96. + size_t display_width = mbsnwidth (buf, nl - buf, MBSW_STOP_AT_NUL);
  97. + if (display_width < (ssize_t) fs->rmargin)
  98. + {
  99. + /* The buffer contains a full line that fits within the maximum
  100. + line width. Reset point and scan the next line. */
  101. + fs->point_col = 0;
  102. + buf = nl + 1;
  103. + continue;
  104. + }
  105. + }
  106. /* This line is too long. */
  107. r = fs->rmargin - 1;
  108. @@ -225,7 +278,7 @@ __argp_fmtstream_update (argp_fmtstream_t fs)
  109. char *p, *nextline;
  110. int i;
  111. - p = buf + (r + 1 - fs->point_col);
  112. + p = buf + add_width (buf, fs->p, (r + 1 - fs->point_col));
  113. while (p >= buf && !isblank ((unsigned char) *p))
  114. --p;
  115. nextline = p + 1; /* This will begin the next line. */
  116. @@ -243,7 +296,7 @@ __argp_fmtstream_update (argp_fmtstream_t fs)
  117. {
  118. /* A single word that is greater than the maximum line width.
  119. Oh well. Put it on an overlong line by itself. */
  120. - p = buf + (r + 1 - fs->point_col);
  121. + p = buf + add_width (buf, fs->p, (r + 1 - fs->point_col));
  122. /* Find the end of the long word. */
  123. if (p < nl)
  124. do
  125. @@ -277,7 +330,8 @@ __argp_fmtstream_update (argp_fmtstream_t fs)
  126. && fs->p > nextline)
  127. {
  128. /* The margin needs more blanks than we removed. */
  129. - if (fs->end - fs->p > fs->wmargin + 1)
  130. + if (mbsnwidth (fs->p, fs->end - fs->p, MBSW_STOP_AT_NUL)
  131. + > fs->wmargin + 1)
  132. /* Make some space for them. */
  133. {
  134. size_t mv = fs->p - nextline;
  135. diff --git a/lib/argp-help.c b/lib/argp-help.c
  136. index e5375a0f0..5d8f451ec 100644
  137. --- a/lib/argp-help.c
  138. +++ b/lib/argp-help.c
  139. @@ -52,6 +52,7 @@
  140. #include "argp.h"
  141. #include "argp-fmtstream.h"
  142. #include "argp-namefrob.h"
  143. +#include "mbswidth.h"
  144. #ifndef SIZE_MAX
  145. # define SIZE_MAX ((size_t) -1)
  146. @@ -1547,7 +1548,7 @@ argp_args_usage (const struct argp *argp, const struct argp_state *state,
  147. /* Manually do line wrapping so that it (probably) won't get wrapped at
  148. any embedded spaces. */
  149. - space (stream, 1 + nl - cp);
  150. + space (stream, 1 + mbsnwidth (cp, nl - cp, MBSW_STOP_AT_NUL));
  151. __argp_fmtstream_write (stream, cp, nl - cp);
  152. }
  153. diff --git a/lib/mbswidth.c b/lib/mbswidth.c
  154. index 408a15e34..b3fb7f83a 100644
  155. --- a/lib/mbswidth.c
  156. +++ b/lib/mbswidth.c
  157. @@ -38,6 +38,14 @@
  158. /* Get INT_MAX. */
  159. #include <limits.h>
  160. +#ifndef FALLTHROUGH
  161. +# if __GNUC__ < 7
  162. +# define FALLTHROUGH ((void) 0)
  163. +# else
  164. +# define FALLTHROUGH __attribute__ ((__fallthrough__))
  165. +# endif
  166. +#endif
  167. +
  168. /* Returns the number of columns needed to represent the multibyte
  169. character string pointed to by STRING. If a non-printable character
  170. occurs, and MBSW_REJECT_UNPRINTABLE is specified, -1 is returned.
  171. @@ -90,6 +98,10 @@ mbsnwidth (const char *string, size_t nbytes, int flags)
  172. p++;
  173. width++;
  174. break;
  175. + case '\0':
  176. + if (flags & MBSW_STOP_AT_NUL)
  177. + return width;
  178. + FALLTHROUGH;
  179. default:
  180. /* If we have a multibyte sequence, scan it up to its end. */
  181. {
  182. @@ -168,6 +180,9 @@ mbsnwidth (const char *string, size_t nbytes, int flags)
  183. {
  184. unsigned char c = (unsigned char) *p++;
  185. + if (c == 0 && (flags & MBSW_STOP_AT_NUL))
  186. + return width;
  187. +
  188. if (isprint (c))
  189. {
  190. if (width == INT_MAX)
  191. diff --git a/lib/mbswidth.h b/lib/mbswidth.h
  192. index 2b5c53c37..45a123e63 100644
  193. --- a/lib/mbswidth.h
  194. +++ b/lib/mbswidth.h
  195. @@ -40,6 +40,10 @@ extern "C" {
  196. control characters and 1 otherwise. */
  197. #define MBSW_REJECT_UNPRINTABLE 2
  198. +/* If this bit is set \0 is treated as the end of string.
  199. + Otherwise it's treated as a normal one column width character. */
  200. +#define MBSW_STOP_AT_NUL 4
  201. +
  202. /* Returns the number of screen columns needed for STRING. */
  203. #define mbswidth gnu_mbswidth /* avoid clash with UnixWare 7.1.1 function */