//----------------------------------------------------------------------------
// ObjectWindows
// Copyright (c) 1993, 1996 by Borland International, All Rights Reserved
//
/// \file
/// Implementation of TPXPictureValidator, Paradox-picture input validator
//----------------------------------------------------------------------------
#include <owl/pch.h>
#include <owl/validate.h>
#include <owl/validate.rh>
#include <owl/applicat.h>
#include <owl/appdict.h>
#include <owl/framewin.h>
#include <ctype.h>
#include <stdio.h>
namespace owl {
OWL_DIAGINFO;
//
/// Constructs a picture validator object by first calling the constructor inherited
/// from TValidator and setting pic to point to it. Then sets the voFill bit in
/// Options if AutoFill is true and sets Options to voOnAppend. Throws a TXValidator
/// exception if the picture is invalid.
//
TPXPictureValidator::TPXPictureValidator(LPCTSTR pic, bool autoFill)
:
TValidator(),
Pic(pic)
{
Init(autoFill);
}
//
/// String-aware overload
//
TPXPictureValidator::TPXPictureValidator(const tstring& pic, bool autoFill)
:
TValidator(),
Pic(pic)
{
Init(autoFill);
}
//
/// Shared initialization
//
void TPXPictureValidator::Init(bool autoFill)
{
Options = voOnAppend;
if (autoFill)
Options |= voFill;
if (!SyntaxCheck())
TXValidator::Raise();
}
//
/// Overrides TValidator's virtual function and displays a message box that indicates
/// an error in the picture format and displays the string pointed to by Pic.
//
void
TPXPictureValidator::Error(TWindow* owner)
{
PRECONDITION(owner);
owner->FormatMessageBox(owner->LoadString(IDS_VALPXPCONFORM), owner->LoadString(IDS_VALCAPTION), MB_ICONERROR | MB_OK, Pic.c_str());
}
//
/// IsValidInput overrides TValidator's virtual function and checks the string
/// passed in str against the format picture specified in Pic. IsValid returns true
/// if Pic is NULL or Picture does not return Error for str; otherwise, it returns
/// false. The suppressFill parameter overrides the value in voFill for the duration
/// of the call to IsValidInput.
/// If suppressFill is false and voFill is set, the call to Picture returns a filled
/// string based on str, so the image in the edit control automatically reflects the
/// format specified in Pic.
//
bool
TPXPictureValidator::IsValidInput(LPTSTR input, bool suppressFill)
{
bool autoFill = (Options&voFill) && !suppressFill;
return Pic.empty()
|| Picture(input, autoFill) != prError;
}
//
/// IsValid overrides TValidator's virtual function and compares the string passed
/// in str with the format picture specified in Pic. IsValid returns true if Pic is
/// NULL or if Picture returns Complete for str, indicating that str needs no
/// further input to meet the specified format; otherwise, it returns false.
//
bool
TPXPictureValidator::IsValid(LPCTSTR input)
{
if (Pic.empty())
return true;
TPicResult rslt = Picture(CONST_CAST(LPTSTR,input), false);
return rslt == prComplete || rslt == prEmpty;
}
//
/// Adjusts the 'value' of the text, given a cursor position and an amount. Returns
/// the actual amount adjusted.
//
int
TPXPictureValidator::Adjust(tstring& /*text*/, int& /*begPos*/, int& /*endPos*/, int /*amount*/)
{
return 0;
}
//
//
//
inline bool
TPXPictureValidator::IsComplete(TPicResult rslt)
{
return rslt == prComplete || rslt == prAmbiguous;
}
//
//
//
inline bool
TPXPictureValidator::IsIncomplete(TPicResult rslt)
{
return rslt == prIncomplete || rslt == prIncompNoFill;
}
//
// Skip a character or a picture group
//
void
TPXPictureValidator::ToGroupEnd(uint termCh, uint& i)
{
int brkLevel = 0;
int brcLevel = 0;
do {
if (i == termCh)
return;
switch (Pic[i]) {
case _T('['): brkLevel++; break;
case _T(']'): brkLevel--; break;
case _T('{'): brcLevel++; break;
case _T('}'): brcLevel--; break;
case _T(';'): i++; break;
case _T('*'):
i++;
while (_istdigit((tchar)Pic[i]))
i++;
ToGroupEnd(termCh, i);
continue;
}
i += CharSize((LPCTSTR)Pic.c_str() + i) / sizeof(tchar);
} while (brkLevel || brcLevel);
}
//
/// Find the next comma separator
//
bool
TPXPictureValidator::SkipToComma(uint termCh, uint& i)
{
for (;;) {
ToGroupEnd(termCh, i);
if (i == termCh)
return false;
if (Pic[i] == _T(',')) {
i++;
return i < termCh;
}
}
}
//
/// Calculate the end of a group (does not modify i)
//
uint
TPXPictureValidator::CalcTerm(uint termCh, uint i)
{
ToGroupEnd(termCh, i);
return i;
}
//
/// The next group is repeated X times
//
TPicResult
TPXPictureValidator::Iteration(LPTSTR input, uint termCh, uint& i, uint& j)
{
TPicResult rslt;
uint newTermCh;
i++; // Skip '*'
// Retrieve number
uint itr = 0;
for (; _istdigit((tchar)Pic[i]); i++)
itr = itr * 10 + Pic[i] - _T('0');
if (i >= termCh)
return prSyntax;
newTermCh = CalcTerm(termCh, i);
//
// if itr is 0 allow any number, otherwise enforce the number
//
uint k = i;
if (itr) {
for (uint m = 0; m < itr; m++) {
i = k;
rslt = Process(input, newTermCh, i, j);
if (!IsComplete(rslt)) {
if (rslt == prEmpty) // Empty means incomplete since all are required
rslt = prIncomplete;
return rslt;
}
}
}
else {
do {
i = k;
rslt = Process(input, newTermCh, i, j);
} while (IsComplete(rslt));
if (rslt == prEmpty || rslt == prError) {
i++;
rslt = prAmbiguous;
}
}
i = newTermCh;
return rslt;
}
//
/// Process a picture group
//
TPicResult
TPXPictureValidator::Group(LPTSTR input, uint termCh, uint& i, uint& j)
{
uint groupTermCh = CalcTerm(termCh, i);
i++;
TPicResult rslt = Process(input, groupTermCh - 1, i, j);
if (!IsIncomplete(rslt))
i = groupTermCh;
return rslt;
}
//
//
//
TPicResult
TPXPictureValidator::CheckComplete(uint termCh, uint& i, TPicResult rslt)
{
uint j = i;
if (IsIncomplete(rslt)) {
// Skip optional pieces
for (;;) {
if (Pic[j] == _T('['))
ToGroupEnd(termCh, j);
else if (Pic[j] == _T('*')) {
if (!isdigit((tchar)Pic[j+1])) {
j++;
ToGroupEnd(termCh, j);
}
else
break;
}
else
break;
if (j == termCh)
return prAmbiguous; // end of the string, don't know if complete
}
}
return rslt;
}
//
// Check for UNICODE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//
#if !defined(__GNUC__)
#pragma warn -aus
#endif
TPicResult
TPXPictureValidator::Scan(LPTSTR input, uint termCh, uint& i, uint& j)
{
tchar ch;
TPicResult rslt = prEmpty;
uint len = static_cast<uint>(::_tcslen(input));
while (i != termCh && Pic[i] != _T(',')) {
if (j >= len)
return CheckComplete(termCh, i, rslt);
ch = input[j];
switch (Pic[i]) {
case _T('#'):
if (!_istdigit((tchar)ch))
return prError;
else {
input[j++] = ch;
i++;
}
break;
case _T('?'):
if (!_istalpha((tchar)ch))
return prError;
else {
input[j++] = ch;
i++;
}
break;
case _T('&'):
if (!_istalpha((tchar)ch))
return prError;
else {
input[j++] = (tchar)_totupper(ch);
i++;
}
break;
case _T('!'): {
#if defined(BI_DBCS_SUPPORT)
uint n = CharSize(&input[j]) / sizeof(tchar);
if (j + n >= len)
j = len;
else{
if (n == 1)
input[j++] = (tchar)_totupper((tchar)ch);
else
j += n;
}
#else
input[j++] = (tchar)_totupper(ch);
#endif
i++;
break;
}
case _T('@'): {
#if defined(BI_DBCS_SUPPORT)
uint n = CharSize(&input[j]) / sizeof(tchar);
if (j + n >= len)
j = len;
else
j += n;
#else
input[j++] = ch;
#endif
i++;
break;
}
case _T('*'):
rslt = Iteration(input, termCh, i, j);
if (!IsComplete(rslt))
return rslt;
if (rslt == prError)
rslt = prAmbiguous;
break;
case _T('{'):
rslt = Group(input, termCh, i, j);
if (!IsComplete(rslt))
return rslt;
break;
case _T('['):
rslt = Group(input, termCh, i, j);
if (IsIncomplete(rslt))
return rslt;
if (rslt == prError)
rslt = prAmbiguous;
break;
default: {
#if defined(BI_DBCS_SUPPORT)
#if defined(BI_PDOXWINJ_SUPPORT)
// Paradox for Windows/J database program has two special picture to
// support Japanese characters in CodePage 932
//
// '��' 0x81+0x93 - (2 byte '%' symbol)
// 1 byte KATAKANA and KATAKANA symbols (0xA1 - 0xDF)
// '��' 0x81+0x97 - (2 byte '@' symbol)
// any 2 byte characters except 2 byte space (0x81+0x40)
//
// This is hard coded, because we don't know how to get current
// code page in Windows 3.1
//
uint n = CharSize(&input[j]) / sizeof(tchar);
uint n2 = CharSize(((const char *))Pic.c_str() + i) / sizeof(tchar);
if (n2 == 2) {
utchar uc1, uc2;
uc1 = (utchar)Pic[i];
uc2 = (utchar)Pic[i+1];
if (uc1 == 0x81 && uc2 == 0x93) {
if ((utchar)ch >= 0xA1 && (utchar)ch <= 0xDF){
i += n2;
j += n;
break;
}
else
return prError;
}
else if (uc1 == 0x81 && uc2 == 0x97){
if (n == 2 && j + n < len &&
((utchar)ch != 0x81 || (utchar)input[j+1] != 0x40)) {
i += n2;
j += n;
break;
} else
return prError;
}
}
if (n2 == 1 && Pic[i] == ';'){
i++;
n2 = CharSize((const char *)Pic.c_str() + i) / sizeof(tchar);
}
#else
if (Pic[i] == _T(';'))
i++;
uint n = CharSize(&input[j]) / sizeof(tchar);
uint n2 = CharSize((LPCTSTR)Pic.c_str() + i) / sizeof(tchar);
#endif
if (j + n >= len)
n = len - j;
if (n == 1) {
if (ch == _T(' ')) {
#if defined(BI_AUTO_COMPLETION_DBCS_BY_SPACE)
// But, couldn't expand input buffer TValidator classes.
//
if (n < n2) {
memmove(input+n2, input+n, len-n+1);
len += n2 - n;
n = n2;
}
while (n-- > 0)
input[j++] = Pic[i++];
#else
if (n != n2)
return prError;
input[j++] = Pic[i++];
#endif
}
else {
if (n != n2)
return prError;
if (_totupper((tchar)Pic[i]) != _totupper((tchar)ch))
return prError;
input[j++] = Pic[i++];
}
}
else {
if (n > n2)
return prError;
for (uint i1 = 0; i1 < n; i1++)
if (input[j+i1] != Pic[i+i1])
return prError;
while (n-- > 0)
input[j++] = Pic[i++];
}
#else
if (Pic[i] == _T(';'))
i++;
if (_totupper(Pic[i]) != _totupper(ch))
{
if (ch == _T(' '))
ch = Pic[i];
else
return prError;
}
input[j++] = Pic[i];
i++;
#endif
}
}
if (rslt == prAmbiguous)
rslt = prIncompNoFill;
else
rslt = prIncomplete;
}
return (rslt == prIncompNoFill) ? prAmbiguous : prComplete;
}
#if !defined(__GNUC__)
#pragma warn .aus
#endif
//
//
//
TPicResult
TPXPictureValidator::Process(LPTSTR input, uint termCh, uint& i, uint& j)
{
TPicResult rslt;
uint incompJ, incompI;
incompJ = incompI = 0;
bool incomp = false;
uint oldI = i;
uint oldJ = j;
do {
rslt = Scan(input, termCh, i, j);
//
// Only accept completes if they make it farther in the input
// stream from the last incomplete
//
if ((rslt==prComplete || rslt==prAmbiguous) && incomp && j < incompJ) {
rslt = prIncomplete;
j = incompJ;
}
if (rslt == prError || rslt == prIncomplete) {
if (!incomp && rslt == prIncomplete) {
incomp = true;
incompI = i;
incompJ = j;
}
i = oldI;
j = oldJ;
if (!SkipToComma(termCh, i)) {
if (incomp) {
i = incompI;
j = incompJ;
return prIncomplete;
}
return rslt;
}
oldI = i;
}
} while (rslt == prError || rslt == prIncomplete);
return (rslt == prComplete && incomp) ? prAmbiguous : rslt;
}
//
//
//
bool
TPXPictureValidator::SyntaxCheck()
{
const auto n = static_cast<int>(Pic.length());
if (Pic.empty() ||
(Pic.back() == _T(';')) ||
(Pic.back() == _T('*') && Pic[n - 2] != _T(';')))
{
return false;
}
auto brkLevel = 0;
auto brcLevel = 0;
auto next = [&](int& i) { i += CharSize(&Pic[i]) / sizeof(tchar); };
for (auto i = 0; i < n; next(i))
{
switch (Pic[i])
{
case _T('['): ++brkLevel; break;
case _T(']'): --brkLevel; break;
case _T('{'): ++brcLevel; break;
case _T('}'): --brcLevel; break;
case _T(';'): next(i); break;
case _T('*'):
next(i);
if (!_istdigit(Pic[i]))
return false;
break;
}
}
return brkLevel == 0 && brcLevel == 0;
}
#if defined(BI_DBCS_SUPPORT)
#if defined(BI_PDOXWINJ_SUPPORT)
namespace {
LPSTR lstrchrp(LPCSTR s1, LPCSTR s2)
{
uint n2 = CharSize(s2) / sizeof(tchar);
do {
uint n1 = CharSize(s1) / sizeof(tchar);
if (n1 == n2 && memcmp(s1, s2, n1) == 0)
return (char *)s1;
for ( ; n1-- > 0; s1++)
if( !*s1 )
break;
} while (*s1) ;
return (char *)NULL;
}
}
#endif
#endif
//
/// Checks the validity of the input according to the format specified by the
/// picture string, possibly adding fill characters to the end of the input.
//
/// Formats the string passed in input according to the format specified by the
/// picture string pointed to by Pic. Picture returns prError if there is an error
/// in the picture string or if input contains data that cannot fit the specified
/// picture. Returns prComplete if input can fully satisfy the specified picture.
/// Returns prIncomplete if input contains data that incompletely fits the specified
/// picture.
//
/// The following characters are used in creating format pictures:
///
/// Special
/// - \b # Accept only a digit
/// - \b ? Accept only a letter (case_insensitive)
/// - \b & Accept only a letter, force to uppercase
/// - \b @ Accept any character
/// - \b ! Accept any character, force to uppercase
/// Match
/// - \b ; Take next character literally
/// - \b * Repetition count
/// - \b [] Option
/// - \b {} Grouping operators
/// - \b , Set of alternatives
/// - All others Taken literally
//
TPicResult
TPXPictureValidator::Picture(LPTSTR input, bool autoFill)
{
// Check if any input data.
//
if (!input || !*input)
return prEmpty;
// Indexes for Pic[] and input[].
//
uint iPic = 0;
uint iInput = 0;
// Process the given input to check for an error.
//
TPicResult rslt = Process(input, static_cast<uint>(Pic.length()), iPic, iInput);
if ((rslt != prError) && (rslt != prSyntax) && (iInput < ::_tcslen(input)))
rslt = prError;
// If the result is incomplete and autofill is requested, then copy literal
// characters from the picture over to the input.
//
if ((rslt == prIncomplete) && autoFill) {
bool reprocess = false;
#if defined(BI_PDOXWINJ_SUPPORT)
// "����"
while (iPic < Pic.length() && !lstrchrp("#?&!@*{}[],\x81\x93\x81\x97", (LPCSTR)Pic.c_str() + iPic))
#else
while (iPic < Pic.length() && !_tcschr(_T("#?&!@*{}[],"), Pic[iPic]))
#endif
{
if (Pic[iPic] == _T(';'))
iPic++;
#if defined(BI_DBCS_SUPPORT)
uint k = static_cast<uint>(::_tcslen(input));
uint n = CharSize((LPCTSTR)Pic.c_str() + iPic) / sizeof(tchar);
memmove(input + k, Pic.c_str() + iPic, n);
input[k + n] = _T('\0');
iPic += n;
iInput += n;
#else
input[iInput++] = Pic[iPic++];
#endif
reprocess = true;
}
if (reprocess)
{
input[iInput] = _T('\0'); // Terminate the copy, since we are probably appending
iInput = iPic = 0;
rslt = Process(input, static_cast<uint>(Pic.length()), iPic, iInput);
}
}
// We perform an additional check here because SyntaxCheck may not catch them
// all with it using different logic.
//
if (rslt == prSyntax)
TXValidator::Raise();
return (rslt == prAmbiguous) ? prComplete : (rslt == prIncompNoFill) ? prIncomplete : rslt;
}
IMPLEMENT_STREAMABLE1(TPXPictureValidator, TValidator);
#if OWL_PERSISTENT_STREAMS
//
//
//
void*
TPXPictureValidator::Streamer::Read(ipstream& is, uint32 /*version*/) const
{
ReadBaseObject((TValidator*)GetObject(), is);
is >> GetObject()->Pic;
return GetObject();
}
//
//
//
void
TPXPictureValidator::Streamer::Write(opstream& os) const
{
WriteBaseObject((TValidator*)GetObject(), os);
os << GetObject()->Pic;
}
#endif
} // OWL namespace
↑ V1004 The 'owner' pointer was used unsafely after it was verified against nullptr. Check lines: 67, 68.
↑ V669 The 'i' argument is a non-constant reference. The analyzer is unable to determine the position at which this argument is being modified. It is possible that the function contains an error.