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