xref: /netbsd-src/external/bsd/openldap/dist/libraries/liblber/stdio.c (revision 549b59ed3ccf0d36d3097190a0db27b770f3a839)
1 /*	$NetBSD: stdio.c,v 1.3 2021/08/14 16:14:55 christos Exp $	*/
2 
3 /* $OpenLDAP$ */
4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5  *
6  * Copyright 1998-2021 The OpenLDAP Foundation.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted only as authorized by the OpenLDAP
11  * Public License.
12  *
13  * A copy of this license is available in the file LICENSE in the
14  * top-level directory of the distribution or, alternatively, at
15  * <http://www.OpenLDAP.org/license.html>.
16  */
17 
18 #include <sys/cdefs.h>
19 __RCSID("$NetBSD: stdio.c,v 1.3 2021/08/14 16:14:55 christos Exp $");
20 
21 #include "portable.h"
22 
23 #include <stdio.h>
24 #include <ac/stdarg.h>
25 #include <ac/string.h>
26 #include <ac/ctype.h>
27 #include <lutil.h>
28 
29 #if !defined(HAVE_VSNPRINTF) && !defined(HAVE_EBCDIC)
30 /* Write at most n characters to the buffer in str, return the
31  * number of chars written or -1 if the buffer would have been
32  * overflowed.
33  *
34  * This is portable to any POSIX-compliant system. We use pipe()
35  * to create a valid file descriptor, and then fdopen() it to get
36  * a valid FILE pointer. The user's buffer and size are assigned
37  * to the FILE pointer using setvbuf. Then we close the read side
38  * of the pipe to invalidate the descriptor.
39  *
40  * If the write arguments all fit into size n, the write will
41  * return successfully. If the write is too large, the stdio
42  * buffer will need to be flushed to the underlying file descriptor.
43  * The flush will fail because it is attempting to write to a
44  * broken pipe, and the write will be terminated.
45  * -- hyc, 2002-07-19
46  */
47 /* This emulation uses vfprintf; on OS/390 we're also emulating
48  * that function so it's more efficient just to have a separate
49  * version of vsnprintf there.
50  */
51 #include <ac/signal.h>
ber_pvt_vsnprintf(char * str,size_t n,const char * fmt,va_list ap)52 int ber_pvt_vsnprintf( char *str, size_t n, const char *fmt, va_list ap )
53 {
54 	int fds[2], res;
55 	FILE *f;
56 	RETSIGTYPE (*sig)();
57 
58 	if (pipe( fds )) return -1;
59 
60 	f = fdopen( fds[1], "w" );
61 	if ( !f ) {
62 		close( fds[1] );
63 		close( fds[0] );
64 		return -1;
65 	}
66 	setvbuf( f, str, _IOFBF, n );
67 	sig = signal( SIGPIPE, SIG_IGN );
68 	close( fds[0] );
69 
70 	res = vfprintf( f, fmt, ap );
71 
72 	fclose( f );
73 	signal( SIGPIPE, sig );
74 	if ( res > 0 && res < n ) {
75 		res = vsprintf( str, fmt, ap );
76 	}
77 	return res;
78 }
79 #endif
80 
81 #ifndef HAVE_SNPRINTF
ber_pvt_snprintf(char * str,size_t n,const char * fmt,...)82 int ber_pvt_snprintf( char *str, size_t n, const char *fmt, ... )
83 {
84 	va_list ap;
85 	int res;
86 
87 	va_start( ap, fmt );
88 	res = vsnprintf( str, n, fmt, ap );
89 	va_end( ap );
90 	return res;
91 }
92 #endif /* !HAVE_SNPRINTF */
93 
94 #ifdef HAVE_EBCDIC
95 /* stdio replacements with ASCII/EBCDIC translation for OS/390.
96  * The OS/390 port depends on the CONVLIT compiler option being
97  * used to force character and string literals to be compiled in
98  * ISO8859-1, and the __LIBASCII cpp symbol to be defined to use the
99  * OS/390 ASCII-compatibility library. This library only supplies
100  * an ASCII version of sprintf, so other needed functions are
101  * provided here.
102  *
103  * All of the internal character manipulation is done in ASCII,
104  * but file I/O is EBCDIC, so we catch any stdio reading/writing
105  * of files here and do the translations.
106  */
107 
108 #undef fputs
109 #undef fgets
110 
ber_pvt_fgets(char * s,int n,FILE * fp)111 char *ber_pvt_fgets( char *s, int n, FILE *fp )
112 {
113 	s = (char *)fgets( s, n, fp );
114 	if ( s ) __etoa( s );
115 	return s;
116 }
117 
ber_pvt_fputs(const char * str,FILE * fp)118 int ber_pvt_fputs( const char *str, FILE *fp )
119 {
120 	char buf[8192];
121 
122 	strncpy( buf, str, sizeof(buf) );
123 	__atoe( buf );
124 	return fputs( buf, fp );
125 }
126 
127 /* The __LIBASCII doesn't include a working vsprintf, so we make do
128  * using just sprintf. This is a very simplistic parser that looks for
129  * format strings and uses sprintf to process them one at a time.
130  * Literal text is just copied straight to the destination.
131  * The result is appended to the destination string. The parser
132  * recognizes field-width specifiers and the 'l' qualifier; it
133  * may need to be extended to recognize other qualifiers but so
134  * far this seems to be enough.
135  */
ber_pvt_vsnprintf(char * str,size_t n,const char * fmt,va_list ap)136 int ber_pvt_vsnprintf( char *str, size_t n, const char *fmt, va_list ap )
137 {
138 	char *ptr, *pct, *s2, *f2, *end;
139 	char fm2[64];
140 	int len, rem;
141 
142 	ptr = (char *)fmt;
143 	s2 = str;
144 	fm2[0] = '%';
145 	if (n) {
146 		end = str + n;
147 	} else {
148 		end = NULL;
149 	}
150 
151 	for (pct = strchr(ptr, '%'); pct; pct = strchr(ptr, '%')) {
152 		len = pct-ptr;
153 		if (end) {
154 			rem = end-s2;
155 			if (rem < 1) return -1;
156 			if (rem < len) len = rem;
157 		}
158 		s2 = lutil_strncopy( s2, ptr, len );
159 		/* Did we cheat the length above? If so, bail out */
160 		if (len < pct-ptr) return -1;
161 		for (pct++, f2 = fm2+1; isdigit(*pct);) *f2++ = *pct++;
162 		if (*pct == 'l') *f2++ = *pct++;
163 		if (*pct == '%') {
164 			*s2++ = '%';
165 		} else {
166 			*f2++ = *pct;
167 			*f2 = '\0';
168 			if (*pct == 's') {
169 				char *ss = va_arg(ap, char *);
170 				/* Attempt to limit sprintf output. This
171 				 * may be thrown off if field widths were
172 				 * specified for this string.
173 				 *
174 				 * If it looks like the string is too
175 				 * long for the remaining buffer, bypass
176 				 * sprintf and just copy what fits, then
177 				 * quit.
178 				 */
179 				if (end && strlen(ss) > (rem=end-s2)) {
180 					strncpy(s2, ss, rem);
181 					return -1;
182 				} else {
183 					s2 += sprintf(s2, fm2, ss);
184 				}
185 			} else {
186 				s2 += sprintf(s2, fm2, va_arg(ap, int));
187 			}
188 		}
189 		ptr = pct + 1;
190 	}
191 	if (end) {
192 		rem = end-s2;
193 		if (rem > 0) {
194 			len = strlen(ptr);
195 			s2 = lutil_strncopy( s2, ptr, rem );
196 			rem -= len;
197 		}
198 		if (rem < 0) return -1;
199 	} else {
200 		s2 = lutil_strcopy( s2, ptr );
201 	}
202 	return s2 - str;
203 }
204 
ber_pvt_vsprintf(char * str,const char * fmt,va_list ap)205 int ber_pvt_vsprintf( char *str, const char *fmt, va_list ap )
206 {
207 	return vsnprintf( str, 0, fmt, ap );
208 }
209 
210 /* The fixed buffer size here is a problem, we don't know how
211  * to flush the buffer and keep printing if the msg is too big.
212  * Hopefully we never try to write something bigger than this
213  * in a log msg...
214  */
ber_pvt_vfprintf(FILE * fp,const char * fmt,va_list ap)215 int ber_pvt_vfprintf( FILE *fp, const char *fmt, va_list ap )
216 {
217 	char buf[8192];
218 	int res;
219 
220 	vsnprintf( buf, sizeof(buf), fmt, ap );
221 	__atoe( buf );
222 	res = fputs( buf, fp );
223 	if (res == EOF) res = -1;
224 	return res;
225 }
226 
ber_pvt_printf(const char * fmt,...)227 int ber_pvt_printf( const char *fmt, ... )
228 {
229 	va_list ap;
230 	int res;
231 
232 	va_start( ap, fmt );
233 	res = ber_pvt_vfprintf( stdout, fmt, ap );
234 	va_end( ap );
235 	return res;
236 }
237 
ber_pvt_fprintf(FILE * fp,const char * fmt,...)238 int ber_pvt_fprintf( FILE *fp, const char *fmt, ... )
239 {
240 	va_list ap;
241 	int res;
242 
243 	va_start( ap, fmt );
244 	res = ber_pvt_vfprintf( fp, fmt, ap );
245 	va_end( ap );
246 	return res;
247 }
248 #endif
249