1/* Line breaking of strings.
2   Copyright (C) 2001-2003, 2006-2010 Free Software Foundation, Inc.
3   Written by Bruno Haible <bruno@clisp.org>, 2001.
4
5   This program is free software: you can redistribute it and/or modify it
6   under the terms of the GNU Lesser General Public License as published
7   by the Free Software Foundation; either version 3 of the License, or
8   (at your option) 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 GNU
13   Lesser General Public License for more details.
14
15   You should have received a copy of the GNU Lesser General Public License
16   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
17
18#include <config.h>
19
20/* Specification.  */
21#include "unilbrk.h"
22
23#include <stdlib.h>
24#include <string.h>
25
26#include "c-ctype.h"
27#include "uniconv.h"
28#include "unilbrk/ulc-common.h"
29
30/* Line breaking of a string in an arbitrary encoding.
31
32   We convert the input string to Unicode.
33
34   The standardized Unicode encodings are UTF-8, UCS-2, UCS-4, UTF-16,
35   UTF-16BE, UTF-16LE, UTF-7.  UCS-2 supports only characters up to
36   \U0000FFFF.  UTF-16 and variants support only characters up to
37   \U0010FFFF.  UTF-7 is way too complex and not supported by glibc-2.1.
38   UCS-4 specification leaves doubts about endianness and byte order mark.
39   glibc currently interprets it as big endian without byte order mark,
40   but this is not backed by an RFC.  So we use UTF-8. It supports
41   characters up to \U7FFFFFFF and is unambiguously defined.  */
42
43void
44ulc_possible_linebreaks (const char *s, size_t n, const char *encoding,
45                         char *p)
46{
47  if (n > 0)
48    {
49      if (is_utf8_encoding (encoding))
50        u8_possible_linebreaks ((const uint8_t *) s, n, encoding, p);
51      else
52        {
53          /* Convert the string to UTF-8 and build a translation table
54             from offsets into s to offsets into the translated string.  */
55          size_t *offsets = (size_t *) malloc (n * sizeof (size_t));
56
57          if (offsets != NULL)
58            {
59              uint8_t *t;
60              size_t m;
61
62              t = u8_conv_from_encoding (encoding, iconveh_question_mark,
63                                         s, n, offsets, NULL, &m);
64              if (t != NULL)
65                {
66                  char *q = (char *) (m > 0 ? malloc (m) : NULL);
67
68                  if (m == 0 || q != NULL)
69                    {
70                      size_t i;
71
72                      /* Determine the possible line breaks of the UTF-8
73                         string.  */
74                      u8_possible_linebreaks (t, m, encoding, q);
75
76                      /* Translate the result back to the original string.  */
77                      memset (p, UC_BREAK_PROHIBITED, n);
78                      for (i = 0; i < n; i++)
79                        if (offsets[i] != (size_t)(-1))
80                          p[i] = q[offsets[i]];
81
82                      free (q);
83                      free (t);
84                      free (offsets);
85                      return;
86                    }
87                  free (t);
88                }
89              free (offsets);
90            }
91
92          /* Impossible to convert.  */
93#if C_CTYPE_ASCII
94          if (is_all_ascii (s, n))
95            {
96              /* ASCII is a subset of UTF-8.  */
97              u8_possible_linebreaks ((const uint8_t *) s, n, encoding, p);
98              return;
99            }
100#endif
101          /* We have a non-ASCII string and cannot convert it.
102             Don't produce line breaks except those already present in the
103             input string.  All we assume here is that the encoding is
104             minimally ASCII compatible.  */
105          {
106            const char *s_end = s + n;
107            while (s < s_end)
108              {
109                *p = (*s == '\n' ? UC_BREAK_MANDATORY : UC_BREAK_PROHIBITED);
110                s++;
111                p++;
112              }
113          }
114        }
115    }
116}
117
118
119#ifdef TEST
120
121#include <stdio.h>
122#include <locale.h>
123#include <string.h>
124
125/* Read the contents of an input stream, and return it, terminated with a NUL
126   byte. */
127char *
128read_file (FILE *stream)
129{
130#define BUFSIZE 4096
131  char *buf = NULL;
132  int alloc = 0;
133  int size = 0;
134  int count;
135
136  while (! feof (stream))
137    {
138      if (size + BUFSIZE > alloc)
139        {
140          alloc = alloc + alloc / 2;
141          if (alloc < size + BUFSIZE)
142            alloc = size + BUFSIZE;
143          buf = realloc (buf, alloc);
144          if (buf == NULL)
145            {
146              fprintf (stderr, "out of memory\n");
147              exit (1);
148            }
149        }
150      count = fread (buf + size, 1, BUFSIZE, stream);
151      if (count == 0)
152        {
153          if (ferror (stream))
154            {
155              perror ("fread");
156              exit (1);
157            }
158        }
159      else
160        size += count;
161    }
162  buf = realloc (buf, size + 1);
163  if (buf == NULL)
164    {
165      fprintf (stderr, "out of memory\n");
166      exit (1);
167    }
168  buf[size] = '\0';
169  return buf;
170#undef BUFSIZE
171}
172
173int
174main (int argc, char * argv[])
175{
176  setlocale (LC_CTYPE, "");
177  if (argc == 1)
178    {
179      /* Display all the break opportunities in the input string.  */
180      char *input = read_file (stdin);
181      int length = strlen (input);
182      char *breaks = malloc (length);
183      int i;
184
185      ulc_possible_linebreaks (input, length, locale_charset (), breaks);
186
187      for (i = 0; i < length; i++)
188        {
189          switch (breaks[i])
190            {
191            case UC_BREAK_POSSIBLE:
192              putc ('|', stdout);
193              break;
194            case UC_BREAK_MANDATORY:
195              break;
196            case UC_BREAK_PROHIBITED:
197              break;
198            default:
199              abort ();
200            }
201          putc (input[i], stdout);
202        }
203
204      free (breaks);
205
206      return 0;
207    }
208  else
209    return 1;
210}
211
212#endif /* TEST */
213