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