
/* Copyright 1989 GROUPE BULL -- See license conditions in file COPYRIGHT
 * Copyright 1989 Massachusetts Institute of Technology
 */
/**************************************************************\
* 							       *
* WOOL.c:						       *
* main body of interpreter				       *
* 							       *
* Here are defined Wool-only functions.			       *
* To add a function:					       *
* 	- declare it (coded in or extern)		       *
* 	- add it to the declaration of predefined functions    *
* 	  in the wool_init function			       *
* 							       *
* 'Kludged' to support the flex skeleton parser for V2.3.7   *
*				Dec 1992, O. Kirch	       *
\**************************************************************/

#if defined SVR4
#define SYSV
#endif

#include <stdio.h>
#include <sys/types.h>
#include <sys/file.h> 
#include <sys/times.h>
#include <signal.h> 
#include <sys/stat.h>
#if defined(IBM_RT) && defined(SYSV)
#include <unistd.h>
#endif /* IBM_RT && SYSV */
#if defined(i386) || defined(stellar)
#include <unistd.h>
#endif /* i386 */
#if defined(sun) && defined(SYSV)
#include <unistd.h>
#endif /* sun && SYSV */
#if defined(linux)
#include <unistd.h>
#endif /* linux */
#if defined(SYSV)
#include <limits.h>
#endif

#include <string.h>
#include <stdlib.h>

#include "INTERN.h"
#include "wool.h"
#include "EXTERN.h"
#include "wl_atom.h"
#include "wl_coll.h"
#include "wl_func.h"
#include "wl_list.h"
#include "wl_number.h"
#include "wl_string.h"
#include "wl_pointer.h"
#include "wl_active.h"
#include "wl_name.h"
#include "yacc.h"

#ifdef SYSV
#include <string.h>
#include <sys/utsname.h>
#else /* SYSV */
#include <sys/wait.h>
#include <strings.h>
#include <sys/timeb.h>
#endif /* SYSV */

#ifdef STATS
extern WOOL_OBJECT zrtstats();
extern WOOL_OBJECT wlcfstats();
#endif /* STATS */
extern WOOL_OBJECT progn();
extern WOOL_OBJECT wool_print_nary();
extern WOOL_OBJECT wool_with(), wool_with_eval(); 
extern int 	wool_error_in_profile;
extern char 	*yytext;
DECLARE_strchr;

#ifdef DEBUG
#ifdef GWM
extern WOOL_OBJECT WLFsm_fp();
extern WOOL_OBJECT WLState_fp();
extern WOOL_OBJECT WLArc_fp();
#endif /* GWM */
#endif /* DEBUG */

/* VARS */

#ifdef SECURE
/* some helps */
char *wool_methods_names[] = {
    "WOOL_type_name",
    "WOOL_eval",
    "WOOL_print",
    "WOOL_free",
    "WOOL_execute",
    "WOOL_set",
    "WOOL_get_C_value",
    "WOOL_open",
    "WOOL_close",
    "WOOL_process_event",
    "WOOL_copy",
    "WOOL_get_dimensions",
    "WOOL_draw",
    "WOOL_equal",
    "WOOL_reconfigure",
    "WOOL_setq",
    "WOOL_silent_eval",
    "WOOL_UNDEFINED",
    "WOOL_UNDEFINED",
    "WOOL_UNDEFINED",
    "WOOL_UNDEFINED"
};

#endif /* SECURE */

#ifdef DO_BUS_ERROR
#define DoABusError() *((int *)0) = 1
#else /* DO_BUS_ERROR */
#define DoABusError()
#endif /* DO_BUS_ERROR */

/*
 * all constructors sets reference count of the object to 0
 * You should call check_reference on this object if it was only
 * a temporary one!
 */

/*******************\
* 		    *
*  run-time errors  *
* 		    *
\*******************/

static int             wool_error_position;
static char 	      *wool_error_expecting;
static int	       wool_error_print_argc;
static WOOL_OBJECT    *wool_error_print_argv;

/* echoes 1st, 2nd, 3rd, nth */

char *
English_enumeration_postfix(n)
int n;
{
    switch (n) {
	case 1: return "st";
	case 2: return "nd";
	case 3: return "rd";
	default: return "th";
    }
}

#ifdef __STDC__
extern void wool_print_error_message(int error, const void *message, char *text);
#else
extern void wool_print_error_message();
#endif

/*
 * wool_error:
 * This is the standard error function. It is really brutal, as it wool_prints
 * an error message an then LONGJMPs to "wool_goes_here_on_error" with an
 * error value, for now 1.
 * You can feel free to redefine wool_goes_here_on_error.
 * 
 * WARNING: Don't forget to clean your structures before going here,
 * you won't return!
 */

WOOL_OBJECT
#ifdef __STDC__
_wool_error(int error, const void *message)
#else
_wool_error(error, message)
int             error;		/* error number (see wool.h) */
char           *message;	/* anything, in fact */
#endif
{
    static int      is_in_wool_error;

    if (is_in_wool_error) {
	stop_if_in_dbx();
	goto direct_longjmp;
    }
    is_in_wool_error = 1;
    if (wool_do_print_errors && error != SILENT_ERROR) {
	wool_print_error_message(error, message, "WOOL ERROR: ");
	wool_stack_dump(0);
	fflush(stdout);
	fflush(stderr);
    }
    if (!wool_continue_reading_on_error)
	wool_error_status = 1;
    wool_error_handler();		/* C package handler */
    DoABusError();			/* debug only */
    WLStackFrame_pop_to(wool_stackframe_on_error);
    calling_function_current = calling_function_stack +
	calling_function_on_error;
direct_longjmp:
    is_in_wool_error = 0;
    stop_if_in_dbx();
    longjmp(wool_goes_here_on_error, 1);
    /* NOTREACHED *//* for lint */
    return NIL;
}

void
#ifdef __STDC__
wool_print_error_message(int error, const void *message, char *text)
#else
wool_print_error_message(error, message, text)
int             error;		/* error number (see wool.h) */
char           *message;	/* anything, in fact */
char           *text;		/* WOOL ERROR: */
#endif
{
#ifndef DEBUG
    wool_newline();
#endif /* DEBUG */
    if (wool_is_reading_file) {
	wool_printf("\"%s\"", wool_is_reading_file);
	wool_printf(": line %d\n", yylineno);
    }
    wool_puts(text);
    switch (error) {
    case UNDEFINED_VARIABLE:
	wool_printf("undefined variable: %s", message);
	break;
    case BAD_NUMBER_OF_ARGS:
	wool_printf("bad number of arguments %d", message);
	break;
    case UNDEFINED_FUNCTION:
	wool_puts("undefined function: ");
	wool_print(message);
	break;
    case BAD_DEFUN:
	wool_printf("bad definition of function: %s", message);
	break;
    case BAD_ARG_TYPE:
	wool_printf("bad %d", wool_error_position + 1);
	wool_printf("%s argument: ",
		    English_enumeration_postfix(wool_error_position + 1));
	wool_print(message);
	if (wool_error_expecting) {
	    wool_puts(" (its type is \"");
	    wool_print(((WOOL_OBJECT) message) -> type[0]);
	    if (wool_error_expecting[0])
	      wool_printf("\", was expecting a \"%s",
			  wool_error_expecting);
	    wool_puts("\")");
	}
	break;
    case RELEASING_ATOM:
	wool_printf("Internal error: trying to free atom: %s", message);
	break;
    case NOT_AN_ATOM:
	wool_printf("Not an atom: %s", message);
	break;
    case BAD_LOCAL_SYNTAX:
	wool_printf("bad local variable declaration. %s", message);
	break;
    case SYNTAX_ERROR:
	wool_printf("%s", message);
	break;
    case INTERNAL_ERROR:
	wool_printf("Internal error: %s", message);
	break;
    case TIME_EXCEEDED:
	wool_printf("Evaluation took longer than %ld second(s) -- Aborted",
		    message);
	break;
    case CANNOT_SET:
	wool_puts("Cannot set a \"");
	wool_print_type(message);
	wool_puts("\": ");
	wool_print(message);
	break;
    case CANNOT_GET_C_VALUE:
	wool_puts("Cannot figure how to get a number from a \"");
	wool_print_type(message);
	wool_puts("\": ");
	wool_print(message);
	break;
    case TOO_MANY_PARAMETERS:
	wool_puts("Too many parameters for a C function call: ");
	wool_print(message);
	break;
    case NON_WOOL_OBJECT:
	wool_printf("%s on a non wool object.", message);
	break;
    case UNDEFINED_METHOD:
#ifdef DEBUG
	wool_printf("Undef WOOL method %s for type \"",
		    wool_methods_names[WOOL_current_method]);
#else /* DEBUG */
	wool_puts("Bad object type \"");
#endif /* DEBUG */
	WLAtom_print(((WOOL_OBJECT) message) -> type[0]);
	wool_puts("\" for object: ");
	wool_print(message);
	break;
    case NO_MEMORY:
	wool_puts("No more memory, bye...\n");
	wool_end(1);
	break;
    case NOT_REDEFINABLE:
	wool_puts("Cannot redefine object: ");
	wool_print(message);
	break;
    case NOT_MODIFIABLE:
	wool_puts("Cannot modify object: ");
	wool_print(message);
	break;
    case USER_ERROR:
	wool_print_nary(wool_error_print_argc, wool_error_print_argv);
	break;
    default:			/* suppose first arg was a string! */
	wool_printf(error, message);
    }
    wool_newline();
}

/* wool_trigger_error
 * triggers an error from wool
 */

WOOL_OBJECT
wool_trigger_error(argc, argv)
int argc;
WOOL_OBJECT	argv[];
{
    wool_error_print_argc = argc;
    wool_error_print_argv = argv;
    return wool_error(USER_ERROR, 0);
}

/* wool_error_has_occurred:
 * encapsulates statements to trap errors without printing them
 */

WOOL_OBJECT
wool_error_has_occurred(argc, argv)
int argc;
WOOL_OBJECT	argv[];
{
    int             we_got_an_error = 0;
    int old_wool_do_print_errors = wool_do_print_errors;

    save_wool_error_resume_point();
    wool_do_print_errors = 0;
    if (set_wool_error_resume_point()) {
	we_got_an_error = 1;
    } else {
	progn(argc, argv);
    }
    wool_do_print_errors = old_wool_do_print_errors;
    restore_wool_error_resume_point();
    return (we_got_an_error ? TRU : NIL);
}

/* bad argument call
 */

WOOL_OBJECT
bad_argument(argument, position, expecting)
WOOL_OBJECT	argument;
int		position;
char	       *expecting;
{
    wool_error_position = position;
    wool_error_expecting = expecting;
    return wool_error(BAD_ARG_TYPE, argument);
}

/* wool warning messages 
 * returns 1 if printed
 */

int
wool_warning(alarm_text)
char	*alarm_text;
{
    if(wool_do_print_errors){
	wool_puts(alarm_text);
	if (wool_is_reading_file) {
	    wool_printf("\"%s\"", wool_is_reading_file);
	    wool_printf(": line %d, ", yylineno);
	}
	wool_stack_dump(0);
	return 1;
    } else
    	return 0;
}

int
wool_warning1(alarm_text, data)
char	*alarm_text;
char    *data;
{
    char msg[MAX_TEMP_STRING_SIZE];

    sprintf(msg, alarm_text, data);
    return wool_warning(msg);
}


/* executes an expression, returns if error (do not jump to toplevel)
 * return eval if no error occurred, NULL otherwise
 */

WOOL_OBJECT
wool_eval_and_catch_errors(wool_expr)
WOOL_OBJECT wool_expr;
{
    WOOL_OBJECT	    result = 0;

    save_wool_error_resume_point();	/* contains decls */
    if (set_wool_error_resume_point()) {
	result = 0;
    } else {
	result = WOOL_send(WOOL_eval, wool_expr, (wool_expr));
    }
    restore_wool_error_resume_point();
    return  result;
}

/* same but do not return a value
 */

wool_eval_and_catch_errors_proc(wool_expr)
WOOL_OBJECT wool_expr;
{
    int             local_zrt_size = zrt_size;

    wool_eval_and_catch_errors(wool_expr);
    zrt_gc(local_zrt_size);
}
  

/* 
 * undefined method, one for each number of parameters.
 */

WOOL_OBJECT
wool_undefined_method_1(object)
WOOL_OBJECT object;
{
    return wool_error(UNDEFINED_METHOD, object);
}

WOOL_OBJECT
wool_undefined_method_2(object, param1)
WOOL_OBJECT object;
WOOL_OBJECT param1;
{
    return wool_error(UNDEFINED_METHOD, object);
}

#ifdef DEBUG
#define RMAGIC		0x5555	/* magic # on range info (see malloc.c)*/

extern char *malloc_zone_begin, *malloc_zone_end;

wool_send_is_valid(message, object)
int message;
WOOL_OBJECT object;
{
    if (!object
	|| ((char *) object) < malloc_zone_begin
	|| ((char *) object) >= malloc_zone_end
	|| *((int *) object) == FREED_MAGIC
	|| ((((WOOL_Atom) object -> type[0]) -> type != WLAtom)
	    && (((WOOL_Atom) object -> type[0]) -> type != WLActive)
	    && (((WOOL_Atom) object -> type[0]) -> type != WLPointer)
	    && (((WOOL_Atom) object -> type[0]) -> type != WLName)))
	wool_error(NON_WOOL_OBJECT, wool_methods_names[message]);
}

wool_object_is_valid(object)
WOOL_OBJECT object;
{
    if (!object
	|| ((char *) object) < malloc_zone_begin
	|| ((char *) object) >= malloc_zone_end
	|| *((int *) object) == FREED_MAGIC
	|| ((((WOOL_Atom) object -> type[0]) -> type != WLAtom)
	    && (((WOOL_Atom) object -> type[0]) -> type != WLActive)
	    && (((WOOL_Atom) object -> type[0]) -> type != WLPointer)
	    && (((WOOL_Atom) object -> type[0]) -> type != WLName)))
	return 0;
    else
	return 1;
}

#endif /* DEBUG */

/*********************************************************\
* 							  *
* The definition of C WOOL routines			  *
* These constitue the WOOL interface to the C programmer  *
* 							  *
\*********************************************************/

/*
 * wool_read:
 * reads an expression from the input (string or stream).
 * returns this expression or NULL if EOF reached
 * In case of syntax error, returns NIL
 * the read expression is in the global variable wool_read_expr,
 * if you need it. (this global is maintained for ref count purposes)
 * You don't need to free it since it's done at the beginning of this
 * routine.
 * Beware that it could be overwritten by a subsequent call to wool_eval
 * or wool_read !
 */

WOOL_OBJECT
wool_read()
{
    if (yyparse())
	return NULL;		/* returns NULL if error */
    else
	return wool_read_expr;	/* returns parsed expression */
}

/*
 * wool_pool:
 * this routine MUST be used when you want to make successive calls to
 * wool_read to parse an expression. It stores the string passed as argument
 * and returns the parenthese level. Thus a normal use would be to call
 * wool_pool with successive lines, while it returns a non-zero value,
 * and then call wool_read on the pooled buffer maintained by wool_poll,
 * whose address is stored in the global wool_pool_buffer.
 * (the parenthese level is in the int wool_pool_parenthese_level)
 * The buffer is reset by calling wool_pool with a NULL argument.
 */

int
wool_pool(s)
char           *s;		/* the string to be put in the pool */
{
    int             required_length;

    if (!wool_pool_buffer)
	wool_pool_buffer =
	    (char *) Malloc(wool_pool_buffer_size);
    if (!s) {
	*wool_pool_buffer = '\0';
	return wool_pool_parentheses_level = 0;
    } else {
	if ((int) (strlen(wool_pool_buffer) + (required_length = strlen(s)))
	    >= wool_pool_buffer_size) {
	    wool_pool_buffer_size +=
		Max(wool_pool_buffer_size, required_length) + 4;
	    wool_pool_buffer = (char *)
		Realloc(wool_pool_buffer, wool_pool_buffer_size);
	}
	strcat(wool_pool_buffer, "\n");
	strcat(wool_pool_buffer, s);
	{
	    char           *oldstream, old_input_buf[AHEAD_BUF_SIZE];
	    int             old_type;

	    old_type = wool_input_redirect(1, s, &oldstream, old_input_buf);
	    while (yylex() != END_OF_FILE);
	    wool_input_redirect(old_type, oldstream, NULL, NULL);
	    wool_unput(old_input_buf);
	    return wool_pool_parentheses_level;
	}
    }
}

/*
 * wool_eval:
 * evals an expression given as argument;
 * returns the result of the evaluation
 * if you want to keep the result, increase its reference count!
 * In case of eval error, calls wool_error which returns NIL
 */

WOOL_OBJECT
wool_eval(read_expr)
WOOL_OBJECT read_expr;
{
    return WOOL_send(WOOL_eval, read_expr, (read_expr));
}

/***************************************************************************\
* 									    *
* WOOL USER routines:							    *
* here are the definition of the standard routines binded to wool atoms by  *
* wool_init								    *
* 									    *
\***************************************************************************/

/*
 * The NULL function is there only as a placeholder
 */

WOOL_OBJECT
NIL_FUNC()
{
    return NIL;
}

/*
 * quoting can be implemented as a function:
 * 'foo ==> (quote foo)
 */

WOOL_OBJECT
wool_quote(obj)
WOOL_OBJECT obj;
{
    return obj;
}

/*
 * eval is the opposite of quoting
 */

WOOL_OBJECT
eval(obj)
WOOL_OBJECT     obj;
{
    return WOOL_send(WOOL_eval, obj, (obj));
}

/* copy an object (useful for lists)
 */

WOOL_OBJECT
wool_copy(obj)
WOOL_OBJECT     obj;
{
    return WOOL_send(WOOL_copy, obj, (obj));
}

/*
 * Arithmetic functions, patterned along Le_Lisp ones
 */

WOOL_OBJECT
wool_divide(n1, n2)
WOOL_Number n1, n2;
{
    return (WOOL_OBJECT) WLNumber_make(n2 -> number ?
				       n1 -> number / n2 -> number : 0);
}

WOOL_OBJECT
wool_modulo(n1, n2)
WOOL_Number n1, n2;
{
    Num tmp = n2 -> number ? n1 -> number % n2 -> number : 0;
    
    if (tmp < 0)
	tmp = tmp + n2 -> number;
    
    return (WOOL_OBJECT) WLNumber_make(tmp);
}

WOOL_OBJECT
wool_multiply(n1, n2)
WOOL_Number n1, n2;
{
    return (WOOL_OBJECT) WLNumber_make(n1 -> number * n2 -> number);
}

WOOL_OBJECT
wool_add(argc,argv)
int		argc;
WOOL_Number     argv[];
{
    WOOL_TYPE       type;

    if (argc == 0)
	wool_error(BAD_NUMBER_OF_ARGS, (char *)argc);
    type = argv[0] -> type;
    if (type == WLList || argv[0] == (WOOL_Number) NIL)
	return WLList_concat(argc, argv);
    else if (type == WLNumber)
	return add_numbers(argc, argv);
    else
	must_be_string(argv[0], 0);
    return add_strings(argc, argv);
}

WOOL_OBJECT
wool_minus(argc, argv)
int		argc;
WOOL_Number     argv[];
{
    switch (argc) {
    case 1:
	return (WOOL_OBJECT) WLNumber_make(-(argv[0] -> number));
    case 2:
	return (WOOL_OBJECT) WLNumber_make(
				 (argv[0] -> number) - (argv[1] -> number));
    case 0:
	return wool_error(BAD_NUMBER_OF_ARGS, (char *)argc);
    default: {int i, result = argv[0] -> number;
	      for (i = 1; i < argc; i++)
		  result -= argv[i] -> number;
	      return (WOOL_OBJECT) WLNumber_make(result);
	  }	
    }
}

/* comparisons
 */

WOOL_OBJECT
wool_equal(o1, o2)
WOOL_OBJECT     o1, o2;
{
    return WOOL_send(WOOL_equal, o1, (o1, o2));
}

WOOL_OBJECT
wool_eq(o1, o2)
WOOL_OBJECT     o1, o2;
{
    if (o1 == o2)
	return TRU;
    else
	return NIL;
}

WOOL_OBJECT
greater_than(o1, o2)
WOOL_OBJECT     o1, o2;
{
    if (o1 -> type != o2 -> type)
	return NIL;
    if (((o1 -> type == WLNumber) &&
	 (((WOOL_Number) o1) -> number > ((WOOL_Number) o2) -> number))
	|| ((o1 -> type == WLString) &&
	    strcmp(((WOOL_String) o1) -> string,
		   ((WOOL_String) o2) -> string) == 1))
	return TRU;
    else
	return NIL;
}

WOOL_OBJECT
lesser_than(o1, o2)
WOOL_OBJECT     o1, o2;
{
    if (o1 -> type != o2 -> type)
	return NIL;
    if (((o1 -> type == WLNumber) &&
	 (((WOOL_Number) o1) -> number < ((WOOL_Number) o2) -> number))
	|| ((o1 -> type == WLString) &&
	    strcmp(((WOOL_String) o1) -> string,
		   ((WOOL_String) o2) -> string) == -1))
	return TRU;
    else
	return NIL;
}
    
/* wool_compare returns -1, 0, +1 if <, =, > and () if not comparable.
 */

WOOL_OBJECT
wool_compare(o1, o2)
WOOL_Number     o1, o2;
{
    if (o1 -> type != o2 -> type)
	return NIL;
    if (o1 -> type == WLNumber) {
	if (o1 -> number < o2 -> number)
	    return (WOOL_OBJECT) WLNumber_make(-1);
	else if (o1 -> number > o2 -> number)
	    return (WOOL_OBJECT) WLNumber_make(1);
	else
	    return (WOOL_OBJECT) WLNumber_make(0);
    } else {
	must_be_string(o1, 0);
	return (WOOL_OBJECT) WLNumber_make(
					strcmp(((WOOL_String) o1) -> string,
					     ((WOOL_String) o2) -> string));
    }
}

/* member of a list, or substring of a string:
 * returns position in list or string or NIL if not found
 */

WOOL_OBJECT
wool_member(obj, list)
WOOL_String	obj;
WOOL_List	list;
{
    if (list -> type == WLList) {	/* list */
	WOOL_OBJECT    *p = list -> list;
	WOOL_OBJECT    *last = p + list -> size;

	while (p < last) {
	    if (WOOL_send(WOOL_equal, obj, (obj, *p)) != NIL)
		return (WOOL_OBJECT) WLNumber_make(p - list -> list);
	    p++;
	}
	return NIL;
    } else if (list == (WOOL_List) NIL) {	/* () */
	return NIL;
    } else {				/* substring of a string */
	char           *p, *string;
	int             length;

	must_be_string(list, 1);
	must_be_string(obj, 0);
	string = ((WOOL_String) list) -> string;
	length = strlen(obj -> string);
	for (p = string; *p; p++)
	    if (!strncmp(obj -> string, p, length))
		return (WOOL_OBJECT) WLNumber_make(p - string);
	return NIL;
    }
}

/* logical operations
 */

WOOL_OBJECT
not(obj)
WOOL_OBJECT     obj;
{
    if (obj == NIL)
	return TRU;
    else
	return NIL;
}

WOOL_OBJECT
and(argc, argv)
int		argc;
WOOL_OBJECT     argv[];
{
    int    i;

    for (i = 0; i < argc; i++)
	if (WOOL_send(WOOL_eval, argv[i], (argv[i])) == NIL)
	    return NIL;
    return TRU;
}

WOOL_OBJECT
or(argc, argv)
int		argc;
WOOL_OBJECT     argv[];
{
    int    i;
    WOOL_OBJECT tmp;

    for (i = 0; i < argc; i++)
	if ((tmp = WOOL_send(WOOL_eval, argv[i], (argv[i]))) != NIL)
	    return tmp;
    return NIL;
}

/*
 * bitwise-operators
 */

WOOL_OBJECT
wool_bitwise_or(argc, argv)
int argc;
WOOL_Number argv[];
{
    int             num = 0;

    while (argc--)
	num |= argv[argc] -> number;
    return (WOOL_OBJECT) WLNumber_make(num);
}

WOOL_OBJECT
wool_bitwise_and(argc, argv)
int argc;
WOOL_Number argv[];
{
    int             num = argv[0] -> number;

    while (argc--)
	num &= argv[argc] -> number;
    return (WOOL_OBJECT) WLNumber_make(num);
}

WOOL_OBJECT
wool_bitwise_xor(argc, argv)
int argc;
WOOL_Number argv[];
{
    int             num = 0;

    while (argc--)
	num ^= argv[argc] -> number;
    return (WOOL_OBJECT) WLNumber_make(num);
}

/*
 * Setq, the most important function
 * implemented as a method
 */

WOOL_OBJECT
setq(atom, value)
WOOL_Atom atom;
WOOL_OBJECT value;
{
    return WOOL_send(WOOL_set, atom, (atom, value));
}

WOOL_OBJECT
set(atom, value)
WOOL_Atom atom;
WOOL_OBJECT value;
{
    return WOOL_send(WOOL_setq, atom, (atom, value));
}

/*
 * unbind: release storage of an atom
 */

WOOL_OBJECT
wool_unbind(atom)
WOOL_Atom atom;
{
    extern WOOL_OBJECT WLAtom_unbind(), WLName_unbind();

    if (atom -> type == WLAtom)
	return WLAtom_unbind(atom);
    else if (atom -> type == WLName)
	return WLName_unbind(atom);
    else
	return bad_argument(atom, 0, "atom or name");
}

/*
 * boundp: tests if atom has already be defined
 */

WOOL_OBJECT
wool_boundp(atom)
WOOL_OBJECT atom;
{
    WOOL_OBJECT	value = WOOL_send(WOOL_silent_eval, atom, (atom));

    if (value && value != UNDEFINED_WOOL_VALUE)
	return (atom == NIL ? TRU : atom);
    else
	return NIL;
}

/*
 * list: makes a list of its evaluated arguments
 */

WOOL_OBJECT
wool_list(argc, argv)
int argc;
WOOL_OBJECT     argv[];
{
    WOOL_List list;
    WOOL_OBJECT *q, *last;

    if (!argc)
	return NIL;
    list = wool_list_make(argc);
    q = list -> list;
    last = argv + argc;

    while (argv < last)
        increase_reference(*q++ = *argv++);
    return (WOOL_OBJECT) list;
}

/*
 * length: of a string or list
 */

WOOL_Number
wool_length(obj)
WOOL_List     obj;
{
    if (obj -> type == WLList)
	return WLNumber_make(obj -> size);
    else if (obj == (WOOL_List) NIL)
	return WLNumber_make(0);
    else {
	must_be_string(obj, 0);
	return WLNumber_make(strlen(((WOOL_String) obj) -> string));
    }
}

/*
 * BEWARE: hacker's corner!
 * returns the object of the same type found at location number!
 * 	type		action
 * 	number		*int
 * 	string		*char
 * 	()		object
 * 	atom		adress of pointer object
 */

WOOL_OBJECT
wool_hack(type, pointer)
WOOL_OBJECT type;
WOOL_Number pointer;
{
    if (type -> type == WLNumber)
	return (WOOL_OBJECT) WLNumber_make(*((int *) pointer -> number));
    else if (type -> type == WLString)
	return (WOOL_OBJECT) WLString_make(pointer -> number);
    else if (type == NIL) {
	increase_reference(pointer -> number);
	return (WOOL_OBJECT) pointer -> number;
    } else if (type -> type == WLAtom)
	return (WOOL_OBJECT) WLNumber_make(pointer);
    else
	return NIL;
}

/* used time function expressed in milliseconds
 */

#ifdef CLK_TCK
#define TIME_UNIT CLK_TCK
#else
#define TIME_UNIT 60
#endif

WOOL_OBJECT
wool_used_time()
{
    long time;

#ifdef SYSV_TIME
    struct tms buffer;

#ifdef CLK_TCK
    time = (times(&buffer) * 1000) / TIME_UNIT;
#else
    times(&buffer);
    time = ((buffer.tms_utime + buffer.tms_stime) * 1000 ) / TIME_UNIT;
#endif
#else /* !SYSV_TIME */
    struct timeb time_bsd;

    ftime(&time_bsd);
    time = 1000 * time_bsd.time + time_bsd.millitm;
#endif /* !SYSV_TIME */

    return (WOOL_OBJECT) WLNumber_make(time);
}

/*
 * atoi and itoa
 */

WOOL_OBJECT
wool_atoi(obj)
WOOL_String obj;
{
    must_be_string(obj, 0);
    return (WOOL_OBJECT) WLNumber_make(atoi(obj -> string));
}

WOOL_OBJECT
wool_itoa(obj)
WOOL_Number obj;
{
    char tmp_str[20];

    must_be_number(obj, 0);
    sprintf(tmp_str, "%d", obj -> number);
    return (WOOL_OBJECT) WLString_make(tmp_str);
}

/*
 * Shell escape: executes a SYSTEM of the string (or atom) argument
 */

WOOL_OBJECT
shell(argc, argv)
int		argc;
WOOL_String     argv[];
{
    int             i;
    char          **program_args = (char **) Malloc(sizeof(char *) * (argc + 1));

    if (!argc)
	wool_error(BAD_NUMBER_OF_ARGS, (char *)argc);
    for (i = 0; i < argc; i++) {
	must_be_string(argv[i], i);
	program_args[i] = argv[i] -> string;
    }
    program_args[argc] = NULL;
    if (!fork()) {
	wool_clean_before_exec();
	execvp(program_args[0], program_args);
	exit(127);
    }
    Free(program_args);
    return NIL;
}

/* signals management avoiding defunct processes */

#if defined SYSV || defined SVR4
void
ChildDeathHandler(sig)
int sig;
{
    wait(0);
    signal(SIGCLD, ChildDeathHandler);
}

SignalsInit()
{
    signal(SIGCLD, ChildDeathHandler);
}
#else /* SYSV */
#include <sys/time.h>
#include <sys/resource.h>
void
ChildDeathHandler(sig)
int sig;
{
    int status;

    wait3(&status, WNOHANG, 0);
    signal(SIGCHLD, ChildDeathHandler);
}

SignalsInit()
{
    signal(SIGCHLD, ChildDeathHandler);
}
#endif /* SYSV */

/*
 *  print value of an object
 */

WOOL_OBJECT
wool_print(obj)
WOOL_OBJECT     obj;
{
    wool_print_level = 0;
    return WOOL_send(WOOL_print, obj, (obj));
}

WOOL_OBJECT
wool_print_nary(argc, argv)
int argc;
WOOL_OBJECT     argv[];
{
    int             i;
    WOOL_OBJECT	result = NIL;

    wool_print_level = 0;
    for (i = 0; i < argc; i++)
	result = WOOL_send(WOOL_print, argv[i], (argv[i]));
    yyoutflush();
    return result;
}

static int
expand_string_stream (str)
  WOOL_STRING_STREAM str;
{
    char *new_buf;
    int nbytes = str->last - str->buffer + 1;
    int ptr_pos = str->ptr - str->buffer;

    str->buffer = Realloc (str->buffer, 2*nbytes);        
    str->last = str->buffer + nbytes - 1;
    str->ptr = str->buffer + ptr_pos;
}

WOOL_OBJECT
wool_with_output_to_string (argc, argv)
  int argc;
  WOOL_OBJECT *argv;
{
    WOOL_STRING_STREAM str, WOOL_STRING_STREAM_make (); 
    int old_type;
    char *old_stream;
    WOOL_OBJECT result;

    str = WOOL_STRING_STREAM_make (256, expand_string_stream);
    old_type = wool_output_redirect (1, str, &old_stream);
    progn (argc, argv);
    wool_output_redirect (old_type, old_stream, NULL);

    result = (WOOL_OBJECT) WLString_make (str->buffer);
    WOOL_STRING_STREAM_free (str);
    return result;
}

WOOL_OBJECT
wool_with_output_to_file (argc, argv)
  int argc;
  WOOL_OBJECT *argv;
{
    FILE* f;
    WOOL_String fname;
    int old_type;
    char *old_stream;
    char* filename;
    int ok;

    fname = (WOOL_String) WOOL_send(WOOL_eval, *argv, (*argv));
    must_be_string(fname, 0);
    f = fopen(fname->string, "w");
    if (f) {
        old_type = wool_output_redirect (0, f, &old_stream);
        progn (argc-1, argv+1);
        wool_output_redirect (old_type, old_stream, NULL);
        fclose(f);
        return TRU;
    } else {
	wool_puts(wool_application_NAME);
    	wool_printf(": could not open file : %s\n", fname->string);
	return NIL;
    }
}

/*
 * (progn inst1 ... instn)
 * evals the n instructions then return the last one's result
 */

WOOL_OBJECT
progn(argc, argv)
int    argc;
WOOL_OBJECT *argv;
{
    if (argc) {
	int             local_zrt_size = zrt_size;

	while (--argc > 0) {
	    WOOL_send(WOOL_eval, *argv, (*argv));
	    zrt_gc(local_zrt_size);
	    argv++;
	}
	return WOOL_send(WOOL_eval, *argv, (*argv));
    } else {
	return NIL;
    }
}

/*
 * if "a la emacs"
 * if test thenclause [test thenclause]* [elseclause]
 * nearly a COND, in fact
 */

WOOL_OBJECT
wool_if(argc, argv)
int    argc;
WOOL_OBJECT *argv;
{
    while (argc > 1) {
	if (WOOL_send(WOOL_eval, *argv, (*argv)) != NIL) {
	    return WOOL_send(WOOL_eval, *(argv + 1), (*(argv + 1)));
	}
	argc -= 2;
	argv += 2;
	if (argc == 1) {
	    return WOOL_send(WOOL_eval, *argv, (*argv));
	}
    }
    return NIL;
}

/*
 * while cond inst1 ... instn
 * classical while
 */

WOOL_OBJECT
wool_while(argc, argv)
int    argc;
WOOL_OBJECT *argv;
{
    while (WOOL_send(WOOL_eval, *argv, (*argv)) != NIL) {
	progn(argc - 1, argv + 1);
    }
    return NIL;
}

/*
 * for:
 * (for var list-of-values instructions...)
 */

WOOL_OBJECT
wool_for_loop(argc, argv, map)
int    argc;
WOOL_List *argv;
int		map;
{
    WOOL_List       list, result_list;
    WOOL_OBJECT	    /* previous_value, */ result;
    int i;

    if (argc < 3)
	wool_error(BAD_NUMBER_OF_ARGS, (char *)argc);
    must_be_atom(argv[0], 0);
    list = (WOOL_List) WOOL_send(WOOL_eval, argv[1], (argv[1]));
    if (WLList_length(list) == 0)  /* (list == (WOOL_List) NIL) */
    	return NIL;

    WLStackFrame_push_value(argv[0]);

    if(map) {
    	result_list = wool_list_make(list -> size);
    	for (i = 0; i < list -> size; i++) {
	    WOOL_send(WOOL_setq, argv[0], (argv[0], list -> list[i]));
	    increase_reference(result_list -> list[i] =
		progn(argc - 2, argv + 2));
        }
    } else {
	for (i = 0; i < list -> size; i++) {
            WOOL_send(WOOL_setq, argv[0], (argv[0], list -> list[i]));
            result = progn(argc - 2, argv + 2);
	}
    }

    WLStackFrame_pop();

    return (map ? (WOOL_OBJECT) result_list : result);
}

WOOL_OBJECT
wool_for(argc, argv)
int    argc;
WOOL_List *argv;
{
    return wool_for_loop(argc, argv, 0);
}

WOOL_OBJECT
wool_mapfor(argc, argv)
int    argc;
WOOL_List *argv;
{
    return wool_for_loop(argc, argv, 1);
}



/*
 * TAG/EXIT:
 * (tag tag insts...)
 * (exit tag insts...)
 */

typedef struct _JumpingPoint {
    WOOL_StackFrame frame;
    int 	    level; 		/* in calling_function_stack */
    jmp_buf	    jump_buffer;
    WOOL_OBJECT	result;
} *JumpingPoint;

WOOL_OBJECT
wool_tag(argc, argv)
int             argc;
WOOL_String    *argv;
{
    struct _JumpingPoint tag;
    WOOL_OBJECT     result;
    WOOL_Pointer    tag_name;

    if (argc < 2)
	wool_error(BAD_NUMBER_OF_ARGS, (char *)argc);
    must_be_string(argv[0], 0);
    wool_self_pointer_make(argv[0] -> string, '\024', &tag_name);
    *(tag_name -> ptr) = (long) &tag;
    tag.frame = wool_current_stackframe;
    tag.level = calling_function_current - calling_function_stack;
    if (setjmp(tag.jump_buffer) != 0) /* nonzero is return from longjmp */
	result = tag.result;
    else
	result = progn(argc - 1, argv + 1);
    *(tag_name -> ptr) = 0;
    return result;
}
	
WOOL_OBJECT
wool_exit(argc, argv)
int             argc;
WOOL_String    *argv;
{
    JumpingPoint    tag;
    WOOL_Pointer    tag_name;
    WOOL_OBJECT     result;

    if (argc < 1)
	wool_error(BAD_NUMBER_OF_ARGS, (char *)argc);
    must_be_string(argv[0], 0);
    if (wool_self_pointer_make(argv[0] -> string, '\024', &tag_name)
	&& *(tag_name -> ptr)) {
	tag = (JumpingPoint) * (tag_name -> ptr);
	result = argc > 1 ? progn(argc - 1, argv + 1) : NIL;
	WLStackFrame_pop_to(tag -> frame);
	calling_function_current = calling_function_stack + tag -> level;
	tag -> result = result;
	longjmp(tag -> jump_buffer, -1);
    } else
	wool_error(UNDEFINED_VARIABLE, argv[0] -> string);
    return NIL;
}

/* the host name as a string
 */

WOOL_OBJECT
wool_hostname_get()
{
    if (!wool_host_name) {
	char buf[256];
	int maxlen = 256;
	int len;
#ifdef SYSV
	struct utsname name;
	
	uname (&name);
	len = strlen (name.nodename);
	if (len >= maxlen) len = maxlen - 1;
	strncpy (buf, name.nodename, len);
	buf[len] = '\0';
#else /* SYSV */
	buf[0] = '\0';
	(void) gethostname (buf, maxlen);
	buf [maxlen - 1] = '\0';
	len = strlen(buf);
#endif /* SYSV */
	increase_reference(wool_host_name = (WOOL_OBJECT) WLString_make(buf));
    }
    return  wool_host_name;
}

/*
 * very useful: de and df!
 * USAGE:
 * (de <func-name> (parameter-list) inst1 ... instn)
 * returns  the atom pointing to the subr
 */

WOOL_OBJECT
de(argc, argv)
int             argc;
WOOL_OBJECT    *argv;
{
    return defun(WLExpr, argc, argv);
}

WOOL_OBJECT
df(argc, argv)
int             argc;
WOOL_OBJECT    *argv;
{
    return defun(WLFExpr, argc, argv);
}

/*
 * wool_loadfile:
 * raw loadfile function: search EXACTLY for name in parameter
 */

WOOL_OBJECT
wool_loadfile(string)
char           *string;
{
    FILE           *fd, *oldinput;
    char            filename[MAX_TEMP_STRING_SIZE];
    int             local_zrt_size = zrt_size;

    if (!string)
	return NIL;
    strcpy(filename, string);
    fd = fopen(filename, "r");
    if (fd) {
	int             we_got_an_error = 0;
	char           *old_file = wool_is_reading_file;
	char            old_input_buffer[AHEAD_BUF_SIZE];
	int             oldtype = wool_input_redirect(0, fd, &oldinput,
						      old_input_buffer);
	int             old_yylineno = yylineno;

	save_wool_error_resume_point();	/* contains decls */
	yylineno = 1;
	wool_is_reading_file = filename;
	/* now, we must close the file and redirect input on error */
	if (wool_continue_reading_on_error) {
	    set_wool_error_resume_point();
	    while (wool_read()) {
		wool_eval(wool_read_expr);
		zrt_gc(local_zrt_size);
	    }
	} else {
	    if (set_wool_error_resume_point()) {
		we_got_an_error = 1;
	    } else {
		while (wool_read()) {
#ifdef READ_ECHO
		    printf("\"%s\"[%d]: ", filename, yylineno);
		    wool_print(wool_read_expr);
		    wool_puts("\n");
		    fflush(stdout);
		    fflush(stderr);
#endif
		    wool_eval(wool_read_expr);
		    zrt_gc(local_zrt_size);
		}
	    }
	}
	wool_input_redirect(oldtype, oldinput, 0, 0);
	wool_unput(old_input_buffer);
	fclose(fd);
	restore_wool_error_resume_point();
	wool_is_reading_file = old_file;
	yylineno = old_yylineno;
	if (we_got_an_error)
	    _wool_error(SILENT_ERROR, 0);
	return TRU;
    } else {
	return NIL;
    }
}

/* executes a given string */

WOOL_OBJECT
wool_execute_string(string)
char           *string;
{
    FILE           *oldinput;
    int             we_got_an_error = 0;
    char           *old_file = wool_is_reading_file;
    char            old_input_buffer[AHEAD_BUF_SIZE];
    int             oldtype = wool_input_redirect(1, string, &oldinput,
						  old_input_buffer);
    int             old_yylineno = yylineno;
    int             local_zrt_size = zrt_size;

    save_wool_error_resume_point();
    yylineno = 1;
    wool_is_reading_file = 0;
    /* now, we redirect input on error */
    if (set_wool_error_resume_point()) {
	we_got_an_error = 1;
    } else {
	while (wool_read()) {
	    wool_eval(wool_read_expr);
	    zrt_gc(local_zrt_size);
	}
    }
    wool_input_redirect(oldtype, oldinput, 0, 0);
    wool_unput(old_input_buffer);
    restore_wool_error_resume_point();
    wool_is_reading_file = old_file;
    yylineno = old_yylineno;
    if (we_got_an_error)
	return NIL;
    else
	return TRU;
}

/* same callable from wool */

WOOL_OBJECT
wool_execute_wool_string(string)
WOOL_String string;
{
    return wool_execute_string(string -> string);
}

/*
 * tests if file exists and is readable
 */

char *
file_exists(name)
char *name;
{
    /* Should detect if the file is a regular file, not a directory */
    struct stat sbuf;

    if ((stat(name, &sbuf)) < 0)
	return 0;
    if ((sbuf.st_mode & S_IFMT) == S_IFREG) {	/* regular file */
	if (access(name, R_OK)) {	/* good mode flags */
	    return 0;
	} else
	    return name;
    } else
	return 0;
}

/*
 * file_with_optional_extension:
 * see if file exists with extension
 */

char *
file_with_optional_extension(filename, extension)
char           *filename;
char           *extension;
{
    static char     filename_wl[MAX_TEMP_STRING_SIZE];

    strcpy(filename_wl, filename);
    strcat(filename_wl, extension);
    if(file_exists(filename_wl))
    	return filename_wl;
    return file_exists(filename);
}

/*
 * file_in_path:
 * find file with using path, extensions, etc...
 * complete_filename is a pointer to temporary space
 */

char *
file_in_path(filename, extension, path, complete_filename)
char           *filename, *extension, *path, *complete_filename;
{
    char  *directory, *name;
    int 	    dirlen;

    if (strchr(filename, '/')) {	/* absolute pathname */
	return (file_with_optional_extension(filename, extension));
    } else {			/* relative pathname */
	while (*path) {
	    directory = path;
	    dirlen = 0;
	    while (*path && (*path != ':')) {
		path++;
		dirlen++;
	    }
	    if (*path)
		path++;
	    complete_filename[dirlen] = '\0';
	    if (dirlen) {
		strncpy(complete_filename, directory, dirlen);
		if (complete_filename[dirlen - 1] != '/')
		    strcat(complete_filename, "/");
	    }
	    strcat(complete_filename, filename);
	    if (name = file_with_optional_extension(complete_filename,
						    extension))
		return name;
	}
	return 0;
    }
}


/*
 * loading a file (callable from wool)
 */

WOOL_OBJECT
wool_loadfile_in_path(string)
WOOL_String     string;
{
    char            temp_filename[MAX_TEMP_STRING_SIZE];
    char	   *actual_pathname = file_in_path(string -> string,
    			wool_text_extension, wool_path, temp_filename);

    if(NIL == wool_loadfile(actual_pathname)) {
	wool_puts(wool_application_NAME);
    	wool_printf(": file not found: %s\n", string -> string);
	return NIL;
    } else {
    	return (WOOL_OBJECT) WLString_make(actual_pathname);
    }
}

/*
 * cond for compatibility
 */

WOOL_OBJECT 
wool_cond(argc, argv)
int    argc;
WOOL_OBJECT *argv;
{
    WOOL_OBJECT    *list;
    WOOL_OBJECT     result = wool_if(argc * 2,
				  list = wool_flatten_pairlist(argc, argv));

    Free(list);
    return result;
}

/*
 * A context is a list of pairs variable-name/variable-values (atom/object).
 * Context operations are:
 * 
 * 	context-save: archives in the context the current values of
 * 		variables (sets to () undefined ones...)
 * 	context-restore: sets the variables to their archived values
 */

must_be_context(context, n)
WOOL_List	context;
int		n;
{
    if ((context != (WOOL_List) NIL)
	 && ((context -> type != WLList)
             || (context -> size % 2)))
	bad_argument(context, n, "even-sized list");
}

WOOL_OBJECT
wool_context_save(context)
WOOL_List	context;
{
    int             i;
    WOOL_List       new;

    must_be_context(context, 0);
    if (context == (WOOL_List) NIL)
        return NIL;
    new = wool_list_make(context -> size);
    for (i = 0; i < context -> size; i += 2) {
#ifdef        STUPID
      WOOL_OBJECT tmp;
#endif 
      increase_reference(new -> list[i] = context -> list[i]);
#ifdef  STUPID
      tmp = context -> list[i];
      if (tmp -> type == WLAtom &&
#else
      if (context -> list[i] -> type == WLAtom &&
#endif
	    (!((WOOL_Atom) context -> list[i]) -> c_val)) 
	    /*
	     * if atom is undefined, take the following element of the list
	     * as a value of new context 
	     */
	    increase_reference(new -> list[i + 1] = context -> list[i + 1]);
	else
	    increase_reference(new -> list[i + 1] =
	    WOOL_send(WOOL_eval, context -> list[i], (context -> list[i])));
    }
    return (WOOL_OBJECT) new;
}

WOOL_OBJECT
wool_context_restore(context)
WOOL_List	context;
{
    int             i;

    must_be_context(context, 0);
    for (i = 0; i < context -> size; i += 2)
	WOOL_send(WOOL_setq, context -> list[i],
		  (context -> list[i], context -> list[i + 1]));
    return (WOOL_OBJECT) context;
}

/*
 * wool_getenv:
 * makes the WOOL_String out of getenv(wool_string)
 */

WOOL_OBJECT
wool_getenv(obj)
WOOL_String obj;
{
    char  *result;

    if (result = (char *) getenv(obj -> string))
	return (WOOL_OBJECT) WLString_make(result);
    else
	return (WOOL_OBJECT) NIL_STRING;
}

/*
 * makes an atom out of a string
 */

WOOL_OBJECT
wool_atom_of_string(s)
WOOL_String	s;
{
    return (WOOL_OBJECT) wool_atom(s -> string);
}

/*
 * if object is from type, ok.
 * if not, evaluates it and call wool_error if result is still not
 * YOU MUST check reference on result when no longer needed!
 */

WOOL_OBJECT
wool_type_or_evaluate(object, type)
WOOL_OBJECT object;
WOOL_TYPE   type;
{
    if ((object == NIL) || (object -> type == type))
	return object;
    if (((object = WOOL_send(WOOL_eval, object, (object))) -> type == type)
	|| (object == NIL))
	return object;
    if (object == UNDEFINED_WOOL_VALUE)
	return wool_error(UNDEFINED_VARIABLE, "");
    return bad_argument(object, 0, WOOL_TYPE_P_NAME(type));
}

wool_user_end()
{
    wool_puts("Bye.\n");
    wool_end(0);
}

#ifdef DEBUG 			/* some routines convenient for debugging: */

stop_if_in_dbx(){} /* used in dbx */

int		dbxi = 0; 
WOOL_Atom 	dbxa;
WOOL_OBJECT 	dbxo;

PO(n)
int n;
{
    wool_print(n);
    wool_newline();
    fflush(stdout);
}

/*
 * break function for gwm for debugging purposes
 */

WOOL_OBJECT
wool_break(){return NIL;}	/* WOOL user break! */

WOOL_OBJECT
wp(obj)
WOOL_OBJECT     obj;
{
    wool_print_level = 0;
    WOOL_send(WOOL_print, obj, (obj));
    wool_newline();
    yyoutflush();
}

/*
 * type(obj) prints its type (under dbx!)
 */

wt(obj)
WOOL_OBJECT obj;
{
    printf("%s\n", ((WOOL_Atom) obj -> type[0]) -> p_name);
}

char *
type(obj)
WOOL_OBJECT obj;
{
    return ((WOOL_Atom) obj -> type[0]) -> p_name;
}


WOOL_OBJECT
wool_print_newline(obj)
WOOL_OBJECT     obj;
{
    WOOL_send(WOOL_print, obj, (obj));
    putchar('\n');
    return obj;
}

struct _UniqId {
    int size;
    int last;
    int *list;
};

int
UniqId(UI, n)
struct _UniqId *UI;
int n;
{
    int i;
    if (!UI->size) {
	UI->size = 4000;
	UI->list = (int *) Malloc(UI->size);
    }
    for (i=0; i< UI->last; i++) {
	if (UI->list[i] == n) {
	    return i;
	}
    }
    UI->list[i] = n;
    return (UI->last)++;
}

/* checksum on jump-buffers */

int
jmpbuf_checksum(jmpbuf)
int *jmpbuf;
{
    int i, result = 0;
    static struct _UniqId UI;
    for(i=0; i < sizeof(jmp_buf)/sizeof(int); i++)
	result = (result << 3) + (result >> 28) + jmpbuf[i];
    return UniqId(&UI, result);
}

#endif /* DEBUG */

WOOL_OBJECT
wool_type(obj)
WOOL_OBJECT obj;
{
    return (WOOL_OBJECT) obj->type[0];
}

wool_print_type(obj)
WOOL_OBJECT obj;
{
    WLAtom_print(obj -> type[0]);
}

#ifdef MONITOR
WOOL_OBJECT
wool_moncontrol(num)
WOOL_Number	num;
{
    moncontrol(num ->number);
}
#endif /* MONITOR */

/* tracing info
 */

WOOL_OBJECT
wool_get_trace()
{
    return (WOOL_OBJECT) WLNumber_make(wool_tracing_on);
}

/* (trace obj)
 * obj = expr, evals expr at each eval of list
 * obj = 0/1 turns tracing on/off (without resetting expr)
 * obj = t resets expr
 */

WOOL_OBJECT
wool_set_trace(obj)
WOOL_OBJECT obj;
{

    if(obj -> type == WLNumber) {
	wool_tracing_on = ((WOOL_Number) obj) -> number;
    } else if(obj == NIL) {
	wool_tracing_on = 0;
    } else {
	wool_tracing_on = 1;
	decrease_reference(wool_tracing_on_EXPR);
	if (obj == TRU)
	    wool_tracing_on_EXPR = 0;
	else
	    increase_reference(wool_tracing_on_EXPR = obj);
    }
    wool_still_tracing = wool_tracing_on;
    return obj;
}

/***************************************************************************\
* 									    *
* add .:$HOME:$HOME/gwm: before built-in-path (INSTALL_DIR) and returns it  *
* (malloced)								    *
* 									    *
\***************************************************************************/

char *
wool_fix_path(built_in_path)
char *built_in_path;
{
    char           *home = (char *) getenv("HOME");
    char           *path =
    Malloc(strlen(built_in_path) + 9 + 2*(home ? strlen(home) : 0));

    strcpy(path, ".:");
    if (home) {
	strcat(path, home);
	strcat(path, ":");
	strcat(path, home);
	strcat(path, "/");
	strcat(path, WOOL_APP_name);
	strcat(path, ":");
    }
    strcat(path, built_in_path);
    return path;
}

/****************************************\
* 					 *
*  INITIALISATION:			 *
*  to be called before everything else 	 *
* 					 *
\****************************************/

/*
 * wool_init returns 0 if all is ok
 * It calls its parameter function if not NULL, just before reading user
 * profile. Used by GWM for setting default keywords
 */

int
wool_init(client_initialisation)
int	(*client_initialisation)();
{
    /* initialize tables */
    zrt_init();
    dft_init();
    WLNumber_init();
    HashTable_init();

    /* initialize signals */
    SignalsInit();

    /* initialize wool's objects */

    wool_atom_make(WOOL_OBJECT, NIL, "()", NIL);	/* atoms */
    increase_reference(wool_atom("nil") -> c_val = NIL);
    wool_atom_make(WOOL_OBJECT, TRU, "t", TRU);
    NIL_STRING_make();
    WA_progn = (WOOL_OBJECT) wool_atom("progn");

    /* init stack */
    WLStackFrame_init();
    calling_function_init();

    /* intitialise predefined functions (Subrs) */
    QUOTE = wool_subr_make(WLFSubr, wool_quote, "quote", 1);
    wool_subr_make(WLFSubr, setq, "setq", 2);
    wool_subr_make(WLFSubr, setq, ":", 2);
    wool_subr_make(WLSubr, set, "set", 2);
    wool_subr_make(WLSubr, wool_multiply, "*", 2);
    wool_subr_make(WLSubr, wool_divide, "/", 2);
    wool_subr_make(WLSubr, wool_modulo, "%", 2);
    wool_subr_make(WLSubr, wool_add, "+", NARY);
    wool_subr_make(WLSubr, wool_minus, "-", NARY);
    increase_reference(wool_atom("defun") -> c_val =
		       wool_subr_make(WLFSubr, de, "de", NARY));
    increase_reference(wool_atom("defunq") -> c_val =
		       wool_subr_make(WLFSubr, df, "df", NARY));
    wool_subr_make(WLFSubr, wool_lambda_make, "lambda", NARY);
    wool_subr_make(WLFSubr, wool_lambdaq_make, "lambdaq", NARY);
    wool_subr_make(WLSubr, wool_atom_of_string, "atom", 1);
    wool_subr_make(WLFSubr, progn, "progn", NARY);
    wool_subr_make(WLFSubr, wool_if, "if", NARY);
    wool_subr_make(WLFSubr, wool_cond, "cond", NARY);
    increase_reference(wool_atom("equal") -> c_val =
		       wool_subr_make(WLSubr, wool_equal, "=", 2));
    wool_subr_make(WLSubr, wool_eq, "eq", 2);
    wool_subr_make(WLSubr, greater_than, ">", 2);
    wool_subr_make(WLSubr, lesser_than, "<", 2);
    wool_subr_make(WLSubr, wool_compare, "compare", 2);
    wool_subr_make(WLSubr, shell, "!", NARY);
    increase_reference(wool_atom("print") -> c_val =
		       wool_subr_make(WLSubr, wool_print_nary, "?", NARY));
    wool_subr_make(WLFSubr, wool_with_output_to_string,
                   "with-output-to-string", NARY);
    wool_subr_make(WLFSubr, wool_with_output_to_file,
                   "with-output-to-file", NARY);
    wool_subr_make(WLSubr, not, "not", 1);
    wool_subr_make(WLFSubr, and, "and", NARY);
    wool_subr_make(WLFSubr, or, "or", NARY);
    increase_reference(wool_atom("together") -> c_val =
	       wool_subr_make(WLSubr, wool_bitwise_or, "bitwise-or", NARY));
    wool_subr_make(WLSubr, wool_bitwise_and, "bitwise-and", NARY);
    wool_subr_make(WLSubr, wool_bitwise_xor, "bitwise-xor", NARY);
    wool_subr_make(WLFSubr, wool_while, "while", NARY);
    wool_subr_make(WLFSubr, wool_for, "for", NARY);
    wool_subr_make(WLFSubr, wool_mapfor, "mapfor", NARY);
    wool_subr_make(WLFSubr, wool_with, "with", NARY);
    wool_subr_make(WLFSubr, wool_with_eval, "with-eval", NARY);
    wool_subr_make(WLSubr, wool_context_save, "context-save", 1);
    wool_subr_make(WLSubr, wool_context_restore, "context-restore", 1);
    wool_subr_make(WLSubr, wool_loadfile_in_path, "load", 1);
    wool_subr_make(WLSubr, wool_execute_wool_string, "execute-string", 1);
    wool_subr_make(WLSubr, eval, "eval", 1);
    wool_subr_make(WLSubr, wool_getenv, "getenv", 1);
    wool_subr_make(WLSubr, wool_unbind, "unbind", 1);
    wool_subr_make(WLSubr, wool_boundp, "boundp", 1);
    wool_subr_make(WLSubr, WLString_match, "match", NARY);
    wool_subr_make(WLSubr, wool_length, "length", 1);
    wool_subr_make(WLSubr, WLList_sub, "sublist", NARY);
    increase_reference(wool_atom("nth") -> c_val =
		       wool_subr_make(WLSubr, WLList_nth, "#", NARY));
    increase_reference(wool_atom("replace-nth") -> c_val =
		    wool_subr_make(WLSubr, WLList_replace_nth, "##", NARY));
    wool_subr_make(WLSubr, WLList_delete_nth, "delete-nth", 2);
    wool_subr_make(WLFSubr, wool_user_end, "end", 0);
    wool_subr_make(WLSubr, wool_atoi, "atoi", 1);
    wool_subr_make(WLSubr, wool_itoa, "itoa", 1);
    wool_subr_make(WLSubr, wool_hack, "hack", 2);
    wool_subr_make(WLSubr, wool_list, "list", NARY);
    wool_subr_make(WLFSubr, wool_tag, "tag", NARY);
    wool_subr_make(WLFSubr, wool_exit, "exit", NARY);
    wool_subr_make(WLSubr, wool_type, "type", 1);
    wool_subr_make(WLFSubr, wool_error_has_occurred, "error-occurred", NARY);
    wool_subr_make(WLSubr, wool_trigger_error, "trigger-error", NARY);
    wool_subr_make(WLSubr, wool_copy, "copy", 1);
    wool_subr_make(WLSubr, wool_used_time, "elapsed-time", 0);
    wool_subr_make(WLSubr, wool_member, "member", 2);
    wool_subr_make(WLSubr, WLNamespace_make, "namespace-make", 0);
    wool_subr_make(WLSubr, WLNamespace_add, "namespace-add", 1);
    wool_subr_make(WLSubr, WLNamespace_remove, "namespace-remove", 2);
    wool_subr_make(WLSubr, WLName_add, "defname", NARY);
    wool_subr_make(WLSubr, WLNamespace_set_current, "namespace", 2);
    wool_subr_make(WLSubr, WLName_namespace, "namespace-of", 1);
    wool_subr_make(WLSubr, WLNamespace_size, "namespace-size", 1);
    wool_subr_make(WLSubr, WLList_qsort, "sort", 2);
    wool_active_make("hostname", wool_hostname_get, NULL);

    /* --- */
#ifdef STATS
    wool_subr_make(WLFSubr, zrtstats, "gcinfo", 0);
    wool_subr_make(WLFSubr, wlcfstats, "wlcfinfo", 0);
    wool_subr_make(WLFSubr, WlMstats, "meminfo", 0);
    wool_subr_make(WLFSubr, hashstats, "hashinfo", 0);
    wool_subr_make(WLFSubr, oblist, "oblist", 0);
#else /* STATS */
    wool_subr_make(WLFSubr, NIL_FUNC, "gcinfo", 0);
    wool_subr_make(WLFSubr, NIL_FUNC, "wlcfinfo", 0);
    wool_subr_make(WLFSubr, NIL_FUNC, "meminfo", 0);
    wool_subr_make(WLFSubr, NIL_FUNC, "hashinfo", 0);
    wool_subr_make(WLFSubr, NIL_FUNC, "oblist", 0);
#endif /* STATS */
#ifdef DEBUG
    wool_subr_make(WLFSubr, wool_break, "break", 0);
#ifdef GWM
    wool_subr_make(WLSubr, WLFsm_fp, "print-fsm", 1);
    wool_subr_make(WLSubr, WLState_fp, "print-state", 1);
    wool_subr_make(WLSubr, WLArc_fp, "print-arc", 1);
#endif
#else /* DEBUG */
    wool_subr_make(WLFSubr, NIL_FUNC, "break", 0);
#endif /* DEBUG */
#ifdef MONITOR
    wool_subr_make(WLSubr, wool_moncontrol, "moncontrol", 1);
#endif /* MONITOR */
#ifdef USER_DEBUG
    wool_active_make("trace", wool_get_trace, wool_set_trace);
    wool_pointer_make("trace-level", &wool_tracing_level);
#endif /* USER_DEBUG */

    wool_pointer_make("print-level", &wool_max_print_level);
    wool_pointer_make("stack-print-level", &wool_max_stack_print_level);

    /* here do client inits before the profile is read */
    if (client_initialisation)
	(*client_initialisation) ();

    /* first time, load the user file */
    wool_error_status = 0;
    if (!set_wool_error_resume_point()) {
	zrt_gc(0);
	if (wool_loadfile_in_path(wool_atom(wool_user_profile_name)) == NIL) {
	    return 1;
	}
    }
    wlcf_flush();
    wool_error_in_profile = wool_error_status;
    set_wool_error_resume_point();
    dft_gc();

    return 0;
}

