8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-31 17:23:03 +01:00
firebird-mirror/src/common/classes/MsgPrint.cpp

402 lines
9.4 KiB
C++

/*
* The contents of this file are subject to the Initial
* Developer's 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.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl.
*
* Software distributed under the License is distributed AS IS,
* 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 Claudio Valderrama on 3-Mar-2007
* for the Firebird Open Source RDBMS project.
*
* Copyright (c) 2007 Claudio Valderrama
* and all contributors signed below.
*
* All Rights Reserved.
* Contributor(s): ______________________________________.
*
*/
// Localized messages type-safe printing facility.
#include "firebird.h"
#include "../jrd/common.h"
#include "BaseStream.h"
#include "MsgPrint.h"
#include <string.h>
#include "../jrd/gds_proto.h"
#include "../common/utils_proto.h"
#include "../jrd/file_params.h"
namespace MsgFormat
{
// Enough to the current conversions. If SINT128 is decoded as a full number
// instead of two parts, it may be updated to 64 and 63 respectively,
// because 2^128 ~ 3.4e38.
const int DECODE_BUF_SIZE = 32;
const int DECODE_BUF_LEN = 31;
// The maximum numeric base we support, using 0..Z
const int MAX_RADIX = 36;
// We don't mess with octal and the like, otherwise DECODE_BUF_* constants have to be enlarged.
const int MIN_RADIX = 10;
// We won't output strings of more than 64K.
const size_t MAX_STRING = 1 << 16;
// Generic functions.
int decode(uint64_t value, char* const rc, int radix = 10);
int decode(int64_t value, char* const rc, int radix = 10);
int decode(double value, char* rc);
int adjust_prefix(int radix, int rev, bool is_neg, char* const rc);
int MsgPrintHelper(BaseStream& out_stream, const safe_cell& item);
// Decode unsigned integer values in any base between 10 and 36.
int decode(uint64_t value, char* const rc, int radix)
{
int rev = DECODE_BUF_LEN;
if (radix < MIN_RADIX || radix > MAX_RADIX)
radix = MIN_RADIX;
if (radix == 10) // go faster for this option
{
while (true)
{
rc[rev--] = static_cast<unsigned char>(value % 10) + '0';
value /= 10;
if (!value)
break;
}
}
else
{
while (true)
{
int temp = static_cast<int>(value % radix);
rc[rev--] = static_cast<unsigned char>(temp < 10 ? temp + '0' : temp - 10 + 'A');
value /= radix;
if (!value)
break;
}
}
return adjust_prefix(radix, rev, false, rc);
}
// Decode signed integer values in any base between 10 and 36.
int decode(int64_t value, char* const rc, int radix)
{
if (value >= 0)
return decode(static_cast<uint64_t>(value), rc, radix);
int rev = DECODE_BUF_LEN;
if (radix < MIN_RADIX || radix > MAX_RADIX)
radix = MIN_RADIX;
// The remainder with negative values is not consistent across compilers.
if (radix == 10) // go faster for this option
{
while (true)
{
int64_t temp = (value / 10) * 10 - value;
rc[rev--] = static_cast<unsigned char>(temp) + '0';
value /= 10;
if (!value)
break;
}
}
else
{
while (true)
{
int64_t temp = (value / radix) * radix - value;
rc[rev--] = static_cast<unsigned char>(temp < 10 ? temp + '0' : temp - 10 + 'A');
value /= radix;
if (!value)
break;
}
}
return adjust_prefix(radix, rev, true, rc);
}
// Stub that relies on the printf family to write a double using "g"
// for smallest representation in text form.
int decode(double value, char* rc)
{
return sprintf(rc, "%g", value);
}
// Sets the radix indicator and returns the length of the adjusted string.
int adjust_prefix(int radix, int rev, bool is_neg, char* const rc)
{
int fwd = 0;
if (is_neg)
rc[fwd++] = '-';
if (radix == 16)
{
rc[fwd++] = '0';
rc[fwd++] = 'x';
}
else if (radix > 10)
{
rc[fwd++] = '(';
rc[fwd++] = static_cast<char>(radix / 10) + '0';
rc[fwd++] = static_cast<char>(radix % 10) + '0';
rc[fwd++] = ')';
}
while (rev < DECODE_BUF_LEN)
rc[fwd++] = rc[++rev];
rc[fwd] = 0;
// fb_assert(fwd < DECODE_BUF_SIZE);
return fwd;
}
// Prints one specific item in the array of type-safe arguments.
// Assumes item is valid.
int MsgPrintHelper(BaseStream& out_stream, const safe_cell& item)
{
switch (item.type)
{
case safe_cell::at_char:
case safe_cell::at_uchar: // For now, treat UCHAR same as char.
return out_stream.write(&item.c_value, 1);
//case safe_cell::at_int16:
//case safe_cell::at_int32:
case safe_cell::at_int64:
{
char s[DECODE_BUF_SIZE];
int n = decode(item.i_value, s);
return out_stream.write(s, n);
}
case safe_cell::at_uint64:
{
char s[DECODE_BUF_SIZE];
int n = decode(static_cast<uint64_t>(item.i_value), s);
return out_stream.write(s, n);
}
case safe_cell::at_int128:
{
// Warning: useless display in real life
char s[DECODE_BUF_SIZE];
int n = decode(item.i128_value.high, s);
int n2 = out_stream.write(s, n) + out_stream.write(".", 1);
n = decode(item.i128_value.low, s);
return n2 + out_stream.write(s, n);
}
case safe_cell::at_double:
{
char s[DECODE_BUF_SIZE];
int n = decode(item.d_value, s);
return out_stream.write(s, n);
}
case safe_cell::at_str:
{
const char* s = item.st_value.s_string;
if (!s)
s = "(null)";
size_t n = strlen(s);
if (n > MAX_STRING)
n = MAX_STRING;
return out_stream.write(s, n);
}
case safe_cell::at_counted_str:
{
size_t n = item.st_value.s_len;
const char* s = item.st_value.s_string;
if (!s)
{
if (!n) // Do not bother with null pointer if length is zero.
return 0;
s = "(null)";
n = strlen(s);
}
else if (n > MAX_STRING)
n = MAX_STRING;
return out_stream.write(s, n);
}
case safe_cell::at_ptr:
{
uint64_t v = reinterpret_cast<uint64_t>(item.p_value);
char s[DECODE_BUF_SIZE];
int n = decode(v, s, 16);
return out_stream.write(s, n);
}
default: // safe_cell::at_none and whatever out of range.
return out_stream.write("(unknown)", 9);
}
}
// Prints the whole chain of arguments, according to format and in the specified stream.
int MsgPrint(BaseStream& out_stream, const char* format, const SafeArg& arg)
{
int out_bytes = 0;
for (const char* iter = format; true; ++iter)
{
switch (*iter)
{
case 0:
return out_bytes;
case '@':
if (iter[1] == '@')
out_bytes += out_stream.write(iter, 1);
else
{
int pos = iter[1] - '0';
if (pos > 0 && static_cast<size_t>(pos) <= arg.m_count)
out_bytes += MsgPrintHelper(out_stream, arg.m_arguments[pos - 1]);
else
{
if (pos >= 0 && pos <= 9)
{
// Show the missing or out of range param number.
char s[3] = {iter[0], iter[1], '?'};
out_bytes += out_stream.write(s, 3);
}
else // Something not a number following @, invalid.
out_bytes += out_stream.write("(error)", 7);
}
}
++iter;
break;
default:
{
const char* iter2 = iter;
while (iter2[1] && iter2[1] != '@')
++iter2;
out_bytes += out_stream.write(iter, iter2 - iter + 1);
iter = iter2;
}
break;
}
}
return 0;
}
// Shortcut version to format a string with arguments on standard output.
int MsgPrint(const char* format, const SafeArg& arg)
{
StdioStream st(stdout);
return MsgPrint(st, format, arg);
}
// Shortcut version to format a string without arguments on standard output.
int MsgPrint(const char* format)
{
static const SafeArg dummy;
StdioStream st(stdout);
return MsgPrint(st, format, dummy);
}
// Shortcut version to format a string with arguments on a string output
// of a given size.
int MsgPrint(char* plainstring, unsigned int s_size,
const char* format, const SafeArg& arg)
{
StringStream st(plainstring, s_size);
return MsgPrint(st, format, arg);
}
// Shortcut version to format a string with arguments on standard error.
int MsgPrintErr(const char* format, const SafeArg& arg)
{
StdioStream st(stderr, true); // flush
return MsgPrint(st, format, arg);
}
} // namespace
int fb_msg_format(void* handle,
USHORT facility,
USHORT number,
unsigned int bsize,
TEXT* buffer,
const MsgFormat::SafeArg& arg)
{
/**************************************
*
* f b _ m s g _ f o r m a t
*
**************************************
*
* Functional description
* Lookup and format message. Return as much of formatted string
* as fits in caller's buffer.
*
**************************************/
using MsgFormat::MsgPrint;
// The field MESSAGES.TEXT is 118 bytes long.
int total_msg = 0;
char msg[120] = "";
const int n = gds__msg_lookup(handle, facility, number, sizeof(msg), msg, NULL);
if (n > 0 && n < sizeof(msg))
{
// Shameful bridge, gds__msg_format emulation for old format messages.
if (strchr(msg, '%'))
{
const TEXT* rep[5];
arg.dump(rep, 5);
total_msg = fb_utils::snprintf(buffer, bsize, msg, rep[0], rep[1], rep[2], rep[3], rep[4]);
}
else
total_msg = MsgPrint(buffer, bsize, msg, arg);
}
else
{
Firebird::string s;
s.printf("can't format message %d:%d -- ", facility, number);
if (n == -1)
s += "message text not found";
else if (n == -2)
{
s += "message file ";
TEXT temp[MAXPATHLEN];
gds__prefix_msg(temp, MSG_FILE);
s += temp;
s += " not found";
}
else
{
fb_utils::snprintf(buffer, bsize, "message system code %d", n);
s += buffer;
}
total_msg = s.copyTo(buffer, bsize);
}
return (n > 0 ? total_msg : -total_msg);
}