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 */
129 #include <sys/types.h>
130 #include <sys/stat.h>
131 #include <sys/file.h>
135 typedef struct dirent DIRENTRY;
139 /* might need to be changed to <dir.h> */
143 typedef struct direct DIRENTRY;
150 #define void char /* might want to remove this line */
173 static char TTY[] = "/dev/tty";
180 extern char *strcpy(), *strchr();
187 /* for System V and BSD */
195 /* for System V and Version 7*/
200 #define utimes(f, t) utime((f), &(t))
204 # define MV_DIR "/usr/lib/mv_dir"
217 #include <sys/time.h>
222 #define mylower(c) (isupper(c) ? (c)-'A'+'a' : (c))
223 #define myupper(c) (islower(c) ? (c)-'a'+'A' : (c))
224 #define STRLEN(s) (sizeof(s) - 1)
225 #define mydup(s) (strcpy((char *)challoc(strlen(s) + 1, 0), (s)))
229 #define NORMCOPY 0x002
230 #define OVERWRITE 0x004
231 #define NORMMOVE 0x008
233 #define DIRMOVE 0x020
234 #define NORMAPPEND 0x040
235 #define ZAPPEND 0x080
236 #define HARDLINK 0x100
237 #define SYMLINK 0x200
239 #define COPY (NORMCOPY | OVERWRITE)
240 #define MOVE (NORMMOVE | XMOVE | DIRMOVE)
241 #define APPEND (NORMAPPEND | ZAPPEND)
242 #define LINK (HARDLINK | SYMLINK)
244 static char MOVENAME[] = "mmv";
245 static char COPYNAME[] = "mcp";
246 static char APPENDNAME[] = "mad";
247 static char LINKNAME[] = "mln";
262 #define MAXPATLEN MAXPATH
264 #define CHUNKSIZE 2048
267 #define FI_STTAKEN 0x01
268 #define FI_LINKERR 0x02
269 #define FI_INSTICKY 0x04
270 #define FI_NODEL 0x08
271 #define FI_KNOWWRITE 0x010
272 #define FI_CANWRITE 0x20
273 #define FI_ISDIR 0x40
274 #define FI_ISLNK 0x80
287 #define DI_KNOWWRITE 0x01
288 #define DI_CANWRITE 0x02
289 #define DI_CLEANED 0x04
300 #define H_NOREADDIR 2
311 #define R_ISALIASED 0x08
312 #define R_ISCYCLE 0x10
313 #define R_ONEDIRLINK 0x20
319 char *r_nto; /* non-path part of new name */
322 struct rep *r_thendo;
334 typedef struct chunk {
335 struct chunk *ch_next;
346 static void init(/* */);
347 static void procargs(/* int argc, char **argv,
348 char **pfrompat, char **ptopat */);
349 static void domatch(/* char *cfrom, char *cto */);
350 static int getpat(/* */);
351 static int getword(/* char *buf */);
352 static void matchpat(/* */);
353 static int parsepat(/* */);
354 static int dostage(/* char *lastend, char *pathend,
355 char **start1, int *len1, int stage, int anylev */);
356 static int trymatch(/* FILEINFO *ffrom, char *pat */);
357 static int keepmatch(/* FILEINFO *ffrom, char *pathend,
358 int *pk, int needslash, int dirs, int fils */);
359 static int badrep(/* HANDLE *hfrom, FILEINFO *ffrom,
360 HANDLE **phto, char **pnto, FILEINFO **pfdel, int *pflags */);
361 static int checkto(/* HANDLE *hfrom, char *f,
362 HANDLE **phto, char **pnto, FILEINFO **pfdel */);
363 static char *getpath(/* char *tpath */);
364 static int badname(/* char *s */);
365 static FILEINFO *fsearch(/* char *s, DIRINFO *d */);
366 static int ffirst(/* char *s, int n, DIRINFO *d */);
367 static HANDLE *checkdir(/* char *p, char *pathend, int which */);
368 static void takedir(/*
369 char *p, DIRINFO *di, int sticky
371 struct ffblk *pff, DIRINFO *di
373 static int fcmp(/* FILEINFO **pf1, FILEINFO **pf2 */);
374 static HANDLE *hadd(/* char *n */);
375 static int hsearch(/* char *n, int which, HANDLE **ph */);
376 static DIRINFO *dadd(/* DEVID v, DIRID d */);
377 static DIRINFO *dsearch(/* DEVID v, DIRID d */);
378 static int match(/* char *pat, char *s, char **start1, int *len1 */);
379 static void makerep(/* */);
380 static void checkcollisions(/* */);
381 static int rdcmp(/* REPDICT *rd1, REPDICT *rd2 */);
382 static void findorder(/* */);
383 static void scandeletes(/* int (*pkilldel)(REP *p) */);
384 static int baddel(/* REP *p */);
385 static int skipdel(/* REP *p */);
386 static void nochains(/* */);
387 static void printchain(/* REP *p */);
388 static void goonordie(/* */);
389 static void doreps(/* */);
390 static long appendalias(/* REP *first, REP *p, int *pprintaliased */);
391 static int movealias(/* REP *first, REP *p, int *pprintaliased */);
392 static int snap(/* REP *first, REP *p */);
393 static void showdone(/* REP *fin */);
394 static void breakout(/* */);
395 static void breakrep(int);
396 static void breakstat(/* */);
397 static void quit(/* */);
398 static int copymove(/* REP *p */);
399 static int copy(/* FILENFO *f, long len */);
400 static int myunlink(/* char *n, FILEINFO *f */);
401 static int getreply(/* char *m, int failact */);
402 static void *myalloc(/* unsigned k */);
403 static void *challoc(/* int k, int which */);
404 static void chgive(/* void *p, unsigned k */);
405 static int mygetc(/* */);
406 static char *mygets(/* char *s, int l */);
408 static int leave(/* */);
409 static void cleanup(/* */);
411 static int getstat(/* char *full, FILEINFO *f */);
412 static int dwritable(/* HANDLE *h */);
413 static int fwritable(/* char *hname, FILEINFO *f */);
417 static void memmove(/* void *to, void *from, int k */);
423 static int rename(/* char *from, char *to */);
426 static int op, badstyle, delstyle, verbose, noex, matchall;
429 static unsigned ndirs = 0, dirroom;
430 static DIRINFO **dirs;
431 static unsigned nhandles = 0, handleroom;
432 static HANDLE **handles;
433 static HANDLE badhandle = {"\200", NULL, 0};
434 static HANDLE *(lasthandle[2]) = {&badhandle, &badhandle};
435 static unsigned nreps = 0;
436 static REP hrep, *lastrep = &hrep;
437 static CHUNK *freechunks = NULL;
438 static SLICER slicer[2] = {{NULL, NULL, 0}, {NULL, NULL, 0}};
440 static int badreps = 0, paterr = 0, direrr, failed = 0, gotsig = 0, repbad;
441 static FILE *outfile;
444 static char IDF[] = "$$mmvdid.";
446 static char TEMP[] = "$$mmvtmp.";
447 static char TOOLONG[] = "(too long)";
448 static char EMPTY[] = "(empty)";
450 static char SLASHSTR[] = {SLASH, '\0'};
452 static char PATLONG[] = "%.40s... : pattern too long.\n";
454 char from[MAXPATLEN], to[MAXPATLEN];
455 static int fromlen, tolen;
456 static char *(stagel[MAXWILD]), *(firstwild[MAXWILD]), *(stager[MAXWILD]);
457 static int nwilds[MAXWILD];
459 char pathbuf[MAXPATH];
460 char fullrep[MAXPATH + 1];
461 static char *(start[MAXWILD]);
462 static int len[MAXWILD];
464 #define MISTAKE (&mistake)
468 static char hasdot[MAXWILD];
469 static int olddevflag, curdisk, maxdisk;
478 } patch = {"mmv 1.0 patchable flags", "mmv", XMOVE, 1, 0};
480 #define DFLTOP (patch.ph_dfltop)
481 #define CLUSTNO(pff) (*(int *)(((char *)(pff)) + patch.ph_clustoff))
482 #define DRIVENO(pff) (*(((char *)(pff)) + patch.ph_driveoff) - patch.ph_drivea)
491 static int uid, euid, oldumask;
492 static DIRID cwdd = -1;
493 static DEVID cwdv = -1;
502 char *frompat, *topat;
507 procargs(argc, argv, &frompat, &topat);
508 domatch(frompat, topat);
512 if (op & (COPY | LINK))
516 if (!(op & APPEND) && delstyle == ASKDEL)
517 scandeletes(skipdel);
519 return(failed ? 2 : nreps == 0 && (paterr || badreps));
527 maxdisk = setdisk(curdisk);
529 Read device availability : undocumented internal MS-DOS function.
530 If (_DX == 0) then \dev\ must precede device names.
535 Write device availability: undocumented internal MS-DOS function.
536 Specify \dev\ must precede device names.
539 atexit((atexit_t)cleanup);
540 ctrlbrk((int (*)())breakout);
544 if ((home = getenv("HOME")) == NULL || strcmp(home, SLASHSTR) == 0)
546 if (!stat(".", &dstat)) {
553 signal(SIGINT, breakout);
556 dirroom = handleroom = INITROOM;
557 dirs = (DIRINFO **)myalloc(dirroom * sizeof(DIRINFO *));
558 handles = (HANDLE **)myalloc(handleroom * sizeof(HANDLE *));
559 ndirs = nhandles = 0;
563 static void procargs(argc, argv, pfrompat, ptopat)
566 char **pfrompat, **ptopat;
569 char *cmdname = basename(argv[0]);
572 #define CMDNAME (patch.ph_name)
574 #define CMDNAME cmdname
578 verbose = noex = matchall = 0;
581 for (argc--, argv++; argc > 0 && **argv == '-'; argc--, argv++)
582 for (p = *argv + 1; *p != '\0'; p++) {
589 if (c == 'v' && !noex)
591 else if (c == 'n' && !verbose)
595 else if (c == 'd' && delstyle == ASKDEL)
597 else if (c == 'p' && delstyle == ASKDEL)
599 else if (c == 'g' && badstyle == ASKBAD)
601 else if (c == 't' && badstyle == ASKBAD)
603 else if (c == 'm' && op == DFLT)
605 else if (c == 'x' && op == DFLT)
607 else if (c == 'r' && op == DFLT)
609 else if (c == 'c' && op == DFLT)
611 else if (c == 'o' && op == DFLT)
613 else if (c == 'a' && op == DFLT)
616 else if (c == 'z' && op == DFLT)
619 else if (c == 'l' && op == DFLT)
622 else if (c == 's' && op == DFLT)
627 fprintf(stderr, USAGE, CMDNAME, OTHEROPT);
634 if (strcmp(cmdname, MOVENAME) == 0)
636 else if (strcmp(cmdname, COPYNAME) == 0)
638 else if (strcmp(cmdname, APPENDNAME) == 0)
640 else if (strcmp(cmdname, LINKNAME) == 0)
659 "Unable to do directory renames. Option -r refused.\n");
664 if (euid != uid && !(op & DIRMOVE)) {
670 if (badstyle != ASKBAD && delstyle == ASKDEL)
675 else if (argc == 2) {
676 *pfrompat = *(argv++);
680 fprintf(stderr, USAGE, CMDNAME, OTHEROPT);
686 static void domatch(cfrom, cto)
692 else if ((fromlen = strlen(cfrom)) >= MAXPATLEN) {
693 printf(PATLONG, cfrom);
696 else if ((tolen = strlen(cto)) >= MAXPATLEN) {
697 printf(PATLONG, cto);
711 char extra[MAXPATLEN];
715 if ((fromlen = getword(from)) == 0 || fromlen == -1)
719 if ((tolen = getword(to)) == 0) {
720 printf("%s -> ? : missing replacement pattern.\n", from);
727 (to[0] == '-' || to[0] == '=') &&
728 (to[1] == '>' || to[1] == '^')
730 if (getword(extra) == 0)
732 else if (strcmp(extra, "(*)") == 0) {
734 gotit = (getword(extra) == 0);
738 while ((c = mygetc()) != '\n' && c != EOF)
748 static int getword(buf)
757 while ((c = mygetc()) != EOF && (prevc == ESC || !isspace(c))) {
760 if (n == MAXPATLEN - 1) {
762 printf(PATLONG, buf);
770 while (c != EOF && isspace(c) && c != '\n')
778 static void matchpat()
782 else if (dostage(from, pathbuf, start, len, 0, 0)) {
783 printf("%s -> %s : no match.\n", from, to);
789 static int parsepat()
791 char *p, *lastname, c;
792 int totwilds, instage, x;
793 static char TRAILESC[] = "%s -> %s : trailing %c is superfluous.\n";
798 if (from[0] != '\0' && from[1] == ':')
801 if (from[0] == '~' && from[1] == SLASH) {
802 if ((homelen = strlen(home)) + fromlen > MAXPATLEN) {
803 printf(PATLONG, from);
806 memmove(from + homelen, from + 1, fromlen);
807 memmove(from, home, homelen);
808 lastname += homelen + 1;
811 totwilds = nstages = instage = 0;
812 for (p = lastname; (c = *p) != '\0'; p++)
823 if (!havedot && lastname != p) {
824 if (fromlen++ == MAXPATLEN) {
825 printf(PATLONG, from);
828 memmove(p + 1, p, strlen(p) + 1);
836 if (firstwild[nstages] == NULL)
837 firstwild[nstages] = p;
838 stager[nstages++] = p;
844 printf("%s -> %s : badly placed ;.\n", from, to);
852 if ((hasdot[totwilds] = (c == '!')) != 0)
855 if (totwilds++ == MAXWILD) {
856 printf("%s -> %s : too many wildcards.\n", from, to);
861 if (firstwild[nstages] == NULL)
862 firstwild[nstages] = p;
865 stagel[nstages] = lastname;
866 firstwild[nstages] = (c == ';' ? NULL : p);
872 while ((c = *(++p)) != ']') {
875 printf("%s -> %s : missing ].\n", from, to);
883 printf("%s -> %s : '%c' can not be part of [].\n",
887 if ((c = *(++p)) == '\0') {
888 printf(TRAILESC, from, to, ESC);
894 *p = c + ('a' - 'A');
900 if ((c = *(++p)) == '\0') {
901 printf(TRAILESC, from, to, ESC);
907 *p = c + ('a' - 'A');
912 if (!havedot && lastname != p) {
913 if (fromlen++ == MAXPATLEN) {
914 printf(PATLONG, from);
922 if (firstwild[nstages] == NULL)
923 firstwild[nstages] = p;
924 stager[nstages++] = p;
927 stagel[nstages] = lastname;
929 firstwild[nstages] = p;
930 stager[nstages++] = p;
936 if (to[0] != '\0' && to[1] == ':')
939 if (to[0] == '~' && to[1] == SLASH) {
940 if ((homelen = strlen(home)) + tolen > MAXPATLEN) {
944 memmove(to + homelen, to + 1, tolen);
945 memmove(to, home, homelen);
946 lastname += homelen + 1;
950 for (p = lastname; (c = *p) != '\0'; p++)
961 printf("%s -> %s : no path allowed in target under -r.\n",
966 if (!havedot && lastname != p) {
967 if (tolen++ == MAXPATLEN) {
971 memmove(p + 1, p, strlen(p) + 1);
981 if (c == 'l' || c == 'u') {
990 printf("%s -> %s : expected digit (not '%c') after #.\n",
994 for(x = 0; ;x *= 10) {
1001 if (x < 1 || x > totwilds) {
1002 printf("%s -> %s : wildcard #%d does not exist.\n",
1012 if ((c = *(++p)) == '\0') {
1013 printf(TRAILESC, from, to, ESC);
1019 c <= ' ' || c >= 127 ||
1020 strchr(":/\\*?[]=+;,\"|<>", c) != NULL
1022 printf("%s -> %s : illegal character '%c' (0x%02X).\n",
1027 *p = c + ('a' - 'A');
1032 if (!havedot && lastname != p) {
1033 if (tolen++ == MAXPATLEN) {
1034 printf(PATLONG, to);
1045 static int dostage(lastend, pathend, start1, len1, stage, anylev)
1046 char *lastend, *pathend;
1054 int prelen, litlen, nfils, i, k, flags, try;
1055 FILEINFO **pf, *fdel = NULL;
1056 char *nto, *firstesc;
1058 int wantdirs, ret = 1, laststage = (stage + 1 == nstages);
1060 wantdirs = !laststage ||
1061 (op & (DIRMOVE | SYMLINK)) ||
1062 (nwilds[nstages - 1] == 0);
1065 prelen = stagel[stage] - lastend;
1066 if (pathend - pathbuf + prelen >= MAXPATH) {
1067 printf("%s -> %s : search path after %s too long.\n",
1072 memmove(pathend, lastend, prelen);
1075 lastend = stagel[stage];
1078 if ((h = checkdir(pathbuf, pathend, 0)) == NULL) {
1079 if (stage == 0 || direrr == H_NOREADDIR) {
1080 printf("%s -> %s : directory %s does not %s.\n",
1081 from, to, pathbuf, direrr == H_NOREADDIR ?
1082 "allow reads/searches" : "exist");
1089 if (*lastend == ';') {
1096 nfils = di->di_nfils;
1099 if ((op & MOVE) && !dwritable(h)) {
1100 printf("%s -> %s : directory %s does not allow writes.\n",
1107 firstesc = strchr(lastend, ESC);
1108 if (firstesc == NULL || firstesc > firstwild[stage])
1109 firstesc = firstwild[stage];
1110 litlen = firstesc - lastend;
1111 pf = di->di_fils + (i = ffirst(lastend, litlen, di));
1115 (try = trymatch(*pf, lastend)) != 0 &&
1118 match(lastend + litlen, (*pf)->fi_name + litlen,
1119 start1 + anylev, len1 + anylev)
1121 keepmatch(*pf, pathend, &k, 0, wantdirs, laststage)
1124 ret &= dostage(stager[stage], pathend + k,
1125 start1 + nwilds[stage], len1 + nwilds[stage],
1130 if (badrep(h, *pf, &hto, &nto, &fdel, &flags))
1131 (*pf)->fi_rep = MISTAKE;
1133 (*pf)->fi_rep = p = (REP *)challoc(sizeof(REP), 1);
1134 p->r_flags = flags | patflags;
1143 lastrep->r_next = p;
1150 } while (i < nfils && strncmp(lastend, (*pf)->fi_name, litlen) == 0);
1154 for (pf = di->di_fils, i = 0; i < nfils; i++, pf++)
1156 *((*pf)->fi_name) != '.' &&
1158 ((*pf)->fi_attrib & FA_DIREC) &&
1160 keepmatch(*pf, pathend, &k, 1, 1, 0)
1162 *len1 = pathend - *start1 + k;
1163 ret &= dostage(lastend, pathend + k, start1, len1, stage, 1);
1170 static int trymatch(ffrom, pat)
1176 if (ffrom->fi_rep != NULL)
1182 if (*p == '.' || (!matchall && ffrom->fi_attrib & (FA_HIDDEN | FA_SYSTEM)))
1183 return(strcmp(pat, p) == 0);
1186 if (p[1] == '\0' || (p[1] == '.' && p[2] == '\0'))
1187 return(strcmp(pat, p) == 0);
1188 else if (!matchall && *pat != '.')
1196 static int keepmatch(ffrom, pathend, pk, needslash, dirs, fils)
1203 *pk = strlen(ffrom->fi_name);
1204 if (pathend - pathbuf + *pk + needslash >= MAXPATH) {
1206 printf("%s -> %s : search path %s%s too long.\n",
1207 from, to, pathbuf, ffrom->fi_name);
1211 strcpy(pathend, ffrom->fi_name);
1213 if ((ffrom->fi_attrib & FA_DIREC) ? !dirs : !fils)
1215 getstat(pathbuf, ffrom);
1216 if ((ffrom->fi_stflags & FI_ISDIR) ? !dirs : !fils)
1221 strcpy(pathend + *pk, SLASHSTR);
1228 static int badrep(hfrom, ffrom, phto, pnto, pfdel, pflags)
1236 char *f = ffrom->fi_name;
1241 (ffrom->fi_attrib & FA_DIREC) &&
1243 (ffrom->fi_stflags & FI_ISDIR) &&
1245 !(op & (DIRMOVE | SYMLINK))
1247 printf("%s -> %s : source file is a directory.\n", pathbuf, fullrep);
1250 else if ((ffrom->fi_stflags & FI_LINKERR) && !(op & (MOVE | SYMLINK)))
1251 printf("%s -> %s : source file is a badly aimed symbolic link.\n",
1255 else if ((ffrom->fi_stflags & FI_NODEL) && (op & MOVE))
1256 printf("%s -> %s : no delete permission for source file.\n",
1259 else if ((op & (COPY | APPEND)) && access(pathbuf, R_OK))
1260 printf("%s -> %s : no read permission for source file.\n",
1265 (f[1] == '\0' || strcmp(f, "..") == 0) &&
1268 printf("%s -> %s : . and .. can't be renamed.\n", pathbuf, fullrep);
1269 else if (repbad || checkto(hfrom, f, phto, pnto, pfdel) || badname(*pnto))
1270 printf("%s -> %s : bad new name.\n", pathbuf, fullrep);
1271 else if (*phto == NULL)
1272 printf("%s -> %s : %s.\n", pathbuf, fullrep,
1274 direrr == H_NOREADDIR ?
1275 "no read or search permission for target directory" :
1277 "target directory does not exist");
1279 else if (!dwritable(*phto))
1280 printf("%s -> %s : no write permission for target directory.\n",
1284 (*phto)->h_di->di_vid != hfrom->h_di->di_vid &&
1285 (*pflags = R_ISX, (op & (NORMMOVE | HARDLINK)))
1287 printf("%s -> %s : cross-device move.\n",
1291 *pflags && (op & MOVE) &&
1292 !(ffrom->fi_stflags & FI_ISLNK) &&
1293 access(pathbuf, R_OK)
1295 printf("%s -> %s : no read permission for source file.\n",
1301 ((*phto)->h_di->di_vid == cwdv && (*phto)->h_di->di_did == cwdd) ||
1302 *(hfrom->h_name) == SLASH ||
1303 (*pflags |= R_ONEDIRLINK, hfrom->h_di == (*phto)->h_di)
1306 printf("%s -> %s : symbolic link would be badly aimed.\n",
1317 static int checkto(hfrom, f, phto, pnto, pfdel)
1324 char tpath[MAXPATH + 1];
1326 FILEINFO *fdel = NULL;
1331 hlen = strlen(hfrom->h_name);
1332 pathend = fullrep + hlen;
1333 memmove(pathend, fullrep, strlen(fullrep) + 1);
1334 memmove(fullrep, hfrom->h_name, hlen);
1335 if ((fdel = *pfdel = fsearch(pathend, hfrom->h_di)) != NULL) {
1336 *pnto = fdel->fi_name;
1338 getstat(fullrep, fdel);
1342 *pnto = mydup(pathend);
1345 pathend = getpath(tpath);
1346 hlen = pathend - fullrep;
1347 *phto = checkdir(tpath, tpath + hlen, 1);
1351 (fdel = *pfdel = fsearch(pathend, (*phto)->h_di)) != NULL &&
1353 (fdel->fi_attrib & FA_DIREC)
1355 (getstat(fullrep, fdel), fdel->fi_stflags & FI_ISDIR)
1358 tlen = strlen(pathend);
1359 strcpy(pathend + tlen, SLASHSTR);
1361 strcpy(tpath + hlen, pathend);
1364 *phto = checkdir(tpath, tpath + hlen, 1);
1367 if (*pathend == '\0') {
1369 if (pathend - fullrep + strlen(f) >= MAXPATH) {
1370 strcpy(fullrep, TOOLONG);
1374 if (*phto != NULL) {
1375 fdel = *pfdel = fsearch(f, (*phto)->h_di);
1378 getstat(fullrep, fdel);
1382 else if (fdel != NULL)
1383 *pnto = fdel->fi_name;
1385 *pnto = mydup(pathend);
1391 static char *getpath(tpath)
1394 char *pathstart, *pathend, c;
1397 if (*fullrep != '\0' && fullrep[1] == ':')
1398 pathstart = fullrep + 2;
1401 pathstart = fullrep;
1403 pathend = pathstart + strlen(pathstart) - 1;
1404 while (pathend >= pathstart && *pathend != SLASH)
1410 strcpy(tpath, fullrep);
1416 static int badname(s)
1427 (ext = strchr(s, '.')) - s >= MAXFILE ||
1428 (*ext == '.' && strchr(ext + 1, '.') != NULL) ||
1429 strlen(ext) >= MAXEXT ||
1430 strncmp(s, IDF, STRLEN(IDF)) == 0
1432 (*s == '.' && (s[1] == '\0' || strcmp(s, "..") == 0)) ||
1433 strlen(s) > MAXNAMLEN
1440 static int getstat(ffull, f)
1447 if ((flags = f->fi_stflags) & FI_STTAKEN)
1448 return(flags & FI_LINKERR);
1449 flags |= FI_STTAKEN;
1451 if (stat(ffull, &fstat)) {
1452 fprintf(stderr, "Strange, couldn't stat %s.\n", ffull);
1456 if (lstat(ffull, &fstat)) {
1457 fprintf(stderr, "Strange, couldn't lstat %s.\n", ffull);
1460 if ((flags & FI_INSTICKY) && fstat.st_uid != uid && uid != 0)
1462 if ((fstat.st_mode & S_IFMT) == S_IFLNK) {
1464 if (stat(ffull, &fstat)) {
1465 f->fi_stflags = flags | FI_LINKERR;
1470 if ((fstat.st_mode & S_IFMT) == S_IFDIR)
1472 f->fi_stflags = flags;
1473 f->fi_mode = fstat.st_mode;
1478 static int dwritable(h)
1481 char *p = h->h_name, *myp, *lastslash = NULL, *pathend;
1482 char *pw = &(h->h_di->di_flags), r;
1487 if (*pw & DI_KNOWWRITE)
1488 return(*pw & DI_CANWRITE);
1490 pathend = p + strlen(p);
1493 else if (pathend == p + 1)
1496 lastslash = pathend - 1;
1500 r = !access(myp, W_OK) ? DI_CANWRITE : 0;
1501 *pw |= DI_KNOWWRITE | r;
1503 if (lastslash != NULL)
1509 static int fwritable(hname, f)
1515 if (f->fi_stflags & FI_KNOWWRITE)
1516 return(f->fi_stflags & FI_CANWRITE);
1518 strcpy(fullrep, hname);
1519 strcat(fullrep, f->fi_name);
1520 r = !access(fullrep, W_OK) ? FI_CANWRITE : 0;
1521 f->fi_stflags |= FI_KNOWWRITE | r;
1527 static FILEINFO *fsearch(s, d)
1531 FILEINFO **fils = d->di_fils;
1532 int nfils = d->di_nfils;
1533 int first, k, last, res;
1535 for(first = 0, last = nfils - 1;;) {
1538 k = (first + last) >> 1;
1539 if ((res = strcmp(s, fils[k]->fi_name)) == 0)
1549 static int ffirst(s, n, d)
1554 int first, k, last, res;
1555 FILEINFO **fils = d->di_fils;
1556 int nfils = d->di_nfils;
1558 if (nfils == 0 || n == 0)
1563 k = (first + last) >> 1;
1564 res = strncmp(s, fils[k]->fi_name, n);
1566 return(res == 0 ? k : nfils);
1576 /* checkdir and takedir for MS-D*S */
1578 static HANDLE *checkdir(p, pathend, which)
1591 if (hsearch(p, which, &h))
1592 if (h->h_di == NULL) {
1599 if (*p == '\0' || p[1] != ':')
1603 v = mylower(p[0]) - 'a';
1604 if (v < 0 || v >= maxdisk)
1608 if (patch.ph_safeid) {
1609 strcpy(pathend, IDF);
1610 strcpy(pathend + STRLEN(IDF), "*");
1611 if (findfirst(p, &de, 0)) {
1612 if ((d = ndirs) == 1000) {
1613 fprintf(stderr, "Too many different directories.\n");
1616 sprintf(pathend + STRLEN(IDF), "%03d", d);
1617 if ((fd = _creat(p, 0)) < 0) {
1618 direrr = h->h_err = H_NODIR;
1622 strcpy(pathend, "*.*");
1623 if (findfirst(p, &de, FA_DIREC | FA_SYSTEM | FA_HIDDEN))
1624 h->h_di = dadd(v, d);
1626 takedir(&de, h->h_di = dadd(v, d));
1628 else if ((d = atoi(de.ff_name + STRLEN(IDF))) < ndirs)
1631 strcpy(pathend, de.ff_name);
1632 fprintf(stderr, "Strange dir-id file encountered: %s.\n", p);
1638 strcpy(pathend, "*.*");
1639 firstfound = !findfirst(p, &de, FA_DIREC | FA_SYSTEM | FA_HIDDEN);
1646 strcpy(pathend, "T.D");
1649 direrr = h->h_err = H_NODIR;
1652 strcpy(pathend, "*.*");
1653 firstfound = !findfirst(p, &de, FA_DIREC | FA_SYSTEM | FA_HIDDEN);
1658 if (!firstfound || d != 0) {
1660 "Strange, %s does not seem to be a root dir.\n",
1666 if ((di = dsearch(v, d)) == NULL)
1668 takedir(&de, h->h_di = dadd(v, d));
1670 h->h_di = dadd(v, d);
1679 static void takedir(pff, di)
1683 int cnt, room, namlen, needdot;
1684 FILEINFO **fils, *f;
1688 di->di_fils = fils = (FILEINFO **)myalloc(room * sizeof(FILEINFO *));
1691 if (strnicmp(pff->ff_name, IDF, STRLEN(IDF)) == 0)
1695 fils = (FILEINFO **)myalloc(room * sizeof(FILEINFO *));
1696 memcpy(fils, di->di_fils, cnt * sizeof(FILEINFO *));
1697 chgive(di->di_fils, cnt * sizeof(FILEINFO *));
1699 fils = di->di_fils + cnt;
1702 for (p = pff->ff_name, namlen = 0; (c = *p) != '\0'; p++, namlen++)
1705 *fils = f = (FILEINFO *)challoc(sizeof(FILEINFO), 1);
1706 f->fi_name = p = (char *)challoc(namlen + needdot + 1, 0);
1707 for (p1 = pff->ff_name; (c = *p1) != '\0'; p1++)
1708 *(p++) = mylower(c);
1712 f->fi_attrib = pff->ff_attrib;
1716 } while (findnext(pff) == 0);
1717 qsort(di->di_fils, cnt, sizeof(FILEINFO *), fcmp);
1722 /* checkdir, takedir for Un*x */
1724 static HANDLE *checkdir(p, pathend, which)
1732 char *myp, *lastslash = NULL;
1736 if (hsearch(p, which, &h)) {
1737 if (h->h_di == NULL) {
1747 else if (pathend == p + 1)
1750 lastslash = pathend - 1;
1755 if (stat(myp, &dstat) || (dstat.st_mode & S_IFMT) != S_IFDIR)
1756 direrr = h->h_err = H_NODIR;
1757 else if (access(myp, R_OK | X_OK))
1758 direrr = h->h_err = H_NOREADDIR;
1761 sticky = (dstat.st_mode & S_ISVTX) && uid != 0 && uid != dstat.st_uid ?
1766 if ((di = dsearch(v, d)) == NULL)
1767 takedir(myp, di = dadd(v, d), sticky);
1770 if (lastslash != NULL)
1779 static void takedir(p, di, sticky)
1786 FILEINFO *f, **fils;
1789 if ((dirp = opendir(p)) == NULL) {
1790 fprintf(stderr, "Strange, can't scan %s.\n", p);
1794 di->di_fils = fils = (FILEINFO **)myalloc(room * sizeof(FILEINFO *));
1796 while ((dp = readdir(dirp)) != NULL) {
1799 fils = (FILEINFO **)myalloc(room * sizeof(FILEINFO *));
1800 memcpy(fils, di->di_fils, cnt * sizeof(FILEINFO *));
1801 chgive(di->di_fils, cnt * sizeof(FILEINFO *));
1803 fils = di->di_fils + cnt;
1805 *fils = f = (FILEINFO *)challoc(sizeof(FILEINFO), 1);
1806 f->fi_name = mydup(dp->d_name);
1807 f->fi_stflags = sticky;
1813 qsort(di->di_fils, cnt, sizeof(FILEINFO *), fcmp);
1817 /* end of Un*x checkdir, takedir; back to general program */
1821 static int fcmp(pf1, pf2)
1822 FILEINFO **pf1, **pf2;
1824 return(strcmp((*pf1)->fi_name, (*pf2)->fi_name));
1828 static HANDLE *hadd(n)
1831 HANDLE **newhandles, *h;
1833 if (nhandles == handleroom) {
1835 newhandles = (HANDLE **)myalloc(handleroom * sizeof(HANDLE *));
1836 memcpy(newhandles, handles, nhandles * sizeof(HANDLE *));
1837 chgive(handles, nhandles * sizeof(HANDLE *));
1838 handles = newhandles;
1840 handles[nhandles++] = h = (HANDLE *)challoc(sizeof(HANDLE), 1);
1841 h->h_name = (char *)challoc(strlen(n) + 1, 0);
1842 strcpy(h->h_name, n);
1848 static int hsearch(n, which, pret)
1856 if (strcmp(n, lasthandle[which]->h_name) == 0) {
1857 *pret = lasthandle[which];
1861 for(i = 0, ph = handles; i < nhandles; i++, ph++)
1862 if (strcmp(n, (*ph)->h_name) == 0) {
1863 lasthandle[which] = *pret = *ph;
1867 lasthandle[which] = *pret = hadd(n);
1872 static DIRINFO *dadd(v, d)
1879 if (ndirs == dirroom) {
1881 newdirs = (DIRINFO **)myalloc(dirroom * sizeof(DIRINFO *));
1882 memcpy(newdirs, dirs, ndirs * sizeof(DIRINFO *));
1883 chgive(dirs, ndirs * sizeof(DIRINFO *));
1886 dirs[ndirs++] = di = (DIRINFO *)challoc(sizeof(DIRINFO), 1);
1896 static DIRINFO *dsearch(v, d)
1903 for(i = 0, di = *dirs; i < ndirs; i++, di++)
1904 if (v == di->di_vid && d == di->di_did)
1910 static int match(pat, s, start1, len1)
1911 char *pat, *s, **start1;
1928 if ((s = strchr(s, '.')) == NULL)
1932 if ((c = *(++pat)) == '\0') {
1936 for ( ; !match(pat, s, start1 + 1, len1 + 1); (*len1)++, s++)
1943 if ((c = *(++pat)) == '\0') {
1948 for (*len1=0; !match(pat, s, start1+1, len1+1); (*len1)++, s++)
1973 int matched = 0, notin = 0, inrange = 0;
1976 if ((c = *(++pat)) == '^') {
1981 if (c == '-' && !inrange)
1988 if (*s >= prevc && *s <= c)
1998 if (inrange && *s >= prevc)
2000 if (!(matched ^ notin))
2021 static void makerep()
2028 char *p, *pat, c, pc;
2032 for (pat = to, l = 0; (c = *pat) != '\0'; pat++, l++) {
2040 else if (c == 'u') {
2047 for(x = 0; ;x *= 10) {
2055 if (l + len[x] >= MAXPATH)
2059 *(start[x]) == '.' &&
2066 if (l + STRLEN(EMPTY) >= MAXPATH)
2076 memmove(p, start[x], len[x]);
2081 for (i = len[x], q = start[x]; i > 0; i--, p++, q++)
2085 for (i = len[x], q = start[x]; i > 0; i--, p++, q++)
2103 p == fullrep ? pat != to :
2106 (pc = *(p - 1)) == SLASH
2116 if (l + STRLEN(EMPTY) >= MAXPATH)
2126 strcpy(fullrep, EMPTY);
2134 strcpy(fullrep, TOOLONG);
2138 static void checkcollisions()
2142 int i, mult, oldnreps;
2146 rd = (REPDICT *)myalloc(nreps * sizeof(REPDICT));
2148 q = &hrep, p = q->r_next, prd = rd, i = 0;
2150 q = p, p = p->r_next, prd++, i++
2153 prd->rd_dto = p->r_hto->h_di;
2154 prd->rd_nto = p->r_nto;
2157 qsort(rd, nreps, sizeof(REPDICT), rdcmp);
2159 for (i = 0, prd = rd, oldnreps = nreps; i < oldnreps; i++, prd++)
2162 prd->rd_dto == (prd + 1)->rd_dto &&
2163 strcmp(prd->rd_nto, (prd + 1)->rd_nto) == 0
2169 printf("%s%s", prd->rd_p->r_hfrom->h_name,
2170 prd->rd_p->r_ffrom->fi_name);
2171 prd->rd_p->r_flags |= R_SKIP;
2172 prd->rd_p->r_ffrom->fi_rep = MISTAKE;
2177 prd->rd_p->r_flags |= R_SKIP;
2178 prd->rd_p->r_ffrom->fi_rep = MISTAKE;
2181 printf(" , %s%s -> %s%s : collision.\n",
2182 prd->rd_p->r_hfrom->h_name, prd->rd_p->r_ffrom->fi_name,
2183 prd->rd_p->r_hto->h_name, prd->rd_nto);
2186 chgive(rd, oldnreps * sizeof(REPDICT));
2190 static int rdcmp(rd1, rd2)
2196 (ret = rd1->rd_dto - rd2->rd_dto) == 0 &&
2197 (ret = strcmp(rd1->rd_nto, rd2->rd_nto)) == 0
2199 ret = rd1->rd_i - rd2->rd_i;
2204 static void findorder()
2206 REP *p, *q, *t, *first, *pred;
2209 for (q = &hrep, p = q->r_next; p != NULL; q = p, p = p->r_next)
2210 if (p->r_flags & R_SKIP) {
2211 q->r_next = p->r_next;
2215 (fi = p->r_fdel) == NULL ||
2216 (pred = fi->fi_rep) == NULL ||
2220 else if ((first = pred->r_first) == p) {
2221 p->r_flags |= R_ISCYCLE;
2222 pred->r_flags |= R_ISALIASED;
2229 while (pred->r_thendo != NULL)
2230 pred = pred->r_thendo;
2232 for (t = p; t != NULL; t = t->r_thendo)
2234 q->r_next = p->r_next;
2240 static void nochains()
2244 for (q = &hrep, p = q->r_next; p != NULL; q = p, p = p->r_next)
2245 if (p->r_flags & R_ISCYCLE || p->r_thendo != NULL) {
2247 printf("%s%s : no chain copies allowed.\n",
2248 p->r_hto->h_name, p->r_nto);
2249 q->r_next = p->r_next;
2255 static void printchain(p)
2258 if (p->r_thendo != NULL)
2259 printchain(p->r_thendo);
2260 printf("%s%s -> ", p->r_hfrom->h_name, p->r_ffrom->fi_name);
2263 p->r_ffrom->fi_rep = MISTAKE;
2267 static void scandeletes(pkilldel)
2272 for (q = &hrep, p = q->r_next; p != NULL; q = p, p = p->r_next) {
2273 if (p->r_fdel != NULL)
2274 while ((*pkilldel)(p)) {
2276 p->r_ffrom->fi_rep = MISTAKE;
2277 if ((n = p->r_thendo) != NULL) {
2279 n->r_fdel = p->r_ffrom;
2280 n->r_next = p->r_next;
2284 q->r_next = p->r_next;
2293 static int baddel(p)
2296 HANDLE *hfrom = p->r_hfrom, *hto = p->r_hto;
2297 FILEINFO *fto = p->r_fdel;
2298 char *t = fto->fi_name, *f = p->r_ffrom->fi_name;
2299 char *hnf = hfrom->h_name, *hnt = hto->h_name;
2301 if (delstyle == NODEL && !(p->r_flags & R_DELOK) && !(op & APPEND))
2302 printf("%s%s -> %s%s : old %s%s would have to be %s.\n",
2303 hnf, f, hnt, t, hnt, t,
2304 (op & OVERWRITE) ? "overwritten" : "deleted");
2305 else if (fto->fi_rep == MISTAKE)
2306 printf("%s%s -> %s%s : old %s%s was to be done first.\n",
2307 hnf, f, hnt, t, hnt, t);
2310 fto->fi_attrib & FA_DIREC
2312 fto->fi_stflags & FI_ISDIR
2315 printf("%s%s -> %s%s : %s%s%s is a directory.\n",
2316 hnf, f, hnt, t, (op & APPEND) ? "" : "old ", hnt, t);
2318 else if ((fto->fi_stflags & FI_NODEL) && !(op & (APPEND | OVERWRITE)))
2319 printf("%s%s -> %s%s : old %s%s lacks delete permission.\n",
2320 hnf, f, hnt, t, hnt, t);
2323 (op & (APPEND | OVERWRITE)) &&
2325 fto->fi_attrib & FA_RDONLY
2327 !fwritable(hnt, fto)
2330 printf("%s%s -> %s%s : %s%s %s.\n",
2331 hnf, f, hnt, t, hnt, t,
2334 fto->fi_stflags & FI_LINKERR ?
2335 "is a badly aimed symbolic link" :
2338 "lacks write permission");
2347 static int skipdel(p)
2350 if (p->r_flags & R_DELOK)
2352 fprintf(stderr, "%s%s -> %s%s : ",
2353 p->r_hfrom->h_name, p->r_ffrom->fi_name,
2354 p->r_hto->h_name, p->r_nto);
2357 p->r_fdel->fi_attrib & FA_RDONLY
2360 !(p->r_ffrom->fi_stflags & FI_ISLNK) &&
2362 !fwritable(p->r_hto->h_name, p->r_fdel)
2365 fprintf(stderr, "old %s%s lacks write permission. delete it",
2366 p->r_hto->h_name, p->r_nto);
2368 fprintf(stderr, "%s old %s%s",
2369 (op & OVERWRITE) ? "overwrite" : "delete",
2370 p->r_hto->h_name, p->r_nto);
2371 return(!getreply("? ", -1));
2375 static void goonordie()
2377 if ((paterr || badreps) && nreps > 0) {
2378 fprintf(stderr, "Not everything specified can be done.");
2379 if (badstyle == ABORTBAD) {
2380 fprintf(stderr, " Aborting.\n");
2383 else if (badstyle == SKIPBAD)
2384 fprintf(stderr, " Proceeding with the rest.\n");
2385 else if (!getreply(" Proceed with the rest? ", -1))
2391 static void doreps()
2394 int k, printaliased = 0, alias = 0;
2401 signal(SIGINT, breakrep);
2404 for (first = hrep.r_next, k = 0; first != NULL; first = first->r_next) {
2405 for (p = first; p != NULL; p = p->r_thendo, k++) {
2408 fprintf(stderr, "User break.\n");
2409 printaliased = snap(first, p);
2412 strcpy(fullrep, p->r_hto->h_name);
2413 strcat(fullrep, p->r_nto);
2414 if (!noex && (p->r_flags & R_ISCYCLE)) {
2416 aliaslen = appendalias(first, p, &printaliased);
2418 alias = movealias(first, p, &printaliased);
2420 strcpy(pathbuf, p->r_hfrom->h_name);
2421 fstart = pathbuf + strlen(pathbuf);
2422 if ((p->r_flags & R_ISALIASED) && !(op & APPEND))
2423 sprintf(fstart, "%s%03d", TEMP, alias);
2425 strcpy(fstart, p->r_ffrom->fi_name);
2427 if (p->r_fdel != NULL && !(op & (APPEND | OVERWRITE)))
2428 myunlink(fullrep, p->r_fdel);
2430 (op & (COPY | APPEND)) ?
2432 p->r_flags & R_ISALIASED ? aliaslen : -1L) :
2435 link(pathbuf, fullrep) :
2438 symlink((p->r_flags & R_ONEDIRLINK) ? fstart : pathbuf,
2442 p->r_flags & R_ISX ?
2445 rename(pathbuf, fullrep)
2448 "%s -> %s has failed.\n", pathbuf, fullrep);
2449 printaliased = snap(first, p);
2452 if (verbose || noex) {
2453 if (p->r_flags & R_ISALIASED && !printaliased)
2454 strcpy(fstart, p->r_ffrom->fi_name);
2455 fprintf(outfile, "%s %c%c %s%s%s\n",
2457 p->r_flags & R_ISALIASED ? '=' : '-',
2458 p->r_flags & R_ISCYCLE ? '^' : '>',
2460 (p->r_fdel != NULL && !(op & APPEND)) ? " (*)" : "",
2461 noex ? "" : " : done");
2467 fprintf(stderr, "Strange, did %d reps; %d were expected.\n",
2470 fprintf(stderr, "Nothing done.\n");
2474 static long appendalias(first, p, pprintaliased)
2483 if ((fd = open(fullrep, O_RDONLY | O_BINARY, 0)) < 0) {
2484 fprintf(stderr, "stat on %s has failed.\n", fullrep);
2485 *pprintaliased = snap(first, p);
2488 ret = filelength(fd);
2494 if (stat(fullrep, &fstat)) {
2495 fprintf(stderr, "append cycle stat on %s has failed.\n", fullrep);
2496 *pprintaliased = snap(first, p);
2499 ret = fstat.st_size;
2506 static int movealias(first, p, pprintaliased)
2513 strcpy(pathbuf, p->r_hto->h_name);
2514 fstart = pathbuf + strlen(pathbuf);
2515 strcpy(fstart, TEMP);
2518 sprintf(fstart + STRLEN(TEMP), "%03d", ret),
2519 fsearch(fstart, p->r_hto->h_di) != NULL;
2523 if (rename(fullrep, pathbuf)) {
2525 "%s -> %s has failed.\n", fullrep, pathbuf);
2526 *pprintaliased = snap(first, p);
2532 static int snap(first, p)
2543 ctrlbrk((int (*)())breakstat);
2545 signal(SIGINT, breakstat);
2548 badstyle == ASKBAD &&
2549 isatty(fileno(stdout)) &&
2550 getreply("Redirect standard output to file? ", 0)
2557 fprintf(stderr, "File name> "),
2558 (outfile = fopen(mygets(fname, 80), "w")) == NULL
2560 fprintf(stderr, "Can't open %s.\n", fname);
2562 if (redirected || !verbose)
2564 fprintf(outfile, "The following left undone:\n");
2570 static void showdone(fin)
2575 for (first = hrep.r_next; ; first = first->r_next)
2576 for (p = first; p != NULL; p = p->r_thendo) {
2579 fprintf(outfile, "%s%s %c%c %s%s : done%s\n",
2580 p->r_hfrom->h_name, p->r_ffrom->fi_name,
2581 p->r_flags & R_ISALIASED ? '=' : '-',
2582 p->r_flags & R_ISCYCLE ? '^' : '>',
2583 p->r_hto->h_name, p->r_nto,
2584 (p->r_fdel != NULL && !(op & APPEND)) ? " (*)" : "");
2589 static void breakout()
2592 fprintf(stderr, "Aborting, nothing done.\n");
2597 static void breakrep(int signum)
2604 static void breakstat()
2612 fprintf(stderr, "Aborting, nothing done.\n");
2617 static int copymove(p)
2624 char linkbuf[MAXPATH];
2626 if ((llen = readlink(pathbuf, linkbuf, MAXPATH - 1)) >= 0) {
2627 linkbuf[llen] = '\0';
2628 return(symlink(linkbuf, fullrep) || myunlink(pathbuf, p->r_ffrom));
2633 return(copy(p->r_ffrom, -1L) || myunlink(pathbuf, p->r_ffrom));
2638 #define IRWMASK (S_IREAD | S_IWRITE)
2639 #define RWMASK (IRWMASK | (IRWMASK >> 3) | (IRWMASK >> 6))
2641 static int copy(ff, len)
2646 int f, t, k, mode, perm;
2654 struct timeval tim[2];
2659 if ((f = open(pathbuf, O_RDONLY | O_BINARY, 0)) < 0)
2663 IRWMASK /* will _chmod it later (to get all the attributes) */
2665 (op & (APPEND | OVERWRITE)) ?
2666 (~oldumask & RWMASK) | (ff->fi_mode & ~RWMASK) :
2674 (((t = open(fullrep, O_RDWR)) < 0 && errno == ENOENT)
2676 t = creat(fullrep, perm);
2678 mode = O_CREAT | (op & APPEND ? 0 : O_TRUNC) |
2680 O_BINARY | (op & ZAPPEND ? O_RDWR : O_WRONLY)
2685 t = open(fullrep, mode, perm);
2692 lseek(t, (off_t)0, SEEK_END);
2694 if (op & ZAPPEND && filelength(t) != 0) {
2695 if (lseek(t, -1L, 1) == -1L || read(t, &c, 1) != 1) {
2704 if ((op & APPEND) && len != (off_t)-1) {
2707 (k = read(f, buf, (len > BUFSIZE) ? BUFSIZE : (size_t)len)) > 0 &&
2708 write(t, buf, k) == k
2715 while ((k = read(f, buf, BUFSIZE)) > 0 && write(t, buf, k) == k)
2717 if (!(op & (APPEND | OVERWRITE)))
2720 getftime(f, &tim) ||
2721 setftime(t, &tim) ||
2722 _chmod(fullrep, 1, ff->fi_attrib) == -1
2724 stat(pathbuf, &fstat) ||
2727 tim.actime = fstat.st_atime,
2728 tim.modtime = fstat.st_mtime,
2730 tim[0].tv_sec = fstat.st_atime,
2732 tim[1].tv_sec = fstat.st_mtime,
2735 utimes(fullrep, tim)
2739 fprintf(stderr, "Strange, couldn't transfer time from %s to %s.\n",
2757 static int rename(from, to)
2762 if (link(from, to) == 0 && unlink(from) == 0)
2766 if (stat(from, &s) < 0 || (s.st_mode&S_IFMT) != S_IFDIR)
2770 do pid = fork(); while (pid >= 0 && errno == EAGAIN);
2774 else if (pid == 0) {
2775 execl(MV_DIR, "mv_dir", from, to, (char *) 0);
2778 } else if (pid > 0) {
2782 do wid = wait(&status);
2783 while (wid != pid && wid >= 0);
2785 return(status == 0 ? 0 : -1);
2790 static int rename(from, to)
2804 static int myunlink(n, f)
2811 if (((a = f->fi_attrib) & FA_RDONLY) && _chmod(n, 1, a & ~FA_RDONLY) < 0) {
2812 fprintf(stderr, "Strange, can not _chmod (or unlink) %s.\n", f);
2817 fprintf(stderr, "Strange, can not unlink %s.\n", n);
2824 static int getreply(m, failact)
2828 static FILE *tty = NULL;
2832 if (tty == NULL && (tty = fopen(TTY, "r")) == NULL) {
2833 fprintf(stderr, "Can not open %s to get reply.\n", TTY);
2842 fprintf(stderr, "Can not get reply.\n");
2849 while ((c = fgetc(tty)) != '\n' && c != EOF)
2852 if (r == 'y' || r == 'n')
2854 fprintf(stderr, "Yes or No? ");
2859 static void *myalloc(k)
2866 if ((ret = (void *)malloc(k)) == NULL) {
2867 fprintf(stderr, "Insufficient memory.\n");
2874 static void *challoc(k, which)
2880 SLICER *sl = &(slicer[which]);
2882 if (k > sl->sl_len) {
2884 q = NULL, p = freechunks;
2885 p != NULL && (sl->sl_len = p->ch_len) < k;
2886 q = p, p = p->ch_next
2890 sl->sl_len = CHUNKSIZE - sizeof(CHUNK *);
2891 p = (CHUNK *)myalloc(CHUNKSIZE);
2894 freechunks = p->ch_next;
2896 q->ch_next = p->ch_next;
2897 p->ch_next = sl->sl_first;
2899 sl->sl_unused = (char *)&(p->ch_len);
2902 ret = (void *)sl->sl_unused;
2908 static void chgive(p, k)
2912 ((CHUNK *)p)->ch_len = k - sizeof(CHUNK *);
2913 ((CHUNK *)p)->ch_next = freechunks;
2914 freechunks = (CHUNK *)p;
2921 static void memmove(to, from, k)
2927 *(to++) = *(from++);
2932 *(--to) = *(--from);
2942 static int lastc = 0;
2946 return(lastc = getchar());
2950 static char *mygets(s, l)
2957 if (fgets(s, l, stdin) == NULL)
2959 if ((nl = strchr(s, '\n')) != NULL)
2961 fprintf(stderr, "Input string too long. Try again> ");
2974 static void cleanup()
2978 if (patch.ph_safeid) {
2979 for (i = 0; i < nhandles; i++) {
2980 if (!(handles[i]->h_di->di_flags & DI_CLEANED)) {
2981 sprintf(pathbuf, "%s%s%03d",
2982 handles[i]->h_name, IDF, handles[i]->h_di->di_did);
2983 if (unlink(pathbuf))
2984 fprintf(stderr, "Strange, couldn't unlink %s.\n", pathbuf);
2985 handles[i]->h_di->di_flags |= DI_CLEANED;
2990 Write device availability: undocumented internal MS-D*S function.
2991 Restore previous value.
2993 bdos(0x37, olddevflag, 3);