19240031aSJohn Baldwin /*- 2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 3d915a14eSPedro F. Giffuni * 4179fa75eSJohn Baldwin * Copyright (c) 2013 Hudson River Trading LLC 59240031aSJohn Baldwin * Written by: John H. Baldwin <jhb@FreeBSD.org> 69240031aSJohn Baldwin * All rights reserved. 79240031aSJohn Baldwin * 89240031aSJohn Baldwin * Redistribution and use in source and binary forms, with or without 99240031aSJohn Baldwin * modification, are permitted provided that the following conditions 109240031aSJohn Baldwin * are met: 119240031aSJohn Baldwin * 1. Redistributions of source code must retain the above copyright 129240031aSJohn Baldwin * notice, this list of conditions and the following disclaimer. 139240031aSJohn Baldwin * 2. Redistributions in binary form must reproduce the above copyright 149240031aSJohn Baldwin * notice, this list of conditions and the following disclaimer in the 159240031aSJohn Baldwin * documentation and/or other materials provided with the distribution. 169240031aSJohn Baldwin * 179240031aSJohn Baldwin * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 189240031aSJohn Baldwin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 199240031aSJohn Baldwin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 209240031aSJohn Baldwin * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 219240031aSJohn Baldwin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 229240031aSJohn Baldwin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 239240031aSJohn Baldwin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 249240031aSJohn Baldwin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 259240031aSJohn Baldwin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 269240031aSJohn Baldwin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 279240031aSJohn Baldwin * SUCH DAMAGE. 289240031aSJohn Baldwin */ 299240031aSJohn Baldwin 309240031aSJohn Baldwin #include <sys/cdefs.h> 319240031aSJohn Baldwin __FBSDID("$FreeBSD$"); 329240031aSJohn Baldwin 339240031aSJohn Baldwin #include "namespace.h" 349240031aSJohn Baldwin #include <assert.h> 359240031aSJohn Baldwin #include <errno.h> 369240031aSJohn Baldwin #include <limits.h> 37c6e61550SEnji Cooper #ifdef DEBUG 38c6e61550SEnji Cooper #include <stdint.h> 39c6e61550SEnji Cooper #endif 409240031aSJohn Baldwin #include <stdio.h> 419240031aSJohn Baldwin #include <stdlib.h> 429240031aSJohn Baldwin #include <string.h> 439240031aSJohn Baldwin #include <wchar.h> 449240031aSJohn Baldwin #include "un-namespace.h" 459240031aSJohn Baldwin 469240031aSJohn Baldwin /* XXX: There is no FPOS_MAX. This assumes fpos_t is an off_t. */ 479240031aSJohn Baldwin #define FPOS_MAX OFF_MAX 489240031aSJohn Baldwin 499240031aSJohn Baldwin struct wmemstream { 509240031aSJohn Baldwin wchar_t **bufp; 519240031aSJohn Baldwin size_t *sizep; 529240031aSJohn Baldwin ssize_t len; 539240031aSJohn Baldwin fpos_t offset; 549240031aSJohn Baldwin mbstate_t mbstate; 559240031aSJohn Baldwin }; 569240031aSJohn Baldwin 579240031aSJohn Baldwin static int 589240031aSJohn Baldwin wmemstream_grow(struct wmemstream *ms, fpos_t newoff) 599240031aSJohn Baldwin { 609240031aSJohn Baldwin wchar_t *buf; 619240031aSJohn Baldwin ssize_t newsize; 629240031aSJohn Baldwin 639240031aSJohn Baldwin if (newoff < 0 || newoff >= SSIZE_MAX / sizeof(wchar_t)) 649240031aSJohn Baldwin newsize = SSIZE_MAX / sizeof(wchar_t) - 1; 659240031aSJohn Baldwin else 669240031aSJohn Baldwin newsize = newoff; 679240031aSJohn Baldwin if (newsize > ms->len) { 689f36610fSPedro F. Giffuni buf = reallocarray(*ms->bufp, newsize + 1, sizeof(wchar_t)); 699240031aSJohn Baldwin if (buf != NULL) { 709240031aSJohn Baldwin #ifdef DEBUG 719240031aSJohn Baldwin fprintf(stderr, "WMS: %p growing from %zd to %zd\n", 729240031aSJohn Baldwin ms, ms->len, newsize); 739240031aSJohn Baldwin #endif 749240031aSJohn Baldwin wmemset(buf + ms->len + 1, 0, newsize - ms->len); 759240031aSJohn Baldwin *ms->bufp = buf; 769240031aSJohn Baldwin ms->len = newsize; 779240031aSJohn Baldwin return (1); 789240031aSJohn Baldwin } 799240031aSJohn Baldwin return (0); 809240031aSJohn Baldwin } 819240031aSJohn Baldwin return (1); 829240031aSJohn Baldwin } 839240031aSJohn Baldwin 849240031aSJohn Baldwin static void 859240031aSJohn Baldwin wmemstream_update(struct wmemstream *ms) 869240031aSJohn Baldwin { 879240031aSJohn Baldwin 889240031aSJohn Baldwin assert(ms->len >= 0 && ms->offset >= 0); 899240031aSJohn Baldwin *ms->sizep = ms->len < ms->offset ? ms->len : ms->offset; 909240031aSJohn Baldwin } 919240031aSJohn Baldwin 929240031aSJohn Baldwin /* 939240031aSJohn Baldwin * Based on a starting multibyte state and an input buffer, determine 949240031aSJohn Baldwin * how many wchar_t's would be output. This doesn't use mbsnrtowcs() 959240031aSJohn Baldwin * so that it can handle embedded null characters. 969240031aSJohn Baldwin */ 979240031aSJohn Baldwin static size_t 989240031aSJohn Baldwin wbuflen(const mbstate_t *state, const char *buf, int len) 999240031aSJohn Baldwin { 1009240031aSJohn Baldwin mbstate_t lenstate; 1019240031aSJohn Baldwin size_t charlen, count; 1029240031aSJohn Baldwin 1039240031aSJohn Baldwin count = 0; 1049240031aSJohn Baldwin lenstate = *state; 1059240031aSJohn Baldwin while (len > 0) { 1069240031aSJohn Baldwin charlen = mbrlen(buf, len, &lenstate); 1079240031aSJohn Baldwin if (charlen == (size_t)-1) 1089240031aSJohn Baldwin return (-1); 1099240031aSJohn Baldwin if (charlen == (size_t)-2) 1109240031aSJohn Baldwin break; 1119240031aSJohn Baldwin if (charlen == 0) 1129240031aSJohn Baldwin /* XXX: Not sure how else to handle this. */ 1139240031aSJohn Baldwin charlen = 1; 1149240031aSJohn Baldwin len -= charlen; 1159240031aSJohn Baldwin buf += charlen; 1169240031aSJohn Baldwin count++; 1179240031aSJohn Baldwin } 1189240031aSJohn Baldwin return (count); 1199240031aSJohn Baldwin } 1209240031aSJohn Baldwin 1219240031aSJohn Baldwin static int 1229240031aSJohn Baldwin wmemstream_write(void *cookie, const char *buf, int len) 1239240031aSJohn Baldwin { 1249240031aSJohn Baldwin struct wmemstream *ms; 1259240031aSJohn Baldwin ssize_t consumed, wlen; 1269240031aSJohn Baldwin size_t charlen; 1279240031aSJohn Baldwin 1289240031aSJohn Baldwin ms = cookie; 1299240031aSJohn Baldwin wlen = wbuflen(&ms->mbstate, buf, len); 1309240031aSJohn Baldwin if (wlen < 0) { 1319240031aSJohn Baldwin errno = EILSEQ; 1329240031aSJohn Baldwin return (-1); 1339240031aSJohn Baldwin } 1349240031aSJohn Baldwin if (!wmemstream_grow(ms, ms->offset + wlen)) 1359240031aSJohn Baldwin return (-1); 1369240031aSJohn Baldwin 1379240031aSJohn Baldwin /* 1389240031aSJohn Baldwin * This copies characters one at a time rather than using 1399240031aSJohn Baldwin * mbsnrtowcs() so it can properly handle embedded null 1409240031aSJohn Baldwin * characters. 1419240031aSJohn Baldwin */ 1429240031aSJohn Baldwin consumed = 0; 1439240031aSJohn Baldwin while (len > 0 && ms->offset < ms->len) { 1449240031aSJohn Baldwin charlen = mbrtowc(*ms->bufp + ms->offset, buf, len, 1459240031aSJohn Baldwin &ms->mbstate); 1469240031aSJohn Baldwin if (charlen == (size_t)-1) { 1479240031aSJohn Baldwin if (consumed == 0) { 1489240031aSJohn Baldwin errno = EILSEQ; 1499240031aSJohn Baldwin return (-1); 1509240031aSJohn Baldwin } 1519240031aSJohn Baldwin /* Treat it as a successful short write. */ 1529240031aSJohn Baldwin break; 1539240031aSJohn Baldwin } 1549240031aSJohn Baldwin if (charlen == 0) 1559240031aSJohn Baldwin /* XXX: Not sure how else to handle this. */ 1569240031aSJohn Baldwin charlen = 1; 1579240031aSJohn Baldwin if (charlen == (size_t)-2) { 1589240031aSJohn Baldwin consumed += len; 1599240031aSJohn Baldwin len = 0; 1609240031aSJohn Baldwin } else { 1619240031aSJohn Baldwin consumed += charlen; 1629240031aSJohn Baldwin buf += charlen; 1639240031aSJohn Baldwin len -= charlen; 1649240031aSJohn Baldwin ms->offset++; 1659240031aSJohn Baldwin } 1669240031aSJohn Baldwin } 1679240031aSJohn Baldwin wmemstream_update(ms); 1689240031aSJohn Baldwin #ifdef DEBUG 1699240031aSJohn Baldwin fprintf(stderr, "WMS: write(%p, %d) = %zd\n", ms, len, consumed); 1709240031aSJohn Baldwin #endif 1719240031aSJohn Baldwin return (consumed); 1729240031aSJohn Baldwin } 1739240031aSJohn Baldwin 1749240031aSJohn Baldwin static fpos_t 1759240031aSJohn Baldwin wmemstream_seek(void *cookie, fpos_t pos, int whence) 1769240031aSJohn Baldwin { 1779240031aSJohn Baldwin struct wmemstream *ms; 1789240031aSJohn Baldwin fpos_t old; 1799240031aSJohn Baldwin 1809240031aSJohn Baldwin ms = cookie; 1819240031aSJohn Baldwin old = ms->offset; 1829240031aSJohn Baldwin switch (whence) { 1839240031aSJohn Baldwin case SEEK_SET: 1849240031aSJohn Baldwin /* _fseeko() checks for negative offsets. */ 1859240031aSJohn Baldwin assert(pos >= 0); 1869240031aSJohn Baldwin ms->offset = pos; 1879240031aSJohn Baldwin break; 1889240031aSJohn Baldwin case SEEK_CUR: 1899240031aSJohn Baldwin /* This is only called by _ftello(). */ 1909240031aSJohn Baldwin assert(pos == 0); 1919240031aSJohn Baldwin break; 1929240031aSJohn Baldwin case SEEK_END: 1939240031aSJohn Baldwin if (pos < 0) { 1949240031aSJohn Baldwin if (pos + ms->len < 0) { 1959240031aSJohn Baldwin #ifdef DEBUG 1969240031aSJohn Baldwin fprintf(stderr, 1979240031aSJohn Baldwin "WMS: bad SEEK_END: pos %jd, len %zd\n", 1989240031aSJohn Baldwin (intmax_t)pos, ms->len); 1999240031aSJohn Baldwin #endif 2009240031aSJohn Baldwin errno = EINVAL; 2019240031aSJohn Baldwin return (-1); 2029240031aSJohn Baldwin } 2039240031aSJohn Baldwin } else { 2049240031aSJohn Baldwin if (FPOS_MAX - ms->len < pos) { 2059240031aSJohn Baldwin #ifdef DEBUG 2069240031aSJohn Baldwin fprintf(stderr, 2079240031aSJohn Baldwin "WMS: bad SEEK_END: pos %jd, len %zd\n", 2089240031aSJohn Baldwin (intmax_t)pos, ms->len); 2099240031aSJohn Baldwin #endif 2109240031aSJohn Baldwin errno = EOVERFLOW; 2119240031aSJohn Baldwin return (-1); 2129240031aSJohn Baldwin } 2139240031aSJohn Baldwin } 2149240031aSJohn Baldwin ms->offset = ms->len + pos; 2159240031aSJohn Baldwin break; 2169240031aSJohn Baldwin } 2179240031aSJohn Baldwin /* Reset the multibyte state if a seek changes the position. */ 2189240031aSJohn Baldwin if (ms->offset != old) 2199240031aSJohn Baldwin memset(&ms->mbstate, 0, sizeof(ms->mbstate)); 2209240031aSJohn Baldwin wmemstream_update(ms); 2219240031aSJohn Baldwin #ifdef DEBUG 2229240031aSJohn Baldwin fprintf(stderr, "WMS: seek(%p, %jd, %d) %jd -> %jd\n", ms, 2239240031aSJohn Baldwin (intmax_t)pos, whence, (intmax_t)old, (intmax_t)ms->offset); 2249240031aSJohn Baldwin #endif 2259240031aSJohn Baldwin return (ms->offset); 2269240031aSJohn Baldwin } 2279240031aSJohn Baldwin 2289240031aSJohn Baldwin static int 2299240031aSJohn Baldwin wmemstream_close(void *cookie) 2309240031aSJohn Baldwin { 2319240031aSJohn Baldwin 2329240031aSJohn Baldwin free(cookie); 2339240031aSJohn Baldwin return (0); 2349240031aSJohn Baldwin } 2359240031aSJohn Baldwin 2369240031aSJohn Baldwin FILE * 2379240031aSJohn Baldwin open_wmemstream(wchar_t **bufp, size_t *sizep) 2389240031aSJohn Baldwin { 2399240031aSJohn Baldwin struct wmemstream *ms; 2409240031aSJohn Baldwin int save_errno; 2419240031aSJohn Baldwin FILE *fp; 2429240031aSJohn Baldwin 2439240031aSJohn Baldwin if (bufp == NULL || sizep == NULL) { 2449240031aSJohn Baldwin errno = EINVAL; 2459240031aSJohn Baldwin return (NULL); 2469240031aSJohn Baldwin } 2479240031aSJohn Baldwin *bufp = calloc(1, sizeof(wchar_t)); 2489240031aSJohn Baldwin if (*bufp == NULL) 2499240031aSJohn Baldwin return (NULL); 2509240031aSJohn Baldwin ms = malloc(sizeof(*ms)); 2519240031aSJohn Baldwin if (ms == NULL) { 2529240031aSJohn Baldwin save_errno = errno; 2539240031aSJohn Baldwin free(*bufp); 2549240031aSJohn Baldwin *bufp = NULL; 2559240031aSJohn Baldwin errno = save_errno; 2569240031aSJohn Baldwin return (NULL); 2579240031aSJohn Baldwin } 2589240031aSJohn Baldwin ms->bufp = bufp; 2599240031aSJohn Baldwin ms->sizep = sizep; 2609240031aSJohn Baldwin ms->len = 0; 2619240031aSJohn Baldwin ms->offset = 0; 2629240031aSJohn Baldwin memset(&ms->mbstate, 0, sizeof(mbstate_t)); 2639240031aSJohn Baldwin wmemstream_update(ms); 2649240031aSJohn Baldwin fp = funopen(ms, NULL, wmemstream_write, wmemstream_seek, 2659240031aSJohn Baldwin wmemstream_close); 2669240031aSJohn Baldwin if (fp == NULL) { 2679240031aSJohn Baldwin save_errno = errno; 2689240031aSJohn Baldwin free(ms); 2699240031aSJohn Baldwin free(*bufp); 2709240031aSJohn Baldwin *bufp = NULL; 2719240031aSJohn Baldwin errno = save_errno; 2729240031aSJohn Baldwin return (NULL); 2739240031aSJohn Baldwin } 2749240031aSJohn Baldwin fwide(fp, 1); 2759240031aSJohn Baldwin return (fp); 2769240031aSJohn Baldwin } 277