//
// File: scolsign.cpp
//
// Modification history:
//
//$ FA(17/07/2001): Promoted to C++ file
//$ FA(17/07/2001): Use lexer utility function a2i() as a replacement of Mgetnum()
//$ FA(02/08/2001): mkdir() function for GNU compiler is defined in sys/stat.h
//

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "lexer.h"
extern "C" {
#include "include/vscol.h"
#include "mmemory.h"
#include "mbytec.h"
#include "scol.h"
#include "scolsign.h"
#include "scolpack.h"
#include "scolsys.h"
}
#ifdef VERSION_WIN
#include <direct.h>
#endif

#if defined(VERSION_X11) || defined(VERSION_NOX)
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#endif

#define NBPARTMAX 32
char namepart[NBPARTMAX][128];
char parampart[NBPARTMAX][128];
int ipart=0;

int SPregistPart(char *name,char *param)
{
	if (ipart>=NBPARTMAX) return 0;
	strcpy(namepart[ipart],name);
	strcpy(parampart[ipart++],param);
	return 0;
}

packdir Firstpack=NULL;
packdir Cachepack=NULL;

char cookies[64];

packdir SPallocpackdir()
// Subsumed by Partition::Partition(...)
{
  return (packdir)malloc(SIZEPACKDIR);
}

/* desalloue les packages */
int SPdesallocpackdir()
{
  packdir p,q;

  p=Firstpack;

  while(p)
    {
	  q=p->next;
	  free(p);
	  p=q;
    }
  Cachepack=Firstpack=NULL;
  ipart=0;
  return 0;
}

extern "C" {
  extern char execpath[1024];  // defined in Myloop.c
}

packdir SPcreatedir(char *name,char *param, packdir last)
// Use Partition::Partition(...) instead.
{
  int  c;
  packdir p;

  if ((p=SPallocpackdir())==NULL) return NULL;
  if ((!strncmp(name,"./",2))||(!strncmp(name,".\\",2)))
  {
	  sprintf(p->path,"%s%s",execpath,name+1);
  }
  else strcpy(p->path,name);
  c=p->path[strlen(p->path)-1];
  if ((c!='/')&&(c!='\\')) strcat(p->path,"/");
  if (param[0]==0) 
    p->quota=-1;  // read only
  else {
    int quota;
    a2i(param, &quota);     //$ FA(17/07/2001)
    p->quota = quota*1024;
  }
  p->next=NULL;
  MMechostr(MSKTRACE,"partition %s %d\n",p->path,p->quota);
  
  return p;
}

packdir SPinitpack()
{
  packdir p,q,first;
  int i;

  cookies[0]=0;
  if ((Cachepack)&&(Cachepack==Firstpack)) return Firstpack;
  first=p=NULL;

  for(i=0;i<ipart;i++)
  {
	  q=SPcreatedir(namepart[i],parampart[i],p);
	  if (q)
	  {
		  if (p) p->next=q;
		  else first=q;
		  p=q;
	  }
  }
  Cachepack=first;
  flagLicense=0;
  if (first)
  {
	  if (first->next) Firstpack=first->next;
	  else Firstpack=first;
  }
  return Firstpack;
}

int SPisname(int c, int l)
{
  if ((c>='a')&&(c<='z')) return 1;
  if ((c>='A')&&(c<='Z')) return 1;
  if ((c>='0')&&(c<='9')) return 1;
  if ((c=='_')||(c=='-')) return 1;
  if ((c=='.')&&(l!='.')) return 1;
  if ((c=='/')&&(l!='/')) return 1;
  if ((c=='~')&&(l!='/')) return 1;
  if ((c==' ')||((c&255)>127)) return 1;
  return 0;
}
 
/* calcul de typsign pour un nom de fichier 
   retourne -1 si erreur */
int SPgettypsign(char *total)
{
  int i,l;
  
  i=0;
  l='/';
  while((i<SIZESIGN)&&(SPisname(total[i],l))) l=total[i++];
  if (i>=SIZESIGN) return -1;
  if (total[i]==0) return TYPESNONE;
  else if (total[i]=='#') return TYPESLOGIC;
  else if (total[i]=='$') return TYPESENV;
  else if (total[i]=='&') return TYPESMACH;
  else if (total[i]==';') return TYPESCOOKIES;
  return -1;
}

/* decoupe le nom en clair d'un nom de fichier
   retourne -1 si incorrect, 0 sinon */
int SPgetname(char *total,char *name)
{
  int i,c,l;
  
  i=0;
  l='/';
  while((i<SIZESIGN)&&(SPisname(c=total[i],l))) name[i++]=l=c;
  if (i>=SIZESIGN) return -1;
  name[i]=0;
  return 0;
}

/* complete le chemin d'un package */
int SPrefinePack(packdir p,char *n)
{
	int i,l,c;

	l=strlen(n);
	if (!SPisname(n[0],0)) return -1;
	for(i=1;i<l;i++) if (!SPisname(n[i],n[i-1])) return -1;
	if (p==NULL) return -1;
	strcat(p->path,n);
	c=p->path[strlen(p->path)-1];
	if ((c!='/')&&(c!='\\')) strcat(p->path,"/");
	MMechostr(1,"refine : %s\n",p->path);
	return 0;
}

int SPslashtoback(char *p)
{
	while(*p)
	{
		if ((*p)=='/') *p='\\';
		p++;
	}
	return 0;
}

int SPkillmaj(char *p)
{
	while(*p)
	{
		if (((*p)>='A')&&((*p)<='Z')) (*p)+=0x20;
		p++;
	}
	return 0;
}

/* trouve un package dans la liste des packdir */
int SPfindfile(packdir p,char *sign,int *size,char *path)
{
  int idx,typ,i;
  FILE *fd;
  char buf[SIZESIGN];
  char bufs[1024];

  if (path) path[0]=0;
  if (p==NULL)
    {
      if ((fd=fopen(sign,"rb"))==NULL) return -1;
      if (size)
	  {
		  idx=0;
          while (fgetc(fd)!=EOF) idx++;
	      *size=idx;
      }
      fclose(fd);
      if (path) strcpy(path,sign);
      return 0;
    }
  if ((typ=SPgettypsign(sign))<0) return -1;
  
  while(p)
    {
      sprintf(buf,"%s%s",p->path,sign);
	  SPkillmaj(buf+strlen(p->path));
      if (fd=fopen(buf,"rb"))
        {
		  if (typ==TYPESLOGIC)
		  {
			SCincSignInit();
			do
			{
				i=fread(bufs,1,1024,fd);
			    SCincSign(bufs,i,NULL);
			}
			while(i==1024);
			SPgetname(sign,bufs);
			strcat(bufs,"#");
			SCincSign(NULL,0,&bufs[strlen(bufs)]);
			MMechostr(MSKDEBUG,"test sign %s %s\n",sign,bufs);
			if (stricmp(sign,bufs)) return -1;
		  }
          if (size)
		  {
			  fseek(fd,0,SEEK_SET);
			  idx=0;
              while (fgetc(fd)!=EOF) idx++;
			  *size=idx;
		  }
          fclose(fd);
#ifdef VERSION_WIN
		  SPslashtoback(buf);
#endif
          if (path) strcpy(path,buf);
          return 0;
        }
      p=p->next;
    }
  return -1;
}

/* test l'existence des repertoires d'un chemin complet (repertoire+nom de fichier) */
int SPcheckdirandcreate(char *path)
{
	char buf[SIZESIGN*2];
	int i;

	strcpy(buf,path);
	i=0;
	while(buf[i])
	{
		if (buf[i]=='/')
		{
			buf[i]=0;
/*			MMechostr(MSKDEBUG,"create directory : %s\n",buf);*/
#ifdef VERSION_WIN
			_mkdir(buf);
#endif
#if defined(VERSION_X11) || defined(VERSION_NOX)
			mkdir(buf,-1); 
#endif
#ifdef VERSION_MAC
			TODO
#endif
			buf[i]='/';
		}
		i++;
	}
	return 0;
}

int SPtestfilename(char *p)
{
	int f;
	f=0;
	while(*p)
	{
		if (((*p)=='.')||((*p)=='#')||((*p)==';')||((*p)=='$')||((*p)=='&')) f=1;
		if ((((*p)=='/')||((*p)=='\\'))&&(f)) return -1;
		p++;
	}
	if (f) return 0;
	return -1;
}


/* ajout d'un fichier : trouve le chemin complet a partir du nom complet et 
  de la taille */
int SPaddfile(packdir p,char *sign,int size,char *path)
{
  int typ;

  /* nouveau test sur les noms de fichiers dans le cache */
  if ((Firstpack==Cachepack)&&(SPtestfilename(sign))) return -1;


  if (path) path[0]=0;
  if (p==NULL)
    {
      if (path) strcpy(path,sign);
      return 0;
    }
  if ((typ=SPgettypsign(sign))<0) return -1;

  while(p)
    {
      /* mettre une gestion fine des quotas */
      if (p->quota!=-1)
        {
          if (path)
		  {
			  sprintf(path,"%s%s",p->path,sign);
			  SPkillmaj(path+strlen(p->path));
			  SPcheckdirandcreate(path);
		  }
          return 0;
        }
      p=p->next;
    }
  return -2;
}

/* Signature d'un fichier. version1.0 */
int SCsign(mmachine m,char *src,int n,char *name,int typesign,char *sign)
{
  char buf[32];
  int i,j,k,c,d,p;

  if (typesign==TYPESNONE)
    {
      strcpy(sign,name);
      return 0;
    }
  if (typesign==TYPESLOGIC)
    {
      for(i=0;i<=16;i++) buf[i]=0;
      d=j=0;
      for(i=0;i<n;i++)
	    {
		  c=src[i];
          for(k=0;k<16;k++)
            {
              buf[(j+k)&15]+=d;
              d=d*c+1;
              d=(d+(d>>8))&255;
            }
          j=(j-1)&15;
        }
      for(i=0;i<16;i++)
        {
          c=(buf[i]>>2)&31;
          if (c<10) c+='0'; else c+='a'-10;  
          buf[i]=c;
        }

      sprintf(sign,"%s#%s",name,buf);
      return 0;
    }
  if (typesign==TYPESENV)
    {  
      p=MMfetch(m,MMgetglobal(m,OFFSCCUR)>>1,OFFCHANENV);
      if (MMpush(m,p)) return MERRMEM;
      if (k=SCsignenv(m)) return k;
      sprintf(sign,"%s$%s",name,MMstartstr(m,MMpull(m)>>1));
      return 0;
    }
  if (typesign==TYPESMACH)
    {  
      if (k=SCsignmachine(m)) return k;
      sprintf(sign,"%s&%s",name,MMstartstr(m,MMpull(m)>>1));
      return 0;
    }
  if (typesign==TYPESCOOKIES)
    {  
      sprintf(sign,"%s;%s",name,cookies);
      return 0;
    }
  return -1;
}


/* Signature incrémentale . version1.0 */

char bufIncSign[32];
int lastIncSign;

int SCincSignInit()
{
	int i;
    for(i=0;i<=16;i++) bufIncSign[i]=0;
    lastIncSign=0;
	return 0;
}

int SCincSign(char *buf,int n,char *sign)
{
  int i,j,k,c;

  j=0;
  for(i=0;i<n;i++)
  {
	  c=buf[i];
	  for(k=0;k<16;k++)
	  {
		  bufIncSign[(j+k)&15]+=lastIncSign;
          lastIncSign=lastIncSign*c+1;
          lastIncSign=(lastIncSign+(lastIncSign>>8))&255;
      }
      j=(j-1)&15;
  }
  if (sign)
  {
	  for(i=0;i<16;i++)
      {
		  c=(bufIncSign[i]>>2)&31;
          if (c<10) c+='0'; else c+='a'-10;
          sign[i]=c;
	  }
	  sign[i]=0;
  }
  return 0;
}

