8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-02-01 06:43:03 +01:00
firebird-mirror/src/qli/picstr.cpp

1217 lines
24 KiB
C++
Raw Normal View History

2001-05-23 15:26:42 +02:00
/*
* PROGRAM: JRD Command Oriented Query Language
* MODULE: picstr.cpp
2001-05-23 15:26:42 +02:00
* DESCRIPTION: Picture String Handler
*
* The contents of this file are subject to the Interbase Public
* License Version 1.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy
* of the License at http://www.Inprise.com/IPL.html
*
* Software distributed under the License is distributed on an
* "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
* or implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code was created by Inprise Corporation
* and its predecessors. Portions created by Inprise Corporation are
* Copyright (C) Inprise Corporation.
*
* All Rights Reserved.
* Contributor(s): ______________________________________.
*/
#include "firebird.h"
2004-04-29 00:36:29 +02:00
#include <stdio.h>
2001-05-23 15:26:42 +02:00
#include <string.h>
2009-01-04 18:46:16 +01:00
#ifdef SOLARIS
#include <nan.h>
#endif
2001-05-23 15:26:42 +02:00
#include "../qli/dtr.h"
2003-09-24 12:30:39 +02:00
#include "../qli/exe.h"
2001-05-23 15:26:42 +02:00
#include "../qli/format.h"
#include "../qli/all_proto.h"
#include "../qli/err_proto.h"
#include "../qli/picst_proto.h"
#include "../qli/mov_proto.h"
#include "../common/classes/timestamp.h"
2001-05-23 15:26:42 +02:00
#include "../jrd/gds_proto.h"
2004-05-16 03:42:11 +02:00
const int PRECISION = 10000;
2001-05-23 15:26:42 +02:00
2003-10-16 10:51:06 +02:00
static TEXT* cvt_to_ascii(SLONG, TEXT*, int);
static const TEXT* default_edit_string(const dsc*, TEXT*);
static void edit_alpha(const dsc*, pics*, TEXT**, USHORT);
static void edit_date(const dsc*, pics*, TEXT**);
static void edit_float(const dsc*, pics*, TEXT**);
static void edit_numeric(const dsc*, pics*, TEXT**);
static int generate(pics*);
static void literal(pics*, TEXT, TEXT**);
2001-05-23 15:26:42 +02:00
static const TEXT* alpha_weekdays[] =
{
2001-05-23 15:26:42 +02:00
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday"
};
2001-05-23 15:26:42 +02:00
static const TEXT* alpha_months[] =
{
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December"
};
pics* PIC_analyze(const TEXT* string, const dsc* desc)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* P I C _ a n a l y z e
*
**************************************
*
* Functional description
* Analyze a picture in preparation for formatting.
*
**************************************/
if (!string)
2008-12-09 08:24:32 +01:00
{
2001-05-23 15:26:42 +02:00
if (!desc)
return NULL;
2008-12-09 08:24:32 +01:00
string = default_edit_string(desc, NULL);
}
2001-05-23 15:26:42 +02:00
pics* picture = (pics*) ALLOCD(type_pic);
2001-05-23 15:26:42 +02:00
picture->pic_string = picture->pic_pointer = string;
// Make a first pass just counting characters
2001-05-23 15:26:42 +02:00
bool debit = false;
2005-03-26 06:40:24 +01:00
TEXT c;
2009-01-10 11:51:02 +01:00
while ((c = generate(picture)) && c != '?')
{
2001-05-23 15:26:42 +02:00
c = UPPER(c);
2009-01-10 11:51:02 +01:00
switch (c)
{
2001-05-23 15:26:42 +02:00
case 'X':
case 'A':
++picture->pic_chars;
break;
case '9':
case 'Z':
case '*':
/* Count all digits;
count them as fractions only after a decimal pt and
2001-05-23 15:26:42 +02:00
before an E */
++picture->pic_digits;
if (picture->pic_decimals && !picture->pic_exponents)
++picture->pic_fractions;
break;
case 'H':
++picture->pic_hex_digits;
break;
case '.':
++picture->pic_decimals;
break;
case '-':
case '+':
picture->pic_flags |= PIC_signed;
case '$':
if (picture->pic_chars || picture->pic_exponents)
++picture->pic_literals;
else if (picture->pic_floats)
++picture->pic_digits;
else
++picture->pic_floats;
break;
case 'M':
++picture->pic_months;
break;
case 'D':
// DB is ambiguous, could be Day Blank or DeBit...
2005-03-26 06:40:24 +01:00
if (UPPER(*picture->pic_pointer) == 'B')
{
2001-05-23 15:26:42 +02:00
++picture->pic_pointer;
++picture->pic_literals;
debit = true;
2001-05-23 15:26:42 +02:00
}
++picture->pic_days;
break;
case 'Y':
++picture->pic_years;
break;
case 'J':
++picture->pic_julians;
break;
case 'W':
++picture->pic_weekdays;
break;
case 'N':
++picture->pic_nmonths;
break;
case 'E':
++picture->pic_exponents;
break;
case 'G':
++picture->pic_float_digits;
break;
case '(':
case ')':
picture->pic_flags |= PIC_signed;
++picture->pic_brackets;
break;
case '\'':
case '"':
picture->pic_flags |= PIC_literal;
2005-03-26 06:40:24 +01:00
{
TEXT d;
// Shouldn't UPPER be used on d before comparing with c?
while ((d = generate(picture)) && d != c)
++picture->pic_literals;
}
2001-05-23 15:26:42 +02:00
picture->pic_flags &= ~PIC_literal;
break;
case '\\':
++picture->pic_literals;
picture->pic_flags |= PIC_literal;
generate(picture);
picture->pic_flags &= ~PIC_literal;
break;
case 'C':
case 'R':
picture->pic_flags |= PIC_signed;
++picture->pic_brackets;
2005-03-26 06:40:24 +01:00
if (generate(picture))
2001-05-23 15:26:42 +02:00
++picture->pic_brackets;
else
++picture->pic_count;
break;
case 'P':
++picture->pic_meridian;
break;
case 'T':
if (picture->pic_hours < 2)
++picture->pic_hours;
else if (picture->pic_minutes < 2)
++picture->pic_minutes;
else
++picture->pic_seconds;
break;
case 'B':
case '%':
case ',':
case '/':
default:
++picture->pic_literals;
break;
}
}
if (c == '?')
picture->pic_missing = PIC_analyze(picture->pic_pointer, 0);
// if a DB showed up, and the string is numeric, treat the DB as DeBit
2001-05-23 15:26:42 +02:00
2009-01-10 11:51:02 +01:00
if (debit && (picture->pic_digits || picture->pic_hex_digits))
{
2001-05-23 15:26:42 +02:00
--picture->pic_days;
--picture->pic_literals;
picture->pic_flags |= PIC_signed;
++picture->pic_brackets;
++picture->pic_brackets;
}
picture->pic_print_length =
picture->pic_digits +
picture->pic_hex_digits +
picture->pic_chars +
picture->pic_floats +
picture->pic_literals +
picture->pic_decimals +
2008-12-09 08:24:32 +01:00
picture->pic_months + picture->pic_days + picture->pic_weekdays + picture->pic_years +
picture->pic_nmonths + picture->pic_julians +
2001-05-23 15:26:42 +02:00
picture->pic_brackets +
picture->pic_exponents +
picture->pic_float_digits +
2008-12-09 08:24:32 +01:00
picture->pic_hours + picture->pic_minutes + picture->pic_seconds +
picture->pic_meridian;
2001-05-23 15:26:42 +02:00
if (picture->pic_missing) {
2008-12-09 08:24:32 +01:00
picture->pic_length = MAX(picture->pic_print_length, picture->pic_missing->pic_print_length);
2001-05-23 15:26:42 +02:00
picture->pic_missing->pic_length = picture->pic_length;
}
else
picture->pic_length = picture->pic_print_length;
2008-12-09 08:24:32 +01:00
if (picture->pic_days || picture->pic_weekdays || picture->pic_months || picture->pic_nmonths ||
picture->pic_years || picture->pic_hours || picture->pic_julians)
2005-03-26 06:40:24 +01:00
{
picture->pic_type = pic_date;
}
2001-05-23 15:26:42 +02:00
else if (picture->pic_exponents || picture->pic_float_digits)
picture->pic_type = pic_float;
else if (picture->pic_digits || picture->pic_hex_digits)
picture->pic_type = pic_numeric;
else
picture->pic_type = pic_alpha;
return picture;
}
void PIC_edit(const dsc* desc, pics* picture, TEXT** output, USHORT max_length)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* P I C _ e d i t
*
**************************************
*
* Functional description
* Edit data from a descriptor through an edit string to a running
* output pointer. For text strings, check that we don't overflow
* the output buffer.
*
**************************************/
2009-01-10 11:51:02 +01:00
switch (picture->pic_type)
{
2001-05-23 15:26:42 +02:00
case pic_alpha:
edit_alpha(desc, picture, output, max_length);
return;
2001-05-23 15:26:42 +02:00
case pic_numeric:
edit_numeric(desc, picture, output);
return;
2001-05-23 15:26:42 +02:00
case pic_date:
edit_date(desc, picture, output);
return;
2001-05-23 15:26:42 +02:00
case pic_float:
edit_float(desc, picture, output);
return;
2001-05-23 15:26:42 +02:00
default:
2004-05-16 03:42:11 +02:00
ERRQ_bugcheck(68); // Msg 68 PIC_edit: class not yet implemented
2001-05-23 15:26:42 +02:00
}
}
void PIC_missing( qli_const* constant, pics* picture)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* P I C _ m i s s i n g
*
**************************************
*
* Functional description
* Create a literal picture string from
* a descriptor for a missing value so
* we can print the missing value
2001-05-23 15:26:42 +02:00
*
**************************************/
2003-10-16 10:51:06 +02:00
const dsc* desc = &constant->con_desc;
2001-05-23 15:26:42 +02:00
2003-10-16 10:51:06 +02:00
const int l = MAX(desc->dsc_length, picture->pic_length);
2001-05-23 15:26:42 +02:00
2004-02-02 12:02:12 +01:00
qli_str* scratch = (qli_str*) ALLOCDV(type_str, l + 3);
TEXT* p = scratch->str_data;
2001-05-23 15:26:42 +02:00
*p++ = '\"';
PIC_edit(desc, picture, &p, l);
*p++ = '\"';
*p = 0;
pics* missing_picture = PIC_analyze(scratch->str_data, desc);
picture->pic_missing = missing_picture;
2008-12-09 08:24:32 +01:00
picture->pic_length = MAX(picture->pic_print_length, missing_picture->pic_print_length);
2001-05-23 15:26:42 +02:00
missing_picture->pic_length = picture->pic_length;
}
2003-10-16 10:51:06 +02:00
static TEXT* cvt_to_ascii( SLONG number, TEXT* pointer, int length)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* c v t _ t o _ a s c i i
*
**************************************
*
* Functional description
* Convert a number to a number of ascii digits (plus terminating
* null), updating pointer.
*
**************************************/
pointer += length + 1;
TEXT* p = pointer;
2001-05-23 15:26:42 +02:00
*--p = 0;
while (--length >= 0) {
*--p = (number % 10) + '0';
number /= 10;
}
return pointer;
}
static const TEXT* default_edit_string(const dsc* desc, TEXT* buff)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* d e f a u l t _ e d i t _ s t r i n g
*
**************************************
*
* Functional description
* Given a skeletal descriptor, generate a default edit string.
*
**************************************/
TEXT buffer[32];
if (!buff)
buff = buffer;
const SSHORT scale = desc->dsc_scale;
2001-05-23 15:26:42 +02:00
2009-01-10 11:51:02 +01:00
switch (desc->dsc_dtype)
{
2001-05-23 15:26:42 +02:00
case dtype_text:
sprintf(buff, "X(%d)", desc->dsc_length);
break;
case dtype_cstring:
sprintf(buff, "X(%d)", desc->dsc_length - 1);
break;
case dtype_varying:
sprintf(buff, "X(%d)", desc->dsc_length - 2);
break;
case dtype_short:
if (!scale)
return "-(5)9";
if (scale < 0 && scale > -5)
sprintf(buff, "-(%d).9(%d)", 6 + scale, -scale);
else if (scale < 0)
sprintf(buff, "-.9(%d)", -scale);
else
sprintf(buff, "-(%d)9", 5 + scale);
break;
case dtype_long:
if (!scale)
return "-(10)9";
if (scale < 0 && scale > -10)
sprintf(buff, "-(%d).9(%d)", 10 + scale, -scale);
else if (scale < 0)
sprintf(buff, "-.9(%d)", -scale);
else
sprintf(buff, "-(%d)9", 11 + scale);
break;
case dtype_int64:
2008-12-05 02:20:14 +01:00
/* replace 16 with 20 later
(as soon as I have sorted out the rounding issues)
2008-12-05 02:20:14 +01:00
FSG*/
if (!scale)
return "-(16)9";
if (scale < 0 && scale > -16)
sprintf(buff, "-(%d).9(%d)", 16 + scale, -scale);
else if (scale < 0)
sprintf(buff, "-.9(%d)", -scale);
else
sprintf(buff, "-(%d)9", 17 + scale);
break;
2001-05-23 15:26:42 +02:00
case dtype_sql_date:
case dtype_timestamp:
return "DD-MMM-YYYY";
case dtype_sql_time:
return "TT:TT:TT.TTTT";
case dtype_real:
return "G(8)";
case dtype_double:
return "G(16)";
default:
return "X(11)";
}
if (buff == buffer) {
2004-02-02 12:02:12 +01:00
qli_str* string = (qli_str*) ALLOCDV(type_str, strlen(buff));
2001-05-23 15:26:42 +02:00
strcpy(string->str_data, buff);
buff = string->str_data;
}
return buff;
}
2009-01-10 11:51:02 +01:00
static void edit_alpha(const dsc* desc,
pics* picture, TEXT** output, USHORT max_length)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* e d i t _ a l p h a
*
**************************************
*
* Functional description
* Edit data from a descriptor through an edit string to a running
* output pointer.
*
**************************************/
TEXT temp[512];
2001-05-23 15:26:42 +02:00
const TEXT* p = NULL;
2003-10-16 10:51:06 +02:00
const USHORT l = MOVQ_get_string(desc, &p, (vary*) temp, sizeof(temp));
const TEXT* const end = p + l;
2001-05-23 15:26:42 +02:00
picture->pic_pointer = picture->pic_string;
picture->pic_count = 0;
TEXT* out = *output;
2001-05-23 15:26:42 +02:00
2009-01-10 11:51:02 +01:00
while (p < end)
{
2001-05-23 15:26:42 +02:00
if ((out - *output) >= max_length)
break;
TEXT c = generate(picture);
2001-05-23 15:26:42 +02:00
if (!c || c == '?')
break;
c = UPPER(c);
2009-01-10 11:51:02 +01:00
switch (c)
{
2001-05-23 15:26:42 +02:00
case 'X':
*out++ = *p++;
break;
case 'A':
if ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z'))
*out++ = *p++;
else
IBERROR(69); // Msg 69 conversion error
2001-05-23 15:26:42 +02:00
break;
case 'B':
*out++ = ' ';
break;
case '"':
case '\'':
case '\\':
literal(picture, c, &out);
break;
default:
*out++ = c;
break;
}
}
*output = out;
}
static void edit_date( const dsc* desc, pics* picture, TEXT** output)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* e d i t _ d a t e
*
**************************************
*
* Functional description
* Edit data from a descriptor through an edit string to a running
* output pointer.
*
**************************************/
SLONG date[2];
2001-05-23 15:26:42 +02:00
DSC temp_desc;
TEXT d, temp[256];
2001-05-23 15:26:42 +02:00
temp_desc.dsc_dtype = dtype_timestamp;
temp_desc.dsc_scale = 0;
temp_desc.dsc_sub_type = 0;
temp_desc.dsc_length = sizeof(date);
temp_desc.dsc_address = (UCHAR *) date;
QLI_validate_desc(temp_desc);
2001-05-23 15:26:42 +02:00
MOVQ_move(desc, &temp_desc);
2003-09-24 12:30:39 +02:00
tm times;
isc_decode_date((ISC_QUAD*) date, &times);
TEXT* p = temp;
2001-05-23 15:26:42 +02:00
const TEXT* nmonth = p;
2001-05-23 15:26:42 +02:00
p = cvt_to_ascii((SLONG) times.tm_mon + 1, p, picture->pic_nmonths);
const TEXT* day = p;
2001-05-23 15:26:42 +02:00
p = cvt_to_ascii((SLONG) times.tm_mday, p, picture->pic_days);
const TEXT* year = p;
2001-05-23 15:26:42 +02:00
p = cvt_to_ascii((SLONG) times.tm_year + 1900, p, picture->pic_years);
const TEXT* julians = p;
2001-05-23 15:26:42 +02:00
p = cvt_to_ascii((SLONG) times.tm_yday + 1, p, picture->pic_julians);
const TEXT* meridian = "";
2009-01-10 11:51:02 +01:00
if (picture->pic_meridian)
{
2001-05-23 15:26:42 +02:00
if (times.tm_hour >= 12) {
meridian = "PM";
if (times.tm_hour > 12)
times.tm_hour -= 12;
}
else
meridian = "AM";
}
2003-10-16 10:51:06 +02:00
const SLONG seconds = date[1] % (60 * PRECISION);
2001-05-23 15:26:42 +02:00
TEXT* hours = p;
2001-05-23 15:26:42 +02:00
p = cvt_to_ascii((SLONG) times.tm_hour, p, picture->pic_hours);
p = cvt_to_ascii((SLONG) times.tm_min, --p, picture->pic_minutes);
p = cvt_to_ascii((SLONG) seconds, --p, 6);
if (*hours == '0')
*hours = ' ';
SLONG rel_day = (date[0] + 3) % 7;
if (rel_day < 0)
2001-05-23 15:26:42 +02:00
rel_day += 7;
const TEXT* weekday = alpha_weekdays[rel_day];
const TEXT* month = alpha_months[times.tm_mon];
2001-05-23 15:26:42 +02:00
picture->pic_pointer = picture->pic_string;
picture->pic_count = 0;
TEXT* out = *output;
bool sig_day = false;
bool blank = true;
2001-05-23 15:26:42 +02:00
2009-01-10 11:51:02 +01:00
for (;;)
{
TEXT c = generate(picture);
2001-05-23 15:26:42 +02:00
if (!c || c == '?')
break;
c = UPPER(c);
2009-01-10 11:51:02 +01:00
switch (c)
{
2001-05-23 15:26:42 +02:00
case 'Y':
*out++ = *year++;
break;
case 'M':
if (*month)
*out++ = *month++;
break;
case 'N':
*out++ = *nmonth++;
break;
case 'D':
d = *day++;
if (!sig_day && d == '0' && blank)
*out++ = ' ';
else {
2003-09-10 19:52:12 +02:00
sig_day = true;
2001-05-23 15:26:42 +02:00
*out++ = d;
}
break;
case 'J':
if (*julians)
*out++ = *julians++;
break;
case 'W':
if (*weekday)
*out++ = *weekday++;
break;
case 'B':
*out++ = ' ';
break;
case 'P':
if (*meridian)
*out++ = *meridian++;
break;
case 'T':
if (*hours)
*out++ = *hours++;
break;
case '"':
case '\'':
case '\\':
literal(picture, c, &out);
break;
default:
*out++ = c;
break;
}
if (c != 'B')
2003-09-10 19:52:12 +02:00
blank = false;
2001-05-23 15:26:42 +02:00
}
*output = out;
}
static void edit_float( const dsc* desc, pics* picture, TEXT** output)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* e d i t _ f l o a t
*
**************************************
*
* Functional description
* Edit data from a descriptor through an edit string to a running
* output pointer.
*
**************************************/
TEXT temp[512];
2003-09-10 19:52:12 +02:00
bool negative = false;
2001-05-23 15:26:42 +02:00
USHORT l, width, decimal_digits, w_digits, f_digits;
#ifdef WIN_NT
2003-09-10 19:52:12 +02:00
bool hack_for_nt_flag = false;
2001-05-23 15:26:42 +02:00
#endif
double number = MOVQ_get_double(desc);
2001-05-23 15:26:42 +02:00
if (number < 0) {
2003-09-10 19:52:12 +02:00
negative = true;
2001-05-23 15:26:42 +02:00
number = -number;
}
2009-01-04 18:46:16 +01:00
#ifdef SOLARIS
if (IsNANorINF(number))
{
sprintf(temp, IsINF(number) ? "Infinity" : "NaN");
}
2009-01-10 11:51:02 +01:00
else
2009-01-04 18:46:16 +01:00
#endif
2001-05-23 15:26:42 +02:00
/* If exponents are explicitly requested (E-format edit_string), generate them.
Otherwise, the rules are: if the number in f-format will fit into the allotted
space, print it in f-format; otherwise print it in e-format.
2001-05-23 15:26:42 +02:00
(G-format is untrustworthy.) */
2009-01-10 11:51:02 +01:00
if (picture->pic_exponents)
{
2008-12-09 08:24:32 +01:00
width = picture->pic_print_length - picture->pic_floats - picture->pic_literals;
2001-05-23 15:26:42 +02:00
decimal_digits = picture->pic_fractions;
sprintf(temp, "%*.*e", width, decimal_digits, number);
#ifdef WIN_NT
2003-09-10 19:52:12 +02:00
hack_for_nt_flag = true;
2001-05-23 15:26:42 +02:00
#endif
}
else if (number == 0)
sprintf(temp, "%.0f", number);
2009-01-10 11:51:02 +01:00
else
{
2001-05-23 15:26:42 +02:00
width = picture->pic_float_digits - 1 + picture->pic_floats;
f_digits = (width > 2) ? width - 2 : 0;
sprintf(temp, "%.*f", f_digits, number);
w_digits = strlen(temp);
2009-01-10 11:51:02 +01:00
if (f_digits)
{
TEXT* p = temp + w_digits; // find the end
2001-05-23 15:26:42 +02:00
w_digits = w_digits - (f_digits + 1);
while (*--p == '0')
--f_digits;
if (*p != '.')
++p;
*p = 0; // move the end
2001-05-23 15:26:42 +02:00
}
2008-12-09 08:24:32 +01:00
if ((w_digits > width) || (!f_digits && w_digits == 1 && temp[0] == '0'))
{
2001-05-23 15:26:42 +02:00
/* if the number doesn't fit in the default window, revert
to exponential notation; displaying the maximum number of
mantissa digits. */
if (number < 1e100)
decimal_digits = (width > 6) ? width - 6 : 0;
else
decimal_digits = (width > 7) ? width - 7 : 0;
sprintf(temp, "%.*e", decimal_digits, number);
#ifdef WIN_NT
2003-09-10 19:52:12 +02:00
hack_for_nt_flag = true;
2001-05-23 15:26:42 +02:00
#endif
}
}
#ifdef WIN_NT
/* On Windows NT exponents have three digits regardless of the magnitude
of the number being formatted. To maintain compatiblity with other
platforms, if the first digit of the exponent is '0', shift the other
digits one to the left. */
2009-01-10 11:51:02 +01:00
if (hack_for_nt_flag)
{
TEXT* p = temp;
2001-05-23 15:26:42 +02:00
while (*p != 'e' && *p != 'E')
++p;
p += 2;
if (*p == '0')
while (*p = *(p + 1))
++p;
}
#endif
TEXT* p = temp;
2001-05-23 15:26:42 +02:00
picture->pic_pointer = picture->pic_string;
picture->pic_count = 0;
TEXT* out = *output;
2001-05-23 15:26:42 +02:00
for (l = picture->pic_length - picture->pic_print_length; l > 0; --l)
*out++ = ' ';
bool is_signed = false;
2008-12-05 02:20:14 +01:00
2009-01-10 11:51:02 +01:00
for (;;)
{
const TEXT e = generate(picture);
TEXT c = e;
2001-05-23 15:26:42 +02:00
if (!c || c == '?')
break;
c = UPPER(c);
2009-01-10 11:51:02 +01:00
switch (c)
{
2001-05-23 15:26:42 +02:00
case 'G':
2009-01-10 11:51:02 +01:00
if (!is_signed)
{
2001-05-23 15:26:42 +02:00
if (negative)
*out++ = '-';
else
*out++ = ' ';
2003-09-10 19:52:12 +02:00
is_signed = true;
2001-05-23 15:26:42 +02:00
}
else if (*p)
*out++ = *p++;
break;
case 'B':
*out++ = ' ';
break;
case '"':
case '\'':
case '\\':
literal(picture, c, &out);
break;
case '9':
case 'Z':
{
2008-01-15 21:15:58 +01:00
if (!(*p) || *p > '9' || *p < '0')
break;
TEXT d = *p++;
if (c == '9' && d == ' ')
d = '0';
else if (c == 'Z' && d == '0')
d = ' ';
*out++ = d;
}
2008-02-02 18:07:19 +01:00
break;
2001-05-23 15:26:42 +02:00
case '.':
*out++ = (*p == c) ? *p++ : c;
break;
case 'E':
if (!(*p))
break;
*out++ = e;
if (UPPER(*p) == c)
++p;
break;
case '+':
case '-':
if (!(*p))
break;
2009-01-10 11:51:02 +01:00
if (*p != '+' && *p != '-')
{
2001-05-23 15:26:42 +02:00
if (is_signed)
*out++ = c;
else if (c == '-' && !negative)
*out++ = ' ';
else if (c == '+' && negative)
*out++ = '-';
else
*out++ = c;
2003-09-10 19:52:12 +02:00
is_signed = true;
2001-05-23 15:26:42 +02:00
}
else if (*p == '-' || c == '+')
*out++ = *p++;
else {
*out++ = ' ';
p++;
}
break;
default:
*out++ = c;
break;
}
}
*output = out;
}
static void edit_numeric(const dsc* desc, pics* picture, TEXT** output)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* e d i t _ n u m e r i c
*
**************************************
*
* Functional description
* Edit data from a descriptor through an edit string to a running
* output pointer.
*
**************************************/
2003-09-10 19:52:12 +02:00
bool negative = false;
bool overflow = false;
2001-05-23 15:26:42 +02:00
TEXT* out = *output;
TEXT* float_ptr = NULL;
2001-05-23 15:26:42 +02:00
double number = MOVQ_get_double(desc);
2009-01-10 11:51:02 +01:00
if (number < 0)
{
2001-05-23 15:26:42 +02:00
number = -number;
2003-09-10 19:52:12 +02:00
negative = true;
2001-05-23 15:26:42 +02:00
if (!(picture->pic_flags & PIC_signed))
2003-09-10 19:52:12 +02:00
overflow = true;
2001-05-23 15:26:42 +02:00
}
SSHORT scale = picture->pic_fractions;
if (scale)
2009-01-10 11:51:02 +01:00
{
2001-05-23 15:26:42 +02:00
if (scale < 0)
do {
2001-05-23 15:26:42 +02:00
number /= 10.;
} while (++scale);
2001-05-23 15:26:42 +02:00
else if (scale > 0)
do {
2001-05-23 15:26:42 +02:00
number *= 10.;
} while (--scale);
2009-01-10 11:51:02 +01:00
}
2001-05-23 15:26:42 +02:00
TEXT temp[512];
TEXT* p = temp;
TEXT* digits = p;
2001-05-23 15:26:42 +02:00
double check;
USHORT power;
2009-01-10 11:51:02 +01:00
if (picture->pic_digits && !overflow)
{
2001-05-23 15:26:42 +02:00
for (check = number, power = picture->pic_digits; power; --power)
2009-01-10 11:51:02 +01:00
check /= 10.0;
2001-05-23 15:26:42 +02:00
if (check >= 1)
2003-09-10 19:52:12 +02:00
overflow = true;
2001-05-23 15:26:42 +02:00
else {
sprintf(digits, "%0*.0f", picture->pic_digits, number);
p = digits + strlen(digits);
}
}
picture->pic_pointer = picture->pic_string;
bool hex_overflow = false;
const TEXT* hex = 0;
2009-01-10 11:51:02 +01:00
if (picture->pic_hex_digits)
{
2001-05-23 15:26:42 +02:00
hex = p;
p += picture->pic_hex_digits;
for (check = number, power = picture->pic_hex_digits; power; --power)
2009-01-10 11:51:02 +01:00
check /= 16.0;
2001-05-23 15:26:42 +02:00
if (check >= 1)
2003-09-10 19:52:12 +02:00
hex_overflow = true;
2001-05-23 15:26:42 +02:00
else {
2004-03-14 06:51:54 +01:00
SLONG nh = static_cast<SLONG>(number);
2001-05-23 15:26:42 +02:00
while (p-- > hex) {
*p = "0123456789abcdef"[nh & 15];
nh >>= 4;
2001-05-23 15:26:42 +02:00
}
}
}
for (USHORT l = picture->pic_length - picture->pic_print_length; l-- > 0;)
2001-05-23 15:26:42 +02:00
*out++ = ' ';
const SLONG n = (number + 0.5 < 1) ? 0 : 1;
TEXT float_char;
TEXT d;
bool signif = false;
2001-05-23 15:26:42 +02:00
2009-01-10 11:51:02 +01:00
for (;;)
{
TEXT c = generate(picture);
2001-05-23 15:26:42 +02:00
if (!c || c == '?')
break;
c = UPPER(c);
if (overflow && c != 'H') {
*out++ = '*';
continue;
}
2009-01-10 11:51:02 +01:00
switch (c)
{
2001-05-23 15:26:42 +02:00
case '9':
2003-09-10 19:52:12 +02:00
signif = true;
2001-05-23 15:26:42 +02:00
*out++ = *digits++;
break;
case 'H':
if (hex_overflow) {
*out++ = '*';
continue;
}
case '*':
case 'Z':
d = (c == 'H') ? *hex++ : *digits++;
if (signif || d != '0') {
*out++ = d;
2003-09-10 19:52:12 +02:00
signif = true;
2001-05-23 15:26:42 +02:00
}
else
*out++ = (c == '*') ? '*' : ' ';
break;
case '+':
case '-':
case '$':
if (c == '+' && negative)
c = '-';
else if (c == '-' && !negative)
c = ' ';
float_char = c;
if (!float_ptr) {
float_ptr = out;
*out++ = (n) ? c : ' ';
break;
}
d = *digits++;
if (signif || d != '0') {
*out++ = d;
2003-09-10 19:52:12 +02:00
signif = true;
2001-05-23 15:26:42 +02:00
break;
}
*float_ptr = ' ';
float_ptr = out;
*out++ = (n) ? c : ' ';
break;
case '(':
case ')':
*out++ = (negative) ? c : ' ';
break;
case 'C':
case 'D':
d = generate(picture);
if (negative) {
*out++ = c;
*out++ = UPPER(d);
}
else if (d) {
*out++ = ' ';
*out++ = ' ';
}
break;
case '.':
2003-09-10 19:52:12 +02:00
signif = true;
2001-05-23 15:26:42 +02:00
*out++ = c;
break;
case 'B':
*out++ = ' ';
break;
case '"':
case '\'':
case '\\':
literal(picture, c, &out);
break;
case ',':
if (signif)
*out++ = c;
else if (float_ptr) {
*float_ptr = ' ';
float_ptr = out;
*out++ = float_char;
}
else
*out++ = ' ';
break;
default:
*out++ = c;
break;
}
if (picture->pic_flags & PIC_suppress_blanks && out[-1] == ' ')
--out;
}
*output = out;
}
static int generate( pics* picture)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* g e n e r a t e
*
**************************************
*
* Functional description
* Generate the next character from a picture string.
*
**************************************/
2003-10-16 10:51:06 +02:00
TEXT c;
2001-05-23 15:26:42 +02:00
2009-01-10 11:51:02 +01:00
for (;;)
{
// If we're in a repeat, pass it back
2001-05-23 15:26:42 +02:00
if (picture->pic_count > 0) {
--picture->pic_count;
return picture->pic_character;
}
// Get the next character. If null, we're done
2001-05-23 15:26:42 +02:00
c = *picture->pic_pointer++;
// If we're in literal mode, just return the character
2001-05-23 15:26:42 +02:00
if (picture->pic_flags & PIC_literal)
break;
/* If the next character is also a paren, it is a debit indicating
bracket. If so, swallow the second. */
if ((c == ')' || c == '(') && *picture->pic_pointer == c) {
picture->pic_pointer++;
return (picture->pic_character = c);
}
// If the character is null and not a repeat count, we're done
2001-05-23 15:26:42 +02:00
if (!c || c != '(')
break;
// We're got a potential repeat count. If real, extract it.
2001-05-23 15:26:42 +02:00
const TEXT* p = picture->pic_pointer;
2001-05-23 15:26:42 +02:00
while (*p >= '0' && *p <= '9')
picture->pic_count = picture->pic_count * 10 + *p++ - '0';
if (p == picture->pic_pointer) {
c = '(';
break;
}
if (*p == ')')
++p;
picture->pic_pointer = p;
/* Someone may have done something very stupid, like specify a repeat
count of zero. It's too late, as we've already gen'd one instance
of the edit character -- but let's not access violate, shall we? */
if (picture->pic_count)
--picture->pic_count;
}
return (picture->pic_character = c);
}
static void literal( pics* picture, TEXT c, TEXT** ptr)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* l i t e r a l
*
**************************************
*
* Functional description
* Handle a literal string in a picture string.
*
**************************************/
TEXT* p = *ptr;
2001-05-23 15:26:42 +02:00
picture->pic_flags |= PIC_literal;
TEXT d;
2001-05-23 15:26:42 +02:00
if (c == '\\')
*p++ = generate(picture);
else
while ((d = generate(picture)) && d != c)
*p++ = d;
*ptr = p;
picture->pic_flags &= ~PIC_literal;
}