/* Copyright (C) 2002-2003 Free Software Foundation, Inc. Contributed by Andy Vaught This file is part of the GNU Fortran 95 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) any later version. 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* transfer.c -- Top level handling of data transfer statements. */ #include "config.h" #include #include "libgfortran.h" #include "io.h" /* Calling conventions: Data transfer statements are unlike other * library calls in that they extend over several calls. * The first call is always a call to st_read() or st_write(). These * subroutines return no status unless a namelist read or write is * being done, in which case there is the usual status. No further * calls are necessary in this case. * * For other sorts of data transfer, there are zero or more data * transfer statement that depend on the format of the data transfer * statement. * * transfer_integer * transfer_logical * transfer_character * transfer_real * transfer_complex * * These subroutines do not return status. * * The last call is a call to st_[read|write]_done(). While * something can easily go wrong with the initial st_read() or * st_write(), an error inhibits any data from actually being * transferred. */ gfc_unit *current_unit; static int sf_seen_eor = 0; char scratch[SCRATCH_SIZE]; static char *line_buffer = NULL; static unit_advance advance_status; static st_option advance_opt[] = { {"yes", ADVANCE_YES}, {"no", ADVANCE_NO}, {NULL} }; static void (*transfer) (bt, void *, int); typedef enum { FORMATTED_SEQUENTIAL, UNFORMATTED_SEQUENTIAL, FORMATTED_DIRECT, UNFORMATTED_DIRECT } file_mode; static file_mode current_mode (void) { file_mode m; if (current_unit->flags.access == ACCESS_DIRECT) { m = current_unit->flags.form == FORM_FORMATTED ? FORMATTED_DIRECT : UNFORMATTED_DIRECT; } else { m = current_unit->flags.form == FORM_FORMATTED ? FORMATTED_SEQUENTIAL : UNFORMATTED_SEQUENTIAL; } return m; } /* Mid level data transfer statements. These subroutines do reading * and writing in the style of salloc_r()/salloc_w() within the * current record. */ /* read_sf()-- When reading sequential formatted records we have a * problem. We don't know how long the line is until we read the * trailing newline, and we don't want to read too much. If we read * too much, we might have to do a physical seek backwards depending * on how much data is present, and devices like terminals aren't * seekable and would cause an I/O error. * * Given this, the solution is to read a byte at a time, stopping if * we hit the newline. For small locations, we use a static buffer. * For larger allocations, we are forced to allocate memory on the * heap. Hopefully this won't happen very often. */ static char * read_sf (int *length) { static char data[SCRATCH_SIZE]; char *base, *p, *q; int n, unity; if (*length > SCRATCH_SIZE) p = base = line_buffer = get_mem (*length); else p = base = data; memset(base,'\0',*length); current_unit->bytes_left = options.default_recl; unity = 1; n = 0; do { if (is_internal_unit()) { /* unity may be modified inside salloc_r if is_internal_unit() is true */ unity = 1; } q = salloc_r (current_unit->s, &unity); if (q == NULL) break; if (*q == '\n') { if (current_unit->unit_number == options.stdin_unit) { if (n <= 0) continue; } /* Unexpected end of line */ if (current_unit->flags.pad == PAD_NO) { generate_error (ERROR_EOR, NULL); return NULL; } current_unit->bytes_left = 0; *length = n; sf_seen_eor = 1; break; } n++; *p++ = *q; sf_seen_eor = 0; } while (n < *length); return base; } /* read_block()-- Function for reading the next couple of bytes from * the current file, advancing the current position. We return a * pointer to a buffer containing the bytes. We return NULL on end of * record or end of file. * * If the read is short, then it is because the current record does not * have enough data to satisfy the read request and the file was * opened with PAD=YES. The caller must assume tailing spaces for * short reads. */ void * read_block (int *length) { char *source; int nread; if (current_unit->flags.form == FORM_FORMATTED && current_unit->flags.access == ACCESS_SEQUENTIAL) return read_sf (length); /* Special case */ if (current_unit->bytes_left < *length) { if (current_unit->flags.pad == PAD_NO) { generate_error (ERROR_EOR, NULL); /* Not enough data left */ return NULL; } *length = current_unit->bytes_left; } current_unit->bytes_left -= *length; nread = *length; source = salloc_r (current_unit->s, &nread); if (ioparm.size != NULL) *ioparm.size += nread; if (nread != *length) { /* Short read, this shouldn't happen */ if (current_unit->flags.pad == PAD_YES) *length = nread; else { generate_error (ERROR_EOR, NULL); source = NULL; } } return source; } /* write_block()-- Function for writing a block of bytes to the * current file at the current position, advancing the file pointer. * We are given a length and return a pointer to a buffer that the * caller must (completely) fill in. Returns NULL on error. */ void * write_block (int length) { char *dest; if (!is_internal_unit() && current_unit->bytes_left < length) { generate_error (ERROR_EOR, NULL); return NULL; } current_unit->bytes_left -= length; dest = salloc_w (current_unit->s, &length); if (ioparm.size != NULL) *ioparm.size += length; return dest; } /* unformatted_read()-- Master function for unformatted reads. */ static void unformatted_read (bt type, void *dest, int length) { void *source; int w; w = length; source = read_block (&w); if (source != NULL) { memcpy (dest, source, w); if (length != w) memset (((char *) dest) + w, ' ', length - w); } } static void unformatted_write (bt type, void *source, int length) { void *dest; dest = write_block (length); if (dest != NULL) memcpy (dest, source, length); } /* type_name()-- Return a pointer to the name of a type. */ const char * type_name (bt type) { const char *p; switch (type) { case BT_INTEGER: p = "INTEGER"; break; case BT_LOGICAL: p = "LOGICAL"; break; case BT_CHARACTER: p = "CHARACTER"; break; case BT_REAL: p = "REAL"; break; case BT_COMPLEX: p = "COMPLEX"; break; default: internal_error ("type_name(): Bad type"); } return p; } /* write_constant_string()-- write a constant string to the output. * This is complicated because the string can have doubled delimiters * in it. The length in the format node is the true length. */ static void write_constant_string (fnode * f) { char c, delimiter, *p, *q; int length; length = f->u.string.length; if (length == 0) return; p = write_block (length); if (p == NULL) return; q = f->u.string.p; delimiter = q[-1]; for (; length > 0; length--) { c = *p++ = *q++; if (c == delimiter && c != 'H') q++; /* Skip the doubled delimiter */ } } /* require_type()-- Given actual and expected types in a formatted * data transfer, make sure they agree. If not, an error message is * generated. Returns nonzero if something went wrong. */ static int require_type (bt expected, bt actual, fnode * f) { char buffer[100]; if (actual == expected) return 0; st_sprintf (buffer, "Expected %s for item %d in formatted transfer, got %s", type_name (expected), g.item_count, type_name (actual)); format_error (f, buffer); return 1; } /* formatted_transfer()-- This subroutine is the main loop for a * formatted data transfer statement. It would be natural to * implement this as a coroutine with the user program, but C makes * that awkward. We loop, processesing format elements. When we * actually have to transfer data instead of just setting flags, we * return control to the user program which calls a subroutine that * supplies the address and type of the next element, then comes back * here to process it. */ static void formatted_transfer (bt type, void *p, int len) { int pos ,m ; fnode *f; int i, n; int consume_data_flag; /* Change a complex data item into a pair of reals */ n = (p == NULL) ? 0 : ((type != BT_COMPLEX) ? 1 : 2); if (type == BT_COMPLEX) type = BT_REAL; /* If reversion has occurred and there is another real data item, * then we have to move to the next record */ if (g.reversion_flag && n > 0) { g.reversion_flag = 0; next_record (0); } for (;;) { consume_data_flag = 1 ; if (ioparm.library_return != LIBRARY_OK) break; f = next_format (); if (f == NULL) return; /* No data descriptors left (already raised) */ switch (f->format) { case FMT_I: if (n == 0) goto need_data; if (require_type (BT_INTEGER, type, f)) return; if (g.mode == READING) read_decimal (f, p, len); else write_i (f, p, len); break; case FMT_B: if (n == 0) goto need_data; if (require_type (BT_INTEGER, type, f)) return; if (g.mode == READING) read_radix (f, p, len, 2); else write_b (f, p, len); break; case FMT_O: if (n == 0) goto need_data; if (g.mode == READING) read_radix (f, p, len, 8); else write_o (f, p, len); break; case FMT_Z: if (n == 0) goto need_data; if (g.mode == READING) read_radix (f, p, len, 16); else write_z (f, p, len); break; case FMT_A: if (n == 0) goto need_data; if (require_type (BT_CHARACTER, type, f)) return; if (g.mode == READING) read_a (f, p, len); else write_a (f, p, len); break; case FMT_L: if (n == 0) goto need_data; if (g.mode == READING) read_l (f, p, len); else write_l (f, p, len); break; case FMT_D: if (n == 0) goto need_data; if (require_type (BT_REAL, type, f)) return; if (g.mode == READING) read_f (f, p, len); else write_d (f, p, len); break; case FMT_E: if (n == 0) goto need_data; if (require_type (BT_REAL, type, f)) return; if (g.mode == READING) read_f (f, p, len); else write_e (f, p, len); break; case FMT_EN: if (n == 0) goto need_data; if (require_type (BT_REAL, type, f)) return; if (g.mode == READING) read_f (f, p, len); else write_en (f, p, len); break; case FMT_ES: if (n == 0) goto need_data; if (require_type (BT_REAL, type, f)) return; if (g.mode == READING) read_f (f, p, len); else write_es (f, p, len); break; case FMT_F: if (n == 0) goto need_data; if (require_type (BT_REAL, type, f)) return; if (g.mode == READING) read_f (f, p, len); else write_f (f, p, len); break; case FMT_G: if (n == 0) goto need_data; if (g.mode == READING) switch (type) { case BT_INTEGER: read_decimal (f, p, len); break; case BT_LOGICAL: read_l (f, p, len); break; case BT_CHARACTER: read_a (f, p, len); break; case BT_REAL: read_f (f, p, len); break; default: goto bad_type; } else switch (type) { case BT_INTEGER: write_i (f, p, len); break; case BT_LOGICAL: write_l (f, p, len); break; case BT_CHARACTER: write_a (f, p, len); break; case BT_REAL: write_d (f, p, len); break; default: bad_type: internal_error ("formatted_transfer(): Bad type"); } break; case FMT_STRING: consume_data_flag = 0 ; if (g.mode == READING) { format_error (f, "Constant string in input format"); return; } write_constant_string (f); break; /* Format codes that don't transfer data */ case FMT_X: case FMT_TR: consume_data_flag = 0 ; if (g.mode == READING) read_x (f); else write_x (f); break; case FMT_T: pos = f->u.n ; pos= current_unit->recl - current_unit->bytes_left - pos; /* fall through */ case FMT_TL: consume_data_flag = 0 ; pos = f->u.n ; if (pos < 0 || pos >= current_unit->recl ) { generate_error (ERROR_EOR, "T Or TL edit position error"); break ; } m = pos - (current_unit->recl - current_unit->bytes_left); if (m == 0) break; if (m > 0) { f->u.n = m; if (g.mode == READING) read_x (f); else write_x (f); } if (m < 0) { move_pos_offset (current_unit->s,m); } break; case FMT_S: consume_data_flag = 0 ; g.sign_status = SIGN_S; break; case FMT_SS: consume_data_flag = 0 ; g.sign_status = SIGN_SS; break; case FMT_SP: consume_data_flag = 0 ; g.sign_status = SIGN_SP; break; case FMT_BN: consume_data_flag = 0 ; g.blank_status = BLANK_NULL; break; case FMT_BZ: consume_data_flag = 0 ; g.blank_status = BLANK_ZERO; break; case FMT_P: consume_data_flag = 0 ; g.scale_factor = f->u.k; break; case FMT_DOLLAR: consume_data_flag = 0 ; g.seen_dollar = 1; break; case FMT_SLASH: consume_data_flag = 0 ; for (i = 0; i < f->repeat; i++) next_record (0); break; case FMT_COLON: /* A colon descriptor causes us to exit this loop (in particular * preventing another / descriptor from being processed) unless there * is another data item to be transferred. */ consume_data_flag = 0 ; if (n == 0) return; break; default: internal_error ("Bad format node"); } /* Free a buffer that we had to allocate during a sequential * formatted read of a block that was larger than the static * buffer. */ if (line_buffer != NULL) { free_mem (line_buffer); line_buffer = NULL; } /* Adjust the item count and data pointer */ if ((consume_data_flag > 0) && (n > 0)) { n--; p = ((char *) p) + len; } } return; /* Come here when we need a data descriptor but don't have one. We * push the current format node back onto the input, then return and * let the user program call us back with the data. */ need_data: unget_format (f); } /* Data transfer entry points. The type of the data entity is * implicit in the subroutine call. This prevents us from having to * share a common enum with the compiler. */ void transfer_integer (void *p, int kind) { g.item_count++; if (ioparm.library_return != LIBRARY_OK) return; transfer (BT_INTEGER, p, kind); } void transfer_real (void *p, int kind) { g.item_count++; if (ioparm.library_return != LIBRARY_OK) return; transfer (BT_REAL, p, kind); } void transfer_logical (void *p, int kind) { g.item_count++; if (ioparm.library_return != LIBRARY_OK) return; transfer (BT_LOGICAL, p, kind); } void transfer_character (void *p, int len) { g.item_count++; if (ioparm.library_return != LIBRARY_OK) return; transfer (BT_CHARACTER, p, len); } void transfer_complex (void *p, int kind) { g.item_count++; if (ioparm.library_return != LIBRARY_OK) return; transfer (BT_COMPLEX, p, kind); } /* us_read()-- Preposition a sequential unformatted file while reading. */ static void us_read (void) { offset_t *p; int n; n = sizeof (offset_t); p = (offset_t *) salloc_r (current_unit->s, &n); if (p == NULL || n != sizeof (offset_t)) { generate_error (ERROR_BAD_US, NULL); return; } current_unit->bytes_left = *p; } /* us_write()-- Preposition a sequential unformatted file while * writing. This amount to writing a bogus length that will be filled * in later. */ static void us_write (void) { offset_t *p; int length; length = sizeof (offset_t); p = (offset_t *) salloc_w (current_unit->s, &length); if (p == NULL) { generate_error (ERROR_OS, NULL); return; } *p = 0; /* Bogus value for now */ if (sfree (current_unit->s) == FAILURE) generate_error (ERROR_OS, NULL); current_unit->bytes_left = current_unit->recl; } /* pre_position()-- position to the next record prior to transfer. We * are assumed to be before the next record. We also calculate the * bytes in the next record. */ static void pre_position (void) { if (current_unit->current_record) return; /* Already positioned */ switch (current_mode ()) { case UNFORMATTED_SEQUENTIAL: if (g.mode == READING) us_read (); else us_write (); break; case FORMATTED_SEQUENTIAL: case FORMATTED_DIRECT: case UNFORMATTED_DIRECT: current_unit->bytes_left = current_unit->recl; break; } current_unit->current_record = 1; } /* data_transfer_init()-- Initialize things for a data transfer. This * code is common for both reading and writing. */ static void data_transfer_init (int read_flag) { unit_flags u_flags; /* used for creating a unit if needed */ g.mode = read_flag ? READING : WRITING; if (ioparm.size != NULL) *ioparm.size = 0; /* Initialize the count */ current_unit = get_unit (read_flag); if (current_unit == NULL) { /* open the unit with some default flags */ memset (&u_flags, '\0', sizeof (u_flags)); u_flags.access = ACCESS_SEQUENTIAL; u_flags.action = ACTION_READWRITE; u_flags.form = FORM_UNSPECIFIED; u_flags.delim = DELIM_UNSPECIFIED; u_flags.blank = BLANK_UNSPECIFIED; u_flags.pad = PAD_UNSPECIFIED; u_flags.status = STATUS_UNKNOWN; new_unit(&u_flags); current_unit = get_unit (read_flag); } if (current_unit == NULL) return; if (is_internal_unit() && g.mode==WRITING) empty_internal_buffer (current_unit->s); /* Check the action */ if (read_flag && current_unit->flags.action == ACTION_WRITE) generate_error (ERROR_BAD_ACTION, "Cannot read from file opened for WRITE"); if (!read_flag && current_unit->flags.action == ACTION_READ) generate_error (ERROR_BAD_ACTION, "Cannot write to file opened for READ"); if (ioparm.library_return != LIBRARY_OK) return; /* Check the format */ if (ioparm.format) parse_format (); if (ioparm.library_return != LIBRARY_OK) return; if (current_unit->flags.form == FORM_UNFORMATTED && (ioparm.format != NULL || ioparm.list_format)) generate_error (ERROR_OPTION_CONFLICT, "Format present for UNFORMATTED data transfer"); if (ioparm.namelist_name != NULL && ionml != NULL) { if(ioparm.format != NULL) generate_error (ERROR_OPTION_CONFLICT, "A format cannot be specified with a namelist"); } else if (current_unit->flags.form == FORM_FORMATTED && ioparm.format == NULL && !ioparm.list_format) generate_error (ERROR_OPTION_CONFLICT, "Missing format for FORMATTED data transfer"); if (is_internal_unit () && current_unit->flags.form == FORM_UNFORMATTED) generate_error (ERROR_OPTION_CONFLICT, "Internal file cannot be accessed by UNFORMATTED data transfer"); /* Check the record number */ if (current_unit->flags.access == ACCESS_DIRECT && ioparm.rec == 0) { generate_error (ERROR_MISSING_OPTION, "Direct access data transfer requires record number"); return; } if (current_unit->flags.access == ACCESS_SEQUENTIAL && ioparm.rec != 0) { generate_error (ERROR_OPTION_CONFLICT, "Record number not allowed for sequential access data transfer"); return; } /* Process the ADVANCE option */ advance_status = (ioparm.advance == NULL) ? ADVANCE_UNSPECIFIED : find_option (ioparm.advance, ioparm.advance_len, advance_opt, "Bad ADVANCE parameter in data transfer statement"); if (advance_status != ADVANCE_UNSPECIFIED) { if (current_unit->flags.access == ACCESS_DIRECT) generate_error (ERROR_OPTION_CONFLICT, "ADVANCE specification conflicts with sequential access"); if (is_internal_unit ()) generate_error (ERROR_OPTION_CONFLICT, "ADVANCE specification conflicts with internal file"); if (ioparm.format == NULL || ioparm.list_format) generate_error (ERROR_OPTION_CONFLICT, "ADVANCE specification requires an explicit format"); } if (read_flag) { if (ioparm.eor != 0 && advance_status == ADVANCE_NO) generate_error (ERROR_MISSING_OPTION, "EOR specification requires an ADVANCE specification of NO"); if (ioparm.size != NULL && advance_status != ADVANCE_NO) generate_error (ERROR_MISSING_OPTION, "SIZE specification requires an ADVANCE specification of NO"); } else { /* Write constraints */ if (ioparm.end != 0) generate_error (ERROR_OPTION_CONFLICT, "END specification cannot appear in a write statement"); if (ioparm.eor != 0) generate_error (ERROR_OPTION_CONFLICT, "EOR specification cannot appear in a write statement"); if (ioparm.size != 0) generate_error (ERROR_OPTION_CONFLICT, "SIZE specification cannot appear in a write statement"); } if (advance_status == ADVANCE_UNSPECIFIED) advance_status = ADVANCE_YES; if (ioparm.library_return != LIBRARY_OK) return; /* Sanity checks on the record number */ if (ioparm.rec) { if (ioparm.rec <= 0) { generate_error (ERROR_BAD_OPTION, "Record number must be positive"); return; } if (ioparm.rec >= current_unit->maxrec) { generate_error (ERROR_BAD_OPTION, "Record number too large"); return; } /* Position the file */ if (sseek (current_unit->s, (ioparm.rec - 1) * current_unit->recl) == FAILURE) generate_error (ERROR_OS, NULL); } /* Set the initial value of flags */ g.blank_status = current_unit->flags.blank; g.sign_status = SIGN_S; g.scale_factor = 0; g.seen_dollar = 0; g.first_item = 1; g.item_count = 0; pre_position (); /* Set up the subroutine that will handle the transfers */ if (read_flag) { if (current_unit->flags.form == FORM_UNFORMATTED) transfer = unformatted_read; else { if (ioparm.list_format) { transfer = list_formatted_read; init_at_eol(); } else transfer = formatted_transfer; } } else { if (current_unit->flags.form == FORM_UNFORMATTED) transfer = unformatted_write; else { if (ioparm.list_format) transfer = list_formatted_write; else transfer = formatted_transfer; } } /* Make sure that we don't do a read after a nonadvancing write */ if (read_flag) { if (current_unit->read_bad) { generate_error (ERROR_BAD_OPTION, "Cannot READ after a nonadvancing WRITE"); return; } } else { if (advance_status == ADVANCE_YES) current_unit->read_bad = 1; } /* Start the data transfer if we are doing a formatted transfer */ if (current_unit->flags.form == FORM_FORMATTED && !ioparm.list_format && ioparm.namelist_name == NULL && ionml == NULL) formatted_transfer (0, NULL, 0); } /* next_record_r()-- Space to the next record for read mode. If the * file is not seekable, we read MAX_READ chunks until we get to the * right position. */ #define MAX_READ 4096 static void next_record_r (int done) { int rlength, length; offset_t new; char *p; switch (current_mode ()) { case UNFORMATTED_SEQUENTIAL: current_unit->bytes_left += sizeof (offset_t); /* Skip over tail */ /* Fall through */ case FORMATTED_DIRECT: case UNFORMATTED_DIRECT: if (current_unit->bytes_left == 0) break; if (is_seekable (current_unit->s)) { new = file_position (current_unit->s) + current_unit->bytes_left; /* Direct access files do not generate END conditions, only I/O errors */ if (sseek (current_unit->s, new) == FAILURE) generate_error (ERROR_OS, NULL); } else { /* Seek by reading data */ while (current_unit->bytes_left > 0) { rlength = length = (MAX_READ > current_unit->bytes_left) ? MAX_READ : current_unit->bytes_left; p = salloc_r (current_unit->s, &rlength); if (p == NULL) { generate_error (ERROR_OS, NULL); break; } current_unit->bytes_left -= length; } } break; case FORMATTED_SEQUENTIAL: length = 1; if (sf_seen_eor && done) break; do { p = salloc_r (current_unit->s, &length); /*In case of internal file, there may not be any '\n'.*/ if (is_internal_unit() && p == NULL) { break; } if (p == NULL) { generate_error (ERROR_OS, NULL); break; } if (length == 0) { current_unit->endfile = AT_ENDFILE; break; } } while (*p != '\n'); break; } if (current_unit->flags.access == ACCESS_SEQUENTIAL) test_endfile (current_unit); } /* next_record_w()-- Position to the next record in write mode */ static void next_record_w (int done) { offset_t c, m; int length; char *p; switch (current_mode ()) { case FORMATTED_DIRECT: case UNFORMATTED_DIRECT: if (current_unit->bytes_left == 0) break; length = current_unit->bytes_left; p = salloc_w (current_unit->s, &length); if (p == NULL) goto io_error; memset (p, ' ', current_unit->bytes_left); if (sfree (current_unit->s) == FAILURE) goto io_error; break; case UNFORMATTED_SEQUENTIAL: m = current_unit->recl - current_unit->bytes_left; /* Bytes written */ c = file_position (current_unit->s); length = sizeof (offset_t); /* Write the length tail */ p = salloc_w (current_unit->s, &length); if (p == NULL) goto io_error; *((offset_t *) p) = m; if (sfree (current_unit->s) == FAILURE) goto io_error; /* Seek to the head and overwrite the bogus length with the real length */ p = salloc_w_at (current_unit->s, &length, c - m - length); if (p == NULL) generate_error (ERROR_OS, NULL); *((offset_t *) p) = m; if (sfree (current_unit->s) == FAILURE) goto io_error; /* Seek past the end of the current record */ if (sseek (current_unit->s, c + sizeof (offset_t)) == FAILURE) goto io_error; break; case FORMATTED_SEQUENTIAL: length = 1; p = salloc_w (current_unit->s, &length); if (!is_internal_unit()) { if (p) *p = '\n'; /* no CR for internal writes */ else goto io_error; } if (sfree (current_unit->s) == FAILURE) goto io_error; break; io_error: generate_error (ERROR_OS, NULL); break; } } /* next_record()-- Position to the next record, which means moving to * the end of the current record. This can happen under several * different conditions. If the done flag is not set, we get ready to * process the next record. */ void next_record (int done) { current_unit->read_bad = 0; if (g.mode == READING) next_record_r (done); else next_record_w (done); current_unit->current_record = 0; if (current_unit->flags.access == ACCESS_DIRECT) current_unit->last_record = file_position (current_unit->s) / current_unit->recl; else current_unit->last_record++; if (!done) pre_position (); } /* Finalize the current data transfer. For a nonadvancing transfer, * this means advancing to the next record. */ static void finalize_transfer (void) { if (setjmp (g.eof_jump)) { generate_error (ERROR_END, NULL); return; } if ((ionml != NULL) && (ioparm.namelist_name != NULL)) { if (ioparm.namelist_read_mode) namelist_read(); else namelist_write(); } transfer = NULL; if (current_unit == NULL) return; if (ioparm.list_format && g.mode == READING) finish_list_read (); else { free_fnodes (); if (advance_status == ADVANCE_NO) return; next_record (1); current_unit->current_record = 0; } sfree (current_unit->s); } /* The READ statement */ void st_read (void) { library_start (); data_transfer_init (1); /* Handle complications dealing with the endfile record. It is * significant that this is the only place where ERROR_END is * generated. Reading an end of file elsewhere is either end of * record or an I/O error. */ if (current_unit->flags.access == ACCESS_SEQUENTIAL) switch (current_unit->endfile) { case NO_ENDFILE: break; case AT_ENDFILE: if (!is_internal_unit()) { generate_error (ERROR_END, NULL); current_unit->endfile = AFTER_ENDFILE; } break; case AFTER_ENDFILE: generate_error (ERROR_ENDFILE, NULL); break; } } void st_read_done (void) { finalize_transfer (); library_end (); } void st_write (void) { library_start (); data_transfer_init (0); } void st_write_done (void) { finalize_transfer (); /* Deal with endfile conditions associated with sequential files */ if (current_unit != NULL && current_unit->flags.access == ACCESS_SEQUENTIAL) switch (current_unit->endfile) { case AT_ENDFILE: /* Remain at the endfile record */ break; case AFTER_ENDFILE: current_unit->endfile = AT_ENDFILE; /* Just at it now */ break; case NO_ENDFILE: /* Get rid of whatever is after this record */ if (struncate (current_unit->s) == FAILURE) generate_error (ERROR_OS, NULL); current_unit->endfile = AT_ENDFILE; break; } library_end (); } static void st_set_nml_var (void * var_addr, char * var_name, int var_name_len, int kind, bt type) { namelist_info *t1 = NULL, *t2 = NULL; namelist_info *nml = (namelist_info *) get_mem (sizeof( namelist_info )); nml->mem_pos = var_addr; nml->var_name = (char*) get_mem (var_name_len+1); strncpy (nml->var_name,var_name,var_name_len); nml->var_name[var_name_len] = 0; nml->len = kind; nml->type = type; nml->next = NULL; if (ionml == NULL) ionml = nml; else { t1 = ionml; while (t1 != NULL) { t2 = t1; t1 = t1->next; } t2->next = nml; } } void st_set_nml_var_int (void * var_addr, char * var_name, int var_name_len, int kind) { st_set_nml_var (var_addr, var_name, var_name_len, kind, BT_INTEGER); } void st_set_nml_var_float (void * var_addr, char * var_name, int var_name_len, int kind) { st_set_nml_var (var_addr, var_name, var_name_len, kind, BT_REAL); } void st_set_nml_var_char (void * var_addr, char * var_name, int var_name_len, int kind) { st_set_nml_var (var_addr, var_name, var_name_len, kind, BT_CHARACTER); } void st_set_nml_var_complex (void * var_addr, char * var_name, int var_name_len, int kind) { st_set_nml_var (var_addr, var_name, var_name_len, kind, BT_COMPLEX); } void st_set_nml_var_log (void * var_addr, char * var_name, int var_name_len, int kind) { st_set_nml_var (var_addr, var_name, var_name_len, kind, BT_LOGICAL); }