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;
443 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];
463 #define MISTAKE (&mistake)
467 static char hasdot[MAXWILD];
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)
658 "Unable to do directory renames. Option -r refused.\n");
663 if (euid != uid && !(op & DIRMOVE)) {
669 if (badstyle != ASKBAD && delstyle == ASKDEL)
674 else if (argc == 2) {
675 *pfrompat = *(argv++);
679 fprintf(stderr, USAGE, CMDNAME, OTHEROPT);
685 static void domatch(cfrom, cto)
691 else if ((fromlen = strlen(cfrom)) >= MAXPATLEN) {
692 printf(PATLONG, cfrom);
695 else if ((tolen = strlen(cto)) >= MAXPATLEN) {
696 printf(PATLONG, cto);
710 char extra[MAXPATLEN];
714 if ((fromlen = getword(from)) == 0 || fromlen == -1)
718 if ((tolen = getword(to)) == 0) {
719 printf("%s -> ? : missing replacement pattern.\n", from);
726 (to[0] == '-' || to[0] == '=') &&
727 (to[1] == '>' || to[1] == '^')
729 if (getword(extra) == 0)
731 else if (strcmp(extra, "(*)") == 0) {
733 gotit = (getword(extra) == 0);
737 while ((c = mygetc()) != '\n' && c != EOF)
747 static int getword(buf)
756 while ((c = mygetc()) != EOF && (prevc == ESC || !isspace(c))) {
759 if (n == MAXPATLEN - 1) {
761 printf(PATLONG, buf);
769 while (c != EOF && isspace(c) && c != '\n')
777 static void matchpat()
781 else if (dostage(from, pathbuf, start, len, 0, 0)) {
782 printf("%s -> %s : no match.\n", from, to);
788 static int parsepat()
790 char *p, *lastname, c;
791 int totwilds, instage, x;
792 static char TRAILESC[] = "%s -> %s : trailing %c is superfluous.\n";
797 if (from[0] != '\0' && from[1] == ':')
800 if (from[0] == '~' && from[1] == SLASH) {
801 if ((homelen = strlen(home)) + fromlen > MAXPATLEN) {
802 printf(PATLONG, from);
805 memmove(from + homelen, from + 1, fromlen);
806 memmove(from, home, homelen);
807 lastname += homelen + 1;
810 totwilds = nstages = instage = 0;
811 for (p = lastname; (c = *p) != '\0'; p++)
822 if (!havedot && lastname != p) {
823 if (fromlen++ == MAXPATLEN) {
824 printf(PATLONG, from);
827 memmove(p + 1, p, strlen(p) + 1);
835 if (firstwild[nstages] == NULL)
836 firstwild[nstages] = p;
837 stager[nstages++] = p;
843 printf("%s -> %s : badly placed ;.\n", from, to);
851 if ((hasdot[totwilds] = (c == '!')) != 0)
854 if (totwilds++ == MAXWILD) {
855 printf("%s -> %s : too many wildcards.\n", from, to);
860 if (firstwild[nstages] == NULL)
861 firstwild[nstages] = p;
864 stagel[nstages] = lastname;
865 firstwild[nstages] = (c == ';' ? NULL : p);
871 while ((c = *(++p)) != ']') {
874 printf("%s -> %s : missing ].\n", from, to);
882 printf("%s -> %s : '%c' can not be part of [].\n",
886 if ((c = *(++p)) == '\0') {
887 printf(TRAILESC, from, to, ESC);
893 *p = c + ('a' - 'A');
899 if ((c = *(++p)) == '\0') {
900 printf(TRAILESC, from, to, ESC);
906 *p = c + ('a' - 'A');
911 if (!havedot && lastname != p) {
912 if (fromlen++ == MAXPATLEN) {
913 printf(PATLONG, from);
921 if (firstwild[nstages] == NULL)
922 firstwild[nstages] = p;
923 stager[nstages++] = p;
926 stagel[nstages] = lastname;
928 firstwild[nstages] = p;
929 stager[nstages++] = p;
935 if (to[0] != '\0' && to[1] == ':')
938 if (to[0] == '~' && to[1] == SLASH) {
939 if ((homelen = strlen(home)) + tolen > MAXPATLEN) {
943 memmove(to + homelen, to + 1, tolen);
944 memmove(to, home, homelen);
945 lastname += homelen + 1;
949 for (p = lastname; (c = *p) != '\0'; p++)
960 printf("%s -> %s : no path allowed in target under -r.\n",
965 if (!havedot && lastname != p) {
966 if (tolen++ == MAXPATLEN) {
970 memmove(p + 1, p, strlen(p) + 1);
980 if (c == 'l' || c == 'u') {
989 printf("%s -> %s : expected digit (not '%c') after #.\n",
993 for(x = 0; ;x *= 10) {
1000 if (x < 1 || x > totwilds) {
1001 printf("%s -> %s : wildcard #%d does not exist.\n",
1011 if ((c = *(++p)) == '\0') {
1012 printf(TRAILESC, from, to, ESC);
1018 c <= ' ' || c >= 127 ||
1019 strchr(":/\\*?[]=+;,\"|<>", c) != NULL
1021 printf("%s -> %s : illegal character '%c' (0x%02X).\n",
1026 *p = c + ('a' - 'A');
1031 if (!havedot && lastname != p) {
1032 if (tolen++ == MAXPATLEN) {
1033 printf(PATLONG, to);
1044 static int dostage(lastend, pathend, start1, len1, stage, anylev)
1045 char *lastend, *pathend;
1053 int prelen, litlen, nfils, i, k, flags, try;
1054 FILEINFO **pf, *fdel = NULL;
1055 char *nto, *firstesc;
1057 int wantdirs, ret = 1, laststage = (stage + 1 == nstages);
1059 wantdirs = !laststage ||
1060 (op & (DIRMOVE | SYMLINK)) ||
1061 (nwilds[nstages - 1] == 0);
1064 prelen = stagel[stage] - lastend;
1065 if (pathend - pathbuf + prelen >= MAXPATH) {
1066 printf("%s -> %s : search path after %s too long.\n",
1071 memmove(pathend, lastend, prelen);
1074 lastend = stagel[stage];
1077 if ((h = checkdir(pathbuf, pathend, 0)) == NULL) {
1078 if (stage == 0 || direrr == H_NOREADDIR) {
1079 printf("%s -> %s : directory %s does not %s.\n",
1080 from, to, pathbuf, direrr == H_NOREADDIR ?
1081 "allow reads/searches" : "exist");
1088 if (*lastend == ';') {
1095 nfils = di->di_nfils;
1098 if ((op & MOVE) && !dwritable(h)) {
1099 printf("%s -> %s : directory %s does not allow writes.\n",
1106 firstesc = strchr(lastend, ESC);
1107 if (firstesc == NULL || firstesc > firstwild[stage])
1108 firstesc = firstwild[stage];
1109 litlen = firstesc - lastend;
1110 pf = di->di_fils + (i = ffirst(lastend, litlen, di));
1114 (try = trymatch(*pf, lastend)) != 0 &&
1117 match(lastend + litlen, (*pf)->fi_name + litlen,
1118 start1 + anylev, len1 + anylev)
1120 keepmatch(*pf, pathend, &k, 0, wantdirs, laststage)
1123 ret &= dostage(stager[stage], pathend + k,
1124 start1 + nwilds[stage], len1 + nwilds[stage],
1129 if (badrep(h, *pf, &hto, &nto, &fdel, &flags))
1130 (*pf)->fi_rep = MISTAKE;
1132 (*pf)->fi_rep = p = (REP *)challoc(sizeof(REP), 1);
1133 p->r_flags = flags | patflags;
1142 lastrep->r_next = p;
1149 } while (i < nfils && strncmp(lastend, (*pf)->fi_name, litlen) == 0);
1153 for (pf = di->di_fils, i = 0; i < nfils; i++, pf++)
1155 *((*pf)->fi_name) != '.' &&
1157 ((*pf)->fi_attrib & FA_DIREC) &&
1159 keepmatch(*pf, pathend, &k, 1, 1, 0)
1161 *len1 = pathend - *start1 + k;
1162 ret &= dostage(lastend, pathend + k, start1, len1, stage, 1);
1169 static int trymatch(ffrom, pat)
1175 if (ffrom->fi_rep != NULL)
1181 if (*p == '.' || (!matchall && ffrom->fi_attrib & (FA_HIDDEN | FA_SYSTEM)))
1182 return(strcmp(pat, p) == 0);
1185 if (p[1] == '\0' || (p[1] == '.' && p[2] == '\0'))
1186 return(strcmp(pat, p) == 0);
1187 else if (!matchall && *pat != '.')
1195 static int keepmatch(ffrom, pathend, pk, needslash, dirs, fils)
1202 *pk = strlen(ffrom->fi_name);
1203 if (pathend - pathbuf + *pk + needslash >= MAXPATH) {
1205 printf("%s -> %s : search path %s%s too long.\n",
1206 from, to, pathbuf, ffrom->fi_name);
1210 strcpy(pathend, ffrom->fi_name);
1212 if ((ffrom->fi_attrib & FA_DIREC) ? !dirs : !fils)
1214 getstat(pathbuf, ffrom);
1215 if ((ffrom->fi_stflags & FI_ISDIR) ? !dirs : !fils)
1220 strcpy(pathend + *pk, SLASHSTR);
1227 static int badrep(hfrom, ffrom, phto, pnto, pfdel, pflags)
1235 char *f = ffrom->fi_name;
1240 (ffrom->fi_attrib & FA_DIREC) &&
1242 (ffrom->fi_stflags & FI_ISDIR) &&
1244 !(op & (DIRMOVE | SYMLINK))
1246 printf("%s -> %s : source file is a directory.\n", pathbuf, fullrep);
1249 else if ((ffrom->fi_stflags & FI_LINKERR) && !(op & (MOVE | SYMLINK)))
1250 printf("%s -> %s : source file is a badly aimed symbolic link.\n",
1254 else if ((ffrom->fi_stflags & FI_NODEL) && (op & MOVE))
1255 printf("%s -> %s : no delete permission for source file.\n",
1258 else if ((op & (COPY | APPEND)) && access(pathbuf, R_OK))
1259 printf("%s -> %s : no read permission for source file.\n",
1264 (f[1] == '\0' || strcmp(f, "..") == 0) &&
1267 printf("%s -> %s : . and .. can't be renamed.\n", pathbuf, fullrep);
1268 else if (repbad || checkto(hfrom, f, phto, pnto, pfdel) || badname(*pnto))
1269 printf("%s -> %s : bad new name.\n", pathbuf, fullrep);
1270 else if (*phto == NULL)
1271 printf("%s -> %s : %s.\n", pathbuf, fullrep,
1273 direrr == H_NOREADDIR ?
1274 "no read or search permission for target directory" :
1276 "target directory does not exist");
1278 else if (!dwritable(*phto))
1279 printf("%s -> %s : no write permission for target directory.\n",
1283 (*phto)->h_di->di_vid != hfrom->h_di->di_vid &&
1284 (*pflags = R_ISX, (op & (NORMMOVE | HARDLINK)))
1286 printf("%s -> %s : cross-device move.\n",
1290 *pflags && (op & MOVE) &&
1291 !(ffrom->fi_stflags & FI_ISLNK) &&
1292 access(pathbuf, R_OK)
1294 printf("%s -> %s : no read permission for source file.\n",
1300 ((*phto)->h_di->di_vid == cwdv && (*phto)->h_di->di_did == cwdd) ||
1301 *(hfrom->h_name) == SLASH ||
1302 (*pflags |= R_ONEDIRLINK, hfrom->h_di == (*phto)->h_di)
1305 printf("%s -> %s : symbolic link would be badly aimed.\n",
1316 static int checkto(hfrom, f, phto, pnto, pfdel)
1323 char tpath[MAXPATH + 1];
1325 FILEINFO *fdel = NULL;
1330 hlen = strlen(hfrom->h_name);
1331 pathend = fullrep + hlen;
1332 memmove(pathend, fullrep, strlen(fullrep) + 1);
1333 memmove(fullrep, hfrom->h_name, hlen);
1334 if ((fdel = *pfdel = fsearch(pathend, hfrom->h_di)) != NULL) {
1335 *pnto = fdel->fi_name;
1337 getstat(fullrep, fdel);
1341 *pnto = mydup(pathend);
1344 pathend = getpath(tpath);
1345 hlen = pathend - fullrep;
1346 *phto = checkdir(tpath, tpath + hlen, 1);
1350 (fdel = *pfdel = fsearch(pathend, (*phto)->h_di)) != NULL &&
1352 (fdel->fi_attrib & FA_DIREC)
1354 (getstat(fullrep, fdel), fdel->fi_stflags & FI_ISDIR)
1357 tlen = strlen(pathend);
1358 strcpy(pathend + tlen, SLASHSTR);
1360 strcpy(tpath + hlen, pathend);
1363 *phto = checkdir(tpath, tpath + hlen, 1);
1366 if (*pathend == '\0') {
1368 if (pathend - fullrep + strlen(f) >= MAXPATH) {
1369 strcpy(fullrep, TOOLONG);
1373 if (*phto != NULL) {
1374 fdel = *pfdel = fsearch(f, (*phto)->h_di);
1377 getstat(fullrep, fdel);
1381 else if (fdel != NULL)
1382 *pnto = fdel->fi_name;
1384 *pnto = mydup(pathend);
1390 static char *getpath(tpath)
1393 char *pathstart, *pathend, c;
1396 if (*fullrep != '\0' && fullrep[1] == ':')
1397 pathstart = fullrep + 2;
1400 pathstart = fullrep;
1402 pathend = pathstart + strlen(pathstart) - 1;
1403 while (pathend >= pathstart && *pathend != SLASH)
1409 strcpy(tpath, fullrep);
1415 static int badname(s)
1426 (ext = strchr(s, '.')) - s >= MAXFILE ||
1427 (*ext == '.' && strchr(ext + 1, '.') != NULL) ||
1428 strlen(ext) >= MAXEXT ||
1429 strncmp(s, IDF, STRLEN(IDF)) == 0
1431 (*s == '.' && (s[1] == '\0' || strcmp(s, "..") == 0)) ||
1432 strlen(s) > MAXNAMLEN
1439 static int getstat(ffull, f)
1446 if ((flags = f->fi_stflags) & FI_STTAKEN)
1447 return(flags & FI_LINKERR);
1448 flags |= FI_STTAKEN;
1450 if (stat(ffull, &fstat)) {
1451 fprintf(stderr, "Strange, couldn't stat %s.\n", ffull);
1455 if (lstat(ffull, &fstat)) {
1456 fprintf(stderr, "Strange, couldn't lstat %s.\n", ffull);
1459 if ((flags & FI_INSTICKY) && fstat.st_uid != uid && uid != 0)
1461 if ((fstat.st_mode & S_IFMT) == S_IFLNK) {
1463 if (stat(ffull, &fstat)) {
1464 f->fi_stflags = flags | FI_LINKERR;
1469 if ((fstat.st_mode & S_IFMT) == S_IFDIR)
1471 f->fi_stflags = flags;
1472 f->fi_mode = fstat.st_mode;
1477 static int dwritable(h)
1480 char *p = h->h_name, *myp, *lastslash = NULL, *pathend;
1481 char *pw = &(h->h_di->di_flags), r;
1486 if (*pw & DI_KNOWWRITE)
1487 return(*pw & DI_CANWRITE);
1489 pathend = p + strlen(p);
1492 else if (pathend == p + 1)
1495 lastslash = pathend - 1;
1499 r = !access(myp, W_OK) ? DI_CANWRITE : 0;
1500 *pw |= DI_KNOWWRITE | r;
1502 if (lastslash != NULL)
1508 static int fwritable(hname, f)
1514 if (f->fi_stflags & FI_KNOWWRITE)
1515 return(f->fi_stflags & FI_CANWRITE);
1517 strcpy(fullrep, hname);
1518 strcat(fullrep, f->fi_name);
1519 r = !access(fullrep, W_OK) ? FI_CANWRITE : 0;
1520 f->fi_stflags |= FI_KNOWWRITE | r;
1526 static FILEINFO *fsearch(s, d)
1530 FILEINFO **fils = d->di_fils;
1531 int nfils = d->di_nfils;
1532 int first, k, last, res;
1534 for(first = 0, last = nfils - 1;;) {
1537 k = (first + last) >> 1;
1538 if ((res = strcmp(s, fils[k]->fi_name)) == 0)
1548 static int ffirst(s, n, d)
1553 int first, k, last, res;
1554 FILEINFO **fils = d->di_fils;
1555 int nfils = d->di_nfils;
1557 if (nfils == 0 || n == 0)
1562 k = (first + last) >> 1;
1563 res = strncmp(s, fils[k]->fi_name, n);
1565 return(res == 0 ? k : nfils);
1575 /* checkdir and takedir for MS-D*S */
1577 static HANDLE *checkdir(p, pathend, which)
1590 if (hsearch(p, which, &h))
1591 if (h->h_di == NULL) {
1598 if (*p == '\0' || p[1] != ':')
1602 v = mylower(p[0]) - 'a';
1603 if (v < 0 || v >= maxdisk)
1607 if (patch.ph_safeid) {
1608 strcpy(pathend, IDF);
1609 strcpy(pathend + STRLEN(IDF), "*");
1610 if (findfirst(p, &de, 0)) {
1611 if ((d = ndirs) == 1000) {
1612 fprintf(stderr, "Too many different directories.\n");
1615 sprintf(pathend + STRLEN(IDF), "%03d", d);
1616 if ((fd = _creat(p, 0)) < 0) {
1617 direrr = h->h_err = H_NODIR;
1621 strcpy(pathend, "*.*");
1622 if (findfirst(p, &de, FA_DIREC | FA_SYSTEM | FA_HIDDEN))
1623 h->h_di = dadd(v, d);
1625 takedir(&de, h->h_di = dadd(v, d));
1627 else if ((d = atoi(de.ff_name + STRLEN(IDF))) < ndirs)
1630 strcpy(pathend, de.ff_name);
1631 fprintf(stderr, "Strange dir-id file encountered: %s.\n", p);
1637 strcpy(pathend, "*.*");
1638 firstfound = !findfirst(p, &de, FA_DIREC | FA_SYSTEM | FA_HIDDEN);
1645 strcpy(pathend, "T.D");
1648 direrr = h->h_err = H_NODIR;
1651 strcpy(pathend, "*.*");
1652 firstfound = !findfirst(p, &de, FA_DIREC | FA_SYSTEM | FA_HIDDEN);
1657 if (!firstfound || d != 0) {
1659 "Strange, %s does not seem to be a root dir.\n",
1665 if ((di = dsearch(v, d)) == NULL)
1667 takedir(&de, h->h_di = dadd(v, d));
1669 h->h_di = dadd(v, d);
1678 static void takedir(pff, di)
1682 int cnt, room, namlen, needdot;
1683 FILEINFO **fils, *f;
1687 di->di_fils = fils = (FILEINFO **)myalloc(room * sizeof(FILEINFO *));
1690 if (strnicmp(pff->ff_name, IDF, STRLEN(IDF)) == 0)
1694 fils = (FILEINFO **)myalloc(room * sizeof(FILEINFO *));
1695 memcpy(fils, di->di_fils, cnt * sizeof(FILEINFO *));
1696 chgive(di->di_fils, cnt * sizeof(FILEINFO *));
1698 fils = di->di_fils + cnt;
1701 for (p = pff->ff_name, namlen = 0; (c = *p) != '\0'; p++, namlen++)
1704 *fils = f = (FILEINFO *)challoc(sizeof(FILEINFO), 1);
1705 f->fi_name = p = (char *)challoc(namlen + needdot + 1, 0);
1706 for (p1 = pff->ff_name; (c = *p1) != '\0'; p1++)
1707 *(p++) = mylower(c);
1711 f->fi_attrib = pff->ff_attrib;
1715 } while (findnext(pff) == 0);
1716 qsort(di->di_fils, cnt, sizeof(FILEINFO *), fcmp);
1721 /* checkdir, takedir for Un*x */
1723 static HANDLE *checkdir(p, pathend, which)
1731 char *myp, *lastslash = NULL;
1735 if (hsearch(p, which, &h)) {
1736 if (h->h_di == NULL) {
1746 else if (pathend == p + 1)
1749 lastslash = pathend - 1;
1754 if (stat(myp, &dstat) || (dstat.st_mode & S_IFMT) != S_IFDIR)
1755 direrr = h->h_err = H_NODIR;
1756 else if (access(myp, R_OK | X_OK))
1757 direrr = h->h_err = H_NOREADDIR;
1760 sticky = (dstat.st_mode & S_ISVTX) && uid != 0 && uid != dstat.st_uid ?
1765 if ((di = dsearch(v, d)) == NULL)
1766 takedir(myp, di = dadd(v, d), sticky);
1769 if (lastslash != NULL)
1778 static void takedir(p, di, sticky)
1785 FILEINFO *f, **fils;
1788 if ((dirp = opendir(p)) == NULL) {
1789 fprintf(stderr, "Strange, can't scan %s.\n", p);
1793 di->di_fils = fils = (FILEINFO **)myalloc(room * sizeof(FILEINFO *));
1795 while ((dp = readdir(dirp)) != NULL) {
1798 fils = (FILEINFO **)myalloc(room * sizeof(FILEINFO *));
1799 memcpy(fils, di->di_fils, cnt * sizeof(FILEINFO *));
1800 chgive(di->di_fils, cnt * sizeof(FILEINFO *));
1802 fils = di->di_fils + cnt;
1804 *fils = f = (FILEINFO *)challoc(sizeof(FILEINFO), 1);
1805 f->fi_name = mydup(dp->d_name);
1806 f->fi_stflags = sticky;
1812 qsort(di->di_fils, cnt, sizeof(FILEINFO *), fcmp);
1816 /* end of Un*x checkdir, takedir; back to general program */
1820 static int fcmp(pf1, pf2)
1821 FILEINFO **pf1, **pf2;
1823 return(strcmp((*pf1)->fi_name, (*pf2)->fi_name));
1827 static HANDLE *hadd(n)
1830 HANDLE **newhandles, *h;
1832 if (nhandles == handleroom) {
1834 newhandles = (HANDLE **)myalloc(handleroom * sizeof(HANDLE *));
1835 memcpy(newhandles, handles, nhandles * sizeof(HANDLE *));
1836 chgive(handles, nhandles * sizeof(HANDLE *));
1837 handles = newhandles;
1839 handles[nhandles++] = h = (HANDLE *)challoc(sizeof(HANDLE), 1);
1840 h->h_name = (char *)challoc(strlen(n) + 1, 0);
1841 strcpy(h->h_name, n);
1847 static int hsearch(n, which, pret)
1855 if (strcmp(n, lasthandle[which]->h_name) == 0) {
1856 *pret = lasthandle[which];
1860 for(i = 0, ph = handles; i < nhandles; i++, ph++)
1861 if (strcmp(n, (*ph)->h_name) == 0) {
1862 lasthandle[which] = *pret = *ph;
1866 lasthandle[which] = *pret = hadd(n);
1871 static DIRINFO *dadd(v, d)
1878 if (ndirs == dirroom) {
1880 newdirs = (DIRINFO **)myalloc(dirroom * sizeof(DIRINFO *));
1881 memcpy(newdirs, dirs, ndirs * sizeof(DIRINFO *));
1882 chgive(dirs, ndirs * sizeof(DIRINFO *));
1885 dirs[ndirs++] = di = (DIRINFO *)challoc(sizeof(DIRINFO), 1);
1895 static DIRINFO *dsearch(v, d)
1902 for(i = 0, di = *dirs; i < ndirs; i++, di++)
1903 if (v == di->di_vid && d == di->di_did)
1909 static int match(pat, s, start1, len1)
1910 char *pat, *s, **start1;
1927 if ((s = strchr(s, '.')) == NULL)
1931 if ((c = *(++pat)) == '\0') {
1935 for ( ; !match(pat, s, start1 + 1, len1 + 1); (*len1)++, s++)
1942 if ((c = *(++pat)) == '\0') {
1947 for (*len1=0; !match(pat, s, start1+1, len1+1); (*len1)++, s++)
1972 int matched = 0, notin = 0, inrange = 0;
1975 if ((c = *(++pat)) == '^') {
1980 if (c == '-' && !inrange)
1987 if (*s >= prevc && *s <= c)
1997 if (inrange && *s >= prevc)
1999 if (!(matched ^ notin))
2020 static void makerep()
2027 char *p, *pat, c, pc;
2031 for (pat = to, l = 0; (c = *pat) != '\0'; pat++, l++) {
2039 else if (c == 'u') {
2046 for(x = 0; ;x *= 10) {
2054 if (l + len[x] >= MAXPATH)
2058 *(start[x]) == '.' &&
2065 if (l + STRLEN(EMPTY) >= MAXPATH)
2075 memmove(p, start[x], len[x]);
2080 for (i = len[x], q = start[x]; i > 0; i--, p++, q++)
2084 for (i = len[x], q = start[x]; i > 0; i--, p++, q++)
2102 p == fullrep ? pat != to :
2105 (pc = *(p - 1)) == SLASH
2115 if (l + STRLEN(EMPTY) >= MAXPATH)
2125 strcpy(fullrep, EMPTY);
2133 strcpy(fullrep, TOOLONG);
2137 static void checkcollisions()
2141 int i, mult, oldnreps;
2145 rd = (REPDICT *)myalloc(nreps * sizeof(REPDICT));
2147 q = &hrep, p = q->r_next, prd = rd, i = 0;
2149 q = p, p = p->r_next, prd++, i++
2152 prd->rd_dto = p->r_hto->h_di;
2153 prd->rd_nto = p->r_nto;
2156 qsort(rd, nreps, sizeof(REPDICT), rdcmp);
2158 for (i = 0, prd = rd, oldnreps = nreps; i < oldnreps; i++, prd++)
2161 prd->rd_dto == (prd + 1)->rd_dto &&
2162 strcmp(prd->rd_nto, (prd + 1)->rd_nto) == 0
2168 printf("%s%s", prd->rd_p->r_hfrom->h_name,
2169 prd->rd_p->r_ffrom->fi_name);
2170 prd->rd_p->r_flags |= R_SKIP;
2171 prd->rd_p->r_ffrom->fi_rep = MISTAKE;
2176 prd->rd_p->r_flags |= R_SKIP;
2177 prd->rd_p->r_ffrom->fi_rep = MISTAKE;
2180 printf(" , %s%s -> %s%s : collision.\n",
2181 prd->rd_p->r_hfrom->h_name, prd->rd_p->r_ffrom->fi_name,
2182 prd->rd_p->r_hto->h_name, prd->rd_nto);
2185 chgive(rd, oldnreps * sizeof(REPDICT));
2189 static int rdcmp(rd1, rd2)
2195 (ret = rd1->rd_dto - rd2->rd_dto) == 0 &&
2196 (ret = strcmp(rd1->rd_nto, rd2->rd_nto)) == 0
2198 ret = rd1->rd_i - rd2->rd_i;
2203 static void findorder()
2205 REP *p, *q, *t, *first, *pred;
2208 for (q = &hrep, p = q->r_next; p != NULL; q = p, p = p->r_next)
2209 if (p->r_flags & R_SKIP) {
2210 q->r_next = p->r_next;
2214 (fi = p->r_fdel) == NULL ||
2215 (pred = fi->fi_rep) == NULL ||
2219 else if ((first = pred->r_first) == p) {
2220 p->r_flags |= R_ISCYCLE;
2221 pred->r_flags |= R_ISALIASED;
2228 while (pred->r_thendo != NULL)
2229 pred = pred->r_thendo;
2231 for (t = p; t != NULL; t = t->r_thendo)
2233 q->r_next = p->r_next;
2239 static void nochains()
2243 for (q = &hrep, p = q->r_next; p != NULL; q = p, p = p->r_next)
2244 if (p->r_flags & R_ISCYCLE || p->r_thendo != NULL) {
2246 printf("%s%s : no chain copies allowed.\n",
2247 p->r_hto->h_name, p->r_nto);
2248 q->r_next = p->r_next;
2254 static void printchain(p)
2257 if (p->r_thendo != NULL)
2258 printchain(p->r_thendo);
2259 printf("%s%s -> ", p->r_hfrom->h_name, p->r_ffrom->fi_name);
2262 p->r_ffrom->fi_rep = MISTAKE;
2266 static void scandeletes(pkilldel)
2271 for (q = &hrep, p = q->r_next; p != NULL; q = p, p = p->r_next) {
2272 if (p->r_fdel != NULL)
2273 while ((*pkilldel)(p)) {
2275 p->r_ffrom->fi_rep = MISTAKE;
2276 if ((n = p->r_thendo) != NULL) {
2278 n->r_fdel = p->r_ffrom;
2279 n->r_next = p->r_next;
2283 q->r_next = p->r_next;
2292 static int baddel(p)
2295 HANDLE *hfrom = p->r_hfrom, *hto = p->r_hto;
2296 FILEINFO *fto = p->r_fdel;
2297 char *t = fto->fi_name, *f = p->r_ffrom->fi_name;
2298 char *hnf = hfrom->h_name, *hnt = hto->h_name;
2300 if (delstyle == NODEL && !(p->r_flags & R_DELOK) && !(op & APPEND))
2301 printf("%s%s -> %s%s : old %s%s would have to be %s.\n",
2302 hnf, f, hnt, t, hnt, t,
2303 (op & OVERWRITE) ? "overwritten" : "deleted");
2304 else if (fto->fi_rep == MISTAKE)
2305 printf("%s%s -> %s%s : old %s%s was to be done first.\n",
2306 hnf, f, hnt, t, hnt, t);
2309 fto->fi_attrib & FA_DIREC
2311 fto->fi_stflags & FI_ISDIR
2314 printf("%s%s -> %s%s : %s%s%s is a directory.\n",
2315 hnf, f, hnt, t, (op & APPEND) ? "" : "old ", hnt, t);
2317 else if ((fto->fi_stflags & FI_NODEL) && !(op & (APPEND | OVERWRITE)))
2318 printf("%s%s -> %s%s : old %s%s lacks delete permission.\n",
2319 hnf, f, hnt, t, hnt, t);
2322 (op & (APPEND | OVERWRITE)) &&
2324 fto->fi_attrib & FA_RDONLY
2326 !fwritable(hnt, fto)
2329 printf("%s%s -> %s%s : %s%s %s.\n",
2330 hnf, f, hnt, t, hnt, t,
2333 fto->fi_stflags & FI_LINKERR ?
2334 "is a badly aimed symbolic link" :
2337 "lacks write permission");
2346 static int skipdel(p)
2349 if (p->r_flags & R_DELOK)
2351 fprintf(stderr, "%s%s -> %s%s : ",
2352 p->r_hfrom->h_name, p->r_ffrom->fi_name,
2353 p->r_hto->h_name, p->r_nto);
2356 p->r_fdel->fi_attrib & FA_RDONLY
2359 !(p->r_ffrom->fi_stflags & FI_ISLNK) &&
2361 !fwritable(p->r_hto->h_name, p->r_fdel)
2364 fprintf(stderr, "old %s%s lacks write permission. delete it",
2365 p->r_hto->h_name, p->r_nto);
2367 fprintf(stderr, "%s old %s%s",
2368 (op & OVERWRITE) ? "overwrite" : "delete",
2369 p->r_hto->h_name, p->r_nto);
2370 return(!getreply("? ", -1));
2374 static void goonordie()
2376 if ((paterr || badreps) && nreps > 0) {
2377 fprintf(stderr, "Not everything specified can be done.");
2378 if (badstyle == ABORTBAD) {
2379 fprintf(stderr, " Aborting.\n");
2382 else if (badstyle == SKIPBAD)
2383 fprintf(stderr, " Proceeding with the rest.\n");
2384 else if (!getreply(" Proceed with the rest? ", -1))
2390 static void doreps()
2393 int k, printaliased = 0, alias = 0;
2400 signal(SIGINT, breakrep);
2403 for (first = hrep.r_next, k = 0; first != NULL; first = first->r_next) {
2404 for (p = first; p != NULL; p = p->r_thendo, k++) {
2407 fprintf(stderr, "User break.\n");
2408 printaliased = snap(first, p);
2411 strcpy(fullrep, p->r_hto->h_name);
2412 strcat(fullrep, p->r_nto);
2413 if (!noex && (p->r_flags & R_ISCYCLE)) {
2415 aliaslen = appendalias(first, p, &printaliased);
2417 alias = movealias(first, p, &printaliased);
2419 strcpy(pathbuf, p->r_hfrom->h_name);
2420 fstart = pathbuf + strlen(pathbuf);
2421 if ((p->r_flags & R_ISALIASED) && !(op & APPEND))
2422 sprintf(fstart, "%s%03d", TEMP, alias);
2424 strcpy(fstart, p->r_ffrom->fi_name);
2426 if (p->r_fdel != NULL && !(op & (APPEND | OVERWRITE)))
2427 myunlink(fullrep, p->r_fdel);
2429 (op & (COPY | APPEND)) ?
2431 p->r_flags & R_ISALIASED ? aliaslen : -1L) :
2434 link(pathbuf, fullrep) :
2437 symlink((p->r_flags & R_ONEDIRLINK) ? fstart : pathbuf,
2441 p->r_flags & R_ISX ?
2444 rename(pathbuf, fullrep)
2447 "%s -> %s has failed.\n", pathbuf, fullrep);
2448 printaliased = snap(first, p);
2451 if (verbose || noex) {
2452 if (p->r_flags & R_ISALIASED && !printaliased)
2453 strcpy(fstart, p->r_ffrom->fi_name);
2454 fprintf(outfile, "%s %c%c %s%s%s\n",
2456 p->r_flags & R_ISALIASED ? '=' : '-',
2457 p->r_flags & R_ISCYCLE ? '^' : '>',
2459 (p->r_fdel != NULL && !(op & APPEND)) ? " (*)" : "",
2460 noex ? "" : " : done");
2466 fprintf(stderr, "Strange, did %d reps; %d were expected.\n",
2469 fprintf(stderr, "Nothing done.\n");
2473 static long appendalias(first, p, pprintaliased)
2482 if ((fd = open(fullrep, O_RDONLY | O_BINARY, 0)) < 0) {
2483 fprintf(stderr, "stat on %s has failed.\n", fullrep);
2484 *pprintaliased = snap(first, p);
2487 ret = filelength(fd);
2493 if (stat(fullrep, &fstat)) {
2494 fprintf(stderr, "append cycle stat on %s has failed.\n", fullrep);
2495 *pprintaliased = snap(first, p);
2498 ret = fstat.st_size;
2505 static int movealias(first, p, pprintaliased)
2512 strcpy(pathbuf, p->r_hto->h_name);
2513 fstart = pathbuf + strlen(pathbuf);
2514 strcpy(fstart, TEMP);
2517 sprintf(fstart + STRLEN(TEMP), "%03d", ret),
2518 fsearch(fstart, p->r_hto->h_di) != NULL;
2522 if (rename(fullrep, pathbuf)) {
2524 "%s -> %s has failed.\n", fullrep, pathbuf);
2525 *pprintaliased = snap(first, p);
2531 static int snap(first, p)
2542 ctrlbrk((int (*)())breakstat);
2544 signal(SIGINT, breakstat);
2547 badstyle == ASKBAD &&
2548 isatty(fileno(stdout)) &&
2549 getreply("Redirect standard output to file? ", 0)
2556 fprintf(stderr, "File name> "),
2557 (outfile = fopen(mygets(fname, 80), "w")) == NULL
2559 fprintf(stderr, "Can't open %s.\n", fname);
2561 if (redirected || !verbose)
2563 fprintf(outfile, "The following left undone:\n");
2569 static void showdone(fin)
2574 for (first = hrep.r_next; ; first = first->r_next)
2575 for (p = first; p != NULL; p = p->r_thendo) {
2578 fprintf(outfile, "%s%s %c%c %s%s : done%s\n",
2579 p->r_hfrom->h_name, p->r_ffrom->fi_name,
2580 p->r_flags & R_ISALIASED ? '=' : '-',
2581 p->r_flags & R_ISCYCLE ? '^' : '>',
2582 p->r_hto->h_name, p->r_nto,
2583 (p->r_fdel != NULL && !(op & APPEND)) ? " (*)" : "");
2588 static void breakout()
2591 fprintf(stderr, "Aborting, nothing done.\n");
2596 static void breakrep(int signum)
2603 static void breakstat()
2611 fprintf(stderr, "Aborting, nothing done.\n");
2616 static int copymove(p)
2623 char linkbuf[MAXPATH];
2625 if ((llen = readlink(pathbuf, linkbuf, MAXPATH - 1)) >= 0) {
2626 linkbuf[llen] = '\0';
2627 return(symlink(linkbuf, fullrep) || myunlink(pathbuf, p->r_ffrom));
2632 return(copy(p->r_ffrom, -1L) || myunlink(pathbuf, p->r_ffrom));
2637 #define IRWMASK (S_IREAD | S_IWRITE)
2638 #define RWMASK (IRWMASK | (IRWMASK >> 3) | (IRWMASK >> 6))
2640 static int copy(ff, len)
2645 int f, t, k, mode, perm;
2653 struct timeval tim[2];
2658 if ((f = open(pathbuf, O_RDONLY | O_BINARY, 0)) < 0)
2662 IRWMASK /* will _chmod it later (to get all the attributes) */
2664 (op & (APPEND | OVERWRITE)) ?
2665 (~oldumask & RWMASK) | (ff->fi_mode & ~RWMASK) :
2673 (((t = open(fullrep, O_RDWR)) < 0 && errno == ENOENT)
2675 t = creat(fullrep, perm);
2677 mode = O_CREAT | (op & APPEND ? 0 : O_TRUNC) |
2679 O_BINARY | (op & ZAPPEND ? O_RDWR : O_WRONLY)
2684 t = open(fullrep, mode, perm);
2691 lseek(t, (off_t)0, SEEK_END);
2693 if (op & ZAPPEND && filelength(t) != 0) {
2694 if (lseek(t, -1L, 1) == -1L || read(t, &c, 1) != 1) {
2703 if ((op & APPEND) && len != (off_t)-1) {
2706 (k = read(f, buf, (len > BUFSIZE) ? BUFSIZE : (size_t)len)) > 0 &&
2707 write(t, buf, k) == k
2714 while ((k = read(f, buf, BUFSIZE)) > 0 && write(t, buf, k) == k)
2716 if (!(op & (APPEND | OVERWRITE)))
2719 getftime(f, &tim) ||
2720 setftime(t, &tim) ||
2721 _chmod(fullrep, 1, ff->fi_attrib) == -1
2723 stat(pathbuf, &fstat) ||
2726 tim.actime = fstat.st_atime,
2727 tim.modtime = fstat.st_mtime,
2729 tim[0].tv_sec = fstat.st_atime,
2730 tim[1].tv_sec = fstat.st_mtime,
2732 utimes(fullrep, tim)
2736 fprintf(stderr, "Strange, couldn't transfer time from %s to %s.\n",
2754 static int rename(from, to)
2759 if (link(from, to) == 0 && unlink(from) == 0)
2763 if (stat(from, &s) < 0 || (s.st_mode&S_IFMT) != S_IFDIR)
2767 do pid = fork(); while (pid >= 0 && errno == EAGAIN);
2771 else if (pid == 0) {
2772 execl(MV_DIR, "mv_dir", from, to, (char *) 0);
2775 } else if (pid > 0) {
2779 do wid = wait(&status);
2780 while (wid != pid && wid >= 0);
2782 return(status == 0 ? 0 : -1);
2787 static int rename(from, to)
2801 static int myunlink(n, f)
2808 if (((a = f->fi_attrib) & FA_RDONLY) && _chmod(n, 1, a & ~FA_RDONLY) < 0) {
2809 fprintf(stderr, "Strange, can not _chmod (or unlink) %s.\n", f);
2814 fprintf(stderr, "Strange, can not unlink %s.\n", n);
2821 static int getreply(m, failact)
2825 static FILE *tty = NULL;
2829 if (tty == NULL && (tty = fopen(TTY, "r")) == NULL) {
2830 fprintf(stderr, "Can not open %s to get reply.\n", TTY);
2839 fprintf(stderr, "Can not get reply.\n");
2846 while ((c = fgetc(tty)) != '\n' && c != EOF)
2849 if (r == 'y' || r == 'n')
2851 fprintf(stderr, "Yes or No? ");
2856 static void *myalloc(k)
2863 if ((ret = (void *)malloc(k)) == NULL) {
2864 fprintf(stderr, "Insufficient memory.\n");
2871 static void *challoc(k, which)
2877 SLICER *sl = &(slicer[which]);
2879 if (k > sl->sl_len) {
2881 q = NULL, p = freechunks;
2882 p != NULL && (sl->sl_len = p->ch_len) < k;
2883 q = p, p = p->ch_next
2887 sl->sl_len = CHUNKSIZE - sizeof(CHUNK *);
2888 p = (CHUNK *)myalloc(CHUNKSIZE);
2891 freechunks = p->ch_next;
2893 q->ch_next = p->ch_next;
2894 p->ch_next = sl->sl_first;
2896 sl->sl_unused = (char *)&(p->ch_len);
2899 ret = (void *)sl->sl_unused;
2905 static void chgive(p, k)
2909 ((CHUNK *)p)->ch_len = k - sizeof(CHUNK *);
2910 ((CHUNK *)p)->ch_next = freechunks;
2911 freechunks = (CHUNK *)p;
2918 static void memmove(to, from, k)
2924 *(to++) = *(from++);
2929 *(--to) = *(--from);
2939 static int lastc = 0;
2943 return(lastc = getchar());
2947 static char *mygets(s, l)
2954 if (fgets(s, l, stdin) == NULL)
2956 if ((nl = strchr(s, '\n')) != NULL)
2958 fprintf(stderr, "Input string too long. Try again> ");
2971 static void cleanup()
2975 if (patch.ph_safeid) {
2976 for (i = 0; i < nhandles; i++) {
2977 if (!(handles[i]->h_di->di_flags & DI_CLEANED)) {
2978 sprintf(pathbuf, "%s%s%03d",
2979 handles[i]->h_name, IDF, handles[i]->h_di->di_did);
2980 if (unlink(pathbuf))
2981 fprintf(stderr, "Strange, couldn't unlink %s.\n", pathbuf);
2982 handles[i]->h_di->di_flags |= DI_CLEANED;
2987 Write device availability: undocumented internal MS-D*S function.
2988 Restore previous value.
2990 bdos(0x37, olddevflag, 3);