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