/*     
      SCOL ENVIRONMENT . Magma 1.0 . 1996 . Sylvain HUET

         scol.c : gestion de l'environnement SCOL
*/
// Modification history:
//
//$ FA(19/04/2001): Opcodes are defined in opcode.h
//$ FA(19/04/2001): Take into account renaming of opcodes
//$ FA(03/05/2001): Temporarily move SCexecXXX functions to exec.cpp
//$ FA(21/05/2001): Debugger integration
//$ FA(01/06/2001): Comment MMpull() after call to SCexeccomm()
//$ FA(06/06/2001): Includes baselib.h
//$ FA(06/06/2001): Debugger integration. Support for channel id and channel user name.
//                  Add notification at channel creation
//$ FA(19/06/2001): Debugger integration. Add support for _fooXXXX mirroring to 
//                  debugger's console
//
//$ LB(20/06/2001): Add  "_definesocksVersion" and "_definesocksAuthentication" scol functions 
//                   to make scol able to specify the socks protocol version and the need of authentication
//$ FA(21/06/2001): Initialisation of debuggger client runtime lists moved after 
//                  initialisation of m->maxpp
//
//$ LB(06/07/2001): Add "base64_encode" and "base64_decode" scol functions
//$ ER(09/07/2001): Update of this file according to the new vscol.h version: move of the SERVERM and SERVERS references
//					to the SERVER && (MUTUALISE/!MUTUALISE)
//					Add functions testDureeLicense and CompNbDayTill2000 to check the utilisation period in the case of a server
//					Add call to testDureeLicense in SCisdead
//					Includes time.h
//					Delete of the "int maxscok=MAXSOCKS" declaration, this initialisation is done in the scolsys.c testLicense function
//					Add declaration of MAXSOCKS,MAXSOCKSE,maxsock, VERSION_NAME
//$ FA(23/07/2001): Initialise OFFSCCHNIDX root. Optimise channel search by socket.
//
//$ ER(27/07/2001): Add "setres scol function"
//$ FA(06/08/2001): Use new MMputs() function to echo strings to the log (and console)
//$ FA(12/11/2001): Replace SCOL_DEBUGGER_AWARE by INCLUDE_DEBUGGER
//$ FA(17/01/2002): Modify SCdelcanal() so that channel external objects are not removed, and
//                  socket is not marked as closed (see SCdestroychannel() function)
//$ FA(24/01/2002): Bug correction: see note in SCdelcanal()
//
//
//$LB (13/10/2004) : _loadS : an equivalent to _load, but from a String 
//

#include "scolPrerequisites.h"

#include <stdio.h>
#include <string.h>

#if SCOL_PLATFORM == SCOL_PLATFORM_WINDOWS
//$BLG - v5.2.06: Add
#  include <io.h>
#endif

#include "scolMMemory.h"
#include "vm/mbytec.h"
#include "baselib.h"
#include "listlab.h"
#include "loadpak.h"
#include "mainscol.h"
#include "scol.h"
#include "scolSystem.h"
#include "fifo.h"
#include "common/socket.h"

#include "common/vscol.h"
#if SCOL_PLATFORM == SCOL_PLATFORM_LINUX
#include <sys/time.h>
#include <unistd.h>
#endif
#include "common/common.h"
#include "scolsign.h"
#include "scolpack.h"
#include "scolobj.h"
//$ FA(08/11/2000): Debugger interface
#include "debug.h"
#include "scolMacros.h"
//$ FA(19/04/2001): Opcodes are defined in opcode.h
#include "opcode.h"
//
//$ LB(06/07/2001) : base64 functions
#include "base64.h"
//$ ER(09/07/2001) : time functions
#include "time.h"

#if SCOL_PLATFORM != SCOL_PLATFORM_WINDOWS
#	include "OS_specific/linux/scolFindData.h" 
#endif

#if defined(SERVER)
# define CHANNEL_INDEX_SIZE   307  // bigger hashtable size for server-side VM
#else
# define CHANNEL_INDEX_SIZE   19
#endif
#define CHNIDX_SOCKET         0
#define CHNIDX_CHANNEL        1
#define CHNIDX_NEXT           2


int SCOLuserinit(mmachine m);
int SCinitsyspack(mmachine m);
//$ FA(03/05/2001): Temporarily moved to exec.cpp
int SCexeccommbis(mmachine m, char *comm);
int SCexeccomm(mmachine m,char *comm);
void MMputs(int i, const char* text);
//

//$ FA(23/07/2001): Channel index related functions
uint socketHashCode(int socket);
int channelIndexInsert(mmachine m, SEWORD chn);
int channelIndexRemove(mmachine m, SEWORD chn);
SEWORD channelIndexFind(mmachine m, int socket);
//

//$BLG - v5.2.06: Add
extern int startmode;

int ScolRights=-1;
int ScolSuper=0;
extern int SClocalIPnumber;

//$ ER(09/07/2001): moved from vscol.h, turned from #define constants to variables
int maxsock;
int MAXSOCKS;
int MAXSOCKSE;

//$ ER(10/07/2001): following variables are only used for servers
#ifdef SERVER
short MUTUALISE;
short Debut,Periode;
#endif


//$BLG - v5.2.06: Add (Declared in myloop.cpp)
//extern char cappdatapath2[MAX_PATH];		// Path to Common App Data directory:  commonappdatapath/subdir/
extern char logfilespath2[MAX_PATH];	// Path to log files directory:				localappdatapath/subdir/



//$BLG - v5.2.06: Add
int SClogClear(mmachine m)
{
	int res;
	long h;
	struct _finddata_t fileinfo;
	char buf[512];
	int ret;
	
	//$BLG - v5.2.06: Add
	char* curpath[MAX_PATH];
	char path[1024];
	char buf2[512];
	
	MMechostr(0, "> _logClear() - Start\n");
	
	//$BLG - v5.2.06: Modif (Former version wasn't taking into account cappdatapath2 nor was he verifying directory access.
	//We were simply accessing log/ from scol/)
	/*
	ret = 1;
	sprintf(buf, "log/*.*");
	h = _findfirst(buf, &fileinfo );
	res = h;
	while (res != -1)
	{
		sprintf(buf, "log/%s", fileinfo.name);
		if (!(fileinfo.attrib & _A_SUBDIR))
		{
			if (fileinfo.attrib & _A_RDONLY)
			{
				if (!SetFileAttributes(buf, (fileinfo.attrib ^ _A_RDONLY)))
				{
					MMechostr(0, "  Can't delete file (READ_ONLY): %s\n", buf);
					ret = 0;
				}
				else
					if (!DeleteFile(buf))
					{
						MMechostr(0, "  Can't delete file (^READ_ONLY): %s\n", buf);
						ret = 0;
					}
			}
			else
				if (!DeleteFile(buf))
				{
					MMechostr(0, "  Can't delete file (?IN_USE?): %s\n", buf);
					ret = 0;
				}
		}
		res = _findnext(h, &fileinfo);
	}
	if (h != -1) _findclose(h);
	*/
	ret = 1;
	GetCurrentDirectory(MAX_PATH, curpath);
	//$BLG - v5.2.06: Modif
	/*
	strcpy(path, cappdatapath2);
	strcat(path, "log/");
	*/
	strcpy(path, logfilespath2);
	if (!SetCurrentDirectory(path))
	{
		MMechostr(0, "Can't access log files directory\n");
		ret = 0;
	}
	else
	{
		//$BLG - v5.2.06: Modif
		//strcpy(buf, cappdatapath2);
		//strcat(buf, "log/*.*");
		strcpy(buf, logfilespath2);
		strcat(buf, "*.*");
		h = _findfirst(buf, &fileinfo );
		res = h;
		while (res != -1)
		{
			//$BLG - v5.2.06: Modif
			//strcpy(buf, cappdatapath2);
			//sprintf(buf2, "log/%s", fileinfo.name);
			strcpy(buf, logfilespath2);
			sprintf(buf2, "%s", fileinfo.name);
			strcat(buf, buf2);
			if (!(fileinfo.attrib & _A_SUBDIR))
			{
				if (fileinfo.attrib & _A_RDONLY)
				{
					if (!SetFileAttributes(buf, (fileinfo.attrib ^ _A_RDONLY)))
					{
						MMechostr(0, "  Can't delete file (READ_ONLY): %s\n", buf);
						ret = 0;
					}
					else
						if (!DeleteFile(buf))
						{
							MMechostr(0, "  Can't delete file (^READ_ONLY): %s\n", buf);
							ret = 0;
						}
				}
				else
					if (!DeleteFile(buf))
					{
						MMechostr(0, "  Can't delete file (?IN_USE?): %s\n", buf);
						ret = 0;
					}
			}
			res = _findnext(h, &fileinfo);
		}
		if (h != -1) _findclose(h);
	}
	
  //$BB restore current path
  SetCurrentDirectory(curpath);
	MMpush(m, ret<<1);
	
	MMechostr(0, "< _logClear() - End\n");
	
	return 0;
}


static int foo(mmachine m, char* s)
{
  MMputs(MSKFOO, s); //$ FA(06/08/2001): use new MMputs() function
#if defined(INCLUDE_DEBUGGER)
  {
    int res;

    if (res = Mpushstrbloc(m, s))
      return res;
    if (res = DBGEcho(m))
      return res;
  }
#endif
  return MERROK;
}


int SCfooS(mmachine m)
{
  int i;

  i = MMget(m,0);
  if (i == NIL)
    return foo(m, "NIL\n");
  else {
      int res;
      if (res = foo(m, MMstartstr(m, i>>1)))
        return res;
      return foo(m, "\n");
  }
  return 0;
}

//$BLG
//As _fooI but with decimal ouput
//Note: There seems to be a 9 digits limit (+ '-' char if needed).
//      This has nothing to do with buffer size but seems to come
//      from sprintf(). Tried with "%ld" for Long Int but without
//      expected result. The problem is present in other log access
//      and data conversion functions.
int SCfooId(mmachine m)
{
  int i;

  i = MMget(m,0);
  if (i == NIL)
    return foo(m, "NIL\n");
  else 
  {
    char s[10];
    int res;
    sprintf(s, "%d", SEW2I(i));
    if (res = foo(m, s))
      return res;
    return foo(m, "\n");
  }
  return 0;
}

int SCfooI(mmachine m)
{
  int i;

  i=MMget(m,0);
  if (i==NIL)
    return foo(m, "NIL\n");
  else {
    char s[10];
    int res;
    sprintf(s, "%x", SEW2I(i));
    if (res = foo(m, s))
      return res;
    return foo(m, "\n");
  }
  return 0;
}


int SCfooIList(mmachine m)
{
  int i,v,res;

  i=MMget(m,0);
  while (i!=NIL)
  {
      v=MMfetch(m,i>>1,OFFLVAL);
      if (v==NIL) {
         if (res = foo(m, "NIL:"))
           return res;
      } else {
        char s[10];
        sprintf(s, "%x", SEW2I(v));
        if (res = foo(m, s))
          return res;
        if (res = foo(m, ":"))
          return res;
      }
      i=MMfetch(m,i>>1,OFFLNEXT);
  }
  return foo(m, "NIL\n");
}

int SCfooSList(mmachine m)
{
  int i,v,res;

  i=MMget(m,0);
  while (i!=NIL)
  {
      v=MMfetch(m,i>>1,OFFLVAL);
      if (v==NIL) {
        if (res = foo(m, "NIL:"))
          return res;
      } else {
        if (res = foo(m, MMstartstr(m,v>>1)))
          return res;
        if (res = foo(m, ":"))
          return res;
      }
      i=MMfetch(m,i>>1,OFFLNEXT);
  }
  return foo(m, "NIL\n");
}


int SCstarterScript(mmachine m)
{
	  return Mpushstrbloc(m,userscript);
}


int MaskEcho=-1;

int Msearchinsyspak(mmachine m, char *name);

extern int indexHard;




//$LB (22/04/2002)
#if SCOL_PLATFORM == SCOL_PLATFORM_LINUX
extern int _globalTickcount;
#endif


/* initialisation de l'environnement */
int SCinit(mmachine m,int sizemem)
{
  int k;


  //$LB (22/04/2002) : avoid negative value of tickcount
#if SCOL_PLATFORM == SCOL_PLATFORM_LINUX
   struct timeval tm;
   gettimeofday(&tm,NULL);
   _globalTickcount = (tm.tv_sec*1000)+(tm.tv_usec/1000);
#endif


  //$BLG - v5.11: Modif
  //MMechostr(0,"masque d'affichage : %x\n",MaskEcho);
  MMechostr(0,"Log and console display mask : 0x%x\n",MaskEcho);
	
	//$BLG - v5.11: Modif
  //MMechostr(MSKTRACE,"initialisation de scol\n");
  MMechostr(MSKTRACE,"Virtual Machine initialization\n");
  if (k=MMinimachine(m,sizemem)) return k;

  OBJinit();

//$ FA(23/07/2001)
  SECHECK(SEPUSH(m, NIL)); // channel index (by socket number)
//$ FA(08/12/2000)
  SECHECK(SEPUSH(m, NIL)); // machine name
  SECHECK(SEPUSH(m, NIL)); // debugger client runtime lists
//
  
  //$BLG - v5.11: Modif
  //MMechostr(MSKTRACE,"recherche des repertoires de packages\n");
  MMechostr(MSKTRACE,"Looking for Scol Partitions\n");
  Cachepack=NULL;
  SPinitpack();
  //$BLG - v5.21: Add
  MMechostr(MSKTRACE,"Scol Partitions scan complete\n");

  SClocalIPnumber=0;
//  if ((ScolRights&M_RIGHTS)==0) SCcacheActivate(m);

	
	//$BLG - v5.11: Modif
  //MMechostr(MSKTRACE,"chargement des packages systeme\n");
  MMechostr(MSKTRACE,"\n> Loading Scol system packages\n\n");
  
  //$BLG - v5.3.03: Modif (The lckdGC parameter management has been moved from hardload.c to encompass the whole init process)
  //if (k=SCinitsyspack(m)) return k;
  m->lckdGC = 1;
  if (k=SCinitsyspack(m)) return k;
  m->lckdGC = 0;
  
  //$BLG - v5.11: Modif
  //MMechostr(MSKTRACE,"fin chargement des packages systeme = %d\n",indexHard);
  MMechostr(MSKTRACE,"\n\nScol system packages loaded = %d\n\n\n",indexHard);
  MMechostr(MSKTRACE,"------------------------------------\n\n");

  if (MMpush(m,NIL)) return MERRMEM; /* liste serveurs */
  if (MMpush(m,NIL)) return MERRMEM; /* liste objets */
  if (MMpush(m,NIL)) return MERRMEM; /* liste ressources */
  if (MMpush(m,NIL)) return MERRMEM; /* liste canaux */
  if (MMpush(m,NIL)) return MERRMEM; /* canal courant */
  m->maxpp=m->pp;
  
//$ FA(21/06/2001): Initialise debugger client
  if (k = DBGInit(m))
    return k;
//

  return 0;
}

//$ ER(09/07/01)
/* Renvoie le nombre de jour depuis l'annee 2000 */
/* Prend en compte les annees bisextilles */
int CompNbDayTill2000( int Year )
{
	int sum=0,a ;
	for (a=0; a<Year; a++)
		sum+=365+((a % 4)==0 ? 1 : 0);

	return sum;

}


//$ ER(09/07/01)
// Compare current date to utilisation period
// Return 1 if in the period else 0
#ifdef SERVER
int testDureeLicense(void)
{
	int NbDay1970_2000=10957;
	int Limit, Current;
	time_t MyTime;


	time(&MyTime);

	Limit=(CompNbDayTill2000(Debut/365)+Periode);
	Current=(MyTime/86400)-NbDay1970_2000;

	return (Current <= Limit);
}
#endif


/* test de fin de machine */
/* Effectue en même temps l'appel à la fonction de comparaison de la date courante et de la période d'utilisation */
int SCisdead(mmachine m)
{

//$ ER(09/07/01) : call to testDureeLicense to compare current date to the utliisation period
//				   The result is quite lethal since the server acts as if the socket life is down and so shut down
#ifdef SERVER
  if (Periode && !testDureeLicense())
  {
	  MMechostr(MSKTRACE,"Connection impossible pour cause de date limite\n");
		return 1;
  }
#endif


  if (m->err) return m->err;
  if ((MMgetglobal(m,OFFSCSERVER)==NIL)
      &&(MMgetglobal(m,OFFSCCHAN)==NIL))
    return 1;
  return 0;
}

/* encodage des droits dans une chaine de caracteres */
int SCendcodeRights(char *c,int i)
{
    if (i&C_RIGHTS) *(c++)='C';
    if (i&S_RIGHTS) *(c++)='S';
    if (i&D_RIGHTS) *(c++)='D';
    if (i&M_RIGHTS) *(c++)='M';
    if (i&R_RIGHTS) *(c++)='R';
    if (i&W_RIGHTS) *(c++)='W';
    if (i&K_RIGHTS) *(c++)='K';
    *c=0;
    return 0;
}
    
/* definition des droits a partir d'une chaine */
int SCdefineRights(char *c)
{
    int k;

    k=0;
    while(*c)
    {
        if ((*c)=='C') k|=C_RIGHTS;
        if ((*c)=='S') k|=S_RIGHTS;
        if ((*c)=='D') k|=D_RIGHTS;
        if ((*c)=='M') k|=M_RIGHTS;
        if ((*c)=='R') k|=R_RIGHTS;
        if ((*c)=='W') k|=W_RIGHTS;
        if ((*c)=='K') k|=K_RIGHTS;
        c++;
    }
    ScolRights=k;
    return 0;
}


/* creation d'un fichier core */
int SCcore(mmachine m)
{
  return MMpush(m,MMcore(m)*2);
}

/* creation d'une application ex-nihilo */
int SCcreateapplibis(mmachine m,char *corres,char *script)
{
  int k;

  if (k=Mpushstrbloc(m,corres)) return k;
  if (k=Mpushstrbloc(m,script)) return k;
  if (MMpush(m,MMgetglobal(m,OFFSCBASE))) return MERRMEM;
  if (k=SCopenchannel3(m)) return k;
  MMpull(m);
  return 0;
}

int SCcreateappli(mmachine m,char *corres,char *script)
{
  int pp,k;

  pp=MMgetPP(m);
  k=SCcreateapplibis(m,corres,script);
  MMsetPP(m,pp);
  return k;
}

/* retourne le nombre de canaux non unplugged */
int SCgetnbsocket(mmachine m)
{
  int p,n,s;

  n=0;
  p=MMgetglobal(m,OFFSCCHAN);
  while(p!=NIL)
  {
      p>>=1;
      s=SCgetsocket(m,MMfetch(m,p,OFFLVAL));
      if ((s>=0)&&(s!=socklife)&&(!SCtestudp(m,MMfetch(m,p,OFFLVAL)))) n++;
      p=MMfetch(m,p,OFFLNEXT);
  }
/*  MMechostr(MSKTRACE,"nbsockets=%d\n",n);*/
  return n;
}



uint socketHashCode(int socket)
//$ FA(23/07/2001): Return hash code associated to channel socket
{
  uint key = (uint)socket;

  // Since unplugged sockets are already evenly distributed,
  // we do nothing. We apply the hash function to plugged
  // sockets only
  if (key > 0) {
    // Use Thomas Wang's 32-bit mix function
    key += ~(key << 15);
    key ^=  (key >> 10);
    key +=  (key << 3);
    key ^=  (key >> 6);
    key += ~(key << 11);
    key ^=  (key >> 16);
  }
  //MMechostr(MSKTRACE, ">>>>> socket hash code = %u\n", key);
  return key;
}


int channelIndexInsert(mmachine m, SEWORD chn)
//$ FA(23/07/2001): Add channel reference into channel index
{
  uint  bucket;
  int   socket;
  SEINT i;

  SECHECK(SEPUSH(m, chn)); // save channel reference
  i = SEGETSP(m);
  //MMechostr(MSKTRACE, ">>> Inserting channel reference %x into index...\n", SEGET(m, i));
  socket = SCgetsocket(m, SEGET(m, i));
  bucket = socketHashCode(socket) % CHANNEL_INDEX_SIZE;
  //MMechostr(MSKTRACE, ">>>>> bucket = %d\n", bucket);
  // Allocate hashtable bucket array if channel index is nil
  if (SEGETROOT(m, OFFSCCHNIDX) == NIL) {
    SEPTR res;
    int i;
    if ((res = MMmalloc(m, CHANNEL_INDEX_SIZE, TYPETAB)) < 0)
      return res;
    // Initialise all hashtable entries to nil
    for (i = 0; i < CHANNEL_INDEX_SIZE; i++)
      SESTORE(m, res, i, NIL);
    //MMechostr(MSKTRACE, ">>>>> index allocated; root = %x\n", SEP2W(res));
    SESETROOT(m, OFFSCCHNIDX, SEP2W(res)); 
  }
  // Add channel reference into the beginning of the bucket list
  SECHECK(SEPUSH(m, SEI2W(socket)));
  SECHECK(SEPUSH(m, SEGET(m, i)));
  SECHECK(SEPUSH(m, SEFETCH(m, SEW2P(SEGETROOT(m, OFFSCCHNIDX)), bucket)));
  SECHECK(SEPUSH(m, SEI2W(3)));
  SECHECK(SENEWTUPLE(m));
  //MMechostr(MSKTRACE, ">>>>> hash table entry %x\n", SEGETTOP(m, 0));
  SESTORE(m, SEW2P(SEGETROOT(m, OFFSCCHNIDX)), bucket, SEPOP(m));
  SEDROP(m, 1); // drop saved channel reference
  return MERROK;
}


int channelIndexRemove(mmachine m, SEWORD chn)
// FA  23/07/2001  Remove channel reference from channel index
// FA  17/01/2002  Socket is not marked here as closed (see SCdestroychannel())
{
  uint   bucket;
  SEWORD cur;
  SEWORD prv;

  //MMechostr(MSKTRACE, ">>> Removing channel reference %x from index...\n", chn);
  bucket = socketHashCode(SCgetsocket(m, chn)) % CHANNEL_INDEX_SIZE;
  //MMechostr(MSKTRACE, ">>>>> bucket = %d\n", bucket);
  cur = SEFETCH(m, SEW2P(SEGETROOT(m, OFFSCCHNIDX)), bucket);
  prv = NIL;
  while (cur != NIL) {
    SEPTR p = SEW2P(cur);
    if (SEFETCH(m, p, CHNIDX_CHANNEL) == chn) {
      //MMechostr(MSKTRACE, ">>>>> found channel at entry %x\n", cur);
      // Remove index entry from list
      if (prv == NIL)
        SESTORE(m, SEW2P(SEGETROOT(m, OFFSCCHNIDX)), bucket, SEFETCH(m, p, CHNIDX_NEXT));
      else
        SESTORE(m, SEW2P(prv), CHNIDX_NEXT, SEFETCH(m, p, CHNIDX_NEXT)); 
      break;
    }
    prv = cur;
    cur = SEFETCH(m, p, CHNIDX_NEXT);
  }
  return MERROK;
}


SEWORD channelIndexFind(mmachine m, int socket)
//$ FA(23/07/2001): Find the channel for the given socket
{
  uint   bucket;
  SEWORD cur;
  SEWORD chn = NIL;

  if (socket != -1) {
    //MMechostr(MSKTRACE, ">>> Searching for channel...\n");
    bucket = socketHashCode(socket) % CHANNEL_INDEX_SIZE;
    //MMechostr(MSKTRACE, ">>>>> bucket = %d\n", bucket);
    cur = SEFETCH(m, SEW2P(SEGETROOT(m, OFFSCCHNIDX)), bucket);
    while (cur != NIL) {
      SEPTR p = SEW2P(cur);
      if (SEW2I(SEFETCH(m, p, CHNIDX_SOCKET)) == socket) {
        //MMechostr(MSKTRACE, ">>>>> found channel at entry %x\n", cur);
        return SEFETCH(m, p, CHNIDX_CHANNEL);
      }
      cur = SEFETCH(m, p, CHNIDX_NEXT);
    }
  }
  return NIL;
}


int SCaddcanal(mmachine m)
// Add current channel into the list of channels
//$ FA(23/07/2001): Rewritten
{
//$ FA(18/06/2001): Do not add debugger's channel into the list
#if defined(INCLUDE_DEBUGGER)
  int res;
  if (debug.sock == -1)
    return MERROK;
#endif
//
  SECHECK(SEPUSH(m, SEGETROOT(m, OFFSCCUR)));
  SECHECK(SEPUSH(m, SEGETROOT(m, OFFSCCHAN)));
  SECHECK(SEPUSH(m, SEI2W(2)));
  SECHECK(SENEWTUPLE(m));
  SESETROOT(m, OFFSCCHAN, SEPOP(m));

//$ FA(23/07/2001): Add channel into index
  SECHECK(channelIndexInsert(m, SEGETROOT(m, OFFSCCUR)));
//$ FA(29/11/2000): Notify debugger that a new channel has been opened
#if defined(INCLUDE_DEBUGGER)
  SECHECK(SEPUSH(m, SEGETROOT(m, OFFSCCUR)));
  if (res = DBGNotifyChannelOpened(m))
    return res;
#endif
//
  return MERROK;
}


int SCdelcanal(mmachine m, SEWORD chn)
// Deletes the channel from the root channel list
// FA  23/07/2001  Rewritten
// FA  17/01/2002  Channel external objects are removed by caller (SCdestroychannel())
// FA  24/01/2002  Pointer to 'chn' invalid if SCsendCapacity() caused a GC
{
  SEWORD cur = SEGETROOT(m, OFFSCCHAN); //SH000808
  SEWORD prv = NIL;
  
  while (cur != NIL) {
    SEPTR p = SEW2P(cur);
    if (SEHEAD(m, p) == chn) {
      if (prv == NIL)
        SESETROOT(m, OFFSCCHAN, SETAIL(m, p));
      else
        SESETTAIL(m, SEW2P(prv), SETAIL(m, p));
      // Save channel, as the following code may cause a GC
      SECHECK(SEPUSH(m, chn)); //$ FA(24/01/2002)
//$ ER(09/07/01) Turn #ifdef SERVERM into the following line
#ifdef SERVER
		  if (!ScolSuper && !MUTUALISE) 
        SCsendCapacity(m);
#endif
//$ FA(23/07/2001): Remove channel reference from index
      SECHECK(channelIndexRemove(m, SEPOP(m)));
//
      return MERROK;
    }
    prv = cur;
    cur = SETAIL(m, p);
  }
  return MERROK;
}


/* ajout d'un serveur
   le serveur se trouve dans la pile */
int SCaddserver(mmachine m)
{
  int k;

  if (MMpush(m,MMgetglobal(m,OFFSCSERVER))) return MERRMEM;
  if (MMpush(m,2*2)) return MERRMEM;
  if (k=MBdeftab(m)) return k;
  MMsetglobal(m,OFFSCSERVER,MMpull(m));
  return 0;
}

/* suppression d'un server de la liste des server */
int SCdelserver(mmachine m,int server)
{
  int p,q;

  //SH000808
  p=MMgetglobal(m,OFFSCSERVER)>>1;
  q=NIL;
  while(p!=NIL)
  {
	  if (MMfetch(m,p,OFFLVAL)==server)
	  {
		  if (q==NIL) MMsetglobal(m,OFFSCSERVER,MMfetch(m,p,OFFLNEXT));
		  else MMstore(m,q,OFFLNEXT,MMfetch(m,p,OFFLNEXT));
		  return 0;
	  }
	  q=p;
	  p=MMfetch(m,p,OFFLNEXT)>>1;
  }
  //
  return 0;
}

/* dump canaux */
int SCdumpcanal(mmachine m)
{
  int p;

  MMechostr(MSKFOO,">>Dump\n");
  p=MMgetglobal(m,OFFSCCHAN);
  while(p!=NIL)
  {
      p>>=1;
      MMechostr(1,">%d\n",SCgetsocket(m,MMfetch(m,p,OFFLVAL)));
      p=MMfetch(m,p,OFFLNEXT);
  }
  MMechostr(MSKFOO,">>DumpEnd\n");
  return 0;
}


int SCselectcanal(mmachine m, int socket)
// Selects as current the channel corresponding to the given socket
//$ FA(23/07/2001): Rewritten and optimised 
{
  SEWORD chn = channelIndexFind(m, socket);
  if (chn != NIL) {
    SESETROOT(m, OFFSCCUR, chn);
    return MERROK;
  }
  // Oops!
  SESETROOT(m, OFFSCCUR, NIL);
  MMechostr(MSKFOO, "(!) There is no channel for socket %d\n", socket);
  return -1;
}


/* retourne le nombre de canaux unplugged */
int SCgetnbsocketu(mmachine m)
{
  int p,n;

  n=0;
  p=MMgetglobal(m,OFFSCCHAN);
  while(p!=NIL)
  {
      p>>=1;
      if (SCgetsocket(m,MMfetch(m,p,OFFLVAL))<0) n++;
      p=MMfetch(m,p,OFFLNEXT);
  }
  return n;
}

/* positionne le serveur courant par le numero de socket */
int SCgetserver(mmachine m, int socket)
{
  int p;

  p=MMgetglobal(m,OFFSCSERVER);
  while(p!=NIL)
  {
      p>>=1;
      if (SCgetsrvsocket(m,MMfetch(m,p,OFFLVAL))==socket)
      {
          MMsetglobal(m,OFFSCCUR,MMfetch(m,p,OFFLVAL));
          return 0;
      }
      p=MMfetch(m,p,OFFLNEXT);
  }
  MMsetglobal(m,OFFSCCUR,NIL);
  return -1;
}

extern int maxsock; /*FLIC*/

#ifdef SERVERS
int SCsendCapacity(mmachine m);
#endif

int cbsendfull(int i,int s)
{
	int k;
	char buf[256];

	strcpy(buf,"__FullServer");
	k=strlen(buf)-2;
	buf[0]=k&255;
	buf[1]=(k>>8)&255;
	SCKsend(s,buf,k+2);
	SCKclose(s);
	mt_del(i);
	return 0;
}

/* creation d'un canal serveur */
int SCacceptbis(mmachine m,int srv)
{
  int k,s,ip,port,t;
  char buf[128];

//$ ER(09/07/01) : comment: the following is not very useful anymore, because maxsock is now initialized just in the calling fucntion
  //$BLG - v5.11: Del
	//maxsock is initialized as MAXSOCKS and never modified (cf testLicence in scolsys.cpp)
	//except in case of ReduceCapacity (but this reduces the value so ...)
  //if (maxsock+1>MAXSOCKS+1) return -1; /*FLIC*/
  
  s=(int)SCKaccept(srv,&ip,&port); /* accepter la connexion socket */
  if (s<0) return 0;
  MMechostr(MSKTRACE,"Connection from %x:%x\n",ip,port);
  if ( ((!ScolSuper)&&(SCgetnbsocket(m)>=maxsock)) ||
      ((ScolSuper)&&(SCgetnbsocket(m)>=MAXSOCKSE)) )
  {
	  MMechostr(MSKRUNTIME,"Scol Full\n");
	  mt_start(1000,s,cbsendfull);
	  if (!ScolSuper)
	  {
		  if ((k=MMpush(m,NIL))) return k;
		  t=MMgetglobal(m,OFFSCCUR)>>1;
		  if ((k=MMpush(m,MMfetch(m,t,OFFSRVENV)))) return k; /* env.canal*/
		  if ((k=MMpush(m,2*2))) return k;
		  if ((k=MBdeftab(m))) return k;
		  MMsetglobal(m,OFFSCCUR,MMpull(m));
		  if (k=SCexeccomm(m,"_fullserver")) return k;
//		  MMpull(m);
	  }
	  return 0;
  }

  if (k=SCmakefifo(m)) return k;            /* bloc.fifo */
  
  t=MMgetglobal(m,OFFSCCUR)>>1;
  if (MMpush(m,MMfetch(m,t,OFFSRVENV))) return MERRMEM; /* env.canal*/
//$ FA(08/11/2000)
  SECHECK(SEPUSH(m, NIL));              // user channel name (unspecified)
//$ FA(23/11/2000)
  SECHECK(SEPUSH(m, SEI2W(chnID++)));   // assign channed id
//
  if (MMpush(m,SIZECHAN*2)) return MERRMEM;
  if (k=MBdeftab(m)) return k;

  SCsetsocket(m,MMget(m,0),s);   /* reglage de la socket */
  if (k=Mpushstrbloc(m,SCKgetstringbyip(buf,ip,port))) return k;
  SCsetsockname(m,MMget(m,1)); /* reglage du nom */

  t=MMgetglobal(m,OFFSCCUR)>>1;
  MMsetglobal(m,OFFSCCUR,MMget(m,0));
  MMset(m,0,MMfetch(m,t,OFFSRVSCRIPT));
  if (k=SCscript(m)) return k;
  MMpull(m);

  if (k=SCaddcanal(m)) return k;
//$ ER(09/07/01) : turning of #ifdef SERVERM by the following
#ifdef SERVER
  if ((!ScolSuper)&&(!MUTUALISE)) SCsendCapacity(m);
#endif

  return 0;
}

int SCaccept(mmachine m,int s)
{
  int pp,k;

  pp=MMgetPP(m);
  k=SCacceptbis(m,s);
  MMsetPP(m,pp);
  return k;
}

int SCexecch(mmachine m)
{
	int k,z;

	if ((MMget(m,1)==NIL)||(MMget(m,2)==NIL))
	{
		MMpull(m); MMpull(m);
		MMset(m,0,NIL);
		return 0;
	}
	z=MMget(m,2);
	MMset(m,2,MMgetglobal(m,OFFSCCUR));
	MMsetglobal(m,OFFSCCUR,z);

	k=Minterpreter2(m,kExeccall);
    if (k!=1) return k;
	MMsetglobal(m,OFFSCCUR,MMget(m,1));
	MMset(m,1,MMget(m,0));
	MMpull(m);
	return 0;
}

int SCswitch(mmachine m)
{
	int p,q,l;
	p=MMpull(m);
	l=MMpull(m);
	while(l!=NIL)
	{
		q=MMfetch(m,l>>1,0);
		if ((q!=NIL)&&(MMfetch(m,q>>1,0)==p))
			return MMpush(m,MMfetch(m,q>>1,1));
		l=MMfetch(m,l>>1,1);
	};
	return MMpush(m,NIL);
}

int SCswitchstr(mmachine m)
{
	int p,q,r,l;
	char *c;
	p=MMpull(m);
	l=MMpull(m);
	if (p!=NIL) c=MMstartstr(m,p>>1);
	while(l!=NIL)
	{
		q=MMfetch(m,l>>1,0);
		if (q!=NIL)
		{
			r=MMfetch(m,q>>1,0);
			if (((r==NIL)&&(p==NIL))||((p!=NIL)&&(!strcmp(c,MMstartstr(m,r>>1)))))
				return MMpush(m,MMfetch(m,q>>1,1));
		}
		l=MMfetch(m,l>>1,1);
	};
	return MMpush(m,NIL);
}

int SCswitchstri(mmachine m)
{
	int p,q,r,l;
	char *c;
	p=MMpull(m);
	l=MMpull(m);
	if (p!=NIL) c=MMstartstr(m,p>>1);
	while(l!=NIL)
	{
		q=MMfetch(m,l>>1,0);
		if (q!=NIL)
		{
			r=MMfetch(m,q>>1,0);
			if (((r==NIL)&&(p==NIL))||((p!=NIL)&&
				(!stricmp(c,MMstartstr(m,r>>1)))
				))
				return MMpush(m,MMfetch(m,q>>1,1));
		}
		l=MMfetch(m,l>>1,1);
	};
	return MMpush(m,NIL);
}

int SCtest_exist(mmachine m)
{
	int p,lp;

	p=MMpull(m);
	if (p==NIL) return MMpush(m,NIL);
	lp=MMgetglobal(m,OFFSCCUR);
	if (lp==NIL) return MMpush(m,0);
	lp=MMfetch(m,lp>>1,OFFCHANENV);
	if (lp==NIL) return MMpush(m,0);
	if (NIL==Msearchinpak(m,lp,MMstartstr(m,p>>1))) return MMpush(m,0);
	return MMpush(m,2);
}

int SCsetLogMask(mmachine m)
{
	MaskEcho=MMget(m,0)>>1;
	return 0;
}

int SCisFirstScol(mmachine m)
{
	return MMpush(m,((int)firstScol)*2);
}


//$BLG - v5.2.06: Add
int SCisStartMode(mmachine m)
{
	return MMpush(m, startmode<<1);
}


int SCplatform(mmachine m)
{
  // 0 was "Linux No X11", deleted
	return MMpush(m, SCOL_PLATFORM*2);
}

int SCscreendepth(mmachine m)
{
#if SCOL_PLATFORM == SCOL_PLATFORM_WINDOWS
	DEVMODE d;

	d.dmSize=sizeof(DEVMODE);
	d.dmDriverExtra=0;
	if (EnumDisplaySettings(NULL,ENUM_CURRENT_SETTINGS,&d))
		return MMpush(m,d.dmBitsPerPel*2);
#endif
	return MMpush(m, 0);
}

	
//$ LB : NSCOLSYS : 122 -> 124 : added SCdefinesocksVersion and SCdefinesocksAuthentication functions
//$ LB(06/07/2001) : add base64 scol functions
// $LB (07/06/2004) : add strcpy 
//$LB (13/10/2004) : _loadS
//$BLG - v5.24:   (137) Add _filemd5
//$BLG - v5.2.06: (138) Add _cacheClear
//$BLG - v5.2.06: (139) Add _logClear
//$BLG - v5.2.06: (140) Add _getScolProcesses
//$BLG - v5.2.06: (141) Add _isStartMode

#define NSCOLSYS 145

char* scolname[NSCOLSYS]=
{"_script","_scriptc","_load","_channel",
 "Env","_closemachine","_envchannel","_setenv",
 "_openchannel","_setserver","_closeserver","_closechannel",
 "_killchannel","_checkpack","_storepack","_getpack",
 //$BLG - v5.22: Add
 "_uncypherpack",
 
 "_loadhard","I","S","F",
 "_waitingfifo","_sizewaitingfifo","_channeltime","Comm",
 "_on","P","_getlongname","_servertime",
 "_channelname","Srv","Chn","_channelIP",
 "_channelport","_gethostbyname","_getnamebyIP","_fooS",
 //$BLG Start: _fooId
 "_fooId",
 //$BLG End
 "_fooI","_hostname","_hostIP","_refine",
 "C_Rights","S_Rights","D_Rights","M_Rights",
 "R_Rights","W_Rights","K_Rights","_getrights",
 "_setrights","_sizememory","_newmachine","_masterchannel",
 "_setsocklife","_showconsole","_hideconsole","_signenv",
 "_signmachine","_openbrowserhttp","_version","_getress",
 "_setress","_loadressini","W","_getmodifypack",
 "_createpack","_appendpack","_WtoP","_removepkg",
 "_setsizefifo","_chgchn","_fooSList","_saveressini",
 "_loadusmini","_saveusmini","_defineproxy","_openbrowserhttpP",
 "_setUDP","_sendUDP","_fooIList","_newmachineS",
 "_setchannel","_envfirstname","_freememory","_tickcount",
 //$BLG - v4.6a5: Add
 "_trimmemory",
 //$BLG - v5.11: Add _getcpuusage
 "_getcpuusage",
 //$BLG - v5.2.06: Add
 "_getScolProcesses",

 "_onX","_setX","_versionname","_cacheActivate",
 
 //$BLG Add
 "_isCacheActivated",
 
 //$BLG - v5.2.06: Add
 "_cacheClear",
 "_logClear",
 
 //$BLG: v4.6a7 - Add
 "_versiondate",

 "_setCookies","_starterScript","_sendUDPchn","_listoffiles",
 "_listofsubdir","_username","_computername","_copyFile",
 "execch","switch","switchstr","switchstri",
 "_reduceCapacity","_fileSign","_test_exist","_setLogMask",
 "_fileSize","_isFirstScol","_platform","_screendepth",
 "_refine_nth","_movecache","_hostIPall","_setLocalIPnumber",
 "_autodetectproxy","_definesocks","_startExe","_refreshExe",
 "_getExe","_addExe","_delExe","_deletepack",
 "_mix8","_mix16",
 //$ LB
 "_definesocksVersion", "_definesocksAuthentication",
 //
 //$ LB(06/07/2001): Add base64 scol functions
 "base64_encode", "base64_decode",
 //
 "_getValidityPeriod",
 //$ ER(27/07/2001): setuser
 "_setuser",
 //$ FA(06/08/2001)
 "_getmodifdate",
 //$BB test pak
 "_testpak",
 "_testpakS",
 //$LB (13/10/2004)
 "_loadS",
 //$BLG - v5.24: Add
 "_filemd5",
 //$BLG - v5.2.06: Add
 "_isStartMode",
 //$BB
 "_addPartition",
 "_removeLastPartition"
 };


int (*scolfun[NSCOLSYS])(mmachine m)=
{SCscript,SCscriptc,SPloadpak,SCchannel,
 NULL,SCclosemachine,SCenvchannel,SCsetenv,
 SCopenchannel,SCsetserver,SCcloseserver,SCclosechannel,
 SCkillchannel,SPcheckpack,SPstorepack,SPgetpack,
 //$BLG - v5.22: Add
 SPuncypherpack,
 
 SCloadhard,NULL,NULL,NULL,
 SCwaitingfifo,SCsizewaitingfifo,SCchanneltime,NULL,
 SCon,NULL,SPgetlongname,SCservertime,
 SCchannelname,NULL,NULL,SCchannelIP,
 SCchannelport,SCgethostbyname,SCgetnamebyIP,SCfooS,
 //$BLG Start: _fooId
 SCfooId,
 //$BLG End
 SCfooI,SChostname,SChostIP,SPrefinePartition,
 (void*)2,(void*)4,(void*)8,(void*)16,
 (void*)32,(void*)64,(void*)128,SCgetrights,
 SCsetrights,SCsizememory,SCnewmachine,(void*)-1,
 SCsetsocklife,SCshowconsole,SChideconsole,SCsignenv,
 SCsignmachine,SCopenbrowserhttp,SCversion,SCgetress,
 SCsetress,SCloadressini,NULL,SPgetmodifypack,
 SPcreatepack,SPappendpack,SPWtoP,SCremovepkg,
 SCsetsizefifo,SCchangeChn,SCfooSList,SCsaveressini,
 SCloadusmini,SCsaveusmini,SCdefineproxy,SCopenbrowserhttpP,
 SCsetUDPserver,SCsendudp,SCfooIList,SCnewmachineS,
 SCsetchannel,SCenvfirstname,SCfreememory,SCtickcount,
 //$BLG - v4.6a5:Add
 SCtrimmemory,
 //$BLG - v5.11: Add _getcpuusage
 SCgetcpuusage,
 //$BLG - v5.2.06: Add
 SCgetScolProcesses,

 SConX,SCsetX,SCversionname,SCcacheActivate,

 //$BLG Add
 SCisCacheActivated,
 
 //$BLG - v5.2.06: Add
 SCcacheClear,
 SClogClear,
  
 //$BLG: v4.6a7 - Add
 SCversiondate,
 
 SPsetCookies,SCstarterScript,SCsendudpchn,SClistoffiles,
 SClistofsubdir,SCusername,SCcomputername,SCcopyFile,
 SCexecch,SCswitch,SCswitchstr,SCswitchstri,
 SCreduceCapacity,SPfileSign,SCtest_exist,SCsetLogMask,
 SPfileSize,SCisFirstScol,SCplatform,SCscreendepth,
 SPrefineNthPartition,SCmovecache,SChostIPall,SCsetLocalIPnumber,
 SCdefaultproxy,SCdefinesocks,SCstartExe,SCrefreshExe,
 SCgetExe,SCaddExe,SCdelExe,SPdeletepack,
 SCmix8,SCmix16,
 //$ LB
 SCdefinesocksVersion, SCdefinesocksAuthentication,
 //
 //$ LB(06/07/2001) : base64 functions
 SCbase64_encode, SCbase64_decode,
 //
 SCreadTrace,
 //$ ER(27/07/2001) : setuser function
 SCsetuser,
 //$ FA(06/08/2001)
 SPGetModifDate,
 //$BB test pack
 SPtestpak,
 SPtestpakS,
 //$LB (13/10/2004)
 SPloadpakS,
 //$BLG - v5.24: Add
 SPfilemd5,
 //$BLG - v5.2.06: Add
 SCisStartMode,
 //$BB
 SPaddPartition,
 SPremovePartition
};





int scolnarg[NSCOLSYS]=
{1,2,1,0,
 0,0,1,2,
 3,3,1,0,
 1,1,2,1,
 //$BLG - v5.22: Add (_uncypherpack)
 2,
 
 1,TYPTYPE,TYPTYPE,TYPTYPE,
 1,1,1,TYPTYPE,
 2,TYPTYPE,3,1,
 1,TYPTYPE,TYPTYPE,1,
 1,1,1,1,
 //$BLG Start: _fooId
 1,
 //$BLG End
 1,0,0,1,
 TYPVAR,TYPVAR,TYPVAR,TYPVAR,
 TYPVAR,TYPVAR,TYPVAR,0,
 1,0,4,TYPVAR,
 1,0,0,1,
 0,1,0,1,
 2,0,TYPTYPE,1,
 2,2,1,1,
 2,2,1,1,
 1,1,3,1,
 3,2,1,3,
 1,1,0,0,
 //$BLG - v4.6a5:Add _trimmemory
 0,
 //$BLG - v5.11: Add _getcpuusage
 0,
 //$BLG - v5.2.06: Add _getScolProcesses
 0,
 
 1,1,0,0,
 
 //$BLG Add: isCacheActivated
 0,
 
 //$BLG - v5.2.06: Add _cacheClear, _logClear
 0,
 0,
 
 //$BLG: v4.6a7 - _versiondate
 0,
 
 //$BLG Stop
 1,0,3,1,
 1,0,0,2,
 3,2,2,2,
 1,1,1,1,
 1,0,0,0,
 2,1,0,1,
 0,2,3,0,
 1,2,2,1,
 2,2,
 //$ LB
 1,1,
 //
 //$ LB(06/07/2001) : base64 functions
 1,1,
 //
 2,
 //$ ER(27/07/2001) : setuser function
 1,
 //$ FA(06/08/2001): SPGetModifDate 
 1,
 //$BB test pak
 1,
 1,
 //$LB (13/10/2004) : _loadS
 1,
 //$BLG - v5.24: Add _filemd5
 1,
 //$BLG - v5.2.06: Add _isStartMode
 0,
 //$BB
 2,
 0
};




char* scoltype[NSCOLSYS]=
{"fun [S] I","fun [Chn S] I","fun [S] I","fun [] Chn",
 NULL,"fun [] I","fun [Chn] Env","fun [Chn Env] I",
 "fun [S S Env] Chn","fun [Env I S] Srv","fun [Srv] I","fun [] I",
 "fun [Chn] I","fun [S] P","fun [S S] I","fun [P] S",
 //$BLG - v5.22: Add (_uncypherpack)
 "fun [S S] I",
 
 "fun [S] I",NULL,NULL,NULL,
 "fun [Chn] I","fun [Chn] I","fun [Chn] I",NULL,
 "fun [Chn Comm] I",NULL,"fun [S S S] S","fun [Srv] I",
 "fun [Chn] S",NULL,NULL,"fun [Chn] S",
 "fun [Chn] I","fun [S] S","fun [S] S","fun [S] S",
 //$BLG Start: _fooId
 "fun [u0] u0",
 //$BLG End
 "fun [u0] u0","fun [] S","fun [] S","fun[S] S",
 "I","I","I","I",
 "I","I","I","fun [] I",
 "fun [I] I","fun [] I","fun [S S I I] I","Chn",
 "fun [Chn] Chn","fun [] I","fun [] I","fun [Env] S",
 "fun [] S","fun [S] S","fun [] I","fun [S] S",
 "fun [S S] S","fun [] S",NULL,"fun [S] W",
 "fun [S W] I","fun [S W] I","fun [W] P","fun [Env] Env",
 "fun [Chn I] Chn","fun [Chn Chn] Chn","fun [[S r1]] [S r1]","fun [S] S",
 "fun [S] S","fun [S] S","fun [S S S] I","fun [P] P",
 "fun [Env I S] Chn","fun [S Comm] I","fun [[u0 r1]] [u0 r1]","fun [P I I] I",
 "fun [Chn] Chn","fun [Env] S","fun [] I","fun [] I",
 //$BLG - v4.6a5:Add _trimmemory
 "fun [] I",
 //$BLG - v5.11: Add _getcpuusage
 "fun [] I",
 //$BLG - v5.2.06: Add _getScolProcesses
 "fun [] I",
 
 "fun [Comm] Comm","fun [Chn] Chn","fun [] S","fun [] I",
 
 //$BLG Add: _isCacheActivated
 "fun [] I",
 
 //$BLG - v5.2.06: Add: _cacheClear, _logClear
 "fun [] I",
 "fun [] I",
 
 //$BLG: v4.6a7 - _versiondate
 "fun [] I",
 
 "fun [S] S","fun [] S","fun [Chn S Comm] I","fun [S] [S r1]",
 "fun [S] [S r1]","fun [] S","fun [] S","fun [P S] P",
 "fun [Chn fun u0 u1 u0] u1","fun [[[u0 u1] r1] u0] u1","fun [[[S u0] r1] S] u0","fun [[[S u0] r1] S] u0",
 "fun [I] I","fun [P] S","fun [S] I","fun [I] I",
 "fun [P] I","fun [] I","fun [] I","fun [] I",
 "fun[S I] S","fun[S] S","fun[][S r1]","fun[I] I",
 "fun[] [S S S]","fun[S S] I","fun[S S S] S","fun[] I",
 "fun[S] [S r1]","fun[S P] I","fun[S S] I","fun[P] P",
 "fun[[[I S] r1] I] [S I]","fun[[[I S] r1] I] [S I]",
 //$ LB
 "fun [S] I", "fun [S] I",
 //
 //$ LB(06/07/2001) : base64 functions
 "fun [S] S", "fun [S] S",
 //
 "fun [S S] [I I]",
 //$ ER(27/07/2001) : setuser function
 "fun [S] I",
 //$ FA(06/08/2001): SPGetModifDate 
 "fun [P] [I I I I I I I I]",
 //$BB test pak
 "fun [S] S",
 "fun [S] S",
 //$LB (13/10/2004) : _loadS
 "fun [S] I",
 //$BLG - v5.24: Add _filemd5
 "fun [P] S",
 //$BLG - v5.2.06: Add _isStartMode
 "fun [] I",
 //$BB
 "fun [S I] I", //_addPartition
 "fun [] I"       //_removeLastPartition
 };


int SCOLloadSYS(mmachine m)
{
    int k;
	k=PKhardpak(m,"scolsys.pkg",NSCOLSYS,scolname,scolfun,scolnarg,scoltype);
    return k;
}





//$BLG
// ****************************************************************************
// Windows Registry Management
// Key location is relative to HKEY_LOCAL_MACHINE/SOFTWARE/Scol-Technologies/
// ****************************************************************************


#define WINREG_STR_HKLM "HKEY_LOCAL_MACHINE"
#define WINREG_LEN_HKLM (sizeof(WINREG_STR_HKLM)-1)
#define WINREG_STR_SOFT "SOFTWARE"
#define WINREG_LEN_SOFT (sizeof(WINREG_STR_SOFT)-1)
#define WINREG_STR_SCOL "Scol-Technologies"
#define WINREG_LEN_SCOL (sizeof(WINREG_STR_SCOL)-1)


// Creating Key ***************************************************************

// fun [S-KeyName] I-ZeroOrNil
int SCWINREG_CreateKey(mmachine m)
{
// No registry in linux!
#if SCOL_PLATFORM == SCOL_PLATFORM_WINDOWS
  int p, size;
  
  char *szKey;
  HKEY hKey, hSubKey;

  // Recovering parameters
  p = MMpull(m)>>1;;
  // Key cannot have a NIL value
  if (p == NIL)    return MMpush(m, NIL);
  szKey = MMstartstr (m, p);
  size = MMsizestr (m, p);
  // Key cannot have a "" value
  if (size == 0)   return MMpush(m, NIL);
  
  // Opening HKLM/SOFTWARE
  hKey = HKEY_LOCAL_MACHINE;
  if (RegOpenKeyEx(hKey, WINREG_STR_SOFT, 0, KEY_READ, &hSubKey) == ERROR_SUCCESS)
  {
    RegCloseKey(hKey);
    hKey = hSubKey;
  }
  else
  {
    RegCloseKey(hKey);
    hKey = NULL;
    return MMpush(m, NIL);
  }

  // Creating/Opening Scol-Technologies
  if (RegCreateKeyEx(hKey, WINREG_STR_SCOL, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hSubKey, NULL) == ERROR_SUCCESS)
  {
    RegCloseKey(hKey);
    hKey = hSubKey;
  }
  else
  {
    RegCloseKey(hKey);
    hKey = NULL;
    return MMpush(m, NIL);
  }

  // Creating/Opening Key
  if (RegCreateKeyEx(hKey, szKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hSubKey, NULL) == ERROR_SUCCESS)
  {
    RegCloseKey(hKey);
    hKey = hSubKey;
    RegCloseKey(hKey);
    return MMpush (m, 0);
  }
  else
  {
    RegCloseKey(hKey);
    hKey = NULL;
    return MMpush(m, NIL);
  }
#elif SCOL_PLATFORM == SCOL_PLATFORM_LINUX
  // TODO LINUX
  return MMpush(m, NIL);
#endif
}

// No registry in linux!
#if SCOL_PLATFORM == SCOL_PLATFORM_WINDOWS
// Deleting Key ***************************************************************

int WinReg_DelNodeRecurse(HKEY hKeyRoot, LPTSTR lpSubKey)
{
  LPTSTR lpEnd;
  long lResult;
  DWORD dwSize;
  TCHAR szName[MAX_PATH];
  HKEY hKey;
  FILETIME ftWrite;

  // First, see if we can delete the key without having to recurse.
  lResult = RegDeleteKey(hKeyRoot, lpSubKey);
  if (lResult == ERROR_SUCCESS) 
    return 1;

  lResult = RegOpenKeyEx (hKeyRoot, lpSubKey, 0, KEY_READ, &hKey);
  if (lResult != ERROR_SUCCESS) 
    return 0;

  // Check for an ending slash and add one if it is missing.
  lpEnd = lpSubKey + lstrlen(lpSubKey);
  if (*(lpEnd - 1) != TEXT('\\')) 
  {
    *lpEnd =  TEXT('\\');
    lpEnd++;
    *lpEnd =  TEXT('\0');
  }

  // Enumerate the keys
  dwSize = MAX_PATH;
  lResult = RegEnumKeyEx(hKey, 0, szName, &dwSize, NULL, NULL, NULL, &ftWrite);
  if (lResult == ERROR_SUCCESS) 
  {
    do 
    {
      lstrcpy (lpEnd, szName);
      if (!WinReg_DelNodeRecurse(hKeyRoot, lpSubKey)) 
      {
        break;
      }
      dwSize = MAX_PATH;
      lResult = RegEnumKeyEx(hKey, 0, szName, &dwSize, NULL, NULL, NULL, &ftWrite);
    } while (lResult == ERROR_SUCCESS);
  }

  lpEnd--;
  *lpEnd = TEXT('\0');

  RegCloseKey (hKey);

  // Try again to delete the key.
  lResult = RegDeleteKey(hKeyRoot, lpSubKey);
  if (lResult == ERROR_SUCCESS) 
    return 1;
  return 0;
}


int WinReg_DelNode(HKEY hKeyRoot, LPTSTR lpSubKey)
{
  TCHAR szDelKey[2 * MAX_PATH];

  lstrcpy (szDelKey, lpSubKey);
  return WinReg_DelNodeRecurse(hKeyRoot, szDelKey);
}
#elif SCOL_PLATFORM == SCOL_PLATFORM_LINUX
  // TODO LINUX
#endif

// fun [S-KeyName] I-ZeroOrNil
int SCWINREG_DeleteKey(mmachine m)
{
// No registry in linux!
#if SCOL_PLATFORM == SCOL_PLATFORM_WINDOWS
  int p, size;
  
  char *szKey;
  HKEY hKey, hSubKey;

  int iSuccess;

  // Recovering parameters
  p = MMpull(m)>>1;;
  // Key cannot have a NIL value
  if (p == NIL)    return MMpush(m, NIL);
  szKey = MMstartstr (m, p);
  size = MMsizestr (m, p);
  // Key cannot have a "" value
  if (size == 0)   return MMpush(m, NIL);
  
  // Opening HKLM/SOFTWARE
  hKey = HKEY_LOCAL_MACHINE;
  if (RegOpenKeyEx(hKey, WINREG_STR_SOFT, 0, KEY_READ, &hSubKey) == ERROR_SUCCESS)
  {
    RegCloseKey(hKey);
    hKey = hSubKey;
  }
  else
  {
    RegCloseKey(hKey);
    hKey = NULL;
    return MMpush (m, NIL);
  }

  // Opening Scol-Technologies
  if (RegOpenKeyEx(hKey, WINREG_STR_SCOL, 0, KEY_READ, &hSubKey) == ERROR_SUCCESS)
  {
    RegCloseKey(hKey);
    hKey = hSubKey;
  }
  else
  {
    RegCloseKey(hKey);
    hKey = NULL;
    return MMpush (m, NIL);
  }

  // Deleting Key
  iSuccess = WinReg_DelNode(hKey, szKey);
  RegCloseKey(hKey);
  hKey = NULL;
  if (iSuccess)
    return MMpush (m, 0);
  else 
    return MMpush (m, NIL);
#elif SCOL_PLATFORM == SCOL_PLATFORM_LINUX
  // TODO LINUX
  return MMpush (m, NIL);
#endif
}


// Adding/Setting value *******************************************************

// fun [S-KeyName S-ValueName I-ValueContentOrNil S-NilOrValueContent] I-ZeroOrNil
int SCWINREG_SetValue(mmachine m)
{
// No registry in linux!
#if SCOL_PLATFORM == SCOL_PLATFORM_WINDOWS
  int p, sizep, q, sizeq, r, s, sizes;
  
  char *szKey, *szVal, *szParam;
  HKEY hKey, hSubKey;

  // Recovering parameters
  s = MMpull(m)>>1;          // Parameter: S or I
  r = MMpull(m)>>1;
  q = MMpull(m)>>1;          // Value
  p = MMpull(m)>>1;          // Key
  // Key cannot have a NIL value
  if (p == NIL)    return MMpush(m, NIL);
  szKey = MMstartstr (m, p);
  sizep = MMsizestr (m, p);
  // Value cannot have a NIL value
  if (q == NIL)    return MMpush(m, NIL);
  szVal = MMstartstr (m, q);
  sizeq = MMsizestr (m, q);
  // Value cannot have a "" value
  if (sizeq == 0)  return MMpush(m, NIL);
  // Parameters cannot both have a value or be set to NIL
  if (((r == NIL) && (s == NIL)) || ((r != NIL) && (s != NIL)))      return MMpush(m, NIL);
  if (s != NIL)
  {
  	szParam = MMstartstr (m, s);
  	sizes = MMsizestr (m, s);
  }
  
  // Opening HKLM/SOFTWARE
  hKey = HKEY_LOCAL_MACHINE;
  if (RegOpenKeyEx(hKey, WINREG_STR_SOFT, 0, KEY_READ, &hSubKey) == ERROR_SUCCESS)
  {
    RegCloseKey(hKey);
    hKey = hSubKey;
  }
  else
  {
    RegCloseKey(hKey);
    hKey = NULL;
    return MMpush(m, NIL);
  }

  // Opening Scol-Technologies
  // if (RegOpenKeyEx(hKey, WINREG_STR_SCOL, 0, KEY_READ, &hSubKey) == ERROR_SUCCESS)
  // Creating/Opening Scol-Technologies
  // In case the user tries to Set/Add a Value at the Scol root registry entry, we ensure
  // that the root entry is present.
  if (RegCreateKeyEx(hKey, WINREG_STR_SCOL, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hSubKey, NULL) == ERROR_SUCCESS)
  {
    RegCloseKey(hKey);
    hKey = hSubKey;
  }
  else
  {
    RegCloseKey(hKey);
    hKey = NULL;
    return MMpush(m, NIL);
  }

  // Opening Key
  // We try to open it only if it isn't ""
  if (sizep != 0)
  {
	  if (RegOpenKeyEx(hKey, szKey, 0, KEY_SET_VALUE, &hSubKey) == ERROR_SUCCESS)
	  {
	    RegCloseKey(hKey);
	    hKey = hSubKey;
	  }
	  else
	  {
	    RegCloseKey(hKey);
	    hKey = NULL;
	    return MMpush(m, NIL);
	  }
	}

  // Setting Value
  if (r != NIL)
  {
  	if (RegSetValueEx(hKey, szVal, 0, REG_DWORD, (LPBYTE) &r, sizeof(DWORD)) != ERROR_SUCCESS) 
  	{
      RegCloseKey(hKey);
      hKey = NULL;  		
  		return MMpush(m, NIL);
  	}
  }
  if (s != NIL)
  {
  	if (RegSetValueEx(hKey, szVal, 0, REG_SZ, (LPBYTE) szParam, (DWORD) lstrlen(szParam)+1) != ERROR_SUCCESS) 
  	{
      RegCloseKey(hKey);
      hKey = NULL;  		
  		return MMpush(m, NIL);
  	}  	
  }
  RegCloseKey(hKey);
  hKey = NULL; 
  return MMpush (m, 0);
#elif SCOL_PLATFORM == SCOL_PLATFORM_LINUX
  // TODO LINUX
  return MMpush (m, NIL);
#endif
}


// Getting value **************************************************************

// fun [S-KeyName S-ValueName] [I-ValueContentOrNil S-NilOrValueContent]
int SCWINREG_GetValue(mmachine m)
{
// No registry in linux!
#if SCOL_PLATFORM == SCOL_PLATFORM_WINDOWS
  int p, sizep, q, sizeq, dataType, r;
  int sizer = 2048;
  
  char *szKey, *szVal, *szParam;
  HKEY hKey, hSubKey;

  int iSuccess;

  // Recovering parameters
  q = MMpull(m)>>1;          // Value
  p = MMpull(m)>>1;          // Key
  // Key cannot have a NIL value
  if (p == NIL)    return MMpush(m, NIL);
  szKey = MMstartstr (m, p);
  sizep = MMsizestr (m, p);
  // Value cannot have a NIL value
  if (q == NIL)    return MMpush(m, NIL);
  szVal = MMstartstr (m, q);
  sizeq = MMsizestr (m, q);
  // Value cannot have a "" value
  if (sizeq == 0)  return MMpush(m, NIL);
  
  // Opening HKLM/SOFTWARE
  hKey = HKEY_LOCAL_MACHINE;
  if (RegOpenKeyEx(hKey, WINREG_STR_SOFT, 0, KEY_READ, &hSubKey) == ERROR_SUCCESS)
  {
    RegCloseKey(hKey);
    hKey = hSubKey;
  }
  else
  {
    RegCloseKey(hKey);
    hKey = NULL;
    return MMpush(m, NIL);
  }

  // Opening Scol-Technologies
  if (RegOpenKeyEx(hKey, WINREG_STR_SCOL, 0, KEY_READ|KEY_QUERY_VALUE, &hSubKey) == ERROR_SUCCESS)
  {
    RegCloseKey(hKey);
    hKey = hSubKey;
  }
  else
  {
    RegCloseKey(hKey);
    hKey = NULL;
    return MMpush(m, NIL);
  }

  // Opening Key
  // We try to open it only if it isn't ""
  if (sizep != 0)
  {
	  if (RegOpenKeyEx(hKey, szKey, 0, KEY_QUERY_VALUE, &hSubKey) == ERROR_SUCCESS)
	  {
	    RegCloseKey(hKey);
	    hKey = hSubKey;
	  }
	  else
	  {
	    RegCloseKey(hKey);
	    hKey = NULL;
	    return MMpush(m, NIL);
	  }
  }

  // Getting Data type and then corresponding Value
  if (RegQueryValueEx(hKey, szVal, NULL, (LPDWORD) &dataType, NULL, (LPDWORD) &sizer) != ERROR_SUCCESS)
  {
    RegCloseKey(hKey);
    hKey = NULL;  		
 	  return MMpush(m, NIL);
  }
  if (dataType == REG_DWORD)
  {
    if (RegQueryValueEx(hKey, szVal, NULL, NULL, (LPBYTE) &r, (LPDWORD) &sizer) != ERROR_SUCCESS)
    {
      RegCloseKey(hKey);
      hKey = NULL;  		
 	    return MMpush(m, NIL);
    }
    if (MMpush(m, r<<1)) return MERRMEM;
    if (MMpush(m, NIL))  return MERRMEM;
    if (MMpush(m, 4))    return MERRMEM;
  }
  else if (dataType == REG_SZ)
  {
    szParam = (char*) malloc(sizer);
    if (szParam == NULL) 
    {
      RegCloseKey(hKey);
      hKey = NULL;  		
 	    return MERRMEM;
    }
    if (RegQueryValueEx(hKey, szVal, NULL, NULL, (LPBYTE) szParam, (LPDWORD) &sizer) != ERROR_SUCCESS)
    {
      free(szParam);
      RegCloseKey(hKey);
      hKey = NULL;  		
 	    return MMpush(m, NIL);
    }
    if (MMpush(m, NIL))           return MERRMEM;
    if (Mpushstrbloc(m, szParam)) return MERRMEM;
    free(szParam);
    if (MMpush(m, 4))             return MERRMEM;
  }
  else
  {
    RegCloseKey(hKey);
    hKey = NULL;  		
 	  return MMpush(m, NIL);
  }
  
  iSuccess = MBdeftab(m);

  RegCloseKey(hKey);
  hKey = NULL; 
  return iSuccess;
#elif SCOL_PLATFORM == SCOL_PLATFORM_LINUX
  // TODO LINUX
  return MMpush (m, NIL);
#endif
}


// Deleting value *************************************************************

// fun [S-KeyName S-ValueName] I-ZeroOrNil
int SCWINREG_DelValue(mmachine m)
{
// No registry in linux!
#if SCOL_PLATFORM == SCOL_PLATFORM_WINDOWS
  int p, sizep, q, sizeq;
  
  char *szKey, *szVal;
  HKEY hKey, hSubKey;

  // Recovering parameters
  q = MMpull(m)>>1;          // Value
  p = MMpull(m)>>1;          // Key
  // Key cannot have a NIL value
  if (p == NIL)    return MMpush(m, NIL);
  szKey = MMstartstr (m, p);
  sizep = MMsizestr (m, p);
  // Value cannot have a NIL value
  if (q == NIL)    return MMpush(m, NIL);
  szVal = MMstartstr (m, q);
  sizeq = MMsizestr (m, q);
  // Value cannot have a "" value
  if (sizeq == 0)  return MMpush(m, NIL);
  
  // Opening HKLM/SOFTWARE
  hKey = HKEY_LOCAL_MACHINE;
  if (RegOpenKeyEx(hKey, WINREG_STR_SOFT, 0, KEY_READ, &hSubKey) == ERROR_SUCCESS)
  {
    RegCloseKey(hKey);
    hKey = hSubKey;
  }
  else
  {
    RegCloseKey(hKey);
    hKey = NULL;
    return MMpush(m, NIL);
  }

  // Opening Scol-Technologies
  if (RegOpenKeyEx(hKey, WINREG_STR_SCOL, 0, KEY_READ|KEY_SET_VALUE, &hSubKey) == ERROR_SUCCESS)
  {
    RegCloseKey(hKey);
    hKey = hSubKey;
  }
  else
  {
    RegCloseKey(hKey);
    hKey = NULL;
    return MMpush(m, NIL);
  }

  // Opening Key
  // We try to open it only if it isn't ""
  if (sizep != 0)
  {
	  if (RegOpenKeyEx(hKey, szKey, 0, KEY_SET_VALUE, &hSubKey) == ERROR_SUCCESS)
	  {
	    RegCloseKey(hKey);
	    hKey = hSubKey;
	  }
	  else
	  {
	    RegCloseKey(hKey);
	    hKey = NULL;
	    return MMpush(m, NIL);
	  }
	}

  // Deletting Value
	if (RegDeleteValue(hKey, szVal) != ERROR_SUCCESS) 
 	{
    RegCloseKey(hKey);
    hKey = NULL;  		
		return MMpush(m, NIL);
 	}

  RegCloseKey(hKey);
  hKey = NULL; 
  return MMpush (m, 0);
#elif SCOL_PLATFORM == SCOL_PLATFORM_LINUX
  // TODO LINUX
  return MMpush (m, NIL);
#endif
}


// ****************************************************************************


#define NSCOL_WINREG 5

char* scol_winreg_name[NSCOL_WINREG]=
{
	"_winreg_createkey",
	"_winreg_deletekey",
	"_winreg_setvalue",
	"_winreg_getvalue",
	"_winreg_delvalue"
	
};


int (*scol_winreg_fun[NSCOL_WINREG])(mmachine m)=
{
	SCWINREG_CreateKey,
	SCWINREG_DeleteKey,
	SCWINREG_SetValue,
	SCWINREG_GetValue,
	SCWINREG_DelValue
};


int scol_winreg_narg[NSCOL_WINREG]=
{
	1,
	1,
	4,
	2,
	2
};


char* scol_winreg_type[NSCOL_WINREG]=
{
	"fun [S] I",
	"fun [S] I",
	"fun [S S I S] I",
	"fun [S S] [I S]",
	"fun [S S] I"
};


// ****************************************************************************


//Note: This function is called in SCinitsyspack() : kernel45/src/win/hardload.c
int SCOLloadWinReg(mmachine m)
{
  int k;
	
	k=PKhardpak(m, "scol_winreg.pkg", NSCOL_WINREG, scol_winreg_name, scol_winreg_fun, scol_winreg_narg, scol_winreg_type);
  
  return k;
}
