/************************************************************
**
** Application: SyncDiscs
**
** Title:       c.arcs
**
*************************************************************/

/*
*
* Copyright (c) 2017, David Pilling and Chris Johnson
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the copyright holder nor the names of their
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/

/* Include files */
/* from standard clib */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>



#include "Tasklib:h.swis"
#include "Tasklib:h.os"
#include "Tasklib:h.bbc"
#include "Tasklib:h.flex"
#include "Tasklib:h.alloc"
#include "Tasklib:h.etc"
#include "Tasklib:h.fsx"


/* from application */
#include "h.zstring"
#include "h.arcs"



/************************************************************/

/* Local defines */

#define MINENTRY 4


/**************************************************************************/


/* exported variables */

int  *  posn;
int  *  lno;
int  *  fpos;
char ** obje;

int  *  posn2;
int  *  lno2;
int  *  fpos2;


/************************************************************/
/* global variables */

static int maxlevel;

#define LEVELCHUNK 32


/************************************************************/

os_error * levelstackcheck(int level)
{
 os_error * err;
 int        nextlevel;

 err=NULL;

 if(level>=maxlevel)
 {
  nextlevel=maxlevel+1+LEVELCHUNK;

           err=flex_extend((flex_ptr)&posn,nextlevel*sizeof(int));
  if(!err) err=flex_extend((flex_ptr)&lno,nextlevel*sizeof(int));
  if(!err) err=flex_extend((flex_ptr)&fpos,nextlevel*sizeof(int));
  if(!err) err=flex_extend((flex_ptr)&obje,nextlevel*sizeof(char*));

  if(!err) err=flex_extend((flex_ptr)&posn2,nextlevel*sizeof(int));
  if(!err) err=flex_extend((flex_ptr)&lno2,nextlevel*sizeof(int));
  if(!err) err=flex_extend((flex_ptr)&fpos2,nextlevel*sizeof(int));

  if(!err) maxlevel=nextlevel-1;
 }

 return(err);
}




/************************************************************/

static os_error * levelstackinit(void)
{
 os_error * err;

 maxlevel=LEVELCHUNK-1;

          err=flex_alloc((flex_ptr)&posn,LEVELCHUNK*sizeof(int));
 if(!err) err=flex_alloc((flex_ptr)&lno,LEVELCHUNK*sizeof(int));
 if(!err) err=flex_alloc((flex_ptr)&fpos,LEVELCHUNK*sizeof(int));
 if(!err) err=flex_alloc((flex_ptr)&obje,LEVELCHUNK*sizeof(char*));

 if(!err) err=flex_alloc((flex_ptr)&posn2,LEVELCHUNK*sizeof(int));
 if(!err) err=flex_alloc((flex_ptr)&lno2,LEVELCHUNK*sizeof(int));
 if(!err) err=flex_alloc((flex_ptr)&fpos2,LEVELCHUNK*sizeof(int));

 return(err);
}







/************************************************************/

/* return the parent of this dir */
/* or -1 if none                 */

int parentof(archive * arc,int child)
{
 int i,pn,ln,level,in,n;

 if(!child) return(-1);

 pn=0;
 level=1;
 ln=arc->fmain;
 in=0;
 n=0;

 while(level)
 {
  for(i=in;i<ln;i++)
  {
   if(n==child)
   {
    if(level>1) return(pn);
    else        return(-1);
   }
   else
   if(n>child)  return(-1);


   if(arc->hdr[n].dirn>=0)
   {
    posn[level]=pn;
    lno[level]=ln;
    fpos[level]=i;
    pn=n;
    i=0;
    in=0;
    ln=arc->hdr[pn].dirn+1;
    levelstackcheck(++level);
   }
   n++;
  }

  level--;
  if(level)
  {
   pn=posn[level];
   ln=lno[level];
   in=fpos[level]+1;
  }
 }
 return(-1);
}







/************************************************************/

/* returns the number of files in this directory                   */
/* which means the total number including those in sub directories */
/* but does not count the directory as an entry                    */

int noindir(int acn,int posn)
{
 int p,i,n,x;

 n=0;
 p=posn;
 for(i=0;i<arc[acn].hdr[posn].dirn;i++)
 {
  n++;
  p++;
  if(arc[acn].hdr[p].dirn>=0)
  {
   x=noindir(acn,p);
   p+=x;
   n+=x;
  }
 }
 return(n);
}



/**************************************************************************/



os_error * filepath1(char * arcpath,int acn,int ins)
{
 os_error * err;
 int        pn;
 int        level;
 int        ln;
 int        in;
 int        n;
 int        i;
 char     * p;


 pn=0;
 level=1;
 ln=arc[acn].fmain;
 in=0;
 n=0;

 p=arcpath+strlen(arcpath); /* point to end of current string */
 if(*arcpath)
 {
  *p++='.';
  *p=0;
 }

 while(level)
 {
  for(i=in;i<ln;i++)
  {
   if(arc[acn].hdr[n].dirn>=0)
   {
    posn[level]=pn;
    lno[level]=ln;
    fpos[level]=i;
    pn=n;
    i=0;
    in=0;
    ln=arc[acn].hdr[pn].dirn+1;
    err=levelstackcheck(++level);
    if(err) return(err);
    posn[level]=pn;
   }
   if(n==ins) break;
   n++;
  }

  if(n==ins && i<ln) break;

  level--;
  if(level)
  {
   pn=posn[level];
   ln=lno[level];
   in=fpos[level]+1;
  }
 }

  for(i=2;i<=level;i++)
  {
    strcpy(p,zstringptr(arc[acn].hdr[posn[i]].name));
    p+=strlen(zstringptr(arc[acn].hdr[posn[i]].name));
    *p++='.';
  }

  if(arc[acn].hdr[n].dirn==DNFILE || arc[acn].hdr[n].dirn==DFLAT)
    strcpy(p,zstringptr(arc[acn].hdr[ins].name));
  else
    *(p-1)=0;

 return(NULL);
}



/************************************************************/

os_error * filepath(char * arcpath,int acn,int ins)
{
 strcpy(arcpath,arc[acn].path);
 if(ins>=0) return(filepath1(arcpath,acn,ins));
 else       return(NULL);
}



/****************************************************************************/

os_error * setnofiles(int acn,int n)
{
 os_error * err;

 n=(n+MINENTRY)*sizeof(heads);

 if(!arc[acn].hdr) err=flex_alloc((flex_ptr)&arc[acn].hdr,n);
 else              err=flex_extend((flex_ptr)&arc[acn].hdr,n);

 return(err);
}



/************************************************************/

os_error * insentry(int acn,int fn,int ind,int size)
{
 os_error * err;
 int        i;

 err=setnofiles(acn,arc[acn].nofiles+size);

 if(!err)
 {
  if(ind<0) arc[acn].fmain++;
  else      arc[acn].hdr[ind].dirn++;

  arc[acn].nofiles+=size;
  for(i=arc[acn].nofiles-1;i>=(fn+size);i--)
  {
   arc[acn].hdr[i]=arc[acn].hdr[i-size];
  }
  memset(&arc[acn].hdr[fn],0,sizeof(heads)*size);
 }

 return(err);
}




/************************************************************/

void rementry(archive * arc,int fn,int size)
{
 int n;
 int i;

 n=parentof(arc,fn);

 if(n<0) arc->fmain--;
 else    arc->hdr[n].dirn--;

 arc->nofiles-=size;
 for(i=fn;i<arc->nofiles;i++) arc->hdr[i]=arc->hdr[i+size];
}




/*****************************************************************************/

archive * arc;
int       maxarc;


#define ARCCHUNK (8*sizeof(archive))



/************************************************************/

static void initbuffer(int i)
{
 memset(&arc[i],0,sizeof(archive));
 arc[i].inuse=1;
}


/************************************************************/

os_error * getarchandle(int * arcn)
{
 os_error * err;
 int        i;

 *arcn=-1;

 for(i=0;i<maxarc;i++)
 {
  if(!arc[i].inuse)
  {
   *arcn=i;
   initbuffer(i);
   return(NULL);
  }
 }

 err=schunk((flex_ptr)&arc,(maxarc+1)*sizeof(archive),ARCCHUNK);
 if(!err)
 {
  *arcn=maxarc;
  initbuffer(maxarc);
  maxarc++;
 }

 return(err);
}









/************************************************************/

os_error * scrapentry(int acn,int n,int size)
{
 os_error * err;
 int        i;

 err=NULL;

 for(i=n+size-1;i>=n;i--)
 {
  zrmstring(arc[acn].hdr[i].name);
 }

 return(err);
}





/************************************************************/

os_error * closedir(int acn,int n,int * sizep)
{
 os_error * err;
 int        size;
 int        i;

 err=NULL;

 *sizep=0;

 if(n>=0 && arc[acn].hdr[n].dirn>=0)
 {
  size=noindir(acn,n);
  err=scrapentry(acn,n+1,size);
  *sizep=size;

  arc[acn].nofiles-=size;
  for(i=n+1;i<arc[acn].nofiles;i++) arc[acn].hdr[i]=arc[acn].hdr[i+size];

  arc[acn].hdr[n].dirn=DFLAT;
 }

 return(err);
}


os_error * losearc(int acn)
{
 scrapentry(acn,0,arc[acn].nofiles);

 if(arc[acn].hdr) flex_free((flex_ptr)&arc[acn].hdr);

 arc[acn].inuse=0;

 return(NULL);
}





/************************************************************/

os_error * arcsinit(void)
{
 os_error * err;

 err=salloc((flex_ptr)&arc,ARCCHUNK);
 if(!err) err=levelstackinit();
 if(!err) err=zstringstart();
 maxarc=0;

 return(err);
}



/*************  End of c.arcs  ***********************/



