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 */
127 #include <sys/types.h>
128 #include <sys/stat.h>
129 #include <sys/file.h>
133 typedef struct dirent DIRENTRY;
137 /* might need to be changed to <dir.h> */
141 typedef struct direct DIRENTRY;
148 #define void char /* might want to remove this line */
171 static char TTY[] = "/dev/tty";
178 extern char *strcpy(), *strchr();
185 /* for System V and BSD */
193 /* for System V and Version 7*/
198 #define utimes(f, t) utime((f), &(t))
202 # define MV_DIR "/usr/lib/mv_dir"
215 #include <sys/time.h>
220 #define mylower(c) (isupper(c) ? (c)-'A'+'a' : (c))
221 #define myupper(c) (islower(c) ? (c)-'a'+'A' : (c))
222 #define STRLEN(s) (sizeof(s) - 1)
223 #define mydup(s) (strcpy((char *)challoc(strlen(s) + 1, 0), (s)))
227 #define NORMCOPY 0x002
228 #define OVERWRITE 0x004
229 #define NORMMOVE 0x008
231 #define DIRMOVE 0x020
232 #define NORMAPPEND 0x040
233 #define ZAPPEND 0x080
234 #define HARDLINK 0x100
235 #define SYMLINK 0x200
237 #define COPY (NORMCOPY | OVERWRITE)
238 #define MOVE (NORMMOVE | XMOVE | DIRMOVE)
239 #define APPEND (NORMAPPEND | ZAPPEND)
240 #define LINK (HARDLINK | SYMLINK)
242 static char MOVENAME[] = "mmv";
243 static char COPYNAME[] = "mcp";
244 static char APPENDNAME[] = "mad";
245 static char LINKNAME[] = "mln";
260 #define MAXPATLEN MAXPATH
262 #define CHUNKSIZE 2048
265 #define FI_STTAKEN 0x01
266 #define FI_LINKERR 0x02
267 #define FI_INSTICKY 0x04
268 #define FI_NODEL 0x08
269 #define FI_KNOWWRITE 0x010
270 #define FI_CANWRITE 0x20
271 #define FI_ISDIR 0x40
272 #define FI_ISLNK 0x80
285 #define DI_KNOWWRITE 0x01
286 #define DI_CANWRITE 0x02
287 #define DI_CLEANED 0x04
298 #define H_NOREADDIR 2
309 #define R_ISALIASED 0x08
310 #define R_ISCYCLE 0x10
311 #define R_ONEDIRLINK 0x20
317 char *r_nto; /* non-path part of new name */
320 struct rep *r_thendo;
332 typedef struct chunk {
333 struct chunk *ch_next;
344 static void init(/* */);
345 static void procargs(/* int argc, char **argv,
346 char **pfrompat, char **ptopat */);
347 static void domatch(/* char *cfrom, char *cto */);
348 static int getpat(/* */);
349 static int getword(/* char *buf */);
350 static void matchpat(/* */);
351 static int parsepat(/* */);
352 static int dostage(/* char *lastend, char *pathend,
353 char **start1, int *len1, int stage, int anylev */);
354 static int trymatch(/* FILEINFO *ffrom, char *pat */);
355 static int keepmatch(/* FILEINFO *ffrom, char *pathend,
356 int *pk, int needslash, int dirs, int fils */);
357 static int badrep(/* HANDLE *hfrom, FILEINFO *ffrom,
358 HANDLE **phto, char **pnto, FILEINFO **pfdel, int *pflags */);
359 static int checkto(/* HANDLE *hfrom, char *f,
360 HANDLE **phto, char **pnto, FILEINFO **pfdel */);
361 static char *getpath(/* char *tpath */);
362 static int badname(/* char *s */);
363 static FILEINFO *fsearch(/* char *s, DIRINFO *d */);
364 static int ffirst(/* char *s, int n, DIRINFO *d */);
365 static HANDLE *checkdir(/* char *p, char *pathend, int which */);
366 static void takedir(/*
367 char *p, DIRINFO *di, int sticky
369 struct ffblk *pff, DIRINFO *di
371 static int fcmp(/* FILEINFO **pf1, FILEINFO **pf2 */);
372 static HANDLE *hadd(/* char *n */);
373 static int hsearch(/* char *n, int which, HANDLE **ph */);
374 static DIRINFO *dadd(/* DEVID v, DIRID d */);
375 static DIRINFO *dsearch(/* DEVID v, DIRID d */);
376 static int match(/* char *pat, char *s, char **start1, int *len1 */);
377 static void makerep(/* */);
378 static void checkcollisions(/* */);
379 static int rdcmp(/* REPDICT *rd1, REPDICT *rd2 */);
380 static void findorder(/* */);
381 static void scandeletes(/* int (*pkilldel)(REP *p) */);
382 static int baddel(/* REP *p */);
383 static int skipdel(/* REP *p */);
384 static void nochains(/* */);
385 static void printchain(/* REP *p */);
386 static void goonordie(/* */);
387 static void doreps(/* */);
388 static long appendalias(/* REP *first, REP *p, int *pprintaliased */);
389 static int movealias(/* REP *first, REP *p, int *pprintaliased */);
390 static int snap(/* REP *first, REP *p */);
391 static void showdone(/* REP *fin */);
392 static void breakout(/* */);
393 static void breakrep(int);
394 static void breakstat(/* */);
395 static void quit(/* */);
396 static int copymove(/* REP *p */);
397 static int copy(/* FILENFO *f, long len */);
398 static int myunlink(/* char *n, FILEINFO *f */);
399 static int getreply(/* char *m, int failact */);
400 static void *myalloc(/* unsigned k */);
401 static void *challoc(/* int k, int which */);
402 static void chgive(/* void *p, unsigned k */);
403 static int mygetc(/* */);
404 static char *mygets(/* char *s, int l */);
406 static int leave(/* */);
407 static void cleanup(/* */);
409 static int getstat(/* char *full, FILEINFO *f */);
410 static int dwritable(/* HANDLE *h */);
411 static int fwritable(/* char *hname, FILEINFO *f */);
415 static void memmove(/* void *to, void *from, int k */);
421 static int rename(/* char *from, char *to */);
424 static int op, badstyle, delstyle, verbose, noex, matchall;
427 static unsigned ndirs = 0, dirroom;
428 static DIRINFO **dirs;
429 static unsigned nhandles = 0, handleroom;
430 static HANDLE **handles;
431 static HANDLE badhandle = {"\200", NULL, 0};
432 static HANDLE *(lasthandle[2]) = {&badhandle, &badhandle};
433 static unsigned nreps = 0;
434 static REP hrep, *lastrep = &hrep;
435 static CHUNK *freechunks = NULL;
436 static SLICER slicer[2] = {{NULL, NULL, 0}, {NULL, NULL, 0}};
438 static int badreps = 0, paterr = 0, direrr, failed = 0, gotsig = 0, repbad;
439 static FILE *outfile;
441 static char IDF[] = "$$mmvdid.";
442 static char TEMP[] = "$$mmvtmp.";
443 static char TOOLONG[] = "(too long)";
444 static char EMPTY[] = "(empty)";
446 static char SLASHSTR[] = {SLASH, '\0'};
448 static char PATLONG[] = "%.40s... : pattern too long.\n";
450 char from[MAXPATLEN], to[MAXPATLEN];
451 static int fromlen, tolen;
452 static char *(stagel[MAXWILD]), *(firstwild[MAXWILD]), *(stager[MAXWILD]);
453 static int nwilds[MAXWILD];
455 char pathbuf[MAXPATH];
456 char fullrep[MAXPATH + 1];
457 static char *(start[MAXWILD]);
458 static int len[MAXWILD];
459 static char hasdot[MAXWILD];
461 #define MISTAKE (&mistake)
465 static int olddevflag, curdisk, maxdisk;
474 } patch = {"mmv 1.0 patchable flags", "mmv", XMOVE, 1, 0};
476 #define DFLTOP (patch.ph_dfltop)
477 #define CLUSTNO(pff) (*(int *)(((char *)(pff)) + patch.ph_clustoff))
478 #define DRIVENO(pff) (*(((char *)(pff)) + patch.ph_driveoff) - patch.ph_drivea)
487 static int uid, euid, oldumask;
488 static DIRID cwdd = -1;
489 static DEVID cwdv = -1;
498 char *frompat, *topat;
503 procargs(argc, argv, &frompat, &topat);
504 domatch(frompat, topat);
508 if (op & (COPY | LINK))
512 if (!(op & APPEND) && delstyle == ASKDEL)
513 scandeletes(skipdel);
515 return(failed ? 2 : nreps == 0 && (paterr || badreps));
523 maxdisk = setdisk(curdisk);
525 Read device availability : undocumented internal MS-DOS function.
526 If (_DX == 0) then \dev\ must precede device names.
531 Write device availability: undocumented internal MS-DOS function.
532 Specify \dev\ must precede device names.
535 atexit((atexit_t)cleanup);
536 ctrlbrk((int (*)())breakout);
540 if ((home = getenv("HOME")) == NULL || strcmp(home, SLASHSTR) == 0)
542 if (!stat(".", &dstat)) {
549 signal(SIGINT, breakout);
552 dirroom = handleroom = INITROOM;
553 dirs = (DIRINFO **)myalloc(dirroom * sizeof(DIRINFO *));
554 handles = (HANDLE **)myalloc(handleroom * sizeof(HANDLE *));
555 ndirs = nhandles = 0;
559 static void procargs(argc, argv, pfrompat, ptopat)
562 char **pfrompat, **ptopat;
565 char *cmdname = argv[0];
568 #define CMDNAME (patch.ph_name)
570 #define CMDNAME cmdname
574 verbose = noex = matchall = 0;
577 for (argc--, argv++; argc > 0 && **argv == '-'; argc--, argv++)
578 for (p = *argv + 1; *p != '\0'; p++) {
585 if (c == 'v' && !noex)
587 else if (c == 'n' && !verbose)
591 else if (c == 'd' && delstyle == ASKDEL)
593 else if (c == 'p' && delstyle == ASKDEL)
595 else if (c == 'g' && badstyle == ASKBAD)
597 else if (c == 't' && badstyle == ASKBAD)
599 else if (c == 'm' && op == DFLT)
601 else if (c == 'x' && op == DFLT)
603 else if (c == 'r' && op == DFLT)
605 else if (c == 'c' && op == DFLT)
607 else if (c == 'o' && op == DFLT)
609 else if (c == 'a' && op == DFLT)
612 else if (c == 'z' && op == DFLT)
615 else if (c == 'l' && op == DFLT)
618 else if (c == 's' && op == DFLT)
623 fprintf(stderr, USAGE, CMDNAME, OTHEROPT);
630 if (strcmp(cmdname, MOVENAME) == 0)
632 else if (strcmp(cmdname, COPYNAME) == 0)
634 else if (strcmp(cmdname, APPENDNAME) == 0)
636 else if (strcmp(cmdname, LINKNAME) == 0)
653 "Unable to do directory renames. Option -r refused.\n");
658 if (euid != uid && !(op & DIRMOVE)) {
664 if (badstyle != ASKBAD && delstyle == ASKDEL)
669 else if (argc == 2) {
670 *pfrompat = *(argv++);
674 fprintf(stderr, USAGE, CMDNAME, OTHEROPT);
680 static void domatch(cfrom, cto)
686 else if ((fromlen = strlen(cfrom)) >= MAXPATLEN) {
687 printf(PATLONG, cfrom);
690 else if ((tolen = strlen(cto)) >= MAXPATLEN) {
691 printf(PATLONG, cto);
705 char extra[MAXPATLEN];
709 if ((fromlen = getword(from)) == 0 || fromlen == -1)
713 if ((tolen = getword(to)) == 0) {
714 printf("%s -> ? : missing replacement pattern.\n", from);
721 (to[0] == '-' || to[0] == '=') &&
722 (to[1] == '>' || to[1] == '^')
724 if (getword(extra) == 0)
726 else if (strcmp(extra, "(*)") == 0) {
728 gotit = (getword(extra) == 0);
732 while ((c = mygetc()) != '\n' && c != EOF)
742 static int getword(buf)
751 while ((c = mygetc()) != EOF && (prevc == ESC || !isspace(c))) {
754 if (n == MAXPATLEN - 1) {
756 printf(PATLONG, buf);
764 while (c != EOF && isspace(c) && c != '\n')
772 static void matchpat()
776 else if (dostage(from, pathbuf, start, len, 0, 0)) {
777 printf("%s -> %s : no match.\n", from, to);
783 static int parsepat()
785 char *p, *lastname, c;
786 int totwilds, instage, x, havedot;
787 static char TRAILESC[] = "%s -> %s : trailing %c is superfluous.\n";
792 if (from[0] != '\0' && from[1] == ':')
795 if (from[0] == '~' && from[1] == SLASH) {
796 if ((homelen = strlen(home)) + fromlen > MAXPATLEN) {
797 printf(PATLONG, from);
800 memmove(from + homelen, from + 1, fromlen);
801 memmove(from, home, homelen);
802 lastname += homelen + 1;
805 totwilds = nstages = instage = 0;
806 for (p = lastname; (c = *p) != '\0'; p++)
817 if (!havedot && lastname != p) {
818 if (fromlen++ == MAXPATLEN) {
819 printf(PATLONG, from);
822 memmove(p + 1, p, strlen(p) + 1);
830 if (firstwild[nstages] == NULL)
831 firstwild[nstages] = p;
832 stager[nstages++] = p;
838 printf("%s -> %s : badly placed ;.\n", from, to);
846 if ((hasdot[totwilds] = (c == '!')) != 0)
849 if (totwilds++ == MAXWILD) {
850 printf("%s -> %s : too many wildcards.\n", from, to);
855 if (firstwild[nstages] == NULL)
856 firstwild[nstages] = p;
859 stagel[nstages] = lastname;
860 firstwild[nstages] = (c == ';' ? NULL : p);
866 while ((c = *(++p)) != ']') {
869 printf("%s -> %s : missing ].\n", from, to);
877 printf("%s -> %s : '%c' can not be part of [].\n",
881 if ((c = *(++p)) == '\0') {
882 printf(TRAILESC, from, to, ESC);
888 *p = c + ('a' - 'A');
894 if ((c = *(++p)) == '\0') {
895 printf(TRAILESC, from, to, ESC);
901 *p = c + ('a' - 'A');
906 if (!havedot && lastname != p) {
907 if (fromlen++ == MAXPATLEN) {
908 printf(PATLONG, from);
916 if (firstwild[nstages] == NULL)
917 firstwild[nstages] = p;
918 stager[nstages++] = p;
921 stagel[nstages] = lastname;
923 firstwild[nstages] = p;
924 stager[nstages++] = p;
930 if (to[0] != '\0' && to[1] == ':')
933 if (to[0] == '~' && to[1] == SLASH) {
934 if ((homelen = strlen(home)) + tolen > MAXPATLEN) {
938 memmove(to + homelen, to + 1, tolen);
939 memmove(to, home, homelen);
940 lastname += homelen + 1;
944 for (p = lastname; (c = *p) != '\0'; p++)
955 printf("%s -> %s : no path allowed in target under -r.\n",
960 if (!havedot && lastname != p) {
961 if (tolen++ == MAXPATLEN) {
965 memmove(p + 1, p, strlen(p) + 1);
975 if (c == 'l' || c == 'u') {
984 printf("%s -> %s : expected digit (not '%c') after #.\n",
988 for(x = 0; ;x *= 10) {
995 if (x < 1 || x > totwilds) {
996 printf("%s -> %s : wildcard #%d does not exist.\n",
1006 if ((c = *(++p)) == '\0') {
1007 printf(TRAILESC, from, to, ESC);
1013 c <= ' ' || c >= 127 ||
1014 strchr(":/\\*?[]=+;,\"|<>", c) != NULL
1016 printf("%s -> %s : illegal character '%c' (0x%02X).\n",
1021 *p = c + ('a' - 'A');
1026 if (!havedot && lastname != p) {
1027 if (tolen++ == MAXPATLEN) {
1028 printf(PATLONG, to);
1039 static int dostage(lastend, pathend, start1, len1, stage, anylev)
1040 char *lastend, *pathend;
1048 int prelen, litlen, nfils, i, k, flags, try;
1049 FILEINFO **pf, *fdel;
1050 char *nto, *firstesc;
1052 int wantdirs, ret = 1, laststage = (stage + 1 == nstages);
1054 wantdirs = !laststage ||
1055 (op & (DIRMOVE | SYMLINK)) ||
1056 (nwilds[nstages - 1] == 0);
1059 prelen = stagel[stage] - lastend;
1060 if (pathend - pathbuf + prelen >= MAXPATH) {
1061 printf("%s -> %s : search path after %s too long.\n",
1066 memmove(pathend, lastend, prelen);
1069 lastend = stagel[stage];
1072 if ((h = checkdir(pathbuf, pathend, 0)) == NULL) {
1073 if (stage == 0 || direrr == H_NOREADDIR) {
1074 printf("%s -> %s : directory %s does not %s.\n",
1075 from, to, pathbuf, direrr == H_NOREADDIR ?
1076 "allow reads/searches" : "exist");
1083 if (*lastend == ';') {
1090 nfils = di->di_nfils;
1093 if ((op & MOVE) && !dwritable(h)) {
1094 printf("%s -> %s : directory %s does not allow writes.\n",
1101 firstesc = strchr(lastend, ESC);
1102 if (firstesc == NULL || firstesc > firstwild[stage])
1103 firstesc = firstwild[stage];
1104 litlen = firstesc - lastend;
1105 pf = di->di_fils + (i = ffirst(lastend, litlen, di));
1109 (try = trymatch(*pf, lastend)) != 0 &&
1112 match(lastend + litlen, (*pf)->fi_name + litlen,
1113 start1 + anylev, len1 + anylev)
1115 keepmatch(*pf, pathend, &k, 0, wantdirs, laststage)
1118 ret &= dostage(stager[stage], pathend + k,
1119 start1 + nwilds[stage], len1 + nwilds[stage],
1124 if (badrep(h, *pf, &hto, &nto, &fdel, &flags))
1125 (*pf)->fi_rep = MISTAKE;
1127 (*pf)->fi_rep = p = (REP *)challoc(sizeof(REP), 1);
1128 p->r_flags = flags | patflags;
1137 lastrep->r_next = p;
1144 } while (i < nfils && strncmp(lastend, (*pf)->fi_name, litlen) == 0);
1148 for (pf = di->di_fils, i = 0; i < nfils; i++, pf++)
1150 *((*pf)->fi_name) != '.' &&
1152 ((*pf)->fi_attrib & FA_DIREC) &&
1154 keepmatch(*pf, pathend, &k, 1, 1, 0)
1156 *len1 = pathend - *start1 + k;
1157 ret &= dostage(lastend, pathend + k, start1, len1, stage, 1);
1164 static int trymatch(ffrom, pat)
1170 if (ffrom->fi_rep != NULL)
1176 if (*p == '.' || (!matchall && ffrom->fi_attrib & (FA_HIDDEN | FA_SYSTEM)))
1177 return(strcmp(pat, p) == 0);
1180 if (p[1] == '\0' || (p[1] == '.' && p[2] == '\0'))
1181 return(strcmp(pat, p) == 0);
1182 else if (!matchall && *pat != '.')
1189 static int keepmatch(ffrom, pathend, pk, needslash, dirs, fils)
1196 *pk = strlen(ffrom->fi_name);
1197 if (pathend - pathbuf + *pk + needslash >= MAXPATH) {
1199 printf("%s -> %s : search path %s%s too long.\n",
1200 from, to, pathbuf, ffrom->fi_name);
1204 strcpy(pathend, ffrom->fi_name);
1206 if ((ffrom->fi_attrib & FA_DIREC) ? !dirs : !fils)
1208 getstat(pathbuf, ffrom);
1209 if ((ffrom->fi_stflags & FI_ISDIR) ? !dirs : !fils)
1214 strcpy(pathend + *pk, SLASHSTR);
1221 static int badrep(hfrom, ffrom, phto, pnto, pfdel, pflags)
1229 char *f = ffrom->fi_name;
1234 (ffrom->fi_attrib & FA_DIREC) &&
1236 (ffrom->fi_stflags & FI_ISDIR) &&
1238 !(op & (DIRMOVE | SYMLINK))
1240 printf("%s -> %s : source file is a directory.\n", pathbuf, fullrep);
1243 else if ((ffrom->fi_stflags & FI_LINKERR) && !(op & (MOVE | SYMLINK)))
1244 printf("%s -> %s : source file is a badly aimed symbolic link.\n",
1248 else if ((ffrom->fi_stflags & FI_NODEL) && (op & MOVE))
1249 printf("%s -> %s : no delete permission for source file.\n",
1252 else if ((op & (COPY | APPEND)) && access(pathbuf, R_OK))
1253 printf("%s -> %s : no read permission for source file.\n",
1258 (f[1] == '\0' || strcmp(f, "..") == 0) &&
1261 printf("%s -> %s : . and .. can't be renamed.\n", pathbuf, fullrep);
1262 else if (repbad || checkto(hfrom, f, phto, pnto, pfdel) || badname(*pnto))
1263 printf("%s -> %s : bad new name.\n", pathbuf, fullrep);
1264 else if (*phto == NULL)
1265 printf("%s -> %s : %s.\n", pathbuf, fullrep,
1267 direrr == H_NOREADDIR ?
1268 "no read or search permission for target directory" :
1270 "target directory does not exist");
1272 else if (!dwritable(*phto))
1273 printf("%s -> %s : no write permission for target directory.\n",
1277 (*phto)->h_di->di_vid != hfrom->h_di->di_vid &&
1278 (*pflags = R_ISX, (op & (NORMMOVE | HARDLINK)))
1280 printf("%s -> %s : cross-device move.\n",
1284 *pflags && (op & MOVE) &&
1285 !(ffrom->fi_stflags & FI_ISLNK) &&
1286 access(pathbuf, R_OK)
1288 printf("%s -> %s : no read permission for source file.\n",
1294 ((*phto)->h_di->di_vid == cwdv && (*phto)->h_di->di_did == cwdd) ||
1295 *(hfrom->h_name) == SLASH ||
1296 (*pflags |= R_ONEDIRLINK, hfrom->h_di == (*phto)->h_di)
1299 printf("%s -> %s : symbolic link would be badly aimed.\n",
1310 static int checkto(hfrom, f, phto, pnto, pfdel)
1317 char tpath[MAXPATH + 1];
1324 hlen = strlen(hfrom->h_name);
1325 pathend = fullrep + hlen;
1326 memmove(pathend, fullrep, strlen(fullrep) + 1);
1327 memmove(fullrep, hfrom->h_name, hlen);
1328 if ((fdel = *pfdel = fsearch(pathend, hfrom->h_di)) != NULL) {
1329 *pnto = fdel->fi_name;
1331 getstat(fullrep, fdel);
1335 *pnto = mydup(pathend);
1338 pathend = getpath(tpath);
1339 hlen = pathend - fullrep;
1340 *phto = checkdir(tpath, tpath + hlen, 1);
1344 (fdel = *pfdel = fsearch(pathend, (*phto)->h_di)) != NULL &&
1346 (fdel->fi_attrib & FA_DIREC)
1348 (getstat(fullrep, fdel), fdel->fi_stflags & FI_ISDIR)
1351 tlen = strlen(pathend);
1352 strcpy(pathend + tlen, SLASHSTR);
1354 strcpy(tpath + hlen, pathend);
1357 *phto = checkdir(tpath, tpath + hlen, 1);
1360 if (*pathend == '\0') {
1362 if (pathend - fullrep + strlen(f) >= MAXPATH) {
1363 strcpy(fullrep, TOOLONG);
1367 if (*phto != NULL) {
1368 fdel = *pfdel = fsearch(f, (*phto)->h_di);
1371 getstat(fullrep, fdel);
1375 else if (fdel != NULL)
1376 *pnto = fdel->fi_name;
1378 *pnto = mydup(pathend);
1384 static char *getpath(tpath)
1387 char *pathstart, *pathend, c;
1390 if (*fullrep != '\0' && fullrep[1] == ':')
1391 pathstart = fullrep + 2;
1394 pathstart = fullrep;
1396 pathend = pathstart + strlen(pathstart) - 1;
1397 while (pathend >= pathstart && *pathend != SLASH)
1403 strcpy(tpath, fullrep);
1409 static int badname(s)
1418 (ext = strchr(s, '.')) - s >= MAXFILE ||
1419 (*ext == '.' && strchr(ext + 1, '.') != NULL) ||
1420 strlen(ext) >= MAXEXT ||
1421 strncmp(s, IDF, STRLEN(IDF)) == 0
1423 (*s == '.' && (s[1] == '\0' || strcmp(s, "..") == 0)) ||
1424 strlen(s) > MAXNAMLEN
1431 static int getstat(ffull, f)
1438 if ((flags = f->fi_stflags) & FI_STTAKEN)
1439 return(flags & FI_LINKERR);
1440 flags |= FI_STTAKEN;
1442 if (stat(ffull, &fstat)) {
1443 fprintf(stderr, "Strange, couldn't stat %s.\n", ffull);
1447 if (lstat(ffull, &fstat)) {
1448 fprintf(stderr, "Strange, couldn't lstat %s.\n", ffull);
1451 if ((flags & FI_INSTICKY) && fstat.st_uid != uid && uid != 0)
1453 if ((fstat.st_mode & S_IFMT) == S_IFLNK) {
1455 if (stat(ffull, &fstat)) {
1456 f->fi_stflags = flags | FI_LINKERR;
1461 if ((fstat.st_mode & S_IFMT) == S_IFDIR)
1463 f->fi_stflags = flags;
1464 f->fi_mode = fstat.st_mode;
1469 static int dwritable(h)
1472 char *p = h->h_name, *myp, *lastslash = NULL, *pathend;
1473 char *pw = &(h->h_di->di_flags), r;
1478 if (*pw & DI_KNOWWRITE)
1479 return(*pw & DI_CANWRITE);
1481 pathend = p + strlen(p);
1484 else if (pathend == p + 1)
1487 lastslash = pathend - 1;
1491 r = !access(myp, W_OK) ? DI_CANWRITE : 0;
1492 *pw |= DI_KNOWWRITE | r;
1494 if (lastslash != NULL)
1500 static int fwritable(hname, f)
1506 if (f->fi_stflags & FI_KNOWWRITE)
1507 return(f->fi_stflags & FI_CANWRITE);
1509 strcpy(fullrep, hname);
1510 strcat(fullrep, f->fi_name);
1511 r = !access(fullrep, W_OK) ? FI_CANWRITE : 0;
1512 f->fi_stflags |= FI_KNOWWRITE | r;
1518 static FILEINFO *fsearch(s, d)
1522 FILEINFO **fils = d->di_fils;
1523 int nfils = d->di_nfils;
1524 int first, k, last, res;
1526 for(first = 0, last = nfils - 1;;) {
1529 k = (first + last) >> 1;
1530 if ((res = strcmp(s, fils[k]->fi_name)) == 0)
1540 static int ffirst(s, n, d)
1545 int first, k, last, res;
1546 FILEINFO **fils = d->di_fils;
1547 int nfils = d->di_nfils;
1549 if (nfils == 0 || n == 0)
1554 k = (first + last) >> 1;
1555 res = strncmp(s, fils[k]->fi_name, n);
1557 return(res == 0 ? k : nfils);
1567 /* checkdir and takedir for MS-D*S */
1569 static HANDLE *checkdir(p, pathend, which)
1582 if (hsearch(p, which, &h))
1583 if (h->h_di == NULL) {
1590 if (*p == '\0' || p[1] != ':')
1594 v = mylower(p[0]) - 'a';
1595 if (v < 0 || v >= maxdisk)
1599 if (patch.ph_safeid) {
1600 strcpy(pathend, IDF);
1601 strcpy(pathend + STRLEN(IDF), "*");
1602 if (findfirst(p, &de, 0)) {
1603 if ((d = ndirs) == 1000) {
1604 fprintf(stderr, "Too many different directories.\n");
1607 sprintf(pathend + STRLEN(IDF), "%03d", d);
1608 if ((fd = _creat(p, 0)) < 0) {
1609 direrr = h->h_err = H_NODIR;
1613 strcpy(pathend, "*.*");
1614 if (findfirst(p, &de, FA_DIREC | FA_SYSTEM | FA_HIDDEN))
1615 h->h_di = dadd(v, d);
1617 takedir(&de, h->h_di = dadd(v, d));
1619 else if ((d = atoi(de.ff_name + STRLEN(IDF))) < ndirs)
1622 strcpy(pathend, de.ff_name);
1623 fprintf(stderr, "Strange dir-id file encountered: %s.\n", p);
1629 strcpy(pathend, "*.*");
1630 firstfound = !findfirst(p, &de, FA_DIREC | FA_SYSTEM | FA_HIDDEN);
1637 strcpy(pathend, "T.D");
1640 direrr = h->h_err = H_NODIR;
1643 strcpy(pathend, "*.*");
1644 firstfound = !findfirst(p, &de, FA_DIREC | FA_SYSTEM | FA_HIDDEN);
1649 if (!firstfound || d != 0) {
1651 "Strange, %s does not seem to be a root dir.\n",
1657 if ((di = dsearch(v, d)) == NULL)
1659 takedir(&de, h->h_di = dadd(v, d));
1661 h->h_di = dadd(v, d);
1670 static void takedir(pff, di)
1674 int cnt, room, namlen, needdot;
1675 FILEINFO **fils, *f;
1679 di->di_fils = fils = (FILEINFO **)myalloc(room * sizeof(FILEINFO *));
1682 if (strnicmp(pff->ff_name, IDF, STRLEN(IDF)) == 0)
1686 fils = (FILEINFO **)myalloc(room * sizeof(FILEINFO *));
1687 memcpy(fils, di->di_fils, cnt * sizeof(FILEINFO *));
1688 chgive(di->di_fils, cnt * sizeof(FILEINFO *));
1690 fils = di->di_fils + cnt;
1693 for (p = pff->ff_name, namlen = 0; (c = *p) != '\0'; p++, namlen++)
1696 *fils = f = (FILEINFO *)challoc(sizeof(FILEINFO), 1);
1697 f->fi_name = p = (char *)challoc(namlen + needdot + 1, 0);
1698 for (p1 = pff->ff_name; (c = *p1) != '\0'; p1++)
1699 *(p++) = mylower(c);
1703 f->fi_attrib = pff->ff_attrib;
1707 } while (findnext(pff) == 0);
1708 qsort(di->di_fils, cnt, sizeof(FILEINFO *), fcmp);
1713 /* checkdir, takedir for Un*x */
1715 static HANDLE *checkdir(p, pathend, which)
1722 DIRINFO **newdirs, *di;
1725 char *myp, *lastslash = NULL;
1729 if (hsearch(p, which, &h))
1730 if (h->h_di == NULL) {
1739 else if (pathend == p + 1)
1742 lastslash = pathend - 1;
1747 if (stat(myp, &dstat) || (dstat.st_mode & S_IFMT) != S_IFDIR)
1748 direrr = h->h_err = H_NODIR;
1749 else if (access(myp, R_OK | X_OK))
1750 direrr = h->h_err = H_NOREADDIR;
1753 sticky = (dstat.st_mode & S_ISVTX) && uid != 0 && uid != dstat.st_uid ?
1758 if ((di = dsearch(v, d)) == NULL)
1759 takedir(myp, di = dadd(v, d), sticky);
1762 if (lastslash != NULL)
1771 static void takedir(p, di, sticky)
1778 FILEINFO *f, **fils;
1781 if ((dirp = opendir(p)) == NULL) {
1782 fprintf(stderr, "Strange, can't scan %s.\n", p);
1786 di->di_fils = fils = (FILEINFO **)myalloc(room * sizeof(FILEINFO *));
1788 while ((dp = readdir(dirp)) != NULL) {
1791 fils = (FILEINFO **)myalloc(room * sizeof(FILEINFO *));
1792 memcpy(fils, di->di_fils, cnt * sizeof(FILEINFO *));
1793 chgive(di->di_fils, cnt * sizeof(FILEINFO *));
1795 fils = di->di_fils + cnt;
1797 *fils = f = (FILEINFO *)challoc(sizeof(FILEINFO), 1);
1798 f->fi_name = mydup(dp->d_name);
1799 f->fi_stflags = sticky;
1805 qsort(di->di_fils, cnt, sizeof(FILEINFO *), fcmp);
1809 /* end of Un*x checkdir, takedir; back to general program */
1813 static int fcmp(pf1, pf2)
1814 FILEINFO **pf1, **pf2;
1816 return(strcmp((*pf1)->fi_name, (*pf2)->fi_name));
1820 static HANDLE *hadd(n)
1823 HANDLE **newhandles, *h;
1825 if (nhandles == handleroom) {
1827 newhandles = (HANDLE **)myalloc(handleroom * sizeof(HANDLE *));
1828 memcpy(newhandles, handles, nhandles * sizeof(HANDLE *));
1829 chgive(handles, nhandles * sizeof(HANDLE *));
1830 handles = newhandles;
1832 handles[nhandles++] = h = (HANDLE *)challoc(sizeof(HANDLE), 1);
1833 h->h_name = (char *)challoc(strlen(n) + 1, 0);
1834 strcpy(h->h_name, n);
1840 static int hsearch(n, which, pret)
1848 if (strcmp(n, lasthandle[which]->h_name) == 0) {
1849 *pret = lasthandle[which];
1853 for(i = 0, ph = handles; i < nhandles; i++, ph++)
1854 if (strcmp(n, (*ph)->h_name) == 0) {
1855 lasthandle[which] = *pret = *ph;
1859 lasthandle[which] = *pret = hadd(n);
1864 static DIRINFO *dadd(v, d)
1871 if (ndirs == dirroom) {
1873 newdirs = (DIRINFO **)myalloc(dirroom * sizeof(DIRINFO *));
1874 memcpy(newdirs, dirs, ndirs * sizeof(DIRINFO *));
1875 chgive(dirs, ndirs * sizeof(DIRINFO *));
1878 dirs[ndirs++] = di = (DIRINFO *)challoc(sizeof(DIRINFO), 1);
1888 static DIRINFO *dsearch(v, d)
1895 for(i = 0, di = *dirs; i < ndirs; i++, di++)
1896 if (v == di->di_vid && d == di->di_did)
1902 static int match(pat, s, start1, len1)
1903 char *pat, *s, **start1;
1917 if ((s = strchr(s, '.')) == NULL)
1921 if ((c = *(++pat)) == '\0') {
1925 for ( ; !match(pat, s, start1 + 1, len1 + 1); (*len1)++, s++)
1932 if ((c = *(++pat)) == '\0') {
1937 for (*len1=0; !match(pat, s, start1+1, len1+1); (*len1)++, s++)
1962 int matched = 0, notin = 0, inrange = 0;
1965 if ((c = *(++pat)) == '^') {
1970 if (c == '-' && !inrange)
1977 if (*s >= prevc && *s <= c)
1987 if (inrange && *s >= prevc)
1989 if (!(matched ^ notin))
2010 static void makerep()
2017 char *p, *pat, c, pc;
2021 for (pat = to, l = 0; (c = *pat) != '\0'; pat++, l++) {
2029 else if (c == 'u') {
2036 for(x = 0; ;x *= 10) {
2044 if (l + len[x] >= MAXPATH)
2048 *(start[x]) == '.' &&
2055 if (l + STRLEN(EMPTY) >= MAXPATH)
2065 memmove(p, start[x], len[x]);
2070 for (i = len[x], q = start[x]; i > 0; i--, p++, q++)
2074 for (i = len[x], q = start[x]; i > 0; i--, p++, q++)
2092 p == fullrep ? pat != to :
2095 (pc = *(p - 1)) == SLASH
2105 if (l + STRLEN(EMPTY) >= MAXPATH)
2115 strcpy(fullrep, EMPTY);
2123 strcpy(fullrep, TOOLONG);
2127 static void checkcollisions()
2131 int i, mult, oldnreps;
2135 rd = (REPDICT *)myalloc(nreps * sizeof(REPDICT));
2137 q = &hrep, p = q->r_next, prd = rd, i = 0;
2139 q = p, p = p->r_next, prd++, i++
2142 prd->rd_dto = p->r_hto->h_di;
2143 prd->rd_nto = p->r_nto;
2146 qsort(rd, nreps, sizeof(REPDICT), rdcmp);
2148 for (i = 0, prd = rd, oldnreps = nreps; i < oldnreps; i++, prd++)
2151 prd->rd_dto == (prd + 1)->rd_dto &&
2152 strcmp(prd->rd_nto, (prd + 1)->rd_nto) == 0
2158 printf("%s%s", prd->rd_p->r_hfrom->h_name,
2159 prd->rd_p->r_ffrom->fi_name);
2160 prd->rd_p->r_flags |= R_SKIP;
2161 prd->rd_p->r_ffrom->fi_rep = MISTAKE;
2166 prd->rd_p->r_flags |= R_SKIP;
2167 prd->rd_p->r_ffrom->fi_rep = MISTAKE;
2170 printf(" , %s%s -> %s%s : collision.\n",
2171 prd->rd_p->r_hfrom->h_name, prd->rd_p->r_ffrom->fi_name,
2172 prd->rd_p->r_hto->h_name, prd->rd_nto);
2175 chgive(rd, oldnreps * sizeof(REPDICT));
2179 static int rdcmp(rd1, rd2)
2185 (ret = rd1->rd_dto - rd2->rd_dto) == 0 &&
2186 (ret = strcmp(rd1->rd_nto, rd2->rd_nto)) == 0
2188 ret = rd1->rd_i - rd2->rd_i;
2193 static void findorder()
2195 REP *p, *q, *t, *first, *pred;
2198 for (q = &hrep, p = q->r_next; p != NULL; q = p, p = p->r_next)
2199 if (p->r_flags & R_SKIP) {
2200 q->r_next = p->r_next;
2204 (fi = p->r_fdel) == NULL ||
2205 (pred = fi->fi_rep) == NULL ||
2209 else if ((first = pred->r_first) == p) {
2210 p->r_flags |= R_ISCYCLE;
2211 pred->r_flags |= R_ISALIASED;
2218 while (pred->r_thendo != NULL)
2219 pred = pred->r_thendo;
2221 for (t = p; t != NULL; t = t->r_thendo)
2223 q->r_next = p->r_next;
2229 static void nochains()
2233 for (q = &hrep, p = q->r_next; p != NULL; q = p, p = p->r_next)
2234 if (p->r_flags & R_ISCYCLE || p->r_thendo != NULL) {
2236 printf("%s%s : no chain copies allowed.\n",
2237 p->r_hto->h_name, p->r_nto);
2238 q->r_next = p->r_next;
2244 static void printchain(p)
2247 if (p->r_thendo != NULL)
2248 printchain(p->r_thendo);
2249 printf("%s%s -> ", p->r_hfrom->h_name, p->r_ffrom->fi_name);
2252 p->r_ffrom->fi_rep = MISTAKE;
2256 static void scandeletes(pkilldel)
2261 for (q = &hrep, p = q->r_next; p != NULL; q = p, p = p->r_next) {
2262 if (p->r_fdel != NULL)
2263 while ((*pkilldel)(p)) {
2265 p->r_ffrom->fi_rep = MISTAKE;
2266 if ((n = p->r_thendo) != NULL) {
2268 n->r_fdel = p->r_ffrom;
2269 n->r_next = p->r_next;
2273 q->r_next = p->r_next;
2282 static int baddel(p)
2285 HANDLE *hfrom = p->r_hfrom, *hto = p->r_hto;
2286 FILEINFO *fto = p->r_fdel;
2287 char *t = fto->fi_name, *f = p->r_ffrom->fi_name;
2288 char *hnf = hfrom->h_name, *hnt = hto->h_name;
2290 if (delstyle == NODEL && !(p->r_flags & R_DELOK) && !(op & APPEND))
2291 printf("%s%s -> %s%s : old %s%s would have to be %s.\n",
2292 hnf, f, hnt, t, hnt, t,
2293 (op & OVERWRITE) ? "overwritten" : "deleted");
2294 else if (fto->fi_rep == MISTAKE)
2295 printf("%s%s -> %s%s : old %s%s was to be done first.\n",
2296 hnf, f, hnt, t, hnt, t);
2299 fto->fi_attrib & FA_DIREC
2301 fto->fi_stflags & FI_ISDIR
2304 printf("%s%s -> %s%s : %s%s%s is a directory.\n",
2305 hnf, f, hnt, t, (op & APPEND) ? "" : "old ", hnt, t);
2307 else if ((fto->fi_stflags & FI_NODEL) && !(op & (APPEND | OVERWRITE)))
2308 printf("%s%s -> %s%s : old %s%s lacks delete permission.\n",
2309 hnf, f, hnt, t, hnt, t);
2312 (op & (APPEND | OVERWRITE)) &&
2314 fto->fi_attrib & FA_RDONLY
2316 !fwritable(hnt, fto)
2319 printf("%s%s -> %s%s : %s%s %s.\n",
2320 hnf, f, hnt, t, hnt, t,
2323 fto->fi_stflags & FI_LINKERR ?
2324 "is a badly aimed symbolic link" :
2327 "lacks write permission");
2336 static int skipdel(p)
2339 if (p->r_flags & R_DELOK)
2341 fprintf(stderr, "%s%s -> %s%s : ",
2342 p->r_hfrom->h_name, p->r_ffrom->fi_name,
2343 p->r_hto->h_name, p->r_nto);
2346 p->r_fdel->fi_attrib & FA_RDONLY
2349 !(p->r_ffrom->fi_stflags & FI_ISLNK) &&
2351 !fwritable(p->r_hto->h_name, p->r_fdel)
2354 fprintf(stderr, "old %s%s lacks write permission. delete it",
2355 p->r_hto->h_name, p->r_nto);
2357 fprintf(stderr, "%s old %s%s",
2358 (op & OVERWRITE) ? "overwrite" : "delete",
2359 p->r_hto->h_name, p->r_nto);
2360 return(!getreply("? ", -1));
2364 static void goonordie()
2366 if ((paterr || badreps) && nreps > 0) {
2367 fprintf(stderr, "Not everything specified can be done.");
2368 if (badstyle == ABORTBAD) {
2369 fprintf(stderr, " Aborting.\n");
2372 else if (badstyle == SKIPBAD)
2373 fprintf(stderr, " Proceeding with the rest.\n");
2374 else if (!getreply(" Proceed with the rest? ", -1))
2380 static void doreps()
2383 int k, printaliased = 0, alias;
2390 signal(SIGINT, breakrep);
2393 for (first = hrep.r_next, k = 0; first != NULL; first = first->r_next) {
2394 for (p = first; p != NULL; p = p->r_thendo, k++) {
2397 fprintf(stderr, "User break.\n");
2398 printaliased = snap(first, p);
2401 strcpy(fullrep, p->r_hto->h_name);
2402 strcat(fullrep, p->r_nto);
2403 if (!noex && (p->r_flags & R_ISCYCLE))
2405 aliaslen = appendalias(first, p, &printaliased);
2407 alias = movealias(first, p, &printaliased);
2408 strcpy(pathbuf, p->r_hfrom->h_name);
2409 fstart = pathbuf + strlen(pathbuf);
2410 if ((p->r_flags & R_ISALIASED) && !(op & APPEND))
2411 sprintf(fstart, "%s%03d", TEMP, alias);
2413 strcpy(fstart, p->r_ffrom->fi_name);
2415 if (p->r_fdel != NULL && !(op & (APPEND | OVERWRITE)))
2416 myunlink(fullrep, p->r_fdel);
2418 (op & (COPY | APPEND)) ?
2420 p->r_flags & R_ISALIASED ? aliaslen : -1L) :
2423 link(pathbuf, fullrep) :
2426 symlink((p->r_flags & R_ONEDIRLINK) ? fstart : pathbuf,
2430 p->r_flags & R_ISX ?
2433 rename(pathbuf, fullrep)
2436 "%s -> %s has failed.\n", pathbuf, fullrep);
2437 printaliased = snap(first, p);
2440 if (verbose || noex) {
2441 if (p->r_flags & R_ISALIASED && !printaliased)
2442 strcpy(fstart, p->r_ffrom->fi_name);
2443 fprintf(outfile, "%s %c%c %s%s%s\n",
2445 p->r_flags & R_ISALIASED ? '=' : '-',
2446 p->r_flags & R_ISCYCLE ? '^' : '>',
2448 (p->r_fdel != NULL && !(op & APPEND)) ? " (*)" : "",
2449 noex ? "" : " : done");
2455 fprintf(stderr, "Strange, did %d reps; %d were expected.\n",
2458 fprintf(stderr, "Nothing done.\n");
2462 static long appendalias(first, p, pprintaliased)
2471 if ((fd = open(fullrep, O_RDONLY | O_BINARY, 0)) < 0) {
2472 fprintf(stderr, "stat on %s has failed.\n", fullrep);
2473 *pprintaliased = snap(first, p);
2476 ret = filelength(fd);
2482 if (stat(fullrep, &fstat)) {
2483 fprintf(stderr, "append cycle stat on %s has failed.\n", fullrep);
2484 *pprintaliased = snap(first, p);
2487 ret = fstat.st_size;
2494 static int movealias(first, p, pprintaliased)
2501 strcpy(pathbuf, p->r_hto->h_name);
2502 fstart = pathbuf + strlen(pathbuf);
2503 strcpy(fstart, TEMP);
2506 sprintf(fstart + STRLEN(TEMP), "%03d", ret),
2507 fsearch(fstart, p->r_hto->h_di) != NULL;
2511 if (rename(fullrep, pathbuf)) {
2513 "%s -> %s has failed.\n", fullrep, pathbuf);
2514 *pprintaliased = snap(first, p);
2520 static int snap(first, p)
2531 ctrlbrk((int (*)())breakstat);
2533 signal(SIGINT, breakstat);
2536 badstyle == ASKBAD &&
2537 isatty(fileno(stdout)) &&
2538 getreply("Redirect standard output to file? ", 0)
2545 fprintf(stderr, "File name> "),
2546 (outfile = fopen(mygets(fname, 80), "w")) == NULL
2548 fprintf(stderr, "Can't open %s.\n", fname);
2550 if (redirected || !verbose)
2552 fprintf(outfile, "The following left undone:\n");
2558 static void showdone(fin)
2563 for (first = hrep.r_next; ; first = first->r_next)
2564 for (p = first; p != NULL; p = p->r_thendo) {
2567 fprintf(outfile, "%s%s %c%c %s%s : done%s\n",
2568 p->r_hfrom->h_name, p->r_ffrom->fi_name,
2569 p->r_flags & R_ISALIASED ? '=' : '-',
2570 p->r_flags & R_ISCYCLE ? '^' : '>',
2571 p->r_hto->h_name, p->r_nto,
2572 (p->r_fdel != NULL && !(op & APPEND)) ? " (*)" : "");
2577 static void breakout()
2580 fprintf(stderr, "Aborting, nothing done.\n");
2585 static void breakrep(int signum)
2592 static void breakstat()
2600 fprintf(stderr, "Aborting, nothing done.\n");
2605 static int copymove(p)
2612 char linkbuf[MAXPATH];
2614 if ((llen = readlink(pathbuf, linkbuf, MAXPATH - 1)) >= 0) {
2615 linkbuf[llen] = '\0';
2616 return(symlink(linkbuf, fullrep) || myunlink(pathbuf, p->r_ffrom));
2621 return(copy(p->r_ffrom, -1L) || myunlink(pathbuf, p->r_ffrom));
2626 #define IRWMASK (S_IREAD | S_IWRITE)
2627 #define RWMASK (IRWMASK | (IRWMASK >> 3) | (IRWMASK >> 6))
2629 static int copy(ff, len)
2633 char buf[BUFSIZE], c;
2634 int f, t, k, mode, perm;
2641 struct timeval tim[2];
2646 if ((f = open(pathbuf, O_RDONLY | O_BINARY, 0)) < 0)
2650 IRWMASK /* will _chmod it later (to get all the attributes) */
2652 (op & (APPEND | OVERWRITE)) ?
2653 (~oldumask & RWMASK) | (ff->fi_mode & ~RWMASK) :
2661 (((t = open(fullrep, O_RDWR)) < 0 && errno == ENOENT)
2663 t = creat(fullrep, perm);
2665 mode = O_CREAT | (op & APPEND ? 0 : O_TRUNC) |
2667 O_BINARY | (op & ZAPPEND ? O_RDWR : O_WRONLY)
2672 t = open(fullrep, mode, perm);
2679 lseek(t, (off_t)0, SEEK_END);
2681 if (op & ZAPPEND && filelength(t) != 0) {
2682 if (lseek(t, -1L, 1) == -1L || read(t, &c, 1) != 1) {
2691 if ((op & APPEND) && len != (off_t)-1) {
2694 (k = read(f, buf, (len > BUFSIZE) ? BUFSIZE : (size_t)len)) > 0 &&
2695 write(t, buf, k) == k
2702 while ((k = read(f, buf, BUFSIZE)) > 0 && write(t, buf, k) == k)
2704 if (!(op & (APPEND | OVERWRITE)))
2707 getftime(f, &tim) ||
2708 setftime(t, &tim) ||
2709 _chmod(fullrep, 1, ff->fi_attrib) == -1
2711 stat(pathbuf, &fstat) ||
2714 tim.actime = fstat.st_atime,
2715 tim.modtime = fstat.st_mtime,
2717 tim[0].tv_sec = fstat.st_atime,
2718 tim[1].tv_sec = fstat.st_mtime,
2720 utimes(fullrep, tim)
2724 fprintf(stderr, "Strange, couldn't transfer time from %s to %s.\n",
2742 static int rename(from, to)
2747 if (link(from, to) == 0 && unlink(from) == 0)
2751 if (stat(from, &s) < 0 || (s.st_mode&S_IFMT) != S_IFDIR)
2755 do pid = fork(); while (pid >= 0 && errno == EAGAIN);
2759 else if (pid == 0) {
2760 execl(MV_DIR, "mv_dir", from, to, (char *) 0);
2763 } else if (pid > 0) {
2767 do wid = wait(&status);
2768 while (wid != pid && wid >= 0);
2770 return(status == 0 ? 0 : -1);
2775 static int rename(from, to)
2789 static int myunlink(n, f)
2796 if (((a = f->fi_attrib) & FA_RDONLY) && _chmod(n, 1, a & ~FA_RDONLY) < 0) {
2797 fprintf(stderr, "Strange, can not _chmod (or unlink) %s.\n", f);
2802 fprintf(stderr, "Strange, can not unlink %s.\n", n);
2809 static int getreply(m, failact)
2813 static FILE *tty = NULL;
2817 if (tty == NULL && (tty = fopen(TTY, "r")) == NULL) {
2818 fprintf(stderr, "Can not open %s to get reply.\n", TTY);
2827 fprintf(stderr, "Can not get reply.\n");
2834 while ((c = fgetc(tty)) != '\n' && c != EOF)
2837 if (r == 'y' || r == 'n')
2839 fprintf(stderr, "Yes or No? ");
2844 static void *myalloc(k)
2851 if ((ret = (void *)malloc(k)) == NULL) {
2852 fprintf(stderr, "Insufficient memory.\n");
2859 static void *challoc(k, which)
2865 SLICER *sl = &(slicer[which]);
2867 if (k > sl->sl_len) {
2869 q = NULL, p = freechunks;
2870 p != NULL && (sl->sl_len = p->ch_len) < k;
2871 q = p, p = p->ch_next
2875 sl->sl_len = CHUNKSIZE - sizeof(CHUNK *);
2876 p = (CHUNK *)myalloc(CHUNKSIZE);
2879 freechunks = p->ch_next;
2881 q->ch_next = p->ch_next;
2882 p->ch_next = sl->sl_first;
2884 sl->sl_unused = (char *)&(p->ch_len);
2887 ret = (void *)sl->sl_unused;
2893 static void chgive(p, k)
2897 ((CHUNK *)p)->ch_len = k - sizeof(CHUNK *);
2898 ((CHUNK *)p)->ch_next = freechunks;
2899 freechunks = (CHUNK *)p;
2906 static void memmove(to, from, k)
2912 *(to++) = *(from++);
2917 *(--to) = *(--from);
2927 static int lastc = 0;
2931 return(lastc = getchar());
2935 static char *mygets(s, l)
2942 if (fgets(s, l, stdin) == NULL)
2944 if ((nl = strchr(s, '\n')) != NULL)
2946 fprintf(stderr, "Input string too long. Try again> ");
2959 static void cleanup()
2963 if (patch.ph_safeid) {
2964 for (i = 0; i < nhandles; i++) {
2965 if (!(handles[i]->h_di->di_flags & DI_CLEANED)) {
2966 sprintf(pathbuf, "%s%s%03d",
2967 handles[i]->h_name, IDF, handles[i]->h_di->di_did);
2968 if (unlink(pathbuf))
2969 fprintf(stderr, "Strange, couldn't unlink %s.\n", pathbuf);
2970 handles[i]->h_di->di_flags |= DI_CLEANED;
2975 Write device availability: undocumented internal MS-D*S function.
2976 Restore previous value.
2978 bdos(0x37, olddevflag, 3);