1 /*
2 mmv 1.01b
3 Copyright (c) 1990 Vladimir Lanin.
4 This program may be freely used and copied on a non-commercial basis.
5
6 Author may be reached at:
7
8 lanin@csd2.nyu.edu
9
10 Vladimir Lanin
11 330 Wadsworth Ave, Apt 6F,
12 New York, NY 10040
13
14 Many thanks to those who have to contributed to the design
15 and/or coding of this program:
16
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:
26 bug reports.
27 */
28
29 /*
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.
40
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,
43 define SLASH.
44
45 When neither MSDOS nor SYSV are defined, compiles under BSD.
46
47 RENAME is automatically defined under MSDOS and BSD.
48
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.
56 */
57
58 static char USAGE[] =
59 #ifdef IS_MSDOS
60
61 "Usage: \
62 %s [-m|x%s|c|o|a|z] [-h] [-d|p] [-g|t] [-v|n] [from to]\n\
63 \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";
67
68 #define OTHEROPT (_osmajor < 3 ? "" : "|r")
69
70 #else
71
72 "Usage: \
73 %s [-m|x|r|c|o|a|l%s] [-h] [-d|p] [-g|t] [-v|n] [from to]\n\
74 \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. = can be used\n\
77 instead of #.\n\
78 \n\
79 A ``from'' pattern containing wildcards should be quoted when given\n\
80 on the command line. Also you may need to quote ``to'' pattern.\n\
81 \n\
82 Use -- as the end of options.\n";
83
84 #ifdef IS_SYSV
85 #define OTHEROPT ""
86 #else
87 #define OTHEROPT "|s"
88 #endif
89
90 #endif
91
92 #include <unistd.h>
93 #include <stdio.h>
94 #include <ctype.h>
95
96 #ifdef IS_MSDOS
97 /* for MS-DOS (under Turbo C 1.5)*/
98
99 #include <string.h>
100 #include <stdlib.h>
101 #include <sys/stat.h>
102 #include <dos.h>
103 #include <dir.h>
104 #include <io.h>
105 #include <fcntl.h>
106
107 #define ESC '\''
108 #ifdef SLASH
109 #define SLASH '\\'
110 #define OTHERSLASH '/'
111 #else
112 #define SLASH '/'
113 #define OTHERSLASH '\\'
114 #endif
115
116 typedef int DIRID;
117 typedef int DEVID;
118
119 static char TTY[] = "/dev/con";
120 extern unsigned _stklen = 10000;
121
122 #undef HAS_RENAME
123 #define HAS_RENAME 1
124
125 #else
126 /* for various flavors of UN*X */
127
128 #include <libgen.h>
129 #include <stdlib.h>
130 #include <sys/types.h>
131 #include <sys/stat.h>
132 #include <sys/file.h>
133
134 #ifdef HAS_DIRENT
135 #include <dirent.h>
136 typedef struct dirent DIRENTRY;
137 #else
138 #ifdef IS_SYSV
139 #include <sys/dir.h>
140 /* might need to be changed to <dir.h> */
141 #else
142 #include <sys/dir.h>
143 #endif
144 typedef struct direct DIRENTRY;
145 #endif
146
147 #ifndef __STDC__
148 #ifndef __GNUC__
149 #ifndef IS_SYSV
150 #ifndef IS_BSD
151 #define void char /* might want to remove this line */
152 #endif
153 #endif
154 #endif
155 #endif
156
157 #ifndef O_BINARY
158 #define O_BINARY 0
159 #endif
160 #ifndef R_OK
161 #define R_OK 4
162 #define W_OK 2
163 #define X_OK 1
164 #endif
165
166 #define ESC '\\'
167 #define SLASH '/'
168
169 typedef ino_t DIRID;
170 typedef dev_t DEVID;
171
172 #define MAXPATH 1024
173
174 static char TTY[] = "/dev/tty";
175
176 #ifdef IS_V7
177 /* for Version 7 */
178 #include <errno.h>
179 extern int errno;
180 #define strchr index
181 extern char *strcpy(), *strchr();
182 #include <signal.h>
183 #define O_RDONLY 0
184 #define O_WRONLY 1
185 #define O_RDWR 2
186
187 #else
188 /* for System V and BSD */
189 #include <string.h>
190 #include <signal.h>
191 #include <fcntl.h>
192 #endif
193
194 #ifdef IS_SYSV
195
196 /* for System V and Version 7*/
197 struct utimbuf {
198 time_t actime;
199 time_t modtime;
200 };
201 #define utimes(f, t) utime((f), &(t))
202
203 #ifndef HAS_RENAME
204 #ifndef MV_DIR
205 # define MV_DIR "/usr/lib/mv_dir"
206 #endif
207 #endif
208
209 #ifdef MV_DIR
210 # define HAS_RENAME
211 #endif
212
213 #else
214
215 /* for BSD */
216 #undef HAS_RENAME
217 #define HAS_RENAME 1
218 #include <sys/time.h>
219
220 #endif
221 #endif
222
223 #define mylower(c) (isupper(c) ? (c)-'A'+'a' : (c))
224 #define myupper(c) (islower(c) ? (c)-'a'+'A' : (c))
225 #define STRLEN(s) (sizeof(s) - 1)
226 #define mydup(s) (strcpy((char *)challoc(strlen(s) + 1, 0), (s)))
227
228
229 #define DFLT 0x001
230 #define NORMCOPY 0x002
231 #define OVERWRITE 0x004
232 #define NORMMOVE 0x008
233 #define XMOVE 0x010
234 #define DIRMOVE 0x020
235 #define NORMAPPEND 0x040
236 #define ZAPPEND 0x080
237 #define HARDLINK 0x100
238 #define SYMLINK 0x200
239
240 #define COPY (NORMCOPY | OVERWRITE)
241 #define MOVE (NORMMOVE | XMOVE | DIRMOVE)
242 #define APPEND (NORMAPPEND | ZAPPEND)
243 #define LINK (HARDLINK | SYMLINK)
244
245 static char MOVENAME[] = "mmv";
246 static char COPYNAME[] = "mcp";
247 static char APPENDNAME[] = "mad";
248 static char LINKNAME[] = "mln";
249
250 #define ASKDEL 0
251 #define ALLDEL 1
252 #define NODEL 2
253
254 #define ASKBAD 0
255 #define SKIPBAD 1
256 #define ABORTBAD 2
257
258 #define STAY 0
259 #define LOWER 1
260 #define UPPER 2
261
262 #define MAXWILD 20
263 #define MAXPATLEN MAXPATH
264 #define INITROOM 10
265 #define CHUNKSIZE 2048
266 #define BUFSIZE 4096
267
268 #define FI_STTAKEN 0x01
269 #define FI_LINKERR 0x02
270 #define FI_INSTICKY 0x04
271 #define FI_NODEL 0x08
272 #define FI_KNOWWRITE 0x010
273 #define FI_CANWRITE 0x20
274 #define FI_ISDIR 0x40
275 #define FI_ISLNK 0x80
276
277 typedef struct {
278 char *fi_name;
279 struct rep *fi_rep;
280 #ifdef IS_MSDOS
281 char fi_attrib;
282 #else
283 short fi_mode;
284 char fi_stflags;
285 #endif
286 } FILEINFO;
287
288 #define DI_KNOWWRITE 0x01
289 #define DI_CANWRITE 0x02
290 #define DI_CLEANED 0x04
291
292 typedef struct {
293 DEVID di_vid;
294 DIRID di_did;
295 unsigned di_nfils;
296 FILEINFO **di_fils;
297 char di_flags;
298 } DIRINFO;
299
300 #define H_NODIR 1
301 #define H_NOREADDIR 2
302
303 typedef struct {
304 char *h_name;
305 DIRINFO *h_di;
306 char h_err;
307 } HANDLE;
308
309 #define R_ISX 0x01
310 #define R_SKIP 0x02
311 #define R_DELOK 0x04
312 #define R_ISALIASED 0x08
313 #define R_ISCYCLE 0x10
314 #define R_ONEDIRLINK 0x20
315
316 typedef struct rep {
317 HANDLE *r_hfrom;
318 FILEINFO *r_ffrom;
319 HANDLE *r_hto;
320 char *r_nto; /* non-path part of new name */
321 FILEINFO *r_fdel;
322 struct rep *r_first;
323 struct rep *r_thendo;
324 struct rep *r_next;
325 char r_flags;
326 } REP;
327
328 typedef struct {
329 REP *rd_p;
330 DIRINFO *rd_dto;
331 char *rd_nto;
332 unsigned rd_i;
333 } REPDICT;
334
335 typedef struct chunk {
336 struct chunk *ch_next;
337 unsigned ch_len;
338 } CHUNK;
339
340 typedef struct {
341 CHUNK *sl_first;
342 char *sl_unused;
343 int sl_len;
344 } SLICER;
345
346
347 static void init(/* */);
348 static void procargs(/* int argc, char **argv,
349 char **pfrompat, char **ptopat */);
350 static void domatch(/* char *cfrom, char *cto */);
351 static int getpat(/* */);
352 static int getword(/* char *buf */);
353 static void matchpat(/* */);
354 static int parsepat(/* */);
355 static int dostage(/* char *lastend, char *pathend,
356 char **start1, int *len1, int stage, int anylev */);
357 static int trymatch(/* FILEINFO *ffrom, char *pat */);
358 static int keepmatch(/* FILEINFO *ffrom, char *pathend,
359 int *pk, int needslash, int dirs, int fils */);
360 static int badrep(/* HANDLE *hfrom, FILEINFO *ffrom,
361 HANDLE **phto, char **pnto, FILEINFO **pfdel, int *pflags */);
362 static int checkto(/* HANDLE *hfrom, char *f,
363 HANDLE **phto, char **pnto, FILEINFO **pfdel */);
364 static char *getpath(/* char *tpath */);
365 static int badname(/* char *s */);
366 static FILEINFO *fsearch(/* char *s, DIRINFO *d */);
367 static int ffirst(/* char *s, int n, DIRINFO *d */);
368 static HANDLE *checkdir(/* char *p, char *pathend, int which */);
369 static void takedir(/*
370 char *p, DIRINFO *di, int sticky
371 or
372 struct ffblk *pff, DIRINFO *di
373 */);
374 static int fcmp(/* FILEINFO **pf1, FILEINFO **pf2 */);
375 static HANDLE *hadd(/* char *n */);
376 static int hsearch(/* char *n, int which, HANDLE **ph */);
377 static DIRINFO *dadd(/* DEVID v, DIRID d */);
378 static DIRINFO *dsearch(/* DEVID v, DIRID d */);
379 static int match(/* char *pat, char *s, char **start1, int *len1 */);
380 static void makerep(/* */);
381 static void checkcollisions(/* */);
382 static int rdcmp(/* REPDICT *rd1, REPDICT *rd2 */);
383 static void findorder(/* */);
384 static void scandeletes(/* int (*pkilldel)(REP *p) */);
385 static int baddel(/* REP *p */);
386 static int skipdel(/* REP *p */);
387 static void nochains(/* */);
388 static void printchain(/* REP *p */);
389 static void goonordie(/* */);
390 static void doreps(/* */);
391 static long appendalias(/* REP *first, REP *p, int *pprintaliased */);
392 static int movealias(/* REP *first, REP *p, int *pprintaliased */);
393 static int snap(/* REP *first, REP *p */);
394 static void showdone(/* REP *fin */);
395 static void breakout(/* */);
396 static void breakrep(int);
397 static void breakstat(/* */);
398 static void quit(/* */);
399 static int copymove(/* REP *p */);
400 static int copy(/* FILENFO *f, long len */);
401 static int myunlink(/* char *n, FILEINFO *f */);
402 static int getreply(/* char *m, int failact */);
403 static void *myalloc(/* unsigned k */);
404 static void *challoc(/* int k, int which */);
405 static void chgive(/* void *p, unsigned k */);
406 static int mygetc(/* */);
407 static char *mygets(/* char *s, int l */);
408 #ifdef IS_MSDOS
409 static int leave(/* */);
410 static void cleanup(/* */);
411 #else
412 static int getstat(/* char *full, FILEINFO *f */);
413 static int dwritable(/* HANDLE *h */);
414 static int fwritable(/* char *hname, FILEINFO *f */);
415 #ifndef __STDC__
416 #ifndef IS_MSDOS
417 #ifndef IS_SYSV
418 static void memmove(/* void *to, void *from, int k */);
419 #endif
420 #endif
421 #endif
422 #endif
423 #ifndef HAS_RENAME
424 static int rename(/* char *from, char *to */);
425 #endif
426
427 static int op, badstyle, delstyle, verbose, noex, matchall;
428 static int patflags;
429
430 static unsigned ndirs = 0, dirroom;
431 static DIRINFO **dirs;
432 static unsigned nhandles = 0, handleroom;
433 static HANDLE **handles;
434 static HANDLE badhandle = {"\200", NULL, 0};
435 static HANDLE *(lasthandle[2]) = {&badhandle, &badhandle};
436 static unsigned nreps = 0;
437 static REP hrep, *lastrep = &hrep;
438 static CHUNK *freechunks = NULL;
439 static SLICER slicer[2] = {{NULL, NULL, 0}, {NULL, NULL, 0}};
440
441 static int badreps = 0, paterr = 0, direrr, failed = 0, gotsig = 0, repbad;
442 static FILE *outfile;
443
444 #ifdef IS_MSDOS
445 static char IDF[] = "$$mmvdid.";
446 #endif
447 static char TEMP[] = "$$mmvtmp.";
448 static char TOOLONG[] = "(too long)";
449 static char EMPTY[] = "(empty)";
450
451 static char SLASHSTR[] = {SLASH, '\0'};
452
453 static char PATLONG[] = "%.40s... : pattern too long.\n";
454
455 char from[MAXPATLEN], to[MAXPATLEN];
456 static int fromlen, tolen;
457 static char *(stagel[MAXWILD]), *(firstwild[MAXWILD]), *(stager[MAXWILD]);
458 static int nwilds[MAXWILD];
459 static int nstages;
460 char pathbuf[MAXPATH];
461 char fullrep[MAXPATH + 1];
462 static char *(start[MAXWILD]);
463 static int len[MAXWILD];
464 static REP mistake;
465 #define MISTAKE (&mistake)
466
467 #ifdef IS_MSDOS
468
469 static char hasdot[MAXWILD];
470 static int olddevflag, curdisk, maxdisk;
471 static struct {
472 char ph_banner[30];
473 char ph_name[9];
474 int ph_dfltop;
475 int ph_safeid;
476 int ph_clustoff;
477 int ph_driveoff;
478 int ph_drivea;
479 } patch = {"mmv 1.0 patchable flags", "mmv", XMOVE, 1, 0};
480
481 #define DFLTOP (patch.ph_dfltop)
482 #define CLUSTNO(pff) (*(int *)(((char *)(pff)) + patch.ph_clustoff))
483 #define DRIVENO(pff) (*(((char *)(pff)) + patch.ph_driveoff) - patch.ph_drivea)
484
485
486 #else
487
488 #define DFLTOP XMOVE
489
490 static char *home;
491 static int homelen;
492 static int uid, euid, oldumask;
493 static DIRID cwdd = -1;
494 static DEVID cwdv = -1;
495
496 #endif
497
498
main(argc,argv)499 int main(argc, argv)
500 int argc;
501 char *(argv[]);
502 {
503 char *frompat, *topat;
504
505 outfile = stdout;
506
507 init();
508 procargs(argc, argv, &frompat, &topat);
509 domatch(frompat, topat);
510 if (!(op & APPEND))
511 checkcollisions();
512 findorder();
513 if (op & (COPY | LINK))
514 nochains();
515 scandeletes(baddel);
516 goonordie();
517 if (!(op & APPEND) && delstyle == ASKDEL)
518 scandeletes(skipdel);
519 doreps();
520 return(failed ? 2 : nreps == 0 && (paterr || badreps));
521 }
522
523
init()524 static void init()
525 {
526 #ifdef IS_MSDOS
527 curdisk = getdisk();
528 maxdisk = setdisk(curdisk);
529 /*
530 Read device availability : undocumented internal MS-DOS function.
531 If (_DX == 0) then \dev\ must precede device names.
532 */
533 bdos(0x37, 0, 2);
534 olddevflag = _DX;
535 /*
536 Write device availability: undocumented internal MS-DOS function.
537 Specify \dev\ must precede device names.
538 */
539 bdos(0x37, 0, 3);
540 atexit((atexit_t)cleanup);
541 ctrlbrk((int (*)())breakout);
542 #else
543 struct stat dstat;
544
545 if ((home = getenv("HOME")) == NULL || strcmp(home, SLASHSTR) == 0)
546 home = "";
547 if (!stat(".", &dstat)) {
548 cwdd = dstat.st_ino;
549 cwdv = dstat.st_dev;
550 }
551 oldumask = umask(0);
552 euid = geteuid();
553 uid = getuid();
554 signal(SIGINT, breakout);
555 #endif
556
557 dirroom = handleroom = INITROOM;
558 dirs = (DIRINFO **)myalloc(dirroom * sizeof(DIRINFO *));
559 handles = (HANDLE **)myalloc(handleroom * sizeof(HANDLE *));
560 ndirs = nhandles = 0;
561 }
562
563
procargs(argc,argv,pfrompat,ptopat)564 static void procargs(argc, argv, pfrompat, ptopat)
565 int argc;
566 char **argv;
567 char **pfrompat, **ptopat;
568 {
569 char *p, c;
570 char *cmdname = basename(argv[0]);
571
572 #ifdef IS_MSDOS
573 #define CMDNAME (patch.ph_name)
574 #else
575 #define CMDNAME cmdname
576 #endif
577
578 op = DFLT;
579 verbose = noex = matchall = 0;
580 delstyle = ASKDEL;
581 badstyle = ASKBAD;
582 for (argc--, argv++; argc > 0 && **argv == '-'; argc--, argv++)
583 for (p = *argv + 1; *p != '\0'; p++) {
584 c = mylower(*p);
585 if (c == '-') {
586 argc--;
587 argv++;
588 goto endargs;
589 }
590 if (c == 'v' && !noex)
591 verbose = 1;
592 else if (c == 'n' && !verbose)
593 noex = 1;
594 else if (c == 'h')
595 matchall = 1;
596 else if (c == 'd' && delstyle == ASKDEL)
597 delstyle = ALLDEL;
598 else if (c == 'p' && delstyle == ASKDEL)
599 delstyle = NODEL;
600 else if (c == 'g' && badstyle == ASKBAD)
601 badstyle = SKIPBAD;
602 else if (c == 't' && badstyle == ASKBAD)
603 badstyle = ABORTBAD;
604 else if (c == 'm' && op == DFLT)
605 op = NORMMOVE;
606 else if (c == 'x' && op == DFLT)
607 op = XMOVE;
608 else if (c == 'r' && op == DFLT)
609 op = DIRMOVE;
610 else if (c == 'c' && op == DFLT)
611 op = NORMCOPY;
612 else if (c == 'o' && op == DFLT)
613 op = OVERWRITE;
614 else if (c == 'a' && op == DFLT)
615 op = NORMAPPEND;
616 #ifdef IS_MSDOS
617 else if (c == 'z' && op == DFLT)
618 op = ZAPPEND;
619 #else
620 else if (c == 'l' && op == DFLT)
621 op = HARDLINK;
622 #ifdef S_IFLNK
623 else if (c == 's' && op == DFLT)
624 op = SYMLINK;
625 #endif
626 #endif
627 else {
628 fprintf(stderr, USAGE, CMDNAME, OTHEROPT);
629 exit(1);
630 }
631 }
632
633 endargs:
634 if (op == DFLT) {
635 if (strcmp(cmdname, MOVENAME) == 0)
636 op = XMOVE;
637 else if (strcmp(cmdname, COPYNAME) == 0)
638 op = NORMCOPY;
639 else if (strcmp(cmdname, APPENDNAME) == 0)
640 op = NORMAPPEND;
641 else if (strcmp(cmdname, LINKNAME) == 0)
642 op = HARDLINK;
643 else
644 op = DFLTOP;
645 }
646
647 if (
648 op & DIRMOVE &&
649 #ifdef IS_MSDOS
650 _osmajor < 3
651 #else
652 #ifndef HAS_RENAME
653 euid != 0
654 #else
655 0
656 #endif
657 #endif
658 ) {
659 fprintf(stderr,
660 "Unable to do directory renames. Option -r refused.\n");
661 quit();
662 }
663
664 #ifndef IS_MSDOS
665 if (euid != uid && !(op & DIRMOVE)) {
666 setuid(uid);
667 setgid(getgid());
668 }
669 #endif
670
671 if (badstyle != ASKBAD && delstyle == ASKDEL)
672 delstyle = NODEL;
673
674 if (argc == 0)
675 *pfrompat = NULL;
676 else if (argc == 2) {
677 *pfrompat = *(argv++);
678 *ptopat = *(argv++);
679 }
680 else {
681 fprintf(stderr, USAGE, CMDNAME, OTHEROPT);
682 exit(1);
683 }
684 }
685
686
domatch(cfrom,cto)687 static void domatch(cfrom, cto)
688 char *cfrom, *cto;
689 {
690 if (cfrom == NULL)
691 while (getpat())
692 matchpat();
693 else if ((fromlen = strlen(cfrom)) >= MAXPATLEN) {
694 printf(PATLONG, cfrom);
695 paterr = 1;
696 }
697 else if ((tolen = strlen(cto)) >= MAXPATLEN) {
698 printf(PATLONG, cto);
699 paterr = 1;
700 }
701 else {
702 strcpy(from, cfrom);
703 strcpy(to, cto);
704 matchpat();
705 }
706 }
707
708
getpat()709 static int getpat()
710 {
711 int c, gotit = 0;
712 char extra[MAXPATLEN];
713
714 patflags = 0;
715 do {
716 if ((fromlen = getword(from)) == 0 || fromlen == -1)
717 goto nextline;
718
719 do {
720 if ((tolen = getword(to)) == 0) {
721 printf("%s -> ? : missing replacement pattern.\n", from);
722 goto nextline;
723 }
724 if (tolen == -1)
725 goto nextline;
726 } while (
727 tolen == 2 &&
728 (to[0] == '-' || to[0] == '=') &&
729 (to[1] == '>' || to[1] == '^')
730 );
731 if (getword(extra) == 0)
732 gotit = 1;
733 else if (strcmp(extra, "(*)") == 0) {
734 patflags |= R_DELOK;
735 gotit = (getword(extra) == 0);
736 }
737
738 nextline:
739 while ((c = mygetc()) != '\n' && c != EOF)
740 ;
741 if (c == EOF)
742 return(0);
743 } while (!gotit);
744
745 return(1);
746 }
747
748
getword(buf)749 static int getword(buf)
750 char *buf;
751 {
752 int c, prevc, n;
753 char *p;
754
755 p = buf;
756 prevc = ' ';
757 n = 0;
758 while ((c = mygetc()) != EOF && (prevc == ESC || !isspace(c))) {
759 if (n == -1)
760 continue;
761 if (n == MAXPATLEN - 1) {
762 *p = '\0';
763 printf(PATLONG, buf);
764 n = -1;
765 }
766 *(p++) = c;
767 n++;
768 prevc = c;
769 }
770 *p = '\0';
771 while (c != EOF && isspace(c) && c != '\n')
772 c = mygetc();
773 if (c != EOF)
774 ungetc(c, stdin);
775 return(n);
776 }
777
778
matchpat()779 static void matchpat()
780 {
781 if (parsepat())
782 paterr = 1;
783 else if (dostage(from, pathbuf, start, len, 0, 0)) {
784 printf("%s -> %s : no match.\n", from, to);
785 paterr = 1;
786 }
787 }
788
789
parsepat()790 static int parsepat()
791 {
792 char *p, *lastname, c;
793 int totwilds, instage, x;
794 static char TRAILESC[] = "%s -> %s : trailing %c is superfluous.\n";
795
796 lastname = from;
797 #ifdef IS_MSDOS
798 havedot = 0;
799 if (from[0] != '\0' && from[1] == ':')
800 lastname += 2;
801 #else
802 if (from[0] == '~' && from[1] == SLASH) {
803 if ((homelen = strlen(home)) + fromlen > MAXPATLEN) {
804 printf(PATLONG, from);
805 return(-1);
806 }
807 memmove(from + homelen, from + 1, fromlen);
808 memmove(from, home, homelen);
809 lastname += homelen + 1;
810 }
811 #endif
812 totwilds = nstages = instage = 0;
813 for (p = lastname; (c = *p) != '\0'; p++)
814 switch (c) {
815 #ifdef IS_MSDOS
816 case '.':
817 havedot = 1;
818 break;
819 case OTHERSLASH:
820 *p = SLASH;
821 #endif
822 case SLASH:
823 #ifdef IS_MSDOS
824 if (!havedot && lastname != p) {
825 if (fromlen++ == MAXPATLEN) {
826 printf(PATLONG, from);
827 return(-1);
828 }
829 memmove(p + 1, p, strlen(p) + 1);
830 *(p++) = '.';
831 }
832 else
833 havedot = 0;
834 #endif
835 lastname = p + 1;
836 if (instage) {
837 if (firstwild[nstages] == NULL)
838 firstwild[nstages] = p;
839 stager[nstages++] = p;
840 instage = 0;
841 }
842 break;
843 case ';':
844 if (lastname != p) {
845 printf("%s -> %s : badly placed ;.\n", from, to);
846 return(-1);
847 }
848 case '!':
849 case '*':
850 case '?':
851 case '[':
852 #ifdef IS_MSDOS
853 if ((hasdot[totwilds] = (c == '!')) != 0)
854 havedot = 1;
855 #endif
856 if (totwilds++ == MAXWILD) {
857 printf("%s -> %s : too many wildcards.\n", from, to);
858 return(-1);
859 }
860 if (instage) {
861 nwilds[nstages]++;
862 if (firstwild[nstages] == NULL)
863 firstwild[nstages] = p;
864 }
865 else {
866 stagel[nstages] = lastname;
867 firstwild[nstages] = (c == ';' ? NULL : p);
868 nwilds[nstages] = 1;
869 instage = 1;
870 }
871 if (c != '[')
872 break;
873 while ((c = *(++p)) != ']') {
874 switch (c) {
875 case '\0':
876 printf("%s -> %s : missing ].\n", from, to);
877 return(-1);
878 #ifdef IS_MSDOS
879 case '.':
880 case ':':
881 case OTHERSLASH:
882 #endif
883 case SLASH:
884 printf("%s -> %s : '%c' can not be part of [].\n",
885 from, to, c);
886 return(-1);
887 case ESC:
888 if ((c = *(++p)) == '\0') {
889 printf(TRAILESC, from, to, ESC);
890 return(-1);
891 }
892 #ifdef IS_MSDOS
893 default:
894 if (isupper(c))
895 *p = c + ('a' - 'A');
896 #endif
897 }
898 }
899 break;
900 case ESC:
901 if ((c = *(++p)) == '\0') {
902 printf(TRAILESC, from, to, ESC);
903 return(-1);
904 }
905 #ifdef IS_MSDOS
906 default:
907 if (isupper(c))
908 *p = c + ('a' - 'A');
909 #endif
910 }
911
912 #ifdef IS_MSDOS
913 if (!havedot && lastname != p) {
914 if (fromlen++ == MAXPATLEN) {
915 printf(PATLONG, from);
916 return(-1);
917 }
918 strcpy(p++, ".");
919 }
920 #endif
921
922 if (instage) {
923 if (firstwild[nstages] == NULL)
924 firstwild[nstages] = p;
925 stager[nstages++] = p;
926 }
927 else {
928 stagel[nstages] = lastname;
929 nwilds[nstages] = 0;
930 firstwild[nstages] = p;
931 stager[nstages++] = p;
932 }
933
934 lastname = to;
935 #ifdef IS_MSDOS
936 havedot = 0;
937 if (to[0] != '\0' && to[1] == ':')
938 lastname += 2;
939 #else
940 if (to[0] == '~' && to[1] == SLASH) {
941 if ((homelen = strlen(home)) + tolen > MAXPATLEN) {
942 printf(PATLONG, to);
943 return(-1);
944 }
945 memmove(to + homelen, to + 1, tolen);
946 memmove(to, home, homelen);
947 lastname += homelen + 1;
948 }
949 #endif
950
951 for (p = lastname; (c = *p) != '\0'; p++)
952 switch (c) {
953 #ifdef IS_MSDOS
954 case '.':
955 havedot = 1;
956 break;
957 case OTHERSLASH:
958 *p = SLASH;
959 #endif
960 case SLASH:
961 if (op & DIRMOVE) {
962 printf("%s -> %s : no path allowed in target under -r.\n",
963 from, to);
964 return(-1);
965 }
966 #ifdef IS_MSDOS
967 if (!havedot && lastname != p) {
968 if (tolen++ == MAXPATLEN) {
969 printf(PATLONG, to);
970 return(-1);
971 }
972 memmove(p + 1, p, strlen(p) + 1);
973 *(p++) = '.';
974 }
975 else
976 havedot = 0;
977 #endif
978 lastname = p + 1;
979 break;
980 case '#':
981 case '=':
982 {
983 char index_char=c;
984 c = *(++p);
985 if (c == 'l' || c == 'u') {
986 #ifdef IS_MSDOS
987 strcpy(p, p + 1);
988 c = *p;
989 #else
990 c = *(++p);
991 #endif
992 }
993 if (!isdigit(c)) {
994 printf("%s -> %s : expected digit (not '%c') after %c.\n",
995 from, to, c, index_char);
996 return(-1);
997 }
998 for(x = 0; ;x *= 10) {
999 x += c - '0';
1000 c = *(p+1);
1001 if (!isdigit(c))
1002 break;
1003 p++;
1004 }
1005 if (x < 1 || x > totwilds) {
1006 printf("%s -> %s : wildcard %c%d does not exist.\n",
1007 from, to, index_char, x);
1008 return(-1);
1009 }
1010 #ifdef IS_MSDOS
1011 if (hasdot[x - 1])
1012 havedot = 1;
1013 #endif
1014 break;
1015 }
1016 case ESC:
1017 if ((c = *(++p)) == '\0') {
1018 printf(TRAILESC, from, to, ESC);
1019 return(-1);
1020 }
1021 #ifdef IS_MSDOS
1022 default:
1023 if (
1024 c <= ' ' || c >= 127 ||
1025 strchr(":/\\*?[]=+;,\"|<>", c) != NULL
1026 ) {
1027 printf("%s -> %s : illegal character '%c' (0x%02X).\n",
1028 from, to, c, c);
1029 return(-1);
1030 }
1031 if (isupper(c))
1032 *p = c + ('a' - 'A');
1033 #endif
1034 }
1035
1036 #ifdef IS_MSDOS
1037 if (!havedot && lastname != p) {
1038 if (tolen++ == MAXPATLEN) {
1039 printf(PATLONG, to);
1040 return(-1);
1041 }
1042 strcpy(p++, ".");
1043 }
1044 #endif
1045
1046 return(0);
1047 }
1048
1049
dostage(lastend,pathend,start1,len1,stage,anylev)1050 static int dostage(lastend, pathend, start1, len1, stage, anylev)
1051 char *lastend, *pathend;
1052 char **start1;
1053 int *len1;
1054 int stage;
1055 int anylev;
1056 {
1057 DIRINFO *di;
1058 HANDLE *h, *hto;
1059 int prelen, litlen, nfils, i, k, flags, try;
1060 FILEINFO **pf, *fdel = NULL;
1061 char *nto, *firstesc;
1062 REP *p;
1063 int wantdirs, ret = 1, laststage = (stage + 1 == nstages);
1064
1065 wantdirs = !laststage ||
1066 (op & (DIRMOVE | SYMLINK)) ||
1067 (nwilds[nstages - 1] == 0);
1068
1069 if (!anylev) {
1070 prelen = stagel[stage] - lastend;
1071 if (pathend - pathbuf + prelen >= MAXPATH) {
1072 printf("%s -> %s : search path after %s too long.\n",
1073 from, to, pathbuf);
1074 paterr = 1;
1075 return(1);
1076 }
1077 memmove(pathend, lastend, prelen);
1078 pathend += prelen;
1079 *pathend = '\0';
1080 lastend = stagel[stage];
1081 }
1082
1083 if ((h = checkdir(pathbuf, pathend, 0)) == NULL) {
1084 if (stage == 0 || direrr == H_NOREADDIR) {
1085 printf("%s -> %s : directory %s does not %s.\n",
1086 from, to, pathbuf, direrr == H_NOREADDIR ?
1087 "allow reads/searches" : "exist");
1088 paterr = 1;
1089 }
1090 return(stage);
1091 }
1092 di = h->h_di;
1093
1094 if (*lastend == ';') {
1095 anylev = 1;
1096 *start1 = pathend;
1097 *len1 = 0;
1098 lastend++;
1099 }
1100
1101 nfils = di->di_nfils;
1102
1103 #ifndef IS_MSDOS
1104 if ((op & MOVE) && !dwritable(h)) {
1105 printf("%s -> %s : directory %s does not allow writes.\n",
1106 from, to, pathbuf);
1107 paterr = 1;
1108 goto skiplev;
1109 }
1110 #endif
1111
1112 firstesc = strchr(lastend, ESC);
1113 if (firstesc == NULL || firstesc > firstwild[stage])
1114 firstesc = firstwild[stage];
1115 litlen = firstesc - lastend;
1116 pf = di->di_fils + (i = ffirst(lastend, litlen, di));
1117 if (i < nfils)
1118 do {
1119 if (
1120 (try = trymatch(*pf, lastend)) != 0 &&
1121 (
1122 try == 1 ||
1123 match(lastend + litlen, (*pf)->fi_name + litlen,
1124 start1 + anylev, len1 + anylev)
1125 ) &&
1126 keepmatch(*pf, pathend, &k, 0, wantdirs, laststage)
1127 ) {
1128 if (!laststage)
1129 ret &= dostage(stager[stage], pathend + k,
1130 start1 + nwilds[stage], len1 + nwilds[stage],
1131 stage + 1, 0);
1132 else {
1133 ret = 0;
1134 makerep();
1135 if (badrep(h, *pf, &hto, &nto, &fdel, &flags))
1136 (*pf)->fi_rep = MISTAKE;
1137 else {
1138 (*pf)->fi_rep = p = (REP *)challoc(sizeof(REP), 1);
1139 p->r_flags = flags | patflags;
1140 p->r_hfrom = h;
1141 p->r_ffrom = *pf;
1142 p->r_hto = hto;
1143 p->r_nto = nto;
1144 p->r_fdel = fdel;
1145 p->r_first = p;
1146 p->r_thendo = NULL;
1147 p->r_next = NULL;
1148 lastrep->r_next = p;
1149 lastrep = p;
1150 nreps++;
1151 }
1152 }
1153 }
1154 i++, pf++;
1155 } while (i < nfils && strncmp(lastend, (*pf)->fi_name, litlen) == 0);
1156
1157 skiplev:
1158 if (anylev)
1159 for (pf = di->di_fils, i = 0; i < nfils; i++, pf++)
1160 if (
1161 *((*pf)->fi_name) != '.' &&
1162 #ifdef IS_MSDOS
1163 ((*pf)->fi_attrib & FA_DIREC) &&
1164 #endif
1165 keepmatch(*pf, pathend, &k, 1, 1, 0)
1166 ) {
1167 *len1 = pathend - *start1 + k;
1168 ret &= dostage(lastend, pathend + k, start1, len1, stage, 1);
1169 }
1170
1171 return(ret);
1172 }
1173
1174
trymatch(ffrom,pat)1175 static int trymatch(ffrom, pat)
1176 FILEINFO *ffrom;
1177 char *pat;
1178 {
1179 char *p;
1180
1181 if (ffrom->fi_rep != NULL)
1182 return(0);
1183
1184 p = ffrom->fi_name;
1185
1186 #ifdef IS_MSDOS
1187 if (*p == '.' || (!matchall && ffrom->fi_attrib & (FA_HIDDEN | FA_SYSTEM)))
1188 return(strcmp(pat, p) == 0);
1189 #else
1190 if (*p == '.') {
1191 if (p[1] == '\0' || (p[1] == '.' && p[2] == '\0'))
1192 return(strcmp(pat, p) == 0);
1193 else if (!matchall && *pat != '.')
1194 return(0);
1195 }
1196 #endif
1197 return(-1);
1198 }
1199
1200
keepmatch(ffrom,pathend,pk,needslash,dirs,fils)1201 static int keepmatch(ffrom, pathend, pk, needslash, dirs, fils)
1202 FILEINFO *ffrom;
1203 char *pathend;
1204 int *pk;
1205 int needslash;
1206 int dirs, fils;
1207 {
1208 *pk = strlen(ffrom->fi_name);
1209 if (pathend - pathbuf + *pk + needslash >= MAXPATH) {
1210 *pathend = '\0';
1211 printf("%s -> %s : search path %s%s too long.\n",
1212 from, to, pathbuf, ffrom->fi_name);
1213 paterr = 1;
1214 return(0);
1215 }
1216 strcpy(pathend, ffrom->fi_name);
1217 #ifdef IS_MSDOS
1218 if ((ffrom->fi_attrib & FA_DIREC) ? !dirs : !fils)
1219 #else
1220 getstat(pathbuf, ffrom);
1221 if ((ffrom->fi_stflags & FI_ISDIR) ? !dirs : !fils)
1222 #endif
1223 {
1224 if (verbose)
1225 printf("ignoring directory %s\n", ffrom->fi_name);
1226 return(0);
1227 }
1228
1229 if (needslash) {
1230 strcpy(pathend + *pk, SLASHSTR);
1231 (*pk)++;
1232 }
1233 return(1);
1234 }
1235
1236
badrep(hfrom,ffrom,phto,pnto,pfdel,pflags)1237 static int badrep(hfrom, ffrom, phto, pnto, pfdel, pflags)
1238 HANDLE *hfrom;
1239 FILEINFO *ffrom;
1240 HANDLE **phto;
1241 char **pnto;
1242 FILEINFO **pfdel;
1243 int *pflags;
1244 {
1245 char *f = ffrom->fi_name;
1246
1247 *pflags = 0;
1248 if (
1249 #ifdef IS_MSDOS
1250 (ffrom->fi_attrib & FA_DIREC) &&
1251 #else
1252 (ffrom->fi_stflags & FI_ISDIR) &&
1253 #endif
1254 !(op & (DIRMOVE | SYMLINK))
1255 )
1256 printf("%s -> %s : source file is a directory.\n", pathbuf, fullrep);
1257 #ifndef IS_MSDOS
1258 #ifdef S_IFLNK
1259 else if ((ffrom->fi_stflags & FI_LINKERR) && !(op & (MOVE | SYMLINK)))
1260 printf("%s -> %s : source file is a badly aimed symbolic link.\n",
1261 pathbuf, fullrep);
1262 #endif
1263 #ifndef IS_SYSV
1264 else if ((ffrom->fi_stflags & FI_NODEL) && (op & MOVE))
1265 printf("%s -> %s : no delete permission for source file.\n",
1266 pathbuf, fullrep);
1267 #endif
1268 else if ((op & (COPY | APPEND)) && access(pathbuf, R_OK))
1269 printf("%s -> %s : no read permission for source file.\n",
1270 pathbuf, fullrep);
1271 #endif
1272 else if (
1273 *f == '.' &&
1274 (f[1] == '\0' || strcmp(f, "..") == 0) &&
1275 !(op & SYMLINK)
1276 )
1277 printf("%s -> %s : . and .. can't be renamed.\n", pathbuf, fullrep);
1278 else if (repbad || checkto(hfrom, f, phto, pnto, pfdel) || badname(*pnto))
1279 printf("%s -> %s : bad new name.\n", pathbuf, fullrep);
1280 else if (*phto == NULL)
1281 printf("%s -> %s : %s.\n", pathbuf, fullrep,
1282 #ifndef IS_MSDOS
1283 direrr == H_NOREADDIR ?
1284 "no read or search permission for target directory" :
1285 #endif
1286 "target directory does not exist");
1287 #ifndef IS_MSDOS
1288 else if (!dwritable(*phto))
1289 printf("%s -> %s : no write permission for target directory.\n",
1290 pathbuf, fullrep);
1291 #endif
1292 else if (
1293 (*phto)->h_di->di_vid != hfrom->h_di->di_vid &&
1294 (*pflags = R_ISX, (op & (NORMMOVE | HARDLINK)))
1295 )
1296 printf("%s -> %s : cross-device move.\n",
1297 pathbuf, fullrep);
1298 #ifndef IS_MSDOS
1299 else if (
1300 *pflags && (op & MOVE) &&
1301 !(ffrom->fi_stflags & FI_ISLNK) &&
1302 access(pathbuf, R_OK)
1303 )
1304 printf("%s -> %s : no read permission for source file.\n",
1305 pathbuf, fullrep);
1306 #ifdef S_IFLNK
1307 else if (
1308 (op & SYMLINK) &&
1309 !(
1310 ((*phto)->h_di->di_vid == cwdv && (*phto)->h_di->di_did == cwdd) ||
1311 *(hfrom->h_name) == SLASH ||
1312 (*pflags |= R_ONEDIRLINK, hfrom->h_di == (*phto)->h_di)
1313 )
1314 )
1315 printf("%s -> %s : symbolic link would be badly aimed.\n",
1316 pathbuf, fullrep);
1317 #endif
1318 #endif
1319 else
1320 return(0);
1321 badreps++;
1322 return(-1);
1323 }
1324
1325
checkto(hfrom,f,phto,pnto,pfdel)1326 static int checkto(hfrom, f, phto, pnto, pfdel)
1327 HANDLE *hfrom;
1328 char *f;
1329 HANDLE **phto;
1330 char **pnto;
1331 FILEINFO **pfdel;
1332 {
1333 char tpath[MAXPATH + 1];
1334 char *pathend;
1335 FILEINFO *fdel = NULL;
1336 int hlen, tlen;
1337
1338 if (op & DIRMOVE) {
1339 *phto = hfrom;
1340 hlen = strlen(hfrom->h_name);
1341 pathend = fullrep + hlen;
1342 memmove(pathend, fullrep, strlen(fullrep) + 1);
1343 memmove(fullrep, hfrom->h_name, hlen);
1344 if ((fdel = *pfdel = fsearch(pathend, hfrom->h_di)) != NULL) {
1345 *pnto = fdel->fi_name;
1346 #ifndef IS_MSDOS
1347 getstat(fullrep, fdel);
1348 #endif
1349 }
1350 else
1351 *pnto = mydup(pathend);
1352 }
1353 else {
1354 pathend = getpath(tpath);
1355 hlen = pathend - fullrep;
1356 *phto = checkdir(tpath, tpath + hlen, 1);
1357 if (
1358 *phto != NULL &&
1359 *pathend != '\0' &&
1360 (fdel = *pfdel = fsearch(pathend, (*phto)->h_di)) != NULL &&
1361 #ifdef IS_MSDOS
1362 (fdel->fi_attrib & FA_DIREC)
1363 #else
1364 (getstat(fullrep, fdel), fdel->fi_stflags & FI_ISDIR)
1365 #endif
1366 ) {
1367 tlen = strlen(pathend);
1368 strcpy(pathend + tlen, SLASHSTR);
1369 tlen++;
1370 strcpy(tpath + hlen, pathend);
1371 pathend += tlen;
1372 hlen += tlen;
1373 *phto = checkdir(tpath, tpath + hlen, 1);
1374 }
1375
1376 if (*pathend == '\0') {
1377 *pnto = f;
1378 if (pathend - fullrep + strlen(f) >= MAXPATH) {
1379 strcpy(fullrep, TOOLONG);
1380 return(-1);
1381 }
1382 strcat(pathend, f);
1383 if (*phto != NULL) {
1384 fdel = *pfdel = fsearch(f, (*phto)->h_di);
1385 #ifndef IS_MSDOS
1386 if (fdel != NULL)
1387 getstat(fullrep, fdel);
1388 #endif
1389 }
1390 }
1391 else if (fdel != NULL)
1392 *pnto = fdel->fi_name;
1393 else
1394 *pnto = mydup(pathend);
1395 }
1396 return(0);
1397 }
1398
1399
getpath(tpath)1400 static char *getpath(tpath)
1401 char *tpath;
1402 {
1403 char *pathstart, *pathend, c;
1404
1405 #ifdef IS_MSDOS
1406 if (*fullrep != '\0' && fullrep[1] == ':')
1407 pathstart = fullrep + 2;
1408 else
1409 #endif
1410 pathstart = fullrep;
1411
1412 pathend = pathstart + strlen(pathstart) - 1;
1413 while (pathend >= pathstart && *pathend != SLASH)
1414 --pathend;
1415 pathend++;
1416
1417 c = *pathend;
1418 *pathend = '\0';
1419 strcpy(tpath, fullrep);
1420 *pathend = c;
1421 return(pathend);
1422 }
1423
1424
badname(s)1425 static int badname(s)
1426 char *s;
1427 {
1428 #ifdef IS_MSDOS
1429 char *ext;
1430 #endif
1431
1432 return (
1433 #ifdef IS_MSDOS
1434 *s == ' ' ||
1435 *s == '.' ||
1436 (ext = strchr(s, '.')) - s >= MAXFILE ||
1437 (*ext == '.' && strchr(ext + 1, '.') != NULL) ||
1438 strlen(ext) >= MAXEXT ||
1439 strncmp(s, IDF, STRLEN(IDF)) == 0
1440 #else
1441 (*s == '.' && (s[1] == '\0' || strcmp(s, "..") == 0)) ||
1442 strlen(s) > MAXNAMLEN
1443 #endif
1444 );
1445 }
1446
1447
1448 #ifndef IS_MSDOS
getstat(ffull,f)1449 static int getstat(ffull, f)
1450 char *ffull;
1451 FILEINFO *f;
1452 {
1453 struct stat fstat;
1454 int flags;
1455
1456 if ((flags = f->fi_stflags) & FI_STTAKEN)
1457 return(flags & FI_LINKERR);
1458 flags |= FI_STTAKEN;
1459 #ifndef S_IFLNK
1460 if (stat(ffull, &fstat)) {
1461 fprintf(stderr, "Strange, couldn't stat %s.\n", ffull);
1462 quit();
1463 }
1464 #else
1465 if (lstat(ffull, &fstat)) {
1466 fprintf(stderr, "Strange, couldn't lstat %s.\n", ffull);
1467 quit();
1468 }
1469 if ((flags & FI_INSTICKY) && fstat.st_uid != uid && uid != 0)
1470 flags |= FI_NODEL;
1471 if ((fstat.st_mode & S_IFMT) == S_IFLNK) {
1472 flags |= FI_ISLNK;
1473 if (stat(ffull, &fstat)) {
1474 f->fi_stflags = flags | FI_LINKERR;
1475 return(1);
1476 }
1477 }
1478 #endif
1479 if ((fstat.st_mode & S_IFMT) == S_IFDIR)
1480 flags |= FI_ISDIR;
1481 f->fi_stflags = flags;
1482 f->fi_mode = fstat.st_mode;
1483 return(0);
1484 }
1485
1486
dwritable(h)1487 static int dwritable(h)
1488 HANDLE *h;
1489 {
1490 char *p = h->h_name, *myp, *lastslash = NULL, *pathend;
1491 char *pw = &(h->h_di->di_flags), r;
1492
1493 if (uid == 0)
1494 return(1);
1495
1496 if (*pw & DI_KNOWWRITE)
1497 return(*pw & DI_CANWRITE);
1498
1499 pathend = p + strlen(p);
1500 if (*p == '\0')
1501 myp = ".";
1502 else if (pathend == p + 1)
1503 myp = SLASHSTR;
1504 else {
1505 lastslash = pathend - 1;
1506 *lastslash = '\0';
1507 myp = p;
1508 }
1509 r = !access(myp, W_OK) ? DI_CANWRITE : 0;
1510 *pw |= DI_KNOWWRITE | r;
1511
1512 if (lastslash != NULL)
1513 *lastslash = SLASH;
1514 return(r);
1515 }
1516
1517
fwritable(hname,f)1518 static int fwritable(hname, f)
1519 char *hname;
1520 FILEINFO *f;
1521 {
1522 int r;
1523
1524 if (f->fi_stflags & FI_KNOWWRITE)
1525 return(f->fi_stflags & FI_CANWRITE);
1526
1527 strcpy(fullrep, hname);
1528 strcat(fullrep, f->fi_name);
1529 r = !access(fullrep, W_OK) ? FI_CANWRITE : 0;
1530 f->fi_stflags |= FI_KNOWWRITE | r;
1531 return(r);
1532 }
1533 #endif
1534
1535
fsearch(s,d)1536 static FILEINFO *fsearch(s, d)
1537 char *s;
1538 DIRINFO *d;
1539 {
1540 FILEINFO **fils = d->di_fils;
1541 int nfils = d->di_nfils;
1542 int first, k, last, res;
1543
1544 for(first = 0, last = nfils - 1;;) {
1545 if (last < first)
1546 return(NULL);
1547 k = (first + last) >> 1;
1548 if ((res = strcmp(s, fils[k]->fi_name)) == 0)
1549 return(fils[k]);
1550 if (res < 0)
1551 last = k - 1;
1552 else
1553 first = k + 1;
1554 }
1555 }
1556
1557
ffirst(s,n,d)1558 static int ffirst(s, n, d)
1559 char *s;
1560 int n;
1561 DIRINFO *d;
1562 {
1563 int first, k, last, res;
1564 FILEINFO **fils = d->di_fils;
1565 int nfils = d->di_nfils;
1566
1567 if (nfils == 0 || n == 0)
1568 return(0);
1569 first = 0;
1570 last = nfils - 1;
1571 for(;;) {
1572 k = (first + last) >> 1;
1573 res = strncmp(s, fils[k]->fi_name, n);
1574 if (first == last)
1575 return(res == 0 ? k : nfils);
1576 else if (res > 0)
1577 first = k + 1;
1578 else
1579 last = k;
1580 }
1581 }
1582
1583
1584 #ifdef IS_MSDOS
1585 /* checkdir and takedir for MS-D*S */
1586
checkdir(p,pathend,which)1587 static HANDLE *checkdir(p, pathend, which)
1588 char *p, *pathend;
1589 int which;
1590 {
1591 struct ffblk de;
1592 DIRID d;
1593 DEVID v;
1594 HANDLE *h;
1595 char *dirstart = p;
1596 int fd;
1597 int firstfound;
1598 DIRINFO *di;
1599
1600 if (hsearch(p, which, &h))
1601 if (h->h_di == NULL) {
1602 direrr = h->h_err;
1603 return(NULL);
1604 }
1605 else
1606 return(h);
1607
1608 if (*p == '\0' || p[1] != ':')
1609 v = curdisk;
1610 else {
1611 dirstart += 2;
1612 v = mylower(p[0]) - 'a';
1613 if (v < 0 || v >= maxdisk)
1614 return(NULL);
1615 }
1616
1617 if (patch.ph_safeid) {
1618 strcpy(pathend, IDF);
1619 strcpy(pathend + STRLEN(IDF), "*");
1620 if (findfirst(p, &de, 0)) {
1621 if ((d = ndirs) == 1000) {
1622 fprintf(stderr, "Too many different directories.\n");
1623 quit();
1624 }
1625 sprintf(pathend + STRLEN(IDF), "%03d", d);
1626 if ((fd = _creat(p, 0)) < 0) {
1627 direrr = h->h_err = H_NODIR;
1628 return(NULL);
1629 }
1630 _close(fd);
1631 strcpy(pathend, "*.*");
1632 if (findfirst(p, &de, FA_DIREC | FA_SYSTEM | FA_HIDDEN))
1633 h->h_di = dadd(v, d);
1634 else
1635 takedir(&de, h->h_di = dadd(v, d));
1636 }
1637 else if ((d = atoi(de.ff_name + STRLEN(IDF))) < ndirs)
1638 h->h_di = dirs[d];
1639 else {
1640 strcpy(pathend, de.ff_name);
1641 fprintf(stderr, "Strange dir-id file encountered: %s.\n", p);
1642 quit();
1643 }
1644 *pathend = '\0';
1645 }
1646 else {
1647 strcpy(pathend, "*.*");
1648 firstfound = !findfirst(p, &de, FA_DIREC | FA_SYSTEM | FA_HIDDEN);
1649 *pathend = '\0';
1650 if (firstfound) {
1651 v = DRIVENO(&de);
1652 d = CLUSTNO(&de);
1653 }
1654 else {
1655 strcpy(pathend, "T.D");
1656 if (mkdir(p)) {
1657 *pathend = '\0';
1658 direrr = h->h_err = H_NODIR;
1659 return(NULL);
1660 }
1661 strcpy(pathend, "*.*");
1662 firstfound = !findfirst(p, &de, FA_DIREC | FA_SYSTEM | FA_HIDDEN);
1663 *pathend = '\0';
1664 v = DRIVENO(&de);
1665 d = CLUSTNO(&de);
1666 rmdir(p);
1667 if (!firstfound || d != 0) {
1668 fprintf(stderr,
1669 "Strange, %s does not seem to be a root dir.\n",
1670 p);
1671 quit();
1672 }
1673 }
1674
1675 if ((di = dsearch(v, d)) == NULL)
1676 if (firstfound)
1677 takedir(&de, h->h_di = dadd(v, d));
1678 else
1679 h->h_di = dadd(v, d);
1680 else
1681 h->h_di = di;
1682 }
1683
1684 return(h);
1685 }
1686
1687
takedir(pff,di)1688 static void takedir(pff, di)
1689 struct ffblk *pff;
1690 DIRINFO *di;
1691 {
1692 int cnt, room, namlen, needdot;
1693 FILEINFO **fils, *f;
1694 char c, *p, *p1;
1695
1696 room = INITROOM;
1697 di->di_fils = fils = (FILEINFO **)myalloc(room * sizeof(FILEINFO *));
1698 cnt = 0;
1699 do {
1700 if (strnicmp(pff->ff_name, IDF, STRLEN(IDF)) == 0)
1701 continue;
1702 if (cnt == room) {
1703 room *= 2;
1704 fils = (FILEINFO **)myalloc(room * sizeof(FILEINFO *));
1705 memcpy(fils, di->di_fils, cnt * sizeof(FILEINFO *));
1706 chgive(di->di_fils, cnt * sizeof(FILEINFO *));
1707 di->di_fils = fils;
1708 fils = di->di_fils + cnt;
1709 }
1710 needdot = 1;
1711 for (p = pff->ff_name, namlen = 0; (c = *p) != '\0'; p++, namlen++)
1712 if (c == '.')
1713 needdot = 0;
1714 *fils = f = (FILEINFO *)challoc(sizeof(FILEINFO), 1);
1715 f->fi_name = p = (char *)challoc(namlen + needdot + 1, 0);
1716 for (p1 = pff->ff_name; (c = *p1) != '\0'; p1++)
1717 *(p++) = mylower(c);
1718 if (needdot)
1719 *(p++) = '.';
1720 *p = '\0';
1721 f->fi_attrib = pff->ff_attrib;
1722 f->fi_rep = NULL;
1723 cnt++;
1724 fils++;
1725 } while (findnext(pff) == 0);
1726 qsort(di->di_fils, cnt, sizeof(FILEINFO *), fcmp);
1727 di->di_nfils = cnt;
1728 }
1729
1730 #else
1731 /* checkdir, takedir for Un*x */
1732
checkdir(p,pathend,which)1733 static HANDLE *checkdir(p, pathend, which)
1734 char *p, *pathend;
1735 int which;
1736 {
1737 struct stat dstat;
1738 DIRID d;
1739 DEVID v;
1740 DIRINFO *di = NULL;
1741 char *myp, *lastslash = NULL;
1742 int sticky;
1743 HANDLE *h;
1744
1745 if (hsearch(p, which, &h)) {
1746 if (h->h_di == NULL) {
1747 direrr = h->h_err;
1748 return(NULL);
1749 }
1750 else
1751 return(h);
1752 }
1753
1754 if (*p == '\0')
1755 myp = ".";
1756 else if (pathend == p + 1)
1757 myp = SLASHSTR;
1758 else {
1759 lastslash = pathend - 1;
1760 *lastslash = '\0';
1761 myp = p;
1762 }
1763
1764 if (stat(myp, &dstat) || (dstat.st_mode & S_IFMT) != S_IFDIR)
1765 direrr = h->h_err = H_NODIR;
1766 else if (access(myp, R_OK | X_OK))
1767 direrr = h->h_err = H_NOREADDIR;
1768 else {
1769 direrr = 0;
1770 sticky = (dstat.st_mode & S_ISVTX) && uid != 0 && uid != dstat.st_uid ?
1771 FI_INSTICKY : 0;
1772 v = dstat.st_dev;
1773 d = dstat.st_ino;
1774
1775 if ((di = dsearch(v, d)) == NULL)
1776 takedir(myp, di = dadd(v, d), sticky);
1777 }
1778
1779 if (lastslash != NULL)
1780 *lastslash = SLASH;
1781 if (direrr != 0)
1782 return(NULL);
1783 h->h_di = di;
1784 return(h);
1785 }
1786
1787
takedir(p,di,sticky)1788 static void takedir(p, di, sticky)
1789 char *p;
1790 DIRINFO *di;
1791 int sticky;
1792 {
1793 int cnt, room;
1794 DIRENTRY *dp;
1795 FILEINFO *f, **fils;
1796 DIR *dirp;
1797
1798 if ((dirp = opendir(p)) == NULL) {
1799 fprintf(stderr, "Strange, can't scan %s.\n", p);
1800 quit();
1801 }
1802 room = INITROOM;
1803 di->di_fils = fils = (FILEINFO **)myalloc(room * sizeof(FILEINFO *));
1804 cnt = 0;
1805 while ((dp = readdir(dirp)) != NULL) {
1806 if (cnt == room) {
1807 room *= 2;
1808 fils = (FILEINFO **)myalloc(room * sizeof(FILEINFO *));
1809 memcpy(fils, di->di_fils, cnt * sizeof(FILEINFO *));
1810 chgive(di->di_fils, cnt * sizeof(FILEINFO *));
1811 di->di_fils = fils;
1812 fils = di->di_fils + cnt;
1813 }
1814 *fils = f = (FILEINFO *)challoc(sizeof(FILEINFO), 1);
1815 f->fi_name = mydup(dp->d_name);
1816 f->fi_stflags = sticky;
1817 f->fi_rep = NULL;
1818 cnt++;
1819 fils++;
1820 }
1821 closedir(dirp);
1822 qsort(di->di_fils, cnt, sizeof(FILEINFO *), fcmp);
1823 di->di_nfils = cnt;
1824 }
1825
1826 /* end of Un*x checkdir, takedir; back to general program */
1827 #endif
1828
1829
fcmp(pf1,pf2)1830 static int fcmp(pf1, pf2)
1831 FILEINFO **pf1, **pf2;
1832 {
1833 return(strcmp((*pf1)->fi_name, (*pf2)->fi_name));
1834 }
1835
1836
hadd(n)1837 static HANDLE *hadd(n)
1838 char *n;
1839 {
1840 HANDLE **newhandles, *h;
1841
1842 if (nhandles == handleroom) {
1843 handleroom *= 2;
1844 newhandles = (HANDLE **)myalloc(handleroom * sizeof(HANDLE *));
1845 memcpy(newhandles, handles, nhandles * sizeof(HANDLE *));
1846 chgive(handles, nhandles * sizeof(HANDLE *));
1847 handles = newhandles;
1848 }
1849 handles[nhandles++] = h = (HANDLE *)challoc(sizeof(HANDLE), 1);
1850 h->h_name = (char *)challoc(strlen(n) + 1, 0);
1851 strcpy(h->h_name, n);
1852 h->h_di = NULL;
1853 return(h);
1854 }
1855
1856
hsearch(n,which,pret)1857 static int hsearch(n, which, pret)
1858 char *n;
1859 int which;
1860 HANDLE **pret;
1861 {
1862 int i;
1863 HANDLE **ph;
1864
1865 if (strcmp(n, lasthandle[which]->h_name) == 0) {
1866 *pret = lasthandle[which];
1867 return(1);
1868 }
1869
1870 for(i = 0, ph = handles; i < nhandles; i++, ph++)
1871 if (strcmp(n, (*ph)->h_name) == 0) {
1872 lasthandle[which] = *pret = *ph;
1873 return(1);
1874 }
1875
1876 lasthandle[which] = *pret = hadd(n);
1877 return(0);
1878 }
1879
1880
dadd(v,d)1881 static DIRINFO *dadd(v, d)
1882 DEVID v;
1883 DIRID d;
1884 {
1885 DIRINFO *di;
1886 DIRINFO **newdirs;
1887
1888 if (ndirs == dirroom) {
1889 dirroom *= 2;
1890 newdirs = (DIRINFO **)myalloc(dirroom * sizeof(DIRINFO *));
1891 memcpy(newdirs, dirs, ndirs * sizeof(DIRINFO *));
1892 chgive(dirs, ndirs * sizeof(DIRINFO *));
1893 dirs = newdirs;
1894 }
1895 dirs[ndirs++] = di = (DIRINFO *)challoc(sizeof(DIRINFO), 1);
1896 di->di_vid = v;
1897 di->di_did = d;
1898 di->di_nfils = 0;
1899 di->di_fils = NULL;
1900 di->di_flags = 0;
1901 return(di);
1902 }
1903
1904
dsearch(v,d)1905 static DIRINFO *dsearch(v, d)
1906 DEVID v;
1907 DIRID d;
1908 {
1909 int i;
1910 DIRINFO *di;
1911
1912 for(i = 0, di = *dirs; i < ndirs; i++, di++)
1913 if (v == di->di_vid && d == di->di_did)
1914 return(di);
1915 return(NULL);
1916 }
1917
1918
match(pat,s,start1,len1)1919 static int match(pat, s, start1, len1)
1920 char *pat, *s, **start1;
1921 int *len1;
1922 {
1923 char c;
1924 #ifdef IS_MSDOS
1925 char *olds;
1926 #endif
1927
1928 *start1 = 0;
1929 for(;;)
1930 switch (c = *pat) {
1931 case '\0':
1932 case SLASH:
1933 return(*s == '\0');
1934 #ifdef IS_MSDOS
1935 case '!':
1936 *start1 = olds = s;
1937 if ((s = strchr(s, '.')) == NULL)
1938 return(0);
1939 s++;
1940 *len1 = s - olds;
1941 if ((c = *(++pat)) == '\0') {
1942 *len1 += strlen(s);
1943 return(1);
1944 }
1945 for ( ; !match(pat, s, start1 + 1, len1 + 1); (*len1)++, s++)
1946 if (*s == '\0')
1947 return(0);
1948 return(1);
1949 #endif
1950 case '*':
1951 *start1 = s;
1952 if ((c = *(++pat)) == '\0') {
1953 *len1 = strlen(s);
1954 return(1);
1955 }
1956 else {
1957 for (*len1=0; !match(pat, s, start1+1, len1+1); (*len1)++, s++)
1958 if (
1959 #ifdef IS_MSDOS
1960 *s == '.' ||
1961 #endif
1962 *s == '\0'
1963 )
1964 return(0);
1965 return(1);
1966 }
1967 case '?':
1968 if (
1969 #ifdef IS_MSDOS
1970 *s == '.' ||
1971 #endif
1972 *s == '\0'
1973 )
1974 return(0);
1975 *(start1++) = s;
1976 *(len1++) = 1;
1977 pat++;
1978 s++;
1979 break;
1980 case '[':
1981 {
1982 int matched = 0, notin = 0, inrange = 0;
1983 char prevc = '\0';
1984
1985 if ((c = *(++pat)) == '^') {
1986 notin = 1;
1987 c = *(++pat);
1988 }
1989 while (c != ']') {
1990 if (c == '-' && !inrange)
1991 inrange = 1;
1992 else {
1993 if (c == ESC) {
1994 c = *(++pat);
1995 }
1996 if (inrange) {
1997 if (*s >= prevc && *s <= c)
1998 matched = 1;
1999 inrange = 0;
2000 }
2001 else if (c == *s)
2002 matched = 1;
2003 prevc = c;
2004 }
2005 c = *(++pat);
2006 }
2007 if (inrange && *s >= prevc)
2008 matched = 1;
2009 if (!(matched ^ notin))
2010 return(0);
2011 *(start1++) = s;
2012 *(len1++) = 1;
2013 pat++;
2014 s++;
2015 }
2016 break;
2017 case ESC:
2018 c = *(++pat);
2019 default:
2020 if (c == *s) {
2021 pat++;
2022 s++;
2023 }
2024 else
2025 return(0);
2026 }
2027 }
2028
2029
makerep()2030 static void makerep()
2031 {
2032 int l, x;
2033 #ifndef IS_MSDOS
2034 int i, cnv;
2035 char *q;
2036 #endif
2037 char *p, *pat, c, pc;
2038
2039 repbad = 0;
2040 p = fullrep;
2041 for (pat = to, l = 0; (c = *pat) != '\0'; pat++, l++) {
2042 if (c == '#' || c == '=') {
2043 c = *(++pat);
2044 #ifndef IS_MSDOS
2045 if (c == 'l') {
2046 cnv = LOWER;
2047 c = *(++pat);
2048 }
2049 else if (c == 'u') {
2050 cnv = UPPER;
2051 c = *(++pat);
2052 }
2053 else
2054 cnv = STAY;
2055 #endif
2056 for(x = 0; ;x *= 10) {
2057 x += c - '0';
2058 c = *(pat+1);
2059 if (!isdigit(c))
2060 break;
2061 pat++;
2062 }
2063 --x;
2064 if (l + len[x] >= MAXPATH)
2065 goto toolong;
2066 #ifdef IS_MSDOS
2067 if (
2068 *(start[x]) == '.' &&
2069 (
2070 p == fullrep ||
2071 *(p - 1) == SLASH
2072 )
2073 ) {
2074 repbad = 1;
2075 if (l + STRLEN(EMPTY) >= MAXPATH)
2076 goto toolong;
2077 strcpy(p, EMPTY);
2078 p += STRLEN(EMPTY);
2079 l += STRLEN(EMPTY);
2080 }
2081 #else
2082 switch (cnv) {
2083 case STAY:
2084 #endif
2085 memmove(p, start[x], len[x]);
2086 p += len[x];
2087 #ifndef IS_MSDOS
2088 break;
2089 case LOWER:
2090 for (i = len[x], q = start[x]; i > 0; i--, p++, q++)
2091 *p = mylower(*q);
2092 break;
2093 case UPPER:
2094 for (i = len[x], q = start[x]; i > 0; i--, p++, q++)
2095 *p = myupper(*q);
2096 }
2097 #endif
2098 }
2099 else {
2100 if (c == ESC)
2101 c = *(++pat);
2102 if (l == MAXPATH)
2103 goto toolong;
2104 if (
2105 (
2106 #ifdef IS_MSDOS
2107 c == '.' ||
2108 #endif
2109 c == SLASH
2110 ) &&
2111 (
2112 p == fullrep ? pat != to :
2113 (
2114 (
2115 (pc = *(p - 1)) == SLASH
2116 #ifdef IS_MSDOS
2117 || pc == ':'
2118 #endif
2119 ) &&
2120 *(pat - 1) != pc
2121 )
2122 )
2123 ) {
2124 repbad = 1;
2125 if (l + STRLEN(EMPTY) >= MAXPATH)
2126 goto toolong;
2127 strcpy(p, EMPTY);
2128 p += STRLEN(EMPTY);
2129 l += STRLEN(EMPTY);
2130 }
2131 *(p++)= c;
2132 }
2133 }
2134 if (p == fullrep) {
2135 strcpy(fullrep, EMPTY);
2136 repbad = 1;
2137 }
2138 *(p++) = '\0';
2139 return;
2140
2141 toolong:
2142 repbad = 1;
2143 strcpy(fullrep, TOOLONG);
2144 }
2145
2146
checkcollisions()2147 static void checkcollisions()
2148 {
2149 REPDICT *rd, *prd;
2150 REP *p, *q;
2151 int i, mult, oldnreps;
2152
2153 if (nreps == 0)
2154 return;
2155 rd = (REPDICT *)myalloc(nreps * sizeof(REPDICT));
2156 for (
2157 q = &hrep, p = q->r_next, prd = rd, i = 0;
2158 p != NULL;
2159 q = p, p = p->r_next, prd++, i++
2160 ) {
2161 prd->rd_p = p;
2162 prd->rd_dto = p->r_hto->h_di;
2163 prd->rd_nto = p->r_nto;
2164 prd->rd_i = i;
2165 }
2166 qsort(rd, nreps, sizeof(REPDICT), rdcmp);
2167 mult = 0;
2168 for (i = 0, prd = rd, oldnreps = nreps; i < oldnreps; i++, prd++)
2169 if (
2170 i < oldnreps - 1 &&
2171 prd->rd_dto == (prd + 1)->rd_dto &&
2172 strcmp(prd->rd_nto, (prd + 1)->rd_nto) == 0
2173 ) {
2174 if (!mult)
2175 mult = 1;
2176 else
2177 printf(" , ");
2178 printf("%s%s", prd->rd_p->r_hfrom->h_name,
2179 prd->rd_p->r_ffrom->fi_name);
2180 prd->rd_p->r_flags |= R_SKIP;
2181 prd->rd_p->r_ffrom->fi_rep = MISTAKE;
2182 nreps--;
2183 badreps++;
2184 }
2185 else if (mult) {
2186 prd->rd_p->r_flags |= R_SKIP;
2187 prd->rd_p->r_ffrom->fi_rep = MISTAKE;
2188 nreps--;
2189 badreps++;
2190 printf(" , %s%s -> %s%s : collision.\n",
2191 prd->rd_p->r_hfrom->h_name, prd->rd_p->r_ffrom->fi_name,
2192 prd->rd_p->r_hto->h_name, prd->rd_nto);
2193 mult = 0;
2194 }
2195 chgive(rd, oldnreps * sizeof(REPDICT));
2196 }
2197
2198
rdcmp(rd1,rd2)2199 static int rdcmp(rd1, rd2)
2200 REPDICT *rd1, *rd2;
2201 {
2202 int ret;
2203
2204 if (
2205 (ret = rd1->rd_dto - rd2->rd_dto) == 0 &&
2206 (ret = strcmp(rd1->rd_nto, rd2->rd_nto)) == 0
2207 )
2208 ret = rd1->rd_i - rd2->rd_i;
2209 return(ret);
2210 }
2211
2212
findorder()2213 static void findorder()
2214 {
2215 REP *p, *q, *t, *first, *pred;
2216 FILEINFO *fi;
2217
2218 for (q = &hrep, p = q->r_next; p != NULL; q = p, p = p->r_next)
2219 if (p->r_flags & R_SKIP) {
2220 q->r_next = p->r_next;
2221 p = q;
2222 }
2223 else if (
2224 (fi = p->r_fdel) == NULL ||
2225 (pred = fi->fi_rep) == NULL ||
2226 pred == MISTAKE
2227 )
2228 continue;
2229 else if ((first = pred->r_first) == p) {
2230 p->r_flags |= R_ISCYCLE;
2231 pred->r_flags |= R_ISALIASED;
2232 if (op & MOVE)
2233 p->r_fdel = NULL;
2234 }
2235 else {
2236 if (op & MOVE)
2237 p->r_fdel = NULL;
2238 while (pred->r_thendo != NULL)
2239 pred = pred->r_thendo;
2240 pred->r_thendo = p;
2241 for (t = p; t != NULL; t = t->r_thendo)
2242 t->r_first = first;
2243 q->r_next = p->r_next;
2244 p = q;
2245 }
2246 }
2247
2248
nochains()2249 static void nochains()
2250 {
2251 REP *p, *q;
2252
2253 for (q = &hrep, p = q->r_next; p != NULL; q = p, p = p->r_next)
2254 if (p->r_flags & R_ISCYCLE || p->r_thendo != NULL) {
2255 printchain(p);
2256 printf("%s%s : no chain copies allowed.\n",
2257 p->r_hto->h_name, p->r_nto);
2258 q->r_next = p->r_next;
2259 p = q;
2260 }
2261 }
2262
2263
printchain(p)2264 static void printchain(p)
2265 REP *p;
2266 {
2267 if (p->r_thendo != NULL)
2268 printchain(p->r_thendo);
2269 printf("%s%s -> ", p->r_hfrom->h_name, p->r_ffrom->fi_name);
2270 badreps++;
2271 nreps--;
2272 p->r_ffrom->fi_rep = MISTAKE;
2273 }
2274
2275
2276 static void scandeletes(pkilldel)
2277 int (*pkilldel)();
2278 {
2279 REP *p, *q, *n;
2280
2281 for (q = &hrep, p = q->r_next; p != NULL; q = p, p = p->r_next) {
2282 if (p->r_fdel != NULL)
2283 while ((*pkilldel)(p)) {
2284 nreps--;
2285 p->r_ffrom->fi_rep = MISTAKE;
2286 if ((n = p->r_thendo) != NULL) {
2287 if (op & MOVE)
2288 n->r_fdel = p->r_ffrom;
2289 n->r_next = p->r_next;
2290 q->r_next = p = n;
2291 }
2292 else {
2293 q->r_next = p->r_next;
2294 p = q;
2295 break;
2296 }
2297 }
2298 }
2299 }
2300
2301
baddel(p)2302 static int baddel(p)
2303 REP *p;
2304 {
2305 HANDLE *hfrom = p->r_hfrom, *hto = p->r_hto;
2306 FILEINFO *fto = p->r_fdel;
2307 char *t = fto->fi_name, *f = p->r_ffrom->fi_name;
2308 char *hnf = hfrom->h_name, *hnt = hto->h_name;
2309
2310 if (delstyle == NODEL && !(p->r_flags & R_DELOK) && !(op & APPEND))
2311 printf("%s%s -> %s%s : old %s%s would have to be %s.\n",
2312 hnf, f, hnt, t, hnt, t,
2313 (op & OVERWRITE) ? "overwritten" : "deleted");
2314 else if (fto->fi_rep == MISTAKE)
2315 printf("%s%s -> %s%s : old %s%s was to be done first.\n",
2316 hnf, f, hnt, t, hnt, t);
2317 else if (
2318 #ifdef IS_MSDOS
2319 fto->fi_attrib & FA_DIREC
2320 #else
2321 fto->fi_stflags & FI_ISDIR
2322 #endif
2323 )
2324 printf("%s%s -> %s%s : %s%s%s is a directory.\n",
2325 hnf, f, hnt, t, (op & APPEND) ? "" : "old ", hnt, t);
2326 #ifndef IS_MSDOS
2327 else if ((fto->fi_stflags & FI_NODEL) && !(op & (APPEND | OVERWRITE)))
2328 printf("%s%s -> %s%s : old %s%s lacks delete permission.\n",
2329 hnf, f, hnt, t, hnt, t);
2330 #endif
2331 else if (
2332 (op & (APPEND | OVERWRITE)) &&
2333 #ifdef IS_MSDOS
2334 fto->fi_attrib & FA_RDONLY
2335 #else
2336 !fwritable(hnt, fto)
2337 #endif
2338 ) {
2339 printf("%s%s -> %s%s : %s%s %s.\n",
2340 hnf, f, hnt, t, hnt, t,
2341 #ifndef IS_MSDOS
2342 #ifdef S_IFLNK
2343 fto->fi_stflags & FI_LINKERR ?
2344 "is a badly aimed symbolic link" :
2345 #endif
2346 #endif
2347 "lacks write permission");
2348 }
2349 else
2350 return(0);
2351 badreps++;
2352 return(1);
2353 }
2354
2355
skipdel(p)2356 static int skipdel(p)
2357 REP *p;
2358 {
2359 if (p->r_flags & R_DELOK)
2360 return(0);
2361 fprintf(stderr, "%s%s -> %s%s : ",
2362 p->r_hfrom->h_name, p->r_ffrom->fi_name,
2363 p->r_hto->h_name, p->r_nto);
2364 if (
2365 #ifdef IS_MSDOS
2366 p->r_fdel->fi_attrib & FA_RDONLY
2367 #else
2368 #ifdef S_IFLNK
2369 !(p->r_ffrom->fi_stflags & FI_ISLNK) &&
2370 #endif
2371 !fwritable(p->r_hto->h_name, p->r_fdel)
2372 #endif
2373 )
2374 fprintf(stderr, "old %s%s lacks write permission. delete it",
2375 p->r_hto->h_name, p->r_nto);
2376 else
2377 fprintf(stderr, "%s old %s%s",
2378 (op & OVERWRITE) ? "overwrite" : "delete",
2379 p->r_hto->h_name, p->r_nto);
2380 return(!getreply("? ", -1));
2381 }
2382
2383
goonordie()2384 static void goonordie()
2385 {
2386 if ((paterr || badreps) && nreps > 0) {
2387 fprintf(stderr, "Not everything specified can be done.");
2388 if (badstyle == ABORTBAD) {
2389 fprintf(stderr, " Aborting.\n");
2390 exit(1);
2391 }
2392 else if (badstyle == SKIPBAD)
2393 fprintf(stderr, " Proceeding with the rest.\n");
2394 else if (!getreply(" Proceed with the rest? ", -1))
2395 exit(1);
2396 }
2397 }
2398
2399
doreps()2400 static void doreps()
2401 {
2402 char *fstart;
2403 int k, printaliased = 0, alias = 0;
2404 REP *first, *p;
2405 long aliaslen = 0l;
2406
2407 #ifdef IS_MSDOS
2408 ctrlbrk(breakrep);
2409 #else
2410 signal(SIGINT, breakrep);
2411 #endif
2412
2413 for (first = hrep.r_next, k = 0; first != NULL; first = first->r_next) {
2414 for (p = first; p != NULL; p = p->r_thendo, k++) {
2415 if (gotsig) {
2416 fflush(stdout);
2417 fprintf(stderr, "User break.\n");
2418 printaliased = snap(first, p);
2419 gotsig = 0;
2420 }
2421 strcpy(fullrep, p->r_hto->h_name);
2422 strcat(fullrep, p->r_nto);
2423 if (!noex && (p->r_flags & R_ISCYCLE)) {
2424 if (op & APPEND)
2425 aliaslen = appendalias(first, p, &printaliased);
2426 else
2427 alias = movealias(first, p, &printaliased);
2428 }
2429 strcpy(pathbuf, p->r_hfrom->h_name);
2430 fstart = pathbuf + strlen(pathbuf);
2431 if ((p->r_flags & R_ISALIASED) && !(op & APPEND))
2432 sprintf(fstart, "%s%03d", TEMP, alias);
2433 else
2434 strcpy(fstart, p->r_ffrom->fi_name);
2435 if (!noex) {
2436 if (p->r_fdel != NULL && !(op & (APPEND | OVERWRITE)))
2437 myunlink(fullrep, p->r_fdel);
2438 if (
2439 (op & (COPY | APPEND)) ?
2440 copy(p->r_ffrom,
2441 p->r_flags & R_ISALIASED ? aliaslen : -1L) :
2442 #ifndef IS_MSDOS
2443 (op & HARDLINK) ?
2444 link(pathbuf, fullrep) :
2445 #ifdef S_IFLNK
2446 (op & SYMLINK) ?
2447 symlink((p->r_flags & R_ONEDIRLINK) ? fstart : pathbuf,
2448 fullrep) :
2449 #endif
2450 #endif
2451 p->r_flags & R_ISX ?
2452 copymove(p) :
2453 /* move */
2454 rename(pathbuf, fullrep)
2455 ) {
2456 fprintf(stderr,
2457 "%s -> %s has failed.\n", pathbuf, fullrep);
2458 printaliased = snap(first, p);
2459 }
2460 }
2461 if (verbose || noex) {
2462 if (p->r_flags & R_ISALIASED && !printaliased)
2463 strcpy(fstart, p->r_ffrom->fi_name);
2464 fprintf(outfile, "%s %c%c %s%s%s\n",
2465 pathbuf,
2466 p->r_flags & R_ISALIASED ? '=' : '-',
2467 p->r_flags & R_ISCYCLE ? '^' : '>',
2468 fullrep,
2469 (p->r_fdel != NULL && !(op & APPEND)) ? " (*)" : "",
2470 noex ? "" : " : done");
2471 }
2472 }
2473 printaliased = 0;
2474 }
2475 if (k != nreps)
2476 fprintf(stderr, "Strange, did %d reps; %d were expected.\n",
2477 k, nreps);
2478 if (k == 0)
2479 fprintf(stderr, "Nothing done.\n");
2480 }
2481
2482
appendalias(first,p,pprintaliased)2483 static long appendalias(first, p, pprintaliased)
2484 REP *first, *p;
2485 int *pprintaliased;
2486 {
2487 long ret = 0l;
2488
2489 #ifdef IS_MSDOS
2490 int fd;
2491
2492 if ((fd = open(fullrep, O_RDONLY | O_BINARY, 0)) < 0) {
2493 fprintf(stderr, "stat on %s has failed.\n", fullrep);
2494 *pprintaliased = snap(first, p);
2495 }
2496 else {
2497 ret = filelength(fd);
2498 close(fd);
2499 }
2500 #else
2501 struct stat fstat;
2502
2503 if (stat(fullrep, &fstat)) {
2504 fprintf(stderr, "append cycle stat on %s has failed.\n", fullrep);
2505 *pprintaliased = snap(first, p);
2506 }
2507 else
2508 ret = fstat.st_size;
2509 #endif
2510
2511 return(ret);
2512 }
2513
2514
movealias(first,p,pprintaliased)2515 static int movealias(first, p, pprintaliased)
2516 REP *first, *p;
2517 int *pprintaliased;
2518 {
2519 char *fstart;
2520 int ret;
2521
2522 strcpy(pathbuf, p->r_hto->h_name);
2523 fstart = pathbuf + strlen(pathbuf);
2524 strcpy(fstart, TEMP);
2525 for (
2526 ret = 0;
2527 sprintf(fstart + STRLEN(TEMP), "%03d", ret),
2528 fsearch(fstart, p->r_hto->h_di) != NULL;
2529 ret++
2530 )
2531 ;
2532 if (rename(fullrep, pathbuf)) {
2533 fprintf(stderr,
2534 "%s -> %s has failed.\n", fullrep, pathbuf);
2535 *pprintaliased = snap(first, p);
2536 }
2537 return(ret);
2538 }
2539
2540
snap(first,p)2541 static int snap(first, p)
2542 REP *first, *p;
2543 {
2544 char fname[80];
2545 int redirected = 0;
2546
2547 if (noex)
2548 exit(1);
2549
2550 failed = 1;
2551 #ifdef IS_MSDOS
2552 ctrlbrk((int (*)())breakstat);
2553 #else
2554 signal(SIGINT, breakstat);
2555 #endif
2556 if (
2557 badstyle == ASKBAD &&
2558 isatty(fileno(stdout)) &&
2559 getreply("Redirect standard output to file? ", 0)
2560 ) {
2561 redirected = 1;
2562 #ifndef IS_MSDOS
2563 umask(oldumask);
2564 #endif
2565 while (
2566 fprintf(stderr, "File name> "),
2567 (outfile = fopen(mygets(fname, 80), "w")) == NULL
2568 )
2569 fprintf(stderr, "Can't open %s.\n", fname);
2570 }
2571 if (redirected || !verbose)
2572 showdone(p);
2573 fprintf(outfile, "The following left undone:\n");
2574 noex = 1;
2575 return(first != p);
2576 }
2577
2578
showdone(fin)2579 static void showdone(fin)
2580 REP *fin;
2581 {
2582 REP *first, *p;
2583
2584 for (first = hrep.r_next; ; first = first->r_next)
2585 for (p = first; p != NULL; p = p->r_thendo) {
2586 if (p == fin)
2587 return;
2588 fprintf(outfile, "%s%s %c%c %s%s : done%s\n",
2589 p->r_hfrom->h_name, p->r_ffrom->fi_name,
2590 p->r_flags & R_ISALIASED ? '=' : '-',
2591 p->r_flags & R_ISCYCLE ? '^' : '>',
2592 p->r_hto->h_name, p->r_nto,
2593 (p->r_fdel != NULL && !(op & APPEND)) ? " (*)" : "");
2594 }
2595 }
2596
2597
breakout()2598 static void breakout()
2599 {
2600 fflush(stdout);
2601 fprintf(stderr, "Aborting, nothing done.\n");
2602 exit(1);
2603 }
2604
2605
breakrep(int signum)2606 static void breakrep(int signum)
2607 {
2608 gotsig = 1;
2609 return;
2610 }
2611
2612
breakstat()2613 static void breakstat()
2614 {
2615 exit(1);
2616 }
2617
2618
quit()2619 static void quit()
2620 {
2621 fprintf(stderr, "Aborting, nothing done.\n");
2622 exit(1);
2623 }
2624
2625
copymove(p)2626 static int copymove(p)
2627 REP *p;
2628 {
2629 #ifndef IS_MSDOS
2630 #ifndef IS_SYSV
2631 {
2632 int llen;
2633 char linkbuf[MAXPATH];
2634
2635 if ((llen = readlink(pathbuf, linkbuf, MAXPATH - 1)) >= 0) {
2636 linkbuf[llen] = '\0';
2637 return(symlink(linkbuf, fullrep) || myunlink(pathbuf, p->r_ffrom));
2638 }
2639 }
2640 #endif
2641 #endif
2642 return(copy(p->r_ffrom, -1L) || myunlink(pathbuf, p->r_ffrom));
2643 }
2644
2645
2646
2647 #define IRWMASK (S_IREAD | S_IWRITE)
2648 #define RWMASK (IRWMASK | (IRWMASK >> 3) | (IRWMASK >> 6))
2649
copy(ff,len)2650 static int copy(ff, len)
2651 FILEINFO *ff;
2652 off_t len;
2653 {
2654 char buf[BUFSIZE];
2655 int f, t, k, mode, perm;
2656 #ifdef IS_MSDOS
2657 char c;
2658 struct ftime tim;
2659 #else
2660 #ifdef IS_SYSV
2661 struct utimbuf tim;
2662 #else
2663 struct timeval tim[2];
2664 #endif
2665 struct stat fstat;
2666 #endif
2667
2668 if ((f = open(pathbuf, O_RDONLY | O_BINARY, 0)) < 0)
2669 return(-1);
2670 perm =
2671 #ifdef IS_MSDOS
2672 IRWMASK /* will _chmod it later (to get all the attributes) */
2673 #else
2674 (op & (APPEND | OVERWRITE)) ?
2675 (~oldumask & RWMASK) | (ff->fi_mode & ~RWMASK) :
2676 ff->fi_mode
2677 #endif
2678 ;
2679
2680 #ifdef IS_V7
2681 if (
2682 !(op & APPEND) ||
2683 (((t = open(fullrep, O_RDWR)) < 0 && errno == ENOENT)
2684 )
2685 t = creat(fullrep, perm);
2686 #else
2687 mode = O_CREAT | (op & APPEND ? 0 : O_TRUNC) |
2688 #ifdef IS_MSDOS
2689 O_BINARY | (op & ZAPPEND ? O_RDWR : O_WRONLY)
2690 #else
2691 O_WRONLY
2692 #endif
2693 ;
2694 t = open(fullrep, mode, perm);
2695 #endif
2696 if (t < 0) {
2697 close(f);
2698 return(-1);
2699 }
2700 if (op & APPEND)
2701 lseek(t, (off_t)0, SEEK_END);
2702 #ifdef IS_MSDOS
2703 if (op & ZAPPEND && filelength(t) != 0) {
2704 if (lseek(t, -1L, 1) == -1L || read(t, &c, 1) != 1) {
2705 close(f);
2706 close(t);
2707 return(-1);
2708 }
2709 if (c == 26)
2710 lseek(t, -1L, 1);
2711 }
2712 #endif
2713 if ((op & APPEND) && len != (off_t)-1) {
2714 while (
2715 len != 0 &&
2716 (k = read(f, buf, (len > BUFSIZE) ? BUFSIZE : (size_t)len)) > 0 &&
2717 write(t, buf, k) == k
2718 )
2719 len -= k;
2720 if (len == 0)
2721 k = 0;
2722 }
2723 else
2724 while ((k = read(f, buf, BUFSIZE)) > 0 && write(t, buf, k) == k)
2725 ;
2726 if (!(op & (APPEND | OVERWRITE)))
2727 if (
2728 #ifdef IS_MSDOS
2729 getftime(f, &tim) ||
2730 setftime(t, &tim) ||
2731 _chmod(fullrep, 1, ff->fi_attrib) == -1
2732 #else
2733 stat(pathbuf, &fstat) ||
2734 (
2735 #ifdef IS_SYSV
2736 tim.actime = fstat.st_atime,
2737 tim.modtime = fstat.st_mtime,
2738 #else
2739 tim[0].tv_sec = fstat.st_atime,
2740 tim[0].tv_usec = 0,
2741 tim[1].tv_sec = fstat.st_mtime,
2742 tim[1].tv_usec = 0,
2743 #endif
2744 utimes(fullrep, tim)
2745 )
2746 #endif
2747 )
2748 fprintf(stderr, "Strange, couldn't transfer time from %s to %s.\n",
2749 pathbuf, fullrep);
2750
2751 close(f);
2752 close(t);
2753 if (k != 0) {
2754 if (!(op & APPEND))
2755 unlink(fullrep);
2756 return(-1);
2757 }
2758 return(0);
2759 }
2760
2761 #ifdef MV_DIR
2762
2763 #include <errno.h>
2764 extern int errno;
2765
2766 static int rename(from, to)
2767 char *from, *to;
2768 {
2769 int pid;
2770
2771 if (link(from, to) == 0 && unlink(from) == 0)
2772 return(0);
2773 else {
2774 struct stat s;
2775 if (stat(from, &s) < 0 || (s.st_mode&S_IFMT) != S_IFDIR)
2776 return(-1);
2777 }
2778
2779 do pid = fork(); while (pid >= 0 && errno == EAGAIN);
2780
2781 if (pid < 0)
2782 return(-1);
2783 else if (pid == 0) {
2784 execl(MV_DIR, "mv_dir", from, to, (char *) 0);
2785 perror(MV_DIR);
2786 exit(errno);
2787 } else if (pid > 0) {
2788 int wid;
2789 int status;
2790
2791 do wid = wait(&status);
2792 while (wid != pid && wid >= 0);
2793
2794 return(status == 0 ? 0 : -1);
2795 }
2796 }
2797 #else
2798 #ifndef HAS_RENAME
2799 static int rename(from, to)
2800 char *from, *to;
2801 {
2802 if (link(from, to))
2803 return(-1);
2804 if (unlink(from)) {
2805 unlink(to);
2806 return(-1);
2807 }
2808 return(0);
2809 }
2810 #endif
2811 #endif /* MV_DIR */
2812
2813 static int myunlink(n, f)
2814 char *n;
2815 FILEINFO *f;
2816 {
2817 #ifdef IS_MSDOS
2818 int a;
2819
2820 if (((a = f->fi_attrib) & FA_RDONLY) && _chmod(n, 1, a & ~FA_RDONLY) < 0) {
2821 fprintf(stderr, "Strange, can not _chmod (or unlink) %s.\n", f);
2822 return(-1);
2823 }
2824 #endif
2825 if (unlink(n)) {
2826 fprintf(stderr, "Strange, can not unlink %s.\n", n);
2827 return(-1);
2828 }
2829 return(0);
2830 }
2831
2832
2833 static int getreply(m, failact)
2834 char *m;
2835 int failact;
2836 {
2837 static FILE *tty = NULL;
2838 int c, r;
2839
2840 fprintf(stderr, "%s", m);
2841 if (tty == NULL && (tty = fopen(TTY, "r")) == NULL) {
2842 fprintf(stderr, "Can not open %s to get reply.\n", TTY);
2843 if (failact == -1)
2844 quit();
2845 else
2846 return(failact);
2847 }
2848 for (;;) {
2849 r = fgetc(tty);
2850 if (r == EOF) {
2851 fprintf(stderr, "Can not get reply.\n");
2852 if (failact == -1)
2853 quit();
2854 else
2855 return(failact);
2856 }
2857 if (r != '\n')
2858 while ((c = fgetc(tty)) != '\n' && c != EOF)
2859 ;
2860 r = mylower(r);
2861 if (r == 'y' || r == 'n')
2862 return(r == 'y');
2863 fprintf(stderr, "Yes or No? ");
2864 }
2865 }
2866
2867
2868 static void *myalloc(k)
2869 unsigned k;
2870 {
2871 void *ret;
2872
2873 if (k == 0)
2874 return(NULL);
2875 if ((ret = (void *)malloc(k)) == NULL) {
2876 fprintf(stderr, "Insufficient memory.\n");
2877 quit();
2878 }
2879 return(ret);
2880 }
2881
2882
2883 static void *challoc(k, which)
2884 int which;
2885 int k;
2886 {
2887 void *ret;
2888 CHUNK *p, *q;
2889 SLICER *sl = &(slicer[which]);
2890
2891 if (k > sl->sl_len) {
2892 for (
2893 q = NULL, p = freechunks;
2894 p != NULL && (sl->sl_len = p->ch_len) < k;
2895 q = p, p = p->ch_next
2896 )
2897 ;
2898 if (p == NULL) {
2899 sl->sl_len = CHUNKSIZE - sizeof(CHUNK *);
2900 p = (CHUNK *)myalloc(CHUNKSIZE);
2901 }
2902 else if (q == NULL)
2903 freechunks = p->ch_next;
2904 else
2905 q->ch_next = p->ch_next;
2906 p->ch_next = sl->sl_first;
2907 sl->sl_first = p;
2908 sl->sl_unused = (char *)&(p->ch_len);
2909 }
2910 sl->sl_len -= k;
2911 ret = (void *)sl->sl_unused;
2912 sl->sl_unused += k;
2913 return(ret);
2914 }
2915
2916
2917 static void chgive(p, k)
2918 void *p;
2919 unsigned k;
2920 {
2921 ((CHUNK *)p)->ch_len = k - sizeof(CHUNK *);
2922 ((CHUNK *)p)->ch_next = freechunks;
2923 freechunks = (CHUNK *)p;
2924 }
2925
2926
2927 #ifndef __STDC__
2928 #ifndef IS_MSDOS
2929 #ifndef IS_SYSV
2930 static void memmove(to, from, k)
2931 char *to, *from;
2932 unsigned k;
2933 {
2934 if (from > to)
2935 while (k-- != 0)
2936 *(to++) = *(from++);
2937 else {
2938 from += k;
2939 to += k;
2940 while (k-- != 0)
2941 *(--to) = *(--from);
2942 }
2943 }
2944 #endif
2945 #endif
2946 #endif
2947
2948
2949 static int mygetc()
2950 {
2951 static int lastc = 0;
2952
2953 if (lastc == EOF)
2954 return(EOF);
2955 return(lastc = getchar());
2956 }
2957
2958
2959 static char *mygets(s, l)
2960 char *s;
2961 int l;
2962 {
2963 char *nl;
2964
2965 for (;;) {
2966 if (fgets(s, l, stdin) == NULL)
2967 return(NULL);
2968 if ((nl = strchr(s, '\n')) != NULL)
2969 break;
2970 fprintf(stderr, "Input string too long. Try again> ");
2971 }
2972 *nl = '\0';
2973 return(s);
2974 }
2975
2976
2977 #ifdef IS_MSDOS
2978 static int leave()
2979 {
2980 return(0);
2981 }
2982
2983 static void cleanup()
2984 {
2985 int i;
2986
2987 if (patch.ph_safeid) {
2988 for (i = 0; i < nhandles; i++) {
2989 if (!(handles[i]->h_di->di_flags & DI_CLEANED)) {
2990 sprintf(pathbuf, "%s%s%03d",
2991 handles[i]->h_name, IDF, handles[i]->h_di->di_did);
2992 if (unlink(pathbuf))
2993 fprintf(stderr, "Strange, couldn't unlink %s.\n", pathbuf);
2994 handles[i]->h_di->di_flags |= DI_CLEANED;
2995 }
2996 }
2997 }
2998 /*
2999 Write device availability: undocumented internal MS-D*S function.
3000 Restore previous value.
3001 */
3002 bdos(0x37, olddevflag, 3);
3003 }
3004
3005 #endif
3006