1*549b59edSchristos /* $NetBSD: stdio.c,v 1.3 2021/08/14 16:14:55 christos Exp $ */
24e6df137Slukem
3d11b170bStron /* $OpenLDAP$ */
42de962bdSlukem /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
52de962bdSlukem *
6*549b59edSchristos * Copyright 1998-2021 The OpenLDAP Foundation.
72de962bdSlukem * All rights reserved.
82de962bdSlukem *
92de962bdSlukem * Redistribution and use in source and binary forms, with or without
102de962bdSlukem * modification, are permitted only as authorized by the OpenLDAP
112de962bdSlukem * Public License.
122de962bdSlukem *
132de962bdSlukem * A copy of this license is available in the file LICENSE in the
142de962bdSlukem * top-level directory of the distribution or, alternatively, at
152de962bdSlukem * <http://www.OpenLDAP.org/license.html>.
162de962bdSlukem */
172de962bdSlukem
18376af7d7Schristos #include <sys/cdefs.h>
19*549b59edSchristos __RCSID("$NetBSD: stdio.c,v 1.3 2021/08/14 16:14:55 christos Exp $");
20376af7d7Schristos
212de962bdSlukem #include "portable.h"
222de962bdSlukem
232de962bdSlukem #include <stdio.h>
242de962bdSlukem #include <ac/stdarg.h>
252de962bdSlukem #include <ac/string.h>
262de962bdSlukem #include <ac/ctype.h>
272de962bdSlukem #include <lutil.h>
282de962bdSlukem
292de962bdSlukem #if !defined(HAVE_VSNPRINTF) && !defined(HAVE_EBCDIC)
302de962bdSlukem /* Write at most n characters to the buffer in str, return the
312de962bdSlukem * number of chars written or -1 if the buffer would have been
322de962bdSlukem * overflowed.
332de962bdSlukem *
342de962bdSlukem * This is portable to any POSIX-compliant system. We use pipe()
352de962bdSlukem * to create a valid file descriptor, and then fdopen() it to get
362de962bdSlukem * a valid FILE pointer. The user's buffer and size are assigned
372de962bdSlukem * to the FILE pointer using setvbuf. Then we close the read side
382de962bdSlukem * of the pipe to invalidate the descriptor.
392de962bdSlukem *
402de962bdSlukem * If the write arguments all fit into size n, the write will
412de962bdSlukem * return successfully. If the write is too large, the stdio
422de962bdSlukem * buffer will need to be flushed to the underlying file descriptor.
432de962bdSlukem * The flush will fail because it is attempting to write to a
442de962bdSlukem * broken pipe, and the write will be terminated.
452de962bdSlukem * -- hyc, 2002-07-19
462de962bdSlukem */
472de962bdSlukem /* This emulation uses vfprintf; on OS/390 we're also emulating
482de962bdSlukem * that function so it's more efficient just to have a separate
492de962bdSlukem * version of vsnprintf there.
502de962bdSlukem */
512de962bdSlukem #include <ac/signal.h>
ber_pvt_vsnprintf(char * str,size_t n,const char * fmt,va_list ap)522de962bdSlukem int ber_pvt_vsnprintf( char *str, size_t n, const char *fmt, va_list ap )
532de962bdSlukem {
542de962bdSlukem int fds[2], res;
552de962bdSlukem FILE *f;
562de962bdSlukem RETSIGTYPE (*sig)();
572de962bdSlukem
582de962bdSlukem if (pipe( fds )) return -1;
592de962bdSlukem
602de962bdSlukem f = fdopen( fds[1], "w" );
612de962bdSlukem if ( !f ) {
622de962bdSlukem close( fds[1] );
632de962bdSlukem close( fds[0] );
642de962bdSlukem return -1;
652de962bdSlukem }
662de962bdSlukem setvbuf( f, str, _IOFBF, n );
672de962bdSlukem sig = signal( SIGPIPE, SIG_IGN );
682de962bdSlukem close( fds[0] );
692de962bdSlukem
702de962bdSlukem res = vfprintf( f, fmt, ap );
712de962bdSlukem
722de962bdSlukem fclose( f );
732de962bdSlukem signal( SIGPIPE, sig );
742de962bdSlukem if ( res > 0 && res < n ) {
752de962bdSlukem res = vsprintf( str, fmt, ap );
762de962bdSlukem }
772de962bdSlukem return res;
782de962bdSlukem }
792de962bdSlukem #endif
802de962bdSlukem
812de962bdSlukem #ifndef HAVE_SNPRINTF
ber_pvt_snprintf(char * str,size_t n,const char * fmt,...)822de962bdSlukem int ber_pvt_snprintf( char *str, size_t n, const char *fmt, ... )
832de962bdSlukem {
842de962bdSlukem va_list ap;
852de962bdSlukem int res;
862de962bdSlukem
872de962bdSlukem va_start( ap, fmt );
882de962bdSlukem res = vsnprintf( str, n, fmt, ap );
892de962bdSlukem va_end( ap );
902de962bdSlukem return res;
912de962bdSlukem }
922de962bdSlukem #endif /* !HAVE_SNPRINTF */
932de962bdSlukem
942de962bdSlukem #ifdef HAVE_EBCDIC
952de962bdSlukem /* stdio replacements with ASCII/EBCDIC translation for OS/390.
962de962bdSlukem * The OS/390 port depends on the CONVLIT compiler option being
972de962bdSlukem * used to force character and string literals to be compiled in
982de962bdSlukem * ISO8859-1, and the __LIBASCII cpp symbol to be defined to use the
992de962bdSlukem * OS/390 ASCII-compatibility library. This library only supplies
1002de962bdSlukem * an ASCII version of sprintf, so other needed functions are
1012de962bdSlukem * provided here.
1022de962bdSlukem *
1032de962bdSlukem * All of the internal character manipulation is done in ASCII,
1042de962bdSlukem * but file I/O is EBCDIC, so we catch any stdio reading/writing
1052de962bdSlukem * of files here and do the translations.
1062de962bdSlukem */
1072de962bdSlukem
1082de962bdSlukem #undef fputs
1092de962bdSlukem #undef fgets
1102de962bdSlukem
ber_pvt_fgets(char * s,int n,FILE * fp)1112de962bdSlukem char *ber_pvt_fgets( char *s, int n, FILE *fp )
1122de962bdSlukem {
1132de962bdSlukem s = (char *)fgets( s, n, fp );
1142de962bdSlukem if ( s ) __etoa( s );
1152de962bdSlukem return s;
1162de962bdSlukem }
1172de962bdSlukem
ber_pvt_fputs(const char * str,FILE * fp)1182de962bdSlukem int ber_pvt_fputs( const char *str, FILE *fp )
1192de962bdSlukem {
1202de962bdSlukem char buf[8192];
1212de962bdSlukem
1222de962bdSlukem strncpy( buf, str, sizeof(buf) );
1232de962bdSlukem __atoe( buf );
1242de962bdSlukem return fputs( buf, fp );
1252de962bdSlukem }
1262de962bdSlukem
1272de962bdSlukem /* The __LIBASCII doesn't include a working vsprintf, so we make do
1282de962bdSlukem * using just sprintf. This is a very simplistic parser that looks for
1292de962bdSlukem * format strings and uses sprintf to process them one at a time.
1302de962bdSlukem * Literal text is just copied straight to the destination.
1312de962bdSlukem * The result is appended to the destination string. The parser
1322de962bdSlukem * recognizes field-width specifiers and the 'l' qualifier; it
1332de962bdSlukem * may need to be extended to recognize other qualifiers but so
1342de962bdSlukem * far this seems to be enough.
1352de962bdSlukem */
ber_pvt_vsnprintf(char * str,size_t n,const char * fmt,va_list ap)1362de962bdSlukem int ber_pvt_vsnprintf( char *str, size_t n, const char *fmt, va_list ap )
1372de962bdSlukem {
1382de962bdSlukem char *ptr, *pct, *s2, *f2, *end;
1392de962bdSlukem char fm2[64];
1402de962bdSlukem int len, rem;
1412de962bdSlukem
1422de962bdSlukem ptr = (char *)fmt;
1432de962bdSlukem s2 = str;
1442de962bdSlukem fm2[0] = '%';
1452de962bdSlukem if (n) {
1462de962bdSlukem end = str + n;
1472de962bdSlukem } else {
1482de962bdSlukem end = NULL;
1492de962bdSlukem }
1502de962bdSlukem
1512de962bdSlukem for (pct = strchr(ptr, '%'); pct; pct = strchr(ptr, '%')) {
1522de962bdSlukem len = pct-ptr;
1532de962bdSlukem if (end) {
1542de962bdSlukem rem = end-s2;
1552de962bdSlukem if (rem < 1) return -1;
1562de962bdSlukem if (rem < len) len = rem;
1572de962bdSlukem }
1582de962bdSlukem s2 = lutil_strncopy( s2, ptr, len );
1592de962bdSlukem /* Did we cheat the length above? If so, bail out */
1602de962bdSlukem if (len < pct-ptr) return -1;
1612de962bdSlukem for (pct++, f2 = fm2+1; isdigit(*pct);) *f2++ = *pct++;
1622de962bdSlukem if (*pct == 'l') *f2++ = *pct++;
1632de962bdSlukem if (*pct == '%') {
1642de962bdSlukem *s2++ = '%';
1652de962bdSlukem } else {
1662de962bdSlukem *f2++ = *pct;
1672de962bdSlukem *f2 = '\0';
1682de962bdSlukem if (*pct == 's') {
1692de962bdSlukem char *ss = va_arg(ap, char *);
1702de962bdSlukem /* Attempt to limit sprintf output. This
1712de962bdSlukem * may be thrown off if field widths were
1722de962bdSlukem * specified for this string.
1732de962bdSlukem *
1742de962bdSlukem * If it looks like the string is too
1752de962bdSlukem * long for the remaining buffer, bypass
1762de962bdSlukem * sprintf and just copy what fits, then
1772de962bdSlukem * quit.
1782de962bdSlukem */
1792de962bdSlukem if (end && strlen(ss) > (rem=end-s2)) {
1802de962bdSlukem strncpy(s2, ss, rem);
1812de962bdSlukem return -1;
1822de962bdSlukem } else {
1832de962bdSlukem s2 += sprintf(s2, fm2, ss);
1842de962bdSlukem }
1852de962bdSlukem } else {
1862de962bdSlukem s2 += sprintf(s2, fm2, va_arg(ap, int));
1872de962bdSlukem }
1882de962bdSlukem }
1892de962bdSlukem ptr = pct + 1;
1902de962bdSlukem }
1912de962bdSlukem if (end) {
1922de962bdSlukem rem = end-s2;
1932de962bdSlukem if (rem > 0) {
1942de962bdSlukem len = strlen(ptr);
1952de962bdSlukem s2 = lutil_strncopy( s2, ptr, rem );
1962de962bdSlukem rem -= len;
1972de962bdSlukem }
1982de962bdSlukem if (rem < 0) return -1;
1992de962bdSlukem } else {
2002de962bdSlukem s2 = lutil_strcopy( s2, ptr );
2012de962bdSlukem }
2022de962bdSlukem return s2 - str;
2032de962bdSlukem }
2042de962bdSlukem
ber_pvt_vsprintf(char * str,const char * fmt,va_list ap)2052de962bdSlukem int ber_pvt_vsprintf( char *str, const char *fmt, va_list ap )
2062de962bdSlukem {
2072de962bdSlukem return vsnprintf( str, 0, fmt, ap );
2082de962bdSlukem }
2092de962bdSlukem
2102de962bdSlukem /* The fixed buffer size here is a problem, we don't know how
2112de962bdSlukem * to flush the buffer and keep printing if the msg is too big.
2122de962bdSlukem * Hopefully we never try to write something bigger than this
2132de962bdSlukem * in a log msg...
2142de962bdSlukem */
ber_pvt_vfprintf(FILE * fp,const char * fmt,va_list ap)2152de962bdSlukem int ber_pvt_vfprintf( FILE *fp, const char *fmt, va_list ap )
2162de962bdSlukem {
2172de962bdSlukem char buf[8192];
2182de962bdSlukem int res;
2192de962bdSlukem
2202de962bdSlukem vsnprintf( buf, sizeof(buf), fmt, ap );
2212de962bdSlukem __atoe( buf );
2222de962bdSlukem res = fputs( buf, fp );
2232de962bdSlukem if (res == EOF) res = -1;
2242de962bdSlukem return res;
2252de962bdSlukem }
2262de962bdSlukem
ber_pvt_printf(const char * fmt,...)2272de962bdSlukem int ber_pvt_printf( const char *fmt, ... )
2282de962bdSlukem {
2292de962bdSlukem va_list ap;
2302de962bdSlukem int res;
2312de962bdSlukem
2322de962bdSlukem va_start( ap, fmt );
2332de962bdSlukem res = ber_pvt_vfprintf( stdout, fmt, ap );
2342de962bdSlukem va_end( ap );
2352de962bdSlukem return res;
2362de962bdSlukem }
2372de962bdSlukem
ber_pvt_fprintf(FILE * fp,const char * fmt,...)2382de962bdSlukem int ber_pvt_fprintf( FILE *fp, const char *fmt, ... )
2392de962bdSlukem {
2402de962bdSlukem va_list ap;
2412de962bdSlukem int res;
2422de962bdSlukem
2432de962bdSlukem va_start( ap, fmt );
2442de962bdSlukem res = ber_pvt_vfprintf( fp, fmt, ap );
2452de962bdSlukem va_end( ap );
2462de962bdSlukem return res;
2472de962bdSlukem }
2482de962bdSlukem #endif
249