/***************************************************************************
 *                                                                         *
 *   FIOAPI.CPP                                                            *
 *                                                                         *
 *   Copyright (c) 1996 Galacticomm, Inc.      All Rights Reserved.        *
 *                                                                         *
 *   File I/O API                                                          *
 *                                                                         *
 *                                            - R. Stein  5/19/91          *
 *   Unix additions                           - Mahesh Neelakanta 10/26/94 *
 *   Modified for Unity and NT version        - Ilya Minkin 01/17/96       *
 *   Modified for Visual C++                  - Joe Delekto 7/31/96        *
 *                                                                         *
 ***************************************************************************/

#include "gcommlib.h"

#define SPECIAL_BITS (FAMDIR|FAMHID|FAMSYS)

LONG ztzone=0;                     // Seconds between GMT and local time

                                   // global vars set by cntdir()/cntdirs()
LONG numfils=0;                    // total number of files
LONG numbyts=0;                    // total number of bytes
LONG numbytp=0;                    // # bytes + wasted part of last cluster
LONG numdirs=0;                    // total number of subdirectories

CHAR deldirerr[GCMAXPTH+40];       // error message when deldir() != 0

static VOID adddirs(INT clsiz,const CHAR *path);
static GBOOL morcfile(const CHAR *src,const CHAR *dst,GBOOL movfil);
static GBOOL copyutil(const CHAR *src,const CHAR *dst);

Cffblk::Cffblk()                   // Constructor for "smart" ffblk
{
     ff_handle=INVALID_HANDLE_VALUE;
}

Cffblk::~Cffblk()                  // Destructor for "smart" ffblk
{
     if (ff_handle != INVALID_HANDLE_VALUE) {
            FindClose(ff_handle);
     }
}


CHAR *                             //   returns pointer to dstbuf or ""
fileparts(                         // grab required part of a full filename
SHORT filpart,                     //   part to be returned
const CHAR *fullname,              //   full filename to be worked on
CHAR *dstbuf,                      //   destination buffer for data
SHORT dstsiz)                      //   destination buffer size
{
     CHAR drvtmp[_MAX_DRIVE];
     CHAR dirtmp[_MAX_DIR];
     CHAR fnmtmp[_MAX_FNAME];
     CHAR exttmp[_MAX_EXT];

     *dstbuf='\0';       // zero out the buffer so we can do stlcat()s
     _splitpath(fullname,drvtmp,dirtmp,fnmtmp,exttmp);
     switch (filpart) {
     case GCPART_PATH:
          stlcat(dstbuf,drvtmp,dstsiz);
          stlcat(dstbuf,dirtmp,dstsiz);
          break;
     case GCPART_FNAM:
          stlcat(dstbuf,fnmtmp,dstsiz);
          stlcat(dstbuf,exttmp,dstsiz);
          break;
     case GCPART_FILE:
          stlcat(dstbuf,fnmtmp,dstsiz);
          break;
     case GCPART_EXTN:
          stlcat(dstbuf,exttmp+1,dstsiz);
          break;
     case GCPART_DRVS:
          stlcat(dstbuf,drvtmp,dstsiz);
          break;
     default:
          break;
     }
     return(dstbuf);
}

GBOOL                              //   returns TRUE=copy successful
cpyutl(                            // copy file utility
const CHAR *src,                   //   source file name
const CHAR *dst,                   //   destination file names
const CHAR *srcmod,                //   source fopen() mode
const CHAR *dstmod)                //   destination fopen() mode
{
     size_t nbyts;
     CHAR cpybuf[CPBSIZ];
     FILE *sfp,*dfp;

     if ((sfp=fopen(src,srcmod)) == NULL) {
          return(FALSE);
     }
     if ((dfp=fopen(dst,dstmod)) == NULL) {
          fclose(sfp);
          return(FALSE);
     }
     while ((nbyts=fread(cpybuf,1,CPBSIZ,sfp)) > 0) {
          if (fwrite(cpybuf,1,nbyts,dfp) != nbyts) {
               fclose(sfp);
               fclose(dfp);
               return(FALSE);
          }
     }
     fclose(sfp);
     fclose(dfp);
     if ((dfp=fopen(dst,FOPRB)) == NULL) {
          return(FALSE);
     }
     fclose(dfp);
     setFileGMT(dst,getFileGMT(src));
     return(TRUE);
}

GBOOL                              //   returns TRUE if one found
filspclst(                         // any files in the first filespec?
struct filblklst *flp)             //   path and file list to search
{
     CHAR *fp,*tp;
     static CHAR buff[GCMAXPTH];

     strcpy(buff,flp->pathpf);
     for (fp=flp->list,tp=buff+strlen(buff)
          ; *fp != '\0' && *fp != ' ' && tp < buff+sizeof(buff)-1
          ; fp++,tp++) {
          *tp=*fp;
     }
     *tp='\0';
     return(fnd1st(&flp->fb,buff,0));
}

GBOOL                              //   returns TRUE if found
fil1stlst(                         // find first file in a list
struct filblklst *flp,             //   struct for continuing w/ filnxtlst
const CHAR *prefix,                //   path prefix
const CHAR *list)                  //   list of files/path
{
     CHAR *np;

     stlcpy(flp->list,skpwht(list),MAXFLL+1);
     stlcpy(flp->pathpf,prefix,MAXFLP+1);
     while (1) {
          if (flp->list[0] == '\0') {
               return(FALSE);
          }
          if (filspclst(flp)) {
               return(TRUE);
          }
          np=skpwht(skpwrd(flp->list));
          memmove(flp->list,np,strlen(np)+1);
     }
}

GBOOL                              //   returns TRUE if one found
filnxtlst(                         // find next file in list
struct filblklst *flp)             //   previous filblklst to cont. search
{
     CHAR *np;

     if (fndnxt(&flp->fb)) {
          return(TRUE);
     }
     while (1) {
          np=skpwht(skpwrd(flp->list));
          memmove(flp->list,np,strlen(np)+1);
          if (flp->list[0] == '\0') {
               return(FALSE);
          }
          if (filspclst(flp)) {
               return(TRUE);
          }
     }
}

VOID
cntdir(                            // count bytes and files in a directory
const CHAR *path)                  //   path name of the directory contents
{
     Cffblk fb;
     USHORT clsiz;

     clsiz=clsize(path);
     numfils=0L;
     numbyts=0L;
     numbytp=0L;
     if (fnd1st(&fb,path,0)) {
          do {
               numfils++;
               numbyts+=fb.ff_fsize;
               numbytp+=clfit(fb.ff_fsize,clsiz);
          } while (fndnxt(&fb));
     }
}

VOID
cntdirs(                           // count bytes/files in dir & its subdir
const CHAR *path)                  //   path name of the directory contents
{
     numfils=0L;
     numbyts=0L;
     numbytp=0L;
     numdirs=0L;
     adddirs(clsize(path),path);
}

LONG                               //  returns total file size
clfit(                             // figure size of file including waste
LONG size,                         //   file size in bytes
USHORT clsiz)                      //   size of clusters on this drive
{
     return((clsiz == 0 || size == 0L) ? 0L : ((size-1)/clsiz+1)*clsiz);
}

UINT                               //   returns size or 0 for error
clsize(                            // get cluster size in bytes
const CHAR *path)                  //   path to drive to check
{
     CHAR drivebuf[GCMAXPTH];
     DWORD secPerClust;
     DWORD bytesPerSec;
     DWORD numOfFreeClust;
     DWORD totalNumOfClust;
     CHAR *rootPathName;

     fileparts(GCPART_DRVS,path,drivebuf,GCMAXPTH);
     if (drivebuf[0] == '\0') {    // use current drive
          rootPathName=NULL;
     }
     else {
          rootPathName=drivebuf;
     }
     if (GetDiskFreeSpace(rootPathName,
                          &secPerClust,
                          &bytesPerSec,
                          &numOfFreeClust,
                          &totalNumOfClust)) {
          return((UINT)(bytesPerSec*secPerClust));
     }
     return(0);
}

GBOOL                              //   returns TRUE=file updated
setFileTm(                         // set file time & date
const CHAR *fname,                 //   file name
USHORT dtim,                       //   time to set
USHORT ddat)                       //   date to set
{
     WIN32_FIND_DATA fb;
     FILETIME localFileTime,fileTime;
     HANDLE hFile;
     GBOOL retval;

     HANDLE hFind = FindFirstFile(fname,&fb);

     if (hFind == INVALID_HANDLE_VALUE) {
          return(FALSE);
     }
     if (!DosDateTimeToFileTime(ddat,dtim,&localFileTime)) {
          FindClose( hFind );
          return(FALSE);
     }
     if (!LocalFileTimeToFileTime(&localFileTime,&fileTime)) {
          FindClose( hFind );
          return(FALSE);
     }
     hFile=CreateFile(fname,GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,
                      NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
     if (hFile == INVALID_HANDLE_VALUE) {
          retval=FALSE;
     }
     else
     {
          retval=SetFileTime(hFile,&fb.ftCreationTime,&fileTime,&fileTime);
          CloseHandle(hFile);
     }

     FindClose( hFind );
     return(retval);
}

GBOOL                              //   returns TRUE=file updated
setFileGMT(                        // set file time and date
const CHAR *fname,                 //   file name
ULONG gmt70)                       //   time in sec since 00:00 1/1/70 GMT
{
     USHORT day,hour,minsec;
     LONG gmtmp;

     gmtmp=gmt70-315532800L-ztzone; // conv to secs since 00:00 1/1/80 lcl
     if (gmtmp < 0L) {
          gmt70=0L;
     }
     else {
          gmt70=gmtmp;
     }
     day=(USHORT)(gmt70/86400L);                      // days since 1/1/80
     hour=(USHORT)((gmt70/3600L)%24L);                // hour (local time)
     minsec=(USHORT)(gmt70%3600L);                    // seconds since hour
     return(setFileTm(fname,dttime(hour,minsec/60,minsec%60),datofc(day)));
}


VOID
getFileTm(                         // get time and date for file
const CHAR *fname,                 //   file name
USHORT *dtim,                      //   time to get
USHORT *ddat)                      //   date to get
{
     WIN32_FIND_DATA fb;
     FILETIME localFTime;
     HANDLE hFind;

     *ddat=*dtim=0;
     if ((hFind = FindFirstFile(fname,&fb)) == INVALID_HANDLE_VALUE) {
          return;
     }
     FileTimeToLocalFileTime(&fb.ftLastWriteTime,&localFTime);
     FileTimeToDosDateTime(&localFTime,ddat,dtim);

     FindClose(hFind);
}

ULONG                              //   returns time and date in GMT format
getFileGMT(                        // get GMT file time and date
const CHAR *fname)                 //   file name
{
     ULONG mtime;
     USHORT dat,tim,cof;

     getFileTm(fname,&tim,&dat);
     /* The following line is because of an assertion in difDate (which is */
     /* called by cofdat()).  getFileTm() returns dat == 0 if the file     */
     /* doesn't exist, which is not a valid date; cofdat() barfs on this.  */
     /* 22615 is what cofdat(0) used to return.                            */
     cof=dat == 0 ? 22615 : cofdat(dat);
     mtime=315532800L+                  /* 00:00 1/1/70 to 00:00 1/1/80    */
           cof*86400L+                  /* 00:00 1/1/80 to 00:00 fileday   */
           dthour(tim)*3600L+           /*  \                              */
           dtmin(tim)*60L+              /*   > compute filetime since      */
           dtsec(tim)+                  /*  /  00:00 on fileday            */
           ztzone;                      /* convert from local time to GMT  */
     return(mtime);
}

LONG                               //   returns kbytes free or -1 for error
dskfre(                            // number of kbytes free on a drive     */
const CHAR *path)                  //   path on drive to check,NULL=default*/
{
     CHAR drivebuf[GCMAXPTH];
     DWORD secPerClust;
     DWORD bytesPerSec;
     DWORD numOfFreeClust;
     DWORD totalNumOfClust;
     CHAR *rootPathName;

     if (path != NULL) {
          fileparts(GCPART_DRVS,path,drivebuf,GCMAXPTH);
          if (drivebuf[0] == '\0') {
               rootPathName=NULL;       // use current drive
          }
          else {
               rootPathName=drivebuf;
          }
     }
     else {
          rootPathName=NULL;            // use current drive
     }
     if (GetDiskFreeSpace(rootPathName,
                          &secPerClust,
                          &bytesPerSec,
                          &numOfFreeClust,
                          &totalNumOfClust)) {
          return((LONG)((bytesPerSec/256)*secPerClust*(numOfFreeClust/4)));
     }
     return(-1L);
}

GBOOL                              //   returns TRUE=found a file
fnd1st(                            // find file matching filspc and attr
Cffblk *fbptr,                     //   ptr to data storage area
const CHAR *filspc,                //   file spec to match
UINT attr)                         //   attribute to match
{
     return(findfirst(filspc,fbptr,attr) == 0);
}

GBOOL                              //   returns TRUE=found a file
fndnxt(                            // find next file matching filspc & attr
Cffblk *fbptr)                     //   ptr to data storage area
{
     return(findnext(fbptr) == 0);
}

INT findfirst(
const CHAR *filspc,
Cffblk *fbptr,
UINT attr)
{
     if (fbptr->ff_handle != INVALID_HANDLE_VALUE) {
          FindClose(fbptr->ff_handle);
          fbptr->ff_handle=INVALID_HANDLE_VALUE;
     }
     stlcpy(fbptr->ff_name,filspc,260);
     fbptr->s_attrib=attr;
     return(findnext(fbptr));
}

INT findnext(
Cffblk *fbptr)
{
     GBOOL found;
     FILETIME localFTime;

     for (;;) {
          if (fbptr->ff_handle == INVALID_HANDLE_VALUE) {
               fbptr->ff_handle=FindFirstFile(fbptr->ff_name,&fbptr->nfblock);
               found=(fbptr->ff_handle != INVALID_HANDLE_VALUE);
          }
          else {
               found=FindNextFile(fbptr->ff_handle,&fbptr->nfblock);
          }
          if (!found) {
               FindClose(fbptr->ff_handle);
               fbptr->ff_handle=INVALID_HANDLE_VALUE;
               return(-1);
          }
          else if (((fbptr->nfblock.dwFileAttributes&SPECIAL_BITS) == 0)
               || ((fbptr->s_attrib&fbptr->nfblock.dwFileAttributes
                    &SPECIAL_BITS) != 0)) {
                         break;
          }
     }
     stlcpy(fbptr->ff_name,fbptr->nfblock.cFileName,260);
     fbptr->ff_attrib=(UINT)(fbptr->nfblock.dwFileAttributes&0xFF);
     fbptr->ff_fsize=(LONG)((fbptr->nfblock.nFileSizeHigh*MAXDWORD)
          +fbptr->nfblock.nFileSizeLow);
     FileTimeToLocalFileTime(&fbptr->nfblock.ftLastWriteTime,&localFTime);
     FileTimeToDosDateTime(&localFTime,&fbptr->ff_fdate,&fbptr->ff_ftime);
     return(0);
}

GBOOL                              /*   TRUE if dir matching dname exists  */
isdir(                             /* check whether a directory exists     */
const CHAR *dname)                 /*   directory name to search for       */
{
     Cffblk fb;

     return(fndfile(&fb,dname,FAMDIR) && (fb.ff_attrib & FAMDIR));
}


GBOOL                              /*   TRUE if matching file exists       */
isfile(                            /* check for existence of file(s)       */
const CHAR *fname)                 /*   filename or wildcard to check      */
{
     Cffblk fb;

     return(fndfile(&fb,fname,FAMSYS|FAMRON|FAMHID));
}


GBOOL                              /*   TRUE if matching file found        */
fndfile(                           /* check file's existence and stats     */
Cffblk *fb,                        /*   file info (CLOSED after findfirst) */
const char *fname,                 /*   filespec we're searching for       */
UINT attr)                         /*   attribute(s) to match              */
{
     return( fnd1st( fb, fname, attr ) );
}


CHAR *                             //   returns converted file name
mkdosn(                            // convert string to DOS file name
const CHAR *string)                //   file name to convert to DOS format
{
     CHAR *cp,*up,c;
     static CHAR buffer[GCMAXFNM];

     stlcpy(buffer,string,8+1);
     if ((cp=strchr(buffer,'.')) == NULL) {
          strcat(buffer,".");
          if ((cp=strchr(string,'.')) != NULL) {
               stlcpy(up=buffer+strlen(buffer),cp+1,3+1);
               if ((cp=strchr(up,'.')) != NULL) {
                    *cp='\0';
               }
          }
     }
     else {
          stlcpy(cp+1,string+((INT)(cp-buffer))+1,3+1);
          if ((up=strchr(cp+1,'.')) != NULL) {
               *cp='\0';
          }
     }
     for (cp=buffer ; (c=*cp) != '\0' ; cp++) {
          if (c < ' ' || '\177' <= c || strchr(" \\:/\"*?,;=[]",c) != NULL) {
               *cp='-';
          }
     }
     return(buffer);
}

VOID
fixpth(                            // add a backslash to string if needed
CHAR *path)                        //   path to append
{
     SHORT length;

     if ((length=strlen(path)) > 0) {
          if (!isvalds(path[length-1])) {
               strcat(path,SLS);
          }
     }
}

GBOOL                              //   TRUE=ok FALSE=err, see deldirerr[]
deldir(                            // delete a subdirectory & all contents
const CHAR *dirpath)               //   name of a directory
{
     Cffblk fb;
     CHAR path[GCMAXPTH],fptmp[GCMAXFNM];
     CHAR *cp;

     strcpy(deldirerr,dirpath);
     if (!fnd1st(&fb,dirpath,FAMDIR)) {
          strcat(deldirerr," does not exist");
          return(FALSE);
     }
     if (!(fb.ff_attrib&FAMDIR)) {
          strcat(deldirerr," is not a directory");
          return(FALSE);
     }
     if (!sameas(fb.ff_name,fileparts(GCPART_FNAM,dirpath,fptmp,GCMAXFNM))) {
          strcat(deldirerr," contains wildcards");
          return(FALSE);
     }
     if (strlen(dirpath) > GCMAXPTH-1-12-1) { // room for "\" file name NUL
          strcat(deldirerr," is too long");
          return(FALSE);
     }
     strcpy(path,dirpath);
     strcat(path,SLS);
     strcpy(cp=path+strlen(path),STAR);
     if (fnd1st(&fb,path,FAMDIR)) {
          do {
               strcpy(cp,fb.ff_name);
               if (fb.ff_attrib&FAMDIR) {
                    if (!sameas(fb.ff_name,".")
               && !sameas(fb.ff_name,"..")
               && _rmdir(path) != 0
               && !deldir(path)) {
                         return(FALSE);
                    }
               }
               else {
                    if (unlink(path) != 0) {
                         sprintf(deldirerr,"%s cannot be deleted",path);
                         return(FALSE);
                    }
               }
          } while (fndnxt(&fb));
          fnd1st(&fb,"\\",FAMDIR); // helps to avoid "Access Violation" error
                                   // under NT
          if (_rmdir(dirpath) != 0) {
               strcat(deldirerr," cannot be removed");
               return(FALSE);
          }
     }
     return(TRUE);
}

CHAR *                             //   returns unique file name
uniqfn(                            // create a unique filespec
CHAR *fspec)                       //   original file specification
{
     return(ll_uniqfn(TRUE,fspec));
}

CHAR *                             //   returns unique file name
ll_uniqfn(                         // low-level routine for unique fspec
GBOOL cataonerr,                   //   catastro() on errors?
CHAR *fspec)                       //   original file specification
{
     SHORT ci,si,di,ai,n,width;
     CHAR c;
     Cffblk fb;
     static USHORT i,limit[]={1,10,100,1000,10000,65535U};

     n=strlen(fspec);
     ci=stgidx(fspec,':');
     si=stgidx(fspec,SL);
     di=stgidx(fspec,'.');
     ai=stgidx(fspec,'*');
     if (si == -1) {
          si=ci;
     }
     else {
          si=(SHORT)(strrchr(fspec,SL)-fspec);
          if (cataonerr) {
               assert(0 <= si && si < n);
          }
     }
     if (di == -1) {
          di=n;
     }
     if (ai > di) {
          width=5-n+di;
     }
     else {
          width=10-di+si;
     }
     if (width > 5) {
          width=5;
     }
     memmove(fspec+ai+width,fspec+ai+1,n-ai);
     c=fspec[ai+width];
     for (i=0 ; i < limit[width] ; i++) {
          sprintf(fspec+ai,"%0*u",width,i);
          fspec[ai+width]=c;
          if (!fnd1st(&fb,fspec,FAMDIR|FAMSYS|FAMHID) || fb.ff_fsize == 0L) {
               return(fspec);
          }
     }
     if (cataonerr) {
          //catastro("Cannot make a unique file name with %s",fspec);
          exit(-1);
     }
     return("");
}

CHAR *                             //   copy of pointer to dest
normspec(                          // normalize filespec to complete path
CHAR *dest,                        //   dest buffer (must be GCMAXPTH long)
const CHAR *src)                   //   path+file name to normalize
{
     CHAR szBuf[GCMAXPTH];

     if (src[0] == '\0') { // Win32 version of fullpath does not like empty stg
          if (GetCurrentDirectory(sizeof(szBuf),szBuf) == 0) {
               dest[0]='\0';
          }
          else {
               strcpy(dest,szBuf);
          }
          return(dest);
     }
     if (_fullpath(dest,src,GCMAXPTH-1) == NULL) {
          dest[0]='\0';
     }
     else {
          fnmcse(dest);
     }
     return(dest);
}

VOID
gmkdir(                            // recursive version of mkdir()
const CHAR *dirnam)                //   directory name to create
{
     CHAR *cp,buf[GCMAXPTH];

     stlcpy(buf,dirnam,GCMAXPTH);
     if ((cp=strchr(buf,':')) != NULL) {
          cp++;
     }
     else {
          cp=buf;
     }
     while (strlen(cp) > 0 && (cp=strchr(cp+1,SL)) != NULL) {
          *cp='\0';
          MKDIR(buf);
          *cp=SL;
     }
     MKDIR(buf);
}

GBOOL                              //   returns TRUE=file moved
movefile(                          // move file(s) from src to dest
const CHAR *src,                   //   source file spec
const CHAR *dest)                  //   destination file spec
{
     return(morcfile(src,dest,TRUE));
}

GBOOL                              //   returns TRUE=file copied
copyfile(                          // copy file(s) from src to dest
const CHAR *src,                   //   source file spec
const CHAR *dest)                  //   destination file spec
{
     return(morcfile(src,dest,FALSE));
}


FILE *                             //   returns FILE pointer or NULL
gcfsopen(                          // opens a file in shared mode
const CHAR *filename,              //   file name to open
const CHAR *mode,                  //   mode to open with
USHORT shflag)                     //   shared mode flags
{
     return(_fsopen(filename,mode,shflag));
}

GBOOL                              //   return TRUE if separator character
isvalds(                           // is this char. a file path separator
CHAR c)                            //   character to test
{
     if (c == SL || c == ':') {
          return(TRUE);
     }
     return(FALSE);
}

GBOOL                              //   returns TRUE=valid file name char
isvalfc(                           // is this a valid file name character?
CHAR c)                            //   character to check
{
     if (c < ' ' || c == '.' || c == '"' || c == '/' || c == SL
      || c == '[' || c == ']' || c == ':' || c == ';' || c == '|'
      || c == '<' || c == '>' || c == '+' || c == '=' || c == ',') {
          return(FALSE);
     }
     return(TRUE);
}

GBOOL                              //   returns TRUE=valid file name
isvalfn(                           // check for valid file name
const CHAR *filnam)                //   string to check
{
     const CHAR *cp;
     INT i;

     for (i=1,cp=filnam ; *cp != '\0' && *cp != '.' ; ++i,++cp) {
          if (i > GCMAXFILE || !isvalfc(*cp)) {
               return(FALSE);
          }
     }
     if (*cp++ == '.') {
          for (i=1 ; *cp != '\0' ; ++i,++cp) {
               if (i > GCMAXEXT || *cp == '.' || !isvalfc(*cp)) {
                    return(FALSE);
               }
          }
     }
     if (rsvnam(filnam)) {
          return(FALSE);
     }
     return(TRUE);
}

GBOOL                              //   returns TRUE=valid directory
isvaldn(                           // verify directory name is ok for OS
const CHAR *dirnam)                //   directory name to check
{
     CHAR *cp,tmpdir[GCMAXPTH];
     INT pos;

     if (strlen(dirnam) >= GCMAXPTH) {
          return(FALSE);
     }
     normspec(tmpdir,dirnam);
     if (tmpdir[pos=strlen(tmpdir)-1] == SL) {
          tmpdir[pos]='\0';
     }
     while (strlen(tmpdir) > 2) {  // e.g. "C:"
          cp=strrchr(tmpdir,SL);
          if (cp == NULL || !isvalfn(cp+1)) {
               return(FALSE);
          }
          *cp='\0';
     }
     return(strlen(tmpdir) == 2 && isalpha(tmpdir[0]) && tmpdir[1] == ':');
}

CHAR *                             //   returns ptr to cvt'd fname
fnmcse(                            // handles filenames before use in code
CHAR *fname)                       //   filename to work on
{
     return(fname);
}

GBOOL                              /*   return TRUE=reserved               */
rsvnam(                            /* is a name reserved by OS for a device*/
const CHAR *name)                  /*   name to check                      */
{
     HANDLE fh;
     BOOL retVal = TRUE;
     CHAR fname[GCMAXFILE];

     fileparts(GCPART_FILE,name,fname,GCMAXFILE);
     fh = CreateFile((LPTSTR)fname, GENERIC_READ,
                     FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
                     OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
     if (fh == INVALID_HANDLE_VALUE) {
          if (GetLastError() == ERROR_SHARING_VIOLATION) {
               retVal = FALSE;
          }
          else {
               fh = CreateFile((LPTSTR)fname, GENERIC_READ,
                               FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
                               CREATE_NEW, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, NULL);
               if (fh != INVALID_HANDLE_VALUE) {
                    retVal = FALSE;
                    CloseHandle(fh);
               }
          }
     }
     else {
          if (GetFileType(fh) == FILE_TYPE_DISK) {
               retVal = FALSE;
          }
          CloseHandle(fh);
     }
     return(retVal);

}

LONG                               //   returns # of bytes copied
xfrfil(                            // copy from one open file to another
FILE *fromfp,                      //   source file pointer
FILE *tofp,                        //   destination file pointer
LONG nbytes)                       //   max bytes to transfer
{
     LONG wbytes;
     INT n;
     CHAR block[BLKSIZ];
     INT fwsiz;

     for (wbytes=0L ; nbytes >= BLKSIZ ; nbytes-=BLKSIZ) {
          if ((n=(INT)fread(block,1,BLKSIZ,fromfp)) > 0) {
               if ((fwsiz=(INT)fwrite(block,1,n,tofp)) > 0) {
                    wbytes+=fwsiz;
               }
          }
          if (n < BLKSIZ) {
               return(wbytes);
          }
     }
     if (nbytes > 0L && (n=fread(block,1,(INT)nbytes,fromfp)) > 0) {
          wbytes+=fwrite(block,1,n,tofp);
     }
     return(wbytes);
}

static VOID
adddirs(                           // recursive workhorse for cntdirs()
INT clsiz,                         //   cluster size on the drive
const CHAR *path)                  //   wildcard path of files & subdirs
{
     Cffblk fb;
     CHAR subpath[GCMAXPTH],fptmp[GCMAXPTH];
     SHORT rootln;

     if (fnd1st(&fb,path,FAMDIR)) {
          do {
               if (fb.ff_attrib&FAMDIR) {
                    if (!sameas(fb.ff_name,".") && !sameas(fb.ff_name,"..")) {
                         rootln=strlen(fileparts(GCPART_PATH,path,fptmp,
                                                 GCMAXPTH));
                         if (rootln+strlen(fb.ff_name)+1+strlen(STAR)
                             < GCMAXPTH) {
                              sprintf(subpath,"%s%s"SLS STAR,fptmp,fb.ff_name);
                              adddirs(clsiz,subpath);
                         }
                         numdirs++;
                    }
               }
               else {
                    numfils++;
                    numbyts+=fb.ff_fsize;
                    numbytp+=clfit(fb.ff_fsize,clsiz);
               }
          } while (fndnxt(&fb));
     }
}

static GBOOL                       //   returns TRUE=file moved/copied
morcfile(                          // move or copy file utility
const CHAR *src,                   //   source file spec
const CHAR *dest,                  //   destination file spec
GBOOL movfil)                      //   move (TRUE) or copy (FALSE) file
{
     Cffblk fb;
     INT error;

     error=0;
     if (fnd1st(&fb,src,0)) {
          do {
               if ((error=!copyutil(src,dest)) == 0 && movfil) {
                    unlink(src);
               }
          } while (!error && fndnxt(&fb));
     }
     return(!error);
}

static GBOOL                       //   returns TRUE=file copied
copyutil(                          // our own copy file utility
const CHAR *src,                   //   source file spec
const CHAR *dst)                   //   destination file spec
{
     if (CopyFile(src,dst,FALSE)) {
          setFileGMT(dst,getFileGMT(src));
          return(TRUE);
     }
     return(FALSE);
}
