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