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