diff options
author | Fritz Reese <fritzoreese@gmail.com> | 2016-10-26 12:11:44 +0000 |
---|---|---|
committer | Fritz Reese <foreese@gcc.gnu.org> | 2016-10-26 12:11:44 +0000 |
commit | 0ef33d44629066e33ffdc46014374a3ef5c5f009 (patch) | |
tree | 45d5f5c66da6b5b2820d025ba02eb604ab6a5b91 /libgfortran/io | |
parent | 9dbe100a4157843d12b192e4aae504b43af4774b (diff) | |
download | gcc-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.c | 16 | ||||
-rw-r--r-- | libgfortran/io/file_pos.c | 2 | ||||
-rw-r--r-- | libgfortran/io/inquire.c | 58 | ||||
-rw-r--r-- | libgfortran/io/io.h | 51 | ||||
-rw-r--r-- | libgfortran/io/open.c | 47 | ||||
-rw-r--r-- | libgfortran/io/read.c | 3 | ||||
-rw-r--r-- | libgfortran/io/transfer.c | 59 | ||||
-rw-r--r-- | libgfortran/io/unit.c | 6 | ||||
-rw-r--r-- | libgfortran/io/unix.c | 89 | ||||
-rw-r--r-- | libgfortran/io/unix.h | 3 | ||||
-rw-r--r-- | libgfortran/io/write.c | 141 |
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 { |