/***************************************************************************
 *                                                                         *
 *   VDIR.C                                                                *
 *                                                                         *
 *   Copyright (c) 1994-1997 Galacticomm, Inc.    All Rights Reserved.     *
 *                                                                         *
 *   Virtual Directory API.                                                *
 *                                                                         *
 *                                        - RNStein  7/11/94               *
 *                                                                         *
 ***************************************************************************/

#include "gcomm.h"
#include "vdir.h"

#define FILREV "$Revision: 12 $"

static INT dirspec(CHAR *dir);
static INT svchance(struct vdirsvc *vds,CHAR *dir);
static INT subtrg(CHAR *bigdir,CHAR *pfix);
static INT roodir(VOID);
static INT roo1st(CHAR *fspec);
static INT roonxt(VOID);

struct vdirsvc vdirroot={          /* "root" server (lists VDIRservers)    */
     NULL,                         /* next link in list of VDIRservers     */
     SLS,                          /* directory prefix                     */
     roodir,                       /* select new subdir (vdirscb->trgdir)  */
     roo1st,                       /* find one, or first of a set, of files*/
     roonxt,                       /* find next files in a set             */
     NULL,                         /* information (VDFXXX/VDDXXX in VDIR.H)*/
     NULL,                         /* DOS path for file from vds1st/nxt=1  */
     NULL,                         /* record reading a file fm vds1st/nxt=1*/
     NULL,                         /* log in new file after vds1st=0/1     */
     NULL,                         /* delete file from vds1st/nxt=1        */
     NULL,                         /* rename file from vds1st/nxt=1        */
     NULL,                         /* make dir from recent vds1st=0        */
     NULL,                         /* remove dir from recent vdsdir=1      */
     0,                            /* creation date                        */
     0                             /* creation time                        */
};

struct vdirsvc *vdshead=&vdirroot; /* VDIR server linked-list head         */
                                   /* "root" server at tail end of list    */

struct vdirscb *vdirscb;           /* current session being serviced       */

#define DIRBUFSIZ (VDRFSPEC+20)    /* (room for some ".." references)      */
static CHAR dirbuf[DIRBUFSIZ+1];   /* used by dirspec(), etc.              */

/*--- vds1st() implicit output ---*/
INT vdirbad;                       /* 1=when vds1st() returns 0 because    */
                                   /* of a bad directory, 0=no files       */
ULONG vdirroom;                    /* when vds1st() sets VDPWRIT, this is  */
                                   /* max room (or add'l room) for file    */

/*--- special application-settable VDIR options ---*/
INT vdircase=-1;                   /*  1=convert all names to upper case   */
                                   /*  0=leave alone                       */
                                   /* -1=convert to lower case             */
INT vdflags;                       /* set by vds1st(,flags), see VDIR.H    */

VOID
regvds(                            /* Register a Virtual Directory Server  */
struct vdirsvc *vds)               /* VDIRserver info                      */
{
     vds->next=vdshead;
     vdshead=vds;
}

INT
vdsdir(                            /* Change current directory             */
CHAR *dir)                         /* relative or absolute directory spec  */
                                   /* returns 1=valid directory, 0=not     */
                                   /* (NOTE dir string slashes will be     */
{                                  /* standardized to SL)                 */
     INT dirlen;
     INT isvalid;
     INT rstsls=0;

     if (dir == NULL) {
          dir="";
     }
     samsls(dir,SL);
     if ((dirlen=strlen(dir)) >= 2 && dir[dirlen-1] == SL) {
          dir[dirlen-1]='\0';
          rstsls=1;
     }
     isvalid=dirspec(dir);
     if (isvalid) {
          strcpy(vdirscb->curdir,vdirscb->trgsvc->dirpfx);
          if (vdirscb->trgdir[0] != '\0') {
               strcat(vdirscb->curdir,SLS);
               stzcat(vdirscb->curdir,vdirscb->trgdir,VDRPFIX+1);
          }
          vdirscb->curnum=vdirscb->trgnum;
          vdirscb->cursvc=vdirscb->trgsvc;
          caseset(vdirscb->curdir);
     }
     if (rstsls) {
          dir[dirlen-1]=SL;
     }
     return(isvalid);
}

static INT
dirspec(                           /* is this a valid directory?           */
CHAR *dir)                         /* (may be relative or absolute)        */
                                   /* if so, sets vdirscb->trgsvc,dir,num  */
                                   /* if not, may trash these fields of scb*/
{
     struct vdirsvc *vds;

     if (dir[0] == SL) {           /* absolute directory specification     */
          dirbuf[0]='\0';
     }
     else {                        /* relative directory specification     */
          strcpy(dirbuf,vdirscb->curdir);
          if (!samend(dirbuf,SLS) && dir[0] != '\0') {
               strcat(dirbuf,SLS);
          }
     }
     direduce(stzcat(dirbuf,dir,DIRBUFSIZ+1));
     if (strlen(dirbuf) > VDRPFIX) {
          return(0);
     }
     if ((vds=vdirscb->cursvc) != NULL && svchance(vds,dirbuf)) {
          vdirscb->trgsvc=vds;
          return(1);
     }
     for (vds=vdshead ; vds != NULL ; vds=vds->next) {
          if (vds != vdirscb->cursvc) {
               if (svchance(vds,dirbuf)) {
                    vdirscb->trgsvc=vds;
                    return(1);
               }
          }
     }
     return(0);
}

static INT
svchance(                          /* give a VDIRserver a chance at a dir  */
struct vdirsvc *vds,               /* server                               */
CHAR *dir)                         /* candidate directory (len <= VDRPFIX) */
{
     CHAR *cp;

     strcpy(vdirscb->trgdir,dir);
     if (sameto(cp=vds->dirpfx,vdirscb->trgdir)
      && subtrg(vdirscb->trgdir,cp)) {
          return(vds->vdsdir());
     }
     return(0);
}

static INT
subtrg(                            /* store subdir in vdirscb->trgdir      */
CHAR *bigdir,                      /* full absolute directory              */
CHAR *pfix)                        /* server's directory prefix            */
{
     INT n;

     if ((n=strlen(pfix)) >= strlen(bigdir)) {
          vdirscb->trgdir[0]='\0';
          return(1);
     }
     else if (bigdir[n] == SL) {
          caseset(strcpy(vdirscb->trgdir,bigdir+n+1));
          return(1);
     }
     return(0);
}

INT
vds1st(                            /* find the first file or subdirectory  */
CHAR *fspec,                       /* may change slash in place            */
INT flags)                         /* see VDSXXX flags in VDIR.H           */
                                   /* implicit output when returning 0:    */
{                                  /* vdirbad: 1=bad directory, 0=no files */
     CHAR *cp,fptmp[GCSTRFNM];
     INT rc;
     static CHAR altbuf[DIRBUFSIZ+1];

     vdirbad=0;
     vdflags=flags;
     vdirroom=MAXROOM;
     vdirscb->flags=0;
     setmem(&vdirscb->fb,sizeof(vdirscb->fb),0);
     if (fspec == NULL) {
          fspec="";
     }
     samsls(fspec,SL);
     if (vdflags&VDSASD && dirspec(fspec)) {
          cp="";
     }
     else if ((cp=strrchr(fspec,SL)) != NULL) {
          stzcpy(altbuf,fspec,(INT)(cp-fspec)+1);
          if (altbuf[0] == '\0') {
               strcpy(altbuf,SLS);
          }
          if (!dirspec(altbuf)) {
               vdirbad=1;
               return(0);
          }
          cp++;
     }
     else {
          subtrg(vdirscb->curdir,vdirscb->cursvc->dirpfx);
          vdirscb->trgsvc=vdirscb->cursvc;
          vdirscb->trgnum=vdirscb->curnum;
          cp=fspec;
     }
     if (!(vdflags&VDSWLD) && (strchr(fspec,'*') != NULL
                            || strchr(fspec,'?') != NULL)) {
          rc=0;
     }
     else {
          for (rc=vdirscb->trgsvc->vds1st(cp)
             ; rc == 2 || rc == 1 && !(vdirscb->flags&VDPVSBL)
             ; rc=vdirscb->trgsvc->vdsnxt()) {
               vdirscb->flags=0;
          }
     }
     if (rc == 0 && vdirscb->fb.ff_name[0] == '\0') {
          stzcpy(vdirscb->fb.ff_name,
                 fileparts(GCPART_FNAM,fspec,fptmp,GCSTRFNM),
                           sizeof(vdirscb->fb.ff_name));
     }
     caseset(vdirscb->fb.ff_name);
     return(rc);
}

INT
vdsnxt(VOID)                       /* 1=next file or subdir, 0=no more     */
{
     INT rc;

     vdirscb->flags=0;
     while ((rc=vdirscb->trgsvc->vdsnxt()) == 2
         || rc == 1 && !(vdirscb->flags&VDPVSBL)) {
          vdirscb->flags=0;
     }
     caseset(vdirscb->fb.ff_name);
     return(rc);
}

CHAR *
vdsinf(                            /* other information on the file        */
INT type)                          /* type code (VDFXXX/VDDXXX in VDIR.H)  */
{
     CHAR *(*rp)(INT);
     CHAR *cp;

     if ((rp=vdirscb->trgsvc->vdsinf) != NULL
      && (cp=(*rp)(type)) != NULL) {
          return(cp);
     }
     else {
          return("");
     }
}

CHAR *
vdspth(VOID)                       /* get DOS path of a VDIR file          */
{                                  /* returns full path with file name     */
     CHAR *(*rp)(VOID);

     return((rp=vdirscb->trgsvc->vdspth) != NULL ? caseset((*rp)()) : "");
}

VOID
vdsrcd(                            /* record reading of file               */
INT nml,                           /* 1=normal 0=aborted                   */
LONG numbyt)                       /* if nml == 0, number of bytes read    */
{
     VOID (*rp)(INT,LONG);

     if ((rp=vdirscb->trgsvc->vdsrcd) != NULL) {
          rp(nml,numbyt);
     }
}

INT
vdslog(                            /* log in new file after vds1st=0       */
CHAR *desc)                        /* description or ""                    */
{                                  /* returns 1=could, 0=couldn't          */
     INT (*rp)(CHAR *);

     return((rp=vdirscb->trgsvc->vdslog) != NULL
         && (vdirscb->flags&VDPWRIT)
         && (*rp)(desc));
}

INT
vdsdel(VOID)                       /* delete file from recent vds1st/nxt=1 */
{                                  /* returns 1=could, 0=couldn't, 2=trying*/
     INT (*rp)(VOID);

     rp=vdirscb->trgsvc->vdsdel;
     if (rp != NULL
     && (vdirscb->flags&(VDPEXIS|VDPWRIT)) == (VDPEXIS|VDPWRIT)) {
          return((*rp)());
     }
     return(0);

}

INT
vdsrnm(                            /* rename file from recent vds1st/nxt=1 */
CHAR *newnam)                      /* new name (no path prefix)            */
{                                  /* returns 1=could, 0=couldn't          */
     INT (*rp)(CHAR *);

     return((rp=vdirscb->trgsvc->vdsrnm) != NULL
         && (vdirscb->flags&(VDPEXIS+VDPWRIT))
                          == VDPEXIS+VDPWRIT
         && (*rp)(newnam));
}

INT
vdsmkd(VOID)                       /* make dir from recent vds1st=0        */
{                                  /* returns 1=could, 0=couldn't          */
     INT (*rp)(VOID);

     return((rp=vdirscb->trgsvc->vdsmkd) != NULL
         && (vdirscb->flags&(VDPEXIS+VDPWRIT))
                          ==         VDPWRIT
         && (*rp)());
}

INT
vdsrmd(VOID)                       /* remove dir from recent vdsdir=1      */
                                   /* returns 1=could, 0=couldn't          */
{                                  /* leaves you at parent directory       */
     INT (*rp)(VOID);

     return(rp=vdirscb->trgsvc->vdsrmd) != NULL && (*rp)();
}

/*--- Root VDIRserver ---*/

static INT
roodir(VOID)                       /* VDIRserver for root, select directory*/
{
     return(sameas(vdirscb->trgdir,""));
}

static INT
roo1st(                            /* List subdirs (VDIRserver subtrees)   */
CHAR *fspec)                       /* only "" and "*" are supported        */
{
     if (fspec[0] == '\0' || fspec[0] == '*' && vdflags&VDSWLD) {
          vdirscb->j.ll=vdshead;
          return(2);
     }
     return(0);
}

static INT                         /* Get next subdir in list              */
roonxt(VOID)
{
     struct vdirsvc *vds;

     if ((vds=vdirscb->j.ll) == NULL) {
          return(0);
     }
     vdirscb->j.ll=vds->next;
     if (vds == &vdirroot) {
          return(2);
     }
     vdirscb->fb.ff_attrib=FAMDIR;
     stzcpy(vdirscb->fb.ff_name,vds->dirpfx+1,12+1);
     vdirscb->fb.ff_fdate=vds->credat;
     vdirscb->fb.ff_ftime=vds->cretim;
     if (vds->vdsdir()) {
          vdirscb->flags|=VDPVSBL+VDPREAD;
     }
     return(1);
}

/*--- Utilities ---*/

CHAR *
samsls(                            /* convert slashes to the same type     */
CHAR *stg,                         /* returns same value as passed         */
CHAR slash)                        /* slash to use (UNIXSL or DOSSL) */
{
     CHAR c;
     CHAR *cp;

     for (cp=stg ; (c=*cp) != '\0' ; cp++) {
          if (c == UNIXSL || c == DOSSL) {
               *cp=slash;
          }
     }
     return(stg);
}

CHAR *
direduce(                          /* reduce "." and ".." in dir name      */
CHAR *stg)                         /* returns same value as passed         */
{
     CHAR *cp,*rp;

     while ((cp=strstr(stg,SLS"..")) != NULL) {
          if (cp[3] != '\0' && cp[3] != SL) {
               return(stg);        /* (insist "\.." precede NUL or '\')    */
          }
          *cp='\0';
          if ((rp=strrchr(stg,SL)) == NULL) {
               *cp=SL;
               return(stg);        /* (give up if no slash before "\..")   */
          }
          strcpy(rp,cp+3);         /* "\a\b\..\c\d" -> "\a\c\d"            */
     }
     while ((cp=strstr(stg,SLS".")) != NULL) {
          if (cp[2] != '\0' && cp[2] != SL) {
               return(stg);        /* (insist "\." precede NUL or '\')     */
          }
          strcpy(cp,cp+2);         /* "\a\b\.\c\d" -> "\a\b\c\d"           */
     }
     if (*stg == '\0') {
          strcpy(stg,SLS);
     }
     return(stg);
}

CHAR *
caseset(                           /* set string's case acc to vdircase    */
CHAR *pth)                         /* string converted in-place (if at all)*/
{
     if (vdircase == 1) {
          strupr(pth);
     }
     else if (vdircase == -1) {
          strlwr(pth);
     }
     return(pth);
}

CHAR *
autwex(                            /* automatically wild-ize extension     */
CHAR *fspec)                       /* file spec (no path prefix)           */
{
     if (vdflags&VDSAWE) {
          fspec=(fspec[0] == '\0') ? "*" : fspec;
#ifndef UNIX
#ifdef GCDOS
          if (strchr(fspec,'.') == NULL && strlen(fspec) <= 8) {
#else
          if (strchr(fspec,'.') == NULL) {
#endif // GCDOS
               fspec=spr("%s.*",fspec);
          }
#endif // UNIX
     }
     return(fspec);
}

