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