
/*                            FindDisk2.2

     Author      : Ross MacGregor
     Date        : 27/10/88
     Last Update : 5/03/90
     Comments    : This is a public domain program.


     DiskList stored in compacted form:

     below: { unskrunched } => { skrunched }
            [] is a byte of memory

     * compacts strings of spaces
         { [$20] [$20] [$20] [$20] [$20] } => { [SKRUNCHAR] [SKRUNOFFS 5] }
         { [$20] [$20] } => { [$20] [$20] }

     * the STARTSTR is compacted
         { STARTSTR } => { [STARTCHAR] }

*/

/*#include <functions.h>*/
#include <exec/types.h>
#include <exec/nodes.h>
#include <exec/lists.h>
#include <exec/libraries.h>
#include <exec/ports.h>
#include <exec/interrupts.h>
#include <exec/io.h>
#include <exec/memory.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>

/* lattice will want smaller strings - sorry, do it yourself */

#define HELPTEXT1  "\nFindDisk2.2 :\n\
FD -l             ; Loads DiskList to ram:\n\
FD -s             ; Saves DiskList back to disk (first doing an update)\n\
FD -d  [path]     ; Saves dir listing to ram:TempDL\n\
FD -a  [path]     ; Saves listing with all sub-directories included\n\
FD -u             ; Updates the DiskList with TempDL\n\
FD -n             ; Creates a new (empty) DiskList\n"

#define HELPTEXT2  "FD -r  diskname   ; Removes directory from DiskList matching 'diskname'\n\
FD text1 [textn]  ; Lists disk dir with a line containing text1 to textn\n\
  FD text           ; Case insensitive scan for text\n\
  FD !text          ; Case sensitive scan for for text\n"

#define HELPTEXT3 "\nEnvironment Variables: (= default setting)\n\
  DLDISK  =  FindDisk:Disklist (Disk storage location/name)\n\
  DLTEMP  =  ram:DiskList (Ram storage location/name)\n\
  DLDRIVE =  df0: (Captures dir of this drive)\n\n"


#define CP_BUFF 1024L    /* Buffer size for the file copy function */

/* This indicates the start line of a dir listing, and
   it MUST have the name of the disk on the same line.  */

#define STARTSTR  "Directory of "


/* Special character used to compact the spaces in DiskList */
#define SKRUNCHAR 1
#define SKRUNOFFS 10

/* Special characters used to replace the STARTSTR */
#define STARTCHAR  2

/* Lines in the stored dir listing array */
#define NUMLINES 25

/* Max length of the lines read in */
#define LINELEN  170
#define MAXBUF   LINELEN+1

struct FileHandle *fdl, *ftmp;
struct FileHandle *confh;

char *tempdl="ram:TempDL";

char *dltemp="ram:DiskList", *dldisk="FindDisk:DiskList";
char *sdir_pat="df0:";

#define MAXPATHLEN 80

char *dlpath;
char buffer[LINELEN];

/* global main arguments */
int glargc;
char **glargv;

/* function error indicator */
int fcnerr;

char *outofmem="Out of Memory Error.\n";
char *cutoff=" - - (no more lines available) - -\n";

/*------------ dynamic structure for coping DL to memory -------------*/

/* Blocks of memory for storing the disklist */
#define BUFBLKS 20
#define BLKSIZE 10000

char *bufferblock[BUFBLKS],*bufptr=NULL;
int bufcount=-1, chrcount=BLKSIZE+1;
void init_bufblks(), free_bufblks();



/*------------ dynamic structure for listing the volume names --------*/
/*                   used in recursive do_ls call also                */

struct disk
{
  char name[LINELEN];
  struct disk *next;
};
struct disk *firstdisk,*diskptr;

#define INIT_DISKNAMES firstdisk=diskptr=NULL
#define FREE_DISKNAMES free_disknames()

void free_disknames();

/*----------------------------------------------------------------------*/

char *strstr(), *strtoupper(), *getstr();
void putstr();
char *skrunch(), *getenv();
void unskrunch(), get_dlpath();
struct FileHandle *opendl(), *opentmp();

char *strchr(), *malloc();
extern struct FileHandle *Open();


void finddisk();
/* finddisk uses this */
char dirlist[NUMLINES][LINELEN];

int update(), remove(), loaddl(), savedl(), sdir(), newdl();

void update_free(), ls_free(), remove_free();


void main(argc,argv)
  int argc;
  char *argv[];
{

  glargv=argv;
  glargc=argc;

  if( (confh=Output())==NULL )
    exit(1);

  if( argc==1)
  {
    putstr(HELPTEXT1);
    putstr(HELPTEXT2);
    putstr(HELPTEXT3);
    exit(0);
  }

  dldisk=getenv("env:dldisk",dldisk);
  dltemp=getenv("env:dltemp",dltemp);
  sdir_pat=getenv("env:dldrive",sdir_pat);
  get_dlpath();

  if( *argv[1]=='-' )
  {
    switch( *(argv[1]+1) )
    {
       case 'd':  if( sdir(0) )
                    putstr("SaveDir failed!\n");
                  break;

       case 'a':  if( sdir(1) )
                    putstr("SaveDir failed!\n");
                  break;

       case 's':  if( savedl() )
                    putstr("SaveDL failed!\n");
                  break;

       case 'l':  if( loaddl() )
                    putstr("LoadDL failed!\n");
                  break;

       case 'u':  if( update() )
                    putstr("Update failed!\n");
                  break;

       case 'r':  if( remove() )
                    putstr("Remove failed!\n");
                  break;

       case 'n':  if( newdl() )
                    putstr("Empty DiskList created (Save if in RAM).\n");
                  break;

       default :  putstr("Unknown Option.\n");
    }
  }
  else finddisk();

}


char *getenv(fullpath,var)
  char *fullpath, *var;
{
  struct FileLock *lock,*Lock();
  struct FileHandle *fh;
  char *buffer;
  int  len;

  if( lock=Lock(fullpath,(long)ACCESS_READ) )
  {
    UnLock(lock);
    if( !(fh=Open(fullpath,(long)MODE_OLDFILE)) )
      return var;
    if( !(buffer=(char *)malloc(MAXPATHLEN+1)) )
    {
      putstr(outofmem);
      Close(fh);
      return var;
    }
    len=Read(fh,buffer,(long)MAXPATHLEN);
    Close(fh);
    *(buffer+len)='\0';
    return buffer;
  }
  return var;
}

void finddisk()
{
  int  kase=0, thisone=0, line=0;
  int i;
  char *p;

  if( !(fdl=opendl((long)MODE_OLDFILE)) )
    return;

  /* Converts non-case sensitive keywords to upper case */
  for(i=1; i<glargc; i++)
    if( *glargv[i]!='!' )
      strtoupper(glargv[i]);

  /* get line of DiskList */
  while( getstr(buffer,fdl) !=NULL )
  {

    /* first checks for a startchar of a dirlist */
    if( strchr(buffer,(char)STARTCHAR) )
    {
       if( thisone )
       {
          /* print the directory */
          line--;
          for(i=0; i<line; i++)
            unskrunch(dirlist[i]);

          /* if at the end of the dirlist array- prints 'cut off' */
          if( line==NUMLINES-1 )
            putstr(cutoff);

          thisone=0;
        }

        line=1;
    }


    if( line )
    {
       /* Copies the line in buffer to the stored dirlist */
       strncpy(dirlist[line-1],buffer,MAXBUF);

       if( ++line > NUMLINES )
         line--;

       /* Checks for given keywords */
       if( thisone==0 )
         for(i=1; i<glargc; i++)
         {
           if( *glargv[i]=='!' )
           {
             p=glargv[i]+1;
             kase=1;
           }
           else
           {
             p=glargv[i];
             kase=0;
           }

           if( strstr(buffer,p,kase) )
             thisone=1;
           else
           {
             thisone=0;
             break;
           }
         }

    }/* if(line) */

  } /* while( get next buffer) */

  if( thisone )
  {
        line--;
        for(i=0; i<line; i++)
          unskrunch(dirlist[i]);

        /* if at the end of the dirlist array- prints 'cut off' */
        if( line==NUMLINES-1 )
          putstr(cutoff);
  }

  Close(fdl);
}


#define update_cleanup(x)  { update_free(); return x; }

update()
{
  int  i;

  if( !(fdl=opendl((long)MODE_OLDFILE)) )
    return 1;

  if( !(ftmp=opentmp((long)MODE_OLDFILE)) )
  {
    Close(fdl);
    return 0;
  }

  INIT_DISKNAMES;
  init_bufblks();

  /* finds the disks names in TempDL */
  while( getstr(buffer,ftmp) !=NULL )
  {
      if( strstr(buffer,STARTSTR,1) )
      {
         skrunch(buffer);
         if( storename(buffer) )
           update_cleanup(3)
      }
  }
  if( fcnerr )
    update_cleanup(10)

  if( storedl() )
    update_cleanup(4)

  Seek(ftmp,0L,(long)OFFSET_BEGINNING);

  /* store TempDL in memory */
  while( getstr(buffer,ftmp) !=NULL )
    if( store( skrunch(buffer) ) )
      update_cleanup(5)
  if( fcnerr )
    update_cleanup(6);

  Close(fdl);
  if( !(fdl=opendl((long)MODE_NEWFILE)) )
  {
    Close(ftmp);
    FREE_DISKNAMES;
    free_bufblks();
    return 7;
  }

  /* write the in memory disklist to the one in ram: */
  for(i=0; i<=bufcount; i++)
    if( fputstr(bufferblock[i],fdl) )
      update_cleanup(8)

  Close(ftmp);
  if( !(ftmp=opentmp((long)MODE_NEWFILE)) )
    update_cleanup(9)

  update_free();
  return 0;
}

void update_free()
{
  Close(fdl);
  Close(ftmp);
  FREE_DISKNAMES;
  free_bufblks();
}


#define remove_cleanup(x)  { remove_free(); return x; }

remove()
{
  int i;

  if( !(fdl=opendl((long)MODE_OLDFILE)) )
    return 2;

  INIT_DISKNAMES;
  init_bufblks();

  /* finds the disk name in DiskList */
  while( getstr(buffer,fdl) !=NULL )
  {
      if( strstr(buffer,glargv[2],1) )
      {
         if( storename(buffer) )
           remove_cleanup(3)
      }
  }
  if( fcnerr )
    remove_cleanup(4);

  Seek(fdl,0L,(long)OFFSET_BEGINNING);

  if( storedl() )
    remove_cleanup(5)

  Close(fdl);
  if( !(fdl=opendl((long)MODE_NEWFILE)) )
  {
    FREE_DISKNAMES;
    free_bufblks();
    return 6;
  }

  /* write the in memory disklist to the one in ram: */
  for(i=0; i<=bufcount; i++)
    if( fputstr(bufferblock[i],fdl) )
      remove_cleanup(7)

  remove_free();
  return 0;
}


void remove_free()
{
  Close(fdl);
  FREE_DISKNAMES;
  free_bufblks();
}



/* loads the DiskList into memory but leaves out the directories in the
   diskname list, pointed to by firstdisk */
storedl()
{
  int storeit=1;  /* saving this directory */

  /* doesn't store directories that are in the temp file */
  while( getstr(buffer,fdl) !=NULL )
  {
      if( strchr(buffer,(char)STARTCHAR) )
      {
         storeit=1;
         diskptr=firstdisk;
         while(diskptr)
         {
           if( strstr(buffer,diskptr->name,1) )
           {
             storeit=0;
             break;
           }
           diskptr=diskptr->next;
         }
      }

      if(storeit)
       if( store(buffer) )
         return 1;
  }

  return fcnerr;
}



/* Saves the name of a disk in a linked list structure */
storename(buffer)
 char *buffer;
{
  if( diskptr==NULL )
  {
    if( (firstdisk=(struct disk *)malloc(sizeof(struct disk)) )==NULL)
    {
      putstr(outofmem);
      return 1;
    }
    diskptr=firstdisk;
    strncpy(diskptr->name,buffer,MAXBUF);
    diskptr->next=NULL;
  }
  else
  {
    if( (diskptr->next=(struct disk *)malloc(sizeof(struct disk)) )==NULL)
    {
      putstr(outofmem);
      return 2;
    }
    diskptr=diskptr->next;
    strncpy(diskptr->name,buffer,MAXBUF);
    diskptr->next=NULL;
  }

  return 0;
}

void free_disknames()
{
  struct disk *p;

  while(firstdisk)
  {
    p=firstdisk->next;
    free(firstdisk);
    firstdisk=p;
  }
}


/* prints line to stdout unskrunched */
/* format  [BYTE]=>   [SKRUNCHAR] [# of spaces ]   */

void unskrunch(line)
  char *line;
{
  char output[LINELEN], *p;
  int i;

  p=output;
  while(*line)
  {
    switch((int)*line)
    {
      case SKRUNCHAR : line++;
                       for(i=SKRUNOFFS; i<(int)*line; i++)
                         *(p++)=' ';
                       break;

      case STARTCHAR : strncpy(p,STARTSTR,MAXBUF);
                       p+=strlen(STARTSTR);
                       break;

      default : *(p++)=*line;
    }

    line++;
  }
  *p='\0';
  putstr(output);
}


char *skrunch(line)
  char *line;
{
  char skbuf[LINELEN],*p;
  int  spaces=0;

  /* first tokenizes the STARTSTR/XnSTR if in line */

  tokenize(line,STARTSTR,STARTCHAR);

  /* this is the compaction of >2 spaces */
  p=skbuf;
  while( *line )
  {
    if( *line==' ' )
      spaces++;
    else
    {
      if( spaces )
        if( spaces>2 )
        {
          *(p++)=(char)SKRUNCHAR;
          *(p++)=(char)(spaces+SKRUNOFFS);
          spaces=0;
        }
        else
        {
          *(p++)=' ';
          if( spaces>1 ) *(p++)=' ';
          spaces=0;
        }

      *p=*line;
      p++;
    }
    line++;
  }
  *p='\0';
  strncpy(buffer,skbuf,MAXBUF);
  return buffer;
}


/* will tokenize first occurance of STR in LINE */
tokenize(line,str,chr)
  char *line,*str;
  int  chr;
{
  char *p,*q;

  if( p=strstr(line,str,1) )
  {
    q=p+strlen(str);
    *p=(char)chr;
    strncpy(p+1,q,MAXBUF);
    return 1;
  }
  return 0;
}



/* Uses:  chrcount bufcount bufferblock[] bufptr */
/* stores line to memory */
store(buffer)
  char *buffer;
{
  int i;

  i=strlen(buffer);
  chrcount+=i;
  if( chrcount>=BLKSIZE )
  {
    *bufptr='\0';
    chrcount=i;
    if( (++bufcount)>=BUFBLKS )
    {
      putstr("Error: DiskList too large (Increase buffer size in source).\n");
      return 1;
    }

    if( (bufferblock[bufcount]=(char *)malloc(BLKSIZE) )==NULL)
    {
       putstr(outofmem);
       return 2;
    }

    bufptr=bufferblock[bufcount];
  }

  strncpy(bufptr,buffer,MAXBUF);
  bufptr+=i;

  return 0;
}

void init_bufblks()
{
  bufptr=NULL;
  bufcount=-1;
  chrcount=BLKSIZE+1;
}

void free_bufblks()
{
  int i;

  for(i=1; i<=bufcount; i++)
    free(bufferblock[i]);
}


/*   OK, this is not quite a true strstr() with case switch.
     The sub string must be in upper case if kase=0 (?!sorry)    */

char *strstr(str,sub,kase)
  char *str,*sub;
  int  kase;
{
  int length;

  length=strlen(sub);

  if( kase )
    str=strchr(str,*sub);
  else
    str=strchr(strtoupper(str),*sub);

  while( str )
  {
    if( strncmp(str,sub,length) )
      str++;
    else
      return str;

    str=strchr(str,*sub);
  }

  return NULL;
}

char *strtoupper(s)
  char *s;
{
    register char *p = s;

    while(*p)
    {
       *p = toupper(*p);
       p++;
    }
    return(s);
}

void get_dlpath()
{
  struct FileLock *lock,*Lock();

  if( lock=Lock(dltemp,(long)ACCESS_READ) )
  {
    dlpath=dltemp;
    UnLock(lock);
  }
  else
    dlpath=dldisk;
}

struct FileHandle *opendl(mode)
  long mode;
{
  struct FileHandle *fdl;

  if( (fdl=Open(dlpath,mode))==NULL)
  {
    putstr("Error: Could not open DiskList\n");
    return 0;
  }
  return fdl;
}


#define MODE_APPEND 1000L

struct FileHandle *opentmp(mode)
  long mode;
{
  struct FileHandle *ftmp;
  struct FileLock *lock,*Lock();
  int    seek=0;

  if( mode==MODE_APPEND)
  {
    if( lock=Lock(tempdl,(long)ACCESS_READ) )
    {
      UnLock(lock);
      mode=(long)MODE_OLDFILE;
      seek=1;
    }
    else
      mode=(long)MODE_NEWFILE;
  }

  if( (ftmp=Open(tempdl,mode))==NULL)
  {
    putstr("Error: Could not open RAM:TempDL\n");
    return 0;
  }

  if( seek )
    Seek(ftmp,0L,(long)OFFSET_END);

  return ftmp;
}


sdir(recur)
  int recur;
{
  char *p;


  if( glargc>2 )
    p=glargv[2];
  else
    p=sdir_pat;

  if( !(ftmp=opentmp(MODE_APPEND)) )
    return 1;

  if( do_ls(p,ftmp,recur) )
  {
    Close(ftmp);
    return 2;
  }
  Close(ftmp);
  return 0;
}


#define ls_cleanup(x) { ls_free(lock,f_info); return x; }

do_ls(pat,fh,recur)
  char *pat;
  struct FileHandle *fh;
  int recur;
{
  struct FileLock *lock=NULL, *Lock();
  struct FileInfoBlock *f_info;

  char *s, *tempstr;
  int  slen, isadir, col, patchg=0, firstrun=1;
  char *spaces1, *spaces2;
  struct disk *nextdir;
  char basename[80], tempname[80];


  /* strings of 18 & 36 spaces for formatting output */
  /*       012345678901234567                        */
  spaces1="                  ";
  /*       0123456789012345678901234567890123456     */
  spaces2="                                     ";

  INIT_DISKNAMES;
  nextdir=NULL;

  strncpy(tempname,"/",2);
  tempstr=tempname+1;

  if((f_info=(struct FileInfoBlock *)
     AllocMem((long)sizeof(struct FileInfoBlock),0L))==0)
  {
    putstr(outofmem);
    FREE_DISKNAMES;
    return 1;
  }

  do
  {
    if( firstdisk )
    {

      while( *(nextdir->name)=='/' )
      {
        strncpy(basename,(nextdir->name)+1,79);
        strncpy(basename+strlen(basename),"/",2);
        nextdir=nextdir->next;
        if( !nextdir )
          ls_cleanup(0)
      }
      strncpy(tempname+1,basename,78);
      slen=strlen(tempname);
      strncpy(tempname+slen,nextdir->name,79-slen);
      if( storename(tempname) )
        ls_cleanup(1);
      pat=tempname+1;
    }

    do
    {
      if( firstrun )
      {
        if(patchg==1)
        {
          patchg=2;
          strncpy(tempstr,f_info->fib_FileName,78);
          slen=strlen(tempstr);
          tempstr[slen]=':';
          strncpy(tempstr+slen+1,s+4,78-slen);
          pat=tempstr;
        }
        else if( strchr(pat,':')<strchr(pat,'\0')-1 )
        {
          patchg=2;
          if( !(strncmp(pat,"df0:",4) && strncmp(pat,"df1:",4)) )
          {
            patchg=1;
            strncpy(tempstr,pat,4);
            *(tempstr+4)='\0';
            s=pat;
            pat=tempstr;
          }
        }
      }

      if((lock=Lock(pat,(long)ACCESS_READ))==0)
        ls_cleanup(2)

      if((Examine(lock,f_info))==0)
        ls_cleanup(3)
    }
    while( patchg==1 );
    firstrun=0;

    if( fputstr(STARTSTR,fh) )
      ls_cleanup(4)
    if( firstdisk )
    {
      fputstr(tempname+1,fh);
      fputstr ("\n",fh);
    }
    else
    {
      if( patchg )
      {
        patchg=0;
        if( fputstr(pat,fh) )
          ls_cleanup(5)
        fputstr ("\n",fh);
        strcpy(basename,pat,79);
        slen=strlen(basename);
        basename[slen++]='/';
        basename[slen]='\0';

      }
      else
      {
        if( fputstr(f_info->fib_FileName,fh) )
          ls_cleanup(6)
        fputstr (":\n",fh);
        strncpy(basename,f_info->fib_FileName,78);
        slen=strlen(basename);
        basename[slen++]=':';
        basename[slen]='\0';
      }
    }
    col=0;

    while( (ExNext(lock,f_info))!=0)
    {
      s=f_info->fib_FileName;
      slen=strlen(s);
      isadir= (f_info->fib_DirEntryType>0);

      if((col == 3) && slen >18) {
           fputstr("\n",fh);
           col = 0;
      }

      if (isadir)
        fputstr ("\033[33m ",fh);
      else
        fputstr (" ",fh);

      if (slen >18) {
         if( fputstr(s,fh) )
           ls_cleanup(7)
         col += 2;
         if( fputstr(spaces2+slen,fh) )
           ls_cleanup(8)
      }
      else {
         if( fputstr(s,fh) )
           ls_cleanup(9)
         if( fputstr(spaces1+slen,fh) )
           ls_cleanup(10)
         col++;
      }

      if( isadir && recur )
        if(storename(s))
          ls_cleanup(1);

      if (isadir)
        fputstr("\033[0m",fh);

      if (col > 3) {
         fputstr("\n",fh);
         col = 0;
      }

    }
    fputstr("\n",fh);
    if( col )
      fputstr("\n",fh);

    UnLock(lock);
    lock=NULL;

    if( nextdir )
      nextdir=nextdir->next;
    else
      nextdir=firstdisk;
  }
  while( nextdir );

  ls_free(lock,f_info);
  return 0;
}

void ls_free(lock,f_info)
  struct FileLock *lock;
  struct FileInfoBlock *f_info;

{
  if( lock )
    UnLock(lock);
  FreeMem(f_info,(long)sizeof(struct FileInfoBlock));
  FREE_DISKNAMES;
}

void putstr(str)
  char *str;
{
    Write(confh,str,(long)strlen(str));
}


fputstr(str,fh)
  char *str;
  struct FileHandle *fh;
{
   long len;
   len=(long)strlen(str);
   if( Write(fh,str,len)==len)
     return 0;
   return 1;
}

/* must be used in the WHILE( GETSTR()!=NULL) fashion */
char *getstr(line,f)
  char *line;
  struct FileHandle *f;
{
  static char *buffer, *bufptr;
  static int  last_read;
  int len, n=MAXBUF;
  char temp, *p, empty='\0';

  if( !buffer )
  {
    if( (buffer=(char *)AllocMem((long)CP_BUFF+1,0L))==0)
    {
       putstr(outofmem);
       fcnerr=1;
       return (char *)0;
    }
    bufptr=&empty;
  }

  while(1)
    if( p=strchr(bufptr,'\n') )
    {
      temp=*(++p);
      *p='\0';
      strncpy(line,bufptr,n);
      *p=temp;
      bufptr=p;
      return line;
    }
    else
    {
      strncpy(line,bufptr,n);
      len=strlen(line);
      n-=len;
      line+=len;

      if( last_read )
      {
        FreeMem(buffer,CP_BUFF);
        buffer=0;
        last_read=0;
        return (char *)0;
      }
      else
        if( (len=Read(f,buffer,CP_BUFF)) <1 )
          last_read=1;
        else
        {
          last_read= len!=CP_BUFF;
          bufptr=buffer;
          *(buffer+len)='\0';
        }
    }
}

savedl()
{
  if( update() )
    return 1;

  if( strcmp(dlpath,dldisk) )
  {
    if( copy(dlpath,dldisk) )
      return 1;
    DeleteFile(dlpath);
    DeleteFile(tempdl);
  }
  return 0;
}

loaddl()
{
  if( strcmp(dltemp,dlpath) )
    if( copy(dlpath,dltemp) )
      return 1;
  return 0;
}

newdl()
{
  struct FileHandle *fh;

  putstr("Are you sure? ");
  Read(confh,buffer,LINELEN);

  if( *buffer=='y' )
  {
    if( fh=Open(dlpath,(long)MODE_NEWFILE) )
    {
      Close(fh);
      return 0;
    }
    return 1;
  }
  return 0;
}

copy(src,dest)
  char *src,*dest;
{
  struct FileHandle *sfh,*dfh;
  char *buffer;
  int  len;

  if((sfh=Open(src,(long)MODE_OLDFILE))==0)
    return 1;

  if((dfh=Open(dest,(long)MODE_NEWFILE))==0)
  {
    Close(sfh);
    return 2;
  }
  if( (buffer=(char *)AllocMem(CP_BUFF,0L))==0)
  {
    Close(sfh);
    Close(dfh);
    putstr(outofmem);
    return 3;
  }

  do
  {
    len=Read(sfh,buffer,CP_BUFF);
    Write(dfh,buffer,(long)len);
  }
  while( len!=0 );

  FreeMem(buffer,CP_BUFF);

  Close(sfh);
  Close(dfh);
  return 0;
}




