/************* TabSys C++ Program Source Code File (.CPP) **************/
/* PROGRAM NAME: TABSYS                                                */
/* -------------                                                       */
/*  Version 2.2                                                        */
/*                                                                     */
/*  Author Olivier BERTRAND                           2004-2013        */
/*                                                                     */
/*  This program are the INI/CFG tables classes.                       */
/***********************************************************************/

/***********************************************************************/
/*  Include relevant sections of the System header files.              */
/***********************************************************************/
#include "my_global.h"
#if defined(WIN32)
#if defined(__BORLANDC__)
#define __MFC_COMPAT__                   // To define min/max as macro
#endif   // __BORLANDC__
//#include <windows.h>
#else   // !WIN32
#if defined(UNIX)
#include <errno.h>
#include <unistd.h>
#else   // !UNIX
#include <io.h>
#endif  // !UNIX
#include <fcntl.h>
#endif  // !WIN32

/***********************************************************************/
/*  Include application header files:                                  */
/*  global.h    is header containing all global declarations.          */
/*  plgdbsem.h  is header containing the DB application declarations.  */
/*  tabdos.h    is header containing the TABDOS class declarations.    */
/***********************************************************************/
#include "global.h"
#include "plgdbsem.h"
#include "reldef.h"
//#include "xobject.h"
#include "filamtxt.h"
#include "tabdos.h"
#include "tabsys.h"
#include "tabmul.h"
#if defined(UNIX)
#include "osutil.h"
#endif   // UNIX

#define CSZ      36                       // Column section name length
#define CDZ      256                      // Column definition length

#if !defined(WIN32)
#define GetPrivateProfileSectionNames(S,L,I)  \
        GetPrivateProfileString(NULL,NULL,"",S,L,I)
#endif   // !WIN32

extern "C" int  trace;

/* -------------- Implementation of the INI classes ------------------ */

/***********************************************************************/
/*  Constructor.                                                       */
/***********************************************************************/
INIDEF::INIDEF(void)
  {
  Pseudo = 3;
  Fn = NULL;
  Xname = NULL;
  Subtype = '?';
  Layout = '?';
  Ln = 0;
  } // end of INIDEF constructor

/***********************************************************************/
/*  DefineAM: define specific AM block values from XDB file.           */
/***********************************************************************/
bool INIDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
  {
  char   buf[8], ds[2];
  void  *memp = Cat->GetDescp();

  if (!stricmp(am, "SYS"))
    strcpy(ds, "T");                 // SYS tables default to T(able)
  else
    strcpy(ds, "I");                 // INI tables default to I(ni)

  Fn = Cat->GetStringCatInfo(g, Name, "Filename", NULL);
  Cat->GetCharCatInfo(Name, "Subtype", ds, buf, sizeof(buf));
  Subtype = toupper(*buf);
  Cat->GetCharCatInfo(Name, "Layout", "C", buf, sizeof(buf));
  Layout = toupper(*buf);

  switch (Subtype) {
#if 0
    case 'C':
    case 'T':
      // Restricted table
      Xname = Cat->GetStringCatInfo(g, Name, "Name", "?");

      if (!strcmp(Xname, "?"))
        Xname = NULL;

      if (*Fn == '?')
        Fn = Cat->GetStringCatInfo(g, Name, "Database", "?");

      if (*Fn != '?') {
        char *p = (char*)PlugSubAlloc(g, memp, _MAX_PATH);

        if (!PlgSetXdbPath(g, Fn, NULL, p, _MAX_PATH, NULL, 0))
          Fn = p;

      } else
        Fn = Cat->GetDescFile();

      Ln = GetIniSize("Database", "Tabsize", "2K", Fn);
      break;
#endif // 0
    case 'I':
      if (*Fn != '?') {
        char   *p = (char*)PlugSubAlloc(g, memp, _MAX_PATH);

        PlugSetPath(p, Fn, GetPath());
        Fn = p;
      } else {
        strcpy(g->Message, MSG(MISSING_FNAME));
        return true;
      } // endif Fn

      Ln = Cat->GetSizeCatInfo((char*)Name, "Secsize", "8K");
      break;
    default:
      sprintf(g->Message, MSG(INV_SUBTYPE), buf);
      return true;
    } // endswitch Subtype

  Desc = Fn;
  return false;
  } // end of DefineAM

/***********************************************************************/
/*  GetTable: makes a new TDB of the proper type.                      */
/***********************************************************************/
PTDB INIDEF::GetTable(PGLOBAL g, MODE m)
  {
  PTDBASE tdbp;

  switch (Subtype) {
    case 'I':
      if (Layout == 'C')
        tdbp = new(g) TDBINI(this);
      else
        tdbp = new(g) TDBXIN(this);

      break;
    default:
      return NULL;
    } // endswitch Subtype

  if (Multiple)
    tdbp = new(g) TDBMUL(tdbp);         // No block optimization yet

  return tdbp;
  } // end of GetTable

/***********************************************************************/
/*  DeleteTableFile: Delete INI table files using platform API.        */
/*  SysTable and SysColumn tables are readonly and not erasable.       */
/***********************************************************************/
bool INIDEF::DeleteTableFile(PGLOBAL g)
  {
  char    filename[_MAX_PATH];
  bool    rc;

  // Delete the INI table file if not protected
  if (Subtype == 'I' && !IsReadOnly()) {
    PlugSetPath(filename, Fn, GetPath());
#if defined(WIN32)
    rc = !DeleteFile(filename);
#else    // UNIX
    rc = remove(filename);
#endif   // UNIX
  } else
    rc =true;

  return rc;                                  // Return true if error
  } // end of DeleteTableFile

/* ------------------------------------------------------------------- */

/***********************************************************************/
/*  Implementation of the TDBINI class.                                */
/***********************************************************************/
TDBINI::TDBINI(PINIDEF tdp) : TDBASE(tdp)
  {
  Ifile = tdp->Fn;
  Seclist = NULL;
  Section = NULL;
  Seclen = tdp->Ln;
  N = 0;
  } // end of TDBINI constructor

TDBINI::TDBINI(PTDBINI tdbp) : TDBASE(tdbp)
  {
  Ifile = tdbp->Ifile;
  Seclist = tdbp->Seclist;
  Section = tdbp->Section;
  Seclen = tdbp->Seclen;
  N = tdbp->N;
  } // end of TDBINI copy constructor

// Is this really useful ???
PTDB TDBINI::CopyOne(PTABS t)
  {
  PTDB    tp;
  PINICOL cp1, cp2;
  PGLOBAL g = t->G;

  tp = new(g) TDBINI(this);

  for (cp1 = (PINICOL)Columns; cp1; cp1 = (PINICOL)cp1->GetNext()) {
    cp2 = new(g) INICOL(cp1, tp);  // Make a copy
    NewPointer(t, cp1, cp2);
    } // endfor cp1

  return tp;
  } // end of CopyOne

/***********************************************************************/
/*  Get the section list from the INI file.                            */
/***********************************************************************/
char *TDBINI::GetSeclist(PGLOBAL g)
  {
  if (trace)
    htrc("GetSeclist: Seclist=%p\n", Seclist);
    
  if (!Seclist) {
    // Result will be retrieved from the INI file
    Seclist = (char*)PlugSubAlloc(g, NULL, Seclen);
    GetPrivateProfileSectionNames(Seclist, Seclen, Ifile);
    } // endif Seclist

  return Seclist;
  } // end of GetSeclist

/***********************************************************************/
/*  Allocate INI column description block.                             */
/***********************************************************************/
PCOL TDBINI::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
  {
  return new(g) INICOL(cdp, this, cprec, n);
  } // end of MakeCol

/***********************************************************************/
/*  INI GetMaxSize: returns the number of sections in the INI file.    */
/***********************************************************************/
int TDBINI::GetMaxSize(PGLOBAL g)
  {
  if (MaxSize < 0 && GetSeclist(g)) {
    // Count the number of sections from the section list
    char *p;

    for (MaxSize = 0, p = Seclist; *p; p += (strlen(p) + 1))
      MaxSize++;

    } // endif MaxSize

  return MaxSize;
  } // end of GetMaxSize

/***********************************************************************/
/*  INI Access Method opening routine.                                 */
/***********************************************************************/
bool TDBINI::OpenDB(PGLOBAL g)
  {
  PINICOL colp;

  if (Use == USE_OPEN) {
    if (To_Kindex)
      /*****************************************************************/
      /*  Table is to be accessed through a sorted index table.        */
      /*****************************************************************/
      To_Kindex->Reset();

    Section = NULL;
    N = 0;
    return false;
    } // endif use

  /*********************************************************************/
  /*  OpenDB: initialize the INI file processing.                      */
  /*********************************************************************/
  GetSeclist(g);
  Use = USE_OPEN;       // Do it now in case we are recursively called

  /*********************************************************************/
  /*  Allocate the buffers that will contain key values.               */
  /*********************************************************************/
  for (colp = (PINICOL)Columns; colp; colp = (PINICOL)colp->GetNext())
    if (!colp->IsSpecial())            // Not a pseudo column
      colp->AllocBuf(g);

  if (trace)
    htrc("INI OpenDB: seclist=%s seclen=%d ifile=%s\n", 
          Seclist, Seclen, Ifile);

  return false;
  } // end of OpenDB

/***********************************************************************/
/*  Data Base read routine for INI access method.                      */
/***********************************************************************/
int TDBINI::ReadDB(PGLOBAL g)
  {
  /*********************************************************************/
  /*  Now start the pseudo reading process.                            */
  /*********************************************************************/
  if (To_Kindex) {
    /*******************************************************************/
    /*  Reading is by an index table.                                  */
    /*******************************************************************/
    int recpos = To_Kindex->Fetch(g);

    switch (recpos) {
      case -1:           // End of file reached
        return RC_EF;
      case -2:           // No match for join
        return RC_NF;
      case -3:           // Same record as last non null one
        return RC_OK;
      default:
        Section = (char*)recpos;
      } // endswitch recpos

  } else {
    if (!Section)
      Section = Seclist;
    else
      Section += (strlen(Section) + 1);

    if (trace > 1)
      htrc("INI ReadDB: section=%s N=%d\n", Section, N);

    N++;
  } // endif To_Kindex

  return (*Section) ? RC_OK : RC_EF;
  } // end of ReadDB

/***********************************************************************/
/*  WriteDB: Data Base write routine for INI access methods.           */
/***********************************************************************/
int TDBINI::WriteDB(PGLOBAL g)
  {
  // This is to check that section name was given when inserting
  if (Mode == MODE_INSERT)
    Section = NULL;

  // Nothing else to do because all was done in WriteColumn
  return RC_OK;
  } // end of WriteDB

/***********************************************************************/
/*  Data Base delete line routine for INI access methods.              */
/***********************************************************************/
int TDBINI::DeleteDB(PGLOBAL g, int irc)
  {
  switch (irc) {
    case RC_EF:
      break;
    case RC_FX:
      while (ReadDB(g) == RC_OK)
        WritePrivateProfileString(Section, NULL, NULL, Ifile);

      break;
    default:
      if (!Section) {
        strcpy(g->Message, MSG(NO_SECTION_NAME));
        return RC_FX;
      } else
        WritePrivateProfileString(Section, NULL, NULL, Ifile);

    } // endswitch irc

  return RC_OK;
  } // end of DeleteDB

/***********************************************************************/
/*  Data Base close routine for INI access methods.                    */
/***********************************************************************/
void TDBINI::CloseDB(PGLOBAL g)
  {
  // Nothing to do
  } // end of CloseDB

// ------------------------ INICOL functions ----------------------------

/***********************************************************************/
/*  INICOL public constructor.                                         */
/***********************************************************************/
INICOL::INICOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am)
  : COLBLK(cdp, tdbp, i)
  {
  if (cprec) {
    Next = cprec->GetNext();
    cprec->SetNext(this);
  } else {
    Next = tdbp->GetColumns();
    tdbp->SetColumns(this);
  } // endif cprec

  // Set additional INI access method information for column.
  Valbuf = NULL;
  Flag = cdp->GetOffset();
  Long = cdp->GetLong();
  To_Val = NULL;
  } // end of INICOL constructor

/***********************************************************************/
/*  INICOL constructor used for copying columns.                       */
/*  tdbp is the pointer to the new table descriptor.                   */
/***********************************************************************/
INICOL::INICOL(INICOL *col1, PTDB tdbp) : COLBLK(col1, tdbp)
  {
  Valbuf = col1->Valbuf;
  Flag = col1->Flag;
  Long = col1->Long;
  To_Val = col1->To_Val;
  } // end of INICOL copy constructor

/***********************************************************************/
/*  Allocate a buffer of the proper size.                              */
/***********************************************************************/
void INICOL::AllocBuf(PGLOBAL g)
  {
  if (!Valbuf)
    Valbuf = (char*)PlugSubAlloc(g, NULL, Long + 1);

  } // end of AllocBuf

/***********************************************************************/
/*  SetBuffer: prepare a column block for write operation.             */
/***********************************************************************/
bool INICOL::SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check)
  {
  if (!(To_Val = value)) {
    sprintf(g->Message, MSG(VALUE_ERROR), Name);
    return true;
  } else if (Buf_Type == value->GetType()) {
    // Values are of the (good) column type
    if (Buf_Type == TYPE_DATE) {
      // If any of the date values is formatted
      // output format must be set for the receiving table
      if (GetDomain() || ((DTVAL *)value)->IsFormatted())
        goto newval;          // This will make a new value;

    } else if (Buf_Type == TYPE_FLOAT)
      // Float values must be written with the correct (column) precision
      // Note: maybe this should be forced by ShowValue instead of this ?
      value->SetPrec(GetPrecision());

    Value = value;            // Directly access the external value
  } else {
    // Values are not of the (good) column type
    if (check) {
      sprintf(g->Message, MSG(TYPE_VALUE_ERR), Name,
              GetTypeName(Buf_Type), GetTypeName(value->GetType()));
      return true;
      } // endif check

 newval:
    if (InitValue(g))         // Allocate the matching value block
      return true;

  } // endif's Value, Buf_Type

  // Allocate the internal value buffer
  AllocBuf(g);

  // Because Colblk's have been made from a copy of the original TDB in
  // case of Update, we must reset them to point to the original one.
  if (To_Tdb->GetOrig())
    To_Tdb = (PTDB)To_Tdb->GetOrig();

  // Set the Column
  Status = (ok) ? BUF_EMPTY : BUF_NO;
  return false;
  } // end of SetBuffer

/***********************************************************************/
/*  ReadColumn: what this routine does is to access the key buffer set */
/*  from the corresponding section, extract from it the key value      */
/*  corresponding to this column name and convert it to buffer type.   */
/***********************************************************************/
void INICOL::ReadColumn(PGLOBAL g)
  {
  PTDBINI tdbp = (PTDBINI)To_Tdb;

  if (trace > 1)
    htrc("INI ReadColumn: col %s R%d flag=%d\n",
          Name, tdbp->GetTdb_No(), Flag);

  /*********************************************************************/
  /*  Get the key value from the INI file.                             */
  /*********************************************************************/
  switch (Flag) {
    case 1:
      strncpy(Valbuf, tdbp->Section, Long);              // Section name
      Valbuf[Long] = '\0';
      break;
    default:
      GetPrivateProfileString(tdbp->Section, Name, "�",
                                        Valbuf, Long + 1, tdbp->Ifile);
      break;
    } // endswitch Flag

  // Missing keys are interpreted as null values
  if (!strcmp(Valbuf, "�")) {
    if (Nullable)
      Value->SetNull(true);

    Value->Reset();              // Null value
  } else
    Value->SetValue_psz(Valbuf);

  } // end of ReadColumn

/***********************************************************************/
/*  WriteColumn: what this routine does is to access the last line     */
/*  read from the corresponding table, and rewrite the field           */
/*  corresponding to this column from the column buffer and type.      */
/***********************************************************************/
void INICOL::WriteColumn(PGLOBAL g)
  {
  char   *p;
  PTDBINI tdbp = (PTDBINI)To_Tdb;

  if (trace > 1)
    htrc("INI WriteColumn: col %s R%d coluse=%.4X status=%.4X\n",
          Name, tdbp->GetTdb_No(), ColUse, Status);

  /*********************************************************************/
  /*  Get the string representation of Value according to column type. */
  /*********************************************************************/
  if (Value != To_Val)
    Value->SetValue_pval(To_Val, false);    // Convert the updated value

  // Null key are missing keys
  if (Value->IsNull())
    return;

  p = Value->GetCharString(Valbuf);

  if (strlen(p) > (unsigned)Long) {
    sprintf(g->Message, MSG(VALUE_TOO_LONG), p, Name, Long);
    longjmp(g->jumper[g->jump_level], 31);
  } else if (Flag == 1) {
    if (tdbp->Mode == MODE_UPDATE) {
      strcpy(g->Message, MSG(NO_SEC_UPDATE));
      longjmp(g->jumper[g->jump_level], 31);
    } else {
      tdbp->Section = p;
      return;
    } // endif Mode

  } else if (!tdbp->Section) {
    strcpy(g->Message, MSG(SEC_NAME_FIRST));
    longjmp(g->jumper[g->jump_level], 31);
  } // endif's

  /*********************************************************************/
  /*  Updating must be done only when not in checking pass.            */
  /*********************************************************************/
  if (Status)
    WritePrivateProfileString(tdbp->Section, Name, p, tdbp->Ifile);

  } // end of WriteColumn

/* ------------------------------------------------------------------- */

/***********************************************************************/
/*  Implementation of the TDBXIN class.                                */
/***********************************************************************/
TDBXIN::TDBXIN(PINIDEF tdp) : TDBINI(tdp)
  {
  Keylist = NULL;
  Keycur = NULL;
  Keylen = Seclen;
  Oldsec = -1;
  } // end of TDBXIN constructor

TDBXIN::TDBXIN(PTDBXIN tdbp) : TDBINI(tdbp)
  {
  Keylist = tdbp->Keylist;
  Keycur = tdbp->Keycur;
  Keylen = tdbp->Keylen;
  Oldsec = tdbp->Oldsec;
  } // end of TDBXIN copy constructor

// Is this really useful ???
PTDB TDBXIN::CopyOne(PTABS t)
  {
  PTDB    tp;
  PXINCOL cp1, cp2;
  PGLOBAL g = t->G;

  tp = new(g) TDBXIN(this);

  for (cp1 = (PXINCOL)Columns; cp1; cp1 = (PXINCOL)cp1->GetNext()) {
    cp2 = new(g) XINCOL(cp1, tp);  // Make a copy
    NewPointer(t, cp1, cp2);
    } // endfor cp1

  return tp;
  } // end of CopyOne

/***********************************************************************/
/*  Get the key list from the INI file.                                */
/***********************************************************************/
char *TDBXIN::GetKeylist(PGLOBAL g, char *sec)
  {
  if (!Keylist)
    Keylist = (char*)PlugSubAlloc(g, NULL, Keylen);

  GetPrivateProfileString(sec, NULL, "", Keylist, Keylen, Ifile);
  return Keylist;
  } // end of GetKeylist

/***********************************************************************/
/*  Allocate XIN column description block.                             */
/***********************************************************************/
PCOL TDBXIN::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
  {
  return new(g) XINCOL(cdp, this, cprec, n);
  } // end of MakeCol

/***********************************************************************/
/*  XIN GetMaxSize: returns the number of sections in the XIN file.    */
/***********************************************************************/
int TDBXIN::GetMaxSize(PGLOBAL g)
  {
  if (MaxSize < 0 && GetSeclist(g)) {
    // Count the number of keys from the section list
    char *p, *k;

    for (MaxSize = 0, p = Seclist; *p; p += (strlen(p) + 1))
      for (k = GetKeylist(g, p); *k; k += (strlen(k) + 1))
        MaxSize++;

    } // endif MaxSize

  return MaxSize;
  } // end of GetMaxSize

/***********************************************************************/
/*  Record position is Section+Key.                                    */
/***********************************************************************/
int TDBXIN::GetRecpos(void)
  {
  union {
    short X[2];                              // Section and Key offsets
    int  Xpos;                              // File position
    }; // end of union

  X[0] = (short)(Section - Seclist);
  X[1] = (short)(Keycur - Keylist);
  return Xpos;
  } // end of GetRecpos

/***********************************************************************/
/*  Record position is Section+Key.                                    */
/***********************************************************************/
bool TDBXIN::SetRecpos(PGLOBAL g, int recpos)
  {
  union {
    short X[2];                              // Section and Key offsets
    int  Xpos;                              // File position
    }; // end of union

  Xpos = recpos;

  if (X[0] != Oldsec) {
    Section = Seclist + X[0];
    Keycur = GetKeylist(g, Section) + X[1];
    Oldsec = X[0];
  } else
    Keycur = Keylist + X[1];

  return false;
  } // end of SetRecpos

/***********************************************************************/
/*  XIN Access Method opening routine.                                 */
/***********************************************************************/
bool TDBXIN::OpenDB(PGLOBAL g)
  {
  Oldsec = -1;       // To replace the table at its beginning
  return TDBINI::OpenDB(g);
  } // end of OpenDB

/***********************************************************************/
/*  Data Base read routine for XIN access method.                      */
/***********************************************************************/
int TDBXIN::ReadDB(PGLOBAL g)
  {
  /*********************************************************************/
  /*  Now start the pseudo reading process.                            */
  /*********************************************************************/
  if (To_Kindex) {
    /*******************************************************************/
    /*  Reading is by an index table.                                  */
    /*******************************************************************/
    int recpos = To_Kindex->Fetch(g);

    switch (recpos) {
      case -1:           // End of file reached
        return RC_EF;
      case -2:           // No match for join
        return RC_NF;
      case -3:           // Same record as last non null one
        return RC_OK;
      default:
        SetRecpos(g, recpos);
      } // endswitch recpos

  } else {
    do {
      if (!Keycur || !*Keycur) {
        if (!Section)
          Section = Seclist;
        else
          Section += (strlen(Section) + 1);

        if (*Section)
          Keycur = GetKeylist(g, Section);
        else
          return RC_EF;

      } else
        Keycur += (strlen(Keycur) + 1);

      } while (!*Keycur);

    N++;
  } // endif To_Kindex

  return RC_OK;
  } // end of ReadDB

/***********************************************************************/
/*  WriteDB: Data Base write routine for XIN access methods.           */
/***********************************************************************/
int TDBXIN::WriteDB(PGLOBAL g)
  {
  // To check that section and key names were given when inserting
  if (Mode == MODE_INSERT) {
    Section = NULL;
    Keycur = NULL;
    } // endif Mode

  // Nothing else to do because all was done in WriteColumn
  return RC_OK;
  } // end of WriteDB

/***********************************************************************/
/*  Data Base delete line routine for XIN access methods.              */
/***********************************************************************/
int TDBXIN::DeleteDB(PGLOBAL g, int irc)
  {
  if (irc == RC_EF) {
  } else if (irc == RC_FX) {
    for (Section = Seclist; *Section; Section += (strlen(Section) + 1))
      WritePrivateProfileString(Section, NULL, NULL, Ifile);

  } else if (Section) {
    WritePrivateProfileString(Section, Keycur, NULL, Ifile);
  } else {
    strcpy(g->Message, MSG(NO_SECTION_NAME));
    return RC_FX;
  } // endif's

  return RC_OK;
  } // end of DeleteDB

// ------------------------ XINCOL functions ----------------------------

/***********************************************************************/
/*  XINCOL public constructor.                                         */
/***********************************************************************/
XINCOL::XINCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am)
      : INICOL(cdp, tdbp, cprec, i, am)
  {
  } // end of XINCOL constructor

/***********************************************************************/
/*  XINCOL constructor used for copying columns.                       */
/*  tdbp is the pointer to the new table descriptor.                   */
/***********************************************************************/
XINCOL::XINCOL(XINCOL *col1, PTDB tdbp) : INICOL(col1, tdbp)
  {
  } // end of XINCOL copy constructor

/***********************************************************************/
/*  ReadColumn: what this routine does is to access the key buffer set */
/*  from the corresponding section, extract from it the key value      */
/*  corresponding to this column name and convert it to buffer type.   */
/***********************************************************************/
void XINCOL::ReadColumn(PGLOBAL g)
  {
  PTDBXIN tdbp = (PTDBXIN)To_Tdb;

  /*********************************************************************/
  /*  Get the key value from the XIN file.                             */
  /*********************************************************************/
  switch (Flag) {
    case 1:
      strncpy(Valbuf, tdbp->Section, Long);              // Section name
      Valbuf[Long] = '\0';
      break;
    case 2:
      strncpy(Valbuf, tdbp->Keycur, Long);               // Key name
      Valbuf[Long] = '\0';
      break;
    default:
      GetPrivateProfileString(tdbp->Section, tdbp->Keycur, "",
                                        Valbuf, Long + 1, tdbp->Ifile);
      break;
    } // endswitch Flag

  Value->SetValue_psz(Valbuf);
  } // end of ReadColumn

/***********************************************************************/
/*  WriteColumn: what this routine does is to access the last line     */
/*  read from the corresponding table, and rewrite the field           */
/*  corresponding to this column from the column buffer and type.      */
/***********************************************************************/
void XINCOL::WriteColumn(PGLOBAL g)
  {
  char   *p;
  PTDBXIN tdbp = (PTDBXIN)To_Tdb;

  if (trace > 1)
    htrc("XIN WriteColumn: col %s R%d coluse=%.4X status=%.4X\n",
          Name, tdbp->GetTdb_No(), ColUse, Status);

  /*********************************************************************/
  /*  Get the string representation of Value according to column type. */
  /*********************************************************************/
  if (Value != To_Val)
    Value->SetValue_pval(To_Val, false);    // Convert the updated value

  p = Value->GetCharString(Valbuf);

  if (strlen(p) > (unsigned)Long) {
    sprintf(g->Message, MSG(VALUE_TOO_LONG), p, Name, Long);
    longjmp(g->jumper[g->jump_level], 31);
  } else if (Flag == 1) {
    if (tdbp->Mode == MODE_UPDATE) {
      strcpy(g->Message, MSG(NO_SEC_UPDATE));
      longjmp(g->jumper[g->jump_level], 31);
    } else {
      tdbp->Section = p;
      return;
    } // endif Mode

  } else if (Flag == 2) {
    if (tdbp->Mode == MODE_UPDATE) {
      strcpy(g->Message, MSG(NO_KEY_UPDATE));
      longjmp(g->jumper[g->jump_level], 31);
    } else {
      tdbp->Keycur = p;
      return;
    } // endif Mode

  } else if (!tdbp->Section || !tdbp->Keycur) {
    strcpy(g->Message, MSG(SEC_KEY_FIRST));
    longjmp(g->jumper[g->jump_level], 31);
  } // endif's

  /*********************************************************************/
  /*  Updating must be done only when not in checking pass.            */
  /*********************************************************************/
  if (Status)
    WritePrivateProfileString(tdbp->Section, tdbp->Keycur, p, tdbp->Ifile);

  } // end of WriteColumn

/* ------------------------ End of System ---------------------------- */