#include "config.h"
#include <stdlib.h>
#include <limits.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>
#include <string.h>
#include <errno.h>
#include "libgfortran.h"
#include "io.h"
#include "unix.h"
#ifndef SSIZE_MAX
#define SSIZE_MAX SHRT_MAX
#endif
#ifndef PATH_MAX
#define PATH_MAX 1024
#endif
#ifndef PROT_READ
#define PROT_READ 1
#endif
#ifndef PROT_WRITE
#define PROT_WRITE 2
#endif
#ifndef S_IRGRP
#define S_IRGRP 0
#endif
#ifndef S_IWGRP
#define S_IWGRP 0
#endif
#ifndef S_IROTH
#define S_IROTH 0
#endif
#ifndef S_IWOTH
#define S_IWOTH 0
#endif
int
move_pos_offset (stream* st, int pos_off)
{
unix_stream * str = (unix_stream*)st;
if (pos_off < 0)
{
str->logical_offset += pos_off;
if (str->dirty_offset + str->ndirty > str->logical_offset)
{
if (str->ndirty + pos_off > 0)
str->ndirty += pos_off;
else
{
str->dirty_offset += pos_off + pos_off;
str->ndirty = 0;
}
}
return pos_off;
}
return 0;
}
static int
fix_fd (int fd)
{
int input, output, error;
input = output = error = 0;
if (fd == STDIN_FILENO)
{
fd = dup (fd);
input = 1;
}
if (fd == STDOUT_FILENO)
{
fd = dup (fd);
output = 1;
}
if (fd == STDERR_FILENO)
{
fd = dup (fd);
error = 1;
}
if (input)
close (STDIN_FILENO);
if (output)
close (STDOUT_FILENO);
if (error)
close (STDERR_FILENO);
return fd;
}
int
is_preconnected (stream * s)
{
int fd;
fd = ((unix_stream *) s)->fd;
if (fd == STDIN_FILENO || fd == STDOUT_FILENO || fd == STDERR_FILENO)
return 1;
else
return 0;
}
void
flush_if_preconnected (stream * s)
{
int fd;
fd = ((unix_stream *) s)->fd;
if (fd == STDIN_FILENO)
fflush (stdin);
else if (fd == STDOUT_FILENO)
fflush (stdout);
else if (fd == STDERR_FILENO)
fflush (stderr);
}
inline static void
reset_stream (unix_stream * s, size_t bytes_rw)
{
s->physical_offset += bytes_rw;
s->logical_offset = s->physical_offset;
if (s->file_length != -1 && s->physical_offset > s->file_length)
s->file_length = s->physical_offset;
}
static int
do_read (unix_stream * s, void * buf, size_t * nbytes)
{
ssize_t trans;
size_t bytes_left;
char *buf_st;
int status;
status = 0;
bytes_left = *nbytes;
buf_st = (char *) buf;
while (bytes_left > 0)
{
trans = (bytes_left < SSIZE_MAX) ? bytes_left : SSIZE_MAX;
trans = read (s->fd, buf_st, trans);
if (trans < 0)
{
if (errno == EINTR)
continue;
else
{
status = errno;
break;
}
}
else if (trans == 0)
break;
buf_st += trans;
bytes_left -= trans;
}
*nbytes -= bytes_left;
return status;
}
static int
do_write (unix_stream * s, const void * buf, size_t * nbytes)
{
ssize_t trans;
size_t bytes_left;
char *buf_st;
int status;
status = 0;
bytes_left = *nbytes;
buf_st = (char *) buf;
while (bytes_left > 0)
{
trans = (bytes_left < SSIZE_MAX) ? bytes_left : SSIZE_MAX;
trans = write (s->fd, buf_st, trans);
if (trans < 0)
{
if (errno == EINTR)
continue;
else
{
status = errno;
break;
}
}
buf_st += trans;
bytes_left -= trans;
}
*nbytes -= bytes_left;
return status;
}
const char *
get_oserror (void)
{
return strerror (errno);
}
void
sys_exit (int code)
{
exit (code);
}
static try
fd_flush (unix_stream * s)
{
size_t writelen;
if (s->ndirty == 0)
return SUCCESS;
if (s->file_length != -1 && s->physical_offset != s->dirty_offset &&
lseek (s->fd, s->dirty_offset, SEEK_SET) < 0)
return FAILURE;
writelen = s->ndirty;
if (do_write (s, s->buffer + (s->dirty_offset - s->buffer_offset),
&writelen) != 0)
return FAILURE;
s->physical_offset = s->dirty_offset + writelen;
if (s->file_length != -1 && s->physical_offset > s->file_length)
s->file_length = s->physical_offset;
s->ndirty -= writelen;
if (s->ndirty != 0)
return FAILURE;
return SUCCESS;
}
static void
fd_alloc (unix_stream * s, gfc_offset where,
int *len __attribute__ ((unused)))
{
char *new_buffer;
int n, read_len;
if (*len <= BUFFER_SIZE)
{
new_buffer = s->small_buffer;
read_len = BUFFER_SIZE;
}
else
{
new_buffer = get_mem (*len);
read_len = *len;
}
if (s->buffer != NULL && s->buffer_offset <= where &&
where <= s->buffer_offset + s->active)
{
n = s->active - (where - s->buffer_offset);
memmove (new_buffer, s->buffer + (where - s->buffer_offset), n);
s->active = n;
}
else
{
s->active = 0;
}
s->buffer_offset = where;
if (s->buffer != NULL && s->buffer != s->small_buffer)
free_mem (s->buffer);
s->buffer = new_buffer;
s->len = read_len;
}
static char *
fd_alloc_r_at (unix_stream * s, int *len, gfc_offset where)
{
gfc_offset m;
if (where == -1)
where = s->logical_offset;
if (s->buffer != NULL && s->buffer_offset <= where &&
where + *len <= s->buffer_offset + s->active)
{
s->logical_offset = where + *len;
return s->buffer + where - s->buffer_offset;
}
fd_alloc (s, where, len);
m = where + s->active;
if (s->physical_offset != m && lseek (s->fd, m, SEEK_SET) < 0)
return NULL;
if (s->special_file)
{
ssize_t n;
n = read (s->fd, s->buffer + s->active, s->len - s->active);
if (n < 0)
return NULL;
s->physical_offset = m + n;
s->active += n;
}
else
{
size_t n;
n = s->len - s->active;
if (do_read (s, s->buffer + s->active, &n) != 0)
return NULL;
s->physical_offset = m + n;
s->active += n;
}
if (s->active < *len)
*len = s->active;
s->logical_offset = where + *len;
return s->buffer;
}
static char *
fd_alloc_w_at (unix_stream * s, int *len, gfc_offset where)
{
gfc_offset n;
if (where == -1)
where = s->logical_offset;
if (s->buffer == NULL || s->buffer_offset > where ||
where + *len > s->buffer_offset + s->len)
{
if (fd_flush (s) == FAILURE)
return NULL;
fd_alloc (s, where, len);
}
if (s->ndirty == 0
|| where > s->dirty_offset + s->ndirty
|| s->dirty_offset > where + *len)
{
if (s->ndirty != 0)
fd_flush (s);
s->dirty_offset = where;
s->ndirty = *len;
}
else
{
gfc_offset start;
if (where < s->dirty_offset)
start = where;
else
start = s->dirty_offset;
if (where + *len > s->dirty_offset + s->ndirty)
s->ndirty = where + *len - start;
else
s->ndirty = s->dirty_offset + s->ndirty - start;
s->dirty_offset = start;
}
s->logical_offset = where + *len;
if (s->file_length != -1 && s->logical_offset > s->file_length)
s->file_length = s->logical_offset;
n = s->logical_offset - s->buffer_offset;
if (n > s->active)
s->active = n;
return s->buffer + where - s->buffer_offset;
}
static try
fd_sfree (unix_stream * s)
{
if (s->ndirty != 0 &&
(s->buffer != s->small_buffer || options.all_unbuffered ||
s->unbuffered))
return fd_flush (s);
return SUCCESS;
}
static try
fd_seek (unix_stream * s, gfc_offset offset)
{
if (s->file_length == -1)
return SUCCESS;
if (s->physical_offset == offset)
{
s->logical_offset = offset;
return SUCCESS;
}
s->physical_offset = s->logical_offset = offset;
s->active = 0;
return (lseek (s->fd, offset, SEEK_SET) < 0) ? FAILURE : SUCCESS;
}
static try
fd_truncate (unix_stream * s)
{
if (lseek (s->fd, s->logical_offset, SEEK_SET) == -1)
{
if (errno == ESPIPE)
return SUCCESS;
else
return FAILURE;
}
#ifdef HAVE_FTRUNCATE
if (s->special_file || ftruncate (s->fd, s->logical_offset))
#else
#ifdef HAVE_CHSIZE
if (s->special_file || chsize (s->fd, s->logical_offset))
#endif
#endif
{
s->physical_offset = s->file_length = 0;
return SUCCESS;
}
s->physical_offset = s->file_length = s->logical_offset;
s->active = 0;
return SUCCESS;
}
static try
fd_sset (unix_stream * s, int c, size_t n)
{
size_t bytes_left;
int trans;
void *p;
bytes_left = n;
while (bytes_left > 0)
{
trans = (bytes_left < BUFFER_SIZE) ? bytes_left : BUFFER_SIZE;
p = fd_alloc_w_at (s, &trans, -1);
if (p)
memset (p, c, trans);
else
return FAILURE;
bytes_left -= trans;
}
return SUCCESS;
}
static int
fd_read (unix_stream * s, void * buf, size_t * nbytes)
{
void *p;
int tmp, status;
if (*nbytes < BUFFER_SIZE && !s->unbuffered)
{
tmp = *nbytes;
p = fd_alloc_r_at (s, &tmp, -1);
if (p)
{
*nbytes = tmp;
memcpy (buf, p, *nbytes);
return 0;
}
else
{
*nbytes = 0;
return errno;
}
}
if (fd_flush (s) == FAILURE)
{
*nbytes = 0;
return errno;
}
if (is_seekable ((stream *) s) && fd_seek (s, s->logical_offset) == FAILURE)
{
*nbytes = 0;
return errno;
}
status = do_read (s, buf, nbytes);
reset_stream (s, *nbytes);
return status;
}
static int
fd_write (unix_stream * s, const void * buf, size_t * nbytes)
{
void *p;
int tmp, status;
if (*nbytes < BUFFER_SIZE && !s->unbuffered)
{
tmp = *nbytes;
p = fd_alloc_w_at (s, &tmp, -1);
if (p)
{
*nbytes = tmp;
memcpy (p, buf, *nbytes);
return 0;
}
else
{
*nbytes = 0;
return errno;
}
}
if (fd_flush (s) == FAILURE)
{
*nbytes = 0;
return errno;
}
if (is_seekable ((stream *) s) && fd_seek (s, s->logical_offset) == FAILURE)
{
*nbytes = 0;
return errno;
}
status = do_write (s, buf, nbytes);
reset_stream (s, *nbytes);
return status;
}
static try
fd_close (unix_stream * s)
{
if (fd_flush (s) == FAILURE)
return FAILURE;
if (s->buffer != NULL && s->buffer != s->small_buffer)
free_mem (s->buffer);
if (s->fd != STDOUT_FILENO && s->fd != STDERR_FILENO)
{
if (close (s->fd) < 0)
return FAILURE;
}
free_mem (s);
return SUCCESS;
}
static void
fd_open (unix_stream * s)
{
if (isatty (s->fd))
s->unbuffered = 1;
s->st.alloc_r_at = (void *) fd_alloc_r_at;
s->st.alloc_w_at = (void *) fd_alloc_w_at;
s->st.sfree = (void *) fd_sfree;
s->st.close = (void *) fd_close;
s->st.seek = (void *) fd_seek;
s->st.truncate = (void *) fd_truncate;
s->st.read = (void *) fd_read;
s->st.write = (void *) fd_write;
s->st.set = (void *) fd_sset;
s->buffer = NULL;
}
static char *
mem_alloc_r_at (unix_stream * s, int *len, gfc_offset where)
{
gfc_offset n;
if (where == -1)
where = s->logical_offset;
if (where < s->buffer_offset || where > s->buffer_offset + s->active)
return NULL;
s->logical_offset = where + *len;
n = s->buffer_offset + s->active - where;
if (*len > n)
*len = n;
return s->buffer + (where - s->buffer_offset);
}
static char *
mem_alloc_w_at (unix_stream * s, int *len, gfc_offset where)
{
gfc_offset m;
assert (*len >= 0);
if (where == -1)
where = s->logical_offset;
m = where + *len;
if (where < s->buffer_offset)
return NULL;
if (m > s->file_length)
return NULL;
s->logical_offset = m;
return s->buffer + (where - s->buffer_offset);
}
static int
mem_read (unix_stream * s, void * buf, size_t * nbytes)
{
void *p;
int tmp;
tmp = *nbytes;
p = mem_alloc_r_at (s, &tmp, -1);
if (p)
{
*nbytes = tmp;
memcpy (buf, p, *nbytes);
return 0;
}
else
{
*nbytes = 0;
return errno;
}
}
static int
mem_write (unix_stream * s, const void * buf, size_t * nbytes)
{
void *p;
int tmp;
errno = 0;
tmp = *nbytes;
p = mem_alloc_w_at (s, &tmp, -1);
if (p)
{
*nbytes = tmp;
memcpy (p, buf, *nbytes);
return 0;
}
else
{
*nbytes = 0;
return errno;
}
}
static int
mem_seek (unix_stream * s, gfc_offset offset)
{
if (offset > s->file_length)
{
errno = ESPIPE;
return FAILURE;
}
s->logical_offset = offset;
return SUCCESS;
}
static try
mem_set (unix_stream * s, int c, size_t n)
{
void *p;
int len;
len = n;
p = mem_alloc_w_at (s, &len, -1);
if (p)
{
memset (p, c, len);
return SUCCESS;
}
else
return FAILURE;
}
static int
mem_truncate (unix_stream * s __attribute__ ((unused)))
{
return SUCCESS;
}
static try
mem_close (unix_stream * s)
{
if (s != NULL)
free_mem (s);
return SUCCESS;
}
static try
mem_sfree (unix_stream * s __attribute__ ((unused)))
{
return SUCCESS;
}
void
empty_internal_buffer(stream *strm)
{
unix_stream * s = (unix_stream *) strm;
memset(s->buffer, ' ', s->file_length);
}
stream *
open_internal (char *base, int length)
{
unix_stream *s;
s = get_mem (sizeof (unix_stream));
memset (s, '\0', sizeof (unix_stream));
s->buffer = base;
s->buffer_offset = 0;
s->logical_offset = 0;
s->active = s->file_length = length;
s->st.alloc_r_at = (void *) mem_alloc_r_at;
s->st.alloc_w_at = (void *) mem_alloc_w_at;
s->st.sfree = (void *) mem_sfree;
s->st.close = (void *) mem_close;
s->st.seek = (void *) mem_seek;
s->st.truncate = (void *) mem_truncate;
s->st.read = (void *) mem_read;
s->st.write = (void *) mem_write;
s->st.set = (void *) mem_set;
return (stream *) s;
}
static stream *
fd_to_stream (int fd, int prot)
{
struct stat statbuf;
unix_stream *s;
s = get_mem (sizeof (unix_stream));
memset (s, '\0', sizeof (unix_stream));
s->fd = fd;
s->buffer_offset = 0;
s->physical_offset = 0;
s->logical_offset = 0;
s->prot = prot;
fstat (fd, &statbuf);
if (lseek (fd, 0, SEEK_CUR) == (off_t) -1)
s->file_length = -1;
else
s->file_length = S_ISREG (statbuf.st_mode) ? statbuf.st_size : -1;
s->special_file = !S_ISREG (statbuf.st_mode);
fd_open (s);
return (stream *) s;
}
int
unit_to_fd (int unit)
{
gfc_unit *us;
int fd;
us = find_unit (unit);
if (us == NULL)
return -1;
fd = ((unix_stream *) us->s)->fd;
unlock_unit (us);
return fd;
}
int
unpack_filename (char *cstring, const char *fstring, int len)
{
len = fstrlen (fstring, len);
if (len >= PATH_MAX)
return 1;
memmove (cstring, fstring, len);
cstring[len] = '\0';
return 0;
}
static int
tempfile (st_parameter_open *opp)
{
const char *tempdir;
char *template;
int fd;
tempdir = getenv ("GFORTRAN_TMPDIR");
if (tempdir == NULL)
tempdir = getenv ("TMP");
if (tempdir == NULL)
tempdir = getenv ("TEMP");
if (tempdir == NULL)
tempdir = DEFAULT_TEMPDIR;
template = get_mem (strlen (tempdir) + 20);
st_sprintf (template, "%s/gfortrantmpXXXXXX", tempdir);
#ifdef HAVE_MKSTEMP
fd = mkstemp (template);
#else
if (mktemp (template))
do
#if defined(HAVE_CRLF) && defined(O_BINARY)
fd = open (template, O_RDWR | O_CREAT | O_EXCL | O_BINARY,
S_IREAD | S_IWRITE);
#else
fd = open (template, O_RDWR | O_CREAT | O_EXCL, S_IREAD | S_IWRITE);
#endif
while (!(fd == -1 && errno == EEXIST) && mktemp (template));
else
fd = -1;
#endif
if (fd < 0)
free_mem (template);
else
{
opp->file = template;
opp->file_len = strlen (template);
}
return fd;
}
static int
regular_file (st_parameter_open *opp, unit_flags *flags)
{
char path[PATH_MAX + 1];
int mode;
int rwflag;
int crflag;
int fd;
if (unpack_filename (path, opp->file, opp->file_len))
{
errno = ENOENT;
return -1;
}
rwflag = 0;
switch (flags->action)
{
case ACTION_READ:
rwflag = O_RDONLY;
break;
case ACTION_WRITE:
rwflag = O_WRONLY;
break;
case ACTION_READWRITE:
case ACTION_UNSPECIFIED:
rwflag = O_RDWR;
break;
default:
internal_error (&opp->common, "regular_file(): Bad action");
}
switch (flags->status)
{
case STATUS_NEW:
crflag = O_CREAT | O_EXCL;
break;
case STATUS_OLD:
crflag = 0;
break;
case STATUS_UNKNOWN:
case STATUS_SCRATCH:
crflag = O_CREAT;
break;
case STATUS_REPLACE:
crflag = O_CREAT | O_TRUNC;
break;
default:
internal_error (&opp->common, "regular_file(): Bad status");
}
#if defined(HAVE_CRLF) && defined(O_BINARY)
crflag |= O_BINARY;
#endif
mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
fd = open (path, rwflag | crflag, mode);
if (flags->action != ACTION_UNSPECIFIED)
return fd;
if (fd >= 0)
{
flags->action = ACTION_READWRITE;
return fd;
}
if (errno != EACCES && errno != EROFS)
return fd;
rwflag = O_RDONLY;
fd = open (path, rwflag | crflag, mode);
if (fd >=0)
{
flags->action = ACTION_READ;
return fd;
}
if (errno != EACCES)
return fd;
rwflag = O_WRONLY;
fd = open (path, rwflag | crflag, mode);
if (fd >=0)
{
flags->action = ACTION_WRITE;
return fd;
}
return fd;
}
stream *
open_external (st_parameter_open *opp, unit_flags *flags)
{
int fd, prot;
if (flags->status == STATUS_SCRATCH)
{
fd = tempfile (opp);
if (flags->action == ACTION_UNSPECIFIED)
flags->action = ACTION_READWRITE;
#if HAVE_UNLINK_OPEN_FILE
if (fd >= 0)
unlink (opp->file);
#endif
}
else
{
fd = regular_file (opp, flags);
}
if (fd < 0)
return NULL;
fd = fix_fd (fd);
switch (flags->action)
{
case ACTION_READ:
prot = PROT_READ;
break;
case ACTION_WRITE:
prot = PROT_WRITE;
break;
case ACTION_READWRITE:
prot = PROT_READ | PROT_WRITE;
break;
default:
internal_error (&opp->common, "open_external(): Bad action");
}
return fd_to_stream (fd, prot);
}
stream *
input_stream (void)
{
return fd_to_stream (STDIN_FILENO, PROT_READ);
}
stream *
output_stream (void)
{
#if defined(HAVE_CRLF) && defined(HAVE_SETMODE)
setmode (STDOUT_FILENO, O_BINARY);
#endif
return fd_to_stream (STDOUT_FILENO, PROT_WRITE);
}
stream *
error_stream (void)
{
#if defined(HAVE_CRLF) && defined(HAVE_SETMODE)
setmode (STDERR_FILENO, O_BINARY);
#endif
return fd_to_stream (STDERR_FILENO, PROT_WRITE);
}
stream *
init_error_stream (unix_stream *error)
{
memset (error, '\0', sizeof (*error));
error->fd = options.use_stderr ? STDERR_FILENO : STDOUT_FILENO;
error->st.alloc_w_at = (void *) fd_alloc_w_at;
error->st.sfree = (void *) fd_sfree;
error->unbuffered = 1;
error->buffer = error->small_buffer;
return (stream *) error;
}
int
compare_file_filename (gfc_unit *u, const char *name, int len)
{
char path[PATH_MAX + 1];
struct stat st1;
#ifdef HAVE_WORKING_STAT
struct stat st2;
#endif
if (unpack_filename (path, name, len))
return 0;
if (stat (path, &st1) < 0)
return 0;
#ifdef HAVE_WORKING_STAT
fstat (((unix_stream *) (u->s))->fd, &st2);
return (st1.st_dev == st2.st_dev) && (st1.st_ino == st2.st_ino);
#else
if (len != u->file_len)
return 0;
return (memcmp(path, u->file, len) == 0);
#endif
}
#ifdef HAVE_WORKING_STAT
# define FIND_FILE0_DECL struct stat *st
# define FIND_FILE0_ARGS st
#else
# define FIND_FILE0_DECL const char *file, gfc_charlen_type file_len
# define FIND_FILE0_ARGS file, file_len
#endif
static gfc_unit *
find_file0 (gfc_unit *u, FIND_FILE0_DECL)
{
gfc_unit *v;
if (u == NULL)
return NULL;
#ifdef HAVE_WORKING_STAT
if (u->s != NULL
&& fstat (((unix_stream *) u->s)->fd, &st[1]) >= 0 &&
st[0].st_dev == st[1].st_dev && st[0].st_ino == st[1].st_ino)
return u;
#else
if (compare_string (u->file_len, u->file, file_len, file) == 0)
return u;
#endif
v = find_file0 (u->left, FIND_FILE0_ARGS);
if (v != NULL)
return v;
v = find_file0 (u->right, FIND_FILE0_ARGS);
if (v != NULL)
return v;
return NULL;
}
gfc_unit *
find_file (const char *file, gfc_charlen_type file_len)
{
char path[PATH_MAX + 1];
struct stat st[2];
gfc_unit *u;
if (unpack_filename (path, file, file_len))
return NULL;
if (stat (path, &st[0]) < 0)
return NULL;
__gthread_mutex_lock (&unit_lock);
retry:
u = find_file0 (unit_root, FIND_FILE0_ARGS);
if (u != NULL)
{
if (! __gthread_mutex_trylock (&u->lock))
{
__gthread_mutex_unlock (&unit_lock);
return u;
}
inc_waiting_locked (u);
}
__gthread_mutex_unlock (&unit_lock);
if (u != NULL)
{
__gthread_mutex_lock (&u->lock);
if (u->closed)
{
__gthread_mutex_lock (&unit_lock);
__gthread_mutex_unlock (&u->lock);
if (predec_waiting_locked (u) == 0)
free_mem (u);
goto retry;
}
dec_waiting_unlocked (u);
}
return u;
}
static gfc_unit *
flush_all_units_1 (gfc_unit *u, int min_unit)
{
while (u != NULL)
{
if (u->unit_number > min_unit)
{
gfc_unit *r = flush_all_units_1 (u->left, min_unit);
if (r != NULL)
return r;
}
if (u->unit_number >= min_unit)
{
if (__gthread_mutex_trylock (&u->lock))
return u;
if (u->s)
flush (u->s);
__gthread_mutex_unlock (&u->lock);
}
u = u->right;
}
return NULL;
}
void
flush_all_units (void)
{
gfc_unit *u;
int min_unit = 0;
__gthread_mutex_lock (&unit_lock);
do
{
u = flush_all_units_1 (unit_root, min_unit);
if (u != NULL)
inc_waiting_locked (u);
__gthread_mutex_unlock (&unit_lock);
if (u == NULL)
return;
__gthread_mutex_lock (&u->lock);
min_unit = u->unit_number + 1;
if (u->closed == 0)
{
flush (u->s);
__gthread_mutex_lock (&unit_lock);
__gthread_mutex_unlock (&u->lock);
(void) predec_waiting_locked (u);
}
else
{
__gthread_mutex_lock (&unit_lock);
__gthread_mutex_unlock (&u->lock);
if (predec_waiting_locked (u) == 0)
free_mem (u);
}
}
while (1);
}
int
stream_at_bof (stream * s)
{
unix_stream *us;
if (!is_seekable (s))
return 0;
us = (unix_stream *) s;
return us->logical_offset == 0;
}
int
stream_at_eof (stream * s)
{
unix_stream *us;
if (!is_seekable (s))
return 0;
us = (unix_stream *) s;
return us->logical_offset == us->dirty_offset;
}
int
delete_file (gfc_unit * u)
{
char path[PATH_MAX + 1];
if (unpack_filename (path, u->file, u->file_len))
{
errno = ENOENT;
return 1;
}
return unlink (path);
}
int
file_exists (const char *file, gfc_charlen_type file_len)
{
char path[PATH_MAX + 1];
struct stat statbuf;
if (unpack_filename (path, file, file_len))
return 0;
if (stat (path, &statbuf) < 0)
return 0;
return 1;
}
static const char yes[] = "YES", no[] = "NO", unknown[] = "UNKNOWN";
const char *
inquire_sequential (const char *string, int len)
{
char path[PATH_MAX + 1];
struct stat statbuf;
if (string == NULL ||
unpack_filename (path, string, len) || stat (path, &statbuf) < 0)
return unknown;
if (S_ISREG (statbuf.st_mode) ||
S_ISCHR (statbuf.st_mode) || S_ISFIFO (statbuf.st_mode))
return yes;
if (S_ISDIR (statbuf.st_mode) || S_ISBLK (statbuf.st_mode))
return no;
return unknown;
}
const char *
inquire_direct (const char *string, int len)
{
char path[PATH_MAX + 1];
struct stat statbuf;
if (string == NULL ||
unpack_filename (path, string, len) || stat (path, &statbuf) < 0)
return unknown;
if (S_ISREG (statbuf.st_mode) || S_ISBLK (statbuf.st_mode))
return yes;
if (S_ISDIR (statbuf.st_mode) ||
S_ISCHR (statbuf.st_mode) || S_ISFIFO (statbuf.st_mode))
return no;
return unknown;
}
const char *
inquire_formatted (const char *string, int len)
{
char path[PATH_MAX + 1];
struct stat statbuf;
if (string == NULL ||
unpack_filename (path, string, len) || stat (path, &statbuf) < 0)
return unknown;
if (S_ISREG (statbuf.st_mode) ||
S_ISBLK (statbuf.st_mode) ||
S_ISCHR (statbuf.st_mode) || S_ISFIFO (statbuf.st_mode))
return yes;
if (S_ISDIR (statbuf.st_mode))
return no;
return unknown;
}
const char *
inquire_unformatted (const char *string, int len)
{
return inquire_formatted (string, len);
}
static const char *
inquire_access (const char *string, int len, int mode)
{
char path[PATH_MAX + 1];
if (string == NULL || unpack_filename (path, string, len) ||
access (path, mode) < 0)
return no;
return yes;
}
const char *
inquire_read (const char *string, int len)
{
return inquire_access (string, len, R_OK);
}
const char *
inquire_write (const char *string, int len)
{
return inquire_access (string, len, W_OK);
}
const char *
inquire_readwrite (const char *string, int len)
{
return inquire_access (string, len, R_OK | W_OK);
}
gfc_offset
file_length (stream * s)
{
return ((unix_stream *) s)->file_length;
}
gfc_offset
file_position (stream * s)
{
return ((unix_stream *) s)->logical_offset;
}
int
is_seekable (stream * s)
{
return ((unix_stream *) s)->file_length!=-1;
}
try
flush (stream *s)
{
return fd_flush( (unix_stream *) s);
}
int
stream_isatty (stream *s)
{
return isatty (((unix_stream *) s)->fd);
}
char *
stream_ttyname (stream *s)
{
#ifdef HAVE_TTYNAME
return ttyname (((unix_stream *) s)->fd);
#else
return NULL;
#endif
}
gfc_offset
stream_offset (stream *s)
{
return (((unix_stream *) s)->logical_offset);
}