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";
96 /* for MS-DOS (under Turbo C 1.5)*/
100 #include <sys/stat.h>
109 #define OTHERSLASH '/'
112 #define OTHERSLASH '\\'
118 static char TTY[] = "/dev/con";
119 extern unsigned _stklen = 10000;
125 /* for various flavors of UN*X */
128 #include <sys/types.h>
129 #include <sys/stat.h>
130 #include <sys/file.h>
134 typedef struct dirent DIRENTRY;
138 /* might need to be changed to <dir.h> */
142 typedef struct direct DIRENTRY;
149 #define void char /* might want to remove this line */
172 static char TTY[] = "/dev/tty";
179 extern char *strcpy(), *strchr();
186 /* for System V and BSD */
194 /* for System V and Version 7*/
199 #define utimes(f, t) utime((f), &(t))
203 # define MV_DIR "/usr/lib/mv_dir"
216 #include <sys/time.h>
221 #define mylower(c) (isupper(c) ? (c)-'A'+'a' : (c))
222 #define myupper(c) (islower(c) ? (c)-'a'+'A' : (c))
223 #define STRLEN(s) (sizeof(s) - 1)
224 #define mydup(s) (strcpy((char *)challoc(strlen(s) + 1, 0), (s)))
228 #define NORMCOPY 0x002
229 #define OVERWRITE 0x004
230 #define NORMMOVE 0x008
232 #define DIRMOVE 0x020
233 #define NORMAPPEND 0x040
234 #define ZAPPEND 0x080
235 #define HARDLINK 0x100
236 #define SYMLINK 0x200
238 #define COPY (NORMCOPY | OVERWRITE)
239 #define MOVE (NORMMOVE | XMOVE | DIRMOVE)
240 #define APPEND (NORMAPPEND | ZAPPEND)
241 #define LINK (HARDLINK | SYMLINK)
243 static char MOVENAME[] = "mmv";
244 static char COPYNAME[] = "mcp";
245 static char APPENDNAME[] = "mad";
246 static char LINKNAME[] = "mln";
261 #define MAXPATLEN MAXPATH
263 #define CHUNKSIZE 2048
266 #define FI_STTAKEN 0x01
267 #define FI_LINKERR 0x02
268 #define FI_INSTICKY 0x04
269 #define FI_NODEL 0x08
270 #define FI_KNOWWRITE 0x010
271 #define FI_CANWRITE 0x20
272 #define FI_ISDIR 0x40
273 #define FI_ISLNK 0x80
286 #define DI_KNOWWRITE 0x01
287 #define DI_CANWRITE 0x02
288 #define DI_CLEANED 0x04
299 #define H_NOREADDIR 2
310 #define R_ISALIASED 0x08
311 #define R_ISCYCLE 0x10
312 #define R_ONEDIRLINK 0x20
318 char *r_nto; /* non-path part of new name */
321 struct rep *r_thendo;
333 typedef struct chunk {
334 struct chunk *ch_next;
345 static void init(/* */);
346 static void procargs(/* int argc, char **argv,
347 char **pfrompat, char **ptopat */);
348 static void domatch(/* char *cfrom, char *cto */);
349 static int getpat(/* */);
350 static int getword(/* char *buf */);
351 static void matchpat(/* */);
352 static int parsepat(/* */);
353 static int dostage(/* char *lastend, char *pathend,
354 char **start1, int *len1, int stage, int anylev */);
355 static int trymatch(/* FILEINFO *ffrom, char *pat */);
356 static int keepmatch(/* FILEINFO *ffrom, char *pathend,
357 int *pk, int needslash, int dirs, int fils */);
358 static int badrep(/* HANDLE *hfrom, FILEINFO *ffrom,
359 HANDLE **phto, char **pnto, FILEINFO **pfdel, int *pflags */);
360 static int checkto(/* HANDLE *hfrom, char *f,
361 HANDLE **phto, char **pnto, FILEINFO **pfdel */);
362 static char *getpath(/* char *tpath */);
363 static int badname(/* char *s */);
364 static FILEINFO *fsearch(/* char *s, DIRINFO *d */);
365 static int ffirst(/* char *s, int n, DIRINFO *d */);
366 static HANDLE *checkdir(/* char *p, char *pathend, int which */);
367 static void takedir(/*
368 char *p, DIRINFO *di, int sticky
370 struct ffblk *pff, DIRINFO *di
372 static int fcmp(/* FILEINFO **pf1, FILEINFO **pf2 */);
373 static HANDLE *hadd(/* char *n */);
374 static int hsearch(/* char *n, int which, HANDLE **ph */);
375 static DIRINFO *dadd(/* DEVID v, DIRID d */);
376 static DIRINFO *dsearch(/* DEVID v, DIRID d */);
377 static int match(/* char *pat, char *s, char **start1, int *len1 */);
378 static void makerep(/* */);
379 static void checkcollisions(/* */);
380 static int rdcmp(/* REPDICT *rd1, REPDICT *rd2 */);
381 static void findorder(/* */);
382 static void scandeletes(/* int (*pkilldel)(REP *p) */);
383 static int baddel(/* REP *p */);
384 static int skipdel(/* REP *p */);
385 static void nochains(/* */);
386 static void printchain(/* REP *p */);
387 static void goonordie(/* */);
388 static void doreps(/* */);
389 static long appendalias(/* REP *first, REP *p, int *pprintaliased */);
390 static int movealias(/* REP *first, REP *p, int *pprintaliased */);
391 static int snap(/* REP *first, REP *p */);
392 static void showdone(/* REP *fin */);
393 static void breakout(/* */);
394 static void breakrep(int);
395 static void breakstat(/* */);
396 static void quit(/* */);
397 static int copymove(/* REP *p */);
398 static int copy(/* FILENFO *f, long len */);
399 static int myunlink(/* char *n, FILEINFO *f */);
400 static int getreply(/* char *m, int failact */);
401 static void *myalloc(/* unsigned k */);
402 static void *challoc(/* int k, int which */);
403 static void chgive(/* void *p, unsigned k */);
404 static int mygetc(/* */);
405 static char *mygets(/* char *s, int l */);
407 static int leave(/* */);
408 static void cleanup(/* */);
410 static int getstat(/* char *full, FILEINFO *f */);
411 static int dwritable(/* HANDLE *h */);
412 static int fwritable(/* char *hname, FILEINFO *f */);
416 static void memmove(/* void *to, void *from, int k */);
422 static int rename(/* char *from, char *to */);
425 static int op, badstyle, delstyle, verbose, noex, matchall;
428 static unsigned ndirs = 0, dirroom;
429 static DIRINFO **dirs;
430 static unsigned nhandles = 0, handleroom;
431 static HANDLE **handles;
432 static HANDLE badhandle = {"\200", NULL, 0};
433 static HANDLE *(lasthandle[2]) = {&badhandle, &badhandle};
434 static unsigned nreps = 0;
435 static REP hrep, *lastrep = &hrep;
436 static CHUNK *freechunks = NULL;
437 static SLICER slicer[2] = {{NULL, NULL, 0}, {NULL, NULL, 0}};
439 static int badreps = 0, paterr = 0, direrr, failed = 0, gotsig = 0, repbad;
440 static FILE *outfile;
442 static char IDF[] = "$$mmvdid.";
443 static char TEMP[] = "$$mmvtmp.";
444 static char TOOLONG[] = "(too long)";
445 static char EMPTY[] = "(empty)";
447 static char SLASHSTR[] = {SLASH, '\0'};
449 static char PATLONG[] = "%.40s... : pattern too long.\n";
451 char from[MAXPATLEN], to[MAXPATLEN];
452 static int fromlen, tolen;
453 static char *(stagel[MAXWILD]), *(firstwild[MAXWILD]), *(stager[MAXWILD]);
454 static int nwilds[MAXWILD];
456 char pathbuf[MAXPATH];
457 char fullrep[MAXPATH + 1];
458 static char *(start[MAXWILD]);
459 static int len[MAXWILD];
460 static char hasdot[MAXWILD];
462 #define MISTAKE (&mistake)
466 static int olddevflag, curdisk, maxdisk;
475 } patch = {"mmv 1.0 patchable flags", "mmv", XMOVE, 1, 0};
477 #define DFLTOP (patch.ph_dfltop)
478 #define CLUSTNO(pff) (*(int *)(((char *)(pff)) + patch.ph_clustoff))
479 #define DRIVENO(pff) (*(((char *)(pff)) + patch.ph_driveoff) - patch.ph_drivea)
488 static int uid, euid, oldumask;
489 static DIRID cwdd = -1;
490 static DEVID cwdv = -1;
499 char *frompat, *topat;
504 procargs(argc, argv, &frompat, &topat);
505 domatch(frompat, topat);
509 if (op & (COPY | LINK))
513 if (!(op & APPEND) && delstyle == ASKDEL)
514 scandeletes(skipdel);
516 return(failed ? 2 : nreps == 0 && (paterr || badreps));
524 maxdisk = setdisk(curdisk);
526 Read device availability : undocumented internal MS-DOS function.
527 If (_DX == 0) then \dev\ must precede device names.
532 Write device availability: undocumented internal MS-DOS function.
533 Specify \dev\ must precede device names.
536 atexit((atexit_t)cleanup);
537 ctrlbrk((int (*)())breakout);
541 if ((home = getenv("HOME")) == NULL || strcmp(home, SLASHSTR) == 0)
543 if (!stat(".", &dstat)) {
550 signal(SIGINT, breakout);
553 dirroom = handleroom = INITROOM;
554 dirs = (DIRINFO **)myalloc(dirroom * sizeof(DIRINFO *));
555 handles = (HANDLE **)myalloc(handleroom * sizeof(HANDLE *));
556 ndirs = nhandles = 0;
560 static void procargs(argc, argv, pfrompat, ptopat)
563 char **pfrompat, **ptopat;
566 char *cmdname = argv[0];
569 #define CMDNAME (patch.ph_name)
571 #define CMDNAME cmdname
575 verbose = noex = matchall = 0;
578 for (argc--, argv++; argc > 0 && **argv == '-'; argc--, argv++)
579 for (p = *argv + 1; *p != '\0'; p++) {
586 if (c == 'v' && !noex)
588 else if (c == 'n' && !verbose)
592 else if (c == 'd' && delstyle == ASKDEL)
594 else if (c == 'p' && delstyle == ASKDEL)
596 else if (c == 'g' && badstyle == ASKBAD)
598 else if (c == 't' && badstyle == ASKBAD)
600 else if (c == 'm' && op == DFLT)
602 else if (c == 'x' && op == DFLT)
604 else if (c == 'r' && op == DFLT)
606 else if (c == 'c' && op == DFLT)
608 else if (c == 'o' && op == DFLT)
610 else if (c == 'a' && op == DFLT)
613 else if (c == 'z' && op == DFLT)
616 else if (c == 'l' && op == DFLT)
619 else if (c == 's' && op == DFLT)
624 fprintf(stderr, USAGE, CMDNAME, OTHEROPT);
631 if (strcmp(cmdname, MOVENAME) == 0)
633 else if (strcmp(cmdname, COPYNAME) == 0)
635 else if (strcmp(cmdname, APPENDNAME) == 0)
637 else if (strcmp(cmdname, LINKNAME) == 0)
654 "Unable to do directory renames. Option -r refused.\n");
659 if (euid != uid && !(op & DIRMOVE)) {
665 if (badstyle != ASKBAD && delstyle == ASKDEL)
670 else if (argc == 2) {
671 *pfrompat = *(argv++);
675 fprintf(stderr, USAGE, CMDNAME, OTHEROPT);
681 static void domatch(cfrom, cto)
687 else if ((fromlen = strlen(cfrom)) >= MAXPATLEN) {
688 printf(PATLONG, cfrom);
691 else if ((tolen = strlen(cto)) >= MAXPATLEN) {
692 printf(PATLONG, cto);
706 char extra[MAXPATLEN];
710 if ((fromlen = getword(from)) == 0 || fromlen == -1)
714 if ((tolen = getword(to)) == 0) {
715 printf("%s -> ? : missing replacement pattern.\n", from);
722 (to[0] == '-' || to[0] == '=') &&
723 (to[1] == '>' || to[1] == '^')
725 if (getword(extra) == 0)
727 else if (strcmp(extra, "(*)") == 0) {
729 gotit = (getword(extra) == 0);
733 while ((c = mygetc()) != '\n' && c != EOF)
743 static int getword(buf)
752 while ((c = mygetc()) != EOF && (prevc == ESC || !isspace(c))) {
755 if (n == MAXPATLEN - 1) {
757 printf(PATLONG, buf);
765 while (c != EOF && isspace(c) && c != '\n')
773 static void matchpat()
777 else if (dostage(from, pathbuf, start, len, 0, 0)) {
778 printf("%s -> %s : no match.\n", from, to);
784 static int parsepat()
786 char *p, *lastname, c;
787 int totwilds, instage, x, havedot;
788 static char TRAILESC[] = "%s -> %s : trailing %c is superfluous.\n";
793 if (from[0] != '\0' && from[1] == ':')
796 if (from[0] == '~' && from[1] == SLASH) {
797 if ((homelen = strlen(home)) + fromlen > MAXPATLEN) {
798 printf(PATLONG, from);
801 memmove(from + homelen, from + 1, fromlen);
802 memmove(from, home, homelen);
803 lastname += homelen + 1;
806 totwilds = nstages = instage = 0;
807 for (p = lastname; (c = *p) != '\0'; p++)
818 if (!havedot && lastname != p) {
819 if (fromlen++ == MAXPATLEN) {
820 printf(PATLONG, from);
823 memmove(p + 1, p, strlen(p) + 1);
831 if (firstwild[nstages] == NULL)
832 firstwild[nstages] = p;
833 stager[nstages++] = p;
839 printf("%s -> %s : badly placed ;.\n", from, to);
847 if ((hasdot[totwilds] = (c == '!')) != 0)
850 if (totwilds++ == MAXWILD) {
851 printf("%s -> %s : too many wildcards.\n", from, to);
856 if (firstwild[nstages] == NULL)
857 firstwild[nstages] = p;
860 stagel[nstages] = lastname;
861 firstwild[nstages] = (c == ';' ? NULL : p);
867 while ((c = *(++p)) != ']') {
870 printf("%s -> %s : missing ].\n", from, to);
878 printf("%s -> %s : '%c' can not be part of [].\n",
882 if ((c = *(++p)) == '\0') {
883 printf(TRAILESC, from, to, ESC);
889 *p = c + ('a' - 'A');
895 if ((c = *(++p)) == '\0') {
896 printf(TRAILESC, from, to, ESC);
902 *p = c + ('a' - 'A');
907 if (!havedot && lastname != p) {
908 if (fromlen++ == MAXPATLEN) {
909 printf(PATLONG, from);
917 if (firstwild[nstages] == NULL)
918 firstwild[nstages] = p;
919 stager[nstages++] = p;
922 stagel[nstages] = lastname;
924 firstwild[nstages] = p;
925 stager[nstages++] = p;
931 if (to[0] != '\0' && to[1] == ':')
934 if (to[0] == '~' && to[1] == SLASH) {
935 if ((homelen = strlen(home)) + tolen > MAXPATLEN) {
939 memmove(to + homelen, to + 1, tolen);
940 memmove(to, home, homelen);
941 lastname += homelen + 1;
945 for (p = lastname; (c = *p) != '\0'; p++)
956 printf("%s -> %s : no path allowed in target under -r.\n",
961 if (!havedot && lastname != p) {
962 if (tolen++ == MAXPATLEN) {
966 memmove(p + 1, p, strlen(p) + 1);
976 if (c == 'l' || c == 'u') {
985 printf("%s -> %s : expected digit (not '%c') after #.\n",
989 for(x = 0; ;x *= 10) {
996 if (x < 1 || x > totwilds) {
997 printf("%s -> %s : wildcard #%d does not exist.\n",
1007 if ((c = *(++p)) == '\0') {
1008 printf(TRAILESC, from, to, ESC);
1014 c <= ' ' || c >= 127 ||
1015 strchr(":/\\*?[]=+;,\"|<>", c) != NULL
1017 printf("%s -> %s : illegal character '%c' (0x%02X).\n",
1022 *p = c + ('a' - 'A');
1027 if (!havedot && lastname != p) {
1028 if (tolen++ == MAXPATLEN) {
1029 printf(PATLONG, to);
1040 static int dostage(lastend, pathend, start1, len1, stage, anylev)
1041 char *lastend, *pathend;
1049 int prelen, litlen, nfils, i, k, flags, try;
1050 FILEINFO **pf, *fdel;
1051 char *nto, *firstesc;
1053 int wantdirs, ret = 1, laststage = (stage + 1 == nstages);
1055 wantdirs = !laststage ||
1056 (op & (DIRMOVE | SYMLINK)) ||
1057 (nwilds[nstages - 1] == 0);
1060 prelen = stagel[stage] - lastend;
1061 if (pathend - pathbuf + prelen >= MAXPATH) {
1062 printf("%s -> %s : search path after %s too long.\n",
1067 memmove(pathend, lastend, prelen);
1070 lastend = stagel[stage];
1073 if ((h = checkdir(pathbuf, pathend, 0)) == NULL) {
1074 if (stage == 0 || direrr == H_NOREADDIR) {
1075 printf("%s -> %s : directory %s does not %s.\n",
1076 from, to, pathbuf, direrr == H_NOREADDIR ?
1077 "allow reads/searches" : "exist");
1084 if (*lastend == ';') {
1091 nfils = di->di_nfils;
1094 if ((op & MOVE) && !dwritable(h)) {
1095 printf("%s -> %s : directory %s does not allow writes.\n",
1102 firstesc = strchr(lastend, ESC);
1103 if (firstesc == NULL || firstesc > firstwild[stage])
1104 firstesc = firstwild[stage];
1105 litlen = firstesc - lastend;
1106 pf = di->di_fils + (i = ffirst(lastend, litlen, di));
1110 (try = trymatch(*pf, lastend)) != 0 &&
1113 match(lastend + litlen, (*pf)->fi_name + litlen,
1114 start1 + anylev, len1 + anylev)
1116 keepmatch(*pf, pathend, &k, 0, wantdirs, laststage)
1119 ret &= dostage(stager[stage], pathend + k,
1120 start1 + nwilds[stage], len1 + nwilds[stage],
1125 if (badrep(h, *pf, &hto, &nto, &fdel, &flags))
1126 (*pf)->fi_rep = MISTAKE;
1128 (*pf)->fi_rep = p = (REP *)challoc(sizeof(REP), 1);
1129 p->r_flags = flags | patflags;
1138 lastrep->r_next = p;
1145 } while (i < nfils && strncmp(lastend, (*pf)->fi_name, litlen) == 0);
1149 for (pf = di->di_fils, i = 0; i < nfils; i++, pf++)
1151 *((*pf)->fi_name) != '.' &&
1153 ((*pf)->fi_attrib & FA_DIREC) &&
1155 keepmatch(*pf, pathend, &k, 1, 1, 0)
1157 *len1 = pathend - *start1 + k;
1158 ret &= dostage(lastend, pathend + k, start1, len1, stage, 1);
1165 static int trymatch(ffrom, pat)
1171 if (ffrom->fi_rep != NULL)
1177 if (*p == '.' || (!matchall && ffrom->fi_attrib & (FA_HIDDEN | FA_SYSTEM)))
1178 return(strcmp(pat, p) == 0);
1181 if (p[1] == '\0' || (p[1] == '.' && p[2] == '\0'))
1182 return(strcmp(pat, p) == 0);
1183 else if (!matchall && *pat != '.')
1190 static int keepmatch(ffrom, pathend, pk, needslash, dirs, fils)
1197 *pk = strlen(ffrom->fi_name);
1198 if (pathend - pathbuf + *pk + needslash >= MAXPATH) {
1200 printf("%s -> %s : search path %s%s too long.\n",
1201 from, to, pathbuf, ffrom->fi_name);
1205 strcpy(pathend, ffrom->fi_name);
1207 if ((ffrom->fi_attrib & FA_DIREC) ? !dirs : !fils)
1209 getstat(pathbuf, ffrom);
1210 if ((ffrom->fi_stflags & FI_ISDIR) ? !dirs : !fils)
1215 strcpy(pathend + *pk, SLASHSTR);
1222 static int badrep(hfrom, ffrom, phto, pnto, pfdel, pflags)
1230 char *f = ffrom->fi_name;
1235 (ffrom->fi_attrib & FA_DIREC) &&
1237 (ffrom->fi_stflags & FI_ISDIR) &&
1239 !(op & (DIRMOVE | SYMLINK))
1241 printf("%s -> %s : source file is a directory.\n", pathbuf, fullrep);
1244 else if ((ffrom->fi_stflags & FI_LINKERR) && !(op & (MOVE | SYMLINK)))
1245 printf("%s -> %s : source file is a badly aimed symbolic link.\n",
1249 else if ((ffrom->fi_stflags & FI_NODEL) && (op & MOVE))
1250 printf("%s -> %s : no delete permission for source file.\n",
1253 else if ((op & (COPY | APPEND)) && access(pathbuf, R_OK))
1254 printf("%s -> %s : no read permission for source file.\n",
1259 (f[1] == '\0' || strcmp(f, "..") == 0) &&
1262 printf("%s -> %s : . and .. can't be renamed.\n", pathbuf, fullrep);
1263 else if (repbad || checkto(hfrom, f, phto, pnto, pfdel) || badname(*pnto))
1264 printf("%s -> %s : bad new name.\n", pathbuf, fullrep);
1265 else if (*phto == NULL)
1266 printf("%s -> %s : %s.\n", pathbuf, fullrep,
1268 direrr == H_NOREADDIR ?
1269 "no read or search permission for target directory" :
1271 "target directory does not exist");
1273 else if (!dwritable(*phto))
1274 printf("%s -> %s : no write permission for target directory.\n",
1278 (*phto)->h_di->di_vid != hfrom->h_di->di_vid &&
1279 (*pflags = R_ISX, (op & (NORMMOVE | HARDLINK)))
1281 printf("%s -> %s : cross-device move.\n",
1285 *pflags && (op & MOVE) &&
1286 !(ffrom->fi_stflags & FI_ISLNK) &&
1287 access(pathbuf, R_OK)
1289 printf("%s -> %s : no read permission for source file.\n",
1295 ((*phto)->h_di->di_vid == cwdv && (*phto)->h_di->di_did == cwdd) ||
1296 *(hfrom->h_name) == SLASH ||
1297 (*pflags |= R_ONEDIRLINK, hfrom->h_di == (*phto)->h_di)
1300 printf("%s -> %s : symbolic link would be badly aimed.\n",
1311 static int checkto(hfrom, f, phto, pnto, pfdel)
1318 char tpath[MAXPATH + 1];
1325 hlen = strlen(hfrom->h_name);
1326 pathend = fullrep + hlen;
1327 memmove(pathend, fullrep, strlen(fullrep) + 1);
1328 memmove(fullrep, hfrom->h_name, hlen);
1329 if ((fdel = *pfdel = fsearch(pathend, hfrom->h_di)) != NULL) {
1330 *pnto = fdel->fi_name;
1332 getstat(fullrep, fdel);
1336 *pnto = mydup(pathend);
1339 pathend = getpath(tpath);
1340 hlen = pathend - fullrep;
1341 *phto = checkdir(tpath, tpath + hlen, 1);
1345 (fdel = *pfdel = fsearch(pathend, (*phto)->h_di)) != NULL &&
1347 (fdel->fi_attrib & FA_DIREC)
1349 (getstat(fullrep, fdel), fdel->fi_stflags & FI_ISDIR)
1352 tlen = strlen(pathend);
1353 strcpy(pathend + tlen, SLASHSTR);
1355 strcpy(tpath + hlen, pathend);
1358 *phto = checkdir(tpath, tpath + hlen, 1);
1361 if (*pathend == '\0') {
1363 if (pathend - fullrep + strlen(f) >= MAXPATH) {
1364 strcpy(fullrep, TOOLONG);
1368 if (*phto != NULL) {
1369 fdel = *pfdel = fsearch(f, (*phto)->h_di);
1372 getstat(fullrep, fdel);
1376 else if (fdel != NULL)
1377 *pnto = fdel->fi_name;
1379 *pnto = mydup(pathend);
1385 static char *getpath(tpath)
1388 char *pathstart, *pathend, c;
1391 if (*fullrep != '\0' && fullrep[1] == ':')
1392 pathstart = fullrep + 2;
1395 pathstart = fullrep;
1397 pathend = pathstart + strlen(pathstart) - 1;
1398 while (pathend >= pathstart && *pathend != SLASH)
1404 strcpy(tpath, fullrep);
1410 static int badname(s)
1419 (ext = strchr(s, '.')) - s >= MAXFILE ||
1420 (*ext == '.' && strchr(ext + 1, '.') != NULL) ||
1421 strlen(ext) >= MAXEXT ||
1422 strncmp(s, IDF, STRLEN(IDF)) == 0
1424 (*s == '.' && (s[1] == '\0' || strcmp(s, "..") == 0)) ||
1425 strlen(s) > MAXNAMLEN
1432 static int getstat(ffull, f)
1439 if ((flags = f->fi_stflags) & FI_STTAKEN)
1440 return(flags & FI_LINKERR);
1441 flags |= FI_STTAKEN;
1443 if (stat(ffull, &fstat)) {
1444 fprintf(stderr, "Strange, couldn't stat %s.\n", ffull);
1448 if (lstat(ffull, &fstat)) {
1449 fprintf(stderr, "Strange, couldn't lstat %s.\n", ffull);
1452 if ((flags & FI_INSTICKY) && fstat.st_uid != uid && uid != 0)
1454 if ((fstat.st_mode & S_IFMT) == S_IFLNK) {
1456 if (stat(ffull, &fstat)) {
1457 f->fi_stflags = flags | FI_LINKERR;
1462 if ((fstat.st_mode & S_IFMT) == S_IFDIR)
1464 f->fi_stflags = flags;
1465 f->fi_mode = fstat.st_mode;
1470 static int dwritable(h)
1473 char *p = h->h_name, *myp, *lastslash = NULL, *pathend;
1474 char *pw = &(h->h_di->di_flags), r;
1479 if (*pw & DI_KNOWWRITE)
1480 return(*pw & DI_CANWRITE);
1482 pathend = p + strlen(p);
1485 else if (pathend == p + 1)
1488 lastslash = pathend - 1;
1492 r = !access(myp, W_OK) ? DI_CANWRITE : 0;
1493 *pw |= DI_KNOWWRITE | r;
1495 if (lastslash != NULL)
1501 static int fwritable(hname, f)
1507 if (f->fi_stflags & FI_KNOWWRITE)
1508 return(f->fi_stflags & FI_CANWRITE);
1510 strcpy(fullrep, hname);
1511 strcat(fullrep, f->fi_name);
1512 r = !access(fullrep, W_OK) ? FI_CANWRITE : 0;
1513 f->fi_stflags |= FI_KNOWWRITE | r;
1519 static FILEINFO *fsearch(s, d)
1523 FILEINFO **fils = d->di_fils;
1524 int nfils = d->di_nfils;
1525 int first, k, last, res;
1527 for(first = 0, last = nfils - 1;;) {
1530 k = (first + last) >> 1;
1531 if ((res = strcmp(s, fils[k]->fi_name)) == 0)
1541 static int ffirst(s, n, d)
1546 int first, k, last, res;
1547 FILEINFO **fils = d->di_fils;
1548 int nfils = d->di_nfils;
1550 if (nfils == 0 || n == 0)
1555 k = (first + last) >> 1;
1556 res = strncmp(s, fils[k]->fi_name, n);
1558 return(res == 0 ? k : nfils);
1568 /* checkdir and takedir for MS-D*S */
1570 static HANDLE *checkdir(p, pathend, which)
1583 if (hsearch(p, which, &h))
1584 if (h->h_di == NULL) {
1591 if (*p == '\0' || p[1] != ':')
1595 v = mylower(p[0]) - 'a';
1596 if (v < 0 || v >= maxdisk)
1600 if (patch.ph_safeid) {
1601 strcpy(pathend, IDF);
1602 strcpy(pathend + STRLEN(IDF), "*");
1603 if (findfirst(p, &de, 0)) {
1604 if ((d = ndirs) == 1000) {
1605 fprintf(stderr, "Too many different directories.\n");
1608 sprintf(pathend + STRLEN(IDF), "%03d", d);
1609 if ((fd = _creat(p, 0)) < 0) {
1610 direrr = h->h_err = H_NODIR;
1614 strcpy(pathend, "*.*");
1615 if (findfirst(p, &de, FA_DIREC | FA_SYSTEM | FA_HIDDEN))
1616 h->h_di = dadd(v, d);
1618 takedir(&de, h->h_di = dadd(v, d));
1620 else if ((d = atoi(de.ff_name + STRLEN(IDF))) < ndirs)
1623 strcpy(pathend, de.ff_name);
1624 fprintf(stderr, "Strange dir-id file encountered: %s.\n", p);
1630 strcpy(pathend, "*.*");
1631 firstfound = !findfirst(p, &de, FA_DIREC | FA_SYSTEM | FA_HIDDEN);
1638 strcpy(pathend, "T.D");
1641 direrr = h->h_err = H_NODIR;
1644 strcpy(pathend, "*.*");
1645 firstfound = !findfirst(p, &de, FA_DIREC | FA_SYSTEM | FA_HIDDEN);
1650 if (!firstfound || d != 0) {
1652 "Strange, %s does not seem to be a root dir.\n",
1658 if ((di = dsearch(v, d)) == NULL)
1660 takedir(&de, h->h_di = dadd(v, d));
1662 h->h_di = dadd(v, d);
1671 static void takedir(pff, di)
1675 int cnt, room, namlen, needdot;
1676 FILEINFO **fils, *f;
1680 di->di_fils = fils = (FILEINFO **)myalloc(room * sizeof(FILEINFO *));
1683 if (strnicmp(pff->ff_name, IDF, STRLEN(IDF)) == 0)
1687 fils = (FILEINFO **)myalloc(room * sizeof(FILEINFO *));
1688 memcpy(fils, di->di_fils, cnt * sizeof(FILEINFO *));
1689 chgive(di->di_fils, cnt * sizeof(FILEINFO *));
1691 fils = di->di_fils + cnt;
1694 for (p = pff->ff_name, namlen = 0; (c = *p) != '\0'; p++, namlen++)
1697 *fils = f = (FILEINFO *)challoc(sizeof(FILEINFO), 1);
1698 f->fi_name = p = (char *)challoc(namlen + needdot + 1, 0);
1699 for (p1 = pff->ff_name; (c = *p1) != '\0'; p1++)
1700 *(p++) = mylower(c);
1704 f->fi_attrib = pff->ff_attrib;
1708 } while (findnext(pff) == 0);
1709 qsort(di->di_fils, cnt, sizeof(FILEINFO *), fcmp);
1714 /* checkdir, takedir for Un*x */
1716 static HANDLE *checkdir(p, pathend, which)
1723 DIRINFO **newdirs, *di;
1726 char *myp, *lastslash = NULL;
1730 if (hsearch(p, which, &h))
1731 if (h->h_di == NULL) {
1740 else if (pathend == p + 1)
1743 lastslash = pathend - 1;
1748 if (stat(myp, &dstat) || (dstat.st_mode & S_IFMT) != S_IFDIR)
1749 direrr = h->h_err = H_NODIR;
1750 else if (access(myp, R_OK | X_OK))
1751 direrr = h->h_err = H_NOREADDIR;
1754 sticky = (dstat.st_mode & S_ISVTX) && uid != 0 && uid != dstat.st_uid ?
1759 if ((di = dsearch(v, d)) == NULL)
1760 takedir(myp, di = dadd(v, d), sticky);
1763 if (lastslash != NULL)
1772 static void takedir(p, di, sticky)
1779 FILEINFO *f, **fils;
1782 if ((dirp = opendir(p)) == NULL) {
1783 fprintf(stderr, "Strange, can't scan %s.\n", p);
1787 di->di_fils = fils = (FILEINFO **)myalloc(room * sizeof(FILEINFO *));
1789 while ((dp = readdir(dirp)) != NULL) {
1792 fils = (FILEINFO **)myalloc(room * sizeof(FILEINFO *));
1793 memcpy(fils, di->di_fils, cnt * sizeof(FILEINFO *));
1794 chgive(di->di_fils, cnt * sizeof(FILEINFO *));
1796 fils = di->di_fils + cnt;
1798 *fils = f = (FILEINFO *)challoc(sizeof(FILEINFO), 1);
1799 f->fi_name = mydup(dp->d_name);
1800 f->fi_stflags = sticky;
1806 qsort(di->di_fils, cnt, sizeof(FILEINFO *), fcmp);
1810 /* end of Un*x checkdir, takedir; back to general program */
1814 static int fcmp(pf1, pf2)
1815 FILEINFO **pf1, **pf2;
1817 return(strcmp((*pf1)->fi_name, (*pf2)->fi_name));
1821 static HANDLE *hadd(n)
1824 HANDLE **newhandles, *h;
1826 if (nhandles == handleroom) {
1828 newhandles = (HANDLE **)myalloc(handleroom * sizeof(HANDLE *));
1829 memcpy(newhandles, handles, nhandles * sizeof(HANDLE *));
1830 chgive(handles, nhandles * sizeof(HANDLE *));
1831 handles = newhandles;
1833 handles[nhandles++] = h = (HANDLE *)challoc(sizeof(HANDLE), 1);
1834 h->h_name = (char *)challoc(strlen(n) + 1, 0);
1835 strcpy(h->h_name, n);
1841 static int hsearch(n, which, pret)
1849 if (strcmp(n, lasthandle[which]->h_name) == 0) {
1850 *pret = lasthandle[which];
1854 for(i = 0, ph = handles; i < nhandles; i++, ph++)
1855 if (strcmp(n, (*ph)->h_name) == 0) {
1856 lasthandle[which] = *pret = *ph;
1860 lasthandle[which] = *pret = hadd(n);
1865 static DIRINFO *dadd(v, d)
1872 if (ndirs == dirroom) {
1874 newdirs = (DIRINFO **)myalloc(dirroom * sizeof(DIRINFO *));
1875 memcpy(newdirs, dirs, ndirs * sizeof(DIRINFO *));
1876 chgive(dirs, ndirs * sizeof(DIRINFO *));
1879 dirs[ndirs++] = di = (DIRINFO *)challoc(sizeof(DIRINFO), 1);
1889 static DIRINFO *dsearch(v, d)
1896 for(i = 0, di = *dirs; i < ndirs; i++, di++)
1897 if (v == di->di_vid && d == di->di_did)
1903 static int match(pat, s, start1, len1)
1904 char *pat, *s, **start1;
1918 if ((s = strchr(s, '.')) == NULL)
1922 if ((c = *(++pat)) == '\0') {
1926 for ( ; !match(pat, s, start1 + 1, len1 + 1); (*len1)++, s++)
1933 if ((c = *(++pat)) == '\0') {
1938 for (*len1=0; !match(pat, s, start1+1, len1+1); (*len1)++, s++)
1963 int matched = 0, notin = 0, inrange = 0;
1966 if ((c = *(++pat)) == '^') {
1971 if (c == '-' && !inrange)
1978 if (*s >= prevc && *s <= c)
1988 if (inrange && *s >= prevc)
1990 if (!(matched ^ notin))
2011 static void makerep()
2018 char *p, *pat, c, pc;
2022 for (pat = to, l = 0; (c = *pat) != '\0'; pat++, l++) {
2030 else if (c == 'u') {
2037 for(x = 0; ;x *= 10) {
2045 if (l + len[x] >= MAXPATH)
2049 *(start[x]) == '.' &&
2056 if (l + STRLEN(EMPTY) >= MAXPATH)
2066 memmove(p, start[x], len[x]);
2071 for (i = len[x], q = start[x]; i > 0; i--, p++, q++)
2075 for (i = len[x], q = start[x]; i > 0; i--, p++, q++)
2093 p == fullrep ? pat != to :
2096 (pc = *(p - 1)) == SLASH
2106 if (l + STRLEN(EMPTY) >= MAXPATH)
2116 strcpy(fullrep, EMPTY);
2124 strcpy(fullrep, TOOLONG);
2128 static void checkcollisions()
2132 int i, mult, oldnreps;
2136 rd = (REPDICT *)myalloc(nreps * sizeof(REPDICT));
2138 q = &hrep, p = q->r_next, prd = rd, i = 0;
2140 q = p, p = p->r_next, prd++, i++
2143 prd->rd_dto = p->r_hto->h_di;
2144 prd->rd_nto = p->r_nto;
2147 qsort(rd, nreps, sizeof(REPDICT), rdcmp);
2149 for (i = 0, prd = rd, oldnreps = nreps; i < oldnreps; i++, prd++)
2152 prd->rd_dto == (prd + 1)->rd_dto &&
2153 strcmp(prd->rd_nto, (prd + 1)->rd_nto) == 0
2159 printf("%s%s", prd->rd_p->r_hfrom->h_name,
2160 prd->rd_p->r_ffrom->fi_name);
2161 prd->rd_p->r_flags |= R_SKIP;
2162 prd->rd_p->r_ffrom->fi_rep = MISTAKE;
2167 prd->rd_p->r_flags |= R_SKIP;
2168 prd->rd_p->r_ffrom->fi_rep = MISTAKE;
2171 printf(" , %s%s -> %s%s : collision.\n",
2172 prd->rd_p->r_hfrom->h_name, prd->rd_p->r_ffrom->fi_name,
2173 prd->rd_p->r_hto->h_name, prd->rd_nto);
2176 chgive(rd, oldnreps * sizeof(REPDICT));
2180 static int rdcmp(rd1, rd2)
2186 (ret = rd1->rd_dto - rd2->rd_dto) == 0 &&
2187 (ret = strcmp(rd1->rd_nto, rd2->rd_nto)) == 0
2189 ret = rd1->rd_i - rd2->rd_i;
2194 static void findorder()
2196 REP *p, *q, *t, *first, *pred;
2199 for (q = &hrep, p = q->r_next; p != NULL; q = p, p = p->r_next)
2200 if (p->r_flags & R_SKIP) {
2201 q->r_next = p->r_next;
2205 (fi = p->r_fdel) == NULL ||
2206 (pred = fi->fi_rep) == NULL ||
2210 else if ((first = pred->r_first) == p) {
2211 p->r_flags |= R_ISCYCLE;
2212 pred->r_flags |= R_ISALIASED;
2219 while (pred->r_thendo != NULL)
2220 pred = pred->r_thendo;
2222 for (t = p; t != NULL; t = t->r_thendo)
2224 q->r_next = p->r_next;
2230 static void nochains()
2234 for (q = &hrep, p = q->r_next; p != NULL; q = p, p = p->r_next)
2235 if (p->r_flags & R_ISCYCLE || p->r_thendo != NULL) {
2237 printf("%s%s : no chain copies allowed.\n",
2238 p->r_hto->h_name, p->r_nto);
2239 q->r_next = p->r_next;
2245 static void printchain(p)
2248 if (p->r_thendo != NULL)
2249 printchain(p->r_thendo);
2250 printf("%s%s -> ", p->r_hfrom->h_name, p->r_ffrom->fi_name);
2253 p->r_ffrom->fi_rep = MISTAKE;
2257 static void scandeletes(pkilldel)
2262 for (q = &hrep, p = q->r_next; p != NULL; q = p, p = p->r_next) {
2263 if (p->r_fdel != NULL)
2264 while ((*pkilldel)(p)) {
2266 p->r_ffrom->fi_rep = MISTAKE;
2267 if ((n = p->r_thendo) != NULL) {
2269 n->r_fdel = p->r_ffrom;
2270 n->r_next = p->r_next;
2274 q->r_next = p->r_next;
2283 static int baddel(p)
2286 HANDLE *hfrom = p->r_hfrom, *hto = p->r_hto;
2287 FILEINFO *fto = p->r_fdel;
2288 char *t = fto->fi_name, *f = p->r_ffrom->fi_name;
2289 char *hnf = hfrom->h_name, *hnt = hto->h_name;
2291 if (delstyle == NODEL && !(p->r_flags & R_DELOK) && !(op & APPEND))
2292 printf("%s%s -> %s%s : old %s%s would have to be %s.\n",
2293 hnf, f, hnt, t, hnt, t,
2294 (op & OVERWRITE) ? "overwritten" : "deleted");
2295 else if (fto->fi_rep == MISTAKE)
2296 printf("%s%s -> %s%s : old %s%s was to be done first.\n",
2297 hnf, f, hnt, t, hnt, t);
2300 fto->fi_attrib & FA_DIREC
2302 fto->fi_stflags & FI_ISDIR
2305 printf("%s%s -> %s%s : %s%s%s is a directory.\n",
2306 hnf, f, hnt, t, (op & APPEND) ? "" : "old ", hnt, t);
2308 else if ((fto->fi_stflags & FI_NODEL) && !(op & (APPEND | OVERWRITE)))
2309 printf("%s%s -> %s%s : old %s%s lacks delete permission.\n",
2310 hnf, f, hnt, t, hnt, t);
2313 (op & (APPEND | OVERWRITE)) &&
2315 fto->fi_attrib & FA_RDONLY
2317 !fwritable(hnt, fto)
2320 printf("%s%s -> %s%s : %s%s %s.\n",
2321 hnf, f, hnt, t, hnt, t,
2324 fto->fi_stflags & FI_LINKERR ?
2325 "is a badly aimed symbolic link" :
2328 "lacks write permission");
2337 static int skipdel(p)
2340 if (p->r_flags & R_DELOK)
2342 fprintf(stderr, "%s%s -> %s%s : ",
2343 p->r_hfrom->h_name, p->r_ffrom->fi_name,
2344 p->r_hto->h_name, p->r_nto);
2347 p->r_fdel->fi_attrib & FA_RDONLY
2350 !(p->r_ffrom->fi_stflags & FI_ISLNK) &&
2352 !fwritable(p->r_hto->h_name, p->r_fdel)
2355 fprintf(stderr, "old %s%s lacks write permission. delete it",
2356 p->r_hto->h_name, p->r_nto);
2358 fprintf(stderr, "%s old %s%s",
2359 (op & OVERWRITE) ? "overwrite" : "delete",
2360 p->r_hto->h_name, p->r_nto);
2361 return(!getreply("? ", -1));
2365 static void goonordie()
2367 if ((paterr || badreps) && nreps > 0) {
2368 fprintf(stderr, "Not everything specified can be done.");
2369 if (badstyle == ABORTBAD) {
2370 fprintf(stderr, " Aborting.\n");
2373 else if (badstyle == SKIPBAD)
2374 fprintf(stderr, " Proceeding with the rest.\n");
2375 else if (!getreply(" Proceed with the rest? ", -1))
2381 static void doreps()
2384 int k, printaliased = 0, alias;
2391 signal(SIGINT, breakrep);
2394 for (first = hrep.r_next, k = 0; first != NULL; first = first->r_next) {
2395 for (p = first; p != NULL; p = p->r_thendo, k++) {
2398 fprintf(stderr, "User break.\n");
2399 printaliased = snap(first, p);
2402 strcpy(fullrep, p->r_hto->h_name);
2403 strcat(fullrep, p->r_nto);
2404 if (!noex && (p->r_flags & R_ISCYCLE))
2406 aliaslen = appendalias(first, p, &printaliased);
2408 alias = movealias(first, p, &printaliased);
2409 strcpy(pathbuf, p->r_hfrom->h_name);
2410 fstart = pathbuf + strlen(pathbuf);
2411 if ((p->r_flags & R_ISALIASED) && !(op & APPEND))
2412 sprintf(fstart, "%s%03d", TEMP, alias);
2414 strcpy(fstart, p->r_ffrom->fi_name);
2416 if (p->r_fdel != NULL && !(op & (APPEND | OVERWRITE)))
2417 myunlink(fullrep, p->r_fdel);
2419 (op & (COPY | APPEND)) ?
2421 p->r_flags & R_ISALIASED ? aliaslen : -1L) :
2424 link(pathbuf, fullrep) :
2427 symlink((p->r_flags & R_ONEDIRLINK) ? fstart : pathbuf,
2431 p->r_flags & R_ISX ?
2434 rename(pathbuf, fullrep)
2437 "%s -> %s has failed.\n", pathbuf, fullrep);
2438 printaliased = snap(first, p);
2441 if (verbose || noex) {
2442 if (p->r_flags & R_ISALIASED && !printaliased)
2443 strcpy(fstart, p->r_ffrom->fi_name);
2444 fprintf(outfile, "%s %c%c %s%s%s\n",
2446 p->r_flags & R_ISALIASED ? '=' : '-',
2447 p->r_flags & R_ISCYCLE ? '^' : '>',
2449 (p->r_fdel != NULL && !(op & APPEND)) ? " (*)" : "",
2450 noex ? "" : " : done");
2456 fprintf(stderr, "Strange, did %d reps; %d were expected.\n",
2459 fprintf(stderr, "Nothing done.\n");
2463 static long appendalias(first, p, pprintaliased)
2472 if ((fd = open(fullrep, O_RDONLY | O_BINARY, 0)) < 0) {
2473 fprintf(stderr, "stat on %s has failed.\n", fullrep);
2474 *pprintaliased = snap(first, p);
2477 ret = filelength(fd);
2483 if (stat(fullrep, &fstat)) {
2484 fprintf(stderr, "append cycle stat on %s has failed.\n", fullrep);
2485 *pprintaliased = snap(first, p);
2488 ret = fstat.st_size;
2495 static int movealias(first, p, pprintaliased)
2502 strcpy(pathbuf, p->r_hto->h_name);
2503 fstart = pathbuf + strlen(pathbuf);
2504 strcpy(fstart, TEMP);
2507 sprintf(fstart + STRLEN(TEMP), "%03d", ret),
2508 fsearch(fstart, p->r_hto->h_di) != NULL;
2512 if (rename(fullrep, pathbuf)) {
2514 "%s -> %s has failed.\n", fullrep, pathbuf);
2515 *pprintaliased = snap(first, p);
2521 static int snap(first, p)
2532 ctrlbrk((int (*)())breakstat);
2534 signal(SIGINT, breakstat);
2537 badstyle == ASKBAD &&
2538 isatty(fileno(stdout)) &&
2539 getreply("Redirect standard output to file? ", 0)
2546 fprintf(stderr, "File name> "),
2547 (outfile = fopen(mygets(fname, 80), "w")) == NULL
2549 fprintf(stderr, "Can't open %s.\n", fname);
2551 if (redirected || !verbose)
2553 fprintf(outfile, "The following left undone:\n");
2559 static void showdone(fin)
2564 for (first = hrep.r_next; ; first = first->r_next)
2565 for (p = first; p != NULL; p = p->r_thendo) {
2568 fprintf(outfile, "%s%s %c%c %s%s : done%s\n",
2569 p->r_hfrom->h_name, p->r_ffrom->fi_name,
2570 p->r_flags & R_ISALIASED ? '=' : '-',
2571 p->r_flags & R_ISCYCLE ? '^' : '>',
2572 p->r_hto->h_name, p->r_nto,
2573 (p->r_fdel != NULL && !(op & APPEND)) ? " (*)" : "");
2578 static void breakout()
2581 fprintf(stderr, "Aborting, nothing done.\n");
2586 static void breakrep(int signum)
2593 static void breakstat()
2601 fprintf(stderr, "Aborting, nothing done.\n");
2606 static int copymove(p)
2613 char linkbuf[MAXPATH];
2615 if ((llen = readlink(pathbuf, linkbuf, MAXPATH - 1)) >= 0) {
2616 linkbuf[llen] = '\0';
2617 return(symlink(linkbuf, fullrep) || myunlink(pathbuf, p->r_ffrom));
2622 return(copy(p->r_ffrom, -1L) || myunlink(pathbuf, p->r_ffrom));
2627 #define IRWMASK (S_IREAD | S_IWRITE)
2628 #define RWMASK (IRWMASK | (IRWMASK >> 3) | (IRWMASK >> 6))
2630 static int copy(ff, len)
2634 char buf[BUFSIZE], c;
2635 int f, t, k, mode, perm;
2642 struct timeval tim[2];
2647 if ((f = open(pathbuf, O_RDONLY | O_BINARY, 0)) < 0)
2651 IRWMASK /* will _chmod it later (to get all the attributes) */
2653 (op & (APPEND | OVERWRITE)) ?
2654 (~oldumask & RWMASK) | (ff->fi_mode & ~RWMASK) :
2662 (((t = open(fullrep, O_RDWR)) < 0 && errno == ENOENT)
2664 t = creat(fullrep, perm);
2666 mode = O_CREAT | (op & APPEND ? 0 : O_TRUNC) |
2668 O_BINARY | (op & ZAPPEND ? O_RDWR : O_WRONLY)
2673 t = open(fullrep, mode, perm);
2680 lseek(t, (off_t)0, SEEK_END);
2682 if (op & ZAPPEND && filelength(t) != 0) {
2683 if (lseek(t, -1L, 1) == -1L || read(t, &c, 1) != 1) {
2692 if ((op & APPEND) && len != (off_t)-1) {
2695 (k = read(f, buf, (len > BUFSIZE) ? BUFSIZE : (size_t)len)) > 0 &&
2696 write(t, buf, k) == k
2703 while ((k = read(f, buf, BUFSIZE)) > 0 && write(t, buf, k) == k)
2705 if (!(op & (APPEND | OVERWRITE)))
2708 getftime(f, &tim) ||
2709 setftime(t, &tim) ||
2710 _chmod(fullrep, 1, ff->fi_attrib) == -1
2712 stat(pathbuf, &fstat) ||
2715 tim.actime = fstat.st_atime,
2716 tim.modtime = fstat.st_mtime,
2718 tim[0].tv_sec = fstat.st_atime,
2719 tim[1].tv_sec = fstat.st_mtime,
2721 utimes(fullrep, tim)
2725 fprintf(stderr, "Strange, couldn't transfer time from %s to %s.\n",
2743 static int rename(from, to)
2748 if (link(from, to) == 0 && unlink(from) == 0)
2752 if (stat(from, &s) < 0 || (s.st_mode&S_IFMT) != S_IFDIR)
2756 do pid = fork(); while (pid >= 0 && errno == EAGAIN);
2760 else if (pid == 0) {
2761 execl(MV_DIR, "mv_dir", from, to, (char *) 0);
2764 } else if (pid > 0) {
2768 do wid = wait(&status);
2769 while (wid != pid && wid >= 0);
2771 return(status == 0 ? 0 : -1);
2776 static int rename(from, to)
2790 static int myunlink(n, f)
2797 if (((a = f->fi_attrib) & FA_RDONLY) && _chmod(n, 1, a & ~FA_RDONLY) < 0) {
2798 fprintf(stderr, "Strange, can not _chmod (or unlink) %s.\n", f);
2803 fprintf(stderr, "Strange, can not unlink %s.\n", n);
2810 static int getreply(m, failact)
2814 static FILE *tty = NULL;
2818 if (tty == NULL && (tty = fopen(TTY, "r")) == NULL) {
2819 fprintf(stderr, "Can not open %s to get reply.\n", TTY);
2828 fprintf(stderr, "Can not get reply.\n");
2835 while ((c = fgetc(tty)) != '\n' && c != EOF)
2838 if (r == 'y' || r == 'n')
2840 fprintf(stderr, "Yes or No? ");
2845 static void *myalloc(k)
2852 if ((ret = (void *)malloc(k)) == NULL) {
2853 fprintf(stderr, "Insufficient memory.\n");
2860 static void *challoc(k, which)
2866 SLICER *sl = &(slicer[which]);
2868 if (k > sl->sl_len) {
2870 q = NULL, p = freechunks;
2871 p != NULL && (sl->sl_len = p->ch_len) < k;
2872 q = p, p = p->ch_next
2876 sl->sl_len = CHUNKSIZE - sizeof(CHUNK *);
2877 p = (CHUNK *)myalloc(CHUNKSIZE);
2880 freechunks = p->ch_next;
2882 q->ch_next = p->ch_next;
2883 p->ch_next = sl->sl_first;
2885 sl->sl_unused = (char *)&(p->ch_len);
2888 ret = (void *)sl->sl_unused;
2894 static void chgive(p, k)
2898 ((CHUNK *)p)->ch_len = k - sizeof(CHUNK *);
2899 ((CHUNK *)p)->ch_next = freechunks;
2900 freechunks = (CHUNK *)p;
2907 static void memmove(to, from, k)
2913 *(to++) = *(from++);
2918 *(--to) = *(--from);
2928 static int lastc = 0;
2932 return(lastc = getchar());
2936 static char *mygets(s, l)
2943 if (fgets(s, l, stdin) == NULL)
2945 if ((nl = strchr(s, '\n')) != NULL)
2947 fprintf(stderr, "Input string too long. Try again> ");
2960 static void cleanup()
2964 if (patch.ph_safeid) {
2965 for (i = 0; i < nhandles; i++) {
2966 if (!(handles[i]->h_di->di_flags & DI_CLEANED)) {
2967 sprintf(pathbuf, "%s%s%03d",
2968 handles[i]->h_name, IDF, handles[i]->h_di->di_did);
2969 if (unlink(pathbuf))
2970 fprintf(stderr, "Strange, couldn't unlink %s.\n", pathbuf);
2971 handles[i]->h_di->di_flags |= DI_CLEANED;
2976 Write device availability: undocumented internal MS-D*S function.
2977 Restore previous value.
2979 bdos(0x37, olddevflag, 3);