/* c4code.c   (c)Copyright Sequiter Software Inc., 1990-1994.  All rights reserved. */

#include "d4all.h"

#ifdef S4TESTING
#include <fcntl.h>
#include <sys\types.h>
#include <sys\stat.h>
#endif

#ifndef S4UNIX
#ifdef __TURBOC__
#pragma hdrstop
#ifndef __DLL__
#ifndef S4OS2PM
extern unsigned _stklen ;
#endif
#endif
#endif

#ifdef _MSC_VER
#ifndef __DLL__
#include <malloc.h>
#endif
#endif
#endif

#ifdef S4CBPP
#ifdef __cplusplus
#include "d4data.hpp"
#endif
#endif

#ifdef S4TESTING
int s4test_handle ;
#endif

char f4memo_null_char = '\0' ;
char  *expr4buf = 0 ;

#ifdef S4OS2
#ifdef S4OS2SEM
#include <time.h>

static char sem4mem[20], sem4expr[20] ;
static HMTX hmtx4mem, hmtx4expr ;
#endif
#endif

#ifdef __DLL__

#ifdef S4PASCAL
typedef char far* LPSTR ;
typedef unsigned int HANDLE ;
typedef unsigned short WORD ;
#define PASCAL _pascal
#define S4USE_WEP
#endif

#ifdef S4WINDOWS
#ifdef _MSC_VER
#if  _MSC_VER != 800
#define S4USE_WEP
#endif
#else
#define S4USE_WEP
#endif
HINSTANCE cb5inst = NULL;
#endif

#ifdef S4OS2
ULONG _dllmain (ULONG termflag, HMODULE modhandle)
{
#ifdef S4OS2SEM
  int i ;
  APIRET rc ;
  time_t t ;
#endif

  if ( termflag == 0 )
  {
    mem4init() ;
#ifdef S4OS2SEM
    strcpy( sem4expr, "\\SEM32\\S4A" ) ;
    strcpy( sem4mem, "\\SEM32\\S4B" ) ;
    for ( i = 0 ; i < 100 ; i++ )
    {
      u4delay_sec() ;
      time( &t ) ;
      t %= 10000L ;

      c4ltoa45( t, sem4expr + 10, -4 ) ;
      c4ltoa45( t, sem4mem + 10, -4 ) ;

      rc = DosCreateMutexSem( sem4expr, &hmtx4expr, 0, 0 ) ;
      if ( rc != 0 )
        continue ;

      rc = DosCreateMutexSem( sem4mem, &hmtx4mem, 0, 0 ) ;
      if ( rc != 0 )
      {
        DosCloseMutexSem( hmtx4expr ) ;
        continue ;
      }
      return 1 ;
    }
#else
    return 1 ;
#endif
  }
  else
  {
    mem4reset() ;
#ifdef S4OS2SEM
    DosCloseMutexSem( hmtx4mem ) ;
    DosCloseMutexSem( hmtx4expr ) ;
#endif
  }

  return 1;
}
#endif

#ifdef S4PASCAL_DOS
int far PASCAL LibMain( HANDLE hInstance, WORD wDataSeg, WORD cbHeapSize, LPSTR lpCmdLine )
{
  mem4init() ;
  return 1 ;
}
#endif

#ifdef S4WINDOWS
#ifndef S4PASCAL_DOS
#pragma argsused
int far PASCAL LibMain( HINSTANCE hInstance, WORD wDataSeg, WORD cbHeapSize, LPSTR lpCmdLine )
{
  if( !cb5inst )
    cb5inst = hInstance;

  mem4init() ;
  return 1 ;
}

HINSTANCE CALLBACK c4dll_inst( void )
{
  return cb5inst;
}
#endif   /* S4PASCAL_DOS */
#endif   /* S4WINDOWS */

#ifdef S4USE_WEP
#pragma argsused
int S4FUNCTION WEP( int nParameter )
{
  mem4reset() ;
  return 1 ;
}
#endif

#endif  /* __DLL__ */

void S4FUNCTION d4init( CODE4 *c4 )
{
#ifdef S4OS2SEM
#ifdef S4OS2
#ifndef __DLL__
  time_t t ;
  int i ;
  APIRET rc ;
#endif
#endif
#endif

#ifdef S4TESTING
  int oflag ;
#endif

  if ( c4 == 0 )
#ifdef S4DEBUG
    e4severe( e4parm, E4_D4INIT ) ;
#else
  return ;
#endif

#ifdef S4WINDOWS
  SetHandleCount(40);
#endif

  memset( (void *)c4, 0, sizeof( CODE4 ) ) ;

  /* if a DLL, can't check the stack length since this is a separate executable */
#ifndef S4WIN32
#ifndef S4WINDOWS
#ifndef __DLL__
#ifdef __TURBOC__
#ifndef S4OS2PM
  if ( _stklen < 5000U ) /* 5000 seems to be an appropriate minimum */
#ifdef S4DEBUG
    e4severe( e4result, E4_RESULT_STC ) ;
#else
  e4( c4, e4result, E4_RESULT_STC ) ;
#endif
#endif
#endif
#endif
#ifdef _MSC_VER
  if ( stackavail() < 5000U )
#ifdef S4DEBUG
    e4severe( e4result, E4_RESULT_STC ) ;
#else
  e4( c4, e4result, E4_RESULT_STC ) ;
#endif
#endif
#endif
#endif

  c4->debug_int = 0x5281 ; /* Some random value for double checking. */
  c4->mem_size_block       = 0x400 ;   /* 1024 */
  c4->mem_size_sort_pool   = 0xF000 ;  /* 61440 */
  c4->mem_size_sort_buffer = 0x1000 ;  /* 4096 */
  c4->mem_size_buffer      = 0x4000 ;  /* 16384 */
  c4->mem_size_memo        =  0x200 ;  /*   512 */
  c4->mem_size_memo_expr   =  0x400 ;  /*  1024 */

  c4->default_unique_error = r4unique_continue ;

#ifndef S4LANGUAGE
  u4ncpy( c4->date_format, "MM/DD/YY", sizeof(c4->date_format) ) ;
#else
#ifdef S4GERMAN
  u4ncpy( c4->date_format, "DD.MM.YY", sizeof(c4->date_format) ) ;
#endif
#ifdef S4FRENCH
  u4ncpy( c4->date_format, "MM/DD/YY", sizeof(c4->date_format) ) ;
#endif
#ifdef S4SWEDISH
  u4ncpy( c4->date_format, "YYYY-MM-DD", sizeof(c4->date_format) ) ;
#endif
#ifdef S4FINNISH
  u4ncpy( c4->date_format, "YYYY-MM-DD", sizeof(c4->date_format) ) ;
#endif
#ifdef S4NORWEGIAN
  u4ncpy( c4->date_format, "DD-MM-YYYY", sizeof(c4->date_format) ) ;
#endif
#endif

#ifdef S4CLIPPER
  c4->numeric_str_len  = 17 ;    /* default length for clipper numeric keys is 10  */
  c4->decimals         = 2 ;
#endif  /* S4CLIPPER */

  /* Flags initialization */
  c4->go_error = c4->open_error = c4->create_error =
    c4->tag_name_error = c4->auto_open = c4->field_name_error =
      c4->safety = c4->skip_error = c4->expr_error = 1 ;
  c4->lock_attempts = -1 ;   /* wait forever */

  c4->mem_start_index = c4->mem_expand_index =
    c4->mem_expand_data  = c4->mem_start_data = 5 ;
  c4->mem_start_block = c4->mem_expand_block =
    c4->mem_start_tag    = c4->mem_expand_tag = 10 ;

  c4->relate_error = 1 ;
  c4->do_index_verify = 1 ;

#ifndef S4OPTIMIZE_OFF
  c4->do_opt = 1 ;   /* by default do optimization */
  c4->optimize = -1 ;   /* by default optimize non-shared files */
  c4->optimize_write = -1 ;
#ifdef S4OS2
  c4->mem_start_max = 0xF0000L ;
#else
#ifdef S4UNIX
  c4->mem_start_max = 0xF0000L ;
#else
#ifdef S4WINDOWS
  c4->mem_start_max = 0xF0000L ;
#else
  c4->mem_start_max = 0x50000L ;
#endif  /* S4WINDOWS */
#endif
#endif
#endif  /* not S4OPTIMIZE_OFF */

  /* set up the semaphores */
#ifdef S4OS2SEM
#ifdef S4OS2
#ifndef __DLL__
  /* create new ones */
  strcpy( sem4expr, "\\SEM32\\S4A" ) ;
  strcpy( sem4mem, "\\SEM32\\S4B" ) ;
  for ( i = 0 ; i < 100 ; i++ )
  {
    u4delay_sec() ;
    time( &t ) ;
    t %= 10000L ;

    c4ltoa45( t, sem4expr + 10, -4 ) ;
    c4ltoa45( t, sem4mem + 10, -4 ) ;

    rc = DosCreateMutexSem( sem4expr, &hmtx4expr, 0, 0 ) ;
    if ( rc != 0 )
      continue ;

    rc = DosCreateMutexSem( sem4mem, &hmtx4mem, 0, 0 ) ;
    if ( rc != 0 )
    {
      DosCloseMutexSem( hmtx4expr ) ;
      continue ;
    }
    break ;
  }
#endif
  /* make sure created and can open... assign to code_base pointer */
  if ( DosOpenMutexSem( sem4expr, &c4->hmtx_expr ) != 0 )
    e4( c4, e4info, "OS/2 Semaphore open failed" ) ;
  if ( DosOpenMutexSem( sem4mem, &c4->hmtx_mem ) != 0 )
    e4( c4, e4info, "OS/2 Semaphore open failed" ) ;
#endif
#endif

#ifdef S4TESTING
  oflag = O_APPEND | O_CREAT | O_RDWR | O_TEXT ;
  s4test_handle = open("S4TEST.ERR", oflag, S_IWRITE ) ;
#endif
}

/* if do_init is set to 1, d4init() is called on the allocated CODE4 */
CODE4 *S4FUNCTION code4alloc( int do_init )
{
  CODE4 *c4 ;
#ifndef S4CBPP
  c4 = (CODE4 *)u4alloc( sizeof( CODE4 ) ) ;
#else
#ifdef __cplusplus
  c4 = (CODE4 *)u4alloc( sizeof( Code4 ) ) ;
#else
  c4 = (CODE4 *)u4alloc( sizeof( CODE4 ) ) ;
#endif
#endif

  if ( c4 == 0 )
    return 0 ;

  if ( do_init == 1 )
    d4init( c4 ) ;

  return c4 ;
}

int S4FUNCTION d4init_undo( CODE4 *c4 )
{
  if ( c4 == 0 )
#ifdef S4DEBUG
    e4severe( e4parm, E4_D4INIT_UNDO ) ;
#else
  return -1 ;
#endif

#ifdef S4OS2
#ifdef S4OS2SEM
  DosCloseMutexSem( hmtx4mem ) ;
  DosCloseMutexSem( hmtx4expr ) ;
#endif
#endif

#ifndef S4OPTIMIZE_OFF
  d4opt_suspend( c4 ) ;
#endif
  d4close_all( c4 ) ;

  mem4release( c4->index_memory ) ;
  c4->index_memory = 0 ;

  mem4release( c4->bitmap_memory ) ;
  c4->bitmap_memory = 0 ;

  mem4release( c4->data_memory ) ;
  c4->data_memory = 0 ;

  mem4release( c4->tag_memory ) ;
  c4->tag_memory = 0 ;

  mem4release( c4->bitmap_memory ) ;
  c4->bitmap_memory = 0 ;

  u4free( c4->expr_work_buf ) ;
  c4->expr_work_buf = 0 ;
  c4->expr_buf_len = 0 ;

  u4free( c4->field_buffer ) ;
  c4->field_buffer = 0 ;
  c4->buf_len = 0 ;

  u4free ( c4->stored_key ) ;
  c4->stored_key = 0 ;
  c4->stored_key_len = 0 ;

  return c4->error_code ;
}

int S4FUNCTION d4close_all( CODE4 *c4 )
{
  DATA4 *data_on, *data_next ;
  int rc ;

#ifdef S4VBASIC
  if ( c4parm_check( c4, 1, E4_D4CLOSE_ALL ) )
    return -1 ;
#endif  /* S4VBASIC */

  if ( c4 == 0 )
#ifdef S4DEBUG
    e4severe( e4parm, E4_D4CLOSE_ALL ) ;
#else
  return -1 ;
#endif

  rc = 0 ;

  for ( data_next = (DATA4 *)l4first( &c4->data_list ) ; ; )
  {
    data_on = data_next ;
    if ( !data_on )
      break ;
    data_next = (DATA4 *)l4next( &c4->data_list, data_next ) ;

    if ( d4close( data_on ) < 0 )
      rc = -1 ;
  }

  if ( c4->error_code < 0 )
    return -1 ;
  return rc ;
}

DATA4 *S4FUNCTION d4data( CODE4 *c4, char *alias_name )
{
  char buf[12] ;
  DATA4 *data_on ;
#ifdef S4DEBUG
  DATA4 *data_result ;
#endif

#ifdef S4VBASIC
  if ( c4parm_check( c4, 1, E4_D4DATA ) )
    return 0 ;
#endif  /* S4VBASIC */

  if ( c4 == 0 || alias_name == 0 )
#ifdef S4DEBUG
    e4severe( e4parm, E4_D4DATA ) ;
#else
  return 0 ;
#endif

  u4ncpy( buf, alias_name, sizeof( buf ) ) ;
#ifndef S4UNIX
  c4upper( buf ) ;
#endif

  data_on = 0 ;
#ifdef S4DEBUG
  data_result = 0 ;
#endif

  for(;;)
  {
    data_on = (DATA4 *)l4next( &c4->data_list, data_on ) ;
    if ( !data_on )
      break ;

    if ( strcmp( buf, data_on->alias ) == 0 )
    {
#ifdef S4DEBUG
      if ( data_result != 0 )
        e4severe( e4info, E4_INFO_DUP ) ;
      data_result = data_on ;
#else
      return data_on ;
#endif  /* S4DEBUG */
    }
  }

#ifdef S4DEBUG
  return data_result ;
#else
  return data_on ;
#endif
}

#ifdef S4FOX
#define S4FORMAT 1
#endif

#ifdef S4CLIPPER
#ifdef S4FORMAT
error Choose only one CodeBase index file compatibility option.
#endif
#define S4FORMAT 2
#endif

#ifdef S4MDX
#ifdef S4FORMAT
error Choose only one CodeBase index file compatibility option.
#endif
#define S4FORMAT 4
#endif

#ifdef S4NDX
#ifdef S4FORMAT
error Choose only one CodeBase index file compatibility option.
#endif
#define S4FORMAT 8
#endif

#ifndef S4FORMAT
error You must define either S4FOX, S4CLIPPER, S4NDX or S4MDX
#endif

#ifdef S4DLL_BUILD
#define S4OPERATING 0x10
#endif

#ifdef S4WINDOWS
#undef S4OPERATING
#define S4OPERATING 0x20
#endif

#ifdef S4OS2
#ifdef S4OPERATING
error Choose one of CodeBase switches S4DLL/S4WINDOWS, S4OS2, and S4CODE_SCREENS
#endif
#define S4OPERATING 0x40
#endif

#ifdef S4CODE_SCREENS
#ifdef S4OPERATING
error Choose one of CodeBase switches S4DLL/S4WINDOWS, S4OS2, and S4CODE_SCREENS
#endif
#define S4OPERATING 0x80
#endif

#ifndef S4OPERATING
#define S4OPERATING 0
#endif

#ifdef S4DEBUG
#define S4DEBUG_VAL  0x100
#else
#define S4DEBUG_VAL  0
#endif

#ifdef S4ERROR_HOOK
#define S4ERROR_HOOK_VAL 0x200
#else
#define S4ERROR_HOOK_VAL 0
#endif

#ifdef S4LOCK_CHECK
#define S4LOCK_CHECK_VAL 0x400
#else
#define S4LOCK_CHECK_VAL 0
#endif

#ifdef S4LOCK_HOOK
#define S4LOCK_HOOK_VAL 0x800
#else
#define S4LOCK_HOOK_VAL 0
#endif

#ifdef S4MAX
#define S4MAX_VAL 0x1000
#else
#define S4MAX_VAL 0
#endif

#ifdef S4MEMO_OFF
#define S4MEMO_OFF_VAL 0x2000
#else
#define S4MEMO_OFF_VAL 0
#endif

#ifdef S4OLD_CODE
#define S4OLD_CODE_VAL 0x4000
#else
#define S4OLD_CODE_VAL 0
#endif

#ifdef S4OPTIMIZE_OFF
#define S4OPTIMIZE_OFF_VAL 0x8000
#else
#define S4OPTIMIZE_OFF_VAL 0
#endif

#ifdef S4SAFE
#define S4SAFE_VAL 0x20000
#else
#define S4SAFE_VAL 0
#endif

#ifdef S4SINGLE
#define S4SINGLE_VAL 0x40000
#else
#define S4SINGLE_VAL 0
#endif

#if S4VERSION != 5104
error Your CodeBase source version does not match your header file version.
#endif

long S4FUNCTION u4switch()
{
  return (long) ( S4FORMAT + S4OPERATING + S4DEBUG_VAL + S4ERROR_HOOK_VAL +
                 S4LOCK_CHECK_VAL + S4LOCK_HOOK_VAL + S4MAX_VAL +
                 S4MEMO_OFF_VAL + S4OLD_CODE_VAL + S4OPTIMIZE_OFF_VAL +
                 S4SAFE_VAL + S4SINGLE_VAL ) ;
}

#ifdef S4VB_DOS

DATA4 *d4data_v( CODE4 *c4, char *alias )
{
  return d4data( c4, c4str(alias) ) ;
}

#endif

#ifdef S4DLL_BUILD
#ifndef S4PASCAL_DOS

/*Structures for CodeControls Functions */

typedef   struct ctrl4code_tag
{
  LINK4       link;
  HINSTANCE   hInst;
  CODE4       *code;
  int         alloc;
  LIST4       form;
}CTRL4CODE;

/***************************************************************\
  \***************************************************************/

int         S4FUNCTION ctrl4addCode(HINSTANCE hInst );                                 //450
void        S4FUNCTION ctrl4codeListInit(void);                                        //451
void        S4FUNCTION ctrl4freeCtrlNode(CTRL4CODE *node);
void        S4FUNCTION ctrl4freeCodeList(void);                                        //452
CTRL4CODE * S4FUNCTION ctrl4getCtrlCode(HINSTANCE hInst);                              //453
void        S4FUNCTION ctrl4initVBX(CODE4 *code,HINSTANCE hInstance,int initialize);   //454
void        S4FUNCTION ctrl4initVBXUndo(CODE4 *code,HINSTANCE hInstance);              //455

/***************************************************************\
 *  List Containing CODE4 and hInstance
 *  structures for CodeControls
 \***************************************************************/

LIST4    ctrl4codeListVBX;


/***************************************************************\
  \***************************************************************/

void S4FUNCTION ctrl4codeListInit(void)
{
  //memset(&ctrl4codeListVBX,0,sizeof(LIST4));
}

/***************************************************************\
  \***************************************************************/

int S4FUNCTION ctrl4addCode(HINSTANCE hInst )
{
  CTRL4CODE   *node;

  if(!hInst)
    return -1;

  node = (CTRL4CODE *) u4alloc(sizeof(CTRL4CODE));
  if(node)
  {
    node->hInst = hInst;
    node->alloc = 1;
    node->code = NULL;
    l4add(&ctrl4codeListVBX,node);
  }
  return 0;
}

/***************************************************************\
  \***************************************************************/

void S4FUNCTION ctrl4freeCtrlNode(CTRL4CODE *node)
{
  if(node)
  {
    l4remove(&ctrl4codeListVBX,node);
    if(node->code)
    {
      d4init_undo(node->code);
      u4free(node->code);
    }
    u4free(node);
  }
}

/***************************************************************\
  \***************************************************************/

void S4FUNCTION ctrl4freeCodeList(void)
{
  CTRL4CODE   *node;

  for(node = (CTRL4CODE *) l4first(&ctrl4codeListVBX);node != NULL;)
  {
    l4remove(&ctrl4codeListVBX,node);
    if(node->code && node->alloc)
    {
      d4init_undo(node->code);
      u4free(node->code);
    }
    u4free(node);
    node = (CTRL4CODE *) l4next(&ctrl4codeListVBX,node);
  }
}

/***************************************************************\
  \***************************************************************/

CTRL4CODE * S4FUNCTION ctrl4getCtrlCode(HINSTANCE hInst)
{
  CTRL4CODE   *node,*newnode,*returnnode = NULL;

  if(hInst == 0)
    return NULL;

  for(node = (CTRL4CODE *) l4first(&ctrl4codeListVBX);node != NULL;)
  {
    if(node->hInst == hInst)
    {
      returnnode = node;
      node = NULL;
    }
    else
      node = (CTRL4CODE *) l4next(&ctrl4codeListVBX,node);
  }
  return returnnode;
}

/***************************************************************\
  \***************************************************************/

void S4FUNCTION ctrl4initVBXUndo(CODE4 *code,HINSTANCE hInstance)
{
  CTRL4CODE   *node;

  node = ctrl4getCtrlCode(hInstance);
  if(node)
  {
    if(code != node->code)
      d4init_undo(code);

    d4init_undo(node->code);
    if(node->alloc)
    {
      u4free(node->code);
    }
    l4remove(&ctrl4codeListVBX,node);
    u4free(node);
  }
}

/***************************************************************\
  \***************************************************************/

void S4FUNCTION ctrl4initVBX(CODE4 *code,HINSTANCE hInstance,int initialize)
{
  CODE4       *oldcode;
  CTRL4CODE   *node;
  int         dealloc;

  if(code)
  {
    node = ctrl4getCtrlCode(hInstance);
    if(!node)
    {
      ctrl4addCode(hInstance);
      node = ctrl4getCtrlCode(hInstance);
      if(node)
        node->alloc = 0;
    }

    if(node)
    {
      if(node->code)
      {
        dealloc = 0;
        if(node->alloc)
          dealloc = IDYES;
        else
          dealloc = MessageBox(0,"Warning! Detected two calls to ctrl4init\nwithout intervening call to ctrl4initUndo!\n\nOk to free previously allocated CODE4 memory ?","Multiple CODE4 Detected",MB_YESNO|MB_TASKMODAL|MB_ICONEXCLAMATION);

        if(dealloc == IDYES)
        {
          oldcode = node->code;
          ctrl4initVBXUndo(node->code,hInstance);
          u4free(oldcode);
          node->code = NULL;
        }
      }
      node->alloc = 0;
      node->code = code;

      if(initialize)
        d4init(code);
    }
  }
}
#endif /* S4PASCAL_DOS */
#endif /* S4DLL_BUILD */