1274 lines
36 KiB
C
1274 lines
36 KiB
C
|
/* scheme.c
|
||
|
* Copyright 1984-2017 Cisco Systems, Inc.
|
||
|
*
|
||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
* you may not use this file except in compliance with the License.
|
||
|
* You may obtain a copy of the License at
|
||
|
*
|
||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
*
|
||
|
* Unless required by applicable law or agreed to in writing, software
|
||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
* See the License for the specific language governing permissions and
|
||
|
* limitations under the License.
|
||
|
*/
|
||
|
|
||
|
#include "system.h"
|
||
|
#include "config.h"
|
||
|
#include <setjmp.h>
|
||
|
#include <limits.h>
|
||
|
#ifdef WIN32
|
||
|
#include <io.h>
|
||
|
#else
|
||
|
#include <sys/time.h>
|
||
|
#endif
|
||
|
#include <fcntl.h>
|
||
|
#include <stddef.h>
|
||
|
|
||
|
#ifndef O_BINARY
|
||
|
#define O_BINARY 0
|
||
|
#endif /* O_BINARY */
|
||
|
|
||
|
static INT boot_count;
|
||
|
static IBOOL verbose;
|
||
|
|
||
|
typedef enum { UNINITIALIZED, BOOTING, RUNNING, DEINITIALIZED } heap_state;
|
||
|
static heap_state current_state = UNINITIALIZED;
|
||
|
|
||
|
/***************************************************************************/
|
||
|
/* INITIALIZATION SUPPORT */
|
||
|
|
||
|
/* locally defined functions */
|
||
|
static void main_init(void);
|
||
|
static void idiot_checks(void);
|
||
|
static INT run_script(const char *who, const char *scriptfile, INT argc, const char *argv[], IBOOL programp);
|
||
|
|
||
|
extern void scheme_include(void);
|
||
|
|
||
|
static void main_init(void) {
|
||
|
ptr tc = get_thread_context();
|
||
|
ptr p;
|
||
|
INT i;
|
||
|
|
||
|
/* create dependency for linker */
|
||
|
scheme_statics();
|
||
|
|
||
|
/* force thread inline allocation to go through find_room until ready */
|
||
|
AP(tc) = (ptr)0;
|
||
|
EAP(tc) = (ptr)0;
|
||
|
REAL_EAP(tc) = (ptr)0;
|
||
|
/* set up dummy CP so locking in read/write/Scall won't choke */
|
||
|
CP(tc) = Svoid;
|
||
|
CODERANGESTOFLUSH(tc) = Snil;
|
||
|
|
||
|
if (S_boot_time) S_G.protect_next = 0;
|
||
|
|
||
|
S_segment_init();
|
||
|
S_alloc_init();
|
||
|
S_thread_init();
|
||
|
S_intern_init();
|
||
|
S_gc_init();
|
||
|
S_number_init();
|
||
|
S_schsig_init();
|
||
|
S_new_io_init();
|
||
|
S_print_init();
|
||
|
S_stats_init();
|
||
|
S_foreign_init();
|
||
|
S_prim_init();
|
||
|
S_prim5_init();
|
||
|
S_fasl_init();
|
||
|
S_machine_init();
|
||
|
S_flushcache_init(); /* must come after S_machine_init(); */
|
||
|
#ifdef FEATURE_EXPEDITOR
|
||
|
S_expeditor_init();
|
||
|
#endif /* FEATURE_EXPEDITOR */
|
||
|
|
||
|
if (!S_boot_time) return;
|
||
|
|
||
|
S_protect(&S_G.profile_counters);
|
||
|
S_G.profile_counters = Snil;
|
||
|
|
||
|
FXLENGTHBV(tc) = p = S_bytevector(256);
|
||
|
for (i = 0; i < 256; i += 1) {
|
||
|
BVIT(p, i) =
|
||
|
(iptr)FIX(i & 0x80 ? 8 : i & 0x40 ? 7 : i & 0x20 ? 6 : i & 0x10 ? 5 :
|
||
|
i & 0x08 ? 4 : i & 0x04 ? 3 : i & 0x02 ? 2 : i & 0x01 ? 1 : 0);
|
||
|
}
|
||
|
|
||
|
FXFIRSTBITSETBV(tc) = p = S_bytevector(256);
|
||
|
for (i = 0; i < 256; i += 1) {
|
||
|
BVIT(p, i) =
|
||
|
(iptr)FIX(i & 0x01 ? 0 : i & 0x02 ? 1 : i & 0x04 ? 2 : i & 0x08 ? 3 :
|
||
|
i & 0x10 ? 4 : i & 0x20 ? 5 : i & 0x40 ? 6 : i & 0x80 ? 7 : 0);
|
||
|
}
|
||
|
|
||
|
NULLIMMUTABLEVECTOR(tc) = S_null_immutable_vector();
|
||
|
NULLIMMUTABLEFXVECTOR(tc) = S_null_immutable_fxvector();
|
||
|
NULLIMMUTABLEBYTEVECTOR(tc) = S_null_immutable_bytevector();
|
||
|
NULLIMMUTABLESTRING(tc) = S_null_immutable_string();
|
||
|
|
||
|
PARAMETERS(tc) = S_G.null_vector;
|
||
|
for (i = 0 ; i < virtual_register_count ; i += 1) {
|
||
|
VIRTREG(tc, i) = FIX(0);
|
||
|
}
|
||
|
|
||
|
p = S_code(tc, type_code, size_rp_header);
|
||
|
CODERELOC(p) = S_relocation_table(0);
|
||
|
CODENAME(p) = Sfalse;
|
||
|
CODEARITYMASK(p) = FIX(0);
|
||
|
CODEFREE(p) = 0;
|
||
|
CODEINFO(p) = Sfalse;
|
||
|
CODEPINFOS(p) = Snil;
|
||
|
RPHEADERFRAMESIZE(&CODEIT(p, 0)) = 0;
|
||
|
RPHEADERLIVEMASK(&CODEIT(p, 0)) = 0;
|
||
|
RPHEADERTOPLINK(&CODEIT(p, 0)) =
|
||
|
(uptr)&RPHEADERTOPLINK(&CODEIT(p, 0)) - (uptr)p;
|
||
|
S_protect(&S_G.dummy_code_object);
|
||
|
S_G.dummy_code_object = p;
|
||
|
|
||
|
S_protect(&S_G.error_invoke_code_object);
|
||
|
S_G.error_invoke_code_object = Snil;
|
||
|
S_protect(&S_G.invoke_code_object);
|
||
|
S_G.invoke_code_object = Snil;
|
||
|
|
||
|
S_protect(&S_G.active_threads_id);
|
||
|
S_G.active_threads_id = S_intern((const unsigned char *)"$active-threads");
|
||
|
S_set_symbol_value(S_G.active_threads_id, FIX(0));
|
||
|
|
||
|
S_protect(&S_G.heap_reserve_ratio_id);
|
||
|
S_G.heap_reserve_ratio_id = S_intern((const unsigned char *)"$heap-reserve-ratio");
|
||
|
SETSYMVAL(S_G.heap_reserve_ratio_id, Sflonum(default_heap_reserve_ratio));
|
||
|
|
||
|
S_protect(&S_G.scheme_version_id);
|
||
|
S_G.scheme_version_id = S_intern((const unsigned char *)"$scheme-version");
|
||
|
S_protect(&S_G.make_load_binary_id);
|
||
|
S_G.make_load_binary_id = S_intern((const unsigned char *)"$make-load-binary");
|
||
|
S_protect(&S_G.load_binary);
|
||
|
S_G.load_binary = Sfalse;
|
||
|
}
|
||
|
|
||
|
static ptr fixtest = FIX(-1);
|
||
|
|
||
|
static void idiot_checks(void) {
|
||
|
IBOOL oops = 0;
|
||
|
|
||
|
if (bytes_per_segment < S_pagesize) {
|
||
|
fprintf(stderr, "bytes_per_segment (%x) < S_pagesize (%lx)\n",
|
||
|
bytes_per_segment, (long)S_pagesize);
|
||
|
oops = 1;
|
||
|
}
|
||
|
if (sizeof(iptr) != sizeof(ptr)) {
|
||
|
fprintf(stderr, "sizeof(iptr) [%ld] != sizeof(ptr) [%ld]\n",
|
||
|
(long)sizeof(iptr), (long)sizeof(ptr));
|
||
|
oops = 1;
|
||
|
}
|
||
|
if (sizeof(uptr) != sizeof(ptr)) {
|
||
|
fprintf(stderr, "sizeof(uptr) [%ld] != sizeof(ptr) [%ld]\n",
|
||
|
(long)sizeof(uptr), (long)sizeof(ptr));
|
||
|
oops = 1;
|
||
|
}
|
||
|
if (sizeof(ptr) * 8 != ptr_bits) {
|
||
|
fprintf(stderr, "sizeof(ptr) * 8 [%ld] != ptr_bits [%d]\n",
|
||
|
(long)sizeof(ptr), ptr_bits);
|
||
|
oops = 1;
|
||
|
}
|
||
|
if (sizeof(int) * 8 != int_bits) {
|
||
|
fprintf(stderr, "sizeof(int) * 8 [%ld] != int_bits [%d]\n",
|
||
|
(long)sizeof(int), int_bits);
|
||
|
oops = 1;
|
||
|
}
|
||
|
if (sizeof(short) * 8 != short_bits) {
|
||
|
fprintf(stderr, "sizeof(short) * 8 [%ld] != short_bits [%d]\n",
|
||
|
(long)sizeof(short), short_bits);
|
||
|
oops = 1;
|
||
|
}
|
||
|
if (sizeof(long) * 8 != long_bits) {
|
||
|
fprintf(stderr, "sizeof(long) * 8 [%ld] != long_bits [%d]\n",
|
||
|
(long)sizeof(long), long_bits);
|
||
|
oops = 1;
|
||
|
}
|
||
|
#ifndef WIN32
|
||
|
if (sizeof(long long) * 8 != long_long_bits) {
|
||
|
fprintf(stderr, "sizeof(long long) * 8 [%ld] != long_long_bits [%d]\n",
|
||
|
(long)sizeof(long long), long_long_bits);
|
||
|
oops = 1;
|
||
|
}
|
||
|
#endif
|
||
|
if (sizeof(wchar_t) * 8 != wchar_bits) {
|
||
|
fprintf(stderr, "sizeof(wchar_t) * 8 [%ld] != wchar_bits [%d]\n",
|
||
|
(long)sizeof(wchar_t), wchar_bits);
|
||
|
oops = 1;
|
||
|
}
|
||
|
if (sizeof(size_t) * 8 != size_t_bits) {
|
||
|
fprintf(stderr, "sizeof(size_t) * 8 [%ld] != size_t_bits [%d]\n",
|
||
|
(long)sizeof(size_t), size_t_bits);
|
||
|
oops = 1;
|
||
|
}
|
||
|
#ifndef WIN32
|
||
|
if (sizeof(ssize_t) * 8 != size_t_bits) {
|
||
|
fprintf(stderr, "sizeof(ssize_t) * 8 [%ld] != size_t_bits [%d]\n",
|
||
|
(long)sizeof(ssize_t), size_t_bits);
|
||
|
oops = 1;
|
||
|
}
|
||
|
#endif
|
||
|
if (sizeof(ptrdiff_t) * 8 != ptrdiff_t_bits) {
|
||
|
fprintf(stderr, "sizeof(ptrdiff_t) * 8 [%ld] != ptrdiff_t_bits [%d]\n",
|
||
|
(long)sizeof(ptrdiff_t), ptrdiff_t_bits);
|
||
|
oops = 1;
|
||
|
}
|
||
|
if (sizeof(time_t) * 8 != time_t_bits) {
|
||
|
fprintf(stderr, "sizeof(time_t) * 8 [%ld] != time_t_bits [%d]\n",
|
||
|
(long)sizeof(time_t), time_t_bits);
|
||
|
oops = 1;
|
||
|
}
|
||
|
if (sizeof(bigit) * 8 != bigit_bits) {
|
||
|
fprintf(stderr, "sizeof(bigit) * 8 [%ld] != bigit_bits [%d]\n",
|
||
|
(long)sizeof(bigit), bigit_bits);
|
||
|
oops = 1;
|
||
|
}
|
||
|
if (sizeof(bigitbigit) != 2 * sizeof(bigit)) {
|
||
|
fprintf(stderr, "sizeof(bigitbigit) [%ld] != sizeof(bigit) [%ld] * 2\n",
|
||
|
(long)sizeof(bigitbigit), (long)sizeof(bigit));
|
||
|
oops = 1;
|
||
|
}
|
||
|
if (sizeof(char) != 1) {
|
||
|
fprintf(stderr, "sizeof(char) [%ld] != 1\n", (long)sizeof(char));
|
||
|
oops = 1;
|
||
|
}
|
||
|
if (sizeof(I8) != 1) {
|
||
|
fprintf(stderr, "sizeof(I8) [%ld] != 1\n", (long)sizeof(I8));
|
||
|
oops = 1;
|
||
|
}
|
||
|
if (sizeof(U8) != 1) {
|
||
|
fprintf(stderr, "sizeof(U8) [%ld] != 1\n", (long)sizeof(U8));
|
||
|
oops = 1;
|
||
|
}
|
||
|
if (sizeof(I16) != 2) {
|
||
|
fprintf(stderr, "sizeof(I16) [%ld] != 2\n", (long)sizeof(I16));
|
||
|
oops = 1;
|
||
|
}
|
||
|
if (sizeof(U16) != 2) {
|
||
|
fprintf(stderr, "sizeof(U16) [%ld] != 2\n", (long)sizeof(U16));
|
||
|
oops = 1;
|
||
|
}
|
||
|
if (sizeof(I32) != 4) {
|
||
|
fprintf(stderr, "sizeof(I32) [%ld] != 4\n", (long)sizeof(I32));
|
||
|
oops = 1;
|
||
|
}
|
||
|
if (sizeof(U32) != 4) {
|
||
|
fprintf(stderr, "sizeof(U32) [%ld] != 4\n", (long)sizeof(U32));
|
||
|
oops = 1;
|
||
|
}
|
||
|
if (sizeof(I64) != 8) {
|
||
|
fprintf(stderr, "sizeof(I64) [%ld] != 8\n", (long)sizeof(I64));
|
||
|
oops = 1;
|
||
|
}
|
||
|
if (sizeof(U64) != 8) {
|
||
|
fprintf(stderr, "sizeof(U64) [%ld] != 8\n", (long)sizeof(U64));
|
||
|
oops = 1;
|
||
|
}
|
||
|
if (sizeof(string_char) != string_char_bytes) {
|
||
|
fprintf(stderr, "sizeof(string_char) [%ld] != string_char_bytes [%d]\n", (long)sizeof(string_char), string_char_bytes);
|
||
|
oops = 1;
|
||
|
}
|
||
|
if (UNFIX(fixtest) != -1) {
|
||
|
fprintf(stderr, "UNFIX operation failed\n");
|
||
|
oops = 1;
|
||
|
}
|
||
|
if (strlen(VERSION)+1 > HEAP_VERSION_LENGTH) {
|
||
|
fprintf(stderr, "insufficient space for version in heap header\n");
|
||
|
oops = 1;
|
||
|
}
|
||
|
if (strlen(MACHINE_TYPE)+1 > HEAP_MACHID_LENGTH) {
|
||
|
fprintf(stderr, "insufficient space for machine id in heap header\n");
|
||
|
oops = 1;
|
||
|
}
|
||
|
#define big 0
|
||
|
#define little 1
|
||
|
if (native_endianness == big) {
|
||
|
uptr x[1];
|
||
|
*x = 1;
|
||
|
if (*(char *)x != 0) {
|
||
|
fprintf(stderr, "endianness claimed to be big, appears to be little\n");
|
||
|
oops = 1;
|
||
|
}
|
||
|
} else {
|
||
|
uptr x[1];
|
||
|
*x = 1;
|
||
|
if (*(char *)x == 0) {
|
||
|
fprintf(stderr, "endianness claimed to be little, appears to be big\n");
|
||
|
oops = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (sizeof(bucket_pointer_list) != sizeof(bucket_list)) {
|
||
|
/* gc repurposes bucket_lists for bucket_pointer lists, so they'd better have the same size */
|
||
|
fprintf(stderr, "bucket_pointer_list and bucket_list have different sizes\n");
|
||
|
oops = 1;
|
||
|
}
|
||
|
|
||
|
if ((cards_per_segment & (sizeof(iptr) - 1)) != 0) {
|
||
|
/* gc sometimes processes dirty bytes sizeof(iptr) bytes at a time */
|
||
|
fprintf(stderr, "cards_per_segment is not a multiple of sizeof(iptr)\n");
|
||
|
oops = 1;
|
||
|
}
|
||
|
if (((uptr)(&((seginfo *)0)->dirty_bytes[0]) & (sizeof(iptr) - 1)) != 0) {
|
||
|
/* gc sometimes processes dirty bytes sizeof(iptr) bytes at a time */
|
||
|
fprintf(stderr, "dirty_bytes[0] is not iptr-aligned wrt to seginfo struct\n");
|
||
|
oops = 1;
|
||
|
}
|
||
|
if (!Sfixnump(type_vector | ~mask_vector)) {
|
||
|
/* gc counts on vector type/length looking like a fixnum, so it can put vectors in space_impure */
|
||
|
fprintf(stderr, "vector type/length field does not look like a fixnum\n");
|
||
|
oops = 1;
|
||
|
}
|
||
|
|
||
|
if (oops) S_abnormal_exit();
|
||
|
}
|
||
|
|
||
|
/***************************************************************************/
|
||
|
/* SUPPORT FOR CALLING INTO SCHEME */
|
||
|
|
||
|
/* locally defined functions */
|
||
|
static ptr boot_call(ptr tc, ptr p, INT n);
|
||
|
static void check_ap(ptr tc);
|
||
|
|
||
|
/* arguments and ac0 set up */
|
||
|
static ptr boot_call(ptr tc, ptr p, INT n) {
|
||
|
AC1(tc) = p;
|
||
|
CP(tc) = Svoid; /* don't have calling code object */
|
||
|
|
||
|
AC0(tc) = (ptr)(uptr)n;
|
||
|
S_call_help(tc, 0, 0);
|
||
|
check_ap(tc);
|
||
|
|
||
|
CP(tc) = Svoid; /* leave clean so direct Scall won't choke */
|
||
|
|
||
|
switch ((iptr)AC1(tc)) {
|
||
|
case 1:
|
||
|
p = AC0(tc);
|
||
|
break;
|
||
|
case 0:
|
||
|
p = Svoid;
|
||
|
break;
|
||
|
default:
|
||
|
p = S_get_scheme_arg(tc, 1);
|
||
|
break;
|
||
|
}
|
||
|
return p;
|
||
|
}
|
||
|
|
||
|
static void check_ap(ptr tc) {
|
||
|
if ((uptr)AP(tc) & (byte_alignment - 1)) {
|
||
|
(void) fprintf(stderr, "ap is not double word aligned\n");
|
||
|
S_abnormal_exit();
|
||
|
}
|
||
|
if ((ptr *)AP(tc) > (ptr *)EAP(tc)) {
|
||
|
(void) fprintf(stderr, "ap is greater than eap\n");
|
||
|
S_abnormal_exit();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void S_generic_invoke(ptr tc, ptr code) {
|
||
|
#if defined(PPCAIX)
|
||
|
struct {caddr_t entry, toc, static_link;} hdr;
|
||
|
hdr.entry = (caddr_t)&CODEIT(code,0);
|
||
|
hdr.toc = (caddr_t)0;
|
||
|
hdr.static_link = (caddr_t)0;
|
||
|
(*((void (*)(ptr))(void *)&hdr))(tc);
|
||
|
#elif defined(PPCNT)
|
||
|
/* under NT, function headers contain no static link */
|
||
|
struct {I32 entry, toc;} hdr;
|
||
|
typedef void (*ugly)(ptr);
|
||
|
ugly p;
|
||
|
hdr.entry = (I32)&CODEIT(code,0);
|
||
|
hdr.toc = (I32)0;
|
||
|
/* MSVC++ bombs with internal compiler error if we don't split this up */
|
||
|
p = (ugly)&hdr;
|
||
|
p(tc);
|
||
|
#elif defined(PARISC)
|
||
|
struct {I32 entry, env;} hdr;
|
||
|
typedef void (*ugly)(ptr);
|
||
|
ugly p;
|
||
|
hdr.entry = (I32)&CODEIT(code,0);
|
||
|
hdr.env = (I32)0;
|
||
|
p = (ugly)((I32)&hdr + 2);
|
||
|
p(tc);
|
||
|
#elif defined(WIN32) && !defined(__MINGW32__)
|
||
|
__try {
|
||
|
(*((void (*)(ptr))(void *)&CODEIT(code,0)))(tc);
|
||
|
}
|
||
|
__except(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
|
||
|
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
|
||
|
{
|
||
|
if (S_pants_down)
|
||
|
S_error_abort("nonrecoverable invalid memory reference");
|
||
|
else
|
||
|
S_error_reset("invalid memory reference");
|
||
|
}
|
||
|
#else
|
||
|
(*((void (*)(ptr))(void *)&CODEIT(code,0)))(tc);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
/***************************************************************************/
|
||
|
/* MISCELLANEOUS HELPERS */
|
||
|
|
||
|
/* locally defined functions */
|
||
|
static IBOOL next_path(char *path, const char *name, const char *ext, const char **sp, const char **dsp);
|
||
|
static const char *path_last(const char *path);
|
||
|
static char *get_defaultheapdirs(void);
|
||
|
|
||
|
static const char *path_last(p) const char *p; {
|
||
|
const char *s;
|
||
|
#ifdef WIN32
|
||
|
char c;
|
||
|
|
||
|
if ((c = *p) >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z')
|
||
|
if (*(p + 1) == ':')
|
||
|
p += 2;
|
||
|
#endif
|
||
|
|
||
|
for (s = p; *s != 0; s += 1)
|
||
|
if (DIRMARKERP(*s)) p = ++s;
|
||
|
return p;
|
||
|
}
|
||
|
|
||
|
#ifdef WIN32
|
||
|
#ifndef DEFAULT_HEAP_PATH
|
||
|
/* by default, look in executable directory or in parallel boot directory */
|
||
|
#define DEFAULT_HEAP_PATH "%x;%x\\..\\..\\boot\\%m"
|
||
|
#endif
|
||
|
#define SEARCHPATHSEP ';'
|
||
|
#define PATHSEP '\\'
|
||
|
|
||
|
static char *get_defaultheapdirs() {
|
||
|
char *result;
|
||
|
wchar_t buf[PATH_MAX];
|
||
|
DWORD len = sizeof(buf);
|
||
|
if (ERROR_SUCCESS != RegGetValueW(HKEY_LOCAL_MACHINE, L"Software\\Chez Scheme\\csv" VERSION, L"HeapSearchPath", RRF_RT_REG_SZ, NULL, buf, &len))
|
||
|
return DEFAULT_HEAP_PATH;
|
||
|
else if ((result = Swide_to_utf8(buf)))
|
||
|
return result;
|
||
|
else
|
||
|
return DEFAULT_HEAP_PATH;
|
||
|
}
|
||
|
#else /* not WIN32: */
|
||
|
#define SEARCHPATHSEP ':'
|
||
|
#define PATHSEP '/'
|
||
|
#ifndef DEFAULT_HEAP_PATH
|
||
|
#define DEFAULT_HEAP_PATH "/usr/lib/csv%v/%m:/usr/local/lib/csv%v/%m"
|
||
|
#endif
|
||
|
|
||
|
static char *get_defaultheapdirs() {
|
||
|
return DEFAULT_HEAP_PATH;
|
||
|
}
|
||
|
#endif /* WIN32 */
|
||
|
|
||
|
/* next_path isolates the next entry in the two-part search path sp/dsp,
|
||
|
* leaving the full path with name affixed in path and *sp / *dsp pointing
|
||
|
* past the current entry. it returns 1 on success and 0 if at the end of
|
||
|
* the search path. path should be a pointer to an unoccupied buffer
|
||
|
* PATH_MAX characters long. either or both of sp/dsp may be empty,
|
||
|
* but neither may be null, i.e., (char *)0. */
|
||
|
static IBOOL next_path(char *path, const char *name, const char *ext,
|
||
|
const char **sp, const char **dsp) {
|
||
|
char *p;
|
||
|
const char *s, *t;
|
||
|
|
||
|
#define setp(c) if (p >= path + PATH_MAX) { fprintf(stderr, "search path entry too long\n"); S_abnormal_exit(); } else *p++ = (c)
|
||
|
for (;;) {
|
||
|
s = *sp;
|
||
|
p = path;
|
||
|
/* copy first searchpath entry into path, substituting MACHINE_TYPE for %m,
|
||
|
* VERSION for %v, % for %%, and : (; windows) for %: (%; windows) */
|
||
|
while (*s != 0 && *s != SEARCHPATHSEP) {
|
||
|
switch (*s) {
|
||
|
case '%':
|
||
|
s += 1;
|
||
|
switch (*s) {
|
||
|
#ifdef WIN32
|
||
|
case 'x': {
|
||
|
wchar_t exepath[PATH_MAX]; DWORD n;
|
||
|
s += 1;
|
||
|
n = GetModuleFileNameW(NULL, exepath, PATH_MAX);
|
||
|
if (n == 0 || (n == PATH_MAX && GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
|
||
|
fprintf(stderr, "warning: executable path is too long; ignoring %%x\n");
|
||
|
} else {
|
||
|
char *tstart;
|
||
|
const char *tend;
|
||
|
tstart = Swide_to_utf8(exepath);
|
||
|
t = tstart;
|
||
|
tend = path_last(t);
|
||
|
if (tend != t) tend -= 1; /* back up to directory separator */
|
||
|
while (t != tend) setp(*t++);
|
||
|
free(tstart);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
#endif
|
||
|
case 'm':
|
||
|
s += 1;
|
||
|
t = MACHINE_TYPE;
|
||
|
while (*t != 0) setp(*t++);
|
||
|
break;
|
||
|
case 'v':
|
||
|
s += 1;
|
||
|
t = VERSION;
|
||
|
while (*t != 0) setp(*t++);
|
||
|
break;
|
||
|
case '%':
|
||
|
case SEARCHPATHSEP:
|
||
|
setp(*s++);
|
||
|
break;
|
||
|
default:
|
||
|
fprintf(stderr, "warning: ignoring extra %% in search path\n");
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
setp(*s++);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* unless entry was null, append name and ext onto path and return true with
|
||
|
* updated path, sp, and possibly dsp */
|
||
|
if (s != *sp) {
|
||
|
if ((p > path) && !DIRMARKERP(*(p - 1))) { setp(PATHSEP); }
|
||
|
t = name;
|
||
|
while (*t != 0) setp(*t++);
|
||
|
t = ext;
|
||
|
while (*t != 0) setp(*t++);
|
||
|
setp(0);
|
||
|
*sp = s;
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/* if current segment is empty, move to next segment. if next segment
|
||
|
* is empty, return false */
|
||
|
if (*s == 0) {
|
||
|
if (*(*sp = *dsp) == 0) return 0;
|
||
|
*dsp = "";
|
||
|
} else {
|
||
|
*sp = s + 1;
|
||
|
}
|
||
|
}
|
||
|
#undef setp
|
||
|
}
|
||
|
|
||
|
/***************************************************************************/
|
||
|
/* BOOT FILES */
|
||
|
|
||
|
typedef struct {
|
||
|
INT fd;
|
||
|
char path[PATH_MAX];
|
||
|
} boot_desc;
|
||
|
|
||
|
#define MAX_BOOT_FILES 10
|
||
|
static boot_desc bd[MAX_BOOT_FILES];
|
||
|
|
||
|
/* locally defined functions */
|
||
|
static char get_u8(INT fd);
|
||
|
static uptr get_uptr(INT fd, uptr *pn);
|
||
|
static INT get_string(INT fd, char *s, iptr max, INT *c);
|
||
|
static IBOOL find_boot(const char *name, const char *ext, int fd, IBOOL errorp);
|
||
|
static void load(ptr tc, iptr n, IBOOL base);
|
||
|
static void check_boot_file_state(const char *who);
|
||
|
|
||
|
static IBOOL find_boot(const char *name, const char *ext, int fd, IBOOL errorp) {
|
||
|
char pathbuf[PATH_MAX], buf[PATH_MAX];
|
||
|
uptr n = 0;
|
||
|
INT c;
|
||
|
const char *path;
|
||
|
char *expandedpath;
|
||
|
|
||
|
if ((fd != -1) || S_fixedpathp(name)) {
|
||
|
if (strlen(name) >= PATH_MAX) {
|
||
|
fprintf(stderr, "boot-file path is too long %s\n", name);
|
||
|
S_abnormal_exit();
|
||
|
}
|
||
|
|
||
|
path = name;
|
||
|
|
||
|
if (fd == -1) {
|
||
|
expandedpath = S_malloc_pathname(path);
|
||
|
fd = OPEN(expandedpath, O_BINARY|O_RDONLY, 0);
|
||
|
free(expandedpath);
|
||
|
}
|
||
|
|
||
|
if (fd == -1) {
|
||
|
if (errorp) {
|
||
|
fprintf(stderr, "cannot open boot file %s\n", path);
|
||
|
S_abnormal_exit();
|
||
|
} else {
|
||
|
if (verbose) fprintf(stderr, "trying %s...cannot open\n", path);
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
if (verbose) fprintf(stderr, "trying %s...opened\n", path);
|
||
|
|
||
|
/* check for magic number */
|
||
|
if (get_u8(fd) != fasl_type_header ||
|
||
|
get_u8(fd) != 0 ||
|
||
|
get_u8(fd) != 0 ||
|
||
|
get_u8(fd) != 0 ||
|
||
|
get_u8(fd) != 'c' ||
|
||
|
get_u8(fd) != 'h' ||
|
||
|
get_u8(fd) != 'e' ||
|
||
|
get_u8(fd) != 'z') {
|
||
|
fprintf(stderr, "malformed fasl-object header in %s\n", path);
|
||
|
S_abnormal_exit();
|
||
|
}
|
||
|
|
||
|
/* check version */
|
||
|
if (get_uptr(fd, &n) != 0) {
|
||
|
fprintf(stderr, "unexpected end of file on %s\n", path);
|
||
|
CLOSE(fd);
|
||
|
S_abnormal_exit();
|
||
|
}
|
||
|
|
||
|
if (n != scheme_version) {
|
||
|
fprintf(stderr, "%s is for Version %s; ", path, S_format_scheme_version(n));
|
||
|
/* use separate fprintf since S_format_scheme_version returns static string */
|
||
|
fprintf(stderr, "need Version %s\n", S_format_scheme_version(scheme_version));
|
||
|
CLOSE(fd);
|
||
|
S_abnormal_exit();
|
||
|
}
|
||
|
|
||
|
/* check machine type */
|
||
|
if (get_uptr(fd, &n) != 0) {
|
||
|
fprintf(stderr, "unexpected end of file on %s\n", path);
|
||
|
CLOSE(fd);
|
||
|
S_abnormal_exit();
|
||
|
}
|
||
|
|
||
|
if (n != machine_type) {
|
||
|
fprintf(stderr, "%s is for machine-type %s; need machine-type %s\n", path,
|
||
|
S_lookup_machine_type(n), S_lookup_machine_type(machine_type));
|
||
|
CLOSE(fd);
|
||
|
S_abnormal_exit();
|
||
|
}
|
||
|
} else {
|
||
|
const char *sp = Sschemeheapdirs;
|
||
|
const char *dsp = Sdefaultheapdirs;
|
||
|
|
||
|
path = pathbuf;
|
||
|
for (;;) {
|
||
|
if (!next_path(pathbuf, name, ext, &sp, &dsp)) {
|
||
|
if (errorp) {
|
||
|
fprintf(stderr, "cannot find compatible boot file %s%s in search path:\n \"%s%s\"\n",
|
||
|
name, ext,
|
||
|
Sschemeheapdirs, Sdefaultheapdirs);
|
||
|
S_abnormal_exit();
|
||
|
} else {
|
||
|
if (verbose) fprintf(stderr, "no compatible %s%s found\n", name, ext);
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
expandedpath = S_malloc_pathname(path);
|
||
|
fd = OPEN(expandedpath, O_BINARY|O_RDONLY, 0);
|
||
|
free(expandedpath);
|
||
|
if (fd == -1) {
|
||
|
if (verbose) fprintf(stderr, "trying %s...cannot open\n", path);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (verbose) fprintf(stderr, "trying %s...opened\n", path);
|
||
|
|
||
|
/* check for magic number */
|
||
|
if (get_u8(fd) != fasl_type_header ||
|
||
|
get_u8(fd) != 0 ||
|
||
|
get_u8(fd) != 0 ||
|
||
|
get_u8(fd) != 0 ||
|
||
|
get_u8(fd) != 'c' ||
|
||
|
get_u8(fd) != 'h' ||
|
||
|
get_u8(fd) != 'e' ||
|
||
|
get_u8(fd) != 'z') {
|
||
|
if (verbose) fprintf(stderr, "malformed fasl-object header in %s\n", path);
|
||
|
CLOSE(fd);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/* check version */
|
||
|
if (get_uptr(fd, &n) != 0) {
|
||
|
if (verbose) fprintf(stderr, "unexpected end of file on %s\n", path);
|
||
|
CLOSE(fd);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (n != scheme_version) {
|
||
|
if (verbose) {
|
||
|
fprintf(stderr, "%s is for Version %s; ", path, S_format_scheme_version(n));
|
||
|
/* use separate fprintf since S_format_scheme_version returns static string */
|
||
|
fprintf(stderr, "need Version %s\n", S_format_scheme_version(scheme_version));
|
||
|
}
|
||
|
CLOSE(fd);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/* check machine type */
|
||
|
if (get_uptr(fd, &n) != 0) {
|
||
|
if (verbose) fprintf(stderr, "unexpected end of file on %s\n", path);
|
||
|
CLOSE(fd);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (n != machine_type) {
|
||
|
if (verbose)
|
||
|
fprintf(stderr, "%s is for machine-type %s; need machine-type %s\n", path,
|
||
|
S_lookup_machine_type(n), S_lookup_machine_type(machine_type));
|
||
|
CLOSE(fd);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (verbose) fprintf(stderr, "version and machine type check\n");
|
||
|
|
||
|
if (get_u8(fd) != '(') { /* ) */
|
||
|
fprintf(stderr, "malformed boot file %s\n", path);
|
||
|
CLOSE(fd);
|
||
|
S_abnormal_exit();
|
||
|
}
|
||
|
|
||
|
/* ( */
|
||
|
if ((c = get_u8(fd)) == ')') {
|
||
|
if (boot_count != 0) {
|
||
|
fprintf(stderr, "base boot file %s must come before other boot files\n", path);
|
||
|
CLOSE(fd);
|
||
|
S_abnormal_exit();
|
||
|
}
|
||
|
} else {
|
||
|
if (boot_count == 0) {
|
||
|
for (;;) {
|
||
|
/* try to load heap or boot file this boot file requires */
|
||
|
if (get_string(fd, buf, PATH_MAX, &c) != 0) {
|
||
|
fprintf(stderr, "unexpected end of file on %s\n", path);
|
||
|
CLOSE(fd);
|
||
|
S_abnormal_exit();
|
||
|
}
|
||
|
if (find_boot(buf, ".boot", -1, 0)) break;
|
||
|
if (c == ')') {
|
||
|
char *sep; char *wastebuf[8];
|
||
|
fprintf(stderr, "cannot find subordinate boot file");
|
||
|
if (LSEEK(fd, 0, SEEK_SET) != 0 || READ(fd, wastebuf, 8) != 8) { /* attempt to rewind and read magic number */
|
||
|
fprintf(stderr, "---retry with verbose flag for more information\n");
|
||
|
CLOSE(fd);
|
||
|
S_abnormal_exit();
|
||
|
}
|
||
|
(void) get_uptr(fd, &n); /* version */
|
||
|
(void) get_uptr(fd, &n); /* machine type */
|
||
|
(void) get_u8(fd); /* open paren */
|
||
|
c = get_u8(fd);
|
||
|
for (sep = " "; ; sep = "or ") {
|
||
|
if (c == ')') break;
|
||
|
(void) get_string(fd, buf, PATH_MAX, &c);
|
||
|
fprintf(stderr, "%s%s.boot ", sep, buf);
|
||
|
}
|
||
|
fprintf(stderr, "required by %s\n", path);
|
||
|
CLOSE(fd);
|
||
|
S_abnormal_exit();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* skip to end of header */
|
||
|
while (c != ')') {
|
||
|
if (c < 0) {
|
||
|
fprintf(stderr, "malformed boot file %s\n", path);
|
||
|
CLOSE(fd);
|
||
|
S_abnormal_exit();
|
||
|
}
|
||
|
c = get_u8(fd);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (boot_count >= MAX_BOOT_FILES) {
|
||
|
fprintf(stderr, "exceeded maximum number of boot files (%d)\n", MAX_BOOT_FILES);
|
||
|
S_abnormal_exit();
|
||
|
}
|
||
|
|
||
|
bd[boot_count].fd = fd;
|
||
|
strcpy(bd[boot_count].path, path);
|
||
|
boot_count += 1;
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
static char get_u8(INT fd) {
|
||
|
char buf[1];
|
||
|
if (READ(fd, &buf, 1) != 1) return -1;
|
||
|
return buf[0];
|
||
|
}
|
||
|
|
||
|
static uptr get_uptr(INT fd, uptr *pn) {
|
||
|
uptr n, m; int c; octet k;
|
||
|
|
||
|
if ((c = get_u8(fd)) < 0) return -1;
|
||
|
k = (octet)c;
|
||
|
n = k >> 1;
|
||
|
while (k & 1) {
|
||
|
if ((c = get_u8(fd)) < 0) return -1;
|
||
|
k = (octet)c;
|
||
|
m = n << 7;
|
||
|
if (m >> 7 != n) return -1;
|
||
|
n = m | (k >> 1);
|
||
|
}
|
||
|
*pn = n;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static INT get_string(INT fd, char *s, iptr max, INT *c) {
|
||
|
while (max-- > 0) {
|
||
|
if (*c < 0) return -1;
|
||
|
if (*c == ' ' || *c == ')') {
|
||
|
if (*c == ' ') *c = get_u8(fd);
|
||
|
*s = 0;
|
||
|
return 0;
|
||
|
}
|
||
|
*s++ = *c;
|
||
|
*c = get_u8(fd);
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
static IBOOL loadecho = 0;
|
||
|
#define LOADSKIP 0
|
||
|
|
||
|
static int set_load_binary(iptr n) {
|
||
|
if (!Ssymbolp(SYMVAL(S_G.scheme_version_id))) return 0; // set by back.ss
|
||
|
ptr make_load_binary = SYMVAL(S_G.make_load_binary_id);
|
||
|
if (Sprocedurep(make_load_binary)) {
|
||
|
S_G.load_binary = Scall1(make_load_binary, Sstring_utf8(bd[n].path, -1));
|
||
|
return 1;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void load(ptr tc, iptr n, IBOOL base) {
|
||
|
ptr x; iptr i;
|
||
|
|
||
|
if (base) {
|
||
|
S_G.error_invoke_code_object = S_boot_read(bd[n].fd, bd[n].path);
|
||
|
if (!Scodep(S_G.error_invoke_code_object)) {
|
||
|
(void) fprintf(stderr, "first object on boot file not code object\n");
|
||
|
S_abnormal_exit();
|
||
|
}
|
||
|
|
||
|
S_G.invoke_code_object = S_boot_read(bd[n].fd, bd[n].path);
|
||
|
if (!Scodep(S_G.invoke_code_object)) {
|
||
|
(void) fprintf(stderr, "second object on boot file not code object\n");
|
||
|
S_abnormal_exit();
|
||
|
}
|
||
|
S_G.base_rtd = S_boot_read(bd[n].fd, bd[n].path);
|
||
|
if (!Srecordp(S_G.base_rtd)) {
|
||
|
S_abnormal_exit();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
i = 0;
|
||
|
while (i++ < LOADSKIP && S_boot_read(bd[n].fd, bd[n].path) != Seof_object);
|
||
|
|
||
|
while ((x = S_boot_read(bd[n].fd, bd[n].path)) != Seof_object) {
|
||
|
if (loadecho) {
|
||
|
printf("%ld: ", (long)i);
|
||
|
fflush(stdout);
|
||
|
}
|
||
|
if (Sprocedurep(x)) {
|
||
|
S_initframe(tc, 0);
|
||
|
x = boot_call(tc, x, 0);
|
||
|
} else if (Sprocedurep(S_G.load_binary) || set_load_binary(n)) {
|
||
|
S_initframe(tc, 1);
|
||
|
S_put_arg(tc, 1, x);
|
||
|
x = boot_call(tc, S_G.load_binary, 1);
|
||
|
}
|
||
|
if (loadecho) {
|
||
|
S_prin1(x);
|
||
|
putchar('\n');
|
||
|
fflush(stdout);
|
||
|
}
|
||
|
i += 1;
|
||
|
}
|
||
|
|
||
|
S_G.load_binary = Sfalse;
|
||
|
CLOSE(bd[n].fd);
|
||
|
}
|
||
|
|
||
|
/***************************************************************************/
|
||
|
/* HEAP FILES */
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
#define debug(x) {x}
|
||
|
#else
|
||
|
#define debug(x)
|
||
|
#endif
|
||
|
|
||
|
#include <fcntl.h>
|
||
|
#include <sys/types.h>
|
||
|
|
||
|
#ifdef WIN32
|
||
|
#include <io.h>
|
||
|
#endif /* WIN32 */
|
||
|
|
||
|
#ifdef MMAP_HEAP
|
||
|
#include <sys/mman.h>
|
||
|
#endif
|
||
|
|
||
|
#ifndef O_BINARY
|
||
|
#define O_BINARY 0
|
||
|
#endif /* O_BINARY */
|
||
|
|
||
|
#define check(expr,path) {if ((INT)(expr) < 0) {perror(path); S_abnormal_exit();}}
|
||
|
|
||
|
/***************************************************************************/
|
||
|
/* EXPORTED ROUTINES */
|
||
|
|
||
|
const char *Skernel_version(void) {
|
||
|
return VERSION;
|
||
|
}
|
||
|
|
||
|
extern void Sset_verbose(INT v) {
|
||
|
verbose = v;
|
||
|
}
|
||
|
|
||
|
extern void Sretain_static_relocation(void) {
|
||
|
S_G.retain_static_relocation = 1;
|
||
|
}
|
||
|
|
||
|
#if defined(CHECK_FOR_ROSETTA)
|
||
|
#include <sys/sysctl.h>
|
||
|
int is_rosetta = 0;
|
||
|
static void init_rosetta_check(void) {
|
||
|
int val = 0;
|
||
|
size_t size = sizeof(val);
|
||
|
if (sysctlbyname("sysctl.proc_translated", &val, &size, NULL, 0) != 0) {
|
||
|
if (errno == ENOENT) {
|
||
|
is_rosetta = 0;
|
||
|
} else {
|
||
|
perror("checking to see if running under Rosetta");
|
||
|
// if for some reason we can't tell whether we are running under Rosetta or not,
|
||
|
// default to the safer choice. It doesn't impact correctness to do the Rosetta
|
||
|
// workarounds when they are not needed.
|
||
|
is_rosetta = 1;
|
||
|
}
|
||
|
}
|
||
|
is_rosetta = val;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
#ifdef ITEST
|
||
|
#include "itest.c"
|
||
|
#endif
|
||
|
|
||
|
static void default_abnormal_exit(void) {
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
extern void Sscheme_init(void (*abnormal_exit)(void)) {
|
||
|
S_abnormal_exit_proc = abnormal_exit ? abnormal_exit : default_abnormal_exit;
|
||
|
S_errors_to_console = 1;
|
||
|
|
||
|
/* set before idiot checks */
|
||
|
S_pagesize = GETPAGESIZE();
|
||
|
|
||
|
idiot_checks();
|
||
|
#if defined(CHECK_FOR_ROSETTA)
|
||
|
init_rosetta_check();
|
||
|
#endif
|
||
|
|
||
|
switch (current_state) {
|
||
|
case RUNNING:
|
||
|
fprintf(stderr, "error (Sscheme_init): call Sscheme_deinit first to terminate\n");
|
||
|
S_abnormal_exit();
|
||
|
case BOOTING:
|
||
|
fprintf(stderr, "error (Sscheme_init): already initialized\n");
|
||
|
S_abnormal_exit();
|
||
|
case UNINITIALIZED:
|
||
|
case DEINITIALIZED:
|
||
|
break;
|
||
|
}
|
||
|
current_state = BOOTING;
|
||
|
|
||
|
S_G.retain_static_relocation = 0;
|
||
|
S_G.enable_object_counts = 0;
|
||
|
|
||
|
boot_count = 0;
|
||
|
|
||
|
#ifdef WIN32
|
||
|
Sschemeheapdirs = Sgetenv("SCHEMEHEAPDIRS");
|
||
|
#else
|
||
|
Sschemeheapdirs = getenv("SCHEMEHEAPDIRS");
|
||
|
#endif
|
||
|
if (Sschemeheapdirs == (char *)0) {
|
||
|
Sschemeheapdirs = "";
|
||
|
if ((Sdefaultheapdirs = get_defaultheapdirs()) == (char *)0) Sdefaultheapdirs = "";
|
||
|
} else if (*Sschemeheapdirs != 0 && Sschemeheapdirs[strlen(Sschemeheapdirs)-1] == SEARCHPATHSEP) {
|
||
|
if ((Sdefaultheapdirs = get_defaultheapdirs()) == (char *)0) Sdefaultheapdirs = "";
|
||
|
} else {
|
||
|
Sdefaultheapdirs = "";
|
||
|
}
|
||
|
|
||
|
#ifdef PTHREADS
|
||
|
{
|
||
|
int status;
|
||
|
if ((status = s_thread_key_create(&S_tc_key)) != 0)
|
||
|
S_error_abort(strerror(status));
|
||
|
s_thread_setspecific(S_tc_key, S_G.thread_context);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
#ifdef ITEST
|
||
|
S_boot_time = 1;
|
||
|
main_init();
|
||
|
|
||
|
bignum_test();
|
||
|
exit(0);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
static void check_boot_file_state(const char *who) {
|
||
|
switch (current_state) {
|
||
|
case UNINITIALIZED:
|
||
|
case DEINITIALIZED:
|
||
|
fprintf(stderr, "error (%s): uninitialized; call Sscheme_init first\n", who);
|
||
|
if (current_state == UNINITIALIZED) exit(1); else S_abnormal_exit();
|
||
|
case RUNNING:
|
||
|
fprintf(stderr, "error (%s): already running\n", who);
|
||
|
S_abnormal_exit();
|
||
|
case BOOTING:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
extern void Sregister_boot_file(const char *name) {
|
||
|
check_boot_file_state("Sregister_boot_file");
|
||
|
find_boot(name, "", -1, 1);
|
||
|
}
|
||
|
|
||
|
extern void Sregister_boot_file_fd(const char *name, int fd) {
|
||
|
check_boot_file_state("Sregister_boot_file_fd");
|
||
|
find_boot(name, "", fd, 1);
|
||
|
}
|
||
|
|
||
|
extern void Sregister_heap_file(UNUSED const char *path) {
|
||
|
fprintf(stderr, "Sregister_heap_file: saved heap files are not presently supported\n");
|
||
|
S_abnormal_exit();
|
||
|
}
|
||
|
|
||
|
extern void Sbuild_heap(const char *kernel, void (*custom_init)(void)) {
|
||
|
ptr tc = Svoid; /* initialize to make gcc happy */
|
||
|
ptr p;
|
||
|
|
||
|
switch (current_state) {
|
||
|
case UNINITIALIZED:
|
||
|
case DEINITIALIZED:
|
||
|
fprintf(stderr, "error (Sbuild_heap): uninitialized; call Sscheme_init first\n");
|
||
|
if (current_state == UNINITIALIZED) exit(1); else S_abnormal_exit();
|
||
|
case RUNNING:
|
||
|
fprintf(stderr, "error (Sbuild_heap): already running\n");
|
||
|
S_abnormal_exit();
|
||
|
case BOOTING:
|
||
|
break;
|
||
|
}
|
||
|
current_state = RUNNING;
|
||
|
|
||
|
S_boot_time = 1;
|
||
|
|
||
|
if (boot_count == 0) {
|
||
|
const char *name;
|
||
|
|
||
|
if (!kernel) {
|
||
|
fprintf(stderr, "no boot file or executable name specified\n");
|
||
|
S_abnormal_exit();
|
||
|
}
|
||
|
|
||
|
name = path_last(kernel);
|
||
|
if (strlen(name) >= PATH_MAX) {
|
||
|
fprintf(stderr, "executable name too long: %s\n", name);
|
||
|
S_abnormal_exit();
|
||
|
}
|
||
|
|
||
|
#ifdef WIN32
|
||
|
{ /* strip off trailing .exe, if any */
|
||
|
static char buf[PATH_MAX];
|
||
|
iptr n;
|
||
|
|
||
|
n = strlen(name) - 4;
|
||
|
if (n >= 0 && (_stricmp(name + n, ".exe") == 0)) {
|
||
|
strcpy(buf, name);
|
||
|
buf[n] = 0;
|
||
|
name = buf;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
if (!find_boot(name, ".boot", -1, 0)) {
|
||
|
fprintf(stderr, "cannot find compatible %s.boot in search path\n \"%s%s\"\n",
|
||
|
name,
|
||
|
Sschemeheapdirs, Sdefaultheapdirs);
|
||
|
S_abnormal_exit();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (boot_count != 0) {
|
||
|
INT i = 0;
|
||
|
|
||
|
main_init();
|
||
|
if (custom_init) custom_init();
|
||
|
|
||
|
S_threads = Snil;
|
||
|
S_nthreads = 0;
|
||
|
S_set_symbol_value(S_G.active_threads_id, FIX(0));
|
||
|
/* pass a parent tc of Svoid, since this call establishes the initial
|
||
|
* thread context and hence there is no parent thread context. */
|
||
|
tc = (ptr)THREADTC(S_create_thread_object("startup", tc));
|
||
|
#ifdef PTHREADS
|
||
|
s_thread_setspecific(S_tc_key, tc);
|
||
|
#endif
|
||
|
|
||
|
/* #scheme-init enables interrupts */
|
||
|
TRAP(tc) = (ptr)most_positive_fixnum;
|
||
|
DISABLECOUNT(tc) = Sfixnum(1);
|
||
|
COMPRESSFORMAT(tc) = FIX(COMPRESS_LZ4);
|
||
|
COMPRESSLEVEL(tc) = FIX(COMPRESS_MEDIUM);
|
||
|
|
||
|
load(tc, i++, 1);
|
||
|
S_boot_time = 0;
|
||
|
|
||
|
while (i < boot_count) load(tc, i++, 0);
|
||
|
}
|
||
|
|
||
|
if (boot_count != 0) Scompact_heap();
|
||
|
|
||
|
/* complete the initialization on the Scheme side */
|
||
|
p = S_symbol_value(S_intern((const unsigned char *)"$scheme-init"));
|
||
|
if (!Sprocedurep(p)) {
|
||
|
(void) fprintf(stderr,"\n$scheme-init is not bound to a procedure\n");
|
||
|
S_abnormal_exit();
|
||
|
}
|
||
|
|
||
|
S_initframe(tc, 0);
|
||
|
(void)boot_call(tc, p, 0);
|
||
|
|
||
|
/* should be okay to invoke Scheme's error handler now */
|
||
|
S_errors_to_console = 0;
|
||
|
}
|
||
|
|
||
|
extern void Senable_expeditor(const char *history_file) {
|
||
|
Scall1(S_symbol_value(Sstring_to_symbol("$enable-expeditor")), Strue);
|
||
|
if (history_file != (const char *)0)
|
||
|
Scall1(S_symbol_value(Sstring_to_symbol("$expeditor-history-file")),
|
||
|
Sstring_utf8(history_file, -1));
|
||
|
}
|
||
|
|
||
|
extern INT Sscheme_start(INT argc, const char *argv[]) {
|
||
|
ptr tc = get_thread_context();
|
||
|
ptr arglist, p; INT i;
|
||
|
|
||
|
switch (current_state) {
|
||
|
case UNINITIALIZED:
|
||
|
case DEINITIALIZED:
|
||
|
fprintf(stderr, "error (Sscheme_start): uninitialized; call Sscheme_init and Sbuild_heap first\n");
|
||
|
if (current_state == UNINITIALIZED) exit(1); else S_abnormal_exit();
|
||
|
case BOOTING:
|
||
|
fprintf(stderr, "error (Sscheme_start): no heap built yet; call Sbuild_heap first\n");
|
||
|
S_abnormal_exit();
|
||
|
case RUNNING:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
arglist = Snil;
|
||
|
for (i = argc - 1; i > 0; i -= 1)
|
||
|
arglist = Scons(Sstring_utf8(argv[i], -1), arglist);
|
||
|
|
||
|
p = S_symbol_value(S_intern((const unsigned char *)"$scheme"));
|
||
|
if (!Sprocedurep(p)) {
|
||
|
(void) fprintf(stderr,"\n$scheme is not bound to a procedure\n");
|
||
|
S_abnormal_exit();
|
||
|
}
|
||
|
|
||
|
S_initframe(tc, 1);
|
||
|
S_put_arg(tc, 1, arglist);
|
||
|
p = boot_call(tc, p, 1);
|
||
|
|
||
|
if (S_integer_valuep(p)) return (INT)Sinteger_value(p);
|
||
|
return p == Svoid ? 0 : 1;
|
||
|
}
|
||
|
|
||
|
static INT run_script(const char *who, const char *scriptfile, INT argc, const char *argv[], IBOOL programp) {
|
||
|
ptr tc = get_thread_context();
|
||
|
ptr arglist, p; INT i;
|
||
|
|
||
|
switch (current_state) {
|
||
|
case UNINITIALIZED:
|
||
|
case DEINITIALIZED:
|
||
|
fprintf(stderr, "error (%s): uninitialized; call Sscheme_init and Sbuild_heap first\n", who);
|
||
|
if (current_state == UNINITIALIZED) exit(1); else S_abnormal_exit();
|
||
|
case BOOTING:
|
||
|
fprintf(stderr, "error (%s): no heap built yet; call Sbuild_heap first\n", who);
|
||
|
S_abnormal_exit();
|
||
|
case RUNNING:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
arglist = Snil;
|
||
|
for (i = argc - 1; i > 0; i -= 1)
|
||
|
arglist = Scons(Sstring_utf8(argv[i], -1), arglist);
|
||
|
|
||
|
p = S_symbol_value(S_intern((const unsigned char *)"$script"));
|
||
|
if (!Sprocedurep(p)) {
|
||
|
(void) fprintf(stderr,"\n$script is not bound to a procedure\n");
|
||
|
S_abnormal_exit();
|
||
|
}
|
||
|
|
||
|
S_initframe(tc, 3);
|
||
|
S_put_arg(tc, 1, Sboolean(programp));
|
||
|
S_put_arg(tc, 2, Sstring_utf8(scriptfile, -1));
|
||
|
S_put_arg(tc, 3, arglist);
|
||
|
p = boot_call(tc, p, 3);
|
||
|
|
||
|
if (S_integer_valuep(p)) return (INT)Sinteger_value(p);
|
||
|
return p == Svoid ? 0 : 1;
|
||
|
}
|
||
|
|
||
|
extern INT Sscheme_script(const char *scriptfile, INT argc, const char *argv[]) {
|
||
|
return run_script("Sscheme_script", scriptfile, argc, argv, 0);
|
||
|
}
|
||
|
|
||
|
extern INT Sscheme_program(const char *programfile, INT argc, const char *argv[]) {
|
||
|
return run_script("Sscheme_program", programfile, argc, argv, 1);
|
||
|
}
|
||
|
|
||
|
extern void Ssave_heap(UNUSED const char *path, UNUSED INT level) {
|
||
|
fprintf(stderr, "Ssave_heap: saved heap files are not presently supported\n");
|
||
|
S_abnormal_exit();
|
||
|
}
|
||
|
|
||
|
extern void Sscheme_deinit(void) {
|
||
|
ptr p, tc = get_thread_context();
|
||
|
|
||
|
switch (current_state) {
|
||
|
case UNINITIALIZED:
|
||
|
case DEINITIALIZED:
|
||
|
fprintf(stderr, "error (Sscheme_deinit): not yet initialized or running\n");
|
||
|
if (current_state == UNINITIALIZED) exit(1); else S_abnormal_exit();
|
||
|
case BOOTING:
|
||
|
fprintf(stderr, "error (Sscheme_deinit): not yet running\n");
|
||
|
S_abnormal_exit();
|
||
|
case RUNNING:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
p = S_symbol_value(S_intern((const unsigned char *)"$close-files"));
|
||
|
S_initframe(tc, 0);
|
||
|
boot_call(tc, p, 0);
|
||
|
|
||
|
S_errors_to_console = 1;
|
||
|
current_state = DEINITIALIZED;
|
||
|
}
|