Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
branch: fix-socket-rea…
Fetching contributors…

Cannot retrieve contributors at this time

652 lines (433 sloc) 15.106 kb
/*
Copyright (C) 2008-2013, Parrot Foundation.
=head1 NAME
src/pmc/filehandle.pmc - FileHandle PMC
=head1 DESCRIPTION
The FileHandle PMC performs I/O operations on a source or destination file.
=head2 Vtable Functions
=over 4
=cut
*/
#include "../src/io/io_private.h"
#include "pmc/pmc_bytebuffer.h"
#ifdef PARROT_HAS_READLINE
#ifdef __cplusplus
extern "C" {
#endif
char *readline(const char *);
void add_history(const char*);
#ifdef __cplusplus
}
#endif
#endif
/* HEADERIZER HFILE: none */
/* HEADERIZER BEGIN: static */
/* HEADERIZER END: static */
pmclass FileHandle extends Handle auto_attrs {
ATTR INTVAL flags; /* Filehandle flags */
ATTR STRING *filename; /* The opened path and filename */
ATTR STRING *mode; /* The mode string used in open */
ATTR INTVAL process_id; /* Child process on pipes */
ATTR INTVAL exit_status; /* Child exit status on pipes */
ATTR PIOOFF_T file_pos; /* Current real file pointer */
ATTR PIOOFF_T last_pos; /* Last file position */
/*
* Using INTVAL for process_id is a temporary solution.
* We may need to define a custom type to store it in a platform dependant way.
*/
/*
=item C<void init()>
Initializes a newly created FileHandle object.
=cut
*/
VTABLE void init() {
Parrot_FileHandle_attributes * const attrs =
PMC_data_typed(SELF, Parrot_FileHandle_attributes *);
attrs->record_separator = CONST_STRING(interp, "\n");
attrs->flags = 0;
attrs->filename = STRINGNULL;
attrs->mode = STRINGNULL;
attrs->encoding = STRINGNULL;
attrs->process_id = 0;
attrs->exit_status = 0;
attrs->file_pos = piooffsetzero;
attrs->last_pos = piooffsetzero;
attrs->io_vtable = (IO_VTABLE *)Parrot_io_get_vtable(interp,
IO_VTABLE_FILEHANDLE, NULL);
/* Initialize the os_handle to the platform-specific value for closed */
attrs->os_handle = (PIOHANDLE) PIO_INVALID_HANDLE;
attrs->read_buffer = NULL;
attrs->write_buffer = NULL;
PObj_custom_mark_SET(SELF);
PObj_custom_destroy_SET(SELF);
}
/*
=item C<PMC *clone()>
Create a copy of the filehandle.
=cut
*/
VTABLE PMC *clone() {
Parrot_FileHandle_attributes * const old_attrs
= PARROT_FILEHANDLE(SELF);
PMC * const copy = Parrot_pmc_new(interp, enum_class_FileHandle);
Parrot_FileHandle_attributes * const new_attrs
= PARROT_FILEHANDLE(copy);
/* Properly clone string data. */
new_attrs->filename = old_attrs->filename;
new_attrs->mode = old_attrs->mode;
new_attrs->encoding = old_attrs->encoding;
/* Prevent GC of the strings. */
Parrot_gc_mark_STRING_alive(INTERP, new_attrs->filename);
Parrot_gc_mark_STRING_alive(INTERP, new_attrs->mode);
Parrot_gc_mark_STRING_alive(INTERP, new_attrs->encoding);
/* Copy over some metadata */
new_attrs->flags = old_attrs->flags;
new_attrs->process_id = old_attrs->process_id;
new_attrs->exit_status = old_attrs->exit_status;
new_attrs->file_pos = old_attrs->file_pos;
new_attrs->last_pos = old_attrs->last_pos;
/* Duplicate the file handle. */
new_attrs->os_handle = Parrot_io_internal_dup(interp, old_attrs->os_handle);
/* TODO: Clone the buffers and copy flags/data */
return copy;
}
/*
=item C<void mark()>
Mark active filehandle data as live.
=cut
*/
VTABLE void mark() {
Parrot_FileHandle_attributes * const attrs
= PARROT_FILEHANDLE(SELF);
Parrot_gc_mark_STRING_alive(INTERP, attrs->mode);
Parrot_gc_mark_STRING_alive(INTERP, attrs->filename);
Parrot_gc_mark_STRING_alive(INTERP, attrs->encoding);
Parrot_gc_mark_STRING_alive(INTERP, attrs->record_separator);
Parrot_io_buffer_mark(interp, attrs->read_buffer);
Parrot_io_buffer_mark(interp, attrs->write_buffer);
}
/*
=item C<void destroy()>
Free structures.
=cut
*/
VTABLE void destroy() {
Parrot_io_close(INTERP, SELF, 1);
/* TODO: flush and free the buffers */
}
/*
=item C<INTVAL get_integer_keyed_int(INTVAL key)>
Shortcut to get the value of some attributes.
For internal usage only, subject to change without notice.
=cut
*/
VTABLE INTVAL get_integer_keyed_int(INTVAL key) {
INTVAL result;
switch (key) {
case 0:
GET_ATTR_process_id(INTERP, SELF, result);
break;
default:
result = 0;
}
return result;
}
/*
=item C<void set_integer_keyed_int(INTVAL key, INTVAL value)>
Shortcut to set the value of some attributes
For internal usage only, subject to change without notice.
=cut
*/
VTABLE void set_integer_keyed_int(INTVAL key, INTVAL value) {
switch (key) {
case 0:
SET_ATTR_process_id(INTERP, SELF, value);
break;
default:
break;
}
}
/*
=back
=head2 Methods
=over 4
=item C<METHOD open(STRING *filename :optional, STRING *mode :optional)>
Opens the file at the given filename (including path) with the given mode. The
invocant is modified and becomes an open filehandle. A copy of the invocant is
also returned by the method (some subclasses may create this as the primary
filehandle, rather than modifying the invocant).
Exceptions:
EXCEPTION_PIO_ERROR with the following messages:
Empty filename
"Cannot open FileHandle, no path"
Already open filehandle
"Cannot reopen already open FileHandle"
Invalid handle, no errno as with ISDIR:
"Unable to open filehandle from path '$path'"
Invalid handle (fd < 0), or other error:
"Unable to open filehandle from path '$path': $strerror($errno)"
EXCEPTION_INVALID_OPERATION with:
"Invalid mode for file open"
=cut
*/
METHOD open(STRING *filename :optional, INTVAL got_filename :opt_flag,
STRING *mode :optional, INTVAL got_mode :opt_flag) {
PMC *filehandle;
if (!Parrot_io_is_closed(INTERP, SELF))
Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_PIO_ERROR,
"Cannot reopen already open FileHandle");
if (!got_mode || STRING_IS_NULL(mode))
GET_ATTR_mode(INTERP, SELF, mode);
if (!got_filename || STRING_IS_NULL(filename))
GET_ATTR_filename(INTERP, SELF, filename);
filehandle = Parrot_io_open(INTERP, SELF, filename, mode);
RETURN(PMC *filehandle);
}
/*
=item C<METHOD isatty()>
Returns a boolean value indicating whether C<SELF> is a console/tty.
=cut
*/
METHOD isatty() {
const INTVAL isatty = Parrot_io_is_tty_handle(INTERP, SELF);
RETURN(INTVAL isatty);
}
/*
=item C<METHOD readline_interactive(STRING *prompt)>
Read a line from the filehandle and return it in a string.
=cut
*/
METHOD readline_interactive(STRING *prompt :optional,
INTVAL got_prompt :opt_flag) {
STRING *string_result = NULL;
#ifdef PARROT_HAS_READLINE
/* 4-column indent to get c_indent.t to DTRT */
char * const prompt_cstring =
(got_prompt ? Parrot_str_to_cstring(INTERP, prompt) : NULL);
char * const r = readline(prompt_cstring);
Parrot_str_free_cstring(prompt_cstring);
if (r) {
STRING *encoding;
const STR_VTABLE *enc = NULL;
GET_ATTR_encoding(INTERP, SELF, encoding);
if (*r)
add_history(r);
if (!STRING_IS_NULL(encoding))
enc = Parrot_find_encoding_by_string(INTERP, encoding);
if (enc == NULL)
string_result = Parrot_str_new(INTERP, r, 0);
else
string_result = Parrot_str_new_init(INTERP, r, strlen(r), enc, 0);
free(r);
}
#else
if (got_prompt) {
char * const prompt_cstring = Parrot_str_to_cstring(INTERP, prompt);
fprintf(stderr, "%s", prompt_cstring);
Parrot_str_free_cstring(prompt_cstring);
}
string_result = Parrot_io_readline(INTERP, SELF);
{
INTVAL len = STRING_length(string_result);
if (len == 0) {
string_result = NULL;
}
else {
while (len > 0) {
const INTVAL c = STRING_ord(interp, string_result, len - 1);
if (c != '\n' && c != '\r')
break;
--len;
}
string_result = STRING_substr(interp, string_result, 0, len);
}
}
#endif
if (string_result)
RETURN(STRING *string_result);
else
RETURN(PMC *PMCNULL);
}
/*
=item METHOD readall(STRING *name);
Read the entire contents of a file named I<name> into a Parrot string. On a
filehandle object that isn't opened yet, the path to a file can be passed to
C<readall> and it will open a filehandle on that file, read in the contents,
and close the filehandle.
.local pmc pio
pio = new 'FileHandle'
$S0 = pio.'readall'('the_file')
If the filehandle is already open, then no file path should be passed. The
C<readall> method will read the contents of the file, and will not close the
filehandle when finished.
pio = open 'the_file', 'r'
$S0 = pio.'readall'()
=cut
*/
METHOD readall(STRING *name :optional, INTVAL got_name :opt_flag) {
STRING *result;
if (got_name) {
/* called as class method - open, slurp, close file */
PMC *filehandle;
STRING *encoding;
GET_ATTR_encoding(INTERP, SELF, encoding);
if (!Parrot_io_is_closed(INTERP, SELF))
Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_PIO_ERROR,
"Cannot readall on a new file from an already open filehandle");
filehandle = Parrot_io_open(INTERP, PMCNULL, name, STRINGNULL);
SET_ATTR_encoding(INTERP, filehandle, encoding);
result = Parrot_io_readall_s(INTERP, filehandle);
Parrot_io_close(INTERP, filehandle, 1);
}
else
result = Parrot_io_readall_s(INTERP, SELF);
RETURN(STRING *result);
}
/*
=item C<METHOD flush()>
Flushes the filehandle.
=cut
*/
METHOD flush() {
Parrot_io_flush(INTERP, SELF);
}
/*
=item C<METHOD print([INTVAL|FLOATVAL|STRING *|PMC*] value)>
Print the passed in integer, number, string, or PMC to the filehandle.
(Integers, numbers, and strings are auto-boxed as PMCs.)
=cut
*/
METHOD print(PMC *to_print) {
STRING * const string_to_print = VTABLE_get_string(INTERP, to_print);
const INTVAL status = Parrot_io_write_s(INTERP, SELF, string_to_print);
RETURN(INTVAL status);
}
/*
=item C<METHOD buffer_type(STRING *new_type :optional)>
Set or retrieve the buffering behavior for the filehandle. The argument and
return value are one of the following:
=over
=item C<unbuffered>
Buffering disabled, bytes are sent as soon as possible.
=item C<line-buffered>
Line buffering, bytes are sent when a record separator is encountered.
=item C<full-buffered>
Full buffering, bytes are sent when the buffer is full.
=cut
*/
METHOD buffer_type(STRING *new_type :optional, INTVAL got_type :opt_flag) {
if (got_type)
Parrot_io_set_buffer_mode(INTERP, SELF, new_type);
else {
STRING * type_str = Parrot_io_get_buffer_mode(INTERP, SELF);
RETURN(STRING *type_str);
}
}
/*
=item C<METHOD buffer_size(INTVAL new_size :optional)>
Set or retrieve the buffer size for the filehandle.
=cut
*/
METHOD buffer_size(INTVAL new_size :optional, INTVAL got_size :opt_flag) {
INTVAL buffer_size = Parrot_io_buffer_size(INTERP, SELF, new_size, got_size);
RETURN(INTVAL buffer_size);
}
/*
=item C<METHOD mode()>
Retrieve the read mode string for the filehandle.
=cut
*/
METHOD mode() {
STRING *mode;
GET_ATTR_mode(INTERP, SELF, mode);
RETURN(STRING *mode);
}
/*
=item C<METHOD handle()>
Returns the INTVAL used by the OS to identify this filehandle.
=cut
*/
METHOD handle() {
INTVAL handle;
GET_ATTR_os_handle(INTERP, SELF, handle);
RETURN(INTVAL handle);
}
/*
=item C<METHOD exit_status()>
If this is a pipe, return the exit status of the child process.
=cut
*/
METHOD exit_status() {
INTVAL exit_status;
GET_ATTR_exit_status(INTERP, SELF, exit_status);
RETURN(INTVAL exit_status);
}
/*
=item C<METHOD tell()>
Get the file position of the stream. 2 C<INTVAL>s are returned. The first is
the position. The second is the position shifted down by 32 bits to handle
overflows on 32-bit systems.
=cut
*/
METHOD tell() {
PIOOFF_T pos;
GET_ATTR_file_pos(INTERP, SELF, pos);
RETURN(INTVAL pos, INTVAL pos >> 32);
}
/*
=item C<METHOD seek(INTVAL whence, INTVAL offs, INTVAL offs_overflow)>
Set the file position to an offset specified by C<offs> (and optionally
C<offs_overflow>). C<whence> determines from where in the file the offset is
taken.
Whence Value Meaning
0 Seek from the beginning of the file
1 Seek from the current position
2 Seek from the end of the file
C<offs_overflow> is optional and is used to handle offsets higher than 2Gb on
32bit systems.
=cut
*/
METHOD seek(INTVAL whence, INTVAL offs, INTVAL offs_overflow :optional,
int has_overflow :opt_flag) {
const PIOOFF_T pio_offs = has_overflow ?
Parrot_io_make_offset32(offs_overflow, offs) :
offs;
const INTVAL status = Parrot_io_seek(INTERP, SELF, pio_offs, whence);
if (0 > status)
Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_PIO_ERROR,
"seek failed: %d", status);
}
/*
=item C<METHOD peek()>
Returns the next byte from the stream, but does not remove it.
=cut
*/
METHOD peek() {
STRING * const s = Parrot_io_peek(INTERP, SELF);
RETURN(STRING* s);
}
/*
=item C<METHOD setasync()>
=cut
*/
METHOD setasync() {
/* Parrot_io_async(INTERP, SELF, 1); */
}
/*
=item C<METHOD setblocking()>
=cut
*/
METHOD setblocking() {
/* Parrot_io_async(INTERP, SELF, 0); */
}
/*
=back
=cut
*/
} /* end pmclass */
/*
* Local variables:
* c-file-style: "parrot"
* End:
* vim: expandtab shiftwidth=4 cinoptions='\:2=2' :
*/
Jump to Line
Something went wrong with that request. Please try again.