-/* Copyright (C) 1995,1996,1997,1998,1999,2000,2001, 2003, 2004 Free Software Foundation, Inc.
+/* Copyright (C) 1995,1996,1997,1998,1999,2000,2001, 2003, 2004, 2006, 2007 Free Software Foundation, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
\f
/* Headers. */
+#define _LARGEFILE64_SOURCE /* ask for stat64 etc */
+
#if HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdio.h>
#include <errno.h>
+#include <fcntl.h> /* for chsize on mingw */
+
+#include <assert.h>
#include "libguile/_scm.h"
#include "libguile/async.h"
#include "libguile/eval.h"
+#include "libguile/fports.h" /* direct access for seek and truncate */
#include "libguile/objects.h"
#include "libguile/goops.h"
#include "libguile/smob.h"
#include <string.h>
#endif
-#ifdef HAVE_MALLOC_H
-#include <malloc.h>
-#endif
-
#ifdef HAVE_IO_H
#include <io.h>
#endif
#include <sys/ioctl.h>
#endif
-#ifdef __MINGW32__
-#include <fcntl.h>
+/* Mingw (version 3.4.5, circa 2006) has ftruncate as an alias for chsize
+ already, but have this code here in case that wasn't so in past versions,
+ or perhaps to help other minimal DOS environments.
+
+ gnulib ftruncate.c has code using fcntl F_CHSIZE and F_FREESP, which
+ might be possibilities if we've got other systems without ftruncate. */
+
+#if HAVE_CHSIZE && ! HAVE_FTRUNCATE
#define ftruncate(fd, size) chsize (fd, size)
+#undef HAVE_FTRUNCATE
+#define HAVE_FTRUNCATE 1
#endif
\f
if (255 <= scm_numptob)
goto ptoberr;
SCM_CRITICAL_SECTION_START;
- SCM_SYSCALL (tmp = (char *) realloc ((char *) scm_ptobs,
- (1 + scm_numptob)
- * sizeof (scm_t_ptob_descriptor)));
+ tmp = (char *) scm_gc_realloc ((char *) scm_ptobs,
+ scm_numptob * sizeof (scm_t_ptob_descriptor),
+ (1 + scm_numptob)
+ * sizeof (scm_t_ptob_descriptor),
+ "port-type");
if (tmp)
{
scm_ptobs = (scm_t_ptob_descriptor *) tmp;
}
void
-scm_set_port_mark (long tc, SCM (*mark) (SCM))
+scm_set_port_mark (scm_t_bits tc, SCM (*mark) (SCM))
{
scm_ptobs[SCM_TC2PTOBNUM (tc)].mark = mark;
}
void
-scm_set_port_free (long tc, size_t (*free) (SCM))
+scm_set_port_free (scm_t_bits tc, size_t (*free) (SCM))
{
scm_ptobs[SCM_TC2PTOBNUM (tc)].free = free;
}
void
-scm_set_port_print (long tc, int (*print) (SCM exp, SCM port,
+scm_set_port_print (scm_t_bits tc, int (*print) (SCM exp, SCM port,
scm_print_state *pstate))
{
scm_ptobs[SCM_TC2PTOBNUM (tc)].print = print;
}
void
-scm_set_port_equalp (long tc, SCM (*equalp) (SCM, SCM))
+scm_set_port_equalp (scm_t_bits tc, SCM (*equalp) (SCM, SCM))
{
scm_ptobs[SCM_TC2PTOBNUM (tc)].equalp = equalp;
}
void
-scm_set_port_flush (long tc, void (*flush) (SCM port))
+scm_set_port_flush (scm_t_bits tc, void (*flush) (SCM port))
{
scm_ptobs[SCM_TC2PTOBNUM (tc)].flush = flush;
}
void
-scm_set_port_end_input (long tc, void (*end_input) (SCM port, int offset))
+scm_set_port_end_input (scm_t_bits tc, void (*end_input) (SCM port, int offset))
{
scm_ptobs[SCM_TC2PTOBNUM (tc)].end_input = end_input;
}
void
-scm_set_port_close (long tc, int (*close) (SCM))
+scm_set_port_close (scm_t_bits tc, int (*close) (SCM))
{
scm_ptobs[SCM_TC2PTOBNUM (tc)].close = close;
}
void
-scm_set_port_seek (long tc, off_t (*seek) (SCM port,
+scm_set_port_seek (scm_t_bits tc, off_t (*seek) (SCM port,
off_t OFFSET,
int WHENCE))
{
}
void
-scm_set_port_truncate (long tc, void (*truncate) (SCM port, off_t length))
+scm_set_port_truncate (scm_t_bits tc, void (*truncate) (SCM port, off_t length))
{
scm_ptobs[SCM_TC2PTOBNUM (tc)].truncate = truncate;
}
void
-scm_set_port_input_waiting (long tc, int (*input_waiting) (SCM))
+scm_set_port_input_waiting (scm_t_bits tc, int (*input_waiting) (SCM))
{
scm_ptobs[SCM_TC2PTOBNUM (tc)].input_waiting = input_waiting;
}
#undef FUNC_NAME
void
-scm_frame_current_input_port (SCM port)
+scm_dynwind_current_input_port (SCM port)
#define FUNC_NAME NULL
{
SCM_VALIDATE_OPINPORT (1, port);
- scm_frame_fluid (cur_inport_fluid, port);
+ scm_dynwind_fluid (cur_inport_fluid, port);
}
#undef FUNC_NAME
void
-scm_frame_current_output_port (SCM port)
+scm_dynwind_current_output_port (SCM port)
#define FUNC_NAME NULL
{
port = SCM_COERCE_OUTPORT (port);
SCM_VALIDATE_OPOUTPORT (1, port);
- scm_frame_fluid (cur_outport_fluid, port);
+ scm_dynwind_fluid (cur_outport_fluid, port);
}
#undef FUNC_NAME
void
-scm_frame_current_error_port (SCM port)
+scm_dynwind_current_error_port (SCM port)
#define FUNC_NAME NULL
{
port = SCM_COERCE_OUTPORT (port);
SCM_VALIDATE_OPOUTPORT (1, port);
- scm_frame_fluid (cur_errport_fluid, port);
+ scm_dynwind_fluid (cur_errport_fluid, port);
}
#undef FUNC_NAME
void
-scm_i_frame_current_load_port (SCM port)
+scm_i_dynwind_current_load_port (SCM port)
{
- scm_frame_fluid (cur_loadport_fluid, port);
+ scm_dynwind_fluid (cur_loadport_fluid, port);
}
\f
/* The port table --- an array of pointers to ports. */
-scm_t_port **scm_i_port_table;
+scm_t_port **scm_i_port_table = NULL;
-long scm_i_port_table_size = 0; /* Number of ports in scm_i_port_table. */
-long scm_i_port_table_room = 20; /* Size of the array. */
+long scm_i_port_table_size = 0; /* Number of ports in SCM_I_PORT_TABLE. */
+long scm_i_port_table_room = 20; /* Actual size of the array. */
scm_i_pthread_mutex_t scm_i_port_table_mutex = SCM_I_PTHREAD_MUTEX_INITIALIZER;
-/* This function is not and should not be thread safe. */
+\f
+/* Port finalization. */
+
+static void finalize_port (GC_PTR, GC_PTR);
+
+/* Register a finalizer for PORT, if needed by its port type. */
+static SCM_C_INLINE_KEYWORD void
+register_finalizer_for_port (SCM port)
+{
+ long port_type;
+
+ port_type = SCM_TC2PTOBNUM (SCM_CELL_TYPE (port));
+ if (scm_ptobs[port_type].free)
+ {
+ GC_finalization_proc prev_finalizer;
+ GC_PTR prev_finalization_data;
+
+ GC_REGISTER_FINALIZER_NO_ORDER (SCM2PTR (port), finalize_port, 0,
+ &prev_finalizer,
+ &prev_finalization_data);
+ }
+}
+
+/* Finalize the object (a port) pointed to by PTR. */
+static void
+finalize_port (GC_PTR ptr, GC_PTR data)
+{
+ long port_type;
+ SCM port = PTR2SCM (ptr);
+
+ if (!SCM_PORTP (port))
+ abort ();
+
+ if (SCM_OPENP (port))
+ {
+ if (SCM_REVEALED (port) > 0)
+ /* Keep "revealed" ports alive and re-register a finalizer. */
+ register_finalizer_for_port (port);
+ else
+ {
+ port_type = SCM_TC2PTOBNUM (SCM_CELL_TYPE (port));
+ if (port_type >= scm_numptob)
+ abort ();
+
+ if (scm_ptobs[port_type].free)
+ /* Yes, I really do mean `.free' rather than `.close'. `.close'
+ is for explicit `close-port' by user. */
+ scm_ptobs[port_type].free (port);
+
+ SCM_SETSTREAM (port, 0);
+ SCM_CLR_PORT_OPEN_FLAG (port);
+ scm_remove_from_port_table (port);
+
+ scm_gc_ports_collected++;
+ }
+ }
+}
+
+
+
+\f
+
+/* This function is not and should not be thread safe. */
SCM
scm_new_port_table_entry (scm_t_bits tag)
#define FUNC_NAME "scm_new_port_table_entry"
{
/* initial malloc is in gc.c. this doesn't use scm_gc_malloc etc.,
since it can never be freed during gc. */
- void *newt = scm_realloc ((char *) scm_i_port_table,
- (size_t) (sizeof (scm_t_port *)
- * scm_i_port_table_room * 2));
+ /* XXX (Ludo): Why not do it actually? */
+ size_t new_size = scm_i_port_table_room * 2;
+ /* XXX (Ludo): Can we use `GC_REALLOC' with
+ `GC_MALLOC_ATOMIC'-allocated data? */
+ void *newt = scm_gc_realloc ((char *) scm_i_port_table,
+ scm_i_port_table_room * sizeof (scm_t_port *),
+ new_size * sizeof (scm_t_port *),
+ "port-table");
scm_i_port_table = (scm_t_port **) newt;
- scm_i_port_table_room *= 2;
+ scm_i_port_table_room = new_size;
}
entry->entry = scm_i_port_table_size;
entry->port = z;
SCM_SET_CELL_TYPE(z, tag);
SCM_SETPTAB_ENTRY(z, entry);
-
+
+ /* For each new port, register a finalizer so that it port type's free
+ function can be invoked eventually. */
+ register_finalizer_for_port (z);
+
return z;
}
#undef FUNC_NAME
for (i = 0; i < n; i++)
proc (data, SCM_SIMPLE_VECTOR_REF (ports, i));
+
+ scm_remember_upto_here_1 (ports);
}
SCM_DEFINE (scm_port_for_each, "port-for-each", 1, 0, 0,
void
scm_putc (char c, SCM port)
{
+ SCM_ASSERT_TYPE (SCM_OPOUTPORTP (port), port, 0, NULL, "output port");
scm_lfwrite (&c, 1, port);
}
void
scm_puts (const char *s, SCM port)
{
+ SCM_ASSERT_TYPE (SCM_OPOUTPORTP (port), port, 0, NULL, "output port");
scm_lfwrite (s, strlen (s), port);
}
pt->rw_active = SCM_PORT_WRITE;
}
-void
+void
scm_flush (SCM port)
{
long i = SCM_PTOBNUM (port);
+ assert ((i >= 0) && (i < scm_i_port_table_size));
(scm_ptobs[i].flush) (port);
}
{
size_t new_size = pt->read_buf_size * 2;
unsigned char *tmp = (unsigned char *)
+ /* XXX: Can we use `GC_REALLOC' with `GC_MALLOC_ATOMIC'-allocated
+ data? (Ludo) */
scm_gc_realloc (pt->putback_buf, pt->read_buf_size, new_size,
"putback buffer");
if (pt->putback_buf == NULL)
{
pt->putback_buf
- = (unsigned char *) scm_gc_malloc (SCM_INITIAL_PUTBACK_BUF_SIZE,
- "putback buffer");
+ = (unsigned char *) scm_gc_malloc_pointerless
+ (SCM_INITIAL_PUTBACK_BUF_SIZE, "putback buffer");
pt->putback_buf_size = SCM_INITIAL_PUTBACK_BUF_SIZE;
}
"@end lisp")
#define FUNC_NAME s_scm_seek
{
- off_t off;
- off_t rv;
int how;
fd_port = SCM_COERCE_OUTPORT (fd_port);
- if (sizeof (off_t) == sizeof (scm_t_intmax))
- off = scm_to_intmax (offset);
- else
- off = scm_to_long (offset);
how = scm_to_int (whence);
-
if (how != SEEK_SET && how != SEEK_CUR && how != SEEK_END)
SCM_OUT_OF_RANGE (3, whence);
- if (SCM_OPPORTP (fd_port))
+
+ if (SCM_OPFPORTP (fd_port))
+ {
+ /* go direct to fport code to allow 64-bit offsets */
+ return scm_i_fport_seek (fd_port, offset, how);
+ }
+ else if (SCM_OPPORTP (fd_port))
{
scm_t_ptob_descriptor *ptob = scm_ptobs + SCM_PTOBNUM (fd_port);
+ off_t off = scm_to_off_t (offset);
+ off_t rv;
if (!ptob->seek)
SCM_MISC_ERROR ("port is not seekable",
scm_cons (fd_port, SCM_EOL));
else
rv = ptob->seek (fd_port, off, how);
+ return scm_from_off_t (rv);
}
else /* file descriptor?. */
{
- rv = lseek (scm_to_int (fd_port), off, how);
+ off_t_or_off64_t off = scm_to_off_t_or_off64_t (offset);
+ off_t_or_off64_t rv;
+ rv = lseek_or_lseek64 (scm_to_int (fd_port), off, how);
if (rv == -1)
SCM_SYSERROR;
+ return scm_from_off_t_or_off64_t (rv);
}
- return scm_from_intmax (rv);
}
#undef FUNC_NAME
-#ifdef __MINGW32__
-/* Define this function since it is not supported under Windows. */
-static int truncate (char *file, int length)
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+/* Mingw has ftruncate(), perhaps implemented above using chsize, but
+ doesn't have the filename version truncate(), hence this code. */
+#if HAVE_FTRUNCATE && ! HAVE_TRUNCATE
+static int
+truncate (const char *file, off_t length)
{
- int ret = -1, fdes;
- if ((fdes = open (file, O_BINARY | O_WRONLY)) != -1)
+ int ret, fdes;
+
+ fdes = open (file, O_BINARY | O_WRONLY);
+ if (fdes == -1)
+ return -1;
+
+ ret = ftruncate (fdes, length);
+ if (ret == -1)
{
- ret = chsize (fdes, length);
+ int save_errno = errno;
close (fdes);
+ errno = save_errno;
+ return -1;
}
- return ret;
+
+ return close (fdes);
}
-#endif /* __MINGW32__ */
+#endif /* HAVE_FTRUNCATE && ! HAVE_TRUNCATE */
SCM_DEFINE (scm_truncate_file, "truncate-file", 1, 1, 0,
(SCM object, SCM length),
- "Truncates the object referred to by @var{object} to at most\n"
- "@var{length} bytes. @var{object} can be a string containing a\n"
- "file name or an integer file descriptor or a port.\n"
- "@var{length} may be omitted if @var{object} is not a file name,\n"
- "in which case the truncation occurs at the current port\n"
- "position. The return value is unspecified.")
+ "Truncate @var{file} to @var{length} bytes. @var{file} can be a\n"
+ "filename string, a port object, or an integer file descriptor.\n"
+ "The return value is unspecified.\n"
+ "\n"
+ "For a port or file descriptor @var{length} can be omitted, in\n"
+ "which case the file is truncated at the current position (per\n"
+ "@code{ftell} above).\n"
+ "\n"
+ "On most systems a file can be extended by giving a length\n"
+ "greater than the current size, but this is not mandatory in the\n"
+ "POSIX standard.")
#define FUNC_NAME s_scm_truncate_file
{
int rv;
- off_t c_length;
- /* object can be a port, fdes or filename. */
+ /* "object" can be a port, fdes or filename.
+
+ Negative "length" makes no sense, but it's left to truncate() or
+ ftruncate() to give back an error for that (normally EINVAL).
+ */
if (SCM_UNBNDP (length))
{
length = scm_seek (object, SCM_INUM0, scm_from_int (SEEK_CUR));
}
- c_length = scm_to_size_t (length);
object = SCM_COERCE_OUTPORT (object);
if (scm_is_integer (object))
{
- SCM_SYSCALL (rv = ftruncate (scm_to_int (object), c_length));
+ off_t_or_off64_t c_length = scm_to_off_t_or_off64_t (length);
+ SCM_SYSCALL (rv = ftruncate_or_ftruncate64 (scm_to_int (object),
+ c_length));
+ }
+ else if (SCM_OPOUTFPORTP (object))
+ {
+ /* go direct to fport code to allow 64-bit offsets */
+ rv = scm_i_fport_truncate (object, length);
}
else if (SCM_OPOUTPORTP (object))
{
+ off_t c_length = scm_to_off_t (length);
scm_t_port *pt = SCM_PTAB_ENTRY (object);
scm_t_ptob_descriptor *ptob = scm_ptobs + SCM_PTOBNUM (object);
}
else
{
+ off_t_or_off64_t c_length = scm_to_off_t_or_off64_t (length);
char *str = scm_to_locale_string (object);
int eno;
- SCM_SYSCALL (rv = truncate (str, c_length));
+ SCM_SYSCALL (rv = truncate_or_truncate64 (str, c_length));
eno = errno;
free (str);
errno = eno;
{
port = SCM_COERCE_OUTPORT (port);
SCM_VALIDATE_OPENPORT (1, port);
- return scm_from_int (SCM_LINUM (port));
+ return scm_from_long (SCM_LINUM (port));
}
#undef FUNC_NAME
{
port = SCM_COERCE_OUTPORT (port);
SCM_VALIDATE_OPENPORT (1, port);
- SCM_PTAB_ENTRY (port)->line_number = scm_to_int (line);
+ SCM_PTAB_ENTRY (port)->line_number = scm_to_long (line);
return SCM_UNSPECIFIED;
}
#undef FUNC_NAME
scm_ports_prehistory ()
{
scm_numptob = 0;
- scm_ptobs = (scm_t_ptob_descriptor *) scm_malloc (sizeof (scm_t_ptob_descriptor));
+ scm_ptobs = NULL;
+
+ /* In order for the ports to be collectable, the port table must not be
+ scanned by the GC. */
+ scm_i_port_table =
+ scm_gc_malloc_pointerless (scm_i_port_table_room
+ * sizeof (scm_t_port *),
+ "port-table");
}
\f