blob: 4d03bf09f67954cf2da5b902aafcf863124b00c9 [file] [log] [blame]
/**********************************************************************
// @@@ START COPYRIGHT @@@
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
// @@@ END COPYRIGHT @@@
//
**********************************************************************/
/* -*-C++-*-
*****************************************************************************
*
* File: ParserMsg.C
* RCS: $Id: ParserMsg.cpp,v 1.13 1998/08/25 17:19:09 Exp $
* Description: This file contains the shared routines for the parsers in sqlci
* and compiler (i.e. parser directory)
*
* Created: 12/11/96
* Modified: $ $Date: 1998/08/25 17:19:09 $ (GMT)
* Language: C++
* Status: $State: Exp $
*
*
*****************************************************************************
*/
#include <ctype.h>
#include <string.h>
#if !defined(__GNUC__) || __GNUC__ < 3
#include <strstream.h>
#endif
#include "NAWinNT.h"
#include "ComDiags.h"
#include "wstr.h"
#include "nawstring.h"
#include "NLSConversion.h"
#include "ErrorMessage.h"
#include "ParserMsg.h" // header for StoreSyntaxError, implemented herein
#include "csconvert.h"
// This function takes as an argument a pointer to a ComCondition object.
// This function accesses the global variables: input_str, input_pos in
// order to format a string indicating where in the given SQL statement
// a syntax error occurred.
//
// This routine prints out each format-line of the buffer until the line
// is reached which contains the error position. That line is the last
// line printed.
//
// Then the next line output consists of a sequence of zero or more spaces
// and then a caret. This gives the appearance of the caret indicating
// where in the buffer's text the error has been detected.
//
// Finally, if there is even one more format-line in the buffer then
// a line is output saying "further output omitted," rather than actually
// continually printing the lines.
//
// A format-line is determined as follows. It is a line ending at a newline
// character. It is a line ending at the end of the buffer (that would
// be the last format-line of the buffer). It is a line that ends after
// running for some number of characters, MAX_FORMAT_LINE. Therefore,
// in effect, you can see that the final rule defining a format-line
// is due to the line-wrapping effect on a terminal or printer, and essentially
// inserts artificial linebreaks into the buffer. If the user doesn't
// like this, then the work around is to supply input that has newlines to
// the user's liking in the first place.
//
// Implementation Tricks:
// In the case that the input_pos was at the end of
// the buffer, we slide it back by a single character. This should have no
// ill-effects on the user detecting where the problem is, and it eliminates
// us having to specially handle this case beyond what we are doing here.
//
// The rest of the routine is fairly straightforward --- particularly if you
// read above about the purpose of this function.
void StoreSyntaxError(const char *input_str, Int32 input_pos,
ComDiagsArea& diags, Int32 dgStrNum,
CharInfo::CharSet input_str_cs,
CharInfo::CharSet terminal_cs)
{
if (!input_str) return;
NAWchar wCharBuffer[ErrorMessage::MSG_BUF_SIZE+1]; // extra space for NULL
Int32 slen = strlen(input_str);
Int32 input_pos_in_numOfNAWchars = input_pos;
if (input_str_cs != CharInfo::ISO88591)
{
// Compute to get the input position in number of NAWcharacters
enum cnv_charset eCnvCS = convertCharsetEnum((Int32)input_str_cs);
char * p1stUnstranslatedChar = NULL;
UInt32 outputOctetLen = 0;
UInt32 translatedCharCount = 0;
Int32 cnvErrStatus = LocaleToUTF16(
cnv_version1 // in - const enum cnv_version version
, input_str // in - const char *in_bufr
, input_pos // in - const int input_str_octet_len
, (const char *)wCharBuffer // out - const char *out_bufr
, ErrorMessage::MSG_BUF_SIZE * sizeof(NAWchar) // no NULL terminator
// in - const int out_bufr_max_octet_len
, eCnvCS // in - enum cnv_charset charset
, p1stUnstranslatedChar // out - char * & first_untranslated_char
, &outputOctetLen // out - unsigned int *output_data_len_p
, 0 // in - const int cnv_flags
, (const Int32)FALSE // in - const int addNullAtEnd_flag
, &translatedCharCount // out - unsigned int * translated_char_cnt_p
);
if (outputOctetLen > 0)
input_pos_in_numOfNAWchars = (Int32)((outputOctetLen - 1) / sizeof(NAWchar));
}
Int32 ulen = LocaleStringToUnicode(input_str_cs, input_str, slen,
wCharBuffer, ErrorMessage::MSG_BUF_SIZE+1, TRUE);
if ( ulen == 0 || ( input_str_cs == CharInfo::ISO88591 && ulen < slen ) )
cerr << input_str << endl;
else
StoreSyntaxError(wCharBuffer, input_pos_in_numOfNAWchars, diags, dgStrNum, terminal_cs);
} // StoreSyntaxError, single-byte version
static const size_t MAX_FORMAT_LINE = 79;
static const size_t MAX_DGSTRING_SIZE =
MINOF(ErrorMessage::MSG_BUF_SIZE,1024) - MAX_FORMAT_LINE;
void StoreSyntaxError(const NAWchar *input_str, Int32 input_pos,
ComDiagsArea& diags, Int32 dgStrNum,
CharInfo::CharSet charset)
{
if (!input_str) return;
NAWString errMsg;
NABoolean internalError = FALSE;
Int32 buffLen = na_wcslen(input_str);
if (buffLen == 0) {
errMsg.append(WIDE_("\n;\n^"));
}
else {
if (input_pos > buffLen) {
if (input_pos > buffLen+1) {
cerr << "*** StoreSyntaxError: (input_pos > buffLen): "
<< input_pos << " " << buffLen;
internalError = TRUE;
}
input_pos = buffLen;
}
else if (input_pos < 0)
input_pos = 0;
// in case there was any text prior on the current line
errMsg.append(WIDE_("\n")); // Start errMsg with a '\n'
const NAWchar *errPos = input_str+input_pos;
if (input_pos == buffLen)
errPos--;
const NAWchar *right = input_str;
const NAWchar *left = NULL;
while (errPos >= right) {
left = right;
size_t charCount = 0;
while ( 1 ) {
charCount++;
right++;
if (*right == '\n' ||
*right == 0 ||
charCount == MAX_FORMAT_LINE)
break;
}
// At this point, *right is a null char, a newline char, or
// the char at (MAX_FORMAT_LINE+1). We want to print the chars
// from left up to but not include right. That's very easy to write
// as a loop! Then, we want to output a newline char. Finally,
// if (*right == newline char) then increment right.
//
// We are careful not to touch (left) in here because we may need
// it later in order to determine the number of chars to space over
// from the start of the final line to where the caret belongeth.
// 3/20/98: Replaced this codeblock:
// const char *currency = left;
// while (currency != right)
// errMsg << *currency++;
// with this:
errMsg.append(left, right - left);
errMsg.append(WIDE_("\n"));
if (*right == '\n')
right++;
}
// The number given by (errPos - left) tells us exactly how many
// characters we must space over before printing the caret.
// We print the spaces, the caret, and then a newline.
{
size_t charCount = errPos - left;
assert(charCount < MAX_FORMAT_LINE);
// 3/20/98: Since the WCHAR version of the SQl text can contain
// half or full width characters, we need to get the exact length
// of the last WCHAR string segment in current locale, then pump that
// many of WCHAR space characters into the buffer.
// Assumption: the fonts used are fixed width!
// Thus, replaced this codeblock:
// while (charCount-- != 0)
// errMsg << ' ';
// with this:
char bufferInLocale[(MAX_FORMAT_LINE+1)*8]; // allow for utf8 characters
Int32 byteCount = UnicodeStringToLocale(charset, (NAWchar*)left, charCount,
bufferInLocale, (MAX_FORMAT_LINE+1)*8
);
for (Int32 i=0; i<byteCount; i++)
errMsg.append(WIDE_(" "));
errMsg.append(WIDE_("^ ("));
char bytestr_str[10];
// get the size of the error offset as a string input_pos is 0 based
str_itoa( input_pos+1, bytestr_str);
NAWString wBytestr_str(CharInfo::ISO88591,bytestr_str);
errMsg.append(wBytestr_str);
errMsg.append(L" characters from start of SQL statement)");
}
// Don't do this; it looks crappy:
//if (right-input_str != buffLen)
// errMsg << endl << "..."; // to indicate rest of SQL stmt not shown
}
NAWchar * const entireBuf = (NAWchar *)errMsg.data();
NAWchar *diagBuf = entireBuf;
// If this is too big, truncate to fit.
// Align to next newline if at least two complete lines will still appear.
//
size_t len = errMsg.length();
if (len > MAX_DGSTRING_SIZE)
{
diagBuf += len - MAX_DGSTRING_SIZE;
NAWchar *newline = na_wcschr(diagBuf, L'\n');
if (newline && na_wcschr(newline, L'\n'))
diagBuf = newline; // Start errMsg with a '\n'
else
diagBuf[0] = L'\n'; // Start errmsg with a '\n'
diagBuf[1] = L'.'; // Interpolate an ellipsis
diagBuf[2] = L'.';
diagBuf[3] = L'.';
}
// convert tabs (but not newlines) to spaces so the caret aligns correctly
for (NAWchar *d = diagBuf; *d; d++)
if (*d == NAWchar('\t'))
*d = ' ';
if (dgStrNum == 0)
diags << DgWString0(diagBuf);
else
diags << DgWString1(diagBuf);
if (internalError) {
char bufferInLocale[MAX_DGSTRING_SIZE];
Lng32 len = UnicodeStringToLocale(charset, entireBuf, na_wcslen(entireBuf),
bufferInLocale, MAX_DGSTRING_SIZE
);
if (len>0) {
cerr << bufferInLocale << endl;
}
}
} // StoreSyntaxError, Unicode version