Re: How to do a seek (nonadvancing read) on a stream file?

2025-02-03 Thread Andi McClure
(This is an attempt to reply to a Mon Feb 3 19:09:38 GMT 2025 message by
Steve Kargl.)

> I believe that you are misreading C1223.  The file is
> opened with form='unformatted'.

Hm, okay. I guess it comes down to whether you "diagram" the sentence in
the spec as "An ADVANCE= specifier shall appear only in a formatted
(sequential or stream) data transfer statement"  or "An ADVANCE= specifier
shall appear only in a (formatted sequential) or (stream data transfer)
statement". If you interpret it the first way the gfortran behavior is
correct (although the error message could be improved…), if you interpret
it the second way gfortran is incorrect.

> PS: You're looking for the INQUIRE statement.

In the context of the program I was trying to write, INQUIRE can be used to
get the byte length of the file. That is not something I was trying to do.
The data was structured, and needed to be parsed to determine the
allocation size. In other words INQUIRE is the equivalent of C ftell() and
I am looking for the equivalent of C fseek(). (Which the standard appears
to only offer through nonadvancing reads… which gfortran appears to
disallow for an unformatted stream file. Unless you do the "silly" thing I
eventually did, which is to bundle your seek together with the next
advancing read.)

Thanks for the information.

On Mon, Feb 3, 2025 at 1:34 PM Andi McClure 
wrote:

> Last week, as an exercise in learning FORTRAN, I did a programming puzzle
> ( https://adventofcode.com/2024/day/4 ) with gfortran. I targeted the
> 2023 standard (*not* GNU). I saw two oddities. The first I posted about in
> this mailing list already (and it turns out there is a relevant bugzilla
> bug). The other I *think* is a bug (and I am not finding an existing
> bugzilla bug on my own), but I am less sure, so wanted to check here first
> before filing.
>
> For my solution, I needed to read in a file in stream mode, character by
> character(1). I needed to do this twice, once to find out what size array
> to load the data into and once to load the data into my newly-allocated
> array. It initially appeared stream mode would allow me to do this without
> closing and reopening the file, by "seeking" back to the beginning of the
> file using the pos= argument to READ. But when I tried to do this,
> something odd happened.
>
> Details: I open the file with
>
> ```
>
> open(10,file=path,access='stream',form='unformatted',action='read',iostat=file_error)
> ```
>
> This works; scanning to determine the input size works. Now I need to
> seek. I find this old stackoverflow answer:
> https://stackoverflow.com/a/11785795
>
> Claiming that the following code works (and implying they tested it in "
> ifort"):
>
> ```
> READ (iu,"()", ADVANCE='NO', POS=file_pos) ! iu is file unit
> ```
>
> I created a test program implementing this:
>
>
> https://github.com/mcclure/aoc2024/blob/38875a77fd48d8208ebcadfb3ec9c53fb722ef8b/04-01-wordsearch/src/puzzle.f90#L4
>
> And using a test input such as:
>
>
> https://github.com/mcclure/aoc2024/blob/38875a77fd48d8208ebcadfb3ec9c53fb722ef8b/04-01-wordsearch/data/sample-2.18.txt
>
> I can run it with `gfortran src/puzzle.f90 -std=f2023 -o program &&
> ./program data/sample-2.18.txt`. When I do this, I get a failure:
>
> ```
> At line 88 of file src/puzzle.f90 (unit = 10, file =
> 'data/sample-2.18.txt')
> Fortran runtime error: Format present for UNFORMATTED data transfer
> ```
>
> It appears it does not like the "()" format. However, imagine I edit line
> 88 to change `read(10, "()", advance='no', pos=1)` to `read(10,
> advance='no', pos=1)`, removing the format. Now I get a different error:
>
> ```
> ./program data/sample-2.18.txt
> src/puzzle.f90:88:21:
>
>88 | read(10, advance='no', pos=1)
>   | 1
> Error: the ADVANCE= specifier at (1) must appear with an explicit format
> expression
> ```
>
> So if I have a (null) format specifier the error message tells me to
> remove the format expression, and if I remove the format specifier the
> error message tells me to add a format expression. There appears to be *no
> way* of using advance='no' that will not cause gfortran to emit an error;
> even putting in a (spurious/useless) read, such as with `read(10, "(a)",
> advance='no', pos=1) char_in`, produces "Fortran runtime error: Format
> present for UNFORMATTED data transfer".
>
> I do not have a specific need for this to be fixed; I eventually got my
> program to work via a workaround I would consider "silly" ( see lin

How to do a seek (nonadvancing read) on a stream file?

2025-02-03 Thread Andi McClure
 Last week, as an exercise in learning FORTRAN, I did a programming puzzle
( https://adventofcode.com/2024/day/4 ) with gfortran. I targeted the 2023
standard (*not* GNU). I saw two oddities. The first I posted about in this
mailing list already (and it turns out there is a relevant bugzilla bug).
The other I *think* is a bug (and I am not finding an existing bugzilla bug
on my own), but I am less sure, so wanted to check here first before filing.

For my solution, I needed to read in a file in stream mode, character by
character(1). I needed to do this twice, once to find out what size array
to load the data into and once to load the data into my newly-allocated
array. It initially appeared stream mode would allow me to do this without
closing and reopening the file, by "seeking" back to the beginning of the
file using the pos= argument to READ. But when I tried to do this,
something odd happened.

Details: I open the file with

```
open(10,file=path,access='stream',form='unformatted',action='read',iostat=file_error)
```

This works; scanning to determine the input size works. Now I need to seek.
I find this old stackoverflow answer: https://stackoverflow.com/a/11785795

Claiming that the following code works (and implying they tested it in "
ifort"):

```
READ (iu,"()", ADVANCE='NO', POS=file_pos) ! iu is file unit
```

I created a test program implementing this:

https://github.com/mcclure/aoc2024/blob/38875a77fd48d8208ebcadfb3ec9c53fb722ef8b/04-01-wordsearch/src/puzzle.f90#L4

And using a test input such as:

https://github.com/mcclure/aoc2024/blob/38875a77fd48d8208ebcadfb3ec9c53fb722ef8b/04-01-wordsearch/data/sample-2.18.txt

I can run it with `gfortran src/puzzle.f90 -std=f2023 -o program &&
./program data/sample-2.18.txt`. When I do this, I get a failure:

```
At line 88 of file src/puzzle.f90 (unit = 10, file = 'data/sample-2.18.txt')
Fortran runtime error: Format present for UNFORMATTED data transfer
```

It appears it does not like the "()" format. However, imagine I edit line
88 to change `read(10, "()", advance='no', pos=1)` to `read(10,
advance='no', pos=1)`, removing the format. Now I get a different error:

```
./program data/sample-2.18.txt
src/puzzle.f90:88:21:

   88 | read(10, advance='no', pos=1)
  | 1
Error: the ADVANCE= specifier at (1) must appear with an explicit format
expression
```

So if I have a (null) format specifier the error message tells me to remove
the format expression, and if I remove the format specifier the error
message tells me to add a format expression. There appears to be *no way*
of using advance='no' that will not cause gfortran to emit an error; even
putting in a (spurious/useless) read, such as with `read(10, "(a)",
advance='no', pos=1) char_in`, produces "Fortran runtime error: Format
present for UNFORMATTED data transfer".

I do not have a specific need for this to be fixed; I eventually got my
program to work via a workaround I would consider "silly" ( see line 102:
https://github.com/mcclure/aoc2024/blob/ca77f141f33ab494dc1bb43f3d6f13e775a36303/04-01-wordsearch/src/puzzle.f90#L102
). However it seems like there is a bug here, for three reasons: because
having some method of seeking the file without simultaneously doing a read
is fundamentally desirable; because the error messages seem to give
misleading directions; and because I *think* this is a standard
nonconformance.

If I look at the current standard ( 2023 draft document:
https://j3-fortran.org/doc/year/23/23-007r1.pdf ) under the definition of
io-control-spec I see;

> C1223 (R1213) An ADVANCE= specifier shall appear only in a formatted
sequential or stream data transfer25
> statement with explicit format specification (13.2) whose
io-control-spec-list does not contain an internal-26
> file-variable as the io-unit.

This implies an io-control-spec-list with *no* "io-unit" should be allowed.
I find this same text (with different numbering) in drafts of the 2008 and
2003 standards, so it appears this has been part of FORTRAN as long as
stream mode itself.

Does anyone know of an impediment to filing this (am I doing something
wrong, is there an existing bug I was not able to find(2))?

---

(1) I understand use of stream mode is considered unusual. The problem I
was solving here was a rare case it was actually appropriate: the input
file, linked above as "test input", was a grid of ASCII art.

(2) I think I have the hang of using the mailing list now, so I expect I
will actually see the reply this time *_*


gfortran not following deferred initialization rules for get_command_argument

2025-02-02 Thread Andi McClure
This last week, as an exercise in learning FORTRAN, I attempted to complete
a programming puzzle ( https://adventofcode.com/2024/day/4 ) with gfortran.
I made a specific decision to target the 2023 standard and *not* the GNU
standard.

I believe I have found a nonconformance with gfortran 14.2.0 targeting the
2023 standard(1).

Details: I wanted my program to input data from a file, and intended it to
take the filename from the command line. I found the standard way to do
this is:

https://gcc.gnu.org/onlinedocs/gfortran/GET_005fCOMMAND_005fARGUMENT.html

Normal usage of this function involves knowing the length of the argument
you are going to input ahead of time(2). However, I had been reading
standards documents, and found this section in this 2023 "informal"
publication of the Fortran working group, "The new features of Fortran
2023" https://wg5-fortran.org/N2201-N2250/N2212.pdf , emphasis mine:

> 2.2 US 14. Automatic allocation of lengths of character variables
> When a deferred-length allocatable variable is defined by intrinsic
assignment, as in the example
>
> character(:), allocatable :: quotation
> :
> quotation = ’Now is the winter of our discontent.’
>
> it is allocated by the processor to the correct length. This behaviour is
extended to messages
> returned through iomsg and errmsg specifiers, writing to a scalar
character variable as an
> internal file, and intent out and inout scalar character arguments of
intrinsic procedures, **such
> as in call `get_command(command)`**.

This is an informal publication but I believe this should be taken to apply
to the "value" argument of GET_COMMAND_ARGUMENT, for three reasons: Because
the "new features" document explicitly calls out GET_COMMAND, and
GET_COMMAND_ARGUMENT is so similar; because the 2023 draft standard I find
https://j3-fortran.org/doc/year/23/23-007r1.pdf seems to confirm the
behavior, in 16.9.1: "When an allocatable deferred-length character scalar
corresponding to an INTENT (INOUT) or INTENT (OUT) argument is assigned a
value, the value is assigned as if by intrinsic assignment."; and because
the same draft spec states in GET_COMMAND_ARGUMENT that "value" is indeed
INTENT(OUT).

However, I do *not* see automatic allocation of the GET_COMMAND_ARGUMENT
out value in gfortran. Here is a test program:

https://github.com/mcclure/aoc2024/blob/822e460f81b944c21ca675303b868c45b22a4c2b/04-01-wordsearch/src/puzzle.f90

I run this with `gfortran src/puzzle.f90 -std=f2023 -o program && ./program
data/puzzle.txt`. This program defines a character string as in the FORTRAN
2023 "new features" document (the source says "len=:" rather than ":", but
changing this to ":" does not change the output), passes the unallocated
string to GET_COMMAND_ARGUMENT, prints the string, then attempts to open a
file by that name. When print, the string appears to be empty; a blank line
is printed, and then

> At line 20 of file src/puzzle.f90 (unit = 10)
> Fortran runtime error: Cannot open file '': No such file or directory

Here's a longer test program, with a long comment, which passes the string
to GET_COMMAND_ARGUMENT but also a variable for the "length" out parameter.
It then allocates a string of that length and passes it to
GET_COMMAND_ARGUMENT. This works (and you can confirm it is opening the
file by passing the name of a non-existent file as argument, which the
program will then create(3)):

https://github.com/mcclure/aoc2024/blob/b31be91adb5a0721f97e2ba8f145da4f36129753/04-01-wordsearch/src/puzzle.f90

Summary: It is my opinion from the FORTRAN 2023 spec that the first test
program above should have worked and the additional steps in the second
test program should not have been necessary. Have I misread the spec, is
there something wrong with my code, or have I identified a bug in gfortran?
Should I file at the GCC Bugzilla?

---

(1) If I'm not mistaken 14.2.0 dates from August 2024, so I would expect it
to be up to date. I installed gfortran via Debian Trixie and here is the
output of gfortran -v:
https://gist.githubusercontent.com/mcclure/9ccf6010b2db4b19adbed3127192c9da/raw/9fb74f44124a727a5d88730f2d69ffb4b2bca6f2/gfortran.txt

(2) The sample code in the GNU doc recommends allocating a string longer
than needed, in which excess space will be filled with blanks. This
solution was in practice adequate for my needs, but because my goal in
writing the program was to learn modern FORTRAN, I decided to try to "do it
right" if possible.

(3) The sample is minimal, so `action='read'` is not specified in the
open() call.