[comp.sys.amiga] Cp: A replacementfor AmigaDos Copy

jlydiatt@van-bc.UUCP (Jeff Lydiatt) (05/23/87)

Here is a replacement for AmigaDog "copy" program that I have
been working on for quite a while.  It implements the AmigaDos
pattern matching algorithm, the "all" command, and has the
added bonus that it retains the date of copied file.

It's my first posting to the net, and I have my fingers crossed
that all goes well...

Contents:
cp.doc     - documentation
cp.c       - the main program
PatMatch.c - supporting routines to implement the pattern matching.
setDate.c  - gets and sets the file date.
wb_parse.c - Aztec allows you to skip the workbench parms parse.
makefile   - to compile and link the whole thing.
cp.uue     - uuencoded cp

============
cat <cp.doc>
============
      Cp - a replacement for AmigaDos Copy that retains the date.

		by Jeff Lydiatt
		Vancouver, Canada
		Release 1.0, May 17, 1987

      Cp and the c source is freely redistributable for personal
      non-commercial use.  Commercial rights are reserved by the
      author.  Feel free to make any modifications or use any 
      of the modules in other programs.  I encourage you to do so
      and hope you will release the code so others can learn by it.

Cp has most of the features of the AmigaDos copy command:
--------------------------------------------------------

   o Cp supports the AmigaDos style pattern matching in the "from" name.
   o Cp supports the All option.
   o Cp supports the optional "from" and "to" qualifiers for file names.
   o Cp will copy directories

Cp has a number of features not found in AmigaDos copy:
-------------------------------------------------------

   o Cp will retain the date of the copied file.
   o Cp uses a 32000 byte buffer, which speeds copies when the from and
     to file is on the same disk.
   o You may specify the current directory by a "to" name of ".".  For
     example if your current directory is "Df0:",
	    "cp ram:x ."
     will copy the file called x in your ram: disk to "df0:x".
   o Cp will also create the "to" file for you if you use the "All"
     option.  For example if "x" is a directory,
     	    "cp from x to y"
     will create a directory called "y", and will copy all the files in
     x to the newly created "y" directory.

About the AmigaDos-style pattern matching.
-----------------------------------------

   Cp uses a compact function for regular expression pattern matching.
The algorithm was taken from a paper by Martin Richards, that was
published in the September 1979 issue of "Software, Practice and
Experience".  Professor Richards published his example in BCPL, and
I have (sucessfully I think) translated it to C.  It's interesting to
note that I translated it verbatim, with no special modifications to
adapt it to AmigaDos conventions.

  Cp recognises a number of special characters with special meanings,
which can be used to recognise any other charcaters that match them. 

  ?	Matches any single character.
  %	Matches the null character.
  #<p>  Matches zero or more occurrences of the pattern <p>
  <p1>|<p2> Matches either pattern <p1> or <p2>.
  ()	Can be used to group expressions together.
  '#	Can be used to turn off the special meaning of the special
        characters #, ?, %, |, (, ), or '.  

  Some examples will help to make this clearer.

  cp a|b .	copies a or b to the current directory.
  cp a#bc .	copies ac abc abbc.
  cp a#(b|c)d .	copies ad abd abcd.
  cp a?b .	copies axb ayb aab.
  cp a#?b	copies ab axxb ax.ab.
  cp '?#?'# .	copies ?# ?ab# ??##.
  cp a(b|%)#c . copies a abc accc.

==========
cat <Cp.c>
==========
/* Cp:  A replacement for AmigaDos Copy that does not modify the date
 *	  by Jeff Lydiatt
 *	  Vancouver, Canada.
 *
 * Features:
 *   1) Handles all functions of AmigaDos copy, including the All switch.
 *   2) Regular expression pattern matching that handles the AmigaDos'
 *       pattern matching characters "#?|%'()".
 *   3) This copy keeps the date of the copied file!
 *   4) Uses a 32K buffer which should speed up single disk copies.
 *   5) In "Cp filex .", the "." is taken to mean the current directory.
 *
 * Created: 07May87
 *
 * Cp has been compiled and tested under Aztec v3.40a with patch v3
 * applied.  The 16 bit integer and small code, small data option is
 * used in compiling and linking all modules.
 *
 * Maintenance Notes:
 *  17May87 - Version 1.0 Released.
 *
 */

/* 
      Cp and the c source is freely redistributable for personal
      non-commercial use.  Commercial rights are reserved by the
      author.  Feel free to make any modifications or use any 
      of the modules in other programs.  I encourage you to do so
      and hope you will release the code so others can learn by it.
 */

#include <stdio.h>
#include <libraries/dos.h>
#include <exec/memory.h>
#include <functions.h>

#define BUFMAX 32768
#define MAXSTR 127
 
extern int Enable_Abort;
extern char *malloc();
extern int CmplPat(), Match();
extern long Chk_Abort();

/*--- A poor man's "printf" which avoids the overhead of printf ---*/

static void msg(str, parm)
char	*str, *parm;
{
   register char *s;
   register struct FileHandle *console;

   console = Output();
   for ( s=str; *s; ++s )
     {
       if ( *s == '%' && *(s+1) == 's' )
	 {
	    (void)Write( console, str, (long)(s - str));
	    (void)Write( console, parm, (long)strlen(parm));
	    (void)Write( console, (char *)(s + 2), (long)strlen((char *)(s+2)));
	    break;
	 }
     }

    if ( *s == '\0' )
       (void)Write( console, str, (long)strlen(str));
}
  
static void error( str, parm )
char *str, *parm;
{
   msg( str, parm );
   exit(NULL);
}

static void useage(name)
char *name;
{
    msg( "%s V1.0 buffered file copy by Jeff Lydiatt.  Usage:\n", name );
    msg( "  %s [From] filein [To] fileout [All]\n", name );
    msg( "  AmigaDos style pattern matching characters permitted in filein.\n" );
    msg( "  Pattern matching characters not allowed in Fileout.\n\n" );
}

/*-----------------------------------------------------------*/
/*		Parse the command string		     */
/*-----------------------------------------------------------*/

static int toupper(c)
register char c;
{
   if ( 'a' <= c && c <= 'z' )
	return c + ('A' - 'a');
   return c;
}
  
/* Case-blind implementation of strcmp() */

static int blindcmp( s1, s2 )
register char *s1, *s2;
{
     
   while (*s1 && *s2)
     {
	if ( (toupper( *s1 )) == (toupper( *s2 )) )
	   {
	      ++s1; 
	      ++s2;
	   }
        else
	   break;
     }

   if ( toupper(*s1) == toupper(*s2) )
      return 0; /* It's Equal */
   else if ( toupper(*s1) > toupper(*s2) )
      return 1;
   else
      return -1; 
}

static void parsecmd(fparm, tparm, All, argc, argv)
char *fparm, *tparm;
register char *argv[];
int *All, argc;
{
   int nextparm;
   register char *parm;

   if ((argc < 2) || ( strcmp( argv[1], "?" ) == 0) )
     {
       useage( argv[0] );
       exit( NULL );
     }

   *All = FALSE;
   nextparm = 1;
   argc--;
   fparm [0] = '\0';
   tparm[0] = '\0';
   while ( nextparm <= argc )
     {
	parm = argv[ nextparm ];
	if ( (nextparm < argc) && (blindcmp( parm, "from" ) == 0) )
	  {
	    ++nextparm;
	    (void)strncpy( fparm, argv [nextparm], MAXSTR);
	  }
	else if ( (nextparm < argc) && (blindcmp( parm, "to" ) == 0) )
	  {
	    ++nextparm;
	    (void)strncpy( tparm, argv [nextparm], MAXSTR );
	   }
	else if ( (nextparm == argc) && (blindcmp( parm, "all") == 0) )
	    *All = TRUE;
	else if ( fparm[0] == '\0' )
	   (void)strncpy( fparm, parm, MAXSTR );
	else
	   (void)strncpy( tparm, parm, MAXSTR );
	++nextparm;
     }
}

/*--------------------------------------------------------------*/
/*	Internal stack manipulation routines			*/
/*--------------------------------------------------------------*/

struct stackframe 
{
	struct stackframe *next;
	char *strptr;
};	
static struct stackframe *Stackhead;

static void initstack()
{
	Stackhead = NULL;
}

static int isempty()
{
   return Stackhead == NULL;
}

/*--------------push a string on the stack------------------*/

static void push(str)	char *str; /* Push a string on the stack */
{
   register struct stackframe *p;
   register char *s;

   if ( (p = (struct stackframe *)malloc(sizeof(struct stackframe))) == NULL
      ||(s = malloc( strlen(str)+1 )) == NULL)
      {
	msg("Not enough memory\n");
	return;
      }

   strcpy( s, str );
   p->next = Stackhead;
   p->strptr = s;
   Stackhead = p;
}

/*---------pop the next string from the stack------------------*/

static void pop(str) char *str;
{
   register struct stackframe *p;

   if (isempty())
	error("Can't happen: stack popped with nothing there\n");

   p = Stackhead;
   strcpy( str, p->strptr );
   free( p->strptr );
   Stackhead = p->next;
   free( p );
}

/*--------------------------------------------------------------*/
/*	Some handy path manipulation routines.			*/
/*--------------------------------------------------------------*/

/*------------separate path and pattern from str----------------*/

static char *getpath( path, pattern, str)
char *path, *pattern, *str;
{
   register char *s1, *s2;
   char *mark;

   mark = NULL;
   for ( s1=str; *s1; ++s1)
	if ( *s1 == ':' || *s1 == '/' )
	   mark = s1;

   if ( isdir(str) )
     {
	strcpy( path, str );
	*pattern = '\0';
     }
   else if ( !mark )
     {
	strcpy( pattern, str );
	*path = '\0';
     }
   else
     {
	s2 = path;
	s1 = str;
	while ( s1 < mark )
	   *s2++ = *s1++;
	if ( *s1 == ':' )
	   *s2++ = *s1;
	*s2 = '\0';
	strcpy( pattern, (char*)( mark+1));
     }

   return path;
}

/*------return path from parent directory and member name-------*/

static char *makepath(path, parent, member)
register char *path;
char *member;
register char *parent;
{
   register int len;

   strcpy( path, parent );
   len = strlen( path );  
   if ( len > 0 && *member )
      if ( (path[len-1] != ':') && (path[len-1] != '/') )
	  strcat( path, "/" );
   strcat( path, member );
   return path;
}

/*--- isdir: Returns 0 if NOT a directory, 1 otherwise. ---*/

static int isdir(filename)
char	*filename;
{
	struct FileInfoBlock *f;
	struct Lock *outlk;
	int		result;

	result = 0;
	outlk = (struct Lock *)Lock(filename,ACCESS_READ);
	if (outlk) 
	  {
	    f = (struct FileInfoBlock *)
		 AllocMem((long)sizeof(struct FileInfoBlock), MEMF_CHIP);
	    if (f == 0) 
		error("Unable to allocate space for FileInfoBlock!\n");
	    if (Examine(outlk,f))
	    if (f->fib_DirEntryType >= 0)
		result = 1;
	    UnLock(outlk);
	    FreeMem((void *)f,(long) sizeof(struct FileInfoBlock));
	  }
	return(result);
}

static int iswild(pattern)
register char *pattern;
{
   for (; *pattern; ++pattern )
     {
	switch( *pattern )
	 {
	   case '#':
	   case '?':
	   case '(':
	   case '%':
	   case '\'':
	   case '|': return 1;
	 }
     }
   return 0;
}

static int cp(fpath, tpath, name )
char *fpath, *tpath, *name;
{
   char fname[MAXSTR+1], tname[MAXSTR+1];
   char *buffer;
   long status, count, bufsize;
   struct FileHandle *fin, *fout;
   struct DateStamp date;

   (void)makepath( fname, fpath, name );

   if ( tpath[0] != '\0' && !isdir(tpath) )
      strcpy( tname, tpath );
   else
      (void)makepath( tname, tpath, name );

   msg( "%s ==> ", fname ); msg( "%s\n", tname);
   if ( (fin = Open( fname, MODE_OLDFILE )) == NULL )
     {
	msg( "Can't open %s for input.\n", fname );
	return 1;
     }
   if ( (fout = Open( tname, MODE_NEWFILE )) == NULL )
     {
	msg( "Can't open %s for ouput.\n", tname );
	Close( fin );
	return 1;
     }

   for ( bufsize = BUFMAX; bufsize > 2048; bufsize -= 2048 )
      if ( (buffer = malloc( (unsigned int)bufsize)) != NULL )
	break;

   if ( bufsize <= 2048 )
      {
	Close( fin );
	Close( fout );
	msg( "Not enough memory.\n" );
	return 1;
      }

    status = 1;
    while( status > 0 && (count = Read( fin, buffer, bufsize )) == bufsize )
      if ( Chk_Abort() )
	status = -1;
      else
	status = Write( fout, buffer, count );

    if (status > 0 && count > 0 )
	status = Write( fout, buffer, count );

    Close( fin );
    Close( fout);
    free( buffer );

    if ( status < 0 || count < 0 )
      {
	(void)DeleteFile( tname );
	msg( "   %s [removed].\n", tname );
	return 1;
      }

    if ( getDate( fname, &date ))
       (void)setDate( tname, &date );

    return 0;
}

main(argc,argv)
int	argc;
char	*argv[];
{
   int  aux[MAXSTR+1];
   char fparm[MAXSTR+1], tparm[MAXSTR+1];
   char fpath[MAXSTR+1], tpath[MAXSTR+1];
   char fparent[MAXSTR+1], dontSearch[MAXSTR+1];
   char fpattern[MAXSTR+1];
   char relpath[MAXSTR+1], junk[MAXSTR+1];
   void *dirlock;
   register struct FileInfoBlock *fileptr;
   int All;

   Enable_Abort = 0;

   parsecmd( fparm, tparm, &All, argc, argv );
   (void)getpath( fparent, fpattern, fparm );
   if ( strcmp(tparm, ".") == 0 )
      tparm[0] = '\0';

   /*--- Are the parms ok? --- */

   if ( iswild(fparent) )
      error( "Regular expression not allowed in path: %s.\n", fparm );
   if ( iswild(tparm) )
      error( "Regular expression not allowed as output name.\n" );

   /*----- if it's just a single file copy, just do it and exit----*/

   if ( !All && !iswild( fpattern ) && !isdir( fparm ))
     {
	(void)cp( fparent, tparm, fpattern );
        exit( NULL );
     }

   /*--- For "All" option, don't search the "to" directory --- */

   fileptr = (struct FileInfoBlock *)
	AllocMem( (long)sizeof(struct FileInfoBlock), MEMF_CHIP);

   strcpy( dontSearch, tparm );
   if ( dontSearch[0] == '\0' )
     {
	dirlock = Lock("", ACCESS_READ);
	if ( dirlock )
	   {
		(void)Examine( dirlock, fileptr );
		strcpy( dontSearch, fileptr->fib_FileName );
		UnLock( dirlock );
	   }   
     }

   initstack();
   push( "" );
   if ( fpattern[0] == '\0' )
      strcpy( fpattern, "#?" );
   if ( CmplPat( fpattern, aux ) == 0 )
     {
	msg("Bad regular expression pattern: %s.\n", fpattern);
	goto all_done;
     }

   while( !isempty() )
     {
	pop( relpath );
	(void)makepath( fpath, fparent, relpath );
	(void)makepath( tpath, tparm, relpath );

	if ( All && !isdir(tpath) )
	   {
		if ( (dirlock = CreateDir( tpath )) == NULL )
		   {
			msg( "Can't create %s \n", tpath );
			continue;
		   }
		else
		   {
			msg("%s ... [created]\n", tpath);
			UnLock( dirlock );
		   }
	   }

	if ( (dirlock = Lock( fpath, ACCESS_READ )) == NULL
	  || !Examine( dirlock, fileptr) )
	   {
		msg( "Can't find directory %s\n", fpath );
		if ( dirlock )
		   UnLock( dirlock );
		goto all_done;
	   }

	while ( ExNext( dirlock, fileptr ) )
	   {
		if ( fileptr->fib_DirEntryType > 0 )
		  {
		    if ( All && strcmp( dontSearch, fileptr->fib_FileName) )
		      push( makepath( junk, relpath, fileptr->fib_FileName ) );
		  }
		else if (Match( fpattern, aux, fileptr->fib_FileName ) )
		  {
		    if ( Chk_Abort() || cp( fpath, tpath, fileptr->fib_FileName ) )
		       {
			  UnLock( dirlock );
			  goto all_done;
		       }
		  }
	   }
	UnLock( dirlock );
     }
all_done:
   FreeMem( fileptr, (long)sizeof(struct FileInfoBlock));
   exit(NULL);
}
================
cat <PatMatch.c>
================
/* PatMatch.c - Implements AmigaDos Regular Expression Pattern Matching.
**
**  This program will test whether a string is an AmigaDos  regular expression
**  It may be used to implement wild expressions such as:
**
**    "copy #?.c to <dir>" to copy any file ending in .c
**
**  The program has two entry points: CmplPat, and Match.
**
**    CmplPat - takes a pattern and returns an auxilliary integer vector
**              which is used by Match.  The pattern is not modified in
**              any way.  CmplPat returns 1 if no errors were detected
**              while compiling the pattern; otherwise it returns 0;
**
**    Match   - takes the pattern, the auxilliary vector, and the string
**              to be matched.  It returns 1 if the string matches the
**              pattern; otherwise it returns 0;
**
**  Translated from BCPL by:
**              Jeff Lydiatt
**              Richmond B.C. Canada
**              16 May 1986.
**
**  Source: "A Compact Function for Regular Expression Pattern Matching",
**           Software - Practice and Experience, September 1979.
**
**  Useage:
**     To test if "file.c" matches the regular expression "#?.c"
**     char *Pat = "#?.c";
**     char *Str = "file.c";
**     WORD Aux[128];
**
**     if ( CmplPat( Pat, Aux ) == 0 )
**        {
**           printf("Bad Wildcard Expression\n");
**           exit(1);
**        }
**     if ( Match( Pat, Aux, Str ) == 1 )
**        printf("String matches the pattern\n");
**     else
**        printf("String does NOT match the pattern\n");
**/

/*--- Included files ----*/

#include <stdio.h>
#include <exec/types.h>
#include <ctype.h>

#define  EOS '\0'

/*--- Global Variables  ---*/

static char     Ch;      /* The current character in Pattern */
static char     *Pat;    /* Pointer to the Pattern */
static int      *Aux;    /* Pointer to returned auxilliary vector */
static int      PatP;    /* Current position in Pat */
static int      Patlen;  /* strlen(pat) */
static BOOL     Errflag; /* TRUE if error */
static int      *Work;   /* Pointer to Active work area */
static int      Wp;      /* Current position in work */
static BOOL     Succflag;/* True if "str" matches "pat" */

/*----------------------------------------------------------------*/
/*                   The Interpreter                              */
/*----------------------------------------------------------------*/

static void Put(N)
int N;
{
   register int *ip;
   register int *to;

   if ( N == 0 )
      Succflag = TRUE;
   else
      {
	for ( ip = &Work[ 1 ], to = &Work[ Wp ]; ip <= to; ip++)
	   if ( *ip == N )
	      return;
	Work[ ++Wp ] = N;
      }
}

int Match( Pat, Aux, Str )
char Pat[];
int  Aux[];
char Str[];
{
   int W[ 128 ];
   int  S = 0;
   int  I, N, Q, P, Strlength;
   char K;
   int  strlen();
   void Put();

   Work = W;
   Wp = 0;
   Succflag = FALSE;
   Strlength = strlen( Str );
   Put( 1 );

   if ( Aux[ 0 ] != 0 )
      Put( Aux[ 0 ] );

   for(;;)
      {
        /* First complete the closure */
        for( N=1; N <= Wp; N++ )
          {
	     P = Work[ N ];
	     K = Pat[ P-1 ];
	     Q = Aux[ P ];
	     switch( K )
	   	{
		  case '#': Put( P + 1 );
		  case '%': Put( Q );
		  default : break;
		  case '(':
		  case '|': Put( P + 1);
			    if ( Q != 0 )
			       Put( Q );
		}
	   }

	if ( S >= Strlength )
	   return Succflag;
	if ( Wp == 0 )
	   return FALSE;
	Ch = Str[ S++ ];

	/* Now deal with the match items */

	N = Wp;
	Wp = 0;
	Succflag = FALSE;

	for ( I = 1; I <= N; I++)
	  {
	     P = Work[ I ];
	     K = Pat[ P - 1 ];
	     switch( K )
	       {
		 case '#':
		 case '|':
		 case '%':
		 case '(': break;
		 case '\'': K = Pat[ P ];
		 default : if ( _toupper( Ch ) != _toupper( K ) )
			      break;
		 case '?': /* Successful match */
		 	   Put ( Aux[ P ] );
		} /* End Switch */
	  } /* End For I */
     } /* End for(;;) */
}


/*----------------------------------------------------------------*/
/*                     The Compiler                               */
/*----------------------------------------------------------------*/

static void  Rch() /* Read next character from Pat */
{
   if ( PatP >= Patlen )
      Ch = EOS;
   else
      Ch = Pat[ PatP++ ];
}

static void Nextitem() /* Get next char from Pat; recognize the ' escape char */
{
   if ( Ch == '\'' )
      Rch();
   Rch();
}

static void Setexits( List, Val )
int List;
int Val;
{
   int A;

   do
     {
	A = Aux[ List ];
	Aux[ List ] = Val;
	List = A;
     }
	while ( List != 0 );
}

static int Join( A, B )
int A, B;
{
    int T = A;

    if ( A == 0 )
	return B;
    while ( Aux[ A ] != 0 )
	A = Aux[ A ];
    Aux[ A ] = B;
    return T;
}

static int Prim()      /* Parse a Prim symbol */
{
   int   A = PatP;
   char Op = Ch;
   int  Exp();
   void Setexits(), Nextitem();

   Nextitem();
   switch( Op )
     {
        case EOS:
        case ')':
        case '|': Errflag = TRUE;
        default : return A;
        case '#': Setexits( Prim(), A ); return A;
        case '(': A = Exp( A );
		  if ( Ch != ')' )
		    {
			Errflag = TRUE;
		    }
		  Nextitem();
		  return A;
     }
}

static int Exp( AltP )    /* Parse an expression */
int AltP;
{
   int Exits = 0;
   int A;
   int Prim(), Exits(), Join();
   void Nextitem(), Setexits();

   for (;;)
	{
	   A = Prim();
	   if ( Ch == '|' || Ch == ')' || Ch == EOS )
	      {
		Exits = Join( Exits, A );
		if ( Ch != '|' )
		   return Exits;
		Aux[ AltP ] = PatP;
		AltP = PatP;
		Nextitem();
	      }
	   else
	      Setexits( A, PatP );
	}
}

int CmplPat( Pattern, CmplPattern)
char Pattern[];
int  CmplPattern[];
{
   int i, strlen();
   void Rch(), Setexits();

   Pat = Pattern;
   Aux = CmplPattern;
   PatP = 0;
   Patlen = strlen( Pat );
   Errflag = FALSE;

   for ( i = 0; i <= Patlen; i++ )
      Aux[ i ] = 0;
   Rch();
   Setexits( Exp(0), 0 );
   return (!Errflag);
}
===============
cat <setDate.c>
===============
#include "exec/types.h"
#include "exec/ports.h"
#include "exec/io.h"
#include "exec/memory.h"
#include "libraries/dos.h"
#include "libraries/dosextens.h"
#include <stdio.h>
#define AZTEC 1
#ifdef AZTEC
#include "functions.h"         /* aztec C include */
#endif

#define ACTION_SETDATE_MODE 34L  /* The packet type we will be playing with */
#define DOSTRUE 	    -1L  /* AmigaDos TRUE */
#define MAXARGS              7L  /* limit in packet structure (dosextens.h) */
#define NARGS		     4L  /* Number of args for setdate */

/*---------------------------------------------------------------------*/
/*  sendpkt: generalized send a dos packet.			       */
/*---------------------------------------------------------------------*/

static long sendpkt(pid,action,args,nargs)

struct MsgPort *pid;	/* process indentifier ... (handlers message port ) */
long  action,	       /* number of arguments in list */
      nargs;           /* number of arguments in list  */
ULONG args[];          /* a pointer to a argument list */
{
 
   struct MsgPort        *replyport;
   struct StandardPacket *packet;
 
   long   count, res1; 
   ULONG  *pargs;

   if(nargs > MAXARGS) return NULL; 
 
   replyport = (struct MsgPort *) CreatePort(NULL,NULL); /* make reply port */
   if(!replyport) return NULL;

   packet = (struct StandardPacket *) 
      AllocMem((long)sizeof(struct StandardPacket),MEMF_PUBLIC | MEMF_CLEAR);
   if(!packet) 
     {
       FreeMem((void *)packet,(long)sizeof(struct StandardPacket));
       return(NULL);
     }

   packet->sp_Msg.mn_Node.ln_Name = (char *) &(packet->sp_Pkt); /* link packet- */
   packet->sp_Pkt.dp_Link         = &(packet->sp_Msg);  /* to message    */
   packet->sp_Pkt.dp_Port         = replyport;         /* set-up reply port */
   packet->sp_Pkt.dp_Type         = action;           /* what to do... */

   /* move all the arguments to the packet */
   pargs = (ULONG *)&(packet->sp_Pkt.dp_Arg1);   /* address of first argument */
   for(count=NULL;count < nargs && count < MAXARGS; ++count) 
     pargs[count]=args[count];

   PutMsg(pid,packet); /* send packet */
   (void)WaitPort(replyport); /* wait for packet to come back */
   (void)GetMsg(replyport);   /* pull message */

   res1 = packet->sp_Pkt.dp_Res1; /* get result */

   /* all done clean up */
   FreeMem((void *)packet,(long)sizeof(*packet)); 
   DeletePort(replyport); 

   return(res1); 
 
}

/*---------------------------------------------------------------------*/
/*  setDate: datestamp the given file with the given date.	       */
/*---------------------------------------------------------------------*/

BOOL setDate( name, date )
char 		 *name;
struct DateStamp *date;
{
   struct MsgPort *task;            /* for process id handler */
   ULONG arg[4];                    /* array of arguments     */
   char *bstr, strcpy();            /* of file to be set      */
   long rc;
   char *strchr();
   int strlen();

   rc = 0;

   if ( !(bstr = (char *)AllocMem(68L, (long)(MEMF_PUBLIC)))) goto exit2;
   if ( !(task = (struct MsgPort *)DeviceProc( name )))       goto exit1;

   /* Dos Packet needs the filename in Bstring format */
 
   (void)strcpy( bstr+1, name );
   *bstr = strlen( name );

   arg[0] = (ULONG)NULL;
   arg[1] = (ULONG)IoErr(); /* lock on parent director set by DeviceProc() */
   arg[2] = (ULONG) bstr >> 2;
   arg[3] = (ULONG) date;
   rc = sendpkt( task, ACTION_SETDATE_MODE, arg, 4L );

exit1: if ( bstr ) FreeMem( (void *)bstr, 68L );
exit2: if ( rc == DOSTRUE )
          return TRUE;
       else
          return FALSE;
}

/*---------------------------------------------------------------------*/
/*  getDate: get the datestamp the given file.			       */
/*---------------------------------------------------------------------*/

BOOL getDate(name, date )
char *name;
register struct DateStamp *date;
{

   struct FileInfoBlock *Fib;
   ULONG		FLock;
   int			result;
   register struct DateStamp	*d;

   if ( (FLock = (ULONG) Lock(name, (long)(ACCESS_READ) )) == NULL)
      return FALSE;

   Fib = (struct FileInfoBlock * )
	AllocMem( (long)sizeof(struct FileInfoBlock), (long)(MEMF_CHIP));

   if (Fib == NULL )
     result = FALSE;
   else
     {
	if ( !Examine( FLock, Fib ))
	   result = FALSE;
	else if ( Fib->fib_DirEntryType > 0 )
	   result = FALSE;  /* It's a directory */
        else
	   {
		d = &Fib->fib_Date;
		date->ds_Days   = d->ds_Days;
		date->ds_Minute = d->ds_Minute;
		date->ds_Tick   = d->ds_Tick;        
		result = TRUE;
	   }
	FreeMem( (void *)Fib, (long)sizeof(struct FileInfoBlock) );
     }

   UnLock( FLock );
   return result;
}
================
cat <wb_parse.c>
================
/* Aztec v3.20a allows you to omit the startup parsing for workbench   */
/* by including this module.  Since this is a cli utility, we are      */
/* going to do just that - well it only saves 232 bytes, but it helps. */
_wb_parse() {}
==============
cat <makefile>
==============
cp:	cp.o PatMatch.o setDate.o wb_parse.o
	ln cp.o PatMatch.o setDate.o wb_parse.o -lc
============
cat <cp.uue>
============
begin 644 Cp
M   #\P         #          (   =/    &     $   /I   '3T[Z%!Y.
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
M!"QL@"Q.[OZ    #[     $    !   4E         /R   #Z@    $ %   
0   #\@   ^L    !   #\@$ 
 
end

chas@gtss.UUCP (Charles Cleveland) (05/25/87)

In article <741@van-bc.UUCP> jlydiatt@van-bc.UUCP (Jeff Lydiatt) writes:
>
>Here is a replacement for AmigaDog "copy" program....
>It ... has the added bonus that it retains the date of copied file.

This is not a feature, it is a bug :-).  Virtually nobody does it this
way and for good reason.  Kindly include this feature as an option only, and
not the default one at that.
-- 
Charles Cleveland		EDU:  chas@ss.physics.gatech.edu
Georgia Tech School of Physics	UUCP: ...!{akgua,allegra,amd,hplabs,ihnp4,
Atlanta, GA 30332			masscomp,ut-ngp,rlgvax,sb1,uf-cgrl,
					unmvax,ut-sally}!gatech!gtss!chas

kim@amdahl.amdahl.com (Kim DeVaughn) (05/26/87)

In article <121@gtss.UUCP>, chas@gtss.UUCP (Charles Cleveland) writes:
> In article <741@van-bc.UUCP> jlydiatt@van-bc.UUCP (Jeff Lydiatt) writes:
> >
> >Here is a replacement for AmigaDog "copy" program....
> >It ... has the added bonus that it retains the date of copied file.
>
> This is not a feature, it is a bug :-).  Virtually nobody does it this
> way and for good reason.  Kindly include this feature as an option only, and
> not the default one at that.

Well, this is probably one of those "religious issues", but I *strongly*
disagree with you, Charles!

It is *especially* important on floppy-based systems to retain the "last
modified" date of a file when it is copied from one floppy to another,
*as the default*.

Without such date retention, it can become quite difficult to figure-out just
which "incarnation" of a set of files (i.e., directory of files) is really the
oldest, newest, etc.  And of course, many processing programs (such as "make",
and various archiving/backup programs) rely on the modification date to guide
their processing.

It may interest you to know that the MS-DOS and PC-DOS "copy" command retains
the file date [I'd hardly call Microsoft and IBM "virtually nobody"], and many
UNIX(R) utilities go to the trouble of preserving/restoring it (e.g., tar,
pack, compress, etc.), as do archivers like ARC, PKARC, etc.

Maybe I'm overlooking something obvious, but I sure can't come up with the
"good reason" you alluded to for not retaining the date on a pure copy
operation.  If you *do* want the file to have the current date, there is
is always "touch" ... :-)!

Anyway, thanks to Jeff for posting the new copy, with file-dating working
the way it should have in the first place!  Now, about the way AmigaDOS
(nee' Tripos) does pattern matching ...

/kim



-- 
UUCP:  kim@amdahl.amdahl.com
  or:  {sun,decwrl,hplabs,pyramid,ihnp4,seismo,oliveb,cbosgd}!amdahl!kim
DDD:   408-746-8462
USPS:  Amdahl Corp.  M/S 249,  1250 E. Arques Av,  Sunnyvale, CA 94086
CIS:   76535,25

mwm@eris.UUCP (05/26/87)

In article <7180@amdahl.amdahl.com> kim@amdahl.amdahl.com (Kim DeVaughn) writes:
<In article <121@gtss.UUCP>, chas@gtss.UUCP (Charles Cleveland) writes:
<> In article <741@van-bc.UUCP> jlydiatt@van-bc.UUCP (Jeff Lydiatt) writes:
<> >It ... has the added bonus that it retains the date of copied file.
<>
<> This is not a feature, it is a bug :-).
<
<Well, this is probably one of those "religious issues", but I *strongly*
<disagree with you, Charles!

No, it's not a religious issue. It depends on what you're using copy for.

<Without such date retention, it can become quite difficult to figure-out just
<which "incarnation" of a set of files (i.e., directory of files) is really the
<oldest, newest, etc.  And of course, many processing programs (such as "make",
<and various archiving/backup programs) rely on the modification date to guide
<their processing.

Odd, I never run into that problem. Maybe because my habits come from
large systems instead of floppy-based systems? Or maybe it's because I
make backups with backup utilities, and use copy for making copies.

<It may interest you to know that the MS-DOS and PC-DOS "copy" command retains
<the file date

Given the general quality of software sold by IBM and Microsoft, this
strikes me as a good indication that copy preserving the date is
wrong 1/2-).

<many UNIX(R) utilities go to the trouble of preserving/restoring it
<(e.g., tar, pack, compress, etc.), as do archivers like ARC, PKARC,
<etc.

Ah, but those unix utilities create archives or backups, *not* copies.
There jobs is to make it possible to recreate files exactly, which
includes the create/modification/access times.

On the other hand, Unix cp creates copies. It makes the creation and
modification date the current time (didn't check access time, but it
hardly matters for this).

<Maybe I'm overlooking something obvious, but I sure can't come up with the
<"good reason" you alluded to for not retaining the date on a pure copy
<operation.

You want a good reason for copy (especially "copy all") to preserve
dates? How about because after I copy new software from a floppy to my
hard disk, I'd like the next incremental backup to pick up all the new
stuff. That way, I don't have to rebuild my hard disk from umpteen
Fish disks (or whatever).

Or how about the following (which I do):

	copy ttyio.c ttyio.c.save
	emacs ttyio.c			; want to try out some new changes...
	make
	mg				; see how they work
	copy ttyio.c.save ttyio.c	; they don't
	make				; gee, nothing happens...

Since copy put the wrong date on the new copy of ttyio.c, it wound up
as "older" than ttyio.o. Similarly, if I get a new version of mg, and
just want to try the ttyio.c, if it was actually edited before I last
tweaked mine, the make won't find anything to do.

<If you *do* want the file to have the current date, there is is
<always "touch" ... :-)!

And if you want the date preserved, there's always "setdate".

In summary, if you want a command to make backups of a file for you, I
suggest you use a backup utility. Don't try and pervert copy into such
a utility. I want copy to make new copies of a file for me.

<Anyway, thanks to Jeff for posting the new copy, with file-dating working
<the way it should have in the first place!

Gee, I must have missed that posting. All I've got is one where
file-dating is broken :-).

	<mike
--
How many times do you have to fall			Mike Meyer
While people stand there gawking?			mwm@berkeley.edu
How many times do you have to fall			ucbvax!mwm
Before you end up walking?				mwm@ucbjade.BITNET

tenny@z.dec.com.UUCP (05/27/87)

Charles Cleveland @ gatech writes:
 
>In article <741@van-bc.UUCP> jlydiatt@van-bc.UUCP (Jeff Lydiatt) writes:

>>Here is a replacement for AmigaDog "copy" program....
>>It ... has the added bonus that it retains the date of copied file.
 
>This is not a feature, it is a bug :-).  Virtually nobody does it this
>way and for good reason.  Kindly include this feature as an option only, and
>not the default one at that.

Since you seem to know that nobody uses date preserving copy programs,
would you care to tell the rest of us what the good reason is?

I agree the date preservation should be an option, but I find the ability
to preserve dates extremely useful in file management.

Dave Tenny
Lisp Development, Digital Equipment Corp.

scotty@l5comp.UUCP (Scott Turner) (05/27/87)

In article <121@gtss.UUCP> chas@gtss.UUCP (Charles Cleveland) writes:
>In article <741@van-bc.UUCP> jlydiatt@van-bc.UUCP (Jeff Lydiatt) writes:
>>
>>Here is a replacement for AmigaDog "copy" program....
>>It ... has the added bonus that it retains the date of copied file.
>
>This is not a feature, it is a bug :-).  Virtually nobody does it this
>way and for good reason.  Kindly include this feature as an option only, and
>not the default one at that.
One man's bug is another man's feature.

I for one am ALL in favor of having my datestamps left alone when I move files
around. I find it a bloody nuisance having the datestamp changed everytime I
move a file from one place to another. And for those that would argue that
dates are not a good way of verifying if one file is newer than the other, I
submit that in that case why not do away with them all together? And I don't
consider and exact copy of another file to be 'newer' than the original.

If I need to change the datestamp that's what I've got 'touch' for.

If there is some reason for why a copy should be seen as 'newer' I'd love to
hear about it.

Scott Turner
-- 
L5 Computing, the home of Merlin, Arthur, Excalibur and the CRAM.
GEnie: JST | UUCP: stride!l5comp!scotty | 12311 Maplewood Ave; Edmonds WA 98020
If Motorola had wanted us to use BPTR's they'd have built in shifts on A regs
[ BCPL? Just say *NO*! ] (I don't smoke, send flames to /dev/null)

mwm@eris.UUCP (05/29/87)

In article <156@l5comp.UUCP> scotty@l5comp.UUCP (Scott Turner) writes:
<I for one am ALL in favor of having my datestamps left alone when I move files
<around. I find it a bloody nuisance having the datestamp changed everytime I
<move a file from one place to another.

You use copy to move a file? That's odd - I use rename. Copy makes a
new copy, rename moves the current one to a new name (or directory).

<If there is some reason for why a copy should be seen as 'newer' I'd love to
<hear about it.

I've already posted a couple; I'm not going to post them again.

A reasonable solution has already been proposed: have two programs,
one called copy creates new copies, and one called cp that creates
copies with preserved dates. "Copy" needs to have behavior identical
to the original DOS copy for script-writers, so the date-mangling one
winds up as "cp" (or "mv", or "backup", or whatever you want to call
it).

While I'm talking to you, let me add to requests that you tone the
excess verbiage (read: flames) in your postings. No matter how good a
tecnical point is, most people will ignore it (or just not bother
reading it) if it's 5% of an article that's 95% flameage.

	<mike
--
How many times do you have to fall			Mike Meyer
While people stand there gawking?			mwm@berkeley.edu
How many times do you have to fall			ucbvax!mwm
Before you end up walking?				mwm@ucbjade.BITNET

ewhac@well.UUCP (05/29/87)

In article <156@l5comp.UUCP> scotty@l5comp.UUCP (Scott Turner) writes:
>In article <121@gtss.UUCP> chas@gtss.UUCP (Charles Cleveland) writes:
>>In article <741@van-bc.UUCP> jlydiatt@van-bc.UUCP (Jeff Lydiatt) writes:
>>>
>>>Here is a replacement for AmigaDog "copy" program....
>>>It ... has the added bonus that it retains the date of copied file.
>>
>>This is not a feature, it is a bug :-).  Virtually nobody does it this
>>way and for good reason.  Kindly include this feature as an option only, and
>>not the default one at that.
>One man's bug is another man's feature.
>
>I for one am ALL in favor of having my datestamps left alone when I move files
>around.  [ ... ]

	In the hopes of quelling this argument, I contribute to this flame:

	My orientation is that copy should "modify" the creation date.  The
reason I have the word "modify" in quotes is because you aren't really
modifying the date at all.

	When you invoke "Copy", you make a copy of the file in question.  It
is *NOT* the same file.  The only thing similar between them is their
contents.  By copying the source file, you *create* a totally *new*
destination file.  When creating a new file, it gets the current date.
Thus, you aren't modifying anything.  You are creating a new file (with
today's date), and filling it with the contents of the source file.

	People complain about this behavior because, when copying the files
to floppy from RAM: (or VD0:), it "changes" the dates, and causes problems
with programs like 'make'.

	However, if you think about it for a while, what you're really
doing is *backing up* your data onto floppies.  A backup utility is designed
to preserve the date when backing up and restoring files.  A typical
development cycle might be:  Create file, make, BACKUP to floppy (preserving
dates), test, Guru, reboot (flushing core), RESTORE RAM: drive from floppy
(preserving dates), and trying again.

	By saying "Copy", you're telling the machine that you want to make a
duplicate of a file's contents by *creating* a new file to keep it in.  By
saying "Backup" (or whatever), you're asking the machine to *preserve* your
files in their current state.

	Anyone who might suggest, because the source and destination
filenames are the same, or that the destination file already exists, that
"Copy" should preserve dates is still wrong.  When copying over an existing
file, one of two things happens:  1) A *new* file is created, data copied
and, if successful, the old version deleted,  2) The destination file is
deleted, and a *new* file created and filled with the data.  Asserting that
the source and destination names are the same doesn't prove anything.

	Someone earlier was picking on semantics.  You should call the
backup utility "Backup", or "Preserve", or something along those lines; and
the file copier "Copy".  That is, if you wanted to be picky :-).

	'Nuff said?

_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
Leo L. Schwab -- The Guy in The Cape	ihnp4!ptsfa -\
 \_ -_	 Bike shrunk by popular demand,	      dual ---> !{well,unicom}!ewhac
O----^o	 But it's still the only way to fly.  hplabs / (pronounced "AE-wack")
"Work FOR?  I don't work FOR anybody!  I'm just having fun."  -- The Doctor

keithd@cadovax.UUCP (06/02/87)

In article <3171@well.UUCP> ewhac@well.UUCP (Leo 'Bols Ewhac' Schwab) writes:
>	When you invoke "Copy", you make a copy of the file in question.  It
>is *NOT* the same file.  The only thing similar between them is their
>contents.  By copying the source file, you *create* a totally *new*
>destination file.  When creating a new file, it gets the current date.
>Thus, you aren't modifying anything.  You are creating a new file (with
>today's date), and filling it with the contents of the source file.

It all depends on what you consider a files 'date' field as,
'last modified date' or 'last copied date'.  I'd prefer a filesystem
that provides BOTH date fields.  'Last modified date' can be an indication 
of 'version', while 'last copied date' can be an indication of when the 
file was backed up.  On a backup disk where all of the files were
backed up at the same time (the way I usually backup files), EVERY files
'last copied date' is the same, and the same as the disk's last used
date.  In such a case, and with only one choice of 'date' fields, I
prefer using 'last modified date' as an indication of version, as
'last copied date' is more usually redundant information.
  
Keith Doyle
#  {ucbvax,ihnp4,decvax}!trwrb!cadovax!keithd
#  cadovax!keithd@ucla-locus.arpa  Contel Business Systems 213-323-8170

tenny@z.dec.com.UUCP (06/02/87)

Mike Meyer at berkeley writes:
 
>You use copy to move a file? That's odd - I use rename. Copy makes a
>new copy, rename moves the current one to a new name (or directory).
 
Unfortunately this doesn't work across devices. (can't rename df1:xxx ram:xxx)

Dave Tenny

scotty@l5comp.UUCP (Scott Turner) (06/04/87)

In article <3762@jade.BERKELEY.EDU> mwm@eris.BERKELEY.EDU (Mike (My watch has windows) Meyer) writes:
>You use copy to move a file? That's odd - I use rename. Copy makes a
>new copy, rename moves the current one to a new name (or directory).
I often move files in ways that rename can't handle. For example: I have a dual
hard disk system. I often "Load Manage" it by shifting things from one drive
to the other as they move from "active" to "not so active". Or from not so
active back to active. And yes when they go "stone cold" I kick 'em off to
floppy.

>I've already posted a couple; I'm not going to post them again.
Great. I got them soon after my posting. This net bounce takes a bit of getting
used to.

>A reasonable solution has already been proposed: have two programs,
>one called copy creates new copies, and one called cp that creates
>copies with preserved dates. "Copy" needs to have behavior identical
>to the original DOS copy for script-writers, so the date-mangling one
>winds up as "cp" (or "mv", or "backup", or whatever you want to call
>it).
A better solution I think is to use a shell 'alias' command to deal with this.
Even before this whole topic came up I had alias'd copy as cp on my system. I
have since re-alias'd cp as xcopy.

An issue that this raises is what to call the two copy programs so that shell
scripts can address both. By using aliases I have allowed myself to use the
copy program I find most useful without interfering with shell scripts or make.
But while copy is standardized the other copy isn't. I'm all for calling it
'xcopy', :) but I'm sure there's got to be a better name out there. I'm NOT in
favor of calling it cp.

>While I'm talking to you, let me add to requests that you tone the
>excess verbiage (read: flames) in your postings. No matter how good a
>tecnical point is, most people will ignore it (or just not bother
>reading it) if it's 5% of an article that's 95% flameage.
I really appreciate all the time that countless netlanders have taken on my
account on this subject. I just hope that some of it will rub off on the
countless OTHER netlanders out there with similar problems. I think I've been
a lightning rod on this subject, but I am by no means the ONLY sinner amongst
those here on the net. I seem to be a natural when it comes to drawing down
attention on topics that need it. But we also have to be careful not to focus
on me to the exclusion of all else.

Scott Turner
-- 
L5 Computing, the home of Merlin, Arthur, Excalibur and the CRAM.
GEnie: JST | UUCP: stride!l5comp!scotty | 12311 Maplewood Ave; Edmonds WA 98020
If Motorola had wanted us to use BPTR's they'd have built in shifts on A regs
[ BCPL? Just say *NO*! ] (I don't smoke, send flames to /dev/null)

jdg@elmgate.UUCP (06/08/87)

In article <1578@cadovax.UUCP> keithd@cadovax.UUCP (Keith Doyle) writes:
>It all depends on what you consider a files 'date' field as,
>'last modified date' or 'last copied date'.  I'd prefer a filesystem
>that provides BOTH date fields.  'Last modified date' can be an indication 
>of 'version', while 'last copied date' can be an indication of when the 
>file was backed up. 
>Keith Doyle
This is probably a dumb idea (I gotta million of 'm,  ah, cha cha cha
cha) but what about a copy program that preserves the old date (or inserts
the new date) into the Filenote area (the last 8 characters)?
Ok, ok .... It was just an idea..... 8^)



-- 
Jeff Gortatowsky       {seismo,allegra}!rochester!kodak!elmgate!jdg
Eastman Kodak Company  
These comments are mine alone and not Eastman Kodak's. How's that for a
simple and complete disclaimer? 

bryce@COGSCI.BERKELEY.EDU.UUCP (06/09/87)

In article <640@elmgate.UUCP> jdg@aurora.UUCP (Jeff Gortatowsky) writes:
>
>but what about a copy program that preserves the old date (or inserts
>the new date) into the Filenote area (the last 8 characters)?

What about a copy command that PRESERVES the FileNote (comment) area!?  Some
of us actually use it.  ( C-A, Metacomco take note.  Your COPY does not )


    ----------------------------------
    | See ClickToFront, coming soon  |
    | to a newsgroup near you.  Try  |
    | it, you'll like it.            |
    ----------------------------------
         Ack!  (NAK,EOT,SOH)
 |\ /|  .
 {o O} .  bryce@cogsci.berkeley.EDU -or- ucbvax!cogsci!bryce
 ( " ) 
   U

spencer@eris.UUCP (06/10/87)

In article <10166@decwrl.DEC.COM> tenny@z.dec.com writes:
>Mike Meyer at berkeley writes:
>>You use copy to move a file? That's odd - I use rename. Copy makes a
>>new copy, rename moves the current one to a new name (or directory).
> 
>Unfortunately this doesn't work across devices. (can't rename df1:xxx ram:xxx)
>Dave Tenny

Oh, too true.  Matt's Shell has that ability, and I always thought that it
shouldn't be that hard to write a program that would allow you to move files
around devices like you can around disk structures.  It should support wild
cards, and it should preserve the date stamp, then we can use copy (or cp) 
to copy files and use the current time stamp (and wild card here too).

Sorry, I was at CES while you all got to have your licks about who gets to
datestamp, I appologize for being late.

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
Randy Spencer      P.O. Box 4542   Berkeley  CA  94704        (415)284-4740 
                         I N F I N I T Y                 BBS: (415)283-5469
Now working for          |||||||||||::::... . .                    BUD-LINX
But in no way            |||||||||||||||::::.. .. .
Officially representing  ||||||||||||:::::... ..    ....ucbvax!mica!spencer
                         s o f t w a r e          spencer@mica.berkeley.edu
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-