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