aboutsummaryrefslogtreecommitdiffstats
path: root/lib/erl_interface/src/legacy/erl_eterm.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/erl_interface/src/legacy/erl_eterm.c')
-rw-r--r--lib/erl_interface/src/legacy/erl_eterm.c1308
1 files changed, 1308 insertions, 0 deletions
diff --git a/lib/erl_interface/src/legacy/erl_eterm.c b/lib/erl_interface/src/legacy/erl_eterm.c
new file mode 100644
index 0000000000..b685709c02
--- /dev/null
+++ b/lib/erl_interface/src/legacy/erl_eterm.c
@@ -0,0 +1,1308 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1996-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%
+ */
+/*
+ * Purpose: Representation of Erlang terms.
+ */
+
+#include "eidef.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "ei_locking.h"
+#include "ei_resolve.h"
+#include "erl_interface.h"
+#include "erl_eterm.h"
+#include "erl_malloc.h"
+#include "erl_marshal.h"
+#include "erl_error.h"
+#include "erl_internal.h"
+#include "ei_internal.h"
+
+#define ERL_IS_BYTE(x) (ERL_IS_INTEGER(x) && (ERL_INT_VALUE(x) & ~0xFF) == 0)
+
+/* FIXME use unsigned char, or uint8 for buffers, cast (int) really needed? */
+
+static void iolist_to_buf(const ETERM* term, char** bufp);
+static char* strsave(const char *src);
+
+/***************************************************************************
+ *
+ * API: erl_init()
+ *
+ * Not documented to set erl_errno.
+ *
+ ***************************************************************************/
+
+/* all initialisation of erl_interface modules should be called from here */
+/* order is important: erl_malloc and erl_resolve depend on ei_locking */
+/* NOTE: don't call this directly - please use erl_init() macro defined
+ in ei_locking.h! */
+void erl_init(void *hp,long heap_size)
+{
+ erl_init_malloc(hp, heap_size);
+ erl_init_marshal();
+ ei_init_resolve();
+}
+
+void erl_set_compat_rel(unsigned rel)
+{
+ ei_set_compat_rel(rel);
+}
+
+/*
+ * Create an INTEGER. Depending on its value it
+ * may end up as a BigNum.
+ */
+ETERM *erl_mk_int (int i)
+{
+ ETERM *ep;
+
+ ep = erl_alloc_eterm(ERL_INTEGER);
+ ERL_COUNT(ep) = 1;
+ ERL_INT_VALUE(ep) = i;
+ return ep;
+}
+
+ETERM *erl_mk_longlong (long long i)
+{
+ ETERM *ep;
+
+ ep = erl_alloc_eterm(ERL_LONGLONG);
+ ERL_COUNT(ep) = 1;
+ ERL_LL_VALUE(ep) = i;
+ return ep;
+}
+
+/*
+ * Create an UNSIGNED INTEGER. Depending on its
+ * value it may end up as a BigNum.
+ */
+
+ETERM *erl_mk_uint (unsigned int u)
+{
+ ETERM *ep;
+
+ ep = erl_alloc_eterm(ERL_U_INTEGER);
+ ERL_COUNT(ep) = 1;
+ ERL_INT_UVALUE(ep) = u;
+ return ep;
+}
+
+ETERM *erl_mk_ulonglong (unsigned long long i)
+{
+ ETERM *ep;
+
+ ep = erl_alloc_eterm(ERL_U_LONGLONG);
+ ERL_COUNT(ep) = 1;
+ ERL_LL_UVALUE(ep) = i;
+ return ep;
+}
+
+/*
+ * Create a FLOAT.
+ */
+ETERM *erl_mk_float (double d)
+{
+ ETERM *ep;
+
+ ep = erl_alloc_eterm(ERL_FLOAT);
+ ERL_COUNT(ep) = 1;
+ ERL_FLOAT_VALUE(ep) = d;
+ return ep;
+}
+
+/*
+ * Create an ATOM
+ */
+ETERM *erl_mk_atom (const char *s)
+{
+ ETERM *ep;
+
+ /* ASSERT(s != NULL); */
+ if (!s) return NULL;
+
+ ep = erl_alloc_eterm(ERL_ATOM);
+ ERL_COUNT(ep) = 1;
+ ERL_ATOM_SIZE(ep) = strlen(s);
+ if ((ERL_ATOM_PTR(ep) = strsave(s)) == NULL)
+ {
+ erl_free_term(ep);
+ erl_errno = ENOMEM;
+ return NULL;
+ }
+ return ep;
+}
+
+/*
+ * Given a string as input, creates a list.
+ */
+ETERM *erl_mk_string(const char *s)
+{
+ /* ASSERT(s != NULL); */
+ if (!s) return NULL;
+
+ return erl_mk_estring(s, strlen(s));
+}
+
+ETERM *erl_mk_estring(const char *s, int len)
+{
+ ETERM *ep;
+ int i;
+
+ if ((!s) || (len < 0)) return NULL;
+
+ /*
+ * ASSERT(s != NULL);
+ * ASSERT(len >= 0);
+ */
+
+ ep = erl_mk_empty_list();
+ for (i = len-1; i >= 0; i--) {
+ ETERM* integer;
+ ETERM* cons;
+
+ integer = erl_alloc_eterm(ERL_INTEGER);
+ ERL_COUNT(integer) = 1;
+ ERL_INT_VALUE(integer) = (unsigned char)s[i];
+
+ cons = erl_alloc_eterm(ERL_LIST);
+ ERL_COUNT(cons) = 1;
+ HEAD(cons) = integer;
+ TAIL(cons) = ep;
+ ep = cons;
+ }
+ return ep;
+}
+
+/*
+ * Create a PID.
+ */
+ETERM *erl_mk_pid(const char *node,
+ unsigned int number,
+ unsigned int serial,
+ unsigned char creation)
+{
+ ETERM *ep;
+
+ if (!node) return NULL;
+ /* ASSERT(node != NULL); */
+
+ ep = erl_alloc_eterm(ERL_PID);
+ ERL_COUNT(ep) = 1;
+ if ((ERL_PID_NODE(ep) = strsave(node)) == NULL)
+ {
+ erl_free_term(ep);
+ erl_errno = ENOMEM;
+ return NULL;
+ }
+ ERL_PID_NUMBER(ep) = number & 0x7fff; /* 15 bits */
+ if (ei_internal_use_r9_pids_ports()) {
+ ERL_PID_SERIAL(ep) = serial & 0x07; /* 3 bits */
+ }
+ else {
+ ERL_PID_SERIAL(ep) = serial & 0x1fff; /* 13 bits */
+ }
+ ERL_PID_CREATION(ep) = creation & 0x03; /* 2 bits */
+ return ep;
+}
+
+/*
+ * Create a PORT.
+ */
+ETERM *erl_mk_port(const char *node,
+ unsigned int number,
+ unsigned char creation)
+{
+ ETERM *ep;
+
+ if (!node) return NULL;
+ /* ASSERT(node != NULL); */
+
+ ep = erl_alloc_eterm(ERL_PORT);
+ ERL_COUNT(ep) = 1;
+ if ((ERL_PORT_NODE(ep) = strsave(node)) == NULL)
+ {
+ erl_free_term(ep);
+ erl_errno = ENOMEM;
+ return NULL;
+ }
+ if (ei_internal_use_r9_pids_ports()) {
+ ERL_PORT_NUMBER(ep) = number & 0x3ffff; /* 18 bits */
+ }
+ else {
+ ERL_PORT_NUMBER(ep) = number & 0x0fffffff; /* 18 bits */
+ }
+ ERL_PORT_CREATION(ep) = creation & 0x03; /* 2 bits */
+ return ep;
+}
+
+/*
+ * Create any kind of reference.
+ */
+ETERM *__erl_mk_reference (const char *node,
+ size_t len,
+ unsigned int n[],
+ unsigned char creation)
+{
+ ETERM * t;
+
+ if (node == NULL) return NULL;
+
+ t = erl_alloc_eterm(ERL_REF);
+ ERL_COUNT(t) = 1;
+
+ if ((ERL_REF_NODE(t) = strsave(node)) == NULL)
+ {
+ erl_free_term(t);
+ erl_errno = ENOMEM;
+ return NULL;
+ }
+ ERL_REF_LEN(t) = len;
+ ERL_REF_NUMBERS(t)[0] = n[0] & 0x3ffff; /* 18 bits */
+ ERL_REF_NUMBERS(t)[1] = n[1];
+ ERL_REF_NUMBERS(t)[2] = n[2];
+ ERL_REF_CREATION(t) = creation & 0x03; /* 2 bits */
+
+ return t;
+}
+
+/*
+ * Create a REFERENCE.
+ */
+ETERM *erl_mk_ref (const char *node,
+ unsigned int number,
+ unsigned char creation)
+{
+ unsigned int n[3] = {0, 0, 0};
+ n[0] = number;
+ return __erl_mk_reference(node, 1, n, creation);
+}
+
+/*
+ * Create a long REFERENCE.
+ */
+ETERM *
+erl_mk_long_ref (const char *node,
+ unsigned int n1, unsigned int n2, unsigned int n3,
+ unsigned char creation)
+{
+ unsigned int n[3] = {0, 0, 0};
+ n[0] = n3; n[1] = n2; n[2] = n1;
+ return __erl_mk_reference(node, 3, n, creation);
+}
+
+/*
+ * Create a BINARY.
+ */
+ETERM *erl_mk_binary (const char *b, int size)
+{
+ ETERM *ep;
+
+ if ((!b) || (size < 0)) return NULL;
+ /* ASSERT(b != NULL); */
+
+ ep = erl_alloc_eterm(ERL_BINARY);
+ ERL_COUNT(ep) = 1;
+ ERL_BIN_SIZE(ep) = size;
+ ERL_BIN_PTR(ep) = (unsigned char *) erl_malloc(size);
+ memcpy(ERL_BIN_PTR(ep), b, size);
+ return ep;
+}
+
+/*
+ * Create a TUPLE. For each element in the tuple
+ * bump its reference counter.
+ */
+ETERM *erl_mk_tuple (ETERM **arr,int size)
+{
+ ETERM *ep;
+ int i;
+
+ if ((!arr) || (size < 0)) return NULL;
+ for (i=0; i<size; i++) if (!arr[i]) return NULL;
+ /* ASSERT(arr != NULL); */
+
+ ep = erl_alloc_eterm(ERL_TUPLE);
+ ERL_COUNT(ep) = 1;
+ ERL_TUPLE_SIZE(ep) = size;
+ ERL_TUPLE_ELEMS(ep) = (ETERM**) erl_malloc((size) * (sizeof(ETERM*)));
+ for (i = 0; i < size; i++) {
+ /* ASSERT(arr[i] != NULL); */
+ ERL_COUNT(arr[i])++;
+ ERL_TUPLE_ELEMENT(ep, i) = arr[i];
+ }
+ return ep;
+}
+
+/*
+ * SET an ELEMENT in a TUPLE. Free the old element
+ * and bump the reference counter of the new one.
+ * Return 1 on success, otherwise 0.
+ */
+#if 0
+int erl_setelement (int ix, ETERM *ep, ETERM *vp)
+{
+ if ((!ep) || (!vp)) return 0;
+ /* ASSERT(ep != NULL);
+ * ASSERT(vp != NULL);
+ */
+
+ if ((ERL_TYPE(ep) == ERL_TUPLE) && (ix <= ERL_TUPLE_SIZE(ep))) {
+ erl_free_term(ERL_TUPLE_ELEMENT(ep, ix-1));
+ ERL_TUPLE_ELEMENT(ep, ix-1) = vp;
+ ERL_COUNT(vp)++;
+ return 1;
+ }
+ erl_err_msg("<ERROR> erl_setelement: Bad type to setelement or out of range \n");
+ return 0;
+}
+#endif
+
+/*
+ * Extract an ELEMENT from a TUPLE. Bump the
+ * reference counter on the extracted object.
+ */
+ETERM *erl_element (int ix, const ETERM *ep)
+{
+ if ((!ep) || (ix < 0)) return NULL;
+ /*
+ * ASSERT(ep != NULL);
+ * ASSERT(ix >= 0);
+ */
+
+ if ((ERL_TYPE(ep) == ERL_TUPLE) && (ix <= ERL_TUPLE_SIZE(ep))) {
+ ERL_COUNT(ERL_TUPLE_ELEMENT(ep, ix-1))++;
+ return ERL_TUPLE_ELEMENT(ep, ix-1);
+ }
+ else
+ return NULL;
+} /* erl_element */
+
+ETERM *erl_mk_empty_list(void)
+{
+ ETERM *ep;
+
+ ep = erl_alloc_eterm(ERL_EMPTY_LIST);
+ ERL_COUNT(ep) = 1;
+ return ep;
+}
+
+/*
+ * Construct a new list by CONS'ing a HEAD on
+ * to the TAIL. Bump the reference counter on
+ * the head and tail object. Note that we allow
+ * non-well formed lists to be created.
+ */
+ETERM *erl_cons(ETERM *hd, ETERM *tl)
+{
+ ETERM *ep;
+
+ if ((!hd) || (!tl)) return NULL;
+
+ /*
+ * ASSERT(hd != NULL);
+ * ASSERT(tl != NULL);
+ */
+
+ ep = erl_alloc_eterm(ERL_LIST);
+ ERL_COUNT(ep) = 1;
+ HEAD(ep) = hd;
+ TAIL(ep) = tl;
+ ERL_COUNT(hd)++;
+ ERL_COUNT(tl)++;
+ return ep;
+}
+
+/*
+ * Extract the HEAD of a LIST. Bump the reference
+ * counter on the head object.
+ */
+ETERM *erl_hd (const ETERM *ep)
+{
+ if (!ep) return NULL;
+ /* ASSERT(ep != NULL); */
+
+ if (ERL_TYPE(ep) != ERL_LIST) {
+ return (ETERM *) NULL;
+ }
+ ERL_COUNT(ERL_CONS_HEAD(ep))++;
+ return ERL_CONS_HEAD(ep);
+}
+
+/*
+ * Extract the TAIL of a LIST. Bump the reference
+ * counter on the tail object.
+ */
+ETERM *erl_tl (const ETERM *ep)
+{
+ ETERM *tl;
+
+ if (!ep) return NULL;
+ /* ASSERT(ep != NULL); */
+
+ if (ERL_TYPE(ep) != ERL_LIST) {
+ return (ETERM *) NULL;
+ }
+
+ tl = TAIL(ep);
+ ERL_COUNT(tl)++;
+ return tl;
+}
+
+/*
+ * Create a LIST from an array of elements. Note that
+ * we create it from the last element in the array to
+ * the first. Also, note that we decrement the reference
+ * counter for each member in the list but the first one.
+ * This is done because of the use of erl_cons.
+ */
+
+ETERM *erl_mk_list (ETERM **arr, int size)
+{
+ ETERM *ep;
+ int i;
+
+ if ((!arr) || (size < 0)) return NULL;
+ for (i=0; i<size; i++) if (!arr[i]) return NULL;
+
+ /* ASSERT(arr != NULL); */
+ ep = erl_mk_empty_list();
+ if (size > 0) {
+ ERL_COUNT(ep)--;
+ }
+
+ for (i = size-1; i >= 0; i--) {
+ /* ASSERT(arr[i] != NULL); */
+ ep = erl_cons(arr[i], ep);
+ if (i > 0)
+ ERL_COUNT(ep)--; /* Internal reference */
+ }
+ return ep;
+}
+
+/*
+ * Create an empty VARIABLE.
+ */
+ETERM *erl_mk_var(const char *s)
+{
+ ETERM *ep;
+
+ if (!s) return NULL;
+
+ /* ASSERT(s != NULL); */
+
+ ep = erl_alloc_eterm(ERL_VARIABLE);
+ ERL_COUNT(ep) = 1;
+ ERL_VAR_LEN(ep) = strlen(s);
+ if ((ERL_VAR_NAME(ep) = strsave(s)) == NULL)
+ {
+ erl_free_term(ep);
+ erl_errno = ENOMEM;
+ return NULL;
+ }
+ ERL_VAR_VALUE(ep) = (ETERM *) NULL;
+ return ep;
+}
+
+/*
+ * Return the CONTENT of a VARIABLE with NAME.
+ * If the content is non-nil then bump its
+ * reference counter.
+ */
+ETERM *erl_var_content (const ETERM *ep, const char *name)
+{
+ int i;
+ ETERM *vp;
+
+ if ((!ep) || (!name)) return NULL;
+
+ /* ASSERT(ep != NULL); */
+
+ switch(ERL_TYPE(ep))
+ {
+ case ERL_VARIABLE:
+ if (strcmp(ERL_VAR_NAME(ep), name) == 0) {
+ if ((vp = ERL_VAR_VALUE(ep)) != NULL) {
+ ERL_COUNT(vp)++;
+ return vp;
+ }
+ }
+ break;
+
+ case ERL_LIST:
+ while (ep && (ERL_TYPE(ep) != ERL_EMPTY_LIST)) {
+ if ((vp = erl_var_content(HEAD(ep), name))) return vp;
+ ep = TAIL(ep);
+ }
+ break;
+
+ case ERL_TUPLE:
+ for (i=0; i < ERL_TUPLE_SIZE(ep); i++)
+ if ((vp = erl_var_content(ERL_TUPLE_ELEMENT(ep, i), name)))
+ {
+ return vp;
+ }
+ break;
+
+ default:
+ /* variables can't occur in other types */
+ break;
+ }
+
+ /* nothing found ! */
+ return NULL;
+}
+
+/*
+ * Return the SIZE of a TUPLE or a BINARY.
+ * At failure -1 is returned.
+ */
+int erl_size (const ETERM *ep)
+{
+ if (!ep) return -1;
+
+ /* ASSERT(ep != NULL); */
+
+ switch (ERL_TYPE(ep)) {
+ case ERL_TUPLE:
+ return ERL_TUPLE_SIZE(ep);
+
+ case ERL_BINARY:
+ return ERL_BIN_SIZE(ep);
+
+ default:
+ return -1;
+
+ }
+}
+
+/*
+ * Return the LENGTH of a LIST.
+ * At failure -1 is returned (this include non-proper lists like [a|b]).
+ */
+int erl_length(const ETERM *ep)
+{
+ int n = 0;
+
+ if (!ep) return -1;
+ /* ASSERT(ep != NULL); */
+
+ while (ERL_TYPE(ep) == ERL_LIST) {
+ n++;
+ ep = TAIL(ep);
+ }
+
+ if (!ERL_IS_EMPTY_LIST(ep)) return -1;
+
+ return n;
+}
+
+
+/***********************************************************************
+ * I o l i s t f u n c t i o n s
+ *
+ * The following functions handles I/O lists.
+ *
+ * Informally, an I/O list is a deep list of characters and binaries,
+ * which can be sent to an Erlang port.
+ *
+ * Formally, in BNF, an I/O list is defined as:
+ *
+ * iolist ::= []
+ * | Binary
+ * | [iohead | iolist]
+ * ;
+ *
+ * iohead ::= Binary
+ * | Byte (integer in the range [0..255])
+ * | iolist
+ * ;
+ *
+ * Note that versions of Erlang/OTP prior to R2 had a slightly more
+ * restricted definition of I/O lists, in that the tail of a an I/O list
+ * was not allowed to be a binary. The erl_interface functions
+ * for I/O lists follows the more liberal rules described by the BNF
+ * description above.
+ ***********************************************************************/
+
+/*
+ * This function converts an I/O list to a '\0' terminated C string.
+ * The I/O list must not contain any occurrences of the integer 0.
+ *
+ * The string will be in memory allocated by erl_malloc(). It is the
+ * responsibility of the caller to eventually call erl_free() to free
+ * the memory.
+ *
+ * Returns: NULL if the list was not an I/O list or contained
+ * the integer 0, otherwise a pointer to '\0' terminated string.
+ */
+
+char* erl_iolist_to_string(const ETERM* term)
+{
+ ETERM* bin;
+
+ if ((bin = erl_iolist_to_binary(term)) == NULL) {
+ return NULL;
+ } else {
+ char* result = NULL;
+
+ if (memchr(ERL_BIN_PTR(bin), '\0', ERL_BIN_SIZE(bin)) == NULL) {
+ result = (char *) erl_malloc(ERL_BIN_SIZE(bin)+1);
+ memcpy(result, ERL_BIN_PTR(bin), ERL_BIN_SIZE(bin));
+ result[ERL_BIN_SIZE(bin)] = '\0';
+ }
+ erl_free_term(bin);
+ return result;
+ }
+}
+
+/*
+ * This function converts an I/O list to a binary term.
+ *
+ * Returns: NULL if the list was not an I/O list, otherwise
+ * an ETERM pointer pointing to a binary term.
+ */
+
+ETERM *erl_iolist_to_binary (const ETERM* term)
+{
+ ETERM *dest;
+ int size;
+ char* ptr;
+
+ if (!term) return NULL;
+ /* ASSERT(term != NULL); */
+
+ /*
+ * Verify that the term is an I/O list and get its length.
+ */
+
+ size = erl_iolist_length(term);
+ if (size == -1) {
+ return NULL;
+ }
+
+ /*
+ * Allocate the binary and copy the contents of the I/O list into it.
+ */
+
+ dest = erl_alloc_eterm(ERL_BINARY);
+ ERL_COUNT(dest) = 1;
+ ERL_BIN_SIZE(dest) = size;
+ ptr = (char *)erl_malloc(size);
+ ERL_BIN_PTR(dest) = (unsigned char *)ptr;
+ iolist_to_buf(term, &ptr);
+
+ /*
+ * If ptr doesn't point exactly one byte beyond the end of the
+ * binary, something must be seriously wrong.
+ */
+
+ if (ERL_BIN_PTR(dest) + size != (unsigned char *) ptr) return NULL;
+ /* ASSERT(ERL_BIN_PTR(dest) + size == (unsigned char *) ptr); */
+
+ return dest;
+}
+
+/*
+ * Returns the length of an I/O list.
+ *
+ * Returns: -1 if the term if the given term is not a I/O list,
+ * or the length otherwise.
+ */
+
+int erl_iolist_length (const ETERM* term)
+{
+ int len = 0;
+
+ while (ERL_IS_CONS(term)) {
+ ETERM* obj = HEAD(term);
+
+ if (ERL_IS_BYTE(obj)) {
+ len++;
+ } else if (ERL_IS_CONS(obj)) {
+ int i;
+ if ((i = erl_iolist_length(obj)) < 0)
+ return i;
+ len += i;
+ } else if (ERL_IS_BINARY(obj)) {
+ len += ERL_BIN_SIZE(obj);
+ } else if (!ERL_IS_EMPTY_LIST(obj)) {
+ return(-1);
+ }
+ term = TAIL(term);
+ }
+ if (ERL_IS_EMPTY_LIST(term))
+ return len;
+ else if (ERL_IS_BINARY(term))
+ return len + ERL_BIN_SIZE(term);
+ else
+ return -1;
+}
+
+/*
+ * Return a brand NEW COPY of an ETERM.
+ */
+/*
+ * FIXME: Deep (the whole tree) or shallow (just the top term) copy?
+ * The documentation never says, but the code as written below will
+ * make a deep copy. This should be documented.
+ */
+ETERM *erl_copy_term(const ETERM *ep)
+{
+ int i;
+ ETERM *cp;
+
+ if (!ep) return NULL;
+ /* ASSERT(ep != NULL); */
+
+ cp = erl_alloc_eterm(ERL_TYPE(ep));
+ ERL_COUNT(cp) = 1;
+
+ switch(ERL_TYPE(cp)) {
+ case ERL_INTEGER:
+ case ERL_SMALL_BIG:
+ ERL_INT_VALUE(cp) = ERL_INT_VALUE(ep);
+ break;
+ case ERL_U_INTEGER:
+ case ERL_U_SMALL_BIG:
+ ERL_INT_UVALUE(cp) = ERL_INT_UVALUE(ep);
+ break;
+ case ERL_FLOAT:
+ ERL_FLOAT_VALUE(cp) = ERL_FLOAT_VALUE(ep);
+ break;
+ case ERL_ATOM:
+ ERL_ATOM_SIZE(cp) = ERL_ATOM_SIZE(ep);
+ ERL_ATOM_PTR(cp) = strsave(ERL_ATOM_PTR(ep));
+ if (ERL_ATOM_PTR(cp) == NULL)
+ {
+ erl_free_term(cp);
+ erl_errno = ENOMEM;
+ return NULL;
+ }
+ break;
+ case ERL_PID:
+ /* FIXME: First copy the bit pattern, then duplicate the node
+ name and plug in. Somewhat ugly (also done with port and
+ ref below). */
+ memcpy(&cp->uval.pidval, &ep->uval.pidval, sizeof(Erl_Pid));
+ ERL_PID_NODE(cp) = strsave(ERL_PID_NODE(ep));
+ ERL_COUNT(cp) = 1;
+ break;
+ case ERL_PORT:
+ memcpy(&cp->uval.portval, &ep->uval.portval, sizeof(Erl_Port));
+ ERL_PORT_NODE(cp) = strsave(ERL_PORT_NODE(ep));
+ ERL_COUNT(cp) = 1;
+ break;
+ case ERL_REF:
+ memcpy(&cp->uval.refval, &ep->uval.refval, sizeof(Erl_Ref));
+ ERL_REF_NODE(cp) = strsave(ERL_REF_NODE(ep));
+ ERL_COUNT(cp) = 1;
+ break;
+ case ERL_LIST:
+ HEAD(cp) = erl_copy_term(HEAD(ep));
+ TAIL(cp) = erl_copy_term(TAIL(ep));
+ break;
+ case ERL_EMPTY_LIST:
+ break;
+ case ERL_TUPLE:
+ i = ERL_TUPLE_SIZE(cp) = ERL_TUPLE_SIZE(ep);
+ ERL_TUPLE_ELEMS(cp) = (ETERM**) erl_malloc(i * sizeof(ETERM*));
+ for(i=0; i < ERL_TUPLE_SIZE(ep); i++)
+ ERL_TUPLE_ELEMENT(cp,i) = erl_copy_term(ERL_TUPLE_ELEMENT(ep, i));
+ break;
+ case ERL_BINARY:
+ ERL_BIN_SIZE(cp) = ERL_BIN_SIZE(ep);
+ ERL_BIN_PTR(cp) = (unsigned char *) erl_malloc(ERL_BIN_SIZE(ep));
+ memcpy(ERL_BIN_PTR(cp), ERL_BIN_PTR(ep), ERL_BIN_SIZE(ep));
+ break;
+ case ERL_FUNCTION:
+ i = ERL_CLOSURE_SIZE(cp) = ERL_CLOSURE_SIZE(ep);
+ ERL_FUN_ARITY(cp) = ERL_FUN_ARITY(ep);
+ ERL_FUN_NEW_INDEX(cp) = ERL_FUN_NEW_INDEX(ep);
+ ERL_FUN_INDEX(cp) = erl_copy_term(ERL_FUN_INDEX(ep));
+ ERL_FUN_UNIQ(cp) = erl_copy_term(ERL_FUN_UNIQ(ep));
+ ERL_FUN_CREATOR(cp) = erl_copy_term(ERL_FUN_CREATOR(ep));
+ ERL_FUN_MODULE(cp) = erl_copy_term(ERL_FUN_MODULE(ep));
+ memcpy(ERL_FUN_MD5(cp), ERL_FUN_MD5(ep), sizeof(ERL_FUN_MD5(ep)));
+ ERL_CLOSURE(cp) = (ETERM**) erl_malloc(i * sizeof(ETERM*));
+ for(i=0; i < ERL_CLOSURE_SIZE(ep); i++)
+ ERL_CLOSURE_ELEMENT(cp,i) =
+ erl_copy_term(ERL_CLOSURE_ELEMENT(ep, i));
+ break;
+ default:
+ erl_err_msg("<ERROR> erl_copy_term: wrong type encountered !");
+ erl_free_term(cp);
+ return (ETERM *) NULL;
+ }
+
+ return cp;
+}
+
+#ifndef SILENT
+
+static int print_string(FILE* fp, const ETERM* ep);
+static int is_printable_list(const ETERM* term);
+
+/*
+ * PRINT out an ETERM.
+ */
+
+int erl_print_term(FILE *fp, const ETERM *ep)
+{
+ int j,i,doquote;
+ int ch_written = 0; /* counter of written chars */
+
+ if ((!fp) || (!ep)) return 0;
+ /* ASSERT(ep != NULL); */
+
+ j = i = doquote = 0;
+ switch(ERL_TYPE(ep))
+ {
+ case ERL_ATOM:
+ /* FIXME: what if some weird locale is in use? */
+ if (!islower((int)ERL_ATOM_PTR(ep)[0]))
+ doquote = 1;
+
+ for (i = 0; !doquote && i < ERL_ATOM_SIZE(ep); i++)
+ {
+ doquote = !(isalnum((int)ERL_ATOM_PTR(ep)[i])
+ || (ERL_ATOM_PTR(ep)[i] == '_'));
+ }
+
+ if (doquote) {
+ putc('\'', fp);
+ ch_written++;
+ }
+ fputs(ERL_ATOM_PTR(ep), fp);
+ ch_written += ERL_ATOM_SIZE(ep);
+ if (doquote) {
+ putc('\'', fp);
+ ch_written++;
+ }
+ break;
+
+ case ERL_VARIABLE:
+ if (!isupper((int)ERL_VAR_NAME(ep)[0])) {
+ doquote = 1;
+ putc('\'', fp);
+ ch_written++;
+ }
+
+ fputs(ERL_VAR_NAME(ep), fp);
+ ch_written += ERL_VAR_LEN(ep);
+
+ if (doquote) {
+ putc('\'', fp);
+ ch_written++;
+ }
+ break;
+
+ case ERL_PID:
+ ch_written += fprintf(fp, "<%s.%d.%d>",
+ ERL_PID_NODE(ep),
+ ERL_PID_NUMBER(ep), ERL_PID_SERIAL(ep));
+ break;
+ case ERL_PORT:
+ ch_written += fprintf(fp, "#Port");
+ break;
+ case ERL_REF:
+ ch_written += fprintf(fp, "#Ref");
+ break;
+ case ERL_EMPTY_LIST:
+ ch_written += fprintf(fp, "[]");
+ break;
+ case ERL_LIST:
+ if (is_printable_list(ep)) {
+ ch_written += print_string(fp, ep);
+ } else {
+ putc('[', fp);
+ ch_written++;
+ while (ERL_IS_CONS(ep)) {
+ ch_written += erl_print_term(fp, HEAD(ep));
+ ep = TAIL(ep);
+ if (ERL_IS_CONS(ep)) {
+ putc(',', fp);
+ ch_written++;
+ }
+ }
+ if (!ERL_IS_EMPTY_LIST(ep)) {
+ putc('|', fp);
+ ch_written++;
+ ch_written += erl_print_term(fp, ep);
+ }
+ putc(']', fp);
+ ch_written++;
+ }
+ break;
+ case ERL_TUPLE:
+ putc('{', fp);
+ ch_written++;
+ for (i=0; i < ERL_TUPLE_SIZE(ep); i++) {
+ ch_written += erl_print_term(fp, ERL_TUPLE_ELEMENT(ep, j++) );
+ if (i != ERL_TUPLE_SIZE(ep)-1) {
+ putc(',', fp);
+ ch_written++;
+ }
+ }
+ putc('}', fp);
+ ch_written++;
+ break;
+ case ERL_BINARY: {
+ int sz = (ERL_BIN_SIZE(ep) > 20) ? 20 : ERL_BIN_SIZE(ep);
+ unsigned char *ptr = ERL_BIN_PTR(ep);
+ ch_written += fprintf(fp, "#Bin<");
+ for (i = 0; i < sz; i++) {
+ putc(ptr[i], fp); ch_written++;
+ }
+ if (sz == 20) ch_written += fprintf(fp, "(%d)....>", ERL_BIN_SIZE(ep)-20);
+ else ch_written += fprintf(fp, ">");
+ break;
+ }
+ case ERL_INTEGER:
+ case ERL_SMALL_BIG:
+ ch_written += fprintf(fp, "%d", ERL_INT_VALUE(ep));
+ break;
+ case ERL_U_INTEGER:
+ case ERL_U_SMALL_BIG:
+ ch_written += fprintf(fp, "%d", ERL_INT_UVALUE(ep));
+ break;
+ case ERL_LONGLONG:
+ case ERL_U_LONGLONG:
+ ch_written += fprintf(fp, "%lld", ERL_LL_UVALUE(ep));
+ break;
+ case ERL_FLOAT:
+ ch_written += fprintf(fp, "%f", ERL_FLOAT_VALUE(ep));
+ break;
+ case ERL_FUNCTION:
+ ch_written += fprintf(fp, "#Fun<");
+ ch_written += erl_print_term(fp, ERL_FUN_MODULE(ep));
+ putc('.', fp);
+ ch_written++;
+ ch_written += erl_print_term(fp, ERL_FUN_INDEX(ep));
+ putc('.', fp);
+ ch_written++;
+ ch_written += erl_print_term(fp, ERL_FUN_UNIQ(ep));
+ putc('>', fp);
+ ch_written++;
+ break;
+ default:
+ ch_written = -10000;
+ erl_err_msg("<ERROR> erl_print_term: Bad type of term !");
+ }
+ return ch_written;
+}
+
+/*
+ * FIXME not done yet....
+ */
+
+#if 0
+
+int erl_sprint_term(char *buf, const ETERM *ep)
+{
+ int j,i,doquote;
+ int ch_written = 0; /* counter of written chars */
+
+ if ((!buf) || (!ep)) return 0;
+ /* ASSERT(ep != NULL); */
+
+ j = i = doquote = 0;
+ switch(ERL_TYPE(ep))
+ {
+ case ERL_ATOM:
+ /* FIXME: what if some weird locale is in use? */
+ if (!islower((int)ERL_ATOM_PTR(ep)[0]))
+ doquote = 1;
+
+ for (i = 0; !doquote && i < ERL_ATOM_SIZE(ep); i++)
+ {
+ doquote = !(isalnum((int)ERL_ATOM_PTR(ep)[i])
+ || (ERL_ATOM_PTR(ep)[i] == '_'));
+ }
+
+ if (doquote) {
+ *buf++ = '\'';
+ ch_written++;
+ }
+ {
+ int len = ERL_ATOM_SIZE(ep);
+ strncpy(buf, ERL_ATOM_PTR(ep), len);
+ buf += len;
+ ch_written += len;
+ }
+ if (doquote) {
+ *buf++ = '\'';
+ ch_written++;
+ }
+ break;
+
+ case ERL_VARIABLE:
+ if (!isupper((int)ERL_VAR_NAME(ep)[0])) {
+ doquote = 1;
+ *buf++ = '\'';
+ ch_written++;
+ }
+ len = ERL_VAR_LEN(ep);
+ strncpy(buf, ERL_VAR_NAME(ep), len);
+ buf += len;
+ ch_written += len;
+
+ if (doquote) {
+ *buf++ = '\'';
+ ch_written++;
+ }
+ break;
+
+ case ERL_PID:
+ len = sprintf(buf, "<%s.%d.%d>",
+ ERL_PID_NODE(ep),
+ ERL_PID_NUMBER(ep), ERL_PID_SERIAL(ep));
+ buf += len;
+ ch_written += len;
+ break;
+ case ERL_PORT:
+ len = sprintf(buf , "#Port");
+ buf += len;
+ ch_written += len;
+ break;
+ case ERL_REF:
+ len = sprintf(buf , "#Ref");
+ buf += len;
+ ch_written += len;
+ break;
+ case ERL_EMPTY_LIST:
+ len = sprintf(buf , "[]");
+ buf += len;
+ ch_written += len;
+ break;
+ case ERL_LIST:
+ if (is_printable_list(ep)) {
+ ch_written += print_string(fp, ep);
+ } else {
+ putc('[', fp);
+ ch_written++;
+ while (ERL_IS_CONS(ep)) {
+ ch_written += erl_sprint_term(fp, HEAD(ep));
+ ep = TAIL(ep);
+ if (ERL_IS_CONS(ep)) {
+ putc(',', fp);
+ ch_written++;
+ }
+ }
+ if (!ERL_IS_EMPTY_LIST(ep)) {
+ putc('|', fp);
+ ch_written++;
+ ch_written += erl_sprint_term(fp, ep);
+ }
+ putc(']', fp);
+ ch_written++;
+ }
+ break;
+ case ERL_TUPLE:
+ putc('{', fp);
+ ch_written++;
+ for (i=0; i < ERL_TUPLE_SIZE(ep); i++) {
+ ch_written += erl_sprint_term(fp, ERL_TUPLE_ELEMENT(ep, j++) );
+ if (i != ERL_TUPLE_SIZE(ep)-1) {
+ putc(',', fp);
+ ch_written++;
+ }
+ }
+ putc('}', fp);
+ ch_written++;
+ break;
+ case ERL_BINARY:
+ len = sprintf(buf , "#Bin");
+ buf += len;
+ ch_written += len;
+ break;
+ case ERL_INTEGER:
+ case ERL_SMALL_BIG:
+ len = sprintf(buf , "%d", ERL_INT_VALUE(ep));
+ buf += len;
+ ch_written += len;
+ break;
+ case ERL_U_INTEGER:
+ case ERL_U_SMALL_BIG:
+ len = sprintf(buf , "%d", ERL_INT_UVALUE(ep));
+ buf += len;
+ ch_written += len;
+ break;
+ case ERL_FLOAT:
+ len = sprintf(buf , "%f", ERL_FLOAT_VALUE(ep));
+ buf += len;
+ ch_written += len;
+ break;
+ case ERL_FUNCTION:
+ len = sprintf(buf , "#Fun<");
+ buf += len;
+ ch_written += len;
+ ch_written += erl_sprint_term(fp, ERL_FUN_MODULE(ep));
+ putc('.', fp);
+ ch_written++;
+ ch_written += erl_sprint_term(fp, ERL_FUN_INDEX(ep));
+ putc('.', fp);
+ ch_written++;
+ ch_written += erl_sprint_term(fp, ERL_FUN_UNIQ(ep));
+ putc('>', fp);
+ ch_written++;
+ break;
+ default:
+ ch_written = -10000;
+ erl_err_msg("<ERROR> erl_sprint_term: Bad type of term !");
+ }
+ return ch_written;
+}
+#endif
+
+static int print_string(FILE* fp, const ETERM* ep)
+{
+ int ch_written = 0; /* counter of written chars */
+
+ putc('"', fp);
+ ch_written++;
+ while (ERL_IS_CONS(ep)) {
+ int c = ERL_INT_VALUE(HEAD(ep));
+
+ if (c >= ' ') {
+ putc(c, fp);
+ ch_written++;
+ }
+ else {
+ switch (c) {
+ case '\n': fputs("\\n", fp); ch_written += 2; break;
+ case '\r': fputs("\\r", fp); ch_written += 2; break;
+ case '\t': fputs("\\t", fp); ch_written += 2; break;
+ case '\v': fputs("\\v", fp); ch_written += 2; break;
+ case '\b': fputs("\\b", fp); ch_written += 2; break;
+ case '\f': fputs("\\f", fp); ch_written += 2; break;
+ break;
+ default:
+ ch_written += fprintf(fp, "\\%o", c);
+ break;
+ }
+ }
+ ep = TAIL(ep);
+ }
+ putc('"', fp);
+ ch_written++;
+ return ch_written;
+}
+
+/*
+ * Returns 1 if term is a list of printable character, otherwise 0.
+ */
+
+static int is_printable_list(const ETERM* term)
+{
+ while (ERL_TYPE(term) == ERL_LIST) {
+ ETERM* head = HEAD(term);
+
+ if (!ERL_IS_BYTE(head)) {
+ return 0;
+ }
+ if (ERL_INT_VALUE(head) < ' ') {
+ switch (ERL_INT_VALUE(head)) {
+ case '\n':
+ case '\r':
+ case '\t':
+ case '\v':
+ case '\b':
+ case '\f':
+ break;
+ default:
+ return 0;
+ }
+ }
+ term = TAIL(term);
+ }
+
+ return ERL_IS_EMPTY_LIST(term);
+}
+
+#endif
+
+/*
+ * Retrieves the bytes from an I/O list and copy into a buffer.
+ *
+ * NOTE! It is the responsibility of the caller to ensure that
+ * that the buffer is big enough (typically by calling
+ * erl_iolist_length()), and that the term is an I/O list.
+ *
+ * ETERM* term; Term to convert to bytes.
+ * char** bufp; Pointer to pointer to buffer
+ * where the bytes should be stored.
+ * On return, the pointer will point beyond
+ * the last byte stored.
+ */
+
+static void iolist_to_buf(const ETERM* term, char** bufp)
+{
+ char* dest = *bufp;
+
+ while (ERL_IS_CONS(term)) {
+ ETERM* obj = HEAD(term);
+
+ if (ERL_IS_BYTE(obj)) {
+ *dest++ = ERL_INT_VALUE(obj);
+ } else if (ERL_IS_CONS(obj)) {
+ iolist_to_buf(obj, &dest);
+ } else if (ERL_IS_BINARY(obj)) {
+ memcpy(dest, ERL_BIN_PTR(obj), ERL_BIN_SIZE(obj));
+ dest += ERL_BIN_SIZE(obj);
+ } else {
+ /*
+ * Types have been checked by caller.
+ */
+ if (!ERL_IS_EMPTY_LIST(obj)) return;
+ /* ASSERT(ERL_IS_EMPTY_LIST(obj)); */
+ }
+ term = TAIL(term);
+ }
+ if (ERL_IS_BINARY(term)) {
+ memcpy(dest, ERL_BIN_PTR(term), ERL_BIN_SIZE(term));
+ dest += ERL_BIN_SIZE(term);
+ } else {
+ /*
+ * Types have been checked by caller.
+ */
+ if (!ERL_IS_EMPTY_LIST(term)) return;
+ /* ASSERT(ERL_IS_EMPTY_LIST(term));*/
+ }
+ *bufp = dest;
+}
+
+static char* strsave(const char *src)
+{
+ char * dest = malloc(strlen(src)+1);
+
+ if (dest != NULL)
+ strcpy(dest, src);
+ return dest;
+}
+
+
+/*
+ * Local Variables:
+ * compile-command: "cd ..; ERL_TOP=/clearcase/otp/erts make -k"
+ * End:
+ */