summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJerry Jelinek <jerry.jelinek@joyent.com>2020-03-27 11:29:00 +0000
committerJerry Jelinek <jerry.jelinek@joyent.com>2020-03-27 11:29:00 +0000
commit6dcfbbc68f881fbf5c20d25817a0221dfb135170 (patch)
tree40307666f6d7711499061d8c4be75029a6d851e9
parentb0624b90ec9a0c04cca626925beee3ae403457ce (diff)
parentcd62a92d4a964bfe61d35ba2301b69e65e22a509 (diff)
downloadillumos-joyent-6dcfbbc68f881fbf5c20d25817a0221dfb135170.tar.gz
[illumos-gate merge]
commit cd62a92d4a964bfe61d35ba2301b69e65e22a509 7092 Want support for stdio memory streams 12360 fwrite can loop forever on zero byte write 12392 ftello64 doesn't handle ungetc() correctly when unbuffered commit 1470234269f4edea4cbf270cb2475e4988b788d5 12359 Want a means to set the umem mtbf at runtine commit 0ac311bae7f6f50d9ba506b52bd8860f2d68d4ce 12358 Need mbrtowc variant that indicates consumed zero bytes commit d726994754c938f91b6fd7e96b5cab3829615c58 12357 getc/putc_unlocked need to set orientation
-rw-r--r--exception_lists/check_rtime6
-rw-r--r--exception_lists/cstyle3
-rw-r--r--manifest4
-rw-r--r--usr/src/cmd/mandoc/lib.in1
-rw-r--r--usr/src/head/stdio.h20
-rw-r--r--usr/src/head/wchar.h2
-rw-r--r--usr/src/lib/libc/amd64/Makefile4
-rw-r--r--usr/src/lib/libc/i386/Makefile.com4
-rw-r--r--usr/src/lib/libc/inc/file64.h27
-rw-r--r--usr/src/lib/libc/inc/libc.h8
-rw-r--r--usr/src/lib/libc/inc/mtlib.h6
-rw-r--r--usr/src/lib/libc/inc/stdiom.h62
-rw-r--r--usr/src/lib/libc/port/locale/big5.c10
-rw-r--r--usr/src/lib/libc/port/locale/euc.c42
-rw-r--r--usr/src/lib/libc/port/locale/fgetwc.c2
-rw-r--r--usr/src/lib/libc/port/locale/gb18030.c10
-rw-r--r--usr/src/lib/libc/port/locale/gb2312.c10
-rw-r--r--usr/src/lib/libc/port/locale/gbk.c10
-rw-r--r--usr/src/lib/libc/port/locale/lctype.h6
-rw-r--r--usr/src/lib/libc/port/locale/mblocal.h2
-rw-r--r--usr/src/lib/libc/port/locale/mbrtowc.c20
-rw-r--r--usr/src/lib/libc/port/locale/mbsnrtowcs.c8
-rw-r--r--usr/src/lib/libc/port/locale/mskanji.c10
-rw-r--r--usr/src/lib/libc/port/locale/none.c8
-rw-r--r--usr/src/lib/libc/port/locale/utf8.c27
-rw-r--r--usr/src/lib/libc/port/mapfile-vers7
-rw-r--r--usr/src/lib/libc/port/stdio/README.design327
-rw-r--r--usr/src/lib/libc/port/stdio/_endopen.c58
-rw-r--r--usr/src/lib/libc/port/stdio/_filbuf.c26
-rw-r--r--usr/src/lib/libc/port/stdio/_findbuf.c55
-rw-r--r--usr/src/lib/libc/port/stdio/_flsbuf.c19
-rw-r--r--usr/src/lib/libc/port/stdio/_stdio_flags.c109
-rw-r--r--usr/src/lib/libc/port/stdio/doscan.c12
-rw-r--r--usr/src/lib/libc/port/stdio/fileno.c6
-rw-r--r--usr/src/lib/libc/port/stdio/flush.c199
-rw-r--r--usr/src/lib/libc/port/stdio/fmemopen.c270
-rw-r--r--usr/src/lib/libc/port/stdio/fopen.c19
-rw-r--r--usr/src/lib/libc/port/stdio/fputs.c13
-rw-r--r--usr/src/lib/libc/port/stdio/fseek.c6
-rw-r--r--usr/src/lib/libc/port/stdio/fseeko.c6
-rw-r--r--usr/src/lib/libc/port/stdio/ftell.c44
-rw-r--r--usr/src/lib/libc/port/stdio/ftello.c29
-rw-r--r--usr/src/lib/libc/port/stdio/fwrite.c14
-rw-r--r--usr/src/lib/libc/port/stdio/getc.c16
-rw-r--r--usr/src/lib/libc/port/stdio/getw.c6
-rw-r--r--usr/src/lib/libc/port/stdio/open_memstream.c263
-rw-r--r--usr/src/lib/libc/port/stdio/open_wmemstream.c225
-rw-r--r--usr/src/lib/libc/port/stdio/putc.c24
-rw-r--r--usr/src/lib/libc/port/stdio/putw.c6
-rw-r--r--usr/src/lib/libc/port/stdio/rewind.c6
-rw-r--r--usr/src/lib/libc/port/stdio/setbuf.c28
-rw-r--r--usr/src/lib/libc/port/stdio/setvbuf.c16
-rw-r--r--usr/src/lib/libc/sparc/Makefile.com6
-rw-r--r--usr/src/lib/libc/sparcv9/Makefile.com6
-rw-r--r--usr/src/lib/libumem/common/mapfile-vers4
-rw-r--r--usr/src/lib/libumem/common/umem.c18
-rw-r--r--usr/src/lib/libumem/common/umem.h4
-rw-r--r--usr/src/man/man3c/Makefile5
-rw-r--r--usr/src/man/man3c/fclose.3c10
-rw-r--r--usr/src/man/man3c/ferror.3c177
-rw-r--r--usr/src/man/man3c/fflush.3c15
-rw-r--r--usr/src/man/man3c/fmemopen.3c222
-rw-r--r--usr/src/man/man3c/fputc.3c14
-rw-r--r--usr/src/man/man3c/fputwc.3c19
-rw-r--r--usr/src/man/man3c/freopen.3c16
-rw-r--r--usr/src/man/man3c/fseek.3c34
-rw-r--r--usr/src/man/man3c/ftell.3c36
-rw-r--r--usr/src/man/man3c/fwrite.3c24
-rw-r--r--usr/src/man/man3c/open_memstream.3c195
-rw-r--r--usr/src/man/man3c/stdio.3c26
-rw-r--r--usr/src/man/man3lib/libumem.3lib17
-rw-r--r--usr/src/man/man3malloc/Makefile5
-rw-r--r--usr/src/man/man3malloc/umem_debug.3malloc16
-rw-r--r--usr/src/man/man3malloc/umem_setmtbf.3malloc74
-rw-r--r--usr/src/pkg/manifests/system-library.man3c.inc3
-rw-r--r--usr/src/pkg/manifests/system-library.man3malloc.inc1
-rw-r--r--usr/src/pkg/manifests/system-test-libctest.mf17
-rw-r--r--usr/src/test/libc-tests/cfg/symbols/stdio_h.cfg16
-rw-r--r--usr/src/test/libc-tests/cfg/symbols/wchar_h.cfg5
-rw-r--r--usr/src/test/libc-tests/runfiles/default.run17
-rw-r--r--usr/src/test/libc-tests/tests/Makefile1
-rw-r--r--usr/src/test/libc-tests/tests/stdio/Makefile89
-rw-r--r--usr/src/test/libc-tests/tests/stdio/fileno.c210
-rw-r--r--usr/src/test/libc-tests/tests/stdio/fmemopentest.c222
-rw-r--r--usr/src/test/libc-tests/tests/stdio/ftell_ungetc.c86
-rw-r--r--usr/src/test/libc-tests/tests/stdio/memstream.c2108
-rw-r--r--usr/src/test/libc-tests/tests/stdio/memstream_reopen.c88
-rw-r--r--usr/src/test/libc-tests/tests/stdio/open_memstreamtest.c196
-rw-r--r--usr/src/test/libc-tests/tests/stdio/orientation_test.c258
-rw-r--r--usr/src/test/libc-tests/tests/stdio/test_mbrtowc.c84
90 files changed, 5916 insertions, 549 deletions
diff --git a/exception_lists/check_rtime b/exception_lists/check_rtime
index 611b885214..e42084ffc7 100644
--- a/exception_lists/check_rtime
+++ b/exception_lists/check_rtime
@@ -63,6 +63,12 @@ SKIP ^usr/platform/.*/libpsvcpolicy_psr\.so\.1
SKIP ^usr/platform/.*/libpsvcpolicy\.so\.1
SKIP ^usr/lib/sysevent/modules/picl_slm.so$
+#
+# The stdio tests all link against libumem to interpose on libc's
+# malloc; however, they do not actually reference the object.
+#
+SKIP ^opt/libc-tests/tests/stdio/
+
# Objects that are allowed to have executable data segments
EXEC_DATA ^MACH(lib)/ld\.so\.1$
EXEC_DATA ^lib/libc\.so\.1$ # 6524709, 32-bit, needed for x86 only
diff --git a/exception_lists/cstyle b/exception_lists/cstyle
index 9e0e25ffe3..af367a074f 100644
--- a/exception_lists/cstyle
+++ b/exception_lists/cstyle
@@ -698,6 +698,9 @@ usr/src/test/os-tests/tests/sockfs/conn.c
usr/src/test/os-tests/tests/sockfs/dgram.c
usr/src/test/os-tests/tests/sockfs/drop_priv.c
usr/src/test/os-tests/tests/sockfs/sockpair.c
+usr/src/test/libc-tests/tests/stdio/fmemopentest.c
+usr/src/test/libc-tests/tests/stdio/open_memstreamtest.c
+usr/src/test/libc-tests/tests/stdio/orientation_test.c
usr/src/tools/btxld/btx.h
usr/src/tools/btxld/btxld.c
usr/src/tools/btxld/elfh.c
diff --git a/manifest b/manifest
index aea01d4b47..a3c4e870e2 100644
--- a/manifest
+++ b/manifest
@@ -13379,6 +13379,7 @@ f usr/share/man/man3c/flockfile.3c 0444 root bin
s usr/share/man/man3c/fls.3c=ffs.3c
s usr/share/man/man3c/flsl.3c=ffs.3c
s usr/share/man/man3c/flsll.3c=ffs.3c
+f path=usr/share/man/man3c/fmemopen.3c 0444 root bin
f usr/share/man/man3c/fmtmsg.3c 0444 root bin
f usr/share/man/man3c/fnmatch.3c 0444 root bin
f usr/share/man/man3c/fopen.3c 0444 root bin
@@ -13750,6 +13751,8 @@ s usr/share/man/man3c/ntohs.3c=byteorder.3c
f usr/share/man/man3c/offsetof.3c 0444 root bin
f usr/share/man/man3c/opendir.3c 0444 root bin
s usr/share/man/man3c/openlog.3c=syslog.3c
+f usr/share/man/man3c/open_memstream.3c 0444 root bin
+s usr/share/man/man3c/open_wmemstream.3c=open_memstream.3c
s usr/share/man/man3c/pclose.3c=popen.3c
f usr/share/man/man3c/perror.3c 0444 root bin
f usr/share/man/man3c/pfmt.3c 0444 root bin
@@ -16767,6 +16770,7 @@ s usr/share/man/man3malloc/umem_cache_free.3malloc=umem_cache_create.3malloc
f usr/share/man/man3malloc/umem_debug.3malloc 0444 root bin
s usr/share/man/man3malloc/umem_free.3malloc=umem_alloc.3malloc
s usr/share/man/man3malloc/umem_nofail_callback.3malloc=umem_alloc.3malloc
+f usr/share/man/man3malloc/umem_setmtbf.3malloc 0444 root bin
s usr/share/man/man3malloc/umem_zalloc.3malloc=umem_alloc.3malloc
s usr/share/man/man3malloc/valloc.3malloc=malloc.3malloc
f usr/share/man/man3malloc/watchmalloc.3malloc 0444 root bin
diff --git a/usr/src/cmd/mandoc/lib.in b/usr/src/cmd/mandoc/lib.in
index 79f249fb5f..0d65eeb086 100644
--- a/usr/src/cmd/mandoc/lib.in
+++ b/usr/src/cmd/mandoc/lib.in
@@ -76,6 +76,7 @@ LINE("libsysevet", "System Event Inteface Library (libsysevent, \\-lsysevent)")
LINE("libtecla", "Interactive Command Line Input Library (libtecla, \\-ltecla)")
LINE("libtnfctl", "TNF Probe Control Library (libtnfctl, \\-ltnfctl)")
LINE("libtsol", "Trusted Extensions Library (libtsol, \\-ltsol)")
+LINE("libumem", "Object-Caching Memory Allocation Library (libumem, \\-lumem")
LINE("libuuid", "UUID Library (libuuid, \\-luuid)")
LINE("libvolmgt", "Volume Management Library (libvolmgt, \\-lvolmgt)")
LINE("libxcurses", "X/Open Curses Library (libxcurses, \\-lxcurses)")
diff --git a/usr/src/head/stdio.h b/usr/src/head/stdio.h
index 9575bf2401..dc480dd271 100644
--- a/usr/src/head/stdio.h
+++ b/usr/src/head/stdio.h
@@ -362,25 +362,21 @@ extern int fseeko64(FILE *, off64_t, int);
extern off64_t ftello64(FILE *);
#endif
-#if !defined(__lint)
+/*
+ * XPG7 symbols
+ */
+#if !defined(_STRICT_SYMBOLS) || defined(_XPG7)
+extern FILE *fmemopen(void *_RESTRICT_KYWD, size_t,
+ const char *_RESTRICT_KYWD);
+extern FILE *open_memstream(char **, size_t *);
+#endif /* !_STRICT_SYMBOLS || _XPG7 */
#if defined(__EXTENSIONS__) || defined(_REENTRANT) || \
(_POSIX_C_SOURCE - 0 >= 199506L)
-#ifndef _LP64
-#define getc_unlocked(p) (--(p)->_cnt < 0 \
- ? __filbuf(p) \
- : (int)*(p)->_ptr++)
-#define putc_unlocked(x, p) (--(p)->_cnt < 0 \
- ? __flsbuf((x), (p)) \
- : (int)(*(p)->_ptr++ = \
- (unsigned char) (x)))
-#endif /* _LP64 */
#define getchar_unlocked() getc_unlocked(stdin)
#define putchar_unlocked(x) putc_unlocked((x), stdout)
#endif /* defined(__EXTENSIONS__) || defined(_REENTRANT).. */
-#endif /* !defined(__lint) */
-
#ifdef __cplusplus
}
#endif
diff --git a/usr/src/head/wchar.h b/usr/src/head/wchar.h
index 1bf97f9a29..29e3ab6f81 100644
--- a/usr/src/head/wchar.h
+++ b/usr/src/head/wchar.h
@@ -188,6 +188,8 @@ extern int wcsncasecmp_l(const wchar_t *, const wchar_t *, size_t, locale_t);
extern size_t mbsnrtowcs(wchar_t *_RESTRICT_KYWD, const char **_RESTRICT_KYWD,
size_t, size_t, mbstate_t *_RESTRICT_KYWD);
+extern FILE *open_wmemstream(wchar_t **, size_t *);
+
#endif /* defined(_XPG7) || !defined(_STRICT_SYMBOLS) */
#ifdef __cplusplus
diff --git a/usr/src/lib/libc/amd64/Makefile b/usr/src/lib/libc/amd64/Makefile
index c0a065c9df..c21ef76ee2 100644
--- a/usr/src/lib/libc/amd64/Makefile
+++ b/usr/src/lib/libc/amd64/Makefile
@@ -639,6 +639,7 @@ PORTSTDIO= \
_filbuf.o \
_findbuf.o \
_flsbuf.o \
+ _stdio_flags.o \
_wrtchk.o \
clearerr.o \
ctermid.o \
@@ -654,6 +655,7 @@ PORTSTDIO= \
fileno.o \
flockf.o \
flush.o \
+ fmemopen.o \
fopen.o \
fpos.o \
fputc.o \
@@ -671,6 +673,8 @@ PORTSTDIO= \
gets.o \
getw.o \
mse.o \
+ open_memstream.o \
+ open_wmemstream.o \
popen.o \
putc.o \
putchar.o \
diff --git a/usr/src/lib/libc/i386/Makefile.com b/usr/src/lib/libc/i386/Makefile.com
index 9987e2ee87..d9e56ee8ec 100644
--- a/usr/src/lib/libc/i386/Makefile.com
+++ b/usr/src/lib/libc/i386/Makefile.com
@@ -686,6 +686,7 @@ PORTSTDIO= \
_filbuf.o \
_findbuf.o \
_flsbuf.o \
+ _stdio_flags.o \
_wrtchk.o \
clearerr.o \
ctermid.o \
@@ -701,6 +702,7 @@ PORTSTDIO= \
fileno.o \
flockf.o \
flush.o \
+ fmemopen.o \
fopen.o \
fpos.o \
fputc.o \
@@ -718,6 +720,8 @@ PORTSTDIO= \
gets.o \
getw.o \
mse.o \
+ open_memstream.o \
+ open_wmemstream.o \
popen.o \
putc.o \
putchar.o \
diff --git a/usr/src/lib/libc/inc/file64.h b/usr/src/lib/libc/inc/file64.h
index 40504d35ec..1bbc98e2bc 100644
--- a/usr/src/lib/libc/inc/file64.h
+++ b/usr/src/lib/libc/inc/file64.h
@@ -25,6 +25,10 @@
*/
/*
+ * Copyright 2020 Robert Mustacchi
+ */
+
+/*
* This is the header where the internal to libc definition of the FILE
* structure is defined. The exrernal defintion defines the FILE structure
* as an array of longs. This prevents customers from writing code that
@@ -41,8 +45,6 @@
#ifndef _FILE64_H
#define _FILE64_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <synch.h>
#include <stdio_tag.h>
#include <wchar_impl.h>
@@ -58,8 +60,25 @@ typedef __mbstate_t mbstate_t;
#define rmutex_t mutex_t
+typedef ssize_t (*fread_t)(__FILE *, char *, size_t);
+typedef ssize_t (*fwrite_t)(__FILE *, const char *, size_t);
+typedef off_t (*fseek_t)(__FILE *, off_t, int);
+typedef int (*fclose_t)(__FILE *);
+
+typedef struct {
+ fread_t std_read;
+ fwrite_t std_write;
+ fseek_t std_seek;
+ fclose_t std_close;
+ void *std_data;
+} stdio_ops_t;
+
#ifdef _LP64
+/*
+ * This structure cannot grow beyond its current size of 128 bytes. See the file
+ * lib/libc/port/stdio/README.design for more information.
+ */
struct __FILE_TAG {
unsigned char *_ptr; /* next character from/to here in buffer */
unsigned char *_base; /* the buffer */
@@ -69,7 +88,8 @@ struct __FILE_TAG {
unsigned int _flag; /* the state of the stream */
rmutex_t _lock; /* lock for this structure */
mbstate_t _state; /* mbstate_t */
- char __fill[32]; /* filler to bring size to 128 bytes */
+ stdio_ops_t *_ops; /* Alternate impl ops */
+ char __fill[24]; /* filler to bring size to 128 bytes */
};
#else
@@ -83,6 +103,7 @@ struct xFILEdata {
rmutex_t _lock; /* lock for this structure */
mbstate_t _state; /* mbstate_t */
int _altfd; /* alternate fd if > 255 */
+ stdio_ops_t *_ops; /* Alternate impl ops */
};
#define XFILEINITIALIZER { 0, NULL, RECURSIVEMUTEX, DEFAULTMBSTATE }
diff --git a/usr/src/lib/libc/inc/libc.h b/usr/src/lib/libc/inc/libc.h
index 448523df01..90a2859b33 100644
--- a/usr/src/lib/libc/inc/libc.h
+++ b/usr/src/lib/libc/inc/libc.h
@@ -333,6 +333,14 @@ extern void __throw_constraint_handler_s(const char *_RESTRICT_KYWD, int);
*/
extern void common_panic(const char *, const char *);
+/*
+ * defined in mbrtowc.c.
+ */
+extern size_t mbrtowc_nz_l(wchar_t *_RESTRICT_KYWD, const char *_RESTRICT_KYWD,
+ size_t, mbstate_t *_RESTRICT_KYWD, locale_t);
+extern size_t mbrtowc_nz(wchar_t *_RESTRICT_KYWD, const char *_RESTRICT_KYWD,
+ size_t, mbstate_t *_RESTRICT_KYWD);
+
#ifdef __cplusplus
}
#endif
diff --git a/usr/src/lib/libc/inc/mtlib.h b/usr/src/lib/libc/inc/mtlib.h
index aa5e9e0992..cad6c7ab8d 100644
--- a/usr/src/lib/libc/inc/mtlib.h
+++ b/usr/src/lib/libc/inc/mtlib.h
@@ -27,8 +27,6 @@
#ifndef _MTLIB_H
#define _MTLIB_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <thread.h>
#ifdef __cplusplus
@@ -66,9 +64,9 @@ extern void assert_no_libc_locks_held(void);
#define _FWRITE _fwrite_unlocked
#define FILENO(s) _fileno(s)
#define FERROR(s) ferror(s)
-#define GETC(s) _getc_unlocked(s)
+#define GETC(s) _getc_internal(s)
#define UNGETC(c, s) _ungetc_unlocked(c, s)
-#define PUTC(c, s) _putc_unlocked(c, s)
+#define PUTC(c, s) _putc_internal(c, s)
#define GETWC(s) getwc(s)
#define PUTWC(c, s) putwc(c, s)
diff --git a/usr/src/lib/libc/inc/stdiom.h b/usr/src/lib/libc/inc/stdiom.h
index 9befb8a3c6..4769231968 100644
--- a/usr/src/lib/libc/inc/stdiom.h
+++ b/usr/src/lib/libc/inc/stdiom.h
@@ -25,7 +25,11 @@
*/
/* Copyright (c) 1988 AT&T */
-/* All Rights Reserved */
+/* All Rights Reserved */
+
+/*
+ * Copyright 2020 Robert Mustacchi
+ */
/*
* stdiom.h - shared guts of stdio
@@ -34,8 +38,6 @@
#ifndef _STDIOM_H
#define _STDIOM_H
-#pragma ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.9 */
-
#include <thread.h>
#include <synch.h>
#include <mtlib.h>
@@ -48,11 +50,18 @@
/*
* The following flags, and the macros that manipulate them, operate upon
* the FILE structure used by stdio. If new flags are required, they should
- * be created in this file. The values of the flags must be differnt from
+ * be created in this file. The values of the flags must be different from
* the currently used values. New macros should be created to use the flags
* so that the compilation mode dependencies can be isolated here.
*/
+/*
+ * The 32-bit version of the stdio FILE has 8 bits for its flags (see
+ * lib/libc/port/stdio/README.design). These 8 bits are used to determine if the
+ * FILE structure is allocated. We define '_DEF_FLAG_MASK' as a means to
+ * indicate this.
+ */
+#define _DEF_FLAG_MASK 0377
#ifdef _LP64
#define _BYTE_MODE_FLAG 0400
#define _WC_MODE_FLAG 01000
@@ -152,7 +161,7 @@ extern int __flsbuf(int, FILE *);
*/
#define _realbufend(iop) ((iop)->_end)
#else
-extern Uchar *_realbufend(FILE *iop);
+extern Uchar *_realbufend(FILE *iop);
extern rmutex_t *_reallock(FILE *iop);
#endif /* _LP64 */
@@ -171,7 +180,7 @@ extern void close_pid(void);
/*
* Internal routines from flush.c
*/
-extern int _file_get(FILE *);
+extern int _get_fd(FILE *);
extern int _file_set(FILE *, int, const char *);
/*
@@ -180,11 +189,8 @@ extern int _file_set(FILE *, int, const char *);
* since 64-bit Solaris is not affected by this.
*/
#ifdef _LP64
-#define GET_FD(iop) ((iop)->_file)
#define SET_FILE(iop, fd) ((iop)->_file = (fd))
#else
-#define GET_FD(iop) \
- (((iop)->__extendedfd) ? _file_get(iop) : (iop)->_magic)
#define SET_FILE(iop, fd) (iop)->_magic = (fd); (iop)->__extendedfd = 0
#endif
@@ -206,7 +212,7 @@ extern int _fileno(FILE *iop);
/*
* Internal routines from _findbuf.c
*/
-extern Uchar *_findbuf(FILE *iop);
+extern Uchar *_findbuf(FILE *iop);
/*
* Internal routine used by fopen.c
@@ -221,12 +227,12 @@ extern size_t _fwrite_unlocked(const void *, size_t, size_t, FILE *);
/*
* Internal routine from getc.c
*/
-int _getc_unlocked(FILE *);
+int _getc_internal(FILE *);
/*
* Internal routine from put.c
*/
-int _putc_unlocked(int, FILE *);
+int _putc_internal(int, FILE *);
/*
* Internal routine from ungetc.c
@@ -260,4 +266,36 @@ extern struct xFILEdata _xftab[];
#endif /* _LP64 */
+/*
+ * A set of stdio routines to allow us to have alternate read, write, lseek, and
+ * close implementations.
+ */
+extern ssize_t _xread(FILE *iop, void *buf, size_t nbytes);
+extern ssize_t _xwrite(FILE *iop, const void *buf, size_t nbytes);
+extern off_t _xseek(FILE *iop, off_t off, int whence);
+extern off64_t _xseek64(FILE *iop, off64_t off, int whence);
+extern int _xclose(FILE *iop);
+extern void *_xdata(FILE *iop);
+extern int _xassoc(FILE *iop, fread_t readf, fwrite_t writef,
+ fseek_t seekf, fclose_t closef, void *data);
+extern void _xunassoc(FILE *iop);
+
+/*
+ * Internal functions from _stdio_flags.c.
+ */
+extern int _stdio_flags(const char *type, int *oflags, int *fflags);
+
+/*
+ * Internal functions from open_memstream.c.
+ */
+extern boolean_t memstream_seek(size_t base, off_t off, size_t max,
+ size_t *nposp);
+extern int memstream_newsize(size_t pos, size_t alloc, size_t nbytes,
+ size_t *nallocp);
+
+/*
+ * Internal function from ftell.o.
+ */
+extern off64_t ftell_common(FILE *iop);
+
#endif /* _STDIOM_H */
diff --git a/usr/src/lib/libc/port/locale/big5.c b/usr/src/lib/libc/port/locale/big5.c
index 2729ab0289..889b182de5 100644
--- a/usr/src/lib/libc/port/locale/big5.c
+++ b/usr/src/lib/libc/port/locale/big5.c
@@ -44,7 +44,7 @@
static size_t _BIG5_mbrtowc(wchar_t *_RESTRICT_KYWD,
const char *_RESTRICT_KYWD,
- size_t, mbstate_t *RESTRICT_KYWD);
+ size_t, mbstate_t *RESTRICT_KYWD, boolean_t);
static int _BIG5_mbsinit(const mbstate_t *);
static size_t _BIG5_wcrtomb(char *_RESTRICT_KYWD, wchar_t,
mbstate_t *_RESTRICT_KYWD);
@@ -88,7 +88,7 @@ _big5_check(uint_t c)
static size_t
_BIG5_mbrtowc(wchar_t *_RESTRICT_KYWD pwc, const char *_RESTRICT_KYWD s,
- size_t n, mbstate_t *_RESTRICT_KYWD ps)
+ size_t n, mbstate_t *_RESTRICT_KYWD ps, boolean_t zero)
{
_BIG5State *bs;
wchar_t wc;
@@ -143,7 +143,11 @@ _BIG5_mbrtowc(wchar_t *_RESTRICT_KYWD pwc, const char *_RESTRICT_KYWD s,
} else {
if (pwc != NULL)
*pwc = wc;
- return (wc == L'\0' ? 0 : 1);
+ if (zero || wc != L'\0') {
+ return (1);
+ } else {
+ return (0);
+ }
}
}
diff --git a/usr/src/lib/libc/port/locale/euc.c b/usr/src/lib/libc/port/locale/euc.c
index a99cdf8689..1d1d25b17b 100644
--- a/usr/src/lib/libc/port/locale/euc.c
+++ b/usr/src/lib/libc/port/locale/euc.c
@@ -46,22 +46,23 @@
static size_t _EUC_mbrtowc_impl(wchar_t *_RESTRICT_KYWD,
const char *_RESTRICT_KYWD,
- size_t, mbstate_t *_RESTRICT_KYWD, uint8_t, uint8_t, uint8_t, uint8_t);
+ size_t, mbstate_t *_RESTRICT_KYWD, uint8_t, uint8_t, uint8_t, uint8_t,
+ boolean_t);
static size_t _EUC_wcrtomb_impl(char *_RESTRICT_KYWD, wchar_t,
mbstate_t *_RESTRICT_KYWD, uint8_t, uint8_t, uint8_t, uint8_t);
static size_t _EUC_CN_mbrtowc(wchar_t *_RESTRICT_KYWD,
const char *_RESTRICT_KYWD,
- size_t, mbstate_t *_RESTRICT_KYWD);
+ size_t, mbstate_t *_RESTRICT_KYWD, boolean_t);
static size_t _EUC_JP_mbrtowc(wchar_t *_RESTRICT_KYWD,
const char *_RESTRICT_KYWD,
- size_t, mbstate_t *_RESTRICT_KYWD);
+ size_t, mbstate_t *_RESTRICT_KYWD, boolean_t);
static size_t _EUC_KR_mbrtowc(wchar_t *_RESTRICT_KYWD,
const char *_RESTRICT_KYWD,
- size_t, mbstate_t *_RESTRICT_KYWD);
+ size_t, mbstate_t *_RESTRICT_KYWD, boolean_t);
static size_t _EUC_TW_mbrtowc(wchar_t *_RESTRICT_KYWD,
const char *_RESTRICT_KYWD,
- size_t, mbstate_t *_RESTRICT_KYWD);
+ size_t, mbstate_t *_RESTRICT_KYWD, boolean_t);
static size_t _EUC_CN_wcrtomb(char *_RESTRICT_KYWD, wchar_t,
mbstate_t *_RESTRICT_KYWD);
@@ -131,9 +132,9 @@ _EUC_CN_init(struct lc_ctype *lct)
static size_t
_EUC_CN_mbrtowc(wchar_t *_RESTRICT_KYWD pwc, const char *_RESTRICT_KYWD s,
- size_t n, mbstate_t *_RESTRICT_KYWD ps)
+ size_t n, mbstate_t *_RESTRICT_KYWD ps, boolean_t zero)
{
- return (_EUC_mbrtowc_impl(pwc, s, n, ps, SS2, 4, 0, 0));
+ return (_EUC_mbrtowc_impl(pwc, s, n, ps, SS2, 4, 0, 0, zero));
}
static size_t
@@ -176,9 +177,9 @@ _EUC_KR_init(struct lc_ctype *lct)
static size_t
_EUC_KR_mbrtowc(wchar_t *_RESTRICT_KYWD pwc, const char *_RESTRICT_KYWD s,
- size_t n, mbstate_t *_RESTRICT_KYWD ps)
+ size_t n, mbstate_t *_RESTRICT_KYWD ps, boolean_t zero)
{
- return (_EUC_mbrtowc_impl(pwc, s, n, ps, 0, 0, 0, 0));
+ return (_EUC_mbrtowc_impl(pwc, s, n, ps, 0, 0, 0, 0, zero));
}
static size_t
@@ -221,9 +222,9 @@ _EUC_JP_init(struct lc_ctype *lct)
static size_t
_EUC_JP_mbrtowc(wchar_t *_RESTRICT_KYWD pwc, const char *_RESTRICT_KYWD s,
- size_t n, mbstate_t *_RESTRICT_KYWD ps)
+ size_t n, mbstate_t *_RESTRICT_KYWD ps, boolean_t zero)
{
- return (_EUC_mbrtowc_impl(pwc, s, n, ps, SS2, 2, SS3, 3));
+ return (_EUC_mbrtowc_impl(pwc, s, n, ps, SS2, 2, SS3, 3, zero));
}
static size_t
@@ -266,9 +267,9 @@ _EUC_TW_init(struct lc_ctype *lct)
static size_t
_EUC_TW_mbrtowc(wchar_t *_RESTRICT_KYWD pwc, const char *_RESTRICT_KYWD s,
- size_t n, mbstate_t *_RESTRICT_KYWD ps)
+ size_t n, mbstate_t *_RESTRICT_KYWD ps, boolean_t zero)
{
- return (_EUC_mbrtowc_impl(pwc, s, n, ps, SS2, 4, 0, 0));
+ return (_EUC_mbrtowc_impl(pwc, s, n, ps, SS2, 4, 0, 0, zero));
}
static size_t
@@ -300,7 +301,8 @@ _EUC_TW_wcsnrtombs(char *_RESTRICT_KYWD dst, const wchar_t **_RESTRICT_KYWD src,
static size_t
_EUC_mbrtowc_impl(wchar_t *_RESTRICT_KYWD pwc, const char *_RESTRICT_KYWD s,
size_t n, mbstate_t *_RESTRICT_KYWD ps,
- uint8_t cs2, uint8_t cs2width, uint8_t cs3, uint8_t cs3width)
+ uint8_t cs2, uint8_t cs2width, uint8_t cs3, uint8_t cs3width,
+ boolean_t zero)
{
_EucState *es;
int i, want;
@@ -329,7 +331,11 @@ _EUC_mbrtowc_impl(wchar_t *_RESTRICT_KYWD pwc, const char *_RESTRICT_KYWD s,
if (((ch = (unsigned char)*s) & 0x80) == 0) {
if (pwc != NULL)
*pwc = ch;
- return (ch != '\0' ? 1 : 0);
+ if (zero || ch != '\0') {
+ return (1);
+ } else {
+ return (0);
+ }
}
if (ch >= 0xa1) {
@@ -367,7 +373,11 @@ _EUC_mbrtowc_impl(wchar_t *_RESTRICT_KYWD pwc, const char *_RESTRICT_KYWD s,
if (pwc != NULL)
*pwc = wc;
es->want = 0;
- return (wc == L'\0' ? 0 : want);
+ if (zero || wc != L'\0') {
+ return (want);
+ } else {
+ return (0);
+ }
}
static size_t
diff --git a/usr/src/lib/libc/port/locale/fgetwc.c b/usr/src/lib/libc/port/locale/fgetwc.c
index 282b65cebc..254a785891 100644
--- a/usr/src/lib/libc/port/locale/fgetwc.c
+++ b/usr/src/lib/libc/port/locale/fgetwc.c
@@ -66,7 +66,7 @@ _fgetwc_unlocked_l(FILE *fp, locale_t loc)
}
do {
char x = (char)c;
- nconv = lct->lc_mbrtowc(&wc, &x, 1, statep);
+ nconv = lct->lc_mbrtowc(&wc, &x, 1, statep, B_FALSE);
if (nconv == (size_t)-1) {
break;
} else if (nconv == (size_t)-2) {
diff --git a/usr/src/lib/libc/port/locale/gb18030.c b/usr/src/lib/libc/port/locale/gb18030.c
index 232daade50..36c48c5cc5 100644
--- a/usr/src/lib/libc/port/locale/gb18030.c
+++ b/usr/src/lib/libc/port/locale/gb18030.c
@@ -44,7 +44,7 @@
static size_t _GB18030_mbrtowc(wchar_t *_RESTRICT_KYWD,
const char *_RESTRICT_KYWD,
- size_t, mbstate_t *_RESTRICT_KYWD);
+ size_t, mbstate_t *_RESTRICT_KYWD, boolean_t);
static int _GB18030_mbsinit(const mbstate_t *);
static size_t _GB18030_wcrtomb(char *_RESTRICT_KYWD, wchar_t,
mbstate_t *_RESTRICT_KYWD);
@@ -83,7 +83,7 @@ _GB18030_mbsinit(const mbstate_t *ps)
static size_t
_GB18030_mbrtowc(wchar_t *_RESTRICT_KYWD pwc, const char *_RESTRICT_KYWD s,
- size_t n, mbstate_t *_RESTRICT_KYWD ps)
+ size_t n, mbstate_t *_RESTRICT_KYWD ps, boolean_t zero)
{
_GB18030State *gs;
wchar_t wch;
@@ -159,7 +159,11 @@ _GB18030_mbrtowc(wchar_t *_RESTRICT_KYWD pwc, const char *_RESTRICT_KYWD s,
if (pwc != NULL)
*pwc = wch;
gs->count = 0;
- return (wch == L'\0' ? 0 : len - ocount);
+ if (zero || wch != L'\0') {
+ return (len - ocount);
+ } else {
+ return (0);
+ }
ilseq:
errno = EILSEQ;
return ((size_t)-1);
diff --git a/usr/src/lib/libc/port/locale/gb2312.c b/usr/src/lib/libc/port/locale/gb2312.c
index a25af781b4..bfb6c0177b 100644
--- a/usr/src/lib/libc/port/locale/gb2312.c
+++ b/usr/src/lib/libc/port/locale/gb2312.c
@@ -39,7 +39,7 @@
static size_t _GB2312_mbrtowc(wchar_t *_RESTRICT_KYWD,
const char *_RESTRICT_KYWD,
- size_t, mbstate_t *_RESTRICT_KYWD);
+ size_t, mbstate_t *_RESTRICT_KYWD, boolean_t);
static int _GB2312_mbsinit(const mbstate_t *);
static size_t _GB2312_wcrtomb(char *_RESTRICT_KYWD, wchar_t,
mbstate_t *_RESTRICT_KYWD);
@@ -101,7 +101,7 @@ _GB2312_check(const char *str, size_t n)
static size_t
_GB2312_mbrtowc(wchar_t *_RESTRICT_KYWD pwc, const char *_RESTRICT_KYWD s,
- size_t n, mbstate_t *_RESTRICT_KYWD ps)
+ size_t n, mbstate_t *_RESTRICT_KYWD ps, boolean_t zero)
{
_GB2312State *gs;
wchar_t wc;
@@ -137,7 +137,11 @@ _GB2312_mbrtowc(wchar_t *_RESTRICT_KYWD pwc, const char *_RESTRICT_KYWD s,
if (pwc != NULL)
*pwc = wc;
gs->count = 0;
- return (wc == L'\0' ? 0 : len - ocount);
+ if (zero || wc != L'\0') {
+ return (len - ocount);
+ } else {
+ return (0);
+ }
}
static size_t
diff --git a/usr/src/lib/libc/port/locale/gbk.c b/usr/src/lib/libc/port/locale/gbk.c
index 5c94b9954b..f422ce8fb5 100644
--- a/usr/src/lib/libc/port/locale/gbk.c
+++ b/usr/src/lib/libc/port/locale/gbk.c
@@ -44,7 +44,7 @@
static size_t _GBK_mbrtowc(wchar_t *_RESTRICT_KYWD,
const char *_RESTRICT_KYWD,
- size_t, mbstate_t *_RESTRICT_KYWD);
+ size_t, mbstate_t *_RESTRICT_KYWD, boolean_t);
static int _GBK_mbsinit(const mbstate_t *);
static size_t _GBK_wcrtomb(char *_RESTRICT_KYWD, wchar_t,
mbstate_t *_RESTRICT_KYWD);
@@ -88,7 +88,7 @@ _gbk_check(uint_t c)
static size_t
_GBK_mbrtowc(wchar_t *_RESTRICT_KYWD pwc, const char *_RESTRICT_KYWD s,
- size_t n, mbstate_t *_RESTRICT_KYWD ps)
+ size_t n, mbstate_t *_RESTRICT_KYWD ps, boolean_t zero)
{
_GBKState *gs;
wchar_t wc;
@@ -143,7 +143,11 @@ _GBK_mbrtowc(wchar_t *_RESTRICT_KYWD pwc, const char *_RESTRICT_KYWD s,
} else {
if (pwc != NULL)
*pwc = wc;
- return (wc == L'\0' ? 0 : 1);
+ if (zero || wc != L'\0') {
+ return (1);
+ } else {
+ return (0);
+ }
}
}
diff --git a/usr/src/lib/libc/port/locale/lctype.h b/usr/src/lib/libc/port/locale/lctype.h
index 25652da1d4..0a9e1335bf 100644
--- a/usr/src/lib/libc/port/locale/lctype.h
+++ b/usr/src/lib/libc/port/locale/lctype.h
@@ -17,6 +17,7 @@
#define _LCTYPE_H_
#include <wchar.h>
+#include <sys/types.h>
/* private LC_CTYPE related structures */
@@ -24,7 +25,8 @@
struct lc_ctype {
size_t (*lc_mbrtowc)(wchar_t *_RESTRICT_KYWD,
- const char *_RESTRICT_KYWD, size_t, mbstate_t *_RESTRICT_KYWD);
+ const char *_RESTRICT_KYWD, size_t, mbstate_t *_RESTRICT_KYWD,
+ boolean_t);
int (*lc_mbsinit)(const mbstate_t *);
@@ -51,7 +53,7 @@ struct lc_ctype {
* Default implementation (C locale, i.e. ASCII).
*/
size_t __mbrtowc_ascii(wchar_t *_RESTRICT_KYWD,
- const char *_RESTRICT_KYWD, size_t, mbstate_t *_RESTRICT_KYWD);
+ const char *_RESTRICT_KYWD, size_t, mbstate_t *_RESTRICT_KYWD, boolean_t);
int __mbsinit_ascii(const mbstate_t *);
size_t __mbsnrtowcs_ascii(wchar_t *_RESTRICT_KYWD dst,
const char **_RESTRICT_KYWD src, size_t nms, size_t len,
diff --git a/usr/src/lib/libc/port/locale/mblocal.h b/usr/src/lib/libc/port/locale/mblocal.h
index 4412bec7cb..3d958e364e 100644
--- a/usr/src/lib/libc/port/locale/mblocal.h
+++ b/usr/src/lib/libc/port/locale/mblocal.h
@@ -48,7 +48,7 @@ void _BIG5_init(struct lc_ctype *);
void _MSKanji_init(struct lc_ctype *);
typedef size_t (*mbrtowc_pfn_t)(wchar_t *_RESTRICT_KYWD,
- const char *_RESTRICT_KYWD, size_t, mbstate_t *_RESTRICT_KYWD);
+ const char *_RESTRICT_KYWD, size_t, mbstate_t *_RESTRICT_KYWD, boolean_t);
typedef size_t (*wcrtomb_pfn_t)(char *_RESTRICT_KYWD, wchar_t,
mbstate_t *_RESTRICT_KYWD);
size_t __mbsnrtowcs_std(wchar_t *_RESTRICT_KYWD, const char **_RESTRICT_KYWD,
diff --git a/usr/src/lib/libc/port/locale/mbrtowc.c b/usr/src/lib/libc/port/locale/mbrtowc.c
index cd1e7b064f..c0934c88d1 100644
--- a/usr/src/lib/libc/port/locale/mbrtowc.c
+++ b/usr/src/lib/libc/port/locale/mbrtowc.c
@@ -40,7 +40,7 @@ mbrtowc_l(wchar_t *_RESTRICT_KYWD pwc, const char *_RESTRICT_KYWD s,
if (ps == NULL)
ps = &mbs;
- return (loc->ctype->lc_mbrtowc(pwc, s, n, ps));
+ return (loc->ctype->lc_mbrtowc(pwc, s, n, ps, B_FALSE));
}
size_t
@@ -49,3 +49,21 @@ mbrtowc(wchar_t *_RESTRICT_KYWD pwc, const char *_RESTRICT_KYWD s,
{
return (mbrtowc_l(pwc, s, n, ps, uselocale(NULL)));
}
+
+size_t
+mbrtowc_nz_l(wchar_t *_RESTRICT_KYWD pwc, const char *_RESTRICT_KYWD s,
+ size_t n, mbstate_t *_RESTRICT_KYWD ps, locale_t loc)
+{
+ static mbstate_t mbs;
+
+ if (ps == NULL)
+ ps = &mbs;
+ return (loc->ctype->lc_mbrtowc(pwc, s, n, ps, B_TRUE));
+}
+
+size_t
+mbrtowc_nz(wchar_t *_RESTRICT_KYWD pwc, const char *_RESTRICT_KYWD s,
+ size_t n, mbstate_t *_RESTRICT_KYWD ps)
+{
+ return (mbrtowc_nz_l(pwc, s, n, ps, uselocale(NULL)));
+}
diff --git a/usr/src/lib/libc/port/locale/mbsnrtowcs.c b/usr/src/lib/libc/port/locale/mbsnrtowcs.c
index 533b7776e0..6411cfcb74 100644
--- a/usr/src/lib/libc/port/locale/mbsnrtowcs.c
+++ b/usr/src/lib/libc/port/locale/mbsnrtowcs.c
@@ -68,11 +68,13 @@ __mbsnrtowcs_std(wchar_t *_RESTRICT_KYWD dst, const char **_RESTRICT_KYWD src,
if (dst == NULL) {
for (;;) {
- if ((nb = pmbrtowc(&wc, s, nms, ps)) == (size_t)-1)
+ if ((nb = pmbrtowc(&wc, s, nms, ps, B_FALSE)) ==
+ (size_t)-1) {
/* Invalid sequence - mbrtowc() sets errno. */
return ((size_t)-1);
- else if (nb == 0 || nb == (size_t)-2)
+ } else if (nb == 0 || nb == (size_t)-2) {
return (nchr);
+ }
s += nb;
nms -= nb;
nchr++;
@@ -81,7 +83,7 @@ __mbsnrtowcs_std(wchar_t *_RESTRICT_KYWD dst, const char **_RESTRICT_KYWD src,
}
while (len-- > 0) {
- if ((nb = pmbrtowc(dst, s, nms, ps)) == (size_t)-1) {
+ if ((nb = pmbrtowc(dst, s, nms, ps, B_FALSE)) == (size_t)-1) {
*src = s;
return ((size_t)-1);
} else if (nb == (size_t)-2) {
diff --git a/usr/src/lib/libc/port/locale/mskanji.c b/usr/src/lib/libc/port/locale/mskanji.c
index 5d9b899aae..69955e5afa 100644
--- a/usr/src/lib/libc/port/locale/mskanji.c
+++ b/usr/src/lib/libc/port/locale/mskanji.c
@@ -46,7 +46,7 @@
static size_t _MSKanji_mbrtowc(wchar_t *_RESTRICT_KYWD,
const char *_RESTRICT_KYWD,
- size_t, mbstate_t *_RESTRICT_KYWD);
+ size_t, mbstate_t *_RESTRICT_KYWD, boolean_t);
static int _MSKanji_mbsinit(const mbstate_t *);
static size_t _MSKanji_wcrtomb(char *_RESTRICT_KYWD, wchar_t,
mbstate_t *_RESTRICT_KYWD);
@@ -83,7 +83,7 @@ _MSKanji_mbsinit(const mbstate_t *ps)
static size_t
_MSKanji_mbrtowc(wchar_t *_RESTRICT_KYWD pwc, const char *_RESTRICT_KYWD s,
- size_t n, mbstate_t *_RESTRICT_KYWD ps)
+ size_t n, mbstate_t *_RESTRICT_KYWD ps, boolean_t zero)
{
_MSKanjiState *ms;
wchar_t wc;
@@ -135,7 +135,11 @@ _MSKanji_mbrtowc(wchar_t *_RESTRICT_KYWD pwc, const char *_RESTRICT_KYWD s,
} else {
if (pwc != NULL)
*pwc = wc;
- return (wc == L'\0' ? 0 : 1);
+ if (zero || wc != L'\0') {
+ return (1);
+ } else {
+ return (0);
+ }
}
}
diff --git a/usr/src/lib/libc/port/locale/none.c b/usr/src/lib/libc/port/locale/none.c
index 0511563cb1..5eefc746aa 100644
--- a/usr/src/lib/libc/port/locale/none.c
+++ b/usr/src/lib/libc/port/locale/none.c
@@ -73,7 +73,7 @@ __mbsinit_ascii(const mbstate_t *unused)
size_t
__mbrtowc_ascii(wchar_t *_RESTRICT_KYWD pwc, const char *_RESTRICT_KYWD s,
- size_t n, mbstate_t *_RESTRICT_KYWD unused)
+ size_t n, mbstate_t *_RESTRICT_KYWD unused, boolean_t zero)
{
_NOTE(ARGUNUSED(unused));
@@ -85,7 +85,11 @@ __mbrtowc_ascii(wchar_t *_RESTRICT_KYWD pwc, const char *_RESTRICT_KYWD s,
return ((size_t)-2);
if (pwc != NULL)
*pwc = (unsigned char)*s;
- return (*s == '\0' ? 0 : 1);
+ if (zero || *s != '\0') {
+ return (1);
+ } else {
+ return (0);
+ }
}
size_t
diff --git a/usr/src/lib/libc/port/locale/utf8.c b/usr/src/lib/libc/port/locale/utf8.c
index e919b9dd4a..a6e037d94e 100644
--- a/usr/src/lib/libc/port/locale/utf8.c
+++ b/usr/src/lib/libc/port/locale/utf8.c
@@ -37,7 +37,7 @@
static size_t _UTF8_mbrtowc(wchar_t *_RESTRICT_KYWD,
const char *_RESTRICT_KYWD,
- size_t, mbstate_t *_RESTRICT_KYWD);
+ size_t, mbstate_t *_RESTRICT_KYWD, boolean_t);
static int _UTF8_mbsinit(const mbstate_t *);
static size_t _UTF8_mbsnrtowcs(wchar_t *_RESTRICT_KYWD,
const char **_RESTRICT_KYWD, size_t, size_t,
@@ -75,7 +75,7 @@ _UTF8_mbsinit(const mbstate_t *ps)
static size_t
_UTF8_mbrtowc(wchar_t *_RESTRICT_KYWD pwc, const char *_RESTRICT_KYWD s,
- size_t n, mbstate_t *_RESTRICT_KYWD ps)
+ size_t n, mbstate_t *_RESTRICT_KYWD ps, boolean_t zero)
{
_UTF8State *us;
int ch, i, mask, want;
@@ -116,7 +116,11 @@ _UTF8_mbrtowc(wchar_t *_RESTRICT_KYWD pwc, const char *_RESTRICT_KYWD s,
/* Fast path for plain ASCII characters. */
if (pwc != NULL)
*pwc = ch;
- return (ch != '\0' ? 1 : 0);
+ if (zero || ch != '\0') {
+ return (1);
+ } else {
+ return (0);
+ }
}
if ((ch & 0xe0) == 0xc0) {
mask = 0x1f;
@@ -192,7 +196,11 @@ _UTF8_mbrtowc(wchar_t *_RESTRICT_KYWD pwc, const char *_RESTRICT_KYWD s,
if (pwc != NULL)
*pwc = wch;
us->want = 0;
- return (wch == L'\0' ? 0 : want);
+ if (zero || wch != L'\0') {
+ return (want);
+ } else {
+ return (0);
+ }
}
static size_t
@@ -221,18 +229,19 @@ _UTF8_mbsnrtowcs(wchar_t *_RESTRICT_KYWD dst, const char **_RESTRICT_KYWD src,
return ((size_t)-1);
}
for (;;) {
- if (nms > 0 && (signed char)*s > 0)
+ if (nms > 0 && (signed char)*s > 0) {
/*
* Fast path for plain ASCII characters
* excluding NUL.
*/
nb = 1;
- else if ((nb = _UTF8_mbrtowc(&wc, s, nms, ps)) ==
- (size_t)-1)
+ } else if ((nb = _UTF8_mbrtowc(&wc, s, nms, ps,
+ B_FALSE)) == (size_t)-1) {
/* Invalid sequence - mbrtowc() sets errno. */
return ((size_t)-1);
- else if (nb == 0 || nb == (size_t)-2)
+ } else if (nb == 0 || nb == (size_t)-2) {
return (nchr);
+ }
s += nb;
nms -= nb;
nchr++;
@@ -257,7 +266,7 @@ _UTF8_mbsnrtowcs(wchar_t *_RESTRICT_KYWD dst, const char **_RESTRICT_KYWD src,
*/
*dst = (wchar_t)*s;
nb = 1;
- } else if ((nb = _UTF8_mbrtowc(dst, s, nms, ps)) ==
+ } else if ((nb = _UTF8_mbrtowc(dst, s, nms, ps, B_FALSE)) ==
(size_t)-1) {
*src = s;
return ((size_t)-1);
diff --git a/usr/src/lib/libc/port/mapfile-vers b/usr/src/lib/libc/port/mapfile-vers
index dbe33eb9d1..da8de86f25 100644
--- a/usr/src/lib/libc/port/mapfile-vers
+++ b/usr/src/lib/libc/port/mapfile-vers
@@ -78,6 +78,13 @@ $if _x86 && _ELF64
$add amd64
$endif
+SYMBOL_VERSION ILLUMOS_0.32 {
+ protected:
+ fmemopen;
+ open_memstream;
+ open_wmemstream;
+} ILLUMOS_0.31;
+
SYMBOL_VERSION ILLUMOS_0.31 {
protected:
__unlockpt_xpg4;
diff --git a/usr/src/lib/libc/port/stdio/README.design b/usr/src/lib/libc/port/stdio/README.design
new file mode 100644
index 0000000000..748f7f9913
--- /dev/null
+++ b/usr/src/lib/libc/port/stdio/README.design
@@ -0,0 +1,327 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2020 Robert Mustacchi
+#
+ _ _ _
+ ___| |_ __| (_) ___
+/ __| __/ _` | |/ _ \
+\__ \ || (_| | | (_) |
+|___/\__\__,_|_|\___/
+
+Notes on the design of stdio.
+
+------------
+File Streams
+------------
+
+At the heart of the stdio is the 'FILE *'. The 'FILE *' represents a
+stream that can be read, written, and seeked. The streams traditionally
+refer to a file descriptor, when created by fopen(3C), or may refer to
+memory, when created by open_memstream(3C) or fmopen(3C). This document
+focuses on the implementation of streams. Other misc. functions in stdio
+are not discussed.
+
+------------
+Organization
+------------
+
+Most functions exist in a file with the same name. When adding new
+files to stdio the file name should match the primary function name.
+There are a few exceptions. Almost all of the logic related to both
+flushing and knowledge of how to handle the 32-bit ABI issues (described
+in the next section) can be found in flush.c.
+
+-----------------------------
+struct __FILE_TAG and the ABI
+-----------------------------
+
+The definition of the 'FILE *' is a pointer to a 'struct __FILE_TAG'.
+The 'struct __FILE_TAG' structure has a long history that dates back to
+historical UNIX. For better or for worse, we have inherited some of the
+design decisions of the past, it's important to understand what those
+are as they have profound impact on the stdio design and serve as a good
+cautionary tale for future ABI decisions.
+
+In the original UNIX designs, the 'struct __FILE_TAG' was exposed as a
+non-opaque structure. This was also true on other platforms. This had a
+couple of challenges:
+
+* It meant the size of the 'struct __FILE_TAG' was part of the ABI
+* Consumers would access the members directly. You can find examples of
+ this in our public headers where things like getc() are inlined in
+ terms of the implementation. Various 3rd-party software that has
+ existed for quite some time knows the offset of members and directly
+ manipulates them. This is still true as of 2020.
+* The 'struct __FILE_TAG' only used an unsigned char (uint8_t) for the
+ file descriptor in the 32-bit version. Other systems used a short, so
+ they were in better shape. This was changed in the 64-bit version to
+ use an int.
+* The main C stdio symbols 'stdin', 'stdout', and 'stderr', were (and
+ still are) exposed as an array. This means that while the 64-bit
+ structure is opaque, its size is actually part of the ABI.
+
+All of these issues have been dealt with in different ways in the
+system. The first thing that is a little confusing is where to find the
+definitions of the actual implementation. The 32-bit 'struct __FILE_BUF'
+is split into two different pieces, the part that is public and a
+secondary, private part.
+
+The public definition of the 'struct __FILE_TAG' for 32-bit code and the
+opaque definition for 64-bit code may be found in
+'usr/src/head/stdio_impl.h.'. The actual definition of the 64-bit
+structure and the 32-bit additions are all found in
+'usr/src/lib/libc/inc/file64.h.'
+
+In file64.h, one will find the 'struct xFILEdata' (extended FILE * data).
+This represents all of the data that has been added to stdio that is
+missing from the public structure. Whenever a 'FILE *' is allocated,
+32-bit code always ensures that there is a corresponding 'struct
+xFILEdata' that exists. Currently, we still have plenty of padding left
+in the 64-bit version of the structure for at least 3 pointers.
+
+To add a member to the structure, one has to add data to the structures
+in 'lib/libc/inc/file64.h'. If for some reason, all the padding would be
+used up, then you must stop. The size of the 64-bit structure _cannot_
+be extended, as noted earlier it is part of the ABI. If we hit this
+case, then one must introduce the struct xFILEdata for the lp64
+environment.
+
+--------------------------
+Allocating FILE Structures
+--------------------------
+
+libc defines a number of 'FILE *' structures by default. These can all
+be found in 'data.c'. The first _NFILE (20 or 60 depending on the
+platform) are defined statically. In the 32-bit case, the corresponding
+'struct _xFILEdata' is allocated along with it.
+
+To determine if a structure is free or not in the array, the `_flag`
+member is consulted. If the flag has been set to zero, then the STREAM
+is considered free and can be allocated. All of the allocated (whether
+used or not) 'FILE *' structures are present on a linked list which is
+found in 'flush.c' rooted at the symbol '__first_link'. This list is
+always scanned to try and reuse an existing 'FILE *' structure before
+allocating a new one. If all of the existing ones are in use, then one
+will be allocated.
+
+An important thing to understand is that once allocated, a 'FILE *' will
+never be freed by libc. It will always exist on the global list of
+structures to be reused.
+
+---------
+Buffering
+---------
+
+Every stream in stdio starts out as buffered. Buffering can be changed
+by calling either setbuf(3C) or setvbuf(3C). This buffer is stored in
+the `_base` member of the 'struct __FILE_TAG'. The amount of valid data
+in the buffer is maintained in the '_cnt' member of the structure. By
+default, there is no associated buffer with a stream. When the stream is
+first used, the buffer will be assigned by a call to _findbuf() in
+_findbuf.c.
+
+There are pre-allocated buffers that exist. There are two specifically
+for stdin and stdout (stderr is unbuffered). These include space for
+both the buffer and the pushback buffer. The pushback buffer is used so
+someone can call fungetc(3C) regardless of whether a buffering mode is
+enabled or not. Characters that we 'unget' are placed on the pushback
+buffer.
+
+For other buffering modes, we'll try and allocate an appropriate sized
+buffer. The buffer size defaults to BUFSIZ, but if the stream is backed
+by a file descriptor, we'll use fstat() to determine the appropriate
+size to use and match the file system block size. If we cannot allocate
+that, we'll fall back to trying to allocate a pushback buffer.
+
+libc defines static data for _NFILE worth of pushback buffers which are
+indexed based on the underlying file descriptor. This and the stdin and
+stdout buffers are all found in 'data.c' in _smbuf, _sibuf, and _sobuf
+respectively.
+
+------------------------------
+Reading, Writing, and Flushing
+------------------------------
+
+By default, reads and writes on a stream, whether backed by a
+file-descriptor or not, go through the buffer described in the previous
+section. If a read or write can be satisfied by the buffer, then no
+underlying I/O will occur, unless buffering has been disabled.
+
+The various function entry points that read such as fread(3C) or
+fgetc(3C) will not call read() directly but will instead try to fill the
+buffer, which will cause a read if required. This is centralized in
+_filbuf(). When a read is required from the underlying file, it will
+call _xread() in flush.c. For more on _xread() see the operations vector
+section further along.
+
+Unlike reads, writes are much less centralized and each of the main
+writing entry points has reimplemented the path of writing to the buffer
+and flushing it. It would be good in the future to consolidate them. In
+general, data will be written directly to the stdio buffer. When that
+buffer needs to be flushed either the _flsbuf() or _xflsbuf() functions
+will be called to actually flush out the buffer.
+
+When data needs to be flushed from a buffer to its underlying file
+descriptor (or other backing store), all of the write family functions
+ultimately call _xwrite().
+
+Flushes can occur in a few different ways:
+
+1. A write has filled up the buffer.
+2. A new line ('\n') is written and new-line buffering is used.
+3. fflush(3C) or a similar function has been called.
+4. A read occurs on a buffer that has unflushed writes.
+5. The stream is being closed.
+
+Most of these methods are fairly similar; however, the fflush(3C) case
+is a little different. fflush() may be asked to flush all of the streams
+when it is passed a NULL stream. Even when that happens it will still
+utilize the same underlying mechanism via _xflsbuf() or _flsbuf().
+
+-----------
+Orientation
+-----------
+
+Streams handle both wide characters and narrow characters. There is an
+internal multi-byte conversion state buffer that is included with every
+stream. A stream may exist in one of three modes:
+
+1. It may have an explicit narrow orientation
+2. It may have an explicit wide orientation
+3. It may have no orientation
+
+When most streams are created, they have no orientation. The orientation
+can then be explicitly set by calling fwide(3C). Some streams are also
+created with an explicit orientation, for example, open_wmemstream(3C)
+always sets the stream to be wide.
+
+The C standard dictates that certain operations will actually cause a
+stream with no orientation to have an explicit orientation set. Calling
+a narrow or wide related character function, such as 'fgetc(3C)' or
+'fgetwc(3C)' respectively will then cause the orientation to be set if
+it has not been. Once an orientation for a stream has been set, it
+cannot be changed until the stream has been closed or it is reset by
+calling freopen(3C).
+
+There are a few functions that don't change this today. One example is
+ungetc(3C). Often this isn't indicative of whether it should or
+shouldn't change the orientation, but is a side effect of the history of
+the stdio implementation.
+
+-------------------------------------
+Operations Vectors and Memory Streams
+-------------------------------------
+
+Traditionally, stdio streams were always backed by a file descriptor of
+some kind and therefore always called out into functions like read(2),
+write(2), lseek(2), and close(2) directly. A series of new functions
+were introduced in POSIX 2008 that add support for streams backed by
+memory in the form of fmemopen(3C), open_memstream(3C), and
+open_wmemstream(3C).
+
+To deal with this and other possible designs, an operations vector was
+added to the stream represented by the 'stdio_ops_t' structure. This is
+stored in the '_ops' member of the 'struct __FILE_BUF'. For a normal
+stream backed by a file descriptor, this member will be NULL.
+
+In places where a normal system call would have been made there is now a
+call to a corresponding function such as _xread(), _xwrite(), xseek(),
+_xseek64(), and _xclose(). If an operations vector is defined, it will
+call into the corresponding operation vector. If not, it will perform
+the traditional system call. This design choice consolidates all of the
+work required to implement non-file descriptor backed streams.
+
+When creating a non-file backed stream there are several expectations in
+the system:
+
+* The stream code should obtain a stream normally through a call to
+ _findiop().
+* If one needs to translate the normal fopen(3C) arguments, they should
+ use the _stdio_flags() function. This will also construct the
+ appropriate internal stdio flags for the stream.
+* The stream code must call _xassoc() to set the file operations vector
+ before return a 'FILE *' out of libc.
+* All of the operations vectors must be implemented.
+* If the stream is seekable, it must explicitly use the SET_SEEKABLE()
+ macro before return the stream.
+* If the stream is supposed to have a default orientation, it must set
+ it by calling _setorientation(). Not all streams have a default
+ orientation.
+* In the stream's close entry point it should call _xunassoc().
+
+--------------------------
+Extended File and fileno()
+--------------------------
+
+The 32-bit libc has historically been limited to 255 open streams
+because of the use of an unsigned char. This problem does not impact the
+64-bit libc. To deal with this, libc uses a series of techniques which
+are summarized for users in extendedFILE(5). The usage of extendedFILE
+can also be enabled by passing the special 'F' character to fopen(3C).
+
+The '_magic' member in the 32-bit 'struct __FILE_TAG' contains what used
+to be the file descriptor. When extended file is not in use, the
+_magic member still does contain the file descriptor. However, when
+extendedFILE is enabled, then the _magic member contains a sentinel
+value and the actual value is stored in the 'struct xFILEdata' _magic
+member.
+
+The act of getting the correct file descriptor has been centralized in a
+function called _get_fd(). This function knows how to handle the special
+32-bit case and the normal case. It also centralizes the logic of
+checking for a non-file backed stream. There are many cases in libc
+where we want to know the file descriptor to perform some operation;
+however, non-file backed streams do not have a corresponding file
+descriptor. When such a stream is detected, we will explicitly return
+-1. This ensures that a bad file descriptor will be used if someone
+mistakenly calls a system call. Functions like _fileno() call this
+directly.
+
+-------
+Testing
+-------
+
+There is a burgeoning test suite for stdio in
+usr/src/test/libc-tests/tests/stdio. If working in stdio (or libc more
+generally) it is recommended that you run this test suite and add new
+tests to it where appropriate. For most new functionality it is
+encouraged that you both import test suites that may already exist and
+that you also write your own test suites to properly cover a number of
+error and corner cases.
+
+Tests should also be written against libumem(3LIB), and umem debugging
+should be explicitly enabled in the program. Enabling umem debugging can
+catch a number of common memory usage errors. It also makes it easier to
+test for memory leaks by taking a core file and used the mdb
+'::findleaks' dcmd. A good starting point is to place the following in
+the program:
+
+const char *
+_umem_debug_init(void)
+{
+ return ("default,verbose");
+}
+
+const char *
+_umem_logging_init(void)
+{
+ return ("fail,contents");
+}
+
+For the definition of these flags, see umem_debug(3MALLOC).
+
+In addition, by leveraging umem debugging it becomes very easy to
+simulate malloc failure when required. This can be enabled by calling
+umem_setmtbf(1), which ensures that any subsequent memory requests
+through malloc(), including those made indirectly by libc, will fail. To
+restore the behavior after a test, one can simply call umem_setmtbf(0).
diff --git a/usr/src/lib/libc/port/stdio/_endopen.c b/usr/src/lib/libc/port/stdio/_endopen.c
index a7c4508cbb..fa548f6385 100644
--- a/usr/src/lib/libc/port/stdio/_endopen.c
+++ b/usr/src/lib/libc/port/stdio/_endopen.c
@@ -25,7 +25,7 @@
*/
/* Copyright (c) 1988 AT&T */
-/* All Rights Reserved */
+/* All Rights Reserved */
/* Copyright (c) 2013 OmniTI Computer Consulting, Inc. All rights reserved. */
@@ -60,53 +60,9 @@ _endopen(const char *name, const char *type, FILE *iop, int largefile)
if (iop == NULL)
return (NULL);
- switch (type[0]) {
- default:
- errno = EINVAL;
- return (NULL);
- case 'r':
- oflag = O_RDONLY;
- fflag = _IOREAD;
- break;
- case 'w':
- oflag = O_WRONLY | O_TRUNC | O_CREAT;
- fflag = _IOWRT;
- break;
- case 'a':
- oflag = O_WRONLY | O_APPEND | O_CREAT;
- fflag = _IOWRT;
- break;
- }
- plusflag = 0;
- eflag = 0;
- xflag = 0;
- for (echr = type + 1; *echr != '\0'; echr++) {
- switch (*echr) {
- /* UNIX ignores 'b' and treats text and binary the same */
- default:
- break;
- case '+':
- plusflag = 1;
- break;
- case 'e':
- eflag = 1;
- break;
- case 'x':
- xflag = 1;
- break;
- }
- }
- if (eflag) {
- /* Subsequent to a mode flag, 'e' indicates O_CLOEXEC */
- oflag = oflag | O_CLOEXEC;
- }
- if (plusflag) {
- oflag = (oflag & ~(O_RDONLY | O_WRONLY)) | O_RDWR;
- fflag = _IORW;
- }
- if (xflag) {
- oflag |= O_EXCL;
+ if (_stdio_flags(type, &oflag, &fflag) != 0) {
+ return (NULL);
}
/* select small or large file open based on flag */
@@ -121,21 +77,23 @@ _endopen(const char *name, const char *type, FILE *iop, int largefile)
/* As long as we make sure _flag stays != 0, we don't need to lock */
#ifdef _LP64
iop->_file = fd;
- iop->_flag = (iop->_flag & ~0377) | fflag;
+ iop->_flag = (iop->_flag & ~_DEF_FLAG_MASK) | fflag;
#else
if (fd <= _FILE_FD_MAX) {
SET_FILE(iop, fd);
} else if (_file_set(iop, fd, type) != 0) {
/* errno set in _file_set() */
+ int err = errno;
(void) close(fd);
+ errno = err;
return (NULL);
}
iop->_flag = fflag;
#endif /* _LP64 */
if (oflag == (O_WRONLY | O_APPEND | O_CREAT)) { /* type == "a" */
- if (lseek64(fd, (off64_t)0, SEEK_END) < (off64_t)0) {
- (void) close(fd);
+ if (_xseek64(iop, (off64_t)0, SEEK_END) < (off64_t)0) {
+ (void) _xclose(iop);
return (NULL);
}
}
diff --git a/usr/src/lib/libc/port/stdio/_filbuf.c b/usr/src/lib/libc/port/stdio/_filbuf.c
index 73150feb3a..2accbe1158 100644
--- a/usr/src/lib/libc/port/stdio/_filbuf.c
+++ b/usr/src/lib/libc/port/stdio/_filbuf.c
@@ -25,10 +25,7 @@
*/
/* Copyright (c) 1988 AT&T */
-/* All Rights Reserved */
-
-#pragma ident "%Z%%M% %I% %E% SMI"
-
+/* All Rights Reserved */
#pragma weak __filbuf = _filbuf
@@ -62,24 +59,24 @@ _filbuf(FILE *iop)
unsigned char flag;
#endif
- if (!(iop->_flag & _IOREAD)) /* check, correct permissions */
- {
- if (iop->_flag & _IORW)
- iop->_flag |= _IOREAD; /* change direction */
+ if (!(iop->_flag & _IOREAD)) { /* check, correct permissions */
+ if (iop->_flag & _IORW) {
+ iop->_flag |= _IOREAD; /* change direction */
/* to read - fseek */
- else {
+ } else {
errno = EBADF;
return (EOF);
}
}
if (iop->_base == 0) {
- if ((endbuf = _findbuf(iop)) == 0) /* get buffer and */
- /* end_of_buffer */
+ /* Get the buffer and end of buffer */
+ if ((endbuf = _findbuf(iop)) == 0) {
return (EOF);
- }
- else
+ }
+ } else {
endbuf = _bufend(iop);
+ }
/*
* Flush all line-buffered streams before we
@@ -109,6 +106,7 @@ _filbuf(FILE *iop)
return (EOF);
}
}
+
/*
* Fill buffer or read 1 byte for unbuffered, handling any errors.
*/
@@ -117,7 +115,7 @@ _filbuf(FILE *iop)
nbyte = 1;
else
nbyte = endbuf - iop->_base;
- if ((res = read(GET_FD(iop), (char *)iop->_base, nbyte)) > 0) {
+ if ((res = _xread(iop, (char *)iop->_base, nbyte)) > 0) {
iop->_cnt = res - 1;
return (*iop->_ptr++);
}
diff --git a/usr/src/lib/libc/port/stdio/_findbuf.c b/usr/src/lib/libc/port/stdio/_findbuf.c
index d0c1949e98..e0633c80c6 100644
--- a/usr/src/lib/libc/port/stdio/_findbuf.c
+++ b/usr/src/lib/libc/port/stdio/_findbuf.c
@@ -25,9 +25,11 @@
*/
/* Copyright (c) 1988 AT&T */
-/* All Rights Reserved */
+/* All Rights Reserved */
-#pragma ident "%Z%%M% %I% %E% SMI"
+/*
+ * Copyright 2020 Robert Mustacchi
+ */
#define _LARGEFILE64_SOURCE 1
@@ -51,48 +53,47 @@
Uchar *
_findbuf(FILE *iop)
{
- int fd = GET_FD(iop);
+ int fd = _get_fd(iop);
Uchar *buf;
int size = BUFSIZ;
Uchar *endbuf;
int tty = -1;
- struct stat64 stbuf; /* used to get file system block size */
-
- if (iop->_flag & _IONBF) /* need a small buffer, at least */
- {
- trysmall:;
+ if (iop->_flag & _IONBF) { /* need a small buffer, at least */
+ trysmall:
size = _SMBFSZ - PUSHBACK;
- if (fd < _NFILE)
+ if (fd >= 0 && fd < _NFILE) {
buf = _smbuf[fd];
- else if ((buf = (Uchar *)malloc(_SMBFSZ * sizeof (Uchar))) !=
- NULL)
+ } else if ((buf = (Uchar *)malloc(_SMBFSZ * sizeof (Uchar))) !=
+ NULL) {
iop->_flag |= _IOMYBUF;
- }
-#ifndef _STDIO_ALLOCATE
- else if (fd < 2 && (tty = isatty(fd))) {
- buf = (fd == 0) ? _sibuf : _sobuf; /* special buffer */
- /* for std{in,out} */
- }
-#endif
- else {
+ }
+ } else if (fd >= 0 && fd < 2 && (tty = isatty(fd))) {
+ /* Use special buffers for standard in and standard out */
+ buf = (fd == 0) ? _sibuf : _sobuf;
+ } else {
+
/*
- * The operating system can tell us the
- * right size for a buffer;
- * avoid 0-size buffers as returned for some
- * special files (doors)
+ * The operating system can tell us the right size for a buffer;
+ * avoid 0-size buffers as returned for some special files
+ * (doors). Use the default buffer size for memory streams.
*/
- if (fstat64(fd, &stbuf) == 0 && stbuf.st_blksize > 0)
+ struct stat64 stbuf;
+
+ if (fd != -1 && fstat64(fd, &stbuf) == 0 && stbuf.st_blksize >
+ 0) {
size = stbuf.st_blksize;
+ }
if ((buf = (Uchar *)malloc(sizeof (Uchar)*(size+_SMBFSZ))) !=
- NULL)
+ NULL) {
iop->_flag |= _IOMYBUF;
- else
+ } else {
goto trysmall;
+ }
}
if (buf == NULL)
- return (NULL); /* malloc() failed */
+ return (NULL); /* malloc() failed */
iop->_base = buf + PUSHBACK; /* bytes for pushback */
iop->_ptr = buf + PUSHBACK;
endbuf = iop->_base + size;
diff --git a/usr/src/lib/libc/port/stdio/_flsbuf.c b/usr/src/lib/libc/port/stdio/_flsbuf.c
index 234d9fd2c4..81d3b0dc3a 100644
--- a/usr/src/lib/libc/port/stdio/_flsbuf.c
+++ b/usr/src/lib/libc/port/stdio/_flsbuf.c
@@ -25,9 +25,7 @@
*/
/* Copyright (c) 1988 AT&T */
-/* All Rights Reserved */
-
-#pragma ident "%Z%%M% %I% %E% SMI"
+/* All Rights Reserved */
#pragma weak __flsbuf = _flsbuf
@@ -41,17 +39,18 @@
#include <sys/types.h>
#include "stdiom.h"
+/*
+ * flush (write) buffer, save ch,
+ * return EOF on failure
+ */
int
-_flsbuf(int ch, FILE *iop) /* flush (write) buffer, save ch, */
- /* return EOF on failure */
+_flsbuf(int ch, FILE *iop)
{
Uchar uch;
- do /* only loop if need to use _wrtchk() on non-_IOFBF */
- {
+ do { /* only loop if need to use _wrtchk() on non-_IOFBF */
switch (iop->_flag & (_IOFBF | _IOLBF | _IONBF |
- _IOWRT | _IOEOF))
- {
+ _IOWRT | _IOEOF)) {
case _IOFBF | _IOWRT: /* okay to do full-buffered case */
if (iop->_base != 0 && iop->_ptr > iop->_base)
goto flush_putc; /* skip _wrtchk() */
@@ -71,7 +70,7 @@ _flsbuf(int ch, FILE *iop) /* flush (write) buffer, save ch, */
case _IONBF | _IOWRT: /* okay to do no-buffered case */
iop->_cnt = 0;
uch = (unsigned char)ch;
- if (write(GET_FD(iop), (char *)&uch, 1) != 1) {
+ if (_xwrite(iop, (char *)&uch, 1) != 1) {
if (!cancel_active())
iop->_flag |= _IOERR;
return (EOF);
diff --git a/usr/src/lib/libc/port/stdio/_stdio_flags.c b/usr/src/lib/libc/port/stdio/_stdio_flags.c
new file mode 100644
index 0000000000..b54f9051e6
--- /dev/null
+++ b/usr/src/lib/libc/port/stdio/_stdio_flags.c
@@ -0,0 +1,109 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1988 AT&T */
+/* All Rights Reserved */
+
+/* Copyright (c) 2013 OmniTI Computer Consulting, Inc. All rights reserved. */
+/*
+ * Copyright 2020 Robert Mustacchi
+ */
+
+/*
+ * Commonized processing of the 'mode' string for stdio.
+ */
+
+#include "mtlib.h"
+#include "file64.h"
+#include <stdio.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <fcntl.h>
+
+int
+_stdio_flags(const char *type, int *oflagsp, int *fflagsp)
+{
+ int oflag, fflag, plusflag, eflag, xflag;
+ const char *echr;
+
+ oflag = fflag = 0;
+ switch (type[0]) {
+ default:
+ errno = EINVAL;
+ return (-1);
+ case 'r':
+ oflag = O_RDONLY;
+ fflag = _IOREAD;
+ break;
+ case 'w':
+ oflag = O_WRONLY | O_TRUNC | O_CREAT;
+ fflag = _IOWRT;
+ break;
+ case 'a':
+ oflag = O_WRONLY | O_APPEND | O_CREAT;
+ fflag = _IOWRT;
+ break;
+ }
+
+ plusflag = 0;
+ eflag = 0;
+ xflag = 0;
+ for (echr = type + 1; *echr != '\0'; echr++) {
+ switch (*echr) {
+ /* UNIX ignores 'b' and treats text and binary the same */
+ default:
+ break;
+ case '+':
+ plusflag = 1;
+ break;
+ case 'e':
+ eflag = 1;
+ break;
+ case 'x':
+ xflag = 1;
+ break;
+ }
+ }
+
+ if (eflag) {
+ /* Subsequent to a mode flag, 'e' indicates O_CLOEXEC */
+ oflag = oflag | O_CLOEXEC;
+ }
+
+ if (plusflag) {
+ oflag = (oflag & ~(O_RDONLY | O_WRONLY)) | O_RDWR;
+ fflag = _IORW;
+ }
+
+ if (xflag) {
+ oflag |= O_EXCL;
+ }
+
+ *oflagsp = oflag;
+ *fflagsp = fflag;
+
+ return (0);
+}
diff --git a/usr/src/lib/libc/port/stdio/doscan.c b/usr/src/lib/libc/port/stdio/doscan.c
index e375d9824f..fda4d5c2ea 100644
--- a/usr/src/lib/libc/port/stdio/doscan.c
+++ b/usr/src/lib/libc/port/stdio/doscan.c
@@ -25,9 +25,7 @@
*/
/* Copyright (c) 1988 AT&T */
-/* All Rights Reserved */
-
-#pragma ident "%Z%%M% %I% %E% SMI"
+/* All Rights Reserved */
#include "lint.h"
#include <sys/types.h>
@@ -402,7 +400,7 @@ charswitch: /* target of a goto 8-( */
if (size == 'l') {
int c, len, i;
int negflg = 0;
- unsigned char *p;
+ unsigned char *p;
p = (unsigned char *)(fmt - 1);
len = 0;
@@ -830,10 +828,10 @@ readchar(FILE *iop, int *chcount)
int inchar;
char buf[1];
- if ((iop->_flag & _IOWRT) || (iop->_cnt != 0))
+ if ((iop->_flag & _IOWRT) || (iop->_cnt != 0)) {
inchar = locgetc((*chcount));
- else {
- if (read(FILENO(iop), buf, 1) != 1)
+ } else {
+ if (_xread(iop, buf, 1) != 1)
return (EOF);
inchar = (int)buf[0];
(*chcount) += 1;
diff --git a/usr/src/lib/libc/port/stdio/fileno.c b/usr/src/lib/libc/port/stdio/fileno.c
index 70f64faa89..4e99438dc9 100644
--- a/usr/src/lib/libc/port/stdio/fileno.c
+++ b/usr/src/lib/libc/port/stdio/fileno.c
@@ -25,9 +25,7 @@
*/
/* Copyright (c) 1988 AT&T */
-/* All Rights Reserved */
-
-#pragma ident "%Z%%M% %I% %E% SMI"
+/* All Rights Reserved */
#pragma weak _fileno = fileno
@@ -43,5 +41,5 @@
int
fileno(FILE *iop)
{
- return (GET_FD(iop));
+ return (_get_fd(iop));
}
diff --git a/usr/src/lib/libc/port/stdio/flush.c b/usr/src/lib/libc/port/stdio/flush.c
index 7d3549ca0d..be710ccb2a 100644
--- a/usr/src/lib/libc/port/stdio/flush.c
+++ b/usr/src/lib/libc/port/stdio/flush.c
@@ -21,10 +21,11 @@
/*
* Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2020 Robert Mustacchi
*/
/* Copyright (c) 1988 AT&T */
-/* All Rights Reserved */
+/* All Rights Reserved */
#include "lint.h"
#include "mtlib.h"
@@ -42,6 +43,8 @@
#include <stddef.h>
#include <errno.h>
#include <fcntl.h>
+#include <sys/debug.h>
+#include <limits.h>
#define _iob __iob
@@ -376,7 +379,8 @@ rescan:
lastlink = *prev = hdr;
fp->_ptr = 0;
fp->_base = 0;
- fp->_flag = 0377; /* claim the fp by setting low 8 bits */
+ /* claim the fp by setting low 8 bits */
+ fp->_flag = _DEF_FLAG_MASK;
if (threaded)
cancel_safe_mutex_unlock(&_first_link_lock);
@@ -387,11 +391,19 @@ static void
isseekable(FILE *iop)
{
struct stat64 fstatbuf;
- int save_errno;
+ int fd, save_errno;
save_errno = errno;
- if (fstat64(GET_FD(iop), &fstatbuf) != 0) {
+ /*
+ * non-FILE based STREAMS are required to declare their own seekability
+ * and therefore we should not try and test them below.
+ */
+ fd = _get_fd(iop);
+ if (fd == -1) {
+ return;
+ }
+ if (fstat64(fd, &fstatbuf) != 0) {
/*
* when we don't know what it is we'll
* do the old behaviour and flush
@@ -545,8 +557,7 @@ _xflsbuf(FILE *iop)
_bufsync(iop, bufend);
if (n > 0) {
- int fd = GET_FD(iop);
- while ((num_wrote = write(fd, base, (size_t)n)) != n) {
+ while ((num_wrote = _xwrite(iop, base, (size_t)n)) != n) {
if (num_wrote <= 0) {
if (!cancel_active())
iop->_flag |= _IOERR;
@@ -600,8 +611,8 @@ _fflush_l_iops(void) /* flush all buffers */
* knowing that when it is 0, it isn't allocated and
* cannot be allocated while we're holding the
* _first_link_lock. And when _IONBF is set (also the
- * case when _flag is 0377, or alloc in progress), we
- * also ignore it.
+ * case when _flag is 0377 -- _DEF_FLAG_MASK, or alloc in
+ * progress), we also ignore it.
*
* Ignore locked streams; it will appear as if
* concurrent updates happened after fflush(NULL). Note
@@ -662,7 +673,7 @@ _fflush_u(FILE *iop)
/* this portion is always assumed locked */
if (!(iop->_flag & _IOWRT)) {
- (void) lseek64(GET_FD(iop), -iop->_cnt, SEEK_CUR);
+ (void) _xseek64(iop, -iop->_cnt, SEEK_CUR);
iop->_cnt = 0;
/* needed for ungetc & multibyte pushbacks */
iop->_ptr = iop->_base;
@@ -700,7 +711,7 @@ fclose(FILE *iop)
/* Is not unbuffered and opened for read and/or write ? */
if (!(iop->_flag & _IONBF) && (iop->_flag & (_IOWRT | _IOREAD | _IORW)))
res = _fflush_u(iop);
- if (close(GET_FD(iop)) < 0)
+ if (_xclose(iop) < 0)
res = EOF;
if (iop->_flag & _IOMYBUF) {
(void) free((char *)iop->_base - PUSHBACK);
@@ -751,7 +762,7 @@ fcloseall(void)
if (!(iop->_flag & _IONBF) &&
(iop->_flag & (_IOWRT | _IOREAD | _IORW)))
(void) _fflush_u(iop);
- (void) close(GET_FD(iop));
+ (void) _xclose(iop);
if (iop->_flag & _IOMYBUF)
free((char *)iop->_base - PUSHBACK);
iop->_base = NULL;
@@ -781,7 +792,7 @@ close_fd(FILE *iop)
/* Is not unbuffered and opened for read and/or write ? */
if (!(iop->_flag & _IONBF) && (iop->_flag & (_IOWRT | _IOREAD | _IORW)))
res = _fflush_u(iop);
- if (close(GET_FD(iop)) < 0)
+ if (_xclose(iop) < 0)
res = EOF;
if (iop->_flag & _IOMYBUF) {
(void) free((char *)iop->_base - PUSHBACK);
@@ -809,7 +820,8 @@ getiop(FILE *fp, rmutex_t *lk, mbstate_t *mb)
fp->_cnt = 0;
fp->_ptr = NULL;
fp->_base = NULL;
- fp->_flag = 0377; /* claim the fp by setting low 8 bits */
+ /* claim the fp by setting low 8 bits */
+ fp->_flag = _DEF_FLAG_MASK;
(void) memset(mb, 0, sizeof (mbstate_t));
FUNLOCKFILE(lk);
return (fp);
@@ -943,3 +955,164 @@ enable_extended_FILE_stdio(int fd, int action)
return (0);
}
#endif
+
+/*
+ * Wrappers around the various system calls that stdio needs to make on a file
+ * descriptor.
+ */
+static stdio_ops_t *
+get_stdops(FILE *iop)
+{
+#ifdef _LP64
+ return (iop->_ops);
+#else
+ struct xFILEdata *dat = getxfdat(iop);
+ return (dat->_ops);
+#endif
+}
+
+static void
+set_stdops(FILE *iop, stdio_ops_t *ops)
+{
+#ifdef _LP64
+ ASSERT3P(iop->_ops, ==, NULL);
+ iop->_ops = ops;
+#else
+ struct xFILEdata *dat = getxfdat(iop);
+ ASSERT3P(dat->_ops, ==, NULL);
+ dat->_ops = ops;
+#endif
+
+}
+
+ssize_t
+_xread(FILE *iop, void *buf, size_t nbytes)
+{
+ stdio_ops_t *ops = get_stdops(iop);
+ if (ops != NULL) {
+ return (ops->std_read(iop, buf, nbytes));
+ }
+
+ return (read(_get_fd(iop), buf, nbytes));
+}
+
+ssize_t
+_xwrite(FILE *iop, const void *buf, size_t nbytes)
+{
+ stdio_ops_t *ops = get_stdops(iop);
+ if (ops != NULL) {
+ return (ops->std_write(iop, buf, nbytes));
+ }
+ return (write(_get_fd(iop), buf, nbytes));
+}
+
+off_t
+_xseek(FILE *iop, off_t off, int whence)
+{
+ stdio_ops_t *ops = get_stdops(iop);
+ if (ops != NULL) {
+ return (ops->std_seek(iop, off, whence));
+ }
+
+ return (lseek(_get_fd(iop), off, whence));
+}
+
+off64_t
+_xseek64(FILE *iop, off64_t off, int whence)
+{
+ stdio_ops_t *ops = get_stdops(iop);
+ if (ops != NULL) {
+ /*
+ * The internal APIs only operate with an off_t. An off64_t in
+ * an ILP32 environment may represent a value larger than they
+ * can accept. As such, we try and catch such cases and error
+ * about it before we get there.
+ */
+ if (off > LONG_MAX || off < LONG_MIN) {
+ errno = EOVERFLOW;
+ return (-1);
+ }
+ return (ops->std_seek(iop, off, whence));
+ }
+
+ return (lseek64(_get_fd(iop), off, whence));
+}
+
+int
+_xclose(FILE *iop)
+{
+ stdio_ops_t *ops = get_stdops(iop);
+ if (ops != NULL) {
+ return (ops->std_close(iop));
+ }
+
+ return (close(_get_fd(iop)));
+}
+
+void *
+_xdata(FILE *iop)
+{
+ stdio_ops_t *ops = get_stdops(iop);
+ if (ops != NULL) {
+ return (ops->std_data);
+ }
+
+ return (NULL);
+}
+
+int
+_xassoc(FILE *iop, fread_t readf, fwrite_t writef, fseek_t seekf,
+ fclose_t closef, void *data)
+{
+ stdio_ops_t *ops = get_stdops(iop);
+
+ if (ops == NULL) {
+ ops = malloc(sizeof (*ops));
+ if (ops == NULL) {
+ return (-1);
+ }
+ set_stdops(iop, ops);
+ }
+
+ ops->std_read = readf;
+ ops->std_write = writef;
+ ops->std_seek = seekf;
+ ops->std_close = closef;
+ ops->std_data = data;
+
+ return (0);
+}
+
+void
+_xunassoc(FILE *iop)
+{
+ stdio_ops_t *ops = get_stdops(iop);
+ if (ops == NULL) {
+ return;
+ }
+ set_stdops(iop, NULL);
+ free(ops);
+}
+
+int
+_get_fd(FILE *iop)
+{
+ /*
+ * Streams with an ops vector (currently the memory stream family) do
+ * not have an underlying file descriptor that we can give back to the
+ * user. In such cases, return -1 to explicitly make sure that they'll
+ * get an ebadf from things.
+ */
+ if (get_stdops(iop) != NULL) {
+ return (-1);
+ }
+#ifdef _LP64
+ return (iop->_file);
+#else
+ if (iop->__extendedfd) {
+ return (_file_get(iop));
+ } else {
+ return (iop->_magic);
+ }
+#endif
+}
diff --git a/usr/src/lib/libc/port/stdio/fmemopen.c b/usr/src/lib/libc/port/stdio/fmemopen.c
new file mode 100644
index 0000000000..e2321c9632
--- /dev/null
+++ b/usr/src/lib/libc/port/stdio/fmemopen.c
@@ -0,0 +1,270 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2020 Robert Mustacchi
+ */
+
+/*
+ * Implements fmemopen(3C).
+ */
+
+#include "mtlib.h"
+#include "file64.h"
+#include <stdio.h>
+#include "stdiom.h"
+#include <errno.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/sysmacros.h>
+#include <limits.h>
+
+typedef enum fmemopen_flags {
+ /*
+ * Indicates that the user gave us the buffer and so we shouldn't free
+ * it.
+ */
+ FMO_F_USER_BUFFER = 1 << 0,
+ /*
+ * When the stream is open for update (a, a+) then we have to have
+ * slightly different behavior on write and zeroing the buffer.
+ */
+ FMO_F_APPEND = 1 << 1
+} fmemopen_flags_t;
+
+typedef struct fmemopen {
+ /*
+ * Pointer to the underlying memory stream.
+ */
+ char *fmo_buf;
+ /*
+ * Allocated length of the buffer.
+ */
+ size_t fmo_alloc;
+ /*
+ * Current position of the buffer.
+ */
+ size_t fmo_pos;
+ /*
+ * Current 'size' of the buffer. POSIX describes a size that the buffer
+ * has which is separate from the allocated size, but cannot exceed it.
+ */
+ size_t fmo_lsize;
+ fmemopen_flags_t fmo_flags;
+} fmemopen_t;
+
+static ssize_t
+fmemopen_read(FILE *iop, char *buf, size_t nbytes)
+{
+ fmemopen_t *fmp = _xdata(iop);
+
+ nbytes = MIN(nbytes, fmp->fmo_lsize - fmp->fmo_pos);
+ if (nbytes == 0) {
+ return (0);
+ }
+
+ (void) memcpy(buf, fmp->fmo_buf, nbytes);
+ fmp->fmo_pos += nbytes;
+
+ return (nbytes);
+}
+
+static ssize_t
+fmemopen_write(FILE *iop, const char *buf, size_t nbytes)
+{
+ size_t npos;
+ fmemopen_t *fmp = _xdata(iop);
+
+ if ((fmp->fmo_flags & FMO_F_APPEND) != 0) {
+ /*
+ * POSIX says that if append mode is in effect, we must always
+ * seek to the logical size. This effectively is mimicking the
+ * O_APPEND behavior.
+ */
+ fmp->fmo_pos = fmp->fmo_lsize;
+ }
+
+ if (nbytes == 0) {
+ return (0);
+ } else if (nbytes >= SSIZE_MAX) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ npos = fmp->fmo_pos + nbytes;
+ if (npos < nbytes) {
+ errno = EOVERFLOW;
+ return (-1);
+ } else if (npos > fmp->fmo_alloc) {
+ nbytes = fmp->fmo_alloc - fmp->fmo_pos;
+ }
+
+ (void) memcpy(&fmp->fmo_buf[fmp->fmo_pos], buf, nbytes);
+ fmp->fmo_pos += nbytes;
+
+ if (fmp->fmo_pos > fmp->fmo_lsize) {
+ fmp->fmo_lsize = fmp->fmo_pos;
+
+ /*
+ * POSIX distinguishes behavior for writing a NUL in these
+ * streams. Basically if we are open for update and we are at
+ * the end of the buffer, we don't place a NUL. Otherwise, we
+ * always place one at the current position (or the end if we
+ * were over the edge).
+ */
+ if (fmp->fmo_lsize < fmp->fmo_alloc) {
+ fmp->fmo_buf[fmp->fmo_lsize] = '\0';
+ } else if ((fmp->fmo_flags & FMO_F_APPEND) == 0) {
+ fmp->fmo_buf[fmp->fmo_alloc - 1] = '\0';
+ }
+ }
+
+ return (nbytes);
+}
+
+static off_t
+fmemopen_seek(FILE *iop, off_t off, int whence)
+{
+ fmemopen_t *fmp = _xdata(iop);
+ size_t base, npos;
+
+ switch (whence) {
+ case SEEK_SET:
+ base = 0;
+ break;
+ case SEEK_CUR:
+ base = fmp->fmo_pos;
+ break;
+ case SEEK_END:
+ base = fmp->fmo_lsize;
+ break;
+ default:
+ errno = EINVAL;
+ return (-1);
+ }
+
+ if (!memstream_seek(base, off, fmp->fmo_alloc, &npos)) {
+ errno = EINVAL;
+ return (-1);
+ }
+ fmp->fmo_pos = npos;
+
+ return ((off_t)fmp->fmo_pos);
+}
+
+static void
+fmemopen_free(fmemopen_t *fmp)
+{
+ if (fmp->fmo_buf != NULL &&
+ (fmp->fmo_flags & FMO_F_USER_BUFFER) == 0) {
+ free(fmp->fmo_buf);
+ }
+
+ free(fmp);
+}
+
+static int
+fmemopen_close(FILE *iop)
+{
+ fmemopen_t *fmp = _xdata(iop);
+ fmemopen_free(fmp);
+ _xunassoc(iop);
+ return (0);
+}
+
+FILE *
+fmemopen(void *_RESTRICT_KYWD buf, size_t size,
+ const char *_RESTRICT_KYWD mode)
+{
+ int oflags, fflags, err;
+ fmemopen_t *fmp;
+ FILE *iop;
+
+ if (size == 0 || mode == NULL) {
+ errno = EINVAL;
+ return (NULL);
+ }
+
+ if (_stdio_flags(mode, &oflags, &fflags) != 0) {
+ /* errno set for us */
+ return (NULL);
+ }
+
+ /*
+ * buf is only allowed to be NULL if the '+' is specified. If the '+'
+ * mode was specified, then we'll have fflags set to _IORW.
+ */
+ if (buf == NULL && fflags != _IORW) {
+ errno = EINVAL;
+ return (NULL);
+ }
+
+ if ((fmp = calloc(1, sizeof (fmemopen_t))) == NULL) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+
+ if (buf == NULL) {
+ fmp->fmo_buf = calloc(size, sizeof (uint8_t));
+ if (fmp->fmo_buf == NULL) {
+ errno = ENOMEM;
+ goto cleanup;
+ }
+ } else {
+ fmp->fmo_buf = buf;
+ fmp->fmo_flags |= FMO_F_USER_BUFFER;
+ }
+
+ fmp->fmo_alloc = size;
+
+ /*
+ * Set the initial logical size and position depending on whether we're
+ * using r(+), w(+), and a(+). The latter two are identified by O_TRUNC
+ * and O_APPEND in oflags.
+ */
+ if ((oflags & O_APPEND) != 0) {
+ fmp->fmo_pos = strnlen(fmp->fmo_buf, fmp->fmo_alloc);
+ fmp->fmo_lsize = fmp->fmo_pos;
+ fmp->fmo_flags |= FMO_F_APPEND;
+ } else if ((oflags & O_TRUNC) != 0) {
+ fmp->fmo_buf[0] = '\0';
+ fmp->fmo_pos = 0;
+ fmp->fmo_lsize = 0;
+ } else {
+ fmp->fmo_pos = 0;
+ fmp->fmo_lsize = size;
+ }
+
+ iop = _findiop();
+ if (iop == NULL) {
+ goto cleanup;
+ }
+
+#ifdef _LP64
+ iop->_flag = (iop->_flag & ~_DEF_FLAG_MASK) | fflags;
+#else
+ iop->_flag = fflags;
+#endif
+ if (_xassoc(iop, fmemopen_read, fmemopen_write, fmemopen_seek,
+ fmemopen_close, fmp) != 0) {
+ goto cleanup;
+ }
+
+ SET_SEEKABLE(iop);
+
+ return (iop);
+
+cleanup:
+ err = errno;
+ fmemopen_free(fmp);
+ errno = err;
+ return (NULL);
+}
diff --git a/usr/src/lib/libc/port/stdio/fopen.c b/usr/src/lib/libc/port/stdio/fopen.c
index eea8093aca..728a0f4978 100644
--- a/usr/src/lib/libc/port/stdio/fopen.c
+++ b/usr/src/lib/libc/port/stdio/fopen.c
@@ -25,7 +25,7 @@
*/
/* Copyright (c) 1988 AT&T */
-/* All Rights Reserved */
+/* All Rights Reserved */
/*
* University Copyright- Copyright (c) 1982, 1986, 1988
@@ -37,7 +37,9 @@
* contributors.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
+/*
+ * Copyright 2020 Robert Mustacchi
+ */
#include "lint.h"
#include "file64.h"
@@ -88,6 +90,17 @@ _freopen_null(const char *type, FILE *iop)
return (NULL);
}
+ /*
+ * If this is not a file-based stream (as in we have no file
+ * descriptor), then we need to close this, but still actually return an
+ * error.
+ */
+ if (_get_fd(iop) == -1) {
+ (void) close_fd(iop);
+ errno = EBADF;
+ return (NULL);
+ }
+
if (!(iop->_flag & _IONBF) && (iop->_flag & (_IOWRT | _IOREAD | _IORW)))
(void) _fflush_u(iop);
@@ -140,7 +153,7 @@ _freopen_null(const char *type, FILE *iop)
}
#ifdef _LP64
- iop->_flag &= ~0377; /* clear lower 8-bits */
+ iop->_flag &= ~_DEF_FLAG_MASK; /* clear lower 8-bits */
if (mode == 'r') {
iop->_flag |= _IOREAD;
nflag = oflag & ~O_APPEND;
diff --git a/usr/src/lib/libc/port/stdio/fputs.c b/usr/src/lib/libc/port/stdio/fputs.c
index be0f5b4f8f..46b0d87107 100644
--- a/usr/src/lib/libc/port/stdio/fputs.c
+++ b/usr/src/lib/libc/port/stdio/fputs.c
@@ -25,10 +25,7 @@
*/
/* Copyright (c) 1988 AT&T */
-/* All Rights Reserved */
-
-#pragma ident "%Z%%M% %I% %E% SMI"
-
+/* All Rights Reserved */
/*
* Ptr args aren't checked for NULL because the program would be a
@@ -112,15 +109,13 @@ fputs(const char *ptr, FILE *iop)
return (EOF);
}
}
- }
- else
- {
+ } else {
/* write out to an unbuffered file */
ssize_t num_wrote;
ssize_t count = (ssize_t)ptrlen;
- int fd = GET_FD(iop);
- while ((num_wrote = write(fd, ptr, (size_t)count)) != count) {
+ while ((num_wrote = _xwrite(iop, ptr, (size_t)count)) !=
+ count) {
if (num_wrote <= 0) {
if (!cancel_active())
iop->_flag |= _IOERR;
diff --git a/usr/src/lib/libc/port/stdio/fseek.c b/usr/src/lib/libc/port/stdio/fseek.c
index 55017a6584..01ef4c4c00 100644
--- a/usr/src/lib/libc/port/stdio/fseek.c
+++ b/usr/src/lib/libc/port/stdio/fseek.c
@@ -25,9 +25,7 @@
*/
/* Copyright (c) 1988 AT&T */
-/* All Rights Reserved */
-
-#pragma ident "%Z%%M% %I% %E% SMI"
+/* All Rights Reserved */
/*
* Seek for standard library. Coordinates with buffering.
@@ -76,7 +74,7 @@ fseek(FILE *iop, long offset, int ptrname)
if (iop->_flag & _IORW) {
iop->_flag &= ~(_IOREAD | _IOWRT);
}
- p = lseek(FILENO(iop), (off_t)offset, ptrname);
+ p = _xseek(iop, (off_t)offset, ptrname);
FUNLOCKFILE(lk);
return ((p == (off_t)-1) ? -1: 0);
}
diff --git a/usr/src/lib/libc/port/stdio/fseeko.c b/usr/src/lib/libc/port/stdio/fseeko.c
index b47c7a5313..2af3fdf532 100644
--- a/usr/src/lib/libc/port/stdio/fseeko.c
+++ b/usr/src/lib/libc/port/stdio/fseeko.c
@@ -25,9 +25,7 @@
*/
/* Copyright (c) 1988 AT&T */
-/* All Rights Reserved */
-
-#pragma ident "%Z%%M% %I% %E% SMI"
+/* All Rights Reserved */
/*
* Seek for standard library. Coordinates with buffering.
@@ -83,7 +81,7 @@ fseeko64(FILE *iop, off64_t offset, int ptrname)
if (iop->_flag & _IORW) {
iop->_flag &= ~(_IOREAD | _IOWRT);
}
- p = lseek64(FILENO(iop), offset, ptrname);
+ p = _xseek64(iop, offset, ptrname);
FUNLOCKFILE(lk);
return ((p == -1)? -1: 0);
}
diff --git a/usr/src/lib/libc/port/stdio/ftell.c b/usr/src/lib/libc/port/stdio/ftell.c
index 45c0b841d6..dae2abea03 100644
--- a/usr/src/lib/libc/port/stdio/ftell.c
+++ b/usr/src/lib/libc/port/stdio/ftell.c
@@ -25,9 +25,7 @@
*/
/* Copyright (c) 1988 AT&T */
-/* All Rights Reserved */
-
-#pragma ident "%Z%%M% %I% %E% SMI"
+/* All Rights Reserved */
/*
* Return file offset.
@@ -49,41 +47,63 @@
#include <sys/types.h>
#include "stdiom.h"
-long
-ftell(FILE *iop)
+off64_t
+ftell_common(FILE *iop)
{
ptrdiff_t adjust;
off64_t tres;
rmutex_t *lk;
FLOCKFILE(lk, iop);
+
+ /*
+ * If we're dealing with a memory stream, we need to flush the internal
+ * state before we try and determine the location. This is especially
+ * important for open_wmemstream() as it will have buffered bytes, but
+ * we need to convert that to wide characters before we proceed. If we
+ * have no file descriptor, then the units that the backing store are in
+ * can be arbitrary.
+ */
+ if (_get_fd(iop) == -1) {
+ (void) _fflush_u(iop);
+ }
+
if (iop->_cnt < 0)
iop->_cnt = 0;
- if (iop->_flag & _IOREAD)
+ if (iop->_flag & _IOREAD) {
adjust = (ptrdiff_t)-iop->_cnt;
- else if (iop->_flag & (_IOWRT | _IORW)) {
+ } else if (iop->_flag & (_IOWRT | _IORW)) {
adjust = 0;
if (((iop->_flag & (_IOWRT | _IONBF)) == _IOWRT) &&
- (iop->_base != 0))
+ (iop->_base != 0)) {
adjust = iop->_ptr - iop->_base;
- else if ((iop->_flag & _IORW) && (iop->_base != 0))
+ } else if ((iop->_flag & _IORW) && (iop->_base != 0)) {
adjust = (ptrdiff_t)-iop->_cnt;
+ }
} else {
errno = EBADF; /* file descriptor refers to no open file */
FUNLOCKFILE(lk);
return (EOF);
}
- tres = lseek64(FILENO(iop), 0, SEEK_CUR);
+ tres = _xseek64(iop, 0, SEEK_CUR);
if (tres >= 0)
tres += adjust;
+ FUNLOCKFILE(lk);
+ return ((long)tres);
+}
+
+long
+ftell(FILE *iop)
+{
+ off64_t tres;
+
+ tres = ftell_common(iop);
if (tres > LONG_MAX) {
errno = EOVERFLOW;
- FUNLOCKFILE(lk);
return (EOF);
}
- FUNLOCKFILE(lk);
return ((long)tres);
}
diff --git a/usr/src/lib/libc/port/stdio/ftello.c b/usr/src/lib/libc/port/stdio/ftello.c
index 2eafdf1fef..cd43037ab1 100644
--- a/usr/src/lib/libc/port/stdio/ftello.c
+++ b/usr/src/lib/libc/port/stdio/ftello.c
@@ -25,9 +25,7 @@
*/
/* Copyright (c) 1988 AT&T */
-/* All Rights Reserved */
-
-#pragma ident "%Z%%M% %I% %E% SMI"
+/* All Rights Reserved */
/*
* Return file offset.
@@ -60,30 +58,7 @@
off64_t
ftello64(FILE *iop)
{
- ptrdiff_t adjust;
- off64_t tres;
- rmutex_t *lk;
-
- FLOCKFILE(lk, iop);
- if (iop->_cnt < 0)
- iop->_cnt = 0;
- if (iop->_flag & _IOREAD)
- adjust = (ptrdiff_t)-iop->_cnt;
- else if (iop->_flag & (_IOWRT | _IORW)) {
- adjust = 0;
- if (((iop->_flag & (_IOWRT | _IONBF)) == _IOWRT) &&
- (iop->_base != 0))
- adjust = iop->_ptr - iop->_base;
- } else {
- errno = EBADF; /* file descriptor refers to no open file */
- FUNLOCKFILE(lk);
- return ((off64_t)EOF);
- }
- tres = lseek64(FILENO(iop), 0, SEEK_CUR);
- if (tres >= 0)
- tres += (off64_t)adjust;
- FUNLOCKFILE(lk);
- return (tres);
+ return (ftell_common(iop));
}
#endif /* _LP64 */
diff --git a/usr/src/lib/libc/port/stdio/fwrite.c b/usr/src/lib/libc/port/stdio/fwrite.c
index db5928f9f4..070354f24b 100644
--- a/usr/src/lib/libc/port/stdio/fwrite.c
+++ b/usr/src/lib/libc/port/stdio/fwrite.c
@@ -25,9 +25,7 @@
*/
/* Copyright (c) 1988 AT&T */
-/* All Rights Reserved */
-
-#pragma ident "%Z%%M% %I% %E% SMI"
+/* All Rights Reserved */
#include "lint.h"
#include "file64.h"
@@ -108,9 +106,9 @@ _fwrite_unlocked(const void *ptr, size_t size, size_t count, FILE *iop)
bytes = iop->_ptr - iop->_base;
data = (char *)iop->_base;
- while ((n = write(fileno(iop), data, (size_t)bytes))
- != bytes) {
- if (n == -1) {
+ while ((n = _xwrite(iop, data, (size_t)bytes)) !=
+ bytes) {
+ if (n <= 0) {
if (!cancel_active())
iop->_flag |= _IOERR;
return (0);
@@ -126,8 +124,8 @@ _fwrite_unlocked(const void *ptr, size_t size, size_t count, FILE *iop)
* written is in bytes until the return.
* Then it is divided by size to produce items.
*/
- while ((n = write(fileno(iop), dptr, s)) != s) {
- if (n == -1) {
+ while ((n = _xwrite(iop, dptr, s)) != s) {
+ if (n <= 0) {
if (!cancel_active())
iop->_flag |= _IOERR;
return (written / size);
diff --git a/usr/src/lib/libc/port/stdio/getc.c b/usr/src/lib/libc/port/stdio/getc.c
index cfcf53f347..b7a48b7f3a 100644
--- a/usr/src/lib/libc/port/stdio/getc.c
+++ b/usr/src/lib/libc/port/stdio/getc.c
@@ -25,9 +25,7 @@
*/
/* Copyright (c) 1988 AT&T */
-/* All Rights Reserved */
-
-#pragma ident "%Z%%M% %I% %E% SMI"
+/* All Rights Reserved */
#pragma weak _getc_unlocked = getc_unlocked
@@ -51,10 +49,7 @@ getc(FILE *iop)
int c;
FLOCKFILE(lk, iop);
-
- _SET_ORIENTATION_BYTE(iop);
-
- c = (--iop->_cnt < 0) ? __filbuf(iop) : *iop->_ptr++;
+ c = getc_unlocked(iop);
FUNLOCKFILE(lk);
return (c);
}
@@ -63,5 +58,12 @@ getc(FILE *iop)
int
getc_unlocked(FILE *iop)
{
+ _SET_ORIENTATION_BYTE(iop);
+ return ((--iop->_cnt < 0) ? __filbuf(iop) : *iop->_ptr++);
+}
+
+int
+_getc_internal(FILE *iop)
+{
return ((--iop->_cnt < 0) ? __filbuf(iop) : *iop->_ptr++);
}
diff --git a/usr/src/lib/libc/port/stdio/getw.c b/usr/src/lib/libc/port/stdio/getw.c
index cca7732a00..9c81e8cea9 100644
--- a/usr/src/lib/libc/port/stdio/getw.c
+++ b/usr/src/lib/libc/port/stdio/getw.c
@@ -25,9 +25,7 @@
*/
/* Copyright (c) 1988 AT&T */
-/* All Rights Reserved */
-
-#pragma ident "%Z%%M% %I% %E% SMI"
+/* All Rights Reserved */
/*
* The intent here is to provide a means to make the order of
@@ -57,7 +55,7 @@ getw(FILE *stream)
FLOCKFILE(lk, stream);
while (--i >= 0 && !(stream->_flag & (_IOERR | _IOEOF)))
- *s++ = GETC(stream);
+ *s++ = getc_unlocked(stream);
ret = ((stream->_flag & (_IOERR | _IOEOF)) ? EOF : w);
FUNLOCKFILE(lk);
return (ret);
diff --git a/usr/src/lib/libc/port/stdio/open_memstream.c b/usr/src/lib/libc/port/stdio/open_memstream.c
new file mode 100644
index 0000000000..a841f44fb5
--- /dev/null
+++ b/usr/src/lib/libc/port/stdio/open_memstream.c
@@ -0,0 +1,263 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2020 Robert Mustacchi
+ */
+
+/*
+ * Implements open_memstream(3C).
+ */
+
+#include "mtlib.h"
+#include "file64.h"
+#include <stdio.h>
+#include "stdiom.h"
+#include <errno.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/sysmacros.h>
+#include <limits.h>
+
+typedef struct memstream {
+ char *mstr_buf;
+ size_t mstr_alloc;
+ size_t mstr_pos;
+ size_t mstr_lsize;
+ char **mstr_ubufp;
+ size_t *mstr_usizep;
+} memstream_t;
+
+/*
+ * Common seek and overflow detection logic for the memory stream family of
+ * functions (open_memstream, open_wmemstream, etc.). We need to validate
+ * several things:
+ *
+ * - That the offset when applied to base doesn't cause an over or underflow.
+ * - That the resulting offset is positive (done implicitly with the above)
+ * - That the resulting offset does not exceed an off_t's maximum size.
+ * Unfortunately the kernel doesn't export an OFF_MAX value to userland, so
+ * we have to know that it will always be equivalent to the environment's
+ * long. This is designed with the assumption that in an ILP32 environment we
+ * care about an off_t and not an off64_t. In cases where an off64_t is
+ * valid, we still have to fit inside of the size_t constraints.
+ *
+ * We check for each of the cases and only perform unsigned arithmetic to verify
+ * that we have defined behavior.
+ */
+boolean_t
+memstream_seek(size_t base, off_t off, size_t max, size_t *nposp)
+{
+ size_t npos;
+
+ npos = base + (size_t)off;
+ if (off >= 0 && npos < base) {
+ return (B_FALSE);
+ }
+
+ if (off >= 0 && npos > LONG_MAX) {
+ return (B_FALSE);
+ }
+
+ if (off < 0 && npos >= base) {
+ return (B_FALSE);
+ }
+
+ if (npos > max) {
+ return (B_FALSE);
+ }
+
+ *nposp = npos;
+ return (B_TRUE);
+}
+
+int
+memstream_newsize(size_t pos, size_t alloc, size_t nbytes, size_t *nallocp)
+{
+ size_t npos = pos + nbytes + 1;
+ if (npos < pos) {
+ /*
+ * We've been asked to write a number of bytes that would result
+ * in an overflow in the position. This means the stream would
+ * need to allocate all of memory, that's impractical.
+ */
+ errno = EOVERFLOW;
+ return (-1);
+ }
+
+ /*
+ * If the new position is beyond the allocated amount, grow the array to
+ * a practical amount.
+ */
+ if (npos > alloc) {
+ size_t newalloc = P2ROUNDUP(npos, BUFSIZ);
+ if (newalloc < npos) {
+ errno = EOVERFLOW;
+ return (-1);
+ }
+ *nallocp = newalloc;
+ return (1);
+ }
+
+ return (0);
+}
+
+/*
+ * The SUSv4 spec says that this should not support reads.
+ */
+static ssize_t
+open_memstream_read(FILE *iop, char *buf, size_t nbytes)
+{
+ errno = EBADF;
+ return (-1);
+}
+
+static ssize_t
+open_memstream_write(FILE *iop, const char *buf, size_t nbytes)
+{
+ memstream_t *memp = _xdata(iop);
+ size_t newsize;
+ int ret;
+
+ /*
+ * We need to fit inside of an ssize_t, so we need to first constrain
+ * nbytes to a reasonable value.
+ */
+ nbytes = MIN(nbytes, SSIZE_MAX);
+ ret = memstream_newsize(memp->mstr_pos, memp->mstr_alloc, nbytes,
+ &newsize);
+ if (ret < 0) {
+ return (-1);
+ } else if (ret > 0) {
+ void *temp;
+ temp = recallocarray(memp->mstr_buf, memp->mstr_alloc,
+ newsize, sizeof (char));
+ if (temp == NULL) {
+ return (-1);
+ }
+ memp->mstr_buf = temp;
+ memp->mstr_alloc = newsize;
+ *memp->mstr_ubufp = temp;
+ }
+
+ (void) memcpy(&memp->mstr_buf[memp->mstr_pos], buf, nbytes);
+ memp->mstr_pos += nbytes;
+
+ if (memp->mstr_pos > memp->mstr_lsize) {
+ memp->mstr_lsize = memp->mstr_pos;
+ memp->mstr_buf[memp->mstr_pos] = '\0';
+ }
+ *memp->mstr_usizep = MIN(memp->mstr_pos, memp->mstr_lsize);
+
+ return (nbytes);
+}
+
+static off_t
+open_memstream_seek(FILE *iop, off_t off, int whence)
+{
+ memstream_t *memp = _xdata(iop);
+ size_t base, npos;
+
+ switch (whence) {
+ case SEEK_SET:
+ base = 0;
+ break;
+ case SEEK_CUR:
+ base = memp->mstr_pos;
+ break;
+ case SEEK_END:
+ base = memp->mstr_lsize;
+ break;
+ default:
+ errno = EINVAL;
+ return (-1);
+ }
+
+ if (!memstream_seek(base, off, SSIZE_MAX, &npos)) {
+ errno = EINVAL;
+ return (-1);
+ }
+ memp->mstr_pos = npos;
+ *memp->mstr_usizep = MIN(memp->mstr_pos, memp->mstr_lsize);
+
+ return ((off_t)memp->mstr_pos);
+}
+
+static int
+open_memstream_close(FILE *iop)
+{
+ memstream_t *memp = _xdata(iop);
+ free(memp);
+ _xunassoc(iop);
+ return (0);
+}
+
+FILE *
+open_memstream(char **bufp, size_t *sizep)
+{
+ int err;
+ FILE *iop;
+ memstream_t *memp;
+
+ if (bufp == NULL || sizep == NULL) {
+ errno = EINVAL;
+ return (NULL);
+ }
+
+ memp = calloc(1, sizeof (memstream_t));
+ if (memp == NULL) {
+ return (NULL);
+ }
+
+ memp->mstr_alloc = BUFSIZ;
+ memp->mstr_buf = calloc(memp->mstr_alloc, sizeof (char));
+ if (memp->mstr_buf == NULL) {
+ goto cleanup;
+ }
+ memp->mstr_buf[0] = '\0';
+ memp->mstr_pos = 0;
+ memp->mstr_lsize = 0;
+ memp->mstr_ubufp = bufp;
+ memp->mstr_usizep = sizep;
+
+ iop = _findiop();
+ if (iop == NULL) {
+ goto cleanup;
+ }
+
+#ifdef _LP64
+ iop->_flag = (iop->_flag & ~_DEF_FLAG_MASK) | _IOWRT;
+#else
+ iop->_flag = _IOWRT;
+#endif
+
+ /*
+ * Update the user pointers now, in case a call to fflush() happens
+ * immediately.
+ */
+
+ if (_xassoc(iop, open_memstream_read, open_memstream_write,
+ open_memstream_seek, open_memstream_close, memp) != 0) {
+ goto cleanup;
+ }
+ _setorientation(iop, _BYTE_MODE);
+ SET_SEEKABLE(iop);
+
+ *memp->mstr_ubufp = memp->mstr_buf;
+ *memp->mstr_usizep = MIN(memp->mstr_pos, memp->mstr_lsize);
+
+ return (iop);
+
+cleanup:
+ free(memp->mstr_buf);
+ free(memp);
+ return (NULL);
+}
diff --git a/usr/src/lib/libc/port/stdio/open_wmemstream.c b/usr/src/lib/libc/port/stdio/open_wmemstream.c
new file mode 100644
index 0000000000..d46c5e34ce
--- /dev/null
+++ b/usr/src/lib/libc/port/stdio/open_wmemstream.c
@@ -0,0 +1,225 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2020 Robert Mustacchi
+ */
+
+/*
+ * Implements open_wmemstream(3C).
+ */
+
+#include "mtlib.h"
+#include "file64.h"
+#include <stdio.h>
+#include "stdiom.h"
+#include <errno.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/sysmacros.h>
+#include <limits.h>
+#include "libc.h"
+
+typedef struct wmemstream {
+ wchar_t *wmstr_buf;
+ size_t wmstr_alloc;
+ size_t wmstr_pos;
+ size_t wmstr_lsize;
+ mbstate_t wmstr_mbs;
+ wchar_t **wmstr_ubufp;
+ size_t *wmstr_usizep;
+} wmemstream_t;
+
+#define WMEMSTREAM_MAX (SSIZE_MAX / sizeof (wchar_t))
+
+/*
+ * The SUSv4 spec says that this should not support reads.
+ */
+static ssize_t
+open_wmemstream_read(FILE *iop, char *buf, size_t nbytes)
+{
+ errno = EBADF;
+ return (-1);
+}
+
+static ssize_t
+open_wmemstream_write(FILE *iop, const char *buf, size_t nbytes)
+{
+ wmemstream_t *wmemp = _xdata(iop);
+ size_t newsize, mbscount;
+ ssize_t nwritten = 0;
+ int ret;
+
+ /*
+ * nbytes is in bytes not wide characters. However, the most
+ * pathological case from a writing perspective is using ASCII
+ * characters. Thus if we size things assuming that nbytes will all
+ * possibly be valid wchar_t values on their own, then we'll always have
+ * enough buffer space.
+ */
+ nbytes = MIN(nbytes, WMEMSTREAM_MAX);
+ ret = memstream_newsize(wmemp->wmstr_pos, wmemp->wmstr_alloc, nbytes,
+ &newsize);
+ if (ret < 0) {
+ return (-1);
+ } else if (ret > 0) {
+ void *temp;
+ temp = recallocarray(wmemp->wmstr_buf, wmemp->wmstr_alloc,
+ newsize, sizeof (wchar_t));
+ if (temp == NULL) {
+ return (-1);
+ }
+ wmemp->wmstr_buf = temp;
+ wmemp->wmstr_alloc = newsize;
+ *wmemp->wmstr_ubufp = temp;
+
+ }
+
+ while (nbytes > 0) {
+ size_t nchars;
+
+ nchars = mbrtowc_nz(&wmemp->wmstr_buf[wmemp->wmstr_pos],
+ &buf[nwritten], nbytes, &wmemp->wmstr_mbs);
+ if (nchars == (size_t)-1) {
+ if (nwritten > 0) {
+ errno = 0;
+ break;
+ } else {
+ /*
+ * Overwrite errno in this case to be EIO. Most
+ * callers of stdio routines don't expect
+ * EILSEQ and it's not documented in POSIX, so
+ * we use this instead.
+ */
+ errno = EIO;
+ return (-1);
+ }
+ } else if (nchars == (size_t)-2) {
+ nwritten += nbytes;
+ nbytes = 0;
+ } else {
+ nwritten += nchars;
+ nbytes -= nchars;
+ wmemp->wmstr_pos++;
+ }
+ }
+
+ if (wmemp->wmstr_pos > wmemp->wmstr_lsize) {
+ wmemp->wmstr_lsize = wmemp->wmstr_pos;
+ wmemp->wmstr_buf[wmemp->wmstr_pos] = L'\0';
+ }
+ *wmemp->wmstr_usizep = MIN(wmemp->wmstr_pos, wmemp->wmstr_lsize);
+ return (nwritten);
+}
+
+static off_t
+open_wmemstream_seek(FILE *iop, off_t off, int whence)
+{
+ wmemstream_t *wmemp = _xdata(iop);
+ size_t base, npos;
+
+ switch (whence) {
+ case SEEK_SET:
+ base = 0;
+ break;
+ case SEEK_CUR:
+ base = wmemp->wmstr_pos;
+ break;
+ case SEEK_END:
+ base = wmemp->wmstr_lsize;
+ break;
+ default:
+ errno = EINVAL;
+ return (-1);
+ }
+
+ if (!memstream_seek(base, off, WMEMSTREAM_MAX, &npos)) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ wmemp->wmstr_pos = npos;
+ *wmemp->wmstr_usizep = MIN(wmemp->wmstr_pos, wmemp->wmstr_lsize);
+
+ return ((off_t)wmemp->wmstr_pos);
+}
+
+static int
+open_wmemstream_close(FILE *iop)
+{
+ wmemstream_t *wmemp = _xdata(iop);
+ free(wmemp);
+ _xunassoc(iop);
+ return (0);
+}
+
+
+FILE *
+open_wmemstream(wchar_t **bufp, size_t *sizep)
+{
+ int err;
+ FILE *iop;
+ wmemstream_t *wmemp;
+
+ if (bufp == NULL || sizep == NULL) {
+ errno = EINVAL;
+ return (NULL);
+ }
+
+ wmemp = calloc(1, sizeof (wmemstream_t));
+ if (wmemp == NULL) {
+ return (NULL);
+ }
+
+ wmemp->wmstr_alloc = BUFSIZ;
+ wmemp->wmstr_buf = calloc(wmemp->wmstr_alloc, sizeof (wchar_t));
+ if (wmemp->wmstr_buf == NULL) {
+ goto cleanup;
+ }
+ wmemp->wmstr_buf[0] = L'\0';
+ wmemp->wmstr_pos = 0;
+ wmemp->wmstr_lsize = 0;
+ wmemp->wmstr_ubufp = bufp;
+ wmemp->wmstr_usizep = sizep;
+
+ iop = _findiop();
+ if (iop == NULL) {
+ goto cleanup;
+ }
+
+#ifdef _LP64
+ iop->_flag = (iop->_flag & ~_DEF_FLAG_MASK) | _IOWRT;
+#else
+ iop->_flag = _IOWRT;
+#endif
+
+ /*
+ * Update the user pointers now, in case a call to fflush() happens
+ * immediately.
+ */
+
+ if (_xassoc(iop, open_wmemstream_read, open_wmemstream_write,
+ open_wmemstream_seek, open_wmemstream_close, wmemp) != 0) {
+ goto cleanup;
+ }
+ _setorientation(iop, _WC_MODE);
+ SET_SEEKABLE(iop);
+
+ *wmemp->wmstr_ubufp = wmemp->wmstr_buf;
+ *wmemp->wmstr_usizep = MIN(wmemp->wmstr_pos, wmemp->wmstr_lsize);
+
+ return (iop);
+
+cleanup:
+ free(wmemp->wmstr_buf);
+ free(wmemp);
+ return (NULL);
+}
diff --git a/usr/src/lib/libc/port/stdio/putc.c b/usr/src/lib/libc/port/stdio/putc.c
index 05383201a3..df74a0cf89 100644
--- a/usr/src/lib/libc/port/stdio/putc.c
+++ b/usr/src/lib/libc/port/stdio/putc.c
@@ -25,9 +25,7 @@
*/
/* Copyright (c) 1988 AT&T */
-/* All Rights Reserved */
-
-#pragma ident "%Z%%M% %I% %E% SMI"
+/* All Rights Reserved */
#pragma weak _putc_unlocked = putc_unlocked
@@ -51,22 +49,24 @@ putc(int ch, FILE *iop)
int ret;
FLOCKFILE(lk, iop);
+ ret = putc_unlocked(ch, iop);
+ FUNLOCKFILE(lk);
+ return (ret);
+}
+int
+putc_unlocked(int ch, FILE *iop)
+{
_SET_ORIENTATION_BYTE(iop);
if (--iop->_cnt < 0)
- ret = __flsbuf((unsigned char) ch, iop);
- else {
- (*iop->_ptr++) = (unsigned char)ch;
- ret = (unsigned char)ch;
- }
- FUNLOCKFILE(lk);
- return (ret);
+ return (__flsbuf((unsigned char) ch, iop));
+ else
+ return (*iop->_ptr++ = (unsigned char)ch);
}
-
int
-putc_unlocked(int ch, FILE *iop)
+_putc_internal(int ch, FILE *iop)
{
if (--iop->_cnt < 0)
return (__flsbuf((unsigned char) ch, iop));
diff --git a/usr/src/lib/libc/port/stdio/putw.c b/usr/src/lib/libc/port/stdio/putw.c
index 043125e113..70848e6a06 100644
--- a/usr/src/lib/libc/port/stdio/putw.c
+++ b/usr/src/lib/libc/port/stdio/putw.c
@@ -25,9 +25,7 @@
*/
/* Copyright (c) 1988 AT&T */
-/* All Rights Reserved */
-
-#pragma ident "%Z%%M% %I% %E% SMI"
+/* All Rights Reserved */
/*
* The intent here is to provide a means to make the order of
@@ -54,7 +52,7 @@ putw(int w, FILE *stream)
rmutex_t *lk;
FLOCKFILE(lk, stream);
- while (--i >= 0 && PUTC(*s++, stream) != EOF)
+ while (--i >= 0 && putc_unlocked(*s++, stream) != EOF)
;
ret = stream->_flag & _IOERR;
FUNLOCKFILE(lk);
diff --git a/usr/src/lib/libc/port/stdio/rewind.c b/usr/src/lib/libc/port/stdio/rewind.c
index 57e4cc2540..c7da173ec3 100644
--- a/usr/src/lib/libc/port/stdio/rewind.c
+++ b/usr/src/lib/libc/port/stdio/rewind.c
@@ -25,9 +25,7 @@
*/
/* Copyright (c) 1988 AT&T */
-/* All Rights Reserved */
-
-#pragma ident "%Z%%M% %I% %E% SMI"
+/* All Rights Reserved */
#include "lint.h"
#include "file64.h"
@@ -55,7 +53,7 @@ void
_rewind_unlocked(FILE *iop)
{
(void) _fflush_u(iop);
- (void) lseek64(FILENO(iop), 0, SEEK_SET);
+ (void) _xseek64(iop, 0, SEEK_SET);
iop->_cnt = 0;
iop->_ptr = iop->_base;
iop->_flag &= ~(_IOERR | _IOEOF);
diff --git a/usr/src/lib/libc/port/stdio/setbuf.c b/usr/src/lib/libc/port/stdio/setbuf.c
index 78c4c8cc76..10eb21d5b5 100644
--- a/usr/src/lib/libc/port/stdio/setbuf.c
+++ b/usr/src/lib/libc/port/stdio/setbuf.c
@@ -25,9 +25,11 @@
*/
/* Copyright (c) 1988 AT&T */
-/* All Rights Reserved */
+/* All Rights Reserved */
-#pragma ident "%Z%%M% %I% %E% SMI"
+/*
+ * Copyright 2020 Robert Mustacchi
+ */
#include "lint.h"
#include "file64.h"
@@ -39,41 +41,37 @@
#include <synch.h>
#include "stdiom.h"
-
void
setbuf(FILE *iop, char *abuf)
{
Uchar *buf = (Uchar *)abuf;
- int fno = GET_FD(iop); /* file number */
+ int fno = _get_fd(iop); /* file number */
int size = BUFSIZ - _SMBFSZ;
Uchar *temp;
rmutex_t *lk;
FLOCKFILE(lk, iop);
- if ((iop->_base != 0) && (iop->_flag & _IOMYBUF))
+ if ((iop->_base != NULL) && (iop->_flag & _IOMYBUF))
free((char *)iop->_base - PUSHBACK);
iop->_flag &= ~(_IOMYBUF | _IONBF | _IOLBF);
- if (buf == 0) {
+ if (buf == NULL) {
iop->_flag |= _IONBF;
-#ifndef _STDIO_ALLOCATE
- if (fno < 2) {
+ if (fno == 0 || fno == 1) {
/* use special buffer for std{in,out} */
buf = (fno == 0) ? _sibuf : _sobuf;
- } else /* needed for ifndef */
-#endif
- if (fno < _NFILE) {
+ } else if (fno >= 2 && fno < _NFILE) {
buf = _smbuf[fno];
size = _SMBFSZ - PUSHBACK;
- } else
- if ((buf = (Uchar *)malloc(_SMBFSZ * sizeof (Uchar))) != 0) {
+ } else if ((buf = (Uchar *)malloc(_SMBFSZ *
+ sizeof (Uchar))) != NULL) {
iop->_flag |= _IOMYBUF;
size = _SMBFSZ - PUSHBACK;
}
} else { /* regular buffered I/O, standard buffer size */
- if (isatty(fno))
+ if (fno != -1 && isatty(fno))
iop->_flag |= _IOLBF;
}
- if (buf == 0) {
+ if (buf == NULL) {
FUNLOCKFILE(lk);
return; /* malloc() failed */
}
diff --git a/usr/src/lib/libc/port/stdio/setvbuf.c b/usr/src/lib/libc/port/stdio/setvbuf.c
index a6e63e09f7..936466f893 100644
--- a/usr/src/lib/libc/port/stdio/setvbuf.c
+++ b/usr/src/lib/libc/port/stdio/setvbuf.c
@@ -25,9 +25,7 @@
*/
/* Copyright (c) 1988 AT&T */
-/* All Rights Reserved */
-
-#pragma ident "%Z%%M% %I% %E% SMI"
+/* All Rights Reserved */
#include "lint.h"
#include "file64.h"
@@ -47,7 +45,7 @@ setvbuf(FILE *iop, char *abuf, int type, size_t size)
Uchar *temp;
int sflag = iop->_flag & _IOMYBUF;
rmutex_t *lk;
- int fd = GET_FD(iop);
+ int fd = _get_fd(iop);
FLOCKFILE(lk, iop);
iop->_flag &= ~(_IOMYBUF | _IONBF | _IOLBF);
@@ -55,17 +53,14 @@ setvbuf(FILE *iop, char *abuf, int type, size_t size)
/* note that the flags are the same as the possible values for type */
case _IONBF:
iop->_flag |= _IONBF; /* file is unbuffered */
-#ifndef _STDIO_ALLOCATE
- if (fd < 2) {
+ if (fd == 0 || fd == 1) {
/* use special buffer for std{in,out} */
buf = (fd == 0) ? _sibuf : _sobuf;
size = BUFSIZ;
- } else /* needed for ifdef */
-#endif
- if (fd < _NFILE) {
+ } else if (fd >= 2 && fd < _NFILE) {
buf = _smbuf[fd];
size = _SMBFSZ - PUSHBACK;
- } else
+ } else {
if ((buf = malloc(_SMBFSZ * sizeof (Uchar))) != NULL) {
iop->_flag |= _IOMYBUF;
size = _SMBFSZ - PUSHBACK;
@@ -73,6 +68,7 @@ setvbuf(FILE *iop, char *abuf, int type, size_t size)
FUNLOCKFILE(lk);
return (EOF);
}
+ }
break;
case _IOLBF:
case _IOFBF:
diff --git a/usr/src/lib/libc/sparc/Makefile.com b/usr/src/lib/libc/sparc/Makefile.com
index e48562f400..ee580e8513 100644
--- a/usr/src/lib/libc/sparc/Makefile.com
+++ b/usr/src/lib/libc/sparc/Makefile.com
@@ -715,6 +715,7 @@ PORTSTDIO= \
_filbuf.o \
_findbuf.o \
_flsbuf.o \
+ _stdio_flags.o \
_wrtchk.o \
clearerr.o \
ctermid.o \
@@ -730,6 +731,7 @@ PORTSTDIO= \
fileno.o \
flockf.o \
flush.o \
+ fmemopen.o \
fopen.o \
fpos.o \
fputc.o \
@@ -746,6 +748,9 @@ PORTSTDIO= \
getpass.o \
gets.o \
getw.o \
+ mse.o \
+ open_memstream.o \
+ open_wmemstream.o \
popen.o \
putc.o \
putchar.o \
@@ -761,7 +766,6 @@ PORTSTDIO= \
tmpfile.o \
tmpnam_r.o \
ungetc.o \
- mse.o \
vscanf.o \
vwscanf.o \
wscanf.o
diff --git a/usr/src/lib/libc/sparcv9/Makefile.com b/usr/src/lib/libc/sparcv9/Makefile.com
index d9bfef117f..3aacce8f61 100644
--- a/usr/src/lib/libc/sparcv9/Makefile.com
+++ b/usr/src/lib/libc/sparcv9/Makefile.com
@@ -658,6 +658,7 @@ PORTSTDIO= \
_filbuf.o \
_findbuf.o \
_flsbuf.o \
+ _stdio_flags.o \
_wrtchk.o \
clearerr.o \
ctermid.o \
@@ -673,6 +674,7 @@ PORTSTDIO= \
fileno.o \
flockf.o \
flush.o \
+ fmemopen.o \
fopen.o \
fpos.o \
fputc.o \
@@ -689,6 +691,9 @@ PORTSTDIO= \
getpass.o \
gets.o \
getw.o \
+ mse.o \
+ open_memstream.o \
+ open_wmemstream.o \
popen.o \
putc.o \
putchar.o \
@@ -704,7 +709,6 @@ PORTSTDIO= \
tmpfile.o \
tmpnam_r.o \
ungetc.o \
- mse.o \
vscanf.o \
vwscanf.o \
wscanf.o
diff --git a/usr/src/lib/libumem/common/mapfile-vers b/usr/src/lib/libumem/common/mapfile-vers
index e95e666885..c2cdadf993 100644
--- a/usr/src/lib/libumem/common/mapfile-vers
+++ b/usr/src/lib/libumem/common/mapfile-vers
@@ -49,6 +49,10 @@ LOAD_SEGMENT umem {
};
$endif
+SYMBOL_VERSION ILLUMOS_0.1 {
+ global:
+ umem_setmtbf;
+} SUNW_1.1;
SYMBOL_VERSION SUNW_1.1 {
global:
diff --git a/usr/src/lib/libumem/common/umem.c b/usr/src/lib/libumem/common/umem.c
index 7f31529852..598a45eb39 100644
--- a/usr/src/lib/libumem/common/umem.c
+++ b/usr/src/lib/libumem/common/umem.c
@@ -395,8 +395,8 @@
* umem_t that looks like:
*
* typedef struct {
- * size_t tm_size;
- * void *tm_roots[NTMEMBASE]; (Currently 16)
+ * size_t tm_size;
+ * void *tm_roots[NTMEMBASE]; (Currently 16)
* } tmem_t;
*
* Each of the roots is treated as the head of a linked list. Each entry in the
@@ -560,10 +560,10 @@
*
* o. _tmem_get_base(void)
*
- * Returns the offset from the ulwp_t (curthread) to the tmem_t structure.
- * This is a constant for all threads and is effectively a way to to do
- * ::offsetof ulwp_t ul_tmem without having to know the specifics of the
- * structure outside of libc.
+ * Returns the offset from the ulwp_t (curthread) to the tmem_t structure.
+ * This is a constant for all threads and is effectively a way to to do
+ * ::offsetof ulwp_t ul_tmem without having to know the specifics of the
+ * structure outside of libc.
*
* o. _tmem_get_nentries(void)
*
@@ -3560,3 +3560,9 @@ fail:
(void) mutex_unlock(&umem_init_lock);
return (0);
}
+
+void
+umem_setmtbf(uint32_t mtbf)
+{
+ umem_mtbf = mtbf;
+}
diff --git a/usr/src/lib/libumem/common/umem.h b/usr/src/lib/libumem/common/umem.h
index f8dc475297..62297be431 100644
--- a/usr/src/lib/libumem/common/umem.h
+++ b/usr/src/lib/libumem/common/umem.h
@@ -27,8 +27,6 @@
#ifndef _UMEM_H
#define _UMEM_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/types.h>
#include <sys/vmem.h>
#include <stdlib.h>
@@ -79,6 +77,8 @@ extern void umem_cache_free(umem_cache_t *, void *);
extern void umem_reap(void);
+extern void umem_setmtbf(uint32_t);
+
#ifdef __cplusplus
}
#endif
diff --git a/usr/src/man/man3c/Makefile b/usr/src/man/man3c/Makefile
index c1eb9bbbde..726125d3e9 100644
--- a/usr/src/man/man3c/Makefile
+++ b/usr/src/man/man3c/Makefile
@@ -140,6 +140,7 @@ MANFILES= __fbufsize.3c \
floating_to_decimal.3c \
flock.3c \
flockfile.3c \
+ fmemopen.3c \
fmtmsg.3c \
fnmatch.3c \
fopen.3c \
@@ -271,6 +272,7 @@ MANFILES= __fbufsize.3c \
newlocale.3c \
nl_langinfo.3c \
offsetof.3c \
+ open_memstream.3c \
opendir.3c \
perror.3c \
pfmt.3c \
@@ -1054,6 +1056,7 @@ MANLINKS= FD_CLR.3c \
ntohl.3c \
ntohll.3c \
ntohs.3c \
+ open_wmemstream.3c \
openlog.3c \
pclose.3c \
port_dissociate.3c \
@@ -2081,6 +2084,8 @@ freelocale.3c := LINKSRC = newlocale.3c
nl_langinfo_l.3c := LINKSRC = nl_langinfo.3c
+open_wmemstream.3c := LINKSRC = open_memstream.3c
+
fdopendir.3c := LINKSRC = opendir.3c
errno.3c := LINKSRC = perror.3c
diff --git a/usr/src/man/man3c/fclose.3c b/usr/src/man/man3c/fclose.3c
index 6d30cb6afe..3b48527eb9 100644
--- a/usr/src/man/man3c/fclose.3c
+++ b/usr/src/man/man3c/fclose.3c
@@ -44,11 +44,10 @@
.\" Portions Copyright (c) 1992, X/Open Company Limited. All Rights Reserved.
.\" Copyright (c) 2002, Sun Microsystems, Inc. All Rights Reserved.
.\"
-.TH FCLOSE 3C "Nov 1, 2003"
+.TH FCLOSE 3C "March 25, 2020"
.SH NAME
fclose \- close a stream
.SH SYNOPSIS
-.LP
.nf
#include <stdio.h>
@@ -56,7 +55,6 @@ fclose \- close a stream
.fi
.SH DESCRIPTION
-.LP
The \fBfclose()\fR function causes the stream pointed to by \fIstream\fR to be
flushed and the associated file to be closed. Any unwritten buffered data for
the stream is written to the file; any unread buffered data is discarded. The
@@ -78,11 +76,9 @@ behavior.
The \fBfclose()\fR function is performed automatically for all open files upon
calling \fBexit\fR(2).
.SH RETURN VALUES
-.LP
Upon successful completion, \fBfclose()\fR returns \fB0\fR. Otherwise, it
returns \fBEOF\fR and sets \fBerrno\fR to indicate the error.
.SH ERRORS
-.LP
The \fBfclose()\fR function will fail if:
.sp
.ne 2
@@ -133,6 +129,8 @@ The \fBfclose()\fR function was interrupted by a signal.
The process is a member of a background process group attempting to write to
its controlling terminal, \fBTOSTOP\fR is set, the process is neither ignoring
nor blocking \fBSIGTTOU\fR and the process group of the process is orphaned.
+\fIstream\fR was created by \fBopen_wmemstream\fR(3C) and a wide-character or
+byte sequence buffered is invalid in the current locale.
.RE
.sp
@@ -169,7 +167,6 @@ limits of the device.
.RE
.SH ATTRIBUTES
-.LP
See \fBattributes\fR(5) for descriptions of the following attributes:
.sp
@@ -186,7 +183,6 @@ MT-Level MT-Safe
.TE
.SH SEE ALSO
-.LP
\fBclose\fR(2), \fBexit\fR(2), \fBgetrlimit\fR(2), \fBulimit\fR(2),
\fBfcloseall\fR(3C), \fBfopen\fR(3C), \fBstdio\fR(3C),
\fBattributes\fR(5), \fBstandards\fR(5)
diff --git a/usr/src/man/man3c/ferror.3c b/usr/src/man/man3c/ferror.3c
index 97ffb7046c..19af21773a 100644
--- a/usr/src/man/man3c/ferror.3c
+++ b/usr/src/man/man3c/ferror.3c
@@ -1,73 +1,104 @@
-'\" te
-.\" Copyright 1989 AT&T Copyright (c) 2005, Sun Microsystems, Inc. All Rights Reserved
-.\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License"). You may not use this file except in compliance with the License.
-.\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing. See the License for the specific language governing permissions and limitations under the License.
-.\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE. If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH FERROR 3C "Feb 17, 2005"
-.SH NAME
-ferror, feof, clearerr, fileno \- stream status inquiries
-.SH SYNOPSIS
-.LP
-.nf
-#include <stdio.h>
-
-\fBint\fR \fBferror\fR(\fBFILE *\fR\fIstream\fR);
-.fi
-
-.LP
-.nf
-\fBint\fR \fBfeof\fR(\fBFILE *\fR\fIstream\fR);
-.fi
-
-.LP
-.nf
-\fBvoid\fR \fBclearerr\fR(\fBFILE *\fR\fIstream\fR);
-.fi
-
-.LP
-.nf
-\fBint\fR \fBfileno\fR(\fBFILE *\fR\fIstream\fR);
-.fi
-
-.SH DESCRIPTION
-.sp
-.LP
-The \fBferror()\fR function returns a non-zero value when an error has
-previously occurred reading from or writing to the named \fIstream\fR (see
-\fBIntro\fR(3)). It returns \fB0\fR otherwise.
-.sp
-.LP
-The \fBfeof()\fR function returns a non-zero value when \fBEOF\fR has
-previously been detected reading the named input \fIstream\fR. It returns
-\fB0\fR otherwise.
-.sp
-.LP
-The \fBclearerr()\fR function resets the error indicator and \fBEOF\fR
-indicator to 0 on the named \fIstream\fR.
-.sp
-.LP
-The \fBfileno()\fR function returns the integer file descriptor associated with
-the named \fIstream\fR; see \fBopen\fR(2).
-.SH ATTRIBUTES
-.sp
-.LP
-See \fBattributes\fR(5) for descriptions of the following attributes:
-.sp
-
-.sp
-.TS
-box;
-c | c
-l | l .
-ATTRIBUTE TYPE ATTRIBUTE VALUE
-_
-Interface Stability Standard
-_
-MT-Level MT-Safe
-.TE
-
-.SH SEE ALSO
-.sp
-.LP
-\fBopen\fR(2), \fBIntro\fR(3), \fBfopen\fR(3C), \fBstdio\fR(3C),
-\fBattributes\fR(5), \fBstandards\fR(5)
+.\"
+.\"
+.\" The contents of this file are subject to the terms of the
+.\" Common Development and Distribution License (the "License").
+.\" You may not use this file except in compliance with the License.
+.\"
+.\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+.\" or http://www.opensolaris.org/os/licensing.
+.\" See the License for the specific language governing permissions
+.\" and limitations under the License.
+.\"
+.\" When distributing Covered Code, include this CDDL HEADER in each
+.\" file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+.\" If applicable, add the following below this CDDL HEADER, with the
+.\" fields enclosed by brackets "[]" replaced with your own identifying
+.\" information: Portions Copyright [yyyy] [name of copyright owner]
+.\"
+.\" Copyright 1989 AT&T
+.\" Copyright (c) 2005, Sun Microsystems, Inc. All Rights Reserved
+.\" Copyright 2020 Robert Mustacchi
+.\"
+.Dd "March 25, 2020"
+.Dt FERROR 3C
+.Os
+.Sh NAME
+.Nm ferror ,
+.Nm feof ,
+.Nm clearerr ,
+.Nm fileno
+.Nd stream status inquiries
+.Sh SYNOPSIS
+.In stdio.h
+.Ft int
+.Fo ferror
+.Fa "FILE *stream"
+.Fc
+.Ft int
+.Fo feof
+.Fa "FILE *stream"
+.Fc
+.Ft void
+.Fo clearerr
+.Fa "FILE *stream"
+.Fc
+.Ft int
+.Fo fileno
+.Fa "FILE *stream"
+.Fc
+.Sh DESCRIPTION
+The
+.Fn ferror
+function returns a non-zero value when an error has
+previously occurred reading from or writing to the named
+.Fa stream
+.Po
+see
+.Xr Intro 3
+.Pc .
+It returns
+.Sy 0
+otherwise.
+.Pp
+The
+.Fn feof
+function returns a non-zero value when
+.Sy EOF
+has previously been detected reading the named input
+.Fa stream .
+It returns
+.Sy 0
+otherwise.
+.Pp
+The
+.Fn clearerr
+function resets the error indicator and
+.Sy EOF
+indicator to 0 on the named
+.Fa stream .
+.Pp
+The
+.Fn fileno
+function returns the integer file descriptor associated with
+the named
+.Fa stream ;
+see
+.Xr open 2 .
+Streams that are not associated with a file descriptor such as those
+that were created through the
+.Xr fmemopen 3C
+or
+.Xr open_memstream 3C
+functions will return
+.Sy -1 .
+.Sh INTERFACE STABILITY
+.Sy Standard
+.Sh MT-LEVEL
+.Sy MT-Safe
+.Sh SEE ALSO
+.Xr Intro 2 ,
+.Xr open 2 ,
+.Xr fmemopen 3C ,
+.Xr fopen 3C ,
+.Xr open_memstream 3C ,
+.Xr stdio 3C
diff --git a/usr/src/man/man3c/fflush.3c b/usr/src/man/man3c/fflush.3c
index 7e71eedfd7..ffadc8399e 100644
--- a/usr/src/man/man3c/fflush.3c
+++ b/usr/src/man/man3c/fflush.3c
@@ -43,11 +43,10 @@
.\" Copyright (c) 1992, X/Open Company Limited. All Rights Reserved.
.\" Portions Copyright (c) 2002, Sun Microsystems, Inc. All Rights Reserved.
.\"
-.TH FFLUSH 3C "Nov 1, 2003"
+.TH FFLUSH 3C "March 25, 2020"
.SH NAME
fflush \- flush a stream
.SH SYNOPSIS
-.LP
.nf
#include <stdio.h>
@@ -55,8 +54,6 @@ fflush \- flush a stream
.fi
.SH DESCRIPTION
-.sp
-.LP
If \fIstream\fR points to an output stream or an update stream in which the
most recent operation was not input, \fBfflush()\fR causes any unwritten data
for that stream to be written to the file, and the \fBst_ctime\fR and
@@ -78,13 +75,9 @@ on all streams for which the behavior is defined above.
An input stream, seekable or non-seekable, can be flushed by explicitly calling
\fBfflush()\fR with a non-null argument specifying that stream.
.SH RETURN VALUES
-.sp
-.LP
Upon successful completion, \fBfflush()\fR returns \fB0\fR. Otherwise, it
returns \fBEOF\fR and sets \fBerrno\fR to indicate the error.
.SH ERRORS
-.sp
-.LP
The \fBfflush()\fR function will fail if:
.sp
.ne 2
@@ -135,6 +128,8 @@ The \fBfflush()\fR function was interrupted by a signal.
The process is a member of a background process group attempting to write to
its controlling terminal, \fBTOSTOP\fR is set, the process is neither ignoring
nor blocking \fBSIGTTOU\fR, and the process group of the process is orphaned.
+\fIstream\fR was created by \fBopen_wmemstream\fR(3C) and a wide-character or
+byte sequence buffered is invalid in the current locale.
.RE
.sp
@@ -171,8 +166,6 @@ limits of the device.
.RE
.SH ATTRIBUTES
-.sp
-.LP
See \fBattributes\fR(5) for descriptions of the following attributes:
.sp
@@ -189,6 +182,4 @@ MT-Level MT-Safe
.TE
.SH SEE ALSO
-.sp
-.LP
\fBgetrlimit\fR(2), \fBulimit\fR(2), \fBattributes\fR(5), \fBstandards\fR(5)
diff --git a/usr/src/man/man3c/fmemopen.3c b/usr/src/man/man3c/fmemopen.3c
new file mode 100644
index 0000000000..20145e5617
--- /dev/null
+++ b/usr/src/man/man3c/fmemopen.3c
@@ -0,0 +1,222 @@
+.\"
+.\" This file and its contents are supplied under the terms of the
+.\" Common Development and Distribution License ("CDDL"), version 1.0.
+.\" You may only use this file in accordance with the terms of version
+.\" 1.0 of the CDDL.
+.\"
+.\" A full copy of the text of the CDDL should have accompanied this
+.\" source. A copy of the CDDL is also available via the Internet at
+.\" http://www.illumos.org/license/CDDL.
+.\"
+.\"
+.\" Copyright 2020 Robert Mustacchi
+.\"
+.Dd March 25, 2020
+.Dt FMEMOPEN 3C
+.Os
+.Sh NAME
+.Nm fmemopen
+.Nd open a memory stream
+.Sh SYNOPSIS
+.In stdio.h
+.Ft "FILE *"
+.Fo fmemopen
+.Fa "void *restrict buf"
+.Fa "size_t size"
+.Fa "const char *restrict mode"
+.Fc
+.Sh DESCRIPTION
+The
+.Fn fmemopen
+function provides a means of associating a file stream with a
+corresponding memory buffer of a fixed size.
+The resulting stream can then be used just like any other stream, and
+when done should be released by calling the
+.Xr fclose 3C
+function.
+.Pp
+The stream can either dynamically allocate memory or it can use an
+existing memory buffer.
+If
+.Fa buf
+is
+.Dv NULL ,
+then a buffer
+.Fa size
+bytes long will be allocated for the stream and initialized to zero.
+This buffer will be allocated as though a call to
+.Xr malloc 3C
+and will be freed when
+.Xr fclose 3C
+is called.
+When using this mode, the stream must be created for update
+.Po
+indicated by a
+.Sq +
+character in the
+.Fa mode
+argument
+.Pc .
+Otherwise, it is assumed that
+.Fa buf
+is at least
+.Fa size
+bytes long.
+.Pp
+The
+.Fa mode
+argument determines whether the stream is opened for read, write, or
+append.
+The
+.Fa mode
+argument accepts all the same values as
+.Xr fopen 3C .
+.Pp
+The resulting stream behaves in a similar way to a stream backed by a
+file.
+The stream can be read and written to.
+The stream is seekeable and can either be byte or wide-character
+oriented.
+A NUL byte has no special meaning when reading.
+.Pp
+The stream logically tracks three different values:
+.Bl -enum -offset indent
+.It
+The current position in the stream.
+.It
+The current size of the stream.
+.It
+The maximum size of the stream.
+.El
+.Pp
+The current position is where reads or writes take place.
+When the stream is opened for read or write
+.Pq r, r+, w, w+
+then the initial position is set to zero.
+If the stream is opened for update
+.Pq a, a+
+then the initial position is set to the first NUL byte in the buffer.
+.Pp
+The current size of the stream represents the length of the stream.
+Like a file, this starts at a specific size and then can grow over time.
+Unlike a file, where the maximum size is determined by the file system,
+the maximum size here is determined at the creation of the stream.
+.Pp
+This size is used when using
+.Dv SEEK_END
+as an argument to functions like
+.Xr fseek 3C .
+Reads cannot advance beyond the current size of the stream and
+attempting to read beyond it is considered hitting the end-of-file.
+Writes beyond the end of the current size will cause the current size to
+increase, though it cannot increase beyond the maximum size.
+.Pp
+The initial size of the stream varies.
+It is set depending on the mode and works as follows:
+.Bl -tag -width Sy -offset indent
+.It Sy r, r+
+The size is set to the
+.Fa size
+argument.
+.It Sy w, w+
+The initial size is set to zero.
+.It Sy a, a+
+The initial size is set according to the following rules:
+.Bl -enum
+.It
+If
+.Fa buf
+is a
+.Dv NULL
+pointer, the current size is set to zero.
+.It
+If a NUL byte is found in the first
+.Fa size
+bytes of
+.Fa buf ,
+then the current size is set to the first NUL byte.
+.It
+The position is set to the
+.Fa size
+argument
+.Pq the maximum size
+if no NUL byte was found in
+.Fa buf .
+.El
+.El
+.Pp
+The maximum size of the stream is determined by the
+.Fa size
+argument.
+Writes beyond this size are dropped.
+Attempts to seek beyond the maximum size will result in an error.
+.Pp
+If the stream was open for writing or update, when the stream is flushed
+or closed, a NUL byte will be written to terminate the stream based on
+the current position and size of the stream.
+If the stream was open for update, if the stream is flushed or closed
+and the last write changed the current buffer size, a NUL byte will be
+written if there is still space for it within the buffer.
+.Pp
+By default, all streams are buffered.
+This means that writes beyond the size of the memory buffer could fail,
+but not be seen until the stream is flushed or closed.
+To detect errors right away, one can explicitly disable buffering with
+the
+.Xr setvbuf 3C
+function or perform explicit buffer flushes with the
+.Xr fflush 3C
+function.
+.Sh RETURN VALUES
+Upon successful completion, the
+.Fn fmemopen
+function returns a pointer to a stream.
+Otherwise,
+.Dv NULL
+is returned and
+.Dv errno
+is set to indicate the error.
+.Sh ERRORS
+The
+.Fn fmemopen
+function will fail if:
+.Bl -tag -width Er
+.It Er EINVAL
+The value of
+.Fa mode
+is invalid.
+.Pp
+The
+.Fa size
+argument was zero.
+.Pp
+The
+.Fa buf
+argument is
+.Dv NULL
+and the
+.Fa mode
+argument does not contain a
+.Sq +
+character.
+.It Er EMFILE
+.Brq FOPEN_MAX
+streams are currently open in the calling process.
+.Pp
+.Brq STREAM_MAX
+streams are currently open in the calling process.
+.It Er ENOMEM
+The system was unable to allocate memory for the stream or its backing
+buffer.
+.El
+.Sh MT-LEVEL
+.Sy MT-Safe
+.Sh INTERFACE STABILITY
+.Sy Committed
+.Sh SEE ALSO
+.Xr fclose 3C ,
+.Xr fflush 3C ,
+.Xr fopen 3C ,
+.Xr fseek 3C ,
+.Xr malloc 3C ,
+.Xr open_memstream 3C
diff --git a/usr/src/man/man3c/fputc.3c b/usr/src/man/man3c/fputc.3c
index 11f4d8f711..2cd811282d 100644
--- a/usr/src/man/man3c/fputc.3c
+++ b/usr/src/man/man3c/fputc.3c
@@ -43,12 +43,11 @@
.\" Portions Copyright (c) 1992, X/Open Company Limited. All Rights Reserved.
.\" Copyright (c) 2003, Sun Microsystems, Inc. All Rights Reserved.
.\"
-.TH FPUTC 3C "May 13, 2017"
+.TH FPUTC 3C "March 25, 2020"
.SH NAME
fputc, putc, putc_unlocked, putchar, putchar_unlocked, putw \- put a byte on a
stream
.SH SYNOPSIS
-.LP
.nf
#include <stdio.h>
@@ -81,7 +80,6 @@ stream
.fi
.SH DESCRIPTION
-.LP
The \fBfputc()\fR function writes the byte specified by \fIc\fR (converted to
an \fBunsigned char\fR) to the output stream pointed to by \fIstream\fR, at the
position indicated by the associated file-position indicator for the stream (if
@@ -125,7 +123,6 @@ update between the successful execution of \fBputw()\fR and the next successful
completion of a call to \fBfflush\fR(3C) or \fBfclose\fR(3C) on the same stream
or a call to \fBexit\fR(3C) or \fBabort\fR(3C).
.SH RETURN VALUES
-.LP
Upon successful completion, \fBfputc()\fR, \fBputc()\fR, \fBputc_unlocked()\fR,
\fBputchar()\fR, and \fBputchar_unlocked()\fR return the value that was
written. Otherwise, these functions return EOF, the error indicator for the
@@ -140,7 +137,6 @@ sets \fBerrno\fR to indicate the error.
An unsuccessful completion will occur, for example, if the file associated with
\fIstream\fR is not open for writing or if the output file cannot grow.
.SH ERRORS
-.LP
The \fBfputc()\fR, \fBputc()\fR, \fBputc_unlocked()\fR, \fBputchar()\fR,
\fBputchar_unlocked()\fR, and \fBputw()\fR functions will fail if either the
\fIstream\fR is unbuffered or the \fIstream\fR's buffer needs to be flushed,
@@ -204,7 +200,9 @@ was transferred.
A physical I/O error has occurred, or the process is a member of a background
process group attempting to write to its controlling terminal, \fBTOSTOP\fR is
set, the process is neither ignoring nor blocking \fBSIGTTOU\fR and the process
-group of the process is orphaned. This error may also be returned under
+group of the process is orphaned. \fIstream\fR was created by
+\fBopen_wmemstream\fR(3C) and a wide-character or byte sequence buffered is
+invalid in the current locale. This error may also be returned under
implementation-dependent conditions.
.RE
@@ -252,7 +250,6 @@ capabilities of the device.
.RE
.SH USAGE
-.LP
Functions exist for the \fBputc()\fR, \fBputc_unlocked()\fR, \fBputchar()\fR,
and \fBputchar_unlocked()\fR macros. To get the function form, the macro name
must be undefined (for example, \fB#undef putc\fR).
@@ -276,7 +273,6 @@ in the context of either multibyte character streams or wide-character streams.
Application programmers are encouraged to use one of the character-based output
functions instead.
.SH ATTRIBUTES
-.LP
See \fBattributes\fR(5) for descriptions of the following attributes:
.sp
@@ -295,14 +291,12 @@ MT-Level See \fBNOTES\fR below.
.TE
.SH SEE ALSO
-.LP
\fBgetrlimit\fR(2), \fBulimit\fR(2) \fBwrite\fR(2), \fBIntro\fR(3),
\fBabort\fR(3C), \fBexit\fR(3C), \fBfclose\fR(3C), \fBferror\fR(3C),
\fBfflush\fR(3C), \fBflockfile\fR(3C), \fBprintf\fR(3C),
\fBputc\fR(3C), \fBputs\fR(3C), \fBsetbuf\fR(3C), \fBstdio\fR(3C),
\fBattributes\fR(5), \fBstandards\fR(5)
.SH NOTES
-.LP
The \fBfputc()\fR, \fBputc()\fR, \fBputchar()\fR, and \fBputw()\fR routines are
MT-Safe in multithreaded applications. The \fBputc_unlocked()\fR and
\fBputchar_unlocked()\fR routines are unsafe in multithreaded applications.
diff --git a/usr/src/man/man3c/fputwc.3c b/usr/src/man/man3c/fputwc.3c
index d4bd72f32b..0a9353c91a 100644
--- a/usr/src/man/man3c/fputwc.3c
+++ b/usr/src/man/man3c/fputwc.3c
@@ -43,11 +43,10 @@
.\" Copyright (c) 1992, X/Open Company Limited. All Rights Reserved.
.\" Portions Copyright (c) 2002, Sun Microsystems, Inc. All Rights Reserved.
.\"
-.TH FPUTWC 3C "Nov 1, 2003"
+.TH FPUTWC 3C "March 25, 2020"
.SH NAME
fputwc, putwc, putwchar \- put wide-character code on a stream
.SH SYNOPSIS
-.LP
.nf
#include <stdio.h>
#include <wchar.h>
@@ -68,8 +67,6 @@ fputwc, putwc, putwchar \- put wide-character code on a stream
.fi
.SH DESCRIPTION
-.sp
-.LP
The \fBfputwc()\fR function writes the character corresponding to the
wide-character code \fIwc\fR to the output stream pointed to by \fIstream\fR,
at the position indicated by the associated file-position indicator for the
@@ -94,14 +91,10 @@ The call \fBputwchar(\fR\fIwc\fR\fB)\fR is equivalent to
\fBputwc(\fR\fIwc,\fR\fB stdout)\fR\fI\&.\fR The \fBputwchar()\fR routine is
implemented as a macro.
.SH RETURN VALUES
-.sp
-.LP
Upon successful completion, \fBfputwc()\fR, \fBputwc()\fR, and \fBputwchar()\fR
return \fIwc\fR. Otherwise, they return \fBWEOF\fR, the error indicator for the
stream is set, and \fBerrno\fR is set to indicate the error.
.SH ERRORS
-.sp
-.LP
The \fBfputwc()\fR, \fBputwc()\fR, and \fBputwchar()\fR functions will fail if
either the stream is unbuffered or data in the \fIstream\fR's buffer needs to
be written, and:
@@ -156,7 +149,9 @@ was transferred.
A physical I/O error has occurred, or the process is a member of a background
process group attempting to write to its controlling terminal, \fBTOSTOP\fR is
set, the process is neither ignoring nor blocking \fBSIGTTOU\fR, and the
-process group of the process is orphaned.
+process group of the process is orphaned. \fIstream\fR was created by
+\fBopen_wmemstream\fR(3C) and a wide-character or byte sequence buffered is
+invalid in the current locale.
.RE
.sp
@@ -211,8 +206,6 @@ The wide-character code \fIwc\fR does not correspond to a valid character.
.RE
.SH USAGE
-.sp
-.LP
Functions exist for the \fBputwc()\fR and \fBputwchar()\fR macros. To get the
function form, the macro name must be undefined (for example, \fB#undef
putc\fR).
@@ -223,8 +216,6 @@ more than once. In particular, \fBputwc(\fR\fIwc\fR\fB, \fR\fI*f++\fR\fB)\fR
does not work sensibly. The \fBfputwc()\fR function should be used instead
when evaluating the \fIstream\fR argument has side effects.
.SH ATTRIBUTES
-.sp
-.LP
See \fBattributes\fR(5) for descriptions of the following attributes:
.sp
@@ -241,8 +232,6 @@ MT-Level MT-Safe
.TE
.SH SEE ALSO
-.sp
-.LP
\fBexit\fR(2), \fBulimit\fR(2), \fBabort\fR(3C), \fBfclose\fR(3C),
\fBferror\fR(3C), \fBfflush\fR(3C), \fBfopen\fR(3C), \fBsetbuf\fR(3C),
\fBattributes\fR(5), \fBstandards\fR(5)
diff --git a/usr/src/man/man3c/freopen.3c b/usr/src/man/man3c/freopen.3c
index bcf4124cb2..a53cd77706 100644
--- a/usr/src/man/man3c/freopen.3c
+++ b/usr/src/man/man3c/freopen.3c
@@ -43,11 +43,10 @@
.\" Portions Copyright (c) 1992, X/Open Company Limited All Rights Reserved
.\" Copyright (c) 2002, Sun Microsystems, Inc. All Rights Reserved.
.\"
-.TH FREOPEN 3C "Jul 24, 2002"
+.TH FREOPEN 3C "March 25, 2020"
.SH NAME
freopen \- open a stream
.SH SYNOPSIS
-.LP
.nf
#include <stdio.h>
@@ -55,7 +54,6 @@ freopen \- open a stream
.fi
.SH DESCRIPTION
-.LP
The \fBfreopen()\fR function first attempts to flush the stream and close any
file descriptor associated with \fIstream\fR. Failure to flush or close the
file successfully is ignored. The error and end-of-file indicators for the
@@ -71,9 +69,10 @@ pointed to by \fIfilename\fR and associates the stream pointed to by
If \fIfilename\fR is a null pointer and the application conforms to SUSv3 (see
\fBstandards\fR(5)), the \fBfreopen()\fR function attempts to change the mode
of the stream to that specified by \fImode\fR, as though the name of the file
-currently associated with the \fIstream\fR had been used. The following
-changes of mode are permitted, depending upon the access mode of the file
-descriptor underlying the stream:
+currently associated with the \fIstream\fR had been used. This behavior
+is not supported by streams that are backed by memory, such as
+\fRfmemopen\fB(3C). The following changes of mode are permitted, depending upon
+the access mode of the file descriptor underlying the stream:
.RS +4
.TP
.ie t \(bu
@@ -113,12 +112,10 @@ The largest value that can be represented correctly in an object of type
\fBoff_t\fR will be established as the offset maximum in the open file
description.
.SH RETURN VALUES
-.LP
Upon successful completion, \fBfreopen()\fR returns the value of \fIstream\fR.
Otherwise, a null pointer is returned and \fBerrno\fR is set to indicate the
error.
.SH ERRORS
-.LP
The \fBfreopen()\fR function will fail if:
.sp
.ne 2
@@ -321,7 +318,6 @@ The file is a pure procedure (shared text) file that is being executed and
.RE
.SH USAGE
-.LP
The \fBfreopen()\fR function is typically used to attach the preopened
\fIstreams\fR associated with \fBstdin\fR, \fBstdout\fR and \fBstderr\fR to
other files. By default \fBstderr\fR is unbuffered, but the use of
@@ -331,7 +327,6 @@ other files. By default \fBstderr\fR is unbuffered, but the use of
The \fBfreopen()\fR function has a transitional interface for 64-bit file
offsets. See \fBlf64\fR(5).
.SH ATTRIBUTES
-.LP
See \fBattributes\fR(5) for descriptions of the following attributes:
.sp
@@ -348,6 +343,5 @@ MT-Level MT-Safe
.TE
.SH SEE ALSO
-.LP
\fBfclose\fR(3C), \fBfdopen\fR(3C), \fBfopen\fR(3C), \fBstdio\fR(3C),
\fBattributes\fR(5), \fBlf64\fR(5), \fBstandards\fR(5)
diff --git a/usr/src/man/man3c/fseek.3c b/usr/src/man/man3c/fseek.3c
index 5c2789be00..fbcdaac52c 100644
--- a/usr/src/man/man3c/fseek.3c
+++ b/usr/src/man/man3c/fseek.3c
@@ -44,11 +44,10 @@
.\" Portions Copyright (c) 1992, X/Open Company Limited. All Rights Reserved.
.\" Copyright (c) 2003, Sun Microsystems, Inc. All Rights Reserved.
.\"
-.TH FSEEK 3C "May 13, 2017"
+.TH FSEEK 3C "March 25, 2020"
.SH NAME
fseek, fseeko \- reposition a file-position indicator in a stream
.SH SYNOPSIS
-.LP
.nf
#include <stdio.h>
@@ -61,7 +60,6 @@ fseek, fseeko \- reposition a file-position indicator in a stream
.fi
.SH DESCRIPTION
-.LP
The \fBfseek()\fR function sets the file-position indicator for the stream
pointed to by \fIstream\fR. The \fBfseeko()\fR function is identical to
\fBfseek()\fR except for the type of \fIoffset\fR.
@@ -101,7 +99,8 @@ Set position to \fBEOF\fR plus \fIoffset\fR.
.LP
If the stream is to be used with wide character input/output functions,
\fIoffset\fR must either be 0 or a value returned by an earlier call to
-\fBftell\fR(3C) on the same stream and \fIwhence\fR must be \fBSEEK_SET\fR.
+\fBftell\fR(3C) on the same stream and \fIwhence\fR must be \fBSEEK_SET\fR. This
+constraint does not hold for streams created by \fBopen_wmemstream\fR(3C).
.sp
.LP
A successful call to \fBfseek()\fR clears the end-of-file indicator for the
@@ -129,12 +128,32 @@ If the stream is writable and buffered data had not been written to the
underlying file, \fBfseek()\fR will cause the unwritten data to be written to
the file and mark the \fBst_ctime\fR and \fBst_mtime\fR fields of the file for
update.
+.SS \fBopen_wmemstream\fR(3C)
+When using a stream based on
+.B open_wmemstream\fR(3C),
+the
+.B fseek()
+and
+.B fseeko()
+functions no longer operate in terms of bytes. Instead, like the stream itself,
+the values used in
+.I offset
+are measured in units of wide characters, the underlying data unit of the
+stream. The values returned by
+.B ftell(3C)
+or
+.B ftello(3C)
+are also in these same units, allowing them to be used in the same way. These
+streams do not have the constraints of other wide character streams and may the
+full range of values in
+.I whence
+and
+.I offset,
+the same as would be done for a normal byte-oriented stream.
.SH RETURN VALUES
-.LP
The \fBfseek()\fR and \fBfseeko()\fR functions return \fB0\fR on success;
otherwise, they returned \fB\(mi1\fR and set \fBerrno\fR to indicate the error.
.SH ERRORS
-.LP
The \fBfseek()\fR and \fBfseeko()\fR functions will fail if, either the
\fIstream\fR is unbuffered or the \fIstream\fR's buffer needed to be flushed,
and the call to \fBfseek()\fR or \fBfseeko()\fR causes an underlying
@@ -269,7 +288,6 @@ correctly in an object of type \fBoff_t\fR.
.RE
.SH USAGE
-.LP
Although on the UNIX system an offset returned by \fBftell()\fR or
\fBftello()\fR (see \fBftell\fR(3C)) is measured in bytes, and it is
permissible to seek to positions relative to that offset, portability to
@@ -281,7 +299,6 @@ necessarily measured in bytes.
The \fBfseeko()\fR function has a transitional interface for 64-bit file
offsets. See \fBlf64\fR(5).
.SH ATTRIBUTES
-.LP
See \fBattributes\fR(5) for descriptions of the following attributes:
.sp
@@ -298,7 +315,6 @@ MT-Level MT-Safe
.TE
.SH SEE ALSO
-.LP
\fBgetrlimit\fR(2), \fBulimit\fR(2), \fBftell\fR(3C),
\fBrewind\fR(3C), \fBungetc\fR(3C), \fBungetwc\fR(3C), \fBattributes\fR(5),
\fBlf64\fR(5), \fBstandards\fR(5)
diff --git a/usr/src/man/man3c/ftell.3c b/usr/src/man/man3c/ftell.3c
index 82670a4feb..7228f12304 100644
--- a/usr/src/man/man3c/ftell.3c
+++ b/usr/src/man/man3c/ftell.3c
@@ -43,11 +43,10 @@
.\" Portions Copyright (c) 1992, X/Open Company Limited All Rights Reserved
.\" Copyright (c) 2002, Sun Microsystems, Inc. All Rights Reserved.
.\"
-.TH FTELL 3C "Jul 24, 2002"
+.TH FTELL 3C "March 25, 2020"
.SH NAME
ftell, ftello \- return a file offset in a stream
.SH SYNOPSIS
-.LP
.nf
#include <stdio.h>
@@ -60,21 +59,28 @@ ftell, ftello \- return a file offset in a stream
.fi
.SH DESCRIPTION
-.sp
-.LP
The \fBftell()\fR function obtains the current value of the file-position
indicator for the stream pointed to by \fIstream\fR. The \fBftello()\fR
function is identical to \fBftell()\fR except for the return type.
+.PP
+Normally the value returned is measured in bytes. However, when
+.I stream
+has been created with
+.B open_wmemstream(3C)
+then the offset of the stream is measured in wide characters. This behavior is
+consistent with how
+.B fseek(3C)
+and
+.B fseeko(3C)
+treat the offset into the stream.
.SH RETURN VALUES
-.sp
-.LP
Upon successful completion, the \fBftell()\fR and \fBftello()\fR functions
return the current value of the file-position indicator for the stream measured
-in bytes from the beginning of the file. Otherwise, they return \fB\(mi1\fR and
-sets \fBerrno\fR to indicate the error.
+in bytes from the beginning of the file, or wide characters for streams created
+by
+.B open_wmemstream(3C).
+Otherwise, they return \fB\(mi1\fR and sets \fBerrno\fR to indicate the error.
.SH ERRORS
-.sp
-.LP
The \fBftell()\fR and \fBftello()\fR functions will fail if:
.sp
.ne 2
@@ -122,13 +128,9 @@ The current file offset cannot be represented correctly in an object of type
.RE
.SH USAGE
-.sp
-.LP
The \fBftello()\fR function has a transitional interface for 64-bit file
offsets. See \fBlf64\fR(5).
.SH ATTRIBUTES
-.sp
-.LP
See \fBattributes\fR(5) for descriptions of the following attributes:
.sp
@@ -145,7 +147,5 @@ MT-Level MT-Safe
.TE
.SH SEE ALSO
-.sp
-.LP
-\fBlseek\fR(2), \fBfopen\fR(3C), \fBfseek\fR(3C), \fBattributes\fR(5), ,
-\fBstandards\fR(5), \fBlf64\fR(5)
+\fBlseek\fR(2), \fBfopen\fR(3C), \fBfseek\fR(3C), \fBopen_wmemstream\fR(3C),
+\fBattributes\fR(5), \fBstandards\fR(5), \fBlf64\fR(5)
diff --git a/usr/src/man/man3c/fwrite.3c b/usr/src/man/man3c/fwrite.3c
index 0027f2a426..92f13cd98f 100644
--- a/usr/src/man/man3c/fwrite.3c
+++ b/usr/src/man/man3c/fwrite.3c
@@ -44,11 +44,10 @@
.\" Copyright (c) 1997, The Open Group. All Rights Reserved.
.\" Portions Copyright (c) 2002, Sun Microsystems, Inc. All Rights Reserved.
.\"
-.TH FWRITE 3C "Jul 24, 2002"
+.TH FWRITE 3C "March 25, 2020"
.SH NAME
fwrite \- binary output
.SH SYNOPSIS
-.LP
.nf
#include <stdio.h>
@@ -56,8 +55,6 @@ fwrite \- binary output
.fi
.SH DESCRIPTION
-.sp
-.LP
The \fBfwrite()\fR function writes, from the array pointed to by \fIptr\fR, up
to \fInitems\fR elements whose size is specified by \fIsize\fR, to the stream
pointed to by \fIstream\fR. For each object, \fIsize\fR calls are made to the
@@ -72,28 +69,26 @@ The \fBst_ctime\fR and \fBst_mtime\fR fields of the file will be marked for
update between the successful execution of \fBfwrite()\fR and the next
successful completion of a call to \fBfflush\fR(3C) or \fBfclose\fR(3C) on the
same stream or a call to \fBexit\fR(2) or \fBabort\fR(3C).
-.SH RETURN VALUES
.sp
.LP
+When using the \fBfwrite()\fR function on a stream that was created with
+\fBopen_wmemstream\fR(3C), there are additional constraints on the
+binary data that can be written due to the nature of the stream. Please
+see \fBopen_wmemstream\fR(3C) for additional information.
+.SH RETURN VALUES
The \fBfwrite()\fR function returns the number of elements successfully
written, which might be less than \fInitems\fR if a write error is encountered.
If \fIsize\fR or \fInitems\fR is 0, \fBfwrite()\fR returns 0 and the state of
the stream remains unchanged. Otherwise, if a write error occurs, the error
indicator for the stream is set and \fBerrno\fR is set to indicate the error.
.SH ERRORS
-.sp
-.LP
Refer to \fBfputc\fR(3C).
.SH USAGE
-.sp
-.LP
Because of possible differences in element length and byte ordering, files
written using \fBfwrite()\fR are application-dependent, and possibly cannot be
read using \fBfread\fR(3C) by a different application or by the same
application on a different processor.
.SH ATTRIBUTES
-.sp
-.LP
See \fBattributes\fR(5) for descriptions of the following attributes:
.sp
@@ -110,8 +105,7 @@ MT-Level MT-Safe
.TE
.SH SEE ALSO
-.sp
-.LP
\fBwrite\fR(2), \fBfclose\fR(3C), \fBferror\fR(3C), \fBfopen\fR(3C),
-\fBfread\fR(3C), \fBgetc\fR(3C), \fBgets\fR(3C), \fBprintf\fR(3C),
-\fBputc\fR(3C), \fBputs\fR(3C), \fBattributes\fR(5), \fBstandards\fR(5)
+\fBfread\fR(3C), \fBgetc\fR(3C), \fBgets\fR(3C), \fBopen_wmemstream\fR(3C),
+\fBprintf\fR(3C), \fBputc\fR(3C), \fBputs\fR(3C), \fBattributes\fR(5),
+\fBstandards\fR(5)
diff --git a/usr/src/man/man3c/open_memstream.3c b/usr/src/man/man3c/open_memstream.3c
new file mode 100644
index 0000000000..9dc9315328
--- /dev/null
+++ b/usr/src/man/man3c/open_memstream.3c
@@ -0,0 +1,195 @@
+.\"
+.\" This file and its contents are supplied under the terms of the
+.\" Common Development and Distribution License ("CDDL"), version 1.0.
+.\" You may only use this file in accordance with the terms of version
+.\" 1.0 of the CDDL.
+.\"
+.\" A full copy of the text of the CDDL should have accompanied this
+.\" source. A copy of the CDDL is also available via the Internet at
+.\" http://www.illumos.org/license/CDDL.
+.\"
+.\"
+.\" Copyright 2020 Robert Mustacchi
+.\"
+.Dd March 25, 2020
+.Dt OPEN_MEMSTREAM 3C
+.Os
+.Sh NAME
+.Nm open_memstream ,
+.Nm open_wmemstream
+.Nd open a memory stream
+.Sh SYNOPSIS
+.In stdio.h
+.Ft "FILE *"
+.Fo open_memstream
+.Fa "char **bufp"
+.Fa "size_t *sizep"
+.Fc
+.In wchar.h
+.Ft "FILE *"
+.Fo open_wmemstream
+.Fa "wchar_t **bufp"
+.Fa "size_t *sizep"
+.Fc
+.Sh DESCRIPTION
+The
+.Fn open_memstream
+and
+.Fn open_wmemstream
+functions create an I/O stream that is backed by a dynamic memory buffer
+which will grow as needed.
+The stream is seekable and writable.
+The stream is not readable.
+The stream is byte-oriented in the case of the
+.Fn open_memstream
+function or it is wide-oriented in the case of the
+.Fn open_wmemstream
+function.
+.Pp
+Memory for the stream is dynamically allocated and reallocated as though
+a call to
+.Xr malloc 3C .
+As the stream is written to, flushed, or closed, the underlying buffer
+will be grown automatically as required.
+After the stream is flushed or closed, the
+.Fa bufp
+argument will be filled in with a pointer to the current buffer.
+The
+.Fa sizep
+argument will be filled in with the smaller of the current buffer length
+and the current position in bytes
+.Pq Fn open_memstream
+or wide characters
+.Pq Fn open_wmemstream ,
+excluding a NUL character.
+Note, because the current position is taken into account, the actual
+size of the buffer may be larger than is found in
+.Fa sizep ;
+however data should not be accessed beyond the indicated size.
+The values stored in the
+.Fa bufp
+and
+.Fa sizep
+arguments are only valid until the next write operation on the stream
+or a call to
+.Xr fclose 3C .
+.Pp
+The stream maintains both the current position and the current length of
+the stream.
+Both the initial position and length of the buffer are set to zero.
+Whenever a write at the current position exceeds the current length of the
+buffer, the current length is increased and a NUL character,
+.Sq \e0
+.Pq Fn open_memstream
+or NUL wide character,
+.Sq L\e0
+.Pq Fn open_wmemstream
+will be added to the buffer.
+If the stream is seeked beyond the current length and a subsequent write
+occurs, intervening characters will be filled with the corresponding NUL
+character for the stream.
+.Pp
+To release the stream, the caller must call the
+.Xr fclose 3C
+function.
+The corresponding dynamically allocated memory will be disassociated
+from the stream and will become owned by the caller.
+The caller must eventually release the memory buffer pointed to in the
+.Fa bufp
+argument with a call to
+.Xr free 3C .
+.Ss Fn open_wmemstream , Xr fseek 3C , Xr fsetops 3C, and Xr ftell 3C
+The specification for the
+.Fn open_wmemstream
+function causes the
+.Xr fseek 3C
+and
+.Xr ftell 3C
+families of functions to operate differently.
+Traditionally, these functions always return units in bytes, regardless
+of whether the underlying stream is byte- or wide-oriented.
+However, when used against a stream created by the
+.Fn open_wmemstream
+function these now count in terms of units of wide characters.
+While this is different from the traditional behavior, this does mean
+that the units will be the same as tracked in
+.Fa sizep .
+.Ss Fn open_wmemstream and byte-oriented functions
+Unlike other streams, streams created by
+.Fn open_wmemstream
+are not only wide-oriented, but operate in terms of the
+.Vt wchar_t
+data type.
+When normal bytes are written to the stream, an implicit multi-byte
+conversion state is maintained.
+Writing byte sequences that don't correspond to valid byte sequences in
+the locale can cause I/O errors to occur when using write functions such
+as
+.Xr fputc 3C
+or
+.Xr fwrite 3C ,
+or when buffered data is flushed through functions like
+.Xr fflush 3C
+or
+.Xr fclose 3C .
+.Pp
+The same problem can occur when explicitly using wide characters, for
+example,
+.Xr fputwc 3C ,
+if the
+wide character represents a code point that is not valid in the current
+locale.
+To avoid these errors when flushing or closing, one can disable
+buffering by passing
+.Dv _IONBF
+as the buffering type with
+.Xr setvbuf 3C .
+.Pp
+It is not recommended to change the locale of such a stream while
+writing a byte sequence that represents valid wide characters.
+The behavior of using byte-oriented functions is not standardized and
+not all systems will behave the same way.
+.Sh RETURN VALUES
+Upon successful completion, the
+.Fn open_memstream
+and
+.Fn open_wmemstream
+functions returns a pointer to a stream.
+Otherwise,
+.Dv NULL
+is returned and
+.Dv errno
+is set to indicate the error.
+.Sh ERRORS
+The
+.Fn fmemopen
+function will fail if:
+.Bl -tag -width Er
+.It Er EINVAL
+Either of the
+.Fa bufp
+or
+.Fa sizep
+arguments are
+.Dv NULL .
+.It Er EMFILE
+.Brq FOPEN_MAX
+streams are currently open in the calling process.
+.Pp
+.Brq STREAM_MAX
+streams are currently open in the calling process.
+.It Er ENOMEM
+The system was unable to allocate memory for the stream or its backing
+memory buffer.
+.El
+.Sh MT-LEVEL
+.Sy MT-Safe
+.Sh INTERFACE STABILITY
+.Sy Committed
+.Sh SEE ALSO
+.Xr fclose 3C ,
+.Xr fflush 3C ,
+.Xr fmemopen 3C ,
+.Xr free 3C ,
+.Xr malloc 3C ,
+.Xr setvbuf 3C
diff --git a/usr/src/man/man3c/stdio.3c b/usr/src/man/man3c/stdio.3c
index ce4b9d1f12..f7dce6dfa7 100644
--- a/usr/src/man/man3c/stdio.3c
+++ b/usr/src/man/man3c/stdio.3c
@@ -3,11 +3,11 @@
.\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License"). You may not use this file except in compliance with the License.
.\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing. See the License for the specific language governing permissions and limitations under the License.
.\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE. If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH STDIO 3C "May 18, 2005"
+.\" Copyright 2020 Robert Mustacchi
+.TH STDIO 3C "March 25, 2020"
.SH NAME
stdio, stdin, stdout, stderr \- standard buffered input/output package
.SH SYNOPSIS
-.LP
.nf
#include <stdio.h>
.fi
@@ -28,8 +28,6 @@ stdio, stdin, stdout, stderr \- standard buffered input/output package
.fi
.SH DESCRIPTION
-.sp
-.LP
The standard I/O functions described in section 3C of this manual constitute an
efficient, user-level \fBI/O\fR buffering scheme. The in-line macros
\fBgetc()\fR and \fBputc()\fR handle characters quickly. The macros
@@ -45,6 +43,8 @@ A file with associated buffering is called a \fIstream\fR (see \fBIntro\fR(3))
and is declared to be a pointer to a defined type \fBFILE\fR. The
\fBfopen\fR(3C) function creates certain descriptive data for a stream and
returns a pointer to designate the stream in all further transactions.
+Streams to memory may also be created through the \fBfmemopen\fR(3C),
+\fBopen_memstream\fR(3C), and \fBopen_wmemstream\fR(3C) functions.
Normally, there are three open streams with constant pointers declared in the
\fB<stdio.h>\fR header and associated with the standard open files:
.sp
@@ -113,7 +113,10 @@ a buffer intended to hold a file's pathname.
The integer constant \fBFOPEN_MAX\fR specifies the minimum number of files that
the implementation guarantees can be open simultaneously. Note that no more
than 255 files may be opened using \fBfopen()\fR, and only file descriptors 0
-through 255 can be used in a stream.
+through 255 can be used in a stream. This restriction only holds for the 32-bit
+compilation environment. The 64-bit environment may use more streams and the use
+of more than 255 may be enabled in a 32-bit environment through the use of
+\fBextendedFILE\fR(5).
.sp
.LP
The functions and constants mentioned in the entries of section 3S of this
@@ -140,8 +143,6 @@ The \fBsetbuf()\fR or \fBsetvbuf()\fR functions (both described on the
\fBsetbuf\fR(3C) manual page) may be used to change the stream's buffering
strategy.
.SS "Interactions of Other FILE-Type C Functions"
-.sp
-.LP
A single open file description can be accessed both through streams and through
file descriptors. Either a file descriptor or a stream will be called a
\fIhandle\fR on the open file description to which it refers; an open file
@@ -290,8 +291,6 @@ are followed, it is implementation defined whether, and under what conditions,
all input is seen exactly once.
.RE
.SS "Use of stdio in Multithreaded Applications"
-.sp
-.LP
All the \fBstdio\fR functions are safe unless they have the \fB_unlocked\fR
suffix. Each \fBFILE\fR pointer has its own lock to guarantee that only one
thread can access it. In the case that output needs to be synchronized, the
@@ -329,18 +328,15 @@ funlockfile(iop);
.in -2
.SH RETURN VALUES
-.sp
-.LP
Invalid stream pointers usually cause grave disorder, possibly including
program termination. Individual function descriptions describe the possible
error conditions.
.SH SEE ALSO
-.sp
-.LP
\fBclose\fR(2), \fBlseek\fR(2), \fBopen\fR(2), \fBpipe\fR(2), \fBread\fR(2),
\fBwrite\fR(2), \fBctermid\fR(3C), \fBcuserid\fR(3C), \fBfclose\fR(3C),
-\fBferror\fR(3C), \fBfopen\fR(3C), \fBfread\fR(3C), \fBfseek\fR(3C),
-\fBflockfile\fR(3C), \fBgetc\fR(3C), \fBgets\fR(3C), \fBpopen\fR(3C),
+\fBferror\fR(3C), \fBfmemopen\fR(3C), \fBfopen\fR(3C), \fBfread\fR(3C),
+\fBfseek\fR(3C), \fBflockfile\fR(3C), \fBgetc\fR(3C), \fBgets\fR(3C),
+\fBopen_memstream(3C)\fR, \fBopen_wmemstream(3C(\fR, \fBpopen\fR(3C),
\fBprintf\fR(3C), \fBputc\fR(3C), \fBputs\fR(3C), \fBscanf\fR(3C),
\fBsetbuf\fR(3C), \fBsystem\fR(3C), \fBtmpfile\fR(3C), \fBtmpnam\fR(3C),
\fBungetc\fR(3C)
diff --git a/usr/src/man/man3lib/libumem.3lib b/usr/src/man/man3lib/libumem.3lib
index 70df5a790d..a4cbd6ebbf 100644
--- a/usr/src/man/man3lib/libumem.3lib
+++ b/usr/src/man/man3lib/libumem.3lib
@@ -3,19 +3,16 @@
.\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License"). You may not use this file except in compliance with the License.
.\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing. See the License for the specific language governing permissions and limitations under the License.
.\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE. If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH LIBUMEM 3LIB "May 22, 2003"
+.TH LIBUMEM 3LIB "February 23, 2020"
.SH NAME
libumem \- object-caching memory allocation library
.SH SYNOPSIS
-.LP
.nf
cc [ \fIflag\fR... ] \fIfile\fR... \fB-lumem\fR [ \fIlibrary\fR... ]
#include <\fBumem.h\fR>
.fi
.SH DESCRIPTION
-.sp
-.LP
Functions in this library provide fast, scalable object-caching memory
allocation with multithreaded application support. In addition to the standard
\fBmalloc\fR(3C) family of functions and the more flexible
@@ -28,8 +25,6 @@ detection of memory leaks, buffer overruns, multiple frees, use of
uninitialized data, use of freed data, and many other common programming
errors. See \fBumem_debug\fR(3MALLOC).
.SH INTERFACES
-.sp
-.LP
The shared object \fBlibumem.so.1\fR provides the public interfaces defined
below. See \fBIntro\fR(3) for additional information on shared object
interfaces.
@@ -45,11 +40,11 @@ l l .
\fBumem_cache_alloc\fR \fBumem_cache_create\fR
\fBumem_cache_destroy\fR \fBumem_cache_free\fR
\fBumem_free\fR \fBumem_nofail_callback\fR
-\fBumem_zalloc\fR \fBvalloc\fR
+\fBumem_setmtbf\fR \fBumem_zalloc\fR
+\fBvalloc\fR
.TE
.SH FILES
-.sp
.ne 2
.na
\fB\fB/usr/lib/libumem.so.1\fR\fR
@@ -68,8 +63,6 @@ shared object
.RE
.SH ATTRIBUTES
-.sp
-.LP
See \fBattributes\fR(5) for descriptions of the following attributes:
.sp
@@ -86,8 +79,6 @@ MT-Level MT-Safe
.TE
.SH SEE ALSO
-.sp
-.LP
\fBIntro\fR(3), \fBmalloc\fR(3C), \fBumem_alloc\fR(3MALLOC),
\fBumem_cache_create\fR(3MALLOC), \fBumem_debug\fR(3MALLOC),
-\fBattributes\fR(5)
+\fBumem_setmtbf\fR(3MALLOC), \fBattributes\fR(5)
diff --git a/usr/src/man/man3malloc/Makefile b/usr/src/man/man3malloc/Makefile
index d39f180742..d7f3718e81 100644
--- a/usr/src/man/man3malloc/Makefile
+++ b/usr/src/man/man3malloc/Makefile
@@ -16,15 +16,16 @@
include $(SRC)/Makefile.master
-MANSECT= 3malloc
+MANSECT= 3malloc
-MANFILES= bsdmalloc.3malloc \
+MANFILES= bsdmalloc.3malloc \
malloc.3malloc \
mapmalloc.3malloc \
mtmalloc.3malloc \
umem_alloc.3malloc \
umem_cache_create.3malloc \
umem_debug.3malloc \
+ umem_setmtbf.3malloc \
watchmalloc.3malloc
MANLINKS= calloc.3malloc \
diff --git a/usr/src/man/man3malloc/umem_debug.3malloc b/usr/src/man/man3malloc/umem_debug.3malloc
index 89fda62a80..411d15e6f0 100644
--- a/usr/src/man/man3malloc/umem_debug.3malloc
+++ b/usr/src/man/man3malloc/umem_debug.3malloc
@@ -7,14 +7,12 @@
.SH NAME
umem_debug \- debugging features of the umem library
.SH SYNOPSIS
-.LP
.nf
\fBcc\fR [ \fIflag\fR\&.\|.\|. ] \fIfile\fR\&.\|.\|. \fB-lumem\fR [ \fIlibrary\fR\&.\|.\|. ]
#include <\fBumem.h\fR>
.fi
.SH DESCRIPTION
-.LP
The \fBlibumem\fR library provides debugging features that detect memory leaks,
buffer overruns, multiple frees, use of uninitialized data, use of freed data,
and many other common programming errors. The activation of the run-time
@@ -24,6 +22,13 @@ debugging features is controlled by environment variables.
When the library detects an error, it writes a description of the error to an
internal buffer that is readable with the \fB::umem_status\fR \fBmdb\fR(1)
\fIdcmd\fR and then calls \fBabort\fR(3C).
+.sp
+.LP
+When using the debugging features of the library, a program may opt into
+changing the failure rate of memory allocation. This is often useful for
+test code that would like to force memory allocation failures in a
+controlled fashion. See the \fBumem_setmtbf\fR(3MALLOC) function for
+more information.
.SH ENVIRONMENT VARIABLES
.ne 2
.na
@@ -166,7 +171,6 @@ cannot be allocated.
.RE
.SH ATTRIBUTES
-.LP
See \fBattributes\fR(5) for descriptions of the following attributes:
.sp
@@ -183,9 +187,9 @@ MT-Level MT-Safe
.TE
.SH SEE ALSO
-.LP
\fBmdb\fR(1), \fBabort\fR(3C), \fBsignal\fR(3C),
-\fBumem_cache_create\fR(3MALLOC), \fBattributes\fR(5)
+\fBumem_cache_create\fR(3MALLOC), \fBumem_setmtbf\fR(3MALLOC),
+\fBattributes\fR(5)
.sp
.LP
\fIModular Debugger Guide\fR:
@@ -193,12 +197,10 @@ MT-Level MT-Safe
.LP
https://illumos.org/books/mdb/
.SH WARNINGS
-.LP
When \fBlibumem\fR aborts the process using \fBabort\fR(3C), any existing
signal handler for \fBSIGABRT\fR is called. If the signal handler performs
allocations, undefined behavior can result.
.SH NOTES
-.LP
Some of the debugging features work only for allocations smaller than 16
kilobytes in size. Allocations larger than 16 kilobytes could have reduced
support.
diff --git a/usr/src/man/man3malloc/umem_setmtbf.3malloc b/usr/src/man/man3malloc/umem_setmtbf.3malloc
new file mode 100644
index 0000000000..5e2a8bb7db
--- /dev/null
+++ b/usr/src/man/man3malloc/umem_setmtbf.3malloc
@@ -0,0 +1,74 @@
+.\"
+.\" This file and its contents are supplied under the terms of the
+.\" Common Development and Distribution License ("CDDL"), version 1.0.
+.\" You may only use this file in accordance with the terms of version
+.\" 1.0 of the CDDL.
+.\"
+.\" A full copy of the text of the CDDL should have accompanied this
+.\" source. A copy of the CDDL is also available via the Internet at
+.\" http://www.illumos.org/license/CDDL.
+.\"
+.\"
+.\" Copyright 2020 Robert Mustacchi
+.\"
+.Dd February 23, 2020
+.Dt UMEM_SETMTBF 3MALLOC
+.Os
+.Sh NAME
+.Nm umem_setmtbf
+.Nd set umem failure threshold
+.Sh SYNOPSIS
+.Lb libumem
+.In umem.h
+.Ft void
+.Fo umem_setmtbf
+.Fa "uint_t thresh"
+.Fc
+.Sh DESCRIPTION
+The
+.Fn umem_setmtbf
+function can be used to modify the run-time behavior of the
+.Xr libumem 3LIB
+library to set a threshold to cause injected memory allocation failures.
+.Pp
+By default, no failures are injected.
+When a non-zero value is passed in
+.Fa thresh
+then error injection is enabled, if the umem debugging features
+described in
+.Xr umem_debug 3MALLOC
+are enabled through the
+.Ev UMEM_DEBUG
+environment variable.
+If a process has not enabled the debugging functionality of
+.Xr libumem 3LIB
+then no errors will be injected, no matter what
+.Fa thresh
+is set to.
+.Pp
+Passing the value
+.Sy 0
+disables error injection.
+To cause every allocation to fail, pass the value
+.Sy 1 .
+The larger the value passed, the more time that will pass between error
+injections.
+Currently, an error is injected if the current time in nanoseconds since
+boot modulus the mtbf is zero.
+Concretely:
+.Bd -literal -offset indent
+if (gethrtime() % thresh) == 0) {
+ <inject error>
+}
+.Ed
+.Pp
+This mechanism is not guaranteed over time; however, the meanings of a
+threshold of zero, one, and the general tendency of larger numbers to
+indicate less errors is.
+.Sh MT-LEVEL
+.Sy Unsafe
+.Sh INTERFACE STABILITY
+.Sy Uncommitted
+.Sh SEE ALSO
+.Xr libumem 3LIB ,
+.Xr umem_debug 3MALLOC
diff --git a/usr/src/pkg/manifests/system-library.man3c.inc b/usr/src/pkg/manifests/system-library.man3c.inc
index 3c2fa00b12..7317e92fce 100644
--- a/usr/src/pkg/manifests/system-library.man3c.inc
+++ b/usr/src/pkg/manifests/system-library.man3c.inc
@@ -136,6 +136,7 @@ file path=usr/share/man/man3c/fgetwc.3c
file path=usr/share/man/man3c/floating_to_decimal.3c
file path=usr/share/man/man3c/flock.3c
file path=usr/share/man/man3c/flockfile.3c
+file path=usr/share/man/man3c/fmemopen.3c
file path=usr/share/man/man3c/fmtmsg.3c
file path=usr/share/man/man3c/fnmatch.3c
file path=usr/share/man/man3c/fopen.3c
@@ -264,6 +265,7 @@ file path=usr/share/man/man3c/ndbm.3c
file path=usr/share/man/man3c/newlocale.3c
file path=usr/share/man/man3c/nl_langinfo.3c
file path=usr/share/man/man3c/offsetof.3c
+file path=usr/share/man/man3c/open_memstream.3c
file path=usr/share/man/man3c/opendir.3c
file path=usr/share/man/man3c/perror.3c
file path=usr/share/man/man3c/pfmt.3c
@@ -1054,6 +1056,7 @@ link path=usr/share/man/man3c/nrand48.3c target=drand48.3c
link path=usr/share/man/man3c/ntohl.3c target=byteorder.3c
link path=usr/share/man/man3c/ntohll.3c target=byteorder.3c
link path=usr/share/man/man3c/ntohs.3c target=byteorder.3c
+link path=usr/share/man/man3c/open_wmemstream.3c target=open_memstream.3c
link path=usr/share/man/man3c/openlog.3c target=syslog.3c
link path=usr/share/man/man3c/pclose.3c target=popen.3c
link path=usr/share/man/man3c/port_dissociate.3c target=port_associate.3c
diff --git a/usr/src/pkg/manifests/system-library.man3malloc.inc b/usr/src/pkg/manifests/system-library.man3malloc.inc
index 7ef761c79a..94f4e1b49d 100644
--- a/usr/src/pkg/manifests/system-library.man3malloc.inc
+++ b/usr/src/pkg/manifests/system-library.man3malloc.inc
@@ -21,6 +21,7 @@ file path=usr/share/man/man3malloc/mtmalloc.3malloc
file path=usr/share/man/man3malloc/umem_alloc.3malloc
file path=usr/share/man/man3malloc/umem_cache_create.3malloc
file path=usr/share/man/man3malloc/umem_debug.3malloc
+file path=usr/share/man/man3malloc/umem_setmtbf.3malloc
file path=usr/share/man/man3malloc/watchmalloc.3malloc
link path=usr/share/man/man3malloc/calloc.3malloc target=malloc.3malloc
link path=usr/share/man/man3malloc/free.3malloc target=malloc.3malloc
diff --git a/usr/src/pkg/manifests/system-test-libctest.mf b/usr/src/pkg/manifests/system-test-libctest.mf
index a4a2daaf43..69252d7c82 100644
--- a/usr/src/pkg/manifests/system-test-libctest.mf
+++ b/usr/src/pkg/manifests/system-test-libctest.mf
@@ -32,6 +32,7 @@ dir path=opt/libc-tests/tests/random
dir path=opt/libc-tests/tests/regex
dir path=opt/libc-tests/tests/regex/data
dir path=opt/libc-tests/tests/select
+dir path=opt/libc-tests/tests/stdio
dir path=opt/libc-tests/tests/symbols
dir path=usr group=sys
dir path=usr/lib
@@ -157,6 +158,22 @@ file path=opt/libc-tests/tests/select/select.sh mode=0555
file path=opt/libc-tests/tests/select/select_test mode=0555
file path=opt/libc-tests/tests/set_constraint_handler_s.32 mode=0555
file path=opt/libc-tests/tests/set_constraint_handler_s.64 mode=0555
+file path=opt/libc-tests/tests/stdio/fileno.32 mode=0555
+file path=opt/libc-tests/tests/stdio/fileno.64 mode=0555
+file path=opt/libc-tests/tests/stdio/fmemopentest.32 mode=0555
+file path=opt/libc-tests/tests/stdio/fmemopentest.64 mode=0555
+file path=opt/libc-tests/tests/stdio/ftell_ungetc.32 mode=0555
+file path=opt/libc-tests/tests/stdio/ftell_ungetc.64 mode=0555
+file path=opt/libc-tests/tests/stdio/memstream.32 mode=0555
+file path=opt/libc-tests/tests/stdio/memstream.64 mode=0555
+file path=opt/libc-tests/tests/stdio/memstream_reopen.32 mode=0555
+file path=opt/libc-tests/tests/stdio/memstream_reopen.64 mode=0555
+file path=opt/libc-tests/tests/stdio/open_memstreamtest.32 mode=0555
+file path=opt/libc-tests/tests/stdio/open_memstreamtest.64 mode=0555
+file path=opt/libc-tests/tests/stdio/orientation_test.32 mode=0555
+file path=opt/libc-tests/tests/stdio/orientation_test.64 mode=0555
+file path=opt/libc-tests/tests/stdio/test_mbrtowc.32 mode=0555
+file path=opt/libc-tests/tests/stdio/test_mbrtowc.64 mode=0555
file path=opt/libc-tests/tests/strcoll-strxfrm-6907.32 mode=0555
file path=opt/libc-tests/tests/strcoll-strxfrm-6907.64 mode=0555
file path=opt/libc-tests/tests/strerror mode=0555
diff --git a/usr/src/test/libc-tests/cfg/symbols/stdio_h.cfg b/usr/src/test/libc-tests/cfg/symbols/stdio_h.cfg
index a88c8be5e4..009fb49734 100644
--- a/usr/src/test/libc-tests/cfg/symbols/stdio_h.cfg
+++ b/usr/src/test/libc-tests/cfg/symbols/stdio_h.cfg
@@ -42,18 +42,28 @@ value | optopt | int | stdio.h | -ALL +XPG3+ -SUSv3+
#
# Functions
#
+func | fmemopen |\
+ FILE * |\
+ void *; size_t; const char * |\
+ stdio.h | -ALL SUSv4+
+
func | gets |\
char * |\
char * |\
stdio.h | ALL -C11
+func | open_memstream |\
+ FILE * |\
+ char **; size_t * |\
+ stdio.h | -ALL SUSv4+
+
func | printf |\
int |\
- const char * |\
+ const char * |\
stdio.h | ALL
-
+
func | rename |\
- int |\
+ int |\
const char *; const char * |\
stdio.h | ALL
diff --git a/usr/src/test/libc-tests/cfg/symbols/wchar_h.cfg b/usr/src/test/libc-tests/cfg/symbols/wchar_h.cfg
index c73e240c8f..888fe9a8ed 100644
--- a/usr/src/test/libc-tests/cfg/symbols/wchar_h.cfg
+++ b/usr/src/test/libc-tests/cfg/symbols/wchar_h.cfg
@@ -31,6 +31,11 @@ value | NULL | void * | wchar.h | ALL
#
# Functions
#
+func | open_wmemstream |\
+ FILE * |\
+ wchar_t **; size_t * |\
+ wchar.h | -ALL SUSv4+
+
func | wcpcpy |\
wchar_t * |\
wchar_t *; const wchar_t * |\
diff --git a/usr/src/test/libc-tests/runfiles/default.run b/usr/src/test/libc-tests/runfiles/default.run
index c2d08b54bc..d945eb7105 100644
--- a/usr/src/test/libc-tests/runfiles/default.run
+++ b/usr/src/test/libc-tests/runfiles/default.run
@@ -62,6 +62,23 @@ outputdir = /var/tmp/test_results
[/opt/libc-tests/tests/select/select.sh]
timeout = 600
+[/opt/libc-tests/tests/stdio/fileno.32]
+[/opt/libc-tests/tests/stdio/fileno.64]
+[/opt/libc-tests/tests/stdio/fmemopentest.32]
+[/opt/libc-tests/tests/stdio/fmemopentest.64]
+[/opt/libc-tests/tests/stdio/ftell_ungetc.32]
+[/opt/libc-tests/tests/stdio/ftell_ungetc.64]
+[/opt/libc-tests/tests/stdio/memstream.32]
+[/opt/libc-tests/tests/stdio/memstream.64]
+[/opt/libc-tests/tests/stdio/memstream_reopen.32]
+[/opt/libc-tests/tests/stdio/memstream_reopen.64]
+[/opt/libc-tests/tests/stdio/open_memstreamtest.32]
+[/opt/libc-tests/tests/stdio/open_memstreamtest.64]
+[/opt/libc-tests/tests/stdio/orientation_test.32]
+[/opt/libc-tests/tests/stdio/orientation_test.64]
+[/opt/libc-tests/tests/stdio/test_mbrtowc.32]
+[/opt/libc-tests/tests/stdio/test_mbrtowc.64]
+
[/opt/libc-tests/tests/aligned_alloc.32]
[/opt/libc-tests/tests/aligned_alloc.64]
[/opt/libc-tests/tests/c11_threads.32]
diff --git a/usr/src/test/libc-tests/tests/Makefile b/usr/src/test/libc-tests/tests/Makefile
index 093f42ebc8..a8932e21c4 100644
--- a/usr/src/test/libc-tests/tests/Makefile
+++ b/usr/src/test/libc-tests/tests/Makefile
@@ -24,6 +24,7 @@ SUBDIRS = \
random \
regex \
select \
+ stdio \
strerror \
symbols \
threads \
diff --git a/usr/src/test/libc-tests/tests/stdio/Makefile b/usr/src/test/libc-tests/tests/stdio/Makefile
new file mode 100644
index 0000000000..b94c42df30
--- /dev/null
+++ b/usr/src/test/libc-tests/tests/stdio/Makefile
@@ -0,0 +1,89 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2020 Robert Mustacchi
+#
+
+PROGS = \
+ fileno \
+ fmemopentest \
+ ftell_ungetc \
+ memstream \
+ memstream_reopen \
+ open_memstreamtest \
+ orientation_test \
+ test_mbrtowc
+
+PROGS32 = $(PROGS:%=%.32)
+PROGS64 = $(PROGS:%=%.64)
+
+ROOTOPTDIR = $(ROOT)/opt/libc-tests/tests
+ROOTOPTSTDIO = $(ROOTOPTDIR)/stdio
+ROOTOPTPROGS = $(PROGS32:%=$(ROOTOPTSTDIO)/%) \
+ $(PROGS64:%=$(ROOTOPTSTDIO)/%)
+
+include $(SRC)/cmd/Makefile.cmd
+
+CPPFLAGS += -D_REENTRANT -D__EXTENSIONS__
+
+#
+# libumem is used to interpose on all of these tests to modify the
+# behavior of libc's malloc for additional debugging. Each of the tests
+# enables umem debugging in their program text.
+#
+LDLIBS += -lumem
+LDLIBS64 += -lumem
+
+#
+# This test requires C99/xpg6 behavior. Therefore we build it explicitly
+# with c99 and XPG7 so it has access to the required functions.
+#
+memstream_reopen.32 := CPPFLAGS += -D_XOPEN_SOURCE=700
+memstream_reopen.64 := CPPFLAGS += -D_XOPEN_SOURCE=700
+memstream_reopen.32 := CSTD = $(CSTD_GNU99)
+memstream_reopen.64 := CSTD = $(CSTD_GNU99)
+
+# Third-party code
+orientation_test.32 := SMOFF += all_func_returns
+orientation_test.64 := SMOFF += all_func_returns
+
+.KEEP_STATE:
+
+install: $(ROOTOPTPROGS)
+
+all: $(PROGS32) $(PROGS64)
+
+clean:
+
+$(ROOTOPTPROGS): $(PROGS32) $(PROGS64) $(ROOTOPTSTDIO)
+
+$(ROOTOPTDIR):
+ $(INS.dir)
+
+$(ROOTOPTSTDIO): $(ROOTOPTDIR)
+ $(INS.dir)
+
+$(ROOTOPTSTDIO)/%: %
+ $(INS.file)
+
+%.64: %.c
+ $(LINK64.c) -o $@ $< $(LDLIBS64)
+ $(POST_PROCESS)
+
+%.32: %.c
+ $(LINK.c) -o $@ $< $(LDLIBS)
+ $(POST_PROCESS)
+
+clobber:
+ $(RM) $(PROGS32) $(PROGS64)
+
+FRC:
diff --git a/usr/src/test/libc-tests/tests/stdio/fileno.c b/usr/src/test/libc-tests/tests/stdio/fileno.c
new file mode 100644
index 0000000000..561b12470d
--- /dev/null
+++ b/usr/src/test/libc-tests/tests/stdio/fileno.c
@@ -0,0 +1,210 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2020 Robert Mustacchi
+ */
+
+/*
+ * Tests to verify fileno(3C) behavior. This test explicitly leaks fds and FILE
+ * structures to make it easier to verify the subsequent fd behavior works and
+ * is apparent through the FILE *.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <err.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <wchar.h>
+
+#define FNO_DUPFD 150
+
+static uint_t fno_nfails;
+static uint_t fno_ntests;
+static int fno_nextfd;
+
+const char *
+_umem_debug_init(void)
+{
+ return ("default,verbose");
+}
+
+const char *
+_umem_logging_init(void)
+{
+ return ("fail,contents");
+}
+
+
+static void
+check_file(FILE *fp, int fd, const char *msg)
+{
+ int act = fileno(fp);
+ if (act != fd) {
+ (void) printf("TEST FAILED: %s: expected fd %d, found %d\n",
+ msg, fd, act);
+ fno_nfails++;
+ } else {
+ (void) printf("TEST PASSED: %s\n", msg);
+ }
+ fno_ntests++;
+}
+
+static void
+check_open_n(int n)
+{
+ int fdbase;
+ uint_t i;
+
+ for (i = 0, fdbase = fno_nextfd; i < n; i++, fdbase++) {
+ FILE *f = fopen("/dev/null", "w+");
+ if (f == NULL) {
+ err(EXIT_FAILURE, "failed to open /dev/null");
+ }
+ check_file(f, fdbase, "Consecutive FDs");
+ }
+}
+
+static void
+check_memstream(void)
+{
+ FILE *fmem, *omem, *wmem;
+ char *buf;
+ wchar_t *wbuf;
+ size_t size;
+
+ fmem = fmemopen(NULL, 10, "w+");
+ if (fmem == NULL) {
+ err(EXIT_FAILURE, "failed to fmemopen()");
+ }
+
+ omem = open_memstream(&buf, &size);
+ if (omem == NULL) {
+ err(EXIT_FAILURE, "failed to open_memstream()");
+ }
+
+ wmem = open_wmemstream(&wbuf, &size);
+ if (wmem == NULL) {
+ err(EXIT_FAILURE, "failed to open_wmemstream()");
+ }
+
+ check_file(fmem, -1, "basic fmemopen()");
+ check_file(omem, -1, "basic open_memstream()");
+ check_file(wmem, -1, "basic open_wmemstream()");
+}
+
+static void
+check_fdopen(void)
+{
+ int fd, dupfd;
+ FILE *f;
+
+ fd = open("/dev/null", O_RDWR);
+ if (fd < 0) {
+ err(EXIT_FAILURE, "failed to open /dev/null");
+ }
+ fno_nextfd = fd + 1;
+
+ f = fdopen(fd, "r+");
+ if (f == NULL) {
+ err(EXIT_FAILURE, "failed to fdopen /dev/null");
+ }
+ check_file(f, fd, "fdopen");
+
+ if ((dupfd = dup2(fd, FNO_DUPFD)) != FNO_DUPFD) {
+ err(EXIT_FAILURE, "failed to dup2 /dev/null");
+ }
+ f = fdopen(dupfd, "r+");
+ if (f == NULL) {
+ err(EXIT_FAILURE, "failed to fdopen dup2'd /dev/null");
+ }
+ check_file(f, dupfd, "fdopen of dup2'd file");
+
+ f = freopen("/dev/zero", "r+", f);
+ if (f == NULL) {
+ err(EXIT_FAILURE, "failed to freopen dup2'd FILE *");
+ }
+ check_file(f, fno_nextfd, "freopen dup2'd FILE *");
+ fno_nextfd++;
+}
+
+static void
+check_alternate(void)
+{
+ wchar_t *c;
+ size_t s, i;
+
+ for (i = 0; i < 10; i++) {
+ FILE *f, *save;
+ f = fmemopen(NULL, 10, "a+");
+ if (f == NULL) {
+ err(EXIT_FAILURE, "failed to create fmemopen stream");
+ }
+ check_file(f, -1, "alternating memstream, fopen (fmemopen)");
+
+ save = f;
+ f = fopen("/dev/zero", "r+");
+ if (f == NULL) {
+ err(EXIT_FAILURE, "failed to open /dev/zero");
+ }
+ check_file(f, fno_nextfd, "alternating memstream, fopen "
+ "(file)");
+ fno_nextfd++;
+
+ f = open_wmemstream(&c, &s);
+ if (f == NULL) {
+ err(EXIT_FAILURE, "failed to create open_wmemstream() "
+ "stream");
+ }
+ check_file(f, -1, "alternating memstream, fopen (wmemstream)");
+
+ f = freopen("/dev/null", "r+", save);
+ if (f == NULL) {
+ err(EXIT_FAILURE, "failed to freopen /dev/null from "
+ "fmemopen()");
+ }
+ check_file(f, fno_nextfd, "alternating memstream, fopen "
+ "(reopen)");
+
+ f = freopen("/dev/zero", "a+", f);
+ check_file(f, fno_nextfd, "alternating memstream, fopen "
+ "(reopen file)");
+ fno_nextfd++;
+ }
+}
+
+int
+main(void)
+{
+ check_file(stdin, STDIN_FILENO, "default stdin fd is correct");
+ check_file(stdout, STDOUT_FILENO, "default stdout fd is correct");
+ check_file(stderr, STDERR_FILENO, "default stderr fd is correct");
+
+ /*
+ * Establish our base fd. The test runner can open files on our behalf.
+ */
+ fno_nextfd = open("/dev/null", O_RDONLY);
+ if (fno_nextfd < 0) {
+ err(EXIT_FAILURE, "failed to open /dev/null");
+ }
+ fno_nextfd++;
+ check_open_n(10);
+ fno_nextfd += 10;
+ check_memstream();
+ check_fdopen();
+ check_alternate();
+
+ printf("%d/%d tests passed\n", fno_ntests - fno_nfails, fno_ntests);
+ return (fno_nfails > 0 ? EXIT_FAILURE : EXIT_SUCCESS);
+}
diff --git a/usr/src/test/libc-tests/tests/stdio/fmemopentest.c b/usr/src/test/libc-tests/tests/stdio/fmemopentest.c
new file mode 100644
index 0000000000..c96454a10f
--- /dev/null
+++ b/usr/src/test/libc-tests/tests/stdio/fmemopentest.c
@@ -0,0 +1,222 @@
+/*
+ * Copyright (c) 2011 Martin Pieuchot <mpi@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+const char *
+_umem_debug_init(void)
+{
+ return ("default,verbose");
+}
+
+const char *
+_umem_logging_init(void)
+{
+ return ("fail,contents");
+}
+
+int
+simpletest(void)
+{
+ FILE *s1, *s2;
+ char string[] = "fmemopen test string!";
+ char buffer[1024], *buf = NULL;
+ size_t len;
+ int c, failures = 0;
+
+ s1 = fmemopen(string, strlen(string) + 1, "r");
+ if (s1 == NULL) {
+ warn("unable to open a stream s1");
+ return (1);
+ }
+
+ s2 = fmemopen(buf, 22, "w+");
+ if (s2 == NULL) {
+ warn("unable to create a stream s2");
+ (void) fclose(s1);
+ return (1);
+ }
+
+ while ((c = fgetc(s1)) != EOF)
+ (void) fputc(c, s2);
+
+ if (ftell(s2) != strlen(string) + 1) {
+ warnx("failed copy test (1)");
+ failures++;
+ }
+ (void) fclose(s1);
+
+ (void) fseek(s2, 0, SEEK_SET);
+ if (ftell(s2) != 0) {
+ warnx("failed seek test (2)");
+ failures++;
+ }
+
+ len = fread(buffer, 1, sizeof(buffer) - 1, s2);
+ if (len != strlen(string) + 1) {
+ warnx("failed read test (3) %zu != %zu",
+ len, strlen(string) + 1);
+ failures++;
+ }
+
+ return (failures);
+}
+
+int
+updatetest(void)
+{
+ FILE *s1;
+ char string[] = "hello\0 test number 2";
+ char buffer[256];
+ size_t len;
+ int failures = 0;
+
+ s1 = fmemopen(string, 19, "a+");
+ if (s1 == NULL)
+ return (1);
+
+ len = fwrite(" world", 1, 6, s1);
+ if (len != 6) {
+ warnx("failed write test (4)");
+ failures++;
+ }
+
+ (void) fseek(s1, 0, SEEK_SET);
+ if (ftell(s1) != 0) {
+ warnx("failed seek test (5)");
+ failures++;
+ }
+
+ len = fread(buffer, 1, sizeof(buffer) - 1, s1);
+ if (strncmp(string, buffer, len)) {
+ warnx("failed compare test (6)");
+ failures++;
+ }
+
+ if (strcmp(string, "hello world")) {
+ warnx("failed compare test (7)");
+ failures++;
+ }
+
+ if (strcmp(string + strlen(string) + 1, "number 2")) {
+ warnx("failed compare test (8)");
+ failures++;
+ }
+
+ return (failures);
+}
+
+int
+writetest(void)
+{
+ FILE *s1;
+ char string[] = "super test number 3";
+ char buffer[256];
+ size_t len, slen;
+ int failures = 0;
+
+ slen = strlen(string) + 1;
+
+ s1 = fmemopen(string, slen, "w");
+ if (s1 == NULL)
+ return (1);
+
+ len = fwrite("short", 1, 5, s1);
+ if (len != strlen("short")) {
+ warnx("failed write test (9)");
+ failures++;
+ }
+ (void) fclose(s1);
+
+ s1 = fmemopen(string, slen, "r");
+ if (s1 == NULL) {
+ warnx("failed open test (10)");
+ failures++;
+ }
+
+ len = fread(buffer, 1, sizeof(buffer) - 1, s1);
+ if (strncmp(string, buffer, len)) {
+ warnx("failed compare test (11)");
+ failures++;
+ }
+
+ if (strcmp(string, "short")) {
+ warnx("failed compare test (12)");
+ failures++;
+ }
+
+ if (strcmp(string + strlen(string) + 1, "test number 3")) {
+ warnx("failed compare test (13)");
+ failures++;
+ }
+
+ return (failures);
+}
+
+int
+seektest(void)
+{
+ FILE *s1;
+ char string[] = "long string for testing seek";
+ size_t slen;
+ int failures = 0;
+
+ slen = strlen(string) + 1;
+
+ s1 = fmemopen(string, slen, "r");
+ if (s1 == NULL)
+ return (1);
+
+ if (fseek(s1, 8, SEEK_SET) != 0) {
+ warnx("failed to fseek. (14)");
+ failures++;
+ }
+
+ if (ftell(s1) != 8) {
+ warnx("failed seek test. (15)");
+ failures++;
+ }
+
+ /* Try to seek backward */
+ if (fseek(s1, -1, SEEK_CUR) != 0) {
+ warnx("failed to fseek. (16)");
+ failures++;
+ }
+
+ if (ftell(s1) != 7) {
+ warnx("failed seeking backward. (17)");
+ failures++;
+ }
+
+ return (failures);
+}
+
+int
+main(void)
+{
+ int failures = 0;
+
+ failures += simpletest();
+ failures += updatetest();
+ failures += writetest();
+ failures += seektest();
+
+ return (failures);
+}
diff --git a/usr/src/test/libc-tests/tests/stdio/ftell_ungetc.c b/usr/src/test/libc-tests/tests/stdio/ftell_ungetc.c
new file mode 100644
index 0000000000..9ecfc76ed2
--- /dev/null
+++ b/usr/src/test/libc-tests/tests/stdio/ftell_ungetc.c
@@ -0,0 +1,86 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2020 Robert Mustacchi
+ */
+
+/*
+ * Regression test for illumos#12392. Here, ftello64 didn't correctly handle an
+ * ungetc() in the write path when it was unbuffered.
+ */
+
+#include <stdio.h>
+#include <err.h>
+#include <stdlib.h>
+
+static void
+check_pos(FILE *f, long pos)
+{
+ long l;
+ off_t off;
+ off64_t off64;
+
+ l = ftell(f);
+ off = ftello(f);
+ off64 = ftello64(f);
+
+ if (l != pos) {
+ errx(EXIT_FAILURE, "ftell position mismatched: found %ld, "
+ "expected %ld", l, pos);
+ }
+
+ if (off != pos) {
+ errx(EXIT_FAILURE, "ftello position mismatched: found %ld, "
+ "expected %ld", off, pos);
+ }
+
+ if (off64 != pos) {
+ errx(EXIT_FAILURE, "ftello64 position mismatched: found %ld, "
+ "expected %ld", off64, pos);
+ }
+}
+
+static void
+check_one(FILE *f)
+{
+ if (fputc('a', f) != 'a') {
+ err(EXIT_FAILURE, "failed to write character");
+ }
+ check_pos(f, 1);
+
+ if (ungetc('b', f) != 'b') {
+ err(EXIT_FAILURE, "failed to unget character");
+ }
+ check_pos(f, 0);
+}
+
+int
+main(void)
+{
+ FILE *f;
+
+ f = tmpfile();
+ if (f == NULL) {
+ err(EXIT_FAILURE, "failed to create temproary "
+ "file");
+ }
+
+ if (setvbuf(f, NULL, _IONBF, 0) != 0) {
+ err(EXIT_FAILURE, "failed to set non-buffering mode");
+ }
+ check_one(f);
+ if (fclose(f) != 0) {
+ err(EXIT_FAILURE, "failed to close temporary file");
+ }
+
+ return (0);
+}
diff --git a/usr/src/test/libc-tests/tests/stdio/memstream.c b/usr/src/test/libc-tests/tests/stdio/memstream.c
new file mode 100644
index 0000000000..5a6161a92d
--- /dev/null
+++ b/usr/src/test/libc-tests/tests/stdio/memstream.c
@@ -0,0 +1,2108 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2020 Robert Mustacchi
+ */
+
+/*
+ * Test memory based streams: opem_memstream(3C), open_wmemstream(3C), and
+ * fmemopen(3C).
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/sysmacros.h>
+#include <strings.h>
+#include <err.h>
+#include <errno.h>
+#include <wchar.h>
+#include <umem.h>
+#include <locale.h>
+
+typedef boolean_t (*memstream_test_f)(void);
+static char *fmemopen_str1 = "The Road goes ever on and on\n"
+ "Down from the door where it began.\n";
+const wchar_t *wstream_str = L"いつか終わる夢";
+/*
+ * smatch doesn't support wide-character constants (wchar_t foo = L'xxx'), so
+ * instead use a string which it'll happily accept.
+ */
+const wchar_t *wstr_const = L"光";
+
+const char *
+_umem_debug_init(void)
+{
+ return ("default,verbose");
+}
+
+const char *
+_umem_logging_init(void)
+{
+ return ("fail,contents");
+}
+
+static boolean_t
+fmemopen_badopen(void *buf, size_t size, const char *mode, int err)
+{
+ FILE *f = fmemopen(buf, size, mode);
+
+ if (f != NULL) {
+ warnx("fmemopen() succeeded erroneously");
+ (void) fclose(f);
+ return (B_FALSE);
+ }
+
+ if (errno != err) {
+ warnx("fmemopen() open failed with wrong errno, "
+ "found %d (%s), expected %d (%s)", errno, strerror(errno),
+ err, strerror(err));
+ return (B_FALSE);
+ }
+
+ return (B_TRUE);
+}
+
+static boolean_t
+fmemopen_badmode(void)
+{
+ return (fmemopen_badopen(fmemopen_str1, strlen(fmemopen_str1), "foobar",
+ EINVAL));
+}
+
+static boolean_t
+fmemopen_zerobuf1(void)
+{
+ return (fmemopen_badopen(fmemopen_str1, 0, "w", EINVAL));
+}
+
+static boolean_t
+fmemopen_zerobuf2(void)
+{
+ return (fmemopen_badopen(NULL, 0, "w+", EINVAL));
+}
+
+static boolean_t
+fmemopen_nullbuf1(void)
+{
+ return (fmemopen_badopen(NULL, 10, "r", EINVAL));
+}
+
+static boolean_t
+fmemopen_nullbuf2(void)
+{
+ return (fmemopen_badopen(NULL, 10, "w", EINVAL));
+}
+
+static boolean_t
+fmemopen_nullbuf3(void)
+{
+ return (fmemopen_badopen(NULL, 10, "a", EINVAL));
+}
+
+static boolean_t
+fmemopen_nullbuf4(void)
+{
+ return (fmemopen_badopen(NULL, 10, "ax", EINVAL));
+}
+
+static boolean_t
+fmemopen_sizemax(void)
+{
+ return (fmemopen_badopen(NULL, SIZE_MAX, "w+", ENOMEM));
+}
+
+static boolean_t
+fmemopen_cantalloc(void)
+{
+ boolean_t ret;
+
+ umem_setmtbf(1);
+ ret = fmemopen_badopen(NULL, 10, "w+", ENOMEM);
+ umem_setmtbf(0);
+ return (ret);
+}
+
+static boolean_t
+open_memstream_badopen(char **bufp, size_t *sizep, int err)
+{
+ FILE *f = open_memstream(bufp, sizep);
+
+ if (f != NULL) {
+ warnx("open_memstream() succeeded erroneously");
+ (void) fclose(f);
+ return (B_FALSE);
+ }
+
+ if (errno != err) {
+ warnx("open_memstream() open failed with wrong errno, "
+ "found %d (%s), expected %d (%s)", errno, strerror(errno),
+ err, strerror(err));
+ return (B_FALSE);
+ }
+
+ return (B_TRUE);
+}
+
+static boolean_t
+open_memstream_badbuf(void)
+{
+ size_t s, check;
+ boolean_t ret;
+
+ arc4random_buf(&s, sizeof (s));
+ check = s;
+ ret = open_memstream_badopen(NULL, &s, EINVAL);
+ if (check != s) {
+ warnx("open_memstream() open erroneously wrote to size "
+ "pointer");
+ return (B_FALSE);
+ }
+ return (ret);
+}
+
+static boolean_t
+open_memstream_badsize(void)
+{
+ char *c;
+ return (open_memstream_badopen(&c, NULL, EINVAL));
+}
+
+static boolean_t
+open_memstream_allnull(void)
+{
+ return (open_memstream_badopen(NULL, NULL, EINVAL));
+}
+
+static boolean_t
+open_memstream_cantalloc(void)
+{
+ boolean_t ret;
+ char *c;
+ size_t len;
+
+ umem_setmtbf(1);
+ ret = open_memstream_badopen(&c, &len, EAGAIN);
+ umem_setmtbf(0);
+ return (ret);
+}
+
+static boolean_t
+open_wmemstream_badopen(wchar_t **bufp, size_t *sizep, int err)
+{
+ FILE *f = open_wmemstream(bufp, sizep);
+
+ if (f != NULL) {
+ warnx("open_wmemstream() succeeded erroneously");
+ (void) fclose(f);
+ return (B_FALSE);
+ }
+
+ if (errno != err) {
+ warnx("open_wmemstream() open failed with wrong errno, "
+ "found %d (%s), expected %d (%s)", errno, strerror(errno),
+ err, strerror(err));
+ return (B_FALSE);
+ }
+
+ return (B_TRUE);
+}
+
+static boolean_t
+open_wmemstream_badbuf(void)
+{
+ size_t s, check;
+ boolean_t ret;
+
+ arc4random_buf(&s, sizeof (s));
+ check = s;
+ ret = open_wmemstream_badopen(NULL, &s, EINVAL);
+ if (check != s) {
+ warnx("open_wmemstream() open erroneously wrote to size "
+ "pointer");
+ return (B_FALSE);
+ }
+ return (ret);
+}
+
+static boolean_t
+open_wmemstream_badsize(void)
+{
+ wchar_t *c;
+ return (open_wmemstream_badopen(&c, NULL, EINVAL));
+}
+
+static boolean_t
+open_wmemstream_allnull(void)
+{
+ return (open_wmemstream_badopen(NULL, NULL, EINVAL));
+}
+
+static boolean_t
+open_wmemstream_cantalloc(void)
+{
+ boolean_t ret;
+ wchar_t *c;
+ size_t len;
+
+ umem_setmtbf(1);
+ ret = open_wmemstream_badopen(&c, &len, EAGAIN);
+ umem_setmtbf(0);
+ return (ret);
+}
+
+static boolean_t
+fmemopen_fill_putc(FILE *f, size_t len, boolean_t buffer)
+{
+ boolean_t ret = B_TRUE;
+ size_t i;
+
+ for (i = 0; i < BUFSIZ * 2; i++) {
+ if (fputc('a', f) != 'a') {
+ break;
+ }
+ }
+
+ if (buffer) {
+ if (i < len) {
+ warnx("write mismatch, had %zu bytes, wrote %zu",
+ len, i);
+ ret = B_FALSE;
+ }
+
+ if (fflush(f) == 0) {
+ warnx("somehow flushed overly full stream, expected "
+ "failure");
+ ret = B_FALSE;
+ }
+ } else if (i != len) {
+ warnx("write mismatch, had %zu bytes, wrote %zu", len, i);
+ ret = B_FALSE;
+ }
+
+ if (feof(f) != 0) {
+ warn("EOF mistakenly set on write");
+ ret = B_FALSE;
+ }
+
+ if (ferror(f) == 0) {
+ warn("feof not set on write past the end");
+ ret = B_FALSE;
+ }
+
+ if (fclose(f) != 0) {
+ warn("failed to close memory stream");
+ return (B_FALSE);
+ }
+
+ return (ret);
+}
+
+static boolean_t
+fmemopen_fill_fwrite(FILE *f, size_t len, boolean_t buffer)
+{
+ boolean_t ret = B_TRUE;
+ size_t i;
+ char buf[BUFSIZ];
+
+ (void) memset(buf, 'a', sizeof (buf));
+ i = fwrite(buf, sizeof (buf), 1, f);
+
+ if (buffer) {
+ if (i != 1) {
+ warnx("write mismatch, expected 1 entry, found %zu", i);
+ ret = B_FALSE;
+ }
+
+ if (fflush(f) == 0) {
+ warnx("somehow flushed overly full stream, expected "
+ "failure");
+ ret = B_FALSE;
+ }
+ } else if (i != 0 && i != len) {
+ warnx("write mismatch, had %zu bytes, wrote %zu", len, i);
+ ret = B_FALSE;
+ }
+
+ if (feof(f) != 0) {
+ warn("EOF mistakenly set on write");
+ ret = B_FALSE;
+ }
+
+ if (ferror(f) == 0) {
+ warn("feof not set on write past the end");
+ ret = B_FALSE;
+ }
+
+ if (fclose(f) != 0) {
+ warn("failed to close memory stream");
+ return (B_FALSE);
+ }
+
+ return (ret);
+}
+
+static boolean_t
+fmemopen_fill_alt_fwrite(FILE *f, size_t len, boolean_t buffer)
+{
+ boolean_t ret = B_TRUE;
+ size_t i;
+ char buf[BUFSIZ];
+
+ (void) memset(buf, 'a', sizeof (buf));
+ i = fwrite(buf, 1, sizeof (buf), f);
+
+ if (buffer) {
+ if (i < len) {
+ warnx("write mismatch, had %zu bytes, wrote %zu",
+ len, i);
+ ret = B_FALSE;
+ }
+
+ if (fflush(f) == 0) {
+ warnx("somehow flushed overly full stream, expected "
+ "failure");
+ ret = B_FALSE;
+ }
+ } else if (i != len) {
+ warnx("write mismatch, had %zu bytes, wrote %zu", len, i);
+ ret = B_FALSE;
+ }
+
+ if (feof(f) != 0) {
+ warn("EOF mistakenly set on write");
+ ret = B_FALSE;
+ }
+
+ if (ferror(f) == 0) {
+ warn("feof not set on write past the end");
+ ret = B_FALSE;
+ }
+
+ if (fclose(f) != 0) {
+ warn("failed to close memory stream");
+ return (B_FALSE);
+ }
+
+ return (ret);
+}
+
+static boolean_t
+fmemopen_fill_fputs(FILE *f, size_t len, boolean_t buffer)
+{
+ boolean_t ret = B_TRUE;
+ size_t i;
+ char buf[17];
+
+ (void) memset(buf, 'a', sizeof (buf));
+ buf[16] = '\0';
+ for (i = 0; i < BUFSIZ * 2; i += 16) {
+ if (fputs(buf, f) != 16) {
+ break;
+ }
+ }
+
+ /*
+ * We don't check flushing in the puts case because fputs seems to clear
+ * the buffer as a side effect.
+ */
+ if (buffer) {
+ if (i < len) {
+ warnx("write mismatch, had %zu bytes, wrote %zu",
+ len, i);
+ ret = B_FALSE;
+ }
+ } else if (i != len) {
+ warnx("write mismatch, had %zu bytes, wrote %zu", len, i);
+ ret = B_FALSE;
+ }
+
+ if (feof(f) != 0) {
+ warn("EOF mistakenly set on write");
+ ret = B_FALSE;
+ }
+
+ if (ferror(f) == 0) {
+ warn("feof not set on write past the end");
+ ret = B_FALSE;
+ }
+
+ if (fclose(f) != 0) {
+ warn("failed to close memory stream");
+ return (B_FALSE);
+ }
+
+ return (ret);
+}
+
+
+static boolean_t
+fmemopen_fill_default(void)
+{
+ FILE *f;
+
+ f = fmemopen(NULL, 128, "w+");
+ if (f == NULL) {
+ warn("failed to open fmemopen stream");
+ return (B_FALSE);
+ }
+
+ return (fmemopen_fill_putc(f, 128, B_TRUE));
+}
+
+static boolean_t
+fmemopen_fill_lbuf(void)
+{
+ FILE *f;
+
+ f = fmemopen(NULL, 128, "w+");
+ if (f == NULL) {
+ warn("failed to open fmemopen stream");
+ return (B_FALSE);
+ }
+
+ if (setvbuf(f, NULL, _IOLBF, BUFSIZ) != 0) {
+ warn("failed to set buffer to line-buffered mode");
+ }
+
+ return (fmemopen_fill_putc(f, 128, B_TRUE));
+}
+
+static boolean_t
+fmemopen_fill_nobuf(void)
+{
+ FILE *f;
+
+ f = fmemopen(NULL, 128, "w+");
+ if (f == NULL) {
+ warn("failed to open fmemopen stream");
+ return (B_FALSE);
+ }
+
+ if (setvbuf(f, NULL, _IONBF, 0) != 0) {
+ warn("failed to set buffer to non-buffered mode");
+ }
+
+ return (fmemopen_fill_putc(f, 128, B_FALSE));
+}
+
+static boolean_t
+fmemopen_fwrite_default(void)
+{
+ FILE *f;
+
+ f = fmemopen(NULL, 128, "w+");
+ if (f == NULL) {
+ warn("failed to open fmemopen stream");
+ return (B_FALSE);
+ }
+
+ return (fmemopen_fill_fwrite(f, 128, B_TRUE));
+}
+
+static boolean_t
+fmemopen_fwrite_lbuf(void)
+{
+ FILE *f;
+
+ f = fmemopen(NULL, 128, "w+");
+ if (f == NULL) {
+ warn("failed to open fmemopen stream");
+ return (B_FALSE);
+ }
+
+ if (setvbuf(f, NULL, _IOLBF, BUFSIZ) != 0) {
+ warn("failed to set buffer to line-buffered mode");
+ }
+
+ return (fmemopen_fill_fwrite(f, 128, B_TRUE));
+}
+
+static boolean_t
+fmemopen_fwrite_nobuf(void)
+{
+ FILE *f;
+
+ f = fmemopen(NULL, 128, "w+");
+ if (f == NULL) {
+ warn("failed to open fmemopen stream");
+ return (B_FALSE);
+ }
+
+ if (setvbuf(f, NULL, _IONBF, 0) != 0) {
+ warn("failed to set buffer to non-buffered mode");
+ }
+
+ return (fmemopen_fill_fwrite(f, 128, B_FALSE));
+}
+
+static boolean_t
+fmemopen_alt_fwrite_default(void)
+{
+ FILE *f;
+
+ f = fmemopen(NULL, 128, "w+");
+ if (f == NULL) {
+ warn("failed to open fmemopen stream");
+ return (B_FALSE);
+ }
+
+ return (fmemopen_fill_alt_fwrite(f, 128, B_TRUE));
+}
+
+static boolean_t
+fmemopen_alt_fwrite_lbuf(void)
+{
+ FILE *f;
+
+ f = fmemopen(NULL, 128, "w+");
+ if (f == NULL) {
+ warn("failed to open fmemopen stream");
+ return (B_FALSE);
+ }
+
+ if (setvbuf(f, NULL, _IOLBF, BUFSIZ) != 0) {
+ warn("failed to set buffer to line-buffered mode");
+ }
+
+ return (fmemopen_fill_alt_fwrite(f, 128, B_TRUE));
+}
+
+static boolean_t
+fmemopen_alt_fwrite_nobuf(void)
+{
+ FILE *f;
+
+ f = fmemopen(NULL, 128, "w+");
+ if (f == NULL) {
+ warn("failed to open fmemopen stream");
+ return (B_FALSE);
+ }
+
+ if (setvbuf(f, NULL, _IONBF, 0) != 0) {
+ warn("failed to set buffer to non-buffered mode");
+ }
+
+ return (fmemopen_fill_alt_fwrite(f, 128, B_FALSE));
+}
+
+static boolean_t
+fmemopen_fputs_default(void)
+{
+ FILE *f;
+
+ f = fmemopen(NULL, 128, "w+");
+ if (f == NULL) {
+ warn("failed to open fmemopen stream");
+ return (B_FALSE);
+ }
+
+ return (fmemopen_fill_fputs(f, 128, B_TRUE));
+}
+
+static boolean_t
+fmemopen_fputs_lbuf(void)
+{
+ FILE *f;
+
+ f = fmemopen(NULL, 128, "w+");
+ if (f == NULL) {
+ warn("failed to open fmemopen stream");
+ return (B_FALSE);
+ }
+
+ if (setvbuf(f, NULL, _IOLBF, BUFSIZ) != 0) {
+ warn("failed to set buffer to line-buffered mode");
+ }
+
+ return (fmemopen_fill_fputs(f, 128, B_TRUE));
+}
+
+static boolean_t
+fmemopen_fputs_nobuf(void)
+{
+ FILE *f;
+
+ f = fmemopen(NULL, 128, "w+");
+ if (f == NULL) {
+ warn("failed to open fmemopen stream");
+ return (B_FALSE);
+ }
+
+ if (setvbuf(f, NULL, _IONBF, 0) != 0) {
+ warn("failed to set buffer to non-buffered mode");
+ }
+
+ return (fmemopen_fill_fputs(f, 128, B_FALSE));
+}
+
+static boolean_t
+memstream_check_seek(FILE *f, size_t len, int whence)
+{
+ off_t o;
+ long l;
+ boolean_t ret = B_TRUE;
+
+ if (fseeko(f, 0, whence) != 0) {
+ warn("failed to seek, whence: %d", whence);
+ return (B_FALSE);
+ }
+
+ if ((o = ftello(f)) == -1) {
+ warn("failed to get offset from ftello");
+ ret = B_FALSE;
+ } else if (o < 0 || (size_t)o != len) {
+ warnx("found bad stream position: expected %zu, found: %zu",
+ len, (size_t)o);
+ ret = B_FALSE;
+ }
+
+ if ((l = ftell(f)) == -1) {
+ warn("failed to get offset from ftell");
+ ret = B_FALSE;
+ } else if (l < 0 || (size_t)l != len) {
+ warnx("found bad stream position: expected %zu, found: %zu",
+ len, (size_t)l);
+ ret = B_FALSE;
+ }
+
+ return (ret);
+}
+
+static boolean_t
+fmemopen_defseek_r(void)
+{
+ FILE *f;
+ size_t len = strlen(fmemopen_str1);
+ boolean_t ret, ret2;
+
+ f = fmemopen(fmemopen_str1, len, "r");
+ if (f == NULL) {
+ warn("failed to open fmemopen stream");
+ return (B_FALSE);
+ }
+
+ ret = memstream_check_seek(f, 0, SEEK_CUR);
+ ret2 = memstream_check_seek(f, len, SEEK_END);
+ (void) fclose(f);
+ return (ret && ret2);
+}
+
+static boolean_t
+fmemopen_defseek_rp(void)
+{
+ FILE *f;
+ size_t len = strlen(fmemopen_str1);
+ boolean_t ret, ret2;
+
+ f = fmemopen(fmemopen_str1, len, "r+");
+ if (f == NULL) {
+ warn("failed to open fmemopen stream");
+ return (B_FALSE);
+ }
+
+ ret = memstream_check_seek(f, 0, SEEK_CUR);
+ ret2 = memstream_check_seek(f, len, SEEK_END);
+ (void) fclose(f);
+ return (ret && ret2);
+}
+
+static boolean_t
+fmemopen_defseek_w(void)
+{
+ FILE *f;
+ size_t len = strlen(fmemopen_str1);
+ boolean_t ret, ret2;
+ char *str;
+
+ if ((str = strdup(fmemopen_str1)) == NULL) {
+ warn("failed to duplicate string");
+ return (B_FALSE);
+ }
+
+ f = fmemopen(str, len, "w");
+ if (f == NULL) {
+ warn("failed to open fmemopen stream");
+ free(str);
+ return (B_FALSE);
+ }
+
+ ret = memstream_check_seek(f, 0, SEEK_CUR);
+ ret2 = memstream_check_seek(f, 0, SEEK_END);
+ (void) fclose(f);
+ free(str);
+ return (ret && ret2);
+}
+
+static boolean_t
+fmemopen_defseek_wp(void)
+{
+ FILE *f;
+ size_t len = strlen(fmemopen_str1);
+ boolean_t ret, ret2;
+ char *str;
+
+ if ((str = strdup(fmemopen_str1)) == NULL) {
+ warn("failed to duplicate string");
+ return (B_FALSE);
+ }
+
+ f = fmemopen(str, len, "w+");
+ if (f == NULL) {
+ warn("failed to open fmemopen stream");
+ free(str);
+ return (B_FALSE);
+ }
+
+ ret = memstream_check_seek(f, 0, SEEK_CUR);
+ ret2 = memstream_check_seek(f, 0, SEEK_END);
+ (void) fclose(f);
+ free(str);
+ return (ret && ret2);
+}
+
+static boolean_t
+fmemopen_defseek_a(void)
+{
+ FILE *f;
+ size_t len = strlen(fmemopen_str1);
+ boolean_t ret, ret2;
+ char *str;
+
+ if ((str = strdup(fmemopen_str1)) == NULL) {
+ warn("failed to duplicate string");
+ return (B_FALSE);
+ }
+
+ f = fmemopen(str, len, "a");
+ if (f == NULL) {
+ warn("failed to open fmemopen stream");
+ free(str);
+ return (B_FALSE);
+ }
+
+ ret = memstream_check_seek(f, len, SEEK_CUR);
+ ret2 = memstream_check_seek(f, len, SEEK_END);
+ (void) fclose(f);
+ free(str);
+ return (ret && ret2);
+}
+
+static boolean_t
+fmemopen_defseek_ap(void)
+{
+ FILE *f;
+ size_t len = strlen(fmemopen_str1);
+ boolean_t ret, ret2;
+ char *str;
+
+ if ((str = strdup(fmemopen_str1)) == NULL) {
+ warn("failed to duplicate string");
+ return (B_FALSE);
+ }
+
+ f = fmemopen(str, len, "a+");
+ if (f == NULL) {
+ warn("failed to open fmemopen stream");
+ free(str);
+ return (B_FALSE);
+ }
+
+ ret = memstream_check_seek(f, len, SEEK_CUR);
+ ret2 = memstream_check_seek(f, len, SEEK_END);
+ (void) fclose(f);
+ free(str);
+ return (ret && ret2);
+}
+
+static boolean_t
+fmemopen_defseek_a_nbyte(void)
+{
+ FILE *f;
+ size_t len = strlen(fmemopen_str1);
+ boolean_t ret, ret2;
+ char *str;
+
+ if ((str = strdup(fmemopen_str1)) == NULL) {
+ warn("failed to duplicate string");
+ return (B_FALSE);
+ }
+ str[8] = '\0';
+
+ f = fmemopen(str, len, "a");
+ if (f == NULL) {
+ warn("failed to open fmemopen stream");
+ free(str);
+ return (B_FALSE);
+ }
+
+ ret = memstream_check_seek(f, 8, SEEK_CUR);
+ ret2 = memstream_check_seek(f, 8, SEEK_END);
+ (void) fclose(f);
+ free(str);
+ return (ret && ret2);
+}
+
+static boolean_t
+fmemopen_defseek_ap_nbyte(void)
+{
+ FILE *f;
+ size_t len = strlen(fmemopen_str1);
+ boolean_t ret, ret2;
+ char *str;
+
+ if ((str = strdup(fmemopen_str1)) == NULL) {
+ warn("failed to duplicate string");
+ return (B_FALSE);
+ }
+ str[12] = '\0';
+
+ f = fmemopen(str, len, "a+");
+ if (f == NULL) {
+ warn("failed to open fmemopen stream");
+ free(str);
+ return (B_FALSE);
+ }
+
+ ret = memstream_check_seek(f, 12, SEEK_CUR);
+ ret2 = memstream_check_seek(f, 12, SEEK_END);
+ (void) fclose(f);
+ free(str);
+ return (ret && ret2);
+}
+
+static boolean_t
+fmemopen_defseek_ap_null(void)
+{
+ FILE *f;
+ size_t len = strlen(fmemopen_str1);
+ boolean_t ret, ret2;
+
+ f = fmemopen(NULL, len, "a+");
+ if (f == NULL) {
+ warn("failed to open fmemopen stream");
+ return (B_FALSE);
+ }
+
+ ret = memstream_check_seek(f, 0, SEEK_CUR);
+ ret2 = memstream_check_seek(f, 0, SEEK_END);
+ (void) fclose(f);
+ return (ret && ret2);
+}
+
+static boolean_t
+fmemopen_read_eof_fgetc(void)
+{
+ FILE *f;
+ size_t len = strlen(fmemopen_str1);
+ boolean_t ret = B_TRUE, ret2, ret3;
+
+ f = fmemopen(fmemopen_str1, len, "r");
+ if (f == NULL) {
+ warn("failed to open fmemopen stream");
+ return (B_FALSE);
+ }
+
+ while (fgetc(f) != EOF) {
+ continue;
+ }
+
+ if (feof(f) == 0) {
+ warnx("stream not at end of EOF");
+ ret = B_FALSE;
+ }
+
+ ret2 = memstream_check_seek(f, len, SEEK_CUR);
+ ret3 = memstream_check_seek(f, len, SEEK_END);
+ (void) fclose(f);
+ return (ret && ret2 && ret3);
+}
+
+static boolean_t
+fmemopen_read_eof_fgets(void)
+{
+ FILE *f;
+ size_t len = strlen(fmemopen_str1);
+ boolean_t ret = B_TRUE, ret2, ret3;
+ char buf[BUFSIZ];
+
+ f = fmemopen(fmemopen_str1, len, "r");
+ if (f == NULL) {
+ warn("failed to open fmemopen stream");
+ return (B_FALSE);
+ }
+
+ while (fgets(buf, sizeof (buf), f) != NULL) {
+ continue;
+ }
+
+ if (feof(f) == 0) {
+ warnx("stream not at end of EOF");
+ ret = B_FALSE;
+ }
+
+ ret2 = memstream_check_seek(f, len, SEEK_CUR);
+ ret3 = memstream_check_seek(f, len, SEEK_END);
+ (void) fclose(f);
+ return (ret && ret2 && ret3);
+}
+
+static boolean_t
+fmemopen_read_eof_fread(void)
+{
+ FILE *f;
+ size_t len = strlen(fmemopen_str1);
+ boolean_t ret = B_TRUE, ret2, ret3;
+ char buf[BUFSIZ];
+
+ f = fmemopen(fmemopen_str1, len, "r");
+ if (f == NULL) {
+ warn("failed to open fmemopen stream");
+ return (B_FALSE);
+ }
+
+ while (fread(buf, sizeof (buf), 1, f) != 0) {
+ continue;
+ }
+
+ if (feof(f) == 0) {
+ warnx("stream not at end of EOF");
+ ret = B_FALSE;
+ }
+
+ ret2 = memstream_check_seek(f, len, SEEK_CUR);
+ ret3 = memstream_check_seek(f, len, SEEK_END);
+ (void) fclose(f);
+ return (ret && ret2 && ret3);
+}
+
+static boolean_t
+fmemopen_read_eof_fread2(void)
+{
+ FILE *f;
+ size_t len = strlen(fmemopen_str1);
+ boolean_t ret = B_TRUE, ret2, ret3;
+ char buf[BUFSIZ];
+
+ f = fmemopen(fmemopen_str1, len, "r");
+ if (f == NULL) {
+ warn("failed to open fmemopen stream");
+ return (B_FALSE);
+ }
+
+ while (fread(buf, 1, sizeof (buf), f) != 0) {
+ continue;
+ }
+
+ if (feof(f) == 0) {
+ warnx("stream not at end of EOF");
+ ret = B_FALSE;
+ }
+
+ ret2 = memstream_check_seek(f, len, SEEK_CUR);
+ ret3 = memstream_check_seek(f, len, SEEK_END);
+ (void) fclose(f);
+ return (ret && ret2 && ret3);
+}
+
+static boolean_t
+fmemopen_bad_seeks(void)
+{
+ FILE *f;
+ boolean_t ret = B_TRUE;
+ size_t len = strlen(fmemopen_str1);
+ uint_t i;
+ struct {
+ int ret;
+ int whence;
+ long off;
+ long newpos;
+ } seeks[] = {
+ { 0, SEEK_CUR, 0, 0 },
+ { -1, SEEK_CUR, -1, 0 },
+ { -1, SEEK_SET, -5, 0 },
+ { -1, SEEK_END, -128, 0 },
+ { -1, SEEK_END, 1, 0 },
+ { -1, SEEK_SET, 128, 0 },
+ { 0, SEEK_SET, 16, 16 },
+ { -1, SEEK_CUR, -20, 16 },
+ { 0, SEEK_CUR, -16, 0 },
+ };
+
+ f = fmemopen(fmemopen_str1, len, "r");
+ if (f == NULL) {
+ warn("failed to open fmemopen stream");
+ return (B_FALSE);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(seeks); i++) {
+ int r;
+
+ r = fseek(f, seeks[i].off, seeks[i].whence);
+ if (r != seeks[i].ret) {
+ warnx("found bad return value for seek %d/%ld, "
+ "expected %d, found %d", seeks[i].whence,
+ seeks[i].off, seeks[i].ret, r);
+ ret = B_FALSE;
+ }
+
+ ret &= memstream_check_seek(f, seeks[i].newpos, SEEK_CUR);
+ }
+
+ (void) fclose(f);
+ return (ret);
+}
+
+static boolean_t
+fmemopen_open_trunc(void)
+{
+ char buf[16];
+ FILE *f;
+ boolean_t ret = B_TRUE;
+
+ (void) memset(buf, 'a', sizeof (buf));
+ f = fmemopen(buf, sizeof (buf), "w+");
+ if (f == NULL) {
+ warn("failed to create fmemopen stream");
+ return (B_FALSE);
+ }
+
+ if (buf[0] != '\0') {
+ warnx("w+ mode didn't truncate the buffer");
+ ret = B_FALSE;
+ }
+
+ (void) fclose(f);
+ return (ret);
+}
+
+static boolean_t
+fmemopen_write_nul(void)
+{
+ char buf[BUFSIZ];
+ FILE *f;
+ boolean_t ret = B_TRUE;
+ size_t npos = sizeof (buf) - 32;
+
+ (void) memset(buf, 'a', sizeof (buf));
+
+ f = fmemopen(buf, sizeof (buf), "w");
+ if (f == NULL) {
+ warn("failed to create fmemopen stream");
+ return (B_FALSE);
+ }
+
+ if (fputc('b', f) != 'b') {
+ warn("failed to write 'b' character to stream");
+ ret = B_FALSE;
+ }
+
+ if (fflush(f) != 0) {
+ warn("failed to flush stream");
+ ret = B_FALSE;
+ }
+
+ if (buf[0] != 'b' || buf[1] != '\0') {
+ warn("stream didn't properly write character and nul");
+ ret = B_FALSE;
+ }
+
+ if (fseek(f, sizeof (buf) - 32, SEEK_SET)) {
+ warn("failed to seek stream");
+ ret = B_FALSE;
+ }
+
+ if (fflush(f) != 0) {
+ warn("failed to flush stream");
+ ret = B_FALSE;
+ }
+
+ if (buf[npos] != 'a' || buf[npos - 1] != 'a' ||
+ buf[npos + 1] != 'a') {
+ warnx("seeking incorrectly inserted a nul");
+ ret = B_FALSE;
+ }
+
+ (void) fclose(f);
+
+ if (buf[npos] != 'a' || buf[npos - 1] != 'a' ||
+ buf[npos + 1] != 'a') {
+ warnx("seeking incorrectly inserted a nul");
+ ret = B_FALSE;
+ }
+
+ return (ret);
+}
+
+static boolean_t
+fmemopen_append_nul(void)
+{
+ char buf[32], buf2[32];
+ FILE *f;
+ boolean_t ret = B_TRUE;
+
+ (void) memset(buf, 'a', sizeof (buf));
+ buf[8] = '\0';
+
+ f = fmemopen(buf, sizeof (buf), "a");
+ if (f == NULL) {
+ warn("failed to create fmemopen stream");
+ return (B_FALSE);
+ }
+
+ if (fputc('b', f) != 'b') {
+ warn("failed to write 'b' character to stream");
+ ret = B_FALSE;
+ }
+
+ if (fflush(f) != 0) {
+ warn("failed to flush stream");
+ ret = B_FALSE;
+ }
+
+ if (buf[8] != 'b' || buf[9] != '\0') {
+ warn("stream didn't properly write character and nul");
+ ret = B_FALSE;
+ }
+
+ /*
+ * Append mode shouldn't insert a NUL if we write the entire buffer.
+ */
+ (void) memset(buf2, 'b', sizeof (buf2));
+ if (fwrite(buf2, sizeof (buf2) - ftell(f), 1, f) != 1) {
+ warn("failed to write buf2");
+ ret = B_FALSE;
+ }
+
+ if (fflush(f) != 0) {
+ warn("failed to flush stream");
+ ret = B_FALSE;
+ }
+
+ if (buf[sizeof (buf) - 1] != 'b') {
+ warnx("found invalid character: %x", buf[sizeof (buf) - 1]);
+ ret = B_FALSE;
+ }
+
+ (void) fclose(f);
+
+ if (buf[sizeof (buf) - 1] != 'b') {
+ warnx("found invalid character: %x", buf[sizeof (buf) - 1]);
+ ret = B_FALSE;
+ }
+
+ return (ret);
+}
+
+static boolean_t
+fmemopen_read_nul(void)
+{
+ char buf[32];
+ FILE *f;
+
+ (void) memset(buf, '\0', sizeof (buf));
+
+ f = fmemopen(buf, sizeof (buf), "r+");
+ if (f == NULL) {
+ warn("failed to create fmemopen stream");
+ return (B_FALSE);
+ }
+
+ if (fgetc(f) != '\0') {
+ warnx("failed to read nul character");
+ return (B_FALSE);
+ }
+
+ (void) fclose(f);
+ return (B_TRUE);
+}
+
+static boolean_t
+open_memstream_def_seek(void)
+{
+ char *c;
+ size_t s;
+ FILE *f;
+ boolean_t ret, ret2;
+
+ if ((f = open_memstream(&c, &s)) == NULL) {
+ warn("failed to call open_memstream()");
+ return (B_FALSE);
+ }
+
+ ret = memstream_check_seek(f, 0, SEEK_CUR);
+ ret2 = memstream_check_seek(f, 0, SEEK_END);
+ (void) fclose(f);
+ free(c);
+ return (ret && ret2);
+}
+
+static boolean_t
+open_wmemstream_def_seek(void)
+{
+ wchar_t *c;
+ size_t s;
+ FILE *f;
+ boolean_t ret, ret2;
+
+ if ((f = open_wmemstream(&c, &s)) == NULL) {
+ warn("failed to call open_wmemstream()");
+ return (B_FALSE);
+ }
+
+ ret = memstream_check_seek(f, 0, SEEK_CUR);
+ ret2 = memstream_check_seek(f, 0, SEEK_END);
+ (void) fclose(f);
+ free(c);
+ return (ret && ret2);
+}
+
+static boolean_t
+open_memstream_no_read(void)
+{
+ char *c;
+ size_t s;
+ FILE *f;
+ boolean_t ret = B_TRUE;
+
+ if ((f = open_memstream(&c, &s)) == NULL) {
+ warn("failed to call open_memstream()");
+ return (B_FALSE);
+ }
+
+ if (fgetc(f) != EOF) {
+ warnx("read succeeded when it should have failed");
+ ret = B_FALSE;
+ }
+
+ if (errno != EBADF) {
+ warnx("found wrong errno, expected %d, found %d", EBADF, errno);
+ ret = B_FALSE;
+ }
+
+ (void) fclose(f);
+ free(c);
+ return (ret);
+}
+
+static boolean_t
+open_wmemstream_no_read(void)
+{
+ wchar_t *c;
+ size_t s;
+ FILE *f;
+ boolean_t ret = B_TRUE;
+
+ if ((f = open_wmemstream(&c, &s)) == NULL) {
+ warn("failed to call open_wmemstream()");
+ return (B_FALSE);
+ }
+
+ if (fgetc(f) != EOF) {
+ warnx("read succeeded when it should have failed");
+ ret = B_FALSE;
+ }
+
+ if (errno != EBADF) {
+ warnx("found wrong errno, expected %d, found %d", EBADF, errno);
+ ret = B_FALSE;
+ }
+
+ (void) fclose(f);
+ free(c);
+ return (ret);
+}
+
+static boolean_t
+open_memstream_bad_flush(void)
+{
+ char *c;
+ size_t s;
+ FILE *f;
+ boolean_t ret = B_TRUE;
+
+ if ((f = open_memstream(&c, &s)) == NULL) {
+ warn("failed to call open_memstream()");
+ return (B_FALSE);
+ }
+
+ /* Force the buffer to exist */
+ if (fputc('a', f) != 'a') {
+ warn("failed to write character to buffer");
+ ret = B_FALSE;
+ }
+
+ if (fseek(f, BUFSIZ * 2 + 1, SEEK_END) != 0) {
+ warn("Failed to seek beyond buffer size");
+ ret = B_FALSE;
+ }
+
+ umem_setmtbf(1);
+ if (fputc('a', f) != 'a') {
+ warn("failed to write character to buffer");
+ ret = B_FALSE;
+ }
+
+ if (fflush(f) != EOF) {
+ warnx("fflush succeeded when it should have failed");
+ }
+
+ if (errno != EAGAIN) {
+ warnx("bad errno, found %d, expected %d", errno, EAGAIN);
+ }
+ umem_setmtbf(0);
+
+ (void) fclose(f);
+ free(c);
+ return (ret);
+}
+
+static boolean_t
+open_wmemstream_bad_flush(void)
+{
+ wchar_t *c;
+ size_t s;
+ FILE *f;
+ boolean_t ret = B_TRUE;
+
+ if ((f = open_wmemstream(&c, &s)) == NULL) {
+ warn("failed to call open_wmemstream()");
+ return (B_FALSE);
+ }
+
+ /* Force the buffer to exist */
+ if (fputwc('a', f) != 'a') {
+ warn("failed to write character to buffer");
+ ret = B_FALSE;
+ }
+
+ if (fseek(f, BUFSIZ * 2 + 1, SEEK_END) != 0) {
+ warn("Failed to seek beyond buffer size");
+ ret = B_FALSE;
+ }
+
+ umem_setmtbf(1);
+ if (fputc('a', f) != 'a') {
+ warn("failed to write character to buffer");
+ ret = B_FALSE;
+ }
+
+ if (fflush(f) != EOF) {
+ warnx("fflush succeeded when it should have failed");
+ }
+
+ if (errno != EAGAIN) {
+ warnx("bad errno, found %d, expected %d", errno, EAGAIN);
+ }
+ umem_setmtbf(0);
+
+ (void) fclose(f);
+ free(c);
+ return (ret);
+}
+
+static boolean_t
+memstream_bad_seek(void)
+{
+ FILE *f, *fw;
+ boolean_t ret = B_TRUE;
+ uint_t i;
+ char *c;
+ wchar_t *w;
+ size_t s1, s2;
+ struct {
+ int ret;
+ int whence;
+ long off;
+ long newpos;
+ } seeks[] = {
+ { 0, SEEK_CUR, 0, 0 },
+ { -1, SEEK_CUR, -1, 0 },
+ { -1, SEEK_SET, -5, 0 },
+ { -1, SEEK_END, -5, 0 },
+ { 0, SEEK_SET, 16, 16 },
+ { -1, SEEK_CUR, -20, 16 },
+ { 0, SEEK_CUR, -16, 0 },
+ };
+
+ f = open_memstream(&c, &s1);
+ fw = open_wmemstream(&w, &s2);
+ if (f == NULL || fw == NULL) {
+ warnx("failed to create memory streams");
+ return (B_FALSE);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(seeks); i++) {
+ int r;
+
+ r = fseek(f, seeks[i].off, seeks[i].whence);
+ if (r != seeks[i].ret) {
+ warnx("found bad return value for seek %d/%ld, "
+ "expected %d, found %d", seeks[i].whence,
+ seeks[i].off, seeks[i].ret, r);
+ ret = B_FALSE;
+ }
+
+ ret &= memstream_check_seek(f, seeks[i].newpos, SEEK_CUR);
+
+ r = fseek(fw, seeks[i].off, seeks[i].whence);
+ if (r != seeks[i].ret) {
+ warnx("found bad return value for seek %d/%ld, "
+ "expected %d, found %d", seeks[i].whence,
+ seeks[i].off, seeks[i].ret, r);
+ ret = B_FALSE;
+ }
+
+ ret &= memstream_check_seek(fw, seeks[i].newpos, SEEK_CUR);
+ }
+
+ (void) fclose(f);
+ (void) fclose(fw);
+ free(c);
+ free(w);
+ return (ret);
+}
+
+static boolean_t
+open_memstream_append_nul(void)
+{
+ char *c;
+ size_t s;
+ FILE *f;
+ boolean_t ret = B_TRUE;
+
+ if ((f = open_memstream(&c, &s)) == NULL) {
+ warn("failed to call open_memstream()");
+ return (B_FALSE);
+ }
+
+ if (fputc('a', f) != 'a') {
+ warn("failed to write 'a' to stream");
+ ret = B_FALSE;
+ }
+
+ if (fflush(f) != 0) {
+ warn("failed to flush stream");
+ ret = B_FALSE;
+ }
+
+ if (c[s] != '\0') {
+ warnx("missing nul character, found %x", c[s]);
+ ret = B_FALSE;
+ }
+
+ if (fseek(f, arc4random_uniform(2 * BUFSIZ), SEEK_SET) != 0) {
+ warn("failed to seek");
+ ret = B_FALSE;
+ }
+
+ if (fflush(f) != 0) {
+ warn("failed to flush stream");
+ ret = B_FALSE;
+ }
+
+ if (c[s] != '\0') {
+ warnx("missing nul character, found %x", c[s]);
+ ret = B_FALSE;
+ }
+
+ (void) fclose(f);
+ free(c);
+ return (ret);
+}
+
+static boolean_t
+open_wmemstream_append_nul(void)
+{
+ wchar_t *c;
+ size_t s;
+ FILE *f;
+ boolean_t ret = B_TRUE;
+
+ if ((f = open_wmemstream(&c, &s)) == NULL) {
+ warn("failed to call open_wmemstream()");
+ return (B_FALSE);
+ }
+
+ if (fputwc('a', f) != 'a') {
+ warn("failed to write 'a' to stream");
+ ret = B_FALSE;
+ }
+
+ if (fflush(f) != 0) {
+ warn("failed to flush stream");
+ ret = B_FALSE;
+ }
+
+ if (c[s] != L'\0') {
+ warnx("missing nul character, found %x", c[s]);
+ ret = B_FALSE;
+ }
+
+ if (fseek(f, arc4random_uniform(2 * BUFSIZ), SEEK_SET) != 0) {
+ warn("failed to seek");
+ ret = B_FALSE;
+ }
+
+ if (fflush(f) != 0) {
+ warn("failed to flush stream");
+ ret = B_FALSE;
+ }
+
+ if (c[s] != L'\0') {
+ warnx("missing nul character, found %x", c[s]);
+ ret = B_FALSE;
+ }
+
+ (void) fclose(f);
+ free(c);
+ return (ret);
+}
+
+static boolean_t
+open_wmemstream_embed_nuls(void)
+{
+ const char str[] = { 'H', 'e', 'l', 'l', 'o', '\0', 'w',
+ 'o', 'r', 'd' };
+ const wchar_t wstr[] = { L'H', L'e', L'l', L'l', L'o', L'\0', L'w',
+ L'o', L'r', L'd' };
+ wchar_t *c;
+ size_t s;
+ FILE *f;
+ boolean_t ret = B_TRUE;
+
+ if ((f = open_wmemstream(&c, &s)) == NULL) {
+ warn("failed to call open_wmemstream()");
+ return (B_FALSE);
+ }
+
+ if (fwrite(str, sizeof (char), ARRAY_SIZE(str), f) == 0) {
+ warn("failed to write data buffer");
+ ret = B_FALSE;
+ }
+
+ if (fflush(f) != 0) {
+ warn("failed to flush data buffer");
+ ret = B_FALSE;
+ }
+
+ if (ARRAY_SIZE(wstr) != s) {
+ warnx("size mismatch, wrote %zu chars, found %zu chars",
+ ARRAY_SIZE(wstr), s);
+ ret = B_FALSE;
+ }
+
+ if (bcmp(wstr, c, sizeof (wstr)) != 0) {
+ warnx("data not written in expected format");
+ ret = B_FALSE;
+ }
+
+ (void) fclose(f);
+ free(c);
+ return (ret);
+}
+
+static boolean_t
+open_wmemstream_wide_write(void)
+{
+ size_t slen = wcslen(wstream_str);
+ wchar_t *c;
+ size_t s;
+ FILE *f;
+ boolean_t ret = B_TRUE;
+
+ if ((f = open_wmemstream(&c, &s)) == NULL) {
+ warn("failed to call open_wmemstream()");
+ return (B_FALSE);
+ }
+
+ if (fputws(wstream_str, f) == -1) {
+ warn("failed to write string");
+ ret = B_FALSE;
+ }
+
+ if (fflush(f) != 0) {
+ warn("failed to flush stream");
+ ret = B_FALSE;
+ }
+
+ if (s != slen) {
+ warnx("size mismatch, expected %zu chars, but found %zu",
+ slen, s);
+ ret = B_FALSE;
+ }
+
+ if (wcscmp(wstream_str, c) != 0) {
+ warnx("basic write doesn't match!");
+ ret = B_FALSE;
+ }
+
+ ret &= memstream_check_seek(f, slen, SEEK_CUR);
+ ret &= memstream_check_seek(f, slen, SEEK_END);
+
+ (void) fclose(f);
+ free(c);
+ return (ret);
+}
+
+/*
+ * Make sure that if we seek somewhere and flush that it doesn't cause us to
+ * grow.
+ */
+static boolean_t
+open_wmemstream_seek_grow(void)
+{
+ size_t slen = wcslen(wstream_str);
+ wchar_t *c;
+ size_t s;
+ FILE *f;
+ boolean_t ret = B_TRUE;
+
+ if ((f = open_wmemstream(&c, &s)) == NULL) {
+ warn("failed to call open_wmemstream()");
+ return (B_FALSE);
+ }
+
+ if (fflush(f) != 0) {
+ warn("failed to flush stream");
+ ret = B_FALSE;
+ }
+
+ if (s != 0) {
+ warn("bad initial size");
+ ret = B_FALSE;
+ }
+
+ ret &= memstream_check_seek(f, 0, SEEK_CUR);
+ ret &= memstream_check_seek(f, 0, SEEK_END);
+ if (fseek(f, 2048, SEEK_SET) != 0) {
+ warn("failed to seek");
+ }
+
+ ret &= memstream_check_seek(f, 2048, SEEK_CUR);
+
+ if (fflush(f) != 0) {
+ warn("failed to flush stream");
+ ret = B_FALSE;
+ }
+
+ if (s != 0) {
+ warnx("bad size after seek");
+ ret = B_FALSE;
+ }
+
+ if (fputws(wstream_str, f) == -1) {
+ warn("failed to write string");
+ ret = B_FALSE;
+ }
+
+ if (fflush(f) != 0) {
+ warn("failed to flush stream");
+ ret = B_FALSE;
+ }
+
+ if (s != slen + 2048) {
+ warnx("size is off after seek and write, found %zu", s);
+ ret = B_FALSE;
+ }
+
+ ret &= memstream_check_seek(f, s, SEEK_CUR);
+ ret &= memstream_check_seek(f, s, SEEK_END);
+
+ (void) fclose(f);
+ free(c);
+ return (ret);
+}
+
+static boolean_t
+open_wmemstream_byte_writes(void)
+{
+ wchar_t *c;
+ size_t s, len, i;
+ FILE *f;
+ boolean_t ret = B_TRUE;
+
+ if ((f = open_wmemstream(&c, &s)) == NULL) {
+ warn("failed to call open_wmemstream()");
+ return (B_FALSE);
+ }
+
+ /*
+ * Use non-buffered mode so that way we can make sure to detect mbs
+ * state errors right away.
+ */
+ if (setvbuf(f, NULL, _IONBF, 0) != 0) {
+ warnx("failed to set to non-buffered mode");
+ ret = B_FALSE;
+ }
+
+ len = wcslen(wstream_str);
+ for (i = 0; i < len; i++) {
+ char buf[MB_CUR_MAX + 1];
+ int mblen, curmb;
+
+ mblen = wctomb(buf, wstream_str[i]);
+
+ if (mblen == -1) {
+ warn("failed to convert wc %zu", i);
+ ret = B_FALSE;
+ continue;
+ }
+ for (curmb = 0; curmb < mblen; curmb++) {
+ if (fputc(buf[curmb], f) == EOF) {
+ warn("failed to write byte %d of wc %zu",
+ curmb, i);
+ ret = B_FALSE;
+ }
+ }
+ }
+
+ if (fflush(f) != 0) {
+ warn("failed to flush stream");
+ ret = B_FALSE;
+ }
+
+ if (s != len) {
+ warnx("found wrong number of wide characters, expected %zu, "
+ "found %zu", len + 1, s);
+ ret = B_FALSE;
+ }
+
+ if (wcscmp(c, wstream_str) != 0) {
+ warnx("the wide character strings don't compare equally");
+ ret = B_FALSE;
+ }
+
+ (void) fclose(f);
+ free(c);
+ return (ret);
+}
+
+static boolean_t
+open_wmemstream_bad_seq(void)
+{
+ wchar_t *c, test = wstr_const[0];
+ size_t s;
+ FILE *f;
+ char buf[MB_CUR_MAX + 1];
+ boolean_t ret = B_TRUE;
+
+ if (wctomb(buf, test) == -1) {
+ warn("failed to convert 光 to multi-byte sequence");
+ return (B_FALSE);
+ }
+
+ if ((f = open_wmemstream(&c, &s)) == NULL) {
+ warn("failed to call open_wmemstream()");
+ return (B_FALSE);
+ }
+
+ /*
+ * Make sure to use a non-buffered mode so that way writes immediately
+ * get sent to the underlying stream.
+ */
+ if (setvbuf(f, NULL, _IONBF, 0) != 0) {
+ warnx("failed to set to non-buffered mode");
+ ret = B_FALSE;
+ }
+
+ if (fputc(buf[0], f) == EOF) {
+ warn("failed to write 0x%x to buffer", buf[0]);
+ ret = B_FALSE;
+ }
+
+ if (fputc(buf[0], f) != EOF) {
+ warnx("successfully wrote 0x%x to buffer, but should have "
+ "failed", buf[0]);
+ ret = B_FALSE;
+ }
+
+ if (errno != EIO) {
+ warnx("found wrong errno, expected EIO, but found 0x%x", errno);
+ ret = B_FALSE;
+ }
+
+ (void) fclose(f);
+ free(c);
+ return (ret);
+}
+
+static boolean_t
+open_wmemstream_bad_seq_fflush(void)
+{
+ wchar_t *c, test = wstr_const[0];
+ size_t s;
+ FILE *f;
+ char buf[MB_CUR_MAX + 1];
+ boolean_t ret = B_TRUE;
+
+ if (wctomb(buf, test) == -1) {
+ warn("failed to convert 光 to multi-byte sequence");
+ return (B_FALSE);
+ }
+
+ if ((f = open_wmemstream(&c, &s)) == NULL) {
+ warn("failed to call open_wmemstream()");
+ return (B_FALSE);
+ }
+
+ if (fputc(buf[0], f) == EOF) {
+ warn("failed to write 0x%x to buffer", buf[0]);
+ ret = B_FALSE;
+ }
+
+ if (fputc(buf[0], f) == EOF) {
+ warn("failed to write bad byte 0x%x to buffer", buf[0]);
+ ret = B_FALSE;
+ }
+
+ if (fflush(f) == 0) {
+ warn("fflush succeeded, expected failure");
+ ret = B_FALSE;
+ }
+
+ if (errno != EIO) {
+ warn("found wrong errno, expected EIO, but found 0x%x", errno);
+ ret = B_FALSE;
+ }
+
+ (void) fclose(f);
+ free(c);
+ return (ret);
+}
+
+/*
+ * When writing individual bytes out, we need to make sure that we don't
+ * incorrectly count buffered data as offsets like we do for other byte oriented
+ * consumers of the ftell family.
+ */
+static boolean_t
+open_wmemstream_ftell(void)
+{
+ wchar_t *c, test = wstr_const[0];
+ size_t s, i, wclen;
+ FILE *f;
+ char buf[MB_CUR_MAX + 1];
+ boolean_t ret = B_TRUE;
+ long loc;
+
+ if ((wclen = wctomb(buf, test)) == -1) {
+ warn("failed to convert 光 to multi-byte sequence");
+ return (B_FALSE);
+ }
+
+ if ((f = open_wmemstream(&c, &s)) == NULL) {
+ warn("failed to call open_wmemstream()");
+ return (B_FALSE);
+ }
+
+ if ((loc = ftell(f)) != 0) {
+ warnx("stream at bad loc after start, found %ld, expected 0",
+ loc);
+ ret = B_FALSE;
+ }
+
+ if (fputwc(test, f) == WEOF) {
+ warn("failed to write wide character to stream");
+ ret = B_FALSE;
+ }
+
+ if ((loc = ftell(f)) != 1) {
+ warnx("stream at bad loc after writing a single wide char, "
+ "found %ld, expected 1", loc);
+ ret = B_FALSE;
+ }
+
+ for (i = 0; i < wclen - 1; i++) {
+ if (fputc(buf[i], f) == EOF) {
+ warn("failed to write mb char 0x%x", buf[i]);
+ ret = B_FALSE;
+ }
+
+ if ((loc = ftell(f)) != 1) {
+ warnx("stream at bad loc after putting partial mb seq, "
+ "found %ld, expected 1", loc);
+ ret = B_FALSE;
+ }
+ }
+
+ /*
+ * Only after we advance the final char should it be two.
+ */
+ if (fputc(buf[i], f) == EOF) {
+ warn("failed to write mb char 0x%x", buf[i]);
+ ret = B_FALSE;
+ }
+
+ if ((loc = ftell(f)) != 2) {
+ warnx("stream at bad loc after writing a mb seq, "
+ "found %ld, expected 2", loc);
+ ret = B_FALSE;
+ }
+
+ if (fflush(f) != 0) {
+ warn("failed to flush stream");
+ ret = B_FALSE;
+ }
+
+ if (s != 2) {
+ warnx("size of stream is wrong, found %zu, expected 2", s);
+ ret = B_FALSE;
+ }
+
+ if (s != loc) {
+ warnx("size of buffer, %zu does not match pre-fflush "
+ "ftell loc: %ld", s, loc);
+ ret = B_FALSE;
+ }
+
+ loc = ftell(f);
+ if (s != loc) {
+ warnx("size of buffer, %zu does not match post-fflush "
+ "ftell loc: %ld", s, loc);
+ ret = B_FALSE;
+ }
+
+ (void) fclose(f);
+ free(c);
+ return (ret);
+}
+
+
+typedef struct memstream_test {
+ memstream_test_f mt_func;
+ const char *mt_test;
+} memstream_test_t;
+
+static const memstream_test_t memstream_tests[] = {
+ { fmemopen_badmode, "fmemopen: bad mode argument" },
+ { fmemopen_zerobuf1, "fmemopen: bad buffer size, valid buf" },
+ { fmemopen_zerobuf2, "fmemopen: bad buffer size, NULL buf" },
+ { fmemopen_nullbuf1, "fmemopen: invalid NULL buf, mode: r" },
+ { fmemopen_nullbuf2, "fmemopen: invalid NULL buf, mode: w" },
+ { fmemopen_nullbuf3, "fmemopen: invalid NULL buf, mode: a" },
+ { fmemopen_nullbuf4, "fmemopen: invalid NULL buf, mode: ax" },
+ { fmemopen_sizemax, "fmemopen: bad open ask for SIZE_MAX bytes" },
+ { fmemopen_cantalloc, "fmemopen: simulate malloc failure at open" },
+ { open_memstream_badbuf, "open_memstream: bad buf" },
+ { open_memstream_badsize, "open_memstream: bad size" },
+ { open_memstream_allnull, "open_memstream: bad buf and size" },
+ { open_memstream_cantalloc, "open_memstream: simulate malloc failure "
+ "at " "open" },
+ { open_wmemstream_badbuf, "open_wmemstream: bad buf" },
+ { open_wmemstream_badsize, "open_wmemstream: bad size" },
+ { open_wmemstream_allnull, "open_wmemstream: bad buf and size" },
+ { open_wmemstream_cantalloc, "open_wmemstream: simulate malloc "
+ "failure at open" },
+ { fmemopen_fill_default, "fmemopen: write beyond end of buffer: putc "
+ "(buf smaller than BUFSIZ)" },
+ { fmemopen_fill_lbuf, "fmemopen: write beyond end of buffer: putc "
+ "(line buffering)" },
+ { fmemopen_fill_nobuf, "fmemopen: write beyond end of buffer: putc "
+ "(no stdio buffering)" },
+ { fmemopen_fwrite_default, "fmemopen: write beyond end of buffer: "
+ "fwrite (buf smaller than BUFSIZ)" },
+ { fmemopen_fwrite_lbuf, "fmemopen: write beyond end of buffer: fwrite "
+ "(line buffering)" },
+ { fmemopen_fwrite_nobuf, "fmemopen: write beyond end of buffer: fwrite "
+ "(no stdio buffering)" },
+ { fmemopen_alt_fwrite_default, "fmemopen: write beyond end of buffer: "
+ "fwrite 2 (buf smaller than BUFSIZ)" },
+ { fmemopen_alt_fwrite_lbuf, "fmemopen: write beyond end of buffer: "
+ "fwrite 2 (line buffering)" },
+ { fmemopen_alt_fwrite_nobuf, "fmemopen: write beyond end of buffer: "
+ "fwrite 2 (no stdio buffering)" },
+ { fmemopen_fputs_default, "fmemopen: write beyond end of buffer: fputs "
+ "(buf smaller than BUFSIZ)" },
+ { fmemopen_fputs_lbuf, "fmemopen: write beyond end of buffer: fputs "
+ "(line buffering)" },
+ { fmemopen_fputs_nobuf, "fmemopen: write beyond end of buffer: fputs "
+ "(no stdio buffering)" },
+ { fmemopen_defseek_r, "fmemopen: default position and log. size, "
+ "mode: r"},
+ { fmemopen_defseek_rp, "fmemopen: default position and log. size, "
+ "mode: r+"},
+ { fmemopen_defseek_w, "fmemopen: default position and log. size, "
+ "mode: w"},
+ { fmemopen_defseek_wp, "fmemopen: default position and log. size, "
+ "mode: w+"},
+ { fmemopen_defseek_a, "fmemopen: default position and log. size, "
+ "mode: a"},
+ { fmemopen_defseek_ap, "fmemopen: default position and log. size, "
+ "mode: a+"},
+ { fmemopen_defseek_a_nbyte, "fmemopen: default position and log. size, "
+ "mode: a, nul byte"},
+ { fmemopen_defseek_ap_nbyte, "fmemopen: default position and log. "
+ "size, mode: a+, nul byte"},
+ { fmemopen_defseek_ap_null, "fmemopen: default position and log. size, "
+ "mode: a+, NULL buf"},
+ { fmemopen_read_eof_fgetc, "fmemopen: read until EOF with fgetc" },
+ { fmemopen_read_eof_fgets, "fmemopen: read until EOF with fgets" },
+ { fmemopen_read_eof_fread, "fmemopen: read until EOF with fread" },
+ { fmemopen_read_eof_fread2, "fmemopen: read until EOF with fread 2" },
+ { fmemopen_bad_seeks, "fmemopen: invalid seeks" },
+ { fmemopen_open_trunc, "fmemopen: w+ mode truncates buffer" },
+ { fmemopen_write_nul, "fmemopen: NULs properly inserted (w)" },
+ { fmemopen_append_nul, "fmemopen: NULs properly inserted (a)" },
+ { fmemopen_read_nul, "fmemopen: read NUL character normally" },
+ { open_memstream_def_seek, "open_memstream: default position and "
+ "logical size" },
+ { open_wmemstream_def_seek, "wopen_memstream: default position and "
+ "logical size" },
+ { open_memstream_no_read, "open_memstream: read doesn't work" },
+ { open_wmemstream_no_read, "open_wmemstream: read doesn't work" },
+ { open_memstream_bad_flush, "open_memstream: flush failure due to "
+ "induced memory failure" },
+ { open_wmemstream_bad_flush, "open_wmemstream: flush failure due to "
+ "induced memory failure" },
+ { memstream_bad_seek, "open_[w]memstream: bad seeks" },
+ { open_memstream_append_nul, "open_memstream: appends NULs" },
+ { open_wmemstream_append_nul, "open_wmemstream: appends NULs" },
+ { open_wmemstream_embed_nuls, "open_wmemstream: handles embedded "
+ "NULs" },
+ { open_wmemstream_wide_write, "open_wmemstream: write wide chars" },
+ { open_wmemstream_seek_grow, "open_wmemstream: seeking doesn't grow" },
+ { open_wmemstream_byte_writes, "open_wmemstream: Write mb sequences" },
+ { open_wmemstream_bad_seq, "open_wmemstream: detect bad utf-8 "
+ "sequence" },
+ { open_wmemstream_bad_seq_fflush, "open_wmemstream: detect bad utf-8 "
+ "sequence 2 (fflush)" },
+ { open_wmemstream_ftell, "open_wmemstream: ftell buffering behavior" }
+};
+
+int
+main(void)
+{
+ uint_t i;
+ uint_t passes = 0;
+ uint_t ntests = ARRAY_SIZE(memstream_tests);
+
+ /*
+ * Set a UTF-8 locale to make sure to exercise open_wmemstream()'s
+ * mbstate logic in a more interesting way than ASCII.
+ */
+ (void) setlocale(LC_ALL, "en_US.UTF-8");
+ for (i = 0; i < ntests; i++) {
+ boolean_t r;
+
+ r = memstream_tests[i].mt_func();
+ (void) fprintf(stderr, "TEST %s: %s\n", r ? "PASSED" : "FAILED",
+ memstream_tests[i].mt_test);
+ if (r) {
+ passes++;
+ }
+ }
+
+ (void) printf("%d/%d test%s passed\n", passes, ntests,
+ passes > 1 ? "s" : "");
+ return (passes == ntests ? EXIT_SUCCESS : EXIT_FAILURE);
+}
diff --git a/usr/src/test/libc-tests/tests/stdio/memstream_reopen.c b/usr/src/test/libc-tests/tests/stdio/memstream_reopen.c
new file mode 100644
index 0000000000..af1bad6324
--- /dev/null
+++ b/usr/src/test/libc-tests/tests/stdio/memstream_reopen.c
@@ -0,0 +1,88 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2020 Robert Mustacchi
+ */
+
+/*
+ * In a c99/xpg6 environment freopen(3C) allows you to specify a NULL path to
+ * try and change the fopen() flags. Verify that the memstream functions do not
+ * allow this. Note, freopen(3C) is defined to try and close the stream, hence
+ * you won't see anything here.
+ */
+
+#include <stdio.h>
+#include <stdio.h>
+#include <wchar.h>
+#include <err.h>
+#include <stdlib.h>
+#include <errno.h>
+
+const char *
+_umem_debug_init(void)
+{
+ return ("default,verbose");
+}
+
+const char *
+_umem_logging_init(void)
+{
+ return ("fail,contents");
+}
+
+static void
+check_reopen(FILE *f, const char *variant)
+{
+ FILE *new = freopen(NULL, "r", f);
+ if (new != NULL) {
+ errx(EXIT_FAILURE, "TEST FAILED: was able to freopen %s",
+ variant);
+ }
+
+ if (errno != EBADF) {
+ errx(EXIT_FAILURE, "TEST FAILED: found wrong errno for %s: "
+ "expected %d, found %d", EBADF, errno);
+ }
+
+ (void) printf("TEST PASSED: %s\n", variant);
+}
+
+int
+main(void)
+{
+ FILE *f;
+ char *c;
+ wchar_t *wc;
+ size_t sz;
+
+ f = fmemopen(NULL, 16, "a+");
+ if (f == NULL) {
+ err(EXIT_FAILURE, "failed to create fmemopen() stream");
+ }
+ check_reopen(f, "fmemopen()");
+
+ f = open_memstream(&c, &sz);
+ if (f == NULL) {
+ err(EXIT_FAILURE, "failed to create open_memstream() stream");
+ }
+ check_reopen(f, "open_memstream()");
+ free(c);
+
+ f = open_wmemstream(&wc, &sz);
+ if (f == NULL) {
+ err(EXIT_FAILURE, "failed to create open_wmemstream() stream");
+ }
+ check_reopen(f, "open_wmemstream()");
+ free(wc);
+
+ return (0);
+}
diff --git a/usr/src/test/libc-tests/tests/stdio/open_memstreamtest.c b/usr/src/test/libc-tests/tests/stdio/open_memstreamtest.c
new file mode 100644
index 0000000000..1dadd6c343
--- /dev/null
+++ b/usr/src/test/libc-tests/tests/stdio/open_memstreamtest.c
@@ -0,0 +1,196 @@
+/*
+ * Copyright (c) 2011 Martin Pieuchot <mpi@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define OFFSET 16384
+
+const char start[] = "start";
+const char hello[] = "hello";
+
+const char *
+_umem_debug_init(void)
+{
+ return ("default,verbose");
+}
+
+const char *
+_umem_logging_init(void)
+{
+ return ("fail,contents");
+}
+
+int
+main(void)
+{
+ FILE *fp;
+ char *buf = (char *)0xff;
+ size_t size = 0;
+ off_t off;
+ int i, failures = 0;
+
+ if ((fp = open_memstream(&buf, &size)) == NULL) {
+ warn("open_memstream failed");
+ return (1);
+ }
+
+ off = ftello(fp);
+ if (off != 0) {
+ warnx("ftello failed. (1)");
+ failures++;
+ }
+
+ if (fflush(fp) != 0) {
+ warnx("fflush failed. (2)");
+ failures++;
+ }
+
+ if (size != 0) {
+ warnx("string should be empty. (3)");
+ failures++;
+ }
+
+ if (buf == (char *)0xff) {
+ warnx("buf not updated. (4)");
+ failures++;
+ }
+
+ if (fseek(fp, OFFSET, SEEK_SET) != 0) {
+ warnx("failed to fseek. (5)");
+ failures++;
+ }
+
+ if (fprintf(fp, hello) == EOF) {
+ warnx("fprintf failed. (6)");
+ failures++;
+ }
+
+ if (fflush(fp) == EOF) {
+ warnx("fflush failed. (7)");
+ failures++;
+ }
+
+ if (size != OFFSET + sizeof(hello)-1) {
+ warnx("failed, size %zu should be %zu. (8)",
+ size, OFFSET + sizeof(hello)-1);
+ failures++;
+ }
+
+ if (fseek(fp, 0, SEEK_SET) != 0) {
+ warnx("failed to fseek. (9)");
+ failures++;
+ }
+
+ if (fprintf(fp, start) == EOF) {
+ warnx("fprintf failed. (10)");
+ failures++;
+ }
+
+ if (fflush(fp) == EOF) {
+ warnx("fflush failed. (11)");
+ failures++;
+ }
+
+ if (size != sizeof(start)-1) {
+ warnx("failed, size %zu should be %zu. (12)",
+ size, sizeof(start)-1);
+ failures++;
+ }
+
+ /* Needed for sparse files */
+ if (strncmp(buf, start, sizeof(start)-1) != 0) {
+ warnx("failed, buffer didn't start with '%s'. (13)", start);
+ failures++;
+ }
+ for (i = sizeof(start)-1; i < OFFSET; i++)
+ if (buf[i] != '\0') {
+ warnx("failed, buffer non zero (offset %d). (14)", i);
+ failures++;
+ break;
+ }
+
+ if (memcmp(buf + OFFSET, hello, sizeof(hello)-1) != 0) {
+ warnx("written string incorrect. (15)");
+ failures++;
+ }
+
+ /* verify that simply seeking past the end doesn't increase the size */
+ if (fseek(fp, 100, SEEK_END) != 0) {
+ warnx("failed to fseek. (16)");
+ failures++;
+ }
+
+ if (fflush(fp) == EOF) {
+ warnx("fflush failed. (17)");
+ failures++;
+ }
+
+ if (size != OFFSET + sizeof(hello)-1) {
+ warnx("failed, size %zu should be %zu. (18)",
+ size, OFFSET + sizeof(hello)-1);
+ failures++;
+ }
+
+ if (fseek(fp, -1, SEEK_END) != 0) {
+ warnx("failed to fseek. (19)");
+ failures++;
+ }
+
+ if (fseek(fp, 8, SEEK_SET) != 0) {
+ warnx("failed to fseek. (20)");
+ failures++;
+ }
+
+ if (ftell(fp) != 8) {
+ warnx("failed seek test. (21)");
+ failures++;
+ }
+
+ /* Try to seek backward */
+ if (fseek(fp, -1, SEEK_CUR) != 0) {
+ warnx("failed to fseek. (22)");
+ failures++;
+ }
+
+ if (ftell(fp) != 7) {
+ warnx("failed seeking backward. (23)");
+ failures++;
+ }
+
+ if (fseek(fp, 5, SEEK_CUR) != 0) {
+ warnx("failed to fseek. (24)");
+ failures++;
+ }
+
+ if (fclose(fp) == EOF) {
+ warnx("fclose failed. (25)");
+ failures++;
+ }
+
+ if (size != 12) {
+ warnx("failed, size %zu should be %u. (26)",
+ size, 12);
+ failures++;
+ }
+
+ free(buf);
+
+ return (failures);
+}
diff --git a/usr/src/test/libc-tests/tests/stdio/orientation_test.c b/usr/src/test/libc-tests/tests/stdio/orientation_test.c
new file mode 100644
index 0000000000..4161bf4327
--- /dev/null
+++ b/usr/src/test/libc-tests/tests/stdio/orientation_test.c
@@ -0,0 +1,258 @@
+/*
+ * Copyright (c) 2009 Philip Guenther
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Test whether the various stdio functions set the stream orientation
+ * ("width") as they should
+ */
+
+#include <sys/types.h>
+#include <err.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <wchar.h>
+
+char filename[] = "/tmp/fwide.XXXXXXXXXX";
+
+FILE *dup_stdout = NULL;
+int failures = 0;
+
+void
+fail(int line, int r, char const *expect, char const *test)
+{
+ failures++;
+ fprintf(dup_stdout,
+ "FAIL: %d: fwide returned %d, expected %s 0 after %s\n",
+ line, r, expect, test);
+}
+
+FILE *
+setup(int line)
+{
+ FILE *f;
+ int r;
+
+ if ((f = fopen(filename, "r+")) == NULL)
+ err(2, "fopen");
+ if ((r = fwide(f, 0)) != 0)
+ fail(line, r, "==", "fopen");
+ return (f);
+}
+
+FILE *
+setup_std(FILE *std, int line)
+{
+ int r;
+
+ if (freopen(filename, "r+", std) == NULL)
+ err(2, "freopen");
+ if ((r = fwide(std, 0)) != 0)
+ fail(line, r, "==", "freopen");
+ return (std);
+}
+
+#define TEST_(x, op) \
+ do { \
+ f = setup(__LINE__); \
+ x; \
+ if (!((r = fwide(f, 0)) op 0)) \
+ fail(__LINE__, r, #op, #x); \
+ fclose(f); \
+ } while (0)
+
+#define TEST_STD_(std, x, op) \
+ do { \
+ f = setup_std(std, __LINE__); \
+ x; \
+ if (!((r = fwide(f, 0)) op 0)) \
+ fail(__LINE__, r, #op, #x); \
+ } while (0)
+
+#define TEST_UNCHANGED(x) TEST_(x, ==)
+#define TEST_NARROW(x) TEST_(x, <)
+#define TEST_WIDE(x) TEST_(x, >)
+#define TEST_UNCHANGED_STD(std, x) TEST_STD_(std, x, ==)
+#define TEST_NARROW_STD(std, x) TEST_STD_(std, x, <)
+#define TEST_WIDE_STD(std, x) TEST_STD_(std, x, >)
+
+int
+main(int argc, char *argv[])
+{
+ char buffer[BUFSIZ];
+ wchar_t wbuffer[BUFSIZ];
+ char *buf;
+ wchar_t *wbuf;
+ FILE *f;
+ fpos_t pos;
+ size_t size;
+ int fd, r;
+ char c;
+ wchar_t wc;
+
+ if ((fd = dup(1)) == -1)
+ err(2, "dup");
+ if ((dup_stdout = fdopen(fd, "w")) == NULL)
+ err(2, "fdopen");
+ if ((fd = mkstemp(filename)) == -1)
+ err(2, "mkstemp");
+ if (write(fd, "0123456789\n\n", 12) != 12 || close(fd))
+ err(2, "write + close");
+
+ /* status */
+ TEST_UNCHANGED(fwide(f, 0));
+ TEST_NARROW(fwide(f, -1));
+ TEST_WIDE(fwide(f, 1));
+ TEST_UNCHANGED(feof(f));
+ TEST_UNCHANGED(ferror(f));
+ TEST_UNCHANGED(fileno(f));
+ TEST_UNCHANGED(clearerr(f));
+
+ /* flush and purge */
+ TEST_UNCHANGED(fflush(f));
+
+ /* positioning */
+ TEST_UNCHANGED(fgetpos(f, &pos));
+ TEST_UNCHANGED(fgetpos(f, &pos); fsetpos(f, &pos));
+ TEST_UNCHANGED(ftell(f));
+ TEST_UNCHANGED(ftello(f));
+ TEST_UNCHANGED(fseek(f, 1, SEEK_CUR));
+ TEST_UNCHANGED(fseek(f, 1, SEEK_SET));
+ TEST_UNCHANGED(fseek(f, 1, SEEK_END));
+ TEST_UNCHANGED(fseeko(f, 1, SEEK_CUR));
+ TEST_UNCHANGED(fseeko(f, 1, SEEK_SET));
+ TEST_UNCHANGED(fseeko(f, 1, SEEK_END));
+ TEST_UNCHANGED(rewind(f));
+
+ /* buffering */
+ TEST_UNCHANGED(setbuf(f, NULL));
+ TEST_UNCHANGED(setbuf(f, buffer));
+ TEST_UNCHANGED(setvbuf(f, buffer, _IONBF, BUFSIZ));
+ TEST_UNCHANGED(setvbuf(f, buffer, _IOLBF, BUFSIZ));
+ TEST_UNCHANGED(setvbuf(f, buffer, _IOFBF, BUFSIZ));
+ TEST_UNCHANGED(setvbuf(f, NULL, _IONBF, 0));
+ TEST_UNCHANGED(setvbuf(f, NULL, _IOLBF, 0));
+ TEST_UNCHANGED(setvbuf(f, NULL, _IOFBF, 0));
+ TEST_UNCHANGED(setbuffer(f, NULL, 0));
+ TEST_UNCHANGED(setbuffer(f, buffer, BUFSIZ));
+ TEST_UNCHANGED(setlinebuf(f));
+
+ /* locking */
+ TEST_UNCHANGED(flockfile(f);funlockfile(f));
+ TEST_UNCHANGED(ftrylockfile(f);funlockfile(f));
+
+ /* input */
+ TEST_NARROW(getc(f));
+ TEST_NARROW(getc_unlocked(f));
+ TEST_NARROW(fgetc(f));
+ TEST_NARROW(c = fgetc(f); ungetc(c, f));
+ TEST_NARROW(fgets(buffer, BUFSIZ, f));
+ TEST_NARROW(fscanf(f, "%s\n", buffer));
+
+ /* output */
+ TEST_NARROW(putc('c', f));
+ TEST_NARROW(putc_unlocked('c', f));
+ TEST_NARROW(fputc('c', f));
+ TEST_NARROW(fputs("foo", f));
+ TEST_NARROW(fprintf(f, "%s\n", "foo"));
+
+ /* input from stdin */
+ TEST_NARROW_STD(stdin, getchar());
+ TEST_NARROW_STD(stdin, getchar_unlocked());
+ TEST_NARROW_STD(stdin, scanf("%s\n", buffer));
+
+ /* output to stdout */
+ TEST_NARROW_STD(stdout, putchar('c'));
+ TEST_NARROW_STD(stdout, putchar_unlocked('c'));
+ TEST_NARROW_STD(stdout, puts("foo"));
+ TEST_NARROW_STD(stdout, printf("foo"));
+
+ /* word-size ops */
+ /*
+ * fread and fwrite are specified as being implemented in
+ * terms of fgetc() and fputc() and therefore must set the
+ * stream orientation to narrow.
+ */
+ TEST_NARROW(fread(buffer, 4, BUFSIZ / 4, f));
+ TEST_NARROW(fwrite(buffer, 4, BUFSIZ / 4, f));
+
+ /*
+ * getw() and putw() aren't specified anywhere but logically
+ * should behave the same as fread/fwrite. Not all OSes agree:
+ * Solaris 10 has them not changing the orientation.
+ */
+ TEST_NARROW(getw(f));
+ TEST_NARROW(putw(1234, f));
+
+
+ /* WIDE CHAR TIME! */
+
+ /* input */
+ TEST_WIDE(getwc(f));
+ TEST_WIDE(fgetwc(f));
+ TEST_WIDE(wc = fgetwc(f); ungetwc(wc, f));
+ TEST_WIDE(fgetws(wbuffer, BUFSIZ, f));
+ TEST_WIDE(fwscanf(f, L"%s\n", wbuffer));
+
+ /* output */
+ TEST_WIDE(putwc(L'c', f));
+ TEST_WIDE(fputwc(L'c', f));
+ TEST_WIDE(fputws(L"foo", f));
+ TEST_WIDE(fwprintf(f, L"%s\n", L"foo"));
+
+ /* input from stdin */
+ TEST_WIDE_STD(stdin, getwchar());
+ TEST_WIDE_STD(stdin, wscanf(L"%s\n", wbuffer));
+
+ /* output to stdout */
+ TEST_WIDE_STD(stdout, putwchar(L'c'));
+ TEST_WIDE_STD(stdout, wprintf(L"foo"));
+
+
+ /* memory streams */
+ f = open_memstream(&buf, &size);
+ if (!((r = fwide(f, 0)) < 0))
+ fail(__LINE__, r, "<", "open_memstream()");
+ fclose(f);
+ f = open_wmemstream(&wbuf, &size);
+ if (f != NULL && !((r = fwide(f, 0)) > 0))
+ fail(__LINE__, r, ">", "open_wmemstream()");
+ fclose(f);
+
+
+ /* random stuff? */
+ TEST_UNCHANGED_STD(stderr, perror("foo"));
+
+ remove(filename);
+ if (failures)
+ exit(1);
+ exit(0);
+}
diff --git a/usr/src/test/libc-tests/tests/stdio/test_mbrtowc.c b/usr/src/test/libc-tests/tests/stdio/test_mbrtowc.c
new file mode 100644
index 0000000000..4b81acbf46
--- /dev/null
+++ b/usr/src/test/libc-tests/tests/stdio/test_mbrtowc.c
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2016 Ingo Schwarze <schwarze@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <err.h>
+#include <errno.h>
+#include <locale.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+
+static mbstate_t mbs;
+
+void
+onetest(const char *name, const char *in, size_t insz,
+ int outerr, size_t outsz, wint_t out)
+{
+ wchar_t wc;
+ size_t sz;
+
+ sz = mbrtowc(&wc, in, insz, &mbs);
+ if (errno != outerr)
+ err(1, "%zu %s(%zd)", MB_CUR_MAX, name, insz);
+ if (sz != outsz || (out != WEOF && wc != out))
+ errx(1, "%zu %s(%zd) = (%zd, %d) != (%zd, %d)",
+ MB_CUR_MAX, name, insz, sz, wc, outsz, out);
+ if (mbsinit(&mbs) == (insz && outsz == (size_t)-2))
+ errx(1, "%zu %s(%zd) mbsinit", MB_CUR_MAX, name, insz);
+ if (errno == 0 && outerr == 0)
+ return;
+ errno = 0;
+ memset(&mbs, 0, sizeof (mbs));
+}
+
+int
+main(void)
+{
+ onetest("NUL", "", 0, 0, -2, WEOF);
+ onetest("NUL", "", 2, 0, 0, L'\0');
+ onetest("BEL", "\a", 2, 0, 1, L'\a');
+ onetest("A", "A", 2, 0, 1, L'A');
+ onetest("DEL", "\177", 2, 0, 1, L'\177');
+ onetest("CSI", "\233", 2, 0, 1, L'\233');
+
+ if (setlocale(LC_CTYPE, "en_US.UTF-8") == NULL)
+ errx(1, "setlocale(UTF-8) failed");
+
+ onetest("NUL", "", 0, 0, -2, WEOF);
+ onetest("NUL", "", 8, 0, 0, L'\0');
+ onetest("BEL", "\a", 8, 0, 1, L'\a');
+ onetest("A", "A", 8, 0, 1, L'A');
+ onetest("DEL", "\177", 8, 0, 1, L'\177');
+ onetest("0x80", "\200", 8, EILSEQ, -1, WEOF);
+ onetest("0xc3", "\303", 1, 0, -2, WEOF);
+ onetest("U+00E9", "\251", 8, 0, 1, 0xe9);
+ onetest("0xec", "\354", 1, 0, -2, WEOF);
+ onetest("0xecbf", "\277", 1, 0, -2, WEOF);
+ onetest("U+CFFF", "\277", 8, 0, 1, 0xcfff);
+
+ if (setlocale(LC_CTYPE, "POSIX") == NULL)
+ errx(1, "setlocale(POSIX) failed");
+
+ onetest("0xff", "\277", 2, 0, 1, L'\277');
+
+ if (setlocale(LC_CTYPE, "en_US.UTF-8") == NULL)
+ errx(1, "second setlocale(UTF-8) failed");
+
+ onetest("U+13000", "\360\223\200\200", 8, 0, 4, 0x13000);
+
+ return (0);
+}