From: Robert Dubner <[email protected]>
Date: Mon, 21 Sep 2026 13:03:11 -0400
Subject: [PATCH] cobol: Improve speed of numeric-edited formatting.

The speed of converting numerical strings to numeric-edited variables
has been improved by moving run-time PICTURE processing to compile time.

gcc/cobol/ChangeLog:

        * genapi.cc (actually_create_the_static_field): Use the
        new expand_expanded routine.
        * symbols.cc (expand_picture): Add space to accommodate the six
        additional pre-processing characters.
        (expand_expanded): Append compile-time data to the end of numeric-
        edited PICTURE strings.
        * symbols.h (redefined_token): Formatting.
        (class current_tokens_t): Formatting.
        (expand_expanded): New declaration.
        * util.cc (cbl_field_t::encode_numeric): Use expand_expanded().

libgcobol/ChangeLog:

        * libgcobol.cc (int128_to_field): Remove rdigits formal parameter.
        (__gg__int128_to_ascii_numeric_display): Likewise.
        * valconv.cc (__gg__string_to_numeric_edited): Complete rewrite to
        use parameters appended by expand_expanded().
        * valconv.h: Remove rdigits format parameter from
        __gg__int128_to_ascii_numeric_display.

gcc/testsuite/ChangeLog:

        *
cobol.dg/group2/CBL_GET_PROGRAM_INFO__get_program_name_-_functions_0_and_2
_.cob: New test.
        *
cobol.dg/group2/CBL_GET_PROGRAM_INFO__get_program_name_-_functions_0_and_2
_.out: New test.
---
 gcc/cobol/genapi.cc                           |   5 +
 gcc/cobol/symbols.cc                          | 223 ++++++++++
 gcc/cobol/symbols.h                           |  22 +-
 gcc/cobol/util.cc                             |   5 +-
 ..._get_program_name_-_functions_0_and_2_.cob |  71 ++++
 ..._get_program_name_-_functions_0_and_2_.out |   3 +
 libgcobol/libgcobol.cc                        |   8 +-
 libgcobol/valconv.cc                          | 390 +++++++-----------
 libgcobol/valconv.h                           |   1 -
 9 files changed, 466 insertions(+), 262 deletions(-)
 create mode 100644
gcc/testsuite/cobol.dg/group2/CBL_GET_PROGRAM_INFO__get_program_name_-_fun
ctions_0_and_2_.cob
 create mode 100644
gcc/testsuite/cobol.dg/group2/CBL_GET_PROGRAM_INFO__get_program_name_-_fun
ctions_0_and_2_.out

diff --git a/gcc/cobol/genapi.cc b/gcc/cobol/genapi.cc
index 5d7d553b9d8..e3753fd7709 100644
--- a/gcc/cobol/genapi.cc
+++ b/gcc/cobol/genapi.cc
@@ -14431,7 +14431,12 @@ actually_create_the_static_field( cbl_field_t
*new_var,
     }
   tree offset = build_int_cst_type(SIZE_T, new_var->offset);
   tree name = gg_string_literal(new_var->name);
+
   char *expanded = expand_picture(new_var->data.picture);
+  if( new_var->type == FldNumericEdited )
+    {
+    expand_expanded(expanded);
+    }
   tree picture = gg_string_literal(expanded);
   free(expanded);
   tree initial;
diff --git a/gcc/cobol/symbols.cc b/gcc/cobol/symbols.cc
index 6d96ba311b6..c41e0b8ff2a 100644
--- a/gcc/cobol/symbols.cc
+++ b/gcc/cobol/symbols.cc
@@ -4798,6 +4798,12 @@ expand_picture(const char *picture)
   // The caller should free() the return value.
   assert(strlen(picture) < PICTURE_MAX); // guaranteed by picset() in
scanner
   size_t retval_length = PICTURE_MAX;
+
+  // In the expand_expanded routine, we are going to tack on some
additional
+  // characters in order to speed up compile-time processin.
+  static const int PICTURE_EXTRA = 6;
+  retval_length += PICTURE_EXTRA;
+
   char *retval = static_cast<char *>(xmalloc(retval_length));
 
   int ch;
@@ -4909,9 +4915,226 @@ expand_picture(const char *picture)
         break;
       }
     }
+
+  // AD HOC FIX for an improper trailing space
+  char *pspace = strchr(retval, ascii_space);
+  if( pspace )
+    {
+    *pspace = NULLCH;
+    }
+
   return retval;
   }
 
+void
+expand_expanded(char *expanded)
+  {
+  if(strlen(expanded) == 0)
+    {
+    return;
+    }
+
+  unsigned char *dest = reinterpret_cast<unsigned char *>(expanded);
+
+  /* In order to make __gg__string_to_numeric_edited() run quickly, we
are
+     going to process the expanded picture string especially for it.
What we
+     do here:
+     
+     Convert B to space, taking care not to touch a final 'DB'
+
+     Find the currency picture symbol
+     
+     Find the span of any '$$', '++', '--' 'Z', and '*' runs.
+
+     For any '$$', '++' and '--' runs, replace the first such char with a
+     space, and all the others with '9'
+     
+     For any 'Z' and '*' runs, replace all the characters with '9'.
+     
+     Figure out if any of the original picture characters are '9'.
+     
+     That information gets encoded into six characters that are appended
to
+     the modified string.  
+     
+     Offset 0:   The currency character
+     Offset 1:   The floating character (space if empty)
+     Offset 2-3: The starting index of the float.
+     Offset 3-4: The one-past-the-end index of the float.
+
+     When there are '9' characters in the original, the 0x40 bit of
offset 2
+     is turned on, turning '0'-'9' into 'q'-'y'
+     
+     Is everybody ready?  Then we'll begin.    */
+
+  int length_d = strlen(expanded);
+
+  unsigned char currency_char = ascii_space;
+  // Note that the currency_picture can be upper- or lower-case, and mean
+  // separate things in IBM.  In ISO COBOL, the comparison is
case-insensitive.
+
+  unsigned floating_char = ascii_space;
+  int leftmost_float = -1;
+  int rightmost_float = -1;  // This is a one-past-the-end index
+
+  bool got_nines = false;
+
+  for(int i=0; i<length_d; i++)
+    {
+    int ch = (unsigned int)dest[i] & 0xFF;
+    if( !__gg__currency_signs[ch].empty() )
+      {
+      currency_char = ch;
+      break;
+      }
+    }
+
+  for(int i=0; i<length_d; i++)
+    {
+    if( dest[i] == ascii_9 )
+      {
+      got_nines = true;
+      break;
+      }
+    }
+
+  if( dest[0] == ascii_B )
+    {
+    dest[0] = ascii_space;
+    }
+  for(int i=1; i<length_d; i++)
+    {
+    if( dest[i] == ascii_B && dest[i-1] != ascii_D && dest[i-1] !=
ascii_d )
+      {
+      dest[i] = ascii_space;
+      }
+    }
+
+  if( currency_char != ascii_space )
+    {
+    leftmost_float =
+      static_cast<unsigned char *>(memchr(dest, currency_char, length_d))
- dest;
+    rightmost_float =
+      static_cast<unsigned char *>(memrchr(dest, currency_char,
length_d)) - dest + 1;
+    if( rightmost_float > leftmost_float+1 )
+      {
+      floating_char = currency_char;
+      // Turn the first floating character into a space
+      dest[leftmost_float] = ascii_space;
+      for(int i=leftmost_float+1; i<rightmost_float; i++)
+        {
+        // Of the remainder, turn all floating characters into '9', so
that
+        // they will be filled with numerical data from 'source'.
+        if( dest[i] == floating_char )
+          {
+          dest[i] = ascii_9;
+          }
+        }
+      }
+    else
+      {
+      leftmost_float = rightmost_float = -1;
+      }
+    }
+
+  if( memchr(dest, ascii_minus, length_d) )
+    {
+    int leftmost =
+      static_cast<unsigned char *>(memchr(dest, ascii_minus, length_d)) -
dest;
+    int rightmost =
+      static_cast<unsigned char *>(memrchr(dest, ascii_minus, length_d))
- dest + 1;
+    if( rightmost > leftmost+1 )
+      {
+      floating_char = ascii_minus;
+      leftmost_float = leftmost;
+      rightmost_float = rightmost;
+      for(int i=leftmost_float+1; i<rightmost_float; i++)
+        {
+        // Of the remainder, turn all floating characters into '9', so
that
+        // they will be filled with numerical data from 'source'.
+        if( dest[i] == floating_char )
+          {
+          dest[i] = ascii_9;
+          }
+        }
+      }
+    }
+
+  if( memchr(dest, ascii_plus, length_d) )
+    {
+    int leftmost =
+      static_cast<unsigned char *>(memchr(dest, ascii_plus, length_d)) -
dest;
+    int rightmost =
+      static_cast<unsigned char *>(memrchr(dest, ascii_plus, length_d)) -
dest + 1;
+    if( rightmost > leftmost+1 )
+      {
+      floating_char = ascii_plus;
+      leftmost_float = leftmost;
+      rightmost_float = rightmost;
+      for(int i=leftmost_float+1; i<rightmost_float; i++)
+        {
+        // Of the remainder, turn all floating characters into '9', so
that
+        // they will be filled with numerical data from 'source'.
+        if( dest[i] == floating_char )
+          {
+          dest[i] = ascii_9;
+          }
+        }
+      }
+    }
+
+  if( memchr(dest, ascii_asterisk, length_d) )
+    {
+    floating_char = ascii_asterisk;
+    leftmost_float =
+      static_cast<unsigned char *>(memchr(dest, ascii_asterisk,
length_d)) - dest;
+    rightmost_float =
+      static_cast<unsigned char *>(memrchr(dest, ascii_asterisk,
length_d)) - dest + 1;
+    for(int i=leftmost_float; i<rightmost_float; i++)
+      {
+      // Turn all floating characters into '9', so that
+      // they will be filled with numerical data from 'source'.
+      if( dest[i] == floating_char )
+        {
+        dest[i] = ascii_9;
+        }
+      }
+    }
+
+  if( memchr(dest, ascii_Z, length_d) )
+    {
+    floating_char = ascii_Z;
+    leftmost_float =
+      static_cast<unsigned char *>(memchr(dest, ascii_Z, length_d)) -
dest;
+    rightmost_float =
+      static_cast<unsigned char *>(memrchr(dest, ascii_Z, length_d)) -
dest + 1;
+    for(int i=leftmost_float; i<rightmost_float; i++)
+      {
+      // Turn all floating characters into '9', so that
+      // they will be filled with numerical data from 'source'.
+      if( dest[i] == floating_char )
+        {
+        dest[i] = ascii_9;
+        }
+      }
+    }
+
+  char extra[7] = "      ";
+  extra[0] = currency_char;
+  if( floating_char != ascii_space )
+    {
+    extra[1] = floating_char;
+    extra[2] = leftmost_float  / 10 + ascii_0;
+    extra[3] = leftmost_float  % 10 + ascii_0;
+    extra[4] = rightmost_float / 10 + ascii_0;
+    extra[5] = rightmost_float % 10 + ascii_0;
+    if( got_nines )
+      {
+      extra[2] |= 0x40;
+      }
+    }
+  strcat(expanded, extra);
+  }
+
 int
 length_of_picture(const char *picture)
 {
diff --git a/gcc/cobol/symbols.h b/gcc/cobol/symbols.h
index 3887b275b7b..d1864b1fdde 100644
--- a/gcc/cobol/symbols.h
+++ b/gcc/cobol/symbols.h
@@ -46,6 +46,7 @@
 #include <string>
 #include <vector>
 
+// The ISO spec says there is a
 #define PICTURE_MAX 64
 
 extern const char *numed_message;
@@ -2970,7 +2971,7 @@ int redefined_token( const cbl_name_t name, int
token );
  * intrinsic functions.
  *
  * tokens and token_names are defined in a token_names.h, generated from
parse.h.
- * 
+ *
  *  - token_names converts a yytokentype enum to a string form of
  *    its name as defined in the parser.  It is used by keyword_str.
  *  - tokens imperfectly maps a lowercase form of the token name to its
value.
@@ -2981,16 +2982,16 @@ int redefined_token( const cbl_name_t name, int
token );
  *  EQUATE     adds a new name for a token in tokens.
  *  UNDEFINE   removes a name from tokens.
  *  SUBSTITUTE is EQUATE for the first name and UNDEFINE for the second.
- *  RESERVE    adds a new name to tokens with the invalid value -42. 
+ *  RESERVE    adds a new name to tokens with the invalid value -42.
  *
  * The generated lexer of course uses static strings and is thus
unaffected by
  * COBOL-WORDS. The parser accesses the generated lexer via a mediation
layer
  * that deals with the CDF.  That is where COBOL-WORDS are applied.
  *
- * There are unsolved problems.  
- *  1.  Some tokens are lexed as more than one text word, or depend on
context. 
- *      For example OBJECT COMPUTER and IDENTIFICATION DIVISION. If the
user 
- *      substitutes COMPUTING-MACHINE COMPUTER, lexing will fail. 
+ * There are unsolved problems.
+ *  1.  Some tokens are lexed as more than one text word, or depend on
context.
+ *      For example OBJECT COMPUTER and IDENTIFICATION DIVISION. If the
user
+ *      substitutes COMPUTING-MACHINE COMPUTER, lexing will fail.
 
  *  2.  Semantic values are known only to the lexer. Some tokens carry a
value
  *      representing their context. For example, both INVALID and NOT
INVALID
@@ -3000,7 +3001,7 @@ int redefined_token( const cbl_name_t name, int
token );
  *      assign it the value "BOGUS". When that string is found by
  *      redefined_token and mapped to INVALID (so the parser can parse
it) the
  *      semantic value is lost.
- * 
+ *
  *  3.  Some names are not NAME tokens, depending on context.  That
subtlety is
  *      also lost.
  *
@@ -3105,7 +3106,7 @@ class current_tokens_t {
       return true;
     }
     /*
-     * name may be: 
+     * name may be:
      * 1.  just a name, return the input token.
      * 2.  reserved with -42 token
      * 3.  a user-defined substitute or alias for a token
@@ -3144,7 +3145,7 @@ class current_tokens_t {
     return tokens.find(name, include_intrinsics);
   }
   bool equate( const cbl_loc_t& loc, const cbl_name_t keyword, const
cbl_name_t alias ) {
-    int token; 
+    int token;
 
     if( 0 == (token = keyword_tok(keyword, true)) ) {
       if( 0 == (token = binary_integer_usage_of(keyword)) ) {
@@ -3165,7 +3166,7 @@ class current_tokens_t {
     return tokens.undefine(loc, keyword);
   }
   bool substitute( const cbl_loc_t& loc, const cbl_name_t keyword, const
cbl_name_t alias ) {
-    int token; 
+    int token;
     if( 0 == (token = keyword_tok(keyword, true)) ) {
       if( 0 == (token = binary_integer_usage_of(keyword)) ) {
        error_msg(loc, "SUBSTITUTE: not a reserved word: %qs", keyword);
@@ -3293,6 +3294,7 @@ cbl_field_t *new_alphanumeric(const cbl_name_t
name=nullptr,
                               cbl_encoding_t encoding=no_encoding_e );
 
 char *expand_picture(const char *picture);
+void expand_expanded(char *picture);
 
 // ENABLE_HIJACKING allows for code generation to be "hijacked" when the
 // program-id is "dubner_h" or "hijack_h".  See the mainline code in
genapi.cc.
diff --git a/gcc/cobol/util.cc b/gcc/cobol/util.cc
index 70122334090..e4525225981 100644
--- a/gcc/cobol/util.cc
+++ b/gcc/cobol/util.cc
@@ -1936,9 +1936,12 @@ cbl_field_t::encode_numeric( const char input[],
cbl_loc_t loc ) {
             // By the time you read this, this next statement ought to be
             // obsolete.  See RT issue 3682.
             expanded[char_capacity()] = '\0';
+            if( type == FldNumericEdited )
+              {
+              expand_expanded(expanded);
+              }
             __gg__string_to_numeric_edited( reinterpret_cast<char
*>(retval),
                                             ach,
-                                            data.rdigits,
                                             negative,
                                             expanded);
             free(expanded);
diff --git
a/gcc/testsuite/cobol.dg/group2/CBL_GET_PROGRAM_INFO__get_program_name_-_f
unctions_0_and_2_.cob
b/gcc/testsuite/cobol.dg/group2/CBL_GET_PROGRAM_INFO__get_program_name_-_f
unctions_0_and_2_.cob
new file mode 100644
index 00000000000..5b2f160397e
--- /dev/null
+++
b/gcc/testsuite/cobol.dg/group2/CBL_GET_PROGRAM_INFO__get_program_name_-_f
unctions_0_and_2_.cob
@@ -0,0 +1,71 @@
+      *> Do not edit this generated file.  See README.txt
+      *> { dg-do run }
+       *> { dg-options "-dialect mf" }
+       *> { dg-output-file
"group2/CBL_GET_PROGRAM_INFO__get_program_name_-_functions_0_and_2_.out" }
+
+        copy "cblproto.cpy".
+        identification division.
+        program-id. prog.
+        data division.
+        working-storage section.
+        copy "cbltypes.cpy".
+        01 param-block type cblt-prog-info-params value zeros.
+        77 fn pic x(4) comp-5.
+        77 status-code pic x(2) comp-5.
+        77 buf pic x(128).
+        77 buflen pic x(4) comp-5.
+        procedure division.
+          move length of param-block to cblte-gpi-size.
+
+      * allocate handle
+          move 7 to cblte-gpi-flags.
+          move 0 to fn.
+          move length of buf to buflen.
+          call "CBL_GET_PROGRAM_INFO" using
+                                by value fn
+                                by reference param-block
+                                             buf
+                                             buflen
+                                returning status-code.
+
+          if status-code <> 0
+            display "CBL_GET_PROGRAM_INFO failed with " status-code
+            goback
+          end-if.
+
+          display "buf is '" buf "'".
+
+      * retrieve program name from handle
+          move 6 to cblte-gpi-flags.
+          move 2 to fn.
+          move x"00" to buf.
+          call "CBL_GET_PROGRAM_INFO" using
+                                by value fn
+                                by reference param-block
+                                             buf
+                                             buflen
+                                returning status-code.
+
+          if status-code <> 0
+            display "CBL_GET_PROGRAM_INFO failed with " status-code
+            goback
+          end-if.
+
+          display "buf from handle is '" buf "'".
+
+      * close handle
+          move 0 to cblte-gpi-flags.
+          move 3 to fn.
+          call "CBL_GET_PROGRAM_INFO" using
+                                by value fn
+                                by reference param-block
+                                             buf
+                                             buflen
+                                returning status-code.
+
+          if status-code <> 0
+            display "CBL_GET_PROGRAM_INFO failed with " status-code
+            goback
+          end-if.
+        end program prog.
+
diff --git
a/gcc/testsuite/cobol.dg/group2/CBL_GET_PROGRAM_INFO__get_program_name_-_f
unctions_0_and_2_.out
b/gcc/testsuite/cobol.dg/group2/CBL_GET_PROGRAM_INFO__get_program_name_-_f
unctions_0_and_2_.out
new file mode 100644
index 00000000000..c86d093d767
--- /dev/null
+++
b/gcc/testsuite/cobol.dg/group2/CBL_GET_PROGRAM_INFO__get_program_name_-_f
unctions_0_and_2_.out
@@ -0,0 +1,3 @@
+buf is 'prog'
+buf from handle is 'prog'
+
diff --git a/libgcobol/libgcobol.cc b/libgcobol/libgcobol.cc
index f436e822889..4332d7ddee0 100644
--- a/libgcobol/libgcobol.cc
+++ b/libgcobol/libgcobol.cc
@@ -2140,7 +2140,6 @@ int128_to_field(cblc_field_t   *var,
               // Convert that string according to the PICTURE clause
               __gg__string_to_numeric_edited(as_chars(location),
                                              ach,
-                                             target_rdigits,
                                              is_negative,
                                              var->picture);
               size_t outlength;
@@ -2309,10 +2308,9 @@ __gg__int128_to_ascii_numeric_display(const
cblc_field_t  *var,
 
     // Convert that string according to the PICTURE clause
     __gg__string_to_numeric_edited(as_chars(location),
-                                       ach,
-                                       var->rdigits,
-                                       is_negative,
-                                       var->picture);
+                                   ach,
+                                   is_negative,
+                                   var->picture);
     }
   return size_error;
   }
diff --git a/libgcobol/valconv.cc b/libgcobol/valconv.cc
index 21e89f437dd..83bdaf09cba 100644
--- a/libgcobol/valconv.cc
+++ b/libgcobol/valconv.cc
@@ -138,12 +138,12 @@ __gg__alphabet_create(  cbl_encoding_t encoding,
 
 extern "C"
 void
-__gg__string_to_numeric_edited( char * const dest,
+__gg__string_to_numeric_edited( char * const dest_,
                                 const char *source,     // In source
characters
-                                int /*rdigits*/,
                                 int is_negative,
                                 const char *picture)
   {
+  unsigned char *dest = reinterpret_cast<unsigned char *>(dest_);
   // This routine operates in ASCII space.  Life is hard enough without
trying
   // to do this in EBCDIC, too.  So, 'source' and 'picture' are assumed
to be
   // CP1252
@@ -159,8 +159,14 @@ __gg__string_to_numeric_edited( char * const dest,
 
   // Make a copy of the PICTURE string in the destination space.  The
picture
   // is supposed to be the same length as the char_capacity of the
variable.
-  const int length_s = strlen(source);
-  const int length_d = strlen(picture);
+
+  // We subtract the length of the extra characters that were appended to
the
+  // adjusted PICTURE string.
+
+  static const int MAX_POSSIBLE = 64 + 6; // See PICTURE_MAX in symbols.h
+
+  const int length_d = strlen(picture) - 6;
+  assert(length_d < MAX_POSSIBLE && length_d >= 0);
   memcpy(dest, picture, length_d);
   /* We have two kinds of floating insertion:  currency and sign.
 
@@ -183,105 +189,72 @@ __gg__string_to_numeric_edited( char * const dest,
 
   // We need to know if we have a currency picture symbol in this string:
   // This is the currency character in the PICTURE
-  unsigned char currency_char = NULLCH;
+  unsigned char currency_char;
   const char *currency_text = NULL;   // This is the text we output when
   //                                  // encountering the
currency_picture
   //                                  // character
   // Note that the currency_picture can be upper- or lower-case, and mean
   // separate things in IBM.  In ISO COBOL, the comparison is
case-insensitive.
 
-  // This is the character for a floating sign
-  char sign_char;
-
-  // In the following limits, all of the rightmosts are past-the-end
indexes
-
-  int leftmost_currency = -1;
-  int rightmost_currency = -1;
-
-  int leftmost_sign = -1;
-  int rightmost_sign = -1;
-
-  int leftmost_asterisk = -1;
-  int rightmost_asterisk = -1;
-  int zeroed_asterisk = 0;
-
-  int leftmost_z = -1;
-  int rightmost_z = -1;
-  int zeroed_z = 0;
-
+  unsigned char floating_char;
+  int leftmost_float = -1;
+  int rightmost_float = -1;  // This is a one-past-the-end index
+  bool got_nines = false;
   int leftmost_nonzero = -1;
-
   int decimal_position = -1;
 
-  // This is the first pass.  Because the currency
+  unsigned const char *extra =
+                    reinterpret_cast<unsigned const char
*>(picture+length_d);
+  currency_char = extra[0];
+  currency_text = __gg__currency_signs[currency_char].c_str();
+
+  floating_char = extra[1] != ascii_space ? extra[1] : NULLCH;
+  if( floating_char != ascii_space )
+    {
+    leftmost_float  = (extra[2] & 0xF) * 10 + (extra[3] & 0xF);
+    rightmost_float = (extra[4] & 0xF) * 10 + (extra[5] & 0xF);
+    }
+  got_nines = !!(extra[2] & 0x40);
+
   int index_s = 0;
   for(int i=0; i<length_d; i++)
     {
-    int ch = (unsigned int)dest[i] & 0xFF;
-    if( ! __gg__currency_signs[ch].empty() )
-      {
-      currency_char = ch;
-      currency_text = __gg__currency_signs[ch].c_str();
-      if( leftmost_currency == -1 )
-        {
-        leftmost_currency = i;
-        }
-      else
-        {
-        // This is not the leftmost currency character, so it is where a
digit
-        // goes.
-        dest[i] = source[index_s++];
-        if( leftmost_nonzero == -1 && dest[i] != ascii_zero )
-          {
-          leftmost_nonzero = i;
-          }
-        }
-      rightmost_currency = i+1;
-      }
+    unsigned char ch = dest[i];
+
     if( ch == currency_char )
       {
-      // We have already handled the currency char.
-      continue;
+      // Arriving here means we have hit a singleton currency character.
Since
+      // it is not part of a floating currency stream, the currency text
goes
+      // here.
+      memcpy(dest + i - (strlen(currency_text)-1),
+             currency_text,
+             strlen(currency_text));
       }
+
     switch(ch)
       {
       case ascii_minus:
       case ascii_plus:
-        {
+        // Arriving here means we have hit a singleton sign character
+        unsigned char sign_char;
         if( is_negative )
           {
           sign_char = ascii_minus;
           }
         else
           {
-          if( ch == ascii_plus )
+          if( ch == ascii_minus )
             {
-            sign_char = ascii_plus;
+            sign_char = ascii_space;
             }
           else
             {
-            sign_char = ascii_space;
+            sign_char = ascii_plus;
             }
           }
-        dest[i] = sign_char;
 
-        if( leftmost_sign == -1 )
-          {
-          leftmost_sign = i;
-          }
-        else
-          {
-          // This is not the leftmost sign character, so it is where a
digit
-          // goes.
-          dest[i] = source[index_s++];
-          if( leftmost_nonzero == -1 && dest[i] != ascii_zero )
-            {
-            leftmost_nonzero = i;
-            }
-          }
-        rightmost_sign = i+1;
+        dest[i] = sign_char;
         break;
-        }
 
       case ascii_comma:
       case ascii_period:
@@ -289,36 +262,6 @@ __gg__string_to_numeric_edited( char * const dest,
           {
           decimal_position = i;
           }
-      break;
-
-      case ascii_slash:
-      case ascii_d:
-      case ascii_D:
-      case ascii_b:
-      case ascii_c:
-      case ascii_C:
-      case ascii_r:
-      case ascii_R:
-      case ascii_0:
-        // These are left where they are
-        break;
-
-      case ascii_B:
-        // This needs some special attention, because a DB at the end is
not
-        // the same as a B on its own at the end.
-        if( i < length_d-1 )
-          {
-          dest[i] = ascii_space;
-          }
-        else if( length_d >= 1 )
-          {
-          if( dest[i-1] != ascii_D && dest[i-1] != ascii_d )
-            {
-            // This is an isolated B at the very end of the string
-            dest[i] = ascii_space;
-            }
-          // Otherwise, that final B is part of a DB, and is left alone.
-          }
         break;
 
       case ascii_9:
@@ -329,175 +272,130 @@ __gg__string_to_numeric_edited( char * const dest,
           leftmost_nonzero = i;
           }
         break;
-
-      case ascii_asterisk:
-        // These are positions that hold digits
-        if( leftmost_asterisk == -1 )
-          {
-          leftmost_asterisk = i;
-          }
-        rightmost_asterisk = i+1;
-
-        dest[i] = source[index_s++];
-        if( dest[i] == ascii_zero )
-          {
-          zeroed_asterisk += 1;
-          }
-        else
-          {
-          if( leftmost_nonzero == -1 )
-            {
-            leftmost_nonzero = i;
-            }
-          }
-        break;
-
-      case ascii_Z:
-        // These are positions that hold digits
-        if( leftmost_z == -1 )
-          {
-          leftmost_z = i;
-          }
-        rightmost_z = i+1;
-
-        dest[i] = source[index_s++];
-        if( dest[i] == ascii_zero )
-          {
-          zeroed_z += 1;
-          }
-        else
-          {
-          if( leftmost_nonzero == -1 )
-            {
-            leftmost_nonzero = i;
-            }
-          }
-        break;
-
-      default:
-        abort();
       }
     }
 
   // Do currency replacement
-  if( leftmost_currency >= 0 )
+  if( floating_char == currency_char )
     {
-    if( leftmost_currency == rightmost_currency-1 )
+    // This is a floating currency symbol.  We need to start at
+    // leftmost_float, and walk to the lesser of rightmost_float and
+    // leftmost_nonzero.  We blank every character we encounter until we
+    // reach the first non-zero character.
+    int left = leftmost_float;
+    int right;
+    if( leftmost_nonzero >= 0 )
       {
-      // This is a solo currency symbol
-      memcpy(dest + leftmost_currency - (strlen(currency_text)-1),
-             currency_text,
-             strlen(currency_text));
+      right = std::min(leftmost_nonzero, rightmost_float);
+      if( decimal_position >= 0 )
+        {
+        right = std::min(right, decimal_position);
+        }
       }
     else
       {
-      // This is a floating currency symbol.  We need to start at
-      // leftmost_currency, and walk to the lesser of rightmost_currency
and
-      // leftmost_nonzero.  We blank every character we encounter.
-      int left = leftmost_currency;
-      int right;
-      if( leftmost_nonzero >= 0 )
-        {
-        right = std::min(leftmost_nonzero, rightmost_currency);
-        if( decimal_position >= 0 )
-          {
-          right = std::min(right, decimal_position);
-          }
-        }
-      else
-        {
-        right = rightmost_currency;
-        }
-      // blank out that range
-      memset(dest+left, ascii_space, right-left);
+      right = rightmost_float;
+      }
+    // blank out that range
+    assert(right-left < MAX_POSSIBLE && right-left >= 0);
+    memset(dest+left, ascii_space, right-left);
 
-      // To handle situations like PIC $$$,999, where the currency sign
has to
-      // overwrite the comma, we walk from here until we hit the column
just to
-      // the left of a digit
-      while( right < length_d )
-        {
+    // To handle situations like PIC $$$,999, where the currency sign has
to
+    // overwrite the comma, we walk from here until we hit the column
just to
+    // the left of a digit
+    while( right < length_d )
+      {
       if(    (dest[right] >= ascii_zero && dest[right] <= ascii_nine)
-          || right == decimal_position ) 
-          {
-          break;
-          }
-        dest[right] = ascii_space;
-        right += 1;
-        }
-      if( right < length_d )
+        || right == decimal_position )
         {
-        right -= 1;
-        memcpy(dest + right - (strlen(currency_text)-1),
-               currency_text,
-               strlen(currency_text));
+        break;
         }
+      dest[right] = ascii_space;
+      right += 1;
+      }
+    if( right < length_d )
+      {
+      right -= 1;
+      memcpy(dest + right - (strlen(currency_text)-1),
+             currency_text,
+             strlen(currency_text));
       }
     }
 
   // Do sign replacement
-  if( leftmost_sign >= 0 )
+  if( floating_char == ascii_plus || floating_char == ascii_minus )
     {
-    if( leftmost_sign == rightmost_sign-1 )
+    // This is a floating sign symbol.  We need to start at
+    // leftmost_float, and walk to the lesser of rightmost_float and
+    // leftmost_nonzero.  We blank every character we encounter.
+    unsigned char sign_char;
+    if( is_negative )
       {
-      // This is a solo sign symbol
-      dest[leftmost_sign] = sign_char;
+      sign_char = ascii_minus;
       }
     else
       {
-      // This is a floating sign symbol.  We need to start at
-      // leftmost_sign, and walk to the lesser of rightmost_sign and
-      // leftmost_nonzero.  We blank every character we encounter.
-      int left = leftmost_sign;
-      int right;
-      if( leftmost_nonzero >= 0 )
+      if( floating_char == ascii_minus )
         {
-        right = std::min(leftmost_nonzero, rightmost_sign);
-        if( decimal_position >= 0 )
-          {
-          right = std::min(right, decimal_position);
-          }
+        sign_char = ascii_space;
         }
       else
         {
-        right = rightmost_sign;
+        sign_char = ascii_plus;
         }
-      // blank out that range
-      memset(dest+left, ascii_space, right-left);
+      }
 
-      // To handle situations like PIC $$$,999, where the sign char has
to
-      // overwrite the comma, we walk from here until we hit the column
just to
-      // the left of a digit
-      while( right < length_d )
+    int left = leftmost_float;
+    int right;
+    if( leftmost_nonzero >= 0 )
+      {
+      right = std::min(leftmost_nonzero, rightmost_float);
+      if( decimal_position >= 0 )
         {
-        if(    (dest[right] >= ascii_zero && dest[right] <= ascii_nine)
-            || right == decimal_position ) 
-          {
-          break;
-          }
-        dest[right] = ascii_space;
-        right += 1;
+        right = std::min(right, decimal_position);
         }
-      if( right < length_d )
+      }
+    else
+      {
+      right = rightmost_float;
+      }
+    // blank out that range
+    assert(right-left < MAX_POSSIBLE && right-left >= 0);
+    memset(dest+left, ascii_space, right-left);
+
+    // To handle situations like PIC $$$,999, where the sign char has to
+    // overwrite the comma, we walk from here until we hit the column
just to
+    // the left of a digit
+    while( right < length_d )
+      {
+      if(    (dest[right] >= ascii_zero && dest[right] <= ascii_nine)
+          || right == decimal_position )
         {
-        right -= 1;
-        dest[right] = sign_char;
+        break;
         }
+      dest[right] = ascii_space;
+      right += 1;
+      }
+    if( right < length_d )
+      {
+      right -= 1;
+      dest[right] = sign_char;
       }
     }
 
   // Do '*' zero suppression
-  if( leftmost_asterisk >= 0 )
+  if( floating_char == ascii_asterisk )
     {
-    // This is a floating zero suppression.  
+    // This is a floating zero suppression.
 
-    int left = leftmost_asterisk;
+    int left = leftmost_float;
     int right;
     if( leftmost_nonzero >= 0 )
       {
       // The value being formatted is non-zero, so make sure 'right' is
no more
       // than the decimal position.
-      right = std::min(leftmost_nonzero, rightmost_asterisk);
-      if( decimal_position >= 0 && decimal_position > leftmost_asterisk )
+      right = std::min(leftmost_nonzero, rightmost_float);
+      if( decimal_position >= 0 && decimal_position > leftmost_float )
         {
         right = std::min(right, decimal_position);
         }
@@ -505,19 +403,19 @@ __gg__string_to_numeric_edited( char * const dest,
     else
       {
       // The value being formatted is zero
-      if( zeroed_asterisk == length_s )
+      if( leftmost_nonzero == -1 && !got_nines )
         {
-        // Every numeric position was a zero on asterisk, so the whole
thing
-        // is starred.
+        // Every numeric position was a zero, and there were no explicit
nines
         left = 0;
         right = length_d;
         }
       else
         {
-        right = rightmost_asterisk;
+        right = rightmost_float;
         }
       }
-    // blank out that range
+    // Fill that range with stars
+    assert(right-left < MAX_POSSIBLE && right-left >= 0);
     memset(dest+left, ascii_asterisk, right-left);
 
     // To handle situations like PIC ZZZ,999, where the suppression char
has to
@@ -526,7 +424,7 @@ __gg__string_to_numeric_edited( char * const dest,
     while( right < length_d )
       {
       if(    (dest[right] >= ascii_zero && dest[right] <= ascii_nine)
-          || right == decimal_position ) 
+          || right == decimal_position )
         {
         break;
         }
@@ -543,37 +441,38 @@ __gg__string_to_numeric_edited( char * const dest,
     }
 
   // Do 'Z' zero suppression
-  if( leftmost_z >= 0 )
+  if( floating_char == ascii_Z )
     {
-    // This is a floating zero suppression.  
+    // This is a floating zero suppression.
 
-    int left = leftmost_z;
+    int left = leftmost_float;
     int right;
     if( leftmost_nonzero >= 0 )
       {
       // The value being formatted is non-zero, so make sure 'right' is
no more
       // than the decimal position.
-      right = std::min(leftmost_nonzero, rightmost_z);
-      if( decimal_position >= 0 && decimal_position > leftmost_asterisk )
+      right = std::min(leftmost_nonzero, rightmost_float);
+      if( decimal_position >= 0 && decimal_position > leftmost_float )
         {
         right = std::min(right, decimal_position);
         }
       }
     else
       {
-      if( zeroed_z == length_s )
+      if( leftmost_nonzero == -1 && !got_nines )
         {
-        // Every numeric position was a zero on asterisk, so the whole
thing
+        // Every numeric position was a zero on 'Z', so the whole thing
         // is blanked.
         left = 0;
         right = length_d;
         }
       else
         {
-        right = rightmost_z;
+        right = rightmost_float;
         }
       }
     // blank out that range
+    assert(right-left < MAX_POSSIBLE && right-left >= 0);
     memset(dest+left, ascii_space, right-left);
 
     // To handle situations like PIC ZZZ,999, where the suppression char
has to
@@ -582,7 +481,7 @@ __gg__string_to_numeric_edited( char * const dest,
     while( right < length_d )
       {
       if(    (dest[right] >= ascii_zero && dest[right] <= ascii_nine)
-          || right == decimal_position ) 
+          || right == decimal_position )
         {
         break;
         }
@@ -591,10 +490,11 @@ __gg__string_to_numeric_edited( char * const dest,
       }
     }
 
-  // Any final DB/CR needs to be addressed:
-  if(    !is_negative
-      && length_d >= 2 
-      && (   dest[length_d-2] == ascii_D 
+  // Any final DB/CR needs to be addressed.  If the number is positive,
or
+  // all the digits were zeroes, then blank any CR/DB at the end:
+  if(     (!is_negative || leftmost_nonzero < 0)
+      && length_d >= 2
+      && (   dest[length_d-2] == ascii_D
           || dest[length_d-2] == ascii_d
           || dest[length_d-2] == ascii_C
           || dest[length_d-2] == ascii_c ) )
diff --git a/libgcobol/valconv.h b/libgcobol/valconv.h
index 62ee342c2b0..cbd2a2194f5 100644
--- a/libgcobol/valconv.h
+++ b/libgcobol/valconv.h
@@ -55,7 +55,6 @@ extern "C"
                               int high_char );
   void __gg__string_to_numeric_edited(char * const dest,
                                       const char *source,       // ASCII
-                                      int rdigits,
                                       int is_negative,
                                       const char *picture);
   void __gg__string_to_alpha_edited(char *dest,
-- 
2.34.1

Reply via email to