/*
 *
 * Copyright 1989 Edwin Hoogerbeets
 *
 * This code may be freely redistributed as long as no charges other than
 * reasonable copying fees are levied for it.
 *
 * Usenet: ehoogerbeets@rose.waterloo.edu
 *         or try uunet!watmath!watrose!ehoogerbeets  [school account]
 *         or edwin@watcsc.waterloo.edu               [permanent account]
 * CIS:    72647,3675                                 [expensive account]
 *
 * Works like the Unix move.
 *
 * Usage: mv [-cfiIsuUvVx]    [-bMaxBufferSize] [-] file1 file2\n");
 *        mv [-cfiIsuUvVx]    [-bMaxBufferSize] [-] path1 [path2 ...] dir\n");
 *        rm [-cdfimrRsvV]    [-bMaxBufferSize] [-] path1 [path1 ...]\n");
 *        cp [-fiImnsuUvVx]   [-bMaxBufferSize] [-] file1 file2\n");
 *        cp [-fiImnsuUvVxrR] [-bMaxBufferSize] [-] path1 [path2 ...] dir\n");
 *
 * Where path is either a file or a directory.
 *
 *  -b use the next argument as the maximum buffer size in K when
 *     copying or moving
 *  -c act like cp instead (as in "mv -c" means do a cp instead of mv)
 *  -d remove directories only if they are empty (as in AmigaDOS Delete)
 *  -f force quiet mode, overwriting destination files if necessary.
 *  -i force Unix-like interactive mode (only when clobbering existing files)
 *  -I force interactive mode all files
 *  -m act like mv instead
 *  -n do not copy file dates, comments and protections (use "n"ew dates..)
 *  -p opposite of -n, and also the default
 *  -r recursively do directories as well (mv is always recursive)
 *  -R same as -r
 *  -s silently process things (this option is default, and opposite to -v)
 *  -u update newer files only on copy or move
 *  -U update newer files, but only if the destination file exists
 *  -v verbosely tell what is going on (opposite of -s)
 *  -V print version
 *  -x act like rm instead
 *  -  end of options (useful to remove a file whose name starts with
 *     a dash eg. "-d")
 *
 * Moves, etc. across devices are supported.
 *
 */

#ifdef DEBUG
#include <stdio.h>
#endif

#include <fcntl.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#ifdef ARP
#include <libraries/arpbase.h>
#include <libraries/arpfunctions.h>
#endif
#include <exec/memory.h>
#include <ctype.h>
char *version = "mv/cp/rm v1.2 copyright 1989 Edwin Hoogerbeets\n";

#define FIBSIZE (long)sizeof(struct FileInfoBlock)
#define BUFSIZE 256

typedef struct fl {
  char name[BUFSIZE];
  struct fl *next;
} filenode;

#define FNSIZE (long)sizeof(filenode)
#define INCREMENT 2     /* how much to indent when using verbose mode   */
#define NOCAPS    0     /* do not correct capitalization in isdir()     */
#define CAPS      1     /* do correct capitalization. (see isdir())     */
#define NOCREATE  0     /* do not create the destination dir in mvdir() */
#define CREATE    1     /* do create the destination dir in mvdir()     */

#ifdef __STDC__
#include <proto/all.h>
#include <string.h>
#include <stdlib.h>
int emit(char *);
#if 0
int ind(int);
#endif
void pathcat(char *, char *);
int arglength(filenode *);
int haswild(char *);
int samedev(char *, char *);
int isdir(char *, struct FileInfoBlock **, int, long);
int rm_file(char *, long, int);
int rm_dir(char *, long, int);
int rm(char *, long, int);
int mv(char *, char *, long, int);
int mvdir(char *, struct FileInfoBlock *, char *, long, int, int);
int mvfile(char *, struct FileInfoBlock *,char *, long, int);
int mv2d(char *, struct FileInfoBlock *, char *, long, int);
int mv2f(char *, struct FileInfoBlock *, char *, struct FileInfoBlock *,
         long, int);
int cp(char *, struct FileInfoBlock *, char *, long);
int move(char *, char *, long);
int setdate(struct DateStamp *, char *, long);
int isdeletable(char *, long);
int comparedates(struct DateStamp *, struct DateStamp *);
void breakcheck(long);
void freefib(struct FileInfoBlock *);
void freebuf(char *);
void freefilenodes(filenode *);
void closethings(void);
void usage(long);
char *strrpbrk(char *, char *);
char *basename(char *);
char *newbuf(long);
char *parent(char *);
long mem(void);
long send_date(struct MsgPort *, BPTR, BPTR, struct DateStamp *);
long AvailMem(long);
filenode *end(filenode *);
filenode *expand(char *, int *, long);
struct FileInfoBlock *newfib(long);
filenode *newfilenode(void);
#else
/* these are so Manx won't complain about ptr/int conversions, etc. */
extern BPTR              ParentDir();
extern BPTR              CreateDir();
extern BPTR              Lock();
extern BPTR              CurrentDir();
extern int               Examine();
extern char              *AllocMem();
extern BPTR              Open();
extern struct Library    *OpenLibrary();
extern struct _dev       *_devtab;
extern struct MsgPort    *DeviceProc();
#endif

#define MVFLAG    (1L<<0)
#define CPFLAG    (1L<<1)
#define RMFLAG    (1L<<2)
#define RFLAG     (1L<<3)
#define FFLAG     (1L<<4)
#define iFLAG     (1L<<5)
#define NFLAG     (1L<<6)
#define DFLAG     (1L<<7)
#define BFLAG     (1L<<8)
#define uFLAG     (1L<<9)
#define VFLAG     (1L<<10)
#define IFLAG     (1L<<11)
#define UFLAG     (1L<<12)
#define ARPFLAG   (1L<<13)
#define INITFLAGS ( CPFLAG | NFLAG | (VERBOSE<<10) )

#define isupdate(flag)      ((flag) & uFLAG ? 1 : ((flag) & UFLAG ? 2 : 0))
#define isinteract(flag)    ((flag) & iFLAG ? 1 : ((flag) & IFLAG ? 2 : 0))
#define setflag(flag,bit)   ((flag) |=  (bit))
#define clearflag(flag,bit) ((flag) &= ~(bit))
#define testflag(flag,bit)  ((flag) & (bit))

#if 0
int mvflag = 0, /* is this a move command? */
    cpflag = 1, /* is this a copy command? (default) */
    rmflag = 0, /* is this a remove command? */
    rflag  = 0, /* is this command recursive? */
    fflag  = 0, /* don't ask if it should overwrite, just do it */
    iflag  = 0, /* do interactive mode */
    nflag  = 1, /* copy file dates, comments and protections */
    dflag  = 0, /* delete directories only if they are empty */
    bflag  = 0, /* use maximum buffer size? */
    uflag  = 0, /* update. copy or move newer files only */
    vflag  = VERBOSE; /* to be verbose or not */
#endif

char commandname[32] = "";
long buffersize = 0;

long ofile;     /* output file handle */
long ifile;     /* input file handle  */

long nobreak = 1;  /* used for checking for breaks */

/* used for quick indentation with verbose option */
char *indentstring =   "                                        \
                                        ";

#ifdef ARP

typedef struct BAP {
  struct AnchorPath bap_ap;
  char padding[BUFSIZE];
} BigAnchorPath;

#define APSIZE (long)sizeof(BigAnchorPath)

#if 0
int arpflag = 0;
#endif

#endif

#ifdef ARP
struct ArpBase       *ArpBase;
struct IntuitionBase *IntuitionBase;
struct GfxBase       *GfxBase;
#endif

#if AZTEC_C && !DEBUG
#include <exec/alerts.h>
#include <workbench/startup.h>

extern long _savsp, _stkbase;

extern int errno;

extern int _argc, _arg_len;
extern char **_argv, *_arg_lin;

_main(alen, aptr)
long alen;
char *aptr;
{
        struct Process *pp, *FindTask();

        _stkbase = _savsp - *((long *)_savsp+1) + 8;
        *(long *)_stkbase = 0x4d414e58L;

        pp = FindTask(0L);
        _cli_parse(pp, alen, aptr);

        exit(main(_argc, _argv));
}
#endif

/* set nobreak to 0 if break has been received (^C) */
void breakcheck(flags)
long flags;
{
  long SetSignal();

  if ( nobreak ) {
    nobreak = !(SetSignal(0,0) & SIGBREAKF_CTRL_C);
    if ( !testflag(flags,FFLAG) && !nobreak ) {
      Write(Output(),"^C\n",3);
    }
  }
}

#ifdef LATTICE
#define index(a,b) strchr(a,b)
#endif

/*
 * The following few routines were taken from my edlib1.1 source. They
 * are included here so that anyone can recompile this source without
 * the library. (I'll be happy to send you edlib if you want it.)
 */

char *strrpbrk(str, charset)
register char *str, *charset;
{
  register char *temp;
#ifdef AZTEC_C
  extern char *index();
#endif

  temp = str + strlen(str) - 1;

  while ( temp != (str - 1)  && !index(charset, *temp) )
    --temp;

  return( (temp != (str - 1)) ? temp : NULL);
}

#ifdef AZTEC_C
int stricmp(str1,str2)
register char *str1,*str2;
{
    register int index = 0;

    while ( str1[index] && str2[index] &&
            tolower(str1[index]) == tolower(str2[index]) )
        ++index;

    return( (tolower(str1[index]) < tolower(str2[index])) ? -1 :
          ( (tolower(str1[index]) > tolower(str2[index])) ?  1 : 0) );
}
#endif

/* append a slash or not, making string ready for appending a filename */
void pathcat(dir,file)
char *dir, *file;
{
  register char c;
  register int len = strlen(dir);

  /*
   * only append a slash if the file name is not the current directory,
   * (ie. "") the last character is not null, and the last character
   * is neither of ':' or '/'. This part is so much easier under Unix
   * path naming conventions, but hey, Amigoids gotta be different!
   */
  if ( len && (c = dir[len - 1] ) && c != ':' && c != '/' )
    strcat(dir,"/");

  strcat(dir,file);
}

/* return a pointer to the first character of a file name in a path name */
char *basename(buf)
register char *buf;
{
  register char *foo = strrpbrk(buf,":/");

  return( foo ? (foo + 1) : buf );
}

/* end of edlib routines */

/* write out a string */
int emit(str)
char *str;
{
  return(Write(ofile,str,strlen(str)));
}

/* indent for verbose option */
#define ind(howfar)  Write(ofile,indentstring,(howfar))

#if 0
int ind(howfar)
int howfar;
{
  return(Write(ofile,indentstring,howfar));
}
#endif

/*
 * return the length of the largest piece of memory that could possibly
 * be contiguous
 */
long mem()
{
   long chipmem, fastmem;
   extern long AvailMem();

   Forbid();
   chipmem = AvailMem(MEMF_CHIP);
   fastmem = AvailMem(MEMF_FAST);
   Permit();

   return(chipmem>fastmem ? chipmem : fastmem);
}

/* make a new file info block and return a pointer to it */
struct FileInfoBlock *newfib(flags)
long flags;
{
  struct FileInfoBlock *fib;

  fib = (struct FileInfoBlock *) AllocMem(FIBSIZE, MEMF_CLEAR);

  if ( !fib ) {
    if ( !testflag(flags,FFLAG) ) {
      emit(commandname);
      emit(": Out of memory!\n");
    }
    return(NULL);
  }

  return(fib);
}

/* get rid of a used file info block */
void freefib(fib)
struct FileInfoBlock *fib;
{
  if ( fib )
    FreeMem((char *)fib,FIBSIZE);
}

/* make a new buffer and return a pointer to it */
char *newbuf(flags)
long flags;
{
  register char *temp = AllocMem(BUFSIZE,MEMF_CLEAR|MEMF_PUBLIC);

  if ( !temp ) {
    if ( !testflag(flags,FFLAG) ) {
      emit(commandname);
      emit(": Out of memory!\n");
    }
    return(NULL);
  }

  return(temp);
}

/* free a buffer previously allocated with newbuf() */
void freebuf(buf)
char *buf;
{
  if ( buf ) {
    FreeMem(buf,BUFSIZE);
  }
}

/* make a new filenode structure and return a pointer to it */
filenode *newfilenode()
{
  filenode *new = (filenode *)
    AllocMem(FNSIZE,MEMF_PUBLIC|MEMF_CLEAR);

  if ( new ) {
    new->name[0] = '\0';
    new->next = NULL;
  }

  return(new);
}

/* free a list of filenode structures */
void freefilenodes(file)
filenode *file;
{
  if ( file ) {
    freefilenodes(file->next);

    FreeMem((char *)file,FNSIZE);
  }
}

/* return a pointer to the last element of a filenode list */
filenode *end(file)
filenode *file;
{
  if ( file ) {
    if ( file->next ) {
      return(end(file->next));
    } else {
      return(file);
    }
  } else {
    return(NULL);
  }
}

/* return the length of a list of filenodes */
int arglength(file)
filenode *file;
{
  if ( file->next ) {
    return(arglength(file->next)+1);
  } else {
    return(1);
  }
}

/* Does the input string contain a wildcard? */
int haswild(name)
char *name;
{
  register int foo = 0;

  /* ARP has a ridiculous number of wildcards, doesn't it? */
  while ( name[foo]        && name[foo] != '*' && name[foo] != '?' &&
          name[foo] != '#' && name[foo] != '(' && name[foo] != '|' &&
          name[foo] != '[' && name[foo] != '%' && name[foo] != '~' )
    ++foo;

  return ( (int) name[foo] );
}


/*
 * This routine takes a string with possibly a wildcard in it and expands
 * it to a list of filenode structures. If arp isn't opened or if it
 * wasn't compiled with arp, then it creates a list of 1 filenode containing
 * the argument it was passed. The pointer to nomem is where it puts the
 * error code for "low on available memory" errors.
 */
filenode *expand(name,nomem,flags)
char *name;
int *nomem;
long flags;
{
  filenode *file;

# ifdef ARP
  if ( testflag(flags,ARPFLAG) && haswild(name) ) {
    filenode *temp;
    int error;
    BigAnchorPath *anchor;

    *nomem = 0;

    anchor = (BigAnchorPath *)
        AllocMem(APSIZE,MEMF_CLEAR|MEMF_PUBLIC);

    anchor->bap_ap.ap_Length = BUFSIZE;

    if ( FindFirst(name,anchor) ) {
      FreeAnchorChain(anchor);
      return(NULL);
    }

    if ( file = newfilenode() ) {
      strcat(file->name,anchor->bap_ap.ap_Buf);
    } else {
      *nomem = 1;
      FreeAnchorChain(anchor);
      return(NULL);
    }
#   ifdef DEBUG
    printf("First matched file: %s\n",file->name);
#   endif

    temp = file;

    while ( !(error = FindNext(anchor)) ) {

      if ( !(temp->next = newfilenode()) ) {
        *nomem = 1;
        FreeAnchorChain(anchor);
        freefilenodes(file);
        return(NULL);
      }

      temp = temp->next;

      strcat(temp->name,anchor->bap_ap.ap_Buf);

#   ifdef DEBUG
    printf("Next matched file: %s\n",temp->name);
#   endif
    }

    FreeAnchorChain(anchor);
  } else {
# endif
    file = newfilenode();

    if ( file ) {
      *nomem = 0;
      strcat(file->name,name);
    } else {
      *nomem = 1;
      return(NULL);
    }

# ifdef ARP
  }
# endif

  return(file);
}

#ifdef AZTEC_C
/* well, I guess this is a pro-choice program. ;-) */
_abort()
{
  exit(-1);
}
#endif

/*
 * make a string containing the path part of a full AmigaDOS path name.
 * return a pointer to this string.
 */
char *parent(name)
char *name;
{
  register char *foo;
  char *temp = AllocMem((long)strlen(name)+1,MEMF_CLEAR);

  strcpy(temp,name);

  /* get a pointer to the filename part */
  foo = basename(temp);

  /*
   * lop off the file name part -- the length of the whole original
   * string must still be freed when freeing what temp points to.
   */
  *foo = '\0';

  return(temp);
}

/* are the two files on the same volume? If you can't tell, guess */
int samedev(src,dst)
char *src, *dst;
{
  char *temp;
  struct FileLock *srclock, *dstlock;
  struct DateStamp *srcdate, *dstdate;

  /* Simultaneous get a lock and convert BPTR to a C pointer */
  srclock = (struct FileLock *)BADDR(Lock(src,ACCESS_READ));

  if (srclock == NULL) {
    return(-1);
  }

  /* get the creation datestamp of the volume, which is practically
   * unique across volumes.
   */
  srcdate = &(((struct DeviceList *)
                BADDR(srclock->fl_Volume))->dl_VolumeDate);


  /* find the name of the parent directory */
  temp = parent(dst);

  dstlock = (struct FileLock *)BADDR(Lock(temp,ACCESS_READ));

  FreeMem(temp,strlen(dst)+1);

  if (dstlock == NULL) {
    return(-2);
  }

  /* get the datestamp of the destination */
  dstdate = &(((struct DeviceList *)
                BADDR(dstlock->fl_Volume))->dl_VolumeDate);

  UnLock(((long)srclock) >> 2);  /* You must UnLock or the GURU visits */
  UnLock(((long)dstlock) >> 2);

# ifdef DEBUG
  printf("%s and %s are %son the same volume\n",src,dst,
        (!comparedates(srcdate,dstdate))?"":"not ");
# endif

  return( !comparedates(srcdate,dstdate) );
}

/*
 * Is the named file a directory? Get a File Info Block and point fib to
 * it and return the results.
 *
 * return   0  for not a dir (ie. file)
 * return   1  for a dir
 * return   2  for no access to file
 * return   3  for not being able to examine file
 *
 */
int isdir(path,fib,newname,flags)
char *path;
struct FileInfoBlock **fib;
int newname;
long flags;
{
  BPTR lock;
  register int result;

  /* allocate a word aligned memory block to hold our info */
  *fib = newfib(flags);

  if ( !(lock = Lock(path,ACCESS_READ)) ) {
    return(2);
  }

  if ( *fib ) {
    if ( Examine(lock,*fib) ) {

      /* if the source is not a directory .. */
      result = (*fib)->fib_DirEntryType > 0 ? 1 : 0;

    } else {
      /* 3 for could not examine */
      result = 3;
    }

  } else {
    if ( !testflag(flags,FFLAG) ) {
      emit(commandname);
      emit(": Out of memory!\n");
    }
    UnLock(lock);
    return(-1);
  }

  UnLock(lock);

  /* The following assumes that the given filename is the same length
   * as the name returned in the FileInfoBlock. This may break if
   * links are implemented sometime.
   */
  if ( testflag(flags,NFLAG) && newname == CAPS ) {
    /* Make sure the capitalization on files only is the same as it is
     * on disk. Directories have the problem that they can be referred
     * to by multiple names, so we can't assume they name Examine()
     * returns is the same length as the name given on the command line.
     */

    if ( !result ) {
      strcpy(basename(path),(*fib)->fib_FileName);
    }
  }

  return(result);
}

void closethings()
{
  Close((BPTR)ofile);

#ifdef ARP
  if ( ArpBase ) {
    CloseLibrary(ArpBase);
  }

#if 0
  if ( IntuitionBase ) {
    CloseLibrary(IntuitionBase);
  }

  if ( GfxBase ) {
    CloseLibrary(GfxBase);
  }
#endif
#endif
}

void usage(flags)
long flags;
{
  if ( !testflag(flags,FFLAG) ) {
    if ( testflag(flags,MVFLAG) ) {
      emit("Usage: mv [-cfiInpsuUvVx]    [-bMaxBufferSize] [-] file1 file2\n");
      emit("       mv [-cfiInpsuUvVx]    [-bMaxBufferSize] [-] path1 [path2 ...] dir\n");
    } else if ( testflag(flags,RMFLAG) ) {
      emit("Usage: rm [-cdfimrRsvV]      [-bMaxBufferSize] [-] path1 [path2 ...]\n");
    } else {
      emit("Usage: cp [-fiImnpsuUvVx]    [-bMaxBufferSize] [-] file1 file2\n");
      emit("       cp [-fiImnpsuUvVxrR]  [-bMaxBufferSize] [-] path1 [path2 ...] dir\n");
    }
  }

  closethings();

  exit(1);
}

/* the following is a mess. brace yourself. */
#ifdef LATTICE
void            /* the lattice header files declare main incorrectly! */
#endif
main(argc,argv)
int argc;
char **argv;
{
  register int index, c;
  struct FileInfoBlock *startfib = NULL, *endfib = NULL;
  filenode *start = NULL, *temp = NULL, *endnode = NULL;
  int args, result, nomem;
  long flags = INITFLAGS;

  ofile = (long) Open("*",MODE_NEWFILE); /* open new file for stderr */
  ifile = Input();

# ifdef ARP
  ArpBase = (struct ArpBase *) OpenLibrary(ArpName,0L);

  if ( ArpBase ) {
#   ifdef DEBUG
    printf("opened arp.library okay\n");
#   endif

    IntuitionBase = (struct IntuitionBase *) ArpBase->IntuiBase;
    GfxBase = (struct GfxBase *) ArpBase->GfxBase;

#if 0
    if ( !(IntuitionBase = (struct IntuitionBase *)
           OpenLibrary("intuition.library",0L)) ) {
      emit("Could not open intuition.library\n");
      closethings();
      exit(-1);
    }

    if ( !(GfxBase = (struct GfxBase *)
           OpenLibrary("graphics.library",0L)) ) {
      emit("Could not open graphics.library\n");
      closethings();
      exit(-1);
    }
#endif

    setflag(flags,ARPFLAG);
  } else {
#   ifdef DEBUG
    printf("Arp.library not opened.\n");
#   endif
    clearflag(flags,ARPFLAG);
  }
# endif

  if ( !stricmp(basename(argv[0]),MVNAME) ) {

    setflag(flags,MVFLAG|RFLAG);

    clearflag(flags,CPFLAG|RMFLAG);

#   ifdef DEBUG
    printf("mv found as executable's name: %s, %s\n",
           basename(argv[0]),MVNAME);
#   endif
  } else if ( !stricmp(basename(argv[0]),RMNAME) ) {

    setflag(flags,RMFLAG);

    clearflag(flags,MVFLAG|CPFLAG);

#   ifdef DEBUG
    printf("rm found as executable's name: %s, %s\n",
           basename(argv[0]),RMNAME);
#   endif
  } else {

    /*
     * default to the copy command so that if the user renames the
     * executable to something we don't understand, we don't do
     * anything really destructive.
     */

    setflag(flags,CPFLAG);

    clearflag(flags,RMFLAG|MVFLAG);
#   ifdef DEBUG
    printf("cp assumed to be executable's name: %s, %s\n",
           basename(argv[0]),CPNAME);
#   endif
  }

  index = 1;

  /* simplistic argument processing */

  while ( argv[index][0] == '-' ) {
    c = 1;

    /* - option was specified to end other options */
    if ( !argv[index][c] ) {
      ++index;
      break;
    }

    while ( argv[index][c] ) {
      switch ( argv[index][c] ) {
        case 'b':
          /* if the next argument exists, then use it as the new buffer
           * size. If it does not exist, an if condition below will
           * barf anyways, so do nothing here.
           */
          c++;

          while ( argv[index][c] && isdigit(argv[index][c]) ) {
            buffersize = (buffersize << 3) + (buffersize << 1)
                         + (long) argv[index][c] - (long) '0';
            c++;
          }
          c--;

          buffersize <<= 10;  /* x1024 to translate to kilobytes */

#         ifdef DEBUG
          printf("maximum buffer size: %d\n",buffersize);
#         endif
          /* if buffersize is 0 or negative, then something is wrong,
           * and the program should not do anything different from
           * what it was going to do anyways.
           */
          if ( buffersize > 0 ) {
            setflag(flags,BFLAG);
          }
          break;

        case 'c':
          setflag(flags,CPFLAG);

          clearflag(flags,RFLAG|MVFLAG|RMFLAG);
          break;

        case 'd':
          setflag(flags,DFLAG);
          break;

        case 'f':
          setflag(flags,FFLAG);
          clearflag(flags,iFLAG|IFLAG|VFLAG);
          break;

        case 'i':
          setflag(flags,iFLAG);
          clearflag(flags,FFLAG|VFLAG|IFLAG);
          break;

        case 'I':
          setflag(flags,IFLAG);
          clearflag(flags,FFLAG|VFLAG|iFLAG);
          break;

        case 'm':
          setflag(flags,MVFLAG|RFLAG);
          clearflag(flags,CPFLAG|RMFLAG);
          break;

        case 'n':
          clearflag(flags,NFLAG);
          break;

        case 'p':
          setflag(flags,NFLAG);
          break;

        case 'R':
        case 'r':
          setflag(flags,RFLAG);
          break;

        case 's':
          clearflag(flags,VFLAG);
          break;

        case 'u':
          setflag(flags,uFLAG);
          clearflag(flags,UFLAG);
          break;

        case 'U':
          setflag(flags,UFLAG);
          clearflag(flags,uFLAG);
          break;

        case 'v':
          setflag(flags,VFLAG);
          break;

        case 'V':
          emit(version);
          break;

        case 'x':
          setflag(flags,RMFLAG);

          clearflag(flags,RFLAG|MVFLAG|CPFLAG);
          break;

        default:
          emit("invalid option ");
          Write(ofile,&argv[index][c],1);
          emit("\n");
          usage(flags);
      }
      c++;
    }
    ++index;
  }

  /* if there are no file names left after the options were processed ... */
  if ( (argc - index) < (testflag(flags,RMFLAG) ? 1 : 2) ) {
    usage(flags);
  }

  if ( testflag(flags,MVFLAG) ) {

    strcat(commandname,MVNAME);
#   ifdef DEBUG
    printf("mv command executing\n");
#   endif

  } else if ( testflag(flags,RMFLAG) ) {

    strcat(commandname,RMNAME);
#   ifdef DEBUG
    printf("rm command executing\n");
#   endif

  } else {

    strcat(commandname,CPNAME);
#   ifdef DEBUG
    printf("cp command executing\n");
#   endif

  }

  /*
   * expand the first argument. index contains the number of the first
   * file name argument at this point, because it is updated by the options
   * parsing piece of code above.
   */
  start = expand(argv[index++],&nomem,flags);

  if ( nomem ) {
    if ( !testflag(flags,FFLAG) ) {
      emit(commandname);
      emit(": Out of memory!\n");
    }
    closethings();
    exit(-1);
  }

  temp = end(start);

  for ( ;index < argc; index++) {

    /*
     * it is possible that previous arguments had a wildcard and didn't
     * match anything, so temp would be NULL at this point, otherwise
     * attach the new file list from expand onto the end of the list.
     */
    if ( temp ) {
      temp->next = expand(argv[index],&nomem,flags);
    } else {
      temp = expand(argv[index],&nomem,flags);
      start = temp;
    }

    if ( nomem ) {
     if ( !testflag(flags,FFLAG) ) {
        emit(commandname);
        emit(": Out of memory!\n");
      }
      closethings();
      freefilenodes(start);
      exit(-1);
    }

    temp = end(temp);
  }

  endnode = end(start);
  args = arglength(start);

# ifdef DEBUG
  printf("argc: %d\n",argc);

  for ( temp = start; temp; temp = temp->next ) {
    printf("argument \"%s\"\n", temp->name );
  }
# endif

  if ( !testflag(flags,RMFLAG) ) {

    /*
     * main case statement for the program to find out what to do with
     * its life. (You gotta fight, for your right,
     * to PPPPPAAAAAAARRRRRRRRIIIIIIIITTTTTTTYYYYYY!!!!!!
     */

    /*
     * check last argument (ie. the destination file) to make sure it
     * is a directory
     */
    switch ( isdir(endnode->name,&endfib,NOCAPS,flags) ) {

      /*
       * destination is a file, check that there are only 2 arguments
       * and that the first one is also a file, or inform the user
       * of his (or her) silliness.
       */
      case 0:
        if ( args == 2 ) {
          switch ( isdir(start->name,&startfib,CAPS,flags) ) {
            case 1:
              if ( !testflag(flags,FFLAG) ) {
                emit(commandname);
                emit(": cannot move a directory onto a file\n");
              }
              break;

            case 2:
            case 3:
              if ( !testflag(flags,FFLAG) ) {
                emit(commandname);
                emit(": could not access file ");
                emit(start->name);
                emit("\n");
              }
              break;

            case 0:
              result = mv2f(start->name,startfib,endnode->name,
                            endfib,flags,INCREMENT);

              freefib(startfib);
              freefib(endfib);
              freefilenodes(start);
              closethings();
              exit(0);
          }

          freefib(startfib);
          freefib(endfib);
          freefilenodes(start);
          closethings();
          exit(3);

        } else {
          if ( !testflag(flags,FFLAG) ) {
            emit(commandname);
            emit(": cannot move multiple files to a file\n");
          }
          freefib(endfib);
          freefilenodes(start);
          closethings();
          exit(3);
        }
        break;

      /* destination is a directory */
      case 1:
        freefib(endfib);

        if ( args == 2 ) {
          switch ( isdir(start->name,&startfib,CAPS,flags) ) {
            case 1:
              result = mvdir(start->name,startfib,endnode->name,flags,
                             INCREMENT,NOCREATE);
              break;

            case 2:
            case 3:
              if ( !testflag(flags,FFLAG) ) {
                emit(commandname);
                emit(": could not access file ");
                emit(start->name);
                emit("\n");
              }
              break;

            case 0:
              result = mv2d(start->name,startfib,endnode->name,flags,
                            INCREMENT);
              break;
          }
          freefib(startfib);
          freefilenodes(start);
          closethings();
          exit(0);
        }

        break;

      /* destination doesn't exist */
      case 2:
        if ( args == 2 && !isdir(start->name,&startfib,CAPS,flags) ) {

          /*
           * move file specified in start->name to a new
           * file in endnode->name
           */
          result = mv2f(start->name,startfib,endnode->name,NULL,
                        flags,INCREMENT);

          freefib(endfib);
          freefib(startfib);
          freefilenodes(start);
          closethings();
          exit(0);
        } else {
          if ( args > 2 ) {
            BPTR lock;
            char *buf = newbuf(flags);

            freefib(startfib);
            freefib(endfib);

            if ( !buf ) {
              freefilenodes(start);
              closethings();
              exit(-1);
            }

            if ( isinteract(flags) ) {
              buf[0] = '\0';
              emit(commandname);
              emit(": create directory ");
              emit(endnode->name);

              Read(ifile,buf,BUFSIZE);

              if ( buf[0] != 'y' && buf[0] != 'Y' ) {
                break;
              }
            }

            if ( testflag(flags,VFLAG) ) {
              ind(INCREMENT);
              emit(endnode->name);
              emit(" [dir]...");
            }

            if ( !(lock = CreateDir(endnode->name)) ) {
              if ( testflag(flags,VFLAG) ) {
                emit("could not create\n");
              } else if ( !testflag(flags,FFLAG) ) {
                emit(commandname);
                emit(": unable to create directory ");
                emit(endnode->name);
                emit("\n");
              }
              freefilenodes(start);
              freebuf(buf);
              closethings();
              exit(-1);
            } else {
              if ( testflag(flags,VFLAG) ) {
                emit("created\n");
              }
              UnLock(lock);
            }
            freebuf(buf);
          }
        }
        break;

      case 3:
        if ( !testflag(flags,FFLAG) ) {
          emit(commandname);
          emit(": could not examine file ");
          emit(endnode->name);
          emit("\n");
        }

        freefilenodes(start);
        freefib(endfib);
        closethings();
        exit(3);
        break;
    }

# ifdef DEBUG
  printf("Move or copy files to directory %s\n",endnode->name);
# endif
  }

  /*
   * For each source argument, move it to the correct directory.
   */
  for ( temp = start; temp; temp = temp->next ) {

    breakcheck(flags);

    if ( nobreak ) {
      if ( (temp != endnode) && testflag(flags,MVFLAG|CPFLAG) ) {
        result = mv(temp->name,endnode->name,flags,INCREMENT);

        if ( result == -1 ) {
          /* if there was an error, get out now */
            closethings();
            freefilenodes(start);
            exit(-1);
        }
      } else if ( testflag(flags,RMFLAG) ) {
        rm(temp->name,flags,INCREMENT);
      }
    }
  }

  closethings();

  freefilenodes(start);

  exit(0);
}

int rm_file(file,flags,indent)
char *file;
long flags;
int indent;
{
  register int result;
  char *buf = newbuf(flags);

  if ( !buf ) {
    return(-1);
  }

# ifdef DEBUG
  printf("Deleting file %s\n",file);
# endif

  if ( testflag(flags,VFLAG) && !testflag(flags,FFLAG) ) {
    ind(indent);
    emit(file);
    emit("...");
  }

  switch ( isdeletable(file,flags) ) {
    case 2:
      if ( !testflag(flags,VFLAG) && !testflag(flags,FFLAG) ) {
        emit(commandname);
        emit(": could not find file ");
        emit(file);
        emit("\n");
      }
      result = -1;
      break;

    case 1:
      if ( isinteract(flags) ) {
        buf[0] = '\0';

        if ( testflag(flags,VFLAG) && !testflag(flags,FFLAG) ) {
          emit("\n");
        }
        emit(commandname);
        emit(": remove ");
        emit(file);
        emit("? ");

        Read(ifile,buf,BUFSIZE);

        if ( buf[0] != 'y' && buf[0] != 'Y' ) {
          result = -1;
          break;
        }
      }

      SetProtection(file,0);

      if ( !DeleteFile(file) ) {
        if ( !testflag(flags,FFLAG) && !testflag(flags,VFLAG) ) {
          emit(commandname);
          emit(": could not remove ");
          emit(file);
          emit("\n");
        }
        result = -1;
      } else {
        result = 1;
      }
      break;

    case 0:
      if ( !testflag(flags,FFLAG) ) {
        buf[0] = '\0';

        if ( testflag(flags,VFLAG) ) {
          emit("\n");
        }
        emit(commandname);
        emit(": overide delete protection for file ");
        emit(file);
        emit("? ");

        Read(ifile,buf,BUFSIZE);

        if ( buf[0] == 'y' || buf[0] == 'Y' ) {

          SetProtection(file,0);

          if ( !DeleteFile(file) ) {
            if ( !testflag(flags,FFLAG) && !testflag(flags,VFLAG) ) {
              emit(commandname);
              emit(": could not remove ");
              emit(file);
              emit("\n");
            }
            result = -1;
          } else {
            result = 1;
          }
        } else {
          result = -1;
        }
      } else {

        SetProtection(file,0);

        if ( !DeleteFile(file) ) {
          if ( !testflag(flags,FFLAG) && !testflag(flags,VFLAG) ) {
            emit(commandname);
            emit(": could not remove ");
            emit(file);
            emit("\n");
          }
          result = -1;
        } else {
          result = 1;
        }
      }
      break;
  }

  if ( testflag(flags,VFLAG) && !testflag(flags,FFLAG) ) {
    if ( result != 1 ) {
      emit("not ");
    }
    emit("removed\n");
  }

  freebuf(buf);
  return(result);
}


/* recursively remove a directory or a remove a file */
int rm_dir(name,flags,indent)
char *name;
long flags;
int indent;
{
  register BPTR lock;
  register struct FileInfoBlock *fib;
  register char *buf, *prev;
  register int result = 1;
  register long tempflag = flags;

  clearflag(tempflag,VFLAG);

# ifdef DEBUG
  printf("Recursively deleting directory %s\n",name);
# endif

  buf = newbuf(flags);
  prev = newbuf(flags);
  fib = newfib(flags);

  if ( !prev || !buf || !fib ) {
    return(-1);
  }

  if ( testflag(flags,VFLAG) ) {
    ind(indent);
    emit(name);
    emit(" [dir]\n");
  }

  if ( lock = Lock(name, ACCESS_READ) ) {

#if 0
    cwd = CurrentDir(lock);
#endif

    if (Examine(lock, fib)) {
      int wasdir = 0;

      buf[0] = '\0';
      breakcheck(flags);

      while (nobreak && result && ExNext(lock, fib)) {
        strcpy(prev,name);
        pathcat(prev,fib->fib_FileName);

        if (buf[0]) {
          rm_file(buf, wasdir ? tempflag : flags,indent+INCREMENT);
        }

        if ( fib->fib_DirEntryType > 0 ) {
          result = rm_dir(prev,flags,indent+INCREMENT);
          wasdir = 1;
        } else {
          wasdir = 0;
        }

        strcpy(buf, prev);

        breakcheck(flags);
      }

      if ( nobreak && buf[0] ) {
        rm_file(buf, wasdir ? tempflag : flags,indent+INCREMENT);
      }
    }

#if 0
    UnLock(CurrentDir(cwd));
#endif

    UnLock(lock);

  } else {
    if ( !testflag(flags,FFLAG) ) {
      emit(commandname);
      emit(": could not get a lock on ");
      emit(name);
      emit("\n");
    }
    result = -1;
  }

  freefib(fib);
  freebuf(buf);
  freebuf(prev);

  return(result);
}

rm(name,flags,indent)
char *name;
long flags;
int indent;
{
  int result;
  struct FileInfoBlock *fib;

  switch ( isdir(name,&fib,NOCAPS,flags) ) {
    case 0:
      result = rm_file(name,flags,indent);
      break;

    case 1:
      if ( testflag(flags,RFLAG) ) {
        /* recursively delete the directory */
        result = rm_dir(name,flags,indent);
        if ( nobreak && result ) {
          rm_file(name,clearflag(flags,VFLAG),indent);
        }
      } else if ( testflag(flags,DFLAG) ) {

        /* only deletes directory if it is empty, as in AmigaDOS Delete */
        result = rm_file(name,flags,indent);

      } else {
        if ( !testflag(flags,FFLAG) ) {
          emit(commandname);
          emit(": ");
          emit(name);
          emit(" is a directory (not removed)\n");
        }
        result = -1;
      }
      break;

    case 2:
      if ( !testflag(flags,FFLAG) ) {
        emit(commandname);
        emit(": could not access file ");
        emit(name);
        emit("\n");
      }
      break;


    case 3:
      if ( !testflag(flags,FFLAG) ) {
        emit(commandname);
        emit(": could not examine file ");
        emit(name);
        emit("\n");
      }
      break;
  }

  if ( fib )
    freefib(fib);

  return(result);
}


/* mv a file or directory _to_a_directory_ */
int mv(src,dst,flags,indent)
char *src, *dst;
long flags;
int indent;
{
  register int result = 0;
  struct FileInfoBlock *srcfib;

  switch ( isdir(src,&srcfib,CAPS,flags) ) {

    /* source is a file */
    case 0:
      result = mvfile(src,srcfib,dst,flags,indent);
      break;

    /* source is a directory */
    case 1:
      result = mvdir(src,srcfib,dst,flags,indent,CREATE);
      break;

    /* no access to source */
    case 2:
      if ( !testflag(flags,FFLAG) ) {
        emit(commandname);
        emit(": could not access file ");
        emit(src);
        emit("\n");
      }
      result = 0;
      break;

    /* not able to examine source */
    case 3:
      if ( !testflag(flags,FFLAG) ) {
        emit(commandname);
        emit(": could not examine file ");
        emit(src);
        emit("\n");
      }
      result = 0;
      break;
  }

  freefib(srcfib);
  return(result);
}

/* mv under a destination dir _from_ a directory source */
int mvdir(src,srcfib,dst,flags,indent,createnew)
char *src, *dst;
struct FileInfoBlock *srcfib;
long flags;
int indent, createnew;
{
  register int result = 1, temp, onsame;
  struct FileInfoBlock *dstfib;
  register long tempflag = flags;

  clearflag(tempflag,IFLAG|iFLAG|VFLAG);
  setflag(tempflag,FFLAG);

  if ( (onsame = samedev(src,dst)) < 0 ) {
    if ( !testflag(flags,FFLAG) ) {
      emit(commandname);
      emit(": ");
      emit( onsame == -1 ? src : dst);
      emit(" not found\n");
    }
    return(0);
  }

  temp = isdir(dst,&dstfib,NOCAPS,flags);

  /* if they are on the same device, treat the src dir as a file */
  if ( onsame && testflag(flags,MVFLAG) ) {

    /* if the destination dir doesn't exist, then move onto it... */
    if ( temp == 2 || !createnew ) {

      result = mv2f(src,srcfib,dst,NULL,flags,indent);
    } else {

      /* ...else move into it */
      result = mv2d(src,srcfib,dst,flags,indent);
    }

  } else if ( testflag(flags,MVFLAG) ||
            ( testflag(flags,CPFLAG) && testflag(flags,RFLAG) ) ) {

    /* ugh. We have to copy the source dir to the destination dir */

    register int success, len;
    register char c;
    register BPTR newlock, dstlock;
    char *buf = newbuf(flags);
    char *dstbuf = newbuf(flags);
    register struct FileInfoBlock *fib = newfib(flags);

    if ( !fib || !buf || !dstbuf ) {
      result = -1;
    } else {
      dstbuf[0] = '\0';

      strcat(dstbuf,dst);

      if ( temp != 2 && createnew ) {
        /*
         * build the name of the new destination from the directory name
         * and the file name.
         */
        pathcat(dstbuf,basename(src));
      }

      if ( testflag(flags,VFLAG) ) {
        ind(indent);
        emit(dstbuf);
        emit(" [dir]");
      }

      /* see if the directory exists */
      if ( !(dstlock = Lock(dstbuf,ACCESS_READ)) ) {

        /* if no lock, try creating it */

        if ( isinteract(flags) ) {
          buf[0] = '\0';

          emit(commandname);
          emit(": create directory ");
          emit(dstbuf);
          emit("? ");

          Read(ifile,buf,BUFSIZE);

          if ( buf[0] != 'y' && buf[0] != 'Y' ) {
            freefib(fib);
            freefib(dstfib);

            return(0);
          }
        }

        if ( !(dstlock = CreateDir(dstbuf)) ) {
          if ( testflag(flags,VFLAG) ) {
            emit("...could not create\n");
          } else if ( !testflag(flags,FFLAG) ) {
            emit(commandname);
            emit(": unable to create directory ");
            emit(dstbuf);
            emit("\n");
          }
          return(0);
        } else {
          if ( testflag(flags,VFLAG) ) {
            emit("...created\n");
          }
        }
#       ifdef DEBUG
        printf("Created directory %s\n",dstbuf);
#       endif

        setdate(&srcfib->fib_Date,dstbuf,flags);

      } else {
        /* directory does exist */
        if ( testflag(flags,VFLAG) ) {
          emit("\n");
        }
      }

      UnLock(dstlock);

      /*
       * get a lock on the source directory, since we have to copy it
       * recursively
       */
      newlock = Lock(src,ACCESS_READ);

      /* Take a look at the directory */
      success = Examine(newlock,fib);

      /* to build the source file name... */
      buf[0] = '\0';

      breakcheck(flags);

      /* look at the first file in the directory */
      if ( nobreak && (success = ExNext(newlock,fib)) ) {

        strcat(buf,src);

        pathcat(buf, &fib->fib_FileName[0]);
      }

      while ( nobreak && result > 0 && success && ExNext(newlock,fib) ) {

        if ( buf[0] ) {
          result = mv(buf,dstbuf,flags,indent+INCREMENT);
        }

        /* build the source file name */
        buf[0] = '\0';

        strcat(buf,src);

        pathcat(buf,&fib->fib_FileName[0]);

        breakcheck(flags);
      }

      if ( nobreak && result > 0 && buf[0] ) {
        result = mv(buf,dstbuf,flags,indent+INCREMENT);
      }

      UnLock(newlock);

      breakcheck(flags);

      /* everything in the directory is done, now remove the directory
       * if neccessary.
       */
      if ( nobreak && result > 0 && testflag(flags,MVFLAG) ) {
        rm_file(src,tempflag,INCREMENT);
      }

    }

    freefib(fib);
    freebuf(buf);
    freebuf(dstbuf);

  } else {
    if ( !testflag(flags,FFLAG) ) {
      emit(commandname);
      emit(": ");
      emit(src);
      emit(" is a directory (not copied)\n");
    }
  }

  freefib(dstfib);

  return(result);
}

/* mv to a destination file or directory _from_ a file */
int mvfile(src,srcfib,dst,flags,indent)
char *src, *dst;
struct FileInfoBlock *srcfib;
long flags;
int indent;
{
  register int result = 0;
  struct FileInfoBlock *dstfib;
  char *buf = newbuf(flags);
  BPTR lock;

  if ( !buf ) {
    return(-1);
  }

  switch ( isdir(dst,&dstfib,NOCAPS,flags) ) {

    /* destination is a file */
    case 0:
      result = mv2f(src,srcfib,dst,dstfib,flags,indent);
      break;

    /* destination is a directory */
    case 1:
      result = mv2d(src,srcfib,dst,flags,indent);
      break;

    case 2:
      if ( isinteract(flags) ) {
        buf[0] = '\0';

        emit(commandname);
        emit(": create directory ");
        emit(dst);
        emit("? ");

        Read(ifile,buf,BUFSIZE);

        if ( buf[0] != 'y' && buf[0] != 'Y' ) {
          result = 0;
          break;
        }
      }
#     ifdef DEBUG
      printf("Creating dir %s\n",dst);
#     endif

      if ( testflag(flags,VFLAG) ) {
        ind(indent);
        emit(dst);
        emit(" [dir]...");
      }

      lock = CreateDir(dst);

      if ( !lock ) {
        if ( testflag(flags,VFLAG) ) {
          emit("could not create\n");
        } else if ( !testflag(flags,FFLAG) ) {
          emit(commandname);
          emit(": could not create destination directory ");
          emit(dst);
          emit("\n");
        }
        result = 0;
      } else {
        if ( testflag(flags,VFLAG) ) {
          emit("created\n");
        }
        UnLock(lock);
        result = mv2d(src,srcfib,dst,flags,indent);
      }
      break;

    case 3:
      if ( !testflag(flags,FFLAG) ) {
        emit(commandname);
        emit(": could not examine directory ");
        emit(dst);
        emit("\n");
      }
      result = 0;
      break;
  }

  freefib(dstfib);
  freebuf(buf);
  return(result);
}

/* mv a file _to_ a directory dst */
int mv2d(src,srcfib,dst,flags,indent)
char *src, *dst;
struct FileInfoBlock *srcfib;
long flags;
int indent;
{
  char *buf = newbuf(flags);
  register char c;
  register int result = 0;
  struct FileInfoBlock *dstfib;

  if ( !buf ) {
    return(-1);
  }

  buf[0] = '\0';

  /* build the file name in the destination directory */
  strcat(buf,dst);

  pathcat(buf,basename(src));

  switch ( isdir(buf,&dstfib,NOCAPS,flags) ) {

    case 0:
      result = mv2f(src,srcfib,buf,dstfib,flags,indent);
      break;

    case 1:
      if ( !testflag(flags,VFLAG) && !testflag(flags,FFLAG) ) {
        emit(commandname);
        emit(": could not move file ");
        emit(src);
        emit(" onto directory ");
        emit(buf);
        emit("\n");
      }
      result = 0;
      break;

    case 2:
      result = mv2f(src,srcfib,buf,NULL,flags,indent);
      break;

    case 3:
      if ( !testflag(flags,VFLAG) && !testflag(flags,FFLAG) ) {
        emit(commandname);
        emit(": something is awry with file ");
        emit(buf);
        emit("\n");
      }
      result = 0;
      break;
  }

  freefib(dstfib);
  freebuf(buf);
  return(result);
}

/* mv a file _to_ a file */
int mv2f(src,srcfib,dst,dstfib,flags,indent)
char *src, *dst;
struct FileInfoBlock *srcfib, *dstfib;
long flags;
int indent;
{
  register int result = 0;
  char *inputbuf;
  register int onsame;
  register long tempflag = flags;

  clearflag(tempflag,IFLAG|iFLAG|VFLAG);
  setflag(tempflag,FFLAG);

  if ( !(inputbuf = newbuf(flags)) ) {
    return(-1);
  }

  /* are they on the same volume? */
  if ( (onsame = samedev(src,dst)) < 0) {
    if ( !testflag(flags,FFLAG) ) {
      char *par = parent(dst);

      emit(commandname);
      emit(": ");
      emit( onsame == -1 ? src : par);
      emit(" not found\n");

      FreeMem(par,strlen(dst)+1);
    }
    freebuf(inputbuf);
    return(0);
  }

  if ( dstfib ) {
    if ( isupdate(flags) ) {
      if ( comparedates(&srcfib->fib_Date,&dstfib->fib_Date) >= 0 ) {
        if ( testflag(flags,VFLAG) ) {
          ind(indent);
          emit(src);
          emit("...not newer\n");
        }
        return(1);
      }
    }

    /* if the files are on the same volume and they have the same
     * first block number, then they must be the same file.
     */
    if ( onsame && srcfib->fib_DiskKey == dstfib->fib_DiskKey ) {

      if ( !testflag(flags,MVFLAG) ) {
        if ( !testflag(flags,FFLAG) ) {
          emit(commandname);
          emit(": cannot copy ");
          emit(src);
          emit(" to itself!\n");
        }
        freebuf(inputbuf);
        return(1);
      }

    } else {
      /* else get rid of the destination before copying to it */

      if ( isinteract(flags) ) {

        inputbuf[0] = '\0';

        emit(commandname);
        emit(": overwrite ");
        emit(dst);
        emit("? ");

        Read(ifile,inputbuf,BUFSIZE);

        if ( inputbuf[0] != 'y' && inputbuf[0] != 'Y' ) {
          freebuf(inputbuf);
          return(1);
        }

        /* if remove unsuccessful, return an error */
        if ( rm_file(dst,tempflag,INCREMENT) == -1 ) {
          freebuf(inputbuf);
          return(0);
        }

      } else {

        /* if remove unsuccessful, return an error */
        if ( rm_file(dst,tempflag,INCREMENT) == -1 ) {
          freebuf(inputbuf);
          return(0);
        }

      }
    }
  } else {

    if ( isupdate(flags) > 1 ) {
      if ( testflag(flags,VFLAG) ) {
        ind(indent);
        emit(dst);
        emit("...does not exist\n");
      }
      return(1);
    }

    if ( isinteract(flags) > 1 ) {
      inputbuf[0] = '\0';

      emit(commandname);
      emit(": create ");
      emit(dst);
      emit("? ");

      Read(ifile,inputbuf,BUFSIZE);

      if ( inputbuf[0] != 'y' && inputbuf[0] != 'Y' ) {
        freebuf(inputbuf);
        return(1);
      }
    }
  }

  if ( testflag(flags,VFLAG) ) {
    ind(indent);
    emit(src);
    emit("...");
  }

  result = (onsame && testflag(flags,MVFLAG)) ?
           move(src,dst,flags) : cp(src,srcfib,dst,flags);

  if ( testflag(flags,VFLAG) ) {
    if ( result < 1 ) {
      emit("not ");
    }

    if ( testflag(flags,MVFLAG) ) {
      emit("moved to ");
    } else {
      emit("copied to ");
    }

    emit(dst);
    emit("\n");
  }

  if ( nobreak ) {
    if ( result == 2 && testflag(flags,MVFLAG) ) {
      rm_file(src,tempflag,INCREMENT);
    }
  } else {
    rm_file(dst,tempflag,INCREMENT);
  }

  freebuf(inputbuf);

  return(result);
}

/* copy a source file to a destination file */
int cp(src,srcfib,dst,flags)
char *src, *dst;
struct FileInfoBlock *srcfib;
long flags;
{
  register int num;
  register long size;
  register char *buf;
  BPTR srchandle, dsthandle;

# ifdef DEBUG
  printf("copy file %s to %s\n",src,dst);
# endif

  if ( testflag(flags,BFLAG) ) {
    size = buffersize;
  } else {
    size = mem() - 4;

    if ( size < 128 ) {
      if ( !testflag(flags,FFLAG) ) {
        emit(commandname);
        emit(": Out of memory!\n");
      }
      return(-1);
    }
  }

  /*
   * allocate a maximum of "size" continous bytes, else source file
   * size worth of bytes. This allows one read and one write if there
   * is enough memory for it. The 8 is added in case there is a
   * zero size file we want to copy and we still want to allocate
   * a buffer anyways.
   */
  size = ( srcfib->fib_Size + 8 > size ) ? size : srcfib->fib_Size + 8;

# ifdef DEBUG
  printf("Allocating %d bytes for the copy\n",size);
# endif

  if ( !(buf = AllocMem(size,MEMF_CLEAR|MEMF_PUBLIC)) ) {
    /* didn't work, try again with a smaller size */
    size = size / 4;

    if ( !(buf = AllocMem(size,MEMF_CLEAR|MEMF_PUBLIC)) ) {
      if ( !testflag(flags,FFLAG) ) {
        emit(commandname);
        emit(": Out of memory!\n");
      }
      return(-1);
    }
  }

  /* if we can't open the file, return an error */
  if ( !(dsthandle = Open(dst,MODE_NEWFILE)) ) {
    if ( !testflag(flags,VFLAG) && !testflag(flags,FFLAG) ) {
      emit(commandname);
      emit(": cannot Open file ");
      emit(dst);
      emit("\n");
    }
    FreeMem(buf,size);

    /* returning 0 indicates an error to the above routines */
    return(0);
  }

  if ( !(srchandle = Open(src,MODE_OLDFILE)) ) {
    if ( !testflag(flags,VFLAG) && !testflag(flags,FFLAG) ) {
      emit(commandname);
      emit(": cannot Open file ");
      emit(src);
      emit("\n");
    }
    Close(dsthandle);
    FreeMem(buf,size);
    return(0);
  }

  /* copy bytes back and forth. Read returns -1 for a read error */
  while ( nobreak && (num = Read(srchandle,buf,size)) > 0 ) {
    if ( Write(dsthandle,buf,num) == -1 ) {
      if ( !testflag(flags,VFLAG) && !testflag(flags,FFLAG) ) {
        emit(commandname);
        emit(": error writing to file ");
        emit(dst);
        emit(". Disk may be full.\n");
      }
      Close(srchandle);
      Close(dsthandle);
      FreeMem(buf,size);
      return(-1);
    }
    breakcheck(flags);
  }

  Close(srchandle);
  Close(dsthandle);

  if ( !nobreak ) {
    FreeMem(buf,size);
    return(0);
  }

  if ( num == -1 ) {
    if ( !testflag(flags,VFLAG) && !testflag(flags,FFLAG) ) {
      emit(commandname);
      emit(": error reading from file ");
      emit(src);
      emit("\n");
    }
    FreeMem(buf,size);
    return(0);
  }

  if ( testflag(flags,NFLAG) ) {
    /*
     * copy the protection bits, the comment and the datestamp to the
     * new file if the n flag is set (ie. -n was NOT specified in the
     * commandline)
     */
    SetProtection(dst,srcfib->fib_Protection);

    SetComment(dst,srcfib->fib_Comment);

    setdate(&srcfib->fib_Date,dst,flags);
  }

  FreeMem(buf,size);
  /* return 2 for successful copy (not 1, because 1 is a move) */
  return(2);
}

/* rename a source file to a destination file */
int move(src,dst,flags)
char *src, *dst;
long flags;
{
  register int foo;

# ifdef DEBUG
  printf("move file %s to %s\n",src,dst);
# endif

  /* return 1 for successful move */
  foo = Rename(src,dst) ? 1 : 0;

  if ( !foo && !testflag(flags,VFLAG|FFLAG) ) {
    emit(commandname);
    emit(": could not move file ");
    emit(src);
    emit(" to ");
    emit(dst);
    emit("\n");
  }

  return(foo);
}

int setdate(date,name,flags)
struct DateStamp *date;
char *name;
long flags;
{
  register UBYTE *ptr;
  struct MsgPort *task;
  register BPTR lock, parent;
  register struct FileInfoBlock *fib;

  if ( !(task = DeviceProc(name)) ) {
    return(0);
  }

  if ( !(lock = Lock(name,ACCESS_READ)) ) {
    return(0);
  }

  fib = newfib(flags);

  if ( fib ) {
    if ( Examine(lock,fib) ) {

      parent = ParentDir(lock);
      UnLock(lock);

      ptr = (UBYTE *) AllocMem(256L,MEMF_CLEAR|MEMF_PUBLIC);

      strcpy((ptr + 1),fib->fib_FileName);

      *ptr = (UBYTE) strlen(fib->fib_FileName);

      send_date(task,parent,(ULONG)&ptr[0] >> 2L,date);

      FreeMem(ptr,256L);

      UnLock(parent);
    }

    freefib(fib);

  } else {
    UnLock(lock);
  }
}

/* is a file delete protected? */
int isdeletable(file,flags)
char *file;
long flags;
{
  register BPTR lock;
  register struct FileInfoBlock *fib;
  register int result = 0;

  if ( !(lock = Lock(file,ACCESS_READ)) ) {
    return(0);
  }

  /* allocate a word aligned memory block to hold our info */
  fib = newfib(flags);

  if ( fib ) {
    if ( Examine(lock,fib) ) {

      result = fib->fib_Protection;
      freefib(fib);

    } else {

      freefib(fib);
      return(2);
    }

  }

  UnLock(lock);

  return( !(result & FIBF_DELETE) );
}

/* compare 2 date stamps and return -1 if ds1>ds2, 0 if equal,
 * and 1 if ds1<ds2 */
int comparedates(ds1,ds2)
struct DateStamp *ds1, *ds2;
{
  register int foo;

  foo = 0;

  foo = (ds1->ds_Days > ds2->ds_Days) ? -1 :
        (ds1->ds_Days < ds2->ds_Days) ?  1 : 0;

  if ( foo ) {
    return ( foo );
  }

  foo = (ds1->ds_Minute > ds2->ds_Minute) ? -1 :
        (ds1->ds_Minute < ds2->ds_Minute) ?  1 : 0;

  if ( foo ) {
    return ( foo );
  }

  foo = (ds1->ds_Tick > ds2->ds_Tick) ? -1 :
        (ds1->ds_Tick < ds2->ds_Tick) ?  1 : 0;

  return ( foo );
}

long send_date(task,object,name,date)
struct MsgPort *task;
BPTR object, name;
struct DateStamp *date;
{
  struct MsgPort *replyport;
  struct StandardPacket *packet;
  extern struct MsgPort *CreatePort();
  long result;

# ifdef DEBUG
  printf("dos packet routine started\n");
# endif

  replyport = CreatePort(NULL,0);
  if ( !replyport ) {
    return(0);
  }

  packet = (struct StandardPacket *)
           AllocMem(sizeof(struct StandardPacket),MEMF_PUBLIC|MEMF_CLEAR);

  if ( !packet ) {
    DeletePort(replyport);
    return(0);
  }

  /* build a set date packet to send to dos */

  packet->sp_Msg.mn_Node.ln_Name = (char *) &packet->sp_Pkt;
  packet->sp_Pkt.dp_Link = &packet->sp_Msg;
  packet->sp_Pkt.dp_Port = replyport;

  packet->sp_Pkt.dp_Type = ACTION_SET_DATE;

  packet->sp_Pkt.dp_Arg1 = (long) NULL;
  packet->sp_Pkt.dp_Arg2 = (long) object; /* parent of object to set date */
  packet->sp_Pkt.dp_Arg3 = (long) name;   /* BCPL string of name of file  */
  packet->sp_Pkt.dp_Arg4 = (long) date;   /* APTR to a DateStamp structure */

  PutMsg(task,(struct Message *)packet);

  WaitPort(replyport);
  GetMsg(replyport);

  result = packet->sp_Pkt.dp_Res2;        /* error code returned here if
                                           * there is one
                                           */

# ifdef DEBUG
  printf("dos packet routine returned: %d\n",result);
# endif

  DeletePort(replyport);
  FreeMem((char *)packet,sizeof(struct StandardPacket));

  return(result);
}




