/*
 * this is the obsolete client for NetWare code.
 * (last id: netware.cpp,v 1.3 1998/06/16 09:36:14 cyruspatel )
 *
 * Its all very hacky, but I've kept it in the tree
 * for public-source purposes as well as a reference
 * to how I did things the first time around.
*/

#if !defined(lint)
static const char *id="@(#)$Id: obsolete.cpp,v 1.1.2.2 1999/11/14 21:06:02 cyp Exp $";
#endif

//  Originally intended to contain NetWare specific stuff so that
//  the common code wouldn't have large NetWare specific sections.

#include <process.h> //ThreadSwitch, FindNLMHandle, GetThreadID, delay, RenameThread
#include <string.h>  
#include <stdio.h>   //NULL, sprintf
#include <nwfile.h>    //ScanErasedFiles() PurgeErasedFile()
extern "C" {
#include <conio.h>     //ConsolePrintf()
}
#include <unistd.h>    //chdir(), getcwd(), unlink(), lseek
#include <fcntl.h>     // O_RDONLY, O_BINARY
#include <signal.h>    //  SIG_xxx
#include <time.h>      // time()
#include <ctype.h>     // isdigit
#include <sys/time.h>  // timeval
#include <sys/stat.h>  // stat
#include <netinet/in.h> // htonl()
extern "C" int GetCurrentConnection(void); 

#include "client.h"    //Client class 
#include "version.h"   //CLIENT_CONTEST
#include "problem.h"   //Problem class and FileEntry
#include "cpucheck.h"  //GetTimesliceBaseline()
#include "triggers.h"  //CheckExitRequestTrigger()/RaiseUserRequestTrigger()
#include "netware.h"   //to keep the prototypes in sync

#define inportb(x) inp(x) 
#define outportb(x,y) outp(x,y) 

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

#define MAX_IDLELOOP_CALLBACKS (1) // (MAXCPUS)
//#define NOAUTODESTROYSCREEN

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

#define DebugPrintf(x) ConsolePrintf(x)
#define DebugPrintf2(x,y) ConsolePrintf(x,y)
#define DebugPrintf3(x,y,z) ConsolePrintf(x,y,z)
#define DebugPrintf4(x,y,z,a) ConsolePrintf(x,y,z,a)

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

extern "C" void *ImportPublicSymbol( int nlmHandle, const char *lname );
extern "C" int UnImportPublicSymbol( int nlmHandle, const char *lname );

static void *ImportSymbol( int nlmHandle, const char *name )
{
  char buf[64]; 
  if (!name || !*name || (((buf[0]=strlen(name))+1) >= sizeof(buf)) )
    return NULL;
  memcpy( buf+1, name, buf[0] );
  return ImportPublicSymbol( nlmHandle, buf );
}  
  
static int UnimportSymbol( int nlmHandle, const char *name )
{
  char buf[64]; 
  if (!name || !*name || (((buf[0]=strlen(name))+1) >= sizeof(buf)) )
    return NULL;
  memcpy( buf+1, name, buf[0] );
  return (int)UnImportPublicSymbol( nlmHandle, buf );
}

/* ================================================================== */


static int __nwCliGetNLMHandle(void)
{
  static int nlmHandle = -1;
  if (nlmHandle == -1) 
    nlmHandle = GetNLMHandle();
  return nlmHandle;
}  

/* ================================================================== */

static int __nwCliGetSetableParameterValue( unsigned int connum,
           const char *setableParameterString, void *retval )
{
  static int (*_GetSetableParameterValue)( unsigned int,  
     const char *, void * ) = 
            (int (*)(unsigned int, const char *, void *))(0x01);
  
  if (_GetSetableParameterValue == 
         (int (*)(unsigned int, const char *, void *))(0x01))
    {
    _GetSetableParameterValue = 
      (int (*)(unsigned int, const char *, void *))
        ImportSymbol( __nwCliGetNLMHandle(), "GetSetableParameterValue" );
    }
  if (_GetSetableParameterValue)
    {
    return (*_GetSetableParameterValue)(connum,setableParameterString,retval);
    }
  return -1;
}  

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

static int __nwCliSetSetableParameterValue( unsigned int connum,
           const char *setableParameterString, void *retval )
{
  static int (*_SetSetableParameterValue)( unsigned int,  
     const char *, void * ) = 
            (int (*)(unsigned int, const char *, void *))(0x01);
  
  if (_SetSetableParameterValue == 
         (int (*)(unsigned int, const char *, void *))(0x01))
    {
    _SetSetableParameterValue = 
      (int (*)(unsigned int, const char *, void *))
        ImportSymbol( __nwCliGetNLMHandle(), "SetSetableParameterValue" );
    }
  if (_SetSetableParameterValue)
    {
    return (*_SetSetableParameterValue)(connum,setableParameterString,retval);
    }
  return -1;
}  


/* ===================================================================== */

//used to rename threads and for messages
static char _cliApplicationName[10]="RC5DES\0";

const char *__nwCliGetApplicationName(void) 
{ return _cliApplicationName; }

static void __nwCliSetApplicationName(const char *p)
{
  if (p && *p)
    {
    const char *q = p+(strlen(p)-1);
    while (*q!='\\' && *q!='/' && *q!=':' && q>p)
      q--;
    if (*q=='\\' || *q=='/' || *q==':')
      q++;
    int i=0;
    while (*q && *q!='.' && i<8)
      _cliApplicationName[i++]=*q++;
    _cliApplicationName[i]=0;
    }
  return;
}    

/* ===================================================================== */

int nwCliIsNetworkAvailable(int unused) /* returns !0 if available */
{
  int _offlinemode = 1;
  unused = unused;

  if (FindNLMHandle( "TCPIP.NLM" )!= NULL)
    {
    char *fname = "gethostid";
    long (*_gethostid)() = 
      (long (*)())ImportSymbol( __nwCliGetNLMHandle(), fname );
    if (_gethostid)
      {
      _offlinemode = (*_gethostid)();
      UnimportSymbol( __nwCliGetNLMHandle(), fname );
      _offlinemode = ((_offlinemode == 0 || _offlinemode == -1) ? 1 : 0);
      }
    }
  return (_offlinemode==0);
}

/* ===================================================================== */

extern "C" int AllocateResourceTag(int NLMHandle, char *desc, int resType);

static int __nwCliGetResourceTag(int tagType)
{
  static int aespTag = 0, plprTag = 0, evntTag = 0;
  int *sel = NULL;
  char *desc;

  DebugPrintf("__nwCliGetResourceTag: beginning\r\n");
  
  if (tagType == 'PSEA' )  /* AESProcessSignature */
    {
    sel = &aespTag;
    desc = "AES Process Call-Back";
    }
  else if (tagType == 'RPLP')
    {
    sel = &plprTag;
    desc = "Polling Procedure Call-Back";
    }
  else if (tagType == 'TNVE')
    {
    sel = &evntTag;
    desc = "Event Notification Call-Back";
    }
  
  if (sel && !*sel)
    *sel = AllocateResourceTag( __nwCliGetNLMHandle(), desc, tagType );

  DebugPrintf("__nwCliGetResourceTag: ended\r\n");
  return ((sel)?(*sel):(0));
}

/* ===================================================================== */

static Client *_clientPtr = NULL;
static Client *__nwCliGetClientPointer(void) { return _clientPtr; }
static void __nwCliSetClientPointer(Client *cli) { _clientPtr = cli; return; }

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

static __inline int __PushPopInt( int *x, int y ) { int i=*x; *x=y; return i; }

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

#ifdef ENABLE_POLLING
static int _cliIsPollingProhibitedFlag = 0;
static int CliGetIsPollingProhibitedFlag(void) { return _cliIsPollingProhibitedFlag; }
static int CliSetIsPollingProhibitedFlag(int f) { return __PushPopInt( &_cliIsPollingProhibitedFlag, f ); }
#endif

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

static int _cliIsScreenHiddenFlag = 0;
static int CliGetIsScreenHiddenFlag(void) { return _cliIsScreenHiddenFlag; }
static int CliSetIsScreenHiddenFlag(int f) { return __PushPopInt( &_cliIsScreenHiddenFlag, f ); }

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

static int _cliFileWasAccessedFlag = 0;
static int CliSetFileWasAccessedFlag(void) { _cliFileWasAccessedFlag=1; return 0; }
static int CliGetFileWasAccessedFlag(void) { return __PushPopInt( &_cliFileWasAccessedFlag, 0 ); }

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

static int _cliMainThreadID = 0; //threadid
static int __nwCliGetMainThreadID(void)  { return _cliMainThreadID; }
static int __nwCliSetMainThreadID(int id)  { return __PushPopInt( &_cliMainThreadID, id ); }

/* ===================================================================== */

//extern "C" unsigned int GetFileServerMajorVersionNumber(void);
//should be in a312.nlm

static unsigned int __nwCliGetFileServerMajorVersionNumber(void)
{
  static int major = 0;
  if (!major)
    {
    const char *fname = "GetFileServerMajorVersionNumber";
    int nlmHandle = __nwCliGetNLMHandle();
    int (*proc)(void) = (int (*)(void))ImportSymbol( nlmHandle, fname );
    major = ((proc) ? ((*proc)()) : 3 );
    UnimportSymbol( nlmHandle, fname );
    }
  return major;
}  
    
/* ===================================================================== */

static unsigned int __nwCliGetConnectionsInUseCount(void)
{
  static unsigned int (*_GetConnectionLicensesInUse)(void) = 
                                                 (unsigned int (*)())0x1;
  static unsigned int (*_StationsInUseCount);
  if (_GetConnectionLicensesInUse == (unsigned int (*)())0x1 )
    {
    _GetConnectionLicensesInUse = 
                        (unsigned int (*)())ImportSymbol( __nwCliGetNLMHandle(), 
                               /* "\x1a\" */ "GetConnectionLicensesInUse" );
    if (!_GetConnectionLicensesInUse)
      _StationsInUseCount = (unsigned int (*))ImportSymbol( __nwCliGetNLMHandle(), 
                               "StationsInUseCount" );
      
    }                           
  if (_GetConnectionLicensesInUse)
    return (*_GetConnectionLicensesInUse)();
  if (_StationsInUseCount) 
    return (*_StationsInUseCount);
  return 0;  
}    

/* ===================================================================== */

static unsigned int __nwCliGetDiskIOsPending(void)
{
  static unsigned int (*_DiskIOsPending)=((unsigned int *)(0x01));

  if (_DiskIOsPending==((unsigned int *)(0x01)))
    _DiskIOsPending = (unsigned int *)ImportSymbol( __nwCliGetNLMHandle(), "DiskIOsPending" );
  return ((_DiskIOsPending)?(*_DiskIOsPending):(0));
}  

/* ===================================================================== */

static unsigned int __nwCliGetDirtyBlocks(void)
{
  static unsigned int (*_NDirtyBlocks)=((unsigned int *)(0x01));

  if (_NDirtyBlocks==((unsigned int *)(0x01)))
    _NDirtyBlocks = (unsigned int *)ImportSymbol( __nwCliGetNLMHandle(), "NDirtyBlocks" );
  return ((_NDirtyBlocks)?(*_NDirtyBlocks):(0));
}

/* ===================================================================== */

extern "C" unsigned int GetNestedInterruptLevel(void);

static unsigned int __nwCliGetNestedInterruptLevel(void)
{ return GetNestedInterruptLevel(); }

/* ===================================================================== */

extern "C" unsigned int GetCurrentTime(void);

static unsigned int __nwCliGetCurrentTicks(void)
{ return GetCurrentTime(); }

/* ===================================================================== */

/* TICKS_PER_DAY == 0x1800b0 [1,573,040]
   UCLKS_PER_SEC == (TICKS_PER_DAY)/(24*60*60 sec/day) 
                    * 65535 utics/sec => 1,193,180 utics/sec
*/

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

#pragma pack(1)
struct _clockstr { time_t secs; unsigned long usecs; unsigned long flags; };
#pragma pack()


extern "C" unsigned int ConvertTicksToSeconds( unsigned long ticks, 
                        unsigned long *secs, unsigned long *hsecs );
extern "C" unsigned int ConvertSecondsToTicks( unsigned long secs, 
                        unsigned long hsecs, unsigned long *ticks );

static int __nwCliEmulateGetCurrentClock( struct _clockstr *clockstr )
{
  time_t secs;
  unsigned long usecs;
  unsigned char lsb, msb;
  unsigned long tics, otics, utics;

  do {
     otics = GetCurrentTime();
     outportb(0x43, 0x00);
     lsb = (unsigned char)inportb(0x40);
     msb = (unsigned char)inportb(0x40);
     tics = GetCurrentTime();
     } while (otics != tics);

  secs  = time(NULL);  /* base time is last completed 8 hours */

  #if 1
  unsigned long hsecs, utics;
  ConvertTicksToSeconds( tics, &secs, &hsecs );
  ConvertSecondsToTicks( secs, 0, &otics); 
  if ((tics - base_tics) > (60*18)) 
    {
    base_tics = tics-otics;
    base_time = ((unsigned int)(time(NULL))) - secs;
    }
  tics = (tics - base_tics) + otics;
  utics = (0x10000L-((msb<<8)|lsb));

  usecs = ((((tics << 16) | utics)<<1)/1193180) >> 1;
  secs +=
  

  #else

  secs  = time(NULL);  /* base time is last completed 8 hours */
  unsigned long p_secs, p_rest;
  ConvertTicksToSeconds( tics % 35, &p_secs, &p_rest ); 
  ConvertSecondsToTicks( p_secs, 0, &p_rest );
  secs -= p_secs;
  tics -= p_rest;
  
  _asm {
    push ebx
    xor  eax,eax
    mov  ah, msb
    mov  al, lsb
    not  ax
    mov  ecx, 8381
    mul  ecx
    mov  ebx, eax

    mov  eax, tics
    mov  ecx, 54925
    mul  ecx
    push edx
    push eax
    
    mov  ecx, 1000
    div  ecx
    mov  eax, edx
    
    add  eax, ebx
    xor  edx, edx
    div  ecx
    mov  ebx, eax
    
    pop  eax
    pop  edx
    add  eax, ebx
    adc  edx, 0
    
    mov  ecx, 1000000
    div  ecx
    add  secs, eax
    mov  usecs, edx
    pop  ebx
    }
  #endif

  clockstr->secs = secs;
  clockstr->usecs = usecs;
  clockstr->flags = 0;
  return 0;
}  

int nwCliGetTimeOfDay( struct timeval *stv )
{
  struct _clockstr clockstr;
  if (stv)
    {      //don't have to worry about LoaderGetClock and stuff
    __nwCliEmulateGetCurrentClock( &clockstr );
    stv->tv_sec = clockstr.secs;
    stv->tv_usec = clockstr.usecs;
    }
  return 0;
}  

#if 0
int nwCliGetTimeOfDay( struct timeval *stv )
{
  //NIOS port note: CLIB funcs have equivs in netware.cpp to avoid clib dependance
  #define PCLOCKS_PER_SEC (1193180) //often defined as UCLOCKS_PER_SEC
  static unsigned int timebase=0;
  static struct timeval base_timeval = {0,0};
  struct timeval tv;

  #ifdef doesnt_work
  static unsigned int tickbase;
  unsigned int ticksnow = __nwCliGetCurrentTicks();
  if (timebase == 0)
    {
    tickbase = ticksnow;
    timebase = (unsigned int)(time(NULL));
    }
  ticksnow -= tickbase;

  double d = ((((double)(GetSuperHighResolutionTimer()&0xFFFF)) +
             (((double)(ticksnow))*65536.0)) / (double)(1193180));
  unsigned long secs = (unsigned long)(d/(1000000.0));

  tv.tv_usec = (unsigned long)((d)-(((double)(secs))*1000000.0));
  tv.tv_sec = (time_t)(timebase + secs);
  #endif

  unsigned int secs, hsecs, ticks; 
  unsigned int ticksnow = __nwCliGetCurrentTicks();
  /*Cli*/ConvertTicksToSeconds( ticksnow, &secs, &hsecs );
  if (timebase==0) timebase = ((unsigned int)(time(NULL))) - secs;
  /*Cli*/ConvertSecondsToTicks( secs, 0, &ticks); // ticks = (secs*18.207)
  ticksnow-=ticks;
  
  tv.tv_usec = __ticks2usecs( ticksnow );
  //unsigned long picsnow = (GetSuperHighResolutionTimer()&0xFFFF) + 
  //                          (ticksnow << 16);
  //tv.tv_usec = (picsnow*100000L)/(PCLOCKS_PER_SEC/10);
  tv.tv_sec = (time_t)(timebase + secs);

  if (tv.tv_usec > 1000000L)
    {
    tv.tv_sec += (stv->tv_usec/1000000L);
    tv.tv_usec = (stv->tv_usec%1000000L);
    }
  if ((tv.tv_sec < base_timeval.tv_sec) || 
    ((tv.tv_sec == base_timeval.tv_sec) &&
      (tv.tv_usec < base_timeval.tv_usec)))
    {
    tv.tv_sec = base_timeval.tv_sec;
    tv.tv_usec = base_timeval.tv_usec;
    }
  else
    {
    base_timeval.tv_sec = tv.tv_sec;
    base_timeval.tv_usec = tv.tv_usec;
    }
  if (stv)
    {
    stv->tv_sec = tv.tv_sec;
    stv->tv_usec = tv.tv_usec;
    }
  return 0;
}  
#endif

/* ======================================================================== */

static struct
{
  int initialized;
  unsigned int shownormal;
  volatile unsigned int marklevel;
  unsigned int maxloops, numloops;
  unsigned int (*_CPU_Combined);
  unsigned int (*_MaximumNumberOfPollingLoops);
  unsigned int (*_NumberOfPollingLoops);
} utilization = {-1,0,0,0,0, NULL, NULL, NULL};

int nwCliUtilizationMark(void)
{
  int nlmHandle;

  if ((++utilization.marklevel) == 1)
    {
    if (utilization.initialized == -1)
      {
      utilization.initialized = 1;
      nlmHandle = __nwCliGetNLMHandle();
      utilization._CPU_Combined = (int *)ImportSymbol( nlmHandle, "CPU_Combined" );
      utilization._MaximumNumberOfPollingLoops = (int *)ImportSymbol( nlmHandle, 
                                        "MaximumNumberOfPollingLoops" );
      utilization._NumberOfPollingLoops = (int *)ImportSymbol( nlmHandle, 
                                        "NumberOfPollingLoops" );
      }
    if (utilization.shownormal && utilization._NumberOfPollingLoops && 
        utilization._MaximumNumberOfPollingLoops)
      {
      utilization.maxloops = (*(utilization._MaximumNumberOfPollingLoops));
      utilization.numloops = (*(utilization._NumberOfPollingLoops));
      }
    }
  return 0;
}  
  
int nwCliUtilizationRelease(void)
{
  if ((--utilization.marklevel) == 0)
    {
    if (utilization.initialized != -1 && 
        utilization._NumberOfPollingLoops && 
        utilization._MaximumNumberOfPollingLoops)
      {
      if ( utilization.shownormal )
        {
        }
      else
        {
        (*(utilization._NumberOfPollingLoops)) = utilization.numloops;
        (*(utilization._MaximumNumberOfPollingLoops)) = utilization.maxloops;
        }
      }                                                 //!!!! MAX not num
    }
  return 0;
}  

static unsigned int __nwCliGetSystemUtilizationPermille(void)
{
  if (utilization.initialized == -1)
    {
    nwCliUtilizationMark();
    nwCliUtilizationRelease();
    }
  if (utilization._CPU_Combined)
    return (100 * (*(utilization._CPU_Combined)));    
  if (utilization._NumberOfPollingLoops &&
      utilization._MaximumNumberOfPollingLoops)
    {
    unsigned int m, n;    
    m = (*(utilization._MaximumNumberOfPollingLoops));
    n = (*(utilization._NumberOfPollingLoops));
    return (10000-(((n*10000)+(m>>1))/m));
    }
  return 0;
}  


/* ======================================================================== */

static void __switch_fixup_utilization(void)
{
  static unsigned long numloops = 0, maxloops = 0;
 
  if (!utilization.shownormal)
    {
    ++numloops;
    if (numloops > maxloops)
      maxloops = numloops;
    unsigned long m = (numloops * 1000)/maxloops;
    utilization.numloops = (utilization.maxloops * m)/1000;
    if (numloops >= maxloops)
      numloops = 0;
    }
 return;
}

//--------------

static struct
{
  void (*_ThreadSwitch)(void);  //ThreadSwitch/thr_yield/CRescheduleMyself/CYieldIfNeeded
  void (*_ThreadSwitchWithDelay)(void);
  void (*_ThreadSwitchLowPriority)(void);
  unsigned int handicap;
  int OS_does_handicap;
} switchschemes = { ThreadSwitch, ThreadSwitch, ThreadSwitch, 0, 0 };

void nwCliThreadSwitch(void)
{
  int handicap = switchschemes.handicap;
  if (switchschemes.OS_does_handicap) handicap = 0;
  __switch_fixup_utilization();
  do { 
     (*(switchschemes._ThreadSwitch))(); 
     } while ((--handicap)>0);
  __switch_fixup_utilization();
}

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

static void __nwCliSimulatedThreadSwitchLowPriority(void)
{
  nwCliThreadSwitch();
  while (__nwCliGetDiskIOsPending() || 
    __nwCliGetNestedInterruptLevel()  /*|| __nwCliGetDirtyBlocks() */)
    nwCliThreadSwitch();
  return;
}  

/* ---------------------------------------------------------- */
  
static void __nwCliSimulatedThreadSwitchWithDelay(void) //can't use delay
{ int i; for (i=0;i<10;i++) nwCliThreadSwitch(); }

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

void nwCliThreadSwitchLowPriority(void) 
{ 
  static unsigned int sticks = 0;
  unsigned int ticks = __nwCliGetCurrentTicks();

  if ((ticks-sticks) > 3)
    {
    sticks = ticks;
    __switch_fixup_utilization();
    (*(switchschemes._ThreadSwitchLowPriority))(); 
    __switch_fixup_utilization();
    return;
    }
  __nwCliSimulatedThreadSwitchLowPriority(); /*depends on NDirtyBlocks/IOsPending*/

 return;
} 

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

void nwCliThreadSwitchWithDelay(void) 
{ 
  __switch_fixup_utilization();
  (*(switchschemes._ThreadSwitchWithDelay))(); 
  __switch_fixup_utilization();
}

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

static void __nwCliInitThreadSwitchSchemes(void)
{
  static int initialized = -1;
  void *tsscheme;
  int smpswitch, nlmHandle;
  const char *fname;
  
  if (initialized == -1)
    {
  DebugPrintf("__nwCliInitThreadSwitchSchemes: beginning\r\n");

    initialized = 1;
    nlmHandle = __nwCliGetNLMHandle();
    smpswitch = 0;
    
    if (nwCliIsSMPAvailable() && 
             (tsscheme = ImportSymbol( nlmHandle, "thr_yield" )) != NULL)
      {
      switchschemes._ThreadSwitch = 
      switchschemes._ThreadSwitchWithDelay = 
      switchschemes._ThreadSwitchLowPriority = (void (*)(void))(tsscheme);
      smpswitch = 1;
      }
    else
      {
      fname = "CYieldIfNeeded"; //==CRescheduleMyself
      if ((tsscheme = ImportSymbol( nlmHandle, fname )) == NULL)
        tsscheme = ImportSymbol( nlmHandle, "CRescheduleMyself" );
      if (tsscheme)
        switchschemes._ThreadSwitch = (void (*)(void))(tsscheme);
      }
      
    fname = "CYieldUntilIdle"; //==CRescheduleLastLowPriority
    if ((tsscheme = ImportSymbol( nlmHandle, fname )) == NULL)
      tsscheme = ImportSymbol( nlmHandle, "CRescheduleLastLowPriority" );
    if (tsscheme)
      switchschemes._ThreadSwitchLowPriority = (void (*)(void))(tsscheme);
    else if (!smpswitch)
      switchschemes._ThreadSwitchLowPriority = __nwCliSimulatedThreadSwitchLowPriority;
      
    fname = "CYieldWithDelay"; //==CReschedule[Last]WithDelay
    if ((tsscheme = ImportSymbol( nlmHandle, fname )) == NULL)
      tsscheme = ImportSymbol( nlmHandle, "CRescheduleLastWithDelay" );
    if (tsscheme)
      switchschemes._ThreadSwitchWithDelay = (void (*)(void))(tsscheme);
    else if (!smpswitch)
      switchschemes._ThreadSwitchWithDelay = __nwCliSimulatedThreadSwitchWithDelay;

    nwCliThreadSwitch();
  DebugPrintf("__nwCliInitThreadSwitchSchemes: ended\r\n");
    }
  return;
}
    
/* ------------------------------------------------------------------ */

extern "C" CValidatePointer(void *);

static int CliValidatePointer(void *ptr)  //need to do a domain check
{
  if (!ptr) return 0;
  char *fname = "CValidatePointer";
  int (*_valiptr)(void *) = (int (*)(void *))ImportSymbol(__nwCliGetNLMHandle(), fname);
  if (_valiptr)
    {
    int isv = (*_valiptr)(ptr);
    UnimportSymbol( __nwCliGetNLMHandle(), fname );
    return isv;
    }
  return 1;
}  

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

static int __nwCliIsThreadRunning( int threadID )
{
  if (threadID == GetThreadID())
    return 1;
  delay(55*5);
  return 0;
  /*
  char threadName[20];
  if (!CliValidatePointer((void *)(threadID)))
    return 0;
  return ( GetThreadName( threadID, threadName ) != EBADHNDL );
  */
}

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

//FIXME: used by threadcd.cpp 
int CliWaitForThreadExit(int thrid) 
{ return nwCliWaitForThreadExit( thrid ); }

int nwCliWaitForThreadExit( int threadID )
{
  if (threadID)
    {
    if (threadID == GetThreadID())
      return -1; /* ExitThread( TSR_THREAD, 0 ); -1, 0 */
    while (__nwCliIsThreadRunning( threadID ))
      delay(110);
    }  
  return 0;  
}    

/* ======================================================================== */

unsigned int nwCliGetNumberOfProcessors(void)
{
  static int processorcount = -1;
  
  if (processorcount == -1)
    {
    processorcount = 1;
    
    const char *fname = "GetNumberOfRegisteredProcessors";
    int nlmHandle = __nwCliGetNLMHandle();
    int (*_GetNumberOfRegisteredProcessors)() = (int (*)())ImportSymbol( 
                                              nlmHandle, fname );
    if (_GetNumberOfRegisteredProcessors)
      {
      processorcount = ((* _GetNumberOfRegisteredProcessors)());
      (int (*)())UnimportSymbol( nlmHandle, fname );
      if (processorcount < 1)
        processorcount = 1;
      }
    }
  return ((unsigned int)(processorcount));
}  

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

static int __nwCliIsThreadOnMP(void)
{
  static int (*_thr_type)(void) = ((int (*)(void))(0xffffffff));
  if (_thr_type == ((int (*)(void))(0xffffffff)))
    _thr_type = (int (*)())ImportSymbol(__nwCliGetNLMHandle(), "thr_type" );
  if (_thr_type != NULL)    
    return (*_thr_type)();
  return 0;
}  

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

int nwCliIsSMPAvailable(void)
{
  static int issmp = -1;
  if (issmp == -1)
    {
    issmp = 0;
    const char *fname = "mdisable";
    int nlmHandle = __nwCliGetNLMHandle();
    int (*_mdisable) = (int (*))ImportSymbol( nlmHandle, fname );
    if (_mdisable)
      {
      if (!(*_mdisable))
        issmp = 1;
      UnimportSymbol( nlmHandle, fname );
      }
    }
ConsolePrintf("have smp: %s\n", issmp ? "YES" : "NO" );
  return (issmp);
}  

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

static int __nwCliMigrateThreadToSMP( unsigned int thread_i ) /* 0=main, 1=first crunch,... */
{
  static int initialized = -1;
  static char shownmsg = 0;    

  static int (*_thr_yield_to_mp)(void) = NULL;
  static int (*_thr_preempt_on)(void) = NULL;
  static void *(*_GetProcessorControlBlock)(unsigned int) = NULL;
  static int (*_thread_bind)(int, void *) = NULL;
  static void (*_EnableSystemPreemption)(void) = NULL;

  unsigned int cpucount = nwCliGetNumberOfProcessors(); /* also init that */
  int nlmHandle;

  if (initialized == -1)
    {
    _thr_yield_to_mp = NULL;
    _thr_preempt_on = NULL;
    _GetProcessorControlBlock = NULL;
    _thread_bind = NULL;
    _EnableSystemPreemption = NULL;
    initialized = 0;
    }

  if (initialized == 0)
    {
    initialized++;
    //ConsolePrintf("init smp\n");
    nlmHandle = __nwCliGetNLMHandle();
    _thr_yield_to_mp =(int (*)(void))ImportSymbol( 
                    nlmHandle, "thr_yield_to_mp" /* "NWSMPThreadToMP" */);
    //ConsolePrintf("_thr_yield_to_mp = %08x\n", _thr_yield_to_mp );
    _thr_preempt_on = (int (*)(void))ImportSymbol( 
                    nlmHandle, "thr_preempt_on" );
    //ConsolePrintf("_thr_preempt_on = %08x\n", _thr_preempt_on );
    _GetProcessorControlBlock = (void *(*)(unsigned int))ImportSymbol( 
                   nlmHandle, "GetProcessorControlBlock" );
    //ConsolePrintf("_GetProcessorControlBlock = %08x\n", _GetProcessorControlBlock );
    _thread_bind = (int (*)(int, void *))ImportSymbol( 
                   nlmHandle, "thread_bind" );
    //ConsolePrintf("_thread_bind = %08x\n", _thread_bind );
    _EnableSystemPreemption = (void (*)(void))ImportSymbol( 
                   nlmHandle, "EnableSystemPreemption" );
    }

  if (!_thr_yield_to_mp)
    {
    if (((shownmsg & 1)==0) && nwCliIsSMPAvailable())
      {
      shownmsg |= 1;
      ConsolePrintf("%s: Unable to initialize SMP functions.\r\n"
          "  The client will continue normally but will not be able to\r\n"
          "  take advantage of multi-processing features.\r\n", 
          __nwCliGetApplicationName() );
      }
    }
  else if (thread_i != 0)
    {
    char param[50]; const char *setstr = "SMP Netware Kernel Mode";
    if ((shownmsg & 2)==0 && __nwCliGetSetableParameterValue( 
                          GetCurrentConnection(), setstr, param ) != -1)
      {
      shownmsg |= 2;
      if (strcmpi( param, "SHARED" ) == 0)
        {
        if (__nwCliSetSetableParameterValue( GetCurrentConnection(), 
                          setstr, "PRIORITY" ) != -1 && 
            __nwCliGetSetableParameterValue(  GetCurrentConnection(), 
            setstr, param ) != -1 &&  strcmp( param, "SHARED" ) != 0 )
        ConsolePrintf("%s: Note: \"%s\" is now set to %s (was SHARED).\r\n",
                  __nwCliGetApplicationName(), setstr, param );
        }
#if 0
      elif if (strcmpi( param, "DEDICATED" ) == 0)
        {
        ConsolePrintf("%s: Note: \"%s\" is currently SET to %s\r\n"
        "  The client will not be able to use processor 0 until\r\n"
        "  kernel mode is set to SHARED or PRIORITY.\r\n", 
        __nwCliGetApplicationName(), setstr, param );
        }
#endif
      }
    (*_thr_yield_to_mp)();
    if (_thr_preempt_on)
      {
      //ConsolePrintf("got thread_preempt\n" );
      (*_thr_preempt_on)();
      //__nwCliIsPreemptionOnFlag = 1;
      }
    if (_EnableSystemPreemption && (shownmsg & 4) == 0)
      {
      (*_EnableSystemPreemption)();
      //__nwCliIsPreemptionOnFlag = 1;
      shownmsg|=4;
      ConsolePrintf("%s: Note: SMP system preemption is now enabled.\r\n",
             __nwCliGetApplicationName());
      }
    if (_GetProcessorControlBlock && _thread_bind)
      {
      unsigned int cpusel = ((cpucount < 2)?(0):(thread_i % cpucount));
      void *cpu_pcb = (*_GetProcessorControlBlock)(cpusel);
      if (cpu_pcb) (*_thread_bind)( GetThreadID(), cpu_pcb );
      //ConsolePrintf("got pcb/threadbind PCB: %08x cpusel: %d \n ", cpu_pcb, cpusel );
      }
    }
  return ((nwCliIsSMPAvailable()==0)?(0):(__nwCliIsThreadOnMP()!=0)); 
}  

/* ======================================================================== */

static int __nwCliAssignThreadName( unsigned int mtpos ) /* 0 = main, 1 = first, etc */
{
  char threadName[20];       /* "RC5DES Crunch #16" -  max 17 chars */
  char *eon;
  int threadID = GetThreadID();
  int retval = 0;
  
  strncpy( threadName, __nwCliGetApplicationName(), sizeof( threadName )-1 );
  threadName[ sizeof( threadName )-1 ]=0;
  
  if ( mtpos == 0 )
    {
    if ( strlen( threadName ) > 17 )
      threadName[17] = 0;
    retval = RenameThread( threadID, threadName );
    }
  else
    {
    if ( strlen( threadName ) > 6 )
      threadName[6] = 0;
    eon = &threadName[strlen( threadName )];
    sprintf( eon, " Crunch #%02d", mtpos );
    retval = RenameThread( threadID, threadName );
    
    if (__nwCliGetMainThreadID())
      {
      strcpy( eon, " Main" );
      RenameThread( __nwCliGetMainThreadID(), threadName );
      }
    }
  return 0;
}

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

static int __nwCliDisableUpgradeLowPriorityThreadsSetting( void )
{
  static char shownmsg = 0;
  char param[10];

  param[0] = 0; const char *setstr = "Upgrade Low Priority Threads";
  if (__nwCliGetSetableParameterValue( GetCurrentConnection(),  
               setstr, param ) != -1)
    {
    if (param[0] != 0 && shownmsg == 0)
      {
      shownmsg = 1;
      ConsolePrintf("%s: Note: \"%s\" is currently SET to %s\r\n"
      "  This will degrade the client's performance and is probably inappropriate\r\n"
      "  for the server as well. SCHDELAY.NLM, the CLIB switch and the +/- keys on\r\n"
      "   MONITOR's 'Scheduling information' screen are more suitable.\r\n",
        __nwCliGetApplicationName(), setstr, ((param[0])?("ON"):("OFF")) );
      }
    }
  return 0;
}                                     

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

static int __nwCliClearThreadContextSpecifier(void)
{
  static int (*_SetThreadContextSpecifier)(int, int) = 
                                         (int (*)(int,int))0xffffffffL;
  
  if (_SetThreadContextSpecifier == (int (*)(int,int))0xffffffffL)
    {
    _SetThreadContextSpecifier = (int (*)(int, int))ImportSymbol(
        __nwCliGetNLMHandle(), "SetThreadContextSpecifier" );
    }
  
  if (_SetThreadContextSpecifier)
    {  //will only fail if thread is not in a clib-"thread-group"
    return (*_SetThreadContextSpecifier)( GetThreadID(), 0 /*NO_CONTEXT*/ );
    }
  return -1;
}  

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

int nwCliSetProcessHandicap( unsigned int handicap )
{
  if (((int)(handicap)) < 0)
    return -1;
  if (handicap > 9)
    handicap = 9;
  int oldhandicap = (int)switchschemes.handicap;
  switchschemes.handicap = handicap;
  return oldhandicap;
}  

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

static int __nwCliSetThreadHandicap( void )
{
  static int (*_SetThreadHandicap)( int, int ) = (int (*)(int,int))(0x01);

  if (_SetThreadHandicap == (int (*)(int,int))(0x01))
    {
    _SetThreadHandicap = (int (*)(int,int))ImportSymbol( 
                           __nwCliGetNLMHandle(), "SetThreadHandicap" );
    if (_SetThreadHandicap)
      switchschemes.OS_does_handicap = 1;
    }
  if (_SetThreadHandicap)
    (*_SetThreadHandicap)(GetThreadID(), switchschemes.handicap );
  return 0;
}        

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

int nwCliInitializeThread( unsigned int thread_i ) // 0 is main thread
{
  __nwCliClearThreadContextSpecifier();
  __nwCliAssignThreadName( thread_i );
  __nwCliMigrateThreadToSMP( thread_i );
  __nwCliSetThreadHandicap();
  return 0;
}   

/* ================================================================== */

static int __nwCliGetSystemConsoleScreen(void)
{
  static int _consoleScreen = -1;
  
  char *fname;
  int nlmHandle;
  int (*_systemConsoleScreen);
  int (*_GetSystemConsoleScreen)(void);

  if (_consoleScreen == -1)
    {
    _consoleScreen = 0;
    nlmHandle = __nwCliGetNLMHandle();
    
    fname = "systemConsoleScreen";
    _systemConsoleScreen = (int (*))ImportSymbol( nlmHandle, fname );
    if (_systemConsoleScreen)
      {
      _consoleScreen = (*_systemConsoleScreen);
      UnimportSymbol( nlmHandle, fname );
      }
    else 
      {
      fname = "GetSystemConsoleScreen";
      _GetSystemConsoleScreen = (int (*)())ImportSymbol( nlmHandle, fname );
      if (_GetSystemConsoleScreen)
        {
        _consoleScreen = (*_GetSystemConsoleScreen)();
        UnimportSymbol( nlmHandle, fname );
        }
      }
    }
  return _consoleScreen;
}  

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

extern "C" int ActivateScreen(int screenID);

int CliActivateScreen(int screenID)
{ return (!screenID || screenID==-1)?(-1):(ActivateScreen(screenID)); }

int CliActivateConsoleScreen(void)
{ return CliActivateScreen( __nwCliGetSystemConsoleScreen()); }

extern "C" void OutputToScreenWithPointer( int, char *, void * );
extern "C" void OutputToScreen( int, char *, ... );

void xxCliConsolePrintf( char *format, ... )
{
  int scrid;
  if ((scrid = __nwCliGetSystemConsoleScreen())!=0)
    {
    OutputToScreen( scrid, "%s: ", __nwCliGetApplicationName() );
    OutputToScreenWithPointer( scrid, format, (&format)+1 );
    }
  return;
}  

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

static int nwScreenID = 0;     
static int nwScreenHandle = 0; 
static char nwCliScreenName[30]={0};
  
static int CliIsClientScreenActive(void)
{ return 1; /* (activeScreen == nwScreenID); */ }

static char *CliGetScreenName(void)
{ return nwCliScreenName; }

static char *CliGetScreenMemPointer(void)
{ 
  static int isValidated=0;
  if (!isValidated && nwScreenID != 0)
    {
    if (nwScreenID && (*((int *)(((char *)(nwScreenID))+0x00))==(int)(0x34561289)))
      {
      unsigned char buff[2], orig[2];
      char *mem = *((char **)(((char *)(nwScreenID))+0x20));
      orig[0]=mem[0]; orig[1]=mem[1];
      buff[0]='C'; buff[1]='Y';
      CopyToScreenMemory( 1, 1, buff, 0, 0 );
      if (mem[0]!=buff[0] || mem[1]!=buff[1])
        isValidated=-1;
      else
        isValidated=1;
      mem[0]=orig[0]; mem[1]=orig[1];
      }
    }
  if (isValidated>0)
    return (*((char **)(((char *)(nwScreenID))+0x20)));
  return NULL;
}    

/* ======================================================================== */
    
//Watcom and NetWare SDK's differ on what AProcessToCall should look like -
//so we just create a new struct in line with the Novell SDK one

struct myAESProcessStructure
{                                                 /* set by or to... */
   struct myAESProcessStructure *ALink;           /* AES will set*/
   LONG                        AWakeUpDelayAmount; /* ticks to wait */
   LONG                        AWakeUpTime;     /* AES will set */
   void                    (*AProcessToCall)(struct myAESProcessStructure *);
                                                /* set to function to call */
   LONG                        ARTag;           /* set to resource tag */
   LONG                        AOldLink;        /* set to NULL */
};


#ifdef CancelSleepAESProcessEvent
#undef CancelSleepAESProcessEvent
#endif
#ifdef ScheduleSleepAESProcessEvent
#undef ScheduleSleepAESProcessEvent
#endif
#ifndef AESProcessSignature
#define AESProcessSignature            0x50534541  /* 'AESP' */
#endif

extern "C" void CancelSleepAESProcessEvent( struct AESProcessStructure *EventNode );
extern "C" void ScheduleSleepAESProcessEvent( struct AESProcessStructure *EventNode );

//-------

static struct myAESProcessStructure cliScreenStatusAESStruct;
static int cliScreenStatusProcessorStatus = 0;
static void (*cliScreenStatusProc)(void) = NULL;

static struct myAESProcessStructure *CliScreenStatusProcessorExec( struct myAESProcessStructure *aesP )
{
  if (cliScreenStatusProcessorStatus < 0 || CheckExitRequestTriggerNoIO())
    cliScreenStatusProcessorStatus = 0;
  else
    {
    cliScreenStatusProcessorStatus = 1;
    ScheduleSleepAESProcessEvent((struct AESProcessStructure *)aesP);
    }
  if (cliScreenStatusProc)
    (*cliScreenStatusProc)();
  return aesP;
}  

static int CliInitScreenStatusProcessor(void)
{
  if (cliScreenStatusProcessorStatus)
    return 0;

  if (CliGetScreenMemPointer()==NULL)
    return -1;

  cliScreenStatusAESStruct.ARTag = __nwCliGetResourceTag(AESProcessSignature);
  if (!cliScreenStatusAESStruct.ARTag)
    return -1;

  cliScreenStatusAESStruct.AOldLink = 0;
  cliScreenStatusAESStruct.AWakeUpDelayAmount = 9;  /* in ticks */

  //Watcom and NetWare SDKs differ on the prototype for AProcessToCall -
  cliScreenStatusAESStruct.AProcessToCall =
       (void (*)(myAESProcessStructure *))CliScreenStatusProcessorExec;
  
  ScheduleSleepAESProcessEvent(
                   (struct AESProcessStructure *)&cliScreenStatusAESStruct);
  int count = 0;
  cliScreenStatusProcessorStatus = 0;
  while (!cliScreenStatusProcessorStatus && ((++count)<=10)) //5 secs
    delay(110*cliScreenStatusAESStruct.AWakeUpDelayAmount); /* let process run */
  if (!cliScreenStatusProcessorStatus)
    CancelSleepAESProcessEvent(
                   (struct AESProcessStructure *)&cliScreenStatusAESStruct);
  return(cliScreenStatusProcessorStatus!=0);
}                   

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

static int CliSetScreenStatusProcessor( void (*_statusProc)(void ) )
{
  cliScreenStatusProc = _statusProc;
  DebugPrintf("CliRunProblemAsCallback: CliSetScreenStatusProcessor has been set\r\n");
  return 0;
}  

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

static int CliDestroyScreenStatusProcessor(void) //this must be clib-call free
{
  if (cliScreenStatusProcessorStatus)
    {
    cliScreenStatusProcessorStatus=-1;
    unsigned int timeout = __nwCliGetCurrentTicks()+(10*cliScreenStatusAESStruct.AWakeUpDelayAmount);
    while (cliScreenStatusProcessorStatus && (__nwCliGetCurrentTicks()<timeout))
      nwCliThreadSwitchWithDelay();  /* let process run */
    if (cliScreenStatusProcessorStatus)
      {
      CancelSleepAESProcessEvent(
                 (struct AESProcessStructure *)&cliScreenStatusAESStruct);
      cliScreenStatusProcessorStatus = 0;
      }
    }
  return 0;                  
}                 

/* ======================================================================== */

static int _cliAutoDestroyScreen = 1;

int CliGetScreenDestructionMode(void)
{ return _cliAutoDestroyScreen; }
    
int CliSetScreenDestructionMode(int mode)
{ int i=_cliAutoDestroyScreen; _cliAutoDestroyScreen=mode; 
  return SetAutoScreenDestructionMode( mode ); } //mode may be change in -test etc

extern "C" void UngetKey( int scrid, int mode, int flags1, int chr, int flags2 );

static void __nwCliSyncConsoleCursors(void)
{
  int cons = __nwCliGetSystemConsoleScreen();
  if (cons)
    {
    UngetKey( cons, 0, ' ', 0, 0 ); //space
    UngetKey( cons, 3,  4 , 0, 0 ); //backspace
    }
  return;
}

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

static int CliUninitClientScreen(void)
{ 
  CliDestroyScreenStatusProcessor();
  return 0;
}

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

static int CliInitClientScreen(void)
{
  if (!__nwCliGetSystemConsoleScreen())
    return -1;

  if (!CliGetScreenDestructionMode()) 
    strcpy( nwCliScreenName, "System Console" );
  else if (CliGetIsScreenHiddenFlag())
    {
    nwScreenHandle = NULL;
    nwScreenID = NULL;
    return 0;
    }
  else
    sprintf(nwCliScreenName, "%s v2.%d.%d client",
     __nwCliGetApplicationName(), CLIENT_CONTEST*100 + CLIENT_BUILD, CLIENT_BUILD_FRAC );
  
  int scr=CreateScreen(nwCliScreenName, DONT_AUTO_ACTIVATE | 
                     DONT_CHECK_CTRL_CHARS );  //| AUTO_DESTROY_SCREEN 

  if (scr==0 || scr==-1)
    return -1;

  nwScreenHandle = scr;
  nwScreenID = __GetScreenID(scr);

  DisplayScreen(scr);
  SetCtrlCharCheckMode( 0 ); //disable ^C, ^S checks. SIGINT causes hang!

  if (CliGetScreenDestructionMode()) 
    {
    CliInitScreenStatusProcessor(); 
    SetAutoScreenDestructionMode( 1 ); //this may be change in -test etc  
    }
  return 0;
}   

/* ======================================================================== */

static int __nwCliInitPaths(char *master)
{
  static char cliBasePathBuffer[128+1]; //used in verifypaths and InitClient
  static int cliCwdIsValid = -1;

  unsigned int pos, c;

  if (cliCwdIsValid==-1 && master && *master)
    {
    pos = strlen(master);
    while (pos)
      {
      if (master[pos]=='\\' || master[pos]=='/' || master[pos]==':')
        {
        if (master[pos]==':')
          {
          pos++;
          if (master[pos]=='\\' || master[pos]=='/') //never happens
            pos++;
          }
        cliCwdIsValid = 0;
        break;
        }
      pos--;
      }
    if (cliCwdIsValid==0 && (pos+1)<= sizeof(cliBasePathBuffer))
      {  
      memcpy( cliBasePathBuffer, master, pos );
      cliBasePathBuffer[pos+1]=0;
      if (chdir( cliBasePathBuffer )==0)
        {
        getcwd(cliBasePathBuffer,sizeof(cliBasePathBuffer)-1);
        cliCwdIsValid = 1;
        }
      }
    else if (getcwd(cliBasePathBuffer,sizeof(cliBasePathBuffer)-1)==NULL)
      strcpy(cliBasePathBuffer,"SYS:");
    }
  return 0;
}  

/* ====================================================================== */

#ifdef ENABLE_POLLING

/* ====================================================================== */
/* -- all the polling related functions must be totally clib-call free -- */

static struct callbacksched 
{
  Problem *problem;
  int timeslice;
  int run;
  int cpu_i;
}  

#if (MAX_IDLELOOP_CALLBACKS < 1)
  callbackschedtable[1];
#else  
  callbackschedtable[MAX_IDLELOOP_CALLBACKS];  
#endif  

static int callbackschedinit = 0;
static int callbackschedinproc = 0;  //CliAllocIdleCallback() and CliFreeIdleCallback() aren't reentrant
static int callbackschedrestag;
static int (*_RemovePollingProcedure)( void (*proc)(void) ) = NULL; 
static int (*_AddPollingProcedureRTag)( void (*proc)(void), int resTag ) = NULL;

static void CliIdleCallbackHandler( void )
{
  static unsigned int index = 0;
  
  if (!GetNestedInterruptLevel())
    {
    unsigned int i=index;
    while (callbackschedtable[i].problem==NULL || callbackschedtable[i].run!=0)
      {
      ++i;
      if (i>=MAX_IDLELOOP_CALLBACKS)
        i=0;
      if (i==index) //nothing to do
        return;
      }
    callbackschedtable[i].problem->tslice = callbackschedtable[i].timeslice;

    nwCliUtilizationMark();
    callbackschedtable[i].problem->Run( callbackschedtable[i].cpu_i);
    nwCliUtilizationRelease();
    
    index = ++i;
    if (index>=MAX_IDLELOOP_CALLBACKS)
      index = 0;
    }
  return;
}

static void CliDummyCallbackHandler( void ) //for resetting polling loop counters
{
  return; 
}

static struct callbacksched *CliAllocIdleCallback( Problem *problem, int timeslice, int cpu_i )
{
  static unsigned int nextResetTime = 0;
  struct callbacksched *cbs = NULL;

  #if (MAX_IDLELOOP_CALLBACKS < 1)
  CliSetIsPollingProhibitedFlag(1);  
  #endif
  
  if (CliGetIsPollingProhibitedFlag())  
    return NULL;

  while (callbackschedinproc)  //mutex or semaphore takes too long
    nwCliThreadSwitchLowPriority();
  callbackschedinproc=1;
  
  if (!callbackschedinit)
    {
    for (int i=0;i<MAX_IDLELOOP_CALLBACKS;i++)
      {
      callbackschedtable[i].problem = NULL;
      callbackschedtable[i].run = 0;
      }
    callbackschedrestag = __nwCliGetResourceTag( 'RPLP' ); 
    if (callbackschedrestag) //only assigned once - see __nwCliGetResourceTag()
      {
      if (!_AddPollingProcedureRTag)
        _AddPollingProcedureRTag = (int (*)(void (*)(void),int))ImportSymbol( __nwCliGetNLMHandle(), "AddPollingProcedureRTag" );
      if (!_RemovePollingProcedure)
        _RemovePollingProcedure = (int (*)(void (*)(void)))ImportSymbol( __nwCliGetNLMHandle(), "RemovePollingProcedure" );
      if (_AddPollingProcedureRTag && _RemovePollingProcedure)
        callbackschedinit = -1;
      }
    }
  if (callbackschedinit == -1)
    {
       //don't know if it makes a difference, but set the callback context to
       //nothing, so that the poll-process callback is being forced to switch
       //context the-trillion-or-so times per sec.
    __nwCliClearThreadContextSpecifier();
      //now add the polling process
    if (!(*_AddPollingProcedureRTag)( CliIdleCallbackHandler, callbackschedrestag))
      {
      callbackschedinit = 1;
      }
    }
  if (callbackschedinit == 1)
    {
    for (int i=0;i<MAX_IDLELOOP_CALLBACKS;i++)
      {
      if (callbackschedtable[i].problem == problem)
        {
        callbackschedtable[i].timeslice = timeslice;
        callbackschedtable[i].cpu_i = cpu_i;
        callbackschedtable[i].run = 0;
        cbs = &(callbackschedtable[i]);
        break;
        }
      }
    if (!cbs)
      {
      for (int i=0;i<MAX_IDLELOOP_CALLBACKS;i++)
        {
        if (callbackschedtable[i].problem == NULL)
          {
          callbackschedtable[i].problem = problem;
          callbackschedtable[i].timeslice = timeslice;
          callbackschedtable[i].cpu_i = cpu_i;
          callbackschedtable[i].run = 0;
          cbs = &(callbackschedtable[i]);
          break;
          }
        }
      }
    if (cbs)
      {
      unsigned int i = __nwCliGetCurrentTicks();
      if (CliGetFileWasAccessedFlag() || (i >= nextResetTime))
        {
        nextResetTime = i+(18*60*10); //every ten minutes
        if (!(*_AddPollingProcedureRTag)( CliDummyCallbackHandler, callbackschedrestag))
          {
          (*_RemovePollingProcedure)( CliDummyCallbackHandler );
          }
        }
      }
    }
  callbackschedinproc=0;
  return cbs;
}  

static int CliFreeIdleCallback( Problem *problem, int timeslice, int cpu_i )
{
#ifdef DELETE_CALLBACK_ON_EMPTY
  while (callbackschedinproc)  //mutex or semaphore takes too long
    nwCliThreadSwitchLowPriority();
  callbackschedinproc=1;

  if (callbackschedinit == 1)
    {
    int found = 0;
    for (int i=0;i<MAX_IDLELOOP_CALLBACKS;i++)
      {
      if (callbackschedtable[i].problem == problem)
        {
        callbackschedtable[i].problem = NULL;
        callbackschedtable[i].run = 0;
        }
      else if (callbackschedtable[i].problem != NULL)
        {
        found = 1;
        break;
        }
      }
    if (!found)
      {
#ifdef DELETE_CALLBACK_ON_EMPTY
      callbackschedinit = -1;
      (*_RemovePollingProcedure)( CliIdleCallbackHandler );
#endif      
      }
    }
  callbackschedinproc=0;
#endif
  return 0;  
}    

static void CliUninitPollingProcedures(void) //this must be clib-call free
{
  if (callbackschedinit == 1)
    {
    for (int i=0;i<MAX_IDLELOOP_CALLBACKS;i++)
      callbackschedtable[i].problem = NULL;
    callbackschedinit = -1;
    (*_RemovePollingProcedure)( CliIdleCallbackHandler );
    }
  return;
}  

#endif

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

static struct { int count; } runningcount = {0};

static int _cliRunningCount = 0;
int __nwCliGetRunningCount(void) 
{ return runningcount.count; }
int __nwCliSetRunningCount(int count) 
{ return __PushPopInt( &(runningcount.count), count ); }

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

//static int _cliThreadGroupID = 0; //used in CliInitClient and CliExitClient

extern "C" unsigned char SetCurrentNameSpace( unsigned char );
extern "C" unsigned char SetTargetNameSpace( unsigned char );
extern "C" unsigned int SetCurrentConnection( unsigned int );

int nwCliInitClient( int argc, char *argv[], void *client )
{
DebugPrintf("CliInitClient: beginning\r\n");
  int retcode = 0;

  __nwCliSetClientPointer((Client *)client); //so we can get at filenames and niceness level

  if ((__nwCliSetRunningCount(__nwCliGetRunningCount()+1)) == 0)
    {
    //_cliThreadGroupID = GetThreadGroupID();
    __nwCliGetNLMHandle(); /* initializes it */
    __nwCliSetApplicationName(argv[0]);
    __nwCliSetMainThreadID( GetThreadID() );

    SetCurrentConnection(0); //this is said to be neccesary on NetWare 4.x
    SetCurrentNameSpace(0); //will this help with chdir/getcwd problems?
    SetTargetNameSpace(0); //will this help with chdir/getcwd problems?

    nwCliInitializeThread( 0 ); //clears thread context and sets name
    __nwCliDisableUpgradeLowPriorityThreadsSetting();

    __nwCliInitPaths( argv[0] );
    __nwCliInitThreadSwitchSchemes();

    int autoclose=1;
    int hidden=0;
    int nopoll=0;
    for (int i = 1; i < argc; i++)
      {
      if ( strcmpi(argv[i], "-benchmark" ) == 0 ||
           strcmpi(argv[i], "-benchmark2" ) == 0 ||
           strcmpi(argv[i], "-fetch" ) == 0 || 
           strcmpi(argv[i], "-flush" ) == 0 || 
           strcmpi(argv[i], "-update" ) == 0 || 
           strcmpi(argv[i], "-cpuinfo" ) == 0 || 
           strcmpi(argv[i], "-ident" ) == 0 || 
           strcmpi(argv[i], "-test" ) == 0 )
        { 
        autoclose = 0;
        }
      else if ( strcmpi(argv[i], "-nopoll" ) == 0 )
        {
        argv[i] = "";
        nopoll = 1;
        }
      else if ( strcmpi(argv[i], "-hidden" ) == 0 )
        {
        argv[i] = "";
        hidden = 1;
        autoclose = 1;
        }
      else if ( strcmpi(argv[i], "-showutilization" ) == 0 )
        {
        argv[i] = "";
        utilization.shownormal = 1;
        }
      }
#ifdef ENABLE_POLLING
    if (nopoll)
      CliSetIsPollingProhibitedFlag(1);
#endif
    if (hidden)  
      CliSetIsScreenHiddenFlag(1);
    #ifndef NOAUTODESTROYSCREEN
    if (!autoclose)
    #endif
      CliSetScreenDestructionMode(0); //wait, so that we can see the results

    if (CliInitClientScreen())
      {
      ConsolePrintf("%s: Unable to create client screen. Quitting...\r\n", 
                                              __nwCliGetApplicationName() );
      retcode = -1;                        
      } 
    if (retcode != -1)
      {
      //void CliScreenStatusUpdateProcedure( void ); /* aes process proto */
      //CliSetScreenStatusProcessor( CliScreenStatusUpdateProcedure );
      //moved to problem runner

      /*
      if (!hidden)
        {
        ConsolePrintf( "  Loading module %s.NLM\r\n"
        "  RC5DES v2.%d.%d client - a project of distributed.net\r\n"
        "  Copyright distributed.net 1997-1998\r\n"
        "  Visit http://www.distributed.net/ for more information\r\n",
           __nwCliGetApplicationName(), CLIENT_CONTEST*100 + CLIENT_BUILD, CLIENT_BUILD_FRAC );
        }               
      */
      __nwCliSyncConsoleCursors();
      }
    if (retcode == -1)
      __nwCliSetRunningCount(__nwCliGetRunningCount()-1);


unsigned long t2, t; 
timeval tv, tv2;
FILE *f = fopen("timetest","w+b");
ConsolePrintf("timetest beginning: %8x\r\n", f);
do
  {
  nwCliGetTimeOfDay(&tv);
  nwCliGetTimeOfDay(&tv2);
  t = GetCurrentTime();
  } while (tv.tv_sec == tv2.tv_sec && tv.tv_usec == tv2.tv_usec);
fprintf(f,"%u %u %u:%u %u:%u\r\n",t, time(NULL),tv.tv_sec,tv.tv_usec,tv2.tv_sec,tv2.tv_usec);
t = GetCurrentTime();
while (t == (t2 = GetCurrentTime()))
  nwCliThreadSwitchLowPriority();
t2+=6;  
while (t2 > (t = GetCurrentTime()))
  {
  nwCliGetTimeOfDay(&tv);  
  fprintf(f,"%u %u:%u\r\n",t, tv.tv_sec,tv.tv_usec);
  }
fprintf(f,"%u %u\r\n",t, time(NULL));
fclose(f);
ConsolePrintf("timetest ended: %8x\r\n", f);
return -1;


    }
  return retcode;

DebugPrintf("CliInitClient: ended\r\n");
}

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

int nwCliExitClient(void)       //make sure every function in here is
{                               //clib call free (there is no "threadgroup")
  if (__nwCliGetRunningCount() == 1)
    {
#ifdef ENABLE_POLLING
    CliUninitPollingProcedures();
#endif    
    CliDestroyScreenStatusProcessor();
    __nwCliSyncConsoleCursors();
    __nwCliSetRunningCount(0); 
    }
  return 0;
} 

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

int nwCliSignalHandler( int sig )
{
  int itsAlive;
  unsigned int dieTime = __nwCliGetCurrentTicks() + (30*18); /* 30 secs to die */

  if (sig == SIGABRT )
    ConsolePrintf("RC5DES: Caught an ABORT signal. Please try again after loading MATHLIB.NLM\r\n");

  /*
  if (sig == SIGINT )
    {
    int thrgrp = SetThreadGroupID(_cliThreadGroupID);    
    CliSetScreenDestructionMode(0); //wait, so that we can see the results
    SetThreadGroupID(thrgrp);    
    }
  */

  ConsolePrintf("RC5DES: Client is shutting down...\r\n"); 

  while (__nwCliGetRunningCount()!=0 && __nwCliGetCurrentTicks()<dieTime )
    nwCliThreadSwitchWithDelay(); //non-clib

  if (__nwCliGetRunningCount()) /* If we got here, we're still alive anyway */
    {
    CliActivateConsoleScreen();
    ConsolePrintf("RC5DES: Failed to shutdown gracefully. Forcing exit.\r\n");
    nwCliExitClient(); /* kill everything */
    return -1;
    }
  ConsolePrintf("RC5DES: Client has shut down.\r\n");
  return 0;
}

/* ------------------------- used elsewhere ------------------------ */

#if 0
int CliValidateSinglePath( char *dest, unsigned int destsize,
                  char *defaultval, int defaultnoneifempty, char *source )
{
  static char dir[48+1+256+1];
  unsigned int i;
  char *p, *q, *s;
  const char *invdirmsg = "NetWare::Invalid directory specified for '%s'.\n"
                    "         Reverting to default directory specification\n";
  const char *none = "none";                  

  if (!defaultval) 
    defaultval="";
    
  if (!source || !*source)
    {
    if (defaultnoneifempty)
      {
      strcpy( dest, none );
      return 0;
      }
    if (!*defaultval)
      {
      *dest = 0;
      return 0;
      }
    source = defaultval;
    }
  if ( strcmpi( source, none )==0 )
    {
    if (!defaultnoneifempty)
      *dest = 0;
    else
      strcpy( dest, none );
    return 0;
    }
  if ( ((*source>='A' && *source<='Z') || (*source>='a' && *source<='z')) && 
    source[1]==':' )  //dos
    {
    strcpy( dest, source );
    return 0;
    }

DebugPrintf2("CliValidateSinglePath: source: \"%s\"", source );
    {
    q = source;
    while (*q) 
      {
      if (*q=='\\')
        *q='/';
      q++;
      }
    q = strrchr( source, ':' );
    p = strrchr( source, '/' );

    if (q && q>p)
      {
      p = source+strlen(source)+1;
      while (*p!=':') 
        {
        *p=*(p-1); 
        p--; 
        }
      *(++p)='/';
      }  
    if (!p)
      strcpy(dir,".");
    else
      {
      *p = 0;
      i = chdir( source );
      *p = '/';
      s = source;
      source = p+1;
      if (i!=0 || getcwd( dir, sizeof(dir)-1 )==NULL )
        {
        printf(invdirmsg, s );
        strcpy( dir, "." );
        }
      else if (strcmp( dir, CliGetClientDirectory() )==0)
        strcpy( dir, "." );
      else
        {
        chdir( CliGetClientDirectory() );
        p = strchr( dir, '/' ); //find vol name
        if (memcmp( dir, CliGetClientDirectory(), (p+1)-dir)==0)
          {
          p++;
          q = dir;
          while (*p)
            *q++ = *p++;
          *q=0;
          }
        }
      }
    
    strcat( dir, "/" );
    strcat( dir, source );
    if (strlen(dir) >= destsize)
      {
      printf(invdirmsg, dir );
      strcpy( dir, "./" );
      strcat( dir, defaultval );
      }
    strcpy(dest,dir);
    }

DebugPrintf2("CliValidateSinglePath: dest: \"%s\"\r\n", dest );
  return 0;
}
#endif

/* ------------------------- used elsewhere ------------------------ */

extern "C" int GetFileServerName(int conn, char *buff /*min char[49]*/);

//The NetWare client will run offline if TCP isn't detected, and thus 
//cannot rely on gethostname() being available. It returns the NetWare
//server name instead.
int CliGetHostName( char *buffer, unsigned int buflen )
{
  char *fname = "gethostname";
  int nlmHandle = __nwCliGetNLMHandle();
  unsigned int len;

  int (*_gethostname)(char *, int ) = 
    (int (*)(char *, int))ImportSymbol( nlmHandle, fname );
  if (_gethostname)
    {
    buffer[0] = 0;
    len = 1;
    if (((* _gethostname)(buffer, buflen)) == -1)
      len = 0;
    UnimportSymbol( nlmHandle, fname );
    if (len == 0)
      return -1;
    if (buffer[0] && !isdigit(*buffer))
      {
      char *p = strchr( buffer, '.' );
      if (p) *p = 0;
      }
    len = strlen(buffer);
    }
  else
    {
    char buff[64];
    buff[0]=0;
    GetFileServerName( 0, buff );
    len = strlen( buff );
    if (len>buflen) 
      len = buflen;
    buff[len]=0;
    strlwr( buff );
    strcpy( buffer, buff );
    }
  if (len == 0)
    {
    buffer[0] = 0;
    return -1;
    }
  return 0;
}    

/* ------------------------- used elsewhere ------------------------ */

// stat and fstat have problems if the directory is in cache/locked

int sizonly_stat( const char *filename, struct stat *statblk )
{
  int rescode = -1;
  int file;
  
  CliSetFileWasAccessedFlag();
  
  if (filename && filename[0] && statblk && (access(filename,0)==0) && 
    (file=sopen(filename, O_RDONLY|O_BINARY, SH_DENYNO, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP ))!=-1)
    {                          
    memset(statblk,0,sizeof(struct stat));
    statblk->st_size = filelength(file);  /* fcntl call or lseek */
    if (((signed long)(statblk->st_size))==-1 && lseek(file,0,SEEK_END)!=-1)
      statblk->st_size = tell(file);
    if (((signed long)(statblk->st_size))!=-1)
      rescode=0;
    close(file);
    }
  return rescode;
}

/* ------------------------- used elsewhere ------------------------ */

int unlink_checkpoint( const char *filename )
{
  FileHeader header;
  FILE *file = fopen(filename, "wb" ); //can't use write() for some reason
  if (file) 
    {
    header.lock = htonl( 64+0 );          
    header.count = htonl( 0 );          
    header.count = fwrite( &header, 1, sizeof(FileHeader), file );
    fclose(file);
    if (header.count == sizeof(FileHeader))
      return 0;
    }
  return -1;
}

//extern "C" int ScanErasedFiles( char *path, long *nextEntryNumber, DIR *deletedFileInfo);
//extern "C" int PurgeErasedFile( char *pathname, long sequenceNumber); 

//warning: change d_name to d_nameDOS if using SDK instead of Watcom includes
//double warning!: do not call it unlink! watcom clib remove() calls unlink()
extern "C" int purged_unlink( const char *filename ); /* returns 0 on success */
int purged_unlink( const char *filename ) /* returns 0 on success */
{
  long entrynumber;
  DIR deletedFileInfo;
  DIR *readdirInfo;
  char dirname[255+1];
  int pathlen;
  int retcode=-1;

  if ( ((*filename>='A' && *filename<='Z') || (*filename>='a' && *filename<='z')) && 
    filename[1]==':' )  //dos
    {
    return remove( filename );
    }

  if (strcmp(filename, __nwCliGetClientPointer()->checkpoint_file[0])==0 ||
      strcmp(filename, __nwCliGetClientPointer()->checkpoint_file[1])==0)
    return unlink_checkpoint( filename );

  if ((readdirInfo=opendir(filename))!=NULL)
    {
    if (readdir(readdirInfo)!=NULL)
      {
      if ((retcode=remove( filename ))==0)
        {
        dirname[0]=0;
    
        for (pathlen=strlen(filename);pathlen!=0;pathlen--)
          {
          if (filename[pathlen]=='\\' || filename[pathlen]=='/' || 
                        filename[pathlen]==':')
            {
            if (filename[pathlen]==':')
              pathlen++;
            memcpy(dirname,filename,pathlen);
            dirname[pathlen]=0;
            break;
            }
          }

        entrynumber=-1;
        while (ScanErasedFiles( dirname, &entrynumber, &deletedFileInfo )==0)
          {
          if (strcmp( readdirInfo->d_name, deletedFileInfo.d_name )==0)
            {
            dirname[pathlen]='\\';
            dirname[pathlen+1]=0;
            strcat( dirname, deletedFileInfo.d_name );
            PurgeErasedFile( dirname, entrynumber);
            break;
            }
          }
        }
      }
    closedir(readdirInfo);
    }
  return retcode;
}       

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

static struct 
 {
 unsigned int timeslice;
 unsigned int yieldrate;
 unsigned int kps;
 } syncStats = {0};

static int yieldUp = 0;

static void CliScreenStatusUpdateProcedure( void )
{
  static union {
    char cStatusLine[80<<1];
    struct {
      char scrName[62<<1];
      char count1, cattr1, count2, cattr2, count3, cattr3, count4, cattr4, count5, cattr5;
      char blankx1, bxattr;
      char util1, uattr1, util2, uattr2, util3, uattr3, utuld, utilr;
      char util4, uattr4, util5, uattr5;
      char perc, pattr;
      char blank1, battr1, blank2, battr2;
      char heart, hattr;
      char blank3, battr3;
      } aStatusLine;
    } statusLine;

  static unsigned int hrepeat, lasttime = 0, lastutil;
  unsigned int util = __nwCliGetSystemUtilizationPermille();
  unsigned int time = __nwCliGetCurrentTicks();
  int health = 0;

  if (lasttime == 0)
    {
    statusLine.cStatusLine[0] = 0;
    lastutil = 0;
    hrepeat = 0;
    }
  else if ((time-lasttime) > 9)  //update interval
    {
    if ((++hrepeat)>=10) health = -1;
    }
  else 
    {
    hrepeat=0;
    if ((time-lasttime) < 9)
      health = 1; // should never happen
    }

  char *scrP;
  if ((scrP=CliGetScreenMemPointer())!=NULL)
    {
    int i,n;
    char *p, *r;

    if (CheckExitRequestTriggerNoIO())    
      {
      if (statusLine.cStatusLine[0] != 0)
        {
        for (i=0;i<sizeof(statusLine.cStatusLine);i+=2)
          { *scrP++ = ' '; *scrP++ = 0; }
        statusLine.cStatusLine[0] = 0;
        }
      }
    else 
      {
      if (statusLine.cStatusLine[0] == 0)
        {
        p=CliGetScreenName();
        r=statusLine.cStatusLine;
        for (i=0;i<sizeof(statusLine.cStatusLine);i+=2)
          { *r++=((!*p)?(' '):(*p++)); *r++=(7<<4)+1; }
        lastutil = 0xffff;
        statusLine.aStatusLine.perc  = '%';
        }
      if (lastutil!=util)
        {
        lastutil = util;
        p=&statusLine.aStatusLine.util5;
        statusLine.aStatusLine.util1 = statusLine.aStatusLine.util2 = ' ';
        for (i=0;i<6;i++)
          {
          if (i==2)
            *p--='.';
          else
            {
            *p--=('0'+(util%10));
            if ((util/=10)==0 && i>2)
              break;
            }
          p--;
          }
        }

      statusLine.aStatusLine.heart = ((time%18<9)?(3):(' '));
      i = (7<<4)+((health<0)?(4):((health>0)?(2):(1))); //(blue, red, green)
      statusLine.aStatusLine.uattr1 = statusLine.aStatusLine.uattr2=
         statusLine.aStatusLine.uattr3 = statusLine.aStatusLine.pattr=i;
      statusLine.aStatusLine.hattr=((yieldUp)?((7<<4)+2):(i));  //green or (blue || red)

      p=statusLine.cStatusLine;
      for (i=0;i<(sizeof(statusLine.cStatusLine));i++)
        *scrP++ = *p++;
      }
    }

  lasttime = time;
  return;    
}

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

static int CliCheckForUserBreak(void)
{
  int i;
  if (CheckExitRequestTriggerNoIO())
    return 1;
  if (kbhit() && (((i=getch())==0x0 && getch()==0x03) || i==0x03))
    {
    RaiseExitRequestTrigger();  /* raise(SIGTERM); */
    CliSetScreenDestructionMode(0); //allow screen to stay open
    return 1;
    }
  return 0;
}  


//used externally as poll driven break check
void nwCliCheckForUserBreak(void) { CliCheckForUserBreak(); }


/* ------------------------------------------------------------------- */
#ifdef ENABLE_POLLING

int nwCliRunProblemAsCallback( Problem *problem, int cpu_i, int niceness )
{
  static int hardslice, softslice, nice=-1;   //for speed
  static unsigned int freerun, ver;

  unsigned int timeslice;
  struct callbacksched *cbs;
  u32 perc = problem->CalcPercent();
  u32 run=0; 
  int issmp;

  if (nice==-1)
    {
    CliSetScreenStatusProcessor( CliScreenStatusUpdateProcedure );
    nice = niceness; //CliGetClientPointer()->niceness;
    freerun = 0;
    ver = __nwCliGetFileServerMajorVersionNumber();
    if (nwCliGetNumberOfProcessors()>1) 
      freerun = __nwCliGetCurrentTicks()+(18*60*2); //run full speed for 2 mins
    softslice = GetTimesliceBaseline();
    hardslice = softslice-((softslice/5)*(3-nice));
    softslice = ((softslice+54)/100) << (nice); //was PIPELINE_COUNT << ( 2 + nice );
    //ConsolePrintf("Timeslice on CPU 0: %d ... on other CPUs: %d\r\n", softslice, hardslice );
    }
  timeslice = softslice;

  issmp = __nwCliIsThreadOnMP();
  
  if ( (freerun && cpu_i) || cpu_i >= MAX_IDLELOOP_CALLBACKS || /* nice==2 ||*/ 
     issmp || (cbs=CliAllocIdleCallback(problem, timeslice, cpu_i))==NULL)
    {
    timeslice = hardslice; 
    if (issmp) //if (CliIsThreadOnMP())
      timeslice = 65536; //hardslice * 1000; //thread will be preemptive ("synthetic-preemption")

    DebugPrintf2("CliRunProblemAsCallback: Beginning NON-polling: %d\r\n",perc);

    problem->tslice = timeslice;
    while (!run && (!CliCheckForUserBreak()) &&
            (problem->CalcPercent() == perc))
      {     

      nwCliUtilizationMark();
      run = problem->Run( cpu_i ); 
      nwCliUtilizationRelease();

      nwCliThreadSwitchLowPriority(); //threadswitchlowpriority
      }
    DebugPrintf("ended non-polling\r\n");
    }
  else
    {
    DebugPrintf2("CliRunProblemAsCallback: Beginning polling: %d\r\n",perc);
    while ((run = cbs->run) == 0 && (!CliCheckForUserBreak()) &&
            (problem->CalcPercent() == perc))
      {      
      delay(2000);
      }
    CliFreeIdleCallback( problem, timeslice, cpu_i );  
    DebugPrintf("ended polling\r\n");
    }
  if (freerun && (__nwCliGetCurrentTicks()>=freerun) )
    freerun = 0;
  return run;
}  
#endif
