<commit_before>/* io_unix.c
 *  Copyright: (When this is determined...it will go here)
 *  CVS Info
 *      $Id$
 *  Overview:
 *      This is the Parrot IO UNIX layer. May be changed to
 *      include other platforms if that platform is similar 
 *      enough to keep from smudging, else implement seperate layer.
 *      For UNIX systems this is the low level OS layer (unbuffered).
 *  Data Structure and Algorithms:
 *  History:
 *      Initially written by <NAME> (<EMAIL>)
 *  Notes:
 *  References:
 *      APitUE - <NAME>, AT&T SFIO, Perl5 (<NAME>)
 */

#include "parrot/parrot.h"

#ifdef PIO_OS_UNIX

/* Defined at bottom */
extern ParrotIOLayerAPI pio_unix_layer_api;

ParrotIOLayer pio_unix_layer = {
    NULL,
    "unix",
    PIO_L_TERMINAL,
    &pio_unix_layer_api,
    0, 0
};


/*
 * Currently keeping layer prototypes local to each layer
 * file.
 */
INTVAL flags_to_unix(INTVAL flags);

INTVAL PIO_unix_init(theINTERP, ParrotIOLayer *layer);
ParrotIO *PIO_unix_open(theINTERP, ParrotIOLayer *layer,
                        const char *spath, INTVAL flags);
ParrotIO *PIO_unix_fdopen(theINTERP, ParrotIOLayer *layer,
                          PIOHANDLE fd, INTVAL flags);
INTVAL PIO_unix_close(theINTERP, ParrotIOLayer *layer, ParrotIO *io);
void PIO_unix_flush(theINTERP, ParrotIOLayer *layer, ParrotIO *io);
size_t PIO_unix_read(theINTERP, ParrotIOLayer *layer,
                     ParrotIO *io, void *buffer, size_t len);
size_t PIO_unix_write(theINTERP, ParrotIOLayer *layer,
                      ParrotIO *io, const void *buffer, size_t len);
INTVAL PIO_unix_puts(theINTERP, ParrotIOLayer *l, ParrotIO *io, const char *s);
INTVAL PIO_unix_seek(theINTERP, ParrotIOLayer *l, ParrotIO *io,
                     INTVAL hi, INTVAL lo, INTVAL whence);
PIOOFF_T PIO_unix_tell(theINTERP, ParrotIOLayer *l, ParrotIO *io);


INTVAL
flags_to_unix(INTVAL flags)
{
    INTVAL oflags;
    oflags = 0;
    if ((flags & (PIO_F_WRITE | PIO_F_READ)) == (PIO_F_WRITE | PIO_F_READ)) {
        oflags |= O_RDWR | O_CREAT;
    }
    else if (flags & PIO_F_WRITE) {
        oflags |= O_WRONLY | O_CREAT;
    }
    else if (flags & PIO_F_READ) {
        oflags |= O_RDONLY;
    }

    if (flags & PIO_F_APPEND) {
        oflags |= O_APPEND;
    }
    else if (flags & PIO_F_TRUNC) {
        oflags |= O_TRUNC;
    }
    return oflags;
}



/*
 * Setup standard streams, etc.
 */
INTVAL
PIO_unix_init(theINTERP, ParrotIOLayer *layer)
{
    ParrotIOData *d = GET_INTERP_IOD(interpreter);
    if (d != NULL && d->table != NULL) {
        if ((PIO_STDIN(interpreter) =
             PIO_unix_fdopen(interpreter, layer, STDIN_FILENO, PIO_F_READ))
            && (PIO_STDOUT(interpreter) =
                PIO_unix_fdopen(interpreter, layer, STDOUT_FILENO,
                                PIO_F_WRITE))
            && (PIO_STDERR(interpreter) =
                PIO_unix_fdopen(interpreter, layer, STDERR_FILENO,
                                PIO_F_WRITE))
            )
            return 0;
    }
    return -1;
}


/*
 * Open modes (read, write, append, etc.) are done in pseudo-Perl
 * style using <, >, etc.
 */
ParrotIO *
PIO_unix_open(theINTERP, ParrotIOLayer *layer,
              const char *spath, INTVAL flags)
{
    ParrotIO *io;
    INTVAL mode;
    INTVAL oflags, type;
    PIOHANDLE fd;
    type = PIO_TYPE_FILE;
    mode = DEFAULT_OPEN_MODE;

    if ((flags & (PIO_F_WRITE | PIO_F_READ)) == 0)
        return NULL;

    oflags = flags_to_unix(flags);

    /* Only files for now */
    flags |= PIO_F_FILE;

    /* Try open with no create first */
    while ((fd = open(spath, oflags & (O_WRONLY | O_RDWR), mode)) < 0
           && errno == EINTR)
        errno = 0;

    /* File open */
    if (fd >= 0) {
        /*
         * Now check if we specified O_CREAT|O_EXCL or not.
         * If so, we must return NULL, else either use the
         * descriptor or create the file.
         */
        if ((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) {
            close(fd);
            return NULL;
        }
        /*
         * Check for truncate?
         */
        if (oflags & O_TRUNC) {
            int tfd;
            while ((tfd = creat(spath, PIO_DEFAULTMODE)) < 0 && errno == EINTR)
                errno = 0;
            close(tfd);
        }
    }
    else if (oflags & O_CREAT) {
        /* O_CREAT and file doesn't exist. */
        while ((fd = creat(spath, PIO_DEFAULTMODE)) < 0 && errno == EINTR)
            errno = 0;
        if (!(oflags & O_WRONLY)) {
            close(fd);
            /*
             * File created, reopen with read+write
             */
            while ((fd = open(spath, oflags & (O_WRONLY | O_RDWR),
                              mode)) < 0 && errno == EINTR)
                errno = 0;
        }
    }
    else {
        /* File doesn't exist and O_CREAT not specified */
    }

    if (fd >= 0) {
        /* Set generic flag here if is a terminal then
         * higher layers can know how to setup buffering.
         * STDIN, STDOUT, STDERR would be in this case
         * so we would setup linebuffering.
         */
        if (PIO_unix_isatty(fd))
            flags |= PIO_F_CONSOLE;
        io = PIO_new(interpreter, NULL, type, flags, mode);
        io->fd = fd;
        return io;
    }
    return NULL;
}


ParrotIO *
PIO_unix_fdopen(theINTERP, ParrotIOLayer *layer, PIOHANDLE fd, INTVAL flags)
{
    ParrotIO *io;
    INTVAL oflags, mode;
#  ifdef HAS_HEADER_FCNTL
    INTVAL rflags;
#  endif
    mode = 0;

    oflags = flags_to_unix(flags);
    UNUSED(oflags)

        /* FIXME - Check file handle flags, validity */
#  ifdef HAS_HEADER_FCNTL
        /* Get descriptor flags */
        if ((rflags = fcntl(fd, F_GETFL, 0)) >= 0) {
        /*int accmode = rflags & O_ACCMODE; */
        /* Check other flags (APPEND, ASYNC, etc) */
    }
    else {
        /* Probably invalid descriptor */
        return NULL;
    }
#  endif

    if (PIO_unix_isatty(fd))
        flags |= PIO_F_CONSOLE;
    io = PIO_new(interpreter, NULL, PIO_F_FILE, flags, mode);
    io->fd = fd;
    return io;
}


INTVAL
PIO_unix_close(theINTERP, ParrotIOLayer *layer, ParrotIO *io)
{
    if (io->fd >= 0)
        close(io->fd);
    io->fd = -1;
    return 0;
}


INTVAL
PIO_unix_isatty(PIOHANDLE fd)
{
    return isatty(fd);
}

/*
 * Various ways of determining block size. If passed a descriptor
 * we can use fstat() and the stat buf if available, or the BLKSIZE
 * constant if available at compile time.
 */
INTVAL
PIO_unix_getblksize(PIOHANDLE fd)
{
    if (fd >= 0) {
        /* Try to get the block size of a regular file */
#  if 0
        /*
         * Is it even worth adding non-portable code here
         * or should we just estimate a nice buffer size?
         * Some systems have st_blksize, some don't.
         */
        {
            struct stat sbuf;
            int err;
            err = fstat(fd, &sbuf);
            if (err == 0) {
                return sbuf.st_blksize;
            }
        }
#  endif
    }
    /* Try to determine it from general means. */
#  ifdef BLKSIZE
    return BLKSIZE;
#  else
    return PIO_BLKSIZE;
#  endif
}

/* At lowest layer all we can do for flush is ask kernel to sync().
 */
void
PIO_unix_flush(theINTERP, ParrotIOLayer *layer, ParrotIO *io)
{
#  if 0
    fsync(io->fd);
#  endif
}


size_t
PIO_unix_read(theINTERP, ParrotIOLayer *layer, ParrotIO *io,
              void *buffer, size_t len)
{
    int bytes;

    UNUSED(interpreter);
    UNUSED(layer);

    for (;;) {
        bytes = read(io->fd, buffer, len);
        if (bytes > 0)
            return bytes;
        else if (bytes < 0) {
            switch (errno) {
            case EINTR:
                continue;
            default:
                return bytes;
            }
        }
        else {
            /* Read returned 0, EOF if len requested > 0 */
            if (len > 0)
                io->flags |= PIO_F_EOF;
            return bytes;
        }
    }
    return bytes;
}


size_t
PIO_unix_write(theINTERP, ParrotIOLayer *layer, ParrotIO *io,
               const void *buffer, size_t len)
{
    int err;<commit_msg>Comment out never-reached "return" line.  It's unclear what the intent was in the first place.<commit_after>/* io_unix.c
 *  Copyright: (When this is determined...it will go here)
 *  CVS Info
 *      $Id$
 *  Overview:
 *      This is the Parrot IO UNIX layer. May be changed to
 *      include other platforms if that platform is similar 
 *      enough to keep from smudging, else implement seperate layer.
 *      For UNIX systems this is the low level OS layer (unbuffered).
 *  Data Structure and Algorithms:
 *  History:
 *      Initially written by <NAME> (<EMAIL>)
 *  Notes:
 *  References:
 *      APitUE - <NAME>, AT&T SFIO, Perl5 (<NAME>)
 */

#include "parrot/parrot.h"

#ifdef PIO_OS_UNIX

/* Defined at bottom */
extern ParrotIOLayerAPI pio_unix_layer_api;

ParrotIOLayer pio_unix_layer = {
    NULL,
    "unix",
    PIO_L_TERMINAL,
    &pio_unix_layer_api,
    0, 0
};


/*
 * Currently keeping layer prototypes local to each layer
 * file.
 */
INTVAL flags_to_unix(INTVAL flags);

INTVAL PIO_unix_init(theINTERP, ParrotIOLayer *layer);
ParrotIO *PIO_unix_open(theINTERP, ParrotIOLayer *layer,
                        const char *spath, INTVAL flags);
ParrotIO *PIO_unix_fdopen(theINTERP, ParrotIOLayer *layer,
                          PIOHANDLE fd, INTVAL flags);
INTVAL PIO_unix_close(theINTERP, ParrotIOLayer *layer, ParrotIO *io);
void PIO_unix_flush(theINTERP, ParrotIOLayer *layer, ParrotIO *io);
size_t PIO_unix_read(theINTERP, ParrotIOLayer *layer,
                     ParrotIO *io, void *buffer, size_t len);
size_t PIO_unix_write(theINTERP, ParrotIOLayer *layer,
                      ParrotIO *io, const void *buffer, size_t len);
INTVAL PIO_unix_puts(theINTERP, ParrotIOLayer *l, ParrotIO *io, const char *s);
INTVAL PIO_unix_seek(theINTERP, ParrotIOLayer *l, ParrotIO *io,
                     INTVAL hi, INTVAL lo, INTVAL whence);
PIOOFF_T PIO_unix_tell(theINTERP, ParrotIOLayer *l, ParrotIO *io);


INTVAL
flags_to_unix(INTVAL flags)
{
    INTVAL oflags;
    oflags = 0;
    if ((flags & (PIO_F_WRITE | PIO_F_READ)) == (PIO_F_WRITE | PIO_F_READ)) {
        oflags |= O_RDWR | O_CREAT;
    }
    else if (flags & PIO_F_WRITE) {
        oflags |= O_WRONLY | O_CREAT;
    }
    else if (flags & PIO_F_READ) {
        oflags |= O_RDONLY;
    }

    if (flags & PIO_F_APPEND) {
        oflags |= O_APPEND;
    }
    else if (flags & PIO_F_TRUNC) {
        oflags |= O_TRUNC;
    }
    return oflags;
}



/*
 * Setup standard streams, etc.
 */
INTVAL
PIO_unix_init(theINTERP, ParrotIOLayer *layer)
{
    ParrotIOData *d = GET_INTERP_IOD(interpreter);
    if (d != NULL && d->table != NULL) {
        if ((PIO_STDIN(interpreter) =
             PIO_unix_fdopen(interpreter, layer, STDIN_FILENO, PIO_F_READ))
            && (PIO_STDOUT(interpreter) =
                PIO_unix_fdopen(interpreter, layer, STDOUT_FILENO,
                                PIO_F_WRITE))
            && (PIO_STDERR(interpreter) =
                PIO_unix_fdopen(interpreter, layer, STDERR_FILENO,
                                PIO_F_WRITE))
            )
            return 0;
    }
    return -1;
}


/*
 * Open modes (read, write, append, etc.) are done in pseudo-Perl
 * style using <, >, etc.
 */
ParrotIO *
PIO_unix_open(theINTERP, ParrotIOLayer *layer,
              const char *spath, INTVAL flags)
{
    ParrotIO *io;
    INTVAL mode;
    INTVAL oflags, type;
    PIOHANDLE fd;
    type = PIO_TYPE_FILE;
    mode = DEFAULT_OPEN_MODE;

    if ((flags & (PIO_F_WRITE | PIO_F_READ)) == 0)
        return NULL;

    oflags = flags_to_unix(flags);

    /* Only files for now */
    flags |= PIO_F_FILE;

    /* Try open with no create first */
    while ((fd = open(spath, oflags & (O_WRONLY | O_RDWR), mode)) < 0
           && errno == EINTR)
        errno = 0;

    /* File open */
    if (fd >= 0) {
        /*
         * Now check if we specified O_CREAT|O_EXCL or not.
         * If so, we must return NULL, else either use the
         * descriptor or create the file.
         */
        if ((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) {
            close(fd);
            return NULL;
        }
        /*
         * Check for truncate?
         */
        if (oflags & O_TRUNC) {
            int tfd;
            while ((tfd = creat(spath, PIO_DEFAULTMODE)) < 0 && errno == EINTR)
                errno = 0;
            close(tfd);
        }
    }
    else if (oflags & O_CREAT) {
        /* O_CREAT and file doesn't exist. */
        while ((fd = creat(spath, PIO_DEFAULTMODE)) < 0 && errno == EINTR)
            errno = 0;
        if (!(oflags & O_WRONLY)) {
            close(fd);
            /*
             * File created, reopen with read+write
             */
            while ((fd = open(spath, oflags & (O_WRONLY | O_RDWR),
                              mode)) < 0 && errno == EINTR)
                errno = 0;
        }
    }
    else {
        /* File doesn't exist and O_CREAT not specified */
    }

    if (fd >= 0) {
        /* Set generic flag here if is a terminal then
         * higher layers can know how to setup buffering.
         * STDIN, STDOUT, STDERR would be in this case
         * so we would setup linebuffering.
         */
        if (PIO_unix_isatty(fd))
            flags |= PIO_F_CONSOLE;
        io = PIO_new(interpreter, NULL, type, flags, mode);
        io->fd = fd;
        return io;
    }
    return NULL;
}


ParrotIO *
PIO_unix_fdopen(theINTERP, ParrotIOLayer *layer, PIOHANDLE fd, INTVAL flags)
{
    ParrotIO *io;
    INTVAL oflags, mode;
#  ifdef HAS_HEADER_FCNTL
    INTVAL rflags;
#  endif
    mode = 0;

    oflags = flags_to_unix(flags);
    UNUSED(oflags)

        /* FIXME - Check file handle flags, validity */
#  ifdef HAS_HEADER_FCNTL
        /* Get descriptor flags */
        if ((rflags = fcntl(fd, F_GETFL, 0)) >= 0) {
        /*int accmode = rflags & O_ACCMODE; */
        /* Check other flags (APPEND, ASYNC, etc) */
    }
    else {
        /* Probably invalid descriptor */
        return NULL;
    }
#  endif

    if (PIO_unix_isatty(fd))
        flags |= PIO_F_CONSOLE;
    io = PIO_new(interpreter, NULL, PIO_F_FILE, flags, mode);
    io->fd = fd;
    return io;
}


INTVAL
PIO_unix_close(theINTERP, ParrotIOLayer *layer, ParrotIO *io)
{
    if (io->fd >= 0)
        close(io->fd);
    io->fd = -1;
    return 0;
}


INTVAL
PIO_unix_isatty(PIOHANDLE fd)
{
    return isatty(fd);
}

/*
 * Various ways of determining block size. If passed a descriptor
 * we can use fstat() and the stat buf if available, or the BLKSIZE
 * constant if available at compile time.
 */
INTVAL
PIO_unix_getblksize(PIOHANDLE fd)
{
    if (fd >= 0) {
        /* Try to get the block size of a regular file */
#  if 0
        /*
         * Is it even worth adding non-portable code here
         * or should we just estimate a nice buffer size?
         * Some systems have st_blksize, some don't.
         */
        {
            struct stat sbuf;
            int err;
            err = fstat(fd, &sbuf);
            if (err == 0) {
                return sbuf.st_blksize;
            }
        }
#  endif
    }
    /* Try to determine it from general means. */
#  ifdef BLKSIZE
    return BLKSIZE;
#  else
    return PIO_BLKSIZE;
#  endif
}

/* At lowest layer all we can do for flush is ask kernel to sync().
 */
void
PIO_unix_flush(theINTERP, ParrotIOLayer *layer, ParrotIO *io)
{
#  if 0
    fsync(io->fd);
#  endif
}


size_t
PIO_unix_read(theINTERP, ParrotIOLayer *layer, ParrotIO *io,
              void *buffer, size_t len)
{
    int bytes;

    UNUSED(interpreter);
    UNUSED(layer);

    for (;;) {
        bytes = read(io->fd, buffer, len);
        if (bytes > 0)
            return bytes;
        else if (bytes < 0) {
            switch (errno) {
            case EINTR:
                continue;
            default:
                return bytes;
            }
        }
        else {
            /* Read returned 0, EOF if len requested > 0 */
            if (len > 0)
                io->flags |= PIO_F_EOF;
            return bytes;
        }
    }
    /* return bytes; */ /* XXX never reached -- why was it here? */
}


size_t
PIO_unix_write(theINTERP, ParrotIOLayer *layer, ParrotIO *io,
               const void *buffer, size_t len)
{
    int err;
{'fraction_non_alphanumeric': 0.10575570371276531, 'fraction_numerical': 0.0039146125145268825, 'mean_word_length': 1.9635671560630779, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 47, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4386374', 'n_tokens_mistral': 5864, 'n_tokens_neox': 5682, 'n_words': 1538}