byte-based C locale, phase 2: stdio and iconv (multibyte callers)
authorRich Felker <dalias@aerifal.cx>
Tue, 16 Jun 2015 05:35:31 +0000 (05:35 +0000)
committerRich Felker <dalias@aerifal.cx>
Tue, 16 Jun 2015 06:10:29 +0000 (06:10 +0000)
this patch adjusts libc components which use the multibyte functions
internally, and which depend on them operating in a particular
encoding, to make the appropriate locale changes before calling them
and restore the calling thread's locale afterwards. activating the
byte-based C locale without these changes would cause regressions in
stdio and iconv.

in the case of iconv, the current implementation was simply using the
multibyte functions as UTF-8 conversions. setting a multibyte UTF-8
locale for the duration of the iconv operation allows the code to
continue working.

in the case of stdio, POSIX requires that FILE streams have an
encoding rule bound at the time of setting wide orientation. as long
as all locales, including the C locale, used the same encoding,
treating high bytes as UTF-8, there was no need to store an encoding
rule as part of the stream's state.

a new locale field in the FILE structure points to the locale that
should be made active during fgetwc/fputwc/ungetwc on the stream. it
cannot point to the locale active at the time the stream becomes
oriented, because this locale could be mutable (the global locale) or
could be destroyed (locale_t objects produced by newlocale) before the
stream is closed. instead, a pointer to the static C or C.UTF-8 locale
object added in commit commit aeeac9ca5490d7d90fe061ab72da446c01ddf746
is used. this is valid since categories other than LC_CTYPE will not
affect these functions.

src/internal/stdio_impl.h
src/locale/iconv.c
src/stdio/fgetwc.c
src/stdio/fputwc.c
src/stdio/fputws.c
src/stdio/fwide.c
src/stdio/ungetwc.c

index e1325fe1d13083a0bc2b717d51f274c328388f76..72c55192727067a84d72d6f85467a0a28839aa5a 100644 (file)
@@ -47,6 +47,7 @@ struct _IO_FILE {
        unsigned char *shend;
        off_t shlim, shcnt;
        FILE *prev_locked, *next_locked;
+       struct __locale_struct *locale;
 };
 
 size_t __stdio_read(FILE *, unsigned char *, size_t);
index e6121aea1ac0c306234a7f0220d59931b6684e68..1eeea94e0a38753691269b978f105b5e65ee33b8 100644 (file)
@@ -5,6 +5,7 @@
 #include <stdlib.h>
 #include <limits.h>
 #include <stdint.h>
+#include "locale_impl.h"
 
 #define UTF_32BE    0300
 #define UTF_16LE    0301
@@ -165,9 +166,12 @@ size_t iconv(iconv_t cd0, char **restrict in, size_t *restrict inb, char **restr
        int err;
        unsigned char type = map[-1];
        unsigned char totype = tomap[-1];
+       locale_t *ploc = &CURRENT_LOCALE, loc = *ploc;
 
        if (!in || !*in || !*inb) return 0;
 
+       *ploc = UTF8_LOCALE;
+
        for (; *inb; *in+=l, *inb-=l) {
                c = *(unsigned char *)*in;
                l = 1;
@@ -431,6 +435,7 @@ size_t iconv(iconv_t cd0, char **restrict in, size_t *restrict inb, char **restr
                        break;
                }
        }
+       *ploc = loc;
        return x;
 ilseq:
        err = EILSEQ;
@@ -445,5 +450,6 @@ starved:
        x = -1;
 end:
        errno = err;
+       *ploc = loc;
        return x;
 }
index b261b44fde5697fef6e59ff91954133f8feecc74..e455cfec408bc101c7c15077e68d45f4491e3395 100644 (file)
@@ -1,8 +1,9 @@
 #include "stdio_impl.h"
+#include "locale_impl.h"
 #include <wchar.h>
 #include <errno.h>
 
-wint_t __fgetwc_unlocked(FILE *f)
+static wint_t __fgetwc_unlocked_internal(FILE *f)
 {
        mbstate_t st = { 0 };
        wchar_t wc;
@@ -10,8 +11,6 @@ wint_t __fgetwc_unlocked(FILE *f)
        unsigned char b;
        size_t l;
 
-       if (f->mode <= 0) fwide(f, 1);
-
        /* Convert character from buffer if possible */
        if (f->rpos < f->rend) {
                l = mbrtowc(&wc, (void *)f->rpos, f->rend - f->rpos, &st);
@@ -39,6 +38,16 @@ wint_t __fgetwc_unlocked(FILE *f)
        return wc;
 }
 
+wint_t __fgetwc_unlocked(FILE *f)
+{
+       locale_t *ploc = &CURRENT_LOCALE, loc = *ploc;
+       if (f->mode <= 0) fwide(f, 1);
+       *ploc = f->locale;
+       wchar_t wc = __fgetwc_unlocked_internal(f);
+       *ploc = loc;
+       return wc;
+}
+
 wint_t fgetwc(FILE *f)
 {
        wint_t c;
index 1bf165bfb850fa14d6a4a70fbed81b6ba773dc32..789fe9c90e98e72faccb570f373bb0ef8f5c384d 100644 (file)
@@ -1,4 +1,5 @@
 #include "stdio_impl.h"
+#include "locale_impl.h"
 #include <wchar.h>
 #include <limits.h>
 #include <ctype.h>
@@ -7,8 +8,10 @@ wint_t __fputwc_unlocked(wchar_t c, FILE *f)
 {
        char mbc[MB_LEN_MAX];
        int l;
+       locale_t *ploc = &CURRENT_LOCALE, loc = *ploc;
 
        if (f->mode <= 0) fwide(f, 1);
+       *ploc = f->locale;
 
        if (isascii(c)) {
                c = putc_unlocked(c, f);
@@ -20,6 +23,8 @@ wint_t __fputwc_unlocked(wchar_t c, FILE *f)
                l = wctomb(mbc, c);
                if (l < 0 || __fwritex((void *)mbc, l, f) < l) c = WEOF;
        }
+       if (c==WEOF) f->flags |= F_ERR;
+       *ploc = loc;
        return c;
 }
 
index 317d65f136dd8bef134b00cd3dbb4b605d401ebd..0ed02f1c004e8622e0d4367116a60f22e249720f 100644 (file)
@@ -1,23 +1,28 @@
 #include "stdio_impl.h"
+#include "locale_impl.h"
 #include <wchar.h>
 
 int fputws(const wchar_t *restrict ws, FILE *restrict f)
 {
        unsigned char buf[BUFSIZ];
        size_t l=0;
+       locale_t *ploc = &CURRENT_LOCALE, loc = *ploc;
 
        FLOCK(f);
 
        fwide(f, 1);
+       *ploc = f->locale;
 
        while (ws && (l = wcsrtombs((void *)buf, (void*)&ws, sizeof buf, 0))+1 > 1)
                if (__fwritex(buf, l, f) < l) {
                        FUNLOCK(f);
+                       *ploc = loc;
                        return -1;
                }
 
        FUNLOCK(f);
 
+       *ploc = loc;
        return l; /* 0 or -1 */
 }
 
index 8088e7ad4311f116a579c998f7aa4bee3e3e85c7..8410b1530780d894e5afc2c9e9769011e75b0ac2 100644 (file)
@@ -1,13 +1,14 @@
-#include <wchar.h>
 #include "stdio_impl.h"
-
-#define SH (8*sizeof(int)-1)
-#define NORMALIZE(x) ((x)>>SH | -((-(x))>>SH))
+#include "locale_impl.h"
 
 int fwide(FILE *f, int mode)
 {
        FLOCK(f);
-       if (!f->mode) f->mode = NORMALIZE(mode);
+       if (mode) {
+               if (!f->locale) f->locale = MB_CUR_MAX==1
+                       ? C_LOCALE : UTF8_LOCALE;
+               if (!f->mode) f->mode = mode>0 ? 1 : -1;
+       }
        mode = f->mode;
        FUNLOCK(f);
        return mode;
index d4c7de39284a3fbf9fdacfb5dbedb26cd39a5ac7..80d6e203d0765564a21e016f8be9577f6a3c67a2 100644 (file)
@@ -1,4 +1,5 @@
 #include "stdio_impl.h"
+#include "locale_impl.h"
 #include <wchar.h>
 #include <limits.h>
 #include <ctype.h>
@@ -8,15 +9,18 @@ wint_t ungetwc(wint_t c, FILE *f)
 {
        unsigned char mbc[MB_LEN_MAX];
        int l=1;
+       locale_t *ploc = &CURRENT_LOCALE, loc = *ploc;
 
        FLOCK(f);
 
        if (f->mode <= 0) fwide(f, 1);
+       *ploc = f->locale;
 
        if (!f->rpos) __toread(f);
        if (!f->rpos || f->rpos < f->buf - UNGET + l || c == WEOF ||
            (!isascii(c) && (l = wctomb((void *)mbc, c)) < 0)) {
                FUNLOCK(f);
+               *ploc = loc;
                return WEOF;
        }
 
@@ -26,5 +30,6 @@ wint_t ungetwc(wint_t c, FILE *f)
        f->flags &= ~F_EOF;
 
        FUNLOCK(f);
+       *ploc = loc;
        return c;
 }