1 /*
2  * %CopyrightBegin%
3  *
4  * Copyright Ericsson AB 2005-2020. All Rights Reserved.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  * %CopyrightEnd%
19  */
20 
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 
25 #if defined(__sun) || defined(__sun__)
26     /* For flockfile(3c), putc_unlocked(3c), etc */
27     #define __EXTENSIONS__
28 #endif
29 
30 #include <string.h>
31 #include "erl_errno.h"
32 #ifdef __WIN32__
33 #	include <io.h>
34 #else
35 #	include <unistd.h>
36 #endif
37 #include "erl_printf.h"
38 #include "erl_printf_format.h"
39 
40 #ifdef DEBUG
41 #include <assert.h>
42 #define ASSERT(X) assert(X)
43 #else
44 #define ASSERT(X)
45 #endif
46 
47 #if defined(__WIN32__) && !defined(__GNUC__)
48 typedef int ssize_t;
49 #endif
50 
51 int (*erts_printf_stdout_func)(char *, va_list) = NULL;
52 int (*erts_printf_stderr_func)(char *, va_list) = NULL;
53 
54 int erts_printf_add_cr_to_stdout = 0;
55 int erts_printf_add_cr_to_stderr = 0;
56 
57 int (*erts_printf_block_fpe)(void) = NULL;
58 void (*erts_printf_unblock_fpe)(int) = NULL;
59 
60 #undef FLOCKFILE
61 #undef FUNLOCKFILE
62 #undef PUTC
63 #undef FWRITE
64 #undef PUTC_ON_SMALL_WRITES
65 
66 #if defined(HAVE_FLOCKFILE)
67 #	define FLOCKFILE(FP)	flockfile(FP)
68 #	define FUNLOCKFILE(FP)	funlockfile(FP)
69 #	ifdef HAVE_PUTC_UNLOCKED
70 #		define PUTC	putc_unlocked
71 #		define PUTC_ON_SMALL_WRITES
72 #	endif
73 #	ifdef HAVE_FWRITE_UNLOCKED
74 #		define FWRITE	fwrite_unlocked
75 #	endif
76 #else
77 #	define FLOCKFILE(FP)
78 #	define FUNLOCKFILE(FP)
79 #endif
80 #ifndef PUTC
81 #	define PUTC putc
82 #endif
83 #ifndef FWRITE
84 #	define FWRITE fwrite
85 #endif
86 
87 /* We use write for stdout and stderr as they could be
88    set to non-blocking by shell drivers, and non-blocking
89    FILE * functions work unpredictably as best */
90 static int
printf_putc(int c,FILE * stream)91 printf_putc(int c, FILE *stream) {
92     if ((FILE*)stream == stdout || (FILE*)stream == stderr) {
93         int fd = stream == stdout ? fileno(stdout) : fileno(stderr);
94         /* cast to a char here, because write expects bytes. */
95         unsigned char buf[1] = { c };
96         int res;
97         do {
98             res = write(fd, buf, 1);
99         } while (res == -1 && (errno == EAGAIN || errno == EINTR));
100         if (res == -1) return EOF;
101         return res;
102     }
103 
104     return PUTC(c, stream);
105 }
106 
107 static size_t
printf_fwrite(const void * ptr,size_t size,size_t nitems,FILE * stream)108 printf_fwrite(const void *ptr, size_t size, size_t nitems,
109           FILE *stream) {
110     if ((FILE*)stream == stdout || (FILE*)stream == stderr) {
111         int fd = stream == stdout ? fileno(stdout) : fileno(stderr);
112         int res;
113         do {
114             res = write(fd, ptr, size*nitems);
115         } while (res == -1 && (errno == EAGAIN || errno == EINTR));
116         if (res == -1) return 0;
117         return res;
118     }
119     return FWRITE(ptr, size, nitems, stream);
120 }
121 
122 static int
get_error_result(void)123 get_error_result(void)
124 {
125     int res = errno;
126     if (res <= 0)
127 	res = EIO;
128     return -res;
129 }
130 
131 
132 static int
write_f_add_cr(void * vfp,char * buf,size_t len)133 write_f_add_cr(void *vfp, char* buf, size_t len)
134 {
135     size_t i;
136     ASSERT(vfp);
137     for (i = 0; i < len; i++) {
138         if (buf[i] == '\n' && printf_putc('\r', (FILE *) vfp) == EOF)
139             return get_error_result();
140         if (printf_putc(buf[i], (FILE *) vfp) == EOF)
141             return get_error_result();
142     }
143     return len;
144 }
145 
146 int
erts_write_fp(void * vfp,char * buf,size_t len)147 erts_write_fp(void *vfp, char* buf, size_t len)
148 {
149     ASSERT(vfp);
150 #ifdef PUTC_ON_SMALL_WRITES
151     if (len <= 64) { /* Try to optimize writes of small bufs. */
152 	int i;
153 	for (i = 0; i < len; i++)
154 	    if (printf_putc(buf[i], (FILE *) vfp) == EOF)
155 		return get_error_result();
156     }
157     else
158 #endif
159     if (printf_fwrite((void *) buf, sizeof(char), len, (FILE *) vfp) != len)
160 	return get_error_result();
161     return len;
162 }
163 
164 int
erts_write_fd(void * vfdp,char * buf,size_t len)165 erts_write_fd(void *vfdp, char* buf, size_t len)
166 {
167     ssize_t size;
168     size_t res = len;
169     ASSERT(vfdp);
170 
171     while (len) {
172 	size = write(*((int *) vfdp), (void *) buf, len);
173 	if (size < 0) {
174 #ifdef EINTR
175 	    if (errno == EINTR)
176 		continue;
177 #endif
178 	    return get_error_result();
179 	}
180 	if (size > len)
181 	    return -EIO;
182 	len -= size;
183     }
184 
185     return res;
186 }
187 
188 static int
write_s(void * vwbufpp,char * bufp,size_t len)189 write_s(void *vwbufpp, char* bufp, size_t len)
190 {
191     char **wbufpp = (char **) vwbufpp;
192     ASSERT(wbufpp && *wbufpp);
193     ASSERT(len > 0);
194     memcpy((void *) *wbufpp, (void *) bufp, len);
195     *wbufpp += len;
196     return len;
197 }
198 
199 
200 typedef struct {
201     char *buf;
202     size_t len;
203 } write_sn_arg_t;
204 
205 static int
write_sn(void * vwsnap,char * buf,size_t len)206 write_sn(void *vwsnap, char* buf, size_t len)
207 {
208     int rv = 0;
209     write_sn_arg_t *wsnap = (write_sn_arg_t *) vwsnap;
210     ASSERT(wsnap);
211     ASSERT(len > 0);
212     if (wsnap->len > 0) {
213 	size_t sz = len;
214 	if (sz >= wsnap->len)
215 	    sz = wsnap->len;
216 	rv = (int)sz;
217 	memcpy((void *) wsnap->buf, (void *) buf, sz);
218 	wsnap->buf += sz;
219 	wsnap->len -= sz;
220 	return sz;
221     }
222     return rv;
223 }
224 
225 int
erts_write_ds(void * vdsbufp,char * buf,size_t len)226 erts_write_ds(void *vdsbufp, char* buf, size_t len)
227 {
228     erts_dsprintf_buf_t *dsbufp = (erts_dsprintf_buf_t *) vdsbufp;
229     size_t need_len = len + 1; /* Also trailing '\0' */
230     ASSERT(dsbufp);
231     ASSERT(len > 0);
232     ASSERT(dsbufp->str_len <= dsbufp->size);
233     if (need_len > dsbufp->size - dsbufp->str_len) {
234 	dsbufp = (*dsbufp->grow)(dsbufp, need_len);
235 	if (!dsbufp)
236 	    return -ENOMEM;
237     }
238     memcpy((void *) (dsbufp->str + dsbufp->str_len), (void *) buf, len);
239     dsbufp->str_len += len;
240     return len;
241 }
242 
243 int
erts_printf(const char * format,...)244 erts_printf(const char *format, ...)
245 {
246     int res;
247     va_list arglist;
248     va_start(arglist, format);
249     errno = 0;
250     if (erts_printf_stdout_func)
251 	res = (*erts_printf_stdout_func)((char *) format, arglist);
252     else {
253 	FLOCKFILE(stdout);
254 	res = erts_printf_format(erts_printf_add_cr_to_stdout
255 				 ? write_f_add_cr
256 				 : erts_write_fp,
257 				 (void *) stdout,
258 				 (char *) format,
259 				 arglist);
260 	FUNLOCKFILE(stdout);
261     }
262     va_end(arglist);
263     return res;
264 }
265 
266 int
erts_fprintf(FILE * filep,const char * format,...)267 erts_fprintf(FILE *filep, const char *format, ...)
268 {
269     int res;
270     va_list arglist;
271     va_start(arglist, format);
272     errno = 0;
273     if (erts_printf_stdout_func && filep == stdout)
274 	res = (*erts_printf_stdout_func)((char *) format, arglist);
275     else if (erts_printf_stderr_func && filep == stderr)
276 	res = (*erts_printf_stderr_func)((char *) format, arglist);
277     else {
278 	int (*fmt_f)(void*, char*, size_t);
279 	if (erts_printf_add_cr_to_stdout && filep == stdout)
280 	    fmt_f = write_f_add_cr;
281 	else if (erts_printf_add_cr_to_stderr && filep == stderr)
282 	    fmt_f = write_f_add_cr;
283 	else
284 	    fmt_f = erts_write_fp;
285 	FLOCKFILE(filep);
286 	res = erts_printf_format(fmt_f,(void *)filep,(char *)format,arglist);
287 	FUNLOCKFILE(filep);
288     }
289     va_end(arglist);
290     return res;
291 }
292 
293 int
erts_fdprintf(int fd,const char * format,...)294 erts_fdprintf(int fd, const char *format, ...)
295 {
296     int res;
297     va_list arglist;
298     va_start(arglist, format);
299     errno = 0;
300     res = erts_printf_format(erts_write_fd,(void *)&fd,(char *)format,arglist);
301     va_end(arglist);
302     return res;
303 }
304 
305 int
erts_sprintf(char * buf,const char * format,...)306 erts_sprintf(char *buf, const char *format, ...)
307 {
308     int res;
309     char *p = buf;
310     va_list arglist;
311     va_start(arglist, format);
312     errno = 0;
313     res = erts_printf_format(write_s, (void *) &p, (char *) format, arglist);
314     if (res < 0)
315 	buf[0] = '\0';
316     else
317 	buf[res] = '\0';
318     va_end(arglist);
319     return res;
320 }
321 
322 int
erts_snprintf(char * buf,size_t size,const char * format,...)323 erts_snprintf(char *buf, size_t size, const char *format, ...)
324 {
325     write_sn_arg_t wsnap;
326     int res;
327     va_list arglist;
328     if (size < 1)
329 	return -EINVAL;
330     wsnap.buf = buf;
331     wsnap.len = size-1; /* Always need room for trailing '\0' */
332     va_start(arglist, format);
333     errno = 0;
334     res = erts_printf_format(write_sn, (void *)&wsnap, (char *)format, arglist);
335     if (res < 0)
336 	buf[0] = '\0';
337     else if (res < size)
338 	buf[res] = '\0';
339     else
340 	buf[size-1] = '\0';
341     va_end(arglist);
342     return res;
343 }
344 
345 int
erts_dsprintf(erts_dsprintf_buf_t * dsbufp,const char * format,...)346 erts_dsprintf(erts_dsprintf_buf_t *dsbufp, const char *format, ...)
347 {
348     int res;
349     va_list arglist;
350     if (!dsbufp)
351 	return -EINVAL;
352     va_start(arglist, format);
353     errno = 0;
354     res = erts_printf_format(erts_write_ds, (void *)dsbufp, (char *)format, arglist);
355     if (dsbufp->str) {
356 	if (res < 0)
357 	    dsbufp->str[0] = '\0';
358 	else
359 	    dsbufp->str[dsbufp->str_len] = '\0';
360     }
361     va_end(arglist);
362     return res;
363 }
364 
365 /*
366  * Callback printf
367  */
erts_cbprintf(fmtfn_t cb_fn,void * cb_arg,const char * format,...)368 int erts_cbprintf(fmtfn_t cb_fn, void* cb_arg, const char* format, ...)
369 {
370     int res;
371     va_list arglist;
372     va_start(arglist, format);
373     errno = 0;
374     res = erts_printf_format(cb_fn, cb_arg, (char *)format, arglist);
375     va_end(arglist);
376     return res;
377 }
378 
379 int
erts_vprintf(const char * format,va_list arglist)380 erts_vprintf(const char *format, va_list arglist)
381 {
382     int res;
383     if (erts_printf_stdout_func)
384 	res = (*erts_printf_stdout_func)((char *) format, arglist);
385     else {
386 	errno = 0;
387 	res = erts_printf_format(erts_printf_add_cr_to_stdout
388 				 ? write_f_add_cr
389 				 : erts_write_fp,
390 				 (void *) stdout,
391 				 (char *) format,
392 				 arglist);
393     }
394     return res;
395 }
396 
397 int
erts_vfprintf(FILE * filep,const char * format,va_list arglist)398 erts_vfprintf(FILE *filep, const char *format, va_list arglist)
399 {
400     int res;
401     if (erts_printf_stdout_func && filep == stdout)
402 	res = (*erts_printf_stdout_func)((char *) format, arglist);
403     else if (erts_printf_stderr_func && filep == stderr)
404 	res = (*erts_printf_stderr_func)((char *) format, arglist);
405     else {
406 	int (*fmt_f)(void*, char*, size_t);
407 	errno = 0;
408 	if (erts_printf_add_cr_to_stdout && filep == stdout)
409 	    fmt_f = write_f_add_cr;
410 	else if (erts_printf_add_cr_to_stderr && filep == stderr)
411 	    fmt_f = write_f_add_cr;
412 	else
413 	    fmt_f = erts_write_fp;
414 	FLOCKFILE(filep);
415 	res = erts_printf_format(fmt_f,(void *)filep,(char *)format,arglist);
416 	FUNLOCKFILE(filep);
417     }
418     return res;
419 }
420 
421 int
erts_vfdprintf(int fd,const char * format,va_list arglist)422 erts_vfdprintf(int fd, const char *format, va_list arglist)
423 {
424     int res;
425     errno = 0;
426     res = erts_printf_format(erts_write_fd,(void *)&fd,(char *)format,arglist);
427     return res;
428 }
429 
430 int
erts_vsprintf(char * buf,const char * format,va_list arglist)431 erts_vsprintf(char *buf, const char *format, va_list arglist)
432 {
433     int res;
434     char *p = buf;
435     errno = 0;
436     res = erts_printf_format(write_s, (void *) &p, (char *) format, arglist);
437     if (res < 0)
438 	buf[0] = '\0';
439     else
440 	buf[res] = '\0';
441     return res;
442 }
443 
444 int
erts_vsnprintf(char * buf,size_t size,const char * format,va_list arglist)445 erts_vsnprintf(char *buf, size_t size, const char *format,  va_list arglist)
446 {
447     write_sn_arg_t wsnap;
448     int res;
449     if (size < 1)
450 	return -EINVAL;
451     wsnap.buf = buf;
452     wsnap.len = size-1; /* Always need room for trailing '\0' */
453     errno = 0;
454     res = erts_printf_format(write_sn, (void *)&wsnap, (char *)format, arglist);
455     if (res < 0)
456 	buf[0] = '\0';
457     else if (res < size)
458 	buf[res] = '\0';
459     else
460 	buf[size-1] = '\0';
461     return res;
462 }
463 
464 int
erts_vdsprintf(erts_dsprintf_buf_t * dsbufp,const char * format,va_list arglist)465 erts_vdsprintf(erts_dsprintf_buf_t *dsbufp, const char *format, va_list arglist)
466 {
467     int res;
468     if (!dsbufp)
469 	return -EINVAL;
470     errno = 0;
471     res = erts_printf_format(erts_write_ds, (void *)dsbufp, (char *)format, arglist);
472     if (dsbufp->str) {
473 	if (res < 0)
474 	    dsbufp->str[0] = '\0';
475 	else
476 	    dsbufp->str[dsbufp->str_len] = '\0';
477     }
478     return res;
479 }
480 
481 int
erts_vcbprintf(fmtfn_t cb_fn,void * cb_arg,const char * format,va_list arglist)482 erts_vcbprintf(fmtfn_t cb_fn, void* cb_arg, const char *format, va_list arglist)
483 {
484     errno = 0;
485     return erts_printf_format(cb_fn, cb_arg, (char *)format, arglist);
486 }
487