3 Copyright (c) 1990 Vladimir Lanin.
4 This program may be freely used and copied on a non-commercial basis.
6 Author may be reached at:
11 330 Wadsworth Ave, Apt 6F,
14 Many thanks to those who have to contributed to the design
15 and/or coding of this program:
17 Tom Albrecht: initial Sys V adaptation, consultation, and testing
18 Carl Mascott: V7 adaptation
19 Mark Lewis: -n flag idea, consultation.
20 Dave Bernhold: upper/lowercase conversion idea.
21 Paul Stodghill: copy option, argv[0] checking.
22 Frank Fiamingo: consultation and testing.
23 Tom Jordahl: bug reports and testing.
24 John Lukas, Hugh Redelmeyer, Barry Nelson, John Sauter,
25 Phil Dench, John Nelson:
30 Define SYSV to compile under System V.
31 Define both SYSV and V7 to compile under V7.
32 If your System V has a rename() call, define RENAME.
33 Otherwise, mmv will only be able to rename directories (via option -r)
34 when running as the super-user.
35 There is no reason to set the suid bit on mmv if rename() is available.
36 It is important that mmv not be run with effective uid set
37 to any value other than either the real uid or the super-user.
38 Even when running with effective uid set to super-user,
39 mmv will only perform actions permitted to the real uid.
41 Define MSDOS to compile under MS-D*S Turbo C 1.5.
42 If you prefer mmv's output to use /'s instead of \'s under MS-D*S,
45 When neither MSDOS nor SYSV are defined, compiles under BSD.
47 RENAME is automatically defined under MSDOS and BSD.
49 If you are running a (UN*X) system that provides the
50 "struct dirent" readdir() directory reading standard,
51 define DIRENT. Otherwise, mmv uses the BSD-like
52 "struct direct" readdir().
53 If your (UN*X) system has neither of these, get the "dirent"
54 by Doug Gwyn, available as gwyn-dir-lib in volume 9
55 of the comp.sources.unix archives.
62 %s [-m|x%s|c|o|a|z] [-h] [-d|p] [-g|t] [-v|n] [from to]\n\
64 Use #N in the ``to'' pattern to get the string matched\n\
65 by the N'th ``from'' pattern wildcard.\n\
66 Use -- as the end of options.\n";
68 #define OTHEROPT (_osmajor < 3 ? "" : "|r")
73 %s [-m|x|r|c|o|a|l%s] [-h] [-d|p] [-g|t] [-v|n] [from to]\n\
75 Use #[l|u]N in the ``to'' pattern to get the [lowercase|uppercase of the]\n\
76 string matched by the N'th ``from'' pattern wildcard.\n\
78 A ``from'' pattern containing wildcards should be quoted when given\n\
79 on the command line. Also you may need to quote ``to'' pattern.\n\
81 Use -- as the end of options.\n";
95 /* for MS-DOS (under Turbo C 1.5)*/
108 #define OTHERSLASH '/'
111 #define OTHERSLASH '\\'
117 static char TTY[] = "/dev/con";
118 extern unsigned _stklen = 10000;
124 /* for various flavors of UN*X */
126 #include <sys/types.h>
127 #include <sys/stat.h>
128 #include <sys/file.h>
130 extern char *getenv();
132 extern char *malloc();
136 typedef struct dirent DIRENTRY;
140 /* might need to be changed to <dir.h> */
144 typedef struct direct DIRENTRY;
151 #define void char /* might want to remove this line */
174 static char TTY[] = "/dev/tty";
181 extern char *strcpy(), *strchr();
188 /* for System V and BSD */
196 /* for System V and Version 7*/
201 #define utimes(f, t) utime((f), &(t))
205 # define MV_DIR "/usr/lib/mv_dir"
218 #include <sys/time.h>
223 #define mylower(c) (isupper(c) ? (c)-'A'+'a' : (c))
224 #define myupper(c) (islower(c) ? (c)-'a'+'A' : (c))
225 #define STRLEN(s) (sizeof(s) - 1)
226 #define mydup(s) (strcpy((char *)challoc(strlen(s) + 1, 0), (s)))
230 #define NORMCOPY 0x002
231 #define OVERWRITE 0x004
232 #define NORMMOVE 0x008
234 #define DIRMOVE 0x020
235 #define NORMAPPEND 0x040
236 #define ZAPPEND 0x080
237 #define HARDLINK 0x100
238 #define SYMLINK 0x200
240 #define COPY (NORMCOPY | OVERWRITE)
241 #define MOVE (NORMMOVE | XMOVE | DIRMOVE)
242 #define APPEND (NORMAPPEND | ZAPPEND)
243 #define LINK (HARDLINK | SYMLINK)
245 static char MOVENAME[] = "mmv";
246 static char COPYNAME[] = "mcp";
247 static char APPENDNAME[] = "mad";
248 static char LINKNAME[] = "mln";
263 #define MAXPATLEN MAXPATH
265 #define CHUNKSIZE 2048
268 #define FI_STTAKEN 0x01
269 #define FI_LINKERR 0x02
270 #define FI_INSTICKY 0x04
271 #define FI_NODEL 0x08
272 #define FI_KNOWWRITE 0x010
273 #define FI_CANWRITE 0x20
274 #define FI_ISDIR 0x40
275 #define FI_ISLNK 0x80
288 #define DI_KNOWWRITE 0x01
289 #define DI_CANWRITE 0x02
290 #define DI_CLEANED 0x04
301 #define H_NOREADDIR 2
312 #define R_ISALIASED 0x08
313 #define R_ISCYCLE 0x10
314 #define R_ONEDIRLINK 0x20
320 char *r_nto; /* non-path part of new name */
323 struct rep *r_thendo;
335 typedef struct chunk {
336 struct chunk *ch_next;
347 static void init(/* */);
348 static void procargs(/* int argc, char **argv,
349 char **pfrompat, char **ptopat */);
350 static void domatch(/* char *cfrom, char *cto */);
351 static int getpat(/* */);
352 static int getword(/* char *buf */);
353 static void matchpat(/* */);
354 static int parsepat(/* */);
355 static int dostage(/* char *lastend, char *pathend,
356 char **start1, int *len1, int stage, int anylev */);
357 static int trymatch(/* FILEINFO *ffrom, char *pat */);
358 static int keepmatch(/* FILEINFO *ffrom, char *pathend,
359 int *pk, int needslash, int dirs, int fils */);
360 static int badrep(/* HANDLE *hfrom, FILEINFO *ffrom,
361 HANDLE **phto, char **pnto, FILEINFO **pfdel, int *pflags */);
362 static int checkto(/* HANDLE *hfrom, char *f,
363 HANDLE **phto, char **pnto, FILEINFO **pfdel */);
364 static char *getpath(/* char *tpath */);
365 static int badname(/* char *s */);
366 static FILEINFO *fsearch(/* char *s, DIRINFO *d */);
367 static int ffirst(/* char *s, int n, DIRINFO *d */);
368 static HANDLE *checkdir(/* char *p, char *pathend, int which */);
369 static void takedir(/*
370 char *p, DIRINFO *di, int sticky
372 struct ffblk *pff, DIRINFO *di
374 static int fcmp(/* FILEINFO **pf1, FILEINFO **pf2 */);
375 static HANDLE *hadd(/* char *n */);
376 static int hsearch(/* char *n, int which, HANDLE **ph */);
377 static DIRINFO *dadd(/* DEVID v, DIRID d */);
378 static DIRINFO *dsearch(/* DEVID v, DIRID d */);
379 static int match(/* char *pat, char *s, char **start1, int *len1 */);
380 static void makerep(/* */);
381 static void checkcollisions(/* */);
382 static int rdcmp(/* REPDICT *rd1, REPDICT *rd2 */);
383 static void findorder(/* */);
384 static void scandeletes(/* int (*pkilldel)(REP *p) */);
385 static int baddel(/* REP *p */);
386 static int skipdel(/* REP *p */);
387 static void nochains(/* */);
388 static void printchain(/* REP *p */);
389 static void goonordie(/* */);
390 static void doreps(/* */);
391 static long appendalias(/* REP *first, REP *p, int *pprintaliased */);
392 static int movealias(/* REP *first, REP *p, int *pprintaliased */);
393 static int snap(/* REP *first, REP *p */);
394 static void showdone(/* REP *fin */);
395 static void breakout(/* */);
396 static int breakrep(/* */);
397 static void breakstat(/* */);
398 static void quit(/* */);
399 static int copymove(/* REP *p */);
400 static int copy(/* FILENFO *f, long len */);
401 static int myunlink(/* char *n, FILEINFO *f */);
402 static int getreply(/* char *m, int failact */);
403 static void *myalloc(/* unsigned k */);
404 static void *challoc(/* int k, int which */);
405 static void chgive(/* void *p, unsigned k */);
406 static int mygetc(/* */);
407 static char *mygets(/* char *s, int l */);
409 static int leave(/* */);
410 static void cleanup(/* */);
412 static int getstat(/* char *full, FILEINFO *f */);
413 static int dwritable(/* HANDLE *h */);
414 static int fwritable(/* char *hname, FILEINFO *f */);
418 static void memmove(/* void *to, void *from, int k */);
424 static int rename(/* char *from, char *to */);
427 static int op, badstyle, delstyle, verbose, noex, matchall;
430 static unsigned ndirs = 0, dirroom;
431 static DIRINFO **dirs;
432 static unsigned nhandles = 0, handleroom;
433 static HANDLE **handles;
434 static HANDLE badhandle = {"\200", NULL, 0};
435 static HANDLE *(lasthandle[2]) = {&badhandle, &badhandle};
436 static unsigned nreps = 0;
437 static REP hrep, *lastrep = &hrep;
438 static CHUNK *freechunks = NULL;
439 static SLICER slicer[2] = {{NULL, NULL, 0}, {NULL, NULL, 0}};
441 static int badreps = 0, paterr = 0, direrr, failed = 0, gotsig = 0, repbad;
442 static FILE *outfile;
444 static char IDF[] = "$$mmvdid.";
445 static char TEMP[] = "$$mmvtmp.";
446 static char TOOLONG[] = "(too long)";
447 static char EMPTY[] = "(empty)";
449 static char SLASHSTR[] = {SLASH, '\0'};
451 static char PATLONG[] = "%.40s... : pattern too long.\n";
453 char from[MAXPATLEN], to[MAXPATLEN];
454 static int fromlen, tolen;
455 static char *(stagel[MAXWILD]), *(firstwild[MAXWILD]), *(stager[MAXWILD]);
456 static int nwilds[MAXWILD];
458 char pathbuf[MAXPATH];
459 char fullrep[MAXPATH + 1];
460 static char *(start[MAXWILD]);
461 static int len[MAXWILD];
462 static char hasdot[MAXWILD];
464 #define MISTAKE (&mistake)
468 static int olddevflag, curdisk, maxdisk;
477 } patch = {"mmv 1.0 patchable flags", "mmv", XMOVE, 1, 0};
479 #define DFLTOP (patch.ph_dfltop)
480 #define CLUSTNO(pff) (*(int *)(((char *)(pff)) + patch.ph_clustoff))
481 #define DRIVENO(pff) (*(((char *)(pff)) + patch.ph_driveoff) - patch.ph_drivea)
490 static int uid, euid, oldumask;
491 static DIRID cwdd = -1;
492 static DEVID cwdv = -1;
501 char *frompat, *topat;
506 procargs(argc, argv, &frompat, &topat);
507 domatch(frompat, topat);
511 if (op & (COPY | LINK))
515 if (!(op & APPEND) && delstyle == ASKDEL)
516 scandeletes(skipdel);
518 return(failed ? 2 : nreps == 0 && (paterr || badreps));
526 maxdisk = setdisk(curdisk);
528 Read device availability : undocumented internal MS-DOS function.
529 If (_DX == 0) then \dev\ must precede device names.
534 Write device availability: undocumented internal MS-DOS function.
535 Specify \dev\ must precede device names.
538 atexit((atexit_t)cleanup);
539 ctrlbrk((int (*)())breakout);
543 if ((home = getenv("HOME")) == NULL || strcmp(home, SLASHSTR) == 0)
545 if (!stat(".", &dstat)) {
552 signal(SIGINT, breakout);
555 dirroom = handleroom = INITROOM;
556 dirs = (DIRINFO **)myalloc(dirroom * sizeof(DIRINFO *));
557 handles = (HANDLE **)myalloc(handleroom * sizeof(HANDLE *));
558 ndirs = nhandles = 0;
562 static void procargs(argc, argv, pfrompat, ptopat)
565 char **pfrompat, **ptopat;
568 char *cmdname = argv[0];
571 #define CMDNAME (patch.ph_name)
573 #define CMDNAME cmdname
577 verbose = noex = matchall = 0;
580 for (argc--, argv++; argc > 0 && **argv == '-'; argc--, argv++)
581 for (p = *argv + 1; *p != '\0'; p++) {
588 if (c == 'v' && !noex)
590 else if (c == 'n' && !verbose)
594 else if (c == 'd' && delstyle == ASKDEL)
596 else if (c == 'p' && delstyle == ASKDEL)
598 else if (c == 'g' && badstyle == ASKBAD)
600 else if (c == 't' && badstyle == ASKBAD)
602 else if (c == 'm' && op == DFLT)
604 else if (c == 'x' && op == DFLT)
606 else if (c == 'r' && op == DFLT)
608 else if (c == 'c' && op == DFLT)
610 else if (c == 'o' && op == DFLT)
612 else if (c == 'a' && op == DFLT)
615 else if (c == 'z' && op == DFLT)
618 else if (c == 'l' && op == DFLT)
621 else if (c == 's' && op == DFLT)
626 fprintf(stderr, USAGE, CMDNAME, OTHEROPT);
633 if (strcmp(cmdname, MOVENAME) == 0)
635 else if (strcmp(cmdname, COPYNAME) == 0)
637 else if (strcmp(cmdname, APPENDNAME) == 0)
639 else if (strcmp(cmdname, LINKNAME) == 0)
656 "Unable to do directory renames. Option -r refused.\n");
661 if (euid != uid && !(op & DIRMOVE)) {
667 if (badstyle != ASKBAD && delstyle == ASKDEL)
672 else if (argc == 2) {
673 *pfrompat = *(argv++);
677 fprintf(stderr, USAGE, CMDNAME, OTHEROPT);
683 static void domatch(cfrom, cto)
689 else if ((fromlen = strlen(cfrom)) >= MAXPATLEN) {
690 printf(PATLONG, cfrom);
693 else if ((tolen = strlen(cto)) >= MAXPATLEN) {
694 printf(PATLONG, cto);
708 char extra[MAXPATLEN];
712 if ((fromlen = getword(from)) == 0 || fromlen == -1)
716 if ((tolen = getword(to)) == 0) {
717 printf("%s -> ? : missing replacement pattern.\n", from);
724 (to[0] == '-' || to[0] == '=') &&
725 (to[1] == '>' || to[1] == '^')
727 if (getword(extra) == 0)
729 else if (strcmp(extra, "(*)") == 0) {
731 gotit = (getword(extra) == 0);
735 while ((c = mygetc()) != '\n' && c != EOF)
745 static int getword(buf)
754 while ((c = mygetc()) != EOF && (prevc == ESC || !isspace(c))) {
757 if (n == MAXPATLEN - 1) {
759 printf(PATLONG, buf);
767 while (c != EOF && isspace(c) && c != '\n')
775 static void matchpat()
779 else if (dostage(from, pathbuf, start, len, 0, 0)) {
780 printf("%s -> %s : no match.\n", from, to);
786 static int parsepat()
788 char *p, *lastname, c;
789 int totwilds, instage, x, havedot;
790 static char TRAILESC[] = "%s -> %s : trailing %c is superfluous.\n";
795 if (from[0] != '\0' && from[1] == ':')
798 if (from[0] == '~' && from[1] == SLASH) {
799 if ((homelen = strlen(home)) + fromlen > MAXPATLEN) {
800 printf(PATLONG, from);
803 memmove(from + homelen, from + 1, fromlen);
804 memmove(from, home, homelen);
805 lastname += homelen + 1;
808 totwilds = nstages = instage = 0;
809 for (p = lastname; (c = *p) != '\0'; p++)
820 if (!havedot && lastname != p) {
821 if (fromlen++ == MAXPATLEN) {
822 printf(PATLONG, from);
825 memmove(p + 1, p, strlen(p) + 1);
833 if (firstwild[nstages] == NULL)
834 firstwild[nstages] = p;
835 stager[nstages++] = p;
841 printf("%s -> %s : badly placed ;.\n", from, to);
849 if ((hasdot[totwilds] = (c == '!')) != 0)
852 if (totwilds++ == MAXWILD) {
853 printf("%s -> %s : too many wildcards.\n", from, to);
858 if (firstwild[nstages] == NULL)
859 firstwild[nstages] = p;
862 stagel[nstages] = lastname;
863 firstwild[nstages] = (c == ';' ? NULL : p);
869 while ((c = *(++p)) != ']') {
872 printf("%s -> %s : missing ].\n", from, to);
880 printf("%s -> %s : '%c' can not be part of [].\n",
884 if ((c = *(++p)) == '\0') {
885 printf(TRAILESC, from, to, ESC);
891 *p = c + ('a' - 'A');
897 if ((c = *(++p)) == '\0') {
898 printf(TRAILESC, from, to, ESC);
904 *p = c + ('a' - 'A');
909 if (!havedot && lastname != p) {
910 if (fromlen++ == MAXPATLEN) {
911 printf(PATLONG, from);
919 if (firstwild[nstages] == NULL)
920 firstwild[nstages] = p;
921 stager[nstages++] = p;
924 stagel[nstages] = lastname;
926 firstwild[nstages] = p;
927 stager[nstages++] = p;
933 if (to[0] != '\0' && to[1] == ':')
936 if (to[0] == '~' && to[1] == SLASH) {
937 if ((homelen = strlen(home)) + tolen > MAXPATLEN) {
941 memmove(to + homelen, to + 1, tolen);
942 memmove(to, home, homelen);
943 lastname += homelen + 1;
947 for (p = lastname; (c = *p) != '\0'; p++)
958 printf("%s -> %s : no path allowed in target under -r.\n",
963 if (!havedot && lastname != p) {
964 if (tolen++ == MAXPATLEN) {
968 memmove(p + 1, p, strlen(p) + 1);
978 if (c == 'l' || c == 'u') {
987 printf("%s -> %s : expected digit (not '%c') after #.\n",
991 for(x = 0; ;x *= 10) {
998 if (x < 1 || x > totwilds) {
999 printf("%s -> %s : wildcard #%d does not exist.\n",
1009 if ((c = *(++p)) == '\0') {
1010 printf(TRAILESC, from, to, ESC);
1016 c <= ' ' || c >= 127 ||
1017 strchr(":/\\*?[]=+;,\"|<>", c) != NULL
1019 printf("%s -> %s : illegal character '%c' (0x%02X).\n",
1024 *p = c + ('a' - 'A');
1029 if (!havedot && lastname != p) {
1030 if (tolen++ == MAXPATLEN) {
1031 printf(PATLONG, to);
1042 static int dostage(lastend, pathend, start1, len1, stage, anylev)
1043 char *lastend, *pathend;
1051 int prelen, litlen, nfils, i, k, flags, try;
1052 FILEINFO **pf, *fdel;
1053 char *nto, *firstesc;
1055 int wantdirs, ret = 1, laststage = (stage + 1 == nstages);
1057 wantdirs = !laststage ||
1058 (op & (DIRMOVE | SYMLINK)) ||
1059 (nwilds[nstages - 1] == 0);
1062 prelen = stagel[stage] - lastend;
1063 if (pathend - pathbuf + prelen >= MAXPATH) {
1064 printf("%s -> %s : search path after %s too long.\n",
1069 memmove(pathend, lastend, prelen);
1072 lastend = stagel[stage];
1075 if ((h = checkdir(pathbuf, pathend, 0)) == NULL) {
1076 if (stage == 0 || direrr == H_NOREADDIR) {
1077 printf("%s -> %s : directory %s does not %s.\n",
1078 from, to, pathbuf, direrr == H_NOREADDIR ?
1079 "allow reads/searches" : "exist");
1086 if (*lastend == ';') {
1093 nfils = di->di_nfils;
1096 if ((op & MOVE) && !dwritable(h)) {
1097 printf("%s -> %s : directory %s does not allow writes.\n",
1104 firstesc = strchr(lastend, ESC);
1105 if (firstesc == NULL || firstesc > firstwild[stage])
1106 firstesc = firstwild[stage];
1107 litlen = firstesc - lastend;
1108 pf = di->di_fils + (i = ffirst(lastend, litlen, di));
1112 (try = trymatch(*pf, lastend)) != 0 &&
1115 match(lastend + litlen, (*pf)->fi_name + litlen,
1116 start1 + anylev, len1 + anylev)
1118 keepmatch(*pf, pathend, &k, 0, wantdirs, laststage)
1121 ret &= dostage(stager[stage], pathend + k,
1122 start1 + nwilds[stage], len1 + nwilds[stage],
1127 if (badrep(h, *pf, &hto, &nto, &fdel, &flags))
1128 (*pf)->fi_rep = MISTAKE;
1130 (*pf)->fi_rep = p = (REP *)challoc(sizeof(REP), 1);
1131 p->r_flags = flags | patflags;
1140 lastrep->r_next = p;
1147 } while (i < nfils && strncmp(lastend, (*pf)->fi_name, litlen) == 0);
1151 for (pf = di->di_fils, i = 0; i < nfils; i++, pf++)
1153 *((*pf)->fi_name) != '.' &&
1155 ((*pf)->fi_attrib & FA_DIREC) &&
1157 keepmatch(*pf, pathend, &k, 1, 1, 0)
1159 *len1 = pathend - *start1 + k;
1160 ret &= dostage(lastend, pathend + k, start1, len1, stage, 1);
1167 static int trymatch(ffrom, pat)
1173 if (ffrom->fi_rep != NULL)
1179 if (*p == '.' || (!matchall && ffrom->fi_attrib & (FA_HIDDEN | FA_SYSTEM)))
1180 return(strcmp(pat, p) == 0);
1183 if (p[1] == '\0' || (p[1] == '.' && p[2] == '\0'))
1184 return(strcmp(pat, p) == 0);
1185 else if (!matchall && *pat != '.')
1192 static int keepmatch(ffrom, pathend, pk, needslash, dirs, fils)
1199 *pk = strlen(ffrom->fi_name);
1200 if (pathend - pathbuf + *pk + needslash >= MAXPATH) {
1202 printf("%s -> %s : search path %s%s too long.\n",
1203 from, to, pathbuf, ffrom->fi_name);
1207 strcpy(pathend, ffrom->fi_name);
1209 if ((ffrom->fi_attrib & FA_DIREC) ? !dirs : !fils)
1211 getstat(pathbuf, ffrom);
1212 if ((ffrom->fi_stflags & FI_ISDIR) ? !dirs : !fils)
1217 strcpy(pathend + *pk, SLASHSTR);
1224 static int badrep(hfrom, ffrom, phto, pnto, pfdel, pflags)
1232 char *f = ffrom->fi_name;
1237 (ffrom->fi_attrib & FA_DIREC) &&
1239 (ffrom->fi_stflags & FI_ISDIR) &&
1241 !(op & (DIRMOVE | SYMLINK))
1243 printf("%s -> %s : source file is a directory.\n", pathbuf, fullrep);
1246 else if ((ffrom->fi_stflags & FI_LINKERR) && !(op & (MOVE | SYMLINK)))
1247 printf("%s -> %s : source file is a badly aimed symbolic link.\n",
1251 else if ((ffrom->fi_stflags & FI_NODEL) && (op & MOVE))
1252 printf("%s -> %s : no delete permission for source file.\n",
1255 else if ((op & (COPY | APPEND)) && access(pathbuf, R_OK))
1256 printf("%s -> %s : no read permission for source file.\n",
1261 (f[1] == '\0' || strcmp(f, "..") == 0) &&
1264 printf("%s -> %s : . and .. can't be renamed.\n", pathbuf, fullrep);
1265 else if (repbad || checkto(hfrom, f, phto, pnto, pfdel) || badname(*pnto))
1266 printf("%s -> %s : bad new name.\n", pathbuf, fullrep);
1267 else if (*phto == NULL)
1268 printf("%s -> %s : %s.\n", pathbuf, fullrep,
1270 direrr == H_NOREADDIR ?
1271 "no read or search permission for target directory" :
1273 "target directory does not exist");
1275 else if (!dwritable(*phto))
1276 printf("%s -> %s : no write permission for target directory.\n",
1280 (*phto)->h_di->di_vid != hfrom->h_di->di_vid &&
1281 (*pflags = R_ISX, (op & (NORMMOVE | HARDLINK)))
1283 printf("%s -> %s : cross-device move.\n",
1287 *pflags && (op & MOVE) &&
1288 !(ffrom->fi_stflags & FI_ISLNK) &&
1289 access(pathbuf, R_OK)
1291 printf("%s -> %s : no read permission for source file.\n",
1297 ((*phto)->h_di->di_vid == cwdv && (*phto)->h_di->di_did == cwdd) ||
1298 *(hfrom->h_name) == SLASH ||
1299 (*pflags |= R_ONEDIRLINK, hfrom->h_di == (*phto)->h_di)
1302 printf("%s -> %s : symbolic link would be badly aimed.\n",
1313 static int checkto(hfrom, f, phto, pnto, pfdel)
1320 char tpath[MAXPATH + 1];
1327 hlen = strlen(hfrom->h_name);
1328 pathend = fullrep + hlen;
1329 memmove(pathend, fullrep, strlen(fullrep) + 1);
1330 memmove(fullrep, hfrom->h_name, hlen);
1331 if ((fdel = *pfdel = fsearch(pathend, hfrom->h_di)) != NULL) {
1332 *pnto = fdel->fi_name;
1334 getstat(fullrep, fdel);
1338 *pnto = mydup(pathend);
1341 pathend = getpath(tpath);
1342 hlen = pathend - fullrep;
1343 *phto = checkdir(tpath, tpath + hlen, 1);
1347 (fdel = *pfdel = fsearch(pathend, (*phto)->h_di)) != NULL &&
1349 (fdel->fi_attrib & FA_DIREC)
1351 (getstat(fullrep, fdel), fdel->fi_stflags & FI_ISDIR)
1354 tlen = strlen(pathend);
1355 strcpy(pathend + tlen, SLASHSTR);
1357 strcpy(tpath + hlen, pathend);
1360 *phto = checkdir(tpath, tpath + hlen, 1);
1363 if (*pathend == '\0') {
1365 if (pathend - fullrep + strlen(f) >= MAXPATH) {
1366 strcpy(fullrep, TOOLONG);
1370 if (*phto != NULL) {
1371 fdel = *pfdel = fsearch(f, (*phto)->h_di);
1374 getstat(fullrep, fdel);
1378 else if (fdel != NULL)
1379 *pnto = fdel->fi_name;
1381 *pnto = mydup(pathend);
1387 static char *getpath(tpath)
1390 char *pathstart, *pathend, c;
1393 if (*fullrep != '\0' && fullrep[1] == ':')
1394 pathstart = fullrep + 2;
1397 pathstart = fullrep;
1399 pathend = pathstart + strlen(pathstart) - 1;
1400 while (pathend >= pathstart && *pathend != SLASH)
1406 strcpy(tpath, fullrep);
1412 static int badname(s)
1421 (ext = strchr(s, '.')) - s >= MAXFILE ||
1422 (*ext == '.' && strchr(ext + 1, '.') != NULL) ||
1423 strlen(ext) >= MAXEXT ||
1424 strncmp(s, IDF, STRLEN(IDF)) == 0
1426 (*s == '.' && (s[1] == '\0' || strcmp(s, "..") == 0)) ||
1427 strlen(s) > MAXNAMLEN
1434 static int getstat(ffull, f)
1441 if ((flags = f->fi_stflags) & FI_STTAKEN)
1442 return(flags & FI_LINKERR);
1443 flags |= FI_STTAKEN;
1445 if (stat(ffull, &fstat)) {
1446 fprintf(stderr, "Strange, couldn't stat %s.\n", ffull);
1450 if (lstat(ffull, &fstat)) {
1451 fprintf(stderr, "Strange, couldn't lstat %s.\n", ffull);
1454 if ((flags & FI_INSTICKY) && fstat.st_uid != uid && uid != 0)
1456 if ((fstat.st_mode & S_IFMT) == S_IFLNK) {
1458 if (stat(ffull, &fstat)) {
1459 f->fi_stflags = flags | FI_LINKERR;
1464 if ((fstat.st_mode & S_IFMT) == S_IFDIR)
1466 f->fi_stflags = flags;
1467 f->fi_mode = fstat.st_mode;
1472 static int dwritable(h)
1475 char *p = h->h_name, *myp, *lastslash = NULL, *pathend;
1476 char *pw = &(h->h_di->di_flags), r;
1481 if (*pw & DI_KNOWWRITE)
1482 return(*pw & DI_CANWRITE);
1484 pathend = p + strlen(p);
1487 else if (pathend == p + 1)
1490 lastslash = pathend - 1;
1494 r = !access(myp, W_OK) ? DI_CANWRITE : 0;
1495 *pw |= DI_KNOWWRITE | r;
1497 if (lastslash != NULL)
1503 static int fwritable(hname, f)
1509 if (f->fi_stflags & FI_KNOWWRITE)
1510 return(f->fi_stflags & FI_CANWRITE);
1512 strcpy(fullrep, hname);
1513 strcat(fullrep, f->fi_name);
1514 r = !access(fullrep, W_OK) ? FI_CANWRITE : 0;
1515 f->fi_stflags |= FI_KNOWWRITE | r;
1521 static FILEINFO *fsearch(s, d)
1525 FILEINFO **fils = d->di_fils;
1526 int nfils = d->di_nfils;
1527 int first, k, last, res;
1529 for(first = 0, last = nfils - 1;;) {
1532 k = (first + last) >> 1;
1533 if ((res = strcmp(s, fils[k]->fi_name)) == 0)
1543 static int ffirst(s, n, d)
1548 int first, k, last, res;
1549 FILEINFO **fils = d->di_fils;
1550 int nfils = d->di_nfils;
1552 if (nfils == 0 || n == 0)
1557 k = (first + last) >> 1;
1558 res = strncmp(s, fils[k]->fi_name, n);
1560 return(res == 0 ? k : nfils);
1570 /* checkdir and takedir for MS-D*S */
1572 static HANDLE *checkdir(p, pathend, which)
1585 if (hsearch(p, which, &h))
1586 if (h->h_di == NULL) {
1593 if (*p == '\0' || p[1] != ':')
1597 v = mylower(p[0]) - 'a';
1598 if (v < 0 || v >= maxdisk)
1602 if (patch.ph_safeid) {
1603 strcpy(pathend, IDF);
1604 strcpy(pathend + STRLEN(IDF), "*");
1605 if (findfirst(p, &de, 0)) {
1606 if ((d = ndirs) == 1000) {
1607 fprintf(stderr, "Too many different directories.\n");
1610 sprintf(pathend + STRLEN(IDF), "%03d", d);
1611 if ((fd = _creat(p, 0)) < 0) {
1612 direrr = h->h_err = H_NODIR;
1616 strcpy(pathend, "*.*");
1617 if (findfirst(p, &de, FA_DIREC | FA_SYSTEM | FA_HIDDEN))
1618 h->h_di = dadd(v, d);
1620 takedir(&de, h->h_di = dadd(v, d));
1622 else if ((d = atoi(de.ff_name + STRLEN(IDF))) < ndirs)
1625 strcpy(pathend, de.ff_name);
1626 fprintf(stderr, "Strange dir-id file encountered: %s.\n", p);
1632 strcpy(pathend, "*.*");
1633 firstfound = !findfirst(p, &de, FA_DIREC | FA_SYSTEM | FA_HIDDEN);
1640 strcpy(pathend, "T.D");
1643 direrr = h->h_err = H_NODIR;
1646 strcpy(pathend, "*.*");
1647 firstfound = !findfirst(p, &de, FA_DIREC | FA_SYSTEM | FA_HIDDEN);
1652 if (!firstfound || d != 0) {
1654 "Strange, %s does not seem to be a root dir.\n",
1660 if ((di = dsearch(v, d)) == NULL)
1662 takedir(&de, h->h_di = dadd(v, d));
1664 h->h_di = dadd(v, d);
1673 static void takedir(pff, di)
1677 int cnt, room, namlen, needdot;
1678 FILEINFO **fils, *f;
1682 di->di_fils = fils = (FILEINFO **)myalloc(room * sizeof(FILEINFO *));
1685 if (strnicmp(pff->ff_name, IDF, STRLEN(IDF)) == 0)
1689 fils = (FILEINFO **)myalloc(room * sizeof(FILEINFO *));
1690 memcpy(fils, di->di_fils, cnt * sizeof(FILEINFO *));
1691 chgive(di->di_fils, cnt * sizeof(FILEINFO *));
1693 fils = di->di_fils + cnt;
1696 for (p = pff->ff_name, namlen = 0; (c = *p) != '\0'; p++, namlen++)
1699 *fils = f = (FILEINFO *)challoc(sizeof(FILEINFO), 1);
1700 f->fi_name = p = (char *)challoc(namlen + needdot + 1, 0);
1701 for (p1 = pff->ff_name; (c = *p1) != '\0'; p1++)
1702 *(p++) = mylower(c);
1706 f->fi_attrib = pff->ff_attrib;
1710 } while (findnext(pff) == 0);
1711 qsort(di->di_fils, cnt, sizeof(FILEINFO *), fcmp);
1716 /* checkdir, takedir for Un*x */
1718 static HANDLE *checkdir(p, pathend, which)
1725 DIRINFO **newdirs, *di;
1728 char *myp, *lastslash = NULL;
1732 if (hsearch(p, which, &h))
1733 if (h->h_di == NULL) {
1742 else if (pathend == p + 1)
1745 lastslash = pathend - 1;
1750 if (stat(myp, &dstat) || (dstat.st_mode & S_IFMT) != S_IFDIR)
1751 direrr = h->h_err = H_NODIR;
1752 else if (access(myp, R_OK | X_OK))
1753 direrr = h->h_err = H_NOREADDIR;
1756 sticky = (dstat.st_mode & S_ISVTX) && uid != 0 && uid != dstat.st_uid ?
1761 if ((di = dsearch(v, d)) == NULL)
1762 takedir(myp, di = dadd(v, d), sticky);
1765 if (lastslash != NULL)
1774 static void takedir(p, di, sticky)
1781 FILEINFO *f, **fils;
1784 if ((dirp = opendir(p)) == NULL) {
1785 fprintf(stderr, "Strange, can't scan %s.\n", p);
1789 di->di_fils = fils = (FILEINFO **)myalloc(room * sizeof(FILEINFO *));
1791 while ((dp = readdir(dirp)) != NULL) {
1794 fils = (FILEINFO **)myalloc(room * sizeof(FILEINFO *));
1795 memcpy(fils, di->di_fils, cnt * sizeof(FILEINFO *));
1796 chgive(di->di_fils, cnt * sizeof(FILEINFO *));
1798 fils = di->di_fils + cnt;
1800 *fils = f = (FILEINFO *)challoc(sizeof(FILEINFO), 1);
1801 f->fi_name = mydup(dp->d_name);
1802 f->fi_stflags = sticky;
1808 qsort(di->di_fils, cnt, sizeof(FILEINFO *), fcmp);
1812 /* end of Un*x checkdir, takedir; back to general program */
1816 static int fcmp(pf1, pf2)
1817 FILEINFO **pf1, **pf2;
1819 return(strcmp((*pf1)->fi_name, (*pf2)->fi_name));
1823 static HANDLE *hadd(n)
1826 HANDLE **newhandles, *h;
1828 if (nhandles == handleroom) {
1830 newhandles = (HANDLE **)myalloc(handleroom * sizeof(HANDLE *));
1831 memcpy(newhandles, handles, nhandles * sizeof(HANDLE *));
1832 chgive(handles, nhandles * sizeof(HANDLE *));
1833 handles = newhandles;
1835 handles[nhandles++] = h = (HANDLE *)challoc(sizeof(HANDLE), 1);
1836 h->h_name = (char *)challoc(strlen(n) + 1, 0);
1837 strcpy(h->h_name, n);
1843 static int hsearch(n, which, pret)
1851 if (strcmp(n, lasthandle[which]->h_name) == 0) {
1852 *pret = lasthandle[which];
1856 for(i = 0, ph = handles; i < nhandles; i++, ph++)
1857 if (strcmp(n, (*ph)->h_name) == 0) {
1858 lasthandle[which] = *pret = *ph;
1862 lasthandle[which] = *pret = hadd(n);
1867 static DIRINFO *dadd(v, d)
1874 if (ndirs == dirroom) {
1876 newdirs = (DIRINFO **)myalloc(dirroom * sizeof(DIRINFO *));
1877 memcpy(newdirs, dirs, ndirs * sizeof(DIRINFO *));
1878 chgive(dirs, ndirs * sizeof(DIRINFO *));
1881 dirs[ndirs++] = di = (DIRINFO *)challoc(sizeof(DIRINFO), 1);
1891 static DIRINFO *dsearch(v, d)
1898 for(i = 0, di = *dirs; i < ndirs; i++, di++)
1899 if (v == di->di_vid && d == di->di_did)
1905 static int match(pat, s, start1, len1)
1906 char *pat, *s, **start1;
1920 if ((s = strchr(s, '.')) == NULL)
1924 if ((c = *(++pat)) == '\0') {
1928 for ( ; !match(pat, s, start1 + 1, len1 + 1); (*len1)++, s++)
1935 if ((c = *(++pat)) == '\0') {
1940 for (*len1=0; !match(pat, s, start1+1, len1+1); (*len1)++, s++)
1965 int matched = 0, notin = 0, inrange = 0;
1968 if ((c = *(++pat)) == '^') {
1973 if (c == '-' && !inrange)
1980 if (*s >= prevc && *s <= c)
1990 if (inrange && *s >= prevc)
1992 if (!(matched ^ notin))
2013 static void makerep()
2020 char *p, *pat, c, pc;
2024 for (pat = to, l = 0; (c = *pat) != '\0'; pat++, l++) {
2032 else if (c == 'u') {
2039 for(x = 0; ;x *= 10) {
2047 if (l + len[x] >= MAXPATH)
2051 *(start[x]) == '.' &&
2058 if (l + STRLEN(EMPTY) >= MAXPATH)
2068 memmove(p, start[x], len[x]);
2073 for (i = len[x], q = start[x]; i > 0; i--, p++, q++)
2077 for (i = len[x], q = start[x]; i > 0; i--, p++, q++)
2095 p == fullrep ? pat != to :
2098 (pc = *(p - 1)) == SLASH
2108 if (l + STRLEN(EMPTY) >= MAXPATH)
2118 strcpy(fullrep, EMPTY);
2126 strcpy(fullrep, TOOLONG);
2130 static void checkcollisions()
2134 int i, mult, oldnreps;
2138 rd = (REPDICT *)myalloc(nreps * sizeof(REPDICT));
2140 q = &hrep, p = q->r_next, prd = rd, i = 0;
2142 q = p, p = p->r_next, prd++, i++
2145 prd->rd_dto = p->r_hto->h_di;
2146 prd->rd_nto = p->r_nto;
2149 qsort(rd, nreps, sizeof(REPDICT), rdcmp);
2151 for (i = 0, prd = rd, oldnreps = nreps; i < oldnreps; i++, prd++)
2154 prd->rd_dto == (prd + 1)->rd_dto &&
2155 strcmp(prd->rd_nto, (prd + 1)->rd_nto) == 0
2161 printf("%s%s", prd->rd_p->r_hfrom->h_name,
2162 prd->rd_p->r_ffrom->fi_name);
2163 prd->rd_p->r_flags |= R_SKIP;
2164 prd->rd_p->r_ffrom->fi_rep = MISTAKE;
2169 prd->rd_p->r_flags |= R_SKIP;
2170 prd->rd_p->r_ffrom->fi_rep = MISTAKE;
2173 printf(" , %s%s -> %s%s : collision.\n",
2174 prd->rd_p->r_hfrom->h_name, prd->rd_p->r_ffrom->fi_name,
2175 prd->rd_p->r_hto->h_name, prd->rd_nto);
2178 chgive(rd, oldnreps * sizeof(REPDICT));
2182 static int rdcmp(rd1, rd2)
2188 (ret = rd1->rd_dto - rd2->rd_dto) == 0 &&
2189 (ret = strcmp(rd1->rd_nto, rd2->rd_nto)) == 0
2191 ret = rd1->rd_i - rd2->rd_i;
2196 static void findorder()
2198 REP *p, *q, *t, *first, *pred;
2201 for (q = &hrep, p = q->r_next; p != NULL; q = p, p = p->r_next)
2202 if (p->r_flags & R_SKIP) {
2203 q->r_next = p->r_next;
2207 (fi = p->r_fdel) == NULL ||
2208 (pred = fi->fi_rep) == NULL ||
2212 else if ((first = pred->r_first) == p) {
2213 p->r_flags |= R_ISCYCLE;
2214 pred->r_flags |= R_ISALIASED;
2221 while (pred->r_thendo != NULL)
2222 pred = pred->r_thendo;
2224 for (t = p; t != NULL; t = t->r_thendo)
2226 q->r_next = p->r_next;
2232 static void nochains()
2236 for (q = &hrep, p = q->r_next; p != NULL; q = p, p = p->r_next)
2237 if (p->r_flags & R_ISCYCLE || p->r_thendo != NULL) {
2239 printf("%s%s : no chain copies allowed.\n",
2240 p->r_hto->h_name, p->r_nto);
2241 q->r_next = p->r_next;
2247 static void printchain(p)
2250 if (p->r_thendo != NULL)
2251 printchain(p->r_thendo);
2252 printf("%s%s -> ", p->r_hfrom->h_name, p->r_ffrom->fi_name);
2255 p->r_ffrom->fi_rep = MISTAKE;
2259 static void scandeletes(pkilldel)
2264 for (q = &hrep, p = q->r_next; p != NULL; q = p, p = p->r_next) {
2265 if (p->r_fdel != NULL)
2266 while ((*pkilldel)(p)) {
2268 p->r_ffrom->fi_rep = MISTAKE;
2269 if ((n = p->r_thendo) != NULL) {
2271 n->r_fdel = p->r_ffrom;
2272 n->r_next = p->r_next;
2276 q->r_next = p->r_next;
2285 static int baddel(p)
2288 HANDLE *hfrom = p->r_hfrom, *hto = p->r_hto;
2289 FILEINFO *fto = p->r_fdel;
2290 char *t = fto->fi_name, *f = p->r_ffrom->fi_name;
2291 char *hnf = hfrom->h_name, *hnt = hto->h_name;
2293 if (delstyle == NODEL && !(p->r_flags & R_DELOK) && !(op & APPEND))
2294 printf("%s%s -> %s%s : old %s%s would have to be %s.\n",
2295 hnf, f, hnt, t, hnt, t,
2296 (op & OVERWRITE) ? "overwritten" : "deleted");
2297 else if (fto->fi_rep == MISTAKE)
2298 printf("%s%s -> %s%s : old %s%s was to be done first.\n",
2299 hnf, f, hnt, t, hnt, t);
2302 fto->fi_attrib & FA_DIREC
2304 fto->fi_stflags & FI_ISDIR
2307 printf("%s%s -> %s%s : %s%s%s is a directory.\n",
2308 hnf, f, hnt, t, (op & APPEND) ? "" : "old ", hnt, t);
2310 else if ((fto->fi_stflags & FI_NODEL) && !(op & (APPEND | OVERWRITE)))
2311 printf("%s%s -> %s%s : old %s%s lacks delete permission.\n",
2312 hnf, f, hnt, t, hnt, t);
2315 (op & (APPEND | OVERWRITE)) &&
2317 fto->fi_attrib & FA_RDONLY
2319 !fwritable(hnt, fto)
2322 printf("%s%s -> %s%s : %s%s %s.\n",
2323 hnf, f, hnt, t, hnt, t,
2326 fto->fi_stflags & FI_LINKERR ?
2327 "is a badly aimed symbolic link" :
2330 "lacks write permission");
2339 static int skipdel(p)
2342 if (p->r_flags & R_DELOK)
2344 fprintf(stderr, "%s%s -> %s%s : ",
2345 p->r_hfrom->h_name, p->r_ffrom->fi_name,
2346 p->r_hto->h_name, p->r_nto);
2349 p->r_fdel->fi_attrib & FA_RDONLY
2352 !(p->r_ffrom->fi_stflags & FI_ISLNK) &&
2354 !fwritable(p->r_hto->h_name, p->r_fdel)
2357 fprintf(stderr, "old %s%s lacks write permission. delete it",
2358 p->r_hto->h_name, p->r_nto);
2360 fprintf(stderr, "%s old %s%s",
2361 (op & OVERWRITE) ? "overwrite" : "delete",
2362 p->r_hto->h_name, p->r_nto);
2363 return(!getreply("? ", -1));
2367 static void goonordie()
2369 if ((paterr || badreps) && nreps > 0) {
2370 fprintf(stderr, "Not everything specified can be done.");
2371 if (badstyle == ABORTBAD) {
2372 fprintf(stderr, " Aborting.\n");
2375 else if (badstyle == SKIPBAD)
2376 fprintf(stderr, " Proceeding with the rest.\n");
2377 else if (!getreply(" Proceed with the rest? ", -1))
2383 static void doreps()
2386 int k, printaliased = 0, alias;
2393 signal(SIGINT, breakrep);
2396 for (first = hrep.r_next, k = 0; first != NULL; first = first->r_next) {
2397 for (p = first; p != NULL; p = p->r_thendo, k++) {
2400 fprintf(stderr, "User break.\n");
2401 printaliased = snap(first, p);
2404 strcpy(fullrep, p->r_hto->h_name);
2405 strcat(fullrep, p->r_nto);
2406 if (!noex && (p->r_flags & R_ISCYCLE))
2408 aliaslen = appendalias(first, p, &printaliased);
2410 alias = movealias(first, p, &printaliased);
2411 strcpy(pathbuf, p->r_hfrom->h_name);
2412 fstart = pathbuf + strlen(pathbuf);
2413 if ((p->r_flags & R_ISALIASED) && !(op & APPEND))
2414 sprintf(fstart, "%s%03d", TEMP, alias);
2416 strcpy(fstart, p->r_ffrom->fi_name);
2418 if (p->r_fdel != NULL && !(op & (APPEND | OVERWRITE)))
2419 myunlink(fullrep, p->r_fdel);
2421 (op & (COPY | APPEND)) ?
2423 p->r_flags & R_ISALIASED ? aliaslen : -1L) :
2426 link(pathbuf, fullrep) :
2429 symlink((p->r_flags & R_ONEDIRLINK) ? fstart : pathbuf,
2433 p->r_flags & R_ISX ?
2436 rename(pathbuf, fullrep)
2439 "%s -> %s has failed.\n", pathbuf, fullrep);
2440 printaliased = snap(first, p);
2443 if (verbose || noex) {
2444 if (p->r_flags & R_ISALIASED && !printaliased)
2445 strcpy(fstart, p->r_ffrom->fi_name);
2446 fprintf(outfile, "%s %c%c %s%s%s\n",
2448 p->r_flags & R_ISALIASED ? '=' : '-',
2449 p->r_flags & R_ISCYCLE ? '^' : '>',
2451 (p->r_fdel != NULL && !(op & APPEND)) ? " (*)" : "",
2452 noex ? "" : " : done");
2458 fprintf(stderr, "Strange, did %d reps; %d were expected.\n",
2461 fprintf(stderr, "Nothing done.\n");
2465 static long appendalias(first, p, pprintaliased)
2474 if ((fd = open(fullrep, O_RDONLY | O_BINARY, 0)) < 0) {
2475 fprintf(stderr, "stat on %s has failed.\n", fullrep);
2476 *pprintaliased = snap(first, p);
2479 ret = filelength(fd);
2485 if (stat(fullrep, &fstat)) {
2486 fprintf(stderr, "append cycle stat on %s has failed.\n", fullrep);
2487 *pprintaliased = snap(first, p);
2490 ret = fstat.st_size;
2497 static int movealias(first, p, pprintaliased)
2504 strcpy(pathbuf, p->r_hto->h_name);
2505 fstart = pathbuf + strlen(pathbuf);
2506 strcpy(fstart, TEMP);
2509 sprintf(fstart + STRLEN(TEMP), "%03d", ret),
2510 fsearch(fstart, p->r_hto->h_di) != NULL;
2514 if (rename(fullrep, pathbuf)) {
2516 "%s -> %s has failed.\n", fullrep, pathbuf);
2517 *pprintaliased = snap(first, p);
2523 static int snap(first, p)
2534 ctrlbrk((int (*)())breakstat);
2536 signal(SIGINT, breakstat);
2539 badstyle == ASKBAD &&
2540 isatty(fileno(stdout)) &&
2541 getreply("Redirect standard output to file? ", 0)
2548 fprintf(stderr, "File name> "),
2549 (outfile = fopen(mygets(fname, 80), "w")) == NULL
2551 fprintf(stderr, "Can't open %s.\n", fname);
2553 if (redirected || !verbose)
2555 fprintf(outfile, "The following left undone:\n");
2561 static void showdone(fin)
2566 for (first = hrep.r_next; ; first = first->r_next)
2567 for (p = first; p != NULL; p = p->r_thendo) {
2570 fprintf(outfile, "%s%s %c%c %s%s : done%s\n",
2571 p->r_hfrom->h_name, p->r_ffrom->fi_name,
2572 p->r_flags & R_ISALIASED ? '=' : '-',
2573 p->r_flags & R_ISCYCLE ? '^' : '>',
2574 p->r_hto->h_name, p->r_nto,
2575 (p->r_fdel != NULL && !(op & APPEND)) ? " (*)" : "");
2580 static void breakout()
2583 fprintf(stderr, "Aborting, nothing done.\n");
2588 static int breakrep()
2595 static void breakstat()
2603 fprintf(stderr, "Aborting, nothing done.\n");
2608 static int copymove(p)
2615 char linkbuf[MAXPATH];
2617 if ((llen = readlink(pathbuf, linkbuf, MAXPATH - 1)) >= 0) {
2618 linkbuf[llen] = '\0';
2619 return(symlink(linkbuf, fullrep) || myunlink(pathbuf, p->r_ffrom));
2624 return(copy(p->r_ffrom, -1L) || myunlink(pathbuf, p->r_ffrom));
2629 #define IRWMASK (S_IREAD | S_IWRITE)
2630 #define RWMASK (IRWMASK | (IRWMASK >> 3) | (IRWMASK >> 6))
2632 static int copy(ff, len)
2636 char buf[BUFSIZE], c;
2637 int f, t, k, mode, perm;
2644 struct timeval tim[2];
2649 if ((f = open(pathbuf, O_RDONLY | O_BINARY, 0)) < 0)
2653 IRWMASK /* will _chmod it later (to get all the attributes) */
2655 (op & (APPEND | OVERWRITE)) ?
2656 (~oldumask & RWMASK) | (ff->fi_mode & ~RWMASK) :
2664 (((t = open(fullrep, O_RDWR)) < 0 && errno == ENOENT)
2666 t = creat(fullrep, perm);
2668 mode = O_CREAT | (op & APPEND ? 0 : O_TRUNC) |
2670 O_BINARY | (op & ZAPPEND ? O_RDWR : O_WRONLY)
2675 t = open(fullrep, mode, perm);
2682 lseek(t, (off_t)0, SEEK_END);
2684 if (op & ZAPPEND && filelength(t) != 0) {
2685 if (lseek(t, -1L, 1) == -1L || read(t, &c, 1) != 1) {
2694 if ((op & APPEND) && len != (off_t)-1) {
2697 (k = read(f, buf, (len > BUFSIZE) ? BUFSIZE : (size_t)len)) > 0 &&
2698 write(t, buf, k) == k
2705 while ((k = read(f, buf, BUFSIZE)) > 0 && write(t, buf, k) == k)
2707 if (!(op & (APPEND | OVERWRITE)))
2710 getftime(f, &tim) ||
2711 setftime(t, &tim) ||
2712 _chmod(fullrep, 1, ff->fi_attrib) == -1
2714 stat(pathbuf, &fstat) ||
2717 tim.actime = fstat.st_atime,
2718 tim.modtime = fstat.st_mtime,
2720 tim[0].tv_sec = fstat.st_atime,
2721 tim[1].tv_sec = fstat.st_mtime,
2723 utimes(fullrep, tim)
2727 fprintf(stderr, "Strange, couldn't transfer time from %s to %s.\n",
2745 static int rename(from, to)
2750 if (link(from, to) == 0 && unlink(from) == 0)
2754 if (stat(from, &s) < 0 || (s.st_mode&S_IFMT) != S_IFDIR)
2758 do pid = fork(); while (pid >= 0 && errno == EAGAIN);
2762 else if (pid == 0) {
2763 execl(MV_DIR, "mv_dir", from, to, (char *) 0);
2766 } else if (pid > 0) {
2770 do wid = wait(&status);
2771 while (wid != pid && wid >= 0);
2773 return(status == 0 ? 0 : -1);
2778 static int rename(from, to)
2792 static int myunlink(n, f)
2799 if (((a = f->fi_attrib) & FA_RDONLY) && _chmod(n, 1, a & ~FA_RDONLY) < 0) {
2800 fprintf(stderr, "Strange, can not _chmod (or unlink) %s.\n", f);
2805 fprintf(stderr, "Strange, can not unlink %s.\n", n);
2812 static int getreply(m, failact)
2816 static FILE *tty = NULL;
2820 if (tty == NULL && (tty = fopen(TTY, "r")) == NULL) {
2821 fprintf(stderr, "Can not open %s to get reply.\n", TTY);
2830 fprintf(stderr, "Can not get reply.\n");
2837 while ((c = fgetc(tty)) != '\n' && c != EOF)
2840 if (r == 'y' || r == 'n')
2842 fprintf(stderr, "Yes or No? ");
2847 static void *myalloc(k)
2854 if ((ret = (void *)malloc(k)) == NULL) {
2855 fprintf(stderr, "Insufficient memory.\n");
2862 static void *challoc(k, which)
2868 SLICER *sl = &(slicer[which]);
2870 if (k > sl->sl_len) {
2872 q = NULL, p = freechunks;
2873 p != NULL && (sl->sl_len = p->ch_len) < k;
2874 q = p, p = p->ch_next
2878 sl->sl_len = CHUNKSIZE - sizeof(CHUNK *);
2879 p = (CHUNK *)myalloc(CHUNKSIZE);
2882 freechunks = p->ch_next;
2884 q->ch_next = p->ch_next;
2885 p->ch_next = sl->sl_first;
2887 sl->sl_unused = (char *)&(p->ch_len);
2890 ret = (void *)sl->sl_unused;
2896 static void chgive(p, k)
2900 ((CHUNK *)p)->ch_len = k - sizeof(CHUNK *);
2901 ((CHUNK *)p)->ch_next = freechunks;
2902 freechunks = (CHUNK *)p;
2909 static void memmove(to, from, k)
2915 *(to++) = *(from++);
2920 *(--to) = *(--from);
2930 static int lastc = 0;
2934 return(lastc = getchar());
2938 static char *mygets(s, l)
2945 if (fgets(s, l, stdin) == NULL)
2947 if ((nl = strchr(s, '\n')) != NULL)
2949 fprintf(stderr, "Input string too long. Try again> ");
2962 static void cleanup()
2966 if (patch.ph_safeid) {
2967 for (i = 0; i < nhandles; i++) {
2968 if (!(handles[i]->h_di->di_flags & DI_CLEANED)) {
2969 sprintf(pathbuf, "%s%s%03d",
2970 handles[i]->h_name, IDF, handles[i]->h_di->di_did);
2971 if (unlink(pathbuf))
2972 fprintf(stderr, "Strange, couldn't unlink %s.\n", pathbuf);
2973 handles[i]->h_di->di_flags |= DI_CLEANED;
2978 Write device availability: undocumented internal MS-D*S function.
2979 Restore previous value.
2981 bdos(0x37, olddevflag, 3);