mirror of git://gcc.gnu.org/git/gcc.git
5705 lines
150 KiB
C++
5705 lines
150 KiB
C++
/*
|
||
* Copyright (c) 2021-2025 Symas Corporation
|
||
*
|
||
* Redistribution and use in source and binary forms, with or without
|
||
* modification, are permitted provided that the following conditions are
|
||
* met:
|
||
*
|
||
* * Redistributions of source code must retain the above copyright
|
||
* notice, this list of conditions and the following disclaimer.
|
||
* * Redistributions in binary form must reproduce the above
|
||
* copyright notice, this list of conditions and the following disclaimer
|
||
* in the documentation and/or other materials provided with the
|
||
* distribution.
|
||
* * Neither the name of the Symas Corporation nor the names of its
|
||
* contributors may be used to endorse or promote products derived from
|
||
* this software without specific prior written permission.
|
||
*
|
||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
*/
|
||
|
||
/* COBOL intrinsic functions.
|
||
*
|
||
* In general, the parameters to these functions are cblc_field_t pointers
|
||
* along with an offset, size, and for some functions the "allflags", which
|
||
* indicate that the variable is a table that was referenced as TABL(ALL)
|
||
*/
|
||
|
||
#include <langinfo.h>
|
||
|
||
#include <cctype>
|
||
#include <cmath>
|
||
#include <cstring>
|
||
#include <ctime>
|
||
|
||
#include <algorithm>
|
||
#include <vector>
|
||
|
||
#include "config.h"
|
||
#include "libgcobol-fp.h"
|
||
|
||
#include "ec.h"
|
||
#include "common-defs.h"
|
||
#include "io.h"
|
||
#include "gcobolio.h"
|
||
#include "libgcobol.h"
|
||
#include "charmaps.h"
|
||
|
||
|
||
#pragma GCC diagnostic ignored "-Wformat-truncation"
|
||
|
||
#define JD_OF_1601_01_02 2305812.5
|
||
|
||
#define WEIRD_TRANSCENDENT_RETURN_VALUE GCOB_FP128_LITERAL (0.0)
|
||
#define NO_RDIGITS (0)
|
||
|
||
struct cobol_tm
|
||
{
|
||
int YYYY; // 1601-9999
|
||
int MM; // 01-12
|
||
int DD; // 01-28,29,30,31
|
||
int hh; // 00-23
|
||
int mm; // 00-59
|
||
int ss; // 00-59
|
||
int nanoseconds; // 0 through 999,999,999
|
||
int tz_offset; // +/- 1359
|
||
int week_of_year; // 01 - 52,53
|
||
int day_of_year; // 001-365, 366
|
||
int day_of_week; // 0-6; 0 being Monday
|
||
int days_in_year; // 365,366
|
||
int weeks_in_year; // 52,53
|
||
int ZZZZ; // Alternate year, when Jan 4 is Mon, Tue, or Wednesday
|
||
};
|
||
|
||
static int is_leap_year(int);
|
||
|
||
typedef char * PCHAR;
|
||
|
||
static void
|
||
trim_trailing_spaces(PCHAR left, PCHAR &right, int mapped_space)
|
||
{
|
||
while( right > left )
|
||
{
|
||
if( *(right-1) != mapped_space )
|
||
{
|
||
break;
|
||
}
|
||
right -= 1;
|
||
}
|
||
}
|
||
|
||
static bool
|
||
is_zulu_format(PCHAR left, PCHAR &right, charmap_t *charmap)
|
||
{
|
||
int char_Z = charmap->mapped_character(ascii_Z);
|
||
bool retval = false;
|
||
if( right > left )
|
||
{
|
||
retval = std::toupper((unsigned char)*(right-1)) == char_Z;
|
||
}
|
||
return retval;
|
||
}
|
||
|
||
static double
|
||
YMD_to_JD(int Y, int M, int D)
|
||
{
|
||
// Calculates the Julian Day
|
||
|
||
if( M <= 2 )
|
||
{
|
||
Y -= 1 ;
|
||
M += 12;
|
||
}
|
||
double A = floor(Y/100.);
|
||
double B = 2. - A + floor(A/4.);
|
||
|
||
double JD;
|
||
JD = floor(365.25 * double(Y + 4716) + floor((30.6001 * double(M+1)))) + D + B -1524.5 ;
|
||
|
||
return JD;
|
||
}
|
||
|
||
static void
|
||
JD_to_YMD(int &YY, int &MM, int &DD, double JD)
|
||
{
|
||
JD += 0.5;
|
||
double Z = floor(JD);
|
||
double F = JD - Z;
|
||
double A;
|
||
if( Z < 2299161.0 )
|
||
{
|
||
A = Z;
|
||
}
|
||
else
|
||
{
|
||
double alpha = floor( (Z-1867216.25) / 36524.25 ) ;
|
||
A = Z + 1.0 + alpha - floor(alpha/4.0);
|
||
}
|
||
double B = A + 1524;
|
||
double C = floor( (B - 122.1)/365.25 );
|
||
double D = floor( 365.25 * C );
|
||
double E = floor( (B-D)/30.6001 );
|
||
|
||
DD = (int)( B - D - floor(30.6001 * E) + F );
|
||
MM = (int)( E < 14 ? E - 1 : E - 13 );
|
||
YY = (int)( MM > 2 ? C - 4716 : C - 4715 );
|
||
}
|
||
|
||
static int
|
||
JD_to_DOW(double JD)
|
||
{
|
||
// Converts a Julian Day to 0 through 6, where
|
||
// 0 is Monday.
|
||
// 2415020.50000 is noon on 1900-01-01, which was a Monday
|
||
return ((int)(JD-0.5)+1)%7;
|
||
}
|
||
|
||
#define DATE_STRING_BUFFER_SIZE 23
|
||
|
||
static
|
||
char *
|
||
timespec_to_string(char *retval, struct cbl_timespec &tp)
|
||
{
|
||
/*
|
||
Returns a 21-character string:
|
||
|
||
1 - 4 Four numeric digits of the year in the Gregorian calendar
|
||
5 - 6 Two numeric digits of the month of the year, in the range 01 through 12
|
||
7 - 8 Two numeric digits of the day of the month, in the range 01 through 31
|
||
9 - 10 Two numeric digits of the hours past midnight, in the range 00 through 23
|
||
11 - 12 Two numeric digits of the minutes past the hour, in the range 00 through 59
|
||
13 - 14 Two numeric digits of the seconds past the minute, in the range 00 through 59
|
||
15 - 16 Two numeric digits of the hundredths of a second past the second, in the range
|
||
17 Either the character '-' or the character '+'.
|
||
18 - 19 If character position 17 is '-', two numeric digits are returned in the range 00
|
||
through 12 indicating the number of hours that the reported time is behind
|
||
Greenwich mean time.
|
||
|
||
If character position 17 is '+', two numeric digits are
|
||
returned in the range 00 through 13 indicating the number of hours that the
|
||
reported time is ahead of Greenwich mean time. If character position 17 is '0', the
|
||
value 00 is returned.
|
||
20 - 21 Two numeric digits are returned in the range 00 through 59 indicating the number
|
||
of additional minutes that the reported time is ahead of or behind Greenwich
|
||
mean time, depending on whether character position 17
|
||
*/
|
||
|
||
const int size_of_buffer = DATE_STRING_BUFFER_SIZE;
|
||
const int offset_to_hundredths = 14;
|
||
const long nanoseconds_to_hundredths = 10000000;
|
||
|
||
// Convert the nanosecond fraction to hundredths of a second:
|
||
char achCentiseconds[3];
|
||
snprintf(achCentiseconds, 3, "%2.2ld", (tp.tv_nsec/nanoseconds_to_hundredths) );
|
||
|
||
// Convert the epoch seconds to broken-down time:
|
||
struct tm tm = {};
|
||
|
||
if( false )
|
||
{
|
||
// With a forced date/time, eliminate local influences
|
||
gmtime_r(&tp.tv_sec, &tm);
|
||
}
|
||
else
|
||
{
|
||
localtime_r(&tp.tv_sec, &tm);
|
||
}
|
||
|
||
// Format the time as per COBOL specifications, leaving two spaces for the
|
||
// hundredths of seconds:
|
||
strftime(retval, size_of_buffer, "%Y%m%d%H%M%S %z", &tm);
|
||
|
||
// Copy the 100ths into place:
|
||
memcpy(retval+offset_to_hundredths, achCentiseconds, 2);
|
||
|
||
return retval;
|
||
}
|
||
|
||
static
|
||
void
|
||
string_to_dest(cblc_field_t *dest, const char *psz)
|
||
{
|
||
size_t dest_length = dest->capacity;
|
||
size_t source_length = strlen(psz);
|
||
size_t length = std::min(dest_length, source_length);
|
||
charmap_t *charmap = __gg__get_charmap(dest->encoding);
|
||
memset(dest->data, charmap->mapped_character(ascii_space), dest_length);
|
||
memcpy(dest->data, psz, length);
|
||
}
|
||
|
||
struct input_state
|
||
{
|
||
size_t nsubscript;
|
||
bool *subscript_alls;
|
||
size_t *subscripts;
|
||
size_t *subscript_limits;
|
||
bool done;
|
||
|
||
void allocate(size_t N)
|
||
{
|
||
nsubscript = N;
|
||
if(N)
|
||
{
|
||
subscript_alls = static_cast<bool *>(malloc(nsubscript));
|
||
subscripts = static_cast<size_t *>(malloc(nsubscript));
|
||
subscript_limits = static_cast<size_t *>(malloc(nsubscript));
|
||
massert(subscript_alls);
|
||
massert(subscripts);
|
||
massert(subscript_limits);
|
||
}
|
||
done = false;
|
||
}
|
||
void deallocate()
|
||
{
|
||
if(nsubscript)
|
||
{
|
||
free(subscript_alls);
|
||
free(subscripts);
|
||
free(subscript_limits);
|
||
}
|
||
}
|
||
};
|
||
|
||
struct refer_state_for_all
|
||
{
|
||
size_t nflags;
|
||
size_t coefficients [MAXIMUM_TABLE_DIMENSIONS];
|
||
size_t capacities [MAXIMUM_TABLE_DIMENSIONS];
|
||
size_t limits [MAXIMUM_TABLE_DIMENSIONS];
|
||
};
|
||
|
||
static
|
||
void
|
||
build_refer_state_for_all( refer_state_for_all &state,
|
||
cblc_field_t *field,
|
||
int flags)
|
||
{
|
||
memset(&state, 0, sizeof(refer_state_for_all) );
|
||
if( flags & REFER_T_ALL_FLAGS_MASK )
|
||
{
|
||
// At this point, refer points to the very first element of
|
||
// an array specification that includes at least one ALL subscript. At
|
||
// this time, those ALLs were calculated as if they had been replaced
|
||
// with one.
|
||
|
||
// We are going to walk the reference up to its ultimate parent, picking
|
||
// up what we need along the way.
|
||
|
||
size_t current_bit = 1;
|
||
size_t current_index = 0;
|
||
cblc_field_t *current_sizer = field;
|
||
while( current_sizer )
|
||
{
|
||
while( current_sizer && !current_sizer->occurs_upper )
|
||
{
|
||
// current_sizer isn't a table, which isn't unusual.
|
||
current_sizer = current_sizer->parent;
|
||
}
|
||
|
||
if( !current_sizer )
|
||
{
|
||
// We have found all of the elements in this data description
|
||
// that have OCCURS clauses
|
||
break;
|
||
}
|
||
|
||
// We are sitting on an occurs clause:
|
||
|
||
if( current_bit & flags )
|
||
{
|
||
// It is an ALL subscript:
|
||
state.nflags += 1;
|
||
state.coefficients[current_index] = 1;
|
||
state.capacities[current_index] = current_sizer->capacity;
|
||
state.limits[current_index] = current_sizer->occurs_upper;
|
||
current_index += 1 ;
|
||
}
|
||
|
||
current_bit <<= 1;
|
||
current_sizer = current_sizer->parent;
|
||
}
|
||
}
|
||
}
|
||
|
||
static
|
||
bool
|
||
update_refer_state_for_all( refer_state_for_all &state,
|
||
cblc_field_t *field)
|
||
{
|
||
bool retval = false; // Means there is nothing left
|
||
|
||
for(size_t i=0; i<state.nflags; i++)
|
||
{
|
||
state.coefficients[i] += 1;
|
||
field->data += state.capacities[i];
|
||
if( state.coefficients[i] <= state.limits[i] )
|
||
{
|
||
// This coefficient is within range:
|
||
retval = true;
|
||
break;
|
||
}
|
||
|
||
// We have used up this coefficient.
|
||
|
||
// Remove the effects of incrementing this coefficient:
|
||
field->data -= state.limits[i] * state.capacities[i];
|
||
// Reset the coefficient back to one:
|
||
state.coefficients[i] = 1;
|
||
|
||
// And continue on to the next coefficient.
|
||
}
|
||
|
||
return retval;
|
||
}
|
||
|
||
static
|
||
int
|
||
year_to_yyyy(int arg1, int arg2, int arg3)
|
||
{
|
||
// See ISO/IEC 2014-1989 section 15.93 for a detailed description of the
|
||
// sliding window calculation
|
||
int max_year = arg2 + arg3;
|
||
int retval;
|
||
if( max_year % 100 >= arg1 )
|
||
{
|
||
retval = arg1 + 100 * (max_year/100);
|
||
}
|
||
else
|
||
{
|
||
retval = arg1 + 100 * (max_year/100 - 1);
|
||
}
|
||
return retval;
|
||
}
|
||
|
||
static
|
||
double
|
||
get_value_as_double_from_qualified_field( const cblc_field_t *input,
|
||
size_t input_o,
|
||
size_t input_s)
|
||
{
|
||
double retval;
|
||
int rdigits;
|
||
|
||
switch( input->type )
|
||
{
|
||
case FldFloat:
|
||
fprintf(stderr, "get_value_as_double_from_qualified_field(): Hey!"
|
||
" We got an unexpected float in intrinsic.cc!\n");
|
||
exit(1);
|
||
break;
|
||
|
||
default:
|
||
retval = __gg__binary_value_from_qualified_field(&rdigits,
|
||
input,
|
||
input_o,
|
||
input_s);
|
||
for(int i=0; i<rdigits; i++)
|
||
{
|
||
retval /= 10.0;
|
||
}
|
||
break;
|
||
}
|
||
|
||
return retval;
|
||
}
|
||
|
||
static
|
||
GCOB_FP128 kahan_summation(size_t ncount,
|
||
cblc_field_t **source,
|
||
const size_t *source_o,
|
||
const size_t *source_s,
|
||
const int *flags,
|
||
size_t *k_count)
|
||
{
|
||
// We use compensated addition. Look up Kahan summation.
|
||
|
||
// In the Kahan summation algorithm, the C value accumulates small errors
|
||
// Algebraically, it should be zero. So, "volatile" is an attempt to prevent
|
||
// an aggressive optimizing compiler from just making it go away.
|
||
|
||
*k_count = 0;
|
||
GCOB_FP128 sum = 0;
|
||
volatile GCOB_FP128 kahan_c = 0;
|
||
GCOB_FP128 input;
|
||
GCOB_FP128 y;
|
||
GCOB_FP128 t;
|
||
|
||
for(size_t i=0; i<ncount; i++)
|
||
{
|
||
refer_state_for_all state;
|
||
build_refer_state_for_all(state, source[i], flags[i]);
|
||
|
||
for(;;)
|
||
{
|
||
input = __gg__float128_from_qualified_field(source[i],
|
||
source_o[i],
|
||
source_s[i]);
|
||
y = input - kahan_c;
|
||
t = sum + y;
|
||
kahan_c = (t - sum) - y ;
|
||
sum = t;
|
||
*k_count += 1;
|
||
if( !update_refer_state_for_all(state, source[i]) )
|
||
{
|
||
// There is nothing left to do.
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
return sum;
|
||
}
|
||
|
||
static
|
||
GCOB_FP128
|
||
variance( size_t ncount,
|
||
cblc_field_t **source,
|
||
const size_t *source_o,
|
||
const size_t *source_s,
|
||
const int *flags)
|
||
{
|
||
// In order to avoid catastrophic cancellation, we are going to use an
|
||
// algorithm that is a bit wasteful of time, but is described as particularly
|
||
// robust.
|
||
|
||
GCOB_FP128 retval = 0;
|
||
if( ncount )
|
||
{
|
||
// First, we calculate the mean of the input variables, which we will use
|
||
// as an offset in the second stage:
|
||
size_t k_count;
|
||
GCOB_FP128 offset = kahan_summation( ncount,
|
||
source,
|
||
source_o,
|
||
source_s,
|
||
flags,
|
||
&k_count);
|
||
offset /= k_count;
|
||
|
||
// Next, we use Welford's algorithm on the residuals:
|
||
|
||
size_t count = 0;
|
||
GCOB_FP128 mean = 0;
|
||
GCOB_FP128 M2 = 0;
|
||
GCOB_FP128 delta;
|
||
GCOB_FP128 delta2;
|
||
GCOB_FP128 newValue;
|
||
|
||
for(size_t i=0; i<ncount; i++)
|
||
{
|
||
refer_state_for_all state;
|
||
build_refer_state_for_all(state, source[i], flags[i]);
|
||
|
||
for(;;)
|
||
{
|
||
newValue = __gg__float128_from_qualified_field(source[i],
|
||
source_o[i],
|
||
source_s[i]);
|
||
newValue -= offset;
|
||
|
||
count += 1;
|
||
delta = newValue - mean ;
|
||
mean += delta / count;
|
||
delta2 = newValue - mean;
|
||
M2 += delta * delta2;
|
||
if( !update_refer_state_for_all(state, source[i]) )
|
||
{
|
||
// There is nothing left to do.
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
retval = M2 / count;
|
||
}
|
||
return retval;
|
||
}
|
||
|
||
static
|
||
void
|
||
get_all_time( const cblc_field_t *dest, // needed for the target encoding
|
||
char *stime,
|
||
const struct cobol_tm &ctm)
|
||
{
|
||
// This routine represents a universal source for all output formatted date
|
||
// and formatted time functions. Messeth not with the format, for all the
|
||
// calling routines are counting on it.
|
||
//
|
||
// 1111111111222222222233333333334
|
||
// 01234567890123456789012345678901234567890
|
||
// Returns YYYYMMDDThhmmss.sssssssss+hhmmWwwdDDDZZZZ
|
||
//
|
||
// YYYY is the year
|
||
// MM is the month
|
||
// DD is the day of the month
|
||
// hh is the hour
|
||
// mm is the minute
|
||
// ss is the second
|
||
// .sssssssss are the nanoseconds
|
||
// +hhmm is the offset from UTC
|
||
// Www is the COBOL-style week number. (See the comments and the spec)
|
||
// d is the COBOL-style day-of-week 1-7, 1 being Monday
|
||
// DDD is the COBOL-style day-of-year, 001 being Jan 01
|
||
// ZZZZ is the alternate year. Sometimes the first one, two, or three
|
||
// days of January show up in the final week of the prior year.
|
||
|
||
sprintf(stime,
|
||
"%4.4d%2.2d%2.2dT" // YYYYMMSS
|
||
"%2.2d%2.2d%2.2d" // hhmmss
|
||
".%9.9d" // .sssssssss
|
||
"%c%2.2d%2.2d" // +hhmm
|
||
"W%2.2d" // Www
|
||
"%1d" // DOW [1-7], 1 for Monday
|
||
"%3.3d" // DDD day of year, 001 - 365,366
|
||
"%4.4d", // ZZZZ Year for YYYY-Www-D
|
||
ctm.YYYY,
|
||
ctm.MM,
|
||
ctm.DD,
|
||
ctm.hh,
|
||
ctm.mm,
|
||
ctm.ss,
|
||
ctm.nanoseconds,
|
||
ctm.tz_offset < 0 ? '-' : '+',
|
||
abs(ctm.tz_offset) / 60,
|
||
abs(ctm.tz_offset) % 60,
|
||
ctm.week_of_year,
|
||
ctm.day_of_week+1,
|
||
ctm.day_of_year,
|
||
ctm.ZZZZ);
|
||
__gg__convert_encoding(PTRCAST(char, stime),
|
||
DEFAULT_CHARMAP_SOURCE,
|
||
dest->encoding);
|
||
}
|
||
|
||
static
|
||
int
|
||
is_leap_year(int yyyy)
|
||
{
|
||
static const unsigned char leap_year_bits[50] =
|
||
{
|
||
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
|
||
0x11, 0x11, 0x01, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
|
||
0x11, 0x11, 0x11, 0x11, 0x11, 0x10, 0x11, 0x11, 0x11, 0x11,
|
||
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x01, 0x11, 0x11,
|
||
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
|
||
};
|
||
|
||
static const unsigned char mask[8] =
|
||
{ 1, 2, 4, 8, 0x10, 0x20, 0x40, 0x80 };
|
||
|
||
int days_in_year;
|
||
|
||
int year_in_cycle = yyyy % 400;
|
||
|
||
if( leap_year_bits[year_in_cycle/8] & mask[year_in_cycle & 0x07] )
|
||
{
|
||
days_in_year = 366;
|
||
}
|
||
else
|
||
{
|
||
days_in_year = 365;
|
||
}
|
||
|
||
return days_in_year;
|
||
}
|
||
|
||
static
|
||
int
|
||
weeks_in_year(int YYYY)
|
||
{
|
||
double Jan4 = YMD_to_JD(YYYY, 1, 4);
|
||
int dow = JD_to_DOW(Jan4);
|
||
if( dow == 6 )
|
||
{
|
||
return 53;
|
||
}
|
||
if( dow == 5 && is_leap_year(YYYY) == 366 )
|
||
{
|
||
return 53;
|
||
}
|
||
return 52;
|
||
}
|
||
|
||
static
|
||
void
|
||
populate_ctm_from_tm(struct cobol_tm &ctm, const struct tm &tm)
|
||
{
|
||
ctm.YYYY = tm.tm_year + 1900;
|
||
ctm.MM = tm.tm_mon + 1;
|
||
ctm.DD = tm.tm_mday ;
|
||
ctm.hh = tm.tm_hour;
|
||
ctm.mm = tm.tm_min;
|
||
ctm.ss = tm.tm_sec;
|
||
ctm.days_in_year = is_leap_year(ctm.YYYY);
|
||
ctm.weeks_in_year = weeks_in_year(ctm.YYYY);
|
||
|
||
double JD = YMD_to_JD(ctm.YYYY, ctm.MM, ctm.DD);
|
||
ctm.day_of_week = JD_to_DOW(JD);
|
||
|
||
double JD_Jan4 = YMD_to_JD(ctm.YYYY, 1, 4);
|
||
ctm.day_of_year = (int)(JD - (JD_Jan4-4));
|
||
|
||
int dow_Jan4 = JD_to_DOW(JD_Jan4);
|
||
double adjusted_starting_date = JD_Jan4 - dow_Jan4;
|
||
|
||
int adjusted_days = (int)(JD - adjusted_starting_date);
|
||
if( adjusted_days >= 0 )
|
||
{
|
||
int week_of_year = adjusted_days/7 + 1;
|
||
if(week_of_year > ctm.weeks_in_year)
|
||
{
|
||
ctm.ZZZZ = ctm.YYYY+1;
|
||
ctm.week_of_year = 1;
|
||
}
|
||
else
|
||
{
|
||
ctm.ZZZZ = ctm.YYYY;
|
||
ctm.week_of_year = week_of_year;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
ctm.ZZZZ = ctm.YYYY - 1;
|
||
ctm.week_of_year = weeks_in_year(ctm.ZZZZ);
|
||
}
|
||
}
|
||
|
||
static
|
||
void
|
||
populate_ctm_from_JD(struct cobol_tm &ctm, double JD )
|
||
{
|
||
// Extract the year, month, and day
|
||
int Y;
|
||
int M;
|
||
int D;
|
||
JD += JD_OF_1601_01_02;
|
||
JD_to_YMD(Y, M, D, JD);
|
||
|
||
struct tm tm = {};
|
||
tm.tm_mday = D;
|
||
tm.tm_mon = M-1;
|
||
tm.tm_year = Y-1900;
|
||
populate_ctm_from_tm(ctm, tm);
|
||
}
|
||
|
||
static
|
||
void
|
||
populate_ctm_from_date( struct cobol_tm &ctm,
|
||
const cblc_field_t *pdate,
|
||
size_t pdate_offset,
|
||
size_t pdate_size)
|
||
{
|
||
// Get the date as an integer
|
||
int rdigits;
|
||
double JD = (double)__gg__binary_value_from_qualified_field(&rdigits,
|
||
pdate,
|
||
pdate_offset,
|
||
pdate_size);
|
||
populate_ctm_from_JD(ctm, JD);
|
||
}
|
||
|
||
static
|
||
void
|
||
populate_ctm_from_double_time(struct cobol_tm &ctm, double time)
|
||
{
|
||
// Get hours, minutes, and seconds
|
||
double intpart;
|
||
double fracpart = modf(time, &intpart);
|
||
|
||
int hour = (int)intpart;
|
||
int second = hour % 60;
|
||
int minute = (hour / 60) % 60;
|
||
hour = hour / 3600;
|
||
ctm.ss = second;
|
||
ctm.mm = minute;
|
||
ctm.hh = hour;
|
||
ctm.nanoseconds = (int)(fracpart * 1000000000 + 0.5);
|
||
}
|
||
|
||
static
|
||
void
|
||
populate_ctm_from_time( struct cobol_tm &ctm,
|
||
const cblc_field_t *ptime,
|
||
size_t ptime_o,
|
||
size_t ptime_s,
|
||
const cblc_field_t *poffset,
|
||
size_t poffset_o,
|
||
size_t poffset_s)
|
||
{
|
||
double time = get_value_as_double_from_qualified_field( ptime,
|
||
ptime_o,
|
||
ptime_s);
|
||
populate_ctm_from_double_time(ctm, time);
|
||
|
||
if( poffset )
|
||
{
|
||
int rdigits;
|
||
int value = (int)__gg__binary_value_from_qualified_field(&rdigits,
|
||
poffset,
|
||
poffset_o,
|
||
poffset_s);
|
||
if( rdigits )
|
||
{
|
||
value /= __gg__power_of_ten(rdigits);
|
||
rdigits = 0;
|
||
}
|
||
ctm.tz_offset = value;
|
||
if( abs(value) >= 1440 )
|
||
{
|
||
exception_raise(ec_argument_function_e);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
ctm.tz_offset = 0;
|
||
}
|
||
}
|
||
|
||
static void
|
||
convert_to_zulu(cobol_tm &ctm)
|
||
{
|
||
// Get the Julian Day
|
||
double JD = YMD_to_JD(ctm.YYYY,
|
||
ctm.MM,
|
||
ctm.DD);
|
||
// Get the time in seconds past midnight
|
||
double seconds_past_midnight = ctm.hh * 3600
|
||
+ ctm.mm * 60
|
||
+ ctm.ss;
|
||
// Subtract the UTC offset, which is given in minutes
|
||
seconds_past_midnight -= ctm.tz_offset * 60;
|
||
if( seconds_past_midnight < 0 )
|
||
{
|
||
JD -= 1;
|
||
seconds_past_midnight += 86400;
|
||
}
|
||
else if( seconds_past_midnight >= 86400 )
|
||
{
|
||
JD += 1;
|
||
seconds_past_midnight -= 86400;
|
||
}
|
||
JD -= JD_OF_1601_01_02;
|
||
populate_ctm_from_JD(ctm, JD);
|
||
populate_ctm_from_double_time(ctm, seconds_past_midnight);
|
||
if( ctm.YYYY < 1601 )
|
||
{
|
||
ctm.YYYY = ctm.MM = ctm.DD = 0;
|
||
}
|
||
}
|
||
|
||
static
|
||
void
|
||
ftime_replace(char *dest,
|
||
char const * const dest_end,
|
||
char const * source,
|
||
char const * const source_end,
|
||
charmap_t * charmap_source,
|
||
char const * const ftime)
|
||
{
|
||
// This routine is highly dependent on the source format being correct.
|
||
int ncount;
|
||
const char *src;
|
||
bool saw_decimal_point = false;
|
||
bool saw_plus_sign = false;
|
||
char decimal_point = __gg__get_decimal_point();
|
||
static const int OFFSET_TO_YYYY = 0;
|
||
static const int OFFSET_TO_MM = 4;
|
||
static const int OFFSET_TO_DD = 6;
|
||
static const int OFFSET_TO_HOUR = 9;
|
||
static const int OFFSET_TO_MINUTE = 11;
|
||
static const int OFFSET_TO_SECOND = 13;
|
||
static const int OFFSET_TO_FRACTION = 16;
|
||
static const int OFFSET_TO_OFFSET = 25;
|
||
static const int OFFSET_TO_OFFSET_HOUR = 26;
|
||
static const int OFFSET_TO_OFFSET_MINUTE = 28;
|
||
static const int OFFSET_TO_WEEK = 30;
|
||
static const int OFFSET_TO_DOW = 33;
|
||
static const int OFFSET_TO_DOY = 34;
|
||
static const int OFFSET_TO_ZZZZ = 37;
|
||
|
||
int source_Y = charmap_source->mapped_character(ascii_Y );
|
||
int source_W = charmap_source->mapped_character(ascii_W );
|
||
int source_s = charmap_source->mapped_character(ascii_s );
|
||
int source_m = charmap_source->mapped_character(ascii_m );
|
||
int source_h = charmap_source->mapped_character(ascii_h );
|
||
int source_plus = charmap_source->mapped_character(ascii_plus);
|
||
int source_D = charmap_source->mapped_character(ascii_D );
|
||
int source_M = charmap_source->mapped_character(ascii_M );
|
||
|
||
while( source < source_end && dest < dest_end )
|
||
{
|
||
char fchar = *source;
|
||
if( fchar == source_Y )
|
||
{
|
||
// This can only be a YYYY
|
||
// But, we have a choice. If there is a 'W' in the format, then we
|
||
// need to use ZZZZ rather than YYYY:
|
||
src = ftime + OFFSET_TO_YYYY;
|
||
const char *p = source;
|
||
while(p < source_end)
|
||
{
|
||
if( *p++ == source_W )
|
||
{
|
||
src = ftime + OFFSET_TO_ZZZZ;
|
||
}
|
||
}
|
||
|
||
ncount = 4;
|
||
}
|
||
else if( fchar == source_M )
|
||
{
|
||
// This can only be a MM
|
||
ncount = 2;
|
||
src = ftime + OFFSET_TO_MM;
|
||
}
|
||
else if( fchar == source_D )
|
||
{
|
||
// It can be a D, DD or DDD
|
||
if( source[2] == source_D )
|
||
{
|
||
ncount = 3;
|
||
src = ftime + OFFSET_TO_DOY;
|
||
}
|
||
else if( source[1] == source_D )
|
||
{
|
||
ncount = 2;
|
||
src = ftime + OFFSET_TO_DD;
|
||
}
|
||
else
|
||
{
|
||
ncount = 1;
|
||
src = ftime + OFFSET_TO_DOW;
|
||
}
|
||
}
|
||
else if( fchar == source_plus )
|
||
{
|
||
saw_plus_sign = true;
|
||
ncount = 1;
|
||
src = ftime + OFFSET_TO_OFFSET;
|
||
}
|
||
else if( fchar == source_h )
|
||
{
|
||
ncount = 2;
|
||
if(saw_plus_sign)
|
||
{
|
||
src = ftime + OFFSET_TO_OFFSET_HOUR;
|
||
}
|
||
else
|
||
{
|
||
src = ftime + OFFSET_TO_HOUR;
|
||
}
|
||
}
|
||
else if( fchar == source_m )
|
||
{
|
||
ncount = 2;
|
||
if(saw_plus_sign)
|
||
{
|
||
src = ftime + OFFSET_TO_OFFSET_MINUTE;
|
||
}
|
||
else
|
||
{
|
||
src = ftime + OFFSET_TO_MINUTE;
|
||
}
|
||
}
|
||
else if( fchar == decimal_point )
|
||
{
|
||
saw_decimal_point = true;
|
||
ncount = 1;
|
||
src = source;
|
||
}
|
||
else if( fchar == source_s )
|
||
{
|
||
if(saw_decimal_point)
|
||
{
|
||
// There can be a variable number of fractional 's'
|
||
ncount = -1;
|
||
src = ftime + OFFSET_TO_FRACTION;
|
||
}
|
||
else
|
||
{
|
||
ncount = 2;
|
||
src = ftime + OFFSET_TO_SECOND;
|
||
}
|
||
}
|
||
else if( fchar == source_W )
|
||
{
|
||
ncount = 3;
|
||
src = ftime + OFFSET_TO_WEEK;
|
||
}
|
||
else
|
||
{
|
||
ncount = 1;
|
||
src = source;
|
||
}
|
||
|
||
// Copy over the ncount characters to dest
|
||
if( ncount == -1 )
|
||
{
|
||
// This indicates special processing for a variable number of 's'
|
||
// characters
|
||
while(*source == 's' && dest < dest_end)
|
||
{
|
||
source += 1;
|
||
*dest++ = *src++;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
source += ncount;
|
||
while(ncount-- && dest < dest_end)
|
||
{
|
||
*dest++ = *src++;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
//
|
||
// Beyond this point, we are implementing phase 2 of intrinsics. These routines
|
||
// are intended to be "better" than the ones above. In an ideal world,
|
||
// eventually all of the above routines will migrate down here, and this comment
|
||
// will be removed. Bob Dubner, 2023-01-18
|
||
|
||
// Although not, of course, necessary, these routines are being placed in
|
||
// alphabetical order by the COBOL function name:
|
||
|
||
extern "C"
|
||
void
|
||
__gg__abs(cblc_field_t *dest,
|
||
const cblc_field_t *source,
|
||
size_t source_offset,
|
||
size_t source_size)
|
||
{
|
||
// FUNCTION ABS
|
||
GCOB_FP128 value;
|
||
value = __gg__float128_from_qualified_field(source,
|
||
source_offset,
|
||
source_size);
|
||
if( value < 0 )
|
||
{
|
||
value = -value;
|
||
}
|
||
__gg__float128_to_field(dest,
|
||
value,
|
||
truncation_e,
|
||
NULL);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__acos( cblc_field_t *dest,
|
||
const cblc_field_t *source,
|
||
size_t source_offset,
|
||
size_t source_size)
|
||
{
|
||
// FUNCTION ACOS
|
||
GCOB_FP128 value;
|
||
value = __gg__float128_from_qualified_field(source, source_offset, source_size);
|
||
|
||
if( value < GCOB_FP128_LITERAL(-1.00) || value > GCOB_FP128_LITERAL(+1.00) )
|
||
{
|
||
exception_raise(ec_argument_function_e);
|
||
value = WEIRD_TRANSCENDENT_RETURN_VALUE;
|
||
}
|
||
else
|
||
{
|
||
value = FP128_FUNC(acos)(value);
|
||
}
|
||
|
||
__gg__float128_to_field( dest,
|
||
value,
|
||
truncation_e,
|
||
NULL);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__annuity(cblc_field_t *dest,
|
||
const cblc_field_t *arg1,
|
||
size_t arg1_offset,
|
||
size_t arg1_size,
|
||
const cblc_field_t *arg2,
|
||
size_t arg2_offset,
|
||
size_t arg2_size)
|
||
{
|
||
// FUNCTION ANNUITY
|
||
|
||
GCOB_FP128 retval = 0;
|
||
|
||
GCOB_FP128 val1 = FP128_FUNC(fabs)(__gg__float128_from_qualified_field(arg1,
|
||
arg1_offset,
|
||
arg1_size));
|
||
GCOB_FP128 val2 = FP128_FUNC(fabs)(__gg__float128_from_qualified_field(arg2,
|
||
arg2_offset,
|
||
arg2_size));
|
||
if( val2 > 0)
|
||
{
|
||
if( val1 < 0 )
|
||
{
|
||
exception_raise(ec_argument_function_e);
|
||
}
|
||
else if( val1 == 0 )
|
||
{
|
||
retval = 1/val2;
|
||
}
|
||
else
|
||
{
|
||
retval = val1 / (1- FP128_FUNC(pow)( (1+val1), -val2 ));
|
||
}
|
||
}
|
||
else
|
||
{
|
||
exception_raise(ec_argument_function_e);
|
||
}
|
||
__gg__float128_to_field(dest,
|
||
retval,
|
||
truncation_e,
|
||
NULL);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__asin( cblc_field_t *dest,
|
||
const cblc_field_t *source,
|
||
size_t source_offset,
|
||
size_t source_size)
|
||
{
|
||
// FUNCTION ASIN
|
||
|
||
GCOB_FP128 value;
|
||
value = __gg__float128_from_qualified_field(source,
|
||
source_offset,
|
||
source_size);
|
||
|
||
if( value < GCOB_FP128_LITERAL(-1.0) || value > GCOB_FP128_LITERAL(+1.00) )
|
||
{
|
||
exception_raise(ec_argument_function_e);
|
||
value = WEIRD_TRANSCENDENT_RETURN_VALUE;
|
||
}
|
||
else
|
||
{
|
||
value = FP128_FUNC(asin)(value);
|
||
}
|
||
|
||
__gg__float128_to_field( dest,
|
||
value,
|
||
truncation_e,
|
||
NULL);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__atan( cblc_field_t *dest,
|
||
const cblc_field_t *source,
|
||
size_t source_offset,
|
||
size_t source_size)
|
||
{
|
||
// FUNCTION ATAN
|
||
|
||
GCOB_FP128 value;
|
||
value = __gg__float128_from_qualified_field(source,
|
||
source_offset,
|
||
source_size);
|
||
|
||
value = FP128_FUNC(atan)(value);
|
||
|
||
__gg__float128_to_field( dest,
|
||
value,
|
||
truncation_e,
|
||
NULL);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__byte_length(cblc_field_t *dest,
|
||
const cblc_field_t */*source*/,
|
||
size_t /*source_offset*/,
|
||
size_t source_size)
|
||
{
|
||
// FUNCTION BYTE-LENGTH
|
||
__int128 value = source_size;
|
||
__gg__int128_to_field(dest,
|
||
value,
|
||
NO_RDIGITS,
|
||
truncation_e,
|
||
NULL);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__char( cblc_field_t *dest,
|
||
const cblc_field_t *source,
|
||
size_t source_offset,
|
||
size_t source_size)
|
||
{
|
||
int rdigits;
|
||
|
||
// The CHAR function takes an integer, the ordinal position. It
|
||
// returns a single-character string, which is the character at that
|
||
// ordinal position.
|
||
|
||
// 'A', with the ascii value of 65, is at the ordinal position 66.
|
||
|
||
int ordinal = (int)(__gg__binary_value_from_qualified_field(&rdigits,
|
||
source,
|
||
source_offset,
|
||
source_size));
|
||
ordinal /= __gg__power_of_ten(rdigits);
|
||
int ch = ordinal-1;
|
||
charmap_t *charmap = __gg__get_charmap(dest->encoding);
|
||
memset(dest->data, charmap->mapped_character(ascii_space), dest->capacity);
|
||
dest->data[0] = ch;
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__combined_datetime(cblc_field_t *dest,
|
||
const cblc_field_t *arg1,
|
||
size_t arg1_offset,
|
||
size_t arg1_size,
|
||
const cblc_field_t *arg2,
|
||
size_t arg2_offset,
|
||
size_t arg2_size)
|
||
{
|
||
int rdigits;
|
||
|
||
__int128 val1 = (int)(__gg__binary_value_from_qualified_field(&rdigits,
|
||
arg1,
|
||
arg1_offset,
|
||
arg1_size));
|
||
__int128 val2 = (int)(__gg__binary_value_from_qualified_field(&rdigits,
|
||
arg2,
|
||
arg2_offset,
|
||
arg2_size));
|
||
__int128 value = val1 * 1000000 + val2;
|
||
__gg__int128_to_field(dest,
|
||
value,
|
||
6,
|
||
truncation_e,
|
||
NULL);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__concat( cblc_field_t *dest,
|
||
size_t ncount)
|
||
{
|
||
size_t bytes = 0;
|
||
size_t offset = 0;
|
||
for(size_t i=0; i<ncount; i++)
|
||
{
|
||
bytes += __gg__treeplet_1s[i];
|
||
}
|
||
__gg__adjust_dest_size(dest, bytes);
|
||
for(size_t i=0; i<ncount; i++)
|
||
{
|
||
memcpy( dest->data + offset,
|
||
__gg__treeplet_1f[i]->data + __gg__treeplet_1o[i],
|
||
__gg__treeplet_1s[i]);
|
||
offset += __gg__treeplet_1s[i];
|
||
}
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__cos(cblc_field_t *dest,
|
||
const cblc_field_t *source,
|
||
size_t source_offset,
|
||
size_t source_size)
|
||
{
|
||
// FUNCTION COS
|
||
|
||
GCOB_FP128 value = __gg__float128_from_qualified_field(source,
|
||
source_offset,
|
||
source_size);
|
||
value = FP128_FUNC(cos)(value);
|
||
__gg__float128_to_field(dest,
|
||
value,
|
||
truncation_e,
|
||
NULL);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__current_date(cblc_field_t *dest)
|
||
{
|
||
// FUNCTION CURRENT-DATE
|
||
struct cbl_timespec tp = {};
|
||
__gg__clock_gettime(&tp); // time_t tv_sec; long tv_nsec
|
||
|
||
char retval[DATE_STRING_BUFFER_SIZE];
|
||
timespec_to_string(retval, tp);
|
||
__gg__convert_encoding(PTRCAST(char, retval),
|
||
DEFAULT_CHARMAP_SOURCE,
|
||
dest->encoding);
|
||
string_to_dest(dest, retval);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__seconds_past_midnight(cblc_field_t *dest)
|
||
{
|
||
// SECONDS-PAST-MIDNIGHT
|
||
struct cbl_timespec tp = {};
|
||
struct tm tm;
|
||
__int128 retval=0;
|
||
|
||
__gg__clock_gettime(&tp); // time_t tv_sec; long tv_nsec
|
||
localtime_r(&tp.tv_sec, &tm);
|
||
|
||
retval += tm.tm_hour;
|
||
retval *= 60;
|
||
retval += tm.tm_min;
|
||
retval *= 60;
|
||
retval += tm.tm_sec;
|
||
retval *= 1000000000;
|
||
retval += tp.tv_nsec;
|
||
__gg__int128_to_field(dest,
|
||
retval,
|
||
9,
|
||
truncation_e,
|
||
NULL);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__date_of_integer(cblc_field_t *dest,
|
||
const cblc_field_t *source,
|
||
size_t source_offset,
|
||
size_t source_size)
|
||
{
|
||
// FUNCTION DATE-OF-INTEGER
|
||
int rdigits;
|
||
double JD = (double)__gg__binary_value_from_qualified_field(&rdigits,
|
||
source,
|
||
source_offset,
|
||
source_size);
|
||
JD += JD_OF_1601_01_02;
|
||
int Y;
|
||
int M;
|
||
int D;
|
||
JD_to_YMD(Y, M, D, JD);
|
||
int retval = Y*10000 + M*100 + D;
|
||
__gg__int128_to_field(dest,
|
||
retval,
|
||
NO_RDIGITS,
|
||
truncation_e,
|
||
NULL);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__date_to_yyyymmdd( cblc_field_t *dest,
|
||
const cblc_field_t *par1,
|
||
size_t par1_o,
|
||
size_t par1_s,
|
||
const cblc_field_t *par2,
|
||
size_t par2_o,
|
||
size_t par2_s,
|
||
const cblc_field_t *par3,
|
||
size_t par3_o,
|
||
size_t par3_s)
|
||
{
|
||
// FUNCTION DATE-TO-YYYYMMDD
|
||
// See the discussion in ISO/IEC 2014-1989 Section 15.20
|
||
int rdigits;
|
||
int arg1 = (int)__gg__binary_value_from_qualified_field(&rdigits, par1, par1_o, par1_s);
|
||
int arg2 = (int)__gg__binary_value_from_qualified_field(&rdigits, par2, par2_o, par2_s );
|
||
int arg3 = (int)__gg__binary_value_from_qualified_field(&rdigits, par3, par3_o, par3_s);
|
||
|
||
int yy = arg1/10000;
|
||
int mmdd = arg1%10000;
|
||
|
||
int retval = year_to_yyyy(yy, arg2, arg3) * 10000 + mmdd;
|
||
__gg__int128_to_field(dest,
|
||
retval,
|
||
NO_RDIGITS,
|
||
truncation_e,
|
||
NULL);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__day_of_integer( cblc_field_t *dest,
|
||
const cblc_field_t *source,
|
||
size_t source_offset,
|
||
size_t source_size)
|
||
{
|
||
// FUNCTION DAY-OF_INTEGER
|
||
int rdigits;
|
||
double JD = (double)__gg__binary_value_from_qualified_field(&rdigits,
|
||
source,
|
||
source_offset,
|
||
source_size);
|
||
JD += JD_OF_1601_01_02;
|
||
int Y;
|
||
int M;
|
||
int D;
|
||
JD_to_YMD(Y, M, D, JD);
|
||
|
||
double start_of_year = YMD_to_JD(Y, 1, 1);
|
||
|
||
__int128 retval = Y * 1000 + int(JD - start_of_year) + 1;
|
||
__gg__int128_to_field(dest,
|
||
retval,
|
||
NO_RDIGITS,
|
||
truncation_e,
|
||
NULL);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__day_to_yyyyddd( cblc_field_t *dest,
|
||
const cblc_field_t *par1,
|
||
size_t par1_o,
|
||
size_t par1_s,
|
||
const cblc_field_t *par2,
|
||
size_t par2_o,
|
||
size_t par2_s,
|
||
const cblc_field_t *par3,
|
||
size_t par3_o,
|
||
size_t par3_s)
|
||
{
|
||
// FUNCTION DAY-TO-YYYYDDD
|
||
// See the discussion in ISO/IEC 2014-1989 Section 15.20
|
||
int rdigits;
|
||
int arg1 = (int)__gg__binary_value_from_qualified_field(&rdigits, par1, par1_o, par1_s);
|
||
int arg2 = (int)__gg__binary_value_from_qualified_field(&rdigits, par2, par2_o, par2_s );
|
||
int arg3 = (int)__gg__binary_value_from_qualified_field(&rdigits, par3, par3_o, par3_s);
|
||
|
||
int yy = arg1/1000;
|
||
int ddd = arg1%1000;
|
||
|
||
int retval = year_to_yyyy(yy, arg2, arg3) * 1000 + ddd;
|
||
|
||
__gg__int128_to_field(dest,
|
||
retval,
|
||
NO_RDIGITS,
|
||
truncation_e,
|
||
NULL);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__e(cblc_field_t *dest)
|
||
{
|
||
// FUNCTION E
|
||
static GCOB_FP128 e
|
||
= GCOB_FP128_LITERAL(2.7182818284590452353602874713526624977572);
|
||
__gg__float128_to_field(dest,
|
||
e,
|
||
truncation_e,
|
||
NULL);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__exp(cblc_field_t *dest,
|
||
const cblc_field_t *source,
|
||
size_t source_offset,
|
||
size_t source_size)
|
||
{
|
||
// FUNCTION EXP
|
||
|
||
GCOB_FP128 value = __gg__float128_from_qualified_field(source,
|
||
source_offset,
|
||
source_size);
|
||
value = FP128_FUNC(exp)(value);
|
||
__gg__float128_to_field(dest,
|
||
value,
|
||
truncation_e,
|
||
NULL);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__exp10(cblc_field_t *dest,
|
||
const cblc_field_t *source,
|
||
size_t source_offset,
|
||
size_t source_size)
|
||
{
|
||
// FUNCTION EXP10
|
||
|
||
GCOB_FP128 value = __gg__float128_from_qualified_field(source,
|
||
source_offset,
|
||
source_size);
|
||
value = FP128_FUNC(pow)(GCOB_FP128_LITERAL(10.0), value);
|
||
__gg__float128_to_field(dest,
|
||
value,
|
||
truncation_e,
|
||
NULL);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__factorial(cblc_field_t *dest,
|
||
const cblc_field_t *source,
|
||
size_t source_offset,
|
||
size_t source_size)
|
||
{
|
||
// FUNCTION FACTORIAL
|
||
int rdigits;
|
||
int N = (int)__gg__binary_value_from_qualified_field( &rdigits,
|
||
source,
|
||
source_offset,
|
||
source_size);
|
||
while(rdigits--)
|
||
{
|
||
N /= 10;
|
||
}
|
||
|
||
__int128 retval = 1;
|
||
|
||
while( N > 1 )
|
||
{
|
||
retval *= N--;
|
||
}
|
||
__gg__int128_to_field(dest,
|
||
retval,
|
||
NO_RDIGITS,
|
||
truncation_e,
|
||
NULL);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__formatted_current_date( cblc_field_t *dest, // Destination string
|
||
const cblc_field_t *input, // datetime format
|
||
size_t input_offset,
|
||
size_t input_size)
|
||
{
|
||
// FUNCTION FORMATTED-CURRENT-DATE
|
||
|
||
cbl_encoding_t from = input->encoding;
|
||
cbl_encoding_t to = dest->encoding;
|
||
charmap_t *charmap_from = __gg__get_charmap(from);
|
||
charmap_t *charmap_to = __gg__get_charmap(to);
|
||
|
||
int dest_space = charmap_to->mapped_character(ascii_space);
|
||
int format_Z = charmap_from->mapped_character(ascii_Z);
|
||
|
||
// Establish the destination, and set it to spaces
|
||
char *d = PTRCAST(char, dest->data);
|
||
const char *dend = d + dest->capacity;
|
||
memset(d, dest_space, dest->capacity);
|
||
|
||
// Establish the formatting string:
|
||
const char *format = PTRCAST(char, (input->data+input_offset));
|
||
const char *format_end = format + input_size;
|
||
|
||
bool is_zulu = false;
|
||
|
||
const char *p = format;
|
||
while( p < format_end )
|
||
{
|
||
int ch = *p++;
|
||
if( ch == format_Z )
|
||
{
|
||
is_zulu = true;
|
||
break;
|
||
}
|
||
}
|
||
|
||
struct cbl_timespec ts = {};
|
||
__gg__clock_gettime(&ts);
|
||
|
||
struct tm tm = {};
|
||
#ifdef HAVE_STRUCT_TM_TM_ZONE
|
||
tm.tm_zone = "GMT";
|
||
#endif
|
||
if( is_zulu )
|
||
{
|
||
gmtime_r(&ts.tv_sec, &tm);
|
||
}
|
||
else
|
||
{
|
||
localtime_r(&ts.tv_sec, &tm);
|
||
}
|
||
|
||
struct cobol_tm ctm = {};
|
||
populate_ctm_from_tm(ctm, tm);
|
||
|
||
ctm.nanoseconds = ts.tv_nsec;
|
||
|
||
tzset();
|
||
// Convert seconds west of UTC to minutes east of UTC
|
||
ctm.tz_offset = -timezone/60;
|
||
|
||
char achftime[64];
|
||
get_all_time(dest, achftime, ctm);
|
||
ftime_replace(d, dend, format, format_end, charmap_from, achftime);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__formatted_date(cblc_field_t *dest, // Destination string
|
||
const cblc_field_t *arg1, // datetime format
|
||
size_t arg1_offset,
|
||
size_t arg1_size,
|
||
const cblc_field_t *arg2, // integer date
|
||
size_t arg2_offset,
|
||
size_t arg2_size)
|
||
{
|
||
// FUNCTION FORMATTED-DATE
|
||
|
||
cbl_encoding_t from = arg1->encoding;
|
||
cbl_encoding_t to = dest->encoding;
|
||
charmap_t *charmap_from = __gg__get_charmap(from);
|
||
charmap_t *charmap_to = __gg__get_charmap(to);
|
||
|
||
int dest_space = charmap_to->mapped_character(ascii_space);
|
||
|
||
// Establish the destination, and set it to spaces
|
||
char *d = PTRCAST(char, dest->data);
|
||
const char *dend = d + dest->capacity;
|
||
memset(d, dest_space, dest->capacity);
|
||
|
||
// Establish the formatting string:
|
||
char *format = PTRCAST(char, (arg1->data+arg1_offset));
|
||
const char *format_end = format + arg1_size;
|
||
|
||
struct cobol_tm ctm = {};
|
||
|
||
populate_ctm_from_date(ctm, arg2, arg2_offset, arg2_size);
|
||
|
||
char achftime[64];
|
||
get_all_time(dest, achftime, ctm);
|
||
if( __gg__exception_code )
|
||
{
|
||
memset(d, dest_space, dend-d);
|
||
}
|
||
else
|
||
{
|
||
ftime_replace(d, dend, format, format_end, charmap_from, achftime);
|
||
__gg__adjust_dest_size(dest, format_end-format);
|
||
}
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__formatted_datetime( cblc_field_t *dest, // Destination string
|
||
const cblc_field_t *par1, // datetime format
|
||
size_t par1_o,
|
||
size_t par1_s,
|
||
const cblc_field_t *par2, // integer date
|
||
size_t par2_o,
|
||
size_t par2_s,
|
||
const cblc_field_t *par3, // numeric time
|
||
size_t par3_o,
|
||
size_t par3_s,
|
||
const cblc_field_t *par4, // optional offset in seconds
|
||
size_t par4_o,
|
||
size_t par4_s
|
||
)
|
||
{
|
||
// FUNCTION FORMATTED-DATETIME
|
||
|
||
cbl_encoding_t from = par1->encoding;
|
||
cbl_encoding_t to = dest->encoding;
|
||
charmap_t *charmap_from = __gg__get_charmap(from);
|
||
charmap_t *charmap_to = __gg__get_charmap(to);
|
||
|
||
// Establish the destination, and set it to spaces
|
||
char *d = PTRCAST(char, (dest->data));
|
||
const char *dend = d + dest->capacity;
|
||
memset(d, charmap_from->mapped_character(ascii_space), dest->capacity);
|
||
|
||
// Establish the formatting string:
|
||
char *format = PTRCAST(char, (par1->data+par1_o));
|
||
char *format_end = format + par1_s;
|
||
trim_trailing_spaces(format, format_end, charmap_from->mapped_character(ascii_space));
|
||
bool is_zulu = is_zulu_format(format, format_end, charmap_from);
|
||
|
||
struct cobol_tm ctm = {};
|
||
|
||
populate_ctm_from_date(ctm, par2, par2_o, par2_s);
|
||
populate_ctm_from_time( ctm,
|
||
par3, par3_o, par3_s,
|
||
par4, par4_o, par4_s);
|
||
|
||
if( is_zulu )
|
||
{
|
||
convert_to_zulu(ctm);
|
||
}
|
||
|
||
char achftime[64];
|
||
get_all_time(dest, achftime, ctm);
|
||
if( __gg__exception_code )
|
||
{
|
||
memset(d, charmap_to->mapped_character(ascii_space), dend-d);
|
||
}
|
||
else
|
||
{
|
||
ftime_replace(d, dend, format, format_end, charmap_from, achftime);
|
||
__gg__adjust_dest_size(dest, format_end-format);
|
||
}
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__formatted_time( cblc_field_t *dest,// Destination string
|
||
const cblc_field_t *par1, // datetime format
|
||
size_t par1_o,
|
||
size_t par1_s,
|
||
const cblc_field_t *par2,// numeric time
|
||
size_t par2_o,
|
||
size_t par2_s,
|
||
const cblc_field_t *par4, // optional offset in seconds
|
||
size_t par4_o,
|
||
size_t par4_s)
|
||
|
||
{
|
||
// FUNCTION FORMATTED-TIME
|
||
|
||
cbl_encoding_t from = par1->encoding;
|
||
cbl_encoding_t to = dest->encoding;
|
||
charmap_t *charmap_from = __gg__get_charmap(from);
|
||
charmap_t *charmap_to = __gg__get_charmap(to);
|
||
|
||
int dest_space = charmap_to->mapped_character(ascii_space);
|
||
|
||
// Establish the destination, and set it to spaces
|
||
char *d = PTRCAST(char, dest->data);
|
||
const char *dend = d + dest->capacity;
|
||
memset(d, dest_space, dest->capacity);
|
||
|
||
// Establish the formatting string:
|
||
char *format = PTRCAST(char, (par1->data+par1_o));
|
||
char *format_end = format + par1_s;
|
||
trim_trailing_spaces( format,
|
||
format_end,
|
||
charmap_from->mapped_character(ascii_space));
|
||
bool is_zulu = is_zulu_format(format, format_end, charmap_from);
|
||
|
||
struct cobol_tm ctm = {};
|
||
populate_ctm_from_time( ctm,
|
||
par2,
|
||
par2_o,
|
||
par2_s,
|
||
par4,
|
||
par4_o,
|
||
par4_s);
|
||
|
||
if( is_zulu )
|
||
{
|
||
convert_to_zulu(ctm);
|
||
}
|
||
|
||
char achftime[64];
|
||
get_all_time(dest, achftime, ctm);
|
||
if( __gg__exception_code )
|
||
{
|
||
memset(d, dest_space, dend-d);
|
||
}
|
||
else
|
||
{
|
||
ftime_replace(d, dend, format, format_end, charmap_from, achftime);
|
||
__gg__adjust_dest_size(dest, format_end-format);
|
||
}
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__integer(cblc_field_t *dest,
|
||
const cblc_field_t *source,
|
||
size_t source_offset,
|
||
size_t source_size)
|
||
{
|
||
// FUNCTION INTEGER
|
||
GCOB_FP128 value = __gg__float128_from_qualified_field(source,
|
||
source_offset,
|
||
source_size);
|
||
value = FP128_FUNC(floor)(value);
|
||
__gg__float128_to_field(dest,
|
||
value,
|
||
truncation_e,
|
||
NULL);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__integer_of_date(cblc_field_t *dest,
|
||
const cblc_field_t *source,
|
||
size_t source_offset,
|
||
size_t source_size)
|
||
{
|
||
// FUNCTION INTEGER-OF-DATE
|
||
int rdigits;
|
||
long argument_1 = (long)(__gg__binary_value_from_qualified_field(&rdigits,
|
||
source,
|
||
source_offset,
|
||
source_size));
|
||
|
||
int retval = 0;
|
||
static const int max_days[13] = {0, 31, 28, 31, 30, 31, 30,
|
||
31, 31, 30, 31, 30, 31};
|
||
|
||
int year = (long)argument_1/10000;
|
||
int month = (long)argument_1/100 % 100;
|
||
int day = (long)argument_1 % 100;
|
||
|
||
// We need to check for validity in the proleptic Gregorian calendar.
|
||
|
||
int max_day = 0;
|
||
if( month >= 1 && month <= 12 )
|
||
{
|
||
max_day = max_days[month];
|
||
}
|
||
if( max_day == 28 && (((year%4) == 0 && ((year)%100) != 0) || ((year%400) == 0) ))
|
||
{
|
||
// Year is divisible by four, but is not divisible by 100, so this
|
||
// is a leap year.
|
||
max_day += 1;
|
||
}
|
||
if( day < 1 || day > max_day )
|
||
{
|
||
max_day = 0;
|
||
}
|
||
if( max_day && year >= 1601 && year <= 9999 )
|
||
{
|
||
// It's a valid Y/M/D:
|
||
double JD = YMD_to_JD(year, month, day);
|
||
|
||
// Offset result so that 1601-01-01 comes back as the first day of
|
||
// the Gregorian Calendar
|
||
retval = (int)(JD - JD_OF_1601_01_02);
|
||
}
|
||
__gg__int128_to_field(dest,
|
||
retval,
|
||
NO_RDIGITS,
|
||
truncation_e,
|
||
NULL);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__integer_of_day( cblc_field_t *dest,
|
||
const cblc_field_t *source,
|
||
size_t source_offset,
|
||
size_t source_size)
|
||
{
|
||
// FUNCTION INTEGER-OF-DAY
|
||
// Convert YYYYDDD to "integer date"
|
||
int rdigits;
|
||
int yyyyddd = (int)__gg__binary_value_from_qualified_field( &rdigits,
|
||
source,
|
||
source_offset,
|
||
source_size);
|
||
int yyyy = yyyyddd / 1000;
|
||
int ddd = yyyyddd % 1000;
|
||
|
||
double JD = YMD_to_JD(yyyy, 1, 0) + ddd;
|
||
int retval = (int)(JD - JD_OF_1601_01_02);
|
||
|
||
__gg__int128_to_field(dest,
|
||
retval,
|
||
NO_RDIGITS,
|
||
truncation_e,
|
||
NULL);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__integer_part( cblc_field_t *dest,
|
||
const cblc_field_t *source,
|
||
size_t source_offset,
|
||
size_t source_size)
|
||
{
|
||
// FUNCTION INTEGER-PART
|
||
GCOB_FP128 value = __gg__float128_from_qualified_field(source,
|
||
source_offset,
|
||
source_size);
|
||
GCOB_FP128 retval = FP128_FUNC(floor)(FP128_FUNC(fabs)(value));
|
||
|
||
if( value < 0 )
|
||
{
|
||
retval = -retval;
|
||
}
|
||
__gg__float128_to_field(dest,
|
||
retval,
|
||
truncation_e,
|
||
NULL);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__fraction_part(cblc_field_t *dest,
|
||
const cblc_field_t *source,
|
||
size_t source_offset,
|
||
size_t source_size)
|
||
{
|
||
// FUNCTION INTEGER-PART
|
||
GCOB_FP128 value = __gg__float128_from_qualified_field(source,
|
||
source_offset,
|
||
source_size);
|
||
bool is_negative = false;
|
||
if( value < 0 )
|
||
{
|
||
is_negative = true;
|
||
value = -value;
|
||
}
|
||
|
||
GCOB_FP128 retval = value - FP128_FUNC(floor)(value);
|
||
|
||
if( is_negative )
|
||
{
|
||
retval = -retval;
|
||
}
|
||
__gg__float128_to_field(dest,
|
||
retval,
|
||
truncation_e,
|
||
NULL);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__log(cblc_field_t *dest,
|
||
const cblc_field_t *source,
|
||
size_t source_offset,
|
||
size_t source_size)
|
||
{
|
||
// FUNCTION LOG
|
||
GCOB_FP128 value = __gg__float128_from_qualified_field(source,
|
||
source_offset,
|
||
source_size);
|
||
if( value <= 0.00 )
|
||
{
|
||
exception_raise(ec_argument_function_e);
|
||
}
|
||
else
|
||
{
|
||
GCOB_FP128 retval = FP128_FUNC(log)(value);
|
||
__gg__float128_to_field(dest,
|
||
retval,
|
||
truncation_e,
|
||
NULL);
|
||
}
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__log10(cblc_field_t *dest,
|
||
const cblc_field_t *source,
|
||
size_t source_offset,
|
||
size_t source_size)
|
||
{
|
||
// FUNCTION LOG10
|
||
GCOB_FP128 value = __gg__float128_from_qualified_field(source,
|
||
source_offset,
|
||
source_size);
|
||
if( value <= 0.00 )
|
||
{
|
||
exception_raise(ec_argument_function_e);
|
||
}
|
||
else
|
||
{
|
||
GCOB_FP128 retval = FP128_FUNC(log10)(value);
|
||
__gg__float128_to_field(dest,
|
||
retval,
|
||
truncation_e,
|
||
NULL);
|
||
}
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__max(cblc_field_t *dest,
|
||
size_t ncount)
|
||
{
|
||
// FUNCTION MAX
|
||
|
||
if( ( __gg__treeplet_1f[0]->type == FldAlphanumeric
|
||
|| __gg__treeplet_1f[0]->type == FldLiteralA) )
|
||
{
|
||
cblc_field_t *best_field ;
|
||
unsigned char *best_location = nullptr ;
|
||
size_t best_length = 0 ;
|
||
int best_attr ;
|
||
int best_flags ;
|
||
|
||
bool first_time = true;
|
||
assert(ncount);
|
||
for(size_t i=0; i<ncount; i++)
|
||
{
|
||
refer_state_for_all state;
|
||
|
||
build_refer_state_for_all(state, __gg__treeplet_1f[i], __gg__fourplet_flags[i]);
|
||
|
||
for(;;)
|
||
{
|
||
if( first_time )
|
||
{
|
||
first_time = false;
|
||
best_field = __gg__treeplet_1f[i];
|
||
best_location = __gg__treeplet_1f[i]->data + __gg__treeplet_1o[i];
|
||
best_length = __gg__treeplet_1s[i];
|
||
best_attr = __gg__treeplet_1f[i]->attr;
|
||
best_flags = __gg__fourplet_flags[i];
|
||
}
|
||
else
|
||
{
|
||
cblc_field_t *candidate_field = __gg__treeplet_1f[i];
|
||
unsigned char *candidate_location = __gg__treeplet_1f[i]->data + __gg__treeplet_1o[i];
|
||
size_t candidate_length = __gg__treeplet_1s[i];
|
||
int candidate_attr = __gg__treeplet_1f[i]->attr;
|
||
int candidate_flags = __gg__fourplet_flags[i];
|
||
|
||
int compare_result = __gg__compare_2(
|
||
candidate_field,
|
||
candidate_location,
|
||
candidate_length,
|
||
candidate_attr,
|
||
candidate_flags,
|
||
best_field,
|
||
best_location,
|
||
best_length,
|
||
best_attr,
|
||
best_flags,
|
||
0);
|
||
if( compare_result >= 0 )
|
||
{
|
||
best_field = candidate_field ;
|
||
best_location = candidate_location ;
|
||
best_length = candidate_length ;
|
||
best_attr = candidate_attr ;
|
||
best_flags = candidate_flags ;
|
||
}
|
||
}
|
||
if( !update_refer_state_for_all(state, __gg__treeplet_1f[i]) )
|
||
{
|
||
// There is nothing left to do.
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
__gg__adjust_dest_size(dest, best_length);
|
||
dest->type = FldAlphanumeric;
|
||
assert(best_location);
|
||
memcpy(dest->data, best_location, best_length);
|
||
}
|
||
else
|
||
{
|
||
GCOB_FP128 retval;
|
||
bool first_time = true;
|
||
assert(ncount);
|
||
for(size_t i=0; i<ncount; i++)
|
||
{
|
||
refer_state_for_all state;
|
||
build_refer_state_for_all(state, __gg__treeplet_1f[i], __gg__fourplet_flags[i]);
|
||
|
||
for(;;)
|
||
{
|
||
if( first_time )
|
||
{
|
||
first_time = false;
|
||
retval = __gg__float128_from_qualified_field(__gg__treeplet_1f[i], __gg__treeplet_1o[i], __gg__treeplet_1s[i]);
|
||
}
|
||
else
|
||
{
|
||
GCOB_FP128 candidate = __gg__float128_from_qualified_field(__gg__treeplet_1f[i], __gg__treeplet_1o[i], __gg__treeplet_1s[i]);
|
||
if( candidate >= retval )
|
||
{
|
||
retval = candidate;
|
||
}
|
||
}
|
||
if( !update_refer_state_for_all(state, __gg__treeplet_1f[i]) )
|
||
{
|
||
// There is nothing left to do for that input.
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
__gg__float128_to_field(dest,
|
||
retval,
|
||
truncation_e,
|
||
NULL);
|
||
}
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__lower_case( cblc_field_t *dest,
|
||
const cblc_field_t *input,
|
||
size_t input_offset,
|
||
size_t input_size)
|
||
{
|
||
cbl_encoding_t from = input->encoding;
|
||
cbl_encoding_t to = dest->encoding;
|
||
charmap_t *charmap_dest = __gg__get_charmap(to);
|
||
|
||
size_t dest_length = dest->capacity;
|
||
size_t source_length = input_size;
|
||
size_t length = std::min(dest_length, source_length);
|
||
memset( dest->data,
|
||
charmap_dest->mapped_character(ascii_space),
|
||
dest_length);
|
||
memcpy(dest->data, input->data+input_offset, length);
|
||
__gg__convert_encoding_length(PTRCAST(char, dest->data),
|
||
length,
|
||
from,
|
||
DEFAULT_CHARMAP_SOURCE);
|
||
std::transform(dest->data, dest->data + dest_length, dest->data,
|
||
[](unsigned char c) { return std::tolower(c); });
|
||
__gg__convert_encoding_length(PTRCAST(char, dest->data),
|
||
length,
|
||
DEFAULT_CHARMAP_SOURCE,
|
||
to);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__mean( cblc_field_t *dest,
|
||
size_t ninputs)
|
||
{
|
||
// FUNCTION MEAN
|
||
size_t k_count;
|
||
GCOB_FP128 sum = kahan_summation(ninputs,
|
||
__gg__treeplet_1f,
|
||
__gg__treeplet_1o,
|
||
__gg__treeplet_1s,
|
||
__gg__fourplet_flags,
|
||
&k_count);
|
||
sum /= k_count;
|
||
__gg__float128_to_field(dest,
|
||
sum,
|
||
truncation_e,
|
||
NULL);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__median( cblc_field_t *dest,
|
||
size_t ncount)
|
||
{
|
||
// FUNCTION MEDIAN
|
||
|
||
// This is wasteful, because it allocates N values in order to sort them. It
|
||
// is also an O(NlogN) solution, when there are O(N) solutions available.
|
||
|
||
// It has the merit of being very simple.
|
||
|
||
// The future beckons, but not today.
|
||
|
||
size_t list_size = 1;
|
||
|
||
GCOB_FP128 *the_list = static_cast<GCOB_FP128 *>(malloc(list_size *sizeof(GCOB_FP128)));
|
||
massert(the_list);
|
||
size_t k_count = 0;
|
||
assert(ncount);
|
||
for(size_t i=0; i<ncount; i++)
|
||
{
|
||
refer_state_for_all state;
|
||
build_refer_state_for_all(state, __gg__treeplet_1f[i], __gg__fourplet_flags[i]);
|
||
|
||
for(;;)
|
||
{
|
||
if(k_count >= list_size)
|
||
{
|
||
list_size *= 2;
|
||
the_list = PTRCAST(GCOB_FP128, realloc(the_list, list_size *sizeof(GCOB_FP128)));
|
||
massert(the_list);
|
||
}
|
||
|
||
assert(the_list);
|
||
the_list[k_count] = __gg__float128_from_qualified_field(__gg__treeplet_1f[i],
|
||
__gg__treeplet_1o[i],
|
||
__gg__treeplet_1s[i]);
|
||
k_count += 1;
|
||
if( !update_refer_state_for_all(state, __gg__treeplet_1f[i]) )
|
||
{
|
||
// There is nothing left to do.
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
std::sort(the_list, the_list+k_count);
|
||
|
||
GCOB_FP128 retval;
|
||
size_t i=k_count/2;
|
||
if( k_count & 1 )
|
||
{
|
||
retval = the_list[i];
|
||
}
|
||
else
|
||
{
|
||
retval = (the_list[i-1] + the_list[i])/2.0;
|
||
}
|
||
__gg__float128_to_field(dest,
|
||
retval,
|
||
truncation_e,
|
||
NULL);
|
||
free(the_list);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__midrange( cblc_field_t *dest,
|
||
size_t ncount)
|
||
{
|
||
// FUNCTION MIDRANGE
|
||
GCOB_FP128 val;
|
||
GCOB_FP128 min=0;
|
||
GCOB_FP128 max=0;
|
||
bool first_time = true;
|
||
assert(ncount);
|
||
for(size_t i=0; i<ncount; i++)
|
||
{
|
||
refer_state_for_all state;
|
||
build_refer_state_for_all(state, __gg__treeplet_1f[i], __gg__fourplet_flags[i]);
|
||
for(;;)
|
||
{
|
||
val = __gg__float128_from_qualified_field(__gg__treeplet_1f[i],
|
||
__gg__treeplet_1o[i],
|
||
__gg__treeplet_1s[i]);
|
||
if( first_time )
|
||
{
|
||
first_time = false;
|
||
min = val;
|
||
max = val;
|
||
}
|
||
min = std::min(min, val);
|
||
max = std::max(max, val);
|
||
if( !update_refer_state_for_all(state, __gg__treeplet_1f[i]) )
|
||
{
|
||
// There is nothing left to do for that input.
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
GCOB_FP128 retval = (min + max)/2.0;
|
||
__gg__float128_to_field(dest,
|
||
retval,
|
||
truncation_e,
|
||
NULL);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__min(cblc_field_t *dest,
|
||
size_t ncount)
|
||
{
|
||
// FUNCTION MIN
|
||
|
||
if( ( __gg__treeplet_1f[0]->type == FldAlphanumeric
|
||
|| __gg__treeplet_1f[0]->type == FldLiteralA) )
|
||
{
|
||
cblc_field_t *best_field ;
|
||
unsigned char *best_location = nullptr ;
|
||
size_t best_length = 0 ;
|
||
int best_attr ;
|
||
int best_flags ;
|
||
|
||
bool first_time = true;
|
||
assert(ncount);
|
||
for(size_t i=0; i<ncount; i++)
|
||
{
|
||
refer_state_for_all state;
|
||
|
||
build_refer_state_for_all(state, __gg__treeplet_1f[i], __gg__fourplet_flags[i]);
|
||
|
||
for(;;)
|
||
{
|
||
if( first_time )
|
||
{
|
||
first_time = false;
|
||
best_field = __gg__treeplet_1f[i];
|
||
best_location = __gg__treeplet_1f[i]->data + __gg__treeplet_1o[i];
|
||
best_length = __gg__treeplet_1s[i];
|
||
best_attr = __gg__treeplet_1f[i]->attr;
|
||
best_flags = __gg__fourplet_flags[i];
|
||
}
|
||
else
|
||
{
|
||
cblc_field_t *candidate_field = __gg__treeplet_1f[i];
|
||
unsigned char *candidate_location = __gg__treeplet_1f[i]->data + __gg__treeplet_1o[i];
|
||
size_t candidate_length = __gg__treeplet_1s[i];
|
||
int candidate_attr = __gg__treeplet_1f[i]->attr;
|
||
int candidate_flags = __gg__fourplet_flags[i];
|
||
|
||
int compare_result = __gg__compare_2(
|
||
candidate_field,
|
||
candidate_location,
|
||
candidate_length,
|
||
candidate_attr,
|
||
candidate_flags,
|
||
best_field,
|
||
best_location,
|
||
best_length,
|
||
best_attr,
|
||
best_flags,
|
||
0);
|
||
if( compare_result < 0 )
|
||
{
|
||
best_field = candidate_field ;
|
||
best_location = candidate_location ;
|
||
best_length = candidate_length ;
|
||
best_attr = candidate_attr ;
|
||
best_flags = candidate_flags ;
|
||
}
|
||
}
|
||
if( !update_refer_state_for_all(state, __gg__treeplet_1f[i]) )
|
||
{
|
||
// There is nothing left to do.
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
__gg__adjust_dest_size(dest, best_length);
|
||
dest->type = FldAlphanumeric;
|
||
assert(best_location);
|
||
memcpy(dest->data, best_location, best_length);
|
||
}
|
||
else
|
||
{
|
||
GCOB_FP128 retval;
|
||
bool first_time = true;
|
||
assert(ncount);
|
||
for(size_t i=0; i<ncount; i++)
|
||
{
|
||
refer_state_for_all state;
|
||
build_refer_state_for_all(state, __gg__treeplet_1f[i], __gg__fourplet_flags[i]);
|
||
|
||
for(;;)
|
||
{
|
||
if( first_time )
|
||
{
|
||
first_time = false;
|
||
retval = __gg__float128_from_qualified_field(__gg__treeplet_1f[i], __gg__treeplet_1o[i], __gg__treeplet_1s[i]);
|
||
}
|
||
else
|
||
{
|
||
GCOB_FP128 candidate = __gg__float128_from_qualified_field(__gg__treeplet_1f[i], __gg__treeplet_1o[i], __gg__treeplet_1s[i]);
|
||
if( candidate < retval )
|
||
{
|
||
retval = candidate;
|
||
}
|
||
}
|
||
if( !update_refer_state_for_all(state, __gg__treeplet_1f[i]) )
|
||
{
|
||
// There is nothing left to do for that input.
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
__gg__float128_to_field(dest,
|
||
retval,
|
||
truncation_e,
|
||
NULL);
|
||
}
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__mod(cblc_field_t *dest,
|
||
cblc_field_t *source1,
|
||
size_t source1_offset,
|
||
size_t source1_size,
|
||
cblc_field_t *source2,
|
||
size_t source2_offset,
|
||
size_t source2_size)
|
||
{
|
||
// FUNCTION MOD
|
||
__int128 arg1 = __gg__integer_from_qualified_field(source1,
|
||
source1_offset,
|
||
source1_size);
|
||
__int128 arg2 = __gg__integer_from_qualified_field(source2,
|
||
source2_offset,
|
||
source2_size);
|
||
__int128 retval;
|
||
|
||
if( arg2 == 0 )
|
||
{
|
||
exception_raise(ec_argument_function_e);
|
||
retval = 0;
|
||
}
|
||
else
|
||
{
|
||
int sign_of_div = arg1 >= 0 ? 1 : -1 ;
|
||
sign_of_div *= arg2 >= 0 ? 1 : -1 ;
|
||
|
||
__int128 div = ( arg1 / arg2 ) ;
|
||
if( sign_of_div < 0 )
|
||
{
|
||
div -= 1;
|
||
}
|
||
|
||
retval = arg1 - arg2 * div ;
|
||
}
|
||
|
||
__gg__int128_to_field(dest,
|
||
retval,
|
||
NO_RDIGITS,
|
||
truncation_e,
|
||
NULL);
|
||
}
|
||
|
||
static int
|
||
numval( cblc_field_t *dest,
|
||
const cblc_field_t *input,
|
||
size_t input_offset,
|
||
size_t input_size)
|
||
{
|
||
// Returns the one-based character position of a bad character
|
||
// returns zero if it is okay
|
||
|
||
const char *p = PTRCAST(char, (input->data + input_offset));
|
||
const char *pend = p + input_size;
|
||
|
||
int errpos = 0;
|
||
__int128 retval = 0;
|
||
int retval_rdigits = 0;
|
||
|
||
charmap_t *charmap = __gg__get_charmap(input->encoding);
|
||
unsigned char decimal_point
|
||
= charmap->mapped_character(__gg__get_decimal_point());
|
||
int mapped_0 = charmap->mapped_character(ascii_0);
|
||
int mapped_9 = charmap->mapped_character(ascii_9);
|
||
int mapped_space = charmap->mapped_character(ascii_space);
|
||
int mapped_plus = charmap->mapped_character(ascii_plus);
|
||
int mapped_minus = charmap->mapped_character(ascii_minus);
|
||
|
||
bool saw_digit= false;
|
||
bool in_fraction = false;
|
||
bool leading_sign = false;
|
||
bool is_negative = false;
|
||
enum
|
||
{
|
||
SPACE1,
|
||
SPACE2,
|
||
DIGITS,
|
||
SPACE3,
|
||
SPACE4,
|
||
} state = SPACE1;
|
||
|
||
if( input_size == 0 )
|
||
{
|
||
errpos = 1;
|
||
goto done;
|
||
}
|
||
while( p < pend )
|
||
{
|
||
unsigned char ch = *p++;
|
||
errpos += 1;
|
||
switch( state )
|
||
{
|
||
case SPACE1:
|
||
// We tolerate spaces, and expect to end with a sign, digit,
|
||
// or decimal point:
|
||
if( ch == mapped_space )
|
||
{
|
||
continue;
|
||
}
|
||
if( ch == mapped_plus )
|
||
{
|
||
leading_sign = true;
|
||
state = SPACE2;
|
||
break;
|
||
}
|
||
if( ch == mapped_minus )
|
||
{
|
||
leading_sign = true;
|
||
is_negative = true;
|
||
state = SPACE2;
|
||
break;
|
||
}
|
||
if( ch >= mapped_0 && ch <= mapped_9 )
|
||
{
|
||
saw_digit = true;
|
||
retval = ch & 0xF;
|
||
state = DIGITS;
|
||
break;
|
||
}
|
||
if( ch == decimal_point )
|
||
{
|
||
in_fraction = true;
|
||
state = DIGITS;
|
||
break;
|
||
}
|
||
// This is a bad character; errpos is correct
|
||
goto done;
|
||
break;
|
||
|
||
case SPACE2:
|
||
// We tolerate spaces, and expect to end with a digit or decimal point:
|
||
if( ch == mapped_space )
|
||
{
|
||
break;
|
||
}
|
||
if( ch >= mapped_0 && ch <= mapped_9 )
|
||
{
|
||
saw_digit = true;
|
||
retval = ch & 0xF;
|
||
state = DIGITS;
|
||
break;
|
||
}
|
||
if( ch == decimal_point )
|
||
{
|
||
in_fraction = true;
|
||
state = DIGITS;
|
||
break;
|
||
}
|
||
// This is a bad character; errpos is correct
|
||
goto done;
|
||
break;
|
||
|
||
case DIGITS:
|
||
// We tolerate digits. We tolerate one decimal point. We expect to
|
||
// end with a space, a sign, "DB" or "CR", or the the end of the string
|
||
// It's a bit complicated
|
||
|
||
if( ch >= mapped_0 && ch <= mapped_9 )
|
||
{
|
||
saw_digit = true;
|
||
retval *= 10;
|
||
retval += ch & 0xF;
|
||
if( in_fraction )
|
||
{
|
||
retval_rdigits += 1;
|
||
}
|
||
break;
|
||
}
|
||
if( ch == decimal_point && in_fraction )
|
||
{
|
||
// Only one decimal is allowed
|
||
goto done;
|
||
}
|
||
if( ch == decimal_point )
|
||
{
|
||
in_fraction = true;
|
||
break;
|
||
}
|
||
if( ch == mapped_space )
|
||
{
|
||
state = SPACE3;
|
||
break;
|
||
}
|
||
if( ch == mapped_plus && leading_sign)
|
||
{
|
||
// We are allowed leading or trailing signs, but not both
|
||
goto done;
|
||
}
|
||
if( ch == mapped_minus && leading_sign)
|
||
{
|
||
// We are allowed leading or trailing signs, but not both
|
||
goto done;
|
||
}
|
||
if( ch == mapped_plus )
|
||
{
|
||
state = SPACE4;
|
||
break;
|
||
}
|
||
if( ch == mapped_minus )
|
||
{
|
||
is_negative = true;
|
||
state = SPACE4;
|
||
break;
|
||
}
|
||
if( std::tolower(ch) == 'd' )
|
||
{
|
||
if( leading_sign )
|
||
{
|
||
goto done;
|
||
}
|
||
ch = *p++;
|
||
errpos += 1;
|
||
if( p > pend || std::tolower(ch) != 'b' )
|
||
{
|
||
goto done;
|
||
}
|
||
is_negative = true;
|
||
state = SPACE4;
|
||
break;
|
||
}
|
||
if( std::tolower(ch) == 'c' )
|
||
{
|
||
if( leading_sign )
|
||
{
|
||
goto done;
|
||
}
|
||
ch = *p++;
|
||
errpos += 1;
|
||
if( p > pend || std::tolower(ch) != 'r' )
|
||
{
|
||
goto done;
|
||
}
|
||
is_negative = true;
|
||
state = SPACE4;
|
||
break;
|
||
}
|
||
// This is a bad character; errpos is correct
|
||
goto done;
|
||
break;
|
||
|
||
case SPACE3:
|
||
// We tolerate spaces, or we end with a sign:
|
||
if( ch == mapped_space )
|
||
{
|
||
break;
|
||
}
|
||
if( ch == mapped_plus && leading_sign)
|
||
{
|
||
// We are allowed leading or trailing signs, but not both
|
||
goto done;
|
||
}
|
||
if( ch == mapped_minus && leading_sign)
|
||
{
|
||
// We are allowed leading or trailing signs, but not both
|
||
goto done;
|
||
}
|
||
if( ch == mapped_plus )
|
||
{
|
||
state = SPACE4;
|
||
break;
|
||
}
|
||
if( ch == mapped_minus )
|
||
{
|
||
is_negative = true;
|
||
state = SPACE4;
|
||
break;
|
||
}
|
||
if( std::tolower(ch) == 'd' )
|
||
{
|
||
if( leading_sign )
|
||
{
|
||
goto done;
|
||
}
|
||
ch = *p++;
|
||
errpos += 1;
|
||
if( p > pend || std::tolower(ch) != 'b' )
|
||
{
|
||
goto done;
|
||
}
|
||
is_negative = true;
|
||
state = SPACE4;
|
||
break;
|
||
}
|
||
if( std::tolower(ch) == 'c' )
|
||
{
|
||
if( leading_sign )
|
||
{
|
||
goto done;
|
||
}
|
||
ch = *p++;
|
||
errpos += 1;
|
||
if( p > pend || std::tolower(ch) != 'r' )
|
||
{
|
||
goto done;
|
||
}
|
||
is_negative = true;
|
||
state = SPACE4;
|
||
break;
|
||
}
|
||
goto done;
|
||
break;
|
||
case SPACE4:
|
||
if( ch == mapped_space )
|
||
{
|
||
break;
|
||
}
|
||
goto done;
|
||
break;
|
||
}
|
||
}
|
||
if( saw_digit )
|
||
{
|
||
errpos = 0;
|
||
}
|
||
else if( p == pend )
|
||
{
|
||
// If we got to the end without seeing adigit, we need to bump the
|
||
// error pointer:
|
||
errpos += 1;
|
||
}
|
||
|
||
done:
|
||
if(errpos)
|
||
{
|
||
retval = 0;
|
||
}
|
||
if( is_negative )
|
||
{
|
||
retval = -retval;
|
||
}
|
||
if(dest)
|
||
{
|
||
__gg__int128_to_field(dest,
|
||
retval,
|
||
retval_rdigits,
|
||
truncation_e,
|
||
NULL);
|
||
}
|
||
return errpos;
|
||
}
|
||
|
||
static
|
||
int
|
||
numval_c( cblc_field_t *dest,
|
||
const cblc_field_t *src,
|
||
size_t src_offset,
|
||
size_t src_size,
|
||
const cblc_field_t *crcy,
|
||
size_t crcy_offset,
|
||
size_t crcy_size
|
||
)
|
||
{
|
||
size_t errcode = 0;
|
||
|
||
char *pstart = PTRCAST(char, (src->data+src_offset));
|
||
char *pend = pstart + src_size;
|
||
char *p = pstart;
|
||
|
||
GCOB_FP128 retval = 0;
|
||
int sign = 0;
|
||
int rdigits = 0;
|
||
int rdigit_bump = 0;
|
||
charmap_t *charmap = __gg__get_charmap(src->encoding);
|
||
unsigned char decimal_point
|
||
= charmap->mapped_character(__gg__get_decimal_point());
|
||
unsigned char decimal_separator
|
||
= charmap->mapped_character(__gg__get_decimal_separator());
|
||
int mapped_0 = charmap->mapped_character(ascii_0);
|
||
int mapped_9 = charmap->mapped_character(ascii_9);
|
||
int mapped_space = charmap->mapped_character(ascii_space);
|
||
int mapped_plus = charmap->mapped_character(ascii_plus);
|
||
int mapped_minus = charmap->mapped_character(ascii_minus);
|
||
int mapped_C = charmap->mapped_character(ascii_C);
|
||
int mapped_R = charmap->mapped_character(ascii_R);
|
||
int mapped_D = charmap->mapped_character(ascii_D);
|
||
int mapped_B = charmap->mapped_character(ascii_B);
|
||
int mapped_c = charmap->mapped_character(ascii_c);
|
||
int mapped_r = charmap->mapped_character(ascii_r);
|
||
int mapped_d = charmap->mapped_character(ascii_d);
|
||
int mapped_b = charmap->mapped_character(ascii_b);
|
||
|
||
char *currency_start;
|
||
char *currency_end;
|
||
if( crcy )
|
||
{
|
||
currency_start = PTRCAST(char, (crcy->data+crcy_offset));
|
||
currency_end = currency_start + crcy_size;
|
||
}
|
||
else
|
||
{
|
||
currency_start = __gg__get_default_currency_string();
|
||
currency_end = currency_start + strlen(currency_start);
|
||
}
|
||
char *pcurrency = currency_start;
|
||
// Trim off spaces from the currency:
|
||
while( *pcurrency == mapped_space && pcurrency < currency_end )
|
||
{
|
||
pcurrency += 1;
|
||
}
|
||
|
||
while( *(currency_end-1) == mapped_space && currency_end > currency_start )
|
||
{
|
||
currency_end -= 1;
|
||
}
|
||
|
||
// We will do this as a state machine:
|
||
|
||
enum
|
||
{
|
||
first_space,
|
||
first_sign,
|
||
second_space,
|
||
currency,
|
||
before_digits,
|
||
digits,
|
||
after_digits,
|
||
second_sign,
|
||
final_space,
|
||
} state = first_space;
|
||
|
||
while( p < pend )
|
||
{
|
||
unsigned char ch = *p++;
|
||
switch( state )
|
||
{
|
||
case first_space :
|
||
// Eat up spaces, if any, and then dispatch on the first non-space:
|
||
if( ch != mapped_space )
|
||
{
|
||
// ch can now be a plus, a minus, a digit, or the first character
|
||
// of the currency string
|
||
if( ch == mapped_plus
|
||
|| ch == mapped_minus )
|
||
{
|
||
state = first_sign;
|
||
// Decrement to pointer in order to pick up the character again
|
||
p -= 1;
|
||
}
|
||
else if( ch == *pcurrency )
|
||
{
|
||
state = currency;
|
||
p -= 1;
|
||
}
|
||
else if( (ch >= mapped_0 && ch <= mapped_9)
|
||
|| ch == decimal_point )
|
||
{
|
||
state = digits;
|
||
p -= 1;
|
||
}
|
||
else
|
||
{
|
||
// We have a bad character. Set the errcode to be the position of
|
||
// the bad character, and adjust p to break out of the loop.
|
||
// Set the state so that the default error processing is suppressed
|
||
state = final_space;
|
||
errcode = p - pstart;
|
||
p = pend;
|
||
}
|
||
}
|
||
break;
|
||
|
||
case first_sign :
|
||
// We know the character is a plus or a minus:
|
||
if( ch == mapped_plus )
|
||
{
|
||
sign = 1;
|
||
state = second_space;
|
||
}
|
||
else
|
||
{
|
||
sign = -1;
|
||
state = second_space;
|
||
}
|
||
break;
|
||
|
||
case second_space :
|
||
// Eat up spaces, if any. This segment has to end with a currency or
|
||
// a digit:
|
||
if( ch != mapped_space )
|
||
{
|
||
if( ch == *pcurrency )
|
||
{
|
||
state = currency;
|
||
p -= 1;
|
||
}
|
||
else if( (ch >= mapped_0 && ch <= mapped_9)
|
||
|| ch == decimal_point )
|
||
{
|
||
state = digits;
|
||
p -= 1;
|
||
}
|
||
else
|
||
{
|
||
// We have a bad character. Set the errcode to be the position of
|
||
// the bad character, and adjust p to break out of the loop.
|
||
state = final_space;
|
||
errcode = p - pstart;
|
||
p = pend;
|
||
}
|
||
}
|
||
break;
|
||
|
||
case currency :
|
||
// At this point, the only valid character is the next character
|
||
// in the currency string:
|
||
if( pcurrency >= currency_end )
|
||
{
|
||
// Hey! Look at us! We got through the whole currency string.
|
||
state = before_digits;
|
||
p -= 1;
|
||
}
|
||
else if( ch == *pcurrency++)
|
||
{
|
||
// We are still marching through the currency
|
||
}
|
||
else
|
||
{
|
||
// We have a bad character:
|
||
errcode = p - pstart;
|
||
state = final_space;
|
||
p = pend;
|
||
}
|
||
break;
|
||
|
||
case before_digits :
|
||
// Eat up spaces, if any. This segment has to end with a digit
|
||
if( ch != mapped_space )
|
||
{
|
||
if( (ch >= mapped_0 && ch <= mapped_9)
|
||
|| ch == decimal_point )
|
||
{
|
||
state = digits;
|
||
p -= 1;
|
||
}
|
||
else
|
||
{
|
||
// We have a bad character. Set the errcode to be the position of
|
||
// the bad character, and adjust p to break out of the loop.
|
||
state = final_space;
|
||
errcode = p - pstart;
|
||
p = pend;
|
||
}
|
||
}
|
||
break;
|
||
|
||
case digits :
|
||
// The only thing allowed here are digits, decimal points, and
|
||
// decimal separators
|
||
if( ch >= mapped_0 && ch <= mapped_9 )
|
||
{
|
||
// We have a digit.
|
||
rdigits += rdigit_bump;
|
||
retval *= 10;
|
||
retval += ch & 0x0F;
|
||
}
|
||
else if( ch == decimal_point && rdigit_bump)
|
||
{
|
||
// We have a second decimal_point, which is against the rules
|
||
errcode = p - pstart;
|
||
state = final_space;
|
||
p = pend;
|
||
}
|
||
else if( ch == decimal_separator )
|
||
{
|
||
// Commas are ignored
|
||
}
|
||
else if( ch == decimal_point )
|
||
{
|
||
rdigit_bump = 1;
|
||
}
|
||
else
|
||
{
|
||
// We have something that isn't a digit or decimal point or decimal
|
||
// separator:
|
||
state = after_digits;
|
||
p -= 1;
|
||
}
|
||
break;
|
||
|
||
case after_digits :
|
||
// after digits, the only valid things are spaces, plus, minus, D, or C
|
||
if( ch != charmap->mapped_character(ascii_space) )
|
||
{
|
||
if( ch == mapped_plus
|
||
|| ch == mapped_minus
|
||
|| ch == mapped_D
|
||
|| ch == mapped_d
|
||
|| ch == mapped_C
|
||
|| ch == mapped_c )
|
||
{
|
||
state = second_sign;
|
||
p -= 1;
|
||
}
|
||
}
|
||
break;
|
||
|
||
case second_sign :
|
||
if( sign )
|
||
{
|
||
// A second sign isn't allowed
|
||
errcode = p - pstart;
|
||
p = pend;
|
||
}
|
||
if( ch == mapped_plus )
|
||
{
|
||
sign = 1;
|
||
}
|
||
else if( ch == mapped_minus )
|
||
{
|
||
sign = -1;
|
||
}
|
||
else if( (ch == mapped_D || ch == mapped_d)
|
||
&& p < pend
|
||
&& (*p == mapped_B || *p == mapped_b) )
|
||
{
|
||
sign = -1;
|
||
p += 1;
|
||
}
|
||
else if( (ch == mapped_C || ch == mapped_c)
|
||
&& p < pend
|
||
&& (*p == mapped_R || *p == mapped_r) )
|
||
{
|
||
sign = -1;
|
||
p += 1;
|
||
}
|
||
state = final_space;
|
||
break;
|
||
|
||
case final_space :
|
||
// There should be only spaces until the end
|
||
if( ch == mapped_space )
|
||
{
|
||
continue;
|
||
}
|
||
// We have a non-space where there should be only space
|
||
state = final_space;
|
||
errcode = p - pstart;
|
||
p = pend;
|
||
break;
|
||
}
|
||
}
|
||
if( sign == 0 )
|
||
{
|
||
sign = 1;
|
||
}
|
||
retval *= sign;
|
||
|
||
if( state != after_digits && state != final_space && state != digits )
|
||
{
|
||
// We broke out of the loop too soon:
|
||
errcode = pend - pstart + 1;
|
||
}
|
||
|
||
if( dest )
|
||
{
|
||
retval /= __gg__power_of_ten(rdigits);
|
||
__gg__float128_to_field(dest,
|
||
retval,
|
||
truncation_e,
|
||
NULL);
|
||
}
|
||
return (int)errcode;
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__numval( cblc_field_t *dest,
|
||
const cblc_field_t *source,
|
||
size_t source_offset,
|
||
size_t source_size)
|
||
{
|
||
int errpos = numval(dest, source, source_offset, source_size);
|
||
if( errpos )
|
||
{
|
||
exception_raise(ec_argument_function_e);
|
||
}
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__test_numval(cblc_field_t *dest,
|
||
const cblc_field_t *source,
|
||
size_t source_offset,
|
||
size_t source_size)
|
||
{
|
||
int retval = numval(NULL, source, source_offset, source_size);
|
||
__gg__int128_to_field(dest,
|
||
retval,
|
||
NO_RDIGITS,
|
||
truncation_e,
|
||
NULL);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__numval_c( cblc_field_t *dest,
|
||
const cblc_field_t *src,
|
||
size_t src_offset,
|
||
size_t src_size,
|
||
const cblc_field_t *crcy,
|
||
size_t crcy_offset,
|
||
size_t crcy_size
|
||
)
|
||
{
|
||
numval_c( dest,
|
||
src,
|
||
src_offset,
|
||
src_size,
|
||
crcy,
|
||
crcy_offset,
|
||
crcy_size);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__test_numval_c(cblc_field_t *dest,
|
||
const cblc_field_t *src,
|
||
size_t src_offset,
|
||
size_t src_size,
|
||
const cblc_field_t *crcy,
|
||
size_t crcy_offset,
|
||
size_t crcy_size
|
||
)
|
||
{
|
||
int retval = numval_c(NULL,
|
||
src,
|
||
src_offset,
|
||
src_size,
|
||
crcy,
|
||
crcy_offset,
|
||
crcy_size);
|
||
__gg__int128_to_field(dest,
|
||
retval,
|
||
NO_RDIGITS,
|
||
truncation_e,
|
||
NULL);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__ord(cblc_field_t *dest,
|
||
const cblc_field_t *input,
|
||
size_t input_offset,
|
||
size_t /*input_size*/)
|
||
{
|
||
// FUNCTION ORD
|
||
const char *arg = PTRCAST(char, (input->data + input_offset));
|
||
|
||
// The ORD function takes a single-character string and returns the
|
||
// ordinal position of that character.
|
||
|
||
size_t retval = (arg[0]&0xFF) + 1;
|
||
__gg__int128_to_field(dest,
|
||
retval,
|
||
NO_RDIGITS,
|
||
truncation_e,
|
||
NULL);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__ord_min(cblc_field_t *dest,
|
||
size_t ninputs)
|
||
{
|
||
// Sets dest to the one-based ordinal position of the first occurrence
|
||
// of the biggest element in the list of refs[]
|
||
|
||
int retval = -1;
|
||
int running_position = -1;
|
||
|
||
cblc_field_t *best;
|
||
unsigned char *best_location;
|
||
size_t best_length;
|
||
int best_attr;
|
||
int best_flags;
|
||
|
||
unsigned char *candidate_location;
|
||
size_t candidate_length;
|
||
int candidate_attr;
|
||
int candidate_flags;
|
||
|
||
for( size_t i=0; i<ninputs; i++ )
|
||
{
|
||
refer_state_for_all state;
|
||
|
||
build_refer_state_for_all(state, __gg__treeplet_1f[i], __gg__fourplet_flags[i]);
|
||
for(;;)
|
||
{
|
||
running_position += 1;
|
||
if( retval == -1)
|
||
{
|
||
// We have to initialize the comparisons:
|
||
retval = running_position;
|
||
best = __gg__treeplet_1f[i];
|
||
best_location = __gg__treeplet_1f[i]->data + __gg__treeplet_1o[i];
|
||
best_length = __gg__treeplet_1s[i];
|
||
best_attr = __gg__treeplet_1f[i]->attr;
|
||
best_flags = __gg__fourplet_flags[i];
|
||
}
|
||
else
|
||
{
|
||
// We need to save the current adjustments, because __gg__compare
|
||
// is free to modify .location
|
||
candidate_location = __gg__treeplet_1f[i]->data + __gg__treeplet_1o[i];
|
||
candidate_length = __gg__treeplet_1s[i];
|
||
candidate_attr = __gg__treeplet_1f[i]->attr;
|
||
candidate_flags = __gg__fourplet_flags[i];
|
||
|
||
int compare_result =
|
||
__gg__compare_2(
|
||
__gg__treeplet_1f[i],
|
||
candidate_location,
|
||
candidate_length,
|
||
candidate_attr,
|
||
candidate_flags,
|
||
best,
|
||
best_location,
|
||
best_length,
|
||
best_attr,
|
||
best_flags,
|
||
0);
|
||
if( compare_result < 0 )
|
||
{
|
||
retval = running_position;
|
||
best = __gg__treeplet_1f[i];
|
||
best_location = candidate_location;
|
||
best_length = candidate_length;
|
||
best_attr = candidate_attr;
|
||
best_flags = candidate_flags;
|
||
}
|
||
}
|
||
if( !update_refer_state_for_all(state, __gg__treeplet_1f[i]) )
|
||
{
|
||
// There is nothing left to do for that input.
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
retval += 1;
|
||
__gg__int128_to_field(dest,
|
||
retval,
|
||
NO_RDIGITS,
|
||
truncation_e,
|
||
NULL);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__ord_max(cblc_field_t *dest,
|
||
size_t ninputs)
|
||
{
|
||
// Sets dest to the one-based ordinal position of the first occurrence
|
||
// of the biggest element in the list of refs[]
|
||
|
||
int retval = -1;
|
||
int running_position = -1;
|
||
|
||
cblc_field_t *best;
|
||
unsigned char *best_location;
|
||
size_t best_length;
|
||
int best_attr;
|
||
int best_flags;
|
||
|
||
unsigned char *candidate_location;
|
||
size_t candidate_length;
|
||
int candidate_attr;
|
||
int candidate_flags;
|
||
|
||
for( size_t i=0; i<ninputs; i++ )
|
||
{
|
||
refer_state_for_all state;
|
||
|
||
build_refer_state_for_all(state, __gg__treeplet_1f[i], __gg__fourplet_flags[i]);
|
||
for(;;)
|
||
{
|
||
running_position += 1;
|
||
if( retval == -1)
|
||
{
|
||
// We have to initialize the comparisons:
|
||
retval = running_position;
|
||
best = __gg__treeplet_1f[i];
|
||
best_location = __gg__treeplet_1f[i]->data + __gg__treeplet_1o[i];
|
||
best_length = __gg__treeplet_1s[i];
|
||
best_attr = __gg__treeplet_1f[i]->attr;
|
||
best_flags = __gg__fourplet_flags[i];
|
||
}
|
||
else
|
||
{
|
||
// We need to save the current adjustments, because __gg__compare
|
||
// is free to modify .location
|
||
candidate_location = __gg__treeplet_1f[i]->data + __gg__treeplet_1o[i];
|
||
candidate_length = __gg__treeplet_1s[i];
|
||
candidate_attr = __gg__treeplet_1f[i]->attr;
|
||
candidate_flags = __gg__fourplet_flags[i];
|
||
|
||
int compare_result =
|
||
__gg__compare_2(
|
||
__gg__treeplet_1f[i],
|
||
candidate_location,
|
||
candidate_length,
|
||
candidate_attr,
|
||
candidate_flags,
|
||
best,
|
||
best_location,
|
||
best_length,
|
||
best_attr,
|
||
best_flags,
|
||
0);
|
||
if( compare_result > 0 )
|
||
{
|
||
retval = running_position;
|
||
best = __gg__treeplet_1f[i];
|
||
best_location = candidate_location;
|
||
best_length = candidate_length;
|
||
best_attr = candidate_attr;
|
||
best_flags = candidate_flags;
|
||
}
|
||
}
|
||
if( !update_refer_state_for_all(state, __gg__treeplet_1f[i]) )
|
||
{
|
||
// There is nothing left to do for that input.
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
retval += 1; // Make the result one-based, as per COBOL specification
|
||
__gg__int128_to_field(dest,
|
||
retval,
|
||
NO_RDIGITS,
|
||
truncation_e,
|
||
NULL);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__pi(cblc_field_t *dest)
|
||
{
|
||
// FUNCTION PI
|
||
|
||
static GCOB_FP128 pi
|
||
= GCOB_FP128_LITERAL(3.141592653589793238462643383279502884);
|
||
__gg__float128_to_field(dest,
|
||
pi,
|
||
truncation_e,
|
||
NULL);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__present_value(cblc_field_t *dest,
|
||
size_t ncount)
|
||
{
|
||
GCOB_FP128 discount = 0;;
|
||
GCOB_FP128 denom = 1;
|
||
|
||
GCOB_FP128 retval = 0;
|
||
bool first_time = true;
|
||
for(size_t i=0; i<ncount; i++)
|
||
{
|
||
refer_state_for_all state;
|
||
build_refer_state_for_all(state, __gg__treeplet_1f[i], __gg__fourplet_flags[i]);
|
||
for(;;)
|
||
{
|
||
if(first_time)
|
||
{
|
||
first_time = false;
|
||
GCOB_FP128 arg1 = __gg__float128_from_qualified_field(__gg__treeplet_1f[i],
|
||
__gg__treeplet_1o[i],
|
||
__gg__treeplet_1s[i]);
|
||
if( arg1 <= GCOB_FP128_LITERAL(-1.0) )
|
||
{
|
||
exception_raise(ec_argument_function_e);
|
||
break;
|
||
}
|
||
discount = GCOB_FP128_LITERAL(1.0) / (GCOB_FP128_LITERAL(1.0) + arg1);
|
||
}
|
||
else
|
||
{
|
||
GCOB_FP128 arg = __gg__float128_from_qualified_field(__gg__treeplet_1f[i],
|
||
__gg__treeplet_1o[i],
|
||
__gg__treeplet_1s[i]);
|
||
denom *= discount;
|
||
retval += arg * denom;
|
||
}
|
||
if( !update_refer_state_for_all(state, __gg__treeplet_1f[i]) )
|
||
{
|
||
// There is nothing left to do for that input.
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
__gg__float128_to_field(dest,
|
||
retval,
|
||
truncation_e,
|
||
NULL);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__range(cblc_field_t *dest,
|
||
size_t ncount)
|
||
{
|
||
// FUNCTION RANGE
|
||
bool first_time = true;
|
||
GCOB_FP128 val;
|
||
GCOB_FP128 min;
|
||
GCOB_FP128 max;
|
||
|
||
assert(ncount > 0);
|
||
for(size_t i=0; i<ncount; i++)
|
||
{
|
||
refer_state_for_all state;
|
||
build_refer_state_for_all(state, __gg__treeplet_1f[i], __gg__fourplet_flags[i]);
|
||
for(;;)
|
||
{
|
||
val = __gg__float128_from_qualified_field(__gg__treeplet_1f[i],
|
||
__gg__treeplet_1o[i],
|
||
__gg__treeplet_1s[i]);
|
||
if( first_time )
|
||
{
|
||
first_time = false;
|
||
min = val;
|
||
max = val;
|
||
}
|
||
min = std::min(min, val);
|
||
max = std::max(max, val);
|
||
if( !update_refer_state_for_all(state, __gg__treeplet_1f[i]) )
|
||
{
|
||
// There is nothing left to do.
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
GCOB_FP128 retval = max - min;
|
||
__gg__float128_to_field(dest,
|
||
retval,
|
||
truncation_e,
|
||
NULL);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__rem(cblc_field_t *dest,
|
||
const cblc_field_t *par1,
|
||
size_t par1_offset,
|
||
size_t par1_size,
|
||
const cblc_field_t *par2,
|
||
size_t par2_offset,
|
||
size_t par2_size)
|
||
{
|
||
// FUNCTION REM
|
||
//
|
||
// Note: REM takes two NUMERICS, not necessarily integers
|
||
|
||
// The ISO spec says:
|
||
// ((argument-1) – ((argument-2) * FUNCTION INTEGER-PART ((argument-1) / (argument-2))))
|
||
|
||
GCOB_FP128 arg1 = __gg__float128_from_qualified_field( par1,
|
||
par1_offset,
|
||
par1_size);
|
||
GCOB_FP128 arg2 = __gg__float128_from_qualified_field( par2,
|
||
par2_offset,
|
||
par2_size);
|
||
|
||
GCOB_FP128 intpart;
|
||
GCOB_FP128 retval;
|
||
if( arg2 == 0 )
|
||
{
|
||
exception_raise(ec_argument_function_e);
|
||
retval = 0;
|
||
}
|
||
else
|
||
{
|
||
FP128_FUNC(modf)(arg1 / arg2, &intpart);
|
||
retval = arg1 - arg2 * intpart;
|
||
}
|
||
|
||
__gg__float128_to_field(dest,
|
||
retval,
|
||
truncation_e,
|
||
NULL);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__trim( cblc_field_t *dest,
|
||
const cblc_field_t *arg1,
|
||
size_t arg1_offset,
|
||
size_t arg1_size,
|
||
const cblc_field_t *arg2,
|
||
size_t arg2_offset,
|
||
size_t arg2_size)
|
||
{
|
||
cbl_encoding_t from = arg1->encoding;
|
||
cbl_encoding_t to = dest->encoding;
|
||
charmap_t *charmap = __gg__get_charmap(to);
|
||
int mapped_space = charmap->mapped_character(ascii_space);
|
||
|
||
int rdigits;
|
||
__int128 type = __gg__binary_value_from_qualified_field(&rdigits,
|
||
arg2,
|
||
arg2_offset,
|
||
arg2_size);
|
||
//static const int BOTH = 0;
|
||
static const int LEADING = 1; // Remove leading spaces
|
||
static const int TRAILING = 2; // Remove trailing spaces
|
||
|
||
if( dest->type != FldAlphanumeric ||
|
||
!(dest->attr & intermediate_e) )
|
||
{
|
||
fprintf(stderr,
|
||
"We expect the target of a FUNCTION TRIM to "
|
||
"be an intermediate alphanumeric\n");
|
||
abort();
|
||
}
|
||
dest->capacity = dest->offset;
|
||
|
||
// Make a copy of the input:
|
||
char *copy = static_cast<char *>(malloc(arg1_size));
|
||
massert(copy);
|
||
memcpy(copy, arg1->data+arg1_offset, arg1_size);
|
||
|
||
// Convert it to the destination encoding
|
||
__gg__convert_encoding_length(copy, arg1_size, from, to);
|
||
|
||
|
||
// No matter what, we want to find the leftmost non-space and the
|
||
// rightmost non-space:
|
||
|
||
char *left = copy;
|
||
char *right = left + arg1_size-1;
|
||
|
||
// Find left and right: the first and last non-spaces
|
||
while( left <= right )
|
||
{
|
||
if( *left != mapped_space && *right != mapped_space )
|
||
{
|
||
break;
|
||
}
|
||
if( *left == mapped_space )
|
||
{
|
||
left += 1;
|
||
}
|
||
if( *right == mapped_space )
|
||
{
|
||
right -= 1;
|
||
}
|
||
}
|
||
if( type == LEADING )
|
||
{
|
||
// We want to leave any trailing spaces, so we return 'right' to its
|
||
// original value:
|
||
right = copy + arg1_size-1;
|
||
}
|
||
else if( type == TRAILING )
|
||
{
|
||
// We want to leave any leading spaces, so we return 'left' to its
|
||
// original value:
|
||
left = copy;
|
||
}
|
||
|
||
if( left > right )
|
||
{
|
||
// When the arg1 input string was empty, we want left to be right+1.
|
||
// The left/right loop can sometimes end up with left equal to right+2.
|
||
// That needs to be fixed:
|
||
left = right+1;
|
||
}
|
||
|
||
size_t ncount = right+1 - left;
|
||
__gg__adjust_dest_size(dest, ncount);
|
||
|
||
char *dest_left = PTRCAST(char, dest->data);
|
||
char *dest_right = dest_left + dest->capacity - 1;
|
||
const char *dest_end = dest_left + dest->capacity;
|
||
|
||
while( dest_left <= dest_right && left <= right )
|
||
{
|
||
*dest_left++ = *left++;
|
||
}
|
||
while(dest_left < dest_end)
|
||
{
|
||
*dest_left++ = mapped_space;
|
||
}
|
||
}
|
||
|
||
#if HAVE_INITSTATE_R && HAVE_SRANDOM_R && HAVE_RANDOM_R
|
||
static struct random_data *buf = NULL;
|
||
static char *state = NULL;
|
||
static const size_t state_len = 256;
|
||
#else
|
||
static unsigned seed = 0;
|
||
#endif
|
||
|
||
extern "C"
|
||
void
|
||
__gg__random( cblc_field_t *dest,
|
||
const cblc_field_t *input,
|
||
size_t input_offset,
|
||
size_t input_size)
|
||
{
|
||
int32_t retval_31;
|
||
int rdigits;
|
||
#if HAVE_INITSTATE_R && HAVE_SRANDOM_R && HAVE_RANDOM_R
|
||
// This creates a thread-safe pseudo-random number generator
|
||
// using input as the seed
|
||
|
||
// The return value is between zero and not quite one
|
||
|
||
if( !buf )
|
||
{
|
||
// This is the very first time through
|
||
buf = (random_data *)malloc(sizeof(struct random_data));
|
||
buf->state = NULL;
|
||
state = static_cast<char *>(malloc(state_len));
|
||
|
||
struct cbl_timespec ts;
|
||
__gg__clock_gettime(&ts);
|
||
initstate_r( ts.tv_nsec, state, state_len, buf);
|
||
}
|
||
int seed = (int)__gg__binary_value_from_qualified_field(&rdigits,
|
||
input,
|
||
input_offset,
|
||
input_size);
|
||
srandom_r(seed, buf);
|
||
|
||
random_r(buf, &retval_31);
|
||
#else
|
||
seed = (unsigned)__gg__binary_value_from_qualified_field(&rdigits,
|
||
input,
|
||
input_offset,
|
||
input_size);
|
||
srandom (seed);
|
||
retval_31 = random ();
|
||
#endif
|
||
// We are going to convert this to a value between zero and not quite one:
|
||
double retval = double(retval_31) / double(0x80000000UL);
|
||
__gg__double_to_target( dest,
|
||
retval,
|
||
truncation_e);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__random_next(cblc_field_t *dest)
|
||
{
|
||
int32_t retval_31;
|
||
#if HAVE_INITSTATE_R && HAVE_SRANDOM_R && HAVE_RANDOM_R
|
||
// The return value is between zero and not quite one
|
||
|
||
if( !buf )
|
||
{
|
||
// This is the very first time through
|
||
buf = (random_data *)malloc(sizeof(struct random_data));
|
||
buf->state = NULL;
|
||
state = static_cast<char *>(malloc(state_len));
|
||
struct cbl_timespec ts;
|
||
__gg__clock_gettime(&ts);
|
||
initstate_r( ts.tv_nsec, state, state_len, buf);
|
||
}
|
||
random_r(buf, &retval_31);
|
||
#else
|
||
retval_31 = random ();
|
||
#endif
|
||
// We are going to convert this to a value between zero and not quite one:
|
||
double retval = double(retval_31) / double(0x80000000UL);
|
||
__gg__double_to_target( dest,
|
||
retval,
|
||
truncation_e);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__reverse(cblc_field_t *dest,
|
||
const cblc_field_t *input,
|
||
size_t input_offset,
|
||
size_t input_size)
|
||
{
|
||
cbl_encoding_t from = input->encoding;
|
||
cbl_encoding_t to = dest->encoding;
|
||
|
||
size_t dest_length = dest->capacity;
|
||
size_t source_length = input_size;
|
||
size_t length = std::min(dest_length, source_length);
|
||
|
||
// Make a copy of the input
|
||
char *copy = static_cast<char *>(malloc(length));
|
||
massert(copy);
|
||
memcpy(copy, input->data+input_offset, length);
|
||
|
||
// Convert the input to the destination encoding
|
||
__gg__convert_encoding_length(copy,
|
||
length,
|
||
from,
|
||
to);
|
||
|
||
// Set the destination to all spaces
|
||
charmap_t *charmap = __gg__get_charmap(to);
|
||
memset(dest->data, charmap->mapped_character(ascii_space), dest_length);
|
||
for(size_t i=0; i<length; i++)
|
||
{
|
||
dest->data[i] = copy[source_length-1-i];
|
||
}
|
||
if( (dest->attr & intermediate_e) )
|
||
{
|
||
dest->capacity = std::min(dest_length, source_length);
|
||
}
|
||
|
||
free(copy);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__sign( cblc_field_t *dest,
|
||
const cblc_field_t *source,
|
||
size_t source_offset,
|
||
size_t source_size)
|
||
{
|
||
// FUNCTION SIGN
|
||
|
||
GCOB_FP128 value = __gg__float128_from_qualified_field(source,
|
||
source_offset,
|
||
source_size);
|
||
|
||
int retval;
|
||
if(value > 0)
|
||
{
|
||
retval = 1;
|
||
}
|
||
else if(value < 0)
|
||
{
|
||
retval = -1;
|
||
}
|
||
else
|
||
{
|
||
retval = 0;
|
||
}
|
||
__gg__int128_to_field(dest,
|
||
retval,
|
||
NO_RDIGITS,
|
||
truncation_e,
|
||
NULL);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__sin(cblc_field_t *dest,
|
||
const cblc_field_t *source,
|
||
size_t source_offset,
|
||
size_t source_size)
|
||
{
|
||
// FUNCTION SIN
|
||
|
||
GCOB_FP128 value = __gg__float128_from_qualified_field(source,
|
||
source_offset,
|
||
source_size);
|
||
|
||
value = FP128_FUNC(sin)(value);
|
||
|
||
__gg__float128_to_field(dest,
|
||
value,
|
||
truncation_e,
|
||
NULL);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__sqrt( cblc_field_t *dest,
|
||
const cblc_field_t *source,
|
||
size_t source_offset,
|
||
size_t source_size)
|
||
{
|
||
// FUNCTION SQRT
|
||
|
||
GCOB_FP128 value = __gg__float128_from_qualified_field(source,
|
||
source_offset,
|
||
source_size);
|
||
|
||
if( value < GCOB_FP128_LITERAL(0.0) )
|
||
{
|
||
exception_raise(ec_argument_function_e);
|
||
}
|
||
else
|
||
{
|
||
value = FP128_FUNC(sqrt)(value);
|
||
}
|
||
|
||
__gg__float128_to_field(dest,
|
||
value,
|
||
truncation_e,
|
||
NULL);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__standard_deviation( cblc_field_t *dest,
|
||
size_t ninputs)
|
||
{
|
||
// FUNCTION STANDARD-DEVIATION
|
||
GCOB_FP128 retval = variance(ninputs,
|
||
__gg__treeplet_1f,
|
||
__gg__treeplet_1o,
|
||
__gg__treeplet_1s,
|
||
__gg__fourplet_flags);
|
||
retval = FP128_FUNC(sqrt)(retval);
|
||
|
||
__gg__float128_to_field(dest,
|
||
retval,
|
||
truncation_e,
|
||
NULL);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__sum(cblc_field_t *dest,
|
||
size_t ninputs)
|
||
{
|
||
// FUNCTION SUM
|
||
size_t k_count;
|
||
GCOB_FP128 sum = kahan_summation(ninputs,
|
||
__gg__treeplet_1f,
|
||
__gg__treeplet_1o,
|
||
__gg__treeplet_1s,
|
||
__gg__fourplet_flags,
|
||
&k_count);
|
||
__gg__float128_to_field(dest,
|
||
sum,
|
||
truncation_e,
|
||
NULL);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__tan(cblc_field_t *dest,
|
||
const cblc_field_t *source,
|
||
size_t source_offset,
|
||
size_t source_size)
|
||
{
|
||
// FUNCTION TAN
|
||
|
||
GCOB_FP128 value = __gg__float128_from_qualified_field(source,
|
||
source_offset,
|
||
source_size);
|
||
value = FP128_FUNC(tan)(value);
|
||
__gg__float128_to_field(dest,
|
||
value,
|
||
truncation_e,
|
||
NULL);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__test_date_yyyymmdd( cblc_field_t *dest,
|
||
const cblc_field_t *source,
|
||
size_t source_offset,
|
||
size_t source_size)
|
||
{
|
||
int rdigits;
|
||
int yyyymmdd = (int)__gg__binary_value_from_qualified_field(&rdigits,
|
||
source,
|
||
source_offset,
|
||
source_size);
|
||
int retval;
|
||
int mmdd = yyyymmdd % 10000;
|
||
int mm = mmdd / 100;
|
||
if( yyyymmdd < 16010000 || yyyymmdd > 99999999 )
|
||
{
|
||
retval = 1;
|
||
}
|
||
else if( mm < 1 || mm > 12 )
|
||
{
|
||
retval = 2;
|
||
}
|
||
else
|
||
{
|
||
int dd = yyyymmdd % 100;
|
||
int yyyy = yyyymmdd / 10000;
|
||
int jy;
|
||
int jm;
|
||
int jd;
|
||
double JD;
|
||
|
||
// If there is something wrong with the number of days per month for a
|
||
// given year, the Julian Date conversion won't reverse properly.
|
||
// For example, January 32 will come back as February 1
|
||
JD = YMD_to_JD(yyyy, mm, dd);
|
||
JD_to_YMD(jy, jm, jd, JD);
|
||
if( jd == dd && jm == mm && jy == yyyy )
|
||
{
|
||
retval = 0;
|
||
}
|
||
else
|
||
{
|
||
retval = 3;
|
||
}
|
||
}
|
||
__gg__int128_to_field(dest,
|
||
retval,
|
||
NO_RDIGITS,
|
||
truncation_e,
|
||
NULL);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__test_day_yyyyddd( cblc_field_t *dest,
|
||
const cblc_field_t *source,
|
||
size_t source_offset,
|
||
size_t source_size)
|
||
{
|
||
int rdigits;
|
||
int yyyyddd = (int)__gg__binary_value_from_qualified_field(&rdigits,
|
||
source,
|
||
source_offset,
|
||
source_size);
|
||
int retval;
|
||
int ddd = yyyyddd % 1000;
|
||
int yyyy = yyyyddd / 1000;
|
||
int days_in_year;
|
||
|
||
days_in_year = is_leap_year(yyyy);
|
||
|
||
if( yyyyddd < 1601000 || yyyyddd > 9999999 )
|
||
{
|
||
retval = 1;
|
||
}
|
||
else if( ddd < 1 || ddd > days_in_year)
|
||
{
|
||
retval = 2;
|
||
}
|
||
else
|
||
{
|
||
retval = 0;
|
||
}
|
||
__gg__int128_to_field(dest,
|
||
retval,
|
||
NO_RDIGITS,
|
||
truncation_e,
|
||
NULL);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__upper_case( cblc_field_t *dest,
|
||
const cblc_field_t *input,
|
||
size_t input_offset,
|
||
size_t input_size)
|
||
{
|
||
cbl_encoding_t from = input->encoding;
|
||
cbl_encoding_t to = dest->encoding;
|
||
charmap_t *charmap_dest = __gg__get_charmap(to);
|
||
|
||
size_t dest_length = dest->capacity;
|
||
size_t source_length = input_size;
|
||
size_t length = std::min(dest_length, source_length);
|
||
memset( dest->data,
|
||
charmap_dest->mapped_character(ascii_space),
|
||
dest_length);
|
||
memcpy(dest->data, input->data+input_offset, length);
|
||
__gg__convert_encoding_length(PTRCAST(char, dest->data),
|
||
length,
|
||
from,
|
||
DEFAULT_CHARMAP_SOURCE);
|
||
std::transform(dest->data, dest->data + dest_length, dest->data,
|
||
[](unsigned char c) { return std::toupper(c); });
|
||
__gg__convert_encoding_length(PTRCAST(char, dest->data),
|
||
length,
|
||
DEFAULT_CHARMAP_SOURCE,
|
||
to);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__variance( cblc_field_t *dest,
|
||
size_t ncount)
|
||
{
|
||
// FUNCTION VARIANCE
|
||
GCOB_FP128 retval = variance(ncount,
|
||
__gg__treeplet_1f,
|
||
__gg__treeplet_1o,
|
||
__gg__treeplet_1s,
|
||
__gg__fourplet_flags);
|
||
__gg__float128_to_field(dest,
|
||
retval,
|
||
truncation_e,
|
||
NULL);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__when_compiled(cblc_field_t *dest, size_t tv_sec, long tv_nsec)
|
||
{
|
||
struct cbl_timespec tp = {};
|
||
tp.tv_sec = tv_sec;
|
||
tp.tv_nsec = tv_nsec;
|
||
char retval[DATE_STRING_BUFFER_SIZE];
|
||
timespec_to_string(retval, tp);
|
||
__gg__convert_encoding(PTRCAST(char, retval),
|
||
DEFAULT_CHARMAP_SOURCE,
|
||
dest->encoding);
|
||
string_to_dest(dest, retval);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__year_to_yyyy( cblc_field_t *dest,
|
||
const cblc_field_t *par1,
|
||
size_t par1_o,
|
||
size_t par1_s,
|
||
const cblc_field_t *par2,
|
||
size_t par2_o,
|
||
size_t par2_s,
|
||
const cblc_field_t *par3,
|
||
size_t par3_o,
|
||
size_t par3_s)
|
||
{
|
||
// FUNCTION YEAR_TO_YYYY
|
||
int rdigits;
|
||
int yy = (int)__gg__binary_value_from_qualified_field(&rdigits, par1, par1_o, par1_s);
|
||
int arg2 = (int)__gg__binary_value_from_qualified_field(&rdigits, par2, par2_o, par2_s );
|
||
int arg3 = (int)__gg__binary_value_from_qualified_field(&rdigits, par3, par3_o, par3_s);
|
||
|
||
int retval = year_to_yyyy(yy, arg2, arg3);
|
||
|
||
__gg__int128_to_field(dest,
|
||
retval,
|
||
NO_RDIGITS,
|
||
truncation_e,
|
||
NULL);
|
||
}
|
||
|
||
static
|
||
int
|
||
gets_int( int ndigits,
|
||
const char *p,
|
||
const char *pend,
|
||
charmap_t *charmap,
|
||
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;
|
||
|
||
int checked_0 = charmap->mapped_character(ascii_0);
|
||
int checked_9 = charmap->mapped_character(ascii_9);
|
||
|
||
memset(digits, 0xFF, ndigits * sizeof(int));
|
||
for(int i=1; i<=ndigits; i++)
|
||
{
|
||
if(p >= pend)
|
||
{
|
||
// We ran out of input too soon
|
||
retval = -i;
|
||
break;
|
||
}
|
||
int ch = *p++;
|
||
if( ch < checked_0 || ch > checked_9 )
|
||
{
|
||
// This isn't a digit zero through nine
|
||
retval = -i;
|
||
break;
|
||
}
|
||
retval *= 10;
|
||
retval += ch & 0xF;
|
||
digits[i-1] = ch & 0xF;
|
||
}
|
||
return retval;
|
||
}
|
||
|
||
static
|
||
int
|
||
gets_year(const char *p,
|
||
const char *pend,
|
||
charmap_t *charmap,
|
||
struct cobol_tm &ctm)
|
||
{
|
||
// Populates ctm.YYYY, ctm.days_in_year, and ctm.weeks_in_year, which are
|
||
// all determined by the YYYY value.
|
||
|
||
// Returns 0 if successful, and returns the ordinal position of the character
|
||
// where a four-character range with a year value of 1601 became impossible.
|
||
|
||
int retval = 0;
|
||
int digits[4];
|
||
int YYYY = gets_int(4, p, pend, charmap, digits);
|
||
|
||
if( digits[0] == -1 || digits[0] == 0 )
|
||
{
|
||
return 1;
|
||
}
|
||
if( digits[1] == -1 )
|
||
{
|
||
return 2;
|
||
}
|
||
if( digits[2] == -1 )
|
||
{
|
||
return 3;
|
||
}
|
||
if( digits[3] == -1 )
|
||
{
|
||
return 4;
|
||
}
|
||
|
||
if( YYYY >= 0 )
|
||
{
|
||
// The year has to be > 1000
|
||
if( YYYY < 1000 )
|
||
{
|
||
// We fail on the initial zero
|
||
retval = 1;
|
||
}
|
||
else if( YYYY < 1600 )
|
||
{
|
||
// We fail on the second digit
|
||
retval = 2;
|
||
}
|
||
else if( YYYY == 1600 )
|
||
{
|
||
// We fail on the fourth digit
|
||
retval = 4;
|
||
}
|
||
else
|
||
{
|
||
// The year is a good value
|
||
ctm.YYYY = YYYY;
|
||
ctm.days_in_year = is_leap_year(YYYY);
|
||
ctm.weeks_in_year = weeks_in_year(YYYY);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
retval = -YYYY;
|
||
}
|
||
return retval;
|
||
}
|
||
|
||
static
|
||
int
|
||
gets_month( const char *p,
|
||
const char *pend,
|
||
charmap_t *charmap,
|
||
struct cobol_tm &ctm)
|
||
{
|
||
// Populates ctm.MM
|
||
|
||
// 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, charmap, digits);
|
||
|
||
if( digits[0] == -1 || digits[0] > 1)
|
||
{
|
||
return 1;
|
||
}
|
||
if( digits[1] == -1 )
|
||
{
|
||
return 2;
|
||
}
|
||
if( MM >= 0 )
|
||
{
|
||
if( MM == 0 )
|
||
{
|
||
// We know the month was wrong at the second zero
|
||
retval = 2;
|
||
}
|
||
if( MM >= 20 )
|
||
{
|
||
// We know the month was wrong at the first digit
|
||
retval = 1;
|
||
}
|
||
else if( MM > 12 )
|
||
{
|
||
// We are betweem 13 and 19, so it was the second digit
|
||
retval = 2;
|
||
}
|
||
ctm.MM = MM;
|
||
}
|
||
else
|
||
{
|
||
retval = -MM;
|
||
}
|
||
return retval;
|
||
}
|
||
|
||
static
|
||
int
|
||
gets_day( const char *p,
|
||
const char *pend,
|
||
charmap_t *charmap,
|
||
struct cobol_tm &ctm)
|
||
{
|
||
// Populates ctm.DD, ctm.day_of_week, ctm.week_of_year, ctm.day_of_week
|
||
|
||
// 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, charmap, digits);
|
||
|
||
if( digits[0] == -1 || digits[0] > 3)
|
||
{
|
||
return 1;
|
||
}
|
||
if( digits[1] == -1 )
|
||
{
|
||
return 2;
|
||
}
|
||
if( DD >= 0 )
|
||
{
|
||
if( DD == 0)
|
||
{
|
||
// If zero, we know we failed at the second '0' in "00"
|
||
retval = 2;
|
||
}
|
||
else if( DD >= 40)
|
||
{
|
||
// 40 or more, then we knew there was trouble at the first digit
|
||
retval = 1;
|
||
}
|
||
else if(ctm.MM == 2 && DD >=30)
|
||
{
|
||
// It's February, so if we see 3x we know on the 3 that we are in
|
||
// error:
|
||
retval = 1;
|
||
}
|
||
else
|
||
{
|
||
static const int month_days[13] = {-1,31,28,31,30,31,30,31,31,30,31,30,31};
|
||
int days_in_month = month_days[ctm.MM];
|
||
if( ctm.MM == 2 && ctm.days_in_year == 366 )
|
||
{
|
||
days_in_month = 29;
|
||
}
|
||
|
||
if( DD > days_in_month )
|
||
{
|
||
retval = 2;
|
||
}
|
||
else
|
||
{
|
||
// We have a good YYYY-MM-DD
|
||
ctm.DD = DD;
|
||
double JD = YMD_to_JD(ctm.YYYY, ctm.MM, DD);
|
||
double JD_Jan0 = YMD_to_JD(ctm.YYYY, 1, 0);
|
||
ctm.day_of_year = (int)(JD - JD_Jan0);
|
||
ctm.day_of_week = JD_to_DOW(JD);
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
retval = -DD;
|
||
}
|
||
return retval;
|
||
}
|
||
|
||
static
|
||
int
|
||
gets_day_of_week( const char *p,
|
||
const char *pend,
|
||
charmap_t *charmap,
|
||
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 digits[1];
|
||
int day_of_week = gets_int(1, p, pend, charmap, digits);
|
||
if( day_of_week<0 || day_of_week >7)
|
||
{
|
||
// The single character at source is no good:
|
||
return 1;
|
||
}
|
||
ctm.day_of_week = day_of_week;
|
||
|
||
// It is a value 1 through 7. Convert it to 1 through 6:
|
||
day_of_week -= 1;
|
||
|
||
// Find the day-of-year using COBOL week logic:
|
||
double JD_Jan4 = YMD_to_JD(ctm.YYYY, 1, 4);
|
||
double JD_Jan0 = JD_Jan4 - 4;
|
||
int dow_Jan4 = JD_to_DOW(JD_Jan4);
|
||
double week_zero = JD_Jan4 - dow_Jan4;
|
||
double JD = week_zero + (ctm.week_of_year-1)*7 + day_of_week;
|
||
|
||
int day_of_year = (int)(JD - JD_Jan0);
|
||
|
||
// It's possible for the year/week/day_of_week to be
|
||
// before Jan 1. This is the case for 1900-12-31, as one example; that
|
||
// date gets converted to 1901-W01-01
|
||
if( day_of_year <= 0 )
|
||
{
|
||
double JD_prior_year = YMD_to_JD(ctm.YYYY-1, 1, 0);
|
||
int day_of_prior_year = (int)(JD-JD_prior_year);
|
||
int days_in_prior_year = is_leap_year(ctm.YYYY-1);
|
||
if( day_of_prior_year > days_in_prior_year )
|
||
{
|
||
return 1;
|
||
}
|
||
ctm.ZZZZ = ctm.YYYY + 1;
|
||
day_of_year = day_of_prior_year;
|
||
}
|
||
|
||
// Arriving here means we have a good JD, which means we can decompose it
|
||
JD_to_YMD(ctm.YYYY, ctm.MM, ctm.DD, JD);
|
||
ctm.day_of_year = day_of_year;
|
||
return 0;
|
||
}
|
||
|
||
static
|
||
int
|
||
gets_day_of_year( const char *p,
|
||
const char *pend,
|
||
charmap_t *charmap,
|
||
struct cobol_tm &ctm)
|
||
{
|
||
// This is a three-digit day-of-year, 001 through 365,366
|
||
int digits[3];
|
||
int DDD = gets_int(3, p, pend, charmap, 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;
|
||
}
|
||
|
||
if( DDD == 0 )
|
||
{
|
||
// We know we went wrong at the third '0' in "000"
|
||
return 3;
|
||
}
|
||
if( DDD >= 400 )
|
||
{
|
||
// We know we went wrong at the first digit
|
||
return 1;
|
||
}
|
||
if( DDD >= 370 )
|
||
{
|
||
// We know we went wrong at the second digit
|
||
return 2;
|
||
}
|
||
if( DDD > ctm.days_in_year )
|
||
{
|
||
// We know we went wrong at the third digit
|
||
return 3;
|
||
}
|
||
// We know that DDD is a good value between 1 and ctm.days_in_year
|
||
ctm.day_of_year = DDD;
|
||
|
||
double JD_Jan0 = YMD_to_JD(ctm.YYYY, 1, 0);
|
||
double JD = JD_Jan0 + DDD;
|
||
JD_to_YMD(ctm.YYYY,
|
||
ctm.MM,
|
||
ctm.DD,
|
||
JD);
|
||
ctm.day_of_week = JD_to_DOW(JD);
|
||
return 0;
|
||
}
|
||
|
||
static
|
||
int
|
||
gets_week(const char *p,
|
||
const char *pend,
|
||
charmap_t *charmap,
|
||
struct cobol_tm &ctm)
|
||
{
|
||
// This is a two-digit value, 01 through 52,53
|
||
int digits[2];
|
||
int ww = gets_int(2, p, pend, charmap, digits);
|
||
if( digits[0] == -1 || digits[0] > 5 )
|
||
{
|
||
return 1;
|
||
}
|
||
if( digits[1] == -1 )
|
||
{
|
||
return 2;
|
||
}
|
||
if( ww < 0 )
|
||
{
|
||
return -ww;
|
||
}
|
||
|
||
if( ww == 0 )
|
||
{
|
||
// We know we went wrong at the second '0' in "00"
|
||
return 2;
|
||
}
|
||
if( ww >= 60 )
|
||
{
|
||
// We know we went wrong at the first digit
|
||
return 1;
|
||
}
|
||
if( ww > ctm.weeks_in_year )
|
||
{
|
||
// We know we went wrong at the second digit
|
||
return 2;
|
||
}
|
||
// We know that ww is a good value for this year.
|
||
ctm.week_of_year = ww;
|
||
return 0;
|
||
}
|
||
|
||
static
|
||
int
|
||
gets_hours( const char *p,
|
||
const char *pend,
|
||
charmap_t *charmap,
|
||
struct cobol_tm &ctm,
|
||
bool in_offset)
|
||
{
|
||
// This is a two-digit value, 01 through 23
|
||
int digits[2];
|
||
int hh = gets_int(2, p, pend, charmap, digits);
|
||
|
||
if( digits[0] == -1 || digits[0] > 2 )
|
||
{
|
||
return 1;
|
||
}
|
||
if( digits[1] == -1 )
|
||
{
|
||
return 2;
|
||
}
|
||
|
||
if( hh < 0 )
|
||
{
|
||
return -hh;
|
||
}
|
||
|
||
if( hh >= 30 )
|
||
{
|
||
// We know we went wrong at the first digit
|
||
return 1;
|
||
}
|
||
|
||
if( hh >= 24 )
|
||
{
|
||
// We know we went wrong at the first digit
|
||
return 2;
|
||
}
|
||
|
||
if( in_offset )
|
||
{
|
||
ctm.tz_offset = 60*hh;
|
||
}
|
||
else
|
||
{
|
||
ctm.hh = hh;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
static
|
||
int
|
||
gets_minutes( const char *p,
|
||
const char *pend,
|
||
charmap_t *charmap,
|
||
struct cobol_tm &ctm,
|
||
bool in_offset)
|
||
{
|
||
// This is a two-digit value, 01 through 59
|
||
int digits[2];
|
||
int mm = gets_int(2, p, pend, charmap, digits);
|
||
if( digits[0] == -1 || digits[0] > 5 )
|
||
{
|
||
return 1;
|
||
}
|
||
if( digits[1] == -1 )
|
||
{
|
||
return 2;
|
||
}
|
||
|
||
if( mm < 0 )
|
||
{
|
||
return -mm;
|
||
}
|
||
|
||
if( mm >= 60 )
|
||
{
|
||
// We know we went wrong at the first digit
|
||
return 1;
|
||
}
|
||
|
||
if( in_offset )
|
||
{
|
||
ctm.tz_offset += mm;
|
||
}
|
||
else
|
||
{
|
||
ctm.mm = mm;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
static
|
||
int
|
||
gets_seconds( const char *p,
|
||
const char *pend,
|
||
charmap_t *charmap,
|
||
struct cobol_tm &ctm)
|
||
{
|
||
// This is a two-digit value, 01 through 59
|
||
int digits[2];
|
||
int ss = gets_int(2, p, pend, charmap, digits);
|
||
if( digits[0] == -1 || digits[0] > 5 )
|
||
{
|
||
return 1;
|
||
}
|
||
if( digits[1] == -1 )
|
||
{
|
||
return 2;
|
||
}
|
||
if( ss < 0 )
|
||
{
|
||
return -ss;
|
||
}
|
||
|
||
if( ss >= 60 )
|
||
{
|
||
// We know we went wrong at the first digit
|
||
return 1;
|
||
}
|
||
|
||
ctm.ss = ss;
|
||
return 0;
|
||
}
|
||
|
||
static
|
||
int
|
||
gets_nanoseconds( const char *f,
|
||
const char *f_end,
|
||
const char *p,
|
||
const char *pend,
|
||
struct cobol_tm &ctm,
|
||
charmap_t *charmap_format,
|
||
charmap_t *charmap_source)
|
||
{
|
||
// Because nanoseconds digits to the right of the decimal point can vary from
|
||
// one digit to our implementation-specific limit of nine characters, this
|
||
// routine is slightly different. If there is an error, that causes a
|
||
// positive return value. A negative return value contains the number of
|
||
// digits we processed,
|
||
|
||
int format_s = charmap_format->mapped_character(ascii_s);
|
||
int source_0 = charmap_source->mapped_character(ascii_0);
|
||
int source_9 = charmap_source->mapped_character(ascii_9);
|
||
|
||
int errpos = 0;
|
||
int ncount = 0;
|
||
int nanoseconds = 0;
|
||
|
||
const char *pinit = p;
|
||
while( f < f_end && *f == format_s && p < pend )
|
||
{
|
||
f += 1;
|
||
int ch = *p++;
|
||
errpos += 1;
|
||
|
||
if( ch < source_0 || ch > source_9 )
|
||
{
|
||
// Let our caller know we see a bad character
|
||
return errpos;
|
||
}
|
||
|
||
if(ncount < 9)
|
||
{
|
||
nanoseconds *= 10;
|
||
nanoseconds += ch & 0x0F;
|
||
}
|
||
ncount += 1;
|
||
}
|
||
while(ncount++ < 9)
|
||
{
|
||
nanoseconds *= 10;
|
||
}
|
||
ctm.nanoseconds = nanoseconds;
|
||
|
||
return -((int)(p - pinit));
|
||
}
|
||
|
||
static
|
||
int
|
||
fill_cobol_tm(cobol_tm &ctm,
|
||
const cblc_field_t *par1,
|
||
size_t par1_offset,
|
||
size_t par1_size,
|
||
const cblc_field_t *par2,
|
||
size_t par2_offset,
|
||
size_t par2_size)
|
||
{
|
||
// Establish the formatting string:
|
||
char *format = PTRCAST(char, (par1->data+par1_offset));
|
||
char *format_end = format + par1_size;
|
||
|
||
// Establish the string to be checked:
|
||
char *source = PTRCAST(char, (par2->data+par2_offset));
|
||
char *source_end = source + par2_size;
|
||
|
||
charmap_t *charmap_format = __gg__get_charmap(par1->encoding);
|
||
charmap_t *charmap_checked = __gg__get_charmap(par2->encoding);
|
||
int checked_space = charmap_checked->mapped_character(ascii_space);
|
||
int format_space = charmap_format->mapped_character(ascii_space);
|
||
int format_T = charmap_format->mapped_character(ascii_T );
|
||
int format_colon = charmap_format->mapped_character(ascii_colon );
|
||
int format_plus = charmap_format->mapped_character(ascii_plus );
|
||
int format_minus = charmap_format->mapped_character(ascii_minus );
|
||
int format_W = charmap_format->mapped_character(ascii_W );
|
||
int format_Z = charmap_format->mapped_character(ascii_Z );
|
||
int format_z = charmap_format->mapped_character(ascii_z );
|
||
int format_s = charmap_format->mapped_character(ascii_s );
|
||
int format_m = charmap_format->mapped_character(ascii_m );
|
||
int format_h = charmap_format->mapped_character(ascii_h );
|
||
int format_w = charmap_format->mapped_character(ascii_w );
|
||
int format_Y = charmap_format->mapped_character(ascii_Y );
|
||
int format_M = charmap_format->mapped_character(ascii_M );
|
||
int format_D = charmap_format->mapped_character(ascii_D );
|
||
int format_zero = charmap_format->mapped_character(ascii_zero );
|
||
|
||
// Let's eliminate trailing spaces...
|
||
trim_trailing_spaces(format, format_end, format_space);
|
||
trim_trailing_spaces(source, source_end, checked_space);
|
||
|
||
bool in_offset = false;
|
||
bool in_nanoseconds = false;
|
||
|
||
char decimal_point = __gg__get_decimal_point();
|
||
|
||
// We keep constant track of the current error location.
|
||
int retval = 1;
|
||
int errpos;
|
||
|
||
// At this juncture, we expect both the format and the source to have valid
|
||
// data. If they don't, it's because the source is too short, and thus
|
||
// retval is the failure point.
|
||
int bump;
|
||
while( format < format_end && source < source_end )
|
||
{
|
||
char ch = *format;
|
||
|
||
if( ch == format_T
|
||
|| ch == format_colon
|
||
|| ch == format_minus
|
||
|| ch == format_W)
|
||
{
|
||
// These are just formatting characters. They need to be duplicated,
|
||
// but are otherwise ignored.
|
||
if( *source != ch )
|
||
{
|
||
break;
|
||
}
|
||
bump = 1;
|
||
goto proceed;
|
||
}
|
||
|
||
if( ch == format_plus )
|
||
{
|
||
// This flags a following hhmm offset. It needs to match a '+' or '-'
|
||
if( *source != format_plus
|
||
&& *source != format_minus
|
||
&& *source != format_zero)
|
||
{
|
||
break;
|
||
}
|
||
if( *source == format_zero )
|
||
{
|
||
// The next four characters have to be zeroes
|
||
if( source[1] != format_zero )
|
||
{
|
||
retval += 1;
|
||
break;
|
||
}
|
||
if( source[2] != format_zero )
|
||
{
|
||
retval += 2;
|
||
break;
|
||
}
|
||
if( source[3] != format_zero )
|
||
{
|
||
retval += 3;
|
||
break;
|
||
}
|
||
if( source[4] != format_zero )
|
||
{
|
||
retval += 4;
|
||
break;
|
||
}
|
||
}
|
||
|
||
in_offset = true;
|
||
bump = 1;
|
||
goto proceed;
|
||
}
|
||
|
||
if( ch == decimal_point )
|
||
{
|
||
// This indicates we are starting to process fractional seconds
|
||
if( *source != decimal_point )
|
||
{
|
||
break;
|
||
}
|
||
in_nanoseconds = true;
|
||
bump = 1;
|
||
goto proceed;
|
||
}
|
||
|
||
if( ch == format_Y )
|
||
{
|
||
errpos = gets_year(source, source_end, charmap_checked, ctm);
|
||
if( errpos > 0 )
|
||
{
|
||
retval += errpos - 1;
|
||
break;
|
||
}
|
||
bump = 4;
|
||
goto proceed;
|
||
}
|
||
|
||
if( ch == format_M )
|
||
{
|
||
errpos = gets_month(source, source_end, charmap_checked, ctm);
|
||
if( errpos > 0 )
|
||
{
|
||
retval += errpos - 1;
|
||
break;
|
||
}
|
||
bump = 2;
|
||
goto proceed;
|
||
}
|
||
|
||
if( ch == format_D )
|
||
{
|
||
// We have three possibilities: DDD, DD, and D
|
||
if( format[1] != format_D )
|
||
{
|
||
// A singleton 'D' is a day-of-week
|
||
errpos = gets_day_of_week(source, source_end, charmap_checked, ctm);
|
||
if( errpos > 0)
|
||
{
|
||
retval += errpos - 1;
|
||
break;
|
||
}
|
||
bump = 1;
|
||
}
|
||
else if( format[2] != format_D )
|
||
{
|
||
// This is DD, for day-of-month
|
||
errpos = gets_day(source, source_end, charmap_checked, ctm);
|
||
if( errpos > 0)
|
||
{
|
||
retval += errpos - 1;
|
||
break;
|
||
}
|
||
bump = 2;
|
||
}
|
||
else
|
||
{
|
||
// Arriving here means that it is DDD, for day-of-year
|
||
// This is DD, for day-of-month
|
||
errpos = gets_day_of_year(source, source_end, charmap_checked, ctm);
|
||
if( errpos > 0)
|
||
{
|
||
retval += errpos - 1;
|
||
break;
|
||
}
|
||
bump = 3;
|
||
}
|
||
goto proceed;
|
||
}
|
||
|
||
if( ch == format_w )
|
||
{
|
||
errpos = gets_week(source, source_end, charmap_checked, ctm);
|
||
if( errpos > 0 )
|
||
{
|
||
retval += errpos - 1;
|
||
break;
|
||
}
|
||
bump = 2;
|
||
goto proceed;
|
||
}
|
||
|
||
if( ch == format_h )
|
||
{
|
||
errpos = gets_hours(source, source_end, charmap_checked, ctm, in_offset);
|
||
if( errpos > 0 )
|
||
{
|
||
retval += errpos - 1;
|
||
break;
|
||
}
|
||
bump = 2;
|
||
goto proceed;
|
||
}
|
||
|
||
if( ch == format_m )
|
||
{
|
||
errpos = gets_minutes(source, source_end, charmap_checked, ctm, in_offset);
|
||
if( errpos > 0 )
|
||
{
|
||
retval += errpos - 1;
|
||
break;
|
||
}
|
||
bump = 2;
|
||
goto proceed;
|
||
}
|
||
|
||
if( ch == format_s && !in_nanoseconds )
|
||
{
|
||
errpos = gets_seconds(source, source_end, charmap_checked, ctm);
|
||
if( errpos > 0 )
|
||
{
|
||
retval += errpos - 1;
|
||
break;
|
||
}
|
||
bump = 2;
|
||
goto proceed;
|
||
}
|
||
|
||
if( ch == format_s && in_nanoseconds )
|
||
{
|
||
// Peel off digits to the right of the decimal point one at a time
|
||
errpos = gets_nanoseconds(format,
|
||
format_end,
|
||
source,
|
||
source_end,
|
||
ctm,
|
||
charmap_format,
|
||
charmap_checked);
|
||
if( errpos > 0 )
|
||
{
|
||
retval += errpos - 1;
|
||
break;
|
||
}
|
||
bump = -errpos;
|
||
goto proceed;
|
||
}
|
||
|
||
if( ch == format_Z || ch == format_z )
|
||
{
|
||
// This has to be the end of the road
|
||
if( std::toupper((unsigned char)source[0]) != 'Z' )
|
||
{
|
||
retval += 0;
|
||
break;
|
||
}
|
||
|
||
convert_to_zulu(ctm);
|
||
bump = 1;
|
||
goto proceed;
|
||
}
|
||
|
||
assert(false);
|
||
|
||
proceed:
|
||
retval += bump;
|
||
format += bump;
|
||
source += bump;
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__test_formatted_datetime(cblc_field_t *dest,
|
||
const cblc_field_t *arg1,
|
||
size_t arg1_offset,
|
||
size_t arg1_size,
|
||
const cblc_field_t *arg2,
|
||
size_t arg2_offset,
|
||
size_t arg2_size)
|
||
|
||
{
|
||
struct cobol_tm ctm = {};
|
||
|
||
int retval = fill_cobol_tm( ctm,
|
||
arg1, arg1_offset, arg1_size,
|
||
arg2, arg2_offset, arg2_size);
|
||
__gg__int128_to_field(dest,
|
||
retval,
|
||
NO_RDIGITS,
|
||
truncation_e,
|
||
NULL);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__integer_of_formatted_date(cblc_field_t *dest,
|
||
const cblc_field_t *arg1,
|
||
size_t arg1_offset,
|
||
size_t arg1_size,
|
||
const cblc_field_t *arg2,
|
||
size_t arg2_offset,
|
||
size_t arg2_size)
|
||
{
|
||
struct cobol_tm ctm = {};
|
||
|
||
int retval = fill_cobol_tm( ctm,
|
||
arg1, arg1_offset, arg1_size,
|
||
arg2, arg2_offset, arg2_size);
|
||
if(retval)
|
||
{
|
||
retval = 0; // Indicates there was a problem with the input data
|
||
}
|
||
else
|
||
{
|
||
double JD = YMD_to_JD(ctm.YYYY, ctm.MM, ctm.DD);
|
||
|
||
// Offset result so that 1601-01-01 comes back as the first day of
|
||
// the Gregorian Calendar
|
||
retval = (int)(JD - JD_OF_1601_01_02);
|
||
}
|
||
|
||
__gg__int128_to_field(dest,
|
||
retval,
|
||
NO_RDIGITS,
|
||
truncation_e,
|
||
NULL);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__seconds_from_formatted_time(cblc_field_t *dest,
|
||
const cblc_field_t *arg1,
|
||
size_t arg1_offset,
|
||
size_t arg1_size,
|
||
const cblc_field_t *arg2,
|
||
size_t arg2_offset,
|
||
size_t arg2_size)
|
||
{
|
||
struct cobol_tm ctm = {};
|
||
|
||
double retval = fill_cobol_tm( ctm,
|
||
arg1, arg1_offset, arg1_size,
|
||
arg2, arg2_offset, arg2_size);
|
||
if(retval > 0)
|
||
{
|
||
retval = 0; // Indicates there was a problem with the input data
|
||
}
|
||
else
|
||
{
|
||
retval = (double)(ctm.hh * 3600 + ctm.mm * 60 + ctm.ss) + ctm.nanoseconds/1000000000.;
|
||
}
|
||
__gg__double_to_target( dest,
|
||
retval,
|
||
truncation_e);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__hex_of(cblc_field_t *dest,
|
||
const cblc_field_t *field,
|
||
size_t field_offset,
|
||
size_t field_size)
|
||
{
|
||
charmap_t *charmap = __gg__get_charmap(dest->encoding);
|
||
static const char hex[17] = "0123456789ABCDEF";
|
||
size_t bytes = field_size;
|
||
__gg__adjust_dest_size(dest, 2*bytes);
|
||
for(size_t i=0; i<bytes; i++)
|
||
{
|
||
unsigned char byte = (field->data+field_offset)[i];
|
||
dest->data[2*i ] = charmap->mapped_character(hex[byte>>4]);
|
||
dest->data[2*i+1] = charmap->mapped_character(hex[byte&0xF]);
|
||
}
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__highest_algebraic(cblc_field_t *dest,
|
||
const cblc_field_t *var,
|
||
size_t,
|
||
size_t)
|
||
{
|
||
__int128 result = 0;
|
||
__int128 result_rdigits = 0;
|
||
|
||
if( var->attr & scaled_e )
|
||
{
|
||
result = __gg__power_of_ten(var->digits) - 1;
|
||
if( var->rdigits<0 )
|
||
{
|
||
result *= __gg__power_of_ten(-var->rdigits);
|
||
}
|
||
else
|
||
{
|
||
result_rdigits = var->digits + var->rdigits;
|
||
}
|
||
}
|
||
else if( var->digits == 0 )
|
||
{
|
||
result = (1<<(var->capacity*8)) -1 ;
|
||
if( var->attr & signable_e )
|
||
{
|
||
result >>=1 ;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
result_rdigits = var->rdigits;
|
||
result = __gg__power_of_ten(var->digits) - 1;
|
||
}
|
||
__gg__int128_to_field(dest,
|
||
result,
|
||
result_rdigits,
|
||
truncation_e,
|
||
NULL);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__lowest_algebraic( cblc_field_t *dest,
|
||
const cblc_field_t *var,
|
||
size_t,
|
||
size_t)
|
||
{
|
||
__int128 result = 0;
|
||
__int128 result_rdigits = 0;
|
||
|
||
if( var->attr & scaled_e )
|
||
{
|
||
result = __gg__power_of_ten(var->digits) - 1;
|
||
if( var->rdigits<0 )
|
||
{
|
||
result *= __gg__power_of_ten(-var->rdigits);
|
||
}
|
||
else
|
||
{
|
||
result_rdigits = var->digits + var->rdigits;
|
||
}
|
||
if( var->attr & signable_e )
|
||
{
|
||
result = -result;
|
||
}
|
||
else
|
||
{
|
||
result = 0;
|
||
}
|
||
}
|
||
else if( var->digits == 0 )
|
||
{
|
||
result = (1<<(var->capacity*8)) -1 ;
|
||
if( var->attr & signable_e )
|
||
{
|
||
result >>=1 ;
|
||
result += 1;
|
||
result = -result;
|
||
}
|
||
else
|
||
{
|
||
result = 0;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
result_rdigits = var->rdigits;
|
||
result = __gg__power_of_ten(var->digits) - 1;
|
||
if( var->attr & signable_e )
|
||
{
|
||
result = -result;
|
||
}
|
||
else
|
||
{
|
||
result = 0;
|
||
}
|
||
}
|
||
__gg__int128_to_field(dest,
|
||
result,
|
||
result_rdigits,
|
||
truncation_e,
|
||
NULL);
|
||
}
|
||
|
||
static int
|
||
floating_format_tester( char const * const f,
|
||
char const * const f_end,
|
||
cbl_encoding_t encoding)
|
||
{
|
||
charmap_t *charmap = __gg__get_charmap(encoding);
|
||
int mapped_space = charmap->mapped_character(ascii_space);
|
||
int mapped_plus = charmap->mapped_character(ascii_plus);
|
||
int mapped_minus = charmap->mapped_character(ascii_minus);
|
||
int mapped_0 = charmap->mapped_character(ascii_0);
|
||
int mapped_9 = charmap->mapped_character(ascii_9);
|
||
int mapped_E = charmap->mapped_character(ascii_E);
|
||
int mapped_e = charmap->mapped_character(ascii_e);
|
||
int decimal_point = charmap->mapped_character(__gg__get_decimal_point());
|
||
|
||
int retval = -1;
|
||
|
||
enum
|
||
{
|
||
SPACE1,
|
||
SPACE2,
|
||
DIGITS1,
|
||
DIGITS2,
|
||
SPACE3,
|
||
SPACE4,
|
||
SPACE5,
|
||
DIGITS3,
|
||
SPACE6,
|
||
} state = SPACE1;
|
||
ssize_t index = 0;
|
||
while(index < f_end - f)
|
||
{
|
||
char ch = f[index];
|
||
switch(state)
|
||
{
|
||
case SPACE1:
|
||
if( ch == mapped_space )
|
||
{
|
||
// Just keep looking
|
||
break;
|
||
}
|
||
if( ch == mapped_minus
|
||
|| ch == mapped_plus)
|
||
{
|
||
state = SPACE2;
|
||
break;
|
||
}
|
||
if( ch >= mapped_0 && ch <= mapped_9 )
|
||
{
|
||
state = DIGITS1;
|
||
break;
|
||
}
|
||
if( decimal_point )
|
||
{
|
||
state = DIGITS2;
|
||
break;
|
||
}
|
||
// Disallowed character
|
||
retval = index;
|
||
break;
|
||
|
||
case SPACE2:
|
||
if( ch == mapped_space )
|
||
{
|
||
break;
|
||
}
|
||
if( ch >= mapped_0 && ch <= mapped_9 )
|
||
{
|
||
state = DIGITS1;
|
||
break;
|
||
}
|
||
if( ch == decimal_point )
|
||
{
|
||
state = DIGITS2;
|
||
break;
|
||
}
|
||
retval = index;
|
||
break;
|
||
|
||
case DIGITS1:
|
||
if( ch >= mapped_0 && ch <= mapped_9 )
|
||
{
|
||
break;
|
||
}
|
||
if( ch == decimal_point )
|
||
{
|
||
state = DIGITS2;
|
||
break;
|
||
}
|
||
if( ch == mapped_space )
|
||
{
|
||
state = SPACE3;
|
||
break;
|
||
}
|
||
retval = index;
|
||
break;
|
||
|
||
case DIGITS2:
|
||
if( ch >= mapped_0 && ch <= mapped_9 )
|
||
{
|
||
break;
|
||
}
|
||
if( ch == mapped_space )
|
||
{
|
||
state = SPACE3;
|
||
break;
|
||
}
|
||
if( ch == mapped_E || ch == mapped_e )
|
||
{
|
||
state = SPACE4;
|
||
break;
|
||
}
|
||
retval = index;
|
||
break;
|
||
|
||
case SPACE3:
|
||
if( ch == mapped_space )
|
||
{
|
||
break;
|
||
}
|
||
if( ch >= mapped_0 && ch <= mapped_9 )
|
||
{
|
||
retval = index;
|
||
break;
|
||
}
|
||
if( ch == mapped_E || ch == mapped_e )
|
||
{
|
||
state = SPACE4;
|
||
break;
|
||
}
|
||
retval = index;
|
||
break;
|
||
|
||
case SPACE4:
|
||
if( ch == mapped_space )
|
||
{
|
||
break;
|
||
}
|
||
if( ch == mapped_minus || ch == mapped_plus )
|
||
{
|
||
state = SPACE5;
|
||
break;
|
||
}
|
||
if( ch >= mapped_0 && ch <= mapped_9 )
|
||
{
|
||
state = DIGITS3;
|
||
break;
|
||
}
|
||
retval = index;
|
||
break;
|
||
|
||
case SPACE5:
|
||
if( ch == mapped_space )
|
||
{
|
||
break;
|
||
}
|
||
if( ch >= mapped_0 && ch <= mapped_9 )
|
||
{
|
||
state = DIGITS3;
|
||
break;
|
||
}
|
||
retval = index;
|
||
break;
|
||
|
||
case DIGITS3:
|
||
if( ch >= mapped_0 && ch <= mapped_9 )
|
||
{
|
||
break;
|
||
}
|
||
if( ch == mapped_space )
|
||
{
|
||
state = SPACE6;
|
||
break;
|
||
}
|
||
retval = index;
|
||
break;
|
||
|
||
case SPACE6:
|
||
if( ch == mapped_space )
|
||
{
|
||
break;
|
||
}
|
||
retval = index;
|
||
break;
|
||
}
|
||
|
||
if( retval > -1 )
|
||
{
|
||
break;
|
||
}
|
||
index += 1;
|
||
}
|
||
|
||
retval += 1;
|
||
return retval;
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__numval_f( cblc_field_t *dest,
|
||
const cblc_field_t *source,
|
||
size_t source_offset,
|
||
size_t source_size)
|
||
{
|
||
GCOB_FP128 value = 0;
|
||
const char *data = PTRCAST(char, (source->data + source_offset));
|
||
const char *data_end = data + source_size;
|
||
charmap_t *charmap = __gg__get_charmap(source->encoding);
|
||
int mapped_space = charmap->mapped_character(ascii_space);
|
||
|
||
int error = floating_format_tester( data,
|
||
data_end,
|
||
source->encoding);
|
||
|
||
if( error || source_size >= 256 )
|
||
{
|
||
exception_raise(ec_argument_function_e);
|
||
}
|
||
else
|
||
{
|
||
// Get rid of any spaces in the string
|
||
char ach[256];
|
||
char *p = ach;
|
||
while( data < data_end )
|
||
{
|
||
char ch = *data++;
|
||
if( ch != mapped_space )
|
||
{
|
||
*p++ = ch;
|
||
}
|
||
}
|
||
*p++ = '\0';
|
||
value = strtofp128(ach, NULL);
|
||
}
|
||
__gg__float128_to_field(dest,
|
||
value,
|
||
truncation_e,
|
||
NULL);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__test_numval_f(cblc_field_t *dest,
|
||
const cblc_field_t *source,
|
||
size_t source_offset,
|
||
size_t source_size)
|
||
{
|
||
const char *data = PTRCAST(char, (source->data + source_offset));
|
||
const char *data_end = data + source_size;
|
||
|
||
int error = floating_format_tester( data,
|
||
data_end,
|
||
source->encoding);
|
||
|
||
__gg__int128_to_field(dest,
|
||
error,
|
||
NO_RDIGITS,
|
||
truncation_e,
|
||
NULL);
|
||
}
|
||
|
||
static bool
|
||
ismatch(const char *a1, const char *a2, const char *b1, const char *b2)
|
||
{
|
||
bool retval = true;
|
||
while( a1 < a2 && b1 < b2 )
|
||
{
|
||
if( *a1++ != *b1++ )
|
||
{
|
||
retval = false;
|
||
}
|
||
}
|
||
return retval;
|
||
}
|
||
|
||
static bool
|
||
iscasematch(const char *a1, const char *a2, const char *b1, const char *b2)
|
||
{
|
||
bool retval = true;
|
||
while( a1 < a2 && b1 < b2 )
|
||
{
|
||
if( std::tolower((unsigned char)*a1++) != std::tolower((unsigned char)*b1++) )
|
||
{
|
||
retval = false;
|
||
}
|
||
}
|
||
return retval;
|
||
}
|
||
|
||
static
|
||
const char *
|
||
strstr( const char *haystack,
|
||
const char *haystack_e,
|
||
const char *needle,
|
||
const char *needle_e)
|
||
{
|
||
const char *retval = NULL;
|
||
const char *pend = haystack_e - (needle_e - needle);
|
||
while( haystack <= pend )
|
||
{
|
||
if(ismatch(haystack, haystack_e, needle, needle_e))
|
||
{
|
||
retval = haystack;
|
||
break;
|
||
}
|
||
haystack += 1;
|
||
}
|
||
return retval;
|
||
}
|
||
|
||
static
|
||
const char *
|
||
strcasestr( const char *haystack,
|
||
const char *haystack_e,
|
||
const char *needle,
|
||
const char *needle_e)
|
||
{
|
||
const char *retval = NULL;
|
||
const char *pend = haystack_e - (needle_e - needle);
|
||
while( haystack <= pend )
|
||
{
|
||
if(iscasematch(haystack, haystack_e, needle, needle_e))
|
||
{
|
||
retval = haystack;
|
||
break;
|
||
}
|
||
haystack += 1;
|
||
}
|
||
return retval;
|
||
}
|
||
|
||
static
|
||
const char *
|
||
strlaststr( const char *haystack,
|
||
const char *haystack_e,
|
||
const char *needle,
|
||
const char *needle_e)
|
||
{
|
||
const char *retval = NULL;
|
||
const char *pend = haystack_e - (needle_e - needle);
|
||
while( haystack <= pend )
|
||
{
|
||
if(ismatch(haystack, haystack_e, needle, needle_e))
|
||
{
|
||
retval = haystack;
|
||
}
|
||
haystack += 1;
|
||
}
|
||
return retval;
|
||
}
|
||
|
||
static
|
||
const char *
|
||
strcaselaststr( const char *haystack,
|
||
const char *haystack_e,
|
||
const char *needle,
|
||
const char *needle_e)
|
||
{
|
||
const char *retval = NULL;
|
||
const char *pend = haystack_e - (needle_e - needle);
|
||
while( haystack <= pend )
|
||
{
|
||
if(iscasematch(haystack, haystack_e, needle, needle_e))
|
||
{
|
||
retval = haystack;
|
||
}
|
||
haystack += 1;
|
||
}
|
||
return retval;
|
||
}
|
||
|
||
|
||
extern "C"
|
||
void
|
||
__gg__substitute( cblc_field_t *dest,
|
||
const cblc_field_t *arg1_f,
|
||
size_t arg1_o,
|
||
size_t arg1_s,
|
||
size_t N,
|
||
const uint8_t *control)
|
||
{
|
||
// arg2 is the Group 1 triplet.
|
||
// arg3 is the Group 2 triplet
|
||
cblc_field_t **arg2_f = __gg__treeplet_1f;
|
||
size_t *arg2_o = __gg__treeplet_1o;
|
||
size_t *arg2_s = __gg__treeplet_1s;
|
||
cblc_field_t **arg3_f = __gg__treeplet_2f;
|
||
const size_t *arg3_o = __gg__treeplet_2o;
|
||
const size_t *arg3_s = __gg__treeplet_2s;
|
||
|
||
ssize_t retval_size;
|
||
retval_size = 256;
|
||
char *retval = static_cast<char *>(malloc(retval_size));
|
||
massert(retval);
|
||
*retval = '\0';
|
||
|
||
const char *haystack = PTRCAST(char, (arg1_f->data + arg1_o));
|
||
const char *haystack_e = haystack + arg1_s;
|
||
|
||
ssize_t outdex = 0;
|
||
|
||
const char **pflasts = static_cast<const char **>(malloc(N * sizeof(char *)));
|
||
massert(pflasts);
|
||
|
||
if( arg1_s == 0 )
|
||
{
|
||
exception_raise(ec_argument_function_e);
|
||
goto bugout;
|
||
}
|
||
|
||
for( size_t i=0; i<N; i++ )
|
||
{
|
||
if( arg2_s[i] == 0 )
|
||
{
|
||
exception_raise(ec_argument_function_e);
|
||
goto bugout;
|
||
}
|
||
if( control[i] & substitute_anycase_e )
|
||
{
|
||
if( control[i] & substitute_first_e )
|
||
{
|
||
pflasts[i] = strcasestr(haystack,
|
||
haystack_e,
|
||
PTRCAST(char, (arg2_f[i]->data+arg2_o[i])),
|
||
PTRCAST(char, (arg2_f[i]->data+arg2_o[i])) + arg2_s[i]);
|
||
}
|
||
else if( control[i] & substitute_last_e)
|
||
{
|
||
pflasts[i] = strcaselaststr(haystack,
|
||
haystack_e,
|
||
PTRCAST(char, (arg2_f[i]->data+arg2_o[i])),
|
||
PTRCAST(char, (arg2_f[i]->data+arg2_o[i])) + arg2_s[i]);
|
||
}
|
||
else
|
||
{
|
||
pflasts[i] = NULL;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if( control[i] & substitute_first_e )
|
||
{
|
||
pflasts[i] = strstr(haystack,
|
||
haystack_e,
|
||
PTRCAST(char, (arg2_f[i]->data+arg2_o[i])),
|
||
PTRCAST(char, (arg2_f[i]->data+arg2_o[i])) + arg2_s[i]);
|
||
}
|
||
else if( control[i] & substitute_last_e)
|
||
{
|
||
pflasts[i] = strlaststr(haystack,
|
||
haystack_e,
|
||
PTRCAST(char, (arg2_f[i]->data+arg2_o[i])),
|
||
PTRCAST(char, (arg2_f[i]->data+arg2_o[i])) + arg2_s[i]);
|
||
}
|
||
else
|
||
{
|
||
pflasts[i] = NULL;
|
||
}
|
||
}
|
||
}
|
||
|
||
while( haystack < haystack_e )
|
||
{
|
||
bool did_something = false;
|
||
for( size_t i=0; i<N; i++ )
|
||
{
|
||
// Let's make sure that there is enough room in the case that we add this
|
||
// arg
|
||
while( outdex - (ssize_t)arg2_s[i] + (ssize_t)arg3_s[i]
|
||
> retval_size )
|
||
{
|
||
retval_size *= 2;
|
||
retval = static_cast<char *>(realloc(retval, retval_size));
|
||
massert(retval);
|
||
}
|
||
|
||
// We checked earlier for FIRST/LAST matches
|
||
bool matched = pflasts[i] == haystack;
|
||
if( !matched )
|
||
{
|
||
// It didn't match. But if it was flagged as FIRST or LAST, we need
|
||
// to skip it
|
||
|
||
if( control[i] & (substitute_first_e|substitute_last_e) )
|
||
{
|
||
continue;
|
||
}
|
||
|
||
const char *needle = PTRCAST(char, arg2_f[i]->data+arg2_o[i]);
|
||
const char *needle_e = PTRCAST(char, arg2_f[i]->data+arg2_o[i]) + arg2_s[i];
|
||
matched = (control[i] & substitute_anycase_e) && iscasematch(
|
||
haystack,
|
||
haystack_e,
|
||
needle,
|
||
needle_e);
|
||
if( !matched )
|
||
{
|
||
matched = !(control[i] & substitute_anycase_e) && ismatch(haystack,
|
||
haystack_e,
|
||
needle,
|
||
needle_e) ;
|
||
}
|
||
}
|
||
if( matched )
|
||
{
|
||
haystack += arg2_s[i];
|
||
memcpy(retval + outdex, arg3_f[i]->data + arg3_o[i], arg3_s[i]);
|
||
outdex += arg3_s[i];
|
||
did_something = true;
|
||
break;
|
||
}
|
||
}
|
||
if( !did_something )
|
||
{
|
||
while( outdex + 1 > retval_size )
|
||
{
|
||
retval_size *= 2;
|
||
retval = static_cast<char *>(realloc(retval, retval_size));
|
||
massert(retval);
|
||
}
|
||
retval[outdex++] = *haystack++;
|
||
}
|
||
}
|
||
|
||
bugout:
|
||
__gg__adjust_dest_size(dest, outdex);
|
||
memcpy(dest->data, retval, outdex);
|
||
|
||
free(pflasts);
|
||
free(retval);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__locale_compare( cblc_field_t *dest,
|
||
const cblc_field_t *arg1,
|
||
size_t arg1_o,
|
||
size_t arg1_s,
|
||
const cblc_field_t *arg2,
|
||
size_t arg2_o,
|
||
size_t arg2_s,
|
||
const cblc_field_t *arg_locale,
|
||
size_t /*arg_locale_o*/,
|
||
size_t /*arg_locale_s*/
|
||
)
|
||
{
|
||
char achretval[2] = "?";
|
||
|
||
if( arg_locale )
|
||
{
|
||
// We don't yet know what to do with a locale
|
||
exception_raise(ec_locale_missing_e);
|
||
}
|
||
else
|
||
{
|
||
// Default locale
|
||
achretval[0] = '=';
|
||
size_t length = std::min(arg1_s, arg2_s);
|
||
for(size_t i=0; i<length; i++ )
|
||
{
|
||
if( (arg1->data+arg1_o)[i] < (arg2->data+arg2_o)[i] )
|
||
{
|
||
achretval[0] = '<';
|
||
break;
|
||
}
|
||
if( (arg1->data+arg1_o)[i] > (arg2->data+arg2_o)[i] )
|
||
{
|
||
achretval[0] = '>';
|
||
break;
|
||
}
|
||
}
|
||
if( achretval[0] == '=' )
|
||
{
|
||
if( arg1_s < arg2_s )
|
||
{
|
||
achretval[0] = '<';
|
||
}
|
||
else if( arg1_s > arg2_s )
|
||
{
|
||
achretval[0] = '>';
|
||
}
|
||
}
|
||
}
|
||
|
||
__gg__adjust_dest_size(dest, 1);
|
||
dest->data[0] = *achretval;
|
||
__gg__convert_encoding(PTRCAST(char, dest->data),
|
||
DEFAULT_CHARMAP_SOURCE,
|
||
dest->encoding);
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__locale_date(cblc_field_t *dest,
|
||
const cblc_field_t *arg1,
|
||
size_t arg1_o,
|
||
size_t /*arg1_s*/,
|
||
const cblc_field_t *arg_locale,
|
||
size_t /*arg_locale_o*/,
|
||
size_t /*arg_locale_s*/)
|
||
{
|
||
char ach[256] = " ";
|
||
|
||
if( arg_locale )
|
||
{
|
||
// We don't yet know what to do with a locale
|
||
exception_raise(ec_locale_missing_e);
|
||
}
|
||
else
|
||
{
|
||
// Default locale
|
||
tm tm;
|
||
memcpy(ach, arg1->data+arg1_o, 8);
|
||
ach[8] = '\0';
|
||
long ymd = atoi(ach);
|
||
tm.tm_year = ymd/10000 - 1900;
|
||
tm.tm_mon = ymd/100 % 100;
|
||
tm.tm_mday = ymd % 100;
|
||
strcpy(ach, nl_langinfo(D_FMT));
|
||
strftime(ach, sizeof(ach), nl_langinfo(D_FMT), &tm);
|
||
}
|
||
|
||
__gg__adjust_dest_size(dest, strlen(ach));
|
||
__gg__convert_encoding(PTRCAST(char, dest->data),
|
||
DEFAULT_CHARMAP_SOURCE,
|
||
dest->encoding);
|
||
memcpy(dest->data, ach, strlen(ach));
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__locale_time(cblc_field_t *dest,
|
||
const cblc_field_t *arg1,
|
||
size_t arg1_o,
|
||
size_t /*arg1_s*/,
|
||
const cblc_field_t *arg_locale,
|
||
size_t /*arg_locale_o*/,
|
||
size_t /*arg_locale_s*/)
|
||
|
||
{
|
||
char ach[256] = " ";
|
||
|
||
if( arg_locale)
|
||
{
|
||
// We don't yet know what to do with a locale
|
||
exception_raise(ec_locale_missing_e);
|
||
}
|
||
else
|
||
{
|
||
// Default locale
|
||
tm tm = {};
|
||
memcpy(ach, arg1->data+arg1_o, 8);
|
||
ach[8] = '\0';
|
||
long hms = atoi(ach);
|
||
tm.tm_hour = hms/10000;
|
||
tm.tm_min = hms/100 % 100;
|
||
tm.tm_sec = hms % 100;
|
||
strftime(ach, sizeof(ach), nl_langinfo(T_FMT), &tm);
|
||
}
|
||
|
||
__gg__adjust_dest_size(dest, strlen(ach));
|
||
__gg__convert_encoding(PTRCAST(char, dest->data),
|
||
DEFAULT_CHARMAP_SOURCE,
|
||
dest->encoding);
|
||
memcpy(dest->data, ach, strlen(ach));
|
||
}
|
||
|
||
extern "C"
|
||
void
|
||
__gg__locale_time_from_seconds( cblc_field_t *dest,
|
||
const cblc_field_t *arg1,
|
||
size_t arg1_o,
|
||
size_t arg1_s,
|
||
const cblc_field_t *arg_locale,
|
||
size_t /*arg_locale_o*/,
|
||
size_t /*arg_locale_s*/)
|
||
{
|
||
char ach[256] = " ";
|
||
|
||
if( arg_locale )
|
||
{
|
||
// We don't yet know what to do with a locale
|
||
exception_raise(ec_locale_missing_e);
|
||
}
|
||
else
|
||
{
|
||
// Default locale
|
||
tm tm = {};
|
||
|
||
int rdigits=0;
|
||
long seconds = (long)__gg__binary_value_from_qualified_field(&rdigits,
|
||
arg1,
|
||
arg1_o,
|
||
arg1_s);
|
||
tm.tm_hour = seconds/3600;
|
||
tm.tm_min = ((seconds%3600) / 60) % 100;
|
||
tm.tm_sec = seconds % 100;
|
||
strftime(ach, sizeof(ach), nl_langinfo(T_FMT), &tm);
|
||
}
|
||
|
||
__gg__adjust_dest_size(dest, strlen(ach));
|
||
__gg__convert_encoding(PTRCAST(char, dest->data),
|
||
DEFAULT_CHARMAP_SOURCE,
|
||
dest->encoding);
|
||
memcpy(dest->data, ach, strlen(ach));
|
||
}
|