-/* Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
- 2011, 2012
- Free Software Foundation, Inc.
+/* Copyright (C) 2002-2015 Free Software Foundation, Inc.
Contributed by Andy Vaught
F2003 I/O support contributed by Jerry DeLisle
#include <stdlib.h>
#include <limits.h>
+#ifdef HAVE_UNISTD_H
#include <unistd.h>
+#endif
+
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>
#include <errno.h>
-/* min macro that evaluates its arguments only once. */
-#define min(a,b) \
- ({ typeof (a) _a = (a); \
- typeof (b) _b = (b); \
- _a < _b ? _a : _b; })
-
-
/* For mingw, we don't identify files by their inode number, but by a
64-bit identifier created from a BY_HANDLE_FILE_INFORMATION. */
#ifdef __MINGW32__
return id_from_handle ((HANDLE) _get_osfhandle (fd));
}
-#endif
-#endif
+#endif /* HAVE_WORKING_STAT */
+#endif /* __MINGW32__ */
+
-#ifndef PATH_MAX
-#define PATH_MAX 1024
+/* min macro that evaluates its arguments only once. */
+#ifdef min
+#undef min
#endif
+#define min(a,b) \
+ ({ typeof (a) _a = (a); \
+ typeof (b) _b = (b); \
+ _a < _b ? _a : _b; })
+
+
/* These flags aren't defined on all targets (mingw32), so provide them
here. */
#ifndef S_IRGRP
/* Cached stat(2) values. */
dev_t st_dev;
ino_t st_ino;
+
+ bool unbuffered; /* Buffer should be flushed after each I/O statement. */
}
unix_stream;
int ret = fstat (s->fd, &statbuf);
if (ret == -1)
return ret;
- return statbuf.st_size;
+ if (S_ISREG (statbuf.st_mode))
+ return statbuf.st_size;
+ else
+ return 0;
}
static int
{
int retval;
- if (s->fd != STDOUT_FILENO
+ if (s->fd == -1)
+ retval = -1;
+ else if (s->fd != STDOUT_FILENO
&& s->fd != STDERR_FILENO
&& s->fd != STDIN_FILENO)
retval = close (s->fd);
return retval;
}
+static int
+raw_markeor (unix_stream * s __attribute__ ((unused)))
+{
+ return 0;
+}
+
static const struct stream_vtable raw_vtable = {
.read = (void *) raw_read,
.write = (void *) raw_write,
.size = (void *) raw_size,
.trunc = (void *) raw_truncate,
.close = (void *) raw_close,
- .flush = (void *) raw_flush
+ .flush = (void *) raw_flush,
+ .markeor = (void *) raw_markeor
};
static int
Buffered I/O functions. These functions have the same semantics as the
raw I/O functions above, except that they are buffered in order to
improve performance. The buffer must be flushed when switching from
-reading to writing and vice versa. Only supported for regular files.
+reading to writing and vice versa.
*********************************************************************/
static int
return nbyte;
}
+
+/* "Unbuffered" really means I/O statement buffering. For formatted
+ I/O, the fbuf manages this, and then uses raw I/O. For unformatted
+ I/O, buffered I/O is used, and the buffer is flushed at the end of
+ each I/O statement, where this function is called. Alternatively,
+ the buffer is flushed at the end of the record if the buffer is
+ more than half full; this prevents needless seeking back and forth
+ when writing sequential unformatted. */
+
+static int
+buf_markeor (unix_stream * s)
+{
+ if (s->unbuffered || s->ndirty >= BUFFER_SIZE / 2)
+ return buf_flush (s);
+ return 0;
+}
+
static gfc_offset
buf_seek (unix_stream * s, gfc_offset offset, int whence)
{
.size = (void *) buf_size,
.trunc = (void *) buf_truncate,
.close = (void *) buf_close,
- .flush = (void *) buf_flush
+ .flush = (void *) buf_flush,
+ .markeor = (void *) buf_markeor
};
static int
void *p;
int nb = nbytes;
- p = mem_alloc_r (s, &nb);
+ p = mem_alloc_r4 (s, &nb);
if (p)
{
- memcpy (buf, p, nb);
+ memcpy (buf, p, nb * 4);
return (ssize_t) nb;
}
else
.size = (void *) buf_size,
.trunc = (void *) mem_truncate,
.close = (void *) mem_close,
- .flush = (void *) mem_flush
+ .flush = (void *) mem_flush,
+ .markeor = (void *) raw_markeor
};
static const struct stream_vtable mem4_vtable = {
.size = (void *) buf_size,
.trunc = (void *) mem_truncate,
.close = (void *) mem_close,
- .flush = (void *) mem_flush
+ .flush = (void *) mem_flush,
+ .markeor = (void *) raw_markeor
};
/*********************************************************************
s->buffer = base;
s->buffer_offset = offset;
- s->active = s->file_length = length;
+ s->active = s->file_length = length * sizeof (gfc_char4_t);
s->st.vptr = &mem4_vtable;
* around it. */
static stream *
-fd_to_stream (int fd)
+fd_to_stream (int fd, bool unformatted)
{
struct stat statbuf;
unix_stream *s;
/* Get the current length of the file. */
- fstat (fd, &statbuf);
+ if (fstat (fd, &statbuf) == -1)
+ {
+ s->st_dev = s->st_ino = -1;
+ s->file_length = 0;
+ if (errno == EBADF)
+ s->fd = -1;
+ raw_init (s);
+ return (stream *) s;
+ }
s->st_dev = statbuf.st_dev;
s->st_ino = statbuf.st_ino;
|| s->fd == STDERR_FILENO)))
buf_init (s);
else
- raw_init (s);
+ {
+ if (unformatted)
+ {
+ s->unbuffered = true;
+ buf_init (s);
+ }
+ else
+ raw_init (s);
+ }
return (stream *) s;
}
}
-/* unpack_filename()-- Given a fortran string and a pointer to a
- * buffer that is PATH_MAX characters, convert the fortran string to a
- * C string in the buffer. Returns nonzero if this is not possible. */
+/* Set the close-on-exec flag for an existing fd, if the system
+ supports such. */
-int
-unpack_filename (char *cstring, const char *fstring, int len)
+static void __attribute__ ((unused))
+set_close_on_exec (int fd __attribute__ ((unused)))
{
- if (fstring == NULL)
- return EFAULT;
- len = fstrlen (fstring, len);
- if (len >= PATH_MAX)
- return ENAMETOOLONG;
-
- memmove (cstring, fstring, len);
- cstring[len] = '\0';
-
- return 0;
+ /* Mingw does not define F_SETFD. */
+#if defined(HAVE_FCNTL) && defined(F_SETFD) && defined(FD_CLOEXEC)
+ if (fd >= 0)
+ fcntl(fd, F_SETFD, FD_CLOEXEC);
+#endif
}
{
int fd;
const char *slash = "/";
+#if defined(HAVE_UMASK) && defined(HAVE_MKSTEMP)
+ mode_t mode_mask;
+#endif
if (!tempdir)
return -1;
snprintf (template, tempdirlen + 23, "%s%sgfortrantmpXXXXXX",
tempdir, slash);
+#ifdef HAVE_UMASK
+ /* Temporarily set the umask such that the file has 0600 permissions. */
+ mode_mask = umask (S_IXUSR | S_IRWXG | S_IRWXO);
+#endif
+
+#if defined(HAVE_MKOSTEMP) && defined(O_CLOEXEC)
+ fd = mkostemp (template, O_CLOEXEC);
+#else
fd = mkstemp (template);
+ set_close_on_exec (fd);
+#endif
+
+#ifdef HAVE_UMASK
+ (void) umask (mode_mask);
+#endif
#else /* HAVE_MKSTEMP */
fd = -1;
int count = 0;
size_t slashlen = strlen (slash);
+ int flags = O_RDWR | O_CREAT | O_EXCL;
+#if defined(HAVE_CRLF) && defined(O_BINARY)
+ flags |= O_BINARY;
+#endif
+#ifdef O_CLOEXEC
+ flags |= O_CLOEXEC;
+#endif
do
{
snprintf (template, tempdirlen + 23, "%s%sgfortrantmpaaaXXXXXX",
continue;
}
-#if defined(HAVE_CRLF) && defined(O_BINARY)
- fd = open (template, O_RDWR | O_CREAT | O_EXCL | O_BINARY,
- S_IRUSR | S_IWUSR, 0600);
-#else
- fd = open (template, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR, 0600);
-#endif
+ fd = open (template, flags, S_IRUSR | S_IWUSR);
}
while (fd == -1 && errno == EEXIST);
+#ifndef O_CLOEXEC
+ set_close_on_exec (fd);
+#endif
#endif /* HAVE_MKSTEMP */
*fname = template;
}
-/* regular_file()-- Open a regular file.
+/* regular_file2()-- Open a regular file.
* Change flags->action if it is ACTION_UNSPECIFIED on entry,
* unless an error occurs.
* Returns the descriptor, which is less than zero on error. */
static int
-regular_file (st_parameter_open *opp, unit_flags *flags)
+regular_file2 (const char *path, st_parameter_open *opp, unit_flags *flags)
{
- char path[min(PATH_MAX, opp->file_len + 1)];
int mode;
int rwflag;
- int crflag;
+ int crflag, crflag2;
int fd;
- int err;
-
- err = unpack_filename (path, opp->file, opp->file_len);
- if (err)
- {
- errno = err; /* Fake an OS error */
- return -1;
- }
#ifdef __CYGWIN__
if (opp->file_len == 7)
}
#endif
- rwflag = 0;
-
switch (flags->action)
{
case ACTION_READ:
break;
case STATUS_UNKNOWN:
- case STATUS_SCRATCH:
- crflag = O_CREAT;
+ if (rwflag == O_RDONLY)
+ crflag = 0;
+ else
+ crflag = O_CREAT;
break;
case STATUS_REPLACE:
break;
default:
+ /* Note: STATUS_SCRATCH is handled by tempfile () and should
+ never be seen here. */
internal_error (&opp->common, "regular_file(): Bad status");
}
crflag |= O_BINARY;
#endif
+#ifdef O_CLOEXEC
+ crflag |= O_CLOEXEC;
+#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)
flags->action = ACTION_READWRITE;
return fd;
}
- if (errno != EACCES && errno != EROFS)
+ if (errno != EACCES && errno != EPERM && errno != EROFS)
return fd;
/* retry for read-only access */
rwflag = O_RDONLY;
- fd = open (path, rwflag | crflag, mode);
+ if (flags->status == STATUS_UNKNOWN)
+ crflag2 = crflag & ~(O_CREAT);
+ else
+ crflag2 = crflag;
+ fd = open (path, rwflag | crflag2, mode);
if (fd >=0)
{
flags->action = ACTION_READ;
return fd; /* success */
}
- if (errno != EACCES)
+ if (errno != EACCES && errno != EPERM && errno != ENOENT)
return fd; /* failure */
/* retry for write-only access */
}
+/* Wrapper around regular_file2, to make sure we free the path after
+ we're done. */
+
+static int
+regular_file (st_parameter_open *opp, unit_flags *flags)
+{
+ char *path = fc_strdup (opp->file, opp->file_len);
+ int fd = regular_file2 (path, opp, flags);
+ free (path);
+ return fd;
+}
+
/* open_external()-- Open an external file, unix specific version.
* Change flags->action if it is ACTION_UNSPECIFIED on entry.
* Returns NULL on operating system error. */
/* regular_file resets flags->action if it is ACTION_UNSPECIFIED and
* if it succeeds */
fd = regular_file (opp, flags);
+#ifndef O_CLOEXEC
+ set_close_on_exec (fd);
+#endif
}
if (fd < 0)
return NULL;
fd = fix_fd (fd);
- return fd_to_stream (fd);
+ return fd_to_stream (fd, flags->form == FORM_UNFORMATTED);
}
stream *
input_stream (void)
{
- return fd_to_stream (STDIN_FILENO);
+ return fd_to_stream (STDIN_FILENO, false);
}
setmode (STDOUT_FILENO, O_BINARY);
#endif
- s = fd_to_stream (STDOUT_FILENO);
+ s = fd_to_stream (STDOUT_FILENO, false);
return s;
}
setmode (STDERR_FILENO, O_BINARY);
#endif
- s = fd_to_stream (STDERR_FILENO);
+ s = fd_to_stream (STDERR_FILENO, false);
return s;
}
int
compare_file_filename (gfc_unit *u, const char *name, int len)
{
- char path[min(PATH_MAX, len + 1)];
struct stat st;
+ int ret;
#ifdef HAVE_WORKING_STAT
unix_stream *s;
#else
# endif
#endif
- if (unpack_filename (path, name, len))
- return 0; /* Can't be the same */
+ char *path = fc_strdup (name, len);
/* If the filename doesn't exist, then there is no match with the
* existing file. */
if (stat (path, &st) < 0)
- return 0;
+ {
+ ret = 0;
+ goto done;
+ }
#ifdef HAVE_WORKING_STAT
s = (unix_stream *) (u->s);
- return (st.st_dev == s->st_dev) && (st.st_ino == s->st_ino);
+ ret = (st.st_dev == s->st_dev) && (st.st_ino == s->st_ino);
+ goto done;
#else
# ifdef __MINGW32__
id1 = id_from_path (path);
id2 = id_from_fd (((unix_stream *) (u->s))->fd);
if (id1 || id2)
- return (id1 == id2);
+ {
+ ret = (id1 == id2);
+ goto done;
+ }
# endif
-
- if (len != u->file_len)
- return 0;
- return (memcmp(path, u->file, len) == 0);
+ ret = (strcmp(path, u->filename) == 0);
#endif
+ done:
+ free (path);
+ return ret;
}
# define FIND_FILE0_DECL struct stat *st
# define FIND_FILE0_ARGS st
#else
-# define FIND_FILE0_DECL uint64_t id, const char *file, gfc_charlen_type file_len
-# define FIND_FILE0_ARGS id, file, file_len
+# define FIND_FILE0_DECL uint64_t id, const char *path
+# define FIND_FILE0_ARGS id, path
#endif
/* find_file0()-- Recursive work function for find_file() */
}
else
# endif
- if (compare_string (u->file_len, u->file, file_len, file) == 0)
+ if (strcmp (u->filename, path) == 0)
return u;
#endif
gfc_unit *
find_file (const char *file, gfc_charlen_type file_len)
{
- char path[min(PATH_MAX, file_len + 1)];
struct stat st[1];
gfc_unit *u;
#if defined(__MINGW32__) && !HAVE_WORKING_STAT
uint64_t id = 0ULL;
#endif
- if (unpack_filename (path, file, file_len))
- return NULL;
+ char *path = fc_strdup (file, file_len);
if (stat (path, &st[0]) < 0)
- return NULL;
+ {
+ u = NULL;
+ goto done;
+ }
#if defined(__MINGW32__) && !HAVE_WORKING_STAT
id = id_from_path (path);
{
/* assert (u->closed == 0); */
__gthread_mutex_unlock (&unit_lock);
- return u;
+ goto done;
}
inc_waiting_locked (u);
dec_waiting_unlocked (u);
}
+ done:
+ free (path);
return u;
}
int
delete_file (gfc_unit * u)
{
- char path[min(PATH_MAX, u->file_len + 1)];
- int err = unpack_filename (path, u->file, u->file_len);
-
- if (err)
- { /* Shouldn't be possible */
- errno = err;
- return 1;
- }
-
- return unlink (path);
+ return unlink (u->filename);
}
int
file_exists (const char *file, gfc_charlen_type file_len)
{
- char path[min(PATH_MAX, file_len + 1)];
-
- if (unpack_filename (path, file, file_len))
- return 0;
-
- return !(access (path, F_OK));
+ char *path = fc_strdup (file, file_len);
+ int res = !(access (path, F_OK));
+ free (path);
+ return res;
}
GFC_IO_INT
file_size (const char *file, gfc_charlen_type file_len)
{
- char path[min(PATH_MAX, file_len + 1)];
+ char *path = fc_strdup (file, file_len);
struct stat statbuf;
-
- if (unpack_filename (path, file, file_len))
+ int err = stat (path, &statbuf);
+ free (path);
+ if (err == -1)
return -1;
-
- if (stat (path, &statbuf) < 0)
- return -1;
-
return (GFC_IO_INT) statbuf.st_size;
}
const char *
inquire_sequential (const char *string, int len)
{
- char path[min(PATH_MAX, len + 1)];
struct stat statbuf;
- if (string == NULL ||
- unpack_filename (path, string, len) || stat (path, &statbuf) < 0)
+ if (string == NULL)
+ return unknown;
+
+ char *path = fc_strdup (string, len);
+ int err = stat (path, &statbuf);
+ free (path);
+ if (err == -1)
return unknown;
if (S_ISREG (statbuf.st_mode) ||
const char *
inquire_direct (const char *string, int len)
{
- char path[min(PATH_MAX, len + 1)];
struct stat statbuf;
- if (string == NULL ||
- unpack_filename (path, string, len) || stat (path, &statbuf) < 0)
+ if (string == NULL)
+ return unknown;
+
+ char *path = fc_strdup (string, len);
+ int err = stat (path, &statbuf);
+ free (path);
+ if (err == -1)
return unknown;
if (S_ISREG (statbuf.st_mode) || S_ISBLK (statbuf.st_mode))
const char *
inquire_formatted (const char *string, int len)
{
- char path[min(PATH_MAX, len + 1)];
struct stat statbuf;
- if (string == NULL ||
- unpack_filename (path, string, len) || stat (path, &statbuf) < 0)
+ if (string == NULL)
+ return unknown;
+
+ char *path = fc_strdup (string, len);
+ int err = stat (path, &statbuf);
+ free (path);
+ if (err == -1)
return unknown;
if (S_ISREG (statbuf.st_mode) ||
static const char *
inquire_access (const char *string, int len, int mode)
{
- char path[min(PATH_MAX, len + 1)];
-
- if (string == NULL || unpack_filename (path, string, len) ||
- access (path, mode) < 0)
+ if (string == NULL)
+ return no;
+ char *path = fc_strdup (string, len);
+ int res = access (path, mode);
+ free (path);
+ if (res == -1)
return no;
return yes;