*** empty log message ***
[gnulib.git] / lib / linebreak.c
1 /* linebreak.c - line breaking of Unicode strings
2    Copyright (C) 2001-2003 Free Software Foundation, Inc.
3    Written by Bruno Haible <haible@clisp.cons.org>, 2001.
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2, or (at your option)
8 any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
18
19 #ifdef HAVE_CONFIG_H
20 # include <config.h>
21 #endif
22
23 /* Specification.  */
24 #include "linebreak.h"
25
26 #include <stdlib.h>
27 #include <string.h>
28 #include "c-ctype.h"
29 #include "xsize.h"
30
31 #include "utf8-ucs4.h"
32
33 #include "utf16-ucs4.h"
34
35 #ifdef unused
36 static inline int
37 u32_mbtouc (unsigned int *puc, const unsigned int *s, size_t n)
38 {
39   *puc = *s;
40   return 1;
41 }
42 #endif
43
44
45 /* Help GCC to generate good code for string comparisons with
46    immediate strings. */
47 #if defined (__GNUC__) && defined (__OPTIMIZE__)
48
49 static inline int
50 streq9 (const char *s1, const char *s2)
51 {
52   return strcmp (s1 + 9, s2 + 9) == 0;
53 }
54
55 static inline int
56 streq8 (const char *s1, const char *s2, char s28)
57 {
58   if (s1[8] == s28)
59     {
60       if (s28 == 0)
61         return 1;
62       else
63         return streq9 (s1, s2);
64     }
65   else
66     return 0;
67 }
68
69 static inline int
70 streq7 (const char *s1, const char *s2, char s27, char s28)
71 {
72   if (s1[7] == s27)
73     {
74       if (s27 == 0)
75         return 1;
76       else
77         return streq8 (s1, s2, s28);
78     }
79   else
80     return 0;
81 }
82
83 static inline int
84 streq6 (const char *s1, const char *s2, char s26, char s27, char s28)
85 {
86   if (s1[6] == s26)
87     {
88       if (s26 == 0)
89         return 1;
90       else
91         return streq7 (s1, s2, s27, s28);
92     }
93   else
94     return 0;
95 }
96
97 static inline int
98 streq5 (const char *s1, const char *s2, char s25, char s26, char s27, char s28)
99 {
100   if (s1[5] == s25)
101     {
102       if (s25 == 0)
103         return 1;
104       else
105         return streq6 (s1, s2, s26, s27, s28);
106     }
107   else
108     return 0;
109 }
110
111 static inline int
112 streq4 (const char *s1, const char *s2, char s24, char s25, char s26, char s27, char s28)
113 {
114   if (s1[4] == s24)
115     {
116       if (s24 == 0)
117         return 1;
118       else
119         return streq5 (s1, s2, s25, s26, s27, s28);
120     }
121   else
122     return 0;
123 }
124
125 static inline int
126 streq3 (const char *s1, const char *s2, char s23, char s24, char s25, char s26, char s27, char s28)
127 {
128   if (s1[3] == s23)
129     {
130       if (s23 == 0)
131         return 1;
132       else
133         return streq4 (s1, s2, s24, s25, s26, s27, s28);
134     }
135   else
136     return 0;
137 }
138
139 static inline int
140 streq2 (const char *s1, const char *s2, char s22, char s23, char s24, char s25, char s26, char s27, char s28)
141 {
142   if (s1[2] == s22)
143     {
144       if (s22 == 0)
145         return 1;
146       else
147         return streq3 (s1, s2, s23, s24, s25, s26, s27, s28);
148     }
149   else
150     return 0;
151 }
152
153 static inline int
154 streq1 (const char *s1, const char *s2, char s21, char s22, char s23, char s24, char s25, char s26, char s27, char s28)
155 {
156   if (s1[1] == s21)
157     {
158       if (s21 == 0)
159         return 1;
160       else
161         return streq2 (s1, s2, s22, s23, s24, s25, s26, s27, s28);
162     }
163   else
164     return 0;
165 }
166
167 static inline int
168 streq0 (const char *s1, const char *s2, char s20, char s21, char s22, char s23, char s24, char s25, char s26, char s27, char s28)
169 {
170   if (s1[0] == s20)
171     {
172       if (s20 == 0)
173         return 1;
174       else
175         return streq1 (s1, s2, s21, s22, s23, s24, s25, s26, s27, s28);
176     }
177   else
178     return 0;
179 }
180
181 #define STREQ(s1,s2,s20,s21,s22,s23,s24,s25,s26,s27,s28) \
182   streq0 (s1, s2, s20, s21, s22, s23, s24, s25, s26, s27, s28)
183
184 #else
185
186 #define STREQ(s1,s2,s20,s21,s22,s23,s24,s25,s26,s27,s28) \
187   (strcmp (s1, s2) == 0)
188
189 #endif
190
191
192 static int
193 is_cjk_encoding (const char *encoding)
194 {
195   if (0
196       /* Legacy Japanese encodings */
197       || STREQ (encoding, "EUC-JP", 'E', 'U', 'C', '-', 'J', 'P', 0, 0, 0)
198       /* Legacy Chinese encodings */
199       || STREQ (encoding, "GB2312", 'G', 'B', '2', '3', '1', '2', 0, 0, 0)
200       || STREQ (encoding, "GBK", 'G', 'B', 'K', 0, 0, 0, 0, 0, 0)
201       || STREQ (encoding, "EUC-TW", 'E', 'U', 'C', '-', 'T', 'W', 0, 0, 0)
202       || STREQ (encoding, "BIG5", 'B', 'I', 'G', '5', 0, 0, 0, 0, 0)
203       /* Legacy Korean encodings */
204       || STREQ (encoding, "EUC-KR", 'E', 'U', 'C', '-', 'K', 'R', 0, 0, 0)
205       || STREQ (encoding, "CP949", 'C', 'P', '9', '4', '9', 0, 0, 0, 0)
206       || STREQ (encoding, "JOHAB", 'J', 'O', 'H', 'A', 'B', 0, 0, 0, 0))
207     return 1;
208   return 0;
209 }
210
211 static int
212 is_utf8_encoding (const char *encoding)
213 {
214   if (STREQ (encoding, "UTF-8", 'U', 'T', 'F', '-', '8', 0, 0, 0 ,0))
215     return 1;
216   return 0;
217 }
218
219
220 /* Determine number of column positions required for UC. */
221 int uc_width (unsigned int uc, const char *encoding);
222
223 /*
224  * Non-spacing attribute table.
225  * Consists of:
226  * - Non-spacing characters; generated from PropList.txt or
227  *   "grep '^[^;]*;[^;]*;[^;]*;[^;]*;NSM;' UnicodeData.txt"
228  * - Format control characters; generated from
229  *   "grep '^[^;]*;[^;]*;Cf;' UnicodeData.txt"
230  * - Zero width characters; generated from
231  *   "grep '^[^;]*;ZERO WIDTH ' UnicodeData.txt"
232  */
233 static const unsigned char nonspacing_table_data[16*64] = {
234   /* 0x0000-0x01ff */
235   0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, /* 0x0000-0x003f */
236   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, /* 0x0040-0x007f */
237   0xff, 0xff, 0xff, 0xff, 0x00, 0x20, 0x00, 0x00, /* 0x0080-0x00bf */
238   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00c0-0x00ff */
239   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0100-0x013f */
240   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0140-0x017f */
241   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0180-0x01bf */
242   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x01c0-0x01ff */
243   /* 0x0200-0x03ff */
244   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0200-0x023f */
245   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0240-0x027f */
246   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0280-0x02bf */
247   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x02c0-0x02ff */
248   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x0300-0x033f */
249   0xff, 0xff, 0xff, 0xe0, 0xff, 0xff, 0x00, 0x00, /* 0x0340-0x037f */
250   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0380-0x03bf */
251   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x03c0-0x03ff */
252   /* 0x0400-0x05ff */
253   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0400-0x043f */
254   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0440-0x047f */
255   0x78, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0480-0x04bf */
256   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04c0-0x04ff */
257   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0500-0x053f */
258   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0540-0x057f */
259   0x00, 0x00, 0xfe, 0xff, 0xfb, 0xff, 0xff, 0xbb, /* 0x0580-0x05bf */
260   0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x05c0-0x05ff */
261   /* 0x0600-0x07ff */
262   0x0f, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0600-0x063f */
263   0x00, 0xf8, 0xff, 0x01, 0x00, 0x00, 0x01, 0x00, /* 0x0640-0x067f */
264   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0680-0x06bf */
265   0x00, 0x00, 0xc0, 0xff, 0x9f, 0x3d, 0x00, 0x00, /* 0x06c0-0x06ff */
266   0x00, 0x80, 0x02, 0x00, 0x00, 0x00, 0xff, 0xff, /* 0x0700-0x073f */
267   0xff, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0740-0x077f */
268   0x00, 0x00, 0x00, 0x00, 0xc0, 0xff, 0x01, 0x00, /* 0x0780-0x07bf */
269   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x07c0-0x07ff */
270   /* 0x0800-0x09ff */
271   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0800-0x083f */
272   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0840-0x087f */
273   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0880-0x08bf */
274   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08c0-0x08ff */
275   0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, /* 0x0900-0x093f */
276   0xfe, 0x21, 0x1e, 0x00, 0x0c, 0x00, 0x00, 0x00, /* 0x0940-0x097f */
277   0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, /* 0x0980-0x09bf */
278   0x1e, 0x20, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, /* 0x09c0-0x09ff */
279   /* 0x0a00-0x0bff */
280   0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, /* 0x0a00-0x0a3f */
281   0x86, 0x39, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, /* 0x0a40-0x0a7f */
282   0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, /* 0x0a80-0x0abf */
283   0xbe, 0x21, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, /* 0x0ac0-0x0aff */
284   0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, /* 0x0b00-0x0b3f */
285   0x0e, 0x20, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0b40-0x0b7f */
286   0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0b80-0x0bbf */
287   0x01, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0bc0-0x0bff */
288   /* 0x0c00-0x0dff */
289   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, /* 0x0c00-0x0c3f */
290   0xc1, 0x3d, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0c40-0x0c7f */
291   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, /* 0x0c80-0x0cbf */
292   0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0cc0-0x0cff */
293   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0d00-0x0d3f */
294   0x0e, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0d40-0x0d7f */
295   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0d80-0x0dbf */
296   0x00, 0x04, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0dc0-0x0dff */
297   /* 0x0e00-0x0fff */
298   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf2, 0x07, /* 0x0e00-0x0e3f */
299   0x80, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0e40-0x0e7f */
300   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf2, 0x1b, /* 0x0e80-0x0ebf */
301   0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0ec0-0x0eff */
302   0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0xa0, 0x02, /* 0x0f00-0x0f3f */
303   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x7f, /* 0x0f40-0x0f7f */
304   0xdf, 0x00, 0xff, 0xfe, 0xff, 0xff, 0xff, 0x1f, /* 0x0f80-0x0fbf */
305   0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0fc0-0x0fff */
306   /* 0x1000-0x11ff */
307   0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xc5, 0x02, /* 0x1000-0x103f */
308   0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, /* 0x1040-0x107f */
309   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1080-0x10bf */
310   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10c0-0x10ff */
311   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1100-0x113f */
312   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1140-0x117f */
313   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1180-0x11bf */
314   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x11c0-0x11ff */
315   /* 0x1600-0x17ff */
316   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1600-0x163f */
317   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1640-0x167f */
318   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1680-0x16bf */
319   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x16c0-0x16ff */
320   0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x1c, 0x00, /* 0x1700-0x173f */
321   0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, /* 0x1740-0x177f */
322   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb0, 0x3f, /* 0x1780-0x17bf */
323   0x40, 0xfe, 0x0f, 0x20, 0x00, 0x00, 0x00, 0x00, /* 0x17c0-0x17ff */
324   /* 0x1800-0x19ff */
325   0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1800-0x183f */
326   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1840-0x187f */
327   0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, /* 0x1880-0x18bf */
328   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18c0-0x18ff */
329   0x00, 0x00, 0x00, 0x00, 0x87, 0x0f, 0x04, 0x0e, /* 0x1900-0x193f */
330   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1940-0x197f */
331   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1980-0x19bf */
332   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x19c0-0x19ff */
333   /* 0x2000-0x21ff */
334   0x00, 0xf8, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, /* 0x2000-0x203f */
335   0x00, 0x00, 0x00, 0x00, 0x0f, 0xfc, 0x00, 0x00, /* 0x2040-0x207f */
336   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2080-0x20bf */
337   0x00, 0x00, 0xff, 0xff, 0xff, 0x07, 0x00, 0x00, /* 0x20c0-0x20ff */
338   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2100-0x213f */
339   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2140-0x217f */
340   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2180-0x21bf */
341   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x21c0-0x21ff */
342   /* 0x3000-0x31ff */
343   0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, /* 0x3000-0x303f */
344   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3040-0x307f */
345   0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, /* 0x3080-0x30bf */
346   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30c0-0x30ff */
347   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3100-0x313f */
348   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3140-0x317f */
349   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3180-0x31bf */
350   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x31c0-0x31ff */
351   /* 0xfa00-0xfbff */
352   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xfa00-0xfa3f */
353   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xfa40-0xfa7f */
354   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xfa80-0xfabf */
355   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xfac0-0xfaff */
356   0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, /* 0xfb00-0xfb3f */
357   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xfb40-0xfb7f */
358   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xfb80-0xfbbf */
359   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xfbc0-0xfbff */
360   /* 0xfe00-0xffff */
361   0xff, 0xff, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, /* 0xfe00-0xfe3f */
362   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xfe40-0xfe7f */
363   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xfe80-0xfebf */
364   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, /* 0xfec0-0xfeff */
365   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xff00-0xff3f */
366   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xff40-0xff7f */
367   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xff80-0xffbf */
368   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, /* 0xffc0-0xffff */
369   /* 0x1d000-0x1d1ff */
370   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1d000-0x1d03f */
371   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1d040-0x1d07f */
372   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1d080-0x1d0bf */
373   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1d0c0-0x1d0ff */
374   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1d100-0x1d13f */
375   0x00, 0x00, 0x00, 0x00, 0x80, 0x03, 0x00, 0xf8, /* 0x1d140-0x1d17f */
376   0xe7, 0x0f, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, /* 0x1d180-0x1d1bf */
377   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  /* 0x1d1c0-0x1d1ff */
378 };
379 static const signed char nonspacing_table_ind[240] = {
380    0,  1,  2,  3,  4,  5,  6,  7, /* 0x0000-0x0fff */
381    8, -1, -1,  9, 10, -1, -1, -1, /* 0x1000-0x1fff */
382   11, -1, -1, -1, -1, -1, -1, -1, /* 0x2000-0x2fff */
383   12, -1, -1, -1, -1, -1, -1, -1, /* 0x3000-0x3fff */
384   -1, -1, -1, -1, -1, -1, -1, -1, /* 0x4000-0x4fff */
385   -1, -1, -1, -1, -1, -1, -1, -1, /* 0x5000-0x5fff */
386   -1, -1, -1, -1, -1, -1, -1, -1, /* 0x6000-0x6fff */
387   -1, -1, -1, -1, -1, -1, -1, -1, /* 0x7000-0x7fff */
388   -1, -1, -1, -1, -1, -1, -1, -1, /* 0x8000-0x8fff */
389   -1, -1, -1, -1, -1, -1, -1, -1, /* 0x9000-0x9fff */
390   -1, -1, -1, -1, -1, -1, -1, -1, /* 0xa000-0xafff */
391   -1, -1, -1, -1, -1, -1, -1, -1, /* 0xb000-0xbfff */
392   -1, -1, -1, -1, -1, -1, -1, -1, /* 0xc000-0xcfff */
393   -1, -1, -1, -1, -1, -1, -1, -1, /* 0xd000-0xdfff */
394   -1, -1, -1, -1, -1, -1, -1, -1, /* 0xe000-0xefff */
395   -1, -1, -1, -1, -1, 13, -1, 14, /* 0xf000-0xffff */
396   -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10000-0x10fff */
397   -1, -1, -1, -1, -1, -1, -1, -1, /* 0x11000-0x11fff */
398   -1, -1, -1, -1, -1, -1, -1, -1, /* 0x12000-0x12fff */
399   -1, -1, -1, -1, -1, -1, -1, -1, /* 0x13000-0x13fff */
400   -1, -1, -1, -1, -1, -1, -1, -1, /* 0x14000-0x14fff */
401   -1, -1, -1, -1, -1, -1, -1, -1, /* 0x15000-0x15fff */
402   -1, -1, -1, -1, -1, -1, -1, -1, /* 0x16000-0x16fff */
403   -1, -1, -1, -1, -1, -1, -1, -1, /* 0x17000-0x17fff */
404   -1, -1, -1, -1, -1, -1, -1, -1, /* 0x18000-0x18fff */
405   -1, -1, -1, -1, -1, -1, -1, -1, /* 0x19000-0x19fff */
406   -1, -1, -1, -1, -1, -1, -1, -1, /* 0x1a000-0x1afff */
407   -1, -1, -1, -1, -1, -1, -1, -1, /* 0x1b000-0x1bfff */
408   -1, -1, -1, -1, -1, -1, -1, -1, /* 0x1c000-0x1cfff */
409   15, -1, -1, -1, -1, -1, -1, -1  /* 0x1d000-0x1dfff */
410 };
411
412 /* Determine number of column positions required for UC. */
413 int
414 uc_width (unsigned int uc, const char *encoding)
415 {
416   /* Test for non-spacing or control character.  */
417   if ((uc >> 9) < 240)
418     {
419       int ind = nonspacing_table_ind[uc >> 9];
420       if (ind >= 0)
421         if ((nonspacing_table_data[64*ind + ((uc >> 3) & 63)] >> (uc & 7)) & 1)
422           {
423             if (uc > 0 && uc < 0xa0)
424               return -1;
425             else
426               return 0;
427           }
428     }
429   else if ((uc >> 9) == (0xe0000 >> 9))
430     {
431       if (uc < 0xe0100
432           ? (uc >= 0xe0020 ? uc <= 0xe007f : uc == 0xe0001)
433           : (uc <= 0xe01ef))
434         return 0;
435     }
436   /* Test for double-width character.
437    * Generated from "grep '^....;[WF]' EastAsianWidth.txt"
438    * and            "grep '^....;[^WF]' EastAsianWidth.txt"
439    */
440   if (uc >= 0x1100
441       && ((uc < 0x1160) /* Hangul Jamo */
442           || (uc >= 0x2e80 && uc < 0x4dc0  /* CJK */
443               && !(uc == 0x303f))
444           || (uc >= 0x4e00 && uc < 0xa4d0) /* CJK ... Yi */
445           || (uc >= 0xac00 && uc < 0xd7a4) /* Hangul Syllables */
446           || (uc >= 0xf900 && uc < 0xfb00) /* CJK Compatibility Ideographs */
447           || (uc >= 0xfe30 && uc < 0xfe70) /* CJK Compatibility Forms */
448           || (uc >= 0xff00 && uc < 0xff61) /* Fullwidth Forms */
449           || (uc >= 0xffe0 && uc < 0xffe7)
450           || (uc >= 0x20000 && uc <= 0x2fffd) /* CJK, CJK Compatibility Ideographs */
451           || (uc >= 0x30000 && uc <= 0x3fffd)
452      )   )
453     return 2;
454   /* In ancient CJK encodings, Cyrillic and most other characters are
455      double-width as well.  */
456   if (uc >= 0x00A1 && uc < 0xFF61 && uc != 0x20A9
457       && is_cjk_encoding (encoding))
458     return 2;
459   return 1;
460 }
461
462
463 /* Determine number of column positions required for first N units
464    (or fewer if S ends before this) in S.  */
465
466 int
467 u8_width (const unsigned char *s, size_t n, const char *encoding)
468 {
469   const unsigned char *s_end = s + n;
470   int width = 0;
471
472   while (s < s_end)
473     {
474       unsigned int uc;
475       int w;
476
477       s += u8_mbtouc (&uc, s, s_end - s);
478
479       if (uc == 0)
480         break; /* end of string reached */
481
482       w = uc_width (uc, encoding);
483       if (w >= 0) /* ignore control characters in the string */
484         width += w;
485     }
486
487   return width;
488 }
489
490 int
491 u16_width (const unsigned short *s, size_t n, const char *encoding)
492 {
493   const unsigned short *s_end = s + n;
494   int width = 0;
495
496   while (s < s_end)
497     {
498       unsigned int uc;
499       int w;
500
501       s += u16_mbtouc (&uc, s, s_end - s);
502
503       if (uc == 0)
504         break; /* end of string reached */
505
506       w = uc_width (uc, encoding);
507       if (w >= 0) /* ignore control characters in the string */
508         width += w;
509     }
510
511   return width;
512 }
513
514 int
515 u32_width (const unsigned int *s, size_t n, const char *encoding)
516 {
517   const unsigned int *s_end = s + n;
518   int width = 0;
519
520   while (s < s_end)
521     {
522       unsigned int uc = *s++;
523       int w;
524
525       if (uc == 0)
526         break; /* end of string reached */
527
528       w = uc_width (uc, encoding);
529       if (w >= 0) /* ignore control characters in the string */
530         width += w;
531     }
532
533   return width;
534 }
535
536
537 /* Determine the line break points in S, and store the result at p[0..n-1].  */
538 /* We don't support line breaking of complex-context dependent characters
539    (Thai, Lao, Myanmar, Khmer) yet, because it requires dictionary lookup. */
540
541 /* Line breaking classification.  */
542
543 enum
544 {
545   /* Values >= 20 are resolved at run time. */
546   LBP_BK =  0, /* mandatory break */
547 /*LBP_CR,         carriage return - not used here because it's a DOSism */
548 /*LBP_LF,         line feed - not used here because it's a DOSism */
549   LBP_CM = 20, /* attached characters and combining marks */
550 /*LBP_SG,         surrogates - not used here because they are not characters */
551   LBP_ZW =  1, /* zero width space */
552   LBP_IN =  2, /* inseparable */
553   LBP_GL =  3, /* non-breaking (glue) */
554   LBP_CB = 22, /* contingent break opportunity */
555   LBP_SP = 21, /* space */
556   LBP_BA =  4, /* break opportunity after */
557   LBP_BB =  5, /* break opportunity before */
558   LBP_B2 =  6, /* break opportunity before and after */
559   LBP_HY =  7, /* hyphen */
560   LBP_NS =  8, /* non starter */
561   LBP_OP =  9, /* opening punctuation */
562   LBP_CL = 10, /* closing punctuation */
563   LBP_QU = 11, /* ambiguous quotation */
564   LBP_EX = 12, /* exclamation/interrogation */
565   LBP_ID = 13, /* ideographic */
566   LBP_NU = 14, /* numeric */
567   LBP_IS = 15, /* infix separator (numeric) */
568   LBP_SY = 16, /* symbols allowing breaks */
569   LBP_AL = 17, /* ordinary alphabetic and symbol characters */
570   LBP_PR = 18, /* prefix (numeric) */
571   LBP_PO = 19, /* postfix (numeric) */
572   LBP_SA = 23, /* complex context (South East Asian) */
573   LBP_AI = 24, /* ambiguous (alphabetic or ideograph) */
574   LBP_XX = 25  /* unknown */
575 };
576
577 #include "lbrkprop.h"
578
579 static inline unsigned char
580 lbrkprop_lookup (unsigned int uc)
581 {
582   unsigned int index1 = uc >> lbrkprop_header_0;
583   if (index1 < lbrkprop_header_1)
584     {
585       int lookup1 = lbrkprop.level1[index1];
586       if (lookup1 >= 0)
587         {
588           unsigned int index2 = (uc >> lbrkprop_header_2) & lbrkprop_header_3;
589           int lookup2 = lbrkprop.level2[lookup1 + index2];
590           if (lookup2 >= 0)
591             {
592               unsigned int index3 = uc & lbrkprop_header_4;
593               return lbrkprop.level3[lookup2 + index3];
594             }
595         }
596     }
597   return LBP_XX;
598 }
599
600 /* Table indexed by two line breaking classifications.  */
601 #define D 1  /* direct break opportunity, empty in table 7.3 of UTR #14 */
602 #define I 2  /* indirect break opportunity, '%' in table 7.3 of UTR #14 */
603 #define P 3  /* prohibited break,           '^' in table 7.3 of UTR #14 */
604 static const unsigned char lbrk_table[19][19] = {
605                                 /* after */
606         /* ZW IN GL BA BB B2 HY NS OP CL QU EX ID NU IS SY AL PR PO */
607 /* ZW */ { P, D, D, D, D, D, D, D, D, D, D, D, D, D, D, D, D, D, D, },
608 /* IN */ { P, I, I, I, D, D, I, I, D, P, I, P, D, D, P, P, D, D, D, },
609 /* GL */ { P, I, I, I, I, I, I, I, I, P, I, P, I, I, P, P, I, I, I, },
610 /* BA */ { P, D, I, I, D, D, I, I, D, P, I, P, D, D, P, P, D, D, D, },
611 /* BB */ { P, I, I, I, I, I, I, I, I, P, I, P, I, I, P, P, I, I, I, },
612 /* B2 */ { P, D, I, I, D, P, I, I, D, P, I, P, D, D, P, P, D, D, D, },
613 /* HY */ { P, D, I, I, D, D, I, I, D, P, I, P, D, D, P, P, D, D, D, },
614 /* NS */ { P, D, I, I, D, D, I, I, D, P, I, P, D, D, P, P, D, D, D, },
615 /* OP */ { P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, },
616 /* CL */ { P, D, I, I, D, D, I, P, D, P, I, P, D, D, P, P, D, D, I, },
617 /* QU */ { P, I, I, I, I, I, I, I, P, P, I, P, I, I, P, P, I, I, I, },
618 /* EX */ { P, D, I, I, D, D, I, I, D, P, I, P, D, D, P, P, D, D, D, },
619 /* ID */ { P, I, I, I, D, D, I, I, D, P, I, P, D, D, P, P, D, D, I, },
620 /* NU */ { P, I, I, I, D, D, I, I, D, P, I, P, D, I, P, P, I, D, I, },
621 /* IS */ { P, D, I, I, D, D, I, I, D, P, I, P, D, I, P, P, D, D, D, },
622 /* SY */ { P, D, I, I, D, D, I, I, D, P, I, P, D, I, P, P, D, D, D, },
623 /* AL */ { P, I, I, I, D, D, I, I, D, P, I, P, D, I, P, P, I, D, D, },
624 /* PR */ { P, D, I, I, D, D, I, I, I, P, I, P, I, I, P, P, I, D, D, },
625 /* PO */ { P, D, I, I, D, D, I, I, D, P, I, P, D, D, P, P, D, D, D, },
626 /* "" */
627 /* before */
628 };
629 /* Note: The (B2,B2) entry should probably be D instead of P.  */
630 /* Note: The (PR,ID) entry should probably be D instead of I.  */
631
632 void
633 u8_possible_linebreaks (const unsigned char *s, size_t n, const char *encoding, char *p)
634 {
635   int LBP_AI_REPLACEMENT = (is_cjk_encoding (encoding) ? LBP_ID : LBP_AL);
636   const unsigned char *s_end = s + n;
637   int last_prop = LBP_BK; /* line break property of last non-space character */
638   char *seen_space = NULL; /* Was a space seen after the last non-space character? */
639   char *seen_space2 = NULL; /* At least two spaces after the last non-space? */
640
641   /* Don't break inside multibyte characters.  */
642   memset (p, UC_BREAK_PROHIBITED, n);
643
644   while (s < s_end)
645     {
646       unsigned int uc;
647       int count = u8_mbtouc (&uc, s, s_end - s);
648       int prop = lbrkprop_lookup (uc);
649
650       if (prop == LBP_BK)
651         {
652           /* Mandatory break.  */
653           *p = UC_BREAK_MANDATORY;
654           last_prop = LBP_BK;
655           seen_space = NULL;
656           seen_space2 = NULL;
657         }
658       else
659         {
660           char *q;
661
662           /* Resolve property values whose behaviour is not fixed.  */
663           switch (prop)
664             {
665               case LBP_AI:
666                 /* Resolve ambiguous.  */
667                 prop = LBP_AI_REPLACEMENT;
668                 break;
669               case LBP_CB:
670                 /* This is arbitrary.  */
671                 prop = LBP_ID;
672                 break;
673               case LBP_SA:
674                 /* We don't handle complex scripts yet.
675                    Treat LBP_SA like LBP_XX.  */
676               case LBP_XX:
677                 /* This is arbitrary.  */
678                 prop = LBP_AL;
679                 break;
680             }
681
682           /* Deal with combining characters.  */
683           q = p;
684           if (prop == LBP_CM)
685             {
686               /* Don't break just before a combining character.  */
687               *p = UC_BREAK_PROHIBITED;
688               /* A combining character turns a preceding space into LBP_AL.  */
689               if (seen_space != NULL)
690                 {
691                   q = seen_space;
692                   seen_space = seen_space2;
693                   prop = LBP_AL;
694                   goto lookup_via_table;
695                 }
696             }
697           else if (prop == LBP_SP)
698             {
699               /* Don't break just before a space.  */
700               *p = UC_BREAK_PROHIBITED;
701               seen_space2 = seen_space;
702               seen_space = p;
703             }
704           else
705             {
706              lookup_via_table:
707               /* prop must be usable as an index for table 7.3 of UTR #14.  */
708               if (!(prop >= 1 && prop <= sizeof(lbrk_table) / sizeof(lbrk_table[0])))
709                 abort ();
710
711               if (last_prop == LBP_BK)
712                 {
713                   /* Don't break at the beginning of a line.  */
714                   *q = UC_BREAK_PROHIBITED;
715                 }
716               else
717                 {
718                   switch (lbrk_table [last_prop-1] [prop-1])
719                     {
720                       case D:
721                         *q = UC_BREAK_POSSIBLE;
722                         break;
723                       case I:
724                         *q = (seen_space != NULL ? UC_BREAK_POSSIBLE : UC_BREAK_PROHIBITED);
725                         break;
726                       case P:
727                         *q = UC_BREAK_PROHIBITED;
728                         break;
729                       default:
730                         abort ();
731                     }
732                 }
733               last_prop = prop;
734               seen_space = NULL;
735               seen_space2 = NULL;
736             }
737         }
738
739       s += count;
740       p += count;
741     }
742 }
743
744 void
745 u16_possible_linebreaks (const unsigned short *s, size_t n, const char *encoding, char *p)
746 {
747   int LBP_AI_REPLACEMENT = (is_cjk_encoding (encoding) ? LBP_ID : LBP_AL);
748   const unsigned short *s_end = s + n;
749   int last_prop = LBP_BK; /* line break property of last non-space character */
750   char *seen_space = NULL; /* Was a space seen after the last non-space character? */
751   char *seen_space2 = NULL; /* At least two spaces after the last non-space? */
752
753   /* Don't break inside multibyte characters.  */
754   memset (p, UC_BREAK_PROHIBITED, n);
755
756   while (s < s_end)
757     {
758       unsigned int uc;
759       int count = u16_mbtouc (&uc, s, s_end - s);
760       int prop = lbrkprop_lookup (uc);
761
762       if (prop == LBP_BK)
763         {
764           /* Mandatory break.  */
765           *p = UC_BREAK_MANDATORY;
766           last_prop = LBP_BK;
767           seen_space = NULL;
768           seen_space2 = NULL;
769         }
770       else
771         {
772           char *q;
773
774           /* Resolve property values whose behaviour is not fixed.  */
775           switch (prop)
776             {
777               case LBP_AI:
778                 /* Resolve ambiguous.  */
779                 prop = LBP_AI_REPLACEMENT;
780                 break;
781               case LBP_CB:
782                 /* This is arbitrary.  */
783                 prop = LBP_ID;
784                 break;
785               case LBP_SA:
786                 /* We don't handle complex scripts yet.
787                    Treat LBP_SA like LBP_XX.  */
788               case LBP_XX:
789                 /* This is arbitrary.  */
790                 prop = LBP_AL;
791                 break;
792             }
793
794           /* Deal with combining characters.  */
795           q = p;
796           if (prop == LBP_CM)
797             {
798               /* Don't break just before a combining character.  */
799               *p = UC_BREAK_PROHIBITED;
800               /* A combining character turns a preceding space into LBP_AL.  */
801               if (seen_space != NULL)
802                 {
803                   q = seen_space;
804                   seen_space = seen_space2;
805                   prop = LBP_AL;
806                   goto lookup_via_table;
807                 }
808             }
809           else if (prop == LBP_SP)
810             {
811               /* Don't break just before a space.  */
812               *p = UC_BREAK_PROHIBITED;
813               seen_space2 = seen_space;
814               seen_space = p;
815             }
816           else
817             {
818              lookup_via_table:
819               /* prop must be usable as an index for table 7.3 of UTR #14.  */
820               if (!(prop >= 1 && prop <= sizeof(lbrk_table) / sizeof(lbrk_table[0])))
821                 abort ();
822
823               if (last_prop == LBP_BK)
824                 {
825                   /* Don't break at the beginning of a line.  */
826                   *q = UC_BREAK_PROHIBITED;
827                 }
828               else
829                 {
830                   switch (lbrk_table [last_prop-1] [prop-1])
831                     {
832                       case D:
833                         *q = UC_BREAK_POSSIBLE;
834                         break;
835                       case I:
836                         *q = (seen_space != NULL ? UC_BREAK_POSSIBLE : UC_BREAK_PROHIBITED);
837                         break;
838                       case P:
839                         *q = UC_BREAK_PROHIBITED;
840                         break;
841                       default:
842                         abort ();
843                     }
844                 }
845               last_prop = prop;
846               seen_space = NULL;
847               seen_space2 = NULL;
848             }
849         }
850
851       s += count;
852       p += count;
853     }
854 }
855
856 void
857 u32_possible_linebreaks (const unsigned int *s, size_t n, const char *encoding, char *p)
858 {
859   int LBP_AI_REPLACEMENT = (is_cjk_encoding (encoding) ? LBP_ID : LBP_AL);
860   const unsigned int *s_end = s + n;
861   int last_prop = LBP_BK; /* line break property of last non-space character */
862   char *seen_space = NULL; /* Was a space seen after the last non-space character? */
863   char *seen_space2 = NULL; /* At least two spaces after the last non-space? */
864
865   while (s < s_end)
866     {
867       unsigned int uc = *s;
868       int prop = lbrkprop_lookup (uc);
869
870       if (prop == LBP_BK)
871         {
872           /* Mandatory break.  */
873           *p = UC_BREAK_MANDATORY;
874           last_prop = LBP_BK;
875           seen_space = NULL;
876           seen_space2 = NULL;
877         }
878       else
879         {
880           char *q;
881
882           /* Resolve property values whose behaviour is not fixed.  */
883           switch (prop)
884             {
885               case LBP_AI:
886                 /* Resolve ambiguous.  */
887                 prop = LBP_AI_REPLACEMENT;
888                 break;
889               case LBP_CB:
890                 /* This is arbitrary.  */
891                 prop = LBP_ID;
892                 break;
893               case LBP_SA:
894                 /* We don't handle complex scripts yet.
895                    Treat LBP_SA like LBP_XX.  */
896               case LBP_XX:
897                 /* This is arbitrary.  */
898                 prop = LBP_AL;
899                 break;
900             }
901
902           /* Deal with combining characters.  */
903           q = p;
904           if (prop == LBP_CM)
905             {
906               /* Don't break just before a combining character.  */
907               *p = UC_BREAK_PROHIBITED;
908               /* A combining character turns a preceding space into LBP_AL.  */
909               if (seen_space != NULL)
910                 {
911                   q = seen_space;
912                   seen_space = seen_space2;
913                   prop = LBP_AL;
914                   goto lookup_via_table;
915                 }
916             }
917           else if (prop == LBP_SP)
918             {
919               /* Don't break just before a space.  */
920               *p = UC_BREAK_PROHIBITED;
921               seen_space2 = seen_space;
922               seen_space = p;
923             }
924           else
925             {
926              lookup_via_table:
927               /* prop must be usable as an index for table 7.3 of UTR #14.  */
928               if (!(prop >= 1 && prop <= sizeof(lbrk_table) / sizeof(lbrk_table[0])))
929                 abort ();
930
931               if (last_prop == LBP_BK)
932                 {
933                   /* Don't break at the beginning of a line.  */
934                   *q = UC_BREAK_PROHIBITED;
935                 }
936               else
937                 {
938                   switch (lbrk_table [last_prop-1] [prop-1])
939                     {
940                       case D:
941                         *q = UC_BREAK_POSSIBLE;
942                         break;
943                       case I:
944                         *q = (seen_space != NULL ? UC_BREAK_POSSIBLE : UC_BREAK_PROHIBITED);
945                         break;
946                       case P:
947                         *q = UC_BREAK_PROHIBITED;
948                         break;
949                       default:
950                         abort ();
951                     }
952                 }
953               last_prop = prop;
954               seen_space = NULL;
955               seen_space2 = NULL;
956             }
957         }
958
959       s++;
960       p++;
961     }
962 }
963
964
965 /* Choose the best line breaks, assuming the uc_width function.
966    Return the column after the end of the string.  */
967
968 int
969 u8_width_linebreaks (const unsigned char *s, size_t n,
970                      int width, int start_column, int at_end_columns,
971                      const char *o, const char *encoding,
972                      char *p)
973 {
974   const unsigned char *s_end;
975   char *last_p;
976   int last_column;
977   int piece_width;
978
979   u8_possible_linebreaks (s, n, encoding, p);
980
981   s_end = s + n;
982   last_p = NULL;
983   last_column = start_column;
984   piece_width = 0;
985   while (s < s_end)
986     {
987       unsigned int uc;
988       int count = u8_mbtouc (&uc, s, s_end - s);
989
990       /* Respect the override.  */
991       if (o != NULL && *o != UC_BREAK_UNDEFINED)
992         *p = *o;
993
994       if (*p == UC_BREAK_POSSIBLE || *p == UC_BREAK_MANDATORY)
995         {
996           /* An atomic piece of text ends here.  */
997           if (last_p != NULL && last_column + piece_width > width)
998             {
999               /* Insert a line break.  */
1000               *last_p = UC_BREAK_POSSIBLE;
1001               last_column = 0;
1002             }
1003         }
1004
1005       if (*p == UC_BREAK_MANDATORY)
1006         {
1007           /* uc is a line break character.  */
1008           /* Start a new piece at column 0.  */
1009           last_p = NULL;
1010           last_column = 0;
1011           piece_width = 0;
1012         }
1013       else
1014         {
1015           /* uc is not a line break character.  */
1016           int w;
1017
1018           if (*p == UC_BREAK_POSSIBLE)
1019             {
1020               /* Start a new piece.  */
1021               last_p = p;
1022               last_column += piece_width;
1023               piece_width = 0;
1024               /* No line break for the moment, may be turned into
1025                  UC_BREAK_POSSIBLE later, via last_p. */
1026             }
1027
1028           *p = UC_BREAK_PROHIBITED;
1029
1030           w = uc_width (uc, encoding);
1031           if (w >= 0) /* ignore control characters in the string */
1032             piece_width += w;
1033          }
1034
1035       s += count;
1036       p += count;
1037       if (o != NULL)
1038         o += count;
1039     }
1040
1041   /* The last atomic piece of text ends here.  */
1042   if (last_p != NULL && last_column + piece_width + at_end_columns > width)
1043     {
1044       /* Insert a line break.  */
1045       *last_p = UC_BREAK_POSSIBLE;
1046       last_column = 0;
1047     }
1048
1049   return last_column + piece_width;
1050 }
1051
1052 int
1053 u16_width_linebreaks (const unsigned short *s, size_t n,
1054                       int width, int start_column, int at_end_columns,
1055                       const char *o, const char *encoding,
1056                       char *p)
1057 {
1058   const unsigned short *s_end;
1059   char *last_p;
1060   int last_column;
1061   int piece_width;
1062
1063   u16_possible_linebreaks (s, n, encoding, p);
1064
1065   s_end = s + n;
1066   last_p = NULL;
1067   last_column = start_column;
1068   piece_width = 0;
1069   while (s < s_end)
1070     {
1071       unsigned int uc;
1072       int count = u16_mbtouc (&uc, s, s_end - s);
1073
1074       /* Respect the override.  */
1075       if (o != NULL && *o != UC_BREAK_UNDEFINED)
1076         *p = *o;
1077
1078       if (*p == UC_BREAK_POSSIBLE || *p == UC_BREAK_MANDATORY)
1079         {
1080           /* An atomic piece of text ends here.  */
1081           if (last_p != NULL && last_column + piece_width > width)
1082             {
1083               /* Insert a line break.  */
1084               *last_p = UC_BREAK_POSSIBLE;
1085               last_column = 0;
1086             }
1087         }
1088
1089       if (*p == UC_BREAK_MANDATORY)
1090         {
1091           /* uc is a line break character.  */
1092           /* Start a new piece at column 0.  */
1093           last_p = NULL;
1094           last_column = 0;
1095           piece_width = 0;
1096         }
1097       else
1098         {
1099           /* uc is not a line break character.  */
1100           int w;
1101
1102           if (*p == UC_BREAK_POSSIBLE)
1103             {
1104               /* Start a new piece.  */
1105               last_p = p;
1106               last_column += piece_width;
1107               piece_width = 0;
1108               /* No line break for the moment, may be turned into
1109                  UC_BREAK_POSSIBLE later, via last_p. */
1110             }
1111
1112           *p = UC_BREAK_PROHIBITED;
1113
1114           w = uc_width (uc, encoding);
1115           if (w >= 0) /* ignore control characters in the string */
1116             piece_width += w;
1117          }
1118
1119       s += count;
1120       p += count;
1121       if (o != NULL)
1122         o += count;
1123     }
1124
1125   /* The last atomic piece of text ends here.  */
1126   if (last_p != NULL && last_column + piece_width + at_end_columns > width)
1127     {
1128       /* Insert a line break.  */
1129       *last_p = UC_BREAK_POSSIBLE;
1130       last_column = 0;
1131     }
1132
1133   return last_column + piece_width;
1134 }
1135
1136 int
1137 u32_width_linebreaks (const unsigned int *s, size_t n,
1138                       int width, int start_column, int at_end_columns,
1139                       const char *o, const char *encoding,
1140                       char *p)
1141 {
1142   const unsigned int *s_end;
1143   char *last_p;
1144   int last_column;
1145   int piece_width;
1146
1147   u32_possible_linebreaks (s, n, encoding, p);
1148
1149   s_end = s + n;
1150   last_p = NULL;
1151   last_column = start_column;
1152   piece_width = 0;
1153   while (s < s_end)
1154     {
1155       unsigned int uc = *s;
1156
1157       /* Respect the override.  */
1158       if (o != NULL && *o != UC_BREAK_UNDEFINED)
1159         *p = *o;
1160
1161       if (*p == UC_BREAK_POSSIBLE || *p == UC_BREAK_MANDATORY)
1162         {
1163           /* An atomic piece of text ends here.  */
1164           if (last_p != NULL && last_column + piece_width > width)
1165             {
1166               /* Insert a line break.  */
1167               *last_p = UC_BREAK_POSSIBLE;
1168               last_column = 0;
1169             }
1170         }
1171
1172       if (*p == UC_BREAK_MANDATORY)
1173         {
1174           /* uc is a line break character.  */
1175           /* Start a new piece at column 0.  */
1176           last_p = NULL;
1177           last_column = 0;
1178           piece_width = 0;
1179         }
1180       else
1181         {
1182           /* uc is not a line break character.  */
1183           int w;
1184
1185           if (*p == UC_BREAK_POSSIBLE)
1186             {
1187               /* Start a new piece.  */
1188               last_p = p;
1189               last_column += piece_width;
1190               piece_width = 0;
1191               /* No line break for the moment, may be turned into
1192                  UC_BREAK_POSSIBLE later, via last_p. */
1193             }
1194
1195           *p = UC_BREAK_PROHIBITED;
1196
1197           w = uc_width (uc, encoding);
1198           if (w >= 0) /* ignore control characters in the string */
1199             piece_width += w;
1200          }
1201
1202       s++;
1203       p++;
1204       if (o != NULL)
1205         o++;
1206     }
1207
1208   /* The last atomic piece of text ends here.  */
1209   if (last_p != NULL && last_column + piece_width + at_end_columns > width)
1210     {
1211       /* Insert a line break.  */
1212       *last_p = UC_BREAK_POSSIBLE;
1213       last_column = 0;
1214     }
1215
1216   return last_column + piece_width;
1217 }
1218
1219
1220 #ifdef TEST1
1221
1222 #include <stdio.h>
1223
1224 /* Read the contents of an input stream, and return it, terminated with a NUL
1225    byte. */
1226 char *
1227 read_file (FILE *stream)
1228 {
1229 #define BUFSIZE 4096
1230   char *buf = NULL;
1231   int alloc = 0;
1232   int size = 0;
1233   int count;
1234
1235   while (! feof (stream))
1236     {
1237       if (size + BUFSIZE > alloc)
1238         {
1239           alloc = alloc + alloc / 2;
1240           if (alloc < size + BUFSIZE)
1241             alloc = size + BUFSIZE;
1242           buf = realloc (buf, alloc);
1243           if (buf == NULL)
1244             {
1245               fprintf (stderr, "out of memory\n");
1246               exit (1);
1247             }
1248         }
1249       count = fread (buf + size, 1, BUFSIZE, stream);
1250       if (count == 0)
1251         {
1252           if (ferror (stream))
1253             {
1254               perror ("fread");
1255               exit (1);
1256             }
1257         }
1258       else
1259         size += count;
1260     }
1261   buf = realloc (buf, size + 1);
1262   if (buf == NULL)
1263     {
1264       fprintf (stderr, "out of memory\n");
1265       exit (1);
1266     }
1267   buf[size] = '\0';
1268   return buf;
1269 #undef BUFSIZE
1270 }
1271
1272 int
1273 main (int argc, char * argv[])
1274 {
1275   if (argc == 1)
1276     {
1277       /* Display all the break opportunities in the input string.  */
1278       char *input = read_file (stdin);
1279       int length = strlen (input);
1280       char *breaks = malloc (length);
1281       int i;
1282
1283       u8_possible_linebreaks ((unsigned char *) input, length, "UTF-8", breaks);
1284
1285       for (i = 0; i < length; i++)
1286         {
1287           switch (breaks[i])
1288             {
1289               case UC_BREAK_POSSIBLE:
1290                 /* U+2027 in UTF-8 encoding */
1291                 putc (0xe2, stdout); putc (0x80, stdout); putc (0xa7, stdout);
1292                 break;
1293               case UC_BREAK_MANDATORY:
1294                 /* U+21B2 (or U+21B5) in UTF-8 encoding */
1295                 putc (0xe2, stdout); putc (0x86, stdout); putc (0xb2, stdout);
1296                 break;
1297               case UC_BREAK_PROHIBITED:
1298                 break;
1299               default:
1300                 abort ();
1301             }
1302           putc (input[i], stdout);
1303         }
1304
1305       free (breaks);
1306
1307       return 0;
1308     }
1309   else if (argc == 2)
1310     {
1311       /* Insert line breaks for a given width.  */
1312       int width = atoi (argv[1]);
1313       char *input = read_file (stdin);
1314       int length = strlen (input);
1315       char *breaks = malloc (length);
1316       int i;
1317
1318       u8_width_linebreaks ((unsigned char *) input, length, width, 0, 0, NULL, "UTF-8", breaks);
1319
1320       for (i = 0; i < length; i++)
1321         {
1322           switch (breaks[i])
1323             {
1324               case UC_BREAK_POSSIBLE:
1325                 putc ('\n', stdout);
1326                 break;
1327               case UC_BREAK_MANDATORY:
1328                 break;
1329               case UC_BREAK_PROHIBITED:
1330                 break;
1331               default:
1332                 abort ();
1333             }
1334           putc (input[i], stdout);
1335         }
1336
1337       free (breaks);
1338
1339       return 0;
1340     }
1341   else
1342     return 1;
1343 }
1344
1345 #endif /* TEST1 */
1346
1347
1348 /* Now the same thing with an arbitrary encoding.
1349
1350    We convert the input string to Unicode.
1351
1352    The standardized Unicode encodings are UTF-8, UCS-2, UCS-4, UTF-16,
1353    UTF-16BE, UTF-16LE, UTF-7.  UCS-2 supports only characters up to
1354    \U0000FFFF.  UTF-16 and variants support only characters up to
1355    \U0010FFFF.  UTF-7 is way too complex and not supported by glibc-2.1.
1356    UCS-4 specification leaves doubts about endianness and byte order mark.
1357    glibc currently interprets it as big endian without byte order mark,
1358    but this is not backed by an RFC.  So we use UTF-8. It supports
1359    characters up to \U7FFFFFFF and is unambiguously defined.  */
1360
1361 #if HAVE_ICONV
1362
1363 #include <iconv.h>
1364 #include <errno.h>
1365
1366 /* Luckily, the encoding's name is platform independent.  */
1367 #define UTF8_NAME "UTF-8"
1368
1369 /* Return the length of a string after conversion through an iconv_t.  */
1370 static size_t
1371 iconv_string_length (iconv_t cd, const char *s, size_t n)
1372 {
1373 #define TMPBUFSIZE 4096
1374   size_t count = 0;
1375   char tmpbuf[TMPBUFSIZE];
1376   const char *inptr = s;
1377   size_t insize = n;
1378   while (insize > 0)
1379     {
1380       char *outptr = tmpbuf;
1381       size_t outsize = TMPBUFSIZE;
1382       size_t res = iconv (cd, (ICONV_CONST char **) &inptr, &insize, &outptr, &outsize);
1383       if (res == (size_t)(-1) && errno != E2BIG)
1384         return (size_t)(-1);
1385       count += outptr - tmpbuf;
1386     }
1387   /* Avoid glibc-2.1 bug and Solaris 7 through 9 bug.  */
1388 #if defined _LIBICONV_VERSION \
1389     || !((__GLIBC__ - 0 == 2 && __GLIBC_MINOR__ - 0 <= 1) || defined __sun)
1390   {
1391     char *outptr = tmpbuf;
1392     size_t outsize = TMPBUFSIZE;
1393     size_t res = iconv (cd, NULL, NULL, &outptr, &outsize);
1394     if (res == (size_t)(-1))
1395       return (size_t)(-1);
1396     count += outptr - tmpbuf;
1397   }
1398   /* Return to the initial state.  */
1399   iconv (cd, NULL, NULL, NULL, NULL);
1400 #endif
1401   return count;
1402 #undef TMPBUFSIZE
1403 }
1404
1405 static void
1406 iconv_string_keeping_offsets (iconv_t cd, const char *s, size_t n,
1407                               size_t *offtable, char *t, size_t m)
1408 {
1409   size_t i;
1410   const char *s_end;
1411   const char *inptr;
1412   char *outptr;
1413   size_t outsize;
1414   /* Avoid glibc-2.1 bug.  */
1415 #if !defined _LIBICONV_VERSION && (__GLIBC__ - 0 == 2 && __GLIBC_MINOR__ - 0 <= 1)
1416   const size_t extra = 1;
1417 #else
1418   const size_t extra = 0;
1419 #endif
1420
1421   for (i = 0; i < n; i++)
1422     offtable[i] = (size_t)(-1);
1423
1424   s_end = s + n;
1425   inptr = s;
1426   outptr = t;
1427   outsize = m + extra;
1428   while (inptr < s_end)
1429     {
1430       const char *saved_inptr;
1431       size_t insize;
1432       size_t res;
1433
1434       offtable[inptr - s] = outptr - t;
1435
1436       saved_inptr = inptr;
1437       res = (size_t)(-1);
1438       for (insize = 1; inptr + insize <= s_end; insize++)
1439         {
1440           res = iconv (cd, (ICONV_CONST char **) &inptr, &insize, &outptr, &outsize);
1441           if (!(res == (size_t)(-1) && errno == EINVAL))
1442             break;
1443           /* We expect that no input bytes have been consumed so far.  */
1444           if (inptr != saved_inptr)
1445             abort ();
1446         }
1447       /* After we verified the convertibility and computed the translation's
1448          size m, there shouldn't be any conversion error here. */
1449       if (res == (size_t)(-1))
1450         abort ();
1451     }
1452   /* Avoid glibc-2.1 bug and Solaris 7 bug.  */
1453 #if defined _LIBICONV_VERSION \
1454     || !((__GLIBC__ - 0 == 2 && __GLIBC_MINOR__ - 0 <= 1) || defined __sun)
1455   if (iconv (cd, NULL, NULL, &outptr, &outsize) == (size_t)(-1))
1456     abort ();
1457 #endif
1458   /* We should have produced exactly m output bytes.  */
1459   if (outsize != extra)
1460     abort ();
1461 }
1462
1463 #endif /* HAVE_ICONV */
1464
1465 #if C_CTYPE_ASCII
1466
1467 /* Tests whether a string is entirely ASCII.  Returns 1 if yes.
1468    Returns 0 if the string is in an 8-bit encoding or an ISO-2022 encoding.  */
1469 static int
1470 is_all_ascii (const char *s, size_t n)
1471 {
1472   for (; n > 0; s++, n--)
1473     {
1474       unsigned char c = (unsigned char) *s;
1475
1476       if (!(c_isprint (c) || c_isspace (c)))
1477         return 0;
1478     }
1479   return 1;
1480 }
1481
1482 #endif /* C_CTYPE_ASCII */
1483
1484 void
1485 mbs_possible_linebreaks (const char *s, size_t n, const char *encoding,
1486                          char *p)
1487 {
1488   if (n == 0)
1489     return;
1490   if (is_utf8_encoding (encoding))
1491     u8_possible_linebreaks ((const unsigned char *) s, n, encoding, p);
1492   else
1493     {
1494 #if HAVE_ICONV
1495       iconv_t to_utf8;
1496       /* Avoid glibc-2.1 bug with EUC-KR.  */
1497 # if (__GLIBC__ - 0 == 2 && __GLIBC_MINOR__ - 0 <= 1) && !defined _LIBICONV_VERSION
1498       if (STREQ (encoding, "EUC-KR", 'E', 'U', 'C', '-', 'K', 'R', 0, 0, 0))
1499         to_utf8 = (iconv_t)(-1);
1500       else
1501 # endif
1502       /* Avoid Solaris 9 bug with GB2312, EUC-TW, BIG5, BIG5-HKSCS, GBK,
1503          GB18030.  */
1504 # if defined __sun && !defined _LIBICONV_VERSION
1505       if (   STREQ (encoding, "GB2312", 'G', 'B', '2', '3', '1', '2', 0, 0, 0)
1506           || STREQ (encoding, "EUC-TW", 'E', 'U', 'C', '-', 'T', 'W', 0, 0, 0)
1507           || STREQ (encoding, "BIG5", 'B', 'I', 'G', '5', 0, 0, 0, 0, 0)
1508           || STREQ (encoding, "BIG5-HKSCS", 'B', 'I', 'G', '5', '-', 'H', 'K', 'S', 'C')
1509           || STREQ (encoding, "GBK", 'G', 'B', 'K', 0, 0, 0, 0, 0, 0)
1510           || STREQ (encoding, "GB18030", 'G', 'B', '1', '8', '0', '3', '0', 0, 0))
1511         to_utf8 = (iconv_t)(-1);
1512       else
1513 # endif
1514       to_utf8 = iconv_open (UTF8_NAME, encoding);
1515       if (to_utf8 != (iconv_t)(-1))
1516         {
1517           /* Determine the length of the resulting UTF-8 string.  */
1518           size_t m = iconv_string_length (to_utf8, s, n);
1519           if (m != (size_t)(-1))
1520             {
1521               /* Convert the string to UTF-8 and build a translation table
1522                  from offsets into s to offsets into the translated string.  */
1523               size_t memory_size = xsum3 (xtimes (n, sizeof (size_t)), m, m);
1524               char *memory =
1525                 (size_in_bounds_p (memory_size) ? malloc (memory_size) : NULL);
1526               if (memory != NULL)
1527                 {
1528                   size_t *offtable = (size_t *) memory;
1529                   char *t = (char *) (offtable + n);
1530                   char *q = (char *) (t + m);
1531                   size_t i;
1532
1533                   iconv_string_keeping_offsets (to_utf8, s, n, offtable, t, m);
1534
1535                   /* Determine the possible line breaks of the UTF-8 string.  */
1536                   u8_possible_linebreaks ((const unsigned char *) t, m, encoding, q);
1537
1538                   /* Translate the result back to the original string.  */
1539                   memset (p, UC_BREAK_PROHIBITED, n);
1540                   for (i = 0; i < n; i++)
1541                     if (offtable[i] != (size_t)(-1))
1542                       p[i] = q[offtable[i]];
1543
1544                   free (memory);
1545                   iconv_close (to_utf8);
1546                   return;
1547                 }
1548             }
1549           iconv_close (to_utf8);
1550         }
1551 #endif
1552       /* Impossible to convert.  */
1553 #if C_CTYPE_ASCII
1554       if (is_all_ascii (s, n))
1555         {
1556           /* ASCII is a subset of UTF-8.  */
1557           u8_possible_linebreaks ((const unsigned char *) s, n, encoding, p);
1558           return;
1559         }
1560 #endif
1561       /* We have a non-ASCII string and cannot convert it.
1562          Don't produce line breaks except those already present in the
1563          input string.  All we assume here is that the encoding is
1564          minimally ASCII compatible.  */
1565       {
1566         const char *s_end = s + n;
1567         while (s < s_end)
1568           {
1569             *p = (*s == '\n' ? UC_BREAK_MANDATORY : UC_BREAK_PROHIBITED);
1570             s++;
1571             p++;
1572           }
1573       }
1574     }
1575 }
1576
1577 int
1578 mbs_width_linebreaks (const char *s, size_t n,
1579                       int width, int start_column, int at_end_columns,
1580                       const char *o, const char *encoding,
1581                       char *p)
1582 {
1583   if (n == 0)
1584     return start_column;
1585   if (is_utf8_encoding (encoding))
1586     return u8_width_linebreaks ((const unsigned char *) s, n, width, start_column, at_end_columns, o, encoding, p);
1587   else
1588     {
1589 #if HAVE_ICONV
1590       iconv_t to_utf8;
1591       /* Avoid glibc-2.1 bug with EUC-KR.  */
1592 # if (__GLIBC__ - 0 == 2 && __GLIBC_MINOR__ - 0 <= 1) && !defined _LIBICONV_VERSION
1593       if (STREQ (encoding, "EUC-KR", 'E', 'U', 'C', '-', 'K', 'R', 0, 0, 0))
1594         to_utf8 = (iconv_t)(-1);
1595       else
1596 # endif
1597       /* Avoid Solaris 9 bug with GB2312, EUC-TW, BIG5, BIG5-HKSCS, GBK,
1598          GB18030.  */
1599 # if defined __sun && !defined _LIBICONV_VERSION
1600       if (   STREQ (encoding, "GB2312", 'G', 'B', '2', '3', '1', '2', 0, 0, 0)
1601           || STREQ (encoding, "EUC-TW", 'E', 'U', 'C', '-', 'T', 'W', 0, 0, 0)
1602           || STREQ (encoding, "BIG5", 'B', 'I', 'G', '5', 0, 0, 0, 0, 0)
1603           || STREQ (encoding, "BIG5-HKSCS", 'B', 'I', 'G', '5', '-', 'H', 'K', 'S', 'C')
1604           || STREQ (encoding, "GBK", 'G', 'B', 'K', 0, 0, 0, 0, 0, 0)
1605           || STREQ (encoding, "GB18030", 'G', 'B', '1', '8', '0', '3', '0', 0, 0))
1606         to_utf8 = (iconv_t)(-1);
1607       else
1608 # endif
1609       to_utf8 = iconv_open (UTF8_NAME, encoding);
1610       if (to_utf8 != (iconv_t)(-1))
1611         {
1612           /* Determine the length of the resulting UTF-8 string.  */
1613           size_t m = iconv_string_length (to_utf8, s, n);
1614           if (m != (size_t)(-1))
1615             {
1616               /* Convert the string to UTF-8 and build a translation table
1617                  from offsets into s to offsets into the translated string.  */
1618               size_t memory_size =
1619                 xsum4 (xtimes (n, sizeof (size_t)), m, m,
1620                        (o != NULL ? m : 0));
1621               char *memory =
1622                 (size_in_bounds_p (memory_size) ? malloc (memory_size) : NULL);
1623               if (memory != NULL)
1624                 {
1625                   size_t *offtable = (size_t *) memory;
1626                   char *t = (char *) (offtable + n);
1627                   char *q = (char *) (t + m);
1628                   char *o8 = (o != NULL ? (char *) (q + m) : NULL);
1629                   int res_column;
1630                   size_t i;
1631
1632                   iconv_string_keeping_offsets (to_utf8, s, n, offtable, t, m);
1633
1634                   /* Translate the overrides to the UTF-8 string.  */
1635                   if (o != NULL)
1636                     {
1637                       memset (o8, UC_BREAK_UNDEFINED, m);
1638                       for (i = 0; i < n; i++)
1639                         if (offtable[i] != (size_t)(-1))
1640                           o8[offtable[i]] = o[i];
1641                     }
1642
1643                   /* Determine the line breaks of the UTF-8 string.  */
1644                   res_column =
1645                     u8_width_linebreaks ((const unsigned char *) t, m, width, start_column, at_end_columns, o8, encoding, q);
1646
1647                   /* Translate the result back to the original string.  */
1648                   memset (p, UC_BREAK_PROHIBITED, n);
1649                   for (i = 0; i < n; i++)
1650                     if (offtable[i] != (size_t)(-1))
1651                       p[i] = q[offtable[i]];
1652
1653                   free (memory);
1654                   iconv_close (to_utf8);
1655                   return res_column;
1656                 }
1657             }
1658           iconv_close (to_utf8);
1659         }
1660 #endif
1661       /* Impossible to convert.  */
1662 #if C_CTYPE_ASCII
1663       if (is_all_ascii (s, n))
1664         {
1665           /* ASCII is a subset of UTF-8.  */
1666           return u8_width_linebreaks ((const unsigned char *) s, n, width, start_column, at_end_columns, o, encoding, p);
1667         }
1668 #endif
1669       /* We have a non-ASCII string and cannot convert it.
1670          Don't produce line breaks except those already present in the
1671          input string.  All we assume here is that the encoding is
1672          minimally ASCII compatible.  */
1673       {
1674         const char *s_end = s + n;
1675         while (s < s_end)
1676           {
1677             *p = ((o != NULL && *o == UC_BREAK_MANDATORY) || *s == '\n'
1678                   ? UC_BREAK_MANDATORY
1679                   : UC_BREAK_PROHIBITED);
1680             s++;
1681             p++;
1682             if (o != NULL)
1683               o++;
1684           }
1685         /* We cannot compute widths in this case.  */
1686         return start_column;
1687       }
1688     }
1689 }
1690
1691
1692 #ifdef TEST2
1693
1694 #include <stdio.h>
1695 #include <locale.h>
1696
1697 /* Read the contents of an input stream, and return it, terminated with a NUL
1698    byte. */
1699 char *
1700 read_file (FILE *stream)
1701 {
1702 #define BUFSIZE 4096
1703   char *buf = NULL;
1704   int alloc = 0;
1705   int size = 0;
1706   int count;
1707
1708   while (! feof (stream))
1709     {
1710       if (size + BUFSIZE > alloc)
1711         {
1712           alloc = alloc + alloc / 2;
1713           if (alloc < size + BUFSIZE)
1714             alloc = size + BUFSIZE;
1715           buf = realloc (buf, alloc);
1716           if (buf == NULL)
1717             {
1718               fprintf (stderr, "out of memory\n");
1719               exit (1);
1720             }
1721         }
1722       count = fread (buf + size, 1, BUFSIZE, stream);
1723       if (count == 0)
1724         {
1725           if (ferror (stream))
1726             {
1727               perror ("fread");
1728               exit (1);
1729             }
1730         }
1731       else
1732         size += count;
1733     }
1734   buf = realloc (buf, size + 1);
1735   if (buf == NULL)
1736     {
1737       fprintf (stderr, "out of memory\n");
1738       exit (1);
1739     }
1740   buf[size] = '\0';
1741   return buf;
1742 #undef BUFSIZE
1743 }
1744
1745 int
1746 main (int argc, char * argv[])
1747 {
1748   setlocale (LC_CTYPE, "");
1749   if (argc == 1)
1750     {
1751       /* Display all the break opportunities in the input string.  */
1752       char *input = read_file (stdin);
1753       int length = strlen (input);
1754       char *breaks = malloc (length);
1755       int i;
1756
1757       mbs_possible_linebreaks (input, length, locale_charset (), breaks);
1758
1759       for (i = 0; i < length; i++)
1760         {
1761           switch (breaks[i])
1762             {
1763               case UC_BREAK_POSSIBLE:
1764                 putc ('|', stdout);
1765                 break;
1766               case UC_BREAK_MANDATORY:
1767                 break;
1768               case UC_BREAK_PROHIBITED:
1769                 break;
1770               default:
1771                 abort ();
1772             }
1773           putc (input[i], stdout);
1774         }
1775
1776       free (breaks);
1777
1778       return 0;
1779     }
1780   else if (argc == 2)
1781     {
1782       /* Insert line breaks for a given width.  */
1783       int width = atoi (argv[1]);
1784       char *input = read_file (stdin);
1785       int length = strlen (input);
1786       char *breaks = malloc (length);
1787       int i;
1788
1789       mbs_width_linebreaks (input, length, width, 0, 0, NULL, locale_charset (), breaks);
1790
1791       for (i = 0; i < length; i++)
1792         {
1793           switch (breaks[i])
1794             {
1795               case UC_BREAK_POSSIBLE:
1796                 putc ('\n', stdout);
1797                 break;
1798               case UC_BREAK_MANDATORY:
1799                 break;
1800               case UC_BREAK_PROHIBITED:
1801                 break;
1802               default:
1803                 abort ();
1804             }
1805           putc (input[i], stdout);
1806         }
1807
1808       free (breaks);
1809
1810       return 0;
1811     }
1812   else
1813     return 1;
1814 }
1815
1816 #endif /* TEST2 */