/* csl.c Copyright (C) 1989-2002 Codemist Ltd */
/*
* This is Lisp system for use when delivering Lisp applications
* (such as REDUCE) on pretty-well any computer that has an ANSI
* C compiler where sizeof(void *)==4 and there is in integral
* type that is also 4 bytes wide. In fact I can also manage if
* sizeof(void *)==8 provided that it can be arranged that all
* addresses returned by malloc() have only their bottom 32 bits
* set... And with even more care I can manage on true 64 bit systems,
* although arithmetic then does not take advantage of the wider words.
*/
/*
* This code may be used and modified, and redistributed in binary
* or source form, subject to the "CCL Public License", which should
* accompany it. This license is a variant on the BSD license, and thus
* permits use of code derived from this in either open and commercial
* projects: but it does require that updates to this code be made
* available back to the originators of the package.
* Before merging other code in with this or linking this code
* with other packages or libraries please check that the license terms
* of the other material are compatible with those of this.
*/
/* Signature: 5d375d00 17-Feb-2003 */
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include "machine.h"
#include "tags.h"
#include "externs.h"
#include "arith.h"
#include "read.h"
#include "stream.h"
#include "entries.h"
#include "version.h"
#define INCLUDE_ERROR_STRING_TABLE 1
#include "cslerror.h"
#undef INCLUDE_ERROR_STRING_TABLE
#ifdef TIMEOUT
#include "timeout.h"
#endif
#ifdef OLD_THINK_C
#include <console.h>
#include <memory.h>
#undef nil /* Yuk - this is defined by <types.h> which <memory.h> loads */
#endif
#ifdef __WATCOMC__
#include <float.h>
#endif
#ifdef SOCKETS
#include "sockhdr.h"
#ifndef ms_windows
#include <sys/wait.h>
#endif
static int port_number, remote_store, current_users, max_users;
SOCKET socket_server;
int sockets_ready;
clock_t cpu_timeout;
time_t elapsed_timeout;
static int char_to_socket(int c);
#endif
/*
* These flags are used to ensure that protected symbols don't get
* overwritten by default, and that the system keeps quiet about it.
*/
CSLbool symbol_protect_flag = YES;
CSLbool warn_about_protected_symbols = NO;
#ifdef WINDOW_SYSTEM
CSLbool use_wimp;
#endif
#ifdef USE_MPI
int32 mpi_rank,mpi_size;
#endif
/*****************************************************************************/
/* Error reporting and recovery */
/*****************************************************************************/
#ifdef CHECK_STACK
/*
* Some computers are notably unhelpful about their behaviour when the system
* stack overflows. As a debugging tool on such machines I can do limited
* software checking by inserting explicit calls to this function in places
* I think may be critical. I impose an arbitrary limit on the stack size,
* but that is better than no checking and random corruption - maybe. Please
* do not enable CHECK_STACK unless it is really necessary to hunt a bug,
* since it is miserably expensive and crude.
*/
#define C_STACK_ALLOCATION 240000
static int spset = 0;
static int32 spbase = 0, spmin;
static int stack_depth[C_STACK_ALLOCATION], stack_line[C_STACK_ALLOCATION];
static char *stack_file[C_STACK_ALLOCATION];
static int c_stack_ptr = 0;
int check_stack(char *file, int line)
{
int32 temp = (int32)&temp;
if (!spset)
{ spbase = spmin = temp;
spset = 1;
c_stack_ptr = 0;
stack_depth[0] = temp;
stack_line[0] = line;
stack_file[0] = file;
}
if (temp < stack_depth[c_stack_ptr] && c_stack_ptr<C_STACK_ALLOCATION-2)
c_stack_ptr++;
else while (temp > stack_depth[c_stack_ptr] && c_stack_ptr>0)
c_stack_ptr--;
stack_depth[c_stack_ptr] = temp;
stack_line[c_stack_ptr] = line;
stack_file[c_stack_ptr] = file;
if (temp < spmin-250) /* Only check at granularity of 250 bytes */
{ int i;
term_printf("Stack depth %d at file %s line %d\n",
spbase-temp, file, line);
for (i=c_stack_ptr; i>=0 && i > c_stack_ptr-30; i--)
term_printf(" %s:%d", stack_file[i], stack_line[i]);
term_printf("\n");
spmin = temp;
if (temp < spbase-C_STACK_ALLOCATION) return 1;
}
return 0;
}
#endif
/*
* error_message_table was defined in cslerror.h since that way I can keep its
* contents textually close to the definitions of the message codes that it
* has to relate to.
*/
#define errcode(n) error_message_table[n]
Lisp_Object MS_CDECL error(int nargs, int code, ...)
/*
* nargs indicates how many values have been provided AFTER the
* code. Thus nargs==0 will just display a simple message, nargs==1
* will be a message plus a value and so on. I will expect that the
* number of actual args here is well within any limits that I ought to
* impose.
*/
{
va_list a;
int i;
Lisp_Object nil = C_nil, w1;
Lisp_Object *w = (Lisp_Object *)&work_1;
if (nargs > ARG_CUT_OFF) nargs = ARG_CUT_OFF;
if (miscflags & (HEADLINE_FLAG|ALWAYS_NOISY))
{ err_printf("\n+++ Error %s: ", errcode(code));
/*
* There is now some painful shuffling around to get all the args
* to error() moved over onto the Lisp stack so that is garbage collection
* is triggered during printing all will be well.
*/
va_start(a, code);
for (i=0; i<nargs; i++) *w++ = va_arg(a, Lisp_Object);
va_end(a);
for (i=0; i<nargs; i++) push(*--w);
if (code != err_stack_overflow) /* Be cautious here! */
{ stackcheck0(nargs);
}
for (i=0; i<nargs; i++)
{ Lisp_Object p;
pop(p);
loop_print_error(p);
err_printf("\n");
}
}
if ((w1 = qvalue(break_function)) != nil &&
symbolp(w1) &&
qfn1(w1) != undefined1)
{ (*qfn1(w1))(qenv(w1), nil);
ignore_exception();
}
/*
* After doing this is is necessary to be VERY careful, since nil is
* used as a base register for lots of things... Still this is the
* cheapest way I can see to mark the need for unwinding.
*/
exit_reason = (miscflags & (MESSAGES_FLAG|ALWAYS_NOISY)) ? UNWIND_ERROR :
UNWIND_UNWIND;
exit_value = exit_tag = nil;
exit_count = 0;
flip_exception();
return nil;
}
Lisp_Object MS_CDECL cerror(int nargs, int code1, int code2, ...)
/*
* nargs indicated the number of EXTRA args after code1 & code2.
*/
{
Lisp_Object nil = C_nil, w1;
va_list a;
int i;
Lisp_Object *w = (Lisp_Object *)&work_1;
if (nargs > ARG_CUT_OFF) nargs = ARG_CUT_OFF;
if (miscflags & (HEADLINE_FLAG|ALWAYS_NOISY))
{ err_printf("\n+++ Error %s, %s: ", errcode(code1), errcode(code2));
va_start(a, code2);
for (i=0; i<nargs; i++) *w++ = va_arg(a, Lisp_Object);
va_end(a);
for (i=0; i<nargs; i++) push(*--w);
stackcheck0(nargs-2);
nil = C_nil;
for (i=0; i<nargs; i++)
{ Lisp_Object p;
pop(p);
loop_print_error(p);
err_printf("\n");
}
}
if ((w1 = qvalue(break_function)) != nil &&
symbolp(w1) &&
qfn1(w1) != undefined1)
{ (*qfn1(w1))(qenv(w1), nil);
ignore_exception();
}
/*
* After doing this is is necessary to be VERY careful, since nil is
* used as a base register for lots of things... Still this is the
* cheapest way I can see to mark the need for unwinding.
*/
exit_reason = (miscflags & (MESSAGES_FLAG|ALWAYS_NOISY)) ? UNWIND_ERROR :
UNWIND_UNWIND;
exit_value = exit_tag = nil;
exit_count = 0;
flip_exception();
return nil;
}
Lisp_Object interrupted(Lisp_Object p)
/*
* Could return onevalue(p) to proceed from the interrupt event...
*/
{
Lisp_Object nil = C_nil, w;
#ifdef WINDOW_SYSTEM
/*
* If I have a windowed system I expect that the mechanism for
* raising an exception will have had a menu that gave me a chance
* to decide whether to proceed or abort. Thus the following code
* is only needed if there is no window system active. On some systems
* this may be an active check.
*/
if (!use_wimp)
#endif
{
if (clock_stack == &consolidated_time[0])
{ clock_t t0 = read_clock();
/*
* On at least some (Unix) systems clock_t is a 32-bit signed value
* and CLOCKS_PER_SEC = 1000000. The effect is that integer overflow
* occurs after around 35 minutes of running. I must therefore check the
* clock and move information into a floating point variable at least
* every half-hour. With luck I will do it more like 20 times per second
* because I have code muck like this in a tick handler that is activated
* on a rather regular basis.
*/
double delta = (double)(t0 - base_time)/(double)CLOCKS_PER_SEC;
base_time = t0;
consolidated_time[0] += delta;
}
#ifndef NAG
term_printf(
"\n+++ [%.2f+%.2f] Type C to continue, A to abort, X to exit\n",
consolidated_time[0], gc_time);
ensure_screen(); nil = C_nil;
if (exception_pending()) return nil;
push(prompt_thing);
prompt_thing = CHAR_EOF;
other_read_action(READ_FLUSH, lisp_terminal_io);
for (;;)
{ int c = char_from_terminal(nil);
/*
* Note that I explicitly say "char_from_terminal()" here - this is because
* I do not expect to be interrupted unless there was a terminal available
* to send the interrupt! This is in fact a slightly marginal assumption.
*/
switch (c)
{
case 'c': case 'C': /* proceed as if no interrupt */
pop(prompt_thing);
return onevalue(p);
case 'a': case 'A': /* raise an exception */
break;
case 'x': case 'X':
my_exit(EXIT_FAILURE); /* Rather abrupt */
case '\n':
term_printf("C to continue, A to abort, X to exit: ");
ensure_screen(); nil = C_nil;
if (exception_pending()) return nil;
continue;
default: /* wait for A or C */
continue;
}
break;
}
pop(prompt_thing);
#endif
}
if (miscflags & (HEADLINE_FLAG|ALWAYS_NOISY))
err_printf("+++ Interrupted\n");
if ((w = qvalue(break_function)) != nil &&
symbolp(w) &&
qfn1(w) != undefined1)
{ (*qfn1(w))(qenv(w), nil);
ignore_exception();
}
exit_reason = (miscflags & (MESSAGES_FLAG|ALWAYS_NOISY)) ? UNWIND_ERROR :
UNWIND_UNWIND;
exit_value = exit_tag = nil;
exit_count = 0;
flip_exception();
return nil;
}
Lisp_Object aerror(char *s)
{
Lisp_Object nil = C_nil, w;
if (miscflags & (HEADLINE_FLAG|ALWAYS_NOISY))
err_printf("+++ Error bad args for %s\n", s);
if ((w = qvalue(break_function)) != nil &&
symbolp(w) &&
qfn1(w) != undefined1)
{ (*qfn1(w))(qenv(w), nil);
ignore_exception();
}
exit_reason = (miscflags & (MESSAGES_FLAG|ALWAYS_NOISY)) ? UNWIND_ERROR :
UNWIND_UNWIND;
exit_value = exit_tag = nil;
exit_count = 0;
flip_exception();
return nil;
}
Lisp_Object aerror0(char *s)
{
Lisp_Object nil = C_nil, w;
if (miscflags & (HEADLINE_FLAG|ALWAYS_NOISY))
err_printf("+++ Error: %s\n", s);
if ((w = qvalue(break_function)) != nil &&
symbolp(w) &&
qfn1(w) != undefined1)
{ (*qfn1(w))(qenv(w), nil);
ignore_exception();
}
exit_reason = (miscflags & (MESSAGES_FLAG|ALWAYS_NOISY)) ? UNWIND_ERROR :
UNWIND_UNWIND;
exit_value = exit_tag = nil;
exit_count = 0;
flip_exception();
#ifdef COMMON
/*
* This is to help me debug in the face of low level system crashes
*/
if (spool_file) fflush(spool_file);
#endif
return nil;
}
Lisp_Object aerror1(char *s, Lisp_Object a)
{
Lisp_Object nil = C_nil, w;
if (miscflags & (HEADLINE_FLAG|ALWAYS_NOISY))
{ err_printf("+++ Error: %s ", s);
loop_print_error(a);
err_printf("\n");
}
if ((w = qvalue(break_function)) != nil &&
symbolp(w) &&
qfn1(w) != undefined1)
{ (*qfn1(w))(qenv(w), nil);
ignore_exception();
}
exit_reason = (miscflags & (MESSAGES_FLAG|ALWAYS_NOISY)) ? UNWIND_ERROR :
UNWIND_UNWIND;
exit_value = exit_tag = nil;
exit_count = 0;
flip_exception();
#ifdef COMMON
/*
* This is to help me debug in the face of low level system crashes
*/
if (spool_file) fflush(spool_file);
#endif
return nil;
}
Lisp_Object aerror2(char *s, Lisp_Object a, Lisp_Object b)
{
Lisp_Object nil = C_nil, w;
if (miscflags & (HEADLINE_FLAG|ALWAYS_NOISY))
{ err_printf("+++ Error: %s ", s);
loop_print_error(a);
err_printf(" ");
loop_print_error(b);
err_printf("\n");
}
if ((w = qvalue(break_function)) != nil &&
symbolp(w) &&
qfn1(w) != undefined1)
{ (*qfn1(w))(qenv(w), nil);
ignore_exception();
}
exit_reason = (miscflags & (MESSAGES_FLAG|ALWAYS_NOISY)) ? UNWIND_ERROR :
UNWIND_UNWIND;
exit_value = exit_tag = nil;
exit_count = 0;
flip_exception();
#ifdef COMMON
/*
* This is to help me debug in the face of low level system crashes
*/
if (spool_file) fflush(spool_file);
#endif
return nil;
}
static Lisp_Object wrong(int wanted, int given, Lisp_Object env)
{
char msg[64];
Lisp_Object nil = C_nil;
CSL_IGNORE(nil);
sprintf(msg, "Function called with %d args where %d wanted", given, wanted);
if (is_cons(env)) env = qcdr(env);
if ((miscflags & (HEADLINE_FLAG|ALWAYS_NOISY)) && is_vector(env))
{ Lisp_Object fname = elt(env, 0);
err_printf("\nCalling ");
loop_print_error(fname);
err_printf("\n");
}
return aerror(msg);
}
Lisp_Object too_few_2(Lisp_Object env, Lisp_Object a1)
{
CSL_IGNORE(a1);
return wrong(2, 1, env);
}
Lisp_Object too_many_1(Lisp_Object env, Lisp_Object a1, Lisp_Object a2)
{
CSL_IGNORE(a1);
CSL_IGNORE(a2);
return wrong(1, 2, env);
}
Lisp_Object wrong_no_0a(Lisp_Object env, Lisp_Object a1)
{
CSL_IGNORE(a1);
return wrong(0, 1, env);
}
Lisp_Object wrong_no_0b(Lisp_Object env, Lisp_Object a1, Lisp_Object a2)
{
CSL_IGNORE(a1);
CSL_IGNORE(a2);
return wrong(0, 2, env);
}
Lisp_Object wrong_no_3a(Lisp_Object env, Lisp_Object a1)
{
CSL_IGNORE(a1);
return wrong(3, 1, env);
}
Lisp_Object wrong_no_3b(Lisp_Object env, Lisp_Object a1, Lisp_Object a2)
{
CSL_IGNORE(a1);
CSL_IGNORE(a2);
return wrong(3, 2, env);
}
Lisp_Object wrong_no_na(Lisp_Object env, Lisp_Object a1)
{
CSL_IGNORE(a1);
if (is_cons(env) && is_bps(qcar(env)))
return wrong(((unsigned char *)data_of_bps(qcar(env)))[0], 1, env);
else return aerror("function called with 1 arg when 0 or >= 3 wanted");
}
Lisp_Object wrong_no_nb(Lisp_Object env, Lisp_Object a1, Lisp_Object a2)
{
CSL_IGNORE(a1);
CSL_IGNORE(a2);
if (is_cons(env) && is_bps(qcar(env)))
return wrong(((unsigned char *)data_of_bps(qcar(env)))[0], 2, env);
else return aerror("function called with 2 args when 0 or >= 3 wanted");
}
Lisp_Object MS_CDECL wrong_no_1(Lisp_Object env, int nargs, ...)
{
CSL_IGNORE(env);
CSL_IGNORE(nargs);
return wrong(1, nargs, env);
}
Lisp_Object MS_CDECL wrong_no_2(Lisp_Object env, int nargs, ...)
{
CSL_IGNORE(env);
CSL_IGNORE(nargs);
return wrong(2, nargs, env);
}
Lisp_Object bad_special2(Lisp_Object env, Lisp_Object a1, Lisp_Object a2)
{
CSL_IGNORE(env);
CSL_IGNORE(a1);
CSL_IGNORE(a2);
return aerror("call to special form");
}
Lisp_Object MS_CDECL bad_specialn(Lisp_Object env, int nargs, ...)
{
CSL_IGNORE(env);
CSL_IGNORE(nargs);
return aerror("call to special form");
}
void MS_CDECL fatal_error(int code, ...)
{
/*
* Note that FATAL error messages are sent to the terminal, not to the
* error output stream. This is because the error output stream may be
* corrupted in such dire circumstances.
*/
term_printf("+++ Fatal error %s\n", errcode(code));
if (spool_file != NULL)
{
#ifdef COMMON
fprintf(spool_file, "\nFinished dribbling to %s.\n", spool_file_name);
#else
fprintf(spool_file, "\n+++ Transcript terminated after error +++\n");
#endif
fclose(spool_file);
spool_file = NULL;
}
my_exit(EXIT_FAILURE);
}
#ifndef __cplusplus
jmp_buf my_exit_buffer;
#endif
void my_exit(int n)
{
#ifdef USE_MPI
MPI_Finalize();
#endif
#if defined(FLEX) && defined(WINDOWS_NT) && defined(CWIN) && defined(NAG)
extern void close_lm();
close_lm();
/* /* What is realy needed here??? */
extern void rlnag();
rlnag();
#endif
#ifdef BUFFERED_STDOUT
ensure_screen();
#endif
#ifdef SOCKETS
if (sockets_ready) WSACleanup();
#endif
#ifdef WINDOW_SYSTEM
pause_for_user();
#endif
#ifdef CWIN
#ifdef __cplusplus
throw n;
#else
if (n == 0) n = 0x80000000;
longjmp(my_exit_buffer, n);
#endif
#else
#if defined(WINDOWS_NT) && defined(NAG)
{ extern void sys_abort(int);
sys_abort(n);
}
#else
#ifdef TICK_STREAM
remove_ticker();
#endif
exit(n);
#endif
#endif
}
static int return_code = 0;
CSLbool segvtrap = YES;
CSLbool batch_flag = NO;
CSLbool ignore_restart_fn = NO;
static void lisp_main(void)
{
Lisp_Object nil;
#ifndef __cplusplus
/*
* The setjmp here is to provide a long-stop for untrapped
* floating point exceptions.
*/
jmp_buf this_level, *save_level = errorset_buffer;
#endif
tty_count = 0;
while (YES)
/*
* The sole purpose of the while loop here is to allow me to proceed
* for a second try if I get a (cold-start) call.
*/
{ Lisp_Object *save = stack;
nil = C_nil;
#ifndef __cplusplus
errorset_buffer = &this_level;
#endif
errorset_msg = NULL;
#ifdef __cplusplus
try
#else
if (!setjmp(this_level))
#endif
{ if (supervisor != nil && !ignore_restart_fn)
{ miscflags |= HEADLINE_FLAG | MESSAGES_FLAG;
if (exit_charvec != NULL)
{ Lisp_Object a = read_from_vector(exit_charvec);
nil = C_nil;
if (exception_pending())
{ flip_exception();
a = nil;
}
exit_charvec = NULL;
push(a);
apply(supervisor, 1, nil, supervisor);
}
else apply(supervisor, 0, nil, supervisor);
}
/*
* Here the default read-eval-print loop used if the user has not provided
* a supervisor function.
*/
else read_eval_print(lisp_true);
}
#ifdef __cplusplus
catch (char *)
#else
else
#endif
{ if (errorset_msg != NULL)
{ term_printf("\n%s detected\n", errorset_msg);
errorset_msg = NULL;
}
unwind_stack(save, NO);
exit_reason = UNWIND_ERROR;
flip_exception();
signal(SIGFPE, low_level_signal_handler);
#ifdef __WATCOMC__
_control87(_EM_OVERFLOW | _EM_INVALID | _EM_DENORMAL |
_EM_ZERODIVIDE | _EM_INEXACT | _EM_UNDERFLOW,
_MCW_EM);
#endif
if (segvtrap) signal(SIGSEGV, low_level_signal_handler);
#ifdef SIGBUS
if (segvtrap) signal(SIGBUS, low_level_signal_handler);
#endif
#ifdef SIGILL
if (segvtrap) signal(SIGILL, low_level_signal_handler);
#endif
}
nil = C_nil;
if (exception_pending())
{ flip_exception();
if (exit_reason == UNWIND_RESTART)
{ if (exit_tag == fixnum_of_int(0)) /* "stop" */
return_code = (int)int_of_fixnum(exit_value);
else if (exit_tag == fixnum_of_int(1)) /* "preserve" */
{ char *msg = "";
return_code = EXIT_SUCCESS;
compression_worth_while = 128;
if (is_vector(exit_value) &&
type_of_header(vechdr(exit_value)) == TYPE_STRING)
msg = &celt(exit_value, 0);
preserve(msg);
nil = C_nil;
if (exception_pending())
{ flip_exception();
return_code = EXIT_FAILURE;
}
}
else /* "restart" */
{ int32 fd = stream_pushed_char(lisp_terminal_io);
char new_module[64], new_fn[64]; /* Limited name length */
int cold_start;
cold_start = (exit_value == nil);
#ifdef TICK_STREAM
remove_ticker();
/*
* Of course a tick may very well have happened rather recently - so
* I will flush it out now just to clear the air.
*/
if (stack >= stacklimit)
{ reclaim(nil, "stack", GC_STACK, 0);
ignore_exception();
}
#endif
cold_start = (exit_value == nil);
Lrds(nil, nil);
Lwrs(nil, nil);
/*
* If either of the above two calls to rds/wrs were to fail I would
* be in a big mess.
*/
if (!cold_start)
{ new_module[0] = 0;
new_fn[0] = 0;
if (exit_value != lisp_true)
{ Lisp_Object modname = nil;
if (is_cons(exit_value))
{ modname = qcar(exit_value);
exit_value = qcdr(exit_value);
if (is_cons(exit_value))
exit_value = qcar(exit_value);
}
if (symbolp(modname) && modname != nil)
{ modname = get_pname(modname);
if (exception_pending()) ignore_exception();
else
{ Header h = vechdr(modname);
int32 len = length_of_header(h) - CELL;
if (len > 63) len = 63;
memcpy(new_module,
(char *)modname + (CELL - TAG_VECTOR),
(size_t)len);
new_module[len] = 0;
}
}
if (symbolp(exit_value) && exit_value != nil)
{ exit_value = get_pname(exit_value);
if (exception_pending()) ignore_exception();
else
{ Header h = vechdr(exit_value);
int32 len = length_of_header(h) - CELL;
if (len > 63) len = 63;
memcpy(new_fn,
(char *)exit_value + (CELL - TAG_VECTOR),
(size_t)len);
new_fn[len] = 0;
}
}
}
}
while (vheap_pages_count != 0)
pages[pages_count++] = vheap_pages[--vheap_pages_count];
while (heap_pages_count != 0)
pages[pages_count++] = heap_pages[--heap_pages_count];
while (bps_pages_count != 0)
pages[pages_count++] = bps_pages[--bps_pages_count];
/*
* When I call restart-csl I will leave the random number generator where it
* was. Anybody who wants to reset if either to a freshly randomised
* configuration or to a defined condition must do so for themselves. For
* people who do not care too much what I do here is probably acceptable!
*/
MD5_Init();
MD5_Update((unsigned char *)errcode(err_registration), 32);
IreInit();
setup(cold_start ? 0 : 1, 0.0);
exit_tag = exit_value = nil;
exit_reason = UNWIND_NULL;
stream_pushed_char(lisp_terminal_io) = fd;
interrupt_pending = already_in_gc = NO;
polltick_pending = tick_pending = tick_on_gc_exit = NO;
if (!cold_start && new_fn[0] != 0)
{ Lisp_Object w;
if (new_module[0] != 0)
{ w = make_undefined_symbol(new_module);
Lload_module(nil, w);
ignore_exception();
}
w = make_undefined_symbol(new_fn);
nil = C_nil;
if (exception_pending()) ignore_exception();
else supervisor = w;
}
#ifdef TICK_STREAM
add_ticker();
#endif
continue;
}
}
}
/*
* In all normal cases when read_eval_print exits (i.e. all cases except
* if it terminates after (cold-start)) I exit here.
*/
#ifndef __cplusplus
errorset_buffer = save_level;
#endif
break;
}
}
#ifndef MS_DOS
#ifndef WINDOWS_NT
#ifndef WXWIN
CSLbool sigint_must_longjmp = NO;
#ifndef __cplusplus
jmp_buf sigint_buf;
#endif
void sigint_handler(int code)
{
/*
* Note that the only things that I am really allowed to do in a routine
* like this involve setting variables of type sig_atomic_t, which can not
* be viewed as much more than boolean. The code actually used here is
* somewhat more ambitious (== non-portable!) so must be viewed as delicate.
* ANSI guarantee that longjmp-ing out of a non-nested signal handler
* is valid, but some earlier C libraries have not supported this. Note that
* with C++ I will use throw rather than longjmp.
*/
/*
* tick_pending etc allow a steady stream of clock events to
* be handed to Lisp.
*/
interrupt_pending = 1;
signal(SIGINT, sigint_handler);
if (sigint_must_longjmp)
{
sigint_must_longjmp = NO;
#ifdef __cplusplus
throw((int *)0);
#else
longjmp(sigint_buf, 1);
#endif
}
#ifndef TICK_STREAM
/*
* If there is not a separate regular stream of ticks I will simulate
* the receipt of a tick here. Thus I need to be able to recognize "ticks"
* even on systems where there are no "real" ones.
*/
if (!tick_pending)
{
if (already_in_gc) tick_on_gc_exit = YES;
else
{
#ifndef NILSEG_EXTERNS
Lisp_Object nil = C_nil;
CSLbool xxx = NO;
if (exception_pending()) flip_exception(), xxx = YES;
#endif
tick_pending = YES;
saveheaplimit = heaplimit;
heaplimit = fringe;
savevheaplimit = vheaplimit;
vheaplimit = vfringe;
savecodelimit = codelimit;
codelimit = codefringe;
savestacklimit = stacklimit;
stacklimit = stackbase;
#ifndef NILSEG_EXTERNS
if (xxx) flip_exception();
#endif
}
}
#endif /* TICK_STREAM */
return;
}
#endif
#endif
#endif
#ifdef SOFTWARE_TICKS
int32 number_of_ticks = 0;
int32 countdown;
int deal_with_tick(void)
{
number_of_ticks++;
if (!tick_pending)
{
if (already_in_gc) tick_on_gc_exit = YES;
else
{
#ifndef NILSEG_EXTERNS
Lisp_Object nil = C_nil;
CSLbool xxx = NO;
if (exception_pending()) flip_exception(), xxx = YES;
#endif
tick_pending = YES;
saveheaplimit = heaplimit;
heaplimit = fringe;
savevheaplimit = vheaplimit;
vheaplimit = vfringe;
savecodelimit = codelimit;
codelimit = codefringe;
savestacklimit = stacklimit;
stacklimit = stackbase;
#ifndef NILSEG_EXTERNS
if (xxx) flip_exception();
#endif
}
}
#ifdef SOFTWARE_TICKS
countdown = SOFTWARE_TICKS;
#endif
return 1;
}
#endif
static long int initial_random_seed, seed2;
char *files_to_read[MAX_INPUT_FILES],
*symbols_to_define[MAX_SYMBOLS_TO_DEFINE],
*fasl_paths[MAX_FASL_PATHS];
int output_directory;
character_reader *procedural_input;
character_writer *procedural_output;
CSLbool undefine_this_one[MAX_SYMBOLS_TO_DEFINE];
int number_of_input_files = 0,
number_of_symbols_to_define = 0,
number_of_fasl_paths = 0,
init_flags = 0;
#ifdef WINDOW_SYSTEM
FILE *alternative_stdout = NULL;
#endif
/*
* standard_directory holds the name of the default image file that CSL
* would load.
*/
char *standard_directory;
/*
* If non-NULL the string module_enquiry is a name presenetd on the
* command line in a "-T name" request, and this will cause the system
* to behave in a totally odd manner - it does not run Lisp at all but
* performs a directory enquiry within the image file.
*/
static char *module_enquiry = NULL;
/*
* The next lines mean that (if you can get in before cslstart is
* called) you can get memory allocation done in a custom way.
*/
static void *CSL_malloc(size_t n)
{
return malloc(n);
}
static void CSL_free(void *p)
{
free(p);
}
static void *CSL_realloc(void *p, size_t n)
{
return realloc(p, n);
}
malloc_function *malloc_hook = CSL_malloc;
realloc_function *realloc_hook = CSL_realloc;
free_function *free_hook = CSL_free;
void cslstart(int argc, char *argv[], character_writer *wout)
{
int i;
CSLbool restartp, always_noisy = NO;
double store_size = 0.0;
stack_segsize = 1;
module_enquiry = NULL;
#ifdef SOFTWARE_TICKS
countdown = 0x7fffffff;
#endif
/*
* Note that I will set up clock_stack AGAIN later on! The one further down
* happens after command line options have been decoded and is where I really
* want to consider Lisp to be starting. The setting here is because
* if I call ensure_screen() it can push and pop the clock stack, and
* especially if I have an error in my options I may print to the terminal
* and then flush it. Thus I need SOMETHING set up early to prevent any
* possible frivolous disasters in that area.
*/
base_time = read_clock();
consolidated_time[0] = gc_time = 0.0;
clock_stack = &consolidated_time[0];
#ifdef WINDOW_SYSTEM
/*
* On some systems (Archimedes/RISCOS) the same executable
* may run either under a window manager or using a command line.
* I select which to use based on a command line option, which
* I scan for VERY early since until I know what I am doing
* I can not report errors etc etc
*/
#ifdef RISCOS
use_wimp = NO;
for (i=1; i<argc; i++)
{ char *opt = argv[i];
if (opt == NULL) continue;
if (opt[0] == '-' && tolower(opt[1] == 'w'))
{ use_wimp = !use_wimp;
break; /* Repeating "-w" flips the effect */
}
}
#else
use_wimp = YES; /* Other than on RISCOS always enable window system */
#ifdef CWIN
cwin_pause_at_end = 1;
#endif
#ifdef macintosh
{ extern int _w_font;
extern void winit(void);
extern int wgetargs(char ***);
SetApplLimit(GetApplLimit() - 64000); /* Try to reserve plenty of stack space */
_w_font = 4; winit();
winitargs(&argc, &argv); /* THINK_C does not do arguments! */
argc = wgetargs(&argv); /* Use dialog box for arguments */
}
#endif
start_up_window_manager(use_wimp);
#endif
#ifdef SOCKETS
sockets_ready = 0;
socket_server = 0;
#endif
/*
* Now that the window manager is active I can send output through
* xx_printf() and get it on the screen neatly.
* On the Archimedes start_up_window_manager() hangs up until somebody
* hits the right icon with a mouse click, so most of the mallocs etc
* will not happen until then.
*/
#endif
procedural_input = NULL;
procedural_output = wout;
standard_directory = find_image_directory(argc, argv);
#ifdef OLD_THINK_C
/*
* Note amazingly well that on the Macintosh I identify the image directory
* BEFORE I grab command-line options etc, since the Think C "ccommand"
* function can reset the current directory, and I need access to same
* (but not to the faked command line!) while establishing my image
* location.
*/
argc = ccommand(&argv);
#endif
restartp = YES;
ignore_restart_fn = NO;
spool_file = NULL;
spool_file_name[0] = 0;
output_directory = 0x80000000;
number_of_input_files = 0;
number_of_symbols_to_define = 0;
number_of_fasl_paths = 0;
gc_method_is_copying = NO;
fasl_output_file = NO;
initial_random_seed = seed2 = 0;
init_flags = INIT_EXPANDABLE;
return_code = EXIT_SUCCESS;
segvtrap = YES;
batch_flag = NO;
{ char *s = REGISTRATION_VERSION;
#define hexval(c) ('0'<=c && c<='9' ? c - '0' : c - 'a' + 10)
#define gx() (s+=2, hexval(s[-1]) + 16*hexval(s[-2]))
unsigned char *p = registration_data;
memset(registration_data, 0, sizeof(REGISTRATION_SIZE));
while (*s != 0) *p++ = *s++;
s = REG1;
while (*s != 0) *p++ = gx();
s = REG2;
while (*s != 0) *p++ = gx();
MD5_Init();
MD5_Update((unsigned char *)errcode(err_registration), 32);
}
#ifdef MEMORY_TRACE
car_counter = 0x7fffffff;
car_low = 0;
car_high = 0xffffffff;
#endif
#ifdef __WATCOMC__
_control87(_EM_OVERFLOW | _EM_INVALID | _EM_DENORMAL |
_EM_ZERODIVIDE | _EM_INEXACT | _EM_UNDERFLOW,
_MCW_EM);
#endif
argc--;
for (i=1; i<=argc; i++)
{ char *opt = argv[i];
/*
* The next line ought never to be activated, but I have sometimes seen
* unwanted NULL args on the end of command lines so I filter them out
* here as a matter of security.
*/
if (opt == NULL || *opt == 0) continue;
if (opt[0] == '-')
{ char *w;
int c1 = opt[1], c2 = opt[2];
if (isupper(c1)) c1 = tolower(c1);
switch (c1)
{
/*
* -- <outfile> arranges that output is sent to the indicated file. It is
* intended to behave a little like "> outfile" as command-line output
* redirection, but is for usin in windowed environments (in particular
* Windows NT) where this would not work. I had intended to use "->" here,
* but then the ">" tends to get spotted as a command-line request for
* redirection, and I would not be using this if command-line redirection
* worked properly!
*/
case '-':
if (c2 != 0) w = &opt[2];
else if (i != argc) w = argv[++i];
else break; /* Illegal at end of command-line */
{ char filename[LONGEST_LEGAL_FILENAME];
FILE *f;
#ifdef WINDOW_SYSTEM
f = open_file(filename, w, strlen(w), "w", NULL);
if (f == NULL)
{
#ifdef CWIN
/*
* Under CWIN if there is a "--" among the arguments I will start off
* with the main window minimized. Thus if an error is detected at a
* stage that the transcript file is not properly opened I need to
* maximize the window so I can see the error!
*/
cwin_maximize();
#endif
term_printf("Unable to write to \"%s\"\n", filename);
continue;
}
else
{ term_printf("Output redirected to \"%s\"\n",
filename);
}
if (alternative_stdout != NULL)
fclose(alternative_stdout);
alternative_stdout = f;
#else
/*
* I use freopen() on stdout here to get my output sent elsewhere. Quite
* what sort of mess I am in if the freopen fails is hard to understand!
* Thus I write a message to stderr and exit promptly in case of trouble.
* I print a message explaining what I am doing BEFORE actually performing
* the redirection.
*/
fprintf(stderr, "Output to be redirected to \"%s\"\n", w);
f = open_file(filename, w, strlen(w), "w", stdout);
if (f == NULL)
{ fprintf(stderr, "Unable to write to \"%s\"\n",
filename);
#ifdef CWIN
#ifdef __cplusplus
throw EXIT_FAILURE;
#else
longjmp(my_exit_buffer, EXIT_FAILURE);
#endif
#else
exit(EXIT_FAILURE);
#endif
}
#endif
}
continue;
/*
* -b is a curious option, not intended for general or casual use. If given
* it causes the (batchp) function to return the opposite result from
* normal! Without "-b" (batchp) returns T either if at least one file
* was specified on the command line, or if the standard input is "not
* a tty" (under some operating systems this makes sense - for instance
* the standard input might not be a "tty" if it is provided via file
* redirection). Otherwise (ie primary input is directly from a keyboard)
* (batchp) returns nil. Sometimes this judgement about how "batch" the
* current run is will be wrong or unhelpful, so "-b" allows the user to
* coax the system into better behaviour.
*/
case 'b':
batch_flag = YES;
continue;
/*
* The option "-C" just prints a dull and unimaginative copyright notice -
* having this option in there will tend to ensure that a copyright
* message is embedded in the object code somehow, while with luck nobody
* will be bothered too much by the fact that there is a stray option to get
* it displayed. Note that on some systems there is a proper character
* for the Copyright symbol... but there is little agreement about what
* that code is!
*/
case 'c':
#ifdef CWIN
cwin_maximize();
#endif
#ifdef macintosh
term_printf("\nCopyright \231 Codemist Ltd, 1988-2003\n");
#else
term_printf("\nCopyright (C) Codemist Ltd, 1988-2003\n");
#endif
continue;
/*
* -D name=val defines a symbol at the start of a run
* I permit either
* -Dname=val
* or -D name=val
*/
case 'd':
if (c2 != 0) w = &opt[2];
else if (i != argc) w = argv[++i];
else break; /* Illegal at end of command-line */
if (number_of_symbols_to_define < MAX_SYMBOLS_TO_DEFINE)
symbols_to_define[number_of_symbols_to_define] = w,
undefine_this_one[number_of_symbols_to_define++] = NO;
else
{
#ifdef CWIN
cwin_maximize();
#endif
term_printf("Too many \"-D\" requests: ignored\n");
}
continue;
case 'e':
/*
* -E
* This option is for an EXPERIMENT. It may do different things in different
* releases of CSL.
*/
continue;
#ifdef SOCKETS
case 'f':
/*
* -F
* This is used with syntax -Fnnn or -F nnn (with nnn a number above
* 1000 but less than 65536) to cause the system to run not as a normal
* interactive application but as a server listening on the indicated port.
* The case -F- (which could of course be "-F -") indicates use of the
* default port for CSL, which I hereby declare to be 1206. This number may
* need to be changed later if I find it conflicts with some other common
* package or usage, but was selected in memory of the project number
* at one time allocated to the Archimedeans Computing Group.
* On some systems if I want to set up a server that can serve multiple
* clients I may need to re-invoke CSL afresh for each new client, and in
* such cases the internally generated tasks can be passed information
* from their parent task using -F followed by non-numeric information.
* Any user who attempts such usage will get "what they deserve".
*/
if (c2 != 0) w = &opt[2];
else if (i != argc) w = argv[++i];
else break; /* Illegal at end of command-line */
port_number = default_csl_server_port;
remote_store = REMOTE_STORE;
max_users = MAX_USERS;
if (strcmp(w, "-") == 0)
port_number = default_csl_server_port;
else if (sscanf(w, "%d:%d:%d",
&port_number, &max_users, &remote_store) < 1 ||
port_number <= 1000 ||
port_number >= 65536 ||
max_users < 2 || max_users > 50 ||
remote_store < 4000 || remote_store > 20000)
{
#ifdef CWIN
cwin_maximize();
#endif
term_printf("\"%s\" is valid (want port:users:store\n", w);
continue;
}
store_size = (double)remote_store;
init_flags &= ~INIT_EXPANDABLE;
current_users = 0;
/*
* The code here is probably a bit painfully system-specific, and so one
* could argue that it should go in a separate file. However a LOT of the
* socket interface is the same regardless of the host, or a few simple
* macros can have made it so. So if SOCKETS has been defined I will
* suppose I can continue here on that basis. I do quite want to put the
* basic socket code in csl.c since it is concerned with system startup and
* the selection of sources and sinks for IO.
*/
if (ensure_sockets_ready() == 0)
{ SOCKET sock1, sock2;
struct sockaddr_in server_address, client_address;
int sin_size;
sock1 = socket(AF_INET, SOCK_STREAM, 0);
if (sock1 == SOCKET_ERROR)
{
#ifdef CWIN
cwin_maximize();
#endif
term_printf("Unable to create a socket\n");
continue;
}
server_address.sin_family = AF_INET;
server_address.sin_port = htons(port_number);
server_address.sin_addr.s_addr = INADDR_ANY;
memset((char *)&(server_address.sin_zero), 0, 8);
if (bind(sock1, (struct sockaddr *)&server_address,
sizeof(struct sockaddr)) == SOCKET_ERROR)
{
#ifdef CWIN
cwin_maximize();
#endif
term_printf("Unable to bind socket to port %d\n",
port_number);
closesocket(sock1);
continue;
}
if (listen(sock1, PERMITTED_BACKLOG) == SOCKET_ERROR)
{
#ifdef CWIN
cwin_maximize();
#endif
term_printf("Failure in listen() on port %d\n",
port_number);
closesocket(sock1);
continue;
}
for (;;)
{ struct hostent *h;
time_t t0;
sin_size = sizeof(struct sockaddr_in);
sock2 = accept(sock1,
(struct sockaddr *)&client_address,
&sin_size);
if (sock2 == SOCKET_ERROR)
{
#ifdef CWIN
cwin_maximize();
#endif
term_printf("Trouble with accept()\n");
continue; /* NB local continue here */
}
t0 = time(NULL);
term_printf("%.24s from %s",
ctime(&t0),
inet_ntoa(client_address.sin_addr));
h = gethostbyaddr((char *)&client_address.sin_addr,
sizeof(client_address.sin_addr), AF_INET);
if (h != NULL)
term_printf(" = %s", h->h_name);
else term_printf(" [unknown host]");
/*
* Here I have a bit of a mess. Under Unix I can do a fork() so that the
* requests that are coming in are handled by a separate process. The
* code is pretty easy. However with Windows I can only create a fresh process
* by re-launching CSL from the file it was originally fetched from. I
* will try to do that in a while, but for now I will leave the
* Windows version of this code only able to handle a single client
* session.
*/
#ifdef ms_windows
closesocket(sock1);
socket_server = sock2;
cpu_timeout = clock() + CLOCKS_PER_SEC*MAX_CPU_TIME;
elapsed_timeout = time(NULL) + 60*MAX_ELAPSED_TIME;
procedural_output = char_to_socket;
term_printf("Welcome to the Codemist server\n");
ensure_screen();
break;
#else /* ms_windows */
while (waitpid(-1, NULL, WNOHANG) > 0) current_users--;
if (current_users >= max_users)
{ term_printf(" refused\n");
socket_server = sock2;
ensure_screen();
procedural_output = char_to_socket;
term_printf(
"\nSorry, there are already %d people using this service\n",
current_users);
term_printf("Please try again later.\n");
ensure_screen();
procedural_output = NULL;
closesocket(socket_server);
socket_server = 0;
continue;
}
else term_printf(" %d of %d\n",
++current_users, max_users);
ensure_screen();
if (!fork())
{ /* Child process here */
closesocket(sock1);
fcntl(sock2, F_SETFL, O_NONBLOCK);
socket_server = sock2;
cpu_timeout = clock() + CLOCKS_PER_SEC*MAX_CPU_TIME;
elapsed_timeout = time(NULL) + 60*MAX_ELAPSED_TIME;
ensure_screen();
procedural_output = char_to_socket;
term_printf("Welcome, you are user %d of %d\n",
current_users, max_users);
term_printf(
"You have been allocated %d seconds CPU time"
" and %d minutes elapsed time\n",
MAX_CPU_TIME, MAX_ELAPSED_TIME);
break;
}
else
{ closesocket(sock2);
if (current_users < 0) current_users = 0;
continue;
/*
* This loops serving as many clients as happen to come along. Having said
* "csl -fnnn" it will be necessary (in due course) to kill the daemon
* by interrupting it with a ^C or some such. When the master process is
* terminated in that way any clients that remain active may continue to
* hang around until they have finished in the usual way.
*/
}
#endif /* ms_windows */
}
}
/*
* The "continue" here gets executed when I have been contacted by some
* client and have an active socket open. It parses the rest of the
* command line and then completes the process of getting CSL running.
*/
continue;
#endif
/*
* -G
* is a debugging option - it sets !*backtrace to true, which applications
* may inspect when they want to do errorsets etc. These days I will
* make it FORCE all errors to be noisy whatever the user tries to do!
*/
case 'g':
if (number_of_symbols_to_define < MAX_SYMBOLS_TO_DEFINE)
symbols_to_define[number_of_symbols_to_define] =
"*backtrace",
undefine_this_one[number_of_symbols_to_define++] = NO;
else
{
#ifdef CWIN
cwin_maximize();
#endif
term_printf("Too many requests: \"-G\" ignored\n");
}
always_noisy = YES;
continue;
/*
* -I is used to specify an image file to be used when CSL starts up.
* The case -I- indicated the "standard" file associated with this
* executable binary. Several images can be given.
*/
case 'i':
if (c2 != 0) w = &opt[2];
else if (i != argc) w = argv[++i];
else break; /* Illegal at end of command-line */
if (w[0] == '-' && w[1] == 0) w = standard_directory;
if (number_of_fasl_paths < MAX_FASL_PATHS-1)
fasl_paths[number_of_fasl_paths++] = w;
else
{
#ifdef CWIN
cwin_maximize();
#endif
term_printf("Too many \"-I/-O\" requests: ignored\n");
}
continue;
/*
* -K nnn sets the size of heap to be used. If it is given then that much
* memory will be allocated and the heap will never expand. Without this
* option a default amount is used, and (on many machines) it will grow
* if space seems tight.
* The extended version of this option is "-K nnn/ss" and then ss is the
* number of "CSL pages" to be allocated to the Lisp stack. The default
* value (which is 1) should suffice for almost all users, and it should
* be noted that the C stack is separate from and independent of this one and
* it too could overflow.
*/
case 'k':
if (c2 != 0) w = &opt[2];
else if (i != argc) w = argv[++i];
else break; /* Illegal at end of command-line */
{ char buffer[16];
int i = 0;
while ((*w != '/') && (*w != 0) && (i<sizeof(buffer)-1))
buffer[i++] = *w++;
buffer[i] = 0;
/*
* store size gets set here - 0.0 is left if either that is specified
* explictly or if no -K option is given.
*/
store_size = atof(buffer);
if (store_size == 0.0) init_flags |= INIT_EXPANDABLE;
else
{ init_flags &= ~INIT_EXPANDABLE;
/* here I impose a minimum store-size... 32 Mbytes! */
if (store_size < 32000) store_size = 32000;
}
if (*w == '/')
{ stack_segsize = atoi(w+1);
if (stack_segsize < 1 || stack_segsize > 10)
stack_segsize = 1;
}
}
continue;
/*
* -L <logfile> arranges that a transcript of the standard output is
* sent to the given file, just as if (spool '<logfile>) had been executed
* at the start of the run.
*/
case 'l':
if (c2 != 0) w = &opt[2];
else if (i != argc) w = argv[++i];
else break; /* Illegal at end of command-line */
{ char filename[LONGEST_LEGAL_FILENAME];
spool_file = open_file(filename, w,
strlen(w), "w", NULL);
if (spool_file == NULL)
{
#ifdef CWIN
cwin_maximize();
#endif
term_printf("Unable to write to \"%s\"\n", filename);
}
else
{ time_t t0 = time(NULL);
strncpy(spool_file_name, filename, 32);
spool_file_name[31] = 0;
#ifdef COMMON
fprintf(spool_file,
"Starts dribbling to %s (%.24s).\n",
spool_file_name, ctime(&t0));
#else
fprintf(spool_file,
"+++ Transcript to %s started at %.24s +++\n",
spool_file_name, ctime(&t0));
#endif
}
}
continue;
#ifdef MEMORY_TRACE
/*
* If MEMORY_TRACE is set up then I can cause an exception by providing
* an option -M n:l:h
* This interrupts after n memory records when a reference in the (inclusive)
* range l..h is next made.
*/
case 'm':
if (c2 != 0) w = &opt[2];
else if (i != argc) w = argv[++i];
else break; /* Illegal at end of command-line */
switch(sscanf(w, "%ld:%lu:%lu",
&car_counter, &car_low, &car_high))
{
case 0: car_counter = 0x7fffffff;
case 1: car_low = 0;
case 2: car_high = 0xffffffff;
default:break;
}
continue;
#endif
/*
* -N tells CSL that even if the image being loaded contains a restart-
* function this should be ignored, and Lisp should run the default
* read-eval-print loop. The only expected use for this is when an image
* has been created but it is seriously broken, so the way it would
* usually restart would crash - then "-N" may allow a suitable expert to
* test and diagnose the trouble at the Lisp level. Ordinary users are
* NOT expected to want to know about this!
*/
case 'n': /* Ignore restart function (-N) */
ignore_restart_fn = YES;
continue;
/*
* -O <file> specifies an image file for output (via FASLOUT or PRESERVE).
*/
case 'o':
if (c2 != 0) w = &opt[2];
else if (i != argc) w = argv[++i];
else break; /* Illegal at end of command-line */
if (w[0] == '-' && w[1] == 0) w = standard_directory;
if (number_of_fasl_paths < MAX_FASL_PATHS-1)
{ output_directory = number_of_fasl_paths;
fasl_paths[number_of_fasl_paths++] = w;
}
else
{
#ifdef CWIN
cwin_maximize();
#endif
term_printf("Too many \"-I/-O\" requests: ignored\n");
}
continue;
/*
* -P is reserved for profile options.
*/
case 'p':
/*
* Please implement something for your favourite system here... what I would
* like would be a call to monitor() or some such...
*/
#ifdef CWIN
cwin_maximize();
#endif
term_printf("Unimplemented option \"-%c\"\n", c1);
continue;
/*
* -Q selects "quiet" mode. See -V for the converse.
*/
case 'q':
if (number_of_symbols_to_define < MAX_SYMBOLS_TO_DEFINE)
/*
* symbols_to_define[number_of_symbols_to_define] =
* "*echo=nil",
* undefine_this_one[number_of_symbols_to_define++] = NO,
*/
init_flags &= ~INIT_VERBOSE,
init_flags |= INIT_QUIET;
else
{
#ifdef CWIN
cwin_maximize();
#endif
term_printf("Too many requests: \"-Q\" ignored\n");
}
continue;
/*
* -R nnn sets the initial random seed, for reproducible runs. -R 0
* (the default) sets the initial seed based on the time of day etc.
* The version -R nnn,mmm makes it possible to pass 64-bits of seed info.
*/
case 'r':
if (c2 != 0) w = &opt[2];
else if (i != argc) w = argv[++i];
else break; /* Illegal at end of command-line */
if (sscanf(w, "%ld,%ld", &initial_random_seed, &seed2) != 2)
{ initial_random_seed = seed2 = 0;
sscanf(w, "%ld", &initial_random_seed);
}
continue;
/*
* -S sets the variable !*plap, which causes the compiler to list the
* bytecodes that it generates.
*/
case 's':
if (number_of_symbols_to_define < MAX_SYMBOLS_TO_DEFINE)
symbols_to_define[number_of_symbols_to_define] =
"*plap",
undefine_this_one[number_of_symbols_to_define++] = NO;
else
{
#ifdef CWIN
cwin_maximize();
#endif
term_printf("Too many requests: \"-S\" ignored\n");
}
continue;
/*
* -T name reports the time-stamp on the named module, and then
* exits. This is for use in perl scripts and the like, and is
* needed because the stamps on modules within an image or
* library file are not otherwise instantly available.
*
* Note that especially on windowed systems it may be
* necessary to use this with "-- filename" since the information
* generated here goes to the default output unit, which in
* some cases is just the screen.
*/
case 't':
if (c2 != 0) w = &opt[2];
else if (i != argc) w = argv[++i];
else break; /* Illegal at end of command-line */
module_enquiry = w;
continue;
/*
* -U name undefines the symbol <name> at the start of the run
*/
case 'u':
if (c2 != 0) w = &opt[2];
else if (i != argc) w = argv[++i];
else break; /* Illegal at end of command-line */
if (number_of_symbols_to_define < MAX_SYMBOLS_TO_DEFINE)
symbols_to_define[number_of_symbols_to_define] = w,
undefine_this_one[number_of_symbols_to_define++] = YES;
else
{
#ifdef CWIN
cwin_maximize();
#endif
term_printf("Too many \"-U\" requests: ignored\n");
}
continue;
/*
* -V selects "verbose" options at the start of the run
*/
case 'v':
if (number_of_symbols_to_define < MAX_SYMBOLS_TO_DEFINE)
/*
* symbols_to_define[number_of_symbols_to_define] =
* "*echo",
* undefine_this_one[number_of_symbols_to_define++] = NO,
*/
init_flags &= ~INIT_QUIET,
init_flags |= INIT_VERBOSE;
else
{
#ifdef CWIN
cwin_maximize();
#endif
term_printf("Too many requests: \"-V\" ignored\n");
}
continue;
#ifdef WINDOW_SYSTEM
/*
* For the Archimedes I insist on a command-line option "-w" to enable
* use of the windowed version of the code. For other window systems I
* reserve the option for fine control over window behaviour.
*/
case 'w':
/*
* I need to detect and process this flag especially early, and so by the time
* I get to regular command decoding there is nothing to be done.
*/
continue;
#endif
/*
* -x is an "undocumented" option intended for use only by system
* support experts - it disables trapping if segment violations by
* errorset and so makes it easier to track down low level disasters -
* maybe! Only those who have access to the source code can make
* good use of the -X option, so it is only described here!
*/
case 'x':
segvtrap = NO;
continue;
/*
* -Y sets the variable !*hankaku , which causes the lisp reader convert
* a Zenkaku code to Hankaku one when read. I leave this option decoded
* on the command line even if the Kanji support code is not otherwise
* compiled into CSL just so I can reduce conditional compilation.
*/
case 'y':
if (number_of_symbols_to_define < MAX_SYMBOLS_TO_DEFINE)
symbols_to_define[number_of_symbols_to_define] =
"*hankaku",
undefine_this_one[number_of_symbols_to_define++] = NO;
else
term_printf("Too many requests: \"-Y\" ignored\n");
continue;
/*
* -Z tells CSL that it should not load an initial heap image, but should
* run in "cold start" mode. This is only intended to be useful for
* system builders.
*/
case 'z': /* Cold start option -z */
restartp = NO;
continue;
default:
#ifdef CWIN
cwin_maximize();
#endif
term_printf("Unrecognized option \"-%c\"\n", c1);
continue;
}
/*
* I do a "break" out of the switch() block if a key occurs at the end
* of the command line in an invalid manner.
*/
#ifdef CWIN
cwin_maximize();
#endif
term_printf("Option \"-%c\" needs an argument: ignored\n", c1);
break;
}
else files_to_read[number_of_input_files++] = opt;
}
if (number_of_fasl_paths == 0)
{ char *p = standard_directory, *p1;
char cur[LONGEST_LEGAL_FILENAME];
/*
* If the user does not specify any image files then the behaviour
* defaults as follows:
* Suppose that the current executable is xxx/yyy/zzz then the
* system behaves as if the user had written
* zzz -o zzz.img -i xxx/yyy/zzz.img
* however if the executable seemed to be in the current directory
* already this is reduced to just
* zzz -o zzz.img
* so that I do not have two different handles on the same image file
* (with the potential muddle that that could result in).
*
* NOTE: this used very generally mean that you ended up with an empty image
* file (eg csl.img or r37.img) in whatever directory you run this
* code from. This could be avoided by running it as
* xxx -i-
* that explicitly sets up the normal image file as the one to use with
* no extras. However these days I try to arrange that an output image file
* only ever gets created if somebody calls FASLOUT or PRESERVE, so what
* I describe here will usually not cause confusion....
*/
if (standard_directory[0] == '.' &&
(standard_directory[1] == '/' ||
standard_directory[1] == '\\')) strcpy(cur, standard_directory);
else get_current_directory(cur, sizeof(cur));
p += strlen(p);
while (p != standard_directory &&
*--p != '/' &&
*p != '\\') /* nothing */;
if (strncmp(standard_directory, cur, p-standard_directory) != 0)
p1 = (char *)(*malloc_hook)(strlen(p));
else p1 = NULL;
if (p == standard_directory || p1 == NULL)
{ fasl_paths[0] = standard_directory;
/*
* If output_directory has the 0x40000000 bit set then the directory
* involved is one that should be opened now if it exists, but if
* it does not its creation should be deferred for as long as possible.
*/
output_directory = 0x40000000 + 0;
number_of_fasl_paths = 1;
if (p1 != NULL) (*free_hook)(p1);
}
else
{ strcpy(p1, p+1);
fasl_paths[0] = p1;
fasl_paths[1] = standard_directory;
output_directory = 0x40000000 + 0;
number_of_fasl_paths = 2;
}
}
Iinit();
#ifdef WINDOW_SYSTEM
#ifdef RISCOS
/*
* Under RISCOS I must delay starting up the window manager until about
* here for two reasons. Firstly a command-line option might have been
* used to decide whether or not it was wanted after all. Secondly a
* side effect of find_image_directory() under RISCOS is the discovery
* of the name that this application is being run as, and I want that to
* go as the title in my window.
*/
start_up_window_manager(use_wimp);
#endif
#endif
if (module_enquiry != NULL)
{ char datestamp[32], fullname[LONGEST_LEGAL_FILENAME];
int32 size;
int i;
Lisp_Object nil;
/*
* Imodulep expects input_libraries to be set up. So I will fudge the
* creation of something that looks sufficiently like a list to pass muster
* here despite the full system not being loaded. I use references to the
* nil-segment and cons().
*/
nilsegment = (Lisp_Object *)my_malloc(NIL_SEGMENT_SIZE);
#ifdef COMMON
nil = doubleword_align_up(nilsegment) + TAG_CONS + 8;
#else
nil = doubleword_align_up(nilsegment) + TAG_SYMBOL;
#endif
C_nil = nil;
pages_count = heap_pages_count = vheap_pages_count =
bps_pages_count = native_pages_count = 0;
stacksegment = (Lisp_Object *)my_malloc(CSL_PAGE_SIZE);
/*
* I am lazy about protection against malloc failure here.
*/
heaplimit = doubleword_align_up(stacksegment);
fringe = heaplimit + CSL_PAGE_SIZE - 16;
input_libraries = heaplimit + 16 + TAG_SYMBOL;
heaplimit += 64;
/*
* I have now fudged up enough simulation of a Lisp heap that maybe I can
* build the library search-list.
*/
qheader(input_libraries) |= SYM_SPECIAL_FORM;
qvalue(input_libraries) = nil;
for (i=number_of_fasl_paths-1; i>=0; i--)
qvalue(input_libraries) = cons(SPID_LIBRARY + (((int32)i)<<20),
qvalue(input_libraries));
if (Imodulep(module_enquiry, strlen(module_enquiry),
datestamp, &size, fullname))
{ strcpy(datestamp, "unknown");
size = 0;
strcpy(fullname, module_enquiry);
}
term_printf("%.24s size=%ld file=%s\n",
datestamp, (long)size, fullname);
init_flags &= ~INIT_VERBOSE;
#ifdef CWIN
cwin_pause_at_end = 0;
#endif
}
else
{ base_time = read_clock();
consolidated_time[0] = gc_time = 0.0;
clock_stack = &consolidated_time[0];
push_clock();
if (init_flags & INIT_VERBOSE)
{
#ifndef WINDOW_SYSTEM
/*
* If I do NOT have a window system I will print a newline here so that I
* can be very certain that my banner appears at the start of a line.
* With a window system I should have a brand-new frash window for output
* and the newline would intrude as an initial blank line.
*/
term_printf("\n");
#endif
#ifndef COMMON
term_printf("Codemist Standard Lisp %s for %s: %s\n",
VERSION, IMPNAME, __DATE__);
#else
term_printf("Codemist Common Lisp %s for %s: %s\n",
VERSION, IMPNAME, __DATE__);
#endif
}
#ifdef MEMORY_TRACE
if (car_counter != 0x7fffffff)
term_printf("Stop after %ld %lu..%lu\n",
car_counter, car_low, car_high);
#endif
#ifdef WINDOW_SYSTEM
ensure_screen();
/* If the user hits the close button here I may be in trouble */
#endif
/*
* Here (potentially) I could set qualifier information into native_code_tag
* based on run-time checks. This might allow me to discriminate between
* systems that are exactly the same as far as the CSL-controlled bits of C
* code are concerned but have extra differences that a bit of system-specific
* code here can detect. I will reserve the bits 0xe0 (3 bits) for such
* information. Note that a system that does not support hard code MUST leave
* hard_code_tag set to zero.
*/
native_code_tag = NATIVE_CODE_TAG;
/*
* Now dynamic code detects the floating point representation that is in use.
* I thougt/hoped that doing it this way would be safer than relying on having
* pre-defined symbols that tracked the machine architecture.
*/
{ union fpch { double d; unsigned char c[8]; } d;
/*
* The following looks at the floating point representation of the
* number 1/7 (in double precision) and picks out two bytes from
* the middle of the first word - where I hope that rounding issues
* will be remote. Investigation shows that these two bytes can be
* used to discriminate among at least a worthwhile range of
* representations, and I will exploit this to help me re-load
* heap-images in a way that allows images to be portable across
* different architectures.
*/
d.d = 1.0/7.0;
switch ((d.c[1] << 8) | d.c[2])
{
case 0x49c2: current_fp_rep = FP_ARM; break;
case 0x2492: current_fp_rep = FP_370; break;
case 0x2449: current_fp_rep = FP_MIPS; break;
case 0x3f24: current_fp_rep = FP_VAX; break;
case 0xc249: current_fp_rep = FP_88K; break;
/*
* The next line is probably not very good under a window manager, but
* it is a case that ought never to arise, so I will not bother.
*/
default: term_printf("Unknown floating point format\n");
my_exit(EXIT_FAILURE);
}
}
/*
* Up until the time I call setup() I may only use term_printf for
* output, because the other relevant streams will not have been set up.
*/
setup(restartp ? 3 : 2, store_size);
/*
* I need to set the NOISY flag after doing setup to avoid it getting
* reloaded from a heap image
*/
{ nil_as_base
if (always_noisy) miscflags |= (ALWAYS_NOISY | 3);
else miscflags &= ~ALWAYS_NOISY;
}
#ifndef COMMON
#ifdef CWIN
/*
* Note that it may make sense to enable this for windowed versions other
* than CWIN-based ones...
*/
cwin_menus(loadable_packages, switches);
#endif
#endif
/*
* Now that setup is complete (and I have done any authorisation I want to)
* I will seed the random number generator as requested by the user. The
* default will be to put it in an unpredictable (well hard to predict!)
* state
*/
Csrand((unsigned32)initial_random_seed, (unsigned32)seed2);
gc_time += pop_clock();
interrupt_pending = already_in_gc = NO;
polltick_pending = tick_pending = tick_on_gc_exit = NO;
sigint_must_longjmp = NO;
signal(SIGINT, sigint_handler);
#ifdef TICK_STREAM
/*
* Now I have enough in place that I am prepared to accept regular clock-
* tick events.
*/
add_ticker();
/*
* atexit(remove_ticker); Maybe "atexit() is itself dangerous!
* I should try (quite hard) to disable the
* ticker at some earlier safer stage.
*/
#endif
ensure_screen();
procedural_output = NULL;
#ifdef CWIN
/*
* OK, if I get this far I will suppose that any messages that report utter
* disasters will have reached the user, so I can allow CWIN to terminate
* rather more promptly.
*/
cwin_pause_at_end = 0;
#endif
}
}
#ifdef SOCKETS
#define SOCKET_BUFFER_SIZE 1024
#define CH_PROMPT 0x9a
#define CH_ENDPROMPT 0x9c
static char socket_in[SOCKET_BUFFER_SIZE], socket_out[SOCKET_BUFFER_SIZE];
static int socket_in_p = 0, socket_in_n = 0,
socket_out_p = 0, socket_prev = '\n';
static int char_from_socket(void)
{
int c;
clock_t c0;
time_t t0;
if (socket_server == 0)
{ socket_prev = ' ';
return EOF;
}
/*
* I generate a prompt whenever I am about to read the character that
* follows a newline. The prompt is issued surrounded by control
* characters 0x9a and 0x9c. That curious arrangement is inherited from
* internal behaviour in my Windows interface code and could be altered
* if something truly better could be invented.
*/
if (socket_prev == '\n')
{ term_printf("%c%s%c", CH_PROMPT, prompt_string, CH_ENDPROMPT);
ensure_screen();
}
if (socket_in_n == 0)
{ for (;;)
{ socket_in_n = recv(socket_server, socket_in, SOCKET_BUFFER_SIZE, 0);
c0 = clock();
t0 = time(NULL);
if (c0 > cpu_timeout || t0 > elapsed_timeout)
{ cpu_timeout = c0 + 20;
elapsed_timeout = t0 + 20;
term_printf(
"\nSorry: timeout on this session. Closing down.\n");
socket_prev = ' ';
return EOF;
}
if (socket_in_n <= 0)
#ifndef EWOULDBLOCK
# define EWOULDBLOCK WSAEWOULDBLOCK
#endif
{ if (errno == EWOULDBLOCK)
{
#ifdef _MSC_VER
Sleep(300); /* Arg in milliseconds here */
#else
sleep(1); /* Delay 1 second before re-polling */
#endif
continue;
}
closesocket(socket_server);
socket_server = 0;
socket_prev = ' ';
return EOF;
}
else break;
}
socket_in_p = 0;
}
c = socket_in[socket_in_p++];
if (c == 0x0a || c == 0x0d) c = '\n';
socket_in_n--;
socket_prev = c;
return c & 0xff;
}
static int char_to_socket(int c)
{
if (socket_server == 0) return 1;
socket_out[socket_out_p++] = (char)c;
if (c == '\n' || socket_out_p == SOCKET_BUFFER_SIZE)
{ if (send(socket_server, socket_out, socket_out_p, 0) < 0)
{ closesocket(socket_server);
socket_server = 0;
return 1;
}
socket_out_p = 0;
}
return 0;
}
void flush_socket(void)
{
if (socket_server == 0) return;
if (send(socket_server, socket_out, socket_out_p, 0) < 0)
{ closesocket(socket_server);
socket_server = 0;
}
socket_out_p = 0;
}
#endif
static void cslaction(void)
/*
* This is the "standard" route into CSL activity - it uses file-names
* from the decoded command-line as files to be read and processed
* unless the system was launched with the flag that says it ought to try
* to provide a network service on some socket.
*/
{
#ifdef __cplusplus
errorset_msg = NULL;
try
#else
jmp_buf this_level;
errorset_buffer = &this_level;
errorset_msg = NULL;
if (!setjmp(this_level))
#endif
{ signal(SIGFPE, low_level_signal_handler);
#ifdef __WATCOMC__
_control87(_EM_OVERFLOW | _EM_INVALID | _EM_DENORMAL |
_EM_ZERODIVIDE | _EM_INEXACT | _EM_UNDERFLOW,
_MCW_EM);
#endif
if (segvtrap) signal(SIGSEGV, low_level_signal_handler);
#ifdef SIGBUS
if (segvtrap) signal(SIGBUS, low_level_signal_handler);
#endif
#ifdef SIGILL
if (segvtrap) signal(SIGILL, low_level_signal_handler);
#endif
non_terminal_input = NULL;
#ifdef SOCKETS
if (socket_server)
{ ensure_screen();
procedural_input = char_from_socket;
procedural_output = char_to_socket;
lisp_main();
ensure_screen();
procedural_input = NULL;
procedural_output = NULL;
}
else
#endif
if (number_of_input_files == 0) lisp_main();
else
{ int i;
for (i=0; i<number_of_input_files; i++)
{ char filename[LONGEST_LEGAL_FILENAME];
FILE *f = open_file(filename, files_to_read[i],
strlen(files_to_read[i]), "r", NULL);
if (f == NULL)
err_printf("\n+++ Could not read file \"%s\"\n",
files_to_read[i]);
else
{ nil_as_base
if (init_flags & INIT_VERBOSE)
term_printf("\n+++ About to read file \"%s\"\n",
files_to_read[i]);
non_terminal_input = f;
lisp_main();
fclose(f);
}
}
}
}
#ifdef __cplusplus
catch (char *)
#else
else
#endif
{ if (errorset_msg != NULL)
{ term_printf("\n%s detected\n", errorset_msg);
errorset_msg = NULL;
}
return;
}
}
int cslfinish(character_writer *w)
{
procedural_output = w;
if (Ifinished())
term_printf("\n+++ Errors on checkpoint-image file\n");
#ifdef TRACED_EQUAL
dump_equals();
#endif
/*
* clock_t is an arithmetic type but I do not know what sort - so I
* widen to double to do arithmetic on it. Actually what I MUST do is
* to compute a time difference in the type clock_t and hope I never
* get a difference that that overflows. The worst case I know of overflows
* after 35 minutes.
*/
if (init_flags & INIT_VERBOSE)
{ long int t = (long int)(100.0 * (consolidated_time[0] +
(double)(read_clock() - base_time)/
(double)CLOCKS_PER_SEC));
long int gct = (long int)(100.0 * gc_time);
term_printf("\n\nEnd of Lisp run after %ld.%.2ld+%ld.%.2ld seconds\n",
t/100, t%100, gct/100, gct%100);
}
#ifdef TICK_STREAM
#ifdef SOFTWARE_TICKS
#ifdef DEBUG_SOFTWARE_TICKS
term_printf("%d ticks processed (%d)\n",
number_of_ticks, SOFTWARE_TICKS);
#endif
#endif
remove_ticker();
#endif
drop_heap_segments();
if (spool_file != NULL)
{
#ifdef COMMON
fprintf(spool_file, "\nFinished dribbling to %s.\n", spool_file_name);
#else
fprintf(spool_file, "\n+++ Transcript closed at end of run +++\n");
#endif
#ifndef _DEBUG
fclose(spool_file);
spool_file = NULL;
#endif
}
ensure_screen();
procedural_output = NULL;
return return_code;
}
/*
* People who want to use this in an embedded context can predefine
* NO_STARTUP_CODE and provide their own entrypoint...
*/
#ifndef NO_STARTUP_CODE
/*
* The next fragment of code is to help with the use of CSL (and hence
* packages written in Lisp and supported under CSL) as OEM products
* embedded within larger C-coded packages. There is (of course) a
* significant issue about clashes between the names of external symbols
* if CSL is to be linked with anything else, but I will not worry about that
* just yet.
* The protocol for calling Lisp code from C is as follows:
*
* cslstart(argc, argv, writer);allocate memory and Lisp heap etc. Args
* should be "as if" CSL was being called
* directly and this was the main entrypoint.
* The extra arg accepts output from this
* stage. Use NULL to get standard I/O.
* execute_lisp_function(fname, reader, writer);
* fname is a (C) string that names a Lisp
* function of 0 args. This is called with
* stdin/stdout access redirected to use the
* two character-at-a-time functions passed
* down. [Value returned indicates if
* the evaluation succeeded?]
* cslfinish(writer); Tidies up ready to stop.
*/
int execute_lisp_function(char *fname,
character_reader *r, character_writer *w)
{
Lisp_Object nil;
Lisp_Object ff = make_undefined_symbol(fname);
nil = C_nil;
if (exception_pending())
{ flip_exception();
return 1; /* Failed to make the symbol */
}
procedural_input = r;
procedural_output = w;
Lapply0(nil, ff);
ensure_screen();
procedural_input = NULL;
procedural_output = NULL;
nil = C_nil;
if (exception_pending())
{ flip_exception();
return 2; /* Failure during evaluation */
}
return 0;
}
#ifdef SAMPLE_OF_PROCEDURAL_INTERFACE
static char ibuff[100], obuff[100];
static int ibufp = 0, obufp = 0;
static int iget()
{
int c = ibuff[ibufp++];
if (c == 0) return EOF;
else return c;
}
static void iput(int c)
{
if (obufp < sizeof(obuff)-1)
{ obuff[obufp++] = c;
obuff[obufp] = 0;
}
}
#endif
static int submain(int argc, char *argv[])
{
cslstart(argc, argv, NULL);
#ifdef FLEX
{
extern void init_lm();
init_lm();
}
#endif
#ifdef SAMPLE_OF_PROCEDURAL_INTERFACE
strcpy(ibuff, "(print '(a b c d))");
execute_lisp_function("oem-supervisor", iget, iput);
printf("Buffered output is <%s>\n", obuff);
#else
if (module_enquiry == NULL) cslaction();
#endif
my_exit(cslfinish(NULL));
/*
* The "return 0" here is unreachable but it still quietens down as many
* C compilers as it causes to moan noisily!
*/
return 0;
}
#if !defined(WINDOWS_NT) || defined(CWIN) || !defined(NAG)
#ifdef CWIN
#define ENTRYPOINT cwin_main
#else
#define ENTRYPOINT main
#endif
int ENTRYPOINT(int argc, char *argv[])
{
int res;
#if defined(FLEX) && !defined(WINDOWS_NT)
extern void init_lm();
init_lm();
#endif
#ifdef USE_MPI
MPI_Init(&argc,&argv);
MPI_Comm_rank(MPI_COMM_WORLD,&mpi_rank);
MPI_Comm_size(MPI_COMM_WORLD,&mpi_size);
printf("I am mpi instance %d of %d.\n", mpi_rank+1, mpi_size);
#endif
#ifdef CWIN
#ifdef NAG
strcpy(about_box_title, "About AXIOM for Windows");
strcpy(about_box_description, "CWIN interface");
strcpy(about_box_rights_1,"Copyright NAG Ltd.");
strcpy(about_box_rights_2,"1991-6");
#else
strcpy(about_box_title, "About CSL");
strcpy(about_box_description, "Codemist Standard Lisp");
#endif
#endif
#ifdef __cplusplus
try { res = submain(argc, argv); }
catch(int r) { res = r; }
#else
res = setjmp(my_exit_buffer);
if (res == 0) res = submain(argc, argv);
if (res == 0x80000000) res = 0;
#endif
#ifdef TICK_STREAM
remove_ticker();
#endif
return res;
#ifdef USE_MPI
MPI_Finalize();
#endif
}
#endif /* NAG */
#endif /* NO_STARTUP_CODE */
/* End of csl.c */