Each argument of a foreign function (except for the control argument) 
is of type term_t, an opaque handle to a Prolog term. Three 
groups of functions are available for the analysis of terms. The first 
just validates the type, like the Prolog predicates var/1, atom/1, 
etc., and are called PL_is_*(). The second group attempts 
to translate the argument into a C primitive type. These predicates take 
a term_t and a pointer to the appropriate C type and return TRUE 
or
FALSE depending on successful or unsuccessful translation. 
If the translation fails, the pointed-to data is never modified.
- int PL_term_type(term_t)
- Obtain the type of a term, which should be a term returned by one of the 
other interface predicates or passed as an argument. The function 
returns the type of the Prolog term. The type identifiers are listed 
below. Note that the extraction functions PL_get_*()also 
validate the type and thus the two sections below are equivalent.
        if ( PL_is_atom(t) )
        { char *s;
          PL_get_atom_chars(t, &s);
          ...;
        }
or
        char *s;
        if ( PL_get_atom_chars(t, &s) )
        { ...;
        }
Version 7 added PL_NIL,PL_BLOB,PL_LIST_PAIRandPL_DICT. Older versions 
classifyPL_NILandPL_BLOBasPL_ATOM,PL_LIST_PAIRasPL_TERMand do not have dicts.
 
 
| PL_VARIABLE | A variable or attributed 
variable |  | PL_ATOM | A Prolog atom |  | PL_NIL | The constant [] |  | PL_BLOB | A blob (see section 
12.4.10.2) |  | PL_STRING | A string (see section 
5.2) |  | PL_INTEGER | A integer |  | PL_RATIONAL | A rational number |  | PL_FLOAT | A floating point number |  | PL_TERM | A compound term |  | PL_LIST_PAIR | A list cell ( [H|T]) |  | PL_DICT | A dict (see section 
5.4)) |  
 
The functions PL_is_<type> are an alternative to PL_term_type(). 
The test PL_is_variable(term) 
is equivalent to
PL_term_type(term) 
== PL_VARIABLE, but the first is considerably faster. On the 
other hand, using a switch over PL_term_type() 
is faster and more readable then using an if-then-else using the 
functions below. All these functions return either TRUE or FALSE.
- bool PL_is_variable(term_t)
- Returns non-zero if term is a variable.
- bool PL_is_ground(term_t)
- Returns non-zero if term is a ground term. See also ground/1. 
This function is cycle-safe.
- bool PL_is_atom(term_t)
- Returns non-zero if term is an atom.
- bool PL_is_string(term_t)
- Returns non-zero if term is a string.
- bool PL_is_integer(term_t)
- Returns non-zero if term is an integer.
- bool PL_is_rational(term_t)
- Returns non-zero if term is a rational number (P/Q). 
Note that all integers are considered rational and this test thus 
succeeds for any term for which PL_is_integer() 
succeeds. See also
PL_get_mpq() 
and PL_unify_mpq().
- bool PL_is_float(term_t)
- Returns non-zero if term is a float. Note that the 
corresponding
PL_get_float() 
converts rationals (and thus integers).
- bool PL_is_callable(term_t)
- Returns non-zero if term is a callable term. See callable/1 
for details.
- bool PL_is_compound(term_t)
- Returns non-zero if term is a compound term.
- bool PL_is_functor(term_t, 
functor_t)
- Returns non-zero if term is compound and its functor is functor. 
This test is equivalent to PL_get_functor(), 
followed by testing the functor, but easier to write and faster.
- bool PL_is_list(term_t)
- Returns non-zero if term is a compound term using the list 
constructor or the list terminator. See also PL_is_pair() 
and
PL_skip_list().
- bool PL_is_pair(term_t)
- Returns non-zero if term is a compound term using the list 
constructor. See also PL_is_list() 
and PL_skip_list().
- bool PL_is_dict(term_t)
- Returns non-zero if term is a dict. See also PL_put_dict() 
and PL_get_dict_key().
- bool PL_is_atomic(term_t)
- Returns non-zero if term is atomic (not a variable or 
compound).
- bool PL_is_number(term_t)
- Returns non-zero if term is an rational (including integers) 
or float.
- bool PL_is_acyclic(term_t)
- Returns non-zero if term is acyclic (i.e. a finite tree).
The functions PL_get_*() read information from a Prolog 
term. Most of them take two arguments. The first is the input term and 
the second is a pointer to the output value or a term reference. The 
return value is TRUE or FALSE, indicating the 
success of the "get" operation. Most functions have a related "_ex" 
function that raises an error if the argument is the operation cannot be 
completed. If the Prolog term is not suitable, this is a type, domain or 
instantiation error. If the receiving C type cannot represent the value 
this is a representation error.
For integers an alternative interface exists, which helps deal with 
the various integer types in C and C++. They are convenient for use with
_Generic selection or C++ overloading.
- bool PL_get_atom(term_t 
+t, atom_t *a)
- If t is an atom, store the unique atom identifier over a. 
See also PL_atom_chars() 
and PL_new_atom(). 
If there is no need to access the data (characters) of an atom, it is 
advised to manipulate atoms using their handle. As the atom is 
referenced by t, it will live at least as long as t 
does. If longer lifetime is required, the atom should be locked using PL_register_atom().
- bool PL_get_atom_chars(term_t 
+t, char **s)
- If t is an atom, store a pointer to a 0-terminated C-string 
in
s. It is explicitly not allowed to modify 
the contents of this string. Some built-in atoms may have the string 
allocated in read-only memory, so‘temporary manipulation’can 
cause an error.
- int PL_get_string_chars(term_t 
+t, char **s, size_t *len)
- If t is a string object, store a pointer to a 0-terminated 
C-string in s and the length of the string in len. 
Note that this pointer is invalidated by backtracking, garbage 
collection and stack-shifts, so generally the only safe operations are 
to pass it immediately to a C function that doesn't involve Prolog.
- bool PL_get_chars(term_t 
+t, char **s, unsigned flags)
- Convert the argument term t to a 0-terminated C-string.
flags is a bitwise disjunction from two groups of constants. 
The first specifies which term types should be converted and the second 
how the argument is stored. Below is a specification of these constants.
BUF_STACKimplies, if the data is not static (as from an 
atom), that the data is pushed on a stack. IfBUF_MALLOCis 
used, the data must be freed using PL_free() 
when no longer needed.
With the introduction of wide characters (see section 
2.18.1), not all atoms can be converted into a char*. 
This function fails if t is of the wrong type, but also if 
the text cannot be represented. See theREP_*flags below 
for details. See also PL_get_wchars() 
and PL_get_nchars().
 
The first set of flags (CVT_ATOMthroughCVT_VARIABLE, 
if set, are tested in order, using the first that matches. If none of 
these match, then a check is made for one ofCVT_WRITE,CVT_WRITE_CANONICAL,CVT_WRITEQbeing set. If 
none of the “CVT_WRITE*” flags are set, then atype_erroris raised.
 
- CVT_ATOM
- Convert if term is an atom.
- CVT_STRING
- Convert if term is a string.
- CVT_LIST
- Convert if term is a list of characters (atoms of length 1) or character 
codes (integers representing Unicode code points).
- CVT_INTEGER
- Convert if term is an integer.
- CVT_RATIONAL
- Convert if term is a rational number (including integers). Non-integral 
numbers are written as <num>r<den>.
- CVT_XINTEGER
- Convert if term is an integer to hexadecimal notation. May be combined 
with CVT_RATIONALto represent rational numbers using 
hexadecimal notation. Hexadecimal notation is notably useful for 
transferring big integers to other programming environments if the 
target system can read hexadecimal notation because the result is both 
more compact and faster to write and read.
- CVT_FLOAT
- Convert if term is a float. The characters returned are the same as
write/1 
would write for the floating point number.
- CVT_NUMBER
- Convert if term is an integer, rational number or float. Equivalent to CVT_RATIONAL|CVT_FLOAT. 
Note thatCVT_INTEGERis implied byCVT_RATIONAL.
- CVT_ATOMIC
- Convert if term is atomic. Equivalent to CVT_NUMBER|CVT_ATOM|CVT_STRING.
- CVT_ALL
- Convert if term is any of the above. Integers and rational numbers are 
written as decimal (i.e., CVT_XINTEGERis not 
implied). Note that this does not include variables or terms (with the 
exception of a list of characters/codes). Equivalent toCVT_ATOMIC|CVT_LIST.
- CVT_VARIABLE
- Convert variable to print-name (e.g., _3290).
- CVT_WRITE
- Convert any term that is not converted by any of the other flags using
write/1. 
If no BUF_*is provided,BUF_STACKis implied.
- CVT_WRITEQ
- As CVT_WRITE, but using writeq/2.
- CVT_WRITE_CANONICAL
- As CVT_WRITE, but using write_canonical/2.
- CVT_EXCEPTION
- If conversion fails due to a type error, raise a Prolog type error 
exception in addition to failure.
- BUF_DISCARDABLE
- Data must copied immediately.
- BUF_STACK
- Data is stored on a stack. The older BUF_RINGis an alias 
forBUF_STACK. See section 
12.4.14.
- BUF_MALLOC
- Data is copied to a new buffer returned by PL_malloc(3). 
When no longer needed the user must call PL_free() 
on the data.
- REP_ISO_LATIN_1
- Text is in ISO Latin-1 encoding and the call fails if text cannot be 
represented. This flag has the value 0 and is thus the default.
- REP_UTF8
- Convert the text to a UTF-8 string. This works for all text.
- REP_MB
- Convert to default locale-defined 8-bit string. Success depends on the 
locale. Conversion is done using the wcrtomb() C library 
function.
 
- bool PL_get_list_chars(+term_t 
l, char **s, unsigned flags)
- Same as PL_get_chars(l, s, 
CVT_LIST|flags), provided flags 
contains none of theCVT_*flags.
- bool PL_get_integer(+term_t 
t, int *i)
- If t is a Prolog integer, assign its value over i. 
On 32-bit machines, this is the same as PL_get_long(), 
but avoids a warning from the compiler. See also PL_get_long() 
and
PL_get_integer_ex().
- bool PL_get_long(term_t 
+t, long *i)
- If t is a Prolog integer that can be represented as a long, 
assign its value over i. If t is an integer that 
cannot be represented by a C long, this function returns FALSE. 
If t is a floating point number that can be represented as a 
long, this function succeeds as well. See also PL_get_int64() 
and PL_get_long_ex().
- bool PL_get_int64(term_t 
+t, int64_t *i)
- If t is a Prolog integer or float that can be represented as 
a
int64_t, assign its value over i. See also PL_get_int64_ex().
- bool PL_get_uint64(term_t 
+t, uint64_t *i)
- If t is a Prolog integer that can be represented as a
uint64_t, assign its value over i. Note that 
this requires GMP support for representinguint64_tvalues 
with the high bit set. See also PL_get_uint64_ex().
- bool PL_get_intptr(term_t 
+t, intptr_t *i)
- Get an integer that is at least as wide as a pointer. On most platforms 
this is the same as PL_get_long(), 
but on Win64 pointers are 8 bytes and longs only 4. Unlike PL_get_pointer(), 
the value is not modified.
- bool PL_get_bool(term_t 
+t, int *val)
- If t has the value true,false, set val 
to the C constantTRUEorFALSEand return 
success, otherwise return failure. The valueson,1,off, 
const0 and are also accepted.
- bool PL_get_pointer(term_t 
+t, void **ptr)
- Together with PL_put_pointer() 
and PL_unify_pointer(), 
these functions allow representing a C pointer as a Prolog integer. The 
integer value is derived from the pointer, but not equivalent. The 
translation aims at producing smaller integers that fit more often in 
the tagged integer range. Representing C pointers as integers 
is unsafe. The blob API described in section 
12.4.10 provides a safe way for handling foreign resources that 
cooperates with Prolog garbage collection.
- bool PL_get_float(term_t 
+t, double *f)
- If t is a float, integer or rational number, its value is 
assigned over f. Note that if t is an integer or 
rational conversion may fail because the number cannot be represented as 
a float.
- bool PL_get_functor(term_t 
+t, functor_t *f)
- If t is compound or an atom, the Prolog representation of the 
name-arity pair will be assigned over f. See also
PL_get_name_arity() 
and PL_is_functor().
- bool PL_get_name_arity(term_t 
+t, atom_t *name, size_t *arity)
- If t is compound or an atom, the functor name will be 
assigned over name and the arity over arity 
(either or both may be NULL). See also PL_get_compound_name_arity(), PL_get_functor() 
and PL_is_functor().
- bool PL_get_compound_name_arity(term_t 
+t, atom_t *name, size_t *arity)
- If t is compound term, the functor name will be assigned over
name and the arity over arity (either or both may 
be
NULL). This is the same as PL_get_name_arity(), 
but this function fails if t is an atom.
- bool PL_get_module(term_t 
+t, module_t *module)
- If t is an atom, the system will look up or create the 
corresponding module and assign an opaque pointer to it over module.
- bool PL_get_arg(size_t 
index, term_t +t, term_t -a)
- If t is compound and index is between 1 and arity 
(inclusive), assign a with a term reference to the argument. 
Returns
FALSEif t is not a compound or index 
is out of range (zero or higher than the arity of the compound). This 
function never raises a Prolog exception.
- int _PL_get_arg(size_t 
index, term_t +t, term_t -a)
- Same as PL_get_arg(), 
but no checking is performed, neither whether t is actually a 
term nor whether index is a valid argument index. This is 
faster, but invalid usage leads to undefined behaviour.
- bool PL_get_dict_key(atom_t 
key, term_t +dict, term_t -value)
- If dict is a dict, get the associated value in value. 
Fails silently if key does not appear in dict or 
if if dict is not a dict.
All internal text representation in SWI-Prolog is represented using
char * plus length and allow for 0-bytes in them. 
The foreign library supports this by implementing a *_nchars() 
function for each applicable *_chars() function. Below we briefly 
present the signatures of these functions. For full documentation 
consult the *_chars() function.
- bool PL_get_atom_nchars(term_t 
t, size_t *len, char **s)
- See PL_get_atom_chars().
- bool PL_get_list_nchars(term_t 
t, size_t *len, char **s)
- See PL_get_list_chars().
- bool PL_get_nchars(term_t 
t, size_t *len, char **s, unsigned int flags)
- See PL_get_chars(). 
The len pointer may be NULL.
- bool PL_put_atom_nchars(term_t 
t, size_t len, const char *s)
- See PL_put_atom_chars().
- bool PL_put_string_nchars(term_t 
t, size_t len, const char *s)
- See PL_put_string_chars().
- bool PL_put_list_ncodes(term_t 
t, size_t len, const char *s)
- See PL_put_list_codes().
- bool PL_put_list_nchars(term_t 
t, size_t len, const char *s)
- See PL_put_list_chars().
- bool PL_unify_atom_nchars(term_t 
t, size_t len, const char *s)
- See PL_unify_atom_chars().
- bool PL_unify_string_nchars(term_t 
t, size_t len, const char *s)
- See PL_unify_string_chars().
- bool PL_unify_list_ncodes(term_t 
t, size_t len, const char *s)
- See PL_unify_codes().
- bool PL_unify_list_nchars(term_t 
t, size_t len, const char *s)
- See PL_unify_list_chars().
In addition, the following functions are available for creating and 
inspecting atoms:
- atom_t PL_new_atom_nchars(size_t 
len, const char *s)
- Create a new atom as PL_new_atom(), 
but using the given length and characters. If len is (size_t)-1, 
it is computed from s using
strlen(). See PL_new_atom() 
for error handling.
- const char * PL_atom_nchars(atom_t 
a, size_t *len)
- Extract the text and length of an atom. If you do not need the length, 
pass NULL as the value of len. If PL_atom_nchars() 
is called for a blob, NULL is returned.
Support for exchange of wide-character strings is still under 
consideration. The functions dealing with 8-bit character strings return 
failure when operating on a wide-character atom or Prolog string object. 
The functions below can extract and unify both 8-bit and wide atoms and 
string objects. Wide character strings are represented as C arrays of 
objects of the type pl_wchar_t, which is guaranteed to be 
the same as wchar_t on platforms supporting this type. For 
example, on MS-Windows, this represents a 16-bit UTF-16 string, while 
using the GNU C library (glibc) this represents 32-bit UCS4 characters.
- atom_t PL_new_atom_wchars(size_t 
len, const pl_wchar_t *s)
- Create atom from wide-character string as PL_new_atom_nchars() 
does for ISO-Latin-1 strings. If s only contains ISO-Latin-1 
characters a normal byte-array atom is created. If len is (size_t)-1, 
it is computed from s using wcslen(). See PL_new_atom() 
for error handling.
- const pl_wchar_t* PL_atom_wchars(atom_t 
atom, size_t *len)
- Extract characters from a wide-character atom. Succeeds on any atom 
marked as‘text’. If the underlying atom is a wide-character 
atom, the returned pointer is a pointer into the atom structure. If the 
atom is represented as an ISO-Latin-1 string, the returned pointer comes 
from Prolog's‘buffer stack’(see section 
12.4.14).
- bool PL_get_wchars(term_t 
t, size_t *len, pl_wchar_t **s, unsigned flags)
- Wide-character version of PL_get_chars(). 
The flags argument is the same as for PL_get_chars(). 
Note that this operation may return a pointer into Prolog's‘buffer 
stack’(see section 
12.4.14).
- bool PL_put_wchars(term_t 
-t, int type, size_t len, const pl_wchar_t *s)
- Put text from a wide character array in t. Arguments 
are the same as PL_unify_wchars().220The 
current implementation uses PL_put_variable() 
followed by PL_unify_wchars().
- bool PL_unify_wchars(term_t 
+t, int type, size_t len, const pl_wchar_t *s)
- Unify t with a textual representation of the C wide-character 
array s. The type argument defines the Prolog 
representation and is one of PL_ATOM,PL_STRING,PL_CODE_LISTorPL_CHAR_LIST.
- bool PL_unify_wchars_diff(term_t 
+t, term_t -tail, int type, size_t len, const pl_wchar_t *s)
- Difference list version of PL_unify_wchars(), 
only supporting the types PL_CODE_LISTandPL_CHAR_LIST. 
It serves two purposes. It allows for returning very long lists from 
data read from a stream without the need for a resizing buffer in C. 
Also, the use of difference lists is often practical for further 
processing in Prolog. Examples can be found inpackages/clib/readutil.cfrom the source distribution.
The functions from this section are intended to read a Prolog list 
from C. Suppose we expect a list of atoms; the code below will print the 
atoms, each on a line. Please note the following:
- We need a term_tterm reference for the 
elements (head). This reference is reused for each element.
- We walk over the list using PL_get_list_ex() 
which overwrites the list term_t. As it is not allowed to 
overwrite theterm_tpassed in as arguments to a predicate, 
we must
copy the argumentterm_t.
- SWI-Prolog atoms are Unicode objects. The PL_get_chars() 
returns a char*. We want it to convert atoms, return the 
result as a multibyte string (REP_UTF8may also be 
used) and finally we want an exception on type, instantiation or 
representation errors (if the system's default encoding cannot represent 
some characters of the Unicode atom). This may create temporary copies 
of the atom text - PL_STRINGS_MARK() ... PL_STRINGS_RELEASE() 
handles that.
- The *_ex() API functions are functionally the same as the 
ones without the _exsuffix, but they raise type, domain, 
or instantiation errors when the input is invalid; whereas the plain 
version may only raise resource exceptions if the request cannot be 
fulfilled due to resource exhaustion.
- PL_get_nil_ex() 
is designed to propagate an already raised exception.
foreign_t
pl_write_atoms(term_t l)
{ term_t head = PL_new_term_ref();   /* the elements */
  term_t tail = PL_copy_term_ref(l); /* copy (we modify tail) */
  int rc = TRUE;
  while( rc && PL_get_list_ex(tail, head, tail) )
  { PL_STRINGS_MARK();
      char *s;
      if (rc=PL_get_chars(head, &s, CVT_ATOM|REP_MB|CVT_EXCEPTION)) )
        rc = Sfprintf(Scurrent_output, "%s\n", s);
    PL_STRINGS_RELEASE();
  }
  return rc && PL_get_nil_ex(tail); /* test end for [] */
}
Note that as of version 7, lists have a new representation 
unless the option --traditional is used. see section 
5.1.
- bool PL_get_list(term_t 
+l, term_t -h, term_t -t)
- If l is a list and not the empty list, assign a term 
reference to the head to h and to the tail to t.
- bool PL_get_head(term_t 
+l, term_t -h)
- If l is a list and not the empty list, assign a term 
reference to the head to h.
- bool PL_get_tail(term_t 
+l, term_t -t)
- If l is a list and not the empty list, assign a term 
reference to the tail to t.
- bool PL_get_nil(term_t 
+l)
- Succeeds if l represents the list termination constant.
- int PL_skip_list(term_t 
+list, term_t -tail, size_t *len)
- This is a multi-purpose function to deal with lists. It allows for 
finding the length of a list, checking whether something is a list, etc. 
The reference tail is set to point to the end of the list,
len is filled with the number of list-cells skipped, and the 
return value indicates the status of the list:
- PL_LIST
- The list is a‘proper’list: one that ends in the list 
terminator constant and tail is filled with the terminator 
constant.
- PL_PARTIAL_LIST
- The list is a‘partial’list: one that ends in a variable and
tail is a reference to this variable.
- PL_CYCLIC_TERM
- The list is cyclic (e.g. X = [a|X]). tail points to an 
arbitrary cell of the list and len is at most twice the cycle 
length of the list.
- PL_NOT_A_LIST
- The term list is not a list at all. tail is bound 
to the non-list term and len is set to the number of 
list-cells skipped.
 
It is allowed to pass 0 for tail and NULLfor len.
 
- bool PL_scan_options(term_t 
options, int flags, const char* opttype, PL_option_t specs[], ...)
- Process an option list as we find with, e.g., write_term/2 
and many other builtin predicates. This function takes an option list 
(or dict) and in the variadic argument list pointers that receive the 
option values. PL_scan_options() 
takes care of validating the list, ensuring the list is not cyclic, 
validating the option type and storing the converted values using the 
supplied pointers.
Below is an example. While PL_option_tis a struct, its 
members are initialised using the PL_OPTION() macro. The data 
structure is not constant because PL_scan_options() 
adds the option names as
atoms to speed up option processing. The macro PL_OPTIONS_END 
terminates the option list.
 
static PL_option_t mypred_options[] =
{ PL_OPTION("quoted",   OPT_BOOL),
  PL_OPTION("length",   OPT_SIZE),
  PL_OPTION("callback", OPT_TERM),
  PL_OPTIONS_END
};
static foreign_t
mypred(term_t a1, term_t options)
{ int    quoted   = FALSE;
  size_t length   = 10;
  term_t callback = 0;
  if ( !PL_scan_options(options, 0, "mypred_options", mypred_options,
                        "ed, &length, &callback) )
    return FALSE;
  <implement mypred>
}
The only defined value for flags is currently OPT_ALL, 
which causes this function to raise a domain error if an option is 
passed that is not in specs. Default in SWI-Prolog is to 
silently ignore unknown options, unless the Prolog flag iso 
istrue. The opttype argument defines the type 
(group) of the options, e.g.,"write_option". Option types 
are defined by the ISO standard. SWI-Prolog only uses this ifOPT_ALLis specified, to raise adomain_errorof the indicated type 
if some option is unused. The type name is normally the name of the 
predicate followed by_optionor the name of a representative of a group of 
predicates to which the options apply.
 
Defined option types and their corresponding pointer type are 
described below.
 
- OPT_BOOL- int
- Convert the option value to a bool. This converts the values described 
by PL_get_bool(). 
In addition, an option without a value (i.e., a plain atom that denotes 
the option name) can act as a boolean
TRUE.
- OPT_INT- int
- OPT_INT64- int64_t
- OPT_UINT64- uint64_t
- OPT_SIZE- size_t
- OPT_DOUBLE- double
- Numeric values of various types. Raises an error if the Prolog value 
cannot be represented by the C type.
- OPT_STRING- char*
- Uses PL_get_chars() 
using the flags
CVT_ALL|REP_UTF8|BUF_STACK|CVT_EXCEPTION. The buffered 
string must be guarded using PL_STRINGS_MARK() 
and PL_STRINGS_RELEASE().
- OPT_ATOM- atom_t
- Accepts an atom. Note that if the C function that implements the 
predicate wishes to keep hold of the atom after it returns it must use PL_register_atom().
- OPT_TERM- term_t
- Accepts an arbitrary Prolog term. The term handle is scoped by the 
foreign predicate invocation. Terms can be preserved using
PL_record().
 
The ISO standard demands that if an option is repeated the last 
occurrence holds. This implies that PL_scan_options() 
must scan the option list to the end. 
Figure 6 
shows a simplified definition of write/1 
to illustrate the described functions. This simplified version does not 
deal with operators. It is called display/1, 
because it mimics closely the behaviour of this Edinburgh predicate.
foreign_t
pl_display(term_t t)
{ functor_t functor;
  int arity, len, n;
  char *s;
  switch( PL_term_type(t) )
  { case PL_VARIABLE:
    case PL_ATOM:
    case PL_INTEGER:
    case PL_FLOAT:
      PL_get_chars(t, &s, CVT_ALL);
      if (! Sfprintf(Scurrent_output, "%s", s) )
        PL_fail;
      break;
    case PL_STRING:
      if ( !PL_get_string_chars(t, &s, &len) &&
           !Sfprintf(Scurrent_output, "\"%s\"", s) )
        PL_fail;
      break;
    case PL_TERM:
    { term_t a = PL_new_term_ref();
      if ( !PL_get_name_arity(t, &name, &arity) &&
           !Sfprintf(Scurrent_output, "%s(", PL_atom_chars(name)) )
        PL_fail
      for(n=1; n<=arity; n++)
      { if ( ! PL_get_arg(n, t, a) )
          PL_fail;
        if ( n > 1 )
          if ( ! Sfprintf(Scurrent_output, ", ") )
            PL_fail;
        if ( !pl_display(a) )
          PL_fail;
      }
      if ( !Sfprintf(Scurrent_output, ")") )
        PL_fail;
      break;
    default:
      PL_fail;                          /* should not happen */
    }
  }
  PL_succeed;
}