xref: /netbsd-src/external/bsd/openldap/dist/libraries/liblber/stdio.c (revision 549b59ed3ccf0d36d3097190a0db27b770f3a839)
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