/************ Xobject C++ Functions Source Code File (.CPP) ************/
/*  Name: XOBJECT.CPP  Version 2.5                                     */
/*                                                                     */
/*  (C) Copyright to the author Olivier BERTRAND          1998-2017    */
/*                                                                     */
/*  This file contains base XOBJECT class functions.                   */
/*  Also here is the implementation of the CONSTANT class.             */
/***********************************************************************/

/***********************************************************************/
/*  Include mariaDB header file.                                       */
/***********************************************************************/
#include "my_global.h"
#include "m_string.h"

/***********************************************************************/
/*  Include required application header files                          */
/*  global.h    is header containing all global Plug declarations.     */
/*  plgdbsem.h  is header containing the DB applic. declarations.      */
/***********************************************************************/
#include "global.h"
#include "plgdbsem.h"
#include "xobject.h"

/***********************************************************************/
/*  Macro definitions.                                                 */
/***********************************************************************/
#if defined(_DEBUG) || defined(DEBTRACE)
#define ASSERT(B)      assert(B);
#else
#define ASSERT(B)
#endif

/***********************************************************************/
/*  The one and only needed void object.                               */
/***********************************************************************/
XVOID Xvoid;
PXOB const pXVOID = &Xvoid;       // Pointer used by other classes

/* ------------------------- Class XOBJECT --------------------------- */

/***********************************************************************/
/*  GetCharValue: returns the Result value as a char string.           */
/*  Using GetCharValue provides no conversion from numeric types.      */
/***********************************************************************/
PSZ XOBJECT::GetCharValue(void)
  {
  ASSERT(Value)
  return Value->GetCharValue();
  } // end of GetCharValue()

/***********************************************************************/
/*  GetShortValue: returns the Result value as a short integer.        */
/***********************************************************************/
short XOBJECT::GetShortValue(void)
  {
  ASSERT(Value)
  return Value->GetShortValue();
  } // end of GetShortValue

/***********************************************************************/
/*  GetIntValue: returns the Result value as a int integer.            */
/***********************************************************************/
int XOBJECT::GetIntValue(void)
  {
  ASSERT(Value)
  return Value->GetIntValue();
  } // end of GetIntValue

/***********************************************************************/
/*  GetFloatValue: returns the Result value as a double float.         */
/***********************************************************************/
double XOBJECT::GetFloatValue(void)
  {
  ASSERT(Value)
  return Value->GetFloatValue();
  } // end of GetFloatValue

/* ------------------------- Class CONSTANT -------------------------- */

/***********************************************************************/
/*  CONSTANT public constructor.                                       */
/***********************************************************************/
CONSTANT::CONSTANT(PGLOBAL g, void *value, short type)
  {
  if (!(Value = AllocateValue(g, value, (int)type)))
		throw (int)TYPE_CONST;

  Constant = true;
  } // end of CONSTANT constructor

/***********************************************************************/
/*  CONSTANT public constructor.                                       */
/***********************************************************************/
CONSTANT::CONSTANT(PGLOBAL g, int n)
  {
  if (!(Value = AllocateValue(g, &n, TYPE_INT)))
		throw (int)TYPE_CONST;

  Constant = true;
  } // end of CONSTANT constructor

/***********************************************************************/
/*  GetLengthEx: returns an evaluation of the constant string length.  */
/*  Note: When converting from token to string, length has to be       */
/*    specified but we need the domain length, not the value length.   */
/***********************************************************************/
int CONSTANT::GetLengthEx(void)
  {
  return Value->GetValLen();
  } // end of GetLengthEx

/***********************************************************************/
/*  Convert a constant to the given type.                              */
/***********************************************************************/
void CONSTANT::Convert(PGLOBAL g, int newtype)
  {
  if (Value->GetType() != newtype)
    if (!(Value = AllocateValue(g, Value, newtype)))
			throw (int)TYPE_CONST;

  } // end of Convert

/***********************************************************************/
/*  Compare: returns true if this object is equivalent to xp.          */
/***********************************************************************/
bool CONSTANT::Compare(PXOB xp)
  {
  if (this == xp)
    return true;
  else if (xp->GetType() != TYPE_CONST)
    return false;
  else
    return Value->IsEqual(xp->GetValue(), true);

  } // end of Compare

#if 0
/***********************************************************************/
/*  Rephrase: temporary implementation used by PlugRephraseSQL.        */
/***********************************************************************/
bool CONSTANT::Rephrase(PGLOBAL g, PSZ work)
  {
  switch (Value->GetType()) {
    case TYPE_STRING:
      sprintf(work + strlen(work), "'%s'", Value->GetCharValue());
      break;
    case TYPE_SHORT:
      sprintf(work + strlen(work), "%hd", Value->GetShortValue());
      break;
    case TYPE_INT:
    case TYPE_DATE:
      sprintf(work + strlen(work), "%d", Value->GetIntValue());
      break;
    case TYPE_DOUBLE:
      sprintf(work + strlen(work), "%lf", Value->GetFloatValue());
      break;
    case TYPE_BIGINT:
      sprintf(work + strlen(work), "%lld", Value->GetBigintValue());
      break;
    case TYPE_TINY:
      sprintf(work + strlen(work), "%d", Value->GetTinyValue());
      break;
    default:
      snprintf(g->Message, sizeof(g->Message), MSG(BAD_CONST_TYPE), Value->GetType());
      return false;
    } // endswitch

  return false;
  } // end of Rephrase
#endif // 0

/***********************************************************************/
/*  Make file output of a constant object.                             */
/***********************************************************************/
void CONSTANT::Printf(PGLOBAL g, FILE *f, uint n)
  {
  Value->Printf(g, f, n);
  } /* end of Printf */

/***********************************************************************/
/*  Make string output of a constant object.                           */
/***********************************************************************/
void CONSTANT::Prints(PGLOBAL g, char *ps, uint z)
  {
  Value->Prints(g, ps, z);
  } /* end of Prints */

/* -------------------------- Class STRING --------------------------- */

/***********************************************************************/
/*  STRING public constructor for new char values. Alloc Size must be  */
/*  calculated because PlugSubAlloc rounds up size to multiple of 8.   */
/***********************************************************************/
STRING::STRING(PGLOBAL g, uint n, PCSZ str)
{
  G = g;
  Length = (str) ? strlen(str) : 0;

  if ((Strp = (PSZ)PlgDBSubAlloc(g, NULL, MY_MAX(n, Length) + 1))) {
    if (str)
      strcpy(Strp, str);
    else
      *Strp = 0;

    Next = GetNext();
    Size = (int)(Next - Strp);
		Trc = false;
  } else {
    // This should normally never happen
    Next = NULL;
    Size = 0;
		Trc = true;
  } // endif Strp

} // end of STRING constructor

/***********************************************************************/
/*  Reallocate the string memory and return the (new) position.        */
/*  If Next is equal to GetNext() this means that no new suballocation */
/*  has been done. Then we can just increase the size of the current   */
/*  allocation and the Strp will remain pointing to the same memory.   */
/***********************************************************************/
char *STRING::Realloc(uint len)
{
  char *p;
  bool  b = (Next == GetNext());
  
  p = (char*)PlgDBSubAlloc(G, NULL, b ? len - Size : len);

  if (!p) {
    // No more room in Sarea; this is very unlikely
    strcpy(G->Message, "No more room in work area");
		Trc = true;
    return NULL;
    } // endif p

  if (b)
    p = Strp;

  Next = GetNext();
  Size = (int)(Next - p);
  return p;
} // end of Realloc

/***********************************************************************/
/*  Set a STRING new PSZ value.                                        */
/***********************************************************************/
bool STRING::Set(PCSZ s)
{
  if (!s)
    return false;

  uint len = strlen(s) + 1;

  if (len > Size) {
    char *p = Realloc(len);
    
    if (!p)
      return true;
    else
      Strp = p;

    } // endif n

 	strcpy(Strp, s);
  Length = len - 1;
  return false;
} // end of Set

/***********************************************************************/
/*  Set a STRING new PSZ value.                                        */
/***********************************************************************/
bool STRING::Set(char *s, uint n)
{
  if (!s)
    return false;

  uint len = strnlen(s, n) + 1;

  if (len > Size) {
    char *p = Realloc(len);
    
    if (!p)
      return true;
    else
      Strp = p;

    } // endif n

 	strncpy(Strp, s, n);
  Length = len - 1;
  return false;
} // end of Set

/***********************************************************************/
/*  Append a char* to a STRING.                                        */
/***********************************************************************/
bool STRING::Append(const char *s, uint ln, bool nq)
{
  if (!s)
    return false;

  uint i, len = Length + ln + 1;

  if (len > Size) {
    char *p = Realloc(len);
    
    if (!p)
      return true;
    else if (p != Strp) {
      strcpy(p, Strp);
      Strp = p;
      } // endif p

    } // endif n

	if (nq) {
		for (i = 0; i < ln; i++)
			switch (s[i]) {
			case '\\':   Strp[Length++] = '\\'; Strp[Length++] = '\\'; break;
			case '\0':   Strp[Length++] = '\\'; Strp[Length++] = '0';  break;
			case '\'':   Strp[Length++] = '\\'; Strp[Length++] = '\''; break;
			case '\n':   Strp[Length++] = '\\'; Strp[Length++] = 'n';  break;
			case '\r':   Strp[Length++] = '\\'; Strp[Length++] = 'r';  break;
			case '\032': Strp[Length++] = '\\'; Strp[Length++] = 'Z';  break;
			default:     Strp[Length++] = s[i];
			}	// endswitch s[i]

	} else
		for (i = 0; i < ln && s[i]; i++)
			Strp[Length++] = s[i];

  Strp[Length] = 0;
  return false;
} // end of Append

/***********************************************************************/
/*  Append a PCSZ to a STRING.                                         */
/***********************************************************************/
bool STRING::Append(PCSZ s)
{
  if (!s)
    return false;

  uint len = Length + strlen(s) + 1;

  if (len > Size) {
    char *p = Realloc(len);
    
    if (!p)
      return true;
    else if (p != Strp) {
      strcpy(p, Strp);
      Strp = p;
      } // endif p

    } // endif n

  strcpy(Strp + Length, s);
  Length = len - 1;
  return false;
} // end of Append

/***********************************************************************/
/*  Append a STRING to a STRING.                                       */
/***********************************************************************/
bool STRING::Append(STRING &str)
{
  return Append(str.GetStr());
} // end of Append

/***********************************************************************/
/*  Append a char to a STRING.                                         */
/***********************************************************************/
bool STRING::Append(char c)
{
  if (Length + 2 > Size) {
    char *p = Realloc(Length + 2);
    
    if (!p)
      return true;
    else if (p != Strp) {
      strcpy(p, Strp);
      Strp = p;
      } // endif p

    } // endif n

  Strp[Length++] = c;
  Strp[Length] = 0;
  return false;
} // end of Append

/***********************************************************************/
/*  Append a quoted PSZ to a STRING.                                   */
/***********************************************************************/
bool STRING::Append_quoted(PCSZ s)
{
  bool b = Append('\'');

  if (s) for (const char *p = s; !b && *p; p++)
    switch (*p) {
      case '\'':
      case '\\':
      case '\t':
      case '\n':
      case '\r':
      case '\b':
      case '\f': b |= Append('\\');
        // fall through
      default:
        b |= Append(*p);
        break;
      } // endswitch *p

  return (b |= Append('\''));
} // end of Append_quoted

/***********************************************************************/
/*  Resize to given length but only when last suballocated.            */
/*  New size should be greater than string length.                     */
/***********************************************************************/
bool STRING::Resize(uint newsize)
{
  if (Next == GetNext() && newsize > Length) {
    uint        nsz = (((signed)newsize + 7) / 8) * 8;
    int         diff = (signed)Size - (signed)nsz;
    PPOOLHEADER pp = (PPOOLHEADER)G->Sarea;

    if ((signed)pp->FreeBlk + diff < 0)
      return true;      // Out of memory

    pp->To_Free -= diff;
    pp->FreeBlk += diff;
    Size = nsz;
    return false;
  } else
    return newsize > Size;

} // end of Resize