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 "namespace.h"
319240031aSJohn Baldwin #include <assert.h>
329240031aSJohn Baldwin #include <errno.h>
339240031aSJohn Baldwin #include <limits.h>
34c6e61550SEnji Cooper #ifdef DEBUG
35c6e61550SEnji Cooper #include <stdint.h>
36c6e61550SEnji Cooper #endif
379240031aSJohn Baldwin #include <stdio.h>
389240031aSJohn Baldwin #include <stdlib.h>
399240031aSJohn Baldwin #include <string.h>
409240031aSJohn Baldwin #include <wchar.h>
419240031aSJohn Baldwin #include "un-namespace.h"
429240031aSJohn Baldwin
439240031aSJohn Baldwin /* XXX: There is no FPOS_MAX. This assumes fpos_t is an off_t. */
449240031aSJohn Baldwin #define FPOS_MAX OFF_MAX
459240031aSJohn Baldwin
469240031aSJohn Baldwin struct wmemstream {
479240031aSJohn Baldwin wchar_t **bufp;
489240031aSJohn Baldwin size_t *sizep;
499240031aSJohn Baldwin ssize_t len;
509240031aSJohn Baldwin fpos_t offset;
519240031aSJohn Baldwin mbstate_t mbstate;
529240031aSJohn Baldwin };
539240031aSJohn Baldwin
549240031aSJohn Baldwin static int
wmemstream_grow(struct wmemstream * ms,fpos_t newoff)559240031aSJohn Baldwin wmemstream_grow(struct wmemstream *ms, fpos_t newoff)
569240031aSJohn Baldwin {
579240031aSJohn Baldwin wchar_t *buf;
589240031aSJohn Baldwin ssize_t newsize;
599240031aSJohn Baldwin
609240031aSJohn Baldwin if (newoff < 0 || newoff >= SSIZE_MAX / sizeof(wchar_t))
619240031aSJohn Baldwin newsize = SSIZE_MAX / sizeof(wchar_t) - 1;
629240031aSJohn Baldwin else
639240031aSJohn Baldwin newsize = newoff;
649240031aSJohn Baldwin if (newsize > ms->len) {
659f36610fSPedro F. Giffuni buf = reallocarray(*ms->bufp, newsize + 1, sizeof(wchar_t));
669240031aSJohn Baldwin if (buf != NULL) {
679240031aSJohn Baldwin #ifdef DEBUG
689240031aSJohn Baldwin fprintf(stderr, "WMS: %p growing from %zd to %zd\n",
699240031aSJohn Baldwin ms, ms->len, newsize);
709240031aSJohn Baldwin #endif
719240031aSJohn Baldwin wmemset(buf + ms->len + 1, 0, newsize - ms->len);
729240031aSJohn Baldwin *ms->bufp = buf;
739240031aSJohn Baldwin ms->len = newsize;
749240031aSJohn Baldwin return (1);
759240031aSJohn Baldwin }
769240031aSJohn Baldwin return (0);
779240031aSJohn Baldwin }
789240031aSJohn Baldwin return (1);
799240031aSJohn Baldwin }
809240031aSJohn Baldwin
819240031aSJohn Baldwin static void
wmemstream_update(struct wmemstream * ms)829240031aSJohn Baldwin wmemstream_update(struct wmemstream *ms)
839240031aSJohn Baldwin {
849240031aSJohn Baldwin
859240031aSJohn Baldwin assert(ms->len >= 0 && ms->offset >= 0);
869240031aSJohn Baldwin *ms->sizep = ms->len < ms->offset ? ms->len : ms->offset;
879240031aSJohn Baldwin }
889240031aSJohn Baldwin
899240031aSJohn Baldwin /*
909240031aSJohn Baldwin * Based on a starting multibyte state and an input buffer, determine
919240031aSJohn Baldwin * how many wchar_t's would be output. This doesn't use mbsnrtowcs()
929240031aSJohn Baldwin * so that it can handle embedded null characters.
939240031aSJohn Baldwin */
949240031aSJohn Baldwin static size_t
wbuflen(const mbstate_t * state,const char * buf,int len)959240031aSJohn Baldwin wbuflen(const mbstate_t *state, const char *buf, int len)
969240031aSJohn Baldwin {
979240031aSJohn Baldwin mbstate_t lenstate;
989240031aSJohn Baldwin size_t charlen, count;
999240031aSJohn Baldwin
1009240031aSJohn Baldwin count = 0;
1019240031aSJohn Baldwin lenstate = *state;
1029240031aSJohn Baldwin while (len > 0) {
1039240031aSJohn Baldwin charlen = mbrlen(buf, len, &lenstate);
1049240031aSJohn Baldwin if (charlen == (size_t)-1)
1059240031aSJohn Baldwin return (-1);
1069240031aSJohn Baldwin if (charlen == (size_t)-2)
1079240031aSJohn Baldwin break;
1089240031aSJohn Baldwin if (charlen == 0)
1099240031aSJohn Baldwin /* XXX: Not sure how else to handle this. */
1109240031aSJohn Baldwin charlen = 1;
1119240031aSJohn Baldwin len -= charlen;
1129240031aSJohn Baldwin buf += charlen;
1139240031aSJohn Baldwin count++;
1149240031aSJohn Baldwin }
1159240031aSJohn Baldwin return (count);
1169240031aSJohn Baldwin }
1179240031aSJohn Baldwin
1189240031aSJohn Baldwin static int
wmemstream_write(void * cookie,const char * buf,int len)1199240031aSJohn Baldwin wmemstream_write(void *cookie, const char *buf, int len)
1209240031aSJohn Baldwin {
1219240031aSJohn Baldwin struct wmemstream *ms;
1229240031aSJohn Baldwin ssize_t consumed, wlen;
1239240031aSJohn Baldwin size_t charlen;
1249240031aSJohn Baldwin
1259240031aSJohn Baldwin ms = cookie;
1269240031aSJohn Baldwin wlen = wbuflen(&ms->mbstate, buf, len);
1279240031aSJohn Baldwin if (wlen < 0) {
1289240031aSJohn Baldwin errno = EILSEQ;
1299240031aSJohn Baldwin return (-1);
1309240031aSJohn Baldwin }
1319240031aSJohn Baldwin if (!wmemstream_grow(ms, ms->offset + wlen))
1329240031aSJohn Baldwin return (-1);
1339240031aSJohn Baldwin
1349240031aSJohn Baldwin /*
1359240031aSJohn Baldwin * This copies characters one at a time rather than using
1369240031aSJohn Baldwin * mbsnrtowcs() so it can properly handle embedded null
1379240031aSJohn Baldwin * characters.
1389240031aSJohn Baldwin */
1399240031aSJohn Baldwin consumed = 0;
1409240031aSJohn Baldwin while (len > 0 && ms->offset < ms->len) {
1419240031aSJohn Baldwin charlen = mbrtowc(*ms->bufp + ms->offset, buf, len,
1429240031aSJohn Baldwin &ms->mbstate);
1439240031aSJohn Baldwin if (charlen == (size_t)-1) {
1449240031aSJohn Baldwin if (consumed == 0) {
1459240031aSJohn Baldwin errno = EILSEQ;
1469240031aSJohn Baldwin return (-1);
1479240031aSJohn Baldwin }
1489240031aSJohn Baldwin /* Treat it as a successful short write. */
1499240031aSJohn Baldwin break;
1509240031aSJohn Baldwin }
1519240031aSJohn Baldwin if (charlen == 0)
1529240031aSJohn Baldwin /* XXX: Not sure how else to handle this. */
1539240031aSJohn Baldwin charlen = 1;
1549240031aSJohn Baldwin if (charlen == (size_t)-2) {
1559240031aSJohn Baldwin consumed += len;
1569240031aSJohn Baldwin len = 0;
1579240031aSJohn Baldwin } else {
1589240031aSJohn Baldwin consumed += charlen;
1599240031aSJohn Baldwin buf += charlen;
1609240031aSJohn Baldwin len -= charlen;
1619240031aSJohn Baldwin ms->offset++;
1629240031aSJohn Baldwin }
1639240031aSJohn Baldwin }
1649240031aSJohn Baldwin wmemstream_update(ms);
1659240031aSJohn Baldwin #ifdef DEBUG
1669240031aSJohn Baldwin fprintf(stderr, "WMS: write(%p, %d) = %zd\n", ms, len, consumed);
1679240031aSJohn Baldwin #endif
1689240031aSJohn Baldwin return (consumed);
1699240031aSJohn Baldwin }
1709240031aSJohn Baldwin
1719240031aSJohn Baldwin static fpos_t
wmemstream_seek(void * cookie,fpos_t pos,int whence)1729240031aSJohn Baldwin wmemstream_seek(void *cookie, fpos_t pos, int whence)
1739240031aSJohn Baldwin {
1749240031aSJohn Baldwin struct wmemstream *ms;
1759240031aSJohn Baldwin fpos_t old;
1769240031aSJohn Baldwin
1779240031aSJohn Baldwin ms = cookie;
1789240031aSJohn Baldwin old = ms->offset;
1799240031aSJohn Baldwin switch (whence) {
1809240031aSJohn Baldwin case SEEK_SET:
1819240031aSJohn Baldwin /* _fseeko() checks for negative offsets. */
1829240031aSJohn Baldwin assert(pos >= 0);
1839240031aSJohn Baldwin ms->offset = pos;
1849240031aSJohn Baldwin break;
1859240031aSJohn Baldwin case SEEK_CUR:
1869240031aSJohn Baldwin /* This is only called by _ftello(). */
1879240031aSJohn Baldwin assert(pos == 0);
1889240031aSJohn Baldwin break;
1899240031aSJohn Baldwin case SEEK_END:
1909240031aSJohn Baldwin if (pos < 0) {
1919240031aSJohn Baldwin if (pos + ms->len < 0) {
1929240031aSJohn Baldwin #ifdef DEBUG
1939240031aSJohn Baldwin fprintf(stderr,
1949240031aSJohn Baldwin "WMS: bad SEEK_END: pos %jd, len %zd\n",
1959240031aSJohn Baldwin (intmax_t)pos, ms->len);
1969240031aSJohn Baldwin #endif
1979240031aSJohn Baldwin errno = EINVAL;
1989240031aSJohn Baldwin return (-1);
1999240031aSJohn Baldwin }
2009240031aSJohn Baldwin } else {
2019240031aSJohn Baldwin if (FPOS_MAX - ms->len < pos) {
2029240031aSJohn Baldwin #ifdef DEBUG
2039240031aSJohn Baldwin fprintf(stderr,
2049240031aSJohn Baldwin "WMS: bad SEEK_END: pos %jd, len %zd\n",
2059240031aSJohn Baldwin (intmax_t)pos, ms->len);
2069240031aSJohn Baldwin #endif
2079240031aSJohn Baldwin errno = EOVERFLOW;
2089240031aSJohn Baldwin return (-1);
2099240031aSJohn Baldwin }
2109240031aSJohn Baldwin }
2119240031aSJohn Baldwin ms->offset = ms->len + pos;
2129240031aSJohn Baldwin break;
2139240031aSJohn Baldwin }
2149240031aSJohn Baldwin /* Reset the multibyte state if a seek changes the position. */
2159240031aSJohn Baldwin if (ms->offset != old)
2169240031aSJohn Baldwin memset(&ms->mbstate, 0, sizeof(ms->mbstate));
2179240031aSJohn Baldwin wmemstream_update(ms);
2189240031aSJohn Baldwin #ifdef DEBUG
2199240031aSJohn Baldwin fprintf(stderr, "WMS: seek(%p, %jd, %d) %jd -> %jd\n", ms,
2209240031aSJohn Baldwin (intmax_t)pos, whence, (intmax_t)old, (intmax_t)ms->offset);
2219240031aSJohn Baldwin #endif
2229240031aSJohn Baldwin return (ms->offset);
2239240031aSJohn Baldwin }
2249240031aSJohn Baldwin
2259240031aSJohn Baldwin static int
wmemstream_close(void * cookie)2269240031aSJohn Baldwin wmemstream_close(void *cookie)
2279240031aSJohn Baldwin {
2289240031aSJohn Baldwin
2299240031aSJohn Baldwin free(cookie);
2309240031aSJohn Baldwin return (0);
2319240031aSJohn Baldwin }
2329240031aSJohn Baldwin
2339240031aSJohn Baldwin FILE *
open_wmemstream(wchar_t ** bufp,size_t * sizep)2349240031aSJohn Baldwin open_wmemstream(wchar_t **bufp, size_t *sizep)
2359240031aSJohn Baldwin {
2369240031aSJohn Baldwin struct wmemstream *ms;
2379240031aSJohn Baldwin int save_errno;
2389240031aSJohn Baldwin FILE *fp;
2399240031aSJohn Baldwin
2409240031aSJohn Baldwin if (bufp == NULL || sizep == NULL) {
2419240031aSJohn Baldwin errno = EINVAL;
2429240031aSJohn Baldwin return (NULL);
2439240031aSJohn Baldwin }
2449240031aSJohn Baldwin *bufp = calloc(1, sizeof(wchar_t));
2459240031aSJohn Baldwin if (*bufp == NULL)
2469240031aSJohn Baldwin return (NULL);
2479240031aSJohn Baldwin ms = malloc(sizeof(*ms));
2489240031aSJohn Baldwin if (ms == NULL) {
2499240031aSJohn Baldwin save_errno = errno;
2509240031aSJohn Baldwin free(*bufp);
2519240031aSJohn Baldwin *bufp = NULL;
2529240031aSJohn Baldwin errno = save_errno;
2539240031aSJohn Baldwin return (NULL);
2549240031aSJohn Baldwin }
2559240031aSJohn Baldwin ms->bufp = bufp;
2569240031aSJohn Baldwin ms->sizep = sizep;
2579240031aSJohn Baldwin ms->len = 0;
2589240031aSJohn Baldwin ms->offset = 0;
2599240031aSJohn Baldwin memset(&ms->mbstate, 0, sizeof(mbstate_t));
2609240031aSJohn Baldwin wmemstream_update(ms);
2619240031aSJohn Baldwin fp = funopen(ms, NULL, wmemstream_write, wmemstream_seek,
2629240031aSJohn Baldwin wmemstream_close);
2639240031aSJohn Baldwin if (fp == NULL) {
2649240031aSJohn Baldwin save_errno = errno;
2659240031aSJohn Baldwin free(ms);
2669240031aSJohn Baldwin free(*bufp);
2679240031aSJohn Baldwin *bufp = NULL;
2689240031aSJohn Baldwin errno = save_errno;
2699240031aSJohn Baldwin return (NULL);
2709240031aSJohn Baldwin }
2719240031aSJohn Baldwin fwide(fp, 1);
2729240031aSJohn Baldwin return (fp);
2739240031aSJohn Baldwin }
274