X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=libgfortran%2Fio%2Funix.c;h=cdac0d7dfdd6d6311c1a9a366008284f738e8de6;hb=7d86687017fb9bfa58571cfc46f786f539ba2601;hp=78ed0f718f8172ae6222b5e6fb8c67f5fb829d2a;hpb=a54b1ce7c5838777cadd23576ff60845a322ec8f;p=pf3gnuchains%2Fgcc-fork.git diff --git a/libgfortran/io/unix.c b/libgfortran/io/unix.c index 78ed0f718f8..cdac0d7dfdd 100644 --- a/libgfortran/io/unix.c +++ b/libgfortran/io/unix.c @@ -1,4 +1,5 @@ -/* Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc. +/* Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 + Free Software Foundation, Inc. Contributed by Andy Vaught This file is part of the GNU Fortran 95 runtime library (libgfortran). @@ -24,8 +25,8 @@ 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, 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. */ +the Free Software Foundation, 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. */ /* Unix stream I/O module */ @@ -35,24 +36,23 @@ Boston, MA 02111-1307, USA. */ #include #include +#include #include #include +#include -#ifdef HAVE_SYS_MMAN_H -#include -#endif #include #include #include "libgfortran.h" #include "io.h" -#ifndef PATH_MAX -#define PATH_MAX 1024 +#ifndef SSIZE_MAX +#define SSIZE_MAX SHRT_MAX #endif -#ifndef MAP_FAILED -#define MAP_FAILED ((void *) -1) +#ifndef PATH_MAX +#define PATH_MAX 1024 #endif #ifndef PROT_READ @@ -81,6 +81,42 @@ Boston, MA 02111-1307, USA. */ #define S_IWOTH 0 #endif + +/* Unix stream I/O module */ + +#define 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. */ + + char *buffer; + 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 */ + + unsigned unbuffered:1; + + char small_buffer[BUFFER_SIZE]; + +} +unix_stream; + +extern stream *init_error_stream (unix_stream *); +internal_proto(init_error_stream); + + /* This implementation of stream I/O is based on the paper: * * "Exploiting the advantages of mapped files for stream I/O", @@ -117,33 +153,6 @@ Boston, MA 02111-1307, USA. */ * 'where' parameter and use the current file pointer. */ -#define 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. */ - - char *buffer; - 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 */ - - unsigned unbuffered:1, mmaped:1; - - char small_buffer[BUFFER_SIZE]; - -} -unix_stream; - /*move_pos_offset()-- Move the record pointer right or left *relative to current position */ @@ -153,26 +162,22 @@ move_pos_offset (stream* st, int pos_off) unix_stream * str = (unix_stream*)st; if (pos_off < 0) { - str->active += pos_off; - if (str->active < 0) - str->active = 0; - - str->logical_offset += pos_off; + str->logical_offset += pos_off; - if (str->dirty_offset+str->ndirty > str->logical_offset) + if (str->dirty_offset + str->ndirty > str->logical_offset) { - if (str->ndirty + pos_off > 0) - str->ndirty += pos_off ; + if (str->ndirty + pos_off > 0) + str->ndirty += pos_off; else { str->dirty_offset += pos_off + pos_off; - str->ndirty = 0 ; + str->ndirty = 0; } } - return pos_off ; + return pos_off; } - return 0 ; + return 0; } @@ -219,59 +224,133 @@ fix_fd (int fd) return fd; } +int +is_preconnected (stream * s) +{ + int fd; -/* write()-- Write a buffer to a descriptor, allowing for short writes */ + fd = ((unix_stream *) s)->fd; + if (fd == STDIN_FILENO || fd == STDOUT_FILENO || fd == STDERR_FILENO) + return 1; + else + return 0; +} -static int -writen (int fd, char *buffer, int len) +/* If the stream corresponds to a preconnected unit, we flush the + corresponding C stream. This is bugware for mixed C-Fortran codes + where the C code doesn't flush I/O before returning. */ +void +flush_if_preconnected (stream * s) { - int n, n0; + int fd; - n0 = len; + 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); +} - while (len > 0) - { - n = write (fd, buffer, len); - if (n < 0) - return n; - buffer += n; - len -= n; - } +/* Reset a stream after reading/writing. Assumes that the buffers have + been flushed. */ - return n0; +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; } -#if 0 -/* readn()-- Read bytes into a buffer, allowing for short reads. If - * fewer than len bytes are returned, it is because we've hit the end - * of file. */ +/* 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. */ static int -readn (int fd, char *buffer, int len) +do_read (unix_stream * s, void * buf, size_t * nbytes) { - int nread, n; + 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; + } - nread = 0; + *nbytes -= bytes_left; + return status; +} - while (len > 0) - { - n = read (fd, buffer, len); - if (n < 0) - return n; - if (n == 0) - return nread; +/* Write a buffer to a stream, allowing for short writes. */ - buffer += n; - nread += n; - len -= n; +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; + + /* 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); + if (trans < 0) + { + if (errno == EINTR) + continue; + else + { + status = errno; + break; + } + } + buf_st += trans; + bytes_left -= trans; } - return nread; + *nbytes -= bytes_left; + return status; } -#endif /* get_oserror()-- Get the most recent operating system error. For @@ -284,41 +363,39 @@ get_oserror (void) } -/* sys_exit()-- Terminate the program with an exit code */ - -void -sys_exit (int code) -{ - exit (code); -} - - /********************************************************************* File descriptor stream functions *********************************************************************/ + /* fd_flush()-- Write bytes that need to be written */ static try fd_flush (unix_stream * s) { - if (s->ndirty == 0) - return SUCCESS;; + size_t writelen; - if (s->physical_offset != s->dirty_offset && + 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; - if (writen (s->fd, s->buffer + (s->dirty_offset - s->buffer_offset), - s->ndirty) < 0) + 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 + s->ndirty; + 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 = 0; + s->file_length = s->physical_offset; + + s->ndirty -= writelen; + if (s->ndirty != 0) + return FAILURE; return SUCCESS; } @@ -329,7 +406,8 @@ fd_flush (unix_stream * s) * to come next. */ static void -fd_alloc (unix_stream * s, gfc_offset where, int *len) +fd_alloc (unix_stream * s, gfc_offset where, + int *len __attribute__ ((unused))) { char *new_buffer; int n, read_len; @@ -371,7 +449,6 @@ fd_alloc (unix_stream * s, gfc_offset where, int *len) s->buffer = new_buffer; s->len = read_len; - s->mmaped = 0; } @@ -383,7 +460,6 @@ static char * fd_alloc_r_at (unix_stream * s, int *len, gfc_offset where) { gfc_offset m; - int n; if (where == -1) where = s->logical_offset; @@ -405,13 +481,32 @@ fd_alloc_r_at (unix_stream * s, int *len, gfc_offset where) if (s->physical_offset != m && lseek (s->fd, m, SEEK_SET) < 0) return NULL; - n = read (s->fd, s->buffer + s->active, s->len - s->active); - if (n < 0) - return NULL; + /* do_read() hangs on read from terminals for *BSD-systems. Only + use read() in that case. */ + + 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 = where + 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 = where + n; + s->physical_offset = where + n; + s->active += n; + } - s->active += n; if (s->active < *len) *len = s->active; /* Bytes actually available */ @@ -468,8 +563,10 @@ fd_alloc_w_at (unix_stream * s, int *len, gfc_offset where) s->logical_offset = where + *len; - if (where + *len > s->file_length) - s->file_length = 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) @@ -491,12 +588,27 @@ fd_sfree (unix_stream * s) } -static int +static try fd_seek (unix_stream * s, gfc_offset offset) { - s->physical_offset = s->logical_offset = offset; - return (lseek (s->fd, offset, SEEK_SET) < 0) ? FAILURE : SUCCESS; + 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; } @@ -507,256 +619,204 @@ fd_seek (unix_stream * s, gfc_offset offset) static try fd_truncate (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) - return FAILURE; - - /* non-seekable files, like terminals and fifo's fail the lseek. - the fd is a regular file at this point */ + { + if (errno == ESPIPE) + return SUCCESS; + else + return FAILURE; + } + /* 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 (ftruncate (s->fd, s->logical_offset)) - return FAILURE; + if (s->special_file || ftruncate (s->fd, s->logical_offset)) #else #ifdef HAVE_CHSIZE - if (chsize (s->fd, s->logical_offset)) - return FAILURE; + 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; - - return SUCCESS; -} - - -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 (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->buffer = NULL; -} - - -/********************************************************************* - mmap stream functions - - Because mmap() is not capable of extending a file, we have to keep - track of how long the file is. We also have to be able to detect end - of file conditions. If there are multiple writers to the file (which - can only happen outside the current program), things will get - confused. Then again, things will get confused anyway. - -*********************************************************************/ - -#if HAVE_MMAP - -static int page_size, page_mask; - -/* mmap_flush()-- Deletes a memory mapping if something is mapped. */ - -static try -mmap_flush (unix_stream * s) -{ - if (!s->mmaped) - return fd_flush (s); - - if (s->buffer == NULL) - return SUCCESS; - - if (munmap (s->buffer, s->active)) - return FAILURE; - - s->buffer = NULL; s->active = 0; - return SUCCESS; } -/* mmap_alloc()-- mmap() a section of the file. The whole section is - * guaranteed to be mappable. */ +/* Similar to memset(), but operating on a stream instead of a string. + Takes care of not using too much memory. */ static try -mmap_alloc (unix_stream * s, gfc_offset where, int *len) +fd_sset (unix_stream * s, int c, size_t n) { - gfc_offset offset; - int length; - char *p; - - if (mmap_flush (s) == FAILURE) - return FAILURE; + size_t bytes_left; + int trans; + void *p; - offset = where & page_mask; /* Round down to the next page */ + bytes_left = n; - length = ((where - offset) & page_mask) + 2 * page_size; + while (bytes_left > 0) + { + /* memset() in chunks of BUFFER_SIZE. */ + trans = (bytes_left < BUFFER_SIZE) ? bytes_left : BUFFER_SIZE; - p = mmap (NULL, length, s->prot, MAP_SHARED, s->fd, offset); - if (p == (char *) MAP_FAILED) - return FAILURE; + p = fd_alloc_w_at (s, &trans, -1); + if (p) + memset (p, c, trans); + else + return FAILURE; - s->mmaped = 1; - s->buffer = p; - s->buffer_offset = offset; - s->active = length; + bytes_left -= trans; + } return SUCCESS; } -static char * -mmap_alloc_r_at (unix_stream * s, int *len, gfc_offset where) -{ - gfc_offset m; +/* 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). */ - if (where == -1) - where = s->logical_offset; +static int +fd_read (unix_stream * s, void * buf, size_t * nbytes) +{ + void *p; + int tmp, status; - m = where + *len; + 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 ((s->buffer == NULL || s->buffer_offset > where || - m > s->buffer_offset + s->active) && - mmap_alloc (s, where, len) == FAILURE) - return NULL; + /* If the request is bigger than BUFFER_SIZE we flush the buffers + and read directly. */ + if (fd_flush (s) == FAILURE) + { + *nbytes = 0; + return errno; + } - if (m > s->file_length) + if (is_seekable ((stream *) s) && fd_seek (s, s->logical_offset) == FAILURE) { - *len = s->file_length - s->logical_offset; - s->logical_offset = s->file_length; + *nbytes = 0; + return errno; } - else - s->logical_offset = m; - return s->buffer + (where - s->buffer_offset); + status = do_read (s, buf, nbytes); + reset_stream (s, *nbytes); + return status; } -static char * -mmap_alloc_w_at (unix_stream * s, int *len, gfc_offset where) -{ - if (where == -1) - where = s->logical_offset; +/* 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). */ - /* If we're extending the file, we have to use file descriptor - * methods. */ +static int +fd_write (unix_stream * s, const void * buf, size_t * nbytes) +{ + void *p; + int tmp, status; - if (where + *len > s->file_length) + if (*nbytes < BUFFER_SIZE && !s->unbuffered) { - if (s->mmaped) - mmap_flush (s); - return fd_alloc_w_at (s, len, where); + 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->buffer == NULL || s->buffer_offset > where || - where + *len > s->buffer_offset + s->active || - where < s->buffer_offset + s->active) && - mmap_alloc (s, where, len) == FAILURE) - return NULL; - - s->logical_offset = where + *len; - - return s->buffer + where - s->buffer_offset; -} + /* If the request is bigger than BUFFER_SIZE we flush the buffers + and write directly. */ + 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; + } -static int -mmap_seek (unix_stream * s, gfc_offset offset) -{ - s->logical_offset = offset; - return SUCCESS; + status = do_write (s, buf, nbytes); + reset_stream (s, *nbytes); + return status; } static try -mmap_close (unix_stream * s) +fd_close (unix_stream * s) { - try t; - - t = mmap_flush (s); + if (fd_flush (s) == FAILURE) + return FAILURE; - if (close (s->fd) < 0) - t = FAILURE; - free_mem (s); + if (s->buffer != NULL && s->buffer != s->small_buffer) + free_mem (s->buffer); - return t; -} + if (s->fd != STDOUT_FILENO && s->fd != STDERR_FILENO) + { + if (close (s->fd) < 0) + return FAILURE; + } + free_mem (s); -static try -mmap_sfree (unix_stream * s) -{ return SUCCESS; } -/* mmap_open()-- mmap_specific open. If the particular file cannot be - * mmap()-ed, we fall back to the file descriptor functions. */ - -static try -mmap_open (unix_stream * s) +static void +fd_open (unix_stream * s) { - char *p; - int i; - - page_size = getpagesize (); - page_mask = ~0; - - p = mmap (0, page_size, s->prot, MAP_SHARED, s->fd, 0); - if (p == (char *) MAP_FAILED) - { - fd_open (s); - return SUCCESS; - } - - munmap (p, page_size); - - i = page_size >> 1; - while (i != 0) - { - page_mask <<= 1; - i >>= 1; - } + if (isatty (s->fd)) + s->unbuffered = 1; - s->st.alloc_r_at = (void *) mmap_alloc_r_at; - s->st.alloc_w_at = (void *) mmap_alloc_w_at; - s->st.sfree = (void *) mmap_sfree; - s->st.close = (void *) mmap_close; - s->st.seek = (void *) mmap_seek; + 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; - if (lseek (s->fd, s->file_length, SEEK_SET) < 0) - return FAILURE; - - return SUCCESS; + s->buffer = NULL; } -#endif + /********************************************************************* @@ -796,12 +856,17 @@ mem_alloc_w_at (unix_stream * s, int *len, gfc_offset where) { gfc_offset m; + assert (*len >= 0); /* Negative values not allowed. */ + if (where == -1) where = s->logical_offset; m = where + *len; - if (where < s->buffer_offset || m > s->buffer_offset + s->active) + if (where < s->buffer_offset) + return NULL; + + if (m > s->file_length) return NULL; s->logical_offset = m; @@ -810,6 +875,60 @@ mem_alloc_w_at (unix_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. */ + +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; + } +} + + +/* 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. */ + +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) { @@ -824,8 +943,27 @@ mem_seek (unix_stream * s, gfc_offset offset) } +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) +mem_truncate (unix_stream * s __attribute__ ((unused))) { return SUCCESS; } @@ -834,14 +972,15 @@ mem_truncate (unix_stream * s) static try mem_close (unix_stream * s) { - free_mem (s); + if (s != NULL) + free_mem (s); return SUCCESS; } static try -mem_sfree (unix_stream * s) +mem_sfree (unix_stream * s __attribute__ ((unused))) { return SUCCESS; } @@ -870,6 +1009,7 @@ 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; @@ -883,6 +1023,9 @@ open_internal (char *base, int length) 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; } @@ -898,6 +1041,7 @@ fd_to_stream (int fd, int prot) unix_stream *s; s = get_mem (sizeof (unix_stream)); + memset (s, '\0', sizeof (unix_stream)); s->fd = fd; s->buffer_offset = 0; @@ -908,13 +1052,15 @@ fd_to_stream (int fd, int prot) /* Get the current length of the file. */ fstat (fd, &statbuf); - s->file_length = S_ISREG (statbuf.st_mode) ? statbuf.st_size : -1; -#if HAVE_MMAP - mmap_open (s); -#else + 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); -#endif return (stream *) s; } @@ -923,15 +1069,18 @@ fd_to_stream (int fd, int prot) /* Given the Fortran unit number, convert it to a C file descriptor. */ int -unit_to_fd(int unit) +unit_to_fd (int unit) { gfc_unit *us; + int fd; - us = find_unit(unit); + us = find_unit (unit); if (us == NULL) return -1; - return ((unix_stream *) us->s)->fd; + fd = ((unix_stream *) us->s)->fd; + unlock_unit (us); + return fd; } @@ -939,7 +1088,7 @@ unit_to_fd(int unit) * buffer that is PATH_MAX characters, convert the fortran string to a * C string in the buffer. Returns nonzero if this is not possible. */ -static int +int unpack_filename (char *cstring, const char *fstring, int len) { len = fstrlen (fstring, len); @@ -957,11 +1106,11 @@ unpack_filename (char *cstring, const char *fstring, int len) * 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 - * ioparm.file, which is copied into the unit structure + * opp->file, which is copied into the unit structure * and freed later. */ static int -tempfile (void) +tempfile (st_parameter_open *opp) { const char *tempdir; char *template; @@ -971,6 +1120,8 @@ tempfile (void) if (tempdir == NULL) tempdir = getenv ("TMP"); if (tempdir == NULL) + tempdir = getenv ("TEMP"); + if (tempdir == NULL) tempdir = DEFAULT_TEMPDIR; template = get_mem (strlen (tempdir) + 20); @@ -985,7 +1136,12 @@ tempfile (void) if (mktemp (template)) do - fd = open (template, O_CREAT | O_EXCL, S_IREAD | S_IWRITE); +#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; @@ -996,8 +1152,8 @@ tempfile (void) free_mem (template); else { - ioparm.file = template; - ioparm.file_len = strlen (template); /* Don't include trailing nul */ + opp->file = template; + opp->file_len = strlen (template); /* Don't include trailing nul */ } return fd; @@ -1010,7 +1166,7 @@ tempfile (void) * Returns the descriptor, which is less than zero on error. */ static int -regular_file (unit_flags *flags) +regular_file (st_parameter_open *opp, unit_flags *flags) { char path[PATH_MAX + 1]; int mode; @@ -1018,7 +1174,7 @@ regular_file (unit_flags *flags) int crflag; int fd; - if (unpack_filename (path, ioparm.file, ioparm.file_len)) + if (unpack_filename (path, opp->file, opp->file_len)) { errno = ENOENT; /* Fake an OS error */ return -1; @@ -1042,7 +1198,7 @@ regular_file (unit_flags *flags) break; default: - internal_error ("regular_file(): Bad action"); + internal_error (&opp->common, "regular_file(): Bad action"); } switch (flags->status) @@ -1061,26 +1217,30 @@ regular_file (unit_flags *flags) break; case STATUS_REPLACE: - crflag = O_CREAT | O_TRUNC; + crflag = O_CREAT | O_TRUNC; break; default: - internal_error ("regular_file(): Bad status"); + internal_error (&opp->common, "regular_file(): Bad status"); } /* rwflag |= O_LARGEFILE; */ +#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; + return fd; if (fd >= 0) { flags->action = ACTION_READWRITE; return fd; } - if (errno != EACCES) + if (errno != EACCES && errno != EROFS) return fd; /* retry for read-only access */ @@ -1112,23 +1272,27 @@ regular_file (unit_flags *flags) * Returns NULL on operating system error. */ stream * -open_external (unit_flags *flags) +open_external (st_parameter_open *opp, unit_flags *flags) { int fd, prot; if (flags->status == STATUS_SCRATCH) { - fd = tempfile (); + fd = tempfile (opp); if (flags->action == ACTION_UNSPECIFIED) flags->action = ACTION_READWRITE; + +#if HAVE_UNLINK_OPEN_FILE /* We can unlink scratch files now and it will go away when closed. */ - unlink (ioparm.file); + if (fd >= 0) + unlink (opp->file); +#endif } else { /* regular_file resets flags->action if it is ACTION_UNSPECIFIED and * if it succeeds */ - fd = regular_file (flags); + fd = regular_file (opp, flags); } if (fd < 0) @@ -1150,7 +1314,7 @@ open_external (unit_flags *flags) break; default: - internal_error ("open_external(): Bad action"); + internal_error (&opp->common, "open_external(): Bad action"); } return fd_to_stream (fd, prot); @@ -1173,6 +1337,9 @@ input_stream (void) 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); } @@ -1183,6 +1350,9 @@ output_stream (void) 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); } @@ -1192,21 +1362,116 @@ error_stream (void) * corrupted. */ stream * -init_error_stream (void) +init_error_stream (unix_stream *error) { - static unix_stream error; + memset (error, '\0', sizeof (*error)); - memset (&error, '\0', sizeof (error)); + error->fd = options.use_stderr ? STDERR_FILENO : STDOUT_FILENO; - 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; +} + +/* st_printf()-- simple printf() function for streams that handles the + * formats %d, %s and %c. This function handles printing of error + * messages that originate within the library itself, not from a user + * program. */ + +int +st_printf (const char *format, ...) +{ + int count, total; + va_list arg; + char *p; + const char *q; + stream *s; + char itoa_buf[GFC_ITOA_BUF_SIZE]; + unix_stream err_stream; - error.st.alloc_w_at = (void *) fd_alloc_w_at; - error.st.sfree = (void *) fd_sfree; + total = 0; + s = init_error_stream (&err_stream); + va_start (arg, format); - error.unbuffered = 1; - error.buffer = error.small_buffer; + for (;;) + { + count = 0; + + while (format[count] != '%' && format[count] != '\0') + count++; + + if (count != 0) + { + p = salloc_w (s, &count); + memmove (p, format, count); + sfree (s); + } + + total += count; + format += count; + if (*format++ == '\0') + break; + + switch (*format) + { + case 'c': + count = 1; + + p = salloc_w (s, &count); + *p = (char) va_arg (arg, int); + + sfree (s); + break; + + case 'd': + q = gfc_itoa (va_arg (arg, int), itoa_buf, sizeof (itoa_buf)); + count = strlen (q); + + p = salloc_w (s, &count); + memmove (p, q, count); + sfree (s); + break; + + case 'x': + q = xtoa (va_arg (arg, unsigned), itoa_buf, sizeof (itoa_buf)); + count = strlen (q); + + p = salloc_w (s, &count); + memmove (p, q, count); + sfree (s); + break; + + case 's': + q = va_arg (arg, char *); + count = strlen (q); + + p = salloc_w (s, &count); + memmove (p, q, count); + sfree (s); + break; + + case '\0': + return total; + + default: + count = 2; + p = salloc_w (s, &count); + p[0] = format[-1]; + p[1] = format[0]; + sfree (s); + break; + } + + total += count; + format++; + } - return (stream *) & error; + va_end (arg); + return total; } @@ -1215,10 +1480,13 @@ init_error_stream (void) * filename. */ int -compare_file_filename (stream * s, const char *name, int len) +compare_file_filename (gfc_unit *u, const char *name, int len) { char path[PATH_MAX + 1]; - struct stat st1, st2; + struct stat st1; +#ifdef HAVE_WORKING_STAT + struct stat st2; +#endif if (unpack_filename (path, name, len)) return 0; /* Can't be the same */ @@ -1229,32 +1497,50 @@ compare_file_filename (stream * s, const char *name, int len) if (stat (path, &st1) < 0) return 0; - fstat (((unix_stream *) s)->fd, &st2); - +#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 + /* find_file0()-- Recursive work function for find_file() */ static gfc_unit * -find_file0 (gfc_unit * u, struct stat *st1) +find_file0 (gfc_unit *u, FIND_FILE0_DECL) { - struct stat st2; gfc_unit *v; if (u == NULL) return NULL; - if (fstat (((unix_stream *) u->s)->fd, &st2) >= 0 && - st1->st_dev == st2.st_dev && st1->st_ino == st2.st_ino) +#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, st1); + v = find_file0 (u->left, FIND_FILE0_ARGS); if (v != NULL) return v; - v = find_file0 (u->right, st1); + v = find_file0 (u->right, FIND_FILE0_ARGS); if (v != NULL) return v; @@ -1266,18 +1552,111 @@ find_file0 (gfc_unit * u, struct stat *st1) * that has the file already open. Returns a pointer to the unit if so. */ gfc_unit * -find_file (void) +find_file (const char *file, gfc_charlen_type file_len) { char path[PATH_MAX + 1]; - struct stat statbuf; + struct stat st[2]; + gfc_unit *u; - if (unpack_filename (path, ioparm.file, ioparm.file_len)) + if (unpack_filename (path, file, file_len)) return NULL; - if (stat (path, &statbuf) < 0) + if (stat (path, &st[0]) < 0) return NULL; - return find_file0 (g.unit_root, &statbuf); + __gthread_mutex_lock (&unit_lock); +retry: + u = find_file0 (unit_root, FIND_FILE0_ARGS); + if (u != NULL) + { + /* Fast path. */ + if (! __gthread_mutex_trylock (&u->lock)) + { + /* assert (u->closed == 0); */ + __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); } @@ -1289,16 +1668,16 @@ stream_at_bof (stream * s) { unix_stream *us; - us = (unix_stream *) s; + if (!is_seekable (s)) + return 0; - if (!us->mmaped) - return 0; /* File is not seekable */ + us = (unix_stream *) s; return us->logical_offset == 0; } -/* stream_at_eof()-- Returns nonzero if the stream is at the beginning +/* stream_at_eof()-- Returns nonzero if the stream is at the end * of the file. */ int @@ -1306,10 +1685,10 @@ stream_at_eof (stream * s) { unix_stream *us; - us = (unix_stream *) s; + if (!is_seekable (s)) + return 0; - if (!us->mmaped) - return 0; /* File is not seekable */ + us = (unix_stream *) s; return us->logical_offset == us->dirty_offset; } @@ -1337,12 +1716,12 @@ delete_file (gfc_unit * u) * the system */ int -file_exists (void) +file_exists (const char *file, gfc_charlen_type file_len) { char path[PATH_MAX + 1]; struct stat statbuf; - if (unpack_filename (path, ioparm.file, ioparm.file_len)) + if (unpack_filename (path, file, file_len)) return 0; if (stat (path, &statbuf) < 0) @@ -1353,7 +1732,7 @@ file_exists (void) -static const char *yes = "YES", *no = "NO", *unknown = "UNKNOWN"; +static const char yes[] = "YES", no[] = "NO", unknown[] = "UNKNOWN"; /* inquire_sequential()-- Given a fortran string, determine if the * file is suitable for sequential access. Returns a C-style @@ -1497,7 +1876,7 @@ file_length (stream * s) /* file_position()-- Return the current position of the file */ gfc_offset -file_position (stream * s) +file_position (stream *s) { return ((unix_stream *) s)->logical_offset; } @@ -1507,20 +1886,51 @@ file_position (stream * s) * it is not */ int -is_seekable (stream * s) +is_seekable (stream *s) { - /* by convention, if file_length == -1, the file is not seekable - note that a mmapped file is always seekable, an fd_ file may - or may not be. */ + /* 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) +{ +#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); +} + /* How files are stored: This is an operating-system specific issue, and therefore belongs here. There are three cases to consider.