summaryrefslogtreecommitdiff
path: root/lisp/core.c
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/core.c')
-rw-r--r--lisp/core.c7040
1 files changed, 7040 insertions, 0 deletions
diff --git a/lisp/core.c b/lisp/core.c
new file mode 100644
index 0000000..d834dd5
--- /dev/null
+++ b/lisp/core.c
@@ -0,0 +1,7040 @@
+/*
+ * Copyright (c) 2001 by The XFree86 Project, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE XFREE86 PROJECT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+ * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Except as contained in this notice, the name of the XFree86 Project shall
+ * not be used in advertising or otherwise to promote the sale, use or other
+ * dealings in this Software without prior written authorization from the
+ * XFree86 Project.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/core.c,v 1.69 2002/12/20 04:32:45 paulo Exp $ */
+
+#include "io.h"
+#include "core.h"
+#include "format.h"
+#include "helper.h"
+#include "package.h"
+#include "private.h"
+#include "write.h"
+
+/*
+ * Types
+ */
+typedef struct _SeqInfo {
+ LispType type;
+ union {
+ LispObj *list;
+ LispObj **vector;
+ unsigned char *string;
+ } data;
+} SeqInfo;
+
+#define SETSEQ(seq, object) \
+ switch (seq.type = XOBJECT_TYPE(object)) { \
+ case LispString_t: \
+ seq.data.string = (unsigned char*)THESTR(object); \
+ break; \
+ case LispCons_t: \
+ seq.data.list = object; \
+ break; \
+ default: \
+ seq.data.list = object->data.array.list; \
+ break; \
+ }
+
+#ifdef NEED_SETENV
+extern int setenv(const char *name, const char *value, int overwrite);
+extern void unsetenv(const char *name);
+#endif
+
+/*
+ * Prototypes
+ */
+#define NONE 0
+
+#define REMOVE 1
+#define SUBSTITUTE 2
+#define DELETE 3
+#define NSUBSTITUTE 4
+
+#define ASSOC 1
+#define MEMBER 2
+
+#define FIND 1
+#define POSITION 2
+
+#define IF 1
+#define IFNOT 2
+
+#define UNION 1
+#define INTERSECTION 2
+#define SETDIFFERENCE 3
+#define SETEXCLUSIVEOR 4
+#define SUBSETP 5
+#define NSETDIFFERENCE 6
+#define NINTERSECTION 7
+#define NUNION 8
+#define NSETEXCLUSIVEOR 9
+
+#define COPY_LIST 1
+#define COPY_ALIST 2
+#define COPY_TREE 3
+
+#define EVERY 1
+#define SOME 2
+#define NOTEVERY 3
+#define NOTANY 4
+
+/* Call directly LispObjectCompare() if possible */
+#define FCODE(predicate) \
+ predicate == Oeql ? FEQL : \
+ predicate == Oequal ? FEQUAL : \
+ predicate == Oeq ? FEQ : \
+ predicate == Oequalp ? FEQUALP : 0
+#define FCOMPARE(predicate, left, right, code) \
+ code == FEQ ? left == right : \
+ code ? LispObjectCompare(left, right, code) != NIL : \
+ APPLY2(predicate, left, right) != NIL
+
+#define FUNCTION_CHECK(predicate) \
+ if (FUNCTIONP(predicate)) \
+ predicate = (predicate)->data.atom->object
+
+#define CHECK_TEST_0() \
+ if (test != UNSPEC && test_not != UNSPEC) \
+ LispDestroy("%s: specify either :TEST or :TEST-NOT", \
+ STRFUN(builtin))
+
+#define CHECK_TEST() \
+ CHECK_TEST_0(); \
+ if (test_not == UNSPEC) { \
+ if (test == UNSPEC) \
+ lambda = Oeql; \
+ else \
+ lambda = test; \
+ expect = 1; \
+ } \
+ else { \
+ lambda = test_not; \
+ expect = 0; \
+ } \
+ FUNCTION_CHECK(lambda); \
+ code = FCODE(lambda)
+
+
+static LispObj *LispAdjoin(LispBuiltin*,
+ LispObj*, LispObj*, LispObj*, LispObj*, LispObj*);
+static LispObj *LispAssocOrMember(LispBuiltin*, int, int);
+static LispObj *LispEverySomeAnyNot(LispBuiltin*, int);
+static LispObj *LispFindOrPosition(LispBuiltin*, int, int);
+static LispObj *LispDeleteOrRemoveDuplicates(LispBuiltin*, int);
+static LispObj *LispDeleteRemoveXSubstitute(LispBuiltin*, int, int);
+static LispObj *LispListSet(LispBuiltin*, int);
+static LispObj *LispMapc(LispBuiltin*, int);
+static LispObj *LispMapl(LispBuiltin*, int);
+static LispObj *LispMapnconc(LispObj*);
+extern LispObj *LispRunSetf(LispArgList*, LispObj*, LispObj*, LispObj*);
+extern LispObj *LispRunSetfMacro(LispAtom*, LispObj*, LispObj*);
+static LispObj *LispMergeSort(LispObj*, LispObj*, LispObj*, int);
+static LispObj *LispXReverse(LispBuiltin*, int);
+static LispObj *LispCopyList(LispBuiltin*, LispObj*, int);
+static LispObj *LispValuesList(LispBuiltin*, int);
+static LispObj *LispTreeEqual(LispObj*, LispObj*, LispObj*, int);
+static LispDocType_t LispDocumentationType(LispBuiltin*, LispObj*);
+
+extern void LispSetAtomObjectProperty(LispAtom*, LispObj*);
+
+/*
+ * Initialization
+ */
+LispObj *Oeq, *Oeql, *Oequal, *Oequalp, *Omake_array,
+ *Kinitial_contents, *Osetf, *Ootherwise, *Oquote;
+LispObj *Ogensym_counter;
+
+Atom_id Svariable, Sstructure, Stype, Ssetf;
+
+/*
+ * Implementation
+ */
+void
+LispCoreInit(void)
+{
+ Oeq = STATIC_ATOM("EQ");
+ Oeql = STATIC_ATOM("EQL");
+ Oequal = STATIC_ATOM("EQUAL");
+ Oequalp = STATIC_ATOM("EQUALP");
+ Omake_array = STATIC_ATOM("MAKE-ARRAY");
+ Kinitial_contents = KEYWORD("INITIAL-CONTENTS");
+ Osetf = STATIC_ATOM("SETF");
+ Ootherwise = STATIC_ATOM("OTHERWISE");
+ LispExportSymbol(Ootherwise);
+ Oquote = STATIC_ATOM("QUOTE");
+ LispExportSymbol(Oquote);
+
+ Svariable = GETATOMID("VARIABLE");
+ Sstructure = GETATOMID("STRUCTURE");
+ Stype = GETATOMID("TYPE");
+
+ /* Create as a constant so that only the C code should change the value */
+ Ogensym_counter = STATIC_ATOM("*GENSYM-COUNTER*");
+ LispDefconstant(Ogensym_counter, FIXNUM(0), NIL);
+ LispExportSymbol(Ogensym_counter);
+
+ Ssetf = ATOMID(Osetf);
+}
+
+LispObj *
+Lisp_Acons(LispBuiltin *builtin)
+/*
+ acons key datum alist
+ */
+{
+ LispObj *key, *datum, *alist;
+
+ alist = ARGUMENT(2);
+ datum = ARGUMENT(1);
+ key = ARGUMENT(0);
+
+ return (CONS(CONS(key, datum), alist));
+}
+
+static LispObj *
+LispAdjoin(LispBuiltin*builtin, LispObj *item, LispObj *list,
+ LispObj *key, LispObj *test, LispObj *test_not)
+{
+ GC_ENTER();
+ int code, expect, value;
+ LispObj *lambda, *compare, *object;
+
+ CHECK_LIST(list);
+ CHECK_TEST();
+
+ if (key != UNSPEC) {
+ item = APPLY1(key, item);
+ /* Result is not guaranteed to be gc protected */
+ GC_PROTECT(item);
+ }
+
+ /* Check if item is not already in place */
+ for (object = list; CONSP(object); object = CDR(object)) {
+ compare = CAR(object);
+ if (key != UNSPEC) {
+ compare = APPLY1(key, compare);
+ GC_PROTECT(compare);
+ value = FCOMPARE(lambda, item, compare, code);
+ /* Unprotect compare... */
+ --lisp__data.protect.length;
+ }
+ else
+ value = FCOMPARE(lambda, item, compare, code);
+
+ if (value == expect) {
+ /* Item is already in list */
+ GC_LEAVE();
+
+ return (list);
+ }
+ }
+ GC_LEAVE();
+
+ return (CONS(item, list));
+}
+
+LispObj *
+Lisp_Adjoin(LispBuiltin *builtin)
+/*
+ adjoin item list &key key test test-not
+ */
+{
+ LispObj *item, *list, *key, *test, *test_not;
+
+ test_not = ARGUMENT(4);
+ test = ARGUMENT(3);
+ key = ARGUMENT(2);
+ list = ARGUMENT(1);
+ item = ARGUMENT(0);
+
+ return (LispAdjoin(builtin, item, list, key, test, test_not));
+}
+
+LispObj *
+Lisp_Append(LispBuiltin *builtin)
+/*
+ append &rest lists
+ */
+{
+ GC_ENTER();
+ LispObj *result, *cons, *list;
+
+ LispObj *lists;
+
+ lists = ARGUMENT(0);
+
+ /* no arguments */
+ if (!CONSP(lists))
+ return (NIL);
+
+ /* skip initial nil lists */
+ for (; CONSP(CDR(lists)) && CAR(lists) == NIL; lists = CDR(lists))
+ ;
+
+ /* last argument is not copied (even if it is the single argument) */
+ if (!CONSP(CDR(lists)))
+ return (CAR(lists));
+
+ /* make sure result is a list */
+ list = CAR(lists);
+ CHECK_CONS(list);
+ result = cons = CONS(CAR(list), NIL);
+ GC_PROTECT(result);
+ for (list = CDR(list); CONSP(list); list = CDR(list)) {
+ RPLACD(cons, CONS(CAR(list), NIL));
+ cons = CDR(cons);
+ }
+ lists = CDR(lists);
+
+ /* copy intermediate lists */
+ for (; CONSP(CDR(lists)); lists = CDR(lists)) {
+ list = CAR(lists);
+ if (list == NIL)
+ continue;
+ /* intermediate elements must be lists */
+ CHECK_CONS(list);
+ for (; CONSP(list); list = CDR(list)) {
+ RPLACD(cons, CONS(CAR(list), NIL));
+ cons = CDR(cons);
+ }
+ }
+
+ /* add last element */
+ RPLACD(cons, CAR(lists));
+
+ GC_LEAVE();
+
+ return (result);
+}
+
+LispObj *
+Lisp_Aref(LispBuiltin *builtin)
+/*
+ aref array &rest subscripts
+ */
+{
+ long c, count, idx, seq;
+ LispObj *obj, *dim;
+
+ LispObj *array, *subscripts;
+
+ subscripts = ARGUMENT(1);
+ array = ARGUMENT(0);
+
+ /* accept strings also */
+ if (STRINGP(array) && CONSP(subscripts) && CDR(subscripts) == NIL) {
+ long offset, length = STRLEN(array);
+
+ CHECK_INDEX(CAR(subscripts));
+ offset = FIXNUM_VALUE(CAR(subscripts));
+
+ if (offset >= length)
+ LispDestroy("%s: index %ld too large for sequence length %ld",
+ STRFUN(builtin), offset, length);
+
+ return (SCHAR(THESTR(array)[offset]));
+ }
+
+ CHECK_ARRAY(array);
+
+ for (count = 0, dim = subscripts, obj = array->data.array.dim; CONSP(dim);
+ count++, dim = CDR(dim), obj = CDR(obj)) {
+ if (count >= array->data.array.rank)
+ LispDestroy("%s: too many subscripts %s",
+ STRFUN(builtin), STROBJ(subscripts));
+ if (!INDEXP(CAR(dim)) ||
+ FIXNUM_VALUE(CAR(dim)) >= FIXNUM_VALUE(CAR(obj)))
+ LispDestroy("%s: %s is out of range or a bad index",
+ STRFUN(builtin), STROBJ(CAR(dim)));
+ }
+ if (count < array->data.array.rank)
+ LispDestroy("%s: too few subscripts %s",
+ STRFUN(builtin), STROBJ(subscripts));
+
+ for (count = seq = 0, dim = subscripts; CONSP(dim); dim = CDR(dim), seq++) {
+ for (idx = 0, obj = array->data.array.dim; idx < seq;
+ obj = CDR(obj), ++idx)
+ ;
+ for (c = 1, obj = CDR(obj); obj != NIL; obj = CDR(obj))
+ c *= FIXNUM_VALUE(CAR(obj));
+ count += c * FIXNUM_VALUE(CAR(dim));
+ }
+
+ for (array = array->data.array.list; count > 0; array = CDR(array), count--)
+ ;
+
+ return (CAR(array));
+}
+
+static LispObj *
+LispAssocOrMember(LispBuiltin *builtin, int function, int comparison)
+/*
+ assoc item list &key test test-not key
+ assoc-if predicate list &key key
+ assoc-if-not predicate list &key key
+ member item list &key test test-not key
+ member-if predicate list &key key
+ member-if-not predicate list &key key
+ */
+{
+ int code = 0, expect, value;
+ LispObj *lambda, *result, *compare;
+
+ LispObj *item, *list, *test, *test_not, *key;
+
+ if (comparison == NONE) {
+ key = ARGUMENT(4);
+ test_not = ARGUMENT(3);
+ test = ARGUMENT(2);
+ list = ARGUMENT(1);
+ item = ARGUMENT(0);
+ lambda = NIL;
+ }
+ else {
+ key = ARGUMENT(2);
+ list = ARGUMENT(1);
+ lambda = ARGUMENT(0);
+ test = test_not = UNSPEC;
+ item = NIL;
+ }
+
+ if (list == NIL)
+ return (NIL);
+ CHECK_CONS(list);
+
+ /* Resolve compare function, and expected result of comparison */
+ if (comparison == NONE) {
+ CHECK_TEST();
+ }
+ else
+ expect = comparison == IFNOT ? 0 : 1;
+
+ result = NIL;
+ for (; CONSP(list); list = CDR(list)) {
+ compare = CAR(list);
+ if (function == ASSOC) {
+ if (!CONSP(compare))
+ continue;
+ compare = CAR(compare);
+ }
+ if (key != UNSPEC)
+ compare = APPLY1(key, compare);
+
+ if (comparison == NONE)
+ value = FCOMPARE(lambda, item, compare, code);
+ else
+ value = APPLY1(lambda, compare) != NIL;
+ if (value == expect) {
+ result = list;
+ if (function == ASSOC)
+ result = CAR(result);
+ break;
+ }
+ }
+ if (function == MEMBER) {
+ CHECK_LIST(list);
+ }
+
+ return (result);
+}
+
+LispObj *
+Lisp_Assoc(LispBuiltin *builtin)
+/*
+ assoc item list &key test test-not key
+ */
+{
+ return (LispAssocOrMember(builtin, ASSOC, NONE));
+}
+
+LispObj *
+Lisp_AssocIf(LispBuiltin *builtin)
+/*
+ assoc-if predicate list &key key
+ */
+{
+ return (LispAssocOrMember(builtin, ASSOC, IF));
+}
+
+LispObj *
+Lisp_AssocIfNot(LispBuiltin *builtin)
+/*
+ assoc-if-not predicate list &key key
+ */
+{
+ return (LispAssocOrMember(builtin, ASSOC, IFNOT));
+}
+
+LispObj *
+Lisp_And(LispBuiltin *builtin)
+/*
+ and &rest args
+ */
+{
+ LispObj *result = T, *args;
+
+ args = ARGUMENT(0);
+
+ for (; CONSP(args); args = CDR(args)) {
+ result = EVAL(CAR(args));
+ if (result == NIL)
+ break;
+ }
+
+ return (result);
+}
+
+LispObj *
+Lisp_Apply(LispBuiltin *builtin)
+/*
+ apply function arg &rest more-args
+ */
+{
+ GC_ENTER();
+ LispObj *result, *arguments;
+
+ LispObj *function, *arg, *more_args;
+
+ more_args = ARGUMENT(2);
+ arg = ARGUMENT(1);
+ function = ARGUMENT(0);
+
+ if (more_args == NIL) {
+ CHECK_LIST(arg);
+ arguments = arg;
+ for (; CONSP(arg); arg = CDR(arg))
+ ;
+ CHECK_LIST(arg);
+ }
+ else {
+ LispObj *cons;
+
+ CHECK_CONS(more_args);
+ arguments = cons = CONS(arg, NIL);
+ GC_PROTECT(arguments);
+ for (arg = CDR(more_args);
+ CONSP(arg);
+ more_args = arg, arg = CDR(arg)) {
+ RPLACD(cons, CONS(CAR(more_args), NIL));
+ cons = CDR(cons);
+ }
+ more_args = CAR(more_args);
+ if (more_args != NIL) {
+ for (arg = more_args; CONSP(arg); arg = CDR(arg))
+ ;
+ CHECK_LIST(arg);
+ RPLACD(cons, more_args);
+ }
+ }
+
+ result = APPLY(function, arguments);
+ GC_LEAVE();
+
+ return (result);
+}
+
+LispObj *
+Lisp_Atom(LispBuiltin *builtin)
+/*
+ atom object
+ */
+{
+ LispObj *object;
+
+ object = ARGUMENT(0);
+
+ return (CONSP(object) ? NIL : T);
+}
+
+LispObj *
+Lisp_Block(LispBuiltin *builtin)
+/*
+ block name &rest body
+ */
+{
+ int did_jump, *pdid_jump = &did_jump;
+ LispObj *res, **pres = &res, **pbody;
+ LispBlock *block;
+
+ LispObj *name, *body;
+
+ body = ARGUMENT(1);
+ name = ARGUMENT(0);
+
+ if (!SYMBOLP(name) && name != NIL && name != T)
+ LispDestroy("%s: %s cannot name a block",
+ STRFUN(builtin), STROBJ(name));
+
+ pbody = &body;
+ *pres = NIL;
+ *pdid_jump = 1;
+ block = LispBeginBlock(name, LispBlockTag);
+ if (setjmp(block->jmp) == 0) {
+ for (; CONSP(body); body = CDR(body))
+ res = EVAL(CAR(body));
+ *pdid_jump = 0;
+ }
+ LispEndBlock(block);
+ if (*pdid_jump)
+ *pres = lisp__data.block.block_ret;
+
+ return (res);
+}
+
+LispObj *
+Lisp_Boundp(LispBuiltin *builtin)
+/*
+ boundp symbol
+ */
+{
+ LispAtom *atom;
+
+ LispObj *symbol = ARGUMENT(0);
+
+ CHECK_SYMBOL(symbol);
+
+ atom = symbol->data.atom;
+ if (atom->package == lisp__data.keyword ||
+ (atom->a_object && atom->property->value != UNBOUND))
+ return (T);
+
+ return (NIL);
+}
+
+LispObj *
+Lisp_Butlast(LispBuiltin *builtin)
+/*
+ butlast list &optional count
+ */
+{
+ GC_ENTER();
+ long length, count;
+ LispObj *result, *cons, *list, *ocount;
+
+ ocount = ARGUMENT(1);
+ list = ARGUMENT(0);
+
+ CHECK_LIST(list);
+ if (ocount == UNSPEC)
+ count = 1;
+ else {
+ CHECK_INDEX(ocount);
+ count = FIXNUM_VALUE(ocount);
+ }
+ length = LispLength(list);
+
+ if (count == 0)
+ return (list);
+ else if (count >= length)
+ return (NIL);
+
+ length -= count + 1;
+ result = cons = CONS(CAR(list), NIL);
+ GC_PROTECT(result);
+ for (list = CDR(list); length > 0; list = CDR(list), length--) {
+ RPLACD(cons, CONS(CAR(list), NIL));
+ cons = CDR(cons);
+ }
+ GC_LEAVE();
+
+ return (result);
+}
+
+LispObj *
+Lisp_Nbutlast(LispBuiltin *builtin)
+/*
+ nbutlast list &optional count
+ */
+{
+ long length, count;
+ LispObj *result, *list, *ocount;
+
+ ocount = ARGUMENT(1);
+ list = ARGUMENT(0);
+
+ CHECK_LIST(list);
+ if (ocount == UNSPEC)
+ count = 1;
+ else {
+ CHECK_INDEX(ocount);
+ count = FIXNUM_VALUE(ocount);
+ }
+ length = LispLength(list);
+
+ if (count == 0)
+ return (list);
+ else if (count >= length)
+ return (NIL);
+
+ length -= count + 1;
+ result = list;
+ for (; length > 0; list = CDR(list), length--)
+ ;
+ RPLACD(list, NIL);
+
+ return (result);
+}
+
+LispObj *
+Lisp_Car(LispBuiltin *builtin)
+/*
+ car list
+ */
+{
+ LispObj *list, *result = NULL;
+
+ list = ARGUMENT(0);
+
+ if (list == NIL)
+ result = NIL;
+ else {
+ CHECK_CONS(list);
+ result = CAR(list);
+ }
+
+ return (result);
+}
+
+LispObj *
+Lisp_Case(LispBuiltin *builtin)
+/*
+ case keyform &rest body
+ */
+{
+ LispObj *result, *code, *keyform, *body, *form;
+
+ body = ARGUMENT(1);
+ keyform = ARGUMENT(0);
+
+ result = NIL;
+ keyform = EVAL(keyform);
+
+ for (; CONSP(body); body = CDR(body)) {
+ code = CAR(body);
+ CHECK_CONS(code);
+
+ form = CAR(code);
+ if (form == T || form == Ootherwise) {
+ if (CONSP(CDR(body)))
+ LispDestroy("%s: %s must be the last clause",
+ STRFUN(builtin), STROBJ(CAR(code)));
+ result = CDR(code);
+ break;
+ }
+ else if (CONSP(form)) {
+ for (; CONSP(form); form = CDR(form))
+ if (XEQL(keyform, CAR(form)) == T) {
+ result = CDR(code);
+ break;
+ }
+ if (CONSP(form)) /* if found match */
+ break;
+ }
+ else if (XEQL(keyform, form) == T) {
+ result = CDR(code);
+ break;
+ }
+ }
+
+ for (body = result; CONSP(body); body = CDR(body))
+ result = EVAL(CAR(body));
+
+ return (result);
+}
+
+LispObj *
+Lisp_Catch(LispBuiltin *builtin)
+/*
+ catch tag &rest body
+ */
+{
+ int did_jump, *pdid_jump = &did_jump;
+ LispObj *res, **pres = &res;
+ LispBlock *block;
+
+ LispObj *tag, *body, **pbody;
+
+ body = ARGUMENT(1);
+ tag = ARGUMENT(0);
+
+ pbody = &body;
+ *pres = NIL;
+ *pdid_jump = 1;
+ block = LispBeginBlock(tag, LispBlockCatch);
+ if (setjmp(block->jmp) == 0) {
+ for (; CONSP(body); body = CDR(body))
+ res = EVAL(CAR(body));
+ *pdid_jump = 0;
+ }
+ LispEndBlock(block);
+ if (*pdid_jump)
+ *pres = lisp__data.block.block_ret;
+
+ return (res);
+}
+
+LispObj *
+Lisp_Coerce(LispBuiltin *builtin)
+/*
+ coerce object result-type
+ */
+{
+ LispObj *object, *result_type;
+
+ result_type = ARGUMENT(1);
+ object = ARGUMENT(0);
+
+ return (LispCoerce(builtin, object, result_type));
+}
+
+LispObj *
+Lisp_Cdr(LispBuiltin *builtin)
+/*
+ cdr list
+ */
+{
+ LispObj *list, *result = NULL;
+
+ list = ARGUMENT(0);
+
+ if (list == NIL)
+ result = NIL;
+ else {
+ CHECK_CONS(list);
+ result = CDR(list);
+ }
+
+ return (result);
+}
+
+LispObj *
+Lisp_C_r(LispBuiltin *builtin)
+/*
+ c[ad]{2,4}r list
+ */
+{
+ char *desc;
+
+ LispObj *list, *result = NULL;
+
+ list = ARGUMENT(0);
+
+ result = list;
+ desc = STRFUN(builtin);
+ while (desc[1] != 'R')
+ ++desc;
+ while (*desc != 'C') {
+ if (result == NIL)
+ break;
+ CHECK_CONS(result);
+ result = *desc == 'A' ? CAR(result) : CDR(result);
+ --desc;
+ }
+
+ return (result);
+}
+
+LispObj *
+Lisp_Cond(LispBuiltin *builtin)
+/*
+ cond &rest body
+ */
+{
+ LispObj *result, *code, *body;
+
+ body = ARGUMENT(0);
+
+ result = NIL;
+ for (; CONSP(body); body = CDR(body)) {
+ code = CAR(body);
+
+ CHECK_CONS(code);
+ result = EVAL(CAR(code));
+ if (result == NIL)
+ continue;
+ for (code = CDR(code); CONSP(code); code = CDR(code))
+ result = EVAL(CAR(code));
+ break;
+ }
+
+ return (result);
+}
+
+static LispObj *
+LispCopyList(LispBuiltin *builtin, LispObj *list, int function)
+{
+ GC_ENTER();
+ LispObj *result, *cons;
+
+ if (list == NIL)
+ return (list);
+ CHECK_CONS(list);
+
+ result = cons = CONS(NIL, NIL);
+ GC_PROTECT(result);
+ if (CONSP(CAR(list))) {
+ switch (function) {
+ case COPY_LIST:
+ RPLACA(result, CAR(list));
+ break;
+ case COPY_ALIST:
+ RPLACA(result, CONS(CAR(CAR(list)), CDR(CAR(list))));
+ break;
+ case COPY_TREE:
+ RPLACA(result, LispCopyList(builtin, CAR(list), COPY_TREE));
+ break;
+ }
+ }
+ else
+ RPLACA(result, CAR(list));
+
+ for (list = CDR(list); CONSP(list); list = CDR(list)) {
+ CDR(cons) = CONS(NIL, NIL);
+ cons = CDR(cons);
+ if (CONSP(CAR(list))) {
+ switch (function) {
+ case COPY_LIST:
+ RPLACA(cons, CAR(list));
+ break;
+ case COPY_ALIST:
+ RPLACA(cons, CONS(CAR(CAR(list)), CDR(CAR(list))));
+ break;
+ case COPY_TREE:
+ RPLACA(cons, LispCopyList(builtin, CAR(list), COPY_TREE));
+ break;
+ }
+ }
+ else
+ RPLACA(cons, CAR(list));
+ }
+ /* in case list is dotted */
+ RPLACD(cons, list);
+ GC_LEAVE();
+
+ return (result);
+}
+
+LispObj *
+Lisp_CopyAlist(LispBuiltin *builtin)
+/*
+ copy-alist list
+ */
+{
+ LispObj *list;
+
+ list = ARGUMENT(0);
+
+ return (LispCopyList(builtin, list, COPY_ALIST));
+}
+
+LispObj *
+Lisp_CopyList(LispBuiltin *builtin)
+/*
+ copy-list list
+ */
+{
+ LispObj *list;
+
+ list = ARGUMENT(0);
+
+ return (LispCopyList(builtin, list, COPY_LIST));
+}
+
+LispObj *
+Lisp_CopyTree(LispBuiltin *builtin)
+/*
+ copy-tree list
+ */
+{
+ LispObj *list;
+
+ list = ARGUMENT(0);
+
+ return (LispCopyList(builtin, list, COPY_TREE));
+}
+
+LispObj *
+Lisp_Cons(LispBuiltin *builtin)
+/*
+ cons car cdr
+ */
+{
+ LispObj *car, *cdr;
+
+ cdr = ARGUMENT(1);
+ car = ARGUMENT(0);
+
+ return (CONS(car, cdr));
+}
+
+LispObj *
+Lisp_Consp(LispBuiltin *builtin)
+/*
+ consp object
+ */
+{
+ LispObj *object;
+
+ object = ARGUMENT(0);
+
+ return (CONSP(object) ? T : NIL);
+}
+
+LispObj *
+Lisp_Constantp(LispBuiltin *builtin)
+/*
+ constantp form &optional environment
+ */
+{
+ LispObj *form, *environment;
+
+ environment = ARGUMENT(1);
+ form = ARGUMENT(0);
+
+ /* not all self-evaluating objects are considered constants */
+ if (!POINTERP(form) ||
+ NUMBERP(form) ||
+ XQUOTEP(form) ||
+ (XCONSP(form) && CAR(form) == Oquote) ||
+ (XSYMBOLP(form) && form->data.atom->constant) ||
+ XSTRINGP(form) ||
+ XARRAYP(form))
+ return (T);
+
+ return (NIL);
+}
+
+LispObj *
+Lisp_Defconstant(LispBuiltin *builtin)
+/*
+ defconstant name initial-value &optional documentation
+ */
+{
+ LispObj *name, *initial_value, *documentation;
+
+ documentation = ARGUMENT(2);
+ initial_value = ARGUMENT(1);
+ name = ARGUMENT(0);
+
+ CHECK_SYMBOL(name);
+ if (documentation != UNSPEC) {
+ CHECK_STRING(documentation);
+ }
+ else
+ documentation = NIL;
+ LispDefconstant(name, EVAL(initial_value), documentation);
+
+ return (name);
+}
+
+LispObj *
+Lisp_Defmacro(LispBuiltin *builtin)
+/*
+ defmacro name lambda-list &rest body
+ */
+{
+ LispArgList *alist;
+
+ LispObj *lambda, *name, *lambda_list, *body;
+
+ body = ARGUMENT(2);
+ lambda_list = ARGUMENT(1);
+ name = ARGUMENT(0);
+
+ CHECK_SYMBOL(name);
+ alist = LispCheckArguments(LispMacro, lambda_list, ATOMID(name), 0);
+
+ if (CONSP(body) && STRINGP(CAR(body))) {
+ LispAddDocumentation(name, CAR(body), LispDocFunction);
+ body = CDR(body);
+ }
+
+ lambda_list = LispListProtectedArguments(alist);
+ lambda = LispNewLambda(name, body, lambda_list, LispMacro);
+
+ if (name->data.atom->a_builtin || name->data.atom->a_compiled) {
+ if (name->data.atom->a_builtin) {
+ ERROR_CHECK_SPECIAL_FORM(name->data.atom);
+ }
+ /* redefining these may cause surprises if bytecode
+ * compiled functions references them */
+ LispWarning("%s: %s is being redefined", STRFUN(builtin), ATOMID(name));
+
+ LispRemAtomBuiltinProperty(name->data.atom);
+ }
+
+ LispSetAtomFunctionProperty(name->data.atom, lambda, alist);
+ LispUseArgList(alist);
+
+ return (name);
+}
+
+LispObj *
+Lisp_Defun(LispBuiltin *builtin)
+/*
+ defun name lambda-list &rest body
+ */
+{
+ LispArgList *alist;
+
+ LispObj *lambda, *name, *lambda_list, *body;
+
+ body = ARGUMENT(2);
+ lambda_list = ARGUMENT(1);
+ name = ARGUMENT(0);
+
+ CHECK_SYMBOL(name);
+ alist = LispCheckArguments(LispFunction, lambda_list, ATOMID(name), 0);
+
+ if (CONSP(body) && STRINGP(CAR(body))) {
+ LispAddDocumentation(name, CAR(body), LispDocFunction);
+ body = CDR(body);
+ }
+
+ lambda_list = LispListProtectedArguments(alist);
+ lambda = LispNewLambda(name, body, lambda_list, LispFunction);
+
+ if (name->data.atom->a_builtin || name->data.atom->a_compiled) {
+ if (name->data.atom->a_builtin) {
+ ERROR_CHECK_SPECIAL_FORM(name->data.atom);
+ }
+ /* redefining these may cause surprises if bytecode
+ * compiled functions references them */
+ LispWarning("%s: %s is being redefined", STRFUN(builtin), ATOMID(name));
+
+ LispRemAtomBuiltinProperty(name->data.atom);
+ }
+ LispSetAtomFunctionProperty(name->data.atom, lambda, alist);
+ LispUseArgList(alist);
+
+ return (name);
+}
+
+LispObj *
+Lisp_Defsetf(LispBuiltin *builtin)
+/*
+ defsetf function lambda-list &rest body
+ */
+{
+ LispArgList *alist;
+ LispObj *obj;
+ LispObj *lambda, *function, *lambda_list, *store, *body;
+
+ body = ARGUMENT(2);
+ lambda_list = ARGUMENT(1);
+ function = ARGUMENT(0);
+
+ CHECK_SYMBOL(function);
+
+ if (body == NIL || (CONSP(body) && STRINGP(CAR(body)))) {
+ if (!SYMBOLP(lambda_list))
+ LispDestroy("%s: syntax error %s %s",
+ STRFUN(builtin), STROBJ(function), STROBJ(lambda_list));
+ if (body != NIL)
+ LispAddDocumentation(function, CAR(body), LispDocSetf);
+
+ LispSetAtomSetfProperty(function->data.atom, lambda_list, NULL);
+
+ return (function);
+ }
+
+ alist = LispCheckArguments(LispSetf, lambda_list, ATOMID(function), 0);
+
+ store = CAR(body);
+ if (!CONSP(store))
+ LispDestroy("%s: %s is a bad store value",
+ STRFUN(builtin), STROBJ(store));
+ for (obj = store; CONSP(obj); obj = CDR(obj)) {
+ CHECK_SYMBOL(CAR(obj));
+ }
+
+ body = CDR(body);
+ if (CONSP(body) && STRINGP(CAR(body))) {
+ LispAddDocumentation(function, CAR(body), LispDocSetf);
+ body = CDR(body);
+ }
+
+ lambda = LispNewLambda(function, body, store, LispSetf);
+ LispSetAtomSetfProperty(function->data.atom, lambda, alist);
+ LispUseArgList(alist);
+
+ return (function);
+}
+
+LispObj *
+Lisp_Defparameter(LispBuiltin *builtin)
+/*
+ defparameter name initial-value &optional documentation
+ */
+{
+ LispObj *name, *initial_value, *documentation;
+
+ documentation = ARGUMENT(2);
+ initial_value = ARGUMENT(1);
+ name = ARGUMENT(0);
+
+ CHECK_SYMBOL(name);
+ if (documentation != UNSPEC) {
+ CHECK_STRING(documentation);
+ }
+ else
+ documentation = NIL;
+
+ LispProclaimSpecial(name, EVAL(initial_value), documentation);
+
+ return (name);
+}
+
+LispObj *
+Lisp_Defvar(LispBuiltin *builtin)
+/*
+ defvar name &optional initial-value documentation
+ */
+{
+ LispObj *name, *initial_value, *documentation;
+
+ documentation = ARGUMENT(2);
+ initial_value = ARGUMENT(1);
+ name = ARGUMENT(0);
+
+ CHECK_SYMBOL(name);
+ if (documentation != UNSPEC) {
+ CHECK_STRING(documentation);
+ }
+ else
+ documentation = NIL;
+
+ LispProclaimSpecial(name,
+ initial_value != UNSPEC ? EVAL(initial_value) : NULL,
+ documentation);
+
+ return (name);
+}
+
+LispObj *
+Lisp_Delete(LispBuiltin *builtin)
+/*
+ delete item sequence &key from-end test test-not start end count key
+ */
+{
+ return (LispDeleteRemoveXSubstitute(builtin, DELETE, NONE));
+}
+
+LispObj *
+Lisp_DeleteIf(LispBuiltin *builtin)
+/*
+ delete-if predicate sequence &key from-end start end count key
+ */
+{
+ return (LispDeleteRemoveXSubstitute(builtin, DELETE, IF));
+}
+
+LispObj *
+Lisp_DeleteIfNot(LispBuiltin *builtin)
+/*
+ delete-if-not predicate sequence &key from-end start end count key
+ */
+{
+ return (LispDeleteRemoveXSubstitute(builtin, DELETE, IFNOT));
+}
+
+LispObj *
+Lisp_DeleteDuplicates(LispBuiltin *builtin)
+/*
+ delete-duplicates sequence &key from-end test test-not start end key
+ */
+{
+ return (LispDeleteOrRemoveDuplicates(builtin, DELETE));
+}
+
+LispObj *
+Lisp_Do(LispBuiltin *builtin)
+/*
+ do init test &rest body
+ */
+{
+ return (LispDo(builtin, 0));
+}
+
+LispObj *
+Lisp_DoP(LispBuiltin *builtin)
+/*
+ do* init test &rest body
+ */
+{
+ return (LispDo(builtin, 1));
+}
+
+static LispDocType_t
+LispDocumentationType(LispBuiltin *builtin, LispObj *type)
+{
+ Atom_id atom;
+ LispDocType_t doc_type = LispDocVariable;
+
+ CHECK_SYMBOL(type);
+ atom = ATOMID(type);
+
+ if (atom == Svariable)
+ doc_type = LispDocVariable;
+ else if (atom == Sfunction)
+ doc_type = LispDocFunction;
+ else if (atom == Sstructure)
+ doc_type = LispDocStructure;
+ else if (atom == Stype)
+ doc_type = LispDocType;
+ else if (atom == Ssetf)
+ doc_type = LispDocSetf;
+ else {
+ LispDestroy("%s: unknown documentation type %s",
+ STRFUN(builtin), STROBJ(type));
+ /*NOTREACHED*/
+ }
+
+ return (doc_type);
+}
+
+LispObj *
+Lisp_Documentation(LispBuiltin *builtin)
+/*
+ documentation symbol type
+ */
+{
+ LispObj *symbol, *type;
+
+ type = ARGUMENT(1);
+ symbol = ARGUMENT(0);
+
+ CHECK_SYMBOL(symbol);
+ /* type is checked in LispDocumentationType() */
+
+ return (LispGetDocumentation(symbol, LispDocumentationType(builtin, type)));
+}
+
+LispObj *
+Lisp_DoList(LispBuiltin *builtin)
+{
+ return (LispDoListTimes(builtin, 0));
+}
+
+LispObj *
+Lisp_DoTimes(LispBuiltin *builtin)
+{
+ return (LispDoListTimes(builtin, 1));
+}
+
+LispObj *
+Lisp_Elt(LispBuiltin *builtin)
+/*
+ elt sequence index
+ svref sequence index
+ */
+{
+ long offset, length;
+ LispObj *result, *sequence, *oindex;
+
+ oindex = ARGUMENT(1);
+ sequence = ARGUMENT(0);
+
+ length = LispLength(sequence);
+
+ CHECK_INDEX(oindex);
+ offset = FIXNUM_VALUE(oindex);
+
+ if (offset >= length)
+ LispDestroy("%s: index %ld too large for sequence length %ld",
+ STRFUN(builtin), offset, length);
+
+ if (STRINGP(sequence))
+ result = SCHAR(THESTR(sequence)[offset]);
+ else {
+ if (ARRAYP(sequence))
+ sequence = sequence->data.array.list;
+
+ for (; offset > 0; offset--, sequence = CDR(sequence))
+ ;
+ result = CAR(sequence);
+ }
+
+ return (result);
+}
+
+LispObj *
+Lisp_Endp(LispBuiltin *builtin)
+/*
+ endp object
+ */
+{
+ LispObj *object;
+
+ object = ARGUMENT(0);
+
+ if (object == NIL)
+ return (T);
+ CHECK_CONS(object);
+
+ return (NIL);
+}
+
+LispObj *
+Lisp_Eq(LispBuiltin *builtin)
+/*
+ eq left right
+ */
+{
+ LispObj *left, *right;
+
+ right = ARGUMENT(1);
+ left = ARGUMENT(0);
+
+ return (XEQ(left, right));
+}
+
+LispObj *
+Lisp_Eql(LispBuiltin *builtin)
+/*
+ eql left right
+ */
+{
+ LispObj *left, *right;
+
+ right = ARGUMENT(1);
+ left = ARGUMENT(0);
+
+ return (XEQL(left, right));
+}
+
+LispObj *
+Lisp_Equal(LispBuiltin *builtin)
+/*
+ equal left right
+ */
+{
+ LispObj *left, *right;
+
+ right = ARGUMENT(1);
+ left = ARGUMENT(0);
+
+ return (XEQUAL(left, right));
+}
+
+LispObj *
+Lisp_Equalp(LispBuiltin *builtin)
+/*
+ equalp left right
+ */
+{
+ LispObj *left, *right;
+
+ right = ARGUMENT(1);
+ left = ARGUMENT(0);
+
+ return (XEQUALP(left, right));
+}
+
+LispObj *
+Lisp_Error(LispBuiltin *builtin)
+/*
+ error control-string &rest arguments
+ */
+{
+ LispObj *string, *arglist;
+
+ LispObj *control_string, *arguments;
+
+ arguments = ARGUMENT(1);
+ control_string = ARGUMENT(0);
+
+ arglist = CONS(NIL, CONS(control_string, arguments));
+ GC_PROTECT(arglist);
+ string = APPLY(Oformat, arglist);
+ LispDestroy("%s", THESTR(string));
+ /*NOTREACHED*/
+
+ /* No need to call GC_ENTER() and GC_LEAVE() macros */
+ return (NIL);
+}
+
+LispObj *
+Lisp_Eval(LispBuiltin *builtin)
+/*
+ eval form
+ */
+{
+ int lex;
+ LispObj *form, *result;
+
+ form = ARGUMENT(0);
+
+ /* make sure eval form will not access local variables */
+ lex = lisp__data.env.lex;
+ lisp__data.env.lex = lisp__data.env.length;
+ result = EVAL(form);
+ lisp__data.env.lex = lex;
+
+ return (result);
+}
+
+static LispObj *
+LispEverySomeAnyNot(LispBuiltin *builtin, int function)
+/*
+ every predicate sequence &rest more-sequences
+ some predicate sequence &rest more-sequences
+ notevery predicate sequence &rest more-sequences
+ notany predicate sequence &rest more-sequences
+ */
+{
+ GC_ENTER();
+ long i, j, length, count;
+ LispObj *result, *list, *item, *arguments, *acons, *value;
+ SeqInfo stk[8], *seqs;
+
+ LispObj *predicate, *sequence, *more_sequences;
+
+ more_sequences = ARGUMENT(2);
+ sequence = ARGUMENT(1);
+ predicate = ARGUMENT(0);
+
+ count = 1;
+ length = LispLength(sequence);
+ for (list = more_sequences; CONSP(list); list = CDR(list), count++) {
+ i = LispLength(CAR(list));
+ if (i < length)
+ length = i;
+ }
+
+ result = function == EVERY || function == NOTANY ? T : NIL;
+
+ /* if at least one sequence has length zero */
+ if (length == 0)
+ return (result);
+
+ if (count > sizeof(stk) / sizeof(stk[0]))
+ seqs = LispMalloc(count * sizeof(SeqInfo));
+ else
+ seqs = &stk[0];
+
+ /* build information about sequences */
+ SETSEQ(seqs[0], sequence);
+ for (i = 1, list = more_sequences; CONSP(list); list = CDR(list), i++) {
+ item = CAR(list);
+ SETSEQ(seqs[i], item);
+ }
+
+ /* prepare argument list */
+ arguments = acons = CONS(NIL, NIL);
+ GC_PROTECT(arguments);
+ for (i = 1; i < count; i++) {
+ RPLACD(acons, CONS(NIL, NIL));
+ acons = CDR(acons);
+ }
+
+ /* loop applying predicate in sequence elements */
+ for (i = 0; i < length; i++) {
+
+ /* build argument list */
+ for (acons = arguments, j = 0; j < count; acons = CDR(acons), j++) {
+ if (seqs[j].type == LispString_t)
+ item = SCHAR(*seqs[j].data.string++);
+ else {
+ item = CAR(seqs[j].data.list);
+ seqs[j].data.list = CDR(seqs[j].data.list);
+ }
+ RPLACA(acons, item);
+ }
+
+ /* apply predicate */
+ value = APPLY(predicate, arguments);
+
+ /* check if needs to terminate loop */
+ if (value == NIL) {
+ if (function == EVERY) {
+ result = NIL;
+ break;
+ }
+ if (function == NOTEVERY) {
+ result = T;
+ break;
+ }
+ }
+ else {
+ if (function == SOME) {
+ result = value;
+ break;
+ }
+ if (function == NOTANY) {
+ result = NIL;
+ break;
+ }
+ }
+ }
+
+ GC_LEAVE();
+ if (seqs != &stk[0])
+ LispFree(seqs);
+
+ return (result);
+}
+
+LispObj *
+Lisp_Every(LispBuiltin *builtin)
+/*
+ every predicate sequence &rest more-sequences
+ */
+{
+ return (LispEverySomeAnyNot(builtin, EVERY));
+}
+
+LispObj *
+Lisp_Some(LispBuiltin *builtin)
+/*
+ some predicate sequence &rest more-sequences
+ */
+{
+ return (LispEverySomeAnyNot(builtin, SOME));
+}
+
+LispObj *
+Lisp_Notevery(LispBuiltin *builtin)
+/*
+ notevery predicate sequence &rest more-sequences
+ */
+{
+ return (LispEverySomeAnyNot(builtin, NOTEVERY));
+}
+
+LispObj *
+Lisp_Notany(LispBuiltin *builtin)
+/*
+ notany predicate sequence &rest more-sequences
+ */
+{
+ return (LispEverySomeAnyNot(builtin, NOTANY));
+}
+
+LispObj *
+Lisp_Fboundp(LispBuiltin *builtin)
+/*
+ fboundp symbol
+ */
+{
+ LispAtom *atom;
+
+ LispObj *symbol = ARGUMENT(0);
+
+ CHECK_SYMBOL(symbol);
+
+ atom = symbol->data.atom;
+ if (atom->a_function || atom->a_builtin || atom->a_compiled)
+ return (T);
+
+ return (NIL);
+}
+
+LispObj *
+Lisp_Find(LispBuiltin *builtin)
+/*
+ find item sequence &key from-end test test-not start end key
+ */
+{
+ return (LispFindOrPosition(builtin, FIND, NONE));
+}
+
+LispObj *
+Lisp_FindIf(LispBuiltin *builtin)
+/*
+ find-if predicate sequence &key from-end start end key
+ */
+{
+ return (LispFindOrPosition(builtin, FIND, IF));
+}
+
+LispObj *
+Lisp_FindIfNot(LispBuiltin *builtin)
+/*
+ find-if-not predicate sequence &key from-end start end key
+ */
+{
+ return (LispFindOrPosition(builtin, FIND, IFNOT));
+}
+
+LispObj *
+Lisp_Fill(LispBuiltin *builtin)
+/*
+ fill sequence item &key start end
+ */
+{
+ long i, start, end, length;
+
+ LispObj *sequence, *item, *ostart, *oend;
+
+ oend = ARGUMENT(3);
+ ostart = ARGUMENT(2);
+ item = ARGUMENT(1);
+ sequence = ARGUMENT(0);
+
+ LispCheckSequenceStartEnd(builtin, sequence, ostart, oend,
+ &start, &end, &length);
+
+ if (STRINGP(sequence)) {
+ int ch;
+ char *string = THESTR(sequence);
+
+ CHECK_STRING_WRITABLE(sequence);
+ CHECK_SCHAR(item);
+ ch = SCHAR_VALUE(item);
+ for (i = start; i < end; i++)
+ string[i] = ch;
+ }
+ else {
+ LispObj *list;
+
+ if (CONSP(sequence))
+ list = sequence;
+ else
+ list = sequence->data.array.list;
+
+ for (i = 0; i < start; i++, list = CDR(list))
+ ;
+ for (; i < end; i++, list = CDR(list))
+ RPLACA(list, item);
+ }
+
+ return (sequence);
+}
+
+LispObj *
+Lisp_Fmakunbound(LispBuiltin *builtin)
+/*
+ fmkaunbound symbol
+ */
+{
+ LispObj *symbol;
+
+ symbol = ARGUMENT(0);
+
+ CHECK_SYMBOL(symbol);
+ if (symbol->data.atom->a_function)
+ LispRemAtomFunctionProperty(symbol->data.atom);
+ else if (symbol->data.atom->a_builtin)
+ LispRemAtomBuiltinProperty(symbol->data.atom);
+ else if (symbol->data.atom->a_compiled)
+ LispRemAtomCompiledProperty(symbol->data.atom);
+
+ return (symbol);
+}
+
+LispObj *
+Lisp_Funcall(LispBuiltin *builtin)
+/*
+ funcall function &rest arguments
+ */
+{
+ LispObj *result;
+
+ LispObj *function, *arguments;
+
+ arguments = ARGUMENT(1);
+ function = ARGUMENT(0);
+
+ result = APPLY(function, arguments);
+
+ return (result);
+}
+
+LispObj *
+Lisp_Functionp(LispBuiltin *builtin)
+/*
+ functionp object
+ */
+{
+ LispObj *object;
+
+ object = ARGUMENT(0);
+
+ return (FUNCTIONP(object) || LAMBDAP(object) ? T : NIL);
+}
+
+LispObj *
+Lisp_Get(LispBuiltin *builtin)
+/*
+ get symbol indicator &optional default
+ */
+{
+ LispObj *result;
+
+ LispObj *symbol, *indicator, *defalt;
+
+ defalt = ARGUMENT(2);
+ indicator = ARGUMENT(1);
+ symbol = ARGUMENT(0);
+
+ CHECK_SYMBOL(symbol);
+
+ result = LispGetAtomProperty(symbol->data.atom, indicator);
+
+ if (result != NIL)
+ result = CAR(result);
+ else
+ result = defalt == UNSPEC ? NIL : defalt;
+
+ return (result);
+}
+
+/*
+ * ext::getenv
+ */
+LispObj *
+Lisp_Getenv(LispBuiltin *builtin)
+/*
+ getenv name
+ */
+{
+ char *value;
+
+ LispObj *name;
+
+ name = ARGUMENT(0);
+
+ CHECK_STRING(name);
+ value = getenv(THESTR(name));
+
+ return (value ? STRING(value) : NIL);
+}
+
+LispObj *
+Lisp_Gc(LispBuiltin *builtin)
+/*
+ gc &optional car cdr
+ */
+{
+ LispObj *car, *cdr;
+
+ cdr = ARGUMENT(1);
+ car = ARGUMENT(0);
+
+ LispGC(car, cdr);
+
+ return (NIL);
+}
+
+LispObj *
+Lisp_Gensym(LispBuiltin *builtin)
+/*
+ gensym &optional arg
+ */
+{
+ char *preffix = "G", name[132];
+ long counter = LONGINT_VALUE(Ogensym_counter->data.atom->property->value);
+ LispObj *symbol;
+
+ LispObj *arg;
+
+ arg = ARGUMENT(0);
+ if (arg != UNSPEC) {
+ if (STRINGP(arg))
+ preffix = THESTR(arg);
+ else {
+ CHECK_INDEX(arg);
+ counter = FIXNUM_VALUE(arg);
+ }
+ }
+ snprintf(name, sizeof(name), "%s%ld", preffix, counter);
+ if (strlen(name) >= 128)
+ LispDestroy("%s: name %s too long", STRFUN(builtin), name);
+ Ogensym_counter->data.atom->property->value = INTEGER(counter + 1);
+
+ symbol = UNINTERNED_ATOM(name);
+ symbol->data.atom->unreadable = !LispCheckAtomString(name);
+
+ return (symbol);
+}
+
+LispObj *
+Lisp_Go(LispBuiltin *builtin)
+/*
+ go tag
+ */
+{
+ unsigned blevel = lisp__data.block.block_level;
+
+ LispObj *tag;
+
+ tag = ARGUMENT(0);
+
+ while (blevel) {
+ LispBlock *block = lisp__data.block.block[--blevel];
+
+ if (block->type == LispBlockClosure)
+ /* if reached a function call */
+ break;
+ if (block->type == LispBlockBody) {
+ lisp__data.block.block_ret = tag;
+ LispBlockUnwind(block);
+ BLOCKJUMP(block);
+ }
+ }
+
+ LispDestroy("%s: no visible tagbody for %s",
+ STRFUN(builtin), STROBJ(tag));
+ /*NOTREACHED*/
+ return (NIL);
+}
+
+LispObj *
+Lisp_If(LispBuiltin *builtin)
+/*
+ if test then &optional else
+ */
+{
+ LispObj *result, *test, *then, *oelse;
+
+ oelse = ARGUMENT(2);
+ then = ARGUMENT(1);
+ test = ARGUMENT(0);
+
+ test = EVAL(test);
+ if (test != NIL)
+ result = EVAL(then);
+ else if (oelse != UNSPEC)
+ result = EVAL(oelse);
+ else
+ result = NIL;
+
+ return (result);
+}
+
+LispObj *
+Lisp_IgnoreErrors(LispBuiltin *builtin)
+/*
+ ignore-erros &rest body
+ */
+{
+ LispObj *result, **presult, **pbody;
+ int i, jumped, *pjumped;
+ LispBlock *block;
+
+ /* interpreter state */
+ GC_ENTER();
+ int stack, lex, length;
+
+ /* memory allocation */
+ int mem_level;
+ void **mem;
+
+ LispObj *body;
+
+ body = ARGUMENT(0);
+
+ /* Save environment information */
+ stack = lisp__data.stack.length;
+ lex = lisp__data.env.lex;
+ length = lisp__data.env.length;
+
+ /* Save memory allocation information */
+ mem_level = lisp__data.mem.level;
+ mem = LispMalloc(mem_level * sizeof(void*));
+ memcpy(mem, lisp__data.mem.mem, mem_level * sizeof(void*));
+
+ ++lisp__data.ignore_errors;
+ presult = &result;
+ pjumped = &jumped;
+ pbody = &body;
+ result = NIL;
+ jumped = 1;
+ block = LispBeginBlock(NIL, LispBlockProtect);
+ if (setjmp(block->jmp) == 0) {
+ for (; CONSP(body); body = CDR(body))
+ result = EVAL(CAR(body));
+ jumped = 0;
+ }
+ LispEndBlock(block);
+ if (!lisp__data.destroyed && jumped)
+ result = lisp__data.block.block_ret;
+
+ if (lisp__data.destroyed) {
+ /* Restore environment */
+ lisp__data.stack.length = stack;
+ lisp__data.env.lex = lex;
+ lisp__data.env.head = lisp__data.env.length = length;
+ GC_LEAVE();
+
+ /* Check for possible leaks due to ignoring errors */
+ for (i = 0; i < mem_level; i++) {
+ if (lisp__data.mem.mem[i] && mem[i] != lisp__data.mem.mem[i])
+ LispFree(lisp__data.mem.mem[i]);
+ }
+ for (; i < lisp__data.mem.level; i++) {
+ if (lisp__data.mem.mem[i])
+ LispFree(lisp__data.mem.mem[i]);
+ }
+
+ lisp__data.destroyed = 0;
+ result = NIL;
+ RETURN_COUNT = 1;
+ RETURN(0) = lisp__data.error_condition;
+ }
+ LispFree(mem);
+ --lisp__data.ignore_errors;
+
+ return (result);
+}
+
+LispObj *
+Lisp_Intersection(LispBuiltin *builtin)
+/*
+ intersection list1 list2 &key test test-not key
+ */
+{
+ return (LispListSet(builtin, INTERSECTION));
+}
+
+LispObj *
+Lisp_Nintersection(LispBuiltin *builtin)
+/*
+ nintersection list1 list2 &key test test-not key
+ */
+{
+ return (LispListSet(builtin, NINTERSECTION));
+}
+
+LispObj *
+Lisp_Keywordp(LispBuiltin *builtin)
+/*
+ keywordp object
+ */
+{
+ LispObj *object;
+
+ object = ARGUMENT(0);
+
+ return (KEYWORDP(object) ? T : NIL);
+}
+
+LispObj *
+Lisp_Lambda(LispBuiltin *builtin)
+/*
+ lambda lambda-list &rest body
+ */
+{
+ GC_ENTER();
+ LispObj *name;
+ LispArgList *alist;
+
+ LispObj *lambda, *lambda_list, *body;
+
+ body = ARGUMENT(1);
+ lambda_list = ARGUMENT(0);
+
+ alist = LispCheckArguments(LispLambda, lambda_list, Snil, 0);
+
+ name = OPAQUE(alist, LispArgList_t);
+ lambda_list = LispListProtectedArguments(alist);
+ GC_PROTECT(name);
+ GC_PROTECT(lambda_list);
+ lambda = LispNewLambda(name, body, lambda_list, LispLambda);
+ LispUseArgList(alist);
+ GC_LEAVE();
+
+ return (lambda);
+}
+
+LispObj *
+Lisp_Last(LispBuiltin *builtin)
+/*
+ last list &optional count
+ */
+{
+ long count, length;
+ LispObj *list, *ocount;
+
+ ocount = ARGUMENT(1);
+ list = ARGUMENT(0);
+
+ if (!CONSP(list))
+ return (list);
+
+ length = LispLength(list);
+
+ if (ocount == UNSPEC)
+ count = 1;
+ else {
+ CHECK_INDEX(ocount);
+ count = FIXNUM_VALUE(ocount);
+ }
+
+ if (count >= length)
+ return (list);
+
+ length -= count;
+ for (; length > 0; length--)
+ list = CDR(list);
+
+ return (list);
+}
+
+LispObj *
+Lisp_Length(LispBuiltin *builtin)
+/*
+ length sequence
+ */
+{
+ LispObj *sequence;
+
+ sequence = ARGUMENT(0);
+
+ return (FIXNUM(LispLength(sequence)));
+}
+
+LispObj *
+Lisp_Let(LispBuiltin *builtin)
+/*
+ let init &rest body
+ */
+{
+ GC_ENTER();
+ int head = lisp__data.env.length;
+ LispObj *init, *body, *pair, *result, *list, *cons = NIL;
+
+ body = ARGUMENT(1);
+ init = ARGUMENT(0);
+
+ CHECK_LIST(init);
+ for (list = NIL; CONSP(init); init = CDR(init)) {
+ LispObj *symbol, *value;
+
+ pair = CAR(init);
+ if (SYMBOLP(pair)) {
+ symbol = pair;
+ value = NIL;
+ }
+ else {
+ CHECK_CONS(pair);
+ symbol = CAR(pair);
+ CHECK_SYMBOL(symbol);
+ pair = CDR(pair);
+ if (CONSP(pair)) {
+ value = CAR(pair);
+ if (CDR(pair) != NIL)
+ LispDestroy("%s: too much arguments to initialize %s",
+ STRFUN(builtin), STROBJ(symbol));
+ value = EVAL(value);
+ }
+ else
+ value = NIL;
+ }
+ pair = CONS(symbol, value);
+ if (list == NIL) {
+ list = cons = CONS(pair, NIL);
+ GC_PROTECT(list);
+ }
+ else {
+ RPLACD(cons, CONS(pair, NIL));
+ cons = CDR(cons);
+ }
+ }
+ /* Add variables */
+ for (; CONSP(list); list = CDR(list)) {
+ pair = CAR(list);
+ CHECK_CONSTANT(CAR(pair));
+ LispAddVar(CAR(pair), CDR(pair));
+ ++lisp__data.env.head;
+ }
+ /* Values of symbols are now protected */
+ GC_LEAVE();
+
+ /* execute body */
+ for (result = NIL; CONSP(body); body = CDR(body))
+ result = EVAL(CAR(body));
+
+ lisp__data.env.head = lisp__data.env.length = head;
+
+ return (result);
+}
+
+LispObj *
+Lisp_LetP(LispBuiltin *builtin)
+/*
+ let* init &rest body
+ */
+{
+ int head = lisp__data.env.length;
+ LispObj *init, *body, *pair, *result;
+
+ body = ARGUMENT(1);
+ init = ARGUMENT(0);
+
+ CHECK_LIST(init);
+ for (; CONSP(init); init = CDR(init)) {
+ LispObj *symbol, *value;
+
+ pair = CAR(init);
+ if (SYMBOLP(pair)) {
+ symbol = pair;
+ value = NIL;
+ }
+ else {
+ CHECK_CONS(pair);
+ symbol = CAR(pair);
+ CHECK_SYMBOL(symbol);
+ pair = CDR(pair);
+ if (CONSP(pair)) {
+ value = CAR(pair);
+ if (CDR(pair) != NIL)
+ LispDestroy("%s: too much arguments to initialize %s",
+ STRFUN(builtin), STROBJ(symbol));
+ value = EVAL(value);
+ }
+ else
+ value = NIL;
+ }
+
+ CHECK_CONSTANT(symbol);
+ LispAddVar(symbol, value);
+ ++lisp__data.env.head;
+ }
+
+ /* execute body */
+ for (result = NIL; CONSP(body); body = CDR(body))
+ result = EVAL(CAR(body));
+
+ lisp__data.env.head = lisp__data.env.length = head;
+
+ return (result);
+}
+
+LispObj *
+Lisp_List(LispBuiltin *builtin)
+/*
+ list &rest args
+ */
+{
+ LispObj *args;
+
+ args = ARGUMENT(0);
+
+ return (args);
+}
+
+LispObj *
+Lisp_ListP(LispBuiltin *builtin)
+/*
+ list* object &rest more-objects
+ */
+{
+ GC_ENTER();
+ LispObj *result, *cons;
+
+ LispObj *object, *more_objects;
+
+ more_objects = ARGUMENT(1);
+ object = ARGUMENT(0);
+
+ if (!CONSP(more_objects))
+ return (object);
+
+ result = cons = CONS(object, CAR(more_objects));
+ GC_PROTECT(result);
+ for (more_objects = CDR(more_objects); CONSP(more_objects);
+ more_objects = CDR(more_objects)) {
+ object = CAR(more_objects);
+ RPLACD(cons, CONS(CDR(cons), object));
+ cons = CDR(cons);
+ }
+ GC_LEAVE();
+
+ return (result);
+}
+
+/* "classic" list-length */
+LispObj *
+Lisp_ListLength(LispBuiltin *builtin)
+/*
+ list-length list
+ */
+{
+ long length;
+ LispObj *fast, *slow;
+
+ LispObj *list;
+
+ list = ARGUMENT(0);
+
+ CHECK_LIST(list);
+ for (fast = slow = list, length = 0;
+ CONSP(slow);
+ slow = CDR(slow), length += 2) {
+ if (fast == NIL)
+ break;
+ CHECK_CONS(fast);
+ fast = CDR(fast);
+ if (fast == NIL) {
+ ++length;
+ break;
+ }
+ CHECK_CONS(fast);
+ fast = CDR(fast);
+ if (slow == fast)
+ /* circular list */
+ return (NIL);
+ }
+
+ return (FIXNUM(length));
+}
+
+LispObj *
+Lisp_Listp(LispBuiltin *builtin)
+/*
+ listp object
+ */
+{
+ LispObj *object;
+
+ object = ARGUMENT(0);
+
+ return (object == NIL || CONSP(object) ? T : NIL);
+}
+
+static LispObj *
+LispListSet(LispBuiltin *builtin, int function)
+/*
+ intersection list1 list2 &key test test-not key
+ nintersection list1 list2 &key test test-not key
+ set-difference list1 list2 &key test test-not key
+ nset-difference list1 list2 &key test test-not key
+ set-exclusive-or list1 list2 &key test test-not key
+ nset-exclusive-or list1 list2 &key test test-not key
+ subsetp list1 list2 &key test test-not key
+ union list1 list2 &key test test-not key
+ nunion list1 list2 &key test test-not key
+ */
+{
+ GC_ENTER();
+ int code, expect, value, inplace, check_list2,
+ intersection, setdifference, xunion, setexclusiveor;
+ LispObj *lambda, *result, *cmp, *cmp1, *cmp2,
+ *item, *clist1, *clist2, *cons, *cdr;
+
+ LispObj *list1, *list2, *test, *test_not, *key;
+
+ key = ARGUMENT(4);
+ test_not = ARGUMENT(3);
+ test = ARGUMENT(2);
+ list2 = ARGUMENT(1);
+ list1 = ARGUMENT(0);
+
+ /* Check if arguments are valid lists */
+ CHECK_LIST(list1);
+ CHECK_LIST(list2);
+
+ setdifference = intersection = xunion = setexclusiveor = inplace = 0;
+ switch (function) {
+ case NSETDIFFERENCE:
+ inplace = 1;
+ case SETDIFFERENCE:
+ setdifference = 1;
+ break;
+ case NINTERSECTION:
+ inplace = 1;
+ case INTERSECTION:
+ intersection = 1;
+ break;
+ case NUNION:
+ inplace = 1;
+ case UNION:
+ xunion = 1;
+ break;
+ case NSETEXCLUSIVEOR:
+ inplace = 1;
+ case SETEXCLUSIVEOR:
+ setexclusiveor = 1;
+ break;
+ }
+
+ /* Check for fast return */
+ if (list1 == NIL)
+ return (setdifference || intersection ?
+ NIL : function == SUBSETP ? T : list2);
+ if (list2 == NIL)
+ return (intersection || xunion || function == SUBSETP ? NIL : list1);
+
+ CHECK_TEST();
+ clist1 = cdr = NIL;
+
+ /* Make a copy of list2 with the key predicate applied */
+ if (key != UNSPEC) {
+ result = cons = CONS(APPLY1(key, CAR(list2)), NIL);
+ GC_PROTECT(result);
+ for (cmp2 = CDR(list2); CONSP(cmp2); cmp2 = CDR(cmp2)) {
+ item = APPLY1(key, CAR(cmp2));
+ RPLACD(cons, CONS(APPLY1(key, CAR(cmp2)), NIL));
+ cons = CDR(cons);
+ }
+ /* check if list2 is a proper list */
+ CHECK_LIST(cmp2);
+ clist2 = result;
+ check_list2 = 0;
+ }
+ else {
+ clist2 = list2;
+ check_list2 = 1;
+ }
+ result = cons = NIL;
+
+ /* Compare elements of lists
+ * Logic:
+ * UNION
+ * 1) Walk list1 and if CAR(list1) not in list2, add it to result
+ * 2) Add list2 to result
+ * INTERSECTION
+ * 1) Walk list1 and if CAR(list1) in list2, add it to result
+ * SET-DIFFERENCE
+ * 1) Walk list1 and if CAR(list1) not in list2, add it to result
+ * SET-EXCLUSIVE-OR
+ * 1) Walk list1 and if CAR(list1) not in list2, add it to result
+ * 2) Walk list2 and if CAR(list2) not in list1, add it to result
+ * SUBSETP
+ * 1) Walk list1 and if CAR(list1) not in list2, return NIL
+ * 2) Return T
+ */
+ value = 0;
+ for (cmp1 = list1; CONSP(cmp1); cmp1 = CDR(cmp1)) {
+ item = CAR(cmp1);
+
+ /* Apply key predicate if required */
+ if (key != UNSPEC) {
+ cmp = APPLY1(key, item);
+ if (setexclusiveor) {
+ if (clist1 == NIL) {
+ clist1 = cdr = CONS(cmp, NIL);
+ GC_PROTECT(clist1);
+ }
+ else {
+ RPLACD(cdr, CONS(cmp, NIL));
+ cdr = CDR(cdr);
+ }
+ }
+ }
+ else
+ cmp = item;
+
+ /* Compare against list2 */
+ for (cmp2 = clist2; CONSP(cmp2); cmp2 = CDR(cmp2)) {
+ value = FCOMPARE(lambda, cmp, CAR(cmp2), code);
+ if (value == expect)
+ break;
+ }
+ if (check_list2 && value != expect) {
+ /* check if list2 is a proper list */
+ CHECK_LIST(cmp2);
+ check_list2 = 0;
+ }
+
+ if (function == SUBSETP) {
+ /* Element of list1 not in list2? */
+ if (value != expect) {
+ GC_LEAVE();
+
+ return (NIL);
+ }
+ }
+ /* If need to add item to result */
+ else if (((setdifference || xunion || setexclusiveor) &&
+ value != expect) ||
+ (intersection && value == expect)) {
+ if (inplace) {
+ if (result == NIL)
+ result = cons = cmp1;
+ else {
+ if (setexclusiveor) {
+ /* don't remove elements yet, will need
+ * to check agains't list2 later */
+ for (cmp2 = cons; CDR(cmp2) != cmp1; cmp2 = CDR(cmp2))
+ ;
+ if (cmp2 != cons) {
+ RPLACD(cmp2, list1);
+ list1 = cmp2;
+ }
+ }
+ RPLACD(cons, cmp1);
+ cons = cmp1;
+ }
+ }
+ else {
+ if (result == NIL) {
+ result = cons = CONS(item, NIL);
+ GC_PROTECT(result);
+ }
+ else {
+ RPLACD(cons, CONS(item, NIL));
+ cons = CDR(cons);
+ }
+ }
+ }
+ }
+ /* check if list1 is a proper list */
+ CHECK_LIST(cmp1);
+
+ if (function == SUBSETP) {
+ GC_LEAVE();
+
+ return (T);
+ }
+ else if (xunion) {
+ /* Add list2 to tail of result */
+ if (result == NIL)
+ result = list2;
+ else
+ RPLACD(cons, list2);
+ }
+ else if (setexclusiveor) {
+ LispObj *result2, *cons2;
+
+ result2 = cons2 = NIL;
+ for (cmp2 = list2; CONSP(cmp2); cmp2 = CDR(cmp2)) {
+ item = CAR(cmp2);
+
+ if (key != UNSPEC) {
+ cmp = CAR(clist2);
+ /* XXX changing clist2 */
+ clist2 = CDR(clist2);
+ cmp1 = clist1;
+ }
+ else {
+ cmp = item;
+ cmp1 = list1;
+ }
+
+ /* Compare against list1 */
+ for (; CONSP(cmp1); cmp1 = CDR(cmp1)) {
+ value = FCOMPARE(lambda, cmp, CAR(cmp1), code);
+ if (value == expect)
+ break;
+ }
+
+ if (value != expect) {
+ if (inplace) {
+ if (result2 == NIL)
+ result2 = cons2 = cmp2;
+ else {
+ RPLACD(cons2, cmp2);
+ cons2 = cmp2;
+ }
+ }
+ else {
+ if (result == NIL) {
+ result = cons = CONS(item, NIL);
+ GC_PROTECT(result);
+ }
+ else {
+ RPLACD(cons, CONS(item, NIL));
+ cons = CDR(cons);
+ }
+ }
+ }
+ }
+ if (inplace) {
+ if (CONSP(cons2))
+ RPLACD(cons2, NIL);
+ if (result == NIL)
+ result = result2;
+ else
+ RPLACD(cons, result2);
+ }
+ }
+ else if ((function == NSETDIFFERENCE || function == NINTERSECTION) &&
+ CONSP(cons))
+ RPLACD(cons, NIL);
+
+ GC_LEAVE();
+
+ return (result);
+}
+
+LispObj *
+Lisp_Loop(LispBuiltin *builtin)
+/*
+ loop &rest body
+ */
+{
+ LispObj *code, *result;
+ LispBlock *block;
+
+ LispObj *body;
+
+ body = ARGUMENT(0);
+
+ result = NIL;
+ block = LispBeginBlock(NIL, LispBlockTag);
+ if (setjmp(block->jmp) == 0) {
+ for (;;)
+ for (code = body; CONSP(code); code = CDR(code))
+ (void)EVAL(CAR(code));
+ }
+ LispEndBlock(block);
+ result = lisp__data.block.block_ret;
+
+ return (result);
+}
+
+/* XXX This function is broken, needs a review
+ (being delayed until true array/vectors be implemented) */
+LispObj *
+Lisp_MakeArray(LispBuiltin *builtin)
+/*
+ make-array dimensions &key element-type initial-element initial-contents
+ adjustable fill-pointer displaced-to
+ displaced-index-offset
+ */
+{
+ long rank = 0, count = 1, offset, zero, c;
+ LispObj *obj, *dim, *array;
+ LispType type;
+
+ LispObj *dimensions, *element_type, *initial_element, *initial_contents,
+ *adjustable, *fill_pointer, *displaced_to,
+ *displaced_index_offset;
+
+ dim = array = NIL;
+ type = LispNil_t;
+
+ displaced_index_offset = ARGUMENT(7);
+ displaced_to = ARGUMENT(6);
+ fill_pointer = ARGUMENT(5);
+ adjustable = ARGUMENT(4);
+ initial_contents = ARGUMENT(3);
+ initial_element = ARGUMENT(2);
+ element_type = ARGUMENT(1);
+ dimensions = ARGUMENT(0);
+
+ if (INDEXP(dimensions)) {
+ dim = CONS(dimensions, NIL);
+ rank = 1;
+ count = FIXNUM_VALUE(dimensions);
+ }
+ else if (CONSP(dimensions)) {
+ dim = dimensions;
+
+ for (rank = 0; CONSP(dim); rank++, dim = CDR(dim)) {
+ obj = CAR(dim);
+ CHECK_INDEX(obj);
+ count *= FIXNUM_VALUE(obj);
+ }
+ dim = dimensions;
+ }
+ else if (dimensions == NIL) {
+ dim = NIL;
+ rank = count = 0;
+ }
+ else
+ LispDestroy("%s: %s is a bad array dimension",
+ STRFUN(builtin), STROBJ(dimensions));
+
+ /* check element-type */
+ if (element_type != UNSPEC) {
+ if (element_type == T)
+ type = LispNil_t;
+ else if (!SYMBOLP(element_type))
+ LispDestroy("%s: unsupported element type %s",
+ STRFUN(builtin), STROBJ(element_type));
+ else {
+ Atom_id atom = ATOMID(element_type);
+
+ if (atom == Satom)
+ type = LispAtom_t;
+ else if (atom == Sinteger)
+ type = LispInteger_t;
+ else if (atom == Scharacter)
+ type = LispSChar_t;
+ else if (atom == Sstring)
+ type = LispString_t;
+ else if (atom == Slist)
+ type = LispCons_t;
+ else if (atom == Sopaque)
+ type = LispOpaque_t;
+ else
+ LispDestroy("%s: unsupported element type %s",
+ STRFUN(builtin), ATOMID(element_type));
+ }
+ }
+
+ /* check initial-contents */
+ if (rank) {
+ CHECK_LIST(initial_contents);
+ }
+
+ /* check displaced-to */
+ if (displaced_to != UNSPEC) {
+ CHECK_ARRAY(displaced_to);
+ }
+
+ /* check displaced-index-offset */
+ offset = -1;
+ if (displaced_index_offset != UNSPEC) {
+ CHECK_INDEX(displaced_index_offset);
+ offset = FIXNUM_VALUE(displaced_index_offset);
+ }
+
+ c = 0;
+ if (initial_element != UNSPEC)
+ ++c;
+ if (initial_contents != UNSPEC)
+ ++c;
+ if (displaced_to != UNSPEC || offset >= 0)
+ ++c;
+ if (c > 1)
+ LispDestroy("%s: more than one initialization specified",
+ STRFUN(builtin));
+ if (initial_element == UNSPEC)
+ initial_element = NIL;
+
+ zero = count == 0;
+ if (displaced_to != UNSPEC) {
+ CHECK_ARRAY(displaced_to);
+ if (offset < 0)
+ offset = 0;
+ for (c = 1, obj = displaced_to->data.array.dim; obj != NIL;
+ obj = CDR(obj))
+ c *= FIXNUM_VALUE(CAR(obj));
+ if (c < count + offset)
+ LispDestroy("%s: array-total-size + displaced-index-offset "
+ "exceeds total size", STRFUN(builtin));
+ for (c = 0, array = displaced_to->data.array.list; c < offset; c++)
+ array = CDR(array);
+ }
+ else if (initial_contents != UNSPEC) {
+ CHECK_CONS(initial_contents);
+ if (rank == 0)
+ array = initial_contents;
+ else if (rank == 1) {
+ for (array = initial_contents, c = 0; c < count;
+ array = CDR(array), c++)
+ if (!CONSP(array))
+ LispDestroy("%s: bad argument or size %s",
+ STRFUN(builtin), STROBJ(array));
+ if (array != NIL)
+ LispDestroy("%s: bad argument or size %s",
+ STRFUN(builtin), STROBJ(array));
+ array = initial_contents;
+ }
+ else {
+ LispObj *err = NIL;
+ /* check if list matches */
+ int i, j, k, *dims, *loop;
+
+ /* create iteration variables */
+ dims = LispMalloc(sizeof(int) * rank);
+ loop = LispCalloc(1, sizeof(int) * (rank - 1));
+ for (i = 0, obj = dim; CONSP(obj); i++, obj = CDR(obj))
+ dims[i] = FIXNUM_VALUE(CAR(obj));
+
+ /* check if list matches specified dimensions */
+ while (loop[0] < dims[0]) {
+ for (obj = initial_contents, i = 0; i < rank - 1; i++) {
+ for (j = 0; j < loop[i]; j++)
+ obj = CDR(obj);
+ err = obj;
+ if (!CONSP(obj = CAR(obj)))
+ goto make_array_error;
+ err = obj;
+ }
+ --i;
+ for (;;) {
+ ++loop[i];
+ if (i && loop[i] >= dims[i])
+ loop[i] = 0;
+ else
+ break;
+ --i;
+ }
+ for (k = 0; k < dims[rank - 1]; obj = CDR(obj), k++) {
+ if (!CONSP(obj))
+ goto make_array_error;
+ }
+ if (obj == NIL)
+ continue;
+make_array_error:
+ LispFree(dims);
+ LispFree(loop);
+ LispDestroy("%s: bad argument or size %s",
+ STRFUN(builtin), STROBJ(err));
+ }
+
+ /* list is correct, use it to fill initial values */
+
+ /* reset loop */
+ memset(loop, 0, sizeof(int) * (rank - 1));
+
+ GCDisable();
+ /* fill array with supplied values */
+ array = NIL;
+ while (loop[0] < dims[0]) {
+ for (obj = initial_contents, i = 0; i < rank - 1; i++) {
+ for (j = 0; j < loop[i]; j++)
+ obj = CDR(obj);
+ obj = CAR(obj);
+ }
+ --i;
+ for (;;) {
+ ++loop[i];
+ if (i && loop[i] >= dims[i])
+ loop[i] = 0;
+ else
+ break;
+ --i;
+ }
+ for (k = 0; k < dims[rank - 1]; obj = CDR(obj), k++) {
+ if (array == NIL)
+ array = CONS(CAR(obj), NIL);
+ else {
+ RPLACD(array, CONS(CAR(array), CDR(array)));
+ RPLACA(array, CAR(obj));
+ }
+ }
+ }
+ LispFree(dims);
+ LispFree(loop);
+ array = LispReverse(array);
+ GCEnable();
+ }
+ }
+ else {
+ GCDisable();
+ /* allocate array */
+ if (count) {
+ --count;
+ array = CONS(initial_element, NIL);
+ while (count) {
+ RPLACD(array, CONS(CAR(array), CDR(array)));
+ RPLACA(array, initial_element);
+ count--;
+ }
+ }
+ GCEnable();
+ }
+
+ obj = LispNew(array, dim);
+ obj->type = LispArray_t;
+ obj->data.array.list = array;
+ obj->data.array.dim = dim;
+ obj->data.array.rank = rank;
+ obj->data.array.type = type;
+ obj->data.array.zero = zero;
+
+ return (obj);
+}
+
+LispObj *
+Lisp_MakeList(LispBuiltin *builtin)
+/*
+ make-list size &key initial-element
+ */
+{
+ GC_ENTER();
+ long count;
+ LispObj *result, *cons;
+
+ LispObj *size, *initial_element;
+
+ initial_element = ARGUMENT(1);
+ size = ARGUMENT(0);
+
+ CHECK_INDEX(size);
+ count = FIXNUM_VALUE(size);
+
+ if (count == 0)
+ return (NIL);
+ if (initial_element == UNSPEC)
+ initial_element = NIL;
+
+ result = cons = CONS(initial_element, NIL);
+ GC_PROTECT(result);
+ for (; count > 1; count--) {
+ RPLACD(cons, CONS(initial_element, NIL));
+ cons = CDR(cons);
+ }
+ GC_LEAVE();
+
+ return (result);
+}
+
+LispObj *
+Lisp_MakeSymbol(LispBuiltin *builtin)
+/*
+ make-symbol name
+ */
+{
+ LispObj *name, *symbol;
+
+ name = ARGUMENT(0);
+ CHECK_STRING(name);
+
+ symbol = UNINTERNED_ATOM(THESTR(name));
+ symbol->data.atom->unreadable = !LispCheckAtomString(THESTR(name));
+
+ return (symbol);
+}
+
+LispObj *
+Lisp_Makunbound(LispBuiltin *builtin)
+/*
+ makunbound symbol
+ */
+{
+ LispObj *symbol;
+
+ symbol = ARGUMENT(0);
+
+ CHECK_SYMBOL(symbol);
+ LispUnsetVar(symbol);
+
+ return (symbol);
+}
+
+LispObj *
+Lisp_Mapc(LispBuiltin *builtin)
+/*
+ mapc function list &rest more-lists
+ */
+{
+ return (LispMapc(builtin, 0));
+}
+
+LispObj *
+Lisp_Mapcar(LispBuiltin *builtin)
+/*
+ mapcar function list &rest more-lists
+ */
+{
+ return (LispMapc(builtin, 1));
+}
+
+/* Like nconc but ignore non list arguments */
+LispObj *
+LispMapnconc(LispObj *list)
+{
+ LispObj *result = NIL;
+
+ if (CONSP(list)) {
+ LispObj *cons, *head, *tail;
+
+ cons = NIL;
+ for (; CONSP(CDR(list)); list = CDR(list)) {
+ head = CAR(list);
+ if (CONSP(head)) {
+ for (tail = head; CONSP(CDR(tail)); tail = CDR(tail))
+ ;
+ if (cons != NIL)
+ RPLACD(cons, head);
+ else
+ result = head;
+ cons = tail;
+ }
+ }
+ head = CAR(list);
+ if (CONSP(head)) {
+ if (cons != NIL)
+ RPLACD(cons, head);
+ else
+ result = head;
+ }
+ }
+
+ return (result);
+}
+
+LispObj *
+Lisp_Mapcan(LispBuiltin *builtin)
+/*
+ mapcan function list &rest more-lists
+ */
+{
+ return (LispMapnconc(LispMapc(builtin, 1)));
+}
+
+static LispObj *
+LispMapc(LispBuiltin *builtin, int mapcar)
+{
+ GC_ENTER();
+ long i, offset, count, length;
+ LispObj *result = NIL, *cons, *arguments, *acons, *rest, *alist, *value;
+ LispObj *stk[8], **cdrs;
+
+ LispObj *function, *list, *more_lists;
+
+ more_lists = ARGUMENT(2);
+ list = ARGUMENT(1);
+ function = ARGUMENT(0);
+
+ /* Result will be no longer than this */
+ for (length = 0, alist = list; CONSP(alist); length++, alist = CDR(alist))
+ ;
+
+ /* If first argument is not a list... */
+ if (length == 0)
+ return (NIL);
+
+ /* At least one argument will be passed to function, count how many
+ * extra arguments will be used, and calculate result length. */
+ count = 0;
+ for (rest = more_lists; CONSP(rest); rest = CDR(rest), count++) {
+
+ /* Check if extra list is really a list, and if it is smaller
+ * than the first list */
+ for (i = 0, alist = CAR(rest);
+ i < length && CONSP(alist);
+ i++, alist = CDR(alist))
+ ;
+
+ /* If it is not a true list */
+ if (i == 0)
+ return (NIL);
+
+ /* If it is smaller than the currently calculated result length */
+ if (i < length)
+ length = i;
+ }
+
+ if (mapcar) {
+ /* Initialize gc protected object cells for resulting list */
+ result = cons = CONS(NIL, NIL);
+ GC_PROTECT(result);
+ }
+ else
+ result = cons = list;
+
+ if (count >= sizeof(stk) / sizeof(stk[0]))
+ cdrs = LispMalloc(count * sizeof(LispObj*));
+ else
+ cdrs = &stk[0];
+ for (i = 0, rest = more_lists; i < count; i++, rest = CDR(rest))
+ cdrs[i] = CAR(rest);
+
+ /* Initialize gc protected object cells for argument list */
+ arguments = acons = CONS(NIL, NIL);
+ GC_PROTECT(arguments);
+
+ /* Allocate space for extra arguments */
+ for (i = 0; i < count; i++) {
+ RPLACD(acons, CONS(NIL, NIL));
+ acons = CDR(acons);
+ }
+
+ /* For every element of the list that will be used */
+ for (offset = 0;; list = CDR(list)) {
+ acons = arguments;
+
+ /* Add first argument */
+ RPLACA(acons, CAR(list));
+ acons = CDR(acons);
+
+ /* For every extra list argument */
+ for (i = 0; i < count; i++) {
+ alist = cdrs[i];
+ cdrs[i] = CDR(cdrs[i]);
+
+ /* Add element to argument list */
+ RPLACA(acons, CAR(alist));
+ acons = CDR(acons);
+ }
+
+ value = APPLY(function, arguments);
+
+ if (mapcar) {
+ /* Store result */
+ RPLACA(cons, value);
+
+ /* Allocate new result cell */
+ if (++offset < length) {
+ RPLACD(cons, CONS(NIL, NIL));
+ cons = CDR(cons);
+ }
+ else
+ break;
+ }
+ else if (++offset >= length)
+ break;
+ }
+
+ /* Unprotect argument and result list */
+ GC_LEAVE();
+ if (cdrs != &stk[0])
+ LispFree(cdrs);
+
+ return (result);
+}
+
+static LispObj *
+LispMapl(LispBuiltin *builtin, int maplist)
+{
+ GC_ENTER();
+ long i, offset, count, length;
+ LispObj *result = NIL, *cons, *arguments, *acons, *rest, *alist, *value;
+ LispObj *stk[8], **cdrs;
+
+ LispObj *function, *list, *more_lists;
+
+ more_lists = ARGUMENT(2);
+ list = ARGUMENT(1);
+ function = ARGUMENT(0);
+
+ /* count is the number of lists, length is the length of the result */
+ for (length = 0, alist = list; CONSP(alist); length++, alist = CDR(alist))
+ ;
+
+ /* first argument is not a list */
+ if (length == 0)
+ return (NIL);
+
+ /* check remaining arguments */
+ for (count = 0, rest = more_lists; CONSP(rest); rest = CDR(rest), count++) {
+ for (i = 0, alist = CAR(rest);
+ i < length && CONSP(alist);
+ i++, alist = CDR(alist))
+ ;
+ /* argument is not a list */
+ if (i == 0)
+ return (NIL);
+ /* result will have the length of the smallest list */
+ if (i < length)
+ length = i;
+ }
+
+ /* result will be a list */
+ if (maplist) {
+ result = cons = CONS(NIL, NIL);
+ GC_PROTECT(result);
+ }
+ else
+ result = cons = list;
+
+ if (count >= sizeof(stk) / sizeof(stk[0]))
+ cdrs = LispMalloc(count * sizeof(LispObj*));
+ else
+ cdrs = &stk[0];
+ for (i = 0, rest = more_lists; i < count; i++, rest = CDR(rest))
+ cdrs[i] = CAR(rest);
+
+ /* initialize argument list */
+ arguments = acons = CONS(NIL, NIL);
+ GC_PROTECT(arguments);
+ for (i = 0; i < count; i++) {
+ RPLACD(acons, CONS(NIL, NIL));
+ acons = CDR(acons);
+ }
+
+ /* for every used list element */
+ for (offset = 0;; list = CDR(list)) {
+ acons = arguments;
+
+ /* first argument */
+ RPLACA(acons, list);
+ acons = CDR(acons);
+
+ /* for every extra list */
+ for (i = 0; i < count; i++) {
+ RPLACA(acons, cdrs[i]);
+ cdrs[i] = CDR(cdrs[i]);
+ acons = CDR(acons);
+ }
+
+ value = APPLY(function, arguments);
+
+ if (maplist) {
+ /* store result */
+ RPLACA(cons, value);
+
+ /* allocate new cell */
+ if (++offset < length) {
+ RPLACD(cons, CONS(NIL, NIL));
+ cons = CDR(cons);
+ }
+ else
+ break;
+ }
+ else if (++offset >= length)
+ break;
+ }
+
+ GC_LEAVE();
+ if (cdrs != &stk[0])
+ LispFree(cdrs);
+
+ return (result);
+}
+
+LispObj *
+Lisp_Mapl(LispBuiltin *builtin)
+/*
+ mapl function list &rest more-lists
+ */
+{
+ return (LispMapl(builtin, 0));
+}
+
+LispObj *
+Lisp_Maplist(LispBuiltin *builtin)
+/*
+ maplist function list &rest more-lists
+ */
+{
+ return (LispMapl(builtin, 1));
+}
+
+LispObj *
+Lisp_Mapcon(LispBuiltin *builtin)
+/*
+ mapcon function list &rest more-lists
+ */
+{
+ return (LispMapnconc(LispMapl(builtin, 1)));
+}
+
+LispObj *
+Lisp_Member(LispBuiltin *builtin)
+/*
+ member item list &key test test-not key
+ */
+{
+ int code, expect;
+ LispObj *compare, *lambda;
+ LispObj *item, *list, *test, *test_not, *key;
+
+ key = ARGUMENT(4);
+ test_not = ARGUMENT(3);
+ test = ARGUMENT(2);
+ list = ARGUMENT(1);
+ item = ARGUMENT(0);
+
+ if (list == NIL)
+ return (NIL);
+ CHECK_CONS(list);
+
+ CHECK_TEST();
+ if (key == UNSPEC) {
+ if (code == FEQ) {
+ for (; CONSP(list); list = CDR(list))
+ if (item == CAR(list))
+ return (list);
+ }
+ else {
+ for (; CONSP(list); list = CDR(list))
+ if (FCOMPARE(lambda, item, CAR(list), code) == expect)
+ return (list);
+ }
+ }
+ else {
+ if (code == FEQ) {
+ for (; CONSP(list); list = CDR(list))
+ if (item == APPLY1(key, CAR(list)))
+ return (list);
+ }
+ else {
+ for (; CONSP(list); list = CDR(list)) {
+ compare = APPLY1(key, CAR(list));
+ if (FCOMPARE(lambda, item, compare, code) == expect)
+ return (list);
+ }
+ }
+ }
+ /* check if is a proper list */
+ CHECK_LIST(list);
+
+ return (NIL);
+}
+
+LispObj *
+Lisp_MemberIf(LispBuiltin *builtin)
+/*
+ member-if predicate list &key key
+ */
+{
+ return (LispAssocOrMember(builtin, MEMBER, IF));
+}
+
+LispObj *
+Lisp_MemberIfNot(LispBuiltin *builtin)
+/*
+ member-if-not predicate list &key key
+ */
+{
+ return (LispAssocOrMember(builtin, MEMBER, IFNOT));
+}
+
+LispObj *
+Lisp_MultipleValueBind(LispBuiltin *builtin)
+/*
+ multiple-value-bind symbols values &rest body
+ */
+{
+ int i, head = lisp__data.env.length;
+ LispObj *result, *symbol, *value;
+
+ LispObj *symbols, *values, *body;
+
+ body = ARGUMENT(2);
+ values = ARGUMENT(1);
+ symbols = ARGUMENT(0);
+
+ result = EVAL(values);
+ for (i = -1; CONSP(symbols); symbols = CDR(symbols), i++) {
+ symbol = CAR(symbols);
+ CHECK_SYMBOL(symbol);
+ CHECK_CONSTANT(symbol);
+ if (i >= 0 && i < RETURN_COUNT)
+ value = RETURN(i);
+ else if (i < 0)
+ value = result;
+ else
+ value = NIL;
+ LispAddVar(symbol, value);
+ ++lisp__data.env.head;
+ }
+
+ /* Execute code with binded variables (if any) */
+ for (result = NIL; CONSP(body); body = CDR(body))
+ result = EVAL(CAR(body));
+
+ lisp__data.env.head = lisp__data.env.length = head;
+
+ return (result);
+}
+
+LispObj *
+Lisp_MultipleValueCall(LispBuiltin *builtin)
+/*
+ multiple-value-call function &rest form
+ */
+{
+ GC_ENTER();
+ int i;
+ LispObj *arguments, *cons, *result;
+
+ LispObj *function, *form;
+
+ form = ARGUMENT(1);
+ function = ARGUMENT(0);
+
+ /* build argument list */
+ arguments = cons = NIL;
+ for (; CONSP(form); form = CDR(form)) {
+ RETURN_COUNT = 0;
+ result = EVAL(CAR(form));
+ if (RETURN_COUNT >= 0) {
+ if (arguments == NIL) {
+ arguments = cons = CONS(result, NIL);
+ GC_PROTECT(arguments);
+ }
+ else {
+ RPLACD(cons, CONS(result, NIL));
+ cons = CDR(cons);
+ }
+ for (i = 0; i < RETURN_COUNT; i++) {
+ RPLACD(cons, CONS(RETURN(i), NIL));
+ cons = CDR(cons);
+ }
+ }
+ }
+
+ /* apply function */
+ if (POINTERP(function) && !XSYMBOLP(function) && !XFUNCTIONP(function)) {
+ function = EVAL(function);
+ GC_PROTECT(function);
+ }
+ result = APPLY(function, arguments);
+ GC_LEAVE();
+
+ return (result);
+}
+
+LispObj *
+Lisp_MultipleValueProg1(LispBuiltin *builtin)
+/*
+ multiple-value-prog1 first-form &rest form
+ */
+{
+ GC_ENTER();
+ int i, count;
+ LispObj *values, *cons;
+
+ LispObj *first_form, *form;
+
+ form = ARGUMENT(1);
+ first_form = ARGUMENT(0);
+
+ values = EVAL(first_form);
+ if (!CONSP(form))
+ return (values);
+
+ cons = NIL;
+ count = RETURN_COUNT;
+ if (count < 0)
+ values = NIL;
+ else if (count == 0) {
+ GC_PROTECT(values);
+ }
+ else {
+ values = cons = CONS(values, NIL);
+ GC_PROTECT(values);
+ for (i = 0; i < count; i++) {
+ RPLACD(cons, CONS(RETURN(i), NIL));
+ cons = CDR(cons);
+ }
+ }
+
+ for (; CONSP(form); form = CDR(form))
+ EVAL(CAR(form));
+
+ RETURN_COUNT = count;
+ if (count > 0) {
+ for (i = 0, cons = CDR(values); CONSP(cons); cons = CDR(cons), i++)
+ RETURN(i) = CAR(cons);
+ values = CAR(values);
+ }
+ GC_LEAVE();
+
+ return (values);
+}
+
+LispObj *
+Lisp_MultipleValueList(LispBuiltin *builtin)
+/*
+ multiple-value-list form
+ */
+{
+ int i;
+ GC_ENTER();
+ LispObj *form, *result, *cons;
+
+ form = ARGUMENT(0);
+
+ result = EVAL(form);
+
+ if (RETURN_COUNT < 0)
+ return (NIL);
+
+ result = cons = CONS(result, NIL);
+ GC_PROTECT(result);
+ for (i = 0; i < RETURN_COUNT; i++) {
+ RPLACD(cons, CONS(RETURN(i), NIL));
+ cons = CDR(cons);
+ }
+ GC_LEAVE();
+
+ return (result);
+}
+
+LispObj *
+Lisp_MultipleValueSetq(LispBuiltin *builtin)
+/*
+ multiple-value-setq symbols form
+ */
+{
+ int i;
+ LispObj *result, *symbol, *value;
+
+ LispObj *symbols, *form;
+
+ form = ARGUMENT(1);
+ symbols = ARGUMENT(0);
+
+ CHECK_LIST(symbols);
+ result = EVAL(form);
+ if (CONSP(symbols)) {
+ symbol = CAR(symbols);
+ CHECK_SYMBOL(symbol);
+ CHECK_CONSTANT(symbol);
+ LispSetVar(symbol, result);
+ symbols = CDR(symbols);
+ }
+ for (i = 0; CONSP(symbols); symbols = CDR(symbols), i++) {
+ symbol = CAR(symbols);
+ CHECK_SYMBOL(symbol);
+ CHECK_CONSTANT(symbol);
+ if (i < RETURN_COUNT && RETURN_COUNT > 0)
+ value = RETURN(i);
+ else
+ value = NIL;
+ LispSetVar(symbol, value);
+ }
+
+ return (result);
+}
+
+LispObj *
+Lisp_Nconc(LispBuiltin *builtin)
+/*
+ nconc &rest lists
+ */
+{
+ LispObj *list, *lists, *cons, *head, *tail;
+
+ lists = ARGUMENT(0);
+
+ /* skip any initial empty lists */
+ for (; CONSP(lists); lists = CDR(lists))
+ if (CAR(lists) != NIL)
+ break;
+
+ /* don't check if a proper list */
+ if (!CONSP(lists))
+ return (lists);
+
+ /* setup to concatenate lists */
+ list = CAR(lists);
+ CHECK_CONS(list);
+ for (cons = list; CONSP(CDR(cons)); cons = CDR(cons))
+ ;
+
+ /* if only two lists */
+ lists = CDR(lists);
+ if (!CONSP(lists)) {
+ RPLACD(cons, lists);
+
+ return (list);
+ }
+
+ /* concatenate */
+ for (; CONSP(CDR(lists)); lists = CDR(lists)) {
+ head = CAR(lists);
+ if (head == NIL)
+ continue;
+ CHECK_CONS(head);
+ for (tail = head; CONSP(CDR(tail)); tail = CDR(tail))
+ ;
+ RPLACD(cons, head);
+ cons = tail;
+ }
+ /* add last list */
+ RPLACD(cons, CAR(lists));
+
+ return (list);
+}
+
+LispObj *
+Lisp_Nreverse(LispBuiltin *builtin)
+/*
+ nreverse sequence
+ */
+{
+ return (LispXReverse(builtin, 1));
+}
+
+LispObj *
+Lisp_NsetDifference(LispBuiltin *builtin)
+/*
+ nset-difference list1 list2 &key test test-not key
+ */
+{
+ return (LispListSet(builtin, NSETDIFFERENCE));
+}
+
+LispObj *
+Lisp_Nsubstitute(LispBuiltin *builtin)
+/*
+ nsubstitute newitem olditem sequence &key from-end test test-not start end count key
+ */
+{
+ return (LispDeleteRemoveXSubstitute(builtin, NSUBSTITUTE, NONE));
+}
+
+LispObj *
+Lisp_NsubstituteIf(LispBuiltin *builtin)
+/*
+ nsubstitute-if newitem test sequence &key from-end start end count key
+ */
+{
+ return (LispDeleteRemoveXSubstitute(builtin, NSUBSTITUTE, IF));
+}
+
+LispObj *
+Lisp_NsubstituteIfNot(LispBuiltin *builtin)
+/*
+ nsubstitute-if-not newitem test sequence &key from-end start end count key
+ */
+{
+ return (LispDeleteRemoveXSubstitute(builtin, NSUBSTITUTE, IFNOT));
+}
+
+LispObj *
+Lisp_Nth(LispBuiltin *builtin)
+/*
+ nth index list
+ */
+{
+ long position;
+ LispObj *oindex, *list;
+
+ list = ARGUMENT(1);
+ oindex = ARGUMENT(0);
+
+ CHECK_INDEX(oindex);
+ position = FIXNUM_VALUE(oindex);
+
+ if (list == NIL)
+ return (NIL);
+
+ CHECK_CONS(list);
+ for (; position > 0; position--) {
+ if (!CONSP(list))
+ return (NIL);
+ list = CDR(list);
+ }
+
+ return (CONSP(list) ? CAR(list) : NIL);
+}
+
+LispObj *
+Lisp_Nthcdr(LispBuiltin *builtin)
+/*
+ nthcdr index list
+ */
+{
+ long position;
+ LispObj *oindex, *list;
+
+ list = ARGUMENT(1);
+ oindex = ARGUMENT(0);
+
+ CHECK_INDEX(oindex);
+ position = FIXNUM_VALUE(oindex);
+
+ if (list == NIL)
+ return (NIL);
+ CHECK_CONS(list);
+
+ for (; position > 0; position--) {
+ if (!CONSP(list))
+ return (NIL);
+ list = CDR(list);
+ }
+
+ return (list);
+}
+
+LispObj *
+Lisp_NthValue(LispBuiltin *builtin)
+/*
+ nth-value index form
+ */
+{
+ long i;
+ LispObj *oindex, *form, *result;
+
+ form = ARGUMENT(1);
+ oindex = ARGUMENT(0);
+
+ oindex = EVAL(oindex);
+ CHECK_INDEX(oindex);
+ i = FIXNUM_VALUE(oindex) - 1;
+
+ result = EVAL(form);
+ if (RETURN_COUNT < 0 || i >= RETURN_COUNT)
+ result = NIL;
+ else if (i >= 0)
+ result = RETURN(i);
+
+ return (result);
+}
+
+LispObj *
+Lisp_Null(LispBuiltin *builtin)
+/*
+ null list
+ */
+{
+ LispObj *list;
+
+ list = ARGUMENT(0);
+
+ return (list == NIL ? T : NIL);
+}
+
+LispObj *
+Lisp_Or(LispBuiltin *builtin)
+/*
+ or &rest args
+ */
+{
+ LispObj *result = NIL, *args;
+
+ args = ARGUMENT(0);
+
+ for (; CONSP(args); args = CDR(args)) {
+ result = EVAL(CAR(args));
+ if (result != NIL)
+ break;
+ }
+
+ return (result);
+}
+
+LispObj *
+Lisp_Pairlis(LispBuiltin *builtin)
+/*
+ pairlis key data &optional alist
+ */
+{
+ LispObj *result, *cons;
+
+ LispObj *key, *data, *alist;
+
+ alist = ARGUMENT(2);
+ data = ARGUMENT(1);
+ key = ARGUMENT(0);
+
+ if (CONSP(key) && CONSP(data)) {
+ GC_ENTER();
+
+ result = cons = CONS(CONS(CAR(key), CAR(data)), NIL);
+ GC_PROTECT(result);
+ key = CDR(key);
+ data = CDR(data);
+ for (; CONSP(key) && CONSP(data); key = CDR(key), data = CDR(data)) {
+ RPLACD(cons, CONS(CONS(CAR(key), CAR(data)), NIL));
+ cons = CDR(cons);
+ }
+ if (CONSP(key) || CONSP(data))
+ LispDestroy("%s: different length lists", STRFUN(builtin));
+ GC_LEAVE();
+ if (alist != UNSPEC)
+ RPLACD(cons, alist);
+ }
+ else
+ result = alist == UNSPEC ? NIL : alist;
+
+ return (result);
+}
+
+static LispObj *
+LispFindOrPosition(LispBuiltin *builtin,
+ int function, int comparison)
+/*
+ find item sequence &key from-end test test-not start end key
+ find-if predicate sequence &key from-end start end key
+ find-if-not predicate sequence &key from-end start end key
+ position item sequence &key from-end test test-not start end key
+ position-if predicate sequence &key from-end start end key
+ position-if-not predicate sequence &key from-end start end key
+ */
+{
+ int code = 0, istring, expect, value;
+ char *string = NULL;
+ long offset = -1, start, end, length, i = comparison == NONE ? 7 : 5;
+ LispObj *cmp, *element, **objects = NULL;
+
+ LispObj *item, *predicate, *sequence, *from_end,
+ *test, *test_not, *ostart, *oend, *key;
+
+ key = ARGUMENT(i); --i;
+ oend = ARGUMENT(i); --i;
+ ostart = ARGUMENT(i); --i;
+ if (comparison == NONE) {
+ test_not = ARGUMENT(i); --i;
+ test = ARGUMENT(i); --i;
+ }
+ else
+ test_not = test = UNSPEC;
+ from_end = ARGUMENT(i); --i;
+ if (from_end == UNSPEC)
+ from_end = NIL;
+ sequence = ARGUMENT(i); --i;
+ if (comparison == NONE) {
+ item = ARGUMENT(i);
+ predicate = Oeql;
+ }
+ else {
+ predicate = ARGUMENT(i);
+ item = NIL;
+ }
+
+ LispCheckSequenceStartEnd(builtin, sequence, ostart, oend,
+ &start, &end, &length);
+
+ if (sequence == NIL)
+ return (NIL);
+
+ /* Cannot specify both :test and :test-not */
+ if (test != UNSPEC && test_not != UNSPEC)
+ LispDestroy("%s: specify either :TEST or :TEST-NOT", STRFUN(builtin));
+
+ expect = 1;
+ if (comparison == NONE) {
+ if (test != UNSPEC)
+ predicate = test;
+ else if (test_not != UNSPEC) {
+ predicate = test_not;
+ expect = 0;
+ }
+ FUNCTION_CHECK(predicate);
+ code = FCODE(predicate);
+ }
+
+ cmp = element = NIL;
+ istring = STRINGP(sequence);
+ if (istring)
+ string = THESTR(sequence);
+ else {
+ if (!CONSP(sequence))
+ sequence = sequence->data.array.list;
+ for (i = 0; i < start; i++)
+ sequence = CDR(sequence);
+ }
+
+ if ((length = end - start) == 0)
+ return (NIL);
+
+ if (from_end != NIL && !istring) {
+ objects = LispMalloc(sizeof(LispObj*) * length);
+ for (i = length - 1; i >= 0; i--, sequence = CDR(sequence))
+ objects[i] = CAR(sequence);
+ }
+
+ for (i = 0; i < length; i++) {
+ if (istring)
+ element = SCHAR(string[from_end == NIL ? i + start : end - i - 1]);
+ else
+ element = from_end == NIL ? CAR(sequence) : objects[i];
+
+ if (key != UNSPEC)
+ cmp = APPLY1(key, element);
+ else
+ cmp = element;
+
+ /* Update list */
+ if (!istring && from_end == NIL)
+ sequence = CDR(sequence);
+
+ if (comparison == NONE)
+ value = FCOMPARE(predicate, item, cmp, code);
+ else
+ value = APPLY1(predicate, cmp) != NIL;
+
+ if ((!value &&
+ (comparison == IFNOT ||
+ (comparison == NONE && !expect))) ||
+ (value &&
+ (comparison == IF ||
+ (comparison == NONE && expect)))) {
+ offset = from_end == NIL ? i + start : end - i - 1;
+ break;
+ }
+ }
+
+ if (from_end != NIL && !istring)
+ LispFree(objects);
+
+ return (offset == -1 ? NIL : function == FIND ? element : FIXNUM(offset));
+}
+
+LispObj *
+Lisp_Pop(LispBuiltin *builtin)
+/*
+ pop place
+ */
+{
+ LispObj *result, *value;
+
+ LispObj *place;
+
+ place = ARGUMENT(0);
+
+ if (SYMBOLP(place)) {
+ result = LispGetVar(place);
+ if (result == NULL)
+ LispDestroy("EVAL: the variable %s is unbound", STROBJ(place));
+ CHECK_CONSTANT(place);
+ if (result != NIL) {
+ CHECK_CONS(result);
+ value = CDR(result);
+ result = CAR(result);
+ }
+ else
+ value = NIL;
+ LispSetVar(place, value);
+ }
+ else {
+ GC_ENTER();
+ LispObj quote;
+
+ result = EVAL(place);
+ if (result != NIL) {
+ CHECK_CONS(result);
+ value = CDR(result);
+ GC_PROTECT(value);
+ result = CAR(result);
+ }
+ else
+ value = NIL;
+ quote.type = LispQuote_t;
+ quote.data.quote = value;
+ APPLY2(Osetf, place, &quote);
+ GC_LEAVE();
+ }
+
+ return (result);
+}
+
+LispObj *
+Lisp_Position(LispBuiltin *builtin)
+/*
+ position item sequence &key from-end test test-not start end key
+ */
+{
+ return (LispFindOrPosition(builtin, POSITION, NONE));
+}
+
+LispObj *
+Lisp_PositionIf(LispBuiltin *builtin)
+/*
+ position-if predicate sequence &key from-end start end key
+ */
+{
+ return (LispFindOrPosition(builtin, POSITION, IF));
+}
+
+LispObj *
+Lisp_PositionIfNot(LispBuiltin *builtin)
+/*
+ position-if-not predicate sequence &key from-end start end key
+ */
+{
+ return (LispFindOrPosition(builtin, POSITION, IFNOT));
+}
+
+LispObj *
+Lisp_Proclaim(LispBuiltin *builtin)
+/*
+ proclaim declaration
+ */
+{
+ LispObj *arguments, *object;
+ char *operation;
+
+ LispObj *declaration;
+
+ declaration = ARGUMENT(0);
+
+ CHECK_CONS(declaration);
+
+ arguments = declaration;
+ object = CAR(arguments);
+ CHECK_SYMBOL(object);
+
+ operation = ATOMID(object);
+ if (strcmp(operation, "SPECIAL") == 0) {
+ for (arguments = CDR(arguments); CONSP(arguments);
+ arguments = CDR(arguments)) {
+ object = CAR(arguments);
+ CHECK_SYMBOL(object);
+ LispProclaimSpecial(object, NULL, NIL);
+ }
+ }
+ else if (strcmp(operation, "TYPE") == 0) {
+ /* XXX no type checking yet, but should be added */
+ }
+ /* else do nothing */
+
+ return (NIL);
+}
+
+LispObj *
+Lisp_Prog1(LispBuiltin *builtin)
+/*
+ prog1 first &rest body
+ */
+{
+ GC_ENTER();
+ LispObj *result;
+
+ LispObj *first, *body;
+
+ body = ARGUMENT(1);
+ first = ARGUMENT(0);
+
+ result = EVAL(first);
+
+ GC_PROTECT(result);
+ for (; CONSP(body); body = CDR(body))
+ (void)EVAL(CAR(body));
+ GC_LEAVE();
+
+ return (result);
+}
+
+LispObj *
+Lisp_Prog2(LispBuiltin *builtin)
+/*
+ prog2 first second &rest body
+ */
+{
+ GC_ENTER();
+ LispObj *result;
+
+ LispObj *first, *second, *body;
+
+ body = ARGUMENT(2);
+ second = ARGUMENT(1);
+ first = ARGUMENT(0);
+
+ (void)EVAL(first);
+ result = EVAL(second);
+ GC_PROTECT(result);
+ for (; CONSP(body); body = CDR(body))
+ (void)EVAL(CAR(body));
+ GC_LEAVE();
+
+ return (result);
+}
+
+LispObj *
+Lisp_Progn(LispBuiltin *builtin)
+/*
+ progn &rest body
+ */
+{
+ LispObj *result = NIL;
+
+ LispObj *body;
+
+ body = ARGUMENT(0);
+
+ for (; CONSP(body); body = CDR(body))
+ result = EVAL(CAR(body));
+
+ return (result);
+}
+
+/*
+ * This does what I believe is the expected behaviour (or at least
+ * acceptable for the the interpreter), if the code being executed
+ * ever tries to change/bind a progv symbol, the symbol state will
+ * be restored when exiting the progv block, so, code like:
+ * (progv '(*x*) '(1) (defvar *x* 10))
+ * when exiting the block, will have *x* unbound, and not a dynamic
+ * symbol; if it was already bound, will have the old value.
+ * Symbols already dynamic can be freely changed, even unbounded in
+ * the progv block.
+ */
+LispObj *
+Lisp_Progv(LispBuiltin *builtin)
+/*
+ progv symbols values &rest body
+ */
+{
+ GC_ENTER();
+ int head = lisp__data.env.length, i, count, ostk[32], *offsets;
+ LispObj *result, *list, *symbol, *value, **presult, **psymbols, **pbody;
+ int jumped, *pjumped, *pcount, **poffsets;
+ char fstk[32], *flags, **pflags;
+ LispBlock *block;
+ LispAtom *atom;
+
+ LispObj *symbols, *values, *body;
+
+ /* Possible states */
+#define DYNAMIC_SYMBOL 1
+#define GLOBAL_SYMBOL 2
+#define UNBOUND_SYMBOL 3
+
+ body = ARGUMENT(2);
+ values = ARGUMENT(1);
+ symbols = ARGUMENT(0);
+
+ /* get symbol names */
+ symbols = EVAL(symbols);
+ GC_PROTECT(symbols);
+
+ /* get symbol values */
+ values = EVAL(values);
+ GC_PROTECT(values);
+
+ /* use variables */
+ pbody = &body;
+ psymbols = &symbols;
+ presult = &result;
+ pjumped = &jumped;
+ poffsets = &offsets;
+ pcount = &count;
+ pflags = &flags;
+
+ /* count/check symbols and allocate space to remember symbol state */
+ for (count = 0, list = symbols; CONSP(list); count++, list = CDR(list)) {
+ symbol = CAR(list);
+ CHECK_SYMBOL(symbol);
+ CHECK_CONSTANT(symbol);
+ }
+ if (count > sizeof(fstk)) {
+ flags = LispMalloc(count);
+ offsets = LispMalloc(count * sizeof(int));
+ }
+ else {
+ flags = &fstk[0];
+ offsets = &ostk[0];
+ }
+
+ /* store flags and save old value if required */
+ for (i = 0, list = symbols; i < count; i++, list = CDR(list)) {
+ atom = CAR(list)->data.atom;
+ if (atom->dyn)
+ flags[i] = DYNAMIC_SYMBOL;
+ else if (atom->a_object) {
+ flags[i] = GLOBAL_SYMBOL;
+ offsets[i] = lisp__data.protect.length;
+ GC_PROTECT(atom->property->value);
+ }
+ else
+ flags[i] = UNBOUND_SYMBOL;
+ }
+
+ /* bind the symbols */
+ for (i = 0, list = symbols; i < count; i++, list = CDR(list)) {
+ symbol = CAR(list);
+ atom = symbol->data.atom;
+ if (CONSP(values)) {
+ value = CAR(values);
+ values = CDR(values);
+ }
+ else
+ value = NIL;
+ if (flags[i] != DYNAMIC_SYMBOL) {
+ if (!atom->a_object)
+ LispSetAtomObjectProperty(atom, value);
+ else
+ SETVALUE(atom, value);
+ }
+ else
+ LispAddVar(symbol, value);
+ }
+ /* bind dynamic symbols */
+ lisp__data.env.head = lisp__data.env.length;
+
+ jumped = 0;
+ result = NIL;
+ block = LispBeginBlock(NIL, LispBlockProtect);
+ if (setjmp(block->jmp) == 0) {
+ for (; CONSP(body); body = CDR(body))
+ result = EVAL(CAR(body));
+ }
+
+ /* restore symbols */
+ for (i = 0, list = symbols; i < count; i++, list = CDR(list)) {
+ symbol = CAR(list);
+ atom = symbol->data.atom;
+ if (flags[i] != DYNAMIC_SYMBOL) {
+ if (flags[i] == UNBOUND_SYMBOL)
+ LispUnsetVar(symbol);
+ else {
+ /* restore global symbol value */
+ LispSetAtomObjectProperty(atom, lisp__data.protect.objects
+ [offsets[i]]);
+ atom->dyn = 0;
+ }
+ }
+ }
+ /* unbind dynamic symbols */
+ lisp__data.env.head = lisp__data.env.length = head;
+ GC_LEAVE();
+
+ if (count > sizeof(fstk)) {
+ LispFree(flags);
+ LispFree(offsets);
+ }
+
+ LispEndBlock(block);
+ if (!lisp__data.destroyed) {
+ if (jumped)
+ result = lisp__data.block.block_ret;
+ }
+ else {
+ /* check if there is an unwind-protect block */
+ LispBlockUnwind(NULL);
+
+ /* no unwind-protect block, return to the toplevel */
+ LispDestroy(".");
+ }
+
+ return (result);
+}
+
+LispObj *
+Lisp_Provide(LispBuiltin *builtin)
+/*
+ provide module
+ */
+{
+ LispObj *module, *obj;
+
+ module = ARGUMENT(0);
+
+ CHECK_STRING(module);
+ for (obj = MOD; obj != NIL; obj = CDR(obj)) {
+ if (STRLEN(CAR(obj)) == STRLEN(module) &&
+ memcmp(THESTR(CAR(obj)), THESTR(module), STRLEN(module)) == 0)
+ return (module);
+ }
+
+ if (MOD == NIL)
+ MOD = CONS(module, NIL);
+ else {
+ RPLACD(MOD, CONS(CAR(MOD), CDR(MOD)));
+ RPLACA(MOD, module);
+ }
+
+ LispSetVar(lisp__data.modules, MOD);
+
+ return (MOD);
+}
+
+LispObj *
+Lisp_Push(LispBuiltin *builtin)
+/*
+ push item place
+ */
+{
+ LispObj *result, *list;
+
+ LispObj *item, *place;
+
+ place = ARGUMENT(1);
+ item = ARGUMENT(0);
+
+ item = EVAL(item);
+
+ if (SYMBOLP(place)) {
+ list = LispGetVar(place);
+ if (list == NULL)
+ LispDestroy("EVAL: the variable %s is unbound", STROBJ(place));
+ CHECK_CONSTANT(place);
+ LispSetVar(place, result = CONS(item, list));
+ }
+ else {
+ GC_ENTER();
+ LispObj quote;
+
+ list = EVAL(place);
+ result = CONS(item, list);
+ GC_PROTECT(result);
+ quote.type = LispQuote_t;
+ quote.data.quote = result;
+ APPLY2(Osetf, place, &quote);
+ GC_LEAVE();
+ }
+
+ return (result);
+}
+
+LispObj *
+Lisp_Pushnew(LispBuiltin *builtin)
+/*
+ pushnew item place &key key test test-not
+ */
+{
+ GC_ENTER();
+ LispObj *result, *list;
+
+ LispObj *item, *place, *key, *test, *test_not;
+
+ test_not = ARGUMENT(4);
+ test = ARGUMENT(3);
+ key = ARGUMENT(2);
+ place = ARGUMENT(1);
+ item = ARGUMENT(0);
+
+ /* Evaluate place */
+ if (SYMBOLP(place)) {
+ list = LispGetVar(place);
+ if (list == NULL)
+ LispDestroy("EVAL: the variable %s is unbound", STROBJ(place));
+ /* Do error checking now. */
+ CHECK_CONSTANT(place);
+ }
+ else
+ /* It is possible that list is not gc protected? */
+ list = EVAL(place);
+
+ item = EVAL(item);
+ GC_PROTECT(item);
+ if (key != UNSPEC) {
+ key = EVAL(key);
+ GC_PROTECT(key);
+ }
+ if (test != UNSPEC) {
+ test = EVAL(test);
+ GC_PROTECT(test);
+ }
+ else if (test_not != UNSPEC) {
+ test_not = EVAL(test_not);
+ GC_PROTECT(test_not);
+ }
+
+ result = LispAdjoin(builtin, item, list, key, test, test_not);
+
+ /* Item already in list */
+ if (result == list) {
+ GC_LEAVE();
+
+ return (result);
+ }
+
+ if (SYMBOLP(place)) {
+ CHECK_CONSTANT(place);
+ LispSetVar(place, result);
+ }
+ else {
+ LispObj quote;
+
+ GC_PROTECT(result);
+ quote.type = LispQuote_t;
+ quote.data.quote = result;
+ APPLY2(Osetf, place, &quote);
+ }
+ GC_LEAVE();
+
+ return (result);
+}
+
+LispObj *
+Lisp_Quit(LispBuiltin *builtin)
+/*
+ quit &optional status
+ */
+{
+ int status = 0;
+ LispObj *ostatus;
+
+ ostatus = ARGUMENT(0);
+
+ if (FIXNUMP(ostatus))
+ status = (int)FIXNUM_VALUE(ostatus);
+ else if (ostatus != UNSPEC)
+ LispDestroy("%s: bad exit status argument %s",
+ STRFUN(builtin), STROBJ(ostatus));
+
+ exit(status);
+}
+
+LispObj *
+Lisp_Quote(LispBuiltin *builtin)
+/*
+ quote object
+ */
+{
+ LispObj *object;
+
+ object = ARGUMENT(0);
+
+ return (object);
+}
+
+LispObj *
+Lisp_Replace(LispBuiltin *builtin)
+/*
+ replace sequence1 sequence2 &key start1 end1 start2 end2
+ */
+{
+ long length, length1, length2, start1, end1, start2, end2;
+ LispObj *sequence1, *sequence2, *ostart1, *oend1, *ostart2, *oend2;
+
+ oend2 = ARGUMENT(5);
+ ostart2 = ARGUMENT(4);
+ oend1 = ARGUMENT(3);
+ ostart1 = ARGUMENT(2);
+ sequence2 = ARGUMENT(1);
+ sequence1 = ARGUMENT(0);
+
+ LispCheckSequenceStartEnd(builtin, sequence1, ostart1, oend1,
+ &start1, &end1, &length1);
+ LispCheckSequenceStartEnd(builtin, sequence2, ostart2, oend2,
+ &start2, &end2, &length2);
+
+ if (start1 == end1 || start2 == end2)
+ return (sequence1);
+
+ length = end1 - start1;
+ if (length > end2 - start2)
+ length = end2 - start2;
+
+ if (STRINGP(sequence1)) {
+ CHECK_STRING_WRITABLE(sequence1);
+ if (!STRINGP(sequence2))
+ LispDestroy("%s: cannot store %s in %s",
+ STRFUN(builtin), STROBJ(sequence2), THESTR(sequence1));
+
+ memmove(THESTR(sequence1) + start1, THESTR(sequence2) + start2, length);
+ }
+ else {
+ int i;
+ LispObj *from, *to;
+
+ if (ARRAYP(sequence1))
+ sequence1 = sequence1->data.array.list;
+ if (ARRAYP(sequence2))
+ sequence2 = sequence2->data.array.list;
+
+ /* adjust pointers */
+ for (i = 0, from = sequence2; i < start2; i++, from = CDR(from))
+ ;
+ for (i = 0, to = sequence1; i < start1; i++, to = CDR(to))
+ ;
+
+ /* copy data */
+ for (i = 0; i < length; i++, from = CDR(from), to = CDR(to))
+ RPLACA(to, CAR(from));
+ }
+
+ return (sequence1);
+}
+
+static LispObj *
+LispDeleteOrRemoveDuplicates(LispBuiltin *builtin, int function)
+/*
+ delete-duplicates sequence &key from-end test test-not start end key
+ remove-duplicates sequence &key from-end test test-not start end key
+ */
+{
+ GC_ENTER();
+ int code, expect, value = 0;
+ long i, j, start, end, length, count;
+ LispObj *lambda, *result, *cons, *compare;
+
+ LispObj *sequence, *from_end, *test, *test_not, *ostart, *oend, *key;
+
+ key = ARGUMENT(6);
+ oend = ARGUMENT(5);
+ ostart = ARGUMENT(4);
+ test_not = ARGUMENT(3);
+ test = ARGUMENT(2);
+ from_end = ARGUMENT(1);
+ if (from_end == UNSPEC)
+ from_end = NIL;
+ sequence = ARGUMENT(0);
+
+ LispCheckSequenceStartEnd(builtin, sequence, ostart, oend,
+ &start, &end, &length);
+
+ /* Check if need to do something */
+ if (start == end)
+ return (sequence);
+
+ CHECK_TEST();
+
+ /* Initialize */
+ count = 0;
+
+ result = cons = NIL;
+ if (STRINGP(sequence)) {
+ char *ptr, *string, *buffer = LispMalloc(length + 1);
+
+ /* Use same code, update start/end offsets */
+ if (from_end != NIL) {
+ i = length - start;
+ start = length - end;
+ end = i;
+ }
+
+ if (from_end == NIL)
+ string = THESTR(sequence);
+ else {
+ /* Make a reversed copy of the sequence */
+ string = LispMalloc(length + 1);
+ for (ptr = THESTR(sequence) + length - 1, i = 0; i < length; i++)
+ string[i] = *ptr--;
+ string[i] = '\0';
+ }
+
+ ptr = buffer;
+ /* Copy leading bytes */
+ for (i = 0; i < start; i++)
+ *ptr++ = string[i];
+
+ compare = SCHAR(string[i]);
+ if (key != UNSPEC)
+ compare = APPLY1(key, compare);
+ result = cons = CONS(compare, NIL);
+ GC_PROTECT(result);
+ for (++i; i < end; i++) {
+ compare = SCHAR(string[i]);
+ if (key != UNSPEC)
+ compare = APPLY1(key, compare);
+ RPLACD(cons, CONS(compare, NIL));
+ cons = CDR(cons);
+ }
+
+ for (i = start; i < end; i++, result = CDR(result)) {
+ compare = CAR(result);
+ for (j = i + 1, cons = CDR(result); j < end; j++, cons = CDR(cons)) {
+ value = FCOMPARE(lambda, compare, CAR(cons), code);
+ if (value == expect)
+ break;
+ }
+ if (value != expect)
+ *ptr++ = string[i];
+ else
+ ++count;
+ }
+
+ if (count) {
+ /* Copy ending bytes */
+ for (; i <= length; i++) /* Also copy the ending nul */
+ *ptr++ = string[i];
+
+ if (from_end == NIL)
+ ptr = buffer;
+ else {
+ for (i = 0, ptr = buffer + strlen(buffer);
+ ptr > buffer;
+ i++)
+ string[i] = *--ptr;
+ string[i] = '\0';
+ ptr = string;
+ LispFree(buffer);
+ }
+ if (function == REMOVE)
+ result = STRING2(ptr);
+ else {
+ CHECK_STRING_WRITABLE(sequence);
+ result = sequence;
+ free(THESTR(result));
+ THESTR(result) = ptr;
+ LispMused(ptr);
+ }
+ }
+ else {
+ result = sequence;
+ if (from_end != NIL)
+ LispFree(string);
+ }
+ }
+ else {
+ long xlength = end - start;
+ LispObj *list, *object, **kobjects = NULL, **xobjects;
+ LispObj **objects = LispMalloc(sizeof(LispObj*) * xlength);
+
+ if (!CONSP(sequence))
+ object = sequence->data.array.list;
+ else
+ object = sequence;
+ list = object;
+
+ for (i = 0; i < start; i++)
+ object = CDR(object);
+
+ /* Put data in a vector */
+ if (from_end == NIL) {
+ for (i = 0; i < xlength; i++, object = CDR(object))
+ objects[i] = CAR(object);
+ }
+ else {
+ for (i = xlength - 1; i >= 0; i--, object = CDR(object))
+ objects[i] = CAR(object);
+ }
+
+ /* Apply key predicate if required */
+ if (key != UNSPEC) {
+ kobjects = LispMalloc(sizeof(LispObj*) * xlength);
+ for (i = 0; i < xlength; i++) {
+ kobjects[i] = APPLY1(key, objects[i]);
+ GC_PROTECT(kobjects[i]);
+ }
+ xobjects = kobjects;
+ }
+ else
+ xobjects = objects;
+
+ /* Check if needs to remove something */
+ for (i = 0; i < xlength; i++) {
+ compare = xobjects[i];
+ for (j = i + 1; j < xlength; j++) {
+ value = FCOMPARE(lambda, compare, xobjects[j], code);
+ if (value == expect) {
+ objects[i] = NULL;
+ ++count;
+ break;
+ }
+ }
+ }
+
+ if (count) {
+ /* Create/set result list */
+ object = list;
+
+ if (start) {
+ /* Skip first elements of resulting list */
+ if (function == REMOVE) {
+ result = cons = CONS(CAR(object), NIL);
+ GC_PROTECT(result);
+ for (i = 1, object = CDR(object);
+ i < start;
+ i++, object = CDR(object)) {
+ RPLACD(cons, CONS(CAR(object), NIL));
+ cons = CDR(cons);
+ }
+ }
+ else {
+ result = cons = object;
+ for (i = 1; i < start; i++, cons = CDR(cons))
+ ;
+ }
+ }
+ else if (function == DELETE)
+ result = list;
+
+ /* Skip initial removed elements */
+ if (function == REMOVE) {
+ for (i = 0; objects[i] == NULL && i < xlength; i++)
+ ;
+ }
+ else
+ i = 0;
+
+ if (i < xlength) {
+ int xstart, xlimit, xinc;
+
+ if (from_end == NIL) {
+ xstart = i;
+ xlimit = xlength;
+ xinc = 1;
+ }
+ else {
+ xstart = xlength - 1;
+ xlimit = i - 1;
+ xinc = -1;
+ }
+
+ if (function == REMOVE) {
+ for (i = xstart; i != xlimit; i += xinc) {
+ if (objects[i] != NULL) {
+ if (result == NIL) {
+ result = cons = CONS(objects[i], NIL);
+ GC_PROTECT(result);
+ }
+ else {
+ RPLACD(cons, CONS(objects[i], NIL));
+ cons = CDR(cons);
+ }
+ }
+ }
+ }
+ else {
+ /* Delete duplicates */
+ for (i = xstart; i != xlimit; i += xinc) {
+ if (objects[i] == NULL) {
+ if (cons == NIL) {
+ if (CONSP(CDR(result))) {
+ RPLACA(result, CADR(result));
+ RPLACD(result, CDDR(result));
+ }
+ else {
+ RPLACA(result, CDR(result));
+ RPLACD(result, NIL);
+ }
+ }
+ else {
+ if (CONSP(CDR(cons)))
+ RPLACD(cons, CDDR(cons));
+ else
+ RPLACD(cons, NIL);
+ }
+ }
+ else {
+ if (cons == NIL)
+ cons = result;
+ else
+ cons = CDR(cons);
+ }
+ }
+ }
+ }
+ if (end < length && function == REMOVE) {
+ for (i = start; i < end; i++, object = CDR(object))
+ ;
+ if (result == NIL) {
+ result = cons = CONS(CAR(object), NIL);
+ GC_PROTECT(result);
+ ++i;
+ object = CDR(object);
+ }
+ for (; i < length; i++, object = CDR(object)) {
+ RPLACD(cons, CONS(CAR(object), NIL));
+ cons = CDR(cons);
+ }
+ }
+ }
+ else
+ result = sequence;
+ LispFree(objects);
+ if (key != UNSPEC)
+ LispFree(kobjects);
+
+ if (count && !CONSP(sequence)) {
+ if (function == REMOVE)
+ result = VECTOR(result);
+ else {
+ length = FIXNUM_VALUE(CAR(sequence->data.array.dim)) - count;
+ CAR(sequence->data.array.dim) = FIXNUM(length);
+ result = sequence;
+ }
+ }
+ }
+ GC_LEAVE();
+
+ return (result);
+}
+
+LispObj *
+Lisp_RemoveDuplicates(LispBuiltin *builtin)
+/*
+ remove-duplicates sequence &key from-end test test-not start end key
+ */
+{
+ return (LispDeleteOrRemoveDuplicates(builtin, REMOVE));
+}
+
+static LispObj *
+LispDeleteRemoveXSubstitute(LispBuiltin *builtin,
+ int function, int comparison)
+/*
+ delete item sequence &key from-end test test-not start end count key
+ delete-if predicate sequence &key from-end start end count key
+ delete-if-not predicate sequence &key from-end start end count key
+ remove item sequence &key from-end test test-not start end count key
+ remove-if predicate sequence &key from-end start end count key
+ remove-if-not predicate sequence &key from-end start end count key
+ substitute newitem olditem sequence &key from-end test test-not start end count key
+ substitute-if newitem test sequence &key from-end start end count key
+ substitute-if-not newitem test sequence &key from-end start end count key
+ nsubstitute newitem olditem sequence &key from-end test test-not start end count key
+ nsubstitute-if newitem test sequence &key from-end start end count key
+ nsubstitute-if-not newitem test sequence &key from-end start end count key
+ */
+{
+ GC_ENTER();
+ int code, expect, value, inplace, substitute;
+ long i, j, start, end, length, copy, count, xstart, xend, xinc, xlength;
+
+ LispObj *result, *compare;
+
+ LispObj *item, *newitem, *lambda, *sequence, *from_end,
+ *test, *test_not, *ostart, *oend, *ocount, *key;
+
+ substitute = function == SUBSTITUTE || function == NSUBSTITUTE;
+ if (!substitute)
+ i = comparison == NONE ? 8 : 6;
+ else /* substitute */
+ i = comparison == NONE ? 9 : 7;
+
+ /* Get function arguments */
+ key = ARGUMENT(i); --i;
+ ocount = ARGUMENT(i); --i;
+ oend = ARGUMENT(i); --i;
+ ostart = ARGUMENT(i); --i;
+ if (comparison == NONE) {
+ test_not = ARGUMENT(i); --i;
+ test = ARGUMENT(i); --i;
+ }
+ else
+ test_not = test = UNSPEC;
+ from_end = ARGUMENT(i); --i;
+ if (from_end == UNSPEC)
+ from_end = NIL;
+ sequence = ARGUMENT(i); --i;
+ if (comparison != NONE) {
+ lambda = ARGUMENT(i); --i;
+ if (substitute)
+ newitem = ARGUMENT(0);
+ else
+ newitem = NIL;
+ item = NIL;
+ }
+ else {
+ lambda = NIL;
+ if (substitute) {
+ item = ARGUMENT(1);
+ newitem = ARGUMENT(0);
+ }
+ else {
+ item = ARGUMENT(0);
+ newitem = NIL;
+ }
+ }
+
+ /* Check if argument is a valid sequence, and if start/end
+ * are correctly specified. */
+ LispCheckSequenceStartEnd(builtin, sequence, ostart, oend,
+ &start, &end, &length);
+
+ /* Check count argument */
+ if (ocount == UNSPEC) {
+ count = length;
+ /* Doesn't matter, but left to right should be slightly faster */
+ from_end = NIL;
+ }
+ else {
+ CHECK_INDEX(ocount);
+ count = FIXNUM_VALUE(ocount);
+ }
+
+ /* Check if need to do something */
+ if (start == end || count == 0)
+ return (sequence);
+
+ CHECK_TEST_0();
+
+ /* Resolve comparison function, and expected result of comparison */
+ if (comparison == NONE) {
+ if (test_not == UNSPEC) {
+ if (test == UNSPEC)
+ lambda = Oeql;
+ else
+ lambda = test;
+ expect = 1;
+ }
+ else {
+ lambda = test_not;
+ expect = 0;
+ }
+ FUNCTION_CHECK(lambda);
+ }
+ else
+ expect = comparison == IFNOT ? 0 : 1;
+
+ /* Check for fast path to comparison function */
+ code = FCODE(lambda);
+
+ /* Initialize for loop */
+ copy = count;
+ result = sequence;
+ inplace = function == DELETE || function == NSUBSTITUTE;
+ xlength = end - start;
+
+ /* String is easier */
+ if (STRINGP(sequence)) {
+ char *buffer, *string;
+
+ if (comparison == NONE) {
+ CHECK_SCHAR(item);
+ }
+ if (substitute) {
+ CHECK_SCHAR(newitem);
+ }
+
+ if (from_end == NIL) {
+ xstart = start;
+ xend = end;
+ xinc = 1;
+ }
+ else {
+ xstart = end - 1;
+ xend = start - 1;
+ xinc = -1;
+ }
+
+ string = THESTR(sequence);
+ buffer = LispMalloc(length + 1);
+
+ /* Copy leading bytes, if any */
+ for (i = 0; i < start; i++)
+ buffer[i] = string[i];
+
+ for (j = xstart; i != xend && count > 0; i += xinc) {
+ compare = SCHAR(string[i]);
+ if (key != UNSPEC) {
+ compare = APPLY1(key, compare);
+ /* Value returned by the key predicate may not be protected */
+ GC_PROTECT(compare);
+ if (comparison == NONE)
+ value = FCOMPARE(lambda, item, compare, code);
+ else
+ value = APPLY1(lambda, compare) != NIL;
+ /* Unprotect value returned by the key predicate */
+ GC_LEAVE();
+ }
+ else {
+ if (comparison == NONE)
+ value = FCOMPARE(lambda, item, compare, code);
+ else
+ value = APPLY1(lambda, compare) != NIL;
+ }
+
+ if (value != expect) {
+ buffer[j] = string[i];
+ j += xinc;
+ }
+ else {
+ if (substitute) {
+ buffer[j] = SCHAR_VALUE(newitem);
+ j += xinc;
+ }
+ else
+ --count;
+ }
+ }
+
+ if (count != copy && from_end != NIL)
+ memmove(buffer + start, buffer + copy - count, count);
+
+ /* Copy remaining bytes, if any */
+ for (; i < length; i++, j++)
+ buffer[j] = string[i];
+ buffer[j] = '\0';
+
+ xlength = length - (copy - count);
+ if (inplace) {
+ CHECK_STRING_WRITABLE(sequence);
+ /* result is a pointer to sequence */
+ LispFree(THESTR(sequence));
+ LispMused(buffer);
+ THESTR(sequence) = buffer;
+ STRLEN(sequence) = xlength;
+ }
+ else
+ result = LSTRING2(buffer, xlength);
+ }
+
+ /* If inplace, need to update CAR and CDR of sequence */
+ else {
+ LispObj *list, *object;
+ LispObj **objects = LispMalloc(sizeof(LispObj*) * xlength);
+
+ if (!CONSP(sequence))
+ list = sequence->data.array.list;
+ else
+ list = sequence;
+
+ /* Put data in a vector */
+ for (i = 0, object = list; i < start; i++)
+ object = CDR(object);
+
+ for (i = 0; i < xlength; i++, object = CDR(object))
+ objects[i] = CAR(object);
+
+ if (from_end == NIL) {
+ xstart = 0;
+ xend = xlength;
+ xinc = 1;
+ }
+ else {
+ xstart = xlength - 1;
+ xend = -1;
+ xinc = -1;
+ }
+
+ /* Check if needs to remove something */
+ for (i = xstart; i != xend && count > 0; i += xinc) {
+ compare = objects[i];
+ if (key != UNSPEC) {
+ compare = APPLY1(key, compare);
+ GC_PROTECT(compare);
+ if (comparison == NONE)
+ value = FCOMPARE(lambda, item, compare, code);
+ else
+ value = APPLY1(lambda, compare) != NIL;
+ GC_LEAVE();
+ }
+ else {
+ if (comparison == NONE)
+ value = FCOMPARE(lambda, item, compare, code);
+ else
+ value = APPLY1(lambda, compare) != NIL;
+ }
+ if (value == expect) {
+ if (substitute)
+ objects[i] = newitem;
+ else
+ objects[i] = NULL;
+ --count;
+ }
+ }
+
+ if (copy != count) {
+ LispObj *cons = NIL;
+
+ i = 0;
+ object = list;
+ if (inplace) {
+ /* While result is NIL, skip initial elements of sequence */
+ result = start ? list : NIL;
+
+ /* Skip initial elements, if any */
+ for (; i < start; i++, cons = object, object = CDR(object))
+ ;
+ }
+ /* Copy initial elements, if any */
+ else {
+ result = NIL;
+ if (start) {
+ result = cons = CONS(CAR(list), NIL);
+ GC_PROTECT(result);
+ for (++i, object = CDR(list);
+ i < start;
+ i++, object = CDR(object)) {
+ RPLACD(cons, CONS(CAR(object), NIL));
+ cons = CDR(cons);
+ }
+ }
+ }
+
+ /* Skip initial removed elements, if any */
+ for (i = 0; objects[i] == NULL && i < xlength; i++)
+ ;
+
+ for (i = 0; i < xlength; i++, object = CDR(object)) {
+ if (objects[i]) {
+ if (inplace) {
+ if (result == NIL)
+ result = cons = object;
+ else {
+ RPLACD(cons, object);
+ cons = CDR(cons);
+ }
+ if (function == NSUBSTITUTE)
+ RPLACA(cons, objects[i]);
+ }
+ else {
+ if (result == NIL) {
+ result = cons = CONS(objects[i], NIL);
+ GC_PROTECT(result);
+ }
+ else {
+ RPLACD(cons, CONS(objects[i], NIL));
+ cons = CDR(cons);
+ }
+ }
+ }
+ }
+
+ if (inplace) {
+ if (result == NIL)
+ result = object;
+ else
+ RPLACD(cons, object);
+
+ if (!CONSP(sequence)) {
+ result = sequence;
+ CAR(result)->data.array.dim =
+ FIXNUM(length - (copy - count));
+ }
+ }
+ else if (end < length) {
+ i = end;
+ /* Copy ending elements, if any */
+ if (result == NIL) {
+ result = cons = CONS(CAR(object), NIL);
+ GC_PROTECT(result);
+ object = CDR(object);
+ i++;
+ }
+ for (; i < length; i++, object = CDR(object)) {
+ RPLACD(cons, CONS(CAR(object), NIL));
+ cons = CDR(cons);
+ }
+ }
+ }
+
+ /* Release comparison vector */
+ LispFree(objects);
+ }
+
+ GC_LEAVE();
+
+ return (result);
+}
+
+LispObj *
+Lisp_Remove(LispBuiltin *builtin)
+/*
+ remove item sequence &key from-end test test-not start end count key
+ */
+{
+ return (LispDeleteRemoveXSubstitute(builtin, REMOVE, NONE));
+}
+
+LispObj *
+Lisp_RemoveIf(LispBuiltin *builtin)
+/*
+ remove-if predicate sequence &key from-end start end count key
+ */
+{
+ return (LispDeleteRemoveXSubstitute(builtin, REMOVE, IF));
+}
+
+LispObj *
+Lisp_RemoveIfNot(LispBuiltin *builtin)
+/*
+ remove-if-not predicate sequence &key from-end start end count key
+ */
+{
+ return (LispDeleteRemoveXSubstitute(builtin, REMOVE, IFNOT));
+}
+
+LispObj *
+Lisp_Remprop(LispBuiltin *builtin)
+/*
+ remprop symbol indicator
+ */
+{
+ LispObj *symbol, *indicator;
+
+ indicator = ARGUMENT(1);
+ symbol = ARGUMENT(0);
+
+ CHECK_SYMBOL(symbol);
+
+ return (LispRemAtomProperty(symbol->data.atom, indicator));
+}
+
+LispObj *
+Lisp_Return(LispBuiltin *builtin)
+/*
+ return &optional result
+ */
+{
+ unsigned blevel = lisp__data.block.block_level;
+
+ LispObj *result;
+
+ result = ARGUMENT(0);
+
+ while (blevel) {
+ LispBlock *block = lisp__data.block.block[--blevel];
+
+ if (block->type == LispBlockClosure)
+ /* if reached a function call */
+ break;
+ if (block->type == LispBlockTag && block->tag == NIL) {
+ lisp__data.block.block_ret = result == UNSPEC ? NIL : EVAL(result);
+ LispBlockUnwind(block);
+ BLOCKJUMP(block);
+ }
+ }
+ LispDestroy("%s: no visible NIL block", STRFUN(builtin));
+
+ /*NOTREACHED*/
+ return (NIL);
+}
+
+LispObj *
+Lisp_ReturnFrom(LispBuiltin *builtin)
+/*
+ return-from name &optional result
+ */
+{
+ unsigned blevel = lisp__data.block.block_level;
+
+ LispObj *name, *result;
+
+ result = ARGUMENT(1);
+ name = ARGUMENT(0);
+
+ if (name != NIL && name != T && !SYMBOLP(name))
+ LispDestroy("%s: %s is not a valid block name",
+ STRFUN(builtin), STROBJ(name));
+
+ while (blevel) {
+ LispBlock *block = lisp__data.block.block[--blevel];
+
+ if (name == block->tag &&
+ (block->type == LispBlockTag || block->type == LispBlockClosure)) {
+ lisp__data.block.block_ret = result == UNSPEC ? NIL : EVAL(result);
+ LispBlockUnwind(block);
+ BLOCKJUMP(block);
+ }
+ if (block->type == LispBlockClosure)
+ /* can use return-from only in the current function */
+ break;
+ }
+ LispDestroy("%s: no visible block named %s",
+ STRFUN(builtin), STROBJ(name));
+
+ /*NOTREACHED*/
+ return (NIL);
+}
+
+static LispObj *
+LispXReverse(LispBuiltin *builtin, int inplace)
+/*
+ nreverse sequence
+ reverse sequence
+ */
+{
+ long length;
+ LispObj *list, *result = NIL;
+
+ LispObj *sequence;
+
+ sequence = ARGUMENT(0);
+
+ /* Do error checking for arrays and object type. */
+ length = LispLength(sequence);
+ if (length <= 1)
+ return (sequence);
+
+ switch (XOBJECT_TYPE(sequence)) {
+ case LispString_t: {
+ long i;
+ char *from, *to;
+
+ from = THESTR(sequence) + length - 1;
+ if (inplace) {
+ char temp;
+
+ CHECK_STRING_WRITABLE(sequence);
+ to = THESTR(sequence);
+ for (i = 0; i < length / 2; i++) {
+ temp = to[i];
+ to[i] = from[-i];
+ from[-i] = temp;
+ }
+ result = sequence;
+ }
+ else {
+ to = LispMalloc(length + 1);
+ to[length] = '\0';
+ for (i = 0; i < length; i++)
+ to[i] = from[-i];
+ result = STRING2(to);
+ }
+ } return (result);
+ case LispCons_t:
+ if (inplace) {
+ long i, j;
+ LispObj *temp;
+
+ /* For large lists this can be very slow, but for small
+ * amounts of data, this avoid allocating a buffer to
+ * to store the CAR of the sequence. This is only done
+ * to not destroy the contents of a variable.
+ */
+ for (i = 0, list = sequence;
+ i < (length + 1) / 2;
+ i++, list = CDR(list))
+ ;
+ length /= 2;
+ for (i = 0; i < length; i++, list = CDR(list)) {
+ for (j = length - i - 1, result = sequence;
+ j > 0;
+ j--, result = CDR(result))
+ ;
+ temp = CAR(list);
+ RPLACA(list, CAR(result));
+ RPLACA(result, temp);
+ }
+ return (sequence);
+ }
+ list = sequence;
+ break;
+ case LispArray_t:
+ if (inplace) {
+ sequence->data.array.list =
+ LispReverse(sequence->data.array.list);
+ return (sequence);
+ }
+ list = sequence->data.array.list;
+ break;
+ default: /* LispNil_t */
+ return (result);
+ }
+
+ {
+ GC_ENTER();
+ LispObj *cons;
+
+ result = cons = CONS(CAR(list), NIL);
+ GC_PROTECT(result);
+ for (list = CDR(list); CONSP(list); list = CDR(list)) {
+ RPLACD(cons, CONS(CAR(list), NIL));
+ cons = CDR(cons);
+ }
+ result = LispReverse(result);
+
+ GC_LEAVE();
+ }
+
+ if (ARRAYP(sequence)) {
+ list = result;
+
+ result = LispNew(list, NIL);
+ result->type = LispArray_t;
+ result->data.array.list = list;
+ result->data.array.dim = sequence->data.array.dim;
+ result->data.array.rank = sequence->data.array.rank;
+ result->data.array.type = sequence->data.array.type;
+ result->data.array.zero = sequence->data.array.zero;
+ }
+
+ return (result);
+}
+
+LispObj *
+Lisp_Reverse(LispBuiltin *builtin)
+/*
+ reverse sequence
+ */
+{
+ return (LispXReverse(builtin, 0));
+}
+
+LispObj *
+Lisp_Rplaca(LispBuiltin *builtin)
+/*
+ rplaca place value
+ */
+{
+ LispObj *place, *value;
+
+ value = ARGUMENT(1);
+ place = ARGUMENT(0);
+
+ CHECK_CONS(place);
+ RPLACA(place, value);
+
+ return (place);
+}
+
+LispObj *
+Lisp_Rplacd(LispBuiltin *builtin)
+/*
+ rplacd place value
+ */
+{
+ LispObj *place, *value;
+
+ value = ARGUMENT(1);
+ place = ARGUMENT(0);
+
+ CHECK_CONS(place);
+ RPLACD(place, value);
+
+ return (place);
+}
+
+LispObj *
+Lisp_Search(LispBuiltin *builtin)
+/*
+ search sequence1 sequence2 &key from-end test test-not key start1 start2 end1 end2
+ */
+{
+ int code = 0, expect, value;
+ long start1, start2, end1, end2, length1, length2, off1, off2, offset = -1;
+ LispObj *cmp1, *cmp2, *list1 = NIL, *lambda;
+ SeqInfo seq1, seq2;
+
+ LispObj *sequence1, *sequence2, *from_end, *test, *test_not,
+ *key, *ostart1, *ostart2, *oend1, *oend2;
+
+ oend2 = ARGUMENT(9);
+ oend1 = ARGUMENT(8);
+ ostart2 = ARGUMENT(7);
+ ostart1 = ARGUMENT(6);
+ key = ARGUMENT(5);
+ test_not = ARGUMENT(4);
+ test = ARGUMENT(3);
+ from_end = ARGUMENT(2);
+ sequence2 = ARGUMENT(1);
+ sequence1 = ARGUMENT(0);
+
+ LispCheckSequenceStartEnd(builtin, sequence1, ostart1, oend1,
+ &start1, &end1, &length1);
+ LispCheckSequenceStartEnd(builtin, sequence2, ostart2, oend2,
+ &start2, &end2, &length2);
+
+ /* Check for special conditions */
+ if (start1 == end1)
+ return (FIXNUM(end2));
+ else if (start2 == end2)
+ return (start1 == end1 ? FIXNUM(start2) : NIL);
+
+ CHECK_TEST();
+
+ if (from_end == UNSPEC)
+ from_end = NIL;
+
+ SETSEQ(seq1, sequence1);
+ SETSEQ(seq2, sequence2);
+
+ length1 = end1 - start1;
+ length2 = end2 - start2;
+
+ /* update start of sequences */
+ if (start1) {
+ if (seq1.type == LispString_t)
+ seq1.data.string += start1;
+ else {
+ for (cmp1 = seq1.data.list; start1; cmp1 = CDR(cmp1), --start1)
+ ;
+ seq1.data.list = cmp1;
+ }
+ end1 = length1;
+ }
+ if (start2) {
+ if (seq2.type == LispString_t)
+ seq2.data.string += start2;
+ else {
+ for (cmp2 = seq2.data.list; start2; cmp2 = CDR(cmp2), --start2)
+ ;
+ seq2.data.list = cmp2;
+ }
+ end2 = length2;
+ }
+
+ /* easier case */
+ if (from_end == NIL) {
+ LispObj *list2 = NIL;
+
+ /* while a match is possible */
+ while (end2 - start2 >= length1) {
+
+ /* prepare to search */
+ off1 = 0;
+ off2 = start2;
+ if (seq1.type != LispString_t)
+ list1 = seq1.data.list;
+ if (seq2.type != LispString_t)
+ list2 = seq2.data.list;
+
+ /* for every element that must match in sequence1 */
+ while (off1 < length1) {
+ if (seq1.type == LispString_t)
+ cmp1 = SCHAR(seq1.data.string[off1]);
+ else
+ cmp1 = CAR(list1);
+ if (seq2.type == LispString_t)
+ cmp2 = SCHAR(seq2.data.string[off2]);
+ else
+ cmp2 = CAR(list2);
+ if (key != UNSPEC) {
+ cmp1 = APPLY1(key, cmp1);
+ cmp2 = APPLY1(key, cmp2);
+ }
+
+ /* compare elements */
+ value = FCOMPARE(lambda, cmp1, cmp2, code);
+ if (value != expect)
+ break;
+
+ /* update offsets/sequence pointers */
+ ++off1;
+ ++off2;
+ if (seq1.type != LispString_t)
+ list1 = CDR(list1);
+ if (seq2.type != LispString_t)
+ list2 = CDR(list2);
+ }
+
+ /* if everything matched */
+ if (off1 == end1) {
+ offset = off2 - length1;
+ break;
+ }
+
+ /* update offset/sequence2 pointer */
+ ++start2;
+ if (seq2.type != LispString_t)
+ seq2.data.list = CDR(seq2.data.list);
+ }
+ }
+ else {
+ /* allocate vector if required, only list2 requires it.
+ * list1 can be traversed forward */
+ if (seq2.type != LispString_t) {
+ cmp2 = seq2.data.list;
+ seq2.data.vector = LispMalloc(sizeof(LispObj*) * length2);
+ for (off2 = 0; off2 < end2; off2++, cmp2 = CDR(cmp2))
+ seq2.data.vector[off2] = CAR(cmp2);
+ }
+
+ /* while a match is possible */
+ while (end2 >= length1) {
+
+ /* prepare to search */
+ off1 = 0;
+ off2 = end2 - length1;
+ if (seq1.type != LispString_t)
+ list1 = seq1.data.list;
+
+ /* for every element that must match in sequence1 */
+ while (off1 < end1) {
+ if (seq1.type == LispString_t)
+ cmp1 = SCHAR(seq1.data.string[off1]);
+ else
+ cmp1 = CAR(list1);
+ if (seq2.type == LispString_t)
+ cmp2 = SCHAR(seq2.data.string[off2]);
+ else
+ cmp2 = seq2.data.vector[off2];
+ if (key != UNSPEC) {
+ cmp1 = APPLY1(key, cmp1);
+ cmp2 = APPLY1(key, cmp2);
+ }
+
+ /* Compare elements */
+ value = FCOMPARE(lambda, cmp1, cmp2, code);
+ if (value != expect)
+ break;
+
+ /* Update offsets */
+ ++off1;
+ ++off2;
+ if (seq1.type != LispString_t)
+ list1 = CDR(list1);
+ }
+
+ /* If all elements matched */
+ if (off1 == end1) {
+ offset = off2 - length1;
+ break;
+ }
+
+ /* Update offset */
+ --end2;
+ }
+
+ if (seq2.type != LispString_t)
+ LispFree(seq2.data.vector);
+ }
+
+ return (offset == -1 ? NIL : FIXNUM(offset));
+}
+
+/*
+ * ext::getenv
+ */
+LispObj *
+Lisp_Setenv(LispBuiltin *builtin)
+/*
+ setenv name value &optional overwrite
+ */
+{
+ char *name, *value;
+
+ LispObj *oname, *ovalue, *overwrite;
+
+ overwrite = ARGUMENT(2);
+ ovalue = ARGUMENT(1);
+ oname = ARGUMENT(0);
+
+ CHECK_STRING(oname);
+ name = THESTR(oname);
+
+ CHECK_STRING(ovalue);
+ value = THESTR(ovalue);
+
+ setenv(name, value, overwrite != UNSPEC && overwrite != NIL);
+ value = getenv(name);
+
+ return (value ? STRING(value) : NIL);
+}
+
+LispObj *
+Lisp_Set(LispBuiltin *builtin)
+/*
+ set symbol value
+ */
+{
+ LispAtom *atom;
+ LispObj *symbol, *value;
+
+ value = ARGUMENT(1);
+ symbol = ARGUMENT(0);
+
+ CHECK_SYMBOL(symbol);
+ atom = symbol->data.atom;
+ if (atom->dyn)
+ LispSetVar(symbol, value);
+ else if (atom->watch || !atom->a_object)
+ LispSetAtomObjectProperty(atom, value);
+ else {
+ CHECK_CONSTANT(symbol);
+ SETVALUE(atom, value);
+ }
+
+ return (value);
+}
+
+LispObj *
+Lisp_SetDifference(LispBuiltin *builtin)
+/*
+ set-difference list1 list2 &key test test-not key
+ */
+{
+ return (LispListSet(builtin, SETDIFFERENCE));
+}
+
+LispObj *
+Lisp_SetExclusiveOr(LispBuiltin *builtin)
+/*
+ set-exclusive-or list1 list2 &key test test-not key
+ */
+{
+ return (LispListSet(builtin, SETEXCLUSIVEOR));
+}
+
+LispObj *
+Lisp_NsetExclusiveOr(LispBuiltin *builtin)
+/*
+ nset-exclusive-or list1 list2 &key test test-not key
+ */
+{
+ return (LispListSet(builtin, NSETEXCLUSIVEOR));
+}
+
+LispObj *
+Lisp_SetQ(LispBuiltin *builtin)
+/*
+ setq &rest form
+ */
+{
+ LispObj *result, *variable, *form;
+
+ form = ARGUMENT(0);
+
+ result = NIL;
+ for (; CONSP(form); form = CDR(form)) {
+ variable = CAR(form);
+ CHECK_SYMBOL(variable);
+ CHECK_CONSTANT(variable);
+ form = CDR(form);
+ if (!CONSP(form))
+ LispDestroy("%s: odd number of arguments", STRFUN(builtin));
+ result = EVAL(CAR(form));
+ LispSetVar(variable, result);
+ }
+
+ return (result);
+}
+
+LispObj *
+Lisp_Psetq(LispBuiltin *builtin)
+/*
+ psetq &rest form
+ */
+{
+ GC_ENTER();
+ int base = gc__protect;
+ LispObj *value, *symbol, *list, *form;
+
+ form = ARGUMENT(0);
+
+ /* parallel setq, first pass evaluate values and basic error checking */
+ for (list = form; CONSP(list); list = CDR(list)) {
+ symbol = CAR(list);
+ CHECK_SYMBOL(symbol);
+ list = CDR(list);
+ if (!CONSP(list))
+ LispDestroy("%s: odd number of arguments", STRFUN(builtin));
+ value = EVAL(CAR(list));
+ GC_PROTECT(value);
+ }
+
+ /* second pass, assign values */
+ for (; CONSP(form); form = CDDR(form)) {
+ symbol = CAR(form);
+ CHECK_CONSTANT(symbol);
+ LispSetVar(symbol, lisp__data.protect.objects[base++]);
+ }
+ GC_LEAVE();
+
+ return (NIL);
+}
+
+LispObj *
+Lisp_Setf(LispBuiltin *builtin)
+/*
+ setf &rest form
+ */
+{
+ LispAtom *atom;
+ LispObj *setf, *place, *value, *result = NIL, *data;
+
+ LispObj *form;
+
+ form = ARGUMENT(0);
+
+ for (; CONSP(form); form = CDR(form)) {
+ place = CAR(form);
+ form = CDR(form);
+ if (!CONSP(form))
+ LispDestroy("%s: odd number of arguments", STRFUN(builtin));
+ value = CAR(form);
+
+ if (!POINTERP(place))
+ goto invalid_place;
+ if (XSYMBOLP(place)) {
+ CHECK_CONSTANT(place);
+ result = EVAL(value);
+ (void)LispSetVar(place, result);
+ }
+ else if (XCONSP(place)) {
+ /* it really should not be required to protect any object
+ * evaluated here, but is done for safety in case one of
+ * the evaluated forms returns data not gc protected, what
+ * could cause surprises if the object is garbage collected
+ * before finishing setf. */
+ GC_ENTER();
+
+ setf = CAR(place);
+ if (!SYMBOLP(setf))
+ goto invalid_place;
+ if (!CONSP(CDR(place)))
+ goto invalid_place;
+
+ value = EVAL(value);
+ GC_PROTECT(value);
+
+ atom = setf->data.atom;
+ if (atom->a_defsetf == 0) {
+ if (atom->a_defstruct &&
+ atom->property->structure.function >= 0) {
+ /* Use a default setf method for the structure field, as
+ * if this definition have been done
+ * (defsetf THE-STRUCT-FIELD (struct) (value)
+ * `(lisp::struct-store 'THE-STRUCT-FIELD ,struct ,value))
+ */
+ place = CDR(place);
+ data = CAR(place);
+ if (CONSP(CDR(place)))
+ goto invalid_place;
+ data = EVAL(data);
+ GC_PROTECT(data);
+ result = APPLY3(Ostruct_store, setf, data, value);
+ GC_LEAVE();
+ continue;
+ }
+ /* Must also expand macros */
+ else if (atom->a_function &&
+ atom->property->fun.function->funtype == LispMacro) {
+ result = LispRunSetfMacro(atom, CDR(place), value);
+ continue;
+ }
+ goto invalid_place;
+ }
+
+ place = CDR(place);
+ setf = setf->data.atom->property->setf;
+ if (SYMBOLP(setf)) {
+ LispObj *arguments, *cons;
+
+ if (!CONSP(CDR(place))) {
+ arguments = EVAL(CAR(place));
+ GC_PROTECT(arguments);
+ result = APPLY2(setf, arguments, value);
+ }
+ else if (!CONSP(CDDR(place))) {
+ arguments = EVAL(CAR(place));
+ GC_PROTECT(arguments);
+ cons = EVAL(CADR(place));
+ GC_PROTECT(cons);
+ result = APPLY3(setf, arguments, cons, value);
+ }
+ else {
+ arguments = cons = CONS(EVAL(CAR(place)), NIL);
+ GC_PROTECT(arguments);
+ for (place = CDR(place); CONSP(place); place = CDR(place)) {
+ RPLACD(cons, CONS(EVAL(CAR(place)), NIL));
+ cons = CDR(cons);
+ }
+ RPLACD(cons, CONS(value, NIL));
+ result = APPLY(setf, arguments);
+ }
+ }
+ else
+ result = LispRunSetf(atom->property->salist, setf, place, value);
+ GC_LEAVE();
+ }
+ else
+ goto invalid_place;
+ }
+
+ return (result);
+invalid_place:
+ LispDestroy("%s: %s is an invalid place", STRFUN(builtin), STROBJ(place));
+ /*NOTREACHED*/
+ return (NIL);
+}
+
+LispObj *
+Lisp_Psetf(LispBuiltin *builtin)
+/*
+ psetf &rest form
+ */
+{
+ int base;
+ GC_ENTER();
+ LispAtom *atom;
+ LispObj *setf, *place = NIL, *value, *data;
+
+ LispObj *form;
+
+ form = ARGUMENT(0);
+
+ /* parallel setf, first pass evaluate values and basic error checking */
+ base = gc__protect;
+ for (setf = form; CONSP(setf); setf = CDR(setf)) {
+ if (!POINTERP(CAR(setf)))
+ goto invalid_place;
+ setf = CDR(setf);
+ if (!CONSP(setf))
+ LispDestroy("%s: odd number of arguments", STRFUN(builtin));
+ value = EVAL(CAR(setf));
+ GC_PROTECT(value);
+ }
+
+ /* second pass, assign values */
+ for (; CONSP(form); form = CDDR(form)) {
+ place = CAR(form);
+ value = lisp__data.protect.objects[base++];
+
+ if (XSYMBOLP(place)) {
+ CHECK_CONSTANT(place);
+ (void)LispSetVar(place, value);
+ }
+ else if (XCONSP(place)) {
+ LispObj *arguments, *cons;
+ int xbase = lisp__data.protect.length;
+
+ setf = CAR(place);
+ if (!SYMBOLP(setf))
+ goto invalid_place;
+ if (!CONSP(CDR(place)))
+ goto invalid_place;
+
+ atom = setf->data.atom;
+ if (atom->a_defsetf == 0) {
+ if (atom->a_defstruct &&
+ atom->property->structure.function >= 0) {
+ place = CDR(place);
+ data = CAR(place);
+ if (CONSP(CDR(place)))
+ goto invalid_place;
+ data = EVAL(data);
+ GC_PROTECT(data);
+ (void)APPLY3(Ostruct_store, setf, data, value);
+ lisp__data.protect.length = xbase;
+ continue;
+ }
+ else if (atom->a_function &&
+ atom->property->fun.function->funtype == LispMacro) {
+ (void)LispRunSetfMacro(atom, CDR(place), value);
+ lisp__data.protect.length = xbase;
+ continue;
+ }
+ goto invalid_place;
+ }
+
+ place = CDR(place);
+ setf = setf->data.atom->property->setf;
+ if (SYMBOLP(setf)) {
+ if (!CONSP(CDR(place))) {
+ arguments = EVAL(CAR(place));
+ GC_PROTECT(arguments);
+ (void)APPLY2(setf, arguments, value);
+ }
+ else if (!CONSP(CDDR(place))) {
+ arguments = EVAL(CAR(place));
+ GC_PROTECT(arguments);
+ cons = EVAL(CADR(place));
+ GC_PROTECT(cons);
+ (void)APPLY3(setf, arguments, cons, value);
+ }
+ else {
+ arguments = cons = CONS(EVAL(CAR(place)), NIL);
+ GC_PROTECT(arguments);
+ for (place = CDR(place); CONSP(place); place = CDR(place)) {
+ RPLACD(cons, CONS(EVAL(CAR(place)), NIL));
+ cons = CDR(cons);
+ }
+ RPLACD(cons, CONS(value, NIL));
+ (void)APPLY(setf, arguments);
+ }
+ lisp__data.protect.length = xbase;
+ }
+ else
+ (void)LispRunSetf(atom->property->salist, setf, place, value);
+ }
+ else
+ goto invalid_place;
+ }
+ GC_LEAVE();
+
+ return (NIL);
+invalid_place:
+ LispDestroy("%s: %s is an invalid place", STRFUN(builtin), STROBJ(place));
+ /*NOTREACHED*/
+ return (NIL);
+}
+
+LispObj *
+Lisp_Sleep(LispBuiltin *builtin)
+/*
+ sleep seconds
+ */
+{
+ long sec, msec;
+ double value, dsec;
+
+ LispObj *seconds;
+
+ seconds = ARGUMENT(0);
+
+ value = -1.0;
+ switch (OBJECT_TYPE(seconds)) {
+ case LispFixnum_t:
+ value = FIXNUM_VALUE(seconds);
+ break;
+ case LispDFloat_t:
+ value = DFLOAT_VALUE(seconds);
+ break;
+ default:
+ break;
+ }
+
+ if (value < 0.0 || value > MOST_POSITIVE_FIXNUM)
+ LispDestroy("%s: %s is not a positive fixnum",
+ STRFUN(builtin), STROBJ(seconds));
+
+ msec = modf(value, &dsec) * 1e6;
+ sec = dsec;
+
+ if (sec)
+ sleep(sec);
+ if (msec)
+ usleep(msec);
+
+ return (NIL);
+}
+
+/*
+ * This function is called recursively, but the contents of "list2" are
+ * kept gc protected until it returns to LispSort. This is required partly
+ * because the "gc protection logic" protects an object, not the contents
+ * of the c pointer.
+ */
+static LispObj *
+LispMergeSort(LispObj *list, LispObj *predicate, LispObj *key, int code)
+{
+ int protect;
+ LispObj *list1, *list2, *left, *right, *result, *cons;
+
+ /* Check if list length is larger than 1 */
+ if (!CONSP(list) || !CONSP(CDR(list)))
+ return (list);
+
+ list1 = list2 = list;
+ for (;;) {
+ list = CDR(list);
+ if (!CONSP(list))
+ break;
+ list = CDR(list);
+ if (!CONSP(list))
+ break;
+ list2 = CDR(list2);
+ }
+ cons = list2;
+ list2 = CDR(list2);
+ RPLACD(cons, NIL);
+
+ protect = 0;
+ if (lisp__data.protect.length + 2 >= lisp__data.protect.space)
+ LispMoreProtects();
+ lisp__data.protect.objects[lisp__data.protect.length++] = list2;
+ list1 = LispMergeSort(list1, predicate, key, code);
+ list2 = LispMergeSort(list2, predicate, key, code);
+
+ left = CAR(list1);
+ right = CAR(list2);
+ if (key != UNSPEC) {
+ protect = lisp__data.protect.length;
+ left = APPLY1(key, left);
+ lisp__data.protect.objects[protect] = left;
+ right = APPLY1(key, right);
+ lisp__data.protect.objects[protect + 1] = right;
+ }
+
+ result = NIL;
+ for (;;) {
+ if ((FCOMPARE(predicate, left, right, code)) == 0 &&
+ (FCOMPARE(predicate, right, left, code)) == 1) {
+ /* right is "smaller" */
+ if (result == NIL)
+ result = list2;
+ else
+ RPLACD(cons, list2);
+ cons = list2;
+ list2 = CDR(list2);
+ if (!CONSP(list2)) {
+ RPLACD(cons, list1);
+ break;
+ }
+ right = CAR(list2);
+ if (key != UNSPEC) {
+ right = APPLY1(key, right);
+ lisp__data.protect.objects[protect + 1] = right;
+ }
+ }
+ else {
+ /* left is "smaller" */
+ if (result == NIL)
+ result = list1;
+ else
+ RPLACD(cons, list1);
+ cons = list1;
+ list1 = CDR(list1);
+ if (!CONSP(list1)) {
+ RPLACD(cons, list2);
+ break;
+ }
+ left = CAR(list1);
+ if (key != UNSPEC) {
+ left = APPLY1(key, left);
+ lisp__data.protect.objects[protect] = left;
+ }
+ }
+ }
+ if (key != UNSPEC)
+ lisp__data.protect.length = protect;
+
+ return (result);
+}
+
+/* XXX The first version made a copy of the list and then adjusted
+ * the CARs of the list. To minimize GC time now it is now doing
+ * the sort inplace. So, instead of writing just (sort variable)
+ * now it is required to write (setq variable (sort variable))
+ * if the variable should always keep all elements.
+ */
+LispObj *
+Lisp_Sort(LispBuiltin *builtin)
+/*
+ sort sequence predicate &key key
+ */
+{
+ GC_ENTER();
+ int istring, code;
+ long length;
+ char *string;
+
+ LispObj *list, *work, *cons = NULL;
+
+ LispObj *sequence, *predicate, *key;
+
+ key = ARGUMENT(2);
+ predicate = ARGUMENT(1);
+ sequence = ARGUMENT(0);
+
+ length = LispLength(sequence);
+ if (length < 2)
+ return (sequence);
+
+ list = sequence;
+ istring = XSTRINGP(sequence);
+ if (istring) {
+ CHECK_STRING_WRITABLE(sequence);
+ /* Convert string to list */
+ string = THESTR(sequence);
+ work = cons = CONS(SCHAR(string[0]), NIL);
+ GC_PROTECT(work);
+ for (++string; *string; ++string) {
+ RPLACD(cons, CONS(SCHAR(*string), NIL));
+ cons = CDR(cons);
+ }
+ }
+ else if (ARRAYP(list))
+ work = list->data.array.list;
+ else
+ work = list;
+
+ FUNCTION_CHECK(predicate);
+ code = FCODE(predicate);
+ work = LispMergeSort(work, predicate, key, code);
+
+ if (istring) {
+ /* Convert list to string */
+ string = THESTR(sequence);
+ for (; CONSP(work); ++string, work = CDR(work))
+ *string = SCHAR_VALUE(CAR(work));
+ }
+ else if (ARRAYP(list))
+ list->data.array.list = work;
+ else
+ sequence = work;
+ GC_LEAVE();
+
+ return (sequence);
+}
+
+LispObj *
+Lisp_Subseq(LispBuiltin *builtin)
+/*
+ subseq sequence start &optional end
+ */
+{
+ long start, end, length, seqlength;
+
+ LispObj *sequence, *ostart, *oend, *result;
+
+ oend = ARGUMENT(2);
+ ostart = ARGUMENT(1);
+ sequence = ARGUMENT(0);
+
+ LispCheckSequenceStartEnd(builtin, sequence, ostart, oend,
+ &start, &end, &length);
+
+ seqlength = end - start;
+
+ if (sequence == NIL)
+ result = NIL;
+ else if (XSTRINGP(sequence)) {
+ char *string = LispMalloc(seqlength + 1);
+
+ memcpy(string, THESTR(sequence) + start, seqlength);
+ string[seqlength] = '\0';
+ result = STRING2(string);
+ }
+ else {
+ GC_ENTER();
+ LispObj *object;
+
+ if (end > start) {
+ /* list or array */
+ int count;
+ LispObj *cons;
+
+ if (ARRAYP(sequence))
+ object = sequence->data.array.list;
+ else
+ object = sequence;
+ /* goto first element to copy */
+ for (count = 0; count < start; count++, object = CDR(object))
+ ;
+ result = cons = CONS(CAR(object), NIL);
+ GC_PROTECT(result);
+ for (++count, object = CDR(object); count < end; count++,
+ object = CDR(object)) {
+ RPLACD(cons, CONS(CAR(object), NIL));
+ cons = CDR(cons);
+ }
+ }
+ else
+ result = NIL;
+
+ if (ARRAYP(sequence)) {
+ object = LispNew(NIL, NIL);
+ GC_PROTECT(object);
+ object->type = LispArray_t;
+ object->data.array.list = result;
+ object->data.array.dim = CONS(FIXNUM(seqlength), NIL);
+ object->data.array.rank = 1;
+ object->data.array.type = sequence->data.array.type;
+ object->data.array.zero = length == 0;
+ result = object;
+ }
+ GC_LEAVE();
+ }
+
+ return (result);
+}
+
+LispObj *
+Lisp_Subsetp(LispBuiltin *builtin)
+/*
+ subsetp list1 list2 &key test test-not key
+ */
+{
+ return (LispListSet(builtin, SUBSETP));
+}
+
+
+LispObj *
+Lisp_Substitute(LispBuiltin *builtin)
+/*
+ substitute newitem olditem sequence &key from-end test test-not start end count key
+ */
+{
+ return (LispDeleteRemoveXSubstitute(builtin, SUBSTITUTE, NONE));
+}
+
+LispObj *
+Lisp_SubstituteIf(LispBuiltin *builtin)
+/*
+ substitute-if newitem test sequence &key from-end start end count key
+ */
+{
+ return (LispDeleteRemoveXSubstitute(builtin, SUBSTITUTE, IF));
+}
+
+LispObj *
+Lisp_SubstituteIfNot(LispBuiltin *builtin)
+/*
+ substitute-if-not newitem test sequence &key from-end start end count key
+ */
+{
+ return (LispDeleteRemoveXSubstitute(builtin, SUBSTITUTE, IFNOT));
+}
+
+LispObj *
+Lisp_Symbolp(LispBuiltin *builtin)
+/*
+ symbolp object
+ */
+{
+ LispObj *object;
+
+ object = ARGUMENT(0);
+
+ return (SYMBOLP(object) ? T : NIL);
+}
+
+LispObj *
+Lisp_SymbolFunction(LispBuiltin *builtin)
+/*
+ symbol-function symbol
+ */
+{
+ LispObj *symbol;
+
+ symbol = ARGUMENT(0);
+ CHECK_SYMBOL(symbol);
+
+ return (LispSymbolFunction(symbol));
+}
+
+LispObj *
+Lisp_SymbolName(LispBuiltin *builtin)
+/*
+ symbol-name symbol
+ */
+{
+ LispObj *symbol;
+
+ symbol = ARGUMENT(0);
+ CHECK_SYMBOL(symbol);
+
+ return (LispSymbolName(symbol));
+}
+
+LispObj *
+Lisp_SymbolPackage(LispBuiltin *builtin)
+/*
+ symbol-package symbol
+ */
+{
+ LispObj *symbol;
+
+ symbol = ARGUMENT(0);
+ CHECK_SYMBOL(symbol);
+
+ symbol = symbol->data.atom->package;
+
+ return (symbol ? symbol : NIL);
+}
+
+LispObj *
+Lisp_SymbolPlist(LispBuiltin *builtin)
+/*
+ symbol-plist symbol
+ */
+{
+ LispObj *symbol;
+
+ symbol = ARGUMENT(0);
+
+ CHECK_SYMBOL(symbol);
+
+ return (symbol->data.atom->a_property ?
+ symbol->data.atom->property->properties : NIL);
+}
+
+LispObj *
+Lisp_SymbolValue(LispBuiltin *builtin)
+/*
+ symbol-value symbol
+ */
+{
+ LispAtom *atom;
+ LispObj *symbol;
+
+ symbol = ARGUMENT(0);
+
+ CHECK_SYMBOL(symbol);
+ atom = symbol->data.atom;
+ if (!atom->a_object || atom->property->value == UNBOUND) {
+ if (atom->package == lisp__data.keyword)
+ return (symbol);
+ LispDestroy("%s: the symbol %s has no value",
+ STRFUN(builtin), STROBJ(symbol));
+ }
+
+ return (atom->dyn ? LispGetVar(symbol) : atom->property->value);
+}
+
+LispObj *
+Lisp_Tagbody(LispBuiltin *builtin)
+/*
+ tagbody &rest body
+ */
+{
+ GC_ENTER();
+ int stack, lex, length;
+ LispObj *list, *body, *ptr, *tag, *labels, *map,
+ **p_list, **p_body, **p_labels;
+ LispBlock *block;
+
+ body = ARGUMENT(0);
+
+ /* Save environment information */
+ stack = lisp__data.stack.length;
+ lex = lisp__data.env.lex;
+ length = lisp__data.env.length;
+
+ /* Since the body may be large, and the code may iterate several
+ * thousand times, it is not a bad idea to avoid checking all
+ * elements of the body to verify if it is a tag. */
+ for (labels = map = NIL, ptr = body; CONSP(ptr); ptr = CDR(ptr)) {
+ tag = CAR(ptr);
+ switch (OBJECT_TYPE(tag)) {
+ case LispNil_t:
+ case LispAtom_t:
+ case LispFixnum_t:
+ /* Don't allow duplicated labels */
+ for (list = labels; CONSP(list); list = CDDR(list)) {
+ if (CAR(list) == tag)
+ LispDestroy("%s: tag %s specified more than once",
+ STRFUN(builtin), STROBJ(tag));
+ }
+ if (labels == NIL) {
+ labels = CONS(tag, CONS(NIL, NIL));
+ map = CDR(labels);
+ GC_PROTECT(labels);
+ }
+ else {
+ RPLACD(map, CONS(tag, CONS(NIL, NIL)));
+ map = CDDR(map);
+ }
+ break;
+ case LispCons_t:
+ /* Restart point for tag */
+ if (map != NIL && CAR(map) == NIL)
+ RPLACA(map, ptr);
+ break;
+ default:
+ break;
+ }
+ }
+ /* Check for consecutive labels without code between them */
+ for (ptr = labels; CONSP(ptr); ptr = CDDR(ptr)) {
+ if (CADR(ptr) == NIL) {
+ for (map = CDDR(ptr); CONSP(map); map = CDDR(map)) {
+ if (CADR(map) != NIL) {
+ RPLACA(CDR(ptr), CADR(map));
+ break;
+ }
+ }
+ }
+ }
+
+ /* Initialize */
+ list = body;
+ p_list = &list;
+ p_body = &body;
+ p_labels = &labels;
+ block = LispBeginBlock(NIL, LispBlockBody);
+
+ /* Loop */
+ if (setjmp(block->jmp) != 0) {
+ /* Restore environment */
+ lisp__data.stack.length = stack;
+ lisp__data.env.lex = lex;
+ lisp__data.env.head = lisp__data.env.length = length;
+
+ tag = lisp__data.block.block_ret;
+ for (ptr = labels; CONSP(ptr); ptr = CDDR(ptr)) {
+ map = CAR(ptr);
+ if (map == tag)
+ break;
+ }
+
+ if (!CONSP(ptr))
+ LispDestroy("%s: no such tag %s", STRFUN(builtin), STROBJ(tag));
+
+ *p_body = CADR(ptr);
+ }
+
+ /* Execute code */
+ for (; CONSP(body); body = CDR(body)) {
+ LispObj *form = CAR(body);
+
+ if (CONSP(form))
+ EVAL(form);
+ }
+ /* If got here, (go) not called, else, labels will be candidate to gc
+ * when GC_LEAVE() be called by the code in the bottom of the stack. */
+ GC_LEAVE();
+
+ /* Finished */
+ LispEndBlock(block);
+
+ /* Always return NIL */
+ return (NIL);
+}
+
+LispObj *
+Lisp_The(LispBuiltin *builtin)
+/*
+ the value-type form
+ */
+{
+ LispObj *value_type, *form;
+
+ form = ARGUMENT(1);
+ value_type = ARGUMENT(0);
+
+ form = EVAL(form);
+
+ return (LispCoerce(builtin, form, value_type));
+}
+
+LispObj *
+Lisp_Throw(LispBuiltin *builtin)
+/*
+ throw tag result
+ */
+{
+ unsigned blevel = lisp__data.block.block_level;
+
+ LispObj *tag, *result;
+
+ result = ARGUMENT(1);
+ tag = ARGUMENT(0);
+
+ tag = EVAL(tag);
+
+ if (blevel == 0)
+ LispDestroy("%s: not within a block", STRFUN(builtin));
+
+ while (blevel) {
+ LispBlock *block = lisp__data.block.block[--blevel];
+
+ if (block->type == LispBlockCatch && tag == block->tag) {
+ lisp__data.block.block_ret = EVAL(result);
+ LispBlockUnwind(block);
+ BLOCKJUMP(block);
+ }
+ }
+ LispDestroy("%s: %s is not a valid tag", STRFUN(builtin), STROBJ(tag));
+
+ /*NOTREACHED*/
+ return (NIL);
+}
+
+static LispObj *
+LispTreeEqual(LispObj *left, LispObj *right, LispObj *test, int expect)
+{
+ LispObj *cmp_left, *cmp_right;
+
+ if ((OBJECT_TYPE(left)) ^ (OBJECT_TYPE(right)))
+ return (NIL);
+ if (CONSP(left)) {
+ for (; CONSP(left) && CONSP(right);
+ left = CDR(left), right = CDR(right)) {
+ cmp_left = CAR(left);
+ cmp_right = CAR(right);
+ if ((OBJECT_TYPE(cmp_left)) ^ (OBJECT_TYPE(cmp_right)))
+ return (NIL);
+ if (CONSP(cmp_left)) {
+ if (LispTreeEqual(cmp_left, cmp_right, test, expect) == NIL)
+ return (NIL);
+ }
+ else {
+ if (POINTERP(cmp_left) &&
+ (XQUOTEP(cmp_left) || XBACKQUOTEP(cmp_left))) {
+ cmp_left = cmp_left->data.quote;
+ cmp_right = cmp_right->data.quote;
+ }
+ else if (COMMAP(cmp_left)) {
+ cmp_left = cmp_left->data.comma.eval;
+ cmp_right = cmp_right->data.comma.eval;
+ }
+ if ((APPLY2(test, cmp_left, cmp_right) != NIL) != expect)
+ return (NIL);
+ }
+ }
+ if ((OBJECT_TYPE(left)) ^ (OBJECT_TYPE(right)))
+ return (NIL);
+ }
+
+ if (POINTERP(left) && (XQUOTEP(left) || XBACKQUOTEP(left))) {
+ left = left->data.quote;
+ right = right->data.quote;
+ }
+ else if (COMMAP(left)) {
+ left = left->data.comma.eval;
+ right = right->data.comma.eval;
+ }
+
+ return ((APPLY2(test, left, right) != NIL) == expect ? T : NIL);
+}
+
+LispObj *
+Lisp_TreeEqual(LispBuiltin *builtin)
+/*
+ tree-equal tree-1 tree-2 &key test test-not
+ */
+{
+ int expect;
+ LispObj *compare;
+
+ LispObj *tree_1, *tree_2, *test, *test_not;
+
+ test_not = ARGUMENT(3);
+ test = ARGUMENT(2);
+ tree_2 = ARGUMENT(1);
+ tree_1 = ARGUMENT(0);
+
+ CHECK_TEST_0();
+ if (test_not != UNSPEC) {
+ expect = 0;
+ compare = test_not;
+ }
+ else {
+ if (test == UNSPEC)
+ test = Oeql;
+ expect = 1;
+ compare = test;
+ }
+
+ return (LispTreeEqual(tree_1, tree_2, compare, expect));
+}
+
+LispObj *
+Lisp_Typep(LispBuiltin *builtin)
+/*
+ typep object type
+ */
+{
+ LispObj *result = NULL;
+
+ LispObj *object, *type;
+
+ type = ARGUMENT(1);
+ object = ARGUMENT(0);
+
+ if (SYMBOLP(type)) {
+ Atom_id atom = ATOMID(type);
+
+ if (OBJECT_TYPE(object) == LispStruct_t)
+ result = ATOMID(CAR(object->data.struc.def)) == atom ? T : NIL;
+ else if (type->data.atom->a_defstruct &&
+ type->data.atom->property->structure.function == STRUCT_NAME)
+ result = NIL;
+ else if (atom == Snil)
+ result = object == NIL ? T : NIL;
+ else if (atom == St)
+ result = object == T ? T : NIL;
+ else if (atom == Satom)
+ result = !CONSP(object) ? T : NIL;
+ else if (atom == Ssymbol)
+ result = SYMBOLP(object) || object == NIL || object == T ? T : NIL;
+ else if (atom == Sinteger)
+ result = INTEGERP(object) ? T : NIL;
+ else if (atom == Srational)
+ result = RATIONALP(object) ? T : NIL;
+ else if (atom == Scons || atom == Slist)
+ result = CONSP(object) ? T : NIL;
+ else if (atom == Sstring)
+ result = STRINGP(object) ? T : NIL;
+ else if (atom == Scharacter)
+ result = SCHARP(object) ? T : NIL;
+ else if (atom == Scomplex)
+ result = COMPLEXP(object) ? T : NIL;
+ else if (atom == Svector || atom == Sarray)
+ result = ARRAYP(object) ? T : NIL;
+ else if (atom == Skeyword)
+ result = KEYWORDP(object) ? T : NIL;
+ else if (atom == Sfunction)
+ result = LAMBDAP(object) ? T : NIL;
+ else if (atom == Spathname)
+ result = PATHNAMEP(object) ? T : NIL;
+ else if (atom == Sopaque)
+ result = OPAQUEP(object) ? T : NIL;
+ }
+ else if (CONSP(type)) {
+ if (OBJECT_TYPE(object) == LispStruct_t &&
+ SYMBOLP(CAR(type)) && ATOMID(CAR(type)) == Sstruct &&
+ SYMBOLP(CAR(CDR(type))) && CDR(CDR(type)) == NIL) {
+ result = ATOMID(CAR(object->data.struc.def)) ==
+ ATOMID(CAR(CDR(type))) ? T : NIL;
+ }
+ }
+ else if (type == NIL)
+ result = object == NIL ? T : NIL;
+ else if (type == T)
+ result = object == T ? T : NIL;
+ if (result == NULL)
+ LispDestroy("%s: bad type specification %s",
+ STRFUN(builtin), STROBJ(type));
+
+ return (result);
+}
+
+LispObj *
+Lisp_Union(LispBuiltin *builtin)
+/*
+ union list1 list2 &key test test-not key
+ */
+{
+ return (LispListSet(builtin, UNION));
+}
+
+LispObj *
+Lisp_Nunion(LispBuiltin *builtin)
+/*
+ nunion list1 list2 &key test test-not key
+ */
+{
+ return (LispListSet(builtin, NUNION));
+}
+
+LispObj *
+Lisp_Unless(LispBuiltin *builtin)
+/*
+ unless test &rest body
+ */
+{
+ LispObj *result, *test, *body;
+
+ body = ARGUMENT(1);
+ test = ARGUMENT(0);
+
+ result = NIL;
+ test = EVAL(test);
+ RETURN_COUNT = 0;
+ if (test == NIL) {
+ for (; CONSP(body); body = CDR(body))
+ result = EVAL(CAR(body));
+ }
+
+ return (result);
+}
+
+/*
+ * ext::until
+ */
+LispObj *
+Lisp_Until(LispBuiltin *builtin)
+/*
+ until test &rest body
+ */
+{
+ LispObj *result, *test, *body, *prog;
+
+ body = ARGUMENT(1);
+ test = ARGUMENT(0);
+
+ result = NIL;
+ for (;;) {
+ if ((result = EVAL(test)) == NIL) {
+ for (prog = body; CONSP(prog); prog = CDR(prog))
+ (void)EVAL(CAR(prog));
+ }
+ else
+ break;
+ }
+
+ return (result);
+}
+
+LispObj *
+Lisp_UnwindProtect(LispBuiltin *builtin)
+/*
+ unwind-protect protect &rest cleanup
+ */
+{
+ LispObj *result, **presult = &result;
+ int did_jump, *pdid_jump = &did_jump, destroyed;
+ LispBlock *block;
+
+ LispObj *protect, *cleanup, **pcleanup = &cleanup;
+
+ cleanup = ARGUMENT(1);
+ protect = ARGUMENT(0);
+
+ /* run protected code */
+ *presult = NIL;
+ *pdid_jump = 1;
+ block = LispBeginBlock(NIL, LispBlockProtect);
+ if (setjmp(block->jmp) == 0) {
+ *presult = EVAL(protect);
+ *pdid_jump = 0;
+ }
+ LispEndBlock(block);
+ if (!lisp__data.destroyed && *pdid_jump)
+ *presult = lisp__data.block.block_ret;
+
+ destroyed = lisp__data.destroyed;
+ lisp__data.destroyed = 0;
+
+ /* run cleanup, unprotected code */
+ if (CONSP(*pcleanup))
+ for (; CONSP(cleanup); cleanup = CDR(cleanup))
+ (void)EVAL(CAR(cleanup));
+
+ if (destroyed) {
+ /* in case there is another unwind-protect */
+ LispBlockUnwind(NULL);
+ /* if not, just return to the toplevel */
+ lisp__data.destroyed = 1;
+ LispDestroy(".");
+ }
+
+ return (result);
+}
+
+static LispObj *
+LispValuesList(LispBuiltin *builtin, int check_list)
+{
+ long i, count;
+ LispObj *result;
+
+ LispObj *list;
+
+ list = ARGUMENT(0);
+
+ count = LispLength(list) - 1;
+
+ if (count >= 0) {
+ result = CAR(list);
+ if ((RETURN_CHECK(count)) != count)
+ LispDestroy("%s: too many values", STRFUN(builtin));
+ RETURN_COUNT = count;
+ for (i = 0, list = CDR(list); count && CONSP(list);
+ count--, i++, list = CDR(list))
+ RETURN(i) = CAR(list);
+ if (check_list) {
+ CHECK_LIST(list);
+ }
+ }
+ else {
+ RETURN_COUNT = -1;
+ result = NIL;
+ }
+
+ return (result);
+}
+
+LispObj *
+Lisp_Values(LispBuiltin *builtin)
+/*
+ values &rest objects
+ */
+{
+ return (LispValuesList(builtin, 0));
+}
+
+LispObj *
+Lisp_ValuesList(LispBuiltin *builtin)
+/*
+ values-list list
+ */
+{
+ return (LispValuesList(builtin, 1));
+}
+
+LispObj *
+Lisp_Vector(LispBuiltin *builtin)
+/*
+ vector &rest objects
+ */
+{
+ LispObj *objects;
+
+ objects = ARGUMENT(0);
+
+ return (VECTOR(objects));
+}
+
+LispObj *
+Lisp_When(LispBuiltin *builtin)
+/*
+ when test &rest body
+ */
+{
+ LispObj *result, *test, *body;
+
+ body = ARGUMENT(1);
+ test = ARGUMENT(0);
+
+ result = NIL;
+ test = EVAL(test);
+ RETURN_COUNT = 0;
+ if (test != NIL) {
+ for (; CONSP(body); body = CDR(body))
+ result = EVAL(CAR(body));
+ }
+
+ return (result);
+}
+
+/*
+ * ext::while
+ */
+LispObj *
+Lisp_While(LispBuiltin *builtin)
+/*
+ while test &rest body
+ */
+{
+ LispObj *result, *test, *body, *prog;
+
+ body = ARGUMENT(1);
+ test = ARGUMENT(0);
+
+ result = NIL;
+ for (;;) {
+ if (EVAL(test) != NIL) {
+ for (prog = body; CONSP(prog); prog = CDR(prog))
+ (void)EVAL(CAR(prog));
+ }
+ else
+ break;
+ }
+
+ return (NIL);
+}
+
+/*
+ * ext::unsetenv
+ */
+LispObj *
+Lisp_Unsetenv(LispBuiltin *builtin)
+/*
+ unsetenv name
+ */
+{
+ char *name;
+
+ LispObj *oname;
+
+ oname = ARGUMENT(0);
+
+ CHECK_STRING(oname);
+ name = THESTR(oname);
+
+ unsetenv(name);
+
+ return (NIL);
+}
+
+LispObj *
+Lisp_XeditEltStore(LispBuiltin *builtin)
+/*
+ lisp::elt-store sequence index value
+ */
+{
+ int length, offset;
+
+ LispObj *sequence, *oindex, *value;
+
+ value = ARGUMENT(2);
+ oindex = ARGUMENT(1);
+ sequence = ARGUMENT(0);
+
+ CHECK_INDEX(oindex);
+ offset = FIXNUM_VALUE(oindex);
+ length = LispLength(sequence);
+
+ if (offset >= length)
+ LispDestroy("%s: index %d too large for sequence length %d",
+ STRFUN(builtin), offset, length);
+
+ if (STRINGP(sequence)) {
+ int ch;
+
+ CHECK_STRING_WRITABLE(sequence);
+ CHECK_SCHAR(value);
+ ch = SCHAR_VALUE(value);
+ if (ch < 0 || ch > 255)
+ LispDestroy("%s: cannot represent character %d",
+ STRFUN(builtin), ch);
+ THESTR(sequence)[offset] = ch;
+ }
+ else {
+ if (ARRAYP(sequence))
+ sequence = sequence->data.array.list;
+
+ for (; offset > 0; offset--, sequence = CDR(sequence))
+ ;
+ RPLACA(sequence, value);
+ }
+
+ return (value);
+}
+
+LispObj *
+Lisp_XeditPut(LispBuiltin *builtin)
+/*
+ lisp::put symbol indicator value
+ */
+{
+ LispObj *symbol, *indicator, *value;
+
+ value = ARGUMENT(2);
+ indicator = ARGUMENT(1);
+ symbol = ARGUMENT(0);
+
+ CHECK_SYMBOL(symbol);
+
+ return (CAR(LispPutAtomProperty(symbol->data.atom, indicator, value)));
+}
+
+LispObj *
+Lisp_XeditSetSymbolPlist(LispBuiltin *builtin)
+/*
+ lisp::set-symbol-plist symbol list
+ */
+{
+ LispObj *symbol, *list;
+
+ list = ARGUMENT(1);
+ symbol = ARGUMENT(0);
+
+ CHECK_SYMBOL(symbol);
+
+ return (LispReplaceAtomPropertyList(symbol->data.atom, list));
+}
+
+LispObj *
+Lisp_XeditVectorStore(LispBuiltin *builtin)
+/*
+ lisp::vector-store array &rest values
+ */
+{
+ LispObj *value, *list, *object;
+ long rank, count, sequence, offset, accum;
+
+ LispObj *array, *values;
+
+ values = ARGUMENT(1);
+ array = ARGUMENT(0);
+
+ /* check for errors */
+ for (rank = 0, list = values;
+ CONSP(list) && CONSP(CDR(list));
+ list = CDR(list), rank++) {
+ CHECK_INDEX(CAR(values));
+ }
+
+ if (rank == 0)
+ LispDestroy("%s: too few subscripts", STRFUN(builtin));
+ value = CAR(list);
+
+ if (STRINGP(array) && rank == 1) {
+ long ch;
+ long length = STRLEN(array);
+ long offset = FIXNUM_VALUE(CAR(values));
+
+ CHECK_SCHAR(value);
+ CHECK_STRING_WRITABLE(array);
+ ch = SCHAR_VALUE(value);
+ if (offset >= length)
+ LispDestroy("%s: index %ld too large for sequence length %ld",
+ STRFUN(builtin), offset, length);
+
+ if (ch < 0 || ch > 255)
+ LispDestroy("%s: cannot represent character %ld",
+ STRFUN(builtin), ch);
+ THESTR(array)[offset] = ch;
+
+ return (value);
+ }
+
+ CHECK_ARRAY(array);
+ if (rank != array->data.array.rank)
+ LispDestroy("%s: too %s subscripts", STRFUN(builtin),
+ rank < array->data.array.rank ? "few" : "many");
+
+ for (list = values, object = array->data.array.dim;
+ CONSP(CDR(list));
+ list = CDR(list), object = CDR(object)) {
+ if (FIXNUM_VALUE(CAR(list)) >= FIXNUM_VALUE(CAR(object)))
+ LispDestroy("%s: %ld is out of range, index %ld",
+ STRFUN(builtin),
+ FIXNUM_VALUE(CAR(list)),
+ FIXNUM_VALUE(CAR(object)));
+ }
+
+ for (count = sequence = 0, list = values;
+ CONSP(CDR(list));
+ list = CDR(list), sequence++) {
+ for (offset = 0, object = array->data.array.dim;
+ offset < sequence; object = CDR(object), offset++)
+ ;
+ for (accum = 1, object = CDR(object); CONSP(object);
+ object = CDR(object))
+ accum *= FIXNUM_VALUE(CAR(object));
+ count += accum * FIXNUM_VALUE(CAR(list));
+ }
+
+ for (array = array->data.array.list; count > 0; array = CDR(array), count--)
+ ;
+
+ RPLACA(array, value);
+
+ return (value);
+}
+
+LispObj *
+Lisp_XeditDocumentationStore(LispBuiltin *builtin)
+/*
+ lisp::documentation-store symbol type string
+ */
+{
+ LispDocType_t doc_type;
+
+ LispObj *symbol, *type, *string;
+
+ string = ARGUMENT(2);
+ type = ARGUMENT(1);
+ symbol = ARGUMENT(0);
+
+ CHECK_SYMBOL(symbol);
+
+ /* type is checked in LispDocumentationType() */
+ doc_type = LispDocumentationType(builtin, type);
+
+ if (string == NIL)
+ /* allow explicitly releasing memory used for documentation */
+ LispRemDocumentation(symbol, doc_type);
+ else {
+ CHECK_STRING(string);
+ LispAddDocumentation(symbol, string, doc_type);
+ }
+
+ return (string);
+}