/* 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 #include #include #include #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); }