1 /* GDK - The GIMP Drawing Kit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 /*
19  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
20  * file for a list of people on the GTK+ Team.  See the ChangeLog
21  * files for a list of changes.  These files are distributed with
22  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
23  */
24 
25 #include "config.h"
26 
27 #include "gdkprivate-x11.h"
28 #include "gdkdisplay-x11.h"
29 
30 #include <X11/Xlib.h>
31 #include <X11/Xatom.h>
32 #include <string.h>
33 
34 
35 /**
36  * gdk_x11_display_text_property_to_text_list:
37  * @display: (type GdkX11Display): The `GdkDisplay` where the encoding is defined
38  * @encoding: a string representing the encoding. The most
39  *   common values for this are "STRING", or "COMPOUND_TEXT".
40  *   This is value used as the type for the property
41  * @format: the format of the property
42  * @text: The text data
43  * @length: The number of items to transform
44  * @list: location to store an  array of strings in
45  *   the encoding of the current locale. This array should be
46  *   freed using gdk_x11_free_text_list().
47  *
48  * Convert a text string from the encoding as it is stored
49  * in a property into an array of strings in the encoding of
50  * the current locale. (The elements of the array represent the
51  * nul-separated elements of the original text string.)
52  *
53  * Returns: the number of strings stored in list, or 0,
54  *   if the conversion failed
55  */
56 int
gdk_x11_display_text_property_to_text_list(GdkDisplay * display,const char * encoding,int format,const guchar * text,int length,char *** list)57 gdk_x11_display_text_property_to_text_list (GdkDisplay   *display,
58                                             const char   *encoding,
59                                             int           format,
60                                             const guchar *text,
61                                             int           length,
62                                             char       ***list)
63 {
64   XTextProperty property;
65   int count = 0;
66   int res;
67   char **local_list;
68   g_return_val_if_fail (GDK_IS_DISPLAY (display), 0);
69 
70   if (list)
71     *list = NULL;
72 
73   if (gdk_display_is_closed (display))
74     return 0;
75 
76   property.value = (guchar *)text;
77   property.encoding = gdk_x11_get_xatom_by_name_for_display (display, encoding);
78   property.format = format;
79   property.nitems = length;
80   res = XmbTextPropertyToTextList (GDK_DISPLAY_XDISPLAY (display), &property,
81                                    &local_list, &count);
82   if (res == XNoMemory || res == XLocaleNotSupported || res == XConverterNotFound)
83     return 0;
84   else
85     {
86       if (list)
87         *list = local_list;
88       else
89         XFreeStringList (local_list);
90 
91       return count;
92     }
93 }
94 
95 /**
96  * gdk_x11_free_text_list:
97  * @list: the value stored in the @list parameter by
98  *   a call to gdk_x11_display_text_property_to_text_list().
99  *
100  * Frees the array of strings created by
101  * gdk_x11_display_text_property_to_text_list().
102  */
103 void
gdk_x11_free_text_list(char ** list)104 gdk_x11_free_text_list (char **list)
105 {
106   g_return_if_fail (list != NULL);
107 
108   XFreeStringList (list);
109 }
110 
111 static int
make_list(const char * text,int length,gboolean latin1,char *** list)112 make_list (const char   *text,
113            int           length,
114            gboolean      latin1,
115            char       ***list)
116 {
117   GSList *strings = NULL;
118   int n_strings = 0;
119   int i;
120   const char *p = text;
121   const char *q;
122   GSList *tmp_list;
123   GError *error = NULL;
124 
125   while (p < text + length)
126     {
127       char *str;
128 
129       q = p;
130       while (*q && q < text + length)
131         q++;
132 
133       if (latin1)
134         {
135           str = g_convert (p, q - p,
136                            "UTF-8", "ISO-8859-1",
137                            NULL, NULL, &error);
138 
139           if (!str)
140             {
141               g_warning ("Error converting selection from STRING: %s",
142                          error->message);
143               g_error_free (error);
144             }
145         }
146       else
147         {
148           str = g_strndup (p, q - p);
149           if (!g_utf8_validate (str, -1, NULL))
150             {
151               g_warning ("Error converting selection from UTF8_STRING");
152               g_free (str);
153               str = NULL;
154             }
155         }
156 
157       if (str)
158         {
159           strings = g_slist_prepend (strings, str);
160           n_strings++;
161         }
162 
163       p = q + 1;
164     }
165 
166   if (list)
167     {
168       *list = g_new (char *, n_strings + 1);
169       (*list)[n_strings] = NULL;
170     }
171 
172   i = n_strings;
173   tmp_list = strings;
174   while (tmp_list)
175     {
176       if (list)
177         (*list)[--i] = tmp_list->data;
178       else
179         g_free (tmp_list->data);
180 
181       tmp_list = tmp_list->next;
182     }
183 
184   g_slist_free (strings);
185 
186   return n_strings;
187 }
188 
189 int
_gdk_x11_display_text_property_to_utf8_list(GdkDisplay * display,const char * encoding,int format,const guchar * text,int length,char *** list)190 _gdk_x11_display_text_property_to_utf8_list (GdkDisplay    *display,
191                                              const char    *encoding,
192                                              int            format,
193                                              const guchar  *text,
194                                              int            length,
195                                              char        ***list)
196 {
197   if (g_str_equal (encoding, "STRING"))
198     {
199       return make_list ((char *)text, length, TRUE, list);
200     }
201   else if (g_str_equal (encoding, "UTF8_STRING"))
202     {
203       return make_list ((char *)text, length, FALSE, list);
204     }
205   else
206     {
207       char **local_list;
208       int local_count;
209       int i;
210       const char *charset = NULL;
211       gboolean need_conversion = !g_get_charset (&charset);
212       int count = 0;
213       GError *error = NULL;
214 
215       /* Probably COMPOUND text, we fall back to Xlib routines
216        */
217       local_count = gdk_x11_display_text_property_to_text_list (display,
218                                                                 encoding,
219                                                                 format,
220                                                                 text,
221                                                                 length,
222                                                                 &local_list);
223       if (list)
224         *list = g_new (char *, local_count + 1);
225 
226       for (i=0; i<local_count; i++)
227         {
228           /* list contains stuff in our default encoding
229            */
230           if (need_conversion)
231             {
232               char *utf = g_convert (local_list[i], -1,
233                                       "UTF-8", charset,
234                                       NULL, NULL, &error);
235               if (utf)
236                 {
237                   if (list)
238                     (*list)[count++] = utf;
239                   else
240                     g_free (utf);
241                 }
242               else
243                 {
244                   g_warning ("Error converting to UTF-8 from '%s': %s",
245                              charset, error->message);
246                   g_error_free (error);
247                   error = NULL;
248                 }
249             }
250           else
251             {
252               if (list)
253                 {
254                   if (g_utf8_validate (local_list[i], -1, NULL))
255                     (*list)[count++] = g_strdup (local_list[i]);
256                   else
257                     g_warning ("Error converting selection");
258                 }
259             }
260         }
261 
262       if (local_count)
263         gdk_x11_free_text_list (local_list);
264 
265       if (list)
266         (*list)[count] = NULL;
267 
268       return count;
269     }
270 }
271 
272 /**
273  * gdk_x11_display_string_to_compound_text:
274  * @display: (type GdkX11Display): the `GdkDisplay` where the encoding is defined
275  * @str: a nul-terminated string
276  * @encoding: (out) (transfer none): location to store the encoding
277  *   (to be used as the type for the property)
278  * @format: (out): location to store the format of the property
279  * @ctext: (out) (array length=length): location to store newly
280  *   allocated data for the property
281  * @length: the length of @ctext, in bytes
282  *
283  * Convert a string from the encoding of the current
284  * locale into a form suitable for storing in a window property.
285  *
286  * Returns: 0 upon success, non-zero upon failure
287  */
288 int
gdk_x11_display_string_to_compound_text(GdkDisplay * display,const char * str,const char ** encoding,int * format,guchar ** ctext,int * length)289 gdk_x11_display_string_to_compound_text (GdkDisplay  *display,
290                                          const char  *str,
291                                          const char **encoding,
292                                          int         *format,
293                                          guchar     **ctext,
294                                          int         *length)
295 {
296   int res;
297   XTextProperty property;
298 
299   g_return_val_if_fail (GDK_IS_DISPLAY (display), 0);
300 
301   if (gdk_display_is_closed (display))
302     res = XLocaleNotSupported;
303   else
304     res = XmbTextListToTextProperty (GDK_DISPLAY_XDISPLAY (display),
305                                      (char **)&str, 1, XCompoundTextStyle,
306                                      &property);
307   if (res != Success)
308     {
309       property.encoding = None;
310       property.format = None;
311       property.value = NULL;
312       property.nitems = 0;
313     }
314 
315   if (encoding)
316     *encoding = gdk_x11_get_xatom_name_for_display (display, property.encoding);
317   if (format)
318     *format = property.format;
319   if (ctext)
320     *ctext = property.value;
321   if (length)
322     *length = property.nitems;
323 
324   return res;
325 }
326 
327 /**
328  * gdk_x11_display_utf8_to_compound_text:
329  * @display: (type GdkX11Display): a `GdkDisplay`
330  * @str: a UTF-8 string
331  * @encoding: (out) (transfer none): location to store resulting encoding
332  * @format: (out): location to store format of the result
333  * @ctext: (out) (array length=length): location to store the data of the result
334  * @length: location to store the length of the data stored in @ctext
335  *
336  * Converts from UTF-8 to compound text.
337  *
338  * Returns: %TRUE if the conversion succeeded, otherwise %FALSE
339  */
340 gboolean
gdk_x11_display_utf8_to_compound_text(GdkDisplay * display,const char * str,const char ** encoding,int * format,guchar ** ctext,int * length)341 gdk_x11_display_utf8_to_compound_text (GdkDisplay  *display,
342                                        const char  *str,
343                                        const char **encoding,
344                                        int         *format,
345                                        guchar     **ctext,
346                                        int         *length)
347 {
348   gboolean need_conversion;
349   const char *charset;
350   char *locale_str, *tmp_str;
351   GError *error = NULL;
352   gboolean result;
353 
354   g_return_val_if_fail (str != NULL, FALSE);
355   g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE);
356 
357   need_conversion = !g_get_charset (&charset);
358 
359   tmp_str = gdk_x11_utf8_to_string_target (str, FALSE);
360 
361   if (need_conversion)
362     {
363       locale_str = g_convert (tmp_str, -1,
364                               charset, "UTF-8",
365                               NULL, NULL, &error);
366       g_free (tmp_str);
367 
368       if (!locale_str)
369         {
370           if (!g_error_matches (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
371             {
372               g_warning ("Error converting from UTF-8 to '%s': %s",
373                          charset, error->message);
374             }
375           g_error_free (error);
376 
377           if (encoding)
378             *encoding = NULL;
379           if (format)
380             *format = None;
381           if (ctext)
382             *ctext = NULL;
383           if (length)
384             *length = 0;
385 
386           return FALSE;
387         }
388     }
389   else
390     locale_str = tmp_str;
391 
392   result = gdk_x11_display_string_to_compound_text (display, locale_str,
393                                                     encoding, format,
394                                                     ctext, length);
395   result = (result == Success? TRUE : FALSE);
396 
397   g_free (locale_str);
398 
399   return result;
400 }
401 
402 /**
403  * gdk_x11_free_compound_text:
404  * @ctext: The pointer stored in @ctext from a call to
405  *   gdk_x11_display_string_to_compound_text().
406  *
407  * Frees the data returned from gdk_x11_display_string_to_compound_text().
408  */
409 void
gdk_x11_free_compound_text(guchar * ctext)410 gdk_x11_free_compound_text (guchar *ctext)
411 {
412   if (ctext)
413     XFree (ctext);
414 }
415