X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=libgfortran%2Fio%2Funix.c;h=e5fc6e19818dcccca662c946e8ea5c7d5b299436;hb=1b961de9db0b7ffb8d7f4614ddf61faf31a544a5;hp=93484ea014c153b797ec7b75f5fdf42bdc36a952;hpb=1f94e1d88efeb405a810a72ad61ab637becb3761;p=gcc.git diff --git a/libgfortran/io/unix.c b/libgfortran/io/unix.c index 93484ea014c..e5fc6e19818 100644 --- a/libgfortran/io/unix.c +++ b/libgfortran/io/unix.c @@ -1,40 +1,39 @@ -/* Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 - Free Software Foundation, Inc. +/* Copyright (C) 2002-2015 Free Software Foundation, Inc. Contributed by Andy Vaught + F2003 I/O support contributed by Jerry DeLisle -This file is part of the GNU Fortran 95 runtime library (libgfortran). +This file is part of the GNU Fortran runtime library (libgfortran). Libgfortran is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2, or (at your option) +the Free Software Foundation; either version 3, or (at your option) any later version. -In addition to the permissions in the GNU General Public License, the -Free Software Foundation gives you unlimited permission to link the -compiled version of this file into combinations with other programs, -and to distribute those combinations without any restriction coming -from the use of this file. (The General Public License restrictions -do apply in other respects; for example, they cover modification of -the file, and distribution when not linked into a combine -executable.) - Libgfortran is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. -You should have received a copy of the GNU General Public License -along with Libgfortran; see the file COPYING. If not, write to -the Free Software Foundation, 51 Franklin Street, Fifth Floor, -Boston, MA 02110-1301, USA. */ +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +. */ /* Unix stream I/O module */ #include "io.h" +#include "unix.h" #include #include +#ifdef HAVE_UNISTD_H #include +#endif + #include #include #include @@ -45,11 +44,21 @@ Boston, MA 02110-1301, USA. */ /* For mingw, we don't identify files by their inode number, but by a 64-bit identifier created from a BY_HANDLE_FILE_INFORMATION. */ -#if defined(__MINGW32__) && !HAVE_WORKING_STAT +#ifdef __MINGW32__ #define WIN32_LEAN_AND_MEAN #include +#if !defined(_FILE_OFFSET_BITS) || _FILE_OFFSET_BITS != 64 +#undef lseek +#define lseek _lseeki64 +#undef fstat +#define fstat _fstati64 +#undef stat +#define stat _stati64 +#endif + +#ifndef HAVE_WORKING_STAT static uint64_t id_from_handle (HANDLE hFile) { @@ -91,25 +100,20 @@ id_from_fd (const int fd) return id_from_handle ((HANDLE) _get_osfhandle (fd)); } -#endif - - +#endif /* HAVE_WORKING_STAT */ +#endif /* __MINGW32__ */ -#ifndef SSIZE_MAX -#define SSIZE_MAX SHRT_MAX -#endif -#ifndef PATH_MAX -#define PATH_MAX 1024 +/* min macro that evaluates its arguments only once. */ +#ifdef min +#undef min #endif -#ifndef PROT_READ -#define PROT_READ 1 -#endif +#define min(a,b) \ + ({ typeof (a) _a = (a); \ + typeof (b) _b = (b); \ + _a < _b ? _a : _b; }) -#ifndef PROT_WRITE -#define PROT_WRITE 2 -#endif /* These flags aren't defined on all targets (mingw32), so provide them here. */ @@ -130,128 +134,88 @@ id_from_fd (const int fd) #endif -/* Unix stream I/O module */ +#ifndef HAVE_ACCESS -#define BUFFER_SIZE 8192 +#ifndef W_OK +#define W_OK 2 +#endif -typedef struct -{ - stream st; +#ifndef R_OK +#define R_OK 4 +#endif - int fd; - gfc_offset buffer_offset; /* File offset of the start of the buffer */ - gfc_offset physical_offset; /* Current physical file offset */ - gfc_offset logical_offset; /* Current logical file offset */ - gfc_offset dirty_offset; /* Start of modified bytes in buffer */ - gfc_offset file_length; /* Length of the file, -1 if not seekable. */ +#ifndef F_OK +#define F_OK 0 +#endif - int len; /* Physical length of the current buffer */ - int active; /* Length of valid bytes in the buffer */ +/* Fallback implementation of access() on systems that don't have it. + Only modes R_OK, W_OK and F_OK are used in this file. */ - int prot; - int ndirty; /* Dirty bytes starting at dirty_offset */ +static int +fallback_access (const char *path, int mode) +{ + int fd; + + if ((mode & R_OK) && (fd = open (path, O_RDONLY)) < 0) + return -1; + close (fd); - int special_file; /* =1 if the fd refers to a special file */ + if ((mode & W_OK) && (fd = open (path, O_WRONLY)) < 0) + return -1; + close (fd); - int unbuffered; /* =1 if the stream is not buffered */ + if (mode == F_OK) + { + struct stat st; + return stat (path, &st); + } - char *buffer; - char small_buffer[BUFFER_SIZE]; + return 0; } -unix_stream; +#undef access +#define access fallback_access +#endif + + +/* Fallback directory for creating temporary files. P_tmpdir is + defined on many POSIX platforms. */ +#ifndef P_tmpdir +#ifdef _P_tmpdir +#define P_tmpdir _P_tmpdir /* MinGW */ +#else +#define P_tmpdir "/tmp" +#endif +#endif + + +/* Unix and internal stream I/O module */ -/* Stream structure for internal files. Fields must be kept in sync - with unix_stream above, except for the buffer. For internal files - we point the buffer pointer directly at the destination memory. */ +static const int BUFFER_SIZE = 8192; typedef struct { stream st; - int fd; gfc_offset buffer_offset; /* File offset of the start of the buffer */ gfc_offset physical_offset; /* Current physical file offset */ gfc_offset logical_offset; /* Current logical file offset */ - gfc_offset dirty_offset; /* Start of modified bytes in buffer */ - gfc_offset file_length; /* Length of the file, -1 if not seekable. */ + gfc_offset file_length; /* Length of the file. */ + + char *buffer; /* Pointer to the buffer. */ + int fd; /* The POSIX file descriptor. */ - int len; /* Physical length of the current buffer */ int active; /* Length of valid bytes in the buffer */ - int prot; - int ndirty; /* Dirty bytes starting at dirty_offset */ - - int special_file; /* =1 if the fd refers to a special file */ - - int unbuffered; /* =1 if the stream is not buffered */ - - char *buffer; -} -int_stream; - -/* This implementation of stream I/O is based on the paper: - * - * "Exploiting the advantages of mapped files for stream I/O", - * O. Krieger, M. Stumm and R. Umrau, "Proceedings of the 1992 Winter - * USENIX conference", p. 27-42. - * - * It differs in a number of ways from the version described in the - * paper. First of all, threads are not an issue during I/O and we - * also don't have to worry about having multiple regions, since - * fortran's I/O model only allows you to be one place at a time. - * - * On the other hand, we have to be able to writing at the end of a - * stream, read from the start of a stream or read and write blocks of - * bytes from an arbitrary position. After opening a file, a pointer - * to a stream structure is returned, which is used to handle file - * accesses until the file is closed. - * - * salloc_at_r(stream, len, where)-- Given a stream pointer, return a - * pointer to a block of memory that mirror the file at position - * 'where' that is 'len' bytes long. The len integer is updated to - * reflect how many bytes were actually read. The only reason for a - * short read is end of file. The file pointer is updated. The - * pointer is valid until the next call to salloc_*. - * - * salloc_at_w(stream, len, where)-- Given the stream pointer, returns - * a pointer to a block of memory that is updated to reflect the state - * of the file. The length of the buffer is always equal to that - * requested. The buffer must be completely set by the caller. When - * data has been written, the sfree() function must be called to - * indicate that the caller is done writing data to the buffer. This - * may or may not cause a physical write. - * - * Short forms of these are salloc_r() and salloc_w() which drop the - * 'where' parameter and use the current file pointer. */ - - -/*move_pos_offset()-- Move the record pointer right or left - *relative to current position */ + int ndirty; /* Dirty bytes starting at buffer_offset */ -int -move_pos_offset (stream* st, int pos_off) -{ - unix_stream * str = (unix_stream*)st; - if (pos_off < 0) - { - str->logical_offset += pos_off; + /* Cached stat(2) values. */ + dev_t st_dev; + ino_t st_ino; - 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; + bool unbuffered; /* Buffer should be flushed after each I/O statement. */ } +unix_stream; /* fix_fd()-- Given a file descriptor, make sure it is not one of the @@ -298,17 +262,6 @@ fix_fd (int fd) 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; -} /* If the stream corresponds to a preconnected unit, we flush the corresponding C stream. This is bugware for mixed C-Fortran codes @@ -328,571 +281,414 @@ flush_if_preconnected (stream * s) } -/* Reset a stream after reading/writing. Assumes that the buffers have - been flushed. */ +/******************************************************************** +Raw I/O functions (read, write, seek, tell, truncate, close). -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; -} - - -/* Read bytes into a buffer, allowing for short reads. If the nbytes - * argument is less on return than on entry, it is because we've hit - * the end of file. */ +These functions wrap the basic POSIX I/O syscalls. Any deviation in +semantics is a bug, except the following: write restarts in case +of being interrupted by a signal, and as the first argument the +functions take the unix_stream struct rather than an integer file +descriptor. Also, for POSIX read() and write() a nbyte argument larger +than SSIZE_MAX is undefined; here the type of nbyte is ssize_t rather +than size_t as for POSIX read/write. +*********************************************************************/ static int -do_read (unix_stream * s, void * buf, size_t * nbytes) +raw_flush (unix_stream * s __attribute__ ((unused))) { - ssize_t trans; - size_t bytes_left; - char *buf_st; - int status; - - status = 0; - bytes_left = *nbytes; - buf_st = (char *) buf; - - /* We must read in a loop since some systems don't restart system - calls in case of a signal. */ - while (bytes_left > 0) - { - /* Requests between SSIZE_MAX and SIZE_MAX are undefined by SUSv3, - so we must read in chunks smaller than SSIZE_MAX. */ - 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) /* We hit EOF. */ - break; - buf_st += trans; - bytes_left -= trans; - } - - *nbytes -= bytes_left; - return status; + return 0; } +static ssize_t +raw_read (unix_stream * s, void * buf, ssize_t nbyte) +{ + /* For read we can't do I/O in a loop like raw_write does, because + that will break applications that wait for interactive I/O. */ + return read (s->fd, buf, nbyte); +} -/* Write a buffer to a stream, allowing for short writes. */ - -static int -do_write (unix_stream * s, const void * buf, size_t * nbytes) +static ssize_t +raw_write (unix_stream * s, const void * buf, ssize_t nbyte) { - ssize_t trans; - size_t bytes_left; + ssize_t trans, bytes_left; char *buf_st; - int status; - status = 0; - bytes_left = *nbytes; + bytes_left = nbyte; buf_st = (char *) buf; /* We must write in a loop since some systems don't restart system calls in case of a signal. */ while (bytes_left > 0) { - /* Requests between SSIZE_MAX and SIZE_MAX are undefined by SUSv3, - so we must write in chunks smaller than SSIZE_MAX. */ - trans = (bytes_left < SSIZE_MAX) ? bytes_left : SSIZE_MAX; - trans = write (s->fd, buf_st, trans); + trans = write (s->fd, buf_st, bytes_left); if (trans < 0) { if (errno == EINTR) continue; else - { - status = errno; - break; - } + return trans; } buf_st += trans; bytes_left -= trans; } - *nbytes -= bytes_left; - return status; + return nbyte - bytes_left; } - -/* get_oserror()-- Get the most recent operating system error. For - * unix, this is errno. */ - -const char * -get_oserror (void) +static gfc_offset +raw_seek (unix_stream * s, gfc_offset offset, int whence) { - return strerror (errno); + return lseek (s->fd, offset, whence); } - -/********************************************************************* - File descriptor stream functions -*********************************************************************/ - - -/* fd_flush()-- Write bytes that need to be written */ - -static try -fd_flush (unix_stream * s) +static gfc_offset +raw_tell (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; - - /* don't increment file_length if the file is non-seekable */ - 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; + return lseek (s->fd, 0, SEEK_CUR); } - -/* fd_alloc()-- Arrange a buffer such that the salloc() request can be - * satisfied. This subroutine gets the buffer ready for whatever is - * to come next. */ - -static void -fd_alloc (unix_stream * s, gfc_offset where, - int *len __attribute__ ((unused))) +static gfc_offset +raw_size (unix_stream * s) { - 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; - } - - /* Salvage bytes currently within the buffer. This is important for - * devices that cannot seek. */ - - 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; - } + struct stat statbuf; + int ret = fstat (s->fd, &statbuf); + if (ret == -1) + return ret; + if (S_ISREG (statbuf.st_mode)) + return statbuf.st_size; else - { /* new buffer starts off empty */ - s->active = 0; - } - - s->buffer_offset = where; - - /* free the old buffer if necessary */ - - if (s->buffer != NULL && s->buffer != s->small_buffer) - free_mem (s->buffer); - - s->buffer = new_buffer; - s->len = read_len; + return 0; } - -/* fd_alloc_r_at()-- Allocate a stream buffer for reading. Either - * we've already buffered the data or we need to load it. Returns - * NULL on I/O error. */ - -static char * -fd_alloc_r_at (unix_stream * s, int *len, gfc_offset where) +static int +raw_truncate (unix_stream * s, gfc_offset length) { - gfc_offset m; +#ifdef __MINGW32__ + HANDLE h; + gfc_offset cur; - if (where == -1) - where = s->logical_offset; - - if (s->buffer != NULL && s->buffer_offset <= where && - where + *len <= s->buffer_offset + s->active) + if (isatty (s->fd)) { - - /* Return a position within the current buffer */ - - s->logical_offset = where + *len; - return s->buffer + where - s->buffer_offset; + errno = EBADF; + return -1; } - - fd_alloc (s, where, len); - - m = where + s->active; - - if (s->physical_offset != m && lseek (s->fd, m, SEEK_SET) < 0) - return NULL; - - /* do_read() hangs on read from terminals for *BSD-systems. Only - use read() in that case. */ - - if (s->special_file) + h = (HANDLE) _get_osfhandle (s->fd); + if (h == INVALID_HANDLE_VALUE) { - 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; + errno = EBADF; + return -1; } - else + cur = lseek (s->fd, 0, SEEK_CUR); + if (cur == -1) + return -1; + if (lseek (s->fd, length, SEEK_SET) == -1) + goto error; + if (!SetEndOfFile (h)) { - 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; + errno = EBADF; + goto error; } - - if (s->active < *len) - *len = s->active; /* Bytes actually available */ - - s->logical_offset = where + *len; - - return s->buffer; + if (lseek (s->fd, cur, SEEK_SET) == -1) + return -1; + return 0; + error: + lseek (s->fd, cur, SEEK_SET); + return -1; +#elif defined HAVE_FTRUNCATE + return ftruncate (s->fd, length); +#elif defined HAVE_CHSIZE + return chsize (s->fd, length); +#else + runtime_error ("required ftruncate or chsize support not present"); + return -1; +#endif } - -/* fd_alloc_w_at()-- Allocate a stream buffer for writing. Either - * we've already buffered the data or we need to load it. */ - -static char * -fd_alloc_w_at (unix_stream * s, int *len, gfc_offset where) +static int +raw_close (unix_stream * s) { - 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); - } - - /* Return a position within the current buffer */ - if (s->ndirty == 0 - || where > s->dirty_offset + s->ndirty - || s->dirty_offset > where + *len) - { /* Discontiguous blocks, start with a clean buffer. */ - /* Flush the buffer. */ - if (s->ndirty != 0) - fd_flush (s); - s->dirty_offset = where; - s->ndirty = *len; - } + int retval; + + if (s->fd == -1) + retval = -1; + else if (s->fd != STDOUT_FILENO + && s->fd != STDERR_FILENO + && s->fd != STDIN_FILENO) + retval = close (s->fd); else - { - gfc_offset start; /* Merge with the existing data. */ - 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; - - /* Don't increment file_length if the file is non-seekable. */ - - 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; + retval = 0; + free (s); + return retval; } - -static try -fd_sfree (unix_stream * s) +static int +raw_markeor (unix_stream * s __attribute__ ((unused))) { - if (s->ndirty != 0 && - (s->buffer != s->small_buffer || options.all_unbuffered || - s->unbuffered)) - return fd_flush (s); - - return SUCCESS; + return 0; } +static const struct stream_vtable raw_vtable = { + .read = (void *) raw_read, + .write = (void *) raw_write, + .seek = (void *) raw_seek, + .tell = (void *) raw_tell, + .size = (void *) raw_size, + .trunc = (void *) raw_truncate, + .close = (void *) raw_close, + .flush = (void *) raw_flush, + .markeor = (void *) raw_markeor +}; -static try -fd_seek (unix_stream * s, gfc_offset offset) +static int +raw_init (unix_stream * s) { + s->st.vptr = &raw_vtable; - if (s->file_length == -1) - return SUCCESS; - - if (s->physical_offset == offset) /* Are we lucky and avoid syscall? */ - { - s->logical_offset = offset; - return SUCCESS; - } - - if (lseek (s->fd, offset, SEEK_SET) >= 0) - { - s->physical_offset = s->logical_offset = offset; - s->active = 0; - return SUCCESS; - } - - return FAILURE; + s->buffer = NULL; + return 0; } -/* truncate_file()-- Given a unit, truncate the file at the current - * position. Sets the physical location to the new end of the file. - * Returns nonzero on error. */ +/********************************************************************* +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. +*********************************************************************/ -static try -fd_truncate (unix_stream * s) +static int +buf_flush (unix_stream * s) { - /* Non-seekable files, like terminals and fifo's fail the lseek so just - return success, there is nothing to truncate. If its not a pipe there - is a real problem. */ - if (lseek (s->fd, s->logical_offset, SEEK_SET) == -1) - { - if (errno == ESPIPE) - return SUCCESS; - else - return FAILURE; - } + int writelen; - /* Using ftruncate on a seekable special file (like /dev/null) - is undefined, so we treat it as if the ftruncate succeeded. */ -#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; + /* Flushing in read mode means discarding read bytes. */ s->active = 0; - return SUCCESS; -} + if (s->ndirty == 0) + return 0; + + if (s->physical_offset != s->buffer_offset + && lseek (s->fd, s->buffer_offset, SEEK_SET) < 0) + return -1; -/* Similar to memset(), but operating on a stream instead of a string. - Takes care of not using too much memory. */ - -static try -fd_sset (unix_stream * s, int c, size_t n) -{ - size_t bytes_left; - int trans; - void *p; - - bytes_left = n; + writelen = raw_write (s, s->buffer, s->ndirty); - while (bytes_left > 0) - { - /* memset() in chunks of BUFFER_SIZE. */ - trans = (bytes_left < BUFFER_SIZE) ? bytes_left : BUFFER_SIZE; + s->physical_offset = s->buffer_offset + writelen; - p = fd_alloc_w_at (s, &trans, -1); - if (p) - memset (p, c, trans); - else - return FAILURE; + if (s->physical_offset > s->file_length) + s->file_length = s->physical_offset; - bytes_left -= trans; - } + s->ndirty -= writelen; + if (s->ndirty != 0) + return -1; - return SUCCESS; + return 0; } - -/* Stream read function. Avoids using a buffer for big reads. The - interface is like POSIX read(), but the nbytes argument is a - pointer; on return it contains the number of bytes written. The - function return value is the status indicator (0 for success). */ - -static int -fd_read (unix_stream * s, void * buf, size_t * nbytes) +static ssize_t +buf_read (unix_stream * s, void * buf, ssize_t nbyte) { - void *p; - int tmp, status; + if (s->active == 0) + s->buffer_offset = s->logical_offset; - if (*nbytes < BUFFER_SIZE && !s->unbuffered) + /* Is the data we want in the buffer? */ + if (s->logical_offset + nbyte <= s->buffer_offset + s->active + && s->buffer_offset <= s->logical_offset) + memcpy (buf, s->buffer + (s->logical_offset - s->buffer_offset), nbyte); + else { - tmp = *nbytes; - p = fd_alloc_r_at (s, &tmp, -1); - if (p) - { - *nbytes = tmp; - memcpy (buf, p, *nbytes); - return 0; - } + /* First copy the active bytes if applicable, then read the rest + either directly or filling the buffer. */ + char *p; + int nread = 0; + ssize_t to_read, did_read; + gfc_offset new_logical; + + p = (char *) buf; + if (s->logical_offset >= s->buffer_offset + && s->buffer_offset + s->active >= s->logical_offset) + { + nread = s->active - (s->logical_offset - s->buffer_offset); + memcpy (buf, s->buffer + (s->logical_offset - s->buffer_offset), + nread); + p += nread; + } + /* At this point we consider all bytes in the buffer discarded. */ + to_read = nbyte - nread; + new_logical = s->logical_offset + nread; + if (s->physical_offset != new_logical + && lseek (s->fd, new_logical, SEEK_SET) < 0) + return -1; + s->buffer_offset = s->physical_offset = new_logical; + if (to_read <= BUFFER_SIZE/2) + { + did_read = raw_read (s, s->buffer, BUFFER_SIZE); + s->physical_offset += did_read; + s->active = did_read; + did_read = (did_read > to_read) ? to_read : did_read; + memcpy (p, s->buffer, did_read); + } else - { - *nbytes = 0; - return errno; - } + { + did_read = raw_read (s, p, to_read); + s->physical_offset += did_read; + s->active = 0; + } + nbyte = did_read + nread; } + s->logical_offset += nbyte; + return nbyte; +} - /* If the request is bigger than BUFFER_SIZE we flush the buffers - and read directly. */ - if (fd_flush (s) == FAILURE) +static ssize_t +buf_write (unix_stream * s, const void * buf, ssize_t nbyte) +{ + if (s->ndirty == 0) + s->buffer_offset = s->logical_offset; + + /* Does the data fit into the buffer? As a special case, if the + buffer is empty and the request is bigger than BUFFER_SIZE/2, + write directly. This avoids the case where the buffer would have + to be flushed at every write. */ + if (!(s->ndirty == 0 && nbyte > BUFFER_SIZE/2) + && s->logical_offset + nbyte <= s->buffer_offset + BUFFER_SIZE + && s->buffer_offset <= s->logical_offset + && s->buffer_offset + s->ndirty >= s->logical_offset) { - *nbytes = 0; - return errno; + memcpy (s->buffer + (s->logical_offset - s->buffer_offset), buf, nbyte); + int nd = (s->logical_offset - s->buffer_offset) + nbyte; + if (nd > s->ndirty) + s->ndirty = nd; } - - if (is_seekable ((stream *) s) && fd_seek (s, s->logical_offset) == FAILURE) + else { - *nbytes = 0; - return errno; - } + /* Flush, and either fill the buffer with the new data, or if + the request is bigger than the buffer size, write directly + bypassing the buffer. */ + buf_flush (s); + if (nbyte <= BUFFER_SIZE/2) + { + memcpy (s->buffer, buf, nbyte); + s->buffer_offset = s->logical_offset; + s->ndirty += nbyte; + } + else + { + if (s->physical_offset != s->logical_offset) + { + if (lseek (s->fd, s->logical_offset, SEEK_SET) < 0) + return -1; + s->physical_offset = s->logical_offset; + } - status = do_read (s, buf, nbytes); - reset_stream (s, *nbytes); - return status; + nbyte = raw_write (s, buf, nbyte); + s->physical_offset += nbyte; + } + } + s->logical_offset += nbyte; + if (s->logical_offset > s->file_length) + s->file_length = s->logical_offset; + return nbyte; } -/* Stream write function. Avoids using a buffer for big writes. The - interface is like POSIX write(), but the nbytes argument is a - pointer; on return it contains the number of bytes written. The - function return value is the status indicator (0 for success). */ +/* "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 -fd_write (unix_stream * s, const void * buf, size_t * nbytes) +buf_markeor (unix_stream * s) { - 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 (s->unbuffered || s->ndirty >= BUFFER_SIZE / 2) + return buf_flush (s); + return 0; +} - /* If the request is bigger than BUFFER_SIZE we flush the buffers - and write directly. */ - if (fd_flush (s) == FAILURE) +static gfc_offset +buf_seek (unix_stream * s, gfc_offset offset, int whence) +{ + switch (whence) { - *nbytes = 0; - return errno; + case SEEK_SET: + break; + case SEEK_CUR: + offset += s->logical_offset; + break; + case SEEK_END: + offset += s->file_length; + break; + default: + return -1; } - - if (is_seekable ((stream *) s) && fd_seek (s, s->logical_offset) == FAILURE) + if (offset < 0) { - *nbytes = 0; - return errno; + errno = EINVAL; + return -1; } - - status = do_write (s, buf, nbytes); - reset_stream (s, *nbytes); - return status; + s->logical_offset = offset; + return offset; } - -static try -fd_close (unix_stream * s) +static gfc_offset +buf_tell (unix_stream * s) { - if (fd_flush (s) == FAILURE) - return FAILURE; - - if (s->buffer != NULL && s->buffer != s->small_buffer) - free_mem (s->buffer); + return buf_seek (s, 0, SEEK_CUR); +} - if (s->fd != STDOUT_FILENO && s->fd != STDERR_FILENO) - { - if (close (s->fd) < 0) - return FAILURE; - } +static gfc_offset +buf_size (unix_stream * s) +{ + return s->file_length; +} - free_mem (s); +static int +buf_truncate (unix_stream * s, gfc_offset length) +{ + int r; - return SUCCESS; + if (buf_flush (s) != 0) + return -1; + r = raw_truncate (s, length); + if (r == 0) + s->file_length = length; + return r; } +static int +buf_close (unix_stream * s) +{ + if (buf_flush (s) != 0) + return -1; + free (s->buffer); + return raw_close (s); +} + +static const struct stream_vtable buf_vtable = { + .read = (void *) buf_read, + .write = (void *) buf_write, + .seek = (void *) buf_seek, + .tell = (void *) buf_tell, + .size = (void *) buf_size, + .trunc = (void *) buf_truncate, + .close = (void *) buf_close, + .flush = (void *) buf_flush, + .markeor = (void *) buf_markeor +}; -static void -fd_open (unix_stream * s) +static int +buf_init (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.trunc = (void *) fd_truncate; - s->st.read = (void *) fd_read; - s->st.write = (void *) fd_write; - s->st.set = (void *) fd_sset; + s->st.vptr = &buf_vtable; - s->buffer = NULL; + s->buffer = xmalloc (BUFFER_SIZE); + return 0; } - - /********************************************************************* memory stream functions - These are used for internal files @@ -903,37 +699,52 @@ fd_open (unix_stream * s) *********************************************************************/ - -static char * -mem_alloc_r_at (int_stream * s, int *len, gfc_offset where) +char * +mem_alloc_r (stream * strm, int * len) { + unix_stream * s = (unix_stream *) strm; gfc_offset n; - - if (where == -1) - where = s->logical_offset; + gfc_offset where = s->logical_offset; if (where < s->buffer_offset || where > s->buffer_offset + s->active) return NULL; + n = s->buffer_offset + s->active - where; + if (*len > n) + *len = n; + s->logical_offset = where + *len; + return s->buffer + (where - s->buffer_offset); +} + + +char * +mem_alloc_r4 (stream * strm, int * len) +{ + unix_stream * s = (unix_stream *) strm; + gfc_offset n; + gfc_offset where = s->logical_offset; + + if (where < s->buffer_offset || where > s->buffer_offset + s->active) + return NULL; + n = s->buffer_offset + s->active - where; if (*len > n) *len = n; - return s->buffer + (where - s->buffer_offset); + s->logical_offset = where + *len; + + return s->buffer + (where - s->buffer_offset) * 4; } -static char * -mem_alloc_w_at (int_stream * s, int *len, gfc_offset where) +char * +mem_alloc_w (stream * strm, int * len) { + unix_stream * s = (unix_stream *) strm; gfc_offset m; - - assert (*len >= 0); /* Negative values not allowed. */ - - if (where == -1) - where = s->logical_offset; + gfc_offset where = s->logical_offset; m = where + *len; @@ -949,157 +760,240 @@ mem_alloc_w_at (int_stream * s, int *len, gfc_offset where) } -/* Stream read function for internal units. This is not actually used - at the moment, as all internal IO is formatted and the formatted IO - routines use mem_alloc_r_at. */ +gfc_char4_t * +mem_alloc_w4 (stream * strm, int * len) +{ + unix_stream * s = (unix_stream *) strm; + gfc_offset m; + gfc_offset where = s->logical_offset; + gfc_char4_t *result = (gfc_char4_t *) s->buffer; -static int -mem_read (int_stream * s, void * buf, size_t * nbytes) + m = where + *len; + + if (where < s->buffer_offset) + return NULL; + + if (m > s->file_length) + return NULL; + + s->logical_offset = m; + return &result[where - s->buffer_offset]; +} + + +/* Stream read function for character(kind=1) internal units. */ + +static ssize_t +mem_read (stream * s, void * buf, ssize_t nbytes) { void *p; - int tmp; + int nb = nbytes; - tmp = *nbytes; - p = mem_alloc_r_at (s, &tmp, -1); + p = mem_alloc_r (s, &nb); if (p) { - *nbytes = tmp; - memcpy (buf, p, *nbytes); - return 0; + memcpy (buf, p, nb); + return (ssize_t) nb; } else + return 0; +} + + +/* Stream read function for chracter(kind=4) internal units. */ + +static ssize_t +mem_read4 (stream * s, void * buf, ssize_t nbytes) +{ + void *p; + int nb = nbytes; + + p = mem_alloc_r4 (s, &nb); + if (p) { - *nbytes = 0; - return errno; + memcpy (buf, p, nb * 4); + return (ssize_t) nb; } + else + return 0; } -/* Stream write function for internal units. This is not actually used - at the moment, as all internal IO is formatted and the formatted IO - routines use mem_alloc_w_at. */ +/* Stream write function for character(kind=1) internal units. */ -static int -mem_write (int_stream * s, const void * buf, size_t * nbytes) +static ssize_t +mem_write (stream * s, const void * buf, ssize_t nbytes) { void *p; - int tmp; + int nb = nbytes; - errno = 0; - - tmp = *nbytes; - p = mem_alloc_w_at (s, &tmp, -1); + p = mem_alloc_w (s, &nb); if (p) { - *nbytes = tmp; - memcpy (p, buf, *nbytes); - return 0; + memcpy (p, buf, nb); + return (ssize_t) nb; } else + return 0; +} + + +/* Stream write function for character(kind=4) internal units. */ + +static ssize_t +mem_write4 (stream * s, const void * buf, ssize_t nwords) +{ + gfc_char4_t *p; + int nw = nwords; + + p = mem_alloc_w4 (s, &nw); + if (p) { - *nbytes = 0; - return errno; + while (nw--) + *p++ = (gfc_char4_t) *((char *) buf); + return nwords; } + else + return 0; } -static int -mem_seek (int_stream * s, gfc_offset offset) +static gfc_offset +mem_seek (stream * strm, gfc_offset offset, int whence) { + unix_stream * s = (unix_stream *) strm; + switch (whence) + { + case SEEK_SET: + break; + case SEEK_CUR: + offset += s->logical_offset; + break; + case SEEK_END: + offset += s->file_length; + break; + default: + return -1; + } + + /* Note that for internal array I/O it's actually possible to have a + negative offset, so don't check for that. */ if (offset > s->file_length) { - errno = ESPIPE; - return FAILURE; + errno = EINVAL; + return -1; } s->logical_offset = offset; - return SUCCESS; + + /* Returning < 0 is the error indicator for sseek(), so return 0 if + offset is negative. Thus if the return value is 0, the caller + has to use stell() to get the real value of logical_offset. */ + if (offset >= 0) + return offset; + return 0; } -static try -mem_set (int_stream * s, int c, size_t n) +static gfc_offset +mem_tell (stream * s) { - 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; + return ((unix_stream *)s)->logical_offset; } static int -mem_truncate (int_stream * s __attribute__ ((unused))) +mem_truncate (unix_stream * s __attribute__ ((unused)), + gfc_offset length __attribute__ ((unused))) { - return SUCCESS; + return 0; } -static try -mem_close (int_stream * s) +static int +mem_flush (unix_stream * s __attribute__ ((unused))) { - if (s != NULL) - free_mem (s); - - return SUCCESS; + return 0; } -static try -mem_sfree (int_stream * s __attribute__ ((unused))) +static int +mem_close (unix_stream * s) { - return SUCCESS; -} + free (s); + return 0; +} +static const struct stream_vtable mem_vtable = { + .read = (void *) mem_read, + .write = (void *) mem_write, + .seek = (void *) mem_seek, + .tell = (void *) mem_tell, + /* buf_size is not a typo, we just reuse an identical + implementation. */ + .size = (void *) buf_size, + .trunc = (void *) mem_truncate, + .close = (void *) mem_close, + .flush = (void *) mem_flush, + .markeor = (void *) raw_markeor +}; + +static const struct stream_vtable mem4_vtable = { + .read = (void *) mem_read4, + .write = (void *) mem_write4, + .seek = (void *) mem_seek, + .tell = (void *) mem_tell, + /* buf_size is not a typo, we just reuse an identical + implementation. */ + .size = (void *) buf_size, + .trunc = (void *) mem_truncate, + .close = (void *) mem_close, + .flush = (void *) mem_flush, + .markeor = (void *) raw_markeor +}; /********************************************************************* Public functions -- A reimplementation of this module needs to define functional equivalents of the following. *********************************************************************/ -/* empty_internal_buffer()-- Zero the buffer of Internal file */ +/* open_internal()-- Returns a stream structure from a character(kind=1) + internal file */ -void -empty_internal_buffer(stream *strm) +stream * +open_internal (char *base, int length, gfc_offset offset) { - int_stream * s = (int_stream *) strm; - memset(s->buffer, ' ', s->file_length); + unix_stream *s; + + s = xcalloc (1, sizeof (unix_stream)); + + s->buffer = base; + s->buffer_offset = offset; + + s->active = s->file_length = length; + + s->st.vptr = &mem_vtable; + + return (stream *) s; } -/* open_internal()-- Returns a stream structure from an internal file */ +/* open_internal4()-- Returns a stream structure from a character(kind=4) + internal file */ stream * -open_internal (char *base, int length) +open_internal4 (char *base, int length, gfc_offset offset) { - int_stream *s; + unix_stream *s; - s = get_mem (sizeof (int_stream)); - memset (s, '\0', sizeof (int_stream)); + s = xcalloc (1, sizeof (unix_stream)); s->buffer = base; - s->buffer_offset = 0; + s->buffer_offset = offset; - s->logical_offset = 0; - s->active = s->file_length = length; + s->active = s->file_length = length * sizeof (gfc_char4_t); - 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.trunc = (void *) mem_truncate; - s->st.read = (void *) mem_read; - s->st.write = (void *) mem_write; - s->st.set = (void *) mem_set; + s->st.vptr = &mem4_vtable; return (stream *) s; } @@ -1109,32 +1003,49 @@ open_internal (char *base, int length) * around it. */ static stream * -fd_to_stream (int fd, int prot) +fd_to_stream (int fd, bool unformatted) { struct stat statbuf; unix_stream *s; - s = get_mem (sizeof (unix_stream)); - memset (s, '\0', sizeof (unix_stream)); + s = xcalloc (1, sizeof (unix_stream)); s->fd = fd; - s->buffer_offset = 0; - s->physical_offset = 0; - s->logical_offset = 0; - s->prot = prot; /* Get the current length of the file. */ - 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); + 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; + } - fd_open (s); + s->st_dev = statbuf.st_dev; + s->st_ino = statbuf.st_ino; + s->file_length = statbuf.st_size; + + /* Only use buffered IO for regular files. */ + if (S_ISREG (statbuf.st_mode) + && !options.all_unbuffered + && !(options.unbuffered_preconnected && + (s->fd == STDIN_FILENO + || s->fd == STDOUT_FILENO + || s->fd == STDERR_FILENO))) + buf_init (s); + else + { + if (unformatted) + { + s->unbuffered = true; + buf_init (s); + } + else + raw_init (s); + } return (stream *) s; } @@ -1158,103 +1069,223 @@ unit_to_fd (int unit) } -/* 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))) { - len = fstrlen (fstring, len); - if (len >= PATH_MAX) - return 1; - - 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 } -/* tempfile()-- Generate a temporary filename for a scratch file and - * open it. mkstemp() opens the file for reading and writing, but the - * library mode prevents anything that is not allowed. The descriptor - * is returned, which is -1 on error. The template is pointed to by - * opp->file, which is copied into the unit structure - * and freed later. */ +/* Helper function for tempfile(). Tries to open a temporary file in + the directory specified by tempdir. If successful, the file name is + stored in fname and the descriptor returned. Returns -1 on + failure. */ static int -tempfile (st_parameter_open *opp) +tempfile_open (const char *tempdir, char **fname) { - const char *tempdir; - char *template; int fd; + const char *slash = "/"; +#if defined(HAVE_UMASK) && defined(HAVE_MKSTEMP) + mode_t mode_mask; +#endif - tempdir = getenv ("GFORTRAN_TMPDIR"); - if (tempdir == NULL) - tempdir = getenv ("TMP"); - if (tempdir == NULL) - tempdir = getenv ("TEMP"); - if (tempdir == NULL) - tempdir = DEFAULT_TEMPDIR; + if (!tempdir) + return -1; - template = get_mem (strlen (tempdir) + 20); + /* Check for the special case that tempdir ends with a slash or + backslash. */ + size_t tempdirlen = strlen (tempdir); + if (*tempdir == 0 || tempdir[tempdirlen - 1] == '/' +#ifdef __MINGW32__ + || tempdir[tempdirlen - 1] == '\\' +#endif + ) + slash = ""; - sprintf (template, "%s/gfortrantmpXXXXXX", tempdir); + // Take care that the template is longer in the mktemp() branch. + char * template = xmalloc (tempdirlen + 23); #ifdef HAVE_MKSTEMP + 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 -#else /* HAVE_MKSTEMP */ +#ifdef HAVE_UMASK + (void) umask (mode_mask); +#endif - if (mktemp (template)) - do +#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) - 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); + flags |= O_BINARY; #endif - while (!(fd == -1 && errno == EEXIST) && mktemp (template)); - else - fd = -1; +#ifdef O_CLOEXEC + flags |= O_CLOEXEC; +#endif + do + { + snprintf (template, tempdirlen + 23, "%s%sgfortrantmpaaaXXXXXX", + tempdir, slash); + if (count > 0) + { + int c = count; + template[tempdirlen + slashlen + 13] = 'a' + (c% 26); + c /= 26; + template[tempdirlen + slashlen + 12] = 'a' + (c % 26); + c /= 26; + template[tempdirlen + slashlen + 11] = 'a' + (c % 26); + if (c >= 26) + break; + } + + if (!mktemp (template)) + { + errno = EEXIST; + count++; + continue; + } + 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 */ - if (fd < 0) - free_mem (template); - else + *fname = template; + return fd; +} + + +/* tempfile()-- Generate a temporary filename for a scratch file and + * open it. mkstemp() opens the file for reading and writing, but the + * library mode prevents anything that is not allowed. The descriptor + * is returned, which is -1 on error. The template is pointed to by + * opp->file, which is copied into the unit structure + * and freed later. */ + +static int +tempfile (st_parameter_open *opp) +{ + const char *tempdir; + char *fname; + int fd = -1; + + tempdir = secure_getenv ("TMPDIR"); + fd = tempfile_open (tempdir, &fname); +#ifdef __MINGW32__ + if (fd == -1) + { + char buffer[MAX_PATH + 1]; + DWORD ret; + ret = GetTempPath (MAX_PATH, buffer); + /* If we are not able to get a temp-directory, we use + current directory. */ + if (ret > MAX_PATH || !ret) + buffer[0] = 0; + else + buffer[ret] = 0; + tempdir = strdup (buffer); + fd = tempfile_open (tempdir, &fname); + } +#elif defined(__CYGWIN__) + if (fd == -1) + { + tempdir = secure_getenv ("TMP"); + fd = tempfile_open (tempdir, &fname); + } + if (fd == -1) { - opp->file = template; - opp->file_len = strlen (template); /* Don't include trailing nul */ + tempdir = secure_getenv ("TEMP"); + fd = tempfile_open (tempdir, &fname); } +#endif + if (fd == -1) + fd = tempfile_open (P_tmpdir, &fname); + + opp->file = fname; + opp->file_len = strlen (fname); /* Don't include trailing nul */ return fd; } -/* 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[PATH_MAX + 1]; int mode; int rwflag; - int crflag; + int crflag, crflag2; int fd; - if (unpack_filename (path, opp->file, opp->file_len)) +#ifdef __CYGWIN__ + if (opp->file_len == 7) { - errno = ENOENT; /* Fake an OS error */ - return -1; + if (strncmp (path, "CONOUT$", 7) == 0 + || strncmp (path, "CONERR$", 7) == 0) + { + fd = open ("/dev/conout", O_WRONLY); + flags->action = ACTION_WRITE; + return fd; + } + } + + if (opp->file_len == 6 && strncmp (path, "CONIN$", 6) == 0) + { + fd = open ("/dev/conin", O_RDONLY); + flags->action = ACTION_READ; + return fd; + } +#endif + + +#ifdef __MINGW32__ + if (opp->file_len == 7) + { + if (strncmp (path, "CONOUT$", 7) == 0 + || strncmp (path, "CONERR$", 7) == 0) + { + fd = open ("CONOUT$", O_WRONLY); + flags->action = ACTION_WRITE; + return fd; + } } - rwflag = 0; + if (opp->file_len == 6 && strncmp (path, "CONIN$", 6) == 0) + { + fd = open ("CONIN$", O_RDONLY); + flags->action = ACTION_READ; + return fd; + } +#endif switch (flags->action) { @@ -1286,8 +1317,10 @@ regular_file (st_parameter_open *opp, unit_flags *flags) break; case STATUS_UNKNOWN: - case STATUS_SCRATCH: - crflag = O_CREAT; + if (rwflag == O_RDONLY) + crflag = 0; + else + crflag = O_CREAT; break; case STATUS_REPLACE: @@ -1295,6 +1328,8 @@ regular_file (st_parameter_open *opp, unit_flags *flags) break; default: + /* Note: STATUS_SCRATCH is handled by tempfile () and should + never be seen here. */ internal_error (&opp->common, "regular_file(): Bad status"); } @@ -1304,6 +1339,10 @@ regular_file (st_parameter_open *opp, unit_flags *flags) 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) @@ -1314,20 +1353,24 @@ regular_file (st_parameter_open *opp, unit_flags *flags) 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 */ + return fd; /* success */ } - if (errno != EACCES) - return fd; /* failure */ + if (errno != EACCES && errno != EPERM && errno != ENOENT) + return fd; /* failure */ /* retry for write-only access */ rwflag = O_WRONLY; @@ -1335,12 +1378,24 @@ regular_file (st_parameter_open *opp, unit_flags *flags) if (fd >=0) { flags->action = ACTION_WRITE; - return fd; /* success */ + return fd; /* success */ } - return fd; /* failure */ + return fd; /* failure */ } +/* 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. */ @@ -1348,13 +1403,13 @@ regular_file (st_parameter_open *opp, unit_flags *flags) stream * open_external (st_parameter_open *opp, unit_flags *flags) { - int fd, prot; + int fd; if (flags->status == STATUS_SCRATCH) { fd = tempfile (opp); if (flags->action == ACTION_UNSPECIFIED) - flags->action = ACTION_READWRITE; + flags->action = ACTION_READWRITE; #if HAVE_UNLINK_OPEN_FILE /* We can unlink scratch files now and it will go away when closed. */ @@ -1367,31 +1422,16 @@ open_external (st_parameter_open *opp, unit_flags *flags) /* 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); - 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); + return fd_to_stream (fd, flags->form == FORM_UNFORMATTED); } @@ -1401,7 +1441,7 @@ open_external (st_parameter_open *opp, unit_flags *flags) stream * input_stream (void) { - return fd_to_stream (STDIN_FILENO, PROT_READ); + return fd_to_stream (STDIN_FILENO, false); } @@ -1417,9 +1457,7 @@ output_stream (void) setmode (STDOUT_FILENO, O_BINARY); #endif - s = fd_to_stream (STDOUT_FILENO, PROT_WRITE); - if (options.unbuffered_preconnected) - ((unix_stream *) s)->unbuffered = 1; + s = fd_to_stream (STDOUT_FILENO, false); return s; } @@ -1436,68 +1474,11 @@ error_stream (void) setmode (STDERR_FILENO, O_BINARY); #endif - s = fd_to_stream (STDERR_FILENO, PROT_WRITE); - if (options.unbuffered_preconnected) - ((unix_stream *) s)->unbuffered = 1; + s = fd_to_stream (STDERR_FILENO, false); return s; } -/* st_vprintf()-- vprintf function for error output. To avoid buffer - overruns, we limit the length of the buffer to ST_VPRINTF_SIZE. 2k - is big enough to completely fill a 80x25 terminal, so it shuld be - OK. We use a direct write() because it is simpler and least likely - to be clobbered by memory corruption. Writing an error message - longer than that is an error. */ - -#define ST_VPRINTF_SIZE 2048 - -int -st_vprintf (const char *format, va_list ap) -{ - static char buffer[ST_VPRINTF_SIZE]; - int written; - int fd; - - fd = options.use_stderr ? STDERR_FILENO : STDOUT_FILENO; -#ifdef HAVE_VSNPRINTF - written = vsnprintf(buffer, ST_VPRINTF_SIZE, format, ap); -#else - written = vsprintf(buffer, format, ap); - - if (written >= ST_VPRINTF_SIZE-1) - { - /* The error message was longer than our buffer. Ouch. Because - we may have messed up things badly, report the error and - quit. */ -#define ERROR_MESSAGE "Internal error: buffer overrun in st_vprintf()\n" - write (fd, buffer, ST_VPRINTF_SIZE-1); - write (fd, ERROR_MESSAGE, strlen(ERROR_MESSAGE)); - sys_exit(2); -#undef ERROR_MESSAGE - - } -#endif - - written = write (fd, buffer, written); - return written; -} - -/* st_printf()-- printf() function for error output. This just calls - st_vprintf() to do the actual work. */ - -int -st_printf (const char *format, ...) -{ - int written; - va_list ap; - va_start (ap, format); - written = st_vprintf(format, ap); - va_end (ap); - return written; -} - - /* compare_file_filename()-- Given an open stream and a fortran string * that is a filename, figure out if the file is the same as the * filename. */ @@ -1505,28 +1486,31 @@ st_printf (const char *format, ...) int compare_file_filename (gfc_unit *u, const char *name, int len) { - char path[PATH_MAX + 1]; - struct stat st1; + struct stat st; + int ret; #ifdef HAVE_WORKING_STAT - struct stat st2; + unix_stream *s; #else # ifdef __MINGW32__ uint64_t id1, id2; # 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, &st1) < 0) - return 0; + if (stat (path, &st) < 0) + { + ret = 0; + goto done; + } #ifdef HAVE_WORKING_STAT - fstat (((unix_stream *) (u->s))->fd, &st2); - return (st1.st_dev == st2.st_dev) && (st1.st_ino == st2.st_ino); + s = (unix_stream *) (u->s); + ret = (st.st_dev == s->st_dev) && (st.st_ino == s->st_ino); + goto done; #else # ifdef __MINGW32__ @@ -1536,13 +1520,16 @@ compare_file_filename (gfc_unit *u, const char *name, int len) 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; } @@ -1550,8 +1537,8 @@ compare_file_filename (gfc_unit *u, const char *name, int len) # 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() */ @@ -1568,10 +1555,12 @@ find_file0 (gfc_unit *u, FIND_FILE0_DECL) 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; + if (u->s != NULL) + { + unix_stream *s = (unix_stream *) (u->s); + if (st[0].st_dev == s->st_dev && st[0].st_ino == s->st_ino) + return u; + } #else # ifdef __MINGW32__ if (u->s && ((id1 = id_from_fd (((unix_stream *) u->s)->fd)) || id1)) @@ -1581,7 +1570,7 @@ find_file0 (gfc_unit *u, FIND_FILE0_DECL) } else # endif - if (compare_string (u->file_len, u->file, file_len, file) == 0) + if (strcmp (u->filename, path) == 0) return u; #endif @@ -1603,21 +1592,22 @@ find_file0 (gfc_unit *u, FIND_FILE0_DECL) gfc_unit * find_file (const char *file, gfc_charlen_type file_len) { - char path[PATH_MAX + 1]; - struct stat st[2]; + struct stat st[1]; gfc_unit *u; - uint64_t id; +#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); -#else - id = 0; #endif __gthread_mutex_lock (&unit_lock); @@ -1630,7 +1620,7 @@ retry: { /* assert (u->closed == 0); */ __gthread_mutex_unlock (&unit_lock); - return u; + goto done; } inc_waiting_locked (u); @@ -1644,12 +1634,14 @@ retry: __gthread_mutex_lock (&unit_lock); __gthread_mutex_unlock (&u->lock); if (predec_waiting_locked (u) == 0) - free_mem (u); + free (u); goto retry; } dec_waiting_unlocked (u); } + done: + free (path); return u; } @@ -1669,7 +1661,7 @@ flush_all_units_1 (gfc_unit *u, int min_unit) if (__gthread_mutex_trylock (&u->lock)) return u; if (u->s) - flush (u->s); + sflush (u->s); __gthread_mutex_unlock (&u->lock); } u = u->right; @@ -1699,7 +1691,7 @@ flush_all_units (void) if (u->closed == 0) { - flush (u->s); + sflush (u->s); __gthread_mutex_lock (&unit_lock); __gthread_mutex_unlock (&u->lock); (void) predec_waiting_locked (u); @@ -1709,62 +1701,20 @@ flush_all_units (void) __gthread_mutex_lock (&unit_lock); __gthread_mutex_unlock (&u->lock); if (predec_waiting_locked (u) == 0) - free_mem (u); + free (u); } } while (1); } -/* stream_at_bof()-- Returns nonzero if the stream is at the beginning - * of the file. */ - -int -stream_at_bof (stream * s) -{ - unix_stream *us; - - if (!is_seekable (s)) - return 0; - - us = (unix_stream *) s; - - return us->logical_offset == 0; -} - - -/* stream_at_eof()-- Returns nonzero if the stream is at the end - * of the file. */ - -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; -} - - /* delete_file()-- Given a unit structure, delete the file associated * with the unit. Returns nonzero if something went wrong. */ int delete_file (gfc_unit * u) { - char path[PATH_MAX + 1]; - - if (unpack_filename (path, u->file, u->file_len)) - { /* Shouldn't be possible */ - errno = ENOENT; - return 1; - } - - return unlink (path); + return unlink (u->filename); } @@ -1774,20 +1724,27 @@ delete_file (gfc_unit * u) int file_exists (const char *file, gfc_charlen_type file_len) { - char path[PATH_MAX + 1]; - struct stat statbuf; + char *path = fc_strdup (file, file_len); + int res = !(access (path, F_OK)); + free (path); + return res; +} - if (unpack_filename (path, file, file_len)) - return 0; - if (stat (path, &statbuf) < 0) - return 0; +/* file_size()-- Returns the size of the file. */ - return 1; +GFC_IO_INT +file_size (const char *file, gfc_charlen_type file_len) +{ + char *path = fc_strdup (file, file_len); + struct stat statbuf; + int err = stat (path, &statbuf); + free (path); + if (err == -1) + return -1; + return (GFC_IO_INT) statbuf.st_size; } - - static const char yes[] = "YES", no[] = "NO", unknown[] = "UNKNOWN"; /* inquire_sequential()-- Given a fortran string, determine if the @@ -1797,16 +1754,20 @@ 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) + 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_ISCHR (statbuf.st_mode) || S_ISFIFO (statbuf.st_mode)) - return yes; + return unknown; if (S_ISDIR (statbuf.st_mode) || S_ISBLK (statbuf.st_mode)) return no; @@ -1821,15 +1782,19 @@ inquire_sequential (const char *string, int len) 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) + 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)) - return yes; + return unknown; if (S_ISDIR (statbuf.st_mode) || S_ISCHR (statbuf.st_mode) || S_ISFIFO (statbuf.st_mode)) @@ -1845,17 +1810,21 @@ inquire_direct (const char *string, int len) 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) + 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) || S_ISCHR (statbuf.st_mode) || S_ISFIFO (statbuf.st_mode)) - return yes; + return unknown; if (S_ISDIR (statbuf.st_mode)) return no; @@ -1874,46 +1843,18 @@ inquire_unformatted (const char *string, int len) } -#ifndef HAVE_ACCESS - -#ifndef W_OK -#define W_OK 2 -#endif - -#ifndef R_OK -#define R_OK 4 -#endif - -/* Fallback implementation of access() on systems that don't have it. - Only modes R_OK and W_OK are used in this file. */ - -static int -fallback_access (const char *path, int mode) -{ - if ((mode & R_OK) && open (path, O_RDONLY) < 0) - return -1; - - if ((mode & W_OK) && open (path, O_WRONLY) < 0) - return -1; - - return 0; -} - -#undef access -#define access fallback_access -#endif - - /* inquire_access()-- Given a fortran string, determine if the file is * suitable for access. */ 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) + 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; @@ -1950,72 +1891,36 @@ inquire_readwrite (const char *string, int len) } -/* file_length()-- Return the file length in bytes, -1 if unknown */ - -gfc_offset -file_length (stream * s) -{ - return ((unix_stream *) s)->file_length; -} - - -/* file_position()-- Return the current position of the file */ - -gfc_offset -file_position (stream *s) -{ - return ((unix_stream *) s)->logical_offset; -} - - -/* is_seekable()-- Return nonzero if the stream is seekable, zero if - * it is not */ - -int -is_seekable (stream *s) -{ - /* By convention, if file_length == -1, the file is not - seekable. */ - return ((unix_stream *) s)->file_length!=-1; -} - - -/* is_special()-- Return nonzero if the stream is not a regular file. */ - -int -is_special (stream *s) -{ - return ((unix_stream *) s)->special_file; -} - - -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 __attribute__ ((unused))) -{ -#ifdef HAVE_TTYNAME - return ttyname (((unix_stream *) s)->fd); +int +stream_ttyname (stream *s __attribute__ ((unused)), + char * buf __attribute__ ((unused)), + size_t buflen __attribute__ ((unused))) +{ +#ifdef HAVE_TTYNAME_R + return ttyname_r (((unix_stream *) s)->fd, buf, buflen); +#elif defined HAVE_TTYNAME + char *p; + size_t plen; + p = ttyname (((unix_stream *) s)->fd); + if (!p) + return errno; + plen = strlen (p); + if (buflen < plen) + plen = buflen; + memcpy (buf, p, plen); + return 0; #else - return NULL; + return ENOSYS; #endif } -gfc_offset -stream_offset (stream *s) -{ - return (((unix_stream *) s)->logical_offset); -} + /* How files are stored: This is an operating-system specific issue, @@ -2038,13 +1943,13 @@ stream_offset (stream *s) the solution used by f2c. Each record contains a pair of length markers: - Length of record n in bytes - Data of record n - Length of record n in bytes + Length of record n in bytes + Data of record n + Length of record n in bytes - Length of record n+1 in bytes - Data of record n+1 - Length of record n+1 in bytes + Length of record n+1 in bytes + Data of record n+1 + Length of record n+1 in bytes The length is stored at the end of a record to allow backspacing to the previous record. Between data transfer statements, the file pointer