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";
67 #define OTHEROPT (_osmajor < 3 ? "" : "|r")
72 %s [-m|x|r|c|o|a|l%s] [-h] [-d|p] [-g|t] [-v|n] [from to]\n\
74 Use #[l|u]N in the ``to'' pattern to get the [lowercase|uppercase of the]\n\
75 string matched by the N'th ``from'' pattern wildcard.\n\
77 A ``from'' pattern containing wildcards should be quoted when given\n\
78 on the command line.\n";
92 /* for MS-DOS (under Turbo C 1.5)*/
105 #define OTHERSLASH '/'
108 #define OTHERSLASH '\\'
114 static char TTY[] = "/dev/con";
115 extern unsigned _stklen = 10000;
121 /* for various flavors of UN*X */
123 #include <sys/types.h>
124 #include <sys/stat.h>
125 #include <sys/file.h>
127 extern char *getenv();
129 extern char *malloc();
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 int breakrep(/* */);
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 = stdout;
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;
501 procargs(argc, argv, &frompat, &topat);
502 domatch(frompat, topat);
506 if (op & (COPY | LINK))
510 if (!(op & APPEND) && delstyle == ASKDEL)
511 scandeletes(skipdel);
513 return(failed ? 2 : nreps == 0 && (paterr || badreps));
521 maxdisk = setdisk(curdisk);
523 Read device availability : undocumented internal MS-DOS function.
524 If (_DX == 0) then \dev\ must precede device names.
529 Write device availability: undocumented internal MS-DOS function.
530 Specify \dev\ must precede device names.
533 atexit((atexit_t)cleanup);
534 ctrlbrk((int (*)())breakout);
538 if ((home = getenv("HOME")) == NULL || strcmp(home, SLASHSTR) == 0)
540 if (!stat(".", &dstat)) {
547 signal(SIGINT, breakout);
550 dirroom = handleroom = INITROOM;
551 dirs = (DIRINFO **)myalloc(dirroom * sizeof(DIRINFO *));
552 handles = (HANDLE **)myalloc(handleroom * sizeof(HANDLE *));
553 ndirs = nhandles = 0;
557 static void procargs(argc, argv, pfrompat, ptopat)
560 char **pfrompat, **ptopat;
563 char *cmdname = argv[0];
566 #define CMDNAME (patch.ph_name)
568 #define CMDNAME cmdname
572 verbose = noex = matchall = 0;
575 for (argc--, argv++; argc > 0 && **argv == '-'; argc--, argv++)
576 for (p = *argv + 1; *p != '\0'; p++) {
578 if (c == 'v' && !noex)
580 else if (c == 'n' && !verbose)
584 else if (c == 'd' && delstyle == ASKDEL)
586 else if (c == 'p' && delstyle == ASKDEL)
588 else if (c == 'g' && badstyle == ASKBAD)
590 else if (c == 't' && badstyle == ASKBAD)
592 else if (c == 'm' && op == DFLT)
594 else if (c == 'x' && op == DFLT)
596 else if (c == 'r' && op == DFLT)
598 else if (c == 'c' && op == DFLT)
600 else if (c == 'o' && op == DFLT)
602 else if (c == 'a' && op == DFLT)
605 else if (c == 'z' && op == DFLT)
608 else if (c == 'l' && op == DFLT)
611 else if (c == 's' && op == DFLT)
616 fprintf(stderr, USAGE, CMDNAME, OTHEROPT);
622 if (strcmp(cmdname, MOVENAME) == 0)
624 else if (strcmp(cmdname, COPYNAME) == 0)
626 else if (strcmp(cmdname, APPENDNAME) == 0)
628 else if (strcmp(cmdname, LINKNAME) == 0)
645 "Unable to do directory renames. Option -r refused.\n");
650 if (euid != uid && !(op & DIRMOVE)) {
656 if (badstyle != ASKBAD && delstyle == ASKDEL)
661 else if (argc == 2) {
662 *pfrompat = *(argv++);
666 fprintf(stderr, USAGE, CMDNAME, OTHEROPT);
672 static void domatch(cfrom, cto)
678 else if ((fromlen = strlen(cfrom)) >= MAXPATLEN) {
679 printf(PATLONG, cfrom);
682 else if ((tolen = strlen(cto)) >= MAXPATLEN) {
683 printf(PATLONG, cto);
697 char extra[MAXPATLEN];
701 if ((fromlen = getword(from)) == 0 || fromlen == -1)
705 if ((tolen = getword(to)) == 0) {
706 printf("%s -> ? : missing replacement pattern.\n", from);
713 (to[0] == '-' || to[0] == '=') &&
714 (to[1] == '>' || to[1] == '^')
716 if (getword(extra) == 0)
718 else if (strcmp(extra, "(*)") == 0) {
720 gotit = (getword(extra) == 0);
724 while ((c = mygetc()) != '\n' && c != EOF)
734 static int getword(buf)
743 while ((c = mygetc()) != EOF && (prevc == ESC || !isspace(c))) {
746 if (n == MAXPATLEN - 1) {
748 printf(PATLONG, buf);
756 while (c != EOF && isspace(c) && c != '\n')
764 static void matchpat()
768 else if (dostage(from, pathbuf, start, len, 0, 0)) {
769 printf("%s -> %s : no match.\n", from, to);
775 static int parsepat()
777 char *p, *lastname, c;
778 int totwilds, instage, x, havedot;
779 static char TRAILESC[] = "%s -> %s : trailing %c is superfluous.\n";
784 if (from[0] != '\0' && from[1] == ':')
787 if (from[0] == '~' && from[1] == SLASH) {
788 if ((homelen = strlen(home)) + fromlen > MAXPATLEN) {
789 printf(PATLONG, from);
792 memmove(from + homelen, from + 1, fromlen);
793 memmove(from, home, homelen);
794 lastname += homelen + 1;
797 totwilds = nstages = instage = 0;
798 for (p = lastname; (c = *p) != '\0'; p++)
809 if (!havedot && lastname != p) {
810 if (fromlen++ == MAXPATLEN) {
811 printf(PATLONG, from);
814 memmove(p + 1, p, strlen(p) + 1);
822 if (firstwild[nstages] == NULL)
823 firstwild[nstages] = p;
824 stager[nstages++] = p;
830 printf("%s -> %s : badly placed ;.\n", from, to);
838 if ((hasdot[totwilds] = (c == '!')) != 0)
841 if (totwilds++ == MAXWILD) {
842 printf("%s -> %s : too many wildcards.\n", from, to);
847 if (firstwild[nstages] == NULL)
848 firstwild[nstages] = p;
851 stagel[nstages] = lastname;
852 firstwild[nstages] = (c == ';' ? NULL : p);
858 while ((c = *(++p)) != ']') {
861 printf("%s -> %s : missing ].\n", from, to);
869 printf("%s -> %s : '%c' can not be part of [].\n",
873 if ((c = *(++p)) == '\0') {
874 printf(TRAILESC, from, to, ESC);
880 *p = c + ('a' - 'A');
886 if ((c = *(++p)) == '\0') {
887 printf(TRAILESC, from, to, ESC);
893 *p = c + ('a' - 'A');
898 if (!havedot && lastname != p) {
899 if (fromlen++ == MAXPATLEN) {
900 printf(PATLONG, from);
908 if (firstwild[nstages] == NULL)
909 firstwild[nstages] = p;
910 stager[nstages++] = p;
913 stagel[nstages] = lastname;
915 firstwild[nstages] = p;
916 stager[nstages++] = p;
922 if (to[0] != '\0' && to[1] == ':')
925 if (to[0] == '~' && to[1] == SLASH) {
926 if ((homelen = strlen(home)) + tolen > MAXPATLEN) {
930 memmove(to + homelen, to + 1, tolen);
931 memmove(to, home, homelen);
932 lastname += homelen + 1;
936 for (p = lastname; (c = *p) != '\0'; p++)
947 printf("%s -> %s : no path allowed in target under -r.\n",
952 if (!havedot && lastname != p) {
953 if (tolen++ == MAXPATLEN) {
957 memmove(p + 1, p, strlen(p) + 1);
967 if (c == 'l' || c == 'u') {
976 printf("%s -> %s : expected digit (not '%c') after #.\n",
980 for(x = 0; ;x *= 10) {
987 if (x < 1 || x > totwilds) {
988 printf("%s -> %s : wildcard #%d does not exist.\n",
998 if ((c = *(++p)) == '\0') {
999 printf(TRAILESC, from, to, ESC);
1005 c <= ' ' || c >= 127 ||
1006 strchr(":/\\*?[]=+;,\"|<>", c) != NULL
1011 printf("%s -> %s : illegal character '%c' (0x%02X).\n",
1017 *p = c + ('a' - 'A');
1022 if (!havedot && lastname != p) {
1023 if (tolen++ == MAXPATLEN) {
1024 printf(PATLONG, to);
1035 static int dostage(lastend, pathend, start1, len1, stage, anylev)
1036 char *lastend, *pathend;
1044 int prelen, litlen, nfils, i, k, flags, try;
1045 FILEINFO **pf, *fdel;
1046 char *nto, *firstesc;
1048 int wantdirs, ret = 1, laststage = (stage + 1 == nstages);
1050 wantdirs = !laststage ||
1051 (op & (DIRMOVE | SYMLINK)) ||
1052 (nwilds[nstages - 1] == 0);
1055 prelen = stagel[stage] - lastend;
1056 if (pathend - pathbuf + prelen >= MAXPATH) {
1057 printf("%s -> %s : search path after %s too long.\n",
1062 memmove(pathend, lastend, prelen);
1065 lastend = stagel[stage];
1068 if ((h = checkdir(pathbuf, pathend, 0)) == NULL) {
1069 if (stage == 0 || direrr == H_NOREADDIR) {
1070 printf("%s -> %s : directory %s does not %s.\n",
1071 from, to, pathbuf, direrr == H_NOREADDIR ?
1072 "allow reads/searches" : "exist");
1079 if (*lastend == ';') {
1086 nfils = di->di_nfils;
1089 if ((op & MOVE) && !dwritable(h)) {
1090 printf("%s -> %s : directory %s does not allow writes.\n",
1097 firstesc = strchr(lastend, ESC);
1098 if (firstesc == NULL || firstesc > firstwild[stage])
1099 firstesc = firstwild[stage];
1100 litlen = firstesc - lastend;
1101 pf = di->di_fils + (i = ffirst(lastend, litlen, di));
1105 (try = trymatch(*pf, lastend)) != 0 &&
1108 match(lastend + litlen, (*pf)->fi_name + litlen,
1109 start1 + anylev, len1 + anylev)
1111 keepmatch(*pf, pathend, &k, 0, wantdirs, laststage)
1114 ret &= dostage(stager[stage], pathend + k,
1115 start1 + nwilds[stage], len1 + nwilds[stage],
1120 if (badrep(h, *pf, &hto, &nto, &fdel, &flags))
1121 (*pf)->fi_rep = MISTAKE;
1123 (*pf)->fi_rep = p = (REP *)challoc(sizeof(REP), 1);
1124 p->r_flags = flags | patflags;
1133 lastrep->r_next = p;
1140 } while (i < nfils && strncmp(lastend, (*pf)->fi_name, litlen) == 0);
1144 for (pf = di->di_fils, i = 0; i < nfils; i++, pf++)
1146 *((*pf)->fi_name) != '.' &&
1148 ((*pf)->fi_attrib & FA_DIREC) &&
1150 keepmatch(*pf, pathend, &k, 1, 1, 0)
1152 *len1 = pathend - *start1 + k;
1153 ret &= dostage(lastend, pathend + k, start1, len1, stage, 1);
1160 static int trymatch(ffrom, pat)
1166 if (ffrom->fi_rep != NULL)
1172 if (*p == '.' || (!matchall && ffrom->fi_attrib & (FA_HIDDEN | FA_SYSTEM)))
1173 return(strcmp(pat, p) == 0);
1176 if (p[1] == '\0' || (p[1] == '.' && p[2] == '\0'))
1177 return(strcmp(pat, p) == 0);
1178 else if (!matchall && *pat != '.')
1185 static int keepmatch(ffrom, pathend, pk, needslash, dirs, fils)
1192 *pk = strlen(ffrom->fi_name);
1193 if (pathend - pathbuf + *pk + needslash >= MAXPATH) {
1195 printf("%s -> %s : search path %s%s too long.\n",
1196 from, to, pathbuf, ffrom->fi_name);
1200 strcpy(pathend, ffrom->fi_name);
1202 if ((ffrom->fi_attrib & FA_DIREC) ? !dirs : !fils)
1204 getstat(pathbuf, ffrom);
1205 if ((ffrom->fi_stflags & FI_ISDIR) ? !dirs : !fils)
1210 strcpy(pathend + *pk, SLASHSTR);
1217 static int badrep(hfrom, ffrom, phto, pnto, pfdel, pflags)
1225 char *f = ffrom->fi_name;
1230 (ffrom->fi_attrib & FA_DIREC) &&
1232 (ffrom->fi_stflags & FI_ISDIR) &&
1234 !(op & (DIRMOVE | SYMLINK))
1236 printf("%s -> %s : source file is a directory.\n", pathbuf, fullrep);
1239 else if ((ffrom->fi_stflags & FI_LINKERR) && !(op & (MOVE | SYMLINK)))
1240 printf("%s -> %s : source file is a badly aimed symbolic link.\n",
1244 else if ((ffrom->fi_stflags & FI_NODEL) && (op & MOVE))
1245 printf("%s -> %s : no delete permission for source file.\n",
1248 else if ((op & (COPY | APPEND)) && access(pathbuf, R_OK))
1249 printf("%s -> %s : no read permission for source file.\n",
1254 (f[1] == '\0' || strcmp(f, "..") == 0) &&
1257 printf("%s -> %s : . and .. can't be renamed.\n", pathbuf, fullrep);
1258 else if (repbad || checkto(hfrom, f, phto, pnto, pfdel) || badname(*pnto))
1259 printf("%s -> %s : bad new name.\n", pathbuf, fullrep);
1260 else if (*phto == NULL)
1261 printf("%s -> %s : %s.\n", pathbuf, fullrep,
1263 direrr == H_NOREADDIR ?
1264 "no read or search permission for target directory" :
1266 "target directory does not exist");
1268 else if (!dwritable(*phto))
1269 printf("%s -> %s : no write permission for target directory.\n",
1273 (*phto)->h_di->di_vid != hfrom->h_di->di_vid &&
1274 (*pflags = R_ISX, (op & (NORMMOVE | HARDLINK)))
1276 printf("%s -> %s : cross-device move.\n",
1280 *pflags && (op & MOVE) &&
1281 !(ffrom->fi_stflags & FI_ISLNK) &&
1282 access(pathbuf, R_OK)
1284 printf("%s -> %s : no read permission for source file.\n",
1290 ((*phto)->h_di->di_vid == cwdv && (*phto)->h_di->di_did == cwdd) ||
1291 *(hfrom->h_name) == SLASH ||
1292 (*pflags |= R_ONEDIRLINK, hfrom->h_di == (*phto)->h_di)
1295 printf("%s -> %s : symbolic link would be badly aimed.\n",
1306 static int checkto(hfrom, f, phto, pnto, pfdel)
1313 char tpath[MAXPATH + 1];
1320 hlen = strlen(hfrom->h_name);
1321 pathend = fullrep + hlen;
1322 memmove(pathend, fullrep, strlen(fullrep) + 1);
1323 memmove(fullrep, hfrom->h_name, hlen);
1324 if ((fdel = *pfdel = fsearch(pathend, hfrom->h_di)) != NULL) {
1325 *pnto = fdel->fi_name;
1327 getstat(fullrep, fdel);
1331 *pnto = mydup(pathend);
1334 pathend = getpath(tpath);
1335 hlen = pathend - fullrep;
1336 *phto = checkdir(tpath, tpath + hlen, 1);
1340 (fdel = *pfdel = fsearch(pathend, (*phto)->h_di)) != NULL &&
1342 (fdel->fi_attrib & FA_DIREC)
1344 (getstat(fullrep, fdel), fdel->fi_stflags & FI_ISDIR)
1347 tlen = strlen(pathend);
1348 strcpy(pathend + tlen, SLASHSTR);
1350 strcpy(tpath + hlen, pathend);
1353 *phto = checkdir(tpath, tpath + hlen, 1);
1356 if (*pathend == '\0') {
1358 if (pathend - fullrep + strlen(f) >= MAXPATH) {
1359 strcpy(fullrep, TOOLONG);
1363 if (*phto != NULL) {
1364 fdel = *pfdel = fsearch(f, (*phto)->h_di);
1367 getstat(fullrep, fdel);
1371 else if (fdel != NULL)
1372 *pnto = fdel->fi_name;
1374 *pnto = mydup(pathend);
1380 static char *getpath(tpath)
1383 char *pathstart, *pathend, c;
1386 if (*fullrep != '\0' && fullrep[1] == ':')
1387 pathstart = fullrep + 2;
1390 pathstart = fullrep;
1392 pathend = pathstart + strlen(pathstart) - 1;
1393 while (pathend >= pathstart && *pathend != SLASH)
1399 strcpy(tpath, fullrep);
1405 static int badname(s)
1414 (ext = strchr(s, '.')) - s >= MAXFILE ||
1415 (*ext == '.' && strchr(ext + 1, '.') != NULL) ||
1416 strlen(ext) >= MAXEXT ||
1417 strncmp(s, IDF, STRLEN(IDF)) == 0
1419 (*s == '.' && (s[1] == '\0' || strcmp(s, "..") == 0)) ||
1420 strlen(s) > MAXNAMLEN
1427 static int getstat(ffull, f)
1434 if ((flags = f->fi_stflags) & FI_STTAKEN)
1435 return(flags & FI_LINKERR);
1436 flags |= FI_STTAKEN;
1438 if (stat(ffull, &fstat)) {
1439 fprintf(stderr, "Strange, couldn't stat %s.\n", ffull);
1443 if (lstat(ffull, &fstat)) {
1444 fprintf(stderr, "Strange, couldn't lstat %s.\n", ffull);
1447 if ((flags & FI_INSTICKY) && fstat.st_uid != uid && uid != 0)
1449 if ((fstat.st_mode & S_IFMT) == S_IFLNK) {
1451 if (stat(ffull, &fstat)) {
1452 f->fi_stflags = flags | FI_LINKERR;
1457 if ((fstat.st_mode & S_IFMT) == S_IFDIR)
1459 f->fi_stflags = flags;
1460 f->fi_mode = fstat.st_mode;
1465 static int dwritable(h)
1468 char *p = h->h_name, *myp, *lastslash = NULL, *pathend;
1469 char *pw = &(h->h_di->di_flags), r;
1474 if (*pw & DI_KNOWWRITE)
1475 return(*pw & DI_CANWRITE);
1477 pathend = p + strlen(p);
1480 else if (pathend == p + 1)
1483 lastslash = pathend - 1;
1487 r = !access(myp, W_OK) ? DI_CANWRITE : 0;
1488 *pw |= DI_KNOWWRITE | r;
1490 if (lastslash != NULL)
1496 static int fwritable(hname, f)
1502 if (f->fi_stflags & FI_KNOWWRITE)
1503 return(f->fi_stflags & FI_CANWRITE);
1505 strcpy(fullrep, hname);
1506 strcat(fullrep, f->fi_name);
1507 r = !access(fullrep, W_OK) ? FI_CANWRITE : 0;
1508 f->fi_stflags |= FI_KNOWWRITE | r;
1514 static FILEINFO *fsearch(s, d)
1518 FILEINFO **fils = d->di_fils;
1519 int nfils = d->di_nfils;
1520 int first, k, last, res;
1522 for(first = 0, last = nfils - 1;;) {
1525 k = (first + last) >> 1;
1526 if ((res = strcmp(s, fils[k]->fi_name)) == 0)
1536 static int ffirst(s, n, d)
1541 int first, k, last, res;
1542 FILEINFO **fils = d->di_fils;
1543 int nfils = d->di_nfils;
1545 if (nfils == 0 || n == 0)
1550 k = (first + last) >> 1;
1551 res = strncmp(s, fils[k]->fi_name, n);
1553 return(res == 0 ? k : nfils);
1563 /* checkdir and takedir for MS-D*S */
1565 static HANDLE *checkdir(p, pathend, which)
1578 if (hsearch(p, which, &h))
1579 if (h->h_di == NULL) {
1586 if (*p == '\0' || p[1] != ':')
1590 v = mylower(p[0]) - 'a';
1591 if (v < 0 || v >= maxdisk)
1595 if (patch.ph_safeid) {
1596 strcpy(pathend, IDF);
1597 strcpy(pathend + STRLEN(IDF), "*");
1598 if (findfirst(p, &de, 0)) {
1599 if ((d = ndirs) == 1000) {
1600 fprintf(stderr, "Too many different directories.\n");
1603 sprintf(pathend + STRLEN(IDF), "%03d", d);
1604 if ((fd = _creat(p, 0)) < 0) {
1605 direrr = h->h_err = H_NODIR;
1609 strcpy(pathend, "*.*");
1610 if (findfirst(p, &de, FA_DIREC | FA_SYSTEM | FA_HIDDEN))
1611 h->h_di = dadd(v, d);
1613 takedir(&de, h->h_di = dadd(v, d));
1615 else if ((d = atoi(de.ff_name + STRLEN(IDF))) < ndirs)
1618 strcpy(pathend, de.ff_name);
1619 fprintf(stderr, "Strange dir-id file encountered: %s.\n", p);
1625 strcpy(pathend, "*.*");
1626 firstfound = !findfirst(p, &de, FA_DIREC | FA_SYSTEM | FA_HIDDEN);
1633 strcpy(pathend, "T.D");
1636 direrr = h->h_err = H_NODIR;
1639 strcpy(pathend, "*.*");
1640 firstfound = !findfirst(p, &de, FA_DIREC | FA_SYSTEM | FA_HIDDEN);
1645 if (!firstfound || d != 0) {
1647 "Strange, %s does not seem to be a root dir.\n",
1653 if ((di = dsearch(v, d)) == NULL)
1655 takedir(&de, h->h_di = dadd(v, d));
1657 h->h_di = dadd(v, d);
1666 static void takedir(pff, di)
1670 int cnt, room, namlen, needdot;
1671 FILEINFO **fils, *f;
1675 di->di_fils = fils = (FILEINFO **)myalloc(room * sizeof(FILEINFO *));
1678 if (strnicmp(pff->ff_name, IDF, STRLEN(IDF)) == 0)
1682 fils = (FILEINFO **)myalloc(room * sizeof(FILEINFO *));
1683 memcpy(fils, di->di_fils, cnt * sizeof(FILEINFO *));
1684 chgive(di->di_fils, cnt * sizeof(FILEINFO *));
1686 fils = di->di_fils + cnt;
1689 for (p = pff->ff_name, namlen = 0; (c = *p) != '\0'; p++, namlen++)
1692 *fils = f = (FILEINFO *)challoc(sizeof(FILEINFO), 1);
1693 f->fi_name = p = (char *)challoc(namlen + needdot + 1, 0);
1694 for (p1 = pff->ff_name; (c = *p1) != '\0'; p1++)
1695 *(p++) = mylower(c);
1699 f->fi_attrib = pff->ff_attrib;
1703 } while (findnext(pff) == 0);
1704 qsort(di->di_fils, cnt, sizeof(FILEINFO *), fcmp);
1709 /* checkdir, takedir for Un*x */
1711 static HANDLE *checkdir(p, pathend, which)
1718 DIRINFO **newdirs, *di;
1721 char *myp, *lastslash = NULL;
1725 if (hsearch(p, which, &h))
1726 if (h->h_di == NULL) {
1735 else if (pathend == p + 1)
1738 lastslash = pathend - 1;
1743 if (stat(myp, &dstat) || (dstat.st_mode & S_IFMT) != S_IFDIR)
1744 direrr = h->h_err = H_NODIR;
1745 else if (access(myp, R_OK | X_OK))
1746 direrr = h->h_err = H_NOREADDIR;
1749 sticky = (dstat.st_mode & S_ISVTX) && uid != 0 && uid != dstat.st_uid ?
1754 if ((di = dsearch(v, d)) == NULL)
1755 takedir(myp, di = dadd(v, d), sticky);
1758 if (lastslash != NULL)
1767 static void takedir(p, di, sticky)
1774 FILEINFO *f, **fils;
1777 if ((dirp = opendir(p)) == NULL) {
1778 fprintf(stderr, "Strange, can't scan %s.\n", p);
1782 di->di_fils = fils = (FILEINFO **)myalloc(room * sizeof(FILEINFO *));
1784 while ((dp = readdir(dirp)) != NULL) {
1787 fils = (FILEINFO **)myalloc(room * sizeof(FILEINFO *));
1788 memcpy(fils, di->di_fils, cnt * sizeof(FILEINFO *));
1789 chgive(di->di_fils, cnt * sizeof(FILEINFO *));
1791 fils = di->di_fils + cnt;
1793 *fils = f = (FILEINFO *)challoc(sizeof(FILEINFO), 1);
1794 f->fi_name = mydup(dp->d_name);
1795 f->fi_stflags = sticky;
1801 qsort(di->di_fils, cnt, sizeof(FILEINFO *), fcmp);
1805 /* end of Un*x checkdir, takedir; back to general program */
1809 static int fcmp(pf1, pf2)
1810 FILEINFO **pf1, **pf2;
1812 return(strcmp((*pf1)->fi_name, (*pf2)->fi_name));
1816 static HANDLE *hadd(n)
1819 HANDLE **newhandles, *h;
1821 if (nhandles == handleroom) {
1823 newhandles = (HANDLE **)myalloc(handleroom * sizeof(HANDLE *));
1824 memcpy(newhandles, handles, nhandles * sizeof(HANDLE *));
1825 chgive(handles, nhandles * sizeof(HANDLE *));
1826 handles = newhandles;
1828 handles[nhandles++] = h = (HANDLE *)challoc(sizeof(HANDLE), 1);
1829 h->h_name = (char *)challoc(strlen(n) + 1, 0);
1830 strcpy(h->h_name, n);
1836 static int hsearch(n, which, pret)
1844 if (strcmp(n, lasthandle[which]->h_name) == 0) {
1845 *pret = lasthandle[which];
1849 for(i = 0, ph = handles; i < nhandles; i++, ph++)
1850 if (strcmp(n, (*ph)->h_name) == 0) {
1851 lasthandle[which] = *pret = *ph;
1855 lasthandle[which] = *pret = hadd(n);
1860 static DIRINFO *dadd(v, d)
1867 if (ndirs == dirroom) {
1869 newdirs = (DIRINFO **)myalloc(dirroom * sizeof(DIRINFO *));
1870 memcpy(newdirs, dirs, ndirs * sizeof(DIRINFO *));
1871 chgive(dirs, ndirs * sizeof(DIRINFO *));
1874 dirs[ndirs++] = di = (DIRINFO *)challoc(sizeof(DIRINFO), 1);
1884 static DIRINFO *dsearch(v, d)
1891 for(i = 0, di = *dirs; i < ndirs; i++, di++)
1892 if (v == di->di_vid && d == di->di_did)
1898 static int match(pat, s, start1, len1)
1899 char *pat, *s, **start1;
1913 if ((s = strchr(s, '.')) == NULL)
1917 if ((c = *(++pat)) == '\0') {
1921 for ( ; !match(pat, s, start1 + 1, len1 + 1); (*len1)++, s++)
1928 if ((c = *(++pat)) == '\0') {
1933 for (*len1=0; !match(pat, s, start1+1, len1+1); (*len1)++, s++)
1958 int matched = 0, notin = 0, inrange = 0;
1961 if ((c = *(++pat)) == '^') {
1966 if (c == '-' && !inrange)
1973 if (*s >= prevc && *s <= c)
1983 if (inrange && *s >= prevc)
1985 if (!(matched ^ notin))
2006 static void makerep()
2013 char *p, *pat, c, pc;
2017 for (pat = to, l = 0; (c = *pat) != '\0'; pat++, l++) {
2025 else if (c == 'u') {
2032 for(x = 0; ;x *= 10) {
2040 if (l + len[x] >= MAXPATH)
2044 *(start[x]) == '.' &&
2051 if (l + STRLEN(EMPTY) >= MAXPATH)
2061 memmove(p, start[x], len[x]);
2066 for (i = len[x], q = start[x]; i > 0; i--, p++, q++)
2070 for (i = len[x], q = start[x]; i > 0; i--, p++, q++)
2088 p == fullrep ? pat != to :
2091 (pc = *(p - 1)) == SLASH
2101 if (l + STRLEN(EMPTY) >= MAXPATH)
2111 strcpy(fullrep, EMPTY);
2119 strcpy(fullrep, TOOLONG);
2123 static void checkcollisions()
2127 int i, mult, oldnreps;
2131 rd = (REPDICT *)myalloc(nreps * sizeof(REPDICT));
2133 q = &hrep, p = q->r_next, prd = rd, i = 0;
2135 q = p, p = p->r_next, prd++, i++
2138 prd->rd_dto = p->r_hto->h_di;
2139 prd->rd_nto = p->r_nto;
2142 qsort(rd, nreps, sizeof(REPDICT), rdcmp);
2144 for (i = 0, prd = rd, oldnreps = nreps; i < oldnreps; i++, prd++)
2147 prd->rd_dto == (prd + 1)->rd_dto &&
2148 strcmp(prd->rd_nto, (prd + 1)->rd_nto) == 0
2154 printf("%s%s", prd->rd_p->r_hfrom->h_name,
2155 prd->rd_p->r_ffrom->fi_name);
2156 prd->rd_p->r_flags |= R_SKIP;
2157 prd->rd_p->r_ffrom->fi_rep = MISTAKE;
2162 prd->rd_p->r_flags |= R_SKIP;
2163 prd->rd_p->r_ffrom->fi_rep = MISTAKE;
2166 printf(" , %s%s -> %s%s : collision.\n",
2167 prd->rd_p->r_hfrom->h_name, prd->rd_p->r_ffrom->fi_name,
2168 prd->rd_p->r_hto->h_name, prd->rd_nto);
2171 chgive(rd, oldnreps * sizeof(REPDICT));
2175 static int rdcmp(rd1, rd2)
2181 (ret = rd1->rd_dto - rd2->rd_dto) == 0 &&
2182 (ret = strcmp(rd1->rd_nto, rd2->rd_nto)) == 0
2184 ret = rd1->rd_i - rd2->rd_i;
2189 static void findorder()
2191 REP *p, *q, *t, *first, *pred;
2194 for (q = &hrep, p = q->r_next; p != NULL; q = p, p = p->r_next)
2195 if (p->r_flags & R_SKIP) {
2196 q->r_next = p->r_next;
2200 (fi = p->r_fdel) == NULL ||
2201 (pred = fi->fi_rep) == NULL ||
2205 else if ((first = pred->r_first) == p) {
2206 p->r_flags |= R_ISCYCLE;
2207 pred->r_flags |= R_ISALIASED;
2214 while (pred->r_thendo != NULL)
2215 pred = pred->r_thendo;
2217 for (t = p; t != NULL; t = t->r_thendo)
2219 q->r_next = p->r_next;
2225 static void nochains()
2229 for (q = &hrep, p = q->r_next; p != NULL; q = p, p = p->r_next)
2230 if (p->r_flags & R_ISCYCLE || p->r_thendo != NULL) {
2232 printf("%s%s : no chain copies allowed.\n",
2233 p->r_hto->h_name, p->r_nto);
2234 q->r_next = p->r_next;
2240 static void printchain(p)
2243 if (p->r_thendo != NULL)
2244 printchain(p->r_thendo);
2245 printf("%s%s -> ", p->r_hfrom->h_name, p->r_ffrom->fi_name);
2248 p->r_ffrom->fi_rep = MISTAKE;
2252 static void scandeletes(pkilldel)
2257 for (q = &hrep, p = q->r_next; p != NULL; q = p, p = p->r_next) {
2258 if (p->r_fdel != NULL)
2259 while ((*pkilldel)(p)) {
2261 p->r_ffrom->fi_rep = MISTAKE;
2262 if ((n = p->r_thendo) != NULL) {
2264 n->r_fdel = p->r_ffrom;
2265 n->r_next = p->r_next;
2269 q->r_next = p->r_next;
2278 static int baddel(p)
2281 HANDLE *hfrom = p->r_hfrom, *hto = p->r_hto;
2282 FILEINFO *fto = p->r_fdel;
2283 char *t = fto->fi_name, *f = p->r_ffrom->fi_name;
2284 char *hnf = hfrom->h_name, *hnt = hto->h_name;
2286 if (delstyle == NODEL && !(p->r_flags & R_DELOK) && !(op & APPEND))
2287 printf("%s%s -> %s%s : old %s%s would have to be %s.\n",
2288 hnf, f, hnt, t, hnt, t,
2289 (op & OVERWRITE) ? "overwritten" : "deleted");
2290 else if (fto->fi_rep == MISTAKE)
2291 printf("%s%s -> %s%s : old %s%s was to be done first.\n",
2292 hnf, f, hnt, t, hnt, t);
2295 fto->fi_attrib & FA_DIREC
2297 fto->fi_stflags & FI_ISDIR
2300 printf("%s%s -> %s%s : %s%s%s is a directory.\n",
2301 hnf, f, hnt, t, (op & APPEND) ? "" : "old ", hnt, t);
2303 else if ((fto->fi_stflags & FI_NODEL) && !(op & (APPEND | OVERWRITE)))
2304 printf("%s%s -> %s%s : old %s%s lacks delete permission.\n",
2305 hnf, f, hnt, t, hnt, t);
2308 (op & (APPEND | OVERWRITE)) &&
2310 fto->fi_attrib & FA_RDONLY
2312 !fwritable(hnt, fto)
2315 printf("%s%s -> %s%s : %s%s %s.\n",
2316 hnf, f, hnt, t, hnt, t,
2319 fto->fi_stflags & FI_LINKERR ?
2320 "is a badly aimed symbolic link" :
2323 "lacks write permission");
2332 static int skipdel(p)
2335 if (p->r_flags & R_DELOK)
2337 fprintf(stderr, "%s%s -> %s%s : ",
2338 p->r_hfrom->h_name, p->r_ffrom->fi_name,
2339 p->r_hto->h_name, p->r_nto);
2342 p->r_fdel->fi_attrib & FA_RDONLY
2345 !(p->r_ffrom->fi_stflags & FI_ISLNK) &&
2347 !fwritable(p->r_hto->h_name, p->r_fdel)
2350 fprintf(stderr, "old %s%s lacks write permission. delete it",
2351 p->r_hto->h_name, p->r_nto);
2353 fprintf(stderr, "%s old %s%s",
2354 (op & OVERWRITE) ? "overwrite" : "delete",
2355 p->r_hto->h_name, p->r_nto);
2356 return(!getreply("? ", -1));
2360 static void goonordie()
2362 if ((paterr || badreps) && nreps > 0) {
2363 fprintf(stderr, "Not everything specified can be done.");
2364 if (badstyle == ABORTBAD) {
2365 fprintf(stderr, " Aborting.\n");
2368 else if (badstyle == SKIPBAD)
2369 fprintf(stderr, " Proceeding with the rest.\n");
2370 else if (!getreply(" Proceed with the rest? ", -1))
2376 static void doreps()
2379 int k, printaliased = 0, alias;
2386 signal(SIGINT, breakrep);
2389 for (first = hrep.r_next, k = 0; first != NULL; first = first->r_next) {
2390 for (p = first; p != NULL; p = p->r_thendo, k++) {
2393 fprintf(stderr, "User break.\n");
2394 printaliased = snap(first, p);
2397 strcpy(fullrep, p->r_hto->h_name);
2398 strcat(fullrep, p->r_nto);
2399 if (!noex && (p->r_flags & R_ISCYCLE))
2401 aliaslen = appendalias(first, p, &printaliased);
2403 alias = movealias(first, p, &printaliased);
2404 strcpy(pathbuf, p->r_hfrom->h_name);
2405 fstart = pathbuf + strlen(pathbuf);
2406 if ((p->r_flags & R_ISALIASED) && !(op & APPEND))
2407 sprintf(fstart, "%s%03d", TEMP, alias);
2409 strcpy(fstart, p->r_ffrom->fi_name);
2411 if (p->r_fdel != NULL && !(op & (APPEND | OVERWRITE)))
2412 myunlink(fullrep, p->r_fdel);
2414 (op & (COPY | APPEND)) ?
2416 p->r_flags & R_ISALIASED ? aliaslen : -1L) :
2419 link(pathbuf, fullrep) :
2422 symlink((p->r_flags & R_ONEDIRLINK) ? fstart : pathbuf,
2426 p->r_flags & R_ISX ?
2429 rename(pathbuf, fullrep)
2432 "%s -> %s has failed.\n", pathbuf, fullrep);
2433 printaliased = snap(first, p);
2436 if (verbose || noex) {
2437 if (p->r_flags & R_ISALIASED && !printaliased)
2438 strcpy(fstart, p->r_ffrom->fi_name);
2439 fprintf(outfile, "%s %c%c %s%s%s\n",
2441 p->r_flags & R_ISALIASED ? '=' : '-',
2442 p->r_flags & R_ISCYCLE ? '^' : '>',
2444 (p->r_fdel != NULL && !(op & APPEND)) ? " (*)" : "",
2445 noex ? "" : " : done");
2451 fprintf(stderr, "Strange, did %d reps; %d were expected.\n",
2454 fprintf(stderr, "Nothing done.\n");
2458 static long appendalias(first, p, pprintaliased)
2467 if ((fd = open(fullrep, O_RDONLY | O_BINARY, 0)) < 0) {
2468 fprintf(stderr, "stat on %s has failed.\n", fullrep);
2469 *pprintaliased = snap(first, p);
2472 ret = filelength(fd);
2478 if (stat(fullrep, &fstat)) {
2479 fprintf(stderr, "append cycle stat on %s has failed.\n", fullrep);
2480 *pprintaliased = snap(first, p);
2483 ret = fstat.st_size;
2490 static int movealias(first, p, pprintaliased)
2497 strcpy(pathbuf, p->r_hto->h_name);
2498 fstart = pathbuf + strlen(pathbuf);
2499 strcpy(fstart, TEMP);
2502 sprintf(fstart + STRLEN(TEMP), "%03d", ret),
2503 fsearch(fstart, p->r_hto->h_di) != NULL;
2507 if (rename(fullrep, pathbuf)) {
2509 "%s -> %s has failed.\n", fullrep, pathbuf);
2510 *pprintaliased = snap(first, p);
2516 static int snap(first, p)
2527 ctrlbrk((int (*)())breakstat);
2529 signal(SIGINT, breakstat);
2532 badstyle == ASKBAD &&
2533 isatty(fileno(stdout)) &&
2534 getreply("Redirect standard output to file? ", 0)
2541 fprintf(stderr, "File name> "),
2542 (outfile = fopen(mygets(fname, 80), "w")) == NULL
2544 fprintf(stderr, "Can't open %s.\n", fname);
2546 if (redirected || !verbose)
2548 fprintf(outfile, "The following left undone:\n");
2554 static void showdone(fin)
2559 for (first = hrep.r_next; ; first = first->r_next)
2560 for (p = first; p != NULL; p = p->r_thendo) {
2563 fprintf(outfile, "%s%s %c%c %s%s : done%s\n",
2564 p->r_hfrom->h_name, p->r_ffrom->fi_name,
2565 p->r_flags & R_ISALIASED ? '=' : '-',
2566 p->r_flags & R_ISCYCLE ? '^' : '>',
2567 p->r_hto->h_name, p->r_nto,
2568 (p->r_fdel != NULL && !(op & APPEND)) ? " (*)" : "");
2573 static void breakout()
2576 fprintf(stderr, "Aborting, nothing done.\n");
2581 static int breakrep()
2588 static void breakstat()
2596 fprintf(stderr, "Aborting, nothing done.\n");
2601 static int copymove(p)
2608 char linkbuf[MAXPATH];
2610 if ((llen = readlink(pathbuf, linkbuf, MAXPATH - 1)) >= 0) {
2611 linkbuf[llen] = '\0';
2612 return(symlink(linkbuf, fullrep) || myunlink(pathbuf, p->r_ffrom));
2617 return(copy(p->r_ffrom, -1L) || myunlink(pathbuf, p->r_ffrom));
2622 #define IRWMASK (S_IREAD | S_IWRITE)
2623 #define RWMASK (IRWMASK | (IRWMASK >> 3) | (IRWMASK >> 6))
2625 static int copy(ff, len)
2629 char buf[BUFSIZE], c;
2630 int f, t, k, mode, perm;
2637 struct timeval tim[2];
2642 if ((f = open(pathbuf, O_RDONLY | O_BINARY, 0)) < 0)
2646 IRWMASK /* will _chmod it later (to get all the attributes) */
2648 (op & (APPEND | OVERWRITE)) ?
2649 (~oldumask & RWMASK) | (ff->fi_mode & ~RWMASK) :
2657 (((t = open(fullrep, O_RDWR)) < 0 && errno == ENOENT)
2659 t = creat(fullrep, perm);
2661 mode = O_CREAT | (op & APPEND ? 0 : O_TRUNC) |
2663 O_BINARY | (op & ZAPPEND ? O_RDWR : O_WRONLY)
2668 t = open(fullrep, mode, perm);
2677 if (op & ZAPPEND && filelength(t) != 0) {
2678 if (lseek(t, -1L, 1) == -1L || read(t, &c, 1) != 1) {
2687 if ((op & APPEND) && len != -1L) {
2690 (k = read(f, buf, len > BUFSIZE ? BUFSIZE : (unsigned)len)) > 0 &&
2691 write(t, buf, k) == k
2698 while ((k = read(f, buf, BUFSIZE)) > 0 && write(t, buf, k) == k)
2700 if (!(op & (APPEND | OVERWRITE)))
2703 getftime(f, &tim) ||
2704 setftime(t, &tim) ||
2705 _chmod(fullrep, 1, ff->fi_attrib) == -1
2707 stat(pathbuf, &fstat) ||
2710 tim.actime = fstat.st_atime,
2711 tim.modtime = fstat.st_mtime,
2713 tim[0].tv_sec = fstat.st_atime,
2714 tim[1].tv_sec = fstat.st_mtime,
2716 utimes(fullrep, tim)
2720 fprintf(stderr, "Strange, couldn't transfer time from %s to %s.\n",
2738 static int rename(from, to)
2743 if (link(from, to) == 0 && unlink(from) == 0)
2747 if (stat(from, &s) < 0 || (s.st_mode&S_IFMT) != S_IFDIR)
2751 do pid = fork(); while (pid >= 0 && errno == EAGAIN);
2755 else if (pid == 0) {
2756 execl(MV_DIR, "mv_dir", from, to, (char *) 0);
2759 } else if (pid > 0) {
2763 do wid = wait(&status);
2764 while (wid != pid && wid >= 0);
2766 return(status == 0 ? 0 : -1);
2771 static int rename(from, to)
2785 static int myunlink(n, f)
2792 if (((a = f->fi_attrib) & FA_RDONLY) && _chmod(n, 1, a & ~FA_RDONLY) < 0) {
2793 fprintf(stderr, "Strange, can not _chmod (or unlink) %s.\n", f);
2798 fprintf(stderr, "Strange, can not unlink %s.\n", n);
2805 static int getreply(m, failact)
2809 static FILE *tty = NULL;
2813 if (tty == NULL && (tty = fopen(TTY, "r")) == NULL) {
2814 fprintf(stderr, "Can not open %s to get reply.\n", TTY);
2823 fprintf(stderr, "Can not get reply.\n");
2830 while ((c = fgetc(tty)) != '\n' && c != EOF)
2833 if (r == 'y' || r == 'n')
2835 fprintf(stderr, "Yes or No? ");
2840 static void *myalloc(k)
2847 if ((ret = (void *)malloc(k)) == NULL) {
2848 fprintf(stderr, "Insufficient memory.\n");
2855 static void *challoc(k, which)
2861 SLICER *sl = &(slicer[which]);
2863 if (k > sl->sl_len) {
2865 q = NULL, p = freechunks;
2866 p != NULL && (sl->sl_len = p->ch_len) < k;
2867 q = p, p = p->ch_next
2871 sl->sl_len = CHUNKSIZE - sizeof(CHUNK *);
2872 p = (CHUNK *)myalloc(CHUNKSIZE);
2875 freechunks = p->ch_next;
2877 q->ch_next = p->ch_next;
2878 p->ch_next = sl->sl_first;
2880 sl->sl_unused = (char *)&(p->ch_len);
2883 ret = (void *)sl->sl_unused;
2889 static void chgive(p, k)
2893 ((CHUNK *)p)->ch_len = k - sizeof(CHUNK *);
2894 ((CHUNK *)p)->ch_next = freechunks;
2895 freechunks = (CHUNK *)p;
2902 static void memmove(to, from, k)
2908 *(to++) = *(from++);
2913 *(--to) = *(--from);
2923 static int lastc = 0;
2927 return(lastc = getchar());
2931 static char *mygets(s, l)
2938 if (fgets(s, l, stdin) == NULL)
2940 if ((nl = strchr(s, '\n')) != NULL)
2942 fprintf(stderr, "Input string too long. Try again> ");
2955 static void cleanup()
2959 if (patch.ph_safeid) {
2960 for (i = 0; i < nhandles; i++) {
2961 if (!(handles[i]->h_di->di_flags & DI_CLEANED)) {
2962 sprintf(pathbuf, "%s%s%03d",
2963 handles[i]->h_name, IDF, handles[i]->h_di->di_did);
2964 if (unlink(pathbuf))
2965 fprintf(stderr, "Strange, couldn't unlink %s.\n", pathbuf);
2966 handles[i]->h_di->di_flags |= DI_CLEANED;
2971 Write device availability: undocumented internal MS-D*S function.
2972 Restore previous value.
2974 bdos(0x37, olddevflag, 3);