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;
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++) {
580 if (c == 'v' && !noex)
582 else if (c == 'n' && !verbose)
586 else if (c == 'd' && delstyle == ASKDEL)
588 else if (c == 'p' && delstyle == ASKDEL)
590 else if (c == 'g' && badstyle == ASKBAD)
592 else if (c == 't' && badstyle == ASKBAD)
594 else if (c == 'm' && op == DFLT)
596 else if (c == 'x' && op == DFLT)
598 else if (c == 'r' && op == DFLT)
600 else if (c == 'c' && op == DFLT)
602 else if (c == 'o' && op == DFLT)
604 else if (c == 'a' && op == DFLT)
607 else if (c == 'z' && op == DFLT)
610 else if (c == 'l' && op == DFLT)
613 else if (c == 's' && op == DFLT)
618 fprintf(stderr, USAGE, CMDNAME, OTHEROPT);
624 if (strcmp(cmdname, MOVENAME) == 0)
626 else if (strcmp(cmdname, COPYNAME) == 0)
628 else if (strcmp(cmdname, APPENDNAME) == 0)
630 else if (strcmp(cmdname, LINKNAME) == 0)
647 "Unable to do directory renames. Option -r refused.\n");
652 if (euid != uid && !(op & DIRMOVE)) {
658 if (badstyle != ASKBAD && delstyle == ASKDEL)
663 else if (argc == 2) {
664 *pfrompat = *(argv++);
668 fprintf(stderr, USAGE, CMDNAME, OTHEROPT);
674 static void domatch(cfrom, cto)
680 else if ((fromlen = strlen(cfrom)) >= MAXPATLEN) {
681 printf(PATLONG, cfrom);
684 else if ((tolen = strlen(cto)) >= MAXPATLEN) {
685 printf(PATLONG, cto);
699 char extra[MAXPATLEN];
703 if ((fromlen = getword(from)) == 0 || fromlen == -1)
707 if ((tolen = getword(to)) == 0) {
708 printf("%s -> ? : missing replacement pattern.\n", from);
715 (to[0] == '-' || to[0] == '=') &&
716 (to[1] == '>' || to[1] == '^')
718 if (getword(extra) == 0)
720 else if (strcmp(extra, "(*)") == 0) {
722 gotit = (getword(extra) == 0);
726 while ((c = mygetc()) != '\n' && c != EOF)
736 static int getword(buf)
745 while ((c = mygetc()) != EOF && (prevc == ESC || !isspace(c))) {
748 if (n == MAXPATLEN - 1) {
750 printf(PATLONG, buf);
758 while (c != EOF && isspace(c) && c != '\n')
766 static void matchpat()
770 else if (dostage(from, pathbuf, start, len, 0, 0)) {
771 printf("%s -> %s : no match.\n", from, to);
777 static int parsepat()
779 char *p, *lastname, c;
780 int totwilds, instage, x, havedot;
781 static char TRAILESC[] = "%s -> %s : trailing %c is superfluous.\n";
786 if (from[0] != '\0' && from[1] == ':')
789 if (from[0] == '~' && from[1] == SLASH) {
790 if ((homelen = strlen(home)) + fromlen > MAXPATLEN) {
791 printf(PATLONG, from);
794 memmove(from + homelen, from + 1, fromlen);
795 memmove(from, home, homelen);
796 lastname += homelen + 1;
799 totwilds = nstages = instage = 0;
800 for (p = lastname; (c = *p) != '\0'; p++)
811 if (!havedot && lastname != p) {
812 if (fromlen++ == MAXPATLEN) {
813 printf(PATLONG, from);
816 memmove(p + 1, p, strlen(p) + 1);
824 if (firstwild[nstages] == NULL)
825 firstwild[nstages] = p;
826 stager[nstages++] = p;
832 printf("%s -> %s : badly placed ;.\n", from, to);
840 if ((hasdot[totwilds] = (c == '!')) != 0)
843 if (totwilds++ == MAXWILD) {
844 printf("%s -> %s : too many wildcards.\n", from, to);
849 if (firstwild[nstages] == NULL)
850 firstwild[nstages] = p;
853 stagel[nstages] = lastname;
854 firstwild[nstages] = (c == ';' ? NULL : p);
860 while ((c = *(++p)) != ']') {
863 printf("%s -> %s : missing ].\n", from, to);
871 printf("%s -> %s : '%c' can not be part of [].\n",
875 if ((c = *(++p)) == '\0') {
876 printf(TRAILESC, from, to, ESC);
882 *p = c + ('a' - 'A');
888 if ((c = *(++p)) == '\0') {
889 printf(TRAILESC, from, to, ESC);
895 *p = c + ('a' - 'A');
900 if (!havedot && lastname != p) {
901 if (fromlen++ == MAXPATLEN) {
902 printf(PATLONG, from);
910 if (firstwild[nstages] == NULL)
911 firstwild[nstages] = p;
912 stager[nstages++] = p;
915 stagel[nstages] = lastname;
917 firstwild[nstages] = p;
918 stager[nstages++] = p;
924 if (to[0] != '\0' && to[1] == ':')
927 if (to[0] == '~' && to[1] == SLASH) {
928 if ((homelen = strlen(home)) + tolen > MAXPATLEN) {
932 memmove(to + homelen, to + 1, tolen);
933 memmove(to, home, homelen);
934 lastname += homelen + 1;
938 for (p = lastname; (c = *p) != '\0'; p++)
949 printf("%s -> %s : no path allowed in target under -r.\n",
954 if (!havedot && lastname != p) {
955 if (tolen++ == MAXPATLEN) {
959 memmove(p + 1, p, strlen(p) + 1);
969 if (c == 'l' || c == 'u') {
978 printf("%s -> %s : expected digit (not '%c') after #.\n",
982 for(x = 0; ;x *= 10) {
989 if (x < 1 || x > totwilds) {
990 printf("%s -> %s : wildcard #%d does not exist.\n",
1000 if ((c = *(++p)) == '\0') {
1001 printf(TRAILESC, from, to, ESC);
1007 c <= ' ' || c >= 127 ||
1008 strchr(":/\\*?[]=+;,\"|<>", c) != NULL
1013 printf("%s -> %s : illegal character '%c' (0x%02X).\n",
1019 *p = c + ('a' - 'A');
1024 if (!havedot && lastname != p) {
1025 if (tolen++ == MAXPATLEN) {
1026 printf(PATLONG, to);
1037 static int dostage(lastend, pathend, start1, len1, stage, anylev)
1038 char *lastend, *pathend;
1046 int prelen, litlen, nfils, i, k, flags, try;
1047 FILEINFO **pf, *fdel;
1048 char *nto, *firstesc;
1050 int wantdirs, ret = 1, laststage = (stage + 1 == nstages);
1052 wantdirs = !laststage ||
1053 (op & (DIRMOVE | SYMLINK)) ||
1054 (nwilds[nstages - 1] == 0);
1057 prelen = stagel[stage] - lastend;
1058 if (pathend - pathbuf + prelen >= MAXPATH) {
1059 printf("%s -> %s : search path after %s too long.\n",
1064 memmove(pathend, lastend, prelen);
1067 lastend = stagel[stage];
1070 if ((h = checkdir(pathbuf, pathend, 0)) == NULL) {
1071 if (stage == 0 || direrr == H_NOREADDIR) {
1072 printf("%s -> %s : directory %s does not %s.\n",
1073 from, to, pathbuf, direrr == H_NOREADDIR ?
1074 "allow reads/searches" : "exist");
1081 if (*lastend == ';') {
1088 nfils = di->di_nfils;
1091 if ((op & MOVE) && !dwritable(h)) {
1092 printf("%s -> %s : directory %s does not allow writes.\n",
1099 firstesc = strchr(lastend, ESC);
1100 if (firstesc == NULL || firstesc > firstwild[stage])
1101 firstesc = firstwild[stage];
1102 litlen = firstesc - lastend;
1103 pf = di->di_fils + (i = ffirst(lastend, litlen, di));
1107 (try = trymatch(*pf, lastend)) != 0 &&
1110 match(lastend + litlen, (*pf)->fi_name + litlen,
1111 start1 + anylev, len1 + anylev)
1113 keepmatch(*pf, pathend, &k, 0, wantdirs, laststage)
1116 ret &= dostage(stager[stage], pathend + k,
1117 start1 + nwilds[stage], len1 + nwilds[stage],
1122 if (badrep(h, *pf, &hto, &nto, &fdel, &flags))
1123 (*pf)->fi_rep = MISTAKE;
1125 (*pf)->fi_rep = p = (REP *)challoc(sizeof(REP), 1);
1126 p->r_flags = flags | patflags;
1135 lastrep->r_next = p;
1142 } while (i < nfils && strncmp(lastend, (*pf)->fi_name, litlen) == 0);
1146 for (pf = di->di_fils, i = 0; i < nfils; i++, pf++)
1148 *((*pf)->fi_name) != '.' &&
1150 ((*pf)->fi_attrib & FA_DIREC) &&
1152 keepmatch(*pf, pathend, &k, 1, 1, 0)
1154 *len1 = pathend - *start1 + k;
1155 ret &= dostage(lastend, pathend + k, start1, len1, stage, 1);
1162 static int trymatch(ffrom, pat)
1168 if (ffrom->fi_rep != NULL)
1174 if (*p == '.' || (!matchall && ffrom->fi_attrib & (FA_HIDDEN | FA_SYSTEM)))
1175 return(strcmp(pat, p) == 0);
1178 if (p[1] == '\0' || (p[1] == '.' && p[2] == '\0'))
1179 return(strcmp(pat, p) == 0);
1180 else if (!matchall && *pat != '.')
1187 static int keepmatch(ffrom, pathend, pk, needslash, dirs, fils)
1194 *pk = strlen(ffrom->fi_name);
1195 if (pathend - pathbuf + *pk + needslash >= MAXPATH) {
1197 printf("%s -> %s : search path %s%s too long.\n",
1198 from, to, pathbuf, ffrom->fi_name);
1202 strcpy(pathend, ffrom->fi_name);
1204 if ((ffrom->fi_attrib & FA_DIREC) ? !dirs : !fils)
1206 getstat(pathbuf, ffrom);
1207 if ((ffrom->fi_stflags & FI_ISDIR) ? !dirs : !fils)
1212 strcpy(pathend + *pk, SLASHSTR);
1219 static int badrep(hfrom, ffrom, phto, pnto, pfdel, pflags)
1227 char *f = ffrom->fi_name;
1232 (ffrom->fi_attrib & FA_DIREC) &&
1234 (ffrom->fi_stflags & FI_ISDIR) &&
1236 !(op & (DIRMOVE | SYMLINK))
1238 printf("%s -> %s : source file is a directory.\n", pathbuf, fullrep);
1241 else if ((ffrom->fi_stflags & FI_LINKERR) && !(op & (MOVE | SYMLINK)))
1242 printf("%s -> %s : source file is a badly aimed symbolic link.\n",
1246 else if ((ffrom->fi_stflags & FI_NODEL) && (op & MOVE))
1247 printf("%s -> %s : no delete permission for source file.\n",
1250 else if ((op & (COPY | APPEND)) && access(pathbuf, R_OK))
1251 printf("%s -> %s : no read permission for source file.\n",
1256 (f[1] == '\0' || strcmp(f, "..") == 0) &&
1259 printf("%s -> %s : . and .. can't be renamed.\n", pathbuf, fullrep);
1260 else if (repbad || checkto(hfrom, f, phto, pnto, pfdel) || badname(*pnto))
1261 printf("%s -> %s : bad new name.\n", pathbuf, fullrep);
1262 else if (*phto == NULL)
1263 printf("%s -> %s : %s.\n", pathbuf, fullrep,
1265 direrr == H_NOREADDIR ?
1266 "no read or search permission for target directory" :
1268 "target directory does not exist");
1270 else if (!dwritable(*phto))
1271 printf("%s -> %s : no write permission for target directory.\n",
1275 (*phto)->h_di->di_vid != hfrom->h_di->di_vid &&
1276 (*pflags = R_ISX, (op & (NORMMOVE | HARDLINK)))
1278 printf("%s -> %s : cross-device move.\n",
1282 *pflags && (op & MOVE) &&
1283 !(ffrom->fi_stflags & FI_ISLNK) &&
1284 access(pathbuf, R_OK)
1286 printf("%s -> %s : no read permission for source file.\n",
1292 ((*phto)->h_di->di_vid == cwdv && (*phto)->h_di->di_did == cwdd) ||
1293 *(hfrom->h_name) == SLASH ||
1294 (*pflags |= R_ONEDIRLINK, hfrom->h_di == (*phto)->h_di)
1297 printf("%s -> %s : symbolic link would be badly aimed.\n",
1308 static int checkto(hfrom, f, phto, pnto, pfdel)
1315 char tpath[MAXPATH + 1];
1322 hlen = strlen(hfrom->h_name);
1323 pathend = fullrep + hlen;
1324 memmove(pathend, fullrep, strlen(fullrep) + 1);
1325 memmove(fullrep, hfrom->h_name, hlen);
1326 if ((fdel = *pfdel = fsearch(pathend, hfrom->h_di)) != NULL) {
1327 *pnto = fdel->fi_name;
1329 getstat(fullrep, fdel);
1333 *pnto = mydup(pathend);
1336 pathend = getpath(tpath);
1337 hlen = pathend - fullrep;
1338 *phto = checkdir(tpath, tpath + hlen, 1);
1342 (fdel = *pfdel = fsearch(pathend, (*phto)->h_di)) != NULL &&
1344 (fdel->fi_attrib & FA_DIREC)
1346 (getstat(fullrep, fdel), fdel->fi_stflags & FI_ISDIR)
1349 tlen = strlen(pathend);
1350 strcpy(pathend + tlen, SLASHSTR);
1352 strcpy(tpath + hlen, pathend);
1355 *phto = checkdir(tpath, tpath + hlen, 1);
1358 if (*pathend == '\0') {
1360 if (pathend - fullrep + strlen(f) >= MAXPATH) {
1361 strcpy(fullrep, TOOLONG);
1365 if (*phto != NULL) {
1366 fdel = *pfdel = fsearch(f, (*phto)->h_di);
1369 getstat(fullrep, fdel);
1373 else if (fdel != NULL)
1374 *pnto = fdel->fi_name;
1376 *pnto = mydup(pathend);
1382 static char *getpath(tpath)
1385 char *pathstart, *pathend, c;
1388 if (*fullrep != '\0' && fullrep[1] == ':')
1389 pathstart = fullrep + 2;
1392 pathstart = fullrep;
1394 pathend = pathstart + strlen(pathstart) - 1;
1395 while (pathend >= pathstart && *pathend != SLASH)
1401 strcpy(tpath, fullrep);
1407 static int badname(s)
1416 (ext = strchr(s, '.')) - s >= MAXFILE ||
1417 (*ext == '.' && strchr(ext + 1, '.') != NULL) ||
1418 strlen(ext) >= MAXEXT ||
1419 strncmp(s, IDF, STRLEN(IDF)) == 0
1421 (*s == '.' && (s[1] == '\0' || strcmp(s, "..") == 0)) ||
1422 strlen(s) > MAXNAMLEN
1429 static int getstat(ffull, f)
1436 if ((flags = f->fi_stflags) & FI_STTAKEN)
1437 return(flags & FI_LINKERR);
1438 flags |= FI_STTAKEN;
1440 if (stat(ffull, &fstat)) {
1441 fprintf(stderr, "Strange, couldn't stat %s.\n", ffull);
1445 if (lstat(ffull, &fstat)) {
1446 fprintf(stderr, "Strange, couldn't lstat %s.\n", ffull);
1449 if ((flags & FI_INSTICKY) && fstat.st_uid != uid && uid != 0)
1451 if ((fstat.st_mode & S_IFMT) == S_IFLNK) {
1453 if (stat(ffull, &fstat)) {
1454 f->fi_stflags = flags | FI_LINKERR;
1459 if ((fstat.st_mode & S_IFMT) == S_IFDIR)
1461 f->fi_stflags = flags;
1462 f->fi_mode = fstat.st_mode;
1467 static int dwritable(h)
1470 char *p = h->h_name, *myp, *lastslash = NULL, *pathend;
1471 char *pw = &(h->h_di->di_flags), r;
1476 if (*pw & DI_KNOWWRITE)
1477 return(*pw & DI_CANWRITE);
1479 pathend = p + strlen(p);
1482 else if (pathend == p + 1)
1485 lastslash = pathend - 1;
1489 r = !access(myp, W_OK) ? DI_CANWRITE : 0;
1490 *pw |= DI_KNOWWRITE | r;
1492 if (lastslash != NULL)
1498 static int fwritable(hname, f)
1504 if (f->fi_stflags & FI_KNOWWRITE)
1505 return(f->fi_stflags & FI_CANWRITE);
1507 strcpy(fullrep, hname);
1508 strcat(fullrep, f->fi_name);
1509 r = !access(fullrep, W_OK) ? FI_CANWRITE : 0;
1510 f->fi_stflags |= FI_KNOWWRITE | r;
1516 static FILEINFO *fsearch(s, d)
1520 FILEINFO **fils = d->di_fils;
1521 int nfils = d->di_nfils;
1522 int first, k, last, res;
1524 for(first = 0, last = nfils - 1;;) {
1527 k = (first + last) >> 1;
1528 if ((res = strcmp(s, fils[k]->fi_name)) == 0)
1538 static int ffirst(s, n, d)
1543 int first, k, last, res;
1544 FILEINFO **fils = d->di_fils;
1545 int nfils = d->di_nfils;
1547 if (nfils == 0 || n == 0)
1552 k = (first + last) >> 1;
1553 res = strncmp(s, fils[k]->fi_name, n);
1555 return(res == 0 ? k : nfils);
1565 /* checkdir and takedir for MS-D*S */
1567 static HANDLE *checkdir(p, pathend, which)
1580 if (hsearch(p, which, &h))
1581 if (h->h_di == NULL) {
1588 if (*p == '\0' || p[1] != ':')
1592 v = mylower(p[0]) - 'a';
1593 if (v < 0 || v >= maxdisk)
1597 if (patch.ph_safeid) {
1598 strcpy(pathend, IDF);
1599 strcpy(pathend + STRLEN(IDF), "*");
1600 if (findfirst(p, &de, 0)) {
1601 if ((d = ndirs) == 1000) {
1602 fprintf(stderr, "Too many different directories.\n");
1605 sprintf(pathend + STRLEN(IDF), "%03d", d);
1606 if ((fd = _creat(p, 0)) < 0) {
1607 direrr = h->h_err = H_NODIR;
1611 strcpy(pathend, "*.*");
1612 if (findfirst(p, &de, FA_DIREC | FA_SYSTEM | FA_HIDDEN))
1613 h->h_di = dadd(v, d);
1615 takedir(&de, h->h_di = dadd(v, d));
1617 else if ((d = atoi(de.ff_name + STRLEN(IDF))) < ndirs)
1620 strcpy(pathend, de.ff_name);
1621 fprintf(stderr, "Strange dir-id file encountered: %s.\n", p);
1627 strcpy(pathend, "*.*");
1628 firstfound = !findfirst(p, &de, FA_DIREC | FA_SYSTEM | FA_HIDDEN);
1635 strcpy(pathend, "T.D");
1638 direrr = h->h_err = H_NODIR;
1641 strcpy(pathend, "*.*");
1642 firstfound = !findfirst(p, &de, FA_DIREC | FA_SYSTEM | FA_HIDDEN);
1647 if (!firstfound || d != 0) {
1649 "Strange, %s does not seem to be a root dir.\n",
1655 if ((di = dsearch(v, d)) == NULL)
1657 takedir(&de, h->h_di = dadd(v, d));
1659 h->h_di = dadd(v, d);
1668 static void takedir(pff, di)
1672 int cnt, room, namlen, needdot;
1673 FILEINFO **fils, *f;
1677 di->di_fils = fils = (FILEINFO **)myalloc(room * sizeof(FILEINFO *));
1680 if (strnicmp(pff->ff_name, IDF, STRLEN(IDF)) == 0)
1684 fils = (FILEINFO **)myalloc(room * sizeof(FILEINFO *));
1685 memcpy(fils, di->di_fils, cnt * sizeof(FILEINFO *));
1686 chgive(di->di_fils, cnt * sizeof(FILEINFO *));
1688 fils = di->di_fils + cnt;
1691 for (p = pff->ff_name, namlen = 0; (c = *p) != '\0'; p++, namlen++)
1694 *fils = f = (FILEINFO *)challoc(sizeof(FILEINFO), 1);
1695 f->fi_name = p = (char *)challoc(namlen + needdot + 1, 0);
1696 for (p1 = pff->ff_name; (c = *p1) != '\0'; p1++)
1697 *(p++) = mylower(c);
1701 f->fi_attrib = pff->ff_attrib;
1705 } while (findnext(pff) == 0);
1706 qsort(di->di_fils, cnt, sizeof(FILEINFO *), fcmp);
1711 /* checkdir, takedir for Un*x */
1713 static HANDLE *checkdir(p, pathend, which)
1720 DIRINFO **newdirs, *di;
1723 char *myp, *lastslash = NULL;
1727 if (hsearch(p, which, &h))
1728 if (h->h_di == NULL) {
1737 else if (pathend == p + 1)
1740 lastslash = pathend - 1;
1745 if (stat(myp, &dstat) || (dstat.st_mode & S_IFMT) != S_IFDIR)
1746 direrr = h->h_err = H_NODIR;
1747 else if (access(myp, R_OK | X_OK))
1748 direrr = h->h_err = H_NOREADDIR;
1751 sticky = (dstat.st_mode & S_ISVTX) && uid != 0 && uid != dstat.st_uid ?
1756 if ((di = dsearch(v, d)) == NULL)
1757 takedir(myp, di = dadd(v, d), sticky);
1760 if (lastslash != NULL)
1769 static void takedir(p, di, sticky)
1776 FILEINFO *f, **fils;
1779 if ((dirp = opendir(p)) == NULL) {
1780 fprintf(stderr, "Strange, can't scan %s.\n", p);
1784 di->di_fils = fils = (FILEINFO **)myalloc(room * sizeof(FILEINFO *));
1786 while ((dp = readdir(dirp)) != NULL) {
1789 fils = (FILEINFO **)myalloc(room * sizeof(FILEINFO *));
1790 memcpy(fils, di->di_fils, cnt * sizeof(FILEINFO *));
1791 chgive(di->di_fils, cnt * sizeof(FILEINFO *));
1793 fils = di->di_fils + cnt;
1795 *fils = f = (FILEINFO *)challoc(sizeof(FILEINFO), 1);
1796 f->fi_name = mydup(dp->d_name);
1797 f->fi_stflags = sticky;
1803 qsort(di->di_fils, cnt, sizeof(FILEINFO *), fcmp);
1807 /* end of Un*x checkdir, takedir; back to general program */
1811 static int fcmp(pf1, pf2)
1812 FILEINFO **pf1, **pf2;
1814 return(strcmp((*pf1)->fi_name, (*pf2)->fi_name));
1818 static HANDLE *hadd(n)
1821 HANDLE **newhandles, *h;
1823 if (nhandles == handleroom) {
1825 newhandles = (HANDLE **)myalloc(handleroom * sizeof(HANDLE *));
1826 memcpy(newhandles, handles, nhandles * sizeof(HANDLE *));
1827 chgive(handles, nhandles * sizeof(HANDLE *));
1828 handles = newhandles;
1830 handles[nhandles++] = h = (HANDLE *)challoc(sizeof(HANDLE), 1);
1831 h->h_name = (char *)challoc(strlen(n) + 1, 0);
1832 strcpy(h->h_name, n);
1838 static int hsearch(n, which, pret)
1846 if (strcmp(n, lasthandle[which]->h_name) == 0) {
1847 *pret = lasthandle[which];
1851 for(i = 0, ph = handles; i < nhandles; i++, ph++)
1852 if (strcmp(n, (*ph)->h_name) == 0) {
1853 lasthandle[which] = *pret = *ph;
1857 lasthandle[which] = *pret = hadd(n);
1862 static DIRINFO *dadd(v, d)
1869 if (ndirs == dirroom) {
1871 newdirs = (DIRINFO **)myalloc(dirroom * sizeof(DIRINFO *));
1872 memcpy(newdirs, dirs, ndirs * sizeof(DIRINFO *));
1873 chgive(dirs, ndirs * sizeof(DIRINFO *));
1876 dirs[ndirs++] = di = (DIRINFO *)challoc(sizeof(DIRINFO), 1);
1886 static DIRINFO *dsearch(v, d)
1893 for(i = 0, di = *dirs; i < ndirs; i++, di++)
1894 if (v == di->di_vid && d == di->di_did)
1900 static int match(pat, s, start1, len1)
1901 char *pat, *s, **start1;
1915 if ((s = strchr(s, '.')) == NULL)
1919 if ((c = *(++pat)) == '\0') {
1923 for ( ; !match(pat, s, start1 + 1, len1 + 1); (*len1)++, s++)
1930 if ((c = *(++pat)) == '\0') {
1935 for (*len1=0; !match(pat, s, start1+1, len1+1); (*len1)++, s++)
1960 int matched = 0, notin = 0, inrange = 0;
1963 if ((c = *(++pat)) == '^') {
1968 if (c == '-' && !inrange)
1975 if (*s >= prevc && *s <= c)
1985 if (inrange && *s >= prevc)
1987 if (!(matched ^ notin))
2008 static void makerep()
2015 char *p, *pat, c, pc;
2019 for (pat = to, l = 0; (c = *pat) != '\0'; pat++, l++) {
2027 else if (c == 'u') {
2034 for(x = 0; ;x *= 10) {
2042 if (l + len[x] >= MAXPATH)
2046 *(start[x]) == '.' &&
2053 if (l + STRLEN(EMPTY) >= MAXPATH)
2063 memmove(p, start[x], len[x]);
2068 for (i = len[x], q = start[x]; i > 0; i--, p++, q++)
2072 for (i = len[x], q = start[x]; i > 0; i--, p++, q++)
2090 p == fullrep ? pat != to :
2093 (pc = *(p - 1)) == SLASH
2103 if (l + STRLEN(EMPTY) >= MAXPATH)
2113 strcpy(fullrep, EMPTY);
2121 strcpy(fullrep, TOOLONG);
2125 static void checkcollisions()
2129 int i, mult, oldnreps;
2133 rd = (REPDICT *)myalloc(nreps * sizeof(REPDICT));
2135 q = &hrep, p = q->r_next, prd = rd, i = 0;
2137 q = p, p = p->r_next, prd++, i++
2140 prd->rd_dto = p->r_hto->h_di;
2141 prd->rd_nto = p->r_nto;
2144 qsort(rd, nreps, sizeof(REPDICT), rdcmp);
2146 for (i = 0, prd = rd, oldnreps = nreps; i < oldnreps; i++, prd++)
2149 prd->rd_dto == (prd + 1)->rd_dto &&
2150 strcmp(prd->rd_nto, (prd + 1)->rd_nto) == 0
2156 printf("%s%s", prd->rd_p->r_hfrom->h_name,
2157 prd->rd_p->r_ffrom->fi_name);
2158 prd->rd_p->r_flags |= R_SKIP;
2159 prd->rd_p->r_ffrom->fi_rep = MISTAKE;
2164 prd->rd_p->r_flags |= R_SKIP;
2165 prd->rd_p->r_ffrom->fi_rep = MISTAKE;
2168 printf(" , %s%s -> %s%s : collision.\n",
2169 prd->rd_p->r_hfrom->h_name, prd->rd_p->r_ffrom->fi_name,
2170 prd->rd_p->r_hto->h_name, prd->rd_nto);
2173 chgive(rd, oldnreps * sizeof(REPDICT));
2177 static int rdcmp(rd1, rd2)
2183 (ret = rd1->rd_dto - rd2->rd_dto) == 0 &&
2184 (ret = strcmp(rd1->rd_nto, rd2->rd_nto)) == 0
2186 ret = rd1->rd_i - rd2->rd_i;
2191 static void findorder()
2193 REP *p, *q, *t, *first, *pred;
2196 for (q = &hrep, p = q->r_next; p != NULL; q = p, p = p->r_next)
2197 if (p->r_flags & R_SKIP) {
2198 q->r_next = p->r_next;
2202 (fi = p->r_fdel) == NULL ||
2203 (pred = fi->fi_rep) == NULL ||
2207 else if ((first = pred->r_first) == p) {
2208 p->r_flags |= R_ISCYCLE;
2209 pred->r_flags |= R_ISALIASED;
2216 while (pred->r_thendo != NULL)
2217 pred = pred->r_thendo;
2219 for (t = p; t != NULL; t = t->r_thendo)
2221 q->r_next = p->r_next;
2227 static void nochains()
2231 for (q = &hrep, p = q->r_next; p != NULL; q = p, p = p->r_next)
2232 if (p->r_flags & R_ISCYCLE || p->r_thendo != NULL) {
2234 printf("%s%s : no chain copies allowed.\n",
2235 p->r_hto->h_name, p->r_nto);
2236 q->r_next = p->r_next;
2242 static void printchain(p)
2245 if (p->r_thendo != NULL)
2246 printchain(p->r_thendo);
2247 printf("%s%s -> ", p->r_hfrom->h_name, p->r_ffrom->fi_name);
2250 p->r_ffrom->fi_rep = MISTAKE;
2254 static void scandeletes(pkilldel)
2259 for (q = &hrep, p = q->r_next; p != NULL; q = p, p = p->r_next) {
2260 if (p->r_fdel != NULL)
2261 while ((*pkilldel)(p)) {
2263 p->r_ffrom->fi_rep = MISTAKE;
2264 if ((n = p->r_thendo) != NULL) {
2266 n->r_fdel = p->r_ffrom;
2267 n->r_next = p->r_next;
2271 q->r_next = p->r_next;
2280 static int baddel(p)
2283 HANDLE *hfrom = p->r_hfrom, *hto = p->r_hto;
2284 FILEINFO *fto = p->r_fdel;
2285 char *t = fto->fi_name, *f = p->r_ffrom->fi_name;
2286 char *hnf = hfrom->h_name, *hnt = hto->h_name;
2288 if (delstyle == NODEL && !(p->r_flags & R_DELOK) && !(op & APPEND))
2289 printf("%s%s -> %s%s : old %s%s would have to be %s.\n",
2290 hnf, f, hnt, t, hnt, t,
2291 (op & OVERWRITE) ? "overwritten" : "deleted");
2292 else if (fto->fi_rep == MISTAKE)
2293 printf("%s%s -> %s%s : old %s%s was to be done first.\n",
2294 hnf, f, hnt, t, hnt, t);
2297 fto->fi_attrib & FA_DIREC
2299 fto->fi_stflags & FI_ISDIR
2302 printf("%s%s -> %s%s : %s%s%s is a directory.\n",
2303 hnf, f, hnt, t, (op & APPEND) ? "" : "old ", hnt, t);
2305 else if ((fto->fi_stflags & FI_NODEL) && !(op & (APPEND | OVERWRITE)))
2306 printf("%s%s -> %s%s : old %s%s lacks delete permission.\n",
2307 hnf, f, hnt, t, hnt, t);
2310 (op & (APPEND | OVERWRITE)) &&
2312 fto->fi_attrib & FA_RDONLY
2314 !fwritable(hnt, fto)
2317 printf("%s%s -> %s%s : %s%s %s.\n",
2318 hnf, f, hnt, t, hnt, t,
2321 fto->fi_stflags & FI_LINKERR ?
2322 "is a badly aimed symbolic link" :
2325 "lacks write permission");
2334 static int skipdel(p)
2337 if (p->r_flags & R_DELOK)
2339 fprintf(stderr, "%s%s -> %s%s : ",
2340 p->r_hfrom->h_name, p->r_ffrom->fi_name,
2341 p->r_hto->h_name, p->r_nto);
2344 p->r_fdel->fi_attrib & FA_RDONLY
2347 !(p->r_ffrom->fi_stflags & FI_ISLNK) &&
2349 !fwritable(p->r_hto->h_name, p->r_fdel)
2352 fprintf(stderr, "old %s%s lacks write permission. delete it",
2353 p->r_hto->h_name, p->r_nto);
2355 fprintf(stderr, "%s old %s%s",
2356 (op & OVERWRITE) ? "overwrite" : "delete",
2357 p->r_hto->h_name, p->r_nto);
2358 return(!getreply("? ", -1));
2362 static void goonordie()
2364 if ((paterr || badreps) && nreps > 0) {
2365 fprintf(stderr, "Not everything specified can be done.");
2366 if (badstyle == ABORTBAD) {
2367 fprintf(stderr, " Aborting.\n");
2370 else if (badstyle == SKIPBAD)
2371 fprintf(stderr, " Proceeding with the rest.\n");
2372 else if (!getreply(" Proceed with the rest? ", -1))
2378 static void doreps()
2381 int k, printaliased = 0, alias;
2388 signal(SIGINT, breakrep);
2391 for (first = hrep.r_next, k = 0; first != NULL; first = first->r_next) {
2392 for (p = first; p != NULL; p = p->r_thendo, k++) {
2395 fprintf(stderr, "User break.\n");
2396 printaliased = snap(first, p);
2399 strcpy(fullrep, p->r_hto->h_name);
2400 strcat(fullrep, p->r_nto);
2401 if (!noex && (p->r_flags & R_ISCYCLE))
2403 aliaslen = appendalias(first, p, &printaliased);
2405 alias = movealias(first, p, &printaliased);
2406 strcpy(pathbuf, p->r_hfrom->h_name);
2407 fstart = pathbuf + strlen(pathbuf);
2408 if ((p->r_flags & R_ISALIASED) && !(op & APPEND))
2409 sprintf(fstart, "%s%03d", TEMP, alias);
2411 strcpy(fstart, p->r_ffrom->fi_name);
2413 if (p->r_fdel != NULL && !(op & (APPEND | OVERWRITE)))
2414 myunlink(fullrep, p->r_fdel);
2416 (op & (COPY | APPEND)) ?
2418 p->r_flags & R_ISALIASED ? aliaslen : -1L) :
2421 link(pathbuf, fullrep) :
2424 symlink((p->r_flags & R_ONEDIRLINK) ? fstart : pathbuf,
2428 p->r_flags & R_ISX ?
2431 rename(pathbuf, fullrep)
2434 "%s -> %s has failed.\n", pathbuf, fullrep);
2435 printaliased = snap(first, p);
2438 if (verbose || noex) {
2439 if (p->r_flags & R_ISALIASED && !printaliased)
2440 strcpy(fstart, p->r_ffrom->fi_name);
2441 fprintf(outfile, "%s %c%c %s%s%s\n",
2443 p->r_flags & R_ISALIASED ? '=' : '-',
2444 p->r_flags & R_ISCYCLE ? '^' : '>',
2446 (p->r_fdel != NULL && !(op & APPEND)) ? " (*)" : "",
2447 noex ? "" : " : done");
2453 fprintf(stderr, "Strange, did %d reps; %d were expected.\n",
2456 fprintf(stderr, "Nothing done.\n");
2460 static long appendalias(first, p, pprintaliased)
2469 if ((fd = open(fullrep, O_RDONLY | O_BINARY, 0)) < 0) {
2470 fprintf(stderr, "stat on %s has failed.\n", fullrep);
2471 *pprintaliased = snap(first, p);
2474 ret = filelength(fd);
2480 if (stat(fullrep, &fstat)) {
2481 fprintf(stderr, "append cycle stat on %s has failed.\n", fullrep);
2482 *pprintaliased = snap(first, p);
2485 ret = fstat.st_size;
2492 static int movealias(first, p, pprintaliased)
2499 strcpy(pathbuf, p->r_hto->h_name);
2500 fstart = pathbuf + strlen(pathbuf);
2501 strcpy(fstart, TEMP);
2504 sprintf(fstart + STRLEN(TEMP), "%03d", ret),
2505 fsearch(fstart, p->r_hto->h_di) != NULL;
2509 if (rename(fullrep, pathbuf)) {
2511 "%s -> %s has failed.\n", fullrep, pathbuf);
2512 *pprintaliased = snap(first, p);
2518 static int snap(first, p)
2529 ctrlbrk((int (*)())breakstat);
2531 signal(SIGINT, breakstat);
2534 badstyle == ASKBAD &&
2535 isatty(fileno(stdout)) &&
2536 getreply("Redirect standard output to file? ", 0)
2543 fprintf(stderr, "File name> "),
2544 (outfile = fopen(mygets(fname, 80), "w")) == NULL
2546 fprintf(stderr, "Can't open %s.\n", fname);
2548 if (redirected || !verbose)
2550 fprintf(outfile, "The following left undone:\n");
2556 static void showdone(fin)
2561 for (first = hrep.r_next; ; first = first->r_next)
2562 for (p = first; p != NULL; p = p->r_thendo) {
2565 fprintf(outfile, "%s%s %c%c %s%s : done%s\n",
2566 p->r_hfrom->h_name, p->r_ffrom->fi_name,
2567 p->r_flags & R_ISALIASED ? '=' : '-',
2568 p->r_flags & R_ISCYCLE ? '^' : '>',
2569 p->r_hto->h_name, p->r_nto,
2570 (p->r_fdel != NULL && !(op & APPEND)) ? " (*)" : "");
2575 static void breakout()
2578 fprintf(stderr, "Aborting, nothing done.\n");
2583 static int breakrep()
2590 static void breakstat()
2598 fprintf(stderr, "Aborting, nothing done.\n");
2603 static int copymove(p)
2610 char linkbuf[MAXPATH];
2612 if ((llen = readlink(pathbuf, linkbuf, MAXPATH - 1)) >= 0) {
2613 linkbuf[llen] = '\0';
2614 return(symlink(linkbuf, fullrep) || myunlink(pathbuf, p->r_ffrom));
2619 return(copy(p->r_ffrom, -1L) || myunlink(pathbuf, p->r_ffrom));
2624 #define IRWMASK (S_IREAD | S_IWRITE)
2625 #define RWMASK (IRWMASK | (IRWMASK >> 3) | (IRWMASK >> 6))
2627 static int copy(ff, len)
2631 char buf[BUFSIZE], c;
2632 int f, t, k, mode, perm;
2639 struct timeval tim[2];
2644 if ((f = open(pathbuf, O_RDONLY | O_BINARY, 0)) < 0)
2648 IRWMASK /* will _chmod it later (to get all the attributes) */
2650 (op & (APPEND | OVERWRITE)) ?
2651 (~oldumask & RWMASK) | (ff->fi_mode & ~RWMASK) :
2659 (((t = open(fullrep, O_RDWR)) < 0 && errno == ENOENT)
2661 t = creat(fullrep, perm);
2663 mode = O_CREAT | (op & APPEND ? 0 : O_TRUNC) |
2665 O_BINARY | (op & ZAPPEND ? O_RDWR : O_WRONLY)
2670 t = open(fullrep, mode, perm);
2679 if (op & ZAPPEND && filelength(t) != 0) {
2680 if (lseek(t, -1L, 1) == -1L || read(t, &c, 1) != 1) {
2689 if ((op & APPEND) && len != -1L) {
2692 (k = read(f, buf, len > BUFSIZE ? BUFSIZE : (unsigned)len)) > 0 &&
2693 write(t, buf, k) == k
2700 while ((k = read(f, buf, BUFSIZE)) > 0 && write(t, buf, k) == k)
2702 if (!(op & (APPEND | OVERWRITE)))
2705 getftime(f, &tim) ||
2706 setftime(t, &tim) ||
2707 _chmod(fullrep, 1, ff->fi_attrib) == -1
2709 stat(pathbuf, &fstat) ||
2712 tim.actime = fstat.st_atime,
2713 tim.modtime = fstat.st_mtime,
2715 tim[0].tv_sec = fstat.st_atime,
2716 tim[1].tv_sec = fstat.st_mtime,
2718 utimes(fullrep, tim)
2722 fprintf(stderr, "Strange, couldn't transfer time from %s to %s.\n",
2740 static int rename(from, to)
2745 if (link(from, to) == 0 && unlink(from) == 0)
2749 if (stat(from, &s) < 0 || (s.st_mode&S_IFMT) != S_IFDIR)
2753 do pid = fork(); while (pid >= 0 && errno == EAGAIN);
2757 else if (pid == 0) {
2758 execl(MV_DIR, "mv_dir", from, to, (char *) 0);
2761 } else if (pid > 0) {
2765 do wid = wait(&status);
2766 while (wid != pid && wid >= 0);
2768 return(status == 0 ? 0 : -1);
2773 static int rename(from, to)
2787 static int myunlink(n, f)
2794 if (((a = f->fi_attrib) & FA_RDONLY) && _chmod(n, 1, a & ~FA_RDONLY) < 0) {
2795 fprintf(stderr, "Strange, can not _chmod (or unlink) %s.\n", f);
2800 fprintf(stderr, "Strange, can not unlink %s.\n", n);
2807 static int getreply(m, failact)
2811 static FILE *tty = NULL;
2815 if (tty == NULL && (tty = fopen(TTY, "r")) == NULL) {
2816 fprintf(stderr, "Can not open %s to get reply.\n", TTY);
2825 fprintf(stderr, "Can not get reply.\n");
2832 while ((c = fgetc(tty)) != '\n' && c != EOF)
2835 if (r == 'y' || r == 'n')
2837 fprintf(stderr, "Yes or No? ");
2842 static void *myalloc(k)
2849 if ((ret = (void *)malloc(k)) == NULL) {
2850 fprintf(stderr, "Insufficient memory.\n");
2857 static void *challoc(k, which)
2863 SLICER *sl = &(slicer[which]);
2865 if (k > sl->sl_len) {
2867 q = NULL, p = freechunks;
2868 p != NULL && (sl->sl_len = p->ch_len) < k;
2869 q = p, p = p->ch_next
2873 sl->sl_len = CHUNKSIZE - sizeof(CHUNK *);
2874 p = (CHUNK *)myalloc(CHUNKSIZE);
2877 freechunks = p->ch_next;
2879 q->ch_next = p->ch_next;
2880 p->ch_next = sl->sl_first;
2882 sl->sl_unused = (char *)&(p->ch_len);
2885 ret = (void *)sl->sl_unused;
2891 static void chgive(p, k)
2895 ((CHUNK *)p)->ch_len = k - sizeof(CHUNK *);
2896 ((CHUNK *)p)->ch_next = freechunks;
2897 freechunks = (CHUNK *)p;
2904 static void memmove(to, from, k)
2910 *(to++) = *(from++);
2915 *(--to) = *(--from);
2925 static int lastc = 0;
2929 return(lastc = getchar());
2933 static char *mygets(s, l)
2940 if (fgets(s, l, stdin) == NULL)
2942 if ((nl = strchr(s, '\n')) != NULL)
2944 fprintf(stderr, "Input string too long. Try again> ");
2957 static void cleanup()
2961 if (patch.ph_safeid) {
2962 for (i = 0; i < nhandles; i++) {
2963 if (!(handles[i]->h_di->di_flags & DI_CLEANED)) {
2964 sprintf(pathbuf, "%s%s%03d",
2965 handles[i]->h_name, IDF, handles[i]->h_di->di_did);
2966 if (unlink(pathbuf))
2967 fprintf(stderr, "Strange, couldn't unlink %s.\n", pathbuf);
2968 handles[i]->h_di->di_flags |= DI_CLEANED;
2973 Write device availability: undocumented internal MS-D*S function.
2974 Restore previous value.
2976 bdos(0x37, olddevflag, 3);