Home | History | Annotate | Line # | Download | only in io
open.c revision 1.1.1.4
      1 /* Copyright (C) 2002-2024 Free Software Foundation, Inc.
      2    Contributed by Andy Vaught
      3    F2003 I/O support contributed by Jerry DeLisle
      4 
      5 This file is part of the GNU Fortran runtime library (libgfortran).
      6 
      7 Libgfortran is free software; you can redistribute it and/or modify
      8 it under the terms of the GNU General Public License as published by
      9 the Free Software Foundation; either version 3, or (at your option)
     10 any later version.
     11 
     12 Libgfortran is distributed in the hope that it will be useful,
     13 but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 GNU General Public License for more details.
     16 
     17 Under Section 7 of GPL version 3, you are granted additional
     18 permissions described in the GCC Runtime Library Exception, version
     19 3.1, as published by the Free Software Foundation.
     20 
     21 You should have received a copy of the GNU General Public License and
     22 a copy of the GCC Runtime Library Exception along with this program;
     23 see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
     24 <http://www.gnu.org/licenses/>.  */
     25 
     26 #include "io.h"
     27 #include "fbuf.h"
     28 #include "unix.h"
     29 #include "async.h"
     30 
     31 #ifdef HAVE_UNISTD_H
     32 #include <unistd.h>
     33 #endif
     34 
     35 #include <string.h>
     36 #include <errno.h>
     37 
     38 
     39 static const st_option access_opt[] = {
     40   {"sequential", ACCESS_SEQUENTIAL},
     41   {"direct", ACCESS_DIRECT},
     42   {"append", ACCESS_APPEND},
     43   {"stream", ACCESS_STREAM},
     44   {NULL, 0}
     45 };
     46 
     47 static const st_option action_opt[] =
     48 {
     49   { "read", ACTION_READ},
     50   { "write", ACTION_WRITE},
     51   { "readwrite", ACTION_READWRITE},
     52   { NULL, 0}
     53 };
     54 
     55 static const st_option share_opt[] =
     56 {
     57   { "denyrw", SHARE_DENYRW },
     58   { "denynone", SHARE_DENYNONE },
     59   { NULL, 0}
     60 };
     61 
     62 static const st_option cc_opt[] =
     63 {
     64   { "list", CC_LIST },
     65   { "fortran", CC_FORTRAN },
     66   { "none", CC_NONE },
     67   { NULL, 0}
     68 };
     69 
     70 static const st_option blank_opt[] =
     71 {
     72   { "null", BLANK_NULL},
     73   { "zero", BLANK_ZERO},
     74   { NULL, 0}
     75 };
     76 
     77 static const st_option delim_opt[] =
     78 {
     79   { "none", DELIM_NONE},
     80   { "apostrophe", DELIM_APOSTROPHE},
     81   { "quote", DELIM_QUOTE},
     82   { NULL, 0}
     83 };
     84 
     85 static const st_option form_opt[] =
     86 {
     87   { "formatted", FORM_FORMATTED},
     88   { "unformatted", FORM_UNFORMATTED},
     89   { NULL, 0}
     90 };
     91 
     92 static const st_option position_opt[] =
     93 {
     94   { "asis", POSITION_ASIS},
     95   { "rewind", POSITION_REWIND},
     96   { "append", POSITION_APPEND},
     97   { NULL, 0}
     98 };
     99 
    100 static const st_option status_opt[] =
    101 {
    102   { "unknown", STATUS_UNKNOWN},
    103   { "old", STATUS_OLD},
    104   { "new", STATUS_NEW},
    105   { "replace", STATUS_REPLACE},
    106   { "scratch", STATUS_SCRATCH},
    107   { NULL, 0}
    108 };
    109 
    110 static const st_option pad_opt[] =
    111 {
    112   { "yes", PAD_YES},
    113   { "no", PAD_NO},
    114   { NULL, 0}
    115 };
    116 
    117 static const st_option decimal_opt[] =
    118 {
    119   { "point", DECIMAL_POINT},
    120   { "comma", DECIMAL_COMMA},
    121   { NULL, 0}
    122 };
    123 
    124 static const st_option encoding_opt[] =
    125 {
    126   { "utf-8", ENCODING_UTF8},
    127   { "default", ENCODING_DEFAULT},
    128   { NULL, 0}
    129 };
    130 
    131 static const st_option round_opt[] =
    132 {
    133   { "up", ROUND_UP},
    134   { "down", ROUND_DOWN},
    135   { "zero", ROUND_ZERO},
    136   { "nearest", ROUND_NEAREST},
    137   { "compatible", ROUND_COMPATIBLE},
    138   { "processor_defined", ROUND_PROCDEFINED},
    139   { NULL, 0}
    140 };
    141 
    142 static const st_option sign_opt[] =
    143 {
    144   { "plus", SIGN_PLUS},
    145   { "suppress", SIGN_SUPPRESS},
    146   { "processor_defined", SIGN_PROCDEFINED},
    147   { NULL, 0}
    148 };
    149 
    150 static const st_option convert_opt[] =
    151 {
    152   { "native", GFC_CONVERT_NATIVE},
    153   { "swap", GFC_CONVERT_SWAP},
    154   { "big_endian", GFC_CONVERT_BIG},
    155   { "little_endian", GFC_CONVERT_LITTLE},
    156 #ifdef HAVE_GFC_REAL_17
    157   /* Rather than write a special parsing routine, enumerate all the
    158      possibilities here.  */
    159   { "r16_ieee", GFC_CONVERT_R16_IEEE},
    160   { "r16_ibm", GFC_CONVERT_R16_IBM},
    161   { "native,r16_ieee", GFC_CONVERT_R16_IEEE},
    162   { "native,r16_ibm", GFC_CONVERT_R16_IBM},
    163   { "r16_ieee,native", GFC_CONVERT_R16_IEEE},
    164   { "r16_ibm,native", GFC_CONVERT_R16_IBM},
    165   { "swap,r16_ieee", GFC_CONVERT_R16_IEEE_SWAP},
    166   { "swap,r16_ibm", GFC_CONVERT_R16_IBM_SWAP},
    167   { "r16_ieee,swap", GFC_CONVERT_R16_IEEE_SWAP},
    168   { "r16_ibm,swap", GFC_CONVERT_R16_IBM_SWAP},
    169   { "big_endian,r16_ieee", GFC_CONVERT_R16_IEEE_BIG},
    170   { "big_endian,r16_ibm", GFC_CONVERT_R16_IBM_BIG},
    171   { "r16_ieee,big_endian", GFC_CONVERT_R16_IEEE_BIG},
    172   { "r16_ibm,big_endian", GFC_CONVERT_R16_IBM_BIG},
    173   { "little_endian,r16_ieee", GFC_CONVERT_R16_IEEE_LITTLE},
    174   { "little_endian,r16_ibm", GFC_CONVERT_R16_IBM_LITTLE},
    175   { "r16_ieee,little_endian", GFC_CONVERT_R16_IEEE_LITTLE},
    176   { "r16_ibm,little_endian",  GFC_CONVERT_R16_IBM_LITTLE},
    177 #endif
    178   { NULL, 0}
    179 };
    180 
    181 static const st_option async_opt[] =
    182 {
    183   { "yes", ASYNC_YES},
    184   { "no", ASYNC_NO},
    185   { NULL, 0}
    186 };
    187 
    188 /* Given a unit, test to see if the file is positioned at the terminal
    189    point, and if so, change state from NO_ENDFILE flag to AT_ENDFILE.
    190    This prevents us from changing the state from AFTER_ENDFILE to
    191    AT_ENDFILE.  */
    192 
    193 static void
    194 test_endfile (gfc_unit *u)
    195 {
    196   if (u->endfile == NO_ENDFILE)
    197     {
    198       gfc_offset sz = ssize (u->s);
    199       if (sz == 0 || sz == stell (u->s))
    200 	u->endfile = AT_ENDFILE;
    201     }
    202 }
    203 
    204 
    205 /* Change the modes of a file, those that are allowed * to be
    206    changed.  */
    207 
    208 static void
    209 edit_modes (st_parameter_open *opp, gfc_unit *u, unit_flags *flags)
    210 {
    211   /* Complain about attempts to change the unchangeable.  */
    212 
    213   if (flags->status != STATUS_UNSPECIFIED && flags->status != STATUS_OLD &&
    214       u->flags.status != flags->status)
    215     generate_error (&opp->common, LIBERROR_BAD_OPTION,
    216 		    "Cannot change STATUS parameter in OPEN statement");
    217 
    218   if (flags->access != ACCESS_UNSPECIFIED && u->flags.access != flags->access)
    219     generate_error (&opp->common, LIBERROR_BAD_OPTION,
    220 		    "Cannot change ACCESS parameter in OPEN statement");
    221 
    222   if (flags->form != FORM_UNSPECIFIED && u->flags.form != flags->form)
    223     generate_error (&opp->common, LIBERROR_BAD_OPTION,
    224 		    "Cannot change FORM parameter in OPEN statement");
    225 
    226   if ((opp->common.flags & IOPARM_OPEN_HAS_RECL_IN)
    227       && opp->recl_in != u->recl)
    228     generate_error (&opp->common, LIBERROR_BAD_OPTION,
    229 		    "Cannot change RECL parameter in OPEN statement");
    230 
    231   if (flags->action != ACTION_UNSPECIFIED && u->flags.action != flags->action)
    232     generate_error (&opp->common, LIBERROR_BAD_OPTION,
    233 		    "Cannot change ACTION parameter in OPEN statement");
    234 
    235   if (flags->share != SHARE_UNSPECIFIED && u->flags.share != flags->share)
    236     generate_error (&opp->common, LIBERROR_BAD_OPTION,
    237 		    "Cannot change SHARE parameter in OPEN statement");
    238 
    239   if (flags->cc != CC_UNSPECIFIED && u->flags.cc != flags->cc)
    240     generate_error (&opp->common, LIBERROR_BAD_OPTION,
    241 		  "Cannot change CARRIAGECONTROL parameter in OPEN statement");
    242 
    243   /* Status must be OLD if present.  */
    244 
    245   if (flags->status != STATUS_UNSPECIFIED && flags->status != STATUS_OLD &&
    246       flags->status != STATUS_UNKNOWN)
    247     {
    248       if (flags->status == STATUS_SCRATCH)
    249 	notify_std (&opp->common, GFC_STD_GNU,
    250 		    "OPEN statement must have a STATUS of OLD or UNKNOWN");
    251       else
    252 	generate_error (&opp->common, LIBERROR_BAD_OPTION,
    253 		    "OPEN statement must have a STATUS of OLD or UNKNOWN");
    254     }
    255 
    256   if (u->flags.form == FORM_UNFORMATTED)
    257     {
    258       if (flags->delim != DELIM_UNSPECIFIED)
    259 	generate_error (&opp->common, LIBERROR_OPTION_CONFLICT,
    260 			"DELIM parameter conflicts with UNFORMATTED form in "
    261 			"OPEN statement");
    262 
    263       if (flags->blank != BLANK_UNSPECIFIED)
    264 	generate_error (&opp->common, LIBERROR_OPTION_CONFLICT,
    265 			"BLANK parameter conflicts with UNFORMATTED form in "
    266 			"OPEN statement");
    267 
    268       if (flags->pad != PAD_UNSPECIFIED)
    269 	generate_error (&opp->common, LIBERROR_OPTION_CONFLICT,
    270 			"PAD parameter conflicts with UNFORMATTED form in "
    271 			"OPEN statement");
    272 
    273       if (flags->decimal != DECIMAL_UNSPECIFIED)
    274 	generate_error (&opp->common, LIBERROR_OPTION_CONFLICT,
    275 			"DECIMAL parameter conflicts with UNFORMATTED form in "
    276 			"OPEN statement");
    277 
    278       if (flags->encoding != ENCODING_UNSPECIFIED)
    279 	generate_error (&opp->common, LIBERROR_OPTION_CONFLICT,
    280 			"ENCODING parameter conflicts with UNFORMATTED form in "
    281 			"OPEN statement");
    282 
    283       if (flags->round != ROUND_UNSPECIFIED)
    284 	generate_error (&opp->common, LIBERROR_OPTION_CONFLICT,
    285 			"ROUND parameter conflicts with UNFORMATTED form in "
    286 			"OPEN statement");
    287 
    288       if (flags->sign != SIGN_UNSPECIFIED)
    289 	generate_error (&opp->common, LIBERROR_OPTION_CONFLICT,
    290 			"SIGN parameter conflicts with UNFORMATTED form in "
    291 			"OPEN statement");
    292     }
    293 
    294   if ((opp->common.flags & IOPARM_LIBRETURN_MASK) == IOPARM_LIBRETURN_OK)
    295     {
    296       /* Change the changeable:  */
    297       if (flags->blank != BLANK_UNSPECIFIED)
    298 	u->flags.blank = flags->blank;
    299       if (flags->delim != DELIM_UNSPECIFIED)
    300 	u->flags.delim = flags->delim;
    301       if (flags->pad != PAD_UNSPECIFIED)
    302 	u->flags.pad = flags->pad;
    303       if (flags->decimal != DECIMAL_UNSPECIFIED)
    304 	u->flags.decimal = flags->decimal;
    305       if (flags->encoding != ENCODING_UNSPECIFIED)
    306 	u->flags.encoding = flags->encoding;
    307       if (flags->async != ASYNC_UNSPECIFIED)
    308 	u->flags.async = flags->async;
    309       if (flags->round != ROUND_UNSPECIFIED)
    310 	u->flags.round = flags->round;
    311       if (flags->sign != SIGN_UNSPECIFIED)
    312 	u->flags.sign = flags->sign;
    313 
    314       /* Reposition the file if necessary.  */
    315 
    316       switch (flags->position)
    317 	{
    318 	case POSITION_UNSPECIFIED:
    319 	case POSITION_ASIS:
    320 	  break;
    321 
    322 	case POSITION_REWIND:
    323 	  if (sseek (u->s, 0, SEEK_SET) != 0)
    324 	    goto seek_error;
    325 
    326 	  u->current_record = 0;
    327 	  u->last_record = 0;
    328 
    329 	  test_endfile (u);
    330 	  break;
    331 
    332 	case POSITION_APPEND:
    333 	  if (sseek (u->s, 0, SEEK_END) < 0)
    334 	    goto seek_error;
    335 
    336 	  if (flags->access != ACCESS_STREAM)
    337 	    u->current_record = 0;
    338 
    339 	  u->endfile = AT_ENDFILE;	/* We are at the end.  */
    340 	  break;
    341 
    342 	seek_error:
    343 	  generate_error (&opp->common, LIBERROR_OS, NULL);
    344 	  break;
    345 	}
    346     }
    347 
    348   unlock_unit (u);
    349 }
    350 
    351 
    352 /* Open an unused unit.  */
    353 
    354 gfc_unit *
    355 new_unit (st_parameter_open *opp, gfc_unit *u, unit_flags *flags)
    356 {
    357   gfc_unit *u2;
    358   stream *s;
    359   char tmpname[5 /* fort. */ + 10 /* digits of unit number */ + 1 /* 0 */];
    360 
    361   /* Change unspecifieds to defaults.  Leave (flags->action ==
    362      ACTION_UNSPECIFIED) alone so open_external() can set it based on
    363      what type of open actually works.  */
    364 
    365   if (flags->access == ACCESS_UNSPECIFIED)
    366     flags->access = ACCESS_SEQUENTIAL;
    367 
    368   if (flags->form == FORM_UNSPECIFIED)
    369     flags->form = (flags->access == ACCESS_SEQUENTIAL)
    370       ? FORM_FORMATTED : FORM_UNFORMATTED;
    371 
    372   if (flags->async == ASYNC_UNSPECIFIED)
    373     flags->async = ASYNC_NO;
    374 
    375   if (flags->status == STATUS_UNSPECIFIED)
    376     flags->status = STATUS_UNKNOWN;
    377 
    378   if (flags->cc == CC_UNSPECIFIED)
    379     flags->cc = flags->form == FORM_UNFORMATTED ? CC_NONE : CC_LIST;
    380   else if (flags->form == FORM_UNFORMATTED && flags->cc != CC_NONE)
    381     {
    382       generate_error (&opp->common, LIBERROR_OPTION_CONFLICT,
    383 	  "CARRIAGECONTROL parameter conflicts with UNFORMATTED form in "
    384 	  "OPEN statement");
    385       goto fail;
    386     }
    387 
    388   /* Checks.  */
    389 
    390   if (flags->delim != DELIM_UNSPECIFIED
    391       && flags->form == FORM_UNFORMATTED)
    392     {
    393       generate_error (&opp->common, LIBERROR_OPTION_CONFLICT,
    394 		      "DELIM parameter conflicts with UNFORMATTED form in "
    395 		      "OPEN statement");
    396       goto fail;
    397     }
    398 
    399   if (flags->blank == BLANK_UNSPECIFIED)
    400     flags->blank = BLANK_NULL;
    401   else
    402     {
    403       if (flags->form == FORM_UNFORMATTED)
    404 	{
    405 	  generate_error (&opp->common, LIBERROR_OPTION_CONFLICT,
    406 			  "BLANK parameter conflicts with UNFORMATTED form in "
    407 			  "OPEN statement");
    408 	  goto fail;
    409 	}
    410     }
    411 
    412   if (flags->pad == PAD_UNSPECIFIED)
    413     flags->pad = PAD_YES;
    414   else
    415     {
    416       if (flags->form == FORM_UNFORMATTED)
    417 	{
    418 	  generate_error (&opp->common, LIBERROR_OPTION_CONFLICT,
    419 			  "PAD parameter conflicts with UNFORMATTED form in "
    420 			  "OPEN statement");
    421 	  goto fail;
    422 	}
    423     }
    424 
    425   if (flags->decimal == DECIMAL_UNSPECIFIED)
    426     flags->decimal = DECIMAL_POINT;
    427   else
    428     {
    429       if (flags->form == FORM_UNFORMATTED)
    430 	{
    431 	  generate_error (&opp->common, LIBERROR_OPTION_CONFLICT,
    432 			  "DECIMAL parameter conflicts with UNFORMATTED form "
    433 			  "in OPEN statement");
    434 	  goto fail;
    435 	}
    436     }
    437 
    438   if (flags->encoding == ENCODING_UNSPECIFIED)
    439     flags->encoding = ENCODING_DEFAULT;
    440   else
    441     {
    442       if (flags->form == FORM_UNFORMATTED)
    443 	{
    444 	  generate_error (&opp->common, LIBERROR_OPTION_CONFLICT,
    445 			  "ENCODING parameter conflicts with UNFORMATTED form in "
    446 			  "OPEN statement");
    447 	  goto fail;
    448 	}
    449     }
    450 
    451   /* NB: the value for ROUND when it's not specified by the user does not
    452          have to be PROCESSOR_DEFINED; the standard says that it is
    453 	 processor dependent, and requires that it is one of the
    454 	 possible value (see F2003, 9.4.5.13).  */
    455   if (flags->round == ROUND_UNSPECIFIED)
    456     flags->round = ROUND_PROCDEFINED;
    457   else
    458     {
    459       if (flags->form == FORM_UNFORMATTED)
    460 	{
    461 	  generate_error (&opp->common, LIBERROR_OPTION_CONFLICT,
    462 			  "ROUND parameter conflicts with UNFORMATTED form in "
    463 			  "OPEN statement");
    464 	  goto fail;
    465 	}
    466     }
    467 
    468   if (flags->sign == SIGN_UNSPECIFIED)
    469     flags->sign = SIGN_PROCDEFINED;
    470   else
    471     {
    472       if (flags->form == FORM_UNFORMATTED)
    473 	{
    474 	  generate_error (&opp->common, LIBERROR_OPTION_CONFLICT,
    475 			  "SIGN parameter conflicts with UNFORMATTED form in "
    476 			  "OPEN statement");
    477 	  goto fail;
    478 	}
    479     }
    480 
    481   if (flags->position != POSITION_ASIS && flags->access == ACCESS_DIRECT)
    482    {
    483      generate_error (&opp->common, LIBERROR_OPTION_CONFLICT,
    484                      "ACCESS parameter conflicts with SEQUENTIAL access in "
    485                      "OPEN statement");
    486      goto fail;
    487    }
    488   else
    489    if (flags->position == POSITION_UNSPECIFIED)
    490      flags->position = POSITION_ASIS;
    491 
    492   if (flags->access == ACCESS_DIRECT
    493       && (opp->common.flags & IOPARM_OPEN_HAS_RECL_IN) == 0)
    494     {
    495       generate_error (&opp->common, LIBERROR_MISSING_OPTION,
    496 		      "Missing RECL parameter in OPEN statement");
    497       goto fail;
    498     }
    499 
    500   if ((opp->common.flags & IOPARM_OPEN_HAS_RECL_IN) && opp->recl_in <= 0)
    501     {
    502       generate_error (&opp->common, LIBERROR_BAD_OPTION,
    503 		      "RECL parameter is non-positive in OPEN statement");
    504       goto fail;
    505     }
    506 
    507   switch (flags->status)
    508     {
    509     case STATUS_SCRATCH:
    510       if ((opp->common.flags & IOPARM_OPEN_HAS_FILE) == 0)
    511 	{
    512 	  opp->file = NULL;
    513 	  break;
    514 	}
    515 
    516       generate_error (&opp->common, LIBERROR_BAD_OPTION,
    517 		      "FILE parameter must not be present in OPEN statement");
    518       goto fail;
    519 
    520     case STATUS_OLD:
    521     case STATUS_NEW:
    522     case STATUS_REPLACE:
    523     case STATUS_UNKNOWN:
    524       if ((opp->common.flags & IOPARM_OPEN_HAS_FILE))
    525 	break;
    526 
    527       opp->file = tmpname;
    528       opp->file_len = snprintf(opp->file, sizeof (tmpname), "fort.%d",
    529 			       (int) opp->common.unit);
    530       break;
    531 
    532     default:
    533       internal_error (&opp->common, "new_unit(): Bad status");
    534     }
    535 
    536   /* Make sure the file isn't already open someplace else.
    537      Do not error if opening file preconnected to stdin, stdout, stderr.  */
    538 
    539   u2 = NULL;
    540   if ((opp->common.flags & IOPARM_OPEN_HAS_FILE) != 0
    541       && !(compile_options.allow_std & GFC_STD_F2018))
    542     u2 = find_file (opp->file, opp->file_len);
    543   if (u2 != NULL
    544       && (options.stdin_unit < 0 || u2->unit_number != options.stdin_unit)
    545       && (options.stdout_unit < 0 || u2->unit_number != options.stdout_unit)
    546       && (options.stderr_unit < 0 || u2->unit_number != options.stderr_unit))
    547     {
    548       unlock_unit (u2);
    549       generate_error (&opp->common, LIBERROR_ALREADY_OPEN, NULL);
    550       goto cleanup;
    551     }
    552 
    553   if (u2 != NULL)
    554     unlock_unit (u2);
    555 
    556   /* If the unit specified is preconnected with a file specified to be open,
    557      then clear the format buffer.  */
    558   if ((opp->common.unit == options.stdin_unit ||
    559        opp->common.unit == options.stdout_unit ||
    560        opp->common.unit == options.stderr_unit)
    561       && (opp->common.flags & IOPARM_OPEN_HAS_FILE) != 0)
    562     fbuf_destroy (u);
    563 
    564   /* Open file.  */
    565 
    566   s = open_external (opp, flags);
    567   if (s == NULL)
    568     {
    569       char errbuf[256];
    570       char *path = fc_strdup (opp->file, opp->file_len);
    571       size_t msglen = opp->file_len + 22 + sizeof (errbuf);
    572       char *msg = xmalloc (msglen);
    573       snprintf (msg, msglen, "Cannot open file '%s': %s", path,
    574 		gf_strerror (errno, errbuf, sizeof (errbuf)));
    575       generate_error (&opp->common, LIBERROR_OS, msg);
    576       free (msg);
    577       free (path);
    578       goto cleanup;
    579     }
    580 
    581   if (flags->status == STATUS_NEW || flags->status == STATUS_REPLACE)
    582     flags->status = STATUS_OLD;
    583 
    584   /* Create the unit structure.  */
    585 
    586   if (u->unit_number != opp->common.unit)
    587     internal_error (&opp->common, "Unit number changed");
    588   u->s = s;
    589   u->flags = *flags;
    590   u->read_bad = 0;
    591   u->endfile = NO_ENDFILE;
    592   u->last_record = 0;
    593   u->current_record = 0;
    594   u->mode = READING;
    595   u->maxrec = 0;
    596   u->bytes_left = 0;
    597   u->saved_pos = 0;
    598 
    599   if (flags->position == POSITION_APPEND)
    600     {
    601       if (sseek (u->s, 0, SEEK_END) < 0)
    602 	{
    603 	  generate_error (&opp->common, LIBERROR_OS, NULL);
    604 	  goto cleanup;
    605 	}
    606       u->endfile = AT_ENDFILE;
    607     }
    608 
    609   /* Unspecified recl ends up with a processor dependent value.  */
    610 
    611   if ((opp->common.flags & IOPARM_OPEN_HAS_RECL_IN))
    612     {
    613       u->flags.has_recl = 1;
    614       u->recl = opp->recl_in;
    615       u->recl_subrecord = u->recl;
    616       u->bytes_left = u->recl;
    617     }
    618   else
    619     {
    620       u->flags.has_recl = 0;
    621       u->recl = default_recl;
    622       if (compile_options.max_subrecord_length)
    623 	{
    624 	  u->recl_subrecord = compile_options.max_subrecord_length;
    625 	}
    626       else
    627 	{
    628 	  switch (compile_options.record_marker)
    629 	    {
    630 	    case 0:
    631 	      /* Fall through */
    632 	    case sizeof (GFC_INTEGER_4):
    633 	      u->recl_subrecord = GFC_MAX_SUBRECORD_LENGTH;
    634 	      break;
    635 
    636 	    case sizeof (GFC_INTEGER_8):
    637 	      u->recl_subrecord = max_offset - 16;
    638 	      break;
    639 
    640 	    default:
    641 	      runtime_error ("Illegal value for record marker");
    642 	      break;
    643 	    }
    644 	}
    645     }
    646 
    647   /* If the file is direct access, calculate the maximum record number
    648      via a division now instead of letting the multiplication overflow
    649      later.  */
    650 
    651   if (flags->access == ACCESS_DIRECT)
    652     u->maxrec = max_offset / u->recl;
    653 
    654   if (flags->access == ACCESS_STREAM)
    655     {
    656       u->maxrec = max_offset;
    657       /* F2018 (N2137) 12.10.2.26: If the connection is for stream
    658 	 access recl is assigned the value -2.  */
    659       u->recl = -2;
    660       u->bytes_left = 1;
    661       u->strm_pos = stell (u->s) + 1;
    662     }
    663 
    664   u->filename = fc_strdup (opp->file, opp->file_len);
    665 
    666   /* Curiously, the standard requires that the
    667      position specifier be ignored for new files so a newly connected
    668      file starts out at the initial point.  We still need to figure
    669      out if the file is at the end or not.  */
    670 
    671   test_endfile (u);
    672 
    673   if (flags->status == STATUS_SCRATCH && opp->file != NULL)
    674     free (opp->file);
    675 
    676   if (flags->form == FORM_FORMATTED)
    677     {
    678       if ((opp->common.flags & IOPARM_OPEN_HAS_RECL_IN))
    679         fbuf_init (u, u->recl);
    680       else
    681         fbuf_init (u, 0);
    682     }
    683   else
    684     u->fbuf = NULL;
    685 
    686   /* Check if asynchrounous.  */
    687   if (flags->async == ASYNC_YES)
    688     init_async_unit (u);
    689   else
    690     u->au = NULL;
    691 
    692   return u;
    693 
    694  cleanup:
    695 
    696   /* Free memory associated with a temporary filename.  */
    697 
    698   if (flags->status == STATUS_SCRATCH && opp->file != NULL)
    699     free (opp->file);
    700 
    701  fail:
    702 
    703   close_unit (u);
    704   return NULL;
    705 }
    706 
    707 
    708 /* Open a unit which is already open.  This involves changing the
    709    modes or closing what is there now and opening the new file.  */
    710 
    711 static void
    712 already_open (st_parameter_open *opp, gfc_unit *u, unit_flags *flags)
    713 {
    714   if ((opp->common.flags & IOPARM_OPEN_HAS_FILE) == 0)
    715     {
    716       edit_modes (opp, u, flags);
    717       return;
    718     }
    719 
    720   /* If the file is connected to something else, close it and open a
    721      new unit.  */
    722 
    723   if (!compare_file_filename (u, opp->file, opp->file_len))
    724     {
    725       if (sclose (u->s) == -1)
    726 	{
    727 	  unlock_unit (u);
    728 	  generate_error (&opp->common, LIBERROR_OS,
    729 			  "Error closing file in OPEN statement");
    730 	  return;
    731 	}
    732 
    733       u->s = NULL;
    734 
    735 #if !HAVE_UNLINK_OPEN_FILE
    736       if (u->filename && u->flags.status == STATUS_SCRATCH)
    737 	remove (u->filename);
    738 #endif
    739       free (u->filename);
    740       u->filename = NULL;
    741 
    742       u = new_unit (opp, u, flags);
    743       if (u != NULL)
    744       unlock_unit (u);
    745       return;
    746     }
    747 
    748   edit_modes (opp, u, flags);
    749 }
    750 
    751 
    752 /* Open file.  */
    753 
    754 extern void st_open (st_parameter_open *opp);
    755 export_proto(st_open);
    756 
    757 void
    758 st_open (st_parameter_open *opp)
    759 {
    760   unit_flags flags;
    761   gfc_unit *u = NULL;
    762   GFC_INTEGER_4 cf = opp->common.flags;
    763   unit_convert conv;
    764 
    765   library_start (&opp->common);
    766 
    767   /* Decode options.  */
    768   flags.readonly = !(cf & IOPARM_OPEN_HAS_READONLY) ? 0 : opp->readonly;
    769 
    770   flags.access = !(cf & IOPARM_OPEN_HAS_ACCESS) ? ACCESS_UNSPECIFIED :
    771     find_option (&opp->common, opp->access, opp->access_len,
    772 		 access_opt, "Bad ACCESS parameter in OPEN statement");
    773 
    774   flags.action = !(cf & IOPARM_OPEN_HAS_ACTION) ? ACTION_UNSPECIFIED :
    775     find_option (&opp->common, opp->action, opp->action_len,
    776 		 action_opt, "Bad ACTION parameter in OPEN statement");
    777 
    778   flags.cc = !(cf & IOPARM_OPEN_HAS_CC) ? CC_UNSPECIFIED :
    779     find_option (&opp->common, opp->cc, opp->cc_len,
    780 		 cc_opt, "Bad CARRIAGECONTROL parameter in OPEN statement");
    781 
    782   flags.share = !(cf & IOPARM_OPEN_HAS_SHARE) ? SHARE_UNSPECIFIED :
    783     find_option (&opp->common, opp->share, opp->share_len,
    784 		 share_opt, "Bad SHARE parameter in OPEN statement");
    785 
    786   flags.blank = !(cf & IOPARM_OPEN_HAS_BLANK) ? BLANK_UNSPECIFIED :
    787     find_option (&opp->common, opp->blank, opp->blank_len,
    788 		 blank_opt, "Bad BLANK parameter in OPEN statement");
    789 
    790   flags.delim = !(cf & IOPARM_OPEN_HAS_DELIM) ? DELIM_UNSPECIFIED :
    791     find_option (&opp->common, opp->delim, opp->delim_len,
    792 		 delim_opt, "Bad DELIM parameter in OPEN statement");
    793 
    794   flags.pad = !(cf & IOPARM_OPEN_HAS_PAD) ? PAD_UNSPECIFIED :
    795     find_option (&opp->common, opp->pad, opp->pad_len,
    796 		 pad_opt, "Bad PAD parameter in OPEN statement");
    797 
    798   flags.decimal = !(cf & IOPARM_OPEN_HAS_DECIMAL) ? DECIMAL_UNSPECIFIED :
    799     find_option (&opp->common, opp->decimal, opp->decimal_len,
    800 		 decimal_opt, "Bad DECIMAL parameter in OPEN statement");
    801 
    802   flags.encoding = !(cf & IOPARM_OPEN_HAS_ENCODING) ? ENCODING_UNSPECIFIED :
    803     find_option (&opp->common, opp->encoding, opp->encoding_len,
    804 		 encoding_opt, "Bad ENCODING parameter in OPEN statement");
    805 
    806   flags.async = !(cf & IOPARM_OPEN_HAS_ASYNCHRONOUS) ? ASYNC_UNSPECIFIED :
    807     find_option (&opp->common, opp->asynchronous, opp->asynchronous_len,
    808 		 async_opt, "Bad ASYNCHRONOUS parameter in OPEN statement");
    809 
    810   flags.round = !(cf & IOPARM_OPEN_HAS_ROUND) ? ROUND_UNSPECIFIED :
    811     find_option (&opp->common, opp->round, opp->round_len,
    812 		 round_opt, "Bad ROUND parameter in OPEN statement");
    813 
    814   flags.sign = !(cf & IOPARM_OPEN_HAS_SIGN) ? SIGN_UNSPECIFIED :
    815     find_option (&opp->common, opp->sign, opp->sign_len,
    816 		 sign_opt, "Bad SIGN parameter in OPEN statement");
    817 
    818   flags.form = !(cf & IOPARM_OPEN_HAS_FORM) ? FORM_UNSPECIFIED :
    819     find_option (&opp->common, opp->form, opp->form_len,
    820 		 form_opt, "Bad FORM parameter in OPEN statement");
    821 
    822   flags.position = !(cf & IOPARM_OPEN_HAS_POSITION) ? POSITION_UNSPECIFIED :
    823     find_option (&opp->common, opp->position, opp->position_len,
    824 		 position_opt, "Bad POSITION parameter in OPEN statement");
    825 
    826   flags.status = !(cf & IOPARM_OPEN_HAS_STATUS) ? STATUS_UNSPECIFIED :
    827     find_option (&opp->common, opp->status, opp->status_len,
    828 		 status_opt, "Bad STATUS parameter in OPEN statement");
    829 
    830   /* First, we check wether the convert flag has been set via environment
    831      variable.  This overrides the convert tag in the open statement.  */
    832 
    833   conv = get_unformatted_convert (opp->common.unit);
    834 
    835   if (conv == GFC_CONVERT_NONE)
    836     {
    837       /* Nothing has been set by environment variable, check the convert tag.  */
    838       if (cf & IOPARM_OPEN_HAS_CONVERT)
    839 	conv = find_option (&opp->common, opp->convert, opp->convert_len,
    840 			    convert_opt,
    841 			    "Bad CONVERT parameter in OPEN statement");
    842       else
    843 	conv = compile_options.convert;
    844     }
    845 
    846   flags.convert = 0;
    847 
    848 #ifdef HAVE_GFC_REAL_17
    849   flags.convert = conv & (GFC_CONVERT_R16_IEEE | GFC_CONVERT_R16_IBM);
    850   conv &= ~(GFC_CONVERT_R16_IEEE | GFC_CONVERT_R16_IBM);
    851 #endif
    852 
    853   switch (conv)
    854     {
    855     case GFC_CONVERT_NATIVE:
    856     case GFC_CONVERT_SWAP:
    857       break;
    858 
    859     case GFC_CONVERT_BIG:
    860       conv = __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ ? GFC_CONVERT_NATIVE : GFC_CONVERT_SWAP;
    861       break;
    862 
    863     case GFC_CONVERT_LITTLE:
    864       conv = __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ ? GFC_CONVERT_SWAP : GFC_CONVERT_NATIVE;
    865       break;
    866 
    867     default:
    868       internal_error (&opp->common, "Illegal value for CONVERT");
    869       break;
    870     }
    871 
    872   flags.convert |= conv;
    873 
    874   if (flags.position != POSITION_UNSPECIFIED
    875       && flags.access == ACCESS_DIRECT)
    876     generate_error (&opp->common, LIBERROR_BAD_OPTION,
    877 		    "Cannot use POSITION with direct access files");
    878 
    879   if (flags.readonly
    880       && flags.action != ACTION_UNSPECIFIED && flags.action != ACTION_READ)
    881     generate_error (&opp->common, LIBERROR_BAD_OPTION,
    882 		    "ACTION conflicts with READONLY in OPEN statement");
    883 
    884   if (flags.access == ACCESS_APPEND)
    885     {
    886       if (flags.position != POSITION_UNSPECIFIED
    887 	  && flags.position != POSITION_APPEND)
    888 	generate_error (&opp->common, LIBERROR_BAD_OPTION,
    889 			"Conflicting ACCESS and POSITION flags in"
    890 			" OPEN statement");
    891 
    892       notify_std (&opp->common, GFC_STD_GNU,
    893 		  "Extension: APPEND as a value for ACCESS in OPEN statement");
    894       flags.access = ACCESS_SEQUENTIAL;
    895       flags.position = POSITION_APPEND;
    896     }
    897 
    898   if (flags.position == POSITION_UNSPECIFIED)
    899     flags.position = POSITION_ASIS;
    900 
    901   if ((opp->common.flags & IOPARM_LIBRETURN_MASK) == IOPARM_LIBRETURN_OK)
    902     {
    903       if ((opp->common.flags & IOPARM_OPEN_HAS_NEWUNIT))
    904 	opp->common.unit = newunit_alloc ();
    905       else if (opp->common.unit < 0)
    906 	{
    907 	  u = find_unit (opp->common.unit);
    908 	  if (u == NULL) /* Negative unit and no NEWUNIT-created unit found.  */
    909 	    {
    910 	      generate_error (&opp->common, LIBERROR_BAD_OPTION,
    911 			      "Bad unit number in OPEN statement");
    912 	      library_end ();
    913 	      return;
    914 	    }
    915 	}
    916 
    917       if (u == NULL)
    918 	u = find_or_create_unit (opp->common.unit);
    919       if (u->s == NULL)
    920 	{
    921 	  u = new_unit (opp, u, &flags);
    922 	  if (u != NULL)
    923 	    unlock_unit (u);
    924 	}
    925       else
    926 	already_open (opp, u, &flags);
    927     }
    928 
    929   if ((opp->common.flags & IOPARM_OPEN_HAS_NEWUNIT)
    930       && (opp->common.flags & IOPARM_LIBRETURN_MASK) == IOPARM_LIBRETURN_OK)
    931     *opp->newunit = opp->common.unit;
    932 
    933   library_end ();
    934 }
    935