home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume21 / mmv / part02 / mmv.c.1
Encoding:
Text File  |  1990-04-08  |  48.7 KB  |  2,435 lines

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