From 741b170ff37b25055c72a059e805222fc333d139 Mon Sep 17 00:00:00 2001
From: Bob Dubner <rdubner@symas.com>
Date: Mon, 18 Dec 2023 20:57:49 -0500
Subject: [PATCH] Polished TEST-FORMATTED-DATETIME

---
 libgcobol/intrinsic.cc | 161 +++++++++++++++++++++++++++++++++++++----
 1 file changed, 147 insertions(+), 14 deletions(-)

diff --git a/libgcobol/intrinsic.cc b/libgcobol/intrinsic.cc
index 8db085774726..df98b3433bfb 100644
--- a/libgcobol/intrinsic.cc
+++ b/libgcobol/intrinsic.cc
@@ -3323,13 +3323,14 @@ __gg__year_to_yyyy( cblc_field_t *dest,
 
 static
 int
-gets_int(int digits, char *p, char *pend)
+gets_int(int ndigits, char *p, char *pend, int *digits)
   {
   // This routine returns the value of the integer at p.  If there is something
   // wrong with the integer, it returns a negative number, the value being the
   // position (starting at 1) where the problem is.
   int retval = 0;
-  for(int i=1; i<=digits; i++)
+  memset(digits, 0xFF, ndigits * sizeof(int));
+  for(int i=1; i<=ndigits; i++)
     {
     if(p >= pend)
       {
@@ -3345,7 +3346,8 @@ gets_int(int digits, char *p, char *pend)
       break;
       }
     retval *= 10;
-    retval += ch - internal_0;
+    retval += ch & 0xF;
+    digits[i-1] = ch & 0xF;
     }
   return retval;
   }
@@ -3361,7 +3363,29 @@ gets_year(char *p, char *pend, struct cobol_tm &ctm)
   // where a four-character range with a year value of 1601 became impossible.
 
   int retval = 0;
-  int YYYY = gets_int(4, p, pend);
+  int digits[4];
+  int YYYY = gets_int(4, p, pend, digits);
+
+  if( digits[0] == -1 || digits[0] == 0 )
+    {
+    return 1;
+    }
+  if( digits[1] == -1 )
+    {
+    return 2;
+    }
+  if( digits[0] == 0 && digits[1] < 5)
+    {
+    return 2;
+    }
+  if( digits[2] == -1 )
+    {
+    return 4;
+    }
+  if( digits[3] == -1 )
+    {
+    return 4;
+    }
 
   if( YYYY >= 0 )
     {
@@ -3405,8 +3429,18 @@ gets_month(char *p, char *pend, struct cobol_tm &ctm)
   // Returns either zero, or else the ordinal position of where the input
   // processing failed.
 
+  int digits[2];
   int retval = 0;
-  int MM = gets_int(2, p, pend);
+  int MM = gets_int(2, p, pend, digits);
+
+  if( digits[0] == -1 || digits[0] > 1)
+    {
+    return 1;
+    }
+  if( digits[1] == -1 )
+    {
+    return 2;
+    }
   if( MM >= 0 )
     {
     if( MM == 0 )
@@ -3441,8 +3475,18 @@ gets_day(char *p, char *pend, struct cobol_tm &ctm)
 
   // The assumption is that YYYY and MM were populated before arriving here
 
+  int digits[2];
   int retval = 0;
-  int DD = gets_int(2, p, pend);
+  int DD = gets_int(2, p, pend, digits);
+
+  if( digits[0] == -1 || digits[0] > 3)
+    {
+    return 1;
+    }
+  if( digits[1] == -1 )
+    {
+    return 2;
+    }
   if(DD >= 0)
     {
     if( DD >= 0 )
@@ -3501,7 +3545,8 @@ gets_day_of_week(char *p, char *pend, struct cobol_tm &ctm)
   {
   // This is just a simple D, for day-of-week.  The COBOL spec is that
   // it be 1 to 7, 1 being Monday
-  int day_of_week = gets_int(1, p, pend);
+  int digits[1];
+  int day_of_week = gets_int(1, p, pend, digits);
   if( day_of_week<0 || day_of_week >7)
     {
     // The single character at source is no good:
@@ -3548,7 +3593,20 @@ int
 gets_day_of_year(char *p, char *pend, struct cobol_tm &ctm)
   {
   // This is a three-digit day-of-year, 001 through 365,366
-  int DDD = gets_int(3, p, pend);
+  int digits[3];
+  int DDD = gets_int(3, p, pend, digits);
+  if( digits[0] == -1 || digits[0] > 3)
+    {
+    return 1;
+    }
+  if( digits[1] == -1 )
+    {
+    return 2;
+    }
+  if( digits[2] == -1 )
+    {
+    return 3;
+    }
   if( DDD < 0 )
     {
     return -DDD;
@@ -3592,7 +3650,16 @@ int
 gets_week(char *p, char *pend, struct cobol_tm &ctm)
   {
   // This is a two-digit value, 01 through 52,53
-  int ww = gets_int(2, p, pend);
+  int digits[2];
+  int ww = gets_int(2, p, pend, digits);
+  if( digits[0] == -1 || digits[0] > 5 )
+    {
+    return 1;
+    }
+  if( digits[1] == -1 )
+    {
+    return 2;
+    }
   if( ww < 0 )
     {
     return -ww;
@@ -3623,7 +3690,18 @@ int
 gets_hours(char *p, char *pend, struct cobol_tm &ctm, bool in_offset)
   {
   // This is a two-digit value, 01 through 23
-  int hh = gets_int(2, p, pend);
+  int digits[2];
+  int hh = gets_int(2, p, pend, digits);
+  
+  if( digits[0] == -1 || digits[0] > 2 )
+    {
+    return 1;
+    }
+  if( digits[1] == -1 )
+    {
+    return 2;
+    }
+  
   if( hh < 0 )
     {
     return -hh;
@@ -3657,7 +3735,17 @@ int
 gets_minutes(char *p, char *pend, struct cobol_tm &ctm, bool in_offset)
   {
   // This is a two-digit value, 01 through 59
-  int mm = gets_int(2, p, pend);
+  int digits[2];
+  int mm = gets_int(2, p, pend, digits);
+  if( digits[0] == -1 || digits[0] > 5 )
+    {
+    return 1;
+    }  
+  if( digits[1] == -1 )
+    {
+    return 2;
+    }  
+
   if( mm < 0 )
     {
     return -mm;
@@ -3685,7 +3773,16 @@ int
 gets_seconds(char *p, char *pend, struct cobol_tm &ctm)
   {
   // This is a two-digit value, 01 through 59
-  int ss = gets_int(2, p, pend);
+  int digits[2];
+  int ss = gets_int(2, p, pend, digits);
+  if( digits[0] == -1 || digits[0] > 5 )
+    {
+    return 1;
+    }  
+  if( digits[1] == -1 )
+    {
+    return 2;
+    }  
   if( ss < 0 )
     {
     return -ss;
@@ -3797,10 +3894,37 @@ fill_cobol_tm(cobol_tm &ctm,
     if( ch == internal_plus )
       {
       // This flags a following hhmm offset.  It needs to match a '+' or '-'
-      if( *source != internal_plus && *source != internal_minus )
+      if(    *source != internal_plus
+          && *source != internal_minus
+          && *source != internal_zero)
         {
         break;
         }
+      if( *source == internal_zero )
+        {
+        // The next four characters have to be zeroes
+        if( source[1] != internal_zero )
+          {
+          retval += 1;
+          break;
+          }
+        if( source[2] != internal_zero )
+          {
+          retval += 2;
+          break;
+          }
+        if( source[3] != internal_zero )
+          {
+          retval += 3;
+          break;
+          }
+        if( source[4] != internal_zero )
+          {
+          retval += 4;
+          break;
+          }
+        }
+
       in_offset = true;
       bump = 1;
       goto proceed;
@@ -3945,6 +4069,13 @@ fill_cobol_tm(cobol_tm &ctm,
 
     if( ch == internal_Z || ch == internal_z )
       {
+      // This has to be the end of the road
+      if( toupper(source[0]) != 'Z' ) 
+        {
+        retval += 0;
+        break;
+        }
+      
       convert_to_zulu(ctm);
       bump = 1;
       goto proceed;
@@ -3958,10 +4089,12 @@ proceed:
     source += bump;
     }
 
-  if( format >= format_end )
+  if( format >= format_end && source >= source_end)
     {
     // This means we processed the entire format string without seeing an error
     retval = 0;
+    
+    // Otherwise, either the format or source was too short
     }
   return retval;
   }
-- 
GitLab