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