/* * xcopy: copies directory trees from one place to another. * -dos version */ #include #include #include #include #include #include #include #include "glob.h" /* for file attribute #defines */ #include "getopt.c" /* iiiiiiieeeeeee!!!! */ #define FUDGE 20 /* want to leave this many blocks free on */ /* destination filesystem */ struct _filelist { char *filename; unsigned fdate, ftime; long fsize; short fattrib; short flags; #define MARKED 0x01 /* marked for copy */ #define COPIED 0x02 /* has been copied */ #define TRASHED 0x04 /* couldn't be copied */ #define TOOBIG 0x08 /* too big to fit onto destination device */ #define FMASK 0x0f /* to pick out various statuses */ #define DIRECT 0x10 /* this is a directory */ } *filelist; int filecount = 0; char verbose = 0; /* whistle while we work */ char dest_is_removable=0; /* we are copying to removable media */ char print_report = 0; /* give a list of files that didn't copy */ char reply=0; /* default [y/n] answer */ char create_directories = 0; /* create directories, even if empty */ char preserve = 0; /* preserve date and time on destinations */ char incremental = 0; /* for incremental backup - only copy files */ /* with archive bit set */ char epoch = 0; /* copy everything, but clear F_ARCHIVE bit */ void *xmalloc(); /* allocate memory or die */ void *xrealloc(); /* reallocate memory or die */ char *xstrdup(); /* strdup() or die */ int sizecompare(); /* for qsorting file list */ int alphacompare(); /* for sorting alphabetically */ char *basename(); /* pick out the basename of a file */ char *plural(); /* for nice output */ void makegood(); /* lowercase and slashify pathnames */ /* * xcopy, in mortal flesh */ main(argc, argv) char **argv; { char source[200]; int c; opterr = 1; while (getopt(argc, argv, "?vtPyndpie") != EOF) switch (optopt) { case 'v': verbose = 1; break; case 't': dest_is_removable = 1; break; case 'P': print_report = 1; break; case 'y': case 'n': reply = optopt; break; case 'd': create_directories = 1; break; case 'p': preserve = 1; break; case 'i': incremental = 1; break; case 'e': epoch = 1; break; default: goto badboy; } argc -= (optind-1); argv += (optind-1); if (argc != 3) { badboy: fprintf(stderr, "usage: xcopy [-deinpPtvy] source-path dest-path\n"); exit(1); } filelist= xmalloc(1); expand(argv[1]); if (filecount > 0) xcopy(argv[2]); exit(0); } /* main */ /* * makegood() lowercases a pathname and maps all backslashes to forward * slashes. */ void makegood(p) register char *p; { strlwr(p); for (; *p; ++p) if (*p == '\\') *p = '/'; } /* makegood */ /* * expand() creeps through a filesystem making a list of files, with * their names, sizes, and pathnames */ expand(base) char *base; { char spath[300]; register char *send; register status; short count; struct find_t st; makegood(base); strcpy(spath, base); send = basename(spath); if (*send) { strcat(spath, "/*.*"); send = basename(spath); } else /* pattern ends in / or : */ strcat(spath, "*.*"); if (_dos_findfirst(spath, F_HIDDEN|F_SYSTEM|F_DIRECT, &st) == 0) { do { if (incremental && (st.attrib & F_ARCHIVE) == 0) continue; strlwr(st.name); strcpy(send, st.name); if (st.attrib & F_DIRECT) if (strcmp(st.name, "..") == 0 || strcmp(st.name,".") == 0) continue; else { expand(spath); if (create_directories) strcat(spath, "/"); else continue; } filelist = xrealloc(filelist, (1+filecount)*sizeof filelist[0]); filelist[filecount].filename = xstrdup(spath); filelist[filecount].fdate = st.wr_date; filelist[filecount].ftime = st.wr_time; filelist[filecount].fattrib = st.attrib; filelist[filecount].fsize = (st.attrib & F_DIRECT) ? 0 : st.size; filelist[filecount].flags = (st.attrib & F_DIRECT) ? DIRECT : 0; filecount++; } while (_dos_findnext(&st) == 0); } } /* expand */ /* * sizecompare() is used to qsort the list of files for best fitting * (sort by reverse size, then filename) */ sizecompare(a, b) register struct _filelist *a, *b; { register long diff; if (diff = (b->fsize - a->fsize)) /* sort by reverse size */ return (diff>0) ? 1 : -1; return alphacompare(a,b); } /* sizecompare */ /* * alphacompare() just makes the list alphabetical */ alphacompare(a,b) register struct _filelist *a, *b; { return strcmp(a->filename, b->filename); } /* alphacompare */ /* * plural() returns an 's' if the number you pass in isn't 1 */ char * plural(num) { return (num!=1)?"s":""; } /* plural */ /* * ask_for_disk() asks you to place a disk (or whatever) * into the destination drive. If you type a !, it will let * you do a shell escape (for formatting disks, etc) */ ask_for_disk(drive) { register c; char escape[200]; top:fprintf(stderr, "insert disk in %c: and [cr] to copy\n", (drive+'a')-1); do { c = getch(); /* do possible shell escapes */ if (c == '!') { putchar('!'); fflush(stdout); gets(escape); system(escape); goto top; } } while (c != '\n' && c != '\r'); } /* ask_for_disk */ /* * xcopy_report() tells us what happened */ xcopy_report() { int didntcopy, badcopy, copied; register i; for (didntcopy=badcopy=i=0; i 0 && ((copied != filecount) || !verbose)) fprintf(stderr, "copied %d file%s.\n", copied, plural(copied)); if (didntcopy > 0) fprintf(stderr, "didn't copy %d file%s.\n", didntcopy, plural(didntcopy)); if (badcopy > 0) fprintf(stderr, "couldn't copy %d file%s.\n", badcopy, plural(badcopy)); if (print_report) { if (badcopy > 0) { printf("\nErrors copying:\n\n"); for (i=0; i 0) { printf("\nDidn't copy:\n\n"); for (i=0; i dt.total_clusters) { fprintf(stderr, "xcopy: %s is too large to fit onto destination\n", filelist[i].filename); filelist[i].flags |= TOOBIG; } } /* copy the files */ if (verbose) fprintf(stderr, "copying %d file%s (%ld byte%s)\n", tocopy, plural(tocopy), totalbytes, plural(totalbytes!=1?2:1)); for (more=i=j=0; i destblocks-FUDGE) { fprintf(stderr, "xcopy: no space on %s\n", dest); exit(3); } if (verbose) fprintf(stderr, "copying %d file%s (%ld byte%s)\n", filecount, plural(filecount), totalbytes, plural(totalbytes!=1?2:1)); for (i=0; iflags & DIRECT) { if (verbose) fprintf(stderr, "mkdir %s\n", destination); if (makedestpath(destination)) { if (preserve) _dos_setfileattr(destination, source->fattrib); return COPIED; } return TRASHED; } if (verbose) fprintf(stderr, "cp %s %s\n", source->filename, destination); if ((src=open(source->filename, O_RDONLY|O_BINARY)) < 0) { perror(source->filename); return TRASHED; } if ((dst=open(destination, O_WRONLY|O_BINARY|O_CREAT|O_TRUNC, 0666)) < 0) { /* if we can't open the destination file, attempt to make * the directory for it, then try to reopen it. If this * open fails, then we abandon ship */ if (!makedestpath(destination)) return TRASHED; if ((dst=open(destination, O_WRONLY|O_BINARY|O_CREAT|O_TRUNC, 0666)) < 0) { perror(destination); close(src); return TRASHED; } } while ((size=read(src, xbuf, XCPYSIZ)) > 0) { if (write(dst, xbuf, size) != size) { perror("write"); status = TRASHED; if (dest_is_removable) { fprintf(stderr, "xcopy: try to copy %s onto next disk?", source->filename); while (1) { c = reply ? reply : getch(); if (strchr("yYnN", c)) { fprintf(stderr, "%c\n", c); if (c == 'Y' || c == 'y') status = 0; break; } putc(7, stderr); } } } } /* write loop */ if (size < 0) { perror("read"); status = TRASHED; } close(src); if (status == COPIED) { /* do we need to preserve dates and times? */ if (preserve || incremental || epoch) _dos_setftime(dst, source->fdate, source->ftime); close(dst); if (incremental || epoch) { source->fattrib &= ~F_ARCHIVE; _dos_setfileattr(source->filename, source->fattrib); } if (preserve || incremental || epoch) _dos_setfileattr(destination, source->fattrib); } else { close(dst); remove(destination); } return status; } /* copyfile */ /* * basename() does what you'd expect. */ char * basename(path) register char *path; { register char *p; /* find end of name */ for (p=path; *p; ++p) ; while (p>=path) if (*p == '/' || *p == ':') return 1+p; else --p; return path; } /* basename */ /* * routines to allocate memory or die a most horrid death */ void * xmalloc(size) { register void *p; if (p=malloc(size)) return p; perror("xmalloc"); exit(2); /*NOTREACHED*/ } /* xmalloc */ void * xrealloc(ptr, size) void *ptr; { register void *p; if (p=realloc(ptr, size)) return p; perror("xrealloc"); exit(2); /*NOTREACHED*/ } /* xrealloc */ char * xstrdup(str) char *str; { register char *p; if (p=malloc(1+strlen(str))) { strcpy(p, str); return p; } perror("xstrdup"); exit(2); /*NOTREACHED*/ } /* xstrdup */