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