stk/Src/port.c

1092 lines
28 KiB
C

/*
* p o r t . c -- ports implementation
*
* Copyright © 1993-1999 Erick Gallesio - I3S-CNRS/ESSI <eg@unice.fr>
*
*
* Permission to use, copy, modify, distribute,and license this
* software and its documentation for any purpose is hereby granted,
* provided that existing copyright notices are retained in all
* copies and that this notice is included verbatim in any
* distributions. No written agreement, license, or royalty fee is
* required for any of the authorized uses.
* This software is provided ``AS IS'' without express or implied
* warranty.
*
* Author: Erick Gallesio [eg@unice.fr]
* Creation date: 17-Feb-1993 12:27
* Last file update: 3-Sep-1999 20:21 (eg)
*
* Win32 support by Steve Pruitt <steve@pruitt.net>
* - Modified do_load for dynamic loading of WIN32 Dll files
* - Added check for *shared-prefix* in try_loadfile
* - Added "#define strcasecmp stricmp" for Win32 *shared-prefix* support
* - Modified STk_open_file to allow Win32 modes "wb" and "rb"
*
*/
#include "stk.h"
#include "module.h"
#define INITIAL_LINE_SIZE 256 /* Initial size for readline */
#if defined(WIN32) && !defined(CYGWIN32)
# define strcasecmp stricmp
/* Provide substitute functions dor WIN32 */
FILE *popen(char *cmd, char *mode)
{
/* Returning NULL will yield an error */
return NULL;
}
void pclose(FILE *f)
{}
#endif
/* external vars */
SCM STk_curr_iport, STk_curr_oport, STk_curr_eport, STk_eof_object;
SCM STk_Cfile2port(char *name, FILE *f, int type, int flags)
{
SCM z;
NEWCELL(z, type);
z->storage_as.port.p = (struct port_descr *)
must_malloc(sizeof(struct port_descr));
PORT_UNGETC(z) = EOF;
PORT_FILE(z) = f;
PORT_FLAGS(z) = flags;
PORT_REVENT(z) = Ntruth;
PORT_WEVENT(z) = Ntruth;
PORT_NAME(z) = (char *) must_malloc(strlen(name)+1);
strcpy(PORT_NAME(z), name);
return z;
}
static SCM makeport(char *name, int type, char *mode, int error)
{
int flags = 0;
FILE *f;
char *full_name;
#ifdef WIN32
/* Transform name in an absolute name */
name = CHARS(STk_internal_expand_file_name(name));
#endif
if (strncmp(name, "| ", 2)) {
full_name = CHARS(STk_internal_expand_file_name(name));
if ((f = fopen(full_name, mode)) == NULL) {
if (error) Err("could not open file", STk_makestring(name));
else return Ntruth;
}
}
else {
full_name = name;
flags = PIPE_PORT;
if ((f = popen(name+1, mode)) == NULL) {
if (error) Err("could not create pipe", STk_makestring(name));
else return Ntruth;
}
}
return STk_Cfile2port(full_name, f, type, flags);
}
static SCM verify_port(char *proc_name, SCM port, int mode)
{
if (port == UNBOUND) /* test write 'cause of flush */
port = (mode&F_WRITE) ? STk_curr_oport: STk_curr_iport;
if (!(INP(port) || OUTP(port))) Serror("bad port", port);
if (PORT_FLAGS(port) & PORT_CLOSED) Serror("port is closed", port);
if ((mode & F_READ) && INP(port)) return port; /* not else. It can be both */
if ((mode & F_WRITE) && OUTP(port)) return port;
Serror("bad port", port);
return UNDEFINED; /* cannot occur */
}
void STk_close_file_port(SCM port)
{
FILE *f = PORT_FILE(port);
#if defined(USE_TK) && !defined(WIN32)
/* For pipe and file ports, delete the fileevent associated to it (if any) */
Tcl_DeleteFileHandler(fileno(f));
#endif
if (PORT_FLAGS(port) & PIPE_PORT) /* Pipe port */
pclose(f);
else { /* File port */
/* FIXME: Normally close should suffice but glibc2.1.1 on Linux dumps
* core if a file is closed two times. (this arrives when a socket is
* lost and the interpreter call this function).
*/
fflush(f);
close(fileno(f));
}
}
void STk_freeport(SCM port)
{
STk_close(port);
free(PORT_NAME(port));
free(port->storage_as.port.p);
}
void STk_init_standard_ports(void)
{
STk_stdin = STk_curr_iport = STk_Cfile2port("*stdin*", stdin, tc_iport, 0);
STk_gc_protect(&STk_stdin);
STk_gc_protect(&STk_curr_iport);
STk_stdout = STk_curr_oport = STk_Cfile2port("*stdout*", stdout, tc_oport, 0);
STk_gc_protect(&STk_stdout);
STk_gc_protect(&STk_curr_oport);
STk_stderr = STk_curr_eport = STk_Cfile2port("*stderr*", stderr, tc_oport, 0);
STk_gc_protect(&STk_stderr);
STk_gc_protect(&STk_curr_eport);
NEWCELL(STk_eof_object, tc_eof);
STk_gc_protect(&STk_eof_object);
STk_line_counter = 1;
STk_current_filename = UNBOUND; /* Unbound <=> stdin */
STk_gc_protect(&STk_current_filename);
}
/*=============================================================================*\
*
* L O A D stuff
*
\*=============================================================================*/
static int do_load(char *full_name, SCM module)
{
int c;
#ifdef WIN32
/* Transform name in an absolute name */
full_name = CHARS(STk_internal_expand_file_name(full_name));
#endif
if (!STk_dirp(full_name)) {
FILE *f = fopen(full_name, "r");
if (f == NULL) return 0;
if (STk_lookup_variable(LOAD_VERBOSE, NIL) != Ntruth)
Fprintf(STk_curr_eport, ";; Loading file \"%s\"\n", full_name);
/* Just read one character. Assume that file is an object if this
* character is a control one. Here, I don't try to see if the file magic
* number has a particular value, since I'm not sure that all platforms
* use identical conventions
*/
c = getc(f); fclose(f);
#if (defined(WIN32) && defined(USE_DYNLOAD) && !defined(CYGWIN32))
/* If suffix is a Win32 shared-suffix then assume object file */
if (c != EOF) {
char *s, *shared_suffix;
s=full_name;
shared_suffix=CHARS(STk_lookup_variable("*shared-suffix*", NIL));
s = s + strlen(s) - strlen(shared_suffix);
if (stricmp(s,shared_suffix) == 0) c = *(s-1); else c = EOF;
}
if (c == '.') {
#else
if (c != EOF && ((iscntrl(c)&& c!= '\n' && c!= '\t') || !isascii(c))) {
#endif
STk_load_object_file(full_name);
}
else {
/* file seems not to be an object file. Try to load it as a Scheme file */
SCM prev_module;
SCM previous_file, port;
int previous_line;
/* Save info about current line and file */
previous_file = STk_current_filename;
previous_line = STk_line_counter;
STk_line_counter = 1;
STk_current_filename = STk_makestring(full_name);
/* Save info about selected module */
prev_module = STk_selected_module;
STk_selected_module = module;
/* Create port for reading */
port = makeport(full_name, tc_iport, "r", TRUE);
PUSH_ERROR_HANDLER
{
SCM form;
STk_err_handler->context |= ERR_IN_LOAD;
for( ; ; ) {
form = STk_readf(port, FALSE);
if (EQ(form, STk_eof_object)) break;
STk_eval(form, MOD_ENV(STk_selected_module));
}
}
WHEN_ERROR
{
STk_close(port);
STk_selected_module = prev_module;
STk_last_defined = Ntruth;
if (!STk_control_C) PROPAGATE_ERROR();
}
POP_ERROR_HANDLER;
STk_close(port);
STk_current_filename = previous_file;
STk_line_counter = previous_line;
STk_selected_module = prev_module;
STk_last_defined = Ntruth;
}
if (STk_lookup_variable(LOAD_VERBOSE, NIL) != Ntruth)
Fprintf(STk_curr_eport, ";; File \"%s\" loaded\n", full_name);
return 1;
}
/* No file found */
return 0;
}
static int try_loadfile(char *prefix, char *fname, SCM suffixes, SCM module)
{
char full_name[MAX_PATH_LENGTH], *s;
/* First try to load without suffix */
if (strlen(prefix) + strlen(fname) + 2 >= MAX_PATH_LENGTH) goto TooLong;
sprintf(full_name, "%s%s%s", prefix, (*prefix ? "/": ""), fname);
if (do_load(full_name, module)) return 1;
/* Now try to load file with suffix */
for ( ; NNULLP(suffixes); suffixes = CDR(suffixes)) {
/* We are sure that suffixes is a well formed list (ensured by loadfile) */
if (NSTRINGP(CAR(suffixes))) Err("load: bad suffix component", CAR(suffixes));
s = CHARS(CAR(suffixes));
if (strlen(prefix)+strlen(fname)+strlen(s)+3 >= MAX_PATH_LENGTH) goto TooLong;
sprintf(full_name, "%s%s%s.%s", prefix, (*prefix ? "/": ""), fname, s);
if (do_load(full_name, module)) return 1;
}
/* No file loaded */
return 0;
TooLong:
Err("load: filename too long", NIL);
return 0; /* cannot occur */
}
SCM STk_load_file(char *fname, int err_if_absent, SCM module)
{
int len;
SCM load_path, load_suffixes;
ENTER_PRIMITIVE("load");
len = strlen(fname);
load_path = STk_lookup_variable(LOAD_PATH, NIL);
load_suffixes = STk_lookup_variable(LOAD_SUFFIXES, NIL);
if (STk_llength(load_path)<0) Serror("bad loading path", load_path);
if (STk_llength(load_suffixes)<0) Serror("bad set of suffixes", load_suffixes);
#ifdef WIN32
if ((len > 0 && (fname[0] == '/' || fname[0] == '\\' || fname[0] == '~')) ||
(len > 1 && fname[0] == '.' && (fname[1] == '/' || fname[1] == '\\')) ||
(len > 2 && fname[0] == '.' && fname[1] == '.' && (fname[2] == '/' ||
fname[2]=='\\')) ||
(len > 1 && isalpha(fname[0]) && fname[1]==':')) {
#else
if ((len > 0 && (fname[0] == '/' || fname[0] == '~')) ||
(len > 1 && fname[0] == '.' && fname[1] == '/') ||
(len > 2 && fname[0] == '.' && fname[1] == '.' && fname[2] == '/')) {
#endif
if (fname[0] == '~')
fname = CHARS(STk_internal_expand_file_name(fname));
if (try_loadfile("", fname, load_suffixes, module))
return(err_if_absent? UNDEFINED: Truth);
}
else {
/* Use *load-path* for loading file */
for ( ; NNULLP(load_path); load_path = CDR(load_path)) {
if (NSTRINGP(CAR(load_path)))
Serror("bad loading path component", CAR(load_path));
if (try_loadfile(CHARS(CAR(load_path)), fname, load_suffixes, module))
return(err_if_absent? UNDEFINED: Truth);
}
}
/* If we are here, we have been unable to load a file. Report err if needed */
if (err_if_absent)
Serror("cannot open file", STk_makestring(fname));
return Ntruth;
}
PRIMITIVE STk_input_portp(SCM port)
{
return INP(port) ? Truth: Ntruth;
}
PRIMITIVE STk_output_portp(SCM port)
{
return OUTP(port)? Truth: Ntruth;
}
PRIMITIVE STk_input_file_portp(SCM port)
{
return IPORTP(port) ? Truth: Ntruth;
}
PRIMITIVE STk_output_file_portp(SCM port)
{
return OPORTP(port)? Truth: Ntruth;
}
PRIMITIVE STk_current_input_port(void)
{
return STk_curr_iport;
}
PRIMITIVE STk_current_output_port(void)
{
return STk_curr_oport;
}
PRIMITIVE STk_current_error_port(void)
{
return STk_curr_eport;
}
/*============================================================================*\
* w i t h - i n p u t - f r o m - . . .
\*============================================================================*/
SCM STk_redirect_input(SCM port, SCM thunk)
{
SCM result = UNDEFINED; /* to make gcc happy*/
SCM prev_iport = STk_curr_iport;
PUSH_ERROR_HANDLER
{
STk_curr_iport = port;
result = Apply0(thunk);
STk_curr_iport = prev_iport;
}
WHEN_ERROR
{
STk_curr_iport = prev_iport;
PROPAGATE_ERROR();
}
POP_ERROR_HANDLER;
return result;
}
PRIMITIVE STk_with_input_from_file(SCM string, SCM thunk)
{
SCM res, p;
ENTER_PRIMITIVE("with-input-from-file");
if (NSTRINGP(string)) Serror("bad string", string);
if (!STk_is_thunk(thunk)) Serror("bad thunk", thunk);
p = makeport(CHARS(string), tc_iport, "r", TRUE);
res = STk_redirect_input(p , thunk);
STk_close(p);
return res;
}
PRIMITIVE STk_with_input_from_port(SCM port, SCM thunk)
{
ENTER_PRIMITIVE("with-input-from-port");
if (!INP(port)) Serror("bad port", port);
if (!STk_is_thunk(thunk)) Serror("bad thunk", thunk);
return STk_redirect_input(port, thunk);
}
/*============================================================================*\
* w i t h - o u t p u t - t o - . . .
\*============================================================================*/
SCM STk_redirect_output(SCM port, SCM thunk)
{
SCM result = UNDEFINED; /* to make gcc happy*/
SCM prev_oport = STk_curr_oport;
PUSH_ERROR_HANDLER
{
STk_curr_oport = port;
result = Apply0(thunk);
STk_curr_oport = prev_oport;
}
WHEN_ERROR
{
STk_curr_oport = prev_oport;
PROPAGATE_ERROR();
}
POP_ERROR_HANDLER;
Flush(port);
return result;
}
PRIMITIVE STk_with_output_to_file(SCM string, SCM thunk)
{
SCM res, p;
ENTER_PRIMITIVE("with-output-to-file");
if (NSTRINGP(string)) Serror("bad string", string);
if (!STk_is_thunk(thunk)) Serror("bad thunk", thunk);
p = makeport(CHARS(string), tc_oport, "w", TRUE);
res = STk_redirect_output(p, thunk);
STk_close(p);
return res;
}
PRIMITIVE STk_with_output_to_port(SCM port, SCM thunk)
{
ENTER_PRIMITIVE("with-output-to-port");
if (!OUTP(port)) Serror("bad port", port);
if (!STk_is_thunk(thunk)) Serror("bad thunk", thunk);
return STk_redirect_output(port, thunk);
}
/*============================================================================*\
* w i t h - e r r o r - t o - . . .
\*============================================================================*/
SCM STk_redirect_error(SCM port, SCM thunk)
{
SCM result = UNDEFINED; /* to make gcc happy*/
SCM prev_eport = STk_curr_eport;
PUSH_ERROR_HANDLER
{
STk_curr_eport = port;
result = Apply0(thunk);
STk_curr_eport = prev_eport;
}
WHEN_ERROR
{
STk_curr_eport = prev_eport;
PROPAGATE_ERROR();
}
POP_ERROR_HANDLER;
Flush(port);
return result;
}
PRIMITIVE STk_with_error_to_file(SCM string, SCM thunk)
{
SCM res, p;
ENTER_PRIMITIVE("with-error-to-file");
if (NSTRINGP(string)) Serror("bad string", string);
if (!STk_is_thunk(thunk)) Serror("bad thunk", thunk);
p = makeport(CHARS(string), tc_oport, "w", TRUE);
res = STk_redirect_error(p, thunk);
STk_close(p);
return res;
}
PRIMITIVE STk_with_error_to_port(SCM port, SCM thunk)
{
ENTER_PRIMITIVE("with-error-to-port");
if (!OUTP(port)) Serror("bad port", port);
if (!STk_is_thunk(thunk)) Serror("bad thunk", thunk);
return STk_redirect_error(port, thunk);
}
/*=============================================================================*\
* Open/Close
\*=============================================================================*/
PRIMITIVE STk_open_input_file(SCM filename)
{
if (NSTRINGP(filename)) Err("open-input-file: bad file name", filename);
return makeport(CHARS(filename), tc_iport, "r", TRUE);
}
PRIMITIVE STk_open_output_file(SCM filename)
{
if (NSTRINGP(filename)) Err("open-output-file: bad file name", filename);
return makeport(CHARS(filename), tc_oport, "w", TRUE);
}
PRIMITIVE STk_close_input_port(SCM port)
{
if (!INP(port)) Err("close-input-port: not an input port", port);
STk_close(port);
return UNDEFINED;
}
PRIMITIVE STk_close_output_port(SCM port)
{
if (!OUTP(port)) Err("close-output-port: not an output port", port);
STk_close(port);
return UNDEFINED;
}
/*=============================================================================*\
* Read
\*=============================================================================*/
PRIMITIVE STk_read(SCM port)
{
port = verify_port("read", port, F_READ);
return(STk_readf(port, FALSE));
}
PRIMITIVE STk_read_char(SCM port)
{
int c;
port = verify_port("read-char", port, F_READ);
c = Getc(port);
return (c == EOF) ? STk_eof_object : STk_makechar((unsigned char) c);
}
PRIMITIVE STk_peek_char(SCM port)
{
int c;
port = verify_port("peek-char", port, F_READ);
c = Getc(port);
Ungetc(c, port);
return (c == EOF) ? STk_eof_object : STk_makechar((unsigned char) c);
}
PRIMITIVE STk_eof_objectp(SCM obj)
{
return (obj == STk_eof_object)? Truth : Ntruth;
}
PRIMITIVE STk_char_readyp(SCM port)
{
port = verify_port("char-ready?", port, F_READ);
return STk_internal_char_readyp(port) ? Truth : Ntruth;
}
/*=============================================================================*\
* Write
\*=============================================================================*/
PRIMITIVE STk_write(SCM expr, SCM port)
{
port = verify_port("write", port, F_WRITE);
STk_print(expr, port, WRT_MODE);
return UNDEFINED;
}
PRIMITIVE STk_display(SCM expr, SCM port)
{
port = verify_port("display", port, F_WRITE);
STk_print(expr, port, DSP_MODE);
return UNDEFINED;
}
PRIMITIVE STk_newline(SCM port)
{
port = verify_port("newline", port, F_WRITE);
Putc('\n', port);
return UNDEFINED;
}
PRIMITIVE STk_write_char(SCM c, SCM port)
{
ENTER_PRIMITIVE("write-char");
if (NCHARP(c)) Serror("not a character", c);
port = verify_port(proc_name, port, F_WRITE);
Putc(CHAR(c), port);
return UNDEFINED;
}
/*=============================================================================*\
* Load
\*=============================================================================*/
PRIMITIVE STk_load(SCM filename, SCM module)
{
ENTER_PRIMITIVE("load");
if (NSTRINGP(filename)) Serror("bad file name", filename);
if (module != UNBOUND) {
if (NMODULEP(module)) Serror("bad module", module);
}
else
module = STk_selected_module;
return STk_load_file(CHARS(filename), TRUE, module);
}
/*===========================================================================*\
*
* S T k b o n u s
*
\*===========================================================================*/
static SCM internal_format(SCM l,int len,int error)/* a very simple and poor one */
{
SCM port, fmt;
int format_in_string = 0;
char *p, *proc_name = error? "error": "format";
if (error) {
if (len < 1) Serror("bad list of parameters", l);
format_in_string = 1;
port = STk_open_output_string();
len -= 1;
}
else {
if (len < 2) Serror("bad list of parameters", l);
port = CAR(l); l = CDR(l);
len -= 2;
}
fmt = CAR(l); l = CDR(l);
if (BOOLEANP(port)){
if (port == Truth) port = STk_curr_oport;
else {
format_in_string = 1;
port= STk_open_output_string();
}
}
verify_port(proc_name, port, F_WRITE);
if (NSTRINGP(fmt)) Serror("bad format string", fmt);
for(p=CHARS(fmt); *p; p++) {
if (*p == '~') {
switch(*(++p)) {
case 'A':
case 'a': if (len-- <= 0) goto TooMuch;
STk_print(CAR(l), port, DSP_MODE);
l = CDR(l);
continue;
case 'S':
case 's': if (len-- <= 0) goto TooMuch;
STk_print(CAR(l), port, WRT_MODE);
l = CDR(l);
continue;
case 'W':
case 'w': if (len-- <= 0) goto TooMuch;
STk_print_star(CAR(l), port);
l = CDR(l);
continue;
case '%': Putc('\n', port);
continue;
case '~': Putc('~', port);
continue;
default: Putc('~', port);
/* NO BREAK */
}
}
Putc(*p, port);
}
if (NNULLP(l)) Serror("too few ~ in format string", l);
return format_in_string ? STk_get_output_string(port) : UNDEFINED;
TooMuch:
Serror("too many ``~'' in format string", l);
return UNDEFINED;
}
PRIMITIVE STk_format(SCM l, int len)
{
return internal_format(l, len, FALSE);
}
PRIMITIVE STk_error(SCM l, int len)
{
/* Set context to ERR_OK but keep the bit indicating if error must be caught */
STk_err_handler->context = ERR_OK | ( STk_err_handler->context & ERR_IGNORED);
Err(CHARS(internal_format(l, len, TRUE)), NIL);
return UNDEFINED; /* for compiler */
}
PRIMITIVE STk_try_load(SCM filename, SCM module)
{
ENTER_PRIMITIVE("try-load");
if (NSTRINGP(filename)) Serror("bad file name", filename);
if (module != UNBOUND) {
if (NMODULEP(module)) Serror("bad module", module);
}
else
module = STk_selected_module;
return STk_load_file(CHARS(filename), FALSE, module);
}
PRIMITIVE STk_open_file(SCM filename, SCM mode)
{
int type;
ENTER_PRIMITIVE("open-file");
if (NSTRINGP(filename)) Serror("bad file name", filename);
/* allow modes "wb" and "rb" on win32 systems */
#if (defined(MSC_VER) && defined(WIN32) && !defined(CYGWIN32))
if (NSTRINGP(mode)) goto Error;
if (CHARS(mode)[1] != '\0' && CHARS(mode)[1] != 'b') goto Error;
#else
if (NSTRINGP(mode) || CHARS(mode)[1] != '\0') goto Error;
#endif
switch (CHARS(mode)[0]) {
case 'a':
case 'w': type = tc_oport; break;
case 'r': type = tc_iport; break;
default: goto Error;
}
return(makeport(CHARS(filename), type, CHARS(mode), FALSE));
Error:
Serror("bad mode", mode);
return UNDEFINED; /* for the compiler */
}
PRIMITIVE STk_close_port(SCM port)
{
if (INP(port) || OUTP(port)) STk_close(port);
else Err("close-port: bad port", port);
return UNDEFINED;
}
PRIMITIVE STk_port_closedp(SCM port)
{
ENTER_PRIMITIVE("port-closed?");
if (!(INP(port) || OUTP(port))) Serror("bad port", port);
return (PORT_FLAGS(port) & PORT_CLOSED) ? Truth : Ntruth;
}
PRIMITIVE STk_read_line(SCM port)
{
int c;
char buffer[INITIAL_LINE_SIZE], *buff;
size_t i, size = INITIAL_LINE_SIZE;
SCM res;
port = verify_port("read-line", port, F_READ);
buff = buffer;
for (i = 0; ; i++) {
if (i == size) {
/* We must enlarge the buffer */
size += size / 2;
if (i == INITIAL_LINE_SIZE) {
/* This is the first resize. Pass from static to dynamic allocation */
buff = must_malloc(size);
strncpy(buff, buffer, INITIAL_LINE_SIZE);
}
else
buff = must_realloc(buff, size);
}
switch (c = Getc(port)) {
case EOF: if (i == 0) return STk_eof_object;
/* NO BREAK */
case '\n': res = STk_makestrg(i, buff);
if (buff != buffer) free(buff);
return res;
case '\r': i--; continue;
default: buff[i] = c;
}
}
}
PRIMITIVE STk_copy_port(SCM in, SCM out)
{
int c;
ENTER_PRIMITIVE("copy-port");
if (! INP(in)) Serror("bad input port", in);
if (! OUTP(out)) Serror("bad output port", out);
while ((c = Getc(in)) != EOF)
Putc(c, out);
return UNDEFINED;
}
PRIMITIVE STk_flush(SCM port)
{
ENTER_PRIMITIVE("flush");
port = verify_port(proc_name, port, F_WRITE|F_READ);
if (STk_internal_flush(port))
Serror("cannot flush buffer", port);
return UNDEFINED;
}
PRIMITIVE STk_write_star(SCM expr, SCM port)
{
port = verify_port("write*", port, F_WRITE);
STk_print_star(expr, port);
return UNDEFINED;
}
/******************************************************************************
*
* Autoload stuff
*
******************************************************************************/
static SCM list_of_files = NULL;
static int dont_do_autoload = 0; /* 1 if we are testing autoload? */
static SCM make_autoload(SCM file, SCM env)
{
SCM z;
NEWCELL(z, tc_autoload);
CAR(z) = file;
CDR(z) = env;
return z;
}
void STk_do_autoload(SCM var, SCM autoload)
{
static int recursive_call = 0;
ENTER_PRIMITIVE("autoload");
if (dont_do_autoload) return;
if (recursive_call) {
/* We have a recursive call if var has not be defined in the specified
* file. In effect when a file has been loaded, we just try to find
* the value of var. If this value has not been defined we will do
* another STk_do_autoload
*/
recursive_call = 0;
Serror("symbol was not defined", var);
}
{
SCM file = CAR(autoload);
SCM module = CDR(autoload);
SCM loaded;
/* Retain in a list, files which are currently autoloaded to avoid mult. load */
if (!list_of_files) {
list_of_files = NIL;
STk_gc_protect(&list_of_files);
}
if (STk_member(file, list_of_files) != Ntruth) return;
list_of_files = Cons(file, list_of_files);
/* Load file in the module used when the symbol was defined as autoload */
loaded = STk_load_file(CHARS(file), FALSE, module);
list_of_files = CDR(list_of_files);
/* Verify that file was really loaded (loaded is true in this case) */
if (loaded == Ntruth)
Serror("file not found for autoload symbol", Cons(var, file));
/* File is now loaded. Try to lookup the value of var and see if it
* provokes another STk_do_autoload call
*/
recursive_call = 1;
STk_varlookup(var, MOD_ENV(module), TRUE);
recursive_call = 0;
}
}
PRIMITIVE STk_autoload(SCM l, SCM env, int len)
{
SCM file, current_module;
ENTER_PRIMITIVE("autoload");
if (len < 2) Serror("bad parameter list", l);
file = CAR(l);
if (NSTRINGP(file)) Serror("bad file name", file);
current_module = STk_current_module(NIL, env, 0);
for (l = CDR(l); NNULLP(l); l = CDR(l)) {
if (NSYMBOLP(CAR(l))) Serror("bad symbol", CAR(l));
STk_define_public_var(current_module,
CAR(l),
make_autoload(file, current_module));
}
return UNDEFINED;
}
PRIMITIVE STk_autoloadp(SCM symbol, SCM module)
{
SCM *value, env;
ENTER_PRIMITIVE("autoload?");
if (NSYMBOLP(symbol)) Serror("bad symbol", symbol);
if (module == UNBOUND)
env = STk_global_module;
else {
if (NMODULEP(module)) Serror("bad module", module);
env = MOD_ENV(module);
}
/* Looking at var value will load the file. Signal that we don't want to load */
/* This is a little bit hacky, but it does the job */
dont_do_autoload = 1;
value = STk_varlookup(symbol, env, FALSE);
dont_do_autoload = 0;
return TYPEP(*value, tc_autoload) ? Truth: Ntruth;
}
#if defined(USE_TK) && !defined(WIN32)
/******************************************************************************
*
* Port event management
*
******************************************************************************/
static void apply_file_closure(SCM closure)
{
Apply0(closure);
}
static SCM when_port_ready(SCM port, SCM closure, char *name, int mode)
{
char str[50];
int fd;
if (NIPORTP(port) && NOPORTP(port)) {
sprintf(str, "%s: bad port", name);
STk_err(str, port);
}
if (closure == UNBOUND) {
/* Return the current handler closure */
return ((mode == TCL_READABLE)? PORT_REVENT(port): PORT_WEVENT(port));
}
fd = fileno(PORT_FILE(port));
if (closure == Ntruth) {
Tcl_DeleteFileHandler(fd);
if (mode == TCL_READABLE)
PORT_REVENT(port) = Ntruth;
else
PORT_WEVENT(port) = Ntruth;
}
else {
if (STk_procedurep(closure) == Ntruth) {
sprintf(str, "%s: bad closure", name);
STk_err(str, closure);
}
/* It is not necessary to mark the closure in the Tcl tables since it is
* also pointed by the Scheme port. This prevent GC problems
*/
Tcl_CreateFileHandler(fd, mode, (Tcl_FileProc *) apply_file_closure,
(ClientData) closure);
if (mode == TCL_READABLE)
PORT_REVENT(port) = closure;
else
PORT_WEVENT(port) = closure;
}
return UNDEFINED;
}
PRIMITIVE STk_when_port_readable(SCM port, SCM closure)
{
return when_port_ready(port, closure, "when-port-readable", TCL_READABLE);
}
PRIMITIVE STk_when_port_writable(SCM port, SCM closure)
{
return when_port_ready(port, closure, "when-port-writable", TCL_WRITABLE);
}
#endif
#ifdef USE_TK
/******************************************************************************
*
* Changing standard ports
*
******************************************************************************/
PRIMITIVE STk_change_standard_ports(SCM in, SCM out, SCM err)
{
static int cpt = 0;
ENTER_PRIMITIVE("%change-standard-ports");
if (cpt++)
Serror("Cannot redirected standard port anymore", NIL);
if (!INP(in)) Serror("bad input port", in);
if (!OUTP(out)) Serror("bad output port", out);
if (!OUTP(err)) Serror("bad error port", err);
STk_curr_iport = in;
STk_curr_oport = out;
STk_curr_eport = err;
return UNDEFINED;
}
#endif