.TH LIBMPUIO 3 "September 2026" "libmpuio" "LIBMPUIO Programmer's Manual" .SH NAME libmpuio \- UCS-2/UTF-8 stdio-style I/O for LIBMPU arbitrary-precision numbers .SH LIBRARY LIBMPUIO companion library for LIBMPU. .SH SYNOPSIS .nf .B #include .PP .B extern mpu_FILE *mpu_stdin; .B extern mpu_FILE *mpu_stdout; .B extern mpu_FILE *mpu_stderr; .fi .SH DESCRIPTION LIBMPUIO provides a stdio-like stream interface without using libc .B FILE objects. Its public stream type is .B mpu_FILE and real-file streams are backed by POSIX file descriptors. .PP Public character and string interfaces use the LIBMPU scalar typedefs .BR __mpu_char8_t , .BR __mpu_char16_t , .B __mpu_char32_t and .BR __mpu_size_t , which are made available through .BR . Text inside the I/O API is strict UCS-2 in .BR __mpu_char16_t . Real-file text is converted to or from UTF-8 at the stream boundary. Surrogates and UTF-8 scalar values above U+FFFF are rejected with .BR EILSEQ . Raw .BR mpu_fread (3) and .BR mpu_fwrite (3) operate on bytes and perform no text conversion. .PP Formatted MPU integer, real, and complex I/O supports configured sizes through .B MPU_REAL_IO_LIMIT (currently up to 65536 bits). Formatted I/O is not restricted by .BR MPU_MATH_FN_LIMIT . .SH FORMATTED TYPE MODEL Formatted I/O deliberately distinguishes ordinary C objects from libmpu objects. Ordinary conversions such as .B %d or .B %g use the normal variadic C rules and fetch values of the types described by the conversion and standard length modifiers. Small integer arguments are subject to the usual default argument promotions. .PP Libmpu conversions use the historical .B z or .B Z size modifier. Both spellings are equivalent and lower-case .B z is reserved by LIBMPUIO for this purpose; the standard C .B z length modifier for .B size_t is not implemented. If no size digits follow, 128 bits are assumed. The set of size modifiers present in a build is compiled directly from .B MPU_REAL_IO_LIMIT in .BR . The argument is storage addressed through a pointer, while .I bits states the exact MPU object size. Libmpu's public arbitrary-precision typedefs are arrays of bytes, so an object name naturally decays to a pointer when used as a function-call argument. .PP A variadic .B va_list does not contain general run-time type/size metadata. Therefore LIBMPUIO does not attempt to infer an MPU object's size from its address: the explicit .B z/Z modifier is the formatted-I/O type contract. Ordinary C objects should use ordinary C conversions; MPU conversions should normally be used with libmpu objects of the matching configured size. See .BR mpu_printf (3) and .BR mpu_scanf (3) for examples and the detailed argument rules. .SH LOCALE AND ORDINARY FLOATING I/O Ordinary C floating conversions .B %e/%E/%f/%F/%g/%G follow the active .B LC_NUMERIC radix convention. Output uses the host libc formatter and decodes the resulting locale multibyte field to strict UCS-2 instead of widening individual output bytes. Ordinary scanf tokenization obtains the locale decimal-point string with .BR localeconv (3), recognizes that UCS-2 sequence in input, and presents the matching locale multibyte token to .BR strtold (3). This makes ordinary floating printf/scanf behavior symmetric for locales whose radix character is not .BR . . .PP Lowercase .B %a is reserved by LIBMPUIO for current-locale multibyte strings. MPU real and complex conversions remain governed by the LIBMPU formatted-number grammar rather than the ordinary libc floating tokenizer. .SH STREAM MODEL A stream contains buffering state, EOF/error indicators, pushback state, a recursive stream mutex, and a backend jump table. LIBMPUIO deliberately has three independent backends: descriptor-backed files with external UTF-8 text, native UCS-2 string/memory streams, and arbitrary byte-oriented callback streams opened by mpu_fopencookie(3). All three use the same formatted engines through the lower-level internal FILE_plus/jump-table abstraction. .PP For real text files, logical positions are external UTF-8 byte offsets. Buffered read-ahead and UCS-2 pushback are accounted for by .BR mpu_ftello (3). .SH LOCKING MODEL Ordinary stream functions acquire the stream's recursive mutex. Functions whose names end in .B _unlocked do not. The unlocked forms are intended for a region already protected by .BR mpu_flockfile (3), or for code that otherwise guarantees exclusive stream access. .PP The unlocked distinction changes locking only. Buffering, validation, UTF-8/UCS-2 conversion, return values, error indicators, and backend behavior are otherwise the same as for the corresponding locked operation. .PP The global stream list used by .BR mpu_fflush(NULL) and .BR mpu_flushlbf() is protected only while a stable snapshot is built. Streams in that snapshot carry internal lifetime pins, so the global list lock is released before an individual stream mutex is acquired or backend code is entered. A close unlinks the stream from future snapshots first and defers object destruction while an earlier snapshot still owns a pin. Application code must still synchronize an explicit close against arbitrary ordinary concurrent use of the same stream pointer. .SH FUNCTION GROUPS .TP .B Stream creation and lifetime .BR mpu_fopen (3), .BR mpu_fdopen (3), .BR mpu_freopen (3), .BR mpu_tmpfile (3), .BR mpu_popen (3), .BR mpu_pclose (3), .BR mpu_fopencookie (3), .BR mpu_fclose (3), .BR mpu_fflush (3), .BR mpu_flushlbf (3), .BR mpu_fpurge (3), .BR mpu_fileno (3). .TP .B UCS-2 memory streams .BR mpu_fmemopen (3), .BR mpu_open_memstream (3). .TP .B Diagnostics .BR mpu_perror (3), .BR mpu_strerror_r (3). .TP .B Raw binary I/O .BR mpu_fread (3), .BR mpu_fwrite (3), and unlocked variants. .TP .B Character and UCS-2 string I/O .BR mpu_fgetc (3), .BR mpu_getc (3), .BR mpu_getchar (3), .BR mpu_ungetc (3), .BR mpu_fputc (3), .BR mpu_putc (3), .BR mpu_putchar (3), .BR mpu_fgets (3), .BR mpu_fputs (3), .BR mpu_puts (3). .TP .B Formatted output .BR mpu_printf (3), .BR mpu_fprintf (3), .BR mpu_sprintf (3), .BR mpu_snprintf (3), .BR mpu_asprintf (3), .BR mpu_dprintf (3) and va_list/unlocked forms. .TP .B Formatted input .BR mpu_scanf (3), .BR mpu_fscanf (3), .BR mpu_sscanf (3) and va_list/unlocked forms. .TP .B Positioning .BR mpu_fseek (3), .BR mpu_fseeko (3), .BR mpu_ftell (3), .BR mpu_ftello (3), .BR mpu_fgetpos (3), .BR mpu_fsetpos (3), .BR mpu_rewind (3). .TP .B Buffering, indicators, and explicit locking .BR mpu_setvbuf (3), .BR mpu_setbuf (3), .BR mpu_feof (3), .BR mpu_ferror (3), .BR mpu_clearerr (3), .BR mpu_fpurge (3), .BR mpu_flockfile (3), .BR mpu_ftrylockfile (3), .BR mpu_funlockfile (3). .SH RETURN VALUES AND ERRORS Each family follows stdio-style return conventions described on its manual page. System-call failures preserve useful .B errno values. Stream failures set the stream error indicator; end of input sets EOF only when an actual end-of-file condition is observed. .SH NOTES LIBMPUIO is a companion to LIBMPU, not a replacement ABI for libc stdio. Do not cast between .B mpu_FILE * and libc .BR FILE * . .SH STREAM-STATE HARDENING All three native backends participate in the same active READING/WRITING state model. In particular, UCS-2 memory streams establish WRITING state on text output, allowing mpu_fflush(NULL) to publish an active mpu_open_memstream() just like the corresponding explicit stream flush. .PP Ordinary mpu_puts(), mpu_rewind(), and mpu_fileno() hold the stream mutex for their complete logical operation; their explicitly unlocked counterparts, where provided, remain caller-synchronized operations. Command-pipe streams are not eligible for mpu_freopen() and retain their required mpu_pclose() lifecycle. .PP Stream construction propagates recursive-mutex initialization errors directly, and a standard stream whose mutex could not be initialized is not inserted into the global stream list. File and cookie buffer replacement also clears directional pointers before an allocation failure can expose stale addresses. .SH STREAM-LIST LIFETIME HARDENING All-stream traversal uses a pinned snapshot instead of holding the global stream-list mutex while backend operations execute. This keeps descriptor, string, and cookie streams alive across each traversal while removing the global list lock from user-defined cookie callback execution. .PP If mpu_fclose() encounters a stream still present in an earlier snapshot, it removes that stream from the live list and completes backend shutdown, but defers destruction of the FILE object and recursive mutex until the final snapshot pin is released. A later snapshot cannot acquire a new pin on the unlinked stream. .PP Private UCS-2 streams created internally for sprintf/snprintf and sscanf engines are not members of the public global stream list. Public mpu_fmemopen() and mpu_open_memstream() streams remain registered and therefore continue to participate in mpu_fflush(NULL) and mpu_flushlbf() as appropriate. .SH MEMORY-SAFETY AND BUFFER-STATE HARDENING Descriptor and cookie setvbuf replacement is transactional with respect to allocation failure: replacement storage is prepared first, so a failed allocation does not destroy the previous usable buffer state. The unused internal _offset cache was removed; logical positions continue to be derived from the backend position together with buffered read/write and UCS-2 pushback state. .PP The project-root Makefile builds only the production shared library. Normal regression tests are built independently in tests/, and AddressSanitizer, UndefinedBehaviorSanitizer and ThreadSanitizer stress suites are built independently in tests/asan, tests/ubsan and tests/tsan. Each test directory builds its own local libmpuio.so and runs its programs with LD_LIBRARY_PATH=. Sanitizer instrumentation therefore never requires changing or rebuilding the production objects in another mode. .SH SEE ALSO .BR mpu_fopen (3), .BR mpu_fmemopen (3), .BR mpu_fopencookie (3), .BR mpu_fpurge (3), .BR mpu_perror (3), .BR mpu_fgetc (3), .BR mpu_fread (3), .BR mpu_fseek (3), .BR mpu_printf (3), .BR mpu_scanf (3), .BR mpu_flockfile (3), .BR mpu_unlocked (3) .SH INTERNAL SYMBOL VISIBILITY Internal cross-module declarations in the private LIBMPUIO headers are ordinary \fBextern\fP declarations. Internal symbol helpers are defined in \fBmpu-symbols.h\fP. In the C file that defines each private cross-module function or data object, \fB__mpu_hidden_decl(name)\fP is emitted after the definition. The macro applies GCC \fBvisibility("hidden")\fP only when both \fBSHARED\fP and \fBHAVE_HIDDEN_VISIBILITY_ATTRIBUTE\fP are defined; the latter is supplied by \fBconfig.h\fP. This keeps compiler-feature policy out of the private interface declarations. File-local helpers remain \fBstatic\fP. .PP This rule also covers the private jump tables and stream-list state: \fB__mpu_IO_file_jumps\fP, \fB__mpu_IO_str_jumps\fP, \fB__mpu_IO_list_all\fP, and \fB__mpu_IO_list_lock\fP. Therefore no symbol with the private \fB__mpu_\fP prefix is exported by the shared library. .PP The hidden declarations define an ABI boundary only; documented public \fBmpu_*\fP functions and public stream objects remain exported.