[Xcb] [PATCH:xwininfo 1/2] Handle non-latin-1 window names

Alan Coopersmith alan.coopersmith at oracle.com
Tue Jun 29 23:04:33 PDT 2010


Uses _NET_WM_NAME to get UTF-8 encoding, iconv to convert to current locale
Warns that COMPOUND_TEXT WM_NAMEs aren't supported if _NET_WM_NAME isn't set
Adds local atom caching code to dsimple.c and uses it in all three *.c

Signed-off-by: Alan Coopersmith <alan.coopersmith at oracle.com>
---
 clientwin.c |   33 +++--------
 dsimple.c   |  196 +++++++++++++++++++++++++++++++++++++++++++++++++++--------
 dsimple.h   |   10 ++-
 xwininfo.c  |  165 ++++++++++++++++++++++++++++++++++++++++++-------
 4 files changed, 326 insertions(+), 78 deletions(-)

diff --git a/clientwin.c b/clientwin.c
index fe6bd18..2b1de04 100644
--- a/clientwin.c
+++ b/clientwin.c
@@ -26,9 +26,9 @@
 #include <string.h>
 
 #include "clientwin.h"
+#include "dsimple.h"
 
 static xcb_atom_t atom_wm_state = XCB_ATOM_NONE;
-typedef enum { False = 0, True } Bool;
 
 /*
  * Check if window has given property
@@ -139,9 +139,7 @@ Find_Client_In_Children(xcb_connection_t * dpy, xcb_window_t win)
 static xcb_window_t *
 Find_Roots(xcb_connection_t * dpy, xcb_window_t root, unsigned int *num)
 {
-    xcb_atom_t atom = XCB_ATOM_NONE;
-    xcb_intern_atom_cookie_t atom_cookie;
-    xcb_intern_atom_reply_t *atom_reply;
+    xcb_atom_t atom_virtual_root;
 
     xcb_get_property_cookie_t prop_cookie;
     xcb_get_property_reply_t *prop_reply;
@@ -150,18 +148,12 @@ Find_Roots(xcb_connection_t * dpy, xcb_window_t root, unsigned int *num)
 
     *num = 0;
 
-    atom_cookie = xcb_intern_atom (dpy, False, strlen("_NET_VIRTUAL_ROOTS"),
-                                   "_NET_VIRTUAL_ROOTS");
-    atom_reply = xcb_intern_atom_reply (dpy, atom_cookie, NULL);
-    if (atom_reply) {
-        atom = atom_reply->atom;
-        free (atom_reply);
-    }
-    if (!atom)
+    atom_virtual_root = Get_Atom (dpy, "_NET_VIRTUAL_ROOTS");
+    if (atom_virtual_root == XCB_ATOM_NONE)
         return NULL;
 
-    prop_cookie = xcb_get_property (dpy, False, root, atom, XCB_ATOM_WINDOW,
-                                    0, 0x7fffffff);
+    prop_cookie = xcb_get_property (dpy, False, root, atom_virtual_root,
+                                    XCB_ATOM_WINDOW, 0, 0x7fffffff);
     prop_reply = xcb_get_property_reply (dpy, prop_cookie, NULL);
     if (!prop_reply)
         return NULL;
@@ -235,17 +227,8 @@ Find_Client(xcb_connection_t * dpy, xcb_window_t root, xcb_window_t subwin)
     free (roots);
 
     if (atom_wm_state == XCB_ATOM_NONE) {
-        xcb_intern_atom_cookie_t atom_cookie;
-        xcb_intern_atom_reply_t *atom_reply;
-
-        atom_cookie = xcb_intern_atom (dpy, False,
-                                       strlen("WM_STATE"), "WM_STATE");
-        atom_reply = xcb_intern_atom_reply (dpy, atom_cookie, NULL);
-        if (atom_reply) {
-            atom_wm_state = atom_reply->atom;
-            free (atom_reply);
-        }
-        if (!atom_wm_state)
+        atom_wm_state = Get_Atom(dpy, "WM_STATE");
+        if (atom_wm_state == XCB_ATOM_NONE)
             return subwin;
     }
 
diff --git a/dsimple.c b/dsimple.c
index f28d270..a9b8678 100644
--- a/dsimple.c
+++ b/dsimple.c
@@ -230,6 +230,7 @@ xcb_window_t Select_Window(xcb_connection_t *dpy,
  */
 
 struct wininfo_cookies {
+    xcb_get_property_cookie_t get_net_wm_name;
     xcb_get_property_cookie_t get_wm_name;
     xcb_query_tree_cookie_t query_tree;
 };
@@ -240,6 +241,13 @@ struct wininfo_cookies {
 		      XCB_GET_PROPERTY_TYPE_ANY, 0, BUFSIZ)
 #endif
 
+static xcb_atom_t atom_net_wm_name, atom_utf8_string;
+
+# define xcb_get_net_wm_name(Dpy, Win)			 \
+    xcb_get_property (Dpy, False, Win, atom_net_wm_name, \
+		      atom_utf8_string, 0, BUFSIZ)
+
+
 static xcb_window_t
 recursive_Window_With_Name  (
     xcb_connection_t *dpy,
@@ -254,43 +262,71 @@ recursive_Window_With_Name  (
     xcb_generic_error_t *err;
     xcb_query_tree_reply_t *tree;
     struct wininfo_cookies *child_cookies;
+    xcb_get_property_reply_t *prop;
 
-#ifdef USE_XCB_ICCCM
-    xcb_get_text_property_reply_t prop;
+    if (cookies->get_net_wm_name.sequence) {
+	prop = xcb_get_property_reply (dpy, cookies->get_net_wm_name, &err);
 
-    if (xcb_get_wm_name_reply (dpy, cookies->get_wm_name, &prop, &err)) {
-	/* can't use strcmp, since prop.name is not null terminated */
-	if (strncmp (prop.name, name, prop.name_len) == 0) {
-	    w = window;
+	if (prop) {
+	    if (prop->type == atom_utf8_string) {
+		const char *prop_name = xcb_get_property_value (prop);
+		int prop_name_len = xcb_get_property_value_length (prop);
+
+		/* can't use strcmp, since prop.name is not null terminated */
+		if (strncmp (prop_name, name, prop_name_len) == 0) {
+		    w = window;
+		}
+	    }
+	    free (prop);
+	} else if (err) {
+	    if (err->response_type == 0)
+		Print_X_Error (dpy, err);
+	    return 0;
 	}
+    }
 
-	xcb_get_text_property_reply_wipe (&prop);
+    if (w) {
+	xcb_discard_reply (dpy, cookies->get_wm_name.sequence);
+    } else {
+#ifdef USE_XCB_ICCCM
+	xcb_get_text_property_reply_t nameprop;
+
+	if (xcb_get_wm_name_reply (dpy, cookies->get_wm_name,
+				   &nameprop, &err)) {
+	    /* can't use strcmp, since nameprop.name is not null terminated */
+	    if (strncmp (nameprop.name, name, nameprop.name_len) == 0) {
+		w = window;
+	    }
+
+	    xcb_get_text_property_reply_wipe (&nameprop);
+	}
 #else
-    xcb_get_property_reply_t *prop
-	= xcb_get_property_reply (dpy, cookies->get_wm_name, &err);
+	prop = xcb_get_property_reply (dpy, cookies->get_wm_name, &err);
 
-    if (prop) {
-	if (prop->type == XCB_ATOM_STRING) {
-	    const char *prop_name = xcb_get_property_value (prop);
-	    int prop_name_len = xcb_get_property_value_length (prop);
+	if (prop) {
+	    if (prop->type == XCB_ATOM_STRING) {
+		const char *prop_name = xcb_get_property_value (prop);
+		int prop_name_len = xcb_get_property_value_length (prop);
 
-	    /* can't use strcmp, since prop.name is not null terminated */
-	    if (strncmp (prop_name, name, prop_name_len) == 0) {
-		w = window;
+		/* can't use strcmp, since prop.name is not null terminated */
+		if (strncmp (prop_name, name, prop_name_len) == 0) {
+		    w = window;
+		}
 	    }
+	    free (prop);
 	}
-	free (prop);
 #endif
-
-	if (w)
-	{
-	    xcb_discard_reply (dpy, cookies->query_tree.sequence);
-	    return w;
+	else if (err) {
+	    if (err->response_type == 0)
+		Print_X_Error (dpy, err);
+	    return 0;
 	}
-    } else if (err) {
-	if (err->response_type == 0)
-	    Print_X_Error (dpy, err);
-	return 0;
+    }
+
+    if (w)
+    {
+	xcb_discard_reply (dpy, cookies->query_tree.sequence);
+	return w;
     }
 
     tree = xcb_query_tree_reply (dpy, cookies->query_tree, &err);
@@ -308,6 +344,9 @@ recursive_Window_With_Name  (
 	Fatal_Error("Failed to allocate memory in recursive_Window_With_Name");
 
     for (i = 0; i < nchildren; i++) {
+	if (atom_net_wm_name && atom_utf8_string)
+	    child_cookies[i].get_net_wm_name =
+		xcb_get_net_wm_name (dpy, children[i]);
 	child_cookies[i].get_wm_name = xcb_get_wm_name (dpy, children[i]);
 	child_cookies[i].query_tree = xcb_query_tree (dpy, children[i]);
     }
@@ -324,6 +363,9 @@ recursive_Window_With_Name  (
     {
 	/* clean up remaining replies */
 	for (/* keep previous i */; i < nchildren; i++) {
+	    if (child_cookies[i].get_net_wm_name.sequence)
+		xcb_discard_reply (dpy,
+				   child_cookies[i].get_net_wm_name.sequence);
 	    xcb_discard_reply (dpy, child_cookies[i].get_wm_name.sequence);
 	    xcb_discard_reply (dpy, child_cookies[i].query_tree.sequence);
 	}
@@ -342,8 +384,14 @@ Window_With_Name (
 {
     struct wininfo_cookies cookies;
 
+    atom_net_wm_name = Get_Atom (dpy, "_NET_WM_NAME");
+    atom_utf8_string = Get_Atom (dpy, "UTF8_STRING");
+
+    if (atom_net_wm_name && atom_utf8_string)
+	cookies.get_net_wm_name = xcb_get_net_wm_name (dpy, top);
     cookies.get_wm_name = xcb_get_wm_name (dpy, top);
     cookies.query_tree = xcb_query_tree (dpy, top);
+    xcb_flush (dpy);
     return recursive_Window_With_Name(dpy, top, &cookies, name);
 }
 
@@ -485,3 +533,99 @@ Print_X_Error (
 
     fprintf (stderr, "  Request serial number: %d\n", err->full_sequence);
 }
+
+/*
+ * Cache for atom lookups in either direction
+ */
+struct atom_cache_entry {
+    xcb_atom_t atom;
+    const char *name;
+    xcb_intern_atom_cookie_t intern_atom;
+    struct atom_cache_entry *next;
+};
+
+static struct atom_cache_entry *atom_cache;
+
+/*
+ * Send a request to the server for an atom by name
+ * Does not create the atom if it is not already present
+ */
+struct atom_cache_entry *Intern_Atom (xcb_connection_t * dpy, const char *name)
+{
+    struct atom_cache_entry *a;
+
+    for (a = atom_cache ; a != NULL ; a = a->next) {
+	if (strcmp (a->name, name) == 0)
+	    return a; /* already requested or found */
+    }
+
+    a = calloc(1, sizeof(struct atom_cache_entry));
+    if (a != NULL) {
+	a->name = name;
+	a->intern_atom = xcb_intern_atom (dpy, False, strlen (name), (name));
+	a->next = atom_cache;
+	atom_cache = a;
+    }
+    return a;
+}
+
+/* Get an atom by name when it is needed. */
+xcb_atom_t Get_Atom (xcb_connection_t * dpy, const char *name)
+{
+    struct atom_cache_entry *a = Intern_Atom (dpy, name);
+
+    if (a == NULL)
+	return XCB_ATOM_NONE;
+
+    if (a->atom == XCB_ATOM_NONE) {
+	xcb_intern_atom_reply_t *reply;
+
+	reply = xcb_intern_atom_reply(dpy, a->intern_atom, NULL);
+	if (reply) {
+	    a->atom = reply->atom;
+	    free (reply);
+	} else {
+	    a->atom = -1;
+	}
+    }
+    if (a->atom == -1) /* internal error */
+	return XCB_ATOM_NONE;
+
+    return a->atom;
+}
+
+/* Get the name for an atom when it is needed. */
+const char *Get_Atom_Name (xcb_connection_t * dpy, xcb_atom_t atom)
+{
+    struct atom_cache_entry *a;
+
+    for (a = atom_cache ; a != NULL ; a = a->next) {
+	if (a->atom == atom)
+	    return a->name; /* already requested or found */
+    }
+
+    a = calloc(1, sizeof(struct atom_cache_entry));
+    if (a != NULL) {
+	xcb_get_atom_name_cookie_t cookie = xcb_get_atom_name (dpy, atom);
+	xcb_get_atom_name_reply_t *reply
+	    = xcb_get_atom_name_reply (dpy, cookie, NULL);
+
+	a->atom = atom;
+	if (reply) {
+	    int len = xcb_get_atom_name_name_length (reply);
+	    char *name = malloc(len + 1);
+	    if (name) {
+		memcpy (name, xcb_get_atom_name_name (reply), len);
+		name[len] = '\0';
+		a->name = name;
+	    }
+	    free (reply);
+	}
+
+	a->next = atom_cache;
+	atom_cache = a;
+
+	return a->name;
+    }
+    return NULL;
+}
diff --git a/dsimple.h b/dsimple.h
index 1a689e0..b6adc43 100644
--- a/dsimple.h
+++ b/dsimple.h
@@ -51,9 +51,13 @@ const char *Get_Display_Name (const char *displayname);
 void Setup_Display_And_Screen (const char *displayname,
 			       xcb_connection_t **dpy, xcb_screen_t **screen);
 
-xcb_window_t Select_Window(xcb_connection_t *, const xcb_screen_t *, int);
-xcb_window_t Window_With_Name(xcb_connection_t *, xcb_window_t, const char *);
+xcb_window_t Select_Window (xcb_connection_t *, const xcb_screen_t *, int);
+xcb_window_t Window_With_Name (xcb_connection_t *, xcb_window_t, const char *);
 
-void Fatal_Error(char *, ...) _X_NORETURN _X_ATTRIBUTE_PRINTF(1, 2);
+void Fatal_Error (char *, ...) _X_NORETURN _X_ATTRIBUTE_PRINTF(1, 2);
 
 void Print_X_Error (xcb_connection_t *, xcb_generic_error_t *);
+
+struct atom_cache_entry *Intern_Atom (xcb_connection_t *, const char *);
+xcb_atom_t Get_Atom (xcb_connection_t *, const char *);
+const char *Get_Atom_Name (xcb_connection_t *, xcb_atom_t);
diff --git a/xwininfo.c b/xwininfo.c
index 6a3cda2..2c6221c 100644
--- a/xwininfo.c
+++ b/xwininfo.c
@@ -76,6 +76,9 @@ of the copyright holder.
 #include <stdlib.h>
 #include <string.h>
 #include <locale.h>
+#include <langinfo.h>
+#include <iconv.h>
+#include <errno.h>
 
 /* Include routines to handle parsing defaults */
 #include "dsimple.h"
@@ -178,12 +181,18 @@ enum {
     xcb_get_wm_size_hints(Dpy, Win, XCB_ATOM_WM_NORMAL_HINTS)
 #endif
 
+/* Possibly in xcb-emwh in the future? */
+static xcb_atom_t atom_net_wm_name, atom_utf8_string;
+static xcb_get_property_cookie_t get_net_wm_name (xcb_connection_t *,
+						  xcb_window_t);
+
 /* Information we keep track of for each window to allow prefetching/reusing */
 struct wininfo {
     xcb_window_t			window;
 
     /* cookies for requests we've sent */
     xcb_get_geometry_cookie_t		geometry_cookie;
+    xcb_get_property_cookie_t		net_wm_name_cookie;
     xcb_get_property_cookie_t		wm_name_cookie;
     xcb_get_property_cookie_t		wm_class_cookie;
     xcb_translate_coordinates_cookie_t	trans_coords_cookie;
@@ -222,6 +231,10 @@ static void wininfo_wipe (struct wininfo *);
 
 static const char *window_id_format = "0x%lx";
 
+static const char *user_encoding;
+static iconv_t iconv_from_utf8;
+static void print_utf8 (const char *, char *, size_t, const char *);
+
 static xcb_connection_t *dpy;
 static xcb_screen_t *screen;
 static xcb_generic_error_t *err;
@@ -407,6 +420,7 @@ main (int argc, char **argv)
 
     if (!setlocale (LC_ALL, ""))
 	fprintf (stderr, "%s: can not set locale properly\n", program_name);
+    user_encoding = nl_langinfo (CODESET);
 
     memset (w, 0, sizeof(struct wininfo));
 
@@ -493,6 +507,10 @@ main (int argc, char **argv)
 
     Setup_Display_And_Screen (display_name, &dpy, &screen);
 
+    /* preload atoms we may need later */
+    Intern_Atom (dpy, "_NET_WM_NAME");
+    Intern_Atom (dpy, "UTF8_STRING");
+
     /* initialize scaling data */
     scale_init(screen);
 
@@ -510,6 +528,8 @@ main (int argc, char **argv)
 		"xwininfo: Please select the window about which you\n"
 		"          would like information by clicking the\n"
 		"          mouse in that window.\n");
+	Intern_Atom (dpy, "_NET_VIRTUAL_ROOTS");
+	Intern_Atom (dpy, "WM_STATE");
 	window = Select_Window (dpy, screen, !frame);
     }
 
@@ -541,6 +561,7 @@ main (int argc, char **argv)
 
     /* Send requests to prefetch data we'll need */
     w->window = window;
+    w->net_wm_name_cookie = get_net_wm_name (dpy, window);
     w->wm_name_cookie = xcb_get_wm_name (dpy, window);
     if (children || tree)
 	w->tree_cookie = xcb_query_tree (dpy, window);
@@ -581,6 +602,9 @@ main (int argc, char **argv)
 
     wininfo_wipe (w);
     xcb_disconnect (dpy);
+    if (iconv_from_utf8 && (iconv_from_utf8 != (iconv_t) -1)) {
+	iconv_close (iconv_from_utf8);
+    }
     exit (0);
 }
 
@@ -694,13 +718,13 @@ static void
 Display_Window_Id (struct wininfo *w, Bool newline_wanted)
 {
 #ifdef USE_XCB_ICCCM
-    xcb_get_text_property_reply_t prop;
-#else
-    xcb_get_property_reply_t *prop;
+    xcb_get_text_property_reply_t wmn_reply;
 #endif
-    uint8_t got_reply;
-    const char *wm_name;
-    int wm_name_len;
+    xcb_get_property_reply_t *prop;
+    uint8_t got_reply = False;
+    const char *wm_name = NULL;
+    unsigned int wm_name_len = 0;
+    xcb_atom_t wm_name_encoding = XCB_NONE;
 
     printf (window_id_format, w->window);      /* print id # in hex/dec */
 
@@ -711,34 +735,54 @@ Display_Window_Id (struct wininfo *w, Bool newline_wanted)
 	    printf (" (the root window)");
 	}
 	/* Get window name if any */
-#ifdef USE_XCB_ICCCM
-	got_reply = xcb_get_wm_name_reply (dpy, w->wm_name_cookie,
-					   &prop, NULL);
-	if (got_reply) {
-	    wm_name = prop.name;
-	    wm_name_len = prop.name_len;
-	}
-#else
-	prop = xcb_get_property_reply (dpy, w->wm_name_cookie, NULL);
-	if (prop && (prop->type == XCB_ATOM_STRING)) {
+	prop = xcb_get_property_reply (dpy, w->net_wm_name_cookie, NULL);
+	if (prop && (prop->type != XCB_NONE)) {
 	    wm_name = xcb_get_property_value (prop);
-            wm_name_len = xcb_get_property_value_length (prop);
+	    wm_name_len = xcb_get_property_value_length (prop);
+	    wm_name_encoding = prop->type;
 	    got_reply = True;
-	} else {
-	    got_reply = False;
 	}
+
+	if (!got_reply) { /* No _NET_WM_NAME, check WM_NAME */
+#ifdef USE_XCB_ICCCM
+	    got_reply = xcb_get_wm_name_reply (dpy, w->wm_name_cookie,
+					       &wmn_reply, NULL);
+	    if (got_reply) {
+		wm_name = wmn_reply.name;
+		wm_name_len = wmn_reply.name_len;
+		wm_name_encoding = wmn_reply.encoding;
+	    }
+#else
+	    prop = xcb_get_property_reply (dpy, w->wm_name_cookie, NULL);
+	    if (prop && (prop->type != XCB_NONE)) {
+		wm_name = xcb_get_property_value (prop);
+		wm_name_len = xcb_get_property_value_length (prop);
+		wm_name_encoding = prop->type;
+		got_reply = True;
+	    }
 #endif
+	}
 	if (!got_reply || wm_name_len == 0) {
 	    printf (" (has no name)");
         } else {
-            printf (" \"");
-	    /* XXX: need to handle encoding */
-	    printf ("%.*s", wm_name_len, wm_name);
-            printf ("\"");
+	    if (wm_name_encoding == XCB_ATOM_STRING) {
+		printf (" \"%.*s\"", wm_name_len, wm_name);
+	    } else if (wm_name_encoding == atom_utf8_string) {
+		print_utf8 (" \"", (char *) wm_name, wm_name_len,  "\"");
+	    } else {
+		/* Encodings we don't support, including COMPOUND_TEXT */
+		const char *enc_name = Get_Atom_Name (dpy, wm_name_encoding);
+		if (enc_name) {
+		    printf (" (name in unsupported encoding %s)", enc_name);
+		} else {
+		    printf (" (name in unsupported encoding ATOM 0x%x)",
+			    wm_name_encoding);
+		}
+	    }
 	}
 #ifdef USE_XCB_ICCCM
 	if (got_reply)
-	    xcb_get_text_property_reply_wipe (&prop);
+	    xcb_get_text_property_reply_wipe (&wmn_reply);
 #else
 	free (prop);
 #endif
@@ -1166,8 +1210,10 @@ display_tree_info_1 (struct wininfo *w, int recurse, int level)
     if (level == 0) {
 	struct wininfo rw, pw;
 	rw.window = tree->root;
+	rw.net_wm_name_cookie = get_net_wm_name (dpy, rw.window);
 	rw.wm_name_cookie = xcb_get_wm_name (dpy, rw.window);
 	pw.window = tree->parent;
+	pw.net_wm_name_cookie = get_net_wm_name (dpy, pw.window);
 	pw.wm_name_cookie = xcb_get_wm_name (dpy, pw.window);
 	xcb_flush (dpy);
 
@@ -1199,6 +1245,7 @@ display_tree_info_1 (struct wininfo *w, int recurse, int level)
 	    struct wininfo *cw = &children[i];
 
 	    cw->window = child_list[i];
+	    cw->net_wm_name_cookie = get_net_wm_name (dpy, child_list[i]);
 	    cw->wm_name_cookie = xcb_get_wm_name (dpy, child_list[i]);
 	    cw->wm_class_cookie = xcb_get_wm_class (dpy, child_list[i]);
 	    cw->geometry_cookie = xcb_get_geometry (dpy, child_list[i]);
@@ -1536,6 +1583,7 @@ Display_WM_Info (struct wininfo *w)
     if (flags & XCB_WM_HINT_ICON_WINDOW) {
 	struct wininfo iw;
 	iw.window = wmhints.icon_window;
+	iw.net_wm_name_cookie = get_net_wm_name (dpy, iw.window);
 	iw.wm_name_cookie = xcb_get_wm_name (dpy, iw.window);
 
 	printf ("      Icon window id: ");
@@ -1559,3 +1607,72 @@ wininfo_wipe (struct wininfo *w)
     free (w->win_attributes);
     free (w->normal_hints);
 }
+
+/* Gets UTF-8 encoded EMWH property _NET_WM_NAME for a window */
+static xcb_get_property_cookie_t
+get_net_wm_name (xcb_connection_t *dpy, xcb_window_t win)
+{
+    if (!atom_net_wm_name)
+	atom_net_wm_name = Get_Atom (dpy, "_NET_WM_NAME");
+
+    if (!atom_utf8_string)
+	atom_utf8_string = Get_Atom (dpy, "UTF8_STRING");
+
+    if (atom_net_wm_name && atom_utf8_string)
+	return xcb_get_property (dpy, False, win, atom_net_wm_name,
+				 atom_utf8_string, 0, BUFSIZ);
+    else {
+	xcb_get_property_cookie_t dummy = { 0 };
+	return dummy;
+    }
+}
+
+/*
+ * Converts a UTF-8 encoded string to the current locale encoding,
+ * if possible, and prints it, with prefix before and suffix after.
+ * Length of the string is specified in bytes, or -1 for going until '\0'
+ */
+static void
+print_utf8 (const char *prefix, char *u8str, size_t length, const char *suffix)
+{
+    char convbuf[BUFSIZ];
+    char *inp = u8str;
+    size_t inlen = length;
+    int convres;
+
+    if (inlen < 0) {
+	inlen = strlen (inp);
+    }
+
+    if (!iconv_from_utf8) {
+	iconv_from_utf8 = iconv_open (user_encoding, "UTF-8");
+    }
+
+    if (iconv_from_utf8 != (iconv_t) -1) {
+	Bool done = True;
+
+	printf ("%s", prefix);
+	do {
+	    char *outp = convbuf;
+	    size_t outlen = sizeof(convbuf);
+
+	    convres = iconv (iconv_from_utf8, &inp, &inlen, &outp, &outlen);
+
+	    if ((convres == -1) && (errno == E2BIG)) {
+		done = False;
+		convres = 0;
+	    }
+
+	    if (convres == 0) {
+		fwrite (convbuf, 1, sizeof(convbuf) - outlen, stdout);
+	    } else {
+		printf (" (failure in conversion from UTF8_STRING to %s)",
+			user_encoding);
+	    }
+	} while (!done);
+	printf ("%s", suffix);
+    } else {
+	printf (" (can't load iconv conversion for UTF8_STRING to %s)",
+		user_encoding);
+    }
+}
-- 
1.5.6.5



More information about the Xcb mailing list