home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
World of A1200
/
World_Of_A1200.iso
/
programs
/
disk
/
directory
/
ecopy
/
source.lha
/
ECopy.c
Wrap
C/C++ Source or Header
|
1995-01-10
|
30KB
|
861 lines
/*
** ECopy V1.1 -- Economical File Copy Utility
** Copyright (c) 1994 by Sam Yee.
** Feb 20, 1994.
**
** Permission is here granted for the distribution of ECopy, provided
** that files are intacted and in unmodified state.
*/
/*******************************************************************/
/* includes */
#include <clib/alib_protos.h>
#include <clib/dos_protos.h>
#include <clib/exec_protos.h>
#include <ctype.h>
#include <dos/dos.h>
#include <dos/dosasl.h>
#include <exec/memory.h>
#include <math.h>
#include <pragmas/dos_pragmas.h>
#include <pragmas/exec_pragmas.h>
#include <stdlib.h>
#include <string.h>
/*******************************************************************/
/* constants */
/* for ReadArgs() */
#define OPT_FROM 0
#define OPT_TO 1
#define OPT_QUIET 2
#define OPT_BUF 3
#define OPT_CLONE 4
#define OPT_LISTNC 5 /* list files not copied */
#define OPT_MOVE 6
#define OPT_RESERVE 7
#define OPT_SUFFIX 8
#define OPT_BLOCKCAP 9 /* block capability; bytes usable in each block */
#define OPT_FORCE 10 /* force move/copy */
#define OPT_READFILE 11 /* read file names from file */
#define OPT_NCTOFILE 12 /* file name to save files not copied to */
#define OPT_COUNT 13
#define BUFFERS 8 /* 8K of buffers */
#define MINBUFS 1 /* minimum 1K buffer */
#define TEMPLATE "FROM/M,TO/A,QUIET/S,BUF=BUFFER/K/N,CLONE/S,LISTNC/S,MOVE/S,\
RES=RESERVE/K/N,SUF=SUFFIX/K,BLOCKCAP/K/N,FORCE/S,READFILE/K,NCTOFILE/K"
/*******************************************************************/
/* structure definitions */
struct FileLink
{
struct Node node;
struct FileInfoBlock *fib;
};
struct FileList
{
struct List list;
long count; /* number of nodes in list */
};
/*******************************************************************/
/* prototypes */
/* need these for #pragmas */
extern struct DOSBase *DOSBase;
extern struct SysBase *SysBase;
/* disable SAS/C's ^C checking */
void __regargs __chkabort(void);
void __regargs __chkabort(){}
void __regargs _CXBRK(void);
void __regargs _CXBRK(){}
BOOL CollectFiles(struct FileList *fl, char *fname);
void FreeFileList(struct FileList *flist, BOOL list_not_copied,
BOOL is_copy, char *nctofname);
void SortListDown(struct FileList *flist);
struct FileLink *GetBestFitFile(struct List *list, long max_size);
int GetDiskInfo(char *disk_name, struct InfoData *info);
long CalcReserveBlocks(long size, long block_size);
BOOL CollectNamesFromFile(struct FileList *flist, char *fname);
/*******************************************************************/
/* version info */
char *version = "$VER: ECopy 1.10 (20.2.94)";
/*******************************************************************/
/* main routine, what else? */
int
main(int argc, /* number of arguments from command invocation */
char *argv[]) /* array of strings of command line options */
{
struct InfoData *info = NULL;
struct FileList flist;
struct FileLink *flink;
struct RDArgs *myrda = NULL;
char **from_files,
vol_name[256], /* destination volume name */
dst_name[256]; /* destination file name */
void *io_buf; /* copy buffer */
LONG result[OPT_COUNT],
rc = 10,
i,
block_cap, /* # of usable bytes in a block */
blocks_free,
read_len,
buffers = BUFFERS,
buf_size,
disk_used = 0L, /* total bytes used on disk(s) */
disk_max = 0L; /* maximum usable bytes on disk(s) */
BPTR src_fh, /* source file handle */
dst_fh; /* destination file handle */
BOOL quit = FALSE,
full, /* disk full? */
broke_scan = FALSE; /* user smashed ^C ? */
NewList(&flist.list);
flist.count = 0;
if (argc == 1)
{
Printf("\
ECopy 1.10 -- Economical File Copy Utility\n\
Copyright (c) 1992-1994 by Sam Yee. All Rights Reserved.\n\
Freely Distributable. Bug reports to samy@sfu.ca or 1:153/765\n\
Compiled on "__DATE__ " (" __TIME__").\n\n\
For command usage type `%s ?'\n",argv[0]);
rc = 0;
}
else
{
myrda = (struct RDArgs *)AllocDosObject(DOS_RDARGS,NULL);
info = (struct InfoData *)AllocVec(sizeof(struct InfoData),MEMF_PUBLIC);
}
if (myrda && info)
{
/* read from stdin instead */
myrda->RDA_Source.CS_Buffer = NULL;
myrda->RDA_Source.CS_Length = 0L;
/* initialize result array */
for (i = 0; i < OPT_COUNT; i++)
result[i] = 0L;
/* parse command line options */
if (ReadArgs(TEMPLATE,result,myrda))
{
if (result[OPT_BUF])
buffers = max(*((long *)result[OPT_BUF]),MINBUFS);
buf_size = buffers*1024L;
/* if copy buffer allocated */
if (io_buf = AllocVec(buf_size,MEMF_PUBLIC))
{
from_files = (char **)result[OPT_FROM];
/* scan all files/directories to copy from */
for (i = 0; from_files[i]; i++)
{
if (broke_scan = CollectFiles(&flist,from_files[i]))
break;
}
if (!broke_scan && result[OPT_READFILE])
broke_scan = CollectNamesFromFile(&flist,
(char *)result[OPT_READFILE]);
if (!broke_scan) /* ^C not hit */
{
SortListDown(&flist); /* sort in inverse order according
to size */
strncpy(vol_name,(char *)result[OPT_TO],
sizeof(vol_name)-1);
}
else
quit = TRUE;
while (!quit)
{
/* get disk info data */
if (!(block_cap = GetDiskInfo(vol_name,info)))
{
PrintFault(IoErr(),vol_name);
break;
}
if (result[OPT_BLOCKCAP])
block_cap = max(*((long *)result[OPT_BLOCKCAP]),1L);
if (CheckSignal(SIGBREAKF_CTRL_C))
{
PrintFault(ERROR_BREAK,NULL);
break;
}
/* reserve 1 for file header and 1 for use by OS */
blocks_free = (info->id_NumBlocks-info->id_NumBlocksUsed-
CalcReserveBlocks(result[OPT_RESERVE] ?
(*((long *)result[OPT_RESERVE])*1024L) : 0L,
block_cap))-2;
full = FALSE;
flink = GetBestFitFile(&(flist.list),blocks_free*block_cap);
if (flink)
{
/* get destination file name */
strncpy(dst_name,vol_name,sizeof(dst_name)-1);
AddPart(dst_name,FilePart(flink->fib->fib_FileName),
sizeof(dst_name));
/* add suffix as required */
if (result[OPT_SUFFIX])
strncat(dst_name,(char *)result[OPT_SUFFIX],
sizeof(dst_name)-1);
if (src_fh = Open(flink->fib->fib_FileName,MODE_OLDFILE))
{
if (!(dst_fh = Open(dst_name,MODE_NEWFILE)))
{
/* if delete/write protected and force then
delete file */
if (((IoErr() == ERROR_DELETE_PROTECTED) ||
(IoErr() == ERROR_WRITE_PROTECTED)) &&
result[OPT_FORCE])
{
SetProtection(dst_name,~(FIBF_READ|FIBF_WRITE|
FIBF_EXECUTE|FIBF_DELETE));
DeleteFile(dst_name);
}
dst_fh = Open(dst_name,MODE_NEWFILE);
}
if (dst_fh)
{
BOOL bad = FALSE;
if (!result[OPT_QUIET])
{
Printf("%s..",flink->fib->fib_FileName);
Flush(Output());
}
while ((read_len = Read(src_fh,io_buf,buf_size)) > 0L)
{
if (CheckSignal(SIGBREAKF_CTRL_C))
{
PrintFault(ERROR_BREAK,NULL);
bad = quit = TRUE;
break;
}
else if (Write(dst_fh,io_buf,read_len) != read_len)
{
PrintFault(IoErr(),NULL);
full = bad = TRUE;
break;
}
}
if (IoErr() && !read_len)
{
PrintFault(IoErr(),NULL);
bad = TRUE;
/* remove file node in the list to be copied */
Remove(&(flink->node));
flist.count--;
}
Close(dst_fh);
if (bad) /* if copy was bad */
{
Printf("Incomplete file %s..",dst_name);
Flush(Output());
DeleteFile(dst_name);
Printf("deleted\n");
}
else
{
if (!result[OPT_QUIET])
Printf(result[OPT_MOVE] ? "moved\n" : "copied\n");
/* if clone or move then clone file */
if (result[OPT_CLONE] || result[OPT_MOVE])
{
SetProtection(dst_name,flink->fib->fib_Protection);
SetFileDate(dst_name,&(flink->fib->fib_Date));
/* if there is a comment, duplicate it */
if (flink->fib->fib_Comment[0])
SetComment(dst_name,flink->fib->fib_Comment);
}
/* if moving files */
if (result[OPT_MOVE])
{
Close(src_fh); /* close file first */
src_fh = NULL;
/* attempt deletion */
if (!DeleteFile(flink->fib->fib_FileName))
{
/* if force deletion */
if (result[OPT_FORCE])
{
SetProtection(flink->fib->fib_FileName,
~(FIBF_READ|FIBF_WRITE|
FIBF_EXECUTE|FIBF_DELETE));
if (!DeleteFile(flink->fib->fib_FileName))
PrintFault(IoErr(),flink->fib->fib_FileName);
}
else
PrintFault(IoErr(),flink->fib->fib_FileName);
}
}
/* remove file node in the list to be copied */
Remove(&(flink->node));
flist.count--;
}
}
else
{
PrintFault(IoErr(),dst_name);
if ((IoErr() == ERROR_DELETE_PROTECTED) ||
(IoErr() == ERROR_WRITE_PROTECTED))
{
Remove(&(flink->node));
flist.count--;
}
}
/* if file not already closed, close it */
if (src_fh)
Close(src_fh);
}
else /* fatal error opening file */
{
PrintFault(IoErr(),flink->fib->fib_FileName);
Remove(&(flink->node));
flist.count--;
if (!flist.count)
{
rc = 0;
quit = TRUE;
}
}
}
else if (flist.count) /* if more files to copy then
disk must be full */
full = TRUE;
else
{
quit = TRUE;
rc = 0;
}
if (full && !quit)
{
char key = '\0';
GetDiskInfo(vol_name,info);
disk_used += (info->id_NumBlocksUsed*info->id_BytesPerBlock);
disk_max += (info->id_NumBlocks*info->id_BytesPerBlock);
PrintFault(ERROR_DISK_FULL,vol_name);
while (TRUE)
{
/* display status and get response */
Printf("\n%ld more file%s to be copied.\n\n",
flist.count, (flist.count == 1L) ? "" : "s");
Printf("[R]etry, [C]hange disk, or [A]bort? ");
Flush(Output());
SetMode(Output(),TRUE);
Read(Input(),&key,1L);
key = toupper(key);
SetMode(Output(),FALSE);
FPutC(Output(),key);
Printf("\n");
if (key == 'R') /* retry copy */
{
Printf("\n");
break;
}
else if (key == 'C') /* change destination disk */
{
char *s;
/* get new disk name */
Printf("\nEnter new disk name [%s]: ",vol_name);
Flush(Output());
Read(Input(),dst_name,sizeof(dst_name)-1);
Printf("\n");
s = dst_name;
while (*s && (*s != '\r') && (*s != '\n')) s++;
*s = '\0';
if (*dst_name)
strncpy(vol_name,dst_name,sizeof(vol_name)-1);
if (GetDiskInfo(vol_name,info))
break;
else
PrintFault(IoErr(),vol_name);
}
else if (key == 'A') /* abort copy */
{
quit = TRUE;
break;
}
}
}
if (!flist.count) /* we are done */
{
rc = 0;
quit = TRUE;
}
}
FreeFileList(&flist,!broke_scan && (result[OPT_LISTNC] ||
result[OPT_NCTOFILE]),
result[OPT_MOVE],(char *)result[OPT_NCTOFILE]);
if (!broke_scan && disk_used && disk_max)
{
ULONG x;
if (disk_used > 4000000) /* check if * will overflow */
x = ((disk_used*100)/disk_max)*10;
else
x = (disk_used*1000)/disk_max;
Printf("\nEfficiency: %ld.%ld%s!\n",x/10,x - ((x/10)*10),"%");
}
FreeVec(io_buf);
}
else
PrintFault(ERROR_NO_FREE_STORE,NULL);
FreeArgs(myrda);
}
else
PrintFault(IoErr(),NULL);
}
if (myrda)
FreeDosObject(DOS_RDARGS,myrda);
if (info)
FreeVec(info);
return(rc); /* return with error code, if any */
}
/*******************************************************************/
/* allocate a file information node that can be added to the list
of files to copy */
struct FileLink *
AllocFileLink(void)
{
struct FileLink *flink;
if (flink = AllocVec(sizeof(struct FileLink),MEMF_PUBLIC|MEMF_CLEAR))
{
if (!(flink->fib = AllocDosObject(DOS_FIB,NULL)))
{
FreeVec(flink);
flink = NULL;
}
}
return(flink);
}
/*******************************************************************/
/* free the file information node allocated by AllocFileLink() */
void
FreeFileLink(struct FileLink *flink)
{
FreeDosObject(DOS_FIB,flink->fib);
FreeVec(flink);
}
/*******************************************************************/
/* free the list if file information nodes, with file name listing */
void
FreeFileList(struct FileList *flist,
BOOL list_not_copied, /* list those files not copied? */
BOOL is_copy, /* did we copy the files?
if not, we moved them */
char *nctofname) /* file to save not copied file
file names to */
{
struct FileLink *flink;
BOOL broke = FALSE;
LONG not_copied_count = flist->count;
BPTR outfh = NULL;
if (nctofname)
{
if (!(outfh = Open(nctofname,MODE_NEWFILE)))
PrintFault(IoErr(),nctofname);
}
while (flink = (struct FileLink *)RemHead(&flist->list))
{
if (!broke && (list_not_copied || outfh))
{
if (CheckSignal(SIGBREAKF_CTRL_C))
{
PrintFault(ERROR_BREAK,NULL);
broke = TRUE;
}
else
{
if (outfh)
FPrintf(outfh,"%s\n",flink->fib->fib_FileName);
if (list_not_copied)
Printf("%s\n",flink->fib->fib_FileName);
}
}
FreeFileLink(flink);
}
if (!broke && list_not_copied && not_copied_count)
{
Printf("\n%ld file%s not %sd.\n",
not_copied_count,(not_copied_count == 1L) ? "" : "s",
is_copy ? "move" : "copie");
}
if (outfh)
Close(outfh);
}
/*******************************************************************/
/* collect a list of file information nodes from a directory given
by 'lock'. each file name is compared with 'pattern'. */
BOOL /* broke? */
CollectDirFiles(struct FileList *fl, /* file link list */
BPTR lock, /* lock of directory */
char *pattern, /* pattern buf from
ParsePatternNoCase() to match
each filename with */
struct FileInfoBlock *fib) /* fib from previous Examine () */
{
struct FileLink *flink;
char pathpart[256],
pathname[256];
BOOL broke = FALSE;
NameFromLock(lock,pathpart,sizeof(pathpart));
/* scan the whole directory */
while (ExNext(lock,fib))
{
/* user press ^C break? */
if (CheckSignal(SIGBREAKF_CTRL_C))
{
PrintFault(ERROR_BREAK,NULL);
broke = TRUE;
break;
}
if (fib->fib_DirEntryType < 0) /* file? */
{
/* pattern matched? */
if (MatchPatternNoCase(pattern,FilePart(fib->fib_FileName)))
{
if (flink = AllocFileLink())
{
/* add file information node to list */
strcpy(pathname,pathpart);
AddPart(pathname,fib->fib_FileName,sizeof(pathname));
strncpy(fib->fib_FileName,pathname,sizeof(fib->fib_FileName)-1);
CopyMem(fib,flink->fib,sizeof(struct FileInfoBlock));
AddTail(&(fl->list),&(flink->node));
fl->count++;
}
}
}
}
return(broke);
}
/*******************************************************************/
/* collect a list of file information nodes from a directory or
just one file information node if a file is specified. */
BOOL /* broke? */
CollectFiles(struct FileList *fl,
char *fname)
{
struct FileLink *flink;
char parse_buf[256],
pathname[256],
*path,
c;
BPTR dlock,
flock;
BOOL error = FALSE,
broke = FALSE;
if (flink = AllocFileLink())
{
strncpy(pathname,fname,sizeof(pathname)-1);
/* Attempt to Lock() file or directory */
if (dlock = Lock(pathname,ACCESS_READ))
{
if (Examine(dlock,flink->fib))
{
if (flink->fib->fib_DirEntryType > 0) /* a directory */
AddPart(pathname,"#?",sizeof(pathname));
}
else
{
PrintFault(IoErr(),pathname);
error = TRUE;
}
UnLock(dlock);
}
if (!error) /* if file examinable */
{
path = PathPart(pathname);
c = *path;
*path = '\0';
if (dlock = Lock(pathname,ACCESS_READ)) /* Lock() file or directory */
{
if (Examine(dlock,flink->fib)) /* examine file or directory */
{
*path = c;
if (!(*path))
path = "#?";
/* if patterned filename specified, scan the whole directory */
if (ParsePatternNoCase(path,parse_buf,sizeof(parse_buf)))
broke = CollectDirFiles(fl,dlock,parse_buf,flink->fib);
else /* file specified */
{
/* get relative file name */
NameFromLock(dlock,parse_buf,sizeof(parse_buf));
AddPart(parse_buf,path,sizeof(parse_buf));
if (flock = Lock(parse_buf,ACCESS_READ)) /* Lock() file */
{
if (Examine(flock,flink->fib)) /* examine file */
{
/* get absolute file name and add to list */
NameFromLock(flock,flink->fib->fib_FileName,
sizeof(flink->fib->fib_FileName));
AddTail(&fl->list,&flink->node);
fl->count++;
flink = NULL;
}
else
PrintFault(IoErr(),parse_buf);
UnLock(flock);
}
else
PrintFault(IoErr(),parse_buf);
}
}
else
PrintFault(IoErr(),pathname);
UnLock(dlock);
}
else
PrintFault(IoErr(),pathname);
}
/* if file information node not used then free it */
if (flink)
FreeFileLink(flink);
}
return(broke);
}
/*******************************************************************/
/* callback routine for qsort() to sort file information nodes
by size */
int
sld_cmp(struct FileLink **f1,
struct FileLink **f2)
{
return((*f1)->fib->fib_Size-(*f2)->fib->fib_Size);
}
/*******************************************************************/
/* sort list of file information nodes inversely by size */
void
SortListDown(struct FileList *flist)
{
struct FileLink **flink_array;
long i;
if (flist->count)
{
if (flink_array = AllocVec(flist->count*sizeof(struct FileLink **),
MEMF_PUBLIC|MEMF_CLEAR))
{
/* create array to sort */
for (i = 0; i < flist->count; i++)
flink_array[i] = (struct FileLink *)RemHead(&(flist->list));
/* sort array in ascending order*/
qsort(flink_array,flist->count,sizeof(struct FileLink **),sld_cmp);
/* rebuild file list in reverse order */
for (i = 0; i < flist->count; i++)
AddHead(&(flist->list),(struct Node *)flink_array[i]);
FreeVec(flink_array);
}
}
}
/*******************************************************************/
/* find the best file to fit into current disk */
struct FileLink *
GetBestFitFile(struct List *list,
long max_size)
{
struct FileLink *flink;
flink = (struct FileLink *)list->lh_Head;
while (flink->node.ln_Succ) /* not end of list? */
{
if (flink->fib->fib_Size <= max_size)
break;
flink = (struct FileLink *)flink->node.ln_Succ;
}
return(flink->node.ln_Succ ? flink : NULL);
}
/*******************************************************************/
/* get information about disk and also return the size of each block
that is usable */
int /* block capability */
GetDiskInfo(char *disk_name,
struct InfoData *info)
{
BPTR lock;
int block_cap = 0;
if (lock = Lock(disk_name,ACCESS_READ))
{
if (Info(lock,info))
{
switch (info->id_DiskType)
{
case ID_DOS_DISK:
case ID_INTER_DOS_DISK:
case ID_FASTDIR_DOS_DISK:
block_cap = 488;
break;
case ID_FFS_DISK:
case ID_INTER_FFS_DISK:
case ID_FASTDIR_FFS_DISK:
block_cap = 504;
break;
case ID_MSDOS_DISK:
block_cap = 512; /* this is a guess,
based on tests */
break;
default:
block_cap = info->id_BytesPerBlock;
break;
}
}
UnLock(lock);
}
return(block_cap);
}
/*******************************************************************/
/* calculate how many blocks to reserve */
long
CalcReserveBlocks(long size, /* minimum size to reserve */
long block_size) /* usable bytes in each block */
{
long reserve = 0L;
if (size > 0L)
{
reserve = size/block_size;
if (size%block_size)
reserve++;
}
return(reserve);
}
/*******************************************************************/
/* get the list of files to copy from a file */
BOOL /* broke? */
CollectNamesFromFile(struct FileList *flist,
char *fname) /* filename to
read names from */
{
BPTR fh;
char line[256],
*s;
BOOL broke = FALSE;
if (fh = Open(fname,MODE_OLDFILE))
{
while (FGets(fh,line,sizeof(line)))
{
/* remove CR and/or LF if any */
s = line;
while (*s && (*s != '\r') && (*s != '\n')) s++;
*s = '\0';
if (broke = CollectFiles(flist,line))
break;
}
Close(fh);
}
else
PrintFault(IoErr(),fname);
return(broke);
}
/*******************************************************************/
/* END */