Anecdotal Fortran... :-)

I have one that compiles up with fpm on Linux; totally unsupported but it is all free-format as well and has been mechanically modernized so there is a lot to do but if you use fpm you should be able to compile it up as a module (accept for five remaining procedures) in just a few minutes.

I plan on removing that in a few days unless somone wants to adopt it and continue development. Making it this far was instructive but I do not have the time and resources at the moment to do much else with it; but maybe it will be useful to someone else

Scientific-Subroutine-Package

1 Like

Thanks. I started conversion to free-format last night of @Beliavsky’s code with my own refactoring tools. There are some interesting “features” in the code that I’ve not seen before. For instance, several of the source files have some continuations where the line being continued also has a character (always 0) in column 6. There is also frequent use of assigning 10E75 to a variable to indicate an error condition. ifort complains about this being a potential overflow. I assume this was a legal value on old 360s.

If you or @rwmsu could upload the scripts you used to modernize the code, that could be useful in dealing with other old codes. All I did to get more files to compile was use strsub, a tool that used to be distributed with Compaq Visual Fortran, to substitute huge(1.0) for large numeric literals such as 1.E75.

Strsub is convenient but is not distributed with Intel Fortran. It could probably be replicated with a mix of Unix tools. To demonstrate its options,

strsub /?

gives

  STRSUB - Version: V1.16   Built: May 16 1996  11:04:04

  Usage is: STRSUB [-sw -sw] infile findstr repstr

  Switches are:
    c   Do string compare without regard for case
    t   Truncate lines if all param strings are found
    r   Remove beginning of line if all param strings are found
    l   Toggle display of operation logging information
    w   Display per line operations
    p   Swap '#' with '%' in find and replace strings
    b   Swap '!' with '\' in find and replace strings
    a   Swap '@' with '"' in find and replace strings
    z   Remove extra nulls from file
    d   Don't save .BAK file
    q   Suppress all console output
    n   NOP mode - don't actually do anything
    s   Iterate over subdirectories
    v   Display program version number
   ?/h  Display this help message

I’m in the process of rewriting my conversion program. My interest in the SSP code was as a test case. The SSP source pointed out a fault in my current logic that caused a seg fault. This was tracked to the SSP files that had continuations in which the first continued line had a character in column six (see for example the first SUBROUTINE statement at line 302 in DLBVP.FOR). I’m somewhat intrigued that this was valid or at least accepted in IBM Fortran way back when. Give me a month or so to redo my code and I’ll gladly post it on Github. Note it just does the basics (change case and continuations etc) so if you want something that also removes GO TOs, arithmetic IFs etc. there are much better tools like fpt for that.

Edit. I also have a bash script that I use that calls sed to execute the sed commands in a user supplied file for all the files with a give file extension in a directory. This is what I will use to change the 1E75 values to HUGE(1).

1 Like

The zero in column 6 was allowed via an interpretation from the Fortran Committee in the early 1970s, and treated as a blank. Then was disallowed again when Fortran 77 was published. I had a copy of that interpretation report way back when and played with it a bit. But a number of compilers objected, so I never tried it again.

I put together a small script to do some transformations on the files too. Namely, renaming them, getting rid of the double newlines, getting rid of the control/Z at the end of each file, getting rid of sequence numbers/trailing blanks, and replacing the huge values with huge(). It also calls the f2f program to convert to free form and modernize some DO loops. Here it is:

#!/bin/bash

for filename in `ls [A-Z]*.FOR | cut -d. -f1`
do
  filename_lc=`echo $filename | tr [:upper:] [:lower:]`
  echo converting: $filename.FOR to $filename_lc.f
  rm -f $filename_lc.f
  tr -d -s '\032' '\n' < $filename.FOR | cut -c 1-72 | \
    sed -e 's/[[:space:]]*$//' \
        -e 's/1.E75/huge (1.0)/'  -e 's/1.0E75/huge (1.0)/' \
        -e 's/1.E+75/huge (1.0)/' -e 's/1.0E70/huge (1.0)/' \
        -e 's/\.99999E+74/huge (1.0)/' -e 's/\.999999E+74/huge (1.0)/' \
         > $filename_lc.f

  ~/computer/fortran/f2f/f2f_wws/f2f.pl $filename_lc.f $filename_lc.f90

  echo compiling $filename_lc.f90
  gfortran -c $filename_lc.f90
  if [ $? -ne 0 ]
  then
    exit 1
  fi
done

The f2f program doesn’t know about ‘0’ in column 6, so I fixed those by hand. (Should be an easy fix. I’ve been into f2f to fix a couple other bugs as well.) There are also a couple of places where significant blanks are an issue (e.g., splitting across a continued line) - which I also fixed by hand.

Unfortunately literally every IF statement is an arithmetic IF. The f2f program doesn’t even begin to attempt to do anything with them.

1 Like

Here is my sedmod script.
script sedmod

#!/bin/sh

# sedmod - A sed editor script

# Version 1 - Jan. 11, 2016 - initial version with if blocks
# Version 2 - Jan. 11, 2016 - replaced if blocks with case statmements

# by Richard Weed

# initialize values to false

SEDCMDFILE=false
FILEEXT=false
SEDCMD=false
NEWDIR=false
IFILE=false
OFILE=false
USESTDOUT=true
SEDNOPT=false

CMDTYPE=false
IFTYPE=false
OTYPE=stdout
# get current directory

THISDIR=`pwd`
export PATH=$THISDIR:$PATH
OFILEPATH=$THISDIR
# process command line options

while getopts he:d:f:i:o:x: OPTION
do
  case $OPTION in
  d)NEWDIR=$OPTARG
     echo " Target directory = $NEWDIR"
     USESTDOUT=false
     ;;
  e)SEDCMD=$OPTARG
     echo " Edit command = $SEDCMD"
     CMDTYPE=cmd
     ;;
  f)SEDCMDFILE=$OPTARG
     echo " sed edit command file = $SEDCMDFILE"
     CMDTYPE=cmdfile
     ;;
  h)echo " "
     echo " sedmod - a sed editor script"
     echo " "
     echo " Runs either single or multiple sed editor commands on a single"
     echo " file or multiple files with a given file extension in the"
    echo " current directory. Output can go to either STDOUT, a named file"
     echo " for single file edits, a user specified directory with file names"
     echo " preserved for multiple file edits, or the current directory with"
     echo " an _new tag appended to the original file name"
     echo " "
     echo " Command line parameters and switches : "
     echo " "
     echo " -f filename  - processes edit commands using an input file (filename)"
     echo " -e 'sed command'  - processes a single sed command. Command must"
     echo "     be enclosed in single quotes"
     echo " -x file extension - processes all files in the current directory"
     echo "    with a given file extension. (ex. -x dat processes all .dat files)"
     echo " -i filename       - processes a single file with name filename"
     echo " -o filename       - Desired output filename"
     echo " -d dirname        - Path to a directory to hold multiple edited"
     echo "    files for multi file processing. The directory is created in"
     echo "    the current directory if it doesn't exist"
     echo " -h                - prints help"
     echo " "
     echo " output goes to standard output if neither -o or -x are specified"
     echo " "
     echo " Examples :"
     echo " "
     echo " sedmod -f ./mods.sed -x dat -d ./Temp"
     echo "   Runs commands in ./mods.sed on all files with a .dat extension"
     echo "   and places the edited files in ./Temp. Commands in mods.sed "
     echo "   should be placed one to a line in desired edit sequence without"
     echo "   quotes"
     echo " "
     echo " sedmod -e 's/ /, /' -x dat -d ./Temp"
     echo "   Replaces the first space in all .dat files with a comma"
     echo " "
     echo " sedmod -e 's/ /, /' -i this.dat -o that.dat"
     echo "   Replaces the first space in this.dat and writes the edited file"
     echo "   to that.dat" 
     echo " "
     echo " sedmod -e 's/ /, /' -i this.dat"
     echo "   Replaces the first space in this.dat and writes the edited file"
     echo "   to stdout" 
     echo " "
     exit 0
     ;;
  i)IFILE=$OPTARG
     echo " Single input file selected = $IFILE"
     IFTYPE=single
     ;;
  o)OFILE=$OPTARG

     echo " Desired output file = $OFILE"
     USESTDOUT=false
     OTYPE=outfile
     ;;
  x)FILEEXT=$OPTARG
     echo " Processing all files in $THISDIR with extension = $FILEEXT"
     USESTDOUT=false
     IFTYPE=multiple
     OTYPE=keepname
     ;;
 esac
done

# perform error checks

if [ "$NEWDIR" = "$THISDIR" ]; then

  echo " ERR : Can't specify output directory to be the same as current dir "
  exit 1
fi

case $IFTYPE in

  multiple)
    if [ "$FILEEXT" = "false" ]; then
      echo " "
      echo " ERR : No file extension input"
      exit 1
    fi

    if [ "$NEWDIR" = "false" ]; then
      echo " "
      echo " WARNING : Target directory for multiple file edits not specified"
      echo " files will be processed into current directory with an _new added"
      echo " to the file extension"
    else
      if [ ! -d $NEWDIR ]; then
        echo " "
        echo " ERR : Target directory doesnt exist - creating it in current dir"
        mkdir $NEWDIR > /dev/null 2>&1
      fi
      OFILEPATH=$NEWDIR
    fi
  ;;
  single)
    if [ "$IFILE" != "false" ]; then
      if [ ! -f $IFILE ]; then
        echo " "
        echo " ERR : Can't find $IFILE in $THISDIR - Check PATH "
        exit 1
      fi      if [ "$FILEEXT" != "false" ]; then
        echo " "
        echo " ERR : Can't select both single file and multi file editing - Try again"
        exit 1
      fi
    fi
  ;;
  *)
     echo " "
     echo " ERR : Neither single or multiple file processing specified - Try again"
     exit 1
  ;;
esac

case $CMDTYPE in

  cmdfile)
    if [ "$SEDCMDFILE" != "false" ]; then
      if [ ! -f $SEDCMDFILE ]; then
        echo " "
        echo " ERR : sed edit command file $SEDCMDFILE not found"
        exit 1
      fi
      if [ "$SEDCMD" != "false" ]; then
        echo " "
        echo " ERR : Can't enter both a sed command and a command file - Try again"
        exit 1
      fi
    fi
   ;;
   cmd)
     if [ "$SEDCMD" = "" ]; then
       echo " "
       echo " ERR: single edit command is blank"
       echo " "
       exit 1
     fi
   ;;
   *)
      echo " "
      echo " No edit commands specified - Try again"
      exit 1
   ;;
esac

# process single edit commands first. Check for a single file edit and
# then multifile edits. Logic jumps to multiple edit commands read from
# a user specified file if -f is specified instead f -e

case $CMDTYPE in

cmd)

  case $IFTYPE in

    single)

      case $OTYPE in

         stdout)
           sed "$SEDCMD" $IFILE
         ;;
         outfile)
           sed "$SEDCMD" $IFILE >$OFILEPATH/$OFILE
           echo " Modifying $IFILE into $OFILEPATH/$OFILE"
         ;;
         keepname)
           if [ "$NEWDIR" != "false" ]; then
             sed "$SEDCMD" $IFILE >$NEWDIR/$IFILE
             echo " Modifying $IFILE into $NEWDIR/$IFILE"
           else
             sed "$SEDCMD" $IFILE >$IFILE"_new"
             echo " Modifying $IFILE into ""$IFILE""_new"
           fi
          ;;
        esac
      ;;

    multiple)

      for f in *.$FILEEXT; do
         if [ "$NEWDIR" != "false" ]; then
           sed "$SEDCMD" $f > $NEWDIR/$f
           echo " Modifying $f into $NEWDIR/$f"
         else
           sed "$SEDCMD" $f > $f"_new"
           echo " Modifying $f into ""$f""_new"
         fi
      done
    ;;
  esac
  ;;

  cmdfile)

    case $IFTYPE in

     single)

       case $OTYPE in

       case $OTYPE in
         stdout)
           sed -f "$SEDCMDFILE" $IFILE
         ;;
         outfile)
           sed -f "$SEDCMDFILE" $IFILE >$OFILEPATH/$OFILE
           echo " Modifying $IFILE into $OFILEPATH/$OFILE"
         ;;
         keepname)
           if [ "$NEWDIR" != "false" ]; then
             sed -f "$SEDCMDFILE" $IFILE >$NEWDIR/$IFILE
             echo " Modifying $IFILE into $NEWDIR/$IFILE"
          else
            sed -f "$SEDCMDFILE" $IFILE >$IFILE"_new"
            echo " Modifying $IFILE into ""$IFILE""_new"
          fi
         ;;
       esac
     ;;

     multiple)

       for f in *.$FILEEXT; do
         if [ "$NEWDIR" != "false" ]; then
           sed -f "$SEDCMDFILE" $f > $NEWDIR/$f
           echo " Modifying $f into $NEWDIR/$f"
         else
           sed -f "$SEDCMDFILE" $f > $f"_new"
           echo " Modifying $f into ""$f""_new"
         fi
       done
     ;;
   esac
   ;;
esac

exit

A typical sed file might look like

file sedcommands.txt

s/1.0E75/HUGE(ONE)/g
s/1.E75/HUGE(ONE)/g
s/1.e75/HUGE(ONE)/g
s/1.0e75/HUGE(ONE)/g
s/Double Precision/Real(WP) :: /
s/Integer\t/Integer /
s/Real\*8\t/Real\*8 /
s/Real\*8,/Real(WP),/
s/Real\*8, Function/Real(WP) Function/
s/Real\*8 /Real(WP) :: /
s/Real /Real(WP) :: /
s/Complex\*16\t/Complex\*16 /
s/Complex\*16,/Complex(WP),/
s/Complex\*16, Function/Complex(WP) Function/
s/Complex\*16 /Complex(WP) :: /
s/Complex /Complex(WP) :: /
s/Integer\*8,/Integer(I64),/
s/Integer\*8 /Integer(I64) :: /
s/Integer ,/Integer,/
s/Integer /Integer :: /
s/Logical /Logical :: /
s/        Sub/  Sub/
s/        Fun/  Fun/
s/        End/  End/
s/        /    /
s/         Sub/  Sub/
s/         Fun/  Fun/
s/         End/  End/
s/       /    /
s/      Sub/  Sub/
s/      Fun/  Fun/
s/      End/  End/
s/      /    /
s/  EndDo/    End Do/
s/  EndIf/    End If/
s/\t/ /g
s/\d26//

These commands will do some formatting to my personal programming style (leading caps for Fortran keywords etc), change real to real(wp), remove tabs and cntrl-Z

The following will run the sedmod script for all files with a file extension of f in a directory and copy the modified files into a ./Temp dir.

chmod 750 sedmod
sedmod -f sedcommands.txt -x f -d ./Temp

I think I got the script uploaded correctly but if someone tries it and has problems let me know.

Zero in column 6 being treated as a blank has continuously been standard since F66 - it is still in F2023.

F66:
image

F77:
image

F2023
image

Amazing. I wrote my first Fortran code in 1971 and this is the first time I’ve ever heard about 0 in column six being parsed as the first line in a continuation. Until, I looked at the IBM SSP code, I have never seen it used in the several hundreds of thousands of lines of code I’ve looked at since I first started my adventures in Fortran programming. I guess you are never too old to learn.

I happened to mention this in Doctor Fortran in “Source Form Just Wants to be Free” - Doctor Fortran It made a great trivia question one time when I presented on Fortran at a Supercomputing.

1 Like

Hmm. Now I am trying to think of what was in that Interpretation…

My mistake. It was how to interpret characters in cols 1-5 when there is a (non-zero or blank) in column 6. The F77 section you quoted specifically says it is not allowed, vs F66 where it leaves the interpretation unanswered.

Likewise, although I started in 1975. This year is my Fortran golden jubilee.

1 Like

How often you see that zero in column six probably varies from organization to organization. It was a recommended practice to number the continuation lines starting with 0-9, and then A-Z, so the 0 issue has come up multiple times for us. . I see a lot of “fixed-to-free” converters that miss this;

Another common miss is that an asterisk is a valid character in column 1
to start a comment in f77, and also often missed is that blank characters at the end of a continued line are significant if occurring in a quoted string or Hollerith.

Most do not look for the common extension where lines are “dee’d” where lines beginning with a “D” are comments unless a compiler switch is used in which case the letter “D” in column 1 is ignored. This was usually used to allow for optionally compiled debug lines; particularly before cpp was commonly available.

The most obscure one was that in several F66 implementations this was OK:

        real  array(10)
        array=1.2

which would set the FIRST element of the array, as if “array(1)=1.2” had been used. Those same lines
compile as standard array syntax post f90 of course. That has come up several times because where I work we are blessed/cursed with an archive system that goes back to the very beginning of Fortran, and reviews that often call for very old code to be resurrected

. Just about any extension that existed in Fortran is in there someplace, including Digital extensions so extensive Fortran was
quite a general system programming language on those systems. It has been several years since anyone was asking what some of those old procedure calls did, what ENCODE and DECODE did and what many CDC and Cray math functions did so I finally tossed a set of old manuals. Within six days I had three questions about the old routines. Murphy’s Law apparently has no expiration date.

I think this is one of the reasons that it is commonly suggested that when you convert to f90 you put an ampersand in both column 73 of the continued line and in column six of the following line. This will in most cases but not all handle the significant blanks issue.

This is also valid in both fixed and free format

Does anyone know the reason for treating zero in column six differently from any other character? It isn’t mentioned in the original IBM 704 Fortran manual. (page 7)

1 Like


I see “a zero or a blank” in my PDF of a scan dated October 15, 1956.

2 Likes

But what was the rationale for treating zero specially? Surely there must have been some forgotten historical reason.

I guess it was so that the initial line could stand out as needing/having a continuation, for the benefit of the human reader/typist/editor.

Yes. A convention (not a requirement) since initially 9 continuation cards were allowed was to use the digits 1 through 9 consecutively as the continuation character. This leads to zero naturally being the continuation character for starting the lines, and it was useful particularly when looking card-per-card through a deck of punch cards to mark the first line continued as well as you would not inadvertently think it was a complete card and perhaps move it. So although 1 through 9 were just a convention 0 was designated as a special character. In time it became apparent using digits that commonly appear in statements was not a good choice and using $ or other characters replaced the use of digits, particularly once more than 9 continuation cards were supported (first as an extension, but then in the standard as well). A “preferred practice” was also to end a line at a point where it was obvious it was not complete when easily done, such as a + or - or * character, and to not split symbols (which was allowed in f77 since blanks between columns 7 and 72 were not significant).

So experience had shown a good continuation character is one not used in statements for any other use. There were more characters to choose from as commonly available by the time free-format was introduced, and & fit all the lessons learned. Hard to miss (a period was not found to be a good choice) not used for anything else, and commonly available on most keyboards (the original problem was not even the USA keyboards were standardized, let alone ones supporting multiple languages).

For instance, < and > were not on many keyboards, hence FORTRAN using .GT. and .LT. was a necessary solution. A lot of keyboards had 1/4 and 1/2 keys, but not ~@|{} …

1 Like

Some authors recommended that ‘$’ be used as a continuation character. “Elements of Programming Style” by Kernighan and Plauger, is one. Rationale was that it was the only character in the Fortran character set that didn’t have another syntactic meaning.

Thinking back on why I was playing with this, almost 50 years ago, was that after I read that interpretation, I was experimenting numbering continued lines something like:

      program xyzzy
      data array(12) /
     1 1.0,
     2 2.0,
     3 3.0,
(and so on)
     9 9.0,
    1O 10.0, (note letter O, not digit 0 in col 6.)
    11 11.0,
    12 12.0/

      print *, array
      end

This would be legal under that interpretation. But a number of compilers objected. So I quickly abandoned the idea. Later F77 explicitly prohibited it.