home *** CD-ROM | disk | FTP | other *** search
- /*
- ** 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 */
-