Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Tools

Conditional Compilation


MAY96: Conditional Compilation

David is a member of the X3J3 Fortran committee and one of the creators of F, a new language for educational and professional programming. He can be reached at [email protected].


In C, conditional compilation is a preprocessor feature that provides text substitution, macro expansion, and file inclusion. While often useful, these features can lead to a number of problems, which contributed to their being replaced or augmented in C++ (examples are const, inline functions, and type-safe linkage).

Fortunately, modern Fortran provides safe replacements for these C++ features. Fortran's PARAMETER provides named constants, PURE or internal procedures offer the speed benefits of C's macro functions without the risks, and Fortran's MODULE provides a safe way for one file to use facilities defined in another. All that's missing is the ability to choose which parts of the program will be compiled.

The Fortran 90 Conditional Compilation Facility (CCF) provides this missing functionality. CCF is a line-based language that can easily be adapted to other programming languages. CCF also is easy to implement; Listing One gives a simple Fortran CCF processor written in under 200 standard Fortran statements.

Although Listing One implements CCF as a preprocessor, this isn't mandatory. In fact, the first implementation of CCF handled conditional compilation as an integral part of the compiler. The benefits of this approach are improved speed and consistent error messages. This kind of full CCF processor would use a scanner, parser, symbol table, and expression analyzer, and most likely would not be free or readily available on new hardware.

CCF for Fortran

CCF is based on a subset of regular Fortran, which makes it easy to learn, implement, and adapt to other languages. To define a CCF for your favorite programming language, just choose a subset of that language that you'd like to use for conditional compilation, and a way to distinguish CCF statements from the regular program statements. With this in hand, you can write a simple CCF processor using the approach in Listing One.

To keep the Fortran CCF simple, I included only the minimum subset of Fortran that supports conditional compilation. This required at least two features: an IF statement, to support the "conditional" aspect, and variables, to control the conditionals. Combining these minimal requirements with a few usability statements yields the nine statements in Table 1.

The difference between initializing a CCF variable with a value in the INTEGER or LOGICAL statements and assigning a value to a CCF variable with the assignment statement is that the assignment statement value cannot be overridden with an optional invocation value.

The aforementioned nine statements are distinguished in the source code by the characters !CCF$ in columns one to five of a line. Since ! starts a comment in Fortran, all CCF statements look like comments to a Fortran compiler.

CCF Processor Output

The CCF processor reads the source file, interprets the CCF commands, and produces one of three different output formats: short, big replace, and big shift.

The CCF short output contains only the lines that will actually be compiled. All CCF statements and all lines that are in the FALSE branches of the CCF IF constructs are deleted. The advantage of this file format is that it's easy to produce; it's the only format supported by the preprocessor in Listing One.

The CCF big replace output contains the same number of lines as the input file. The only difference between the big-replace file and the input file are the lines in the FALSE branch(es) of the CCF IF constructs. These lines are commented out by placing the characters !CCF* in columns one to five. Because it has the same number of lines, it's easy to match compiler warnings against the original file. Also, no lines become longer. However, some of the original file might be lost if the lines being commented out already had something in the first five columns.

The CCF big shift is similar, but the lines in the FALSE branches are shifted to the right five characters, and the string !CCF> is placed in columns one to five of this new line. Like the big-replace file, the big-shift file makes it easy to match compiler warnings to the original source. Unlike the big-replace output, however, big-shift files preserve all of the original program text.

The big-shift and big-replace files make it possible to simply discard the original file after processing. The processed file can be used as subsequent input to the CCF processor, which simply recognizes lines beginning with !CCF* or !CCF> as lines commented by a previous run through CCF. For instance, Example 1(a) has already been run through a CCF processor to produce code for DOS. Without any additional editing, it can be reprocessed to produce UNIX code, as in Example 1(b).

Having only one file helps avoid problems with accidentally changing the wrong file. With the short output, it may be tempting to make temporary changes to the processed file, which can cause confusion if these changes don't get carried into the original source.

CCF Processor-Invocation Options

The CCF processor must know two pieces of information when it is invoked: which CCF variables to override, and what type of output file to produce. The simple CCF processor in Listing One accepts these options from the keyboard or a batch file.

A Simple CCF Processor

Because CCF statements are a subset of the statements in the target language, it's relatively easy to use the compiler for your target language to help process CCF input. Listing One implements a CCF processor by creating a new program that, when compiled and run, outputs the final program. This involves simply uncommenting the CCF lines and replacing all other lines with corresponding PRINT statements. Example 2(a) is sample input to this algorithm, and the corresponding output is Example 2(b). When Example 2(b) is compiled and run, it will output Example 2(c)--the final, processed output. This simple processor doesn't support either the big-shift or big-replace formats. (For the more complete CCF_95 processor, contact [email protected].)

Other Uses of Conditional Compilation

Although portability is the most popular use of conditional compilation, it can simplify many other tasks as well. Conditional compilation can be used to selectively enable debugging code. This allows the program to be compiled with different levels of debugging enabled, ranging from simple Entering FUNCTION Foo comments to more extensive checks on complex processes. Example 3 shows how this might be used. To avoid using the processor on every compile, you can place !CCF> and !CCF* comments manually.

Because the CCF processor completely ignores all non-CCF lines, you can place comments or other text in the FALSE branch of a CCF condition. You can even maintain several different kinds of source code in a single file, such as the Fortran source and a batch file; suitable CCF options will extract either one.

I've used CCF to test Fortran 90 expression parsers by including CCF statements to select parts of a large test file. This made it easy to narrow down a failure to a specific part of the input without manually editing the file to identify the problem line.

The Future of CCF

CCF has been submitted to X3J3 as a possible addition to the Fortran standard. The most recent summary (available from ftp://ftp.ncsa.uiuc.edu/x3j3) lists CCF as a "high priority'' for the next update. Additional information is available from http://www.fortran.com/fortran/market.html.

Acknowledgments

Many have contributed to the development of CCF, including Karen Barney, John Ehrman, Dick Weaver, Bruce Pumplin, Mike Dedina, Mark Epstein, Kelly Flanagan, and Harris Hall. The use of conditional compilation for debugging was introduced to me by Don Rose. Support for the standardization of CCF has come from many X3J3 members, but particularly from Jeanne Martin.

References

Epstein, David. "Imagine A Conditional Compilation Facility (CCF)." Fortran Journal (May/June 1994).

------. "CCF Here and Now." Fortran Journal (September/October 1994).

------. "CCF Is Unlike The Others." Fortran Journal (January/February 1995).

Table 1: CCF statements.

Statement   Description

INTEGER	    Declare and initialize an integer CCF variable.
LOGICAL	    Declare and initialize a logical CCF variable.
IF          Classical conditional statement.
ELSEIF      Part of the IF construct.
ELSE        Part of the IF construct.
ENDIF       Part of the IF construct.
assignment  Assign a value to a CCF variable.
PRINT       Output to the screen during CCF processing.
STOP        Halt during CCF processing.

Example 1: (a) File processed for DOS; (b) file processed for UNIX.

(a)
!CCF$ IF (system == DOS) THEN
      filename = '\back\slash\eightdot.3'
!CCF$ ELSEIF (system == UNIX) THEN
!CCF* filename = '/forward/slash/manydot.many'
!CCF$ ELSE
!CCF$  STOP 'Set CCF variable "system" to DOS or UNIX'
!CCF$ ENDIF


(b)
!CCF$ IF (system == DOS) THEN
!CCF* filename = '\back\slash\eightdot.3'
!CCF$ ELSEIF (system == UNIX) THEN
      filename = '/forward/slash/manydot.many'
!CCF$ ELSE
!CCF$  STOP 'Set CCF var "system" to DOS or UNIX'
!CCF$ ENDIF

Example 2: (a) Original file; (b) intermediate file; (c) output file.

(a)
!CCF$ INTEGER :: version_num = 2
!CCF$ IF (version_num == 2) THEN
   max = 1024
!CCF$ ELSE
   max = 512
!CCF$ ENDIF


(b)
     INTEGER :: version_num = 2
     IF (version_num == 2) THEN
PRINT *,'   max = 1024'
     ELSE
PRINT *,'   max = 512' 
     ENDIF


(c)
max = 1024

Example 3: Using CCF for debugging.

!CCF$ IF (debug_level > 0) THEN
!CCF>  PRINT *, 'Entering FUNCTION SnickersBar'
!CCF$  IF (debug_level > 1) THEN
!CCF>   PRINT *, 'with argument "foo" = ',foo
!CCF>   PRINT *, ' and argument "moo" = ',moo
!CCF$  ENDIF ! (debug_level > 1) THEN
!CCF$ ENDIF ! (debug_level > 0) THEN

Listing One

!******************************************************************************
! Simple CCF Processor
! This code is free.  You are also free (do whatever you want with this code).
! David Epstein and imagine1 appreciates any donations (comments, 
! coding suggestions or checks made payable to imagine1).
!  e-mail    : [email protected]
!  telephone : +1-503-383-4846
!  address   : imagine1
!              PO Box 250
!              Sweet Home, OR 97386
! BASIC IDEA
! Since the Conditional Compilation Facility (CCF) is a subset of Fortran,
! a simple CCF processor is achieved by using a Fortran processor to
! execute the CCF statements.
! STEPS
! 1. Compile this CCF processor with your Fortran processor.
!    You now have a CCF processor.
! 2. Invoke this CCF processor and follow the prompts for required input.
! 3. Compile the ccf-temp file (ccftemp.f90 for dos and unix systems).
! 4. Execute the ccf-temp file to create the resulting output file.
! ALGORITHM
! Turn !ccf$ lines into Fortran lines by replacing !ccf$ with blanks.
! Turn all other lines into output statements (include handling of !ccf*
! and !ccf> lines as lines that CCF previously turned into comments).
! Special handling of CCF variables is required due to Fortran requirement
! that the specification-part precedes the execution-part.  All CCF variable
! declaration lines are buffered until written to a MODULE.  This MODULE
! is also used to handle initialization of CCF variables when the user
! wants to override initial values in the source.
! DESCRIPTION OF OUTPUT
! This simple CCF processor creates a file that consists only of the lines
! that the Fortran processor would see.  In other words, all CCF lines
! (those with !ccf$ in columns 1 to 5) will not appear in the created file
! and all lines that are in a FALSE branch of a CCF if-construct will not
! appear in the created file.
! DIFFERENCES BETWEEN THIS CCF AND FULL CCF
! This simple CCF processor differs from the CCF in your Fortran processor
! in a few ways:
!   1) Two files must be maintained because this CCF processor does not
!      give the option of creating a file with the same number of lines
!      as the input file.  A full CCF processor can comment lines in the
!      the FALSE branch of a CCF if-construct with a !ccf* or !ccf>.  This
!      CCF processor will not include CCF lines or the lines in a FALSE
!      branch of a CCF if-construct.
!   2) This CCF processor creates two temp files that must be compiled and
!      run in order to create the CCF output file.  One of these temp files
!      is INCLUDEd in the other temp file.  A full CCF processor does not
!      require creating extra files.
!   3) The CCF PRINT and STOP statements are executed at compile time in
!      the CCF in your Fortran processor.  In this CCF processor the CCF
!      PRINT and STOP statements are executed when the ccf temp file is
!      executed.  This slightly hinders the usability of these CCF stmts.
!   4) There is a limit on the number of CCF variable declaration lines
!      (you may change this limit by changing MAX_CCF_VAR_DECL_LINES).
!   5) Fortran keywords INTEGER and LOGICAL are reserved words for this
!      CCF processor (do not name a CCF variable "integer" or "logical").
!   6) Diagnostic checks are not made on the CCF statements.  Please stick
!      to the CCF language definition (CCF variables do not have "kinds",
!      CCF statements do not have labels, use free source form for the
!      CCF lines (even if your source is fixed source form), etc.).
!******************************************************************************
program SimpleCcf
implicit NONE
  ! for buffering CCF variable declarations so they can be written to a MODULE
  integer, parameter    :: MAX_CCF_VAR_DECL_LINES = 100
  character (len = 132) :: ccf_var_lines(MAX_CCF_VAR_DECL_LINES)
  integer               :: num_ccf_var_decl_lines = 0
  ! for storing lines of input
  character (len = 132) :: line
  ! file names (limit of 32 is a random selection and can be changed)
  character (len = 132)  :: in_file, &     ! Input file to be CCFed
                           out_file       ! Output file excludes CCF lines and
                                          ! lines inside FALSE branches
  character (len = 132)  :: temp_file1, &  ! Middle-step file MAIN
                           temp_file2,  & ! Middle-step file MODULE
                           batch_file     ! Option to run CCF batch mode
  ! batch mode for input (input and output filenames and initial values)
  logical               :: batch          ! TRUE if batch_file exists
!CCF$ integer :: dos = 1
!CCF$ integer :: unix = 2
!CCF$ integer :: your_system = 3 ! Edit here for system other than dos or unix
!CCF$ integer :: system = 1
   ! set filenames
!CCF$ if (system == dos .OR. system == unix) then
   temp_file1 = "ccftemp.f90"
   temp_file2 = "ccfmod.f90"
   batch_file = "ccfbatch.dat"
!CCF$ else ! system other than dos or unix
!CCF$    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!CCF$    stop 'Please edit here and above for system other than dos or unix'
!CCF$    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!CCF>   temp_file1 = "ccftemp filename for your system"
!CCF>   temp_file2 = "ccfmod filename for your system"
!CCF>   batch_file = "ccfbatch filename for your system"
!CCF$ endif
   inquire (FILE=batch_file, EXIST=batch)
   if (batch) then
      open (UNIT=7, ACTION="READ", &
            STATUS="OLD", POSITION="REWIND", ERR=101, FILE=batch_file)
      read (UNIT=7, FMT="(A)", END=102) in_file
      read (UNIT=7, FMT="(A)", END=102) out_file
   else
      print *, "Enter the name of the file to be CCFed:"
      read *, in_file
      print *, "Enter the name you want for the output file:"
      read *, out_file
   endif
   open (UNIT=8, ACTION="READ", &
         STATUS="OLD", POSITION="REWIND", ERR=103, FILE=in_file)
   open (UNIT=9, ACTION="WRITE", &
         STATUS="REPLACE", POSITION="REWIND", ERR=104, FILE=temp_file1)
   call WriteLine(9, "INCLUDE '" // TRIM(temp_file2) // "'"                   )
   call WriteLine(9, "PROGRAM CcfIt"                                          )
   call WriteLine(9, "USE CcfVars    ! Module with CCF vars and init values"  )
   call WriteLine(9, "implicit NONE"                                          )
   call WriteLine(9, " "                                                      )
   call WriteLine(9, "CALL mCcfInits  ! Init CCF values"                      )
   call WriteLine(9, 'open (UNIT=9, ACTION="WRITE", STATUS="REPLACE", ERR=7,&')
   call WriteLine(9, '      POSITION="REWIND", FILE="' //TRIM(out_file)// '")')
   !**************************************************************************!
   !**  Read each line of the input file looking for                        **!
   !**    1) CCF lines                                                      **!
   !**    2) lines previously commented out by CCF in a Fortran processor   **!
   !**    3) other lines                                                    **!
   !**    1) CCF lines -                                                    **!
   !**        a) CCF variable declarations are buffered until written to    **!
   !**           the CCF module                                             **!
   !**        b) all other CCF lines are turned into Fortran lines by       **!
   !**           replacing the !ccf$ in columns 1-5 with blanks             **!
   !**    2) lines previously commented out by CCF in a Fortran processor - **!
   !**        a) !ccf* in columns 1-5 - Replaced columns 1-5 with blanks    **!
   !**        b) !ccf> in columns 1-5 - Shift the line left 5 columns       **!
   !**        Double all double quotes (") as in 3)                         **!
   !**    3) other lines -                                                  **!
   !**        Other lines are simply echoed to the output file.  Doing      **!
   !**        this requires doubling all the double quotes (").             **!
   !**************************************************************************!
   do ! until end of file
      read (UNIT=8, FMT="(A)", END=50) line
      ! !ccf$ - a ccf line
      if ((line(1:5) == '!ccf$') .OR. (line(1:5) == '!CCF$')) then
         ! if a CCF var declaration, save this line for the CcfVar MODULE
         ! else replace the !ccf$ with blanks (turn into a Fortran statement)
         if (CcfVarDeclaration()) then
            num_ccf_var_decl_lines = num_ccf_var_decl_lines + 1
            if (num_ccf_var_decl_lines > MAX_CCF_VAR_DECL_LINES) then
               print *, 'CCF halts.  Limit of CCF variable declaration lines'
               print *, MAX_CCF_VAR_DECL_LINES,'exceeded on line:'
               print *, line
               stop
            endif
            line(1:5) = '     ' ! replace !ccf$ with blanks
            ccf_var_lines(num_ccf_var_decl_lines) = line
         else
            line(1:5) = '     ' ! replace !ccf$ with blanks
            call WriteLine(9, line)
         endif
      else ! not a CCF line
         ! !ccf* - a non-ccf line, commented by ccf by replacing columns 1 to 5
         ! !ccf> - a non-ccf line, commented by ccf by shifting the line
         !         right 5 columns and placing !ccf> in columns 1-5
         if ((line(1:5) == '!ccf*') .OR. (line(1:5) == '!CCF*')) then
            line(1:5) = '     '  ! blank out "!ccf$"
         elseif ((line(1:5) == '!ccf>') .OR. (line(1:5) == '!CCF>')) then
            line = line(6:)      ! shift left 5 char
         else
            ! No !ccf* or !ccf> to blank out or shift.
         endif
         ! Double each double quote (") and output the new line
         call HandleDoubleQuotesThenWriteLine()
      endif
   enddo
   50 continue  ! end of input file
   call WriteLine (9, "goto 8 ! We are done."                               )
   call WriteLine (9, "7 print *, 'Trying to open CCF output file with the&")
   call WriteLine (9, "& name you supplied >" // TRIM(out_file) // "'"      )
   call WriteLine (9, "stop 'CCF Error: opening this output file.'"         )
   call WriteLine (9, "8 continue"                                          )
   call WriteLine (9, "END")
   call WriteLine (9, " ")
   call WriteLine (9, "subroutine WriteLine(unit_num, line)           ")
   call WriteLine (9, "   integer :: unit_num                         ")
   call WriteLine (9, "   character (len=*) :: line                   ")
   call WriteLine (9, "                                               ")
   call WriteLine (9, '   write (UNIT=unit_num, FMT="(A)") TRIM(line) ')
   call WriteLine (9, "end subroutine WriteLine                       ")
   ! Write the MODULE file which contains all the CCF variable declarations
   ! and any initial values.
   call WriteCcfVarsModule()
   ! Reminder of middle-step
   if (.NOT. batch) then
      print *
      print *, "CCF is creating ", TRIM(temp_file1), &
               " as the temporary file to compile and run"
   endif
   ! Close files and we are done
   close (UNIT=7, STATUS="KEEP", ERR=105)
   close (UNIT=8, STATUS="KEEP", ERR=106)
   close (UNIT=9, STATUS="KEEP", ERR=107)
   goto 110
   !**** ERROR messages ******************************************************!
   101  print *, 'Trying to open CCF batch file "', TRIM(batch_file), '"'
   stop 'CCF Error: opening this input file.'
   102  print *, 'Trying to read a line from CCF Batch file "', &
                 TRIM(batch_file), '"'
   stop 'CCF Error: Batch file expecting input and output filenames'
   103  print *, 'Trying to open CCF input file "', TRIM(in_file), '"'
   stop 'CCF Error: opening this input file.'
   104  print *, 'Trying to open CCF temp file "', TRIM(temp_file1), '"'
   stop 'CCF Error: opening this input/output file.'
   105  print *, 'Trying to close CCF batch file "', TRIM(batch_file), '"'
   stop 'CCF Error: closing this input file.'
   106  print *, 'Trying to close CCF input file "', TRIM(in_file), '"'
   stop 'CCF Error: closing this input file.'
   107  print *, 'Trying to close CCF temp file "', TRIM(temp_file1), '"'
   stop 'CCF Error: closing this input/output file.'
   ! end ERROR messages ******************************************************!
   110 continue  ! no I/O errors.  We are done.
contains
!******************************************************************************
subroutine HandleDoubleQuotesThenWriteLine()
! Echo a non-CCF line to the middle-step temp file.  This is done by turning
! the line into a character constant.  Turning a line into a character constant
! requires placing it in between two double quotes (") and doubling each 
! occurrence of a double quote.  The new line is then passed to a WriteLine 
! subroutine.  Note that the original line could be 132 double quotes.
  character (len = 264) :: did_quotes_line ! line after doubling the "
  integer               :: new_len         ! line len after doubling the "
  character (len = 285) :: new_line        ! 285 handles a line of 132 "s as:
                                           !   1-19 : call WriteLine(9, "
                                           !  20-283: 264 "s (132 "s doubled)
                                           ! 284-285: ")
  integer :: pos,     &  ! pos in original line
             new_pos     ! position in did_quotes_line
   did_quotes_line = ' '
   new_pos = 1
   ! Double each occurrence of a double quote (").
   do pos = 1, LEN_TRIM(line)
      if (line(pos:pos) == '"') then
         did_quotes_line(new_pos:new_pos+1) = '""'
         new_pos = new_pos + 2
      else
         did_quotes_line(new_pos:new_pos) = line(pos:pos)
         new_pos = new_pos + 1
      endif
   enddo
   new_len = new_pos - 1
   new_line(1:19) = 'call WriteLine(9, "'
   new_line(20:20+new_len -1)           = did_quotes_line(1:new_len)
   new_line(20+new_len:20+new_len+1)                             = '")'
   ! Set new_len to the length of the Fortran statement that may need splitting
   new_len = 20+new_len+1
   if (new_len <=132) then
      call WriteLine(9, new_line(1:new_len))
   elseif (new_len <= 262) then
   ! need to split line once
      call WriteLine(9, new_line(1:131)//"&")
      call WriteLine(9, "&"//new_line(132:new_len))
   else
   ! need to split line twice
      call WriteLine(9, new_line(1:131)//"&")
      call WriteLine(9, "&"//new_line(132:261)//"&")
      call WriteLine(9, "&"//new_line(262:new_len))
   endif
end subroutine HandleDoubleQuotesThenWriteLine
!******************************************************************************
function CcfVarDeclaration()
! Determine if the current CCF line ("line") is a CCF variable declaration
! line.  This simple CCF processor expects free source form, keywords are
! reserved words, and there are no "kinds" on these variables.
  logical              :: CcfVarDeclaration ! TRUE if CCF INTEGER or
                                            !         CCF LOGICAL statement
  integer              :: pos               ! pos in line
   ! skip over the !ccf$ and the blanks
   pos = 6
   do while (line(pos:pos) == ' ')
      pos = pos + 1
   end do
   ! This Simple CCF processor expects a blank or a ':' after
   ! INTEGER or LOGICAL.  Note that INTEGER and LOGICAL are reserved words.
   if (((line(pos:pos+6) == 'INTEGER') .OR. &
        (line(pos:pos+6) == 'LOGICAL') .OR. &
        (line(pos:pos+6) == 'integer') .OR. &
        (line(pos:pos+6) == 'logical'))     &
       .AND.                                &
       ((line(pos+7:pos+7) == ' ') .OR.     &
        (line(pos+7:pos+7) == ':'))) THEN
      CcfVarDeclaration = .TRUE.
   else
      CcfVarDeclaration = .FALSE.
   endif
end function CcfVarDeclaration
!******************************************************************************
subroutine WriteCcfVarsModule()
! Write the CcfVars MODULE.  CcfVars contains all the CCF variable
! declarations and a subroutine called mCcfInits.  mCcfInits contains
! assignments of any initial values for CCF variables supplied either
! in the CCF batch file or from standard input.
  integer              :: i            ! counter for loop
  character (len = 32) :: init_var, &  ! CCF variable to be initialized
                          init_val     ! intial value for CCF variable
   open (UNIT=10, ACTION="WRITE", STATUS="REPLACE", &
         POSITION="REWIND", ERR=201, FILE=temp_file2)
   call WriteLine(10, "MODULE CcfVars")
   call WriteLine(10, "implicit NONE")
   do i = 1, num_ccf_var_decl_lines
      call WriteLine(10, ccf_var_lines(i))
   enddo
   call WriteLine(10, "CONTAINS")
   call WriteLine(10, "SUBROUTINE mCcfInits")
   init_var = "foo" ! any foo other than '0' since '0' terminates loop
   do while (init_var /= '0')
      if (batch) then
         read (UNIT=7, FMT="(A)", END=202) init_var
         if (init_var(1:1) /= '0') then
            read (UNIT=7, FMT="(A)", END=202) init_val
            line = init_var // "=" // init_val
            call WriteLine(10, line)
         endif
      else
         print *, "Enter the name of a CCF variable to be initialized"
         print *, "(or '0' when you are done initializing):"
         read *, init_var
         if (init_var /= '0') then
            print *, "Enter the value you want for this CCF variable: "
            read *, init_val
            line = init_var // "=" // init_val
            call WriteLine(10, line)
         endif
      endif
   enddo
   call WriteLine(10, "END SUBROUTINE mCcfInits")
   call WriteLine(10, "END MODULE CcfVars")
   close (UNIT=10, STATUS="KEEP", ERR=203)
   goto 210
   !**** ERROR messages ******************************************************!
   201  print *, 'Trying to open CcfVars Module file "', TRIM(temp_file2), '"'
   stop 'CCF Error: opening this output file.'
   202  print *, 'Trying to read a line from CCF Batch file "', &
                 TRIM(batch_file), '"'
   stop 'CCF Error: Batch file expecting initial var-val pairs or 0'
   203  print *, 'Trying to close CcfVars Module file "', TRIM(temp_file2), '"'
   stop 'CCF Error: closing this output file.'
   ! end ERROR messages ******************************************************!
   210 continue ! no I/O errors
end subroutine WriteCcfVarsModule
end program
!******************************************************************************
subroutine WriteLine(unit_num, line)
implicit NONE
  integer :: unit_num
  character (len=*) :: line
   write (UNIT=unit_num, FMT="(A)") TRIM(line)
end subroutine WriteLine


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.