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