/* * 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 #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(value % 10) + '0'; value /= 10; if (!value) break; } } else { while (true) { int temp = static_cast(value % radix); rc[rev--] = static_cast(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(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(temp) + '0'; value /= 10; if (!value) break; } } else { while (true) { int64_t temp = (value / radix) * radix - value; rc[rev--] = static_cast(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(radix / 10) + '0'; rc[fwd++] = static_cast(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(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(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(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); }