aboutsummaryrefslogtreecommitdiffstats
path: root/erts/lib_src/common/erl_printf.c
diff options
context:
space:
mode:
Diffstat (limited to 'erts/lib_src/common/erl_printf.c')
-rw-r--r--erts/lib_src/common/erl_printf.c427
1 files changed, 427 insertions, 0 deletions
diff --git a/erts/lib_src/common/erl_printf.c b/erts/lib_src/common/erl_printf.c
new file mode 100644
index 0000000000..72d18ab6f1
--- /dev/null
+++ b/erts/lib_src/common/erl_printf.c
@@ -0,0 +1,427 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2005-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/* Without this, variable argument lists break on VxWorks */
+#ifdef VXWORKS
+#include <vxWorks.h>
+#endif
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+#include "erl_errno.h"
+#ifdef __WIN32__
+# include <io.h>
+#else
+# include <unistd.h>
+#endif
+#include "erl_printf.h"
+#include "erl_printf_format.h"
+
+#ifdef DEBUG
+#include <assert.h>
+#define ASSERT(X) assert(X)
+#else
+#define ASSERT(X)
+#endif
+
+#if defined(__WIN32__) && !defined(__GNUC__)
+typedef int ssize_t;
+#endif
+
+int (*erts_printf_stdout_func)(char *, va_list) = NULL;
+int (*erts_printf_stderr_func)(char *, va_list) = NULL;
+
+int erts_printf_add_cr_to_stdout = 0;
+int erts_printf_add_cr_to_stderr = 0;
+
+int (*erts_printf_block_fpe)(void) = NULL;
+void (*erts_printf_unblock_fpe)(int) = NULL;
+
+#undef FLOCKFILE
+#undef FUNLOCKFILE
+#undef PUTC
+#undef FWRITE
+#undef PUTC_ON_SMALL_WRITES
+
+#if defined(USE_THREADS) && defined(HAVE_FLOCKFILE)
+# define FLOCKFILE(FP) flockfile(FP)
+# define FUNLOCKFILE(FP) funlockfile(FP)
+# ifdef HAVE_PUTC_UNLOCKED
+# define PUTC putc_unlocked
+# define PUTC_ON_SMALL_WRITES
+# endif
+# ifdef HAVE_FWRITE_UNLOCKED
+# define FWRITE fwrite_unlocked
+# endif
+#endif
+#if !defined(USE_THREADS) && defined(putc) && !defined(fwrite)
+# define PUTC_ON_SMALL_WRITES
+#endif
+#if !defined(FLOCKFILE) || !defined(FUNLOCKFILE)
+# define FLOCKFILE(FP)
+# define FUNLOCKFILE(FP)
+#endif
+#ifndef PUTC
+# define PUTC putc
+#endif
+#ifndef FWRITE
+# define FWRITE fwrite
+#endif
+
+static int
+get_error_result(void)
+{
+ int res = errno;
+ if (res <= 0)
+ res = EIO;
+ return -res;
+}
+
+
+static int
+write_f_add_cr(void *vfp, char* buf, size_t len)
+{
+ size_t i;
+ ASSERT(vfp);
+ for (i = 0; i < len; i++) {
+ if (buf[i] == '\n' && PUTC('\r', (FILE *) vfp) == EOF)
+ return get_error_result();
+ if (PUTC(buf[i], (FILE *) vfp) == EOF)
+ return get_error_result();
+ }
+ return 0;
+}
+
+static int
+write_f(void *vfp, char* buf, size_t len)
+{
+ ASSERT(vfp);
+#ifdef PUTC_ON_SMALL_WRITES
+ if (len <= 64) { /* Try to optimize writes of small bufs. */
+ int i;
+ for (i = 0; i < len; i++)
+ if (PUTC(buf[i], (FILE *) vfp) == EOF)
+ return get_error_result();
+ }
+ else
+#endif
+ if (FWRITE((void *) buf, sizeof(char), len, (FILE *) vfp) != len)
+ return get_error_result();
+ return 0;
+}
+
+static int
+write_fd(void *vfdp, char* buf, size_t len)
+{
+ ssize_t size;
+ ASSERT(vfdp);
+
+ while (len) {
+ size = write(*((int *) vfdp), (void *) buf, len);
+ if (size < 0) {
+#ifdef EINTR
+ if (errno == EINTR)
+ continue;
+#endif
+ return get_error_result();
+ }
+ if (size > len)
+ return -EIO;
+ len -= size;
+ }
+
+ return 0;
+}
+
+static int
+write_s(void *vwbufpp, char* bufp, size_t len)
+{
+ char **wbufpp = (char **) vwbufpp;
+ ASSERT(wbufpp && *wbufpp);
+ ASSERT(len > 0);
+ memcpy((void *) *wbufpp, (void *) bufp, len);
+ *wbufpp += len;
+ return 0;
+}
+
+
+typedef struct {
+ char *buf;
+ size_t len;
+} write_sn_arg_t;
+
+static int
+write_sn(void *vwsnap, char* buf, size_t len)
+{
+ write_sn_arg_t *wsnap = (write_sn_arg_t *) vwsnap;
+ ASSERT(wsnap);
+ ASSERT(len > 0);
+ if (wsnap->len > 0) {
+ size_t sz = len;
+ if (sz >= wsnap->len)
+ sz = wsnap->len;
+ memcpy((void *) wsnap->buf, (void *) buf, sz);
+ wsnap->buf += sz;
+ wsnap->len -= sz;
+ }
+ return 0;
+}
+
+static int
+write_ds(void *vdsbufp, char* buf, size_t len)
+{
+ erts_dsprintf_buf_t *dsbufp = (erts_dsprintf_buf_t *) vdsbufp;
+ size_t need_len = len + 1; /* Also trailing '\0' */
+ ASSERT(dsbufp);
+ ASSERT(len > 0);
+ ASSERT(dsbufp->str_len <= dsbufp->size);
+ if (need_len > dsbufp->size - dsbufp->str_len) {
+ dsbufp = (*dsbufp->grow)(dsbufp, need_len);
+ if (!dsbufp)
+ return -ENOMEM;
+ }
+ memcpy((void *) (dsbufp->str + dsbufp->str_len), (void *) buf, len);
+ dsbufp->str_len += len;
+ return 0;
+}
+
+int
+erts_printf(const char *format, ...)
+{
+ int res;
+ va_list arglist;
+ va_start(arglist, format);
+ errno = 0;
+ if (erts_printf_stdout_func)
+ res = (*erts_printf_stdout_func)((char *) format, arglist);
+ else {
+ FLOCKFILE(stdout);
+ res = erts_printf_format(erts_printf_add_cr_to_stdout
+ ? write_f_add_cr
+ : write_f,
+ (void *) stdout,
+ (char *) format,
+ arglist);
+ FUNLOCKFILE(stdout);
+ }
+ va_end(arglist);
+ return res;
+}
+
+int
+erts_fprintf(FILE *filep, const char *format, ...)
+{
+ int res;
+ va_list arglist;
+ va_start(arglist, format);
+ errno = 0;
+ if (erts_printf_stdout_func && filep == stdout)
+ res = (*erts_printf_stdout_func)((char *) format, arglist);
+ else if (erts_printf_stderr_func && filep == stderr)
+ res = (*erts_printf_stderr_func)((char *) format, arglist);
+ else {
+ int (*fmt_f)(void*, char*, size_t);
+ if (erts_printf_add_cr_to_stdout && filep == stdout)
+ fmt_f = write_f_add_cr;
+ else if (erts_printf_add_cr_to_stderr && filep == stderr)
+ fmt_f = write_f_add_cr;
+ else
+ fmt_f = write_f;
+ FLOCKFILE(filep);
+ res = erts_printf_format(fmt_f,(void *)filep,(char *)format,arglist);
+ FUNLOCKFILE(filep);
+ }
+ va_end(arglist);
+ return res;
+}
+
+int
+erts_fdprintf(int fd, const char *format, ...)
+{
+ int res;
+ va_list arglist;
+ va_start(arglist, format);
+ errno = 0;
+ res = erts_printf_format(write_fd,(void *)&fd,(char *)format,arglist);
+ va_end(arglist);
+ return res;
+}
+
+int
+erts_sprintf(char *buf, const char *format, ...)
+{
+ int res;
+ char *p = buf;
+ va_list arglist;
+ va_start(arglist, format);
+ errno = 0;
+ res = erts_printf_format(write_s, (void *) &p, (char *) format, arglist);
+ if (res < 0)
+ buf[0] = '\0';
+ else
+ buf[res] = '\0';
+ va_end(arglist);
+ return res;
+}
+
+int
+erts_snprintf(char *buf, size_t size, const char *format, ...)
+{
+ write_sn_arg_t wsnap;
+ int res;
+ va_list arglist;
+ if (size < 1)
+ return -EINVAL;
+ wsnap.buf = buf;
+ wsnap.len = size-1; /* Always need room for trailing '\0' */
+ va_start(arglist, format);
+ errno = 0;
+ res = erts_printf_format(write_sn, (void *)&wsnap, (char *)format, arglist);
+ if (res < 0)
+ buf[0] = '\0';
+ else if (res < size)
+ buf[res] = '\0';
+ else
+ buf[size-1] = '\0';
+ va_end(arglist);
+ return res;
+}
+
+int
+erts_dsprintf(erts_dsprintf_buf_t *dsbufp, const char *format, ...)
+{
+ int res;
+ va_list arglist;
+ if (!dsbufp)
+ return -EINVAL;
+ va_start(arglist, format);
+ errno = 0;
+ res = erts_printf_format(write_ds, (void *)dsbufp, (char *)format, arglist);
+ if (dsbufp->str) {
+ if (res < 0)
+ dsbufp->str[0] = '\0';
+ else
+ dsbufp->str[dsbufp->str_len] = '\0';
+ }
+ va_end(arglist);
+ return res;
+}
+
+int
+erts_vprintf(const char *format, va_list arglist)
+{
+ int res;
+ if (erts_printf_stdout_func)
+ res = (*erts_printf_stdout_func)((char *) format, arglist);
+ else {
+ errno = 0;
+ res = erts_printf_format(erts_printf_add_cr_to_stdout
+ ? write_f_add_cr
+ : write_f,
+ (void *) stdout,
+ (char *) format,
+ arglist);
+ }
+ return res;
+}
+
+int
+erts_vfprintf(FILE *filep, const char *format, va_list arglist)
+{
+ int res;
+ if (erts_printf_stdout_func && filep == stdout)
+ res = (*erts_printf_stdout_func)((char *) format, arglist);
+ else if (erts_printf_stderr_func && filep == stderr)
+ res = (*erts_printf_stderr_func)((char *) format, arglist);
+ else {
+ int (*fmt_f)(void*, char*, size_t);
+ errno = 0;
+ if (erts_printf_add_cr_to_stdout && filep == stdout)
+ fmt_f = write_f_add_cr;
+ else if (erts_printf_add_cr_to_stderr && filep == stderr)
+ fmt_f = write_f_add_cr;
+ else
+ fmt_f = write_f;
+ res = erts_printf_format(fmt_f,(void *)filep,(char *)format,arglist);
+ }
+ return res;
+}
+
+int
+erts_vfdprintf(int fd, const char *format, va_list arglist)
+{
+ int res;
+ errno = 0;
+ res = erts_printf_format(write_fd,(void *)&fd,(char *)format,arglist);
+ return res;
+}
+
+int
+erts_vsprintf(char *buf, const char *format, va_list arglist)
+{
+ int res;
+ char *p = buf;
+ errno = 0;
+ res = erts_printf_format(write_s, (void *) &p, (char *) format, arglist);
+ if (res < 0)
+ buf[0] = '\0';
+ else
+ buf[res] = '\0';
+ return res;
+}
+
+int
+erts_vsnprintf(char *buf, size_t size, const char *format, va_list arglist)
+{
+ write_sn_arg_t wsnap;
+ int res;
+ if (size < 1)
+ return -EINVAL;
+ wsnap.buf = buf;
+ wsnap.len = size-1; /* Always need room for trailing '\0' */
+ errno = 0;
+ res = erts_printf_format(write_sn, (void *)&wsnap, (char *)format, arglist);
+ if (res < 0)
+ buf[0] = '\0';
+ else if (res < size)
+ buf[res] = '\0';
+ else
+ buf[size-1] = '\0';
+ return res;
+}
+
+int
+erts_vdsprintf(erts_dsprintf_buf_t *dsbufp, const char *format, va_list arglist)
+{
+ int res;
+ if (!dsbufp)
+ return -EINVAL;
+ errno = 0;
+ res = erts_printf_format(write_ds, (void *)dsbufp, (char *)format, arglist);
+ if (dsbufp->str) {
+ if (res < 0)
+ dsbufp->str[0] = '\0';
+ else
+ dsbufp->str[dsbufp->str_len] = '\0';
+ }
+ return res;
+}