[gcc r17-2732] cobol: Resume source format after copybook ends.

"James K. Lowden via Gcc-cvs" <[email protected]>
Newsgroups gmane.comp.gcc.cvs
Message-ID <[email protected]>
https://gcc.gnu.org/g:bdcc2f7fd6fc0ef6b9b892d5e39d7848f977a94d

commit r17-2732-gbdcc2f7fd6fc0ef6b9b892d5e39d7848f977a94d
Author: James K. Lowden <[email protected]>
Date:   Mon Jul 27 12:30:17 2026 -0400

    cobol: Resume source format after copybook ends.
    
    The ISO CDF SOURCE FORMAT directive controls interpretation of the ensuing
    source code, including copybook files until EOF.  At the end of a
    copybook, any changes it made to the SOURCE FORMAT status is
    discarded.
    
    The scanner determines whether or not Debugging Mode ('D' in column 7)
    lines are included.  Because it does not know the source format, for
    Reference Format files the lexio module replaces the 'D' with 0x8D,
    which to the scanner signifies a Debug line.
    
    gcc/cobol/ChangeLog:
    
            * cbldiag.h (cdf_push_source_format): Remove declaration.
            (cdf_pop_source_format): Same.
            * cdf-copy.cc (is_fixed_format): Same.
            (is_reference_format): Same.
            (esc): Pass new format parameter and use new 0x8D Debug indicator.
            * cdf.y: Ignore SOURCE FORMAT directive (handled in lexio.cc).
            * lexio.cc (cdf_source_format): Delete function definition.
            (source_format_t::infer): Same.
            (is_fixed_format): Same.
            (is_reference_format): Same.
            (left_margin): Same.
            (right_margin): Same.
            (include_debug): Assume fixed format.
            (set_debug): Same.
            (continues_at): Remove unused function.
            (indicated): Move function to source_format_t class.
            (source_format_t::indicated): Replace ::indicated().
            (filespan_t::tab_check): Delete function.
            (check_push_pop_directive): Add format stack parameter.
            (check_source_format_directive): Same.
            (esc): Add format parameter and use 0x8D indicator.
            (parse_replace_pairs): Add format parameter.
            (parse_copy_directive): Same.
            (parse_replace_text): Same.
            (parse_replace_directive): Same.
            (cdftext::lex_open): Instantiate and pass format stack.
            (cdftext::free_form_reference_format): Add format stack parameter and 0x8D indicator.
            (cobol_set_indicator_column): Stash command-line option.
            (cdftext::process_file): Add format stack parameter.
            * lexio.h (struct filespan_t): Remove tab_check() function.
            (class source_format_t): Lift into header file to share with cdf-copy.cc.
            (class cdftext): Add several functions, formerly file-scope.
            (free_form_reference_format): Move to source_format_t.
            * parse.y: Slightly simplify parse of WITH DEBUGGING MODE.
            * scan.l: Recognize new 0x8D indicator.
            * scan_ante.h (cobol_set_indicator_column): Remove declaration.
            * util.cc (class cdf_directives_t): Remove source format support.
            (cobol_set_indicator_column): Move function to lexio.cc.
            (cdf_source_format): Delete function.
            (cdf_push_source_format): Same.
            (cdf_pop_source_format): Same.
            * util.h (class source_format_t): Move to lexio.h.

Diff:
---
 gcc/cobol/cbldiag.h   |   2 -
 gcc/cobol/cdf-copy.cc |   8 +-
 gcc/cobol/cdf.y       |   4 +-
 gcc/cobol/lexio.cc    | 198 ++++++++++++++++++++++++--------------------------
 gcc/cobol/lexio.h     |  83 ++++++++++++++++++---
 gcc/cobol/parse.y     |   5 +-
 gcc/cobol/scan.l      |  11 +--
 gcc/cobol/scan_ante.h |   2 -
 gcc/cobol/util.cc     |  26 -------
 gcc/cobol/util.h      |  52 -------------
 10 files changed, 181 insertions(+), 210 deletions(-)

diff --git a/gcc/cobol/cbldiag.h b/gcc/cobol/cbldiag.h
index 22d703b2cada..a3f5f70b4388 100644
--- a/gcc/cobol/cbldiag.h
+++ b/gcc/cobol/cbldiag.h
@@ -81,13 +81,11 @@ void cdf_push_call_convention();
 void cdf_push_current_tokens();
 void cdf_push_dictionary();
 void cdf_push_enabled_exceptions();
-void cdf_push_source_format();
 
 void cdf_pop();
 void cdf_pop_call_convention();
 void cdf_pop_current_tokens();
 void cdf_pop_dictionary();
-void cdf_pop_source_format();
 void cdf_pop_enabled_exceptions();
 
 size_t current_program_index();
diff --git a/gcc/cobol/cdf-copy.cc b/gcc/cobol/cdf-copy.cc
index f88357b1e90b..ce437999c9a9 100644
--- a/gcc/cobol/cdf-copy.cc
+++ b/gcc/cobol/cdf-copy.cc
@@ -76,8 +76,6 @@
  */
 
 const char * cobol_filename();
-bool is_fixed_format();
-bool is_reference_format();
 
 struct line_t {
   char *p, *pend;
@@ -123,14 +121,14 @@ verify_bounds( size_t pos, size_t size, const char input[] ) {
  * directive.
  */
 const char *
-esc( size_t len, const char input[] ) {
+esc( size_t len, const char input[], bool is_fixed_format ) {
   static const char space[]  = "([,;]?[[:space:]])+";
-  static const char spaceD[] = "(\n {6}D" "|" "[,;]?[[:space:]])+";
+  static const char spaceD[] = "(\n {6}\x8D" "|" "[,;]?[[:space:]])+";
   static char buffer[64 * 1024];
   char *p = buffer;
   const char *eoinput = input + len;
 
-  const char *spacex = is_reference_format()? spaceD : space;
+  const char *spacex = is_fixed_format? spaceD : space;
 
   for( const char *s=input; *s && s < eoinput; s++ ) {
     *p = '\0';
diff --git a/gcc/cobol/cdf.y b/gcc/cobol/cdf.y
index 9196a545b6a5..b12886684fb9 100644
--- a/gcc/cobol/cdf.y
+++ b/gcc/cobol/cdf.y
@@ -513,7 +513,7 @@ cdf_push:       CDF_PUSH cdf_stackable {
                   case parser::token::YDF_CALL_CONVENTION: cdf_push_call_convention(); break;
                   case parser::token::YDF_CDF_DEFINE: 	cdf_push_dictionary(); break;
                   case parser::token::YDF_COBOL_WORDS: 	cdf_push_current_tokens(); break;
-                  case parser::token::YDF_SOURCE_FORMAT:	cdf_push_source_format(); break;
+                  case parser::token::YDF_SOURCE_FORMAT:
                   default: cdf_unreachable(); 
                   }
                 }
@@ -524,7 +524,7 @@ cdf_pop:        CDF_POP cdf_stackable {
                   case parser::token::YDF_CALL_CONVENTION: cdf_pop_call_convention(); break;
                   case parser::token::YDF_CDF_DEFINE: 	cdf_pop_dictionary(); break;
                   case parser::token::YDF_COBOL_WORDS: 	cdf_pop_current_tokens(); break;
-                  case parser::token::YDF_SOURCE_FORMAT:	cdf_pop_source_format(); break; 
+                  case parser::token::YDF_SOURCE_FORMAT:
                   default: cdf_unreachable(); 
                   }
                 }
diff --git a/gcc/cobol/lexio.cc b/gcc/cobol/lexio.cc
index fb0c5cfc56f0..44ca02cc8aa9 100644
--- a/gcc/cobol/lexio.cc
+++ b/gcc/cobol/lexio.cc
@@ -38,27 +38,6 @@
 
 extern int yy_flex_debug;
 
-source_format_t& cdf_source_format();
-
-void
-source_format_t::infer( const char *bol, bool want_reference_format ) {
-  if( bol ) {
-    left = 7;
-    if( want_reference_format ) {
-      right = 73;
-    }
-  }
-  dbgmsg("%s:%d: %s format detected", __func__, __LINE__,
-         description());
-}
-
-
-// public source format test functions
-bool is_fixed_format() { return cdf_source_format().is_fixed(); }
-bool is_reference_format() { return cdf_source_format().is_reffmt(); }
-
-static bool debug_mode = false;
-
 /*
  * The "debug mode" is a little odd, because we have to make sure a
  * leading "D" doesn't start the verb DISPLAY (for example).  If
@@ -68,14 +47,8 @@ static bool debug_mode = false;
  *
  * So, the line is excluded if: fixed format and not debug mode
  * Else, it's included.
-*/
-
-static inline int left_margin() {
-  return cdf_source_format().left_margin();
-}
-static inline int right_margin() {
-  return cdf_source_format().right_margin();
-}
+ */
+static bool debug_mode = false;
 
 /*
  * When setting the indicator column explicitly:
@@ -85,8 +58,8 @@ static inline int right_margin() {
 void
 cobol_set_indicator_column( int column );
 
-bool include_debug()      { return is_fixed_format() && debug_mode; }
-bool set_debug( bool tf ) { return debug_mode = tf && is_fixed_format(); }
+bool include_debug()      { return debug_mode; }
+bool set_debug( bool tf ) { return debug_mode = tf; }
 
 static bool nonblank( const char ch ) { return !isblank(ch); }
 
@@ -97,19 +70,11 @@ start_of_line( char *bol, char *eol ) {
   return bol;
 }
 
-static inline char *
-continues_at( char *bol, char *eol ) {
-  if( cdf_source_format().is_free() ) return NULL;  // cannot continue in free format
-  bol += left_margin();
-  if( *bol != '-' ) return NULL; // not a continuation line
-  return start_of_line(++bol, eol);
-}
-
 // Return pointer to indicator column. Test ch if provided.
 // NULL means no indicator column or tested value not present.
-static inline char *
-indicated( char *bol, const char *eol, char ch = '\0' ) {
-  if( cdf_source_format().left_margin() == 0 && *bol != '*' ) {
+char *
+source_format_t::indicated( char *bol, const char *eol, char ch ) {
+  if( left_margin() == 0 && *bol != '*' ) {
     return NULL;  // no indicator column in free format, except for comments
   }
   gcc_assert(bol != NULL);
@@ -150,20 +115,6 @@ count_newlines( const char *beg, const char *end ) {
   return std::count(beg, end, '\n');
 }
 
-size_t
-filespan_t::tab_check( const char *src, const char *esrc ) {
-  static const char tab = '\t';
-
-  const char *data = src + left_margin();
-  if( data < esrc ) { // not a blank line
-    const char *tab_at = std::find(src, data, tab);
-    if( tab_at < data ) {
-      return (tab_at - src) + 1;
-    }
-  }
-  return 0;
-}
-
 static const auto extended_icase = regex::extended | regex::icase;
 
 std::stack< std::list<replace_t> > replace_directives;
@@ -321,7 +272,8 @@ recognize_replacements( filespan_t mfile, std::list<replace_t>& pending_replacem
 }
 
 static void
-check_push_pop_directive( filespan_t& mfile ) {
+check_push_pop_directive( filespan_t& mfile,
+                          source_format_stack_t& source_format ) {
   char eol = '\0';
   const char *p = std::find(mfile.cur, mfile.eol, '>');
   if( ! (p < mfile.eol && p[1] == *p ) ) return;
@@ -368,7 +320,14 @@ check_push_pop_directive( filespan_t& mfile ) {
       push? cdf_push_dictionary() : cdf_pop_dictionary();
       break;
     case 'S': // SOURCE FORMAT
-      push? cdf_push_source_format() : cdf_pop_source_format();
+      if( push ) {
+        auto format = source_format.top();
+        source_format.push(format);
+      } else {
+        source_format.pop();
+        dbgmsg("%s: POP: format now %s", __func__,
+               source_format.top().description());
+      }
       break;
     case 'T': // TURN
       push? cdf_push_enabled_exceptions() : cdf_pop_enabled_exceptions();
@@ -382,7 +341,8 @@ check_push_pop_directive( filespan_t& mfile ) {
 }
 
 static void
-check_source_format_directive( filespan_t& mfile ) {
+check_source_format_directive( filespan_t& mfile,
+                               source_format_t& source_format ) {
   char eol = '\0';
   const char *p = std::find(mfile.cur, mfile.eol, '>');
   if( ! (p < mfile.eol && p[1] == *p ) ) return;
@@ -405,10 +365,10 @@ check_source_format_directive( filespan_t& mfile ) {
     gcc_assert(cm.size() > 1);
     switch( cm[3].length() ) {
     case 4:
-      cobol_set_indicator_column(0);
+      source_format.indicator_column_set(0);
       break;
     case 5:
-      cobol_set_indicator_column(-7);
+      source_format.indicator_column_set(-7);
       break;
     default:
       gcc_assert(cm[3].length() == 4 || cm[3].length() == 5);
@@ -417,9 +377,9 @@ check_source_format_directive( filespan_t& mfile ) {
 
     dbgmsg( "%s:%d: %s format set, on line " HOST_SIZE_T_PRINT_UNSIGNED,
             __func__, __LINE__,
-            cdf_source_format().description(),
+            source_format.description(),
             (fmt_size_t)mfile.lineno() );
-    char *bol = cdf_source_format().is_fixed()? mfile.cur : const_cast<char*>(cm[0].first);
+    char *bol = source_format.is_fixed()? mfile.cur : const_cast<char*>(cm[0].first);
     gcc_assert(cm[0].second <= mfile.eol);
     erase_line(bol, const_cast<char*>(cm[0].second));
   }
@@ -478,7 +438,7 @@ is_program_id( const char *p, const char *eol ) {
   return false;
 }
 
-const char * esc( size_t len, const char input[] );
+const char * esc( size_t len, const char input[], bool is_fixed_format );
 
 static bool
 is_word_char( char ch ) {
@@ -811,12 +771,13 @@ parse_replacing_pair( const char *stmt, const char *estmt ) {
 }
 
 static std::pair<std::list<replace_t>, char *>
-parse_replace_pairs( const char *stmt, const char *estmt, bool is_copy_stmt ) {
+parse_replace_pairs( const char *stmt, const char *estmt,
+                     bool is_copy_stmt, const source_format_t& source_format ) {
   std::list<replace_t> pairs ;
 
   static const char     any_ch[] = "";
-  ////   const char    word_ch[] = "[[:alnum:]$_-]";
-  static const char nonword_ch[] = "[^[:alnum:]\"'$_-]";
+  // lexio replaces 'D' with 0x8D for a Debug indicator, which scan.l recognizes. 
+  static const char nonword_ch[] = "[^[:alnum:]\x8D\"'$_-]";
 
   // Pattern to find one REPLACE pseudo-text pair
   static const char replace_pattern[] =
@@ -873,7 +834,7 @@ parse_replace_pairs( const char *stmt, const char *estmt, bool is_copy_stmt ) {
     if( !is_word_char(before.p[0]) )     befter[0] = any_ch;
     if( !is_word_char(before.pend[-1]) ) befter[1] = any_ch;
 
-    const char *src = esc(before.size(), before.p);
+    const char *src = esc(before.size(), before.p, source_format.is_fixed());
 
     if( parsed.leading_trailing.size() > 0 ) {
       switch( TOUPPER(parsed.leading_trailing.p[0]) ) {
@@ -953,7 +914,7 @@ location_in( const filespan_t& mfile, const csub_match& cm ) {
 }
 
 static copy_descr_t
-parse_copy_directive( filespan_t& mfile ) {
+parse_copy_directive( filespan_t& mfile, const source_format_t& source_format ) {
   static const char *most_recent_buffer;
   static span_t copy_stmt(mfile.eodata, mfile.eodata);
 
@@ -1037,7 +998,7 @@ parse_copy_directive( filespan_t& mfile ) {
 
     if( replacing ) {
       std::pair<std::list<replace_t>, char*>
-        result = parse_replace_pairs( cm[0].second, mfile.eodata, true );
+        result = parse_replace_pairs( cm[0].second, mfile.eodata, true, source_format );
 
       const std::list<replace_t>& replacements(result.first);
       outcome.parsed = (outcome.nreplace = replacements.size()) > 0;
@@ -1060,7 +1021,7 @@ parse_copy_directive( filespan_t& mfile ) {
     }
 
     mfile.eol = const_cast<char*>(copy_stmt.pend);
-    mfile.next_line();
+    mfile.next_line(source_format.is_reffmt());
   }
   return outcome;
 }
@@ -1104,7 +1065,7 @@ parse_replace_last_off( const filespan_t& mfile ) {
 }
 
 static span_t
-parse_replace_text( filespan_t& mfile ) {
+parse_replace_text( filespan_t& mfile, const source_format_t& source_format ) {
   static const char pattern[] =
     /* 0 */    "REPLACE"
     /* 1 */    "([[:space:]]+ALSO)?"
@@ -1166,7 +1127,7 @@ parse_replace_text( filespan_t& mfile ) {
   span_t replace_stmt(cm[0].first, cm[0].second);
 
   std::pair<std::list<replace_t>, char*>
-        result = parse_replace_pairs(replace_stmt.p, replace_stmt.pend, false);
+    result = parse_replace_pairs(replace_stmt.p, replace_stmt.pend, false, source_format);
   const std::list<replace_t>& replacements(result.first);
   replace_directives.push( replacements );
 
@@ -1190,7 +1151,7 @@ parse_replace_text( filespan_t& mfile ) {
 }
 
 static span_t
-parse_replace_directive( filespan_t& mfile ) {
+parse_replace_directive( filespan_t& mfile, const source_format_t& source_format ) {
   static const char *most_recent_buffer, *next_directive;
   static bool off_coming_up;
   static const char pattern[] =
@@ -1232,7 +1193,7 @@ parse_replace_directive( filespan_t& mfile ) {
     if( off_coming_up ) {
       parse_replace_last_off(mfile);
     } else {
-      erased = parse_replace_text(mfile);
+      erased = parse_replace_text(mfile, source_format);
     }
   }
   return erased;
@@ -1509,6 +1470,20 @@ inode_of( int fd ) {
 
 FILE *
 cdftext::lex_open( const char filename[] ) {
+  /*
+   * The source_format stack should be instantiated here with one element, the
+   * source format that lexio determined from the command-line options and
+   * heuristics.  The recently added command_line_indicator_column is probably
+   * redundant.
+   */
+
+  source_format_stack_t source_format;
+  source_format_t format;  // free-form by default
+  if( command_line_indicator_column ) {
+    format.indicator_column_set(command_line_indicator_column);
+  }
+  source_format.push(format);
+
   int input = open_input( filename );
   if( input == -1 ) return NULL;
 
@@ -1524,10 +1499,10 @@ cdftext::lex_open( const char filename[] ) {
     }
     dbgmsg("lex_open: including %zu of %zu: '%s'", ++n, included_files.size(), name);
     cobol_filename(name, inode_of(input));
-    filespan_t mfile( free_form_reference_format( input ) );
+    filespan_t mfile( free_form_reference_format( input, source_format ) );
 
     please_push_filename = true;
-    process_file( mfile, output );
+    process_file( mfile, output, source_format );
 
     dbgmsg("lex_open: processed %zu of %zu: '%s'", n, included_files.size(), name);
     cobol_filename_restore(); // process_file restores only for COPY
@@ -1536,9 +1511,9 @@ cdftext::lex_open( const char filename[] ) {
   dbgmsg("lex_open: '%s'", filename);
 
   cobol_filename(filename, inode_of(input));
-  filespan_t mfile( free_form_reference_format( input ) );
+  filespan_t mfile( free_form_reference_format( input, source_format ) );
 
-  process_file( mfile, output );
+  process_file( mfile, output, source_format );
 
   if( lexer_echo() ) {
     echo_input(output, filename);
@@ -1738,7 +1713,8 @@ infer_reference_format( const char *bol, const char *eodata ) {
 }
 
 filespan_t
-cdftext::free_form_reference_format( int input ) {
+cdftext::free_form_reference_format( int input,
+                                     source_format_stack_t& format ) {
   filespan_t source_buffer = map_file(input);
   filespan_t mfile(source_buffer);
 
@@ -1757,34 +1733,34 @@ cdftext::free_form_reference_format( int input ) {
   /*
    * Infer source code format.
    */
-  if( cdf_source_format().inference_pending()  ) {
+  if( format.top().inference_pending()  ) {
     const char *bol = valid_sequence_area(mfile.data, mfile.eodata);
     if( bol ) {
-      cdf_source_format().infer( bol, infer_reference_format(bol, mfile.eodata) );
+      format.top().infer( bol, infer_reference_format(bol, mfile.eodata) );
     }
   }
 
-  while( mfile.next_line() ) {
-    check_push_pop_directive(mfile);
-    check_source_format_directive(mfile);
+  while( mfile.next_line(format.top().is_reffmt()) ) {
+    check_push_pop_directive(mfile, format);
+    check_source_format_directive(mfile, format.top());
     remove_inline_comment(mfile.cur, mfile.eol);
 
     if( mfile.is_blank_line() ) continue;
 
-    char *indcol = indicated(mfile.cur, mfile.eol); // true only for fixed
+    char *indcol = format.top().indicated(mfile.cur, mfile.eol); // true only for fixed
     //                                              // format
 
-    if( is_fixed_format() && !indcol ) { // short line
+    if( format.top().is_fixed() && !indcol ) { // short line
       erase_source(mfile.cur, mfile.eol);
     }
 
     if( indcol ) {
       // Set to blank columns 1-6 and anything past the right margin.
       erase_source(mfile.cur, indcol);
-      if( is_reference_format() ) {
-        if( mfile.cur + right_margin() <  mfile.eol ) {
-          auto p = std::find(mfile.cur + right_margin(), mfile.eol, '\n');
-          erase_source(mfile.cur + right_margin(), p);
+      if( format.top().is_reffmt() ) {
+        if( mfile.cur + format.top().right_margin() <  mfile.eol ) {
+          auto p = std::find(mfile.cur + format.top().right_margin(), mfile.eol, '\n');
+          erase_source(mfile.cur + format.top().right_margin(), p);
         }
       }
 
@@ -1798,8 +1774,8 @@ cdftext::free_form_reference_format( int input ) {
          */
         {
           char *pend = mfile.eol;
-          if( right_margin() ) {
-            pend = std::min(mfile.cur + right_margin(), mfile.eol);
+          if( format.top().right_margin() ) {
+            pend = std::min(mfile.cur + format.top().right_margin(), mfile.eol);
           }
           // The appended segment has no newline because the erased line retains
           // one.
@@ -1814,14 +1790,18 @@ cdftext::free_form_reference_format( int input ) {
         break;
       case 'D':
         /*
-         * Pass the D to the lexer, because WITH DEBUGGING MODE is
+         * Pass the D to the lexer as 0x8D, because WITH DEBUGGING MODE is
          * parsed in the parser.  This assumes too strict a rule: that
          * all the source is in one format. In fact, DEBUGGING MODE
          * could be set on, and >>SOURCE-FORMAT can switch back and
          * forth. To solve that, we'd have to parse WITH DEBUGGING MODE
          * in free_form_reference_format(), which is a lot of work for
          * an obsolete feature.
+         *
+         * Use the weird input value to signify fixed format, information
+         * otherwise not available and not significant to the parser.
          */
+        if( format.top().is_fixed() ) *indcol = 0x8D;
         break;
       case '*':
       case '/':
@@ -1845,7 +1825,7 @@ cdftext::free_form_reference_format( int input ) {
         break;
       }
     }
-    current.line.update(mfile.cur, mfile.eol, right_margin());
+    current.line.update(mfile.cur, mfile.eol, format.top().right_margin());
     current.lineno = mfile.lineno();
   } // next line
 
@@ -1853,6 +1833,15 @@ cdftext::free_form_reference_format( int input ) {
 }
 
 bool cdftext::please_push_filename = false;
+int cdftext::command_line_indicator_column = 0;
+
+void
+cobol_set_indicator_column( int column ) {
+  cdftext::command_line_indicator_column = column;
+  source_format_t local;
+  local.indicator_column_set(column);
+  dbgmsg("%s: format now %s", __func__, local.description());
+}
 
 void
 cdftext::output_push_directive( const char filename[],
@@ -1898,7 +1887,10 @@ cdftext::output_push_directive( const char filename[],
  * cobol filename and yylineno.
  */
 void
-cdftext::process_file( filespan_t mfile, int output, bool second_pass ) {
+cdftext::process_file( filespan_t mfile, int output, 
+                       source_format_stack_t source_format, 
+                       bool second_pass )
+{
   static size_t nfiles = 0;
   
   __gnu_cxx::stdio_filebuf<char> outbuf(fdopen(output, "a"), std::ios::out);
@@ -1926,9 +1918,9 @@ cdftext::process_file( filespan_t mfile, int output, bool second_pass ) {
   }
 
   // parse CDF directives
-  while( mfile.next_line() ) {
+  while( mfile.next_line(source_format.top().is_reffmt()) ) {
     yylloc = mfile.as_location();
-    auto copied = parse_copy_directive(mfile);
+    auto copied = parse_copy_directive(mfile, source_format.top());
     if( copied.parsed && copied.fd != -1 ) {
       gcc_assert(copied.erased_lines.p);
       output_push_directive( cobol_filename(), ofs );
@@ -1938,7 +1930,7 @@ cdftext::process_file( filespan_t mfile, int output, bool second_pass ) {
              __func__, __LINE__, (fmt_size_t)mfile.lineno(),
              copybook.source(), copybook.current()->fd);
       copy.in = copybook.current()->fd;
-      copy.mfile = free_form_reference_format( copy.in );
+      copy.mfile = free_form_reference_format( copy.in, source_format );
 
       if( copied.partial_line.size() ) {
         std::copy(copied.partial_line.p, copied.partial_line.pend, ofs);
@@ -1947,15 +1939,15 @@ cdftext::process_file( filespan_t mfile, int output, bool second_pass ) {
 
       if( copied.nreplace == 0 ) {
         // process with extant REPLACE directive
-        process_file(copy.mfile, output);
+        process_file(copy.mfile, output, source_format );
       } else {
         copy.out = open_output();
         // process to intermediate, applying COPY ... REPLACING
-        process_file(copy.mfile, copy.out);
+        process_file(copy.mfile, copy.out, source_format);
         copy.mfile = map_file(copy.out);
         replace_directives.pop();
         // process intermediate with extant REPLACE directive
-        process_file(copy.mfile, output, true);
+        process_file(copy.mfile, output, source_format, true);
         // COPY statement is erased from input if processed successfully
       }
       /*
@@ -1973,7 +1965,7 @@ cdftext::process_file( filespan_t mfile, int output, bool second_pass ) {
       dbgmsg("%s:%d: %lu blank lines erased", __func__, __LINE__, n);
     }
 
-    auto erased = parse_replace_directive(mfile);
+    auto erased = parse_replace_directive(mfile, source_format.top());
     if( erased.p ) {
       std::copy_if( erased.p, erased.pend, ofs,
                     []( char ch ) { return ch == '\n'; } );
diff --git a/gcc/cobol/lexio.h b/gcc/cobol/lexio.h
index f1eddad54e5a..2e64d936fcc4 100644
--- a/gcc/cobol/lexio.h
+++ b/gcc/cobol/lexio.h
@@ -152,9 +152,9 @@ struct filespan_t : public bytespan_t {
    */
   bool was_quote72() const { return iline == line_quote72 + 1; }
 
-  size_t next_line() {
+  size_t next_line(bool is_reference_format) {
     // Before advancing, mark the current line as ending in a quote, if true.
-    if( is_reference_format() && 72 <= line_length() ) {
+    if( is_reference_format && 72 <= line_length() ) {
       if( isquote(cur[71]) ) { line_quote72 = iline; }
     }
 
@@ -187,8 +187,6 @@ struct filespan_t : public bytespan_t {
 
   size_t line_length() const { return eol - cur; }
 
-  static size_t tab_check( const char *src, const char *esrc );
-
   bool is_blank_line() const {
     auto p = std::find_if( cur, eol, []( char ch ) { return !fisspace(ch); } );
     return p == eol;
@@ -281,13 +279,82 @@ struct replace_t {
   }
 };
 
+/*
+ * The default source format, whether free or fixed, is determined
+ * heuristically by examining the PROGRAM-ID line, if it exists, in the first
+ * input file. If that file does not have such a line, the default is free
+ * format.  Else the format is set to fixed if anything appears on that line
+ * that would prohibit parsing it as free format,
+ */
+class source_format_t {
+  bool first_file, explicitly;
+  int left, right;
+public:
+  source_format_t()
+    : first_file(true), explicitly(false), left(0), right(0)
+  {}
+  void indicator_column_set( int column ) {
+    explicitly = true;
+    if( column == 0 ) right = 0;
+    if( column < 0 ) {
+      column = -column;
+      right = 73;
+    }
+    left = column;
+  }
+  
+  char * indicated( char *bol, const char *eol, char ch = '\0' );
+
+  bool inference_pending() {
+    bool tf = first_file && !explicitly;
+    first_file = false;
+    return tf;
+  }
+
+  inline bool is_fixed() const { return left == 7; }
+  inline bool is_reffmt() const { return is_fixed() && right == 73; }
+  inline bool is_free() const { return ! is_fixed(); }
+  
+  const char * description() const {
+    if( is_reffmt() ) return "REFERENCE";
+    if( is_fixed() ) return "FIXED";
+    if( is_free() ) return "FREE";
+    gcc_unreachable();
+  }    
+
+  inline int left_margin() {
+    return left == 0? left : left - 1;
+  }
+  inline int right_margin() {
+    return right == 0? right : right - 1;
+  }
+
+  void infer( const char *bol, bool want_reference_format ) {
+    if( bol ) {
+      left = 7;
+      if( want_reference_format ) {
+        right = 73;
+      }
+    }
+    dbgmsg("%s:%d: %s format detected", __func__, __LINE__,
+           description());
+  }
+};
+
+typedef std::stack<source_format_t> source_format_stack_t;
+
 #include <cstdio>
-#include <list>
 
 class cdftext {
   static bool please_push_filename;
-  static filespan_t  free_form_reference_format( int fd );
-  static void process_file( filespan_t, int output, bool second_pass = false );
+  static int command_line_indicator_column;
+
+  friend void cobol_set_indicator_column( int column );
+
+  static void process_file( filespan_t mfile, int output, 
+                            source_format_stack_t source_format, 
+                            bool second_pass = false ) ;
+  static filespan_t  free_form_reference_format( int fd, source_format_stack_t& );
 
   static void output_push_directive( const char filename[],
                                     std::ostream_iterator<char>& ofs);
@@ -305,6 +372,4 @@ class cdftext {
   static FILE * lex_open( const char filename[] );
 };
 
-std::list<replace_t> free_form_reference_format( filespan_t mfile );
-
 #endif
diff --git a/gcc/cobol/parse.y b/gcc/cobol/parse.y
index 7599c99ec686..728d8d164a83 100644
--- a/gcc/cobol/parse.y
+++ b/gcc/cobol/parse.y
@@ -323,7 +323,7 @@ class locale_tgt_t {
 
   struct rel_part_t;
 
-  bool set_debug(bool);
+  bool set_debug(bool = true);
 
 #include "../../libgcobol/ec.h"
 #include "../../libgcobol/common-defs.h"
@@ -2868,7 +2868,8 @@ repo_property:  PROPERTY NAME repo_as
                 ;
 
 with_debug:     with DEBUGGING MODE {
-                  if( ! set_debug(true) ) {
+                  if( ! set_debug() ) {
+                    // always true because format not checked in parser
                     error_msg(@2, "DEBUGGING MODE valid only in fixed format");
                   }
                 }
diff --git a/gcc/cobol/scan.l b/gcc/cobol/scan.l
index 0b3bcc38c2f2..eaa323d6d9a4 100644
--- a/gcc/cobol/scan.l
+++ b/gcc/cobol/scan.l
@@ -2404,13 +2404,10 @@ BASIS		{ yy_push_state(basis); return BASIS; }
 		 * start condition.
                  */
 <*>{
-  ^[ ]{6}D.*\n 	{
-		  if( !is_fixed_format() ) {
-		    myless(6);
-		  } else {
-		    // If WITH DEBUGGING MODE, drop the D, else drop the line.
-		    if( include_debug() ) myless(7);
-		  }
+  ^[ ]{6}\x8d.*\n {
+		  // lexio munged fixed-format D indicator to 0x8D. 
+		  // If WITH DEBUGGING MODE, drop the D, else drop the line.
+		  if( include_debug() ) myless(7);
 		}
   ^[ ]*>>{OBLANK}IF		{ yy_push_state(cdf_state); return CDF_IF; }
   ^[ ]*>>{OBLANK}ELSE	 	{ return CDF_ELSE; }
diff --git a/gcc/cobol/scan_ante.h b/gcc/cobol/scan_ante.h
index c3f466070881..e502e45a041b 100644
--- a/gcc/cobol/scan_ante.h
+++ b/gcc/cobol/scan_ante.h
@@ -56,8 +56,6 @@ const char * keyword_str( int token );
 
 int repository_function_tok( const char name[] );
 
-void cobol_set_indicator_column( int column );
-
 void next_sentence_label(cbl_label_t*);
 
 int repeat_count( const char picture[] );
diff --git a/gcc/cobol/util.cc b/gcc/cobol/util.cc
index a385a6c9a778..b0f08f2c9f3d 100644
--- a/gcc/cobol/util.cc
+++ b/gcc/cobol/util.cc
@@ -183,9 +183,6 @@ class cdf_directives_t
     static std::string str(cdf_values_t) {
       return "<dictionary>";
     }
-    static std::string str(source_format_t arg) {
-      return arg.description();
-    }
     static std::string str(cbl_enabled_exceptions_t) {
       return "<enabled_exceptions>";
     }
@@ -195,7 +192,6 @@ class cdf_directives_t
   cdf_stack_t<cbl_call_convention_t> call_convention;
   cdf_stack_t<current_tokens_t> cobol_words;
   cdf_stack_t<cdf_values_t> dictionary;   // DEFINE
-  cdf_stack_t<source_format_t> source_format;
   cdf_stack_t<cbl_enabled_exceptions_t> enabled_exceptions;
 
   cdf_directives_t() {
@@ -206,14 +202,12 @@ class cdf_directives_t
     call_convention.push();
     cobol_words.push();
     dictionary.push();
-    source_format.push();
     enabled_exceptions.push();
   }
   void pop() {
     call_convention.pop();
     cobol_words.pop();
     dictionary.pop();
-    source_format.pop();
     enabled_exceptions.pop();
   }
 };
@@ -282,16 +276,6 @@ cbl_prototype_ok( const cbl_loc_t& loc, size_t iprog, dspc_t clause ) {
   return true;
 }
 
-void
-cobol_set_indicator_column( int column ) {
-  cdf_directives.source_format.value().indicator_column_set(column);
-  dbgmsg("%s: format now %s", __func__,
-         cdf_directives.source_format.value().description());
-}
-source_format_t& cdf_source_format() {
-  return cdf_directives.source_format.value();
-}
-
 cbl_enabled_exceptions_t&
 cdf_enabled_exceptions() {
   return cdf_directives.enabled_exceptions.value();
@@ -302,22 +286,12 @@ void cdf_push_call_convention() { cdf_directives.call_convention.push(); }
 void cdf_push_current_tokens() { cdf_directives.cobol_words.push(); }
 void cdf_push_dictionary() { cdf_directives.dictionary.push(); }
 void cdf_push_enabled_exceptions() { cdf_directives.enabled_exceptions.push(); }
-void cdf_push_source_format() {
-  cdf_directives.source_format.push();
-  dbgmsg("%s: format still %s", __func__,
-         cdf_directives.source_format.value().description());
-}
 
 void cdf_pop() { cdf_directives.pop(); }
 void cdf_pop_call_convention() { cdf_directives.call_convention.pop(); }
 void cdf_pop_current_tokens() { cdf_directives.cobol_words.pop(); }
 void cdf_pop_dictionary() { cdf_directives.dictionary.pop(); }
 void cdf_pop_enabled_exceptions() { cdf_directives.enabled_exceptions.pop(); }
-void cdf_pop_source_format() {
-  cdf_directives.source_format.pop();
-  dbgmsg("%s: format now %s", __func__,
-         cdf_directives.source_format.value().description());
-}
 
 void cdf_unreachable() { gcc_unreachable(); }
 
diff --git a/gcc/cobol/util.h b/gcc/cobol/util.h
index a5a2b9d26382..b99533944376 100644
--- a/gcc/cobol/util.h
+++ b/gcc/cobol/util.h
@@ -53,58 +53,6 @@ as_voidp( P p ) {
   return static_cast<const void *>(p);
 }
 
-/*
- * The default source format, whether free or fixed, is determined
- * heuristically by examining the PROGRAM-ID line, if it exists, in the first
- * input file. If that file does not have such a line, the default is free
- * format.  Else the format is set to fixed if anything appears on that line
- * that would prohibit parsing it as free format,
- */
-class source_format_t {
-  bool first_file, explicitly;
-  int left, right;
-public:
-  source_format_t()
-    : first_file(true), explicitly(false), left(0), right(0)
-  {}
-  void indicator_column_set( int column ) {
-    explicitly = true;
-    if( column == 0 ) right = 0;
-    if( column < 0 ) {
-      column = -column;
-      right = 73;
-    }
-    left = column;
-  }
-  
-  bool inference_pending() {
-    bool tf = first_file && !explicitly;
-    first_file = false;
-    return tf;
-  }
-
-  void infer( const char *bol, bool want_reference_format );
-  
-  inline bool is_fixed() const { return left == 7; }
-  inline bool is_reffmt() const { return is_fixed() && right == 73; }
-  inline bool is_free() const { return ! is_fixed(); }
-  
-  const char * description() const {
-    if( is_reffmt() ) return "REFERENCE";
-    if( is_fixed() ) return "FIXED";
-    if( is_free() ) return "FREE";
-    gcc_unreachable();
-  }    
-
-  inline int left_margin() {
-    return left == 0? left : left - 1;
-  }
-  inline int right_margin() {
-    return right == 0? right : right - 1;
-  }
-}; 
-
-
 /*
  * Functions that validate every PERFORM calls a unique reference.
  */
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.