aboutsummaryrefslogtreecommitdiff
path: root/libgfortran/io
diff options
context:
space:
mode:
authorDiego Novillo <dnovillo@gcc.gnu.org>2004-05-13 02:41:07 -0400
committerDiego Novillo <dnovillo@gcc.gnu.org>2004-05-13 02:41:07 -0400
commit6de9cd9a886ea695aa892c3c7c07818a7b7e9e6f (patch)
treea2568888a519c077427b133de9ece5879a8484a5 /libgfortran/io
parentac1a20aec53364d77f3bdff94a2a0a06840e0fe9 (diff)
downloadgcc-6de9cd9a886ea695aa892c3c7c07818a7b7e9e6f.zip
gcc-6de9cd9a886ea695aa892c3c7c07818a7b7e9e6f.tar.gz
gcc-6de9cd9a886ea695aa892c3c7c07818a7b7e9e6f.tar.bz2
Merge tree-ssa-20020619-branch into mainline.
From-SVN: r81764
Diffstat (limited to 'libgfortran/io')
-rw-r--r--libgfortran/io/backspace.c160
-rw-r--r--libgfortran/io/close.c70
-rw-r--r--libgfortran/io/endfile.c46
-rw-r--r--libgfortran/io/format.c1285
-rw-r--r--libgfortran/io/inquire.c371
-rw-r--r--libgfortran/io/io.h653
-rw-r--r--libgfortran/io/list_read.c1531
-rw-r--r--libgfortran/io/lock.c84
-rw-r--r--libgfortran/io/open.c528
-rw-r--r--libgfortran/io/read.c793
-rw-r--r--libgfortran/io/rewind.c56
-rw-r--r--libgfortran/io/transfer.c1498
-rw-r--r--libgfortran/io/unit.c380
-rw-r--r--libgfortran/io/unix.c1432
-rw-r--r--libgfortran/io/write.c1129
15 files changed, 10016 insertions, 0 deletions
diff --git a/libgfortran/io/backspace.c b/libgfortran/io/backspace.c
new file mode 100644
index 0000000..7502f1d
--- /dev/null
+++ b/libgfortran/io/backspace.c
@@ -0,0 +1,160 @@
+/* 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. */
+
+#include "config.h"
+#include "libgfortran.h"
+#include "io.h"
+
+/* backspace.c -- Implement the BACKSPACE statement */
+
+/* formatted_backspace(void)-- Move the file back one line. The
+ * current position is after the newline that terminates the previous
+ * record, and we have to sift backwards to find the newline before
+ * that or the start of the file, whichever comes first. */
+
+#define READ_CHUNK 4096
+
+static void
+formatted_backspace (void)
+{
+ offset_t base;
+ char *p;
+ int n;
+
+ base = file_position (current_unit->s) - 1;
+
+ do
+ {
+ n = (base < READ_CHUNK) ? base : READ_CHUNK;
+ base -= n;
+
+ p = salloc_r_at (current_unit->s, &n, base);
+ if (p == NULL)
+ goto io_error;
+
+ /* Because we've moved backwords from the current position, it
+ * should not be possible to get a short read. Because it isn't
+ * clear what to do about such thing, we ignore the possibility. */
+
+ /* There is no memrchr() in the C library, so we have to do it
+ * ourselves. */
+
+ n--;
+ while (n >= 0)
+ {
+ if (p[n] == '\n')
+ {
+ base += n + 1;
+ goto done;
+ }
+
+ n--;
+ }
+
+ }
+ while (base != 0);
+
+/* base is the new pointer. Seek to it exactly */
+
+done:
+ if (sseek (current_unit->s, base) == FAILURE)
+ goto io_error;
+ current_unit->last_record--;
+
+ return;
+
+io_error:
+ generate_error (ERROR_OS, NULL);
+}
+
+
+/* unformatted_backspace()-- Move the file backwards for an
+ * unformatted sequential file. We are guaranteed to be between
+ * records on entry and we have to shift to the previous record. */
+
+static void
+unformatted_backspace (void)
+{
+ offset_t *p, new;
+ int length;
+
+ length = sizeof (offset_t);
+
+ p = (offset_t *) salloc_r_at (current_unit->s, &length,
+ file_position (current_unit->s) - length);
+ if (p == NULL)
+ goto io_error;
+
+ new = file_position (current_unit->s) - *p - length;
+ if (sseek (current_unit->s, new) == FAILURE)
+ goto io_error;
+
+ current_unit->last_record--;
+ return;
+
+io_error:
+ generate_error (ERROR_OS, NULL);
+}
+
+
+void
+st_backspace (void)
+{
+ unit_t *u;
+
+ library_start ();
+
+ u = find_unit (ioparm.unit);
+ if (u == NULL)
+ {
+ generate_error (ERROR_BAD_UNIT, NULL);
+ goto done;
+ }
+
+ current_unit = u;
+
+ /* Ignore direct access. Non-advancing I/O is only allowed for
+ * formatted sequential I/O and the next direct access transfer
+ * repositions the file anyway. */
+
+ if (u->flags.access == ACCESS_DIRECT)
+ goto done;
+
+ /* Check for special cases involving the ENDFILE record first */
+
+ if (u->endfile == AFTER_ENDFILE)
+ u->endfile = AT_ENDFILE;
+ else
+ {
+ if (u->current_record)
+ next_record (1);
+
+ if (file_position (u->s) == 0)
+ goto done; /* Common special case */
+
+ if (u->flags.form == FORM_UNFORMATTED)
+ formatted_backspace ();
+ else
+ unformatted_backspace ();
+ }
+
+done:
+ library_end ();
+}
diff --git a/libgfortran/io/close.c b/libgfortran/io/close.c
new file mode 100644
index 0000000..9e2a5a3
--- /dev/null
+++ b/libgfortran/io/close.c
@@ -0,0 +1,70 @@
+/* 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. */
+
+#include "config.h"
+#include "libgfortran.h"
+#include "io.h"
+
+typedef enum
+{ CLOSE_DELETE, CLOSE_KEEP, CLOSE_UNSPECIFIED }
+close_status;
+
+static st_option status_opt[] = {
+ {"keep", CLOSE_KEEP},
+ {"delete", CLOSE_DELETE},
+ {NULL}
+};
+
+
+void
+st_close (void)
+{
+ close_status status;
+ unit_t *u;
+
+ library_start ();
+
+ status = (ioparm.status == NULL) ? CLOSE_UNSPECIFIED :
+ find_option (ioparm.status, ioparm.status_len, status_opt,
+ "Bad STATUS parameter in CLOSE statement");
+
+ if (ioparm.library_return != LIBRARY_OK)
+ return;
+
+ u = find_unit (ioparm.unit);
+ if (u != NULL)
+ {
+ if (u->flags.status == STATUS_SCRATCH)
+ {
+ if (status == CLOSE_KEEP)
+ generate_error (ERROR_BAD_OPTION,
+ "Can't KEEP a scratch file on CLOSE");
+ }
+ else
+ {
+ if (status == CLOSE_DELETE)
+ delete_file (u);
+ }
+
+ close_unit (u);
+ }
+
+ library_end ();
+}
diff --git a/libgfortran/io/endfile.c b/libgfortran/io/endfile.c
new file mode 100644
index 0000000..56f81f0
--- /dev/null
+++ b/libgfortran/io/endfile.c
@@ -0,0 +1,46 @@
+/* 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. */
+
+#include "config.h"
+#include "libgfortran.h"
+#include "io.h"
+
+/* endfile.c-- Implement the ENDFILE statement */
+
+void
+st_endfile (void)
+{
+ unit_t *u;
+
+ library_start ();
+
+ u = get_unit (0);
+ if (u != NULL)
+ {
+ current_unit = u; /* next_record() needs this set */
+ if (u->current_record)
+ next_record (1);
+
+ struncate (u->s);
+ u->endfile = AFTER_ENDFILE;
+ }
+
+ library_end ();
+}
diff --git a/libgfortran/io/format.c b/libgfortran/io/format.c
new file mode 100644
index 0000000..caec167
--- /dev/null
+++ b/libgfortran/io/format.c
@@ -0,0 +1,1285 @@
+/* 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. */
+
+
+/* format.c-- parse a FORMAT string into a binary format suitable for
+ * interpretation during I/O statements */
+
+#include "config.h"
+#include <ctype.h>
+#include <string.h>
+#include "libgfortran.h"
+#include "io.h"
+
+
+
+/* Number of format nodes that we can store statically before we have
+ * to resort to dynamic allocation. The root node is array[0]. */
+
+#define FARRAY_SIZE 200
+
+static fnode *avail, array[FARRAY_SIZE];
+
+/* Local variables for checking format strings. The saved_token is
+ * used to back up by a single format token during the parsing process. */
+
+static char *format_string, *string;
+static const char *error;
+static format_token saved_token;
+static int value, format_string_len, reversion_ok;
+
+static fnode *saved_format, colon_node = { FMT_COLON };
+
+/* Error messages */
+
+static char posint_required[] = "Positive width required in format",
+ period_required[] = "Period required in format",
+ nonneg_required[] = "Nonnegative width required in format",
+ unexpected_element[] = "Unexpected element in format",
+ unexpected_end[] = "Unexpected end of format string",
+ bad_string[] = "Unterminated character constant in format",
+ bad_hollerith[] = "Hollerith constant extends past the end of the format",
+ reversion_error[] = "Exhausted data descriptors in format";
+
+
+/* next_char()-- Return the next character in the format string.
+ * Returns -1 when the string is done. If the literal flag is set,
+ * spaces are significant, otherwise they are not. */
+
+static int
+next_char (int literal)
+{
+ int c;
+
+ do
+ {
+ if (format_string_len == 0)
+ return -1;
+
+ format_string_len--;
+ c = toupper (*format_string++);
+ }
+ while (c == ' ' && !literal);
+
+ return c;
+}
+
+
+/* unget_char()-- Back up one character position. */
+
+#define unget_char() { format_string--; format_string_len++; }
+
+
+/* get_fnode()-- Allocate a new format node, inserting it into the
+ * current singly linked list. These are initially allocated from the
+ * static buffer. */
+
+static fnode *
+get_fnode (fnode ** head, fnode ** tail, format_token t)
+{
+ fnode *f;
+
+ if (avail - array >= FARRAY_SIZE)
+ f = get_mem (sizeof (fnode));
+ else
+ {
+ f = avail++;
+ memset (f, '\0', sizeof (fnode));
+ }
+
+ if (*head == NULL)
+ *head = *tail = f;
+ else
+ {
+ (*tail)->next = f;
+ *tail = f;
+ }
+
+ f->format = t;
+ f->repeat = -1;
+ f->source = format_string;
+ return f;
+}
+
+
+/* free_fnode()-- Recursive function to free the given fnode and
+ * everything it points to. We only have to actually free something
+ * if it is outside of the static array. */
+
+static void
+free_fnode (fnode * f)
+{
+ fnode *next;
+
+ for (; f; f = next)
+ {
+ next = f->next;
+
+ if (f->format == FMT_LPAREN)
+ free_fnode (f->u.child);
+ if (f < array || f >= array + FARRAY_SIZE)
+ free_mem (f);
+ }
+}
+
+
+/* free_fnodes()-- Free the current tree of fnodes. We only have to
+ * traverse the tree if some nodes were allocated dynamically. */
+
+void
+free_fnodes (void)
+{
+
+ if (avail - array >= FARRAY_SIZE)
+ free_fnode (&array[0]);
+
+ avail = array;
+ memset(array, 0, sizeof(avail[0]) * FARRAY_SIZE);
+}
+
+
+/* format_lex()-- Simple lexical analyzer for getting the next token
+ * in a FORMAT string. We support a one-level token pushback in the
+ * saved_token variable. */
+
+static format_token
+format_lex (void)
+{
+ format_token token;
+ int negative_flag;
+ char c, delim;
+
+ if (saved_token != FMT_NONE)
+ {
+ token = saved_token;
+ saved_token = FMT_NONE;
+ return token;
+ }
+
+ negative_flag = 0;
+ c = next_char (0);
+
+ switch (c)
+ {
+ case '-':
+ negative_flag = 1;
+ /* Fall Through */
+
+ case '+':
+ c = next_char (0);
+ if (!isdigit (c))
+ {
+ token = FMT_UNKNOWN;
+ break;
+ }
+
+ value = c - '0';
+
+ for (;;)
+ {
+ c = next_char (0);
+ if (!isdigit (c))
+ break;
+
+ value = 10 * value + c - '0';
+ }
+
+ unget_char ();
+
+ if (negative_flag)
+ value = -value;
+ token = FMT_SIGNED_INT;
+ break;
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ value = c - '0';
+
+ for (;;)
+ {
+ c = next_char (0);
+ if (!isdigit (c))
+ break;
+
+ value = 10 * value + c - '0';
+ }
+
+ unget_char ();
+ token = (value == 0) ? FMT_ZERO : FMT_POSINT;
+ break;
+
+ case '.':
+ token = FMT_PERIOD;
+ break;
+
+ case ',':
+ token = FMT_COMMA;
+ break;
+
+ case ':':
+ token = FMT_COLON;
+ break;
+
+ case '/':
+ token = FMT_SLASH;
+ break;
+
+ case '$':
+ token = FMT_DOLLAR;
+ break;
+
+ case 'T':
+ switch (next_char (0))
+ {
+ case 'L':
+ token = FMT_TL;
+ break;
+ case 'R':
+ token = FMT_TR;
+ break;
+ default:
+ token = FMT_T;
+ unget_char ();
+ break;
+ }
+
+ break;
+
+ case '(':
+ token = FMT_LPAREN;
+ break;
+
+ case ')':
+ token = FMT_RPAREN;
+ break;
+
+ case 'X':
+ token = FMT_X;
+ break;
+
+ case 'S':
+ switch (next_char (0))
+ {
+ case 'S':
+ token = FMT_SS;
+ break;
+ case 'P':
+ token = FMT_SP;
+ break;
+ default:
+ token = FMT_S;
+ unget_char ();
+ break;
+ }
+
+ break;
+
+ case 'B':
+ switch (next_char (0))
+ {
+ case 'N':
+ token = FMT_BN;
+ break;
+ case 'Z':
+ token = FMT_BZ;
+ break;
+ default:
+ token = FMT_B;
+ unget_char ();
+ break;
+ }
+
+ break;
+
+ case '\'':
+ case '"':
+ delim = c;
+
+ string = format_string;
+ value = 0; /* This is the length of the string */
+
+ for (;;)
+ {
+ c = next_char (1);
+ if (c == -1)
+ {
+ token = FMT_BADSTRING;
+ error = bad_string;
+ break;
+ }
+
+ if (c == delim)
+ {
+ c = next_char (1);
+
+ if (c == -1)
+ {
+ token = FMT_BADSTRING;
+ error = bad_string;
+ break;
+ }
+
+ if (c != delim)
+ {
+ unget_char ();
+ token = FMT_STRING;
+ break;
+ }
+ }
+
+ value++;
+ }
+
+ break;
+
+ case 'P':
+ token = FMT_P;
+ break;
+
+ case 'I':
+ token = FMT_I;
+ break;
+
+ case 'O':
+ token = FMT_O;
+ break;
+
+ case 'Z':
+ token = FMT_Z;
+ break;
+
+ case 'F':
+ token = FMT_F;
+ break;
+
+ case 'E':
+ switch (next_char (0))
+ {
+ case 'N':
+ token = FMT_EN;
+ break;
+ case 'S':
+ token = FMT_ES;
+ break;
+ default:
+ token = FMT_E;
+ unget_char ();
+ break;
+ }
+
+ break;
+
+ case 'G':
+ token = FMT_G;
+ break;
+
+ case 'H':
+ token = FMT_H;
+ break;
+
+ case 'L':
+ token = FMT_L;
+ break;
+
+ case 'A':
+ token = FMT_A;
+ break;
+
+ case 'D':
+ token = FMT_D;
+ break;
+
+ case -1:
+ token = FMT_END;
+ break;
+
+ default:
+ token = FMT_UNKNOWN;
+ break;
+ }
+
+ return token;
+}
+
+
+/* parse_format_list()-- Parse a format list. Assumes that a left
+ * paren has already been seen. Returns a list representing the
+ * parenthesis node which contains the rest of the list. */
+
+static fnode *
+parse_format_list (void)
+{
+ fnode *head, *tail;
+ format_token t, u, t2;
+ int repeat;
+
+ head = tail = NULL;
+
+/* Get the next format item */
+
+format_item:
+ t = format_lex ();
+ switch (t)
+ {
+ case FMT_POSINT:
+ repeat = value;
+
+ t = format_lex ();
+ switch (t)
+ {
+ case FMT_LPAREN:
+ get_fnode (&head, &tail, FMT_LPAREN);
+ tail->repeat = repeat;
+ tail->u.child = parse_format_list ();
+ if (error != NULL)
+ goto finished;
+
+ goto between_desc;
+
+ case FMT_SLASH:
+ get_fnode (&head, &tail, FMT_SLASH);
+ tail->repeat = repeat;
+ goto optional_comma;
+
+ case FMT_X:
+ get_fnode (&head, &tail, FMT_X);
+ tail->repeat = 1;
+ tail->u.k = value;
+ goto between_desc;
+
+ case FMT_P:
+ goto p_descriptor;
+
+ default:
+ goto data_desc;
+ }
+
+ case FMT_LPAREN:
+ get_fnode (&head, &tail, FMT_LPAREN);
+ tail->repeat = 1;
+ tail->u.child = parse_format_list ();
+ if (error != NULL)
+ goto finished;
+
+ goto between_desc;
+
+ case FMT_SIGNED_INT: /* Signed integer can only precede a P format. */
+ case FMT_ZERO: /* Same for zero. */
+ t = format_lex ();
+ if (t != FMT_P)
+ {
+ error = "Expected P edit descriptor in format";
+ goto finished;
+ }
+
+ p_descriptor:
+ get_fnode (&head, &tail, FMT_P);
+ tail->u.k = value;
+
+ t = format_lex ();
+ if (t == FMT_F || t == FMT_EN || t == FMT_ES || t == FMT_D
+ || t == FMT_G || t == FMT_E)
+ {
+ repeat = 1;
+ goto data_desc;
+ }
+
+ saved_token = t;
+ goto between_desc;
+
+ case FMT_P: /* P and X require a prior number */
+ error = "P descriptor requires leading scale factor";
+ goto finished;
+
+ case FMT_X:
+/*
+ EXTENSION!
+
+ If we would be pedantic in the library, we would have to reject
+ an X descriptor without an integer prefix:
+
+ error = "X descriptor requires leading space count";
+ goto finished;
+
+ However, this is an extension supported by many Fortran compilers,
+ including Cray, HP, AIX, and IRIX. Therefore, we allow it in the
+ runtime library, and make the front end reject it if the compiler
+ is in pedantic mode. The interpretation of 'X' is '1X'.
+*/
+ get_fnode (&head, &tail, FMT_X);
+ tail->repeat = 1;
+ tail->u.k = 1;
+ goto between_desc;
+
+ case FMT_STRING:
+ get_fnode (&head, &tail, FMT_STRING);
+
+ tail->u.string.p = string;
+ tail->u.string.length = value;
+ tail->repeat = 1;
+ goto between_desc;
+
+ case FMT_S:
+ case FMT_SS:
+ case FMT_SP:
+ case FMT_BN:
+ case FMT_BZ:
+ get_fnode (&head, &tail, t);
+ goto between_desc;
+
+ case FMT_COLON:
+ get_fnode (&head, &tail, FMT_COLON);
+ goto optional_comma;
+
+ case FMT_SLASH:
+ get_fnode (&head, &tail, FMT_SLASH);
+ tail->repeat = 1;
+ tail->u.r = 1;
+ goto optional_comma;
+
+ case FMT_DOLLAR:
+ get_fnode (&head, &tail, FMT_DOLLAR);
+ goto between_desc;
+
+ case FMT_T:
+ case FMT_TL:
+ case FMT_TR:
+ t2 = format_lex ();
+ if (t2 != FMT_POSINT)
+ {
+ error = posint_required;
+ goto finished;
+ }
+ get_fnode (&head, &tail, t);
+ tail->u.n = value;
+ tail->repeat = 1;
+ goto between_desc;
+
+ case FMT_I:
+ case FMT_B:
+ case FMT_O:
+ case FMT_Z:
+ case FMT_E:
+ case FMT_EN:
+ case FMT_ES:
+ case FMT_D:
+ case FMT_L:
+ case FMT_A:
+ case FMT_F:
+ case FMT_G:
+ repeat = 1;
+ goto data_desc;
+
+ case FMT_H:
+ get_fnode (&head, &tail, FMT_STRING);
+
+ if (format_string_len < 1)
+ {
+ error = bad_hollerith;
+ goto finished;
+ }
+
+ tail->u.string.p = format_string;
+ tail->u.string.length = 1;
+ tail->repeat = 1;
+
+ format_string++;
+ format_string_len--;
+
+ goto between_desc;
+
+ case FMT_END:
+ error = unexpected_end;
+ goto finished;
+
+ case FMT_BADSTRING:
+ goto finished;
+
+ case FMT_RPAREN:
+ goto finished;
+
+ default:
+ error = unexpected_element;
+ goto finished;
+ }
+
+/* In this state, t must currently be a data descriptor. Deal with
+ * things that can/must follow the descriptor */
+
+data_desc:
+ switch (t)
+ {
+ case FMT_P:
+ t = format_lex ();
+ if (t == FMT_POSINT)
+ {
+ error = "Repeat count cannot follow P descriptor";
+ goto finished;
+ }
+
+ saved_token = t;
+ get_fnode (&head, &tail, FMT_P);
+
+ goto optional_comma;
+
+ case FMT_L:
+ t = format_lex ();
+ if (t != FMT_POSINT)
+ {
+ error = posint_required;
+ goto finished;
+ }
+
+ get_fnode (&head, &tail, FMT_L);
+ tail->u.n = value;
+ tail->repeat = repeat;
+ break;
+
+ case FMT_A:
+ t = format_lex ();
+ if (t != FMT_POSINT)
+ {
+ saved_token = t;
+ value = -1; /* Width not present */
+ }
+
+ get_fnode (&head, &tail, FMT_A);
+ tail->repeat = repeat;
+ tail->u.n = value;
+ break;
+
+ case FMT_D:
+ case FMT_E:
+ case FMT_F:
+ case FMT_G:
+ case FMT_EN:
+ case FMT_ES:
+ get_fnode (&head, &tail, t);
+ tail->repeat = repeat;
+
+ u = format_lex ();
+ if (t == FMT_F || g.mode == WRITING)
+ {
+ if (u != FMT_POSINT && u != FMT_ZERO)
+ {
+ error = nonneg_required;
+ goto finished;
+ }
+ }
+ else
+ {
+ if (u != FMT_POSINT)
+ {
+ error = posint_required;
+ goto finished;
+ }
+ }
+
+ tail->u.real.w = value;
+ t2 = t;
+ t = format_lex ();
+ if (t != FMT_PERIOD)
+ {
+ error = period_required;
+ goto finished;
+ }
+
+ t = format_lex ();
+ if (t != FMT_ZERO && t != FMT_POSINT)
+ {
+ error = nonneg_required;
+ goto finished;
+ }
+
+ tail->u.real.d = value;
+
+ if (t == FMT_D || t == FMT_F)
+ break;
+
+ tail->u.real.e = -1;
+
+/* Look for optional exponent */
+
+ t = format_lex ();
+ if (t != FMT_E)
+ saved_token = t;
+ else
+ {
+ t = format_lex ();
+ if (t != FMT_POSINT)
+ {
+ error = "Positive exponent width required in format";
+ goto finished;
+ }
+
+ tail->u.real.e = value;
+ }
+
+ break;
+
+ case FMT_H:
+ if (repeat > format_string_len)
+ {
+ error = bad_hollerith;
+ goto finished;
+ }
+
+ get_fnode (&head, &tail, FMT_STRING);
+
+ tail->u.string.p = format_string;
+ tail->u.string.length = repeat;
+ tail->repeat = 1;
+
+ format_string += value;
+ format_string_len -= repeat;
+
+ break;
+
+ case FMT_I:
+ case FMT_B:
+ case FMT_O:
+ case FMT_Z:
+ get_fnode (&head, &tail, t);
+ tail->repeat = repeat;
+
+ t = format_lex ();
+
+ if (g.mode == READING)
+ {
+ if (t != FMT_POSINT)
+ {
+ error = posint_required;
+ goto finished;
+ }
+ }
+ else
+ {
+ if (t != FMT_ZERO && t != FMT_POSINT)
+ {
+ error = nonneg_required;
+ goto finished;
+ }
+ }
+
+ tail->u.integer.w = value;
+ tail->u.integer.m = -1;
+
+ t = format_lex ();
+ if (t != FMT_PERIOD)
+ {
+ saved_token = t;
+ }
+ else
+ {
+ t = format_lex ();
+ if (t != FMT_ZERO && t != FMT_POSINT)
+ {
+ error = nonneg_required;
+ goto finished;
+ }
+
+ tail->u.integer.m = value;
+ }
+
+ if (tail->u.integer.w != 0 && tail->u.integer.m > tail->u.integer.w)
+ {
+ error = "Minimum digits exceeds field width";
+ goto finished;
+ }
+
+ break;
+
+ default:
+ error = unexpected_element;
+ goto finished;
+ }
+
+/* Between a descriptor and what comes next */
+between_desc:
+ t = format_lex ();
+ switch (t)
+ {
+ case FMT_COMMA:
+ goto format_item;
+
+ case FMT_RPAREN:
+ goto finished;
+
+ case FMT_SLASH:
+ get_fnode (&head, &tail, FMT_SLASH);
+ tail->repeat = 1;
+
+ /* Fall Through */
+
+ case FMT_COLON:
+ goto optional_comma;
+
+ case FMT_END:
+ error = unexpected_end;
+ goto finished;
+
+ default:
+ error = "Missing comma in format";
+ goto finished;
+ }
+
+/* Optional comma is a weird between state where we've just finished
+ * reading a colon, slash or P descriptor. */
+
+optional_comma:
+ t = format_lex ();
+ switch (t)
+ {
+ case FMT_COMMA:
+ break;
+
+ case FMT_RPAREN:
+ goto finished;
+
+ default: /* Assume that we have another format item */
+ saved_token = t;
+ break;
+ }
+
+ goto format_item;
+
+finished:
+ return head;
+}
+
+
+/* format_error()-- Generate an error message for a format statement.
+ * If the node that gives the location of the error is NULL, the error
+ * is assumed to happen at parse time, and the current location of the
+ * parser is shown.
+ *
+ * After freeing any dynamically allocated fnodes, generate a message
+ * showing where the problem is. We take extra care to print only the
+ * relevant part of the format if it is longer than a standard 80
+ * column display. */
+
+void
+format_error (fnode * f, const char *message)
+{
+ int width, i, j, offset;
+ char *p, buffer[300];
+
+ if (f != NULL)
+ format_string = f->source;
+
+ free_fnodes ();
+
+ st_sprintf (buffer, "%s\n", message);
+
+ j = format_string - ioparm.format;
+
+ offset = (j > 60) ? j - 40 : 0;
+
+ j -= offset;
+ width = ioparm.format_len - offset;
+
+ if (width > 80)
+ width = 80;
+
+ /* Show the format */
+
+ p = strchr (buffer, '\0');
+
+ memcpy (p, ioparm.format + offset, width);
+
+ p += width;
+ *p++ = '\n';
+
+ /* Show where the problem is */
+
+ for (i = 1; i < j; i++)
+ *p++ = ' ';
+
+ *p++ = '^';
+ *p = '\0';
+
+ generate_error (ERROR_FORMAT, buffer);
+}
+
+
+/* parse_format()-- Parse a format string. */
+
+void
+parse_format (void)
+{
+
+ format_string = ioparm.format;
+ format_string_len = ioparm.format_len;
+
+ saved_token = FMT_NONE;
+ error = NULL;
+
+/* Initialize variables used during traversal of the tree */
+
+ reversion_ok = 0;
+ g.reversion_flag = 0;
+ saved_format = NULL;
+
+/* Allocate the first format node as the root of the tree */
+
+ avail = array;
+
+ avail->format = FMT_LPAREN;
+ avail->repeat = 1;
+ avail++;
+
+ if (format_lex () == FMT_LPAREN)
+ array[0].u.child = parse_format_list ();
+ else
+ error = "Missing initial left parenthesis in format";
+
+ if (error)
+ format_error (NULL, error);
+}
+
+
+/* revert()-- Do reversion of the format. Control reverts to the left
+ * parenthesis that matches the rightmost right parenthesis. From our
+ * tree structure, we are looking for the rightmost parenthesis node
+ * at the second level, the first level always being a single
+ * parenthesis node. If this node doesn't exit, we use the top
+ * level. */
+
+static void
+revert (void)
+{
+ fnode *f, *r;
+
+ g.reversion_flag = 1;
+
+ r = NULL;
+
+ for (f = array[0].u.child; f; f = f->next)
+ if (f->format == FMT_LPAREN)
+ r = f;
+
+ /* If r is NULL because no node was found, the whole tree will be used */
+
+ array[0].current = r;
+ array[0].count = 0;
+}
+
+
+/* next_format0()-- Get the next format node without worrying about
+ * reversion. Returns NULL when we hit the end of the list.
+ * Parenthesis nodes are incremented after the list has been
+ * exhausted, other nodes are incremented before they are returned. */
+
+static fnode *
+next_format0 (fnode * f)
+{
+ fnode *r;
+
+ if (f == NULL)
+ return NULL;
+
+ if (f->format != FMT_LPAREN)
+ {
+ f->count++;
+ if (f->count <= f->repeat)
+ return f;
+
+ f->count = 0;
+ return NULL;
+ }
+
+ /* Deal with a parenthesis node */
+
+ for (; f->count < f->repeat; f->count++)
+ {
+ if (f->current == NULL)
+ f->current = f->u.child;
+
+ for (; f->current != NULL; f->current = f->current->next)
+ {
+ r = next_format0 (f->current);
+ if (r != NULL)
+ return r;
+ }
+ }
+
+ f->count = 0;
+ return NULL;
+}
+
+
+/* next_format()-- Return the next format node. If the format list
+ * ends up being exhausted, we do reversion. Reversion is only
+ * allowed if the we've seen a data descriptor since the
+ * initialization or the last reversion. We return NULL if the there
+ * are no more data descriptors to return (which is an error
+ * condition). */
+
+fnode *
+next_format (void)
+{
+ format_token t;
+ fnode *f;
+
+ if (saved_format != NULL)
+ { /* Deal with a pushed-back format node */
+ f = saved_format;
+ saved_format = NULL;
+ goto done;
+ }
+
+ f = next_format0 (&array[0]);
+ if (f == NULL)
+ {
+ if (!reversion_ok)
+ {
+ return NULL;
+ }
+
+ reversion_ok = 0;
+ revert ();
+
+ f = next_format0 (&array[0]);
+ if (f == NULL)
+ {
+ format_error (NULL, reversion_error);
+ return NULL;
+ }
+
+ /* Push the first reverted token and return a colon node in case
+ * there are no more data items. */
+
+ saved_format = f;
+ return &colon_node;
+ }
+
+ /* If this is a data edit descriptor, then reversion has become OK. */
+
+done:
+ t = f->format;
+
+ if (!reversion_ok &&
+ (t == FMT_I || t == FMT_B || t == FMT_O || t == FMT_Z || t == FMT_F ||
+ t == FMT_E || t == FMT_EN || t == FMT_ES || t == FMT_G || t == FMT_L ||
+ t == FMT_A || t == FMT_D))
+ reversion_ok = 1;
+ return f;
+}
+
+
+/* unget_format()-- Push the given format back so that it will be
+ * returned on the next call to next_format() without affecting
+ * counts. This is necessary when we've encountered a data
+ * descriptor, but don't know what the data item is yet. The format
+ * node is pushed back, and we return control to the main program,
+ * which calls the library back with the data item (or not). */
+
+void
+unget_format (fnode * f)
+{
+
+ saved_format = f;
+}
+
+
+
+
+#if 0
+
+static void dump_format1 (fnode * f);
+
+/* dump_format0()-- Dump a single format node */
+
+void
+dump_format0 (fnode * f)
+{
+ char *p;
+ int i;
+
+ switch (f->format)
+ {
+ case FMT_COLON:
+ st_printf (" :");
+ break;
+ case FMT_SLASH:
+ st_printf (" %d/", f->u.r);
+ break;
+ case FMT_DOLLAR:
+ st_printf (" $");
+ break;
+ case FMT_T:
+ st_printf (" T%d", f->u.n);
+ break;
+ case FMT_TR:
+ st_printf (" TR%d", f->u.n);
+ break;
+ case FMT_TL:
+ st_printf (" TL%d", f->u.n);
+ break;
+ case FMT_X:
+ st_printf (" %dX", f->u.n);
+ break;
+ case FMT_S:
+ st_printf (" S");
+ break;
+ case FMT_SS:
+ st_printf (" SS");
+ break;
+ case FMT_SP:
+ st_printf (" SP");
+ break;
+
+ case FMT_LPAREN:
+ if (f->repeat == 1)
+ st_printf (" (");
+ else
+ st_printf (" %d(", f->repeat);
+
+ dump_format1 (f->u.child);
+ st_printf (" )");
+ break;
+
+ case FMT_STRING:
+ st_printf (" '");
+ p = f->u.string.p;
+ for (i = f->u.string.length; i > 0; i--)
+ st_printf ("%c", *p++);
+
+ st_printf ("'");
+ break;
+
+ case FMT_P:
+ st_printf (" %dP", f->u.k);
+ break;
+ case FMT_I:
+ st_printf (" %dI%d.%d", f->repeat, f->u.integer.w, f->u.integer.m);
+ break;
+
+ case FMT_B:
+ st_printf (" %dB%d.%d", f->repeat, f->u.integer.w, f->u.integer.m);
+ break;
+
+ case FMT_O:
+ st_printf (" %dO%d.%d", f->repeat, f->u.integer.w, f->u.integer.m);
+ break;
+
+ case FMT_Z:
+ st_printf (" %dZ%d.%d", f->repeat, f->u.integer.w, f->u.integer.m);
+ break;
+
+ case FMT_BN:
+ st_printf (" BN");
+ break;
+ case FMT_BZ:
+ st_printf (" BZ");
+ break;
+ case FMT_D:
+ st_printf (" %dD%d.%d", f->repeat, f->u.real.w, f->u.real.d);
+ break;
+
+ case FMT_EN:
+ st_printf (" %dEN%d.%dE%d", f->repeat, f->u.real.w, f->u.real.d,
+ f->u.real.e);
+ break;
+
+ case FMT_ES:
+ st_printf (" %dES%d.%dE%d", f->repeat, f->u.real.w, f->u.real.d,
+ f->u.real.e);
+ break;
+
+ case FMT_F:
+ st_printf (" %dF%d.%d", f->repeat, f->u.real.w, f->u.real.d);
+ break;
+
+ case FMT_E:
+ st_printf (" %dE%d.%dE%d", f->repeat, f->u.real.w, f->u.real.d,
+ f->u.real.e);
+ break;
+
+ case FMT_G:
+ st_printf (" %dG%d.%dE%d", f->repeat, f->u.real.w, f->u.real.d,
+ f->u.real.e);
+ break;
+
+ case FMT_L:
+ st_printf (" %dL%d", f->repeat, f->u.w);
+ break;
+ case FMT_A:
+ st_printf (" %dA%d", f->repeat, f->u.w);
+ break;
+
+ default:
+ st_printf (" ???");
+ break;
+ }
+}
+
+
+/* dump_format1()-- Dump a string of format nodes */
+
+static void
+dump_format1 (fnode * f)
+{
+
+ for (; f; f = f->next)
+ dump_format1 (f);
+}
+
+/* dump_format()-- Dump the whole format node tree */
+
+void
+dump_format (void)
+{
+
+ st_printf ("format = ");
+ dump_format0 (&array[0]);
+ st_printf ("\n");
+}
+
+
+void
+next_test (void)
+{
+ fnode *f;
+ int i;
+
+ for (i = 0; i < 20; i++)
+ {
+ f = next_format ();
+ if (f == NULL)
+ {
+ st_printf ("No format!\n");
+ break;
+ }
+
+ dump_format1 (f);
+ st_printf ("\n");
+ }
+}
+
+#endif
diff --git a/libgfortran/io/inquire.c b/libgfortran/io/inquire.c
new file mode 100644
index 0000000..88e805e
--- /dev/null
+++ b/libgfortran/io/inquire.c
@@ -0,0 +1,371 @@
+/* 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. */
+
+
+/* Implement the non-IOLENGTH variant of the INQUIRY statement */
+
+#include "config.h"
+#include "libgfortran.h"
+#include "io.h"
+
+
+static char undefined[] = "UNDEFINED";
+
+
+/* inquire_via_unit()-- Inquiry via unit number. The unit might not exist. */
+
+static void
+inquire_via_unit (unit_t * u)
+{
+ const char *p;
+
+ if (ioparm.exist != NULL)
+ *ioparm.exist = (u != NULL);
+
+ if (ioparm.opened != NULL)
+ *ioparm.opened = (u != NULL);
+
+ if (ioparm.number != NULL)
+ *ioparm.number = (u != NULL) ? u->unit_number : -1;
+
+ if (ioparm.named != NULL)
+ *ioparm.named = (u != NULL && u->flags.status != STATUS_SCRATCH);
+
+ if (ioparm.name != NULL && u != NULL && u->flags.status != STATUS_SCRATCH)
+ fstrcpy (ioparm.name, ioparm.name_len, u->file, u->file_len);
+
+ if (ioparm.access != NULL)
+ {
+ if (u == NULL)
+ p = undefined;
+ else
+ switch (u->flags.access)
+ {
+ case ACCESS_SEQUENTIAL:
+ p = "SEQUENTIAL";
+ break;
+ case ACCESS_DIRECT:
+ p = "DIRECT";
+ break;
+ default:
+ internal_error ("inquire_via_unit(): Bad access");
+ }
+
+ cf_strcpy (ioparm.access, ioparm.access_len, p);
+ }
+
+ if (ioparm.sequential != NULL)
+ {
+ p = (u == NULL) ? inquire_sequential (NULL, 0) :
+ inquire_sequential (u->file, u->file_len);
+
+ cf_strcpy (ioparm.sequential, ioparm.sequential_len, p);
+ }
+
+ if (ioparm.direct != NULL)
+ {
+ p = (u == NULL) ? inquire_direct (NULL, 0) :
+ inquire_direct (u->file, u->file_len);
+
+ cf_strcpy (ioparm.direct, ioparm.direct_len, p);
+ }
+
+ if (ioparm.form != NULL)
+ {
+ if (u == NULL)
+ p = undefined;
+ else
+ switch (u->flags.form)
+ {
+ case FORM_FORMATTED:
+ p = "FORMATTED";
+ break;
+ case FORM_UNFORMATTED:
+ p = "UNFORMATTED";
+ break;
+ default:
+ internal_error ("inquire_via_unit(): Bad form");
+ }
+
+ cf_strcpy (ioparm.form, ioparm.form_len, p);
+ }
+
+ if (ioparm.formatted != NULL)
+ {
+ p = (u == NULL) ? inquire_formatted (NULL, 0) :
+ inquire_formatted (u->file, u->file_len);
+
+ cf_strcpy (ioparm.formatted, ioparm.formatted_len, p);
+ }
+
+ if (ioparm.unformatted != NULL)
+ {
+ p = (u == NULL) ? inquire_unformatted (NULL, 0) :
+ inquire_unformatted (u->file, u->file_len);
+
+ cf_strcpy (ioparm.unformatted, ioparm.unformatted_len, p);
+ }
+
+ if (ioparm.recl_out != NULL)
+ *ioparm.recl_out = (u != NULL) ? u->recl : 0;
+
+ if (ioparm.nextrec != NULL)
+ *ioparm.nextrec = (u != NULL) ? u->last_record + 1 : 0;
+
+ if (ioparm.blank != NULL)
+ {
+ if (u == NULL)
+ p = undefined;
+ else
+ switch (u->flags.blank)
+ {
+ case BLANK_NULL:
+ p = "NULL";
+ break;
+ case BLANK_ZERO:
+ p = "ZERO";
+ break;
+ default:
+ internal_error ("inquire_via_unit(): Bad blank");
+ }
+
+ cf_strcpy (ioparm.blank, ioparm.blank_len, p);
+ }
+
+ if (ioparm.position != NULL)
+ {
+ if (u == NULL || u->flags.access == ACCESS_DIRECT)
+ p = undefined;
+ else
+ {
+ p = NULL; /* TODO: Try to decode what the standard says... */
+ }
+
+ cf_strcpy (ioparm.blank, ioparm.blank_len, p);
+ }
+
+ if (ioparm.action != NULL)
+ {
+ if (u == NULL)
+ p = undefined;
+ else
+ switch (u->flags.action)
+ {
+ case ACTION_READ:
+ p = "READ";
+ break;
+ case ACTION_WRITE:
+ p = "WRITE";
+ break;
+ case ACTION_READWRITE:
+ p = "READWRITE";
+ break;
+ default:
+ internal_error ("inquire_via_unit(): Bad action");
+ }
+
+ cf_strcpy (ioparm.action, ioparm.action_len, p);
+ }
+
+ if (ioparm.read != NULL)
+ {
+ p = (u == NULL) ? inquire_read (NULL, 0) :
+ inquire_read (u->file, u->file_len);
+
+ cf_strcpy (ioparm.read, ioparm.read_len, p);
+ }
+
+ if (ioparm.write != NULL)
+ {
+ p = (u == NULL) ? inquire_write (NULL, 0) :
+ inquire_write (u->file, u->file_len);
+
+ cf_strcpy (ioparm.write, ioparm.write_len, p);
+ }
+
+ if (ioparm.readwrite != NULL)
+ {
+ p = (u == NULL) ? inquire_readwrite (NULL, 0) :
+ inquire_readwrite (u->file, u->file_len);
+
+ cf_strcpy (ioparm.readwrite, ioparm.readwrite_len, p);
+ }
+
+ if (ioparm.delim != NULL)
+ {
+ if (u == NULL || u->flags.form != FORM_FORMATTED)
+ p = undefined;
+ else
+ switch (u->flags.delim)
+ {
+ case DELIM_NONE:
+ p = "NONE";
+ break;
+ case DELIM_QUOTE:
+ p = "QUOTE";
+ break;
+ case DELIM_APOSTROPHE:
+ p = "APOSTROPHE";
+ break;
+ default:
+ internal_error ("inquire_via_unit(): Bad delim");
+ }
+
+ cf_strcpy (ioparm.access, ioparm.access_len, p);
+ }
+
+ if (ioparm.pad != NULL)
+ {
+ if (u == NULL || u->flags.form != FORM_FORMATTED)
+ p = undefined;
+ else
+ switch (u->flags.pad)
+ {
+ case PAD_NO:
+ p = "NO";
+ break;
+ case PAD_YES:
+ p = "YES";
+ break;
+ default:
+ internal_error ("inquire_via_unit(): Bad pad");
+ }
+
+ cf_strcpy (ioparm.pad, ioparm.pad_len, p);
+ }
+}
+
+
+/* inquire_via_filename()-- Inquiry via filename. This subroutine is
+ * only used if the filename is *not* connected to a unit number. */
+
+static void
+inquire_via_filename (void)
+{
+ const char *p;
+
+ if (ioparm.exist != NULL)
+ *ioparm.exist = file_exists ();
+
+ if (ioparm.opened != NULL)
+ *ioparm.opened = 0;
+
+ if (ioparm.number != NULL)
+ *ioparm.number = -1;
+
+ if (ioparm.named != NULL)
+ *ioparm.named = 1;
+
+ if (ioparm.name != NULL)
+ fstrcpy (ioparm.name, ioparm.name_len, ioparm.file, ioparm.file_len);
+
+ if (ioparm.access != NULL)
+ cf_strcpy (ioparm.access, ioparm.access_len, undefined);
+
+ if (ioparm.sequential != NULL)
+ {
+ p = inquire_sequential (ioparm.file, ioparm.file_len);
+ cf_strcpy (ioparm.sequential, ioparm.sequential_len, p);
+ }
+
+ if (ioparm.direct != NULL)
+ {
+ p = inquire_direct (ioparm.file, ioparm.file_len);
+ cf_strcpy (ioparm.direct, ioparm.direct_len, p);
+ }
+
+ if (ioparm.form != NULL)
+ cf_strcpy (ioparm.form, ioparm.form_len, undefined);
+
+ if (ioparm.formatted != NULL)
+ {
+ p = inquire_formatted (ioparm.file, ioparm.file_len);
+ cf_strcpy (ioparm.formatted, ioparm.formatted_len, p);
+ }
+
+ if (ioparm.unformatted != NULL)
+ {
+ p = inquire_unformatted (ioparm.file, ioparm.file_len);
+ cf_strcpy (ioparm.unformatted, ioparm.unformatted_len, p);
+ }
+
+ if (ioparm.recl_out != NULL)
+ *ioparm.recl_out = 0;
+
+ if (ioparm.nextrec != NULL)
+ *ioparm.nextrec = 0;
+
+ if (ioparm.blank != NULL)
+ cf_strcpy (ioparm.blank, ioparm.blank_len, undefined);
+
+ if (ioparm.position != NULL)
+ cf_strcpy (ioparm.position, ioparm.position_len, undefined);
+
+ if (ioparm.access != NULL)
+ cf_strcpy (ioparm.access, ioparm.access_len, undefined);
+
+ if (ioparm.read != NULL)
+ {
+ p = inquire_read (ioparm.file, ioparm.file_len);
+ cf_strcpy (ioparm.read, ioparm.read_len, p);
+ }
+
+ if (ioparm.write != NULL)
+ {
+ p = inquire_write (ioparm.file, ioparm.file_len);
+ cf_strcpy (ioparm.write, ioparm.write_len, p);
+ }
+
+ if (ioparm.readwrite != NULL)
+ {
+ p = inquire_read (ioparm.file, ioparm.file_len);
+ cf_strcpy (ioparm.readwrite, ioparm.readwrite_len, p);
+ }
+
+ if (ioparm.delim != NULL)
+ cf_strcpy (ioparm.delim, ioparm.delim_len, undefined);
+
+ if (ioparm.pad != NULL)
+ cf_strcpy (ioparm.pad, ioparm.pad_len, undefined);
+
+}
+
+
+
+void
+st_inquire (void)
+{
+ unit_t *u;
+
+ library_start ();
+
+ if (ioparm.file == NULL)
+ inquire_via_unit (find_unit (ioparm.unit));
+ else
+ {
+ u = find_file ();
+ if (u == NULL)
+ inquire_via_filename ();
+ else
+ inquire_via_unit (u);
+ }
+
+ library_end ();
+}
diff --git a/libgfortran/io/io.h b/libgfortran/io/io.h
new file mode 100644
index 0000000..3b01912
--- /dev/null
+++ b/libgfortran/io/io.h
@@ -0,0 +1,653 @@
+/* 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. */
+
+#ifndef GFOR_IO_H
+#define GFOR_IO_H
+
+/* IO library include. */
+
+#include <setjmp.h>
+#include "libgfortran.h"
+#define DEFAULT_TEMPDIR "/var/tmp"
+
+/* Basic types used in data transfers. */
+
+typedef enum
+{ BT_NULL, BT_INTEGER, BT_LOGICAL, BT_CHARACTER, BT_REAL,
+ BT_COMPLEX
+}
+bt;
+
+
+typedef enum
+{ SUCCESS = 1, FAILURE }
+try;
+
+typedef struct stream
+{
+ char *(*alloc_w_at) (struct stream *, int *, offset_t);
+ char *(*alloc_r_at) (struct stream *, int *, offset_t);
+ try (*sfree) (struct stream *);
+ try (*close) (struct stream *);
+ try (*seek) (struct stream *, offset_t);
+ try (*truncate) (struct stream *);
+}
+stream;
+
+
+/* Macros for doing file I/O given a stream. */
+
+#define sfree(s) ((s)->sfree)(s)
+#define sclose(s) ((s)->close)(s)
+
+#define salloc_r(s, len) ((s)->alloc_r_at)(s, len, -1)
+#define salloc_w(s, len) ((s)->alloc_w_at)(s, len, -1)
+
+#define salloc_r_at(s, len, where) ((s)->alloc_r_at)(s, len, where)
+#define salloc_w_at(s, len, where) ((s)->alloc_w_at)(s, len, where)
+
+#define sseek(s, pos) ((s)->seek)(s, pos)
+#define struncate(s) ((s)->truncate)(s)
+
+/* Namelist represent object */
+/*
+ Namelist Records
+ &groupname object=value [,object=value].../
+ or
+ &groupname object=value [,object=value]...&groupname
+
+ Even more complex, during the execution of a program containing a
+ namelist READ statement, you can specify a question mark character(?)
+ or a question mark character preceded by an equal sign(=?) to get
+ the information of the namelist group. By '?', the name of variables
+ in the namelist will be displayed, by '=?', the name and value of
+ variables will be displayed.
+
+ All these requirements need a new data structure to record all info
+ about the namelist.
+*/
+
+typedef struct namelist_type
+{
+ char * var_name;
+ void * mem_pos;
+ int value_acquired;
+ int len;
+ bt type;
+ struct namelist_type * next;
+}
+namelist_info;
+
+/* Options for the OPEN statement. */
+
+typedef enum
+{ ACCESS_SEQUENTIAL, ACCESS_DIRECT,
+ ACCESS_UNSPECIFIED
+}
+unit_access;
+
+typedef enum
+{ ACTION_READ, ACTION_WRITE, ACTION_READWRITE,
+ ACTION_UNSPECIFIED
+}
+unit_action;
+
+typedef enum
+{ BLANK_NULL, BLANK_ZERO, BLANK_UNSPECIFIED }
+unit_blank;
+
+typedef enum
+{ DELIM_NONE, DELIM_APOSTROPHE, DELIM_QUOTE,
+ DELIM_UNSPECIFIED
+}
+unit_delim;
+
+typedef enum
+{ FORM_FORMATTED, FORM_UNFORMATTED, FORM_UNSPECIFIED }
+unit_form;
+
+typedef enum
+{ POSITION_ASIS, POSITION_REWIND, POSITION_APPEND,
+ POSITION_UNSPECIFIED
+}
+unit_position;
+
+typedef enum
+{ STATUS_UNKNOWN, STATUS_OLD, STATUS_NEW, STATUS_SCRATCH,
+ STATUS_REPLACE, STATUS_UNSPECIFIED
+}
+unit_status;
+
+typedef enum
+{ PAD_YES, PAD_NO, PAD_UNSPECIFIED }
+unit_pad;
+
+typedef enum
+{ ADVANCE_YES, ADVANCE_NO, ADVANCE_UNSPECIFIED }
+unit_advance;
+
+
+
+/* Statement parameters. These are all the things that can appear in
+ an I/O statement. Some are inputs and some are outputs, but none
+ are both. All of these values are initially zeroed and are zeroed
+ at the end of a library statement. The relevant values need to be
+ set before entry to an I/O statement. This structure needs to be
+ duplicated by the back end. */
+
+typedef struct
+{
+ int unit;
+ int err, end, eor, list_format; /* These are flags, not values. */
+
+/* Return values from library statements. These are returned only if
+ the labels are specified in the statement itself and the condition
+ occurs. In most cases, none of the labels are specified and the
+ return value does not have to be checked. Must be consistent with
+ the front end. */
+
+ enum
+ {
+ LIBRARY_OK = 0,
+ LIBRARY_ERROR,
+ LIBRARY_END,
+ LIBRARY_EOR
+ }
+ library_return;
+
+ int *iostat, *exist, *opened, *number, *named, rec, *nextrec, *size;
+
+ int recl_in;
+ int *recl_out;
+
+ char *file;
+ int file_len;
+ char *status;
+ int status_len;
+ char *access;
+ int access_len;
+ char *form;
+ int form_len;
+ char *blank;
+ int blank_len;
+ char *position;
+ int position_len;
+ char *action;
+ int action_len;
+ char *delim;
+ int delim_len;
+ char *pad;
+ int pad_len;
+ char *format;
+ int format_len;
+ char *advance;
+ int advance_len;
+ char *name;
+ int name_len;
+ char *internal_unit;
+ int internal_unit_len;
+ char *sequential;
+ int sequential_len;
+ char *direct;
+ int direct_len;
+ char *formatted;
+ int formatted_len;
+ char *unformatted;
+ int unformatted_len;
+ char *read;
+ int read_len;
+ char *write;
+ int write_len;
+ char *readwrite;
+ int readwrite_len;
+
+/* namelist related data */
+ char * namelist_name;
+ int namelist_name_len;
+ int namelist_read_mode;
+}
+st_parameter;
+
+
+
+#define ioparm prefix(ioparm)
+extern st_parameter ioparm;
+
+#define ionml prefix(ionml)
+extern namelist_info * ionml;
+
+typedef struct
+{
+ unit_access access;
+ unit_action action;
+ unit_blank blank;
+ unit_delim delim;
+ unit_form form;
+ int is_notpadded;
+ unit_position position;
+ unit_status status;
+ unit_pad pad;
+}
+unit_flags;
+
+
+/* The default value of record length is defined here. This value can
+ be overriden by the OPEN statement or by an environment variable. */
+
+#define DEFAULT_RECL 10000
+
+
+typedef struct unit_t
+{
+ int unit_number;
+
+ stream *s;
+
+ struct unit_t *left, *right; /* Treap links. */
+ int priority;
+
+ int read_bad, current_record;
+ enum
+ { NO_ENDFILE, AT_ENDFILE, AFTER_ENDFILE }
+ endfile;
+
+ unit_flags flags;
+ offset_t recl, last_record, maxrec, bytes_left;
+
+ /* recl -- Record length of the file.
+ last_record -- Last record number read or written
+ maxrec -- Maximum record number in a direct access file
+ bytes_left -- Bytes left in current record. */
+
+ int file_len;
+ char file[1]; /* Filename is allocated at the end of the structure. */
+}
+unit_t;
+
+/* Global variables. Putting these in a structure makes it easier to
+ maintain, particularly with the constraint of a prefix. */
+
+typedef struct
+{
+ int in_library; /* Nonzero if a library call is being processed. */
+ int size; /* Bytes processed by the current data-transfer statement. */
+ offset_t max_offset; /* Maximum file offset. */
+ int item_count; /* Item number in a formatted data transfer. */
+ int reversion_flag; /* Format reversion has occurred. */
+ int first_item;
+
+ unit_t *unit_root;
+ int seen_dollar;
+
+ enum {READING, WRITING} mode;
+
+ unit_blank blank_status;
+ enum {SIGN_S, SIGN_SS, SIGN_SP} sign_status;
+ int scale_factor;
+ jmp_buf eof_jump;
+}
+global_t;
+
+
+#define g prefix(g)
+extern global_t g;
+
+
+#define current_unit prefix(current_unit)
+extern unit_t *current_unit;
+
+/* Format tokens. Only about half of these can be stored in the
+ format nodes. */
+
+typedef enum
+{
+ FMT_NONE = 0, FMT_UNKNOWN, FMT_SIGNED_INT, FMT_ZERO, FMT_POSINT, FMT_PERIOD,
+ FMT_COMMA, FMT_COLON, FMT_SLASH, FMT_DOLLAR, FMT_T, FMT_TR, FMT_TL,
+ FMT_LPAREN, FMT_RPAREN, FMT_X, FMT_S, FMT_SS, FMT_SP, FMT_STRING,
+ FMT_BADSTRING, FMT_P, FMT_I, FMT_B, FMT_BN, FMT_BZ, FMT_O, FMT_Z, FMT_F,
+ FMT_E, FMT_EN, FMT_ES, FMT_G, FMT_L, FMT_A, FMT_D, FMT_H, FMT_END
+}
+format_token;
+
+
+/* Format nodes. A format string is converted into a tree of these
+ structures, which is traversed as part of a data transfer statement. */
+
+typedef struct fnode
+{
+ format_token format;
+ int repeat;
+ struct fnode *next;
+ char *source;
+
+ union
+ {
+ struct
+ {
+ int w, d, e;
+ }
+ real;
+
+ struct
+ {
+ int length;
+ char *p;
+ }
+ string;
+
+ struct
+ {
+ int w, m;
+ }
+ integer;
+
+ int w;
+ int k;
+ int r;
+ int n;
+
+ struct fnode *child;
+ }
+ u;
+
+ /* Members for traversing the tree during data transfer. */
+
+ int count;
+ struct fnode *current;
+
+}
+fnode;
+
+
+/* unix.c */
+
+#define sys_exit prefix(sys_exit)
+void sys_exit (int) __attribute__ ((noreturn));
+
+#define move_pos_offset prefix(move_pos_offset)
+int move_pos_offset (stream *, int);
+
+#define get_oserror prefix(get_oserror)
+const char *get_oserror (void);
+
+#define compare_files prefix(compare_files)
+int compare_files (stream *, stream *);
+
+#define init_error_stream prefix(init_error_stream)
+stream *init_error_stream (void);
+
+#define open_external prefix(open_external)
+stream *open_external (unit_action, unit_status);
+
+#define open_internal prefix(open_internal)
+stream *open_internal (char *, int);
+
+#define input_stream prefix(input_stream)
+stream *input_stream (void);
+
+#define output_stream prefix(output_stream)
+stream *output_stream (void);
+
+#define compare_file_filename prefix(compare_file_filename)
+int compare_file_filename (stream *, const char *, int);
+
+#define find_file prefix(find_file)
+unit_t *find_file (void);
+
+#define stream_at_bof prefix(stream_at_bof)
+int stream_at_bof (stream *);
+
+#define stream_at_eof prefix(stream_at_eof)
+int stream_at_eof (stream *);
+
+#define delete_file prefix(delete_file)
+int delete_file (unit_t *);
+
+#define file_exists prefix(file_exists)
+int file_exists (void);
+
+#define inquire_sequential prefix(inquire_sequential)
+const char *inquire_sequential (const char *, int);
+
+#define inquire_direct prefix(inquire_direct)
+const char *inquire_direct (const char *, int);
+
+#define inquire_formatted prefix(inquire_formatted)
+const char *inquire_formatted (const char *, int);
+
+#define inquire_unformatted prefix(inquire_unformatted)
+const char *inquire_unformatted (const char *, int);
+
+#define inquire_read prefix(inquire_read)
+const char *inquire_read (const char *, int);
+
+#define inquire_write prefix(inquire_write)
+const char *inquire_write (const char *, int);
+
+#define inquire_readwrite prefix(inquire_readwrite)
+const char *inquire_readwrite (const char *, int);
+
+#define file_length prefix(file_length)
+offset_t file_length (stream *);
+
+#define file_position prefix(file_position)
+offset_t file_position (stream *);
+
+#define is_seekable prefix(is_seekable)
+int is_seekable (stream *);
+
+#define empty_internal_buffer prefix(empty_internal_buffer)
+void empty_internal_buffer(stream *);
+
+
+/* unit.c */
+
+#define insert_unit prefix(insert_unix)
+void insert_unit (unit_t *);
+
+#define close_unit prefix(close_unit)
+int close_unit (unit_t *);
+
+#define is_internal_unit prefix(is_internal_unit)
+int is_internal_unit (void);
+
+#define find_unit prefix(find_unit)
+unit_t *find_unit (int);
+
+#define get_unit prefix(get_unit)
+unit_t *get_unit (int);
+
+/* open.c */
+
+#define test_endfile prefix(test_endfile)
+void test_endfile (unit_t *);
+
+#define new_unit prefix(new_unit)
+void new_unit (unit_flags *);
+
+/* format.c */
+
+#define parse_format prefix(parse_format)
+void parse_format (void);
+
+#define next_format prefix(next_format)
+fnode *next_format (void);
+
+#define unget_format prefix(unget_format)
+void unget_format (fnode *);
+
+#define format_error prefix(format_error)
+void format_error (fnode *, const char *);
+
+#define free_fnodes prefix(free_fnodes)
+void free_fnodes (void);
+
+/* transfer.c */
+
+#define SCRATCH_SIZE 300
+
+#define scratch prefix(scratch)
+extern char scratch[];
+
+#define type_name prefix(type_name)
+const char *type_name (bt);
+
+#define read_block prefix(read_block)
+void *read_block (int *);
+
+#define write_block prefix(write_block)
+void *write_block (int);
+
+#define transfer_integer prefix(transfer_integer)
+void transfer_integer (void *, int);
+
+#define transfer_real prefix(transfer_real)
+void transfer_real (void *, int);
+
+#define transfer_logical prefix(transfer_logical)
+void transfer_logical (void *, int);
+
+#define transfer_character prefix(transfer_character)
+void transfer_character (void *, int);
+
+#define transfer_complex prefix(transfer_complex)
+void transfer_complex (void *, int);
+
+#define next_record prefix(next_record)
+void next_record (int);
+
+#define st_set_nml_var_int prefix(st_set_nml_var_int)
+void st_set_nml_var_int (void * , char * , int , int );
+
+#define st_set_nml_var_float prefix(st_set_nml_var_float)
+void st_set_nml_var_float (void * , char * , int , int );
+
+#define st_set_nml_var_char prefix(st_set_nml_var_char)
+void st_set_nml_var_char (void * , char * , int , int );
+
+#define st_set_nml_var_complex prefix(st_set_nml_var_complex)
+void st_set_nml_var_complex (void * , char * , int , int );
+
+#define st_set_nml_var_log prefix(st_set_nml_var_log)
+void st_set_nml_var_log (void * , char * , int , int );
+
+/* read.c */
+
+#define set_integer prefix(set_integer)
+void set_integer (void *, int64_t, int);
+
+#define max_value prefix(max_value)
+uint64_t max_value (int, int);
+
+#define convert_real prefix(convert_real)
+int convert_real (void *, const char *, int);
+
+#define read_a prefix(read_a)
+void read_a (fnode *, char *, int);
+
+#define read_f prefix(read_f)
+void read_f (fnode *, char *, int);
+
+#define read_l prefix(read_l)
+void read_l (fnode *, char *, int);
+
+#define read_x prefix(read_x)
+void read_x (fnode *);
+
+#define read_radix prefix(read_radix)
+void read_radix (fnode *, char *, int, int);
+
+#define read_decimal prefix(read_decimal)
+void read_decimal (fnode *, char *, int);
+
+/* list_read.c */
+
+#define list_formatted_read prefix(list_formatted_read)
+void list_formatted_read (bt, void *, int);
+
+#define finish_list_read prefix(finish_list_read)
+void finish_list_read (void);
+
+#define init_at_eol prefix(init_at_eol)
+void init_at_eol();
+
+#define namelist_read prefix(namelist_read)
+void namelist_read();
+
+#define namelist_write prefix(namelist_write)
+void namelist_write();
+
+/* write.c */
+
+#define write_a prefix(write_a)
+void write_a (fnode *, const char *, int);
+
+#define write_b prefix(write_b)
+void write_b (fnode *, const char *, int);
+
+#define write_d prefix(write_d)
+void write_d (fnode *, const char *, int);
+
+#define write_e prefix(write_e)
+void write_e (fnode *, const char *, int);
+
+#define write_en prefix(write_en)
+void write_en (fnode *, const char *, int);
+
+#define write_es prefix(write_es)
+void write_es (fnode *, const char *, int);
+
+#define write_f prefix(write_f)
+void write_f (fnode *, const char *, int);
+
+#define write_i prefix(write_i)
+void write_i (fnode *, const char *, int);
+
+#define write_l prefix(write_l)
+void write_l (fnode *, char *, int);
+
+#define write_o prefix(write_o)
+void write_o (fnode *, const char *, int);
+
+#define write_x prefix(write_x)
+void write_x (fnode *);
+
+#define write_z prefix(write_z)
+void write_z (fnode *, const char *, int);
+
+#define list_formatted_write prefix(list_formatted_write)
+void list_formatted_write (bt, void *, int);
+
+
+#define st_open prefix(st_open)
+#define st_close prefix(st_close)
+#define st_inquire prefix(st_inquire)
+#define st_rewind prefix(st_rewind)
+#define st_read prefix(st_read)
+#define st_read_done prefix(st_read_done)
+#define st_write prefix(st_write)
+#define st_write_done prefix(st_write_done)
+#define st_backspace prefix(st_backspace)
+#define st_endfile prefix(st_endfile)
+
+
+void __MAIN (void);
+
+#endif
diff --git a/libgfortran/io/list_read.c b/libgfortran/io/list_read.c
new file mode 100644
index 0000000..2b62d31
--- /dev/null
+++ b/libgfortran/io/list_read.c
@@ -0,0 +1,1531 @@
+/* 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. */
+
+
+#include "config.h"
+#include <string.h>
+#include <ctype.h>
+#include "libgfortran.h"
+#include "io.h"
+
+
+/* List directed input. Several parsing subroutines are practically
+ * reimplemented from formatted input, the reason being that there are
+ * all kinds of small differences between formatted and list directed
+ * parsing. */
+
+
+/* Subroutines for reading characters from the input. Because a
+ * repeat count is ambiguous with an integer, we have to read the
+ * whole digit string before seeing if there is a '*' which signals
+ * the repeat count. Since we can have a lot of potential leading
+ * zeros, we have to be able to back up by arbitrary amount. Because
+ * the input might not be seekable, we have to buffer the data
+ * ourselves. Data is buffered in scratch[] until it becomes too
+ * large, after which we start allocating memory on the heap. */
+
+static int repeat_count, saved_length, saved_used, input_complete, at_eol;
+static int comma_flag, namelist_mode;
+
+static char last_char, *saved_string;
+static bt saved_type;
+
+
+
+/* Storage area for values except for strings. Must be large enough
+ * to hold a complex value (two reals) of the largest kind */
+
+static char value[20];
+
+#define CASE_DIGITS case '0': case '1': case '2': case '3': case '4': \
+ case '5': case '6': case '7': case '8': case '9'
+
+#define CASE_SEPARATORS case ' ': case ',': case '/': case '\n': case '\t'
+
+/* This macro assumes that we're operating on a variable */
+
+#define is_separator(c) (c == '/' || c == ',' || c == '\n' || c == ' ' \
+ || c == '\t')
+
+/* Maximum repeat count. Less than ten times the maximum signed int32. */
+
+#define MAX_REPEAT 200000000
+
+
+/* push_char()-- Save a character to a string buffer, enlarging it as
+ * necessary. */
+
+static void
+push_char (char c)
+{
+ char *new;
+
+ if (saved_string == NULL)
+ {
+ saved_string = scratch;
+ memset (saved_string,0,SCRATCH_SIZE);
+ saved_length = SCRATCH_SIZE;
+ saved_used = 0;
+ }
+
+ if (saved_used >= saved_length)
+ {
+ saved_length = 2 * saved_length;
+ new = get_mem (2 * saved_length);
+
+ memset (new,0,2 * saved_length);
+
+ memcpy (new, saved_string, saved_used);
+ if (saved_string != scratch)
+ free_mem (saved_string);
+
+ saved_string = new;
+ }
+
+ saved_string[saved_used++] = c;
+}
+
+
+/* free_saved()-- Free the input buffer if necessary. */
+
+static void
+free_saved (void)
+{
+
+ if (saved_string == NULL)
+ return;
+
+ if (saved_string != scratch)
+ free_mem (saved_string);
+
+ saved_string = NULL;
+}
+
+
+static char
+next_char (void)
+{
+ int length;
+ char c, *p;
+
+ if (last_char != '\0')
+ {
+ at_eol = 0;
+ c = last_char;
+ last_char = '\0';
+ goto done;
+ }
+
+ length = 1;
+
+ p = salloc_r (current_unit->s, &length);
+ if (p == NULL)
+ {
+ generate_error (ERROR_OS, NULL);
+ return '\0';
+ }
+
+ if (length == 0)
+ longjmp (g.eof_jump, 1);
+ c = *p;
+
+done:
+ at_eol = (c == '\n');
+ return c;
+}
+
+
+/* unget_char()-- Push a character back onto the input */
+
+static void
+unget_char (char c)
+{
+
+ last_char = c;
+}
+
+
+/* eat_spaces()-- Skip over spaces in the input. Returns the nonspace
+ * character that terminated the eating and also places it back on the
+ * input. */
+
+static char
+eat_spaces (void)
+{
+ char c;
+
+ do
+ {
+ c = next_char ();
+ }
+ while (c == ' ' || c == '\t');
+
+ unget_char (c);
+ return c;
+}
+
+
+/* eat_separator()-- Skip over a separator. Technically, we don't
+ * always eat the whole separator. This is because if we've processed
+ * the last input item, then a separator is unnecessary. Plus the
+ * fact that operating systems usually deliver console input on a line
+ * basis.
+ *
+ * The upshot is that if we see a newline as part of reading a
+ * separator, we stop reading. If there are more input items, we
+ * continue reading the separator with finish_separator() which takes
+ * care of the fact that we may or may not have seen a comma as part
+ * of the separator. */
+
+static void
+eat_separator (void)
+{
+ char c;
+
+ eat_spaces ();
+ comma_flag = 0;
+
+ c = next_char ();
+ switch (c)
+ {
+ case ',':
+ comma_flag = 1;
+ eat_spaces ();
+ break;
+
+ case '/':
+ input_complete = 1;
+ next_record (0);
+ break;
+
+ case '\n':
+ break;
+
+ case '!':
+ if (namelist_mode)
+ { /* Eat a namelist comment */
+ do
+ c = next_char ();
+ while (c != '\n');
+
+ break;
+ }
+
+ /* Fall Through */
+
+ default:
+ unget_char (c);
+ break;
+ }
+}
+
+
+/* finish_separator()-- Finish processing a separator that was
+ * interrupted by a newline. If we're here, then another data item is
+ * present, so we finish what we started on the previous line. */
+
+static void
+finish_separator (void)
+{
+ char c;
+
+restart:
+ eat_spaces ();
+
+ c = next_char ();
+ switch (c)
+ {
+ case ',':
+ if (comma_flag)
+ unget_char (c);
+ else
+ {
+ c = eat_spaces ();
+ if (c == '\n')
+ goto restart;
+ }
+
+ break;
+
+ case '/':
+ input_complete = 1;
+ next_record (0);
+ break;
+
+ case '\n':
+ goto restart;
+
+ case '!':
+ if (namelist_mode)
+ {
+ do
+ c = next_char ();
+ while (c != '\n');
+
+ goto restart;
+ }
+
+ default:
+ unget_char (c);
+ break;
+ }
+}
+
+
+/* convert_integer()-- Convert an unsigned string to an integer. The
+ * length value is -1 if we are working on a repeat count. Returns
+ * nonzero if we have a range problem. As a side effect, frees the
+ * saved_string. */
+
+static int
+convert_integer (int length, int negative)
+{
+ char c, *buffer, message[100];
+ int m;
+ int64_t v, max, max10;
+
+ buffer = saved_string;
+ v = 0;
+
+ max = (length == -1) ? MAX_REPEAT : max_value (length, 1);
+ max10 = max / 10;
+
+ for (;;)
+ {
+ c = *buffer++;
+ if (c == '\0')
+ break;
+ c -= '0';
+
+ if (v > max10)
+ goto overflow;
+ v = 10 * v;
+
+ if (v > max - c)
+ goto overflow;
+ v += c;
+ }
+
+ m = 0;
+
+ if (length != -1)
+ {
+ if (negative)
+ v = -v;
+ set_integer (value, v, length);
+ }
+ else
+ {
+ repeat_count = v;
+
+ if (repeat_count == 0)
+ {
+ st_sprintf (message, "Zero repeat count in item %d of list input",
+ g.item_count);
+
+ generate_error (ERROR_READ_VALUE, message);
+ m = 1;
+ }
+ }
+
+ free_saved ();
+ return m;
+
+overflow:
+ if (length == -1)
+ st_sprintf (message, "Repeat count overflow in item %d of list input",
+ g.item_count);
+ else
+ st_sprintf (message, "Integer overflow while reading item %d",
+ g.item_count);
+
+ free_saved ();
+ generate_error (ERROR_READ_VALUE, message);
+
+ return 1;
+}
+
+
+/* parse_repeat()-- Parse a repeat count for logical and complex
+ * values which cannot begin with a digit. Returns nonzero if we are
+ * done, zero if we should continue on. */
+
+static int
+parse_repeat (void)
+{
+ char c, message[100];
+ int repeat;
+
+ c = next_char ();
+ switch (c)
+ {
+ CASE_DIGITS:
+ repeat = c - '0';
+ break;
+
+ CASE_SEPARATORS:
+ unget_char (c);
+ eat_separator ();
+ return 1;
+
+ default:
+ unget_char (c);
+ return 0;
+ }
+
+ for (;;)
+ {
+ c = next_char ();
+ switch (c)
+ {
+ CASE_DIGITS:
+ repeat = 10 * repeat + c - '0';
+
+ if (repeat > MAX_REPEAT)
+ {
+ st_sprintf (message,
+ "Repeat count overflow in item %d of list input",
+ g.item_count);
+
+ generate_error (ERROR_READ_VALUE, message);
+ return 1;
+ }
+
+ break;
+
+ case '*':
+ if (repeat == 0)
+ {
+ st_sprintf (message,
+ "Zero repeat count in item %d of list input",
+ g.item_count);
+
+ generate_error (ERROR_READ_VALUE, message);
+ return 1;
+ }
+
+ goto done;
+
+ default:
+ goto bad_repeat;
+ }
+ }
+
+done:
+ repeat_count = repeat;
+ return 0;
+
+bad_repeat:
+ st_sprintf (message, "Bad repeat count in item %d of list input",
+ g.item_count);
+
+ generate_error (ERROR_READ_VALUE, message);
+ return 1;
+}
+
+
+/* read_logical()-- Read a logical character on the input */
+
+static void
+read_logical (int length)
+{
+ char c, message[100];
+ int v;
+
+ if (parse_repeat ())
+ return;
+
+ c = next_char ();
+ switch (c)
+ {
+ case 't':
+ case 'T':
+ v = 1;
+ break;
+ case 'f':
+ case 'F':
+ v = 0;
+ break;
+
+ case '.':
+ c = next_char ();
+ switch (c)
+ {
+ case 't':
+ case 'T':
+ v = 1;
+ break;
+ case 'f':
+ case 'F':
+ v = 0;
+ break;
+ default:
+ goto bad_logical;
+ }
+
+ break;
+
+ CASE_SEPARATORS:
+ unget_char (c);
+ eat_separator ();
+ return; /* Null value */
+
+ default:
+ goto bad_logical;
+ }
+
+ saved_type = BT_LOGICAL;
+ saved_length = length;
+
+ /* Eat trailing garbage */
+
+ do
+ {
+ c = next_char ();
+ }
+ while (!is_separator (c));
+
+ unget_char (c);
+ eat_separator ();
+ free_saved ();
+ set_integer ((int *) value, v, length);
+
+ return;
+
+bad_logical:
+ st_sprintf (message, "Bad logical value while reading item %d",
+ g.item_count);
+
+ generate_error (ERROR_READ_VALUE, message);
+}
+
+
+/* read_integer()-- Reading integers is tricky because we can actually
+ * be reading a repeat count. We have to store the characters in a
+ * buffer because we could be reading an integer that is larger than the
+ * default int used for repeat counts. */
+
+static void
+read_integer (int length)
+{
+ char c, message[100];
+ int negative;
+
+ negative = 0;
+
+ c = next_char ();
+ switch (c)
+ {
+ case '-':
+ negative = 1;
+ /* Fall through */
+
+ case '+':
+ c = next_char ();
+ goto get_integer;
+
+ CASE_SEPARATORS: /* Single null */
+ unget_char (c);
+ eat_separator ();
+ return;
+
+ CASE_DIGITS:
+ push_char (c);
+ break;
+
+ default:
+ goto bad_integer;
+ }
+
+ /* Take care of what may be a repeat count */
+
+ for (;;)
+ {
+ c = next_char ();
+ switch (c)
+ {
+ CASE_DIGITS:
+ push_char (c);
+ break;
+
+ case '*':
+ push_char ('\0');
+ goto repeat;
+
+ CASE_SEPARATORS: /* Not a repeat count */
+ goto done;
+
+ default:
+ goto bad_integer;
+ }
+ }
+
+repeat:
+ if (convert_integer (-1, 0))
+ return;
+
+/* Get the real integer */
+
+ c = next_char ();
+ switch (c)
+ {
+ CASE_DIGITS:
+ break;
+
+ CASE_SEPARATORS:
+ unget_char (c);
+ eat_separator ();
+ return;
+
+ case '-':
+ negative = 1;
+ /* Fall through */
+
+ case '+':
+ c = next_char ();
+ break;
+ }
+
+get_integer:
+ if (!isdigit (c))
+ goto bad_integer;
+ push_char (c);
+
+ for (;;)
+ {
+ c = next_char ();
+ switch (c)
+ {
+ CASE_DIGITS:
+ push_char (c);
+ break;
+
+ CASE_SEPARATORS:
+ goto done;
+
+ default:
+ goto bad_integer;
+ }
+ }
+
+bad_integer:
+ free_saved ();
+
+ st_sprintf (message, "Bad integer for item %d in list input", g.item_count);
+ generate_error (ERROR_READ_VALUE, message);
+
+ return;
+
+done:
+ unget_char (c);
+ eat_separator ();
+
+ push_char ('\0');
+ if (convert_integer (length, negative))
+ {
+ free_saved ();
+ return;
+ }
+
+ free_saved ();
+ saved_type = BT_INTEGER;
+}
+
+
+/* read_character()-- Read a character variable */
+
+static void
+read_character (int length)
+{
+ char c, quote, message[100];
+
+ quote = ' '; /* Space means no quote character */
+
+ c = next_char ();
+ switch (c)
+ {
+ CASE_DIGITS:
+ push_char (c);
+ break;
+
+ CASE_SEPARATORS:
+ unget_char (c); /* NULL value */
+ eat_separator ();
+ return;
+
+ case '"':
+ case '\'':
+ quote = c;
+ goto get_string;
+
+ default:
+ push_char (c);
+ goto get_string;
+ }
+
+/* Deal with a possible repeat count */
+
+ for (;;)
+ {
+ c = next_char ();
+ switch (c)
+ {
+ CASE_DIGITS:
+ push_char (c);
+ break;
+
+ CASE_SEPARATORS:
+ unget_char (c);
+ goto done; /* String was only digits! */
+
+ case '*':
+ push_char ('\0');
+ goto got_repeat;
+
+ default:
+ push_char (c);
+ goto get_string; /* Not a repeat count after all */
+ }
+ }
+
+got_repeat:
+ if (convert_integer (-1, 0))
+ return;
+
+ /* Now get the real string */
+
+ c = next_char ();
+ switch (c)
+ {
+ CASE_SEPARATORS:
+ unget_char (c); /* repeated NULL values */
+ eat_separator ();
+ return;
+
+ case '"':
+ case '\'':
+ quote = c;
+ break;
+
+ default:
+ push_char (c);
+ break;
+ }
+
+get_string:
+ for (;;)
+ {
+ c = next_char ();
+ switch (c)
+ {
+ case '"':
+ case '\'':
+ if (c != quote)
+ {
+ push_char (c);
+ break;
+ }
+
+ /* See if we have a doubled quote character or the end of the string */
+
+ c = next_char ();
+ if (c == quote)
+ {
+ push_char (quote);
+ break;
+ }
+
+ unget_char (c);
+ goto done;
+
+ CASE_SEPARATORS:
+ if (quote == ' ')
+ {
+ unget_char (c);
+ goto done;
+ }
+
+ if (c != '\n')
+ push_char (c);
+ break;
+
+ default:
+ push_char (c);
+ break;
+ }
+ }
+
+/* At this point, we have to have a separator, or else the string is invalid */
+
+done:
+ c = next_char ();
+ if (is_separator (c))
+ {
+ unget_char (c);
+ eat_separator ();
+ saved_type = BT_CHARACTER;
+ }
+ else
+ {
+ free_saved ();
+ st_sprintf (message, "Invalid string input in item %d", g.item_count);
+ generate_error (ERROR_READ_VALUE, message);
+ }
+}
+
+
+/* parse_real()-- Parse a component of a complex constant or a real
+ * number that we are sure is already there. This is a straight real
+ * number parser. */
+
+static int
+parse_real (void *buffer, int length)
+{
+ char c, message[100];
+ int m, seen_dp;
+
+ c = next_char ();
+ if (c == '-' || c == '+')
+ {
+ push_char (c);
+ c = next_char ();
+ }
+
+ if (!isdigit (c) && c != '.')
+ goto bad;
+
+ push_char (c);
+
+ seen_dp = (c == '.') ? 1 : 0;
+
+ for (;;)
+ {
+ c = next_char ();
+ switch (c)
+ {
+ CASE_DIGITS:
+ push_char (c);
+ break;
+
+ case '.':
+ if (seen_dp)
+ goto bad;
+
+ seen_dp = 1;
+ push_char (c);
+ break;
+
+ case 'e':
+ case 'E':
+ case 'd':
+ case 'D':
+ push_char ('e');
+ goto exp1;
+
+ case '-':
+ case '+':
+ push_char ('e');
+ push_char (c);
+ c = next_char ();
+ goto exp2;
+
+ CASE_SEPARATORS:
+ unget_char (c);
+ goto done;
+
+ default:
+ goto done;
+ }
+ }
+
+exp1:
+ c = next_char ();
+ if (c != '-' && c != '+')
+ push_char ('+');
+ else
+ {
+ push_char (c);
+ c = next_char ();
+ }
+
+exp2:
+ if (!isdigit (c))
+ goto bad;
+ push_char (c);
+
+ for (;;)
+ {
+ c = next_char ();
+ switch (c)
+ {
+ CASE_DIGITS:
+ push_char (c);
+ break;
+
+ CASE_SEPARATORS:
+ unget_char (c);
+ goto done;
+
+ default:
+ goto done;
+ }
+ }
+
+done:
+ unget_char (c);
+ push_char ('\0');
+
+ m = convert_real (buffer, saved_string, length);
+ free_saved ();
+
+ return m;
+
+bad:
+ free_saved ();
+ st_sprintf (message, "Bad floating point number for item %d", g.item_count);
+ generate_error (ERROR_READ_VALUE, message);
+
+ return 1;
+}
+
+
+/* read_complex()-- Reading a complex number is straightforward
+ * because we can tell what it is right away. */
+
+static void
+read_complex (int length)
+{
+ char message[100];
+ char c;
+
+ if (parse_repeat ())
+ return;
+
+ c = next_char ();
+ switch (c)
+ {
+ case '(':
+ break;
+
+ CASE_SEPARATORS:
+ unget_char (c);
+ eat_separator ();
+ return;
+
+ default:
+ goto bad_complex;
+ }
+
+ eat_spaces ();
+ if (parse_real (value, length))
+ return;
+
+ eat_spaces ();
+ if (next_char () != ',')
+ goto bad_complex;
+
+ eat_spaces ();
+ if (parse_real (value + length, length))
+ return;
+
+ eat_spaces ();
+ if (next_char () != ')')
+ goto bad_complex;
+
+ c = next_char ();
+ if (!is_separator (c))
+ goto bad_complex;
+
+ unget_char (c);
+ eat_separator ();
+
+ free_saved ();
+ saved_type = BT_COMPLEX;
+ return;
+
+bad_complex:
+ st_sprintf (message, "Bad complex value in item %d of list input",
+ g.item_count);
+
+ generate_error (ERROR_READ_VALUE, message);
+}
+
+
+/* read_real()-- Parse a real number with a possible repeat count. */
+
+static void
+read_real (int length)
+{
+ char c, message[100];
+ int seen_dp;
+
+ seen_dp = 0;
+
+ c = next_char ();
+ switch (c)
+ {
+ CASE_DIGITS:
+ push_char (c);
+ break;
+
+ case '.':
+ push_char (c);
+ seen_dp = 1;
+ break;
+
+ case '+':
+ case '-':
+ goto got_sign;
+
+ CASE_SEPARATORS:
+ unget_char (c); /* Single null */
+ eat_separator ();
+ return;
+
+ default:
+ goto bad_real;
+ }
+
+ /* Get the digit string that might be a repeat count */
+
+ for (;;)
+ {
+ c = next_char ();
+ switch (c)
+ {
+ CASE_DIGITS:
+ push_char (c);
+ break;
+
+ case '.':
+ if (seen_dp)
+ goto bad_real;
+
+ seen_dp = 1;
+ push_char (c);
+ goto real_loop;
+
+ case 'E':
+ case 'e':
+ case 'D':
+ case 'd':
+ goto exp1;
+
+ case '+':
+ case '-':
+ push_char ('e');
+ push_char (c);
+ c = next_char ();
+ goto exp2;
+
+ case '*':
+ push_char ('\0');
+ goto got_repeat;
+
+ CASE_SEPARATORS:
+ if (c != '\n')
+ unget_char (c); /* Real number that is just a digit-string */
+ goto done;
+
+ default:
+ goto bad_real;
+ }
+ }
+
+got_repeat:
+ if (convert_integer (-1, 0))
+ return;
+
+/* Now get the number itself */
+
+ c = next_char ();
+ if (is_separator (c))
+ { /* Repeated null value */
+ unget_char (c);
+ eat_separator ();
+ return;
+ }
+
+ if (c != '-' && c != '+')
+ push_char ('+');
+ else
+ {
+ got_sign:
+ push_char (c);
+ c = next_char ();
+ }
+
+ if (!isdigit (c) && c != '.')
+ goto bad_real;
+
+ if (c == '.')
+ {
+ if (seen_dp)
+ goto bad_real;
+ else
+ seen_dp = 1;
+ }
+
+ push_char (c);
+
+real_loop:
+ for (;;)
+ {
+ c = next_char ();
+ switch (c)
+ {
+ CASE_DIGITS:
+ push_char (c);
+ break;
+
+ CASE_SEPARATORS:
+ goto done;
+
+ case '.':
+ if (seen_dp)
+ goto bad_real;
+
+ seen_dp = 1;
+ push_char (c);
+ break;
+
+ case 'E':
+ case 'e':
+ case 'D':
+ case 'd':
+ goto exp1;
+
+ case '+':
+ case '-':
+ push_char ('e');
+ push_char (c);
+ c = next_char ();
+ goto exp2;
+
+ default:
+ goto bad_real;
+ }
+ }
+
+exp1:
+ push_char ('e');
+
+ c = next_char ();
+ if (c != '+' && c != '-')
+ push_char ('+');
+ else
+ {
+ push_char (c);
+ c = next_char ();
+ }
+
+exp2:
+ if (!isdigit (c))
+ goto bad_real;
+ push_char (c);
+
+ for (;;)
+ {
+ c = next_char ();
+
+ switch (c)
+ {
+ CASE_DIGITS:
+ push_char (c);
+ break;
+
+ CASE_SEPARATORS:
+ unget_char (c);
+ eat_separator ();
+ goto done;
+
+ default:
+ goto bad_real;
+ }
+ }
+
+done:
+ push_char ('\0');
+ if (convert_real (value, saved_string, length))
+ return;
+
+ free_saved ();
+ saved_type = BT_REAL;
+ return;
+
+bad_real:
+ st_sprintf (message, "Bad real number in item %d of list input",
+ g.item_count);
+
+ generate_error (ERROR_READ_VALUE, message);
+}
+
+
+/* check_type()-- Check the current type against the saved type to
+ * make sure they are compatible. Returns nonzero if incompatible. */
+
+static int
+check_type (bt type, int len)
+{
+ char message[100];
+
+ if (saved_type != BT_NULL && saved_type != type)
+ {
+ st_sprintf (message, "Read type %s where %s was expected for item %d",
+ type_name (saved_type), type_name (type), g.item_count);
+
+ generate_error (ERROR_READ_VALUE, message);
+ return 1;
+ }
+
+ if (saved_type == BT_NULL || saved_type == BT_CHARACTER)
+ return 0;
+
+ if (saved_length != len)
+ {
+ st_sprintf (message,
+ "Read kind %d %s where kind %d is required for item %d",
+ saved_length, type_name (saved_type), len, g.item_count);
+ generate_error (ERROR_READ_VALUE, message);
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/* list_formatted_read()-- Top level data transfer subroutine for list
+ * reads. Because we have to deal with repeat counts, the data item
+ * is always saved after reading, usually in the value[] array. If a
+ * repeat count is greater than one, we copy the data item multiple
+ * times. */
+
+void
+list_formatted_read (bt type, void *p, int len)
+{
+ char c;
+ int m;
+
+ namelist_mode = 0;
+
+ if (setjmp (g.eof_jump))
+ {
+ generate_error (ERROR_END, NULL);
+ return;
+ }
+
+ if (g.first_item)
+ {
+ g.first_item = 0;
+ input_complete = 0;
+ repeat_count = 1;
+ at_eol = 0;
+
+ c = eat_spaces ();
+ if (is_separator (c))
+ { /* Found a null value */
+ eat_separator ();
+ repeat_count = 0;
+ if (at_eol)
+ finish_separator ();
+ else
+ return;
+ }
+
+ }
+ else
+ {
+ if (input_complete)
+ return;
+
+ if (repeat_count > 0)
+ {
+ if (check_type (type, len))
+ return;
+ goto set_value;
+ }
+
+ if (at_eol)
+ finish_separator ();
+ else
+ eat_spaces ();
+
+ saved_type = BT_NULL;
+ repeat_count = 1;
+ }
+
+
+ switch (type)
+ {
+ case BT_INTEGER:
+ read_integer (len);
+ break;
+ case BT_LOGICAL:
+ read_logical (len);
+ break;
+ case BT_CHARACTER:
+ read_character (len);
+ break;
+ case BT_REAL:
+ read_real (len);
+ break;
+ case BT_COMPLEX:
+ read_complex (len);
+ break;
+ default:
+ internal_error ("Bad type for list read");
+ }
+
+ if (saved_type != BT_CHARACTER && saved_type != BT_NULL)
+ saved_length = len;
+
+ if (ioparm.library_return != LIBRARY_OK)
+ return;
+
+set_value:
+ switch (saved_type)
+ {
+ case BT_COMPLEX:
+ len = 2 * len;
+ /* Fall through */
+
+ case BT_INTEGER:
+ case BT_REAL:
+ case BT_LOGICAL:
+ memcpy (p, value, len);
+ break;
+
+ case BT_CHARACTER:
+ m = (len < saved_used) ? len : saved_used;
+ memcpy (p, saved_string, m);
+
+ if (m < len)
+ memset (((char *) p) + m, ' ', len - m);
+ break;
+
+ case BT_NULL:
+ break;
+ }
+
+ if (--repeat_count <= 0)
+ free_saved ();
+}
+
+void
+init_at_eol()
+{
+ at_eol = 0;
+}
+
+/* finish_list_read()-- Finish a list read */
+
+void
+finish_list_read (void)
+{
+ char c;
+
+ free_saved ();
+
+ if (at_eol)
+ {
+ at_eol = 0;
+ return;
+ }
+
+
+ do
+ {
+ c = next_char ();
+ }
+ while (c != '\n');
+}
+
+static namelist_info *
+find_nml_node (char * var_name)
+{
+ namelist_info * t = ionml;
+ while (t != NULL)
+ {
+ if (strcmp (var_name,t->var_name) == 0)
+ {
+ t->value_acquired = 1;
+ return t;
+ }
+ t = t->next;
+ }
+ return NULL;
+}
+
+static void
+match_namelist_name (char *name, int len)
+{
+ int name_len;
+ char c;
+ char * namelist_name = name;
+
+ name_len = 0;
+ /* Match the name of the namelist */
+
+ if (tolower (next_char ()) != tolower (namelist_name[name_len++]))
+ {
+ wrong_name:
+ generate_error (ERROR_READ_VALUE, "Wrong namelist name found");
+ return;
+ }
+
+ while (name_len < len)
+ {
+ c = next_char ();
+ if (tolower (c) != tolower (namelist_name[name_len++]))
+ goto wrong_name;
+ }
+}
+
+
+/********************************************************************
+ Namelist reads
+********************************************************************/
+
+/* namelist_read()-- Process a namelist read. This subroutine
+ * initializes things, positions to the first element and */
+
+void
+namelist_read (void)
+{
+ char c;
+ int name_matched, next_name ;
+ namelist_info * nl;
+ int len, m;
+ void * p;
+
+ namelist_mode = 1;
+
+ if (setjmp (g.eof_jump))
+ {
+ generate_error (ERROR_END, NULL);
+ return;
+ }
+
+restart:
+ c = next_char ();
+ switch (c)
+ {
+ case ' ':
+ goto restart;
+ case '!':
+ do
+ c = next_char ();
+ while (c != '\n');
+
+ goto restart;
+
+ case '&':
+ break;
+
+ default:
+ generate_error (ERROR_READ_VALUE, "Invalid character in namelist");
+ return;
+ }
+
+ /* Match the name of the namelist */
+ match_namelist_name(ioparm.namelist_name, ioparm.namelist_name_len);
+
+ /* Ready to read namelist elements */
+ for (;;)
+ {
+ c = next_char ();
+ switch (c)
+ {
+ case '&':
+ match_namelist_name("end",3);
+ return;
+ case '\\':
+ return;
+ case ' ':
+ case '\n':
+ case '\t':
+ break;
+ case ',':
+ next_name = 1;
+ break;
+
+ case '=':
+ name_matched = 1;
+ nl = find_nml_node (saved_string);
+ if (nl == NULL)
+ internal_error ("Can not found a valid namelist var!");
+ free_saved();
+
+ len = nl->len;
+ p = nl->mem_pos;
+ switch (nl->type)
+ {
+ case BT_INTEGER:
+ read_integer (len);
+ break;
+ case BT_LOGICAL:
+ read_logical (len);
+ break;
+ case BT_CHARACTER:
+ read_character (len);
+ break;
+ case BT_REAL:
+ read_real (len);
+ break;
+ case BT_COMPLEX:
+ read_complex (len);
+ break;
+ default:
+ internal_error ("Bad type for namelist read");
+ }
+
+ switch (saved_type)
+ {
+ case BT_COMPLEX:
+ len = 2 * len;
+ /* Fall through */
+
+ case BT_INTEGER:
+ case BT_REAL:
+ case BT_LOGICAL:
+ memcpy (p, value, len);
+ break;
+
+ case BT_CHARACTER:
+ m = (len < saved_used) ? len : saved_used;
+ memcpy (p, saved_string, m);
+
+ if (m < len)
+ memset (((char *) p) + m, ' ', len - m);
+ break;
+
+ case BT_NULL:
+ break;
+ }
+
+ break;
+
+ default :
+ push_char(c);
+ break;
+ }
+ }
+}
+
diff --git a/libgfortran/io/lock.c b/libgfortran/io/lock.c
new file mode 100644
index 0000000..1d3f069
--- /dev/null
+++ b/libgfortran/io/lock.c
@@ -0,0 +1,84 @@
+/* Thread/recursion locking
+ Copyright 2002 Free Software Foundation, Inc.
+ Contributed by Paul Brook <paul@nowt.org> and Andy Vaught
+
+This file is part of the GNU Fortran 95 runtime library (libgfor).
+
+Libgfor is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+Libgfor 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with libgfor; see the file COPYING.LIB. If not,
+write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
+
+#include "config.h"
+#include <string.h>
+#include "libgfortran.h"
+#include "io.h"
+
+st_parameter ioparm;
+namelist_info * ionml;
+global_t g;
+
+
+/* library_start()-- Called with a library call is entered. */
+
+void
+library_start (void)
+{
+
+ if (g.in_library)
+ internal_error ("Recursive library calls not allowed");
+
+/* The in_library flag indicates whether we're currently processing a
+ * library call. Some calls leave immediately, but READ and WRITE
+ * processing return control to the caller but are still considered to
+ * stay within the library. */
+
+ g.in_library = 1;
+
+ if (ioparm.iostat != NULL && ioparm.library_return == LIBRARY_OK)
+ *ioparm.iostat = ERROR_OK;
+
+ ioparm.library_return = LIBRARY_OK;
+}
+
+
+/* library_end()-- Called when a library call is complete in order to
+ * clean up for the next call. */
+
+void
+library_end (void)
+{
+ int t;
+ namelist_info * t1, *t2;
+
+ g.in_library = 0;
+ filename = NULL;
+ line = 0;
+
+ t = ioparm.library_return;
+ if (ionml != NULL)
+ {
+ t1 = ionml;
+ while (t1 != NULL)
+ {
+ t2 = t1;
+ t1 = t1->next;
+ free_mem (t2);
+ }
+ }
+
+ ionml = NULL;
+ memset (&ioparm, '\0', sizeof (ioparm));
+ ioparm.library_return = t;
+}
+
diff --git a/libgfortran/io/open.c b/libgfortran/io/open.c
new file mode 100644
index 0000000..e6fa50d
--- /dev/null
+++ b/libgfortran/io/open.c
@@ -0,0 +1,528 @@
+
+/* 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. */
+
+#include "config.h"
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include "libgfortran.h"
+#include "io.h"
+
+
+static st_option access_opt[] = {
+ {"sequential", ACCESS_SEQUENTIAL},
+ {"direct", ACCESS_DIRECT},
+ {NULL}
+}, action_opt[] =
+{
+ {
+ "read", ACTION_READ}
+ ,
+ {
+ "write", ACTION_WRITE}
+ ,
+ {
+ "readwrite", ACTION_READWRITE}
+ ,
+ {
+ NULL}
+}
+
+, blank_opt[] =
+{
+ {
+ "null", BLANK_NULL}
+ ,
+ {
+ "zero", BLANK_ZERO}
+ ,
+ {
+ NULL}
+}
+
+, delim_opt[] =
+{
+ {
+ "none", DELIM_NONE}
+ ,
+ {
+ "apostrophe", DELIM_APOSTROPHE}
+ ,
+ {
+ "quote", DELIM_QUOTE}
+ ,
+ {
+ NULL}
+}
+
+, form_opt[] =
+{
+ {
+ "formatted", FORM_FORMATTED}
+ ,
+ {
+ "unformatted", FORM_UNFORMATTED}
+ ,
+ {
+ NULL}
+}
+
+, position_opt[] =
+{
+ {
+ "asis", POSITION_ASIS}
+ ,
+ {
+ "rewind", POSITION_REWIND}
+ ,
+ {
+ "append", POSITION_APPEND}
+ ,
+ {
+ NULL}
+}
+
+, status_opt[] =
+{
+ {
+ "unknown", STATUS_UNKNOWN}
+ ,
+ {
+ "old", STATUS_OLD}
+ ,
+ {
+ "new", STATUS_NEW}
+ ,
+ {
+ "replace", STATUS_REPLACE}
+ ,
+ {
+ "scratch", STATUS_SCRATCH}
+ ,
+ {
+ NULL}
+}
+
+, pad_opt[] =
+{
+ {
+ "yes", PAD_YES}
+ ,
+ {
+ "no", PAD_NO}
+ ,
+ {
+ NULL}
+};
+
+
+/* test_endfile()-- Given a unit, test to see if the file is
+ * positioned at the terminal point, and if so, change state from
+ * NO_ENDFILE flag to AT_ENDFILE. This prevents us from changing the
+ * state from AFTER_ENDFILE to AT_ENDFILE. */
+
+void
+test_endfile (unit_t * u)
+{
+
+ if (u->endfile == NO_ENDFILE && file_length (u->s) == file_position (u->s))
+ u->endfile = AT_ENDFILE;
+}
+
+
+/* edit_modes()-- Change the modes of a file, those that are allowed
+ * to be changed. */
+
+static void
+edit_modes (unit_t * u, unit_flags * flags)
+{
+
+ /* Complain about attempts to change the unchangeable */
+
+ if (flags->status != STATUS_UNSPECIFIED &&
+ u->flags.status != flags->position)
+ generate_error (ERROR_BAD_OPTION,
+ "Cannot change STATUS parameter in OPEN statement");
+
+ if (flags->access != ACCESS_UNSPECIFIED && u->flags.access != flags->access)
+ generate_error (ERROR_BAD_OPTION,
+ "Cannot change ACCESS parameter in OPEN statement");
+
+ if (flags->form != FORM_UNSPECIFIED && u->flags.form != flags->form)
+ generate_error (ERROR_BAD_OPTION,
+ "Cannot change FORM parameter in OPEN statement");
+
+ if (ioparm.recl_in != 0 && ioparm.recl_in != u->recl)
+ generate_error (ERROR_BAD_OPTION,
+ "Cannot change RECL parameter in OPEN statement");
+
+ if (flags->action != ACTION_UNSPECIFIED && u->flags.access != flags->access)
+ generate_error (ERROR_BAD_OPTION,
+ "Cannot change ACTION parameter in OPEN statement");
+
+ /* Status must be OLD if present */
+
+ if (flags->status != STATUS_UNSPECIFIED && flags->status != STATUS_OLD)
+ generate_error (ERROR_BAD_OPTION,
+ "OPEN statement must have a STATUS of OLD");
+
+ if (u->flags.form == FORM_UNFORMATTED)
+ {
+ if (flags->delim != DELIM_UNSPECIFIED)
+ generate_error (ERROR_OPTION_CONFLICT,
+ "DELIM parameter conflicts with UNFORMATTED form in "
+ "OPEN statement");
+
+ if (flags->blank != BLANK_UNSPECIFIED)
+ generate_error (ERROR_OPTION_CONFLICT,
+ "BLANK parameter conflicts with UNFORMATTED form in "
+ "OPEN statement");
+
+ if (flags->pad != PAD_UNSPECIFIED)
+ generate_error (ERROR_OPTION_CONFLICT,
+ "PAD paramter conflicts with UNFORMATTED form in "
+ "OPEN statement");
+ }
+
+ if (ioparm.library_return == LIBRARY_OK)
+ { /* Change the changeable */
+ if (flags->blank != BLANK_UNSPECIFIED)
+ u->flags.blank = flags->blank;
+ if (flags->delim != DELIM_UNSPECIFIED)
+ u->flags.delim = flags->delim;
+ if (flags->pad != PAD_UNSPECIFIED)
+ u->flags.pad = flags->pad;
+ }
+
+ /* Reposition the file if necessary. */
+
+ switch (flags->position)
+ {
+ case POSITION_UNSPECIFIED:
+ case POSITION_ASIS:
+ break;
+
+ case POSITION_REWIND:
+ if (sseek (u->s, 0) == FAILURE)
+ goto seek_error;
+
+ u->current_record = 0;
+ u->last_record = 0;
+
+ test_endfile (u); /* We might be at the end */
+ break;
+
+ case POSITION_APPEND:
+ if (sseek (u->s, file_length (u->s)) == FAILURE)
+ goto seek_error;
+
+ u->current_record = 0;
+ u->endfile = AT_ENDFILE; /* We are at the end */
+ break;
+
+ seek_error:
+ generate_error (ERROR_OS, NULL);
+ break;
+ }
+}
+
+
+/* new_unit()-- Open an unused unit */
+
+void
+new_unit (unit_flags * flags)
+{
+ unit_t *u;
+ stream *s;
+ char tmpname[5 /* fort. */ + 10 /* digits of unit number */ + 1 /* 0 */];
+
+ /* Change unspecifieds to defaults */
+
+ if (flags->access == ACCESS_UNSPECIFIED)
+ flags->access = ACCESS_SEQUENTIAL;
+
+ if (flags->action == ACTION_UNSPECIFIED)
+ flags->action = ACTION_READWRITE; /* Processor dependent */
+
+ if (flags->form == FORM_UNSPECIFIED)
+ flags->form = (flags->access == ACCESS_SEQUENTIAL)
+ ? FORM_FORMATTED : FORM_UNFORMATTED;
+
+
+ if (flags->delim == DELIM_UNSPECIFIED)
+ flags->delim = DELIM_NONE;
+ else
+ {
+ if (flags->form == FORM_UNFORMATTED)
+ {
+ generate_error (ERROR_OPTION_CONFLICT,
+ "DELIM parameter conflicts with UNFORMATTED form in "
+ "OPEN statement");
+ goto cleanup;
+ }
+ }
+
+ if (flags->blank == BLANK_UNSPECIFIED)
+ flags->blank = BLANK_NULL;
+ else
+ {
+ if (flags->form == FORM_UNFORMATTED)
+ {
+ generate_error (ERROR_OPTION_CONFLICT,
+ "BLANK parameter conflicts with UNFORMATTED form in "
+ "OPEN statement");
+ goto cleanup;
+ }
+ }
+
+ if (flags->pad == PAD_UNSPECIFIED)
+ flags->pad = PAD_YES;
+ else
+ {
+ if (flags->form == FORM_UNFORMATTED)
+ {
+ generate_error (ERROR_OPTION_CONFLICT,
+ "PAD paramter conflicts with UNFORMATTED form in "
+ "OPEN statement");
+ goto cleanup;
+ }
+ }
+
+ if (flags->position != POSITION_ASIS && flags->access == ACCESS_DIRECT)
+ {
+ generate_error (ERROR_OPTION_CONFLICT,
+ "ACCESS parameter conflicts with SEQUENTIAL access in "
+ "OPEN statement");
+ goto cleanup;
+ }
+ else
+ if (flags->position == POSITION_UNSPECIFIED)
+ flags->position = POSITION_ASIS;
+
+
+ if (flags->status == STATUS_UNSPECIFIED)
+ flags->status = STATUS_UNKNOWN;
+
+ /* Checks */
+
+ if (flags->access == ACCESS_DIRECT && ioparm.recl_in == 0)
+ {
+ generate_error (ERROR_MISSING_OPTION,
+ "Missing RECL parameter in OPEN statement");
+ goto cleanup;
+ }
+
+ if (ioparm.recl_in != 0 && ioparm.recl_in <= 0)
+ {
+ generate_error (ERROR_BAD_OPTION,
+ "RECL parameter is non-positive in OPEN statement");
+ goto cleanup;
+ }
+
+ switch (flags->status)
+ {
+ case STATUS_SCRATCH:
+ if (ioparm.file == NULL)
+ break;
+
+ generate_error (ERROR_BAD_OPTION,
+ "FILE parameter must not be present in OPEN statement");
+ return;
+
+ case STATUS_OLD:
+ case STATUS_NEW:
+ case STATUS_REPLACE:
+ case STATUS_UNKNOWN:
+ if (ioparm.file != NULL)
+ break;
+
+ ioparm.file = tmpname;
+ ioparm.file_len = sprintf(ioparm.file, "fort.%d", ioparm.unit);
+ break;
+
+ default:
+ internal_error ("new_unit(): Bad status");
+ }
+
+ /* Make sure the file isn't already open someplace else */
+
+ if (find_file () != NULL)
+ {
+ generate_error (ERROR_ALREADY_OPEN, NULL);
+ goto cleanup;
+ }
+
+ /* Open file */
+
+ s = open_external (flags->action, flags->status);
+ if (s == NULL)
+ {
+ generate_error (ERROR_OS, NULL);
+ goto cleanup;
+ }
+
+ if (flags->status == STATUS_NEW || flags->status == STATUS_REPLACE)
+ flags->status = STATUS_OLD;
+
+ /* Create the unit structure */
+
+ u = get_mem (sizeof (unit_t) + ioparm.file_len);
+
+ u->unit_number = ioparm.unit;
+ u->s = s;
+ u->flags = *flags;
+
+ /* Unspecified recl ends up with a processor dependent value */
+
+ u->recl = (ioparm.recl_in != 0) ? ioparm.recl_in : DEFAULT_RECL;
+ u->last_record = 0;
+ u->current_record = 0;
+
+ /* If the file is direct access, calculate the maximum record number
+ * via a division now instead of letting the multiplication overflow
+ * later. */
+
+ if (flags->access == ACCESS_DIRECT)
+ u->maxrec = g.max_offset / u->recl;
+
+ memmove (u->file, ioparm.file, ioparm.file_len);
+ u->file_len = ioparm.file_len;
+
+ insert_unit (u);
+
+ /* The file is now connected. Errors after this point leave the
+ * file connected. Curiously, the standard requires that the
+ * position specifier be ignored for new files so a newly connected
+ * file starts out that the initial point. We still need to figure
+ * out if the file is at the end or not. */
+
+ test_endfile (u);
+
+cleanup:
+
+ /* Free memory associated with a temporary filename */
+
+ if (flags->status == STATUS_SCRATCH)
+ free_mem (ioparm.file);
+}
+
+
+/* already_open()-- Open a unit which is already open. This involves
+ * changing the modes or closing what is there now and opening the new
+ * file. */
+
+static void
+already_open (unit_t * u, unit_flags * flags)
+{
+
+ if (ioparm.file == NULL)
+ {
+ edit_modes (u, flags);
+ return;
+ }
+
+ /* If the file is connected to something else, close it and open a
+ * new unit */
+
+ if (!compare_file_filename (u->s, ioparm.file, ioparm.file_len))
+ {
+ if (close_unit (u))
+ {
+ generate_error (ERROR_OS, "Error closing file in OPEN statement");
+ return;
+ }
+
+ new_unit (flags);
+ return;
+ }
+
+ edit_modes (u, flags);
+}
+
+
+/*************/
+/* open file */
+
+void
+st_open (void)
+{
+ unit_flags flags;
+ unit_t *u = NULL;
+
+ library_start ();
+
+ /* Decode options */
+
+ flags.access = (ioparm.access == NULL) ? ACCESS_UNSPECIFIED :
+ find_option (ioparm.access, ioparm.access_len, access_opt,
+ "Bad ACCESS parameter in OPEN statement");
+
+ flags.action = (ioparm.action == NULL) ? ACTION_UNSPECIFIED :
+ find_option (ioparm.action, ioparm.action_len, action_opt,
+ "Bad ACTION parameter in OPEN statement");
+
+ flags.blank = (ioparm.blank == NULL) ? BLANK_UNSPECIFIED :
+ find_option (ioparm.blank, ioparm.blank_len, blank_opt,
+ "Bad BLANK parameter in OPEN statement");
+
+ flags.delim = (ioparm.delim == NULL) ? DELIM_UNSPECIFIED :
+ find_option (ioparm.delim, ioparm.delim_len, delim_opt,
+ "Bad DELIM parameter in OPEN statement");
+
+ flags.pad = (ioparm.pad == NULL) ? PAD_UNSPECIFIED :
+ find_option (ioparm.pad, ioparm.pad_len, pad_opt,
+ "Bad PAD parameter in OPEN statement");
+
+ flags.form = (ioparm.form == NULL) ? FORM_UNSPECIFIED :
+ find_option (ioparm.form, ioparm.form_len, form_opt,
+ "Bad FORM parameter in OPEN statement");
+
+ flags.position = (ioparm.position == NULL) ? POSITION_UNSPECIFIED :
+ find_option (ioparm.position, ioparm.position_len, position_opt,
+ "Bad POSITION parameter in OPEN statement");
+
+ flags.status = (ioparm.status == NULL) ? STATUS_UNSPECIFIED :
+ find_option (ioparm.status, ioparm.status_len, status_opt,
+ "Bad STATUS parameter in OPEN statement");
+
+ if (ioparm.unit < 0)
+ generate_error (ERROR_BAD_OPTION, "Bad unit number in OPEN statement");
+
+ if (flags.position != POSITION_UNSPECIFIED &&
+ u->flags.access == ACCESS_DIRECT)
+ generate_error (ERROR_BAD_OPTION,
+ "Cannot use POSITION with direct access files");
+
+ if (flags.position == POSITION_UNSPECIFIED)
+ flags.position = POSITION_ASIS;
+
+ if (ioparm.library_return != LIBRARY_OK)
+ return;
+
+ u = find_unit (ioparm.unit);
+
+ if (u == NULL)
+ new_unit (&flags);
+ else
+ already_open (u, &flags);
+
+ library_end ();
+}
diff --git a/libgfortran/io/read.c b/libgfortran/io/read.c
new file mode 100644
index 0000000..3ce9f1d
--- /dev/null
+++ b/libgfortran/io/read.c
@@ -0,0 +1,793 @@
+/* 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. */
+
+
+#include "config.h"
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include "libgfortran.h"
+#include "io.h"
+
+/* read.c -- Deal with formatted reads */
+
+/* set_integer()-- All of the integer assignments come here to
+ * actually place the value into memory. */
+
+void
+set_integer (void *dest, int64_t value, int length)
+{
+
+ switch (length)
+ {
+ case 8:
+ *((int64_t *) dest) = value;
+ break;
+ case 4:
+ *((int32_t *) dest) = value;
+ break;
+ case 2:
+ *((int16_t *) dest) = value;
+ break;
+ case 1:
+ *((int8_t *) dest) = value;
+ break;
+ default:
+ internal_error ("Bad integer kind");
+ }
+}
+
+
+/* max_value()-- Given a length (kind), return the maximum signed or
+ * unsigned value */
+
+uint64_t
+max_value (int length, int signed_flag)
+{
+ uint64_t value;
+
+ switch (length)
+ {
+ case 8:
+ value = signed_flag ? 0x7fffffffffffffff : 0xffffffffffffffff;
+ break;
+ case 4:
+ value = signed_flag ? 0x7fffffff : 0xffffffff;
+ break;
+ case 2:
+ value = signed_flag ? 0x7fff : 0xffff;
+ break;
+ case 1:
+ value = signed_flag ? 0x7f : 0xff;
+ break;
+ default:
+ internal_error ("Bad integer kind");
+ }
+
+ return value;
+}
+
+
+/* convert_real()-- Convert a character representation of a floating
+ * point number to the machine number. Returns nonzero if there is a
+ * range problem during conversion. TODO: handle not-a-numbers and
+ * infinities. Handling of kind 4 is probably wrong because of double
+ * rounding. */
+
+int
+convert_real (void *dest, const char *buffer, int length)
+{
+
+ errno = 0;
+
+ switch (length)
+ {
+ case 4:
+ *((float *) dest) = (float) strtod (buffer, NULL);
+ break;
+ case 8:
+ *((double *) dest) = strtod (buffer, NULL);
+ break;
+ default:
+ internal_error ("Bad real number kind");
+ }
+
+ if (errno != 0)
+ {
+ generate_error (ERROR_READ_VALUE,
+ "Range error during floating point read");
+ return 1;
+ }
+
+ return 0;
+}
+
+static int
+convert_precision_real (void *dest, int sign,
+ char *buffer, int length, int exponent)
+{
+ int w, new_dp_pos, i, slen, k, dp;
+ char * p, c;
+ double fval;
+ float tf;
+
+ fval =0.0;
+ tf = 0.0;
+ dp = 0;
+ new_dp_pos = 0;
+
+ slen = strlen (buffer);
+ w = slen;
+ p = buffer;
+
+/* for (i = w - 1; i > 0; i --)
+ {
+ if (buffer[i] == '0' || buffer[i] == 0)
+ buffer[i] = 0;
+ else
+ break;
+ }
+*/
+ for (i = 0; i < w; i++)
+ {
+ if (buffer[i] == '.')
+ break;
+ }
+
+ new_dp_pos = i;
+ new_dp_pos += exponent;
+
+ while (w > 0)
+ {
+ c = *p;
+ switch (c)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ fval = fval * 10.0 + c - '0';
+ p++;
+ w--;
+ break;
+
+ case '.':
+ dp = 1;
+ p++;
+ w--;
+ break;
+
+ default:
+ p++;
+ w--;
+ break;
+ }
+ }
+
+ if (sign)
+ fval = - fval;
+
+ i = new_dp_pos - slen + dp;
+ k = abs(i);
+ tf = 1.0;
+
+ while (k > 0)
+ {
+ tf *= 10.0 ;
+ k -- ;
+ }
+
+ if (fval != 0.0)
+ {
+ if (i < 0)
+ {
+ fval = fval / tf;
+ }
+ else
+ {
+ fval = fval * tf;
+ }
+ }
+
+ switch (length)
+ {
+ case 4:
+ *((float *) dest) = (float)fval;
+ break;
+ case 8:
+ *((double *) dest) = fval;
+ break;
+ default:
+ internal_error ("Bad real number kind");
+ }
+
+ return 0;
+}
+
+
+/* read_l()-- Read a logical value */
+
+void
+read_l (fnode * f, char *dest, int length)
+{
+ char *p;
+ int w;
+
+ w = f->u.w;
+ p = read_block (&w);
+ if (p == NULL)
+ return;
+
+ while (*p == ' ')
+ {
+ if (--w == 0)
+ goto bad;
+ p++;
+ }
+
+ if (*p == '.')
+ {
+ if (--w == 0)
+ goto bad;
+ p++;
+ }
+
+ switch (*p)
+ {
+ case 't':
+ case 'T':
+ set_integer (dest, 1, length);
+ break;
+ case 'f':
+ case 'F':
+ set_integer (dest, 0, length);
+ break;
+ default:
+ bad:
+ generate_error (ERROR_READ_VALUE, "Bad value on logical read");
+ break;
+ }
+}
+
+
+/* read_a()-- Read a character record. This one is pretty easy. */
+
+void
+read_a (fnode * f, char *p, int length)
+{
+ char *source;
+ int w, m, n;
+
+ w = f->u.w;
+ if (w == -1) /* '(A)' edit descriptor */
+ w = length;
+
+ source = read_block (&w);
+ if (source == NULL)
+ return;
+ if (w > length)
+ source += (w - length);
+
+ m = (w > length) ? length : w;
+ memcpy (p, source, m);
+
+ n = length - w;
+ if (n > 0)
+ memset (p + m, ' ', n);
+}
+
+
+/* eat_leading_spaces()-- Given a character pointer and a width,
+ * ignore the leading spaces. */
+
+static char *
+eat_leading_spaces (int *width, char *p)
+{
+
+ for (;;)
+ {
+ if (*width == 0 || *p != ' ')
+ break;
+
+ (*width)--;
+ p++;
+ }
+
+ return p;
+}
+
+
+static char
+next_char (char **p, int *w)
+{
+ char c, *q;
+
+ if (*w == 0)
+ return '\0';
+
+ q = *p;
+ c = *q++;
+ *p = q;
+
+ (*w)--;
+
+ if (c != ' ')
+ return c;
+ if (g.blank_status == BLANK_ZERO)
+ return '0';
+
+ /* At this point, the rest of the field has to be trailing blanks */
+
+ while (*w > 0)
+ {
+ if (*q++ != ' ')
+ return '?';
+ (*w)--;
+ }
+
+ *p = q;
+ return '\0';
+}
+
+
+/* read_decimal()-- Read a decimal integer value. The values here are
+ * signed values. */
+
+void
+read_decimal (fnode * f, char *dest, int length)
+{
+ unsigned value, maxv, maxv_10;
+ int v, w, negative;
+ char c, *p;
+
+ w = f->u.w;
+ p = read_block (&w);
+ if (p == NULL)
+ return;
+
+ p = eat_leading_spaces (&w, p);
+ if (w == 0)
+ {
+ set_integer (dest, 0, length);
+ return;
+ }
+
+ maxv = max_value (length, 1);
+ maxv_10 = maxv / 10;
+
+ negative = 0;
+ value = 0;
+
+ switch (*p)
+ {
+ case '-':
+ negative = 1;
+ /* Fall through */
+
+ case '+':
+ p++;
+ if (--w == 0)
+ goto bad;
+ /* Fall through */
+
+ default:
+ break;
+ }
+
+ /* At this point we have a digit-string */
+ value = 0;
+
+ for (;;)
+ {
+ c = next_char (&p, &w);
+ if (c == '\0')
+ break;
+
+ if (c < '0' || c > '9')
+ goto bad;
+
+ if (value > maxv_10)
+ goto overflow;
+
+ c -= '0';
+ value = 10 * value;
+
+ if (value > maxv - c)
+ goto overflow;
+ value += c;
+ }
+
+ v = (signed int) value;
+ if (negative)
+ v = -v;
+
+ set_integer (dest, v, length);
+ return;
+
+bad:
+ generate_error (ERROR_READ_VALUE, "Bad value during integer read");
+ return;
+
+overflow:
+ generate_error (ERROR_READ_OVERFLOW,
+ "Value overflowed during integer read");
+ return;
+}
+
+
+/* read_radix()-- This function reads values for non-decimal radixes.
+ * The difference here is that we treat the values here as unsigned
+ * values for the purposes of overflow. If minus sign is present and
+ * the top bit is set, the value will be incorrect. */
+
+void
+read_radix (fnode * f, char *dest, int length, int radix)
+{
+ unsigned value, maxv, maxv_r;
+ int v, w, negative;
+ char c, *p;
+
+ w = f->u.w;
+ p = read_block (&w);
+ if (p == NULL)
+ return;
+
+ p = eat_leading_spaces (&w, p);
+ if (w == 0)
+ {
+ set_integer (dest, 0, length);
+ return;
+ }
+
+ maxv = max_value (length, 0);
+ maxv_r = maxv / radix;
+
+ negative = 0;
+ value = 0;
+
+ switch (*p)
+ {
+ case '-':
+ negative = 1;
+ /* Fall through */
+
+ case '+':
+ p++;
+ if (--w == 0)
+ goto bad;
+ /* Fall through */
+
+ default:
+ break;
+ }
+
+ /* At this point we have a digit-string */
+ value = 0;
+
+ for (;;)
+ {
+ c = next_char (&p, &w);
+ if (c == '\0')
+ break;
+
+ switch (radix)
+ {
+ case 2:
+ if (c < '0' || c > '1')
+ goto bad;
+ break;
+
+ case 8:
+ if (c < '0' || c > '7')
+ goto bad;
+ break;
+
+ case 16:
+ switch (c)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ break;
+
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ c = c - 'a' + '9' + 1;
+ break;
+
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ c = c - 'A' + '9' + 1;
+ break;
+
+ default:
+ goto bad;
+ }
+
+ break;
+ }
+
+ if (value > maxv_r)
+ goto overflow;
+
+ c -= '0';
+ value = radix * value;
+
+ if (maxv - c < value)
+ goto overflow;
+ value += c;
+ }
+
+ v = (signed int) value;
+ if (negative)
+ v = -v;
+
+ set_integer (dest, v, length);
+ return;
+
+bad:
+ generate_error (ERROR_READ_VALUE, "Bad value during integer read");
+ return;
+
+overflow:
+ generate_error (ERROR_READ_OVERFLOW,
+ "Value overflowed during integer read");
+ return;
+}
+
+
+/* read_f()-- Read a floating point number with F-style editing, which
+ * is what all of the other floating point descriptors behave as. The
+ * tricky part is that optional spaces are allowed after an E or D,
+ * and the implicit decimal point if a decimal point is not present in
+ * the input. */
+
+void
+read_f (fnode * f, char *dest, int length)
+{
+ int w, seen_dp, exponent;
+ int exponent_sign, val_sign;
+ char *p, *buffer, *n;
+
+ val_sign = 0;
+ seen_dp = 0;
+ w = f->u.w;
+ p = read_block (&w);
+ if (p == NULL)
+ return;
+
+ p = eat_leading_spaces (&w, p);
+ if (w == 0)
+ {
+ switch (length)
+ {
+ case 4:
+ *((float *) dest) = 0.0;
+ break;
+
+ case 8:
+ *((double *) dest) = 0.0;
+ break;
+ }
+
+ return;
+ }
+
+ if (w + 2 < SCRATCH_SIZE)
+ buffer = scratch;
+ else
+ buffer = get_mem (w + 2);
+
+ memset(buffer, 0, w + 2);
+
+ n = buffer;
+
+ /* Optional sign */
+
+ if (*p == '-' || *p == '+')
+ {
+ if (*p == '-')
+ val_sign = 1;
+ p++;
+
+ if (--w == 0)
+ goto bad_float;
+ }
+
+ exponent_sign = 1;
+
+ /* A digit (or a '.') is required at this point */
+
+ if (!isdigit (*p) && *p != '.')
+ goto bad_float;
+
+ while (w > 0)
+ {
+ switch (*p)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ *n++ = *p++;
+ w--;
+ break;
+
+ case '.':
+ if (seen_dp)
+ goto bad_float;
+ seen_dp = 1;
+
+ *n++ = *p++;
+ w--;
+ break;
+
+ case ' ':
+ if (g.blank_status == BLANK_ZERO)
+ *n++ = '0';
+ p++;
+ w--;
+ break;
+
+ case '-':
+ exponent_sign = -1;
+ /* Fall through */
+
+ case '+':
+ p++;
+ w--;
+ goto exp2;
+
+ case 'd':
+ case 'e':
+ case 'D':
+ case 'E':
+ p++;
+ w--;
+ goto exp1;
+
+ default:
+ goto bad_float;
+ }
+ }
+
+/* No exponent has been seen, so we use the current scale factor */
+
+ exponent = -g.scale_factor;
+ goto done;
+
+bad_float:
+ generate_error (ERROR_READ_VALUE, "Bad value during floating point read");
+ if (buffer != scratch)
+ free_mem (buffer);
+ return;
+
+/* At this point the start of an exponent has been found */
+
+exp1:
+ while (w > 0 && *p == ' ')
+ {
+ w--;
+ p++;
+ }
+
+ switch (*p)
+ {
+ case '-':
+ exponent_sign = -1;
+ /* Fall through */
+
+ case '+':
+ p++;
+ w--;
+ break;
+ }
+
+ if (w == 0)
+ goto bad_float;
+
+/* At this point a digit string is required. We calculate the value
+ * of the exponent in order to take account of the scale factor and
+ * the d parameter before explict conversion takes place. */
+
+exp2:
+ if (!isdigit (*p))
+ goto bad_float;
+
+ exponent = *p - '0';
+ p++;
+ w--;
+
+ while (w > 0 && isdigit (*p))
+ {
+ exponent = 10 * exponent + *p - '0';
+ if (exponent > 999999)
+ goto bad_float;
+
+ p++;
+ w--;
+ }
+
+ /* Only allow trailing blanks */
+
+ while (w > 0)
+ {
+ if (*p != ' ')
+ goto bad_float;
+ p++;
+ w--;
+ }
+
+ exponent = exponent * exponent_sign;
+
+done:
+ if (!seen_dp)
+ exponent -= f->u.real.d;
+
+ /* The number is syntactically correct and ready for conversion.
+ * The only thing that can go wrong at this point is overflow or
+ * underflow. */
+
+ convert_precision_real (dest, val_sign, buffer, length, exponent);
+
+ if (buffer != scratch)
+ free_mem (buffer);
+
+ return;
+}
+
+
+/* read_x()-- Deal with the X/TR descriptor. We just read some data
+ * and never look at it. */
+
+void
+read_x (fnode * f)
+{
+ int n;
+
+ n = f->u.n;
+ read_block (&n);
+}
diff --git a/libgfortran/io/rewind.c b/libgfortran/io/rewind.c
new file mode 100644
index 0000000..d068853
--- /dev/null
+++ b/libgfortran/io/rewind.c
@@ -0,0 +1,56 @@
+
+/* 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. */
+
+#include "config.h"
+#include "libgfortran.h"
+#include "io.h"
+
+/* rewind.c-- Implement the rewind statement */
+
+void
+st_rewind (void)
+{
+ unit_t *u;
+
+ library_start ();
+
+ u = find_unit (ioparm.unit);
+ if (u != NULL)
+ {
+ if (u->flags.access != ACCESS_SEQUENTIAL)
+ generate_error (ERROR_BAD_OPTION,
+ "Cannot REWIND a file opened for DIRECT access");
+ else
+ {
+ if (g.mode==WRITING)
+ struncate(u->s);
+ u->last_record = 0;
+ if (sseek (u->s, 0) == FAILURE)
+ generate_error (ERROR_OS, NULL);
+
+ u->endfile = NO_ENDFILE;
+ u->current_record = 0;
+ test_endfile (u);
+ }
+ }
+
+ library_end ();
+}
diff --git a/libgfortran/io/transfer.c b/libgfortran/io/transfer.c
new file mode 100644
index 0000000..b1f0ef1
--- /dev/null
+++ b/libgfortran/io/transfer.c
@@ -0,0 +1,1498 @@
+
+/* 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 <string.h>
+#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.
+ */
+
+unit_t *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);
+}
+
diff --git a/libgfortran/io/unit.c b/libgfortran/io/unit.c
new file mode 100644
index 0000000..87f9095
--- /dev/null
+++ b/libgfortran/io/unit.c
@@ -0,0 +1,380 @@
+/* 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. */
+
+#include "config.h"
+#include <stdlib.h>
+#include <string.h>
+#include "libgfortran.h"
+#include "io.h"
+
+
+/* Subroutines related to units */
+
+
+#define CACHE_SIZE 3
+static unit_t internal_unit, *unit_cache[CACHE_SIZE];
+
+
+/* This implementation is based on Stefan Nilsson's article in the
+ * July 1997 Doctor Dobb's Journal, "Treaps in Java". */
+
+/* pseudo_random()-- Simple linear congruential pseudorandom number
+ * generator. The period of this generator is 44071, which is plenty
+ * for our purposes. */
+
+static int
+pseudo_random (void)
+{
+ static int x0 = 5341;
+
+ x0 = (22611 * x0 + 10) % 44071;
+ return x0;
+}
+
+
+/* rotate_left()-- Rotate the treap left */
+
+static unit_t *
+rotate_left (unit_t * t)
+{
+ unit_t *temp;
+
+ temp = t->right;
+ t->right = t->right->left;
+ temp->left = t;
+
+ return temp;
+}
+
+
+/* rotate_right()-- Rotate the treap right */
+
+static unit_t *
+rotate_right (unit_t * t)
+{
+ unit_t *temp;
+
+ temp = t->left;
+ t->left = t->left->right;
+ temp->right = t;
+
+ return temp;
+}
+
+
+
+static int
+compare (int a, int b)
+{
+
+ if (a < b)
+ return -1;
+ if (a > b)
+ return 1;
+
+ return 0;
+}
+
+
+/* insert()-- Recursive insertion function. Returns the updated treap. */
+
+static unit_t *
+insert (unit_t * new, unit_t * t)
+{
+ int c;
+
+ if (t == NULL)
+ return new;
+
+ c = compare (new->unit_number, t->unit_number);
+
+ if (c < 0)
+ {
+ t->left = insert (new, t->left);
+ if (t->priority < t->left->priority)
+ t = rotate_right (t);
+ }
+
+ if (c > 0)
+ {
+ t->right = insert (new, t->right);
+ if (t->priority < t->right->priority)
+ t = rotate_left (t);
+ }
+
+ if (c == 0)
+ internal_error ("insert(): Duplicate key found!");
+
+ return t;
+}
+
+
+/* insert_unit()-- Given a new node, insert it into the treap. It is
+ * an error to insert a key that already exists. */
+
+void
+insert_unit (unit_t * new)
+{
+
+ new->priority = pseudo_random ();
+ g.unit_root = insert (new, g.unit_root);
+}
+
+
+static unit_t *
+delete_root (unit_t * t)
+{
+ unit_t *temp;
+
+ if (t->left == NULL)
+ return t->right;
+ if (t->right == NULL)
+ return t->left;
+
+ if (t->left->priority > t->right->priority)
+ {
+ temp = rotate_right (t);
+ temp->right = delete_root (t);
+ }
+ else
+ {
+ temp = rotate_left (t);
+ temp->left = delete_root (t);
+ }
+
+ return temp;
+}
+
+
+/* delete_treap()-- Delete an element from a tree. The 'old' value
+ * does not necessarily have to point to the element to be deleted, it
+ * must just point to a treap structure with the key to be deleted.
+ * Returns the new root node of the tree. */
+
+static unit_t *
+delete_treap (unit_t * old, unit_t * t)
+{
+ int c;
+
+ if (t == NULL)
+ return NULL;
+
+ c = compare (old->unit_number, t->unit_number);
+
+ if (c < 0)
+ t->left = delete_treap (old, t->left);
+ if (c > 0)
+ t->right = delete_treap (old, t->right);
+ if (c == 0)
+ t = delete_root (t);
+
+ return t;
+}
+
+
+/* delete_unit()-- Delete a unit from a tree */
+
+static void
+delete_unit (unit_t * old)
+{
+
+ g.unit_root = delete_treap (old, g.unit_root);
+}
+
+
+/* find_unit()-- Given an integer, return a pointer to the unit
+ * structure. Returns NULL if the unit does not exist. */
+
+unit_t *
+find_unit (int n)
+{
+ unit_t *p;
+ int c;
+
+ for (c = 0; c < CACHE_SIZE; c++)
+ if (unit_cache[c] != NULL && unit_cache[c]->unit_number == n)
+ {
+ p = unit_cache[c];
+ return p;
+ }
+
+ p = g.unit_root;
+ while (p != NULL)
+ {
+ c = compare (n, p->unit_number);
+ if (c < 0)
+ p = p->left;
+ if (c > 0)
+ p = p->right;
+ if (c == 0)
+ break;
+ }
+
+ if (p != NULL)
+ {
+ for (c = 0; c < CACHE_SIZE - 1; c++)
+ unit_cache[c] = unit_cache[c + 1];
+
+ unit_cache[CACHE_SIZE - 1] = p;
+ }
+
+ return p;
+}
+
+/* get_unit()-- Returns the unit structure associated with the integer
+ * unit or the internal file. */
+
+unit_t *
+get_unit (int read_flag)
+{
+ unit_t *u;
+
+ if (ioparm.internal_unit != NULL)
+ {
+ internal_unit.s =
+ open_internal (ioparm.internal_unit, ioparm.internal_unit_len);
+
+ /* Set flags for the internal unit */
+
+ internal_unit.flags.access = ACCESS_SEQUENTIAL;
+ internal_unit.flags.action = ACTION_READWRITE;
+ internal_unit.flags.form = FORM_FORMATTED;
+ internal_unit.flags.delim = DELIM_NONE;
+
+ return &internal_unit;
+ }
+
+ /* Has to be an external unit */
+
+ u = find_unit (ioparm.unit);
+ if (u != NULL)
+ return u;
+
+ return NULL;
+}
+
+
+/* is_internal_unit()-- Determine if the current unit is internal or
+ * not */
+
+int
+is_internal_unit ()
+{
+
+ return current_unit == &internal_unit;
+}
+
+
+
+/*************************/
+/* Initialize everything */
+
+void
+init_units (void)
+{
+ offset_t m, n;
+ unit_t *u;
+ int i;
+
+ if (options.stdin_unit >= 0)
+ { /* STDIN */
+ u = get_mem (sizeof (unit_t));
+
+ u->unit_number = options.stdin_unit;
+ u->s = input_stream ();
+
+ u->flags.action = ACTION_READ;
+
+ u->flags.access = ACCESS_SEQUENTIAL;
+ u->flags.form = FORM_FORMATTED;
+ u->flags.status = STATUS_OLD;
+ u->flags.blank = BLANK_ZERO;
+ u->flags.position = POSITION_ASIS;
+
+ u->recl = options.default_recl;
+ u->endfile = NO_ENDFILE;
+
+ insert_unit (u);
+ }
+
+ if (options.stdout_unit >= 0)
+ { /* STDOUT */
+ u = get_mem (sizeof (unit_t));
+
+ u->unit_number = options.stdout_unit;
+ u->s = output_stream ();
+
+ u->flags.action = ACTION_WRITE;
+
+ u->flags.access = ACCESS_SEQUENTIAL;
+ u->flags.form = FORM_FORMATTED;
+ u->flags.status = STATUS_OLD;
+ u->flags.blank = BLANK_ZERO;
+ u->flags.position = POSITION_ASIS;
+
+ u->recl = options.default_recl;
+ u->endfile = AT_ENDFILE;
+
+ insert_unit (u);
+ }
+
+ /* Calculate the maximum file offset in a portable manner.
+ * max will be the largest signed number for the type offset_t.
+ *
+ * set a 1 in the LSB and keep a running sum, stopping at MSB-1 bit. */
+
+ g.max_offset = 0;
+ for (i=0; i < sizeof(g.max_offset) * 8 - 1; i++)
+ g.max_offset = g.max_offset + ((offset_t) 1 << i);
+
+}
+
+
+/* close_unit()-- Close a unit. The stream is closed, and any memory
+ * associated with the stream is freed. Returns nonzero on I/O error. */
+
+int
+close_unit (unit_t * u)
+{
+ int i, rc;
+
+ for (i = 0; i < CACHE_SIZE; i++)
+ if (unit_cache[i] == u)
+ unit_cache[i] = NULL;
+
+ rc = (u->s == NULL) ? 0 : sclose (u->s) == FAILURE;
+
+ delete_unit (u);
+ free_mem (u);
+
+ return rc;
+}
+
+
+/* close_units()-- Delete units on completion. We just keep deleting
+ * the root of the treap until there is nothing left. */
+
+void
+close_units (void)
+{
+
+ while (g.unit_root != NULL)
+ close_unit (g.unit_root);
+}
diff --git a/libgfortran/io/unix.c b/libgfortran/io/unix.c
new file mode 100644
index 0000000..185608a
--- /dev/null
+++ b/libgfortran/io/unix.c
@@ -0,0 +1,1432 @@
+/* Copyright (C) 2002, 2003, 2004 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. */
+
+/* Unix stream I/O module */
+
+#include "config.h"
+#include <stdlib.h>
+#include <limits.h>
+
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <sys/mman.h>
+#include <string.h>
+#include <errno.h>
+
+#include "libgfortran.h"
+#include "io.h"
+
+#ifndef PATH_MAX
+#define PATH_MAX 1024
+#endif
+
+#ifndef MAP_FAILED
+#define MAP_FAILED ((void *) -1)
+#endif
+
+/* This implementation of stream I/O is based on the paper:
+ *
+ * "Exploiting the advantages of mapped files for stream I/O",
+ * O. Krieger, M. Stumm and R. Umrau, "Proceedings of the 1992 Winter
+ * USENIX conference", p. 27-42.
+ *
+ * It differs in a number of ways from the version described in the
+ * paper. First of all, threads are not an issue during I/O and we
+ * also don't have to worry about having multiple regions, since
+ * fortran's I/O model only allows you to be one place at a time.
+ *
+ * On the other hand, we have to be able to writing at the end of a
+ * stream, read from the start of a stream or read and write blocks of
+ * bytes from an arbitrary position. After opening a file, a pointer
+ * to a stream structure is returned, which is used to handle file
+ * accesses until the file is closed.
+ *
+ * salloc_at_r(stream, len, where)-- Given a stream pointer, return a
+ * pointer to a block of memory that mirror the file at position
+ * 'where' that is 'len' bytes long. The len integer is updated to
+ * reflect how many bytes were actually read. The only reason for a
+ * short read is end of file. The file pointer is updated. The
+ * pointer is valid until the next call to salloc_*.
+ *
+ * salloc_at_w(stream, len, where)-- Given the stream pointer, returns
+ * a pointer to a block of memory that is updated to reflect the state
+ * of the file. The length of the buffer is always equal to that
+ * requested. The buffer must be completely set by the caller. When
+ * data has been written, the sfree() function must be called to
+ * indicate that the caller is done writing data to the buffer. This
+ * may or may not cause a physical write.
+ *
+ * Short forms of these are salloc_r() and salloc_w() which drop the
+ * 'where' parameter and use the current file pointer. */
+
+
+#define BUFFER_SIZE 8192
+
+typedef struct
+{
+ stream st;
+
+ int fd;
+ offset_t buffer_offset; /* File offset of the start of the buffer */
+ offset_t physical_offset; /* Current physical file offset */
+ offset_t logical_offset; /* Current logical file offset */
+ offset_t dirty_offset; /* Start of modified bytes in buffer */
+ offset_t 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 */
+
+int
+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;
+
+ if (str->dirty_offset+str->ndirty > str->logical_offset)
+ {
+ if (str->ndirty + pos_off > 0)
+ str->ndirty += pos_off ;
+ else
+ {
+ str->dirty_offset += pos_off + pos_off;
+ str->ndirty = 0 ;
+ }
+ }
+
+ return pos_off ;
+ }
+ return 0 ;
+}
+
+
+/* fix_fd()-- Given a file descriptor, make sure it is not one of the
+ * standard descriptors, returning a non-standard descriptor. If the
+ * user specifies that system errors should go to standard output,
+ * then closes standard output, we don't want the system errors to a
+ * file that has been given file descriptor 1 or 0. We want to send
+ * the error to the invalid descriptor. */
+
+static int
+fix_fd (int fd)
+{
+ int input, output, error;
+
+ input = output = error = 0;
+
+/* Unix allocates the lowest descriptors first, so a loop is not
+ * required, but this order is. */
+
+ if (fd == STDIN_FILENO)
+ {
+ fd = dup (fd);
+ input = 1;
+ }
+ if (fd == STDOUT_FILENO)
+ {
+ fd = dup (fd);
+ output = 1;
+ }
+ if (fd == STDERR_FILENO)
+ {
+ fd = dup (fd);
+ error = 1;
+ }
+
+ if (input)
+ close (STDIN_FILENO);
+ if (output)
+ close (STDOUT_FILENO);
+ if (error)
+ close (STDERR_FILENO);
+
+ return fd;
+}
+
+
+/* write()-- Write a buffer to a descriptor, allowing for short writes */
+
+static int
+writen (int fd, char *buffer, int len)
+{
+ int n, n0;
+
+ n0 = len;
+
+ while (len > 0)
+ {
+ n = write (fd, buffer, len);
+ if (n < 0)
+ return n;
+
+ buffer += n;
+ len -= n;
+ }
+
+ return n0;
+}
+
+
+#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. */
+
+static int
+readn (int fd, char *buffer, int len)
+{
+ int nread, n;
+
+ nread = 0;
+
+ while (len > 0)
+ {
+ n = read (fd, buffer, len);
+ if (n < 0)
+ return n;
+
+ if (n == 0)
+ return nread;
+
+ buffer += n;
+ nread += n;
+ len -= n;
+ }
+
+ return nread;
+}
+#endif
+
+
+/* get_oserror()-- Get the most recent operating system error. For
+ * unix, this is errno. */
+
+const char *
+get_oserror (void)
+{
+
+ return strerror (errno);
+}
+
+
+/* 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;;
+
+ if (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)
+ return FAILURE;
+
+ s->physical_offset = s->dirty_offset + s->ndirty;
+ if (s->physical_offset > s->file_length)
+ s->file_length = s->physical_offset;
+ s->ndirty = 0;
+
+ return SUCCESS;
+}
+
+
+/* fd_alloc()-- Arrange a buffer such that the salloc() request can be
+ * satisfied. This subroutine gets the buffer ready for whatever is
+ * to come next. */
+
+static void
+fd_alloc (unix_stream * s, offset_t where, int *len)
+{
+ char *new_buffer;
+ int n, read_len;
+
+ if (*len <= BUFFER_SIZE)
+ {
+ new_buffer = s->small_buffer;
+ read_len = BUFFER_SIZE;
+ }
+ else
+ {
+ new_buffer = get_mem (*len);
+ read_len = *len;
+ }
+
+ /* Salvage bytes currently within the buffer. This is important for
+ * devices that cannot seek. */
+
+ if (s->buffer != NULL && s->buffer_offset <= where &&
+ where <= s->buffer_offset + s->active)
+ {
+
+ n = s->active - (where - s->buffer_offset);
+ memmove (new_buffer, s->buffer + (where - s->buffer_offset), n);
+
+ s->active = n;
+ }
+ else
+ { /* new buffer starts off empty */
+ s->active = 0;
+ }
+
+ s->buffer_offset = where;
+
+ /* free the old buffer if necessary */
+
+ if (s->buffer != NULL && s->buffer != s->small_buffer)
+ free_mem (s->buffer);
+
+ s->buffer = new_buffer;
+ s->len = read_len;
+ s->mmaped = 0;
+}
+
+
+/* fd_alloc_r_at()-- Allocate a stream buffer for reading. Either
+ * we've already buffered the data or we need to load it. Returns
+ * NULL on I/O error. */
+
+static char *
+fd_alloc_r_at (unix_stream * s, int *len, offset_t where)
+{
+ offset_t m;
+ int n;
+
+ if (where == -1)
+ where = s->logical_offset;
+
+ if (s->buffer != NULL && s->buffer_offset <= where &&
+ where + *len <= s->buffer_offset + s->active)
+ {
+
+ /* Return a position within the current buffer */
+
+ s->logical_offset = where + *len;
+ return s->buffer + where - s->buffer_offset;
+ }
+
+ fd_alloc (s, where, len);
+
+ m = where + s->active;
+
+ 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;
+
+ s->physical_offset = where + n;
+
+ s->active += n;
+ if (s->active < *len)
+ *len = s->active; /* Bytes actually available */
+
+ s->logical_offset = where + *len;
+
+ return s->buffer;
+}
+
+
+/* fd_alloc_w_at()-- Allocate a stream buffer for writing. Either
+ * we've already buffered the data or we need to load it. */
+
+static char *
+fd_alloc_w_at (unix_stream * s, int *len, offset_t where)
+{
+ offset_t n;
+
+ if (where == -1)
+ where = s->logical_offset;
+
+ if (s->buffer == NULL || s->buffer_offset > where ||
+ where + *len > s->buffer_offset + s->len)
+ {
+
+ if (fd_flush (s) == FAILURE)
+ return NULL;
+ fd_alloc (s, where, len);
+ }
+
+ /* Return a position within the current buffer */
+
+ if (s->ndirty == 0)
+ { /* First write into a clean buffer */
+ s->dirty_offset = where;
+ s->ndirty = *len;
+ }
+ else
+ {
+ if (s->dirty_offset + s->ndirty == where)
+ s->ndirty += *len;
+ else
+ fd_flush (s); /* Can't combine two dirty blocks */
+ }
+
+ s->logical_offset = where + *len;
+
+ n = s->logical_offset - s->buffer_offset;
+ if (n > s->active)
+ s->active = n;
+
+ return s->buffer + where - s->buffer_offset;
+}
+
+
+static try
+fd_sfree (unix_stream * s)
+{
+
+ if (s->ndirty != 0 &&
+ (s->buffer != s->small_buffer || options.all_unbuffered ||
+ s->unbuffered))
+ return fd_flush (s);
+
+ return SUCCESS;
+}
+
+
+static int
+fd_seek (unix_stream * s, offset_t offset)
+{
+
+ s->physical_offset = s->logical_offset = offset;
+
+ return (lseek (s->fd, offset, SEEK_SET) < 0) ? FAILURE : SUCCESS;
+}
+
+
+/* truncate_file()-- Given a unit, truncate the file at the current
+ * position. Sets the physical location to the new end of the file.
+ * Returns nonzero on error. */
+
+static try
+fd_truncate (unix_stream * s)
+{
+
+ if (ftruncate (s->fd, s->logical_offset))
+ return FAILURE;
+
+ s->physical_offset = s->file_length = s->logical_offset;
+
+ if (lseek (s->fd, s->file_length, SEEK_SET) == -1)
+ return FAILURE;
+
+ 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. */
+
+static try
+mmap_alloc (unix_stream * s, offset_t where, int *len)
+{
+ offset_t offset;
+ int length;
+ char *p;
+
+ if (mmap_flush (s) == FAILURE)
+ return FAILURE;
+
+ offset = where & page_mask; /* Round down to the next page */
+
+ length = ((where - offset) & page_mask) + 2 * page_size;
+
+ p = mmap (NULL, length, s->prot, MAP_SHARED, s->fd, offset);
+ if (p == (char *) MAP_FAILED)
+ return FAILURE;
+
+ s->mmaped = 1;
+ s->buffer = p;
+ s->buffer_offset = offset;
+ s->active = length;
+
+ return SUCCESS;
+}
+
+
+static char *
+mmap_alloc_r_at (unix_stream * s, int *len, offset_t where)
+{
+ offset_t m;
+
+ if (where == -1)
+ where = s->logical_offset;
+
+ m = where + *len;
+
+ if ((s->buffer == NULL || s->buffer_offset > where ||
+ m > s->buffer_offset + s->active) &&
+ mmap_alloc (s, where, len) == FAILURE)
+ return NULL;
+
+ if (m > s->file_length)
+ {
+ *len = s->file_length - s->logical_offset;
+ s->logical_offset = s->file_length;
+ }
+ else
+ s->logical_offset = m;
+
+ return s->buffer + (where - s->buffer_offset);
+}
+
+
+static char *
+mmap_alloc_w_at (unix_stream * s, int *len, offset_t where)
+{
+ if (where == -1)
+ where = s->logical_offset;
+
+ /* If we're extending the file, we have to use file descriptor
+ * methods. */
+
+ if (where + *len > s->file_length)
+ {
+ if (s->mmaped)
+ mmap_flush (s);
+ return fd_alloc_w_at (s, len, where);
+ }
+
+ if ((s->buffer == NULL || s->buffer_offset > where ||
+ where + *len > 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;
+}
+
+
+static int
+mmap_seek (unix_stream * s, offset_t offset)
+{
+
+ s->logical_offset = offset;
+ return SUCCESS;
+}
+
+
+static try
+mmap_close (unix_stream * s)
+{
+ try t;
+
+ t = mmap_flush (s);
+
+ if (close (s->fd) < 0)
+ t = FAILURE;
+ free_mem (s);
+
+ return t;
+}
+
+
+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)
+{
+ 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;
+ }
+
+ 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.truncate = (void *) fd_truncate;
+
+ if (lseek (s->fd, s->file_length, SEEK_SET) < 0)
+ return FAILURE;
+
+ return SUCCESS;
+}
+
+#endif
+
+
+/*********************************************************************
+ memory stream functions - These are used for internal files
+
+ The idea here is that a single stream structure is created and all
+ requests must be satisfied from it. The location and size of the
+ buffer is the character variable supplied to the READ or WRITE
+ statement.
+
+*********************************************************************/
+
+
+static char *
+mem_alloc_r_at (unix_stream * s, int *len, offset_t where)
+{
+ offset_t n;
+
+ if (where == -1)
+ where = s->logical_offset;
+
+ if (where < s->buffer_offset || where > s->buffer_offset + s->active)
+ return NULL;
+
+ if (is_internal_unit() && where + *len > s->file_length)
+ return NULL;
+
+ s->logical_offset = where + *len;
+
+ n = (where - s->buffer_offset) - s->active;
+ if (*len > n)
+ *len = n;
+
+ return s->buffer + (where - s->buffer_offset);
+}
+
+
+static char *
+mem_alloc_w_at (unix_stream * s, int *len, offset_t where)
+{
+ offset_t m;
+
+ if (where == -1)
+ where = s->logical_offset;
+
+ m = where + *len;
+
+ if (where < s->buffer_offset || m > s->buffer_offset + s->active)
+ return NULL;
+
+ s->logical_offset = m;
+
+ return s->buffer + (where - s->buffer_offset);
+}
+
+
+static int
+mem_seek (unix_stream * s, offset_t offset)
+{
+
+ if (offset > s->file_length)
+ {
+ errno = ESPIPE;
+ return FAILURE;
+ }
+
+ s->logical_offset = offset;
+ return SUCCESS;
+}
+
+
+static int
+mem_truncate (unix_stream * s)
+{
+
+ return SUCCESS;
+}
+
+
+static try
+mem_close (unix_stream * s)
+{
+
+ return SUCCESS;
+}
+
+
+static try
+mem_sfree (unix_stream * s)
+{
+
+ return SUCCESS;
+}
+
+
+
+/*********************************************************************
+ Public functions -- A reimplementation of this module needs to
+ define functional equivalents of the following.
+*********************************************************************/
+
+/* empty_internal_buffer()-- Zero the buffer of Internal file */
+
+void
+empty_internal_buffer(stream *strm)
+{
+ unix_stream * s = (unix_stream *) strm;
+ memset(s->buffer, ' ', s->file_length);
+}
+
+/* open_internal()-- Returns a stream structure from an internal file */
+
+stream *
+open_internal (char *base, int length)
+{
+ unix_stream *s;
+
+ s = get_mem (sizeof (unix_stream));
+
+ s->buffer = base;
+ s->buffer_offset = 0;
+
+ s->logical_offset = 0;
+ s->active = s->file_length = length;
+
+ s->st.alloc_r_at = (void *) mem_alloc_r_at;
+ s->st.alloc_w_at = (void *) mem_alloc_w_at;
+ s->st.sfree = (void *) mem_sfree;
+ s->st.close = (void *) mem_close;
+ s->st.seek = (void *) mem_seek;
+ s->st.truncate = (void *) mem_truncate;
+
+ return (stream *) s;
+}
+
+
+/* fd_to_stream()-- Given an open file descriptor, build a stream
+ * around it. */
+
+static stream *
+fd_to_stream (int fd, int prot)
+{
+ struct stat statbuf;
+ unix_stream *s;
+
+ s = get_mem (sizeof (unix_stream));
+
+ s->fd = fd;
+ s->buffer_offset = 0;
+ s->physical_offset = 0;
+ s->logical_offset = 0;
+ s->prot = prot;
+
+ /* Get the current length of the file. */
+
+ fstat (fd, &statbuf);
+ s->file_length = S_ISREG (statbuf.st_mode) ? statbuf.st_size : -1;
+
+#if HAVE_MMAP
+ mmap_open (s);
+#else
+ fd_open (s);
+#endif
+
+ return (stream *) s;
+}
+
+
+/* unpack_filename()-- Given a fortran string and a pointer to a
+ * buffer that is PATH_MAX characters, convert the fortran string to a
+ * C string in the buffer. Returns nonzero if this is not possible. */
+
+static int
+unpack_filename (char *cstring, const char *fstring, int len)
+{
+
+ len = fstrlen (fstring, len);
+ if (len >= PATH_MAX)
+ return 1;
+
+ memmove (cstring, fstring, len);
+ cstring[len] = '\0';
+
+ return 0;
+}
+
+
+/* tempfile()-- Generate a temporary filename for a scratch file and
+ * open it. mkstemp() opens the file for reading and writing, but the
+ * library mode prevents anything that is not allowed. The descriptor
+ * is returns, which is less than zero on error. The template is
+ * pointed to by ioparm.file, which is copied into the unit structure
+ * and freed later. */
+
+static int
+tempfile (void)
+{
+ const char *tempdir;
+ char *template;
+ int fd;
+
+ tempdir = getenv ("GFORTRAN_TMPDIR");
+ if (tempdir == NULL)
+ tempdir = getenv ("TMP");
+ if (tempdir == NULL)
+ tempdir = DEFAULT_TEMPDIR;
+
+ template = get_mem (strlen (tempdir) + 20);
+
+ st_sprintf (template, "%s/gfortantmpXXXXXX", tempdir);
+
+ fd = mkstemp (template);
+
+ if (fd < 0)
+ free_mem (template);
+ else
+ {
+ ioparm.file = template;
+ ioparm.file_len = strlen (template); /* Don't include trailing nul */
+ }
+
+ return fd;
+}
+
+
+/* regular_file()-- Open a regular file. Returns the descriptor, which is less than zero on error. */
+
+static int
+regular_file (unit_action action, unit_status status)
+{
+ char path[PATH_MAX + 1];
+ struct stat statbuf;
+ int mode;
+
+ if (unpack_filename (path, ioparm.file, ioparm.file_len))
+ {
+ errno = ENOENT; /* Fake an OS error */
+ return -1;
+ }
+
+ mode = 0;
+
+ switch (action)
+ {
+ case ACTION_READ:
+ mode = O_RDONLY;
+ break;
+
+ case ACTION_WRITE:
+ mode = O_WRONLY;
+ break;
+
+ case ACTION_READWRITE:
+ mode = O_RDWR;
+ break;
+
+ default:
+ internal_error ("regular_file(): Bad action");
+ }
+
+ switch (status)
+ {
+ case STATUS_NEW:
+ mode |= O_CREAT | O_EXCL;
+ break;
+
+ case STATUS_OLD: /* file must exist, so check for its existence */
+ if (stat (path, &statbuf) < 0)
+ return -1;
+ break;
+
+ case STATUS_UNKNOWN:
+ case STATUS_SCRATCH:
+ mode |= O_CREAT;
+ break;
+
+ case STATUS_REPLACE:
+ mode |= O_TRUNC;
+ break;
+
+ default:
+ internal_error ("regular_file(): Bad status");
+ }
+
+ // mode |= O_LARGEFILE;
+
+ return open (path, mode,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
+}
+
+
+/* open_external()-- Open an external file, unix specific version.
+ * Returns NULL on operating system error. */
+
+stream *
+open_external (unit_action action, unit_status status)
+{
+ int fd, prot;
+
+ fd =
+ (status == STATUS_SCRATCH) ? tempfile () : regular_file (action, status);
+
+ if (fd < 0)
+ return NULL;
+ fd = fix_fd (fd);
+
+ switch (action)
+ {
+ case ACTION_READ:
+ prot = PROT_READ;
+ break;
+
+ case ACTION_WRITE:
+ prot = PROT_WRITE;
+ break;
+
+ case ACTION_READWRITE:
+ prot = PROT_READ | PROT_WRITE;
+ break;
+
+ default:
+ internal_error ("open_external(): Bad action");
+ }
+
+ /* If this is a scratch file, we can unlink it now and the file will
+ * go away when it is closed. */
+
+ if (status == STATUS_SCRATCH)
+ unlink (ioparm.file);
+
+ return fd_to_stream (fd, prot);
+}
+
+
+/* input_stream()-- Return a stream pointer to the default input stream.
+ * Called on initialization. */
+
+stream *
+input_stream (void)
+{
+
+ return fd_to_stream (STDIN_FILENO, PROT_READ);
+}
+
+
+/* output_stream()-- Return a stream pointer to the default input stream.
+ * Called on initialization. */
+
+stream *
+output_stream (void)
+{
+
+ return fd_to_stream (STDOUT_FILENO, PROT_WRITE);
+}
+
+
+/* init_error_stream()-- Return a pointer to the error stream. This
+ * subroutine is called when the stream is needed, rather than at
+ * initialization. We want to work even if memory has been seriously
+ * corrupted. */
+
+stream *
+init_error_stream (void)
+{
+ static unix_stream error;
+
+ memset (&error, '\0', sizeof (error));
+
+ 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;
+}
+
+
+/* compare_file_filename()-- Given an open stream and a fortran string
+ * that is a filename, figure out if the file is the same as the
+ * filename. */
+
+int
+compare_file_filename (stream * s, const char *name, int len)
+{
+ char path[PATH_MAX + 1];
+ struct stat st1, st2;
+
+ if (unpack_filename (path, name, len))
+ return 0; /* Can't be the same */
+
+ /* If the filename doesn't exist, then there is no match with the
+ * existing file. */
+
+ if (stat (path, &st1) < 0)
+ return 0;
+
+ fstat (((unix_stream *) s)->fd, &st2);
+
+ return (st1.st_dev == st2.st_dev) && (st1.st_ino == st2.st_ino);
+}
+
+
+/* find_file0()-- Recursive work function for find_file() */
+
+static unit_t *
+find_file0 (unit_t * u, struct stat *st1)
+{
+ struct stat st2;
+ unit_t *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)
+ return u;
+
+ v = find_file0 (u->left, st1);
+ if (v != NULL)
+ return v;
+
+ v = find_file0 (u->right, st1);
+ if (v != NULL)
+ return v;
+
+ return NULL;
+}
+
+
+/* find_file()-- Take the current filename and see if there is a unit
+ * that has the file already open. Returns a pointer to the unit if so. */
+
+unit_t *
+find_file (void)
+{
+ char path[PATH_MAX + 1];
+ struct stat statbuf;
+
+ if (unpack_filename (path, ioparm.file, ioparm.file_len))
+ return NULL;
+
+ if (stat (path, &statbuf) < 0)
+ return NULL;
+
+ return find_file0 (g.unit_root, &statbuf);
+}
+
+
+/* stream_at_bof()-- Returns nonzero if the stream is at the beginning
+ * of the file. */
+
+int
+stream_at_bof (stream * s)
+{
+ unix_stream *us;
+
+ us = (unix_stream *) s;
+
+ if (!us->mmaped)
+ return 0; /* File is not seekable */
+
+ return us->logical_offset == 0;
+}
+
+
+/* stream_at_eof()-- Returns nonzero if the stream is at the beginning
+ * of the file. */
+
+int
+stream_at_eof (stream * s)
+{
+ unix_stream *us;
+
+ us = (unix_stream *) s;
+
+ if (!us->mmaped)
+ return 0; /* File is not seekable */
+
+ return us->logical_offset == us->dirty_offset;
+}
+
+
+/* delete_file()-- Given a unit structure, delete the file associated
+ * with the unit. Returns nonzero if something went wrong. */
+
+int
+delete_file (unit_t * u)
+{
+ char path[PATH_MAX + 1];
+
+ if (unpack_filename (path, u->file, u->file_len))
+ { /* Shouldn't be possible */
+ errno = ENOENT;
+ return 1;
+ }
+
+ return unlink (path);
+}
+
+
+/* file_exists()-- Returns nonzero if the current filename exists on
+ * the system */
+
+int
+file_exists (void)
+{
+ char path[PATH_MAX + 1];
+ struct stat statbuf;
+
+ if (unpack_filename (path, ioparm.file, ioparm.file_len))
+ return 0;
+
+ if (stat (path, &statbuf) < 0)
+ return 0;
+
+ return 1;
+}
+
+
+
+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
+ * string. */
+
+const char *
+inquire_sequential (const char *string, int len)
+{
+ char path[PATH_MAX + 1];
+ struct stat statbuf;
+
+ if (string == NULL ||
+ unpack_filename (path, string, len) || stat (path, &statbuf) < 0)
+ return unknown;
+
+ if (S_ISREG (statbuf.st_mode) ||
+ S_ISCHR (statbuf.st_mode) || S_ISFIFO (statbuf.st_mode))
+ return yes;
+
+ if (S_ISDIR (statbuf.st_mode) || S_ISBLK (statbuf.st_mode))
+ return no;
+
+ return unknown;
+}
+
+
+/* inquire_direct()-- Given a fortran string, determine if the file is
+ * suitable for direct access. Returns a C-style string. */
+
+const char *
+inquire_direct (const char *string, int len)
+{
+ char path[PATH_MAX + 1];
+ struct stat statbuf;
+
+ if (string == NULL ||
+ unpack_filename (path, string, len) || stat (path, &statbuf) < 0)
+ return unknown;
+
+ if (S_ISREG (statbuf.st_mode) || S_ISBLK (statbuf.st_mode))
+ return yes;
+
+ if (S_ISDIR (statbuf.st_mode) ||
+ S_ISCHR (statbuf.st_mode) || S_ISFIFO (statbuf.st_mode))
+ return no;
+
+ return unknown;
+}
+
+
+/* inquire_formatted()-- Given a fortran string, determine if the file
+ * is suitable for formatted form. Returns a C-style string. */
+
+const char *
+inquire_formatted (const char *string, int len)
+{
+ char path[PATH_MAX + 1];
+ struct stat statbuf;
+
+ if (string == NULL ||
+ unpack_filename (path, string, len) || stat (path, &statbuf) < 0)
+ return unknown;
+
+ if (S_ISREG (statbuf.st_mode) ||
+ S_ISBLK (statbuf.st_mode) ||
+ S_ISCHR (statbuf.st_mode) || S_ISFIFO (statbuf.st_mode))
+ return yes;
+
+ if (S_ISDIR (statbuf.st_mode))
+ return no;
+
+ return unknown;
+}
+
+
+/* inquire_unformatted()-- Given a fortran string, determine if the file
+ * is suitable for unformatted form. Returns a C-style string. */
+
+const char *
+inquire_unformatted (const char *string, int len)
+{
+
+ return inquire_formatted (string, len);
+}
+
+
+/* inquire_access()-- Given a fortran string, determine if the file is
+ * suitable for access. */
+
+static const char *
+inquire_access (const char *string, int len, int mode)
+{
+ char path[PATH_MAX + 1];
+
+ if (string == NULL || unpack_filename (path, string, len) ||
+ access (path, mode) < 0)
+ return no;
+
+ return yes;
+}
+
+
+/* inquire_read()-- Given a fortran string, determine if the file is
+ * suitable for READ access. */
+
+const char *
+inquire_read (const char *string, int len)
+{
+
+ return inquire_access (string, len, R_OK);
+}
+
+
+/* inquire_write()-- Given a fortran string, determine if the file is
+ * suitable for READ access. */
+
+const char *
+inquire_write (const char *string, int len)
+{
+
+ return inquire_access (string, len, W_OK);
+}
+
+
+/* inquire_readwrite()-- Given a fortran string, determine if the file is
+ * suitable for read and write access. */
+
+const char *
+inquire_readwrite (const char *string, int len)
+{
+
+ return inquire_access (string, len, R_OK | W_OK);
+}
+
+
+/* file_length()-- Return the file length in bytes, -1 if unknown */
+
+offset_t
+file_length (stream * s)
+{
+
+ return ((unix_stream *) s)->file_length;
+}
+
+
+/* file_position()-- Return the current position of the file */
+
+offset_t
+file_position (stream * s)
+{
+
+ return ((unix_stream *) s)->logical_offset;
+}
+
+
+/* is_seekable()-- Return nonzero if the stream is seekable, zero if
+ * it is not */
+
+int
+is_seekable (stream * s)
+{
+
+ return ((unix_stream *) s)->mmaped;
+}
+
+
+/* How files are stored: This is an operating-system specific issue,
+ and therefore belongs here. There are three cases to consider.
+
+ Direct Access:
+ Records are written as block of bytes corresponding to the record
+ length of the file. This goes for both formatted and unformatted
+ records. Positioning is done explicitly for each data transfer,
+ so positioning is not much of an issue.
+
+ Sequential Formatted:
+ Records are separated by newline characters. The newline character
+ is prohibited from appearing in a string. If it does, this will be
+ messed up on the next read. End of file is also the end of a record.
+
+ Sequential Unformatted:
+ In this case, we are merely copying bytes to and from main storage,
+ yet we need to keep track of varying record lengths. We adopt
+ the solution used by f2c. Each record contains a pair of length
+ markers:
+
+ Length of record n in bytes
+ Data of record n
+ Length of record n in bytes
+
+ Length of record n+1 in bytes
+ Data of record n+1
+ Length of record n+1 in bytes
+
+ The length is stored at the end of a record to allow backspacing to the
+ previous record. Between data transfer statements, the file pointer
+ is left pointing to the first length of the current record.
+
+ ENDFILE records are never explicitly stored.
+
+*/
diff --git a/libgfortran/io/write.c b/libgfortran/io/write.c
new file mode 100644
index 0000000..dd44f6e
--- /dev/null
+++ b/libgfortran/io/write.c
@@ -0,0 +1,1129 @@
+/* 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. */
+
+#include "config.h"
+#include <string.h>
+#include <float.h>
+#include "libgfortran.h"
+#include "io.h"
+#include <stdio.h>
+
+
+#define star_fill(p, n) memset(p, '*', n)
+
+
+typedef enum
+{ SIGN_NONE, SIGN_MINUS, SIGN_PLUS }
+sign_t;
+
+
+void
+write_a (fnode * f, const char *source, int len)
+{
+ int wlen;
+ char *p;
+
+ wlen = f->u.string.length < 0 ? len : f->u.string.length;
+
+ p = write_block (wlen);
+ if (p == NULL)
+ return;
+
+ if (wlen < len)
+ memcpy (p, source, wlen);
+ else
+ {
+ memcpy (p, source, len);
+ memset (p + len, ' ', wlen - len);
+ }
+}
+
+static int64_t
+extract_int (const void *p, int len)
+{
+ int64_t i = 0;
+
+ if (p == NULL)
+ return i;
+
+ switch (len)
+ {
+ case 1:
+ i = *((const int8_t *) p);
+ break;
+ case 2:
+ i = *((const int16_t *) p);
+ break;
+ case 4:
+ i = *((const int32_t *) p);
+ break;
+ case 8:
+ i = *((const int64_t *) p);
+ break;
+ default:
+ internal_error ("bad integer kind");
+ }
+
+ return i;
+}
+
+static double
+extract_real (const void *p, int len)
+{
+ double i = 0.0;
+ switch (len)
+ {
+ case 4:
+ i = *((const float *) p);
+ break;
+ case 8:
+ i = *((const double *) p);
+ break;
+ default:
+ internal_error ("bad real kind");
+ }
+ return i;
+
+}
+
+
+/* calculate sign()-- Given a flag that indicate if a value is
+ * negative or not, return a sign_t that gives the sign that we need
+ * to produce. */
+
+static sign_t
+calculate_sign (int negative_flag)
+{
+ sign_t s = SIGN_NONE;
+
+ if (negative_flag)
+ s = SIGN_MINUS;
+ else
+ switch (g.sign_status)
+ {
+ case SIGN_SP:
+ s = SIGN_PLUS;
+ break;
+ case SIGN_SS:
+ s = SIGN_NONE;
+ break;
+ case SIGN_S:
+ s = options.optional_plus ? SIGN_PLUS : SIGN_NONE;
+ break;
+ }
+
+ return s;
+}
+
+
+/* calculate_exp()-- returns the value of 10**d. */
+
+static double
+calculate_exp (int d)
+{
+ int i;
+ double r = 1.0;
+
+ for (i = 0; i< (d >= 0 ? d : -d); i++)
+ r *= 10;
+
+ r = (d >= 0) ? r : 1.0 / r;
+
+ return r;
+}
+
+
+/* calculate_G_format()-- geneate corresponding I/O format for
+ FMT_G output.
+ The rules to translate FMT_G to FMT_E or FNT_F from DEC fortran
+ LRM (table 11-2, Chapter 11, "I/O Formatting", P11-25) is:
+
+ Data Magnitude Equivalent Conversion
+ 0< m < 0.1-0.5*10**(-d-1) Ew.d[Ee]
+ m = 0 F(w-n).(d-1), n' '
+ 0.1-0.5*10**(-d-1)<= m < 1-0.5*10**(-d) F(w-n).d, n' '
+ 1-0.5*10**(-d)<= m < 10-0.5*10**(-d+1) F(w-n).(d-1), n' '
+ 10-0.5*10**(-d+1)<= m < 100-0.5*10**(-d+2) F(w-n).(d-2), n' '
+ ................ ..........
+ 10**(d-1)-0.5*10**(-1)<= m <10**d-0.5 F(w-n).0,n(' ')
+ m >= 10**d-0.5 Ew.d[Ee]
+
+ notes: for Gw.d , n' ' means 4 blanks
+ for Gw.dEe, n' ' means e+2 blanks */
+
+static fnode *
+calculate_G_format (fnode *f, double value, int len, int *num_blank)
+{
+ int e = f->u.real.e;
+ int d = f->u.real.d;
+ int w = f->u.real.w;
+ fnode *newf;
+ double m, exp_d;
+ int low, high, mid;
+ int ubound, lbound;
+
+ newf = get_mem (sizeof (fnode));
+
+ /* Absolute value. */
+ m = (value > 0.0) ? value : -value;
+
+ /* In case of the two data magnitude ranges,
+ generate E editing, Ew.d[Ee]. */
+ exp_d = calculate_exp (d);
+ if ((m > 0.0 && m < 0.1 - 0.05 / (double) exp_d)
+ || (m >= (double) exp_d - 0.5 ))
+ {
+ newf->format = FMT_E;
+ newf->u.real.w = w;
+ newf->u.real.d = d;
+ newf->u.real.e = e;
+ *num_blank = e + 2;
+ return newf;
+ }
+
+ /* Use binary search to find the data magnitude range. */
+ mid = 0;
+ low = 0;
+ high = d + 1;
+ lbound = 0;
+ ubound = d + 1;
+
+ while (low <= high)
+ {
+ double temp;
+ mid = (low + high) / 2;
+
+ /* 0.1 * 10**mid - 0.5 * 10**(mid-d-1) */
+ temp = 0.1 * calculate_exp (mid) - 0.5 * calculate_exp (mid - d - 1);
+
+ if (m < temp)
+ {
+ ubound = mid;
+ if (ubound == lbound + 1)
+ break;
+ high = mid - 1;
+ }
+ else if (m > temp)
+ {
+ lbound = mid;
+ if (ubound == lbound + 1)
+ {
+ mid ++;
+ break;
+ }
+ low = mid + 1;
+ }
+ else
+ break;
+ }
+
+ /* Generate the F editing. F(w-4).(-(mid-d-1)), 4' '. */
+ newf->format = FMT_F;
+ newf->u.real.w = f->u.real.w - 4;
+
+ /* Special case. */
+ if (m == 0.0)
+ newf->u.real.d = d - 1;
+ else
+ newf->u.real.d = - (mid - d - 1);
+
+ *num_blank = 4;
+
+ /* For F editing, the scale factor is ignored. */
+ g.scale_factor = 0;
+ return newf;
+}
+
+
+/* output_float() -- output a real number according to its format
+ which is FMT_G free */
+
+static void
+output_float (fnode *f, double value, int len)
+{
+ int w, d, e, e_new;
+ int digits;
+ int nsign, nblank, nesign;
+ int sca, neval, itmp;
+ char *p;
+ const char *q, *intstr, *base;
+ double n;
+ format_token ft;
+ char exp_char = 'E';
+ int with_exp = 1;
+ int scale_flag = 1 ;
+ double minv = 0.0, maxv = 0.0;
+ sign_t sign = SIGN_NONE, esign = SIGN_NONE;
+
+ int intval = 0, intlen = 0;
+ int j;
+
+ /* EXP value for this number */
+ neval = 0;
+
+ /* Width of EXP and it's sign*/
+ nesign = 0;
+
+ ft = f->format;
+ w = f->u.real.w;
+ d = f->u.real.d + 1;
+
+ /* Width of the EXP */
+ e = 0;
+
+ sca = g.scale_factor;
+ n = value;
+
+ sign = calculate_sign (n < 0.0);
+ if (n < 0)
+ n = -n;
+
+ /* Width of the sign for the whole number */
+ nsign = (sign == SIGN_NONE ? 0 : 1);
+
+ digits = 0;
+ if (ft != FMT_F)
+ {
+ e = f->u.real.e;
+ }
+ if (ft == FMT_F || ft == FMT_E || ft == FMT_D)
+ {
+ if (ft == FMT_F)
+ scale_flag = 0;
+ if (ft == FMT_D)
+ exp_char = 'D' ;
+ minv = 0.1;
+ maxv = 1.0;
+
+ /* Here calculate the new val of the number with consideration
+ of Globle Scale value */
+ while (sca > 0)
+ {
+ minv *= 10.0;
+ maxv *= 10.0;
+ n *= 10.0;
+ sca -- ;
+ neval --;
+ }
+
+ /* Now calculate the new Exp value for this number */
+ sca = g.scale_factor;
+ while(sca >= 1)
+ {
+ sca /= 10;
+ digits ++ ;
+ }
+ }
+
+ if (ft == FMT_EN )
+ {
+ minv = 1.0;
+ maxv = 1000.0;
+ }
+ if (ft == FMT_ES)
+ {
+ minv = 1.0;
+ maxv = 10.0;
+ }
+
+ /* OK, let's scale the number to appropriate range */
+ while (scale_flag && n > 0.0 && n < minv)
+ {
+ if (n < minv)
+ {
+ n = n * 10.0 ;
+ neval --;
+ }
+ }
+ while (scale_flag && n > 0.0 && n > maxv)
+ {
+ if (n > maxv)
+ {
+ n = n / 10.0 ;
+ neval ++;
+ }
+ }
+
+ /* It is time to process the EXP part of the number.
+ Value of 'nesign' is 0 unless following codes is executed.
+ */
+ if (ft != FMT_F)
+ {
+ /* Sign of the EXP value */
+ if (neval >= 0)
+ esign = SIGN_PLUS;
+ else
+ {
+ esign = SIGN_MINUS;
+ neval = - neval ;
+ }
+
+ /* Width of the EXP*/
+ e_new = 0;
+ j = neval;
+ while (j > 0)
+ {
+ j = j / 10;
+ e_new ++ ;
+ }
+ if (e <= e_new)
+ e = e_new;
+
+ /* Got the width of EXP */
+ if (e < digits)
+ e = digits ;
+
+ /* Minimum value of the width would be 2 */
+ if (e < 2)
+ e = 2;
+
+ nesign = 1 ; /* We must give a position for the 'exp_char' */
+ if (e > 0)
+ nesign = e + nesign + (esign != SIGN_NONE ? 1 : 0);
+ }
+
+
+ intval = n;
+ intstr = itoa (intval);
+ intlen = strlen (intstr);
+
+ q = rtoa (n, len, d);
+ digits = strlen (q);
+
+ /* Select a width if none was specified. */
+ if (w <= 0)
+ w = digits + nsign;
+
+ p = write_block (w);
+ if (p == NULL)
+ return;
+
+ base = p;
+
+ nblank = w - (nsign + intlen + d + nesign);
+ if (nblank == -1 && ft != FMT_F)
+ {
+ with_exp = 0;
+ nesign -= 1;
+ nblank = w - (nsign + intlen + d + nesign);
+ }
+ /* don't let a leading '0' cause field overflow */
+ if (nblank == -1 && ft == FMT_F && q[0] == '0')
+ {
+ q++;
+ nblank = 0;
+ }
+
+ if (nblank < 0)
+ {
+ star_fill (p, w);
+ goto done;
+ }
+ memset (p, ' ', nblank);
+ p += nblank;
+
+ switch (sign)
+ {
+ case SIGN_PLUS:
+ *p++ = '+';
+ break;
+ case SIGN_MINUS:
+ *p++ = '-';
+ break;
+ case SIGN_NONE:
+ break;
+ }
+
+ memcpy (p, q, intlen + d + 1);
+ p += intlen + d;
+
+ if (nesign > 0)
+ {
+ if (with_exp)
+ *p++ = exp_char;
+ switch (esign)
+ {
+ case SIGN_PLUS:
+ *p++ = '+';
+ break;
+ case SIGN_MINUS:
+ *p++ = '-';
+ break;
+ case SIGN_NONE:
+ break;
+ }
+ q = itoa (neval);
+ digits = strlen (q);
+
+ for (itmp = 0; itmp < e - digits; itmp++)
+ *p++ = '0';
+ memcpy (p, q, digits);
+ p[digits] = 0;
+ }
+
+done:
+ return ;
+}
+
+void
+write_l (fnode * f, char *source, int len)
+{
+ char *p;
+ int64_t n;
+
+ p = write_block (f->u.w);
+ if (p == NULL)
+ return;
+
+ memset (p, ' ', f->u.w - 1);
+ n = extract_int (source, len);
+ p[f->u.w - 1] = (n) ? 'T' : 'F';
+}
+
+/* write_float() -- output a real number according to its format */
+
+static void
+write_float (fnode *f, const char *source, int len)
+{
+ double n;
+ int nb =0, res;
+ char * p, fin;
+ fnode *f2 = NULL;
+
+ n = extract_real (source, len);
+
+ if (f->format != FMT_B && f->format != FMT_O && f->format != FMT_Z)
+ {
+ res = finite (n);
+ if (res == 0)
+ {
+ nb = f->u.real.w;
+ if (nb <= 4)
+ nb = 4;
+ p = write_block (nb);
+ memset (p, ' ' , 1);
+
+ res = isinf (n);
+ if (res != 0)
+ {
+ if (res > 0)
+ fin = '+';
+ else
+ fin = '-';
+
+ memset (p + 1, fin, nb - 1);
+ }
+ else
+ sprintf(p + 1, "NaN");
+ return;
+ }
+ }
+
+ if (f->format != FMT_G)
+ {
+ output_float (f, n, len);
+ }
+ else
+ {
+ f2 = calculate_G_format(f, n, len, &nb);
+ output_float (f2, n, len);
+ if (f2 != NULL)
+ free_mem(f2);
+
+ if (nb > 0)
+ {
+ p = write_block (nb);
+ memset (p, ' ', nb);
+ }
+ }
+}
+
+
+static void
+write_int (fnode *f, const char *source, int len, char *(*conv) (uint64_t))
+{
+ uint32_t ns =0;
+ uint64_t n = 0;
+ int w, m, digits, nzero, nblank;
+ char *p, *q;
+
+ w = f->u.integer.w;
+ m = f->u.integer.m;
+
+ n = extract_int (source, len);
+
+ /* Special case */
+
+ if (m == 0 && n == 0)
+ {
+ if (w == 0)
+ w = 1;
+
+ p = write_block (w);
+ if (p == NULL)
+ return;
+
+ memset (p, ' ', w);
+ goto done;
+ }
+
+
+ if (len < 8)
+ {
+ ns = n;
+ q = conv (ns);
+ }
+ else
+ q = conv (n);
+
+ digits = strlen (q);
+
+ /* Select a width if none was specified. The idea here is to always
+ * print something. */
+
+ if (w == 0)
+ w = ((digits < m) ? m : digits);
+
+ p = write_block (w);
+ if (p == NULL)
+ return;
+
+ nzero = 0;
+ if (digits < m)
+ nzero = m - digits;
+
+ /* See if things will work */
+
+ nblank = w - (nzero + digits);
+
+ if (nblank < 0)
+ {
+ star_fill (p, w);
+ goto done;
+ }
+
+ memset (p, ' ', nblank);
+ p += nblank;
+
+ memset (p, '0', nzero);
+ p += nzero;
+
+ memcpy (p, q, digits);
+
+done:
+ return;
+}
+
+static void
+write_decimal (fnode *f, const char *source, int len, char *(*conv) (int64_t))
+{
+ int64_t n = 0;
+ int w, m, digits, nsign, nzero, nblank;
+ char *p, *q;
+ sign_t sign;
+
+ w = f->u.integer.w;
+ m = f->u.integer.m;
+
+ n = extract_int (source, len);
+
+ /* Special case */
+
+ if (m == 0 && n == 0)
+ {
+ if (w == 0)
+ w = 1;
+
+ p = write_block (w);
+ if (p == NULL)
+ return;
+
+ memset (p, ' ', w);
+ goto done;
+ }
+
+ sign = calculate_sign (n < 0);
+ if (n < 0)
+ n = -n;
+
+ nsign = sign == SIGN_NONE ? 0 : 1;
+ q = conv (n);
+
+ digits = strlen (q);
+
+ /* Select a width if none was specified. The idea here is to always
+ * print something. */
+
+ if (w == 0)
+ w = ((digits < m) ? m : digits) + nsign;
+
+ p = write_block (w);
+ if (p == NULL)
+ return;
+
+ nzero = 0;
+ if (digits < m)
+ nzero = m - digits;
+
+ /* See if things will work */
+
+ nblank = w - (nsign + nzero + digits);
+
+ if (nblank < 0)
+ {
+ star_fill (p, w);
+ goto done;
+ }
+
+ memset (p, ' ', nblank);
+ p += nblank;
+
+ switch (sign)
+ {
+ case SIGN_PLUS:
+ *p++ = '+';
+ break;
+ case SIGN_MINUS:
+ *p++ = '-';
+ break;
+ case SIGN_NONE:
+ break;
+ }
+
+ memset (p, '0', nzero);
+ p += nzero;
+
+ memcpy (p, q, digits);
+
+done:
+ return;
+}
+
+
+/* otoa()-- Convert unsigned octal to ascii */
+
+static char *
+otoa (uint64_t n)
+{
+ char *p;
+
+ if (n == 0)
+ {
+ scratch[0] = '0';
+ scratch[1] = '\0';
+ return scratch;
+ }
+
+ p = scratch + sizeof (SCRATCH_SIZE) - 1;
+ *p-- = '\0';
+
+ while (n != 0)
+ {
+ *p = '0' + (n & 7);
+ p -- ;
+ n >>= 3;
+ }
+
+ return ++p;
+}
+
+
+/* btoa()-- Convert unsigned binary to ascii */
+
+static char *
+btoa (uint64_t n)
+{
+ char *p;
+
+ if (n == 0)
+ {
+ scratch[0] = '0';
+ scratch[1] = '\0';
+ return scratch;
+ }
+
+ p = scratch + sizeof (SCRATCH_SIZE) - 1;
+ *p-- = '\0';
+
+ while (n != 0)
+ {
+ *p-- = '0' + (n & 1);
+ n >>= 1;
+ }
+
+ return ++p;
+}
+
+
+void
+write_i (fnode * f, const char *p, int len)
+{
+
+ write_decimal (f, p, len, (void *) itoa);
+}
+
+
+void
+write_b (fnode * f, const char *p, int len)
+{
+
+ write_int (f, p, len, btoa);
+}
+
+
+void
+write_o (fnode * f, const char *p, int len)
+{
+
+ write_int (f, p, len, otoa);
+}
+
+void
+write_z (fnode * f, const char *p, int len)
+{
+
+ write_int (f, p, len, xtoa);
+}
+
+
+void
+write_d (fnode *f, const char *p, int len)
+{
+ write_float (f, p, len);
+}
+
+
+void
+write_e (fnode *f, const char *p, int len)
+{
+ write_float (f, p, len);
+}
+
+
+void
+write_f (fnode *f, const char *p, int len)
+{
+ write_float (f, p, len);
+}
+
+
+void
+write_en (fnode *f, const char *p, int len)
+{
+ write_float (f, p, len);
+}
+
+
+void
+write_es (fnode *f, const char *p, int len)
+{
+ write_float (f, p, len);
+}
+
+
+/* write_x()-- Take care of the X/TR descriptor */
+
+void
+write_x (fnode * f)
+{
+ char *p;
+
+ p = write_block (f->u.n);
+ if (p == NULL)
+ return;
+
+ memset (p, ' ', f->u.n);
+}
+
+
+/* List-directed writing */
+
+
+/* write_char()-- Write a single character to the output. Returns
+ * nonzero if something goes wrong. */
+
+static int
+write_char (char c)
+{
+ char *p;
+
+ p = write_block (1);
+ if (p == NULL)
+ return 1;
+
+ *p = c;
+
+ return 0;
+}
+
+
+/* write_logical()-- Write a list-directed logical value */
+/* Default logical output should be L2
+ according to DEC fortran Manual. */
+static void
+write_logical (const char *source, int length)
+{
+ write_char (' ');
+ write_char (extract_int (source, length) ? 'T' : 'F');
+}
+
+
+/* write_integer()-- Write a list-directed integer value. */
+
+static void
+write_integer (const char *source, int length)
+{
+ char *p;
+ const char *q;
+ int digits;
+ int width = 12;
+
+ q = itoa (extract_int (source, length));
+
+ digits = strlen (q);
+
+ if(width < digits )
+ width = digits ;
+ p = write_block (width) ;
+
+ memset(p ,' ', width - digits) ;
+ memcpy (p + width - digits, q, digits);
+}
+
+
+/* write_character()-- Write a list-directed string. We have to worry
+ * about delimiting the strings if the file has been opened in that
+ * mode. */
+
+static void
+write_character (const char *source, int length)
+{
+ int i, extra;
+ char *p, d;
+
+ switch (current_unit->flags.delim)
+ {
+ case DELIM_APOSTROPHE:
+ d = '\'';
+ break;
+ case DELIM_QUOTE:
+ d = '"';
+ break;
+ default:
+ d = ' ';
+ break;
+ }
+
+ if (d == ' ')
+ extra = 0;
+ else
+ {
+ extra = 2;
+
+ for (i = 0; i < length; i++)
+ if (source[i] == d)
+ extra++;
+ }
+
+ p = write_block (length + extra);
+ if (p == NULL)
+ return;
+
+ if (d == ' ')
+ memcpy (p, source, length);
+ else
+ {
+ *p++ = d;
+
+ for (i = 0; i < length; i++)
+ {
+ *p++ = source[i];
+ if (source[i] == d)
+ *p++ = d;
+ }
+
+ *p = d;
+ }
+}
+
+
+/* Output the Real number with default format.
+ According to DEC fortran LRM, default format for
+ REAL(4) is 1PG15.7E2, and for REAL(8) is 1PG25.15E3 */
+
+static void
+write_real (const char *source, int length)
+{
+ fnode f ;
+ int org_scale = g.scale_factor;
+ f.format = FMT_G;
+ g.scale_factor = 1;
+ if (length < 8)
+ {
+ f.u.real.w = 15;
+ f.u.real.d = 7;
+ f.u.real.e = 2;
+ }
+ else
+ {
+ f.u.real.w = 24;
+ f.u.real.d = 15;
+ f.u.real.e = 3;
+ }
+ write_float (&f, source , length);
+ g.scale_factor = org_scale;
+}
+
+
+static void
+write_complex (const char *source, int len)
+{
+
+ if (write_char ('('))
+ return;
+ write_real (source, len);
+
+ if (write_char (','))
+ return;
+ write_real (source + len, len);
+
+ write_char (')');
+}
+
+
+/* write_separator()-- Write the separator between items. */
+
+static void
+write_separator (void)
+{
+ char *p;
+
+ p = write_block (options.separator_len);
+ if (p == NULL)
+ return;
+
+ memcpy (p, options.separator, options.separator_len);
+}
+
+
+/* list_formatted_write()-- Write an item with list formatting.
+ * TODO: handle skipping to the next record correctly, particularly
+ * with strings. */
+
+void
+list_formatted_write (bt type, void *p, int len)
+{
+ static int char_flag;
+
+ if (current_unit == NULL)
+ return;
+
+ if (g.first_item)
+ {
+ g.first_item = 0;
+ char_flag = 0;
+ }
+ else
+ {
+ if (type != BT_CHARACTER || !char_flag ||
+ current_unit->flags.delim != DELIM_NONE)
+ write_separator ();
+ }
+
+ switch (type)
+ {
+ case BT_INTEGER:
+ write_integer (p, len);
+ break;
+ case BT_LOGICAL:
+ write_logical (p, len);
+ break;
+ case BT_CHARACTER:
+ write_character (p, len);
+ break;
+ case BT_REAL:
+ write_real (p, len);
+ break;
+ case BT_COMPLEX:
+ write_complex (p, len);
+ break;
+ default:
+ internal_error ("list_formatted_write(): Bad type");
+ }
+
+ char_flag = (type == BT_CHARACTER);
+}
+
+void
+namelist_write (void)
+{
+ namelist_info * t1, *t2;
+ int len,num;
+ void * p;
+
+ num = 0;
+ write_character("&",1);
+ write_character (ioparm.namelist_name, ioparm.namelist_name_len);
+ write_character("\n",1);
+
+ if (ionml != NULL)
+ {
+ t1 = ionml;
+ while (t1 != NULL)
+ {
+ num ++;
+ t2 = t1;
+ t1 = t1->next;
+ write_character(t2->var_name, strlen(t2->var_name));
+ write_character("=",1);
+ len = t2->len;
+ p = t2->mem_pos;
+ switch (t2->type)
+ {
+ case BT_INTEGER:
+ write_integer (p, len);
+ break;
+ case BT_LOGICAL:
+ write_logical (p, len);
+ break;
+ case BT_CHARACTER:
+ write_character (p, len);
+ break;
+ case BT_REAL:
+ write_real (p, len);
+ break;
+ case BT_COMPLEX:
+ write_complex (p, len);
+ break;
+ default:
+ internal_error ("Bad type for namelist write");
+ }
+ write_character(",",1);
+ if (num > 5)
+ {
+ num = 0;
+ write_character("\n",1);
+ }
+ }
+ }
+ write_character("/",1);
+
+}
+