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