home *** CD-ROM | disk | FTP | other *** search
/ minnie.tuhs.org / unixen.tar / unixen / PDP-11 / Trees / V7 / usr / src / cmd / find.c < prev    next >
Encoding:
C/C++ Source or Header  |  1979-05-05  |  14.3 KB  |  724 lines

  1. /*    find    COMPILE:    cc -o find -s -O -i find.c -lS    */
  2. #include <stdio.h>
  3. #include <sys/types.h>
  4. #include <sys/dir.h>
  5. #include <sys/stat.h>
  6. #define A_DAY    86400L /* a day full of seconds */
  7. #define EQ(x, y)    (strcmp(x, y)==0)
  8.  
  9. int    Randlast;
  10. char    Pathname[200];
  11.  
  12. struct anode {
  13.     int (*F)();
  14.     struct anode *L, *R;
  15. } Node[100];
  16. int Nn;  /* number of nodes */
  17. char    *Fname;
  18. long    Now;
  19. int    Argc,
  20.     Ai,
  21.     Pi;
  22. char    **Argv;
  23. /* cpio stuff */
  24. int    Cpio;
  25. short    *Buf, *Dbuf, *Wp;
  26. int    Bufsize = 5120;
  27. int    Wct = 2560;
  28.  
  29. long    Newer;
  30.  
  31. struct stat Statb;
  32.  
  33. struct    anode    *exp(),
  34.         *e1(),
  35.         *e2(),
  36.         *e3(),
  37.         *mk();
  38. char    *nxtarg();
  39. char    Home[128];
  40. long    Blocks;
  41. char *rindex();
  42. char *sbrk();
  43. main(argc, argv) char *argv[];
  44. {
  45.     struct anode *exlist;
  46.     int paths;
  47.     register char *cp, *sp = 0;
  48.     FILE *pwd, *popen();
  49.  
  50.     time(&Now);
  51.     pwd = popen("pwd", "r");
  52.     fgets(Home, 128, pwd);
  53.     pclose(pwd);
  54.     Home[strlen(Home) - 1] = '\0';
  55.     Argc = argc; Argv = argv;
  56.     if(argc<3) {
  57. usage:        pr("Usage: find path-list predicate-list\n");
  58.         exit(1);
  59.     }
  60.     for(Ai = paths = 1; Ai < (argc-1); ++Ai, ++paths)
  61.         if(*Argv[Ai] == '-' || EQ(Argv[Ai], "(") || EQ(Argv[Ai], "!"))
  62.             break;
  63.     if(paths == 1) /* no path-list */
  64.         goto usage;
  65.     if(!(exlist = exp())) { /* parse and compile the arguments */
  66.         pr("find: parsing error\n");
  67.         exit(1);
  68.     }
  69.     if(Ai<argc) {
  70.         pr("find: missing conjunction\n");
  71.         exit(1);
  72.     }
  73.     for(Pi = 1; Pi < paths; ++Pi) {
  74.         sp = 0;
  75.         chdir(Home);
  76.         strcpy(Pathname, Argv[Pi]);
  77.         if(cp = rindex(Pathname, '/')) {
  78.             sp = cp + 1;
  79.             *cp = '\0';
  80.             if(chdir(*Pathname? Pathname: "/") == -1) {
  81.                 pr("find: bad starting directory\n");
  82.                 exit(2);
  83.             }
  84.             *cp = '/';
  85.         }
  86.         Fname = sp? sp: Pathname;
  87.         descend(Pathname, Fname, exlist); /* to find files that match  */
  88.     }
  89.     if(Cpio) {
  90.         strcpy(Pathname, "TRAILER!!!");
  91.         Statb.st_size = 0;
  92.         cpio();
  93.     }
  94.     exit(0);
  95. }
  96.  
  97. /* compile time functions:  priority is  exp()<e1()<e2()<e3()  */
  98.  
  99. struct anode *exp() { /* parse ALTERNATION (-o)  */
  100.     int or();
  101.     register struct anode * p1;
  102.  
  103.     p1 = e1() /* get left operand */ ;
  104.     if(EQ(nxtarg(), "-o")) {
  105.         Randlast--;
  106.         return(mk(or, p1, exp()));
  107.     }
  108.     else if(Ai <= Argc) --Ai;
  109.     return(p1);
  110. }
  111. struct anode *e1() { /* parse CONCATENATION (formerly -a) */
  112.     int and();
  113.     register struct anode * p1;
  114.     register char *a;
  115.  
  116.     p1 = e2();
  117.     a = nxtarg();
  118.     if(EQ(a, "-a")) {
  119. And:
  120.         Randlast--;
  121.         return(mk(and, p1, e1()));
  122.     } else if(EQ(a, "(") || EQ(a, "!") || (*a=='-' && !EQ(a, "-o"))) {
  123.         --Ai;
  124.         goto And;
  125.     } else if(Ai <= Argc) --Ai;
  126.     return(p1);
  127. }
  128. struct anode *e2() { /* parse NOT (!) */
  129.     int not();
  130.  
  131.     if(Randlast) {
  132.         pr("find: operand follows operand\n");
  133.         exit(1);
  134.     }
  135.     Randlast++;
  136.     if(EQ(nxtarg(), "!"))
  137.         return(mk(not, e3(), (struct anode *)0));
  138.     else if(Ai <= Argc) --Ai;
  139.     return(e3());
  140. }
  141. struct anode *e3() { /* parse parens and predicates */
  142.     int exeq(), ok(), glob(),  mtime(), atime(), ctime(), user(),
  143.         group(), size(), perm(), links(), print(),
  144.         type(), ino(), cpio(), newer();
  145.     struct anode *p1;
  146.     int i;
  147.     register char *a, *b, s;
  148.  
  149.     a = nxtarg();
  150.     if(EQ(a, "(")) {
  151.         Randlast--;
  152.         p1 = exp();
  153.         a = nxtarg();
  154.         if(!EQ(a, ")")) goto err;
  155.         return(p1);
  156.     }
  157.     else if(EQ(a, "-print")) {
  158.         return(mk(print, (struct anode *)0, (struct anode *)0));
  159.     }
  160.     b = nxtarg();
  161.     s = *b;
  162.     if(s=='+') b++;
  163.     if(EQ(a, "-name"))
  164.         return(mk(glob, (struct anode *)b, (struct anode *)0));
  165.     else if(EQ(a, "-mtime"))
  166.         return(mk(mtime, (struct anode *)atoi(b), (struct anode *)s));
  167.     else if(EQ(a, "-atime"))
  168.         return(mk(atime, (struct anode *)atoi(b), (struct anode *)s));
  169.     else if(EQ(a, "-ctime"))
  170.         return(mk(ctime, (struct anode *)atoi(b), (struct anode *)s));
  171.     else if(EQ(a, "-user")) {
  172.         if((i=getunum("/etc/passwd", b)) == -1) {
  173.             if(gmatch(b, "[0-9][0-9][0-9]*")
  174.             || gmatch(b, "[0-9][0-9]")
  175.             || gmatch(b, "[0-9]"))
  176.                 return mk(user, (struct anode *)atoi(b), (struct anode *)s);
  177.             pr("find: cannot find -user name\n");
  178.             exit(1);
  179.         }
  180.         return(mk(user, (struct anode *)i, (struct anode *)s));
  181.     }
  182.     else if(EQ(a, "-inum"))
  183.         return(mk(ino, (struct anode *)atoi(b), (struct anode *)s));
  184.     else if(EQ(a, "-group")) {
  185.         if((i=getunum("/etc/group", b)) == -1) {
  186.             if(gmatch(b, "[0-9][0-9][0-9]*")
  187.             || gmatch(b, "[0-9][0-9]")
  188.             || gmatch(b, "[0-9]"))
  189.                 return mk(group, (struct anode *)atoi(b), (struct anode *)s);
  190.             pr("find: cannot find -group name\n");
  191.             exit(1);
  192.         }
  193.         return(mk(group, (struct anode *)i, (struct anode *)s));
  194.     } else if(EQ(a, "-size"))
  195.         return(mk(size, (struct anode *)atoi(b), (struct anode *)s));
  196.     else if(EQ(a, "-links"))
  197.         return(mk(links, (struct anode *)atoi(b), (struct anode *)s));
  198.     else if(EQ(a, "-perm")) {
  199.         for(i=0; *b ; ++b) {
  200.             if(*b=='-') continue;
  201.             i <<= 3;
  202.             i = i + (*b - '0');
  203.         }
  204.         return(mk(perm, (struct anode *)i, (struct anode *)s));
  205.     }
  206.     else if(EQ(a, "-type")) {
  207.         i = s=='d' ? S_IFDIR :
  208.             s=='b' ? S_IFBLK :
  209.             s=='c' ? S_IFCHR :
  210.             s=='f' ? 0100000 :
  211.             0;
  212.         return(mk(type, (struct anode *)i, (struct anode *)0));
  213.     }
  214.     else if (EQ(a, "-exec")) {
  215.         i = Ai - 1;
  216.         while(!EQ(nxtarg(), ";"));
  217.         return(mk(exeq, (struct anode *)i, (struct anode *)0));
  218.     }
  219.     else if (EQ(a, "-ok")) {
  220.         i = Ai - 1;
  221.         while(!EQ(nxtarg(), ";"));
  222.         return(mk(ok, (struct anode *)i, (struct anode *)0));
  223.     }
  224.     else if(EQ(a, "-cpio")) {
  225.         if((Cpio = creat(b, 0666)) < 0) {
  226.             pr("find: cannot create "), pr(s), pr("\n");
  227.             exit(1);
  228.         }
  229.         Buf = (short *)sbrk(512);
  230.         Wp = Dbuf = (short *)sbrk(5120);
  231.         return(mk(cpio, (struct anode *)0, (struct anode *)0));
  232.     }
  233.     else if(EQ(a, "-newer")) {
  234.         if(stat(b, &Statb) < 0) {
  235.             pr("find: cannot access "), pr(b), pr("\n");
  236.             exit(1);
  237.         }
  238.         Newer = Statb.st_mtime;
  239.         return mk(newer, (struct anode *)0, (struct anode *)0);
  240.     }
  241. err:    pr("find: bad option "), pr(a), pr("\n");
  242.     exit(1);
  243. }
  244. struct anode *mk(f, l, r)
  245. int (*f)();
  246. struct anode *l, *r;
  247. {
  248.     Node[Nn].F = f;
  249.     Node[Nn].L = l;
  250.     Node[Nn].R = r;
  251.     return(&(Node[Nn++]));
  252. }
  253.  
  254. char *nxtarg() { /* get next arg from command line */
  255.     static strikes = 0;
  256.  
  257.     if(strikes==3) {
  258.         pr("find: incomplete statement\n");
  259.         exit(1);
  260.     }
  261.     if(Ai>=Argc) {
  262.         strikes++;
  263.         Ai = Argc + 1;
  264.         return("");
  265.     }
  266.     return(Argv[Ai++]);
  267. }
  268.  
  269. /* execution time functions */
  270. and(p)
  271. register struct anode *p;
  272. {
  273.     return(((*p->L->F)(p->L)) && ((*p->R->F)(p->R))?1:0);
  274. }
  275. or(p)
  276. register struct anode *p;
  277. {
  278.      return(((*p->L->F)(p->L)) || ((*p->R->F)(p->R))?1:0);
  279. }
  280. not(p)
  281. register struct anode *p;
  282. {
  283.     return( !((*p->L->F)(p->L)));
  284. }
  285. glob(p)
  286. register struct { int f; char *pat; } *p; 
  287. {
  288.     return(gmatch(Fname, p->pat));
  289. }
  290. print()
  291. {
  292.     puts(Pathname);
  293.     return(1);
  294. }
  295. mtime(p)
  296. register struct { int f, t, s; } *p; 
  297. {
  298.     return(scomp((int)((Now - Statb.st_mtime) / A_DAY), p->t, p->s));
  299. }
  300. atime(p)
  301. register struct { int f, t, s; } *p; 
  302. {
  303.     return(scomp((int)((Now - Statb.st_atime) / A_DAY), p->t, p->s));
  304. }
  305. ctime(p)
  306. register struct { int f, t, s; } *p; 
  307. {
  308.     return(scomp((int)((Now - Statb.st_ctime) / A_DAY), p->t, p->s));
  309. }
  310. user(p)
  311. register struct { int f, u, s; } *p; 
  312. {
  313.     return(scomp(Statb.st_uid, p->u, p->s));
  314. }
  315. ino(p)
  316. register struct { int f, u, s; } *p;
  317. {
  318.     return(scomp((int)Statb.st_ino, p->u, p->s));
  319. }
  320. group(p)
  321. register struct { int f, u; } *p; 
  322. {
  323.     return(p->u == Statb.st_gid);
  324. }
  325. links(p)
  326. register struct { int f, link, s; } *p; 
  327. {
  328.     return(scomp(Statb.st_nlink, p->link, p->s));
  329. }
  330. size(p)
  331. register struct { int f, sz, s; } *p; 
  332. {
  333.     return(scomp((int)((Statb.st_size+511)>>9), p->sz, p->s));
  334. }
  335. perm(p)
  336. register struct { int f, per, s; } *p; 
  337. {
  338.     register i;
  339.     i = (p->s=='-') ? p->per : 07777; /* '-' means only arg bits */
  340.     return((Statb.st_mode & i & 07777) == p->per);
  341. }
  342. type(p)
  343. register struct { int f, per, s; } *p;
  344. {
  345.     return((Statb.st_mode&S_IFMT)==p->per);
  346. }
  347. exeq(p)
  348. register struct { int f, com; } *p;
  349. {
  350.     fflush(stdout); /* to flush possible `-print' */
  351.     return(doex(p->com));
  352. }
  353. ok(p)
  354. struct { int f, com; } *p;
  355. {
  356.     int c;  int yes;
  357.     yes = 0;
  358.     fflush(stdout); /* to flush possible `-print' */
  359.     pr("< "), pr(Argv[p->com]), pr(" ... "), pr(Pathname), pr(" >?   ");
  360.     fflush(stderr);
  361.     if((c=getchar())=='y') yes = 1;
  362.     while(c!='\n')
  363.         if(c==EOF)
  364.             exit(2);
  365.         else
  366.             c = getchar();
  367.     if(yes) return(doex(p->com));
  368.     return(0);
  369. }
  370.  
  371. #define MKSHORT(v, lv) {U.l=1L;if(U.c[0]) U.l=lv, v[0]=U.s[1], v[1]=U.s[0]; else U.l=lv, v[0]=U.s[0], v[1]=U.s[1];}
  372. union { long l; short s[2]; char c[4]; } U;
  373. long mklong(v)
  374. short v[];
  375. {
  376.     U.l = 1;
  377.     if(U.c[0] /* VAX */)
  378.         U.s[0] = v[1], U.s[1] = v[0];
  379.     else
  380.         U.s[0] = v[0], U.s[1] = v[1];
  381.     return U.l;
  382. }
  383. cpio()
  384. {
  385. #define MAGIC 070707
  386.     struct header {
  387.         short    h_magic,
  388.             h_dev,
  389.             h_ino,
  390.             h_mode,
  391.             h_uid,
  392.             h_gid,
  393.             h_nlink,
  394.             h_rdev;
  395.         short    h_mtime[2];
  396.         short    h_namesize;
  397.         short    h_filesize[2];
  398.         char    h_name[256];
  399.     } hdr;
  400.     register ifile, ct;
  401.     static long fsz;
  402.     register i;
  403.  
  404.     hdr.h_magic = MAGIC;
  405.     strcpy(hdr.h_name, !strncmp(Pathname, "./", 2)? Pathname+2: Pathname);
  406.     hdr.h_namesize = strlen(hdr.h_name) + 1;
  407.     hdr.h_uid = Statb.st_uid;
  408.     hdr.h_gid = Statb.st_gid;
  409.     hdr.h_dev = Statb.st_dev;
  410.     hdr.h_ino = Statb.st_ino;
  411.     hdr.h_mode = Statb.st_mode;
  412.     MKSHORT(hdr.h_mtime, Statb.st_mtime);
  413.     hdr.h_nlink = Statb.st_nlink;
  414.     fsz = hdr.h_mode & S_IFREG? Statb.st_size: 0L;
  415.     MKSHORT(hdr.h_filesize, fsz);
  416.     hdr.h_rdev = Statb.st_rdev;
  417.     if(EQ(hdr.h_name, "TRAILER!!!")) {
  418.         bwrite((short *)&hdr, (sizeof hdr-256)+hdr.h_namesize);
  419.         for(i = 0; i < 10; ++i)
  420.             bwrite(Buf, 512);
  421.         return;
  422.     }
  423.     if(!mklong(hdr.h_filesize)) {
  424.         bwrite((short *)&hdr, (sizeof hdr-256)+hdr.h_namesize);
  425.         return;
  426.     }
  427.     if((ifile = open(Fname, 0)) < 0) {
  428. cerror:
  429.         pr("find: cannot copy "), pr(hdr.h_name), pr("\n");
  430.         return;
  431.     }
  432.     bwrite((short *)&hdr, (sizeof hdr-256)+hdr.h_namesize);
  433.     for(fsz = mklong(hdr.h_filesize); fsz > 0; fsz -= 512) {
  434.         ct = fsz>512? 512: fsz;
  435.         if(read(ifile, (char *)Buf, ct) < 0)
  436.             goto cerror;
  437.         bwrite(Buf, ct);
  438.     }
  439.     close(ifile);
  440.     return;
  441. }
  442. newer()
  443. {
  444.     return Statb.st_mtime > Newer;
  445. }
  446.  
  447. /* support functions */
  448. scomp(a, b, s) /* funny signed compare */
  449. register a, b;
  450. register char s;
  451. {
  452.     if(s == '+')
  453.         return(a > b);
  454.     if(s == '-')
  455.         return(a < (b * -1));
  456.     return(a == b);
  457. }
  458.  
  459. doex(com)
  460. {
  461.     register np;
  462.     register char *na;
  463.     static char *nargv[50];
  464.     static ccode;
  465.  
  466.     ccode = np = 0;
  467.     while (na=Argv[com++]) {
  468.         if(strcmp(na, ";")==0) break;
  469.         if(strcmp(na, "{}")==0) nargv[np++] = Pathname;
  470.         else nargv[np++] = na;
  471.     }
  472.     nargv[np] = 0;
  473.     if (np==0) return(9);
  474.     if(fork()) /*parent*/ wait(&ccode);
  475.     else { /*child*/
  476.         chdir(Home);
  477.         execvp(nargv[0], nargv, np);
  478.         exit(1);
  479.     }
  480.     return(ccode ? 0:1);
  481. }
  482.  
  483. getunum(f, s) char *f, *s; { /* find user/group name and return number */
  484.     register i;
  485.     register char *sp;
  486.     register c;
  487.     char str[20];
  488.     FILE *pin;
  489.  
  490.     i = -1;
  491.     pin = fopen(f, "r");
  492.     c = '\n'; /* prime with a CR */
  493.     do {
  494.         if(c=='\n') {
  495.             sp = str;
  496.             while((c = *sp++ = getc(pin)) != ':')
  497.                 if(c == EOF) goto RET;
  498.             *--sp = '\0';
  499.             if(EQ(str, s)) {
  500.                 while((c=getc(pin)) != ':')
  501.                     if(c == EOF) goto RET;
  502.                 sp = str;
  503.                 while((*sp = getc(pin)) != ':') sp++;
  504.                 *sp = '\0';
  505.                 i = atoi(str);
  506.                 goto RET;
  507.             }
  508.         }
  509.     } while((c = getc(pin)) != EOF);
  510.  RET:
  511.     fclose(pin);
  512.     return(i);
  513. }
  514.  
  515. descend(name, fname, exlist)
  516. struct anode *exlist;
  517. char *name, *fname;
  518. {
  519.     int    dir = 0, /* open directory */
  520.         offset,
  521.         dsize,
  522.         entries,
  523.         dirsize;
  524.     struct direct dentry[32];
  525.     register struct direct    *dp;
  526.     register char *c1, *c2;
  527.     int i;
  528.     int rv = 0;
  529.     char *endofname;
  530.  
  531.     if(stat(fname, &Statb)<0) {
  532.         pr("find: bad status-- "), pr(name), pr("\n");
  533.         return(0);
  534.     }
  535.     (*exlist->F)(exlist);
  536.     if((Statb.st_mode&S_IFMT)!=S_IFDIR)
  537.         return(1);
  538.  
  539.     for(c1 = name; *c1; ++c1);
  540.     if(*(c1-1) == '/')
  541.         --c1;
  542.     endofname = c1;
  543.     dirsize = Statb.st_size;
  544.  
  545.     if(chdir(fname) == -1)
  546.         return(0);
  547.     for(offset=0 ; offset < dirsize ; offset += 512) { /* each block */
  548.         dsize = 512<(dirsize-offset)? 512: (dirsize-offset);
  549.         if(!dir) {
  550.             if((dir=open(".", 0))<0) {
  551.                 pr("find: cannot open "), pr(name), pr("\n");
  552.                 rv = 0;
  553.                 goto ret;
  554.             }
  555.             if(offset) lseek(dir, (long)offset, 0);
  556.             if(read(dir, (char *)dentry, dsize)<0) {
  557.                 pr("find: cannot read "), pr(name), pr("\n");
  558.                 rv = 0;
  559.                 goto ret;
  560.             }
  561.             if(dir > 10) {
  562.                 close(dir);
  563.                 dir = 0;
  564.             }
  565.         } else 
  566.             if(read(dir, (char *)dentry, dsize)<0) {
  567.                 pr("find: cannot read "), pr(name), pr("\n");
  568.                 rv = 0;
  569.                 goto ret;
  570.             }
  571.         for(dp=dentry, entries=dsize>>4; entries; --entries, ++dp) { /* each directory entry */
  572.             if(dp->d_ino==0
  573.             || (dp->d_name[0]=='.' && dp->d_name[1]=='\0')
  574.             || (dp->d_name[0]=='.' && dp->d_name[1]=='.' && dp->d_name[2]=='\0'))
  575.                 continue;
  576.             c1 = endofname;
  577.             *c1++ = '/';
  578.             c2 = dp->d_name;
  579.             for(i=0; i<14; ++i)
  580.                 if(*c2)
  581.                     *c1++ = *c2++;
  582.                 else
  583.                     break;
  584.             *c1 = '\0';
  585.             if(c1 == endofname) { /* ?? */
  586.                 rv = 0;
  587.                 goto ret;
  588.             }
  589.             Fname = endofname+1;
  590.             if(!descend(name, Fname, exlist)) {
  591.                 *endofname = '\0';
  592.                 chdir(Home);
  593.                 if(chdir(Pathname) == -1) {
  594.                     pr("find: bad directory tree\n");
  595.                     exit(1);
  596.                 }
  597.             }
  598.         }
  599.     }
  600.     rv = 1;
  601. ret:
  602.     if(dir)
  603.         close(dir);
  604.     if(chdir("..") == -1) {
  605.         *endofname = '\0';
  606.         pr("find: bad directory "), pr(name), pr("\n");
  607.         rv = 1;
  608.     }
  609.     return(rv);
  610. }
  611.  
  612. gmatch(s, p) /* string match as in glob */
  613. register char *s, *p;
  614. {
  615.     if (*s=='.' && *p!='.') return(0);
  616.     return amatch(s, p);
  617. }
  618.  
  619. amatch(s, p)
  620. register char *s, *p;
  621. {
  622.     register cc;
  623.     int scc, k;
  624.     int c, lc;
  625.  
  626.     scc = *s;
  627.     lc = 077777;
  628.     switch (c = *p) {
  629.  
  630.     case '[':
  631.         k = 0;
  632.         while (cc = *++p) {
  633.             switch (cc) {
  634.  
  635.             case ']':
  636.                 if (k)
  637.                     return(amatch(++s, ++p));
  638.                 else
  639.                     return(0);
  640.  
  641.             case '-':
  642.                 k |= lc <= scc & scc <= (cc=p[1]);
  643.             }
  644.             if (scc==(lc=cc)) k++;
  645.         }
  646.         return(0);
  647.  
  648.     case '?':
  649.     caseq:
  650.         if(scc) return(amatch(++s, ++p));
  651.         return(0);
  652.     case '*':
  653.         return(umatch(s, ++p));
  654.     case 0:
  655.         return(!scc);
  656.     }
  657.     if (c==scc) goto caseq;
  658.     return(0);
  659. }
  660.  
  661. umatch(s, p)
  662. register char *s, *p;
  663. {
  664.     if(*p==0) return(1);
  665.     while(*s)
  666.         if (amatch(s++, p)) return(1);
  667.     return(0);
  668. }
  669.  
  670. bwrite(rp, c)
  671. register short *rp;
  672. register c;
  673. {
  674.     register short *wp = Wp;
  675.  
  676.     c = (c+1) >> 1;
  677.     while(c--) {
  678.         if(!Wct) {
  679. again:
  680.             if(write(Cpio, (char *)Dbuf, Bufsize)<0) {
  681.                 Cpio = chgreel(1, Cpio);
  682.                 goto again;
  683.             }
  684.             Wct = Bufsize >> 1;
  685.             wp = Dbuf;
  686.             ++Blocks;
  687.         }
  688.         *wp++ = *rp++;
  689.         --Wct;
  690.     }
  691.     Wp = wp;
  692. }
  693. chgreel(x, fl)
  694. {
  695.     register f;
  696.     char str[22];
  697.     FILE *devtty;
  698.     struct stat statb;
  699.  
  700.     pr("find: can't "), pr(x? "write output": "read input"), pr("\n");
  701.     fstat(fl, &statb);
  702.     if((statb.st_mode&S_IFMT) != S_IFCHR)
  703.         exit(1);
  704. again:
  705.     pr("If you want to go on, type device/file name when ready\n");
  706.     devtty = fopen("/dev/tty", "r");
  707.     fgets(str, 20, devtty);
  708.     str[strlen(str) - 1] = '\0';
  709.     if(!*str)
  710.         exit(1);
  711.     close(fl);
  712.     if((f = open(str, x? 1: 0)) < 0) {
  713.         pr("That didn't work");
  714.         fclose(devtty);
  715.         goto again;
  716.     }
  717.     return f;
  718. }
  719. pr(s)
  720. char *s;
  721. {
  722.     fputs(s, stderr);
  723. }
  724.