aboutsummaryrefslogtreecommitdiff
path: root/libgfortran/io
diff options
context:
space:
mode:
authorFritz Reese <fritzoreese@gmail.com>2016-10-26 12:11:44 +0000
committerFritz Reese <foreese@gcc.gnu.org>2016-10-26 12:11:44 +0000
commit0ef33d44629066e33ffdc46014374a3ef5c5f009 (patch)
tree45d5f5c66da6b5b2820d025ba02eb604ab6a5b91 /libgfortran/io
parent9dbe100a4157843d12b192e4aae504b43af4774b (diff)
downloadgcc-0ef33d44629066e33ffdc46014374a3ef5c5f009.zip
gcc-0ef33d44629066e33ffdc46014374a3ef5c5f009.tar.gz
gcc-0ef33d44629066e33ffdc46014374a3ef5c5f009.tar.bz2
New I/O specifiers CARRIAGECONTROL, READONLY, SHARE with -fdec.
gcc/fortran/ * gfortran.texi: Document. * frontend-passes.c (gfc_code_walker): Add SHARE and CARRIAGECONTROL. * io.c (gfc_free_open, gfc_resolve_open, gfc_match_open): Ditto. * gfortran.h (gfc_open): Add SHARE, CARRIAGECONTROL, and READONLY. * io.c (io_tag, match_open_element): Ditto. * ioparm.def: Ditto. * trans-io.c (gfc_trans_open): Ditto. * io.c (match_dec_etag, match_dec_ftag): New functions. libgfortran/io/ * libgfortran.h (IOPARM_OPEN_HAS_READONLY, IOPARM_OPEN_HAS_SHARE, IOPARM_OPEN_HAS_CC): New for READONLY, SHARE, and CARRIAGECONTROL. * close.c (st_close): Support READONLY. * io.h (st_parameter_open, unit_flags): Support SHARE, CARRIAGECONTROL, and READONLY. * open.c (st_open): Ditto. * transfer.c (data_transfer_init): Ditto. * io.h (st_parameter_dt): New member 'cc' for CARRIAGECONTROL. * write.c (write_check_cc, write_cc): New functions for CARRIAGECONTROL. * transfer.c (next_record_cc): Ditto. * file_pos.c (st_endfile): Support SHARE and CARRIAGECONTROL. * io.h (st_parameter_inquire): Ditto. * open.c (edit_modes, new_unit): Ditto. * inquire.c (inquire_via_unit, inquire_via_filename): Ditto. * io.h (unit_share, unit_cc, cc_fortran, IOPARM_INQUIRE_HAS_SHARE, IOPARM_INQUIRE_HAS_CC): New for SHARE and CARRIAGECONTROL. * open.c (share_opt, cc_opt): Ditto. * read.c (read_x): Support CARRIAGECONTROL. * transfer.c (read_sf, next_record_r, next_record_w): Ditto. * write.c (list_formatted_write_scalar, write_a): Ditto. * unix.h (close_share): New prototype. * unix.c (open_share, close_share): New functions to handle SHARE. * unix.c (open_external): Handle READONLY. Call open_share. * close.c (st_close): Call close_share. gcc/testsuite/ * dec_io_1.f90: New test. * dec_io_2.f90: New test. * dec_io_3.f90: New test. * dec_io_4.f90: New test. * dec_io_5.f90: New test. * dec_io_6.f90: New test. From-SVN: r241550
Diffstat (limited to 'libgfortran/io')
-rw-r--r--libgfortran/io/close.c16
-rw-r--r--libgfortran/io/file_pos.c2
-rw-r--r--libgfortran/io/inquire.c58
-rw-r--r--libgfortran/io/io.h51
-rw-r--r--libgfortran/io/open.c47
-rw-r--r--libgfortran/io/read.c3
-rw-r--r--libgfortran/io/transfer.c59
-rw-r--r--libgfortran/io/unit.c6
-rw-r--r--libgfortran/io/unix.c89
-rw-r--r--libgfortran/io/unix.h3
-rw-r--r--libgfortran/io/write.c141
11 files changed, 458 insertions, 17 deletions
diff --git a/libgfortran/io/close.c b/libgfortran/io/close.c
index c29c125..8fbfe82 100644
--- a/libgfortran/io/close.c
+++ b/libgfortran/io/close.c
@@ -66,6 +66,8 @@ st_close (st_parameter_close *clp)
u = find_unit (clp->common.unit);
if (u != NULL)
{
+ if (close_share (u) < 0)
+ generate_error (&clp->common, LIBERROR_OS, "Problem in CLOSE");
if (u->flags.status == STATUS_SCRATCH)
{
if (status == CLOSE_KEEP)
@@ -78,13 +80,19 @@ st_close (st_parameter_close *clp)
else
{
if (status == CLOSE_DELETE)
- {
+ {
+ if (u->flags.readonly)
+ generate_warning (&clp->common, "STATUS set to DELETE on CLOSE"
+ " but file protected by READONLY specifier");
+ else
+ {
#if HAVE_UNLINK_OPEN_FILE
- remove (u->filename);
+ remove (u->filename);
#else
- path = strdup (u->filename);
+ path = strdup (u->filename);
#endif
- }
+ }
+ }
}
close_unit (u);
diff --git a/libgfortran/io/file_pos.c b/libgfortran/io/file_pos.c
index 5720eae..6611a8d 100644
--- a/libgfortran/io/file_pos.c
+++ b/libgfortran/io/file_pos.c
@@ -362,6 +362,8 @@ st_endfile (st_parameter_filepos *fpp)
u_flags.sign = SIGN_UNSPECIFIED;
u_flags.status = STATUS_UNKNOWN;
u_flags.convert = GFC_CONVERT_NATIVE;
+ u_flags.share = SHARE_UNSPECIFIED;
+ u_flags.cc = CC_UNSPECIFIED;
opp.common = fpp->common;
opp.common.flags &= IOPARM_COMMON_MASK;
diff --git a/libgfortran/io/inquire.c b/libgfortran/io/inquire.c
index 7e663130e..7e013e0 100644
--- a/libgfortran/io/inquire.c
+++ b/libgfortran/io/inquire.c
@@ -428,6 +428,58 @@ inquire_via_unit (st_parameter_inquire *iqp, gfc_unit * u)
cf_strcpy (iqp->iqstream, iqp->iqstream_len, p);
}
+
+ if ((cf2 & IOPARM_INQUIRE_HAS_SHARE) != 0)
+ {
+ if (u == NULL)
+ p = "UNKNOWN";
+ else
+ switch (u->flags.share)
+ {
+ case SHARE_DENYRW:
+ p = "DENYRW";
+ break;
+ case SHARE_DENYNONE:
+ p = "DENYNONE";
+ break;
+ case SHARE_UNSPECIFIED:
+ p = "NODENY";
+ break;
+ default:
+ internal_error (&iqp->common,
+ "inquire_via_unit(): Bad share");
+ break;
+ }
+
+ cf_strcpy (iqp->share, iqp->share_len, p);
+ }
+
+ if ((cf2 & IOPARM_INQUIRE_HAS_CC) != 0)
+ {
+ if (u == NULL)
+ p = "UNKNOWN";
+ else
+ switch (u->flags.cc)
+ {
+ case CC_FORTRAN:
+ p = "FORTRAN";
+ break;
+ case CC_LIST:
+ p = "LIST";
+ break;
+ case CC_NONE:
+ p = "NONE";
+ break;
+ case CC_UNSPECIFIED:
+ p = "UNKNOWN";
+ break;
+ default:
+ internal_error (&iqp->common, "inquire_via_unit(): Bad cc");
+ break;
+ }
+
+ cf_strcpy (iqp->cc, iqp->cc_len, p);
+ }
}
if ((cf & IOPARM_INQUIRE_HAS_POSITION) != 0)
@@ -671,6 +723,12 @@ inquire_via_filename (st_parameter_inquire *iqp)
if ((cf2 & IOPARM_INQUIRE_HAS_IQSTREAM) != 0)
cf_strcpy (iqp->iqstream, iqp->iqstream_len, "UNKNOWN");
+
+ if ((cf2 & IOPARM_INQUIRE_HAS_SHARE) != 0)
+ cf_strcpy (iqp->share, iqp->share_len, "UNKNOWN");
+
+ if ((cf2 & IOPARM_INQUIRE_HAS_CC) != 0)
+ cf_strcpy (iqp->cc, iqp->cc_len, "UNKNOWN");
}
if ((cf & IOPARM_INQUIRE_HAS_POSITION) != 0)
diff --git a/libgfortran/io/io.h b/libgfortran/io/io.h
index 8c6caef..7a54849 100644
--- a/libgfortran/io/io.h
+++ b/libgfortran/io/io.h
@@ -269,9 +269,35 @@ typedef enum
unit_async;
typedef enum
+{ SHARE_DENYRW, SHARE_DENYNONE,
+ SHARE_UNSPECIFIED
+}
+unit_share;
+
+typedef enum
+{ CC_LIST, CC_FORTRAN, CC_NONE,
+ CC_UNSPECIFIED
+}
+unit_cc;
+
+/* End-of-record types for CC_FORTRAN. */
+typedef enum
+{ CCF_DEFAULT=0x0,
+ CCF_OVERPRINT=0x1,
+ CCF_ONE_LF=0x2,
+ CCF_TWO_LF=0x4,
+ CCF_PAGE_FEED=0x8,
+ CCF_PROMPT=0x10,
+ CCF_OVERPRINT_NOA=0x20,
+} /* 6 bits */
+cc_fortran;
+
+typedef enum
{ SIGN_S, SIGN_SS, SIGN_SP }
unit_sign_s;
+/* Make sure to keep st_parameter_* in sync with gcc/fortran/ioparm.def. */
+
#define CHARACTER1(name) \
char * name; \
gfc_charlen_type name ## _len
@@ -299,6 +325,9 @@ typedef struct
CHARACTER1 (sign);
CHARACTER2 (asynchronous);
GFC_INTEGER_4 *newunit;
+ GFC_INTEGER_4 readonly;
+ CHARACTER2 (cc);
+ CHARACTER1 (share);
}
st_parameter_open;
@@ -352,6 +381,8 @@ st_parameter_filepos;
#define IOPARM_INQUIRE_HAS_SIZE (1 << 6)
#define IOPARM_INQUIRE_HAS_ID (1 << 7)
#define IOPARM_INQUIRE_HAS_IQSTREAM (1 << 8)
+#define IOPARM_INQUIRE_HAS_SHARE (1 << 9)
+#define IOPARM_INQUIRE_HAS_CC (1 << 10)
typedef struct
{
@@ -386,6 +417,8 @@ typedef struct
GFC_IO_INT *size;
GFC_INTEGER_4 *id;
CHARACTER1 (iqstream);
+ CHARACTER2 (share);
+ CHARACTER1 (cc);
}
st_parameter_inquire;
@@ -526,6 +559,21 @@ typedef struct st_parameter_dt
GFC_IO_INT not_used; /* Needed for alignment. */
formatted_dtio fdtio_ptr;
unformatted_dtio ufdtio_ptr;
+ /* With CC_FORTRAN, the first character of a record determines the
+ style of record end (and start) to use. We must mark down the type
+ when we write first in write_a so we remember the end type later in
+ next_record_w. */
+ struct
+ {
+ unsigned type : 6; /* See enum cc_fortran. */
+ unsigned len : 2; /* Always 0, 1, or 2. */
+ /* The union is updated after start-of-record is written. */
+ union
+ {
+ char start; /* Output character for start of record. */
+ char end; /* Output character for end of record. */
+ } u;
+ } cc;
} p;
/* This pad size must be equal to the pad_size declared in
trans-io.c (gfc_build_io_library_fndecls). The above structure
@@ -571,6 +619,9 @@ typedef struct
unit_round round;
unit_sign sign;
unit_async async;
+ unit_share share;
+ unit_cc cc;
+ int readonly;
}
unit_flags;
diff --git a/libgfortran/io/open.c b/libgfortran/io/open.c
index 2e7163d..b0f1009 100644
--- a/libgfortran/io/open.c
+++ b/libgfortran/io/open.c
@@ -52,6 +52,21 @@ static const st_option action_opt[] =
{ NULL, 0}
};
+static const st_option share_opt[] =
+{
+ { "denyrw", SHARE_DENYRW },
+ { "denynone", SHARE_DENYNONE },
+ { NULL, 0}
+};
+
+static const st_option cc_opt[] =
+{
+ { "list", CC_LIST },
+ { "fortran", CC_FORTRAN },
+ { "none", CC_NONE },
+ { NULL, 0}
+};
+
static const st_option blank_opt[] =
{
{ "null", BLANK_NULL},
@@ -195,6 +210,14 @@ edit_modes (st_parameter_open *opp, gfc_unit * u, unit_flags * flags)
generate_error (&opp->common, LIBERROR_BAD_OPTION,
"Cannot change ACTION parameter in OPEN statement");
+ if (flags->share != SHARE_UNSPECIFIED && u->flags.share != flags->share)
+ generate_error (&opp->common, LIBERROR_BAD_OPTION,
+ "Cannot change SHARE parameter in OPEN statement");
+
+ if (flags->cc != CC_UNSPECIFIED && u->flags.cc != flags->cc)
+ generate_error (&opp->common, LIBERROR_BAD_OPTION,
+ "Cannot change CARRIAGECONTROL parameter in OPEN statement");
+
/* Status must be OLD if present. */
if (flags->status != STATUS_UNSPECIFIED && flags->status != STATUS_OLD &&
@@ -330,6 +353,16 @@ new_unit (st_parameter_open *opp, gfc_unit *u, unit_flags * flags)
if (flags->status == STATUS_UNSPECIFIED)
flags->status = STATUS_UNKNOWN;
+ if (flags->cc == CC_UNSPECIFIED)
+ flags->cc = flags->form == FORM_UNFORMATTED ? CC_NONE : CC_LIST;
+ else if (flags->form == FORM_UNFORMATTED && flags->cc != CC_NONE)
+ {
+ generate_error (&opp->common, LIBERROR_OPTION_CONFLICT,
+ "CARRIAGECONTROL parameter conflicts with UNFORMATTED form in "
+ "OPEN statement");
+ goto fail;
+ }
+
/* Checks. */
if (flags->delim != DELIM_UNSPECIFIED
@@ -695,6 +728,7 @@ st_open (st_parameter_open *opp)
library_start (&opp->common);
/* Decode options. */
+ flags.readonly = !(cf & IOPARM_OPEN_HAS_READONLY) ? 0 : opp->readonly;
flags.access = !(cf & IOPARM_OPEN_HAS_ACCESS) ? ACCESS_UNSPECIFIED :
find_option (&opp->common, opp->access, opp->access_len,
@@ -704,6 +738,14 @@ st_open (st_parameter_open *opp)
find_option (&opp->common, opp->action, opp->action_len,
action_opt, "Bad ACTION parameter in OPEN statement");
+ flags.cc = !(cf & IOPARM_OPEN_HAS_CC) ? CC_UNSPECIFIED :
+ find_option (&opp->common, opp->cc, opp->cc_len,
+ cc_opt, "Bad CARRIAGECONTROL parameter in OPEN statement");
+
+ flags.share = !(cf & IOPARM_OPEN_HAS_SHARE) ? SHARE_UNSPECIFIED :
+ find_option (&opp->common, opp->share, opp->share_len,
+ share_opt, "Bad SHARE parameter in OPEN statement");
+
flags.blank = !(cf & IOPARM_OPEN_HAS_BLANK) ? BLANK_UNSPECIFIED :
find_option (&opp->common, opp->blank, opp->blank_len,
blank_opt, "Bad BLANK parameter in OPEN statement");
@@ -792,6 +834,11 @@ st_open (st_parameter_open *opp)
generate_error (&opp->common, LIBERROR_BAD_OPTION,
"Cannot use POSITION with direct access files");
+ if (flags.readonly
+ && flags.action != ACTION_UNSPECIFIED && flags.action != ACTION_READ)
+ generate_error (&opp->common, LIBERROR_BAD_OPTION,
+ "ACTION conflicts with READONLY in OPEN statement");
+
if (flags.access == ACCESS_APPEND)
{
if (flags.position != POSITION_UNSPECIFIED
diff --git a/libgfortran/io/read.c b/libgfortran/io/read.c
index d72cdb3..23b6f64 100644
--- a/libgfortran/io/read.c
+++ b/libgfortran/io/read.c
@@ -1256,7 +1256,8 @@ read_x (st_parameter_dt *dtp, int n)
q = fbuf_getc (dtp->u.p.current_unit);
if (q == EOF)
break;
- else if (q == '\n' || q == '\r')
+ else if (dtp->u.p.current_unit->flags.cc != CC_NONE
+ && (q == '\n' || q == '\r'))
{
/* Unexpected end of line. Set the position. */
dtp->u.p.sf_seen_eor = 1;
diff --git a/libgfortran/io/transfer.c b/libgfortran/io/transfer.c
index e3f75b6..b8eb5ed 100644
--- a/libgfortran/io/transfer.c
+++ b/libgfortran/io/transfer.c
@@ -316,7 +316,8 @@ read_sf (st_parameter_dt *dtp, int * length)
q = fbuf_getc (dtp->u.p.current_unit);
if (q == EOF)
break;
- else if (q == '\n' || q == '\r')
+ else if (dtp->u.p.current_unit->flags.cc != CC_NONE
+ && (q == '\n' || q == '\r'))
{
/* Unexpected end of line. Set the position. */
dtp->u.p.sf_seen_eor = 1;
@@ -2598,6 +2599,8 @@ data_transfer_init (st_parameter_dt *dtp, int read_flag)
dtp->u.p.ionml = ionml;
dtp->u.p.mode = read_flag ? READING : WRITING;
+ dtp->u.p.cc.len = 0;
+
if ((dtp->common.flags & IOPARM_LIBRETURN_MASK) != IOPARM_LIBRETURN_OK)
return;
@@ -2636,6 +2639,9 @@ data_transfer_init (st_parameter_dt *dtp, int read_flag)
u_flags.async = ASYNC_UNSPECIFIED;
u_flags.round = ROUND_UNSPECIFIED;
u_flags.sign = SIGN_UNSPECIFIED;
+ u_flags.share = SHARE_UNSPECIFIED;
+ u_flags.cc = CC_UNSPECIFIED;
+ u_flags.readonly = 0;
u_flags.status = STATUS_UNKNOWN;
@@ -3349,7 +3355,7 @@ next_record_r (st_parameter_dt *dtp, int done)
}
break;
}
- else
+ else if (dtp->u.p.current_unit->flags.cc != CC_NONE)
{
do
{
@@ -3531,6 +3537,30 @@ sset (stream * s, int c, ssize_t nbyte)
}
+/* Finish up a record according to the legacy carriagecontrol type, based
+ on the first character in the record. */
+
+static void
+next_record_cc (st_parameter_dt *dtp)
+{
+ /* Only valid with CARRIAGECONTROL=FORTRAN. */
+ if (dtp->u.p.current_unit->flags.cc != CC_FORTRAN)
+ return;
+
+ fbuf_seek (dtp->u.p.current_unit, 0, SEEK_END);
+ if (dtp->u.p.cc.len > 0)
+ {
+ char * p = fbuf_alloc (dtp->u.p.current_unit, dtp->u.p.cc.len);
+ if (!p)
+ generate_error (&dtp->common, LIBERROR_OS, NULL);
+
+ /* Output CR for the first character with default CC setting. */
+ *(p++) = dtp->u.p.cc.u.end;
+ if (dtp->u.p.cc.len > 1)
+ *p = dtp->u.p.cc.u.end;
+ }
+}
+
/* Position to the next record in write mode. */
static void
@@ -3677,21 +3707,30 @@ next_record_w (st_parameter_dt *dtp, int done)
}
}
}
+ /* Handle legacy CARRIAGECONTROL line endings. */
+ else if (dtp->u.p.current_unit->flags.cc == CC_FORTRAN)
+ next_record_cc (dtp);
else
{
+ /* Skip newlines for CC=CC_NONE. */
+ const int len = (dtp->u.p.current_unit->flags.cc == CC_NONE)
+ ? 0
#ifdef HAVE_CRLF
- const int len = 2;
+ : 2;
#else
- const int len = 1;
+ : 1;
#endif
- fbuf_seek (dtp->u.p.current_unit, 0, SEEK_END);
- char * p = fbuf_alloc (dtp->u.p.current_unit, len);
- if (!p)
- goto io_error;
+ fbuf_seek (dtp->u.p.current_unit, 0, SEEK_END);
+ if (dtp->u.p.current_unit->flags.cc != CC_NONE)
+ {
+ char * p = fbuf_alloc (dtp->u.p.current_unit, len);
+ if (!p)
+ goto io_error;
#ifdef HAVE_CRLF
- *(p++) = '\r';
+ *(p++) = '\r';
#endif
- *p = '\n';
+ *p = '\n';
+ }
if (is_stream_io (dtp))
{
dtp->u.p.current_unit->strm_pos += len;
diff --git a/libgfortran/io/unit.c b/libgfortran/io/unit.c
index 41cd52f..6fa264c 100644
--- a/libgfortran/io/unit.c
+++ b/libgfortran/io/unit.c
@@ -652,6 +652,8 @@ init_units (void)
u->flags.encoding = ENCODING_DEFAULT;
u->flags.async = ASYNC_NO;
u->flags.round = ROUND_UNSPECIFIED;
+ u->flags.share = SHARE_UNSPECIFIED;
+ u->flags.cc = CC_LIST;
u->recl = options.default_recl;
u->endfile = NO_ENDFILE;
@@ -681,6 +683,8 @@ init_units (void)
u->flags.encoding = ENCODING_DEFAULT;
u->flags.async = ASYNC_NO;
u->flags.round = ROUND_UNSPECIFIED;
+ u->flags.share = SHARE_UNSPECIFIED;
+ u->flags.cc = CC_LIST;
u->recl = options.default_recl;
u->endfile = AT_ENDFILE;
@@ -709,6 +713,8 @@ init_units (void)
u->flags.encoding = ENCODING_DEFAULT;
u->flags.async = ASYNC_NO;
u->flags.round = ROUND_UNSPECIFIED;
+ u->flags.share = SHARE_UNSPECIFIED;
+ u->flags.cc = CC_LIST;
u->recl = options.default_recl;
u->endfile = AT_ENDFILE;
diff --git a/libgfortran/io/unix.c b/libgfortran/io/unix.c
index 1e84c42..5301b84 100644
--- a/libgfortran/io/unix.c
+++ b/libgfortran/io/unix.c
@@ -1425,6 +1425,56 @@ regular_file2 (const char *path, st_parameter_open *opp, unit_flags *flags)
}
+/* Lock the file, if necessary, based on SHARE flags. */
+
+#if defined(HAVE_FCNTL) && defined(F_SETLK) && defined(F_UNLCK)
+static int
+open_share (st_parameter_open *opp, int fd, unit_flags *flags)
+{
+ int r = 0;
+ struct flock f;
+ if (fd == STDOUT_FILENO || fd == STDERR_FILENO || fd == STDIN_FILENO)
+ return 0;
+
+ f.l_start = 0;
+ f.l_len = 0;
+ f.l_whence = SEEK_SET;
+
+ switch (flags->share)
+ {
+ case SHARE_DENYNONE:
+ f.l_type = F_RDLCK;
+ r = fcntl (fd, F_SETLK, &f);
+ break;
+ case SHARE_DENYRW:
+ /* Must be writable to hold write lock. */
+ if (flags->action == ACTION_READ)
+ {
+ generate_error (&opp->common, LIBERROR_BAD_ACTION,
+ "Cannot set write lock on file opened for READ");
+ return -1;
+ }
+ f.l_type = F_WRLCK;
+ r = fcntl (fd, F_SETLK, &f);
+ break;
+ case SHARE_UNSPECIFIED:
+ default:
+ break;
+ }
+
+ return r;
+}
+#else
+static int
+open_share (st_parameter_open *opp __attribute__ ((unused)),
+ int fd __attribute__ ((unused)),
+ unit_flags *flags __attribute__ ((unused)))
+{
+ return 0;
+}
+#endif /* defined(HAVE_FCNTL) ... */
+
+
/* Wrapper around regular_file2, to make sure we free the path after
we're done. */
@@ -1450,7 +1500,7 @@ open_external (st_parameter_open *opp, unit_flags *flags)
{
fd = tempfile (opp);
if (flags->action == ACTION_UNSPECIFIED)
- flags->action = ACTION_READWRITE;
+ flags->action = flags->readonly ? ACTION_READ : ACTION_READWRITE;
#if HAVE_UNLINK_OPEN_FILE
/* We can unlink scratch files now and it will go away when closed. */
@@ -1472,6 +1522,9 @@ open_external (st_parameter_open *opp, unit_flags *flags)
return NULL;
fd = fix_fd (fd);
+ if (open_share (opp, fd, flags) < 0)
+ return NULL;
+
return fd_to_stream (fd, flags->form == FORM_UNFORMATTED);
}
@@ -1752,6 +1805,40 @@ flush_all_units (void)
}
+/* Unlock the unit if necessary, based on SHARE flags. */
+
+int
+close_share (gfc_unit *u __attribute__ ((unused)))
+{
+ int r = 0;
+#if defined(HAVE_FCNTL) && defined(F_SETLK) && defined(F_UNLCK)
+ unix_stream *s = (unix_stream *) u->s;
+ int fd = s->fd;
+ struct flock f;
+
+ switch (u->flags.share)
+ {
+ case SHARE_DENYRW:
+ case SHARE_DENYNONE:
+ if (fd != STDOUT_FILENO && fd != STDERR_FILENO && fd != STDIN_FILENO)
+ {
+ f.l_start = 0;
+ f.l_len = 0;
+ f.l_whence = SEEK_SET;
+ f.l_type = F_UNLCK;
+ r = fcntl (fd, F_SETLK, &f);
+ }
+ break;
+ case SHARE_UNSPECIFIED:
+ default:
+ break;
+ }
+
+#endif
+ return r;
+}
+
+
/* file_exists()-- Returns nonzero if the current filename exists on
* the system */
diff --git a/libgfortran/io/unix.h b/libgfortran/io/unix.h
index 6b1b02eb..3d4de26 100644
--- a/libgfortran/io/unix.h
+++ b/libgfortran/io/unix.h
@@ -141,6 +141,9 @@ internal_proto(compare_file_filename);
extern gfc_unit *find_file (const char *file, gfc_charlen_type file_len);
internal_proto(find_file);
+extern int close_share (gfc_unit *);
+internal_proto(close_share);
+
extern int file_exists (const char *file, gfc_charlen_type file_len);
internal_proto(file_exists);
diff --git a/libgfortran/io/write.c b/libgfortran/io/write.c
index d4b1bc8..c8bba3c 100644
--- a/libgfortran/io/write.c
+++ b/libgfortran/io/write.c
@@ -228,6 +228,138 @@ write_utf8_char4 (st_parameter_dt *dtp, gfc_char4_t *source,
}
+/* Check the first character in source if we are using CC_FORTRAN
+ and set the cc.type appropriately. The cc.type is used later by write_cc
+ to determine the output start-of-record, and next_record_cc to determine the
+ output end-of-record.
+ This function is called before the output buffer is allocated, so alloc_len
+ is set to the appropriate size to allocate. */
+
+static void
+write_check_cc (st_parameter_dt *dtp, const char **source, int *alloc_len)
+{
+ /* Only valid for CARRIAGECONTROL=FORTRAN. */
+ if (dtp->u.p.current_unit->flags.cc != CC_FORTRAN
+ || alloc_len == NULL || source == NULL)
+ return;
+
+ /* Peek at the first character. */
+ int c = (*alloc_len > 0) ? (*source)[0] : EOF;
+ if (c != EOF)
+ {
+ /* The start-of-record character which will be printed. */
+ dtp->u.p.cc.u.start = '\n';
+ /* The number of characters to print at the start-of-record.
+ len > 1 means copy the SOR character multiple times.
+ len == 0 means no SOR will be output. */
+ dtp->u.p.cc.len = 1;
+
+ switch (c)
+ {
+ case '+':
+ dtp->u.p.cc.type = CCF_OVERPRINT;
+ dtp->u.p.cc.len = 0;
+ break;
+ case '-':
+ dtp->u.p.cc.type = CCF_ONE_LF;
+ dtp->u.p.cc.len = 1;
+ break;
+ case '0':
+ dtp->u.p.cc.type = CCF_TWO_LF;
+ dtp->u.p.cc.len = 2;
+ break;
+ case '1':
+ dtp->u.p.cc.type = CCF_PAGE_FEED;
+ dtp->u.p.cc.len = 1;
+ dtp->u.p.cc.u.start = '\f';
+ break;
+ case '$':
+ dtp->u.p.cc.type = CCF_PROMPT;
+ dtp->u.p.cc.len = 1;
+ break;
+ case '\0':
+ dtp->u.p.cc.type = CCF_OVERPRINT_NOA;
+ dtp->u.p.cc.len = 0;
+ break;
+ default:
+ /* In the default case we copy ONE_LF. */
+ dtp->u.p.cc.type = CCF_DEFAULT;
+ dtp->u.p.cc.len = 1;
+ break;
+ }
+
+ /* We add n-1 to alloc_len so our write buffer is the right size.
+ We are replacing the first character, and possibly prepending some
+ additional characters. Note for n==0, we actually subtract one from
+ alloc_len, which is correct, since that character is skipped. */
+ if (*alloc_len > 0)
+ {
+ *source += 1;
+ *alloc_len += dtp->u.p.cc.len - 1;
+ }
+ /* If we have no input, there is no first character to replace. Make
+ sure we still allocate enough space for the start-of-record string. */
+ else
+ *alloc_len = dtp->u.p.cc.len;
+ }
+}
+
+
+/* Write the start-of-record character(s) for CC_FORTRAN.
+ Also adjusts the 'cc' struct to contain the end-of-record character
+ for next_record_cc.
+ The source_len is set to the remaining length to copy from the source,
+ after the start-of-record string was inserted. */
+
+static char *
+write_cc (st_parameter_dt *dtp, char *p, int *source_len)
+{
+ /* Only valid for CARRIAGECONTROL=FORTRAN. */
+ if (dtp->u.p.current_unit->flags.cc != CC_FORTRAN || source_len == NULL)
+ return p;
+
+ /* Write the start-of-record string to the output buffer. Note that len is
+ never more than 2. */
+ if (dtp->u.p.cc.len > 0)
+ {
+ *(p++) = dtp->u.p.cc.u.start;
+ if (dtp->u.p.cc.len > 1)
+ *(p++) = dtp->u.p.cc.u.start;
+
+ /* source_len comes from write_check_cc where it is set to the full
+ allocated length of the output buffer. Therefore we subtract off the
+ length of the SOR string to obtain the remaining source length. */
+ *source_len -= dtp->u.p.cc.len;
+ }
+
+ /* Common case. */
+ dtp->u.p.cc.len = 1;
+ dtp->u.p.cc.u.end = '\r';
+
+ /* Update end-of-record character for next_record_w. */
+ switch (dtp->u.p.cc.type)
+ {
+ case CCF_PROMPT:
+ case CCF_OVERPRINT_NOA:
+ /* No end-of-record. */
+ dtp->u.p.cc.len = 0;
+ dtp->u.p.cc.u.end = '\0';
+ break;
+ case CCF_OVERPRINT:
+ case CCF_ONE_LF:
+ case CCF_TWO_LF:
+ case CCF_PAGE_FEED:
+ case CCF_DEFAULT:
+ default:
+ /* Carriage return. */
+ dtp->u.p.cc.len = 1;
+ dtp->u.p.cc.u.end = '\r';
+ break;
+ }
+
+ return p;
+}
+
void
write_a (st_parameter_dt *dtp, const fnode *f, const char *source, int len)
{
@@ -296,10 +428,16 @@ write_a (st_parameter_dt *dtp, const fnode *f, const char *source, int len)
else
{
#endif
+ if (dtp->u.p.current_unit->flags.cc == CC_FORTRAN)
+ write_check_cc (dtp, &source, &wlen);
+
p = write_block (dtp, wlen);
if (p == NULL)
return;
+ if (dtp->u.p.current_unit->flags.cc == CC_FORTRAN)
+ p = write_cc (dtp, p, &wlen);
+
if (unlikely (is_char4_unit (dtp)))
{
gfc_char4_t *p4 = (gfc_char4_t *) p;
@@ -1726,7 +1864,8 @@ list_formatted_write_scalar (st_parameter_dt *dtp, bt type, void *p, int kind,
if (dtp->u.p.first_item)
{
dtp->u.p.first_item = 0;
- write_char (dtp, ' ');
+ if (dtp->u.p.current_unit->flags.cc != CC_FORTRAN)
+ write_char (dtp, ' ');
}
else
{