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