1/* GIO - GLib Input, Output and Streaming Library
2 *
3 * Copyright (C) 2017 Red Hat, Inc.
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library 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
16 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
17 *
18 * Author: Benjamin Otte <otte@gnome.org>
19 */
20
21#include "config.h"
22
23#include "gdktextlistconverter-x11.h"
24
25#include "gdkintl.h"
26#include "gdkprivate-x11.h"
27
28#define GDK_X11_TEXT_LIST_CONVERTER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GDK_TYPE_X11_TEXT_LIST_CONVERTER, GdkX11TextListConverterClass))
29#define GDK_IS_X11_TEXT_LIST_CONVERTER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GDK_TYPE_X11_TEXT_LIST_CONVERTER))
30#define GDK_X11_TEXT_LIST_CONVERTER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDK_TYPE_X11_TEXT_LIST_CONVERTER, GdkX11TextListConverterClass))
31
32typedef struct _GdkX11TextListConverterClass GdkX11TextListConverterClass;
33
34struct _GdkX11TextListConverter
35{
36 GObject parent_instance;
37
38 GdkDisplay *display;
39
40 const char *encoding; /* interned */
41 int format;
42
43 guint encoder : 1;
44};
45
46struct _GdkX11TextListConverterClass
47{
48 GObjectClass parent_class;
49};
50
51static GConverterResult
52write_output (void *outbuf,
53 gsize outbuf_size,
54 gsize *bytes_written,
55 const void *data,
56 gssize len,
57 GError **error)
58{
59 if (len < 0)
60 len = strlen (s: data) + 1;
61
62 if (outbuf_size < len)
63 {
64 g_set_error_literal (err: error, G_IO_ERROR, code: G_IO_ERROR_NO_SPACE,
65 _("Not enough space in destination"));
66 return G_CONVERTER_ERROR;
67 }
68
69 memcpy (dest: outbuf, src: data, n: len);
70 *bytes_written = len;
71 return G_CONVERTER_FINISHED;
72}
73
74static GConverterResult
75gdk_x11_text_list_converter_decode (GdkX11TextListConverter *conv,
76 const void *inbuf,
77 gsize inbuf_size,
78 void *outbuf,
79 gsize outbuf_size,
80 GConverterFlags flags,
81 gsize *bytes_read,
82 gsize *bytes_written,
83 GError **error)
84{
85 int count;
86 char **list;
87
88 if (!(flags & G_CONVERTER_INPUT_AT_END))
89 {
90 g_set_error_literal (err: error, G_IO_ERROR, code: G_IO_ERROR_PARTIAL_INPUT,
91 _("Need complete input to do conversion"));
92 return G_CONVERTER_ERROR;
93 }
94
95 count = _gdk_x11_display_text_property_to_utf8_list (display: conv->display,
96 encoding: conv->encoding,
97 format: conv->format,
98 text: inbuf,
99 length: inbuf_size,
100 list: &list);
101 if (count < 0)
102 {
103 /* XXX: add error handling from gdkselection-x11.c */
104 g_set_error_literal (err: error, G_IO_ERROR, code: G_IO_ERROR_NO_SPACE,
105 _("Not enough space in destination"));
106 return G_CONVERTER_ERROR;
107 }
108 else if (count == 0)
109 {
110 *bytes_read = inbuf_size;
111 return write_output (outbuf, outbuf_size, bytes_written, data: "", len: 1, error);
112 }
113 else
114 {
115 GConverterResult result;
116
117 result = write_output (outbuf, outbuf_size, bytes_written, data: list[0], len: -1, error);
118 g_strfreev (str_array: list);
119 *bytes_read = inbuf_size;
120 return result;
121 }
122}
123
124/* The specifications for COMPOUND_TEXT and STRING specify that C0 and
125 * C1 are not allowed except for \n and \t, however the X conversions
126 * routines for COMPOUND_TEXT only enforce this in one direction,
127 * causing cut-and-paste of \r and \r\n separated text to fail.
128 * This routine strips out all non-allowed C0 and C1 characters
129 * from the input string and also canonicalizes \r, and \r\n to \n
130 */
131char *
132gdk_x11_utf8_to_string_target (const char *utf8_str,
133 gboolean return_latin1)
134{
135 int len = strlen (s: utf8_str);
136 GString *result = g_string_sized_new (dfl_size: len);
137 const char *p = utf8_str;
138
139 while (*p)
140 {
141 if (*p == '\r')
142 {
143 p++;
144 if (*p == '\n')
145 p++;
146
147 g_string_append_c (result, '\n');
148 }
149 else
150 {
151 gunichar ch = g_utf8_get_char (p);
152
153 if (!((ch < 0x20 && ch != '\t' && ch != '\n') || (ch >= 0x7f && ch < 0xa0)))
154 {
155 if (return_latin1)
156 {
157 if (ch <= 0xff)
158 g_string_append_c (result, ch);
159 else
160 g_string_append_printf (string: result,
161 format: ch < 0x10000 ? "\\u%04x" : "\\U%08x",
162 ch);
163 }
164 else
165 {
166 char buf[7];
167 int buflen;
168
169 buflen = g_unichar_to_utf8 (c: ch, outbuf: buf);
170 g_string_append_len (string: result, val: buf, len: buflen);
171 }
172 }
173
174 p = g_utf8_next_char (p);
175 }
176 }
177
178 return g_string_free (string: result, FALSE);
179}
180
181static GConverterResult
182gdk_x11_text_list_converter_encode (GdkX11TextListConverter *conv,
183 const void *inbuf,
184 gsize inbuf_size,
185 void *outbuf,
186 gsize outbuf_size,
187 GConverterFlags flags,
188 gsize *bytes_read,
189 gsize *bytes_written,
190 GError **error)
191{
192 if (!(flags & G_CONVERTER_INPUT_AT_END))
193 {
194 g_set_error_literal (err: error, G_IO_ERROR, code: G_IO_ERROR_PARTIAL_INPUT,
195 _("Need complete input to do conversion"));
196 return G_CONVERTER_ERROR;
197 }
198
199 if (g_str_equal (v1: conv->encoding, v2: "STRING") ||
200 g_str_equal (v1: conv->encoding, v2: "TEXT"))
201 {
202 GConverterResult result;
203 char *tmp, *latin1;
204
205 tmp = g_strndup (str: inbuf, n: inbuf_size);
206 latin1 = gdk_x11_utf8_to_string_target (utf8_str: tmp, TRUE);
207 g_free (mem: tmp);
208 if (latin1)
209 {
210 result = write_output (outbuf, outbuf_size, bytes_written, data: latin1, len: -1, error);
211 g_free (mem: latin1);
212 }
213 else
214 {
215 g_set_error_literal (err: error, G_IO_ERROR, code: G_IO_ERROR_INVALID_DATA,
216 _("Invalid byte sequence in conversion input"));
217 result = G_CONVERTER_ERROR;
218 }
219 return result;
220 }
221 else if (g_str_equal (v1: conv->encoding, v2: "COMPOUND_TEXT"))
222 {
223 GConverterResult result;
224 guchar *text;
225 const char *encoding;
226 int format;
227 int new_length;
228 char *tmp;
229
230 tmp = g_strndup (str: inbuf, n: inbuf_size);
231 if (gdk_x11_display_utf8_to_compound_text (display: conv->display, str: tmp,
232 encoding: &encoding, format: &format, ctext: &text, length: &new_length))
233 {
234 if (g_str_equal (v1: encoding, v2: conv->encoding) &&
235 format == conv->format)
236 {
237 result = write_output (outbuf, outbuf_size, bytes_written, data: text, len: new_length, error);
238 }
239 else
240 {
241 g_set_error_literal (err: error, G_IO_ERROR, code: G_IO_ERROR_FAILED,
242 _("Invalid formats in compound text conversion."));
243 result = G_CONVERTER_ERROR;
244 }
245 gdk_x11_free_compound_text (ctext: text);
246 }
247 else
248 {
249 g_set_error_literal (err: error, G_IO_ERROR, code: G_IO_ERROR_INVALID_DATA,
250 _("Invalid byte sequence in conversion input"));
251 result = G_CONVERTER_ERROR;
252 }
253 g_free (mem: tmp);
254 return result;
255 }
256 else
257 {
258 g_set_error (err: error, G_IO_ERROR, code: G_IO_ERROR_FAILED,
259 _("Unsupported encoding ā€œ%sā€"), conv->encoding);
260 return G_CONVERTER_ERROR;
261 }
262
263 return FALSE;
264}
265
266static GConverterResult
267gdk_x11_text_list_converter_convert (GConverter *converter,
268 const void *inbuf,
269 gsize inbuf_size,
270 void *outbuf,
271 gsize outbuf_size,
272 GConverterFlags flags,
273 gsize *bytes_read,
274 gsize *bytes_written,
275 GError **error)
276{
277 GdkX11TextListConverter *conv = GDK_X11_TEXT_LIST_CONVERTER (converter);
278
279 if (conv->encoder)
280 {
281 return gdk_x11_text_list_converter_encode (conv,
282 inbuf, inbuf_size,
283 outbuf, outbuf_size,
284 flags,
285 bytes_read, bytes_written,
286 error);
287 }
288 else
289 {
290 return gdk_x11_text_list_converter_decode (conv,
291 inbuf, inbuf_size,
292 outbuf, outbuf_size,
293 flags,
294 bytes_read, bytes_written,
295 error);
296 }
297}
298
299static void
300gdk_x11_text_list_converter_reset (GConverter *converter)
301{
302}
303
304static void
305gdk_x11_text_list_converter_iface_init (GConverterIface *iface)
306{
307 iface->convert = gdk_x11_text_list_converter_convert;
308 iface->reset = gdk_x11_text_list_converter_reset;
309}
310
311G_DEFINE_TYPE_WITH_CODE (GdkX11TextListConverter, gdk_x11_text_list_converter, G_TYPE_OBJECT,
312 G_IMPLEMENT_INTERFACE (G_TYPE_CONVERTER,
313 gdk_x11_text_list_converter_iface_init))
314
315static void
316gdk_x11_text_list_converter_finalize (GObject *object)
317{
318 GdkX11TextListConverter *conv = GDK_X11_TEXT_LIST_CONVERTER (object);
319
320 g_object_unref (object: conv->display);
321
322 G_OBJECT_CLASS (gdk_x11_text_list_converter_parent_class)->finalize (object);
323}
324
325static void
326gdk_x11_text_list_converter_class_init (GdkX11TextListConverterClass *klass)
327{
328 GObjectClass *object_class = G_OBJECT_CLASS (klass);
329
330 object_class->finalize = gdk_x11_text_list_converter_finalize;
331}
332
333static void
334gdk_x11_text_list_converter_init (GdkX11TextListConverter *local)
335{
336}
337
338GConverter *
339gdk_x11_text_list_converter_to_utf8_new (GdkDisplay *display,
340 const char *encoding,
341 int format)
342{
343 GdkX11TextListConverter *conv;
344
345 conv = g_object_new (GDK_TYPE_X11_TEXT_LIST_CONVERTER, NULL);
346
347 conv->display = g_object_ref (display);
348 conv->encoding = g_intern_string (string: encoding);
349 conv->format = format;
350
351 return G_CONVERTER (conv);
352}
353
354GConverter *
355gdk_x11_text_list_converter_from_utf8_new (GdkDisplay *display,
356 const char *encoding,
357 int format)
358{
359 GdkX11TextListConverter *conv;
360
361 conv = g_object_new (GDK_TYPE_X11_TEXT_LIST_CONVERTER, NULL);
362
363 conv->display = g_object_ref (display);
364 conv->encoding = g_intern_string (string: encoding);
365 conv->format = format;
366 conv->encoder = TRUE;
367
368 return G_CONVERTER (conv);
369}
370
371

source code of gtk/gdk/x11/gdktextlistconverter-x11.c