core.c revision f14f4646
1/*
2 * Copyright (c) 2001 by The XFree86 Project, Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17 * THE XFREE86 PROJECT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
18 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
19 * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 * SOFTWARE.
21 *
22 * Except as contained in this notice, the name of the XFree86 Project shall
23 * not be used in advertising or otherwise to promote the sale, use or other
24 * dealings in this Software without prior written authorization from the
25 * XFree86 Project.
26 *
27 * Author: Paulo César Pereira de Andrade
28 */
29
30/* $XFree86: xc/programs/xedit/lisp/core.c,v 1.71tsi Exp $ */
31
32#include "lisp/io.h"
33#include "lisp/core.h"
34#include "lisp/format.h"
35#include "lisp/helper.h"
36#include "lisp/package.h"
37#include "lisp/private.h"
38#include "lisp/write.h"
39
40/*
41 * Types
42 */
43typedef struct _SeqInfo {
44    LispType type;
45    union {
46	LispObj *list;
47	LispObj **vector;
48	unsigned char *string;
49    } data;
50} SeqInfo;
51
52#define SETSEQ(seq, object)						\
53    switch (seq.type = XOBJECT_TYPE(object)) {				\
54	case LispString_t:						\
55	    seq.data.string = (unsigned char*)THESTR(object);		\
56	    break;							\
57	case LispCons_t:						\
58	    seq.data.list = object;					\
59	    break;							\
60	default:							\
61	    seq.data.list = object->data.array.list;			\
62	    break;							\
63    }
64
65#ifdef __UNIXOS2__
66# define finite(x) isfinite(x)
67#endif
68
69#ifdef NEED_SETENV
70extern int setenv(const char *name, const char *value, int overwrite);
71extern void unsetenv(const char *name);
72#endif
73
74/*
75 * Prototypes
76 */
77#define NONE		0
78
79#define	REMOVE		1
80#define	SUBSTITUTE	2
81#define DELETE		3
82#define	NSUBSTITUTE	4
83
84#define ASSOC		1
85#define MEMBER		2
86
87#define FIND		1
88#define POSITION	2
89
90#define	IF		1
91#define	IFNOT		2
92
93#define UNION		1
94#define INTERSECTION	2
95#define SETDIFFERENCE	3
96#define SETEXCLUSIVEOR	4
97#define SUBSETP		5
98#define NSETDIFFERENCE	6
99#define NINTERSECTION	7
100#define NUNION		8
101#define NSETEXCLUSIVEOR	9
102
103#define COPY_LIST	1
104#define COPY_ALIST	2
105#define COPY_TREE	3
106
107#define EVERY		1
108#define SOME		2
109#define NOTEVERY	3
110#define NOTANY		4
111
112/* Call directly LispObjectCompare() if possible */
113#define FCODE(predicate)					\
114    predicate == Oeql ? FEQL :					\
115	predicate == Oequal ? FEQUAL :				\
116	    predicate == Oeq ? FEQ :				\
117		predicate == Oequalp ? FEQUALP : 0
118#define FCOMPARE(predicate, left, right, code)			\
119    code == FEQ ? left == right :				\
120	code ? LispObjectCompare(left, right, code) != NIL :	\
121	       APPLY2(predicate, left, right) != NIL
122
123#define FUNCTION_CHECK(predicate)				\
124    if (FUNCTIONP(predicate))					\
125	predicate = (predicate)->data.atom->object
126
127#define CHECK_TEST_0()						\
128    if (test != UNSPEC && test_not != UNSPEC)			\
129	LispDestroy("%s: specify either :TEST or :TEST-NOT",	\
130		    STRFUN(builtin))
131
132#define CHECK_TEST()						\
133    CHECK_TEST_0();						\
134    if (test_not == UNSPEC) {					\
135	if (test == UNSPEC)					\
136	    lambda = Oeql;					\
137	else							\
138	    lambda = test;					\
139	expect = 1;						\
140    }								\
141    else {							\
142	lambda = test_not;					\
143	expect = 0;						\
144    }								\
145    FUNCTION_CHECK(lambda);					\
146    code = FCODE(lambda)
147
148
149static LispObj *LispAdjoin(LispBuiltin*,
150			   LispObj*, LispObj*, LispObj*, LispObj*, LispObj*);
151static LispObj *LispAssocOrMember(LispBuiltin*, int, int);
152static LispObj *LispEverySomeAnyNot(LispBuiltin*, int);
153static LispObj *LispFindOrPosition(LispBuiltin*, int, int);
154static LispObj *LispDeleteOrRemoveDuplicates(LispBuiltin*, int);
155static LispObj *LispDeleteRemoveXSubstitute(LispBuiltin*, int, int);
156static LispObj *LispListSet(LispBuiltin*, int);
157static LispObj *LispMapc(LispBuiltin*, int);
158static LispObj *LispMapl(LispBuiltin*, int);
159static LispObj *LispMapnconc(LispObj*);
160extern LispObj *LispRunSetf(LispArgList*, LispObj*, LispObj*, LispObj*);
161extern LispObj *LispRunSetfMacro(LispAtom*, LispObj*, LispObj*);
162static LispObj *LispMergeSort(LispObj*, LispObj*, LispObj*, int);
163static LispObj *LispXReverse(LispBuiltin*, int);
164static LispObj *LispCopyList(LispBuiltin*, LispObj*, int);
165static LispObj *LispValuesList(LispBuiltin*, int);
166static LispObj *LispTreeEqual(LispObj*, LispObj*, LispObj*, int);
167static LispDocType_t LispDocumentationType(LispBuiltin*, LispObj*);
168
169extern void LispSetAtomObjectProperty(LispAtom*, LispObj*);
170
171/*
172 * Initialization
173 */
174LispObj *Oeq, *Oeql, *Oequal, *Oequalp, *Omake_array,
175	*Kinitial_contents, *Osetf, *Ootherwise, *Oquote;
176LispObj *Ogensym_counter;
177
178Atom_id Svariable, Sstructure, Stype, Ssetf;
179
180/*
181 * Implementation
182 */
183void
184LispCoreInit(void)
185{
186    Oeq			= STATIC_ATOM("EQ");
187    Oeql		= STATIC_ATOM("EQL");
188    Oequal		= STATIC_ATOM("EQUAL");
189    Oequalp		= STATIC_ATOM("EQUALP");
190    Omake_array		= STATIC_ATOM("MAKE-ARRAY");
191    Kinitial_contents	= KEYWORD("INITIAL-CONTENTS");
192    Osetf		= STATIC_ATOM("SETF");
193    Ootherwise		= STATIC_ATOM("OTHERWISE");
194    LispExportSymbol(Ootherwise);
195    Oquote		= STATIC_ATOM("QUOTE");
196    LispExportSymbol(Oquote);
197
198    Svariable		= GETATOMID("VARIABLE");
199    Sstructure		= GETATOMID("STRUCTURE");
200    Stype		= GETATOMID("TYPE");
201
202    /* Create as a constant so that only the C code should change the value */
203    Ogensym_counter	= STATIC_ATOM("*GENSYM-COUNTER*");
204    LispDefconstant(Ogensym_counter, FIXNUM(0), NIL);
205    LispExportSymbol(Ogensym_counter);
206
207    Ssetf	= ATOMID(Osetf);
208}
209
210LispObj *
211Lisp_Acons(LispBuiltin *builtin)
212/*
213 acons key datum alist
214 */
215{
216    LispObj *key, *datum, *alist;
217
218    alist = ARGUMENT(2);
219    datum = ARGUMENT(1);
220    key = ARGUMENT(0);
221
222    return (CONS(CONS(key, datum), alist));
223}
224
225static LispObj *
226LispAdjoin(LispBuiltin*builtin, LispObj *item, LispObj *list,
227	   LispObj *key, LispObj *test, LispObj *test_not)
228{
229    GC_ENTER();
230    int code, expect, value;
231    LispObj *lambda, *compare, *object;
232
233    CHECK_LIST(list);
234    CHECK_TEST();
235
236    if (key != UNSPEC) {
237	item = APPLY1(key, item);
238	/* Result is not guaranteed to be gc protected */
239	GC_PROTECT(item);
240    }
241
242    /* Check if item is not already in place */
243    for (object = list; CONSP(object); object = CDR(object)) {
244	compare = CAR(object);
245	if (key != UNSPEC) {
246	    compare = APPLY1(key, compare);
247	    GC_PROTECT(compare);
248	    value = FCOMPARE(lambda, item, compare, code);
249	    /* Unprotect compare... */
250	    --lisp__data.protect.length;
251	}
252	else
253	    value = FCOMPARE(lambda, item, compare, code);
254
255	if (value == expect) {
256	    /* Item is already in list */
257	    GC_LEAVE();
258
259	    return (list);
260	}
261    }
262    GC_LEAVE();
263
264    return (CONS(item, list));
265}
266
267LispObj *
268Lisp_Adjoin(LispBuiltin *builtin)
269/*
270 adjoin item list &key key test test-not
271 */
272{
273    LispObj *item, *list, *key, *test, *test_not;
274
275    test_not = ARGUMENT(4);
276    test = ARGUMENT(3);
277    key = ARGUMENT(2);
278    list = ARGUMENT(1);
279    item = ARGUMENT(0);
280
281    return (LispAdjoin(builtin, item, list, key, test, test_not));
282}
283
284LispObj *
285Lisp_Append(LispBuiltin *builtin)
286/*
287 append &rest lists
288 */
289{
290    GC_ENTER();
291    LispObj *result, *cons, *list;
292
293    LispObj *lists;
294
295    lists = ARGUMENT(0);
296
297    /* no arguments */
298    if (!CONSP(lists))
299	return (NIL);
300
301    /* skip initial nil lists */
302    for (; CONSP(CDR(lists)) && CAR(lists) == NIL; lists = CDR(lists))
303	;
304
305    /* last argument is not copied (even if it is the single argument) */
306    if (!CONSP(CDR(lists)))
307	return (CAR(lists));
308
309    /* make sure result is a list */
310    list = CAR(lists);
311    CHECK_CONS(list);
312    result = cons = CONS(CAR(list), NIL);
313    GC_PROTECT(result);
314    for (list = CDR(list); CONSP(list); list = CDR(list)) {
315	RPLACD(cons, CONS(CAR(list), NIL));
316	cons = CDR(cons);
317    }
318    lists = CDR(lists);
319
320    /* copy intermediate lists */
321    for (; CONSP(CDR(lists)); lists = CDR(lists)) {
322	list = CAR(lists);
323	if (list == NIL)
324	    continue;
325	/* intermediate elements must be lists */
326	CHECK_CONS(list);
327	for (; CONSP(list); list = CDR(list)) {
328	    RPLACD(cons, CONS(CAR(list), NIL));
329	    cons = CDR(cons);
330	}
331    }
332
333    /* add last element */
334    RPLACD(cons, CAR(lists));
335
336    GC_LEAVE();
337
338    return (result);
339}
340
341LispObj *
342Lisp_Aref(LispBuiltin *builtin)
343/*
344 aref array &rest subscripts
345 */
346{
347    long c, count, idx, seq;
348    LispObj *obj, *dim;
349
350    LispObj *array, *subscripts;
351
352    subscripts = ARGUMENT(1);
353    array = ARGUMENT(0);
354
355    /* accept strings also */
356    if (STRINGP(array) && CONSP(subscripts) && CDR(subscripts) == NIL) {
357	long offset, length = STRLEN(array);
358
359	CHECK_INDEX(CAR(subscripts));
360	offset = FIXNUM_VALUE(CAR(subscripts));
361
362	if (offset >= length)
363	    LispDestroy("%s: index %ld too large for sequence length %ld",
364			STRFUN(builtin), offset, length);
365
366	return (SCHAR(THESTR(array)[offset]));
367    }
368
369    CHECK_ARRAY(array);
370
371    for (count = 0, dim = subscripts, obj = array->data.array.dim; CONSP(dim);
372	 count++, dim = CDR(dim), obj = CDR(obj)) {
373	if (count >= array->data.array.rank)
374	    LispDestroy("%s: too many subscripts %s",
375			STRFUN(builtin), STROBJ(subscripts));
376	if (!INDEXP(CAR(dim)) ||
377	    FIXNUM_VALUE(CAR(dim)) >= FIXNUM_VALUE(CAR(obj)))
378	    LispDestroy("%s: %s is out of range or a bad index",
379			STRFUN(builtin), STROBJ(CAR(dim)));
380    }
381    if (count < array->data.array.rank)
382	LispDestroy("%s: too few subscripts %s",
383		    STRFUN(builtin), STROBJ(subscripts));
384
385    for (count = seq = 0, dim = subscripts; CONSP(dim); dim = CDR(dim), seq++) {
386	for (idx = 0, obj = array->data.array.dim; idx < seq;
387	     obj = CDR(obj), ++idx)
388	    ;
389	for (c = 1, obj = CDR(obj); obj != NIL; obj = CDR(obj))
390	    c *= FIXNUM_VALUE(CAR(obj));
391	count += c * FIXNUM_VALUE(CAR(dim));
392    }
393
394    for (array = array->data.array.list; count > 0; array = CDR(array), count--)
395	;
396
397    return (CAR(array));
398}
399
400static LispObj *
401LispAssocOrMember(LispBuiltin *builtin, int function, int comparison)
402/*
403 assoc item list &key test test-not key
404 assoc-if predicate list &key key
405 assoc-if-not predicate list &key key
406 member item list &key test test-not key
407 member-if predicate list &key key
408 member-if-not predicate list &key key
409 */
410{
411    int code = 0, expect, value;
412    LispObj *lambda, *result, *compare;
413
414    LispObj *item, *list, *test, *test_not, *key;
415
416    if (comparison == NONE) {
417	key = ARGUMENT(4);
418	test_not = ARGUMENT(3);
419	test = ARGUMENT(2);
420	list = ARGUMENT(1);
421	item = ARGUMENT(0);
422	lambda = NIL;
423    }
424    else {
425	key = ARGUMENT(2);
426	list = ARGUMENT(1);
427	lambda = ARGUMENT(0);
428	test = test_not = UNSPEC;
429	item = NIL;
430    }
431
432    if (list == NIL)
433	return (NIL);
434    CHECK_CONS(list);
435
436    /* Resolve compare function, and expected result of comparison */
437    if (comparison == NONE) {
438	CHECK_TEST();
439    }
440    else
441	expect = comparison == IFNOT ? 0 : 1;
442
443    result = NIL;
444    for (; CONSP(list); list = CDR(list)) {
445	compare = CAR(list);
446	if (function == ASSOC) {
447	    if (!CONSP(compare))
448		continue;
449	    compare = CAR(compare);
450	}
451	if (key != UNSPEC)
452	    compare = APPLY1(key, compare);
453
454	if (comparison == NONE)
455	    value = FCOMPARE(lambda, item, compare, code);
456	else
457	    value = APPLY1(lambda, compare) != NIL;
458	if (value == expect) {
459	    result = list;
460	    if (function == ASSOC)
461		result = CAR(result);
462	    break;
463	}
464    }
465    if (function == MEMBER) {
466	CHECK_LIST(list);
467    }
468
469    return (result);
470}
471
472LispObj *
473Lisp_Assoc(LispBuiltin *builtin)
474/*
475 assoc item list &key test test-not key
476 */
477{
478    return (LispAssocOrMember(builtin, ASSOC, NONE));
479}
480
481LispObj *
482Lisp_AssocIf(LispBuiltin *builtin)
483/*
484 assoc-if predicate list &key key
485 */
486{
487    return (LispAssocOrMember(builtin, ASSOC, IF));
488}
489
490LispObj *
491Lisp_AssocIfNot(LispBuiltin *builtin)
492/*
493 assoc-if-not predicate list &key key
494 */
495{
496    return (LispAssocOrMember(builtin, ASSOC, IFNOT));
497}
498
499LispObj *
500Lisp_And(LispBuiltin *builtin)
501/*
502 and &rest args
503 */
504{
505    LispObj *result = T, *args;
506
507    args = ARGUMENT(0);
508
509    for (; CONSP(args); args = CDR(args)) {
510	result = EVAL(CAR(args));
511	if (result == NIL)
512	    break;
513    }
514
515    return (result);
516}
517
518LispObj *
519Lisp_Apply(LispBuiltin *builtin)
520/*
521 apply function arg &rest more-args
522 */
523{
524    GC_ENTER();
525    LispObj *result, *arguments;
526
527    LispObj *function, *arg, *more_args;
528
529    more_args = ARGUMENT(2);
530    arg = ARGUMENT(1);
531    function = ARGUMENT(0);
532
533    if (more_args == NIL) {
534	CHECK_LIST(arg);
535	arguments = arg;
536	for (; CONSP(arg); arg = CDR(arg))
537	    ;
538	CHECK_LIST(arg);
539    }
540    else {
541	LispObj *cons;
542
543	CHECK_CONS(more_args);
544	arguments = cons = CONS(arg, NIL);
545	GC_PROTECT(arguments);
546	for (arg = CDR(more_args);
547	     CONSP(arg);
548	     more_args = arg, arg = CDR(arg)) {
549	    RPLACD(cons, CONS(CAR(more_args), NIL));
550	    cons = CDR(cons);
551	}
552	more_args = CAR(more_args);
553	if (more_args != NIL) {
554	    for (arg = more_args; CONSP(arg); arg = CDR(arg))
555		;
556	    CHECK_LIST(arg);
557	    RPLACD(cons, more_args);
558	}
559    }
560
561    result = APPLY(function, arguments);
562    GC_LEAVE();
563
564    return (result);
565}
566
567LispObj *
568Lisp_Atom(LispBuiltin *builtin)
569/*
570 atom object
571 */
572{
573    LispObj *object;
574
575    object = ARGUMENT(0);
576
577    return (CONSP(object) ? NIL : T);
578}
579
580LispObj *
581Lisp_Block(LispBuiltin *builtin)
582/*
583 block name &rest body
584 */
585{
586    int did_jump, *pdid_jump = &did_jump;
587    LispObj *res, **pres = &res;
588    LispBlock *block;
589
590    LispObj *name, *body;
591
592    body = ARGUMENT(1);
593    name = ARGUMENT(0);
594
595    if (!SYMBOLP(name) && name != NIL && name != T)
596	LispDestroy("%s: %s cannot name a block",
597		    STRFUN(builtin), STROBJ(name));
598
599    *pres = NIL;
600    *pdid_jump = 1;
601    block = LispBeginBlock(name, LispBlockTag);
602    if (setjmp(block->jmp) == 0) {
603	for (; CONSP(body); body = CDR(body))
604	    res = EVAL(CAR(body));
605	*pdid_jump = 0;
606    }
607    LispEndBlock(block);
608    if (*pdid_jump)
609	*pres = lisp__data.block.block_ret;
610
611    return (res);
612}
613
614LispObj *
615Lisp_Boundp(LispBuiltin *builtin)
616/*
617 boundp symbol
618 */
619{
620    LispAtom *atom;
621
622    LispObj *symbol = ARGUMENT(0);
623
624    CHECK_SYMBOL(symbol);
625
626    atom = symbol->data.atom;
627    if (atom->package == lisp__data.keyword ||
628	(atom->a_object && atom->property->value != UNBOUND))
629	return (T);
630
631    return (NIL);
632}
633
634LispObj *
635Lisp_Butlast(LispBuiltin *builtin)
636/*
637 butlast list &optional count
638 */
639{
640    GC_ENTER();
641    long length, count;
642    LispObj *result, *cons, *list, *ocount;
643
644    ocount = ARGUMENT(1);
645    list = ARGUMENT(0);
646
647    CHECK_LIST(list);
648    if (ocount == UNSPEC)
649	count = 1;
650    else {
651	CHECK_INDEX(ocount);
652	count = FIXNUM_VALUE(ocount);
653    }
654    length = LispLength(list);
655
656    if (count == 0)
657	return (list);
658    else if (count >= length)
659	return (NIL);
660
661    length -= count + 1;
662    result = cons = CONS(CAR(list), NIL);
663    GC_PROTECT(result);
664    for (list = CDR(list); length > 0; list = CDR(list), length--) {
665	RPLACD(cons, CONS(CAR(list), NIL));
666	cons = CDR(cons);
667    }
668    GC_LEAVE();
669
670    return (result);
671}
672
673LispObj *
674Lisp_Nbutlast(LispBuiltin *builtin)
675/*
676 nbutlast list &optional count
677 */
678{
679    long length, count;
680    LispObj *result, *list, *ocount;
681
682    ocount = ARGUMENT(1);
683    list = ARGUMENT(0);
684
685    CHECK_LIST(list);
686    if (ocount == UNSPEC)
687	count = 1;
688    else {
689	CHECK_INDEX(ocount);
690	count = FIXNUM_VALUE(ocount);
691    }
692    length = LispLength(list);
693
694    if (count == 0)
695	return (list);
696    else if (count >= length)
697	return (NIL);
698
699    length -= count + 1;
700    result = list;
701    for (; length > 0; list = CDR(list), length--)
702	;
703    RPLACD(list, NIL);
704
705    return (result);
706}
707
708LispObj *
709Lisp_Car(LispBuiltin *builtin)
710/*
711 car list
712 */
713{
714    LispObj *list, *result = NULL;
715
716    list = ARGUMENT(0);
717
718    if (list == NIL)
719	result = NIL;
720    else {
721	CHECK_CONS(list);
722	result = CAR(list);
723    }
724
725    return (result);
726}
727
728LispObj *
729Lisp_Case(LispBuiltin *builtin)
730/*
731 case keyform &rest body
732 */
733{
734    LispObj *result, *code, *keyform, *body, *form;
735
736    body = ARGUMENT(1);
737    keyform = ARGUMENT(0);
738
739    result = NIL;
740    keyform = EVAL(keyform);
741
742    for (; CONSP(body); body = CDR(body)) {
743	code = CAR(body);
744	CHECK_CONS(code);
745
746	form = CAR(code);
747	if (form == T || form == Ootherwise) {
748	    if (CONSP(CDR(body)))
749		LispDestroy("%s: %s must be the last clause",
750			    STRFUN(builtin), STROBJ(CAR(code)));
751	    result = CDR(code);
752	    break;
753	}
754	else if (CONSP(form)) {
755	    for (; CONSP(form); form = CDR(form))
756		if (XEQL(keyform, CAR(form)) == T) {
757		    result = CDR(code);
758		    break;
759		}
760	    if (CONSP(form))	/* if found match */
761		break;
762	}
763	else if (XEQL(keyform, form) == T) {
764	    result = CDR(code);
765	    break;
766	}
767    }
768
769    for (body = result; CONSP(body); body = CDR(body))
770	result = EVAL(CAR(body));
771
772    return (result);
773}
774
775LispObj *
776Lisp_Catch(LispBuiltin *builtin)
777/*
778 catch tag &rest body
779 */
780{
781    int did_jump, *pdid_jump = &did_jump;
782    LispObj *res, **pres = &res;
783    LispBlock *block;
784
785    LispObj *tag, *body;
786
787    body = ARGUMENT(1);
788    tag = ARGUMENT(0);
789
790    *pres = NIL;
791    *pdid_jump = 1;
792    block = LispBeginBlock(tag, LispBlockCatch);
793    if (setjmp(block->jmp) == 0) {
794	for (; CONSP(body); body = CDR(body))
795	    res = EVAL(CAR(body));
796	*pdid_jump = 0;
797    }
798    LispEndBlock(block);
799    if (*pdid_jump)
800	*pres = lisp__data.block.block_ret;
801
802    return (res);
803}
804
805LispObj *
806Lisp_Coerce(LispBuiltin *builtin)
807/*
808 coerce object result-type
809 */
810{
811    LispObj *object, *result_type;
812
813    result_type = ARGUMENT(1);
814    object = ARGUMENT(0);
815
816    return (LispCoerce(builtin, object, result_type));
817}
818
819LispObj *
820Lisp_Cdr(LispBuiltin *builtin)
821/*
822 cdr list
823 */
824{
825    LispObj *list, *result = NULL;
826
827    list = ARGUMENT(0);
828
829    if (list == NIL)
830	result = NIL;
831    else {
832	CHECK_CONS(list);
833	result = CDR(list);
834    }
835
836    return (result);
837}
838
839LispObj *
840Lisp_C_r(LispBuiltin *builtin)
841/*
842 c[ad]{2,4}r list
843 */
844{
845    char *desc;
846
847    LispObj *list, *result = NULL;
848
849    list = ARGUMENT(0);
850
851    result = list;
852    desc = STRFUN(builtin);
853    while (desc[1] != 'R')
854	++desc;
855    while (*desc != 'C') {
856	if (result == NIL)
857	    break;
858	CHECK_CONS(result);
859	result = *desc == 'A' ? CAR(result) : CDR(result);
860	--desc;
861    }
862
863    return (result);
864}
865
866LispObj *
867Lisp_Cond(LispBuiltin *builtin)
868/*
869 cond &rest body
870 */
871{
872    LispObj *result, *code, *body;
873
874    body = ARGUMENT(0);
875
876    result = NIL;
877    for (; CONSP(body); body = CDR(body)) {
878	code = CAR(body);
879
880	CHECK_CONS(code);
881	result = EVAL(CAR(code));
882	if (result == NIL)
883	    continue;
884	for (code = CDR(code); CONSP(code); code = CDR(code))
885	    result = EVAL(CAR(code));
886	break;
887    }
888
889    return (result);
890}
891
892static LispObj *
893LispCopyList(LispBuiltin *builtin, LispObj *list, int function)
894{
895    GC_ENTER();
896    LispObj *result, *cons;
897
898    if (list == NIL)
899	return (list);
900    CHECK_CONS(list);
901
902    result = cons = CONS(NIL, NIL);
903    GC_PROTECT(result);
904    if (CONSP(CAR(list))) {
905	switch (function) {
906	    case COPY_LIST:
907		RPLACA(result, CAR(list));
908		break;
909	    case COPY_ALIST:
910		RPLACA(result, CONS(CAR(CAR(list)), CDR(CAR(list))));
911		break;
912	    case COPY_TREE:
913		RPLACA(result, LispCopyList(builtin, CAR(list), COPY_TREE));
914		break;
915	}
916    }
917    else
918	RPLACA(result, CAR(list));
919
920    for (list = CDR(list); CONSP(list); list = CDR(list)) {
921	CDR(cons) = CONS(NIL, NIL);
922	cons = CDR(cons);
923	if (CONSP(CAR(list))) {
924	    switch (function) {
925		case COPY_LIST:
926		    RPLACA(cons, CAR(list));
927		    break;
928		case COPY_ALIST:
929		    RPLACA(cons, CONS(CAR(CAR(list)), CDR(CAR(list))));
930		    break;
931		case COPY_TREE:
932		    RPLACA(cons, LispCopyList(builtin, CAR(list), COPY_TREE));
933		    break;
934	    }
935	}
936	else
937	    RPLACA(cons, CAR(list));
938    }
939    /* in case list is dotted */
940    RPLACD(cons, list);
941    GC_LEAVE();
942
943    return (result);
944}
945
946LispObj *
947Lisp_CopyAlist(LispBuiltin *builtin)
948/*
949 copy-alist list
950 */
951{
952    LispObj *list;
953
954    list = ARGUMENT(0);
955
956    return (LispCopyList(builtin, list, COPY_ALIST));
957}
958
959LispObj *
960Lisp_CopyList(LispBuiltin *builtin)
961/*
962 copy-list list
963 */
964{
965    LispObj *list;
966
967    list = ARGUMENT(0);
968
969    return (LispCopyList(builtin, list, COPY_LIST));
970}
971
972LispObj *
973Lisp_CopyTree(LispBuiltin *builtin)
974/*
975 copy-tree list
976 */
977{
978    LispObj *list;
979
980    list = ARGUMENT(0);
981
982    return (LispCopyList(builtin, list, COPY_TREE));
983}
984
985LispObj *
986Lisp_Cons(LispBuiltin *builtin)
987/*
988 cons car cdr
989 */
990{
991    LispObj *car, *cdr;
992
993    cdr = ARGUMENT(1);
994    car = ARGUMENT(0);
995
996    return (CONS(car, cdr));
997}
998
999LispObj *
1000Lisp_Consp(LispBuiltin *builtin)
1001/*
1002 consp object
1003 */
1004{
1005    LispObj *object;
1006
1007    object = ARGUMENT(0);
1008
1009    return (CONSP(object) ? T : NIL);
1010}
1011
1012LispObj *
1013Lisp_Constantp(LispBuiltin *builtin)
1014/*
1015 constantp form &optional environment
1016 */
1017{
1018    LispObj *form;
1019
1020    form = ARGUMENT(0);
1021
1022    /* not all self-evaluating objects are considered constants */
1023    if (!POINTERP(form) ||
1024	NUMBERP(form) ||
1025	XQUOTEP(form) ||
1026	(XCONSP(form) && CAR(form) == Oquote) ||
1027	(XSYMBOLP(form) && form->data.atom->constant) ||
1028	XSTRINGP(form) ||
1029	XARRAYP(form))
1030	return (T);
1031
1032    return (NIL);
1033}
1034
1035LispObj *
1036Lisp_Defconstant(LispBuiltin *builtin)
1037/*
1038 defconstant name initial-value &optional documentation
1039 */
1040{
1041    LispObj *name, *initial_value, *documentation;
1042
1043    documentation = ARGUMENT(2);
1044    initial_value = ARGUMENT(1);
1045    name = ARGUMENT(0);
1046
1047    CHECK_SYMBOL(name);
1048    if (documentation != UNSPEC) {
1049	CHECK_STRING(documentation);
1050    }
1051    else
1052	documentation = NIL;
1053    LispDefconstant(name, EVAL(initial_value), documentation);
1054
1055    return (name);
1056}
1057
1058LispObj *
1059Lisp_Defmacro(LispBuiltin *builtin)
1060/*
1061 defmacro name lambda-list &rest body
1062 */
1063{
1064    LispArgList *alist;
1065
1066    LispObj *lambda, *name, *lambda_list, *body;
1067
1068    body = ARGUMENT(2);
1069    lambda_list = ARGUMENT(1);
1070    name = ARGUMENT(0);
1071
1072    CHECK_SYMBOL(name);
1073    alist = LispCheckArguments(LispMacro, lambda_list, ATOMID(name)->value, 0);
1074
1075    if (CONSP(body) && STRINGP(CAR(body))) {
1076	LispAddDocumentation(name, CAR(body), LispDocFunction);
1077	body = CDR(body);
1078    }
1079
1080    lambda_list = LispListProtectedArguments(alist);
1081    lambda = LispNewLambda(name, body, lambda_list, LispMacro);
1082
1083    if (name->data.atom->a_builtin || name->data.atom->a_compiled) {
1084	if (name->data.atom->a_builtin) {
1085	    ERROR_CHECK_SPECIAL_FORM(name->data.atom);
1086	}
1087	/* redefining these may cause surprises if bytecode
1088	 * compiled functions references them */
1089	LispWarning("%s: %s is being redefined", STRFUN(builtin),
1090		    ATOMID(name)->value);
1091
1092	LispRemAtomBuiltinProperty(name->data.atom);
1093    }
1094
1095    LispSetAtomFunctionProperty(name->data.atom, lambda, alist);
1096    LispUseArgList(alist);
1097
1098    return (name);
1099}
1100
1101LispObj *
1102Lisp_Defun(LispBuiltin *builtin)
1103/*
1104 defun name lambda-list &rest body
1105 */
1106{
1107    LispArgList *alist;
1108
1109    LispObj *lambda, *name, *lambda_list, *body;
1110
1111    body = ARGUMENT(2);
1112    lambda_list = ARGUMENT(1);
1113    name = ARGUMENT(0);
1114
1115    CHECK_SYMBOL(name);
1116    alist = LispCheckArguments(LispFunction, lambda_list, ATOMID(name)->value, 0);
1117
1118    if (CONSP(body) && STRINGP(CAR(body))) {
1119	LispAddDocumentation(name, CAR(body), LispDocFunction);
1120	body = CDR(body);
1121    }
1122
1123    lambda_list = LispListProtectedArguments(alist);
1124    lambda = LispNewLambda(name, body, lambda_list, LispFunction);
1125
1126    if (name->data.atom->a_builtin || name->data.atom->a_compiled) {
1127	if (name->data.atom->a_builtin) {
1128	    ERROR_CHECK_SPECIAL_FORM(name->data.atom);
1129	}
1130	/* redefining these may cause surprises if bytecode
1131	 * compiled functions references them */
1132	LispWarning("%s: %s is being redefined", STRFUN(builtin),
1133		    ATOMID(name)->value);
1134
1135	LispRemAtomBuiltinProperty(name->data.atom);
1136    }
1137    LispSetAtomFunctionProperty(name->data.atom, lambda, alist);
1138    LispUseArgList(alist);
1139
1140    return (name);
1141}
1142
1143LispObj *
1144Lisp_Defsetf(LispBuiltin *builtin)
1145/*
1146 defsetf function lambda-list &rest body
1147 */
1148{
1149    LispArgList *alist;
1150    LispObj *obj;
1151    LispObj *lambda, *function, *lambda_list, *store, *body;
1152
1153    body = ARGUMENT(2);
1154    lambda_list = ARGUMENT(1);
1155    function = ARGUMENT(0);
1156
1157    CHECK_SYMBOL(function);
1158
1159    if (body == NIL || (CONSP(body) && STRINGP(CAR(body)))) {
1160	if (!SYMBOLP(lambda_list))
1161	    LispDestroy("%s: syntax error %s %s",
1162			STRFUN(builtin), STROBJ(function), STROBJ(lambda_list));
1163	if (body != NIL)
1164	    LispAddDocumentation(function, CAR(body), LispDocSetf);
1165
1166	LispSetAtomSetfProperty(function->data.atom, lambda_list, NULL);
1167
1168	return (function);
1169    }
1170
1171    alist = LispCheckArguments(LispSetf, lambda_list, ATOMID(function)->value, 0);
1172
1173    store = CAR(body);
1174    if (!CONSP(store))
1175	LispDestroy("%s: %s is a bad store value",
1176		    STRFUN(builtin), STROBJ(store));
1177    for (obj = store; CONSP(obj); obj = CDR(obj)) {
1178	CHECK_SYMBOL(CAR(obj));
1179    }
1180
1181    body = CDR(body);
1182    if (CONSP(body) && STRINGP(CAR(body))) {
1183	LispAddDocumentation(function, CAR(body), LispDocSetf);
1184	body = CDR(body);
1185    }
1186
1187    lambda = LispNewLambda(function, body, store, LispSetf);
1188    LispSetAtomSetfProperty(function->data.atom, lambda, alist);
1189    LispUseArgList(alist);
1190
1191    return (function);
1192}
1193
1194LispObj *
1195Lisp_Defparameter(LispBuiltin *builtin)
1196/*
1197 defparameter name initial-value &optional documentation
1198 */
1199{
1200    LispObj *name, *initial_value, *documentation;
1201
1202    documentation = ARGUMENT(2);
1203    initial_value = ARGUMENT(1);
1204    name = ARGUMENT(0);
1205
1206    CHECK_SYMBOL(name);
1207    if (documentation != UNSPEC) {
1208	CHECK_STRING(documentation);
1209    }
1210    else
1211	documentation = NIL;
1212
1213    LispProclaimSpecial(name, EVAL(initial_value), documentation);
1214
1215    return (name);
1216}
1217
1218LispObj *
1219Lisp_Defvar(LispBuiltin *builtin)
1220/*
1221 defvar name &optional initial-value documentation
1222 */
1223{
1224    LispObj *name, *initial_value, *documentation;
1225
1226    documentation = ARGUMENT(2);
1227    initial_value = ARGUMENT(1);
1228    name = ARGUMENT(0);
1229
1230    CHECK_SYMBOL(name);
1231    if (documentation != UNSPEC) {
1232	CHECK_STRING(documentation);
1233    }
1234    else
1235	documentation = NIL;
1236
1237    LispProclaimSpecial(name,
1238			initial_value != UNSPEC ? EVAL(initial_value) : NULL,
1239			documentation);
1240
1241    return (name);
1242}
1243
1244LispObj *
1245Lisp_Delete(LispBuiltin *builtin)
1246/*
1247 delete item sequence &key from-end test test-not start end count key
1248 */
1249{
1250    return (LispDeleteRemoveXSubstitute(builtin, DELETE, NONE));
1251}
1252
1253LispObj *
1254Lisp_DeleteIf(LispBuiltin *builtin)
1255/*
1256 delete-if predicate sequence &key from-end start end count key
1257 */
1258{
1259    return (LispDeleteRemoveXSubstitute(builtin, DELETE, IF));
1260}
1261
1262LispObj *
1263Lisp_DeleteIfNot(LispBuiltin *builtin)
1264/*
1265 delete-if-not predicate sequence &key from-end start end count key
1266 */
1267{
1268    return (LispDeleteRemoveXSubstitute(builtin, DELETE, IFNOT));
1269}
1270
1271LispObj *
1272Lisp_DeleteDuplicates(LispBuiltin *builtin)
1273/*
1274 delete-duplicates sequence &key from-end test test-not start end key
1275 */
1276{
1277    return (LispDeleteOrRemoveDuplicates(builtin, DELETE));
1278}
1279
1280LispObj *
1281Lisp_Do(LispBuiltin *builtin)
1282/*
1283 do init test &rest body
1284 */
1285{
1286    return (LispDo(builtin, 0));
1287}
1288
1289LispObj *
1290Lisp_DoP(LispBuiltin *builtin)
1291/*
1292 do* init test &rest body
1293 */
1294{
1295    return (LispDo(builtin, 1));
1296}
1297
1298static LispDocType_t
1299LispDocumentationType(LispBuiltin *builtin, LispObj *type)
1300{
1301    Atom_id atom;
1302    LispDocType_t doc_type = LispDocVariable;
1303
1304    CHECK_SYMBOL(type);
1305    atom = ATOMID(type);
1306
1307    if (atom == Svariable)
1308	doc_type = LispDocVariable;
1309    else if (atom == Sfunction)
1310	doc_type = LispDocFunction;
1311    else if (atom == Sstructure)
1312	doc_type = LispDocStructure;
1313    else if (atom == Stype)
1314	doc_type = LispDocType;
1315    else if (atom == Ssetf)
1316	doc_type = LispDocSetf;
1317    else {
1318	LispDestroy("%s: unknown documentation type %s",
1319		    STRFUN(builtin), STROBJ(type));
1320	/*NOTREACHED*/
1321    }
1322
1323    return (doc_type);
1324}
1325
1326LispObj *
1327Lisp_Documentation(LispBuiltin *builtin)
1328/*
1329 documentation symbol type
1330 */
1331{
1332    LispObj *symbol, *type;
1333
1334    type = ARGUMENT(1);
1335    symbol = ARGUMENT(0);
1336
1337    CHECK_SYMBOL(symbol);
1338    /* type is checked in LispDocumentationType() */
1339
1340    return (LispGetDocumentation(symbol, LispDocumentationType(builtin, type)));
1341}
1342
1343LispObj *
1344Lisp_DoList(LispBuiltin *builtin)
1345{
1346    return (LispDoListTimes(builtin, 0));
1347}
1348
1349LispObj *
1350Lisp_DoTimes(LispBuiltin *builtin)
1351{
1352    return (LispDoListTimes(builtin, 1));
1353}
1354
1355LispObj *
1356Lisp_Elt(LispBuiltin *builtin)
1357/*
1358 elt sequence index
1359 svref sequence index
1360 */
1361{
1362    long offset, length;
1363    LispObj *result, *sequence, *oindex;
1364
1365    oindex = ARGUMENT(1);
1366    sequence = ARGUMENT(0);
1367
1368    length = LispLength(sequence);
1369
1370    CHECK_INDEX(oindex);
1371    offset = FIXNUM_VALUE(oindex);
1372
1373    if (offset >= length)
1374	LispDestroy("%s: index %ld too large for sequence length %ld",
1375		    STRFUN(builtin), offset, length);
1376
1377    if (STRINGP(sequence))
1378	result = SCHAR(THESTR(sequence)[offset]);
1379    else {
1380	if (ARRAYP(sequence))
1381	    sequence = sequence->data.array.list;
1382
1383	for (; offset > 0; offset--, sequence = CDR(sequence))
1384	    ;
1385	result = CAR(sequence);
1386    }
1387
1388    return (result);
1389}
1390
1391LispObj *
1392Lisp_Endp(LispBuiltin *builtin)
1393/*
1394 endp object
1395 */
1396{
1397    LispObj *object;
1398
1399    object = ARGUMENT(0);
1400
1401    if (object == NIL)
1402	return (T);
1403    CHECK_CONS(object);
1404
1405    return (NIL);
1406}
1407
1408LispObj *
1409Lisp_Eq(LispBuiltin *builtin)
1410/*
1411 eq left right
1412 */
1413{
1414    LispObj *left, *right;
1415
1416    right = ARGUMENT(1);
1417    left = ARGUMENT(0);
1418
1419    return (XEQ(left, right));
1420}
1421
1422LispObj *
1423Lisp_Eql(LispBuiltin *builtin)
1424/*
1425 eql left right
1426 */
1427{
1428    LispObj *left, *right;
1429
1430    right = ARGUMENT(1);
1431    left = ARGUMENT(0);
1432
1433    return (XEQL(left, right));
1434}
1435
1436LispObj *
1437Lisp_Equal(LispBuiltin *builtin)
1438/*
1439 equal left right
1440 */
1441{
1442    LispObj *left, *right;
1443
1444    right = ARGUMENT(1);
1445    left = ARGUMENT(0);
1446
1447    return (XEQUAL(left, right));
1448}
1449
1450LispObj *
1451Lisp_Equalp(LispBuiltin *builtin)
1452/*
1453 equalp left right
1454 */
1455{
1456    LispObj *left, *right;
1457
1458    right = ARGUMENT(1);
1459    left = ARGUMENT(0);
1460
1461    return (XEQUALP(left, right));
1462}
1463
1464LispObj *
1465Lisp_Error(LispBuiltin *builtin)
1466/*
1467 error control-string &rest arguments
1468 */
1469{
1470    LispObj *string, *arglist;
1471
1472    LispObj *control_string, *arguments;
1473
1474    arguments = ARGUMENT(1);
1475    control_string = ARGUMENT(0);
1476
1477    arglist = CONS(NIL, CONS(control_string, arguments));
1478    GC_PROTECT(arglist);
1479    string = APPLY(Oformat, arglist);
1480    LispDestroy("%s", THESTR(string));
1481    /*NOTREACHED*/
1482
1483    /* No need to call GC_ENTER() and GC_LEAVE() macros */
1484    return (NIL);
1485}
1486
1487LispObj *
1488Lisp_Eval(LispBuiltin *builtin)
1489/*
1490 eval form
1491 */
1492{
1493    int lex;
1494    LispObj *form, *result;
1495
1496    form = ARGUMENT(0);
1497
1498    /* make sure eval form will not access local variables */
1499    lex = lisp__data.env.lex;
1500    lisp__data.env.lex = lisp__data.env.length;
1501    result = EVAL(form);
1502    lisp__data.env.lex = lex;
1503
1504    return (result);
1505}
1506
1507static LispObj *
1508LispEverySomeAnyNot(LispBuiltin *builtin, int function)
1509/*
1510 every predicate sequence &rest more-sequences
1511 some predicate sequence &rest more-sequences
1512 notevery predicate sequence &rest more-sequences
1513 notany predicate sequence &rest more-sequences
1514 */
1515{
1516    GC_ENTER();
1517    long i, j, length, count;
1518    LispObj *result, *list, *item, *arguments, *acons, *value;
1519    SeqInfo stk[8], *seqs;
1520
1521    LispObj *predicate, *sequence, *more_sequences;
1522
1523    more_sequences = ARGUMENT(2);
1524    sequence = ARGUMENT(1);
1525    predicate = ARGUMENT(0);
1526
1527    count = 1;
1528    length = LispLength(sequence);
1529    for (list = more_sequences; CONSP(list); list = CDR(list), count++) {
1530	i = LispLength(CAR(list));
1531	if (i < length)
1532	    length = i;
1533    }
1534
1535    result = function == EVERY || function == NOTANY ? T : NIL;
1536
1537    /* if at least one sequence has length zero */
1538    if (length == 0)
1539	return (result);
1540
1541    if (count > sizeof(stk) / sizeof(stk[0]))
1542	seqs = LispMalloc(count * sizeof(SeqInfo));
1543    else
1544	seqs = &stk[0];
1545
1546    /* build information about sequences */
1547    SETSEQ(seqs[0], sequence);
1548    for (i = 1, list = more_sequences; CONSP(list); list = CDR(list), i++) {
1549	item = CAR(list);
1550	SETSEQ(seqs[i], item);
1551    }
1552
1553    /* prepare argument list */
1554    arguments = acons = CONS(NIL, NIL);
1555    GC_PROTECT(arguments);
1556    for (i = 1; i < count; i++) {
1557	RPLACD(acons, CONS(NIL, NIL));
1558	acons = CDR(acons);
1559    }
1560
1561    /* loop applying predicate in sequence elements */
1562    for (i = 0; i < length; i++) {
1563
1564	/* build argument list */
1565	for (acons = arguments, j = 0; j < count; acons = CDR(acons), j++) {
1566	    if (seqs[j].type == LispString_t)
1567		item = SCHAR(*seqs[j].data.string++);
1568	    else {
1569		item = CAR(seqs[j].data.list);
1570		seqs[j].data.list = CDR(seqs[j].data.list);
1571	    }
1572	    RPLACA(acons, item);
1573	}
1574
1575	/* apply predicate */
1576	value = APPLY(predicate, arguments);
1577
1578	/* check if needs to terminate loop */
1579	if (value == NIL) {
1580	    if (function == EVERY) {
1581		result = NIL;
1582		break;
1583	    }
1584	    if (function == NOTEVERY) {
1585		result = T;
1586		break;
1587	    }
1588	}
1589	else {
1590	    if (function == SOME) {
1591		result = value;
1592		break;
1593	    }
1594	    if (function == NOTANY) {
1595		result = NIL;
1596		break;
1597	    }
1598	}
1599    }
1600
1601    GC_LEAVE();
1602    if (seqs != &stk[0])
1603	LispFree(seqs);
1604
1605    return (result);
1606}
1607
1608LispObj *
1609Lisp_Every(LispBuiltin *builtin)
1610/*
1611 every predicate sequence &rest more-sequences
1612 */
1613{
1614    return (LispEverySomeAnyNot(builtin, EVERY));
1615}
1616
1617LispObj *
1618Lisp_Some(LispBuiltin *builtin)
1619/*
1620 some predicate sequence &rest more-sequences
1621 */
1622{
1623    return (LispEverySomeAnyNot(builtin, SOME));
1624}
1625
1626LispObj *
1627Lisp_Notevery(LispBuiltin *builtin)
1628/*
1629 notevery predicate sequence &rest more-sequences
1630 */
1631{
1632    return (LispEverySomeAnyNot(builtin, NOTEVERY));
1633}
1634
1635LispObj *
1636Lisp_Notany(LispBuiltin *builtin)
1637/*
1638 notany predicate sequence &rest more-sequences
1639 */
1640{
1641    return (LispEverySomeAnyNot(builtin, NOTANY));
1642}
1643
1644LispObj *
1645Lisp_Fboundp(LispBuiltin *builtin)
1646/*
1647 fboundp symbol
1648 */
1649{
1650    LispAtom *atom;
1651
1652    LispObj *symbol = ARGUMENT(0);
1653
1654    CHECK_SYMBOL(symbol);
1655
1656    atom = symbol->data.atom;
1657    if (atom->a_function || atom->a_builtin || atom->a_compiled)
1658	return (T);
1659
1660    return (NIL);
1661}
1662
1663LispObj *
1664Lisp_Find(LispBuiltin *builtin)
1665/*
1666 find item sequence &key from-end test test-not start end key
1667 */
1668{
1669    return (LispFindOrPosition(builtin, FIND, NONE));
1670}
1671
1672LispObj *
1673Lisp_FindIf(LispBuiltin *builtin)
1674/*
1675 find-if predicate sequence &key from-end start end key
1676 */
1677{
1678    return (LispFindOrPosition(builtin, FIND, IF));
1679}
1680
1681LispObj *
1682Lisp_FindIfNot(LispBuiltin *builtin)
1683/*
1684 find-if-not predicate sequence &key from-end start end key
1685 */
1686{
1687    return (LispFindOrPosition(builtin, FIND, IFNOT));
1688}
1689
1690LispObj *
1691Lisp_Fill(LispBuiltin *builtin)
1692/*
1693 fill sequence item &key start end
1694 */
1695{
1696    long i, start, end, length;
1697
1698    LispObj *sequence, *item, *ostart, *oend;
1699
1700    oend = ARGUMENT(3);
1701    ostart = ARGUMENT(2);
1702    item = ARGUMENT(1);
1703    sequence = ARGUMENT(0);
1704
1705    LispCheckSequenceStartEnd(builtin, sequence, ostart, oend,
1706			      &start, &end, &length);
1707
1708    if (STRINGP(sequence)) {
1709	int ch;
1710	char *string = THESTR(sequence);
1711
1712	CHECK_STRING_WRITABLE(sequence);
1713	CHECK_SCHAR(item);
1714	ch = SCHAR_VALUE(item);
1715	for (i = start; i < end; i++)
1716	    string[i] = ch;
1717    }
1718    else {
1719	LispObj *list;
1720
1721	if (CONSP(sequence))
1722	    list = sequence;
1723	else
1724	    list = sequence->data.array.list;
1725
1726	for (i = 0; i < start; i++, list = CDR(list))
1727	    ;
1728	for (; i < end; i++, list = CDR(list))
1729	    RPLACA(list, item);
1730    }
1731
1732    return (sequence);
1733}
1734
1735LispObj *
1736Lisp_Fmakunbound(LispBuiltin *builtin)
1737/*
1738 fmkaunbound symbol
1739 */
1740{
1741    LispObj *symbol;
1742
1743    symbol = ARGUMENT(0);
1744
1745    CHECK_SYMBOL(symbol);
1746    if (symbol->data.atom->a_function)
1747	LispRemAtomFunctionProperty(symbol->data.atom);
1748    else if (symbol->data.atom->a_builtin)
1749	LispRemAtomBuiltinProperty(symbol->data.atom);
1750    else if (symbol->data.atom->a_compiled)
1751	LispRemAtomCompiledProperty(symbol->data.atom);
1752
1753    return (symbol);
1754}
1755
1756LispObj *
1757Lisp_Funcall(LispBuiltin *builtin)
1758/*
1759 funcall function &rest arguments
1760 */
1761{
1762    LispObj *result;
1763
1764    LispObj *function, *arguments;
1765
1766    arguments = ARGUMENT(1);
1767    function = ARGUMENT(0);
1768
1769    result = APPLY(function, arguments);
1770
1771    return (result);
1772}
1773
1774LispObj *
1775Lisp_Functionp(LispBuiltin *builtin)
1776/*
1777 functionp object
1778 */
1779{
1780    LispObj *object;
1781
1782    object = ARGUMENT(0);
1783
1784    return (FUNCTIONP(object) || LAMBDAP(object) ? T : NIL);
1785}
1786
1787LispObj *
1788Lisp_Get(LispBuiltin *builtin)
1789/*
1790 get symbol indicator &optional default
1791 */
1792{
1793    LispObj *result;
1794
1795    LispObj *symbol, *indicator, *defalt;
1796
1797    defalt = ARGUMENT(2);
1798    indicator = ARGUMENT(1);
1799    symbol = ARGUMENT(0);
1800
1801    CHECK_SYMBOL(symbol);
1802
1803    result = LispGetAtomProperty(symbol->data.atom, indicator);
1804
1805    if (result != NIL)
1806	result = CAR(result);
1807    else
1808	result = defalt == UNSPEC ? NIL : defalt;
1809
1810    return (result);
1811}
1812
1813/*
1814 * ext::getenv
1815 */
1816LispObj *
1817Lisp_Getenv(LispBuiltin *builtin)
1818/*
1819 getenv name
1820 */
1821{
1822    char *value;
1823
1824    LispObj *name;
1825
1826    name = ARGUMENT(0);
1827
1828    CHECK_STRING(name);
1829    value = getenv(THESTR(name));
1830
1831    return (value ? STRING(value) : NIL);
1832}
1833
1834LispObj *
1835Lisp_Gc(LispBuiltin *builtin)
1836/*
1837 gc &optional car cdr
1838 */
1839{
1840    LispObj *car, *cdr;
1841
1842    cdr = ARGUMENT(1);
1843    car = ARGUMENT(0);
1844
1845    LispGC(car, cdr);
1846
1847    return (NIL);
1848}
1849
1850LispObj *
1851Lisp_Gensym(LispBuiltin *builtin)
1852/*
1853 gensym &optional arg
1854 */
1855{
1856    char *preffix = "G", name[132];
1857    long counter = LONGINT_VALUE(Ogensym_counter->data.atom->property->value);
1858    LispObj *symbol;
1859
1860    LispObj *arg;
1861
1862    arg = ARGUMENT(0);
1863    if (arg != UNSPEC) {
1864	if (STRINGP(arg))
1865	    preffix = THESTR(arg);
1866	else {
1867	    CHECK_INDEX(arg);
1868	    counter = FIXNUM_VALUE(arg);
1869	}
1870    }
1871    snprintf(name, sizeof(name), "%s%ld", preffix, counter);
1872    if (strlen(name) >= 128)
1873	LispDestroy("%s: name %s too long", STRFUN(builtin), name);
1874    Ogensym_counter->data.atom->property->value = INTEGER(counter + 1);
1875
1876    symbol = UNINTERNED_ATOM(name);
1877    symbol->data.atom->unreadable = !LispCheckAtomString(name);
1878
1879    return (symbol);
1880}
1881
1882LispObj *
1883Lisp_Go(LispBuiltin *builtin)
1884/*
1885 go tag
1886 */
1887{
1888    unsigned blevel = lisp__data.block.block_level;
1889
1890    LispObj *tag;
1891
1892    tag = ARGUMENT(0);
1893
1894    while (blevel) {
1895	LispBlock *block = lisp__data.block.block[--blevel];
1896
1897	if (block->type == LispBlockClosure)
1898	    /* if reached a function call */
1899	    break;
1900	if (block->type == LispBlockBody) {
1901	    lisp__data.block.block_ret = tag;
1902	    LispBlockUnwind(block);
1903	    BLOCKJUMP(block);
1904	}
1905     }
1906
1907    LispDestroy("%s: no visible tagbody for %s",
1908		STRFUN(builtin), STROBJ(tag));
1909    /*NOTREACHED*/
1910    return (NIL);
1911}
1912
1913LispObj *
1914Lisp_If(LispBuiltin *builtin)
1915/*
1916 if test then &optional else
1917 */
1918{
1919    LispObj *result, *test, *then, *oelse;
1920
1921    oelse = ARGUMENT(2);
1922    then = ARGUMENT(1);
1923    test = ARGUMENT(0);
1924
1925    test = EVAL(test);
1926    if (test != NIL)
1927	result = EVAL(then);
1928    else if (oelse != UNSPEC)
1929	result = EVAL(oelse);
1930    else
1931	result = NIL;
1932
1933    return (result);
1934}
1935
1936LispObj *
1937Lisp_IgnoreErrors(LispBuiltin *builtin)
1938/*
1939 ignore-erros &rest body
1940 */
1941{
1942    LispObj *result;
1943    int i, jumped;
1944    LispBlock *block;
1945
1946    /* interpreter state */
1947    GC_ENTER();
1948    int stack, lex, length;
1949
1950    /* memory allocation */
1951    int mem_level;
1952    void **mem;
1953
1954    LispObj *body;
1955
1956    body = ARGUMENT(0);
1957
1958    /* Save environment information */
1959    stack = lisp__data.stack.length;
1960    lex = lisp__data.env.lex;
1961    length = lisp__data.env.length;
1962
1963    /* Save memory allocation information */
1964    mem_level = lisp__data.mem.level;
1965    mem = LispMalloc(mem_level * sizeof(void*));
1966    memcpy(mem, lisp__data.mem.mem, mem_level * sizeof(void*));
1967
1968    ++lisp__data.ignore_errors;
1969    result = NIL;
1970    jumped = 1;
1971    block = LispBeginBlock(NIL, LispBlockProtect);
1972    if (setjmp(block->jmp) == 0) {
1973	for (; CONSP(body); body = CDR(body))
1974	    result = EVAL(CAR(body));
1975	jumped = 0;
1976    }
1977    LispEndBlock(block);
1978    if (!lisp__data.destroyed && jumped)
1979	result = lisp__data.block.block_ret;
1980
1981    if (lisp__data.destroyed) {
1982	/* Restore environment */
1983	lisp__data.stack.length = stack;
1984	lisp__data.env.lex = lex;
1985	lisp__data.env.head = lisp__data.env.length = length;
1986	GC_LEAVE();
1987
1988	/* Check for possible leaks due to ignoring errors */
1989	for (i = 0; i < mem_level; i++) {
1990	    if (lisp__data.mem.mem[i] && mem[i] != lisp__data.mem.mem[i])
1991		LispFree(lisp__data.mem.mem[i]);
1992	}
1993	for (; i < lisp__data.mem.level; i++) {
1994	    if (lisp__data.mem.mem[i])
1995		LispFree(lisp__data.mem.mem[i]);
1996	}
1997
1998	lisp__data.destroyed = 0;
1999	result = NIL;
2000	RETURN_COUNT = 1;
2001	RETURN(0) = lisp__data.error_condition;
2002    }
2003    LispFree(mem);
2004    --lisp__data.ignore_errors;
2005
2006    return (result);
2007}
2008
2009LispObj *
2010Lisp_Intersection(LispBuiltin *builtin)
2011/*
2012 intersection list1 list2 &key test test-not key
2013 */
2014{
2015    return (LispListSet(builtin, INTERSECTION));
2016}
2017
2018LispObj *
2019Lisp_Nintersection(LispBuiltin *builtin)
2020/*
2021 nintersection list1 list2 &key test test-not key
2022 */
2023{
2024    return (LispListSet(builtin, NINTERSECTION));
2025}
2026
2027LispObj *
2028Lisp_Keywordp(LispBuiltin *builtin)
2029/*
2030 keywordp object
2031 */
2032{
2033    LispObj *object;
2034
2035    object = ARGUMENT(0);
2036
2037    return (KEYWORDP(object) ? T : NIL);
2038}
2039
2040LispObj *
2041Lisp_Lambda(LispBuiltin *builtin)
2042/*
2043 lambda lambda-list &rest body
2044 */
2045{
2046    GC_ENTER();
2047    LispObj *name;
2048    LispArgList *alist;
2049
2050    LispObj *lambda, *lambda_list, *body;
2051
2052    body = ARGUMENT(1);
2053    lambda_list = ARGUMENT(0);
2054
2055    alist = LispCheckArguments(LispLambda, lambda_list, Snil->value, 0);
2056
2057    name = OPAQUE(alist, LispArgList_t);
2058    lambda_list = LispListProtectedArguments(alist);
2059    GC_PROTECT(name);
2060    GC_PROTECT(lambda_list);
2061    lambda = LispNewLambda(name, body, lambda_list, LispLambda);
2062    LispUseArgList(alist);
2063    GC_LEAVE();
2064
2065    return (lambda);
2066}
2067
2068LispObj *
2069Lisp_Last(LispBuiltin *builtin)
2070/*
2071 last list &optional count
2072 */
2073{
2074    long count, length;
2075    LispObj *list, *ocount;
2076
2077    ocount = ARGUMENT(1);
2078    list = ARGUMENT(0);
2079
2080    if (!CONSP(list))
2081	return (list);
2082
2083    length = LispLength(list);
2084
2085    if (ocount == UNSPEC)
2086	count = 1;
2087    else {
2088	CHECK_INDEX(ocount);
2089	count = FIXNUM_VALUE(ocount);
2090    }
2091
2092    if (count >= length)
2093	return (list);
2094
2095    length -= count;
2096    for (; length > 0; length--)
2097	list = CDR(list);
2098
2099    return (list);
2100}
2101
2102LispObj *
2103Lisp_Length(LispBuiltin *builtin)
2104/*
2105 length sequence
2106 */
2107{
2108    LispObj *sequence;
2109
2110    sequence = ARGUMENT(0);
2111
2112    return (FIXNUM(LispLength(sequence)));
2113}
2114
2115LispObj *
2116Lisp_Let(LispBuiltin *builtin)
2117/*
2118 let init &rest body
2119 */
2120{
2121    GC_ENTER();
2122    int head = lisp__data.env.length;
2123    LispObj *init, *body, *pair, *result, *list, *cons = NIL;
2124
2125    body = ARGUMENT(1);
2126    init = ARGUMENT(0);
2127
2128    CHECK_LIST(init);
2129    for (list = NIL; CONSP(init); init = CDR(init)) {
2130	LispObj *symbol, *value;
2131
2132	pair = CAR(init);
2133	if (SYMBOLP(pair)) {
2134	    symbol = pair;
2135	    value = NIL;
2136	}
2137	else {
2138	    CHECK_CONS(pair);
2139	    symbol = CAR(pair);
2140	    CHECK_SYMBOL(symbol);
2141	    pair = CDR(pair);
2142	    if (CONSP(pair)) {
2143		value = CAR(pair);
2144		if (CDR(pair) != NIL)
2145		    LispDestroy("%s: too much arguments to initialize %s",
2146				STRFUN(builtin), STROBJ(symbol));
2147		value = EVAL(value);
2148	    }
2149	    else
2150		value = NIL;
2151	}
2152	pair = CONS(symbol, value);
2153	if (list == NIL) {
2154	    list = cons = CONS(pair, NIL);
2155	    GC_PROTECT(list);
2156	}
2157	else {
2158	    RPLACD(cons, CONS(pair, NIL));
2159	    cons = CDR(cons);
2160	}
2161    }
2162    /* Add variables */
2163    for (; CONSP(list); list = CDR(list)) {
2164	pair = CAR(list);
2165	CHECK_CONSTANT(CAR(pair));
2166	LispAddVar(CAR(pair), CDR(pair));
2167	++lisp__data.env.head;
2168    }
2169    /* Values of symbols are now protected */
2170    GC_LEAVE();
2171
2172    /* execute body */
2173    for (result = NIL; CONSP(body); body = CDR(body))
2174	result = EVAL(CAR(body));
2175
2176    lisp__data.env.head = lisp__data.env.length = head;
2177
2178    return (result);
2179}
2180
2181LispObj *
2182Lisp_LetP(LispBuiltin *builtin)
2183/*
2184 let* init &rest body
2185 */
2186{
2187    int head = lisp__data.env.length;
2188    LispObj *init, *body, *pair, *result;
2189
2190    body = ARGUMENT(1);
2191    init = ARGUMENT(0);
2192
2193    CHECK_LIST(init);
2194    for (; CONSP(init); init = CDR(init)) {
2195	LispObj *symbol, *value;
2196
2197	pair = CAR(init);
2198	if (SYMBOLP(pair)) {
2199	    symbol = pair;
2200	    value = NIL;
2201	}
2202	else {
2203	    CHECK_CONS(pair);
2204	    symbol = CAR(pair);
2205	    CHECK_SYMBOL(symbol);
2206	    pair = CDR(pair);
2207	    if (CONSP(pair)) {
2208		value = CAR(pair);
2209		if (CDR(pair) != NIL)
2210		    LispDestroy("%s: too much arguments to initialize %s",
2211				STRFUN(builtin), STROBJ(symbol));
2212		value = EVAL(value);
2213	    }
2214	    else
2215		value = NIL;
2216	}
2217
2218	CHECK_CONSTANT(symbol);
2219	LispAddVar(symbol, value);
2220	++lisp__data.env.head;
2221    }
2222
2223    /* execute body */
2224    for (result = NIL; CONSP(body); body = CDR(body))
2225	result = EVAL(CAR(body));
2226
2227    lisp__data.env.head = lisp__data.env.length = head;
2228
2229    return (result);
2230}
2231
2232LispObj *
2233Lisp_List(LispBuiltin *builtin)
2234/*
2235 list &rest args
2236 */
2237{
2238    LispObj *args;
2239
2240    args = ARGUMENT(0);
2241
2242    return (args);
2243}
2244
2245LispObj *
2246Lisp_ListP(LispBuiltin *builtin)
2247/*
2248 list* object &rest more-objects
2249 */
2250{
2251    GC_ENTER();
2252    LispObj *result, *cons;
2253
2254    LispObj *object, *more_objects;
2255
2256    more_objects = ARGUMENT(1);
2257    object = ARGUMENT(0);
2258
2259    if (!CONSP(more_objects))
2260	return (object);
2261
2262    result = cons = CONS(object, CAR(more_objects));
2263    GC_PROTECT(result);
2264    for (more_objects = CDR(more_objects); CONSP(more_objects);
2265	 more_objects = CDR(more_objects)) {
2266	object = CAR(more_objects);
2267	RPLACD(cons, CONS(CDR(cons), object));
2268	cons = CDR(cons);
2269    }
2270    GC_LEAVE();
2271
2272    return (result);
2273}
2274
2275/* "classic" list-length */
2276LispObj *
2277Lisp_ListLength(LispBuiltin *builtin)
2278/*
2279 list-length list
2280 */
2281{
2282    long length;
2283    LispObj *fast, *slow;
2284
2285    LispObj *list;
2286
2287    list = ARGUMENT(0);
2288
2289    CHECK_LIST(list);
2290    for (fast = slow = list, length = 0;
2291	 CONSP(slow);
2292	 slow = CDR(slow), length += 2) {
2293	if (fast == NIL)
2294	    break;
2295	CHECK_CONS(fast);
2296	fast = CDR(fast);
2297	if (fast == NIL) {
2298	    ++length;
2299	    break;
2300	}
2301	CHECK_CONS(fast);
2302	fast = CDR(fast);
2303	if (slow == fast)
2304	    /* circular list */
2305	    return (NIL);
2306    }
2307
2308    return (FIXNUM(length));
2309}
2310
2311LispObj *
2312Lisp_Listp(LispBuiltin *builtin)
2313/*
2314 listp object
2315 */
2316{
2317    LispObj *object;
2318
2319    object = ARGUMENT(0);
2320
2321    return (object == NIL || CONSP(object) ? T : NIL);
2322}
2323
2324static LispObj *
2325LispListSet(LispBuiltin *builtin, int function)
2326/*
2327 intersection list1 list2 &key test test-not key
2328 nintersection list1 list2 &key test test-not key
2329 set-difference list1 list2 &key test test-not key
2330 nset-difference list1 list2 &key test test-not key
2331 set-exclusive-or list1 list2 &key test test-not key
2332 nset-exclusive-or list1 list2 &key test test-not key
2333 subsetp list1 list2 &key test test-not key
2334 union list1 list2 &key test test-not key
2335 nunion list1 list2 &key test test-not key
2336 */
2337{
2338    GC_ENTER();
2339    int code, expect, value, inplace, check_list2,
2340	intersection, setdifference, xunion, setexclusiveor;
2341    LispObj *lambda, *result, *cmp, *cmp1, *cmp2,
2342	    *item, *clist1, *clist2, *cons, *cdr;
2343
2344    LispObj *list1, *list2, *test, *test_not, *key;
2345
2346    key = ARGUMENT(4);
2347    test_not = ARGUMENT(3);
2348    test = ARGUMENT(2);
2349    list2 = ARGUMENT(1);
2350    list1 = ARGUMENT(0);
2351
2352    /* Check if arguments are valid lists */
2353    CHECK_LIST(list1);
2354    CHECK_LIST(list2);
2355
2356    setdifference = intersection = xunion = setexclusiveor = inplace = 0;
2357    switch (function) {
2358	case NSETDIFFERENCE:
2359	    inplace = 1;
2360	case SETDIFFERENCE:
2361	    setdifference = 1;
2362	    break;
2363	case NINTERSECTION:
2364	    inplace = 1;
2365	case INTERSECTION:
2366	    intersection = 1;
2367	    break;
2368	case NUNION:
2369	    inplace = 1;
2370	case UNION:
2371	    xunion = 1;
2372	    break;
2373	case NSETEXCLUSIVEOR:
2374	    inplace = 1;
2375	case SETEXCLUSIVEOR:
2376	    setexclusiveor = 1;
2377	    break;
2378    }
2379
2380    /* Check for fast return */
2381    if (list1 == NIL)
2382	return (setdifference || intersection ?
2383		NIL : function == SUBSETP ? T : list2);
2384    if (list2 == NIL)
2385	return (intersection || xunion || function == SUBSETP ? NIL : list1);
2386
2387    CHECK_TEST();
2388    clist1 = cdr = NIL;
2389
2390    /* Make a copy of list2 with the key predicate applied */
2391    if (key != UNSPEC) {
2392	result = cons = CONS(APPLY1(key, CAR(list2)), NIL);
2393	GC_PROTECT(result);
2394	for (cmp2 = CDR(list2); CONSP(cmp2); cmp2 = CDR(cmp2)) {
2395	    item = APPLY1(key, CAR(cmp2));
2396	    RPLACD(cons, CONS(APPLY1(key, CAR(cmp2)), NIL));
2397	    cons = CDR(cons);
2398	}
2399	/* check if list2 is a proper list */
2400	CHECK_LIST(cmp2);
2401	clist2 = result;
2402	check_list2 = 0;
2403    }
2404    else {
2405	clist2 = list2;
2406	check_list2 = 1;
2407    }
2408    result = cons = NIL;
2409
2410    /* Compare elements of lists
2411     * Logic:
2412     *	   UNION
2413     *		1) Walk list1 and if CAR(list1) not in list2, add it to result
2414     *		2) Add list2 to result
2415     *	   INTERSECTION
2416     *		1) Walk list1 and if CAR(list1) in list2, add it to result
2417     *	   SET-DIFFERENCE
2418     *		1) Walk list1 and if CAR(list1) not in list2, add it to result
2419     *	   SET-EXCLUSIVE-OR
2420     *		1) Walk list1 and if CAR(list1) not in list2, add it to result
2421     *		2) Walk list2 and if CAR(list2) not in list1, add it to result
2422     *	   SUBSETP
2423     *		1) Walk list1 and if CAR(list1) not in list2, return NIL
2424     *		2) Return T
2425     */
2426    value = 0;
2427    for (cmp1 = list1; CONSP(cmp1); cmp1 = CDR(cmp1)) {
2428	item = CAR(cmp1);
2429
2430	/* Apply key predicate if required */
2431	if (key != UNSPEC) {
2432	    cmp = APPLY1(key, item);
2433	    if (setexclusiveor) {
2434		if (clist1 == NIL) {
2435		    clist1 = cdr = CONS(cmp, NIL);
2436		    GC_PROTECT(clist1);
2437		}
2438		else {
2439		    RPLACD(cdr, CONS(cmp, NIL));
2440		    cdr = CDR(cdr);
2441		}
2442	    }
2443	}
2444	else
2445	    cmp = item;
2446
2447	/* Compare against list2 */
2448	for (cmp2 = clist2; CONSP(cmp2); cmp2 = CDR(cmp2)) {
2449	    value = FCOMPARE(lambda, cmp, CAR(cmp2), code);
2450	    if (value == expect)
2451		break;
2452	}
2453	if (check_list2 && value != expect) {
2454	    /* check if list2 is a proper list */
2455	    CHECK_LIST(cmp2);
2456	    check_list2 = 0;
2457	}
2458
2459	if (function == SUBSETP) {
2460	    /* Element of list1 not in list2? */
2461	    if (value != expect) {
2462		GC_LEAVE();
2463
2464		return (NIL);
2465	    }
2466	}
2467	/* If need to add item to result */
2468	else if (((setdifference || xunion || setexclusiveor) &&
2469		  value != expect) ||
2470		 (intersection && value == expect)) {
2471	    if (inplace) {
2472		if (result == NIL)
2473		    result = cons = cmp1;
2474		else {
2475		    if (setexclusiveor) {
2476			/* don't remove elements yet, will need
2477			 * to check agains't list2 later */
2478			for (cmp2 = cons; CDR(cmp2) != cmp1; cmp2 = CDR(cmp2))
2479			    ;
2480			if (cmp2 != cons) {
2481			    RPLACD(cmp2, list1);
2482			    list1 = cmp2;
2483			}
2484		    }
2485		    RPLACD(cons, cmp1);
2486		    cons = cmp1;
2487		}
2488	    }
2489	    else {
2490		if (result == NIL) {
2491		    result = cons = CONS(item, NIL);
2492		    GC_PROTECT(result);
2493		}
2494		else {
2495		    RPLACD(cons, CONS(item, NIL));
2496		    cons = CDR(cons);
2497		}
2498	    }
2499	}
2500    }
2501    /* check if list1 is a proper list */
2502    CHECK_LIST(cmp1);
2503
2504    if (function == SUBSETP) {
2505	GC_LEAVE();
2506
2507	return (T);
2508    }
2509    else if (xunion) {
2510	/* Add list2 to tail of result */
2511	if (result == NIL)
2512	    result = list2;
2513	else
2514	    RPLACD(cons, list2);
2515    }
2516    else if (setexclusiveor) {
2517	LispObj *result2, *cons2;
2518
2519	result2 = cons2 = NIL;
2520	for (cmp2 = list2; CONSP(cmp2); cmp2 = CDR(cmp2)) {
2521	    item = CAR(cmp2);
2522
2523	    if (key != UNSPEC) {
2524		cmp = CAR(clist2);
2525		/* XXX changing clist2 */
2526		clist2 = CDR(clist2);
2527		cmp1 = clist1;
2528	    }
2529	    else {
2530		cmp = item;
2531		cmp1 = list1;
2532	    }
2533
2534	    /* Compare against list1 */
2535	    for (; CONSP(cmp1); cmp1 = CDR(cmp1)) {
2536		value = FCOMPARE(lambda, cmp, CAR(cmp1), code);
2537		if (value == expect)
2538		    break;
2539	    }
2540
2541	    if (value != expect) {
2542		if (inplace) {
2543		    if (result2 == NIL)
2544			result2 = cons2 = cmp2;
2545		    else {
2546			RPLACD(cons2, cmp2);
2547			cons2 = cmp2;
2548		    }
2549		}
2550		else {
2551		    if (result == NIL) {
2552			result = cons = CONS(item, NIL);
2553			GC_PROTECT(result);
2554		    }
2555		    else {
2556			RPLACD(cons, CONS(item, NIL));
2557			cons = CDR(cons);
2558		    }
2559		}
2560	    }
2561	}
2562	if (inplace) {
2563	    if (CONSP(cons2))
2564		RPLACD(cons2, NIL);
2565	    if (result == NIL)
2566		result = result2;
2567	    else
2568		RPLACD(cons, result2);
2569	}
2570    }
2571    else if ((function == NSETDIFFERENCE || function == NINTERSECTION) &&
2572	     CONSP(cons))
2573	RPLACD(cons, NIL);
2574
2575    GC_LEAVE();
2576
2577    return (result);
2578}
2579
2580LispObj *
2581Lisp_Loop(LispBuiltin *builtin)
2582/*
2583 loop &rest body
2584 */
2585{
2586    LispObj *code, *result;
2587    LispBlock *block;
2588
2589    LispObj *body;
2590
2591    body = ARGUMENT(0);
2592
2593    result = NIL;
2594    block = LispBeginBlock(NIL, LispBlockTag);
2595    if (setjmp(block->jmp) == 0) {
2596	for (;;)
2597	    for (code = body; CONSP(code); code = CDR(code))
2598		(void)EVAL(CAR(code));
2599    }
2600    LispEndBlock(block);
2601    result = lisp__data.block.block_ret;
2602
2603    return (result);
2604}
2605
2606/* XXX This function is broken, needs a review
2607 (being delayed until true array/vectors be implemented) */
2608LispObj *
2609Lisp_MakeArray(LispBuiltin *builtin)
2610/*
2611 make-array dimensions &key element-type initial-element initial-contents
2612			    adjustable fill-pointer displaced-to
2613			    displaced-index-offset
2614 */
2615{
2616    long rank = 0, count = 1, offset, zero, c;
2617    LispObj *obj, *dim, *array;
2618    LispType type;
2619
2620    LispObj *dimensions, *element_type, *initial_element, *initial_contents,
2621	    *displaced_to, *displaced_index_offset;
2622
2623    dim = array = NIL;
2624    type = LispNil_t;
2625
2626    displaced_index_offset = ARGUMENT(7);
2627    displaced_to = ARGUMENT(6);
2628    initial_contents = ARGUMENT(3);
2629    initial_element = ARGUMENT(2);
2630    element_type = ARGUMENT(1);
2631    dimensions = ARGUMENT(0);
2632
2633    if (INDEXP(dimensions)) {
2634	dim = CONS(dimensions, NIL);
2635	rank = 1;
2636	count = FIXNUM_VALUE(dimensions);
2637    }
2638    else if (CONSP(dimensions)) {
2639	dim = dimensions;
2640
2641	for (rank = 0; CONSP(dim); rank++, dim = CDR(dim)) {
2642	    obj = CAR(dim);
2643	    CHECK_INDEX(obj);
2644	    count *= FIXNUM_VALUE(obj);
2645	}
2646	dim = dimensions;
2647    }
2648    else if (dimensions == NIL) {
2649	dim = NIL;
2650	rank = count = 0;
2651    }
2652    else
2653	LispDestroy("%s: %s is a bad array dimension",
2654		    STRFUN(builtin), STROBJ(dimensions));
2655
2656    /* check element-type */
2657    if (element_type != UNSPEC) {
2658	if (element_type == T)
2659	    type = LispNil_t;
2660	else if (!SYMBOLP(element_type))
2661	    LispDestroy("%s: unsupported element type %s",
2662			STRFUN(builtin), STROBJ(element_type));
2663	else {
2664	    Atom_id atom = ATOMID(element_type);
2665
2666	    if (atom == Satom)
2667		type = LispAtom_t;
2668	    else if (atom == Sinteger)
2669		type = LispInteger_t;
2670	    else if (atom == Scharacter)
2671		type = LispSChar_t;
2672	    else if (atom == Sstring)
2673		type = LispString_t;
2674	    else if (atom == Slist)
2675		type = LispCons_t;
2676	    else if (atom == Sopaque)
2677		type = LispOpaque_t;
2678	    else
2679		LispDestroy("%s: unsupported element type %s",
2680			    STRFUN(builtin), ATOMID(element_type)->value);
2681	}
2682    }
2683
2684    /* check initial-contents */
2685    if (rank) {
2686	CHECK_LIST(initial_contents);
2687    }
2688
2689    /* check displaced-to */
2690    if (displaced_to != UNSPEC) {
2691	CHECK_ARRAY(displaced_to);
2692    }
2693
2694    /* check displaced-index-offset */
2695    offset = -1;
2696    if (displaced_index_offset != UNSPEC) {
2697	CHECK_INDEX(displaced_index_offset);
2698	offset = FIXNUM_VALUE(displaced_index_offset);
2699    }
2700
2701    c = 0;
2702    if (initial_element != UNSPEC)
2703	++c;
2704    if (initial_contents != UNSPEC)
2705	++c;
2706    if (displaced_to != UNSPEC || offset >= 0)
2707	++c;
2708    if (c > 1)
2709	LispDestroy("%s: more than one initialization specified",
2710		    STRFUN(builtin));
2711    if (initial_element == UNSPEC)
2712	initial_element = NIL;
2713
2714    zero = count == 0;
2715    if (displaced_to != UNSPEC) {
2716	CHECK_ARRAY(displaced_to);
2717	if (offset < 0)
2718	    offset = 0;
2719	for (c = 1, obj = displaced_to->data.array.dim; obj != NIL;
2720	     obj = CDR(obj))
2721	    c *= FIXNUM_VALUE(CAR(obj));
2722	if (c < count + offset)
2723	    LispDestroy("%s: array-total-size + displaced-index-offset "
2724			"exceeds total size", STRFUN(builtin));
2725	for (c = 0, array = displaced_to->data.array.list; c < offset; c++)
2726	    array = CDR(array);
2727    }
2728    else if (initial_contents != UNSPEC) {
2729	CHECK_CONS(initial_contents);
2730	if (rank == 0)
2731	    array = initial_contents;
2732	else if (rank == 1) {
2733	    for (array = initial_contents, c = 0; c < count;
2734		 array = CDR(array), c++)
2735		if (!CONSP(array))
2736		    LispDestroy("%s: bad argument or size %s",
2737				STRFUN(builtin), STROBJ(array));
2738	    if (array != NIL)
2739		LispDestroy("%s: bad argument or size %s",
2740			    STRFUN(builtin), STROBJ(array));
2741	    array = initial_contents;
2742	}
2743	else {
2744	    LispObj *err = NIL;
2745	    /* check if list matches */
2746	    int i, j, k, *dims, *loop;
2747
2748	    /* create iteration variables */
2749	    dims = LispMalloc(sizeof(int) * rank);
2750	    loop = LispCalloc(1, sizeof(int) * (rank - 1));
2751	    for (i = 0, obj = dim; CONSP(obj); i++, obj = CDR(obj))
2752		dims[i] = FIXNUM_VALUE(CAR(obj));
2753
2754	    /* check if list matches specified dimensions */
2755	    while (loop[0] < dims[0]) {
2756		for (obj = initial_contents, i = 0; i < rank - 1; i++) {
2757		    for (j = 0; j < loop[i]; j++)
2758			obj = CDR(obj);
2759		    err = obj;
2760		    if (!CONSP(obj = CAR(obj)))
2761			goto make_array_error;
2762		    err = obj;
2763		}
2764		--i;
2765		for (;;) {
2766		    ++loop[i];
2767		    if (i && loop[i] >= dims[i])
2768			loop[i] = 0;
2769		    else
2770			break;
2771		    --i;
2772		}
2773		for (k = 0; k < dims[rank - 1]; obj = CDR(obj), k++) {
2774		    if (!CONSP(obj))
2775			goto make_array_error;
2776		}
2777		if (obj == NIL)
2778		    continue;
2779make_array_error:
2780		LispFree(dims);
2781		LispFree(loop);
2782		LispDestroy("%s: bad argument or size %s",
2783			    STRFUN(builtin), STROBJ(err));
2784	    }
2785
2786	    /* list is correct, use it to fill initial values */
2787
2788	    /* reset loop */
2789	    memset(loop, 0, sizeof(int) * (rank - 1));
2790
2791	    GCDisable();
2792	    /* fill array with supplied values */
2793	    array = NIL;
2794	    while (loop[0] < dims[0]) {
2795		for (obj = initial_contents, i = 0; i < rank - 1; i++) {
2796		    for (j = 0; j < loop[i]; j++)
2797			obj = CDR(obj);
2798		    obj = CAR(obj);
2799		}
2800		--i;
2801		for (;;) {
2802		    ++loop[i];
2803		    if (i && loop[i] >= dims[i])
2804			loop[i] = 0;
2805		    else
2806			break;
2807		    --i;
2808		}
2809		for (k = 0; k < dims[rank - 1]; obj = CDR(obj), k++) {
2810		    if (array == NIL)
2811			array = CONS(CAR(obj), NIL);
2812		    else {
2813			RPLACD(array, CONS(CAR(array), CDR(array)));
2814			RPLACA(array, CAR(obj));
2815		    }
2816		}
2817	    }
2818	    LispFree(dims);
2819	    LispFree(loop);
2820	    array = LispReverse(array);
2821	    GCEnable();
2822	}
2823    }
2824    else {
2825	GCDisable();
2826	/* allocate array */
2827	if (count) {
2828	    --count;
2829	    array = CONS(initial_element, NIL);
2830	    while (count) {
2831		RPLACD(array, CONS(CAR(array), CDR(array)));
2832		RPLACA(array, initial_element);
2833		count--;
2834	    }
2835	}
2836	GCEnable();
2837    }
2838
2839    obj = LispNew(array, dim);
2840    obj->type = LispArray_t;
2841    obj->data.array.list = array;
2842    obj->data.array.dim = dim;
2843    obj->data.array.rank = rank;
2844    obj->data.array.type = type;
2845    obj->data.array.zero = zero;
2846
2847    return (obj);
2848}
2849
2850LispObj *
2851Lisp_MakeList(LispBuiltin *builtin)
2852/*
2853 make-list size &key initial-element
2854 */
2855{
2856    GC_ENTER();
2857    long count;
2858    LispObj *result, *cons;
2859
2860    LispObj *size, *initial_element;
2861
2862    initial_element = ARGUMENT(1);
2863    size = ARGUMENT(0);
2864
2865    CHECK_INDEX(size);
2866    count = FIXNUM_VALUE(size);
2867
2868    if (count == 0)
2869	return (NIL);
2870    if (initial_element == UNSPEC)
2871	initial_element = NIL;
2872
2873    result = cons = CONS(initial_element, NIL);
2874    GC_PROTECT(result);
2875    for (; count > 1; count--) {
2876	RPLACD(cons, CONS(initial_element, NIL));
2877	cons = CDR(cons);
2878    }
2879    GC_LEAVE();
2880
2881    return (result);
2882}
2883
2884LispObj *
2885Lisp_MakeSymbol(LispBuiltin *builtin)
2886/*
2887 make-symbol name
2888 */
2889{
2890    LispObj *name, *symbol;
2891
2892    name = ARGUMENT(0);
2893    CHECK_STRING(name);
2894
2895    symbol = UNINTERNED_ATOM(THESTR(name));
2896    symbol->data.atom->unreadable = !LispCheckAtomString(THESTR(name));
2897
2898    return (symbol);
2899}
2900
2901LispObj *
2902Lisp_Makunbound(LispBuiltin *builtin)
2903/*
2904 makunbound symbol
2905 */
2906{
2907    LispObj *symbol;
2908
2909    symbol = ARGUMENT(0);
2910
2911    CHECK_SYMBOL(symbol);
2912    LispUnsetVar(symbol);
2913
2914    return (symbol);
2915}
2916
2917LispObj *
2918Lisp_Mapc(LispBuiltin *builtin)
2919/*
2920 mapc function list &rest more-lists
2921 */
2922{
2923    return (LispMapc(builtin, 0));
2924}
2925
2926LispObj *
2927Lisp_Mapcar(LispBuiltin *builtin)
2928/*
2929 mapcar function list &rest more-lists
2930 */
2931{
2932    return (LispMapc(builtin, 1));
2933}
2934
2935/* Like nconc but ignore non list arguments */
2936LispObj *
2937LispMapnconc(LispObj *list)
2938{
2939    LispObj *result = NIL;
2940
2941    if (CONSP(list)) {
2942	LispObj *cons, *head, *tail;
2943
2944	cons = NIL;
2945	for (; CONSP(CDR(list)); list = CDR(list)) {
2946	    head = CAR(list);
2947	    if (CONSP(head)) {
2948		for (tail = head; CONSP(CDR(tail)); tail = CDR(tail))
2949		    ;
2950		if (cons != NIL)
2951		    RPLACD(cons, head);
2952		else
2953		    result = head;
2954		cons = tail;
2955	    }
2956	}
2957	head = CAR(list);
2958	if (CONSP(head)) {
2959	    if (cons != NIL)
2960		RPLACD(cons, head);
2961	    else
2962		result = head;
2963	}
2964    }
2965
2966    return (result);
2967}
2968
2969LispObj *
2970Lisp_Mapcan(LispBuiltin *builtin)
2971/*
2972 mapcan function list &rest more-lists
2973 */
2974{
2975    return (LispMapnconc(LispMapc(builtin, 1)));
2976}
2977
2978static LispObj *
2979LispMapc(LispBuiltin *builtin, int mapcar)
2980{
2981    GC_ENTER();
2982    long i, offset, count, length;
2983    LispObj *result = NIL, *cons, *arguments, *acons, *rest, *alist, *value;
2984    LispObj *stk[8], **cdrs;
2985
2986    LispObj *function, *list, *more_lists;
2987
2988    more_lists = ARGUMENT(2);
2989    list = ARGUMENT(1);
2990    function = ARGUMENT(0);
2991
2992    /* Result will be no longer than this */
2993    for (length = 0, alist = list; CONSP(alist); length++, alist = CDR(alist))
2994	;
2995
2996    /* If first argument is not a list... */
2997    if (length == 0)
2998	return (NIL);
2999
3000    /* At least one argument will be passed to function, count how many
3001     * extra arguments will be used, and calculate result length. */
3002    count = 0;
3003    for (rest = more_lists; CONSP(rest); rest = CDR(rest), count++) {
3004
3005	/* Check if extra list is really a list, and if it is smaller
3006	 * than the first list */
3007	for (i = 0, alist = CAR(rest);
3008	     i < length && CONSP(alist);
3009	     i++, alist = CDR(alist))
3010	    ;
3011
3012	/* If it is not a true list */
3013	if (i == 0)
3014	    return (NIL);
3015
3016	/* If it is smaller than the currently calculated result length */
3017	if (i < length)
3018	    length = i;
3019    }
3020
3021    if (mapcar) {
3022	/* Initialize gc protected object cells for resulting list */
3023	result = cons = CONS(NIL, NIL);
3024	GC_PROTECT(result);
3025    }
3026    else
3027	result = cons = list;
3028
3029    if (count >= sizeof(stk) / sizeof(stk[0]))
3030	cdrs = LispMalloc(count * sizeof(LispObj*));
3031    else
3032	cdrs = &stk[0];
3033    for (i = 0, rest = more_lists; i < count; i++, rest = CDR(rest))
3034	cdrs[i] = CAR(rest);
3035
3036    /* Initialize gc protected object cells for argument list */
3037    arguments = acons = CONS(NIL, NIL);
3038    GC_PROTECT(arguments);
3039
3040    /* Allocate space for extra arguments */
3041    for (i = 0; i < count; i++) {
3042	RPLACD(acons, CONS(NIL, NIL));
3043	acons = CDR(acons);
3044    }
3045
3046    /* For every element of the list that will be used */
3047    for (offset = 0;; list = CDR(list)) {
3048	acons = arguments;
3049
3050	/* Add first argument */
3051	RPLACA(acons, CAR(list));
3052	acons = CDR(acons);
3053
3054	/* For every extra list argument */
3055	for (i = 0; i < count; i++) {
3056	    alist = cdrs[i];
3057	    cdrs[i] = CDR(cdrs[i]);
3058
3059	    /* Add element to argument list */
3060	    RPLACA(acons, CAR(alist));
3061	    acons = CDR(acons);
3062	}
3063
3064	value = APPLY(function, arguments);
3065
3066	if (mapcar) {
3067	    /* Store result */
3068	    RPLACA(cons, value);
3069
3070	    /* Allocate new result cell */
3071	    if (++offset < length) {
3072		RPLACD(cons, CONS(NIL, NIL));
3073		cons = CDR(cons);
3074	    }
3075	    else
3076		break;
3077	}
3078	else if (++offset >= length)
3079	    break;
3080    }
3081
3082    /* Unprotect argument and result list */
3083    GC_LEAVE();
3084    if (cdrs != &stk[0])
3085	LispFree(cdrs);
3086
3087    return (result);
3088}
3089
3090static LispObj *
3091LispMapl(LispBuiltin *builtin, int maplist)
3092{
3093    GC_ENTER();
3094    long i, offset, count, length;
3095    LispObj *result = NIL, *cons, *arguments, *acons, *rest, *alist, *value;
3096    LispObj *stk[8], **cdrs;
3097
3098    LispObj *function, *list, *more_lists;
3099
3100    more_lists = ARGUMENT(2);
3101    list = ARGUMENT(1);
3102    function = ARGUMENT(0);
3103
3104    /* count is the number of lists, length is the length of the result */
3105    for (length = 0, alist = list; CONSP(alist); length++, alist = CDR(alist))
3106	;
3107
3108    /* first argument is not a list */
3109    if (length == 0)
3110	return (NIL);
3111
3112    /* check remaining arguments */
3113    for (count = 0, rest = more_lists; CONSP(rest); rest = CDR(rest), count++) {
3114	for (i = 0, alist = CAR(rest);
3115	     i < length && CONSP(alist);
3116	     i++, alist = CDR(alist))
3117	    ;
3118	/* argument is not a list */
3119	if (i == 0)
3120	    return (NIL);
3121	/* result will have the length of the smallest list */
3122	if (i < length)
3123	    length = i;
3124    }
3125
3126    /* result will be a list */
3127    if (maplist) {
3128	result = cons = CONS(NIL, NIL);
3129	GC_PROTECT(result);
3130    }
3131    else
3132	result = cons = list;
3133
3134    if (count >= sizeof(stk) / sizeof(stk[0]))
3135	cdrs = LispMalloc(count * sizeof(LispObj*));
3136    else
3137	cdrs = &stk[0];
3138    for (i = 0, rest = more_lists; i < count; i++, rest = CDR(rest))
3139	cdrs[i] = CAR(rest);
3140
3141    /* initialize argument list */
3142    arguments = acons = CONS(NIL, NIL);
3143    GC_PROTECT(arguments);
3144    for (i = 0; i < count; i++) {
3145	RPLACD(acons, CONS(NIL, NIL));
3146	acons = CDR(acons);
3147    }
3148
3149    /* for every used list element */
3150    for (offset = 0;; list = CDR(list)) {
3151	acons = arguments;
3152
3153	/* first argument */
3154	RPLACA(acons, list);
3155	acons = CDR(acons);
3156
3157	/* for every extra list */
3158	for (i = 0; i < count; i++) {
3159	    RPLACA(acons, cdrs[i]);
3160	    cdrs[i] = CDR(cdrs[i]);
3161	    acons = CDR(acons);
3162	}
3163
3164	value = APPLY(function, arguments);
3165
3166	if (maplist) {
3167	    /* store result */
3168	    RPLACA(cons, value);
3169
3170	    /* allocate new cell */
3171	    if (++offset < length) {
3172		RPLACD(cons, CONS(NIL, NIL));
3173		cons = CDR(cons);
3174	    }
3175	    else
3176		break;
3177	}
3178	else if (++offset >= length)
3179	    break;
3180    }
3181
3182    GC_LEAVE();
3183    if (cdrs != &stk[0])
3184	LispFree(cdrs);
3185
3186    return (result);
3187}
3188
3189LispObj *
3190Lisp_Mapl(LispBuiltin *builtin)
3191/*
3192 mapl function list &rest more-lists
3193 */
3194{
3195    return (LispMapl(builtin, 0));
3196}
3197
3198LispObj *
3199Lisp_Maplist(LispBuiltin *builtin)
3200/*
3201 maplist function list &rest more-lists
3202 */
3203{
3204    return (LispMapl(builtin, 1));
3205}
3206
3207LispObj *
3208Lisp_Mapcon(LispBuiltin *builtin)
3209/*
3210 mapcon function list &rest more-lists
3211 */
3212{
3213    return (LispMapnconc(LispMapl(builtin, 1)));
3214}
3215
3216LispObj *
3217Lisp_Member(LispBuiltin *builtin)
3218/*
3219 member item list &key test test-not key
3220 */
3221{
3222    int code, expect;
3223    LispObj *compare, *lambda;
3224    LispObj *item, *list, *test, *test_not, *key;
3225
3226    key = ARGUMENT(4);
3227    test_not = ARGUMENT(3);
3228    test = ARGUMENT(2);
3229    list = ARGUMENT(1);
3230    item = ARGUMENT(0);
3231
3232    if (list == NIL)
3233	return (NIL);
3234    CHECK_CONS(list);
3235
3236    CHECK_TEST();
3237    if (key == UNSPEC) {
3238	if (code == FEQ) {
3239	    for (; CONSP(list); list = CDR(list))
3240		if (item == CAR(list))
3241		    return (list);
3242	}
3243	else {
3244	    for (; CONSP(list); list = CDR(list))
3245		if ((FCOMPARE(lambda, item, CAR(list), code)) == expect)
3246		    return (list);
3247	}
3248    }
3249    else {
3250	if (code == FEQ) {
3251	    for (; CONSP(list); list = CDR(list))
3252		if (item == APPLY1(key, CAR(list)))
3253		    return (list);
3254	}
3255	else {
3256	    for (; CONSP(list); list = CDR(list)) {
3257		compare = APPLY1(key, CAR(list));
3258		if ((FCOMPARE(lambda, item, compare, code)) == expect)
3259		    return (list);
3260	    }
3261	}
3262    }
3263    /* check if is a proper list */
3264    CHECK_LIST(list);
3265
3266    return (NIL);
3267}
3268
3269LispObj *
3270Lisp_MemberIf(LispBuiltin *builtin)
3271/*
3272 member-if predicate list &key key
3273 */
3274{
3275    return (LispAssocOrMember(builtin, MEMBER, IF));
3276}
3277
3278LispObj *
3279Lisp_MemberIfNot(LispBuiltin *builtin)
3280/*
3281 member-if-not predicate list &key key
3282 */
3283{
3284    return (LispAssocOrMember(builtin, MEMBER, IFNOT));
3285}
3286
3287LispObj *
3288Lisp_MultipleValueBind(LispBuiltin *builtin)
3289/*
3290 multiple-value-bind symbols values &rest body
3291 */
3292{
3293    int i, head = lisp__data.env.length;
3294    LispObj *result, *symbol, *value;
3295
3296    LispObj *symbols, *values, *body;
3297
3298    body = ARGUMENT(2);
3299    values = ARGUMENT(1);
3300    symbols = ARGUMENT(0);
3301
3302    result = EVAL(values);
3303    for (i = -1; CONSP(symbols); symbols = CDR(symbols), i++) {
3304	symbol = CAR(symbols);
3305	CHECK_SYMBOL(symbol);
3306	CHECK_CONSTANT(symbol);
3307	if (i >= 0 && i < RETURN_COUNT)
3308	    value = RETURN(i);
3309	else if (i < 0)
3310	    value = result;
3311	else
3312	    value = NIL;
3313	LispAddVar(symbol, value);
3314	++lisp__data.env.head;
3315    }
3316
3317    /* Execute code with binded variables (if any) */
3318    for (result = NIL; CONSP(body); body = CDR(body))
3319	result = EVAL(CAR(body));
3320
3321    lisp__data.env.head = lisp__data.env.length = head;
3322
3323    return (result);
3324}
3325
3326LispObj *
3327Lisp_MultipleValueCall(LispBuiltin *builtin)
3328/*
3329 multiple-value-call function &rest form
3330 */
3331{
3332    GC_ENTER();
3333    int i;
3334    LispObj *arguments, *cons, *result;
3335
3336    LispObj *function, *form;
3337
3338    form = ARGUMENT(1);
3339    function = ARGUMENT(0);
3340
3341    /* build argument list */
3342    arguments = cons = NIL;
3343    for (; CONSP(form); form = CDR(form)) {
3344	RETURN_COUNT = 0;
3345	result = EVAL(CAR(form));
3346	if (RETURN_COUNT >= 0) {
3347	    if (arguments == NIL) {
3348		arguments = cons = CONS(result, NIL);
3349		GC_PROTECT(arguments);
3350	    }
3351	    else {
3352		RPLACD(cons, CONS(result, NIL));
3353		cons = CDR(cons);
3354	    }
3355	    for (i = 0; i < RETURN_COUNT; i++) {
3356		RPLACD(cons, CONS(RETURN(i), NIL));
3357		cons = CDR(cons);
3358	    }
3359	}
3360    }
3361
3362    /* apply function */
3363    if (POINTERP(function) && !XSYMBOLP(function) && !XFUNCTIONP(function)) {
3364	function = EVAL(function);
3365	GC_PROTECT(function);
3366    }
3367    result = APPLY(function, arguments);
3368    GC_LEAVE();
3369
3370    return (result);
3371}
3372
3373LispObj *
3374Lisp_MultipleValueProg1(LispBuiltin *builtin)
3375/*
3376 multiple-value-prog1 first-form &rest form
3377 */
3378{
3379    GC_ENTER();
3380    int i, count;
3381    LispObj *values, *cons;
3382
3383    LispObj *first_form, *form;
3384
3385    form = ARGUMENT(1);
3386    first_form = ARGUMENT(0);
3387
3388    values = EVAL(first_form);
3389    if (!CONSP(form))
3390	return (values);
3391
3392    cons = NIL;
3393    count = RETURN_COUNT;
3394    if (count < 0)
3395	values = NIL;
3396    else if (count == 0) {
3397	GC_PROTECT(values);
3398    }
3399    else {
3400	values = cons = CONS(values, NIL);
3401	GC_PROTECT(values);
3402	for (i = 0; i < count; i++) {
3403	    RPLACD(cons, CONS(RETURN(i), NIL));
3404	    cons = CDR(cons);
3405	}
3406    }
3407
3408    for (; CONSP(form); form = CDR(form))
3409	EVAL(CAR(form));
3410
3411    RETURN_COUNT = count;
3412    if (count > 0) {
3413	for (i = 0, cons = CDR(values); CONSP(cons); cons = CDR(cons), i++)
3414	    RETURN(i) = CAR(cons);
3415	values = CAR(values);
3416    }
3417    GC_LEAVE();
3418
3419    return (values);
3420}
3421
3422LispObj *
3423Lisp_MultipleValueList(LispBuiltin *builtin)
3424/*
3425 multiple-value-list form
3426 */
3427{
3428    int i;
3429    GC_ENTER();
3430    LispObj *form, *result, *cons;
3431
3432    form = ARGUMENT(0);
3433
3434    result = EVAL(form);
3435
3436    if (RETURN_COUNT < 0)
3437	return (NIL);
3438
3439    result = cons = CONS(result, NIL);
3440    GC_PROTECT(result);
3441    for (i = 0; i < RETURN_COUNT; i++) {
3442	RPLACD(cons, CONS(RETURN(i), NIL));
3443	cons = CDR(cons);
3444    }
3445    GC_LEAVE();
3446
3447    return (result);
3448}
3449
3450LispObj *
3451Lisp_MultipleValueSetq(LispBuiltin *builtin)
3452/*
3453 multiple-value-setq symbols form
3454 */
3455{
3456    int i;
3457    LispObj *result, *symbol, *value;
3458
3459    LispObj *symbols, *form;
3460
3461    form = ARGUMENT(1);
3462    symbols = ARGUMENT(0);
3463
3464    CHECK_LIST(symbols);
3465    result = EVAL(form);
3466    if (CONSP(symbols)) {
3467	symbol = CAR(symbols);
3468	CHECK_SYMBOL(symbol);
3469	CHECK_CONSTANT(symbol);
3470	LispSetVar(symbol, result);
3471	symbols = CDR(symbols);
3472    }
3473    for (i = 0; CONSP(symbols); symbols = CDR(symbols), i++) {
3474	symbol = CAR(symbols);
3475	CHECK_SYMBOL(symbol);
3476	CHECK_CONSTANT(symbol);
3477	if (i < RETURN_COUNT && RETURN_COUNT > 0)
3478	    value = RETURN(i);
3479	else
3480	    value = NIL;
3481	LispSetVar(symbol, value);
3482    }
3483
3484    return (result);
3485}
3486
3487LispObj *
3488Lisp_Nconc(LispBuiltin *builtin)
3489/*
3490 nconc &rest lists
3491 */
3492{
3493    LispObj *list, *lists, *cons, *head, *tail;
3494
3495    lists = ARGUMENT(0);
3496
3497    /* skip any initial empty lists */
3498    for (; CONSP(lists); lists = CDR(lists))
3499	if (CAR(lists) != NIL)
3500	    break;
3501
3502    /* don't check if a proper list */
3503    if (!CONSP(lists))
3504	return (lists);
3505
3506    /* setup to concatenate lists */
3507    list = CAR(lists);
3508    CHECK_CONS(list);
3509    for (cons = list; CONSP(CDR(cons)); cons = CDR(cons))
3510	;
3511
3512    /* if only two lists */
3513    lists = CDR(lists);
3514    if (!CONSP(lists)) {
3515	RPLACD(cons, lists);
3516
3517	return (list);
3518    }
3519
3520    /* concatenate */
3521    for (; CONSP(CDR(lists)); lists = CDR(lists)) {
3522	head = CAR(lists);
3523	if (head == NIL)
3524	    continue;
3525	CHECK_CONS(head);
3526	for (tail = head; CONSP(CDR(tail)); tail = CDR(tail))
3527	    ;
3528	RPLACD(cons, head);
3529	cons = tail;
3530    }
3531    /* add last list */
3532    RPLACD(cons, CAR(lists));
3533
3534    return (list);
3535}
3536
3537LispObj *
3538Lisp_Nreverse(LispBuiltin *builtin)
3539/*
3540 nreverse sequence
3541 */
3542{
3543    return (LispXReverse(builtin, 1));
3544}
3545
3546LispObj *
3547Lisp_NsetDifference(LispBuiltin *builtin)
3548/*
3549 nset-difference list1 list2 &key test test-not key
3550 */
3551{
3552    return (LispListSet(builtin, NSETDIFFERENCE));
3553}
3554
3555LispObj *
3556Lisp_Nsubstitute(LispBuiltin *builtin)
3557/*
3558 nsubstitute newitem olditem sequence &key from-end test test-not start end count key
3559 */
3560{
3561    return (LispDeleteRemoveXSubstitute(builtin, NSUBSTITUTE, NONE));
3562}
3563
3564LispObj *
3565Lisp_NsubstituteIf(LispBuiltin *builtin)
3566/*
3567 nsubstitute-if newitem test sequence &key from-end start end count key
3568 */
3569{
3570    return (LispDeleteRemoveXSubstitute(builtin, NSUBSTITUTE, IF));
3571}
3572
3573LispObj *
3574Lisp_NsubstituteIfNot(LispBuiltin *builtin)
3575/*
3576 nsubstitute-if-not newitem test sequence &key from-end start end count key
3577 */
3578{
3579    return (LispDeleteRemoveXSubstitute(builtin, NSUBSTITUTE, IFNOT));
3580}
3581
3582LispObj *
3583Lisp_Nth(LispBuiltin *builtin)
3584/*
3585 nth index list
3586 */
3587{
3588    long position;
3589    LispObj *oindex, *list;
3590
3591    list = ARGUMENT(1);
3592    oindex = ARGUMENT(0);
3593
3594    CHECK_INDEX(oindex);
3595    position = FIXNUM_VALUE(oindex);
3596
3597    if (list == NIL)
3598	return (NIL);
3599
3600    CHECK_CONS(list);
3601    for (; position > 0; position--) {
3602	if (!CONSP(list))
3603	    return (NIL);
3604	list = CDR(list);
3605    }
3606
3607    return (CONSP(list) ? CAR(list) : NIL);
3608}
3609
3610LispObj *
3611Lisp_Nthcdr(LispBuiltin *builtin)
3612/*
3613 nthcdr index list
3614 */
3615{
3616    long position;
3617    LispObj *oindex, *list;
3618
3619    list = ARGUMENT(1);
3620    oindex = ARGUMENT(0);
3621
3622    CHECK_INDEX(oindex);
3623    position = FIXNUM_VALUE(oindex);
3624
3625    if (list == NIL)
3626	return (NIL);
3627    CHECK_CONS(list);
3628
3629    for (; position > 0; position--) {
3630	if (!CONSP(list))
3631	    return (NIL);
3632	list = CDR(list);
3633    }
3634
3635    return (list);
3636}
3637
3638LispObj *
3639Lisp_NthValue(LispBuiltin *builtin)
3640/*
3641 nth-value index form
3642 */
3643{
3644    long i;
3645    LispObj *oindex, *form, *result;
3646
3647    form = ARGUMENT(1);
3648    oindex = ARGUMENT(0);
3649
3650    oindex = EVAL(oindex);
3651    CHECK_INDEX(oindex);
3652    i = FIXNUM_VALUE(oindex) - 1;
3653
3654    result = EVAL(form);
3655    if (RETURN_COUNT < 0 || i >= RETURN_COUNT)
3656	result = NIL;
3657    else if (i >= 0)
3658	result = RETURN(i);
3659
3660    return (result);
3661}
3662
3663LispObj *
3664Lisp_Null(LispBuiltin *builtin)
3665/*
3666 null list
3667 */
3668{
3669    LispObj *list;
3670
3671    list = ARGUMENT(0);
3672
3673    return (list == NIL ? T : NIL);
3674}
3675
3676LispObj *
3677Lisp_Or(LispBuiltin *builtin)
3678/*
3679 or &rest args
3680 */
3681{
3682    LispObj *result = NIL, *args;
3683
3684    args = ARGUMENT(0);
3685
3686    for (; CONSP(args); args = CDR(args)) {
3687	result = EVAL(CAR(args));
3688	if (result != NIL)
3689	    break;
3690    }
3691
3692    return (result);
3693}
3694
3695LispObj *
3696Lisp_Pairlis(LispBuiltin *builtin)
3697/*
3698 pairlis key data &optional alist
3699 */
3700{
3701    LispObj *result, *cons;
3702
3703    LispObj *key, *data, *alist;
3704
3705    alist = ARGUMENT(2);
3706    data = ARGUMENT(1);
3707    key = ARGUMENT(0);
3708
3709    if (CONSP(key) && CONSP(data)) {
3710	GC_ENTER();
3711
3712	result = cons = CONS(CONS(CAR(key), CAR(data)), NIL);
3713	GC_PROTECT(result);
3714	key = CDR(key);
3715	data = CDR(data);
3716	for (; CONSP(key) && CONSP(data); key = CDR(key), data = CDR(data)) {
3717	    RPLACD(cons, CONS(CONS(CAR(key), CAR(data)), NIL));
3718	    cons = CDR(cons);
3719	}
3720	if (CONSP(key) || CONSP(data))
3721	    LispDestroy("%s: different length lists", STRFUN(builtin));
3722	GC_LEAVE();
3723	if (alist != UNSPEC)
3724	    RPLACD(cons, alist);
3725    }
3726    else
3727	result = alist == UNSPEC ? NIL : alist;
3728
3729    return (result);
3730}
3731
3732static LispObj *
3733LispFindOrPosition(LispBuiltin *builtin,
3734		   int function, int comparison)
3735/*
3736 find item sequence &key from-end test test-not start end key
3737 find-if predicate sequence &key from-end start end key
3738 find-if-not predicate sequence &key from-end start end key
3739 position item sequence &key from-end test test-not start end key
3740 position-if predicate sequence &key from-end start end key
3741 position-if-not predicate sequence &key from-end start end key
3742 */
3743{
3744    int code = 0, istring, expect, value;
3745    char *string = NULL;
3746    long offset = -1, start, end, length, i = comparison == NONE ? 7 : 5;
3747    LispObj *cmp, *element, **objects = NULL;
3748
3749    LispObj *item, *predicate, *sequence, *from_end,
3750	    *test, *test_not, *ostart, *oend, *key;
3751
3752    key = ARGUMENT(i);		--i;
3753    oend = ARGUMENT(i);		--i;
3754    ostart = ARGUMENT(i);	--i;
3755    if (comparison == NONE) {
3756	test_not = ARGUMENT(i);	--i;
3757	test = ARGUMENT(i);	--i;
3758    }
3759    else
3760	test_not = test = UNSPEC;
3761    from_end = ARGUMENT(i);	--i;
3762    if (from_end == UNSPEC)
3763	from_end = NIL;
3764    sequence = ARGUMENT(i);	--i;
3765    if (comparison == NONE) {
3766	item = ARGUMENT(i);
3767	predicate = Oeql;
3768    }
3769    else {
3770	predicate = ARGUMENT(i);
3771	item = NIL;
3772    }
3773
3774    LispCheckSequenceStartEnd(builtin, sequence, ostart, oend,
3775			      &start, &end, &length);
3776
3777    if (sequence == NIL)
3778	return (NIL);
3779
3780    /* Cannot specify both :test and :test-not */
3781    if (test != UNSPEC && test_not != UNSPEC)
3782	LispDestroy("%s: specify either :TEST or :TEST-NOT", STRFUN(builtin));
3783
3784    expect = 1;
3785    if (comparison == NONE) {
3786	if (test != UNSPEC)
3787	    predicate = test;
3788	else if (test_not != UNSPEC) {
3789	    predicate = test_not;
3790	    expect = 0;
3791	}
3792	FUNCTION_CHECK(predicate);
3793	code = FCODE(predicate);
3794    }
3795
3796    cmp = element = NIL;
3797    istring = STRINGP(sequence);
3798    if (istring)
3799	string = THESTR(sequence);
3800    else {
3801	if (!CONSP(sequence))
3802	    sequence = sequence->data.array.list;
3803	for (i = 0; i < start; i++)
3804	    sequence = CDR(sequence);
3805    }
3806
3807    if ((length = end - start) == 0)
3808	return (NIL);
3809
3810    if (from_end != NIL && !istring) {
3811	objects = LispMalloc(sizeof(LispObj*) * length);
3812	for (i = length - 1; i >= 0; i--, sequence = CDR(sequence))
3813	    objects[i] = CAR(sequence);
3814    }
3815
3816    for (i = 0; i < length; i++) {
3817	if (istring)
3818	    element = SCHAR(string[from_end == NIL ? i + start : end - i - 1]);
3819	else
3820	    element = from_end == NIL ? CAR(sequence) : objects[i];
3821
3822	if (key != UNSPEC)
3823	    cmp = APPLY1(key, element);
3824	else
3825	    cmp = element;
3826
3827	/* Update list */
3828	if (!istring && from_end == NIL)
3829	    sequence = CDR(sequence);
3830
3831	if (comparison == NONE)
3832	    value = FCOMPARE(predicate, item, cmp, code);
3833	else
3834	    value = APPLY1(predicate, cmp) != NIL;
3835
3836	if ((!value &&
3837	     (comparison == IFNOT ||
3838	      (comparison == NONE && !expect))) ||
3839	    (value &&
3840	     (comparison == IF ||
3841	      (comparison == NONE && expect)))) {
3842	    offset = from_end == NIL ? i + start : end - i - 1;
3843	    break;
3844	}
3845    }
3846
3847    if (from_end != NIL && !istring)
3848	LispFree(objects);
3849
3850    return (offset == -1 ? NIL : function == FIND ? element : FIXNUM(offset));
3851}
3852
3853LispObj *
3854Lisp_Pop(LispBuiltin *builtin)
3855/*
3856 pop place
3857 */
3858{
3859    LispObj *result, *value;
3860
3861    LispObj *place;
3862
3863    place = ARGUMENT(0);
3864
3865    if (SYMBOLP(place)) {
3866	result = LispGetVar(place);
3867	if (result == NULL)
3868	    LispDestroy("EVAL: the variable %s is unbound", STROBJ(place));
3869	CHECK_CONSTANT(place);
3870	if (result != NIL) {
3871	    CHECK_CONS(result);
3872	    value = CDR(result);
3873	    result = CAR(result);
3874	}
3875	else
3876	    value = NIL;
3877	LispSetVar(place, value);
3878    }
3879    else {
3880	GC_ENTER();
3881	LispObj quote;
3882
3883	result = EVAL(place);
3884	if (result != NIL) {
3885	    CHECK_CONS(result);
3886	    value = CDR(result);
3887	    GC_PROTECT(value);
3888	    result = CAR(result);
3889	}
3890	else
3891	    value = NIL;
3892	quote.type = LispQuote_t;
3893	quote.data.quote = value;
3894	APPLY2(Osetf, place, &quote);
3895	GC_LEAVE();
3896    }
3897
3898    return (result);
3899}
3900
3901LispObj *
3902Lisp_Position(LispBuiltin *builtin)
3903/*
3904 position item sequence &key from-end test test-not start end key
3905 */
3906{
3907    return (LispFindOrPosition(builtin, POSITION, NONE));
3908}
3909
3910LispObj *
3911Lisp_PositionIf(LispBuiltin *builtin)
3912/*
3913 position-if predicate sequence &key from-end start end key
3914 */
3915{
3916    return (LispFindOrPosition(builtin, POSITION, IF));
3917}
3918
3919LispObj *
3920Lisp_PositionIfNot(LispBuiltin *builtin)
3921/*
3922 position-if-not predicate sequence &key from-end start end key
3923 */
3924{
3925    return (LispFindOrPosition(builtin, POSITION, IFNOT));
3926}
3927
3928LispObj *
3929Lisp_Proclaim(LispBuiltin *builtin)
3930/*
3931 proclaim declaration
3932 */
3933{
3934    LispObj *arguments, *object;
3935    char *operation;
3936
3937    LispObj *declaration;
3938
3939    declaration = ARGUMENT(0);
3940
3941    CHECK_CONS(declaration);
3942
3943    arguments = declaration;
3944    object = CAR(arguments);
3945    CHECK_SYMBOL(object);
3946
3947    operation = ATOMID(object)->value;
3948    if (strcmp(operation, "SPECIAL") == 0) {
3949	for (arguments = CDR(arguments); CONSP(arguments);
3950	     arguments = CDR(arguments)) {
3951	    object = CAR(arguments);
3952	    CHECK_SYMBOL(object);
3953	    LispProclaimSpecial(object, NULL, NIL);
3954	}
3955    }
3956    else if (strcmp(operation, "TYPE") == 0) {
3957	/* XXX no type checking yet, but should be added */
3958    }
3959    /* else do nothing */
3960
3961    return (NIL);
3962}
3963
3964LispObj *
3965Lisp_Prog1(LispBuiltin *builtin)
3966/*
3967 prog1 first &rest body
3968 */
3969{
3970    GC_ENTER();
3971    LispObj *result;
3972
3973    LispObj *first, *body;
3974
3975    body = ARGUMENT(1);
3976    first = ARGUMENT(0);
3977
3978    result = EVAL(first);
3979
3980    GC_PROTECT(result);
3981    for (; CONSP(body); body = CDR(body))
3982	(void)EVAL(CAR(body));
3983    GC_LEAVE();
3984
3985    return (result);
3986}
3987
3988LispObj *
3989Lisp_Prog2(LispBuiltin *builtin)
3990/*
3991 prog2 first second &rest body
3992 */
3993{
3994    GC_ENTER();
3995    LispObj *result;
3996
3997    LispObj *first, *second, *body;
3998
3999    body = ARGUMENT(2);
4000    second = ARGUMENT(1);
4001    first = ARGUMENT(0);
4002
4003    (void)EVAL(first);
4004    result = EVAL(second);
4005    GC_PROTECT(result);
4006    for (; CONSP(body); body = CDR(body))
4007	(void)EVAL(CAR(body));
4008    GC_LEAVE();
4009
4010    return (result);
4011}
4012
4013LispObj *
4014Lisp_Progn(LispBuiltin *builtin)
4015/*
4016 progn &rest body
4017 */
4018{
4019    LispObj *result = NIL;
4020
4021    LispObj *body;
4022
4023    body = ARGUMENT(0);
4024
4025    for (; CONSP(body); body = CDR(body))
4026	result = EVAL(CAR(body));
4027
4028    return (result);
4029}
4030
4031/*
4032 *  This does what I believe is the expected behaviour (or at least
4033 * acceptable for the the interpreter), if the code being executed
4034 * ever tries to change/bind a progv symbol, the symbol state will
4035 * be restored when exiting the progv block, so, code like:
4036 *	(progv '(*x*) '(1) (defvar *x* 10))
4037 * when exiting the block, will have *x* unbound, and not a dynamic
4038 * symbol; if it was already bound, will have the old value.
4039 *  Symbols already dynamic can be freely changed, even unbounded in
4040 * the progv block.
4041 */
4042LispObj *
4043Lisp_Progv(LispBuiltin *builtin)
4044/*
4045 progv symbols values &rest body
4046 */
4047{
4048    GC_ENTER();
4049    int head = lisp__data.env.length, i, count, ostk[32], *offsets;
4050    LispObj *result, *list, *symbol, *value;
4051    int jumped;
4052    char fstk[32], *flags;
4053    LispBlock *block;
4054    LispAtom *atom;
4055
4056    LispObj *symbols, *values, *body;
4057
4058    /* Possible states */
4059#define DYNAMIC_SYMBOL		1
4060#define GLOBAL_SYMBOL		2
4061#define UNBOUND_SYMBOL		3
4062
4063    body = ARGUMENT(2);
4064    values = ARGUMENT(1);
4065    symbols = ARGUMENT(0);
4066
4067    /* get symbol names */
4068    symbols = EVAL(symbols);
4069    GC_PROTECT(symbols);
4070
4071    /* get symbol values */
4072    values = EVAL(values);
4073    GC_PROTECT(values);
4074
4075    /* count/check symbols and allocate space to remember symbol state */
4076    for (count = 0, list = symbols; CONSP(list); count++, list = CDR(list)) {
4077	symbol = CAR(list);
4078	CHECK_SYMBOL(symbol);
4079	CHECK_CONSTANT(symbol);
4080    }
4081    if (count > sizeof(fstk)) {
4082	flags = LispMalloc(count);
4083	offsets = LispMalloc(count * sizeof(int));
4084    }
4085    else {
4086	flags = &fstk[0];
4087	offsets = &ostk[0];
4088    }
4089
4090    /* store flags and save old value if required */
4091    for (i = 0, list = symbols; i < count; i++, list = CDR(list)) {
4092	atom = CAR(list)->data.atom;
4093	if (atom->dyn)
4094	    flags[i] = DYNAMIC_SYMBOL;
4095	else if (atom->a_object) {
4096	    flags[i] = GLOBAL_SYMBOL;
4097	    offsets[i] = lisp__data.protect.length;
4098	    GC_PROTECT(atom->property->value);
4099	}
4100	else
4101	    flags[i] = UNBOUND_SYMBOL;
4102    }
4103
4104    /* bind the symbols */
4105    for (i = 0, list = symbols; i < count; i++, list = CDR(list)) {
4106	symbol = CAR(list);
4107	atom = symbol->data.atom;
4108	if (CONSP(values)) {
4109	    value = CAR(values);
4110	    values = CDR(values);
4111	}
4112	else
4113	    value = NIL;
4114	if (flags[i] != DYNAMIC_SYMBOL) {
4115	    if (!atom->a_object)
4116		LispSetAtomObjectProperty(atom, value);
4117	    else
4118		SETVALUE(atom, value);
4119	}
4120	else
4121	    LispAddVar(symbol, value);
4122    }
4123    /* bind dynamic symbols */
4124    lisp__data.env.head = lisp__data.env.length;
4125
4126    jumped = 0;
4127    result = NIL;
4128    block = LispBeginBlock(NIL, LispBlockProtect);
4129    if (setjmp(block->jmp) == 0) {
4130	for (; CONSP(body); body = CDR(body))
4131	    result = EVAL(CAR(body));
4132    }
4133
4134    /* restore symbols */
4135    for (i = 0, list = symbols; i < count; i++, list = CDR(list)) {
4136	symbol = CAR(list);
4137	atom = symbol->data.atom;
4138	if (flags[i] != DYNAMIC_SYMBOL) {
4139	    if (flags[i] == UNBOUND_SYMBOL)
4140		LispUnsetVar(symbol);
4141	    else {
4142		/* restore global symbol value */
4143		LispSetAtomObjectProperty(atom, lisp__data.protect.objects
4144					  [offsets[i]]);
4145		atom->dyn = 0;
4146	    }
4147	}
4148    }
4149    /* unbind dynamic symbols */
4150    lisp__data.env.head = lisp__data.env.length = head;
4151    GC_LEAVE();
4152
4153    if (count > sizeof(fstk)) {
4154	LispFree(flags);
4155	LispFree(offsets);
4156    }
4157
4158    LispEndBlock(block);
4159    if (!lisp__data.destroyed) {
4160	if (jumped)
4161	    result = lisp__data.block.block_ret;
4162    }
4163    else {
4164	/* check if there is an unwind-protect block */
4165	LispBlockUnwind(NULL);
4166
4167	/* no unwind-protect block, return to the toplevel */
4168	LispDestroy(".");
4169    }
4170
4171    return (result);
4172}
4173
4174LispObj *
4175Lisp_Provide(LispBuiltin *builtin)
4176/*
4177 provide module
4178 */
4179{
4180    LispObj *module, *obj;
4181
4182    module = ARGUMENT(0);
4183
4184    CHECK_STRING(module);
4185    for (obj = MOD; obj != NIL; obj = CDR(obj)) {
4186	if (STRLEN(CAR(obj)) == STRLEN(module) &&
4187	    memcmp(THESTR(CAR(obj)), THESTR(module), STRLEN(module)) == 0)
4188	    return (module);
4189    }
4190
4191    if (MOD == NIL)
4192	MOD = CONS(module, NIL);
4193    else {
4194	RPLACD(MOD, CONS(CAR(MOD), CDR(MOD)));
4195	RPLACA(MOD, module);
4196    }
4197
4198    LispSetVar(lisp__data.modules, MOD);
4199
4200    return (MOD);
4201}
4202
4203LispObj *
4204Lisp_Push(LispBuiltin *builtin)
4205/*
4206 push item place
4207 */
4208{
4209    LispObj *result, *list;
4210
4211    LispObj *item, *place;
4212
4213    place = ARGUMENT(1);
4214    item = ARGUMENT(0);
4215
4216    item = EVAL(item);
4217
4218    if (SYMBOLP(place)) {
4219	list = LispGetVar(place);
4220	if (list == NULL)
4221	    LispDestroy("EVAL: the variable %s is unbound", STROBJ(place));
4222	CHECK_CONSTANT(place);
4223	LispSetVar(place, result = CONS(item, list));
4224    }
4225    else {
4226	GC_ENTER();
4227	LispObj quote;
4228
4229	list = EVAL(place);
4230	result = CONS(item, list);
4231	GC_PROTECT(result);
4232	quote.type = LispQuote_t;
4233	quote.data.quote = result;
4234	APPLY2(Osetf, place, &quote);
4235	GC_LEAVE();
4236    }
4237
4238    return (result);
4239}
4240
4241LispObj *
4242Lisp_Pushnew(LispBuiltin *builtin)
4243/*
4244 pushnew item place &key key test test-not
4245 */
4246{
4247    GC_ENTER();
4248    LispObj *result, *list;
4249
4250    LispObj *item, *place, *key, *test, *test_not;
4251
4252    test_not = ARGUMENT(4);
4253    test = ARGUMENT(3);
4254    key = ARGUMENT(2);
4255    place = ARGUMENT(1);
4256    item = ARGUMENT(0);
4257
4258    /* Evaluate place */
4259    if (SYMBOLP(place)) {
4260	list = LispGetVar(place);
4261	if (list == NULL)
4262	    LispDestroy("EVAL: the variable %s is unbound", STROBJ(place));
4263	/* Do error checking now. */
4264	CHECK_CONSTANT(place);
4265    }
4266    else
4267	/* It is possible that list is not gc protected? */
4268	list = EVAL(place);
4269
4270    item = EVAL(item);
4271    GC_PROTECT(item);
4272    if (key != UNSPEC) {
4273	key = EVAL(key);
4274	GC_PROTECT(key);
4275    }
4276    if (test != UNSPEC) {
4277	test = EVAL(test);
4278	GC_PROTECT(test);
4279    }
4280    else if (test_not != UNSPEC) {
4281	test_not = EVAL(test_not);
4282	GC_PROTECT(test_not);
4283    }
4284
4285    result = LispAdjoin(builtin, item, list, key, test, test_not);
4286
4287    /* Item already in list */
4288    if (result == list) {
4289	GC_LEAVE();
4290
4291	return (result);
4292    }
4293
4294    if (SYMBOLP(place)) {
4295	CHECK_CONSTANT(place);
4296	LispSetVar(place, result);
4297    }
4298    else {
4299	LispObj quote;
4300
4301	GC_PROTECT(result);
4302	quote.type = LispQuote_t;
4303	quote.data.quote = result;
4304	APPLY2(Osetf, place, &quote);
4305    }
4306    GC_LEAVE();
4307
4308    return (result);
4309}
4310
4311LispObj *
4312Lisp_Quit(LispBuiltin *builtin)
4313/*
4314 quit &optional status
4315 */
4316{
4317    int status = 0;
4318    LispObj *ostatus;
4319
4320    ostatus = ARGUMENT(0);
4321
4322    if (FIXNUMP(ostatus))
4323	status = (int)FIXNUM_VALUE(ostatus);
4324    else if (ostatus != UNSPEC)
4325	LispDestroy("%s: bad exit status argument %s",
4326		    STRFUN(builtin), STROBJ(ostatus));
4327
4328    exit(status);
4329}
4330
4331LispObj *
4332Lisp_Quote(LispBuiltin *builtin)
4333/*
4334 quote object
4335 */
4336{
4337    LispObj *object;
4338
4339    object = ARGUMENT(0);
4340
4341    return (object);
4342}
4343
4344LispObj *
4345Lisp_Replace(LispBuiltin *builtin)
4346/*
4347 replace sequence1 sequence2 &key start1 end1 start2 end2
4348 */
4349{
4350    long length, length1, length2, start1, end1, start2, end2;
4351    LispObj *sequence1, *sequence2, *ostart1, *oend1, *ostart2, *oend2;
4352
4353    oend2 = ARGUMENT(5);
4354    ostart2 = ARGUMENT(4);
4355    oend1 = ARGUMENT(3);
4356    ostart1 = ARGUMENT(2);
4357    sequence2 = ARGUMENT(1);
4358    sequence1 = ARGUMENT(0);
4359
4360    LispCheckSequenceStartEnd(builtin, sequence1, ostart1, oend1,
4361			      &start1, &end1, &length1);
4362    LispCheckSequenceStartEnd(builtin, sequence2, ostart2, oend2,
4363			      &start2, &end2, &length2);
4364
4365    if (start1 == end1 || start2 == end2)
4366	return (sequence1);
4367
4368    length = end1 - start1;
4369    if (length > end2 - start2)
4370	length = end2 - start2;
4371
4372    if (STRINGP(sequence1)) {
4373	CHECK_STRING_WRITABLE(sequence1);
4374	if (!STRINGP(sequence2))
4375	    LispDestroy("%s: cannot store %s in %s",
4376			STRFUN(builtin), STROBJ(sequence2), THESTR(sequence1));
4377
4378	memmove(THESTR(sequence1) + start1, THESTR(sequence2) + start2, length);
4379    }
4380    else {
4381	int i;
4382	LispObj *from, *to;
4383
4384	if (ARRAYP(sequence1))
4385	    sequence1 = sequence1->data.array.list;
4386	if (ARRAYP(sequence2))
4387	    sequence2 = sequence2->data.array.list;
4388
4389	/* adjust pointers */
4390	for (i = 0, from = sequence2; i < start2; i++, from = CDR(from))
4391	    ;
4392	for (i = 0, to = sequence1; i < start1; i++, to = CDR(to))
4393	    ;
4394
4395	/* copy data */
4396	for (i = 0; i < length; i++, from = CDR(from), to = CDR(to))
4397	    RPLACA(to, CAR(from));
4398    }
4399
4400    return (sequence1);
4401}
4402
4403static LispObj *
4404LispDeleteOrRemoveDuplicates(LispBuiltin *builtin, int function)
4405/*
4406 delete-duplicates sequence &key from-end test test-not start end key
4407 remove-duplicates sequence &key from-end test test-not start end key
4408 */
4409{
4410    GC_ENTER();
4411    int code, expect, value = 0;
4412    long i, j, start, end, length, count;
4413    LispObj *lambda, *result, *cons, *compare;
4414
4415    LispObj *sequence, *from_end, *test, *test_not, *ostart, *oend, *key;
4416
4417    key = ARGUMENT(6);
4418    oend = ARGUMENT(5);
4419    ostart = ARGUMENT(4);
4420    test_not = ARGUMENT(3);
4421    test = ARGUMENT(2);
4422    from_end = ARGUMENT(1);
4423    if (from_end == UNSPEC)
4424	from_end = NIL;
4425    sequence = ARGUMENT(0);
4426
4427    LispCheckSequenceStartEnd(builtin, sequence, ostart, oend,
4428			      &start, &end, &length);
4429
4430    /* Check if need to do something */
4431    if (start == end)
4432	return (sequence);
4433
4434    CHECK_TEST();
4435
4436    /* Initialize */
4437    count = 0;
4438
4439    result = cons = NIL;
4440    if (STRINGP(sequence)) {
4441	char *ptr, *string, *buffer = LispMalloc(length + 1);
4442
4443	/* Use same code, update start/end offsets */
4444	if (from_end != NIL) {
4445	    i = length - start;
4446	    start = length - end;
4447	    end = i;
4448	}
4449
4450	if (from_end == NIL)
4451	    string = THESTR(sequence);
4452	else {
4453	    /* Make a reversed copy of the sequence */
4454	    string = LispMalloc(length + 1);
4455	    for (ptr = THESTR(sequence) + length - 1, i = 0; i < length; i++)
4456		string[i] = *ptr--;
4457	    string[i] = '\0';
4458	}
4459
4460	ptr = buffer;
4461	/* Copy leading bytes */
4462	for (i = 0; i < start; i++)
4463	    *ptr++ = string[i];
4464
4465	compare = SCHAR(string[i]);
4466	if (key != UNSPEC)
4467	    compare = APPLY1(key, compare);
4468	result = cons = CONS(compare, NIL);
4469	GC_PROTECT(result);
4470	for (++i; i < end; i++) {
4471	    compare = SCHAR(string[i]);
4472	    if (key != UNSPEC)
4473		compare = APPLY1(key, compare);
4474	    RPLACD(cons, CONS(compare, NIL));
4475	    cons = CDR(cons);
4476	}
4477
4478	for (i = start; i < end; i++, result = CDR(result)) {
4479	    compare = CAR(result);
4480	    for (j = i + 1, cons = CDR(result); j < end; j++, cons = CDR(cons)) {
4481		value = FCOMPARE(lambda, compare, CAR(cons), code);
4482		if (value == expect)
4483		    break;
4484	    }
4485	    if (value != expect)
4486		*ptr++ = string[i];
4487	    else
4488		++count;
4489	}
4490
4491	if (count) {
4492	    /* Copy ending bytes */
4493	    for (; i <= length; i++)   /* Also copy the ending nul */
4494		*ptr++ = string[i];
4495
4496	    if (from_end == NIL)
4497		ptr = buffer;
4498	    else {
4499		for (i = 0, ptr = buffer + strlen(buffer);
4500		     ptr > buffer;
4501		     i++)
4502		    string[i] = *--ptr;
4503		string[i] = '\0';
4504		ptr = string;
4505		LispFree(buffer);
4506	    }
4507	    if (function == REMOVE)
4508		result = STRING2(ptr);
4509	    else {
4510		CHECK_STRING_WRITABLE(sequence);
4511		result = sequence;
4512		free(THESTR(result));
4513		THESTR(result) = ptr;
4514		LispMused(ptr);
4515	    }
4516	}
4517	else {
4518	    result = sequence;
4519	    if (from_end != NIL)
4520		LispFree(string);
4521	}
4522    }
4523    else {
4524	long xlength = end - start;
4525	LispObj *list, *object, **kobjects = NULL, **xobjects;
4526	LispObj **objects = LispMalloc(sizeof(LispObj*) * xlength);
4527
4528	if (!CONSP(sequence))
4529	    object = sequence->data.array.list;
4530	else
4531	    object = sequence;
4532	list = object;
4533
4534	for (i = 0; i < start; i++)
4535	    object = CDR(object);
4536
4537	/* Put data in a vector */
4538	if (from_end == NIL) {
4539	    for (i = 0; i < xlength; i++, object = CDR(object))
4540		objects[i] = CAR(object);
4541	}
4542	else {
4543	    for (i = xlength - 1; i >= 0; i--, object = CDR(object))
4544		objects[i] = CAR(object);
4545	}
4546
4547	/* Apply key predicate if required */
4548	if (key != UNSPEC) {
4549	    kobjects = LispMalloc(sizeof(LispObj*) * xlength);
4550	    for (i = 0; i < xlength; i++) {
4551		kobjects[i] = APPLY1(key, objects[i]);
4552		GC_PROTECT(kobjects[i]);
4553	    }
4554	    xobjects = kobjects;
4555	}
4556	else
4557	    xobjects = objects;
4558
4559	/* Check if needs to remove something */
4560	for (i = 0; i < xlength; i++) {
4561	    compare = xobjects[i];
4562	    for (j = i + 1; j < xlength; j++) {
4563		value = FCOMPARE(lambda, compare, xobjects[j], code);
4564		if (value == expect) {
4565		    objects[i] = NULL;
4566		    ++count;
4567		    break;
4568		}
4569	    }
4570	}
4571
4572	if (count) {
4573	    /* Create/set result list */
4574	    object = list;
4575
4576	    if (start) {
4577		/* Skip first elements of resulting list */
4578		if (function == REMOVE) {
4579		    result = cons = CONS(CAR(object), NIL);
4580		    GC_PROTECT(result);
4581		    for (i = 1, object = CDR(object);
4582			 i < start;
4583			 i++, object = CDR(object)) {
4584			RPLACD(cons, CONS(CAR(object), NIL));
4585			cons = CDR(cons);
4586		    }
4587		}
4588		else {
4589		    result = cons = object;
4590		    for (i = 1; i < start; i++, cons = CDR(cons))
4591			;
4592		}
4593	    }
4594	    else if (function == DELETE)
4595		result = list;
4596
4597	    /* Skip initial removed elements */
4598	    if (function == REMOVE) {
4599		for (i = 0; objects[i] == NULL && i < xlength; i++)
4600		    ;
4601	    }
4602	    else
4603		i = 0;
4604
4605	    if (i < xlength) {
4606		int xstart, xlimit, xinc;
4607
4608		if (from_end == NIL) {
4609		    xstart = i;
4610		    xlimit = xlength;
4611		    xinc = 1;
4612		}
4613		else {
4614		    xstart = xlength - 1;
4615		    xlimit = i - 1;
4616		    xinc = -1;
4617		}
4618
4619		if (function == REMOVE) {
4620		    for (i = xstart; i != xlimit; i += xinc) {
4621			if (objects[i] != NULL) {
4622			    if (result == NIL) {
4623				result = cons = CONS(objects[i], NIL);
4624				GC_PROTECT(result);
4625			    }
4626			    else {
4627				RPLACD(cons, CONS(objects[i], NIL));
4628				cons = CDR(cons);
4629			    }
4630			}
4631		    }
4632		}
4633		else {
4634		    /* Delete duplicates */
4635		    for (i = xstart; i != xlimit; i += xinc) {
4636			if (objects[i] == NULL) {
4637			    if (cons == NIL) {
4638				if (CONSP(CDR(result))) {
4639				    RPLACA(result, CADR(result));
4640				    RPLACD(result, CDDR(result));
4641				}
4642				else {
4643				    RPLACA(result, CDR(result));
4644				    RPLACD(result, NIL);
4645				}
4646			    }
4647			    else {
4648				if (CONSP(CDR(cons)))
4649				    RPLACD(cons, CDDR(cons));
4650				else
4651				    RPLACD(cons, NIL);
4652			    }
4653			}
4654			else {
4655			    if (cons == NIL)
4656				cons = result;
4657			    else
4658				cons = CDR(cons);
4659			}
4660		    }
4661		}
4662	    }
4663	    if (end < length && function == REMOVE) {
4664		for (i = start; i < end; i++, object = CDR(object))
4665		    ;
4666		if (result == NIL) {
4667		    result = cons = CONS(CAR(object), NIL);
4668		    GC_PROTECT(result);
4669		    ++i;
4670		    object = CDR(object);
4671		}
4672		for (; i < length; i++, object = CDR(object)) {
4673		    RPLACD(cons, CONS(CAR(object), NIL));
4674		    cons = CDR(cons);
4675		}
4676	    }
4677	}
4678	else
4679	    result = sequence;
4680	LispFree(objects);
4681	if (key != UNSPEC)
4682	    LispFree(kobjects);
4683
4684	if (count && !CONSP(sequence)) {
4685	    if (function == REMOVE)
4686		result = VECTOR(result);
4687	    else {
4688		length = FIXNUM_VALUE(CAR(sequence->data.array.dim)) - count;
4689		CAR(sequence->data.array.dim) = FIXNUM(length);
4690		result = sequence;
4691	    }
4692	}
4693    }
4694    GC_LEAVE();
4695
4696    return (result);
4697}
4698
4699LispObj *
4700Lisp_RemoveDuplicates(LispBuiltin *builtin)
4701/*
4702 remove-duplicates sequence &key from-end test test-not start end key
4703 */
4704{
4705    return (LispDeleteOrRemoveDuplicates(builtin, REMOVE));
4706}
4707
4708static LispObj *
4709LispDeleteRemoveXSubstitute(LispBuiltin *builtin,
4710			    int function, int comparison)
4711/*
4712 delete item sequence &key from-end test test-not start end count key
4713 delete-if predicate sequence &key from-end start end count key
4714 delete-if-not predicate sequence &key from-end start end count key
4715 remove item sequence &key from-end test test-not start end count key
4716 remove-if predicate sequence &key from-end start end count key
4717 remove-if-not predicate sequence &key from-end start end count key
4718 substitute newitem olditem sequence &key from-end test test-not start end count key
4719 substitute-if newitem test sequence &key from-end start end count key
4720 substitute-if-not newitem test sequence &key from-end start end count key
4721 nsubstitute newitem olditem sequence &key from-end test test-not start end count key
4722 nsubstitute-if newitem test sequence &key from-end start end count key
4723 nsubstitute-if-not newitem test sequence &key from-end start end count key
4724 */
4725{
4726    GC_ENTER();
4727    int code, expect, value, inplace, substitute;
4728    long i, j, start, end, length, copy, count, xstart, xend, xinc, xlength;
4729
4730    LispObj *result, *compare;
4731
4732    LispObj *item, *newitem, *lambda, *sequence, *from_end,
4733	    *test, *test_not, *ostart, *oend, *ocount, *key;
4734
4735    substitute = function == SUBSTITUTE || function == NSUBSTITUTE;
4736    if (!substitute)
4737	i = comparison == NONE ? 8 : 6;
4738    else /* substitute */
4739	i = comparison == NONE ? 9 : 7;
4740
4741    /* Get function arguments */
4742    key = ARGUMENT(i);			--i;
4743    ocount = ARGUMENT(i);		--i;
4744    oend = ARGUMENT(i);			--i;
4745    ostart = ARGUMENT(i);		--i;
4746    if (comparison == NONE) {
4747	test_not = ARGUMENT(i);		--i;
4748	test = ARGUMENT(i);		--i;
4749    }
4750    else
4751	test_not = test = UNSPEC;
4752    from_end = ARGUMENT(i);		--i;
4753    if (from_end == UNSPEC)
4754	from_end = NIL;
4755    sequence = ARGUMENT(i);		--i;
4756    if (comparison != NONE) {
4757	lambda = ARGUMENT(i);	--i;
4758	if (substitute)
4759	    newitem = ARGUMENT(0);
4760	else
4761	    newitem = NIL;
4762	item = NIL;
4763    }
4764    else {
4765	lambda = NIL;
4766	if (substitute) {
4767	    item = ARGUMENT(1);
4768	    newitem = ARGUMENT(0);
4769	}
4770	else {
4771	    item = ARGUMENT(0);
4772	    newitem = NIL;
4773	}
4774    }
4775
4776    /* Check if argument is a valid sequence, and if start/end
4777     * are correctly specified. */
4778    LispCheckSequenceStartEnd(builtin, sequence, ostart, oend,
4779			      &start, &end, &length);
4780
4781    /* Check count argument */
4782    if (ocount == UNSPEC) {
4783	count = length;
4784	/* Doesn't matter, but left to right should be slightly faster */
4785	from_end = NIL;
4786    }
4787    else {
4788	CHECK_INDEX(ocount);
4789	count = FIXNUM_VALUE(ocount);
4790    }
4791
4792    /* Check if need to do something */
4793    if (start == end || count == 0)
4794	return (sequence);
4795
4796    CHECK_TEST_0();
4797
4798    /* Resolve comparison function, and expected result of comparison */
4799    if (comparison == NONE) {
4800	if (test_not == UNSPEC) {
4801	    if (test == UNSPEC)
4802		lambda = Oeql;
4803	    else
4804		lambda = test;
4805	    expect = 1;
4806	}
4807	else {
4808	    lambda = test_not;
4809	    expect = 0;
4810	}
4811	FUNCTION_CHECK(lambda);
4812    }
4813    else
4814	expect = comparison == IFNOT ? 0 : 1;
4815
4816    /* Check for fast path to comparison function */
4817    code = FCODE(lambda);
4818
4819    /* Initialize for loop */
4820    copy = count;
4821    result = sequence;
4822    inplace = function == DELETE || function == NSUBSTITUTE;
4823    xlength = end - start;
4824
4825    /* String is easier */
4826    if (STRINGP(sequence)) {
4827	char *buffer, *string;
4828
4829	if (comparison == NONE) {
4830	    CHECK_SCHAR(item);
4831	}
4832	if (substitute) {
4833	    CHECK_SCHAR(newitem);
4834	}
4835
4836	if (from_end == NIL) {
4837	    xstart = start;
4838	    xend = end;
4839	    xinc = 1;
4840	}
4841	else {
4842	    xstart = end - 1;
4843	    xend = start - 1;
4844	    xinc = -1;
4845	}
4846
4847	string = THESTR(sequence);
4848	buffer = LispMalloc(length + 1);
4849
4850	/* Copy leading bytes, if any */
4851	for (i = 0; i < start; i++)
4852	    buffer[i] = string[i];
4853
4854	for (j = xstart; i != xend && count > 0; i += xinc) {
4855	    compare = SCHAR(string[i]);
4856	    if (key != UNSPEC) {
4857		compare = APPLY1(key, compare);
4858		/* Value returned by the key predicate may not be protected */
4859		GC_PROTECT(compare);
4860		if (comparison == NONE)
4861		    value = FCOMPARE(lambda, item, compare, code);
4862		else
4863		    value = APPLY1(lambda, compare) != NIL;
4864		/* Unprotect value returned by the key predicate */
4865		GC_LEAVE();
4866	    }
4867	    else {
4868		if (comparison == NONE)
4869		    value = FCOMPARE(lambda, item, compare, code);
4870		else
4871		    value = APPLY1(lambda, compare) != NIL;
4872	    }
4873
4874	    if (value != expect) {
4875		buffer[j] = string[i];
4876		j += xinc;
4877	    }
4878	    else {
4879		if (substitute) {
4880		    buffer[j] = SCHAR_VALUE(newitem);
4881		    j += xinc;
4882		}
4883		else
4884		    --count;
4885	    }
4886	}
4887
4888	if (count != copy && from_end != NIL)
4889	    memmove(buffer + start, buffer + copy - count, count);
4890
4891	/* Copy remaining bytes, if any */
4892	for (; i < length; i++, j++)
4893	    buffer[j] = string[i];
4894	buffer[j] = '\0';
4895
4896	xlength = length - (copy - count);
4897	if (inplace) {
4898	    CHECK_STRING_WRITABLE(sequence);
4899	    /* result is a pointer to sequence */
4900	    LispFree(THESTR(sequence));
4901	    LispMused(buffer);
4902	    THESTR(sequence) = buffer;
4903	    STRLEN(sequence) = xlength;
4904	}
4905	else
4906	    result = LSTRING2(buffer, xlength);
4907    }
4908
4909    /* If inplace, need to update CAR and CDR of sequence */
4910    else {
4911	LispObj *list, *object;
4912	LispObj **objects = LispMalloc(sizeof(LispObj*) * xlength);
4913
4914	if (!CONSP(sequence))
4915	    list = sequence->data.array.list;
4916	else
4917	    list = sequence;
4918
4919	/* Put data in a vector */
4920	for (i = 0, object = list; i < start; i++)
4921	    object = CDR(object);
4922
4923	for (i = 0; i < xlength; i++, object = CDR(object))
4924	    objects[i] = CAR(object);
4925
4926	if (from_end == NIL) {
4927	    xstart = 0;
4928	    xend = xlength;
4929	    xinc = 1;
4930	}
4931	else {
4932	    xstart = xlength - 1;
4933	    xend = -1;
4934	    xinc = -1;
4935	}
4936
4937	/* Check if needs to remove something */
4938	for (i = xstart; i != xend && count > 0; i += xinc) {
4939	    compare = objects[i];
4940	    if (key != UNSPEC) {
4941		compare = APPLY1(key, compare);
4942		GC_PROTECT(compare);
4943		if (comparison == NONE)
4944		    value = FCOMPARE(lambda, item, compare, code);
4945		else
4946		    value = APPLY1(lambda, compare) != NIL;
4947		GC_LEAVE();
4948	    }
4949	    else {
4950		if (comparison == NONE)
4951		    value = FCOMPARE(lambda, item, compare, code);
4952		else
4953		    value = APPLY1(lambda, compare) != NIL;
4954	    }
4955	    if (value == expect) {
4956		if (substitute)
4957		    objects[i] = newitem;
4958		else
4959		    objects[i] = NULL;
4960		--count;
4961	    }
4962	}
4963
4964	if (copy != count) {
4965	    LispObj *cons = NIL;
4966
4967	    i = 0;
4968	    object = list;
4969	    if (inplace) {
4970		/* While result is NIL, skip initial elements of sequence */
4971		result = start ? list : NIL;
4972
4973		/* Skip initial elements, if any */
4974		for (; i < start; i++, cons = object, object = CDR(object))
4975		    ;
4976	    }
4977	    /* Copy initial elements, if any */
4978	    else {
4979		result = NIL;
4980		if (start) {
4981		    result = cons = CONS(CAR(list), NIL);
4982		    GC_PROTECT(result);
4983		    for (++i, object = CDR(list);
4984			 i < start;
4985			 i++, object = CDR(object)) {
4986			RPLACD(cons, CONS(CAR(object), NIL));
4987		 	cons = CDR(cons);
4988		    }
4989		}
4990	    }
4991
4992	    /* Skip initial removed elements, if any */
4993	    for (i = 0; i < xlength && objects[i] == NULL; i++)
4994		;
4995
4996	    for (i = 0; i < xlength; i++, object = CDR(object)) {
4997		if (objects[i]) {
4998		    if (inplace) {
4999			if (result == NIL)
5000			    result = cons = object;
5001			else {
5002			    RPLACD(cons, object);
5003			    cons = CDR(cons);
5004			}
5005			if (function == NSUBSTITUTE)
5006			    RPLACA(cons, objects[i]);
5007		    }
5008		    else {
5009			if (result == NIL) {
5010			    result = cons = CONS(objects[i], NIL);
5011			    GC_PROTECT(result);
5012			}
5013			else {
5014			    RPLACD(cons, CONS(objects[i], NIL));
5015			    cons = CDR(cons);
5016			}
5017		    }
5018		}
5019	    }
5020
5021	    if (inplace) {
5022		if (result == NIL)
5023		    result = object;
5024		else
5025		    RPLACD(cons, object);
5026
5027		if (!CONSP(sequence)) {
5028		    result = sequence;
5029		    CAR(result)->data.array.dim =
5030			FIXNUM(length - (copy - count));
5031		}
5032	    }
5033	    else if (end < length) {
5034		i = end;
5035		/* Copy ending elements, if any */
5036		if (result == NIL) {
5037		    result = cons = CONS(CAR(object), NIL);
5038		    GC_PROTECT(result);
5039		    object = CDR(object);
5040		    i++;
5041		}
5042		for (; i < length; i++, object = CDR(object)) {
5043		    RPLACD(cons, CONS(CAR(object), NIL));
5044		    cons = CDR(cons);
5045		}
5046	    }
5047	}
5048
5049	/* Release comparison vector */
5050	LispFree(objects);
5051    }
5052
5053    GC_LEAVE();
5054
5055    return (result);
5056}
5057
5058LispObj *
5059Lisp_Remove(LispBuiltin *builtin)
5060/*
5061 remove item sequence &key from-end test test-not start end count key
5062 */
5063{
5064    return (LispDeleteRemoveXSubstitute(builtin, REMOVE, NONE));
5065}
5066
5067LispObj *
5068Lisp_RemoveIf(LispBuiltin *builtin)
5069/*
5070 remove-if predicate sequence &key from-end start end count key
5071 */
5072{
5073    return (LispDeleteRemoveXSubstitute(builtin, REMOVE, IF));
5074}
5075
5076LispObj *
5077Lisp_RemoveIfNot(LispBuiltin *builtin)
5078/*
5079 remove-if-not predicate sequence &key from-end start end count key
5080 */
5081{
5082    return (LispDeleteRemoveXSubstitute(builtin, REMOVE, IFNOT));
5083}
5084
5085LispObj *
5086Lisp_Remprop(LispBuiltin *builtin)
5087/*
5088 remprop symbol indicator
5089 */
5090{
5091    LispObj *symbol, *indicator;
5092
5093    indicator = ARGUMENT(1);
5094    symbol = ARGUMENT(0);
5095
5096    CHECK_SYMBOL(symbol);
5097
5098    return (LispRemAtomProperty(symbol->data.atom, indicator));
5099}
5100
5101LispObj *
5102Lisp_Return(LispBuiltin *builtin)
5103/*
5104 return &optional result
5105 */
5106{
5107    unsigned blevel = lisp__data.block.block_level;
5108
5109    LispObj *result;
5110
5111    result = ARGUMENT(0);
5112
5113    while (blevel) {
5114	LispBlock *block = lisp__data.block.block[--blevel];
5115
5116	if (block->type == LispBlockClosure)
5117	    /* if reached a function call */
5118	    break;
5119	if (block->type == LispBlockTag && block->tag == NIL) {
5120	    lisp__data.block.block_ret = result == UNSPEC ? NIL : EVAL(result);
5121	    LispBlockUnwind(block);
5122	    BLOCKJUMP(block);
5123	}
5124    }
5125    LispDestroy("%s: no visible NIL block", STRFUN(builtin));
5126
5127    /*NOTREACHED*/
5128    return (NIL);
5129}
5130
5131LispObj *
5132Lisp_ReturnFrom(LispBuiltin *builtin)
5133/*
5134 return-from name &optional result
5135 */
5136{
5137    unsigned blevel = lisp__data.block.block_level;
5138
5139    LispObj *name, *result;
5140
5141    result = ARGUMENT(1);
5142    name = ARGUMENT(0);
5143
5144    if (name != NIL && name != T && !SYMBOLP(name))
5145	LispDestroy("%s: %s is not a valid block name",
5146		    STRFUN(builtin), STROBJ(name));
5147
5148    while (blevel) {
5149	LispBlock *block = lisp__data.block.block[--blevel];
5150
5151	if (name == block->tag &&
5152	    (block->type == LispBlockTag || block->type == LispBlockClosure)) {
5153	    lisp__data.block.block_ret = result == UNSPEC ? NIL : EVAL(result);
5154	    LispBlockUnwind(block);
5155	    BLOCKJUMP(block);
5156	}
5157	if (block->type == LispBlockClosure)
5158	    /* can use return-from only in the current function */
5159	    break;
5160    }
5161    LispDestroy("%s: no visible block named %s",
5162		STRFUN(builtin), STROBJ(name));
5163
5164    /*NOTREACHED*/
5165    return (NIL);
5166}
5167
5168static LispObj *
5169LispXReverse(LispBuiltin *builtin, int inplace)
5170/*
5171 nreverse sequence
5172 reverse sequence
5173 */
5174{
5175    long length;
5176    LispObj *list, *result = NIL;
5177
5178    LispObj *sequence;
5179
5180    sequence = ARGUMENT(0);
5181
5182    /* Do error checking for arrays and object type. */
5183    length = LispLength(sequence);
5184    if (length <= 1)
5185	return (sequence);
5186
5187    switch (XOBJECT_TYPE(sequence)) {
5188	case LispString_t: {
5189	    long i;
5190	    char *from, *to;
5191
5192	    from = THESTR(sequence) + length - 1;
5193	    if (inplace) {
5194		char temp;
5195
5196		CHECK_STRING_WRITABLE(sequence);
5197		to = THESTR(sequence);
5198		for (i = 0; i < length / 2; i++) {
5199		    temp = to[i];
5200		    to[i] = from[-i];
5201		    from[-i] = temp;
5202		}
5203		result = sequence;
5204	    }
5205	    else {
5206		to = LispMalloc(length + 1);
5207		to[length] = '\0';
5208		for (i = 0; i < length; i++)
5209		    to[i] = from[-i];
5210		result = STRING2(to);
5211	    }
5212	}   return (result);
5213	case LispCons_t:
5214	    if (inplace) {
5215		long i, j;
5216		LispObj *temp;
5217
5218		/* For large lists this can be very slow, but for small
5219		 * amounts of data, this avoid allocating a buffer to
5220		 * to store the CAR of the sequence. This is only done
5221		 * to not destroy the contents of a variable.
5222		 */
5223		for (i = 0, list = sequence;
5224		     i < (length + 1) / 2;
5225		     i++, list = CDR(list))
5226		    ;
5227		length /= 2;
5228		for (i = 0; i < length; i++, list = CDR(list)) {
5229		    for (j = length - i - 1, result = sequence;
5230			 j > 0;
5231			 j--, result = CDR(result))
5232			;
5233		    temp = CAR(list);
5234		    RPLACA(list, CAR(result));
5235		    RPLACA(result, temp);
5236		}
5237		return (sequence);
5238	    }
5239	    list = sequence;
5240	    break;
5241	case LispArray_t:
5242	    if (inplace) {
5243		sequence->data.array.list =
5244		    LispReverse(sequence->data.array.list);
5245		return (sequence);
5246	    }
5247	    list = sequence->data.array.list;
5248	    break;
5249	default:	/* LispNil_t */
5250	    return (result);
5251    }
5252
5253    {
5254	GC_ENTER();
5255	LispObj *cons;
5256
5257	result = cons = CONS(CAR(list), NIL);
5258	GC_PROTECT(result);
5259	for (list = CDR(list); CONSP(list); list = CDR(list)) {
5260	    RPLACD(cons, CONS(CAR(list), NIL));
5261	    cons = CDR(cons);
5262	}
5263	result = LispReverse(result);
5264
5265	GC_LEAVE();
5266    }
5267
5268    if (ARRAYP(sequence)) {
5269	list = result;
5270
5271	result = LispNew(list, NIL);
5272	result->type = LispArray_t;
5273	result->data.array.list = list;
5274	result->data.array.dim = sequence->data.array.dim;
5275	result->data.array.rank = sequence->data.array.rank;
5276	result->data.array.type = sequence->data.array.type;
5277	result->data.array.zero = sequence->data.array.zero;
5278    }
5279
5280    return (result);
5281}
5282
5283LispObj *
5284Lisp_Reverse(LispBuiltin *builtin)
5285/*
5286 reverse sequence
5287 */
5288{
5289    return (LispXReverse(builtin, 0));
5290}
5291
5292LispObj *
5293Lisp_Rplaca(LispBuiltin *builtin)
5294/*
5295 rplaca place value
5296 */
5297{
5298    LispObj *place, *value;
5299
5300    value = ARGUMENT(1);
5301    place = ARGUMENT(0);
5302
5303    CHECK_CONS(place);
5304    RPLACA(place, value);
5305
5306    return (place);
5307}
5308
5309LispObj *
5310Lisp_Rplacd(LispBuiltin *builtin)
5311/*
5312 rplacd place value
5313 */
5314{
5315    LispObj *place, *value;
5316
5317    value = ARGUMENT(1);
5318    place = ARGUMENT(0);
5319
5320    CHECK_CONS(place);
5321    RPLACD(place, value);
5322
5323    return (place);
5324}
5325
5326LispObj *
5327Lisp_Search(LispBuiltin *builtin)
5328/*
5329 search sequence1 sequence2 &key from-end test test-not key start1 start2 end1 end2
5330 */
5331{
5332    int code = 0, expect, value;
5333    long start1, start2, end1, end2, length1, length2, off1, off2, offset = -1;
5334    LispObj *cmp1, *cmp2, *list1 = NIL, *lambda;
5335    SeqInfo seq1, seq2;
5336
5337    LispObj *sequence1, *sequence2, *from_end, *test, *test_not,
5338	    *key, *ostart1, *ostart2, *oend1, *oend2;
5339
5340    oend2 = ARGUMENT(9);
5341    oend1 = ARGUMENT(8);
5342    ostart2 = ARGUMENT(7);
5343    ostart1 = ARGUMENT(6);
5344    key = ARGUMENT(5);
5345    test_not = ARGUMENT(4);
5346    test = ARGUMENT(3);
5347    from_end = ARGUMENT(2);
5348    sequence2 = ARGUMENT(1);
5349    sequence1 = ARGUMENT(0);
5350
5351    LispCheckSequenceStartEnd(builtin, sequence1, ostart1, oend1,
5352			      &start1, &end1, &length1);
5353    LispCheckSequenceStartEnd(builtin, sequence2, ostart2, oend2,
5354			      &start2, &end2, &length2);
5355
5356    /* Check for special conditions */
5357    if (start1 == end1)
5358	return (FIXNUM(end2));
5359    else if (start2 == end2)
5360	return (start1 == end1 ? FIXNUM(start2) : NIL);
5361
5362    CHECK_TEST();
5363
5364    if (from_end == UNSPEC)
5365	from_end = NIL;
5366
5367    SETSEQ(seq1, sequence1);
5368    SETSEQ(seq2, sequence2);
5369
5370    length1 = end1 - start1;
5371    length2 = end2 - start2;
5372
5373    /* update start of sequences */
5374    if (start1) {
5375	if (seq1.type == LispString_t)
5376	    seq1.data.string += start1;
5377	else {
5378	    for (cmp1 = seq1.data.list; start1; cmp1 = CDR(cmp1), --start1)
5379		;
5380	    seq1.data.list = cmp1;
5381	}
5382	end1 = length1;
5383    }
5384    if (start2) {
5385	if (seq2.type == LispString_t)
5386	    seq2.data.string += start2;
5387	else {
5388	    for (cmp2 = seq2.data.list; start2; cmp2 = CDR(cmp2), --start2)
5389		;
5390	    seq2.data.list = cmp2;
5391	}
5392	end2 = length2;
5393    }
5394
5395    /* easier case */
5396    if (from_end == NIL) {
5397	LispObj *list2 = NIL;
5398
5399	/* while a match is possible */
5400	while (end2 - start2 >= length1) {
5401
5402	    /* prepare to search */
5403	    off1 = 0;
5404	    off2 = start2;
5405	    if (seq1.type != LispString_t)
5406		list1 = seq1.data.list;
5407	    if (seq2.type != LispString_t)
5408		list2 = seq2.data.list;
5409
5410	    /* for every element that must match in sequence1 */
5411	    while (off1 < length1) {
5412		if (seq1.type == LispString_t)
5413		    cmp1 = SCHAR(seq1.data.string[off1]);
5414		else
5415		    cmp1 = CAR(list1);
5416		if (seq2.type == LispString_t)
5417		    cmp2 = SCHAR(seq2.data.string[off2]);
5418		else
5419		    cmp2 = CAR(list2);
5420		if (key != UNSPEC) {
5421		    cmp1 = APPLY1(key, cmp1);
5422		    cmp2 = APPLY1(key, cmp2);
5423		}
5424
5425		/* compare elements */
5426		value = FCOMPARE(lambda, cmp1, cmp2, code);
5427		if (value != expect)
5428		    break;
5429
5430		/* update offsets/sequence pointers */
5431		++off1;
5432		++off2;
5433		if (seq1.type != LispString_t)
5434		    list1 = CDR(list1);
5435		if (seq2.type != LispString_t)
5436		    list2 = CDR(list2);
5437	    }
5438
5439	    /* if everything matched */
5440	    if (off1 == end1) {
5441		offset = off2 - length1;
5442		break;
5443	    }
5444
5445	    /* update offset/sequence2 pointer */
5446	    ++start2;
5447	    if (seq2.type != LispString_t)
5448		seq2.data.list = CDR(seq2.data.list);
5449	}
5450    }
5451    else {
5452	/* allocate vector if required, only list2 requires it.
5453	 * list1 can be traversed forward */
5454	if (seq2.type != LispString_t) {
5455	    cmp2 = seq2.data.list;
5456	    seq2.data.vector = LispMalloc(sizeof(LispObj*) * length2);
5457	    for (off2 = 0; off2 < end2; off2++, cmp2 = CDR(cmp2))
5458		seq2.data.vector[off2] = CAR(cmp2);
5459	}
5460
5461	/* while a match is possible */
5462	while (end2 >= length1) {
5463
5464	    /* prepare to search */
5465	    off1 = 0;
5466	    off2 = end2 - length1;
5467	    if (seq1.type != LispString_t)
5468		list1 = seq1.data.list;
5469
5470	    /* for every element that must match in sequence1 */
5471	    while (off1 < end1) {
5472		if (seq1.type == LispString_t)
5473		    cmp1 = SCHAR(seq1.data.string[off1]);
5474		else
5475		    cmp1 = CAR(list1);
5476		if (seq2.type == LispString_t)
5477		    cmp2 = SCHAR(seq2.data.string[off2]);
5478		else
5479		    cmp2 = seq2.data.vector[off2];
5480		if (key != UNSPEC) {
5481		    cmp1 = APPLY1(key, cmp1);
5482		    cmp2 = APPLY1(key, cmp2);
5483		}
5484
5485		/* Compare elements */
5486		value = FCOMPARE(lambda, cmp1, cmp2, code);
5487		if (value != expect)
5488		    break;
5489
5490		/* Update offsets */
5491		++off1;
5492		++off2;
5493		if (seq1.type != LispString_t)
5494		    list1 = CDR(list1);
5495	    }
5496
5497	    /* If all elements matched */
5498	    if (off1 == end1) {
5499		offset = off2 - length1;
5500		break;
5501	    }
5502
5503	    /* Update offset */
5504	    --end2;
5505	}
5506
5507	if (seq2.type != LispString_t)
5508	    LispFree(seq2.data.vector);
5509    }
5510
5511    return (offset == -1 ? NIL : FIXNUM(offset));
5512}
5513
5514/*
5515 * ext::getenv
5516 */
5517LispObj *
5518Lisp_Setenv(LispBuiltin *builtin)
5519/*
5520 setenv name value &optional overwrite
5521 */
5522{
5523    char *name, *value;
5524
5525    LispObj *oname, *ovalue, *overwrite;
5526
5527    overwrite = ARGUMENT(2);
5528    ovalue = ARGUMENT(1);
5529    oname = ARGUMENT(0);
5530
5531    CHECK_STRING(oname);
5532    name = THESTR(oname);
5533
5534    CHECK_STRING(ovalue);
5535    value = THESTR(ovalue);
5536
5537    setenv(name, value, overwrite != UNSPEC && overwrite != NIL);
5538    value = getenv(name);
5539
5540    return (value ? STRING(value) : NIL);
5541}
5542
5543LispObj *
5544Lisp_Set(LispBuiltin *builtin)
5545/*
5546 set symbol value
5547 */
5548{
5549    LispAtom *atom;
5550    LispObj *symbol, *value;
5551
5552    value = ARGUMENT(1);
5553    symbol = ARGUMENT(0);
5554
5555    CHECK_SYMBOL(symbol);
5556    atom = symbol->data.atom;
5557    if (atom->dyn)
5558	LispSetVar(symbol, value);
5559    else if (atom->watch || !atom->a_object)
5560	LispSetAtomObjectProperty(atom, value);
5561    else {
5562	CHECK_CONSTANT(symbol);
5563	SETVALUE(atom, value);
5564    }
5565
5566    return (value);
5567}
5568
5569LispObj *
5570Lisp_SetDifference(LispBuiltin *builtin)
5571/*
5572 set-difference list1 list2 &key test test-not key
5573 */
5574{
5575    return (LispListSet(builtin, SETDIFFERENCE));
5576}
5577
5578LispObj *
5579Lisp_SetExclusiveOr(LispBuiltin *builtin)
5580/*
5581 set-exclusive-or list1 list2 &key test test-not key
5582 */
5583{
5584    return (LispListSet(builtin, SETEXCLUSIVEOR));
5585}
5586
5587LispObj *
5588Lisp_NsetExclusiveOr(LispBuiltin *builtin)
5589/*
5590 nset-exclusive-or list1 list2 &key test test-not key
5591 */
5592{
5593    return (LispListSet(builtin, NSETEXCLUSIVEOR));
5594}
5595
5596LispObj *
5597Lisp_SetQ(LispBuiltin *builtin)
5598/*
5599 setq &rest form
5600 */
5601{
5602    LispObj *result, *variable, *form;
5603
5604    form = ARGUMENT(0);
5605
5606    result = NIL;
5607    for (; CONSP(form); form = CDR(form)) {
5608	variable = CAR(form);
5609	CHECK_SYMBOL(variable);
5610	CHECK_CONSTANT(variable);
5611	form = CDR(form);
5612	if (!CONSP(form))
5613	    LispDestroy("%s: odd number of arguments", STRFUN(builtin));
5614	result = EVAL(CAR(form));
5615	LispSetVar(variable, result);
5616    }
5617
5618    return (result);
5619}
5620
5621LispObj *
5622Lisp_Psetq(LispBuiltin *builtin)
5623/*
5624 psetq &rest form
5625 */
5626{
5627    GC_ENTER();
5628    int base = gc__protect;
5629    LispObj *value, *symbol, *list, *form;
5630
5631    form = ARGUMENT(0);
5632
5633    /* parallel setq, first pass evaluate values and basic error checking */
5634    for (list = form; CONSP(list); list = CDR(list)) {
5635	symbol = CAR(list);
5636	CHECK_SYMBOL(symbol);
5637	list = CDR(list);
5638	if (!CONSP(list))
5639	    LispDestroy("%s: odd number of arguments", STRFUN(builtin));
5640	value = EVAL(CAR(list));
5641	GC_PROTECT(value);
5642    }
5643
5644    /* second pass, assign values */
5645    for (; CONSP(form); form = CDDR(form)) {
5646	symbol = CAR(form);
5647	CHECK_CONSTANT(symbol);
5648	LispSetVar(symbol, lisp__data.protect.objects[base++]);
5649    }
5650    GC_LEAVE();
5651
5652    return (NIL);
5653}
5654
5655LispObj *
5656Lisp_Setf(LispBuiltin *builtin)
5657/*
5658 setf &rest form
5659 */
5660{
5661    LispAtom *atom;
5662    LispObj *setf, *place, *value, *result = NIL, *data;
5663
5664    LispObj *form;
5665
5666    form = ARGUMENT(0);
5667
5668    for (; CONSP(form); form = CDR(form)) {
5669	place = CAR(form);
5670	form = CDR(form);
5671	if (!CONSP(form))
5672	    LispDestroy("%s: odd number of arguments", STRFUN(builtin));
5673	value = CAR(form);
5674
5675	if (!POINTERP(place))
5676	    goto invalid_place;
5677	if (XSYMBOLP(place)) {
5678	    CHECK_CONSTANT(place);
5679	    result = EVAL(value);
5680	    (void)LispSetVar(place, result);
5681	}
5682	else if (XCONSP(place)) {
5683	    /* it really should not be required to protect any object
5684	     * evaluated here, but is done for safety in case one of
5685	     * the evaluated forms returns data not gc protected, what
5686	     * could cause surprises if the object is garbage collected
5687	     * before finishing setf. */
5688	    GC_ENTER();
5689
5690	    setf = CAR(place);
5691	    if (!SYMBOLP(setf))
5692		goto invalid_place;
5693	    if (!CONSP(CDR(place)))
5694		goto invalid_place;
5695
5696	    value = EVAL(value);
5697	    GC_PROTECT(value);
5698
5699	    atom = setf->data.atom;
5700	    if (atom->a_defsetf == 0) {
5701		if (atom->a_defstruct &&
5702		    atom->property->structure.function >= 0) {
5703		    /* Use a default setf method for the structure field, as
5704		     * if this definition have been done
5705		     *	(defsetf THE-STRUCT-FIELD (struct) (value)
5706		     *	 `(lisp::struct-store 'THE-STRUCT-FIELD ,struct ,value))
5707		     */
5708		    place = CDR(place);
5709		    data = CAR(place);
5710		    if (CONSP(CDR(place)))
5711			goto invalid_place;
5712		    data = EVAL(data);
5713		    GC_PROTECT(data);
5714		    result = APPLY3(Ostruct_store, setf, data, value);
5715		    GC_LEAVE();
5716		    continue;
5717		}
5718		/* Must also expand macros */
5719		else if (atom->a_function &&
5720			 atom->property->fun.function->funtype == LispMacro) {
5721		    result = LispRunSetfMacro(atom, CDR(place), value);
5722		    continue;
5723		}
5724		goto invalid_place;
5725	    }
5726
5727	    place = CDR(place);
5728	    setf = setf->data.atom->property->setf;
5729	    if (SYMBOLP(setf)) {
5730		LispObj *arguments, *cons;
5731
5732		if (!CONSP(CDR(place))) {
5733		    arguments = EVAL(CAR(place));
5734		    GC_PROTECT(arguments);
5735		    result = APPLY2(setf, arguments, value);
5736		}
5737		else if (!CONSP(CDDR(place))) {
5738		    arguments = EVAL(CAR(place));
5739		    GC_PROTECT(arguments);
5740		    cons = EVAL(CADR(place));
5741		    GC_PROTECT(cons);
5742		    result = APPLY3(setf, arguments, cons, value);
5743		}
5744		else {
5745		    arguments = cons = CONS(EVAL(CAR(place)), NIL);
5746		    GC_PROTECT(arguments);
5747		    for (place = CDR(place); CONSP(place); place = CDR(place)) {
5748			RPLACD(cons, CONS(EVAL(CAR(place)), NIL));
5749			cons = CDR(cons);
5750		    }
5751		    RPLACD(cons, CONS(value, NIL));
5752		    result = APPLY(setf, arguments);
5753		}
5754	    }
5755	    else
5756		result = LispRunSetf(atom->property->salist, setf, place, value);
5757	    GC_LEAVE();
5758	}
5759	else
5760	    goto invalid_place;
5761    }
5762
5763    return (result);
5764invalid_place:
5765    LispDestroy("%s: %s is an invalid place", STRFUN(builtin), STROBJ(place));
5766    /*NOTREACHED*/
5767    return (NIL);
5768}
5769
5770LispObj *
5771Lisp_Psetf(LispBuiltin *builtin)
5772/*
5773 psetf &rest form
5774 */
5775{
5776    int base;
5777    GC_ENTER();
5778    LispAtom *atom;
5779    LispObj *setf, *place = NIL, *value, *data;
5780
5781    LispObj *form;
5782
5783    form = ARGUMENT(0);
5784
5785    /* parallel setf, first pass evaluate values and basic error checking */
5786    base = gc__protect;
5787    for (setf = form; CONSP(setf); setf = CDR(setf)) {
5788	if (!POINTERP(CAR(setf)))
5789	    goto invalid_place;
5790	setf = CDR(setf);
5791	if (!CONSP(setf))
5792	    LispDestroy("%s: odd number of arguments", STRFUN(builtin));
5793	value = EVAL(CAR(setf));
5794	GC_PROTECT(value);
5795    }
5796
5797    /* second pass, assign values */
5798    for (; CONSP(form); form = CDDR(form)) {
5799	place = CAR(form);
5800	value = lisp__data.protect.objects[base++];
5801
5802	if (XSYMBOLP(place)) {
5803	    CHECK_CONSTANT(place);
5804	    (void)LispSetVar(place, value);
5805	}
5806	else if (XCONSP(place)) {
5807	    LispObj *arguments, *cons;
5808	    int xbase = lisp__data.protect.length;
5809
5810	    setf = CAR(place);
5811	    if (!SYMBOLP(setf))
5812		goto invalid_place;
5813	    if (!CONSP(CDR(place)))
5814		goto invalid_place;
5815
5816	    atom = setf->data.atom;
5817	    if (atom->a_defsetf == 0) {
5818		if (atom->a_defstruct &&
5819		    atom->property->structure.function >= 0) {
5820		    place = CDR(place);
5821		    data = CAR(place);
5822		    if (CONSP(CDR(place)))
5823			goto invalid_place;
5824		    data = EVAL(data);
5825		    GC_PROTECT(data);
5826		    (void)APPLY3(Ostruct_store, setf, data, value);
5827		    lisp__data.protect.length = xbase;
5828		    continue;
5829		}
5830		else if (atom->a_function &&
5831			 atom->property->fun.function->funtype == LispMacro) {
5832		    (void)LispRunSetfMacro(atom, CDR(place), value);
5833		    lisp__data.protect.length = xbase;
5834		    continue;
5835		}
5836		goto invalid_place;
5837	    }
5838
5839	    place = CDR(place);
5840	    setf = setf->data.atom->property->setf;
5841	    if (SYMBOLP(setf)) {
5842		if (!CONSP(CDR(place))) {
5843		    arguments = EVAL(CAR(place));
5844		    GC_PROTECT(arguments);
5845		    (void)APPLY2(setf, arguments, value);
5846		}
5847		else if (!CONSP(CDDR(place))) {
5848		    arguments = EVAL(CAR(place));
5849		    GC_PROTECT(arguments);
5850		    cons = EVAL(CADR(place));
5851		    GC_PROTECT(cons);
5852		    (void)APPLY3(setf, arguments, cons, value);
5853		}
5854		else {
5855		    arguments = cons = CONS(EVAL(CAR(place)), NIL);
5856		    GC_PROTECT(arguments);
5857		    for (place = CDR(place); CONSP(place); place = CDR(place)) {
5858			RPLACD(cons, CONS(EVAL(CAR(place)), NIL));
5859			cons = CDR(cons);
5860		    }
5861		    RPLACD(cons, CONS(value, NIL));
5862		    (void)APPLY(setf, arguments);
5863		}
5864		lisp__data.protect.length = xbase;
5865	    }
5866	    else
5867		(void)LispRunSetf(atom->property->salist, setf, place, value);
5868	}
5869	else
5870	    goto invalid_place;
5871    }
5872    GC_LEAVE();
5873
5874    return (NIL);
5875invalid_place:
5876    LispDestroy("%s: %s is an invalid place", STRFUN(builtin), STROBJ(place));
5877    /*NOTREACHED*/
5878    return (NIL);
5879}
5880
5881LispObj *
5882Lisp_Sleep(LispBuiltin *builtin)
5883/*
5884 sleep seconds
5885 */
5886{
5887    long sec, msec;
5888    double value, dsec;
5889
5890    LispObj *seconds;
5891
5892    seconds = ARGUMENT(0);
5893
5894    value = -1.0;
5895    switch (OBJECT_TYPE(seconds)) {
5896	case LispFixnum_t:
5897	    value = FIXNUM_VALUE(seconds);
5898	    break;
5899	case LispDFloat_t:
5900	    value = DFLOAT_VALUE(seconds);
5901	    break;
5902	default:
5903	    break;
5904    }
5905
5906    if (value < 0.0 || value > MOST_POSITIVE_FIXNUM)
5907	LispDestroy("%s: %s is not a positive fixnum",
5908		    STRFUN(builtin), STROBJ(seconds));
5909
5910    msec = modf(value, &dsec) * 1e6;
5911    sec = dsec;
5912
5913    if (sec)
5914	sleep(sec);
5915    if (msec)
5916	usleep(msec);
5917
5918    return (NIL);
5919}
5920
5921/*
5922 *   This function is called recursively, but the contents of "list2" are
5923 * kept gc protected until it returns to LispSort. This is required partly
5924 * because the "gc protection logic" protects an object, not the contents
5925 * of the c pointer.
5926 */
5927static LispObj *
5928LispMergeSort(LispObj *list, LispObj *predicate, LispObj *key, int code)
5929{
5930    int protect;
5931    LispObj *list1, *list2, *left, *right, *result, *cons;
5932
5933    /* Check if list length is larger than 1 */
5934    if (!CONSP(list) || !CONSP(CDR(list)))
5935	return (list);
5936
5937    list1 = list2 = list;
5938    for (;;) {
5939	list = CDR(list);
5940	if (!CONSP(list))
5941	    break;
5942	list = CDR(list);
5943	if (!CONSP(list))
5944	    break;
5945	list2 = CDR(list2);
5946    }
5947    cons = list2;
5948    list2 = CDR(list2);
5949    RPLACD(cons, NIL);
5950
5951    protect = 0;
5952    if (lisp__data.protect.length + 2 >= lisp__data.protect.space)
5953	LispMoreProtects();
5954    lisp__data.protect.objects[lisp__data.protect.length++] = list2;
5955    list1 = LispMergeSort(list1, predicate, key, code);
5956    list2 = LispMergeSort(list2, predicate, key, code);
5957
5958    left = CAR(list1);
5959    right = CAR(list2);
5960    if (key != UNSPEC) {
5961	protect = lisp__data.protect.length;
5962	left = APPLY1(key, left);
5963	lisp__data.protect.objects[protect] = left;
5964	right = APPLY1(key, right);
5965	lisp__data.protect.objects[protect + 1] = right;
5966    }
5967
5968    result = NIL;
5969    for (;;) {
5970	if ((FCOMPARE(predicate, left, right, code)) == 0 &&
5971	    (FCOMPARE(predicate, right, left, code)) == 1) {
5972	    /* right is "smaller" */
5973	    if (result == NIL)
5974		result = list2;
5975	    else
5976		RPLACD(cons, list2);
5977	    cons = list2;
5978	    list2 = CDR(list2);
5979	    if (!CONSP(list2)) {
5980		RPLACD(cons, list1);
5981		break;
5982	    }
5983	    right = CAR(list2);
5984	    if (key != UNSPEC) {
5985		right = APPLY1(key, right);
5986		lisp__data.protect.objects[protect + 1] = right;
5987	    }
5988	}
5989	else {
5990	    /* left is "smaller" */
5991	    if (result == NIL)
5992		result = list1;
5993	    else
5994		RPLACD(cons, list1);
5995	    cons = list1;
5996	    list1 = CDR(list1);
5997	    if (!CONSP(list1)) {
5998		RPLACD(cons, list2);
5999		break;
6000	    }
6001	    left = CAR(list1);
6002	    if (key != UNSPEC) {
6003		left = APPLY1(key, left);
6004		lisp__data.protect.objects[protect] = left;
6005	    }
6006	}
6007    }
6008    if (key != UNSPEC)
6009	lisp__data.protect.length = protect;
6010
6011    return (result);
6012}
6013
6014/* XXX The first version made a copy of the list and then adjusted
6015 *     the CARs of the list. To minimize GC time now it is now doing
6016 *     the sort inplace. So, instead of writing just (sort variable)
6017 *     now it is required to write (setq variable (sort variable))
6018 *     if the variable should always keep all elements.
6019 */
6020LispObj *
6021Lisp_Sort(LispBuiltin *builtin)
6022/*
6023 sort sequence predicate &key key
6024 */
6025{
6026    GC_ENTER();
6027    int istring, code;
6028    long length;
6029    char *string;
6030
6031    LispObj *list, *work, *cons = NULL;
6032
6033    LispObj *sequence, *predicate, *key;
6034
6035    key = ARGUMENT(2);
6036    predicate = ARGUMENT(1);
6037    sequence = ARGUMENT(0);
6038
6039    length = LispLength(sequence);
6040    if (length < 2)
6041	return (sequence);
6042
6043    list = sequence;
6044    istring = XSTRINGP(sequence);
6045    if (istring) {
6046	CHECK_STRING_WRITABLE(sequence);
6047	/* Convert string to list */
6048	string = THESTR(sequence);
6049	work = cons = CONS(SCHAR(string[0]), NIL);
6050	GC_PROTECT(work);
6051	for (++string; *string; ++string) {
6052	    RPLACD(cons, CONS(SCHAR(*string), NIL));
6053	    cons = CDR(cons);
6054	}
6055    }
6056    else if (ARRAYP(list))
6057	work = list->data.array.list;
6058    else
6059	work = list;
6060
6061    FUNCTION_CHECK(predicate);
6062    code = FCODE(predicate);
6063    work = LispMergeSort(work, predicate, key, code);
6064
6065    if (istring) {
6066	/* Convert list to string */
6067	string = THESTR(sequence);
6068	for (; CONSP(work); ++string, work = CDR(work))
6069	    *string = SCHAR_VALUE(CAR(work));
6070    }
6071    else if (ARRAYP(list))
6072	list->data.array.list = work;
6073    else
6074	sequence = work;
6075    GC_LEAVE();
6076
6077    return (sequence);
6078}
6079
6080LispObj *
6081Lisp_Subseq(LispBuiltin *builtin)
6082/*
6083 subseq sequence start &optional end
6084 */
6085{
6086    long start, end, length, seqlength;
6087
6088    LispObj *sequence, *ostart, *oend, *result;
6089
6090    oend = ARGUMENT(2);
6091    ostart = ARGUMENT(1);
6092    sequence = ARGUMENT(0);
6093
6094    LispCheckSequenceStartEnd(builtin, sequence, ostart, oend,
6095			      &start, &end, &length);
6096
6097    seqlength = end - start;
6098
6099    if (sequence == NIL)
6100	result = NIL;
6101    else if (XSTRINGP(sequence)) {
6102	char *string = LispMalloc(seqlength + 1);
6103
6104	memcpy(string, THESTR(sequence) + start, seqlength);
6105	string[seqlength] = '\0';
6106	result = STRING2(string);
6107    }
6108    else {
6109	GC_ENTER();
6110	LispObj *object;
6111
6112	if (end > start) {
6113	    /* list or array */
6114	    int count;
6115	    LispObj *cons;
6116
6117	    if (ARRAYP(sequence))
6118		object = sequence->data.array.list;
6119	    else
6120		object = sequence;
6121	    /* goto first element to copy */
6122	    for (count = 0; count < start; count++, object = CDR(object))
6123		;
6124	    result = cons = CONS(CAR(object), NIL);
6125	    GC_PROTECT(result);
6126	    for (++count, object = CDR(object); count < end; count++,
6127		 object = CDR(object)) {
6128		RPLACD(cons, CONS(CAR(object), NIL));
6129		cons = CDR(cons);
6130	    }
6131	}
6132	else
6133	    result = NIL;
6134
6135	if (ARRAYP(sequence)) {
6136	    object = LispNew(NIL, NIL);
6137	    GC_PROTECT(object);
6138	    object->type = LispArray_t;
6139	    object->data.array.list = result;
6140	    object->data.array.dim = CONS(FIXNUM(seqlength), NIL);
6141	    object->data.array.rank = 1;
6142	    object->data.array.type = sequence->data.array.type;
6143	    object->data.array.zero = length == 0;
6144	    result = object;
6145	}
6146	GC_LEAVE();
6147    }
6148
6149    return (result);
6150}
6151
6152LispObj *
6153Lisp_Subsetp(LispBuiltin *builtin)
6154/*
6155 subsetp list1 list2 &key test test-not key
6156 */
6157{
6158    return (LispListSet(builtin, SUBSETP));
6159}
6160
6161
6162LispObj *
6163Lisp_Substitute(LispBuiltin *builtin)
6164/*
6165 substitute newitem olditem sequence &key from-end test test-not start end count key
6166 */
6167{
6168    return (LispDeleteRemoveXSubstitute(builtin, SUBSTITUTE, NONE));
6169}
6170
6171LispObj *
6172Lisp_SubstituteIf(LispBuiltin *builtin)
6173/*
6174 substitute-if newitem test sequence &key from-end start end count key
6175 */
6176{
6177    return (LispDeleteRemoveXSubstitute(builtin, SUBSTITUTE, IF));
6178}
6179
6180LispObj *
6181Lisp_SubstituteIfNot(LispBuiltin *builtin)
6182/*
6183 substitute-if-not newitem test sequence &key from-end start end count key
6184 */
6185{
6186    return (LispDeleteRemoveXSubstitute(builtin, SUBSTITUTE, IFNOT));
6187}
6188
6189LispObj *
6190Lisp_Symbolp(LispBuiltin *builtin)
6191/*
6192 symbolp object
6193 */
6194{
6195    LispObj *object;
6196
6197    object = ARGUMENT(0);
6198
6199    return (SYMBOLP(object) ? T : NIL);
6200}
6201
6202LispObj *
6203Lisp_SymbolFunction(LispBuiltin *builtin)
6204/*
6205 symbol-function symbol
6206 */
6207{
6208    LispObj *symbol;
6209
6210    symbol = ARGUMENT(0);
6211    CHECK_SYMBOL(symbol);
6212
6213    return (LispSymbolFunction(symbol));
6214}
6215
6216LispObj *
6217Lisp_SymbolName(LispBuiltin *builtin)
6218/*
6219 symbol-name symbol
6220 */
6221{
6222    LispObj *symbol;
6223
6224    symbol = ARGUMENT(0);
6225    CHECK_SYMBOL(symbol);
6226
6227    return (LispSymbolName(symbol));
6228}
6229
6230LispObj *
6231Lisp_SymbolPackage(LispBuiltin *builtin)
6232/*
6233 symbol-package symbol
6234 */
6235{
6236    LispObj *symbol;
6237
6238    symbol = ARGUMENT(0);
6239    CHECK_SYMBOL(symbol);
6240
6241    symbol = symbol->data.atom->package;
6242
6243    return (symbol ? symbol : NIL);
6244}
6245
6246LispObj *
6247Lisp_SymbolPlist(LispBuiltin *builtin)
6248/*
6249 symbol-plist symbol
6250 */
6251{
6252    LispObj *symbol;
6253
6254    symbol = ARGUMENT(0);
6255
6256    CHECK_SYMBOL(symbol);
6257
6258    return (symbol->data.atom->a_property ?
6259	    symbol->data.atom->property->properties : NIL);
6260}
6261
6262LispObj *
6263Lisp_SymbolValue(LispBuiltin *builtin)
6264/*
6265 symbol-value symbol
6266 */
6267{
6268    LispAtom *atom;
6269    LispObj *symbol;
6270
6271    symbol = ARGUMENT(0);
6272
6273    CHECK_SYMBOL(symbol);
6274    atom = symbol->data.atom;
6275    if (!atom->a_object || atom->property->value == UNBOUND) {
6276	if (atom->package == lisp__data.keyword)
6277	    return (symbol);
6278	LispDestroy("%s: the symbol %s has no value",
6279		    STRFUN(builtin), STROBJ(symbol));
6280    }
6281
6282    return (atom->dyn ? LispGetVar(symbol) : atom->property->value);
6283}
6284
6285LispObj *
6286Lisp_Tagbody(LispBuiltin *builtin)
6287/*
6288 tagbody &rest body
6289 */
6290{
6291    GC_ENTER();
6292    int stack, lex, length;
6293    LispObj *list, *body, *ptr, *tag, *labels, *map, **p_body;
6294    LispBlock *block;
6295
6296    body = ARGUMENT(0);
6297
6298    /* Save environment information */
6299    stack = lisp__data.stack.length;
6300    lex = lisp__data.env.lex;
6301    length = lisp__data.env.length;
6302
6303    /* Since the body may be large, and the code may iterate several
6304     * thousand times, it is not a bad idea to avoid checking all
6305     * elements of the body to verify if it is a tag. */
6306    for (labels = map = NIL, ptr = body; CONSP(ptr); ptr = CDR(ptr)) {
6307	tag = CAR(ptr);
6308	switch (OBJECT_TYPE(tag)) {
6309	    case LispNil_t:
6310	    case LispAtom_t:
6311	    case LispFixnum_t:
6312		/* Don't allow duplicated labels */
6313		for (list = labels; CONSP(list); list = CDDR(list)) {
6314		    if (CAR(list) == tag)
6315			LispDestroy("%s: tag %s specified more than once",
6316				    STRFUN(builtin), STROBJ(tag));
6317		}
6318		if (labels == NIL) {
6319		    labels = CONS(tag, CONS(NIL, NIL));
6320		    map = CDR(labels);
6321		    GC_PROTECT(labels);
6322		}
6323		else {
6324		    RPLACD(map, CONS(tag, CONS(NIL, NIL)));
6325		    map = CDDR(map);
6326		}
6327		break;
6328	    case LispCons_t:
6329		/* Restart point for tag */
6330		if (map != NIL && CAR(map) == NIL)
6331		    RPLACA(map, ptr);
6332		break;
6333	    default:
6334		break;
6335	}
6336    }
6337    /* Check for consecutive labels without code between them */
6338    for (ptr = labels; CONSP(ptr); ptr = CDDR(ptr)) {
6339	if (CADR(ptr) == NIL) {
6340	    for (map = CDDR(ptr); CONSP(map); map = CDDR(map)) {
6341		if (CADR(map) != NIL) {
6342		    RPLACA(CDR(ptr), CADR(map));
6343		    break;
6344		}
6345	    }
6346	}
6347    }
6348
6349    /* Initialize */
6350    list = body;
6351    p_body = &body;
6352    block = LispBeginBlock(NIL, LispBlockBody);
6353
6354    /* Loop */
6355    if (setjmp(block->jmp) != 0) {
6356	/* Restore environment */
6357	lisp__data.stack.length = stack;
6358	lisp__data.env.lex = lex;
6359	lisp__data.env.head = lisp__data.env.length = length;
6360
6361	tag = lisp__data.block.block_ret;
6362	for (ptr = labels; CONSP(ptr); ptr = CDDR(ptr)) {
6363	    map = CAR(ptr);
6364	    if (map == tag)
6365		break;
6366	}
6367
6368	if (!CONSP(ptr))
6369	    LispDestroy("%s: no such tag %s", STRFUN(builtin), STROBJ(tag));
6370
6371	*p_body = CADR(ptr);
6372    }
6373
6374    /* Execute code */
6375    for (; CONSP(body); body = CDR(body)) {
6376	LispObj *form = CAR(body);
6377
6378	if (CONSP(form))
6379	    EVAL(form);
6380    }
6381    /* If got here, (go) not called, else, labels will be candidate to gc
6382     * when GC_LEAVE() be called by the code in the bottom of the stack. */
6383    GC_LEAVE();
6384
6385    /* Finished */
6386    LispEndBlock(block);
6387
6388    /* Always return NIL */
6389    return (NIL);
6390}
6391
6392LispObj *
6393Lisp_The(LispBuiltin *builtin)
6394/*
6395 the value-type form
6396 */
6397{
6398    LispObj *value_type, *form;
6399
6400    form = ARGUMENT(1);
6401    value_type = ARGUMENT(0);
6402
6403    form = EVAL(form);
6404
6405    return (LispCoerce(builtin, form, value_type));
6406}
6407
6408LispObj *
6409Lisp_Throw(LispBuiltin *builtin)
6410/*
6411 throw tag result
6412 */
6413{
6414    unsigned blevel = lisp__data.block.block_level;
6415
6416    LispObj *tag, *result;
6417
6418    result = ARGUMENT(1);
6419    tag = ARGUMENT(0);
6420
6421    tag = EVAL(tag);
6422
6423    if (blevel == 0)
6424	LispDestroy("%s: not within a block", STRFUN(builtin));
6425
6426    while (blevel) {
6427	LispBlock *block = lisp__data.block.block[--blevel];
6428
6429	if (block->type == LispBlockCatch && tag == block->tag) {
6430	    lisp__data.block.block_ret = EVAL(result);
6431	    LispBlockUnwind(block);
6432	    BLOCKJUMP(block);
6433	}
6434    }
6435    LispDestroy("%s: %s is not a valid tag", STRFUN(builtin), STROBJ(tag));
6436
6437    /*NOTREACHED*/
6438    return (NIL);
6439}
6440
6441static LispObj *
6442LispTreeEqual(LispObj *left, LispObj *right, LispObj *test, int expect)
6443{
6444    LispObj *cmp_left, *cmp_right;
6445
6446    if ((OBJECT_TYPE(left)) ^ (OBJECT_TYPE(right)))
6447	return (NIL);
6448    if (CONSP(left)) {
6449	for (; CONSP(left) && CONSP(right);
6450	     left = CDR(left), right = CDR(right)) {
6451	    cmp_left = CAR(left);
6452	    cmp_right = CAR(right);
6453	    if ((OBJECT_TYPE(cmp_left)) ^ (OBJECT_TYPE(cmp_right)))
6454		return (NIL);
6455	    if (CONSP(cmp_left)) {
6456		if (LispTreeEqual(cmp_left, cmp_right, test, expect) == NIL)
6457		    return (NIL);
6458	    }
6459	    else {
6460		if (POINTERP(cmp_left) &&
6461		    (XQUOTEP(cmp_left) || XBACKQUOTEP(cmp_left))) {
6462		    cmp_left = cmp_left->data.quote;
6463		    cmp_right = cmp_right->data.quote;
6464		}
6465		else if (COMMAP(cmp_left)) {
6466		    cmp_left = cmp_left->data.comma.eval;
6467		    cmp_right = cmp_right->data.comma.eval;
6468		}
6469		if ((APPLY2(test, cmp_left, cmp_right) != NIL) != expect)
6470		    return (NIL);
6471	    }
6472	}
6473	if ((OBJECT_TYPE(left)) ^ (OBJECT_TYPE(right)))
6474	    return (NIL);
6475    }
6476
6477    if (POINTERP(left) && (XQUOTEP(left) || XBACKQUOTEP(left))) {
6478	left = left->data.quote;
6479	right = right->data.quote;
6480    }
6481    else if (COMMAP(left)) {
6482	left = left->data.comma.eval;
6483	right = right->data.comma.eval;
6484    }
6485
6486    return ((APPLY2(test, left, right) != NIL) == expect ? T : NIL);
6487}
6488
6489LispObj *
6490Lisp_TreeEqual(LispBuiltin *builtin)
6491/*
6492 tree-equal tree-1 tree-2 &key test test-not
6493 */
6494{
6495    int expect;
6496    LispObj *compare;
6497
6498    LispObj *tree_1, *tree_2, *test, *test_not;
6499
6500    test_not = ARGUMENT(3);
6501    test = ARGUMENT(2);
6502    tree_2 = ARGUMENT(1);
6503    tree_1 = ARGUMENT(0);
6504
6505    CHECK_TEST_0();
6506    if (test_not != UNSPEC) {
6507	expect = 0;
6508	compare = test_not;
6509    }
6510    else {
6511	if (test == UNSPEC)
6512	    test = Oeql;
6513	expect = 1;
6514	compare = test;
6515    }
6516
6517    return (LispTreeEqual(tree_1, tree_2, compare, expect));
6518}
6519
6520LispObj *
6521Lisp_Typep(LispBuiltin *builtin)
6522/*
6523 typep object type
6524 */
6525{
6526    LispObj *result = NULL;
6527
6528    LispObj *object, *type;
6529
6530    type = ARGUMENT(1);
6531    object = ARGUMENT(0);
6532
6533    if (SYMBOLP(type)) {
6534	Atom_id atom = ATOMID(type);
6535
6536	if (OBJECT_TYPE(object) == LispStruct_t)
6537	    result = ATOMID(CAR(object->data.struc.def)) == atom ? T : NIL;
6538	else if (type->data.atom->a_defstruct &&
6539		 type->data.atom->property->structure.function == STRUCT_NAME)
6540	    result = NIL;
6541	else if (atom == Snil)
6542	    result = object == NIL ? T : NIL;
6543	else if (atom == St)
6544	    result = object == T ? T : NIL;
6545	else if (atom == Satom)
6546	    result = !CONSP(object) ? T : NIL;
6547	else if (atom == Ssymbol)
6548	    result = SYMBOLP(object) || object == NIL || object == T ? T : NIL;
6549	else if (atom == Sinteger)
6550	    result = INTEGERP(object) ? T : NIL;
6551	else if (atom == Srational)
6552	    result = RATIONALP(object) ? T : NIL;
6553	else if (atom == Scons || atom == Slist)
6554	    result = CONSP(object) ? T : NIL;
6555	else if (atom == Sstring)
6556	    result = STRINGP(object) ? T : NIL;
6557	else if (atom == Scharacter)
6558	    result = SCHARP(object) ? T : NIL;
6559	else if (atom == Scomplex)
6560	    result = COMPLEXP(object) ? T : NIL;
6561	else if (atom == Svector || atom == Sarray)
6562	    result = ARRAYP(object) ? T : NIL;
6563	else if (atom == Skeyword)
6564	    result = KEYWORDP(object) ? T : NIL;
6565	else if (atom == Sfunction)
6566	    result = LAMBDAP(object) ? T : NIL;
6567	else if (atom == Spathname)
6568	    result = PATHNAMEP(object) ? T : NIL;
6569	else if (atom == Sopaque)
6570	    result = OPAQUEP(object) ? T : NIL;
6571    }
6572    else if (CONSP(type)) {
6573	if (OBJECT_TYPE(object) == LispStruct_t &&
6574	    SYMBOLP(CAR(type)) && ATOMID(CAR(type)) == Sstruct &&
6575	    SYMBOLP(CAR(CDR(type))) && CDR(CDR(type)) == NIL) {
6576	    result = ATOMID(CAR(object->data.struc.def)) ==
6577		     ATOMID(CAR(CDR(type))) ? T : NIL;
6578	}
6579    }
6580    else if (type == NIL)
6581	result = object == NIL ? T : NIL;
6582    else if (type == T)
6583	result = object == T ? T : NIL;
6584    if (result == NULL)
6585	LispDestroy("%s: bad type specification %s",
6586		    STRFUN(builtin), STROBJ(type));
6587
6588    return (result);
6589}
6590
6591LispObj *
6592Lisp_Union(LispBuiltin *builtin)
6593/*
6594 union list1 list2 &key test test-not key
6595 */
6596{
6597    return (LispListSet(builtin, UNION));
6598}
6599
6600LispObj *
6601Lisp_Nunion(LispBuiltin *builtin)
6602/*
6603 nunion list1 list2 &key test test-not key
6604 */
6605{
6606    return (LispListSet(builtin, NUNION));
6607}
6608
6609LispObj *
6610Lisp_Unless(LispBuiltin *builtin)
6611/*
6612 unless test &rest body
6613 */
6614{
6615    LispObj *result, *test, *body;
6616
6617    body = ARGUMENT(1);
6618    test = ARGUMENT(0);
6619
6620    result = NIL;
6621    test = EVAL(test);
6622    RETURN_COUNT = 0;
6623    if (test == NIL) {
6624	for (; CONSP(body); body = CDR(body))
6625	    result = EVAL(CAR(body));
6626    }
6627
6628    return (result);
6629}
6630
6631/*
6632 * ext::until
6633 */
6634LispObj *
6635Lisp_Until(LispBuiltin *builtin)
6636/*
6637 until test &rest body
6638 */
6639{
6640    LispObj *result, *test, *body, *prog;
6641
6642    body = ARGUMENT(1);
6643    test = ARGUMENT(0);
6644
6645    result = NIL;
6646    for (;;) {
6647	if ((result = EVAL(test)) == NIL) {
6648	    for (prog = body; CONSP(prog); prog = CDR(prog))
6649		(void)EVAL(CAR(prog));
6650	}
6651	else
6652	    break;
6653    }
6654
6655    return (result);
6656}
6657
6658LispObj *
6659Lisp_UnwindProtect(LispBuiltin *builtin)
6660/*
6661 unwind-protect protect &rest cleanup
6662 */
6663{
6664    LispObj *result, **presult = &result;
6665    int did_jump, *pdid_jump = &did_jump, destroyed;
6666    LispBlock *block;
6667
6668    LispObj *protect, *cleanup, **pcleanup = &cleanup;
6669
6670    cleanup = ARGUMENT(1);
6671    protect = ARGUMENT(0);
6672
6673    /* run protected code */
6674    *presult = NIL;
6675    *pdid_jump = 1;
6676    block = LispBeginBlock(NIL, LispBlockProtect);
6677    if (setjmp(block->jmp) == 0) {
6678	*presult = EVAL(protect);
6679	*pdid_jump = 0;
6680    }
6681    LispEndBlock(block);
6682    if (!lisp__data.destroyed && *pdid_jump)
6683	*presult = lisp__data.block.block_ret;
6684
6685    destroyed = lisp__data.destroyed;
6686    lisp__data.destroyed = 0;
6687
6688    /* run cleanup, unprotected code */
6689    if (CONSP(*pcleanup))
6690	for (; CONSP(cleanup); cleanup = CDR(cleanup))
6691	    (void)EVAL(CAR(cleanup));
6692
6693    if (destroyed) {
6694	/* in case there is another unwind-protect */
6695	LispBlockUnwind(NULL);
6696	/* if not, just return to the toplevel */
6697	lisp__data.destroyed = 1;
6698	LispDestroy(".");
6699    }
6700
6701    return (result);
6702}
6703
6704static LispObj *
6705LispValuesList(LispBuiltin *builtin, int check_list)
6706{
6707    long i, count;
6708    LispObj *result;
6709
6710    LispObj *list;
6711
6712    list = ARGUMENT(0);
6713
6714    count = LispLength(list) - 1;
6715
6716    if (count >= 0) {
6717	result = CAR(list);
6718	if ((RETURN_CHECK(count)) != count)
6719	    LispDestroy("%s: too many values", STRFUN(builtin));
6720	RETURN_COUNT = count;
6721	for (i = 0, list = CDR(list); count && CONSP(list);
6722	     count--, i++, list = CDR(list))
6723	    RETURN(i) = CAR(list);
6724	if (check_list) {
6725	    CHECK_LIST(list);
6726	}
6727    }
6728    else {
6729	RETURN_COUNT = -1;
6730	result = NIL;
6731    }
6732
6733    return (result);
6734}
6735
6736LispObj *
6737Lisp_Values(LispBuiltin *builtin)
6738/*
6739 values &rest objects
6740 */
6741{
6742    return (LispValuesList(builtin, 0));
6743}
6744
6745LispObj *
6746Lisp_ValuesList(LispBuiltin *builtin)
6747/*
6748 values-list list
6749 */
6750{
6751    return (LispValuesList(builtin, 1));
6752}
6753
6754LispObj *
6755Lisp_Vector(LispBuiltin *builtin)
6756/*
6757 vector &rest objects
6758 */
6759{
6760    LispObj *objects;
6761
6762    objects = ARGUMENT(0);
6763
6764    return (VECTOR(objects));
6765}
6766
6767LispObj *
6768Lisp_When(LispBuiltin *builtin)
6769/*
6770 when test &rest body
6771 */
6772{
6773    LispObj *result, *test, *body;
6774
6775    body = ARGUMENT(1);
6776    test = ARGUMENT(0);
6777
6778    result = NIL;
6779    test = EVAL(test);
6780    RETURN_COUNT = 0;
6781    if (test != NIL) {
6782	for (; CONSP(body); body = CDR(body))
6783	    result = EVAL(CAR(body));
6784    }
6785
6786    return (result);
6787}
6788
6789/*
6790 * ext::while
6791 */
6792LispObj *
6793Lisp_While(LispBuiltin *builtin)
6794/*
6795 while test &rest body
6796 */
6797{
6798    LispObj *test, *body, *prog;
6799
6800    body = ARGUMENT(1);
6801    test = ARGUMENT(0);
6802
6803    for (;;) {
6804	if (EVAL(test) != NIL) {
6805	    for (prog = body; CONSP(prog); prog = CDR(prog))
6806		(void)EVAL(CAR(prog));
6807	}
6808	else
6809	    break;
6810    }
6811
6812    return (NIL);
6813}
6814
6815/*
6816 * ext::unsetenv
6817 */
6818LispObj *
6819Lisp_Unsetenv(LispBuiltin *builtin)
6820/*
6821 unsetenv name
6822 */
6823{
6824    char *name;
6825
6826    LispObj *oname;
6827
6828    oname = ARGUMENT(0);
6829
6830    CHECK_STRING(oname);
6831    name = THESTR(oname);
6832
6833    unsetenv(name);
6834
6835    return (NIL);
6836}
6837
6838LispObj *
6839Lisp_XeditEltStore(LispBuiltin *builtin)
6840/*
6841 lisp::elt-store sequence index value
6842 */
6843{
6844    int length, offset;
6845
6846    LispObj *sequence, *oindex, *value;
6847
6848    value = ARGUMENT(2);
6849    oindex = ARGUMENT(1);
6850    sequence = ARGUMENT(0);
6851
6852    CHECK_INDEX(oindex);
6853    offset = FIXNUM_VALUE(oindex);
6854    length = LispLength(sequence);
6855
6856    if (offset >= length)
6857	LispDestroy("%s: index %d too large for sequence length %d",
6858		    STRFUN(builtin), offset, length);
6859
6860    if (STRINGP(sequence)) {
6861	int ch;
6862
6863	CHECK_STRING_WRITABLE(sequence);
6864	CHECK_SCHAR(value);
6865	ch = SCHAR_VALUE(value);
6866	if (ch < 0 || ch > 255)
6867	    LispDestroy("%s: cannot represent character %d",
6868			STRFUN(builtin), ch);
6869	THESTR(sequence)[offset] = ch;
6870    }
6871    else {
6872	if (ARRAYP(sequence))
6873	    sequence = sequence->data.array.list;
6874
6875	for (; offset > 0; offset--, sequence = CDR(sequence))
6876	    ;
6877	RPLACA(sequence, value);
6878    }
6879
6880    return (value);
6881}
6882
6883LispObj *
6884Lisp_XeditPut(LispBuiltin *builtin)
6885/*
6886 lisp::put symbol indicator value
6887 */
6888{
6889    LispObj *symbol, *indicator, *value;
6890
6891    value = ARGUMENT(2);
6892    indicator = ARGUMENT(1);
6893    symbol = ARGUMENT(0);
6894
6895    CHECK_SYMBOL(symbol);
6896
6897    return (CAR(LispPutAtomProperty(symbol->data.atom, indicator, value)));
6898}
6899
6900LispObj *
6901Lisp_XeditSetSymbolPlist(LispBuiltin *builtin)
6902/*
6903 lisp::set-symbol-plist symbol list
6904 */
6905{
6906    LispObj *symbol, *list;
6907
6908    list = ARGUMENT(1);
6909    symbol = ARGUMENT(0);
6910
6911    CHECK_SYMBOL(symbol);
6912
6913    return (LispReplaceAtomPropertyList(symbol->data.atom, list));
6914}
6915
6916LispObj *
6917Lisp_XeditVectorStore(LispBuiltin *builtin)
6918/*
6919 lisp::vector-store array &rest values
6920 */
6921{
6922    LispObj *value, *list, *object;
6923    long rank, count, sequence, offset, accum;
6924
6925    LispObj *array, *values;
6926
6927    values = ARGUMENT(1);
6928    array = ARGUMENT(0);
6929
6930    /* check for errors */
6931    for (rank = 0, list = values;
6932	 CONSP(list) && CONSP(CDR(list));
6933	 list = CDR(list), rank++) {
6934	CHECK_INDEX(CAR(values));
6935    }
6936
6937    if (rank == 0)
6938	LispDestroy("%s: too few subscripts", STRFUN(builtin));
6939    value = CAR(list);
6940
6941    if (STRINGP(array) && rank == 1) {
6942	long ch;
6943	long length = STRLEN(array);
6944	long offset = FIXNUM_VALUE(CAR(values));
6945
6946	CHECK_SCHAR(value);
6947	CHECK_STRING_WRITABLE(array);
6948	ch = SCHAR_VALUE(value);
6949	if (offset >= length)
6950	    LispDestroy("%s: index %ld too large for sequence length %ld",
6951			STRFUN(builtin), offset, length);
6952
6953	if (ch < 0 || ch > 255)
6954	    LispDestroy("%s: cannot represent character %ld",
6955			STRFUN(builtin), ch);
6956	THESTR(array)[offset] = ch;
6957
6958	return (value);
6959    }
6960
6961    CHECK_ARRAY(array);
6962    if (rank != array->data.array.rank)
6963	LispDestroy("%s: too %s subscripts", STRFUN(builtin),
6964		    rank < array->data.array.rank ? "few" : "many");
6965
6966    for (list = values, object = array->data.array.dim;
6967	 CONSP(CDR(list));
6968	 list = CDR(list), object = CDR(object)) {
6969	if (FIXNUM_VALUE(CAR(list)) >= FIXNUM_VALUE(CAR(object)))
6970	    LispDestroy("%s: %ld is out of range, index %ld",
6971			STRFUN(builtin),
6972			FIXNUM_VALUE(CAR(list)),
6973			FIXNUM_VALUE(CAR(object)));
6974    }
6975
6976    for (count = sequence = 0, list = values;
6977	 CONSP(CDR(list));
6978	 list = CDR(list), sequence++) {
6979	for (offset = 0, object = array->data.array.dim;
6980	     offset < sequence; object = CDR(object), offset++)
6981	    ;
6982	for (accum = 1, object = CDR(object); CONSP(object);
6983	     object = CDR(object))
6984	    accum *= FIXNUM_VALUE(CAR(object));
6985	count += accum * FIXNUM_VALUE(CAR(list));
6986    }
6987
6988    for (array = array->data.array.list; count > 0; array = CDR(array), count--)
6989	;
6990
6991    RPLACA(array, value);
6992
6993    return (value);
6994}
6995
6996LispObj *
6997Lisp_XeditDocumentationStore(LispBuiltin *builtin)
6998/*
6999 lisp::documentation-store symbol type string
7000 */
7001{
7002    LispDocType_t doc_type;
7003
7004    LispObj *symbol, *type, *string;
7005
7006    string = ARGUMENT(2);
7007    type = ARGUMENT(1);
7008    symbol = ARGUMENT(0);
7009
7010    CHECK_SYMBOL(symbol);
7011
7012    /* type is checked in LispDocumentationType() */
7013    doc_type = LispDocumentationType(builtin, type);
7014
7015    if (string == NIL)
7016	/* allow explicitly releasing memory used for documentation */
7017	LispRemDocumentation(symbol, doc_type);
7018    else {
7019	CHECK_STRING(string);
7020	LispAddDocumentation(symbol, string, doc_type);
7021    }
7022
7023    return (string);
7024}
7025