Convert output to the current locale.
authorAnas Nashif <anas.nashif@intel.com>
Thu, 11 Oct 2012 19:26:20 +0000 (12:26 -0700)
committerAnas Nashif <anas.nashif@intel.com>
Sun, 3 Feb 2013 00:44:14 +0000 (16:44 -0800)
Assumes utf8 input if the decoding works, otherwise iso-8859-1.
(imported from suse rpm)

lib/tagexts.c

index 29b2bae..563c1b4 100644 (file)
@@ -2,6 +2,7 @@
  * \file lib/formats.c
  */
 
+#include <wchar.h>
 #include "system.h"
 
 #include <rpm/rpmtypes.h>
@@ -191,6 +192,114 @@ exit:
     return rc;
 }
 
+static char * strtolocale(char *str)
+{
+    wchar_t *wstr, *wp;
+    const unsigned char *cp;
+    char *cc;
+    int state = 0;
+    int c;
+    int ccl, cca, mb_cur_max;
+    size_t l;
+    mbstate_t ps;
+    int strisutf8 = 1;
+    int locisutf8 = 1;
+
+    if (!str)
+       return 0;
+    if (!*str)
+       return str;
+    wstr = (wchar_t *)xmalloc((strlen(str) + 1) * sizeof(*wstr));
+    wp = wstr;
+    cp = (const unsigned char *)str;
+    while ((c = *cp++) != 0) {
+       if (state) {
+           if ((c & 0xc0) != 0x80) {
+               /* encoding error */
+               break;
+           }
+           c = (c & 0x3f) | (state << 6);
+           if (!(state & 0x40000000)) {
+             /* check for overlong sequences */
+               if ((c & 0x820823e0) == 0x80000000)
+                   c = 0xfdffffff;
+               else if ((c & 0x020821f0) == 0x02000000)
+                   c = 0xfff7ffff;
+               else if ((c & 0x000820f8) == 0x00080000)
+                   c = 0xffffd000;
+               else if ((c & 0x0000207c) == 0x00002000)
+                   c = 0xffffff70;
+           }
+       } else {
+           /* new sequence */
+           if (c >= 0xfe)
+               c = 0xfffd;
+           else if (c >= 0xfc)
+               c = (c & 0x01) | 0xbffffffc;    /* 5 bytes to follow */
+           else if (c >= 0xf8)
+               c = (c & 0x03) | 0xbfffff00;    /* 4 */
+           else if (c >= 0xf0)
+               c = (c & 0x07) | 0xbfffc000;    /* 3 */
+           else if (c >= 0xe0)
+               c = (c & 0x0f) | 0xbff00000;    /* 2 */
+           else if (c >= 0xc2)
+               c = (c & 0x1f) | 0xfc000000;    /* 1 */
+           else if (c >= 0xc0)
+               c = 0xfdffffff;         /* overlong */
+           else if (c >= 0x80)
+               c = 0xfffd;
+        }
+       state = (c & 0x80000000) ? c : 0;
+       if (state)
+           continue;
+       *wp++ = (wchar_t)c;
+    }
+    if (state) {
+       /* encoding error, assume latin1 */
+        strisutf8 = 0;
+       cp = (const unsigned char *)str;
+       wp = wstr;
+       while ((c = *cp++) != 0) {
+           *wp++ = (wchar_t)c;
+       }
+    }
+    *wp = 0;
+    mb_cur_max = MB_CUR_MAX;
+    memset(&ps, 0, sizeof(ps));
+    cc = xmalloc(mb_cur_max);
+    /* test locale encoding */
+    if (wcrtomb(cc, 0x20ac, &ps) != 3 || memcmp(cc, "\342\202\254", 3))
+       locisutf8 = 0;
+    if (locisutf8 == strisutf8) {
+       wstr = _free(wstr);
+       return str;
+    }
+    str = _free((char *)str);
+    memset(&ps, 0, sizeof(ps));
+    ccl = cca = 0;
+    for (wp = wstr; ; wp++) {
+       l = wcrtomb(cc + ccl, *wp, &ps);
+       if (*wp == 0)
+           break;
+       if (l == (size_t)-1) {
+           if (*wp < (wchar_t)256 && mbsinit(&ps)) {
+               cc[ccl] = *wp;
+               l = 1;
+           } else
+               l = wcrtomb(cc + ccl, (wchar_t)'?', &ps);
+       }
+        if (l == 0 || l == (size_t)-1)
+           continue;
+        ccl += l;
+        if (ccl > cca) {
+           cca = ccl + 16;
+           cc = xrealloc(cc, cca + mb_cur_max);
+       }
+    }
+    wstr = _free(wstr);
+    return (char *)cc;
+}
+
 /**
  * Retrieve trigger info.
  * @param h            header
@@ -508,10 +617,41 @@ static int i18nTag(Header h, rpmTag tag, rpmtd td, headerGetFlags hgflags)
 #endif
 
     rc = headerGet(h, tag, td, HEADERGET_ALLOC);
+    if (rc && td->data) {
+       td->data = strtolocale(td->data);
+    }
     return rc;
 }
 
 /**
+ * Retrieve text and convert to locale.
+ */
+static int localeTag(Header h, rpmTag tag, rpmtd td)
+{
+    int rc;
+    rc = headerGet(h, tag, td, HEADERGET_ALLOC);
+    if (!rc)
+       return 0;
+    if (td->type == RPM_STRING_TYPE) {
+       td->data = strtolocale(td->data);
+       td->count = 1;
+    } else if (td->type == RPM_STRING_ARRAY_TYPE) {
+       char **arr;
+       int i;
+       arr = xmalloc(td->count * sizeof(*arr));
+       for (i = 0; i < td->count; i++) {
+           arr[i] = xstrdup(((char **)td->data)[i]);
+           arr[i] = strtolocale(arr[i]);
+       }
+       _free(td->data);
+       td->data = arr;
+       td->flags = RPMTD_ALLOCED | RPMTD_PTR_ALLOCED;
+    }
+    return rc; 
+}
+
+
+/**
  * Retrieve summary text.
  * @param h            header
  * @retval td          tag data container
@@ -533,6 +673,16 @@ static int descriptionTag(Header h, rpmtd td, headerGetFlags hgflags)
     return i18nTag(h, RPMTAG_DESCRIPTION, td, hgflags);
 }
 
+static int changelognameTag(Header h, rpmtd td)
+{
+    return localeTag(h, RPMTAG_CHANGELOGNAME, td);
+}
+
+static int changelogtextTag(Header h, rpmtd td)
+{
+    return localeTag(h, RPMTAG_CHANGELOGTEXT, td);
+}
+
 /**
  * Retrieve group text.
  * @param h            header
@@ -812,6 +962,8 @@ static const struct headerTagFunc_s rpmHeaderTagExtensions[] = {
     { RPMTAG_LONGARCHIVESIZE,  longarchivesizeTag },
     { RPMTAG_LONGSIZE,         longsizeTag },
     { RPMTAG_LONGSIGSIZE,      longsigsizeTag },
+    { RPMTAG_CHANGELOGNAME,     changelognameTag },
+    { RPMTAG_CHANGELOGTEXT,     changelogtextTag },
     { RPMTAG_DBINSTANCE,       dbinstanceTag },
     { RPMTAG_EVR,              evrTag },
     { RPMTAG_NVR,              nvrTag },